Skip to main content

Module dedup

Module dedup 

Source
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§

ProcessTemplateKey
PR-029: struct ProcessTemplateKey{definition: SymbolId, params: Vec<ConstantValue>} – a hashable dedup key built from Symbol::definition()+parameters() (see this module’s doc comment for the instance_body_definition() deviation), used for O(1) amortized template lookup (an ordinary HashMap<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 exactly order’s sequence. order is meant to be netlist::NetlistView::registration_order()’s own DFS-order output – this function performs no traversal of its own (see no_second_preorder_walk_of_design_exists_in_process_dedup_rs in tests/process_dedup.rs): PR-076’s reconciliation note is explicit that registration_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’s superseded_by note names and this function is deliberately shaped to make impossible.
template_key_of
PR-029: builds a ProcessTemplateKey from an instance’s owning Instance symbol (for parameters()) and its InstanceBody (for instance_body_definition()) – see this module’s doc comment for why both are needed. params uses each parameter’s elaborated ConstantValue (Symbol::constant_value(), forced during elaboration, full-width, defparam-aware) rather than the printed-text parameter_value() netlist’s own TemplateKey uses, per this task’s literal Vec<ConstantValue> field type. A parameter whose constant value failed to fold (None) is recorded as ConstantValue::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).