fe_mir/ir/
body_builder.rs

1use fe_analyzer::namespace::items::ContractId;
2use num_bigint::BigInt;
3
4use crate::ir::{
5    body_cursor::{BodyCursor, CursorLocation},
6    inst::{BinOp, Inst, InstKind, UnOp},
7    value::{AssignableValue, Local},
8    BasicBlock, BasicBlockId, FunctionBody, FunctionId, InstId, SourceInfo, TypeId,
9};
10
11use super::{
12    inst::{CallType, CastKind, SwitchTable, YulIntrinsicOp},
13    ConstantId, Value, ValueId,
14};
15
16#[derive(Debug)]
17pub struct BodyBuilder {
18    pub body: FunctionBody,
19    loc: CursorLocation,
20}
21
22macro_rules! impl_unary_inst {
23    ($name:ident, $code:path) => {
24        pub fn $name(&mut self, value: ValueId, source: SourceInfo) -> InstId {
25            let inst = Inst::unary($code, value, source);
26            self.insert_inst(inst)
27        }
28    };
29}
30
31macro_rules! impl_binary_inst {
32    ($name:ident, $code:path) => {
33        pub fn $name(&mut self, lhs: ValueId, rhs: ValueId, source: SourceInfo) -> InstId {
34            let inst = Inst::binary($code, lhs, rhs, source);
35            self.insert_inst(inst)
36        }
37    };
38}
39
40impl BodyBuilder {
41    pub fn new(fid: FunctionId, source: SourceInfo) -> Self {
42        let body = FunctionBody::new(fid, source);
43        let entry_block = body.order.entry();
44        Self {
45            body,
46            loc: CursorLocation::BlockTop(entry_block),
47        }
48    }
49
50    pub fn build(self) -> FunctionBody {
51        self.body
52    }
53
54    pub fn func_id(&self) -> FunctionId {
55        self.body.fid
56    }
57
58    pub fn make_block(&mut self) -> BasicBlockId {
59        let block = BasicBlock {};
60        let block_id = self.body.store.store_block(block);
61        self.body.order.append_block(block_id);
62        block_id
63    }
64
65    pub fn make_value(&mut self, value: impl Into<Value>) -> ValueId {
66        self.body.store.store_value(value.into())
67    }
68
69    pub fn map_result(&mut self, inst: InstId, result: AssignableValue) {
70        self.body.store.map_result(inst, result)
71    }
72
73    pub fn inst_result(&mut self, inst: InstId) -> Option<&AssignableValue> {
74        self.body.store.inst_result(inst)
75    }
76
77    pub fn move_to_block(&mut self, block: BasicBlockId) {
78        self.loc = CursorLocation::BlockBottom(block)
79    }
80
81    pub fn move_to_block_top(&mut self, block: BasicBlockId) {
82        self.loc = CursorLocation::BlockTop(block)
83    }
84
85    pub fn make_unit(&mut self, unit_ty: TypeId) -> ValueId {
86        self.body.store.store_value(Value::Unit { ty: unit_ty })
87    }
88
89    pub fn make_imm(&mut self, imm: BigInt, ty: TypeId) -> ValueId {
90        self.body.store.store_value(Value::Immediate { imm, ty })
91    }
92
93    pub fn make_imm_from_bool(&mut self, imm: bool, ty: TypeId) -> ValueId {
94        if imm {
95            self.make_imm(1u8.into(), ty)
96        } else {
97            self.make_imm(0u8.into(), ty)
98        }
99    }
100
101    pub fn make_constant(&mut self, constant: ConstantId, ty: TypeId) -> ValueId {
102        self.body
103            .store
104            .store_value(Value::Constant { constant, ty })
105    }
106
107    pub fn declare(&mut self, local: Local) -> ValueId {
108        let source = local.source.clone();
109        let local_id = self.body.store.store_value(Value::Local(local));
110
111        let kind = InstKind::Declare { local: local_id };
112        let inst = Inst::new(kind, source);
113        self.insert_inst(inst);
114        local_id
115    }
116
117    pub fn store_func_arg(&mut self, local: Local) -> ValueId {
118        self.body.store.store_value(Value::Local(local))
119    }
120
121    impl_unary_inst!(not, UnOp::Not);
122    impl_unary_inst!(neg, UnOp::Neg);
123    impl_unary_inst!(inv, UnOp::Inv);
124
125    impl_binary_inst!(add, BinOp::Add);
126    impl_binary_inst!(sub, BinOp::Sub);
127    impl_binary_inst!(mul, BinOp::Mul);
128    impl_binary_inst!(div, BinOp::Div);
129    impl_binary_inst!(modulo, BinOp::Mod);
130    impl_binary_inst!(pow, BinOp::Pow);
131    impl_binary_inst!(shl, BinOp::Shl);
132    impl_binary_inst!(shr, BinOp::Shr);
133    impl_binary_inst!(bit_or, BinOp::BitOr);
134    impl_binary_inst!(bit_xor, BinOp::BitXor);
135    impl_binary_inst!(bit_and, BinOp::BitAnd);
136    impl_binary_inst!(logical_and, BinOp::LogicalAnd);
137    impl_binary_inst!(logical_or, BinOp::LogicalOr);
138    impl_binary_inst!(eq, BinOp::Eq);
139    impl_binary_inst!(ne, BinOp::Ne);
140    impl_binary_inst!(ge, BinOp::Ge);
141    impl_binary_inst!(gt, BinOp::Gt);
142    impl_binary_inst!(le, BinOp::Le);
143    impl_binary_inst!(lt, BinOp::Lt);
144
145    pub fn primitive_cast(
146        &mut self,
147        value: ValueId,
148        result_ty: TypeId,
149        source: SourceInfo,
150    ) -> InstId {
151        let kind = InstKind::Cast {
152            kind: CastKind::Primitive,
153            value,
154            to: result_ty,
155        };
156        let inst = Inst::new(kind, source);
157        self.insert_inst(inst)
158    }
159
160    pub fn untag_cast(&mut self, value: ValueId, result_ty: TypeId, source: SourceInfo) -> InstId {
161        let kind = InstKind::Cast {
162            kind: CastKind::Untag,
163            value,
164            to: result_ty,
165        };
166        let inst = Inst::new(kind, source);
167        self.insert_inst(inst)
168    }
169
170    pub fn aggregate_construct(
171        &mut self,
172        ty: TypeId,
173        args: Vec<ValueId>,
174        source: SourceInfo,
175    ) -> InstId {
176        let kind = InstKind::AggregateConstruct { ty, args };
177        let inst = Inst::new(kind, source);
178        self.insert_inst(inst)
179    }
180
181    pub fn bind(&mut self, src: ValueId, source: SourceInfo) -> InstId {
182        let kind = InstKind::Bind { src };
183        let inst = Inst::new(kind, source);
184        self.insert_inst(inst)
185    }
186
187    pub fn mem_copy(&mut self, src: ValueId, source: SourceInfo) -> InstId {
188        let kind = InstKind::MemCopy { src };
189        let inst = Inst::new(kind, source);
190        self.insert_inst(inst)
191    }
192
193    pub fn load(&mut self, src: ValueId, source: SourceInfo) -> InstId {
194        let kind = InstKind::Load { src };
195        let inst = Inst::new(kind, source);
196        self.insert_inst(inst)
197    }
198
199    pub fn aggregate_access(
200        &mut self,
201        value: ValueId,
202        indices: Vec<ValueId>,
203        source: SourceInfo,
204    ) -> InstId {
205        let kind = InstKind::AggregateAccess { value, indices };
206        let inst = Inst::new(kind, source);
207        self.insert_inst(inst)
208    }
209
210    pub fn map_access(&mut self, value: ValueId, key: ValueId, source: SourceInfo) -> InstId {
211        let kind = InstKind::MapAccess { value, key };
212        let inst = Inst::new(kind, source);
213        self.insert_inst(inst)
214    }
215
216    pub fn call(
217        &mut self,
218        func: FunctionId,
219        args: Vec<ValueId>,
220        call_type: CallType,
221        source: SourceInfo,
222    ) -> InstId {
223        let kind = InstKind::Call {
224            func,
225            args,
226            call_type,
227        };
228        let inst = Inst::new(kind, source);
229        self.insert_inst(inst)
230    }
231
232    pub fn keccak256(&mut self, arg: ValueId, source: SourceInfo) -> InstId {
233        let kind = InstKind::Keccak256 { arg };
234        let inst = Inst::new(kind, source);
235        self.insert_inst(inst)
236    }
237
238    pub fn abi_encode(&mut self, arg: ValueId, source: SourceInfo) -> InstId {
239        let kind = InstKind::AbiEncode { arg };
240        let inst = Inst::new(kind, source);
241        self.insert_inst(inst)
242    }
243
244    pub fn create(&mut self, value: ValueId, contract: ContractId, source: SourceInfo) -> InstId {
245        let kind = InstKind::Create { value, contract };
246        let inst = Inst::new(kind, source);
247        self.insert_inst(inst)
248    }
249
250    pub fn create2(
251        &mut self,
252        value: ValueId,
253        salt: ValueId,
254        contract: ContractId,
255        source: SourceInfo,
256    ) -> InstId {
257        let kind = InstKind::Create2 {
258            value,
259            salt,
260            contract,
261        };
262        let inst = Inst::new(kind, source);
263        self.insert_inst(inst)
264    }
265
266    pub fn yul_intrinsic(
267        &mut self,
268        op: YulIntrinsicOp,
269        args: Vec<ValueId>,
270        source: SourceInfo,
271    ) -> InstId {
272        let inst = Inst::intrinsic(op, args, source);
273        self.insert_inst(inst)
274    }
275
276    pub fn jump(&mut self, dest: BasicBlockId, source: SourceInfo) -> InstId {
277        let kind = InstKind::Jump { dest };
278        let inst = Inst::new(kind, source);
279        self.insert_inst(inst)
280    }
281
282    pub fn branch(
283        &mut self,
284        cond: ValueId,
285        then: BasicBlockId,
286        else_: BasicBlockId,
287        source: SourceInfo,
288    ) -> InstId {
289        let kind = InstKind::Branch { cond, then, else_ };
290        let inst = Inst::new(kind, source);
291        self.insert_inst(inst)
292    }
293
294    pub fn switch(
295        &mut self,
296        disc: ValueId,
297        table: SwitchTable,
298        default: Option<BasicBlockId>,
299        source: SourceInfo,
300    ) -> InstId {
301        let kind = InstKind::Switch {
302            disc,
303            table,
304            default,
305        };
306        let inst = Inst::new(kind, source);
307        self.insert_inst(inst)
308    }
309
310    pub fn revert(&mut self, arg: Option<ValueId>, source: SourceInfo) -> InstId {
311        let kind = InstKind::Revert { arg };
312        let inst = Inst::new(kind, source);
313        self.insert_inst(inst)
314    }
315
316    pub fn emit(&mut self, arg: ValueId, source: SourceInfo) -> InstId {
317        let kind = InstKind::Emit { arg };
318        let inst = Inst::new(kind, source);
319        self.insert_inst(inst)
320    }
321
322    pub fn ret(&mut self, arg: ValueId, source: SourceInfo) -> InstId {
323        let kind = InstKind::Return { arg: arg.into() };
324        let inst = Inst::new(kind, source);
325        self.insert_inst(inst)
326    }
327
328    pub fn nop(&mut self, source: SourceInfo) -> InstId {
329        let kind = InstKind::Nop;
330        let inst = Inst::new(kind, source);
331        self.insert_inst(inst)
332    }
333
334    pub fn value_ty(&mut self, value: ValueId) -> TypeId {
335        self.body.store.value_ty(value)
336    }
337
338    pub fn value_data(&mut self, value: ValueId) -> &Value {
339        self.body.store.value_data(value)
340    }
341
342    /// Returns `true` if current block is terminated.
343    pub fn is_block_terminated(&mut self, block: BasicBlockId) -> bool {
344        self.body.order.is_terminated(&self.body.store, block)
345    }
346
347    pub fn is_current_block_terminated(&mut self) -> bool {
348        let current_block = self.current_block();
349        self.is_block_terminated(current_block)
350    }
351
352    pub fn current_block(&mut self) -> BasicBlockId {
353        self.cursor().expect_block()
354    }
355
356    pub fn remove_inst(&mut self, inst: InstId) {
357        let mut cursor = BodyCursor::new(&mut self.body, CursorLocation::Inst(inst));
358        if self.loc == cursor.loc() {
359            self.loc = cursor.prev_loc();
360        }
361        cursor.remove_inst();
362    }
363
364    pub fn inst_data(&self, inst: InstId) -> &Inst {
365        self.body.store.inst_data(inst)
366    }
367
368    fn insert_inst(&mut self, inst: Inst) -> InstId {
369        let mut cursor = self.cursor();
370        let inst_id = cursor.store_and_insert_inst(inst);
371
372        // Set cursor to the new inst.
373        self.loc = CursorLocation::Inst(inst_id);
374
375        inst_id
376    }
377
378    fn cursor(&mut self) -> BodyCursor {
379        BodyCursor::new(&mut self.body, self.loc)
380    }
381}