Skip to main content

solar_codegen/mir/
builder.rs

1//! MIR function builder.
2
3use super::{
4    BlockId, Function, FunctionId, Immediate, InstId, InstKind, Instruction, MemoryRegion, MirType,
5    StorageAlias, Terminator, Value, ValueId,
6};
7use alloy_primitives::U256;
8use smallvec::SmallVec;
9
10/// A builder for constructing MIR functions.
11pub struct FunctionBuilder<'a> {
12    /// The function being built.
13    func: &'a mut Function,
14    /// The current block.
15    current_block: BlockId,
16}
17
18impl<'a> FunctionBuilder<'a> {
19    /// Creates a new function builder.
20    pub fn new(func: &'a mut Function) -> Self {
21        let entry = func.entry_block;
22        Self { func, current_block: entry }
23    }
24
25    /// Returns the current block.
26    #[must_use]
27    pub const fn current_block(&self) -> BlockId {
28        self.current_block
29    }
30
31    /// Switches to a different block.
32    pub fn switch_to_block(&mut self, block: BlockId) {
33        self.current_block = block;
34    }
35
36    /// Creates a new basic block.
37    pub fn create_block(&mut self) -> BlockId {
38        self.func.alloc_block()
39    }
40
41    /// Adds an argument to the function.
42    pub fn add_param(&mut self, ty: MirType) -> ValueId {
43        let index = self.func.params.len() as u32;
44        self.func.params.push(ty);
45        self.func.alloc_value(Value::Arg { index, ty })
46    }
47
48    /// Adds a return type to the function.
49    pub fn add_return(&mut self, ty: MirType) {
50        self.func.returns.push(ty);
51    }
52
53    /// Creates an immediate value.
54    pub fn imm_u256(&mut self, value: U256) -> ValueId {
55        self.func.alloc_value(Value::Immediate(Immediate::uint256(value)))
56    }
57
58    /// Creates a u64 immediate value.
59    pub fn imm_u64(&mut self, value: u64) -> ValueId {
60        self.imm_u256(U256::from(value))
61    }
62
63    /// Creates a boolean immediate.
64    pub fn imm_bool(&mut self, value: bool) -> ValueId {
65        self.func.alloc_value(Value::Immediate(Immediate::bool(value)))
66    }
67
68    /// Creates an undefined value.
69    pub fn undef(&mut self, ty: MirType) -> ValueId {
70        self.func.alloc_value(Value::Undef(ty))
71    }
72
73    /// Creates an error sentinel value for an already-reported lowering error.
74    pub fn error_value(&mut self, guar: solar_interface::diagnostics::ErrorGuaranteed) -> ValueId {
75        self.func.alloc_value(Value::Error(guar))
76    }
77
78    fn emit_inst_raw(&mut self, kind: InstKind, result_ty: Option<MirType>) -> InstId {
79        let mut inst = Instruction::new(kind, result_ty);
80        inst.metadata.set_effect(Some(inst.kind.effect_kind()));
81        inst.metadata.set_memory_region(self.memory_region_for_inst(&inst.kind));
82        inst.metadata.set_storage_alias(self.storage_alias_for_inst(&inst.kind));
83
84        let inst_id = self.func.alloc_inst(inst);
85        self.func.blocks[self.current_block].instructions.push(inst_id);
86        inst_id
87    }
88
89    fn emit_inst(&mut self, kind: InstKind, result_ty: Option<MirType>) -> ValueId {
90        debug_assert!(result_ty.is_some(), "value-producing instructions must have a result type");
91        let inst_id = self.emit_inst_raw(kind, result_ty);
92        self.func.alloc_value(Value::Inst(inst_id))
93    }
94
95    /// Emits an instruction that produces no value, such as a store or a log.
96    ///
97    /// No result [`Value`] is allocated: only value-producing instructions get
98    /// an entry in the function's value table.
99    fn emit_void_inst(&mut self, kind: InstKind) {
100        self.emit_inst_raw(kind, None);
101    }
102
103    fn memory_region_for_inst(&self, kind: &InstKind) -> Option<MemoryRegion> {
104        let addr = match *kind {
105            InstKind::MLoad(addr)
106            | InstKind::MStore(addr, _)
107            | InstKind::MStore8(addr, _)
108            | InstKind::Keccak256(addr, _) => addr,
109            InstKind::MCopy(dest, _, _)
110            | InstKind::CalldataCopy(dest, _, _)
111            | InstKind::CodeCopy(dest, _, _)
112            | InstKind::ReturnDataCopy(dest, _, _)
113            | InstKind::ExtCodeCopy(_, dest, _, _) => dest,
114            _ => return None,
115        };
116        Some(self.memory_region_for_addr(addr))
117    }
118
119    fn memory_region_for_addr(&self, addr: ValueId) -> MemoryRegion {
120        match self.func.value(addr) {
121            Value::Immediate(imm)
122                if imm.as_u256().is_some_and(|value| value < U256::from(0x80)) =>
123            {
124                MemoryRegion::Scratch
125            }
126            Value::Inst(inst_id) => match self.func.instructions[*inst_id].kind {
127                InstKind::InternalFrameAddr(_) => MemoryRegion::InternalFrame,
128                InstKind::Add(lhs, rhs) if self.is_internal_frame_add(lhs, rhs) => {
129                    MemoryRegion::InternalFrame
130                }
131                InstKind::Sub(lhs, rhs)
132                    if self.is_internal_frame_addr(lhs) && self.is_immediate(rhs) =>
133                {
134                    MemoryRegion::InternalFrame
135                }
136                _ => MemoryRegion::Unknown,
137            },
138            Value::Arg { .. } | Value::Immediate(_) | Value::Undef(_) | Value::Error(_) => {
139                MemoryRegion::Unknown
140            }
141        }
142    }
143
144    fn is_internal_frame_add(&self, lhs: ValueId, rhs: ValueId) -> bool {
145        (self.is_internal_frame_addr(lhs) && self.is_immediate(rhs))
146            || (self.is_internal_frame_addr(rhs) && self.is_immediate(lhs))
147    }
148
149    fn is_internal_frame_addr(&self, value: ValueId) -> bool {
150        matches!(
151            self.func.value(value),
152            Value::Inst(inst_id)
153                if matches!(self.func.instructions[*inst_id].kind, InstKind::InternalFrameAddr(_))
154        )
155    }
156
157    fn is_immediate(&self, value: ValueId) -> bool {
158        matches!(self.func.value(value), Value::Immediate(_))
159    }
160
161    fn storage_alias_for_inst(&self, kind: &InstKind) -> Option<StorageAlias> {
162        match *kind {
163            InstKind::SLoad(slot) | InstKind::SStore(slot, _) => Some(self.storage_alias(slot)),
164            _ => None,
165        }
166    }
167
168    fn storage_alias(&self, slot: ValueId) -> StorageAlias {
169        StorageAlias::for_value(self.func, slot)
170    }
171
172    /// Emits an add instruction.
173    pub fn add(&mut self, a: ValueId, b: ValueId) -> ValueId {
174        self.emit_inst(InstKind::Add(a, b), Some(MirType::uint256()))
175    }
176
177    /// Emits a sub instruction.
178    pub fn sub(&mut self, a: ValueId, b: ValueId) -> ValueId {
179        self.emit_inst(InstKind::Sub(a, b), Some(MirType::uint256()))
180    }
181
182    /// Emits a mul instruction.
183    pub fn mul(&mut self, a: ValueId, b: ValueId) -> ValueId {
184        self.emit_inst(InstKind::Mul(a, b), Some(MirType::uint256()))
185    }
186
187    /// Emits a div instruction.
188    pub fn div(&mut self, a: ValueId, b: ValueId) -> ValueId {
189        self.emit_inst(InstKind::Div(a, b), Some(MirType::uint256()))
190    }
191
192    /// Emits a sdiv instruction.
193    pub fn sdiv(&mut self, a: ValueId, b: ValueId) -> ValueId {
194        self.emit_inst(InstKind::SDiv(a, b), Some(MirType::int256()))
195    }
196
197    /// Emits a mod instruction.
198    pub fn mod_(&mut self, a: ValueId, b: ValueId) -> ValueId {
199        self.emit_inst(InstKind::Mod(a, b), Some(MirType::uint256()))
200    }
201
202    /// Emits an addmod instruction.
203    pub fn addmod(&mut self, a: ValueId, b: ValueId, n: ValueId) -> ValueId {
204        self.emit_inst(InstKind::AddMod(a, b, n), Some(MirType::uint256()))
205    }
206
207    /// Emits a mulmod instruction.
208    pub fn mulmod(&mut self, a: ValueId, b: ValueId, n: ValueId) -> ValueId {
209        self.emit_inst(InstKind::MulMod(a, b, n), Some(MirType::uint256()))
210    }
211
212    /// Emits a smod instruction.
213    pub fn smod(&mut self, a: ValueId, b: ValueId) -> ValueId {
214        self.emit_inst(InstKind::SMod(a, b), Some(MirType::int256()))
215    }
216
217    /// Emits an exp instruction.
218    pub fn exp(&mut self, a: ValueId, b: ValueId) -> ValueId {
219        self.emit_inst(InstKind::Exp(a, b), Some(MirType::uint256()))
220    }
221
222    /// Emits an and instruction.
223    pub fn and(&mut self, a: ValueId, b: ValueId) -> ValueId {
224        self.emit_inst(InstKind::And(a, b), Some(MirType::uint256()))
225    }
226
227    /// Emits an or instruction.
228    pub fn or(&mut self, a: ValueId, b: ValueId) -> ValueId {
229        self.emit_inst(InstKind::Or(a, b), Some(MirType::uint256()))
230    }
231
232    /// Emits a xor instruction.
233    pub fn xor(&mut self, a: ValueId, b: ValueId) -> ValueId {
234        self.emit_inst(InstKind::Xor(a, b), Some(MirType::uint256()))
235    }
236
237    /// Emits a not instruction.
238    pub fn not(&mut self, a: ValueId) -> ValueId {
239        self.emit_inst(InstKind::Not(a), Some(MirType::uint256()))
240    }
241
242    /// Emits a shl instruction.
243    pub fn shl(&mut self, shift: ValueId, value: ValueId) -> ValueId {
244        self.emit_inst(InstKind::Shl(shift, value), Some(MirType::uint256()))
245    }
246
247    /// Emits a shr instruction.
248    pub fn shr(&mut self, shift: ValueId, value: ValueId) -> ValueId {
249        self.emit_inst(InstKind::Shr(shift, value), Some(MirType::uint256()))
250    }
251
252    /// Emits a sar instruction.
253    pub fn sar(&mut self, shift: ValueId, value: ValueId) -> ValueId {
254        self.emit_inst(InstKind::Sar(shift, value), Some(MirType::int256()))
255    }
256
257    /// Emits a lt instruction.
258    pub fn lt(&mut self, a: ValueId, b: ValueId) -> ValueId {
259        self.emit_inst(InstKind::Lt(a, b), Some(MirType::Bool))
260    }
261
262    /// Emits a gt instruction.
263    pub fn gt(&mut self, a: ValueId, b: ValueId) -> ValueId {
264        self.emit_inst(InstKind::Gt(a, b), Some(MirType::Bool))
265    }
266
267    /// Emits a slt instruction.
268    pub fn slt(&mut self, a: ValueId, b: ValueId) -> ValueId {
269        self.emit_inst(InstKind::SLt(a, b), Some(MirType::Bool))
270    }
271
272    /// Emits a sgt instruction.
273    pub fn sgt(&mut self, a: ValueId, b: ValueId) -> ValueId {
274        self.emit_inst(InstKind::SGt(a, b), Some(MirType::Bool))
275    }
276
277    /// Emits an eq instruction.
278    pub fn eq(&mut self, a: ValueId, b: ValueId) -> ValueId {
279        self.emit_inst(InstKind::Eq(a, b), Some(MirType::Bool))
280    }
281
282    /// Emits an iszero instruction.
283    pub fn iszero(&mut self, a: ValueId) -> ValueId {
284        self.emit_inst(InstKind::IsZero(a), Some(MirType::Bool))
285    }
286
287    /// Emits a byte instruction.
288    pub fn byte(&mut self, index: ValueId, value: ValueId) -> ValueId {
289        self.emit_inst(InstKind::Byte(index, value), Some(MirType::uint256()))
290    }
291
292    /// Emits a signextend instruction.
293    pub fn signextend(&mut self, size: ValueId, value: ValueId) -> ValueId {
294        self.emit_inst(InstKind::SignExtend(size, value), Some(MirType::int256()))
295    }
296
297    /// Emits an mload instruction.
298    pub fn mload(&mut self, offset: ValueId) -> ValueId {
299        self.emit_inst(InstKind::MLoad(offset), Some(MirType::uint256()))
300    }
301
302    /// Emits an mstore instruction.
303    pub fn mstore(&mut self, offset: ValueId, value: ValueId) {
304        self.emit_void_inst(InstKind::MStore(offset, value))
305    }
306
307    /// Emits an mstore8 instruction.
308    pub fn mstore8(&mut self, offset: ValueId, value: ValueId) {
309        self.emit_void_inst(InstKind::MStore8(offset, value))
310    }
311
312    /// Emits an msize instruction.
313    pub fn msize(&mut self) -> ValueId {
314        self.emit_inst(InstKind::MSize, Some(MirType::uint256()))
315    }
316
317    /// Emits an mcopy instruction.
318    pub fn mcopy(&mut self, dest: ValueId, src: ValueId, len: ValueId) {
319        self.emit_void_inst(InstKind::MCopy(dest, src, len))
320    }
321
322    /// Emits an sload instruction.
323    pub fn sload(&mut self, slot: ValueId) -> ValueId {
324        self.emit_inst(InstKind::SLoad(slot), Some(MirType::uint256()))
325    }
326
327    /// Emits an sstore instruction.
328    pub fn sstore(&mut self, slot: ValueId, value: ValueId) {
329        self.emit_void_inst(InstKind::SStore(slot, value))
330    }
331
332    /// Emits a tload instruction.
333    pub fn tload(&mut self, slot: ValueId) -> ValueId {
334        self.emit_inst(InstKind::TLoad(slot), Some(MirType::uint256()))
335    }
336
337    /// Emits a tstore instruction.
338    pub fn tstore(&mut self, slot: ValueId, value: ValueId) {
339        self.emit_void_inst(InstKind::TStore(slot, value))
340    }
341
342    /// Emits a calldataload instruction.
343    pub fn calldataload(&mut self, offset: ValueId) -> ValueId {
344        self.emit_inst(InstKind::CalldataLoad(offset), Some(MirType::uint256()))
345    }
346
347    /// Emits a calldatasize instruction.
348    pub fn calldatasize(&mut self) -> ValueId {
349        self.emit_inst(InstKind::CalldataSize, Some(MirType::uint256()))
350    }
351
352    /// Emits a calldatacopy instruction.
353    pub fn calldatacopy(&mut self, dest: ValueId, offset: ValueId, size: ValueId) {
354        self.emit_void_inst(InstKind::CalldataCopy(dest, offset, size))
355    }
356
357    /// Emits a codesize instruction.
358    pub fn codesize(&mut self) -> ValueId {
359        self.emit_inst(InstKind::CodeSize, Some(MirType::uint256()))
360    }
361
362    /// Emits an extcodesize instruction.
363    pub fn extcodesize(&mut self, addr: ValueId) -> ValueId {
364        self.emit_inst(InstKind::ExtCodeSize(addr), Some(MirType::uint256()))
365    }
366
367    /// Emits a loadimmutable instruction for the immutable at `offset`.
368    pub fn load_immutable(&mut self, offset: u32) -> ValueId {
369        self.emit_inst(InstKind::LoadImmutable(offset), Some(MirType::uint256()))
370    }
371
372    /// Emits an extcodecopy instruction.
373    pub fn extcodecopy(&mut self, addr: ValueId, dest: ValueId, offset: ValueId, size: ValueId) {
374        self.emit_void_inst(InstKind::ExtCodeCopy(addr, dest, offset, size))
375    }
376
377    /// Emits an extcodehash instruction.
378    pub fn extcodehash(&mut self, addr: ValueId) -> ValueId {
379        self.emit_inst(InstKind::ExtCodeHash(addr), Some(MirType::uint256()))
380    }
381
382    /// Emits a returndatasize instruction.
383    pub fn returndatasize(&mut self) -> ValueId {
384        self.emit_inst(InstKind::ReturnDataSize, Some(MirType::uint256()))
385    }
386
387    /// Emits a returndatacopy instruction.
388    pub fn returndatacopy(&mut self, dest: ValueId, offset: ValueId, size: ValueId) {
389        self.emit_void_inst(InstKind::ReturnDataCopy(dest, offset, size))
390    }
391
392    /// Emits an internal function call.
393    pub fn internal_call(
394        &mut self,
395        function: FunctionId,
396        args: Vec<ValueId>,
397        result_ty: MirType,
398        returns: usize,
399    ) -> ValueId {
400        let returns = u32::try_from(returns).expect("too many internal call return values");
401        self.emit_inst(
402            InstKind::InternalCall { function, args: args.into(), returns },
403            Some(result_ty),
404        )
405    }
406
407    /// Emits an internal function call whose result, if any, is not used as a value.
408    pub fn internal_call_void(&mut self, function: FunctionId, args: Vec<ValueId>, returns: usize) {
409        let returns = u32::try_from(returns).expect("too many internal call return values");
410        self.emit_void_inst(InstKind::InternalCall { function, args: args.into(), returns });
411    }
412
413    /// Emits an address inside the current internal-call frame.
414    pub fn internal_frame_addr(&mut self, offset: u64) -> ValueId {
415        self.emit_inst(InstKind::InternalFrameAddr(offset), Some(MirType::MemPtr))
416    }
417
418    /// Emits a caller instruction.
419    pub fn caller(&mut self) -> ValueId {
420        self.emit_inst(InstKind::Caller, Some(MirType::Address))
421    }
422
423    /// Emits a callvalue instruction.
424    pub fn callvalue(&mut self) -> ValueId {
425        self.emit_inst(InstKind::CallValue, Some(MirType::uint256()))
426    }
427
428    /// Emits an origin instruction.
429    pub fn origin(&mut self) -> ValueId {
430        self.emit_inst(InstKind::Origin, Some(MirType::Address))
431    }
432
433    /// Emits a gasprice instruction.
434    pub fn gasprice(&mut self) -> ValueId {
435        self.emit_inst(InstKind::GasPrice, Some(MirType::uint256()))
436    }
437
438    /// Emits a blockhash instruction.
439    pub fn blockhash(&mut self, block_num: ValueId) -> ValueId {
440        self.emit_inst(InstKind::BlockHash(block_num), Some(MirType::FixedBytes(32)))
441    }
442
443    /// Emits a coinbase instruction.
444    pub fn coinbase(&mut self) -> ValueId {
445        self.emit_inst(InstKind::Coinbase, Some(MirType::Address))
446    }
447
448    /// Emits a timestamp instruction.
449    pub fn timestamp(&mut self) -> ValueId {
450        self.emit_inst(InstKind::Timestamp, Some(MirType::uint256()))
451    }
452
453    /// Emits a number instruction.
454    pub fn number(&mut self) -> ValueId {
455        self.emit_inst(InstKind::BlockNumber, Some(MirType::uint256()))
456    }
457
458    /// Emits a prevrandao instruction.
459    pub fn prevrandao(&mut self) -> ValueId {
460        self.emit_inst(InstKind::PrevRandao, Some(MirType::uint256()))
461    }
462
463    /// Emits a gaslimit instruction.
464    pub fn gaslimit(&mut self) -> ValueId {
465        self.emit_inst(InstKind::GasLimit, Some(MirType::uint256()))
466    }
467
468    /// Emits a chainid instruction.
469    pub fn chainid(&mut self) -> ValueId {
470        self.emit_inst(InstKind::ChainId, Some(MirType::uint256()))
471    }
472
473    /// Emits an address instruction.
474    pub fn address(&mut self) -> ValueId {
475        self.emit_inst(InstKind::Address, Some(MirType::Address))
476    }
477
478    /// Emits a balance instruction.
479    pub fn balance(&mut self, addr: ValueId) -> ValueId {
480        self.emit_inst(InstKind::Balance(addr), Some(MirType::uint256()))
481    }
482
483    /// Emits a selfbalance instruction.
484    pub fn selfbalance(&mut self) -> ValueId {
485        self.emit_inst(InstKind::SelfBalance, Some(MirType::uint256()))
486    }
487
488    /// Emits a gas instruction.
489    pub fn gas(&mut self) -> ValueId {
490        self.emit_inst(InstKind::Gas, Some(MirType::uint256()))
491    }
492
493    /// Emits a keccak256 instruction.
494    pub fn keccak256(&mut self, offset: ValueId, size: ValueId) -> ValueId {
495        self.emit_inst(InstKind::Keccak256(offset, size), Some(MirType::bytes32()))
496    }
497
498    /// Emits a basefee instruction.
499    pub fn basefee(&mut self) -> ValueId {
500        self.emit_inst(InstKind::BaseFee, Some(MirType::uint256()))
501    }
502
503    /// Emits a blobbasefee instruction.
504    pub fn blobbasefee(&mut self) -> ValueId {
505        self.emit_inst(InstKind::BlobBaseFee, Some(MirType::uint256()))
506    }
507
508    /// Emits a blobhash instruction.
509    pub fn blobhash(&mut self, index: ValueId) -> ValueId {
510        self.emit_inst(InstKind::BlobHash(index), Some(MirType::FixedBytes(32)))
511    }
512
513    /// Emits a call instruction (external call).
514    #[allow(clippy::too_many_arguments)]
515    pub fn call(
516        &mut self,
517        gas: ValueId,
518        addr: ValueId,
519        value: ValueId,
520        args_offset: ValueId,
521        args_size: ValueId,
522        ret_offset: ValueId,
523        ret_size: ValueId,
524    ) -> ValueId {
525        self.emit_inst(
526            InstKind::Call { gas, addr, value, args_offset, args_size, ret_offset, ret_size },
527            Some(MirType::uint256()),
528        )
529    }
530
531    /// Emits a staticcall instruction (read-only external call).
532    pub fn staticcall(
533        &mut self,
534        gas: ValueId,
535        addr: ValueId,
536        args_offset: ValueId,
537        args_size: ValueId,
538        ret_offset: ValueId,
539        ret_size: ValueId,
540    ) -> ValueId {
541        self.emit_inst(
542            InstKind::StaticCall { gas, addr, args_offset, args_size, ret_offset, ret_size },
543            Some(MirType::uint256()),
544        )
545    }
546
547    /// Emits a delegatecall instruction (call with caller's context).
548    pub fn delegatecall(
549        &mut self,
550        gas: ValueId,
551        addr: ValueId,
552        args_offset: ValueId,
553        args_size: ValueId,
554        ret_offset: ValueId,
555        ret_size: ValueId,
556    ) -> ValueId {
557        self.emit_inst(
558            InstKind::DelegateCall { gas, addr, args_offset, args_size, ret_offset, ret_size },
559            Some(MirType::uint256()),
560        )
561    }
562
563    /// Emits a create instruction (deploy a contract).
564    pub fn create(&mut self, value: ValueId, offset: ValueId, size: ValueId) -> ValueId {
565        self.emit_inst(InstKind::Create(value, offset, size), Some(MirType::Address))
566    }
567
568    /// Emits a create2 instruction (deploy a contract with salt).
569    pub fn create2(
570        &mut self,
571        value: ValueId,
572        offset: ValueId,
573        size: ValueId,
574        salt: ValueId,
575    ) -> ValueId {
576        self.emit_inst(InstKind::Create2(value, offset, size, salt), Some(MirType::Address))
577    }
578
579    /// Emits a codecopy instruction.
580    pub fn codecopy(&mut self, dest: ValueId, offset: ValueId, size: ValueId) {
581        self.emit_void_inst(InstKind::CodeCopy(dest, offset, size))
582    }
583
584    /// Emits a log0 instruction (event with no topics).
585    pub fn log0(&mut self, offset: ValueId, size: ValueId) {
586        self.emit_void_inst(InstKind::Log0(offset, size));
587    }
588
589    /// Emits a log1 instruction (event with 1 topic).
590    pub fn log1(&mut self, offset: ValueId, size: ValueId, topic1: ValueId) {
591        self.emit_void_inst(InstKind::Log1(offset, size, topic1));
592    }
593
594    /// Emits a log2 instruction (event with 2 topics).
595    pub fn log2(&mut self, offset: ValueId, size: ValueId, topic1: ValueId, topic2: ValueId) {
596        self.emit_void_inst(InstKind::Log2(offset, size, topic1, topic2));
597    }
598
599    /// Emits a log3 instruction (event with 3 topics).
600    pub fn log3(
601        &mut self,
602        offset: ValueId,
603        size: ValueId,
604        topic1: ValueId,
605        topic2: ValueId,
606        topic3: ValueId,
607    ) {
608        self.emit_void_inst(InstKind::Log3(offset, size, topic1, topic2, topic3));
609    }
610
611    /// Emits a log4 instruction (event with 4 topics).
612    pub fn log4(
613        &mut self,
614        offset: ValueId,
615        size: ValueId,
616        topic1: ValueId,
617        topic2: ValueId,
618        topic3: ValueId,
619        topic4: ValueId,
620    ) {
621        self.emit_void_inst(InstKind::Log4(offset, size, topic1, topic2, topic3, topic4));
622    }
623
624    /// Emits a select instruction.
625    pub fn select(&mut self, cond: ValueId, then_val: ValueId, else_val: ValueId) -> ValueId {
626        self.emit_inst(InstKind::Select(cond, then_val, else_val), Some(MirType::uint256()))
627    }
628
629    /// Emits a phi instruction. `incoming` pairs each predecessor block of the
630    /// current block with the value the phi takes when control arrives from
631    /// that block. Emit phis before any other instruction in their block.
632    pub fn phi(&mut self, incoming: Vec<(BlockId, ValueId)>) -> ValueId {
633        self.emit_inst(InstKind::Phi(incoming), Some(MirType::uint256()))
634    }
635
636    /// Adds an incoming `(block, value)` edge to an existing phi. This is used
637    /// to patch loop-carried phis whose back-edge values are only known after
638    /// the loop body has been built.
639    ///
640    /// # Panics
641    ///
642    /// Panics if `phi` does not refer to a phi instruction result.
643    pub fn add_phi_incoming(&mut self, phi: ValueId, block: BlockId, value: ValueId) {
644        let Value::Inst(inst_id) = *self.func.value(phi) else {
645            panic!("add_phi_incoming: value is not an instruction result");
646        };
647        let InstKind::Phi(incoming) = &mut self.func.instructions[inst_id].kind else {
648            panic!("add_phi_incoming: instruction is not a phi");
649        };
650        incoming.push((block, value));
651    }
652
653    /// Sets a jump terminator.
654    pub fn jump(&mut self, target: BlockId) {
655        let block = &mut self.func.blocks[self.current_block];
656        block.terminator = Some(Terminator::Jump(target));
657        self.func.blocks[target].predecessors.push(self.current_block);
658    }
659
660    /// Sets a branch terminator.
661    pub fn branch(&mut self, condition: ValueId, then_block: BlockId, else_block: BlockId) {
662        let block = &mut self.func.blocks[self.current_block];
663        block.terminator = Some(Terminator::Branch { condition, then_block, else_block });
664        self.func.blocks[then_block].predecessors.push(self.current_block);
665        self.func.blocks[else_block].predecessors.push(self.current_block);
666    }
667
668    /// Sets a switch terminator.
669    pub fn switch(&mut self, value: ValueId, default: BlockId, cases: Vec<(ValueId, BlockId)>) {
670        let current = self.current_block;
671        self.func.blocks[current].terminator =
672            Some(Terminator::Switch { value, default, cases: cases.clone() });
673        self.func.blocks[default].predecessors.push(current);
674        for (_, case_block) in cases {
675            self.func.blocks[case_block].predecessors.push(current);
676        }
677    }
678
679    /// Sets a return terminator.
680    pub fn ret(&mut self, values: impl IntoIterator<Item = ValueId>) {
681        let values: SmallVec<[ValueId; 2]> = values.into_iter().collect();
682        self.func.blocks[self.current_block].terminator = Some(Terminator::Return { values });
683    }
684
685    /// Sets a revert terminator.
686    pub fn revert(&mut self, offset: ValueId, size: ValueId) {
687        self.func.blocks[self.current_block].terminator = Some(Terminator::Revert { offset, size });
688    }
689
690    /// Sets a return-data terminator: `RETURN(offset, size)`.
691    pub fn ret_data(&mut self, offset: ValueId, size: ValueId) {
692        self.func.blocks[self.current_block].terminator =
693            Some(Terminator::ReturnData { offset, size });
694    }
695
696    /// Sets a stop terminator.
697    pub fn stop(&mut self) {
698        self.func.blocks[self.current_block].terminator = Some(Terminator::Stop);
699    }
700
701    /// Sets an invalid terminator.
702    pub fn invalid(&mut self) {
703        self.func.blocks[self.current_block].terminator = Some(Terminator::Invalid);
704    }
705
706    /// Sets a selfdestruct terminator.
707    pub fn selfdestruct(&mut self, recipient: ValueId) {
708        self.func.blocks[self.current_block].terminator =
709            Some(Terminator::SelfDestruct { recipient });
710    }
711
712    /// Returns a reference to the function.
713    #[must_use]
714    pub fn func(&self) -> &Function {
715        self.func
716    }
717
718    /// Returns a mutable reference to the function.
719    pub fn func_mut(&mut self) -> &mut Function {
720        self.func
721    }
722}