@@ -78,19 +78,37 @@ static TestBB make_nop_bb(uint32_t bb_id, uint8_t epoch_base, uint32_t N) {
7878 return bb;
7979}
8080
81- // / Build a setup BB of exactly N insns with the given instructions + NOP padding + JMP.
82- static TestBB make_setup_bb (uint32_t bb_id, uint8_t epoch_base,
83- const std::vector<TestInstruction>& setup_insns,
84- uint32_t target_bb_id, uint32_t N) {
85- auto bb = make_bb (bb_id, epoch_base);
86- for (const auto & insn : setup_insns)
87- bb.instructions .push_back (insn);
88- // Pad with NOPs, leaving room for JMP at end
89- while (bb.instructions .size () + 1 < N)
90- bb.instructions .push_back ({VmOpcode::NOP, f_none (), 0 , 0 , 0 });
91- // JMP to first measured BB (or next setup BB)
92- bb.instructions .push_back ({VmOpcode::JMP, f_none (), 0 , 0 , target_bb_id});
93- return bb;
81+ // / Build setup BBs of exactly N insns each.
82+ // /
83+ // / For N≥2: one BB with setup_insns + NOP padding + JMP (fits in N insns).
84+ // / For N=1: one BB per setup instruction (no JMP — fallthrough handles it).
85+ // /
86+ // / Returns the BBs (may be 1 or many) and updates next_bb_id.
87+ static std::vector<TestBB> make_setup_bbs (uint32_t & next_bb_id,
88+ uint8_t epoch_base,
89+ const std::vector<TestInstruction>& setup_insns,
90+ uint32_t target_bb_id, uint32_t N) {
91+ std::vector<TestBB> result;
92+
93+ if (N >= 2 ) {
94+ // All setup insns + NOPs + JMP fit in one N-insn BB
95+ auto bb = make_bb (next_bb_id++, epoch_base);
96+ for (const auto & insn : setup_insns)
97+ bb.instructions .push_back (insn);
98+ while (bb.instructions .size () + 1 < N)
99+ bb.instructions .push_back ({VmOpcode::NOP, f_none (), 0 , 0 , 0 });
100+ bb.instructions .push_back ({VmOpcode::JMP, f_none (), 0 , 0 , target_bb_id});
101+ result.push_back (std::move (bb));
102+ } else {
103+ // N=1: each setup insn is its own 1-insn BB (fallthrough to next)
104+ for (const auto & insn : setup_insns) {
105+ auto bb = make_bb (next_bb_id++, epoch_base);
106+ bb.instructions .push_back (insn);
107+ result.push_back (std::move (bb));
108+ }
109+ }
110+
111+ return result;
94112}
95113
96114// ─── Trivial native for NATIVE_CALL benchmarks ────────────────────────
@@ -169,56 +187,49 @@ DUBenchProgram build_du_program(const OpcodeBenchSpec& spec,
169187 // ── Build setup BBs (untimed) ───────────────────────────────────
170188 uint32_t first_measured_id = 0 ; // set after setup BBs
171189
190+ // Helper: add setup BBs and update setup_du_count.
191+ auto add_setup = [&](const std::vector<TestInstruction>& insns) {
192+ auto sbs = make_setup_bbs (next_bb_id, 0xA0 , insns, 0 , N);
193+ prog.setup_du_count += static_cast <uint32_t >(sbs.size ());
194+ for (auto & sb : sbs)
195+ bbs.push_back (std::move (sb));
196+ };
197+
172198 switch (spec.setup ) {
173- case Setup::Reg1: {
199+ case Setup::Reg1:
174200 pool.push_back (42 );
175- auto sb = make_setup_bb (next_bb_id++, 0xA0 ,
176- {{VmOpcode::LOAD_CONST, f_pool (), spec.reg_a , 0 , 0 }},
177- 0 /* target set later */ , N);
178- bbs.push_back (std::move (sb));
179- prog.setup_du_count = 1 ;
201+ add_setup ({{VmOpcode::LOAD_CONST, f_pool (), spec.reg_a , 0 , 0 }});
180202 break ;
181- }
182- case Setup::Reg2: {
203+
204+ case Setup::Reg2:
183205 pool.push_back (42 );
184206 pool.push_back (3 );
185- auto sb = make_setup_bb (next_bb_id++, 0xA0 ,
186- {{VmOpcode::LOAD_CONST, f_pool (), spec.reg_a , 0 , 0 },
187- {VmOpcode::LOAD_CONST, f_pool (), spec.reg_b , 0 , 1 }},
188- 0 , N);
189- bbs.push_back (std::move (sb));
190- prog.setup_du_count = 1 ;
207+ add_setup ({{VmOpcode::LOAD_CONST, f_pool (), spec.reg_a , 0 , 0 },
208+ {VmOpcode::LOAD_CONST, f_pool (), spec.reg_b , 0 , 1 }});
191209 break ;
192- }
193- case Setup::Memory: {
210+
211+ case Setup::Memory:
194212 pool.push_back (42 );
195- auto sb = make_setup_bb (next_bb_id++, 0xA0 ,
196- {{VmOpcode::LOAD_CONST, f_pool (), 0 , 0 , 0 },
197- {VmOpcode::STORE, f_rm (), 0 , 0 , 0 }},
198- 0 , N);
199- bbs.push_back (std::move (sb));
200- prog.setup_du_count = 1 ;
213+ add_setup ({{VmOpcode::LOAD_CONST, f_pool (), 0 , 0 , 0 },
214+ {VmOpcode::STORE, f_rm (), 0 , 0 , 0 }});
201215 break ;
202- }
203- case Setup::OramPush: {
216+
217+ case Setup::OramPush:
204218 pool.push_back (42 );
205- auto sb = make_setup_bb (next_bb_id++, 0xA0 ,
206- {{VmOpcode::LOAD_CONST, f_pool (), 0 , 0 , 0 }},
207- 0 , N);
208- bbs.push_back (std::move (sb));
209- prog.setup_du_count = 1 ;
219+ add_setup ({{VmOpcode::LOAD_CONST, f_pool (), 0 , 0 , 0 }});
210220 break ;
211- }
221+
212222 case Setup::OramPop: {
213- // Need K PUSHes to fill stack, then K POPs measured.
214- // Setup: 1 LOAD_CONST BB + K PUSH BBs.
223+ // Setup: 1 LOAD_CONST BB + K PUSH BBs, all fallthrough (no JMP).
224+ // The LOAD_CONST BB must NOT use add_setup() because add_setup's
225+ // JMP gets fixed to first_measured_id, skipping the PUSH BBs.
215226 pool.push_back (42 );
216- auto sb = make_setup_bb (next_bb_id++, 0xA0 ,
217- {{ VmOpcode::LOAD_CONST, f_pool (), 0 , 0 , 0 }},
218- 0 , N);
219- bbs. push_back ( std::move (sb ));
220- prog.setup_du_count = 1 ;
221-
227+ {
228+ TestInstruction lc{ VmOpcode::LOAD_CONST, f_pool (), 0 , 0 , 0 };
229+ bbs. push_back ( make_measured_bb (next_bb_id++, 0xA0 ,
230+ lc, N, false ));
231+ prog.setup_du_count ++ ;
232+ }
222233 // K PUSH BBs (each is a DU with 1 PUSH + N-1 NOP)
223234 for (uint32_t i = 0 ; i < K; ++i) {
224235 TestInstruction push_insn{VmOpcode::PUSH, f_r (), 0 , 0 , 0 };
@@ -228,36 +239,32 @@ DUBenchProgram build_du_program(const OpcodeBenchSpec& spec,
228239 }
229240 break ;
230241 }
231- case Setup::Pool: {
242+
243+ case Setup::Pool:
232244 for (uint32_t i = 0 ; i < K; ++i)
233245 pool.push_back (i + 100 );
234- // Pool index cycles: aux = i % pool_count
235246 break ;
236- }
237- case Setup::CtxWrite: {
247+
248+ case Setup::CtxWrite:
238249 pool.push_back (0x800 );
239- auto sb = make_setup_bb (next_bb_id++, 0xA0 ,
240- {{VmOpcode::LOAD_CONST, f_pool (), 0 , 0 , 0 }},
241- 0 , N);
242- bbs.push_back (std::move (sb));
243- prog.setup_du_count = 1 ;
250+ add_setup ({{VmOpcode::LOAD_CONST, f_pool (), 0 , 0 , 0 }});
244251 break ;
245- }
246- case Setup::NativeCall: {
247- // One transition entry for all NATIVE_CALL instructions.
248- // All point to the same trivial native.
252+
253+ case Setup::NativeCall:
249254 break ;
250- }
255+
251256 default :
252257 break ;
253258 }
254259
255- // Fix setup BB JMP targets → first measured BB
260+ // Fix setup BB JMP targets → first measured BB (N≥2 only; N=1 uses fallthrough)
256261 first_measured_id = next_bb_id;
257262 for (auto & sb : bbs) {
258- auto & last = sb.instructions .back ();
259- if (last.opcode == VmOpcode::JMP)
260- last.aux = first_measured_id;
263+ if (!sb.instructions .empty ()) {
264+ auto & last = sb.instructions .back ();
265+ if (last.opcode == VmOpcode::JMP)
266+ last.aux = first_measured_id;
267+ }
261268 }
262269
263270 // ── Build K measured BBs ────────────────────────────────────────
0 commit comments