Expand description
Task S5 (docs/superpowers/plans/2026-09-26-wave-A2-slice.md): the
process TEMPLATE dedup key (PR-028, PR-029, PR-030) and the
ProcessInstanceId registration-order tie-break (PR-075, PR-076,
PR-077).
PR-028’s core claim (“process template dedup must key off slang’s own
InstanceBodySymbol canonicalization … compiling one kernel per
canonical process shape and sharing it across every instance”) is
implemented here as a direct (definition, resolved params) equality key
(ProcessTemplateKey), not a from-scratch structural-equality pass over
the AST: definition is sv_lang’s own stable crate::symid::SymbolId for
the shared Definition symbol (the same symbol every structurally-
identical instance’s body resolves back to via
instance_body_definition()), and params is the instance’s own
elaborated, already-folded sv_lang::ConstantValue list. Two instances
with an identical (definition, params) pair are, by construction,
instances of the same module with the same resolved parameter values –
exactly PR-028’s dedup criterion – with no separate call to
Symbol::instance_body_has_same_type needed at this layer (unlike
netlist::dedup’s own TemplateKey, which buckets on a lossy u64 hash
and so must re-verify with the real oracle before merging; this key
carries the real, structured parameter values, so key equality already
is the ground truth).
Deviation from the plan’s illustrative signature (fn template_key_of(instance_body: &Symbol<'_>) -> ProcessTemplateKey, taking
only the instance body): verified against pinned sv-lang = "=0.1.9"
that Symbol::parameters()’s own doc/SAFETY comment (“count is 0 for
non-instances”) means it is populated only when called on an
Instance-kind symbol, never on that instance’s InstanceBody – the
exact same constraint netlist::dedup::template_key_of already
documents and works around. template_key_of therefore takes both the
owning Instance symbol (for parameters()) and its InstanceBody
(for instance_body_definition()), by value (Symbol is Copy and
pointer-sized), mirroring that precedent exactly.
Deviation: ProcessTemplateKey’s Hash/Eq are hand-written, not
derived. sv_lang::ConstantValue derives only Clone, Debug, PartialEq
(it is #[non_exhaustive] and its Real variant is a bare f64, which
has no Eq; its Integer variant wraps SVInt, which derives Eq but
not Hash), so #[derive(Eq, Hash)] on a struct embedding
Vec<ConstantValue> does not compile. [hash_constant_value] below
provides a Hash implementation kept manually consistent with
ConstantValue’s derived PartialEq (structurally recursing the same
way, normalizing -0.0/0.0 to the same hash since PartialEq already
treats them as equal), and Eq is asserted as a marker trait on top of
that same PartialEq. The one honest gap this leaves (documented, not
silently swept under a #[derive]): a real-valued module parameter
equal to NaN is PartialEq-unequal to itself, so Eq’s reflexivity
technically does not hold for that one value class. This can only ever
cost a missed dedup opportunity (two NaN-parameterized instances
compiling their own, functionally-identical kernels instead of sharing
one) – never a wrong simulation result, since every instance still gets
its own correct ActivationRecord/state-arena storage regardless of
kernel sharing (PR-030) – and the A2 slice’s construct set has no real
parameters at all, so no slice fixture can even reach this path.
Structs§
- Process
Template Key - PR-029:
struct ProcessTemplateKey{definition: SymbolId, params: Vec<ConstantValue>}– a hashable dedup key built fromSymbol::definition()+parameters()(see this module’s doc comment for theinstance_body_definition()deviation), used for O(1) amortized template lookup (an ordinaryHashMap<ProcessTemplateKey, _>in the caller, e.g. codegen’s future per-template kernel cache).
Functions§
- assign_
process_ instance_ ids - PR-075/076/077: assigns
ProcessInstanceId(0), ProcessInstanceId(1), ...in exactlyorder’s sequence.orderis meant to benetlist::NetlistView::registration_order()’s own DFS-order output – this function performs no traversal of its own (seeno_second_preorder_walk_of_design_exists_in_process_dedup_rsintests/process_dedup.rs): PR-076’s reconciliation note is explicit thatregistration_order()(elaboration’s own deterministic pre-order DFS over the design’s top-level instance list) directly is the tie-break order, so a second, independently-derived pre-order walk here would just be a second place the “canonical instance order” could silently disagree with the first – exactly the bug class PR-076’ssuperseded_bynote names and this function is deliberately shaped to make impossible. - template_
key_ of - PR-029: builds a
ProcessTemplateKeyfrom an instance’s owningInstancesymbol (forparameters()) and itsInstanceBody(forinstance_body_definition()) – see this module’s doc comment for why both are needed.paramsuses each parameter’s elaboratedConstantValue(Symbol::constant_value(), forced during elaboration, full-width,defparam-aware) rather than the printed-textparameter_value()netlist’s ownTemplateKeyuses, per this task’s literalVec<ConstantValue>field type. A parameter whose constant value failed to fold (None) is recorded asConstantValue::Bad– a real, distinguishable member of the key rather than being silently dropped (which would shift every later parameter’s position and could spuriously equate two instances with a different number of foldable parameters).