From 207393dd2639b9f401fd53f6ae976706243d3748 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Wed, 23 Feb 2022 17:06:19 +0900 Subject: [PATCH 01/15] Add cfg post order traversal --- crates/mir/src/analysis/cfg.rs | 74 ++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/crates/mir/src/analysis/cfg.rs b/crates/mir/src/analysis/cfg.rs index 82c2439ad5..7f98ea3b77 100644 --- a/crates/mir/src/analysis/cfg.rs +++ b/crates/mir/src/analysis/cfg.rs @@ -27,6 +27,10 @@ impl ControlFlowGraph { cfg } + pub fn entry(&self) -> BasicBlockId { + self.entry + } + pub fn preds(&self, block: BasicBlockId) -> &[BasicBlockId] { self.blocks[&block].preds() } @@ -35,6 +39,10 @@ impl ControlFlowGraph { self.blocks[&block].succs() } + pub fn post_order(&self) -> CfgPostOrder { + CfgPostOrder::new(self) + } + fn analyze_terminator(&mut self, func: &FunctionBody, terminator: InstId) { let block = func.order.inst_block(terminator); match func.store.branch_info(terminator) { @@ -82,3 +90,69 @@ impl BlockNode { &self.succs } } + +pub struct CfgPostOrder<'a> { + cfg: &'a ControlFlowGraph, + node_state: FxHashMap, + stack: Vec, +} + +impl<'a> CfgPostOrder<'a> { + fn new(cfg: &'a ControlFlowGraph) -> Self { + let stack = vec![cfg.entry()]; + + Self { + cfg, + node_state: FxHashMap::default(), + stack, + } + } +} + +impl<'a> Iterator for CfgPostOrder<'a> { + type Item = BasicBlockId; + + fn next(&mut self) -> Option { + while let Some(&block) = self.stack.last() { + let node_state = self.node_state.entry(block).or_default(); + if node_state.is_unvisited() { + node_state.set_visited(); + for &succ in self.cfg.succs(block) { + let pred_state = self.node_state.entry(succ).or_default(); + if pred_state.is_unvisited() { + self.stack.push(succ); + } + } + } else { + self.stack.pop().unwrap(); + if !node_state.has_finished() { + node_state.set_finished(); + return Some(block); + } + } + } + + None + } +} + +#[derive(Default, Debug, Clone, Copy)] +struct NodeState(u8); + +impl NodeState { + fn is_unvisited(self) -> bool { + self.0 == 0 + } + + fn has_finished(self) -> bool { + self.0 == 2 + } + + fn set_visited(&mut self) { + self.0 = 1; + } + + fn set_finished(&mut self) { + self.0 = 2; + } +} From 9b2706755d23afeb83c1eaf6036a40ced41799a8 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Wed, 23 Feb 2022 19:11:14 +0900 Subject: [PATCH 02/15] Implement dominator tree --- crates/mir/src/analysis/domtree.rs | 286 +++++++++++++++++++++++++++++ crates/mir/src/analysis/mod.rs | 1 + 2 files changed, 287 insertions(+) create mode 100644 crates/mir/src/analysis/domtree.rs diff --git a/crates/mir/src/analysis/domtree.rs b/crates/mir/src/analysis/domtree.rs new file mode 100644 index 0000000000..ae6a1043f7 --- /dev/null +++ b/crates/mir/src/analysis/domtree.rs @@ -0,0 +1,286 @@ +//! This module contains dominantor tree related structs. +//! +//! The algorithm is based on Keith D. Cooper., Timothy J. Harvey., and Ken +//! Kennedy.: A Simple, Fast Dominance Algorithm: + +use fxhash::FxHashMap; + +use crate::ir::BasicBlockId; + +use super::cfg::ControlFlowGraph; + +#[derive(Debug, Clone)] +pub struct DomTree { + doms: FxHashMap, + /// CFG sorted in reverse post order. + rpo: Vec, +} + +impl DomTree { + pub fn compute(cfg: &ControlFlowGraph) -> Self { + let mut doms = FxHashMap::default(); + doms.insert(cfg.entry(), cfg.entry()); + let mut rpo: Vec<_> = cfg.post_order().collect(); + rpo.reverse(); + + let mut domtree = Self { doms, rpo }; + + let block_num = domtree.rpo.len(); + + let mut rpo_nums = FxHashMap::default(); + for (i, &block) in domtree.rpo.iter().enumerate() { + rpo_nums.insert(block, (block_num - i) as u32); + } + + let mut changed = true; + while changed { + changed = false; + for &block in domtree.rpo.iter().skip(1) { + let processed_pred = match cfg + .preds(block) + .iter() + .find(|pred| domtree.doms.contains_key(pred)) + { + Some(pred) => *pred, + _ => continue, + }; + let mut new_dom = processed_pred; + + for &pred in cfg.preds(block) { + if pred != processed_pred && domtree.doms.contains_key(&pred) { + new_dom = domtree.intersect(new_dom, pred, &rpo_nums); + } + } + if Some(new_dom) != domtree.doms.get(&block).copied() { + changed = true; + domtree.doms.insert(block, new_dom); + } + } + } + + domtree + } + + /// Returns the immediate dominator of the `block`. + /// Returns None if the `block` is unreachable from the entry block, or the + /// `block` is the entry block itself. + pub fn idom(&self, block: BasicBlockId) -> Option { + if self.rpo[0] == block { + return None; + } + self.doms.get(&block).copied() + } + + /// Returns `true` if block1 strictly dominates block2. + pub fn strictly_dominates(&self, block1: BasicBlockId, block2: BasicBlockId) -> bool { + let mut current_block = block2; + while let Some(block) = self.idom(current_block) { + if block == block1 { + return true; + } + current_block = block; + } + + false + } + + /// Returns `true` if block1 dominates block2. + pub fn dominates(&self, block1: BasicBlockId, block2: BasicBlockId) -> bool { + if block1 == block2 { + return true; + } + + self.strictly_dominates(block1, block2) + } + + /// Returns `true` if block is reachable from the entry block. + pub fn is_reachable(&self, block: BasicBlockId) -> bool { + self.idom(block).is_some() + } + + /// Returns blocks in RPO. + pub fn rpo(&self) -> &[BasicBlockId] { + &self.rpo + } + + fn intersect( + &self, + mut b1: BasicBlockId, + mut b2: BasicBlockId, + rpo_nums: &FxHashMap, + ) -> BasicBlockId { + while b1 != b2 { + while rpo_nums[&b1] < rpo_nums[&b2] { + b1 = self.doms[&b1]; + } + while rpo_nums[&b2] < rpo_nums[&b1] { + b2 = self.doms[&b2] + } + } + + b1 + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use crate::ir::{body_builder::BodyBuilder, FunctionBody, FunctionId, SourceInfo, TypeId}; + + fn calc_dom(func: &FunctionBody) -> DomTree { + let cfg = ControlFlowGraph::compute(func); + DomTree::compute(&cfg) + } + + fn body_builder() -> BodyBuilder { + BodyBuilder::new(FunctionId(0), SourceInfo::dummy()) + } + + #[test] + fn dom_tree_if_else() { + let mut builder = body_builder(); + + let then_block = builder.make_block(); + let else_block = builder.make_block(); + let merge_block = builder.make_block(); + + let dummy_ty = TypeId(0); + let v0 = builder.make_imm_from_bool(true, dummy_ty); + builder.branch(v0, else_block, then_block, SourceInfo::dummy()); + + builder.move_to_block(then_block); + builder.jump(merge_block, SourceInfo::dummy()); + + builder.move_to_block(else_block); + builder.jump(merge_block, SourceInfo::dummy()); + + builder.move_to_block(merge_block); + let dummy_value = builder.make_unit(dummy_ty); + builder.ret(dummy_value, SourceInfo::dummy()); + + let func = builder.build(); + + let dom_tree = calc_dom(&func); + let entry_block = func.order.entry_block(); + assert_eq!(dom_tree.idom(entry_block), None); + assert_eq!(dom_tree.idom(then_block), Some(entry_block)); + assert_eq!(dom_tree.idom(else_block), Some(entry_block)); + assert_eq!(dom_tree.idom(merge_block), Some(entry_block)); + } + + #[test] + fn unreachable_edge() { + let mut builder = body_builder(); + + let block1 = builder.make_block(); + let block2 = builder.make_block(); + let block3 = builder.make_block(); + let block4 = builder.make_block(); + + let dummy_ty = TypeId(0); + let v0 = builder.make_imm_from_bool(true, dummy_ty); + builder.branch(v0, block1, block2, SourceInfo::dummy()); + + builder.move_to_block(block1); + builder.jump(block4, SourceInfo::dummy()); + + builder.move_to_block(block2); + builder.jump(block4, SourceInfo::dummy()); + + builder.move_to_block(block3); + builder.jump(block4, SourceInfo::dummy()); + + builder.move_to_block(block4); + let dummy_value = builder.make_unit(dummy_ty); + builder.ret(dummy_value, SourceInfo::dummy()); + + let func = builder.build(); + + let dom_tree = calc_dom(&func); + let entry_block = func.order.entry_block(); + assert_eq!(dom_tree.idom(entry_block), None); + assert_eq!(dom_tree.idom(block1), Some(entry_block)); + assert_eq!(dom_tree.idom(block2), Some(entry_block)); + assert_eq!(dom_tree.idom(block3), None); + assert!(!dom_tree.is_reachable(block3)); + assert_eq!(dom_tree.idom(block4), Some(entry_block)); + } + + #[test] + fn dom_tree_complex() { + let mut builder = body_builder(); + + let block1 = builder.make_block(); + let block2 = builder.make_block(); + let block3 = builder.make_block(); + let block4 = builder.make_block(); + let block5 = builder.make_block(); + let block6 = builder.make_block(); + let block7 = builder.make_block(); + let block8 = builder.make_block(); + let block9 = builder.make_block(); + let block10 = builder.make_block(); + let block11 = builder.make_block(); + let block12 = builder.make_block(); + + let dummy_ty = TypeId(0); + let v0 = builder.make_imm_from_bool(true, dummy_ty); + builder.branch(v0, block2, block1, SourceInfo::dummy()); + + builder.move_to_block(block1); + builder.branch(v0, block6, block3, SourceInfo::dummy()); + + builder.move_to_block(block2); + builder.branch(v0, block7, block4, SourceInfo::dummy()); + + builder.move_to_block(block3); + builder.branch(v0, block6, block5, SourceInfo::dummy()); + + builder.move_to_block(block4); + builder.branch(v0, block7, block2, SourceInfo::dummy()); + + builder.move_to_block(block5); + builder.branch(v0, block10, block8, SourceInfo::dummy()); + + builder.move_to_block(block6); + builder.jump(block9, SourceInfo::dummy()); + + builder.move_to_block(block7); + builder.jump(block12, SourceInfo::dummy()); + + builder.move_to_block(block8); + builder.jump(block11, SourceInfo::dummy()); + + builder.move_to_block(block9); + builder.jump(block8, SourceInfo::dummy()); + + builder.move_to_block(block10); + builder.jump(block11, SourceInfo::dummy()); + + builder.move_to_block(block11); + builder.branch(v0, block12, block2, SourceInfo::dummy()); + + builder.move_to_block(block12); + let dummy_value = builder.make_unit(dummy_ty); + builder.ret(dummy_value, SourceInfo::dummy()); + + let func = builder.build(); + + let dom_tree = calc_dom(&func); + let entry_block = func.order.entry_block(); + assert_eq!(dom_tree.idom(entry_block), None); + assert_eq!(dom_tree.idom(block1), Some(entry_block)); + assert_eq!(dom_tree.idom(block2), Some(entry_block)); + assert_eq!(dom_tree.idom(block3), Some(block1)); + assert_eq!(dom_tree.idom(block4), Some(block2)); + assert_eq!(dom_tree.idom(block5), Some(block3)); + assert_eq!(dom_tree.idom(block6), Some(block1)); + assert_eq!(dom_tree.idom(block7), Some(block2)); + assert_eq!(dom_tree.idom(block8), Some(block1)); + assert_eq!(dom_tree.idom(block9), Some(block6)); + assert_eq!(dom_tree.idom(block10), Some(block5)); + assert_eq!(dom_tree.idom(block11), Some(block1)); + assert_eq!(dom_tree.idom(block12), Some(entry_block)); + } +} diff --git a/crates/mir/src/analysis/mod.rs b/crates/mir/src/analysis/mod.rs index 7fadafc66e..6feeed8632 100644 --- a/crates/mir/src/analysis/mod.rs +++ b/crates/mir/src/analysis/mod.rs @@ -1,3 +1,4 @@ pub mod cfg; +pub mod domtree; pub use cfg::ControlFlowGraph; From 6306e5c7c8a1c86760653e1cd882abcb8e27017c Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Thu, 24 Feb 2022 01:30:49 +0900 Subject: [PATCH 03/15] Implement loop tree --- crates/mir/src/analysis/loop_tree.rs | 347 +++++++++++++++++++++++++++ crates/mir/src/analysis/mod.rs | 1 + 2 files changed, 348 insertions(+) create mode 100644 crates/mir/src/analysis/loop_tree.rs diff --git a/crates/mir/src/analysis/loop_tree.rs b/crates/mir/src/analysis/loop_tree.rs new file mode 100644 index 0000000000..ca13db5dcb --- /dev/null +++ b/crates/mir/src/analysis/loop_tree.rs @@ -0,0 +1,347 @@ +use id_arena::{Arena, Id}; + +use fxhash::FxHashMap; + +use super::{cfg::ControlFlowGraph, domtree::DomTree}; + +use crate::ir::BasicBlockId; + +#[derive(Debug, Default, Clone)] +pub struct LoopTree { + /// Stores loops. + /// The index of an outer loops is guaranteed to be lower than its inner + /// loops because loops are found in RPO. + loops: Arena, + + /// Maps blocks to its contained loop. + /// If the block is contained by multiple nested loops, then the block is + /// mapped to the innermost loop. + block_to_loop: FxHashMap, +} + +pub type LoopId = Id; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Loop { + /// A header of the loop. + pub header: BasicBlockId, + + /// A parent loop that includes the loop. + pub parent: Option, + + /// Child loops that the loop includes. + pub children: Vec, +} + +impl LoopTree { + pub fn compute(cfg: &ControlFlowGraph, domtree: &DomTree) -> Self { + let mut tree = LoopTree::default(); + + // Find loop headers in RPO, this means outer loops are guaranteed to be + // inserted first, then its inner loops are inserted. + for &block in domtree.rpo() { + for &pred in cfg.preds(block) { + if domtree.dominates(block, pred) { + let loop_data = Loop { + header: block, + parent: None, + children: Vec::new(), + }; + + tree.loops.alloc(loop_data); + break; + } + } + } + + tree.analyze_loops(cfg, domtree); + + tree + } + + /// Returns all blocks in the loop. + pub fn iter_blocks_post_order<'a, 'b>( + &'a self, + cfg: &'b ControlFlowGraph, + lp: LoopId, + ) -> BlocksInLoopPostOrder<'a, 'b> { + BlocksInLoopPostOrder::new(self, cfg, lp) + } + + /// Returns all loops in a function body. + /// An outer loop is guaranteed to be iterated before its inner loops. + pub fn loops(&self) -> impl Iterator + '_ { + self.loops.iter().map(|(id, _)| id) + } + + /// Returns number of loops found. + pub fn loop_num(&self) -> usize { + self.loops.len() + } + + /// Returns `true` if the `block` is in the `lp`. + pub fn is_block_in_loop(&self, block: BasicBlockId, lp: LoopId) -> bool { + let mut loop_of_block = self.loop_of_block(block); + while let Some(cur_lp) = loop_of_block { + if lp == cur_lp { + return true; + } + loop_of_block = self.parent_loop(cur_lp); + } + false + } + + /// Returns header block of the `lp`. + pub fn loop_header(&self, lp: LoopId) -> BasicBlockId { + self.loops[lp].header + } + + /// Get parent loop of the `lp` if exists. + pub fn parent_loop(&self, lp: LoopId) -> Option { + self.loops[lp].parent + } + + /// Returns the loop that the `block` belongs to. + /// If the `block` belongs to multiple loops, then returns the innermost + /// loop. + pub fn loop_of_block(&self, block: BasicBlockId) -> Option { + self.block_to_loop.get(&block).copied() + } + + /// Analyze loops. This method does + /// 1. Mapping each blocks to its contained loop. + /// 2. Setting parent and child of the loops. + fn analyze_loops(&mut self, cfg: &ControlFlowGraph, domtree: &DomTree) { + let mut worklist = vec![]; + + // Iterate loops reversely to ensure analyze inner loops first. + let loops_rev: Vec<_> = self.loops.iter().rev().map(|(id, _)| id).collect(); + for cur_lp in loops_rev { + let cur_lp_header = self.loop_header(cur_lp); + + // Add predecessors of the loop header to worklist. + for &block in cfg.preds(cur_lp_header) { + if domtree.dominates(cur_lp_header, block) { + worklist.push(block); + } + } + + while let Some(block) = worklist.pop() { + match self.block_to_loop.get(&block).copied() { + Some(lp_of_block) => { + let outermost_parent = self.outermost_parent(lp_of_block); + + // If outermost parent is current loop, then the block is already visited. + if outermost_parent == cur_lp { + continue; + } else { + self.loops[cur_lp].children.push(outermost_parent); + self.loops[outermost_parent].parent = cur_lp.into(); + + let lp_header_of_block = self.loop_header(lp_of_block); + worklist.extend(cfg.preds(lp_header_of_block)); + } + } + + // If the block is not mapped to any loops, then map it to the loop. + None => { + self.map_block(block, cur_lp); + // If block is not loop header, then add its predecessors to the worklist. + if block != cur_lp_header { + worklist.extend(cfg.preds(block)); + } + } + } + } + } + } + + /// Returns the outermost parent loop of `lp`. If `lp` doesn't have any + /// parent, then returns `lp` itself. + fn outermost_parent(&self, mut lp: LoopId) -> LoopId { + while let Some(parent) = self.parent_loop(lp) { + lp = parent; + } + lp + } + + /// Map `block` to `lp`. + fn map_block(&mut self, block: BasicBlockId, lp: LoopId) { + self.block_to_loop.insert(block, lp); + } +} + +pub struct BlocksInLoopPostOrder<'a, 'b> { + lpt: &'a LoopTree, + cfg: &'b ControlFlowGraph, + lp: LoopId, + stack: Vec, + block_state: FxHashMap, +} + +impl<'a, 'b> BlocksInLoopPostOrder<'a, 'b> { + fn new(lpt: &'a LoopTree, cfg: &'b ControlFlowGraph, lp: LoopId) -> Self { + let loop_header = lpt.loop_header(lp); + + Self { + lpt, + cfg, + lp, + stack: vec![loop_header], + block_state: FxHashMap::default(), + } + } +} + +impl<'a, 'b> Iterator for BlocksInLoopPostOrder<'a, 'b> { + type Item = BasicBlockId; + + fn next(&mut self) -> Option { + while let Some(&block) = self.stack.last() { + match self.block_state.get(&block) { + // The block is already visited, but not returned from the iterator, + // so mark the block as `Finished` and return the block. + Some(BlockState::Visited) => { + let block = self.stack.pop().unwrap(); + self.block_state.insert(block, BlockState::Finished); + return Some(block); + } + + // The block is already returned, so just remove the block from the stack. + Some(BlockState::Finished) => { + self.stack.pop().unwrap(); + } + + // The block is not visited yet, so push its unvisited in-loop successors to the + // stack and mark the block as `Visited`. + None => { + self.block_state.insert(block, BlockState::Visited); + for &succ in self.cfg.succs(block) { + if self.block_state.get(&succ).is_none() + && self.lpt.is_block_in_loop(succ, self.lp) + { + self.stack.push(succ); + } + } + } + } + } + + None + } +} + +enum BlockState { + Visited, + Finished, +} + +#[cfg(test)] +mod tests { + use super::*; + + use crate::ir::{body_builder::BodyBuilder, FunctionBody, FunctionId, SourceInfo, TypeId}; + + fn compute_loop(func: &FunctionBody) -> LoopTree { + let cfg = ControlFlowGraph::compute(func); + let domtree = DomTree::compute(&cfg); + LoopTree::compute(&cfg, &domtree) + } + + fn body_builder() -> BodyBuilder { + BodyBuilder::new(FunctionId(0), SourceInfo::dummy()) + } + + #[test] + fn simple_loop() { + let mut builder = body_builder(); + + let entry = builder.current_block(); + let block1 = builder.make_block(); + let block2 = builder.make_block(); + + let dummy_ty = TypeId(0); + let v0 = builder.make_imm_from_bool(false, dummy_ty); + builder.branch(v0, block1, block2, SourceInfo::dummy()); + + builder.move_to_block(block1); + builder.jump(entry, SourceInfo::dummy()); + + builder.move_to_block(block2); + let dummy_value = builder.make_unit(dummy_ty); + builder.ret(dummy_value, SourceInfo::dummy()); + + let func = builder.build(); + + let lpt = compute_loop(&func); + + assert_eq!(lpt.loop_num(), 1); + let lp = lpt.loops().next().unwrap(); + + assert!(lpt.is_block_in_loop(entry, lp)); + assert_eq!(lpt.loop_of_block(entry), Some(lp)); + + assert!(lpt.is_block_in_loop(block1, lp)); + assert_eq!(lpt.loop_of_block(block1), Some(lp)); + + assert!(!lpt.is_block_in_loop(block2, lp)); + assert!(lpt.loop_of_block(block2).is_none()); + + assert_eq!(lpt.loop_header(lp), entry); + } + + #[test] + fn nested_loop() { + let mut builder = body_builder(); + + let entry = builder.current_block(); + let block1 = builder.make_block(); + let block2 = builder.make_block(); + let block3 = builder.make_block(); + + let dummy_ty = TypeId(0); + let v0 = builder.make_imm_from_bool(false, dummy_ty); + builder.branch(v0, block1, block3, SourceInfo::dummy()); + + builder.move_to_block(block1); + builder.branch(v0, entry, block2, SourceInfo::dummy()); + + builder.move_to_block(block2); + builder.jump(block1, SourceInfo::dummy()); + + builder.move_to_block(block3); + let dummy_value = builder.make_unit(dummy_ty); + builder.ret(dummy_value, SourceInfo::dummy()); + + let func = builder.build(); + + let lpt = compute_loop(&func); + + assert_eq!(lpt.loop_num(), 2); + let mut loops = lpt.loops(); + let outer_lp = loops.next().unwrap(); + let inner_lp = loops.next().unwrap(); + + assert!(lpt.is_block_in_loop(entry, outer_lp)); + assert!(!lpt.is_block_in_loop(entry, inner_lp)); + assert_eq!(lpt.loop_of_block(entry), Some(outer_lp)); + + assert!(lpt.is_block_in_loop(block1, outer_lp)); + assert!(lpt.is_block_in_loop(block1, inner_lp)); + assert_eq!(lpt.loop_of_block(block1), Some(inner_lp)); + + assert!(lpt.is_block_in_loop(block2, outer_lp)); + assert!(lpt.is_block_in_loop(block2, inner_lp)); + assert_eq!(lpt.loop_of_block(block2), Some(inner_lp)); + + assert!(!lpt.is_block_in_loop(block3, outer_lp)); + assert!(!lpt.is_block_in_loop(block3, inner_lp)); + assert!(lpt.loop_of_block(block3).is_none()); + + assert!(lpt.parent_loop(outer_lp).is_none()); + assert_eq!(lpt.parent_loop(inner_lp), Some(outer_lp)); + + assert_eq!(lpt.loop_header(outer_lp), entry); + assert_eq!(lpt.loop_header(inner_lp), block1); + } +} diff --git a/crates/mir/src/analysis/mod.rs b/crates/mir/src/analysis/mod.rs index 6feeed8632..ab7f579730 100644 --- a/crates/mir/src/analysis/mod.rs +++ b/crates/mir/src/analysis/mod.rs @@ -1,4 +1,5 @@ pub mod cfg; pub mod domtree; +pub mod loop_tree; pub use cfg::ControlFlowGraph; From 1ea6a692cd69a06837702695468950222dcb985e Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Fri, 25 Feb 2022 14:31:18 +0900 Subject: [PATCH 04/15] Enable tests related to `std::context` --- crates/mir/tests/lowering.rs | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/crates/mir/tests/lowering.rs b/crates/mir/tests/lowering.rs index cd9e4cfe59..89d7ec702f 100644 --- a/crates/mir/tests/lowering.rs +++ b/crates/mir/tests/lowering.rs @@ -44,12 +44,9 @@ fn mir_lower_std_lib() { } } -// TODO: Uncomment when context is implemented. -// test_lowering! { mir_erc20_token, "demos/erc20_token.fe"} -// TODO: Uncomment when context is implemented. -// test_lowering! { mir_guest_book, "demos/guest_book.fe"} -// TODO: Uncomment when context is implemented. -// test_lowering! { mir_uniswap, "demos/uniswap.fe"} +test_lowering! { mir_erc20_token, "demos/erc20_token.fe"} +test_lowering! { mir_guest_book, "demos/guest_book.fe"} +test_lowering! { mir_uniswap, "demos/uniswap.fe"} test_lowering! { mir_address_bytes10_map, "features/address_bytes10_map.fe"} test_lowering! { mir_assert, "features/assert.fe"} test_lowering! { mir_associated_fns, "features/associated_fns.fe"} @@ -65,8 +62,7 @@ test_lowering! { mir_create_contract, "features/create_contract.fe"} test_lowering! { mir_create_contract_from_init, "features/create_contract_from_init.fe"} test_lowering! { mir_empty, "features/empty.fe"} test_lowering! { mir_events, "features/events.fe"} -// TODO: Uncomment when context is implemented. -// test_lowering! { mir_module_level_events, "features/module_level_events.fe"} +test_lowering! { mir_module_level_events, "features/module_level_events.fe"} test_lowering! { mir_external_contract, "features/external_contract.fe"} test_lowering! { mir_for_loop_with_break, "features/for_loop_with_break.fe"} test_lowering! { mir_for_loop_with_continue, "features/for_loop_with_continue.fe"} @@ -80,8 +76,7 @@ test_lowering! { mir_module_const, "features/module_const.fe"} test_lowering! { mir_multi_param, "features/multi_param.fe"} test_lowering! { mir_nested_map, "features/nested_map.fe"} test_lowering! { mir_numeric_sizes, "features/numeric_sizes.fe"} -// TODO: Uncomment when context is implemented. -// test_lowering! { mir_ownable, "features/ownable.fe"} +test_lowering! { mir_ownable, "features/ownable.fe"} test_lowering! { mir_pure_fn_standalone, "features/pure_fn_standalone.fe"} test_lowering! { mir_return_addition_i256, "features/return_addition_i256.fe"} test_lowering! { mir_return_addition_u128, "features/return_addition_u128.fe"} @@ -99,9 +94,7 @@ test_lowering! { mir_return_bool_inverted, "features/return_bool_inverted.fe"} test_lowering! { mir_return_bool_op_and, "features/return_bool_op_and.fe"} test_lowering! { mir_return_bool_op_or, "features/return_bool_op_or.fe"} test_lowering! { mir_return_bool_true, "features/return_bool_true.fe"} -// TODO: Uncomment when context is implemented. -// test_lowering! { mir_return_builtin_attributes, -// "features/return_builtin_attributes.fe"} +test_lowering! { mir_return_builtin_attributes, "features/return_builtin_attributes.fe"} test_lowering! { mir_return_division_i256, "features/return_division_i256.fe"} test_lowering! { mir_return_division_u256, "features/return_division_u256.fe"} test_lowering! { mir_return_empty_tuple, "features/return_unit.fe"} @@ -125,8 +118,7 @@ test_lowering! { mir_return_lte_i256, "features/return_lte_i256.fe"} test_lowering! { mir_return_lte_u256, "features/return_lte_u256.fe"} test_lowering! { mir_return_mod_i256, "features/return_mod_i256.fe"} test_lowering! { mir_return_mod_u256, "features/return_mod_u256.fe"} -// TODO: Uncomment when context is implemented. -// test_lowering! { mir_return_msg_sig, "features/return_msg_sig.fe"} +test_lowering! { mir_return_msg_sig, "features/return_msg_sig.fe"} test_lowering! { mir_return_multiplication_i256, "features/return_multiplication_i256.fe"} test_lowering! { mir_return_multiplication_u256, "features/return_multiplication_u256.fe"} test_lowering! { mir_return_noteq_u256, "features/return_noteq_u256.fe"} @@ -139,8 +131,7 @@ test_lowering! { mir_return_u256, "features/return_u256.fe"} test_lowering! { mir_return_u256_from_called_fn, "features/return_u256_from_called_fn.fe"} test_lowering! { mir_return_u256_from_called_fn_with_args, "features/return_u256_from_called_fn_with_args.fe"} test_lowering! { mir_revert, "features/revert.fe"} -// TODO: Uncomment when context is implemented. -// test_lowering! { mir_self_address, "features/self_address.fe"} +test_lowering! { mir_self_address, "features/self_address.fe"} test_lowering! { mir_send_value, "features/send_value.fe"} test_lowering! { mir_balances, "features/balances.fe"} test_lowering! { mir_sized_vals_in_sto, "features/sized_vals_in_sto.fe"} @@ -148,8 +139,7 @@ test_lowering! { mir_strings, "features/strings.fe"} test_lowering! { mir_structs, "features/structs.fe"} test_lowering! { mir_struct_fns, "features/struct_fns.fe"} test_lowering! { mir_ternary_expression, "features/ternary_expression.fe"} -// TODO: Uncomment when context is implemented. -// test_lowering! { mir_two_contracts, "features/two_contracts.fe"} +test_lowering! { mir_two_contracts, "features/two_contracts.fe"} test_lowering! { mir_u8_u8_map, "features/u8_u8_map.fe"} test_lowering! { mir_u16_u16_map, "features/u16_u16_map.fe"} test_lowering! { mir_u32_u32_map, "features/u32_u32_map.fe"} @@ -163,7 +153,6 @@ test_lowering! { mir_while_loop_with_continue, "features/while_loop_with_continu test_lowering! { mir_abi_encoding_stress, "stress/abi_encoding_stress.fe"} test_lowering! { mir_data_copying_stress, "stress/data_copying_stress.fe"} test_lowering! { mir_tuple_stress, "stress/tuple_stress.fe"} -// TODO: Uncomment when context is implemented. -// test_lowering! { mir_type_aliases, "features/type_aliases.fe"} +test_lowering! { mir_type_aliases, "features/type_aliases.fe"} test_lowering! { mir_const_generics, "features/const_generics.fe" } test_lowering! { mir_const_local, "features/const_local.fe" } From 06303a2379024319a91997f225288270b039e1f6 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Mon, 28 Feb 2022 17:48:58 +0900 Subject: [PATCH 05/15] Remove a salsa query related to MIR analysis --- crates/mir/src/db.rs | 8 +------- crates/mir/src/db/queries/function.rs | 12 ------------ crates/mir/src/graphviz/block.rs | 4 +++- crates/mir/tests/lowering.rs | 13 ++++++++----- 4 files changed, 12 insertions(+), 25 deletions(-) diff --git a/crates/mir/src/db.rs b/crates/mir/src/db.rs index 533cb1b9b4..e2c2525a7f 100644 --- a/crates/mir/src/db.rs +++ b/crates/mir/src/db.rs @@ -7,10 +7,7 @@ use fe_analyzer::{ }; use fe_common::db::{SourceDb, SourceDbStorage, Upcast, UpcastMut}; -use crate::{ - analysis, - ir::{self, ConstantId, TypeId}, -}; +use crate::ir::{self, ConstantId, TypeId}; mod queries; @@ -56,9 +53,6 @@ pub trait MirDb: AnalyzerDb + Upcast + UpcastMut ) -> ir::FunctionId; #[salsa::invoke(queries::function::mir_lowered_func_body)] fn mir_lowered_func_body(&self, func: ir::FunctionId) -> Rc; - - #[salsa::invoke(queries::function::mir_func_cfg)] - fn mir_func_cfg(&self, func: ir::FunctionId) -> Rc; } #[salsa::database(SourceDbStorage, AnalyzerDbStorage, MirDbStorage)] diff --git a/crates/mir/src/db/queries/function.rs b/crates/mir/src/db/queries/function.rs index eeaba639b0..706d694608 100644 --- a/crates/mir/src/db/queries/function.rs +++ b/crates/mir/src/db/queries/function.rs @@ -4,7 +4,6 @@ use fe_analyzer::namespace::items as analyzer_items; use smol_str::SmolStr; use crate::{ - analysis::ControlFlowGraph, db::MirDb, ir::{self, FunctionSignature, TypeId}, lower::function::{lower_func_body, lower_func_signature}, @@ -21,13 +20,6 @@ pub fn mir_lowered_func_body(db: &dyn MirDb, func: ir::FunctionId) -> Rc Rc { - let body = func.body(db); - let cfg = ControlFlowGraph::compute(&body); - - cfg.into() -} - impl ir::FunctionId { pub fn data(self, db: &dyn MirDb) -> Rc { db.lookup_mir_intern_function(self) @@ -45,10 +37,6 @@ impl ir::FunctionId { db.mir_lowered_func_body(self) } - pub fn cfg(self, db: &dyn MirDb) -> Rc { - db.mir_func_cfg(self) - } - pub fn module(self, db: &dyn MirDb) -> analyzer_items::ModuleId { let analyzer_func = self.analyzer_func(db); analyzer_func.module(db.upcast()) diff --git a/crates/mir/src/graphviz/block.rs b/crates/mir/src/graphviz/block.rs index bc851726ea..f193f38e0c 100644 --- a/crates/mir/src/graphviz/block.rs +++ b/crates/mir/src/graphviz/block.rs @@ -3,6 +3,7 @@ use std::fmt::Write; use dot2::{label, Id}; use crate::{ + analysis::ControlFlowGraph, db::MirDb, ir::{BasicBlockId, FunctionId}, pretty_print::PrettyPrint, @@ -51,7 +52,8 @@ impl BlockNode { } pub(super) fn preds(self, db: &dyn MirDb) -> Vec { - let cfg = self.func.cfg(db); + let func_body = self.func.body(db); + let cfg = ControlFlowGraph::compute(&func_body); cfg.preds(self.block) .iter() .map(|block| Self::new(self.func, *block)) diff --git a/crates/mir/tests/lowering.rs b/crates/mir/tests/lowering.rs index 89d7ec702f..ced23bba07 100644 --- a/crates/mir/tests/lowering.rs +++ b/crates/mir/tests/lowering.rs @@ -1,6 +1,9 @@ use fe_analyzer::namespace::items::{IngotId, ModuleId}; use fe_common::{db::Upcast, files::Utf8Path}; -use fe_mir::db::{MirDb, NewDb}; +use fe_mir::{ + analysis::ControlFlowGraph, + db::{MirDb, NewDb}, +}; macro_rules! test_lowering { ($name:ident, $path:expr) => { @@ -17,8 +20,8 @@ macro_rules! test_lowering { } for func in db.mir_lower_module_all_functions(module).iter() { - func.body(&db); - func.cfg(&db); + let body = func.body(&db); + ControlFlowGraph::compute(&body); } } }; @@ -38,8 +41,8 @@ fn mir_lower_std_lib() { for &module in std_ingot.all_modules(db.upcast()).iter() { for func in db.mir_lower_module_all_functions(module).iter() { - func.body(&db); - func.cfg(&db); + let body = func.body(&db); + ControlFlowGraph::compute(&body); } } } From d39791b8221157c2c5d0a3fc8269a834ebeba9d5 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Mon, 28 Feb 2022 19:24:04 +0900 Subject: [PATCH 06/15] Implement `PostDomTree` --- crates/mir/src/analysis/cfg.rs | 26 ++- crates/mir/src/analysis/domtree.rs | 2 +- crates/mir/src/analysis/mod.rs | 1 + crates/mir/src/analysis/post_domtree.rs | 284 ++++++++++++++++++++++++ 4 files changed, 307 insertions(+), 6 deletions(-) create mode 100644 crates/mir/src/analysis/post_domtree.rs diff --git a/crates/mir/src/analysis/cfg.rs b/crates/mir/src/analysis/cfg.rs index 7f98ea3b77..a9141a3454 100644 --- a/crates/mir/src/analysis/cfg.rs +++ b/crates/mir/src/analysis/cfg.rs @@ -6,6 +6,7 @@ use crate::ir::{inst::BranchInfo, BasicBlockId, FunctionBody, InstId}; pub struct ControlFlowGraph { entry: BasicBlockId, blocks: FxHashMap, + pub(super) exits: Vec, } impl ControlFlowGraph { @@ -14,6 +15,7 @@ impl ControlFlowGraph { let mut cfg = Self { entry, blocks: FxHashMap::default(), + exits: vec![], }; for block in func.order.iter_block() { @@ -43,11 +45,26 @@ impl ControlFlowGraph { CfgPostOrder::new(self) } + pub(super) fn add_edge(&mut self, from: BasicBlockId, to: BasicBlockId) { + self.node_mut(to).push_pred(from); + self.node_mut(from).push_succ(to); + } + + pub(super) fn reverse_edge(&mut self, new_entry: BasicBlockId, new_exits: Vec) { + for (_, block) in self.blocks.iter_mut() { + block.reverse_edge() + } + + self.entry = new_entry; + self.exits = new_exits; + } + fn analyze_terminator(&mut self, func: &FunctionBody, terminator: InstId) { let block = func.order.inst_block(terminator); match func.store.branch_info(terminator) { BranchInfo::NotBranch => { self.node_mut(block); + self.exits.push(block) } BranchInfo::Jump(dest) => self.add_edge(block, dest), BranchInfo::Branch((then, else_)) => { @@ -57,11 +74,6 @@ impl ControlFlowGraph { } } - fn add_edge(&mut self, from: BasicBlockId, to: BasicBlockId) { - self.node_mut(to).push_pred(from); - self.node_mut(from).push_succ(to); - } - fn node_mut(&mut self, block: BasicBlockId) -> &mut BlockNode { self.blocks.entry(block).or_default() } @@ -89,6 +101,10 @@ impl BlockNode { fn succs(&self) -> &[BasicBlockId] { &self.succs } + + fn reverse_edge(&mut self) { + std::mem::swap(&mut self.preds, &mut self.succs) + } } pub struct CfgPostOrder<'a> { diff --git a/crates/mir/src/analysis/domtree.rs b/crates/mir/src/analysis/domtree.rs index ae6a1043f7..0e51742c02 100644 --- a/crates/mir/src/analysis/domtree.rs +++ b/crates/mir/src/analysis/domtree.rs @@ -147,7 +147,7 @@ mod tests { let dummy_ty = TypeId(0); let v0 = builder.make_imm_from_bool(true, dummy_ty); - builder.branch(v0, else_block, then_block, SourceInfo::dummy()); + builder.branch(v0, then_block, else_block, SourceInfo::dummy()); builder.move_to_block(then_block); builder.jump(merge_block, SourceInfo::dummy()); diff --git a/crates/mir/src/analysis/mod.rs b/crates/mir/src/analysis/mod.rs index ab7f579730..9c2c2bd5e8 100644 --- a/crates/mir/src/analysis/mod.rs +++ b/crates/mir/src/analysis/mod.rs @@ -1,5 +1,6 @@ pub mod cfg; pub mod domtree; pub mod loop_tree; +pub mod post_domtree; pub use cfg::ControlFlowGraph; diff --git a/crates/mir/src/analysis/post_domtree.rs b/crates/mir/src/analysis/post_domtree.rs new file mode 100644 index 0000000000..0a7f4cc334 --- /dev/null +++ b/crates/mir/src/analysis/post_domtree.rs @@ -0,0 +1,284 @@ +//! This module contains implementation of `Post Dominator Tree`. + +use id_arena::{ArenaBehavior, DefaultArenaBehavior}; + +use super::{cfg::ControlFlowGraph, domtree::DomTree}; + +use crate::ir::{BasicBlock, BasicBlockId, FunctionBody}; + +#[derive(Debug)] +pub struct PostDomTree { + /// Dummy entry block to calculate post dom tree. + dummy_entry: BasicBlockId, + /// Canonical dummy exit block to calculate post dom tree. All blocks ends + /// with `return` has an edge to this block. + dummy_exit: BasicBlockId, + + /// Dominator tree of reverse control flow graph. + domtree: DomTree, +} + +impl PostDomTree { + pub fn compute(func: &FunctionBody) -> Self { + let mut rcfg = ControlFlowGraph::compute(func); + + let real_entry = rcfg.entry(); + + let dummy_entry = Self::make_dummy_block(); + let dummy_exit = Self::make_dummy_block(); + // Add edges from dummy entry block to real entry block and dummy exit block. + rcfg.add_edge(dummy_entry, real_entry); + rcfg.add_edge(dummy_entry, dummy_exit); + + // Add edges from real exit blocks to dummy exit block. + for exit in std::mem::take(&mut rcfg.exits) { + rcfg.add_edge(exit, dummy_exit); + } + + rcfg.reverse_edge(dummy_exit, vec![dummy_entry]); + let domtree = DomTree::compute(&rcfg); + + Self { + dummy_entry, + dummy_exit, + domtree, + } + } + + pub fn post_idom(&self, block: BasicBlockId) -> PostIDom { + match self.domtree.idom(block).unwrap() { + block if block == self.dummy_entry => PostIDom::DummyEntry, + block if block == self.dummy_exit => PostIDom::DummyExit, + other => PostIDom::Block(other), + } + } + + /// Returns `true` if block is reachable from the exit blocks. + pub fn is_reachable(&self, block: BasicBlockId) -> bool { + self.domtree.is_reachable(block) + } + + fn make_dummy_block() -> BasicBlockId { + let arena_id = DefaultArenaBehavior::::new_arena_id(); + DefaultArenaBehavior::new_id(arena_id, 0) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum PostIDom { + DummyEntry, + DummyExit, + Block(BasicBlockId), +} + +#[cfg(test)] +mod tests { + use super::*; + + use crate::ir::{body_builder::BodyBuilder, FunctionId, SourceInfo, TypeId}; + + fn body_builder() -> BodyBuilder { + BodyBuilder::new(FunctionId(0), SourceInfo::dummy()) + } + + #[test] + fn test_if_else_merge() { + let mut builder = body_builder(); + let then_block = builder.make_block(); + let else_block = builder.make_block(); + let merge_block = builder.make_block(); + + let dummy_ty = TypeId(0); + let v0 = builder.make_imm_from_bool(true, dummy_ty); + builder.branch(v0, then_block, else_block, SourceInfo::dummy()); + + builder.move_to_block(then_block); + builder.jump(merge_block, SourceInfo::dummy()); + + builder.move_to_block(else_block); + builder.jump(merge_block, SourceInfo::dummy()); + + builder.move_to_block(merge_block); + let dummy_value = builder.make_unit(dummy_ty); + builder.ret(dummy_value, SourceInfo::dummy()); + + let func = builder.build(); + + let post_dom_tree = PostDomTree::compute(&func); + let entry_block = func.order.entry_block(); + assert_eq!( + post_dom_tree.post_idom(entry_block), + PostIDom::Block(merge_block) + ); + assert_eq!( + post_dom_tree.post_idom(then_block), + PostIDom::Block(merge_block) + ); + assert_eq!( + post_dom_tree.post_idom(else_block), + PostIDom::Block(merge_block) + ); + assert_eq!(post_dom_tree.post_idom(merge_block), PostIDom::DummyExit); + } + + #[test] + fn test_if_else_return() { + let mut builder = body_builder(); + let then_block = builder.make_block(); + let else_block = builder.make_block(); + let merge_block = builder.make_block(); + + let dummy_ty = TypeId(0); + let dummy_value = builder.make_unit(dummy_ty); + let v0 = builder.make_imm_from_bool(true, dummy_ty); + builder.branch(v0, then_block, else_block, SourceInfo::dummy()); + + builder.move_to_block(then_block); + builder.jump(merge_block, SourceInfo::dummy()); + + builder.move_to_block(else_block); + builder.ret(dummy_value, SourceInfo::dummy()); + + builder.move_to_block(merge_block); + builder.ret(dummy_value, SourceInfo::dummy()); + + let func = builder.build(); + + let post_dom_tree = PostDomTree::compute(&func); + let entry_block = func.order.entry_block(); + assert_eq!(post_dom_tree.post_idom(entry_block), PostIDom::DummyExit,); + assert_eq!( + post_dom_tree.post_idom(then_block), + PostIDom::Block(merge_block), + ); + assert_eq!(post_dom_tree.post_idom(else_block), PostIDom::DummyExit); + assert_eq!(post_dom_tree.post_idom(merge_block), PostIDom::DummyExit); + } + + #[test] + fn test_if_non_else() { + let mut builder = body_builder(); + let then_block = builder.make_block(); + let merge_block = builder.make_block(); + + let dummy_ty = TypeId(0); + let dummy_value = builder.make_unit(dummy_ty); + let v0 = builder.make_imm_from_bool(true, dummy_ty); + builder.branch(v0, then_block, merge_block, SourceInfo::dummy()); + + builder.move_to_block(then_block); + builder.jump(merge_block, SourceInfo::dummy()); + + builder.move_to_block(merge_block); + builder.ret(dummy_value, SourceInfo::dummy()); + + let func = builder.build(); + + let post_dom_tree = PostDomTree::compute(&func); + let entry_block = func.order.entry_block(); + assert_eq!( + post_dom_tree.post_idom(entry_block), + PostIDom::Block(merge_block), + ); + assert_eq!( + post_dom_tree.post_idom(then_block), + PostIDom::Block(merge_block), + ); + assert_eq!(post_dom_tree.post_idom(merge_block), PostIDom::DummyExit); + } + + #[test] + fn test_loop() { + let mut builder = body_builder(); + let block1 = builder.make_block(); + let block2 = builder.make_block(); + let block3 = builder.make_block(); + let block4 = builder.make_block(); + + let dummy_ty = TypeId(0); + let v0 = builder.make_imm_from_bool(true, dummy_ty); + + builder.branch(v0, block1, block2, SourceInfo::dummy()); + + builder.move_to_block(block1); + builder.jump(block3, SourceInfo::dummy()); + + builder.move_to_block(block2); + builder.branch(v0, block3, block4, SourceInfo::dummy()); + + builder.move_to_block(block3); + let dummy_value = builder.make_unit(dummy_ty); + builder.ret(dummy_value, SourceInfo::dummy()); + + builder.move_to_block(block4); + builder.jump(block2, SourceInfo::dummy()); + + let func = builder.build(); + + let post_dom_tree = PostDomTree::compute(&func); + let entry_block = func.order.entry_block(); + assert_eq!( + post_dom_tree.post_idom(entry_block), + PostIDom::Block(block3), + ); + assert_eq!(post_dom_tree.post_idom(block1), PostIDom::Block(block3)); + assert_eq!(post_dom_tree.post_idom(block2), PostIDom::Block(block3)); + assert_eq!(post_dom_tree.post_idom(block3), PostIDom::DummyExit); + assert_eq!(post_dom_tree.post_idom(block4), PostIDom::Block(block2)); + } + + #[test] + fn test_pd_complex() { + let mut builder = body_builder(); + let block1 = builder.make_block(); + let block2 = builder.make_block(); + let block3 = builder.make_block(); + let block4 = builder.make_block(); + let block5 = builder.make_block(); + let block6 = builder.make_block(); + let block7 = builder.make_block(); + + let dummy_ty = TypeId(0); + let v0 = builder.make_imm_from_bool(true, dummy_ty); + + builder.branch(v0, block1, block2, SourceInfo::dummy()); + + builder.move_to_block(block1); + builder.jump(block6, SourceInfo::dummy()); + + builder.move_to_block(block2); + builder.branch(v0, block3, block4, SourceInfo::dummy()); + + builder.move_to_block(block3); + builder.jump(block5, SourceInfo::dummy()); + + builder.move_to_block(block4); + builder.jump(block5, SourceInfo::dummy()); + + builder.move_to_block(block5); + builder.jump(block6, SourceInfo::dummy()); + + builder.move_to_block(block6); + builder.jump(block7, SourceInfo::dummy()); + + builder.move_to_block(block7); + let dummy_value = builder.make_unit(dummy_ty); + builder.ret(dummy_value, SourceInfo::dummy()); + + let func = builder.build(); + + let post_dom_tree = PostDomTree::compute(&func); + let entry_block = func.order.entry_block(); + assert_eq!( + post_dom_tree.post_idom(entry_block), + PostIDom::Block(block6), + ); + assert_eq!(post_dom_tree.post_idom(block1), PostIDom::Block(block6)); + assert_eq!(post_dom_tree.post_idom(block2), PostIDom::Block(block5)); + assert_eq!(post_dom_tree.post_idom(block3), PostIDom::Block(block5)); + assert_eq!(post_dom_tree.post_idom(block4), PostIDom::Block(block5)); + assert_eq!(post_dom_tree.post_idom(block5), PostIDom::Block(block6)); + assert_eq!(post_dom_tree.post_idom(block6), PostIDom::Block(block7)); + assert_eq!(post_dom_tree.post_idom(block7), PostIDom::DummyExit); + } +} From 5c8fb27360a79bbadabff32265eae745531c64a9 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Tue, 1 Mar 2022 12:24:45 +0900 Subject: [PATCH 07/15] Model memory/storage layout --- crates/analyzer/src/namespace/items.rs | 6 + crates/mir/src/db/queries/types.rs | 287 +++++++++++++++++++++++++ 2 files changed, 293 insertions(+) diff --git a/crates/analyzer/src/namespace/items.rs b/crates/analyzer/src/namespace/items.rs index c7ff2237c7..37c5c88616 100644 --- a/crates/analyzer/src/namespace/items.rs +++ b/crates/analyzer/src/namespace/items.rs @@ -750,6 +750,12 @@ impl ModuleId { .iter() .for_each(|id| id.sink_diagnostics(db, sink)); } + + #[doc(hidden)] + // DO NOT USE THIS METHOD except for testing purpose. + pub fn from_raw_internal(raw: u32) -> Self { + Self(raw) + } } #[derive(Debug, PartialEq, Eq, Hash, Clone)] diff --git a/crates/mir/src/db/queries/types.rs b/crates/mir/src/db/queries/types.rs index 3a48eaa687..532afcbae0 100644 --- a/crates/mir/src/db/queries/types.rs +++ b/crates/mir/src/db/queries/types.rs @@ -43,6 +43,18 @@ impl TypeId { } } + pub fn projection_ty_imm(self, db: &dyn MirDb, index: usize) -> TypeId { + debug_assert!(self.is_aggregate(db)); + + match self.data(db).as_ref() { + Type::Array(ArrayDef { elem_ty, .. }) => *elem_ty, + Type::Tuple(def) => def.items[index], + Type::Struct(def) | Type::Contract(def) => def.fields[index].1, + Type::Event(def) => def.fields[index].1, + _ => unreachable!(), + } + } + pub fn index_from_fname(self, db: &dyn MirDb, fname: &str, index_ty: TypeId) -> Immediate { match self.data(db).as_ref() { Type::Tuple(_) => { @@ -105,6 +117,75 @@ impl TypeId { ) } + /// Returns size of the type in bytes. + pub fn size_of(self, db: &dyn MirDb, word_size: usize) -> usize { + match self.data(db).as_ref() { + Type::Bool | Type::I8 | Type::U8 => 1, + Type::I16 | Type::U16 => 2, + Type::I32 | Type::U32 => 4, + Type::I64 | Type::U64 => 8, + Type::I128 | Type::U128 => 16, + Type::I256 | Type::U256 | Type::Map(_) => 32, + Type::Address => 20, + Type::Unit => 0, + + Type::Array(def) => array_elem_size_imp(def, db, word_size) * def.len, + + Type::Tuple(def) => { + let last_idx = def.items.len() - 1; + self.aggregate_elem_offset(db, last_idx, word_size) + + def.items[last_idx].size_of(db, word_size) + } + + Type::Struct(def) | Type::Contract(def) => { + let last_idx = def.fields.len() - 1; + self.aggregate_elem_offset(db, last_idx, word_size) + + def.fields[last_idx].1.size_of(db, word_size) + } + + Type::Event(def) => { + let last_idx = def.fields.len() - 1; + self.aggregate_elem_offset(db, last_idx, word_size) + + def.fields[last_idx].1.size_of(db, word_size) + } + } + } + + pub fn align_of(self, db: &dyn MirDb, word_size: usize) -> usize { + if self.is_primitive(db) { + 1 + } else { + // TODO: Too naive, we could implement more efficient layout for aggregate + // types. + word_size + } + } + + /// Returns an offset of the element of aggregate type. + pub fn aggregate_elem_offset(self, db: &dyn MirDb, elem_idx: usize, word_size: usize) -> usize { + debug_assert!(self.is_aggregate(db)); + + if elem_idx == 0 { + return 0; + } + + if let Type::Array(def) = self.data(db).as_ref() { + return array_elem_size_imp(def, db, word_size) * elem_idx; + } + + let mut offset = self.aggregate_elem_offset(db, elem_idx - 1, word_size) + + self + .projection_ty_imm(db, elem_idx - 1) + .size_of(db, word_size); + + let elem_ty = self.projection_ty_imm(db, elem_idx); + if (offset % word_size + elem_ty.size_of(db, word_size)) > word_size { + offset = round_up(offset, word_size); + } + + round_up(offset, elem_ty.align_of(db, word_size)) + } + pub fn is_aggregate(self, db: &dyn MirDb) -> bool { matches!( self.data(db).as_ref(), @@ -115,6 +196,21 @@ impl TypeId { pub fn is_contract(self, db: &dyn MirDb) -> bool { matches!(self.data(db).as_ref(), Type::Contract(_)) } + + pub fn array_elem_size(self, db: &dyn MirDb, word_size: usize) -> usize { + let data = self.data(db); + if let Type::Array(def) = data.as_ref() { + array_elem_size_imp(def, db, word_size) + } else { + panic!("expected `Array` type; but got {:?}", data.as_ref()) + } + } +} + +fn array_elem_size_imp(arr: &ArrayDef, db: &dyn MirDb, word_size: usize) -> usize { + let elem_ty = arr.elem_ty; + let elem = elem_ty.size_of(db, word_size); + round_up(elem, elem_ty.align_of(db, word_size)) } fn expect_projection_index(value: &Value) -> usize { @@ -123,3 +219,194 @@ fn expect_projection_index(value: &Value) -> usize { _ => panic!("given `value` is not an immediate"), } } + +fn round_up(value: usize, word_size: usize) -> usize { + ((value + word_size - 1) / word_size) * word_size +} + +#[cfg(test)] +mod tests { + use fe_analyzer::namespace::items::ModuleId; + use fe_common::Span; + + use super::*; + use crate::{ + db::{MirDb, NewDb}, + ir::types::StructDef, + }; + + #[test] + fn test_primitive_type_info() { + let db = NewDb::default(); + let i8 = db.mir_intern_type(Type::I8.into()); + let bool = db.mir_intern_type(Type::Bool.into()); + + debug_assert_eq!(i8.size_of(&db, 1), 1); + debug_assert_eq!(i8.size_of(&db, 32), 1); + debug_assert_eq!(i8.align_of(&db, 1), 1); + debug_assert_eq!(i8.align_of(&db, 32), 1); + debug_assert_eq!(bool.size_of(&db, 1), 1); + debug_assert_eq!(bool.size_of(&db, 32), 1); + debug_assert_eq!(i8.align_of(&db, 32), 1); + debug_assert_eq!(i8.align_of(&db, 32), 1); + + let u32 = db.mir_intern_type(Type::U32.into()); + debug_assert_eq!(u32.size_of(&db, 1), 4); + debug_assert_eq!(u32.size_of(&db, 32), 4); + debug_assert_eq!(u32.align_of(&db, 32), 1); + + let address = db.mir_intern_type(Type::Address.into()); + debug_assert_eq!(address.size_of(&db, 1), 20); + debug_assert_eq!(address.size_of(&db, 32), 20); + debug_assert_eq!(address.align_of(&db, 32), 1); + } + + #[test] + fn test_primitive_elem_array_type_info() { + let db = NewDb::default(); + let i32 = db.mir_intern_type(Type::I32.into()); + + let array_len = 10; + let array_def = ArrayDef { + elem_ty: i32, + len: array_len, + }; + let array = db.mir_intern_type(Type::Array(array_def).into()); + + let elem_size = array.array_elem_size(&db, 1); + debug_assert_eq!(elem_size, 4); + debug_assert_eq!(array.array_elem_size(&db, 32), elem_size); + + debug_assert_eq!(array.size_of(&db, 1), elem_size * array_len); + debug_assert_eq!(array.size_of(&db, 32), elem_size * array_len); + debug_assert_eq!(array.align_of(&db, 1), 1); + debug_assert_eq!(array.align_of(&db, 32), 32); + + debug_assert_eq!(array.aggregate_elem_offset(&db, 3, 32), elem_size * 3); + debug_assert_eq!(array.aggregate_elem_offset(&db, 9, 1), elem_size * 9); + } + + #[test] + fn test_aggregate_elem_array_type_info() { + let db = NewDb::default(); + let i8 = db.mir_intern_type(Type::I8.into()); + let i64 = db.mir_intern_type(Type::I64.into()); + let i128 = db.mir_intern_type(Type::I128.into()); + + let fields = vec![ + ("".into(), i64), + ("".into(), i64), + ("".into(), i8), + ("".into(), i128), + ("".into(), i8), + ]; + + let struct_def = StructDef { + name: "".into(), + fields, + span: Span::dummy(), + module_id: ModuleId::from_raw_internal(0), + }; + let aggregate = db.mir_intern_type(Type::Struct(struct_def).into()); + + let array_len = 10; + let array_def = ArrayDef { + elem_ty: aggregate, + len: array_len, + }; + let array = db.mir_intern_type(Type::Array(array_def).into()); + + debug_assert_eq!(array.array_elem_size(&db, 1), 34); + debug_assert_eq!(array.array_elem_size(&db, 32), 64); + + debug_assert_eq!(array.size_of(&db, 1), 34 * array_len); + debug_assert_eq!(array.size_of(&db, 32), 64 * array_len); + + debug_assert_eq!(array.align_of(&db, 1), 1); + debug_assert_eq!(array.align_of(&db, 32), 32); + + debug_assert_eq!(array.aggregate_elem_offset(&db, 3, 1), 102); + debug_assert_eq!(array.aggregate_elem_offset(&db, 3, 32), 192); + } + + #[test] + fn test_primitive_elem_aggregate_type_info() { + let db = NewDb::default(); + let i8 = db.mir_intern_type(Type::I8.into()); + let i64 = db.mir_intern_type(Type::I64.into()); + let i128 = db.mir_intern_type(Type::I128.into()); + + let fields = vec![ + ("".into(), i64), + ("".into(), i64), + ("".into(), i8), + ("".into(), i128), + ("".into(), i8), + ]; + + let struct_def = StructDef { + name: "".into(), + fields, + span: Span::dummy(), + module_id: ModuleId::from_raw_internal(0), + }; + let aggregate = db.mir_intern_type(Type::Struct(struct_def).into()); + + debug_assert_eq!(aggregate.size_of(&db, 1), 34); + debug_assert_eq!(aggregate.size_of(&db, 32), 49); + + debug_assert_eq!(aggregate.align_of(&db, 1), 1); + debug_assert_eq!(aggregate.align_of(&db, 32), 32); + + debug_assert_eq!(aggregate.aggregate_elem_offset(&db, 0, 1), 0); + debug_assert_eq!(aggregate.aggregate_elem_offset(&db, 0, 32), 0); + debug_assert_eq!(aggregate.aggregate_elem_offset(&db, 3, 1), 17); + debug_assert_eq!(aggregate.aggregate_elem_offset(&db, 3, 32), 32); + debug_assert_eq!(aggregate.aggregate_elem_offset(&db, 4, 1), 33); + debug_assert_eq!(aggregate.aggregate_elem_offset(&db, 4, 32), 48); + } + + #[test] + fn test_aggregate_elem_aggregate_type_info() { + let db = NewDb::default(); + let i8 = db.mir_intern_type(Type::I8.into()); + let i64 = db.mir_intern_type(Type::I64.into()); + let i128 = db.mir_intern_type(Type::I128.into()); + + let fields_inner = vec![ + ("".into(), i64), + ("".into(), i64), + ("".into(), i8), + ("".into(), i128), + ("".into(), i8), + ]; + + let struct_def_inner = StructDef { + name: "".into(), + fields: fields_inner, + span: Span::dummy(), + module_id: ModuleId::from_raw_internal(0), + }; + let aggregate_inner = db.mir_intern_type(Type::Struct(struct_def_inner).into()); + + let fields = vec![("".into(), i8), ("".into(), aggregate_inner)]; + let struct_def = StructDef { + name: "".into(), + fields, + span: Span::dummy(), + module_id: ModuleId::from_raw_internal(0), + }; + let aggregate = db.mir_intern_type(Type::Struct(struct_def).into()); + + debug_assert_eq!(aggregate.size_of(&db, 1), 35); + debug_assert_eq!(aggregate.size_of(&db, 32), 81); + + debug_assert_eq!(aggregate.align_of(&db, 1), 1); + debug_assert_eq!(aggregate.align_of(&db, 32), 32); + + debug_assert_eq!(aggregate.aggregate_elem_offset(&db, 0, 1), 0); + debug_assert_eq!(aggregate.aggregate_elem_offset(&db, 0, 32), 0); + debug_assert_eq!(aggregate.aggregate_elem_offset(&db, 1, 1), 1); + debug_assert_eq!(aggregate.aggregate_elem_offset(&db, 1, 32), 32); + } +} From 904bcbb75b20857a7ed764117bc42c12719dcd1e Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Wed, 2 Mar 2022 19:24:16 +0900 Subject: [PATCH 08/15] Allow to chain aggregate access for more effective element ptr calculation --- crates/mir/src/ir/body_builder.rs | 4 +- crates/mir/src/ir/inst.rs | 2 +- crates/mir/src/lower/function.rs | 59 +++++++++++++++++++++-------- crates/mir/src/pretty_print/inst.rs | 11 ++++-- 4 files changed, 54 insertions(+), 22 deletions(-) diff --git a/crates/mir/src/ir/body_builder.rs b/crates/mir/src/ir/body_builder.rs index 0017901d41..229e5c8897 100644 --- a/crates/mir/src/ir/body_builder.rs +++ b/crates/mir/src/ir/body_builder.rs @@ -170,11 +170,11 @@ impl BodyBuilder { pub fn aggregate_access( &mut self, value: ValueId, - index: ValueId, + indices: Vec, result_ty: TypeId, source: SourceInfo, ) -> ValueId { - let kind = InstKind::AggregateAccess { value, index }; + let kind = InstKind::AggregateAccess { value, indices }; let inst = Inst::new(kind, source); self.insert_inst(inst, Some(result_ty)).unwrap() } diff --git a/crates/mir/src/ir/inst.rs b/crates/mir/src/ir/inst.rs index 2958c0d242..8f3bfe5565 100644 --- a/crates/mir/src/ir/inst.rs +++ b/crates/mir/src/ir/inst.rs @@ -61,7 +61,7 @@ pub enum InstKind { /// `foo.y` is lowered into `AggregateAccess(foo, [1])' for example. AggregateAccess { value: ValueId, - index: ValueId, + indices: Vec, }, MapAccess { diff --git a/crates/mir/src/lower/function.rs b/crates/mir/src/lower/function.rs index e4968d1a08..a8f6d09317 100644 --- a/crates/mir/src/lower/function.rs +++ b/crates/mir/src/lower/function.rs @@ -140,7 +140,9 @@ impl<'db, 'a> BodyLowerHelper<'db, 'a> { for (local, indices) in locals { for index in indices { let ty = ty.projection_ty(self.db, self.builder.value_data(rhs)); - rhs = self.builder.aggregate_access(rhs, index, ty, expr.into()); + rhs = self + .builder + .aggregate_access(rhs, vec![index], ty, expr.into()); } self.builder.assign(local, rhs, stmt.into()); } @@ -468,7 +470,7 @@ impl<'db, 'a> BodyLowerHelper<'db, 'a> { // loop_variable = array[ioop_idx] let iter_elem = self.builder - .aggregate_access(iter, loop_idx, iter_elem_ty, SourceInfo::dummy()); + .aggregate_access(iter, vec![loop_idx], iter_elem_ty, SourceInfo::dummy()); self.builder .assign(loop_value, iter_elem, SourceInfo::dummy()); // loop_idx+= 1 @@ -547,25 +549,27 @@ impl<'db, 'a> BodyLowerHelper<'db, 'a> { self.lower_comp_op(op.kind, lhs, rhs, ty, expr.into()) } - ast::Expr::Attribute { value, attr } => { - let value = self.lower_expr(value); - let value_ty = self.builder.value_ty(value); - let index = value_ty.index_from_fname(self.db, &attr.kind, self.u256_ty()); - let index = self.builder.make_value(index); + ast::Expr::Attribute { .. } => { + let mut indices = vec![]; + let value = self.lower_aggregate_access(expr, &mut indices); let ty = self.expr_ty(expr); - self.builder.aggregate_access(value, index, ty, expr.into()) + self.builder + .aggregate_access(value, indices, ty, expr.into()) } ast::Expr::Subscript { value, index } => { - let value = self.lower_expr(value); - let index = self.lower_expr(index); - let value_ty = self.builder.value_ty(value); - if value_ty.is_aggregate(self.db) { - self.builder - .aggregate_access(value, index, self.expr_ty(expr), expr.into()) + let result_ty = self.expr_ty(expr); + + if !self.expr_ty(value).is_aggregate(self.db) { + // Indices is empty is the `expr` is map + let value = self.lower_expr(value); + let key = self.lower_expr(index); + self.builder.map_access(value, key, result_ty, expr.into()) } else { + let mut indices = vec![]; + let value = self.lower_aggregate_access(expr, &mut indices); self.builder - .map_access(value, index, self.expr_ty(expr), expr.into()) + .aggregate_access(value, indices, result_ty, expr.into()) } } @@ -797,6 +801,31 @@ impl<'db, 'a> BodyLowerHelper<'db, 'a> { } } + fn lower_aggregate_access( + &mut self, + expr: &Node, + indices: &mut Vec, + ) -> ValueId { + match &expr.kind { + ast::Expr::Attribute { value, attr } => { + let index = + self.expr_ty(value) + .index_from_fname(self.db, &attr.kind, self.u256_ty()); + let value = self.lower_aggregate_access(value, indices); + indices.push(self.builder.make_value(index)); + value + } + + ast::Expr::Subscript { value, index } if self.expr_ty(value).is_aggregate(self.db) => { + let value = self.lower_aggregate_access(value, indices); + indices.push(self.lower_expr(index)); + value + } + + _ => self.lower_expr(expr), + } + } + fn make_unit(&mut self) -> ValueId { let unit_ty = analyzer_types::Type::Base(analyzer_types::Base::Unit); let unit_ty = self.db.mir_lowered_type(unit_ty); diff --git a/crates/mir/src/pretty_print/inst.rs b/crates/mir/src/pretty_print/inst.rs index a6be2219b2..2e3697d0c8 100644 --- a/crates/mir/src/pretty_print/inst.rs +++ b/crates/mir/src/pretty_print/inst.rs @@ -73,11 +73,14 @@ impl PrettyPrint for InstId { write!(w, "}}") } - InstKind::AggregateAccess { value, index } => { + InstKind::AggregateAccess { value, indices } => { value.pretty_print(db, store, w)?; - write!(w, ".<")?; - index.pretty_print(db, store, w)?; - write!(w, ">") + for index in indices { + write!(w, ".<")?; + index.pretty_print(db, store, w)?; + write!(w, ">")? + } + Ok(()) } InstKind::MapAccess { value, key } => { From 7e0cfbe109765eb7fd660bfb325a9b69c1a9ccae Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Wed, 2 Mar 2022 20:41:22 +0900 Subject: [PATCH 09/15] Add `DomTree`, `PostDomTree` and `LoopTree` to integration test --- crates/mir/src/analysis/mod.rs | 3 +++ crates/mir/tests/lowering.rs | 7 +++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/crates/mir/src/analysis/mod.rs b/crates/mir/src/analysis/mod.rs index 9c2c2bd5e8..b895cc02a7 100644 --- a/crates/mir/src/analysis/mod.rs +++ b/crates/mir/src/analysis/mod.rs @@ -4,3 +4,6 @@ pub mod loop_tree; pub mod post_domtree; pub use cfg::ControlFlowGraph; +pub use domtree::DomTree; +pub use loop_tree::LoopTree; +pub use post_domtree::PostDomTree; diff --git a/crates/mir/tests/lowering.rs b/crates/mir/tests/lowering.rs index ced23bba07..6e1781aa8e 100644 --- a/crates/mir/tests/lowering.rs +++ b/crates/mir/tests/lowering.rs @@ -1,7 +1,7 @@ use fe_analyzer::namespace::items::{IngotId, ModuleId}; use fe_common::{db::Upcast, files::Utf8Path}; use fe_mir::{ - analysis::ControlFlowGraph, + analysis::{ControlFlowGraph, DomTree, LoopTree, PostDomTree}, db::{MirDb, NewDb}, }; @@ -42,7 +42,10 @@ fn mir_lower_std_lib() { for &module in std_ingot.all_modules(db.upcast()).iter() { for func in db.mir_lower_module_all_functions(module).iter() { let body = func.body(&db); - ControlFlowGraph::compute(&body); + let cfg = ControlFlowGraph::compute(&body); + let domtree = DomTree::compute(&cfg); + LoopTree::compute(&cfg, &domtree); + PostDomTree::compute(&body); } } } From f4c5e3fac7734f22e87ec5eb137334372a1d7505 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Thu, 3 Mar 2022 15:31:21 +0900 Subject: [PATCH 10/15] Add function legalizer --- Cargo.lock | 9 +++ crates/codegen/Cargo.toml | 10 ++++ crates/codegen/src/abi.rs | 3 + crates/codegen/src/lib.rs | 2 + crates/codegen/src/yul/legalize/body.rs | 63 ++++++++++++++++++++ crates/codegen/src/yul/legalize/mod.rs | 5 ++ crates/codegen/src/yul/legalize/signature.rs | 6 ++ crates/codegen/src/yul/mod.rs | 1 + crates/fe/src/main.rs | 2 +- crates/mir/src/analysis/cfg.rs | 2 +- crates/mir/src/analysis/domtree.rs | 6 +- crates/mir/src/analysis/post_domtree.rs | 10 ++-- crates/mir/src/db/queries/function.rs | 8 +-- crates/mir/src/db/queries/types.rs | 63 ++++++++++++-------- crates/mir/src/graphviz/function.rs | 8 ++- crates/mir/src/ir/body_builder.rs | 4 +- crates/mir/src/ir/body_cursor.rs | 19 +++--- crates/mir/src/ir/body_order.rs | 2 +- crates/mir/src/ir/function.rs | 23 ++++--- crates/mir/src/ir/inst.rs | 11 +++- crates/mir/src/lower/function.rs | 4 +- crates/mir/src/pretty_print/inst.rs | 12 +++- 22 files changed, 207 insertions(+), 66 deletions(-) create mode 100644 crates/codegen/Cargo.toml create mode 100644 crates/codegen/src/abi.rs create mode 100644 crates/codegen/src/lib.rs create mode 100644 crates/codegen/src/yul/legalize/body.rs create mode 100644 crates/codegen/src/yul/legalize/mod.rs create mode 100644 crates/codegen/src/yul/legalize/signature.rs create mode 100644 crates/codegen/src/yul/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 978204fd73..202bf79d44 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -593,6 +593,15 @@ dependencies = [ "wasm-bindgen-test", ] +[[package]] +name = "fe-codegen" +version = "0.14.0-alpha" +dependencies = [ + "fe-abi", + "fe-common", + "fe-mir", +] + [[package]] name = "fe-common" version = "0.16.0-alpha" diff --git a/crates/codegen/Cargo.toml b/crates/codegen/Cargo.toml new file mode 100644 index 0000000000..dd7e9c1c42 --- /dev/null +++ b/crates/codegen/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "fe-codegen" +version = "0.14.0-alpha" +authors = ["The Fe Developers "] +edition = "2021" + +[dependencies] +fe-mir = { path = "../mir", version = "^0.14.0-alpha" } +fe-common = { path = "../common", version = "^0.14.0-alpha" } +fe-abi= { path = "../abi", version = "^0.14.0-alpha" } \ No newline at end of file diff --git a/crates/codegen/src/abi.rs b/crates/codegen/src/abi.rs new file mode 100644 index 0000000000..6945184c23 --- /dev/null +++ b/crates/codegen/src/abi.rs @@ -0,0 +1,3 @@ +pub enum AbiType { + SolidityV1, +} diff --git a/crates/codegen/src/lib.rs b/crates/codegen/src/lib.rs new file mode 100644 index 0000000000..d55fae34cc --- /dev/null +++ b/crates/codegen/src/lib.rs @@ -0,0 +1,2 @@ +pub mod abi; +pub mod yul; diff --git a/crates/codegen/src/yul/legalize/body.rs b/crates/codegen/src/yul/legalize/body.rs new file mode 100644 index 0000000000..69292db8a2 --- /dev/null +++ b/crates/codegen/src/yul/legalize/body.rs @@ -0,0 +1,63 @@ +use fe_mir::{ + db::MirDb, + ir::{ + body_cursor::{BodyCursor, CursorLocation}, + inst::InstKind, + FunctionBody, Inst, InstId, + }, +}; + +pub fn legalize_func_body(db: &dyn MirDb, body: &mut FunctionBody) { + let mut cursor = BodyCursor::new_at_entry(body); + + loop { + match cursor.loc() { + CursorLocation::BlockTop(_) | CursorLocation::BlockBottom(_) => cursor.proceed(), + CursorLocation::Inst(inst) => { + legalize_inst(db, cursor.body_mut(), inst); + cursor.proceed(); + } + CursorLocation::NoWhere => break, + } + } +} + +fn legalize_inst(db: &dyn MirDb, body: &mut FunctionBody, inst: InstId) { + legalize_call(db, body, inst); + legalize_return(db, body, inst); + legalize_inst_result(db, body, inst); +} + +// Remove zero-sized arguments from call instruction. +fn legalize_call(db: &dyn MirDb, body: &mut FunctionBody, inst: InstId) { + let dummy_inst = Inst::nop(); + let mut call_inst = body.store.replace_inst(inst, dummy_inst); + if let InstKind::Call { args, .. } = &mut call_inst.kind { + args.retain(|arg| !body.store.value_ty(*arg).is_zero_sized(db)); + } + + body.store.replace_inst(inst, call_inst); +} + +// Remove return argument if its type is zero sized. +fn legalize_return(db: &dyn MirDb, body: &mut FunctionBody, inst: InstId) { + if let Inst { + kind: InstKind::Return { arg: Some(arg) }, + source, + } = body.store.inst_data(inst) + { + if body.store.value_ty(*arg).is_zero_sized(db) { + let ret_inst = Inst::new(InstKind::Return { arg: None }, source.clone()); + body.store.replace_inst(inst, ret_inst); + } + } +} + +/// Remove instruction result if its type is zero sized. +fn legalize_inst_result(db: &dyn MirDb, body: &mut FunctionBody, inst: InstId) { + if let Some(result) = body.store.inst_result(inst) { + if body.store.value_ty(result).is_zero_sized(db) { + body.store.remove_inst_result(inst) + } + } +} diff --git a/crates/codegen/src/yul/legalize/mod.rs b/crates/codegen/src/yul/legalize/mod.rs new file mode 100644 index 0000000000..450e825562 --- /dev/null +++ b/crates/codegen/src/yul/legalize/mod.rs @@ -0,0 +1,5 @@ +mod body; +mod signature; + +pub use body::legalize_func_body; +pub use signature::legalize_func_signature; diff --git a/crates/codegen/src/yul/legalize/signature.rs b/crates/codegen/src/yul/legalize/signature.rs new file mode 100644 index 0000000000..ab7755949f --- /dev/null +++ b/crates/codegen/src/yul/legalize/signature.rs @@ -0,0 +1,6 @@ +use fe_mir::{db::MirDb, ir::FunctionSignature}; + +pub fn legalize_func_signature(_db: &dyn MirDb, _sig: &mut FunctionSignature) { + // TODO: Remove zero sized types from arguments, also remove return type if + // it's zero-sized +} diff --git a/crates/codegen/src/yul/mod.rs b/crates/codegen/src/yul/mod.rs new file mode 100644 index 0000000000..47c4684630 --- /dev/null +++ b/crates/codegen/src/yul/mod.rs @@ -0,0 +1 @@ +pub mod legalize; diff --git a/crates/fe/src/main.rs b/crates/fe/src/main.rs index dd33587dde..75c4608700 100644 --- a/crates/fe/src/main.rs +++ b/crates/fe/src/main.rs @@ -53,7 +53,7 @@ pub fn main() { .long("emit") .help("Comma separated compile targets e.g. -e=bytecode,yul") .possible_values(&["abi", "bytecode", "ast", "tokens", "yul", "loweredAst"]) - .default_value("abi,bytecode") + .default_value("abi,bytecode,yul") .use_delimiter(true) .takes_value(true), ) diff --git a/crates/mir/src/analysis/cfg.rs b/crates/mir/src/analysis/cfg.rs index a9141a3454..4f70d9c322 100644 --- a/crates/mir/src/analysis/cfg.rs +++ b/crates/mir/src/analysis/cfg.rs @@ -11,7 +11,7 @@ pub struct ControlFlowGraph { impl ControlFlowGraph { pub fn compute(func: &FunctionBody) -> Self { - let entry = func.order.entry_block(); + let entry = func.order.entry(); let mut cfg = Self { entry, blocks: FxHashMap::default(), diff --git a/crates/mir/src/analysis/domtree.rs b/crates/mir/src/analysis/domtree.rs index 0e51742c02..77220401f1 100644 --- a/crates/mir/src/analysis/domtree.rs +++ b/crates/mir/src/analysis/domtree.rs @@ -162,7 +162,7 @@ mod tests { let func = builder.build(); let dom_tree = calc_dom(&func); - let entry_block = func.order.entry_block(); + let entry_block = func.order.entry(); assert_eq!(dom_tree.idom(entry_block), None); assert_eq!(dom_tree.idom(then_block), Some(entry_block)); assert_eq!(dom_tree.idom(else_block), Some(entry_block)); @@ -198,7 +198,7 @@ mod tests { let func = builder.build(); let dom_tree = calc_dom(&func); - let entry_block = func.order.entry_block(); + let entry_block = func.order.entry(); assert_eq!(dom_tree.idom(entry_block), None); assert_eq!(dom_tree.idom(block1), Some(entry_block)); assert_eq!(dom_tree.idom(block2), Some(entry_block)); @@ -268,7 +268,7 @@ mod tests { let func = builder.build(); let dom_tree = calc_dom(&func); - let entry_block = func.order.entry_block(); + let entry_block = func.order.entry(); assert_eq!(dom_tree.idom(entry_block), None); assert_eq!(dom_tree.idom(block1), Some(entry_block)); assert_eq!(dom_tree.idom(block2), Some(entry_block)); diff --git a/crates/mir/src/analysis/post_domtree.rs b/crates/mir/src/analysis/post_domtree.rs index 0a7f4cc334..ba33aab5f0 100644 --- a/crates/mir/src/analysis/post_domtree.rs +++ b/crates/mir/src/analysis/post_domtree.rs @@ -105,7 +105,7 @@ mod tests { let func = builder.build(); let post_dom_tree = PostDomTree::compute(&func); - let entry_block = func.order.entry_block(); + let entry_block = func.order.entry(); assert_eq!( post_dom_tree.post_idom(entry_block), PostIDom::Block(merge_block) @@ -145,7 +145,7 @@ mod tests { let func = builder.build(); let post_dom_tree = PostDomTree::compute(&func); - let entry_block = func.order.entry_block(); + let entry_block = func.order.entry(); assert_eq!(post_dom_tree.post_idom(entry_block), PostIDom::DummyExit,); assert_eq!( post_dom_tree.post_idom(then_block), @@ -175,7 +175,7 @@ mod tests { let func = builder.build(); let post_dom_tree = PostDomTree::compute(&func); - let entry_block = func.order.entry_block(); + let entry_block = func.order.entry(); assert_eq!( post_dom_tree.post_idom(entry_block), PostIDom::Block(merge_block), @@ -216,7 +216,7 @@ mod tests { let func = builder.build(); let post_dom_tree = PostDomTree::compute(&func); - let entry_block = func.order.entry_block(); + let entry_block = func.order.entry(); assert_eq!( post_dom_tree.post_idom(entry_block), PostIDom::Block(block3), @@ -268,7 +268,7 @@ mod tests { let func = builder.build(); let post_dom_tree = PostDomTree::compute(&func); - let entry_block = func.order.entry_block(); + let entry_block = func.order.entry(); assert_eq!( post_dom_tree.post_idom(entry_block), PostIDom::Block(block6), diff --git a/crates/mir/src/db/queries/function.rs b/crates/mir/src/db/queries/function.rs index 706d694608..4a5d56baed 100644 --- a/crates/mir/src/db/queries/function.rs +++ b/crates/mir/src/db/queries/function.rs @@ -21,16 +21,16 @@ pub fn mir_lowered_func_body(db: &dyn MirDb, func: ir::FunctionId) -> Rc Rc { + pub fn signature(self, db: &dyn MirDb) -> Rc { db.lookup_mir_intern_function(self) } - pub fn return_type(self, db: &dyn MirDb) -> TypeId { - self.data(db).return_type + pub fn return_type(self, db: &dyn MirDb) -> Option { + self.signature(db).return_type } pub fn analyzer_func(self, db: &dyn MirDb) -> analyzer_items::FunctionId { - self.data(db).analyzer_func_id + self.signature(db).analyzer_func_id } pub fn body(self, db: &dyn MirDb) -> Rc { diff --git a/crates/mir/src/db/queries/types.rs b/crates/mir/src/db/queries/types.rs index 532afcbae0..15365acc3e 100644 --- a/crates/mir/src/db/queries/types.rs +++ b/crates/mir/src/db/queries/types.rs @@ -118,7 +118,7 @@ impl TypeId { } /// Returns size of the type in bytes. - pub fn size_of(self, db: &dyn MirDb, word_size: usize) -> usize { + pub fn size_of(self, db: &dyn MirDb, slot_size: usize) -> usize { match self.data(db).as_ref() { Type::Bool | Type::I8 | Type::U8 => 1, Type::I16 | Type::U16 => 2, @@ -129,40 +129,55 @@ impl TypeId { Type::Address => 20, Type::Unit => 0, - Type::Array(def) => array_elem_size_imp(def, db, word_size) * def.len, + Type::Array(def) => array_elem_size_imp(def, db, slot_size) * def.len, Type::Tuple(def) => { + if def.items.is_empty() { + return 0; + } let last_idx = def.items.len() - 1; - self.aggregate_elem_offset(db, last_idx, word_size) - + def.items[last_idx].size_of(db, word_size) + self.aggregate_elem_offset(db, last_idx, slot_size) + + def.items[last_idx].size_of(db, slot_size) } Type::Struct(def) | Type::Contract(def) => { + if def.fields.is_empty() { + return 0; + } let last_idx = def.fields.len() - 1; - self.aggregate_elem_offset(db, last_idx, word_size) - + def.fields[last_idx].1.size_of(db, word_size) + self.aggregate_elem_offset(db, last_idx, slot_size) + + def.fields[last_idx].1.size_of(db, slot_size) } Type::Event(def) => { + if def.fields.is_empty() { + return 0; + } let last_idx = def.fields.len() - 1; - self.aggregate_elem_offset(db, last_idx, word_size) - + def.fields[last_idx].1.size_of(db, word_size) + self.aggregate_elem_offset(db, last_idx, slot_size) + + def.fields[last_idx].1.size_of(db, slot_size) } } } - pub fn align_of(self, db: &dyn MirDb, word_size: usize) -> usize { + pub fn is_zero_sized(self, db: &dyn MirDb) -> bool { + // It's ok to use 1 as a slot size because slot size doesn't affect whether a + // type is zero sized or not. + self.size_of(db, 1) == 0 + } + + pub fn align_of(self, db: &dyn MirDb, slot_size: usize) -> usize { if self.is_primitive(db) { 1 } else { // TODO: Too naive, we could implement more efficient layout for aggregate // types. - word_size + slot_size } } /// Returns an offset of the element of aggregate type. - pub fn aggregate_elem_offset(self, db: &dyn MirDb, elem_idx: usize, word_size: usize) -> usize { + pub fn aggregate_elem_offset(self, db: &dyn MirDb, elem_idx: usize, slot_size: usize) -> usize { debug_assert!(self.is_aggregate(db)); if elem_idx == 0 { @@ -170,20 +185,20 @@ impl TypeId { } if let Type::Array(def) = self.data(db).as_ref() { - return array_elem_size_imp(def, db, word_size) * elem_idx; + return array_elem_size_imp(def, db, slot_size) * elem_idx; } - let mut offset = self.aggregate_elem_offset(db, elem_idx - 1, word_size) + let mut offset = self.aggregate_elem_offset(db, elem_idx - 1, slot_size) + self .projection_ty_imm(db, elem_idx - 1) - .size_of(db, word_size); + .size_of(db, slot_size); let elem_ty = self.projection_ty_imm(db, elem_idx); - if (offset % word_size + elem_ty.size_of(db, word_size)) > word_size { - offset = round_up(offset, word_size); + if (offset % slot_size + elem_ty.size_of(db, slot_size)) > slot_size { + offset = round_up(offset, slot_size); } - round_up(offset, elem_ty.align_of(db, word_size)) + round_up(offset, elem_ty.align_of(db, slot_size)) } pub fn is_aggregate(self, db: &dyn MirDb) -> bool { @@ -197,20 +212,20 @@ impl TypeId { matches!(self.data(db).as_ref(), Type::Contract(_)) } - pub fn array_elem_size(self, db: &dyn MirDb, word_size: usize) -> usize { + pub fn array_elem_size(self, db: &dyn MirDb, slot_size: usize) -> usize { let data = self.data(db); if let Type::Array(def) = data.as_ref() { - array_elem_size_imp(def, db, word_size) + array_elem_size_imp(def, db, slot_size) } else { panic!("expected `Array` type; but got {:?}", data.as_ref()) } } } -fn array_elem_size_imp(arr: &ArrayDef, db: &dyn MirDb, word_size: usize) -> usize { +fn array_elem_size_imp(arr: &ArrayDef, db: &dyn MirDb, slot_size: usize) -> usize { let elem_ty = arr.elem_ty; - let elem = elem_ty.size_of(db, word_size); - round_up(elem, elem_ty.align_of(db, word_size)) + let elem = elem_ty.size_of(db, slot_size); + round_up(elem, elem_ty.align_of(db, slot_size)) } fn expect_projection_index(value: &Value) -> usize { @@ -220,8 +235,8 @@ fn expect_projection_index(value: &Value) -> usize { } } -fn round_up(value: usize, word_size: usize) -> usize { - ((value + word_size - 1) / word_size) * word_size +fn round_up(value: usize, slot_size: usize) -> usize { + ((value + slot_size - 1) / slot_size) * slot_size } #[cfg(test)] diff --git a/crates/mir/src/graphviz/function.rs b/crates/mir/src/graphviz/function.rs index 4cac45f633..4f2636c16a 100644 --- a/crates/mir/src/graphviz/function.rs +++ b/crates/mir/src/graphviz/function.rs @@ -50,7 +50,7 @@ impl FunctionNode { fn signature(self, db: &dyn MirDb) -> String { let body = self.func.body(db); - let sig_data = self.func.data(db); + let sig_data = self.func.signature(db); let mut sig = format!("fn {}(", self.func.name_with_class(db)); let params = &sig_data.params; @@ -67,8 +67,10 @@ impl FunctionNode { write!(sig, ")").unwrap(); let ret_ty = self.func.return_type(db); - write!(sig, " -> ").unwrap(); - ret_ty.pretty_print(db, &body.store, &mut sig).unwrap(); + if let Some(ret_ty) = ret_ty { + write!(sig, " -> ").unwrap(); + ret_ty.pretty_print(db, &body.store, &mut sig).unwrap(); + } dot2::escape_html(&sig) } diff --git a/crates/mir/src/ir/body_builder.rs b/crates/mir/src/ir/body_builder.rs index 229e5c8897..0a18d542e1 100644 --- a/crates/mir/src/ir/body_builder.rs +++ b/crates/mir/src/ir/body_builder.rs @@ -47,7 +47,7 @@ macro_rules! impl_binary_inst { impl BodyBuilder { pub fn new(fid: FunctionId, source: SourceInfo) -> Self { let body = FunctionBody::new(fid, source); - let entry_block = body.order.entry_block(); + let entry_block = body.order.entry(); Self { body, loc: CursorLocation::BlockTop(entry_block), @@ -303,7 +303,7 @@ impl BodyBuilder { } pub fn ret(&mut self, arg: ValueId, source: SourceInfo) { - let kind = InstKind::Return { arg }; + let kind = InstKind::Return { arg: arg.into() }; let inst = Inst::new(kind, source); self.insert_inst(inst, None); } diff --git a/crates/mir/src/ir/body_cursor.rs b/crates/mir/src/ir/body_cursor.rs index 6e347a071f..70615d697d 100644 --- a/crates/mir/src/ir/body_cursor.rs +++ b/crates/mir/src/ir/body_cursor.rs @@ -22,6 +22,14 @@ impl<'a> BodyCursor<'a> { pub fn new(body: &'a mut FunctionBody, loc: CursorLocation) -> Self { Self { body, loc } } + + pub fn new_at_entry(body: &'a mut FunctionBody) -> Self { + let entry = body.order.entry(); + Self { + body, + loc: CursorLocation::BlockTop(entry), + } + } pub fn set_loc(&mut self, loc: CursorLocation) { self.loc = loc; } @@ -102,7 +110,7 @@ impl<'a> BodyCursor<'a> { /// Sets a cursor to an entry block. pub fn set_to_entry(&mut self) { - let entry_bb = self.body().order.entry_block(); + let entry_bb = self.body().order.entry(); let loc = CursorLocation::BlockTop(entry_bb); self.set_loc(loc); } @@ -127,15 +135,6 @@ impl<'a> BodyCursor<'a> { inst } - /// Replace a current pointed [`Inst`] with `new_inst` in-place. - /// - /// # Panics - /// Panics if a cursor doesn't point [`CursorLocation::Inst`]. - pub fn replace(&mut self, new_inst: Inst) { - let inst = self.expect_inst(); - self.body.store.replace_inst(inst, new_inst); - } - /// Remove a current pointed [`Inst`] from a function body. A cursor /// proceeds to a next inst. /// diff --git a/crates/mir/src/ir/body_order.rs b/crates/mir/src/ir/body_order.rs index 7c8b85e1e3..9028296e63 100644 --- a/crates/mir/src/ir/body_order.rs +++ b/crates/mir/src/ir/body_order.rs @@ -25,7 +25,7 @@ impl BodyOrder { } /// Returns an entry block of a function body. - pub fn entry_block(&self) -> BasicBlockId { + pub fn entry(&self) -> BasicBlockId { self.entry_block } diff --git a/crates/mir/src/ir/function.rs b/crates/mir/src/ir/function.rs index 569e6a7899..f4f02d8608 100644 --- a/crates/mir/src/ir/function.rs +++ b/crates/mir/src/ir/function.rs @@ -17,7 +17,7 @@ use super::{ #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct FunctionSignature { pub params: Vec, - pub return_type: TypeId, + pub return_type: Option, pub module_id: analyzer_items::ModuleId, pub analyzer_func_id: analyzer_items::FunctionId, pub linkage: Linkage, @@ -51,7 +51,7 @@ pub enum Linkage { /// A function body, which is not stored in salsa db to enable in-place /// transformation. -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct FunctionBody { pub fid: FunctionId, @@ -78,7 +78,7 @@ impl FunctionBody { /// A collection of basic block, instructions and values appear in a function /// body. -#[derive(Default, Debug, PartialEq, Eq)] +#[derive(Default, Debug, Clone, PartialEq, Eq)] pub struct BodyDataStore { /// Instructions appear in a function body. insts: Arena, @@ -110,6 +110,15 @@ impl BodyDataStore { &self.insts[inst] } + pub fn inst_data_mut(&mut self, inst: InstId) -> &mut Inst { + &mut self.insts[inst] + } + + pub fn replace_inst(&mut self, inst: InstId, new: Inst) -> Inst { + let old = &mut self.insts[inst]; + std::mem::replace(old, new) + } + pub fn store_value(&mut self, value: Value) -> ValueId { match value { Value::Immediate(imm) => self.store_immediate(imm), @@ -159,12 +168,12 @@ impl BodyDataStore { self.inst_results.get(&inst).copied() } - pub fn value_ty(&self, vid: ValueId) -> TypeId { - self.values[vid].ty() + pub fn remove_inst_result(&mut self, inst: InstId) { + self.inst_results.remove(&inst); } - pub fn replace_inst(&mut self, inst: InstId, new: Inst) { - self.insts[inst] = new; + pub fn value_ty(&self, vid: ValueId) -> TypeId { + self.values[vid].ty() } pub fn map_result(&mut self, inst: InstId, result: ValueId) { diff --git a/crates/mir/src/ir/inst.rs b/crates/mir/src/ir/inst.rs index 8f3bfe5565..ea8b4520aa 100644 --- a/crates/mir/src/ir/inst.rs +++ b/crates/mir/src/ir/inst.rs @@ -96,7 +96,7 @@ pub enum InstKind { }, Return { - arg: ValueId, + arg: Option, }, Keccak256 { @@ -115,6 +115,8 @@ pub enum InstKind { arg: ValueId, }, + Nop, + Create { value: ValueId, contract: ContractId, @@ -152,6 +154,13 @@ impl Inst { Self::new(kind, source) } + pub fn nop() -> Self { + Self { + kind: InstKind::Nop, + source: SourceInfo::dummy(), + } + } + pub fn is_terminator(&self) -> bool { match self.kind { InstKind::Jump { .. } diff --git a/crates/mir/src/lower/function.rs b/crates/mir/src/lower/function.rs index a8f6d09317..88bdebaa7a 100644 --- a/crates/mir/src/lower/function.rs +++ b/crates/mir/src/lower/function.rs @@ -51,7 +51,7 @@ pub fn lower_func_signature(db: &dyn MirDb, func: analyzer_items::FunctionId) -> let sig = FunctionSignature { params, - return_type, + return_type: Some(return_type), module_id: func.module(db.upcast()), analyzer_func_id: func, linkage, @@ -933,7 +933,7 @@ impl Scope { }; // Declare function parameters. - for param in &func.data(db).params { + for param in &func.signature(db).params { let local = Local::arg_local(param.name.clone(), param.ty, param.source.clone()); let value_id = builder.store_func_arg(local); root.declare_var(¶m.name, value_id) diff --git a/crates/mir/src/pretty_print/inst.rs b/crates/mir/src/pretty_print/inst.rs index 2e3697d0c8..4af6582b55 100644 --- a/crates/mir/src/pretty_print/inst.rs +++ b/crates/mir/src/pretty_print/inst.rs @@ -122,8 +122,12 @@ impl PrettyPrint for InstId { } InstKind::Return { arg } => { - write!(w, "return ")?; - arg.pretty_print(db, store, w) + if let Some(arg) = arg { + write!(w, "return ")?; + arg.pretty_print(db, store, w) + } else { + write!(w, "return") + } } InstKind::Keccak256 { arg } => { @@ -146,6 +150,10 @@ impl PrettyPrint for InstId { arg.pretty_print(db, store, w) } + InstKind::Nop => { + write!(w, "nop") + } + InstKind::Create { value, contract } => { write!(w, "create ")?; let contract_name = contract.name(db.upcast()); From 22a470b4db92b8f37e3e867fc0c2e4e89cca188f Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Fri, 4 Mar 2022 23:30:10 +0900 Subject: [PATCH 11/15] Add new abi crate --- Cargo.lock | 21 +- crates/abi/src/utils.rs | 2 +- crates/codegen/Cargo.toml | 4 +- crates/codegen/src/abi.rs | 3 - crates/codegen/src/lib.rs | 1 - crates/common/src/utils/keccak.rs | 4 +- crates/new_abi/Cargo.toml | 14 ++ crates/new_abi/src/contract.rs | 33 +++ crates/new_abi/src/event.rs | 148 ++++++++++++ crates/new_abi/src/function.rs | 203 ++++++++++++++++ crates/new_abi/src/lib.rs | 4 + crates/new_abi/src/types.rs | 294 +++++++++++++++++++++++ crates/yulgen/src/mappers/expressions.rs | 5 +- 13 files changed, 722 insertions(+), 14 deletions(-) delete mode 100644 crates/codegen/src/abi.rs create mode 100644 crates/new_abi/Cargo.toml create mode 100644 crates/new_abi/src/contract.rs create mode 100644 crates/new_abi/src/event.rs create mode 100644 crates/new_abi/src/function.rs create mode 100644 crates/new_abi/src/lib.rs create mode 100644 crates/new_abi/src/types.rs diff --git a/Cargo.lock b/Cargo.lock index 202bf79d44..3f43e1f0b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -560,7 +560,6 @@ version = "0.16.0-alpha" dependencies = [ "fe-analyzer", "fe-common", - "fe-parser", "serde", "serde_json", ] @@ -597,8 +596,6 @@ dependencies = [ name = "fe-codegen" version = "0.14.0-alpha" dependencies = [ - "fe-abi", - "fe-common", "fe-mir", ] @@ -732,6 +729,15 @@ dependencies = [ "smol_str", ] +[[package]] +name = "fe-new_abi" +version = "0.14.0-alpha" +dependencies = [ + "fe-common", + "serde", + "serde_test", +] + [[package]] name = "fe-parser" version = "0.16.0-alpha" @@ -1765,6 +1771,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_test" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21675ba6f9d97711cc00eee79d8dd7d0a31e571c350fb4d8a7c78f70c0e7b0e9" +dependencies = [ + "serde", +] + [[package]] name = "serde_yaml" version = "0.8.23" diff --git a/crates/abi/src/utils.rs b/crates/abi/src/utils.rs index e68eea770b..45e0b77a4c 100644 --- a/crates/abi/src/utils.rs +++ b/crates/abi/src/utils.rs @@ -14,5 +14,5 @@ pub fn func_selector(name: &str, params: &[String]) -> String { fn hash_signature(name: &str, params: &[String], size: usize) -> String { let signature = format!("{}({})", name, params.join(",")); - keccak::partial(signature.as_bytes(), size) + format!("0x{}", keccak::partial(signature.as_bytes(), size)) } diff --git a/crates/codegen/Cargo.toml b/crates/codegen/Cargo.toml index dd7e9c1c42..bc618a69ed 100644 --- a/crates/codegen/Cargo.toml +++ b/crates/codegen/Cargo.toml @@ -5,6 +5,4 @@ authors = ["The Fe Developers "] edition = "2021" [dependencies] -fe-mir = { path = "../mir", version = "^0.14.0-alpha" } -fe-common = { path = "../common", version = "^0.14.0-alpha" } -fe-abi= { path = "../abi", version = "^0.14.0-alpha" } \ No newline at end of file +fe-mir = { path = "../mir", version = "^0.14.0-alpha" } \ No newline at end of file diff --git a/crates/codegen/src/abi.rs b/crates/codegen/src/abi.rs deleted file mode 100644 index 6945184c23..0000000000 --- a/crates/codegen/src/abi.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub enum AbiType { - SolidityV1, -} diff --git a/crates/codegen/src/lib.rs b/crates/codegen/src/lib.rs index d55fae34cc..a795c81958 100644 --- a/crates/codegen/src/lib.rs +++ b/crates/codegen/src/lib.rs @@ -1,2 +1 @@ -pub mod abi; pub mod yul; diff --git a/crates/common/src/utils/keccak.rs b/crates/common/src/utils/keccak.rs index 7df04ba9a6..20e1035487 100644 --- a/crates/common/src/utils/keccak.rs +++ b/crates/common/src/utils/keccak.rs @@ -15,13 +15,13 @@ pub fn partial_right_padded(content: &[u8], size: usize) -> String { .map(|(index, byte)| if index >= size { 0 } else { *byte }) .collect(); - format!("0x{}", hex::encode(&padded_output)) + hex::encode(&padded_output) } /// Take the first `size` number of bytes of the hash with no padding. pub fn partial(content: &[u8], size: usize) -> String { let result = full_as_bytes(content); - format!("0x{}", hex::encode(&result[0..size])) + hex::encode(&result[0..size]) } /// Get the full 32 byte hash of the content as a byte array. diff --git a/crates/new_abi/Cargo.toml b/crates/new_abi/Cargo.toml new file mode 100644 index 0000000000..ae4bfff262 --- /dev/null +++ b/crates/new_abi/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "fe-new_abi" +version = "0.14.0-alpha" +authors = ["The Fe Developers "] +edition = "2021" +license = "Apache-2.0" +repository = "https://github.com/ethereum/fe" + +[dependencies] +fe-common = { path = "../common", version = "^0.14.0-alpha" } +serde = { version = "1.0", features = ["derive"] } + +[dev-dependencies] +serde_test = "1.0" diff --git a/crates/new_abi/src/contract.rs b/crates/new_abi/src/contract.rs new file mode 100644 index 0000000000..6557fecb60 --- /dev/null +++ b/crates/new_abi/src/contract.rs @@ -0,0 +1,33 @@ +use super::{event::AbiEvent, function::AbiFunction}; + +use serde::{ser::SerializeSeq, Serialize, Serializer}; + +#[derive(Debug, Clone)] +pub struct AbiContract { + /// Public functions in the contract. + funcs: Vec, + + /// Events emitted from the contract. + events: Vec, +} + +impl Serialize for AbiContract { + fn serialize(&self, s: S) -> Result { + let mut seq = s.serialize_seq(Some(self.funcs.len() + self.events.len()))?; + for func in &self.funcs { + seq.serialize_element(func)?; + } + + for event in &self.events { + seq.serialize_element(event)?; + } + + seq.end() + } +} + +impl AbiContract { + pub fn new(funcs: Vec, events: Vec) -> Self { + Self { funcs, events } + } +} diff --git a/crates/new_abi/src/event.rs b/crates/new_abi/src/event.rs new file mode 100644 index 0000000000..5925951a62 --- /dev/null +++ b/crates/new_abi/src/event.rs @@ -0,0 +1,148 @@ +use super::types::AbiType; + +use fe_common::utils::keccak; +use serde::Serialize; + +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] +pub struct AbiEvent { + #[serde(rename = "type")] + ty: &'static str, + name: String, + inputs: Vec, + anonymous: bool, +} + +impl AbiEvent { + pub fn new(name: String, fields: Vec, anonymous: bool) -> Self { + Self { + ty: "event", + name, + inputs: fields, + anonymous, + } + } + + pub fn signature(&self) -> AbiEventSignature { + AbiEventSignature::new(self) + } +} + +pub struct AbiEventSignature { + sig: String, +} + +impl AbiEventSignature { + pub fn signature(&self) -> &str { + &self.sig + } + + pub fn hash_hex(&self) -> String { + keccak::full(self.sig.as_bytes()) + } + + pub fn hash_raw(&self) -> [u8; 32] { + keccak::full_as_bytes(self.sig.as_bytes()) + } + + fn new(event: &AbiEvent) -> Self { + let sig = format!( + "{}({})", + event.name, + event + .inputs + .iter() + .map(|input| input.ty.selector_type_name()) + .collect::>() + .join(",") + ); + + Self { sig } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] +pub struct AbiEventField { + name: String, + #[serde(flatten)] + ty: AbiType, + indexed: bool, +} + +impl AbiEventField { + pub fn new(name: String, ty: impl Into, indexed: bool) -> Self { + Self { + name, + ty: ty.into(), + indexed, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use serde_test::{assert_ser_tokens, Token}; + + fn test_event() -> AbiEvent { + let i32_ty = AbiType::Int(32); + let u32_ty = AbiType::UInt(32); + let field1 = AbiEventField::new("x".into(), i32_ty, true); + let field2 = AbiEventField::new("y".into(), u32_ty, false); + + AbiEvent::new("MyEvent".into(), vec![field1, field2], false) + } + + #[test] + fn serialize_event() { + let event = test_event(); + + assert_ser_tokens( + &event, + &[ + Token::Struct { + name: "AbiEvent", + len: 4, + }, + Token::Str("type"), + Token::Str("event"), + Token::String("name"), + Token::String("MyEvent"), + Token::Str("inputs"), + Token::Seq { len: Some(2) }, + Token::Map { len: None }, + Token::String("name"), + Token::String("x"), + Token::String("type"), + Token::String("int32"), + Token::Str("indexed"), + Token::Bool(true), + Token::MapEnd, + Token::Map { len: None }, + Token::String("name"), + Token::String("y"), + Token::String("type"), + Token::String("uint32"), + Token::Str("indexed"), + Token::Bool(false), + Token::MapEnd, + Token::SeqEnd, + Token::Str("anonymous"), + Token::Bool(false), + Token::StructEnd, + ], + ) + } + + #[test] + fn event_signature() { + let event = test_event(); + + let sig = event.signature(); + debug_assert_eq!(sig.signature(), "MyEvent(int32,uint32)"); + debug_assert_eq!( + sig.hash_hex(), + "ec835d5150565cb216f72ba07d715e875b0738b1ac3f412e103839e5157b7ee6" + ); + } +} diff --git a/crates/new_abi/src/function.rs b/crates/new_abi/src/function.rs new file mode 100644 index 0000000000..5fa8dbae80 --- /dev/null +++ b/crates/new_abi/src/function.rs @@ -0,0 +1,203 @@ +use fe_common::utils::keccak; + +use serde::Serialize; + +use super::types::AbiType; + +#[derive(Debug, Clone, Serialize)] +pub struct AbiFunction { + #[serde(rename = "type")] + func_type: AbiFunctionType, + name: String, + inputs: Vec, + outputs: Vec, +} + +impl AbiFunction { + pub fn new( + func_type: AbiFunctionType, + name: String, + args: Vec<(String, AbiType)>, + ret_ty: Option, + ) -> Self { + let inputs = args + .into_iter() + .map(|(arg_name, arg_ty)| AbiFunctionParamInner::new(arg_name, arg_ty)) + .collect(); + let outputs = ret_ty.map_or_else(Vec::new, |ret_ty| { + vec![AbiFunctionParamInner::new("".into(), ret_ty)] + }); + + Self { + func_type, + name, + inputs, + outputs, + } + } + + pub fn selector(&self) -> AbiFunctionSelector { + AbiFunctionSelector::new(self) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)] +#[serde(rename_all = "lowercase")] +pub enum AbiFunctionType { + Function, + Receive, + Payable, + Fallback, +} + +pub struct AbiFunctionSelector { + selector_sig: String, +} + +impl AbiFunctionSelector { + fn new(func_sig: &AbiFunction) -> Self { + let selector_sig = format!( + "{}({})", + func_sig.name, + func_sig + .inputs + .iter() + .map(|param| param.ty.selector_type_name()) + .collect::>() + .join(",") + ); + + Self { selector_sig } + } + + pub fn selector_signature(&self) -> &str { + &self.selector_sig + } + + pub fn selector_raw(&self) -> [u8; 4] { + keccak::full_as_bytes(self.selector_sig.as_bytes())[..4] + .try_into() + .unwrap() + } + + /// Returns first 4 bytes of signature hash in hex. + pub fn hex(&self) -> String { + keccak::partial(self.selector_sig.as_bytes(), 4) + } +} + +#[derive(Debug, Clone, Serialize)] +struct AbiFunctionParamInner { + name: String, + #[serde(flatten)] + ty: AbiType, +} + +impl AbiFunctionParamInner { + fn new(name: String, ty: AbiType) -> Self { + Self { name, ty } + } +} + +#[cfg(test)] +mod tests { + use crate::types::AbiTupleField; + + use super::*; + use serde_test::{assert_ser_tokens, Token}; + + fn simple_tuple() -> AbiType { + let u16_ty = AbiType::UInt(16); + let bool_ty = AbiType::Bool; + let field1 = AbiTupleField::new("field1".into(), u16_ty); + let field2 = AbiTupleField::new("field2".into(), bool_ty); + + AbiType::Tuple(vec![field1, field2]) + } + fn test_func() -> AbiFunction { + let i32_ty = AbiType::Int(32); + let tuple_ty = simple_tuple(); + let u64_ty = AbiType::UInt(64); + + AbiFunction::new( + AbiFunctionType::Function, + "test_func".into(), + vec![("arg1".into(), i32_ty), ("arg2".into(), tuple_ty)], + Some(u64_ty), + ) + } + + #[test] + fn serialize_func() { + let func = test_func(); + + assert_ser_tokens( + &func, + &[ + Token::Struct { + name: "AbiFunction", + len: 4, + }, + Token::Str("type"), + Token::UnitVariant { + name: "AbiFunctionType", + variant: "function", + }, + Token::String("name"), + Token::String("test_func"), + Token::Str("inputs"), + Token::Seq { len: Some(2) }, + Token::Map { len: None }, + Token::String("name"), + Token::String("arg1"), + Token::String("type"), + Token::String("int32"), + Token::MapEnd, + Token::Map { len: None }, + Token::String("name"), + Token::String("arg2"), + Token::String("type"), + Token::String("tuple"), + Token::String("components"), + Token::Seq { len: Some(2) }, + Token::Map { len: None }, + Token::String("name"), + Token::String("field1"), + Token::String("type"), + Token::String("uint16"), + Token::MapEnd, + Token::Map { len: None }, + Token::String("name"), + Token::String("field2"), + Token::String("type"), + Token::String("bool"), + Token::MapEnd, + Token::SeqEnd, + Token::MapEnd, + Token::SeqEnd, + Token::Str("outputs"), + Token::Seq { len: Some(1) }, + Token::Map { len: None }, + Token::String("name"), + Token::String(""), + Token::String("type"), + Token::String("uint64"), + Token::MapEnd, + Token::SeqEnd, + Token::StructEnd, + ], + ) + } + + #[test] + fn func_selector() { + let func = test_func(); + let selector = func.selector(); + + debug_assert_eq!( + selector.selector_signature(), + "test_func(int32,(uint16,bool))" + ); + debug_assert_eq!(selector.hex(), "79c3c8b2"); + } +} diff --git a/crates/new_abi/src/lib.rs b/crates/new_abi/src/lib.rs new file mode 100644 index 0000000000..7860aec9c2 --- /dev/null +++ b/crates/new_abi/src/lib.rs @@ -0,0 +1,4 @@ +pub mod contract; +pub mod event; +pub mod function; +pub mod types; diff --git a/crates/new_abi/src/types.rs b/crates/new_abi/src/types.rs new file mode 100644 index 0000000000..8cf60dc60b --- /dev/null +++ b/crates/new_abi/src/types.rs @@ -0,0 +1,294 @@ +use serde::{ser::SerializeMap, Serialize, Serializer}; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum AbiType { + UInt(usize), + Int(usize), + Address, + Bool, + Function, + Array { elem_ty: Box, len: usize }, + Tuple(Vec), +} + +impl AbiType { + pub fn selector_type_name(&self) -> String { + match self { + Self::UInt(bits) => format!("uint{}", bits), + Self::Int(bits) => format!("int{}", bits), + Self::Address => "address".to_string(), + Self::Bool => "bool".to_string(), + Self::Function => "function".to_string(), + Self::Array { elem_ty, len } => format!("{}[{}]", elem_ty.selector_type_name(), len), + Self::Tuple(elems) => format!( + "({})", + elems + .iter() + .map(|component| component.ty.selector_type_name()) + .collect::>() + .join(",") + ), + } + } + + pub fn abi_type_name(&self) -> String { + match self { + Self::Tuple(_) => "tuple".to_string(), + Self::Array { elem_ty, len } => format!("{}[{}]", elem_ty.abi_type_name(), len), + _ => self.selector_type_name(), + } + } +} + +impl Serialize for AbiType { + fn serialize(&self, s: S) -> Result { + let mut map = s.serialize_map(None)?; + let type_name = self.abi_type_name(); + + map.serialize_entry("type", &type_name)?; + + match self { + Self::Tuple(entry) => map.serialize_entry("components", entry)?, + Self::Array { elem_ty, .. } => { + if let Self::Tuple(entry) = elem_ty.as_ref() { + map.serialize_entry("components", entry)? + } + } + _ => {} + } + + map.end() + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] +pub struct AbiTupleField { + pub(crate) name: String, + #[serde(flatten)] + pub(crate) ty: AbiType, +} + +impl AbiTupleField { + pub fn new(name: String, ty: impl Into) -> Self { + Self { + name, + ty: ty.into(), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use serde_test::{assert_ser_tokens, Token}; + + #[test] + fn primitive() { + let u32_ty = AbiType::UInt(32); + assert_ser_tokens( + &u32_ty, + &[ + Token::Map { len: None }, + Token::String("type"), + Token::String("uint32"), + Token::MapEnd, + ], + ) + } + + #[test] + fn primitive_array() { + let i32_ty = AbiType::Int(32); + let array_i32 = AbiType::Array { + elem_ty: i32_ty.into(), + len: 10, + }; + + assert_ser_tokens( + &array_i32, + &[ + Token::Map { len: None }, + Token::String("type"), + Token::String("int32[10]"), + Token::MapEnd, + ], + ) + } + + #[test] + fn tuple_array() { + let u16_ty = AbiType::UInt(16); + let bool_ty = AbiType::Bool; + let array_bool = AbiType::Array { + elem_ty: bool_ty.into(), + len: 16, + }; + + let field1 = AbiTupleField::new("field1".into(), u16_ty); + let field2 = AbiTupleField::new("field2".into(), array_bool); + let tuple_ty = AbiType::Tuple(vec![field1, field2]); + + let tuple_array_ty = AbiType::Array { + elem_ty: tuple_ty.into(), + len: 16, + }; + + assert_ser_tokens( + &tuple_array_ty, + &[ + Token::Map { len: None }, + Token::String("type"), + Token::String("tuple[16]"), + Token::String("components"), + Token::Seq { len: Some(2) }, + // Field1. + Token::Map { len: None }, + Token::String("name"), + Token::String("field1"), + Token::String("type"), + Token::String("uint16"), + Token::MapEnd, + // Field2. + Token::Map { len: None }, + Token::String("name"), + Token::String("field2"), + Token::String("type"), + Token::String("bool[16]"), + Token::MapEnd, + Token::SeqEnd, + Token::MapEnd, + ], + ) + } + + #[test] + fn simple_tuple() { + let u16_ty = AbiType::UInt(16); + let bool_ty = AbiType::Bool; + let bool_array_ty = AbiType::Array { + elem_ty: bool_ty.into(), + len: 16, + }; + + let field1 = AbiTupleField::new("field1".into(), u16_ty); + let field2 = AbiTupleField::new("field2".into(), bool_array_ty); + let tuple_ty = AbiType::Tuple(vec![field1, field2]); + + assert_ser_tokens( + &tuple_ty, + &[ + Token::Map { len: None }, + Token::String("type"), + Token::String("tuple"), + Token::String("components"), + Token::Seq { len: Some(2) }, + // Field1. + Token::Map { len: None }, + Token::String("name"), + Token::String("field1"), + Token::String("type"), + Token::String("uint16"), + Token::MapEnd, + // Field2. + Token::Map { len: None }, + Token::String("name"), + Token::String("field2"), + Token::String("type"), + Token::String("bool[16]"), + Token::MapEnd, + Token::SeqEnd, + Token::MapEnd, + ], + ) + } + + #[test] + fn complex_tuple() { + let u16_ty = AbiType::UInt(16); + let bool_ty = AbiType::Bool; + + let inner_field1 = AbiTupleField::new("inner_field1".into(), u16_ty); + let inner_field2 = AbiTupleField::new("inner_field2".into(), bool_ty); + let inner_tuple_ty = AbiType::Tuple(vec![inner_field1, inner_field2]); + + let inner_tuple_array_ty = AbiType::Array { + elem_ty: inner_tuple_ty.clone().into(), + len: 16, + }; + + let outer_field1 = AbiTupleField::new("outer_field1".into(), inner_tuple_array_ty); + let outer_field2 = AbiTupleField::new("outer_field2".into(), inner_tuple_ty); + let outer_tuple_ty = AbiType::Tuple(vec![outer_field1, outer_field2]); + + assert_ser_tokens( + &outer_tuple_ty, + &[ + // Outer tuple start. + Token::Map { len: None }, + Token::String("type"), + Token::String("tuple"), + // Outer tuple components start. + Token::String("components"), + Token::Seq { len: Some(2) }, + // Outer field1 start. + Token::Map { len: None }, + Token::String("name"), + Token::String("outer_field1"), + Token::String("type"), + Token::String("tuple[16]"), + Token::String("components"), + Token::Seq { len: Some(2) }, + // Inner field1 start. + Token::Map { len: None }, + Token::String("name"), + Token::String("inner_field1"), + Token::String("type"), + Token::String("uint16"), + Token::MapEnd, + // Inner field1 end. + // Inner field2 start. + Token::Map { len: None }, + Token::String("name"), + Token::String("inner_field2"), + Token::String("type"), + Token::String("bool"), + Token::MapEnd, + // Inner field2 end. + Token::SeqEnd, + Token::MapEnd, + // Outer field1 end. + // Outer field2 start. + Token::Map { len: None }, + Token::String("name"), + Token::String("outer_field2"), + Token::String("type"), + Token::String("tuple"), + Token::String("components"), + Token::Seq { len: Some(2) }, + // Inner field1 start. + Token::Map { len: None }, + Token::String("name"), + Token::String("inner_field1"), + Token::String("type"), + Token::String("uint16"), + Token::MapEnd, + // Inner field1 end. + // Inner field2 start. + Token::Map { len: None }, + Token::String("name"), + Token::String("inner_field2"), + Token::String("type"), + Token::String("bool"), + Token::MapEnd, + // Inner field2 end. + Token::SeqEnd, + Token::MapEnd, + // Outer field2 end. + Token::SeqEnd, + // Outer tuple components end. + Token::MapEnd, + ], + ) + } +} diff --git a/crates/yulgen/src/mappers/expressions.rs b/crates/yulgen/src/mappers/expressions.rs index bdd561ca27..07eeaf1085 100644 --- a/crates/yulgen/src/mappers/expressions.rs +++ b/crates/yulgen/src/mappers/expressions.rs @@ -456,6 +456,9 @@ fn expr_attribute(context: &mut FnContext, exp: &Node) -> yul::Express /// our byte pointer sits at the start of a word (32 | `ptr` ). pub fn nonce_to_ptr(nonce: usize) -> yul::Expression { // set the last byte to `0x00` to ensure our pointer sits at the start of a word - let ptr = keccak::partial_right_padded(nonce.to_string().as_bytes(), 31); + let ptr = format!( + "0x{}", + keccak::partial_right_padded(nonce.to_string().as_bytes(), 31) + ); literal_expression! { (ptr) } } From 309af09a3091de28b405f1fff125288eb740df64 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Mon, 7 Mar 2022 21:06:19 +0900 Subject: [PATCH 12/15] Implement `InstSerializer` for yul codegen --- Cargo.lock | 6 + crates/bar.dot | 0 crates/codegen/Cargo.toml | 8 +- crates/codegen/src/db.rs | 23 ++ crates/codegen/src/db/queries.rs | 2 + crates/codegen/src/db/queries/abi.rs | 85 +++++ crates/codegen/src/db/queries/function.rs | 17 + crates/codegen/src/lib.rs | 1 + crates/codegen/src/yul/inst_order.rs | 309 ++++++++++++++++++ crates/codegen/src/yul/isel/mod.rs | 25 ++ crates/codegen/src/yul/legalize/body.rs | 69 ++-- .../codegen/src/yul/legalize/critical_edge.rs | 133 ++++++++ crates/codegen/src/yul/legalize/mod.rs | 1 + crates/codegen/src/yul/legalize/signature.rs | 6 +- crates/codegen/src/yul/mod.rs | 3 + crates/mir/bar.dot | 0 crates/mir/src/analysis/cfg.rs | 2 +- crates/mir/src/analysis/domtree.rs | 67 +++- crates/mir/src/db/queries/function.rs | 5 + crates/mir/src/graphviz/block.rs | 4 +- crates/mir/src/graphviz/function.rs | 7 +- crates/mir/src/graphviz/module.rs | 6 +- crates/mir/src/ir/body_order.rs | 1 - crates/mir/src/ir/function.rs | 23 +- crates/mir/src/ir/inst.rs | 4 +- crates/mir/src/ir/types.rs | 2 +- crates/mir/src/lower/function.rs | 146 +++++---- crates/new_abi/src/function.rs | 4 +- crates/new_abi/src/types.rs | 4 +- 29 files changed, 841 insertions(+), 122 deletions(-) create mode 100644 crates/bar.dot create mode 100644 crates/codegen/src/db.rs create mode 100644 crates/codegen/src/db/queries.rs create mode 100644 crates/codegen/src/db/queries/abi.rs create mode 100644 crates/codegen/src/db/queries/function.rs create mode 100644 crates/codegen/src/yul/inst_order.rs create mode 100644 crates/codegen/src/yul/isel/mod.rs create mode 100644 crates/codegen/src/yul/legalize/critical_edge.rs create mode 100644 crates/mir/bar.dot diff --git a/Cargo.lock b/Cargo.lock index 3f43e1f0b0..7163783dd6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -596,7 +596,13 @@ dependencies = [ name = "fe-codegen" version = "0.14.0-alpha" dependencies = [ + "fe-common", "fe-mir", + "fe-new_abi", + "fxhash", + "salsa", + "smol_str", + "yultsur", ] [[package]] diff --git a/crates/bar.dot b/crates/bar.dot new file mode 100644 index 0000000000..e69de29bb2 diff --git a/crates/codegen/Cargo.toml b/crates/codegen/Cargo.toml index bc618a69ed..31f40afbb3 100644 --- a/crates/codegen/Cargo.toml +++ b/crates/codegen/Cargo.toml @@ -5,4 +5,10 @@ authors = ["The Fe Developers "] edition = "2021" [dependencies] -fe-mir = { path = "../mir", version = "^0.14.0-alpha" } \ No newline at end of file +fe-mir = { path = "../mir", version = "^0.14.0-alpha" } +fe-common = { path = "../common", version = "^0.14.0-alpha" } +fe-new_abi = { path = "../new_abi", version = "^0.14.0-alpha" } +salsa = "0.16.1" +fxhash = "0.2.1" +smol_str = "0.1.21" +yultsur = { git = "https://github.com/g-r-a-n-t/yultsur", rev = "ae85470" } \ No newline at end of file diff --git a/crates/codegen/src/db.rs b/crates/codegen/src/db.rs new file mode 100644 index 0000000000..835697d507 --- /dev/null +++ b/crates/codegen/src/db.rs @@ -0,0 +1,23 @@ +use std::rc::Rc; + +use fe_common::db::{Upcast, UpcastMut}; +use fe_mir::{ + db::MirDb, + ir::{FunctionBody, FunctionId, FunctionSignature, TypeId}, +}; +use fe_new_abi::{function::AbiFunction, types::AbiType}; + +mod queries; + +#[salsa::query_group(CodegenDbStorage)] +pub trait CodegenDb: MirDb + Upcast + UpcastMut { + #[salsa::invoke(queries::function::legalized_signature)] + fn codegen_legalized_signature(&self, function_id: FunctionId) -> Rc; + #[salsa::invoke(queries::function::legalized_body)] + fn codegen_legalized_body(&self, function_id: FunctionId) -> Rc; + + #[salsa::invoke(queries::abi::abi_type)] + fn codegen_abi_type(&self, ty: TypeId) -> AbiType; + #[salsa::invoke(queries::abi::abi_function)] + fn codegen_abi_function(&self, function_id: FunctionId) -> Rc; +} diff --git a/crates/codegen/src/db/queries.rs b/crates/codegen/src/db/queries.rs new file mode 100644 index 0000000000..db68776cbe --- /dev/null +++ b/crates/codegen/src/db/queries.rs @@ -0,0 +1,2 @@ +pub mod abi; +pub mod function; diff --git a/crates/codegen/src/db/queries/abi.rs b/crates/codegen/src/db/queries/abi.rs new file mode 100644 index 0000000000..2720503cc0 --- /dev/null +++ b/crates/codegen/src/db/queries/abi.rs @@ -0,0 +1,85 @@ +use std::rc::Rc; + +use fe_mir::ir::{self, FunctionId, TypeId}; +use fe_new_abi::{ + function::{AbiFunction, AbiFunctionType}, + types::{AbiTupleField, AbiType}, +}; + +use crate::db::CodegenDb; + +pub fn abi_function(db: &dyn CodegenDb, function: FunctionId) -> Rc { + // We use a legalized signature. + let sig = db.codegen_legalized_signature(function); + + let name = function.name(db.upcast()); + let args = sig + .params + .iter() + .map(|param| (param.name.to_string(), db.codegen_abi_type(param.ty))) + .collect(); + let ret_ty = sig.return_type.map(|ty| db.codegen_abi_type(ty)); + + AbiFunction::new(AbiFunctionType::Function, name.to_string(), args, ret_ty).into() +} + +pub fn abi_type(db: &dyn CodegenDb, ty: TypeId) -> AbiType { + if ty.is_zero_sized(db.upcast()) { + unreachable!("zero-sized type must be removed in legalization"); + } + + let ty = ty.data(db.upcast()); + + match ty.as_ref() { + ir::Type::I8 => AbiType::Int(8), + ir::Type::I16 => AbiType::Int(16), + ir::Type::I32 => AbiType::Int(32), + ir::Type::I64 => AbiType::Int(64), + ir::Type::I128 => AbiType::Int(128), + ir::Type::I256 => AbiType::Int(256), + ir::Type::U8 => AbiType::UInt(8), + ir::Type::U16 => AbiType::UInt(16), + ir::Type::U32 => AbiType::UInt(32), + ir::Type::U64 => AbiType::UInt(64), + ir::Type::U128 => AbiType::UInt(128), + ir::Type::U256 => AbiType::UInt(256), + ir::Type::Bool => AbiType::Bool, + ir::Type::Address => AbiType::Address, + ir::Type::Unit => unreachable!("zero-sized type must be removed in legalization"), + ir::Type::Array(def) => { + let elem_ty = db.codegen_abi_type(def.elem_ty); + let len = def.len; + AbiType::Array { + elem_ty: elem_ty.into(), + len, + } + } + ir::Type::Tuple(def) => { + let fields = def + .items + .iter() + .enumerate() + .map(|(i, item)| { + let field_ty = db.codegen_abi_type(*item); + AbiTupleField::new(format!("{}", i), field_ty) + }) + .collect(); + + AbiType::Tuple(fields) + } + ir::Type::Struct(def) => { + let fields = def + .fields + .iter() + .map(|(name, ty)| { + let ty = db.codegen_abi_type(*ty); + AbiTupleField::new(name.to_string(), ty) + }) + .collect(); + + AbiType::Tuple(fields) + } + ir::Type::Event(_) | ir::Type::Contract(_) => unreachable!(), + ir::Type::Map(_) => todo!("map type can't be used in parameter or return type"), + } +} diff --git a/crates/codegen/src/db/queries/function.rs b/crates/codegen/src/db/queries/function.rs new file mode 100644 index 0000000000..4878dd4be8 --- /dev/null +++ b/crates/codegen/src/db/queries/function.rs @@ -0,0 +1,17 @@ +use std::rc::Rc; + +use fe_mir::ir::{FunctionBody, FunctionId, FunctionSignature}; + +use crate::{db::CodegenDb, yul::legalize}; + +pub fn legalized_signature(db: &dyn CodegenDb, function: FunctionId) -> Rc { + let mut sig = function.signature(db.upcast()).as_ref().clone(); + legalize::legalize_func_signature(db, &mut sig); + sig.into() +} + +pub fn legalized_body(db: &dyn CodegenDb, function: FunctionId) -> Rc { + let mut body = function.body(db.upcast()).as_ref().clone(); + legalize::legalize_func_body(db, &mut body); + body.into() +} diff --git a/crates/codegen/src/lib.rs b/crates/codegen/src/lib.rs index a795c81958..37ec962db2 100644 --- a/crates/codegen/src/lib.rs +++ b/crates/codegen/src/lib.rs @@ -1 +1,2 @@ +pub mod db; pub mod yul; diff --git a/crates/codegen/src/yul/inst_order.rs b/crates/codegen/src/yul/inst_order.rs new file mode 100644 index 0000000000..1e599100a4 --- /dev/null +++ b/crates/codegen/src/yul/inst_order.rs @@ -0,0 +1,309 @@ +#![allow(unused)] + +use fe_mir::{ + analysis::{ + domtree::DFSet, loop_tree::LoopId, post_domtree::PostIDom, ControlFlowGraph, DomTree, + LoopTree, PostDomTree, + }, + ir::{inst::BranchInfo, BasicBlockId, FunctionBody, InstId, ValueId}, +}; + +#[derive(Debug, Clone, Default)] +pub struct InstOrder { + pub order: Vec, +} + +#[derive(Debug, Clone)] +pub enum StructuralInst { + Inst(InstId), + If { + cond: ValueId, + then: Vec, + else_: Vec, + }, + For { + body: Vec, + }, + Break, + Continue, +} + +struct InstSerializer<'a> { + body: &'a FunctionBody, + cfg: ControlFlowGraph, + loop_tree: LoopTree, + df: DFSet, + pd_tree: PostDomTree, + scope: Option, +} + +impl<'a> InstSerializer<'a> { + fn new(body: &'a FunctionBody) -> Self { + let cfg = ControlFlowGraph::compute(body); + let domtree = DomTree::compute(&cfg); + let df = domtree.compute_df(&cfg); + let pd_tree = PostDomTree::compute(body); + let loop_tree = LoopTree::compute(&cfg, &domtree); + + Self { + body, + cfg, + loop_tree, + df, + pd_tree, + scope: None, + } + } + + fn serialize_insts(&mut self) -> InstOrder { + self.scope = None; + let entry = self.cfg.entry(); + let mut order = vec![]; + self.analyze_block(entry, &mut order); + InstOrder { order } + } + + fn analyze_block(&mut self, block: BasicBlockId, order: &mut Vec) { + match self.loop_tree.loop_of_block(block) { + Some(lp) + if block == self.loop_tree.loop_header(lp) + && Some(block) != self.scope.as_ref().and_then(Scope::loop_header) => + { + let loop_exit = self.find_loop_exit(lp); + self.enter_loop_scope(block, loop_exit); + let mut body = vec![]; + self.analyze_block(block, &mut body); + self.exit_scope(); + order.push(StructuralInst::For { body }); + + if let Some(exit) = loop_exit { + self.analyze_block(exit, order); + } + return; + } + _ => {} + }; + + for inst in self.body.order.iter_inst(block) { + if self.body.store.is_terminator(inst) { + break; + } + order.push(StructuralInst::Inst(inst)); + } + + let terminator = self.body.order.terminator(&self.body.store, block).unwrap(); + match self.analyze_terminator(terminator) { + TerminatorInfo::If { + cond, + then, + else_, + merge_block, + } => { + let mut then_body = vec![]; + let mut else_body = vec![]; + + self.enter_if_scope(merge_block); + if let Some(merge_block) = merge_block { + if merge_block == else_ { + self.analyze_block(then, &mut then_body) + } else if merge_block == then { + self.analyze_block(else_, &mut else_body); + } else { + self.analyze_block(then, &mut then_body); + self.analyze_block(else_, &mut else_body); + } + order.push(StructuralInst::If { + cond, + then: then_body, + else_: else_body, + }); + self.exit_scope(); + self.analyze_block(merge_block, order) + } else { + self.analyze_block(then, &mut then_body); + self.analyze_block(else_, &mut else_body); + self.exit_scope(); + order.push(StructuralInst::If { + cond, + then: then_body, + else_: else_body, + }); + } + } + TerminatorInfo::ToMergeBlock => {} + TerminatorInfo::Continue => order.push(StructuralInst::Continue), + TerminatorInfo::Break => order.push(StructuralInst::Break), + TerminatorInfo::FallThrough(next) => self.analyze_block(next, order), + TerminatorInfo::NormalInst(inst) => order.push(StructuralInst::Inst(inst)), + } + } + + fn enter_loop_scope(&mut self, header: BasicBlockId, exit: Option) { + let kind = ScopeKind::Loop { header, exit }; + let current_scope = std::mem::take(&mut self.scope); + self.scope = Some(Scope { + kind, + parent: current_scope.map(Into::into), + }); + } + + fn enter_if_scope(&mut self, merge_block: Option) { + let kind = ScopeKind::If { merge_block }; + let current_scope = std::mem::take(&mut self.scope); + self.scope = Some(Scope { + kind, + parent: current_scope.map(Into::into), + }); + } + + fn exit_scope(&mut self) { + let current_scope = std::mem::take(&mut self.scope); + self.scope = current_scope.unwrap().parent.map(|parent| *parent); + } + + // NOTE: We assume loop has at most one canonical loop exit. + fn find_loop_exit(&self, lp: LoopId) -> Option { + let mut exit_candidates = vec![]; + for block_in_loop in self.loop_tree.iter_blocks_post_order(&self.cfg, lp) { + for &succ in self.cfg.succs(block_in_loop) { + if !self.loop_tree.is_block_in_loop(succ, lp) { + exit_candidates.push(succ); + } + } + } + + if exit_candidates.is_empty() { + return None; + } + + for &cand in &exit_candidates { + // `cand` is true loop exit if the `cand` is contained in the dominance frontier + // of all other candidates. and yeset foo + if exit_candidates.iter().all(|&block| { + if block == cand { + true + } else if let Some(mut df) = self.df.frontiers(block) { + df.any(|frontier| frontier == cand) + } else { + true + } + }) { + return Some(cand); + } + } + + None + } + + fn analyze_terminator(&self, inst: InstId) -> TerminatorInfo { + debug_assert!(self.body.store.is_terminator(inst)); + + match self.body.store.branch_info(inst) { + BranchInfo::Jump(dest) => self.analyze_jump(dest), + BranchInfo::Branch(cond, then, else_) => { + self.analyze_branch(self.body.order.inst_block(inst), cond, then, else_) + } + BranchInfo::NotBranch => TerminatorInfo::NormalInst(inst), + } + } + + // NOTE: We remove critical edges in legalization pass, so `break` and + // `continue` never appear in branch info. + fn analyze_branch( + &self, + block: BasicBlockId, + cond: ValueId, + then: BasicBlockId, + else_: BasicBlockId, + ) -> TerminatorInfo { + let merge_block = match self.pd_tree.post_idom(block) { + PostIDom::DummyEntry | PostIDom::DummyExit => None, + PostIDom::Block(block) => Some(block), + }; + + TerminatorInfo::If { + cond, + then, + else_, + merge_block, + } + } + + fn analyze_jump(&self, dest: BasicBlockId) -> TerminatorInfo { + match &self.scope { + Some(scope) => { + if Some(dest) == scope.loop_header_recursive() { + TerminatorInfo::Continue + } else if Some(dest) == scope.loop_exit_recursive() { + TerminatorInfo::Break + } else if Some(dest) == scope.if_merge_block() { + TerminatorInfo::ToMergeBlock + } else { + TerminatorInfo::FallThrough(dest) + } + } + + None => TerminatorInfo::FallThrough(dest), + } + } +} + +struct Scope { + kind: ScopeKind, + parent: Option>, +} + +#[derive(Debug, Clone, Copy)] +enum ScopeKind { + Loop { + header: BasicBlockId, + exit: Option, + }, + If { + merge_block: Option, + }, +} + +impl Scope { + fn loop_header(&self) -> Option { + match self.kind { + ScopeKind::Loop { header, .. } => Some(header), + _ => None, + } + } + fn loop_header_recursive(&self) -> Option { + match self.kind { + ScopeKind::Loop { header, .. } => Some(header), + _ => self.parent.as_ref()?.loop_header_recursive(), + } + } + + fn loop_exit_recursive(&self) -> Option { + match self.kind { + ScopeKind::Loop { exit, .. } => exit, + _ => self.parent.as_ref()?.loop_exit_recursive(), + } + } + + fn if_merge_block(&self) -> Option { + match self.kind { + ScopeKind::If { merge_block } => merge_block, + _ => None, + } + } +} + +#[derive(Debug, Clone)] +enum TerminatorInfo { + If { + cond: ValueId, + then: BasicBlockId, + else_: BasicBlockId, + merge_block: Option, + }, + ToMergeBlock, + Continue, + Break, + FallThrough(BasicBlockId), + NormalInst(InstId), +} diff --git a/crates/codegen/src/yul/isel/mod.rs b/crates/codegen/src/yul/isel/mod.rs new file mode 100644 index 0000000000..e6a246a6fa --- /dev/null +++ b/crates/codegen/src/yul/isel/mod.rs @@ -0,0 +1,25 @@ +#![allow(unused)] +use fe_mir::{ + analysis::ControlFlowGraph, + ir::{FunctionBody, FunctionSignature, ValueId}, +}; +use fxhash::FxHashMap; +use smol_str::SmolStr; + +use crate::db::CodegenDb; + +struct FuncLowerHelper<'db, 'a> { + db: &'db dyn CodegenDb, + value_map: FxHashMap, + sig: &'a FunctionSignature, + body: &'a FunctionBody, + cfg: ControlFlowGraph, + sink: Vec, + ret_value: Option, +} + +impl<'db, 'a> FuncLowerHelper<'db, 'a> { + fn lower_func(self) -> Vec { + todo!() + } +} diff --git a/crates/codegen/src/yul/legalize/body.rs b/crates/codegen/src/yul/legalize/body.rs index 69292db8a2..4683402b59 100644 --- a/crates/codegen/src/yul/legalize/body.rs +++ b/crates/codegen/src/yul/legalize/body.rs @@ -1,15 +1,19 @@ -use fe_mir::{ - db::MirDb, - ir::{ - body_cursor::{BodyCursor, CursorLocation}, - inst::InstKind, - FunctionBody, Inst, InstId, - }, +use fe_mir::ir::{ + body_cursor::{BodyCursor, CursorLocation}, + inst::InstKind, + FunctionBody, Inst, InstId, }; -pub fn legalize_func_body(db: &dyn MirDb, body: &mut FunctionBody) { - let mut cursor = BodyCursor::new_at_entry(body); +use crate::db::CodegenDb; + +use super::critical_edge::CriticalEdgeSplitter; + +pub fn legalize_func_body(db: &dyn CodegenDb, body: &mut FunctionBody) { + // Remove critical edges. + CriticalEdgeSplitter::new().run(body); + // Remove zero-sized types usage. + let mut cursor = BodyCursor::new_at_entry(body); loop { match cursor.loc() { CursorLocation::BlockTop(_) | CursorLocation::BlockBottom(_) => cursor.proceed(), @@ -22,41 +26,40 @@ pub fn legalize_func_body(db: &dyn MirDb, body: &mut FunctionBody) { } } -fn legalize_inst(db: &dyn MirDb, body: &mut FunctionBody, inst: InstId) { - legalize_call(db, body, inst); - legalize_return(db, body, inst); +fn legalize_inst(db: &dyn CodegenDb, body: &mut FunctionBody, inst: InstId) { + legalize_inst_arg(db, body, inst); legalize_inst_result(db, body, inst); } -// Remove zero-sized arguments from call instruction. -fn legalize_call(db: &dyn MirDb, body: &mut FunctionBody, inst: InstId) { +fn legalize_inst_arg(db: &dyn CodegenDb, body: &mut FunctionBody, inst_id: InstId) { + // Replace inst with dummy inst to avoid borrow checker complaining. let dummy_inst = Inst::nop(); - let mut call_inst = body.store.replace_inst(inst, dummy_inst); - if let InstKind::Call { args, .. } = &mut call_inst.kind { - args.retain(|arg| !body.store.value_ty(*arg).is_zero_sized(db)); - } + let mut inst = body.store.replace_inst(inst_id, dummy_inst); - body.store.replace_inst(inst, call_inst); -} + match &mut inst.kind { + InstKind::AggregateConstruct { args, .. } | InstKind::Call { args, .. } => { + args.retain(|arg| !body.store.value_ty(*arg).is_zero_sized(db.upcast())); + } -// Remove return argument if its type is zero sized. -fn legalize_return(db: &dyn MirDb, body: &mut FunctionBody, inst: InstId) { - if let Inst { - kind: InstKind::Return { arg: Some(arg) }, - source, - } = body.store.inst_data(inst) - { - if body.store.value_ty(*arg).is_zero_sized(db) { - let ret_inst = Inst::new(InstKind::Return { arg: None }, source.clone()); - body.store.replace_inst(inst, ret_inst); + InstKind::Return { arg } => { + if arg + .map(|arg| body.store.value_ty(arg).is_zero_sized(db.upcast())) + .unwrap_or(false) + { + *arg = None; + } } + + _ => {} } + + body.store.replace_inst(inst_id, inst); } -/// Remove instruction result if its type is zero sized. -fn legalize_inst_result(db: &dyn MirDb, body: &mut FunctionBody, inst: InstId) { +/// Remove instruction result if its type is zero-sized. +fn legalize_inst_result(db: &dyn CodegenDb, body: &mut FunctionBody, inst: InstId) { if let Some(result) = body.store.inst_result(inst) { - if body.store.value_ty(result).is_zero_sized(db) { + if body.store.value_ty(result).is_zero_sized(db.upcast()) { body.store.remove_inst_result(inst) } } diff --git a/crates/codegen/src/yul/legalize/critical_edge.rs b/crates/codegen/src/yul/legalize/critical_edge.rs new file mode 100644 index 0000000000..40a898c18b --- /dev/null +++ b/crates/codegen/src/yul/legalize/critical_edge.rs @@ -0,0 +1,133 @@ +use fe_mir::{ + analysis::ControlFlowGraph, + ir::{ + body_cursor::{BodyCursor, CursorLocation}, + inst::{BranchInfo, InstKind}, + BasicBlock, BasicBlockId, FunctionBody, Inst, InstId, SourceInfo, + }, +}; + +#[derive(Debug)] +pub struct CriticalEdgeSplitter { + critical_edges: Vec, +} + +impl CriticalEdgeSplitter { + pub fn new() -> Self { + Self { + critical_edges: Vec::default(), + } + } + + pub fn run(&mut self, func: &mut FunctionBody) { + let cfg = ControlFlowGraph::compute(func); + + for block in func.order.iter_block() { + let terminator = func.order.terminator(&func.store, block).unwrap(); + self.add_critical_edges(terminator, func, &cfg); + } + + self.split_edges(func); + } + + fn add_critical_edges( + &mut self, + terminator: InstId, + func: &FunctionBody, + cfg: &ControlFlowGraph, + ) { + match func.store.branch_info(terminator) { + BranchInfo::Branch(_, then, else_) => { + if cfg.preds(then).len() > 1 { + self.critical_edges.push(CriticalEdge { + terminator, + to: then, + }); + } + if cfg.preds(else_).len() > 1 { + self.critical_edges.push(CriticalEdge { + terminator, + to: else_, + }); + } + } + BranchInfo::Jump(_) | BranchInfo::NotBranch => {} + } + } + + fn split_edges(&mut self, func: &mut FunctionBody) { + for edge in std::mem::take(&mut self.critical_edges) { + let terminator = edge.terminator; + let source_block = func.order.inst_block(terminator); + let original_dest = edge.to; + + // Create new block that contains only jump inst. + let new_dest = func.store.store_block(BasicBlock {}); + let mut cursor = BodyCursor::new(func, CursorLocation::BlockTop(source_block)); + cursor.insert_block(new_dest); + cursor.set_loc(CursorLocation::BlockTop(new_dest)); + cursor.store_and_insert_inst(Inst::new( + InstKind::Jump { + dest: original_dest, + }, + SourceInfo::dummy(), + )); + + // Rewrite branch destination to the new dest. + func.store + .rewrite_branch_dest(terminator, original_dest, new_dest); + } + } +} + +#[derive(Debug)] +struct CriticalEdge { + terminator: InstId, + to: BasicBlockId, +} + +#[cfg(test)] +mod tests { + use fe_mir::ir::{body_builder::BodyBuilder, FunctionId, TypeId}; + + use super::*; + + fn body_builder() -> BodyBuilder { + BodyBuilder::new(FunctionId(0), SourceInfo::dummy()) + } + + #[test] + fn critical_edge_remove() { + let mut builder = body_builder(); + let lp_header = builder.make_block(); + let lp_body = builder.make_block(); + let exit = builder.make_block(); + + let dummy_ty = TypeId(0); + let v0 = builder.make_imm_from_bool(false, dummy_ty); + builder.branch(v0, lp_header, exit, SourceInfo::dummy()); + + builder.move_to_block(lp_header); + builder.jump(lp_body, SourceInfo::dummy()); + + builder.move_to_block(lp_body); + builder.branch(v0, lp_header, exit, SourceInfo::dummy()); + + builder.move_to_block(exit); + builder.ret(v0, SourceInfo::dummy()); + + let mut func = builder.build(); + CriticalEdgeSplitter::new().run(&mut func); + let cfg = ControlFlowGraph::compute(&func); + + for &header_pred in cfg.preds(lp_header) { + debug_assert_eq!(cfg.succs(header_pred).len(), 1); + debug_assert_eq!(cfg.succs(header_pred)[0], lp_header); + } + + for &exit_pred in cfg.preds(exit) { + debug_assert_eq!(cfg.succs(exit_pred).len(), 1); + debug_assert_eq!(cfg.succs(exit_pred)[0], exit); + } + } +} diff --git a/crates/codegen/src/yul/legalize/mod.rs b/crates/codegen/src/yul/legalize/mod.rs index 450e825562..62e82f78fe 100644 --- a/crates/codegen/src/yul/legalize/mod.rs +++ b/crates/codegen/src/yul/legalize/mod.rs @@ -1,4 +1,5 @@ mod body; +mod critical_edge; mod signature; pub use body::legalize_func_body; diff --git a/crates/codegen/src/yul/legalize/signature.rs b/crates/codegen/src/yul/legalize/signature.rs index ab7755949f..1354f70f8e 100644 --- a/crates/codegen/src/yul/legalize/signature.rs +++ b/crates/codegen/src/yul/legalize/signature.rs @@ -1,6 +1,8 @@ -use fe_mir::{db::MirDb, ir::FunctionSignature}; +use fe_mir::ir::FunctionSignature; -pub fn legalize_func_signature(_db: &dyn MirDb, _sig: &mut FunctionSignature) { +use crate::db::CodegenDb; + +pub fn legalize_func_signature(_db: &dyn CodegenDb, _sig: &mut FunctionSignature) { // TODO: Remove zero sized types from arguments, also remove return type if // it's zero-sized } diff --git a/crates/codegen/src/yul/mod.rs b/crates/codegen/src/yul/mod.rs index 47c4684630..ba32f5b6eb 100644 --- a/crates/codegen/src/yul/mod.rs +++ b/crates/codegen/src/yul/mod.rs @@ -1 +1,4 @@ +pub mod isel; pub mod legalize; + +mod inst_order; diff --git a/crates/mir/bar.dot b/crates/mir/bar.dot new file mode 100644 index 0000000000..e69de29bb2 diff --git a/crates/mir/src/analysis/cfg.rs b/crates/mir/src/analysis/cfg.rs index 4f70d9c322..bf1f2a44fa 100644 --- a/crates/mir/src/analysis/cfg.rs +++ b/crates/mir/src/analysis/cfg.rs @@ -67,7 +67,7 @@ impl ControlFlowGraph { self.exits.push(block) } BranchInfo::Jump(dest) => self.add_edge(block, dest), - BranchInfo::Branch((then, else_)) => { + BranchInfo::Branch(_, then, else_) => { self.add_edge(block, then); self.add_edge(block, else_); } diff --git a/crates/mir/src/analysis/domtree.rs b/crates/mir/src/analysis/domtree.rs index 77220401f1..9775db6335 100644 --- a/crates/mir/src/analysis/domtree.rs +++ b/crates/mir/src/analysis/domtree.rs @@ -3,6 +3,8 @@ //! The algorithm is based on Keith D. Cooper., Timothy J. Harvey., and Ken //! Kennedy.: A Simple, Fast Dominance Algorithm: +use std::collections::BTreeSet; + use fxhash::FxHashMap; use crate::ir::BasicBlockId; @@ -120,6 +122,47 @@ impl DomTree { b1 } + + /// Compute dominance frontiers of each blocks. + pub fn compute_df(&self, cfg: &ControlFlowGraph) -> DFSet { + let mut df = DFSet::default(); + + for &block in &self.rpo { + let preds = cfg.preds(block); + if preds.len() < 2 { + continue; + } + + for pred in preds { + let mut runner = *pred; + while self.doms.get(&block) != Some(&runner) && self.is_reachable(runner) { + df.0.entry(runner).or_default().insert(block); + runner = self.doms[&runner]; + } + } + } + + df + } +} + +/// Dominance frontiers of each blocks. +#[derive(Default, Debug)] +pub struct DFSet(FxHashMap>); + +impl DFSet { + /// Returns all dominance frontieres of a `block`. + pub fn frontiers( + &self, + block: BasicBlockId, + ) -> Option + '_> { + self.0.get(&block).map(|set| set.iter().copied()) + } + + /// Returns number of frontier blocks of a `block`. + pub fn frontier_num(&self, block: BasicBlockId) -> usize { + self.0.get(&block).map(BTreeSet::len).unwrap_or(0) + } } #[cfg(test)] @@ -128,9 +171,11 @@ mod tests { use crate::ir::{body_builder::BodyBuilder, FunctionBody, FunctionId, SourceInfo, TypeId}; - fn calc_dom(func: &FunctionBody) -> DomTree { + fn calc_dom(func: &FunctionBody) -> (DomTree, DFSet) { let cfg = ControlFlowGraph::compute(func); - DomTree::compute(&cfg) + let domtree = DomTree::compute(&cfg); + let df = domtree.compute_df(&cfg); + (domtree, df) } fn body_builder() -> BodyBuilder { @@ -161,12 +206,24 @@ mod tests { let func = builder.build(); - let dom_tree = calc_dom(&func); + let (dom_tree, df) = calc_dom(&func); let entry_block = func.order.entry(); assert_eq!(dom_tree.idom(entry_block), None); assert_eq!(dom_tree.idom(then_block), Some(entry_block)); assert_eq!(dom_tree.idom(else_block), Some(entry_block)); assert_eq!(dom_tree.idom(merge_block), Some(entry_block)); + + assert_eq!(df.frontier_num(entry_block), 0); + assert_eq!(df.frontier_num(then_block), 1); + assert_eq!( + df.frontiers(then_block).unwrap().next().unwrap(), + merge_block + ); + assert_eq!( + df.frontiers(else_block).unwrap().next().unwrap(), + merge_block + ); + assert_eq!(df.frontier_num(merge_block), 0); } #[test] @@ -197,7 +254,7 @@ mod tests { let func = builder.build(); - let dom_tree = calc_dom(&func); + let (dom_tree, _) = calc_dom(&func); let entry_block = func.order.entry(); assert_eq!(dom_tree.idom(entry_block), None); assert_eq!(dom_tree.idom(block1), Some(entry_block)); @@ -267,7 +324,7 @@ mod tests { let func = builder.build(); - let dom_tree = calc_dom(&func); + let (dom_tree, _) = calc_dom(&func); let entry_block = func.order.entry(); assert_eq!(dom_tree.idom(entry_block), None); assert_eq!(dom_tree.idom(block1), Some(entry_block)); diff --git a/crates/mir/src/db/queries/function.rs b/crates/mir/src/db/queries/function.rs index 4a5d56baed..f1bba171d1 100644 --- a/crates/mir/src/db/queries/function.rs +++ b/crates/mir/src/db/queries/function.rs @@ -46,6 +46,11 @@ impl ir::FunctionId { self.analyzer_func(db).is_constructor(db.upcast()) } + pub fn name(&self, db: &dyn MirDb) -> SmolStr { + let analyzer_func = self.analyzer_func(db); + analyzer_func.name(db.upcast()) + } + /// Returns `class_name::fn_name` if a function is a method else `fn_name`. pub fn name_with_class(self, db: &dyn MirDb) -> SmolStr { let analyzer_func = self.analyzer_func(db); diff --git a/crates/mir/src/graphviz/block.rs b/crates/mir/src/graphviz/block.rs index f193f38e0c..38d840fc99 100644 --- a/crates/mir/src/graphviz/block.rs +++ b/crates/mir/src/graphviz/block.rs @@ -51,10 +51,10 @@ impl BlockNode { label::Text::HtmlStr(label.into()) } - pub(super) fn preds(self, db: &dyn MirDb) -> Vec { + pub(super) fn succs(self, db: &dyn MirDb) -> Vec { let func_body = self.func.body(db); let cfg = ControlFlowGraph::compute(&func_body); - cfg.preds(self.block) + cfg.succs(self.block) .iter() .map(|block| Self::new(self.func, *block)) .collect() diff --git a/crates/mir/src/graphviz/function.rs b/crates/mir/src/graphviz/function.rs index 4f2636c16a..85840e2c0e 100644 --- a/crates/mir/src/graphviz/function.rs +++ b/crates/mir/src/graphviz/function.rs @@ -2,7 +2,7 @@ use std::fmt::Write; use dot2::{label, Id}; -use crate::{db::MirDb, ir::FunctionId, pretty_print::PrettyPrint}; +use crate::{analysis::ControlFlowGraph, db::MirDb, ir::FunctionId, pretty_print::PrettyPrint}; use super::block::BlockNode; @@ -41,8 +41,9 @@ impl FunctionNode { pub(super) fn blocks(self, db: &dyn MirDb) -> Vec { let body = self.func.body(db); - body.order - .iter_block() + // We use control flow graph to collect reachable blocks. + let cfg = ControlFlowGraph::compute(&body); + cfg.post_order() .map(|block| BlockNode::new(self.func, block)) .collect() } diff --git a/crates/mir/src/graphviz/module.rs b/crates/mir/src/graphviz/module.rs index 8380f55c92..8882d97663 100644 --- a/crates/mir/src/graphviz/module.rs +++ b/crates/mir/src/graphviz/module.rs @@ -41,10 +41,10 @@ impl<'db> GraphWalk<'db> for ModuleGraph<'db> { let mut edges = vec![]; for func in self.db.mir_lower_module_all_functions(self.module).iter() { for block in FunctionNode::new(*func).blocks(self.db) { - for pred in block.preds(self.db) { + for succ in block.succs(self.db) { let edge = ModuleGraphEdge { - from: pred, - to: block, + from: block, + to: succ, }; edges.push(edge); } diff --git a/crates/mir/src/ir/body_order.rs b/crates/mir/src/ir/body_order.rs index 9028296e63..70df3cf76a 100644 --- a/crates/mir/src/ir/body_order.rs +++ b/crates/mir/src/ir/body_order.rs @@ -83,7 +83,6 @@ impl BodyOrder { /// # Panics /// Panics if /// 1. `block` is not inserted yet. - /// 2. No terminator found in a block. pub fn terminator(&self, store: &BodyDataStore, block: BasicBlockId) -> Option { let last_inst = self.last_inst(block)?; if store.is_terminator(last_inst) { diff --git a/crates/mir/src/ir/function.rs b/crates/mir/src/ir/function.rs index f4f02d8608..760dfc854b 100644 --- a/crates/mir/src/ir/function.rs +++ b/crates/mir/src/ir/function.rs @@ -7,7 +7,7 @@ use smol_str::SmolStr; use super::{ basic_block::BasicBlock, body_order::BodyOrder, - inst::{BranchInfo, Inst, InstId}, + inst::{BranchInfo, Inst, InstId, InstKind}, types::TypeId, value::{Immediate, Local, Value, ValueId}, BasicBlockId, SourceInfo, @@ -32,7 +32,7 @@ pub struct FunctionParam { } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct FunctionId(pub(crate) u32); +pub struct FunctionId(pub u32); impl_intern_key!(FunctionId); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -168,6 +168,25 @@ impl BodyDataStore { self.inst_results.get(&inst).copied() } + pub fn rewrite_branch_dest(&mut self, inst: InstId, from: BasicBlockId, to: BasicBlockId) { + match &mut self.inst_data_mut(inst).kind { + InstKind::Jump { dest } => { + if *dest == from { + *dest = to; + } + } + InstKind::Branch { then, else_, .. } => { + if *then == from { + *then = to; + } + if *else_ == from { + *else_ = to; + } + } + _ => unreachable!("inst is not a branch"), + } + } + pub fn remove_inst_result(&mut self, inst: InstId) { self.inst_results.remove(&inst); } diff --git a/crates/mir/src/ir/inst.rs b/crates/mir/src/ir/inst.rs index ea8b4520aa..e8ad3eed65 100644 --- a/crates/mir/src/ir/inst.rs +++ b/crates/mir/src/ir/inst.rs @@ -175,7 +175,7 @@ impl Inst { pub fn branch_info(&self) -> BranchInfo { match self.kind { InstKind::Jump { dest } => BranchInfo::Jump(dest), - InstKind::Branch { then, else_, .. } => BranchInfo::Branch((then, else_)), + InstKind::Branch { cond, then, else_ } => BranchInfo::Branch(cond, then, else_), _ => BranchInfo::NotBranch, } } @@ -526,5 +526,5 @@ impl From for YulIntrinsicOp { pub enum BranchInfo { NotBranch, Jump(BasicBlockId), - Branch((BasicBlockId, BasicBlockId)), + Branch(ValueId, BasicBlockId, BasicBlockId), } diff --git a/crates/mir/src/ir/types.rs b/crates/mir/src/ir/types.rs index 983e1864a0..c300e0be31 100644 --- a/crates/mir/src/ir/types.rs +++ b/crates/mir/src/ir/types.rs @@ -29,7 +29,7 @@ pub enum Type { /// An interned Id for [`ArrayDef`]. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct TypeId(pub(crate) u32); +pub struct TypeId(pub u32); impl_intern_key!(TypeId); /// A static array type definition. diff --git a/crates/mir/src/lower/function.rs b/crates/mir/src/lower/function.rs index 88bdebaa7a..0fa08967ec 100644 --- a/crates/mir/src/lower/function.rs +++ b/crates/mir/src/lower/function.rs @@ -126,7 +126,9 @@ impl<'db, 'a> BodyLowerHelper<'db, 'a> { } else { self.make_unit() }; - self.builder.ret(value, stmt.into()) + self.builder.ret(value, stmt.into()); + let next_block = self.builder.make_block(); + self.builder.move_to_block(next_block); } ast::FuncStmt::VarDecl { target, value, .. } => { @@ -182,25 +184,23 @@ impl<'db, 'a> BodyLowerHelper<'db, 'a> { ast::FuncStmt::For { target, iter, body } => self.lower_for_loop(target, iter, body), ast::FuncStmt::While { test, body } => { - let entry_bb = self.builder.make_block(); - let body_bb = self.builder.make_block(); + let header_bb = self.builder.make_block(); let exit_bb = self.builder.make_block(); - self.builder.jump(entry_bb, SourceInfo::dummy()); - - // Lower while entry. - self.builder.move_to_block(entry_bb); let cond = self.lower_expr(test); self.builder - .branch(cond, body_bb, exit_bb, SourceInfo::dummy()); + .branch(cond, header_bb, exit_bb, SourceInfo::dummy()); // Lower while body. - self.builder.move_to_block(body_bb); - self.enter_loop_scope(entry_bb, exit_bb); + self.builder.move_to_block(header_bb); + self.enter_loop_scope(header_bb, exit_bb); for stmt in body { self.lower_stmt(stmt); } - self.builder.jump(entry_bb, SourceInfo::dummy()); + let cond = self.lower_expr(test); + self.builder + .branch(cond, header_bb, exit_bb, SourceInfo::dummy()); + self.exit_scope(); // Move to while exit bb. @@ -257,16 +257,37 @@ impl<'db, 'a> BodyLowerHelper<'db, 'a> { // TODO: Generate appropriate error message. let arg = self.make_unit(); self.builder.revert(arg, stmt.into()); + let next_block = self.builder.make_block(); + self.builder.move_to_block(next_block); } ast::FuncStmt::Break => { let exit = self.scope().loop_exit(&self.scopes); - self.builder.jump(exit, stmt.into()) + self.builder.jump(exit, stmt.into()); + let next_block = self.builder.make_block(); + self.builder.move_to_block(next_block); } ast::FuncStmt::Continue => { let entry = self.scope().loop_entry(&self.scopes); - self.builder.jump(entry, stmt.into()) + if let Some(loop_idx) = self.scope().loop_idx(&self.scopes) { + let u256_ty = self.u256_ty(); + let imm_one = self.builder.make_imm(1u32.into(), u256_ty); + let inc = self + .builder + .add(loop_idx, imm_one, u256_ty, SourceInfo::dummy()); + self.builder.assign(loop_idx, inc, SourceInfo::dummy()); + let maximum_iter_count = self.scope().maximum_iter_count(&self.scopes).unwrap(); + let cond = self + .builder + .eq(loop_idx, maximum_iter_count, u256_ty, stmt.into()); + let exit = self.scope().loop_exit(&self.scopes); + self.builder.branch(cond, exit, entry, stmt.into()); + } else { + self.builder.jump(entry, stmt.into()) + } + let next_block = self.builder.make_block(); + self.builder.move_to_block(next_block); } ast::FuncStmt::Revert { error } => { @@ -277,6 +298,8 @@ impl<'db, 'a> BodyLowerHelper<'db, 'a> { }; self.builder.revert(error, stmt.into()); + let next_block = self.builder.make_block(); + self.builder.move_to_block(next_block); } ast::FuncStmt::Unsafe(stmts) => { @@ -342,10 +365,7 @@ impl<'db, 'a> BodyLowerHelper<'db, 'a> { for stmt in then { self.lower_stmt(stmt); } - let current_block = self.builder.current_block(); - if !self.builder.is_block_terminated(current_block) { - self.builder.jump(merge_bb, SourceInfo::dummy()); - } + self.builder.jump(merge_bb, SourceInfo::dummy()); self.builder.move_to_block(merge_bb); self.exit_scope(); } else { @@ -373,37 +393,17 @@ impl<'db, 'a> BodyLowerHelper<'db, 'a> { self.exit_scope(); let else_block_end_bb = self.builder.current_block(); - match ( - self.builder.is_block_terminated(then_block_end_bb), - self.builder.is_block_terminated(else_block_end_bb), - ) { - (true, true) => {} - (false, true) => { - let merge_bb = self.builder.make_block(); - self.builder.move_to_block(then_block_end_bb); - self.builder.jump(merge_bb, SourceInfo::dummy()); - self.builder.move_to_block(merge_bb); - } - (true, false) => { - let merge_bb = self.builder.make_block(); - self.builder.move_to_block(else_block_end_bb); - self.builder.jump(merge_bb, SourceInfo::dummy()); - self.builder.move_to_block(merge_bb); - } - (false, false) => { - let merge_bb = self.builder.make_block(); - self.builder.move_to_block(then_block_end_bb); - self.builder.jump(merge_bb, SourceInfo::dummy()); - self.builder.move_to_block(else_block_end_bb); - self.builder.jump(merge_bb, SourceInfo::dummy()); - self.builder.move_to_block(merge_bb); - } - } + let merge_bb = self.builder.make_block(); + self.builder.move_to_block(then_block_end_bb); + self.builder.jump(merge_bb, SourceInfo::dummy()); + self.builder.move_to_block(else_block_end_bb); + self.builder.jump(merge_bb, SourceInfo::dummy()); + self.builder.move_to_block(merge_bb); } } // NOTE: we assume a type of `iter` is array. - // TODO: Desugar to `loop` + `match` like rustc. + // TODO: Desugar to `loop` + `match` like rustc in HIR to generate better MIR. fn lower_for_loop( &mut self, loop_variable: &Node, @@ -412,7 +412,6 @@ impl<'db, 'a> BodyLowerHelper<'db, 'a> { ) { let preheader_bb = self.builder.make_block(); let entry_bb = self.builder.make_block(); - let body_bb = self.builder.make_block(); let exit_bb = self.builder.make_block(); let iter_elem_ty = self.analyzer_body.var_types[&loop_variable.id].clone(); let iter_elem_ty = self.db.mir_lowered_type(iter_elem_ty); @@ -434,18 +433,15 @@ impl<'db, 'a> BodyLowerHelper<'db, 'a> { self.scope_mut() .declare_var(&loop_variable.kind, loop_value); - // Creates index that stores loop iteration count. + // Declare and initialize `loop_idx` to 0. let u256_ty = self.u256_ty(); let loop_idx = Local::tmp_local("$loop_idx_tmp".into(), u256_ty); - - // Declare and initialize `loop_idx` to 0. let loop_idx = self.builder.declare(loop_idx); let imm_zero = self.builder.make_imm(0u32.into(), u256_ty); self.builder.assign(loop_idx, imm_zero, SourceInfo::dummy()); // Evaluates loop variable. let iter = self.lower_expr(iter); - self.builder.jump(entry_bb, SourceInfo::dummy()); // Create maximum loop count. let iter_ty = self.builder.value_ty(iter); @@ -454,36 +450,39 @@ impl<'db, 'a> BodyLowerHelper<'db, 'a> { _ => unreachable!(), }; let maximum_iter_count = self.builder.make_imm(maximum_iter_count.into(), u256_ty); - - /* Lower entry. */ - self.builder.move_to_block(entry_bb); - // if loop_idx == array length, then jump to loop exit. let cond = self .builder .eq(loop_idx, maximum_iter_count, u256_ty, SourceInfo::dummy()); self.builder - .branch(cond, exit_bb, body_bb, SourceInfo::dummy()); + .branch(cond, exit_bb, entry_bb, SourceInfo::dummy()); + self.scope_mut().loop_idx = Some(loop_idx); + self.scope_mut().maximum_iter_count = Some(maximum_iter_count); /* Lower body. */ - self.builder.move_to_block(body_bb); + self.builder.move_to_block(entry_bb); - // loop_variable = array[ioop_idx] + // loop_variable = array[loop_idx] let iter_elem = self.builder .aggregate_access(iter, vec![loop_idx], iter_elem_ty, SourceInfo::dummy()); self.builder .assign(loop_value, iter_elem, SourceInfo::dummy()); - // loop_idx+= 1 + + for stmt in body { + self.lower_stmt(stmt); + } + + // loop_idx += 1 let imm_one = self.builder.make_imm(1u32.into(), u256_ty); let inc = self .builder .add(loop_idx, imm_one, u256_ty, SourceInfo::dummy()); self.builder.assign(loop_idx, inc, SourceInfo::dummy()); - - for stmt in body { - self.lower_stmt(stmt); - } - self.builder.jump(entry_bb, SourceInfo::dummy()); + let cond = self + .builder + .eq(loop_idx, maximum_iter_count, u256_ty, SourceInfo::dummy()); + self.builder + .branch(cond, exit_bb, entry_bb, SourceInfo::dummy()); /* Move to exit bb */ self.exit_scope(); @@ -921,6 +920,9 @@ struct Scope { loop_entry: Option, loop_exit: Option, variables: FxHashMap, + // TODO: Remove the below two fields when `for` loop desugaring is implemented. + loop_idx: Option, + maximum_iter_count: Option, } impl Scope { @@ -930,6 +932,8 @@ impl Scope { loop_entry: None, loop_exit: None, variables: FxHashMap::default(), + loop_idx: None, + maximum_iter_count: None, }; // Declare function parameters. @@ -948,6 +952,8 @@ impl Scope { loop_entry: None, loop_exit: None, variables: FxHashMap::default(), + loop_idx: None, + maximum_iter_count: None, } } @@ -957,6 +963,8 @@ impl Scope { loop_entry: loop_entry.into(), loop_exit: loop_exit.into(), variables: FxHashMap::default(), + loop_idx: None, + maximum_iter_count: None, } } @@ -974,6 +982,20 @@ impl Scope { } } + fn loop_idx(&self, scopes: &Arena) -> Option { + match self.loop_idx { + Some(idx) => Some(idx), + None => scopes[self.parent?].loop_idx(scopes), + } + } + + fn maximum_iter_count(&self, scopes: &Arena) -> Option { + match self.maximum_iter_count { + Some(count) => Some(count), + None => scopes[self.parent?].maximum_iter_count(scopes), + } + } + fn declare_var(&mut self, name: &SmolStr, value: ValueId) { debug_assert!(!self.variables.contains_key(name)); diff --git a/crates/new_abi/src/function.rs b/crates/new_abi/src/function.rs index 5fa8dbae80..1ff7417d8e 100644 --- a/crates/new_abi/src/function.rs +++ b/crates/new_abi/src/function.rs @@ -4,7 +4,7 @@ use serde::Serialize; use super::types::AbiType; -#[derive(Debug, Clone, Serialize)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] pub struct AbiFunction { #[serde(rename = "type")] func_type: AbiFunctionType, @@ -86,7 +86,7 @@ impl AbiFunctionSelector { } } -#[derive(Debug, Clone, Serialize)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] struct AbiFunctionParamInner { name: String, #[serde(flatten)] diff --git a/crates/new_abi/src/types.rs b/crates/new_abi/src/types.rs index 8cf60dc60b..78bcdafe39 100644 --- a/crates/new_abi/src/types.rs +++ b/crates/new_abi/src/types.rs @@ -63,9 +63,9 @@ impl Serialize for AbiType { #[derive(Debug, Clone, PartialEq, Eq, Serialize)] pub struct AbiTupleField { - pub(crate) name: String, + name: String, #[serde(flatten)] - pub(crate) ty: AbiType, + ty: AbiType, } impl AbiTupleField { From c9c29ac4faedf681b5e01bb4a4b262356c05f2b0 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Thu, 10 Mar 2022 22:51:27 +0900 Subject: [PATCH 13/15] Implement Codegen --- Cargo.lock | 8 +- crates/analyzer/src/namespace/items.rs | 4 + .../analysis__data_copying_stress.snap | 17 +- .../snapshots/analysis__erc20_token.snap | 5 +- crates/bar.dot | 0 crates/codegen/Cargo.toml | 10 +- crates/codegen/src/db.rs | 76 +- crates/codegen/src/db/queries.rs | 3 + crates/codegen/src/db/queries/abi.rs | 171 +++- crates/codegen/src/db/queries/constant.rs | 12 + crates/codegen/src/db/queries/contract.rs | 20 + crates/codegen/src/db/queries/function.rs | 8 + crates/codegen/src/db/queries/types.rs | 124 +++ crates/codegen/src/yul/inst_order.rs | 309 ------- crates/codegen/src/yul/isel/context.rs | 78 ++ crates/codegen/src/yul/isel/contract.rs | 289 ++++++ crates/codegen/src/yul/isel/function.rs | 855 ++++++++++++++++++ crates/codegen/src/yul/isel/inst_order.rs | 830 +++++++++++++++++ crates/codegen/src/yul/isel/mod.rs | 30 +- crates/codegen/src/yul/legalize/body.rs | 175 +++- .../codegen/src/yul/legalize/critical_edge.rs | 2 +- crates/codegen/src/yul/legalize/signature.rs | 23 +- crates/codegen/src/yul/mod.rs | 24 +- crates/codegen/src/yul/runtime/abi.rs | 675 ++++++++++++++ crates/codegen/src/yul/runtime/contract.rs | 127 +++ crates/codegen/src/yul/runtime/data.rs | 410 +++++++++ crates/codegen/src/yul/runtime/emit.rs | 74 ++ crates/codegen/src/yul/runtime/mod.rs | 772 ++++++++++++++++ crates/codegen/src/yul/runtime/revert.rs | 83 ++ crates/codegen/src/yul/runtime/safe_math.rs | 628 +++++++++++++ crates/codegen/src/yul/slot_size.rs | 16 + crates/driver/Cargo.toml | 1 + crates/driver/src/lib.rs | 179 ++-- crates/fe/src/main.rs | 42 +- crates/mir/bar.dot | 0 crates/mir/src/db/queries/function.rs | 12 +- crates/mir/src/db/queries/types.rs | 309 ++++--- crates/mir/src/ir/body_builder.rs | 185 ++-- crates/mir/src/ir/body_cursor.rs | 12 +- crates/mir/src/ir/function.rs | 98 +- crates/mir/src/ir/inst.rs | 169 +++- crates/mir/src/ir/mod.rs | 2 +- crates/mir/src/ir/types.rs | 22 +- crates/mir/src/ir/value.rs | 99 +- crates/mir/src/lower/function.rs | 510 +++++++---- crates/mir/src/lower/types.rs | 108 +-- crates/mir/src/pretty_print/inst.rs | 39 +- crates/mir/src/pretty_print/types.rs | 57 +- crates/mir/src/pretty_print/value.rs | 40 +- crates/new_abi/Cargo.toml | 4 +- crates/new_abi/src/contract.rs | 2 +- crates/new_abi/src/event.rs | 14 +- crates/new_abi/src/function.rs | 1 + crates/new_abi/src/types.rs | 86 +- .../test-files/fixtures/demos/erc20_token.fe | 2 +- .../fixtures/stress/data_copying_stress.fe | 2 +- crates/test-utils/src/lib.rs | 12 +- crates/tests/src/crashes.rs | 2 +- crates/tests/src/demo_simple_open_auction.rs | 4 +- crates/tests/src/features.rs | 7 +- ...mpiler_tests__demo_erc20__erc20_token.snap | 31 +- ...ler_tests__demo_guestbook__guest_book.snap | 5 +- ...ple_open_auction__simple_open_auction.snap | 10 +- ...ts__demo_uniswap__uniswap_contracts-2.snap | 3 +- ...ests__demo_uniswap__uniswap_contracts.snap | 25 +- ..._tests__features__address_bytes10_map.snap | 9 +- ...fe_compiler_tests__features__balances.snap | 13 +- ..._compiler_tests__features__base_tuple.snap | 3 +- ...fe_compiler_tests__features__case_001.snap | 3 +- ...fe_compiler_tests__features__case_002.snap | 3 +- ...fe_compiler_tests__features__case_003.snap | 3 +- ...fe_compiler_tests__features__case_004.snap | 3 +- ...fe_compiler_tests__features__case_005.snap | 3 +- ...fe_compiler_tests__features__case_006.snap | 3 +- ...fe_compiler_tests__features__case_007.snap | 3 +- ...fe_compiler_tests__features__case_008.snap | 3 +- ...fe_compiler_tests__features__case_009.snap | 3 +- .../fe_compiler_tests__features__case_01.snap | 3 +- ...fe_compiler_tests__features__case_010.snap | 3 +- ...fe_compiler_tests__features__case_011.snap | 3 +- ...fe_compiler_tests__features__case_012.snap | 3 +- ...fe_compiler_tests__features__case_013.snap | 3 +- ...fe_compiler_tests__features__case_014.snap | 3 +- ...fe_compiler_tests__features__case_015.snap | 3 +- ...fe_compiler_tests__features__case_016.snap | 3 +- ...fe_compiler_tests__features__case_017.snap | 3 +- ...fe_compiler_tests__features__case_018.snap | 3 +- ...fe_compiler_tests__features__case_019.snap | 3 +- .../fe_compiler_tests__features__case_02.snap | 3 +- ...fe_compiler_tests__features__case_020.snap | 3 +- ...fe_compiler_tests__features__case_021.snap | 3 +- ...fe_compiler_tests__features__case_022.snap | 3 +- ...fe_compiler_tests__features__case_023.snap | 3 +- ...fe_compiler_tests__features__case_024.snap | 3 +- ...fe_compiler_tests__features__case_025.snap | 3 +- ...fe_compiler_tests__features__case_026.snap | 3 +- ...fe_compiler_tests__features__case_027.snap | 3 +- ...fe_compiler_tests__features__case_028.snap | 3 +- ...fe_compiler_tests__features__case_029.snap | 3 +- .../fe_compiler_tests__features__case_03.snap | 3 +- ...fe_compiler_tests__features__case_030.snap | 3 +- ...fe_compiler_tests__features__case_031.snap | 3 +- ...fe_compiler_tests__features__case_032.snap | 3 +- ...fe_compiler_tests__features__case_033.snap | 3 +- ...fe_compiler_tests__features__case_034.snap | 3 +- ...fe_compiler_tests__features__case_035.snap | 3 +- ...fe_compiler_tests__features__case_036.snap | 3 +- ...fe_compiler_tests__features__case_037.snap | 3 +- ...fe_compiler_tests__features__case_038.snap | 3 +- ...fe_compiler_tests__features__case_039.snap | 3 +- .../fe_compiler_tests__features__case_04.snap | 3 +- ...fe_compiler_tests__features__case_040.snap | 3 +- ...fe_compiler_tests__features__case_041.snap | 3 +- ...fe_compiler_tests__features__case_042.snap | 3 +- ...fe_compiler_tests__features__case_043.snap | 3 +- ...fe_compiler_tests__features__case_044.snap | 3 +- ...fe_compiler_tests__features__case_045.snap | 3 +- ...fe_compiler_tests__features__case_046.snap | 3 +- ...fe_compiler_tests__features__case_047.snap | 3 +- ...fe_compiler_tests__features__case_048.snap | 3 +- ...fe_compiler_tests__features__case_049.snap | 3 +- .../fe_compiler_tests__features__case_05.snap | 3 +- ...fe_compiler_tests__features__case_050.snap | 3 +- ...fe_compiler_tests__features__case_051.snap | 3 +- ...fe_compiler_tests__features__case_052.snap | 3 +- ...fe_compiler_tests__features__case_053.snap | 3 +- ...fe_compiler_tests__features__case_054.snap | 3 +- ...fe_compiler_tests__features__case_055.snap | 3 +- ...fe_compiler_tests__features__case_056.snap | 3 +- ...fe_compiler_tests__features__case_057.snap | 3 +- ...fe_compiler_tests__features__case_058.snap | 3 +- ...fe_compiler_tests__features__case_059.snap | 3 +- .../fe_compiler_tests__features__case_06.snap | 3 +- ...fe_compiler_tests__features__case_060.snap | 3 +- ...fe_compiler_tests__features__case_061.snap | 3 +- ...fe_compiler_tests__features__case_062.snap | 3 +- ...fe_compiler_tests__features__case_063.snap | 3 +- ...fe_compiler_tests__features__case_064.snap | 3 +- ...fe_compiler_tests__features__case_065.snap | 3 +- ...fe_compiler_tests__features__case_066.snap | 3 +- ...fe_compiler_tests__features__case_067.snap | 3 +- ...fe_compiler_tests__features__case_068.snap | 3 +- ...fe_compiler_tests__features__case_069.snap | 3 +- .../fe_compiler_tests__features__case_07.snap | 3 +- ...fe_compiler_tests__features__case_070.snap | 3 +- ...fe_compiler_tests__features__case_071.snap | 3 +- ...fe_compiler_tests__features__case_072.snap | 3 +- ...fe_compiler_tests__features__case_073.snap | 3 +- ...fe_compiler_tests__features__case_074.snap | 3 +- ...fe_compiler_tests__features__case_075.snap | 3 +- ...fe_compiler_tests__features__case_076.snap | 3 +- ...fe_compiler_tests__features__case_077.snap | 3 +- ...fe_compiler_tests__features__case_078.snap | 3 +- ...fe_compiler_tests__features__case_079.snap | 3 +- .../fe_compiler_tests__features__case_08.snap | 3 +- ...fe_compiler_tests__features__case_080.snap | 3 +- ...fe_compiler_tests__features__case_081.snap | 3 +- ...fe_compiler_tests__features__case_082.snap | 3 +- ...fe_compiler_tests__features__case_083.snap | 3 +- ...fe_compiler_tests__features__case_084.snap | 3 +- ...fe_compiler_tests__features__case_085.snap | 3 +- ...fe_compiler_tests__features__case_086.snap | 3 +- ...fe_compiler_tests__features__case_087.snap | 3 +- ...fe_compiler_tests__features__case_088.snap | 3 +- ...fe_compiler_tests__features__case_089.snap | 3 +- .../fe_compiler_tests__features__case_09.snap | 3 +- ...fe_compiler_tests__features__case_090.snap | 3 +- ...fe_compiler_tests__features__case_091.snap | 3 +- ...fe_compiler_tests__features__case_092.snap | 3 +- ...fe_compiler_tests__features__case_093.snap | 3 +- ...fe_compiler_tests__features__case_094.snap | 3 +- ...fe_compiler_tests__features__case_095.snap | 3 +- ...fe_compiler_tests__features__case_096.snap | 3 +- ...fe_compiler_tests__features__case_097.snap | 3 +- ...fe_compiler_tests__features__case_098.snap | 3 +- ...fe_compiler_tests__features__case_099.snap | 3 +- ...fe_compiler_tests__features__case_1-2.snap | 3 +- ...fe_compiler_tests__features__case_1-3.snap | 9 +- .../fe_compiler_tests__features__case_1.snap | 3 +- .../fe_compiler_tests__features__case_10.snap | 3 +- ...fe_compiler_tests__features__case_100.snap | 3 +- ...fe_compiler_tests__features__case_101.snap | 3 +- ...fe_compiler_tests__features__case_102.snap | 3 +- ...fe_compiler_tests__features__case_103.snap | 3 +- ...fe_compiler_tests__features__case_104.snap | 3 +- ...fe_compiler_tests__features__case_105.snap | 3 +- ...fe_compiler_tests__features__case_106.snap | 3 +- ...fe_compiler_tests__features__case_107.snap | 3 +- ...fe_compiler_tests__features__case_108.snap | 3 +- ...fe_compiler_tests__features__case_109.snap | 3 +- .../fe_compiler_tests__features__case_11.snap | 3 +- ...fe_compiler_tests__features__case_110.snap | 3 +- ...fe_compiler_tests__features__case_111.snap | 3 +- ...fe_compiler_tests__features__case_112.snap | 3 +- ...fe_compiler_tests__features__case_113.snap | 3 +- ...fe_compiler_tests__features__case_114.snap | 3 +- ...fe_compiler_tests__features__case_115.snap | 3 +- ...fe_compiler_tests__features__case_116.snap | 3 +- ...fe_compiler_tests__features__case_117.snap | 3 +- ...fe_compiler_tests__features__case_118.snap | 3 +- ...fe_compiler_tests__features__case_119.snap | 3 +- .../fe_compiler_tests__features__case_12.snap | 3 +- ...fe_compiler_tests__features__case_120.snap | 3 +- ...fe_compiler_tests__features__case_121.snap | 3 +- ...fe_compiler_tests__features__case_122.snap | 3 +- ...fe_compiler_tests__features__case_123.snap | 3 +- ...fe_compiler_tests__features__case_124.snap | 3 +- ...fe_compiler_tests__features__case_125.snap | 3 +- ...fe_compiler_tests__features__case_126.snap | 3 +- ...fe_compiler_tests__features__case_127.snap | 3 +- ...fe_compiler_tests__features__case_128.snap | 3 +- ...fe_compiler_tests__features__case_129.snap | 3 +- .../fe_compiler_tests__features__case_13.snap | 3 +- ...fe_compiler_tests__features__case_130.snap | 3 +- ...fe_compiler_tests__features__case_131.snap | 3 +- ...fe_compiler_tests__features__case_132.snap | 3 +- ...fe_compiler_tests__features__case_133.snap | 3 +- ...fe_compiler_tests__features__case_134.snap | 3 +- ...fe_compiler_tests__features__case_135.snap | 3 +- ...fe_compiler_tests__features__case_136.snap | 3 +- ...fe_compiler_tests__features__case_137.snap | 3 +- ...fe_compiler_tests__features__case_138.snap | 3 +- ...fe_compiler_tests__features__case_139.snap | 3 +- ...fe_compiler_tests__features__case_140.snap | 3 +- ...fe_compiler_tests__features__case_141.snap | 3 +- ...fe_compiler_tests__features__case_142.snap | 3 +- ...fe_compiler_tests__features__case_143.snap | 3 +- ...fe_compiler_tests__features__case_144.snap | 3 +- ...fe_compiler_tests__features__case_145.snap | 3 +- ...fe_compiler_tests__features__case_146.snap | 3 +- ...fe_compiler_tests__features__case_147.snap | 3 +- ...fe_compiler_tests__features__case_148.snap | 3 +- ...fe_compiler_tests__features__case_149.snap | 3 +- ...fe_compiler_tests__features__case_150.snap | 3 +- ...fe_compiler_tests__features__case_151.snap | 3 +- ...fe_compiler_tests__features__case_152.snap | 3 +- ...fe_compiler_tests__features__case_153.snap | 3 +- ...fe_compiler_tests__features__case_154.snap | 3 +- ...fe_compiler_tests__features__case_155.snap | 3 +- ...fe_compiler_tests__features__case_156.snap | 3 +- ...fe_compiler_tests__features__case_157.snap | 3 +- ...r_tests__features__case_158_map_tuple.snap | 3 +- ...atures__case_159_int_literal_coercion.snap | 3 +- ...ts__features__case_160_associated_fns.snap | 3 +- ..._tests__features__case_161_struct_fns.snap | 3 +- ...atures__case_162_cast_address_to_u256.snap | 3 +- ...fe_compiler_tests__features__case_2-2.snap | 3 +- ...fe_compiler_tests__features__case_2-3.snap | 9 +- .../fe_compiler_tests__features__case_2.snap | 3 +- .../fe_compiler_tests__features__case_3.snap | 9 +- .../fe_compiler_tests__features__case_4.snap | 9 +- .../fe_compiler_tests__features__case_5.snap | 9 +- .../fe_compiler_tests__features__case_6.snap | 9 +- ...r_tests__features__checked_arithmetic.snap | 217 +++-- ...compiler_tests__features__constructor.snap | 3 +- ...ler_tests__features__create2_contract.snap | 3 +- ...iler_tests__features__create_contract.snap | 3 +- ...__features__create_contract_from_init.snap | 3 +- ...eatures__ctx_param_external_func_call.snap | 5 +- ...eatures__ctx_param_internal_func_call.snap | 3 +- ...ler_tests__features__ctx_param_simple.snap | 3 +- .../fe_compiler_tests__features__events.snap | 9 +- ...er_tests__features__external_contract.snap | 5 +- ..._compiler_tests__features__intrinsics.snap | 15 +- .../fe_compiler_tests__features__keccak.snap | 11 +- .../fe_compiler_tests__features__math.snap | 5 +- ...compiler_tests__features__multi_param.snap | 3 +- ..._compiler_tests__features__nested_map.snap | 25 +- ...mpiler_tests__features__numeric_sizes.snap | 49 +- ...ompiler_tests__features__return_array.snap | 3 +- ...__features__return_builtin_attributes.snap | 21 +- ..._compiler_tests__features__send_value.snap | 3 +- ...mpiler_tests__features__short_circuit.snap | 7 +- ...er_tests__features__sized_vals_in_sto.snap | 15 +- .../fe_compiler_tests__features__strings.snap | 9 +- .../fe_compiler_tests__features__structs.snap | 19 +- ..._tests__features__tuple_destructuring.snap | 5 +- ...mpiler_tests__features__two_contracts.snap | 3 +- ...er_tests__stress__abi_encoding_stress.snap | 31 +- ...er_tests__stress__data_copying_stress.snap | 23 +- ..._tests__stress__external_calls_stress.snap | 21 +- ..._compiler_tests__stress__tuple_stress.snap | 19 +- crates/yulc/src/lib.rs | 2 + crates/yulgen/src/runtime/abi_dispatcher.rs | 4 +- 284 files changed, 7278 insertions(+), 1837 deletions(-) delete mode 100644 crates/bar.dot create mode 100644 crates/codegen/src/db/queries/constant.rs create mode 100644 crates/codegen/src/db/queries/contract.rs create mode 100644 crates/codegen/src/db/queries/types.rs delete mode 100644 crates/codegen/src/yul/inst_order.rs create mode 100644 crates/codegen/src/yul/isel/context.rs create mode 100644 crates/codegen/src/yul/isel/contract.rs create mode 100644 crates/codegen/src/yul/isel/function.rs create mode 100644 crates/codegen/src/yul/isel/inst_order.rs create mode 100644 crates/codegen/src/yul/runtime/abi.rs create mode 100644 crates/codegen/src/yul/runtime/contract.rs create mode 100644 crates/codegen/src/yul/runtime/data.rs create mode 100644 crates/codegen/src/yul/runtime/emit.rs create mode 100644 crates/codegen/src/yul/runtime/mod.rs create mode 100644 crates/codegen/src/yul/runtime/revert.rs create mode 100644 crates/codegen/src/yul/runtime/safe_math.rs create mode 100644 crates/codegen/src/yul/slot_size.rs delete mode 100644 crates/mir/bar.dot diff --git a/Cargo.lock b/Cargo.lock index 7163783dd6..424c562af4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -560,6 +560,7 @@ version = "0.16.0-alpha" dependencies = [ "fe-analyzer", "fe-common", + "fe-parser", "serde", "serde_json", ] @@ -594,12 +595,14 @@ dependencies = [ [[package]] name = "fe-codegen" -version = "0.14.0-alpha" +version = "0.16.0-alpha" dependencies = [ + "fe-analyzer", "fe-common", "fe-mir", "fe-new_abi", "fxhash", + "num-bigint", "salsa", "smol_str", "yultsur", @@ -680,6 +683,7 @@ version = "0.16.0-alpha" dependencies = [ "fe-abi", "fe-analyzer", + "fe-codegen", "fe-common", "fe-lowering", "fe-mir", @@ -737,7 +741,7 @@ dependencies = [ [[package]] name = "fe-new_abi" -version = "0.14.0-alpha" +version = "0.16.0-alpha" dependencies = [ "fe-common", "serde", diff --git a/crates/analyzer/src/namespace/items.rs b/crates/analyzer/src/namespace/items.rs index 37c5c88616..b669042952 100644 --- a/crates/analyzer/src/namespace/items.rs +++ b/crates/analyzer/src/namespace/items.rs @@ -1223,6 +1223,10 @@ impl FunctionId { sink.push_all(db.function_signature(*self).diagnostics.iter()); sink.push_all(db.function_body(*self).diagnostics.iter()); } + + pub fn is_contract_func(self, db: &dyn AnalyzerDb) -> bool { + matches! {self.parent(db), Item::Type(TypeDef::Contract(_))} + } } /// A `Class` is an item that can have member functions. diff --git a/crates/analyzer/tests/snapshots/analysis__data_copying_stress.snap b/crates/analyzer/tests/snapshots/analysis__data_copying_stress.snap index 924b089d46..648a537a96 100644 --- a/crates/analyzer/tests/snapshots/analysis__data_copying_stress.snap +++ b/crates/analyzer/tests/snapshots/analysis__data_copying_stress.snap @@ -1,7 +1,6 @@ --- source: crates/analyzer/tests/analysis.rs expression: "build_snapshot(&db, module)" - --- note: ┌─ data_copying_stress.fe:4:5 @@ -732,7 +731,7 @@ note: 71 │ │ emit_my_event_internal( 72 │ │ ctx, 73 │ │ self.my_string.to_mem(), -74 │ │ self.my_u256.to_mem() +74 │ │ self.my_u256 75 │ │ ) │ ╰─────────^ attributes hash: 1731341862738941170 │ @@ -783,20 +782,14 @@ note: │ 73 │ self.my_string.to_mem(), │ ^^^^^^^^^^^^^^^^^^^^^^^ String<42>: Storage { nonce: Some(0) } => Memory -74 │ self.my_u256.to_mem() +74 │ self.my_u256 │ ^^^^ Foo: Value note: ┌─ data_copying_stress.fe:74:13 │ -74 │ self.my_u256.to_mem() - │ ^^^^^^^^^^^^ u256: Storage { nonce: Some(2) } - -note: - ┌─ data_copying_stress.fe:74:13 - │ -74 │ self.my_u256.to_mem() - │ ^^^^^^^^^^^^^^^^^^^^^ u256: Storage { nonce: Some(2) } => Value +74 │ self.my_u256 + │ ^^^^^^^^^^^^ u256: Storage { nonce: Some(2) } => Value note: ┌─ data_copying_stress.fe:71:9 @@ -804,7 +797,7 @@ note: 71 │ â•­ emit_my_event_internal( 72 │ │ ctx, 73 │ │ self.my_string.to_mem(), -74 │ │ self.my_u256.to_mem() +74 │ │ self.my_u256 75 │ │ ) │ ╰─────────^ (): Value diff --git a/crates/analyzer/tests/snapshots/analysis__erc20_token.snap b/crates/analyzer/tests/snapshots/analysis__erc20_token.snap index 7021b3f67a..b70a6a1803 100644 --- a/crates/analyzer/tests/snapshots/analysis__erc20_token.snap +++ b/crates/analyzer/tests/snapshots/analysis__erc20_token.snap @@ -1,7 +1,6 @@ --- source: crates/analyzer/tests/analysis.rs expression: "build_snapshot(&db, module)" - --- note: ┌─ erc20_token.fe:4:5 @@ -1790,8 +1789,8 @@ note: ┌─ erc20_token.fe:98:5 │ 98 │ â•­ fn _before_token_transfer(from: address, to: address, _ value: u256): -99 │ │ pass - │ ╰────────────^ attributes hash: 10552855728897650120 +99 │ │ return + │ ╰──────────────^ attributes hash: 10552855728897650120 │ = FunctionSignature { self_decl: None, diff --git a/crates/bar.dot b/crates/bar.dot deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/crates/codegen/Cargo.toml b/crates/codegen/Cargo.toml index 31f40afbb3..9edf15271c 100644 --- a/crates/codegen/Cargo.toml +++ b/crates/codegen/Cargo.toml @@ -1,14 +1,16 @@ [package] name = "fe-codegen" -version = "0.14.0-alpha" +version = "0.16.0-alpha" authors = ["The Fe Developers "] edition = "2021" [dependencies] -fe-mir = { path = "../mir", version = "^0.14.0-alpha" } -fe-common = { path = "../common", version = "^0.14.0-alpha" } -fe-new_abi = { path = "../new_abi", version = "^0.14.0-alpha" } +fe-analyzer = { path = "../analyzer", version = "^0.16.0-alpha"} +fe-mir = { path = "../mir", version = "^0.16.0-alpha" } +fe-common = { path = "../common", version = "^0.16.0-alpha" } +fe-new_abi = { path = "../new_abi", version = "^0.16.0-alpha" } salsa = "0.16.1" +num-bigint = "0.4.3" fxhash = "0.2.1" smol_str = "0.1.21" yultsur = { git = "https://github.com/g-r-a-n-t/yultsur", rev = "ae85470" } \ No newline at end of file diff --git a/crates/codegen/src/db.rs b/crates/codegen/src/db.rs index 835697d507..16ee725ed5 100644 --- a/crates/codegen/src/db.rs +++ b/crates/codegen/src/db.rs @@ -1,11 +1,12 @@ use std::rc::Rc; -use fe_common::db::{Upcast, UpcastMut}; +use fe_analyzer::{db::AnalyzerDbStorage, namespace::items::ContractId, AnalyzerDb}; +use fe_common::db::{SourceDb, SourceDbStorage, Upcast, UpcastMut}; use fe_mir::{ - db::MirDb, + db::{MirDb, MirDbStorage}, ir::{FunctionBody, FunctionId, FunctionSignature, TypeId}, }; -use fe_new_abi::{function::AbiFunction, types::AbiType}; +use fe_new_abi::{contract::AbiContract, event::AbiEvent, function::AbiFunction, types::AbiType}; mod queries; @@ -15,9 +16,76 @@ pub trait CodegenDb: MirDb + Upcast + UpcastMut { fn codegen_legalized_signature(&self, function_id: FunctionId) -> Rc; #[salsa::invoke(queries::function::legalized_body)] fn codegen_legalized_body(&self, function_id: FunctionId) -> Rc; + #[salsa::invoke(queries::function::symbol_name)] + fn codegen_function_symbol_name(&self, function_id: FunctionId) -> Rc; + + #[salsa::invoke(queries::types::legalized_type)] + fn codegen_legalized_type(&self, ty: TypeId) -> TypeId; #[salsa::invoke(queries::abi::abi_type)] fn codegen_abi_type(&self, ty: TypeId) -> AbiType; #[salsa::invoke(queries::abi::abi_function)] - fn codegen_abi_function(&self, function_id: FunctionId) -> Rc; + fn codegen_abi_function(&self, function_id: FunctionId) -> AbiFunction; + #[salsa::invoke(queries::abi::abi_event)] + fn codegen_abi_event(&self, ty: TypeId) -> AbiEvent; + #[salsa::invoke(queries::abi::abi_contract)] + fn codegen_abi_contract(&self, contract: ContractId) -> AbiContract; + #[salsa::invoke(queries::abi::abi_type_maximum_size)] + fn codegen_abi_type_maximum_size(&self, ty: TypeId) -> usize; + #[salsa::invoke(queries::abi::abi_function_argument_maximum_size)] + fn codegen_abi_function_argument_maximum_size(&self, contract: FunctionId) -> usize; + #[salsa::invoke(queries::abi::abi_function_return_maximum_size)] + fn codegen_abi_function_return_maximum_size(&self, function: FunctionId) -> usize; + + #[salsa::invoke(queries::contract::symbol_name)] + fn codegen_contract_symbol_name(&self, contract: ContractId) -> Rc; + #[salsa::invoke(queries::contract::deployer_symbol_name)] + fn codegen_contract_deployer_symbol_name(&self, contract: ContractId) -> Rc; + + #[salsa::invoke(queries::constant::string_symbol_name)] + fn codegen_constant_string_symbol_name(&self, data: String) -> Rc; +} + +// TODO: Move this to driver. +#[salsa::database(SourceDbStorage, AnalyzerDbStorage, MirDbStorage, CodegenDbStorage)] +#[derive(Default)] +pub struct NewDb { + storage: salsa::Storage, +} +impl salsa::Database for NewDb {} + +impl Upcast for NewDb { + fn upcast(&self) -> &(dyn MirDb + 'static) { + &*self + } +} + +impl UpcastMut for NewDb { + fn upcast_mut(&mut self) -> &mut (dyn MirDb + 'static) { + &mut *self + } +} + +impl Upcast for NewDb { + fn upcast(&self) -> &(dyn SourceDb + 'static) { + &*self + } +} + +impl UpcastMut for NewDb { + fn upcast_mut(&mut self) -> &mut (dyn SourceDb + 'static) { + &mut *self + } +} + +impl Upcast for NewDb { + fn upcast(&self) -> &(dyn AnalyzerDb + 'static) { + &*self + } +} + +impl UpcastMut for NewDb { + fn upcast_mut(&mut self) -> &mut (dyn AnalyzerDb + 'static) { + &mut *self + } } diff --git a/crates/codegen/src/db/queries.rs b/crates/codegen/src/db/queries.rs index db68776cbe..31cca43870 100644 --- a/crates/codegen/src/db/queries.rs +++ b/crates/codegen/src/db/queries.rs @@ -1,2 +1,5 @@ pub mod abi; +pub mod constant; +pub mod contract; pub mod function; +pub mod types; diff --git a/crates/codegen/src/db/queries/abi.rs b/crates/codegen/src/db/queries/abi.rs index 2720503cc0..19d5113b2c 100644 --- a/crates/codegen/src/db/queries/abi.rs +++ b/crates/codegen/src/db/queries/abi.rs @@ -1,14 +1,42 @@ -use std::rc::Rc; - +use fe_analyzer::namespace::items::ContractId; use fe_mir::ir::{self, FunctionId, TypeId}; use fe_new_abi::{ + contract::AbiContract, + event::{AbiEvent, AbiEventField}, function::{AbiFunction, AbiFunctionType}, types::{AbiTupleField, AbiType}, }; use crate::db::CodegenDb; -pub fn abi_function(db: &dyn CodegenDb, function: FunctionId) -> Rc { +pub fn abi_contract(db: &dyn CodegenDb, contract: ContractId) -> AbiContract { + let mut funcs = vec![]; + + if let Some(init) = contract.init_function(db.upcast()) { + let init_func = db.mir_lowered_func_signature(init); + let init_abi = db.codegen_abi_function(init_func); + funcs.push(init_abi); + } + + for &func in contract.all_functions(db.upcast()).as_ref() { + let mir_func = db.mir_lowered_func_signature(func); + if mir_func.linkage(db.upcast()).is_exported() { + let func_abi = db.codegen_abi_function(mir_func); + funcs.push(func_abi); + } + } + + let mut events = vec![]; + for &event in db.contract_all_events(contract).as_ref() { + let mir_event = db.mir_lowered_event_type(event); + let event = db.codegen_abi_event(mir_event); + events.push(event); + } + + AbiContract::new(funcs, events) +} + +pub fn abi_function(db: &dyn CodegenDb, function: FunctionId) -> AbiFunction { // We use a legalized signature. let sig = db.codegen_legalized_signature(function); @@ -20,41 +48,88 @@ pub fn abi_function(db: &dyn CodegenDb, function: FunctionId) -> Rc .collect(); let ret_ty = sig.return_type.map(|ty| db.codegen_abi_type(ty)); - AbiFunction::new(AbiFunctionType::Function, name.to_string(), args, ret_ty).into() + let func_type = if function.is_contract_init(db.upcast()) { + AbiFunctionType::Constructor + } else { + AbiFunctionType::Function + }; + + AbiFunction::new(func_type, name.to_string(), args, ret_ty) +} + +pub fn abi_function_argument_maximum_size(db: &dyn CodegenDb, function: FunctionId) -> usize { + let sig = db.codegen_legalized_signature(function); + sig.params.iter().fold(0, |acc, param| { + acc + db.codegen_abi_type_maximum_size(param.ty) + }) +} + +pub fn abi_function_return_maximum_size(db: &dyn CodegenDb, function: FunctionId) -> usize { + let sig = db.codegen_legalized_signature(function); + sig.return_type + .map(|ty| db.codegen_abi_type_maximum_size(ty)) + .unwrap_or_default() +} + +pub fn abi_type_maximum_size(db: &dyn CodegenDb, ty: TypeId) -> usize { + let abi_type = db.codegen_abi_type(ty); + if abi_type.is_static() { + abi_type.header_size() + } else { + match &ty.data(db.upcast()).kind { + ir::TypeKind::Array(def) => { + debug_assert! {matches!(def.elem_ty.data + (db.upcast()).kind, ir::TypeKind::U8), + } + abi_type.header_size() + 32 + ceil_32(def.len) + } + ir::TypeKind::String(len) => abi_type.header_size() + 32 + ceil_32(*len), + _ => unreachable!(), + } + } } pub fn abi_type(db: &dyn CodegenDb, ty: TypeId) -> AbiType { - if ty.is_zero_sized(db.upcast()) { + let legalized_ty = db.codegen_legalized_type(ty); + + if legalized_ty.is_zero_sized(db.upcast()) { unreachable!("zero-sized type must be removed in legalization"); } - let ty = ty.data(db.upcast()); - - match ty.as_ref() { - ir::Type::I8 => AbiType::Int(8), - ir::Type::I16 => AbiType::Int(16), - ir::Type::I32 => AbiType::Int(32), - ir::Type::I64 => AbiType::Int(64), - ir::Type::I128 => AbiType::Int(128), - ir::Type::I256 => AbiType::Int(256), - ir::Type::U8 => AbiType::UInt(8), - ir::Type::U16 => AbiType::UInt(16), - ir::Type::U32 => AbiType::UInt(32), - ir::Type::U64 => AbiType::UInt(64), - ir::Type::U128 => AbiType::UInt(128), - ir::Type::U256 => AbiType::UInt(256), - ir::Type::Bool => AbiType::Bool, - ir::Type::Address => AbiType::Address, - ir::Type::Unit => unreachable!("zero-sized type must be removed in legalization"), - ir::Type::Array(def) => { - let elem_ty = db.codegen_abi_type(def.elem_ty); - let len = def.len; - AbiType::Array { - elem_ty: elem_ty.into(), - len, + let ty_data = legalized_ty.data(db.upcast()); + + match &ty_data.kind { + ir::TypeKind::I8 => AbiType::Int(8), + ir::TypeKind::I16 => AbiType::Int(16), + ir::TypeKind::I32 => AbiType::Int(32), + ir::TypeKind::I64 => AbiType::Int(64), + ir::TypeKind::I128 => AbiType::Int(128), + ir::TypeKind::I256 => AbiType::Int(256), + ir::TypeKind::U8 => AbiType::UInt(8), + ir::TypeKind::U16 => AbiType::UInt(16), + ir::TypeKind::U32 => AbiType::UInt(32), + ir::TypeKind::U64 => AbiType::UInt(64), + ir::TypeKind::U128 => AbiType::UInt(128), + ir::TypeKind::U256 => AbiType::UInt(256), + ir::TypeKind::Bool => AbiType::Bool, + ir::TypeKind::Address => AbiType::Address, + ir::TypeKind::String(_) => AbiType::String, + ir::TypeKind::Unit => unreachable!("zero-sized type must be removed in legalization"), + ir::TypeKind::Array(def) => { + let elem_ty_data = &def.elem_ty.data(db.upcast()); + match &elem_ty_data.kind { + ir::TypeKind::U8 => AbiType::Bytes, + _ => { + let elem_ty = db.codegen_abi_type(def.elem_ty); + let len = def.len; + AbiType::Array { + elem_ty: elem_ty.into(), + len, + } + } } } - ir::Type::Tuple(def) => { + ir::TypeKind::Tuple(def) => { let fields = def .items .iter() @@ -67,7 +142,7 @@ pub fn abi_type(db: &dyn CodegenDb, ty: TypeId) -> AbiType { AbiType::Tuple(fields) } - ir::Type::Struct(def) => { + ir::TypeKind::Struct(def) => { let fields = def .fields .iter() @@ -79,7 +154,37 @@ pub fn abi_type(db: &dyn CodegenDb, ty: TypeId) -> AbiType { AbiType::Tuple(fields) } - ir::Type::Event(_) | ir::Type::Contract(_) => unreachable!(), - ir::Type::Map(_) => todo!("map type can't be used in parameter or return type"), + + ir::TypeKind::Event(_) + | ir::TypeKind::Contract(_) + | ir::TypeKind::Map(_) + | ir::TypeKind::MPtr(_) + | ir::TypeKind::SPtr(_) => unreachable!(), } } + +pub fn abi_event(db: &dyn CodegenDb, ty: TypeId) -> AbiEvent { + debug_assert!(ty.is_event(db.upcast())); + let legalized_ty = db.codegen_legalized_type(ty); + + let legalized_ty_data = legalized_ty.data(db.upcast()); + let event_def = match &legalized_ty_data.kind { + ir::TypeKind::Event(def) => def, + _ => unreachable!(), + }; + + let fields = event_def + .fields + .iter() + .map(|(name, ty, indexed)| { + let ty = db.codegen_abi_type(*ty); + AbiEventField::new(name.to_string(), ty, *indexed) + }) + .collect(); + + AbiEvent::new(event_def.name.to_string(), fields, false) +} + +fn ceil_32(value: usize) -> usize { + ((value + 31) / 32) * 32 +} diff --git a/crates/codegen/src/db/queries/constant.rs b/crates/codegen/src/db/queries/constant.rs new file mode 100644 index 0000000000..2a78aba9b4 --- /dev/null +++ b/crates/codegen/src/db/queries/constant.rs @@ -0,0 +1,12 @@ +use crate::db::CodegenDb; +use std::{ + collections::hash_map::DefaultHasher, + hash::{Hash, Hasher}, + rc::Rc, +}; + +pub fn string_symbol_name(_db: &dyn CodegenDb, data: String) -> Rc { + let mut hasher = DefaultHasher::new(); + data.hash(&mut hasher); + format! {"{}", hasher.finish()}.into() +} diff --git a/crates/codegen/src/db/queries/contract.rs b/crates/codegen/src/db/queries/contract.rs new file mode 100644 index 0000000000..be0002a371 --- /dev/null +++ b/crates/codegen/src/db/queries/contract.rs @@ -0,0 +1,20 @@ +use std::rc::Rc; + +use fe_analyzer::namespace::items::ContractId; + +use crate::db::CodegenDb; + +pub fn symbol_name(db: &dyn CodegenDb, contract: ContractId) -> Rc { + let module = contract.module(db.upcast()); + + format!( + "{}${}", + module.name(db.upcast()), + contract.name(db.upcast()) + ) + .into() +} + +pub fn deployer_symbol_name(db: &dyn CodegenDb, contract: ContractId) -> Rc { + format!("deploy_{}", symbol_name(db, contract).as_ref()).into() +} diff --git a/crates/codegen/src/db/queries/function.rs b/crates/codegen/src/db/queries/function.rs index 4878dd4be8..53ab8cc158 100644 --- a/crates/codegen/src/db/queries/function.rs +++ b/crates/codegen/src/db/queries/function.rs @@ -15,3 +15,11 @@ pub fn legalized_body(db: &dyn CodegenDb, function: FunctionId) -> Rc Rc { + let module = function.signature(db.upcast()).module_id; + let module_name = module.name(db.upcast()); + let func_name = function.name_with_class(db.upcast()).replace("::", "$"); + + format!("{}${}", module_name, func_name).into() +} diff --git a/crates/codegen/src/db/queries/types.rs b/crates/codegen/src/db/queries/types.rs new file mode 100644 index 0000000000..6912aa64d5 --- /dev/null +++ b/crates/codegen/src/db/queries/types.rs @@ -0,0 +1,124 @@ +use fe_mir::ir::{ + types::{ArrayDef, EventDef, MapDef, StructDef, TupleDef}, + Type, TypeId, TypeKind, +}; + +use crate::db::CodegenDb; + +pub fn legalized_type(db: &dyn CodegenDb, ty: TypeId) -> TypeId { + let ty_data = ty.data(db.upcast()); + let ty_kind = match &ty.data(db.upcast()).kind { + TypeKind::Tuple(def) => { + let items = def + .items + .iter() + .filter_map(|item| { + if item.is_zero_sized(db.upcast()) { + None + } else { + Some(legalized_type(db, *item)) + } + }) + .collect(); + let new_def = TupleDef { items }; + TypeKind::Tuple(new_def) + } + + TypeKind::Array(def) => { + let new_def = ArrayDef { + elem_ty: legalized_type(db, def.elem_ty), + len: def.len, + }; + TypeKind::Array(new_def) + } + + TypeKind::Struct(def) => { + let fields = def + .fields + .iter() + .cloned() + .filter_map(|(name, ty)| { + if ty.is_zero_sized(db.upcast()) { + None + } else { + Some((name, legalized_type(db, ty))) + } + }) + .collect(); + let new_def = StructDef { + name: def.name.clone(), + fields, + span: def.span, + module_id: def.module_id, + }; + TypeKind::Struct(new_def) + } + + TypeKind::Event(def) => { + let fields = def + .fields + .iter() + .cloned() + .filter_map(|(name, ty, indexed)| { + if ty.is_zero_sized(db.upcast()) { + None + } else { + Some((name, legalized_type(db, ty), indexed)) + } + }) + .collect(); + let new_def = EventDef { + name: def.name.clone(), + fields, + span: def.span, + module_id: def.module_id, + }; + TypeKind::Event(new_def) + } + + TypeKind::Contract(def) => { + let fields = def + .fields + .iter() + .cloned() + .filter_map(|(name, ty)| { + if ty.is_zero_sized(db.upcast()) { + None + } else { + Some((name, legalized_type(db, ty))) + } + }) + .collect(); + let new_def = StructDef { + name: def.name.clone(), + fields, + span: def.span, + module_id: def.module_id, + }; + TypeKind::Contract(new_def) + } + + TypeKind::Map(def) => { + let new_def = MapDef { + key_ty: legalized_type(db, def.key_ty), + value_ty: legalized_type(db, def.value_ty), + }; + TypeKind::Map(new_def) + } + + TypeKind::MPtr(ty) => { + let new_ty = legalized_type(db, *ty); + TypeKind::MPtr(new_ty) + } + + TypeKind::SPtr(ty) => { + let new_ty = legalized_type(db, *ty); + TypeKind::SPtr(new_ty) + } + + _ => return ty, + }; + + let analyzer_ty = ty_data.analyzer_ty.clone(); + db.mir_intern_type(Type::new(ty_kind, analyzer_ty).into()) +} diff --git a/crates/codegen/src/yul/inst_order.rs b/crates/codegen/src/yul/inst_order.rs deleted file mode 100644 index 1e599100a4..0000000000 --- a/crates/codegen/src/yul/inst_order.rs +++ /dev/null @@ -1,309 +0,0 @@ -#![allow(unused)] - -use fe_mir::{ - analysis::{ - domtree::DFSet, loop_tree::LoopId, post_domtree::PostIDom, ControlFlowGraph, DomTree, - LoopTree, PostDomTree, - }, - ir::{inst::BranchInfo, BasicBlockId, FunctionBody, InstId, ValueId}, -}; - -#[derive(Debug, Clone, Default)] -pub struct InstOrder { - pub order: Vec, -} - -#[derive(Debug, Clone)] -pub enum StructuralInst { - Inst(InstId), - If { - cond: ValueId, - then: Vec, - else_: Vec, - }, - For { - body: Vec, - }, - Break, - Continue, -} - -struct InstSerializer<'a> { - body: &'a FunctionBody, - cfg: ControlFlowGraph, - loop_tree: LoopTree, - df: DFSet, - pd_tree: PostDomTree, - scope: Option, -} - -impl<'a> InstSerializer<'a> { - fn new(body: &'a FunctionBody) -> Self { - let cfg = ControlFlowGraph::compute(body); - let domtree = DomTree::compute(&cfg); - let df = domtree.compute_df(&cfg); - let pd_tree = PostDomTree::compute(body); - let loop_tree = LoopTree::compute(&cfg, &domtree); - - Self { - body, - cfg, - loop_tree, - df, - pd_tree, - scope: None, - } - } - - fn serialize_insts(&mut self) -> InstOrder { - self.scope = None; - let entry = self.cfg.entry(); - let mut order = vec![]; - self.analyze_block(entry, &mut order); - InstOrder { order } - } - - fn analyze_block(&mut self, block: BasicBlockId, order: &mut Vec) { - match self.loop_tree.loop_of_block(block) { - Some(lp) - if block == self.loop_tree.loop_header(lp) - && Some(block) != self.scope.as_ref().and_then(Scope::loop_header) => - { - let loop_exit = self.find_loop_exit(lp); - self.enter_loop_scope(block, loop_exit); - let mut body = vec![]; - self.analyze_block(block, &mut body); - self.exit_scope(); - order.push(StructuralInst::For { body }); - - if let Some(exit) = loop_exit { - self.analyze_block(exit, order); - } - return; - } - _ => {} - }; - - for inst in self.body.order.iter_inst(block) { - if self.body.store.is_terminator(inst) { - break; - } - order.push(StructuralInst::Inst(inst)); - } - - let terminator = self.body.order.terminator(&self.body.store, block).unwrap(); - match self.analyze_terminator(terminator) { - TerminatorInfo::If { - cond, - then, - else_, - merge_block, - } => { - let mut then_body = vec![]; - let mut else_body = vec![]; - - self.enter_if_scope(merge_block); - if let Some(merge_block) = merge_block { - if merge_block == else_ { - self.analyze_block(then, &mut then_body) - } else if merge_block == then { - self.analyze_block(else_, &mut else_body); - } else { - self.analyze_block(then, &mut then_body); - self.analyze_block(else_, &mut else_body); - } - order.push(StructuralInst::If { - cond, - then: then_body, - else_: else_body, - }); - self.exit_scope(); - self.analyze_block(merge_block, order) - } else { - self.analyze_block(then, &mut then_body); - self.analyze_block(else_, &mut else_body); - self.exit_scope(); - order.push(StructuralInst::If { - cond, - then: then_body, - else_: else_body, - }); - } - } - TerminatorInfo::ToMergeBlock => {} - TerminatorInfo::Continue => order.push(StructuralInst::Continue), - TerminatorInfo::Break => order.push(StructuralInst::Break), - TerminatorInfo::FallThrough(next) => self.analyze_block(next, order), - TerminatorInfo::NormalInst(inst) => order.push(StructuralInst::Inst(inst)), - } - } - - fn enter_loop_scope(&mut self, header: BasicBlockId, exit: Option) { - let kind = ScopeKind::Loop { header, exit }; - let current_scope = std::mem::take(&mut self.scope); - self.scope = Some(Scope { - kind, - parent: current_scope.map(Into::into), - }); - } - - fn enter_if_scope(&mut self, merge_block: Option) { - let kind = ScopeKind::If { merge_block }; - let current_scope = std::mem::take(&mut self.scope); - self.scope = Some(Scope { - kind, - parent: current_scope.map(Into::into), - }); - } - - fn exit_scope(&mut self) { - let current_scope = std::mem::take(&mut self.scope); - self.scope = current_scope.unwrap().parent.map(|parent| *parent); - } - - // NOTE: We assume loop has at most one canonical loop exit. - fn find_loop_exit(&self, lp: LoopId) -> Option { - let mut exit_candidates = vec![]; - for block_in_loop in self.loop_tree.iter_blocks_post_order(&self.cfg, lp) { - for &succ in self.cfg.succs(block_in_loop) { - if !self.loop_tree.is_block_in_loop(succ, lp) { - exit_candidates.push(succ); - } - } - } - - if exit_candidates.is_empty() { - return None; - } - - for &cand in &exit_candidates { - // `cand` is true loop exit if the `cand` is contained in the dominance frontier - // of all other candidates. and yeset foo - if exit_candidates.iter().all(|&block| { - if block == cand { - true - } else if let Some(mut df) = self.df.frontiers(block) { - df.any(|frontier| frontier == cand) - } else { - true - } - }) { - return Some(cand); - } - } - - None - } - - fn analyze_terminator(&self, inst: InstId) -> TerminatorInfo { - debug_assert!(self.body.store.is_terminator(inst)); - - match self.body.store.branch_info(inst) { - BranchInfo::Jump(dest) => self.analyze_jump(dest), - BranchInfo::Branch(cond, then, else_) => { - self.analyze_branch(self.body.order.inst_block(inst), cond, then, else_) - } - BranchInfo::NotBranch => TerminatorInfo::NormalInst(inst), - } - } - - // NOTE: We remove critical edges in legalization pass, so `break` and - // `continue` never appear in branch info. - fn analyze_branch( - &self, - block: BasicBlockId, - cond: ValueId, - then: BasicBlockId, - else_: BasicBlockId, - ) -> TerminatorInfo { - let merge_block = match self.pd_tree.post_idom(block) { - PostIDom::DummyEntry | PostIDom::DummyExit => None, - PostIDom::Block(block) => Some(block), - }; - - TerminatorInfo::If { - cond, - then, - else_, - merge_block, - } - } - - fn analyze_jump(&self, dest: BasicBlockId) -> TerminatorInfo { - match &self.scope { - Some(scope) => { - if Some(dest) == scope.loop_header_recursive() { - TerminatorInfo::Continue - } else if Some(dest) == scope.loop_exit_recursive() { - TerminatorInfo::Break - } else if Some(dest) == scope.if_merge_block() { - TerminatorInfo::ToMergeBlock - } else { - TerminatorInfo::FallThrough(dest) - } - } - - None => TerminatorInfo::FallThrough(dest), - } - } -} - -struct Scope { - kind: ScopeKind, - parent: Option>, -} - -#[derive(Debug, Clone, Copy)] -enum ScopeKind { - Loop { - header: BasicBlockId, - exit: Option, - }, - If { - merge_block: Option, - }, -} - -impl Scope { - fn loop_header(&self) -> Option { - match self.kind { - ScopeKind::Loop { header, .. } => Some(header), - _ => None, - } - } - fn loop_header_recursive(&self) -> Option { - match self.kind { - ScopeKind::Loop { header, .. } => Some(header), - _ => self.parent.as_ref()?.loop_header_recursive(), - } - } - - fn loop_exit_recursive(&self) -> Option { - match self.kind { - ScopeKind::Loop { exit, .. } => exit, - _ => self.parent.as_ref()?.loop_exit_recursive(), - } - } - - fn if_merge_block(&self) -> Option { - match self.kind { - ScopeKind::If { merge_block } => merge_block, - _ => None, - } - } -} - -#[derive(Debug, Clone)] -enum TerminatorInfo { - If { - cond: ValueId, - then: BasicBlockId, - else_: BasicBlockId, - merge_block: Option, - }, - ToMergeBlock, - Continue, - Break, - FallThrough(BasicBlockId), - NormalInst(InstId), -} diff --git a/crates/codegen/src/yul/isel/context.rs b/crates/codegen/src/yul/isel/context.rs new file mode 100644 index 0000000000..1cec56b245 --- /dev/null +++ b/crates/codegen/src/yul/isel/context.rs @@ -0,0 +1,78 @@ +use std::collections::BTreeSet; + +use fe_analyzer::namespace::items::ContractId; +use fe_mir::ir::FunctionId; +use fxhash::FxHashSet; +use yultsur::yul; + +use crate::{ + db::CodegenDb, + yul::runtime::{DefaultRuntimeProvider, RuntimeProvider}, +}; + +use super::{lower_contract_deployable, lower_function}; + +pub struct Context { + pub runtime: Box, + pub(super) contract_dependency: BTreeSet, + pub(super) function_dependency: BTreeSet, + pub(super) string_constants: BTreeSet, + pub(super) lowered_functions: FxHashSet, +} + +impl Default for Context { + fn default() -> Self { + Self { + runtime: Box::new(DefaultRuntimeProvider::default()), + contract_dependency: BTreeSet::default(), + function_dependency: BTreeSet::default(), + string_constants: BTreeSet::default(), + lowered_functions: FxHashSet::default(), + } + } +} + +impl Context { + pub(super) fn resolve_function_dependency( + &mut self, + db: &dyn CodegenDb, + ) -> Vec { + let mut funcs = vec![]; + loop { + let dependencies = std::mem::take(&mut self.function_dependency); + if dependencies.is_empty() { + break; + } + for dependency in dependencies { + if self.lowered_functions.contains(&dependency) { + // Ignore dependency if it's already lowered. + continue; + } else { + funcs.push(lower_function(db, self, dependency)) + } + } + } + + funcs + } + + pub(super) fn resolve_constant_dependency(&self, db: &dyn CodegenDb) -> Vec { + self.string_constants + .iter() + .map(|s| { + let symbol = db.codegen_constant_string_symbol_name(s.to_string()); + yul::Data { + name: symbol.as_ref().clone(), + value: s.to_string(), + } + }) + .collect() + } + + pub(super) fn resolve_contract_dependency(&self, db: &dyn CodegenDb) -> Vec { + self.contract_dependency + .iter() + .map(|cid| lower_contract_deployable(db, *cid)) + .collect() + } +} diff --git a/crates/codegen/src/yul/isel/contract.rs b/crates/codegen/src/yul/isel/contract.rs new file mode 100644 index 0000000000..3703381d8e --- /dev/null +++ b/crates/codegen/src/yul/isel/contract.rs @@ -0,0 +1,289 @@ +use fe_analyzer::namespace::items::ContractId; +use fe_mir::ir::{function::Linkage, FunctionId}; +use yultsur::{yul, *}; + +use crate::{ + db::CodegenDb, + yul::{runtime::AbiSrcLocation, YulVariable}, +}; + +use super::context::Context; + +pub fn lower_contract_deployable(db: &dyn CodegenDb, contract: ContractId) -> yul::Object { + let mut context = Context::default(); + + let constructor = if let Some(init) = contract.init_function(db.upcast()) { + let init = db.mir_lowered_func_signature(init); + make_init(db, &mut context, contract, init) + } else { + statements! {} + }; + + let deploy_code = make_deploy(db, contract); + + let dep_functions: Vec<_> = context + .resolve_function_dependency(db) + .into_iter() + .map(yul::Statement::FunctionDefinition) + .collect(); + let runtime_funcs: Vec<_> = context + .runtime + .collect_definitions() + .into_iter() + .map(yul::Statement::FunctionDefinition) + .collect(); + + let deploy_block = block_statement! { + [constructor...] + [deploy_code...] + }; + + let code = code! { + [deploy_block] + [dep_functions...] + [runtime_funcs...] + }; + + let mut dep_contracts = context.resolve_contract_dependency(db); + dep_contracts.push(lower_contract(db, contract)); + let dep_constants = context.resolve_constant_dependency(db); + + let name = identifier! {( + db.codegen_contract_deployer_symbol_name(contract).as_ref() + )}; + let object = yul::Object { + name, + code, + objects: dep_contracts, + data: dep_constants, + }; + + normalize_object(object) +} + +pub fn lower_contract(db: &dyn CodegenDb, contract: ContractId) -> yul::Object { + let exported_funcs: Vec<_> = db + .mir_lower_contract_all_functions(contract) + .iter() + .filter_map(|fid| { + if fid.signature(db.upcast()).linkage == Linkage::Export { + Some(*fid) + } else { + None + } + }) + .collect(); + + let mut context = Context::default(); + let dispatcher = if let Some(call_fn) = contract.call_function(db.upcast()) { + let call_fn = db.mir_lowered_func_signature(call_fn); + context.function_dependency.insert(call_fn); + let call_symbol = identifier! { (db.codegen_function_symbol_name(call_fn)) }; + statement! { + ([call_symbol]()) + } + } else { + make_dispatcher(db, &mut context, &exported_funcs) + }; + + let dep_functions: Vec<_> = context + .resolve_function_dependency(db) + .into_iter() + .map(yul::Statement::FunctionDefinition) + .collect(); + let runtime_funcs: Vec<_> = context + .runtime + .collect_definitions() + .into_iter() + .map(yul::Statement::FunctionDefinition) + .collect(); + + let code = code! { + ([dispatcher]) + [dep_functions...] + [runtime_funcs...] + }; + + // Lower dependant contracts. + let dep_contracts = context.resolve_contract_dependency(db); + + // Collect string constants. + let dep_constants = context.resolve_constant_dependency(db); + let contract_symbol = identifier! { (db.codegen_contract_symbol_name(contract)) }; + + yul::Object { + name: contract_symbol, + code, + objects: dep_contracts, + data: dep_constants, + } +} + +fn make_dispatcher( + db: &dyn CodegenDb, + context: &mut Context, + funcs: &[FunctionId], +) -> yul::Statement { + let arms = funcs + .iter() + .map(|func| dispatch_arm(db, context, *func)) + .collect::>(); + + if arms.is_empty() { + statement! { return(0, 0) } + } else { + let selector = expression! { + and((shr((sub(256, 32)), (calldataload(0)))), 0xffffffff) + }; + switch! { + switch ([selector]) + [arms...] + (default { (return(0, 0)) }) + } + } +} + +fn dispatch_arm(db: &dyn CodegenDb, context: &mut Context, func: FunctionId) -> yul::Case { + context.function_dependency.insert(func); + let func_sig = db.codegen_legalized_signature(func); + let mut param_vars = Vec::with_capacity(func_sig.params.len()); + let mut param_tys = Vec::with_capacity(func_sig.params.len()); + func_sig.params.iter().for_each(|param| { + param_vars.push(YulVariable::new(param.name.as_str())); + param_tys.push(param.ty); + }); + + let decode_params = if func_sig.params.is_empty() { + statements! {} + } else { + let ident_params: Vec<_> = param_vars.iter().map(YulVariable::ident).collect(); + let param_size = YulVariable::new("param_size"); + statements! { + (let [param_size.ident()] := sub((calldatasize()), 4)) + (let [ident_params...] := [context.runtime.abi_decode(db, expression! { 4 }, param_size.expr(), ¶m_tys, AbiSrcLocation::CallData)]) + } + }; + + let call_and_encode_return = { + let name = identifier! { (db.codegen_function_symbol_name(func)) }; + // we pass in a `0` for the expected `Context` argument + let call = expression! {[name]([(param_vars.iter().map(YulVariable::expr).collect::>())...])}; + if let Some(mut return_type) = func_sig.return_type { + if return_type.is_aggregate(db.upcast()) { + return_type = return_type.make_mptr(db.upcast()); + } + + let ret = YulVariable::new("ret"); + let enc_start = YulVariable::new("enc_start"); + let enc_size = YulVariable::new("enc_size"); + let abi_encode = context.runtime.abi_encode_seq( + db, + &[ret.expr()], + enc_start.expr(), + &[return_type], + false, + ); + statements! { + (let [ret.ident()] := [call]) + (let [enc_start.ident()] := [context.runtime.avail(db)]) + (let [enc_size.ident()] := [abi_encode]) + (return([enc_start.expr()], [enc_size.expr()])) + } + } else { + statements! { + ([yul::Statement::Expression(call)]) + (return(0, 0)) + } + } + }; + + let abi_sig = db.codegen_abi_function(func); + let selector = literal! { (format!("0x{}", abi_sig.selector().hex())) }; + case! { + case [selector] { + [decode_params...] + [call_and_encode_return...] + } + } +} + +fn make_init( + db: &dyn CodegenDb, + context: &mut Context, + contract: ContractId, + init: FunctionId, +) -> Vec { + context.function_dependency.insert(init); + let init_func_name = identifier! { (db.codegen_function_symbol_name(init)) }; + let contract_name = identifier_expression! { (format!{r#""{}""#, db.codegen_contract_deployer_symbol_name(contract)}) }; + + let func_sig = db.codegen_legalized_signature(init); + let mut param_vars = Vec::with_capacity(func_sig.params.len()); + let mut param_tys = Vec::with_capacity(func_sig.params.len()); + let program_size = YulVariable::new("$program_size"); + let arg_size = YulVariable::new("$arg_size"); + let code_size = YulVariable::new("$code_size"); + let memory_data_offset = YulVariable::new("$memory_data_offset"); + func_sig.params.iter().for_each(|param| { + param_vars.push(YulVariable::new(param.name.as_str())); + param_tys.push(param.ty); + }); + + let decode_params = if func_sig.params.is_empty() { + statements! {} + } else { + let ident_params: Vec<_> = param_vars.iter().map(YulVariable::ident).collect(); + statements! { + (let [ident_params...] := [context.runtime.abi_decode(db, memory_data_offset.expr(), arg_size.expr(), ¶m_tys, AbiSrcLocation::Memory)]) + } + }; + + let call = expression! {[init_func_name]([(param_vars.iter().map(YulVariable::expr).collect::>())...])}; + statements! { + (let [program_size.ident()] := datasize([contract_name])) + (let [code_size.ident()] := codesize()) + (let [arg_size.ident()] := sub([code_size.expr()], [program_size.expr()])) + (let [memory_data_offset.ident()] := [context.runtime.alloc(db, arg_size.expr())]) + (codecopy([memory_data_offset.expr()], [program_size.expr()], [arg_size.expr()])) + [decode_params...] + ([yul::Statement::Expression(call)]) + } +} + +fn make_deploy(db: &dyn CodegenDb, contract: ContractId) -> Vec { + let contract_symbol = + identifier_expression! { (format!{r#""{}""#, db.codegen_contract_symbol_name(contract)}) }; + let size = YulVariable::new("$$size"); + statements! { + (let [size.ident()] := (datasize([contract_symbol.clone()]))) + (datacopy(0, (dataoffset([contract_symbol])), [size.expr()])) + (return (0, [size.expr()])) + } +} + +fn normalize_object(obj: yul::Object) -> yul::Object { + let data = obj + .data + .into_iter() + .map(|data| yul::Data { + name: data.name, + value: data + .value + .replace('\\', "\\\\\\\\") + .replace('\n', "\\\\n") + .replace('"', "\\\\\"") + .replace('\r', "\\\\r") + .replace('\t', "\\\\t"), + }) + .collect::>(); + yul::Object { + name: obj.name, + code: obj.code, + objects: obj + .objects + .into_iter() + .map(normalize_object) + .collect::>(), + data, + } +} diff --git a/crates/codegen/src/yul/isel/function.rs b/crates/codegen/src/yul/isel/function.rs new file mode 100644 index 0000000000..2516f78691 --- /dev/null +++ b/crates/codegen/src/yul/isel/function.rs @@ -0,0 +1,855 @@ +#![allow(unused)] +use super::{context::Context, inst_order::InstSerializer}; + +use fe_common::db::Upcast; +use fe_mir::ir::{ + self, + constant::ConstantValue, + inst::{BinOp, CallType, InstKind, UnOp}, + value::AssignableValue, + Constant, FunctionBody, FunctionId, FunctionSignature, InstId, Type, TypeId, TypeKind, Value, + ValueId, +}; +use fe_new_abi::function::{AbiFunction, AbiFunctionType}; +use fxhash::FxHashMap; +use smol_str::SmolStr; +use yultsur::{ + yul::{self, Statement}, + *, +}; + +use crate::{ + db::CodegenDb, + yul::isel::inst_order::StructuralInst, + yul::slot_size::{function_hash_type, yul_primitive_type, SLOT_SIZE}, + yul::{ + runtime::{self, RuntimeProvider}, + YulVariable, + }, +}; + +pub fn lower_function( + db: &dyn CodegenDb, + ctx: &mut Context, + function: FunctionId, +) -> yul::FunctionDefinition { + debug_assert!(!ctx.lowered_functions.contains(&function)); + ctx.lowered_functions.insert(function); + let sig = &db.codegen_legalized_signature(function); + let body = &db.codegen_legalized_body(function); + FuncLowerHelper::new(db, ctx, function, sig, body).lower_func() +} + +struct FuncLowerHelper<'db, 'a> { + db: &'db dyn CodegenDb, + ctx: &'a mut Context, + value_map: FxHashMap, + func: FunctionId, + sig: &'a FunctionSignature, + body: &'a FunctionBody, + ret_value: Option, + sink: Vec, +} + +impl<'db, 'a> FuncLowerHelper<'db, 'a> { + fn new( + db: &'db dyn CodegenDb, + ctx: &'a mut Context, + func: FunctionId, + sig: &'a FunctionSignature, + body: &'a FunctionBody, + ) -> Self { + let mut value_map = FxHashMap::default(); + // Register arguments to value_map. + for &value in body.store.locals() { + match body.store.value_data(value) { + Value::Local(local) if local.is_arg => { + let ident = YulVariable::new(local.name.as_str()).ident(); + value_map.insert(value, ident); + } + _ => {} + } + } + + let ret_value = if sig.return_type.is_some() { + Some(YulVariable::new("$ret").ident()) + } else { + None + }; + + Self { + db, + ctx, + value_map, + func, + sig, + body, + ret_value, + sink: Vec::new(), + } + } + + fn lower_func(mut self) -> yul::FunctionDefinition { + let name = identifier! { (self.db.codegen_function_symbol_name(self.func)) }; + + let parameters = self + .sig + .params + .iter() + .map(|param| YulVariable::new(param.name.as_str()).ident()) + .collect(); + + let ret = self + .ret_value + .clone() + .map(|value| vec![value]) + .unwrap_or_default(); + + let body = self.lower_body(); + + yul::FunctionDefinition { + name, + parameters, + returns: ret, + block: body, + } + } + + fn lower_body(mut self) -> yul::Block { + let inst_order = InstSerializer::new(self.body).serialize(); + + for inst in inst_order { + self.lower_structural_inst(inst) + } + + yul::Block { + statements: self.sink, + } + } + + fn lower_structural_inst(&mut self, inst: StructuralInst) { + match inst { + StructuralInst::Inst(inst) => self.lower_inst(inst), + StructuralInst::If { cond, then, else_ } => { + let if_block = self.lower_if(cond, then, else_); + self.sink.push(if_block) + } + StructuralInst::For { body } => { + let for_block = self.lower_for(body); + self.sink.push(for_block) + } + StructuralInst::Break => self.sink.push(yul::Statement::Break), + StructuralInst::Continue => self.sink.push(yul::Statement::Continue), + }; + } + + fn lower_inst(&mut self, inst: InstId) { + if let Some(lhs) = self.body.store.inst_result(inst) { + self.declare_assignable_value(lhs) + } + + match &self.body.store.inst_data(inst).kind { + InstKind::Declare { local } => self.declare_value(*local), + + InstKind::Unary { op, value } => { + let inst_result = self.body.store.inst_result(inst).unwrap(); + let inst_result_ty = self.assignable_value_ty(inst_result); + let result = self.lower_unary(*op, *value); + self.assign_inst_result(inst, result, inst_result_ty.deref(self.db.upcast())) + } + + InstKind::Binary { op, lhs, rhs } => { + let inst_result = self.body.store.inst_result(inst).unwrap(); + let inst_result_ty = self.assignable_value_ty(inst_result); + let result = self.lower_binary(*op, *lhs, *rhs, inst); + self.assign_inst_result(inst, result, inst_result_ty.deref(self.db.upcast())) + } + + InstKind::Cast { value, to } => { + let from_ty = self.body.store.value_ty(*value); + let value = self.value_expr(*value); + let result = self.ctx.runtime.primitive_cast(self.db, value, from_ty); + self.assign_inst_result(inst, result, *to) + } + + InstKind::AggregateConstruct { ty, args } => { + let lhs = self.body.store.inst_result(inst).unwrap(); + let ptr = self.lower_assignable_value(lhs); + let ptr_ty = self.assignable_value_ty(lhs); + let arg_values = args.iter().map(|arg| self.value_expr(*arg)).collect(); + let arg_tys = args + .iter() + .map(|arg| self.body.store.value_ty(*arg)) + .collect(); + self.sink.push(yul::Statement::Expression( + self.ctx + .runtime + .aggregate_init(self.db, ptr, arg_values, ptr_ty, arg_tys), + )) + } + + InstKind::Bind { src } => { + match self.body.store.value_data(*src) { + Value::Constant { constant, .. } => { + // Need special handling when rhs is the string literal because it needs ptr + // copy. + if let ConstantValue::Str(s) = &constant.data(self.db.upcast()).value { + self.ctx.string_constants.insert(s.to_string()); + let size = self.value_ty_size_deref(*src); + let lhs = self.body.store.inst_result(inst).unwrap(); + let ptr = self.lower_assignable_value(lhs); + let inst_result_ty = self.assignable_value_ty(lhs); + self.sink.push(yul::Statement::Expression( + self.ctx.runtime.string_copy( + self.db, + ptr, + s, + inst_result_ty.is_sptr(self.db.upcast()), + ), + )) + } else { + let src_ty = self.body.store.value_ty(*src); + let src = self.value_expr(*src); + self.assign_inst_result(inst, src, src_ty) + } + } + _ => { + let src_ty = self.body.store.value_ty(*src); + let src = self.value_expr(*src); + self.assign_inst_result(inst, src, src_ty) + } + } + } + + InstKind::MemCopy { src } => { + let lhs = self.body.store.inst_result(inst).unwrap(); + let dst_ptr = self.lower_assignable_value(lhs); + let dst_ptr_ty = self.assignable_value_ty(lhs); + let src_ptr = self.value_expr(*src); + let src_ptr_ty = self.body.store.value_ty(*src); + let ty_size = literal_expression! { (self.value_ty_size_deref(*src)) }; + self.sink + .push(yul::Statement::Expression(self.ctx.runtime.ptr_copy( + self.db, + src_ptr, + dst_ptr, + ty_size, + src_ptr_ty.is_sptr(self.db.upcast()), + dst_ptr_ty.is_sptr(self.db.upcast()), + ))) + } + + InstKind::AggregateAccess { value, indices } => { + let base = self.value_expr(*value); + let mut ptr = base; + let ptr_ty = self.body.store.value_ty(*value); + let mut inner_ty = ptr_ty.deref(self.db.upcast()); + for &idx in indices { + ptr = self.aggregate_elem_ptr(ptr, idx, inner_ty); + inner_ty = + inner_ty.projection_ty(self.db.upcast(), self.body.store.value_data(idx)); + } + + let elem_ptr_ty = if ptr_ty.is_mptr(self.db.upcast()) { + inner_ty.make_mptr(self.db.upcast()) + } else { + inner_ty.make_sptr(self.db.upcast()) + }; + + let result = self.body.store.inst_result(inst).unwrap(); + self.assign_inst_result(inst, ptr, elem_ptr_ty) + } + + InstKind::MapAccess { value, key } => { + let map_ty = self.body.store.value_ty(*value).deref(self.db.upcast()); + let value_expr = self.value_expr(*value); + let key_expr = self.value_expr(*key); + let key_ty = self.body.store.value_ty(*key); + let ptr = self + .ctx + .runtime + .map_value_ptr(self.db, value_expr, key_expr, key_ty); + let value_ty = match &map_ty.data(self.db.upcast()).kind { + TypeKind::Map(def) => def.value_ty, + _ => unreachable!(), + }; + + self.assign_inst_result(inst, ptr, value_ty.make_sptr(self.db.upcast())); + } + + InstKind::Call { + func, + args, + call_type, + } => { + let args: Vec<_> = args.iter().map(|arg| self.value_expr(*arg)).collect(); + let result = match call_type { + CallType::Internal => { + self.ctx.function_dependency.insert(*func); + let func_name = identifier! {(self.db.codegen_function_symbol_name(*func))}; + expression! {[func_name]([args...])} + } + CallType::External => self.ctx.runtime.external_call(self.db, *func, args), + }; + match self.db.codegen_legalized_signature(*func).return_type { + Some(mut result_ty) => { + if result_ty.is_aggregate(self.db.upcast()) + | result_ty.is_string(self.db.upcast()) + { + result_ty = result_ty.make_mptr(self.db.upcast()); + } + self.assign_inst_result(inst, result, result_ty) + } + _ => self.sink.push(Statement::Expression(result)), + } + } + + InstKind::Revert { arg } => match arg { + Some(arg) => { + let arg_ty = self.body.store.value_ty(*arg); + let deref_ty = arg_ty.deref(self.db.upcast()); + let ty_data = deref_ty.data(self.db.upcast()); + let arg_expr = if deref_ty.is_zero_sized(self.db.upcast()) { + None + } else { + Some(self.value_expr(*arg)) + }; + let name = match &ty_data.kind { + ir::TypeKind::Struct(def) => &def.name, + ir::TypeKind::String(def) => "Error", + _ => "Panic", + }; + self.sink.push(yul::Statement::Expression( + self.ctx.runtime.revert(self.db, arg_expr, name, arg_ty), + )); + } + None => self.sink.push(statement! {revert(0, 0)}), + }, + + InstKind::Emit { arg } => { + let event = self.value_expr(*arg); + let event_ty = self.body.store.value_ty(*arg); + let result = self.ctx.runtime.emit(self.db, event, event_ty); + let u256_ty = yul_primitive_type(self.db); + self.assign_inst_result(inst, result, u256_ty); + } + + InstKind::Return { arg } => { + if let Some(arg) = arg { + let arg = self.value_expr(*arg); + let ret_value = self.ret_value.clone().unwrap(); + self.sink.push(statement! {[ret_value] := [arg]}); + } + self.sink.push(yul::Statement::Leave) + } + + InstKind::Keccak256 { arg } => { + let result = self.keccak256(*arg); + let u256_ty = yul_primitive_type(self.db); + self.assign_inst_result(inst, result, u256_ty); + } + + InstKind::AbiEncode { arg } => { + let lhs = self.body.store.inst_result(inst).unwrap(); + let ptr = self.lower_assignable_value(lhs); + let ptr_ty = self.assignable_value_ty(lhs); + let src_expr = self.value_expr(*arg); + let src_ty = self.body.store.value_ty(*arg); + + let abi_encode = self.ctx.runtime.abi_encode( + self.db, + src_expr, + ptr, + src_ty, + ptr_ty.is_sptr(self.db.upcast()), + ); + self.sink.push(statement! { + pop([abi_encode]) + }); + } + + InstKind::Create { value, contract } => { + self.ctx.contract_dependency.insert(*contract); + + let value_expr = self.value_expr(*value); + let result = self.ctx.runtime.create(self.db, *contract, value_expr); + let u256_ty = yul_primitive_type(self.db); + self.assign_inst_result(inst, result, u256_ty) + } + + InstKind::Create2 { + value, + salt, + contract, + } => { + self.ctx.contract_dependency.insert(*contract); + + let value_expr = self.value_expr(*value); + let salt_expr = self.value_expr(*salt); + let result = self + .ctx + .runtime + .create2(self.db, *contract, value_expr, salt_expr); + let u256_ty = yul_primitive_type(self.db); + self.assign_inst_result(inst, result, u256_ty) + } + + InstKind::YulIntrinsic { op, args } => { + let args: Vec<_> = args.iter().map(|arg| self.value_expr(*arg)).collect(); + let op_name = identifier! { (format!("{}", op).strip_prefix("__").unwrap()) }; + let result = expression! { [op_name]([args...]) }; + // Intrinsic operation never returns ptr type, so we can use u256_ty as a dummy + // type for the result. + let u256_ty = yul_primitive_type(self.db); + self.assign_inst_result(inst, result, u256_ty) + } + + InstKind::Nop => {} + + InstKind::Jump { .. } | InstKind::Branch { .. } => { + unreachable!() + } + } + } + + fn lower_if( + &mut self, + cond: ValueId, + then: Vec, + else_: Vec, + ) -> yul::Statement { + let cond = self.value_expr(cond); + let mut then_stmts = vec![]; + let mut else_stmts = vec![]; + + for inst in then { + std::mem::swap(&mut self.sink, &mut then_stmts); + self.lower_structural_inst(inst); + std::mem::swap(&mut self.sink, &mut then_stmts); + } + for inst in else_ { + std::mem::swap(&mut self.sink, &mut else_stmts); + self.lower_structural_inst(inst); + std::mem::swap(&mut self.sink, &mut else_stmts); + } + + switch! { + switch ([cond]) + (case 1 {[then_stmts...]}) + (case 0 {[else_stmts...]}) + } + } + + fn lower_for(&mut self, body: Vec) -> yul::Statement { + let mut body_stmts = vec![]; + std::mem::swap(&mut self.sink, &mut body_stmts); + for inst in body { + self.lower_structural_inst(inst); + } + std::mem::swap(&mut self.sink, &mut body_stmts); + + block_statement! {( + for {} (1) {} + { + [body_stmts...] + } + )} + } + fn lower_assign(&mut self, lhs: &AssignableValue, rhs: ValueId) -> yul::Statement { + match lhs { + AssignableValue::Value(value) => { + let lhs = self.value_ident(*value); + let rhs = self.value_expr(rhs); + statement! { [lhs] := [rhs] } + } + AssignableValue::Aggregate { .. } | AssignableValue::Map { .. } => { + let dst_ty = self.assignable_value_ty(lhs); + let src_ty = self.body.store.value_ty(rhs); + debug_assert_eq!( + dst_ty.deref(self.db.upcast()), + src_ty.deref(self.db.upcast()) + ); + + let dst = self.lower_assignable_value(lhs); + let src = self.value_expr(rhs); + + if src_ty.is_ptr(self.db.upcast()) { + let ty_size = literal_expression! { (self.value_ty_size_deref(rhs)) }; + + let expr = self.ctx.runtime.ptr_copy( + self.db, + src, + dst, + ty_size, + src_ty.is_sptr(self.db.upcast()), + dst_ty.is_sptr(self.db.upcast()), + ); + yul::Statement::Expression(expr) + } else { + let expr = self.ctx.runtime.ptr_store(self.db, dst, src, dst_ty); + yul::Statement::Expression(expr) + } + } + } + } + + fn lower_unary(&mut self, op: UnOp, value: ValueId) -> yul::Expression { + let value = self.value_expr(value); + match op { + UnOp::Not => expression! { iszero([value])}, + UnOp::Neg => { + let zero = literal_expression! {0}; + expression! { sub([zero], [value])} + } + UnOp::Inv => expression! { not([value])}, + } + } + + fn lower_binary( + &mut self, + op: BinOp, + lhs: ValueId, + rhs: ValueId, + inst: InstId, + ) -> yul::Expression { + let lhs = self.value_expr(lhs); + let rhs = self.value_expr(rhs); + let is_signed = self + .body + .store + .inst_result(inst) + .map(|val| { + let ty = self.assignable_value_ty(val); + ty.is_signed(self.db.upcast()) + }) + .unwrap_or(false); + let inst_result = self.body.store.inst_result(inst).unwrap(); + let inst_result_ty = self + .assignable_value_ty(inst_result) + .deref(self.db.upcast()); + match op { + BinOp::Add => self.ctx.runtime.safe_add(self.db, lhs, rhs, inst_result_ty), + BinOp::Sub => self.ctx.runtime.safe_sub(self.db, lhs, rhs, inst_result_ty), + BinOp::Mul => self.ctx.runtime.safe_mul(self.db, lhs, rhs, inst_result_ty), + BinOp::Div => self.ctx.runtime.safe_div(self.db, lhs, rhs, inst_result_ty), + BinOp::Mod => self.ctx.runtime.safe_mod(self.db, lhs, rhs, inst_result_ty), + BinOp::Pow => self.ctx.runtime.safe_pow(self.db, lhs, rhs, inst_result_ty), + BinOp::Shl => expression! {shl([rhs], [lhs])}, + BinOp::Shr => expression! {shr([rhs], [lhs])}, + BinOp::BitOr | BinOp::LogicalOr => expression! {or([lhs], [rhs])}, + BinOp::BitXor => expression! {xor([lhs], [rhs])}, + BinOp::BitAnd | BinOp::LogicalAnd => expression! {and([lhs], [rhs])}, + BinOp::Eq => expression! {eq([lhs], [rhs])}, + BinOp::Ne => expression! {iszero((eq([lhs], [rhs])))}, + BinOp::Ge if is_signed => expression! {iszero((slt([lhs], [rhs])))}, + BinOp::Ge => expression! {iszero((lt([lhs], [rhs])))}, + BinOp::Gt if is_signed => expression! {sgt([lhs], [rhs])}, + BinOp::Gt => expression! {gt([lhs], [rhs])}, + BinOp::Le if is_signed => expression! {iszero((sgt([lhs], [rhs])))}, + BinOp::Le => expression! {iszero((gt([lhs], [rhs])))}, + BinOp::Lt if is_signed => expression! {slt([lhs], [rhs])}, + BinOp::Lt => expression! {lt([lhs], [rhs])}, + } + } + + fn lower_cast(&mut self, value: ValueId, to: TypeId) -> yul::Expression { + let from_ty = self.body.store.value_ty(value); + debug_assert!(from_ty.is_primitive(self.db.upcast())); + debug_assert!(to.is_primitive(self.db.upcast())); + + let value = self.value_expr(value); + self.ctx.runtime.primitive_cast(self.db, value, from_ty) + } + + fn assign_inst_result(&mut self, inst: InstId, rhs: yul::Expression, rhs_ty: TypeId) { + // NOTE: We don't have `deref` feature yet, so need a heuristics for an + // assignment. + let stmt = if let Some(result) = self.body.store.inst_result(inst) { + let lhs = self.lower_assignable_value(result); + let lhs_ty = self.assignable_value_ty(result); + match result { + AssignableValue::Value(value) => { + match ( + lhs_ty.is_ptr(self.db.upcast()), + rhs_ty.is_ptr(self.db.upcast()), + ) { + (true, true) => { + if lhs_ty.is_mptr(self.db.upcast()) == rhs_ty.is_mptr(self.db.upcast()) + { + let rhs = self.extend_value(rhs, lhs_ty); + let lhs_ident = self.value_ident(*value); + statement! { [lhs_ident] := [rhs] } + } else { + let ty_size = rhs_ty + .deref(self.db.upcast()) + .size_of(self.db.upcast(), SLOT_SIZE); + yul::Statement::Expression(self.ctx.runtime.ptr_copy( + self.db, + rhs, + lhs, + literal_expression! { (ty_size) }, + rhs_ty.is_sptr(self.db.upcast()), + lhs_ty.is_sptr(self.db.upcast()), + )) + } + } + (true, false) => yul::Statement::Expression( + self.ctx.runtime.ptr_store(self.db, lhs, rhs, lhs_ty), + ), + + (false, true) => { + let rhs = self.ctx.runtime.ptr_load(self.db, rhs, rhs_ty); + let rhs = self.extend_value(rhs, lhs_ty); + let lhs_ident = self.value_ident(*value); + statement! { [lhs_ident] := [rhs] } + } + (false, false) => { + let rhs = self.extend_value(rhs, lhs_ty); + let lhs_ident = self.value_ident(*value); + statement! { [lhs_ident] := [rhs] } + } + } + } + AssignableValue::Aggregate { .. } | AssignableValue::Map { .. } => { + let expr = if rhs_ty.is_ptr(self.db.upcast()) { + let ty_size = rhs_ty + .deref(self.db.upcast()) + .size_of(self.db.upcast(), SLOT_SIZE); + self.ctx.runtime.ptr_copy( + self.db, + rhs, + lhs, + literal_expression! { (ty_size) }, + rhs_ty.is_sptr(self.db.upcast()), + lhs_ty.is_sptr(self.db.upcast()), + ) + } else { + self.ctx.runtime.ptr_store(self.db, lhs, rhs, lhs_ty) + }; + yul::Statement::Expression(expr) + } + } + } else { + yul::Statement::Expression(rhs) + }; + + self.sink.push(stmt); + } + + /// Extend a value to 256 bits. + fn extend_value(&mut self, value: yul::Expression, ty: TypeId) -> yul::Expression { + if ty.is_primitive(self.db.upcast()) { + self.ctx.runtime.primitive_cast(self.db, value, ty) + } else { + value + } + } + + fn declare_assignable_value(&mut self, value: &AssignableValue) { + match value { + AssignableValue::Value(value) if !self.value_map.contains_key(value) => { + self.declare_value(*value); + } + _ => {} + } + } + + fn declare_value(&mut self, value: ValueId) { + let var = match self.body.store.value_data(value) { + Value::Local(local) => { + if local.is_tmp { + YulVariable::new(format! { + "$tmp_{}", value.index()}) + } else { + YulVariable::new(local.name.as_str()) + } + } + Value::Temporary { .. } => YulVariable::new(format!("$tmp_{}", value.index())), + _ => unreachable!(), + }; + self.value_map.insert(value, var.ident()); + let value_ty = self.body.store.value_ty(value); + + // Allocate memory for a value if a value is a pointer type. + let init = if value_ty.is_mptr(self.db.upcast()) { + let deref_ty = value_ty.deref(self.db.upcast()); + let ty_size = deref_ty.size_of(self.db.upcast(), SLOT_SIZE); + let size = literal_expression! { (ty_size) }; + Some(self.ctx.runtime.alloc(self.db, size)) + } else { + None + }; + + self.sink.push(yul::Statement::VariableDeclaration( + yul::VariableDeclaration { + identifiers: vec![var.ident()], + expression: init, + }, + )) + } + + fn value_expr(&mut self, value: ValueId) -> yul::Expression { + match self.body.store.value_data(value) { + Value::Local(_) | Value::Temporary { .. } => { + let ident = &self.value_map[&value]; + literal_expression! {(ident)} + } + Value::Immediate { imm, .. } => { + literal_expression! {(imm)} + } + Value::Constant { constant, .. } => match &constant.data(self.db.upcast()).value { + ConstantValue::Immediate(imm) => { + literal_expression! {(imm)} + } + ConstantValue::Str(s) => { + self.ctx.string_constants.insert(s.to_string()); + self.ctx.runtime.string_construct(self.db, s, s.len()) + } + ConstantValue::Bool(true) => { + literal_expression! {1} + } + ConstantValue::Bool(false) => { + literal_expression! {0} + } + }, + Value::Unit { .. } => unreachable!(), + } + } + + fn value_ident(&self, value: ValueId) -> yul::Identifier { + self.value_map[&value].clone() + } + + fn make_tmp(&mut self, tmp: ValueId) -> yul::Identifier { + let ident = YulVariable::new(format! {"$tmp_{}", tmp.index()}).ident(); + self.value_map.insert(tmp, ident.clone()); + ident + } + + fn keccak256(&mut self, value: ValueId) -> yul::Expression { + let value_ty = self.body.store.value_ty(value); + debug_assert!(value_ty.is_mptr(self.db.upcast())); + + let value_size = value_ty + .deref(self.db.upcast()) + .size_of(self.db.upcast(), SLOT_SIZE); + let value_size_expr = literal_expression! {(value_size)}; + let value_expr = self.value_expr(value); + expression! {keccak256([value_expr], [value_size_expr])} + } + + fn lower_assignable_value(&mut self, value: &AssignableValue) -> yul::Expression { + match value { + AssignableValue::Value(value) => self.value_expr(*value), + + AssignableValue::Aggregate { lhs, idx } => { + let base_ptr = self.lower_assignable_value(lhs); + let ty = self.assignable_value_ty(lhs).deref(self.db.upcast()); + self.aggregate_elem_ptr(base_ptr, *idx, ty) + } + AssignableValue::Map { lhs, key } => { + let map_ptr = self.lower_assignable_value(lhs); + let key_ty = self.body.store.value_ty(*key); + let key = self.value_expr(*key); + self.ctx + .runtime + .map_value_ptr(self.db, map_ptr, key, key_ty) + } + } + } + + fn assignable_value_ty(&self, value: &AssignableValue) -> TypeId { + match value { + AssignableValue::Value(value) => self.body.store.value_ty(*value), + AssignableValue::Aggregate { lhs, idx } => { + let lhs_ty = self.assignable_value_ty(lhs); + let ty = lhs_ty + .deref(self.db.upcast()) + .projection_ty(self.db.upcast(), self.body.store.value_data(*idx)); + + if lhs_ty.is_sptr(self.db.upcast()) { + ty.make_sptr(self.db.upcast()) + } else { + ty.make_mptr(self.db.upcast()) + } + } + AssignableValue::Map { lhs, key } => { + let map_ty = self.assignable_value_ty(lhs).deref(self.db.upcast()); + match &map_ty.data(self.db.upcast()).kind { + TypeKind::Map(def) => def.value_ty.make_sptr(self.db.upcast()), + _ => unreachable!(), + } + } + } + } + + fn aggregate_elem_ptr( + &mut self, + base_ptr: yul::Expression, + idx: ValueId, + base_ty: TypeId, + ) -> yul::Expression { + debug_assert!(base_ty.is_aggregate(self.db.upcast())); + + match &base_ty.data(self.db.upcast()).kind { + TypeKind::Array(def) => { + let elem_size = + literal_expression! {(base_ty.array_elem_size(self.db.upcast(), SLOT_SIZE))}; + self.validate_array_indexing(def.len, idx); + let idx = self.value_expr(idx); + let offset = expression! {mul([elem_size], [idx])}; + expression! { add([base_ptr], [offset]) } + } + _ => { + let elem_idx = match self.body.store.value_data(idx) { + Value::Immediate { imm, .. } => imm, + _ => panic!("only array type can use dynamic value indexing"), + }; + let offset = literal_expression! {(base_ty.aggregate_elem_offset(self.db.upcast(), elem_idx.clone(), SLOT_SIZE))}; + expression! {add([base_ptr], [offset])} + } + } + } + + fn validate_array_indexing(&mut self, array_len: usize, idx: ValueId) { + const PANIC_OUT_OF_BOUNDS: usize = 0x32; + + if let Value::Immediate { .. } = self.body.store.value_data(idx) { + return; + } + + let idx = self.value_expr(idx); + let max_idx = literal_expression! {(array_len - 1)}; + self.sink.push(statement!(if (gt([idx], [max_idx])) { + ([runtime::panic_revert_numeric( + self.ctx.runtime.as_mut(), + self.db, + literal_expression! {(PANIC_OUT_OF_BOUNDS)}, + )]) + })); + } + + fn value_ty_size(&self, value: ValueId) -> usize { + self.body + .store + .value_ty(value) + .size_of(self.db.upcast(), SLOT_SIZE) + } + + fn value_ty_size_deref(&self, value: ValueId) -> usize { + self.body + .store + .value_ty(value) + .deref(self.db.upcast()) + .size_of(self.db.upcast(), SLOT_SIZE) + } +} + +fn bit_mask(byte_size: usize) -> usize { + (1 << (byte_size * 8)) - 1 +} + +fn bit_mask_expr(byte_size: usize) -> yul::Expression { + let mask = format!("{:#x}", bit_mask(byte_size)); + literal_expression! {(mask)} +} diff --git a/crates/codegen/src/yul/isel/inst_order.rs b/crates/codegen/src/yul/isel/inst_order.rs new file mode 100644 index 0000000000..b6e5317e77 --- /dev/null +++ b/crates/codegen/src/yul/isel/inst_order.rs @@ -0,0 +1,830 @@ +use fe_mir::{ + analysis::{ + domtree::DFSet, loop_tree::LoopId, post_domtree::PostIDom, ControlFlowGraph, DomTree, + LoopTree, PostDomTree, + }, + ir::{inst::BranchInfo, BasicBlockId, FunctionBody, InstId, ValueId}, +}; +use fxhash::FxHashSet; + +#[derive(Debug, Clone)] +pub(super) enum StructuralInst { + Inst(InstId), + If { + cond: ValueId, + then: Vec, + else_: Vec, + }, + For { + body: Vec, + }, + Break, + Continue, +} + +pub(super) struct InstSerializer<'a> { + body: &'a FunctionBody, + cfg: ControlFlowGraph, + loop_tree: LoopTree, + df: DFSet, + pd_tree: PostDomTree, + scope: Option, +} + +impl<'a> InstSerializer<'a> { + pub(super) fn new(body: &'a FunctionBody) -> Self { + let cfg = ControlFlowGraph::compute(body); + let domtree = DomTree::compute(&cfg); + let df = domtree.compute_df(&cfg); + let pd_tree = PostDomTree::compute(body); + let loop_tree = LoopTree::compute(&cfg, &domtree); + + Self { + body, + cfg, + loop_tree, + df, + pd_tree, + scope: None, + } + } + + pub(super) fn serialize(&mut self) -> Vec { + self.scope = None; + let entry = self.cfg.entry(); + let mut order = vec![]; + self.serialize_block(entry, &mut order); + order + } + + fn serialize_block(&mut self, block: BasicBlockId, order: &mut Vec) { + match self.loop_tree.loop_of_block(block) { + Some(lp) + if block == self.loop_tree.loop_header(lp) + && Some(block) != self.scope.as_ref().and_then(Scope::loop_header) => + { + let loop_exit = self.find_loop_exit(lp); + self.enter_loop_scope(lp, block, loop_exit); + let mut body = vec![]; + self.serialize_block(block, &mut body); + self.exit_scope(); + order.push(StructuralInst::For { body }); + + match loop_exit { + Some(exit) + if self + .scope + .as_ref() + .map(|scope| scope.if_merge_block() != Some(exit)) + .unwrap_or(true) => + { + self.serialize_block(exit, order); + } + _ => {} + } + + return; + } + _ => {} + }; + + for inst in self.body.order.iter_inst(block) { + if self.body.store.is_terminator(inst) { + break; + } + if !self.body.store.is_nop(inst) { + order.push(StructuralInst::Inst(inst)); + } + } + + let terminator = self.body.order.terminator(&self.body.store, block).unwrap(); + match self.analyze_terminator(terminator) { + TerminatorInfo::If { + cond, + then, + else_, + merge_block, + } => self.serialize_if_terminator(cond, *then, *else_, merge_block, order), + TerminatorInfo::ToMergeBlock => {} + TerminatorInfo::Continue => order.push(StructuralInst::Continue), + TerminatorInfo::Break => order.push(StructuralInst::Break), + TerminatorInfo::FallThrough(next) => self.serialize_block(next, order), + TerminatorInfo::NormalInst(inst) => order.push(StructuralInst::Inst(inst)), + } + } + + fn serialize_if_terminator( + &mut self, + cond: ValueId, + then: TerminatorInfo, + else_: TerminatorInfo, + merge_block: Option, + order: &mut Vec, + ) { + let mut then_body = vec![]; + let mut else_body = vec![]; + + self.enter_if_scope(merge_block); + + let mut serialize_dest = + |dest_info, body: &mut Vec, merge_block| match dest_info { + TerminatorInfo::Break => body.push(StructuralInst::Break), + TerminatorInfo::Continue => body.push(StructuralInst::Continue), + TerminatorInfo::ToMergeBlock => {} + TerminatorInfo::FallThrough(dest) => { + if Some(dest) != merge_block { + self.serialize_block(dest, body); + } + } + _ => unreachable!(), + }; + serialize_dest(then, &mut then_body, merge_block); + serialize_dest(else_, &mut else_body, merge_block); + self.exit_scope(); + + order.push(StructuralInst::If { + cond, + then: then_body, + else_: else_body, + }); + if let Some(merge_block) = merge_block { + self.serialize_block(merge_block, order); + } + } + + fn enter_loop_scope(&mut self, lp: LoopId, header: BasicBlockId, exit: Option) { + let kind = ScopeKind::Loop { lp, header, exit }; + let current_scope = std::mem::take(&mut self.scope); + self.scope = Some(Scope { + kind, + parent: current_scope.map(Into::into), + }); + } + + fn enter_if_scope(&mut self, merge_block: Option) { + let kind = ScopeKind::If { merge_block }; + let current_scope = std::mem::take(&mut self.scope); + self.scope = Some(Scope { + kind, + parent: current_scope.map(Into::into), + }); + } + + fn exit_scope(&mut self) { + let current_scope = std::mem::take(&mut self.scope); + self.scope = current_scope.unwrap().parent.map(|parent| *parent); + } + + // NOTE: We assume loop has at most one canonical loop exit. + fn find_loop_exit(&self, lp: LoopId) -> Option { + let mut exit_candidates = vec![]; + for block_in_loop in self.loop_tree.iter_blocks_post_order(&self.cfg, lp) { + for &succ in self.cfg.succs(block_in_loop) { + if !self.loop_tree.is_block_in_loop(succ, lp) { + exit_candidates.push(succ); + } + } + } + + if exit_candidates.is_empty() { + return None; + } + + if exit_candidates.len() == 1 { + let candidate = exit_candidates[0]; + let exit = if let Some(mut df) = self.df.frontiers(candidate) { + debug_assert_eq!(self.df.frontier_num(candidate), 1); + df.next() + } else { + Some(candidate) + }; + return exit; + } + + // If a candidate is a dominance frontier of all other nodes, then the candidate + // is a loop exit. + for &cand in &exit_candidates { + if exit_candidates.iter().all(|&block| { + if block == cand { + true + } else if let Some(mut df) = self.df.frontiers(block) { + df.any(|frontier| frontier == cand) + } else { + true + } + }) { + return Some(cand); + } + } + + // If all candidates have the same dominance frontier, then the frontier block + // is the canonicalized loop exit. + let mut frontier: FxHashSet<_> = self + .df + .frontiers(exit_candidates.pop().unwrap()) + .map(std::iter::Iterator::collect) + .unwrap_or_default(); + for cand in exit_candidates { + for cand_frontier in self.df.frontiers(cand).unwrap() { + if !frontier.contains(&cand_frontier) { + frontier.remove(&cand_frontier); + } + } + } + debug_assert!(frontier.len() < 2); + frontier.iter().next().copied() + } + + fn analyze_terminator(&self, inst: InstId) -> TerminatorInfo { + debug_assert!(self.body.store.is_terminator(inst)); + + match self.body.store.branch_info(inst) { + BranchInfo::Jump(dest) => self.analyze_jump(dest), + BranchInfo::Branch(cond, then, else_) => { + self.analyze_branch(self.body.order.inst_block(inst), cond, then, else_) + } + BranchInfo::NotBranch => TerminatorInfo::NormalInst(inst), + } + } + + fn analyze_branch( + &self, + block: BasicBlockId, + cond: ValueId, + then: BasicBlockId, + else_: BasicBlockId, + ) -> TerminatorInfo { + let then = Box::new(self.analyze_dest(then)); + let else_ = Box::new(self.analyze_dest(else_)); + + let merge_block = match self.pd_tree.post_idom(block) { + PostIDom::Block(block) => { + if let Some(lp) = self.scope.as_ref().and_then(Scope::loop_recursive) { + if self.loop_tree.is_block_in_loop(block, lp) { + Some(block) + } else { + None + } + } else { + Some(block) + } + } + _ => None, + }; + + TerminatorInfo::If { + cond, + then, + else_, + merge_block, + } + } + + fn analyze_jump(&self, dest: BasicBlockId) -> TerminatorInfo { + self.analyze_dest(dest) + } + + fn analyze_dest(&self, dest: BasicBlockId) -> TerminatorInfo { + match &self.scope { + Some(scope) => { + if Some(dest) == scope.loop_header_recursive() { + TerminatorInfo::Continue + } else if Some(dest) == scope.loop_exit_recursive() { + TerminatorInfo::Break + } else if Some(dest) == scope.if_merge_block() { + TerminatorInfo::ToMergeBlock + } else { + TerminatorInfo::FallThrough(dest) + } + } + + None => TerminatorInfo::FallThrough(dest), + } + } +} + +struct Scope { + kind: ScopeKind, + parent: Option>, +} + +#[derive(Debug, Clone, Copy)] +enum ScopeKind { + Loop { + lp: LoopId, + header: BasicBlockId, + exit: Option, + }, + If { + merge_block: Option, + }, +} + +impl Scope { + fn loop_recursive(&self) -> Option { + match self.kind { + ScopeKind::Loop { lp, .. } => Some(lp), + _ => self.parent.as_ref()?.loop_recursive(), + } + } + + fn loop_header(&self) -> Option { + match self.kind { + ScopeKind::Loop { header, .. } => Some(header), + _ => None, + } + } + + fn loop_header_recursive(&self) -> Option { + match self.kind { + ScopeKind::Loop { header, .. } => Some(header), + _ => self.parent.as_ref()?.loop_header_recursive(), + } + } + + fn loop_exit_recursive(&self) -> Option { + match self.kind { + ScopeKind::Loop { exit, .. } => exit, + _ => self.parent.as_ref()?.loop_exit_recursive(), + } + } + + fn if_merge_block(&self) -> Option { + match self.kind { + ScopeKind::If { merge_block } => merge_block, + _ => None, + } + } +} + +#[derive(Debug, Clone)] +enum TerminatorInfo { + If { + cond: ValueId, + then: Box, + else_: Box, + merge_block: Option, + }, + ToMergeBlock, + Continue, + Break, + FallThrough(BasicBlockId), + NormalInst(InstId), +} + +#[cfg(test)] +mod tests { + use fe_mir::ir::{body_builder::BodyBuilder, inst::InstKind, FunctionId, SourceInfo, TypeId}; + + use super::*; + + fn body_builder() -> BodyBuilder { + BodyBuilder::new(FunctionId(0), SourceInfo::dummy()) + } + + fn serialize_func_body(func: &mut FunctionBody) -> impl Iterator { + InstSerializer::new(func).serialize().into_iter() + } + + fn expect_if( + inst: StructuralInst, + ) -> ( + impl Iterator, + impl Iterator, + ) { + match inst { + StructuralInst::If { then, else_, .. } => (then.into_iter(), else_.into_iter()), + _ => panic!("expect if inst"), + } + } + + fn expect_for(inst: StructuralInst) -> impl Iterator { + match inst { + StructuralInst::For { body } => body.into_iter(), + _ => panic!("expect if inst"), + } + } + + fn expect_break(inst: StructuralInst) { + assert!(matches!(inst, StructuralInst::Break)) + } + + fn expect_continue(inst: StructuralInst) { + assert!(matches!(inst, StructuralInst::Continue)) + } + + fn expect_return(func: &FunctionBody, inst: &StructuralInst) { + match inst { + StructuralInst::Inst(inst) => { + assert!(matches!( + func.store.inst_data(*inst).kind, + InstKind::Return { .. } + )) + } + _ => panic!("expect return"), + } + } + + #[test] + fn if_non_merge() { + // +------+ +-------+ + // | then | <-- | bb0 | + // +------+ +-------+ + // | + // | + // v + // +-------+ + // | else_ | + // +-------+ + let mut builder = body_builder(); + + let then = builder.make_block(); + let else_ = builder.make_block(); + + let dummy_ty = TypeId(0); + let v0 = builder.make_imm_from_bool(true, dummy_ty); + let unit = builder.make_unit(dummy_ty); + + builder.branch(v0, then, else_, SourceInfo::dummy()); + + builder.move_to_block(then); + builder.ret(unit, SourceInfo::dummy()); + + builder.move_to_block(else_); + builder.ret(unit, SourceInfo::dummy()); + + let mut func = builder.build(); + let mut order = serialize_func_body(&mut func); + + let (mut then, mut else_) = expect_if(order.next().unwrap()); + expect_return(&func, &then.next().unwrap()); + assert!(then.next().is_none()); + expect_return(&func, &else_.next().unwrap()); + assert!(else_.next().is_none()); + + assert!(order.next().is_none()); + } + + #[test] + fn if_merge() { + // +------+ +-------+ + // | then | <-- | bb0 | + // +------+ +-------+ + // | | + // | | + // | v + // | +-------+ + // | | else_ | + // | +-------+ + // | | + // | | + // | v + // | +-------+ + // +--------> | merge | + // +-------+ + let mut builder = body_builder(); + + let then = builder.make_block(); + let else_ = builder.make_block(); + let merge = builder.make_block(); + + let dummy_ty = TypeId(0); + let v0 = builder.make_imm_from_bool(true, dummy_ty); + let unit = builder.make_unit(dummy_ty); + + builder.branch(v0, then, else_, SourceInfo::dummy()); + + builder.move_to_block(then); + builder.jump(merge, SourceInfo::dummy()); + + builder.move_to_block(else_); + builder.jump(merge, SourceInfo::dummy()); + + builder.move_to_block(merge); + builder.ret(unit, SourceInfo::dummy()); + + let mut func = builder.build(); + let mut order = serialize_func_body(&mut func); + + let (mut then, mut else_) = expect_if(order.next().unwrap()); + assert!(then.next().is_none()); + assert!(else_.next().is_none()); + + expect_return(&func, &order.next().unwrap()); + assert!(order.next().is_none()); + } + + #[test] + fn simple_loop() { + // +--------+ + // | bb0 | -+ + // +--------+ | + // | | + // | | + // v | + // +--------+ | + // +> | header | | + // | +--------+ | + // | | | + // | | | + // | v | + // | +--------+ | + // +- | latch | | + // +--------+ | + // | | + // | | + // v | + // +--------+ | + // | exit | <+ + // +--------+ + let mut builder = body_builder(); + + let header = builder.make_block(); + let latch = builder.make_block(); + let exit = builder.make_block(); + + let dummy_ty = TypeId(0); + let v0 = builder.make_imm_from_bool(true, dummy_ty); + let unit = builder.make_unit(dummy_ty); + + builder.branch(v0, header, exit, SourceInfo::dummy()); + + builder.move_to_block(header); + builder.jump(latch, SourceInfo::dummy()); + + builder.move_to_block(latch); + builder.branch(v0, header, exit, SourceInfo::dummy()); + + builder.move_to_block(exit); + builder.ret(unit, SourceInfo::dummy()); + + let mut func = builder.build(); + let mut order = serialize_func_body(&mut func); + + let (mut lp, mut empty) = expect_if(order.next().unwrap()); + + let mut body = expect_for(lp.next().unwrap()); + let (mut continue_, mut break_) = expect_if(body.next().unwrap()); + assert!(body.next().is_none()); + + expect_continue(continue_.next().unwrap()); + assert!(continue_.next().is_none()); + + expect_break(break_.next().unwrap()); + assert!(break_.next().is_none()); + + assert!(empty.next().is_none()); + + expect_return(&func, &order.next().unwrap()); + assert!(order.next().is_none()); + } + + #[test] + fn loop_with_continue() { + // +-----+ + // +- | bb0 | + // | +-----+ + // | | + // | | + // | v + // | +---------------+ +-----+ + // | | bb1 | --> | bb3 | + // | +---------------+ +-----+ + // | | ^ ^ | + // | | | +---------+ + // | v | + // | +-----+ | + // | | bb4 | -+ + // | +-----+ + // | | + // | | + // | v + // | +-----+ + // +> | bb2 | + // +-----+ + let mut builder = body_builder(); + + let bb1 = builder.make_block(); + let bb2 = builder.make_block(); + let bb3 = builder.make_block(); + let bb4 = builder.make_block(); + + let dummy_ty = TypeId(0); + let v0 = builder.make_imm_from_bool(true, dummy_ty); + let unit = builder.make_unit(dummy_ty); + + builder.branch(v0, bb1, bb2, SourceInfo::dummy()); + + builder.move_to_block(bb1); + builder.branch(v0, bb3, bb4, SourceInfo::dummy()); + + builder.move_to_block(bb3); + builder.jump(bb1, SourceInfo::dummy()); + + builder.move_to_block(bb4); + builder.branch(v0, bb1, bb2, SourceInfo::dummy()); + + builder.move_to_block(bb2); + builder.ret(unit, SourceInfo::dummy()); + + let mut func = builder.build(); + let mut order = serialize_func_body(&mut func); + + let (mut lp, mut empty) = expect_if(order.next().unwrap()); + assert!(empty.next().is_none()); + + let mut body = expect_for(lp.next().unwrap()); + + let (mut continue_, mut empty) = expect_if(body.next().unwrap()); + expect_continue(continue_.next().unwrap()); + assert!(continue_.next().is_none()); + assert!(empty.next().is_none()); + + let (mut continue_, mut break_) = expect_if(body.next().unwrap()); + expect_continue(continue_.next().unwrap()); + assert!(continue_.next().is_none()); + expect_break(break_.next().unwrap()); + assert!(break_.next().is_none()); + + assert!(body.next().is_none()); + assert!(lp.next().is_none()); + + expect_return(&func, &order.next().unwrap()); + assert!(order.next().is_none()); + } + + #[test] + fn loop_with_break() { + // +-----+ + // +- | bb0 | + // | +-----+ + // | | + // | | +---------+ + // | v v | + // | +---------------+ +-----+ + // | | bb1 | --> | bb4 | + // | +---------------+ +-----+ + // | | | + // | | | + // | v | + // | +-----+ | + // | | bb3 | | + // | +-----+ | + // | | | + // | | | + // | v | + // | +-----+ | + // +> | bb2 | <---------------+ + // +-----+ + let mut builder = body_builder(); + + let bb1 = builder.make_block(); + let bb2 = builder.make_block(); + let bb3 = builder.make_block(); + let bb4 = builder.make_block(); + + let dummy_ty = TypeId(0); + let v0 = builder.make_imm_from_bool(true, dummy_ty); + let unit = builder.make_unit(dummy_ty); + + builder.branch(v0, bb1, bb2, SourceInfo::dummy()); + + builder.move_to_block(bb1); + builder.branch(v0, bb3, bb4, SourceInfo::dummy()); + + builder.move_to_block(bb3); + builder.jump(bb2, SourceInfo::dummy()); + + builder.move_to_block(bb4); + builder.branch(v0, bb1, bb2, SourceInfo::dummy()); + + builder.move_to_block(bb2); + builder.ret(unit, SourceInfo::dummy()); + + let mut func = builder.build(); + let mut order = serialize_func_body(&mut func); + + let (mut lp, mut empty) = expect_if(order.next().unwrap()); + assert!(empty.next().is_none()); + + let mut body = expect_for(lp.next().unwrap()); + + let (mut break_, mut latch) = expect_if(body.next().unwrap()); + expect_break(break_.next().unwrap()); + assert!(break_.next().is_none()); + + let (mut continue_, mut break_) = expect_if(latch.next().unwrap()); + assert!(latch.next().is_none()); + expect_continue(continue_.next().unwrap()); + assert!(continue_.next().is_none()); + expect_break(break_.next().unwrap()); + assert!(break_.next().is_none()); + + assert!(body.next().is_none()); + assert!(lp.next().is_none()); + + expect_return(&func, &order.next().unwrap()); + assert!(order.next().is_none()); + } + + #[test] + fn loop_no_guard() { + // +-----+ + // | bb0 | + // +-----+ + // | + // | + // v + // +-----+ + // | bb1 | <+ + // +-----+ | + // | | + // | | + // v | + // +-----+ | + // | bb2 | -+ + // +-----+ + // | + // | + // v + // +-----+ + // | bb3 | + // +-----+ + let mut builder = body_builder(); + + let bb1 = builder.make_block(); + let bb2 = builder.make_block(); + let bb3 = builder.make_block(); + + let dummy_ty = TypeId(0); + let v0 = builder.make_imm_from_bool(true, dummy_ty); + let unit = builder.make_unit(dummy_ty); + + builder.jump(bb1, SourceInfo::dummy()); + + builder.move_to_block(bb1); + builder.jump(bb2, SourceInfo::dummy()); + + builder.move_to_block(bb2); + builder.branch(v0, bb1, bb3, SourceInfo::dummy()); + + builder.move_to_block(bb3); + builder.ret(unit, SourceInfo::dummy()); + + let mut func = builder.build(); + let mut order = serialize_func_body(&mut func); + + let mut body = expect_for(order.next().unwrap()); + let (mut continue_, mut break_) = expect_if(body.next().unwrap()); + assert!(body.next().is_none()); + + expect_continue(continue_.next().unwrap()); + assert!(continue_.next().is_none()); + + expect_break(break_.next().unwrap()); + assert!(break_.next().is_none()); + + expect_return(&func, &order.next().unwrap()); + assert!(order.next().is_none()); + } + + #[test] + fn infinite_loop() { + // +-----+ + // | bb0 | + // +-----+ + // | + // | + // v + // +-----+ + // | bb1 | <+ + // +-----+ | + // | | + // | | + // v | + // +-----+ | + // | bb2 | -+ + // +-----+ + let mut builder = body_builder(); + + let bb1 = builder.make_block(); + let bb2 = builder.make_block(); + + builder.jump(bb1, SourceInfo::dummy()); + + builder.move_to_block(bb1); + builder.jump(bb2, SourceInfo::dummy()); + + builder.move_to_block(bb2); + builder.jump(bb1, SourceInfo::dummy()); + + let mut func = builder.build(); + let mut order = serialize_func_body(&mut func); + + let mut body = expect_for(order.next().unwrap()); + expect_continue(body.next().unwrap()); + assert!(body.next().is_none()); + + assert!(order.next().is_none()); + } +} diff --git a/crates/codegen/src/yul/isel/mod.rs b/crates/codegen/src/yul/isel/mod.rs index e6a246a6fa..f210a28064 100644 --- a/crates/codegen/src/yul/isel/mod.rs +++ b/crates/codegen/src/yul/isel/mod.rs @@ -1,25 +1,7 @@ -#![allow(unused)] -use fe_mir::{ - analysis::ControlFlowGraph, - ir::{FunctionBody, FunctionSignature, ValueId}, -}; -use fxhash::FxHashMap; -use smol_str::SmolStr; +pub mod context; +mod contract; +mod function; +mod inst_order; -use crate::db::CodegenDb; - -struct FuncLowerHelper<'db, 'a> { - db: &'db dyn CodegenDb, - value_map: FxHashMap, - sig: &'a FunctionSignature, - body: &'a FunctionBody, - cfg: ControlFlowGraph, - sink: Vec, - ret_value: Option, -} - -impl<'db, 'a> FuncLowerHelper<'db, 'a> { - fn lower_func(self) -> Vec { - todo!() - } -} +pub use contract::{lower_contract, lower_contract_deployable}; +pub use function::lower_function; diff --git a/crates/codegen/src/yul/legalize/body.rs b/crates/codegen/src/yul/legalize/body.rs index 4683402b59..9e3cbe5ae1 100644 --- a/crates/codegen/src/yul/legalize/body.rs +++ b/crates/codegen/src/yul/legalize/body.rs @@ -1,7 +1,8 @@ use fe_mir::ir::{ body_cursor::{BodyCursor, CursorLocation}, inst::InstKind, - FunctionBody, Inst, InstId, + value::AssignableValue, + FunctionBody, Inst, InstId, TypeId, TypeKind, Value, ValueId, }; use crate::db::CodegenDb; @@ -9,26 +10,76 @@ use crate::db::CodegenDb; use super::critical_edge::CriticalEdgeSplitter; pub fn legalize_func_body(db: &dyn CodegenDb, body: &mut FunctionBody) { - // Remove critical edges. CriticalEdgeSplitter::new().run(body); + legalize_func_arg(db, body); - // Remove zero-sized types usage. let mut cursor = BodyCursor::new_at_entry(body); loop { match cursor.loc() { CursorLocation::BlockTop(_) | CursorLocation::BlockBottom(_) => cursor.proceed(), CursorLocation::Inst(inst) => { - legalize_inst(db, cursor.body_mut(), inst); - cursor.proceed(); + legalize_inst(db, &mut cursor, inst); } CursorLocation::NoWhere => break, } } } -fn legalize_inst(db: &dyn CodegenDb, body: &mut FunctionBody, inst: InstId) { - legalize_inst_arg(db, body, inst); - legalize_inst_result(db, body, inst); +fn legalize_func_arg(db: &dyn CodegenDb, body: &mut FunctionBody) { + for value in body.store.func_args_mut() { + let ty = value.ty(); + if ty.is_contract(db.upcast()) { + let slot_ptr = make_storage_ptr(db, ty); + *value = slot_ptr; + } else if (ty.is_aggregate(db.upcast()) || ty.is_string(db.upcast())) + && !ty.is_zero_sized(db.upcast()) + { + change_ty(value, ty.make_mptr(db.upcast())) + } + } +} + +fn legalize_inst(db: &dyn CodegenDb, cursor: &mut BodyCursor, inst: InstId) { + if legalize_unit_construct(db, cursor, inst) { + return; + } + legalize_declared_ty(db, cursor.body_mut(), inst); + legalize_inst_arg(db, cursor.body_mut(), inst); + legalize_inst_result(db, cursor.body_mut(), inst); + cursor.proceed(); +} + +fn legalize_unit_construct(db: &dyn CodegenDb, cursor: &mut BodyCursor, inst: InstId) -> bool { + let should_remove = match &cursor.body().store.inst_data(inst).kind { + InstKind::Declare { local } => is_value_zst(db, cursor.body(), *local), + InstKind::AggregateConstruct { ty, .. } => ty.deref(db.upcast()).is_zero_sized(db.upcast()), + InstKind::AggregateAccess { .. } | InstKind::MapAccess { .. } => { + let result_value = cursor.body().store.inst_result(inst).unwrap(); + is_lvalue_zst(db, cursor.body(), result_value) + } + + _ => false, + }; + + if should_remove { + cursor.remove_inst() + } + + should_remove +} + +fn legalize_declared_ty(db: &dyn CodegenDb, body: &mut FunctionBody, inst_id: InstId) { + let value = match &body.store.inst_data(inst_id).kind { + InstKind::Declare { local } => *local, + _ => return, + }; + + let value_ty = body.store.value_ty(value); + if value_ty.is_aggregate(db.upcast()) { + let new_ty = value_ty.make_mptr(db.upcast()); + let value_data = body.store.value_data_mut(value); + change_ty(value_data, new_ty) + } } fn legalize_inst_arg(db: &dyn CodegenDb, body: &mut FunctionBody, inst_id: InstId) { @@ -36,31 +87,117 @@ fn legalize_inst_arg(db: &dyn CodegenDb, body: &mut FunctionBody, inst_id: InstI let dummy_inst = Inst::nop(); let mut inst = body.store.replace_inst(inst_id, dummy_inst); + for arg in inst.args() { + let ty = body.store.value_ty(arg); + if ty.is_string(db.upcast()) { + let string_ptr = ty.make_mptr(db.upcast()); + change_ty(body.store.value_data_mut(arg), string_ptr) + } + } + match &mut inst.kind { - InstKind::AggregateConstruct { args, .. } | InstKind::Call { args, .. } => { - args.retain(|arg| !body.store.value_ty(*arg).is_zero_sized(db.upcast())); + InstKind::AggregateConstruct { args, .. } => { + args.retain(|arg| !is_value_zst(db, body, *arg)); + } + + InstKind::Call { args, .. } => { + args.retain(|arg| !is_value_zst(db, body, *arg) && !is_value_contract(db, body, *arg)) } InstKind::Return { arg } => { - if arg - .map(|arg| body.store.value_ty(arg).is_zero_sized(db.upcast())) - .unwrap_or(false) - { + if arg.map(|arg| is_value_zst(db, body, arg)).unwrap_or(false) { *arg = None; } } + InstKind::MapAccess { key: arg, .. } | InstKind::Emit { arg } => { + let arg_ty = body.store.value_ty(*arg); + if arg_ty.is_zero_sized(db.upcast()) { + *arg = body.store.store_value(make_zst_ptr(db, arg_ty)); + } + } + _ => {} } body.store.replace_inst(inst_id, inst); } -/// Remove instruction result if its type is zero-sized. -fn legalize_inst_result(db: &dyn CodegenDb, body: &mut FunctionBody, inst: InstId) { - if let Some(result) = body.store.inst_result(inst) { - if body.store.value_ty(result).is_zero_sized(db.upcast()) { - body.store.remove_inst_result(inst) +fn legalize_inst_result(db: &dyn CodegenDb, body: &mut FunctionBody, inst_id: InstId) { + let result_value = if let Some(result) = body.store.inst_result(inst_id) { + result + } else { + return; + }; + + if is_lvalue_zst(db, body, result_value) { + body.store.remove_inst_result(inst_id); + return; + }; + + let value_id = if let Some(value_id) = result_value.value_id() { + value_id + } else { + return; + }; + let result_ty = body.store.value_ty(value_id); + let new_ty = if result_ty.is_aggregate(db.upcast()) || result_ty.is_string(db.upcast()) { + match &body.store.inst_data(inst_id).kind { + InstKind::AggregateAccess { value, .. } => { + let value_ty = body.store.value_ty(*value); + match &value_ty.data(db.upcast()).kind { + TypeKind::MPtr(..) => result_ty.make_mptr(db.upcast()), + TypeKind::SPtr(..) => result_ty.make_sptr(db.upcast()), + _ => unreachable!(), + } + } + _ => result_ty.make_mptr(db.upcast()), } + } else if result_ty.is_map(db.upcast()) { + result_ty.make_sptr(db.upcast()) + } else { + return; + }; + + let value = body.store.value_data_mut(value_id); + change_ty(value, new_ty); +} + +fn change_ty(value: &mut Value, new_ty: TypeId) { + match value { + Value::Local(val) => val.ty = new_ty, + Value::Immediate { ty, .. } + | Value::Temporary { ty, .. } + | Value::Unit { ty } + | Value::Constant { ty, .. } => *ty = new_ty, } } + +fn make_storage_ptr(db: &dyn CodegenDb, ty: TypeId) -> Value { + debug_assert!(ty.is_contract(db.upcast())); + let ty = ty.make_sptr(db.upcast()); + + Value::Immediate { imm: 0.into(), ty } +} + +fn make_zst_ptr(db: &dyn CodegenDb, ty: TypeId) -> Value { + debug_assert!(ty.is_zero_sized(db.upcast())); + let ty = ty.make_mptr(db.upcast()); + + Value::Immediate { imm: 0.into(), ty } +} + +/// Returns `true` if a value has a zero sized type. +fn is_value_zst(db: &dyn CodegenDb, body: &FunctionBody, value: ValueId) -> bool { + body.store.value_ty(value).is_zero_sized(db.upcast()) +} + +fn is_value_contract(db: &dyn CodegenDb, body: &FunctionBody, value: ValueId) -> bool { + let ty = body.store.value_ty(value); + ty.deref(db.upcast()).is_contract(db.upcast()) +} + +fn is_lvalue_zst(db: &dyn CodegenDb, body: &FunctionBody, lvalue: &AssignableValue) -> bool { + let ty = body.store.assignable_value_ty(db.upcast(), lvalue); + ty.is_zero_sized(db.upcast()) +} diff --git a/crates/codegen/src/yul/legalize/critical_edge.rs b/crates/codegen/src/yul/legalize/critical_edge.rs index 40a898c18b..aa468db29e 100644 --- a/crates/codegen/src/yul/legalize/critical_edge.rs +++ b/crates/codegen/src/yul/legalize/critical_edge.rs @@ -22,7 +22,7 @@ impl CriticalEdgeSplitter { pub fn run(&mut self, func: &mut FunctionBody) { let cfg = ControlFlowGraph::compute(func); - for block in func.order.iter_block() { + for block in cfg.post_order() { let terminator = func.order.terminator(&func.store, block).unwrap(); self.add_critical_edges(terminator, func, &cfg); } diff --git a/crates/codegen/src/yul/legalize/signature.rs b/crates/codegen/src/yul/legalize/signature.rs index 1354f70f8e..94a5118308 100644 --- a/crates/codegen/src/yul/legalize/signature.rs +++ b/crates/codegen/src/yul/legalize/signature.rs @@ -2,7 +2,24 @@ use fe_mir::ir::FunctionSignature; use crate::db::CodegenDb; -pub fn legalize_func_signature(_db: &dyn CodegenDb, _sig: &mut FunctionSignature) { - // TODO: Remove zero sized types from arguments, also remove return type if - // it's zero-sized +pub fn legalize_func_signature(db: &dyn CodegenDb, sig: &mut FunctionSignature) { + // Remove param if the type is contract or zero-sized. + let params = &mut sig.params; + params + .retain(|param| !param.ty.is_contract(db.upcast()) && !param.ty.is_zero_sized(db.upcast())); + + // Legalize param types. + for param in params.iter_mut() { + param.ty = db.codegen_legalized_type(param.ty); + } + + if let Some(ret_ty) = sig.return_type { + // Remove return type if the type is contract or zero-sized. + if ret_ty.is_contract(db.upcast()) || ret_ty.is_zero_sized(db.upcast()) { + sig.return_type = None; + } else { + // Legalize param types. + sig.return_type = Some(db.codegen_legalized_type(ret_ty)); + } + } } diff --git a/crates/codegen/src/yul/mod.rs b/crates/codegen/src/yul/mod.rs index ba32f5b6eb..6e7e95457e 100644 --- a/crates/codegen/src/yul/mod.rs +++ b/crates/codegen/src/yul/mod.rs @@ -1,4 +1,26 @@ +use std::borrow::Cow; + pub mod isel; pub mod legalize; +pub mod runtime; + +mod slot_size; + +use yultsur::*; + +/// A helper struct to abstract ident and expr. +struct YulVariable<'a>(Cow<'a, str>); + +impl<'a> YulVariable<'a> { + fn expr(&self) -> yul::Expression { + identifier_expression! {(format!{"${}", self.0})} + } + + fn ident(&self) -> yul::Identifier { + identifier! {(format!{"${}", self.0})} + } -mod inst_order; + fn new(name: impl Into>) -> Self { + Self(name.into()) + } +} diff --git a/crates/codegen/src/yul/runtime/abi.rs b/crates/codegen/src/yul/runtime/abi.rs new file mode 100644 index 0000000000..defc0ace31 --- /dev/null +++ b/crates/codegen/src/yul/runtime/abi.rs @@ -0,0 +1,675 @@ +use crate::{ + db::CodegenDb, + yul::{ + runtime::{error_revert_numeric, make_ptr}, + slot_size::{yul_primitive_type, SLOT_SIZE}, + YulVariable, + }, +}; + +use super::{AbiSrcLocation, DefaultRuntimeProvider, RuntimeFunction, RuntimeProvider}; + +use fe_mir::ir::{self, types::ArrayDef, TypeId, TypeKind}; +use yultsur::*; + +pub(super) fn make_abi_encode_primitive_type( + provider: &mut DefaultRuntimeProvider, + db: &dyn CodegenDb, + func_name: &str, + legalized_ty: TypeId, + is_dst_storage: bool, +) -> RuntimeFunction { + let func_name = YulVariable::new(func_name); + let src = YulVariable::new("src"); + let dst = YulVariable::new("dst"); + let enc_size = YulVariable::new("enc_size"); + let func_def = function_definition! { + function [func_name.ident()]([src.ident()], [dst.ident()]) -> [enc_size.ident()] { + ([src.ident()] := [provider.primitive_cast(db, src.expr(), legalized_ty)]) + ([yul::Statement::Expression(provider.ptr_store( + db, + dst.expr(), + src.expr(), + make_ptr(db, yul_primitive_type(db), is_dst_storage), + ))]) + ([enc_size.ident()] := 32) + } + }; + + RuntimeFunction::from_statement(func_def) +} + +pub(super) fn make_abi_encode_static_array_type( + provider: &mut DefaultRuntimeProvider, + db: &dyn CodegenDb, + func_name: &str, + legalized_ty: TypeId, +) -> RuntimeFunction { + let is_dst_storage = legalized_ty.is_sptr(db.upcast()); + let deref_ty = legalized_ty.deref(db.upcast()); + let (elem_ty, len) = match &deref_ty.data(db.upcast()).kind { + ir::TypeKind::Array(def) => (def.elem_ty, def.len), + _ => unreachable!(), + }; + let elem_abi_ty = db.codegen_abi_type(elem_ty); + let elem_ptr_ty = make_ptr(db, elem_ty, false); + let elem_ty_size = deref_ty.array_elem_size(db.upcast(), SLOT_SIZE); + + let func_name = YulVariable::new(func_name); + let src = YulVariable::new("src"); + let dst = YulVariable::new("dst"); + let enc_size = YulVariable::new("enc_size"); + let header_size = elem_abi_ty.header_size(); + let iter_count = literal_expression! {(len)}; + + let func = function_definition! { + function [func_name.ident()]([src.ident()], [dst.ident()]) -> [enc_size.ident()] { + (for {(let i := 0)} (lt(i, [iter_count])) {(i := (add(i, 1)))} + { + + (pop([provider.abi_encode(db, src.expr(), dst.expr(), elem_ptr_ty, is_dst_storage)])) + ([src.ident()] := add([src.expr()], [literal_expression!{(elem_ty_size)}])) + ([dst.ident()] := add([dst.expr()], [literal_expression!{(header_size)}])) + }) + ([enc_size.ident()] := [literal_expression! {(header_size * len)}]) + } + }; + + RuntimeFunction::from_statement(func) +} + +pub(super) fn make_abi_encode_static_aggregate_type( + provider: &mut DefaultRuntimeProvider, + db: &dyn CodegenDb, + func_name: &str, + legalized_ty: TypeId, +) -> RuntimeFunction { + let func_name = YulVariable::new(func_name); + let is_dst_storage = legalized_ty.is_sptr(db.upcast()); + let deref_ty = legalized_ty.deref(db.upcast()); + let src = YulVariable::new("src"); + let dst = YulVariable::new("dst"); + let enc_size = YulVariable::new("enc_size"); + let field_enc_size = YulVariable::new("field_enc_size"); + let mut body = vec![ + statement! {[enc_size.ident()] := 0 }, + statement! {let [field_enc_size.ident()] := 0 }, + ]; + let field_num = deref_ty.aggregate_field_num(db.upcast()); + + for idx in 0..field_num { + let field_ty = deref_ty.projection_ty_imm(db.upcast(), idx); + let field_ty_ptr = make_ptr(db, field_ty, false); + let field_offset = deref_ty.aggregate_elem_offset(db.upcast(), idx, SLOT_SIZE); + let src_offset = expression! { add([src.expr()], [literal_expression!{(field_offset)}]) }; + body.push(statement!{ + [field_enc_size.ident()] := [provider.abi_encode(db, src_offset, dst.expr(), field_ty_ptr, is_dst_storage)] + }); + body.push(statement! { + [enc_size.ident()] := add([enc_size.expr()], [field_enc_size.expr()]) + }); + + if idx < field_num - 1 { + body.push(assignment! {[dst.ident()] := add([dst.expr()], [field_enc_size.expr()])}); + } + } + + let func_def = yul::FunctionDefinition { + name: func_name.ident(), + parameters: vec![src.ident(), dst.ident()], + returns: vec![enc_size.ident()], + block: yul::Block { statements: body }, + }; + + RuntimeFunction(func_def) +} + +pub(super) fn make_abi_encode_string_type( + provider: &mut DefaultRuntimeProvider, + db: &dyn CodegenDb, + func_name: &str, + is_dst_storage: bool, +) -> RuntimeFunction { + let func_name = YulVariable::new(func_name); + let src = YulVariable::new("src"); + let dst = YulVariable::new("dst"); + let string_len = YulVariable::new("string_len"); + let enc_size = YulVariable::new("enc_size"); + + let func_def = function_definition! { + function [func_name.ident()]([src.ident()], [dst.ident()]) -> [enc_size.ident()] { + (let [string_len.ident()] := mload([src.expr()])) + ([enc_size.ident()] := add(32, (mul([string_len.expr()], 32)))) + (let data_size := add(32, [string_len.expr()])) + ([yul::Statement::Expression(provider.ptr_copy(db, src.expr(), dst.expr(), literal_expression!{data_size}, false, is_dst_storage))]) + ([enc_size.ident()] := mul((div((add(data_size, 31)), 32)), 32)) + } + }; + RuntimeFunction::from_statement(func_def) +} + +pub(super) fn make_abi_encode_bytes_type( + provider: &mut DefaultRuntimeProvider, + db: &dyn CodegenDb, + func_name: &str, + len: usize, + is_dst_storage: bool, +) -> RuntimeFunction { + let func_name = YulVariable::new(func_name); + let src = YulVariable::new("src"); + let dst = YulVariable::new("dst"); + let enc_size = YulVariable::new("enc_size"); + let dst_len_ty = make_ptr(db, yul_primitive_type(db), is_dst_storage); + + let func_def = function_definition! { + function [func_name.ident()]([src.ident()], [dst.ident()]) -> [enc_size.ident()] { + ([enc_size.ident()] := [literal_expression!{ (ceil_32(32 + len)) }]) + ([yul::Statement::Expression(provider.ptr_store(db, dst.expr(), literal_expression!{ (len) }, dst_len_ty))]) + ([dst.ident()] := add(32, [dst.expr()])) + ([yul::Statement::Expression(provider.ptr_copy(db, src.expr(), dst.expr(), literal_expression!{(len)}, false, is_dst_storage))]) + } + }; + RuntimeFunction::from_statement(func_def) +} + +pub(super) fn make_abi_encode_value_seq( + provider: &mut DefaultRuntimeProvider, + db: &dyn CodegenDb, + func_name: &str, + value_tys: &[TypeId], + is_dst_storage: bool, +) -> RuntimeFunction { + let func_name = YulVariable::new(func_name); + let value_num = value_tys.len(); + let abi_tys: Vec<_> = value_tys + .iter() + .map(|ty| db.codegen_abi_type(ty.deref(db.upcast()))) + .collect(); + let dst = YulVariable::new("dst"); + let header_ptr = YulVariable::new("header_ptr"); + let enc_size = YulVariable::new("enc_size"); + let data_ptr = YulVariable::new("data_ptr"); + let values: Vec<_> = (0..value_num) + .map(|idx| YulVariable::new(format!("value{}", idx))) + .collect(); + + let total_header_size = + literal_expression! { (abi_tys.iter().fold(0, |acc, ty| acc + ty.header_size())) }; + let mut body = statements! { + (let [header_ptr.ident()] := [dst.expr()]) + ([enc_size.ident()] := [total_header_size]) + (let [data_ptr.ident()] := add([dst.expr()], [enc_size.expr()])) + }; + + for i in 0..value_num { + let ty = value_tys[i]; + let abi_ty = &abi_tys[i]; + let value = &values[i]; + let header_size = literal_expression! { (abi_ty.header_size()) }; + let stmts = if abi_ty.is_static() { + statements! { + (pop([provider.abi_encode(db, value.expr(), header_ptr.expr(), ty, is_dst_storage)])) + ([header_ptr.ident()] := add([header_ptr.expr()], [header_size])) + } + } else { + let header_ty = make_ptr(db, yul_primitive_type(db), is_dst_storage); + statements! { + ([yul::Statement::Expression(provider.ptr_store(db, header_ptr.expr(), enc_size.expr(), header_ty))]) + ([enc_size.ident()] := add([provider.abi_encode(db, value.expr(), data_ptr.expr(), ty, is_dst_storage)], [enc_size.expr()])) + ([header_ptr.ident()] := add([header_ptr.expr()], [header_size])) + ([data_ptr.ident()] := add([dst.expr()], [enc_size.expr()])) + } + }; + body.extend_from_slice(&stmts); + } + + let mut parameters = vec![dst.ident()]; + for value in values { + parameters.push(value.ident()); + } + + let func_def = yul::FunctionDefinition { + name: func_name.ident(), + parameters, + returns: vec![enc_size.ident()], + block: yul::Block { statements: body }, + }; + + RuntimeFunction(func_def) +} + +pub(super) fn make_abi_decode( + provider: &mut DefaultRuntimeProvider, + db: &dyn CodegenDb, + func_name: &str, + types: &[TypeId], + abi_loc: AbiSrcLocation, +) -> RuntimeFunction { + let func_name = YulVariable::new(func_name); + let header_size = types + .iter() + .fold(0, |acc, ty| acc + db.codegen_abi_type(*ty).header_size()); + let src = YulVariable::new("$src"); + let enc_size = YulVariable::new("$enc_size"); + let header_ptr = YulVariable::new("header_ptr"); + let data_offset = YulVariable::new("data_offset"); + let tmp_offset = YulVariable::new("tmp_offset"); + let returns: Vec<_> = (0..types.len()) + .map(|i| YulVariable::new(format!("$ret{}", i))) + .collect(); + + let abi_enc_size = abi_enc_size(db, types); + let size_check = match abi_enc_size { + AbiEncodingSize::Static(size) => statements! { + (if (iszero((eq([enc_size.expr()], [literal_expression!{(size)}])))) + { [revert_with_invalid_abi_data(provider, db)] + }) + }, + AbiEncodingSize::Bounded { min, max } => statements! { + (if (or( + (lt([enc_size.expr()], [literal_expression!{(min)}])), + (gt([enc_size.expr()], [literal_expression!{(max)}])) + )) { + [revert_with_invalid_abi_data(provider, db)] + }) + }, + }; + + let mut body = statements! { + (let [header_ptr.ident()] := [src.expr()]) + (let [data_offset.ident()] := [literal_expression!{ (header_size) }]) + (let [tmp_offset.ident()] := 0) + }; + for i in 0..returns.len() { + let ret_value = &returns[i]; + let field_ty = types[i]; + let field_abi_ty = db.codegen_abi_type(field_ty.deref(db.upcast())); + if field_abi_ty.is_static() { + body.push(statement!{ [ret_value.ident()] := [provider.abi_decode_static(db, header_ptr.expr(), field_ty, abi_loc)] }); + } else { + let identifiers = identifiers! { + [ret_value.ident()] + [tmp_offset.ident()] + }; + body.push(yul::Statement::Assignment(yul::Assignment { + identifiers, + expression: provider.abi_decode_dynamic( + db, + expression! {add([src.expr()], [data_offset.expr()])}, + field_ty, + abi_loc, + ), + })); + body.push(statement! { ([data_offset.ident()] := add([data_offset.expr()], [tmp_offset.expr()])) }); + }; + + let field_header_size = literal_expression! { (field_abi_ty.header_size()) }; + body.push( + statement! { [header_ptr.ident()] := add([header_ptr.expr()], [field_header_size]) }, + ); + } + + let offset_check = match abi_enc_size { + AbiEncodingSize::Static(_) => vec![], + AbiEncodingSize::Bounded { .. } => statements! { + (if (iszero((eq([enc_size.expr()], [data_offset.expr()])))) { [revert_with_invalid_abi_data(provider, db)] }) + }, + }; + + let returns: Vec<_> = returns.iter().map(YulVariable::ident).collect(); + let func_def = function_definition! { + function [func_name.ident()]([src.ident()], [enc_size.ident()]) -> [returns...] { + [size_check...] + [body...] + [offset_check...] + } + }; + RuntimeFunction::from_statement(func_def) +} + +impl DefaultRuntimeProvider { + fn abi_decode_static( + &mut self, + db: &dyn CodegenDb, + src: yul::Expression, + ty: TypeId, + abi_loc: AbiSrcLocation, + ) -> yul::Expression { + let ty = db.codegen_legalized_type(ty).deref(db.upcast()); + let abi_ty = db.codegen_abi_type(ty.deref(db.upcast())); + debug_assert!(abi_ty.is_static()); + + let func_name_postfix = match abi_loc { + AbiSrcLocation::CallData => "call_data", + AbiSrcLocation::Memory => "memory", + }; + + let args = vec![src]; + if ty.is_primitive(db.upcast()) { + let name = format! { + "$abi_decode_primitive_type_{}_from_{}", + ty.0, func_name_postfix, + }; + return self.create_then_call(&name, args, |provider| { + make_abi_decode_primitive_type(provider, db, &name, ty, abi_loc) + }); + } + + let name = format! { + "$abi_decode_static_aggregate_type_{}_from_{}", + ty.0, func_name_postfix, + }; + self.create_then_call(&name, args, |provider| { + make_abi_decode_static_aggregate_type(provider, db, &name, ty, abi_loc) + }) + } + + fn abi_decode_dynamic( + &mut self, + db: &dyn CodegenDb, + src: yul::Expression, + ty: TypeId, + abi_loc: AbiSrcLocation, + ) -> yul::Expression { + let ty = db.codegen_legalized_type(ty).deref(db.upcast()); + let abi_ty = db.codegen_abi_type(ty.deref(db.upcast())); + debug_assert!(!abi_ty.is_static()); + + let func_name_postfix = match abi_loc { + AbiSrcLocation::CallData => "call_data", + AbiSrcLocation::Memory => "memory", + }; + + let mut args = vec![src]; + if abi_ty.is_string() { + let len = match &ty.data(db.upcast()).kind { + TypeKind::String(len) => *len, + _ => unreachable!(), + }; + args.push(literal_expression! {(len)}); + let name = format! {"$abi_decode_string_from_{}", func_name_postfix}; + self.create_then_call(&name, args, |provider| { + make_abi_decode_string_type(provider, db, &name, abi_loc) + }) + } else if abi_ty.is_bytes() { + let len = match &ty.data(db.upcast()).kind { + TypeKind::Array(ArrayDef { len, .. }) => *len, + _ => unreachable!(), + }; + args.push(literal_expression! {(len)}); + let name = format! {"$abi_decode_bytes_from_{}", func_name_postfix}; + self.create_then_call(&name, args, |provider| { + make_abi_decode_bytes_type(provider, db, &name, abi_loc) + }) + } else { + todo! {} + } + } +} + +fn make_abi_decode_primitive_type( + provider: &mut DefaultRuntimeProvider, + db: &dyn CodegenDb, + func_name: &str, + ty: TypeId, + abi_loc: AbiSrcLocation, +) -> RuntimeFunction { + debug_assert! {ty.is_primitive(db.upcast())} + let func_name = YulVariable::new(func_name); + let src = YulVariable::new("src"); + let ret = YulVariable::new("ret"); + + let decode = match abi_loc { + AbiSrcLocation::CallData => { + statement! { [ret.ident()] := calldataload([src.expr()]) } + } + AbiSrcLocation::Memory => { + statement! { [ret.ident()] := mload([src.expr()]) } + } + }; + + let ty_size_bits = ty.size_of(db.upcast(), SLOT_SIZE) * 8; + let validation = if ty_size_bits == 256 { + statements! {} + } else if ty.is_signed(db.upcast()) { + let shift_num = literal_expression! { ( ty_size_bits - 1) }; + let tmp1 = YulVariable::new("tmp1"); + let tmp2 = YulVariable::new("tmp2"); + statements! { + (let [tmp1.ident()] := iszero((shr([shift_num.clone()], [ret.expr()])))) + (let [tmp2.ident()] := iszero((shr([shift_num], (not([ret.expr()])))))) + (if (iszero((or([tmp1.expr()], [tmp2.expr()])))) { + [revert_with_invalid_abi_data(provider, db)] + }) + } + } else { + let shift_num = literal_expression! { ( ty_size_bits) }; + let tmp = YulVariable::new("tmp"); + statements! { + (let [tmp.ident()] := iszero((shr([shift_num], [ret.expr()])))) + (if (iszero([tmp.expr()])) { + [revert_with_invalid_abi_data(provider, db)] + }) + } + }; + + let func = function_definition! { + function [func_name.ident()]([src.ident()]) -> [ret.ident()] { + ([decode]) + [validation...] + } + }; + + RuntimeFunction::from_statement(func) +} + +fn make_abi_decode_static_aggregate_type( + provider: &mut DefaultRuntimeProvider, + db: &dyn CodegenDb, + func_name: &str, + ty: TypeId, + abi_loc: AbiSrcLocation, +) -> RuntimeFunction { + debug_assert!(ty.is_aggregate(db.upcast())); + + let func_name = YulVariable::new(func_name); + let src = YulVariable::new("src"); + let ret = YulVariable::new("ret"); + let field_data = YulVariable::new("field_data"); + let type_size = literal_expression! { (ty.size_of(db.upcast(), SLOT_SIZE)) }; + + let mut body = statements! { + (let [field_data.ident()] := 0) + ([ret.ident()] := [provider.alloc(db, type_size)]) + }; + + let field_num = ty.aggregate_field_num(db.upcast()); + for idx in 0..field_num { + let field_ty = ty.projection_ty_imm(db.upcast(), idx); + let field_ty_size = field_ty.size_of(db.upcast(), SLOT_SIZE); + body.push(statement! { [field_data.ident()] := [provider.abi_decode_static(db, src.expr(), field_ty, abi_loc)] }); + + let dst_offset = + literal_expression! { (ty.aggregate_elem_offset(db.upcast(), idx, SLOT_SIZE)) }; + let field_ty_ptr = make_ptr(db, field_ty, false); + if field_ty.is_primitive(db.upcast()) { + body.push(yul::Statement::Expression(provider.ptr_store( + db, + expression! {add([ret.expr()], [dst_offset])}, + field_data.expr(), + field_ty_ptr, + ))); + } else { + body.push(yul::Statement::Expression(provider.ptr_copy( + db, + expression! {add([ret.expr()], [dst_offset])}, + field_data.expr(), + literal_expression! { (field_ty_size) }, + false, + false, + ))); + } + + if idx < field_num - 1 { + let abi_field_ty = db.codegen_abi_type(field_ty); + let field_abi_ty_size = literal_expression! { (abi_field_ty.header_size()) }; + body.push(assignment! {[src.ident()] := add([src.expr()], + [field_abi_ty_size])}); + } + } + + let func_def = yul::FunctionDefinition { + name: func_name.ident(), + parameters: vec![src.ident()], + returns: vec![ret.ident()], + block: yul::Block { statements: body }, + }; + + RuntimeFunction(func_def) +} + +fn make_abi_decode_string_type( + provider: &mut DefaultRuntimeProvider, + db: &dyn CodegenDb, + func_name: &str, + abi_loc: AbiSrcLocation, +) -> RuntimeFunction { + let func_name = YulVariable::new(func_name); + let src = YulVariable::new("src"); + let decoded_data = YulVariable::new("decoded_data"); + let decoded_size = YulVariable::new("decoded_size"); + let max_len = YulVariable::new("max_len"); + let string_size = YulVariable::new("string_size"); + let dst_size = YulVariable::new("dst_size"); + let end_word = YulVariable::new("end_word"); + let end_word_ptr = YulVariable::new("end_word_ptr"); + let padding_size_bits = YulVariable::new("padding_size_bits"); + let primitive_ty_ptr = make_ptr(db, yul_primitive_type(db), false); + + let func = function_definition! { + function [func_name.ident()]([src.ident()], [max_len.ident()]) -> [(vec![decoded_data.ident(), decoded_size.ident()])...] { + (let string_len := [provider.abi_decode_static(db, src.expr(), primitive_ty_ptr, abi_loc)]) + (if (gt(string_len, [max_len.expr()])) { [revert_with_invalid_abi_data(provider, db)] } ) + (let [string_size.ident()] := add(string_len, 32)) + ([decoded_size.ident()] := mul((div((add([string_size.expr()], 31)), 32)), 32)) + (let [end_word_ptr.ident()] := sub((add([src.expr()], [decoded_size.expr()])), 32)) + (let [end_word.ident()] := [provider.abi_decode_static(db, end_word_ptr.expr(), primitive_ty_ptr, abi_loc)]) + (let [padding_size_bits.ident()] := mul((sub([decoded_size.expr()], [string_size.expr()])), 8)) + [(check_right_padding(provider, db, end_word.expr(), padding_size_bits.expr()))...] + (let [dst_size.ident()] := add([max_len.expr()], 32)) + ([decoded_data.ident()] := [provider.alloc(db, dst_size.expr())]) + ([ptr_copy_decode(provider, db, src.expr(), decoded_data.expr(), string_size.expr(), abi_loc)]) + } + }; + + RuntimeFunction::from_statement(func) +} + +fn make_abi_decode_bytes_type( + provider: &mut DefaultRuntimeProvider, + db: &dyn CodegenDb, + func_name: &str, + abi_loc: AbiSrcLocation, +) -> RuntimeFunction { + let func_name = YulVariable::new(func_name); + let src = YulVariable::new("src"); + let decoded_data = YulVariable::new("decoded_data"); + let decoded_size = YulVariable::new("decoded_size"); + let max_len = YulVariable::new("max_len"); + let bytes_size = YulVariable::new("bytes_size"); + let end_word = YulVariable::new("end_word"); + let end_word_ptr = YulVariable::new("end_word_ptr"); + let padding_size_bits = YulVariable::new("padding_size_bits"); + let primitive_ty_ptr = make_ptr(db, yul_primitive_type(db), false); + + let func = function_definition! { + function [func_name.ident()]([src.ident()], [max_len.ident()]) -> [(vec![decoded_data.ident(),decoded_size.ident()])...] { + (let [bytes_size.ident()] := [provider.abi_decode_static(db, src.expr(), primitive_ty_ptr, abi_loc)]) + (if (iszero((eq([bytes_size.expr()], [max_len.expr()])))) { [revert_with_invalid_abi_data(provider, db)] } ) + ([src.ident()] := add([src.expr()], 32)) + (let padded_data_size := mul((div((add([bytes_size.expr()], 31)), 32)), 32)) + ([decoded_size.ident()] := add(padded_data_size, 32)) + (let [end_word_ptr.ident()] := sub((add([src.expr()], padded_data_size)), 32)) + (let [end_word.ident()] := [provider.abi_decode_static(db, end_word_ptr.expr(), primitive_ty_ptr, abi_loc)]) + (let [padding_size_bits.ident()] := mul((sub(padded_data_size, [bytes_size.expr()])), 8)) + [(check_right_padding(provider, db, end_word.expr(), padding_size_bits.expr()))...] + ([decoded_data.ident()] := [provider.alloc(db, max_len.expr())]) + ([ptr_copy_decode(provider, db, src.expr(), decoded_data.expr(), bytes_size.expr(), abi_loc)]) + } + }; + + RuntimeFunction::from_statement(func) +} + +enum AbiEncodingSize { + Static(usize), + Bounded { min: usize, max: usize }, +} + +fn abi_enc_size(db: &dyn CodegenDb, types: &[TypeId]) -> AbiEncodingSize { + let mut min = 0; + let mut max = 0; + for &ty in types { + let legalized_ty = db.codegen_legalized_type(ty); + let abi_ty = db.codegen_abi_type(legalized_ty); + let maximum_size = db.codegen_abi_type_maximum_size(ty); + if abi_ty.is_string() { + min += 64; + } else { + min += maximum_size + }; + max += maximum_size; + } + + if min == max { + AbiEncodingSize::Static(min) + } else { + AbiEncodingSize::Bounded { min, max } + } +} + +fn revert_with_invalid_abi_data( + provider: &mut dyn RuntimeProvider, + db: &dyn CodegenDb, +) -> yul::Statement { + const ERROR_INVALID_ABI_DATA: usize = 0x103; + let error_code = literal_expression! { (ERROR_INVALID_ABI_DATA) }; + error_revert_numeric(provider, db, error_code) +} + +fn check_right_padding( + provider: &mut dyn RuntimeProvider, + db: &dyn CodegenDb, + val: yul::Expression, + size_bits: yul::Expression, +) -> Vec { + statements! { + (let bits_shifted := sub(256, [size_bits])) + (let is_ok := iszero((shl(bits_shifted, [val])))) + (if (iszero((is_ok))) { + [revert_with_invalid_abi_data(provider, db)] + }) + } +} + +fn ptr_copy_decode( + provider: &mut DefaultRuntimeProvider, + db: &dyn CodegenDb, + src: yul::Expression, + dst: yul::Expression, + len: yul::Expression, + loc: AbiSrcLocation, +) -> yul::Statement { + match loc { + AbiSrcLocation::CallData => { + statement! { calldatacopy([dst], [src], [len]) } + } + AbiSrcLocation::Memory => { + yul::Statement::Expression(provider.ptr_copy(db, src, dst, len, false, false)) + } + } +} + +fn ceil_32(len: usize) -> usize { + ((len + 31) / 32) * 32 +} diff --git a/crates/codegen/src/yul/runtime/contract.rs b/crates/codegen/src/yul/runtime/contract.rs new file mode 100644 index 0000000000..d85321b377 --- /dev/null +++ b/crates/codegen/src/yul/runtime/contract.rs @@ -0,0 +1,127 @@ +use crate::{ + db::CodegenDb, + yul::{runtime::AbiSrcLocation, YulVariable}, +}; + +use super::{DefaultRuntimeProvider, RuntimeFunction, RuntimeProvider}; + +use fe_analyzer::namespace::items::ContractId; +use fe_mir::ir::{FunctionId, Type, TypeKind}; + +use yultsur::*; + +pub(super) fn make_create( + provider: &mut DefaultRuntimeProvider, + db: &dyn CodegenDb, + func_name: &str, + contract: ContractId, +) -> RuntimeFunction { + let func_name = YulVariable::new(func_name); + let contract_symbol = literal_expression! { + (format!(r#""{}""#, db.codegen_contract_deployer_symbol_name(contract))) + }; + + let size = YulVariable::new("size"); + let value = YulVariable::new("value"); + let func = function_definition! { + function [func_name.ident()]([value.ident()]) -> addr { + (let [size.ident()] := datasize([contract_symbol.clone()])) + (let mem_ptr := [provider.avail(db)]) + (let contract_ptr := dataoffset([contract_symbol])) + (datacopy(mem_ptr, contract_ptr, [size.expr()])) + (addr := create([value.expr()], mem_ptr, [size.expr()])) + } + }; + + RuntimeFunction::from_statement(func) +} + +pub(super) fn make_create2( + provider: &mut DefaultRuntimeProvider, + db: &dyn CodegenDb, + func_name: &str, + contract: ContractId, +) -> RuntimeFunction { + let func_name = YulVariable::new(func_name); + let contract_symbol = literal_expression! { + (format!(r#""{}""#, db.codegen_contract_deployer_symbol_name(contract))) + }; + + let size = YulVariable::new("size"); + let value = YulVariable::new("value"); + let func = function_definition! { + function [func_name.ident()]([value.ident()], salt) -> addr { + (let [size.ident()] := datasize([contract_symbol.clone()])) + (let mem_ptr := [provider.avail(db)]) + (let contract_ptr := dataoffset([contract_symbol])) + (datacopy(mem_ptr, contract_ptr, [size.expr()])) + (addr := create2([value.expr()], mem_ptr, [size.expr()], salt)) + } + }; + + RuntimeFunction::from_statement(func) +} + +pub(super) fn make_external_call( + provider: &mut DefaultRuntimeProvider, + db: &dyn CodegenDb, + func_name: &str, + function: FunctionId, +) -> RuntimeFunction { + let func_name = YulVariable::new(func_name); + let sig = db.codegen_legalized_signature(function); + let param_num = sig.params.len(); + + let mut args = Vec::with_capacity(param_num); + let mut arg_tys = Vec::with_capacity(param_num); + for param in &sig.params { + args.push(YulVariable::new(param.name.as_str())); + arg_tys.push(param.ty); + } + let ret_ty = sig.return_type; + + let func_addr = YulVariable::new("func_addr"); + let params: Vec<_> = args.iter().map(YulVariable::ident).collect(); + let params_expr: Vec<_> = args.iter().map(YulVariable::expr).collect(); + let input = YulVariable::new("input"); + let input_size = YulVariable::new("input_size"); + let output_size = YulVariable::new("output_size"); + let output = YulVariable::new("output"); + + let func_selector = literal_expression! { (format!{"0x{}", db.codegen_abi_function(function).selector().hex()}) }; + let selector_ty = db.mir_intern_type(Type::new(TypeKind::U32, None).into()); + + let mut body = statements! { + (let [input.ident()] := [provider.avail(db)]) + [yul::Statement::Expression(provider.ptr_store(db, input.expr(), func_selector, selector_ty.make_mptr(db.upcast())))] + (let [input_size.ident()] := add(4, [provider.abi_encode_seq(db, ¶ms_expr, expression!{ add([input.expr()], 4) }, &arg_tys, false)])) + (let [output.ident()] := add([provider.avail(db)], [input_size.expr()])) + (let success := call((gas()), [func_addr.expr()], 0, [input.expr()], [input_size.expr()], 0, 0)) + (let [output_size.ident()] := returndatasize()) + (returndatacopy([output.expr()], 0, [output_size.expr()])) + (if (iszero(success)) { + (revert([output.expr()], [output_size.expr()])) + }) + }; + let func = if let Some(ret_ty) = ret_ty { + let ret = YulVariable::new("$ret"); + body.push( + statement!{ + [ret.ident()] := [provider.abi_decode(db, output.expr(), output_size.expr(), &[ret_ty], AbiSrcLocation::Memory)] + } + ); + function_definition! { + function [func_name.ident()]([func_addr.ident()], [params...]) -> [ret.ident()] { + [body...] + } + } + } else { + function_definition! { + function [func_name.ident()]([func_addr.ident()], [params...]) { + [body...] + } + } + }; + + RuntimeFunction::from_statement(func) +} diff --git a/crates/codegen/src/yul/runtime/data.rs b/crates/codegen/src/yul/runtime/data.rs new file mode 100644 index 0000000000..0808ea73c8 --- /dev/null +++ b/crates/codegen/src/yul/runtime/data.rs @@ -0,0 +1,410 @@ +use crate::{ + db::CodegenDb, + yul::{ + runtime::{make_ptr, BitMask}, + slot_size::{yul_primitive_type, SLOT_SIZE}, + YulVariable, + }, +}; + +use super::{DefaultRuntimeProvider, RuntimeFunction, RuntimeProvider}; + +use fe_mir::ir::TypeId; + +use yultsur::*; + +const HASH_SCRATCH_SPACE_START: usize = 0x00; +const HASH_SCRATCH_SPACE_SIZE: usize = 64; +const FREE_MEMORY_ADDRESS_STORE: usize = HASH_SCRATCH_SPACE_START + HASH_SCRATCH_SPACE_SIZE; +const FREE_MEMORY_START: usize = FREE_MEMORY_ADDRESS_STORE + 32; + +pub(super) fn make_alloc(func_name: &str) -> RuntimeFunction { + let func_name = YulVariable::new(func_name); + let free_address_ptr = literal_expression! {(FREE_MEMORY_ADDRESS_STORE)}; + let free_memory_start = literal_expression! {(FREE_MEMORY_START)}; + let func = function_definition! { + function [func_name.ident()](size) -> ptr { + (ptr := mload([free_address_ptr.clone()])) + (if (eq(ptr, 0x00)) { (ptr := [free_memory_start]) }) + (mstore([free_address_ptr], (add(ptr, size)))) + } + }; + + RuntimeFunction::from_statement(func) +} + +pub(super) fn make_avail(func_name: &str) -> RuntimeFunction { + let func_name = YulVariable::new(func_name); + let free_address_ptr = literal_expression! {(FREE_MEMORY_ADDRESS_STORE)}; + let free_memory_start = literal_expression! {(FREE_MEMORY_START)}; + let func = function_definition! { + function [func_name.ident()]() -> ptr { + (ptr := mload([free_address_ptr])) + (if (eq(ptr, 0x00)) { (ptr := [free_memory_start]) }) + } + }; + + RuntimeFunction::from_statement(func) +} + +pub(super) fn make_mcopym(func_name: &str) -> RuntimeFunction { + let func_name = YulVariable::new(func_name); + let src = YulVariable::new("src"); + let dst = YulVariable::new("dst"); + let size = YulVariable::new("size"); + + let func = function_definition! { + function [func_name.ident()]([src.ident()], [dst.ident()], [size.ident()]) { + (let iter_count := div([size.expr()], 32)) + (let original_src := [src.expr()]) + (for {(let i := 0)} (lt(i, iter_count)) {(i := (add(i, 1)))} + { + (mstore([dst.expr()], (mload([src.expr()])))) + ([src.ident()] := add([src.expr()], 32)) + ([dst.ident()] := add([dst.expr()], 32)) + }) + + (let rem := sub([size.expr()], (sub([src.expr()], original_src)))) + (if (gt(rem, 0)) { + (let rem_bits := mul(rem, 8)) + (let dst_mask := sub((shl((sub(256, rem_bits)), 1)), 1)) + (let src_mask := not(dst_mask)) + (let src_value := and((mload([src.expr()])), src_mask)) + (let dst_value := and((mload([dst.expr()])), dst_mask)) + (mstore([dst.expr()], (or(src_value, dst_value)))) + }) + } + }; + + RuntimeFunction::from_statement(func) +} + +pub(super) fn make_mcopys(func_name: &str) -> RuntimeFunction { + let func_name = YulVariable::new(func_name); + let src = YulVariable::new("src"); + let dst = YulVariable::new("dst"); + let size = YulVariable::new("size"); + + let func = function_definition! { + function [func_name.ident()]([src.ident()], [dst.ident()], [size.ident()]) { + ([dst.ident()] := div([dst.expr()], 32)) + (let iter_count := div([size.expr()], 32)) + (let original_src := [src.expr()]) + (for {(let i := 0)} (lt(i, iter_count)) {(i := (add(i, 1)))} + { + (sstore([dst.expr()], (mload([src.expr()])))) + ([src.ident()] := add([src.expr()], 32)) + ([dst.ident()] := add([dst.expr()], 1)) + }) + + (let rem := sub([size.expr()], (sub([src.expr()], original_src)))) + (if (gt(rem, 0)) { + (let rem_bits := mul(rem, 8)) + (let dst_mask := sub((shl((sub(256, rem_bits)), 1)), 1)) + (let src_mask := not(dst_mask)) + (let src_value := and((mload([src.expr()])), src_mask)) + (let dst_value := and((sload([dst.expr()])), dst_mask)) + (sstore([dst.expr()], (or(src_value, dst_value)))) + }) + } + }; + + RuntimeFunction::from_statement(func) +} + +pub(super) fn make_scopym(func_name: &str) -> RuntimeFunction { + let func_name = YulVariable::new(func_name); + let src = YulVariable::new("src"); + let dst = YulVariable::new("dst"); + let size = YulVariable::new("size"); + + let func = function_definition! { + function [func_name.ident()]([src.ident()], [dst.ident()], [size.ident()]) { + ([src.ident()] := div([src.expr()], 32)) + (let iter_count := div([size.expr()], 32)) + (let original_dst := [dst.expr()]) + (for {(let i := 0)} (lt(i, iter_count)) {(i := (add(i, 1)))} + { + (mstore([dst.expr()], (sload([src.expr()])))) + ([src.ident()] := add([src.expr()], 1)) + ([dst.ident()] := add([dst.expr()], 32)) + }) + + (let rem := sub([size.expr()], (sub([dst.expr()], original_dst)))) + (if (gt(rem, 0)) { + (let rem_bits := mul(rem, 8)) + (let dst_mask := sub((shl((sub(256, rem_bits)), 1)), 1)) + (let src_mask := not(dst_mask)) + (let src_value := and((sload([src.expr()])), src_mask)) + (let dst_value := and((mload([dst.expr()])), dst_mask)) + (mstore([dst.expr()], (or(src_value, dst_value)))) + }) + } + }; + + RuntimeFunction::from_statement(func) +} + +pub(super) fn make_scopys(func_name: &str) -> RuntimeFunction { + let func_name = YulVariable::new(func_name); + let src = YulVariable::new("src"); + let dst = YulVariable::new("dst"); + let size = YulVariable::new("size"); + let func = function_definition! { + function [func_name.ident()]([src.ident()], [dst.ident()], [size.ident()]) { + ([src.ident()] := div([src.expr()], 32)) + ([dst.ident()] := div([dst.expr()], 32)) + (let iter_count := div((add([size.expr()], 31)), 32)) + (for {(let i := 0)} (lt(i, iter_count)) {(i := (add(i, 1)))} + { + (sstore([dst.expr()], (sload([src.expr()])))) + ([src.ident()] := add([src.expr()], 1)) + ([dst.ident()] := add([dst.expr()], 1)) + }) + } + }; + + RuntimeFunction::from_statement(func) +} + +pub(super) fn make_sptr_store(func_name: &str) -> RuntimeFunction { + let func_name = YulVariable::new(func_name); + let func = function_definition! { + function [func_name.ident()](ptr, value, size_bits) { + (let rem_bits := mul((mod(ptr, 32)), 8)) + (let shift_bits := sub(256, (add(rem_bits, size_bits)))) + (let mask := (shl(shift_bits, (sub((shl(size_bits, 1)), 1))))) + (let inv_mask := not(mask)) + (let slot := div(ptr, 32)) + (let new_value := or((and((sload(slot)), inv_mask)), (and((shl(shift_bits, value)), mask)))) + (sstore(slot, new_value)) + } + }; + + RuntimeFunction::from_statement(func) +} + +pub(super) fn make_mptr_store(func_name: &str) -> RuntimeFunction { + let func_name = YulVariable::new(func_name); + let func = function_definition! { + function [func_name.ident()](ptr, value, shift_num, mask) { + (value := shl(shift_num, value)) + (let ptr_value := and((mload(ptr)), mask)) + (value := or(value, ptr_value)) + (mstore(ptr, value)) + } + }; + + RuntimeFunction::from_statement(func) +} + +pub(super) fn make_sptr_load(func_name: &str) -> RuntimeFunction { + let func_name = YulVariable::new(func_name); + let func = function_definition! { + function [func_name.ident()](ptr, size_bits) -> ret { + (let rem_bits := mul((mod(ptr, 32)), 8)) + (let shift_num := sub(256, (add(rem_bits, size_bits)))) + (let slot := div(ptr, 32)) + (ret := shr(shift_num, (sload(slot)))) + } + }; + + RuntimeFunction::from_statement(func) +} + +pub(super) fn make_mptr_load(func_name: &str) -> RuntimeFunction { + let func_name = YulVariable::new(func_name); + let func = function_definition! { + function [func_name.ident()](ptr, shift_num) -> ret { + (ret := shr(shift_num, (mload(ptr)))) + } + }; + + RuntimeFunction::from_statement(func) +} + +// TODO: We can optimize aggregate initialization by combining multiple +// `ptr_store` operations into single `ptr_store` operation. +pub(super) fn make_aggregate_init( + provider: &mut DefaultRuntimeProvider, + db: &dyn CodegenDb, + func_name: &str, + legalized_ty: TypeId, + arg_tys: Vec, +) -> RuntimeFunction { + debug_assert!(legalized_ty.is_ptr(db.upcast())); + let is_sptr = legalized_ty.is_sptr(db.upcast()); + let inner_ty = legalized_ty.deref(db.upcast()); + let ptr = YulVariable::new("ptr"); + let field_num = inner_ty.aggregate_field_num(db.upcast()); + + let iter_field_args = || { + (0..field_num) + .into_iter() + .map(|i| YulVariable::new(format! {"arg{}", i})) + }; + + let mut body = vec![]; + for (idx, field_arg) in iter_field_args().enumerate() { + let field_arg_ty = arg_tys[idx]; + let field_ty = inner_ty.projection_ty_imm(db.upcast(), idx); + let field_ty_size = field_ty.size_of(db.upcast(), SLOT_SIZE); + let field_ptr_ty = make_ptr(db, field_ty, is_sptr); + let elem_offset = + literal_expression! {(inner_ty.aggregate_elem_offset(db.upcast(), idx, SLOT_SIZE))}; + + let elem_ptr = expression! { add([ptr.expr()], [elem_offset] )}; + let copy_expr = if field_ty.is_aggregate(db.upcast()) || field_ty.is_string(db.upcast()) { + // Call ptr copy function if field type is aggregate. + debug_assert!(field_arg_ty.is_ptr(db.upcast())); + provider.ptr_copy( + db, + field_arg.expr(), + elem_ptr, + literal_expression! {(field_ty_size)}, + field_arg_ty.is_sptr(db.upcast()), + is_sptr, + ) + } else { + // Call store function if field type is not aggregate. + provider.ptr_store(db, elem_ptr, field_arg.expr(), field_ptr_ty) + }; + body.push(yul::Statement::Expression(copy_expr)); + } + + let func_name = identifier! {(func_name)}; + let parameters = std::iter::once(ptr) + .chain(iter_field_args()) + .map(|var| var.ident()) + .collect(); + let func_def = yul::FunctionDefinition { + name: func_name, + parameters, + returns: vec![], + block: yul::Block { statements: body }, + }; + + RuntimeFunction(func_def) +} + +pub(super) fn make_string_copy( + provider: &mut DefaultRuntimeProvider, + db: &dyn CodegenDb, + func_name: &str, + data: &str, + is_dst_storage: bool, +) -> RuntimeFunction { + let func_name = YulVariable::new(func_name); + let dst_ptr = YulVariable::new("dst_ptr"); + let symbol_name = literal_expression! { (format!(r#""{}""#, db.codegen_constant_string_symbol_name(data.to_string()))) }; + + let func = if is_dst_storage { + let tmp_ptr = YulVariable::new("tmp_ptr"); + let data_size = YulVariable::new("data_size"); + function_definition! { + function [func_name.ident()]([dst_ptr.ident()]) { + (let [tmp_ptr.ident()] := [provider.avail(db)]) + (let data_offset := dataoffset([symbol_name.clone()])) + (let [data_size.ident()] := datasize([symbol_name])) + (let len_slot := div([dst_ptr.expr()], 32)) + (sstore(len_slot, [data_size.expr()])) + (datacopy([tmp_ptr.expr()], data_offset, [data_size.expr()])) + ([dst_ptr.ident()] := add([dst_ptr.expr()], 32)) + ([yul::Statement::Expression( + provider.ptr_copy(db, tmp_ptr.expr(), dst_ptr.expr(), data_size.expr(), false, true)) + ]) + } + } + } else { + function_definition! { + function [func_name.ident()]([dst_ptr.ident()]) { + (let data_offset := dataoffset([symbol_name.clone()])) + (let data_size := datasize([symbol_name])) + (mstore([dst_ptr.expr()], data_size)) + ([dst_ptr.ident()] := add([dst_ptr.expr()], 32)) + (datacopy([dst_ptr.expr()], data_offset, data_size)) + } + } + }; + + RuntimeFunction::from_statement(func) +} + +pub(super) fn make_string_construct( + provider: &mut DefaultRuntimeProvider, + db: &dyn CodegenDb, + func_name: &str, + data: &str, +) -> RuntimeFunction { + let func_name = YulVariable::new(func_name); + let ptr_size = YulVariable::new("ptr_size"); + let string_ptr = YulVariable::new("string_ptr"); + + let func = function_definition! { + function [func_name.ident()]([ptr_size.ident()]) -> [string_ptr.ident()] { + ([string_ptr.ident()] := [provider.alloc(db, ptr_size.expr())]) + ([yul::Statement::Expression(provider.string_copy(db, string_ptr.expr(), data, false))]) + } + }; + + RuntimeFunction::from_statement(func) +} + +pub(super) fn make_map_value_ptr_with_primitive_key( + provider: &mut DefaultRuntimeProvider, + db: &dyn CodegenDb, + func_name: &str, + key_ty: TypeId, +) -> RuntimeFunction { + debug_assert!(key_ty.is_primitive(db.upcast())); + let scratch_space = literal_expression! {(HASH_SCRATCH_SPACE_START)}; + let scratch_size = literal_expression! {(HASH_SCRATCH_SPACE_SIZE)}; + let func_name = YulVariable::new(func_name); + let map_ptr = YulVariable::new("map_ptr"); + let key = YulVariable::new("key"); + let yul_primitive_type = yul_primitive_type(db); + + let mask = BitMask::new(1).not(); + + let func = function_definition! { + function [func_name.ident()]([map_ptr.ident()], [key.ident()]) -> ret { + ([yul::Statement::Expression(provider.ptr_store( + db, + scratch_space.clone(), + key.expr(), + yul_primitive_type.make_mptr(db.upcast()), + ))]) + ([yul::Statement::Expression(provider.ptr_store( + db, + expression!(add([scratch_space.clone()], 32)), + map_ptr.expr(), + yul_primitive_type.make_mptr(db.upcast()), + ))]) + (ret := and([mask.as_expr()], (keccak256([scratch_space], [scratch_size])))) + }}; + + RuntimeFunction::from_statement(func) +} + +pub(super) fn make_map_value_ptr_with_ptr_key( + provider: &mut DefaultRuntimeProvider, + db: &dyn CodegenDb, + func_name: &str, + key_ty: TypeId, +) -> RuntimeFunction { + debug_assert!(key_ty.is_ptr(db.upcast())); + + let func_name = YulVariable::new(func_name); + let size = literal_expression! {(key_ty.deref(db.upcast()).size_of(db.upcast(), SLOT_SIZE))}; + let map_ptr = YulVariable::new("map_ptr"); + let key = YulVariable::new("key"); + + let key_hash = expression! { keccak256([key.expr()], [size]) }; + let u256_ty = yul_primitive_type(db); + let def = function_definition! { + function [func_name.ident()]([map_ptr.ident()], [key.ident()]) -> ret { + (ret := [provider.map_value_ptr(db, map_ptr.expr(), key_hash, u256_ty)]) + } + }; + RuntimeFunction::from_statement(def) +} diff --git a/crates/codegen/src/yul/runtime/emit.rs b/crates/codegen/src/yul/runtime/emit.rs new file mode 100644 index 0000000000..7e1f8ab8c6 --- /dev/null +++ b/crates/codegen/src/yul/runtime/emit.rs @@ -0,0 +1,74 @@ +use crate::{ + db::CodegenDb, + yul::{runtime::make_ptr, slot_size::SLOT_SIZE, YulVariable}, +}; + +use super::{DefaultRuntimeProvider, RuntimeFunction, RuntimeProvider}; + +use fe_mir::ir::TypeId; + +use yultsur::*; + +pub(super) fn make_emit( + provider: &mut DefaultRuntimeProvider, + db: &dyn CodegenDb, + func_name: &str, + legalized_ty: TypeId, +) -> RuntimeFunction { + let func_name = YulVariable::new(func_name); + let event_ptr = YulVariable::new("event_ptr"); + let deref_ty = legalized_ty.deref(db.upcast()); + + let abi = db.codegen_abi_event(deref_ty); + let mut topics = vec![literal_expression! {(format!("0x{}", abi.signature().hash_hex()))}]; + for (idx, field) in abi.inputs.iter().enumerate() { + if !field.indexed { + continue; + } + let field_ty = deref_ty.projection_ty_imm(db.upcast(), idx); + let offset = + literal_expression! {(deref_ty.aggregate_elem_offset(db.upcast(), idx, SLOT_SIZE))}; + let elem_ptr = expression! { add([event_ptr.expr()], [offset]) }; + let topic = if field_ty.is_aggregate(db.upcast()) { + todo!() + } else { + let topic = provider.ptr_load( + db, + elem_ptr, + make_ptr(db, field_ty, legalized_ty.is_sptr(db.upcast())), + ); + provider.primitive_cast(db, topic, field_ty) + }; + + topics.push(topic) + } + + let mut event_data_tys = vec![]; + let mut event_data_values = vec![]; + for (idx, field) in abi.inputs.iter().enumerate() { + if field.indexed { + continue; + } + + let field_ty = deref_ty.projection_ty_imm(db.upcast(), idx); + let field_offset = + literal_expression! { (deref_ty.aggregate_elem_offset(db.upcast(), idx, SLOT_SIZE)) }; + event_data_tys.push(make_ptr(db, field_ty, legalized_ty.is_sptr(db.upcast()))); + event_data_values.push(expression! { add([event_ptr.expr()], [field_offset]) }); + } + + debug_assert!(topics.len() < 5); + let log_func = identifier! { (format!("log{}", topics.len()))}; + + let event_data_ptr = YulVariable::new("event_data_ptr"); + let event_enc_size = YulVariable::new("event_enc_size"); + let func = function_definition! { + function [func_name.ident()]([event_ptr.ident()]) { + (let [event_data_ptr.ident()] := [provider.avail(db)]) + (let [event_enc_size.ident()] := [provider.abi_encode_seq(db, &event_data_values, event_data_ptr.expr(), &event_data_tys, false )]) + ([log_func]([event_data_ptr.expr()], [event_enc_size.expr()], [topics...])) + } + }; + + RuntimeFunction::from_statement(func) +} diff --git a/crates/codegen/src/yul/runtime/mod.rs b/crates/codegen/src/yul/runtime/mod.rs new file mode 100644 index 0000000000..2f1ef831f8 --- /dev/null +++ b/crates/codegen/src/yul/runtime/mod.rs @@ -0,0 +1,772 @@ +mod abi; +mod contract; +mod data; +mod emit; +mod revert; +mod safe_math; + +use std::fmt::Write; + +use fe_analyzer::namespace::items::ContractId; +use fe_mir::ir::{types::ArrayDef, FunctionId, TypeId, TypeKind}; +use fxhash::FxHashMap; +use yultsur::*; + +use num_bigint::BigInt; + +use crate::{db::CodegenDb, yul::slot_size::SLOT_SIZE}; + +use super::slot_size::yul_primitive_type; + +pub trait RuntimeProvider { + fn collect_definitions(&self) -> Vec; + + fn alloc(&mut self, db: &dyn CodegenDb, size: yul::Expression) -> yul::Expression; + + fn avail(&mut self, db: &dyn CodegenDb) -> yul::Expression; + + fn create( + &mut self, + db: &dyn CodegenDb, + contract: ContractId, + value: yul::Expression, + ) -> yul::Expression; + + fn create2( + &mut self, + db: &dyn CodegenDb, + contract: ContractId, + value: yul::Expression, + salt: yul::Expression, + ) -> yul::Expression; + + fn emit( + &mut self, + db: &dyn CodegenDb, + event: yul::Expression, + event_ty: TypeId, + ) -> yul::Expression; + + fn revert( + &mut self, + db: &dyn CodegenDb, + arg: Option, + arg_name: &str, + arg_ty: TypeId, + ) -> yul::Expression; + + fn external_call( + &mut self, + db: &dyn CodegenDb, + function: FunctionId, + args: Vec, + ) -> yul::Expression; + + fn map_value_ptr( + &mut self, + db: &dyn CodegenDb, + map_ptr: yul::Expression, + key: yul::Expression, + key_ty: TypeId, + ) -> yul::Expression; + + fn aggregate_init( + &mut self, + db: &dyn CodegenDb, + ptr: yul::Expression, + args: Vec, + ptr_ty: TypeId, + arg_tys: Vec, + ) -> yul::Expression; + + fn string_copy( + &mut self, + db: &dyn CodegenDb, + dst: yul::Expression, + data: &str, + is_dst_storage: bool, + ) -> yul::Expression; + + fn string_construct( + &mut self, + db: &dyn CodegenDb, + data: &str, + string_len: usize, + ) -> yul::Expression; + + /// Copy data from `src` to `dst`. + /// NOTE: src and dst must be aligned by 32 when a ptr is storage ptr. + fn ptr_copy( + &mut self, + db: &dyn CodegenDb, + src: yul::Expression, + dst: yul::Expression, + size: yul::Expression, + is_src_storage: bool, + is_dst_storage: bool, + ) -> yul::Expression; + + fn ptr_store( + &mut self, + db: &dyn CodegenDb, + ptr: yul::Expression, + imm: yul::Expression, + ptr_ty: TypeId, + ) -> yul::Expression; + + fn ptr_load( + &mut self, + db: &dyn CodegenDb, + ptr: yul::Expression, + ptr_ty: TypeId, + ) -> yul::Expression; + + fn abi_encode( + &mut self, + db: &dyn CodegenDb, + src: yul::Expression, + dst: yul::Expression, + src_ty: TypeId, + is_dst_storage: bool, + ) -> yul::Expression; + + fn abi_encode_seq( + &mut self, + db: &dyn CodegenDb, + src: &[yul::Expression], + dst: yul::Expression, + src_tys: &[TypeId], + is_dst_storage: bool, + ) -> yul::Expression; + + fn abi_decode( + &mut self, + db: &dyn CodegenDb, + src: yul::Expression, + size: yul::Expression, + types: &[TypeId], + abi_loc: AbiSrcLocation, + ) -> yul::Expression; + + fn primitive_cast( + &mut self, + db: &dyn CodegenDb, + value: yul::Expression, + from_ty: TypeId, + ) -> yul::Expression { + debug_assert!(from_ty.is_primitive(db.upcast())); + let from_size = from_ty.size_of(db.upcast(), SLOT_SIZE); + + if from_ty.is_signed(db.upcast()) { + let significant = literal_expression! {(from_size-1)}; + expression! { signextend([significant], [value]) } + } else { + let mask = BitMask::new(from_size); + expression! { and([value], [mask.as_expr()]) } + } + } + + // TODO: The all functions below will be reimplemented in `std`. + fn safe_add( + &mut self, + db: &dyn CodegenDb, + lhs: yul::Expression, + rhs: yul::Expression, + ty: TypeId, + ) -> yul::Expression; + + fn safe_sub( + &mut self, + db: &dyn CodegenDb, + lhs: yul::Expression, + rhs: yul::Expression, + ty: TypeId, + ) -> yul::Expression; + + fn safe_mul( + &mut self, + db: &dyn CodegenDb, + lhs: yul::Expression, + rhs: yul::Expression, + ty: TypeId, + ) -> yul::Expression; + + fn safe_div( + &mut self, + db: &dyn CodegenDb, + lhs: yul::Expression, + rhs: yul::Expression, + ty: TypeId, + ) -> yul::Expression; + + fn safe_mod( + &mut self, + db: &dyn CodegenDb, + lhs: yul::Expression, + rhs: yul::Expression, + ty: TypeId, + ) -> yul::Expression; + + fn safe_pow( + &mut self, + db: &dyn CodegenDb, + lhs: yul::Expression, + rhs: yul::Expression, + ty: TypeId, + ) -> yul::Expression; +} + +#[derive(Clone, Copy, Debug)] +pub enum AbiSrcLocation { + CallData, + Memory, +} + +#[derive(Debug, Default)] +pub struct DefaultRuntimeProvider { + functions: FxHashMap, +} + +impl DefaultRuntimeProvider { + fn create_then_call( + &mut self, + name: &str, + args: Vec, + func_builder: F, + ) -> yul::Expression + where + F: FnOnce(&mut Self) -> RuntimeFunction, + { + if let Some(func) = self.functions.get(name) { + func.call(args) + } else { + let func = func_builder(self); + let result = func.call(args); + self.functions.insert(name.to_string(), func); + result + } + } +} + +impl RuntimeProvider for DefaultRuntimeProvider { + fn collect_definitions(&self) -> Vec { + self.functions + .values() + .map(RuntimeFunction::definition) + .collect() + } + + fn alloc(&mut self, _db: &dyn CodegenDb, bytes: yul::Expression) -> yul::Expression { + let name = "$alloc"; + let arg = vec![bytes]; + self.create_then_call(name, arg, |_| data::make_alloc(name)) + } + + fn avail(&mut self, _db: &dyn CodegenDb) -> yul::Expression { + let name = "$avail"; + let arg = vec![]; + self.create_then_call(name, arg, |_| data::make_avail(name)) + } + + fn create( + &mut self, + db: &dyn CodegenDb, + contract: ContractId, + value: yul::Expression, + ) -> yul::Expression { + let name = format!("$create_{}", db.codegen_contract_symbol_name(contract)); + let arg = vec![value]; + self.create_then_call(&name, arg, |provider| { + contract::make_create(provider, db, &name, contract) + }) + } + + fn create2( + &mut self, + db: &dyn CodegenDb, + contract: ContractId, + value: yul::Expression, + salt: yul::Expression, + ) -> yul::Expression { + let name = format!("$create2_{}", db.codegen_contract_symbol_name(contract)); + let arg = vec![value, salt]; + self.create_then_call(&name, arg, |provider| { + contract::make_create2(provider, db, &name, contract) + }) + } + + fn emit( + &mut self, + db: &dyn CodegenDb, + event: yul::Expression, + event_ty: TypeId, + ) -> yul::Expression { + let name = format!("$emit_{}", event_ty.0); + let legalized_ty = db.codegen_legalized_type(event_ty); + self.create_then_call(&name, vec![event], |provider| { + emit::make_emit(provider, db, &name, legalized_ty) + }) + } + + fn revert( + &mut self, + db: &dyn CodegenDb, + arg: Option, + arg_name: &str, + arg_ty: TypeId, + ) -> yul::Expression { + let func_name = format! {"$revert_{}_{}", arg_name, arg_ty.0}; + let args = match arg { + Some(arg) => vec![arg], + None => vec![], + }; + self.create_then_call(&func_name, args, |provider| { + revert::make_revert(provider, db, &func_name, arg_name, arg_ty) + }) + } + + fn external_call( + &mut self, + db: &dyn CodegenDb, + function: FunctionId, + args: Vec, + ) -> yul::Expression { + let name = format!( + "$call_external__{}", + db.codegen_function_symbol_name(function) + ); + self.create_then_call(&name, args, |provider| { + contract::make_external_call(provider, db, &name, function) + }) + } + + fn map_value_ptr( + &mut self, + db: &dyn CodegenDb, + map_ptr: yul::Expression, + key: yul::Expression, + key_ty: TypeId, + ) -> yul::Expression { + if key_ty.is_primitive(db.upcast()) { + let name = "$map_value_ptr_with_primitive_key"; + self.create_then_call(name, vec![map_ptr, key], |provider| { + data::make_map_value_ptr_with_primitive_key(provider, db, name, key_ty) + }) + } else if key_ty.is_mptr(db.upcast()) { + let name = "$map_value_ptr_with_ptr_key"; + self.create_then_call(name, vec![map_ptr, key], |provider| { + data::make_map_value_ptr_with_ptr_key(provider, db, name, key_ty) + }) + } else { + unreachable!() + } + } + + fn aggregate_init( + &mut self, + db: &dyn CodegenDb, + ptr: yul::Expression, + args: Vec, + ptr_ty: TypeId, + arg_tys: Vec, + ) -> yul::Expression { + debug_assert!(ptr_ty.is_ptr(db.upcast())); + let name = format!("$aggregate_init_{}", ptr_ty.0); + let args = std::iter::once(ptr).chain(args.into_iter()).collect(); + let legalized_ty = db.codegen_legalized_type(ptr_ty); + self.create_then_call(&name, args, |provider| { + data::make_aggregate_init(provider, db, &name, legalized_ty, arg_tys) + }) + } + + fn string_copy( + &mut self, + db: &dyn CodegenDb, + dst: yul::Expression, + data: &str, + is_dst_storage: bool, + ) -> yul::Expression { + debug_assert!(data.is_ascii()); + let symbol_name = db.codegen_constant_string_symbol_name(data.to_string()); + + let name = if is_dst_storage { + format!("$string_copy_{}_storage", symbol_name) + } else { + format!("$string_copy_{}_memory", symbol_name) + }; + + self.create_then_call(&name, vec![dst], |provider| { + data::make_string_copy(provider, db, &name, data, is_dst_storage) + }) + } + + fn string_construct( + &mut self, + db: &dyn CodegenDb, + data: &str, + string_len: usize, + ) -> yul::Expression { + debug_assert!(data.is_ascii()); + debug_assert!(string_len >= data.len()); + let symbol_name = db.codegen_constant_string_symbol_name(data.to_string()); + + let name = format!("$string_construct_{}", symbol_name); + let arg = literal_expression!((32 + string_len)); + self.create_then_call(&name, vec![arg], |provider| { + data::make_string_construct(provider, db, &name, data) + }) + } + + fn ptr_copy( + &mut self, + _db: &dyn CodegenDb, + src: yul::Expression, + dst: yul::Expression, + size: yul::Expression, + is_src_storage: bool, + is_dst_storage: bool, + ) -> yul::Expression { + let args = vec![src, dst, size]; + match (is_src_storage, is_dst_storage) { + (true, true) => { + let name = "scopys"; + self.create_then_call(name, args, |_| data::make_scopys(name)) + } + (true, false) => { + let name = "scopym"; + self.create_then_call(name, args, |_| data::make_scopym(name)) + } + (false, true) => { + let name = "mcopys"; + self.create_then_call(name, args, |_| data::make_mcopys(name)) + } + (false, false) => { + let name = "mcopym"; + self.create_then_call(name, args, |_| data::make_mcopym(name)) + } + } + } + + fn ptr_store( + &mut self, + db: &dyn CodegenDb, + ptr: yul::Expression, + imm: yul::Expression, + ptr_ty: TypeId, + ) -> yul::Expression { + debug_assert!(ptr_ty.is_ptr(db.upcast())); + let size = ptr_ty.deref(db.upcast()).size_of(db.upcast(), SLOT_SIZE); + debug_assert!(size <= 32); + + let size_bits = size * 8; + if ptr_ty.is_sptr(db.upcast()) { + let name = "$sptr_store"; + let args = vec![ptr, imm, literal_expression! {(size_bits)}]; + self.create_then_call(name, args, |_| data::make_sptr_store(name)) + } else if ptr_ty.is_mptr(db.upcast()) { + let name = "$mptr_store"; + let shift_num = literal_expression! {(256 - size_bits)}; + let mask = BitMask::new(32 - size); + let args = vec![ptr, imm, shift_num, mask.as_expr()]; + self.create_then_call(name, args, |_| data::make_mptr_store(name)) + } else { + unreachable!() + } + } + + fn ptr_load( + &mut self, + db: &dyn CodegenDb, + ptr: yul::Expression, + ptr_ty: TypeId, + ) -> yul::Expression { + debug_assert!(ptr_ty.is_ptr(db.upcast())); + let size = ptr_ty.deref(db.upcast()).size_of(db.upcast(), SLOT_SIZE); + debug_assert!(size <= 32); + + let size_bits = size * 8; + if ptr_ty.is_sptr(db.upcast()) { + let name = "$sptr_load"; + let args = vec![ptr, literal_expression! {(size_bits)}]; + self.create_then_call(name, args, |_| data::make_sptr_load(name)) + } else if ptr_ty.is_mptr(db.upcast()) { + let name = "$mptr_load"; + let shift_num = literal_expression! {(256 - size_bits)}; + let args = vec![ptr, shift_num]; + self.create_then_call(name, args, |_| data::make_mptr_load(name)) + } else { + unreachable!() + } + } + + fn abi_encode( + &mut self, + db: &dyn CodegenDb, + src: yul::Expression, + dst: yul::Expression, + src_ty: TypeId, + is_dst_storage: bool, + ) -> yul::Expression { + let legalized_ty = db.codegen_legalized_type(src_ty); + let args = vec![src.clone(), dst.clone()]; + + let func_name_postfix = if is_dst_storage { "storage" } else { "memory" }; + + if legalized_ty.is_primitive(db.upcast()) { + let name = format!( + "$abi_encode_primitive_type_{}_to_{}", + src_ty.0, func_name_postfix + ); + return self.create_then_call(&name, args, |provider| { + abi::make_abi_encode_primitive_type( + provider, + db, + &name, + legalized_ty, + is_dst_storage, + ) + }); + } + + let deref_ty = legalized_ty.deref(db.upcast()); + let abi_ty = db.codegen_abi_type(deref_ty); + if abi_ty.is_primitive() { + let value = self.ptr_load(db, src, src_ty); + let extended_value = self.primitive_cast(db, value, deref_ty); + self.abi_encode(db, extended_value, dst, deref_ty, is_dst_storage) + } else if abi_ty.is_static() { + if deref_ty.is_array(db.upcast()) { + let name = format!( + "$abi_encode_static_array_type_{}_to_{}", + src_ty.0, func_name_postfix + ); + self.create_then_call(&name, args, |provider| { + abi::make_abi_encode_static_array_type(provider, db, &name, legalized_ty) + }) + } else { + let name = format!( + "$abi_encode_static_aggregate_type_{}_to_{}", + src_ty.0, func_name_postfix + ); + self.create_then_call(&name, args, |provider| { + abi::make_abi_encode_static_aggregate_type(provider, db, &name, legalized_ty) + }) + } + } else if abi_ty.is_string() { + let name = format! {"$abi_encode_string_type_to_{}", func_name_postfix}; + self.create_then_call(&name, args, |provider| { + abi::make_abi_encode_string_type(provider, db, &name, is_dst_storage) + }) + } else if abi_ty.is_bytes() { + let len = match &deref_ty.data(db.upcast()).kind { + TypeKind::Array(ArrayDef { len, .. }) => *len, + _ => unreachable!(), + }; + let name = format! {"$abi_encode_bytes{}_type_to_{}", len, func_name_postfix}; + self.create_then_call(&name, args, |provider| { + abi::make_abi_encode_bytes_type(provider, db, &name, len, is_dst_storage) + }) + } else { + todo! {} + } + } + + fn abi_encode_seq( + &mut self, + db: &dyn CodegenDb, + src: &[yul::Expression], + dst: yul::Expression, + src_tys: &[TypeId], + is_dst_storage: bool, + ) -> yul::Expression { + let mut name = "$abi_encode_value_seq".to_string(); + for ty in src_tys { + write!(&mut name, "_{}", ty.0).unwrap(); + } + + let mut args = vec![dst]; + args.extend(src.iter().cloned()); + self.create_then_call(&name, args, |provider| { + abi::make_abi_encode_value_seq(provider, db, &name, src_tys, is_dst_storage) + }) + } + + fn abi_decode( + &mut self, + db: &dyn CodegenDb, + src: yul::Expression, + size: yul::Expression, + types: &[TypeId], + abi_loc: AbiSrcLocation, + ) -> yul::Expression { + let mut name = "$abi_decode_seq".to_string(); + for ty in types { + write!(name, "_{}", ty.0).unwrap(); + } + + match abi_loc { + AbiSrcLocation::CallData => write!(name, "_from_calldata").unwrap(), + AbiSrcLocation::Memory => write!(name, "_from_memory").unwrap(), + }; + + self.create_then_call(&name, vec![src, size], |provider| { + abi::make_abi_decode(provider, db, &name, types, abi_loc) + }) + } + + fn safe_add( + &mut self, + db: &dyn CodegenDb, + lhs: yul::Expression, + rhs: yul::Expression, + ty: TypeId, + ) -> yul::Expression { + debug_assert!(ty.is_integral(db.upcast())); + safe_math::dispatch_safe_add(self, db, lhs, rhs, ty) + } + + fn safe_sub( + &mut self, + db: &dyn CodegenDb, + lhs: yul::Expression, + rhs: yul::Expression, + ty: TypeId, + ) -> yul::Expression { + debug_assert!(ty.is_integral(db.upcast())); + safe_math::dispatch_safe_sub(self, db, lhs, rhs, ty) + } + + fn safe_mul( + &mut self, + db: &dyn CodegenDb, + lhs: yul::Expression, + rhs: yul::Expression, + ty: TypeId, + ) -> yul::Expression { + debug_assert!(ty.is_integral(db.upcast())); + safe_math::dispatch_safe_mul(self, db, lhs, rhs, ty) + } + + fn safe_div( + &mut self, + db: &dyn CodegenDb, + lhs: yul::Expression, + rhs: yul::Expression, + ty: TypeId, + ) -> yul::Expression { + debug_assert!(ty.is_integral(db.upcast())); + safe_math::dispatch_safe_div(self, db, lhs, rhs, ty) + } + + fn safe_mod( + &mut self, + db: &dyn CodegenDb, + lhs: yul::Expression, + rhs: yul::Expression, + ty: TypeId, + ) -> yul::Expression { + debug_assert!(ty.is_integral(db.upcast())); + safe_math::dispatch_safe_mod(self, db, lhs, rhs, ty) + } + + fn safe_pow( + &mut self, + db: &dyn CodegenDb, + lhs: yul::Expression, + rhs: yul::Expression, + ty: TypeId, + ) -> yul::Expression { + debug_assert!(ty.is_integral(db.upcast())); + safe_math::dispatch_safe_pow(self, db, lhs, rhs, ty) + } +} + +#[derive(Debug)] +struct RuntimeFunction(yul::FunctionDefinition); + +impl RuntimeFunction { + fn arg_num(&self) -> usize { + self.0.parameters.len() + } + + fn definition(&self) -> yul::FunctionDefinition { + self.0.clone() + } + + /// # Panics + /// Panics if a number of arguments doesn't match the definition. + fn call(&self, args: Vec) -> yul::Expression { + debug_assert_eq!(self.arg_num(), args.len()); + + yul::Expression::FunctionCall(yul::FunctionCall { + identifier: self.0.name.clone(), + arguments: args, + }) + } + + /// Remove this when `yultsur::function_definition!` becomes to return + /// `FunctionDefinition`. + fn from_statement(func: yul::Statement) -> Self { + match func { + yul::Statement::FunctionDefinition(def) => Self(def), + _ => unreachable!(), + } + } +} + +fn make_ptr(db: &dyn CodegenDb, inner: TypeId, is_sptr: bool) -> TypeId { + if is_sptr { + inner.make_sptr(db.upcast()) + } else { + inner.make_mptr(db.upcast()) + } +} + +struct BitMask(BigInt); + +impl BitMask { + fn new(byte_size: usize) -> Self { + debug_assert!(byte_size <= 32); + let one: BigInt = 1usize.into(); + Self((one << (byte_size * 8)) - 1) + } + + fn not(&self) -> Self { + // Bigint is variable length integer, so we need special handling for `not` + // operation. + let one: BigInt = 1usize.into(); + let u256_max = (one << 256) - 1; + Self(u256_max ^ &self.0) + } + + fn as_expr(&self) -> yul::Expression { + let mask = format!("{:#x}", self.0); + literal_expression! {(mask)} + } +} + +pub(super) fn error_revert_numeric( + provider: &mut dyn RuntimeProvider, + db: &dyn CodegenDb, + error_code: yul::Expression, +) -> yul::Statement { + yul::Statement::Expression(provider.revert( + db, + Some(error_code), + "Error", + yul_primitive_type(db), + )) +} + +pub(super) fn panic_revert_numeric( + provider: &mut dyn RuntimeProvider, + db: &dyn CodegenDb, + error_code: yul::Expression, +) -> yul::Statement { + yul::Statement::Expression(provider.revert( + db, + Some(error_code), + "Panic", + yul_primitive_type(db), + )) +} diff --git a/crates/codegen/src/yul/runtime/revert.rs b/crates/codegen/src/yul/runtime/revert.rs new file mode 100644 index 0000000000..caf9aa4214 --- /dev/null +++ b/crates/codegen/src/yul/runtime/revert.rs @@ -0,0 +1,83 @@ +use crate::{ + db::CodegenDb, + yul::{slot_size::function_hash_type, YulVariable}, +}; + +use super::{DefaultRuntimeProvider, RuntimeFunction, RuntimeProvider}; + +use fe_mir::ir::{self, TypeId}; +use fe_new_abi::function::{AbiFunction, AbiFunctionType}; +use yultsur::*; + +pub(super) fn make_revert( + provider: &mut DefaultRuntimeProvider, + db: &dyn CodegenDb, + func_name: &str, + arg_name: &str, + arg_ty: TypeId, +) -> RuntimeFunction { + let func_name = YulVariable::new(func_name); + let arg = YulVariable::new("arg"); + + let abi_size = YulVariable::new("abi_size"); + let abi_tmp_ptr = YulVariable::new("$abi_tmp_ptr"); + let signature = type_signature_for_revert(db, arg_name, arg_ty); + + let signature_store = yul::Statement::Expression(provider.ptr_store( + db, + abi_tmp_ptr.expr(), + signature, + function_hash_type(db).make_mptr(db.upcast()), + )); + + let func = if arg_ty.deref(db.upcast()).is_zero_sized(db.upcast()) { + function_definition! { + function [func_name.ident()]() { + (let [abi_tmp_ptr.ident()] := [provider.avail(db)]) + ([signature_store]) + (revert([abi_tmp_ptr.expr()], [literal_expression!{4}])) + } + } + } else { + let encode = provider.abi_encode_seq( + db, + &[arg.expr()], + expression! { add([abi_tmp_ptr.expr()], 4) }, + &[arg_ty], + false, + ); + + function_definition! { + function [func_name.ident()]([arg.ident()]) { + (let [abi_tmp_ptr.ident()] := [provider.avail(db)]) + ([signature_store]) + (let [abi_size.ident()] := add([encode], 4)) + (revert([abi_tmp_ptr.expr()], [abi_size.expr()])) + } + } + }; + + RuntimeFunction::from_statement(func) +} + +/// Returns signature hash of the type. +fn type_signature_for_revert(db: &dyn CodegenDb, name: &str, ty: TypeId) -> yul::Expression { + let deref_ty = ty.deref(db.upcast()); + let ty_data = deref_ty.data(db.upcast()); + let args = match &ty_data.kind { + ir::TypeKind::Struct(def) => def + .fields + .iter() + .map(|(_, ty)| ("".to_string(), db.codegen_abi_type(*ty))) + .collect(), + _ => { + let abi_ty = db.codegen_abi_type(deref_ty); + vec![("_".to_string(), abi_ty)] + } + }; + + let selector = + AbiFunction::new(AbiFunctionType::Function, name.to_string(), args, None).selector(); + let type_sig = selector.hex(); + literal_expression! {(format!{"0x{}", type_sig })} +} diff --git a/crates/codegen/src/yul/runtime/safe_math.rs b/crates/codegen/src/yul/runtime/safe_math.rs new file mode 100644 index 0000000000..5bdfe8b04a --- /dev/null +++ b/crates/codegen/src/yul/runtime/safe_math.rs @@ -0,0 +1,628 @@ +use crate::{db::CodegenDb, yul::YulVariable}; + +use super::{DefaultRuntimeProvider, RuntimeFunction, RuntimeProvider}; + +use fe_mir::ir::{TypeId, TypeKind}; + +use yultsur::*; + +pub(super) fn dispatch_safe_add( + provider: &mut DefaultRuntimeProvider, + db: &dyn CodegenDb, + lhs: yul::Expression, + rhs: yul::Expression, + ty: TypeId, +) -> yul::Expression { + debug_assert!(ty.is_integral(db.upcast())); + let min_value = get_min_value(db, ty); + let max_value = get_max_value(db, ty); + + if ty.is_signed(db.upcast()) { + let name = "$safe_add_signed"; + let args = vec![lhs, rhs, min_value, max_value]; + provider.create_then_call(name, args, |provider| { + make_safe_add_signed(provider, db, name) + }) + } else { + let name = "$safe_add_unsigned"; + let args = vec![lhs, rhs, max_value]; + provider.create_then_call(name, args, |provider| { + make_safe_add_unsigned(provider, db, name) + }) + } +} + +pub(super) fn dispatch_safe_sub( + provider: &mut DefaultRuntimeProvider, + db: &dyn CodegenDb, + lhs: yul::Expression, + rhs: yul::Expression, + ty: TypeId, +) -> yul::Expression { + debug_assert!(ty.is_integral(db.upcast())); + let min_value = get_min_value(db, ty); + let max_value = get_max_value(db, ty); + + if ty.is_signed(db.upcast()) { + let name = "$safe_sub_signed"; + let args = vec![lhs, rhs, min_value, max_value]; + provider.create_then_call(name, args, |provider| { + make_safe_sub_signed(provider, db, name) + }) + } else { + let name = "$safe_sub_unsigned"; + let args = vec![lhs, rhs]; + provider.create_then_call(name, args, |provider| { + make_safe_sub_unsigned(provider, db, name) + }) + } +} + +pub(super) fn dispatch_safe_mul( + provider: &mut DefaultRuntimeProvider, + db: &dyn CodegenDb, + lhs: yul::Expression, + rhs: yul::Expression, + ty: TypeId, +) -> yul::Expression { + debug_assert!(ty.is_integral(db.upcast())); + let min_value = get_min_value(db, ty); + let max_value = get_max_value(db, ty); + + if ty.is_signed(db.upcast()) { + let name = "$safe_mul_signed"; + let args = vec![lhs, rhs, min_value, max_value]; + provider.create_then_call(name, args, |provider| { + make_safe_mul_signed(provider, db, name) + }) + } else { + let name = "$safe_mul_unsigned"; + let args = vec![lhs, rhs, max_value]; + provider.create_then_call(name, args, |provider| { + make_safe_mul_unsigned(provider, db, name) + }) + } +} + +pub(super) fn dispatch_safe_div( + provider: &mut DefaultRuntimeProvider, + db: &dyn CodegenDb, + lhs: yul::Expression, + rhs: yul::Expression, + ty: TypeId, +) -> yul::Expression { + debug_assert!(ty.is_integral(db.upcast())); + let min_value = get_min_value(db, ty); + + if ty.is_signed(db.upcast()) { + let name = "$safe_div_signed"; + let args = vec![lhs, rhs, min_value]; + provider.create_then_call(name, args, |provider| { + make_safe_div_signed(provider, db, name) + }) + } else { + let name = "$safe_div_unsigned"; + let args = vec![lhs, rhs]; + provider.create_then_call(name, args, |provider| { + make_safe_div_unsigned(provider, db, name) + }) + } +} + +pub(super) fn dispatch_safe_mod( + provider: &mut DefaultRuntimeProvider, + db: &dyn CodegenDb, + lhs: yul::Expression, + rhs: yul::Expression, + ty: TypeId, +) -> yul::Expression { + debug_assert!(ty.is_integral(db.upcast())); + if ty.is_signed(db.upcast()) { + let name = "$safe_mod_signed"; + let args = vec![lhs, rhs]; + provider.create_then_call(name, args, |provider| { + make_safe_mod_signed(provider, db, name) + }) + } else { + let name = "$safe_mod_unsigned"; + let args = vec![lhs, rhs]; + provider.create_then_call(name, args, |provider| { + make_safe_mod_unsigned(provider, db, name) + }) + } +} + +pub(super) fn dispatch_safe_pow( + provider: &mut DefaultRuntimeProvider, + db: &dyn CodegenDb, + lhs: yul::Expression, + rhs: yul::Expression, + ty: TypeId, +) -> yul::Expression { + debug_assert!(ty.is_integral(db.upcast())); + let min_value = get_min_value(db, ty); + let max_value = get_max_value(db, ty); + + if ty.is_signed(db.upcast()) { + let name = "$safe_pow_signed"; + let args = vec![lhs, rhs, min_value, max_value]; + provider.create_then_call(name, args, |provider| { + make_safe_pow_signed(provider, db, name) + }) + } else { + let name = "$safe_pow_unsigned"; + let args = vec![lhs, rhs, max_value]; + provider.create_then_call(name, args, |provider| { + make_safe_pow_unsigned(provider, db, name) + }) + } +} + +fn make_safe_add_signed( + provider: &mut DefaultRuntimeProvider, + db: &dyn CodegenDb, + func_name: &str, +) -> RuntimeFunction { + let func_name = YulVariable::new(func_name); + let lhs = YulVariable::new("$lhs"); + let rhs = YulVariable::new("$rhs"); + let min_value = YulVariable::new("$min_value"); + let max_value = YulVariable::new("$max_value"); + let ret = YulVariable::new("$ret"); + + let func = function_definition! { + function [func_name.ident()]([lhs.ident()], [rhs.ident()], [min_value.ident()], [max_value.ident()]) -> [ret.ident()] { + (if (and((iszero((slt([lhs.expr()], 0)))), (sgt([rhs.expr()], (sub([max_value.expr()], [lhs.expr()])))))) { [revert_with_overflow(provider, db)] }) + (if (and((slt([lhs.expr()], 0)), (slt([rhs.expr()], (sub([min_value.expr()], [lhs.expr()])))))) { [revert_with_overflow(provider, db)] }) + ([ret.ident()] := add([lhs.expr()], [rhs.expr()])) + } + }; + RuntimeFunction::from_statement(func) +} + +fn make_safe_add_unsigned( + provider: &mut DefaultRuntimeProvider, + db: &dyn CodegenDb, + func_name: &str, +) -> RuntimeFunction { + let func_name = YulVariable::new(func_name); + let lhs = YulVariable::new("$lhs"); + let rhs = YulVariable::new("$rhs"); + let max_value = YulVariable::new("$max_value"); + let ret = YulVariable::new("$ret"); + + let func = function_definition! { + function [func_name.ident()]([lhs.ident()], [rhs.ident()], [max_value.ident()]) -> [ret.ident()] { + (if (gt([lhs.expr()], (sub([max_value.expr()], [rhs.expr()])))) { [revert_with_overflow(provider, db)] }) + ([ret.ident()] := add([lhs.expr()], [rhs.expr()])) + } + }; + RuntimeFunction::from_statement(func) +} + +fn make_safe_sub_signed( + provider: &mut DefaultRuntimeProvider, + db: &dyn CodegenDb, + func_name: &str, +) -> RuntimeFunction { + let func_name = YulVariable::new(func_name); + let lhs = YulVariable::new("$lhs"); + let rhs = YulVariable::new("$rhs"); + let min_value = YulVariable::new("$min_value"); + let max_value = YulVariable::new("$max_value"); + let ret = YulVariable::new("$ret"); + + let func = function_definition! { + function [func_name.ident()]([lhs.ident()], [rhs.ident()], [min_value.ident()], [max_value.ident()]) -> [ret.ident()] { + (if (and((iszero((slt([rhs.expr()], 0)))), (slt([lhs.expr()], (add([min_value.expr()], [rhs.expr()])))))) { [revert_with_overflow(provider, db)] }) + (if (and((slt([rhs.expr()], 0)), (sgt([lhs.expr()], (add([max_value.expr()], [rhs.expr()])))))) { [revert_with_overflow(provider, db)] }) + ([ret.ident()] := sub([lhs.expr()], [rhs.expr()])) + } + }; + RuntimeFunction::from_statement(func) +} + +fn make_safe_sub_unsigned( + provider: &mut DefaultRuntimeProvider, + db: &dyn CodegenDb, + func_name: &str, +) -> RuntimeFunction { + let func_name = YulVariable::new(func_name); + let lhs = YulVariable::new("$lhs"); + let rhs = YulVariable::new("$rhs"); + let ret = YulVariable::new("$ret"); + + let func = function_definition! { + function [func_name.ident()]([lhs.ident()], [rhs.ident()]) -> [ret.ident()] { + (if (lt([lhs.expr()], [rhs.expr()])) { [revert_with_overflow(provider, db)] }) + ([ret.ident()] := sub([lhs.expr()], [rhs.expr()])) + } + }; + RuntimeFunction::from_statement(func) +} + +fn make_safe_mul_signed( + provider: &mut DefaultRuntimeProvider, + db: &dyn CodegenDb, + func_name: &str, +) -> RuntimeFunction { + let func_name = YulVariable::new(func_name); + let lhs = YulVariable::new("$lhs"); + let rhs = YulVariable::new("$rhs"); + let min_value = YulVariable::new("$min_value"); + let max_value = YulVariable::new("$max_value"); + let ret = YulVariable::new("$ret"); + + let func = function_definition! { + function [func_name.ident()]([lhs.ident()], [rhs.ident()], [min_value.ident()], [max_value.ident()]) -> [ret.ident()] { + // overflow, if lhs > 0, rhs > 0 and lhs > (max_value / rhs) + (if (and((and((sgt([lhs.expr()], 0)), (sgt([rhs.expr()], 0)))), (gt([lhs.expr()], (div([max_value.expr()], [rhs.expr()])))))) { [revert_with_overflow(provider, db)] }) + // underflow, if lhs > 0, rhs < 0 and rhs < (min_value / lhs) + (if (and((and((sgt([lhs.expr()], 0)), (slt([rhs.expr()], 0)))), (slt([rhs.expr()], (sdiv([min_value.expr()], [lhs.expr()])))))) { [revert_with_overflow(provider, db)] }) + // underflow, if lhs < 0, rhs > 0 and lhs < (min_value / rhs) + (if (and((and((slt([lhs.expr()], 0)), (sgt([rhs.expr()], 0)))), (slt([lhs.expr()], (sdiv([min_value.expr()], [rhs.expr()])))))) { [revert_with_overflow(provider, db)] }) + // overflow, if lhs < 0, rhs < 0 and lhs < (max_value / rhs) + (if (and((and((slt([lhs.expr()], 0)), (slt([rhs.expr()], 0)))), (slt([lhs.expr()], (sdiv([max_value.expr()], [rhs.expr()])))))) { [revert_with_overflow(provider, db)] }) + ([ret.ident()] := mul([lhs.expr()], [rhs.expr()])) + } + }; + RuntimeFunction::from_statement(func) +} + +fn make_safe_mul_unsigned( + provider: &mut DefaultRuntimeProvider, + db: &dyn CodegenDb, + func_name: &str, +) -> RuntimeFunction { + let func_name = YulVariable::new(func_name); + let lhs = YulVariable::new("$lhs"); + let rhs = YulVariable::new("$rhs"); + let max_value = YulVariable::new("$max_value"); + let ret = YulVariable::new("$ret"); + + let func = function_definition! { + function [func_name.ident()]([lhs.ident()], [rhs.ident()], [max_value.ident()]) -> [ret.ident()] { + // overflow, if lhs != 0 and rhs > (max_value / lhs) + (if (and((iszero((iszero([lhs.expr()])))), (gt([rhs.expr()], (div([max_value.expr()], [lhs.expr()])))))) { [revert_with_overflow(provider ,db)] }) + ([ret.ident()] := mul([lhs.expr()], [rhs.expr()])) + } + }; + RuntimeFunction::from_statement(func) +} + +fn make_safe_div_signed( + provider: &mut DefaultRuntimeProvider, + db: &dyn CodegenDb, + func_name: &str, +) -> RuntimeFunction { + let func_name = YulVariable::new(func_name); + let lhs = YulVariable::new("$lhs"); + let rhs = YulVariable::new("$rhs"); + let min_value = YulVariable::new("$min_value"); + let ret = YulVariable::new("$ret"); + + let func = function_definition! { + function [func_name.ident()]([lhs.ident()], [rhs.ident()], [min_value.ident()]) -> [ret.ident()] { + (if (iszero([rhs.expr()])) { [revert_with_zero_division(provider, db)] }) + (if (and( (eq([lhs.expr()], [min_value.expr()])), (eq([rhs.expr()], (sub(0, 1))))) ) { [revert_with_overflow(provider, db)] }) + ([ret.ident()] := sdiv([lhs.expr()], [rhs.expr()])) + } + }; + RuntimeFunction::from_statement(func) +} + +fn make_safe_div_unsigned( + provider: &mut DefaultRuntimeProvider, + db: &dyn CodegenDb, + func_name: &str, +) -> RuntimeFunction { + let func_name = YulVariable::new(func_name); + let lhs = YulVariable::new("$lhs"); + let rhs = YulVariable::new("$rhs"); + let ret = YulVariable::new("$ret"); + + let func = function_definition! { + function [func_name.ident()]([lhs.ident()], [rhs.ident()]) -> [ret.ident()] { + (if (iszero([rhs.expr()])) { [revert_with_zero_division(provider, db)] }) + ([ret.ident()] := div([lhs.expr()], [rhs.expr()])) + } + }; + RuntimeFunction::from_statement(func) +} + +fn make_safe_mod_signed( + provider: &mut DefaultRuntimeProvider, + db: &dyn CodegenDb, + func_name: &str, +) -> RuntimeFunction { + let func_name = YulVariable::new(func_name); + let lhs = YulVariable::new("$lhs"); + let rhs = YulVariable::new("$rhs"); + let ret = YulVariable::new("$ret"); + + let func = function_definition! { + function [func_name.ident()]([lhs.ident()], [rhs.ident()]) -> [ret.ident()] { + (if (iszero([rhs.expr()])) { [revert_with_zero_division(provider, db)] }) + ([ret.ident()] := smod([lhs.expr()], [rhs.expr()])) + } + }; + RuntimeFunction::from_statement(func) +} + +fn make_safe_mod_unsigned( + provider: &mut DefaultRuntimeProvider, + db: &dyn CodegenDb, + func_name: &str, +) -> RuntimeFunction { + let func_name = YulVariable::new(func_name); + let lhs = YulVariable::new("$lhs"); + let rhs = YulVariable::new("$rhs"); + let ret = YulVariable::new("$ret"); + + let func = function_definition! { + function [func_name.ident()]([lhs.ident()], [rhs.ident()]) -> [ret.ident()] { + (if (iszero([rhs.expr()])) { [revert_with_zero_division(provider, db)] }) + ([ret.ident()] := mod([lhs.expr()], [rhs.expr()])) + } + }; + RuntimeFunction::from_statement(func) +} + +const SAFE_POW_HELPER_NAME: &str = "safe_pow_helper"; + +fn make_safe_pow_unsigned( + provider: &mut DefaultRuntimeProvider, + db: &dyn CodegenDb, + func_name: &str, +) -> RuntimeFunction { + let func_name = YulVariable::new(func_name); + let base = YulVariable::new("base"); + let exponent = YulVariable::new("exponent"); + let max_value = YulVariable::new("max_value"); + let power = YulVariable::new("power"); + + let safe_pow_helper_call = yul::Statement::Assignment(yul::Assignment { + identifiers: vec![base.ident(), power.ident()], + expression: { + let args = vec![ + base.expr(), + exponent.expr(), + literal_expression! {1}, + max_value.expr(), + ]; + provider.create_then_call(SAFE_POW_HELPER_NAME, args, |provider| { + make_safe_exp_helper(provider, db, SAFE_POW_HELPER_NAME) + }) + }, + }); + + let func = function_definition! { + function [func_name.ident()]([base.ident()], [exponent.ident()], [max_value.ident()]) -> [power.ident()] { + // Currently, `leave` avoids this function being inlined. + // YUL team is working on optimizer improvements to fix that. + + // Note that 0**0 == 1 + (if (iszero([exponent.expr()])) { + ([power.ident()] := 1 ) + (leave) + }) + (if (iszero([base.expr()])) { + ([power.ident()] := 0 ) + (leave) + }) + // Specializations for small bases + ([switch! { + switch [base.expr()] + // 0 is handled above + (case 1 { + ([power.ident()] := 1 ) + (leave) + }) + (case 2 { + (if (gt([exponent.expr()], 255)) { + [revert_with_overflow(provider, db)] + }) + ([power.ident()] := (exp(2, [exponent.expr()]))) + (if (gt([power.expr()], [max_value.expr()])) { + [revert_with_overflow(provider, db)] + }) + (leave) + }) + }]) + (if (and((sgt([power.expr()], 0)), (gt([power.expr()], (div([max_value.expr()], [base.expr()])))))) { [revert_with_overflow(provider, db)] }) + + (if (or((and((lt([base.expr()], 11)), (lt([exponent.expr()], 78)))), (and((lt([base.expr()], 307)), (lt([exponent.expr()], 32)))))) { + ([power.ident()] := (exp([base.expr()], [exponent.expr()]))) + (if (gt([power.expr()], [max_value.expr()])) { + [revert_with_overflow(provider, db)] + }) + (leave) + }) + + ([safe_pow_helper_call]) + (if (gt([power.expr()], (div([max_value.expr()], [base.expr()])))) { + [revert_with_overflow(provider, db)] + }) + ([power.ident()] := (mul([power.expr()], [base.expr()]))) + } + }; + RuntimeFunction::from_statement(func) +} + +fn make_safe_pow_signed( + provider: &mut DefaultRuntimeProvider, + db: &dyn CodegenDb, + func_name: &str, +) -> RuntimeFunction { + let func_name = YulVariable::new(func_name); + let base = YulVariable::new("base"); + let exponent = YulVariable::new("exponent"); + let min_value = YulVariable::new("min_value"); + let max_value = YulVariable::new("max_value"); + let power = YulVariable::new("power"); + + let safe_pow_helper_call = yul::Statement::Assignment(yul::Assignment { + identifiers: vec![base.ident(), power.ident()], + expression: { + let args = vec![base.expr(), exponent.expr(), power.expr(), max_value.expr()]; + provider.create_then_call(SAFE_POW_HELPER_NAME, args, |provider| { + make_safe_exp_helper(provider, db, SAFE_POW_HELPER_NAME) + }) + }, + }); + + let func = function_definition! { + function [func_name.ident()]([base.ident()], [exponent.ident()], [min_value.ident()], [max_value.ident()]) -> [power.ident()] { + // Currently, `leave` avoids this function being inlined. + // YUL team is working on optimizer improvements to fix that. + + // Note that 0**0 == 1 + ([switch! { + switch [exponent.expr()] + (case 0 { + ([power.ident()] := 1 ) + (leave) + }) + (case 1 { + ([power.ident()] := [base.expr()] ) + (leave) + }) + }]) + (if (iszero([base.expr()])) { + ([power.ident()] := 0 ) + (leave) + }) + ([power.ident()] := 1 ) + // We pull out the first iteration because it is the only one in which + // base can be negative. + // Exponent is at least 2 here. + // overflow check for base * base + ([switch! { + switch (sgt([base.expr()], 0)) + (case 1 { + (if (gt([base.expr()], (div([max_value.expr()], [base.expr()])))) { + [revert_with_overflow(provider, db)] + }) + }) + (case 0 { + (if (slt([base.expr()], (sdiv([max_value.expr()], [base.expr()])))) { + [revert_with_overflow(provider, db)] + }) + }) + }]) + (if (and([exponent.expr()], 1)) { + ([power.ident()] := [base.expr()] ) + }) + ([base.ident()] := (mul([base.expr()], [base.expr()]))) + ([exponent.ident()] := shr(1, [exponent.expr()])) + // // Below this point, base is always positive. + ([safe_pow_helper_call]) // power = 1, base = 16 which is wrong + (if (and((sgt([power.expr()], 0)), (gt([power.expr()], (div([max_value.expr()], [base.expr()])))))) { [revert_with_overflow(provider , db)] }) + (if (and((slt([power.expr()], 0)), (slt([power.expr()], (sdiv([min_value.expr()], [base.expr()])))))) { [revert_with_overflow(provider, db)] }) + ([power.ident()] := (mul([power.expr()], [base.expr()]))) + } + }; + RuntimeFunction::from_statement(func) +} + +fn make_safe_exp_helper( + provider: &mut DefaultRuntimeProvider, + db: &dyn CodegenDb, + func_name: &str, +) -> RuntimeFunction { + let func_name = YulVariable::new(func_name); + let base = YulVariable::new("base"); + let exponent = YulVariable::new("exponent"); + let power = YulVariable::new("power"); + let max_value = YulVariable::new("max_value"); + let ret_power = YulVariable::new("ret_power"); + let ret_base = YulVariable::new("ret_base"); + + let func = function_definition! { + function [func_name.ident()]([base.ident()], [exponent.ident()], [power.ident()], [max_value.ident()]) + -> [(vec![ret_base.ident(), ret_power.ident()])...] { + ([ret_base.ident()] := [base.expr()]) + ([ret_power.ident()] := [power.expr()]) + (for {} (gt([exponent.expr()], 1)) {} + { + // overflow check for base * base + (if (gt([ret_base.expr()], (div([max_value.expr()], [ret_base.expr()])))) { [revert_with_overflow(provider, db)] }) + (if (and([exponent.expr()], 1)) { + // No checks for power := mul(power, base) needed, because the check + // for base * base above is sufficient, since: + // |power| <= base (proof by induction) and thus: + // |power * base| <= base * base <= max <= |min| (for signed) + // (this is equally true for signed and unsigned exp) + ([ret_power.ident()] := (mul([ret_power.expr()], [ret_base.expr()]))) + }) + ([ret_base.ident()] := mul([ret_base.expr()], [ret_base.expr()])) + ([exponent.ident()] := shr(1, [exponent.expr()])) + }) + } + }; + RuntimeFunction::from_statement(func) +} + +fn revert_with_overflow(provider: &mut dyn RuntimeProvider, db: &dyn CodegenDb) -> yul::Statement { + const PANIC_OVERFLOW: usize = 0x11; + + super::panic_revert_numeric(provider, db, literal_expression! {(PANIC_OVERFLOW)}) +} + +fn revert_with_zero_division( + provider: &mut dyn RuntimeProvider, + db: &dyn CodegenDb, +) -> yul::Statement { + pub const PANIC_ZERO_DIVISION: usize = 0x12; + + super::panic_revert_numeric(provider, db, literal_expression! {(PANIC_ZERO_DIVISION)}) +} + +fn get_max_value(db: &dyn CodegenDb, ty: TypeId) -> yul::Expression { + match &ty.data(db.upcast()).kind { + TypeKind::I8 => literal_expression! {0x7f}, + TypeKind::I16 => literal_expression! {0x7fff}, + TypeKind::I32 => literal_expression! {0x7fffffff}, + TypeKind::I64 => literal_expression! {0x7fffffffffffffff}, + TypeKind::I128 => literal_expression! {0x7fffffffffffffffffffffffffffffff}, + TypeKind::I256 => { + literal_expression! {0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff} + } + TypeKind::U8 => literal_expression! {0xff}, + TypeKind::U16 => literal_expression! {0xffff}, + TypeKind::U32 => literal_expression! {0xffffffff}, + TypeKind::U64 => literal_expression! {0xffffffffffffffff}, + TypeKind::U128 => literal_expression! {0xffffffffffffffffffffffffffffffff}, + TypeKind::U256 => { + literal_expression! {0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff} + } + _ => unreachable!(), + } +} + +fn get_min_value(db: &dyn CodegenDb, ty: TypeId) -> yul::Expression { + debug_assert! {ty.is_integral(db.upcast())}; + + match &ty.data(db.upcast()).kind { + TypeKind::I8 => { + literal_expression! {0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80} + } + TypeKind::I16 => { + literal_expression! {0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8000} + } + TypeKind::I32 => { + literal_expression! {0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff80000000} + } + TypeKind::I64 => { + literal_expression! {0xffffffffffffffffffffffffffffffffffffffffffffffff8000000000000000} + } + TypeKind::I128 => { + literal_expression! {0xffffffffffffffffffffffffffffffff80000000000000000000000000000000} + } + TypeKind::I256 => { + literal_expression! {0x8000000000000000000000000000000000000000000000000000000000000000} + } + + _ => literal_expression! {0x0}, + } +} diff --git a/crates/codegen/src/yul/slot_size.rs b/crates/codegen/src/yul/slot_size.rs new file mode 100644 index 0000000000..aa931132c0 --- /dev/null +++ b/crates/codegen/src/yul/slot_size.rs @@ -0,0 +1,16 @@ +use fe_mir::ir::{Type, TypeId, TypeKind}; + +use crate::db::CodegenDb; + +// We use the same slot size between memory and storage to simplify the +// implementation and minimize gas consumption in memory <-> storage copy +// instructions. +pub(crate) const SLOT_SIZE: usize = 32; + +pub(crate) fn yul_primitive_type(db: &dyn CodegenDb) -> TypeId { + db.mir_intern_type(Type::new(TypeKind::U256, None).into()) +} + +pub(crate) fn function_hash_type(db: &dyn CodegenDb) -> TypeId { + db.mir_intern_type(Type::new(TypeKind::U32, None).into()) +} diff --git a/crates/driver/Cargo.toml b/crates/driver/Cargo.toml index c77d78cf18..64147cf7c2 100644 --- a/crates/driver/Cargo.toml +++ b/crates/driver/Cargo.toml @@ -17,6 +17,7 @@ fe-analyzer = {path = "../analyzer", version = "^0.16.0-alpha"} fe-common = {path = "../common", version = "^0.16.0-alpha"} fe-lowering = {path = "../lowering", version = "^0.16.0-alpha"} fe-mir = {path = "../mir", version = "^0.16.0-alpha"} +fe-codegen = {path = "../codegen", version = "^0.16.0-alpha"} fe-parser = {path = "../parser", version = "^0.16.0-alpha"} fe-yulgen = {path = "../yulgen", version = "^0.16.0-alpha"} fe-yulc = {path = "../yulc", version = "^0.16.0-alpha", features = ["solc-backend"], optional = true} diff --git a/crates/driver/src/lib.rs b/crates/driver/src/lib.rs index 764bd8ea6c..8d355f8558 100644 --- a/crates/driver/src/lib.rs +++ b/crates/driver/src/lib.rs @@ -1,11 +1,13 @@ #![allow(unused_imports, dead_code)] -pub use fe_mir::db::NewDb; +pub use fe_codegen::db::{CodegenDb, NewDb}; +use fe_codegen::yul::runtime::RuntimeProvider; pub use fe_yulgen::Db; -use fe_analyzer::context::Analysis; use fe_analyzer::namespace::items::{IngotId, IngotMode, ModuleId}; use fe_analyzer::AnalyzerDb; +use fe_analyzer::{context::Analysis, namespace::items::ContractId}; +use fe_common::db::Upcast; use fe_common::diagnostics::{print_diagnostics, Diagnostic}; use fe_common::files::{FileKind, SourceFileId}; use fe_mir::db::MirDb; @@ -36,7 +38,7 @@ pub struct CompiledContract { pub struct CompileError(pub Vec); pub fn compile_single_file( - db: &mut Db, + db: &mut NewDb, path: &str, src: &str, with_bytecode: bool, @@ -57,7 +59,7 @@ pub fn compile_single_file( /// If `with_bytecode` is set to false, the compiler will skip the final Yul -> /// Bytecode pass. This is useful when debugging invalid Yul code. pub fn compile_ingot( - db: &mut Db, + db: &mut NewDb, name: &str, files: &[(impl AsRef, impl AsRef)], with_bytecode: bool, @@ -99,89 +101,122 @@ pub fn dump_mir_single_file(db: &mut NewDb, path: &str, src: &str) -> Result Result { - // build abi - let json_abis = fe_abi::build(db, module_id).expect("failed to generate abi"); +// TODO: Remove this!!!! +pub fn dump_codegen_funcs(db: &mut NewDb, path: &str, src: &str) -> Result { + let module = ModuleId::new_standalone(db, path, src); - // lower the AST - let lowered_module_id = fe_lowering::lower_main_module(db, module_id); - let lowered_ast = format!("{:#?}", &lowered_module_id.ast(db)); + let diags = module.diagnostics(db); + if !diags.is_empty() { + return Err(CompileError(diags)); + } - if !lowered_module_id.diagnostics(db).is_empty() { - eprintln!("Error: Analysis of lowered module resulted in the following errors:"); - print_diagnostics(db, &lowered_module_id.diagnostics(db)); - panic!("Lowered module has errors. Unfortunately, this is a bug in the Fe compiler.") + let mut contracts = String::new(); + for contract in db.module_contracts(module).as_ref() { + contracts.push_str(&format!( + "{}", + fe_codegen::yul::isel::lower_contract_deployable(db, *contract) + )) } - // compile to yul - let yul_contracts = fe_yulgen::compile(db, lowered_module_id); + Ok(contracts) +} - // compile to bytecode if required - let _bytecode_contracts = if _with_bytecode { - compile_yul(yul_contracts.iter(), _optimize) - } else { - IndexMap::new() - }; - - // combine all of the named contract maps - let contracts = json_abis - .keys() - .map(|name| { - ( - name.clone(), - CompiledContract { - json_abi: json_abis[name].clone(), - yul: yul_contracts[name].clone(), - #[cfg(feature = "solc-backend")] - bytecode: if _with_bytecode { - _bytecode_contracts[name].to_owned() - } else { - "".to_string() - }, - }, - ) - }) - .collect::>(); +#[cfg(feature = "solc-backend")] +fn compile_module_id( + db: &mut NewDb, + module_id: ModuleId, + with_bytecode: bool, + optimize: bool, +) -> Result { + let mut contracts = IndexMap::default(); + for &contract in module_id.all_contracts(db.upcast()).as_ref() { + let name = &contract.data(db.upcast()).name; + let abi = db.codegen_abi_contract(contract); + let yul_contract = compile_to_yul(db, contract); + + let bytecode = if with_bytecode { + let deployable_name = db.codegen_contract_deployer_symbol_name(contract); + compile_to_evm(deployable_name.as_str(), &yul_contract, optimize) + } else { + "".to_string() + }; + + contracts.insert( + name.to_string(), + CompiledContract { + json_abi: serde_json::to_string_pretty(&abi).unwrap(), + yul: yul_contract, + bytecode, + }, + ); + } Ok(CompiledModule { src_ast: format!("{:?}", module_id.ast(db)), - lowered_ast, + lowered_ast: format!("{:?}", module_id.ast(db)), contracts, }) } -fn compile_yul( - _contracts: impl Iterator, impl AsRef)>, +#[cfg(not(feature = "solc-backend"))] +fn compile_module_id( + db: &mut NewDb, + module_id: ModuleId, + _with_bytecode: bool, _optimize: bool, -) -> IndexMap { - #[cfg(feature = "solc-backend")] - { - match fe_yulc::compile(_contracts, _optimize) { - Err(error) => { - for error in serde_json::from_str::(&error.0) - .expect("unable to deserialize json output")["errors"] - .as_array() - .expect("errors not an array") - { - eprintln!( - "Error: {}", - error["formattedMessage"] - .as_str() - .expect("error value not a string") - .replace("\\\n", "\n") - ) - } - panic!("Yul compilation failed with the above errors") +) -> Result { + let mut contracts = IndexMap::default(); + for &contract in module_id.all_contracts(db.upcast()).as_ref() { + let name = &contract.data(db.upcast()).name; + let abi = db.codegen_abi_contract(contract); + let yul_contract = compile_to_yul(db, contract); + + contracts.insert( + name.to_string(), + CompiledContract { + json_abi: serde_json::to_string_pretty(&abi).unwrap(), + yul: yul_contract, + }, + ); + } + + Ok(CompiledModule { + src_ast: format!("{:?}", module_id.ast(db)), + lowered_ast: format!("{:?}", module_id.ast(db)), + contracts, + }) +} + +fn compile_to_yul(db: &mut NewDb, contract: ContractId) -> String { + let yul_contract = fe_codegen::yul::isel::lower_contract_deployable(db, contract); + yul_contract.to_string().replace('"', "\\\"") +} + +#[cfg(feature = "solc-backend")] +fn compile_to_evm(name: &str, yul_object: &str, optimize: bool) -> String { + match fe_yulc::compile_single_contract(name, yul_object, optimize) { + Ok(contracts) => contracts, + + Err(error) => { + for error in serde_json::from_str::(&error.0) + .expect("unable to deserialize json output")["errors"] + .as_array() + .expect("errors not an array") + { + eprintln!( + "Error: {}", + error["formattedMessage"] + .as_str() + .expect("error value not a string") + .replace("\\\n", "\n") + ) } - Ok(contracts) => contracts, + panic!("Yul compilation failed with the above errors") } } +} - #[cfg(not(feature = "solc-backend"))] - IndexMap::new() +#[cfg(not(feature = "solc-backend"))] +fn compile_to_evm(_: &str, _: &str, _: bool) -> String { + unreachable!() } diff --git a/crates/fe/src/main.rs b/crates/fe/src/main.rs index 75c4608700..efdffb1e1b 100644 --- a/crates/fe/src/main.rs +++ b/crates/fe/src/main.rs @@ -9,7 +9,7 @@ use clap::{arg_enum, values_t, App, Arg}; use fe_common::diagnostics::print_diagnostics; use fe_common::files::SourceFileId; use fe_common::panic::install_panic_hook; -use fe_driver::{CompiledModule, Db}; +use fe_driver::CompiledModule; use walkdir::WalkDir; const DEFAULT_OUTPUT_DIR_NAME: &str = "output"; @@ -77,6 +77,12 @@ pub fn main() { .help("dump mir dot file") .takes_value(false), ) + .arg( + Arg::with_name("codegen") + .long("codegen") + .help("todo") + .takes_value(false), + ) .get_matches(); let input_path = matches.value_of("input").unwrap(); @@ -90,12 +96,17 @@ pub fn main() { if matches.is_present("mir") { return mir_dump(input_path); } + + if matches.is_present("codegen") { + return yul_functions_dump(input_path); + } + #[cfg(not(feature = "solc-backend"))] if with_bytecode { eprintln!("Warning: bytecode output requires 'solc-backend' feature. Try `cargo build --release --features solc-backend`. Skipping."); } - let mut db = Db::default(); + let mut db = fe_driver::NewDb::default(); let (content, compiled_module) = if Path::new(input_path).is_file() { let content = match std::fs::read_to_string(input_path) { @@ -296,3 +307,30 @@ fn mir_dump(input_path: &str) { std::process::exit(1) } } + +fn yul_functions_dump(input_path: &str) { + let mut db = fe_driver::NewDb::default(); + if Path::new(input_path).is_file() { + let content = match std::fs::read_to_string(input_path) { + Err(err) => { + eprintln!("Failed to load file: `{}`. Error: {}", input_path, err); + std::process::exit(1) + } + Ok(content) => content, + }; + + match fe_driver::dump_codegen_funcs(&mut db, input_path, &content) { + Ok(contract) => { + println!("{}", contract) + } + Err(err) => { + eprintln!("Unable to dump mir `{}", input_path); + print_diagnostics(&db, &err.0); + std::process::exit(1) + } + } + } else { + eprintln!("mir doesn't support ingot yet"); + std::process::exit(1) + } +} diff --git a/crates/mir/bar.dot b/crates/mir/bar.dot deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/crates/mir/src/db/queries/function.rs b/crates/mir/src/db/queries/function.rs index f1bba171d1..d863f36d18 100644 --- a/crates/mir/src/db/queries/function.rs +++ b/crates/mir/src/db/queries/function.rs @@ -5,7 +5,7 @@ use smol_str::SmolStr; use crate::{ db::MirDb, - ir::{self, FunctionSignature, TypeId}, + ir::{self, function::Linkage, FunctionSignature, TypeId}, lower::function::{lower_func_body, lower_func_signature}, }; @@ -29,6 +29,10 @@ impl ir::FunctionId { self.signature(db).return_type } + pub fn linkage(self, db: &dyn MirDb) -> Linkage { + self.signature(db).linkage + } + pub fn analyzer_func(self, db: &dyn MirDb) -> analyzer_items::FunctionId { self.signature(db).analyzer_func_id } @@ -63,4 +67,10 @@ impl ir::FunctionId { func_name } } + + pub fn returns_aggregate(self, db: &dyn MirDb) -> bool { + self.return_type(db) + .map(|ty| ty.is_aggregate(db)) + .unwrap_or_default() + } } diff --git a/crates/mir/src/db/queries/types.rs b/crates/mir/src/db/queries/types.rs index 15365acc3e..5a97972c07 100644 --- a/crates/mir/src/db/queries/types.rs +++ b/crates/mir/src/db/queries/types.rs @@ -7,7 +7,10 @@ use num_traits::ToPrimitive; use crate::{ db::MirDb, - ir::{types::ArrayDef, value::Immediate, Type, TypeId, Value}, + ir::{ + types::{ArrayDef, TypeKind}, + Type, TypeId, Value, + }, lower::types::{lower_event_type, lower_type}, }; @@ -25,17 +28,18 @@ impl TypeId { } pub fn projection_ty(self, db: &dyn MirDb, access: &Value) -> TypeId { - match self.data(db).as_ref() { - Type::Array(ArrayDef { elem_ty, .. }) => *elem_ty, - Type::Tuple(def) => { + let ty = self.deref(db); + match &ty.data(db).as_ref().kind { + TypeKind::Array(ArrayDef { elem_ty, .. }) => *elem_ty, + TypeKind::Tuple(def) => { let index = expect_projection_index(access); def.items[index] } - Type::Struct(def) | Type::Contract(def) => { + TypeKind::Struct(def) | TypeKind::Contract(def) => { let index = expect_projection_index(access); def.fields[index].1 } - Type::Event(def) => { + TypeKind::Event(def) => { let index = expect_projection_index(access); def.fields[index].1 } @@ -43,54 +47,66 @@ impl TypeId { } } + pub fn deref(self, db: &dyn MirDb) -> TypeId { + match &self.data(db).as_ref().kind { + TypeKind::SPtr(inner) => *inner, + TypeKind::MPtr(inner) => *inner, + _ => self, + } + } + + pub fn make_sptr(self, db: &dyn MirDb) -> TypeId { + db.mir_intern_type(Type::new(TypeKind::SPtr(self), None).into()) + } + + pub fn make_mptr(self, db: &dyn MirDb) -> TypeId { + db.mir_intern_type(Type::new(TypeKind::MPtr(self), None).into()) + } + pub fn projection_ty_imm(self, db: &dyn MirDb, index: usize) -> TypeId { debug_assert!(self.is_aggregate(db)); - match self.data(db).as_ref() { - Type::Array(ArrayDef { elem_ty, .. }) => *elem_ty, - Type::Tuple(def) => def.items[index], - Type::Struct(def) | Type::Contract(def) => def.fields[index].1, - Type::Event(def) => def.fields[index].1, + match &self.data(db).as_ref().kind { + TypeKind::Array(ArrayDef { elem_ty, .. }) => *elem_ty, + TypeKind::Tuple(def) => def.items[index], + TypeKind::Struct(def) | TypeKind::Contract(def) => def.fields[index].1, + TypeKind::Event(def) => def.fields[index].1, + _ => unreachable!(), + } + } + + pub fn aggregate_field_num(self, db: &dyn MirDb) -> usize { + match &self.data(db).as_ref().kind { + TypeKind::Array(ArrayDef { len, .. }) => *len, + TypeKind::Tuple(def) => def.items.len(), + TypeKind::Struct(def) | TypeKind::Contract(def) => def.fields.len(), + TypeKind::Event(def) => def.fields.len(), _ => unreachable!(), } } - pub fn index_from_fname(self, db: &dyn MirDb, fname: &str, index_ty: TypeId) -> Immediate { - match self.data(db).as_ref() { - Type::Tuple(_) => { + pub fn index_from_fname(self, db: &dyn MirDb, fname: &str) -> BigInt { + let ty = self.deref(db); + match &ty.data(db).as_ref().kind { + TypeKind::Tuple(_) => { // TODO: Fix this when the syntax for tuple access changes. let index_str = &fname[4..]; - Immediate { - value: BigInt::from_str(index_str).unwrap(), - ty: index_ty, - } + BigInt::from_str(index_str).unwrap() } - Type::Struct(def) | Type::Contract(def) => { - let index = def - .fields - .iter() - .enumerate() - .find_map(|(i, field)| (field.0 == fname).then(|| i.into())) - .unwrap(); - Immediate { - value: index, - ty: index_ty, - } - } + TypeKind::Struct(def) | TypeKind::Contract(def) => def + .fields + .iter() + .enumerate() + .find_map(|(i, field)| (field.0 == fname).then(|| i.into())) + .unwrap(), - Type::Event(def) => { - let index = def - .fields - .iter() - .enumerate() - .find_map(|(i, field)| (field.0 == fname).then(|| i.into())) - .unwrap(); - Immediate { - value: index, - ty: index_ty, - } - } + TypeKind::Event(def) => def + .fields + .iter() + .enumerate() + .find_map(|(i, field)| (field.0 == fname).then(|| i.into())) + .unwrap(), other => unreachable!("{:?} does not have fields", other), } @@ -98,40 +114,86 @@ impl TypeId { pub fn is_primitive(self, db: &dyn MirDb) -> bool { matches!( - self.data(db).as_ref(), - Type::I8 - | Type::I16 - | Type::I32 - | Type::I64 - | Type::I128 - | Type::I256 - | Type::U8 - | Type::U16 - | Type::U32 - | Type::U64 - | Type::U128 - | Type::U256 - | Type::Bool - | Type::Address - | Type::Unit + &self.data(db).as_ref().kind, + TypeKind::I8 + | TypeKind::I16 + | TypeKind::I32 + | TypeKind::I64 + | TypeKind::I128 + | TypeKind::I256 + | TypeKind::U8 + | TypeKind::U16 + | TypeKind::U32 + | TypeKind::U64 + | TypeKind::U128 + | TypeKind::U256 + | TypeKind::Bool + | TypeKind::Address + | TypeKind::Unit + ) + } + + pub fn is_integral(self, db: &dyn MirDb) -> bool { + matches!( + &self.data(db).as_ref().kind, + TypeKind::I8 + | TypeKind::I16 + | TypeKind::I32 + | TypeKind::I64 + | TypeKind::I128 + | TypeKind::I256 + | TypeKind::U8 + | TypeKind::U16 + | TypeKind::U32 + | TypeKind::U64 + | TypeKind::U128 + | TypeKind::U256 + ) + } + + pub fn is_address(self, db: &dyn MirDb) -> bool { + matches!(&self.data(db).as_ref().kind, TypeKind::Address) + } + + pub fn is_signed(self, db: &dyn MirDb) -> bool { + matches!( + &self.data(db).kind, + TypeKind::I8 + | TypeKind::I16 + | TypeKind::I32 + | TypeKind::I64 + | TypeKind::I128 + | TypeKind::I256 + ) + } + + pub fn is_fe_string(self, db: &dyn MirDb) -> bool { + matches!( + &self.data(db).analyzer_ty, + Some(analyzer_types::Type::String(_)) ) } /// Returns size of the type in bytes. pub fn size_of(self, db: &dyn MirDb, slot_size: usize) -> usize { - match self.data(db).as_ref() { - Type::Bool | Type::I8 | Type::U8 => 1, - Type::I16 | Type::U16 => 2, - Type::I32 | Type::U32 => 4, - Type::I64 | Type::U64 => 8, - Type::I128 | Type::U128 => 16, - Type::I256 | Type::U256 | Type::Map(_) => 32, - Type::Address => 20, - Type::Unit => 0, - - Type::Array(def) => array_elem_size_imp(def, db, slot_size) * def.len, - - Type::Tuple(def) => { + match &self.data(db).as_ref().kind { + TypeKind::Bool | TypeKind::I8 | TypeKind::U8 => 1, + TypeKind::I16 | TypeKind::U16 => 2, + TypeKind::I32 | TypeKind::U32 => 4, + TypeKind::I64 | TypeKind::U64 => 8, + TypeKind::I128 | TypeKind::U128 => 16, + TypeKind::String(len) => 32 + len, + TypeKind::MPtr(..) + | TypeKind::SPtr(..) + | TypeKind::I256 + | TypeKind::U256 + | TypeKind::Map(_) => 32, + TypeKind::Address => 20, + TypeKind::Unit => 0, + + TypeKind::Array(def) => array_elem_size_imp(def, db, slot_size) * def.len, + + TypeKind::Tuple(def) => { if def.items.is_empty() { return 0; } @@ -140,7 +202,7 @@ impl TypeId { + def.items[last_idx].size_of(db, slot_size) } - Type::Struct(def) | Type::Contract(def) => { + TypeKind::Struct(def) | TypeKind::Contract(def) => { if def.fields.is_empty() { return 0; } @@ -149,7 +211,7 @@ impl TypeId { + def.fields[last_idx].1.size_of(db, slot_size) } - Type::Event(def) => { + TypeKind::Event(def) => { if def.fields.is_empty() { return 0; } @@ -177,14 +239,18 @@ impl TypeId { } /// Returns an offset of the element of aggregate type. - pub fn aggregate_elem_offset(self, db: &dyn MirDb, elem_idx: usize, slot_size: usize) -> usize { + pub fn aggregate_elem_offset(self, db: &dyn MirDb, elem_idx: T, slot_size: usize) -> usize + where + T: num_traits::ToPrimitive, + { debug_assert!(self.is_aggregate(db)); + let elem_idx = elem_idx.to_usize().unwrap(); if elem_idx == 0 { return 0; } - if let Type::Array(def) = self.data(db).as_ref() { + if let TypeKind::Array(def) = &self.data(db).kind { return array_elem_size_imp(def, db, slot_size) * elem_idx; } @@ -203,18 +269,53 @@ impl TypeId { pub fn is_aggregate(self, db: &dyn MirDb) -> bool { matches!( - self.data(db).as_ref(), - Type::Array(_) | Type::Tuple(_) | Type::Struct(_) | Type::Contract(_) | Type::Event(_) + &self.data(db).as_ref().kind, + TypeKind::Array(_) + | TypeKind::Tuple(_) + | TypeKind::Struct(_) + | TypeKind::Contract(_) + | TypeKind::Event(_) ) } + pub fn is_array(self, db: &dyn MirDb) -> bool { + matches!(&self.data(db).as_ref().kind, TypeKind::Array(_)) + } + + pub fn is_string(self, db: &dyn MirDb) -> bool { + matches! { + &self.data(db).as_ref().kind, + TypeKind::String(_) + } + } + + pub fn is_ptr(self, db: &dyn MirDb) -> bool { + self.is_mptr(db) || self.is_sptr(db) + } + + pub fn is_mptr(self, db: &dyn MirDb) -> bool { + matches!(self.data(db).kind, TypeKind::MPtr(_)) + } + + pub fn is_sptr(self, db: &dyn MirDb) -> bool { + matches!(self.data(db).kind, TypeKind::SPtr(_)) + } + + pub fn is_map(self, db: &dyn MirDb) -> bool { + matches!(self.data(db).kind, TypeKind::Map(_)) + } + pub fn is_contract(self, db: &dyn MirDb) -> bool { - matches!(self.data(db).as_ref(), Type::Contract(_)) + matches!(self.data(db).kind, TypeKind::Contract(_)) + } + + pub fn is_event(self, db: &dyn MirDb) -> bool { + matches!(self.data(db).kind, TypeKind::Event(_)) } pub fn array_elem_size(self, db: &dyn MirDb, slot_size: usize) -> usize { let data = self.data(db); - if let Type::Array(def) = data.as_ref() { + if let TypeKind::Array(def) = &data.kind { array_elem_size_imp(def, db, slot_size) } else { panic!("expected `Array` type; but got {:?}", data.as_ref()) @@ -225,12 +326,17 @@ impl TypeId { fn array_elem_size_imp(arr: &ArrayDef, db: &dyn MirDb, slot_size: usize) -> usize { let elem_ty = arr.elem_ty; let elem = elem_ty.size_of(db, slot_size); - round_up(elem, elem_ty.align_of(db, slot_size)) + let align = if elem_ty.is_address(db) { + slot_size + } else { + elem_ty.align_of(db, slot_size) + }; + round_up(elem, align) } fn expect_projection_index(value: &Value) -> usize { match value { - Value::Immediate(imm) => imm.value.to_usize().unwrap(), + Value::Immediate { imm, .. } => imm.to_usize().unwrap(), _ => panic!("given `value` is not an immediate"), } } @@ -253,8 +359,8 @@ mod tests { #[test] fn test_primitive_type_info() { let db = NewDb::default(); - let i8 = db.mir_intern_type(Type::I8.into()); - let bool = db.mir_intern_type(Type::Bool.into()); + let i8 = db.mir_intern_type(Type::new(TypeKind::I8, None).into()); + let bool = db.mir_intern_type(Type::new(TypeKind::Bool, None).into()); debug_assert_eq!(i8.size_of(&db, 1), 1); debug_assert_eq!(i8.size_of(&db, 32), 1); @@ -265,12 +371,12 @@ mod tests { debug_assert_eq!(i8.align_of(&db, 32), 1); debug_assert_eq!(i8.align_of(&db, 32), 1); - let u32 = db.mir_intern_type(Type::U32.into()); + let u32 = db.mir_intern_type(Type::new(TypeKind::U32, None).into()); debug_assert_eq!(u32.size_of(&db, 1), 4); debug_assert_eq!(u32.size_of(&db, 32), 4); debug_assert_eq!(u32.align_of(&db, 32), 1); - let address = db.mir_intern_type(Type::Address.into()); + let address = db.mir_intern_type(Type::new(TypeKind::Address, None).into()); debug_assert_eq!(address.size_of(&db, 1), 20); debug_assert_eq!(address.size_of(&db, 32), 20); debug_assert_eq!(address.align_of(&db, 32), 1); @@ -279,14 +385,14 @@ mod tests { #[test] fn test_primitive_elem_array_type_info() { let db = NewDb::default(); - let i32 = db.mir_intern_type(Type::I32.into()); + let i32 = db.mir_intern_type(Type::new(TypeKind::I32, None).into()); let array_len = 10; let array_def = ArrayDef { elem_ty: i32, len: array_len, }; - let array = db.mir_intern_type(Type::Array(array_def).into()); + let array = db.mir_intern_type(Type::new(TypeKind::Array(array_def), None).into()); let elem_size = array.array_elem_size(&db, 1); debug_assert_eq!(elem_size, 4); @@ -304,9 +410,9 @@ mod tests { #[test] fn test_aggregate_elem_array_type_info() { let db = NewDb::default(); - let i8 = db.mir_intern_type(Type::I8.into()); - let i64 = db.mir_intern_type(Type::I64.into()); - let i128 = db.mir_intern_type(Type::I128.into()); + let i8 = db.mir_intern_type(Type::new(TypeKind::I8, None).into()); + let i64 = db.mir_intern_type(Type::new(TypeKind::I64, None).into()); + let i128 = db.mir_intern_type(Type::new(TypeKind::I128, None).into()); let fields = vec![ ("".into(), i64), @@ -322,14 +428,14 @@ mod tests { span: Span::dummy(), module_id: ModuleId::from_raw_internal(0), }; - let aggregate = db.mir_intern_type(Type::Struct(struct_def).into()); + let aggregate = db.mir_intern_type(Type::new(TypeKind::Struct(struct_def), None).into()); let array_len = 10; let array_def = ArrayDef { elem_ty: aggregate, len: array_len, }; - let array = db.mir_intern_type(Type::Array(array_def).into()); + let array = db.mir_intern_type(Type::new(TypeKind::Array(array_def), None).into()); debug_assert_eq!(array.array_elem_size(&db, 1), 34); debug_assert_eq!(array.array_elem_size(&db, 32), 64); @@ -347,9 +453,9 @@ mod tests { #[test] fn test_primitive_elem_aggregate_type_info() { let db = NewDb::default(); - let i8 = db.mir_intern_type(Type::I8.into()); - let i64 = db.mir_intern_type(Type::I64.into()); - let i128 = db.mir_intern_type(Type::I128.into()); + let i8 = db.mir_intern_type(Type::new(TypeKind::I8, None).into()); + let i64 = db.mir_intern_type(Type::new(TypeKind::I64, None).into()); + let i128 = db.mir_intern_type(Type::new(TypeKind::I128, None).into()); let fields = vec![ ("".into(), i64), @@ -365,7 +471,7 @@ mod tests { span: Span::dummy(), module_id: ModuleId::from_raw_internal(0), }; - let aggregate = db.mir_intern_type(Type::Struct(struct_def).into()); + let aggregate = db.mir_intern_type(Type::new(TypeKind::Struct(struct_def), None).into()); debug_assert_eq!(aggregate.size_of(&db, 1), 34); debug_assert_eq!(aggregate.size_of(&db, 32), 49); @@ -384,9 +490,9 @@ mod tests { #[test] fn test_aggregate_elem_aggregate_type_info() { let db = NewDb::default(); - let i8 = db.mir_intern_type(Type::I8.into()); - let i64 = db.mir_intern_type(Type::I64.into()); - let i128 = db.mir_intern_type(Type::I128.into()); + let i8 = db.mir_intern_type(Type::new(TypeKind::I8, None).into()); + let i64 = db.mir_intern_type(Type::new(TypeKind::I64, None).into()); + let i128 = db.mir_intern_type(Type::new(TypeKind::I128, None).into()); let fields_inner = vec![ ("".into(), i64), @@ -402,7 +508,8 @@ mod tests { span: Span::dummy(), module_id: ModuleId::from_raw_internal(0), }; - let aggregate_inner = db.mir_intern_type(Type::Struct(struct_def_inner).into()); + let aggregate_inner = + db.mir_intern_type(Type::new(TypeKind::Struct(struct_def_inner), None).into()); let fields = vec![("".into(), i8), ("".into(), aggregate_inner)]; let struct_def = StructDef { @@ -411,7 +518,7 @@ mod tests { span: Span::dummy(), module_id: ModuleId::from_raw_internal(0), }; - let aggregate = db.mir_intern_type(Type::Struct(struct_def).into()); + let aggregate = db.mir_intern_type(Type::new(TypeKind::Struct(struct_def), None).into()); debug_assert_eq!(aggregate.size_of(&db, 1), 35); debug_assert_eq!(aggregate.size_of(&db, 32), 81); diff --git a/crates/mir/src/ir/body_builder.rs b/crates/mir/src/ir/body_builder.rs index 0a18d542e1..e8c6563e76 100644 --- a/crates/mir/src/ir/body_builder.rs +++ b/crates/mir/src/ir/body_builder.rs @@ -4,14 +4,13 @@ use num_bigint::BigInt; use crate::ir::{ body_cursor::{BodyCursor, CursorLocation}, inst::{BinOp, Inst, InstKind, UnOp}, - value::{Local, Temporary}, - BasicBlock, BasicBlockId, FunctionBody, FunctionId, SourceInfo, TypeId, ValueId, + value::{AssignableValue, Local}, + BasicBlock, BasicBlockId, FunctionBody, FunctionId, InstId, SourceInfo, TypeId, }; use super::{ inst::{CallType, YulIntrinsicOp}, - value::{self, Constant, Immediate}, - ConstantId, Value, + ConstantId, Value, ValueId, }; #[derive(Debug)] @@ -22,24 +21,18 @@ pub struct BodyBuilder { macro_rules! impl_unary_inst { ($name:ident, $code:path) => { - pub fn $name(&mut self, value: ValueId, result_ty: TypeId, source: SourceInfo) -> ValueId { + pub fn $name(&mut self, value: ValueId, source: SourceInfo) -> InstId { let inst = Inst::unary($code, value, source); - self.insert_inst(inst, Some(result_ty)).unwrap() + self.insert_inst(inst) } }; } macro_rules! impl_binary_inst { ($name:ident, $code:path) => { - pub fn $name( - &mut self, - lhs: ValueId, - rhs: ValueId, - result_ty: TypeId, - source: SourceInfo, - ) -> ValueId { + pub fn $name(&mut self, lhs: ValueId, rhs: ValueId, source: SourceInfo) -> InstId { let inst = Inst::binary($code, lhs, rhs, source); - self.insert_inst(inst, Some(result_ty)).unwrap() + self.insert_inst(inst) } }; } @@ -73,20 +66,20 @@ impl BodyBuilder { self.body.store.store_value(value.into()) } + pub fn map_result(&mut self, inst: InstId, result: AssignableValue) { + self.body.store.map_result(inst, result) + } + pub fn move_to_block(&mut self, block: BasicBlockId) { self.loc = CursorLocation::BlockBottom(block) } pub fn make_unit(&mut self, unit_ty: TypeId) -> ValueId { - self.body - .store - .store_value(Value::Unit(value::Unit { ty: unit_ty })) + self.body.store.store_value(Value::Unit { ty: unit_ty }) } pub fn make_imm(&mut self, imm: BigInt, ty: TypeId) -> ValueId { - self.body - .store - .store_value(Value::Immediate(Immediate { value: imm, ty })) + self.body.store.store_value(Value::Immediate { imm, ty }) } pub fn make_imm_from_bool(&mut self, imm: bool, ty: TypeId) -> ValueId { @@ -100,27 +93,21 @@ impl BodyBuilder { pub fn make_constant(&mut self, constant: ConstantId, ty: TypeId) -> ValueId { self.body .store - .store_value(Value::Constant(Constant { constant, ty })) + .store_value(Value::Constant { constant, ty }) } pub fn declare(&mut self, local: Local) -> ValueId { let source = local.source.clone(); - let local_id = self.body.store.store_value(local.into()); + let local_id = self.body.store.store_value(Value::Local(local)); let kind = InstKind::Declare { local: local_id }; let inst = Inst::new(kind, source); - self.insert_inst(inst, None); + self.insert_inst(inst); local_id } pub fn store_func_arg(&mut self, local: Local) -> ValueId { - self.body.store.store_value(local.into()) - } - - pub fn assign(&mut self, lhs: ValueId, rhs: ValueId, source: SourceInfo) { - let kind = InstKind::Assign { lhs, rhs }; - let inst = Inst::new(kind, source); - self.insert_inst(inst, None); + self.body.store.store_value(Value::Local(local)) } impl_unary_inst!(not, UnOp::Not); @@ -147,13 +134,13 @@ impl BodyBuilder { impl_binary_inst!(le, BinOp::Le); impl_binary_inst!(lt, BinOp::Lt); - pub fn cast(&mut self, value: ValueId, result_ty: TypeId, source: SourceInfo) -> ValueId { + pub fn cast(&mut self, value: ValueId, result_ty: TypeId, source: SourceInfo) -> InstId { let kind = InstKind::Cast { value, to: result_ty, }; let inst = Inst::new(kind, source); - self.insert_inst(inst, Some(result_ty)).unwrap() + self.insert_inst(inst) } pub fn aggregate_construct( @@ -161,34 +148,39 @@ impl BodyBuilder { ty: TypeId, args: Vec, source: SourceInfo, - ) -> ValueId { + ) -> InstId { let kind = InstKind::AggregateConstruct { ty, args }; let inst = Inst::new(kind, source); - self.insert_inst(inst, Some(ty)).unwrap() + self.insert_inst(inst) + } + + pub fn bind(&mut self, src: ValueId, source: SourceInfo) -> InstId { + let kind = InstKind::Bind { src }; + let inst = Inst::new(kind, source); + self.insert_inst(inst) + } + + pub fn mem_copy(&mut self, src: ValueId, source: SourceInfo) -> InstId { + let kind = InstKind::MemCopy { src }; + let inst = Inst::new(kind, source); + self.insert_inst(inst) } pub fn aggregate_access( &mut self, value: ValueId, indices: Vec, - result_ty: TypeId, source: SourceInfo, - ) -> ValueId { + ) -> InstId { let kind = InstKind::AggregateAccess { value, indices }; let inst = Inst::new(kind, source); - self.insert_inst(inst, Some(result_ty)).unwrap() + self.insert_inst(inst) } - pub fn map_access( - &mut self, - value: ValueId, - key: ValueId, - result_ty: TypeId, - source: SourceInfo, - ) -> ValueId { + pub fn map_access(&mut self, value: ValueId, key: ValueId, source: SourceInfo) -> InstId { let kind = InstKind::MapAccess { value, key }; let inst = Inst::new(kind, source); - self.insert_inst(inst, Some(result_ty)).unwrap() + self.insert_inst(inst) } pub fn call( @@ -196,52 +188,33 @@ impl BodyBuilder { func: FunctionId, args: Vec, call_type: CallType, - result_ty: TypeId, source: SourceInfo, - ) -> ValueId { + ) -> InstId { let kind = InstKind::Call { func, args, call_type, }; let inst = Inst::new(kind, source); - self.insert_inst(inst, Some(result_ty)).unwrap() + self.insert_inst(inst) } - pub fn keccak256(&mut self, arg: ValueId, result_ty: TypeId, source: SourceInfo) -> ValueId { + pub fn keccak256(&mut self, arg: ValueId, source: SourceInfo) -> InstId { let kind = InstKind::Keccak256 { arg }; let inst = Inst::new(kind, source); - self.insert_inst(inst, Some(result_ty)).unwrap() + self.insert_inst(inst) } - pub fn clone(&mut self, arg: ValueId, result_ty: TypeId, source: SourceInfo) -> ValueId { - let kind = InstKind::Clone { arg }; - let inst = Inst::new(kind, source); - self.insert_inst(inst, Some(result_ty)).unwrap() - } - - pub fn to_mem(&mut self, arg: ValueId, result_ty: TypeId, source: SourceInfo) -> ValueId { - let kind = InstKind::ToMem { arg }; - let inst = Inst::new(kind, source); - self.insert_inst(inst, Some(result_ty)).unwrap() - } - - pub fn abi_encode(&mut self, arg: ValueId, result_ty: TypeId, source: SourceInfo) -> ValueId { + pub fn abi_encode(&mut self, arg: ValueId, source: SourceInfo) -> InstId { let kind = InstKind::AbiEncode { arg }; let inst = Inst::new(kind, source); - self.insert_inst(inst, Some(result_ty)).unwrap() + self.insert_inst(inst) } - pub fn create( - &mut self, - value: ValueId, - contract: ContractId, - result_ty: TypeId, - source: SourceInfo, - ) -> ValueId { + pub fn create(&mut self, value: ValueId, contract: ContractId, source: SourceInfo) -> InstId { let kind = InstKind::Create { value, contract }; let inst = Inst::new(kind, source); - self.insert_inst(inst, Some(result_ty)).unwrap() + self.insert_inst(inst) } pub fn create2( @@ -249,33 +222,31 @@ impl BodyBuilder { value: ValueId, salt: ValueId, contract: ContractId, - result_ty: TypeId, source: SourceInfo, - ) -> ValueId { + ) -> InstId { let kind = InstKind::Create2 { value, salt, contract, }; let inst = Inst::new(kind, source); - self.insert_inst(inst, Some(result_ty)).unwrap() + self.insert_inst(inst) } pub fn yul_intrinsic( &mut self, op: YulIntrinsicOp, args: Vec, - result_ty: TypeId, source: SourceInfo, - ) -> ValueId { + ) -> InstId { let inst = Inst::intrinsic(op, args, source); - self.insert_inst(inst, Some(result_ty)).unwrap() + self.insert_inst(inst) } - pub fn jump(&mut self, dest: BasicBlockId, source: SourceInfo) { + pub fn jump(&mut self, dest: BasicBlockId, source: SourceInfo) -> InstId { let kind = InstKind::Jump { dest }; let inst = Inst::new(kind, source); - self.insert_inst(inst, None); + self.insert_inst(inst) } pub fn branch( @@ -284,28 +255,34 @@ impl BodyBuilder { then: BasicBlockId, else_: BasicBlockId, source: SourceInfo, - ) { + ) -> InstId { let kind = InstKind::Branch { cond, then, else_ }; let inst = Inst::new(kind, source); - self.insert_inst(inst, None); + self.insert_inst(inst) } - pub fn revert(&mut self, arg: ValueId, source: SourceInfo) { + pub fn revert(&mut self, arg: Option, source: SourceInfo) -> InstId { let kind = InstKind::Revert { arg }; let inst = Inst::new(kind, source); - self.insert_inst(inst, None); + self.insert_inst(inst) } - pub fn emit(&mut self, arg: ValueId, source: SourceInfo) { + pub fn emit(&mut self, arg: ValueId, source: SourceInfo) -> InstId { let kind = InstKind::Emit { arg }; let inst = Inst::new(kind, source); - self.insert_inst(inst, None); + self.insert_inst(inst) } - pub fn ret(&mut self, arg: ValueId, source: SourceInfo) { + pub fn ret(&mut self, arg: ValueId, source: SourceInfo) -> InstId { let kind = InstKind::Return { arg: arg.into() }; let inst = Inst::new(kind, source); - self.insert_inst(inst, None); + self.insert_inst(inst) + } + + pub fn nop(&mut self, source: SourceInfo) -> InstId { + let kind = InstKind::Nop; + let inst = Inst::new(kind, source); + self.insert_inst(inst) } pub fn value_ty(&mut self, value: ValueId) -> TypeId { @@ -325,32 +302,26 @@ impl BodyBuilder { self.cursor().expect_block() } - fn insert_inst(&mut self, inst: Inst, result_ty: Option) -> Option { - let block = self.current_block(); - if self.is_block_terminated(block) { - return None; + pub fn remove_inst(&mut self, inst: InstId) { + let mut cursor = BodyCursor::new(&mut self.body, CursorLocation::Inst(inst)); + if self.loc == cursor.loc() { + self.loc = cursor.prev_loc(); } + cursor.remove_inst(); + } + pub fn inst_data(&self, inst: InstId) -> &Inst { + self.body.store.inst_data(inst) + } + + fn insert_inst(&mut self, inst: Inst) -> InstId { let mut cursor = self.cursor(); let inst_id = cursor.store_and_insert_inst(inst); // Set cursor to the new inst. - let loc = CursorLocation::Inst(inst_id); - cursor.set_loc(loc); - - let result = if let Some(result_ty) = result_ty { - // Map a result value to the inst. - let temp = Temporary { - inst: inst_id, - ty: result_ty, - }; - Some(cursor.store_and_map_result(temp.into())) - } else { - None - }; + self.loc = CursorLocation::Inst(inst_id); - self.loc = loc; - result + inst_id } fn cursor(&mut self) -> BodyCursor { diff --git a/crates/mir/src/ir/body_cursor.rs b/crates/mir/src/ir/body_cursor.rs index 70615d697d..ed4199a345 100644 --- a/crates/mir/src/ir/body_cursor.rs +++ b/crates/mir/src/ir/body_cursor.rs @@ -2,7 +2,9 @@ //! in-place. // The design used here is greatly inspired by [`cranelift`](https://crates.io/crates/cranelift) -use super::{BasicBlock, BasicBlockId, FunctionBody, Inst, InstId, Value, ValueId}; +use super::{ + value::AssignableValue, BasicBlock, BasicBlockId, FunctionBody, Inst, InstId, ValueId, +}; /// Specify a current location of [`BodyCursor`] #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -197,11 +199,11 @@ impl<'a> BodyCursor<'a> { block_id } - pub fn store_and_map_result(&mut self, result: Value) -> ValueId { + pub fn map_result(&mut self, result: AssignableValue) -> Option { let inst = self.expect_inst(); - let result_id = self.body.store.store_value(result); - self.body.store.map_result(inst, result_id); - result_id + let result_value = result.value_id(); + self.body.store.map_result(inst, result); + result_value } /// Returns current inst that cursor points. diff --git a/crates/mir/src/ir/function.rs b/crates/mir/src/ir/function.rs index 760dfc854b..1943b8345f 100644 --- a/crates/mir/src/ir/function.rs +++ b/crates/mir/src/ir/function.rs @@ -2,14 +2,17 @@ use fe_analyzer::namespace::items as analyzer_items; use fe_common::impl_intern_key; use fxhash::FxHashMap; use id_arena::Arena; +use num_bigint::BigInt; use smol_str::SmolStr; +use crate::db::MirDb; + use super::{ basic_block::BasicBlock, body_order::BodyOrder, inst::{BranchInfo, Inst, InstId, InstKind}, - types::TypeId, - value::{Immediate, Local, Value, ValueId}, + types::{TypeId, TypeKind}, + value::{AssignableValue, Local, Value, ValueId}, BasicBlockId, SourceInfo, }; @@ -31,7 +34,7 @@ pub struct FunctionParam { pub source: SourceInfo, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct FunctionId(pub u32); impl_intern_key!(FunctionId); @@ -49,6 +52,12 @@ pub enum Linkage { Export, } +impl Linkage { + pub fn is_exported(self) -> bool { + self == Linkage::Export + } +} + /// A function body, which is not stored in salsa db to enable in-place /// transformation. #[derive(Debug, Clone, PartialEq, Eq)] @@ -90,12 +99,12 @@ pub struct BodyDataStore { /// Maps an immediate to a value to ensure the same immediate results in the /// same value. - immediates: FxHashMap, + immediates: FxHashMap<(BigInt, TypeId), ValueId>, unit_value: Option, /// Maps an instruction to a value. - inst_results: FxHashMap, + inst_results: FxHashMap, /// All declared local variables in a function. locals: Vec, @@ -121,9 +130,9 @@ impl BodyDataStore { pub fn store_value(&mut self, value: Value) -> ValueId { match value { - Value::Immediate(imm) => self.store_immediate(imm), + Value::Immediate { imm, ty } => self.store_immediate(imm, ty), - Value::Unit(_) => { + Value::Unit { .. } => { if let Some(unit_value) = self.unit_value { unit_value } else { @@ -146,6 +155,10 @@ impl BodyDataStore { } } + pub fn is_nop(&self, inst: InstId) -> bool { + matches!(&self.inst_data(inst).kind, InstKind::Nop) + } + pub fn is_terminator(&self, inst: InstId) -> bool { self.inst_data(inst).is_terminator() } @@ -158,14 +171,26 @@ impl BodyDataStore { &self.values[value] } + pub fn value_data_mut(&mut self, value: ValueId) -> &mut Value { + &mut self.values[value] + } + + pub fn values(&self) -> impl Iterator { + self.values.iter().map(|(_, value_data)| value_data) + } + + pub fn values_mut(&mut self) -> impl Iterator { + self.values.iter_mut().map(|(_, value_data)| value_data) + } + pub fn store_block(&mut self, block: BasicBlock) -> BasicBlockId { self.blocks.alloc(block) } /// Returns an instruction result. A returned value is guaranteed to be a /// temporary value. - pub fn inst_result(&self, inst: InstId) -> Option { - self.inst_results.get(&inst).copied() + pub fn inst_result(&self, inst: InstId) -> Option<&AssignableValue> { + self.inst_results.get(&inst) } pub fn rewrite_branch_dest(&mut self, inst: InstId, from: BasicBlockId, to: BasicBlockId) { @@ -195,7 +220,24 @@ impl BodyDataStore { self.values[vid].ty() } - pub fn map_result(&mut self, inst: InstId, result: ValueId) { + pub fn assignable_value_ty(&self, db: &dyn MirDb, value: &AssignableValue) -> TypeId { + match value { + AssignableValue::Value(value) => self.value_ty(*value), + AssignableValue::Aggregate { lhs, idx } => { + let lhs = self.assignable_value_ty(db, lhs).deref(db); + lhs.projection_ty(db, self.value_data(*idx)) + } + AssignableValue::Map { lhs, .. } => { + let lhs = self.assignable_value_ty(db, lhs).deref(db); + match &lhs.data(db).kind { + TypeKind::Map(def) => def.value_ty, + _ => unreachable!(), + } + } + } + } + + pub fn map_result(&mut self, inst: InstId, result: AssignableValue) { self.inst_results.insert(inst, result); } @@ -203,6 +245,27 @@ impl BodyDataStore { &self.locals } + pub fn locals_mut(&mut self) -> &[ValueId] { + &mut self.locals + } + + pub fn func_args(&self) -> impl Iterator + '_ { + self.locals() + .iter() + .filter(|value| match self.value_data(**value) { + Value::Local(local) => local.is_arg, + _ => unreachable!(), + }) + .copied() + } + + pub fn func_args_mut(&mut self) -> impl Iterator { + self.values_mut().filter(|value| match value { + Value::Local(local) => local.is_arg, + _ => false, + }) + } + /// Returns Some(`local_name`) if value is `Value::Local`. pub fn local_name(&self, value: ValueId) -> Option<&str> { match self.value_data(value) { @@ -211,12 +274,19 @@ impl BodyDataStore { } } - fn store_immediate(&mut self, imm: Immediate) -> ValueId { - if let Some(value) = self.immediates.get(&imm) { + pub fn replace_value(&mut self, value: ValueId, to: Value) -> Value { + std::mem::replace(&mut self.values[value], to) + } + + fn store_immediate(&mut self, imm: BigInt, ty: TypeId) -> ValueId { + if let Some(value) = self.immediates.get(&(imm.clone(), ty)) { *value } else { - let id = self.values.alloc(Value::Immediate(imm.clone())); - self.immediates.insert(imm, id); + let id = self.values.alloc(Value::Immediate { + imm: imm.clone(), + ty, + }); + self.immediates.insert((imm, ty), id); id } } diff --git a/crates/mir/src/ir/inst.rs b/crates/mir/src/ir/inst.rs index e8ad3eed65..5aeb085195 100644 --- a/crates/mir/src/ir/inst.rs +++ b/crates/mir/src/ir/inst.rs @@ -21,11 +21,6 @@ pub enum InstKind { local: ValueId, }, - Assign { - lhs: ValueId, - rhs: ValueId, - }, - /// Unary instruction. Unary { op: UnOp, @@ -50,6 +45,14 @@ pub enum InstKind { args: Vec, }, + Bind { + src: ValueId, + }, + + MemCopy { + src: ValueId, + }, + /// Access to aggregate fields or elements. /// # Example /// @@ -65,8 +68,8 @@ pub enum InstKind { }, MapAccess { - value: ValueId, key: ValueId, + value: ValueId, }, Call { @@ -88,7 +91,7 @@ pub enum InstKind { }, Revert { - arg: ValueId, + arg: Option, }, Emit { @@ -103,14 +106,6 @@ pub enum InstKind { arg: ValueId, }, - Clone { - arg: ValueId, - }, - - ToMem { - arg: ValueId, - }, - AbiEncode { arg: ValueId, }, @@ -179,6 +174,84 @@ impl Inst { _ => BranchInfo::NotBranch, } } + + pub fn args(&self) -> ArgIter { + use InstKind::*; + match &self.kind { + Declare { local: arg } + | Bind { src: arg } + | MemCopy { src: arg } + | Unary { value: arg, .. } + | Cast { value: arg, .. } + | Emit { arg } + | Keccak256 { arg } + | AbiEncode { arg } + | Create { value: arg, .. } + | Branch { cond: arg, .. } => ArgIter::One(Some(*arg)), + + Binary { lhs, rhs, .. } + | MapAccess { + value: lhs, + key: rhs, + } + | Create2 { + value: lhs, + salt: rhs, + .. + } => ArgIter::One(Some(*lhs)).chain(ArgIter::One(Some(*rhs))), + + Revert { arg } | Return { arg } => ArgIter::One(*arg), + + Nop | Jump { .. } => ArgIter::Zero, + + AggregateAccess { value, indices } => { + ArgIter::One(Some(*value)).chain(ArgIter::Slice(indices.iter())) + } + + AggregateConstruct { args, .. } | Call { args, .. } | YulIntrinsic { args, .. } => { + ArgIter::Slice(args.iter()) + } + } + } + + pub fn args_mut(&mut self) -> ArgMutIter { + use InstKind::*; + match &mut self.kind { + Declare { local: arg } + | Bind { src: arg } + | MemCopy { src: arg } + | Unary { value: arg, .. } + | Cast { value: arg, .. } + | Emit { arg } + | Keccak256 { arg } + | AbiEncode { arg } + | Create { value: arg, .. } + | Branch { cond: arg, .. } => ArgMutIter::One(Some(arg)), + + Binary { lhs, rhs, .. } + | MapAccess { + value: lhs, + key: rhs, + } + | Create2 { + value: lhs, + salt: rhs, + .. + } => ArgMutIter::One(Some(lhs)).chain(ArgMutIter::One(Some(rhs))), + + Revert { arg } | Return { arg } => ArgMutIter::One(arg.as_mut()), + + Nop | Jump { .. } => ArgMutIter::Zero, + + AggregateAccess { value, indices } => { + ArgMutIter::One(Some(value)).chain(ArgMutIter::Slice(indices.iter_mut())) + } + + AggregateConstruct { args, .. } | Call { args, .. } | YulIntrinsic { args, .. } => { + ArgMutIter::Slice(args.iter_mut()) + } + } + } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -528,3 +601,69 @@ pub enum BranchInfo { Jump(BasicBlockId), Branch(ValueId, BasicBlockId, BasicBlockId), } + +#[derive(Debug)] +pub enum ArgIter<'a> { + Zero, + One(Option), + Slice(std::slice::Iter<'a, ValueId>), + Chain(Box>, Box>), +} + +impl<'a> ArgIter<'a> { + fn chain(self, rhs: Self) -> Self { + Self::Chain(self.into(), rhs.into()) + } +} + +impl<'a> Iterator for ArgIter<'a> { + type Item = ValueId; + + fn next(&mut self) -> Option { + match self { + Self::Zero => None, + Self::One(value) => value.take(), + Self::Slice(s) => s.next().copied(), + Self::Chain(first, second) => { + if let Some(value) = first.next() { + Some(value) + } else { + second.next() + } + } + } + } +} + +#[derive(Debug)] +pub enum ArgMutIter<'a> { + Zero, + One(Option<&'a mut ValueId>), + Slice(std::slice::IterMut<'a, ValueId>), + Chain(Box>, Box>), +} + +impl<'a> ArgMutIter<'a> { + fn chain(self, rhs: Self) -> Self { + Self::Chain(self.into(), rhs.into()) + } +} + +impl<'a> Iterator for ArgMutIter<'a> { + type Item = &'a mut ValueId; + + fn next(&mut self) -> Option { + match self { + Self::Zero => None, + Self::One(value) => value.take(), + Self::Slice(s) => s.next(), + Self::Chain(first, second) => { + if let Some(value) = first.next() { + Some(value) + } else { + second.next() + } + } + } + } +} diff --git a/crates/mir/src/ir/mod.rs b/crates/mir/src/ir/mod.rs index af4ca0801f..f4df0b5cf7 100644 --- a/crates/mir/src/ir/mod.rs +++ b/crates/mir/src/ir/mod.rs @@ -15,7 +15,7 @@ pub use basic_block::{BasicBlock, BasicBlockId}; pub use constant::{Constant, ConstantId}; pub use function::{FunctionBody, FunctionId, FunctionParam, FunctionSignature}; pub use inst::{Inst, InstId}; -pub use types::{Type, TypeId}; +pub use types::{Type, TypeId, TypeKind}; pub use value::{Value, ValueId}; /// An original source information that indicates where `mir` entities derive diff --git a/crates/mir/src/ir/types.rs b/crates/mir/src/ir/types.rs index c300e0be31..2c77d197eb 100644 --- a/crates/mir/src/ir/types.rs +++ b/crates/mir/src/ir/types.rs @@ -1,9 +1,25 @@ use fe_analyzer::namespace::items as analyzer_items; +use fe_analyzer::namespace::types as analyzer_types; use fe_common::{impl_intern_key, Span}; use smol_str::SmolStr; #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum Type { +pub struct Type { + pub kind: TypeKind, + pub analyzer_ty: Option, +} + +impl Type { + pub fn new(kind: TypeKind, analyzer_type: Option) -> Self { + Self { + kind, + analyzer_ty: analyzer_type, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum TypeKind { I8, I16, I32, @@ -20,11 +36,15 @@ pub enum Type { Address, Unit, Array(ArrayDef), + // TODO: we should consider whether we really need `String` type. + String(usize), Tuple(TupleDef), Struct(StructDef), Event(EventDef), Contract(StructDef), Map(MapDef), + MPtr(TypeId), + SPtr(TypeId), } /// An interned Id for [`ArrayDef`]. diff --git a/crates/mir/src/ir/value.rs b/crates/mir/src/ir/value.rs index c869dd3949..cdb2d69a8b 100644 --- a/crates/mir/src/ir/value.rs +++ b/crates/mir/src/ir/value.rs @@ -2,69 +2,91 @@ use id_arena::Id; use num_bigint::BigInt; use smol_str::SmolStr; -use super::{constant::ConstantId, inst::InstId, types::TypeId, SourceInfo}; +use crate::db::MirDb; + +use super::{ + constant::ConstantId, + function::BodyDataStore, + inst::InstId, + types::{TypeId, TypeKind}, + SourceInfo, +}; pub type ValueId = Id; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Value { /// A value resulted from an instruction. - Temporary(Temporary), + Temporary { inst: InstId, ty: TypeId }, /// A local variable declared in a function body. Local(Local), /// An immediate value. - Immediate(Immediate), + Immediate { imm: BigInt, ty: TypeId }, /// A constant value. - Constant(Constant), + Constant { constant: ConstantId, ty: TypeId }, /// A singleton value representing `Unit` type. - Unit(Unit), + Unit { ty: TypeId }, } impl Value { pub fn ty(&self) -> TypeId { match self { - Self::Temporary(val) => val.ty, Self::Local(val) => val.ty, - Self::Immediate(val) => val.ty, - Self::Constant(val) => val.ty, - Self::Unit(val) => val.ty, + Self::Immediate { ty, .. } + | Self::Temporary { ty, .. } + | Self::Unit { ty } + | Self::Constant { ty, .. } => *ty, } } } -macro_rules! embed { - ($(($variant: expr, $ty: ty)),*) => { - $( - impl From<$ty> for Value { - fn from(val: $ty) -> Self { - $variant(val) - } - })* - }; +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum AssignableValue { + Value(ValueId), + Aggregate { + lhs: Box, + idx: ValueId, + }, + Map { + lhs: Box, + key: ValueId, + }, } -embed! { - (Value::Temporary, Temporary), - (Value::Local, Local), - (Value::Immediate, Immediate), - (Value::Constant, Constant), - (Value::Unit, Unit) +impl From for AssignableValue { + fn from(value: ValueId) -> Self { + Self::Value(value) + } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct Temporary { - pub inst: InstId, - pub ty: TypeId, -} +impl AssignableValue { + pub fn ty(&self, db: &dyn MirDb, store: &BodyDataStore) -> TypeId { + match self { + Self::Value(value) => store.value_ty(*value), + Self::Aggregate { lhs, idx } => { + let lhs_ty = lhs.ty(db, store); + lhs_ty.projection_ty(db, store.value_data(*idx)) + } + Self::Map { lhs, .. } => { + let lhs_ty = lhs.ty(db, store); + match lhs_ty.data(db).kind { + TypeKind::Map(def) => def.value_ty, + _ => unreachable!(), + } + } + } + } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct Immediate { - pub value: BigInt, - pub ty: TypeId, + pub fn value_id(&self) -> Option { + match self { + Self::Value(value) => Some(*value), + _ => None, + } + } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -114,14 +136,3 @@ impl Local { } } } - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct Constant { - pub constant: ConstantId, - pub ty: TypeId, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct Unit { - pub ty: TypeId, -} diff --git a/crates/mir/src/lower/function.rs b/crates/mir/src/lower/function.rs index 0fa08967ec..d0b743cbe7 100644 --- a/crates/mir/src/lower/function.rs +++ b/crates/mir/src/lower/function.rs @@ -1,22 +1,27 @@ -use std::{rc::Rc, str::FromStr}; +use std::rc::Rc; use fe_analyzer::{ builtins::{ContractTypeMethod, GlobalFunction, ValueMethod}, context::CallType as AnalyzerCallType, namespace::{items as analyzer_items, types as analyzer_types}, }; +use fe_common::numeric::Literal; use fe_parser::{ast, node::Node}; use fxhash::FxHashMap; use id_arena::{Arena, Id}; -use num_bigint::BigInt; use smol_str::SmolStr; use crate::{ db::MirDb, ir::{ - self, body_builder::BodyBuilder, constant::ConstantValue, function::Linkage, - inst::CallType, value::Local, BasicBlockId, Constant, FunctionBody, FunctionId, - FunctionParam, FunctionSignature, SourceInfo, TypeId, ValueId, + self, + body_builder::BodyBuilder, + constant::ConstantValue, + function::Linkage, + inst::{CallType, InstKind}, + value::{AssignableValue, Local}, + BasicBlockId, Constant, FunctionBody, FunctionId, FunctionParam, FunctionSignature, InstId, + SourceInfo, TypeId, Value, ValueId, }, }; @@ -40,7 +45,7 @@ pub fn lower_func_signature(db: &dyn MirDb, func: analyzer_items::FunctionId) -> let return_type = db.mir_lowered_type(analyzer_signature.return_type.clone().unwrap()); let linkage = if func.is_public(db.upcast()) { - if has_self { + if func.is_contract_func(db.upcast()) && !func.is_constructor(db.upcast()) { Linkage::Export } else { Linkage::Public @@ -122,7 +127,7 @@ impl<'db, 'a> BodyLowerHelper<'db, 'a> { match &stmt.kind { ast::FuncStmt::Return { value } => { let value = if let Some(expr) = value { - self.lower_expr(expr) + self.lower_expr_to_value(expr) } else { self.make_unit() }; @@ -132,23 +137,7 @@ impl<'db, 'a> BodyLowerHelper<'db, 'a> { } ast::FuncStmt::VarDecl { target, value, .. } => { - // Declare variables. - let locals = self.lower_var_decl(target); - - // Lower an assignment statement in variable declaration. - if let Some(expr) = value { - let mut rhs = self.lower_expr(expr); - let ty = self.builder.value_ty(rhs); - for (local, indices) in locals { - for index in indices { - let ty = ty.projection_ty(self.db, self.builder.value_data(rhs)); - rhs = self - .builder - .aggregate_access(rhs, vec![index], ty, expr.into()); - } - self.builder.assign(local, rhs, stmt.into()); - } - } + self.lower_var_decl(target, value.as_ref(), stmt.into()); } ast::FuncStmt::ConstantDecl { name, value, .. } => { @@ -167,18 +156,18 @@ impl<'db, 'a> BodyLowerHelper<'db, 'a> { } ast::FuncStmt::Assign { target, value } => { - let lhs = self.lower_expr(target); - let rhs = self.lower_expr(value); - self.builder.assign(lhs, rhs, stmt.into()); + let result = self.lower_assignable_value(target); + let expr = self.lower_expr(value); + self.builder.map_result(expr, result) } ast::FuncStmt::AugAssign { target, op, value } => { - let lhs = self.lower_expr(target); - let rhs = self.lower_expr(value); - let ty = self.expr_ty(target); + let result = self.lower_assignable_value(target); + let lhs = self.lower_expr_to_value(target); + let rhs = self.lower_expr_to_value(value); - let tmp = self.lower_binop(op.kind, lhs, rhs, ty, stmt.into()); - self.builder.assign(lhs, tmp, stmt.into()); + let inst = self.lower_binop(op.kind, lhs, rhs, stmt.into()); + self.builder.map_result(inst, result) } ast::FuncStmt::For { target, iter, body } => self.lower_for_loop(target, iter, body), @@ -187,7 +176,7 @@ impl<'db, 'a> BodyLowerHelper<'db, 'a> { let header_bb = self.builder.make_block(); let exit_bb = self.builder.make_block(); - let cond = self.lower_expr(test); + let cond = self.lower_expr_to_value(test); self.builder .branch(cond, header_bb, exit_bb, SourceInfo::dummy()); @@ -197,7 +186,7 @@ impl<'db, 'a> BodyLowerHelper<'db, 'a> { for stmt in body { self.lower_stmt(stmt); } - let cond = self.lower_expr(test); + let cond = self.lower_expr_to_value(test); self.builder .branch(cond, header_bb, exit_bb, SourceInfo::dummy()); @@ -217,18 +206,20 @@ impl<'db, 'a> BodyLowerHelper<'db, 'a> { let then_bb = self.builder.make_block(); let false_bb = self.builder.make_block(); - let cond = self.lower_expr(test); + let cond = self.lower_expr_to_value(test); self.builder .branch(cond, then_bb, false_bb, SourceInfo::dummy()); self.builder.move_to_block(false_bb); - let msg = if let Some(msg) = msg { - self.lower_expr(msg) - } else { - self.make_unit() - }; - self.builder.revert(msg, stmt.into()); + let msg = match msg { + Some(msg) => self.lower_expr_to_value(msg), + None => { + let u256_ty = self.u256_ty(); + self.builder.make_imm(1.into(), u256_ty) + } + }; + self.builder.revert(Some(msg), stmt.into()); self.builder.move_to_block(then_bb); } @@ -240,23 +231,25 @@ impl<'db, 'a> BodyLowerHelper<'db, 'a> { let lowered_args = args .kind .iter() - .map(|arg| self.lower_expr(&arg.kind.value)) + .map(|arg| self.lower_expr_to_value(&arg.kind.value)) .collect(); - let emit_arg = - self.builder - .aggregate_construct(event_type, lowered_args, args.into()); - self.builder.emit(emit_arg, stmt.into()); + let event = Local::tmp_local("$event".into(), event_type); + let event = self.builder.declare(event); + let inst = self + .builder + .aggregate_construct(event_type, lowered_args, args.into()); + self.builder.map_result(inst, event.into()); + self.builder.emit(event, stmt.into()); } ast::FuncStmt::Expr { value } => { - self.lower_expr(value); + self.lower_expr_to_value(value); } ast::FuncStmt::Pass => { // TODO: Generate appropriate error message. - let arg = self.make_unit(); - self.builder.revert(arg, stmt.into()); + self.builder.revert(None, stmt.into()); let next_block = self.builder.make_block(); self.builder.move_to_block(next_block); } @@ -273,30 +266,20 @@ impl<'db, 'a> BodyLowerHelper<'db, 'a> { if let Some(loop_idx) = self.scope().loop_idx(&self.scopes) { let u256_ty = self.u256_ty(); let imm_one = self.builder.make_imm(1u32.into(), u256_ty); - let inc = self - .builder - .add(loop_idx, imm_one, u256_ty, SourceInfo::dummy()); - self.builder.assign(loop_idx, inc, SourceInfo::dummy()); + let inc = self.builder.add(loop_idx, imm_one, SourceInfo::dummy()); + self.builder.map_result(inc, loop_idx.into()); let maximum_iter_count = self.scope().maximum_iter_count(&self.scopes).unwrap(); - let cond = self - .builder - .eq(loop_idx, maximum_iter_count, u256_ty, stmt.into()); let exit = self.scope().loop_exit(&self.scopes); - self.builder.branch(cond, exit, entry, stmt.into()); + self.branch_eq(loop_idx, maximum_iter_count, exit, entry, stmt.into()); } else { - self.builder.jump(entry, stmt.into()) + self.builder.jump(entry, stmt.into()); } let next_block = self.builder.make_block(); self.builder.move_to_block(next_block); } ast::FuncStmt::Revert { error } => { - let error = if let Some(error) = error { - self.lower_expr(error) - } else { - self.make_unit() - }; - + let error = error.as_ref().map(|err| self.lower_expr_to_value(err)); self.builder.revert(error, stmt.into()); let next_block = self.builder.make_block(); self.builder.move_to_block(next_block); @@ -312,7 +295,26 @@ impl<'db, 'a> BodyLowerHelper<'db, 'a> { } } - fn lower_var_decl(&mut self, var: &Node) -> Vec<(ValueId, Vec)> { + fn branch_eq( + &mut self, + v1: ValueId, + v2: ValueId, + true_bb: BasicBlockId, + false_bb: BasicBlockId, + source: SourceInfo, + ) { + let cond = self.builder.eq(v1, v2, source.clone()); + let bool_ty = self.bool_ty(); + let cond = self.map_to_tmp(cond, bool_ty); + self.builder.branch(cond, true_bb, false_bb, source); + } + + fn lower_var_decl( + &mut self, + var: &Node, + init: Option<&Node>, + source: SourceInfo, + ) { match &var.kind { ast::VarDeclTarget::Name(name) => { let ty = self @@ -322,24 +324,64 @@ impl<'db, 'a> BodyLowerHelper<'db, 'a> { let value = self.builder.declare(local); self.scope_mut().declare_var(name, value); + if let Some(init) = init { + let rhs = self.lower_expr(init); + self.builder.map_result(rhs, value.into()); + } + } - vec![(value, vec![])] + ast::VarDeclTarget::Tuple(decls) => { + if let Some(init) = init { + if let ast::Expr::Tuple { elts } = &init.kind { + debug_assert_eq!(decls.len(), elts.len()); + for (decl, init_elem) in decls.iter().zip(elts.iter()) { + self.lower_var_decl(decl, Some(init_elem), source.clone()); + } + } else { + let init_ty = self.expr_ty(init); + let init_value = self.lower_expr_to_value(init); + self.lower_var_decl_unpack(var, init_value, init_ty, source); + }; + } else { + for decl in decls { + self.lower_var_decl(decl, None, source.clone()) + } + } + } + } + } + + fn lower_var_decl_unpack( + &mut self, + var: &Node, + init: ValueId, + init_ty: TypeId, + source: SourceInfo, + ) { + match &var.kind { + ast::VarDeclTarget::Name(name) => { + let ty = self + .db + .mir_lowered_type(self.analyzer_body.var_types[&var.id].clone()); + let local = Local::user_local(name.clone(), ty, var.into()); + + let lhs = self.builder.declare(local); + self.scope_mut().declare_var(name, lhs); + let bind = self.builder.bind(init, source); + self.builder.map_result(bind, lhs.into()); } ast::VarDeclTarget::Tuple(decls) => { - let mut result = vec![]; - for (i, var) in decls.iter().enumerate() { + for (index, decl) in decls.iter().enumerate() { + let elem_ty = init_ty.projection_ty_imm(self.db, index); let u256_ty = self.u256_ty(); - let index = self.builder.make_imm(i.into(), u256_ty); - self.lower_var_decl(var) - .into_iter() - .for_each(|(local, mut local_indices)| { - let mut indices = vec![index]; - indices.append(&mut local_indices); - result.push((local, indices)) - }) + let index_value = self.builder.make_imm(index.into(), u256_ty); + let elem_inst = + self.builder + .aggregate_access(init, vec![index_value], source.clone()); + let elem_value = self.map_to_tmp(elem_inst, elem_ty); + self.lower_var_decl_unpack(decl, elem_value, elem_ty, source.clone()) } - result } } } @@ -350,7 +392,7 @@ impl<'db, 'a> BodyLowerHelper<'db, 'a> { then: &[Node], else_: &[Node], ) { - let cond = self.lower_expr(cond); + let cond = self.lower_expr_to_value(cond); if else_.is_empty() { let then_bb = self.builder.make_block(); @@ -394,10 +436,14 @@ impl<'db, 'a> BodyLowerHelper<'db, 'a> { let else_block_end_bb = self.builder.current_block(); let merge_bb = self.builder.make_block(); - self.builder.move_to_block(then_block_end_bb); - self.builder.jump(merge_bb, SourceInfo::dummy()); - self.builder.move_to_block(else_block_end_bb); - self.builder.jump(merge_bb, SourceInfo::dummy()); + if !self.builder.is_block_terminated(then_block_end_bb) { + self.builder.move_to_block(then_block_end_bb); + self.builder.jump(merge_bb, SourceInfo::dummy()); + } + if !self.builder.is_block_terminated(else_block_end_bb) { + self.builder.move_to_block(else_block_end_bb); + self.builder.jump(merge_bb, SourceInfo::dummy()); + } self.builder.move_to_block(merge_bb); } } @@ -438,23 +484,26 @@ impl<'db, 'a> BodyLowerHelper<'db, 'a> { let loop_idx = Local::tmp_local("$loop_idx_tmp".into(), u256_ty); let loop_idx = self.builder.declare(loop_idx); let imm_zero = self.builder.make_imm(0u32.into(), u256_ty); - self.builder.assign(loop_idx, imm_zero, SourceInfo::dummy()); + let imm_zero = self.builder.bind(imm_zero, SourceInfo::dummy()); + self.builder.map_result(imm_zero, loop_idx.into()); // Evaluates loop variable. - let iter = self.lower_expr(iter); + let iter_ty = self.expr_ty(iter); + let iter = self.lower_expr_to_value(iter); // Create maximum loop count. - let iter_ty = self.builder.value_ty(iter); - let maximum_iter_count = match iter_ty.data(self.db).as_ref() { - ir::Type::Array(ir::types::ArrayDef { len, .. }) => *len, + let maximum_iter_count = match &iter_ty.deref(self.db).data(self.db).kind { + ir::TypeKind::Array(ir::types::ArrayDef { len, .. }) => *len, _ => unreachable!(), }; let maximum_iter_count = self.builder.make_imm(maximum_iter_count.into(), u256_ty); - let cond = self - .builder - .eq(loop_idx, maximum_iter_count, u256_ty, SourceInfo::dummy()); - self.builder - .branch(cond, exit_bb, entry_bb, SourceInfo::dummy()); + self.branch_eq( + loop_idx, + maximum_iter_count, + exit_bb, + entry_bb, + SourceInfo::dummy(), + ); self.scope_mut().loop_idx = Some(loop_idx); self.scope_mut().maximum_iter_count = Some(maximum_iter_count); @@ -462,11 +511,11 @@ impl<'db, 'a> BodyLowerHelper<'db, 'a> { self.builder.move_to_block(entry_bb); // loop_variable = array[loop_idx] - let iter_elem = - self.builder - .aggregate_access(iter, vec![loop_idx], iter_elem_ty, SourceInfo::dummy()); + let iter_elem = self + .builder + .aggregate_access(iter, vec![loop_idx], SourceInfo::dummy()); self.builder - .assign(loop_value, iter_elem, SourceInfo::dummy()); + .map_result(iter_elem, AssignableValue::Value(loop_value)); for stmt in body { self.lower_stmt(stmt); @@ -474,22 +523,23 @@ impl<'db, 'a> BodyLowerHelper<'db, 'a> { // loop_idx += 1 let imm_one = self.builder.make_imm(1u32.into(), u256_ty); - let inc = self - .builder - .add(loop_idx, imm_one, u256_ty, SourceInfo::dummy()); - self.builder.assign(loop_idx, inc, SourceInfo::dummy()); - let cond = self - .builder - .eq(loop_idx, maximum_iter_count, u256_ty, SourceInfo::dummy()); + let inc = self.builder.add(loop_idx, imm_one, SourceInfo::dummy()); self.builder - .branch(cond, exit_bb, entry_bb, SourceInfo::dummy()); + .map_result(inc, AssignableValue::Value(loop_idx)); + self.branch_eq( + loop_idx, + maximum_iter_count, + exit_bb, + entry_bb, + SourceInfo::dummy(), + ); /* Move to exit bb */ self.exit_scope(); self.builder.move_to_block(exit_bb); } - fn lower_expr(&mut self, expr: &Node) -> ValueId { + fn lower_expr(&mut self, expr: &Node) -> InstId { let ty = self.expr_ty(expr); match &expr.kind { ast::Expr::Ternary { @@ -505,22 +555,22 @@ impl<'db, 'a> BodyLowerHelper<'db, 'a> { .builder .declare(Local::tmp_local("$ternary_tmp".into(), ty)); - let cond = self.lower_expr(test); + let cond = self.lower_expr_to_value(test); self.builder .branch(cond, true_bb, false_bb, SourceInfo::dummy()); self.builder.move_to_block(true_bb); let value = self.lower_expr(if_expr); - self.builder.assign(tmp, value, SourceInfo::dummy()); + self.builder.map_result(value, tmp.into()); self.builder.jump(merge_bb, SourceInfo::dummy()); self.builder.move_to_block(false_bb); let value = self.lower_expr(else_expr); - self.builder.assign(tmp, value, SourceInfo::dummy()); + self.builder.map_result(value, tmp.into()); self.builder.jump(merge_bb, SourceInfo::dummy()); self.builder.move_to_block(merge_bb); - tmp + self.builder.bind(tmp, SourceInfo::dummy()) } ast::Expr::BoolOperation { left, op, right } => { @@ -528,47 +578,41 @@ impl<'db, 'a> BodyLowerHelper<'db, 'a> { } ast::Expr::BinOperation { left, op, right } => { - let lhs = self.lower_expr(left); - let rhs = self.lower_expr(right); - self.lower_binop(op.kind, lhs, rhs, ty, expr.into()) + let lhs = self.lower_expr_to_value(left); + let rhs = self.lower_expr_to_value(right); + self.lower_binop(op.kind, lhs, rhs, expr.into()) } ast::Expr::UnaryOperation { op, operand } => { - let value = self.lower_expr(operand); + let value = self.lower_expr_to_value(operand); match op.kind { - ast::UnaryOperator::Invert => self.builder.inv(value, ty, expr.into()), - ast::UnaryOperator::Not => self.builder.not(value, ty, expr.into()), - ast::UnaryOperator::USub => self.builder.neg(value, ty, expr.into()), + ast::UnaryOperator::Invert => self.builder.inv(value, expr.into()), + ast::UnaryOperator::Not => self.builder.not(value, expr.into()), + ast::UnaryOperator::USub => self.builder.neg(value, expr.into()), } } ast::Expr::CompOperation { left, op, right } => { - let lhs = self.lower_expr(left); - let rhs = self.lower_expr(right); - self.lower_comp_op(op.kind, lhs, rhs, ty, expr.into()) + let lhs = self.lower_expr_to_value(left); + let rhs = self.lower_expr_to_value(right); + self.lower_comp_op(op.kind, lhs, rhs, expr.into()) } ast::Expr::Attribute { .. } => { let mut indices = vec![]; let value = self.lower_aggregate_access(expr, &mut indices); - let ty = self.expr_ty(expr); - self.builder - .aggregate_access(value, indices, ty, expr.into()) + self.builder.aggregate_access(value, indices, expr.into()) } ast::Expr::Subscript { value, index } => { - let result_ty = self.expr_ty(expr); - if !self.expr_ty(value).is_aggregate(self.db) { - // Indices is empty is the `expr` is map - let value = self.lower_expr(value); - let key = self.lower_expr(index); - self.builder.map_access(value, key, result_ty, expr.into()) + let value = self.lower_expr_to_value(value); + let key = self.lower_expr_to_value(index); + self.builder.map_access(value, key, expr.into()) } else { let mut indices = vec![]; let value = self.lower_aggregate_access(expr, &mut indices); - self.builder - .aggregate_access(value, indices, result_ty, expr.into()) + self.builder.aggregate_access(value, indices, expr.into()) } } @@ -582,32 +626,83 @@ impl<'db, 'a> BodyLowerHelper<'db, 'a> { } ast::Expr::List { elts } | ast::Expr::Tuple { elts } => { - let args = elts.iter().map(|elem| self.lower_expr(elem)).collect(); + let args = elts + .iter() + .map(|elem| self.lower_expr_to_value(elem)) + .collect(); + let ty = self.expr_ty(expr); self.builder.aggregate_construct(ty, args, expr.into()) } - ast::Expr::Bool(b) => self.builder.make_imm_from_bool(*b, ty), + ast::Expr::Bool(b) => { + let imm = self.builder.make_imm_from_bool(*b, ty); + self.builder.bind(imm, expr.into()) + } - ast::Expr::Name(name) => self.resolve_name(name), + ast::Expr::Name(name) => { + let value = self.resolve_name(name); + self.builder.bind(value, expr.into()) + } - ast::Expr::Path(path) => self.resolve_path(path), + ast::Expr::Path(path) => { + let value = self.resolve_path(path); + self.builder.bind(value, expr.into()) + } ast::Expr::Num(num) => { - let imm = BigInt::from_str(num).unwrap(); - self.builder.make_imm(imm, ty) + let imm = Literal::new(num).parse().unwrap(); + let imm = self.builder.make_imm(imm, ty); + self.builder.bind(imm, expr.into()) } ast::Expr::Str(s) => { let ty = self.expr_ty(expr); - self.make_local_constant( + let const_value = self.make_local_constant( "str_in_func".into(), ty, ConstantValue::Str(s.clone()), expr.into(), - ) + ); + self.builder.bind(const_value, expr.into()) + } + + ast::Expr::Unit => { + let value = self.make_unit(); + self.builder.bind(value, expr.into()) } + } + } + + fn lower_expr_to_value(&mut self, expr: &Node) -> ValueId { + let ty = self.expr_ty(expr); + let inst = self.lower_expr(expr); + self.map_to_tmp(inst, ty) + } - ast::Expr::Unit => self.make_unit(), + fn lower_assignable_value(&mut self, expr: &Node) -> AssignableValue { + match &expr.kind { + ast::Expr::Attribute { value, attr } => { + let idx_ty = self.u256_ty(); + let idx = self.expr_ty(value).index_from_fname(self.db, &attr.kind); + let idx = self.builder.make_value(Value::Immediate { + imm: idx, + ty: idx_ty, + }); + let lhs = self.lower_assignable_value(value).into(); + AssignableValue::Aggregate { lhs, idx } + } + ast::Expr::Subscript { value, index } => { + let lhs = self.lower_assignable_value(value).into(); + let attr = self.lower_expr_to_value(index); + if self.expr_ty(value).is_aggregate(self.db) { + AssignableValue::Aggregate { lhs, idx: attr } + } else { + AssignableValue::Map { lhs, key: attr } + } + } + ast::Expr::Name(name) => self.resolve_name(name).into(), + ast::Expr::Path(path) => self.resolve_path(path).into(), + _ => self.lower_expr_to_value(expr).into(), } } @@ -622,12 +717,12 @@ impl<'db, 'a> BodyLowerHelper<'db, 'a> { lhs: &Node, rhs: &Node, ty: TypeId, - ) -> ValueId { + ) -> InstId { let true_bb = self.builder.make_block(); let false_bb = self.builder.make_block(); let merge_bb = self.builder.make_block(); - let lhs = self.lower_expr(lhs); + let lhs = self.lower_expr_to_value(lhs); let tmp = self .builder .declare(Local::tmp_local(format!("${}_tmp", op).into(), ty)); @@ -639,12 +734,13 @@ impl<'db, 'a> BodyLowerHelper<'db, 'a> { self.builder.move_to_block(true_bb); let rhs = self.lower_expr(rhs); - self.builder.assign(tmp, rhs, SourceInfo::dummy()); + self.builder.map_result(rhs, tmp.into()); self.builder.jump(merge_bb, SourceInfo::dummy()); self.builder.move_to_block(false_bb); let false_imm = self.builder.make_imm_from_bool(false, ty); - self.builder.assign(tmp, false_imm, SourceInfo::dummy()); + let false_imm_copy = self.builder.bind(false_imm, SourceInfo::dummy()); + self.builder.map_result(false_imm_copy, tmp.into()); self.builder.jump(merge_bb, SourceInfo::dummy()); } @@ -654,18 +750,19 @@ impl<'db, 'a> BodyLowerHelper<'db, 'a> { self.builder.move_to_block(true_bb); let true_imm = self.builder.make_imm_from_bool(true, ty); - self.builder.assign(tmp, true_imm, SourceInfo::dummy()); + let true_imm_copy = self.builder.bind(true_imm, SourceInfo::dummy()); + self.builder.map_result(true_imm_copy, tmp.into()); self.builder.jump(merge_bb, SourceInfo::dummy()); self.builder.move_to_block(false_bb); let rhs = self.lower_expr(rhs); - self.builder.assign(tmp, rhs, SourceInfo::dummy()); + self.builder.map_result(rhs, tmp.into()); self.builder.jump(merge_bb, SourceInfo::dummy()); } } self.builder.move_to_block(merge_bb); - tmp + self.builder.bind(tmp, SourceInfo::dummy()) } fn lower_binop( @@ -673,21 +770,20 @@ impl<'db, 'a> BodyLowerHelper<'db, 'a> { op: ast::BinOperator, lhs: ValueId, rhs: ValueId, - ty: TypeId, source: SourceInfo, - ) -> ValueId { + ) -> InstId { match op { - ast::BinOperator::Add => self.builder.add(lhs, rhs, ty, source), - ast::BinOperator::Sub => self.builder.sub(lhs, rhs, ty, source), - ast::BinOperator::Mult => self.builder.mul(lhs, rhs, ty, source), - ast::BinOperator::Div => self.builder.div(lhs, rhs, ty, source), - ast::BinOperator::Mod => self.builder.modulo(lhs, rhs, ty, source), - ast::BinOperator::Pow => self.builder.pow(lhs, rhs, ty, source), - ast::BinOperator::LShift => self.builder.shl(lhs, rhs, ty, source), - ast::BinOperator::RShift => self.builder.shr(lhs, rhs, ty, source), - ast::BinOperator::BitOr => self.builder.bit_or(lhs, rhs, ty, source), - ast::BinOperator::BitXor => self.builder.bit_xor(lhs, rhs, ty, source), - ast::BinOperator::BitAnd => self.builder.bit_and(lhs, rhs, ty, source), + ast::BinOperator::Add => self.builder.add(lhs, rhs, source), + ast::BinOperator::Sub => self.builder.sub(lhs, rhs, source), + ast::BinOperator::Mult => self.builder.mul(lhs, rhs, source), + ast::BinOperator::Div => self.builder.div(lhs, rhs, source), + ast::BinOperator::Mod => self.builder.modulo(lhs, rhs, source), + ast::BinOperator::Pow => self.builder.pow(lhs, rhs, source), + ast::BinOperator::LShift => self.builder.shl(lhs, rhs, source), + ast::BinOperator::RShift => self.builder.shr(lhs, rhs, source), + ast::BinOperator::BitOr => self.builder.bit_or(lhs, rhs, source), + ast::BinOperator::BitXor => self.builder.bit_xor(lhs, rhs, source), + ast::BinOperator::BitAnd => self.builder.bit_and(lhs, rhs, source), } } @@ -696,16 +792,15 @@ impl<'db, 'a> BodyLowerHelper<'db, 'a> { op: ast::CompOperator, lhs: ValueId, rhs: ValueId, - ty: TypeId, source: SourceInfo, - ) -> ValueId { + ) -> InstId { match op { - ast::CompOperator::Eq => self.builder.eq(lhs, rhs, ty, source), - ast::CompOperator::NotEq => self.builder.ne(lhs, rhs, ty, source), - ast::CompOperator::Lt => self.builder.lt(lhs, rhs, ty, source), - ast::CompOperator::LtE => self.builder.le(lhs, rhs, ty, source), - ast::CompOperator::Gt => self.builder.gt(lhs, rhs, ty, source), - ast::CompOperator::GtE => self.builder.ge(lhs, rhs, ty, source), + ast::CompOperator::Eq => self.builder.eq(lhs, rhs, source), + ast::CompOperator::NotEq => self.builder.ne(lhs, rhs, source), + ast::CompOperator::Lt => self.builder.lt(lhs, rhs, source), + ast::CompOperator::LtE => self.builder.le(lhs, rhs, source), + ast::CompOperator::Gt => self.builder.gt(lhs, rhs, source), + ast::CompOperator::GtE => self.builder.ge(lhs, rhs, source), } } @@ -716,45 +811,44 @@ impl<'db, 'a> BodyLowerHelper<'db, 'a> { args: &[Node], ty: TypeId, source: SourceInfo, - ) -> ValueId { + ) -> InstId { let call_type = &self.analyzer_body.calls[&func.id]; let mut args: Vec<_> = args .iter() - .map(|arg| self.lower_expr(&arg.kind.value)) + .map(|arg| self.lower_expr_to_value(&arg.kind.value)) .collect(); match call_type { AnalyzerCallType::BuiltinFunction(GlobalFunction::Keccak256) => { - self.builder.keccak256(args[0], ty, source) + self.builder.keccak256(args[0], source) } AnalyzerCallType::Intrinsic(intrinsic) => { self.builder - .yul_intrinsic((*intrinsic).into(), args, ty, source) + .yul_intrinsic((*intrinsic).into(), args, source) } AnalyzerCallType::BuiltinValueMethod { method, .. } => { let arg = self.lower_method_receiver(func); match method { - ValueMethod::ToMem => self.builder.to_mem(arg, ty, source), - ValueMethod::Clone => self.builder.clone(arg, ty, source), - ValueMethod::AbiEncode => self.builder.abi_encode(arg, ty, source), + ValueMethod::ToMem | ValueMethod::Clone => self.builder.mem_copy(arg, source), + ValueMethod::AbiEncode => self.builder.abi_encode(arg, source), } } + // We ignores `args[0]', which represents `context` and not used for now. AnalyzerCallType::BuiltinAssociatedFunction { contract, function } => match function { - ContractTypeMethod::Create => self.builder.create(args[0], *contract, ty, source), - ContractTypeMethod::Create2 => self - .builder - .create2(args[0], args[1], *contract, ty, source), + ContractTypeMethod::Create => self.builder.create(args[1], *contract, source), + ContractTypeMethod::Create2 => { + self.builder.create2(args[1], args[2], *contract, source) + } }, AnalyzerCallType::AssociatedFunction { function, .. } | AnalyzerCallType::Pure(function) => { let func_id = self.db.mir_lowered_func_signature(*function); - self.builder - .call(func_id, args, CallType::Internal, ty, source) + self.builder.call(func_id, args, CallType::Internal, source) } AnalyzerCallType::ValueMethod { method, .. } => { @@ -763,7 +857,7 @@ impl<'db, 'a> BodyLowerHelper<'db, 'a> { let func_id = self.db.mir_lowered_func_signature(*method); self.builder - .call(func_id, method_args, CallType::Internal, ty, source) + .call(func_id, method_args, CallType::Internal, source) } AnalyzerCallType::External { function, .. } => { @@ -771,15 +865,19 @@ impl<'db, 'a> BodyLowerHelper<'db, 'a> { method_args.append(&mut args); let func_id = self.db.mir_lowered_func_signature(*function); self.builder - .call(func_id, method_args, CallType::External, ty, source) + .call(func_id, method_args, CallType::External, source) } - AnalyzerCallType::TypeConstructor(_) => { - if ty.is_primitive(self.db) { - let arg = args[0]; + AnalyzerCallType::TypeConstructor(to_ty) => { + if matches!(to_ty, fe_analyzer::namespace::types::Type::String(..)) { + let arg = *args.last().unwrap(); + self.builder.mem_copy(arg, source) + } else if ty.is_primitive(self.db) { + // TODO: Ignore `ctx` for now. + let arg = *args.last().unwrap(); let arg_ty = self.builder.value_ty(arg); if arg_ty == ty { - arg + self.builder.bind(arg, source) } else { self.builder.cast(arg, ty, source) } @@ -795,7 +893,7 @@ impl<'db, 'a> BodyLowerHelper<'db, 'a> { // FIXME: This is ugly hack to properly analyze method call. Remove this when https://github.com/ethereum/fe/issues/670 is resolved. fn lower_method_receiver(&mut self, receiver: &Node) -> ValueId { match &receiver.kind { - ast::Expr::Attribute { value, .. } => self.lower_expr(value), + ast::Expr::Attribute { value, .. } => self.lower_expr_to_value(value), _ => unreachable!(), } } @@ -807,21 +905,39 @@ impl<'db, 'a> BodyLowerHelper<'db, 'a> { ) -> ValueId { match &expr.kind { ast::Expr::Attribute { value, attr } => { - let index = - self.expr_ty(value) - .index_from_fname(self.db, &attr.kind, self.u256_ty()); + let index = self.expr_ty(value).index_from_fname(self.db, &attr.kind); + let index_ty = self.u256_ty(); let value = self.lower_aggregate_access(value, indices); - indices.push(self.builder.make_value(index)); + indices.push(self.builder.make_value(Value::Immediate { + imm: index, + ty: index_ty, + })); value } ast::Expr::Subscript { value, index } if self.expr_ty(value).is_aggregate(self.db) => { let value = self.lower_aggregate_access(value, indices); - indices.push(self.lower_expr(index)); + indices.push(self.lower_expr_to_value(index)); value } - _ => self.lower_expr(expr), + _ => self.lower_expr_to_value(expr), + } + } + + fn map_to_tmp(&mut self, inst: InstId, ty: TypeId) -> ValueId { + match &self.builder.inst_data(inst).kind { + InstKind::Bind { src } => { + let value = *src; + self.builder.remove_inst(inst); + value + } + _ => { + let tmp = Value::Temporary { inst, ty }; + let result = self.builder.make_value(tmp); + self.builder.map_result(inst, result.into()); + result + } } } @@ -852,7 +968,13 @@ impl<'db, 'a> BodyLowerHelper<'db, 'a> { } fn u256_ty(&mut self) -> TypeId { - self.db.mir_intern_type(ir::Type::U256.into()) + self.db + .mir_intern_type(ir::Type::new(ir::TypeKind::U256, None).into()) + } + + fn bool_ty(&mut self) -> TypeId { + self.db + .mir_intern_type(ir::Type::new(ir::TypeKind::Bool, None).into()) } fn enter_scope(&mut self) { diff --git a/crates/mir/src/lower/types.rs b/crates/mir/src/lower/types.rs index a7e8db07c0..b53a89600e 100644 --- a/crates/mir/src/lower/types.rs +++ b/crates/mir/src/lower/types.rs @@ -1,26 +1,26 @@ -use std::rc::Rc; - use crate::{ db::MirDb, ir::{ types::{ArrayDef, EventDef, MapDef, StructDef, TupleDef}, - Type, TypeId, + Type, TypeId, TypeKind, }, }; use fe_analyzer::namespace::{items as analyzer_items, types as analyzer_types}; -pub fn lower_type(db: &dyn MirDb, ty: &analyzer_types::Type) -> TypeId { - match ty { - analyzer_types::Type::Base(base) => lower_base(db, base), +pub fn lower_type(db: &dyn MirDb, analyzer_ty: &analyzer_types::Type) -> TypeId { + let ty_kind = match analyzer_ty { + analyzer_types::Type::Base(base) => lower_base(base), analyzer_types::Type::Array(arr) => lower_array(db, arr), analyzer_types::Type::Map(map) => lower_map(db, map), analyzer_types::Type::Tuple(tup) => lower_tuple(db, tup), - analyzer_types::Type::String(string) => lower_string(db, string), - analyzer_types::Type::Contract(_) => db.mir_intern_type(Type::Address.into()), + analyzer_types::Type::String(string) => TypeKind::String(string.max_size), + analyzer_types::Type::Contract(_) => TypeKind::Address, analyzer_types::Type::SelfContract(contract) => lower_contract(db, contract), analyzer_types::Type::Struct(struct_) => lower_struct(db, struct_), - } + }; + + intern_type(db, ty_kind, Some(analyzer_ty)) } pub fn lower_event_type(db: &dyn MirDb, event: analyzer_items::EventId) -> TypeId { @@ -48,56 +48,52 @@ pub fn lower_event_type(db: &dyn MirDb, event: analyzer_items::EventId) -> TypeI span, module_id, }; - let ty = Type::Event(def); - intern_type(db, ty) + let ty = TypeKind::Event(def); + intern_type(db, ty, None) } -fn lower_base(db: &dyn MirDb, base: &analyzer_types::Base) -> TypeId { +fn lower_base(base: &analyzer_types::Base) -> TypeKind { use analyzer_types::{Base, Integer}; - let ty = match base { + match base { Base::Numeric(int_ty) => match int_ty { - Integer::I8 => Type::I8, - Integer::I16 => Type::I16, - Integer::I32 => Type::I32, - Integer::I64 => Type::I64, - Integer::I128 => Type::I128, - Integer::I256 => Type::I256, - Integer::U8 => Type::U8, - Integer::U16 => Type::U16, - Integer::U32 => Type::U32, - Integer::U64 => Type::U64, - Integer::U128 => Type::U128, - Integer::U256 => Type::U256, + Integer::I8 => TypeKind::I8, + Integer::I16 => TypeKind::I16, + Integer::I32 => TypeKind::I32, + Integer::I64 => TypeKind::I64, + Integer::I128 => TypeKind::I128, + Integer::I256 => TypeKind::I256, + Integer::U8 => TypeKind::U8, + Integer::U16 => TypeKind::U16, + Integer::U32 => TypeKind::U32, + Integer::U64 => TypeKind::U64, + Integer::U128 => TypeKind::U128, + Integer::U256 => TypeKind::U256, }, - Base::Bool => Type::Bool, - Base::Address => Type::Address, - Base::Unit => Type::Unit, - }; - - intern_type(db, ty) + Base::Bool => TypeKind::Bool, + Base::Address => TypeKind::Address, + Base::Unit => TypeKind::Unit, + } } -fn lower_array(db: &dyn MirDb, arr: &analyzer_types::Array) -> TypeId { +fn lower_array(db: &dyn MirDb, arr: &analyzer_types::Array) -> TypeKind { let len = arr.size; - let elem_ty = lower_base(db, &arr.inner); + let elem_ty = db.mir_lowered_type(arr.inner.into()); let def = ArrayDef { elem_ty, len }; - let ty = Type::Array(def); - intern_type(db, ty) + TypeKind::Array(def) } -fn lower_map(db: &dyn MirDb, map: &analyzer_types::Map) -> TypeId { - let key_ty = lower_base(db, &map.key); +fn lower_map(db: &dyn MirDb, map: &analyzer_types::Map) -> TypeKind { + let key_ty = db.mir_lowered_type(map.key.into()); let value_ty = db.mir_lowered_type(*map.value.clone()); let def = MapDef { key_ty, value_ty }; - let ty = Type::Map(def); - intern_type(db, ty) + TypeKind::Map(def) } -fn lower_tuple(db: &dyn MirDb, tup: &analyzer_types::Tuple) -> TypeId { +fn lower_tuple(db: &dyn MirDb, tup: &analyzer_types::Tuple) -> TypeKind { let items = tup .items .iter() @@ -105,22 +101,10 @@ fn lower_tuple(db: &dyn MirDb, tup: &analyzer_types::Tuple) -> TypeId { .collect(); let def = TupleDef { items }; - let ty = Type::Tuple(def); - intern_type(db, ty) -} - -/// `FeString` type is lowered into Array type. -fn lower_string(db: &dyn MirDb, string: &analyzer_types::FeString) -> TypeId { - // We assume a string consists of only ascii encoding chars. - let elem_ty = intern_type(db, Type::U8); - let len = string.max_size; - - let def = ArrayDef { elem_ty, len }; - let ty = Type::Array(def); - intern_type(db, ty) + TypeKind::Tuple(def) } -fn lower_contract(db: &dyn MirDb, contract: &analyzer_types::Contract) -> TypeId { +fn lower_contract(db: &dyn MirDb, contract: &analyzer_types::Contract) -> TypeKind { let id = contract.id; let name = id.name(db.upcast()); @@ -147,11 +131,10 @@ fn lower_contract(db: &dyn MirDb, contract: &analyzer_types::Contract) -> TypeId span, module_id, }; - let ty = Type::Contract(def); - intern_type(db, ty) + TypeKind::Contract(def) } -fn lower_struct(db: &dyn MirDb, struct_: &analyzer_types::Struct) -> TypeId { +fn lower_struct(db: &dyn MirDb, struct_: &analyzer_types::Struct) -> TypeKind { let id = struct_.id; let name = id.name(db.upcast()); @@ -178,10 +161,13 @@ fn lower_struct(db: &dyn MirDb, struct_: &analyzer_types::Struct) -> TypeId { span, module_id, }; - let ty = Type::Struct(def); - intern_type(db, ty) + TypeKind::Struct(def) } -fn intern_type(db: &dyn MirDb, ty: Type) -> TypeId { - db.mir_intern_type(Rc::new(ty)) +fn intern_type( + db: &dyn MirDb, + ty_kind: TypeKind, + analyzer_type: Option<&analyzer_types::Type>, +) -> TypeId { + db.mir_intern_type(Type::new(ty_kind, analyzer_type.cloned()).into()) } diff --git a/crates/mir/src/pretty_print/inst.rs b/crates/mir/src/pretty_print/inst.rs index 4af6582b55..d047a54494 100644 --- a/crates/mir/src/pretty_print/inst.rs +++ b/crates/mir/src/pretty_print/inst.rs @@ -17,7 +17,9 @@ impl PrettyPrint for InstId { if let Some(result) = store.inst_result(*self) { result.pretty_print(db, store, w)?; write!(w, ": ")?; - store.value_ty(result).pretty_print(db, store, w)?; + + let result_ty = result.ty(db, store); + result_ty.pretty_print(db, store, w)?; write!(w, " = ")?; } @@ -29,14 +31,6 @@ impl PrettyPrint for InstId { store.value_ty(*local).pretty_print(db, store, w) } - InstKind::Assign { lhs, rhs } => { - lhs.pretty_print(db, store, w)?; - write!(w, ": ")?; - store.value_ty(*lhs).pretty_print(db, store, w)?; - write!(w, " = ")?; - rhs.pretty_print(db, store, w) - } - InstKind::Unary { op, value } => { write!(w, "{}", op)?; value.pretty_print(db, store, w) @@ -73,6 +67,16 @@ impl PrettyPrint for InstId { write!(w, "}}") } + InstKind::Bind { src } => { + write!(w, "bind ")?; + src.pretty_print(db, store, w) + } + + InstKind::MemCopy { src } => { + write!(w, "memcopy ")?; + src.pretty_print(db, store, w) + } + InstKind::AggregateAccess { value, indices } => { value.pretty_print(db, store, w)?; for index in indices { @@ -113,7 +117,10 @@ impl PrettyPrint for InstId { InstKind::Revert { arg } => { write!(w, "revert ")?; - arg.pretty_print(db, store, w) + if let Some(arg) = arg { + arg.pretty_print(db, store, w)?; + } + Ok(()) } InstKind::Emit { arg } => { @@ -135,16 +142,6 @@ impl PrettyPrint for InstId { arg.pretty_print(db, store, w) } - InstKind::Clone { arg } => { - write!(w, "clone ")?; - arg.pretty_print(db, store, w) - } - - InstKind::ToMem { arg } => { - write!(w, "to_mem ")?; - arg.pretty_print(db, store, w) - } - InstKind::AbiEncode { arg } => { write!(w, "abi_encode ")?; arg.pretty_print(db, store, w) @@ -177,7 +174,7 @@ impl PrettyPrint for InstId { InstKind::YulIntrinsic { op, args } => { write!(w, "{}(", op)?; args.as_slice().pretty_print(db, store, w)?; - write!(w, "{})", op) + write!(w, ")") } } } diff --git a/crates/mir/src/pretty_print/types.rs b/crates/mir/src/pretty_print/types.rs index 2b8330e037..8acc25ed7f 100644 --- a/crates/mir/src/pretty_print/types.rs +++ b/crates/mir/src/pretty_print/types.rs @@ -4,8 +4,8 @@ use crate::{ db::MirDb, ir::{ function::BodyDataStore, - types::{ArrayDef, TupleDef}, - Type, TypeId, + types::{ArrayDef, TupleDef, TypeKind}, + TypeId, }, }; @@ -18,28 +18,29 @@ impl PrettyPrint for TypeId { store: &BodyDataStore, w: &mut W, ) -> fmt::Result { - match self.data(db).as_ref() { - Type::I8 => write!(w, "i8"), - Type::I16 => write!(w, "i16"), - Type::I32 => write!(w, "i32"), - Type::I64 => write!(w, "i64"), - Type::I128 => write!(w, "i128"), - Type::I256 => write!(w, "i256"), - Type::U8 => write!(w, "u8"), - Type::U16 => write!(w, "u16"), - Type::U32 => write!(w, "u32"), - Type::U64 => write!(w, "u64"), - Type::U128 => write!(w, "u128"), - Type::U256 => write!(w, "u256"), - Type::Bool => write!(w, "bool"), - Type::Address => write!(w, "address"), - Type::Unit => write!(w, "()"), - Type::Array(ArrayDef { elem_ty, len }) => { + match &self.data(db).kind { + TypeKind::I8 => write!(w, "i8"), + TypeKind::I16 => write!(w, "i16"), + TypeKind::I32 => write!(w, "i32"), + TypeKind::I64 => write!(w, "i64"), + TypeKind::I128 => write!(w, "i128"), + TypeKind::I256 => write!(w, "i256"), + TypeKind::U8 => write!(w, "u8"), + TypeKind::U16 => write!(w, "u16"), + TypeKind::U32 => write!(w, "u32"), + TypeKind::U64 => write!(w, "u64"), + TypeKind::U128 => write!(w, "u128"), + TypeKind::U256 => write!(w, "u256"), + TypeKind::Bool => write!(w, "bool"), + TypeKind::Address => write!(w, "address"), + TypeKind::Unit => write!(w, "()"), + TypeKind::String(size) => write!(w, "Str<{}>", size), + TypeKind::Array(ArrayDef { elem_ty, len }) => { write!(w, "[")?; elem_ty.pretty_print(db, store, w)?; write!(w, "; {}]", len) } - Type::Tuple(TupleDef { items }) => { + TypeKind::Tuple(TupleDef { items }) => { write!(w, "(")?; if items.is_empty() { return write!(w, ")"); @@ -53,22 +54,30 @@ impl PrettyPrint for TypeId { items.last().unwrap().pretty_print(db, store, w)?; write!(w, ")") } - Type::Struct(def) => { + TypeKind::Struct(def) => { write!(w, "{}", def.name) } - Type::Event(def) => { + TypeKind::Event(def) => { write!(w, "{}", def.name) } - Type::Contract(def) => { + TypeKind::Contract(def) => { write!(w, "{}", def.name) } - Type::Map(def) => { + TypeKind::Map(def) => { write!(w, "Map<")?; def.key_ty.pretty_print(db, store, w)?; write!(w, ",")?; def.value_ty.pretty_print(db, store, w)?; write!(w, ">") } + TypeKind::MPtr(inner) => { + write!(w, "*@s ")?; + inner.pretty_print(db, store, w) + } + TypeKind::SPtr(inner) => { + write!(w, "*@m ")?; + inner.pretty_print(db, store, w) + } } } } diff --git a/crates/mir/src/pretty_print/value.rs b/crates/mir/src/pretty_print/value.rs index a8ef1255ce..4301298a77 100644 --- a/crates/mir/src/pretty_print/value.rs +++ b/crates/mir/src/pretty_print/value.rs @@ -2,7 +2,9 @@ use std::fmt::{self, Write}; use crate::{ db::MirDb, - ir::{constant::ConstantValue, function::BodyDataStore, Value, ValueId}, + ir::{ + constant::ConstantValue, function::BodyDataStore, value::AssignableValue, Value, ValueId, + }, }; use super::PrettyPrint; @@ -15,10 +17,10 @@ impl PrettyPrint for ValueId { w: &mut W, ) -> fmt::Result { match store.value_data(*self) { - Value::Temporary(_) | Value::Local(_) => write!(w, "_{}", self.index()), - Value::Immediate(imm) => write!(w, "{}", imm.value), - Value::Constant(constant) => { - let const_value = constant.constant.data(db); + Value::Temporary { .. } | Value::Local(_) => write!(w, "_{}", self.index()), + Value::Immediate { imm, .. } => write!(w, "{}", imm), + Value::Constant { constant, .. } => { + let const_value = constant.data(db); write!(w, "const ")?; match &const_value.value { ConstantValue::Immediate(num) => write!(w, "{}", num), @@ -26,7 +28,7 @@ impl PrettyPrint for ValueId { ConstantValue::Bool(b) => write!(w, "{}", b), } } - Value::Unit(_) => write!(w, "()"), + Value::Unit { .. } => write!(w, "()"), } } } @@ -51,3 +53,29 @@ impl PrettyPrint for &[ValueId] { arg.pretty_print(db, store, w) } } + +impl PrettyPrint for AssignableValue { + fn pretty_print( + &self, + db: &dyn MirDb, + store: &BodyDataStore, + w: &mut W, + ) -> fmt::Result { + match self { + Self::Value(value) => value.pretty_print(db, store, w), + Self::Aggregate { lhs, idx } => { + lhs.pretty_print(db, store, w)?; + write!(w, ".<")?; + idx.pretty_print(db, store, w)?; + write!(w, ">") + } + + Self::Map { lhs, key } => { + lhs.pretty_print(db, store, w)?; + write!(w, "{{")?; + key.pretty_print(db, store, w)?; + write!(w, "}}") + } + } + } +} diff --git a/crates/new_abi/Cargo.toml b/crates/new_abi/Cargo.toml index ae4bfff262..7fd7373be9 100644 --- a/crates/new_abi/Cargo.toml +++ b/crates/new_abi/Cargo.toml @@ -1,13 +1,13 @@ [package] name = "fe-new_abi" -version = "0.14.0-alpha" +version = "0.16.0-alpha" authors = ["The Fe Developers "] edition = "2021" license = "Apache-2.0" repository = "https://github.com/ethereum/fe" [dependencies] -fe-common = { path = "../common", version = "^0.14.0-alpha" } +fe-common = { path = "../common", version = "^0.16.0-alpha" } serde = { version = "1.0", features = ["derive"] } [dev-dependencies] diff --git a/crates/new_abi/src/contract.rs b/crates/new_abi/src/contract.rs index 6557fecb60..efe05089d2 100644 --- a/crates/new_abi/src/contract.rs +++ b/crates/new_abi/src/contract.rs @@ -2,7 +2,7 @@ use super::{event::AbiEvent, function::AbiFunction}; use serde::{ser::SerializeSeq, Serialize, Serializer}; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct AbiContract { /// Public functions in the contract. funcs: Vec, diff --git a/crates/new_abi/src/event.rs b/crates/new_abi/src/event.rs index 5925951a62..b26d5795d1 100644 --- a/crates/new_abi/src/event.rs +++ b/crates/new_abi/src/event.rs @@ -6,10 +6,10 @@ use serde::Serialize; #[derive(Debug, Clone, PartialEq, Eq, Serialize)] pub struct AbiEvent { #[serde(rename = "type")] - ty: &'static str, - name: String, - inputs: Vec, - anonymous: bool, + pub ty: &'static str, + pub name: String, + pub inputs: Vec, + pub anonymous: bool, } impl AbiEvent { @@ -62,10 +62,10 @@ impl AbiEventSignature { #[derive(Debug, Clone, PartialEq, Eq, Serialize)] pub struct AbiEventField { - name: String, + pub name: String, #[serde(flatten)] - ty: AbiType, - indexed: bool, + pub ty: AbiType, + pub indexed: bool, } impl AbiEventField { diff --git a/crates/new_abi/src/function.rs b/crates/new_abi/src/function.rs index 1ff7417d8e..fb204e6fe9 100644 --- a/crates/new_abi/src/function.rs +++ b/crates/new_abi/src/function.rs @@ -45,6 +45,7 @@ impl AbiFunction { #[serde(rename_all = "lowercase")] pub enum AbiFunctionType { Function, + Constructor, Receive, Payable, Fallback, diff --git a/crates/new_abi/src/types.rs b/crates/new_abi/src/types.rs index 78bcdafe39..6b785b3991 100644 --- a/crates/new_abi/src/types.rs +++ b/crates/new_abi/src/types.rs @@ -9,6 +9,8 @@ pub enum AbiType { Function, Array { elem_ty: Box, len: usize }, Tuple(Vec), + Bytes, + String, } impl AbiType { @@ -19,7 +21,13 @@ impl AbiType { Self::Address => "address".to_string(), Self::Bool => "bool".to_string(), Self::Function => "function".to_string(), - Self::Array { elem_ty, len } => format!("{}[{}]", elem_ty.selector_type_name(), len), + Self::Array { elem_ty, len } => { + if elem_ty.as_ref() == &AbiType::UInt(8) { + "bytes".to_string() + } else { + format!("{}[{}]", elem_ty.selector_type_name(), len) + } + } Self::Tuple(elems) => format!( "({})", elems @@ -28,16 +36,90 @@ impl AbiType { .collect::>() .join(",") ), + + Self::Bytes => "bytes".to_string(), + Self::String => "string".to_string(), } } pub fn abi_type_name(&self) -> String { match self { Self::Tuple(_) => "tuple".to_string(), - Self::Array { elem_ty, len } => format!("{}[{}]", elem_ty.abi_type_name(), len), + Self::Array { elem_ty, len } => { + if elem_ty.as_ref() == &AbiType::UInt(8) { + "bytes".to_string() + } else { + format!("{}[{}]", elem_ty.abi_type_name(), len) + } + } _ => self.selector_type_name(), } } + + pub fn header_size(&self) -> usize { + match self { + Self::UInt(_) | Self::Int(_) | Self::Address | Self::Bool | Self::Function => 32, + Self::Array { elem_ty, len } => elem_ty.header_size() * len, + Self::Tuple(fields) => { + if self.is_static() { + fields + .iter() + .fold(0, |acc, field| acc + field.ty.header_size()) + } else { + 32 + } + } + Self::Bytes | Self::String => 32, + } + } + + pub fn is_primitive(&self) -> bool { + matches! { + self, + Self::UInt(_) | Self::Int(_) | Self::Address | Self::Bool + } + } + + pub fn is_bytes(&self) -> bool { + matches! { + self, + Self::Bytes, + } + } + + pub fn is_string(&self) -> bool { + matches! { + self, + Self::String + } + } + + pub fn is_static(&self) -> bool { + match self { + Self::UInt(_) | Self::Int(_) | Self::Address | Self::Bool | Self::Function => true, + Self::Array { elem_ty, .. } => elem_ty.is_static(), + Self::Tuple(fields) => fields.iter().all(|field| field.ty.is_static()), + Self::Bytes | Self::String => false, + } + } + + /// Returns bytes size of the encoded type if the type is static. + pub fn size(&self) -> Option { + match self { + Self::UInt(_) | Self::Int(_) | Self::Address | Self::Bool => Some(32), + Self::Function => Some(24), + Self::Array { elem_ty, len } => Some(elem_ty.size()? * len), + Self::Tuple(fields) => { + let mut size = 0; + for field in fields.iter() { + size += field.ty.size()?; + } + Some(size) + } + + Self::Bytes | Self::String => None, + } + } } impl Serialize for AbiType { diff --git a/crates/test-files/fixtures/demos/erc20_token.fe b/crates/test-files/fixtures/demos/erc20_token.fe index e7274867fe..691b5fe7d3 100644 --- a/crates/test-files/fixtures/demos/erc20_token.fe +++ b/crates/test-files/fixtures/demos/erc20_token.fe @@ -96,4 +96,4 @@ contract ERC20: self._decimals = decimals_ fn _before_token_transfer(from: address, to: address, _ value: u256): - pass + return diff --git a/crates/test-files/fixtures/stress/data_copying_stress.fe b/crates/test-files/fixtures/stress/data_copying_stress.fe index 9db0419e70..68d211864a 100644 --- a/crates/test-files/fixtures/stress/data_copying_stress.fe +++ b/crates/test-files/fixtures/stress/data_copying_stress.fe @@ -71,7 +71,7 @@ contract Foo: emit_my_event_internal( ctx, self.my_string.to_mem(), - self.my_u256.to_mem() + self.my_u256 ) fn emit_my_event_internal(ctx: Context, _ my_string: String<42>, _ my_u256: u256): diff --git a/crates/test-utils/src/lib.rs b/crates/test-utils/src/lib.rs index 4666604e49..67997cb8bf 100644 --- a/crates/test-utils/src/lib.rs +++ b/crates/test-utils/src/lib.rs @@ -330,7 +330,7 @@ pub fn deploy_contract( contract_name: &str, init_params: &[ethabi::Token], ) -> ContractHarness { - let mut db = driver::Db::default(); + let mut db = driver::NewDb::default(); let compiled_module = match driver::compile_single_file( &mut db, fixture, @@ -367,7 +367,7 @@ pub fn deploy_contract_from_ingot( init_params: &[ethabi::Token], ) -> ContractHarness { let files = test_files::fixture_dir_files(path); - let mut db = driver::Db::default(); + let mut db = driver::NewDb::default(); let compiled_module = match driver::compile_ingot(&mut db, "test", &files, true, true) { Ok(module) => module, Err(error) => { @@ -476,7 +476,11 @@ fn _deploy_contract( bytecode, None, ) { - return ContractHarness::new(exit.1.expect("Unable to retrieve contract address"), abi); + return ContractHarness::new( + exit.1 + .unwrap_or_else(|| panic!("Unable to retrieve contract address: {:?}", exit.0)), + abi, + ); } panic!("Failed to create contract") @@ -574,7 +578,7 @@ pub fn compile_solidity_contract( #[allow(dead_code)] pub fn load_contract(address: H160, fixture: &str, contract_name: &str) -> ContractHarness { - let mut db = driver::Db::default(); + let mut db = driver::NewDb::default(); let compiled_module = driver::compile_single_file(&mut db, fixture, test_files::fixture(fixture), true, true) .unwrap_or_else(|err| { diff --git a/crates/tests/src/crashes.rs b/crates/tests/src/crashes.rs index 8abec5ce35..8d0d686c63 100644 --- a/crates/tests/src/crashes.rs +++ b/crates/tests/src/crashes.rs @@ -5,7 +5,7 @@ macro_rules! test_file { #[test] #[wasm_bindgen_test] fn $name() { - let mut db = fe_driver::Db::default(); + let mut db = fe_driver::NewDb::default(); let path = concat!("crashes/", stringify!($name), ".fe"); let src = test_files::fixture(path); fe_driver::compile_single_file(&mut db, path, src, true, true).ok(); diff --git a/crates/tests/src/demo_simple_open_auction.rs b/crates/tests/src/demo_simple_open_auction.rs index b4c0773e00..bec7ffd5fb 100644 --- a/crates/tests/src/demo_simple_open_auction.rs +++ b/crates/tests/src/demo_simple_open_auction.rs @@ -84,8 +84,8 @@ fn simple_open_auction() { .set_storage( harness.address, H256::from_slice(&[ - 0, 34, 66, 149, 149, 51, 133, 111, 42, 3, 243, 199, 217, 67, 30, 40, 239, 79, - 229, 203, 42, 21, 3, 140, 55, 241, 215, 109, 53, 220, 80, 136, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, ]), H256::from_slice(&[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, diff --git a/crates/tests/src/features.rs b/crates/tests/src/features.rs index da701e914c..9873e253e2 100644 --- a/crates/tests/src/features.rs +++ b/crates/tests/src/features.rs @@ -916,7 +916,6 @@ fn checked_arithmetic() { // ADDITION // unsigned: max_value + 1 fails - harness.test_function_reverts( &mut executor, &format!("add_u{}", config.size), @@ -1603,7 +1602,7 @@ fn abi_decode_checks() { harness.test_call_reverts(&mut executor, tampered_data, &revert_data); } - // decode_u128_bool + // // decode_u128_bool { let input = [uint_token(99999999), bool_token(true)]; let data = harness.build_calldata("decode_u128_bool", &input); @@ -1742,8 +1741,8 @@ fn abi_decode_checks() { // this would break the equivalence of string's `data_offset + data_size` and // the bytes' `data_offset`, making the encoding invalid tampered_data[byte_index] = 33; - // the string length is completely valid otherwise. 32 for example will not revert - // tampered_data[byte_index] = 32; + // the string length is completely valid otherwise. 32 for example will not + // revert tampered_data[byte_index] = 32; harness.test_call_reverts(&mut executor, tampered_data, &revert_data); // place non-zero byte in padded region of the string diff --git a/crates/tests/src/snapshots/fe_compiler_tests__demo_erc20__erc20_token.snap b/crates/tests/src/snapshots/fe_compiler_tests__demo_erc20__erc20_token.snap index 642ead66f6..cc70c8e2f6 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__demo_erc20__erc20_token.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__demo_erc20__erc20_token.snap @@ -1,21 +1,20 @@ --- source: crates/tests/src/demo_erc20.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -name([]) used 1885 gas -symbol([]) used 1907 gas -decimals([]) used 339 gas -totalSupply([]) used 349 gas -balanceOf([Address(0x1000000000000000000000000000000000000001)]) used 681 gas -transfer([Address(0x2000000000000000000000000000000000000002), Uint(4200000000000000)]) used 25942 gas -balanceOf([Address(0x2000000000000000000000000000000000000002)]) used 681 gas -balanceOf([Address(0x1000000000000000000000000000000000000001)]) used 681 gas -approve([Address(0x2000000000000000000000000000000000000002), Uint(5000000000000000)]) used 24934 gas -transferFrom([Address(0x1000000000000000000000000000000000000001), Address(0x3000000000000000000000000000000000000003), Uint(2500000000000000)]) used 29619 gas -balanceOf([Address(0x1000000000000000000000000000000000000001)]) used 681 gas -balanceOf([Address(0x3000000000000000000000000000000000000003)]) used 681 gas -allowance([Address(0x1000000000000000000000000000000000000001), Address(0x2000000000000000000000000000000000000002)]) used 929 gas -transferFrom([Address(0x1000000000000000000000000000000000000001), Address(0x3000000000000000000000000000000000000003), Uint(2000000000000000)]) used 7719 gas -balanceOf([Address(0x3000000000000000000000000000000000000003)]) used 681 gas +name([]) used 1530 gas +symbol([]) used 1564 gas +decimals([]) used 308 gas +totalSupply([]) used 309 gas +balanceOf([Address(0x1000000000000000000000000000000000000001)]) used 579 gas +transfer([Address(0x2000000000000000000000000000000000000002), Uint(4200000000000000)]) used 25865 gas +balanceOf([Address(0x2000000000000000000000000000000000000002)]) used 579 gas +balanceOf([Address(0x1000000000000000000000000000000000000001)]) used 579 gas +approve([Address(0x2000000000000000000000000000000000000002), Uint(5000000000000000)]) used 24996 gas +transferFrom([Address(0x1000000000000000000000000000000000000001), Address(0x3000000000000000000000000000000000000003), Uint(2500000000000000)]) used 29360 gas +balanceOf([Address(0x1000000000000000000000000000000000000001)]) used 579 gas +balanceOf([Address(0x3000000000000000000000000000000000000003)]) used 579 gas +allowance([Address(0x1000000000000000000000000000000000000001), Address(0x2000000000000000000000000000000000000002)]) used 733 gas +transferFrom([Address(0x1000000000000000000000000000000000000001), Address(0x3000000000000000000000000000000000000003), Uint(2000000000000000)]) used 7460 gas +balanceOf([Address(0x3000000000000000000000000000000000000003)]) used 579 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__demo_guestbook__guest_book.snap b/crates/tests/src/snapshots/fe_compiler_tests__demo_guestbook__guest_book.snap index abd2d54830..7c971b6cd2 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__demo_guestbook__guest_book.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__demo_guestbook__guest_book.snap @@ -1,8 +1,7 @@ --- source: crates/tests/src/demo_guestbook.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -sign([String("hello world")]) used 93866 gas -get_msg([Address(0x1234000000000000000000000000000000005678)]) used 2179 gas +sign([String("hello world")]) used 54310 gas +get_msg([Address(0x1234000000000000000000000000000000005678)]) used 2332 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__demo_simple_open_auction__simple_open_auction.snap b/crates/tests/src/snapshots/fe_compiler_tests__demo_simple_open_auction__simple_open_auction.snap index 5457366564..0612a21ba2 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__demo_simple_open_auction__simple_open_auction.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__demo_simple_open_auction__simple_open_auction.snap @@ -2,9 +2,9 @@ source: crates/tests/src/demo_simple_open_auction.rs expression: "format!(\"{}\", harness.gas_reporter)" --- -bid([]) used 46000 gas -bid([]) used 24981 gas -withdraw([]) used 15536 gas -withdraw([]) used 2553 gas -action_end([]) used 31258 gas +bid([]) used 46243 gas +bid([]) used 25169 gas +withdraw([]) used 15391 gas +withdraw([]) used 2439 gas +action_end([]) used 31528 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__demo_uniswap__uniswap_contracts-2.snap b/crates/tests/src/snapshots/fe_compiler_tests__demo_uniswap__uniswap_contracts-2.snap index 2fd611cb0d..e662c74841 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__demo_uniswap__uniswap_contracts-2.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__demo_uniswap__uniswap_contracts-2.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/demo_uniswap.rs expression: "format!(\"{}\", factory_harness.gas_reporter)" - --- -create_pair([Address(0x5dddfce53ee040d9eb21afbc0ae1bb4dbb0ba643), Address(0x5f8bd49cd9f0cb2bd5bb9d4320dfe9b61023249d)]) used 1477217 gas +create_pair([Address(0x5dddfce53ee040d9eb21afbc0ae1bb4dbb0ba643), Address(0x5f8bd49cd9f0cb2bd5bb9d4320dfe9b61023249d)]) used 1549892 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__demo_uniswap__uniswap_contracts.snap b/crates/tests/src/snapshots/fe_compiler_tests__demo_uniswap__uniswap_contracts.snap index dcbdc9a5b1..6d53efabb0 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__demo_uniswap__uniswap_contracts.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__demo_uniswap__uniswap_contracts.snap @@ -1,18 +1,17 @@ --- source: crates/tests/src/demo_uniswap.rs expression: "format!(\"{}\", pair_harness.gas_reporter)" - --- -factory([]) used 288 gas -token0([]) used 343 gas -token1([]) used 365 gas -mint([Address(0x1000000000000000000000000000000000000001)]) used 143325 gas -balanceOf([Address(0x1000000000000000000000000000000000000001)]) used 744 gas -balanceOf([Address(0x0000000000000000000000000000000000000000)]) used 744 gas -get_reserves([]) used 1217 gas -swap([Uint(1993), Uint(0), Address(0x0000000000000000000000000000000000000042)]) used 36265 gas -get_reserves([]) used 1217 gas -transfer([Address(0x90c84d90bb9fd08f46f6b7efc23b758feb9c2d56), Uint(141421356237309503880)]) used 6048 gas -burn([Address(0x1000000000000000000000000000000000000001)]) used 4959 gas -get_reserves([]) used 1217 gas +factory([]) used 275 gas +token0([]) used 297 gas +token1([]) used 319 gas +mint([Address(0x1000000000000000000000000000000000000001)]) used 148429 gas +balanceOf([Address(0x1000000000000000000000000000000000000001)]) used 628 gas +balanceOf([Address(0x0000000000000000000000000000000000000000)]) used 628 gas +get_reserves([]) used 801 gas +swap([Uint(1993), Uint(0), Address(0x0000000000000000000000000000000000000042)]) used 36368 gas +get_reserves([]) used 801 gas +transfer([Address(0xffb8f605926b517aff3e271e76904afb4b9999b2), Uint(141421356237309503880)]) used 5802 gas +burn([Address(0x1000000000000000000000000000000000000001)]) used 3650 gas +get_reserves([]) used 801 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__address_bytes10_map.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__address_bytes10_map.snap index 9a8e7e1a85..cdd1de82a3 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__address_bytes10_map.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__address_bytes10_map.snap @@ -1,10 +1,9 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -write_bar([Address(0x0000000000000000000000000000000000000001), Bytes([116, 101, 110, 32, 98, 121, 116, 101, 115, 49])]) used 22956 gas -write_bar([Address(0x0000000000000000000000000000000000000002), Bytes([116, 101, 110, 32, 98, 121, 116, 101, 115, 50])]) used 22956 gas -read_bar([Address(0x0000000000000000000000000000000000000001)]) used 1269 gas -read_bar([Address(0x0000000000000000000000000000000000000002)]) used 1269 gas +write_bar([Address(0x0000000000000000000000000000000000000001), Bytes([116, 101, 110, 32, 98, 121, 116, 101, 115, 49])]) used 22639 gas +write_bar([Address(0x0000000000000000000000000000000000000002), Bytes([116, 101, 110, 32, 98, 121, 116, 101, 115, 50])]) used 22639 gas +read_bar([Address(0x0000000000000000000000000000000000000001)]) used 802 gas +read_bar([Address(0x0000000000000000000000000000000000000002)]) used 802 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__balances.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__balances.snap index 71715ee3d0..60d2712dfe 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__balances.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__balances.snap @@ -1,12 +1,11 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -my_balance([]) used 174 gas -other_balance([Address(0x5dddfce53ee040d9eb21afbc0ae1bb4dbb0ba643)]) used 360 gas -other_balance([Address(0x2000000000000000000000000000000000000002)]) used 2860 gas -my_balance([]) used 174 gas -other_balance([Address(0x5dddfce53ee040d9eb21afbc0ae1bb4dbb0ba643)]) used 360 gas -other_balance([Address(0x2000000000000000000000000000000000000002)]) used 360 gas +my_balance([]) used 127 gas +other_balance([Address(0x5dddfce53ee040d9eb21afbc0ae1bb4dbb0ba643)]) used 320 gas +other_balance([Address(0x2000000000000000000000000000000000000002)]) used 2820 gas +my_balance([]) used 127 gas +other_balance([Address(0x5dddfce53ee040d9eb21afbc0ae1bb4dbb0ba643)]) used 320 gas +other_balance([Address(0x2000000000000000000000000000000000000002)]) used 320 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__base_tuple.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__base_tuple.snap index d2ab10f7cb..8e7ee6da07 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__base_tuple.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__base_tuple.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(42), Bool(true)]) used 627 gas +bar([Uint(42), Bool(true)]) used 373 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_001.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_001.snap index ac5f3e0534..d17fb3445d 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_001.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_001.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([]) used 193 gas +bar([]) used 143 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_002.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_002.snap index 4d725c82a7..d50d6b6665 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_002.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_002.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([]) used 705 gas +bar([]) used 1020 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_003.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_003.snap index 645d77ea64..a96dba8d4f 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_003.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_003.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([]) used 69978 gas +bar([]) used 67982 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_004.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_004.snap index f5643a6aaf..2e6ef6f829 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_004.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_004.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([]) used 576 gas +bar([]) used 759 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_005.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_005.snap index b4cf734875..3fe69d8bc6 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_005.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_005.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([]) used 1103 gas +bar([]) used 1658 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_006.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_006.snap index 6702b22d2f..6859c92621 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_006.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_006.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([]) used 516 gas +bar([]) used 465 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_007.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_007.snap index 685130f1ae..ee6b1e38e7 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_007.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_007.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([]) used 435 gas +bar([]) used 421 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_008.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_008.snap index f502751cff..dbbc053bbd 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_008.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_008.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([]) used 2421 gas +bar([]) used 2329 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_009.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_009.snap index f789dbcda3..258d191e3d 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_009.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_009.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([]) used 169 gas +bar([]) used 102 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_01.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_01.snap index 1ad018da80..948153e6c0 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_01.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_01.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -add([Uint(2), Uint(5)]) used 313 gas +add([Uint(2), Uint(5)]) used 274 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_010.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_010.snap index 79bbab7a0c..29bec9b751 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_010.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_010.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([]) used 283 gas +bar([]) used 236 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_011.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_011.snap index f4b58aec2d..72ce80e8df 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_011.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_011.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(6)]) used 271 gas +bar([Uint(6)]) used 234 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_012.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_012.snap index 15669c9b2d..f974ded6a5 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_012.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_012.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(4)]) used 285 gas +bar([Uint(4)]) used 248 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_013.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_013.snap index 723c7eb520..de0bb82aec 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_013.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_013.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([]) used 26803 gas +bar([]) used 22418 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_014.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_014.snap index 7bf75afdd6..72ce80e8df 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_014.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_014.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(6)]) used 260 gas +bar([Uint(6)]) used 234 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_015.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_015.snap index f789dbcda3..258d191e3d 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_015.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_015.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([]) used 169 gas +bar([]) used 102 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_016.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_016.snap index 18cd174c11..c773a629ff 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_016.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_016.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(6)]) used 280 gas +bar([Uint(6)]) used 219 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_017.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_017.snap index 995c8c6be7..eeacbe8f4e 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_017.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_017.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(4)]) used 295 gas +bar([Uint(4)]) used 230 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_018.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_018.snap index 7beb3d75be..52f87f5b86 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_018.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_018.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([]) used 22689 gas +bar([]) used 22235 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_019.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_019.snap index 7beb3d75be..52f87f5b86 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_019.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_019.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([]) used 22689 gas +bar([]) used 22235 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_02.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_02.snap index c9cc58cd70..0cbe8e61a7 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_02.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_02.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -sub([Uint(42), Uint(26)]) used 332 gas +sub([Uint(42), Uint(26)]) used 293 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_020.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_020.snap index 7beb3d75be..52f87f5b86 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_020.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_020.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([]) used 22689 gas +bar([]) used 22235 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_021.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_021.snap index f789dbcda3..258d191e3d 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_021.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_021.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([]) used 169 gas +bar([]) used 102 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_022.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_022.snap index f789dbcda3..258d191e3d 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_022.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_022.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([]) used 169 gas +bar([]) used 102 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_023.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_023.snap index dafc99731b..ba218f438d 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_023.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_023.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Bool(true)]) used 232 gas +bar([Bool(true)]) used 189 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_024.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_024.snap index 2d9984d098..53d55057b7 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_024.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_024.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Bool(false)]) used 232 gas +bar([Bool(false)]) used 189 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_025.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_025.snap index f0d0e63dcf..a37a183336 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_025.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_025.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([]) used 22809 gas +bar([]) used 22307 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_026.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_026.snap index 88a4bf9264..4defc66265 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_026.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_026.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([]) used 172 gas +bar([]) used 111 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_027.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_027.snap index f789dbcda3..258d191e3d 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_027.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_027.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([]) used 169 gas +bar([]) used 102 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_028.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_028.snap index 8e7f946e5c..56bd5e2cb8 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_028.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_028.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([]) used 166 gas +bar([]) used 105 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_029.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_029.snap index 03c6803085..d8668aa525 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_029.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_029.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639933)]) used 44855 gas +bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639933)]) used 22672 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_03.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_03.snap index efab9bf108..95290dc50d 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_03.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_03.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -mul([Uint(10), Uint(42)]) used 379 gas +mul([Uint(10), Uint(42)]) used 340 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_030.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_030.snap index c4f7117ae7..67ed6492c9 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_030.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_030.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639926)]) used 22857 gas +bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639926)]) used 22659 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_031.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_031.snap index 8164a82969..6acf6b4e4e 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_031.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_031.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639933)]) used 475 gas +bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639933)]) used 495 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_032.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_032.snap index cd83a16ae1..b34a4eb760 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_032.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_032.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(42)]) used 202 gas +bar([Uint(42)]) used 153 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_033.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_033.snap index 3da522d822..24bf637d0b 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_033.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_033.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(42)]) used 223 gas +bar([Uint(42)]) used 186 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_034.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_034.snap index 3da522d822..24bf637d0b 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_034.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_034.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(42)]) used 223 gas +bar([Uint(42)]) used 186 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_035.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_035.snap index 3da522d822..24bf637d0b 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_035.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_035.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(42)]) used 223 gas +bar([Uint(42)]) used 186 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_036.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_036.snap index 3da522d822..24bf637d0b 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_036.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_036.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(42)]) used 223 gas +bar([Uint(42)]) used 186 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_037.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_037.snap index 3da522d822..24bf637d0b 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_037.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_037.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(42)]) used 223 gas +bar([Uint(42)]) used 186 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_038.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_038.snap index f789dbcda3..258d191e3d 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_038.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_038.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([]) used 169 gas +bar([]) used 102 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_039.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_039.snap index 8e7f946e5c..56bd5e2cb8 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_039.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_039.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([]) used 166 gas +bar([]) used 105 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_04.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_04.snap index 4ee3b60e25..966b080bef 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_04.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_04.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -div([Uint(43), Uint(5)]) used 375 gas +div([Uint(43), Uint(5)]) used 336 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_040.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_040.snap index 5f70c01537..120369afd9 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_040.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_040.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(253)]) used 234 gas +bar([Uint(253)]) used 194 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_041.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_041.snap index 4d8deb696a..76553949c9 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_041.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_041.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(127)]) used 234 gas +bar([Uint(127)]) used 194 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_042.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_042.snap index 5b9fdc1165..55339a68fc 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_042.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_042.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(128)]) used 234 gas +bar([Uint(128)]) used 194 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_043.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_043.snap index 04468cb48e..205f721a29 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_043.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_043.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(129)]) used 234 gas +bar([Uint(129)]) used 194 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_044.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_044.snap index f3178cbd0a..0b831709ad 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_044.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_044.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(0)]) used 234 gas +bar([Uint(0)]) used 194 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_045.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_045.snap index 388b8b9fb1..8ecb6f9b6f 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_045.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_045.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(1)]) used 234 gas +bar([Uint(1)]) used 194 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_046.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_046.snap index 3845cab5c6..18297c2af9 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_046.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_046.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639933)]) used 265 gas +bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639933)]) used 210 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_047.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_047.snap index 920d0f6486..07b6c447a2 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_047.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_047.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639808)]) used 265 gas +bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639808)]) used 210 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_048.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_048.snap index ddab38cec9..edc86ad71e 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_048.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_048.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639809)]) used 265 gas +bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639809)]) used 210 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_049.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_049.snap index 9f4c4cebfc..5e2a97ef3a 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_049.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_049.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(127)]) used 265 gas +bar([Int(127)]) used 210 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_05.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_05.snap index a4dd4c23c1..1f1fa6f196 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_05.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_05.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -mod([Uint(43), Uint(5)]) used 397 gas +mod([Uint(43), Uint(5)]) used 358 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_050.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_050.snap index 84490d6469..04e792a075 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_050.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_050.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(0)]) used 265 gas +bar([Int(0)]) used 210 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_051.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_051.snap index fddf4412e0..17bface688 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_051.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_051.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(1)]) used 265 gas +bar([Int(1)]) used 210 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_052.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_052.snap index f789dbcda3..258d191e3d 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_052.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_052.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([]) used 169 gas +bar([]) used 102 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_053.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_053.snap index e6598d2fe0..ee7d303939 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_053.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_053.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([]) used 25689 gas +bar([]) used 28225 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_054.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_054.snap index 7b6ec4fbf8..e8c67cf96c 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_054.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_054.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([]) used 2629 gas +bar([]) used 4140 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_055.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_055.snap index 47af2c96e2..5ff49512c5 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_055.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_055.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(42), Uint(26)]) used 262 gas +bar([Uint(42), Uint(26)]) used 211 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_056.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_056.snap index f6d57f5b8f..60e78a1028 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_056.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_056.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(42), Uint(26)]) used 302 gas +bar([Uint(42), Uint(26)]) used 255 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_057.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_057.snap index e4706cfbc5..151276d576 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_057.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_057.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(5)]) used 47240 gas +bar([Uint(5)]) used 46603 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_058.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_058.snap index 47b523fb72..cf89ede573 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_058.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_058.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(1)]) used 253 gas +bar([Int(1)]) used 156 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_059.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_059.snap index 804e4f48fe..5416b4adfc 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_059.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_059.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(1)]) used 267 gas +bar([Int(1)]) used 218 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_06.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_06.snap index 0a9f633583..e689db94f6 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_06.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_06.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -pow([Uint(3), Uint(5)]) used 662 gas +pow([Uint(3), Uint(5)]) used 623 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_060.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_060.snap index 412eedefa9..e903e9853e 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_060.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_060.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(4000000000)]) used 267 gas +bar([Int(4000000000)]) used 218 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_061.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_061.snap index e50160f734..3562eafbb5 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_061.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_061.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(30000)]) used 267 gas +bar([Int(30000)]) used 218 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_062.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_062.snap index 8ccd26dc44..aacf250047 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_062.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_062.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(2000)]) used 267 gas +bar([Int(2000)]) used 218 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_063.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_063.snap index b49e972110..5416b4adfc 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_063.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_063.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(1)]) used 264 gas +bar([Int(1)]) used 218 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_064.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_064.snap index 7eeb268481..1e93964bd7 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_064.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_064.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(1)]) used 205 gas +bar([Uint(1)]) used 156 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_065.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_065.snap index f444fd38fa..a36684e185 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_065.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_065.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(1)]) used 244 gas +bar([Uint(1)]) used 189 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_066.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_066.snap index f444fd38fa..a36684e185 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_066.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_066.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(1)]) used 244 gas +bar([Uint(1)]) used 189 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_067.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_067.snap index f444fd38fa..a36684e185 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_067.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_067.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(1)]) used 244 gas +bar([Uint(1)]) used 189 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_068.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_068.snap index f444fd38fa..a36684e185 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_068.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_068.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(1)]) used 244 gas +bar([Uint(1)]) used 189 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_069.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_069.snap index f444fd38fa..a36684e185 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_069.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_069.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(1)]) used 244 gas +bar([Uint(1)]) used 189 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_07.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_07.snap index 48c21481e1..8b8c1618f7 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_07.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_07.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -lshift([Uint(1), Uint(7)]) used 458 gas +lshift([Uint(1), Uint(7)]) used 426 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_070.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_070.snap index a68c93f5dd..0016e3fd16 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_070.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_070.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(42), Uint(42)]) used 262 gas +bar([Uint(42), Uint(42)]) used 211 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_071.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_071.snap index 396d471ecb..0c52d8effd 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_071.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_071.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639894), Int(115792089237316195423570985008687907853269984665640564039457584007913129639894)]) used 450 gas +bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639894), Int(115792089237316195423570985008687907853269984665640564039457584007913129639894)]) used 286 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_072.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_072.snap index a8e11f0d76..bc48802dd3 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_072.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_072.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639894), Int(42)]) used 450 gas +bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639894), Int(42)]) used 286 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_073.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_073.snap index 7b82770c98..407f40f656 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_073.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_073.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(42), Uint(42)]) used 333 gas +bar([Uint(42), Uint(42)]) used 335 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_074.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_074.snap index c3f2e10dcf..157626b204 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_074.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_074.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(42), Uint(42)]) used 259 gas +bar([Uint(42), Uint(42)]) used 208 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_075.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_075.snap index 6dcb92aac8..d03990318e 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_075.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_075.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639894), Int(115792089237316195423570985008687907853269984665640564039457584007913129639894)]) used 444 gas +bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639894), Int(115792089237316195423570985008687907853269984665640564039457584007913129639894)]) used 280 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_076.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_076.snap index 552c9fc98d..f01fd050d5 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_076.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_076.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639894), Int(42)]) used 444 gas +bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639894), Int(42)]) used 280 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_077.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_077.snap index e775c45a82..90da5b6009 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_077.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_077.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(42), Uint(42)]) used 284 gas +bar([Uint(42), Uint(42)]) used 233 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_078.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_078.snap index 1a2c0fd73d..0c8fd1dd67 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_078.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_078.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639894), Int(115792089237316195423570985008687907853269984665640564039457584007913129639894)]) used 613 gas +bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639894), Int(115792089237316195423570985008687907853269984665640564039457584007913129639894)]) used 429 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_079.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_079.snap index b7d6381fd7..f08ab57b01 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_079.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_079.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639894), Int(42)]) used 613 gas +bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639894), Int(42)]) used 429 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_08.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_08.snap index 5918933651..551db1c702 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_08.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_08.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -rshift([Uint(128), Uint(7)]) used 462 gas +rshift([Uint(128), Uint(7)]) used 448 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_080.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_080.snap index f954096cf4..6d6677e710 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_080.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_080.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(42), Uint(42)]) used 255 gas +bar([Uint(42), Uint(42)]) used 210 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_081.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_081.snap index 30d883f063..8143a6f1b6 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_081.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_081.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639894), Int(115792089237316195423570985008687907853269984665640564039457584007913129639894)]) used 413 gas +bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639894), Int(115792089237316195423570985008687907853269984665640564039457584007913129639894)]) used 249 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_082.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_082.snap index ac8fa38222..9f79fda04e 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_082.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_082.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639935), Int(1)]) used 413 gas +bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639935), Int(1)]) used 249 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_083.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_083.snap index 99ce008dc9..14b3112391 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_083.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_083.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639894), Int(42)]) used 413 gas +bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639894), Int(42)]) used 249 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_084.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_084.snap index e914d5aea9..397e049186 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_084.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_084.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(2), Uint(0)]) used 270 gas +bar([Uint(2), Uint(0)]) used 227 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_085.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_085.snap index 7eedba4563..a1965d8982 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_085.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_085.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(2), Uint(4)]) used 359 gas +bar([Uint(2), Uint(4)]) used 316 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_086.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_086.snap index 8ffdcb8bcf..1e0be4b757 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_086.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_086.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639934), Uint(3)]) used 696 gas +bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639934), Uint(3)]) used 670 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_087.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_087.snap index 0643250fe3..9ab41ddc5e 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_087.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_087.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(5), Uint(2)]) used 255 gas +bar([Uint(5), Uint(2)]) used 210 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_088.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_088.snap index b16026ab28..04fea1f17d 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_088.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_088.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(5), Uint(3)]) used 255 gas +bar([Uint(5), Uint(3)]) used 210 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_089.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_089.snap index 5ddabbae28..f41f9bc01b 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_089.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_089.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(5), Uint(5)]) used 255 gas +bar([Uint(5), Uint(5)]) used 210 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_09.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_09.snap index 7947ca1591..f86d2bf44f 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_09.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_09.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bit_or([Uint(26), Uint(42)]) used 484 gas +bit_or([Uint(26), Uint(42)]) used 473 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_090.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_090.snap index 2c2fc467ce..587474cf40 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_090.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_090.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(5), Int(2)]) used 374 gas +bar([Int(5), Int(2)]) used 210 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_091.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_091.snap index ddb98b214a..3c1003f3e6 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_091.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_091.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(5), Int(3)]) used 374 gas +bar([Int(5), Int(3)]) used 210 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_092.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_092.snap index 43673e2d44..9ba7f270ce 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_092.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_092.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(5), Int(5)]) used 374 gas +bar([Int(5), Int(5)]) used 210 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_093.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_093.snap index 7a024d4d87..68ac6be2ed 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_093.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_093.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(12), Uint(25)]) used 228 gas +bar([Uint(12), Uint(25)]) used 162 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_094.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_094.snap index 9b11d91919..a5d3f420c9 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_094.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_094.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(12), Uint(25)]) used 261 gas +bar([Uint(12), Uint(25)]) used 218 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_095.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_095.snap index 7a024d4d87..68ac6be2ed 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_095.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_095.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(12), Uint(25)]) used 228 gas +bar([Uint(12), Uint(25)]) used 162 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_096.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_096.snap index 7a024d4d87..68ac6be2ed 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_096.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_096.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(12), Uint(25)]) used 228 gas +bar([Uint(12), Uint(25)]) used 162 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_097.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_097.snap index b3edc09b58..3d87637306 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_097.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_097.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(1), Uint(8)]) used 282 gas +bar([Uint(1), Uint(8)]) used 221 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_098.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_098.snap index a201fea89a..4d731bd183 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_098.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_098.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639935), Uint(1)]) used 316 gas +bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639935), Uint(1)]) used 247 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_099.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_099.snap index 9fca76d1ce..7fa1d8ff7d 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_099.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_099.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(212), Uint(0)]) used 228 gas +bar([Uint(212), Uint(0)]) used 162 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_1-2.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_1-2.snap index 8717e399e6..b67a0684ba 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_1-2.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_1-2.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -i8_array([FixedArray([Int(115792089237316195423570985008687907853269984665640564039457584007913129639926), Int(100), Int(115792089237316195423570985008687907853269984665640564039457584007913129639808), Int(127)])]) used 1714 gas +i8_array([FixedArray([Int(115792089237316195423570985008687907853269984665640564039457584007913129639926), Int(100), Int(115792089237316195423570985008687907853269984665640564039457584007913129639808), Int(127)])]) used 1183 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_1-3.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_1-3.snap index 485f419967..ab964b3bad 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_1-3.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_1-3.snap @@ -1,10 +1,9 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -write_bar([Uint(4), Uint(42)]) used 22530 gas -write_bar([Uint(26), Uint(12)]) used 22530 gas -read_bar([Uint(4)]) used 488 gas -read_bar([Uint(26)]) used 488 gas +write_bar([Uint(4), Uint(42)]) used 22410 gas +write_bar([Uint(26), Uint(12)]) used 22410 gas +read_bar([Uint(4)]) used 394 gas +read_bar([Uint(26)]) used 394 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_1.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_1.snap index c4f7117ae7..67ed6492c9 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_1.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_1.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639926)]) used 22857 gas +bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639926)]) used 22659 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_10.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_10.snap index dc3dae2c4f..b1220aceb0 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_10.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_10.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bit_xor([Uint(26), Uint(42)]) used 506 gas +bit_xor([Uint(26), Uint(42)]) used 495 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_100.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_100.snap index e311525c8f..1576dbfe19 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_100.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_100.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(212), Uint(1)]) used 228 gas +bar([Uint(212), Uint(1)]) used 162 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_101.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_101.snap index 9fca76d1ce..7fa1d8ff7d 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_101.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_101.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(212), Uint(0)]) used 228 gas +bar([Uint(212), Uint(0)]) used 162 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_102.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_102.snap index e311525c8f..1576dbfe19 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_102.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_102.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(212), Uint(1)]) used 228 gas +bar([Uint(212), Uint(1)]) used 162 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_103.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_103.snap index 2d9c88f69b..9a2be30c0f 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_103.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_103.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(212), Uint(0)]) used 262 gas +bar([Int(212), Uint(0)]) used 162 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_104.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_104.snap index 3caaeac7cd..62c22cab0e 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_104.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_104.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(212), Uint(1)]) used 262 gas +bar([Int(212), Uint(1)]) used 162 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_105.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_105.snap index 8e7f946e5c..56bd5e2cb8 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_105.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_105.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([]) used 166 gas +bar([]) used 105 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_106.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_106.snap index 01b95503b4..35866de662 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_106.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_106.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(1), Uint(1)]) used 228 gas +bar([Uint(1), Uint(1)]) used 168 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_107.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_107.snap index b0bf135b3b..c355d484c6 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_107.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_107.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(1), Uint(2)]) used 228 gas +bar([Uint(1), Uint(2)]) used 168 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_108.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_108.snap index ed8fed113d..b0d7484155 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_108.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_108.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(1), Uint(1)]) used 231 gas +bar([Uint(1), Uint(1)]) used 171 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_109.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_109.snap index de962c5459..dd7f4e7ddc 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_109.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_109.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(1), Uint(2)]) used 231 gas +bar([Uint(1), Uint(2)]) used 171 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_11.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_11.snap index 70dfce827e..65b4ef2ae8 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_11.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_11.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bit_and([Uint(26), Uint(42)]) used 528 gas +bit_and([Uint(26), Uint(42)]) used 517 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_110.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_110.snap index b0bf135b3b..c355d484c6 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_110.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_110.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(1), Uint(2)]) used 228 gas +bar([Uint(1), Uint(2)]) used 168 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_111.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_111.snap index 01b95503b4..35866de662 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_111.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_111.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(1), Uint(1)]) used 228 gas +bar([Uint(1), Uint(1)]) used 168 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_112.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_112.snap index 1de6f5b33f..f3cea2d492 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_112.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_112.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(2), Uint(1)]) used 228 gas +bar([Uint(2), Uint(1)]) used 168 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_113.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_113.snap index d2b85bbb63..171299446b 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_113.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_113.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(1), Uint(2)]) used 261 gas +bar([Uint(1), Uint(2)]) used 218 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_114.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_114.snap index 894a814090..5ed02bdbcc 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_114.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_114.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(1), Int(2)]) used 321 gas +bar([Int(1), Int(2)]) used 168 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_115.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_115.snap index a7b40392c1..db81b0a261 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_115.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_115.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(1), Int(1)]) used 321 gas +bar([Int(1), Int(1)]) used 168 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_116.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_116.snap index 1a0c35c1cc..c44d209fef 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_116.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_116.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(2), Int(1)]) used 321 gas +bar([Int(2), Int(1)]) used 168 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_117.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_117.snap index 17cfae937d..e7db133812 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_117.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_117.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639934), Int(115792089237316195423570985008687907853269984665640564039457584007913129639935)]) used 321 gas +bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639934), Int(115792089237316195423570985008687907853269984665640564039457584007913129639935)]) used 168 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_118.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_118.snap index 740b831c06..e4649e7356 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_118.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_118.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639935), Int(115792089237316195423570985008687907853269984665640564039457584007913129639935)]) used 321 gas +bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639935), Int(115792089237316195423570985008687907853269984665640564039457584007913129639935)]) used 168 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_119.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_119.snap index 1ffd9cdee0..372ce6ee24 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_119.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_119.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639935), Int(115792089237316195423570985008687907853269984665640564039457584007913129639934)]) used 321 gas +bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639935), Int(115792089237316195423570985008687907853269984665640564039457584007913129639934)]) used 168 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_12.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_12.snap index 2249c88d94..5f54d6a4d9 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_12.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_12.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -add_from_sto([Uint(2), Uint(5)]) used 22803 gas +add_from_sto([Uint(2), Uint(5)]) used 22729 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_120.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_120.snap index de962c5459..dd7f4e7ddc 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_120.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_120.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(1), Uint(2)]) used 231 gas +bar([Uint(1), Uint(2)]) used 171 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_121.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_121.snap index ed8fed113d..b0d7484155 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_121.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_121.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(1), Uint(1)]) used 231 gas +bar([Uint(1), Uint(1)]) used 171 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_122.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_122.snap index c05ce189fc..25a6930cca 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_122.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_122.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(2), Uint(1)]) used 231 gas +bar([Uint(2), Uint(1)]) used 171 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_123.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_123.snap index afa2594af0..9601e51452 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_123.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_123.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(1), Int(2)]) used 324 gas +bar([Int(1), Int(2)]) used 171 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_124.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_124.snap index 381ffffa09..0708f2bca1 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_124.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_124.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(1), Int(1)]) used 324 gas +bar([Int(1), Int(1)]) used 171 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_125.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_125.snap index 630fcb6421..dc378943cc 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_125.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_125.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(2), Int(1)]) used 324 gas +bar([Int(2), Int(1)]) used 171 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_126.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_126.snap index ee4b1a20f4..41aa63e115 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_126.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_126.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639934), Int(115792089237316195423570985008687907853269984665640564039457584007913129639935)]) used 324 gas +bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639934), Int(115792089237316195423570985008687907853269984665640564039457584007913129639935)]) used 171 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_127.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_127.snap index d3cc7fa90f..afcf56eb8d 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_127.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_127.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639935), Int(115792089237316195423570985008687907853269984665640564039457584007913129639935)]) used 324 gas +bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639935), Int(115792089237316195423570985008687907853269984665640564039457584007913129639935)]) used 171 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_128.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_128.snap index cb0ba6b6d5..6be03ebb92 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_128.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_128.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639935), Int(115792089237316195423570985008687907853269984665640564039457584007913129639934)]) used 324 gas +bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639935), Int(115792089237316195423570985008687907853269984665640564039457584007913129639934)]) used 171 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_129.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_129.snap index 1de6f5b33f..f3cea2d492 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_129.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_129.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(2), Uint(1)]) used 228 gas +bar([Uint(2), Uint(1)]) used 168 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_13.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_13.snap index dfd14bb3a2..0a8343e2a1 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_13.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_13.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -add_from_mem([Uint(2), Uint(5)]) used 686 gas +add_from_mem([Uint(2), Uint(5)]) used 643 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_130.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_130.snap index 01b95503b4..35866de662 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_130.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_130.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(1), Uint(1)]) used 228 gas +bar([Uint(1), Uint(1)]) used 168 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_131.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_131.snap index b0bf135b3b..c355d484c6 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_131.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_131.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(1), Uint(2)]) used 228 gas +bar([Uint(1), Uint(2)]) used 168 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_132.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_132.snap index 1a0c35c1cc..c44d209fef 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_132.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_132.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(2), Int(1)]) used 321 gas +bar([Int(2), Int(1)]) used 168 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_133.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_133.snap index a7b40392c1..db81b0a261 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_133.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_133.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(1), Int(1)]) used 321 gas +bar([Int(1), Int(1)]) used 168 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_134.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_134.snap index 894a814090..5ed02bdbcc 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_134.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_134.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(1), Int(2)]) used 321 gas +bar([Int(1), Int(2)]) used 168 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_135.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_135.snap index 1ffd9cdee0..372ce6ee24 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_135.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_135.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639935), Int(115792089237316195423570985008687907853269984665640564039457584007913129639934)]) used 321 gas +bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639935), Int(115792089237316195423570985008687907853269984665640564039457584007913129639934)]) used 168 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_136.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_136.snap index 740b831c06..e4649e7356 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_136.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_136.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639935), Int(115792089237316195423570985008687907853269984665640564039457584007913129639935)]) used 321 gas +bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639935), Int(115792089237316195423570985008687907853269984665640564039457584007913129639935)]) used 168 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_137.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_137.snap index 17cfae937d..e7db133812 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_137.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_137.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639934), Int(115792089237316195423570985008687907853269984665640564039457584007913129639935)]) used 321 gas +bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639934), Int(115792089237316195423570985008687907853269984665640564039457584007913129639935)]) used 168 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_138.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_138.snap index c05ce189fc..25a6930cca 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_138.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_138.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(2), Uint(1)]) used 231 gas +bar([Uint(2), Uint(1)]) used 171 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_139.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_139.snap index ed8fed113d..b0d7484155 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_139.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_139.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(1), Uint(1)]) used 231 gas +bar([Uint(1), Uint(1)]) used 171 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_140.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_140.snap index de962c5459..dd7f4e7ddc 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_140.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_140.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(1), Uint(2)]) used 231 gas +bar([Uint(1), Uint(2)]) used 171 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_141.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_141.snap index 630fcb6421..dc378943cc 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_141.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_141.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(2), Int(1)]) used 324 gas +bar([Int(2), Int(1)]) used 171 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_142.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_142.snap index 381ffffa09..0708f2bca1 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_142.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_142.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(1), Int(1)]) used 324 gas +bar([Int(1), Int(1)]) used 171 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_143.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_143.snap index afa2594af0..9601e51452 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_143.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_143.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(1), Int(2)]) used 324 gas +bar([Int(1), Int(2)]) used 171 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_144.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_144.snap index cb0ba6b6d5..6be03ebb92 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_144.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_144.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639935), Int(115792089237316195423570985008687907853269984665640564039457584007913129639934)]) used 324 gas +bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639935), Int(115792089237316195423570985008687907853269984665640564039457584007913129639934)]) used 171 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_145.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_145.snap index d3cc7fa90f..afcf56eb8d 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_145.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_145.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639935), Int(115792089237316195423570985008687907853269984665640564039457584007913129639935)]) used 324 gas +bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639935), Int(115792089237316195423570985008687907853269984665640564039457584007913129639935)]) used 171 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_146.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_146.snap index ee4b1a20f4..41aa63e115 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_146.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_146.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639934), Int(115792089237316195423570985008687907853269984665640564039457584007913129639935)]) used 324 gas +bar([Int(115792089237316195423570985008687907853269984665640564039457584007913129639934), Int(115792089237316195423570985008687907853269984665640564039457584007913129639935)]) used 171 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_147.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_147.snap index 00c3b19121..0a28158165 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_147.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_147.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Bool(true), Bool(true)]) used 299 gas +bar([Bool(true), Bool(true)]) used 278 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_148.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_148.snap index ffa0ce9b13..caec559727 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_148.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_148.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Bool(true), Bool(false)]) used 299 gas +bar([Bool(true), Bool(false)]) used 278 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_149.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_149.snap index 480b71104e..896b69f87b 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_149.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_149.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Bool(false), Bool(true)]) used 280 gas +bar([Bool(false), Bool(true)]) used 285 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_150.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_150.snap index 71e6cbf723..77118a7e3c 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_150.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_150.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Bool(false), Bool(false)]) used 280 gas +bar([Bool(false), Bool(false)]) used 285 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_151.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_151.snap index 2c26c8dbf2..f76f10a941 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_151.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_151.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Bool(true), Bool(true)]) used 283 gas +bar([Bool(true), Bool(true)]) used 275 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_152.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_152.snap index f0a9979f26..0c46db6681 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_152.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_152.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Bool(true), Bool(false)]) used 283 gas +bar([Bool(true), Bool(false)]) used 275 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_153.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_153.snap index 592de4bf29..e7e8a0d4ff 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_153.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_153.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Bool(false), Bool(true)]) used 302 gas +bar([Bool(false), Bool(true)]) used 296 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_154.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_154.snap index 0adcf3c07a..6111b3c7e0 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_154.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_154.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Bool(false), Bool(false)]) used 302 gas +bar([Bool(false), Bool(false)]) used 296 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_155.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_155.snap index f789dbcda3..258d191e3d 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_155.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_155.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([]) used 169 gas +bar([]) used 102 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_156.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_156.snap index f789dbcda3..258d191e3d 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_156.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_156.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([]) used 169 gas +bar([]) used 102 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_157.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_157.snap index f789dbcda3..258d191e3d 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_157.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_157.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([]) used 169 gas +bar([]) used 102 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_158_map_tuple.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_158_map_tuple.snap index 9d7f053660..6ac9179f2d 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_158_map_tuple.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_158_map_tuple.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(1234)]) used 45148 gas +bar([Uint(1234)]) used 45061 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_159_int_literal_coercion.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_159_int_literal_coercion.snap index 02b2cf54cd..ad178e8221 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_159_int_literal_coercion.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_159_int_literal_coercion.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([]) used 320 gas +bar([]) used 299 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_160_associated_fns.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_160_associated_fns.snap index c2b017a47a..c9bcfcdaee 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_160_associated_fns.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_160_associated_fns.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(12)]) used 22687 gas +bar([Uint(12)]) used 22716 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_161_struct_fns.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_161_struct_fns.snap index 8322b2549a..f2f3ff518f 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_161_struct_fns.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_161_struct_fns.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(10), Uint(20)]) used 1939 gas +bar([Uint(10), Uint(20)]) used 2002 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_162_cast_address_to_u256.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_162_cast_address_to_u256.snap index f4d44b732b..88c6f17388 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_162_cast_address_to_u256.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_162_cast_address_to_u256.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Address(0x2012301230123012301230123012301230123002)]) used 223 gas +bar([Address(0x2012301230123012301230123012301230123002)]) used 201 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_2-2.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_2-2.snap index 75bcf79e15..72b1bb6f20 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_2-2.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_2-2.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -i32_array([FixedArray([Int(115792089237316195423570985008687907853269984665640564039457584007913129639926), Int(100), Int(115792089237316195423570985008687907853269984665640564039457584007910982156288), Int(2147483647)])]) used 1782 gas +i32_array([FixedArray([Int(115792089237316195423570985008687907853269984665640564039457584007913129639926), Int(100), Int(115792089237316195423570985008687907853269984665640564039457584007910982156288), Int(2147483647)])]) used 1246 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_2-3.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_2-3.snap index be6ee4cdd9..2a2c1827d6 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_2-3.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_2-3.snap @@ -1,10 +1,9 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -write_bar([Uint(4), Uint(42)]) used 22611 gas -write_bar([Uint(26), Uint(12)]) used 22611 gas -read_bar([Uint(4)]) used 516 gas -read_bar([Uint(26)]) used 516 gas +write_bar([Uint(4), Uint(42)]) used 22497 gas +write_bar([Uint(26), Uint(12)]) used 22497 gas +read_bar([Uint(4)]) used 473 gas +read_bar([Uint(26)]) used 473 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_2.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_2.snap index 66f6000edd..d8ec4f33f0 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_2.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_2.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Int(100)]) used 22857 gas +bar([Int(100)]) used 22659 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_3.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_3.snap index be6ee4cdd9..7de9796231 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_3.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_3.snap @@ -1,10 +1,9 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -write_bar([Uint(4), Uint(42)]) used 22611 gas -write_bar([Uint(26), Uint(12)]) used 22611 gas -read_bar([Uint(4)]) used 516 gas -read_bar([Uint(26)]) used 516 gas +write_bar([Uint(4), Uint(42)]) used 22485 gas +write_bar([Uint(26), Uint(12)]) used 22485 gas +read_bar([Uint(4)]) used 449 gas +read_bar([Uint(26)]) used 449 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_4.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_4.snap index d7449d84af..7de9796231 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_4.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_4.snap @@ -1,10 +1,9 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -write_bar([Uint(4), Uint(42)]) used 22617 gas -write_bar([Uint(26), Uint(12)]) used 22617 gas -read_bar([Uint(4)]) used 498 gas -read_bar([Uint(26)]) used 498 gas +write_bar([Uint(4), Uint(42)]) used 22485 gas +write_bar([Uint(26), Uint(12)]) used 22485 gas +read_bar([Uint(4)]) used 449 gas +read_bar([Uint(26)]) used 449 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_5.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_5.snap index be6ee4cdd9..7de9796231 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_5.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_5.snap @@ -1,10 +1,9 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -write_bar([Uint(4), Uint(42)]) used 22611 gas -write_bar([Uint(26), Uint(12)]) used 22611 gas -read_bar([Uint(4)]) used 516 gas -read_bar([Uint(26)]) used 516 gas +write_bar([Uint(4), Uint(42)]) used 22485 gas +write_bar([Uint(26), Uint(12)]) used 22485 gas +read_bar([Uint(4)]) used 449 gas +read_bar([Uint(26)]) used 449 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__case_6.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__case_6.snap index be6ee4cdd9..7de9796231 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__case_6.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__case_6.snap @@ -1,10 +1,9 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -write_bar([Uint(4), Uint(42)]) used 22611 gas -write_bar([Uint(26), Uint(12)]) used 22611 gas -read_bar([Uint(4)]) used 516 gas -read_bar([Uint(26)]) used 516 gas +write_bar([Uint(4), Uint(42)]) used 22485 gas +write_bar([Uint(26), Uint(12)]) used 22485 gas +read_bar([Uint(4)]) used 449 gas +read_bar([Uint(26)]) used 449 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__checked_arithmetic.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__checked_arithmetic.snap index 3acc3adddb..55d17837fb 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__checked_arithmetic.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__checked_arithmetic.snap @@ -1,114 +1,113 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -add_u8([Uint(255), Uint(0)]) used 478 gas -add_i8([Int(127), Int(0)]) used 737 gas -add_i8([Int(115792089237316195423570985008687907853269984665640564039457584007913129639808), Int(0)]) used 737 gas -sub_u8([Uint(0), Uint(0)]) used 736 gas -sub_i8([Int(115792089237316195423570985008687907853269984665640564039457584007913129639808), Int(0)]) used 1001 gas -sub_i8([Int(127), Int(0)]) used 1001 gas -div_u8([Uint(3), Uint(2)]) used 999 gas -div_i8([Int(3), Int(115792089237316195423570985008687907853269984665640564039457584007913129639934)]) used 1246 gas -pow_u8([Uint(2), Uint(7)]) used 1911 gas -pow_i8([Int(2), Uint(6)]) used 2438 gas -pow_i8([Int(115792089237316195423570985008687907853269984665640564039457584007913129639934), Uint(7)]) used 2472 gas -mod_u8([Uint(255), Uint(2)]) used 1527 gas -mod_i8([Int(127), Int(2)]) used 1733 gas -mod_i8([Int(13), Int(115792089237316195423570985008687907853269984665640564039457584007913129639933)]) used 1733 gas -mod_i8([Int(115792089237316195423570985008687907853269984665640564039457584007913129639923), Int(3)]) used 1733 gas -mul_u8([Uint(255), Uint(1)]) used 1286 gas -mul_i8([Int(127), Int(1)]) used 1663 gas -mul_i8([Int(115792089237316195423570985008687907853269984665640564039457584007913129639808), Int(1)]) used 1663 gas -add_u16([Uint(65535), Uint(0)]) used 456 gas -add_i16([Int(32767), Int(0)]) used 715 gas -add_i16([Int(115792089237316195423570985008687907853269984665640564039457584007913129607168), Int(0)]) used 715 gas -sub_u16([Uint(0), Uint(0)]) used 714 gas -sub_i16([Int(115792089237316195423570985008687907853269984665640564039457584007913129607168), Int(0)]) used 979 gas -sub_i16([Int(32767), Int(0)]) used 979 gas -div_u16([Uint(3), Uint(2)]) used 977 gas -div_i16([Int(3), Int(115792089237316195423570985008687907853269984665640564039457584007913129639934)]) used 1224 gas -pow_u16([Uint(2), Uint(15)]) used 1895 gas -pow_i16([Int(2), Uint(14)]) used 2558 gas -pow_i16([Int(115792089237316195423570985008687907853269984665640564039457584007913129639934), Uint(15)]) used 2592 gas -mod_u16([Uint(65535), Uint(2)]) used 1505 gas -mod_i16([Int(32767), Int(2)]) used 1711 gas -mod_i16([Int(13), Int(115792089237316195423570985008687907853269984665640564039457584007913129639933)]) used 1711 gas -mod_i16([Int(115792089237316195423570985008687907853269984665640564039457584007913129639923), Int(3)]) used 1711 gas -mul_u16([Uint(65535), Uint(1)]) used 1264 gas -mul_i16([Int(32767), Int(1)]) used 1642 gas -mul_i16([Int(115792089237316195423570985008687907853269984665640564039457584007913129607168), Int(1)]) used 1642 gas -add_u32([Uint(4294967295), Uint(0)]) used 434 gas -add_i32([Int(2147483647), Int(0)]) used 693 gas -add_i32([Int(115792089237316195423570985008687907853269984665640564039457584007910982156288), Int(0)]) used 693 gas -sub_u32([Uint(0), Uint(0)]) used 692 gas -sub_i32([Int(115792089237316195423570985008687907853269984665640564039457584007910982156288), Int(0)]) used 957 gas -sub_i32([Int(2147483647), Int(0)]) used 957 gas -div_u32([Uint(3), Uint(2)]) used 955 gas -div_i32([Int(3), Int(115792089237316195423570985008687907853269984665640564039457584007913129639934)]) used 1202 gas -pow_u32([Uint(2), Uint(31)]) used 1873 gas -pow_i32([Int(2), Uint(30)]) used 2673 gas -pow_i32([Int(115792089237316195423570985008687907853269984665640564039457584007913129639934), Uint(31)]) used 2707 gas -mod_u32([Uint(4294967295), Uint(2)]) used 1483 gas -mod_i32([Int(2147483647), Int(2)]) used 1689 gas -mod_i32([Int(13), Int(115792089237316195423570985008687907853269984665640564039457584007913129639933)]) used 1689 gas -mod_i32([Int(115792089237316195423570985008687907853269984665640564039457584007913129639923), Int(3)]) used 1689 gas -mul_u32([Uint(4294967295), Uint(1)]) used 1242 gas -mul_i32([Int(2147483647), Int(1)]) used 1620 gas -mul_i32([Int(115792089237316195423570985008687907853269984665640564039457584007910982156288), Int(1)]) used 1620 gas -add_u64([Uint(18446744073709551615), Uint(0)]) used 412 gas -add_i64([Int(9223372036854775807), Int(0)]) used 671 gas -add_i64([Int(115792089237316195423570985008687907853269984665640564039448360635876274864128), Int(0)]) used 671 gas -sub_u64([Uint(0), Uint(0)]) used 670 gas -sub_i64([Int(115792089237316195423570985008687907853269984665640564039448360635876274864128), Int(0)]) used 935 gas -sub_i64([Int(9223372036854775807), Int(0)]) used 935 gas -div_u64([Uint(3), Uint(2)]) used 933 gas -div_i64([Int(3), Int(115792089237316195423570985008687907853269984665640564039457584007913129639934)]) used 1180 gas -pow_u64([Uint(2), Uint(63)]) used 1851 gas -pow_i64([Int(2), Uint(62)]) used 2788 gas -pow_i64([Int(115792089237316195423570985008687907853269984665640564039457584007913129639934), Uint(63)]) used 2822 gas -mod_u64([Uint(18446744073709551615), Uint(2)]) used 1461 gas -mod_i64([Int(9223372036854775807), Int(2)]) used 1667 gas -mod_i64([Int(13), Int(115792089237316195423570985008687907853269984665640564039457584007913129639933)]) used 1667 gas -mod_i64([Int(115792089237316195423570985008687907853269984665640564039457584007913129639923), Int(3)]) used 1667 gas -mul_u64([Uint(18446744073709551615), Uint(1)]) used 1220 gas -mul_i64([Int(9223372036854775807), Int(1)]) used 1598 gas -mul_i64([Int(115792089237316195423570985008687907853269984665640564039448360635876274864128), Int(1)]) used 1598 gas -add_u128([Uint(340282366920938463463374607431768211455), Uint(0)]) used 402 gas -add_i128([Int(170141183460469231731687303715884105727), Int(0)]) used 673 gas -add_i128([Int(115792089237316195423570985008687907853099843482180094807725896704197245534208), Int(0)]) used 673 gas -sub_u128([Uint(0), Uint(0)]) used 648 gas -sub_i128([Int(115792089237316195423570985008687907853099843482180094807725896704197245534208), Int(0)]) used 937 gas -sub_i128([Int(170141183460469231731687303715884105727), Int(0)]) used 937 gas -div_u128([Uint(3), Uint(2)]) used 911 gas -div_i128([Int(3), Int(115792089237316195423570985008687907853269984665640564039457584007913129639934)]) used 1170 gas -pow_u128([Uint(2), Uint(127)]) used 1841 gas -pow_i128([Int(2), Uint(126)]) used 2999 gas -pow_i128([Int(115792089237316195423570985008687907853269984665640564039457584007913129639934), Uint(127)]) used 3033 gas -mod_u128([Uint(340282366920938463463374607431768211455), Uint(2)]) used 1439 gas -mod_i128([Int(170141183460469231731687303715884105727), Int(2)]) used 1645 gas -mod_i128([Int(13), Int(115792089237316195423570985008687907853269984665640564039457584007913129639933)]) used 1645 gas -mod_i128([Int(115792089237316195423570985008687907853269984665640564039457584007913129639923), Int(3)]) used 1645 gas -mul_u128([Uint(340282366920938463463374607431768211455), Uint(1)]) used 1210 gas -mul_i128([Int(170141183460469231731687303715884105727), Int(1)]) used 1602 gas -mul_i128([Int(115792089237316195423570985008687907853099843482180094807725896704197245534208), Int(1)]) used 1602 gas -add_u256([Uint(115792089237316195423570985008687907853269984665640564039457584007913129639935), Uint(0)]) used 314 gas -add_i256([Int(57896044618658097711785492504343953926634992332820282019728792003956564819967), Int(0)]) used 648 gas -add_i256([Int(57896044618658097711785492504343953926634992332820282019728792003956564819968), Int(0)]) used 648 gas -sub_u256([Uint(0), Uint(0)]) used 575 gas -sub_i256([Int(57896044618658097711785492504343953926634992332820282019728792003956564819968), Int(0)]) used 906 gas -sub_i256([Int(57896044618658097711785492504343953926634992332820282019728792003956564819967), Int(0)]) used 906 gas -div_u256([Uint(3), Uint(2)]) used 838 gas -div_i256([Int(3), Int(115792089237316195423570985008687907853269984665640564039457584007913129639934)]) used 1139 gas -pow_u256([Uint(2), Uint(255)]) used 1733 gas -pow_i256([Int(2), Uint(254)]) used 3116 gas -pow_i256([Int(115792089237316195423570985008687907853269984665640564039457584007913129639934), Uint(255)]) used 3150 gas -mod_u256([Uint(115792089237316195423570985008687907853269984665640564039457584007913129639935), Uint(2)]) used 1366 gas -mod_i256([Int(57896044618658097711785492504343953926634992332820282019728792003956564819967), Int(2)]) used 1623 gas -mod_i256([Int(13), Int(115792089237316195423570985008687907853269984665640564039457584007913129639933)]) used 1623 gas -mod_i256([Int(115792089237316195423570985008687907853269984665640564039457584007913129639923), Int(3)]) used 1623 gas -mul_u256([Uint(115792089237316195423570985008687907853269984665640564039457584007913129639935), Uint(1)]) used 1128 gas -mul_i256([Int(57896044618658097711785492504343953926634992332820282019728792003956564819967), Int(1)]) used 1569 gas -mul_i256([Int(57896044618658097711785492504343953926634992332820282019728792003956564819968), Int(1)]) used 1569 gas +add_u8([Uint(255), Uint(0)]) used 452 gas +add_i8([Int(127), Int(0)]) used 716 gas +add_i8([Int(115792089237316195423570985008687907853269984665640564039457584007913129639808), Int(0)]) used 716 gas +sub_u8([Uint(0), Uint(0)]) used 710 gas +sub_i8([Int(115792089237316195423570985008687907853269984665640564039457584007913129639808), Int(0)]) used 980 gas +sub_i8([Int(127), Int(0)]) used 980 gas +div_u8([Uint(3), Uint(2)]) used 973 gas +div_i8([Int(3), Int(115792089237316195423570985008687907853269984665640564039457584007913129639934)]) used 1225 gas +pow_u8([Uint(2), Uint(7)]) used 1885 gas +pow_i8([Int(2), Uint(6)]) used 2423 gas +pow_i8([Int(115792089237316195423570985008687907853269984665640564039457584007913129639934), Uint(7)]) used 2460 gas +mod_u8([Uint(255), Uint(2)]) used 1501 gas +mod_i8([Int(127), Int(2)]) used 1712 gas +mod_i8([Int(13), Int(115792089237316195423570985008687907853269984665640564039457584007913129639933)]) used 1712 gas +mod_i8([Int(115792089237316195423570985008687907853269984665640564039457584007913129639923), Int(3)]) used 1712 gas +mul_u8([Uint(255), Uint(1)]) used 1260 gas +mul_i8([Int(127), Int(1)]) used 1642 gas +mul_i8([Int(115792089237316195423570985008687907853269984665640564039457584007913129639808), Int(1)]) used 1642 gas +add_u16([Uint(65535), Uint(0)]) used 429 gas +add_i16([Int(32767), Int(0)]) used 696 gas +add_i16([Int(115792089237316195423570985008687907853269984665640564039457584007913129607168), Int(0)]) used 696 gas +sub_u16([Uint(0), Uint(0)]) used 687 gas +sub_i16([Int(115792089237316195423570985008687907853269984665640564039457584007913129607168), Int(0)]) used 960 gas +sub_i16([Int(32767), Int(0)]) used 960 gas +div_u16([Uint(3), Uint(2)]) used 950 gas +div_i16([Int(3), Int(115792089237316195423570985008687907853269984665640564039457584007913129639934)]) used 1205 gas +pow_u16([Uint(2), Uint(15)]) used 1868 gas +pow_i16([Int(2), Uint(14)]) used 2545 gas +pow_i16([Int(115792089237316195423570985008687907853269984665640564039457584007913129639934), Uint(15)]) used 2582 gas +mod_u16([Uint(65535), Uint(2)]) used 1478 gas +mod_i16([Int(32767), Int(2)]) used 1692 gas +mod_i16([Int(13), Int(115792089237316195423570985008687907853269984665640564039457584007913129639933)]) used 1692 gas +mod_i16([Int(115792089237316195423570985008687907853269984665640564039457584007913129639923), Int(3)]) used 1692 gas +mul_u16([Uint(65535), Uint(1)]) used 1237 gas +mul_i16([Int(32767), Int(1)]) used 1623 gas +mul_i16([Int(115792089237316195423570985008687907853269984665640564039457584007913129607168), Int(1)]) used 1623 gas +add_u32([Uint(4294967295), Uint(0)]) used 408 gas +add_i32([Int(2147483647), Int(0)]) used 674 gas +add_i32([Int(115792089237316195423570985008687907853269984665640564039457584007910982156288), Int(0)]) used 674 gas +sub_u32([Uint(0), Uint(0)]) used 666 gas +sub_i32([Int(115792089237316195423570985008687907853269984665640564039457584007910982156288), Int(0)]) used 938 gas +sub_i32([Int(2147483647), Int(0)]) used 938 gas +div_u32([Uint(3), Uint(2)]) used 929 gas +div_i32([Int(3), Int(115792089237316195423570985008687907853269984665640564039457584007913129639934)]) used 1183 gas +pow_u32([Uint(2), Uint(31)]) used 1847 gas +pow_i32([Int(2), Uint(30)]) used 2660 gas +pow_i32([Int(115792089237316195423570985008687907853269984665640564039457584007913129639934), Uint(31)]) used 2697 gas +mod_u32([Uint(4294967295), Uint(2)]) used 1457 gas +mod_i32([Int(2147483647), Int(2)]) used 1670 gas +mod_i32([Int(13), Int(115792089237316195423570985008687907853269984665640564039457584007913129639933)]) used 1670 gas +mod_i32([Int(115792089237316195423570985008687907853269984665640564039457584007913129639923), Int(3)]) used 1670 gas +mul_u32([Uint(4294967295), Uint(1)]) used 1216 gas +mul_i32([Int(2147483647), Int(1)]) used 1601 gas +mul_i32([Int(115792089237316195423570985008687907853269984665640564039457584007910982156288), Int(1)]) used 1601 gas +add_u64([Uint(18446744073709551615), Uint(0)]) used 385 gas +add_i64([Int(9223372036854775807), Int(0)]) used 652 gas +add_i64([Int(115792089237316195423570985008687907853269984665640564039448360635876274864128), Int(0)]) used 652 gas +sub_u64([Uint(0), Uint(0)]) used 643 gas +sub_i64([Int(115792089237316195423570985008687907853269984665640564039448360635876274864128), Int(0)]) used 916 gas +sub_i64([Int(9223372036854775807), Int(0)]) used 916 gas +div_u64([Uint(3), Uint(2)]) used 906 gas +div_i64([Int(3), Int(115792089237316195423570985008687907853269984665640564039457584007913129639934)]) used 1161 gas +pow_u64([Uint(2), Uint(63)]) used 1824 gas +pow_i64([Int(2), Uint(62)]) used 2775 gas +pow_i64([Int(115792089237316195423570985008687907853269984665640564039457584007913129639934), Uint(63)]) used 2812 gas +mod_u64([Uint(18446744073709551615), Uint(2)]) used 1434 gas +mod_i64([Int(9223372036854775807), Int(2)]) used 1648 gas +mod_i64([Int(13), Int(115792089237316195423570985008687907853269984665640564039457584007913129639933)]) used 1648 gas +mod_i64([Int(115792089237316195423570985008687907853269984665640564039457584007913129639923), Int(3)]) used 1648 gas +mul_u64([Uint(18446744073709551615), Uint(1)]) used 1193 gas +mul_i64([Int(9223372036854775807), Int(1)]) used 1579 gas +mul_i64([Int(115792089237316195423570985008687907853269984665640564039448360635876274864128), Int(1)]) used 1579 gas +add_u128([Uint(340282366920938463463374607431768211455), Uint(0)]) used 400 gas +add_i128([Int(170141183460469231731687303715884105727), Int(0)]) used 654 gas +add_i128([Int(115792089237316195423570985008687907853099843482180094807725896704197245534208), Int(0)]) used 654 gas +sub_u128([Uint(0), Uint(0)]) used 646 gas +sub_i128([Int(115792089237316195423570985008687907853099843482180094807725896704197245534208), Int(0)]) used 918 gas +sub_i128([Int(170141183460469231731687303715884105727), Int(0)]) used 918 gas +div_u128([Uint(3), Uint(2)]) used 909 gas +div_i128([Int(3), Int(115792089237316195423570985008687907853269984665640564039457584007913129639934)]) used 1151 gas +pow_u128([Uint(2), Uint(127)]) used 1839 gas +pow_i128([Int(2), Uint(126)]) used 2986 gas +pow_i128([Int(115792089237316195423570985008687907853269984665640564039457584007913129639934), Uint(127)]) used 3023 gas +mod_u128([Uint(340282366920938463463374607431768211455), Uint(2)]) used 1437 gas +mod_i128([Int(170141183460469231731687303715884105727), Int(2)]) used 1626 gas +mod_i128([Int(13), Int(115792089237316195423570985008687907853269984665640564039457584007913129639933)]) used 1626 gas +mod_i128([Int(115792089237316195423570985008687907853269984665640564039457584007913129639923), Int(3)]) used 1626 gas +mul_u128([Uint(340282366920938463463374607431768211455), Uint(1)]) used 1208 gas +mul_i128([Int(170141183460469231731687303715884105727), Int(1)]) used 1581 gas +mul_i128([Int(115792089237316195423570985008687907853099843482180094807725896704197245534208), Int(1)]) used 1581 gas +add_u256([Uint(115792089237316195423570985008687907853269984665640564039457584007913129639935), Uint(0)]) used 279 gas +add_i256([Int(57896044618658097711785492504343953926634992332820282019728792003956564819967), Int(0)]) used 488 gas +add_i256([Int(57896044618658097711785492504343953926634992332820282019728792003956564819968), Int(0)]) used 488 gas +sub_u256([Uint(0), Uint(0)]) used 540 gas +sub_i256([Int(57896044618658097711785492504343953926634992332820282019728792003956564819968), Int(0)]) used 746 gas +sub_i256([Int(57896044618658097711785492504343953926634992332820282019728792003956564819967), Int(0)]) used 746 gas +div_u256([Uint(3), Uint(2)]) used 803 gas +div_i256([Int(3), Int(115792089237316195423570985008687907853269984665640564039457584007913129639934)]) used 979 gas +pow_u256([Uint(2), Uint(255)]) used 1698 gas +pow_i256([Int(2), Uint(254)]) used 3014 gas +pow_i256([Int(115792089237316195423570985008687907853269984665640564039457584007913129639934), Uint(255)]) used 3051 gas +mod_u256([Uint(115792089237316195423570985008687907853269984665640564039457584007913129639935), Uint(2)]) used 1331 gas +mod_i256([Int(57896044618658097711785492504343953926634992332820282019728792003956564819967), Int(2)]) used 1463 gas +mod_i256([Int(13), Int(115792089237316195423570985008687907853269984665640564039457584007913129639933)]) used 1463 gas +mod_i256([Int(115792089237316195423570985008687907853269984665640564039457584007913129639923), Int(3)]) used 1463 gas +mul_u256([Uint(115792089237316195423570985008687907853269984665640564039457584007913129639935), Uint(1)]) used 1093 gas +mul_i256([Int(57896044618658097711785492504343953926634992332820282019728792003956564819967), Int(1)]) used 1411 gas +mul_i256([Int(57896044618658097711785492504343953926634992332820282019728792003956564819968), Int(1)]) used 1411 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__constructor.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__constructor.snap index ea2c6f3d7b..95ccc3663d 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__constructor.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__constructor.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -read_bar([]) used 383 gas +read_bar([]) used 295 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__create2_contract.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__create2_contract.snap index c246e4929c..e75cfe7b46 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__create2_contract.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__create2_contract.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", factory_harness.gas_reporter)" - --- -create2_foo([]) used 45065 gas +create2_foo([]) used 40474 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__create_contract.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__create_contract.snap index b8d5569224..095f493576 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__create_contract.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__create_contract.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", factory_harness.gas_reporter)" - --- -create_foo([]) used 45044 gas +create_foo([]) used 40459 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__create_contract_from_init.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__create_contract_from_init.snap index 7089f5f5f8..0499ec9aed 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__create_contract_from_init.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__create_contract_from_init.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", factory_harness.gas_reporter)" - --- -get_foo_addr([]) used 266 gas +get_foo_addr([]) used 211 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__ctx_param_external_func_call.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__ctx_param_external_func_call.snap index 63491e43ad..eb8714cf56 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__ctx_param_external_func_call.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__ctx_param_external_func_call.snap @@ -1,8 +1,7 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -call_bing([]) used 96464 gas -call_baz([Address(0x5f8bd49cd9f0cb2bd5bb9d4320dfe9b61023249d)]) used 824 gas +call_bing([]) used 80874 gas +call_baz([Address(0x5f8bd49cd9f0cb2bd5bb9d4320dfe9b61023249d)]) used 729 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__ctx_param_internal_func_call.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__ctx_param_internal_func_call.snap index 0dc1035ebf..64762e4b98 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__ctx_param_internal_func_call.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__ctx_param_internal_func_call.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([]) used 2389 gas +bar([]) used 2328 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__ctx_param_simple.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__ctx_param_simple.snap index 05817578f4..51edf0384c 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__ctx_param_simple.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__ctx_param_simple.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([]) used 162 gas +bar([]) used 101 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__events.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__events.snap index a9bc57695a..7331022eaa 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__events.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__events.snap @@ -1,10 +1,9 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -emit_nums([]) used 1576 gas -emit_bases([Address(0x1234000000000000000000000000000000005678)]) used 1625 gas -emit_mix([Address(0x1234000000000000000000000000000000005678), Bytes([116, 101, 110, 32, 98, 121, 116, 101, 115, 46, 116, 101, 110, 32, 98, 121, 116, 101, 115, 46, 116, 101, 110, 32, 98, 121, 116, 101, 115, 46, 116, 101, 110, 32, 98, 121, 116, 101, 115, 46, 116, 101, 110, 32, 98, 121, 116, 101, 115, 46, 116, 101, 110, 32, 98, 121, 116, 101, 115, 46, 116, 101, 110, 32, 98, 121, 116, 101, 115, 46, 116, 101, 110, 32, 98, 121, 116, 101, 115, 46, 116, 101, 110, 32, 98, 121, 116, 101, 115, 46, 116, 101, 110, 32, 98, 121, 116, 101, 115, 46])]) used 4567 gas -emit_addresses([Address(0x1234000000000000000000000000000000005678), Address(0x9123000000000000000000000000000000004567)]) used 1938 gas +emit_nums([]) used 1600 gas +emit_bases([Address(0x1234000000000000000000000000000000005678)]) used 1676 gas +emit_mix([Address(0x1234000000000000000000000000000000005678), Bytes([116, 101, 110, 32, 98, 121, 116, 101, 115, 46, 116, 101, 110, 32, 98, 121, 116, 101, 115, 46, 116, 101, 110, 32, 98, 121, 116, 101, 115, 46, 116, 101, 110, 32, 98, 121, 116, 101, 115, 46, 116, 101, 110, 32, 98, 121, 116, 101, 115, 46, 116, 101, 110, 32, 98, 121, 116, 101, 115, 46, 116, 101, 110, 32, 98, 121, 116, 101, 115, 46, 116, 101, 110, 32, 98, 121, 116, 101, 115, 46, 116, 101, 110, 32, 98, 121, 116, 101, 115, 46, 116, 101, 110, 32, 98, 121, 116, 101, 115, 46])]) used 4763 gas +emit_addresses([Address(0x1234000000000000000000000000000000005678), Address(0x9123000000000000000000000000000000004567)]) used 2344 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__external_contract.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__external_contract.snap index 3c34ae4843..215da3633f 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__external_contract.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__external_contract.snap @@ -1,8 +1,7 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", proxy_harness.gas_reporter)" - --- -call_emit_event([Address(0x5dddfce53ee040d9eb21afbc0ae1bb4dbb0ba643), Uint(26), FixedArray([Address(0x0000000000000000000000000000000000000000), Address(0x0000000000000000000000000000000000000001), Address(0x0000000000000000000000000000000000000042), Address(0x0000000000000000000000000000000000000003), Address(0x0000000000000000000000000000000000000004)]), String("hello world")]) used 9269 gas -call_build_array([Address(0x5dddfce53ee040d9eb21afbc0ae1bb4dbb0ba643), Uint(26), Uint(42)]) used 2514 gas +call_emit_event([Address(0x5dddfce53ee040d9eb21afbc0ae1bb4dbb0ba643), Uint(26), FixedArray([Address(0x0000000000000000000000000000000000000000), Address(0x0000000000000000000000000000000000000001), Address(0x0000000000000000000000000000000000000042), Address(0x0000000000000000000000000000000000000003), Address(0x0000000000000000000000000000000000000004)]), String("hello world")]) used 8195 gas +call_build_array([Address(0x5dddfce53ee040d9eb21afbc0ae1bb4dbb0ba643), Uint(26), Uint(42)]) used 1945 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__intrinsics.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__intrinsics.snap index b532faa650..5403069bc6 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__intrinsics.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__intrinsics.snap @@ -1,13 +1,12 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -add([Uint(10), Uint(100)]) used 259 gas -self_balance([]) used 251 gas -calldatasize([]) used 358 gas -calldatacopy([Uint(36), Uint(32)]) used 465 gas -callvalue([]) used 314 gas -basefee([]) used 490 gas -caller([]) used 292 gas +add([Uint(10), Uint(100)]) used 228 gas +self_balance([]) used 211 gas +calldatasize([]) used 300 gas +calldatacopy([Uint(36), Uint(32)]) used 459 gas +callvalue([]) used 256 gas +basefee([]) used 432 gas +caller([]) used 234 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__keccak.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__keccak.snap index 3b0bf8f67d..cbe8fe4f61 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__keccak.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__keccak.snap @@ -1,11 +1,10 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -return_hash_from_u256([Bytes([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1])]) used 602 gas -return_hash_from_u256([Bytes([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1])]) used 602 gas -return_hash_from_u8([Bytes([1])]) used 519 gas -return_hash_from_u8([Bytes([0])]) used 519 gas -return_hash_from_foo([Bytes([102, 111, 111])]) used 583 gas +return_hash_from_u256([Bytes([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1])]) used 467 gas +return_hash_from_u256([Bytes([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1])]) used 467 gas +return_hash_from_u8([Bytes([1])]) used 383 gas +return_hash_from_u8([Bytes([0])]) used 383 gas +return_hash_from_foo([Bytes([102, 111, 111])]) used 450 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__math.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__math.snap index 18f486bdd7..f9d445fd28 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__math.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__math.snap @@ -1,8 +1,7 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -sqrt([Uint(16000)]) used 1780 gas -min([Uint(1), Uint(2)]) used 356 gas +sqrt([Uint(16000)]) used 2612 gas +min([Uint(1), Uint(2)]) used 322 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__multi_param.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__multi_param.snap index 659defd01f..06d0836468 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__multi_param.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__multi_param.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(4), Uint(42), Uint(420)]) used 614 gas +bar([Uint(4), Uint(42), Uint(420)]) used 579 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__nested_map.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__nested_map.snap index 33cc8398c5..9a7855c39f 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__nested_map.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__nested_map.snap @@ -1,18 +1,17 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -write_bar([Address(0x1000000000000000000000000000000000000001), Address(0x2000000000000000000000000000000000000002), Uint(12)]) used 22811 gas -write_bar([Address(0x1000000000000000000000000000000000000001), Address(0x3000000000000000000000000000000000000003), Uint(13)]) used 22811 gas -write_bar([Address(0x2000000000000000000000000000000000000002), Address(0x1000000000000000000000000000000000000001), Uint(21)]) used 22811 gas -write_baz([Address(0x1000000000000000000000000000000000000001), Uint(26), Bool(true)]) used 22844 gas -write_baz([Address(0x2000000000000000000000000000000000000002), Uint(42), Bool(true)]) used 22844 gas -write_baz([Address(0x2000000000000000000000000000000000000002), Uint(100), Bool(false)]) used 2944 gas -read_bar([Address(0x1000000000000000000000000000000000000001), Address(0x2000000000000000000000000000000000000002)]) used 717 gas -read_bar([Address(0x1000000000000000000000000000000000000001), Address(0x3000000000000000000000000000000000000003)]) used 717 gas -read_bar([Address(0x2000000000000000000000000000000000000002), Address(0x1000000000000000000000000000000000000001)]) used 717 gas -read_baz([Address(0x1000000000000000000000000000000000000001), Uint(26)]) used 778 gas -read_baz([Address(0x2000000000000000000000000000000000000002), Uint(42)]) used 778 gas -read_baz([Address(0x2000000000000000000000000000000000000002), Uint(100)]) used 778 gas +write_bar([Address(0x1000000000000000000000000000000000000001), Address(0x2000000000000000000000000000000000000002), Uint(12)]) used 22599 gas +write_bar([Address(0x1000000000000000000000000000000000000001), Address(0x3000000000000000000000000000000000000003), Uint(13)]) used 22599 gas +write_bar([Address(0x2000000000000000000000000000000000000002), Address(0x1000000000000000000000000000000000000001), Uint(21)]) used 22599 gas +write_baz([Address(0x1000000000000000000000000000000000000001), Uint(26), Bool(true)]) used 22634 gas +write_baz([Address(0x2000000000000000000000000000000000000002), Uint(42), Bool(true)]) used 22634 gas +write_baz([Address(0x2000000000000000000000000000000000000002), Uint(100), Bool(false)]) used 2734 gas +read_bar([Address(0x1000000000000000000000000000000000000001), Address(0x2000000000000000000000000000000000000002)]) used 606 gas +read_bar([Address(0x1000000000000000000000000000000000000001), Address(0x3000000000000000000000000000000000000003)]) used 606 gas +read_bar([Address(0x2000000000000000000000000000000000000002), Address(0x1000000000000000000000000000000000000001)]) used 606 gas +read_baz([Address(0x1000000000000000000000000000000000000001), Uint(26)]) used 643 gas +read_baz([Address(0x2000000000000000000000000000000000000002), Uint(42)]) used 643 gas +read_baz([Address(0x2000000000000000000000000000000000000002), Uint(100)]) used 643 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__numeric_sizes.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__numeric_sizes.snap index f10562515e..b343333f99 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__numeric_sizes.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__numeric_sizes.snap @@ -1,30 +1,29 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -get_u8_min([]) used 171 gas -get_u8_max([]) used 447 gas -get_i8_min([]) used 318 gas -get_i8_max([]) used 579 gas -get_u16_min([]) used 193 gas -get_u16_max([]) used 469 gas -get_i16_min([]) used 340 gas -get_i16_max([]) used 601 gas -get_u32_min([]) used 215 gas -get_u32_max([]) used 491 gas -get_i32_min([]) used 362 gas -get_i32_max([]) used 623 gas -get_u64_min([]) used 237 gas -get_u64_max([]) used 513 gas -get_i64_min([]) used 384 gas -get_i64_max([]) used 645 gas -get_u128_min([]) used 259 gas -get_u128_max([]) used 535 gas -get_i128_min([]) used 406 gas -get_i128_max([]) used 667 gas -get_u256_min([]) used 281 gas -get_u256_max([]) used 560 gas -get_i256_min([]) used 431 gas -get_i256_max([]) used 696 gas +get_u8_min([]) used 125 gas +get_u8_max([]) used 389 gas +get_i8_min([]) used 260 gas +get_i8_max([]) used 521 gas +get_u16_min([]) used 147 gas +get_u16_max([]) used 411 gas +get_i16_min([]) used 282 gas +get_i16_max([]) used 543 gas +get_u32_min([]) used 169 gas +get_u32_max([]) used 433 gas +get_i32_min([]) used 304 gas +get_i32_max([]) used 565 gas +get_u64_min([]) used 191 gas +get_u64_max([]) used 455 gas +get_i64_min([]) used 326 gas +get_i64_max([]) used 587 gas +get_u128_min([]) used 213 gas +get_u128_max([]) used 477 gas +get_i128_min([]) used 348 gas +get_i128_max([]) used 609 gas +get_u256_min([]) used 235 gas +get_u256_max([]) used 502 gas +get_i256_min([]) used 373 gas +get_i256_max([]) used 638 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__return_array.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__return_array.snap index 17e88b7424..e43f830560 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__return_array.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__return_array.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(42)]) used 849 gas +bar([Uint(42)]) used 727 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__return_builtin_attributes.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__return_builtin_attributes.snap index 30127f4386..83983aae42 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__return_builtin_attributes.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__return_builtin_attributes.snap @@ -1,16 +1,15 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -base_fee([]) used 171 gas -coinbase([]) used 204 gas -difficulty([]) used 226 gas -number([]) used 248 gas -timestamp([]) used 270 gas -chainid([]) used 292 gas -sender([]) used 314 gas -value([]) used 336 gas -origin([]) used 358 gas -gas_price([]) used 375 gas +base_fee([]) used 124 gas +coinbase([]) used 146 gas +difficulty([]) used 168 gas +number([]) used 190 gas +timestamp([]) used 212 gas +chainid([]) used 234 gas +sender([]) used 256 gas +value([]) used 278 gas +origin([]) used 300 gas +gas_price([]) used 317 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__send_value.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__send_value.snap index 67ed24d44c..8ebf0321e0 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__send_value.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__send_value.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -send_them_wei([Address(0x2000000000000000000000000000000000000002), Uint(1)]) used 34477 gas +send_them_wei([Address(0x2000000000000000000000000000000000000002), Uint(1)]) used 34568 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__short_circuit.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__short_circuit.snap index 4726ba108d..371be161d1 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__short_circuit.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__short_circuit.snap @@ -1,9 +1,8 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([Uint(6)]) used 290 gas -short_circuit_and([Bool(false)]) used 345 gas -short_circuit_or([Bool(true)]) used 365 gas +bar([Uint(6)]) used 246 gas +short_circuit_and([Bool(false)]) used 337 gas +short_circuit_or([Bool(true)]) used 345 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__sized_vals_in_sto.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__sized_vals_in_sto.snap index 9d6fae745f..53daedd8ff 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__sized_vals_in_sto.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__sized_vals_in_sto.snap @@ -1,13 +1,12 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -write_num([Uint(68)]) used 22232 gas -read_num([]) used 310 gas -write_nums([FixedArray([Uint(0), Uint(1), Uint(2), Uint(3), Uint(4), Uint(5), Uint(6), Uint(7), Uint(8), Uint(9), Uint(10), Uint(11), Uint(12), Uint(13), Uint(14), Uint(15), Uint(16), Uint(17), Uint(18), Uint(19), Uint(20), Uint(21), Uint(22), Uint(23), Uint(24), Uint(25), Uint(26), Uint(27), Uint(28), Uint(29), Uint(30), Uint(31), Uint(32), Uint(33), Uint(34), Uint(35), Uint(36), Uint(37), Uint(38), Uint(39), Uint(40), Uint(41)])]) used 919066 gas -read_nums([]) used 15558 gas -write_str([String("there are 26 protons in fe")]) used 45053 gas -read_str([]) used 1489 gas -emit_event([]) used 30232 gas +write_num([Uint(68)]) used 22213 gas +read_num([]) used 265 gas +write_nums([FixedArray([Uint(0), Uint(1), Uint(2), Uint(3), Uint(4), Uint(5), Uint(6), Uint(7), Uint(8), Uint(9), Uint(10), Uint(11), Uint(12), Uint(13), Uint(14), Uint(15), Uint(16), Uint(17), Uint(18), Uint(19), Uint(20), Uint(21), Uint(22), Uint(23), Uint(24), Uint(25), Uint(26), Uint(27), Uint(28), Uint(29), Uint(30), Uint(31), Uint(32), Uint(33), Uint(34), Uint(35), Uint(36), Uint(37), Uint(38), Uint(39), Uint(40), Uint(41)])]) used 912904 gas +read_nums([]) used 11803 gas +write_str([String("there are 26 protons in fe")]) used 44924 gas +read_str([]) used 1197 gas +emit_event([]) used 29632 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__strings.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__strings.snap index c186021d4f..f170f4669c 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__strings.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__strings.snap @@ -1,10 +1,9 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([String("string 4"), String("string 5")]) used 1385 gas -return_static_string([]) used 1002 gas -return_casted_static_string([]) used 942 gas -return_special_chars([]) used 984 gas +bar([String("string 4"), String("string 5")]) used 1039 gas +return_static_string([]) used 722 gas +return_casted_static_string([]) used 966 gas +return_special_chars([]) used 688 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__structs.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__structs.snap index 3565a821b8..6bbe63162b 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__structs.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__structs.snap @@ -1,15 +1,14 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -create_house([]) used 92804 gas -bar([]) used 1215 gas -set_house([Tuple([Uint(1000000), Uint(250), Uint(6), Bool(true)])]) used 1559 gas -get_house([]) used 1861 gas -encode_house([]) used 1805 gas -hashed_house([]) used 1270 gas -create_mixed([]) used 351 gas -complex_struct_in_memory([]) used 3004 gas -complex_struct_in_storage([]) used 283698 gas +create_house([]) used 69429 gas +bar([]) used 1415 gas +set_house([Tuple([Uint(1000000), Uint(250), Uint(6), Bool(true)])]) used 1181 gas +get_house([]) used 1085 gas +encode_house([]) used 1282 gas +hashed_house([]) used 872 gas +create_mixed([]) used 363 gas +complex_struct_in_memory([]) used 3643 gas +complex_struct_in_storage([]) used 183598 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__tuple_destructuring.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__tuple_destructuring.snap index 39c1804038..7c8a4603c0 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__tuple_destructuring.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__tuple_destructuring.snap @@ -1,8 +1,7 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -bar([]) used 286 gas -baz([Uint(1), Bool(false)]) used 389 gas +bar([]) used 122 gas +baz([Uint(1), Bool(false)]) used 382 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__features__two_contracts.snap b/crates/tests/src/snapshots/fe_compiler_tests__features__two_contracts.snap index 18b2291f8a..12dbd3b7af 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__features__two_contracts.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__features__two_contracts.snap @@ -1,7 +1,6 @@ --- source: crates/tests/src/features.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -foo([]) used 24428 gas +foo([]) used 24301 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__stress__abi_encoding_stress.snap b/crates/tests/src/snapshots/fe_compiler_tests__stress__abi_encoding_stress.snap index 632772a59c..f1c62a92b5 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__stress__abi_encoding_stress.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__stress__abi_encoding_stress.snap @@ -1,21 +1,20 @@ --- source: crates/tests/src/stress.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -set_my_addrs([FixedArray([Address(0x000000000000000000000000000000000000000a), Address(0x000000000000000000000000000000000000000b), Address(0x000000000000000000000000000000000000000c), Address(0x000000000000000000000000000000000000000d), Address(0x000000000000000000000000000000000000000e)])]) used 112206 gas -get_my_addrs([]) used 2244 gas -set_my_u128([Uint(42)]) used 22317 gas -get_my_u128([]) used 366 gas -set_my_string([String("my string")]) used 45053 gas -get_my_string([]) used 1486 gas -set_my_u16s([FixedArray([Uint(0), Uint(1), Uint(2), Uint(3), Uint(4), Uint(5), Uint(6), Uint(7), Uint(8), Uint(9), Uint(10), Uint(11), Uint(12), Uint(13), Uint(14), Uint(15), Uint(16), Uint(17), Uint(18), Uint(19), Uint(20), Uint(21), Uint(22), Uint(23), Uint(24), Uint(25), Uint(26), Uint(27), Uint(28), Uint(29), Uint(30), Uint(31), Uint(32), Uint(33), Uint(34), Uint(35), Uint(36), Uint(37), Uint(38), Uint(39), Uint(40), Uint(41), Uint(42), Uint(43), Uint(44), Uint(45), Uint(46), Uint(47), Uint(48), Uint(49), Uint(50), Uint(51), Uint(52), Uint(53), Uint(54), Uint(55), Uint(56), Uint(57), Uint(58), Uint(59), Uint(60), Uint(61), Uint(62), Uint(63), Uint(64), Uint(65), Uint(66), Uint(67), Uint(68), Uint(69), Uint(70), Uint(71), Uint(72), Uint(73), Uint(74), Uint(75), Uint(76), Uint(77), Uint(78), Uint(79), Uint(80), Uint(81), Uint(82), Uint(83), Uint(84), Uint(85), Uint(86), Uint(87), Uint(88), Uint(89), Uint(90), Uint(91), Uint(92), Uint(93), Uint(94), Uint(95), Uint(96), Uint(97), Uint(98), Uint(99), Uint(100), Uint(101), Uint(102), Uint(103), Uint(104), Uint(105), Uint(106), Uint(107), Uint(108), Uint(109), Uint(110), Uint(111), Uint(112), Uint(113), Uint(114), Uint(115), Uint(116), Uint(117), Uint(118), Uint(119), Uint(120), Uint(121), Uint(122), Uint(123), Uint(124), Uint(125), Uint(126), Uint(127), Uint(128), Uint(129), Uint(130), Uint(131), Uint(132), Uint(133), Uint(134), Uint(135), Uint(136), Uint(137), Uint(138), Uint(139), Uint(140), Uint(141), Uint(142), Uint(143), Uint(144), Uint(145), Uint(146), Uint(147), Uint(148), Uint(149), Uint(150), Uint(151), Uint(152), Uint(153), Uint(154), Uint(155), Uint(156), Uint(157), Uint(158), Uint(159), Uint(160), Uint(161), Uint(162), Uint(163), Uint(164), Uint(165), Uint(166), Uint(167), Uint(168), Uint(169), Uint(170), Uint(171), Uint(172), Uint(173), Uint(174), Uint(175), Uint(176), Uint(177), Uint(178), Uint(179), Uint(180), Uint(181), Uint(182), Uint(183), Uint(184), Uint(185), Uint(186), Uint(187), Uint(188), Uint(189), Uint(190), Uint(191), Uint(192), Uint(193), Uint(194), Uint(195), Uint(196), Uint(197), Uint(198), Uint(199), Uint(200), Uint(201), Uint(202), Uint(203), Uint(204), Uint(205), Uint(206), Uint(207), Uint(208), Uint(209), Uint(210), Uint(211), Uint(212), Uint(213), Uint(214), Uint(215), Uint(216), Uint(217), Uint(218), Uint(219), Uint(220), Uint(221), Uint(222), Uint(223), Uint(224), Uint(225), Uint(226), Uint(227), Uint(228), Uint(229), Uint(230), Uint(231), Uint(232), Uint(233), Uint(234), Uint(235), Uint(236), Uint(237), Uint(238), Uint(239), Uint(240), Uint(241), Uint(242), Uint(243), Uint(244), Uint(245), Uint(246), Uint(247), Uint(248), Uint(249), Uint(250), Uint(251), Uint(252), Uint(253), Uint(254)])]) used 408408 gas -get_my_u16s([]) used 40477 gas -set_my_bool([Bool(true)]) used 22461 gas -get_my_bool([]) used 492 gas -set_my_bytes([Bytes([116, 101, 110, 32, 98, 121, 116, 101, 115, 46, 116, 101, 110, 32, 98, 121, 116, 101, 115, 46, 116, 101, 110, 32, 98, 121, 116, 101, 115, 46, 116, 101, 110, 32, 98, 121, 116, 101, 115, 46, 116, 101, 110, 32, 98, 121, 116, 101, 115, 46, 116, 101, 110, 32, 98, 121, 116, 101, 115, 46, 116, 101, 110, 32, 98, 121, 116, 101, 115, 46, 116, 101, 110, 32, 98, 121, 116, 101, 115, 46, 116, 101, 110, 32, 98, 121, 116, 101, 115, 46, 116, 101, 110, 32, 98, 121, 116, 101, 115, 46])]) used 89618 gas -get_my_bytes([]) used 2106 gas -get_my_struct([]) used 1138 gas -mod_my_struct([Tuple([Uint(42), Uint(26), Bool(true), Address(0x000000000000000000000000000000000001e240)])]) used 1584 gas -emit_my_event([]) used 116997 gas +set_my_addrs([FixedArray([Address(0x000000000000000000000000000000000000000a), Address(0x000000000000000000000000000000000000000b), Address(0x000000000000000000000000000000000000000c), Address(0x000000000000000000000000000000000000000d), Address(0x000000000000000000000000000000000000000e)])]) used 111679 gas +get_my_addrs([]) used 1786 gas +set_my_u128([Uint(42)]) used 22345 gas +get_my_u128([]) used 341 gas +set_my_string([String("my string")]) used 44951 gas +get_my_string([]) used 1197 gas +set_my_u16s([FixedArray([Uint(0), Uint(1), Uint(2), Uint(3), Uint(4), Uint(5), Uint(6), Uint(7), Uint(8), Uint(9), Uint(10), Uint(11), Uint(12), Uint(13), Uint(14), Uint(15), Uint(16), Uint(17), Uint(18), Uint(19), Uint(20), Uint(21), Uint(22), Uint(23), Uint(24), Uint(25), Uint(26), Uint(27), Uint(28), Uint(29), Uint(30), Uint(31), Uint(32), Uint(33), Uint(34), Uint(35), Uint(36), Uint(37), Uint(38), Uint(39), Uint(40), Uint(41), Uint(42), Uint(43), Uint(44), Uint(45), Uint(46), Uint(47), Uint(48), Uint(49), Uint(50), Uint(51), Uint(52), Uint(53), Uint(54), Uint(55), Uint(56), Uint(57), Uint(58), Uint(59), Uint(60), Uint(61), Uint(62), Uint(63), Uint(64), Uint(65), Uint(66), Uint(67), Uint(68), Uint(69), Uint(70), Uint(71), Uint(72), Uint(73), Uint(74), Uint(75), Uint(76), Uint(77), Uint(78), Uint(79), Uint(80), Uint(81), Uint(82), Uint(83), Uint(84), Uint(85), Uint(86), Uint(87), Uint(88), Uint(89), Uint(90), Uint(91), Uint(92), Uint(93), Uint(94), Uint(95), Uint(96), Uint(97), Uint(98), Uint(99), Uint(100), Uint(101), Uint(102), Uint(103), Uint(104), Uint(105), Uint(106), Uint(107), Uint(108), Uint(109), Uint(110), Uint(111), Uint(112), Uint(113), Uint(114), Uint(115), Uint(116), Uint(117), Uint(118), Uint(119), Uint(120), Uint(121), Uint(122), Uint(123), Uint(124), Uint(125), Uint(126), Uint(127), Uint(128), Uint(129), Uint(130), Uint(131), Uint(132), Uint(133), Uint(134), Uint(135), Uint(136), Uint(137), Uint(138), Uint(139), Uint(140), Uint(141), Uint(142), Uint(143), Uint(144), Uint(145), Uint(146), Uint(147), Uint(148), Uint(149), Uint(150), Uint(151), Uint(152), Uint(153), Uint(154), Uint(155), Uint(156), Uint(157), Uint(158), Uint(159), Uint(160), Uint(161), Uint(162), Uint(163), Uint(164), Uint(165), Uint(166), Uint(167), Uint(168), Uint(169), Uint(170), Uint(171), Uint(172), Uint(173), Uint(174), Uint(175), Uint(176), Uint(177), Uint(178), Uint(179), Uint(180), Uint(181), Uint(182), Uint(183), Uint(184), Uint(185), Uint(186), Uint(187), Uint(188), Uint(189), Uint(190), Uint(191), Uint(192), Uint(193), Uint(194), Uint(195), Uint(196), Uint(197), Uint(198), Uint(199), Uint(200), Uint(201), Uint(202), Uint(203), Uint(204), Uint(205), Uint(206), Uint(207), Uint(208), Uint(209), Uint(210), Uint(211), Uint(212), Uint(213), Uint(214), Uint(215), Uint(216), Uint(217), Uint(218), Uint(219), Uint(220), Uint(221), Uint(222), Uint(223), Uint(224), Uint(225), Uint(226), Uint(227), Uint(228), Uint(229), Uint(230), Uint(231), Uint(232), Uint(233), Uint(234), Uint(235), Uint(236), Uint(237), Uint(238), Uint(239), Uint(240), Uint(241), Uint(242), Uint(243), Uint(244), Uint(245), Uint(246), Uint(247), Uint(248), Uint(249), Uint(250), Uint(251), Uint(252), Uint(253), Uint(254)])]) used 386187 gas +get_my_u16s([]) used 26303 gas +set_my_bool([Bool(true)]) used 553 gas +get_my_bool([]) used 467 gas +set_my_bytes([Bytes([116, 101, 110, 32, 98, 121, 116, 101, 115, 46, 116, 101, 110, 32, 98, 121, 116, 101, 115, 46, 116, 101, 110, 32, 98, 121, 116, 101, 115, 46, 116, 101, 110, 32, 98, 121, 116, 101, 115, 46, 116, 101, 110, 32, 98, 121, 116, 101, 115, 46, 116, 101, 110, 32, 98, 121, 116, 101, 115, 46, 116, 101, 110, 32, 98, 121, 116, 101, 115, 46, 116, 101, 110, 32, 98, 121, 116, 101, 115, 46, 116, 101, 110, 32, 98, 121, 116, 101, 115, 46, 116, 101, 110, 32, 98, 121, 116, 101, 115, 46])]) used 89350 gas +get_my_bytes([]) used 1784 gas +get_my_struct([]) used 773 gas +mod_my_struct([Tuple([Uint(42), Uint(26), Bool(true), Address(0x000000000000000000000000000000000001e240)])]) used 1145 gas +emit_my_event([]) used 103783 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__stress__data_copying_stress.snap b/crates/tests/src/snapshots/fe_compiler_tests__stress__data_copying_stress.snap index f25e5be016..e3368d1875 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__stress__data_copying_stress.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__stress__data_copying_stress.snap @@ -1,17 +1,16 @@ --- source: crates/tests/src/stress.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -set_my_vals([String("my string"), String("my other string"), Uint(26), Uint(42)]) used 158532 gas -emit_my_event([]) used 3643 gas -set_to_my_other_vals([]) used 420469 gas -emit_my_event([]) used 3643 gas -mutate_and_return([FixedArray([Uint(1), Uint(2), Uint(3), Uint(4), Uint(5), Uint(6), Uint(7), Uint(8), Uint(9), Uint(10)])]) used 3101 gas -multiple_references_shared_memory([FixedArray([Uint(1), Uint(2), Uint(3), Uint(4), Uint(5), Uint(6), Uint(7), Uint(8), Uint(9), Uint(10)])]) used 1757 gas -clone_and_return([FixedArray([Uint(1), Uint(2), Uint(3), Uint(4), Uint(5), Uint(6), Uint(7), Uint(8), Uint(9), Uint(10)])]) used 4091 gas -clone_mutate_and_return([FixedArray([Uint(1), Uint(2), Uint(3), Uint(4), Uint(5), Uint(6), Uint(7), Uint(8), Uint(9), Uint(10)])]) used 4155 gas -assign_my_nums_and_return([]) used 92844 gas -set_my_addrs([FixedArray([Address(0x0000000000000000000000000000000000000000), Address(0x0000000000000000000000000000000000000001), Address(0x0000000000000000000000000000000000000002)])]) used 47577 gas -get_my_second_addr([]) used 481 gas +set_my_vals([String("my string"), String("my other string"), Uint(26), Uint(42)]) used 138325 gas +emit_my_event([]) used 3694 gas +set_to_my_other_vals([]) used 1286 gas +emit_my_event([]) used 3694 gas +mutate_and_return([FixedArray([Uint(1), Uint(2), Uint(3), Uint(4), Uint(5), Uint(6), Uint(7), Uint(8), Uint(9), Uint(10)])]) used 1493 gas +multiple_references_shared_memory([FixedArray([Uint(1), Uint(2), Uint(3), Uint(4), Uint(5), Uint(6), Uint(7), Uint(8), Uint(9), Uint(10)])]) used 680 gas +clone_and_return([FixedArray([Uint(1), Uint(2), Uint(3), Uint(4), Uint(5), Uint(6), Uint(7), Uint(8), Uint(9), Uint(10)])]) used 2494 gas +clone_mutate_and_return([FixedArray([Uint(1), Uint(2), Uint(3), Uint(4), Uint(5), Uint(6), Uint(7), Uint(8), Uint(9), Uint(10)])]) used 2528 gas +assign_my_nums_and_return([]) used 92449 gas +set_my_addrs([FixedArray([Address(0x0000000000000000000000000000000000000000), Address(0x0000000000000000000000000000000000000001), Address(0x0000000000000000000000000000000000000002)])]) used 47327 gas +get_my_second_addr([]) used 468 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__stress__external_calls_stress.snap b/crates/tests/src/snapshots/fe_compiler_tests__stress__external_calls_stress.snap index f8b3fe2b65..5eb5c01155 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__stress__external_calls_stress.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__stress__external_calls_stress.snap @@ -1,16 +1,15 @@ --- source: crates/tests/src/stress.rs expression: "format!(\"{}\", proxy_harness.gas_reporter)" - --- -call_set_my_string([String("hello world")]) used 53441 gas -call_get_my_string([]) used 3963 gas -call_set_my_tuple([Tuple([Uint(42), Address(0x0000000000000000000000000000000000000026)])]) used 46152 gas -call_get_my_tuple([]) used 2503 gas -call_set_my_string_and_tuple([String("foo"), Tuple([Uint(99), Address(0x00000000000000000000000000000000000000ab)])]) used 44371 gas -call_get_my_tuple([]) used 2503 gas -call_get_my_string([]) used 3963 gas -call_get_tuple([]) used 2404 gas -call_get_string([]) used 3009 gas -call_get_array([]) used 2979 gas +call_set_my_string([String("hello world")]) used 52880 gas +call_get_my_string([]) used 3238 gas +call_set_my_tuple([Tuple([Uint(42), Address(0x0000000000000000000000000000000000000026)])]) used 45640 gas +call_get_my_tuple([]) used 1744 gas +call_set_my_string_and_tuple([String("foo"), Tuple([Uint(99), Address(0x00000000000000000000000000000000000000ab)])]) used 3874 gas +call_get_my_tuple([]) used 1744 gas +call_get_my_string([]) used 3238 gas +call_get_tuple([]) used 1617 gas +call_get_string([]) used 2759 gas +call_get_array([]) used 2444 gas diff --git a/crates/tests/src/snapshots/fe_compiler_tests__stress__tuple_stress.snap b/crates/tests/src/snapshots/fe_compiler_tests__stress__tuple_stress.snap index 23caad4ea2..891795841b 100644 --- a/crates/tests/src/snapshots/fe_compiler_tests__stress__tuple_stress.snap +++ b/crates/tests/src/snapshots/fe_compiler_tests__stress__tuple_stress.snap @@ -1,15 +1,14 @@ --- source: crates/tests/src/stress.rs expression: "format!(\"{}\", harness.gas_reporter)" - --- -build_my_tuple([Uint(26), Bool(true), Address(0x0000000000000000000000000000000000000042)]) used 900 gas -read_my_tuple_item0([Tuple([Uint(26), Bool(true), Address(0x0000000000000000000000000000000000000042)])]) used 636 gas -read_my_tuple_item1([Tuple([Uint(26), Bool(true), Address(0x0000000000000000000000000000000000000042)])]) used 685 gas -read_my_tuple_item2([Tuple([Uint(26), Bool(true), Address(0x0000000000000000000000000000000000000042)])]) used 701 gas -set_my_sto_tuple([Uint(42), Int(115792089237316195423570985008687907853269984665640564039457584007913129639910)]) used 45074 gas -get_my_sto_tuple([]) used 1289 gas -emit_my_event([Tuple([Uint(26), Bool(true), Address(0x0000000000000000000000000000000000000042)])]) used 2655 gas -build_tuple_and_emit([]) used 2572 gas -encode_my_tuple([Tuple([Uint(26), Bool(true), Address(0x0000000000000000000000000000000000000042)])]) used 1794 gas +build_my_tuple([Uint(26), Bool(true), Address(0x0000000000000000000000000000000000000042)]) used 605 gas +read_my_tuple_item0([Tuple([Uint(26), Bool(true), Address(0x0000000000000000000000000000000000000042)])]) used 464 gas +read_my_tuple_item1([Tuple([Uint(26), Bool(true), Address(0x0000000000000000000000000000000000000042)])]) used 522 gas +read_my_tuple_item2([Tuple([Uint(26), Bool(true), Address(0x0000000000000000000000000000000000000042)])]) used 556 gas +set_my_sto_tuple([Uint(42), Int(115792089237316195423570985008687907853269984665640564039457584007913129639910)]) used 44808 gas +get_my_sto_tuple([]) used 922 gas +emit_my_event([Tuple([Uint(26), Bool(true), Address(0x0000000000000000000000000000000000000042)])]) used 2484 gas +build_tuple_and_emit([]) used 2537 gas +encode_my_tuple([Tuple([Uint(26), Bool(true), Address(0x0000000000000000000000000000000000000042)])]) used 1205 gas diff --git a/crates/yulc/src/lib.rs b/crates/yulc/src/lib.rs index a98980f85f..227fc9d6c4 100644 --- a/crates/yulc/src/lib.rs +++ b/crates/yulc/src/lib.rs @@ -38,6 +38,8 @@ pub fn compile_single_contract( .replace('"', ""); if bytecode == "null" { + println! {"{}", name}; + println! {"{}", output["contracts"]["input.yul"]}; return Err(YulcError(output.to_string())); } diff --git a/crates/yulgen/src/runtime/abi_dispatcher.rs b/crates/yulgen/src/runtime/abi_dispatcher.rs index 5772cd0d30..d472668f58 100644 --- a/crates/yulgen/src/runtime/abi_dispatcher.rs +++ b/crates/yulgen/src/runtime/abi_dispatcher.rs @@ -5,8 +5,8 @@ use fe_abi::utils as abi_utils; use smol_str::SmolStr; use yultsur::*; -/// Builds a switch statement that dispatches calls to the contract and wraps it in -/// a `$$__call__` function. +/// Builds a switch statement that dispatches calls to the contract and wraps it +/// in a `$$__call__` function. pub fn dispatcher( functions: &[( SmolStr, From b8393191097b32520c626ad79240fc55dc3ea312 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Thu, 19 May 2022 21:23:05 +0900 Subject: [PATCH 14/15] Remove `lowering` and `yulgen` --- Cargo.lock | 66 +- crates/abi/Cargo.toml | 10 +- crates/abi/src/builder.rs | 178 ---- crates/{new_abi => abi}/src/contract.rs | 0 crates/abi/src/elements.rs | 330 ------ crates/abi/src/errors.rs | 5 - crates/{new_abi => abi}/src/event.rs | 0 crates/{new_abi => abi}/src/function.rs | 0 crates/abi/src/lib.rs | 33 +- crates/{new_abi => abi}/src/types.rs | 0 crates/abi/src/utils.rs | 18 - crates/codegen/Cargo.toml | 2 +- crates/codegen/src/db.rs | 2 +- crates/codegen/src/db/queries/abi.rs | 6 +- crates/codegen/src/yul/isel/function.rs | 2 +- crates/codegen/src/yul/runtime/revert.rs | 2 +- crates/driver/Cargo.toml | 2 - crates/driver/src/lib.rs | 2 - crates/lowering/Cargo.toml | 22 - crates/lowering/src/ast_utils.rs | 960 ------------------ crates/lowering/src/context.rs | 81 -- crates/lowering/src/db.rs | 38 - crates/lowering/src/db/queries.rs | 9 - crates/lowering/src/lib.rs | 72 -- crates/lowering/src/mappers/contracts.rs | 65 -- crates/lowering/src/mappers/events.rs | 38 - crates/lowering/src/mappers/expressions.rs | 222 ---- crates/lowering/src/mappers/functions.rs | 401 -------- crates/lowering/src/mappers/mod.rs | 7 - crates/lowering/src/mappers/module.rs | 175 ---- crates/lowering/src/mappers/structs.rs | 47 - crates/lowering/src/mappers/types.rs | 49 - crates/lowering/src/names.rs | 53 - crates/lowering/src/utils.rs | 22 - crates/lowering/tests/lowering.rs | 59 -- .../tests/snapshots/lowering__and_or.snap | 182 ---- .../tests/snapshots/lowering__aug_assign.snap | 62 -- .../tests/snapshots/lowering__base_tuple.snap | 60 -- .../lowering__custom_empty_type.snap | 11 - .../tests/snapshots/lowering__init.snap | 9 - .../snapshots/lowering__list_expressions.snap | 22 - .../tests/snapshots/lowering__map_tuple.snap | 19 - .../snapshots/lowering__module_const.snap | 62 -- .../tests/snapshots/lowering__module_fn.snap | 35 - .../lowering__module_level_events.snap | 16 - .../snapshots/lowering__nested_tuple.snap | 28 - .../snapshots/lowering__return_unit.snap | 41 - .../tests/snapshots/lowering__struct_fn.snap | 22 - .../tests/snapshots/lowering__ternary.snap | 88 -- .../snapshots/lowering__tuple_destruct.snap | 28 - .../snapshots/lowering__type_alias_tuple.snap | 15 - .../snapshots/lowering__unit_implicit.snap | 9 - crates/new_abi/Cargo.toml | 14 - crates/new_abi/src/lib.rs | 4 - crates/test-utils/Cargo.toml | 1 - crates/test-utils/src/lib.rs | 3 +- crates/tests/Cargo.toml | 2 - crates/tests/src/lib.rs | 2 - crates/tests/src/runtime.rs | 290 ------ crates/yulc/Cargo.toml | 1 - crates/yulgen/Cargo.toml | 29 - crates/yulgen/src/constants.rs | 69 -- crates/yulgen/src/constructor.rs | 96 -- crates/yulgen/src/context.rs | 56 - crates/yulgen/src/db.rs | 114 --- crates/yulgen/src/db/queries.rs | 48 - crates/yulgen/src/db/queries/contracts.rs | 219 ---- crates/yulgen/src/db/queries/events.rs | 22 - crates/yulgen/src/db/queries/functions.rs | 202 ---- crates/yulgen/src/db/queries/structs.rs | 148 --- crates/yulgen/src/lib.rs | 29 - crates/yulgen/src/mappers/assignments.rs | 106 -- crates/yulgen/src/mappers/declarations.rs | 54 - crates/yulgen/src/mappers/expressions.rs | 464 --------- crates/yulgen/src/mappers/functions.rs | 252 ----- crates/yulgen/src/mappers/mod.rs | 5 - crates/yulgen/src/mappers/module.rs | 23 - crates/yulgen/src/names/abi.rs | 148 --- crates/yulgen/src/names/mod.rs | 73 -- crates/yulgen/src/operations/abi.rs | 118 --- crates/yulgen/src/operations/contracts.rs | 27 - crates/yulgen/src/operations/data.rs | 148 --- crates/yulgen/src/operations/math.rs | 13 - crates/yulgen/src/operations/mod.rs | 6 - crates/yulgen/src/operations/revert.rs | 32 - crates/yulgen/src/operations/structs.rs | 88 -- crates/yulgen/src/runtime/abi_dispatcher.rs | 126 --- crates/yulgen/src/runtime/functions/abi.rs | 625 ------------ .../yulgen/src/runtime/functions/contracts.rs | 28 - crates/yulgen/src/runtime/functions/data.rs | 385 ------- crates/yulgen/src/runtime/functions/math.rs | 519 ---------- crates/yulgen/src/runtime/functions/mod.rs | 19 - crates/yulgen/src/runtime/functions/revert.rs | 61 -- crates/yulgen/src/runtime/mod.rs | 2 - crates/yulgen/src/types.rs | 269 ----- crates/yulgen/src/utils.rs | 4 - ...decode_component_address_mem_function.snap | 10 - ...code_component_bool_calldata_function.snap | 10 - ...ecode_component_bytes_26_mem_function.snap | 19 - ...ode_component_int16_calldata_function.snap | 10 - ...tatic_array_address_calldata_function.snap | 14 - ...component_string_26_calldata_function.snap | 19 - ...onent_tuple_u256_address_mem_function.snap | 19 - ...decode_component_uint256_mem_function.snap | 10 - ...decode_data_address_bool_mem_function.snap | 28 - ..._bool_address_bytes_calldata_function.snap | 76 -- .../snapshots/yulgen__abi_dispatcher.snap | 20 - ...gen__abi_encode_u256_address_function.snap | 17 - .../yulgen__constructor_no_init.snap | 10 - ...gen__decode_data_name_string_mem_name.snap | 6 - ...__decode_data_name_u256_calldata_name.snap | 6 - ...gen__decode_string_calldata_operation.snap | 6 - ...lgen__emit_event_no_indexed_operation.snap | 6 - ...gen__emit_event_one_indexed_operation.snap | 6 - .../tests/snapshots/yulgen__encode_name.snap | 6 - .../yulgen__encode_size_u256_operation.snap | 6 - .../yulgen__encode_u256_operation.snap | 6 - .../yulgen__revert_string_error.snap | 10 - .../yulgen__struct_empty_function.snap | 6 - ...lgen__struct_getter_gen_bar2_function.snap | 6 - ...ulgen__struct_getter_gen_bar_function.snap | 6 - .../yulgen__struct_new_gen_function.snap | 11 - .../snapshots/yulgen__sum_operation.snap | 6 - crates/yulgen/tests/yulgen.rs | 167 --- 124 files changed, 19 insertions(+), 9134 deletions(-) delete mode 100644 crates/abi/src/builder.rs rename crates/{new_abi => abi}/src/contract.rs (100%) delete mode 100644 crates/abi/src/elements.rs delete mode 100644 crates/abi/src/errors.rs rename crates/{new_abi => abi}/src/event.rs (100%) rename crates/{new_abi => abi}/src/function.rs (100%) rename crates/{new_abi => abi}/src/types.rs (100%) delete mode 100644 crates/abi/src/utils.rs delete mode 100644 crates/lowering/Cargo.toml delete mode 100644 crates/lowering/src/ast_utils.rs delete mode 100644 crates/lowering/src/context.rs delete mode 100644 crates/lowering/src/db.rs delete mode 100644 crates/lowering/src/db/queries.rs delete mode 100644 crates/lowering/src/lib.rs delete mode 100644 crates/lowering/src/mappers/contracts.rs delete mode 100644 crates/lowering/src/mappers/events.rs delete mode 100644 crates/lowering/src/mappers/expressions.rs delete mode 100644 crates/lowering/src/mappers/functions.rs delete mode 100644 crates/lowering/src/mappers/mod.rs delete mode 100644 crates/lowering/src/mappers/module.rs delete mode 100644 crates/lowering/src/mappers/structs.rs delete mode 100644 crates/lowering/src/mappers/types.rs delete mode 100644 crates/lowering/src/names.rs delete mode 100644 crates/lowering/src/utils.rs delete mode 100644 crates/lowering/tests/lowering.rs delete mode 100644 crates/lowering/tests/snapshots/lowering__and_or.snap delete mode 100644 crates/lowering/tests/snapshots/lowering__aug_assign.snap delete mode 100644 crates/lowering/tests/snapshots/lowering__base_tuple.snap delete mode 100644 crates/lowering/tests/snapshots/lowering__custom_empty_type.snap delete mode 100644 crates/lowering/tests/snapshots/lowering__init.snap delete mode 100644 crates/lowering/tests/snapshots/lowering__list_expressions.snap delete mode 100644 crates/lowering/tests/snapshots/lowering__map_tuple.snap delete mode 100644 crates/lowering/tests/snapshots/lowering__module_const.snap delete mode 100644 crates/lowering/tests/snapshots/lowering__module_fn.snap delete mode 100644 crates/lowering/tests/snapshots/lowering__module_level_events.snap delete mode 100644 crates/lowering/tests/snapshots/lowering__nested_tuple.snap delete mode 100644 crates/lowering/tests/snapshots/lowering__return_unit.snap delete mode 100644 crates/lowering/tests/snapshots/lowering__struct_fn.snap delete mode 100644 crates/lowering/tests/snapshots/lowering__ternary.snap delete mode 100644 crates/lowering/tests/snapshots/lowering__tuple_destruct.snap delete mode 100644 crates/lowering/tests/snapshots/lowering__type_alias_tuple.snap delete mode 100644 crates/lowering/tests/snapshots/lowering__unit_implicit.snap delete mode 100644 crates/new_abi/Cargo.toml delete mode 100644 crates/new_abi/src/lib.rs delete mode 100644 crates/tests/src/runtime.rs delete mode 100644 crates/yulgen/Cargo.toml delete mode 100644 crates/yulgen/src/constants.rs delete mode 100644 crates/yulgen/src/constructor.rs delete mode 100644 crates/yulgen/src/context.rs delete mode 100644 crates/yulgen/src/db.rs delete mode 100644 crates/yulgen/src/db/queries.rs delete mode 100644 crates/yulgen/src/db/queries/contracts.rs delete mode 100644 crates/yulgen/src/db/queries/events.rs delete mode 100644 crates/yulgen/src/db/queries/functions.rs delete mode 100644 crates/yulgen/src/db/queries/structs.rs delete mode 100644 crates/yulgen/src/lib.rs delete mode 100644 crates/yulgen/src/mappers/assignments.rs delete mode 100644 crates/yulgen/src/mappers/declarations.rs delete mode 100644 crates/yulgen/src/mappers/expressions.rs delete mode 100644 crates/yulgen/src/mappers/functions.rs delete mode 100644 crates/yulgen/src/mappers/mod.rs delete mode 100644 crates/yulgen/src/mappers/module.rs delete mode 100644 crates/yulgen/src/names/abi.rs delete mode 100644 crates/yulgen/src/names/mod.rs delete mode 100644 crates/yulgen/src/operations/abi.rs delete mode 100644 crates/yulgen/src/operations/contracts.rs delete mode 100644 crates/yulgen/src/operations/data.rs delete mode 100644 crates/yulgen/src/operations/math.rs delete mode 100644 crates/yulgen/src/operations/mod.rs delete mode 100644 crates/yulgen/src/operations/revert.rs delete mode 100644 crates/yulgen/src/operations/structs.rs delete mode 100644 crates/yulgen/src/runtime/abi_dispatcher.rs delete mode 100644 crates/yulgen/src/runtime/functions/abi.rs delete mode 100644 crates/yulgen/src/runtime/functions/contracts.rs delete mode 100644 crates/yulgen/src/runtime/functions/data.rs delete mode 100644 crates/yulgen/src/runtime/functions/math.rs delete mode 100644 crates/yulgen/src/runtime/functions/mod.rs delete mode 100644 crates/yulgen/src/runtime/functions/revert.rs delete mode 100644 crates/yulgen/src/runtime/mod.rs delete mode 100644 crates/yulgen/src/types.rs delete mode 100644 crates/yulgen/src/utils.rs delete mode 100644 crates/yulgen/tests/snapshots/yulgen__abi_decode_component_address_mem_function.snap delete mode 100644 crates/yulgen/tests/snapshots/yulgen__abi_decode_component_bool_calldata_function.snap delete mode 100644 crates/yulgen/tests/snapshots/yulgen__abi_decode_component_bytes_26_mem_function.snap delete mode 100644 crates/yulgen/tests/snapshots/yulgen__abi_decode_component_int16_calldata_function.snap delete mode 100644 crates/yulgen/tests/snapshots/yulgen__abi_decode_component_static_array_address_calldata_function.snap delete mode 100644 crates/yulgen/tests/snapshots/yulgen__abi_decode_component_string_26_calldata_function.snap delete mode 100644 crates/yulgen/tests/snapshots/yulgen__abi_decode_component_tuple_u256_address_mem_function.snap delete mode 100644 crates/yulgen/tests/snapshots/yulgen__abi_decode_component_uint256_mem_function.snap delete mode 100644 crates/yulgen/tests/snapshots/yulgen__abi_decode_data_address_bool_mem_function.snap delete mode 100644 crates/yulgen/tests/snapshots/yulgen__abi_decode_data_u256_bytes_string_bool_address_bytes_calldata_function.snap delete mode 100644 crates/yulgen/tests/snapshots/yulgen__abi_dispatcher.snap delete mode 100644 crates/yulgen/tests/snapshots/yulgen__abi_encode_u256_address_function.snap delete mode 100644 crates/yulgen/tests/snapshots/yulgen__constructor_no_init.snap delete mode 100644 crates/yulgen/tests/snapshots/yulgen__decode_data_name_string_mem_name.snap delete mode 100644 crates/yulgen/tests/snapshots/yulgen__decode_data_name_u256_calldata_name.snap delete mode 100644 crates/yulgen/tests/snapshots/yulgen__decode_string_calldata_operation.snap delete mode 100644 crates/yulgen/tests/snapshots/yulgen__emit_event_no_indexed_operation.snap delete mode 100644 crates/yulgen/tests/snapshots/yulgen__emit_event_one_indexed_operation.snap delete mode 100644 crates/yulgen/tests/snapshots/yulgen__encode_name.snap delete mode 100644 crates/yulgen/tests/snapshots/yulgen__encode_size_u256_operation.snap delete mode 100644 crates/yulgen/tests/snapshots/yulgen__encode_u256_operation.snap delete mode 100644 crates/yulgen/tests/snapshots/yulgen__revert_string_error.snap delete mode 100644 crates/yulgen/tests/snapshots/yulgen__struct_empty_function.snap delete mode 100644 crates/yulgen/tests/snapshots/yulgen__struct_getter_gen_bar2_function.snap delete mode 100644 crates/yulgen/tests/snapshots/yulgen__struct_getter_gen_bar_function.snap delete mode 100644 crates/yulgen/tests/snapshots/yulgen__struct_new_gen_function.snap delete mode 100644 crates/yulgen/tests/snapshots/yulgen__sum_operation.snap delete mode 100644 crates/yulgen/tests/yulgen.rs diff --git a/Cargo.lock b/Cargo.lock index 424c562af4..53ef9611bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -558,11 +558,9 @@ dependencies = [ name = "fe-abi" version = "0.16.0-alpha" dependencies = [ - "fe-analyzer", "fe-common", - "fe-parser", "serde", - "serde_json", + "serde_test", ] [[package]] @@ -597,10 +595,10 @@ dependencies = [ name = "fe-codegen" version = "0.16.0-alpha" dependencies = [ + "fe-abi", "fe-analyzer", "fe-common", "fe-mir", - "fe-new_abi", "fxhash", "num-bigint", "salsa", @@ -639,7 +637,6 @@ dependencies = [ "fe-driver", "fe-test-files", "fe-yulc", - "fe-yulgen", "getrandom 0.2.3", "hex", "indexmap", @@ -661,11 +658,9 @@ dependencies = [ "fe-common", "fe-compiler-test-utils", "fe-driver", - "fe-lowering", "fe-parser", "fe-test-files", "fe-yulc", - "fe-yulgen", "hex", "insta", "pretty_assertions", @@ -685,11 +680,9 @@ dependencies = [ "fe-analyzer", "fe-codegen", "fe-common", - "fe-lowering", "fe-mir", "fe-parser", "fe-yulc", - "fe-yulgen", "indexmap", "serde_json", "smol_str", @@ -704,23 +697,6 @@ dependencies = [ "include_dir", ] -[[package]] -name = "fe-lowering" -version = "0.16.0-alpha" -dependencies = [ - "fe-analyzer", - "fe-common", - "fe-parser", - "fe-test-files", - "indexmap", - "insta", - "pretty_assertions", - "regex", - "rstest", - "salsa", - "wasm-bindgen-test", -] - [[package]] name = "fe-mir" version = "0.16.0-alpha" @@ -739,15 +715,6 @@ dependencies = [ "smol_str", ] -[[package]] -name = "fe-new_abi" -version = "0.16.0-alpha" -dependencies = [ - "fe-common", - "serde", - "serde_test", -] - [[package]] name = "fe-parser" version = "0.16.0-alpha" @@ -781,34 +748,11 @@ dependencies = [ name = "fe-yulc" version = "0.16.0-alpha" dependencies = [ - "fe-yulgen", "indexmap", "serde_json", "solc", ] -[[package]] -name = "fe-yulgen" -version = "0.16.0-alpha" -dependencies = [ - "fe-abi", - "fe-analyzer", - "fe-common", - "fe-lowering", - "fe-parser", - "fe-test-files", - "if_chain", - "indexmap", - "insta", - "maplit", - "num-bigint", - "pretty_assertions", - "salsa", - "smol_str", - "wasm-bindgen-test", - "yultsur", -] - [[package]] name = "fixed-hash" version = "0.7.0" @@ -1138,12 +1082,6 @@ dependencies = [ "utf8-ranges", ] -[[package]] -name = "maplit" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" - [[package]] name = "memchr" version = "2.4.1" diff --git a/crates/abi/Cargo.toml b/crates/abi/Cargo.toml index b2cd51634a..9e1454c108 100644 --- a/crates/abi/Cargo.toml +++ b/crates/abi/Cargo.toml @@ -7,8 +7,8 @@ license = "Apache-2.0" repository = "https://github.com/ethereum/fe" [dependencies] -fe-common = {path = "../common", version = "^0.16.0-alpha"} -fe-parser = {path = "../parser", version = "^0.16.0-alpha"} -fe-analyzer = {path = "../analyzer", version = "^0.16.0-alpha"} -serde_json = "1.0" -serde = "1.0" +fe-common = { path = "../common", version = "^0.16.0-alpha" } +serde = { version = "1.0", features = ["derive"] } + +[dev-dependencies] +serde_test = "1.0" diff --git a/crates/abi/src/builder.rs b/crates/abi/src/builder.rs deleted file mode 100644 index 3453cfe34b..0000000000 --- a/crates/abi/src/builder.rs +++ /dev/null @@ -1,178 +0,0 @@ -use crate::elements::{ - Component, Contract, Event, EventField, FuncInput, FuncOutput, FuncType, Function, JsonAbi, - ModuleAbis, StateMutability, -}; -use crate::AbiError; -use fe_analyzer::namespace::items::{ContractId, FunctionId, ModuleId}; -use fe_analyzer::namespace::types; -use fe_analyzer::AnalyzerDb; - -/// Parse a map of contract ABIs from the input `module`. -pub fn module(db: &dyn AnalyzerDb, module: ModuleId) -> Result { - module - .all_contracts(db) - .iter() - .try_fold(ModuleAbis::new(), |mut abis, contract| { - if abis - .insert(contract.name(db).into(), contract_def(db, *contract)) - .is_some() - { - return Err(AbiError::DuplicateContractDefinition( - contract.name(db).into(), - )); - } - Ok(abis) - }) -} - -fn contract_def(db: &dyn AnalyzerDb, contract: ContractId) -> Contract { - let events = contract - .events(db) - .iter() - .map(|(name, eventid)| { - let attributes = eventid.typ(db); - Event { - name: name.to_string(), - typ: "event".to_string(), - fields: attributes - .fields - .iter() - .map(|field| { - let typ = field.typ.clone().expect("event field type error"); - EventField { - name: field.name.to_string(), - typ: typ.abi_json_name(), - indexed: field.is_indexed, - components: components(db, &typ), - } - }) - .collect(), - anonymous: false, - } - }) - .collect(); - - let mut functions = contract - .public_functions(db) - .iter() - .map(|(name, func)| function_def(db, name, *func, FuncType::Function)) - .collect::>(); - - if let Some(init_fn) = contract.init_function(db) { - functions.push(function_def(db, "", init_fn, FuncType::Constructor)); - } - - Contract { events, functions } -} - -fn function_def(db: &dyn AnalyzerDb, name: &str, fn_id: FunctionId, typ: FuncType) -> Function { - let sig = fn_id.signature(db); - let inputs = sig - .external_params() - .iter() - .map(|param| { - let typ = param.typ.clone().expect("function parameter type error"); - - FuncInput { - name: param.name.to_string(), - typ: typ.abi_json_name(), - components: components(db, &typ), - } - }) - .collect(); - - let return_type = sig.return_type.clone().expect("function return type error"); - let outputs = if return_type.is_unit() { - vec![] - } else { - vec![FuncOutput { - name: "".to_string(), - typ: return_type.abi_json_name(), - components: components(db, &return_type), - }] - }; - - Function { - name: name.to_string(), - typ, - inputs, - outputs, - state_mutability: StateMutability::Payable, - } -} - -fn components(db: &dyn AnalyzerDb, typ: &types::Type) -> Vec { - match typ { - types::Type::Struct(types::Struct { id, .. }) => id - .fields(db) - .iter() - .map(|(name, field_id)| Component { - name: name.to_string(), - typ: field_id - .typ(db) - .expect("struct field type error") - .abi_json_name(), - }) - .collect(), - types::Type::Tuple(types::Tuple { items }) => items - .iter() - .enumerate() - .map(|(index, item)| Component { - name: format!("item{}", index), - typ: item.abi_json_name(), - }) - .collect(), - _ => vec![], - } -} - -#[cfg(test)] -mod tests { - use crate::builder; - use fe_analyzer::namespace::items::ModuleId; - use fe_analyzer::TestDb; - use fe_common::diagnostics::print_diagnostics; - - #[test] - fn build_contract_abi() { - let contract = r#" -pub fn add(_ x: u256, _ y: u256) -> u256: - return x + y - -contract Foo: - event Food: - idx barge: u256 - pub fn __init__(x: address): - pass - fn baz(_ x: address) -> u256: - add(10, 20) - revert - pub fn bar(_ x: u256) -> Array: - revert"#; - - let mut db = TestDb::default(); - let module = ModuleId::new_standalone(&mut db, "test_module", contract); - - if !module.diagnostics(&db).is_empty() { - print_diagnostics(&db, &module.diagnostics(&db)); - panic!("failed to analyze source") - } - let abis = builder::module(&db, module).expect("unable to build ABI"); - - if let Some(abi) = abis.get("Foo") { - // event - assert_eq!(abi.events[0].name, "Food"); - // function count - assert_eq!(abi.functions.len(), 2); - // bar - assert_eq!(abi.functions[0].name, "bar",); - assert_eq!(abi.functions[0].inputs[0].typ, "uint256",); - assert_eq!(abi.functions[0].outputs[0].typ, "uint256[10]",); - // __init__ always comes after normal functions - assert_eq!(abi.functions[1].name, ""); - assert_eq!(abi.functions[1].inputs[0].typ, "address",); - } else { - panic!("contract \"Foo\" not found in module") - } - } -} diff --git a/crates/new_abi/src/contract.rs b/crates/abi/src/contract.rs similarity index 100% rename from crates/new_abi/src/contract.rs rename to crates/abi/src/contract.rs diff --git a/crates/abi/src/elements.rs b/crates/abi/src/elements.rs deleted file mode 100644 index 2e7b257d16..0000000000 --- a/crates/abi/src/elements.rs +++ /dev/null @@ -1,330 +0,0 @@ -use crate::errors::AbiError; -use fe_analyzer::namespace::types::{Array, Base, FeString, Integer, Struct, Tuple, Type}; -use serde::ser::SerializeSeq; -use serde::{Serialize, Serializer}; -use std::collections::HashMap; - -/// The ABIs for each contract in a Fe module. -pub type ModuleAbis = HashMap; - -/// All public interfaces of a Fe contract. -#[derive(Debug, PartialEq, Clone)] -pub struct Contract { - /// All events defined in a contract. - pub events: Vec, - /// All public functions defined in a contract. - pub functions: Vec, -} - -impl Default for Contract { - fn default() -> Self { - Self::new() - } -} - -impl Contract { - pub fn new() -> Self { - Self { - events: vec![], - functions: vec![], - } - } -} - -impl Contract { - /// Serialize the contract into a valid JSON ABI. - pub fn json(&self, prettify: bool) -> Result { - match prettify { - true => serde_json::to_string_pretty(self), - false => serde_json::to_string(self), - } - .map_err(|_| AbiError::SerializationFailed) - } -} - -impl Serialize for Contract { - fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> - where - S: Serializer, - { - let mut seq = serializer.serialize_seq(None)?; - - for event in self.events.iter() { - seq.serialize_element(event)?; - } - - for function in self.functions.iter() { - seq.serialize_element(function)?; - } - - seq.end() - } -} - -/// Single component of a tuple. -#[derive(Clone, Debug, PartialEq, PartialOrd, Ord, Eq)] -pub struct AbiComponent { - pub name: String, - pub typ: String, - /// The subcomponents of the component. - pub components: Vec, -} - -/// Information relevant to ABI encoding. -pub trait JsonAbi { - /// Name of the type as it appears in the Json ABI. - fn abi_json_name(&self) -> String; -} - -impl JsonAbi for Type { - fn abi_json_name(&self) -> String { - match self { - Type::Array(array) => array.abi_json_name(), - Type::Base(base) => base.abi_json_name(), - Type::Tuple(tuple) => tuple.abi_json_name(), - Type::String(string) => string.abi_json_name(), - Type::Contract(_) => "address".to_string(), - Type::Struct(val) => val.abi_json_name(), - _ => panic!("not abi encodable"), - } - } -} - -impl JsonAbi for Base { - fn abi_json_name(&self) -> String { - match self { - Base::Numeric(Integer::U256) => "uint256".to_string(), - Base::Numeric(Integer::U128) => "uint128".to_string(), - Base::Numeric(Integer::U64) => "uint64".to_string(), - Base::Numeric(Integer::U32) => "uint32".to_string(), - Base::Numeric(Integer::U16) => "uint16".to_string(), - Base::Numeric(Integer::U8) => "uint8".to_string(), - Base::Numeric(Integer::I256) => "int256".to_string(), - Base::Numeric(Integer::I128) => "int128".to_string(), - Base::Numeric(Integer::I64) => "int64".to_string(), - Base::Numeric(Integer::I32) => "int32".to_string(), - Base::Numeric(Integer::I16) => "int16".to_string(), - Base::Numeric(Integer::I8) => "int8".to_string(), - Base::Address => "address".to_string(), - Base::Bool => "bool".to_string(), - Base::Unit => panic!("unit type is not abi encodable"), - } - } -} - -impl JsonAbi for Array { - fn abi_json_name(&self) -> String { - if self.inner == Base::Numeric(Integer::U8) { - "bytes".to_string() - } else { - format!("{}[{}]", self.inner.abi_json_name(), self.size) - } - } -} - -impl JsonAbi for Struct { - fn abi_json_name(&self) -> String { - "tuple".to_string() - } -} - -impl JsonAbi for Tuple { - fn abi_json_name(&self) -> String { - "tuple".to_string() - } -} - -impl JsonAbi for FeString { - fn abi_json_name(&self) -> String { - "string".to_string() - } -} - -/// An event interface. -#[derive(Serialize, Debug, PartialEq, Clone)] -pub struct Event { - /// The event's name. - pub name: String, - /// The type of an event (Always "event"). - #[serde(rename = "type")] - pub typ: String, - /// All event fields. - #[serde(rename = "inputs")] - pub fields: Vec, - /// True if the event was declared as anonymous. - pub anonymous: bool, -} - -/// A single event field. -#[derive(Serialize, Debug, PartialEq, Clone)] -pub struct EventField { - /// The event field's name. - pub name: String, - /// The type of an event (e.g. u256, address, bytes100,...) - #[serde(rename = "type")] - pub typ: String, - /// True if the field is part of the log’s topics, false if it is one of the - /// log’s data segment. - pub indexed: bool, - /// Components of a tuple. This field is excluded if there are no - /// components. - #[serde(skip_serializing_if = "should_skip_components")] - pub components: Vec, -} - -/// A function interface. -#[derive(Serialize, Debug, PartialEq, Clone)] -#[serde(rename_all = "camelCase")] -pub struct Function { - /// The function's name. - pub name: String, - /// The type of a function (Function, Constructor, Receive, and Fallback) - #[serde(rename = "type")] - pub typ: FuncType, - /// All function inputs. - pub inputs: Vec, - /// All function outputs. - pub outputs: Vec, - /// Mutability of the function - pub state_mutability: StateMutability, -} - -/// Component of an ABI tuple. -#[derive(Serialize, Debug, PartialEq, Clone)] -pub struct Component { - pub name: String, - #[serde(rename = "type")] - pub typ: String, -} - -impl From for Component { - fn from(component: AbiComponent) -> Self { - if !component.components.is_empty() { - todo!("ABI serialization of subcomponents") - } - - Self { - name: component.name, - typ: component.typ, - } - } -} - -/// A single function input. -#[derive(Serialize, Debug, PartialEq, Clone)] -pub struct FuncInput { - /// The input's name. - pub name: String, - /// The input's type. - #[serde(rename = "type")] - pub typ: String, - /// Components of a tuple. This field is excluded if there are no - /// components. - #[serde(skip_serializing_if = "should_skip_components")] - pub components: Vec, -} - -/// A single function output. -#[derive(Serialize, Debug, PartialEq, Clone)] -pub struct FuncOutput { - /// The output's name. - pub name: String, - /// The output's type. - #[serde(rename = "type")] - pub typ: String, - /// Components of a tuple. This field is excluded if there are no - /// components. - #[serde(skip_serializing_if = "should_skip_components")] - pub components: Vec, -} - -fn should_skip_components(components: &[Component]) -> bool { - components.is_empty() -} - -/// The type of a public function. -#[allow(dead_code)] -#[derive(Serialize, Debug, PartialEq, Clone)] -#[serde(rename_all = "lowercase")] -pub enum FuncType { - Function, - Constructor, - Receive, - Fallback, -} - -/// The mutability of a public function. -#[derive(Serialize, Debug, PartialEq, Clone)] -#[serde(rename_all = "lowercase")] -pub enum StateMutability { - Pure, - View, - Nonpayable, - Payable, -} - -#[cfg(test)] -mod tests { - use crate::elements::{ - Contract, Event, EventField, FuncInput, FuncOutput, FuncType, Function, StateMutability, - }; - - #[test] - fn contract_json() { - let contract = Contract { - events: vec![Event { - name: "event_name".to_string(), - typ: "event".to_string(), - fields: vec![EventField { - name: "input_name".to_string(), - typ: "uint256".to_string(), - indexed: true, - components: vec![], - }], - anonymous: false, - }], - functions: vec![Function { - name: "function_name".to_string(), - typ: FuncType::Function, - inputs: vec![FuncInput { - name: "input_name".to_string(), - typ: "address".to_string(), - components: vec![], - }], - outputs: vec![FuncOutput { - name: "output_name".to_string(), - typ: "uint256".to_string(), - components: vec![], - }], - state_mutability: StateMutability::Payable, - }], - }; - - assert_eq!( - contract.json(false).unwrap(), - r#"[ - { - "name":"event_name", - "type":"event", - "inputs":[ - { - "name":"input_name", - "type":"uint256", - "indexed":true - } - ], - "anonymous":false - }, - { - "name":"function_name", - "type":"function", - "inputs":[{"name":"input_name","type":"address"}], - "outputs":[{"name":"output_name","type":"uint256"}], - "stateMutability":"payable" - } - ]"# - .split_whitespace() - .collect::(), - ) - } -} diff --git a/crates/abi/src/errors.rs b/crates/abi/src/errors.rs deleted file mode 100644 index 7b2af11b87..0000000000 --- a/crates/abi/src/errors.rs +++ /dev/null @@ -1,5 +0,0 @@ -#[derive(Debug)] -pub enum AbiError { - DuplicateContractDefinition(String), - SerializationFailed, -} diff --git a/crates/new_abi/src/event.rs b/crates/abi/src/event.rs similarity index 100% rename from crates/new_abi/src/event.rs rename to crates/abi/src/event.rs diff --git a/crates/new_abi/src/function.rs b/crates/abi/src/function.rs similarity index 100% rename from crates/new_abi/src/function.rs rename to crates/abi/src/function.rs diff --git a/crates/abi/src/lib.rs b/crates/abi/src/lib.rs index e4a78c267f..7860aec9c2 100644 --- a/crates/abi/src/lib.rs +++ b/crates/abi/src/lib.rs @@ -1,29 +1,4 @@ -//! Fe to ABI builder. - -use fe_analyzer::namespace::items::ModuleId; -use fe_analyzer::AnalyzerDb; -use std::collections::HashMap; - -mod builder; -pub mod utils; - -/// Elements used to define contract ABIs. -pub mod elements; - -mod errors; -pub use errors::AbiError; - -/// A mapping of contract names and their ABIs. -pub type NamedAbis = HashMap; -/// The ABI of a contract as a string. -pub type JsonAbi = String; -/// The name of a Fe contract. -pub type ContractName = String; - -/// Builds ABIs for each contract in the module. -pub fn build(db: &dyn AnalyzerDb, module: ModuleId) -> Result { - builder::module(db, module)? - .drain() - .map(|(name, abi)| abi.json(true).map(|json| (name, json))) - .collect::>() -} +pub mod contract; +pub mod event; +pub mod function; +pub mod types; diff --git a/crates/new_abi/src/types.rs b/crates/abi/src/types.rs similarity index 100% rename from crates/new_abi/src/types.rs rename to crates/abi/src/types.rs diff --git a/crates/abi/src/utils.rs b/crates/abi/src/utils.rs deleted file mode 100644 index 45e0b77a4c..0000000000 --- a/crates/abi/src/utils.rs +++ /dev/null @@ -1,18 +0,0 @@ -use fe_common::utils::keccak; - -/// Formats the name and fields and calculates the 32 byte keccak256 value of -/// the signature. -pub fn event_topic(name: &str, fields: &[String]) -> String { - hash_signature(name, fields, 32) -} - -/// Formats the name and params and calculates the 4 byte keccak256 value of the -/// signature. -pub fn func_selector(name: &str, params: &[String]) -> String { - hash_signature(name, params, 4) -} - -fn hash_signature(name: &str, params: &[String], size: usize) -> String { - let signature = format!("{}({})", name, params.join(",")); - format!("0x{}", keccak::partial(signature.as_bytes(), size)) -} diff --git a/crates/codegen/Cargo.toml b/crates/codegen/Cargo.toml index 9edf15271c..19f33e6da3 100644 --- a/crates/codegen/Cargo.toml +++ b/crates/codegen/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" fe-analyzer = { path = "../analyzer", version = "^0.16.0-alpha"} fe-mir = { path = "../mir", version = "^0.16.0-alpha" } fe-common = { path = "../common", version = "^0.16.0-alpha" } -fe-new_abi = { path = "../new_abi", version = "^0.16.0-alpha" } +fe-abi = { path = "../abi", version = "^0.16.0-alpha" } salsa = "0.16.1" num-bigint = "0.4.3" fxhash = "0.2.1" diff --git a/crates/codegen/src/db.rs b/crates/codegen/src/db.rs index 16ee725ed5..2808dddb80 100644 --- a/crates/codegen/src/db.rs +++ b/crates/codegen/src/db.rs @@ -1,12 +1,12 @@ use std::rc::Rc; +use fe_abi::{contract::AbiContract, event::AbiEvent, function::AbiFunction, types::AbiType}; use fe_analyzer::{db::AnalyzerDbStorage, namespace::items::ContractId, AnalyzerDb}; use fe_common::db::{SourceDb, SourceDbStorage, Upcast, UpcastMut}; use fe_mir::{ db::{MirDb, MirDbStorage}, ir::{FunctionBody, FunctionId, FunctionSignature, TypeId}, }; -use fe_new_abi::{contract::AbiContract, event::AbiEvent, function::AbiFunction, types::AbiType}; mod queries; diff --git a/crates/codegen/src/db/queries/abi.rs b/crates/codegen/src/db/queries/abi.rs index 19d5113b2c..f89bbee0ec 100644 --- a/crates/codegen/src/db/queries/abi.rs +++ b/crates/codegen/src/db/queries/abi.rs @@ -1,11 +1,11 @@ -use fe_analyzer::namespace::items::ContractId; -use fe_mir::ir::{self, FunctionId, TypeId}; -use fe_new_abi::{ +use fe_abi::{ contract::AbiContract, event::{AbiEvent, AbiEventField}, function::{AbiFunction, AbiFunctionType}, types::{AbiTupleField, AbiType}, }; +use fe_analyzer::namespace::items::ContractId; +use fe_mir::ir::{self, FunctionId, TypeId}; use crate::db::CodegenDb; diff --git a/crates/codegen/src/yul/isel/function.rs b/crates/codegen/src/yul/isel/function.rs index 2516f78691..46f60c5c9e 100644 --- a/crates/codegen/src/yul/isel/function.rs +++ b/crates/codegen/src/yul/isel/function.rs @@ -1,6 +1,7 @@ #![allow(unused)] use super::{context::Context, inst_order::InstSerializer}; +use fe_abi::function::{AbiFunction, AbiFunctionType}; use fe_common::db::Upcast; use fe_mir::ir::{ self, @@ -10,7 +11,6 @@ use fe_mir::ir::{ Constant, FunctionBody, FunctionId, FunctionSignature, InstId, Type, TypeId, TypeKind, Value, ValueId, }; -use fe_new_abi::function::{AbiFunction, AbiFunctionType}; use fxhash::FxHashMap; use smol_str::SmolStr; use yultsur::{ diff --git a/crates/codegen/src/yul/runtime/revert.rs b/crates/codegen/src/yul/runtime/revert.rs index caf9aa4214..cdc31eb030 100644 --- a/crates/codegen/src/yul/runtime/revert.rs +++ b/crates/codegen/src/yul/runtime/revert.rs @@ -5,8 +5,8 @@ use crate::{ use super::{DefaultRuntimeProvider, RuntimeFunction, RuntimeProvider}; +use fe_abi::function::{AbiFunction, AbiFunctionType}; use fe_mir::ir::{self, TypeId}; -use fe_new_abi::function::{AbiFunction, AbiFunctionType}; use yultsur::*; pub(super) fn make_revert( diff --git a/crates/driver/Cargo.toml b/crates/driver/Cargo.toml index 64147cf7c2..baba9c516d 100644 --- a/crates/driver/Cargo.toml +++ b/crates/driver/Cargo.toml @@ -15,11 +15,9 @@ serde_json = "1.0" fe-abi = {path = "../abi", version = "^0.16.0-alpha"} fe-analyzer = {path = "../analyzer", version = "^0.16.0-alpha"} fe-common = {path = "../common", version = "^0.16.0-alpha"} -fe-lowering = {path = "../lowering", version = "^0.16.0-alpha"} fe-mir = {path = "../mir", version = "^0.16.0-alpha"} fe-codegen = {path = "../codegen", version = "^0.16.0-alpha"} fe-parser = {path = "../parser", version = "^0.16.0-alpha"} -fe-yulgen = {path = "../yulgen", version = "^0.16.0-alpha"} fe-yulc = {path = "../yulc", version = "^0.16.0-alpha", features = ["solc-backend"], optional = true} indexmap = "1.6.2" vfs = "0.5.1" diff --git a/crates/driver/src/lib.rs b/crates/driver/src/lib.rs index 8d355f8558..3bfd47de34 100644 --- a/crates/driver/src/lib.rs +++ b/crates/driver/src/lib.rs @@ -2,7 +2,6 @@ pub use fe_codegen::db::{CodegenDb, NewDb}; use fe_codegen::yul::runtime::RuntimeProvider; -pub use fe_yulgen::Db; use fe_analyzer::namespace::items::{IngotId, IngotMode, ModuleId}; use fe_analyzer::AnalyzerDb; @@ -12,7 +11,6 @@ use fe_common::diagnostics::{print_diagnostics, Diagnostic}; use fe_common::files::{FileKind, SourceFileId}; use fe_mir::db::MirDb; use fe_parser::ast::SmolStr; -use fe_yulgen::YulgenDb; use indexmap::{indexmap, IndexMap}; #[cfg(feature = "solc-backend")] use serde_json::Value; diff --git a/crates/lowering/Cargo.toml b/crates/lowering/Cargo.toml deleted file mode 100644 index 4bb350a8f3..0000000000 --- a/crates/lowering/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "fe-lowering" -version = "0.16.0-alpha" -authors = ["The Fe Developers "] -edition = "2021" -license = "Apache-2.0" -repository = "https://github.com/ethereum/fe" - -[dependencies] -fe-common = {path = "../common", version = "^0.16.0-alpha"} -fe-parser = {path = "../parser", version = "^0.16.0-alpha"} -fe-analyzer = {path = "../analyzer", version = "^0.16.0-alpha"} -indexmap = "1.6.2" -salsa = "0.16.1" - -[dev-dependencies] -rstest = "0.6.4" -regex = "1.1.0" -test-files = {path = "../test-files", package = "fe-test-files" } -wasm-bindgen-test = "0.3" -insta = "1.7.1" -pretty_assertions = "1.0.0" diff --git a/crates/lowering/src/ast_utils.rs b/crates/lowering/src/ast_utils.rs deleted file mode 100644 index 34f40797c5..0000000000 --- a/crates/lowering/src/ast_utils.rs +++ /dev/null @@ -1,960 +0,0 @@ -use fe_analyzer::namespace::types::Type; -use fe_parser::ast::{BoolOperator, CallArg, Expr, FuncStmt, UnaryOperator, VarDeclTarget}; -use fe_parser::node::{Node, NodeId}; - -use crate::names; -use crate::utils::ZeroSpanNode; -use std::ops::Deref; - -#[derive(Debug)] -pub enum StmtOrExpr { - Stmt(Box>), - Expr(Node), -} - -impl From> for StmtOrExpr { - fn from(stmt: Node) -> Self { - StmtOrExpr::Stmt(Box::new(stmt)) - } -} - -impl From> for StmtOrExpr { - fn from(expr: Node) -> Self { - StmtOrExpr::Expr(expr) - } -} - -impl StmtOrExpr { - pub fn as_stmt(&self) -> Node { - match self { - StmtOrExpr::Stmt(stmt) => stmt.deref().clone(), - StmtOrExpr::Expr(_) => panic!("not a statement"), - } - } - - pub fn as_expr(&self) -> Node { - match self { - StmtOrExpr::Expr(expr) => expr.clone(), - StmtOrExpr::Stmt(_) => panic!("not an expression"), - } - } -} - -/// Recursively map the given `node` by applying the given `map_fn` -pub fn map_ast_node(node: StmtOrExpr, map_fn: &mut M) -> StmtOrExpr -where - M: FnMut(StmtOrExpr) -> StmtOrExpr, -{ - match node { - StmtOrExpr::Stmt(stmt) => { - let node = match stmt.kind { - FuncStmt::Assert { test, msg } => FuncStmt::Assert { - test: map_ast_node(test.into(), map_fn).as_expr(), - msg: msg.map(|val| map_ast_node(val.into(), map_fn).as_expr()), - }, - FuncStmt::Assign { target, value } => FuncStmt::Assign { - target: map_ast_node(target.into(), map_fn).as_expr(), - value: map_ast_node(value.into(), map_fn).as_expr(), - }, - FuncStmt::AugAssign { target, op, value } => FuncStmt::AugAssign { - target: map_ast_node(target.into(), map_fn).as_expr(), - value: map_ast_node(value.into(), map_fn).as_expr(), - op, - }, - FuncStmt::Emit { name, args } => FuncStmt::Emit { - name, - args: map_call_args(args, map_fn), - }, - FuncStmt::Expr { value } => FuncStmt::Expr { - value: map_ast_node(value.into(), map_fn).as_expr(), - }, - FuncStmt::For { target, iter, body } => FuncStmt::For { - target, - iter: map_ast_node(iter.into(), map_fn).as_expr(), - body: map_body(body, map_fn), - }, - FuncStmt::If { - body, - test, - or_else, - } => FuncStmt::If { - body: map_body(body, map_fn), - or_else: map_body(or_else, map_fn), - test: map_ast_node(test.into(), map_fn).as_expr(), - }, - - FuncStmt::Return { value } => FuncStmt::Return { - value: value.map(|val| map_ast_node(val.into(), map_fn).as_expr()), - }, - FuncStmt::Revert { error } => FuncStmt::Revert { - error: error.map(|val| map_ast_node(val.into(), map_fn).as_expr()), - }, - FuncStmt::Unsafe(body) => FuncStmt::Unsafe(map_body(body, map_fn)), - FuncStmt::VarDecl { target, typ, value } => FuncStmt::VarDecl { - target, - typ, - value: value.map(|val| map_ast_node(val.into(), map_fn).as_expr()), - }, - FuncStmt::ConstantDecl { name, typ, value } => FuncStmt::ConstantDecl { - name, - typ, - value: map_ast_node(value.into(), map_fn).as_expr(), - }, - FuncStmt::While { test, body } => FuncStmt::While { - test: map_ast_node(test.into(), map_fn).as_expr(), - body: map_body(body, map_fn), - }, - // See comment below for why no catch all should be used here - FuncStmt::Pass | FuncStmt::Break | FuncStmt::Continue => stmt.kind, - } - .into_traceable_node(stmt.original_id); - - map_fn(node.into()) - } - StmtOrExpr::Expr(expr) => { - let expr = match expr.kind { - Expr::Attribute { value, attr } => Expr::Attribute { - value: Box::new(map_ast_node((*value).into(), map_fn).as_expr()), - attr, - }, - Expr::BinOperation { left, op, right } => Expr::BinOperation { - left: Box::new(map_ast_node((*left).into(), map_fn).as_expr()), - right: Box::new(map_ast_node((*right).into(), map_fn).as_expr()), - op, - }, - Expr::BoolOperation { left, op, right } => Expr::BoolOperation { - left: Box::new(map_ast_node((*left).into(), map_fn).as_expr()), - right: Box::new(map_ast_node((*right).into(), map_fn).as_expr()), - op, - }, - Expr::Call { - args, - func, - generic_args, - } => Expr::Call { - args: map_call_args(args, map_fn), - func: Box::new(map_ast_node((*func).into(), map_fn).as_expr()), - generic_args, - }, - Expr::CompOperation { left, op, right } => Expr::CompOperation { - left: Box::new(map_ast_node((*left).into(), map_fn).as_expr()), - right: Box::new(map_ast_node((*right).into(), map_fn).as_expr()), - op, - }, - Expr::List { elts } => Expr::List { - elts: elts - .into_iter() - .map(|val| map_ast_node(val.into(), map_fn).as_expr()) - .collect(), - }, - Expr::Subscript { value, index } => Expr::Subscript { - value: Box::new(map_ast_node((*value).into(), map_fn).as_expr()), - index: Box::new(map_ast_node((*index).into(), map_fn).as_expr()), - }, - Expr::Ternary { - if_expr, - test, - else_expr, - } => Expr::Ternary { - if_expr: Box::new(map_ast_node((*if_expr).into(), map_fn).as_expr()), - test: Box::new(map_ast_node((*test).into(), map_fn).as_expr()), - else_expr: Box::new(map_ast_node((*else_expr).into(), map_fn).as_expr()), - }, - Expr::Tuple { elts } => Expr::Tuple { - elts: elts - .into_iter() - .map(|val| map_ast_node(val.into(), map_fn).as_expr()) - .collect(), - }, - Expr::UnaryOperation { op, operand } => Expr::UnaryOperation { - op, - operand: Box::new(map_ast_node((*operand).into(), map_fn).as_expr()), - }, - // The following *could* be covered via catch all. However, that would turn into a footgun if we add - // more expressions in the future that need to be walked. It's better to not use a catch all here. - Expr::Bool(_) - | Expr::Name(_) - | Expr::Num(_) - | Expr::Path(_) - | Expr::Str(_) - | Expr::Unit => expr.kind, - } - .into_traceable_node(expr.original_id); - - map_fn(expr.into()) - } - } -} - -fn map_call_args(args: Node>>, map_fn: &mut M) -> Node>> -where - M: FnMut(StmtOrExpr) -> StmtOrExpr, -{ - args.kind - .into_iter() - .map(|call_arg| { - CallArg { - label: call_arg.kind.label, - value: map_ast_node(call_arg.kind.value.into(), map_fn).as_expr(), - } - .into_traceable_node(call_arg.original_id) - }) - .collect::>() - .into_traceable_node(args.original_id) -} - -fn map_body(body: Vec>, map_fn: &mut M) -> Vec> -where - M: FnMut(StmtOrExpr) -> StmtOrExpr, -{ - body.into_iter() - .map(|val| map_ast_node(val.into(), map_fn).as_stmt()) - .collect() -} - -/// Returns `true` if the `node_id` matches the `orignal_id` of `node` or any of its children. -pub fn contains_node(node: &Node, node_id: NodeId) -> bool { - let mut success = false; - let wrapper = FuncStmt::Expr { - value: node.clone(), - } - .into_node(); - map_ast_node(wrapper.into(), &mut |val| { - match val { - StmtOrExpr::Expr(ref expr) if expr.original_id == node_id => { - success = true; - } - StmtOrExpr::Stmt(ref stmt) if stmt.original_id == node_id => { - success = true; - } - _ => {} - } - - val - }); - - success -} - -/// Takes a mutable `body` of statements and inserts an `injection` of statements if any of the -/// `test_nodes` or their children contain the given `target_nodeid`. -pub fn inject_if_contains_target( - body: &mut Vec>, - injection: &[Node], - test_nodes: &[&Node], - target_nodeid: NodeId, -) -> bool { - if test_nodes - .iter() - .any(|val| contains_node(val, target_nodeid)) - { - for item in injection { - body.push(item.clone()) - } - return true; - } - false -} - -/// Like `inject_if_contains_target` but appends a `fallback` statement to the body if otherwise -/// no injection would take place. -pub fn inject_or_add_current( - body: &mut Vec>, - injection: &[Node], - test: &[&Node], - target: NodeId, - fallback: &Node, -) { - if !inject_if_contains_target(body, injection, test, target) { - body.push(fallback.clone()); - } -} - -/// Inject a series of statements within a body of existing statements right before -/// a given `expression` occures but not any earlier. -pub fn inject_before_expression( - body: &[Node], - expression: NodeId, - injection: &[Node], -) -> Vec> { - let mut transformed_body = vec![]; - - for stmt in body { - let injection_and_current_stmt = &[injection, &[stmt.clone()]].concat(); - match stmt.kind.clone() { - FuncStmt::If { - body, - test, - or_else, - } => { - if !inject_if_contains_target( - &mut transformed_body, - injection_and_current_stmt, - &[&test], - expression, - ) { - transformed_body.push( - FuncStmt::If { - body: inject_before_expression(&body, expression, injection), - or_else: inject_before_expression(&or_else, expression, injection), - test, - } - .into_traceable_node(stmt.original_id), - ); - } - } - FuncStmt::For { target, iter, body } => { - if !inject_if_contains_target( - &mut transformed_body, - injection_and_current_stmt, - &[&iter], - expression, - ) { - transformed_body.push( - FuncStmt::For { - target, - iter, - body: inject_before_expression(&body, expression, injection), - } - .into_traceable_node(stmt.original_id), - ); - } - } - FuncStmt::While { test, body } => { - if !inject_if_contains_target( - &mut transformed_body, - injection_and_current_stmt, - &[&test], - expression, - ) { - transformed_body.push( - FuncStmt::While { - test, - body: inject_before_expression(&body, expression, injection), - } - .into_traceable_node(stmt.original_id), - ) - } - } - FuncStmt::Unsafe(body) => transformed_body.push( - FuncStmt::Unsafe(inject_before_expression(&body, expression, injection)) - .into_traceable_node(stmt.original_id), - ), - // The following statements contain no further sub statements, only expressions. - // At this point it doesn't matter how deeply nested our expression is found because - // expressions can not contain statements. - // If we find it somewhere in the expression tree, we will inject the code right before it. - FuncStmt::Expr { value } | FuncStmt::ConstantDecl { value, .. } => { - inject_or_add_current( - &mut transformed_body, - injection_and_current_stmt, - &[&value], - expression, - stmt, - ); - } - FuncStmt::Assign { target, value } => { - inject_or_add_current( - &mut transformed_body, - injection_and_current_stmt, - &[&value, &target], - expression, - stmt, - ); - } - FuncStmt::VarDecl { value, .. } | FuncStmt::Return { value } => { - if let Some(val) = value { - inject_or_add_current( - &mut transformed_body, - injection_and_current_stmt, - &[&val], - expression, - stmt, - ); - } else { - transformed_body.push(stmt.clone()) - } - } - FuncStmt::Assert { test, msg } => { - if let Some(msg) = msg { - inject_or_add_current( - &mut transformed_body, - injection_and_current_stmt, - &[&test, &msg], - expression, - stmt, - ); - } else { - inject_or_add_current( - &mut transformed_body, - injection_and_current_stmt, - &[&test], - expression, - stmt, - ); - } - } - FuncStmt::AugAssign { target, value, .. } => { - inject_or_add_current( - &mut transformed_body, - injection_and_current_stmt, - &[&target, &value], - expression, - stmt, - ); - } - FuncStmt::Emit { args, .. } => { - if args - .kind - .iter() - .any(|val| contains_node(&val.kind.value, expression)) - { - transformed_body = [&transformed_body, injection].concat(); - } - transformed_body.push(stmt.clone()); - } - FuncStmt::Revert { error } => { - if let Some(error) = error { - inject_or_add_current( - &mut transformed_body, - injection_and_current_stmt, - &[&error], - expression, - stmt, - ) - } else { - transformed_body.push(stmt.clone()) - } - } - FuncStmt::Break | FuncStmt::Continue | FuncStmt::Pass => { - transformed_body.push(stmt.clone()) - } - } - } - transformed_body -} - -/// Turns a ternary expression into a set of statements resembling an if/else block with equal -/// functionality. Expects the type and variable result name to be provided as parameters. -pub fn ternary_to_if( - ternary_type: Type, - expr: &Node, - result_name: &str, -) -> Vec> { - if let Expr::Ternary { - if_expr, - test, - else_expr, - } = &expr.kind - { - let mut stmts = vec![FuncStmt::VarDecl { - target: VarDeclTarget::Name(result_name.into()).into_node(), - typ: names::build_type_desc(&ternary_type).into_node(), - value: None, - } - .into_node()]; - - let if_branch = FuncStmt::Assign { - target: Expr::Name(result_name.into()).into_node(), - value: if_expr - .kind - .clone() - .into_traceable_node(if_expr.original_id), - } - .into_node(); - - let else_branch = FuncStmt::Assign { - target: Expr::Name(result_name.into()).into_node(), - value: else_expr - .kind - .clone() - .into_traceable_node(else_expr.original_id), - } - .into_node(); - - stmts.push( - FuncStmt::If { - test: test.kind.clone().into_node(), - body: vec![if_branch], - or_else: vec![else_branch], - } - .into_node(), - ); - - return stmts; - } - - unreachable!() -} - -/// Turns a boolean expression into a set of statements resembling an if/else block with equal -/// functionality. Expects the type and variable result name to be provided as parameters. -pub fn boolean_expr_to_if( - expr_type: Type, - expr: &Node, - result_name: &str, -) -> Vec> { - if let Expr::BoolOperation { left, op, right } = &expr.kind { - return match op.kind { - BoolOperator::And => { - // from: left && right - - // into: - // res: bool = false - // if left: - // res = right - - let mut stmts = vec![FuncStmt::VarDecl { - target: VarDeclTarget::Name(result_name.into()).into_node(), - typ: names::build_type_desc(&expr_type).into_node(), - value: Some(Expr::Bool(false).into_node()), - } - .into_node()]; - let if_branch = FuncStmt::Assign { - target: Expr::Name(result_name.into()).into_node(), - value: right.kind.clone().into_traceable_node(right.original_id), - } - .into_node(); - - stmts.push( - FuncStmt::If { - test: left.kind.clone().into_traceable_node(left.original_id), - body: vec![if_branch], - or_else: vec![], - } - .into_node(), - ); - stmts - } - BoolOperator::Or => { - // from: left || right - // into: - // res: bool = true - // if not left: - // res = right - let mut stmts = vec![FuncStmt::VarDecl { - target: VarDeclTarget::Name(result_name.into()).into_node(), - typ: names::build_type_desc(&expr_type).into_node(), - value: Some(Expr::Bool(true).into_node()), - } - .into_node()]; - let if_branch = FuncStmt::Assign { - target: Expr::Name(result_name.into()).into_node(), - value: right.kind.clone().into_traceable_node(right.original_id), - } - .into_node(); - - stmts.push( - FuncStmt::If { - test: Expr::UnaryOperation { - op: UnaryOperator::Not.into_node(), - operand: Box::new( - left.kind.clone().into_traceable_node(left.original_id), - ), - } - .into_node(), - body: vec![if_branch], - or_else: vec![], - } - .into_node(), - ); - stmts - } - }; - } - - unreachable!() -} - -/// Returns a vector of expressions with all ternary expressions that are -/// contained within the given function statement. The last expression -/// in the list is the outermost ternary expression found in the statement. -pub fn get_all_ternary_expressions(node: &Node) -> Vec> { - let mut terns = vec![]; - map_ast_node(node.clone().into(), &mut |exp| { - if let StmtOrExpr::Expr(expr) = &exp { - if let Expr::Ternary { .. } = expr.kind { - terns.push(expr.clone()) - } - } - - exp - }); - - terns -} - -/// For a given set of nodes returns the first set of ternary expressions that can be found. -/// The last expression in the list is the outermost ternary expression found in the statement. -pub fn get_first_ternary_expressions(nodes: &[Node]) -> Vec> { - for node in nodes { - let result = get_all_ternary_expressions(node); - if !result.is_empty() { - return result; - } - } - vec![] -} - -/// Returns a vector of expressions with all boolean expressions that are -/// contained within the given function statement. The last expression -/// in the list is the outermost boolean expression found in the statement. -pub fn get_all_boolean_expressions(node: &Node) -> Vec> { - let mut expressions = vec![]; - map_ast_node(node.clone().into(), &mut |exp| { - if let StmtOrExpr::Expr(expr) = &exp { - if let Expr::BoolOperation { .. } = expr.kind { - expressions.push(expr.clone()) - } - } - - exp - }); - - expressions -} - -/// For a given set of nodes returns the first set of boolean expressions that can be found. -/// The last expression in the list is the outermost boolean expression found in the statement. -pub fn get_first_boolean_expressions(nodes: &[Node]) -> Vec> { - for node in nodes { - let result = get_all_boolean_expressions(node); - if !result.is_empty() { - return result; - } - } - vec![] -} - -/// In a given set of `nodes replaces a node that matches the `node_id` with a name expression node. -pub fn replace_node_with_name_expression( - nodes: &[Node], - node_id: NodeId, - name: &str, -) -> Vec> { - nodes - .iter() - .map(|node| { - map_ast_node(node.clone().into(), &mut |val| match val { - StmtOrExpr::Expr(expr) if expr.original_id == node_id => { - Expr::Name(name.into()).into_node().into() - } - _ => val, - }) - .as_stmt() - }) - .collect() -} - -#[cfg(test)] -mod tests { - - use crate::ast_utils::get_first_ternary_expressions; - use crate::ast_utils::inject_before_expression; - use crate::ast_utils::map_ast_node; - use crate::ast_utils::replace_node_with_name_expression; - use crate::ast_utils::ternary_to_if; - use crate::ast_utils::StmtOrExpr; - use crate::utils::ZeroSpanNode; - use fe_analyzer::namespace::types::Type; - use fe_parser::ast::BinOperator; - use fe_parser::ast::CallArg; - use fe_parser::ast::Expr; - use fe_parser::ast::FuncStmt; - use fe_parser::node::Node; - use std::vec; - - fn to_code(body: &[Node]) -> String { - let mut source = String::new(); - for stmt in body { - source.push_str(&stmt.kind.to_string()); - source.push('\n'); - } - source.trim().into() - } - - #[test] - fn transform_statement() { - let stmt = FuncStmt::Return { value: None }.into_node(); - assert_eq!(to_code(&[stmt.clone()]), "return"); - - let transformed_stmt = map_ast_node(stmt.into(), &mut |stmt| { - if let StmtOrExpr::Stmt(stmt) = stmt { - let node = match stmt.kind { - FuncStmt::Return { .. } => FuncStmt::Return { - value: Some(Expr::Name("foo".into()).into_node()), - }, - _ => stmt.kind, - } - .into_node(); - return node.into(); - } - stmt - }) - .as_stmt(); - - assert_eq!(to_code(&[transformed_stmt]), "return foo"); - } - - #[test] - fn transform_expr() { - let stmt = FuncStmt::Return { - value: Some(Expr::Name("foo".into()).into_node()), - } - .into_node(); - assert_eq!(to_code(&[stmt.clone()]), "return foo"); - - let mut transform_expr = |expr| { - if let StmtOrExpr::Expr(expr) = expr { - let node = match expr.kind { - Expr::Name(_) => Expr::Name("bar".into()), - _ => expr.kind, - } - .into_node(); - return node.into(); - } - expr - }; - - let transformed_stmt = map_ast_node(stmt.into(), &mut transform_expr).as_stmt(); - - assert_eq!(to_code(&[transformed_stmt]), "return bar"); - } - - #[test] - fn count_expr() { - let add_expr = Expr::BinOperation { - left: Expr::Num("1".into()).into_boxed_node(), - right: Expr::Num("2".into()).into_boxed_node(), - op: BinOperator::Add.into_node(), - }; - let stmt = FuncStmt::Return { - value: Some(add_expr.into_node()), - } - .into_node(); - - let mut counter = 0; - - let mut transform_expr = |expr| { - if let StmtOrExpr::Expr(expr) = expr { - let node = match expr.kind.clone() { - Expr::Num(_) => { - counter += 1; - expr.kind - } - _ => expr.kind, - } - .into_node(); - return node.into(); - } - - expr - }; - - map_ast_node(stmt.into(), &mut transform_expr); - - assert_eq!(counter, 2) - } - - #[test] - fn inject_expression() { - let nested_ternary = Expr::Ternary { - test: Expr::Bool(true).into_boxed_node(), - if_expr: Expr::Num("1".into()).into_boxed_node(), - else_expr: Expr::Num("2".into()).into_boxed_node(), - } - .into_boxed_node(); - - let ternary = Expr::Ternary { - test: Expr::Bool(true).into_boxed_node(), - if_expr: Expr::Num("1".into()).into_boxed_node(), - else_expr: nested_ternary, - } - .into_node(); - - let replacement = vec![FuncStmt::Return { value: None }.into_node()]; - - let stmt = FuncStmt::Expr { - value: ternary.clone(), - } - .into_node(); - - let original_body = vec![stmt]; - assert_eq!(to_code(&original_body), "1 if true else 1 if true else 2"); - let body = inject_before_expression(&original_body, ternary.original_id, &replacement); - - assert_eq!( - to_code(&body), - "return -1 if true else 1 if true else 2" - ); - } - - #[test] - fn inject_expression_nested() { - let nested_ternary = Expr::Ternary { - test: Expr::Bool(true).into_boxed_node(), - if_expr: Expr::Num("1".into()).into_boxed_node(), - else_expr: Expr::Num("2".into()).into_boxed_node(), - } - .into_boxed_node(); - - let ternary = Expr::Ternary { - test: Expr::Bool(true).into_boxed_node(), - if_expr: Expr::Num("1".into()).into_boxed_node(), - else_expr: nested_ternary, - } - .into_node(); - - let if_else = FuncStmt::If { - test: Expr::Bool(true).into_node(), - body: vec![], - or_else: vec![FuncStmt::Expr { - value: ternary.clone(), - } - .into_node()], - } - .into_node(); - - let replacement = vec![FuncStmt::Return { value: None }.into_node()]; - - let original_body = vec![if_else]; - assert_eq!( - to_code(&original_body), - "if true: - -else: - 1 if true else 1 if true else 2" - ); - - let body = inject_before_expression(&original_body, ternary.original_id, &replacement); - - assert_eq!( - to_code(&body), - "if true: - -else: - return - 1 if true else 1 if true else 2" - ); - } - - #[test] - fn lower_nested_ternary() { - let nested_ternary = Expr::Ternary { - test: Expr::Bool(true).into_boxed_node(), - if_expr: Expr::Num("1".into()).into_boxed_node(), - else_expr: Expr::Num("2".into()).into_boxed_node(), - } - .into_boxed_node(); - - let ternary = Expr::Ternary { - test: Expr::Bool(true).into_boxed_node(), - if_expr: Expr::Num("1".into()).into_boxed_node(), - else_expr: nested_ternary.clone(), - } - .into_node(); - - let call = Expr::Call { - func: Expr::Name("foo".into()).into_boxed_node(), - args: vec![CallArg { - value: ternary.clone(), - label: None, - } - .into_node()] - .into_node(), - generic_args: None, - } - .into_node(); - - let stmt = FuncStmt::Expr { value: call }.into_node(); - - let original_body = vec![stmt]; - - assert_eq!( - to_code(&original_body), - "foo(1 if true else 1 if true else 2)" - ); - - let all_ternary = get_first_ternary_expressions(&original_body); - - assert_eq!(all_ternary.len(), 2); - - // They are collected inside-out which means the lowest level is at the end of the vec. - let first_ternary = all_ternary.get(0).unwrap(); - assert_eq!(first_ternary.original_id, nested_ternary.original_id); - let second_ternary = all_ternary.get(1).unwrap(); - assert_eq!(second_ternary.original_id, ternary.original_id); - - let ternary_type = Type::u256(); - - let transformed_outer = ternary_to_if(ternary_type.clone(), second_ternary, "outer"); - - assert_eq!( - to_code(&transformed_outer), - "let outer: u256 -if true: - outer = 1 -else: - outer = 1 if true else 2" - ); - - let new_body = inject_before_expression( - &original_body, - second_ternary.original_id, - &transformed_outer, - ); - - assert_eq!( - to_code(&new_body), - "let outer: u256 -if true: - outer = 1 -else: - outer = 1 if true else 2 - -foo(1 if true else 1 if true else 2)" - ); - - let new_body = - replace_node_with_name_expression(&new_body, second_ternary.original_id, "outer"); - - assert_eq!( - to_code(&new_body), - "let outer: u256 -if true: - outer = 1 -else: - outer = 1 if true else 2 - -foo(outer)" - ); - - let remaining_terns = get_first_ternary_expressions(&new_body); - let last_ternary = remaining_terns.last().unwrap(); - let transformed_inner = ternary_to_if(ternary_type, last_ternary, "inner"); - - let new_body = - inject_before_expression(&new_body, last_ternary.original_id, &transformed_inner); - - let new_body = - replace_node_with_name_expression(&new_body, last_ternary.original_id, "inner"); - - assert_eq!( - to_code(&new_body), - r#"let outer: u256 -if true: - outer = 1 -else: - let inner: u256 - if true: - inner = 1 - else: - inner = 2 - - outer = inner - -foo(outer)"# - ) - } -} diff --git a/crates/lowering/src/context.rs b/crates/lowering/src/context.rs deleted file mode 100644 index 960193654f..0000000000 --- a/crates/lowering/src/context.rs +++ /dev/null @@ -1,81 +0,0 @@ -use fe_analyzer::context::{ExpressionAttributes, FunctionBody}; -use fe_analyzer::namespace::items::{FunctionId, ModuleId}; -use fe_analyzer::namespace::types::{Array, Tuple, Type}; -use fe_analyzer::AnalyzerDb; -use fe_parser::node::NodeId; -use indexmap::IndexSet; -use std::rc::Rc; - -pub struct ModuleContext<'db> { - pub db: &'db dyn AnalyzerDb, - pub module: ModuleId, - - /// List expressions that are used in the module - pub list_expressions: IndexSet, - - /// Tuples that are used in the module - pub tuples: IndexSet, -} - -impl<'db> ModuleContext<'db> { - pub fn new(db: &'db dyn AnalyzerDb, module: ModuleId) -> Self { - Self { - db, - module, - list_expressions: IndexSet::new(), - tuples: IndexSet::new(), - } - } -} - -pub struct FnContext<'a, 'db> { - pub module: &'a mut ModuleContext<'db>, - pub body: Rc, - pub id: FunctionId, - - /// Holds fresh id for [`FnContext::make_unique_name`] - fresh_id: u64, -} - -impl<'a, 'db> FnContext<'a, 'db> { - pub fn new(module: &'a mut ModuleContext<'db>, id: FunctionId, body: Rc) -> Self { - Self { - module, - body, - id, - fresh_id: 0, - } - } - - /// Makes a unique name from the given name, keeping it as readable as possible. - pub fn make_unique_name(&mut self, name: &str) -> String { - let id = self.fresh_id; - self.fresh_id += 1; - format!("${}_{}", name, id) - } - - pub fn db(&self) -> &'db dyn AnalyzerDb { - self.module.db - } - - pub fn expression_attributes>( - &self, - node_id: T, - ) -> Option<&ExpressionAttributes> { - self.body.expressions.get(&node_id.into()) - } - - pub fn var_decl_type>(&self, node_id: T) -> Option<&Type> { - self.body.var_decl_types.get(&node_id.into()) - } - - pub fn const_decl_type>(&self, node_id: T) -> Option<&Type> { - self.body.var_decl_types.get(&node_id.into()) - } -} - -impl<'a, 'db> AsMut> for FnContext<'a, 'db> { - fn as_mut(&mut self) -> &mut ModuleContext<'db> { - self.module - } -} diff --git a/crates/lowering/src/db.rs b/crates/lowering/src/db.rs deleted file mode 100644 index e7975a782a..0000000000 --- a/crates/lowering/src/db.rs +++ /dev/null @@ -1,38 +0,0 @@ -use fe_analyzer::namespace::items::ModuleId; -use fe_analyzer::AnalyzerDb; -use fe_common::db::{SourceDb, SourceDbStorage, Upcast, UpcastMut}; -use fe_parser::ast; -use std::rc::Rc; - -mod queries; - -#[salsa::query_group(LoweringDbStorage)] -pub trait LoweringDb: AnalyzerDb + Upcast { - #[salsa::invoke(queries::lowered_module_ast)] - fn lowered_module_ast(&self, module: ModuleId) -> Rc; -} - -#[salsa::database(SourceDbStorage, fe_analyzer::db::AnalyzerDbStorage, LoweringDbStorage)] -#[derive(Default)] -pub struct TestDb { - storage: salsa::Storage, -} -impl salsa::Database for TestDb {} - -impl Upcast for TestDb { - fn upcast(&self) -> &(dyn AnalyzerDb + 'static) { - &*self - } -} - -impl UpcastMut for TestDb { - fn upcast_mut(&mut self) -> &mut (dyn SourceDb + 'static) { - &mut *self - } -} - -impl Upcast for TestDb { - fn upcast(&self) -> &(dyn SourceDb + 'static) { - &*self - } -} diff --git a/crates/lowering/src/db/queries.rs b/crates/lowering/src/db/queries.rs deleted file mode 100644 index 4baa359074..0000000000 --- a/crates/lowering/src/db/queries.rs +++ /dev/null @@ -1,9 +0,0 @@ -use crate::db::LoweringDb; -use crate::mappers; -use fe_analyzer::namespace::items::ModuleId; -use fe_parser::ast; -use std::rc::Rc; - -pub fn lowered_module_ast(db: &dyn LoweringDb, module: ModuleId) -> Rc { - mappers::module::module(db.upcast(), module).into() -} diff --git a/crates/lowering/src/lib.rs b/crates/lowering/src/lib.rs deleted file mode 100644 index 62c88f651b..0000000000 --- a/crates/lowering/src/lib.rs +++ /dev/null @@ -1,72 +0,0 @@ -//! Fe Lowering. - -use fe_analyzer::namespace::items::{Ingot, IngotId, Module, ModuleId, ModuleSource}; - -mod ast_utils; -mod context; -pub mod db; -mod mappers; -mod names; -mod utils; - -pub use crate::db::{LoweringDb, TestDb}; - -use std::rc::Rc; - -pub fn lower_main_module(db: &mut dyn LoweringDb, module: ModuleId) -> ModuleId { - let original_ingot = module.ingot(db.upcast()); - let lowered_ingot = lower_ingot(db, original_ingot); - - let lowered_mod = lowered_ingot - .root_module(db.upcast()) - .expect("lowered ingot has no main module"); - - lowered_mod -} - -/// Lower every module of an ingot. -/// -/// Creates a new `IngotId` and new `ModuleId`s for the ingot's modules. -pub fn lower_ingot(db: &mut dyn LoweringDb, ingot: IngotId) -> IngotId { - let data = ingot.data(db.upcast()); - let lowered_ingot = db.intern_ingot(Rc::new(Ingot { - name: data.name.clone(), - mode: data.mode, - original: Some(ingot), - src_dir: data.src_dir.clone(), - })); - - let lowered_mods = ingot - .all_modules(db.upcast()) - .iter() - .map(|module| lower_module(db, *module, lowered_ingot)) - .collect(); - - let lowered_deps = ingot - .external_ingots(db.upcast()) - .iter() - .map(|(name, ing)| (name.clone(), lower_ingot(db, *ing))) - .collect(); - - db.set_ingot_modules(lowered_ingot, lowered_mods); - db.set_ingot_external_ingots(lowered_ingot, Rc::new(lowered_deps)); - lowered_ingot -} - -fn lower_module(db: &dyn LoweringDb, module: ModuleId, lowered_ingot: IngotId) -> ModuleId { - let data = module.data(db.upcast()); - - assert!( - !matches!(data.source, ModuleSource::Lowered { .. }), - "lowering an already lowered module" - ); - - db.intern_module(Rc::new(Module { - name: data.name.clone(), - ingot: lowered_ingot, - source: ModuleSource::Lowered { - original: module, - ast: db.lowered_module_ast(module), - }, - })) -} diff --git a/crates/lowering/src/mappers/contracts.rs b/crates/lowering/src/mappers/contracts.rs deleted file mode 100644 index 402f62a8e9..0000000000 --- a/crates/lowering/src/mappers/contracts.rs +++ /dev/null @@ -1,65 +0,0 @@ -use crate::context::ModuleContext; -use crate::mappers::{events, functions, types}; -use fe_analyzer::namespace::items::{ContractFieldId, ContractId}; -use fe_parser::ast; -use fe_parser::node::Node; - -/// Lowers a contract definition. -pub fn contract_def(context: &mut ModuleContext, contract: ContractId) -> Node { - let db = context.db; - let fields = contract - .fields(db) - .values() - .map(|field| contract_field(context, *field)) - .collect(); - - let events = contract - .events(db) - .values() - .map(|event| ast::ContractStmt::Event(events::event_def(context, *event))) - .collect::>(); - - let mut functions = contract - .functions(db) - .values() - .map(|function| ast::ContractStmt::Function(functions::func_def(context, *function))) - .collect::>(); - - if let Some(init_fn) = contract.init_function(db) { - functions.push(ast::ContractStmt::Function(functions::func_def( - context, init_fn, - ))); - } - - if let Some(call_fn) = contract.call_function(db) { - functions.push(ast::ContractStmt::Function(functions::func_def( - context, call_fn, - ))); - } - - let node = &contract.data(context.db).ast; - Node::new( - ast::Contract { - name: node.kind.name.clone(), - fields, - body: [events, functions].concat(), - pub_qual: node.kind.pub_qual, - }, - node.span, - ) -} - -fn contract_field(context: &mut ModuleContext, field: ContractFieldId) -> Node { - let node = &field.data(context.db).ast; - let typ = field.typ(context.db).expect("contract field type error"); - Node::new( - ast::Field { - is_pub: node.kind.is_pub, - is_const: node.kind.is_const, - name: node.kind.name.clone(), - typ: types::type_desc(context, node.kind.typ.clone(), &typ), - value: node.kind.value.clone(), - }, - node.span, - ) -} diff --git a/crates/lowering/src/mappers/events.rs b/crates/lowering/src/mappers/events.rs deleted file mode 100644 index e44066296a..0000000000 --- a/crates/lowering/src/mappers/events.rs +++ /dev/null @@ -1,38 +0,0 @@ -use crate::context::ModuleContext; -use crate::mappers::types; -use crate::utils::ZeroSpanNode; -use fe_analyzer::namespace::items::EventId; -use fe_parser::ast; -use fe_parser::node::Node; - -pub fn event_def(context: &mut ModuleContext, event: EventId) -> Node { - let ast_fields = &event.data(context.db).ast.kind.fields; - let fields = event - .typ(context.db) - .fields - .iter() - .zip(ast_fields.iter()) - .map(|(field, node)| { - ast::EventField { - is_idx: field.is_indexed, - name: field.name.clone().into_node(), - typ: types::type_desc( - context, - node.kind.typ.clone(), - &field.typ.as_ref().expect("event field type error").clone(), - ), - } - .into_node() - }) - .collect(); - - let node = &event.data(context.db).ast; - Node::new( - ast::Event { - name: node.kind.name.clone(), - fields, - pub_qual: node.kind.pub_qual, - }, - node.span, - ) -} diff --git a/crates/lowering/src/mappers/expressions.rs b/crates/lowering/src/mappers/expressions.rs deleted file mode 100644 index 082f8d539e..0000000000 --- a/crates/lowering/src/mappers/expressions.rs +++ /dev/null @@ -1,222 +0,0 @@ -use crate::context::FnContext; -use crate::names::{list_expr_generator_fn_name, tuple_struct_name}; -use crate::utils::ZeroSpanNode; -use fe_analyzer::context::Constant; -use fe_analyzer::namespace::items::Item; -use fe_analyzer::namespace::types::{Type, TypeDowncast}; -use fe_parser::ast::{self as fe}; -use fe_parser::node::Node; - -/// Lowers an expression and all sub expressions. -pub fn expr(context: &mut FnContext, exp: Node) -> Node { - let original_exp = exp.clone(); - let span = exp.span; - - // Fold constants if expression is const local definition. - // - // NOTE: This folding is NOT for optimization, - // but to avoid introducing new variables in later ternary lowering, etc. - if let Some(const_value) = context - .expression_attributes(exp.id) - .and_then(|attr| attr.const_value.as_ref()) - { - let literal = match const_value { - Constant::Int(val) => fe::Expr::Num(val.to_string().into()), - Constant::Bool(val) => fe::Expr::Bool(*val), - Constant::Str(val) => fe::Expr::Str(val.clone()), - }; - - return Node::new(literal, exp.span); - } - - let lowered_kind = match exp.kind { - fe::Expr::Name(_) => expr_name(context, exp), - fe::Expr::Path(_) - | fe::Expr::Num(_) - | fe::Expr::Bool(_) - | fe::Expr::Str(_) - | fe::Expr::Unit => exp.kind, - fe::Expr::Subscript { value, index } => fe::Expr::Subscript { - value: boxed_expr(context, value), - index: boxed_expr(context, index), - }, - fe::Expr::Attribute { value, attr } => fe::Expr::Attribute { - value: boxed_expr(context, value), - attr, - }, - fe::Expr::Ternary { - if_expr, - test, - else_expr, - } => fe::Expr::Ternary { - if_expr: boxed_expr(context, if_expr), - test: boxed_expr(context, test), - else_expr: boxed_expr(context, else_expr), - }, - fe::Expr::BoolOperation { left, op, right } => fe::Expr::BoolOperation { - left: boxed_expr(context, left), - op, - right: boxed_expr(context, right), - }, - fe::Expr::BinOperation { left, op, right } => fe::Expr::BinOperation { - left: boxed_expr(context, left), - op, - right: boxed_expr(context, right), - }, - fe::Expr::UnaryOperation { op, operand } => fe::Expr::UnaryOperation { - op, - operand: boxed_expr(context, operand), - }, - fe::Expr::CompOperation { left, op, right } => fe::Expr::CompOperation { - left: boxed_expr(context, left), - op, - right: boxed_expr(context, right), - }, - fe::Expr::Call { - func, - generic_args, - args, - } => fe::Expr::Call { - func: boxed_expr(context, func), - generic_args, - args: call_args(context, args), - }, - fe::Expr::List { .. } => expr_list(context, exp), - fe::Expr::Tuple { .. } => expr_tuple(context, exp), - }; - - Node::with_original_id(lowered_kind, span, original_exp.original_id) -} - -/// Lowers and optional expression. -pub fn optional_expr( - context: &mut FnContext, - exp: Option>, -) -> Option> { - exp.map(|exp| expr(context, exp)) -} - -/// Lowers a boxed expression. -#[allow(clippy::boxed_local)] -pub fn boxed_expr(context: &mut FnContext, exp: Box>) -> Box> { - Box::new(expr(context, *exp)) -} - -/// Lowers call arguments -pub fn call_args( - context: &mut FnContext, - args: Node>>, -) -> Node>> { - let lowered_args = args - .kind - .into_iter() - .map(|arg| { - Node::new( - fe::CallArg { - label: arg.kind.label, - value: expr(context, arg.kind.value), - }, - arg.span, - ) - }) - .collect(); - - Node::new(lowered_args, args.span) -} - -fn expr_name(context: &mut FnContext, exp: Node) -> fe::Expr { - let name = match &exp.kind { - fe::Expr::Name(name) => name, - _ => unreachable!(), - }; - - let db = context.db(); - match context.id.module(db).resolve_name(db, name) { - Ok(Some(Item::Constant(val))) => { - assert!( - val.is_base_type(db), - "Should have been rejected at first analyzer pass" - ); - val.value(db) - } - _ => exp.kind, - } -} - -fn expr_tuple(context: &mut FnContext, exp: Node) -> fe::Expr { - let typ = context - .expression_attributes(&exp) - .expect("missing attributes") - .typ - .as_tuple() - .expect("expected tuple type") - .clone(); - - let struct_name = tuple_struct_name(&typ); - - let elts = match exp.kind { - fe::Expr::Tuple { elts } => elts, - _ => unreachable!(), - }; - - // map the tuple args to named args - let args = Node::new( - elts.into_iter() - .enumerate() - .map(|(index, elt)| { - let span = elt.span; - Node::new( - fe::CallArg { - label: Some(Node::new(format!("item{}", index).into(), span)), - value: expr(context, elt), - }, - span, - ) - }) - .collect(), - exp.span, - ); - context.module.tuples.insert(typ); - - // create type constructor call for the lowered tuple - fe::Expr::Call { - func: Box::new(Node::new(fe::Expr::Name(struct_name), exp.span)), - generic_args: None, - args, - } -} - -fn expr_list(context: &mut FnContext, exp: Node) -> fe::Expr { - let attributes = context - .expression_attributes(&exp) - .expect("missing attributes"); - - if let Type::Array(array) = &attributes.typ { - let array = array.clone(); - let fn_name = list_expr_generator_fn_name(&array); - context.module.list_expressions.insert(array); - - if let fe::Expr::List { elts } = exp.kind { - let args = elts - .into_iter() - .map(|list_val| { - fe::CallArg { - label: None, - value: expr(context, list_val), - } - .into_node() - }) - .collect::>() - .into_node(); - - // Turn List Expression into a function call - return fe::Expr::Call { - func: fe::Expr::Name(fn_name).into_boxed_node(), - generic_args: None, - args, - }; - } - } - - unreachable!() -} diff --git a/crates/lowering/src/mappers/functions.rs b/crates/lowering/src/mappers/functions.rs deleted file mode 100644 index 47dbe2ca8d..0000000000 --- a/crates/lowering/src/mappers/functions.rs +++ /dev/null @@ -1,401 +0,0 @@ -use crate::ast_utils::{ - boolean_expr_to_if, get_first_boolean_expressions, get_first_ternary_expressions, -}; -use crate::ast_utils::{ - inject_before_expression, replace_node_with_name_expression, ternary_to_if, -}; -use crate::context::{FnContext, ModuleContext}; -use crate::mappers::expressions; -use crate::mappers::types; -use crate::utils::ZeroSpanNode; -use fe_analyzer::namespace::items::FunctionId; -use fe_analyzer::namespace::types::TypeDowncast; -use fe_analyzer::namespace::types::{Base, Type}; -use fe_parser::ast::{self as fe, Expr, FuncStmt, RegularFunctionArg, SmolStr}; -use fe_parser::node::Node; - -/// Lowers a function definition. -pub fn func_def(context: &mut ModuleContext, function: FunctionId) -> Node { - let node = &function.data(context.db).ast; - let fe::Function { - pub_, - unsafe_, - name, - args, - generic_params, - return_type: return_type_node, - body, - } = &node.kind; - - let signature = function.signature(context.db); - - let return_type = signature - .return_type - .as_ref() - .expect("fn return type error"); - - let mut fn_ctx = FnContext::new(context, function, function.body(context.db)); - - let lowered_body = { - let mut lowered_body = multiple_stmts(&mut fn_ctx, body.clone()); - // append `return ()` to the body if there is no return - if return_type.is_unit() && !is_last_statement_return(&lowered_body) { - lowered_body.push( - fe::FuncStmt::Return { - value: Some(fe::Expr::Unit.into_node()), - } - .into_node(), - ); - } - lowered_body - }; - - let lowered_body = lower_iteratively( - &mut fn_ctx, - lowered_body, - "ternary_result", - &get_first_ternary_expressions, - &ternary_to_if, - ); - let lowered_body = lower_iteratively( - &mut fn_ctx, - lowered_body, - "boolean_expr_result", - &get_first_boolean_expressions, - &boolean_expr_to_if, - ); - - let param_types = { - let params = &signature.params; - let mut types = vec![]; - if matches!( - args.first(), - Some(Node { - kind: fe::FunctionArg::Self_, - .. - }) - ) { - // The type of self is excluded from the parameter type list, so we add - // a unit type placeholder to accommodate the following zip. - types.push(Type::Base(Base::Unit)); - } - for param in params { - types.push(param.typ.clone().expect("fn param type error")) - } - types - }; - - let args = args - .iter() - .zip(param_types) - .map(|(pnode, ptype)| { - if let fe::FunctionArg::Regular(regular) = &pnode.kind { - fe::FunctionArg::Regular(RegularFunctionArg { - label: regular.label.clone(), - name: regular.name.clone(), - typ: types::type_desc(fn_ctx.module, regular.typ.clone(), &ptype), - }) - .into_node() - } else { - // the self arg remains the same - pnode.clone() - } - }) - .collect(); - - // The return type is lowered if it exists. If there is no return type, we set it to the unit type. - let lowered_return_type = return_type_node - .clone() - .map(|type_desc| types::type_desc(fn_ctx.module, type_desc, &return_type.clone())) - .unwrap_or_else(|| fe::TypeDesc::Unit.into_node()); - - let lowered_function = fe::Function { - pub_: *pub_, - unsafe_: *unsafe_, - name: name.clone(), - args, - generic_params: generic_params.clone(), - return_type: Some(lowered_return_type), - body: lowered_body, - }; - - Node::new(lowered_function, node.span) -} - -fn lower_iteratively( - context: &mut FnContext, - statements: Vec>, - result_name: &str, - getter_fn: &dyn Fn(&[Node]) -> Vec>, - mapper_fn: &dyn Fn(Type, &Node, &str) -> Vec>, -) -> Vec> { - let mut current_statements = statements; - - loop { - let expressions = getter_fn(¤t_statements); - - if let Some(current_expression) = expressions.last() { - let expr_attr = context - .expression_attributes(current_expression.original_id) - .expect("missing attributes"); - - let expression_type = expr_attr.typ.clone(); - let unique_name = context.make_unique_name(result_name); - let generated_statements = - mapper_fn(expression_type.clone(), current_expression, &unique_name); - current_statements = inject_before_expression( - ¤t_statements, - current_expression.original_id, - &generated_statements, - ); - current_statements = replace_node_with_name_expression( - ¤t_statements, - current_expression.original_id, - &unique_name, - ); - } else { - break; - } - } - - current_statements -} - -fn func_stmt(context: &mut FnContext, stmt: Node) -> Vec> { - let lowered_kinds = match stmt.kind { - fe::FuncStmt::Return { value } => stmt_return(context, value), - fe::FuncStmt::VarDecl { target, typ, value } => { - let var_type = context - .var_decl_type(typ.id) - .expect("missing var decl type") - .clone(); - - match target.kind { - fe::VarDeclTarget::Name(_) => vec![fe::FuncStmt::VarDecl { - target, - typ: types::type_desc(context.module, typ, &var_type), - value: expressions::optional_expr(context, value), - }], - fe::VarDeclTarget::Tuple(_) => { - lower_tuple_destructuring(context, target, typ, &var_type, value, stmt.span) - } - } - } - fe::FuncStmt::ConstantDecl { name, typ, value } => { - let var_type = context - .const_decl_type(typ.id) - .expect("missing var decl type") - .clone(); - - vec![fe::FuncStmt::ConstantDecl { - name, - typ: types::type_desc(context.module, typ, &var_type), - value: expressions::expr(context, value), - }] - } - fe::FuncStmt::Assign { target, value } => vec![fe::FuncStmt::Assign { - target: expressions::expr(context, target), - value: expressions::expr(context, value), - }], - fe::FuncStmt::Emit { name, args } => vec![fe::FuncStmt::Emit { - name, - args: expressions::call_args(context, args), - }], - fe::FuncStmt::AugAssign { target, op, value } => { - stmt_aug_assign(context, target, op, value) - } - fe::FuncStmt::For { target, iter, body } => vec![fe::FuncStmt::For { - target, - iter: expressions::expr(context, iter), - body: multiple_stmts(context, body), - }], - fe::FuncStmt::While { test, body } => vec![fe::FuncStmt::While { - test: expressions::expr(context, test), - body: multiple_stmts(context, body), - }], - fe::FuncStmt::If { - test, - body, - or_else, - } => vec![fe::FuncStmt::If { - test: expressions::expr(context, test), - body: multiple_stmts(context, body), - or_else: multiple_stmts(context, or_else), - }], - fe::FuncStmt::Unsafe(body) => vec![fe::FuncStmt::Unsafe(multiple_stmts(context, body))], - fe::FuncStmt::Assert { test, msg } => vec![fe::FuncStmt::Assert { - test: expressions::expr(context, test), - msg: expressions::optional_expr(context, msg), - }], - fe::FuncStmt::Expr { value } => vec![fe::FuncStmt::Expr { - value: expressions::expr(context, value), - }], - fe::FuncStmt::Pass | fe::FuncStmt::Break | fe::FuncStmt::Continue => vec![stmt.kind], - fe::FuncStmt::Revert { error } => vec![fe::FuncStmt::Revert { - error: error.map(|expr| expressions::expr(context, expr)), - }], - }; - let span = stmt.span; - - lowered_kinds - .into_iter() - .map(|kind| Node::new(kind, span)) - .collect() -} - -fn multiple_stmts( - context: &mut FnContext, - stmts: Vec>, -) -> Vec> { - stmts - .into_iter() - .map(|stmt| func_stmt(context, stmt)) - .collect::>>>() - .concat() -} - -fn stmt_aug_assign( - context: &mut FnContext, - target: Node, - op: Node, - value: Node, -) -> Vec { - let original_value_span = value.span; - let lowered_target = expressions::expr(context, target.clone()); - let lowered_lh_value = expressions::expr(context, target); - let lowered_rh_value = expressions::expr(context, value); - - let new_value_kind = fe::Expr::BinOperation { - left: Box::new(lowered_lh_value), - op, - right: Box::new(lowered_rh_value), - }; - - let new_value = Node::new(new_value_kind, original_value_span); - - // the new statement is: `target = target value`. - vec![fe::FuncStmt::Assign { - target: lowered_target, - value: new_value, - }] -} - -fn stmt_return(context: &mut FnContext, value: Option>) -> Vec { - if let Some(value) = value { - // lower a return statement that contains a value (e.g. `return true` or `return ()`) - vec![fe::FuncStmt::Return { - value: Some(expressions::expr(context, value)), - }] - } else { - // lower a return statement with no value to `return empty_tuple()` - vec![fe::FuncStmt::Return { - value: Some(fe::Expr::Unit.into_node()), - }] - } -} - -fn is_last_statement_return(stmts: &[Node]) -> bool { - if let Some(stmt) = stmts.last() { - matches!(stmt.kind, fe::FuncStmt::Return { .. }) - } else { - false - } -} - -/// Lowers tuple desctructuring. -/// -/// e.g. -/// ```fe -/// (x, y): (uint256, bool) = (1, true) -/// ``` -/// will be lowered to -/// ```fe -/// $tmp_tuple_0: (uint256, bool) = (1, true) -/// x: uint256 = $tmp_tuple_0.item0 -/// y: bool = $tmp_tuple_0.item1 -/// ``` -fn lower_tuple_destructuring( - context: &mut FnContext, - target: Node, - type_desc: Node, - typ: &Type, - value: Option>, - span: fe_common::Span, -) -> Vec { - let mut stmts = vec![]; - let tmp_tuple: SmolStr = context.make_unique_name("tmp_tuple").into(); - stmts.push(fe::FuncStmt::VarDecl { - target: Node::new(fe::VarDeclTarget::Name(tmp_tuple.clone()), span), - typ: types::type_desc(context.module, type_desc.clone(), typ), - value: expressions::optional_expr(context, value), - }); - - declare_tuple_items( - context, - target, - type_desc, - typ, - &tmp_tuple, - &mut vec![], - &mut stmts, - ); - stmts -} - -fn declare_tuple_items( - context: &mut FnContext, - target: Node, - type_desc: Node, - typ: &Type, - tmp_tuple: &str, - indices: &mut Vec, - stmts: &mut Vec, -) { - match target.kind { - fe::VarDeclTarget::Name(_) => { - let mut value = fe::Expr::Name(tmp_tuple.into()).into_node(); - for index in indices.iter() { - value = fe::Expr::Attribute { - value: value.into(), - attr: SmolStr::new(format!("item{}", index)).into_node(), - } - .into_node(); - } - - stmts.push(fe::FuncStmt::VarDecl { - target, - typ: types::type_desc(context.module, type_desc, typ), - value: expressions::optional_expr(context, Some(value)), - }); - } - - fe::VarDeclTarget::Tuple(targets) => { - let item_type_descs = match type_desc.kind { - fe::TypeDesc::Tuple { items } => items, - _ => unreachable!(), - }; - let item_types = &typ - .as_tuple() - .expect("tuple declaration type mismatch") - .items; - for (index, ((target, type_desc), typ)) in targets - .into_iter() - .zip(item_type_descs.into_iter()) - .zip(item_types.into_iter()) - .enumerate() - { - indices.push(index); - declare_tuple_items( - context, - target, - type_desc, - &typ.clone(), - tmp_tuple, - indices, - stmts, - ); - indices.pop().unwrap(); - } - } - } -} diff --git a/crates/lowering/src/mappers/mod.rs b/crates/lowering/src/mappers/mod.rs deleted file mode 100644 index 877b40c5b6..0000000000 --- a/crates/lowering/src/mappers/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -mod contracts; -mod events; -mod expressions; -mod functions; -pub mod module; -mod structs; -mod types; diff --git a/crates/lowering/src/mappers/module.rs b/crates/lowering/src/mappers/module.rs deleted file mode 100644 index 63f8647b87..0000000000 --- a/crates/lowering/src/mappers/module.rs +++ /dev/null @@ -1,175 +0,0 @@ -use crate::context::ModuleContext; -use crate::mappers::{contracts, events, functions, structs, types}; -use crate::names::{self, build_type_desc}; -use crate::utils::ZeroSpanNode; -use fe_analyzer::namespace::items::{Item, ModuleId, TypeDef}; -use fe_analyzer::namespace::types::{Array, Tuple, Type}; -use fe_analyzer::AnalyzerDb; -use fe_parser::ast::{self, SmolStr}; -use fe_parser::node::Node; - -/// Lowers a module. -pub fn module(db: &dyn AnalyzerDb, module: ModuleId) -> ast::Module { - let mut context = ModuleContext::new(db, module); - - let mut lowered_body = module - .ast(db) - .body - .iter() - .filter_map(|stmt| match stmt { - ast::ModuleStmt::Pragma(_) | ast::ModuleStmt::Use(_) => Some(stmt.clone()), - _ => None, - }) - .collect::>(); - - lowered_body.extend(module.all_items(db).iter().filter_map(|item| match item { - Item::Type(typedef) => match typedef { - TypeDef::Alias(id) => { - let node = &id.data(db).ast; - let name = node.kind.name.clone(); - Some(ast::ModuleStmt::TypeAlias(Node::new( - ast::TypeAlias { - name, - typ: types::type_desc( - &mut context, - node.kind.typ.clone(), - &id.typ(db).expect("type alias error"), - ), - pub_qual: node.kind.pub_qual, - }, - id.span(db), - ))) - } - TypeDef::Struct(id) => Some(ast::ModuleStmt::Struct(structs::struct_def( - &mut context, - *id, - ))), - TypeDef::Contract(id) => Some(ast::ModuleStmt::Contract(contracts::contract_def( - &mut context, - *id, - ))), - TypeDef::Primitive(_) => unreachable!(), - }, - Item::Function(id) => Some(ast::ModuleStmt::Function(functions::func_def( - &mut context, - *id, - ))), - - Item::GenericType(_) => todo!("generic types can't be defined in fe yet"), - Item::Event(id) => Some(ast::ModuleStmt::Event(events::event_def(&mut context, *id))), - Item::BuiltinFunction(_) | Item::Intrinsic(_) => { - unreachable!("special built-in stuff") - } - - Item::Constant(constant) => Some(ast::ModuleStmt::Constant(constant.data(db).ast.clone())), - - Item::Ingot(_) => unreachable!("ingots cannot be defined in a module"), - Item::Module(_) => unreachable!("modules cannot be defined in modules (at least not yet)"), - })); - - let struct_defs_from_tuples = context - .tuples - .iter() - .map(|typ| ast::ModuleStmt::Struct(build_tuple_struct(typ).into_node())) - .collect::>(); - - let func_defs_from_list_expr = context - .list_expressions - .iter() - .map(|expr| ast::ModuleStmt::Function(list_expr_to_fn_def(expr).into_node())) - .collect::>(); - - ast::Module { - body: [ - struct_defs_from_tuples, - func_defs_from_list_expr, - lowered_body, - ] - .concat(), - } -} - -fn build_tuple_struct(tuple: &Tuple) -> ast::Struct { - let fields = tuple - .items - .iter() - .enumerate() - .map(|(index, typ)| { - build_struct_field(format!("item{}", index), build_type_desc(typ)).into_node() - }) - .collect(); - - ast::Struct { - name: names::tuple_struct_name(tuple).into_node(), - fields, - functions: vec![], - pub_qual: None, - } -} - -fn build_struct_field(name: String, type_desc: ast::TypeDesc) -> ast::Field { - ast::Field { - is_pub: true, - is_const: false, - name: SmolStr::new(name).into_node(), - typ: type_desc.into_node(), - value: None, - } -} - -fn list_expr_to_fn_def(array: &Array) -> ast::Function { - // Built the AST nodes for the function arguments - let args = (0..array.size) - .map(|index| { - ast::FunctionArg::Regular(ast::RegularFunctionArg { - label: Some(SmolStr::new("_").into_node()), - name: SmolStr::new(format!("val{}", index)).into_node(), - typ: build_type_desc(&Type::Base(array.inner)).into_node(), - }) - .into_node() - }) - .collect::>(); - - // Build the AST node for the array declaration - let var_decl_name = "generated_array"; - let var_decl = ast::FuncStmt::VarDecl { - target: ast::VarDeclTarget::Name(var_decl_name.into()).into_node(), - typ: build_type_desc(&Type::Array(array.clone())).into_node(), - value: None, - } - .into_node(); - - // Build the AST nodes for the individual assignments of array slots - let assignments = (0..array.size) - .map(|index| { - ast::FuncStmt::Assign { - target: ast::Expr::Subscript { - value: ast::Expr::Name(var_decl_name.into()).into_boxed_node(), - index: ast::Expr::Num(index.to_string().into()).into_boxed_node(), - } - .into_node(), - value: ast::Expr::Name(format!("val{}", index).into()).into_node(), - } - .into_node() - }) - .collect::>(); - - // Build the AST node for the return statement - let return_stmt = ast::FuncStmt::Return { - value: Some(ast::Expr::Name(var_decl_name.into()).into_node()), - } - .into_node(); - - let return_type = Some(build_type_desc(&Type::Array(array.clone())).into_node()); - - // Put it all together in one AST node that holds the entire function definition - ast::Function { - pub_: None, - unsafe_: None, - name: names::list_expr_generator_fn_name(array).into_node(), - args, - generic_params: Vec::new().into_node(), - return_type, - body: [vec![var_decl], assignments, vec![return_stmt]].concat(), - } -} diff --git a/crates/lowering/src/mappers/structs.rs b/crates/lowering/src/mappers/structs.rs deleted file mode 100644 index 5b49dd92a5..0000000000 --- a/crates/lowering/src/mappers/structs.rs +++ /dev/null @@ -1,47 +0,0 @@ -use crate::context::ModuleContext; -use crate::mappers::{functions, types}; -use fe_analyzer::namespace::items::{StructFieldId, StructId}; -use fe_parser::ast; -use fe_parser::node::Node; - -pub fn struct_def(context: &mut ModuleContext, struct_: StructId) -> Node { - let db = context.db; - - let fields = struct_ - .fields(db) - .values() - .map(|field| struct_field(context, *field)) - .collect(); - - let functions = struct_ - .functions(db) - .values() - .map(|function| functions::func_def(context, *function)) - .collect(); - - let node = &struct_.data(context.db).ast; - Node::new( - ast::Struct { - name: node.kind.name.clone(), - fields, - functions, - pub_qual: node.kind.pub_qual, - }, - node.span, - ) -} - -fn struct_field(context: &mut ModuleContext, field: StructFieldId) -> Node { - let node = &field.data(context.db).ast; - let typ = field.typ(context.db).expect("struct field type error"); - Node::new( - ast::Field { - is_pub: node.kind.is_pub, - is_const: node.kind.is_const, - name: node.kind.name.clone(), - typ: types::type_desc(context, node.kind.typ.clone(), &typ), - value: node.kind.value.clone(), - }, - node.span, - ) -} diff --git a/crates/lowering/src/mappers/types.rs b/crates/lowering/src/mappers/types.rs deleted file mode 100644 index 836b3dfdfc..0000000000 --- a/crates/lowering/src/mappers/types.rs +++ /dev/null @@ -1,49 +0,0 @@ -use crate::context::ModuleContext; -use crate::names; -use fe_analyzer::namespace::types::{Type, TypeDowncast}; -use fe_parser::ast::{GenericArg, TypeDesc}; -use fe_parser::node::Node; - -pub fn type_desc(context: &mut ModuleContext, desc: Node, typ: &Type) -> Node { - match desc.kind { - TypeDesc::Unit | TypeDesc::Base { .. } | TypeDesc::Path(_) => desc, - - TypeDesc::Tuple { items } => { - let typ = typ.as_tuple().expect("expected tuple type"); - - for (item_desc, item_type) in items.into_iter().zip(typ.items.iter()) { - type_desc(context, item_desc, &item_type.clone()); - } - context.tuples.insert(typ.clone()); - Node::new( - TypeDesc::Base { - base: names::tuple_struct_name(typ), - }, - desc.span, - ) - } - - TypeDesc::Generic { base, args } => Node::new( - TypeDesc::Generic { - base, - args: Node::new( - args.kind - .into_iter() - .enumerate() - .map(|(idx, arg)| match arg { - GenericArg::TypeDesc(node) => GenericArg::TypeDesc(type_desc( - context, - node, - &typ.generic_arg_type(idx) - .expect("expected generic type arg"), - )), - GenericArg::Int(_) | GenericArg::ConstExpr(_) => arg, - }) - .collect(), - args.span, - ), - }, - desc.span, - ), - } -} diff --git a/crates/lowering/src/names.rs b/crates/lowering/src/names.rs deleted file mode 100644 index b6d38bd93f..0000000000 --- a/crates/lowering/src/names.rs +++ /dev/null @@ -1,53 +0,0 @@ -use crate::names; -use crate::utils::ZeroSpanNode; -use fe_analyzer::namespace::types::{Array, Base, SafeNames, Tuple, Type}; -use fe_parser::ast::{self, SmolStr}; - -/// The name of a lowered list expression generator function. -pub fn list_expr_generator_fn_name(list_expr_type: &Array) -> SmolStr { - format!("list_expr_{}", list_expr_type.lower_snake()).into() -} - -/// The name of a lowered tuple struct definition. -pub fn tuple_struct_name(tuple: &Tuple) -> SmolStr { - format!("${}", tuple.lower_snake()).into() -} - -/// Maps a Type type to its type description. -pub fn build_type_desc(typ: &Type) -> ast::TypeDesc { - match typ { - Type::Base(Base::Unit) => ast::TypeDesc::Unit, - Type::Base(base) => ast::TypeDesc::Base { base: base.name() }, - Type::Array(array) => ast::TypeDesc::Generic { - base: SmolStr::new("Array").into_node(), - args: vec![ - ast::GenericArg::TypeDesc(build_type_desc(&Type::Base(array.inner)).into_node()), - ast::GenericArg::Int(array.size.into_node()), - ] - .into_node(), - }, - Type::Tuple(tuple) => ast::TypeDesc::Base { - base: names::tuple_struct_name(tuple), - }, - Type::String(string) => ast::TypeDesc::Generic { - base: SmolStr::new("String").into_node(), - args: vec![ast::GenericArg::Int(string.max_size.into_node())].into_node(), - }, - Type::Contract(contract) => ast::TypeDesc::Base { - base: contract.name.clone(), - }, - Type::Struct(strukt) => ast::TypeDesc::Base { - base: strukt.name.clone(), - }, - Type::Map(map) => ast::TypeDesc::Generic { - base: SmolStr::new("Map").into_node(), - args: vec![ast::GenericArg::TypeDesc( - build_type_desc(&map.value).into_node(), - )] - .into_node(), - }, - Type::SelfContract(contract) => ast::TypeDesc::Base { - base: contract.name.clone(), - }, - } -} diff --git a/crates/lowering/src/utils.rs b/crates/lowering/src/utils.rs deleted file mode 100644 index 70945ff688..0000000000 --- a/crates/lowering/src/utils.rs +++ /dev/null @@ -1,22 +0,0 @@ -use fe_common::files::SourceFileId; -use fe_common::Span; -use fe_parser::node::{Node, NodeId}; - -/// A trait to turn any sized type into a `Node` with a span of zero. -pub trait ZeroSpanNode: Sized { - /// Wrap the value in a `Node` with a span of zero. - fn into_node(self) -> Node { - Node::new(self, Span::zero(SourceFileId::dummy_file())) - } - - fn into_traceable_node(self, original_id: NodeId) -> Node { - Node::with_original_id(self, Span::zero(SourceFileId::dummy_file()), original_id) - } - - /// Wrap the value in a boxed `Node` with a span of zero - fn into_boxed_node(self) -> Box> { - Box::new(self.into_node()) - } -} - -impl ZeroSpanNode for T {} diff --git a/crates/lowering/tests/lowering.rs b/crates/lowering/tests/lowering.rs deleted file mode 100644 index 1305955de4..0000000000 --- a/crates/lowering/tests/lowering.rs +++ /dev/null @@ -1,59 +0,0 @@ -use fe_analyzer::namespace::items::ModuleId; -use fe_common::diagnostics::print_diagnostics; -use fe_lowering::TestDb; -use insta::assert_snapshot; -use wasm_bindgen_test::wasm_bindgen_test; - -macro_rules! test_file { - ($name:ident, $path:expr) => { - #[test] - #[wasm_bindgen_test] - fn $name() { - let mut db = TestDb::default(); - let module = - ModuleId::new_standalone(&mut db, $path.into(), test_files::fixture($path).into()); - - if !module.diagnostics(&db).is_empty() { - print_diagnostics(&db, &module.diagnostics(&db)); - panic!("failed to analyze module") - } - - let lowered_module = fe_lowering::lower_main_module(&mut db, module); - - if !lowered_module.diagnostics(&db).is_empty() { - print_diagnostics(&db, &lowered_module.diagnostics(&db)); - panic!("failed to analyze lowered module") - } - - let lowered = format!("{}", lowered_module.ast(&db)); - if cfg!(target_arch = "wasm32") { - fe_common::assert_snapshot_wasm!( - concat!("snapshots/lowering__", stringify!($name), ".snap"), - lowered - ); - } else { - assert_snapshot!(lowered); - } - } - }; -} - -test_file! { aug_assign, "lowering/aug_assign.fe" } -test_file! { base_tuple, "lowering/base_tuple.fe" } -test_file! { list_expressions, "lowering/list_expressions.fe" } -test_file! { return_unit, "lowering/return_unit.fe" } -test_file! { unit_implicit, "lowering/unit_implicit.fe" } -test_file! { init, "lowering/init.fe" } -test_file! { custom_empty_type, "lowering/custom_empty_type.fe" } -test_file! { nested_tuple, "lowering/nested_tuple.fe" } -test_file! { map_tuple, "lowering/map_tuple.fe" } -test_file! { type_alias_tuple, "lowering/type_alias_tuple.fe" } -test_file! { tuple_destruct, "lowering/tuple_destruct.fe" } -test_file! { module_const, "lowering/module_const.fe" } -test_file! { module_fn, "lowering/module_fn.fe" } -test_file! { struct_fn, "lowering/struct_fn.fe" } -test_file! { ternary, "lowering/ternary.fe" } -test_file! { and_or, "lowering/and_or.fe" } -test_file! { module_level_events, "lowering/module_level_events.fe" } -// TODO: the analyzer rejects lowered nested tuples. -// test_file!(array_tuple, "lowering/array_tuple.fe"); diff --git a/crates/lowering/tests/snapshots/lowering__and_or.snap b/crates/lowering/tests/snapshots/lowering__and_or.snap deleted file mode 100644 index 2e4e78082b..0000000000 --- a/crates/lowering/tests/snapshots/lowering__and_or.snap +++ /dev/null @@ -1,182 +0,0 @@ ---- -source: crates/lowering/tests/lowering.rs -expression: lowered - ---- -struct $tuple_bool_bool_: - pub item0: bool - pub item1: bool - -contract Foo: - pub fn bar() -> bool: - let $boolean_expr_result_0: bool = true - if not false: - $boolean_expr_result_0 = true - - return baz($boolean_expr_result_0) - - pub fn nested() -> (): - if true: - let a: bool = true - let b: bool = false - let x: bool = true - let y: bool = false - let $boolean_expr_result_0: bool = true - if not x: - $boolean_expr_result_0 = y - - let $boolean_expr_result_1: bool = false - if a: - $boolean_expr_result_1 = b - - return double_baz($boolean_expr_result_1, $boolean_expr_result_0) - - return () - - pub fn nested_ternary() -> bool: - let a: bool = true - let b: bool = false - let x: bool = true - let y: bool = false - let $boolean_expr_result_0: bool = false - let $boolean_expr_result_1: bool = false - if a: - $boolean_expr_result_1 = b - - if baz($boolean_expr_result_1): - let $boolean_expr_result_2: bool = false - if x: - $boolean_expr_result_2 = y - - $boolean_expr_result_0 = $boolean_expr_result_2 - - return $boolean_expr_result_0 - - pub fn in_dec() -> (): - let a: bool = true - let b: bool = false - let x: bool = true - let y: bool = false - let $boolean_expr_result_0: bool = true - if not x: - $boolean_expr_result_0 = y - - let $boolean_expr_result_1: bool = false - if a: - $boolean_expr_result_1 = b - - let z: $tuple_bool_bool_ = $tuple_bool_bool_(item0: $boolean_expr_result_1, item1: $boolean_expr_result_0) - return () - - pub fn short_or(_ first: bool, _ second: bool, _ third: bool, _ fourth: bool) -> bool: - let $boolean_expr_result_0: bool = true - let $boolean_expr_result_1: bool = true - let $boolean_expr_result_2: bool = true - if not first: - $boolean_expr_result_2 = second - - if not $boolean_expr_result_2: - $boolean_expr_result_1 = third - - if not $boolean_expr_result_1: - $boolean_expr_result_0 = fourth - - return $boolean_expr_result_0 - - pub fn short_or2(_ first: bool, _ second: bool, _ third: bool, _ fourth: bool) -> bool: - let $boolean_expr_result_0: bool = true - let $boolean_expr_result_1: bool = true - if not first: - $boolean_expr_result_1 = second - - if not $boolean_expr_result_1: - let $boolean_expr_result_2: bool = true - if not third: - $boolean_expr_result_2 = fourth - - $boolean_expr_result_0 = $boolean_expr_result_2 - - return $boolean_expr_result_0 - - pub fn short_or3(_ first: bool, _ second: bool, _ third: bool, _ fourth: bool) -> bool: - let $boolean_expr_result_0: bool = true - let $boolean_expr_result_1: bool = true - let $boolean_expr_result_2: bool = true - if not first: - $boolean_expr_result_2 = second - - if not $boolean_expr_result_2: - $boolean_expr_result_1 = third - - if not $boolean_expr_result_1: - $boolean_expr_result_0 = fourth - - return $boolean_expr_result_0 - - pub fn short_and(_ first: bool, _ second: bool, _ third: bool, _ fourth: bool) -> bool: - let $boolean_expr_result_0: bool = false - let $boolean_expr_result_1: bool = false - let $boolean_expr_result_2: bool = false - if first: - $boolean_expr_result_2 = second - - if $boolean_expr_result_2: - $boolean_expr_result_1 = third - - if $boolean_expr_result_1: - $boolean_expr_result_0 = fourth - - return $boolean_expr_result_0 - - pub fn short_and2(_ first: bool, _ second: bool, _ third: bool, _ fourth: bool) -> bool: - let $boolean_expr_result_0: bool = false - let $boolean_expr_result_1: bool = false - if first: - $boolean_expr_result_1 = second - - if $boolean_expr_result_1: - let $boolean_expr_result_2: bool = false - if third: - $boolean_expr_result_2 = fourth - - $boolean_expr_result_0 = $boolean_expr_result_2 - - return $boolean_expr_result_0 - - pub fn short_and3(_ first: bool, _ second: bool, _ third: bool, _ fourth: bool) -> bool: - let $boolean_expr_result_0: bool = false - let $boolean_expr_result_1: bool = false - let $boolean_expr_result_2: bool = false - if first: - $boolean_expr_result_2 = second - - if $boolean_expr_result_2: - $boolean_expr_result_1 = third - - if $boolean_expr_result_1: - $boolean_expr_result_0 = fourth - - return $boolean_expr_result_0 - - pub fn short_mixed(_ first: bool, _ second: bool, _ third: bool, _ fourth: bool) -> bool: - let $boolean_expr_result_0: bool = true - let $boolean_expr_result_1: bool = false - let $boolean_expr_result_2: bool = true - if not first: - $boolean_expr_result_2 = second - - if $boolean_expr_result_2: - $boolean_expr_result_1 = third - - if not $boolean_expr_result_1: - $boolean_expr_result_0 = fourth - - return $boolean_expr_result_0 - - pub fn baz(_ val: bool) -> bool: - return val - - pub fn double_baz(_ val: bool, _ val2: bool) -> (): - pass - return () - diff --git a/crates/lowering/tests/snapshots/lowering__aug_assign.snap b/crates/lowering/tests/snapshots/lowering__aug_assign.snap deleted file mode 100644 index fe1bb08526..0000000000 --- a/crates/lowering/tests/snapshots/lowering__aug_assign.snap +++ /dev/null @@ -1,62 +0,0 @@ ---- -source: crates/lowering/tests/lowering.rs -expression: lowered_code - ---- -contract Foo: - my_num: u256 - - pub fn add(a: u256, b: u256) -> u256: - a = a + b - return a - - pub fn sub(a: u256, b: u256) -> u256: - a = a - b - return a - - pub fn mul(a: u256, b: u256) -> u256: - a = a * b - return a - - pub fn div(a: u256, b: u256) -> u256: - a = a / b - return a - - pub fn mod(a: u256, b: u256) -> u256: - a = a % b - return a - - pub fn pow(a: u256, b: u256) -> u256: - a = a ** b - return a - - pub fn lshift(a: u8, b: u8) -> u8: - a = a << b - return a - - pub fn rshift(a: u8, b: u8) -> u8: - a = a >> b - return a - - pub fn bit_or(a: u8, b: u8) -> u8: - a = a | b - return a - - pub fn bit_xor(a: u8, b: u8) -> u8: - a = a ^ b - return a - - pub fn bit_and(a: u8, b: u8) -> u8: - a = a & b - return a - - pub fn add_from_sto(self, a: u256, b: u256) -> u256: - self.my_num = a - self.my_num = self.my_num + b - return self.my_num - - pub fn add_from_mem(a: u256, b: u256) -> u256: - let my_array: Array - my_array[7] = a - my_array[7] = my_array[7] + b - return my_array[7] diff --git a/crates/lowering/tests/snapshots/lowering__base_tuple.snap b/crates/lowering/tests/snapshots/lowering__base_tuple.snap deleted file mode 100644 index 0ff6b72e4e..0000000000 --- a/crates/lowering/tests/snapshots/lowering__base_tuple.snap +++ /dev/null @@ -1,60 +0,0 @@ ---- -source: crates/lowering/tests/lowering.rs -expression: lowered - ---- -struct $tuple_bool_address_: - pub item0: bool - pub item1: address - -struct $tuple_bool_bool_: - pub item0: bool - pub item1: bool - -struct $tuple_address_address_: - pub item0: address - pub item1: address - -struct $tuple_u256_bool_: - pub item0: u256 - pub item1: bool - -struct $tuple_u256_bool_u8_address_: - pub item0: u256 - pub item1: bool - pub item2: u8 - pub item3: address - -struct $tuple_address_address_u16_i32_bool_: - pub item0: address - pub item1: address - pub item2: u16 - pub item3: i32 - pub item4: bool - -use std::context::Context - -contract Foo: - my_tuple_field: $tuple_bool_address_ - - event MyEvent: - my_tuple: $tuple_bool_bool_ - my_other_tuple: $tuple_address_address_ - - pub fn bar(my_num: u256, my_bool: bool) -> $tuple_u256_bool_: - return $tuple_u256_bool_(item0: my_num, item1: my_bool) - - pub fn baz() -> $tuple_u256_bool_u8_address_: - return $tuple_u256_bool_u8_address_(item0: 999999, item1: false, item2: u8(42), item3: address(26)) - - pub fn bing() -> (): - let foo: $tuple_address_address_u16_i32_bool_ = $tuple_address_address_u16_i32_bool_(item0: address(0), item1: address(0), item2: u16(0), item3: i32(0), item4: false) - return () - - pub fn bop(my_tuple1: $tuple_u256_bool_, my_tuple2: $tuple_u256_bool_u8_address_, my_tuple3: $tuple_address_address_u16_i32_bool_) -> (): - pass - return () - - pub fn food(ctx: Context) -> (): - emit MyEvent(ctx, my_tuple: $tuple_bool_bool_(item0: false, item1: true), my_other_tuple: $tuple_address_address_(item0: address(0), item1: address(1))) - return () diff --git a/crates/lowering/tests/snapshots/lowering__custom_empty_type.snap b/crates/lowering/tests/snapshots/lowering__custom_empty_type.snap deleted file mode 100644 index 46fe25fa6a..0000000000 --- a/crates/lowering/tests/snapshots/lowering__custom_empty_type.snap +++ /dev/null @@ -1,11 +0,0 @@ ---- -source: crates/lowering/tests/lowering.rs -expression: lowered_code - ---- -struct MyEmptyType: - pass - -contract Foo: - pub fn bar() -> MyEmptyType: - return MyEmptyType() diff --git a/crates/lowering/tests/snapshots/lowering__init.snap b/crates/lowering/tests/snapshots/lowering__init.snap deleted file mode 100644 index 2de41ae976..0000000000 --- a/crates/lowering/tests/snapshots/lowering__init.snap +++ /dev/null @@ -1,9 +0,0 @@ ---- -source: crates/lowering/tests/lowering.rs -expression: lowered_code - ---- -contract Foo: - pub fn __init__() -> (): - pass - return () diff --git a/crates/lowering/tests/snapshots/lowering__list_expressions.snap b/crates/lowering/tests/snapshots/lowering__list_expressions.snap deleted file mode 100644 index 833f9db6ee..0000000000 --- a/crates/lowering/tests/snapshots/lowering__list_expressions.snap +++ /dev/null @@ -1,22 +0,0 @@ ---- -source: crates/lowering/tests/lowering.rs -expression: lowered - ---- -fn list_expr_array_u256_3(_ val0: u256, _ val1: u256, _ val2: u256) -> Array: - let generated_array: Array - generated_array[0] = val0 - generated_array[1] = val1 - generated_array[2] = val2 - return generated_array - -fn list_expr_array_unit_0() -> Array<(), 0>: - let generated_array: Array<(), 0> - return generated_array - -contract Foo: - pub fn foo() -> (): - let x: Array = list_expr_array_u256_3(10, 20, 30) - list_expr_array_unit_0() - return () - diff --git a/crates/lowering/tests/snapshots/lowering__map_tuple.snap b/crates/lowering/tests/snapshots/lowering__map_tuple.snap deleted file mode 100644 index 478c2e67d3..0000000000 --- a/crates/lowering/tests/snapshots/lowering__map_tuple.snap +++ /dev/null @@ -1,19 +0,0 @@ ---- -source: crates/lowering/tests/lowering.rs -expression: lowered - ---- -struct $tuple_u256_u8_: - pub item0: u256 - pub item1: u8 - -struct $tuple_address_tuple_u256_u8__: - pub item0: address - pub item1: $tuple_u256_u8_ - -contract Foo: - tuples: Map - - pub fn bar(self, x: u256) -> u256: - self.tuples[0] = $tuple_address_tuple_u256_u8__(item0: address(100), item1: $tuple_u256_u8_(item0: x, item1: 5)) - return self.tuples[0].item1.item0 diff --git a/crates/lowering/tests/snapshots/lowering__module_const.snap b/crates/lowering/tests/snapshots/lowering__module_const.snap deleted file mode 100644 index 73c1d94bfa..0000000000 --- a/crates/lowering/tests/snapshots/lowering__module_const.snap +++ /dev/null @@ -1,62 +0,0 @@ ---- -source: crates/lowering/tests/lowering.rs -expression: lowered - ---- -struct $tuple_u256_u256_: - pub item0: u256 - pub item1: u256 - -fn list_expr_array_u256_2(_ val0: u256, _ val1: u256) -> Array: - let generated_array: Array - generated_array[0] = val0 - generated_array[1] = val1 - return generated_array - -const THREE: u256 = 3 - -const TEN: u256 = 10 - -const IS_ADMIN: bool = true - -const UNIT: () = () - -struct Bar: - pub val: u256 - -contract Foo: - table: Map - - pub fn revert_with_bar() -> (): - revert Bar(val: 10) - return () - - pub fn return_unit() -> (): - return () - - pub fn usage(self) -> u256: - let my_calc: u256 = 3 * 10 - let my_other_calc: u256 = 3 * 3 - let my_array: Array = list_expr_array_u256_2(3, 10) - let my_tuple: $tuple_u256_u256_ = $tuple_u256_u256_(item0: 3, item1: 10) - let my_bar: Bar = Bar(val: 3) - while 3 > 4: - pass - - for x in list_expr_array_u256_2(3, 10): - pass - - self.table[10] = 3 - if 3 == 10: - pass - else: - if 10 == 10: - return 3 * 10 - else: - if true: - revert Bar(val: 3) - - - - return self.table[3] - diff --git a/crates/lowering/tests/snapshots/lowering__module_fn.snap b/crates/lowering/tests/snapshots/lowering__module_fn.snap deleted file mode 100644 index 1ac7d0bb87..0000000000 --- a/crates/lowering/tests/snapshots/lowering__module_fn.snap +++ /dev/null @@ -1,35 +0,0 @@ ---- -source: crates/lowering/tests/lowering.rs -expression: lowered - ---- -struct $tuple_u256_u8_: - pub item0: u256 - pub item1: u8 - -struct $tuple_address_tuple_u256_u8__: - pub item0: address - pub item1: $tuple_u256_u8_ - -fn list_expr_array_u8_3(_ val0: u8, _ val1: u8, _ val2: u8) -> Array: - let generated_array: Array - generated_array[0] = val0 - generated_array[1] = val1 - generated_array[2] = val2 - return generated_array - -fn return_tuple() -> $tuple_address_tuple_u256_u8__: - return $tuple_address_tuple_u256_u8__(item0: address(0), item1: $tuple_u256_u8_(item0: 10, item1: 20)) - -fn return_array() -> Array: - return list_expr_array_u8_3(1, 2, 3) - -contract Foo: - pub fn bar(self) -> u256: - let tuple: $tuple_address_tuple_u256_u8__ = return_tuple() - return tuple.item1.item0 - - pub fn sum_things() -> u8: - let x: Array = return_array() - return x[0] + x[1] + x[2] - diff --git a/crates/lowering/tests/snapshots/lowering__module_level_events.snap b/crates/lowering/tests/snapshots/lowering__module_level_events.snap deleted file mode 100644 index 94c383f24b..0000000000 --- a/crates/lowering/tests/snapshots/lowering__module_level_events.snap +++ /dev/null @@ -1,16 +0,0 @@ ---- -source: crates/lowering/tests/lowering.rs -expression: lowered - ---- -use std::context::Context - -event Transfer: - idx sender: address - idx receiver: address - value: u256 - -contract Foo: - fn transfer(ctx: Context, to: address, value: u256) -> (): - emit Transfer(ctx, sender: ctx.msg_sender(), receiver: to, value) - return () diff --git a/crates/lowering/tests/snapshots/lowering__nested_tuple.snap b/crates/lowering/tests/snapshots/lowering__nested_tuple.snap deleted file mode 100644 index c682345ef2..0000000000 --- a/crates/lowering/tests/snapshots/lowering__nested_tuple.snap +++ /dev/null @@ -1,28 +0,0 @@ ---- -source: crates/lowering/tests/lowering.rs -expression: lowered - ---- -struct $tuple_u8_bool_: - pub item0: u8 - pub item1: bool - -struct $tuple_u8_u8_: - pub item0: u8 - pub item1: u8 - -struct $tuple_address_tuple_u8_u8__: - pub item0: address - pub item1: $tuple_u8_u8_ - -struct $tuple_u256_tuple_u8_bool__tuple_address_tuple_u8_u8___: - pub item0: u256 - pub item1: $tuple_u8_bool_ - pub item2: $tuple_address_tuple_u8_u8__ - -contract Foo: - tup: $tuple_u256_tuple_u8_bool__tuple_address_tuple_u8_u8___ - - pub fn bar(self, x: u256) -> u8: - self.tup = $tuple_u256_tuple_u8_bool__tuple_address_tuple_u8_u8___(item0: 1, item1: $tuple_u8_bool_(item0: 0, item1: true), item2: $tuple_address_tuple_u8_u8__(item0: address(0), item1: $tuple_u8_u8_(item0: 10, item1: 100))) - return self.tup.item2.item1.item0 diff --git a/crates/lowering/tests/snapshots/lowering__return_unit.snap b/crates/lowering/tests/snapshots/lowering__return_unit.snap deleted file mode 100644 index ce84293711..0000000000 --- a/crates/lowering/tests/snapshots/lowering__return_unit.snap +++ /dev/null @@ -1,41 +0,0 @@ ---- -source: crates/lowering/tests/lowering.rs -expression: lowered_code - ---- -contract Foo: - pub fn explicit_return_a1() -> (): - return () - - pub fn explicit_return_a2() -> (): - return () - - pub fn explicit_return_b1() -> (): - return () - - pub fn explicit_return_b2() -> (): - return () - - pub fn implicit_a1() -> (): - pass - return () - - pub fn implicit_a2() -> (): - pass - return () - - pub fn if_elif_else() -> (): - if true: - return () - else: - if false: - return () - else: - pass - - - return () - - pub fn tuple_stmt() -> (): - () - return () diff --git a/crates/lowering/tests/snapshots/lowering__struct_fn.snap b/crates/lowering/tests/snapshots/lowering__struct_fn.snap deleted file mode 100644 index a9258179d1..0000000000 --- a/crates/lowering/tests/snapshots/lowering__struct_fn.snap +++ /dev/null @@ -1,22 +0,0 @@ ---- -source: crates/lowering/tests/lowering.rs -expression: lowered - ---- -struct Foo: - pub x: u256 - - pub fn set_x(self, _ new: u256) -> u256: - let old: u256 = self.get_x() - self.x = new - return old - - pub fn get_x(self) -> u256: - return self.x - -fn main() -> (): - let foo: Foo = Foo(x: 10) - foo.set_x(100) - assert foo.get_x() == 100 - return () - diff --git a/crates/lowering/tests/snapshots/lowering__ternary.snap b/crates/lowering/tests/snapshots/lowering__ternary.snap deleted file mode 100644 index 35f973296a..0000000000 --- a/crates/lowering/tests/snapshots/lowering__ternary.snap +++ /dev/null @@ -1,88 +0,0 @@ ---- -source: crates/lowering/tests/lowering.rs -expression: lowered - ---- -struct $tuple_u256_u256_: - pub item0: u256 - pub item1: u256 - -contract Foo: - pub fn bar() -> (): - let $ternary_result_0: u256 - if true: - $ternary_result_0 = 1 - else: - $ternary_result_0 = 0 - - return baz($ternary_result_0) - - pub fn nested() -> (): - if true: - let a: u256 = 10 - let b: u256 = 20 - let x: u256 = 10 - let y: u256 = 20 - let $ternary_result_0: u256 - if true: - $ternary_result_0 = x - else: - $ternary_result_0 = y - - let $ternary_result_1: u256 - if true: - $ternary_result_1 = a - else: - $ternary_result_1 = b - - return double_baz($ternary_result_1, $ternary_result_0) - - return () - - pub fn nested_ternary() -> (): - let a: u256 = 10 - let b: u256 = 20 - let x: u256 = 10 - let y: u256 = 20 - let $ternary_result_0: u256 - if true: - $ternary_result_0 = a - else: - let $ternary_result_1: u256 - if true: - $ternary_result_1 = x - else: - $ternary_result_1 = y - - $ternary_result_0 = $ternary_result_1 - - return baz($ternary_result_0) - - pub fn in_dec() -> (): - let a: u256 = 10 - let b: u256 = 20 - let x: u256 = 10 - let y: u256 = 20 - let $ternary_result_0: u256 - if true: - $ternary_result_0 = x - else: - $ternary_result_0 = y - - let $ternary_result_1: u256 - if true: - $ternary_result_1 = a - else: - $ternary_result_1 = b - - let z: $tuple_u256_u256_ = $tuple_u256_u256_(item0: $ternary_result_1, item1: $ternary_result_0) - return () - - pub fn baz(_ val: u256) -> (): - pass - return () - - pub fn double_baz(_ val1: u256, _ val2: u256) -> (): - pass - return () - diff --git a/crates/lowering/tests/snapshots/lowering__tuple_destruct.snap b/crates/lowering/tests/snapshots/lowering__tuple_destruct.snap deleted file mode 100644 index a1de172a75..0000000000 --- a/crates/lowering/tests/snapshots/lowering__tuple_destruct.snap +++ /dev/null @@ -1,28 +0,0 @@ ---- -source: crates/lowering/tests/lowering.rs -expression: lowered_code - ---- -struct $tuple_bool_bool_address_u8_: - pub item0: bool - pub item1: bool - pub item2: address - pub item3: u8 - -struct $tuple_u256_bool_: - pub item0: u256 - pub item1: bool - -contract Foo: - my_sto_tuple: $tuple_bool_bool_address_u8_ - - pub fn bar(self, my_tuple: $tuple_u256_bool_) -> (): - let $tmp_tuple_0: $tuple_u256_bool_ = my_tuple - let my_u256: u256 = $tmp_tuple_0.item0 - let my_bool: bool = $tmp_tuple_0.item1 - let $tmp_tuple_1: $tuple_bool_bool_address_u8_ = self.my_sto_tuple.to_mem() - let a: bool = $tmp_tuple_1.item0 - let b: bool = $tmp_tuple_1.item1 - let c: address = $tmp_tuple_1.item2 - let d: u8 = $tmp_tuple_1.item3 - return () diff --git a/crates/lowering/tests/snapshots/lowering__type_alias_tuple.snap b/crates/lowering/tests/snapshots/lowering__type_alias_tuple.snap deleted file mode 100644 index c09fdda914..0000000000 --- a/crates/lowering/tests/snapshots/lowering__type_alias_tuple.snap +++ /dev/null @@ -1,15 +0,0 @@ ---- -source: crates/lowering/tests/lowering.rs -expression: lowered_code - ---- -struct $tuple_u8_address_: - pub item0: u8 - pub item1: address - -type Tup = $tuple_u8_address_ - -contract Foo: - tup: Tup - - diff --git a/crates/lowering/tests/snapshots/lowering__unit_implicit.snap b/crates/lowering/tests/snapshots/lowering__unit_implicit.snap deleted file mode 100644 index ba9a85d4ac..0000000000 --- a/crates/lowering/tests/snapshots/lowering__unit_implicit.snap +++ /dev/null @@ -1,9 +0,0 @@ ---- -source: crates/lowering/tests/lowering.rs -expression: lowered_code - ---- -contract Foo: - fn bar() -> (): - pass - return () diff --git a/crates/new_abi/Cargo.toml b/crates/new_abi/Cargo.toml deleted file mode 100644 index 7fd7373be9..0000000000 --- a/crates/new_abi/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "fe-new_abi" -version = "0.16.0-alpha" -authors = ["The Fe Developers "] -edition = "2021" -license = "Apache-2.0" -repository = "https://github.com/ethereum/fe" - -[dependencies] -fe-common = { path = "../common", version = "^0.16.0-alpha" } -serde = { version = "1.0", features = ["derive"] } - -[dev-dependencies] -serde_test = "1.0" diff --git a/crates/new_abi/src/lib.rs b/crates/new_abi/src/lib.rs deleted file mode 100644 index 7860aec9c2..0000000000 --- a/crates/new_abi/src/lib.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod contract; -pub mod event; -pub mod function; -pub mod types; diff --git a/crates/test-utils/Cargo.toml b/crates/test-utils/Cargo.toml index 094ab8bc12..c697318654 100644 --- a/crates/test-utils/Cargo.toml +++ b/crates/test-utils/Cargo.toml @@ -12,7 +12,6 @@ evm = "0.35.0" evm-runtime = "0.35.0" fe-common = {path = "../common", version = "^0.16.0-alpha"} fe-driver = {path = "../driver", version = "^0.16.0-alpha"} -fe-yulgen = {path = "../yulgen", version = "^0.16.0-alpha"} fe-yulc = {path = "../yulc", version = "^0.16.0-alpha", optional = true, features = ["solc-backend"]} fe-analyzer = {path = "../analyzer", version = "^0.16.0-alpha"} test-files = {path = "../test-files", package = "fe-test-files" } diff --git a/crates/test-utils/src/lib.rs b/crates/test-utils/src/lib.rs index 67997cb8bf..0d0c2b4d79 100644 --- a/crates/test-utils/src/lib.rs +++ b/crates/test-utils/src/lib.rs @@ -2,7 +2,6 @@ use evm_runtime::{ExitReason, Handler}; use fe_common::diagnostics::print_diagnostics; use fe_common::utils::keccak; use fe_driver as driver; -use fe_yulgen::runtime::functions; use primitive_types::{H160, U256}; use std::cell::RefCell; use std::collections::BTreeMap; @@ -602,7 +601,7 @@ pub struct Runtime { impl Default for Runtime { fn default() -> Self { - Self::new().with_functions(functions::std()) + Self::new() } } diff --git a/crates/tests/Cargo.toml b/crates/tests/Cargo.toml index 09327d0d05..040090a0bb 100644 --- a/crates/tests/Cargo.toml +++ b/crates/tests/Cargo.toml @@ -15,9 +15,7 @@ evm-runtime = "0.35.0" fe-analyzer = {path = "../analyzer", version = "^0.16.0-alpha"} fe-common = {path = "../common", version = "^0.16.0-alpha"} fe-compiler-test-utils = {path = "../test-utils" } -fe-lowering = {path = "../lowering", version = "^0.16.0-alpha"} fe-parser = {path = "../parser", version = "^0.16.0-alpha"} -fe-yulgen = {path = "../yulgen", version = "^0.16.0-alpha"} fe-yulc = {path = "../yulc", version = "^0.16.0-alpha"} fe-driver = {path = "../driver", version = "^0.16.0-alpha"} test-files = {path = "../test-files", package = "fe-test-files" } diff --git a/crates/tests/src/lib.rs b/crates/tests/src/lib.rs index b42838655d..6a71d42f7d 100644 --- a/crates/tests/src/lib.rs +++ b/crates/tests/src/lib.rs @@ -15,8 +15,6 @@ mod features; #[cfg(test)] mod ingots; #[cfg(test)] -mod runtime; -#[cfg(test)] mod solidity; #[cfg(test)] mod stress; diff --git a/crates/tests/src/runtime.rs b/crates/tests/src/runtime.rs deleted file mode 100644 index f8d387ff49..0000000000 --- a/crates/tests/src/runtime.rs +++ /dev/null @@ -1,290 +0,0 @@ -//! Tests for the Fe runtime - -#![cfg(feature = "solc-backend")] - -use fe_compiler_test_utils::*; -use yultsur::*; - -macro_rules! assert_eq { - ($a:tt, $b:tt) => { - statement! { - (if (iszero((eq($a, $b)))) { - (revert(0, 0)) - }) - } - }; -} - -#[test] -fn test_runtime_alloc_and_avail() { - with_executor(&|mut executor| { - Runtime::default() - .with_test_statements(statements! { - (let a := avail()) - (let b := alloc(5)) - (let c := alloc(10)) - (let d := avail()) - - [assert_eq!(b, a)] - [assert_eq!(c, (add(b, 5)))] - [assert_eq!(d, (add(c, 10)))] - }) - .execute(&mut executor) - .expect_success(); - }) -} - -#[test] -fn test_runtime_mcopys() { - with_executor(&|mut executor| { - Runtime::default() - .with_test_statements(statements! { - (let a := 0x0111114211111111011111112342311101111112221151110111111111111111) - (let b := 0x0111111234111111011123411111111101112431111111110111111234411111) - (let c := 0x0111341111111111011111111123411101111123411111110111111234111111) - - (let d := 0x0111112344111111011111145111111101111111111111110111111111111111) - (let e := 0x0111112344111111011111145111111101111111110000000000000000000000) - - (mstore(100, a)) - (mstore(132, b)) - (mstore(164, c)) - (mstore(196, d)) - - (mcopys(100, 42, 117)) - - [assert_eq!(a, (sload(42)))] - [assert_eq!(b, (sload(43)))] - [assert_eq!(c, (sload(44)))] - [assert_eq!(e, (sload(45)))] - - (mcopys(100, 46, 128)) - - [assert_eq!(a, (sload(46)))] - [assert_eq!(b, (sload(47)))] - [assert_eq!(c, (sload(48)))] - [assert_eq!(d, (sload(49)))] - }) - .execute(&mut executor) - .expect_success(); - }) -} - -#[test] -fn test_runtime_scopym() { - with_executor(&|mut executor| { - Runtime::default() - .with_test_statements(statements! { - (let a := 0x0111114211111111011111112342311101111112221151110111111111111111) - (let b := 0x0111111234111111011123411111111101112431111111110111111234411111) - (let c := 0x0111341111111111011111111123411101111123411111110111111234111111) - - (let d := 0x0111112344111111011111145111111101111111111111110111111111111111) - (let e := 0x0111112344111111011111145111111101111111110000000000000000000000) - - (sstore(42, a)) - (sstore(43, b)) - (sstore(44, c)) - (sstore(45, d)) - - (let ptr1 := scopym(42, 117)) - (let ptr2 := add(ptr1, 32)) - (let ptr3 := add(ptr2, 32)) - (let ptr4 := add(ptr3, 32)) - - [assert_eq!(a, (mload(ptr1)))] - [assert_eq!(b, (mload(ptr2)))] - [assert_eq!(c, (mload(ptr3)))] - [assert_eq!(e, (mload(ptr4)))] - - (let ptr5 := scopym(42, 128)) - (let ptr6 := add(ptr5, 32)) - (let ptr7 := add(ptr6, 32)) - (let ptr8 := add(ptr7, 32)) - - [assert_eq!(a, (mload(ptr5)))] - [assert_eq!(b, (mload(ptr6)))] - [assert_eq!(c, (mload(ptr7)))] - [assert_eq!(d, (mload(ptr8)))] - }) - .execute(&mut executor) - .expect_success(); - }) -} - -#[test] -fn test_runtime_mloadn() { - with_executor(&|mut executor| { - Runtime::default() - .with_test_statements(statements! { - (let a := 0x4200000000000000000000000000000000000000000000000000000000420026) - (mstore(100, a)) - - [assert_eq!(0x42, (mloadn(100, 1)))] - [assert_eq!(0x420026, (mloadn(129, 3)))] - [assert_eq!(0x26, (mloadn(130, 2)))] - [assert_eq!(0x26, (mloadn(131, 1)))] - }) - .execute(&mut executor) - .expect_success(); - }) -} - -#[test] -fn test_runtime_storage_sanity() { - with_executor(&|mut executor| { - Runtime::new() - .with_test_statements(statements! { - (let a := 0x4200000000000000000000000000000000000000000000000000000000000026) - (let b := 0x9900000000000000000000000000000000000000000000000000000000000077) - (sstore(0, a)) - (sstore(1, b)) - - [assert_eq!(a, (sload(0)))] - [assert_eq!(b, (sload(1)))] - }) - .execute(&mut executor) - .expect_success(); - }) -} - -#[test] -fn test_runtime_sloadn() { - with_executor(&|mut executor| { - Runtime::default() - .with_test_statements(statements! { - (let a := 0x4200000000000000000000000000000000000000000000000000000000000026) - (let b := 0x9900530000003900000000000000000000000000000000000000000000000077) - (sstore(1000, a)) - (sstore(1001, b)) - - [assert_eq!(a, (sloadn(1000, 0, 32)))] - [assert_eq!(b, (sloadn(1001, 0, 32)))] - - [assert_eq!(0x42, (sloadn(1000, 0, 1)))] - [assert_eq!(0x26, (sloadn(1000, 31, 1)))] - [assert_eq!(0x4200, (sloadn(1000, 0, 2)))] - - [assert_eq!(0x99, (sloadn(1001, 0, 1)))] - [assert_eq!(0x77, (sloadn(1001, 31, 1)))] - [assert_eq!(0x990053, (sloadn(1001, 0, 3)))] - [assert_eq!(0x5300000039, (sloadn(1001, 2, 5)))] - }) - .execute(&mut executor) - .expect_success(); - }) -} - -#[test] -fn test_runtime_sstoren() { - with_executor(&|mut executor| { - Runtime::default() - .with_test_statements(statements! { - (let a := 0x0111111111111111011111111111111101111111111111110111111111111111) - // dashes indicate which bytes are to be replaced in this test - // 0----2 8----10 15----------------23 31--32 - (let b := 0x4201111111111111123411111111119999999998998999110111111111111126) - (sstore(1000, a)) - - (sstoren(1000, 0, 2, 0x4201)) - (sstoren(1000, 8, 2, 0x1234)) - (sstoren(1000, 15, 8, 0x9999999998998999)) - (sstoren(1000, 31, 1, 0x26)) - - [assert_eq!(b, (sload(1000)))] - - (let c := 0x4242424242424242424242424242424242424242424242424242424242424242) - (sstoren(1000, 0, 32, c)) - - [assert_eq!(c, (sload(1000)))] - }) - .execute(&mut executor) - .expect_success(); - }) -} - -#[test] -fn test_runtime_ceil32() { - with_executor(&|mut executor| { - Runtime::default() - .with_test_statements(statements! { - [assert_eq!(32, (ceil32(29)))] - [assert_eq!(256, (ceil32(225)))] - }) - .execute(&mut executor) - .expect_success(); - }) -} - -#[test] -fn test_runtime_abi_unpack() { - with_executor(&|mut executor| { - Runtime::default() - .with_test_statements(statements! { - (let a := 0x0042002600530000000000000000000000000000000000000000000000000000) - (let packed := alloc_mstoren(a, 32)) - (let unpacked := avail()) - (abi_unpack(packed, 3, 2, 0)) - - (let elem0 := mload(unpacked)) - (let elem1 := mload((add(unpacked, 32)))) - (let elem2 := mload((add(unpacked, 64)))) - - [assert_eq!(elem0, 0x0042)] - [assert_eq!(elem1, 0x0026)] - [assert_eq!(elem2, 0x0053)] - }) - .execute(&mut executor) - .expect_success(); - }) -} - -#[test] -fn test_keccak256() { - with_executor(&|mut executor| { - Runtime::default().with_test_statements( - statements! { - (let num := 63806209331542711802848847270949280092855778197726125910674179583545433573378) - (let result :=109966633016701122630199943745061001312678661825260870342362413625737614346915) - // Can be validated with solidity - // uint(keccak256(abi.encodePacked(uint(63806209331542711802848847270949280092855778197726125910674179583545433573378)))); - // which returns 109966633016701122630199943745061001312678661825260870342362413625737614346915 - (mstore(0, num)) - [assert_eq!(result, (keccak256(0, 32)))] - }, - ) - .execute(&mut executor) - .expect_success(); - }) -} - -#[test] -fn test_runtime_set_zero() { - with_executor(&|mut executor| { - Runtime::default() - .with_test_statements(statements! { - (let a := 0x1111111111111111111111111111111111111111111111111111111111111111) - (let b := 0x1111110000000000000000000000000000000000000000000000001111111111) - (let c := 0x1111100000000000000000000000000000000000000000000000000000000000) - - [assert_eq!(0, (set_zero(0, 256, a)))] - [assert_eq!(0x11, (set_zero(0, 248, a)))] - [assert_eq!(b, (set_zero(24, 216, a)))] - [assert_eq!(c, (set_zero(20, 256, a)))] - }) - .execute(&mut executor) - .expect_success(); - }) -} - -#[test] -fn checked_exp_signed() { - with_executor(&|mut executor| { - Runtime::default() - .with_test_statements(statements! { - [assert_eq!(4, (checked_exp_signed(2, 2, 0, 100)))] - }) - .execute(&mut executor) - .expect_success(); - }) -} diff --git a/crates/yulc/Cargo.toml b/crates/yulc/Cargo.toml index 57a1dc9271..21ab3e8230 100644 --- a/crates/yulc/Cargo.toml +++ b/crates/yulc/Cargo.toml @@ -7,7 +7,6 @@ license = "GPL-3.0-or-later" repository = "https://github.com/ethereum/fe" [dependencies] -fe-yulgen = {path = "../yulgen", version = "^0.16.0-alpha"} # This fork supports concurrent compilation, which is required for Rust tests. solc = { git = "https://github.com/g-r-a-n-t/solc-rust", rev = "52d4146", optional = true} serde_json = "1.0" diff --git a/crates/yulgen/Cargo.toml b/crates/yulgen/Cargo.toml deleted file mode 100644 index 8657ccf7ee..0000000000 --- a/crates/yulgen/Cargo.toml +++ /dev/null @@ -1,29 +0,0 @@ -[package] -name = "fe-yulgen" -version = "0.16.0-alpha" -authors = ["The Fe Developers "] -edition = "2021" -license = "GPL-3.0-or-later" -repository = "https://github.com/ethereum/fe" - -[dependencies] -fe-abi = {path = "../abi", version = "^0.16.0-alpha"} -fe-analyzer = {path = "../analyzer", version = "^0.16.0-alpha"} -fe-lowering = {path = "../lowering", version = "^0.16.0-alpha"} -fe-common = {path = "../common", version = "^0.16.0-alpha"} -fe-parser = {path = "../parser", version = "^0.16.0-alpha"} -indexmap = "1.6.2" -maplit = "1.0.2" -num-bigint = "0.4.3" -salsa = "0.16.1" - -# This fork contains the shorthand macros and some other necessary updates. -yultsur = { git = "https://github.com/g-r-a-n-t/yultsur", rev = "ae85470"} -smol_str = "0.1.21" -if_chain = "1.0.2" - -[dev-dependencies] -insta = "1.7.1" -test-files = {path = "../test-files", package = "fe-test-files" } -wasm-bindgen-test = "0.3" -pretty_assertions = "1.0.0" diff --git a/crates/yulgen/src/constants.rs b/crates/yulgen/src/constants.rs deleted file mode 100644 index 29a1c2ee49..0000000000 --- a/crates/yulgen/src/constants.rs +++ /dev/null @@ -1,69 +0,0 @@ -use fe_analyzer::namespace::types::Integer; -use maplit::hashmap; -use std::collections::HashMap; -use yultsur::*; - -/// Return a hashmap containing min/max YUL literals for each supported integer -/// size -pub fn numeric_min_max() -> HashMap { - hashmap! { - Integer::U256 => ( - literal_expression! {0x0}, - literal_expression! {0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff}, - ), - Integer::U128 => ( - literal_expression! {0x0}, - literal_expression! {0xffffffffffffffffffffffffffffffff}, - ), - Integer::U64 => ( - literal_expression! {0x0}, - literal_expression! {0xffffffffffffffff}, - ), - Integer::U32 => ( - literal_expression! {0x0}, - literal_expression! {0xffffffff}, - ), - Integer::U16 => ( - literal_expression! {0x0}, - literal_expression! {0xffff}, - ), - Integer::U8 => ( - literal_expression! {0x0}, - literal_expression! {0xff}, - ), - Integer::I256 => ( - literal_expression! {0x8000000000000000000000000000000000000000000000000000000000000000}, - literal_expression! {0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff} - ), - Integer::I128 => ( - literal_expression! {0xffffffffffffffffffffffffffffffff80000000000000000000000000000000}, - literal_expression! {0x7fffffffffffffffffffffffffffffff} - ), - Integer::I64 => ( - literal_expression! {0xffffffffffffffffffffffffffffffffffffffffffffffff8000000000000000}, - literal_expression! {0x7fffffffffffffff} - ), - Integer::I32 => ( - literal_expression! {0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff80000000}, - literal_expression! {0x7fffffff} - ), - Integer::I16 => ( - literal_expression! {0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8000}, - literal_expression! {0x7fff} - ), - Integer::I8 => ( - literal_expression! {0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80}, - literal_expression! {0x7f} - ) - } -} - -// Panic codes as defined by solidity -// https://docs.soliditylang.org/en/v0.8.6/control-structures.html?highlight=0x12#panic-via-assert-and-error-via-require - -pub const PANIC_FAILED_ASSERTION: usize = 0x01; -pub const PANIC_OVER_OR_UNDERFLOW: usize = 0x11; -pub const PANIC_DIV_OR_MOD_BY_ZERO: usize = 0x12; -pub const PANIC_OUT_OF_BOUNDS: usize = 0x32; - -pub const ERROR_INVALID_ABI_DATA: usize = 0x103; diff --git a/crates/yulgen/src/constructor.rs b/crates/yulgen/src/constructor.rs deleted file mode 100644 index 0e49628ed5..0000000000 --- a/crates/yulgen/src/constructor.rs +++ /dev/null @@ -1,96 +0,0 @@ -use crate::names::abi as abi_names; -use crate::operations::abi as abi_operations; -use crate::runtime::functions; -use crate::types::{AbiDecodeLocation, AbiType}; -use yultsur::*; - -/// Builds a constructor for a contract with no init function. -/// -/// The contract is simply deployed by loading the code into memory and -/// returning it. -pub fn build() -> yul::Code { - // we get the deployment statements and wrap them in a code block - let deployment = deployment(); - code! { [deployment...] } -} - -/// Builds a constructor for a contract with an init function. -pub fn build_with_init( - contract_name: &str, - init_function_name: &str, - init_params: &[AbiType], - init_callgraph: Vec, - expects_ctx: bool, -) -> yul::Code { - // Generate names for our constructor parameters. - let (param_idents, param_exprs) = abi_names::vals("init", init_params.len()); - - let decode_fns = functions::abi::decode_functions(init_params, AbiDecodeLocation::Memory); - - // Decode the parameters, if any are given. - let maybe_decode_params = if init_params.is_empty() { - statements! {} - } else { - let decode_expr = abi_operations::decode_data( - init_params, - expression! { params_start_mem }, - expression! { params_end_mem }, - AbiDecodeLocation::Memory, - ); - - statements! { (let [param_idents...] := [decode_expr]) } - }; - - // init function name after it is mapped. - let init_function_name = identifier! { (init_function_name) }; - - let contract_name = literal_expression! { (format!("\"{}\"", contract_name)) }; - - let deployment = deployment(); - - let init_call = if expects_ctx { - // we pass in a `0` for the expected `Context` argument - expression! { [init_function_name](0, [param_exprs...]) } - } else { - expression! { [init_function_name]([param_exprs...]) } - }; - - // Build a constructor that runs a user defined init function. Parameters for - // init functions are appended to the end of the initialization code. - // - // The start of the init parameters in code is stored in `params_start_code`. - // The parameters are immediately copied from this location into memory at - // `mem_start`. From there, parameters are decoded and passed into the - // init function. - code! { - // add init function and dependencies to scope - [init_callgraph...] - [decode_fns...] - - // copy the encoded parameters to memory - (let params_start_code := datasize([contract_name])) - (let params_end_code := codesize()) - (let params_size := sub(params_end_code, params_start_code)) - (let params_start_mem := alloc(params_size)) - (let params_end_mem := add(params_start_mem, params_size)) - (codecopy(params_start_mem, params_start_code, params_size)) - - // decode the parameters from memory - [maybe_decode_params...] - - // call the init function defined above - (pop([init_call])) - - // deploy the contract - [deployment...] - } -} - -/// Copies contract data to memory and returns it. -fn deployment() -> Vec { - statements! { - (let size := datasize("runtime")) - (datacopy(0, (dataoffset("runtime")), size)) - (return(0, size)) - } -} diff --git a/crates/yulgen/src/context.rs b/crates/yulgen/src/context.rs deleted file mode 100644 index f1c75dfe12..0000000000 --- a/crates/yulgen/src/context.rs +++ /dev/null @@ -1,56 +0,0 @@ -use crate::{AnalyzerDb, YulgenDb}; -use fe_analyzer::context::{CallType, ExpressionAttributes, FunctionBody}; -use fe_analyzer::namespace::types::{Event, Type}; -use fe_parser::ast; -use fe_parser::node::Node; -use std::rc::Rc; - -pub struct FnContext<'a> { - pub adb: &'a dyn AnalyzerDb, - pub db: &'a dyn YulgenDb, - fn_body: Rc, -} - -impl<'a> FnContext<'a> { - pub fn new(db: &'a dyn YulgenDb, fn_body: Rc) -> Self { - Self { - adb: db.upcast(), - db, - fn_body, - } - } - - /// Get information that has been attributed to an expression node. - pub fn expression_attributes(&self, expr: &Node) -> &ExpressionAttributes { - self.fn_body - .expressions - .get(&expr.id) - .expect("missing expression attributes") - } - - /// Get the type of a variable declaration. - pub fn declaration_type(&self, typ: &Node) -> &Type { - self.fn_body - .var_decl_types - .get(&typ.id) - .expect("missing declaration type") - } - - /// Get information that has been attributed to a call expression node. - pub fn call_type(&self, expr: &Node) -> CallType { - self.fn_body - .calls - .get(&expr.id) - .cloned() - .expect("missing call type") - } - - /// Get information that has been attributed to an emit statement node. - pub fn emitted_event(&self, emit_stmt: &Node) -> Rc { - self.fn_body - .emits - .get(&emit_stmt.id) - .expect("missing emit event type") - .typ(self.adb) - } -} diff --git a/crates/yulgen/src/db.rs b/crates/yulgen/src/db.rs deleted file mode 100644 index 4764d67c5e..0000000000 --- a/crates/yulgen/src/db.rs +++ /dev/null @@ -1,114 +0,0 @@ -use crate::types::AbiType; -use fe_analyzer::namespace::items::{ContractId, EventId, FunctionId, ModuleId, StructId}; -use fe_analyzer::AnalyzerDb; -use fe_common::db::{SourceDb, SourceDbStorage, Upcast, UpcastMut}; -use fe_lowering::LoweringDb; -use indexmap::{IndexMap, IndexSet}; -use smol_str::SmolStr; -use std::rc::Rc; -use yultsur::yul; - -mod queries; - -#[salsa::query_group(YulgenDbStorage)] -pub trait YulgenDb: - SourceDb - + AnalyzerDb - + LoweringDb - + Upcast - + Upcast - + Upcast - + UpcastMut - + UpcastMut -{ - #[salsa::invoke(queries::compile_module)] - fn compile_module(&self, module_id: ModuleId) -> IndexMap; - - #[salsa::invoke(queries::contracts::contract_object)] - fn contract_object(&self, contract: ContractId) -> yul::Object; - #[salsa::invoke(queries::contracts::contract_abi_dispatcher)] - fn contract_abi_dispatcher(&self, contract: ContractId) -> Vec; - - #[salsa::invoke(queries::functions::function_yul_name)] - fn function_yul_name(&self, function: FunctionId) -> SmolStr; - #[salsa::invoke(queries::functions::function_external_call_name)] - fn function_external_call_name(&self, function: FunctionId) -> SmolStr; - #[salsa::invoke(queries::functions::function_external_call_fn)] - fn function_external_call_fn(&self, function: FunctionId) -> Vec; - #[salsa::invoke(queries::functions::function_def)] - fn function_def(&self, function: FunctionId) -> yul::Statement; - #[salsa::invoke(queries::functions::function_sig_abi_types)] - fn function_sig_abi_types(&self, function: FunctionId) -> (Rc<[AbiType]>, Option); - #[salsa::invoke(queries::functions::assert_string_types)] - fn function_assert_string_types(&self, function: FunctionId) -> Rc>; - #[salsa::invoke(queries::functions::revert_types)] - fn function_revert_errors(&self, function: FunctionId) -> Rc>; - - #[salsa::invoke(queries::events::event_idx_abi_types)] - fn event_idx_abi_types(&self, event: EventId) -> Rc<[AbiType]>; - - #[salsa::invoke(queries::structs::struct_abi_type)] - fn struct_abi_type(&self, id: StructId) -> AbiType; - #[salsa::invoke(queries::structs::struct_field_abi_types)] - fn struct_field_abi_types(&self, id: StructId) -> Rc<[AbiType]>; - #[salsa::invoke(queries::structs::struct_qualified_name)] - fn struct_qualified_name(&self, id: StructId) -> SmolStr; - #[salsa::invoke(queries::structs::struct_getter_name)] - fn struct_getter_name(&self, id: StructId, field: SmolStr) -> SmolStr; - #[salsa::invoke(queries::structs::struct_getter_fn)] - fn struct_getter_fn(&self, id: StructId, field: SmolStr) -> yul::Statement; - #[salsa::invoke(queries::structs::struct_init_name)] - fn struct_init_name(&self, id: StructId) -> SmolStr; - #[salsa::invoke(queries::structs::struct_init_fn)] - fn struct_init_fn(&self, id: StructId) -> yul::Statement; - #[salsa::invoke(queries::structs::struct_api_fns)] - fn struct_api_fns(&self, id: StructId) -> Vec; -} - -#[salsa::database( - SourceDbStorage, - fe_analyzer::db::AnalyzerDbStorage, - fe_lowering::db::LoweringDbStorage, - YulgenDbStorage -)] -#[derive(Default)] -pub struct Db { - storage: salsa::Storage, -} -impl salsa::Database for Db {} - -impl Upcast for Db { - fn upcast(&self) -> &(dyn SourceDb + 'static) { - &*self - } -} - -impl UpcastMut for Db { - fn upcast_mut(&mut self) -> &mut (dyn SourceDb + 'static) { - &mut *self - } -} - -impl Upcast for Db { - fn upcast(&self) -> &(dyn LoweringDb + 'static) { - &*self - } -} - -impl Upcast for Db { - fn upcast(&self) -> &(dyn AnalyzerDb + 'static) { - &*self - } -} - -impl UpcastMut for Db { - fn upcast_mut(&mut self) -> &mut (dyn AnalyzerDb + 'static) { - &mut *self - } -} - -impl Upcast for Db { - fn upcast(&self) -> &(dyn YulgenDb + 'static) { - &*self - } -} diff --git a/crates/yulgen/src/db/queries.rs b/crates/yulgen/src/db/queries.rs deleted file mode 100644 index 887525f020..0000000000 --- a/crates/yulgen/src/db/queries.rs +++ /dev/null @@ -1,48 +0,0 @@ -use crate::db::YulgenDb; -use crate::mappers; -use fe_analyzer::namespace::items::ModuleId; -use indexmap::map::IndexMap; -use yultsur::yul; - -pub mod contracts; -pub mod events; -pub mod functions; -pub mod structs; - -pub fn compile_module(db: &dyn YulgenDb, module: ModuleId) -> IndexMap { - mappers::module::module(db, module) - .drain() - .map(|(name, object)| (name, to_safe_json(object))) - .collect() -} - -fn to_safe_json(obj: yul::Object) -> String { - normalize_object(obj).to_string().replace('"', "\\\"") -} - -fn normalize_object(obj: yul::Object) -> yul::Object { - let data = obj - .data - .into_iter() - .map(|data| yul::Data { - name: data.name, - value: data - .value - .replace('\\', "\\\\\\\\") - .replace('\n', "\\\\n") - .replace('"', "\\\\\"") - .replace('\r', "\\\\r") - .replace('\t', "\\\\t"), - }) - .collect::>(); - yul::Object { - name: obj.name, - code: obj.code, - objects: obj - .objects - .into_iter() - .map(normalize_object) - .collect::>(), - data, - } -} diff --git a/crates/yulgen/src/db/queries/contracts.rs b/crates/yulgen/src/db/queries/contracts.rs deleted file mode 100644 index f118b53685..0000000000 --- a/crates/yulgen/src/db/queries/contracts.rs +++ /dev/null @@ -1,219 +0,0 @@ -use crate::constructor; -use crate::context::FnContext; -use crate::db::YulgenDb; -use crate::mappers::functions::multiple_func_stmt; -use crate::runtime::{abi_dispatcher, functions}; -use crate::types::{AbiDecodeLocation, AsAbiType}; -use fe_analyzer::builtins::ValueMethod; -use fe_analyzer::context::CallType; -use fe_analyzer::namespace::items::{walk_local_dependencies, ContractId, DepGraph, Item, TypeDef}; -use fe_common::utils::keccak; -use indexmap::IndexSet; -use smol_str::SmolStr; -use yultsur::*; - -pub fn contract_object(db: &dyn YulgenDb, contract: ContractId) -> yul::Object { - let adb = db.upcast(); - - let runtime_object = { - let (mut functions, data, objects) = build_dependency_graph( - db, - &contract.runtime_dependency_graph(adb), - Item::Type(TypeDef::Contract(contract)), - ); - - // This can all be replaced with a call to the contract's `__call__` function once the - // dispatching code has been moved into lowering. - // - // For now, we must do the following: - // - check if a `__call__` function has been defined in the contract and, if so, we map its - // body into a Yul function named `$$__call__` - // - if a `__call__` function is not defined in the contract, we generate a `$$__call__` - // function that dispatches calls using the Solidity ABI - // - call the `$$__call__` function - let call_fn_ident = identifier! { ("$$__call__") }; - if let Some(call_fn) = contract.call_function(adb) { - let mut fn_context = FnContext::new(db, call_fn.body(adb)); - let function_statements = - multiple_func_stmt(&mut fn_context, &call_fn.data(adb).ast.kind.body); - let call_fn_yul = function_definition! { - function [call_fn_ident.clone()]() { - // the function body will contain one or more `return_val` assignments - (let return_val := 0) - [function_statements...] - } - }; - functions.push(call_fn_yul); - } else { - functions.extend(db.contract_abi_dispatcher(contract)); - } - functions.sort(); - functions.dedup(); - - yul::Object { - name: identifier! { runtime }, - code: yul::Code { - block: yul::Block { - statements: statements! { - [functions...] - ([call_fn_ident]()) - }, - }, - }, - objects, - data, - } - }; - - let contract_name = contract.name(adb); - if let Some(init_fn) = contract.init_function(adb) { - let (functions, data, objects) = - build_dependency_graph(db, &init_fn.dependency_graph(adb), Item::Function(init_fn)); - - let (params, _) = db.function_sig_abi_types(init_fn); - let code = constructor::build_with_init( - &contract_name, - &db.function_yul_name(init_fn), - ¶ms, - functions, - init_fn.signature(adb).ctx_decl.is_some(), - ); - - // Return constructor object - yul::Object { - name: identifier! { (contract_name) }, - code, - objects: [vec![runtime_object], objects].concat(), - data, - } - } else { - yul::Object { - name: identifier! { (contract_name) }, - code: constructor::build(), - objects: vec![runtime_object], - data: vec![], - } - } -} - -/// Dispatch function and required encode/decode functions. -pub fn contract_abi_dispatcher(db: &dyn YulgenDb, contract: ContractId) -> Vec { - let adb = db.upcast(); - let public_functions = contract - .public_functions(adb) - .values() - .map(|id| { - let bare_name = id.name(adb); - let qualified_name = db.function_yul_name(*id); - let expects_ctx = id.signature(db.upcast()).ctx_decl.is_some(); - let (param_types, return_type) = db.function_sig_abi_types(*id); - ( - bare_name, - qualified_name, - param_types, - return_type, - expects_ctx, - ) - }) - .collect::>(); - - let mut fns = - public_functions - .iter() - .fold(vec![], |mut fns, (_, _, param_types, return_type, _)| { - fns.extend(functions::abi::decode_functions( - param_types, - AbiDecodeLocation::Calldata, - )); - if let Some(return_type) = return_type { - fns.push(functions::abi::encode(&[return_type.clone()])); - } - fns - }); - fns.push(abi_dispatcher::dispatcher(&public_functions)); - fns.sort(); - fns.dedup(); - fns -} - -fn build_dependency_graph( - db: &dyn YulgenDb, - graph: &DepGraph, - root: Item, -) -> (Vec, Vec, Vec) { - let adb = db.upcast(); // AnalyzerDb - - let mut string_literals = IndexSet::::new(); - let mut created_contracts = IndexSet::::new(); - - // We need all of the "std" yul functions, - // because we can't yet track which ones are needed. - let mut yulfns = functions::std(); - - walk_local_dependencies(graph, root, |item| { - match item { - Item::Function(function) => { - yulfns.push(db.function_def(function)); - - let body = function.body(adb); - for calltype in body.calls.values() { - match calltype { - CallType::External { function: fun, .. } => { - yulfns.extend(db.function_external_call_fn(*fun)); - } - CallType::BuiltinValueMethod { - method: ValueMethod::AbiEncode, - typ, - } => { - yulfns.push(functions::abi::encode(&[typ.as_abi_type(adb)])); - } - CallType::BuiltinAssociatedFunction { contract, .. } => { - created_contracts.insert(*contract); - } - _ => {} - } - } - - for struct_ in db.function_revert_errors(function).iter() { - yulfns.push(functions::abi::encode(&[db.struct_abi_type(*struct_)])); - yulfns.push(functions::revert::revert( - &struct_.name(adb), - &db.struct_abi_type(*struct_), - )); - } - for string_type in db.function_assert_string_types(function).iter() { - yulfns.push(functions::revert::error_revert(string_type)); - yulfns.push(functions::abi::encode(&[string_type.clone()])); - } - string_literals.extend(body.string_literals.iter().cloned()); - } - Item::Type(TypeDef::Struct(struct_)) => { - // We don't know which struct fields are actually accessed, so we need - // accessor functions for all of them. - yulfns.extend(db.struct_api_fns(struct_)); - } - Item::Event(event) => { - yulfns.push(functions::abi::encode(&db.event_idx_abi_types(event))); - } - _ => {} - } - }); - - yulfns.sort(); - yulfns.dedup(); - - let data = string_literals - .into_iter() - .map(|string| yul::Data { - name: keccak::full(string.as_bytes()), - value: string.to_string(), - }) - .collect(); - - let objects = created_contracts - .iter() - .map(|contract| db.contract_object(*contract)) - .collect(); - - (yulfns, data, objects) -} diff --git a/crates/yulgen/src/db/queries/events.rs b/crates/yulgen/src/db/queries/events.rs deleted file mode 100644 index 7716fbe37e..0000000000 --- a/crates/yulgen/src/db/queries/events.rs +++ /dev/null @@ -1,22 +0,0 @@ -use crate::db::YulgenDb; -use crate::types::{AbiType, AsAbiType}; -use fe_analyzer::namespace::items::EventId; -use std::rc::Rc; - -pub fn event_idx_abi_types(db: &dyn YulgenDb, event: EventId) -> Rc<[AbiType]> { - event - .typ(db.upcast()) - .fields - .iter() - .filter_map(|field| { - (!field.is_indexed).then(|| { - field - .typ - .clone() - .expect("event field type error") - .as_abi_type(db.upcast()) - }) - }) - .collect::>() - .into() -} diff --git a/crates/yulgen/src/db/queries/functions.rs b/crates/yulgen/src/db/queries/functions.rs deleted file mode 100644 index d219686d55..0000000000 --- a/crates/yulgen/src/db/queries/functions.rs +++ /dev/null @@ -1,202 +0,0 @@ -use crate::context::FnContext; -use crate::db::YulgenDb; -use crate::mappers::functions::multiple_func_stmt; -use crate::names; -use crate::operations::abi as abi_operations; -use crate::runtime::functions; -use crate::types::{to_abi_selector_names, to_abi_types, AbiDecodeLocation, AbiType, AsAbiType}; -use fe_abi::utils as abi_utils; -use fe_analyzer::namespace::items::{Class, FunctionId, Item, StructId, TypeDef}; -use fe_analyzer::namespace::types::{Struct, Type}; -use fe_parser::{ast, node::Node}; -use indexmap::IndexSet; -use smol_str::SmolStr; -use std::rc::Rc; -use yultsur::*; - -pub fn function_yul_name(db: &dyn YulgenDb, function: FunctionId) -> SmolStr { - // foo::Bar::new => $$foo$Bar$new - format!("$${}", Item::Function(function).path(db.upcast()).join("$")).into() -} - -pub fn function_def(db: &dyn YulgenDb, function: FunctionId) -> yul::Statement { - let analyzer_db = db.upcast(); - let sig = function.signature(analyzer_db); - - let mut param_names = if sig.self_decl.is_some() - && matches!(function.parent(analyzer_db), Item::Type(TypeDef::Struct(_))) - { - // struct member functions take `$self` in yul - vec![names::var_name("self")] - } else { - vec![] - }; - param_names.extend(sig.params.iter().map(|param| names::var_name(¶m.name))); - - let mut fn_context = FnContext::new(db, function.body(analyzer_db)); - let function_statements = - multiple_func_stmt(&mut fn_context, &function.data(analyzer_db).ast.kind.body); - - let function_name = identifier! { (db.function_yul_name(function)) }; - // all user-defined functions are given a return value during lowering - function_definition! { - function [function_name]([param_names...]) -> return_val { - [function_statements...] - } - } -} - -pub fn function_sig_abi_types( - db: &dyn YulgenDb, - function: FunctionId, -) -> (Rc<[AbiType]>, Option) { - let adb = db.upcast(); - let sig = function.signature(adb); - let return_type = sig.return_type.clone().expect("return type error"); - let param_types = sig.external_param_types(); - ( - to_abi_types(adb, ¶m_types).into(), - (!return_type.is_unit()).then(|| return_type.as_abi_type(adb)), - ) -} - -pub fn revert_types(db: &dyn YulgenDb, function: FunctionId) -> Rc> { - let body = function.body(db.upcast()); - - let mut structs = IndexSet::new(); - for_each_stmt(&function.data(db.upcast()).ast.kind.body, &mut |stmt| { - if let ast::FuncStmt::Revert { error: Some(node) } = stmt { - let attr = body - .expressions - .get(&node.id) - .expect("missing expr attributes"); - if let Type::Struct(Struct { id, .. }) = &attr.typ { - structs.insert(*id); - } - } - }); - - Rc::new(structs) -} - -pub fn assert_string_types(db: &dyn YulgenDb, function: FunctionId) -> Rc> { - let body = function.body(db.upcast()); - - let mut strings = IndexSet::new(); - for_each_stmt(&function.data(db.upcast()).ast.kind.body, &mut |stmt| { - if let ast::FuncStmt::Assert { - msg: Some(node), .. - } = stmt - { - let attr = body - .expressions - .get(&node.id) - .expect("missing expr attributes"); - if let Type::String(string) = &attr.typ { - strings.insert(string.as_abi_type(db.upcast())); - } - } - }); - - Rc::new(strings) -} - -pub fn function_external_call_name(db: &dyn YulgenDb, function: FunctionId) -> SmolStr { - // foo::Bar::new => $$foo$Bar$new - format!("call_{}", db.function_yul_name(function)).into() -} - -/// Create a yul function to make a call to an external contract function. -/// Includes required encode/decode functions. -pub fn function_external_call_fn(db: &dyn YulgenDb, function: FunctionId) -> Vec { - let adb = db.upcast(); - if !matches!(function.class(adb), Some(Class::Contract(_))) { - panic!("external call to non-contract fn") - }; - - let function_name = function.name(adb); - // get the name of the call function and its parameters - let call_fn_name = identifier! { (db.function_external_call_name(function)) }; - let (param_types, return_type) = db.function_sig_abi_types(function); - - // create a pair of identifiers and expressions for the parameters - let (param_idents, param_exprs) = names::abi::vals("param", param_types.len()); - // the function selector must be added to the first 4 bytes of the calldata - let selector = { - let selector = - abi_utils::func_selector(&function_name, &to_abi_selector_names(¶m_types)); - literal_expression! { (selector) } - }; - - // the size of the encoded data - let encoding_size = abi_operations::encoding_size(¶m_types, ¶m_exprs); - // the operations used to encode the parameters - let encoding_operation = abi_operations::encode(¶m_types, param_exprs); - - let mut fns = vec![functions::abi::encode(¶m_types)]; - - if let Some(return_type) = return_type { - fns.extend(functions::abi::decode_functions( - &[return_type.clone()], - AbiDecodeLocation::Memory, - )); - let decoding_operation = abi_operations::decode_data( - &[return_type], - expression! { outstart }, - expression! { add(outstart, outsize) }, - AbiDecodeLocation::Memory, - ); - // return data must be captured and decoded - fns.push(function_definition! { - function [call_fn_name](addr, [param_idents...]) -> return_val { - (let instart := alloc_mstoren([selector], 4)) - (let insize := add(4, [encoding_size])) - (pop([encoding_operation])) - (let success := call((gas()), addr, 0, instart, insize, 0, 0)) - (let outsize := returndatasize()) - (let outstart := alloc(outsize)) - (returndatacopy(outstart, 0, outsize)) - (if (iszero(success)) { (revert(outstart, outsize)) }) - (return_val := [decoding_operation]) - } - }) - } else { - // unit type; there is no return data to handle - // TODO: return_val isn't assigned - fns.push(function_definition! { - function [call_fn_name](addr, [param_idents...]) -> return_val { - (let instart := alloc_mstoren([selector], 4)) - (let insize := add(4, [encoding_size])) - (pop([encoding_operation])) - (let success := call((gas()), addr, 0, instart, insize, 0, 0)) - (if (iszero(success)) { - (let outsize := returndatasize()) - (let outstart := alloc(outsize)) - (returndatacopy(outstart, 0, outsize)) - (revert(outstart, outsize)) - }) - } - }) - } - fns -} - -fn for_each_stmt(stmts: &[Node], f: &mut F) -where - F: FnMut(&ast::FuncStmt), -{ - for node in stmts { - f(&node.kind); - match &node.kind { - ast::FuncStmt::For { body, .. } | ast::FuncStmt::While { body, .. } => { - for_each_stmt(body, f) - } - ast::FuncStmt::If { body, or_else, .. } => { - for_each_stmt(body, f); - for_each_stmt(or_else, f); - } - ast::FuncStmt::Unsafe(body) => for_each_stmt(body, f), - _ => {} - } - } -} diff --git a/crates/yulgen/src/db/queries/structs.rs b/crates/yulgen/src/db/queries/structs.rs deleted file mode 100644 index ea3c1e184d..0000000000 --- a/crates/yulgen/src/db/queries/structs.rs +++ /dev/null @@ -1,148 +0,0 @@ -use crate::db::YulgenDb; -use crate::types::{AbiType, AsAbiType, AsEvmSized}; -use fe_analyzer::namespace::items::{Item, StructId, TypeDef}; -use smol_str::SmolStr; -use std::rc::Rc; -use yultsur::*; - -pub fn struct_field_abi_types(db: &dyn YulgenDb, struct_: StructId) -> Rc<[AbiType]> { - let db = db.upcast(); - struct_ - .fields(db) - .values() - .map(|field| { - field - .typ(db) - .expect("struct field type error") - .as_abi_type(db) - }) - .collect::>() - .into() -} - -pub fn struct_abi_type(db: &dyn YulgenDb, struct_: StructId) -> AbiType { - let components = db.struct_field_abi_types(struct_).to_vec(); - AbiType::Tuple { components } -} - -pub fn struct_qualified_name(db: &dyn YulgenDb, struct_: StructId) -> SmolStr { - // foo::Bar => $$foo$Bar - format!( - "$${}", - Item::Type(TypeDef::Struct(struct_)) - .path(db.upcast()) - .join("$") - ) - .into() -} - -pub fn struct_getter_name(db: &dyn YulgenDb, struct_: StructId, field: SmolStr) -> SmolStr { - format!("{}.get_{}_ptr", db.struct_qualified_name(struct_), field).into() -} - -pub fn struct_getter_fn(db: &dyn YulgenDb, struct_: StructId, field: SmolStr) -> yul::Statement { - let fields = struct_.fields(db.upcast()); - - let (index, _, field_id) = fields - .get_full(field.as_str()) - .expect("invalid struct field name"); - - let field_type = field_id.typ(db.upcast()).expect("struct field error"); - let field_sized = field_type.as_evm_sized(); - // The value of each field occupies 32 bytes. This includes values with sizes - // less than 32 bytes. So, when we get the pointer to the value of a struct - // field, we must take into consideration the left-padding. The left-padding is - // equal to the difference between the value's size and 32 bytes, so we end up - // adding the word offset and the byte offset. - let field_offset = if !field_type.is_base() { - // For now we just assume that non-base types are always stored as references and so the size of the field - // is always of the size of a pointer (32 bytes) - index * 32 - } else if field_sized.size() < 32 { - index * 32 + (32 - field_sized.size()) - } else { - index * field_sized.size() - }; - - let function_name = identifier! { (db.struct_getter_name(struct_, field)) }; - let offset = literal_expression! { (field_offset) }; - - function_definition! { - function [function_name](ptr) -> return_val { - (return_val := add(ptr, [offset])) - } - } -} - -pub fn struct_init_name(db: &dyn YulgenDb, struct_: StructId) -> SmolStr { - format!("{}.new", db.struct_qualified_name(struct_)).into() -} - -pub fn struct_init_fn(db: &dyn YulgenDb, struct_: StructId) -> yul::Statement { - let function_name = identifier! { (db.struct_init_name(struct_)) }; - let fields = struct_.fields(db.upcast()); - - if fields.is_empty() { - // We return 0 here because it is safe to assume that we never write to an empty - // struct. If we end up writing to an empty struct that's an actual Fe - // bug. - return function_definition! { - function [function_name]() -> return_val { - (return_val := 0) - } - }; - } - - let params = fields - .keys() - .map(|name| { - identifier! {(name)} - }) - .collect::>(); - - let body = fields - .iter() - .enumerate() - .flat_map(|(index, (name, _))| { - if index == 0 { - let param_identifier_exp = identifier_expression! {(name)}; - statements! { - (return_val := alloc(32)) - (mstore(return_val, [param_identifier_exp])) - } - } else { - let ptr_identifier = format!("{}_ptr", name); - let ptr_identifier = identifier! {(ptr_identifier)}; - let ptr_identifier_exp = identifier_expression! {(ptr_identifier)}; - let param_identifier_exp = identifier_expression! {(name)}; - statements! { - (let [ptr_identifier] := alloc(32)) - (mstore([ptr_identifier_exp], [param_identifier_exp])) - } - } - }) - .collect::>(); - - function_definition! { - function [function_name]([params...]) -> return_val { - [body...] - } - } -} - -pub fn struct_api_fns(db: &dyn YulgenDb, struct_: StructId) -> Vec { - [ - vec![db.struct_init_fn(struct_)], - struct_ - .fields(db.upcast()) - .keys() - .map(|name| db.struct_getter_fn(struct_, name.clone())) - .collect(), - struct_ - .fields(db.upcast()) - .keys() - .map(|name| db.struct_getter_fn(struct_, name.clone())) - .collect(), - ] - .concat() -} diff --git a/crates/yulgen/src/lib.rs b/crates/yulgen/src/lib.rs deleted file mode 100644 index efa3304aba..0000000000 --- a/crates/yulgen/src/lib.rs +++ /dev/null @@ -1,29 +0,0 @@ -//! Fe to Yul compiler. - -pub use crate::db::{Db, YulgenDb}; -use fe_analyzer::namespace::items::ModuleId; -use fe_analyzer::AnalyzerDb; -use indexmap::map::IndexMap; - -pub mod constants; -pub mod constructor; -mod context; -mod db; -mod mappers; -pub mod names; -pub mod operations; -pub mod runtime; -pub mod types; -mod utils; - -/// Compiles a lowered Fe module to Yul contracts. -/// -/// Returns a `contract_name -> hex_encoded_bytecode` map. -/// -/// # Panics -/// -/// Any failure to compile an AST to Yul is considered a bug, and thus panics. -/// Invalid ASTs should be caught by an analysis step prior to Yul generation. -pub fn compile(db: &dyn YulgenDb, module: ModuleId) -> IndexMap { - db.compile_module(module) -} diff --git a/crates/yulgen/src/mappers/assignments.rs b/crates/yulgen/src/mappers/assignments.rs deleted file mode 100644 index d690139d47..0000000000 --- a/crates/yulgen/src/mappers/assignments.rs +++ /dev/null @@ -1,106 +0,0 @@ -use crate::context::FnContext; -use crate::mappers::expressions; -use crate::operations::data as data_operations; -use crate::operations::structs as struct_operations; -use crate::types::AsEvmSized; -use fe_analyzer::context::Location; -use fe_analyzer::namespace::types::Type; -use fe_parser::ast as fe; -use fe_parser::node::Node; -use yultsur::yul::FunctionCall; -use yultsur::*; - -/// Builds a Yul statement from a Fe assignment. -pub fn assign(context: &mut FnContext, stmt: &Node) -> yul::Statement { - if let fe::FuncStmt::Assign { - target: target_node, - value: value_node, - } = &stmt.kind - { - let target = expressions::expr(context, target_node); - let value = expressions::expr(context, value_node); - - let target_attributes = context.expression_attributes(target_node); - let typ = target_attributes.typ.as_evm_sized(); - let value_attributes = context.expression_attributes(value_node); - - return match ( - value_attributes.final_location(), - target_attributes.final_location(), - ) { - (Location::Memory, Location::Storage { .. }) => { - if let fe::Expr::Attribute { .. } = &target_node.kind { - if let Type::Struct(struct_) = &target_attributes.typ { - return struct_operations::copy_to_storage( - context.db, struct_, target, value, - ); - } - } - - data_operations::mcopys(typ, target, value) - } - (Location::Memory, Location::Value) => { - let target = expr_as_ident(target); - let value = data_operations::mload(typ, value); - statement! { [target] := [value] } - } - (Location::Memory, Location::Memory) => { - if let fe::Expr::Attribute { value: val, .. } = &target_node.kind { - if let Type::Struct(_) = context.expression_attributes(val).typ { - return statement! { mstoren([drop_deref(&target)], 32, [value]) }; - } - } - - let target = expr_as_ident(target); - statement! { [target] := [value] } - } - (Location::Storage { .. }, Location::Storage { .. }) => { - data_operations::scopys(typ, target, value) - } - (Location::Storage { .. }, Location::Value) => { - let target = expr_as_ident(target); - let value = data_operations::sload(typ, value); - statement! { [target] := [value] } - } - (Location::Storage { .. }, Location::Memory) => { - unreachable!("raw sto to mem assign") - } - (Location::Value, Location::Memory) => data_operations::mstore(typ, target, value), - (Location::Value, Location::Storage { .. }) => { - data_operations::sstore(typ, target, value) - } - (Location::Value, Location::Value) => { - let target = expr_as_ident(target); - statement! { [target] := [value] } - } - }; - } - - unreachable!() -} - -fn expr_as_ident(expr: yul::Expression) -> yul::Identifier { - if let yul::Expression::Identifier(ident) = expr { - ident - } else { - panic!("expression is not an identifier {}", expr); - } -} - -fn drop_deref(expr: &yul::Expression) -> yul::Expression { - if let yul::Expression::FunctionCall(FunctionCall { - identifier, - arguments, - }) = expr - { - if identifier.identifier != "mload" { - panic!("Expected mload(some_fn(..)"); - } - arguments - .first() - .expect("expected mload(..) to contain an argument") - .clone() - } else { - panic!("expression is not a function call {}", expr); - } -} diff --git a/crates/yulgen/src/mappers/declarations.rs b/crates/yulgen/src/mappers/declarations.rs deleted file mode 100644 index 912914b8ab..0000000000 --- a/crates/yulgen/src/mappers/declarations.rs +++ /dev/null @@ -1,54 +0,0 @@ -use crate::context::FnContext; -use crate::mappers::expressions; -use crate::names; -use crate::types::EvmSized; -use fe_analyzer::namespace::types::Type; -use fe_parser::ast as fe; -use fe_parser::node::Node; -use yultsur::*; - -/// Builds a Yul statement from a Fe variable declaration -pub fn var_decl(context: &mut FnContext, stmt: &Node) -> yul::Statement { - if let fe::FuncStmt::VarDecl { target, typ, value } = &stmt.kind { - let decl_type = context.declaration_type(typ); - - let target = names::var_name(var_decl_name(&target.kind)); - - return if let Some(value) = value { - let value = expressions::expr(context, value); - statement! { let [target] := [value] } - } else { - match decl_type { - Type::Base(_) => statement! { let [target] := 0 }, - typ => { - let typ: Box = typ.clone().try_into().expect("Invalid type"); - let size = literal_expression! { (typ.size()) }; - statement! { let [target] := alloc([size]) } - } - } - }; - } - - unreachable!() -} - -/// Builds a Yul statement from a Fe const declaration. -/// TODO: We don't perform any optimization on constant and treat it in the same way as a local variable. -pub fn const_decl(context: &mut FnContext, stmt: &Node) -> yul::Statement { - if let fe::FuncStmt::ConstantDecl { name, value, .. } = &stmt.kind { - let target = names::var_name(name.kind.as_str()); - let value = expressions::expr(context, value); - - return statement! { let [target] := [value] }; - } - - unreachable!() -} - -fn var_decl_name(target: &fe::VarDeclTarget) -> &str { - if let fe::VarDeclTarget::Name(name) = target { - name - } else { - panic!("complex VarDeclTargets should be lowered to VarDeclTarget::Name") - } -} diff --git a/crates/yulgen/src/mappers/expressions.rs b/crates/yulgen/src/mappers/expressions.rs deleted file mode 100644 index 07eeaf1085..0000000000 --- a/crates/yulgen/src/mappers/expressions.rs +++ /dev/null @@ -1,464 +0,0 @@ -use crate::context::FnContext; -use crate::names; -use crate::operations::{ - abi as abi_operations, contracts as contract_operations, data as data_operations, - math as math_operations, structs as struct_operations, -}; -use crate::types::{AsAbiType, AsEvmSized, EvmSized}; -use fe_analyzer::builtins::{self, ContractTypeMethod, GlobalFunction}; -use fe_analyzer::context::{CallType, Location}; -use fe_analyzer::namespace::items::Class; -use fe_analyzer::namespace::types::{Base, Type}; -use fe_common::numeric; -use fe_common::utils::keccak; -use fe_parser::ast as fe; -use fe_parser::node::Node; -use num_bigint::BigInt; -use smol_str::SmolStr; -use yultsur::*; - -/// Builds a Yul expression from a Fe expression. -pub fn expr(context: &mut FnContext, exp: &Node) -> yul::Expression { - let expression = match &exp.kind { - fe::Expr::Name(_) => expr_name(exp), - fe::Expr::Path(_) => panic!("path expressions should be lowered or rejected"), - fe::Expr::Num(_) => expr_num(exp), - fe::Expr::Bool(_) => expr_bool(exp), - fe::Expr::Subscript { .. } => expr_subscript(context, exp), - fe::Expr::Attribute { .. } => expr_attribute(context, exp), - fe::Expr::Ternary { .. } => panic!("ternary expressions should be lowered"), - fe::Expr::BoolOperation { .. } => panic!("bool operation expressions should be lowered"), - fe::Expr::BinOperation { .. } => expr_bin_operation(context, exp), - fe::Expr::UnaryOperation { .. } => expr_unary_operation(context, exp), - fe::Expr::CompOperation { .. } => expr_comp_operation(context, exp), - fe::Expr::Call { .. } => expr_call(context, exp), - fe::Expr::List { .. } => panic!("list expressions should be lowered"), - fe::Expr::Tuple { .. } => panic!("tuple expressions should be lowered"), - fe::Expr::Str(_) => expr_str(exp), - fe::Expr::Unit => expression! { 0x0 }, - }; - - let attributes = context.expression_attributes(exp); - match (attributes.location, attributes.move_location) { - (from, Some(to)) => move_expression(expression, attributes.typ.clone(), from, to), - (_, None) => expression, - } -} - -fn move_expression( - val: yul::Expression, - typ: Type, - from: Location, - to: Location, -) -> yul::Expression { - let fixed_size = typ.as_evm_sized(); - match (from, to) { - (Location::Storage { .. }, Location::Value) => { - if let Type::Base(Base::Numeric(integer)) = typ { - math_operations::adjust_numeric_size( - &integer, - data_operations::sload(fixed_size, val), - ) - } else { - data_operations::sload(fixed_size, val) - } - } - (Location::Memory, Location::Value) => { - if let Type::Base(Base::Numeric(integer)) = typ { - math_operations::adjust_numeric_size( - &integer, - data_operations::mload(fixed_size, val), - ) - } else { - data_operations::mload(fixed_size, val) - } - } - (Location::Memory, Location::Memory) => data_operations::mcopym(fixed_size, val), - (Location::Storage { .. }, Location::Memory) => data_operations::scopym(fixed_size, val), - _ => panic!("invalid expression move: {:?} {:?}", from, to), - } -} - -fn expr_call(context: &mut FnContext, exp: &Node) -> yul::Expression { - let (args, func) = match &exp.kind { - fe::Expr::Call { args, func, .. } => (args, func), - _ => unreachable!(), - }; - let call_type = context.call_type(func); - let yul_args: Vec = args - .kind - .iter() - .map(|arg| expr(context, &arg.kind.value)) - .collect(); - - return match call_type { - CallType::BuiltinFunction(func) => match func { - GlobalFunction::Keccak256 => { - let first_arg = &args.kind.first().expect("Missing argument").kind.value; - let attributes = context.expression_attributes(first_arg); - - let size: Box = - attributes.typ.clone().try_into().expect("Invalid type"); - let func_name = identifier! { (func.as_ref()) }; - let size = identifier_expression! { (size.size()) }; - expression! { [func_name]([yul_args[0].clone()], [size]) } - } - }, - CallType::Intrinsic(func) => { - let yul_name = identifier! { (func.as_ref().strip_prefix("__").unwrap()) }; - expression! { [yul_name]([yul_args...]) } - } - CallType::BuiltinValueMethod { method, typ } => { - let target = match &func.kind { - fe::Expr::Attribute { value, .. } => value, - _ => unreachable!(), - }; - match method { - // Copying is done in `expr(..)` based on the move location set - // in the expression's attributes, so we just map the value for - // `to_mem` and `clone`. - builtins::ValueMethod::ToMem | builtins::ValueMethod::Clone => { - expr(context, target) - } - builtins::ValueMethod::AbiEncode => match typ { - Type::Struct(struct_) => abi_operations::encode( - &[struct_.as_abi_type(context.adb)], - vec![expr(context, target)], - ), - _ => panic!("invalid attributes"), - }, - } - } - CallType::TypeConstructor(Type::Struct(val)) => { - struct_operations::init(context.db, val.id, yul_args) - } - CallType::TypeConstructor(Type::Base(Base::Numeric(integer))) => { - math_operations::adjust_numeric_size(&integer, yul_args[0].clone()) - } - CallType::TypeConstructor(typ) => { - if matches!(typ, Type::Contract(_)) { - // the first argument is `ctx`, so we ignore it and give the contract's address - yul_args[1].clone() - } else { - yul_args[0].clone() - } - } - CallType::Pure(func) => { - let func_name = identifier! { (context.db.function_yul_name(func)) }; - expression! { [func_name]([yul_args...]) } - } - CallType::BuiltinAssociatedFunction { contract, function } => { - let contract_name = contract.name(context.adb); - match function { - ContractTypeMethod::Create2 => contract_operations::create2( - &contract_name, - yul_args[1].clone(), - yul_args[2].clone(), - ), - ContractTypeMethod::Create => { - contract_operations::create(&contract_name, yul_args[0].clone()) - } - } - } - CallType::AssociatedFunction { class, function } => { - assert!( - matches!(class, Class::Struct(_)), - "call to contract-associated fn should be rejected by analyzer as not-yet-implemented" - ); - let func_name = identifier! { (context.db.function_yul_name(function)) }; - expression! { [func_name]([yul_args...]) } - } - CallType::ValueMethod { - is_self, - class, - method, - } => { - let target = match &func.kind { - fe::Expr::Attribute { value, .. } => value, - _ => unreachable!(), - }; - - match class { - Class::Contract(_) => { - assert!( - is_self, - "non-self contract calls should be CallType::External" - ); - let fn_name = identifier! { (context.db.function_yul_name(method)) }; - expression! { [fn_name]([yul_args...]) } - } - Class::Struct(_) => { - let target = expr(context, target); - let fn_name = identifier! { (context.db.function_yul_name(method)) }; - expression! { [fn_name]([target], [yul_args...]) } - } - } - } - CallType::External { function, .. } => { - let target = match &func.kind { - fe::Expr::Attribute { value, .. } => value, - _ => unreachable!(), - }; - let address = expr(context, target); - let fn_name = identifier! { (context.db.function_external_call_name(function)) }; - expression! { [fn_name]([address], [yul_args...]) } - } - }; -} - -pub fn expr_comp_operation(context: &mut FnContext, exp: &Node) -> yul::Expression { - if let fe::Expr::CompOperation { left, op, right } = &exp.kind { - let yul_left = expr(context, left); - let yul_right = expr(context, right); - - let typ = &context.expression_attributes(left).typ; - - return match op.kind { - fe::CompOperator::Eq => expression! { eq([yul_left], [yul_right]) }, - fe::CompOperator::NotEq => expression! { iszero((eq([yul_left], [yul_right]))) }, - fe::CompOperator::Lt => match typ.is_signed_integer() { - true => expression! { slt([yul_left], [yul_right]) }, - false => expression! { lt([yul_left], [yul_right]) }, - }, - fe::CompOperator::LtE => match typ.is_signed_integer() { - true => expression! { iszero((sgt([yul_left], [yul_right]))) }, - false => expression! { iszero((gt([yul_left], [yul_right]))) }, - }, - fe::CompOperator::Gt => match typ.is_signed_integer() { - true => expression! { sgt([yul_left], [yul_right]) }, - false => expression! { gt([yul_left], [yul_right]) }, - }, - fe::CompOperator::GtE => match typ.is_signed_integer() { - true => expression! { iszero((slt([yul_left], [yul_right]))) }, - false => expression! { iszero((lt([yul_left], [yul_right]))) }, - }, - }; - } - - unreachable!() -} - -pub fn expr_bin_operation(context: &mut FnContext, exp: &Node) -> yul::Expression { - if let fe::Expr::BinOperation { left, op, right } = &exp.kind { - let yul_left = expr(context, left); - let yul_right = expr(context, right); - - let typ = &context.expression_attributes(left).typ; - - return match op.kind { - fe::BinOperator::Add => match typ { - Type::Base(Base::Numeric(integer)) => { - expression! { [names::checked_add(integer)]([yul_left], [yul_right]) } - } - _ => unimplemented!("Addition for non-numeric types not yet supported"), - }, - fe::BinOperator::Sub => match typ { - Type::Base(Base::Numeric(integer)) => { - expression! { [names::checked_sub(integer)]([yul_left], [yul_right]) } - } - _ => unimplemented!("Subtraction for non-numeric types not yet supported"), - }, - fe::BinOperator::Mult => match typ { - Type::Base(Base::Numeric(integer)) => { - expression! { [names::checked_mul(integer)]([yul_left], [yul_right]) } - } - _ => unreachable!(), - }, - fe::BinOperator::Div => match typ { - Type::Base(Base::Numeric(integer)) => { - expression! { [names::checked_div(integer)]([yul_left], [yul_right]) } - } - _ => unreachable!(), - }, - fe::BinOperator::BitAnd => expression! { and([yul_left], [yul_right]) }, - fe::BinOperator::BitOr => expression! { or([yul_left], [yul_right]) }, - fe::BinOperator::BitXor => expression! { xor([yul_left], [yul_right]) }, - fe::BinOperator::LShift => match typ { - Type::Base(Base::Numeric(integer)) => math_operations::adjust_numeric_size( - integer, - expression! { shl([yul_right], [math_operations::adjust_numeric_size(integer, yul_left)]) }, - ), - _ => unreachable!(), - }, - fe::BinOperator::RShift => match typ.is_signed_integer() { - true => expression! { sar([yul_right], [yul_left]) }, - false => expression! { shr([yul_right], [yul_left]) }, - }, - fe::BinOperator::Mod => match typ { - Type::Base(Base::Numeric(integer)) => { - expression! { [names::checked_mod(integer)]([yul_left], [yul_right]) } - } - _ => unreachable!(), - }, - fe::BinOperator::Pow => match typ { - Type::Base(Base::Numeric(integer)) => { - expression! { [names::checked_exp(integer)]([yul_left], [yul_right]) } - } - _ => unreachable!(), - }, - }; - } - - unreachable!() -} - -pub fn expr_unary_operation(context: &mut FnContext, exp: &Node) -> yul::Expression { - if let fe::Expr::UnaryOperation { op, operand } = &exp.kind { - let yul_operand = expr(context, operand); - - let typ = &context.expression_attributes(operand).typ; - - return match &op.kind { - fe::UnaryOperator::USub => { - match typ { - Type::Base(Base::Numeric(integer)) => { - if let fe::Expr::Num(_) = &operand.kind { - // Literals are checked at compile time (e.g. -128) so there's no point - // in adding a runtime check. - let zero = literal_expression! {0}; - expression! { sub([zero], [yul_operand]) } - } else { - expression! { [names::checked_neg(integer)]([yul_operand]) } - } - } - _ => unreachable!(), - } - } - fe::UnaryOperator::Not => expression! { iszero([yul_operand]) }, - fe::UnaryOperator::Invert => match typ { - Type::Base(Base::Numeric(integer)) => { - math_operations::adjust_numeric_size(integer, expression! { not([yul_operand])}) - } - _ => unreachable!(), - }, - }; - } - - unreachable!() -} - -/// Retrieves the String value of a name expression. -pub fn expr_name_string(exp: &Node) -> SmolStr { - if let fe::Expr::Name(name) = &exp.kind { - return name.clone(); - } - - unreachable!() -} - -fn expr_name(exp: &Node) -> yul::Expression { - let name = expr_name_string(exp); - - identifier_expression! { [names::var_name(&name)] } -} - -fn expr_num(exp: &Node) -> yul::Expression { - if let fe::Expr::Num(num) = &exp.kind { - let literal = numeric::Literal::new(num); - let num = literal.parse::().expect("Invalid numeric literal"); - let num = if matches!(literal.radix(), numeric::Radix::Decimal) { - format!("{}", num) - } else { - format!("{:#x}", num) - }; - - return literal_expression! {(num)}; - } - - unreachable!() -} - -fn expr_bool(exp: &Node) -> yul::Expression { - if let fe::Expr::Bool(val) = &exp.kind { - return literal_expression! {(val)}; - } - - unreachable!() -} - -fn expr_str(exp: &Node) -> yul::Expression { - if let fe::Expr::Str(content) = &exp.kind { - let string_identifier = format!(r#""{}""#, keccak::full(content.as_bytes())); - - let offset = expression! { dataoffset([literal_expression! { (string_identifier) }]) }; - let size = expression! { datasize([literal_expression! { (string_identifier) }]) }; - - return expression! {load_data_string([offset], [size])}; - } - - unreachable!() -} - -fn expr_subscript(context: &mut FnContext, exp: &Node) -> yul::Expression { - if let fe::Expr::Subscript { - value: value_node, - index: index_node, - } = &exp.kind - { - let value = expr(context, value_node); - let index = expr(context, index_node); - - let value_attributes = context.expression_attributes(value_node); - - return match &value_attributes.typ { - Type::Map(_) => data_operations::keyed_map(value, index), - Type::Array(array) => data_operations::indexed_array(array.clone(), value, index), - _ => panic!("invalid attributes"), - }; - } - - unreachable!() -} - -fn expr_attribute(context: &mut FnContext, exp: &Node) -> yul::Expression { - let (target, field) = match &exp.kind { - fe::Expr::Attribute { value, attr } => (value, attr), - _ => unreachable!(), - }; - - let target_attrs = context.expression_attributes(target).clone(); - - match &target_attrs.typ { - Type::Contract(_) => { - // TODO: verify that this is caught by analyzer - unreachable!("only `self` contract fields can be accessed for now") - } - Type::SelfContract(_) => { - let exp_attrs = context.expression_attributes(exp); - let nonce = match exp_attrs.location { - Location::Storage { nonce: Some(nonce) } => nonce, - _ => unreachable!("expected contract `self` field to be in storage and have nonce"), - }; - match exp_attrs.typ { - Type::Map(_) => literal_expression! { (nonce) }, - _ => nonce_to_ptr(nonce), - } - } - Type::Struct(struct_) => { - // struct `self` is handled like any other struct value, - // and keeps the name `self` in the generated yul. - let target = expr(context, target); - struct_operations::get_attribute( - context.db, - struct_.id, - &field.kind, - target, - target_attrs.location, - ) - } - _ => panic!("invalid type for field access: {:?}", &target_attrs.typ), - } -} - -/// Converts a storage nonce into a pointer based on the keccak256 hash -/// -/// Pointers created here have the last byte set to zero. This is to ensure that -/// our byte pointer sits at the start of a word (32 | `ptr` ). -pub fn nonce_to_ptr(nonce: usize) -> yul::Expression { - // set the last byte to `0x00` to ensure our pointer sits at the start of a word - let ptr = format!( - "0x{}", - keccak::partial_right_padded(nonce.to_string().as_bytes(), 31) - ); - literal_expression! { (ptr) } -} diff --git a/crates/yulgen/src/mappers/functions.rs b/crates/yulgen/src/mappers/functions.rs deleted file mode 100644 index dbd41e0af2..0000000000 --- a/crates/yulgen/src/mappers/functions.rs +++ /dev/null @@ -1,252 +0,0 @@ -use crate::constants::PANIC_FAILED_ASSERTION; -use crate::context::FnContext; -use crate::mappers::{assignments, declarations, expressions}; -use crate::names; -use crate::operations::data as data_operations; -use crate::operations::revert as revert_operations; -use crate::types::{AbiType, AsAbiType, EvmSized}; -use fe_analyzer::context::{CallType, ExpressionAttributes}; -use fe_analyzer::namespace::types::{Base, Type}; -use fe_parser::ast as fe; -use fe_parser::node::Node; -use if_chain::if_chain; -use yultsur::*; - -pub fn multiple_func_stmt( - context: &mut FnContext, - statements: &[Node], -) -> Vec { - statements - .iter() - .map(|statement| func_stmt(context, statement)) - .collect() -} - -fn func_stmt(context: &mut FnContext, stmt: &Node) -> yul::Statement { - match &stmt.kind { - fe::FuncStmt::Return { .. } => func_return(context, stmt), - fe::FuncStmt::VarDecl { .. } => declarations::var_decl(context, stmt), - fe::FuncStmt::ConstantDecl { .. } => declarations::const_decl(context, stmt), - fe::FuncStmt::Assign { .. } => assignments::assign(context, stmt), - fe::FuncStmt::Emit { .. } => emit(context, stmt), - fe::FuncStmt::AugAssign { .. } => panic!("AugAssign should be lowered"), - fe::FuncStmt::For { .. } => for_loop(context, stmt), - fe::FuncStmt::While { .. } => while_loop(context, stmt), - fe::FuncStmt::If { .. } => if_statement(context, stmt), - fe::FuncStmt::Unsafe(body) => { - let yul_body = multiple_func_stmt(context, body); - block_statement! { - [yul_body...] - } - } - fe::FuncStmt::Assert { .. } => assert(context, stmt), - fe::FuncStmt::Expr { .. } => expr(context, stmt), - fe::FuncStmt::Pass => statement! { pop(0) }, - fe::FuncStmt::Break => break_statement(context, stmt), - fe::FuncStmt::Continue => continue_statement(context, stmt), - fe::FuncStmt::Revert { .. } => revert(context, stmt), - } -} - -fn for_loop(context: &mut FnContext, stmt: &Node) -> yul::Statement { - if let fe::FuncStmt::For { target, iter, body } = &stmt.kind { - let iterator = expressions::expr(context, iter); - let target_var = names::var_name(&target.kind); - let yul_body = multiple_func_stmt(context, body); - return if let ExpressionAttributes { - typ: Type::Array(array), - .. - } = context.expression_attributes(iter) - { - let size = literal_expression! { (array.size) }; - let inner_size = literal_expression! { (array.inner.size()) }; - block_statement! { - (for {(let i := 0)} (lt(i, [size])) {(i := add(i, 1))} - { - // Below yul statement to load values from memory to `target_var`. - (let [target_var] := [expression! { mloadn([expression! { add([iterator], (mul(i, [inner_size.clone()]))) }], [inner_size]) }]) - [yul_body...] - }) - } - } else { - panic!("invalid iter expression") - }; - } - unreachable!() -} - -fn if_statement(context: &mut FnContext, stmt: &Node) -> yul::Statement { - if let fe::FuncStmt::If { - test, - body, - or_else, - } = &stmt.kind - { - let yul_test = expressions::expr(context, test); - let yul_body = multiple_func_stmt(context, body); - let yul_or_else = multiple_func_stmt(context, or_else); - - return switch! { - switch ([yul_test]) - (case 1 {[yul_body...]}) - (case 0 {[yul_or_else...]}) - }; - } - - unreachable!() -} - -fn expr(context: &mut FnContext, stmt: &Node) -> yul::Statement { - if let fe::FuncStmt::Expr { value } = &stmt.kind { - let expr = expressions::expr(context, value); - - // Yul's evm functions that don't return a value *really* don't return a value, - // unlike fe functions with unit return type, which currently return 0 when - // compiled to yul. - if_chain! { - if let fe::Expr::Call { func, .. } = &value.kind; - if let CallType::Intrinsic(intrinsic) = context.call_type(func); - if intrinsic.return_type() == Base::Unit; - then { - yul::Statement::Expression(expr) - } else { - statement! { pop([expr])} - } - } - } else { - unreachable!() - } -} - -fn revert(context: &mut FnContext, stmt: &Node) -> yul::Statement { - if let fe::FuncStmt::Revert { error } = &stmt.kind { - if let Some(error_expr) = error { - let error_attributes = context.expression_attributes(error_expr).clone(); - - if let Type::Struct(struct_) = &error_attributes.typ { - revert_operations::revert( - &struct_.name, - &struct_.as_abi_type(context.adb), - expressions::expr(context, error_expr), - ) - } else { - panic!("trying to revert with non-struct expression") - } - } else { - statement! { revert(0, 0) } - } - } else { - unreachable!() - } -} - -fn emit(context: &mut FnContext, stmt: &Node) -> yul::Statement { - if let fe::FuncStmt::Emit { args, .. } = &stmt.kind { - let event_values = args - .kind - .iter() - // the `ctx` parameter is not logged - .skip(1) - .map(|arg| expressions::expr(context, &arg.kind.value)) - .collect(); - - let event = context.emitted_event(stmt); - let event_fields: Vec<(AbiType, bool)> = event - .fields - .iter() - .map(|field| { - ( - field - .typ - .clone() - .expect("event field type error") - .as_abi_type(context.adb), - field.is_indexed, - ) - }) - .collect(); - return data_operations::emit_event(&event.name, &event_fields, event_values); - } - - unreachable!() -} - -fn assert(context: &mut FnContext, stmt: &Node) -> yul::Statement { - if let fe::FuncStmt::Assert { test, msg } = &stmt.kind { - let test = expressions::expr(context, test); - match msg { - Some(val) => { - let msg = expressions::expr(context, val); - let msg_attributes = context.expression_attributes(val).clone(); - - if let Type::String(string) = msg_attributes.typ { - let abi_type = string.as_abi_type(context.adb); - statement! { - if (iszero([test])) { - [revert_operations::error_revert(&abi_type, msg)] - } - } - } else { - unreachable!() - } - } - None => { - statement! { - if (iszero([test])) { - [revert_operations::panic_revert(PANIC_FAILED_ASSERTION)] - } - } - } - } - } else { - unreachable!() - } -} - -fn break_statement(_context: &mut FnContext, stmt: &Node) -> yul::Statement { - if let fe::FuncStmt::Break {} = &stmt.kind { - return statement! { break }; - } - - unreachable!() -} - -fn continue_statement(_context: &mut FnContext, stmt: &Node) -> yul::Statement { - if let fe::FuncStmt::Continue {} = &stmt.kind { - return statement! { continue }; - } - - unreachable!() -} - -fn func_return(context: &mut FnContext, stmt: &Node) -> yul::Statement { - if let fe::FuncStmt::Return { value } = &stmt.kind { - let value = value - .as_ref() - .expect("valueless return made it to Yul codegen"); - let value = expressions::expr(context, value); - - block_statement! { - (return_val := [value]) - (leave) - } - } else { - unreachable!() - } -} - -fn while_loop(context: &mut FnContext, stmt: &Node) -> yul::Statement { - if let fe::FuncStmt::While { test, body } = &stmt.kind { - let test = expressions::expr(context, test); - let yul_body = multiple_func_stmt(context, body); - - return block_statement! { - (for {} ([test]) {} - { - [yul_body...] - }) - }; - } - - unreachable!() -} diff --git a/crates/yulgen/src/mappers/mod.rs b/crates/yulgen/src/mappers/mod.rs deleted file mode 100644 index a706b5003b..0000000000 --- a/crates/yulgen/src/mappers/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod assignments; -mod declarations; -mod expressions; -pub mod functions; -pub mod module; diff --git a/crates/yulgen/src/mappers/module.rs b/crates/yulgen/src/mappers/module.rs deleted file mode 100644 index 119b9e1b94..0000000000 --- a/crates/yulgen/src/mappers/module.rs +++ /dev/null @@ -1,23 +0,0 @@ -use crate::{ModuleId, YulgenDb}; -use std::collections::HashMap; -use yultsur::yul; - -pub type YulContracts = HashMap; - -/// Builds a vector of Yul contracts from a Fe module. -pub fn module(db: &dyn YulgenDb, module: ModuleId) -> YulContracts { - module - .all_contracts(db.upcast()) - .iter() - .fold(YulContracts::new(), |mut contracts, id| { - let yul_contract = db.contract_object(*id); - - if contracts - .insert(id.name(db.upcast()).to_string(), yul_contract) - .is_some() - { - panic!("duplicate contract definition"); - } - contracts - }) -} diff --git a/crates/yulgen/src/names/abi.rs b/crates/yulgen/src/names/abi.rs deleted file mode 100644 index 58fb160718..0000000000 --- a/crates/yulgen/src/names/abi.rs +++ /dev/null @@ -1,148 +0,0 @@ -use crate::types::{AbiDecodeLocation, AbiType}; -use yultsur::*; - -/// Generates an ABI encoding function name for a given set of types. -pub fn encode(_types: &[AbiType]) -> yul::Identifier { - let name = format!("abi_encode_{}", types(_types)); - - identifier! { (name) } -} - -pub fn decode_data(_types: &[AbiType], location: AbiDecodeLocation) -> yul::Identifier { - let name = format!( - "abi_decode_data_{}_{}", - types(_types), - decode_location(location) - ); - - identifier! { (name) } -} - -pub fn decode_component(typ: &AbiType, location: AbiDecodeLocation) -> yul::Identifier { - match typ { - AbiType::Address => decode_component_address(location), - AbiType::Bool => decode_component_bool(location), - AbiType::Uint { size } => decode_component_uint(*size, location), - AbiType::Int { size } => decode_component_int(*size, location), - AbiType::StaticArray { inner, size } => { - decode_component_static_array(inner, *size, location) - } - AbiType::Tuple { components: elems } => decode_component_tuple(elems, location), - AbiType::String { max_size } => decode_component_string(*max_size, location), - AbiType::Bytes { size } => decode_component_bytes(*size, location), - } -} - -pub fn decode_component_uint(size: usize, location: AbiDecodeLocation) -> yul::Identifier { - let name = format!( - "abi_decode_component_uint{}_{}", - size * 8, - decode_location(location) - ); - - identifier! { (name) } -} - -pub fn decode_component_int(size: usize, location: AbiDecodeLocation) -> yul::Identifier { - let name = format!( - "abi_decode_component_int{}_{}", - size * 8, - decode_location(location) - ); - - identifier! { (name) } -} - -pub fn decode_component_bool(location: AbiDecodeLocation) -> yul::Identifier { - let name = format!("abi_decode_component_bool_{}", decode_location(location)); - - identifier! { (name) } -} - -pub fn decode_component_address(location: AbiDecodeLocation) -> yul::Identifier { - let name = format!("abi_decode_component_address_{}", decode_location(location)); - - identifier! { (name) } -} - -pub fn decode_component_static_array( - inner: &AbiType, - size: usize, - location: AbiDecodeLocation, -) -> yul::Identifier { - let name = format!( - "abi_decode_component_static_array_{}_{}_{}", - size, - typ(inner), - decode_location(location) - ); - - identifier! { (name) } -} - -pub fn decode_component_tuple( - components: &[AbiType], - location: AbiDecodeLocation, -) -> yul::Identifier { - let name = format!( - "abi_decode_component_tuple_{}_{}", - types(components), - decode_location(location) - ); - - identifier! { (name) } -} - -pub fn decode_component_bytes(size: usize, location: AbiDecodeLocation) -> yul::Identifier { - let name = format!( - "abi_decode_component_bytes_{}_{}", - size, - decode_location(location) - ); - - identifier! { (name) } -} - -pub fn decode_component_string(max_size: usize, location: AbiDecodeLocation) -> yul::Identifier { - let name = format!( - "abi_decode_component_string_{}_{}", - max_size, - decode_location(location) - ); - - identifier! { (name) } -} - -pub fn vals(prefix: &str, n: usize) -> (Vec, Vec) { - (0..n) - .into_iter() - .map(|index| { - let name = format!("{}_val_{}", prefix, index); - (identifier! { (name) }, identifier_expression! { (name) }) - }) - .unzip() -} - -pub fn typ(_typ: &AbiType) -> String { - match _typ { - AbiType::Uint { size } => format!("uint{}", size * 8), - AbiType::Int { size } => format!("int{}", size * 8), - AbiType::Bool => "bool".to_string(), - AbiType::Address => "address".to_string(), - AbiType::StaticArray { size, inner } => format!("array_{}_{}", size, typ(inner)), - AbiType::Tuple { components } => format!("tuple_{}", types(components)), - AbiType::String { max_size } => format!("string_{}", max_size), - AbiType::Bytes { size } => format!("bytes_{}", size), - } -} - -pub fn types(types: &[AbiType]) -> String { - types.iter().map(typ).collect::>().join("_") -} - -fn decode_location(location: AbiDecodeLocation) -> &'static str { - match location { - AbiDecodeLocation::Memory => "mem", - AbiDecodeLocation::Calldata => "calldata", - } -} diff --git a/crates/yulgen/src/names/mod.rs b/crates/yulgen/src/names/mod.rs deleted file mode 100644 index 38218df04a..0000000000 --- a/crates/yulgen/src/names/mod.rs +++ /dev/null @@ -1,73 +0,0 @@ -use crate::names::abi as abi_names; -use crate::types::AbiType; -use fe_analyzer::namespace::types::Integer; -use yultsur::*; - -pub mod abi; - -/// Generate a function name to perform checked negation -pub fn checked_neg(size: &Integer) -> yul::Identifier { - identifier! {(format!("checked_neg_{}", size.as_ref().to_lowercase()))} -} - -/// Generate a function name to perform checked addition -pub fn checked_add(size: &Integer) -> yul::Identifier { - identifier! {(format!("checked_add_{}", size.as_ref().to_lowercase()))} -} - -/// Generate a function name to perform checked division -pub fn checked_div(size: &Integer) -> yul::Identifier { - let size: &str = if size.is_signed() { - size.as_ref() - } else { - "unsigned" - }; - identifier! {(format!("checked_div_{}", size.to_lowercase()))} -} - -/// Generate a function name to perform checked modulo -pub fn checked_mod(size: &Integer) -> yul::Identifier { - let sign: &str = if size.is_signed() { - "signed" - } else { - "unsigned" - }; - identifier! {(format!("checked_mod_{}", sign.to_lowercase()))} -} - -/// Generate a function name to perform checked exponentiation -pub fn checked_exp(size: &Integer) -> yul::Identifier { - identifier! {(format!("checked_exp_{}", size.as_ref().to_lowercase()))} -} - -/// Generate a function name to perform checked multiplication -pub fn checked_mul(size: &Integer) -> yul::Identifier { - identifier! {(format!("checked_mul_{}", size.as_ref().to_lowercase()))} -} - -/// Generate a function name to perform checked subtraction -pub fn checked_sub(size: &Integer) -> yul::Identifier { - let size: &str = if size.is_signed() { - size.as_ref() - } else { - "unsigned" - }; - identifier! {(format!("checked_sub_{}", size.to_lowercase()))} -} - -/// Generate a function name to adjust the size of the integer -pub fn adjust_numeric_size(size: &Integer) -> yul::Identifier { - identifier! {(format!("adjust_numeric_{}", size.as_ref().to_lowercase()))} -} - -/// Generate a safe variable name for a user defined function -pub fn var_name(name: &str) -> yul::Identifier { - identifier! { (format!("${}", name)) } -} - -/// Generates a revert function name for a given name and types -pub fn revert(name: &str, typ: &AbiType) -> yul::Identifier { - let name = format!("revert_with_{}_{}", name, abi_names::typ(typ)); - - identifier! { (name) } -} diff --git a/crates/yulgen/src/operations/abi.rs b/crates/yulgen/src/operations/abi.rs deleted file mode 100644 index a652e3ac6f..0000000000 --- a/crates/yulgen/src/operations/abi.rs +++ /dev/null @@ -1,118 +0,0 @@ -use crate::names::abi as abi_names; -use crate::operations::data as data_operations; -use crate::types::{AbiDecodeLocation, AbiType}; -use crate::utils::ceil_32; -use yultsur::*; - -/// The size of an encoding known at compile-time. -/// -/// The expressions should only be given literal values. -pub enum EncodingSize { - Exact(yul::Expression), - Bounded { - min: yul::Expression, - max: yul::Expression, - }, -} - -/// Returns an expression that encodes the given values and returns a pointer to -/// the encoding. -pub fn encode(types: &[AbiType], vals: Vec) -> yul::Expression { - let func_name = abi_names::encode(types); - expression! { [func_name]([vals...]) } -} - -/// Returns an expression that gives size of the encoded values. -/// -/// It will sum up the sizes known at compile-time with the sizes known during runtime. -pub fn encoding_size(types: &[AbiType], vals: &[yul::Expression]) -> yul::Expression { - let mut head_size = 0; - let mut known_data_size = 0; - let mut unknown_data_size = vec![]; - - let typed_vals = types.iter().zip(vals); - - for (typ, val) in typed_vals { - head_size += typ.head_size(); - match typ { - AbiType::String { .. } => { - known_data_size += 32; - unknown_data_size.push(expression! { ceil32((mload([val.clone()]))) }) - } - AbiType::Bytes { size } => known_data_size += 32 + ceil_32(*size), - _ => {} - } - } - - let static_size = literal_expression! { (head_size + known_data_size) }; - expression! { add([static_size], [data_operations::sum(unknown_data_size)]) } -} - -/// Returns an expression that gives the size of the encoding's head. -pub fn encoding_head_size(types: &[AbiType]) -> yul::Expression { - literal_expression! { (types.iter().map(AbiType::head_size).sum::()) } -} - -/// Returns the known-at-compile-time encoding size. -pub fn encoding_known_size(types: &[AbiType]) -> EncodingSize { - let (min, max) = types.iter().fold((0, 0), |(mut min, mut max), typ| { - min += typ.head_size(); - max += typ.head_size(); - - match typ { - AbiType::String { max_size } => { - min += 32; - max += ceil_32(*max_size) + 32; - } - AbiType::Bytes { size } => { - let size = ceil_32(*size) + 32; - min += size; - max += size; - } - _ => {} - } - - (min, max) - }); - - if min == max { - EncodingSize::Exact(literal_expression! { (min) }) - } else { - EncodingSize::Bounded { - min: literal_expression! { (min) }, - max: literal_expression! { (max) }, - } - } -} - -/// Decode a segment of memory and return each decoded component as separate values. -pub fn decode_data( - types: &[AbiType], - head_start: yul::Expression, - data_end: yul::Expression, - location: AbiDecodeLocation, -) -> yul::Expression { - let func_name = abi_names::decode_data(types, location); - expression! { [func_name]([head_start], [data_end]) } -} - -/// Decode a single component. -pub fn decode_component( - typ: &AbiType, - head_start: yul::Expression, - offset: yul::Expression, - location: AbiDecodeLocation, -) -> yul::Expression { - let func_name = abi_names::decode_component(typ, location); - expression! { [func_name]([head_start], [offset]) } -} - -/// Unpack each value into a newly allocated segment of memory. -pub fn unpack( - ptr: yul::Expression, - array_size: yul::Expression, - inner_data_size: yul::Expression, - signed: yul::Expression, -) -> yul::Statement { - statement! { abi_unpack([ptr], [array_size], [inner_data_size], [signed]) } -} diff --git a/crates/yulgen/src/operations/contracts.rs b/crates/yulgen/src/operations/contracts.rs deleted file mode 100644 index 9be8b4abcd..0000000000 --- a/crates/yulgen/src/operations/contracts.rs +++ /dev/null @@ -1,27 +0,0 @@ -use yultsur::*; - -/// Executes the `create2` operation for a given contract with the given value -/// and salt. -pub fn create2(name: &str, value: yul::Expression, salt: yul::Expression) -> yul::Expression { - let name = literal_expression! { (format!("\"{}\"", name)) }; - expression! { - contract_create2( - (dataoffset([name.clone()])), - (datasize([name])), - [value], - [salt] - ) - } -} - -/// Executes the `create` operation for a given contract with the given value. -pub fn create(name: &str, value: yul::Expression) -> yul::Expression { - let name = literal_expression! { (format!("\"{}\"", name)) }; - expression! { - contract_create( - (dataoffset([name.clone()])), - (datasize([name])), - [value] - ) - } -} diff --git a/crates/yulgen/src/operations/data.rs b/crates/yulgen/src/operations/data.rs deleted file mode 100644 index eb87d42927..0000000000 --- a/crates/yulgen/src/operations/data.rs +++ /dev/null @@ -1,148 +0,0 @@ -use crate::operations::abi as abi_operations; -use crate::types::{AbiType, EvmSized}; -use fe_analyzer::namespace::types::Array; -use yultsur::*; - -/// Loads a value of the given type from storage. -pub fn sload(typ: Box, sptr: yul::Expression) -> yul::Expression { - let size = literal_expression! { (typ.size()) }; - expression! { bytes_sloadn([sptr], [size]) } -} - -/// Stores a value of the given type in storage. -pub fn sstore( - typ: Box, - sptr: yul::Expression, - value: yul::Expression, -) -> yul::Statement { - let size = literal_expression! { (typ.size()) }; - statement! { bytes_sstoren([sptr], [size], [value]) } -} - -/// Loads a value of the given type from memory. -pub fn mload(typ: Box, mptr: yul::Expression) -> yul::Expression { - let size = literal_expression! { (typ.size()) }; - expression! { mloadn([mptr], [size]) } -} - -/// Stores a value of the given type in memory. -pub fn mstore( - typ: Box, - mptr: yul::Expression, - value: yul::Expression, -) -> yul::Statement { - let size = literal_expression! { (typ.size()) }; - statement! { mstoren([mptr], [size], [value]) } -} - -/// Copies a segment of memory into storage. -pub fn mcopys( - typ: Box, - sptr: yul::Expression, - mptr: yul::Expression, -) -> yul::Statement { - let size = literal_expression! { (typ.size()) }; - let word_ptr = expression! { div([sptr], 32) }; - statement! { mcopys([mptr], [word_ptr], [size]) } -} - -/// Copies a segment of storage into memory. -/// -/// Returns the address of the data in memory. -pub fn scopym(typ: Box, sptr: yul::Expression) -> yul::Expression { - let size = literal_expression! { (typ.size()) }; - let word_ptr = expression! { div([sptr], 32) }; - expression! { scopym([word_ptr], [size]) } -} - -/// Copies a segment of storage to another segment of storage. -pub fn scopys( - typ: Box, - dest_ptr: yul::Expression, - origin_ptr: yul::Expression, -) -> yul::Statement { - let size = literal_expression! { (typ.size()) }; - let origin_word = expression! { div([origin_ptr], 32) }; - let dest_word = expression! { div([dest_ptr], 32) }; - statement! { scopys([origin_word], [dest_word], [size]) } -} - -/// Copies a segment of memory to another segment of memory. -pub fn mcopym(typ: Box, ptr: yul::Expression) -> yul::Expression { - let size = literal_expression! { (typ.size()) }; - expression! { mcopym([ptr], [size]) } -} - -/// Logs an event. -pub fn emit_event( - event_name: &str, - fields: &[(AbiType, bool)], // is_idx - vals: Vec, -) -> yul::Statement { - // (abi_type, is_idx) - let topics = { - // the first topic is the hash of the event signature - let topic_0 = fe_abi::utils::event_topic( - event_name, - &fields - .iter() - .map(|(abi_type, _)| abi_type.selector_name()) - .collect::>(), - ); - let mut topics = vec![literal_expression! { (topic_0) }]; - - // Types will be relevant here when we implement indexed array values. - // For now we assume these are all base type values and therefore do not need to - // be hashed. - let mut idx_field_vals = fields - .iter() - .zip(vals.iter()) - .filter_map(|((_field_type, is_idx), val)| is_idx.then(|| val.clone())) - .collect::>(); - - topics.append(&mut idx_field_vals); - topics - }; - - let (non_idx_field_types, non_idx_field_vals): (Vec<_>, Vec<_>) = fields - .iter() - .zip(vals.iter()) - .filter_map(|((abi_type, is_idx), val)| (!is_idx).then(|| (abi_type.clone(), val.clone()))) - .unzip(); - - let encoding_size = abi_operations::encoding_size(&non_idx_field_types, &non_idx_field_vals); - let encoding = abi_operations::encode(&non_idx_field_types, non_idx_field_vals); - - let log_func = identifier! { (format!("log{}", topics.len())) }; - - return statement! { [log_func]([encoding], [encoding_size], [topics...]) }; -} - -/// Sums a list of expressions using nested add operations. -pub fn sum(vals: Vec) -> yul::Expression { - if vals.is_empty() { - return expression! { 0 }; - } - - vals.into_iter() - .reduce(|val1, val2| expression! { add([val1], [val2]) }) - .unwrap() -} - -/// Hashes the storage nonce of a map with a key to determine the value's -/// location in storage. -pub fn keyed_map(map: yul::Expression, key: yul::Expression) -> yul::Expression { - expression! { map_value_ptr([map], [key]) } -} - -/// Finds the location of an array element base on the element size, element -/// index, and array location. -pub fn indexed_array( - typ: Array, - array: yul::Expression, - index: yul::Expression, -) -> yul::Expression { - let inner_size = literal_expression! { (typ.inner.size()) }; - let array_length = literal_expression! { (typ.size) }; - expression! { get_array_item([array], [array_length], [index], [inner_size] ) } -} diff --git a/crates/yulgen/src/operations/math.rs b/crates/yulgen/src/operations/math.rs deleted file mode 100644 index 8f9f7419f3..0000000000 --- a/crates/yulgen/src/operations/math.rs +++ /dev/null @@ -1,13 +0,0 @@ -use fe_analyzer::namespace::types::Integer; -use yultsur::*; - -use crate::names; - -/// Loads a value of the given type from storage. -pub fn adjust_numeric_size(integer: &Integer, value: yul::Expression) -> yul::Expression { - if integer.size() < 32 { - expression! { [names::adjust_numeric_size(integer)]([value]) } - } else { - value - } -} diff --git a/crates/yulgen/src/operations/mod.rs b/crates/yulgen/src/operations/mod.rs deleted file mode 100644 index 6479271855..0000000000 --- a/crates/yulgen/src/operations/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub mod abi; -pub mod contracts; -pub mod data; -pub mod math; -pub mod revert; -pub mod structs; diff --git a/crates/yulgen/src/operations/revert.rs b/crates/yulgen/src/operations/revert.rs deleted file mode 100644 index 1e6fe8e8b4..0000000000 --- a/crates/yulgen/src/operations/revert.rs +++ /dev/null @@ -1,32 +0,0 @@ -use crate::names; -use crate::types::AbiType; -use yultsur::*; - -/// Revert with an error message -pub fn error_revert(typ: &AbiType, msg: yul::Expression) -> yul::Statement { - revert("Error", typ, msg) -} - -/// Revert with an error code -pub fn error_revert_numeric(val: usize) -> yul::Statement { - revert( - "Error", - &AbiType::Uint { size: 32 }, - literal_expression! { (val) }, - ) -} - -/// Revert with a panic code -pub fn panic_revert(val: usize) -> yul::Statement { - revert( - "Panic", - &AbiType::Uint { size: 32 }, - literal_expression! { (val) }, - ) -} - -/// Revert with a name and a single value -pub fn revert(name: &str, typ: &AbiType, val: yul::Expression) -> yul::Statement { - let func_name = names::revert(name, typ); - statement! { [func_name]([val]) } -} diff --git a/crates/yulgen/src/operations/structs.rs b/crates/yulgen/src/operations/structs.rs deleted file mode 100644 index 3d8eb0f73f..0000000000 --- a/crates/yulgen/src/operations/structs.rs +++ /dev/null @@ -1,88 +0,0 @@ -use crate::operations::data as data_operations; -use crate::types::AsEvmSized; -use crate::YulgenDb; -use fe_analyzer::{context::Location, namespace::items::StructId, namespace::types::Struct}; -use yultsur::*; - -pub fn init(db: &dyn YulgenDb, struct_: StructId, params: Vec) -> yul::Expression { - let function_name = identifier! { (db.struct_init_name(struct_)) }; - expression! { [function_name]([params...]) } -} - -pub fn get_attribute( - db: &dyn YulgenDb, - struct_: StructId, - field_name: &str, - val: yul::Expression, - location: Location, -) -> yul::Expression { - let function_name = identifier! { (db.struct_getter_name(struct_, field_name.into())) }; - if struct_.is_base_type(db.upcast(), field_name) { - expression! { [function_name]([val]) } - } else if matches!(location, Location::Storage { .. }) { - let index = struct_ - .field_index(db.upcast(), field_name) - .expect("unknown field"); - let index = literal_expression! { (index)}; - // non-base type fields in storage use the same compile time reference scheme as map types - data_operations::keyed_map(val, index) - } else { - expression! { mload(([function_name]([val]))) } - } -} - -pub fn copy_to_storage( - db: &dyn YulgenDb, - struct_: &Struct, - target: yul::Expression, - value: yul::Expression, -) -> yul::Statement { - let yul_body = [ - // We first copy the entire struct from memory to storage *including* the memory references. - // The memory references are a pointless waste of storage space and are never read or written to. - // We'll fix that later. - vec![data_operations::mcopys( - Box::new(struct_.clone()), - target.clone(), - value.clone(), - )], - struct_ - .id - .fields(db.upcast()) - .values() - .filter_map(|field| { - if field.is_base_type(db.upcast()) { - None - } else { - let typ = field.typ(db.upcast()).expect("not a fixed size"); - let field_to = get_attribute( - db, - struct_.id, - field.name(db.upcast()).as_str(), - target.clone(), - Location::Storage { nonce: None }, - ); - let field_from = get_attribute( - db, - struct_.id, - field.name(db.upcast()).as_str(), - value.clone(), - Location::Memory, - ); - // We have to go over all struct fields to copy the actual data of the reference type fields - // to their respective location in storage because all we copied so far were useless memory references - Some(data_operations::mcopys( - typ.as_evm_sized(), - field_to, - field_from, - )) - } - }) - .collect(), - ] - .concat(); - - return block_statement! { - [yul_body...] - }; -} diff --git a/crates/yulgen/src/runtime/abi_dispatcher.rs b/crates/yulgen/src/runtime/abi_dispatcher.rs deleted file mode 100644 index d472668f58..0000000000 --- a/crates/yulgen/src/runtime/abi_dispatcher.rs +++ /dev/null @@ -1,126 +0,0 @@ -use crate::names::abi as abi_names; -use crate::operations::abi as abi_operations; -use crate::types::{to_abi_selector_names, AbiDecodeLocation, AbiType}; -use fe_abi::utils as abi_utils; -use smol_str::SmolStr; -use yultsur::*; - -/// Builds a switch statement that dispatches calls to the contract and wraps it -/// in a `$$__call__` function. -pub fn dispatcher( - functions: &[( - SmolStr, - SmolStr, - impl AsRef<[AbiType]>, - Option, - bool, - )], -) -> yul::Statement { - let arms = functions - .iter() - .map(|(name, qname, params, ret, expects_ctx)| { - dispatch_arm(name, qname, params.as_ref(), ret, *expects_ctx) - }) - .collect::>(); - - let dispatcher = if arms.is_empty() { - statement! { return(0, 0) } - } else { - switch! { - switch (cloadn(0, 4)) - [arms...] - (default { (return(0, 0)) }) - } - }; - - let call_fn_ident = identifier! { ("$$__call__") }; - - function_definition! { - function [call_fn_ident]() { - [dispatcher] - } - } -} - -fn dispatch_arm( - bare_name: &str, - qualified_name: &str, - params: &[AbiType], - return_type: &Option, - expects_ctx: bool, -) -> yul::Case { - let selector = selector(bare_name, params); - - let (param_idents, param_exprs) = abi_names::vals("call", params.len()); - - // If there are no params, we create an empty vector. - let maybe_decode_params = if params.is_empty() { - statements! {} - } else { - let decode_expr = abi_operations::decode_data( - params, - expression! { 4 }, - expression! { calldatasize() }, - AbiDecodeLocation::Calldata, - ); - statements! { (let [param_idents...] := [decode_expr]) } - }; - - // If the function returns a unit value, we call the function and return - // nothing. Otherwise, we encode the value and return it. - let call_and_maybe_encode_return = { - let name = identifier! { (qualified_name) }; - // we pass in a `0` for the expected `Context` argument - let call = if expects_ctx { - expression! { [name](0, [param_exprs...]) } - } else { - expression! { [name]([param_exprs...]) } - }; - if let Some(return_type) = return_type { - let return_expr = expressions! { return_val }; - let encoding_size = abi_operations::encoding_size(&[return_type.clone()], &return_expr); - let encode_expr = abi_operations::encode(&[return_type.clone()], return_expr); - statements! { - (let return_val := [call]) - (let encoding_start := [encode_expr]) - (let encoding_size := [encoding_size]) - (return(encoding_start, encoding_size)) - } - } else { - statements! { - (pop([call])) - (return(0, 0)) - } - } - }; - - case! { - case [selector] { - [maybe_decode_params...] - [call_and_maybe_encode_return...] - } - } -} - -fn selector(name: &str, params: &[AbiType]) -> yul::Literal { - literal! { (abi_utils::func_selector(name, &to_abi_selector_names(params))) } -} - -#[cfg(test)] -mod tests { - use crate::runtime::abi_dispatcher::selector; - use crate::types::AbiType; - - #[test] - fn test_selector_literal_basic() { - assert_eq!(selector("foo", &[]).to_string(), String::from("0xc2985578"),) - } - - #[test] - fn test_selector_literal() { - assert_eq!( - selector("bar", &[AbiType::Uint { size: 32 }]).to_string(), - String::from("0x0423a132"), - ) - } -} diff --git a/crates/yulgen/src/runtime/functions/abi.rs b/crates/yulgen/src/runtime/functions/abi.rs deleted file mode 100644 index 9ad5312d99..0000000000 --- a/crates/yulgen/src/runtime/functions/abi.rs +++ /dev/null @@ -1,625 +0,0 @@ -use crate::constants::ERROR_INVALID_ABI_DATA; -use crate::names::abi as abi_names; -use crate::operations::abi as abi_operations; -use crate::operations::abi::EncodingSize; -use crate::operations::revert as revert_operations; -use crate::types::{AbiDecodeLocation, AbiType}; -use crate::utils::ceil_32; -use yultsur::*; - -/// Return all abi runtime functions -pub fn all() -> Vec { - vec![ - unpack(), - is_left_padded(), - is_right_padded(), - // This is needed for `revert_with_Panic_uint256`, which is included in the std batch of - // revert functions. - // It will be removed in https://github.com/ethereum/fe/pull/478 along with all other - // batches of runtime functions. - encode(&[AbiType::Uint { size: 32 }]), - ] -} - -/// Returns a yul function that decodes a block of abi-encoded data into the -/// specified [`AbiType`] components, eg `abi_decode_data_u256_Foo_u8_calldata`. -/// The decoding of each component is handled by a separate function, eg. -/// `abi_decode_component_uint32_mem`; these component decoding functions -/// are also included in the returned `Vec`. -pub fn decode_functions(types: &[AbiType], location: AbiDecodeLocation) -> Vec { - let mut component_fns: Vec<_> = types.iter().fold(vec![], |mut funcs, typ| { - funcs.push(decode_component(typ, location)); - match typ { - AbiType::Tuple { components } => { - for ctyp in components { - funcs.push(decode_component(ctyp, location)) - } - } - AbiType::StaticArray { inner, .. } => funcs.push(decode_component(inner, location)), - _ => {} - }; - funcs - }); - - component_fns.sort(); - component_fns.dedup(); - component_fns.push(decode_data(types, location)); - component_fns -} - -/// Creates a function that decodes ABI encoded data. -fn decode_data(types: &[AbiType], location: AbiDecodeLocation) -> yul::Statement { - #[derive(Clone)] - struct IdentExpr { - ident: yul::Identifier, - expr: yul::Expression, - } - - impl From for IdentExpr { - fn from(string: String) -> Self { - IdentExpr { - ident: identifier! { (string) }, - expr: identifier_expression! { (string) }, - } - } - } - - #[derive(Clone)] - struct DataOffsets { - start: IdentExpr, - end: IdentExpr, - } - - #[derive(Clone)] - struct DecodeVal { - typ: AbiType, - return_val: IdentExpr, - decoded_val: IdentExpr, - head_offset: IdentExpr, - data_offsets: Option, - } - - let func_name = abi_names::decode_data(types, location); - - let vals: Vec = types - .iter() - .enumerate() - .map(|(i, typ)| { - let data_offsets = if typ.has_data() { - Some(DataOffsets { - start: format!("data_start_offset_{}", i).into(), - end: format!("data_end_offset_{}", i).into(), - }) - } else { - None - }; - - DecodeVal { - typ: typ.clone(), - return_val: format!("return_val_{}", i).into(), - decoded_val: format!("decoded_val_{}", i).into(), - head_offset: format!("head_offset_{}", i).into(), - data_offsets, - } - }) - .collect(); - - let size_check = match abi_operations::encoding_known_size(types) { - EncodingSize::Exact(size) => statements! { - (let encoding_size := sub(data_end, head_start)) - (if (iszero((eq(encoding_size, [size])))) { - [revert_with_invalid_abi_data()] - }) - }, - EncodingSize::Bounded { min, max } => statements! { - (let encoding_size := sub(data_end, head_start)) - (if (or( - (lt(encoding_size, [min])), - (gt(encoding_size, [max])) - )) { - [revert_with_invalid_abi_data()] - }) - }, - }; - - let return_val_idents: Vec<_> = vals - .clone() - .into_iter() - .map(|val| val.return_val.ident) - .collect(); - - // Create declaration statements for each offset. - let head_offset_decls: Vec<_> = { - let mut curr_offset = 0; - vals.clone() - .into_iter() - .map(|val| { - let head_offset = literal_expression! { (curr_offset) }; - let head_offset_decl = statement! { let [val.head_offset.ident] := [head_offset] }; - curr_offset += val.typ.head_size(); - head_offset_decl - }) - .collect() - }; - - // Call the component decoding functions for each offset value. - let decode_stmts: Vec<_> = vals - .clone() - .into_iter() - .map(|val| { - let target_idents = if let Some(data_offsets) = val.data_offsets { - identifiers! { - [val.decoded_val.ident] - [data_offsets.start.ident] - [data_offsets.end.ident] - } - } else { - identifiers! { [val.decoded_val.ident] } - }; - - let decode_expr = abi_operations::decode_component( - &val.typ, - expression! { head_start }, - val.head_offset.expr, - location, - ); - statement! { let [target_idents...] := [decode_expr] } - }) - .collect(); - - let encoding_head_size = abi_operations::encoding_head_size(types); - let data_offset_checks: Vec<_> = { - let (mut start_offset_exprs, mut end_offset_exprs): (Vec<_>, Vec<_>) = vals - .clone() - .into_iter() - .filter_map(|val| { - val.data_offsets - .map(|data_offsets| (data_offsets.start.expr, data_offsets.end.expr)) - }) - .unzip(); - - start_offset_exprs.push(expression! { encoding_size }); - end_offset_exprs.insert(0, encoding_head_size); - - start_offset_exprs - .into_iter() - .zip(end_offset_exprs) - .map(|(start_offset, end_offset)| { - statement! { - if (iszero((eq([start_offset], [end_offset])))) { [revert_with_invalid_abi_data()] } - } - }) - .collect() - }; - - let return_assignments: Vec<_> = vals - .into_iter() - .map(|val| statement! { [val.return_val.ident] := [val.decoded_val.expr] }) - .collect(); - - function_definition! { - function [func_name](head_start, data_end) -> [return_val_idents...] { - [size_check...] - [head_offset_decls...] - [decode_stmts...] - [data_offset_checks...] - [return_assignments...] - } - } -} - -/// Creates a function that decodes a single component in ABI encoded data. -pub fn decode_component(typ: &AbiType, location: AbiDecodeLocation) -> yul::Statement { - match typ { - AbiType::StaticArray { inner, size } => { - decode_component_static_array(inner, *size, location) - } - AbiType::Tuple { components: elems } => decode_component_tuple(elems, location), - AbiType::Uint { size } => decode_component_uint(*size, location), - AbiType::Int { size } => decode_component_int(*size, location), - AbiType::Bool => decode_component_bool(location), - AbiType::Address => decode_component_address(location), - AbiType::String { max_size } => decode_component_string(*max_size, location), - AbiType::Bytes { size } => decode_component_bytes(*size, location), - } -} - -pub fn decode_component_uint(size: usize, location: AbiDecodeLocation) -> yul::Statement { - let func_name = abi_names::decode_component_uint(size, location); - let decode_expr = load_word(expression! { ptr }, location); - let check_padding = check_left_padding( - literal_expression! { ((32 - size) * 8) }, - expression! { return_val }, - ); - - function_definition! { - function [func_name](head_start, offset) -> return_val { - (let ptr := add(head_start, offset)) - (return_val := [decode_expr]) - [check_padding] - } - } -} - -pub fn decode_component_int(size: usize, location: AbiDecodeLocation) -> yul::Statement { - let func_name = abi_names::decode_component_int(size, location); - let decode_expr = load_word(expression! { ptr }, location); - let check_size = check_int_size(size, expression! { return_val }); - - function_definition! { - function [func_name](head_start, offset) -> return_val { - (let ptr := add(head_start, offset)) - (return_val := [decode_expr]) - [check_size] - } - } -} - -pub fn decode_component_bool(location: AbiDecodeLocation) -> yul::Statement { - let func_name = abi_names::decode_component_bool(location); - let decode_expr = load_word(expression! { ptr }, location); - let check_padding = check_left_padding(expression! { 255 }, expression! { return_val }); - - function_definition! { - function [func_name](head_start, offset) -> return_val { - (let ptr := add(head_start, offset)) - (return_val := [decode_expr]) - [check_padding] - } - } -} - -pub fn decode_component_address(location: AbiDecodeLocation) -> yul::Statement { - let func_name = abi_names::decode_component_address(location); - let decode_expr = load_word(expression! { ptr }, location); - let check_padding = check_left_padding(expression! { 96 }, expression! { return_val }); - - function_definition! { - function [func_name](head_start, offset) -> return_val { - (let ptr := add(head_start, offset)) - (return_val := [decode_expr]) - [check_padding] - } - } -} - -pub fn decode_component_static_array( - inner: &AbiType, - array_size: usize, - location: AbiDecodeLocation, -) -> yul::Statement { - let func_name = abi_names::decode_component_static_array(inner, array_size, location); - let array_size = literal_expression! { (array_size) }; - let inner_packed_size = literal_expression! { (inner.packed_size()) }; - let decode_inner_expr = abi_operations::decode_component( - inner, - expression! { head_start }, - expression! { inner_offset }, - location, - ); - - function_definition! { - function [func_name](head_start, offset) -> return_val { - (let ptr := add(head_start, offset)) - (return_val := avail()) - (for {(let i := 0)} (lt(i, [array_size])) {(i := add(i, 1))} - { - (let inner_offset := add(offset, (mul(i, 32)))) - (let decoded_val := [decode_inner_expr]) - (pop((alloc_mstoren(decoded_val, [inner_packed_size])))) - }) - } - } -} - -pub fn decode_component_tuple(elems: &[AbiType], location: AbiDecodeLocation) -> yul::Statement { - let func_name = abi_names::decode_component_tuple(elems, location); - let decode_stmts: Vec<_> = elems - .iter() - .enumerate() - .map(|(index, component)| { - let decode_component_expr = abi_operations::decode_component( - component, - expression! { head_start }, - expression! { component_offset }, - location, - ); - let index = literal_expression! { (index) }; - - block_statement! { - (let component_offset := add(offset, (mul(32, [index])))) - (let decoded_val := [decode_component_expr]) - (pop((alloc_mstoren(decoded_val, 32)))) - } - }) - .collect(); - - function_definition! { - function [func_name](head_start, offset) -> return_val { - (let ptr := add(head_start, offset)) - (return_val := avail()) - [decode_stmts...] - } - } -} - -pub fn decode_component_bytes(size: usize, location: AbiDecodeLocation) -> yul::Statement { - let func_name = abi_names::decode_component_bytes(size, location); - let size = literal_expression! { (size) }; - - function_definition! { - function [func_name](head_start, head_offset) -> return_val, data_start_offset, data_end_offset { - (let head_ptr := add(head_start, head_offset)) - (data_start_offset := [load_word(expression! { head_ptr }, location)]) - (let data_start := add(head_start, data_start_offset)) - (let bytes_size := [load_word(expression! { data_start }, location)]) - (if (iszero((eq(bytes_size, [size])))) { [revert_with_invalid_abi_data()] } ) - (let data_size := add(bytes_size, 32)) - (let padded_data_size := ceil32(data_size)) - (data_end_offset := add(data_start_offset, padded_data_size)) - (let end_word := [load_word(expression! { sub((add(head_start, data_end_offset)), 32) }, location)]) - (let padding_size_bits := mul((sub(padded_data_size, data_size)), 8)) - [check_right_padding( - expression! { padding_size_bits }, - expression! { end_word } - )] - (return_val := [copy_data( - // We do not copy the dynamic size value like we do with strings, so we add 32 bytes - // to the start and subtract 32 bytes from the size being copied. - expression! { add(data_start, 32) }, - expression! { sub(data_size, 32) }, - location - )]) - } - } -} - -pub fn decode_component_string(max_size: usize, location: AbiDecodeLocation) -> yul::Statement { - let func_name = abi_names::decode_component_string(max_size, location); - let max_size = literal_expression! { (max_size) }; - - function_definition! { - function [func_name](head_start, head_offset) -> return_val, data_start_offset, data_end_offset { - (let head_ptr := add(head_start, head_offset)) - (data_start_offset := [load_word(expression! { head_ptr }, location)]) - (let data_start := add(head_start, data_start_offset)) - (let string_size := [load_word(expression! { data_start }, location)]) - (if (gt(string_size, [max_size])) { [revert_with_invalid_abi_data()] }) - (let data_size := add(string_size, 32)) - (let padded_data_size := ceil32(data_size)) - (data_end_offset := add(data_start_offset, padded_data_size)) - (let end_word := [load_word(expression! { sub((add(head_start, data_end_offset)), 32) }, location)]) - (let padding_size_bits := mul((sub(padded_data_size, data_size)), 8)) - [check_right_padding( - expression! { padding_size_bits }, - expression! { end_word } - )] - (return_val := [copy_data( - expression! { data_start }, - expression! { data_size }, - location - )]) - } - } -} - -/// Returns 0 if the value is not padded on the left with zeros. -/// -/// `size_bits` refers to the size of the padding in bits. -pub fn is_left_padded() -> yul::Statement { - function_definition! { - function is_left_padded(size_bits, val) -> return_val { - (let bits_shifted := sub(256, size_bits)) - (let shifted_val := shr(bits_shifted, val)) - (return_val := iszero(shifted_val)) - } - } -} -/// Returns 0 if the value is not padded on the right with zeros. -/// -/// `size_bits` refers to the size of the padding in bits. -pub fn is_right_padded() -> yul::Statement { - function_definition! { - function is_right_padded(size_bits, val) -> return_val { - (let bits_shifted := sub(256, size_bits)) - (let shifted_val := shl(bits_shifted, val)) - (return_val := iszero(shifted_val)) - } - } -} - -fn revert_with_invalid_abi_data() -> yul::Statement { - revert_operations::error_revert_numeric(ERROR_INVALID_ABI_DATA) -} - -/// Reverts if the value is not left padded with the given number of bits. -fn check_left_padding(size_bits: yul::Expression, val: yul::Expression) -> yul::Statement { - statement! { - if (iszero((is_left_padded([size_bits], [val])))) { - [revert_with_invalid_abi_data()] - } - } -} - -/// Reverts if the value is not right padded with the given number of bits. -fn check_right_padding(size_bits: yul::Expression, val: yul::Expression) -> yul::Statement { - statement! { - if (iszero((is_right_padded([size_bits], [val])))) { - [revert_with_invalid_abi_data()] - } - } -} - -/// Reverts if the integer value does not fit within the given number of bytes. -fn check_int_size(size: usize, val: yul::Expression) -> yul::Statement { - // the bits to the left of this size should be either all 0s or all 1s - let size_bits = literal_expression! { (size * 8 - 1) }; - let is_all_0s = expression! { iszero((shr([size_bits.clone()], [val.clone()]))) }; - let is_all_1s = expression! { iszero((shr([size_bits], (not([val]))))) }; - let is_all_0s_or_1s = expression! { or([is_all_0s], [is_all_1s]) }; - - statement! { - if (iszero([is_all_0s_or_1s])) { - [revert_with_invalid_abi_data()] - } - } -} - -fn load_word(ptr: yul::Expression, location: AbiDecodeLocation) -> yul::Expression { - match location { - AbiDecodeLocation::Memory => expression! { mload([ptr]) }, - AbiDecodeLocation::Calldata => expression! { calldataload([ptr]) }, - } -} - -fn copy_data( - ptr: yul::Expression, - size: yul::Expression, - location: AbiDecodeLocation, -) -> yul::Expression { - match location { - AbiDecodeLocation::Memory => expression! { mcopym([ptr], [size]) }, - AbiDecodeLocation::Calldata => expression! { ccopym([ptr], [size]) }, - } -} - -/// Adds padding to array elements following the ABI standard. -pub fn unpack() -> yul::Statement { - function_definition! { - function abi_unpack(mptr, array_size, inner_data_size, signed) { - (for {(let i := 0)} (lt(i, array_size)) {(i := add(i, 1))} - { - (let val_ptr := add(mptr, (mul(i, inner_data_size)))) - (let val := mloadn(val_ptr, inner_data_size)) - (if signed { - (val := signextend((sub(inner_data_size, 1)), val)) - }) - (pop((alloc_mstoren(val, 32)))) - }) - } - } -} - -// fn abi_unpack(mptr: u256, array_size: u256) { -// for i in 0..array_size { -// let val_ptr = mptr + i * T::size_in_bytes() -// let val = unpack_int(val_ptr) -// pop(alloc_mstoren(val, 32)) -// } -// } -// fn unpack_int(mptr: u256) -> T { -// let val: T = mloadn(val_ptr, T::size_in_bytes()) -// if T::is_signed() { -// signextend(val, T::size_in_bytes()) -// } -// } - -/// Generates an encoding function for any set of type parameters. -pub fn encode(types: &[AbiType]) -> yul::Statement { - let func_name = abi_names::encode(types); - - // Create names for each of the values we're encoding. - let (param_idents, param_exprs) = abi_names::vals("encode", types.len()); - let typed_params: Vec<_> = types.iter().zip(param_exprs).collect(); - - // Encode the head section of each component. - let head_encode_stmts: Vec<_> = typed_params - .clone() - .into_iter() - .map(|(typ, param)| match typ { - AbiType::StaticArray { inner, size } => encode_static_array(param, inner, *size), - AbiType::Tuple { components } => encode_tuple(param, components), - AbiType::Uint { .. } | AbiType::Int { .. } | AbiType::Bool | AbiType::Address => { - encode_uint(param) - } - AbiType::String { .. } => encode_string_head(param), - AbiType::Bytes { size } => encode_bytes_head(*size), - }) - .collect(); - - // Encode the data section of each component with dynamically-sized data. - let data_encode_stmts: Vec<_> = typed_params - .into_iter() - .filter_map(|(typ, param)| match typ { - AbiType::String { .. } => Some(encode_string_data(param)), - AbiType::Bytes { size } => Some(encode_bytes_data(*size, param)), - _ => None, - }) - .collect(); - - function_definition! { - function [func_name]([param_idents...]) -> return_ptr { - // Set the return to the available memory address. - (return_ptr := avail()) - // The data section begins at the end of the head. - (let data_offset := [abi_operations::encoding_head_size(types)]) - [head_encode_stmts...] - [data_encode_stmts...] - } - } -} - -fn encode_tuple(val: yul::Expression, components: &[AbiType]) -> yul::Statement { - let tuple_size = components.len() * 32; - let tuple_size = literal_expression! { (tuple_size) }; - statement! { pop((mcopym([val], [tuple_size]))) } -} - -fn encode_uint(val: yul::Expression) -> yul::Statement { - block_statement! { - (let ptr := alloc(32)) - (mstore(ptr, [val])) - } -} - -fn encode_string_head(ptr: yul::Expression) -> yul::Statement { - block_statement! { - (mstore((alloc(32)), data_offset)) - (let num_bytes := mload([ptr])) - (let data_size := ceil32((add(32, num_bytes)))) - (data_offset := add(data_offset, data_size)) - } -} - -fn encode_string_data(ptr: yul::Expression) -> yul::Statement { - block_statement! { - (let num_bytes := mload([ptr.clone()])) - (let data_size := add(32, num_bytes)) - (let remainder := sub((ceil32(data_size)), data_size)) - (pop((mcopym([ptr], data_size)))) - (pop((alloc(remainder)))) - } -} - -fn encode_bytes_head(size: usize) -> yul::Statement { - block_statement! { - (mstore((alloc(32)), data_offset)) - (let data_size := [literal_expression! { (ceil_32(32 + size)) }]) - (data_offset := add(data_offset, data_size)) - } -} - -fn encode_bytes_data(size: usize, ptr: yul::Expression) -> yul::Statement { - block_statement! { - (let num_bytes := [literal_expression! { (size) }]) - (let remainder := [literal_expression! { (ceil_32(size) - size) }]) - (mstore((alloc(32)), num_bytes)) - (pop((mcopym([ptr], num_bytes)))) - (pop((alloc(remainder)))) - } -} - -fn encode_static_array(val: yul::Expression, inner: &AbiType, size: usize) -> yul::Statement { - let signed = if matches!(inner, AbiType::Int { .. }) { - expression! { 1 } - } else { - expression! { 0 } - }; - - abi_operations::unpack( - val, - literal_expression! { (size) }, - literal_expression! { (inner.packed_size()) }, - signed, - ) -} diff --git a/crates/yulgen/src/runtime/functions/contracts.rs b/crates/yulgen/src/runtime/functions/contracts.rs deleted file mode 100644 index 13d180fe10..0000000000 --- a/crates/yulgen/src/runtime/functions/contracts.rs +++ /dev/null @@ -1,28 +0,0 @@ -use yultsur::*; - -/// Return all contacts runtime functions -pub fn all() -> Vec { - vec![create2(), create()] -} - -/// Function that executes the `create2` operation. -pub fn create2() -> yul::Statement { - function_definition! { - function contract_create2(data_ptr, data_size, value, salt) -> return_address { - (let mptr := alloc(data_size)) - (datacopy(mptr, data_ptr, data_size)) - (return_address := create2(value, mptr, data_size, salt)) - } - } -} - -/// Function that executes the `create` operation. -pub fn create() -> yul::Statement { - function_definition! { - function contract_create(data_ptr, data_size, value) -> return_address { - (let mptr := alloc(data_size)) - (datacopy(mptr, data_ptr, data_size)) - (return_address := create(value, mptr, data_size)) - } - } -} diff --git a/crates/yulgen/src/runtime/functions/data.rs b/crates/yulgen/src/runtime/functions/data.rs deleted file mode 100644 index 929f88a012..0000000000 --- a/crates/yulgen/src/runtime/functions/data.rs +++ /dev/null @@ -1,385 +0,0 @@ -use crate::constants::PANIC_OUT_OF_BOUNDS; -use crate::operations::revert as revert_operations; - -use yultsur::*; - -/// Return all data runtime functions -pub fn all() -> Vec { - vec![ - alloc_mstoren(), - alloc(), - avail(), - bytes_mcopys(), - bytes_scopym(), - bytes_scopys(), - bytes_sloadn(), - bytes_sstoren(), - ccopym(), - ceil32(), - cloadn(), - free(), - get_array_item(), - load_data_string(), - map_value_ptr(), - mcopym(), - mcopys(), - mloadn(), - mstoren(), - scopym(), - scopys(), - set_zero(), - sloadn(), - sstoren(), - ] -} - -/// Returns the highest available pointer. -pub fn avail() -> yul::Statement { - function_definition! { - function avail() -> ptr { - (ptr := mload(0x00)) - (if (eq(ptr, 0x00)) { (ptr := 0x20) }) - } - } -} - -/// Allocate a given number of bytes. -pub fn alloc() -> yul::Statement { - function_definition! { - function alloc(size) -> ptr { - (ptr := mload(0x00)) - (if (eq(ptr, 0x00)) { (ptr := 0x20) }) - (mstore(0x00, (add(ptr, size)))) - } - } -} - -/// Set the highest available pointer. -pub fn free() -> yul::Statement { - function_definition! { - function free(ptr) { - (mstore(0x00, ptr)) - } - } -} - -/// Set the given segment of the value (defined in bits) to zero. -pub fn set_zero() -> yul::Statement { - function_definition! { - function set_zero(start_bit, end_bit, val) -> result { - (let left_shift_dist := sub(256, start_bit)) - (let right_shift_dist := end_bit) - // shift left then right to zero out the desired bits - (let left := shl(left_shift_dist, (shr(left_shift_dist, val)))) - // shift right then left to zero out the desired bits - (let right := shr(right_shift_dist, (shl(right_shift_dist, val)))) - // combine the left and right side - // the segment defined by `start_bit` and `end_bit` will be zero - (result := or(left, right)) - } - } -} - -/// Rounds a 256 bit value up to the nearest multiple of 32. -pub fn ceil32() -> yul::Statement { - function_definition! { - function ceil32(n) -> return_val { - (return_val := mul((div((add(n, 31)), 32)), 32)) - } - } -} - -/// Copy calldata to a newly allocated segment of memory. -pub fn ccopym() -> yul::Statement { - function_definition! { - function ccopym(cptr, size) -> mptr { - (mptr := alloc(size)) - (calldatacopy(mptr, cptr, size)) - } - } -} - -/// Copy memory to a given segment of storage. -/// -/// The storage pointer addresses a word. -pub fn mcopys() -> yul::Statement { - function_definition! { - function mcopys(mptr, sptr, size) { - (let mptr_offset := 0) - (let sptr_offset := 0) - (for { } (lt((add(mptr_offset, 32)), size)) { } - { - (let _mptr := add(mptr, mptr_offset)) - (let _sptr := add(sptr, sptr_offset)) - (sstore(_sptr, (mload(_mptr)))) - (mptr_offset := add(mptr_offset, 32)) - (sptr_offset := add(sptr_offset, 1)) - }) - - (let rem := sub(size, mptr_offset)) - (if (gt(rem, 0)) { - (let _mptr := add(mptr, mptr_offset)) - (let _sptr := add(sptr, sptr_offset)) - (let zeroed_val := set_zero((mul(rem, 8)), 256, (mload(_mptr)))) - (sstore(_sptr, zeroed_val)) - }) - } - } -} - -/// Copy storage to a newly allocated segment of memory. -/// -/// The storage pointer addresses a word. -pub fn scopym() -> yul::Statement { - function_definition! { - function scopym(sptr, size) -> mptr { - (mptr := alloc(size)) - (let mptr_offset := 0) - (let sptr_offset := 0) - (for { } (lt((add(mptr_offset, 32)), size)) { } - { - (let _mptr := add(mptr, mptr_offset)) - (let _sptr := add(sptr, sptr_offset)) - (mstore(_mptr, (sload(_sptr)))) - (mptr_offset := add(mptr_offset, 32)) - (sptr_offset := add(sptr_offset, 1)) - }) - - (let rem := sub(size, mptr_offset)) - (if (gt(rem, 0)) { - (let _mptr := add(mptr, mptr_offset)) - (let _sptr := add(sptr, sptr_offset)) - (mstoren(_mptr, rem, (sloadn(_sptr, 0, rem)))) - }) - } - } -} - -/// Copies a segment of storage to another segment of storage. -/// -/// The storage pointers address words. -pub fn scopys() -> yul::Statement { - function_definition! { - function scopys(ptr1, ptr2, size) { - (let word_size := div((add(size, 31)), 32)) - (let offset := 0) - (for { } (lt((add(offset, 1)), size)) { } - { - (let _ptr1 := add(ptr1, offset)) - (let _ptr2 := add(ptr2, offset)) - (sstore(_ptr2, (sload(_ptr1)))) - (offset := add(offset, 1)) - }) - } - } -} - -/// Copies a segment of memory to another segment of memory. -pub fn mcopym() -> yul::Statement { - function_definition! { - function mcopym(ptr1, size) -> ptr2 { - (ptr2 := alloc(size)) - (let offset := 0) - (for { } (lt((add(offset, 32)), size)) { } - { - (let _ptr1 := add(ptr1, offset)) - (let _ptr2 := add(ptr2, offset)) - (mstore(_ptr2, (mload(_ptr1)))) - (offset := add(offset, 32)) - }) - - (let rem := sub(size, offset)) - (if (gt(rem, 0)) { - (let _ptr1 := add(ptr1, offset)) - (let _ptr2 := add(ptr2, offset)) - (mstoren(_ptr2, rem, (mloadn(_ptr1, rem)))) - }) - } - } -} - -/// Read a value of n bytes from memory at the given address. -pub fn mloadn() -> yul::Statement { - function_definition! { - function mloadn(ptr, size) -> val { - (val := shr((sub(256, (mul(8, size)))), (mload(ptr)))) - } - } -} - -/// Read a value of n bytes at the given word address and bytes offset. -/// -/// (offset + size) must be less that 32, otherwise it enters undefined -/// behavior. -pub fn sloadn() -> yul::Statement { - function_definition! { - function sloadn(word_ptr, bytes_offset, bytes_size) -> val { - (let bits_offset := mul(bytes_offset, 8)) - (let bits_size := mul(bytes_size, 8)) - (let bits_padding := sub(256, bits_size)) - - (let word := sload(word_ptr)) - (let word_shl := shl(bits_offset, word)) - (val := shr(bits_padding, word_shl)) - } - } -} - -/// Read a value of n bytes from calldata at the given address. -pub fn cloadn() -> yul::Statement { - function_definition! { - function cloadn(ptr, size) -> val { - (val := (shr((sub(256, (mul(8, size)))), (calldataload(ptr))))) - } - } -} - -/// Stores a value in memory, only modifying the given size (0 < size <= 32). -pub fn mstoren() -> yul::Statement { - function_definition! { - function mstoren(ptr, size, val) { - (let size_bits := mul(8, size)) - (let left := shl((sub(256, size_bits)), val)) - (let right := shr(size_bits, (mload((add(ptr, size)))))) - (mstore(ptr, (or(left, right)))) - } - } -} - -/// Stores a value in storage, only modifying the given size (0 < size <= 32). -pub fn sstoren() -> yul::Statement { - function_definition! { - function sstoren(word_ptr, bytes_offset, bytes_size, val) { - (let bits_offset := mul(bytes_offset, 8)) - (let bits_size := mul(bytes_size, 8)) - - (let old_word := sload(word_ptr)) - // zero out the section to be modified - (let zeroed_word := set_zero( - bits_offset, - (add(bits_offset, bits_size)), - old_word - )) - // get the number of bits we need to shift to get the value to the correct offset - (let left_shift_dist := sub((sub(256, bits_size)), bits_offset)) - (let offset_val := shl(left_shift_dist, val)) - // use or to place the new value in the zeroed out section - (let new_word := or(zeroed_word, offset_val)) - (sstore(word_ptr, new_word)) - } - } -} - -/// Copy memory to a given segment of storage. -/// -/// The storage pointer addresses a byte. -pub fn bytes_mcopys() -> yul::Statement { - function_definition! { - function bytes_mcopys(mptr, sptr, size) { - (let word_ptr := div(sptr, 32)) - (mcopys(mptr, word_ptr, size)) - } - } -} - -/// Copy storage to a newly allocated segment of memory. -/// -/// The storage pointer addresses a byte. -pub fn bytes_scopym() -> yul::Statement { - function_definition! { - function bytes_scopym(sptr, size) -> mptr { - (let word_ptr := div(sptr, 32)) - (mptr := scopym(word_ptr, size)) - } - } -} - -/// Copies a segment of storage to another segment of storage. -/// -/// The storage pointers address bytes. -pub fn bytes_scopys() -> yul::Statement { - function_definition! { - function bytes_scopys(ptr1, ptr2, size) { - (let word_ptr1 := div(ptr1, 32)) - (let word_ptr2 := div(ptr2, 32)) - (scopys(word_ptr1, word_ptr2, size)) - } - } -} - -/// Read a value of n bytes at the given byte address. -/// -/// The value must not span multiple words. -pub fn bytes_sloadn() -> yul::Statement { - function_definition! { - function bytes_sloadn(sptr, size) -> val { - (let word_ptr := div(sptr, 32)) - (let bytes_offset := mod(sptr, 32)) - (val := sloadn(word_ptr, bytes_offset, size)) - } - } -} - -/// Stores a value in storage at the given address, only modifying a segment of -/// the given size. -/// -/// The modified segment must not span multiple words. -pub fn bytes_sstoren() -> yul::Statement { - function_definition! { - function bytes_sstoren(sptr, size, val) { - (let word_ptr := div(sptr, 32)) - (let bytes_offset := mod(sptr, 32)) - (sstoren(word_ptr, bytes_offset, size, val)) - } - } -} - -/// Stores a value in a newly allocated memory segment. -pub fn alloc_mstoren() -> yul::Statement { - function_definition! { - function alloc_mstoren(val, size) -> ptr { - (ptr := alloc(size)) - (mstoren(ptr, size, val)) - } - } -} - -/// Derives the byte address of a value corresponding to a map key. -/// -/// The address is always divisible by 32, so it points to a word. -pub fn map_value_ptr() -> yul::Statement { - function_definition! { - function map_value_ptr(a, b) -> return_val { - (let ptr := alloc(64)) - (mstore(ptr, a)) - (mstore((add(ptr, 32)), b)) - (let hash := keccak256(ptr, 64)) - (return_val := set_zero(248, 256, hash)) - } - } -} - -/// Load a static string from data into a newly allocated segment of memory. -pub fn load_data_string() -> yul::Statement { - function_definition! { - function load_data_string(code_ptr, size) -> mptr { - (mptr := alloc(32)) - (mstore(mptr, size)) - (let content_ptr := alloc(size)) - (datacopy(content_ptr, code_ptr, size)) - } - } -} - -/// Returns a pointer to the array item at the requested index. -/// Reverts with a panic if the index is out of bounds. -pub fn get_array_item() -> yul::Statement { - function_definition! { - function get_array_item(array_ptr, array_length, index, inner_size) -> ptr { - (if (iszero((lt(index, array_length)))) { - [revert_operations::panic_revert(PANIC_OUT_OF_BOUNDS)] - } ) - (ptr := add(array_ptr, (mul(index, inner_size)))) - } - } -} diff --git a/crates/yulgen/src/runtime/functions/math.rs b/crates/yulgen/src/runtime/functions/math.rs deleted file mode 100644 index d384165ae4..0000000000 --- a/crates/yulgen/src/runtime/functions/math.rs +++ /dev/null @@ -1,519 +0,0 @@ -use crate::constants::{numeric_min_max, PANIC_DIV_OR_MOD_BY_ZERO, PANIC_OVER_OR_UNDERFLOW}; -use crate::names; -use crate::operations::revert as revert_operations; -use fe_analyzer::namespace::types::Integer; -use yultsur::*; - -/// Return a vector of runtime functions for negations with over-/underflow -/// protection -pub fn checked_neg_fns() -> Vec { - vec![ - checked_neg_signed(Integer::I256), - checked_neg_signed(Integer::I128), - checked_neg_signed(Integer::I64), - checked_neg_signed(Integer::I32), - checked_neg_signed(Integer::I16), - checked_neg_signed(Integer::I8), - ] -} - -/// Return a vector of runtime functions for additions with over-/underflow -/// protection -pub fn checked_add_fns() -> Vec { - vec![ - checked_add_unsigned(Integer::U256), - checked_add_unsigned(Integer::U128), - checked_add_unsigned(Integer::U64), - checked_add_unsigned(Integer::U32), - checked_add_unsigned(Integer::U16), - checked_add_unsigned(Integer::U8), - checked_add_signed(Integer::I256), - checked_add_signed(Integer::I128), - checked_add_signed(Integer::I64), - checked_add_signed(Integer::I32), - checked_add_signed(Integer::I16), - checked_add_signed(Integer::I8), - ] -} - -/// Return a vector of runtime functions for divisions with -/// over-/underflow protection -pub fn checked_div_fns() -> Vec { - vec![ - checked_div_unsigned(), - checked_div_signed(Integer::I256), - checked_div_signed(Integer::I128), - checked_div_signed(Integer::I64), - checked_div_signed(Integer::I32), - checked_div_signed(Integer::I16), - checked_div_signed(Integer::I8), - ] -} - -/// Return a vector of runtime functions for exponentiation with over-/underflow -/// protection -pub fn checked_exp_fns() -> Vec { - vec![ - checked_exp_unsigned(), - checked_exp_signed(), - checked_exp_helper(), - _checked_exp_unsigned(Integer::U256), - _checked_exp_unsigned(Integer::U128), - _checked_exp_unsigned(Integer::U64), - _checked_exp_unsigned(Integer::U32), - _checked_exp_unsigned(Integer::U16), - _checked_exp_unsigned(Integer::U8), - _checked_exp_signed(Integer::I256), - _checked_exp_signed(Integer::I128), - _checked_exp_signed(Integer::I64), - _checked_exp_signed(Integer::I32), - _checked_exp_signed(Integer::I16), - _checked_exp_signed(Integer::I8), - ] -} - -/// Return a vector of runtime functions for checked modulo arithmetic -pub fn checked_mod_fns() -> Vec { - vec![checked_mod_unsigned(), checked_mod_signed()] -} - -/// Return a vector of runtime functions for multiplications with -/// over-/underflow protection -pub fn checked_mul_fns() -> Vec { - vec![ - checked_mul_unsigned(Integer::U256), - checked_mul_unsigned(Integer::U128), - checked_mul_unsigned(Integer::U64), - checked_mul_unsigned(Integer::U32), - checked_mul_unsigned(Integer::U16), - checked_mul_unsigned(Integer::U8), - checked_mul_signed(Integer::I256), - checked_mul_signed(Integer::I128), - checked_mul_signed(Integer::I64), - checked_mul_signed(Integer::I32), - checked_mul_signed(Integer::I16), - checked_mul_signed(Integer::I8), - ] -} - -/// Return a vector of runtime functions for subtraction with over-/underflow -/// protection -pub fn checked_sub_fns() -> Vec { - vec![ - checked_sub_unsigned(), - checked_sub_signed(Integer::I256), - checked_sub_signed(Integer::I128), - checked_sub_signed(Integer::I64), - checked_sub_signed(Integer::I32), - checked_sub_signed(Integer::I16), - checked_sub_signed(Integer::I8), - ] -} - -/// Return a vector of runtime functions to adjust numeric sizes -pub fn adjust_numeric_size_fns() -> Vec { - vec![ - adjust_numeric_unsigned(Integer::U128), - adjust_numeric_unsigned(Integer::U64), - adjust_numeric_unsigned(Integer::U32), - adjust_numeric_unsigned(Integer::U16), - adjust_numeric_unsigned(Integer::U8), - adjust_numeric_signed(Integer::I128, 15), - adjust_numeric_signed(Integer::I64, 7), - adjust_numeric_signed(Integer::I32, 3), - adjust_numeric_signed(Integer::I16, 1), - adjust_numeric_signed(Integer::I8, 0), - ] -} - -// Return all math runtime functions -pub fn all() -> Vec { - [ - checked_add_fns(), - checked_div_fns(), - checked_exp_fns(), - checked_mod_fns(), - checked_mul_fns(), - checked_sub_fns(), - checked_neg_fns(), - adjust_numeric_size_fns(), - ] - .concat() -} - -fn revert_with_over_or_under_flow() -> yul::Statement { - revert_operations::panic_revert(PANIC_OVER_OR_UNDERFLOW) -} - -fn revert_with_div_or_mod_by_zero() -> yul::Statement { - revert_operations::panic_revert(PANIC_DIV_OR_MOD_BY_ZERO) -} - -fn checked_neg_signed(size: Integer) -> yul::Statement { - if !size.is_signed() { - panic!("Expected signed integer") - } - let (min_value, _) = get_min_max(size); - let fn_name = names::checked_neg(&size); - function_definition! { - function [fn_name](val1) -> result { - // overflow for min_val - (if ((eq(val1, [min_value]))) { [revert_with_over_or_under_flow()] }) - (result := sub(0, val1)) - } - } -} - -fn checked_mod_unsigned() -> yul::Statement { - function_definition! { - function checked_mod_unsigned(val1, val2) -> result { - (if (iszero(val2)) { [revert_with_div_or_mod_by_zero()] }) - (result := mod(val1, val2)) - } - } -} - -fn checked_mod_signed() -> yul::Statement { - function_definition! { - function checked_mod_signed(val1, val2) -> result { - (if (iszero(val2)) { [revert_with_div_or_mod_by_zero()] }) - (result := smod(val1, val2)) - } - } -} - -fn checked_mul_unsigned(size: Integer) -> yul::Statement { - if size.is_signed() { - panic!("Expected unsigned integer") - } - let fn_name = names::checked_mul(&size); - let max_value = get_max(size); - - function_definition! { - function [fn_name](val1, val2) -> product { - // overflow, if val1 != 0 and val2 > (max_value / val1) - (if (and((iszero((iszero(val1)))), (gt(val2, (div([max_value], val1)))))) { [revert_with_over_or_under_flow()] }) - (product := mul(val1, val2)) - } - } -} - -fn checked_mul_signed(size: Integer) -> yul::Statement { - if !size.is_signed() { - panic!("Expected signed integer") - } - let fn_name = names::checked_mul(&size); - let (min_value, max_value) = get_min_max(size); - - function_definition! { - function [fn_name](val1, val2) -> product { - // overflow, if val1 > 0, val2 > 0 and val1 > (max_value / val2) - (if (and((and((sgt(val1, 0)), (sgt(val2, 0)))), (gt(val1, (div([max_value.clone()], val2)))))) { [revert_with_over_or_under_flow()] }) - // underflow, if val1 > 0, val2 < 0 and val2 < (min_value / val1) - (if (and((and((sgt(val1, 0)), (slt(val2, 0)))), (slt(val2, (sdiv([min_value.clone()], val1)))))) { [revert_with_over_or_under_flow()] }) - // underflow, if val1 < 0, val2 > 0 and val1 < (min_value / val2) - (if (and((and((slt(val1, 0)), (sgt(val2, 0)))), (slt(val1, (sdiv([min_value], val2)))))) { [revert_with_over_or_under_flow()] }) - // overflow, if val1 < 0, val2 < 0 and val1 < (max_value / val2) - (if (and((and((slt(val1, 0)), (slt(val2, 0)))), (slt(val1, (sdiv([max_value], val2)))))) { [revert_with_over_or_under_flow()] }) - (product := mul(val1, val2)) - } - } -} - -fn checked_add_unsigned(size: Integer) -> yul::Statement { - if size.is_signed() { - panic!("Expected unsigned integer") - } - let fn_name = names::checked_add(&size); - let max_value = get_max(size); - - function_definition! { - function [fn_name](val1, val2) -> sum { - // overflow, if val1 > (max_value - val2) - (if (gt(val1, (sub([max_value], val2)))) { [revert_with_over_or_under_flow()] }) - (sum := add(val1, val2)) - } - } -} - -fn checked_add_signed(size: Integer) -> yul::Statement { - if !size.is_signed() { - panic!("Expected signed integer") - } - let (min_value, max_value) = get_min_max(size); - let fn_name = names::checked_add(&size); - function_definition! { - function [fn_name](val1, val2) -> sum { - // overflow, if val1 >= 0 and val2 > (max_value - val1) - (if (and((iszero((slt(val1, 0)))), (sgt(val2, (sub([max_value], val1)))))) { [revert_with_over_or_under_flow()] }) - // underflow, if val1 < 0 and val2 < (min_val - val1) - (if (and((slt(val1, 0)), (slt(val2, (sub([min_value], val1)))))) { [revert_with_over_or_under_flow()] }) - (sum := add(val1, val2)) - } - } -} - -fn checked_div_unsigned() -> yul::Statement { - function_definition! { - function checked_div_unsigned(val1, val2) -> result { - (if (iszero(val2)) { [revert_with_div_or_mod_by_zero()] }) - (result := div(val1, val2)) - } - } -} - -fn checked_div_signed(size: Integer) -> yul::Statement { - if !size.is_signed() { - panic!("Expected signed integer") - } - let (min_value, _) = get_min_max(size); - let fn_name = names::checked_div(&size); - function_definition! { - function [fn_name](val1, val2) -> result { - (if (iszero(val2)) { [revert_with_div_or_mod_by_zero()] }) - - // overflow for min_val / -1 - (if (and( (eq(val1, [min_value])), (eq(val2, (sub(0, 1))))) ) { [revert_with_over_or_under_flow()] }) - (result := sdiv(val1, val2)) - } - } -} - -fn checked_sub_unsigned() -> yul::Statement { - function_definition! { - function checked_sub_unsigned(val1, val2) -> diff { - // underflow, if val2 > val1 - (if (lt(val1, val2)) { [revert_with_over_or_under_flow()] }) - (diff := sub(val1, val2)) - } - } -} - -fn checked_sub_signed(size: Integer) -> yul::Statement { - if !size.is_signed() { - panic!("Expected signed integer") - } - let fn_name = names::checked_sub(&size); - let (min_value, max_value) = get_min_max(size); - - function_definition! { - function [fn_name](val1, val2) -> diff { - // underflow, if val2 >= 0 and val1 < (min_value + val2) - (if (and((iszero((slt(val2, 0)))), (slt(val1, (add([min_value], val2)))))) { [revert_with_over_or_under_flow()] }) - // overflow, if val2 < 0 and val1 > (max_value + val2) - (if (and((slt(val2, 0)), (sgt(val1, (add([max_value], val2)))))) { [revert_with_over_or_under_flow()] }) - (diff := sub(val1, val2)) - } - } -} - -fn _checked_exp_signed(size: Integer) -> yul::Statement { - if !size.is_signed() { - panic!("Expected signed integer") - } - let (min_value, max_value) = get_min_max(size); - let fn_name = names::checked_exp(&size); - function_definition! { - function [fn_name](base, exponent) -> power { - (power := checked_exp_signed(base, exponent, [min_value], [max_value])) - } - } -} - -fn _checked_exp_unsigned(size: Integer) -> yul::Statement { - if size.is_signed() { - panic!("Expected unsigned integer") - } - let (_, max_value) = get_min_max(size); - let fn_name = names::checked_exp(&size); - function_definition! { - function [fn_name](base, exponent) -> power { - (power := checked_exp_unsigned(base, exponent, [max_value])) - } - } -} - -fn adjust_numeric_unsigned(size: Integer) -> yul::Statement { - if size.is_signed() { - panic!("Expected unsigned integer") - } - let max_value = get_max(size); - let fn_name = names::adjust_numeric_size(&size); - function_definition! { - function [fn_name](value) -> cleaned { - (cleaned := and(value, [max_value])) - } - } -} - -fn adjust_numeric_signed(size: Integer, base: usize) -> yul::Statement { - if !size.is_signed() { - panic!("Expected signed integer") - } - let fn_name = names::adjust_numeric_size(&size); - function_definition! { - function [fn_name](value) -> cleaned { - (cleaned := signextend([literal_expression! { (base) }], value)) - } - } -} - -fn checked_exp_helper() -> yul::Statement { - // TODO: Refactor once https://github.com/ethereum/fe/issues/314 is resolved - yul::Statement::FunctionDefinition(yul::FunctionDefinition { - name: identifier! { ("checked_exp_helper") }, - parameters: identifiers! { ("_power") ("_base") ("exponent") ("max")}, - returns: identifiers! { ("power") ("base")}, - block: block! { - (power := _power) - (base := _base) - (for {} (gt(exponent, 1)) {} - { - // overflow check for base * base - (if (gt(base, (div(max, base)))) { [revert_with_over_or_under_flow()] }) - (if (and(exponent, 1)) { - // No checks for power := mul(power, base) needed, because the check - // for base * base above is sufficient, since: - // |power| <= base (proof by induction) and thus: - // |power * base| <= base * base <= max <= |min| (for signed) - // (this is equally true for signed and unsigned exp) - (power := (mul(power, base))) - }) - (base := (mul(base, base))) - (exponent := shr(1, exponent)) - }) - }, - }) -} - -fn checked_exp_signed() -> yul::Statement { - // Refactor once https://github.com/ethereum/fe/issues/314 is resolved - let checked_exp_helper_call = yul::Statement::Assignment(yul::Assignment { - identifiers: identifiers! { ("power") ("base")}, - expression: expression! { checked_exp_helper(power, base, exponent, max) }, - }); - - function_definition! { - function checked_exp_signed(base, exponent, min, max) -> power { - // Currently, `leave` avoids this function being inlined. - // YUL team is working on optimizer improvements to fix that. - - // Note that 0**0 == 1 - ([switch! { - switch exponent - (case 0 { - (power := 1 ) - (leave) - }) - (case 1 { - (power := base ) - (leave) - }) - }]) - (if (iszero(base)) { - (power := 0 ) - (leave) - }) - (power := 1 ) - // We pull out the first iteration because it is the only one in which - // base can be negative. - // Exponent is at least 2 here. - // overflow check for base * base - ([switch! { - switch (sgt(base, 0)) - (case 1 { - (if (gt(base, (div(max, base)))) { - [revert_with_over_or_under_flow()] - }) - }) - (case 0 { - (if (slt(base, (sdiv(max, base)))) { - [revert_with_over_or_under_flow()] - }) - }) - }]) - (if (and(exponent, 1)) { - (power := base ) - }) - (base := (mul(base, base))) - (exponent := shr(1, exponent)) - // // Below this point, base is always positive. - ([checked_exp_helper_call]) // power = 1, base = 16 which is wrong - (if (and((sgt(power, 0)), (gt(power, (div(max, base)))))) { [revert_with_over_or_under_flow()] }) - (if (and((slt(power, 0)), (slt(power, (sdiv(min, base)))))) { [revert_with_over_or_under_flow()] }) - (power := (mul(power, base))) - } - } -} - -fn checked_exp_unsigned() -> yul::Statement { - // TODO: Refactor once https://github.com/ethereum/fe/issues/314 is resolved - let checked_exp_helper_call = yul::Statement::Assignment(yul::Assignment { - identifiers: identifiers! { ("power") ("base")}, - expression: expression! { checked_exp_helper(1, base, exponent, max) }, - }); - - function_definition! { - function checked_exp_unsigned(base, exponent, max) -> power { - // Currently, `leave` avoids this function being inlined. - // YUL team is working on optimizer improvements to fix that. - - // Note that 0**0 == 1 - (if (iszero(exponent)) { - (power := 1 ) - (leave) - }) - (if (iszero(base)) { - (power := 0 ) - (leave) - }) - // Specializations for small bases - ([switch! { - switch base - // 0 is handled above - (case 1 { - (power := 1 ) - (leave) - }) - (case 2 { - (if (gt(exponent, 255)) { - [revert_with_over_or_under_flow()] - }) - (power := (exp(2, exponent))) - (if (gt(power, max)) { - [revert_with_over_or_under_flow()] - }) - (leave) - }) - }]) - (if (and((sgt(power, 0)), (gt(power, (div(max, base)))))) { [revert_with_over_or_under_flow()] }) - - (if (or((and((lt(base, 11)), (lt(exponent, 78)))), (and((lt(base, 307)), (lt(exponent, 32)))))) { - (power := (exp(base, exponent))) - (if (gt(power, max)) { - [revert_with_over_or_under_flow()] - }) - (leave) - }) - - ([checked_exp_helper_call]) - (if (gt(power, (div(max, base)))) { - [revert_with_over_or_under_flow()] - }) - (power := (mul(power, base))) - } - } -} - -fn get_min_max(integer: Integer) -> (yul::Expression, yul::Expression) { - let map = numeric_min_max(); - - map.get(&integer) - .expect("Expected integer to be in map") - .to_owned() -} - -fn get_max(integer: Integer) -> yul::Expression { - let (_, max) = get_min_max(integer); - max -} diff --git a/crates/yulgen/src/runtime/functions/mod.rs b/crates/yulgen/src/runtime/functions/mod.rs deleted file mode 100644 index 3e8b800c7b..0000000000 --- a/crates/yulgen/src/runtime/functions/mod.rs +++ /dev/null @@ -1,19 +0,0 @@ -use yultsur::*; - -pub mod abi; -pub mod contracts; -pub mod data; -pub mod math; -pub mod revert; - -/// Returns all functions that should be available during runtime. -pub fn std() -> Vec { - [ - contracts::all(), - abi::all(), - data::all(), - math::all(), - revert::all(), - ] - .concat() -} diff --git a/crates/yulgen/src/runtime/functions/revert.rs b/crates/yulgen/src/runtime/functions/revert.rs deleted file mode 100644 index 9070334936..0000000000 --- a/crates/yulgen/src/runtime/functions/revert.rs +++ /dev/null @@ -1,61 +0,0 @@ -use crate::names; -use crate::operations::abi as abi_operations; -use crate::types::AbiType; -use yultsur::*; - -/// Generate a YUL function to revert with the `Error` signature and the -/// given string. -/// -/// NOTE: This is currently used for `assert False, "message"` statements which are -/// encoded as `Error(msg="message")`. This will be removed in the future. -pub fn error_revert(typ: &AbiType) -> yul::Statement { - revert("Error", typ) -} - -/// Generate a YUL function to revert with an error code. -pub fn error_revert_numeric() -> yul::Statement { - revert("Error", &AbiType::Uint { size: 32 }) -} - -/// Generate a YUL function to revert with a panic code. -pub fn panic_revert() -> yul::Statement { - revert("Panic", &AbiType::Uint { size: 32 }) -} - -/// Generate a YUL function to revert with any signature name and type. -/// Note: The parentheses on a tuple are removed in the selector preimage. -pub fn revert(name: &str, typ: &AbiType) -> yul::Statement { - let func_name = names::revert(name, typ); - // the selector parens around a tuple are removed for the selector preimage - // e.g. we use `MyError(bool, address)` instead of `MyError((bool, address))` - let selector = { - let selector_params = match typ.clone() { - AbiType::Tuple { components } => components, - typ => vec![typ], - }; - let selector = fe_abi::utils::func_selector( - name, - &selector_params - .iter() - .map(AbiType::selector_name) - .collect::>(), - ); - literal_expression! { (selector) } - }; - let val_expr = vec![expression! { val }]; - let encoding_size = abi_operations::encoding_size(&[typ.clone()], &val_expr); - let encode_val = abi_operations::encode(&[typ.clone()], val_expr); - - function_definition! { - function [func_name](val) { - (let ptr := alloc_mstoren([selector], 4)) - (pop([encode_val])) - (revert(ptr, (add(4, [encoding_size])))) - } - } -} - -/// Return all revert functions used by default. -pub fn all() -> Vec { - vec![panic_revert(), error_revert_numeric()] -} diff --git a/crates/yulgen/src/runtime/mod.rs b/crates/yulgen/src/runtime/mod.rs deleted file mode 100644 index 09aa74ece8..0000000000 --- a/crates/yulgen/src/runtime/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod abi_dispatcher; -pub mod functions; diff --git a/crates/yulgen/src/types.rs b/crates/yulgen/src/types.rs deleted file mode 100644 index 5f9b1df981..0000000000 --- a/crates/yulgen/src/types.rs +++ /dev/null @@ -1,269 +0,0 @@ -use fe_analyzer::errors::NotFixedSize; -use fe_analyzer::namespace::types::{ - Array, Base, Contract, FeString, Integer, Struct, Tuple, Type, -}; -use fe_analyzer::AnalyzerDb; - -pub trait EvmSized { - /// The amount of bytes used by the type when being stored. - fn size(&self) -> usize; -} - -pub trait AsEvmSized { - fn as_evm_sized(&self) -> Box; -} - -impl AsEvmSized for Type { - fn as_evm_sized(&self) -> Box { - let sized: Box = self.clone().try_into().unwrap_or_else(|_| { - panic!("Expected type that implements EvmSized trait but is {self}") - }); - sized - } -} - -impl TryFrom for Box { - type Error = NotFixedSize; - - fn try_from(value: Type) -> Result { - match value { - Type::Array(val) => Ok(Box::new(val)), - Type::Base(val) => Ok(Box::new(val)), - Type::Tuple(val) => Ok(Box::new(val)), - Type::String(val) => Ok(Box::new(val)), - Type::Struct(val) => Ok(Box::new(val)), - Type::Contract(val) => Ok(Box::new(val)), - Type::Map(_) | Type::SelfContract(_) => Err(NotFixedSize), - } - } -} - -impl EvmSized for Integer { - fn size(&self) -> usize { - match self { - Integer::U8 | Integer::I8 => 1, - Integer::U16 | Integer::I16 => 2, - Integer::U32 | Integer::I32 => 4, - Integer::U64 | Integer::I64 => 8, - Integer::U128 | Integer::I128 => 16, - Integer::U256 | Integer::I256 => 32, - } - } -} - -impl EvmSized for Base { - fn size(&self) -> usize { - match self { - Base::Numeric(integer) => integer.size(), - Base::Bool => 1, - Base::Address => 32, - Base::Unit => 0, - } - } -} - -impl EvmSized for Array { - fn size(&self) -> usize { - self.size * self.inner.size() - } -} - -impl EvmSized for Tuple { - fn size(&self) -> usize { - self.items - .iter() - .map(|val| { - let size: Box = - val.clone().try_into().expect("Invalid type of tuple item"); - size.size() - }) - .sum() - } -} - -impl EvmSized for Struct { - fn size(&self) -> usize { - self.field_count * 32 - } -} - -impl EvmSized for FeString { - fn size(&self) -> usize { - self.max_size + 32 - } -} - -impl EvmSized for Contract { - fn size(&self) -> usize { - 32 - } -} - -/// Solidity ABI type with extra information needed for generation encoding/decoding functions. -#[derive(Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash)] -pub enum AbiType { - StaticArray { inner: Box, size: usize }, - Tuple { components: Vec }, - Uint { size: usize }, - Int { size: usize }, - Bool, - Address, - String { max_size: usize }, - Bytes { size: usize }, -} - -#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash)] -pub enum AbiDecodeLocation { - Calldata, - Memory, -} - -pub fn to_abi_types(db: &dyn AnalyzerDb, types: &[impl AsAbiType]) -> Vec { - types.iter().map(|typ| typ.as_abi_type(db)).collect() -} - -pub fn to_abi_selector_names(types: &[AbiType]) -> Vec { - types.iter().map(AbiType::selector_name).collect() -} - -impl AbiType { - /// The number of bytes used to encode the type's head. - pub fn head_size(&self) -> usize { - match self { - AbiType::StaticArray { size, .. } => 32 * size, - AbiType::Tuple { components } => 32 * components.len(), - AbiType::Uint { .. } - | AbiType::Int { .. } - | AbiType::Bool - | AbiType::Address - | AbiType::String { .. } - | AbiType::Bytes { .. } => 32, - } - } - - /// The number of bytes used in Fe's data layout. This is used when packing and unpacking - /// arrays. - pub fn packed_size(&self) -> usize { - match *self { - AbiType::Uint { size } | AbiType::Int { size } => size, - AbiType::Bool => 1, - AbiType::Address => 32, - _ => todo!("recursive encoding"), - } - } - - /// `true` if the encoded value is stored in the data section, `false` if it is not. - pub fn has_data(&self) -> bool { - match self { - AbiType::Uint { .. } - | AbiType::StaticArray { .. } - | AbiType::Tuple { .. } - | AbiType::Int { .. } - | AbiType::Bool - | AbiType::Address => false, - AbiType::String { .. } | AbiType::Bytes { .. } => true, - } - } - - pub fn selector_name(&self) -> String { - match self { - AbiType::StaticArray { inner, size } => format!("{}[{}]", inner.selector_name(), size), - AbiType::Tuple { components } => format!( - "({})", - components - .iter() - .map(AbiType::selector_name) - .collect::>() - .join(",") - ), - AbiType::Uint { size } => format!("uint{}", 8 * size), - AbiType::Int { size } => format!("int{}", 8 * size), - AbiType::Bool => "bool".to_string(), - AbiType::Address => "address".to_string(), - AbiType::String { .. } => "string".to_string(), - AbiType::Bytes { .. } => "bytes".to_string(), - } - } -} - -pub trait AsAbiType { - fn as_abi_type(&self, db: &dyn AnalyzerDb) -> AbiType; -} - -impl AsAbiType for Type { - fn as_abi_type(&self, db: &dyn AnalyzerDb) -> AbiType { - match self { - Type::Base(base) => base.as_abi_type(db), - Type::Array(array) => array.as_abi_type(db), - Type::Tuple(tuple) => tuple.as_abi_type(db), - Type::String(string) => string.as_abi_type(db), - Type::Contract(_) => AbiType::Address, - Type::Struct(val) => val.as_abi_type(db), - _ => panic!("Can not force {self} into an AbiType"), - } - } -} - -impl AsAbiType for Base { - fn as_abi_type(&self, _db: &dyn AnalyzerDb) -> AbiType { - match self { - Base::Numeric(integer) => { - let size = integer.size(); - if integer.is_signed() { - AbiType::Int { size } - } else { - AbiType::Uint { size } - } - } - Base::Address => AbiType::Address, - Base::Bool => AbiType::Bool, - Base::Unit => panic!("unit type is not abi encodable"), - } - } -} - -impl AsAbiType for Array { - fn as_abi_type(&self, db: &dyn AnalyzerDb) -> AbiType { - if matches!(self.inner, Base::Numeric(Integer::U8)) { - AbiType::Bytes { size: self.size } - } else { - AbiType::StaticArray { - inner: Box::new(self.inner.as_abi_type(db)), - size: self.size, - } - } - } -} - -impl AsAbiType for Struct { - fn as_abi_type(&self, db: &dyn AnalyzerDb) -> AbiType { - let components = self - .id - .fields(db) - .values() - .map(|field| { - field - .typ(db) - .expect("struct field type error") - .as_abi_type(db) - }) - .collect(); - AbiType::Tuple { components } - } -} - -impl AsAbiType for Tuple { - fn as_abi_type(&self, db: &dyn AnalyzerDb) -> AbiType { - AbiType::Tuple { - components: self.items.iter().map(|typ| typ.as_abi_type(db)).collect(), - } - } -} - -impl AsAbiType for FeString { - fn as_abi_type(&self, _db: &dyn AnalyzerDb) -> AbiType { - AbiType::String { - max_size: self.max_size, - } - } -} diff --git a/crates/yulgen/src/utils.rs b/crates/yulgen/src/utils.rs deleted file mode 100644 index 278a5d5e8f..0000000000 --- a/crates/yulgen/src/utils.rs +++ /dev/null @@ -1,4 +0,0 @@ -/// Rounds up to nearest multiple of 32. -pub fn ceil_32(n: usize) -> usize { - ((n + 31) / 32) * 32 -} diff --git a/crates/yulgen/tests/snapshots/yulgen__abi_decode_component_address_mem_function.snap b/crates/yulgen/tests/snapshots/yulgen__abi_decode_component_address_mem_function.snap deleted file mode 100644 index 3f073850ba..0000000000 --- a/crates/yulgen/tests/snapshots/yulgen__abi_decode_component_address_mem_function.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: crates/yulgen/tests/yulgen.rs -expression: "abi_functions::decode_component_bool(AbiDecodeLocation::Memory)" - ---- -function abi_decode_component_bool_mem(head_start, offset) -> return_val { - let ptr := add(head_start, offset) - return_val := mload(ptr) - if iszero(is_left_padded(255, return_val)) { revert_with_Error_uint256(259) } -} diff --git a/crates/yulgen/tests/snapshots/yulgen__abi_decode_component_bool_calldata_function.snap b/crates/yulgen/tests/snapshots/yulgen__abi_decode_component_bool_calldata_function.snap deleted file mode 100644 index 0806e37ea3..0000000000 --- a/crates/yulgen/tests/snapshots/yulgen__abi_decode_component_bool_calldata_function.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: crates/yulgen/tests/yulgen.rs -expression: "abi_functions::decode_component_bool(AbiDecodeLocation::Calldata)" - ---- -function abi_decode_component_bool_calldata(head_start, offset) -> return_val { - let ptr := add(head_start, offset) - return_val := calldataload(ptr) - if iszero(is_left_padded(255, return_val)) { revert_with_Error_uint256(259) } -} diff --git a/crates/yulgen/tests/snapshots/yulgen__abi_decode_component_bytes_26_mem_function.snap b/crates/yulgen/tests/snapshots/yulgen__abi_decode_component_bytes_26_mem_function.snap deleted file mode 100644 index a6dbee2d7f..0000000000 --- a/crates/yulgen/tests/snapshots/yulgen__abi_decode_component_bytes_26_mem_function.snap +++ /dev/null @@ -1,19 +0,0 @@ ---- -source: crates/yulgen/tests/yulgen.rs -expression: "abi_functions::decode_component_bytes(26, AbiDecodeLocation::Memory)" - ---- -function abi_decode_component_bytes_26_mem(head_start, head_offset) -> return_val, data_start_offset, data_end_offset { - let head_ptr := add(head_start, head_offset) - data_start_offset := mload(head_ptr) - let data_start := add(head_start, data_start_offset) - let bytes_size := mload(data_start) - if iszero(eq(bytes_size, 26)) { revert_with_Error_uint256(259) } - let data_size := add(bytes_size, 32) - let padded_data_size := ceil32(data_size) - data_end_offset := add(data_start_offset, padded_data_size) - let end_word := mload(sub(add(head_start, data_end_offset), 32)) - let padding_size_bits := mul(sub(padded_data_size, data_size), 8) - if iszero(is_right_padded(padding_size_bits, end_word)) { revert_with_Error_uint256(259) } - return_val := mcopym(add(data_start, 32), sub(data_size, 32)) -} diff --git a/crates/yulgen/tests/snapshots/yulgen__abi_decode_component_int16_calldata_function.snap b/crates/yulgen/tests/snapshots/yulgen__abi_decode_component_int16_calldata_function.snap deleted file mode 100644 index 6558b6471e..0000000000 --- a/crates/yulgen/tests/snapshots/yulgen__abi_decode_component_int16_calldata_function.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: crates/yulgen/tests/yulgen.rs -expression: "abi_functions::decode_component_int(2, AbiDecodeLocation::Calldata)" - ---- -function abi_decode_component_int16_calldata(head_start, offset) -> return_val { - let ptr := add(head_start, offset) - return_val := calldataload(ptr) - if iszero(or(iszero(shr(15, return_val)), iszero(shr(15, not(return_val))))) { revert_with_Error_uint256(259) } -} diff --git a/crates/yulgen/tests/snapshots/yulgen__abi_decode_component_static_array_address_calldata_function.snap b/crates/yulgen/tests/snapshots/yulgen__abi_decode_component_static_array_address_calldata_function.snap deleted file mode 100644 index c1640d12e5..0000000000 --- a/crates/yulgen/tests/snapshots/yulgen__abi_decode_component_static_array_address_calldata_function.snap +++ /dev/null @@ -1,14 +0,0 @@ ---- -source: crates/yulgen/tests/yulgen.rs -expression: "abi_functions::decode_component_static_array(&AbiType::Address, 42,\n AbiDecodeLocation::Calldata)" - ---- -function abi_decode_component_static_array_42_address_calldata(head_start, offset) -> return_val { - let ptr := add(head_start, offset) - return_val := avail() - for { let i := 0 } lt(i, 42) { i := add(i, 1) } { - let inner_offset := add(offset, mul(i, 32)) - let decoded_val := abi_decode_component_address_calldata(head_start, inner_offset) - pop(alloc_mstoren(decoded_val, 32)) - } -} diff --git a/crates/yulgen/tests/snapshots/yulgen__abi_decode_component_string_26_calldata_function.snap b/crates/yulgen/tests/snapshots/yulgen__abi_decode_component_string_26_calldata_function.snap deleted file mode 100644 index 5df135e849..0000000000 --- a/crates/yulgen/tests/snapshots/yulgen__abi_decode_component_string_26_calldata_function.snap +++ /dev/null @@ -1,19 +0,0 @@ ---- -source: crates/yulgen/tests/yulgen.rs -expression: "abi_functions::decode_component_bytes(26, AbiDecodeLocation::Calldata)" - ---- -function abi_decode_component_bytes_26_calldata(head_start, head_offset) -> return_val, data_start_offset, data_end_offset { - let head_ptr := add(head_start, head_offset) - data_start_offset := calldataload(head_ptr) - let data_start := add(head_start, data_start_offset) - let bytes_size := calldataload(data_start) - if iszero(eq(bytes_size, 26)) { revert_with_Error_uint256(259) } - let data_size := add(bytes_size, 32) - let padded_data_size := ceil32(data_size) - data_end_offset := add(data_start_offset, padded_data_size) - let end_word := calldataload(sub(add(head_start, data_end_offset), 32)) - let padding_size_bits := mul(sub(padded_data_size, data_size), 8) - if iszero(is_right_padded(padding_size_bits, end_word)) { revert_with_Error_uint256(259) } - return_val := ccopym(add(data_start, 32), sub(data_size, 32)) -} diff --git a/crates/yulgen/tests/snapshots/yulgen__abi_decode_component_tuple_u256_address_mem_function.snap b/crates/yulgen/tests/snapshots/yulgen__abi_decode_component_tuple_u256_address_mem_function.snap deleted file mode 100644 index 189ba05bfa..0000000000 --- a/crates/yulgen/tests/snapshots/yulgen__abi_decode_component_tuple_u256_address_mem_function.snap +++ /dev/null @@ -1,19 +0,0 @@ ---- -source: crates/yulgen/tests/yulgen.rs -expression: "abi_functions::decode_component_tuple(&[AbiType::Uint{size: 32,},\n AbiType::Address],\n AbiDecodeLocation::Memory)" - ---- -function abi_decode_component_tuple_uint256_address_mem(head_start, offset) -> return_val { - let ptr := add(head_start, offset) - return_val := avail() - { - let component_offset := add(offset, mul(32, 0)) - let decoded_val := abi_decode_component_uint256_mem(head_start, component_offset) - pop(alloc_mstoren(decoded_val, 32)) - } - { - let component_offset := add(offset, mul(32, 1)) - let decoded_val := abi_decode_component_address_mem(head_start, component_offset) - pop(alloc_mstoren(decoded_val, 32)) - } -} diff --git a/crates/yulgen/tests/snapshots/yulgen__abi_decode_component_uint256_mem_function.snap b/crates/yulgen/tests/snapshots/yulgen__abi_decode_component_uint256_mem_function.snap deleted file mode 100644 index 2ac5dcce92..0000000000 --- a/crates/yulgen/tests/snapshots/yulgen__abi_decode_component_uint256_mem_function.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: crates/yulgen/tests/yulgen.rs -expression: "abi_functions::decode_component_uint(32, AbiDecodeLocation::Memory)" - ---- -function abi_decode_component_uint256_mem(head_start, offset) -> return_val { - let ptr := add(head_start, offset) - return_val := mload(ptr) - if iszero(is_left_padded(0, return_val)) { revert_with_Error_uint256(259) } -} diff --git a/crates/yulgen/tests/snapshots/yulgen__abi_decode_data_address_bool_mem_function.snap b/crates/yulgen/tests/snapshots/yulgen__abi_decode_data_address_bool_mem_function.snap deleted file mode 100644 index 59764e726c..0000000000 --- a/crates/yulgen/tests/snapshots/yulgen__abi_decode_data_address_bool_mem_function.snap +++ /dev/null @@ -1,28 +0,0 @@ ---- -source: crates/yulgen/tests/yulgen.rs -expression: "yul::Block{statements:\n abi_functions::decode_functions(&[AbiType::Bool,\n AbiType::Address],\n AbiDecodeLocation::Memory),}" - ---- -{ - function abi_decode_component_address_mem(head_start, offset) -> return_val { - let ptr := add(head_start, offset) - return_val := mload(ptr) - if iszero(is_left_padded(96, return_val)) { revert_with_Error_uint256(259) } - } - function abi_decode_component_bool_mem(head_start, offset) -> return_val { - let ptr := add(head_start, offset) - return_val := mload(ptr) - if iszero(is_left_padded(255, return_val)) { revert_with_Error_uint256(259) } - } - function abi_decode_data_bool_address_mem(head_start, data_end) -> return_val_0, return_val_1 { - let encoding_size := sub(data_end, head_start) - if iszero(eq(encoding_size, 64)) { revert_with_Error_uint256(259) } - let head_offset_0 := 0 - let head_offset_1 := 32 - let decoded_val_0 := abi_decode_component_bool_mem(head_start, head_offset_0) - let decoded_val_1 := abi_decode_component_address_mem(head_start, head_offset_1) - if iszero(eq(encoding_size, 64)) { revert_with_Error_uint256(259) } - return_val_0 := decoded_val_0 - return_val_1 := decoded_val_1 - } -} diff --git a/crates/yulgen/tests/snapshots/yulgen__abi_decode_data_u256_bytes_string_bool_address_bytes_calldata_function.snap b/crates/yulgen/tests/snapshots/yulgen__abi_decode_data_u256_bytes_string_bool_address_bytes_calldata_function.snap deleted file mode 100644 index f6abeecf6c..0000000000 --- a/crates/yulgen/tests/snapshots/yulgen__abi_decode_data_u256_bytes_string_bool_address_bytes_calldata_function.snap +++ /dev/null @@ -1,76 +0,0 @@ ---- -source: crates/yulgen/tests/yulgen.rs -expression: "yul::Block{statements:\n abi_functions::decode_functions(&[AbiType::Uint{size: 32,},\n AbiType::Bytes{size: 100,},\n AbiType::String{max_size:\n 42,},\n AbiType::Bool,\n AbiType::Address,\n AbiType::Bytes{size: 100,}],\n AbiDecodeLocation::Calldata),}" - ---- -{ - function abi_decode_component_address_calldata(head_start, offset) -> return_val { - let ptr := add(head_start, offset) - return_val := calldataload(ptr) - if iszero(is_left_padded(96, return_val)) { revert_with_Error_uint256(259) } - } - function abi_decode_component_bool_calldata(head_start, offset) -> return_val { - let ptr := add(head_start, offset) - return_val := calldataload(ptr) - if iszero(is_left_padded(255, return_val)) { revert_with_Error_uint256(259) } - } - function abi_decode_component_bytes_100_calldata(head_start, head_offset) -> return_val, data_start_offset, data_end_offset { - let head_ptr := add(head_start, head_offset) - data_start_offset := calldataload(head_ptr) - let data_start := add(head_start, data_start_offset) - let bytes_size := calldataload(data_start) - if iszero(eq(bytes_size, 100)) { revert_with_Error_uint256(259) } - let data_size := add(bytes_size, 32) - let padded_data_size := ceil32(data_size) - data_end_offset := add(data_start_offset, padded_data_size) - let end_word := calldataload(sub(add(head_start, data_end_offset), 32)) - let padding_size_bits := mul(sub(padded_data_size, data_size), 8) - if iszero(is_right_padded(padding_size_bits, end_word)) { revert_with_Error_uint256(259) } - return_val := ccopym(add(data_start, 32), sub(data_size, 32)) - } - function abi_decode_component_string_42_calldata(head_start, head_offset) -> return_val, data_start_offset, data_end_offset { - let head_ptr := add(head_start, head_offset) - data_start_offset := calldataload(head_ptr) - let data_start := add(head_start, data_start_offset) - let string_size := calldataload(data_start) - if gt(string_size, 42) { revert_with_Error_uint256(259) } - let data_size := add(string_size, 32) - let padded_data_size := ceil32(data_size) - data_end_offset := add(data_start_offset, padded_data_size) - let end_word := calldataload(sub(add(head_start, data_end_offset), 32)) - let padding_size_bits := mul(sub(padded_data_size, data_size), 8) - if iszero(is_right_padded(padding_size_bits, end_word)) { revert_with_Error_uint256(259) } - return_val := ccopym(data_start, data_size) - } - function abi_decode_component_uint256_calldata(head_start, offset) -> return_val { - let ptr := add(head_start, offset) - return_val := calldataload(ptr) - if iszero(is_left_padded(0, return_val)) { revert_with_Error_uint256(259) } - } - function abi_decode_data_uint256_bytes_100_string_42_bool_address_bytes_100_calldata(head_start, data_end) -> return_val_0, return_val_1, return_val_2, return_val_3, return_val_4, return_val_5 { - let encoding_size := sub(data_end, head_start) - if or(lt(encoding_size, 544), gt(encoding_size, 608)) { revert_with_Error_uint256(259) } - let head_offset_0 := 0 - let head_offset_1 := 32 - let head_offset_2 := 64 - let head_offset_3 := 96 - let head_offset_4 := 128 - let head_offset_5 := 160 - let decoded_val_0 := abi_decode_component_uint256_calldata(head_start, head_offset_0) - let decoded_val_1, data_start_offset_1, data_end_offset_1 := abi_decode_component_bytes_100_calldata(head_start, head_offset_1) - let decoded_val_2, data_start_offset_2, data_end_offset_2 := abi_decode_component_string_42_calldata(head_start, head_offset_2) - let decoded_val_3 := abi_decode_component_bool_calldata(head_start, head_offset_3) - let decoded_val_4 := abi_decode_component_address_calldata(head_start, head_offset_4) - let decoded_val_5, data_start_offset_5, data_end_offset_5 := abi_decode_component_bytes_100_calldata(head_start, head_offset_5) - if iszero(eq(data_start_offset_1, 192)) { revert_with_Error_uint256(259) } - if iszero(eq(data_start_offset_2, data_end_offset_1)) { revert_with_Error_uint256(259) } - if iszero(eq(data_start_offset_5, data_end_offset_2)) { revert_with_Error_uint256(259) } - if iszero(eq(encoding_size, data_end_offset_5)) { revert_with_Error_uint256(259) } - return_val_0 := decoded_val_0 - return_val_1 := decoded_val_1 - return_val_2 := decoded_val_2 - return_val_3 := decoded_val_3 - return_val_4 := decoded_val_4 - return_val_5 := decoded_val_5 - } -} diff --git a/crates/yulgen/tests/snapshots/yulgen__abi_dispatcher.snap b/crates/yulgen/tests/snapshots/yulgen__abi_dispatcher.snap deleted file mode 100644 index 6fe69a0480..0000000000 --- a/crates/yulgen/tests/snapshots/yulgen__abi_dispatcher.snap +++ /dev/null @@ -1,20 +0,0 @@ ---- -source: crates/yulgen/tests/yulgen.rs -expression: "abi_dispatcher::dispatcher(&functions())" - ---- -function $$__call__() { switch cloadn(0, 4) -case 0x9476f922 { - let return_val := $$somemod$hello_world() - let encoding_start := abi_encode_string_42(return_val) - let encoding_size := add(64, ceil32(mload(return_val))) - return(encoding_start, encoding_size) -} -case 0x771602f7 { - let call_val_0, call_val_1 := abi_decode_data_uint256_uint256_calldata(4, calldatasize()) - let return_val := $$somemod$add(0, call_val_0, call_val_1) - let encoding_start := abi_encode_uint256(return_val) - let encoding_size := add(32, 0) - return(encoding_start, encoding_size) -} -default { return(0, 0) } } diff --git a/crates/yulgen/tests/snapshots/yulgen__abi_encode_u256_address_function.snap b/crates/yulgen/tests/snapshots/yulgen__abi_encode_u256_address_function.snap deleted file mode 100644 index 5f93b491d0..0000000000 --- a/crates/yulgen/tests/snapshots/yulgen__abi_encode_u256_address_function.snap +++ /dev/null @@ -1,17 +0,0 @@ ---- -source: crates/yulgen/tests/yulgen.rs -expression: "abi_functions::encode(vec![U256, Base :: Address])" - ---- -function abi_encode_uint256_address(encode_val_0, encode_val_1) -> return_ptr { - return_ptr := avail() - let data_offset := 64 - { - let ptr := alloc(32) - mstore(ptr, encode_val_0) - } - { - let ptr := alloc(32) - mstore(ptr, encode_val_1) - } -} diff --git a/crates/yulgen/tests/snapshots/yulgen__constructor_no_init.snap b/crates/yulgen/tests/snapshots/yulgen__constructor_no_init.snap deleted file mode 100644 index bb2fb92437..0000000000 --- a/crates/yulgen/tests/snapshots/yulgen__constructor_no_init.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: crates/yulgen/tests/yulgen.rs -expression: "constructor::build()" - ---- -code { - let size := datasize("runtime") - datacopy(0, dataoffset("runtime"), size) - return(0, size) -} diff --git a/crates/yulgen/tests/snapshots/yulgen__decode_data_name_string_mem_name.snap b/crates/yulgen/tests/snapshots/yulgen__decode_data_name_string_mem_name.snap deleted file mode 100644 index 352ead7b21..0000000000 --- a/crates/yulgen/tests/snapshots/yulgen__decode_data_name_string_mem_name.snap +++ /dev/null @@ -1,6 +0,0 @@ ---- -source: crates/yulgen/tests/yulgen.rs -expression: "abi_names::decode_data(&[FeString{max_size: 42,}], AbiDecodeLocation::Memory)" - ---- -abi_decode_data_string_42_mem diff --git a/crates/yulgen/tests/snapshots/yulgen__decode_data_name_u256_calldata_name.snap b/crates/yulgen/tests/snapshots/yulgen__decode_data_name_u256_calldata_name.snap deleted file mode 100644 index b4635b8b8a..0000000000 --- a/crates/yulgen/tests/snapshots/yulgen__decode_data_name_u256_calldata_name.snap +++ /dev/null @@ -1,6 +0,0 @@ ---- -source: crates/yulgen/tests/yulgen.rs -expression: "abi_names::decode_data(&[U256], AbiDecodeLocation::Calldata)" - ---- -abi_decode_data_uint256_calldata diff --git a/crates/yulgen/tests/snapshots/yulgen__decode_string_calldata_operation.snap b/crates/yulgen/tests/snapshots/yulgen__decode_string_calldata_operation.snap deleted file mode 100644 index 31e656c4ca..0000000000 --- a/crates/yulgen/tests/snapshots/yulgen__decode_string_calldata_operation.snap +++ /dev/null @@ -1,6 +0,0 @@ ---- -source: crates/yulgen/tests/yulgen.rs -expression: "abi_operations::decode_data(&[FeString{max_size: 26,}], expression! { 42 },\n expression! { 10 }, AbiDecodeLocation::Calldata)" - ---- -abi_decode_data_string_26_calldata(42, 10) diff --git a/crates/yulgen/tests/snapshots/yulgen__emit_event_no_indexed_operation.snap b/crates/yulgen/tests/snapshots/yulgen__emit_event_no_indexed_operation.snap deleted file mode 100644 index 9b4d6bfb75..0000000000 --- a/crates/yulgen/tests/snapshots/yulgen__emit_event_no_indexed_operation.snap +++ /dev/null @@ -1,6 +0,0 @@ ---- -source: crates/yulgen/tests/yulgen.rs -expression: "data_operations::emit_event(event_no_indexed(),\n vec![expression ! { 26 }, expression ! { 0x42 }])" - ---- -log1(abi_encode_uint256_address(26, 0x42), add(64, 0), 0x74bffa18f2b20140b65de9264a54040b23ab0a34e7643d52f67f7fb18be9bbcb) diff --git a/crates/yulgen/tests/snapshots/yulgen__emit_event_one_indexed_operation.snap b/crates/yulgen/tests/snapshots/yulgen__emit_event_one_indexed_operation.snap deleted file mode 100644 index 9538a3df7f..0000000000 --- a/crates/yulgen/tests/snapshots/yulgen__emit_event_one_indexed_operation.snap +++ /dev/null @@ -1,6 +0,0 @@ ---- -source: crates/yulgen/tests/yulgen.rs -expression: "data_operations::emit_event(event_one_indexed(),\n vec![expression ! { 26 }, expression ! { 0x42 }])" - ---- -log2(abi_encode_address(0x42), add(32, 0), 0x74bffa18f2b20140b65de9264a54040b23ab0a34e7643d52f67f7fb18be9bbcb, 26) diff --git a/crates/yulgen/tests/snapshots/yulgen__encode_name.snap b/crates/yulgen/tests/snapshots/yulgen__encode_name.snap deleted file mode 100644 index 5ec50e8d25..0000000000 --- a/crates/yulgen/tests/snapshots/yulgen__encode_name.snap +++ /dev/null @@ -1,6 +0,0 @@ ---- -source: crates/yulgen/tests/yulgen.rs -expression: "abi_names::encode(&[FixedSize::Base(U256),\n FixedSize::Array(Array{inner: Base::Numeric(Integer::U8),\n size: 100,})])" - ---- -abi_encode_uint256_bytes_100 diff --git a/crates/yulgen/tests/snapshots/yulgen__encode_size_u256_operation.snap b/crates/yulgen/tests/snapshots/yulgen__encode_size_u256_operation.snap deleted file mode 100644 index c29db40973..0000000000 --- a/crates/yulgen/tests/snapshots/yulgen__encode_size_u256_operation.snap +++ /dev/null @@ -1,6 +0,0 @@ ---- -source: crates/yulgen/tests/yulgen.rs -expression: "abi_operations::encoding_size(&[U256], vec![expression ! { 42 }])" - ---- -add(32, 0) diff --git a/crates/yulgen/tests/snapshots/yulgen__encode_u256_operation.snap b/crates/yulgen/tests/snapshots/yulgen__encode_u256_operation.snap deleted file mode 100644 index 9eabfd2220..0000000000 --- a/crates/yulgen/tests/snapshots/yulgen__encode_u256_operation.snap +++ /dev/null @@ -1,6 +0,0 @@ ---- -source: crates/yulgen/tests/yulgen.rs -expression: "abi_operations::encode(&[U256], vec![expression ! { 42 }])" - ---- -abi_encode_uint256(42) diff --git a/crates/yulgen/tests/snapshots/yulgen__revert_string_error.snap b/crates/yulgen/tests/snapshots/yulgen__revert_string_error.snap deleted file mode 100644 index d75993702d..0000000000 --- a/crates/yulgen/tests/snapshots/yulgen__revert_string_error.snap +++ /dev/null @@ -1,10 +0,0 @@ ---- -source: crates/yulgen/tests/yulgen.rs -expression: "revert_functions::error_revert(&AbiType::String{max_size: 3,})" - ---- -function revert_with_Error_string_3(val) { - let ptr := alloc_mstoren(0x08c379a0, 4) - pop(abi_encode_string_3(val)) - revert(ptr, add(4, add(64, ceil32(mload(val))))) -} diff --git a/crates/yulgen/tests/snapshots/yulgen__struct_empty_function.snap b/crates/yulgen/tests/snapshots/yulgen__struct_empty_function.snap deleted file mode 100644 index 305c9fbdb4..0000000000 --- a/crates/yulgen/tests/snapshots/yulgen__struct_empty_function.snap +++ /dev/null @@ -1,6 +0,0 @@ ---- -source: crates/yulgen/tests/yulgen.rs -expression: "structs_functions::generate_new_fn(&Struct::new(\"Foo\"))" - ---- -function struct_Foo_new() -> return_val { return_val := 0 } diff --git a/crates/yulgen/tests/snapshots/yulgen__struct_getter_gen_bar2_function.snap b/crates/yulgen/tests/snapshots/yulgen__struct_getter_gen_bar2_function.snap deleted file mode 100644 index d8fd653681..0000000000 --- a/crates/yulgen/tests/snapshots/yulgen__struct_getter_gen_bar2_function.snap +++ /dev/null @@ -1,6 +0,0 @@ ---- -source: crates/yulgen/tests/yulgen.rs -expression: "structs_functions::generate_get_fn(&struct_bool_bool(),\n &struct_bool_bool().fields[1].0)" - ---- -function struct_Foo_get_bar2_ptr(ptr) -> return_val { return_val := add(ptr, 63) } diff --git a/crates/yulgen/tests/snapshots/yulgen__struct_getter_gen_bar_function.snap b/crates/yulgen/tests/snapshots/yulgen__struct_getter_gen_bar_function.snap deleted file mode 100644 index fc789c0adf..0000000000 --- a/crates/yulgen/tests/snapshots/yulgen__struct_getter_gen_bar_function.snap +++ /dev/null @@ -1,6 +0,0 @@ ---- -source: crates/yulgen/tests/yulgen.rs -expression: "structs_functions::generate_get_fn(&struct_bool_bool(),\n &struct_bool_bool().fields[0].0)" - ---- -function struct_Foo_get_bar_ptr(ptr) -> return_val { return_val := add(ptr, 31) } diff --git a/crates/yulgen/tests/snapshots/yulgen__struct_new_gen_function.snap b/crates/yulgen/tests/snapshots/yulgen__struct_new_gen_function.snap deleted file mode 100644 index 5eef8ca2c8..0000000000 --- a/crates/yulgen/tests/snapshots/yulgen__struct_new_gen_function.snap +++ /dev/null @@ -1,11 +0,0 @@ ---- -source: crates/yulgen/tests/yulgen.rs -expression: "structs_functions::generate_new_fn(&struct_bool_bool())" - ---- -function struct_Foo_new(bar, bar2) -> return_val { - return_val := alloc(32) - mstore(return_val, bar) - let bar2_ptr := alloc(32) - mstore(bar2_ptr, bar2) -} diff --git a/crates/yulgen/tests/snapshots/yulgen__sum_operation.snap b/crates/yulgen/tests/snapshots/yulgen__sum_operation.snap deleted file mode 100644 index a95a708898..0000000000 --- a/crates/yulgen/tests/snapshots/yulgen__sum_operation.snap +++ /dev/null @@ -1,6 +0,0 @@ ---- -source: crates/yulgen/tests/yulgen.rs -expression: "data_operations::sum(expressions! { 42 26 99 })" - ---- -add(add(42, 26), 99) diff --git a/crates/yulgen/tests/yulgen.rs b/crates/yulgen/tests/yulgen.rs deleted file mode 100644 index 9fb731feea..0000000000 --- a/crates/yulgen/tests/yulgen.rs +++ /dev/null @@ -1,167 +0,0 @@ -use fe_yulgen::constructor; -use fe_yulgen::names::abi as abi_names; -use fe_yulgen::operations::{abi as abi_operations, data as data_operations}; -use fe_yulgen::runtime::abi_dispatcher; -use fe_yulgen::runtime::functions::{abi as abi_functions, revert as revert_functions}; -use fe_yulgen::types::{AbiDecodeLocation, AbiType}; -use insta::assert_display_snapshot; -use smol_str::SmolStr; -use wasm_bindgen_test::wasm_bindgen_test; -use yultsur::*; - -macro_rules! test_yulgen { - ($name:ident, $code:expr) => { - #[test] - #[wasm_bindgen_test] - fn $name() { - if cfg!(target_arch = "wasm32") { - fe_common::assert_snapshot_wasm!( - concat!("snapshots/yulgen__", stringify!($name), ".snap"), - $code.to_string() - ); - } else { - assert_display_snapshot!($code); - } - } - }; -} - -// constructor -test_yulgen! { constructor_no_init, constructor::build() } - -#[allow(clippy::type_complexity)] -fn functions() -> Vec<(SmolStr, SmolStr, Vec, Option, bool)> { - vec![ - ( - "hello_world".into(), - "$$somemod$hello_world".into(), - vec![], - Some(AbiType::String { max_size: 42 }), - false, - ), - ( - "add".into(), - "$$somemod$add".into(), - vec![AbiType::Uint { size: 32 }, AbiType::Uint { size: 32 }], - Some(AbiType::Uint { size: 32 }), - true, - ), - ] -} - -// ABI dispatcher -test_yulgen! { abi_dispatcher, abi_dispatcher::dispatcher(&functions()) } - -// ABI encoding functions -test_yulgen! { - abi_encode_u256_address_function, - abi_functions::encode(&[AbiType::Uint { size: 32 }, AbiType::Address]) -} - -// ABI decoding functions -test_yulgen! { - abi_decode_data_address_bool_mem_function, - yul::Block { statements: abi_functions::decode_functions(&[AbiType::Bool, AbiType::Address], AbiDecodeLocation::Memory) } -} -test_yulgen! { - abi_decode_data_u256_bytes_string_bool_address_bytes_calldata_function, - yul::Block { statements: abi_functions::decode_functions(&[ - AbiType::Uint { size: 32 }, - AbiType::Bytes { size: 100 }, - AbiType::String { max_size: 42 }, - AbiType::Bool, - AbiType::Address, - AbiType::Bytes { size: 100 }, - ], AbiDecodeLocation::Calldata) } -} -test_yulgen! { - abi_decode_component_uint256_mem_function, - abi_functions::decode_component_uint(32, AbiDecodeLocation::Memory) -} -test_yulgen! { - abi_decode_component_int16_calldata_function, - abi_functions::decode_component_int(2, AbiDecodeLocation::Calldata) -} -test_yulgen! { - abi_decode_component_bool_calldata_function, - abi_functions::decode_component_bool(AbiDecodeLocation::Calldata) -} -test_yulgen! { - abi_decode_component_address_mem_function, - abi_functions::decode_component_bool(AbiDecodeLocation::Memory) -} -test_yulgen! { - abi_decode_component_static_array_address_calldata_function, - abi_functions::decode_component_static_array(&AbiType::Address, 42, AbiDecodeLocation::Calldata) -} -test_yulgen! { - abi_decode_component_tuple_u256_address_mem_function, - abi_functions::decode_component_tuple( - &[AbiType::Uint { size: 32 }, AbiType::Address], - AbiDecodeLocation::Memory - ) -} -test_yulgen! { - abi_decode_component_bytes_26_mem_function, - abi_functions::decode_component_bytes(26, AbiDecodeLocation::Memory) -} -test_yulgen! { - abi_decode_component_string_26_calldata_function, - abi_functions::decode_component_bytes(26, AbiDecodeLocation::Calldata) -} - -// data operations -test_yulgen! { - emit_event_no_indexed_operation, - data_operations::emit_event("MyEvent", &[(AbiType::Uint { size: 32 }, false), (AbiType::Address, false)], vec![expression! { 26 }, expression! { 0x42 }]) -} -test_yulgen! { - emit_event_one_indexed_operation, - data_operations::emit_event("MyEvent", &[(AbiType::Uint { size: 32 }, true), (AbiType::Address, false)], vec![expression! { 26 }, expression! { 0x42 }]) -} -test_yulgen! { - sum_operation, - data_operations::sum(expressions! { 42 26 99 }) -} - -// ABI operations -test_yulgen! { - encode_u256_operation, - abi_operations::encode(&[AbiType::Uint { size: 32 }], vec![expression! { 42 }]) -} -test_yulgen! { - encode_size_u256_operation, - abi_operations::encoding_size(&[AbiType::Uint { size: 32 }], &[expression! { 42 }]) -} -test_yulgen! { - decode_string_calldata_operation, - abi_operations::decode_data( - &[AbiType::String { max_size: 26 }], - expression! { 42 }, - expression! { 10 }, - AbiDecodeLocation::Calldata - ) -} - -// ABI names -test_yulgen! { - encode_name, - abi_names::encode(&[ - AbiType::Uint { size: 32 }, - AbiType::Bytes { size: 100 } - ]) -} -test_yulgen! { - decode_data_name_u256_calldata_name, - abi_names::decode_data(&[AbiType::Uint { size: 32 }], AbiDecodeLocation::Calldata) -} -test_yulgen! { - decode_data_name_string_mem_name, - abi_names::decode_data(&[AbiType::String { max_size: 42 }], AbiDecodeLocation::Memory) -} - -// revert functions -test_yulgen! { - revert_string_error, - revert_functions::error_revert(&AbiType::String { max_size: 3 }) -} From 026d0c1c3cd01815882f07bfea7d082c75256728 Mon Sep 17 00:00:00 2001 From: Yoshitomo Nakanishi Date: Mon, 23 May 2022 23:12:46 +0900 Subject: [PATCH 15/15] Remove unused codes --- crates/codegen/src/db.rs | 18 +++++++------- crates/driver/src/lib.rs | 36 ++++++--------------------- crates/fe/src/main.rs | 45 +++------------------------------- crates/mir/src/analysis/cfg.rs | 36 +++++++++++---------------- crates/test-utils/src/lib.rs | 6 ++--- crates/tests/src/crashes.rs | 2 +- 6 files changed, 39 insertions(+), 104 deletions(-) diff --git a/crates/codegen/src/db.rs b/crates/codegen/src/db.rs index 2808dddb80..828b062acb 100644 --- a/crates/codegen/src/db.rs +++ b/crates/codegen/src/db.rs @@ -49,42 +49,42 @@ pub trait CodegenDb: MirDb + Upcast + UpcastMut { // TODO: Move this to driver. #[salsa::database(SourceDbStorage, AnalyzerDbStorage, MirDbStorage, CodegenDbStorage)] #[derive(Default)] -pub struct NewDb { - storage: salsa::Storage, +pub struct Db { + storage: salsa::Storage, } -impl salsa::Database for NewDb {} +impl salsa::Database for Db {} -impl Upcast for NewDb { +impl Upcast for Db { fn upcast(&self) -> &(dyn MirDb + 'static) { &*self } } -impl UpcastMut for NewDb { +impl UpcastMut for Db { fn upcast_mut(&mut self) -> &mut (dyn MirDb + 'static) { &mut *self } } -impl Upcast for NewDb { +impl Upcast for Db { fn upcast(&self) -> &(dyn SourceDb + 'static) { &*self } } -impl UpcastMut for NewDb { +impl UpcastMut for Db { fn upcast_mut(&mut self) -> &mut (dyn SourceDb + 'static) { &mut *self } } -impl Upcast for NewDb { +impl Upcast for Db { fn upcast(&self) -> &(dyn AnalyzerDb + 'static) { &*self } } -impl UpcastMut for NewDb { +impl UpcastMut for Db { fn upcast_mut(&mut self) -> &mut (dyn AnalyzerDb + 'static) { &mut *self } diff --git a/crates/driver/src/lib.rs b/crates/driver/src/lib.rs index 3bfd47de34..80972fc053 100644 --- a/crates/driver/src/lib.rs +++ b/crates/driver/src/lib.rs @@ -1,7 +1,7 @@ #![allow(unused_imports, dead_code)] -pub use fe_codegen::db::{CodegenDb, NewDb}; -use fe_codegen::yul::runtime::RuntimeProvider; +pub use fe_codegen::db::{CodegenDb, Db}; +//use fe_codegen::yul::runtime::RuntimeProvider; use fe_analyzer::namespace::items::{IngotId, IngotMode, ModuleId}; use fe_analyzer::AnalyzerDb; @@ -36,7 +36,7 @@ pub struct CompiledContract { pub struct CompileError(pub Vec); pub fn compile_single_file( - db: &mut NewDb, + db: &mut Db, path: &str, src: &str, with_bytecode: bool, @@ -57,7 +57,7 @@ pub fn compile_single_file( /// If `with_bytecode` is set to false, the compiler will skip the final Yul -> /// Bytecode pass. This is useful when debugging invalid Yul code. pub fn compile_ingot( - db: &mut NewDb, + db: &mut Db, name: &str, files: &[(impl AsRef, impl AsRef)], with_bytecode: bool, @@ -86,7 +86,7 @@ pub fn compile_ingot( /// Returns graphviz string. // TODO: This is temporary function for debugging. -pub fn dump_mir_single_file(db: &mut NewDb, path: &str, src: &str) -> Result { +pub fn dump_mir_single_file(db: &mut Db, path: &str, src: &str) -> Result { let module = ModuleId::new_standalone(db, path, src); let diags = module.diagnostics(db); @@ -99,29 +99,9 @@ pub fn dump_mir_single_file(db: &mut NewDb, path: &str, src: &str) -> Result Result { - let module = ModuleId::new_standalone(db, path, src); - - let diags = module.diagnostics(db); - if !diags.is_empty() { - return Err(CompileError(diags)); - } - - let mut contracts = String::new(); - for contract in db.module_contracts(module).as_ref() { - contracts.push_str(&format!( - "{}", - fe_codegen::yul::isel::lower_contract_deployable(db, *contract) - )) - } - - Ok(contracts) -} - #[cfg(feature = "solc-backend")] fn compile_module_id( - db: &mut NewDb, + db: &mut Db, module_id: ModuleId, with_bytecode: bool, optimize: bool, @@ -158,7 +138,7 @@ fn compile_module_id( #[cfg(not(feature = "solc-backend"))] fn compile_module_id( - db: &mut NewDb, + db: &mut Db, module_id: ModuleId, _with_bytecode: bool, _optimize: bool, @@ -185,7 +165,7 @@ fn compile_module_id( }) } -fn compile_to_yul(db: &mut NewDb, contract: ContractId) -> String { +fn compile_to_yul(db: &mut Db, contract: ContractId) -> String { let yul_contract = fe_codegen::yul::isel::lower_contract_deployable(db, contract); yul_contract.to_string().replace('"', "\\\"") } diff --git a/crates/fe/src/main.rs b/crates/fe/src/main.rs index efdffb1e1b..34fd109eb1 100644 --- a/crates/fe/src/main.rs +++ b/crates/fe/src/main.rs @@ -53,7 +53,7 @@ pub fn main() { .long("emit") .help("Comma separated compile targets e.g. -e=bytecode,yul") .possible_values(&["abi", "bytecode", "ast", "tokens", "yul", "loweredAst"]) - .default_value("abi,bytecode,yul") + .default_value("abi,bytecode") .use_delimiter(true) .takes_value(true), ) @@ -77,12 +77,6 @@ pub fn main() { .help("dump mir dot file") .takes_value(false), ) - .arg( - Arg::with_name("codegen") - .long("codegen") - .help("todo") - .takes_value(false), - ) .get_matches(); let input_path = matches.value_of("input").unwrap(); @@ -97,16 +91,12 @@ pub fn main() { return mir_dump(input_path); } - if matches.is_present("codegen") { - return yul_functions_dump(input_path); - } - #[cfg(not(feature = "solc-backend"))] if with_bytecode { eprintln!("Warning: bytecode output requires 'solc-backend' feature. Try `cargo build --release --features solc-backend`. Skipping."); } - let mut db = fe_driver::NewDb::default(); + let mut db = fe_driver::Db::default(); let (content, compiled_module) = if Path::new(input_path).is_file() { let content = match std::fs::read_to_string(input_path) { @@ -284,7 +274,7 @@ fn verify_nonexistent_or_empty(dir: &Path) -> Result<(), String> { } fn mir_dump(input_path: &str) { - let mut db = fe_driver::NewDb::default(); + let mut db = fe_driver::Db::default(); if Path::new(input_path).is_file() { let content = match std::fs::read_to_string(input_path) { Err(err) => { @@ -303,34 +293,7 @@ fn mir_dump(input_path: &str) { } } } else { - eprintln!("mir doesn't support ingot yet"); - std::process::exit(1) - } -} - -fn yul_functions_dump(input_path: &str) { - let mut db = fe_driver::NewDb::default(); - if Path::new(input_path).is_file() { - let content = match std::fs::read_to_string(input_path) { - Err(err) => { - eprintln!("Failed to load file: `{}`. Error: {}", input_path, err); - std::process::exit(1) - } - Ok(content) => content, - }; - - match fe_driver::dump_codegen_funcs(&mut db, input_path, &content) { - Ok(contract) => { - println!("{}", contract) - } - Err(err) => { - eprintln!("Unable to dump mir `{}", input_path); - print_diagnostics(&db, &err.0); - std::process::exit(1) - } - } - } else { - eprintln!("mir doesn't support ingot yet"); + eprintln!("dumping mir for ingot is not supported yet"); std::process::exit(1) } } diff --git a/crates/mir/src/analysis/cfg.rs b/crates/mir/src/analysis/cfg.rs index bf1f2a44fa..5e2af0d0d6 100644 --- a/crates/mir/src/analysis/cfg.rs +++ b/crates/mir/src/analysis/cfg.rs @@ -131,18 +131,18 @@ impl<'a> Iterator for CfgPostOrder<'a> { fn next(&mut self) -> Option { while let Some(&block) = self.stack.last() { let node_state = self.node_state.entry(block).or_default(); - if node_state.is_unvisited() { - node_state.set_visited(); + if *node_state == NodeState::Unvisited { + *node_state = NodeState::Visited; for &succ in self.cfg.succs(block) { let pred_state = self.node_state.entry(succ).or_default(); - if pred_state.is_unvisited() { + if *pred_state == NodeState::Unvisited { self.stack.push(succ); } } } else { self.stack.pop().unwrap(); - if !node_state.has_finished() { - node_state.set_finished(); + if *node_state != NodeState::Finished { + *node_state = NodeState::Finished; return Some(block); } } @@ -152,23 +152,15 @@ impl<'a> Iterator for CfgPostOrder<'a> { } } -#[derive(Default, Debug, Clone, Copy)] -struct NodeState(u8); - -impl NodeState { - fn is_unvisited(self) -> bool { - self.0 == 0 - } - - fn has_finished(self) -> bool { - self.0 == 2 - } - - fn set_visited(&mut self) { - self.0 = 1; - } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum NodeState { + Unvisited, + Visited, + Finished, +} - fn set_finished(&mut self) { - self.0 = 2; +impl Default for NodeState { + fn default() -> Self { + Self::Unvisited } } diff --git a/crates/test-utils/src/lib.rs b/crates/test-utils/src/lib.rs index 0d0c2b4d79..4c22822d3d 100644 --- a/crates/test-utils/src/lib.rs +++ b/crates/test-utils/src/lib.rs @@ -329,7 +329,7 @@ pub fn deploy_contract( contract_name: &str, init_params: &[ethabi::Token], ) -> ContractHarness { - let mut db = driver::NewDb::default(); + let mut db = driver::Db::default(); let compiled_module = match driver::compile_single_file( &mut db, fixture, @@ -366,7 +366,7 @@ pub fn deploy_contract_from_ingot( init_params: &[ethabi::Token], ) -> ContractHarness { let files = test_files::fixture_dir_files(path); - let mut db = driver::NewDb::default(); + let mut db = driver::Db::default(); let compiled_module = match driver::compile_ingot(&mut db, "test", &files, true, true) { Ok(module) => module, Err(error) => { @@ -577,7 +577,7 @@ pub fn compile_solidity_contract( #[allow(dead_code)] pub fn load_contract(address: H160, fixture: &str, contract_name: &str) -> ContractHarness { - let mut db = driver::NewDb::default(); + let mut db = driver::Db::default(); let compiled_module = driver::compile_single_file(&mut db, fixture, test_files::fixture(fixture), true, true) .unwrap_or_else(|err| { diff --git a/crates/tests/src/crashes.rs b/crates/tests/src/crashes.rs index 8d0d686c63..8abec5ce35 100644 --- a/crates/tests/src/crashes.rs +++ b/crates/tests/src/crashes.rs @@ -5,7 +5,7 @@ macro_rules! test_file { #[test] #[wasm_bindgen_test] fn $name() { - let mut db = fe_driver::NewDb::default(); + let mut db = fe_driver::Db::default(); let path = concat!("crashes/", stringify!($name), ".fe"); let src = test_files::fixture(path); fe_driver::compile_single_file(&mut db, path, src, true, true).ok();