Rust · 21029 bytes Raw Blame History
1 //! IR builder — ergonomic API for constructing IR programmatically.
2 //!
3 //! Used by the AST→IR lowering pass and by tests. Manages ValueId
4 //! allocation, block insertion, and instruction emission.
5
6 use super::inst::*;
7 use super::types::*;
8 use crate::lexer::{Position, Span};
9
10 /// Builder for constructing a function's IR.
11 pub struct FuncBuilder<'a> {
12 func: &'a mut Function,
13 current_block: BlockId,
14 }
15
16 impl<'a> FuncBuilder<'a> {
17 pub fn new(func: &'a mut Function) -> Self {
18 let entry = func.entry;
19 Self {
20 func,
21 current_block: entry,
22 }
23 }
24
25 /// Switch to emitting into a different block.
26 pub fn set_block(&mut self, block: BlockId) {
27 self.current_block = block;
28 }
29
30 /// Get the current block ID.
31 pub fn current_block(&self) -> BlockId {
32 self.current_block
33 }
34
35 /// Create a new basic block and return its ID.
36 pub fn create_block(&mut self, name: &str) -> BlockId {
37 self.func.create_block(name)
38 }
39
40 /// Add a block parameter and return its ValueId.
41 pub fn add_block_param(&mut self, block: BlockId, ty: IrType) -> ValueId {
42 let id = self.func.next_value_id();
43 self.func.register_type(id, ty.clone());
44 self.func
45 .block_mut(block)
46 .params
47 .push(BlockParam { id, ty });
48 id
49 }
50
51 /// Get the underlying function.
52 pub fn func(&self) -> &Function {
53 self.func
54 }
55
56 // ---- Instruction emission ----
57
58 fn emit(&mut self, kind: InstKind, ty: IrType) -> ValueId {
59 let id = self.func.next_value_id();
60 self.func.register_type(id, ty.clone());
61 let inst = Inst {
62 id,
63 kind,
64 ty,
65 span: dummy_span(),
66 };
67 self.func.block_mut(self.current_block).insts.push(inst);
68 id
69 }
70
71 fn emit_with_span(&mut self, kind: InstKind, ty: IrType, span: Span) -> ValueId {
72 let id = self.func.next_value_id();
73 self.func.register_type(id, ty.clone());
74 let inst = Inst { id, kind, ty, span };
75 self.func.block_mut(self.current_block).insts.push(inst);
76 id
77 }
78
79 // ---- Constants ----
80
81 pub fn const_int(&mut self, value: i128, width: IntWidth) -> ValueId {
82 self.emit(InstKind::ConstInt(value, width), IrType::Int(width))
83 }
84
85 pub fn const_i32(&mut self, value: i32) -> ValueId {
86 self.const_int(value as i128, IntWidth::I32)
87 }
88
89 pub fn const_i64(&mut self, value: i64) -> ValueId {
90 self.const_int(value as i128, IntWidth::I64)
91 }
92
93 pub fn const_i128(&mut self, value: i128) -> ValueId {
94 self.const_int(value, IntWidth::I128)
95 }
96
97 pub fn const_float(&mut self, value: f64, width: FloatWidth) -> ValueId {
98 self.emit(InstKind::ConstFloat(value, width), IrType::Float(width))
99 }
100
101 pub fn const_f32(&mut self, value: f32) -> ValueId {
102 self.const_float(value as f64, FloatWidth::F32)
103 }
104
105 pub fn const_f64(&mut self, value: f64) -> ValueId {
106 self.const_float(value, FloatWidth::F64)
107 }
108
109 pub fn const_bool(&mut self, value: bool) -> ValueId {
110 self.emit(InstKind::ConstBool(value), IrType::Bool)
111 }
112
113 pub fn const_string(&mut self, value: &[u8]) -> ValueId {
114 let ty = IrType::Ptr(Box::new(IrType::Int(IntWidth::I8)));
115 self.emit(InstKind::ConstString(value.to_vec()), ty)
116 }
117
118 pub fn undef(&mut self, ty: IrType) -> ValueId {
119 let t = ty.clone();
120 self.emit(InstKind::Undef(ty), t)
121 }
122
123 // ---- Integer arithmetic ----
124
125 pub fn iadd(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId {
126 let ty = self
127 .func
128 .value_type(lhs)
129 .unwrap_or(IrType::Int(IntWidth::I32));
130 self.emit(InstKind::IAdd(lhs, rhs), ty)
131 }
132
133 pub fn isub(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId {
134 let ty = self
135 .func
136 .value_type(lhs)
137 .unwrap_or(IrType::Int(IntWidth::I32));
138 self.emit(InstKind::ISub(lhs, rhs), ty)
139 }
140
141 pub fn imul(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId {
142 let ty = self
143 .func
144 .value_type(lhs)
145 .unwrap_or(IrType::Int(IntWidth::I32));
146 self.emit(InstKind::IMul(lhs, rhs), ty)
147 }
148
149 pub fn idiv(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId {
150 let ty = self
151 .func
152 .value_type(lhs)
153 .unwrap_or(IrType::Int(IntWidth::I32));
154 self.emit(InstKind::IDiv(lhs, rhs), ty)
155 }
156
157 pub fn imod(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId {
158 let ty = self
159 .func
160 .value_type(lhs)
161 .unwrap_or(IrType::Int(IntWidth::I32));
162 self.emit(InstKind::IMod(lhs, rhs), ty)
163 }
164
165 pub fn ineg(&mut self, val: ValueId) -> ValueId {
166 let ty = self
167 .func
168 .value_type(val)
169 .unwrap_or(IrType::Int(IntWidth::I32));
170 self.emit(InstKind::INeg(val), ty)
171 }
172
173 // ---- Float arithmetic ----
174
175 pub fn fadd(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId {
176 let ty = self
177 .func
178 .value_type(lhs)
179 .unwrap_or(IrType::Float(FloatWidth::F32));
180 self.emit(InstKind::FAdd(lhs, rhs), ty)
181 }
182
183 pub fn fsub(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId {
184 let ty = self
185 .func
186 .value_type(lhs)
187 .unwrap_or(IrType::Float(FloatWidth::F32));
188 self.emit(InstKind::FSub(lhs, rhs), ty)
189 }
190
191 pub fn fmul(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId {
192 let ty = self
193 .func
194 .value_type(lhs)
195 .unwrap_or(IrType::Float(FloatWidth::F32));
196 self.emit(InstKind::FMul(lhs, rhs), ty)
197 }
198
199 pub fn fdiv(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId {
200 let ty = self
201 .func
202 .value_type(lhs)
203 .unwrap_or(IrType::Float(FloatWidth::F32));
204 self.emit(InstKind::FDiv(lhs, rhs), ty)
205 }
206
207 pub fn fneg(&mut self, val: ValueId) -> ValueId {
208 let ty = self
209 .func
210 .value_type(val)
211 .unwrap_or(IrType::Float(FloatWidth::F32));
212 self.emit(InstKind::FNeg(val), ty)
213 }
214
215 pub fn fabs(&mut self, val: ValueId) -> ValueId {
216 let ty = self
217 .func
218 .value_type(val)
219 .unwrap_or(IrType::Float(FloatWidth::F64));
220 self.emit(InstKind::FAbs(val), ty)
221 }
222
223 pub fn fsqrt(&mut self, val: ValueId) -> ValueId {
224 let ty = self
225 .func
226 .value_type(val)
227 .unwrap_or(IrType::Float(FloatWidth::F64));
228 self.emit(InstKind::FSqrt(val), ty)
229 }
230
231 pub fn fpow(&mut self, base: ValueId, exp: ValueId) -> ValueId {
232 let ty = self
233 .func
234 .value_type(base)
235 .unwrap_or(IrType::Float(FloatWidth::F64));
236 self.emit(InstKind::FPow(base, exp), ty)
237 }
238
239 // ---- Comparison ----
240
241 pub fn icmp(&mut self, op: CmpOp, lhs: ValueId, rhs: ValueId) -> ValueId {
242 self.emit(InstKind::ICmp(op, lhs, rhs), IrType::Bool)
243 }
244
245 pub fn fcmp(&mut self, op: CmpOp, lhs: ValueId, rhs: ValueId) -> ValueId {
246 self.emit(InstKind::FCmp(op, lhs, rhs), IrType::Bool)
247 }
248
249 // ---- Select ----
250
251 /// Conditional select: cond ? true_val : false_val
252 pub fn select(&mut self, cond: ValueId, true_val: ValueId, false_val: ValueId) -> ValueId {
253 let ty = self
254 .func
255 .value_type(true_val)
256 .unwrap_or(IrType::Int(IntWidth::I32));
257 self.emit(InstKind::Select(cond, true_val, false_val), ty)
258 }
259
260 // ---- Logic ----
261
262 pub fn and(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId {
263 self.emit(InstKind::And(lhs, rhs), IrType::Bool)
264 }
265
266 pub fn or(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId {
267 self.emit(InstKind::Or(lhs, rhs), IrType::Bool)
268 }
269
270 pub fn not(&mut self, val: ValueId) -> ValueId {
271 self.emit(InstKind::Not(val), IrType::Bool)
272 }
273
274 // ---- Bitwise ----
275
276 pub fn bit_and(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId {
277 let ty = self
278 .func
279 .value_type(lhs)
280 .unwrap_or(IrType::Int(IntWidth::I32));
281 self.emit(InstKind::BitAnd(lhs, rhs), ty)
282 }
283
284 pub fn bit_or(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId {
285 let ty = self
286 .func
287 .value_type(lhs)
288 .unwrap_or(IrType::Int(IntWidth::I32));
289 self.emit(InstKind::BitOr(lhs, rhs), ty)
290 }
291
292 pub fn bit_xor(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId {
293 let ty = self
294 .func
295 .value_type(lhs)
296 .unwrap_or(IrType::Int(IntWidth::I32));
297 self.emit(InstKind::BitXor(lhs, rhs), ty)
298 }
299
300 pub fn shl(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId {
301 let ty = self
302 .func
303 .value_type(lhs)
304 .unwrap_or(IrType::Int(IntWidth::I32));
305 self.emit(InstKind::Shl(lhs, rhs), ty)
306 }
307
308 pub fn bit_not(&mut self, val: ValueId) -> ValueId {
309 let ty = self
310 .func
311 .value_type(val)
312 .unwrap_or(IrType::Int(IntWidth::I32));
313 self.emit(InstKind::BitNot(val), ty)
314 }
315
316 pub fn lshr(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId {
317 let ty = self
318 .func
319 .value_type(lhs)
320 .unwrap_or(IrType::Int(IntWidth::I32));
321 self.emit(InstKind::LShr(lhs, rhs), ty)
322 }
323
324 pub fn clz(&mut self, val: ValueId) -> ValueId {
325 let ty = self
326 .func
327 .value_type(val)
328 .unwrap_or(IrType::Int(IntWidth::I32));
329 self.emit(InstKind::CountLeadingZeros(val), ty)
330 }
331
332 pub fn ctz(&mut self, val: ValueId) -> ValueId {
333 let ty = self
334 .func
335 .value_type(val)
336 .unwrap_or(IrType::Int(IntWidth::I32));
337 self.emit(InstKind::CountTrailingZeros(val), ty)
338 }
339
340 pub fn popcount(&mut self, val: ValueId) -> ValueId {
341 let ty = self
342 .func
343 .value_type(val)
344 .unwrap_or(IrType::Int(IntWidth::I32));
345 self.emit(InstKind::PopCount(val), ty)
346 }
347
348 // ---- Conversions ----
349
350 pub fn int_to_float(&mut self, val: ValueId, width: FloatWidth) -> ValueId {
351 self.emit(InstKind::IntToFloat(val, width), IrType::Float(width))
352 }
353
354 pub fn float_to_int(&mut self, val: ValueId, width: IntWidth) -> ValueId {
355 self.emit(InstKind::FloatToInt(val, width), IrType::Int(width))
356 }
357
358 pub fn float_extend(&mut self, val: ValueId, width: FloatWidth) -> ValueId {
359 self.emit(InstKind::FloatExtend(val, width), IrType::Float(width))
360 }
361
362 pub fn float_trunc(&mut self, val: ValueId, width: FloatWidth) -> ValueId {
363 self.emit(InstKind::FloatTrunc(val, width), IrType::Float(width))
364 }
365
366 pub fn int_extend(&mut self, val: ValueId, width: IntWidth, signed: bool) -> ValueId {
367 self.emit(InstKind::IntExtend(val, width, signed), IrType::Int(width))
368 }
369
370 pub fn int_trunc(&mut self, val: ValueId, width: IntWidth) -> ValueId {
371 self.emit(InstKind::IntTrunc(val, width), IrType::Int(width))
372 }
373
374 pub fn ptr_to_int(&mut self, val: ValueId) -> ValueId {
375 self.emit(InstKind::PtrToInt(val), IrType::Int(IntWidth::I64))
376 }
377
378 pub fn int_to_ptr(&mut self, val: ValueId, pointee: IrType) -> ValueId {
379 let ptr_ty = IrType::Ptr(Box::new(pointee));
380 self.emit(InstKind::IntToPtr(val, ptr_ty.clone()), ptr_ty)
381 }
382
383 // ---- Memory ----
384
385 pub fn alloca(&mut self, ty: IrType) -> ValueId {
386 let ptr_ty = IrType::Ptr(Box::new(ty.clone()));
387 self.emit(InstKind::Alloca(ty), ptr_ty)
388 }
389
390 /// Take the address of a module-level global. The result is
391 /// `Ptr<elem_ty>` and can flow into Load/Store/GEP just like
392 /// any other pointer. The global itself must be added to the
393 /// containing Module by the caller (typically lower.rs).
394 pub fn global_addr(&mut self, name: &str, elem_ty: IrType) -> ValueId {
395 let ptr_ty = IrType::Ptr(Box::new(elem_ty));
396 self.emit(InstKind::GlobalAddr(name.into()), ptr_ty)
397 }
398
399 pub fn load(&mut self, addr: ValueId) -> ValueId {
400 let ty = match self.func.value_type(addr) {
401 Some(IrType::Ptr(inner)) => *inner,
402 _ => IrType::Int(IntWidth::I64), // fallback
403 };
404 self.emit(InstKind::Load(addr), ty)
405 }
406
407 /// Load with an explicit result type (ignoring the pointer's inner type).
408 /// Used for loading fields from aggregate pointers (e.g., first 8 bytes of a descriptor).
409 pub fn load_typed(&mut self, addr: ValueId, ty: IrType) -> ValueId {
410 self.emit(InstKind::Load(addr), ty)
411 }
412
413 pub fn store(&mut self, value: ValueId, addr: ValueId) -> ValueId {
414 self.emit(InstKind::Store(value, addr), IrType::Void)
415 }
416
417 pub fn gep(&mut self, base: ValueId, indices: Vec<ValueId>, result_ty: IrType) -> ValueId {
418 self.emit(
419 InstKind::GetElementPtr(base, indices),
420 IrType::Ptr(Box::new(result_ty)),
421 )
422 }
423
424 // ---- Calls ----
425
426 pub fn call(&mut self, func_ref: FuncRef, args: Vec<ValueId>, ret_ty: IrType) -> ValueId {
427 self.emit(InstKind::Call(func_ref, args), ret_ty)
428 }
429
430 pub fn runtime_call(
431 &mut self,
432 func: RuntimeFunc,
433 args: Vec<ValueId>,
434 ret_ty: IrType,
435 ) -> ValueId {
436 self.emit(InstKind::RuntimeCall(func, args), ret_ty)
437 }
438
439 // ---- Aggregates ----
440
441 pub fn extract_field(&mut self, agg: ValueId, field: u32, field_ty: IrType) -> ValueId {
442 self.emit(InstKind::ExtractField(agg, field), field_ty)
443 }
444
445 pub fn insert_field(&mut self, agg: ValueId, field: u32, value: ValueId) -> ValueId {
446 let ty = self.func.value_type(agg).unwrap_or(IrType::Void);
447 self.emit(InstKind::InsertField(agg, field, value), ty)
448 }
449
450 // ---- SIMD vector ops (Sprint 12) ----
451
452 pub fn vadd(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId {
453 let ty = self.func.value_type(lhs).unwrap_or(IrType::Void);
454 self.emit(InstKind::VAdd(lhs, rhs), ty)
455 }
456
457 pub fn vsub(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId {
458 let ty = self.func.value_type(lhs).unwrap_or(IrType::Void);
459 self.emit(InstKind::VSub(lhs, rhs), ty)
460 }
461
462 pub fn vmul(&mut self, lhs: ValueId, rhs: ValueId) -> ValueId {
463 let ty = self.func.value_type(lhs).unwrap_or(IrType::Void);
464 self.emit(InstKind::VMul(lhs, rhs), ty)
465 }
466
467 pub fn vfma(&mut self, a: ValueId, b: ValueId, c: ValueId) -> ValueId {
468 let ty = self.func.value_type(a).unwrap_or(IrType::Void);
469 self.emit(InstKind::VFma(a, b, c), ty)
470 }
471
472 pub fn vload(&mut self, addr: ValueId, ty: IrType) -> ValueId {
473 self.emit(InstKind::VLoad(addr), ty)
474 }
475
476 pub fn vstore(&mut self, value: ValueId, addr: ValueId) -> ValueId {
477 self.emit(InstKind::VStore(value, addr), IrType::Void)
478 }
479
480 pub fn vbroadcast(&mut self, scalar: ValueId, vector_ty: IrType) -> ValueId {
481 self.emit(InstKind::VBroadcast(scalar), vector_ty)
482 }
483
484 pub fn vreduce_sum(&mut self, vector: ValueId, scalar_ty: IrType) -> ValueId {
485 self.emit(InstKind::VReduceSum(vector), scalar_ty)
486 }
487
488 pub fn vextract(&mut self, vector: ValueId, lane: u8, scalar_ty: IrType) -> ValueId {
489 self.emit(InstKind::VExtract(vector, lane), scalar_ty)
490 }
491
492 // ---- Terminators ----
493
494 pub fn ret(&mut self, value: Option<ValueId>) {
495 self.func.block_mut(self.current_block).terminator = Some(Terminator::Return(value));
496 }
497
498 pub fn ret_void(&mut self) {
499 self.ret(None);
500 }
501
502 pub fn branch(&mut self, dest: BlockId, args: Vec<ValueId>) {
503 self.func.block_mut(self.current_block).terminator = Some(Terminator::Branch(dest, args));
504 }
505
506 pub fn cond_branch(
507 &mut self,
508 cond: ValueId,
509 true_dest: BlockId,
510 true_args: Vec<ValueId>,
511 false_dest: BlockId,
512 false_args: Vec<ValueId>,
513 ) {
514 self.func.block_mut(self.current_block).terminator = Some(Terminator::CondBranch {
515 cond,
516 true_dest,
517 true_args,
518 false_dest,
519 false_args,
520 });
521 }
522
523 pub fn switch(&mut self, selector: ValueId, cases: Vec<(i64, BlockId)>, default: BlockId) {
524 self.func.block_mut(self.current_block).terminator = Some(Terminator::Switch {
525 selector,
526 cases,
527 default,
528 });
529 }
530
531 pub fn unreachable(&mut self) {
532 self.func.block_mut(self.current_block).terminator = Some(Terminator::Unreachable);
533 }
534
535 // ---- Test helpers (bypass type inference for verifier tests) ----
536 #[cfg(test)]
537 pub fn emit_bogus_iadd(&mut self, a: ValueId, b: ValueId) -> ValueId {
538 self.emit(InstKind::IAdd(a, b), IrType::Int(IntWidth::I32))
539 }
540
541 #[cfg(test)]
542 pub fn emit_bogus_store(&mut self, val: ValueId, addr: ValueId) -> ValueId {
543 self.emit(InstKind::Store(val, addr), IrType::Void)
544 }
545 }
546
547 fn dummy_span() -> Span {
548 Span {
549 file_id: 0,
550 start: Position { line: 0, col: 0 },
551 end: Position { line: 0, col: 0 },
552 }
553 }
554
555 #[cfg(test)]
556 mod tests {
557 use super::*;
558
559 #[test]
560 fn build_simple_function() {
561 let mut func = Function::new("main".into(), vec![], IrType::Void);
562 {
563 let mut b = FuncBuilder::new(&mut func);
564 let x = b.const_i32(10);
565 let y = b.const_i32(20);
566 let z = b.iadd(x, y);
567 b.runtime_call(RuntimeFunc::PrintInt, vec![z], IrType::Void);
568 b.ret_void();
569 }
570 assert_eq!(func.blocks.len(), 1);
571 assert_eq!(func.blocks[0].insts.len(), 4); // const, const, iadd, runtime_call
572 assert!(func.blocks[0].terminator.is_some());
573 }
574
575 #[test]
576 fn build_branching_function() {
577 let mut func = Function::new("test".into(), vec![], IrType::Void);
578 {
579 let mut b = FuncBuilder::new(&mut func);
580 let cond = b.const_bool(true);
581 let bb_true = b.create_block("then");
582 let bb_false = b.create_block("else");
583 b.cond_branch(cond, bb_true, vec![], bb_false, vec![]);
584
585 b.set_block(bb_true);
586 b.ret_void();
587
588 b.set_block(bb_false);
589 b.ret_void();
590 }
591 assert_eq!(func.blocks.len(), 3);
592 }
593
594 #[test]
595 fn build_block_params() {
596 let mut func = Function::new("loop".into(), vec![], IrType::Void);
597 {
598 let mut b = FuncBuilder::new(&mut func);
599 let header = b.create_block("header");
600 let i_param = b.add_block_param(header, IrType::Int(IntWidth::I32));
601 let init = b.const_i32(0);
602 b.branch(header, vec![init]);
603
604 b.set_block(header);
605 let one = b.const_i32(1);
606 let next = b.iadd(i_param, one);
607 let limit = b.const_i32(10);
608 let done = b.icmp(CmpOp::Ge, next, limit);
609 let exit = b.create_block("exit");
610 b.cond_branch(done, exit, vec![], header, vec![next]);
611
612 b.set_block(exit);
613 b.ret_void();
614 }
615 assert_eq!(func.blocks.len(), 3);
616 assert_eq!(func.block(BlockId(1)).params.len(), 1); // header has 1 param
617 }
618
619 #[test]
620 fn alloca_load_store() {
621 let mut func = Function::new("test".into(), vec![], IrType::Void);
622 {
623 let mut b = FuncBuilder::new(&mut func);
624 let addr = b.alloca(IrType::Int(IntWidth::I32));
625 let val = b.const_i32(42);
626 b.store(val, addr);
627 let loaded = b.load(addr);
628 b.runtime_call(RuntimeFunc::PrintInt, vec![loaded], IrType::Void);
629 b.ret_void();
630 }
631 assert_eq!(func.blocks[0].insts.len(), 5); // alloca, const, store, load, runtime_call
632 }
633
634 #[test]
635 fn float_arithmetic() {
636 let mut func = Function::new("test".into(), vec![], IrType::Void);
637 {
638 let mut b = FuncBuilder::new(&mut func);
639 let a = b.const_f64(3.14);
640 let c = b.const_f64(2.0);
641 let sum = b.fadd(a, c);
642 let neg = b.fneg(sum);
643 b.runtime_call(RuntimeFunc::PrintReal, vec![neg], IrType::Void);
644 b.ret_void();
645 }
646 assert_eq!(func.blocks[0].insts.len(), 5);
647 }
648
649 #[test]
650 fn conversions() {
651 let mut func = Function::new("test".into(), vec![], IrType::Void);
652 {
653 let mut b = FuncBuilder::new(&mut func);
654 let i = b.const_i32(42);
655 let f = b.int_to_float(i, FloatWidth::F64);
656 let back = b.float_to_int(f, IntWidth::I32);
657 let _ = back;
658 b.ret_void();
659 }
660 assert_eq!(func.blocks[0].insts.len(), 3);
661 }
662 }
663