1#![allow(unused)]
2use std::thread::Scope;
3
4use super::{context::Context, inst_order::InstSerializer};
5use fe_common::numeric::to_hex_str;
6
7use fe_abi::function::{AbiFunction, AbiFunctionType};
8use fe_common::db::Upcast;
9use fe_mir::{
10 ir::{
11 self,
12 constant::ConstantValue,
13 inst::{BinOp, CallType, CastKind, InstKind, UnOp},
14 value::AssignableValue,
15 Constant, FunctionBody, FunctionId, FunctionSignature, InstId, Type, TypeId, TypeKind,
16 Value, ValueId,
17 },
18 pretty_print::PrettyPrint,
19};
20use fxhash::FxHashMap;
21use smol_str::SmolStr;
22use yultsur::{
23 yul::{self, Statement},
24 *,
25};
26
27use crate::{
28 db::CodegenDb,
29 yul::isel::inst_order::StructuralInst,
30 yul::slot_size::{function_hash_type, yul_primitive_type, SLOT_SIZE},
31 yul::{
32 runtime::{self, RuntimeProvider},
33 YulVariable,
34 },
35};
36
37pub fn lower_function(
38 db: &dyn CodegenDb,
39 ctx: &mut Context,
40 function: FunctionId,
41) -> yul::FunctionDefinition {
42 debug_assert!(!ctx.lowered_functions.contains(&function));
43 ctx.lowered_functions.insert(function);
44 let sig = &db.codegen_legalized_signature(function);
45 let body = &db.codegen_legalized_body(function);
46 FuncLowerHelper::new(db, ctx, function, sig, body).lower_func()
47}
48
49struct FuncLowerHelper<'db, 'a> {
50 db: &'db dyn CodegenDb,
51 ctx: &'a mut Context,
52 value_map: ScopedValueMap,
53 func: FunctionId,
54 sig: &'a FunctionSignature,
55 body: &'a FunctionBody,
56 ret_value: Option<yul::Identifier>,
57 sink: Vec<yul::Statement>,
58}
59
60impl<'db, 'a> FuncLowerHelper<'db, 'a> {
61 fn new(
62 db: &'db dyn CodegenDb,
63 ctx: &'a mut Context,
64 func: FunctionId,
65 sig: &'a FunctionSignature,
66 body: &'a FunctionBody,
67 ) -> Self {
68 let mut value_map = ScopedValueMap::default();
69 for &value in body.store.locals() {
71 match body.store.value_data(value) {
72 Value::Local(local) if local.is_arg => {
73 let ident = YulVariable::new(local.name.as_str()).ident();
74 value_map.insert(value, ident);
75 }
76 _ => {}
77 }
78 }
79
80 let ret_value = if sig.return_type.is_some() {
81 Some(YulVariable::new("$ret").ident())
82 } else {
83 None
84 };
85
86 Self {
87 db,
88 ctx,
89 value_map,
90 func,
91 sig,
92 body,
93 ret_value,
94 sink: Vec::new(),
95 }
96 }
97
98 fn lower_func(mut self) -> yul::FunctionDefinition {
99 let name = identifier! { (self.db.codegen_function_symbol_name(self.func)) };
100
101 let parameters = self
102 .sig
103 .params
104 .iter()
105 .map(|param| YulVariable::new(param.name.as_str()).ident())
106 .collect();
107
108 let ret = self
109 .ret_value
110 .clone()
111 .map(|value| vec![value])
112 .unwrap_or_default();
113
114 let body = self.lower_body();
115
116 yul::FunctionDefinition {
117 name,
118 parameters,
119 returns: ret,
120 block: body,
121 }
122 }
123
124 fn lower_body(mut self) -> yul::Block {
125 let inst_order = InstSerializer::new(self.body).serialize();
126
127 for inst in inst_order {
128 self.lower_structural_inst(inst)
129 }
130
131 yul::Block {
132 statements: self.sink,
133 }
134 }
135
136 fn lower_structural_inst(&mut self, inst: StructuralInst) {
137 match inst {
138 StructuralInst::Inst(inst) => self.lower_inst(inst),
139 StructuralInst::If { cond, then, else_ } => {
140 let if_block = self.lower_if(cond, then, else_);
141 self.sink.push(if_block)
142 }
143 StructuralInst::Switch {
144 scrutinee,
145 table,
146 default,
147 } => {
148 let switch_block = self.lower_switch(scrutinee, table, default);
149 self.sink.push(switch_block)
150 }
151 StructuralInst::For { body } => {
152 let for_block = self.lower_for(body);
153 self.sink.push(for_block)
154 }
155 StructuralInst::Break => self.sink.push(yul::Statement::Break),
156 StructuralInst::Continue => self.sink.push(yul::Statement::Continue),
157 };
158 }
159
160 fn lower_inst(&mut self, inst: InstId) {
161 if let Some(lhs) = self.body.store.inst_result(inst) {
162 self.declare_assignable_value(lhs)
163 }
164
165 match &self.body.store.inst_data(inst).kind {
166 InstKind::Declare { local } => self.declare_value(*local),
167
168 InstKind::Unary { op, value } => {
169 let inst_result = self.body.store.inst_result(inst).unwrap();
170 let inst_result_ty = inst_result.ty(self.db.upcast(), &self.body.store);
171 let result = self.lower_unary(*op, *value);
172 self.assign_inst_result(inst, result, inst_result_ty.deref(self.db.upcast()))
173 }
174
175 InstKind::Binary { op, lhs, rhs } => {
176 let inst_result = self.body.store.inst_result(inst).unwrap();
177 let inst_result_ty = inst_result.ty(self.db.upcast(), &self.body.store);
178 let result = self.lower_binary(*op, *lhs, *rhs, inst);
179 self.assign_inst_result(inst, result, inst_result_ty.deref(self.db.upcast()))
180 }
181
182 InstKind::Cast { kind, value, to } => {
183 let from_ty = self.body.store.value_ty(*value);
184 let result = match kind {
185 CastKind::Primitive => {
186 debug_assert!(
187 from_ty.is_primitive(self.db.upcast())
188 && to.is_primitive(self.db.upcast())
189 );
190 let value = self.value_expr(*value);
191 self.ctx.runtime.primitive_cast(self.db, value, from_ty)
192 }
193 CastKind::Untag => {
194 let from_ty = from_ty.deref(self.db.upcast());
195 debug_assert!(from_ty.is_enum(self.db.upcast()));
196 let value = self.value_expr(*value);
197 let offset = literal_expression! {(from_ty.enum_data_offset(self.db.upcast(), SLOT_SIZE))};
198 expression! {add([value], [offset])}
199 }
200 };
201
202 self.assign_inst_result(inst, result, *to)
203 }
204
205 InstKind::AggregateConstruct { ty, args } => {
206 let lhs = self.body.store.inst_result(inst).unwrap();
207 let ptr = self.lower_assignable_value(lhs);
208 let ptr_ty = lhs.ty(self.db.upcast(), &self.body.store);
209 let arg_values = args.iter().map(|arg| self.value_expr(*arg)).collect();
210 let arg_tys = args
211 .iter()
212 .map(|arg| self.body.store.value_ty(*arg))
213 .collect();
214 self.sink.push(yul::Statement::Expression(
215 self.ctx
216 .runtime
217 .aggregate_init(self.db, ptr, arg_values, ptr_ty, arg_tys),
218 ))
219 }
220
221 InstKind::Bind { src } => {
222 match self.body.store.value_data(*src) {
223 Value::Constant { constant, .. } => {
224 if let ConstantValue::Str(s) = &constant.data(self.db.upcast()).value {
227 self.ctx.string_constants.insert(s.to_string());
228 let size = self.value_ty_size_deref(*src);
229 let lhs = self.body.store.inst_result(inst).unwrap();
230 let ptr = self.lower_assignable_value(lhs);
231 let inst_result_ty = lhs.ty(self.db.upcast(), &self.body.store);
232 self.sink.push(yul::Statement::Expression(
233 self.ctx.runtime.string_copy(
234 self.db,
235 ptr,
236 s,
237 inst_result_ty.is_sptr(self.db.upcast()),
238 ),
239 ))
240 } else {
241 let src_ty = self.body.store.value_ty(*src);
242 let src = self.value_expr(*src);
243 self.assign_inst_result(inst, src, src_ty)
244 }
245 }
246 _ => {
247 let src_ty = self.body.store.value_ty(*src);
248 let src = self.value_expr(*src);
249 self.assign_inst_result(inst, src, src_ty)
250 }
251 }
252 }
253
254 InstKind::MemCopy { src } => {
255 let lhs = self.body.store.inst_result(inst).unwrap();
256 let dst_ptr = self.lower_assignable_value(lhs);
257 let dst_ptr_ty = lhs.ty(self.db.upcast(), &self.body.store);
258 let src_ptr = self.value_expr(*src);
259 let src_ptr_ty = self.body.store.value_ty(*src);
260 let ty_size = literal_expression! { (self.value_ty_size_deref(*src)) };
261 self.sink
262 .push(yul::Statement::Expression(self.ctx.runtime.ptr_copy(
263 self.db,
264 src_ptr,
265 dst_ptr,
266 ty_size,
267 src_ptr_ty.is_sptr(self.db.upcast()),
268 dst_ptr_ty.is_sptr(self.db.upcast()),
269 )))
270 }
271
272 InstKind::Load { src } => {
273 let src_ty = self.body.store.value_ty(*src);
274 let src = self.value_expr(*src);
275 debug_assert!(src_ty.is_ptr(self.db.upcast()));
276
277 let result = self.body.store.inst_result(inst).unwrap();
278 debug_assert!(!result
279 .ty(self.db.upcast(), &self.body.store)
280 .is_ptr(self.db.upcast()));
281 self.assign_inst_result(inst, src, src_ty)
282 }
283
284 InstKind::AggregateAccess { value, indices } => {
285 let base = self.value_expr(*value);
286 let mut ptr = base;
287 let mut inner_ty = self.body.store.value_ty(*value);
288 for &idx in indices {
289 ptr = self.aggregate_elem_ptr(ptr, idx, inner_ty.deref(self.db.upcast()));
290 inner_ty =
291 inner_ty.projection_ty(self.db.upcast(), self.body.store.value_data(idx));
292 }
293
294 let result = self.body.store.inst_result(inst).unwrap();
295 self.assign_inst_result(inst, ptr, inner_ty)
296 }
297
298 InstKind::MapAccess { value, key } => {
299 let map_ty = self.body.store.value_ty(*value).deref(self.db.upcast());
300 let value_expr = self.value_expr(*value);
301 let key_expr = self.value_expr(*key);
302 let key_ty = self.body.store.value_ty(*key);
303 let ptr = self
304 .ctx
305 .runtime
306 .map_value_ptr(self.db, value_expr, key_expr, key_ty);
307 let value_ty = match &map_ty.data(self.db.upcast()).kind {
308 TypeKind::Map(def) => def.value_ty,
309 _ => unreachable!(),
310 };
311
312 self.assign_inst_result(inst, ptr, value_ty.make_sptr(self.db.upcast()));
313 }
314
315 InstKind::Call {
316 func,
317 args,
318 call_type,
319 } => {
320 let args: Vec<_> = args.iter().map(|arg| self.value_expr(*arg)).collect();
321 let result = match call_type {
322 CallType::Internal => {
323 self.ctx.function_dependency.insert(*func);
324 let func_name = identifier! {(self.db.codegen_function_symbol_name(*func))};
325 expression! {[func_name]([args...])}
326 }
327 CallType::External => self.ctx.runtime.external_call(self.db, *func, args),
328 };
329 match self.db.codegen_legalized_signature(*func).return_type {
330 Some(mut result_ty) => {
331 if result_ty.is_aggregate(self.db.upcast())
332 | result_ty.is_string(self.db.upcast())
333 {
334 result_ty = result_ty.make_mptr(self.db.upcast());
335 }
336 self.assign_inst_result(inst, result, result_ty)
337 }
338 _ => self.sink.push(Statement::Expression(result)),
339 }
340 }
341
342 InstKind::Revert { arg } => match arg {
343 Some(arg) => {
344 let arg_ty = self.body.store.value_ty(*arg);
345 let deref_ty = arg_ty.deref(self.db.upcast());
346 let ty_data = deref_ty.data(self.db.upcast());
347 let arg_expr = if deref_ty.is_zero_sized(self.db.upcast()) {
348 None
349 } else {
350 Some(self.value_expr(*arg))
351 };
352 let name = match &ty_data.kind {
353 ir::TypeKind::Struct(def) => &def.name,
354 ir::TypeKind::String(def) => "Error",
355 _ => "Panic",
356 };
357 self.sink.push(yul::Statement::Expression(
358 self.ctx.runtime.revert(self.db, arg_expr, name, arg_ty),
359 ));
360 }
361 None => self.sink.push(statement! {revert(0, 0)}),
362 },
363
364 InstKind::Emit { arg } => {
365 let event = self.value_expr(*arg);
366 let event_ty = self.body.store.value_ty(*arg);
367 let result = self.ctx.runtime.emit(self.db, event, event_ty);
368 let u256_ty = yul_primitive_type(self.db);
369 self.assign_inst_result(inst, result, u256_ty);
370 }
371
372 InstKind::Return { arg } => {
373 if let Some(arg) = arg {
374 let arg = self.value_expr(*arg);
375 let ret_value = self.ret_value.clone().unwrap();
376 self.sink.push(statement! {[ret_value] := [arg]});
377 }
378 self.sink.push(yul::Statement::Leave)
379 }
380
381 InstKind::Keccak256 { arg } => {
382 let result = self.keccak256(*arg);
383 let u256_ty = yul_primitive_type(self.db);
384 self.assign_inst_result(inst, result, u256_ty);
385 }
386
387 InstKind::AbiEncode { arg } => {
388 let lhs = self.body.store.inst_result(inst).unwrap();
389 let ptr = self.lower_assignable_value(lhs);
390 let ptr_ty = lhs.ty(self.db.upcast(), &self.body.store);
391 let src_expr = self.value_expr(*arg);
392 let src_ty = self.body.store.value_ty(*arg);
393
394 let abi_encode = self.ctx.runtime.abi_encode(
395 self.db,
396 src_expr,
397 ptr,
398 src_ty,
399 ptr_ty.is_sptr(self.db.upcast()),
400 );
401 self.sink.push(statement! {
402 pop([abi_encode])
403 });
404 }
405
406 InstKind::Create { value, contract } => {
407 self.ctx.contract_dependency.insert(*contract);
408
409 let value_expr = self.value_expr(*value);
410 let result = self.ctx.runtime.create(self.db, *contract, value_expr);
411 let u256_ty = yul_primitive_type(self.db);
412 self.assign_inst_result(inst, result, u256_ty)
413 }
414
415 InstKind::Create2 {
416 value,
417 salt,
418 contract,
419 } => {
420 self.ctx.contract_dependency.insert(*contract);
421
422 let value_expr = self.value_expr(*value);
423 let salt_expr = self.value_expr(*salt);
424 let result = self
425 .ctx
426 .runtime
427 .create2(self.db, *contract, value_expr, salt_expr);
428 let u256_ty = yul_primitive_type(self.db);
429 self.assign_inst_result(inst, result, u256_ty)
430 }
431
432 InstKind::YulIntrinsic { op, args } => {
433 let args: Vec<_> = args.iter().map(|arg| self.value_expr(*arg)).collect();
434 let op_name = identifier! { (format!("{op}").strip_prefix("__").unwrap()) };
435 let result = expression! { [op_name]([args...]) };
436 let u256_ty = yul_primitive_type(self.db);
439 self.assign_inst_result(inst, result, u256_ty)
440 }
441
442 InstKind::Nop => {}
443
444 InstKind::Jump { .. } | InstKind::Branch { .. } | InstKind::Switch { .. } => {
446 unreachable!()
447 }
448 }
449 }
450
451 fn lower_if(
452 &mut self,
453 cond: ValueId,
454 then: Vec<StructuralInst>,
455 else_: Vec<StructuralInst>,
456 ) -> yul::Statement {
457 let cond = self.value_expr(cond);
458
459 self.enter_scope();
460 let then_body = self.lower_branch_body(then);
461 self.leave_scope();
462
463 self.enter_scope();
464 let else_body = self.lower_branch_body(else_);
465 self.leave_scope();
466
467 switch! {
468 switch ([cond])
469 (case 1 {[then_body...]})
470 (case 0 {[else_body...]})
471 }
472 }
473
474 fn lower_switch(
475 &mut self,
476 scrutinee: ValueId,
477 table: Vec<(ValueId, Vec<StructuralInst>)>,
478 default: Option<Vec<StructuralInst>>,
479 ) -> yul::Statement {
480 let scrutinee = self.value_expr(scrutinee);
481
482 let mut cases = vec![];
483 for (value, insts) in table {
484 let value = self.value_expr(value);
485 let value = match value {
486 yul::Expression::Literal(lit) => lit,
487 _ => panic!("switch table values must be literal"),
488 };
489
490 self.enter_scope();
491 let body = self.lower_branch_body(insts);
492 self.leave_scope();
493 cases.push(yul::Case {
494 literal: Some(value),
495 block: block! { [body...] },
496 })
497 }
498
499 if let Some(insts) = default {
500 let block = self.lower_branch_body(insts);
501 cases.push(case! {
502 default {[block...]}
503 });
504 }
505
506 switch! {
507 switch ([scrutinee])
508 [cases...]
509 }
510 }
511
512 fn lower_branch_body(&mut self, insts: Vec<StructuralInst>) -> Vec<yul::Statement> {
513 let mut body = vec![];
514 std::mem::swap(&mut self.sink, &mut body);
515 for inst in insts {
516 self.lower_structural_inst(inst);
517 }
518 std::mem::swap(&mut self.sink, &mut body);
519 body
520 }
521
522 fn lower_for(&mut self, body: Vec<StructuralInst>) -> yul::Statement {
523 let mut body_stmts = vec![];
524 std::mem::swap(&mut self.sink, &mut body_stmts);
525 for inst in body {
526 self.lower_structural_inst(inst);
527 }
528 std::mem::swap(&mut self.sink, &mut body_stmts);
529
530 block_statement! {(
531 for {} (1) {}
532 {
533 [body_stmts...]
534 }
535 )}
536 }
537
538 fn lower_assign(&mut self, lhs: &AssignableValue, rhs: ValueId) -> yul::Statement {
539 match lhs {
540 AssignableValue::Value(value) => {
541 let lhs = self.value_ident(*value);
542 let rhs = self.value_expr(rhs);
543 statement! { [lhs] := [rhs] }
544 }
545 AssignableValue::Aggregate { .. } | AssignableValue::Map { .. } => {
546 let dst_ty = lhs.ty(self.db.upcast(), &self.body.store);
547 let src_ty = self.body.store.value_ty(rhs);
548 debug_assert_eq!(
549 dst_ty.deref(self.db.upcast()),
550 src_ty.deref(self.db.upcast())
551 );
552
553 let dst = self.lower_assignable_value(lhs);
554 let src = self.value_expr(rhs);
555
556 if src_ty.is_ptr(self.db.upcast()) {
557 let ty_size = literal_expression! { (self.value_ty_size_deref(rhs)) };
558
559 let expr = self.ctx.runtime.ptr_copy(
560 self.db,
561 src,
562 dst,
563 ty_size,
564 src_ty.is_sptr(self.db.upcast()),
565 dst_ty.is_sptr(self.db.upcast()),
566 );
567 yul::Statement::Expression(expr)
568 } else {
569 let expr = self.ctx.runtime.ptr_store(self.db, dst, src, dst_ty);
570 yul::Statement::Expression(expr)
571 }
572 }
573 }
574 }
575
576 fn lower_unary(&mut self, op: UnOp, value: ValueId) -> yul::Expression {
577 let value_expr = self.value_expr(value);
578 match op {
579 UnOp::Not => expression! { iszero([value_expr])},
580 UnOp::Neg => {
581 let zero = literal_expression! {0};
582 if self.body.store.value_data(value).is_imm() {
583 expression! {sub([zero], [value_expr])}
586 } else {
587 let value_ty = self.body.store.value_ty(value);
588 self.ctx
589 .runtime
590 .safe_sub(self.db, zero, value_expr, value_ty)
591 }
592 }
593 UnOp::Inv => expression! { not([value_expr])},
594 }
595 }
596
597 fn lower_binary(
598 &mut self,
599 op: BinOp,
600 lhs: ValueId,
601 rhs: ValueId,
602 inst: InstId,
603 ) -> yul::Expression {
604 let lhs_expr = self.value_expr(lhs);
605 let rhs_expr = self.value_expr(rhs);
606 let is_result_signed = self
607 .body
608 .store
609 .inst_result(inst)
610 .map(|val| {
611 let ty = val.ty(self.db.upcast(), &self.body.store);
612 ty.is_signed(self.db.upcast())
613 })
614 .unwrap_or(false);
615 let is_lhs_signed = self.body.store.value_ty(lhs).is_signed(self.db.upcast());
616
617 let inst_result = self.body.store.inst_result(inst).unwrap();
618 let inst_result_ty = inst_result
619 .ty(self.db.upcast(), &self.body.store)
620 .deref(self.db.upcast());
621 match op {
622 BinOp::Add => self
623 .ctx
624 .runtime
625 .safe_add(self.db, lhs_expr, rhs_expr, inst_result_ty),
626 BinOp::Sub => self
627 .ctx
628 .runtime
629 .safe_sub(self.db, lhs_expr, rhs_expr, inst_result_ty),
630 BinOp::Mul => self
631 .ctx
632 .runtime
633 .safe_mul(self.db, lhs_expr, rhs_expr, inst_result_ty),
634 BinOp::Div => self
635 .ctx
636 .runtime
637 .safe_div(self.db, lhs_expr, rhs_expr, inst_result_ty),
638 BinOp::Mod => self
639 .ctx
640 .runtime
641 .safe_mod(self.db, lhs_expr, rhs_expr, inst_result_ty),
642 BinOp::Pow => self
643 .ctx
644 .runtime
645 .safe_pow(self.db, lhs_expr, rhs_expr, inst_result_ty),
646 BinOp::Shl => expression! {shl([rhs_expr], [lhs_expr])},
647 BinOp::Shr if is_result_signed => expression! {sar([rhs_expr], [lhs_expr])},
648 BinOp::Shr => expression! {shr([rhs_expr], [lhs_expr])},
649 BinOp::BitOr | BinOp::LogicalOr => expression! {or([lhs_expr], [rhs_expr])},
650 BinOp::BitXor => expression! {xor([lhs_expr], [rhs_expr])},
651 BinOp::BitAnd | BinOp::LogicalAnd => expression! {and([lhs_expr], [rhs_expr])},
652 BinOp::Eq => expression! {eq([lhs_expr], [rhs_expr])},
653 BinOp::Ne => expression! {iszero((eq([lhs_expr], [rhs_expr])))},
654 BinOp::Ge if is_lhs_signed => expression! {iszero((slt([lhs_expr], [rhs_expr])))},
655 BinOp::Ge => expression! {iszero((lt([lhs_expr], [rhs_expr])))},
656 BinOp::Gt if is_lhs_signed => expression! {sgt([lhs_expr], [rhs_expr])},
657 BinOp::Gt => expression! {gt([lhs_expr], [rhs_expr])},
658 BinOp::Le if is_lhs_signed => expression! {iszero((sgt([lhs_expr], [rhs_expr])))},
659 BinOp::Le => expression! {iszero((gt([lhs_expr], [rhs_expr])))},
660 BinOp::Lt if is_lhs_signed => expression! {slt([lhs_expr], [rhs_expr])},
661 BinOp::Lt => expression! {lt([lhs_expr], [rhs_expr])},
662 }
663 }
664
665 fn lower_cast(&mut self, value: ValueId, to: TypeId) -> yul::Expression {
666 let from_ty = self.body.store.value_ty(value);
667 debug_assert!(from_ty.is_primitive(self.db.upcast()));
668 debug_assert!(to.is_primitive(self.db.upcast()));
669
670 let value = self.value_expr(value);
671 self.ctx.runtime.primitive_cast(self.db, value, from_ty)
672 }
673
674 fn assign_inst_result(&mut self, inst: InstId, rhs: yul::Expression, rhs_ty: TypeId) {
675 let stmt = if let Some(result) = self.body.store.inst_result(inst) {
678 let lhs = self.lower_assignable_value(result);
679 let lhs_ty = result.ty(self.db.upcast(), &self.body.store);
680 match result {
681 AssignableValue::Value(value) => {
682 match (
683 lhs_ty.is_ptr(self.db.upcast()),
684 rhs_ty.is_ptr(self.db.upcast()),
685 ) {
686 (true, true) => {
687 if lhs_ty.is_mptr(self.db.upcast()) == rhs_ty.is_mptr(self.db.upcast())
688 {
689 let rhs = self.extend_value(rhs, lhs_ty);
690 let lhs_ident = self.value_ident(*value);
691 statement! { [lhs_ident] := [rhs] }
692 } else {
693 let ty_size = rhs_ty
694 .deref(self.db.upcast())
695 .size_of(self.db.upcast(), SLOT_SIZE);
696 yul::Statement::Expression(self.ctx.runtime.ptr_copy(
697 self.db,
698 rhs,
699 lhs,
700 literal_expression! { (ty_size) },
701 rhs_ty.is_sptr(self.db.upcast()),
702 lhs_ty.is_sptr(self.db.upcast()),
703 ))
704 }
705 }
706 (true, false) => yul::Statement::Expression(
707 self.ctx.runtime.ptr_store(self.db, lhs, rhs, lhs_ty),
708 ),
709
710 (false, true) => {
711 let rhs = self.ctx.runtime.ptr_load(self.db, rhs, rhs_ty);
712 let rhs = self.extend_value(rhs, lhs_ty);
713 let lhs_ident = self.value_ident(*value);
714 statement! { [lhs_ident] := [rhs] }
715 }
716 (false, false) => {
717 let rhs = self.extend_value(rhs, lhs_ty);
718 let lhs_ident = self.value_ident(*value);
719 statement! { [lhs_ident] := [rhs] }
720 }
721 }
722 }
723 AssignableValue::Aggregate { .. } | AssignableValue::Map { .. } => {
724 let expr = if rhs_ty.is_ptr(self.db.upcast()) {
725 let ty_size = rhs_ty
726 .deref(self.db.upcast())
727 .size_of(self.db.upcast(), SLOT_SIZE);
728 self.ctx.runtime.ptr_copy(
729 self.db,
730 rhs,
731 lhs,
732 literal_expression! { (ty_size) },
733 rhs_ty.is_sptr(self.db.upcast()),
734 lhs_ty.is_sptr(self.db.upcast()),
735 )
736 } else {
737 self.ctx.runtime.ptr_store(self.db, lhs, rhs, lhs_ty)
738 };
739 yul::Statement::Expression(expr)
740 }
741 }
742 } else {
743 yul::Statement::Expression(rhs)
744 };
745
746 self.sink.push(stmt);
747 }
748
749 fn extend_value(&mut self, value: yul::Expression, ty: TypeId) -> yul::Expression {
751 if ty.is_primitive(self.db.upcast()) {
752 self.ctx.runtime.primitive_cast(self.db, value, ty)
753 } else {
754 value
755 }
756 }
757
758 fn declare_assignable_value(&mut self, value: &AssignableValue) {
759 match value {
760 AssignableValue::Value(value) if !self.value_map.contains(*value) => {
761 self.declare_value(*value);
762 }
763 _ => {}
764 }
765 }
766
767 fn declare_value(&mut self, value: ValueId) {
768 let var = YulVariable::new(format!("$tmp_{}", value.index()));
769 self.value_map.insert(value, var.ident());
770 let value_ty = self.body.store.value_ty(value);
771
772 let init = if value_ty.is_mptr(self.db.upcast()) {
774 let deref_ty = value_ty.deref(self.db.upcast());
775 let ty_size = deref_ty.size_of(self.db.upcast(), SLOT_SIZE);
776 let size = literal_expression! { (ty_size) };
777 Some(self.ctx.runtime.alloc(self.db, size))
778 } else {
779 None
780 };
781
782 self.sink.push(yul::Statement::VariableDeclaration(
783 yul::VariableDeclaration {
784 identifiers: vec![var.ident()],
785 expression: init,
786 },
787 ))
788 }
789
790 fn value_expr(&mut self, value: ValueId) -> yul::Expression {
791 match self.body.store.value_data(value) {
792 Value::Local(_) | Value::Temporary { .. } => {
793 let ident = self.value_map.lookup(value).unwrap();
794 literal_expression! {(ident)}
795 }
796 Value::Immediate { imm, .. } => {
797 literal_expression! {(imm)}
798 }
799 Value::Constant { constant, .. } => match &constant.data(self.db.upcast()).value {
800 ConstantValue::Immediate(imm) => {
801 literal_expression! {(to_hex_str(imm))}
805 }
806 ConstantValue::Str(s) => {
807 self.ctx.string_constants.insert(s.to_string());
808 self.ctx.runtime.string_construct(self.db, s, s.len())
809 }
810 ConstantValue::Bool(true) => {
811 literal_expression! {1}
812 }
813 ConstantValue::Bool(false) => {
814 literal_expression! {0}
815 }
816 },
817 Value::Unit { .. } => unreachable!(),
818 }
819 }
820
821 fn value_ident(&self, value: ValueId) -> yul::Identifier {
822 self.value_map.lookup(value).unwrap().clone()
823 }
824
825 fn make_tmp(&mut self, tmp: ValueId) -> yul::Identifier {
826 let ident = YulVariable::new(format! {"$tmp_{}", tmp.index()}).ident();
827 self.value_map.insert(tmp, ident.clone());
828 ident
829 }
830
831 fn keccak256(&mut self, value: ValueId) -> yul::Expression {
832 let value_ty = self.body.store.value_ty(value);
833 debug_assert!(value_ty.is_mptr(self.db.upcast()));
834
835 let value_size = value_ty
836 .deref(self.db.upcast())
837 .size_of(self.db.upcast(), SLOT_SIZE);
838 let value_size_expr = literal_expression! {(value_size)};
839 let value_expr = self.value_expr(value);
840 expression! {keccak256([value_expr], [value_size_expr])}
841 }
842
843 fn lower_assignable_value(&mut self, value: &AssignableValue) -> yul::Expression {
844 match value {
845 AssignableValue::Value(value) => self.value_expr(*value),
846
847 AssignableValue::Aggregate { lhs, idx } => {
848 let base_ptr = self.lower_assignable_value(lhs);
849 let ty = lhs
850 .ty(self.db.upcast(), &self.body.store)
851 .deref(self.db.upcast());
852 self.aggregate_elem_ptr(base_ptr, *idx, ty)
853 }
854 AssignableValue::Map { lhs, key } => {
855 let map_ptr = self.lower_assignable_value(lhs);
856 let key_ty = self.body.store.value_ty(*key);
857 let key = self.value_expr(*key);
858 self.ctx
859 .runtime
860 .map_value_ptr(self.db, map_ptr, key, key_ty)
861 }
862 }
863 }
864
865 fn aggregate_elem_ptr(
866 &mut self,
867 base_ptr: yul::Expression,
868 idx: ValueId,
869 base_ty: TypeId,
870 ) -> yul::Expression {
871 debug_assert!(base_ty.is_aggregate(self.db.upcast()));
872
873 match &base_ty.data(self.db.upcast()).kind {
874 TypeKind::Array(def) => {
875 let elem_size =
876 literal_expression! {(base_ty.array_elem_size(self.db.upcast(), SLOT_SIZE))};
877 self.validate_array_indexing(def.len, idx);
878 let idx = self.value_expr(idx);
879 let offset = expression! {mul([elem_size], [idx])};
880 expression! { add([base_ptr], [offset]) }
881 }
882 _ => {
883 let elem_idx = match self.body.store.value_data(idx) {
884 Value::Immediate { imm, .. } => imm,
885 _ => panic!("only array type can use dynamic value indexing"),
886 };
887 let offset = literal_expression! {(base_ty.aggregate_elem_offset(self.db.upcast(), elem_idx.clone(), SLOT_SIZE))};
888 expression! {add([base_ptr], [offset])}
889 }
890 }
891 }
892
893 fn validate_array_indexing(&mut self, array_len: usize, idx: ValueId) {
894 const PANIC_OUT_OF_BOUNDS: usize = 0x32;
895
896 if let Value::Immediate { .. } = self.body.store.value_data(idx) {
897 return;
898 }
899
900 let idx = self.value_expr(idx);
901 let max_idx = literal_expression! {(array_len - 1)};
902 self.sink.push(statement!(if (gt([idx], [max_idx])) {
903 ([runtime::panic_revert_numeric(
904 self.ctx.runtime.as_mut(),
905 self.db,
906 literal_expression! {(PANIC_OUT_OF_BOUNDS)},
907 )])
908 }));
909 }
910
911 fn value_ty_size(&self, value: ValueId) -> usize {
912 self.body
913 .store
914 .value_ty(value)
915 .size_of(self.db.upcast(), SLOT_SIZE)
916 }
917
918 fn value_ty_size_deref(&self, value: ValueId) -> usize {
919 self.body
920 .store
921 .value_ty(value)
922 .deref(self.db.upcast())
923 .size_of(self.db.upcast(), SLOT_SIZE)
924 }
925
926 fn enter_scope(&mut self) {
927 let value_map = std::mem::take(&mut self.value_map);
928 self.value_map = ScopedValueMap::with_parent(value_map);
929 }
930
931 fn leave_scope(&mut self) {
932 let value_map = std::mem::take(&mut self.value_map);
933 self.value_map = value_map.into_parent();
934 }
935}
936
937#[derive(Debug, Default)]
938struct ScopedValueMap {
939 parent: Option<Box<ScopedValueMap>>,
940 map: FxHashMap<ValueId, yul::Identifier>,
941}
942
943impl ScopedValueMap {
944 fn lookup(&self, value: ValueId) -> Option<&yul::Identifier> {
945 match self.map.get(&value) {
946 Some(ident) => Some(ident),
947 None => self.parent.as_ref().and_then(|p| p.lookup(value)),
948 }
949 }
950
951 fn with_parent(parent: ScopedValueMap) -> Self {
952 Self {
953 parent: Some(parent.into()),
954 ..Self::default()
955 }
956 }
957
958 fn into_parent(self) -> Self {
959 *self.parent.unwrap()
960 }
961
962 fn insert(&mut self, value: ValueId, ident: yul::Identifier) {
963 self.map.insert(value, ident);
964 }
965
966 fn contains(&self, value: ValueId) -> bool {
967 self.lookup(value).is_some()
968 }
969}
970
971fn bit_mask(byte_size: usize) -> usize {
972 (1 << (byte_size * 8)) - 1
973}
974
975fn bit_mask_expr(byte_size: usize) -> yul::Expression {
976 let mask = format!("{:#x}", bit_mask(byte_size));
977 literal_expression! {(mask)}
978}