|
| 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 |
0 commit comments