One generic struct instantiated with itself n levels deep: a chain of `typealias T_i = Pair<Leaf, T_{i-1}>` where `Pair` is constrained to an interface that extends another. Every other generic workload in the suite scales BREADTH (many shallow instantiations); this one scales DEPTH, the axis on which the front-end's substitution / inheritance-witness work is known to multiply per level. The shape arises in real code that composes generic "model"/"module" types recursively (e.g. chained ML evaluator stacks), where each wrapper layer adds one level of nesting. Scaling null: each level adds O(1) declarations, so ideal front-end cost is O(n). Measured behavior when this was added (2026-07): substitution work grows ~3-4x PER LEVEL (exponential), with the wall-clock knee near depth 18 — v2025.14 through tip-of-tree all take ~17-20 s at depth 24. The sweep ladder deliberately stays at or below the knee; the finding to watch is the top rung's multiple of the linear expectation moving.
bucket: sema · compile mode: module · flags: (none) · default N: 16
Full sub-counter decomposition of compileInner — named leaf timers plus (self) residuals (a parent's time not covered by a named child, e.g. the autodiff transform in linkAndOptimizeIR (self)). Topmost band traces compileInner; hover a band for its phase.
Overall compileInner: 3.0 → 3.0 ms -2.0% (2026-07-30 7c58a326b → 2026-08-29 28c755b09)
Contributors — the mutually-exclusive phase buckets (named leaves + (self) residuals) that tile compileInner; the pp column sums to the overall %. Buckets moving the total by ≥0.2% are listed, the rest fold into the remainder row. Below them, every other reported counter (nested/overlapping, e.g. serialized-module reads — own change only):
| counter | Δ | own % | of total |
|---|---|---|---|
| frontEndExecute (self) | -0.0 ms | -32.1% | -1.5pp |
| SemanticChecking | +0.0 ms | +6.0% | +1.3pp |
| generateOutput (self) | -0.0 ms | -3.8% | -1.2pp |
| generateIR | -0.0 ms | -0.9% | -0.3pp |
| compileInner (self) | -0.0 ms | – | -0.3pp |
| (remaining 1 buckets) | +0.0 ms | – | +0.0pp |
| loadBuiltinModule | -5.0 ms | -4.6% | – |
| readSerializedModuleIR | -2.7 ms | -5.0% | – |
Largest day steps (≥5% of the previous day, both directions; bisect with git log <c0>..<c1> -- source/):
| boundary | % vs prev day | commits | top buckets (own %) |
|---|---|---|---|
| none | |||
Run from the slang repo root. This regenerates the sources below and re-runs this workload's measurement; --gen-dir keeps the generated files (they go to a tempdir and are deleted otherwise).
python3 tools/compile-perf/bench.py --slangc /path/to/slangc --only generic_nesting --label repro --gen-dir repro-generic_nesting
the complete compiled source (N = 16), shown in full
// AUTO-GENERATED by perf-suite/workloads.py - do not edit by hand.
interface IBase {}
interface IModel : IBase {}
struct Leaf : IModel {}
struct Pair<First : IModel, Second : IModel> : IModel
{
First first;
Second second;
}
typealias T0 = Leaf;
typealias T1 = Pair<Leaf, T0>;
typealias T2 = Pair<Leaf, T1>;
typealias T3 = Pair<Leaf, T2>;
typealias T4 = Pair<Leaf, T3>;
typealias T5 = Pair<Leaf, T4>;
typealias T6 = Pair<Leaf, T5>;
typealias T7 = Pair<Leaf, T6>;
typealias T8 = Pair<Leaf, T7>;
typealias T9 = Pair<Leaf, T8>;
typealias T10 = Pair<Leaf, T9>;
typealias T11 = Pair<Leaf, T10>;
typealias T12 = Pair<Leaf, T11>;
typealias T13 = Pair<Leaf, T12>;
typealias T14 = Pair<Leaf, T13>;
typealias T15 = Pair<Leaf, T14>;
typealias T16 = Pair<Leaf, T15>;
T16 value;
[shader("compute")]
[numthreads(1, 1, 1)]
void computeMain() {}