Add per-object origin tracking (vOrigins) to Gia_Man_t#487
Add per-object origin tracking (vOrigins) to Gia_Man_t#487robtaylor wants to merge 2 commits intoberkeley-abc:masterfrom
Conversation
Add lightweight origin tracking that propagates source-location provenance through ABC's optimization passes. This enables the Yosys abc9 flow to preserve \src attributes on LUT cells after technology mapping, achieving ~100% coverage on tested designs with negligible overhead (~0.6% time, ~3% memory on picorv32). Changes: - Add Vec_Int_t *vOrigins field to Gia_Man_t with inline accessors - Read/write origins via AIGER "y" extension (sentinel -1 for unmapped) - Propagate through all Gia_ManDup* variants via Gia_ManOriginsDup() - Propagate through structural hashing (AND/XOR/MUX) in giaHash.c - Recover origins after GIA→AIG→GIA round-trips (&dc2, &dch) via Gia_ManOriginsAfterRoundTrip() using CO driver + top-down propagation - Propagate through IF mapper using iCopy correspondence - Instrument giaEquiv, giaMuxes, giaTim, giaBalAig, dauGia Co-developed-by: Claude Code v2.1.44 (claude-opus-4-6)
ABC now propagates origin mappings natively through optimization passes via vOrigins (berkeley-abc/abc#487), so we no longer need to run &verify -y and rewrite the output to reconstruct the mapping. Keep &verify for correctness checking but without -y flag. Co-developed-by: Claude Code v2.1.44 (claude-opus-4-6)
|
Adding @alanminko's response here from email for my context:
I'm now working on adding propagation into the engines, as advised. |
|
Following up on Alan's feedback — this new commit propagates Engines now instrumented:
New helper added: Supporting functions also instrumented: Testing: Origins survive through all individual engines and chained pipelines ( |
|
can we not with the ai slop? |
would you like to actually check the code first? Already discussed with @alanminko as this is the sort of change where AI helps. |
Performance BenchmarkBenchmarked origin propagation overhead on i10.aig (2675 ANDs, 257 inputs, 224 outputs) — same circuit with and without origins loaded, 20 iterations each:
All overhead is within measurement noise (~1%). The full Methodology: Same circuit loaded with ( |
|
@robtaylor Does this support multiple origins per object? Or just one? |
|
Thank you for keeping me in the loop. One thing observed is that following comment is not correct in all cases: "&nf -> No changes needed (returns same GIA)". Please note that all technology mappers (&if, &lf, &jf, &nf), when applied to an AIG with structural choices, derived by &dch or &synch2, return a different AIG after mapping. Of course it is possible to not use structural choices, but this leads to area/delay degradation on most of the designs. |
|
@akashlevy Single origin per object — each GIA object maps to one ancestor in the original AIG. This keeps the tracking lightweight (one Multi-source attribution is better handled at the Yosys layer: when mapping LUTs back to RTL cells, a single LUT may cover multiple original AIG objects. Yosys already has infrastructure for merging |
|
@alanminko Thank you for the feedback. You're right that all technology mappers can return a different AIG when structural choices are present. Looking at the current code, I believe
So I believe the current code is correct, but my summary table was wrong to say "No changes needed (returns same GIA)" without explaining why — it implied I hadn't considered the choices case. That said, would you prefer a defensive |
|
@akashlevy To follow up — the Yosys-side patch that consumes the origin tracking now handles multi-source attribution: for each mapped cell, it collects See the Yosys patch at: https://github.com/robtaylor/yosys/tree/src-retention-y-ext (specifically |
|
@robtaylor When one of above mappers is called on AIG with choices derived by &dch or &synch2, it produces a new AIG, annotated with the mapping. It would be good if the origin propagation handled this case. |
|
@alanminko Thank you for the clarification. I've traced the code paths for all mappers with structural choices: Mappers that create a new GIA (origin propagation already added):
Mapper that modifies in-place (vOrigins survives on same object):
Is there a specific scenario where &nf (or another mapper) with choices produces a genuinely new GIA object that I'm missing? I want to make sure I'm not overlooking a code path. |
|
@alanminko Thank you for pressing on this — I was being too narrow in my earlier analysis. Let me restate more carefully. For
More broadly, the Dup functions used across all mappers ( Does this cover the cases you had in mind, or is there a path I'm still missing? |
|
Following up — I wrote a static analysis to find all
All 6 use the Coverage summary: 23 |
|
Latest patch set (6211d3c): Added The analysis script (gist) finds all
It flags 173 functions total, but most are outside the abc9 pipeline (miters, verification, framing, etc). The second script cross-references against abc9 engine entry points to find the 6 that matter. All are now fixed. Full commit summary:
|
Extend origin tracking to cover the complete abc9 optimization pipeline. Add Gia_ManOriginsDupVec for functions that use Vec_Int_t copy vectors instead of the Value field. Engines instrumented: - &dc2, &dch: Round-trip recovery (giaAig.c) - &if: iCopy-based propagation in SopBalance/DsdBalance (giaIf.c), plus DupHashMapping/DupUnhashMapping for timing paths - &jf, &lf: OriginsDupVec in Jf_ManDeriveGia/Lf_ManDeriveMappingGia - &syn2, &synch2: Sub-operations (Jf/Lf mappers) + round-trip recovery in Gia_ManAigSynch2Choices, DupFromBarBufs/DupToBarBufs for box designs (giaScript.c) - &sweep: Gia_ManFraigReduceGia, Gia_ManDupWithBoxes (giaSweep.c) - &scorr: Gia_ManCorrReduce (cecCorr.c) - &mfs: Custom propagation via vMfs2Old/vMfs2Gia (giaMfs.c) - Supporting: Gia_ManDupCollapse/DupNormalize/DupUnnormalize (giaTim.c), DupUnshuffleInputs/DupMoveLast (giaTim.c), EquivToChoices (giaEquiv.c), DupMuxes (giaMuxes.c), BalanceInt (giaBalAig.c), DauMergePart (dauGia.c), DupWithAttributes (giaDup.c) Add bounds checks in Gia_ObjOrigin and Gia_ObjSetOrigin for GIAs that grow after vOrigins is allocated (e.g., AreaBalance adding nodes). Coverage verified by static analysis finding all Gia_Man_t* functions that call Gia_ManStart without Gia_ManOriginsDup — 23 functions covered, remaining 167 are outside the abc9 pipeline. Co-developed-by: Claude Code v2.1.58 (claude-opus-4-6)
6211d3c to
06cc387
Compare
Single origin per object implies that the src attribution is lossy. When nodes are merged in abc, you are then forcing one origin only among multiple possibilities. I think this solution is strictly worse than what we had implemented, where we allow for up to |
|
@robtaylor The AIG duplication/reconstruction paths you mentioned are the one I had in mind, so if they are supported, the codes should be fine for handling XAIGs while propagating the origin markers. |
|
Ah, it’s one origin per *input* object.
Check the Yosys patch for more details.
…On Mon, 9 Mar 2026 at 18:40, Akash Levy ***@***.***> wrote:
*akashlevy* left a comment (berkeley-abc/abc#487)
<#487 (comment)>
@akashlevy <https://github.com/akashlevy> Single origin per object — each
GIA object maps to one ancestor in the original AIG. This keeps the
tracking lightweight (one int per object, zero allocations on the hot
path).
Multi-source attribution is better handled at the Yosys layer: when
mapping LUTs back to RTL cells, a single LUT may cover multiple original
AIG objects. Yosys already has infrastructure for merging \src attributes
(e.g., \src "foo.v:10|bar.v:20"), so it can union the \src values of all
original objects that a LUT's origin chain touches. ABC just needs to
provide the single best origin per object as an anchor for that lookup.
Single origin per object implies that the src attribution is lossy. When
nodes are merged in abc, you are then forcing one origin only among
multiple possibilities. I think this solution is strictly worse than what
we had implemented, where we allow for up to N src attributes. Yes, it's
more complicated, but it's higher fidelity. Your point about handling
multi-src attribution in Yosys is irrelevant, because the information loss
happens in abc.
—
Reply to this email directly, view it on GitHub
<#487 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAB4O4FEPR5VSFPK37MUW2L4P4FYVAVCNFSM6AAAAACWCKO2UKVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHM2DAMRVHEZTQNZQGU>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
Ah got it! So there can still be multiple src attrs on the output objects as merge operations happen? |
yes indeed, thats the point =) |
|
To clarify my earlier reply — let me be more precise about what's happening. You're right that single origin per object is lossy within ABC. There are two loss points:
The Yosys-side multi-source collection partially compensates: for each mapped LUT, it collects So in practice: the coverage is good for typical RTL (structurally identical sub-expressions from different source locations are uncommon), but it is not lossless. An N-origins-per-object approach would be higher fidelity at the cost of N× memory and significantly more complex propagation through all the engine paths. The current design is a pragmatic starting point — if real-world usage shows the loss matters, the origin field could be extended. |
Got it. I suppose it depends on the application you are going for. Having one src attribute is obviously better than having none. But an issue that can happen (we've seen happen) is "aliasing" of many final nodes to a single origin node. In reality, these final nodes have a large diversity of origins, which is evident when multi-src attribution is kept. For our application (giving users an understanding of power/perf/area bottlenecks), it's not ideal to say, "this is the only line of RTL that led to this bottleneck" when in reality, it was multiple. |
|
@alanminko @akashlevy Thinking about this further — there's a middle ground between single-origin and N-origins-per-object that could address the fidelity concern without the memory overhead. The origin loss happens at two specific points in structural hashing (
Since these merge events are relatively rare compared to total node count, we could keep a global merge log — a flat vector of Vec_Int_t * vOriginsMerged; // append-only pairs: (node_id, alt_origin)This is sparse and append-only — no per-object overhead, just a small side table for the rare merge events. Propagation through Dup functions would remap node IDs the same way For XAIGER transport, the merge log could be a new extension (e.g., The main subtlety is multi-pass optimization: a node merged in pass 1 might get further merged in pass 2, creating chains. These would need transitive resolution through the origin mapping at write time. Alan — would something like this be acceptable as an addition to vOrigins, or would it add too much complexity to the core data structures? |
|
This proposed solution sounds like it would achieve multi-src attribution properly, just in a slightly different way than we had done in our fork. No need to have per-node arrays until the end. Seems like it would work well. I will say that the merge events are not as rare as you might think for a typical design. Transitive resolution would be necessary in quite a few real-world cases. |
ah, but is it rare with respect to the total number of nodes? |
No, it's very common. We see about 4-5 src attributes per node on average, when we retain thru merge. |
We are also using a pretty aggressive |
It will be acceptable as long as it is helpful for the Yosys team and other users and the default behavior of ABC is not changed. I do not have immediate plans to use this feature and this is why I do not have a preference regarding a good tradeoff for this data-structure (compactness vs expressiveness). |
Summary
This adds lightweight per-object origin tracking to
Gia_Man_t, enabling the Yosysabc9flow to preserve\src(source-location) attributes on LUT cells through ABC's optimization passes.Motivation
We are working on preserving
\src(source-location) attributes through the Yosysabc9synthesis flow. The approach uses the XAIGER "y" extension to pass an object-to-source mapping through ABC — Yosys writes the mapping on&read, ABC preserves it during optimization, and Yosys reads it back on&writeto annotate the resulting LUT cells.We initially prototyped a
&verify -yapproach that uses combinational equivalence checking to reconstruct the mapping after optimization, but this only achieves 17-57% coverage on non-trivial designs and doesn't work for sequential circuits. This PR takes a different approach — propagating origins during optimization — which achieves ~100% coverage.Approach
Add a single
Vec_Int_t *vOriginsvector toGia_Man_tthat maps each object to its "origin" input-side object ID:&read(AIGER reader)&write(AIGER writer)Three propagation helpers handle different transformation patterns:
Gia_ManOriginsDup()— for standard dup operations that use theValuefield for old→new mappingGia_ManOriginsDupVec()— for functions usingVec_Int_tcopy vectors (e.g., Jf/Lf mappers)Gia_ManOriginsAfterRoundTrip()— for GIA→AIG→GIA round-trips (&dc2,&dch,&synch2) where per-object data is lost; recovers origins via CI/CO correspondence + top-down fanin propagationEngine Coverage
Origins are propagated through all major ABC9 engines:
&dc2Gia_ManCompress2&dchGia_ManPerformDch&ifSopBalance/DsdBalanceiCopycorrespondence&syn2OriginsDupVec&synch2AigSynch2ChoicesOriginsDupVec+ round-trip recovery&sweepFraigReduceGia,DupWithBoxesOriginsDup&scorrGia_ManCorrReduceOriginsDup&mfsGia_ManInsertMfsvMfs2Old/vMfs2Gia&nfNf_ManDeriveMappingPerformance
Benchmarked on picorv32 (4-LUT mapping via
abc9):The overhead is negligible — approximately 4 bytes per GIA object for the
vOriginsvector, with O(1) propagation per node creation.Coverage
With this change,
\srcretention on LUT cells afterabc9mapping improves from 17-57% to 100% on all tested designs (simple combinational, Amaranth-style, and larger multi-output designs with 54 LUTs).Files changed
src/aig/gia/gia.hvOriginsfield + inline accessors + helper declarationssrc/aig/gia/giaMan.cvOriginsinGia_ManStopsrc/aig/gia/giaAiger.cvOriginsvia "y" extensionsrc/aig/gia/giaDup.cGia_ManOriginsDup+Gia_ManOriginsDupVec+Gia_ManOriginsAfterRoundTrip; instrument allGia_ManDup*variantssrc/aig/gia/giaHash.cGia_ManHashAnd,Gia_ManHashXorReal,Gia_ManHashMuxReal,Gia_ManRehashsrc/aig/gia/giaAig.cGia_ManCompress2(&dc2) andGia_ManPerformDch(&dch)src/aig/gia/giaIf.ciCopy+SopBalance/DsdBalancesrc/aig/gia/giaJf.cJf_ManDeriveGiaandJf_ManDeriveMappingGiaviaOriginsDupVecsrc/aig/gia/giaLf.cLf_ManDeriveMappingCoarseandLf_ManDeriveMappingGiasrc/aig/gia/giaMfs.cGia_ManInsertMfsvia MFS ID correspondencesrc/aig/gia/giaScript.cGia_ManAigSynch2Choicessrc/aig/gia/giaSweep.cGia_ManFraigReduceGiaandGia_ManDupWithBoxessrc/aig/gia/giaEquiv.cGia_ManEquivReduceandGia_ManEquivToChoicessrc/aig/gia/giaMuxes.cGia_ManDupMuxes/Gia_ManDupNoMuxessrc/aig/gia/giaTim.cGia_ManDupNormalize/Gia_ManDupUnnormalize/Gia_ManDupCollapsesrc/aig/gia/giaBalAig.csrc/opt/dau/dauGia.cDsm_ManDeriveGiasrc/proof/cec/cecCorr.cGia_ManCorrReduceContext
This is part of work on YosysHQ/yosys#5712 to improve
\srcattribute retention through theabc9flow.cc @alanminko @Ravenslofty — would appreciate your thoughts on this approach.