Skip to content

Commit 6416532

Browse files
committed
Add regression tests for origin tracking (vOrigins)
PR berkeley-abc#487 shipped no tests; this adds a gtest suite (test/gia/origins_test.cc, consistent with the existing gia_test.cc) covering: - core infra: set/get, add-with-dedup, inline->overflow promotion, cross-manager union, and the nOriginsMax cap; - propagation through Gia_ManDup; - the AIGER "y" extension write/read round-trip; - preservation through the &nf standard-cell mapper (driven via read_genlib + &read + &nf using a small bundled genlib fixture). &nf maps in place, so origins survive standard-cell mapping with no dedicated propagation code. All 8 cases pass under ctest. Co-developed-by: Claude Code v2.1.195 (claude-opus-4-8)
1 parent 4f03583 commit 6416532

3 files changed

Lines changed: 241 additions & 0 deletions

File tree

test/gia/CMakeLists.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,15 @@ target_link_libraries(gia_test
77

88
gtest_discover_tests(gia_test
99
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
10+
)
11+
12+
add_executable(origins_test origins_test.cc)
13+
14+
target_link_libraries(origins_test
15+
gtest_main
16+
libabc
17+
)
18+
19+
gtest_discover_tests(origins_test
20+
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
1021
)

test/gia/origins_test.cc

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
// Regression tests for per-object origin tracking (vOrigins).
2+
//
3+
// Covers the core infrastructure (set/get/add/dedup/inline->overflow
4+
// promotion/union/cap), propagation through Gia_ManDup, the AIGER "y"
5+
// extension write/read round-trip, and preservation through the &nf
6+
// standard-cell mapper (which maps in place and is therefore expected to
7+
// preserve origins without dedicated propagation code).
8+
9+
#include "gtest/gtest.h"
10+
11+
#include "aig/gia/gia.h"
12+
#include "base/main/main.h"
13+
#include "base/cmd/cmd.h"
14+
15+
#include <cstdio>
16+
#include <set>
17+
18+
ABC_NAMESPACE_IMPL_START
19+
20+
// --------------------------------------------------------------------------
21+
// helpers
22+
// --------------------------------------------------------------------------
23+
24+
// Build a 2-input single-AND combinational AIG: o = a & b.
25+
static Gia_Man_t* BuildSmallAig() {
26+
Gia_Man_t* p = Gia_ManStart(100);
27+
int a = Gia_ManAppendCi(p);
28+
int b = Gia_ManAppendCi(p);
29+
int g = Gia_ManAppendAnd(p, a, b);
30+
Gia_ManAppendCo(p, g);
31+
return p;
32+
}
33+
34+
// Build o = (a & b) | (c & d), expressed with ANDs and inverters so the
35+
// mapper has something non-trivial to do.
36+
static Gia_Man_t* BuildOrOfAnds() {
37+
Gia_Man_t* p = Gia_ManStart(100);
38+
int a = Gia_ManAppendCi(p);
39+
int b = Gia_ManAppendCi(p);
40+
int c = Gia_ManAppendCi(p);
41+
int d = Gia_ManAppendCi(p);
42+
int ab = Gia_ManAppendAnd(p, a, b);
43+
int cd = Gia_ManAppendAnd(p, c, d);
44+
int nand = Gia_ManAppendAnd(p, Abc_LitNot(ab), Abc_LitNot(cd)); // ~ab & ~cd
45+
Gia_ManAppendCo(p, Abc_LitNot(nand)); // ab | cd
46+
return p;
47+
}
48+
49+
static int FirstAndId(Gia_Man_t* p) {
50+
Gia_Obj_t* pObj;
51+
int i;
52+
Gia_ManForEachAnd(p, pObj, i) return i;
53+
return -1;
54+
}
55+
56+
static int CountTotalOrigins(Gia_Man_t* p) {
57+
Gia_Obj_t* pObj;
58+
int i, total = 0;
59+
if (!p->vOrigins) return 0;
60+
Gia_ManForEachObj(p, pObj, i) total += Gia_ObjOriginsNum(p, i);
61+
return total;
62+
}
63+
64+
// Allocate origins and seed every AND node with its own id (the "identity"
65+
// mapping Yosys writes via the XAIGER "y" extension).
66+
static void SeedIdentityOrigins(Gia_Man_t* p) {
67+
Gia_Obj_t* pObj;
68+
int i;
69+
p->vOrigins = Gia_ManOriginsAlloc(Gia_ManObjNum(p));
70+
Gia_ManForEachAnd(p, pObj, i) Gia_ObjSetOrigin(p, i, i);
71+
}
72+
73+
// --------------------------------------------------------------------------
74+
// core infrastructure
75+
// --------------------------------------------------------------------------
76+
77+
TEST(OriginsTest, SetAndGetSingleOrigin) {
78+
Gia_Man_t* p = BuildSmallAig();
79+
p->vOrigins = Gia_ManOriginsAlloc(Gia_ManObjNum(p));
80+
int g = FirstAndId(p);
81+
Gia_ObjSetOrigin(p, g, 42);
82+
EXPECT_EQ(Gia_ObjOriginsNum(p, g), 1);
83+
EXPECT_EQ(Gia_ObjOriginsGet(p, g, 0), 42);
84+
Gia_ManStop(p);
85+
}
86+
87+
TEST(OriginsTest, AddDeduplicates) {
88+
Gia_Man_t* p = BuildSmallAig();
89+
p->vOrigins = Gia_ManOriginsAlloc(Gia_ManObjNum(p));
90+
int g = FirstAndId(p);
91+
Gia_ObjAddOrigin(p, g, 7);
92+
Gia_ObjAddOrigin(p, g, 7); // duplicate: ignored
93+
EXPECT_EQ(Gia_ObjOriginsNum(p, g), 1);
94+
Gia_ObjAddOrigin(p, g, 9);
95+
EXPECT_EQ(Gia_ObjOriginsNum(p, g), 2);
96+
Gia_ManStop(p);
97+
}
98+
99+
TEST(OriginsTest, InlineToOverflowPromotion) {
100+
Gia_Man_t* p = BuildSmallAig();
101+
p->vOrigins = Gia_ManOriginsAlloc(Gia_ManObjNum(p));
102+
int g = FirstAndId(p);
103+
const int N = GIA_ORIGINS_INLINE + 3; // force promotion past the inline buffer
104+
for (int k = 0; k < N; k++) Gia_ObjAddOrigin(p, g, 100 + k);
105+
EXPECT_EQ(Gia_ObjOriginsNum(p, g), N);
106+
std::set<int> got;
107+
for (int k = 0; k < N; k++) got.insert(Gia_ObjOriginsGet(p, g, k));
108+
for (int k = 0; k < N; k++) EXPECT_TRUE(got.count(100 + k)) << "missing origin " << (100 + k);
109+
Gia_ManStop(p);
110+
}
111+
112+
TEST(OriginsTest, UnionAcrossManagers) {
113+
Gia_Man_t* src = BuildSmallAig();
114+
src->vOrigins = Gia_ManOriginsAlloc(Gia_ManObjNum(src));
115+
int sg = FirstAndId(src);
116+
Gia_ObjAddOrigin(src, sg, 11);
117+
Gia_ObjAddOrigin(src, sg, 22);
118+
119+
Gia_Man_t* dst = BuildSmallAig();
120+
dst->vOrigins = Gia_ManOriginsAlloc(Gia_ManObjNum(dst));
121+
int dg = FirstAndId(dst);
122+
Gia_ObjAddOrigin(dst, dg, 11); // overlaps with src
123+
124+
Gia_ObjUnionOrigins(dst, dg, src, sg);
125+
EXPECT_EQ(Gia_ObjOriginsNum(dst, dg), 2); // {11, 22} deduplicated
126+
127+
Gia_ManStop(src);
128+
Gia_ManStop(dst);
129+
}
130+
131+
TEST(OriginsTest, NOriginsMaxCapsAccumulation) {
132+
Gia_Man_t* p = BuildSmallAig();
133+
p->vOrigins = Gia_ManOriginsAlloc(Gia_ManObjNum(p));
134+
p->nOriginsMax = 10;
135+
int g = FirstAndId(p);
136+
for (int k = 0; k < 50; k++) Gia_ObjAddOrigin(p, g, 1000 + k);
137+
EXPECT_EQ(Gia_ObjOriginsNum(p, g), 10); // capped once in overflow mode
138+
Gia_ManStop(p);
139+
}
140+
141+
// --------------------------------------------------------------------------
142+
// propagation
143+
// --------------------------------------------------------------------------
144+
145+
TEST(OriginsTest, GiaManDupPreservesOrigins) {
146+
Gia_Man_t* p = BuildSmallAig();
147+
p->vOrigins = Gia_ManOriginsAlloc(Gia_ManObjNum(p));
148+
int g = FirstAndId(p);
149+
Gia_ObjAddOrigin(p, g, 55);
150+
151+
Gia_Man_t* dup = Gia_ManDup(p);
152+
ASSERT_TRUE(dup->vOrigins != nullptr);
153+
EXPECT_EQ(CountTotalOrigins(dup), 1);
154+
int dg = FirstAndId(dup);
155+
ASSERT_GE(dg, 0);
156+
EXPECT_EQ(Gia_ObjOriginsNum(dup, dg), 1);
157+
EXPECT_EQ(Gia_ObjOriginsGet(dup, dg, 0), 55);
158+
159+
Gia_ManStop(p);
160+
Gia_ManStop(dup);
161+
}
162+
163+
TEST(OriginsTest, AigerWriteReadRoundTripPreservesOrigins) {
164+
Gia_Man_t* p = BuildSmallAig();
165+
p->vOrigins = Gia_ManOriginsAlloc(Gia_ManObjNum(p));
166+
int g = FirstAndId(p);
167+
Gia_ObjAddOrigin(p, g, 77);
168+
169+
const char* fname = "test_origins_roundtrip.aig";
170+
Gia_AigerWrite(p, (char*)fname, 0, 0, 0); // "y" extension emitted unconditionally
171+
Gia_Man_t* q = Gia_AigerRead((char*)fname, 0, 0, 0);
172+
remove(fname);
173+
174+
ASSERT_TRUE(q != nullptr);
175+
ASSERT_TRUE(q->vOrigins != nullptr);
176+
EXPECT_EQ(CountTotalOrigins(q), 1);
177+
178+
Gia_ManStop(p);
179+
Gia_ManStop(q);
180+
}
181+
182+
// --------------------------------------------------------------------------
183+
// engine: &nf standard-cell mapping
184+
//
185+
// &nf maps in place (Nf_ManDeriveMapping returns p->pGia without renumbering),
186+
// so origins on the AIG nodes must survive standard-cell mapping unchanged.
187+
// --------------------------------------------------------------------------
188+
189+
TEST(OriginsTest, NfMappingPreservesOrigins) {
190+
// Seed a design with origins and write it to AIGER (carries the "y" ext).
191+
Gia_Man_t* p = BuildOrOfAnds();
192+
SeedIdentityOrigins(p);
193+
const int seeded = CountTotalOrigins(p);
194+
ASSERT_GT(seeded, 0);
195+
const char* aig = "test_origins_nf_in.aig";
196+
Gia_AigerWrite(p, (char*)aig, 0, 0, 0);
197+
Gia_ManStop(p);
198+
199+
Abc_Start();
200+
Abc_Frame_t* pAbc = Abc_FrameGetGlobalFrame();
201+
202+
ASSERT_EQ(Cmd_CommandExecute(pAbc, "read_genlib test_origins.genlib"), 0);
203+
char cmd[256];
204+
snprintf(cmd, sizeof(cmd), "&read %s", aig);
205+
ASSERT_EQ(Cmd_CommandExecute(pAbc, cmd), 0);
206+
207+
Gia_Man_t* before = Abc_FrameReadGia(pAbc);
208+
ASSERT_TRUE(before != nullptr);
209+
ASSERT_GT(CountTotalOrigins(before), 0); // origins read back from "y"
210+
211+
ASSERT_EQ(Cmd_CommandExecute(pAbc, "&nf"), 0);
212+
213+
Gia_Man_t* after = Abc_FrameReadGia(pAbc);
214+
ASSERT_TRUE(after != nullptr);
215+
ASSERT_TRUE(after->vOrigins != nullptr);
216+
EXPECT_GT(CountTotalOrigins(after), 0); // preserved through &nf std-cell mapping
217+
218+
remove(aig);
219+
Abc_Stop();
220+
}
221+
222+
ABC_NAMESPACE_IMPL_END

test/gia/test_origins.genlib

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
GATE zero 0 O=CONST0;
2+
GATE one 0 O=CONST1;
3+
GATE buf 1 O=a; PIN * NONINV 1 999 1.0 0.0 1.0 0.0
4+
GATE inv 1 O=!a; PIN * INV 1 999 1.0 0.0 1.0 0.0
5+
GATE and2 3 O=a*b; PIN * NONINV 1 999 1.0 0.0 1.0 0.0
6+
GATE or2 3 O=a+b; PIN * NONINV 1 999 1.0 0.0 1.0 0.0
7+
GATE nand2 2 O=!(a*b); PIN * INV 1 999 1.0 0.0 1.0 0.0
8+
GATE nor2 2 O=!(a+b); PIN * INV 1 999 1.0 0.0 1.0 0.0

0 commit comments

Comments
 (0)