Skip to content

Add per-object origin tracking (vOrigins) to Gia_Man_t#487

Open
robtaylor wants to merge 4 commits intoberkeley-abc:masterfrom
robtaylor:origin-tracking-clean
Open

Add per-object origin tracking (vOrigins) to Gia_Man_t#487
robtaylor wants to merge 4 commits intoberkeley-abc:masterfrom
robtaylor:origin-tracking-clean

Conversation

@robtaylor
Copy link

@robtaylor robtaylor commented Feb 28, 2026

Summary

This adds lightweight per-object origin tracking to Gia_Man_t, enabling the Yosys abc9 flow 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 Yosys abc9 synthesis 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 &write to annotate the resulting LUT cells.

We initially prototyped a &verify -y approach 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 *vOrigins vector to Gia_Man_t that maps each object to its "origin" input-side object ID:

  • Initialized from the "y" extension on &read (AIGER reader)
  • Propagated through all dup/conversion/optimization passes and all major ABC9 engines
  • Written to "y" extension on &write (AIGER writer)
  • Heuristic: when creating AND/XOR/MUX via structural hashing, inherit the origin with the lowest valid ID from the parents

Three propagation helpers handle different transformation patterns:

  • Gia_ManOriginsDup() — for standard dup operations that use the Value field for old→new mapping
  • Gia_ManOriginsDupVec() — for functions using Vec_Int_t copy 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 propagation

Engine Coverage

Origins are propagated through all major ABC9 engines:

Engine Function(s) Propagation method
&dc2 Gia_ManCompress2 Round-trip recovery
&dch Gia_ManPerformDch Round-trip recovery
&if IF mapper + SopBalance/DsdBalance iCopy correspondence
&syn2 Jf/Lf mappers + sub-operations OriginsDupVec
&synch2 Jf/Lf mappers + AigSynch2Choices OriginsDupVec + round-trip recovery
&sweep FraigReduceGia, DupWithBoxes OriginsDup
&scorr Gia_ManCorrReduce OriginsDup
&mfs Gia_ManInsertMfs Custom via vMfs2Old/vMfs2Gia
&nf Nf_ManDeriveMapping No changes needed (returns same GIA)

Performance

Benchmarked on picorv32 (4-LUT mapping via abc9):

Metric Without origins With origins Overhead
Wall-clock time ~3.0s ~3.02s ~0.6%
Peak memory ~48MB ~49.5MB ~3%
LUT count identical identical 0%

The overhead is negligible — approximately 4 bytes per GIA object for the vOrigins vector, with O(1) propagation per node creation.

Coverage

With this change, \src retention on LUT cells after abc9 mapping improves from 17-57% to 100% on all tested designs (simple combinational, Amaranth-style, and larger multi-output designs with 54 LUTs).

Files changed

File Change
src/aig/gia/gia.h Add vOrigins field + inline accessors + helper declarations
src/aig/gia/giaMan.c Free vOrigins in Gia_ManStop
src/aig/gia/giaAiger.c Read/write vOrigins via "y" extension
src/aig/gia/giaDup.c Gia_ManOriginsDup + Gia_ManOriginsDupVec + Gia_ManOriginsAfterRoundTrip; instrument all Gia_ManDup* variants
src/aig/gia/giaHash.c Propagate origins in Gia_ManHashAnd, Gia_ManHashXorReal, Gia_ManHashMuxReal, Gia_ManRehash
src/aig/gia/giaAig.c Recover origins after AIG round-trips in Gia_ManCompress2 (&dc2) and Gia_ManPerformDch (&dch)
src/aig/gia/giaIf.c Propagate through IF mapper via iCopy + SopBalance/DsdBalance
src/aig/gia/giaJf.c Propagate in Jf_ManDeriveGia and Jf_ManDeriveMappingGia via OriginsDupVec
src/aig/gia/giaLf.c Propagate in Lf_ManDeriveMappingCoarse and Lf_ManDeriveMappingGia
src/aig/gia/giaMfs.c Propagate in Gia_ManInsertMfs via MFS ID correspondence
src/aig/gia/giaScript.c Round-trip recovery in Gia_ManAigSynch2Choices
src/aig/gia/giaSweep.c Propagate in Gia_ManFraigReduceGia and Gia_ManDupWithBoxes
src/aig/gia/giaEquiv.c Propagate in Gia_ManEquivReduce and Gia_ManEquivToChoices
src/aig/gia/giaMuxes.c Propagate in Gia_ManDupMuxes/Gia_ManDupNoMuxes
src/aig/gia/giaTim.c Propagate in Gia_ManDupNormalize/Gia_ManDupUnnormalize/Gia_ManDupCollapse
src/aig/gia/giaBalAig.c Propagate in balance operations
src/opt/dau/dauGia.c Propagate in Dsm_ManDeriveGia
src/proof/cec/cecCorr.c Propagate in Gia_ManCorrReduce

Context

This is part of work on YosysHQ/yosys#5712 to improve \src attribute retention through the abc9 flow.

cc @alanminko @Ravenslofty — would appreciate your thoughts on this approach.

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)
robtaylor added a commit to robtaylor/yosys that referenced this pull request Feb 28, 2026
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)
@robtaylor
Copy link
Author

Adding @alanminko's response here from email for my context:

It looks like your implementation is quite clean and has low resource usage. Currently it supports basic AIG manipulations (copy, balancing, etc). The next step is to propagate it into the engines, which manipulate XAIGs (&dc2, &if, &nf, &mfs, &syn2, &dch, &synch2, &sweep, &scorr, etc). It appears to be a lot of work but perhaps it can be quickly done using AI agents.

Glad this method works for mapping RTL code into the resulting AIG nodes (as well gates and LUTs after mapping). It also does not have the overhead of equivalence checking, required by the previous approach.

I'm now working on adding propagation into the engines, as advised.

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: Already had round-trip recovery (giaAig.c)
- &if: iCopy-based propagation in SopBalance/DsdBalance (giaIf.c)
- &syn2, &synch2: Sub-operations (Jf/Lf mappers) + round-trip
  recovery in Gia_ManAigSynch2Choices (giaScript.c)
- &sweep: Gia_ManFraigReduceGia, Gia_ManDupWithBoxes (giaSweep.c)
- &scorr: Gia_ManCorrReduce (cecCorr.c)
- &mfs: Custom propagation via vMfs2Old/vMfs2Gia (giaMfs.c)
- Supporting functions: Gia_ManDupCollapse (giaTim.c),
  Gia_ManEquivToChoices (giaEquiv.c)

Tested: origins survive read → engine → write for all engines
including chained pipelines (&dc2; &synch2; &if; &mfs).

Co-developed-by: Claude Code v2.1.58 (claude-opus-4-6)
@robtaylor
Copy link
Author

Following up on Alan's feedback — this new commit propagates vOrigins through all the major ABC9 engines:

Engines now instrumented:

Engine Function(s) Propagation method
&dc2 Gia_ManCompress2 Round-trip recovery (already in first commit)
&dch Gia_ManPerformDch Round-trip recovery (already in first commit)
&if Gia_ManPerformSopBalance, Gia_ManPerformDsdBalance iCopy correspondence
&syn2 Jf/Lf mappers + sub-operations OriginsDupVec (for vCopies-based mappers)
&synch2 Jf/Lf mappers + Gia_ManAigSynch2Choices OriginsDupVec + round-trip recovery
&sweep Gia_ManFraigReduceGia, Gia_ManDupWithBoxes OriginsDup (Value field)
&scorr Gia_ManCorrReduce OriginsDup (Value field)
&mfs Gia_ManInsertMfs Custom via vMfs2Old/vMfs2Gia
&nf Nf_ManDeriveMapping No changes needed (returns same GIA)

New helper added: Gia_ManOriginsDupVec() — like Gia_ManOriginsDup() but for functions that use a Vec_Int_t copy vector (e.g., Jf/Lf mappers) instead of the Value field.

Supporting functions also instrumented: Gia_ManDupCollapse (giaTim.c), Gia_ManEquivToChoices (giaEquiv.c).

Testing: Origins survive through all individual engines and chained pipelines (&dc2; &synch2; &if; &mfs). The change adds 86 lines across 11 files with no new allocations on the hot path.

When a GIA is grown after vOrigins is created (e.g., AreaBalance adds
new nodes), object IDs can exceed Vec_IntSize(vOrigins). Add bounds
checks in Gia_ObjOrigin, Gia_ManOriginsDup, OriginsAfterRoundTrip,
and IF mapper propagation to prevent Vec_IntEntry assertions.

Co-developed-by: Claude Code v2.1.58 (claude-opus-4-6)
@Shadlock0133
Copy link

can we not with the ai slop?

@robtaylor
Copy link
Author

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.

@robtaylor
Copy link
Author

Performance Benchmark

Benchmarked origin propagation overhead on i10.aig (2675 ANDs, 257 inputs, 224 outputs) — same circuit with and without origins loaded, 20 iterations each:

Pipeline No Origins With Origins Overhead
&dc2 68.6±0.9ms 70.3±1.0ms +2.4%
&syn2 25.0±0.7ms 24.5±0.5ms -1.8%
&synch2 139.8±0.9ms 139.9±1.4ms +0.1%
&dch 143.6±1.4ms 143.8±1.7ms +0.1%
&scorr 17.2±0.6ms 17.4±0.8ms +1.4%
&sweep 26.1±1.1ms 26.3±0.6ms +0.8%
&if -K 6 39.5±0.9ms 39.4±0.6ms -0.2%
&dc2; &syn2; &if 89.2±1.6ms 88.1±1.1ms -1.2%
&dc2; &synch2; &if; &mfs 390.7±1.6ms 393.3±3.6ms +0.7%

All overhead is within measurement noise (~1%). The full abc9-style pipeline (&dc2; &synch2; &if -K 6; &mfs) shows +0.7% overhead — essentially free.

Methodology: Same circuit loaded with (&r -s file_with_y.aig) and without (&r -s file_no_y.aig) the "y" extension. The only difference is whether vOrigins is allocated and propagated. macOS/ARM64, 20 iterations averaged.

… DupWithAttributes

Gia_ObjSetOrigin lacked the bounds check that Gia_ObjOrigin already
had — if iObj exceeded Vec_IntSize(vOrigins) (e.g., after GIA growth),
Vec_IntWriteEntry would assert. Add matching guard.

Gia_ManDupWithAttributes copies all optional GIA attributes but was
missing vOrigins. Not on the abc9 hot path (only used by &save/&load),
but completes the attribute set for correctness.

Co-developed-by: Claude Code v2.1.58 (claude-opus-4-6)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants