Parcourir la source

Partial support for Yul (#824)

Lucas Steuernagel il y a 3 ans
Parent
commit
8367554b06
88 fichiers modifiés avec 5046 ajouts et 1216 suppressions
  1. 2 0
      Cargo.toml
  2. 1 1
      solang-parser/src/pt.rs
  3. 1 1
      solang-parser/src/solidity.lalrpop
  4. 96 42
      src/codegen/cfg.rs
  5. 46 9
      src/codegen/constant_folding.rs
  6. 37 24
      src/codegen/expression.rs
  7. 1 1
      src/codegen/external_functions.rs
  8. 80 29
      src/codegen/mod.rs
  9. 8 7
      src/codegen/statements.rs
  10. 13 11
      src/codegen/strength_reduce.rs
  11. 2 2
      src/codegen/subexpression_elimination/available_expression.rs
  12. 28 6
      src/codegen/subexpression_elimination/available_expression_set.rs
  13. 158 14
      src/codegen/subexpression_elimination/common_subexpression_tracker.rs
  14. 35 10
      src/codegen/subexpression_elimination/expression.rs
  15. 26 14
      src/codegen/subexpression_elimination/mod.rs
  16. 30 16
      src/codegen/subexpression_elimination/operator.rs
  17. 18 10
      src/codegen/subexpression_elimination/tests.rs
  18. 23 9
      src/codegen/undefined_variable.rs
  19. 2 0
      src/codegen/unused_variable.rs
  20. 514 0
      src/codegen/yul/builtin.rs
  21. 196 13
      src/codegen/yul/expression.rs
  22. 120 0
      src/codegen/yul/mod.rs
  23. 443 0
      src/codegen/yul/statements.rs
  24. 428 15
      src/codegen/yul/tests/expression.rs
  25. 24 8
      src/emit/mod.rs
  26. 22 0
      src/sema/ast.rs
  27. 5 5
      src/sema/dotgraphviz.rs
  28. 7 1
      src/sema/expression.rs
  29. 0 9
      src/sema/mutability.rs
  30. 1 1
      src/sema/types.rs
  31. 80 7
      src/sema/yul/ast.rs
  32. 1 0
      src/sema/yul/block.rs
  33. 89 0
      src/sema/yul/builtin.rs
  34. 73 17
      src/sema/yul/expression.rs
  35. 1 0
      src/sema/yul/for_loop.rs
  36. 2 2
      src/sema/yul/functions.rs
  37. 13 7
      src/sema/yul/statements.rs
  38. 15 25
      src/sema/yul/tests/block.rs
  39. 76 15
      src/sema/yul/tests/expression.rs
  40. 5 8
      src/sema/yul/tests/for_loop.rs
  41. 1 1
      src/sema/yul/tests/mod.rs
  42. 19 16
      src/sema/yul/tests/mutability.rs
  43. 6 18
      src/sema/yul/tests/statements.rs
  44. 2 5
      src/sema/yul/tests/switch.rs
  45. 2 2
      src/sema/yul/tests/types.rs
  46. 6 9
      src/sema/yul/tests/unused_variable.rs
  47. 5 5
      src/sema/yul/types.rs
  48. 2 2
      src/sema/yul/unused_variable.rs
  49. 13 2
      tests/codegen.rs
  50. 0 0
      tests/codegen_testcases/solidity/address_cast.sol
  51. 0 0
      tests/codegen_testcases/solidity/array_of_struct.sol
  52. 23 23
      tests/codegen_testcases/solidity/common_subexpression_elimination.sol
  53. 0 0
      tests/codegen_testcases/solidity/const.sol
  54. 0 0
      tests/codegen_testcases/solidity/const_fail.sol
  55. 0 0
      tests/codegen_testcases/solidity/constant_folding.sol
  56. 0 0
      tests/codegen_testcases/solidity/dead_storage.sol
  57. 0 0
      tests/codegen_testcases/solidity/dead_storage_off.sol
  58. 0 0
      tests/codegen_testcases/solidity/llvm_type.sol
  59. 0 0
      tests/codegen_testcases/solidity/multiple.sol
  60. 0 0
      tests/codegen_testcases/solidity/slice1.sol
  61. 0 0
      tests/codegen_testcases/solidity/storage_reference_return.sol
  62. 4 4
      tests/codegen_testcases/solidity/strength_reduce.sol
  63. 0 0
      tests/codegen_testcases/solidity/temp1.sol
  64. 0 0
      tests/codegen_testcases/solidity/temp2.sol
  65. 0 0
      tests/codegen_testcases/solidity/unused_returns.sol
  66. 0 0
      tests/codegen_testcases/solidity/unused_variable_elimination.sol
  67. 164 0
      tests/codegen_testcases/yul/binary_arithmetic_builtins.sol
  68. 60 0
      tests/codegen_testcases/yul/common_builtins.sol
  69. 47 0
      tests/codegen_testcases/yul/common_subexpression_elimination.sol
  70. 59 0
      tests/codegen_testcases/yul/ewasm_builtin.sol
  71. 191 0
      tests/codegen_testcases/yul/expression.sol
  72. 73 0
      tests/codegen_testcases/yul/functions.sol
  73. 476 0
      tests/codegen_testcases/yul/statements.sol
  74. 594 638
      tests/contract_testcases/ewasm/comment_tests.dot
  75. 84 90
      tests/contract_testcases/solana/yul/function_cal_cond.dot
  76. 3 3
      tests/contract_testcases/solana/yul/function_cal_cond.sol
  77. 4 24
      tests/contract_testcases/solana/yul/parse.dot
  78. 4 8
      tests/contract_testcases/solana/yul/return_in_asm.dot
  79. 10 10
      tests/contract_testcases/solana/yul/yul_switch.dot
  80. 1 1
      tests/contract_testcases/substrate/yul/unreachable_after_asm.dot
  81. 1 8
      tests/contract_testcases/substrate/yul/unreachable_after_asm.sol
  82. 1 0
      tests/ewasm_tests/mod.rs
  83. 96 0
      tests/ewasm_tests/yul.rs
  84. 1 0
      tests/solana_tests/mod.rs
  85. 262 0
      tests/solana_tests/yul.rs
  86. 1 0
      tests/substrate_tests/mod.rs
  87. 108 0
      tests/substrate_tests/yul.rs
  88. 1 8
      tests/undefined_variable_detection.rs

+ 2 - 0
Cargo.toml

@@ -46,6 +46,7 @@ solang-parser = { path = "solang-parser", version = "0.1.13" }
 codespan-reporting = "0.11"
 phf = "0.10.1"
 rust-lapper = "1.0"
+bitflags = "1.3"
 
 [dev-dependencies]
 num-derive = "0.3"
@@ -62,6 +63,7 @@ bincode = "1.3"
 ed25519-dalek = "1.0"
 path-slash = "0.1"
 pretty_assertions = "1.2"
+byte-slice-cast = "1.2.1"
 
 [package.metadata.docs.rs]
 no-default-features = true

+ 1 - 1
solang-parser/src/pt.rs

@@ -727,7 +727,7 @@ pub enum YulExpression {
     StringLiteral(StringLiteral, Option<Identifier>),
     Variable(Identifier),
     FunctionCall(Box<YulFunctionCall>),
-    Member(Loc, Box<YulExpression>, Identifier),
+    SuffixAccess(Loc, Box<YulExpression>, Identifier),
 }
 
 #[derive(Debug, PartialEq, Clone)]

+ 1 - 1
solang-parser/src/solidity.lalrpop

@@ -845,7 +845,7 @@ YulFunctionCall: YulFunctionCall = {
 YulPath: YulExpression = {
     <YulIdentifier> => YulExpression::Variable(<>),
     <l:@L> <array:YulPath> "." <member:YulIdentifier> <r:@R> => {
-        YulExpression::Member(Loc::File(file_no, l, r), Box::new(array), member)
+        YulExpression::SuffixAccess(Loc::File(file_no, l, r), Box::new(array), member)
     },
 }
 

+ 96 - 42
src/codegen/cfg.rs

@@ -12,6 +12,7 @@ use super::{
     vartable::{Vars, Vartable},
     vector_to_slice, Options,
 };
+use crate::ast::FunctionAttributes;
 use crate::codegen::subexpression_elimination::common_sub_expression_elimination;
 use crate::codegen::{undefined_variable, Expression, LLVMName};
 use crate::parser::pt;
@@ -338,7 +339,7 @@ pub struct ControlFlowGraph {
     current: usize,
 }
 
-#[derive(Clone)]
+#[derive(Clone, Copy, PartialEq)]
 pub enum ASTFunction {
     SolidityFunction(usize),
     YulFunction(usize),
@@ -503,13 +504,23 @@ impl ControlFlowGraph {
                 self.expr_to_string(contract, ns, l),
                 self.expr_to_string(contract, ns, r)
             ),
-            Expression::Divide(_, _, l, r) => format!(
-                "({} / {})",
+            Expression::SignedDivide(_, _, l, r) => format!(
+                "(signed divide {} / {})",
+                self.expr_to_string(contract, ns, l),
+                self.expr_to_string(contract, ns, r),
+            ),
+            Expression::UnsignedDivide(_, _, l, r) => format!(
+                "(unsigned divide {} / {})",
+                self.expr_to_string(contract, ns, l),
+                self.expr_to_string(contract, ns, r),
+            ),
+            Expression::SignedModulo(_, _, l, r) => format!(
+                "(signed modulo {} % {})",
                 self.expr_to_string(contract, ns, l),
                 self.expr_to_string(contract, ns, r)
             ),
-            Expression::Modulo(_, _, l, r) => format!(
-                "({} % {})",
+            Expression::UnsignedModulo(_, _, l, r) => format!(
+                "(unsigned modulo {} % {})",
                 self.expr_to_string(contract, ns, l),
                 self.expr_to_string(contract, ns, r)
             ),
@@ -537,13 +548,23 @@ impl ControlFlowGraph {
                 ty.to_string(ns),
                 self.expr_to_string(contract, ns, e)
             ),
-            Expression::More(_, l, r) => format!(
-                "({} > {})",
+            Expression::UnsignedMore(_, l, r) => format!(
+                "(unsigned more {} > {})",
                 self.expr_to_string(contract, ns, l),
                 self.expr_to_string(contract, ns, r)
             ),
-            Expression::Less(_, l, r) => format!(
-                "({} < {})",
+            Expression::SignedMore(_, l, r) => format!(
+                "(signed more {} > {})",
+                self.expr_to_string(contract, ns, l),
+                self.expr_to_string(contract, ns, r)
+            ),
+            Expression::UnsignedLess(_, l, r) => format!(
+                "(unsigned less {} < {})",
+                self.expr_to_string(contract, ns, l),
+                self.expr_to_string(contract, ns, r)
+            ),
+            Expression::SignedLess(_, l, r) => format!(
+                "(signed less {} < {})",
                 self.expr_to_string(contract, ns, l),
                 self.expr_to_string(contract, ns, r)
             ),
@@ -1151,7 +1172,16 @@ pub fn generate_cfg(
         cfg.selector = func.selector();
     }
 
-    optimize_and_check_cfg(&mut cfg, ns, function_no, opt);
+    optimize_and_check_cfg(
+        &mut cfg,
+        ns,
+        if let Some(func_no) = function_no {
+            ASTFunction::SolidityFunction(func_no)
+        } else {
+            ASTFunction::None
+        },
+        opt,
+    );
 
     all_cfgs[cfg_no] = cfg;
 }
@@ -1186,13 +1216,13 @@ fn resolve_modifier_call<'a>(
 pub fn optimize_and_check_cfg(
     cfg: &mut ControlFlowGraph,
     ns: &mut Namespace,
-    func_no: Option<usize>,
+    func_no: ASTFunction,
     opt: &Options,
 ) {
     reaching_definitions::find(cfg);
-    if let Some(function) = func_no {
+    if func_no != ASTFunction::None {
         // If there are undefined variables, we raise an error and don't run optimizations
-        if undefined_variable::find_undefined_variables(cfg, ns, function) {
+        if undefined_variable::find_undefined_variables(cfg, ns, func_no) {
             return;
         }
     }
@@ -1210,7 +1240,7 @@ pub fn optimize_and_check_cfg(
     }
 
     // If the function is a default constructor, there is nothing to optimize.
-    if opt.common_subexpression_elimination && func_no.is_some() {
+    if opt.common_subexpression_elimination && func_no != ASTFunction::None {
         common_sub_expression_elimination(cfg, ns);
     }
 }
@@ -1296,19 +1326,7 @@ fn function_cfg(
     };
 
     // populate the argument variables
-    for (i, arg) in func.symtable.arguments.iter().enumerate() {
-        if let Some(pos) = arg {
-            let var = &func.symtable.vars[pos];
-            cfg.add(
-                &mut vartab,
-                Instr::Set {
-                    loc: func.params[i].loc,
-                    res: *pos,
-                    expr: Expression::FunctionArg(var.id.loc, var.ty.clone(), i),
-                },
-            );
-        }
-    }
+    populate_arguments(func, &mut cfg, &mut vartab);
 
     // Hold your breath, this is the trickest part of the codegen ahead.
     // For each contract, the top-level constructor calls the base constructors. The base
@@ -1429,20 +1447,7 @@ fn function_cfg(
     }
 
     // named returns should be populated
-    for (i, pos) in func.symtable.returns.iter().enumerate() {
-        if let Some(name) = &func.returns[i].id {
-            if let Some(expr) = func.returns[i].ty.default(ns) {
-                cfg.add(
-                    &mut vartab,
-                    Instr::Set {
-                        loc: name.loc,
-                        res: *pos,
-                        expr,
-                    },
-                );
-            }
-        }
-    }
+    populate_named_returns(func, ns, &mut cfg, &mut vartab);
 
     for stmt in &func.body {
         statement(
@@ -1493,6 +1498,50 @@ fn function_cfg(
     cfg
 }
 
+/// Populate the arguments of a function
+pub(crate) fn populate_arguments<T: FunctionAttributes>(
+    func: &T,
+    cfg: &mut ControlFlowGraph,
+    vartab: &mut Vartable,
+) {
+    for (i, arg) in func.get_symbol_table().arguments.iter().enumerate() {
+        if let Some(pos) = arg {
+            let var = &func.get_symbol_table().vars[pos];
+            cfg.add(
+                vartab,
+                Instr::Set {
+                    loc: func.get_parameters()[i].loc,
+                    res: *pos,
+                    expr: Expression::FunctionArg(var.id.loc, var.ty.clone(), i),
+                },
+            );
+        }
+    }
+}
+
+/// Populate returns of functions that have named returns
+pub(crate) fn populate_named_returns<T: FunctionAttributes>(
+    func: &T,
+    ns: &Namespace,
+    cfg: &mut ControlFlowGraph,
+    vartab: &mut Vartable,
+) {
+    for (i, pos) in func.get_symbol_table().returns.iter().enumerate() {
+        if let Some(name) = &func.get_returns()[i].id {
+            if let Some(expr) = func.get_returns()[i].ty.default(ns) {
+                cfg.add(
+                    vartab,
+                    Instr::Set {
+                        loc: name.loc,
+                        res: *pos,
+                        expr,
+                    },
+                );
+            }
+        }
+    }
+}
+
 /// Generate the CFG for a modifier on a function
 pub fn generate_modifier_dispatch(
     contract_no: usize,
@@ -1682,13 +1731,18 @@ impl Contract {
         var_contract_no: usize,
         var_no: usize,
         ns: &Namespace,
+        ty: Option<Type>,
     ) -> Expression {
         if let Some(layout) = self
             .layout
             .iter()
             .find(|l| l.contract_no == var_contract_no && l.var_no == var_no)
         {
-            Expression::NumberLiteral(pt::Loc::Codegen, ns.storage_type(), layout.slot.clone())
+            Expression::NumberLiteral(
+                pt::Loc::Codegen,
+                ty.unwrap_or_else(|| ns.storage_type()),
+                layout.slot.clone(),
+            )
         } else {
             panic!("get_storage_slot called on non-storage variable");
         }

+ 46 - 9
src/codegen/constant_folding.rs

@@ -506,7 +506,8 @@ fn expression(
                 left.1 && right.1,
             )
         }
-        Expression::Divide(loc, ty, left, right) => {
+        Expression::UnsignedDivide(loc, ty, left, right)
+        | Expression::SignedDivide(loc, ty, left, right) => {
             let left = expression(left, vars, cfg, ns);
             let right = expression(right, vars, cfg, ns);
 
@@ -518,13 +519,22 @@ fn expression(
                     return bigint_to_expression(loc, ty, left.div(right));
                 }
             }
-
             (
-                Expression::Divide(*loc, ty.clone(), Box::new(left.0), Box::new(right.0)),
+                if matches!(expr, Expression::SignedDivide(..)) {
+                    Expression::SignedDivide(*loc, ty.clone(), Box::new(left.0), Box::new(right.0))
+                } else {
+                    Expression::UnsignedDivide(
+                        *loc,
+                        ty.clone(),
+                        Box::new(left.0),
+                        Box::new(right.0),
+                    )
+                },
                 left.1 && right.1,
             )
         }
-        Expression::Modulo(loc, ty, left, right) => {
+        Expression::SignedModulo(loc, ty, left, right)
+        | Expression::UnsignedModulo(loc, ty, left, right) => {
             let left = expression(left, vars, cfg, ns);
             let right = expression(right, vars, cfg, ns);
 
@@ -538,7 +548,16 @@ fn expression(
             }
 
             (
-                Expression::Modulo(*loc, ty.clone(), Box::new(left.0), Box::new(right.0)),
+                if matches!(expr, Expression::SignedModulo(..)) {
+                    Expression::SignedModulo(*loc, ty.clone(), Box::new(left.0), Box::new(right.0))
+                } else {
+                    Expression::UnsignedModulo(
+                        *loc,
+                        ty.clone(),
+                        Box::new(left.0),
+                        Box::new(right.0),
+                    )
+                },
                 left.1 && right.1,
             )
         }
@@ -843,21 +862,39 @@ fn expression(
                 false,
             )
         }
-        Expression::More(loc, left, right) => {
+        Expression::UnsignedMore(loc, left, right) => {
+            let left = expression(left, vars, cfg, ns);
+            let right = expression(right, vars, cfg, ns);
+
+            (
+                Expression::UnsignedMore(*loc, Box::new(left.0), Box::new(right.0)),
+                false,
+            )
+        }
+        Expression::SignedMore(loc, left, right) => {
+            let left = expression(left, vars, cfg, ns);
+            let right = expression(right, vars, cfg, ns);
+
+            (
+                Expression::SignedMore(*loc, Box::new(left.0), Box::new(right.0)),
+                false,
+            )
+        }
+        Expression::SignedLess(loc, left, right) => {
             let left = expression(left, vars, cfg, ns);
             let right = expression(right, vars, cfg, ns);
 
             (
-                Expression::More(*loc, Box::new(left.0), Box::new(right.0)),
+                Expression::SignedLess(*loc, Box::new(left.0), Box::new(right.0)),
                 false,
             )
         }
-        Expression::Less(loc, left, right) => {
+        Expression::UnsignedLess(loc, left, right) => {
             let left = expression(left, vars, cfg, ns);
             let right = expression(right, vars, cfg, ns);
 
             (
-                Expression::Less(*loc, Box::new(left.0), Box::new(right.0)),
+                Expression::UnsignedLess(*loc, Box::new(left.0), Box::new(right.0)),
                 false,
             )
         }

+ 37 - 24
src/codegen/expression.rs

@@ -32,7 +32,7 @@ pub fn expression(
     match expr {
         ast::Expression::StorageVariable(_, _, var_contract_no, var_no) => {
             // base storage variables should precede contract variables, not overlap
-            ns.contracts[contract_no].get_storage_slot(*var_contract_no, *var_no, ns)
+            ns.contracts[contract_no].get_storage_slot(*var_contract_no, *var_no, ns, None)
         }
         ast::Expression::StorageLoad(loc, ty, expr) => {
             let storage = expression(expr, cfg, contract_no, func, ns, vartab, opt);
@@ -80,18 +80,24 @@ pub fn expression(
                 )
             }
         }
-        ast::Expression::Divide(loc, ty, left, right) => Expression::Divide(
-            *loc,
-            ty.clone(),
-            Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
-            Box::new(expression(right, cfg, contract_no, func, ns, vartab, opt)),
-        ),
-        ast::Expression::Modulo(loc, ty, left, right) => Expression::Modulo(
-            *loc,
-            ty.clone(),
-            Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
-            Box::new(expression(right, cfg, contract_no, func, ns, vartab, opt)),
-        ),
+        ast::Expression::Divide(loc, ty, left, right) => {
+            let l = expression(left, cfg, contract_no, func, ns, vartab, opt);
+            let r = expression(right, cfg, contract_no, func, ns, vartab, opt);
+            if ty.is_signed_int() {
+                Expression::SignedDivide(*loc, ty.clone(), Box::new(l), Box::new(r))
+            } else {
+                Expression::UnsignedDivide(*loc, ty.clone(), Box::new(l), Box::new(r))
+            }
+        }
+        ast::Expression::Modulo(loc, ty, left, right) => {
+            let l = expression(left, cfg, contract_no, func, ns, vartab, opt);
+            let r = expression(right, cfg, contract_no, func, ns, vartab, opt);
+            if ty.is_signed_int() {
+                Expression::SignedModulo(*loc, ty.clone(), Box::new(l), Box::new(r))
+            } else {
+                Expression::UnsignedModulo(*loc, ty.clone(), Box::new(l), Box::new(r))
+            }
+        }
         ast::Expression::Power(loc, ty, unchecked, left, right) => Expression::Power(
             *loc,
             ty.clone(),
@@ -140,21 +146,29 @@ pub fn expression(
             Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
             Box::new(expression(right, cfg, contract_no, func, ns, vartab, opt)),
         ),
-        ast::Expression::More(loc, left, right) => Expression::More(
-            *loc,
-            Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
-            Box::new(expression(right, cfg, contract_no, func, ns, vartab, opt)),
-        ),
+        ast::Expression::More(loc, left, right) => {
+            let l = expression(left, cfg, contract_no, func, ns, vartab, opt);
+            let r = expression(right, cfg, contract_no, func, ns, vartab, opt);
+            if l.ty().is_signed_int() {
+                Expression::SignedMore(*loc, Box::new(l), Box::new(r))
+            } else {
+                Expression::UnsignedMore(*loc, Box::new(l), Box::new(r))
+            }
+        }
         ast::Expression::MoreEqual(loc, left, right) => Expression::MoreEqual(
             *loc,
             Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
             Box::new(expression(right, cfg, contract_no, func, ns, vartab, opt)),
         ),
-        ast::Expression::Less(loc, left, right) => Expression::Less(
-            *loc,
-            Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
-            Box::new(expression(right, cfg, contract_no, func, ns, vartab, opt)),
-        ),
+        ast::Expression::Less(loc, left, right) => {
+            let l = expression(left, cfg, contract_no, func, ns, vartab, opt);
+            let r = expression(right, cfg, contract_no, func, ns, vartab, opt);
+            if l.ty().is_signed_int() {
+                Expression::SignedLess(*loc, Box::new(l), Box::new(r))
+            } else {
+                Expression::UnsignedLess(*loc, Box::new(l), Box::new(r))
+            }
+        }
         ast::Expression::LessEqual(loc, left, right) => Expression::LessEqual(
             *loc,
             Box::new(expression(left, cfg, contract_no, func, ns, vartab, opt)),
@@ -400,7 +414,6 @@ pub fn expression(
                 ast::Expression::ExternalFunction { function_no, .. }
                 | ast::Expression::InternalFunction { function_no, .. } => {
                     let selector = ns.functions[*function_no].selector();
-
                     Expression::NumberLiteral(*loc, Type::Bytes(4), BigInt::from(selector))
                 }
                 _ => {

+ 1 - 1
src/codegen/external_functions.rs

@@ -29,7 +29,7 @@ pub fn add_external_functions(contract_no: usize, ns: &mut Namespace) {
     }
 
     // we've now collected all the functions which are called.
-    while !call_list.solidity.is_empty() {
+    while !call_list.solidity.is_empty() || !call_list.yul.is_empty() {
         let mut new_call_list = CallList::default();
 
         for function_no in &call_list.solidity {

+ 80 - 29
src/codegen/mod.rs

@@ -30,6 +30,7 @@ use std::cmp::Ordering;
 use crate::ast::Function;
 use crate::ast::{FormatArg, RetrieveType, StringLocation, Type};
 use crate::codegen::cfg::ASTFunction;
+use crate::codegen::yul::generate_yul_function_cfg;
 use crate::sema::Recurse;
 use num_bigint::{BigInt, Sign};
 use num_rational::BigRational;
@@ -180,11 +181,11 @@ fn contract(contract_no: usize, ns: &mut Namespace, opt: &Options) {
             cfg_no += 1;
         }
 
-        // TODO: This should be done when we create a CFG for yul functions, which is not the case yet
-        // for yul_fn_no in &ns.contracts[contract_no].yul_functions {
-        //     ns.yul_functions[*yul_fn_no].cfg_no = cfg_no;
-        //     cfg_no += 1;
-        // }
+        // create a cfg number for yul functions
+        for yul_fn_no in &ns.contracts[contract_no].yul_functions {
+            ns.yul_functions[*yul_fn_no].cfg_no = cfg_no;
+            cfg_no += 1;
+        }
 
         all_cfg.resize(cfg_no, ControlFlowGraph::placeholder());
 
@@ -206,9 +207,10 @@ fn contract(contract_no: usize, ns: &mut Namespace, opt: &Options) {
             )
         }
 
-        // for yul_func_no in &ns.contracts[contract_no].yul_functions {
-        //     // TODO: Generate Yul function CFG
-        // }
+        // generate the cfg for yul functions
+        for yul_func_no in ns.contracts[contract_no].yul_functions.clone() {
+            generate_yul_function_cfg(contract_no, yul_func_no, &mut all_cfg, ns, opt);
+        }
 
         // Generate cfg for storage initializers
         let cfg = storage_initializer(contract_no, ns, opt);
@@ -244,8 +246,12 @@ fn storage_initializer(contract_no: usize, ns: &mut Namespace, opt: &Options) ->
         let var = &ns.contracts[layout.contract_no].variables[layout.var_no];
 
         if let Some(init) = &var.initializer {
-            let storage =
-                ns.contracts[contract_no].get_storage_slot(layout.contract_no, layout.var_no, ns);
+            let storage = ns.contracts[contract_no].get_storage_slot(
+                layout.contract_no,
+                layout.var_no,
+                ns,
+                None,
+            );
 
             let value = expression(init, &mut cfg, contract_no, None, ns, &mut vartab, opt);
 
@@ -266,7 +272,7 @@ fn storage_initializer(contract_no: usize, ns: &mut Namespace, opt: &Options) ->
     cfg.vars = vars;
     ns.next_id = next_id;
 
-    optimize_and_check_cfg(&mut cfg, ns, None, opt);
+    optimize_and_check_cfg(&mut cfg, ns, ASTFunction::None, opt);
 
     cfg
 }
@@ -357,7 +363,8 @@ pub enum Expression {
     CodeLiteral(pt::Loc, usize, bool),
     Complement(pt::Loc, Type, Box<Expression>),
     ConstArrayLiteral(pt::Loc, Type, Vec<u32>, Vec<Expression>),
-    Divide(pt::Loc, Type, Box<Expression>, Box<Expression>),
+    UnsignedDivide(pt::Loc, Type, Box<Expression>, Box<Expression>),
+    SignedDivide(pt::Loc, Type, Box<Expression>, Box<Expression>),
     Equal(pt::Loc, Box<Expression>, Box<Expression>),
     ExternalFunction {
         loc: pt::Loc,
@@ -371,11 +378,14 @@ pub enum Expression {
     InternalFunctionCfg(usize),
     Keccak256(pt::Loc, Type, Vec<Expression>),
     List(pt::Loc, Vec<Expression>),
-    Less(pt::Loc, Box<Expression>, Box<Expression>),
+    SignedLess(pt::Loc, Box<Expression>, Box<Expression>),
+    UnsignedLess(pt::Loc, Box<Expression>, Box<Expression>),
     LessEqual(pt::Loc, Box<Expression>, Box<Expression>),
     Load(pt::Loc, Type, Box<Expression>),
-    Modulo(pt::Loc, Type, Box<Expression>, Box<Expression>),
-    More(pt::Loc, Box<Expression>, Box<Expression>),
+    UnsignedModulo(pt::Loc, Type, Box<Expression>, Box<Expression>),
+    SignedModulo(pt::Loc, Type, Box<Expression>, Box<Expression>),
+    SignedMore(pt::Loc, Box<Expression>, Box<Expression>),
+    UnsignedMore(pt::Loc, Box<Expression>, Box<Expression>),
     MoreEqual(pt::Loc, Box<Expression>, Box<Expression>),
     Multiply(pt::Loc, Type, bool, Box<Expression>, Box<Expression>),
     Not(pt::Loc, Box<Expression>),
@@ -441,8 +451,10 @@ impl CodeLocation for Expression {
             | Expression::FormatString(loc, ..)
             | Expression::LessEqual(loc, ..)
             | Expression::BoolLiteral(loc, ..)
-            | Expression::Divide(loc, ..)
-            | Expression::Modulo(loc, ..)
+            | Expression::UnsignedDivide(loc, ..)
+            | Expression::SignedDivide(loc, ..)
+            | Expression::UnsignedModulo(loc, ..)
+            | Expression::SignedModulo(loc, ..)
             | Expression::Power(loc, ..)
             | Expression::BitwiseOr(loc, ..)
             | Expression::BitwiseAnd(loc, ..)
@@ -451,7 +463,8 @@ impl CodeLocation for Expression {
             | Expression::NotEqual(loc, ..)
             | Expression::Complement(loc, ..)
             | Expression::UnaryMinus(loc, ..)
-            | Expression::Less(loc, ..)
+            | Expression::UnsignedLess(loc, ..)
+            | Expression::SignedLess(loc, ..)
             | Expression::Not(loc, ..)
             | Expression::StructLiteral(loc, ..)
             | Expression::ArrayLiteral(loc, ..)
@@ -467,7 +480,8 @@ impl CodeLocation for Expression {
             | Expression::RationalNumberLiteral(loc, ..)
             | Expression::AllocDynamicArray(loc, ..)
             | Expression::BytesCast(loc, ..)
-            | Expression::More(loc, ..)
+            | Expression::SignedMore(loc, ..)
+            | Expression::UnsignedMore(loc, ..)
             | Expression::ZeroExt(loc, ..) => *loc,
 
             Expression::InternalFunctionCfg(_) | Expression::Poison | Expression::Undefined(_) => {
@@ -496,12 +510,15 @@ impl Recurse for Expression {
 
             Expression::BitwiseAnd(_, _, left, right)
             | Expression::BitwiseOr(_, _, left, right)
-            | Expression::Divide(_, _, left, right)
+            | Expression::UnsignedDivide(_, _, left, right)
+            | Expression::SignedDivide(_, _, left, right)
             | Expression::Equal(_, left, right)
-            | Expression::Less(_, left, right)
+            | Expression::UnsignedLess(_, left, right)
+            | Expression::SignedLess(_, left, right)
             | Expression::LessEqual(_, left, right)
             | Expression::BitwiseXor(_, _, left, right)
-            | Expression::More(_, left, right)
+            | Expression::SignedMore(_, left, right)
+            | Expression::UnsignedMore(_, left, right)
             | Expression::MoreEqual(_, left, right)
             | Expression::Multiply(_, _, _, left, right)
             | Expression::NotEqual(_, left, right)
@@ -588,8 +605,10 @@ impl RetrieveType for Expression {
             | Expression::NumberLiteral(_, ty, ..)
             | Expression::Multiply(_, ty, ..)
             | Expression::Subtract(_, ty, ..)
-            | Expression::Divide(_, ty, ..)
-            | Expression::Modulo(_, ty, ..)
+            | Expression::SignedDivide(_, ty, ..)
+            | Expression::UnsignedDivide(_, ty, ..)
+            | Expression::SignedModulo(_, ty, ..)
+            | Expression::UnsignedModulo(_, ty, ..)
             | Expression::Power(_, ty, ..)
             | Expression::BitwiseOr(_, ty, ..)
             | Expression::BitwiseAnd(_, ty, ..)
@@ -613,10 +632,12 @@ impl RetrieveType for Expression {
 
             Expression::BoolLiteral(..)
             | Expression::MoreEqual(..)
-            | Expression::More(..)
+            | Expression::SignedMore(..)
+            | Expression::UnsignedMore(..)
             | Expression::Not(..)
             | Expression::NotEqual(..)
-            | Expression::Less(..)
+            | Expression::UnsignedLess(..)
+            | Expression::SignedLess(..)
             | Expression::Equal(..)
             | Expression::StringCompare(..)
             | Expression::LessEqual(..) => Type::Bool,
@@ -857,6 +878,20 @@ impl Expression {
                 Expression::BytesCast(self.loc(), from.clone(), to.clone(), Box::new(self.clone()))
             }
 
+            (Type::Bool, Type::Int(_) | Type::Uint(_)) => {
+                Expression::Cast(self.loc(), to.clone(), Box::new(self.clone()))
+            }
+
+            (Type::Int(_) | Type::Uint(_), Type::Bool) => Expression::NotEqual(
+                self.loc(),
+                Box::new(Expression::NumberLiteral(
+                    self.loc(),
+                    self.ty(),
+                    BigInt::zero(),
+                )),
+                Box::new(self.clone()),
+            ),
+
             (Type::Bytes(_), Type::Uint(_))
             | (Type::Bytes(_), Type::Int(_))
             | (Type::Uint(_), Type::Bytes(_))
@@ -928,7 +963,13 @@ impl Expression {
                     Box::new(filter(left, ctx)),
                     Box::new(filter(right, ctx)),
                 ),
-                Expression::Divide(loc, ty, left, right) => Expression::Divide(
+                Expression::UnsignedDivide(loc, ty, left, right) => Expression::UnsignedDivide(
+                    *loc,
+                    ty.clone(),
+                    Box::new(filter(left, ctx)),
+                    Box::new(filter(right, ctx)),
+                ),
+                Expression::SignedDivide(loc, ty, left, right) => Expression::SignedDivide(
                     *loc,
                     ty.clone(),
                     Box::new(filter(left, ctx)),
@@ -995,12 +1036,22 @@ impl Expression {
                     from.clone(),
                     Box::new(filter(expr, ctx)),
                 ),
-                Expression::More(loc, left, right) => Expression::More(
+                Expression::SignedMore(loc, left, right) => Expression::SignedMore(
+                    *loc,
+                    Box::new(filter(left, ctx)),
+                    Box::new(filter(right, ctx)),
+                ),
+                Expression::UnsignedMore(loc, left, right) => Expression::UnsignedMore(
+                    *loc,
+                    Box::new(filter(left, ctx)),
+                    Box::new(filter(right, ctx)),
+                ),
+                Expression::UnsignedLess(loc, left, right) => Expression::UnsignedLess(
                     *loc,
                     Box::new(filter(left, ctx)),
                     Box::new(filter(right, ctx)),
                 ),
-                Expression::Less(loc, left, right) => Expression::Less(
+                Expression::SignedLess(loc, left, right) => Expression::SignedLess(
                     *loc,
                     Box::new(filter(left, ctx)),
                     Box::new(filter(right, ctx)),

+ 8 - 7
src/codegen/statements.rs

@@ -11,6 +11,7 @@ use crate::ast;
 use crate::codegen::unused_variable::{
     should_remove_assignment, should_remove_variable, SideEffectsCheckParameters,
 };
+use crate::codegen::yul::inline_assembly_cfg;
 use crate::codegen::{Builtin, Expression};
 use crate::parser::pt;
 use crate::parser::pt::CodeLocation;
@@ -23,7 +24,7 @@ use num_traits::Zero;
 use tiny_keccak::{Hasher, Keccak};
 
 /// Resolve a statement, which might be a block of statements or an entire body of a function
-pub fn statement(
+pub(crate) fn statement(
     stmt: &Statement,
     func: &Function,
     cfg: &mut ControlFlowGraph,
@@ -625,8 +626,8 @@ pub fn statement(
             }
         }
 
-        Statement::Assembly(..) => {
-            unimplemented!("Assembly block codegen not yet ready");
+        Statement::Assembly(inline_assembly, ..) => {
+            inline_assembly_cfg(inline_assembly, contract_no, ns, cfg, vartab, opt);
         }
     }
 }
@@ -1362,22 +1363,22 @@ impl LoopScopes {
         LoopScopes(LinkedList::new())
     }
 
-    fn new_scope(&mut self, break_bb: usize, continue_bb: usize) {
+    pub(crate) fn new_scope(&mut self, break_bb: usize, continue_bb: usize) {
         self.0.push_front(LoopScope {
             break_bb,
             continue_bb,
         })
     }
 
-    fn leave_scope(&mut self) -> LoopScope {
+    pub(crate) fn leave_scope(&mut self) -> LoopScope {
         self.0.pop_front().expect("should be in loop scope")
     }
 
-    fn do_break(&mut self) -> usize {
+    pub(crate) fn do_break(&mut self) -> usize {
         self.0.front().unwrap().break_bb
     }
 
-    fn do_continue(&mut self) -> usize {
+    pub(crate) fn do_continue(&mut self) -> usize {
         self.0.front().unwrap().continue_bb
     }
 }

+ 13 - 11
src/codegen/strength_reduce.rs

@@ -298,7 +298,8 @@ fn expression_reduce(expr: &Expression, vars: &Variables, ns: &mut Namespace) ->
 
                 expr.clone()
             }
-            Expression::Divide(loc, ty, left, right) => {
+            Expression::UnsignedDivide(loc, ty, left, right)
+            | Expression::SignedDivide(loc, ty, left, right) => {
                 let bits = ty.bits(ns) as usize;
 
                 if bits >= 128 {
@@ -352,7 +353,7 @@ fn expression_reduce(expr: &Expression, vars: &Variables, ns: &mut Namespace) ->
                                 return Expression::SignExt(
                                     *loc,
                                     ty.clone(),
-                                    Box::new(Expression::Divide(
+                                    Box::new(Expression::UnsignedDivide(
                                         *loc,
                                         Type::Int(64),
                                         Box::new(left.as_ref().clone().cast(&Type::Int(64), ns)),
@@ -375,7 +376,7 @@ fn expression_reduce(expr: &Expression, vars: &Variables, ns: &mut Namespace) ->
                             return Expression::ZeroExt(
                                 *loc,
                                 ty.clone(),
-                                Box::new(Expression::Divide(
+                                Box::new(Expression::UnsignedDivide(
                                     *loc,
                                     Type::Uint(64),
                                     Box::new(left.as_ref().clone().cast(&Type::Uint(64), ns)),
@@ -388,7 +389,8 @@ fn expression_reduce(expr: &Expression, vars: &Variables, ns: &mut Namespace) ->
 
                 expr.clone()
             }
-            Expression::Modulo(loc, ty, left, right) => {
+            Expression::SignedModulo(loc, ty, left, right)
+            | Expression::UnsignedModulo(loc, ty, left, right) => {
                 let bits = ty.bits(ns) as usize;
 
                 if bits >= 128 {
@@ -440,7 +442,7 @@ fn expression_reduce(expr: &Expression, vars: &Variables, ns: &mut Namespace) ->
                                 return Expression::SignExt(
                                     *loc,
                                     ty.clone(),
-                                    Box::new(Expression::Modulo(
+                                    Box::new(Expression::SignedModulo(
                                         *loc,
                                         Type::Int(64),
                                         Box::new(left.as_ref().clone().cast(&Type::Int(64), ns)),
@@ -463,7 +465,7 @@ fn expression_reduce(expr: &Expression, vars: &Variables, ns: &mut Namespace) ->
                             return Expression::ZeroExt(
                                 *loc,
                                 ty.clone(),
-                                Box::new(Expression::Modulo(
+                                Box::new(Expression::UnsignedModulo(
                                     *loc,
                                     Type::Uint(64),
                                     Box::new(left.as_ref().clone().cast(&Type::Uint(64), ns)),
@@ -1173,7 +1175,7 @@ fn expression_values(expr: &Expression, vars: &Variables, ns: &Namespace) -> Has
                 })
                 .collect()
         }
-        Expression::More(_, left, right) => {
+        Expression::SignedMore(_, left, right) | Expression::UnsignedMore(_, left, right) => {
             let ty = left.ty();
 
             let left = expression_values(left, vars, ns);
@@ -1289,7 +1291,7 @@ fn expression_values(expr: &Expression, vars: &Variables, ns: &Namespace) -> Has
                 })
                 .collect()
         }
-        Expression::Less(_, left, right) => {
+        Expression::SignedLess(_, left, right) | Expression::UnsignedLess(_, left, right) => {
             let ty = left.ty();
 
             let left = expression_values(left, vars, ns);
@@ -2158,7 +2160,7 @@ fn expresson_known_bits() {
     vars.insert(1, var2);
 
     // should always be true
-    let expr = Expression::More(
+    let expr = Expression::UnsignedMore(
         loc,
         Box::new(Expression::Variable(loc, Type::Uint(64), 0)),
         Box::new(Expression::Variable(loc, Type::Uint(64), 1)),
@@ -2210,7 +2212,7 @@ fn expresson_known_bits() {
     vars.insert(1, var2);
 
     // should always be true
-    let expr = Expression::More(
+    let expr = Expression::UnsignedMore(
         loc,
         Box::new(Expression::Variable(loc, Type::Uint(64), 0)),
         Box::new(Expression::Variable(loc, Type::Uint(64), 1)),
@@ -2266,7 +2268,7 @@ fn expresson_known_bits() {
     vars.insert(1, var2);
 
     // should always be true
-    let expr = Expression::Less(
+    let expr = Expression::UnsignedLess(
         loc,
         Box::new(Expression::Variable(loc, Type::Uint(64), 0)),
         Box::new(Expression::Variable(loc, Type::Uint(64), 1)),

+ 2 - 2
src/codegen/subexpression_elimination/available_expression.rs

@@ -49,7 +49,7 @@ impl AvailableExpression {
     ) -> NodeId {
         let operation = exp.get_ave_operator();
         let new_node = Rc::new(RefCell::new(BasicExpression {
-            expr_type: ExpressionType::BinaryOperation(left, right, operation),
+            expr_type: ExpressionType::BinaryOperation(left, right, operation.clone()),
             expression_id: self.global_id_counter,
             children: Default::default(),
             available_variable: AvailableVariable::Unavailable,
@@ -94,7 +94,7 @@ impl AvailableExpression {
     ) -> NodeId {
         let operation = exp.get_ave_operator();
         let new_node = Rc::new(RefCell::new(BasicExpression {
-            expr_type: ExpressionType::UnaryOperation(parent, operation),
+            expr_type: ExpressionType::UnaryOperation(parent, operation.clone()),
             expression_id: self.global_id_counter,
             children: Default::default(),
             available_variable: AvailableVariable::Unavailable,

+ 28 - 6
src/codegen/subexpression_elimination/available_expression_set.rs

@@ -67,7 +67,11 @@ impl AvailableExpressionSet {
     }
 
     /// When we exit two blocks, we must intersect their set of available expressions
-    pub fn intersect_sets(&mut self, set_2: &AvailableExpressionSet) {
+    pub fn intersect_sets(
+        &mut self,
+        set_2: &AvailableExpressionSet,
+        cst: &CommonSubExpressionTracker,
+    ) {
         self.expr_map
             .retain(|key, value| AvailableExpressionSet::check_intersection(key, value, set_2));
 
@@ -82,7 +86,9 @@ impl AvailableExpressionSet {
                 let node_2_id = set_2.expr_map.get(key).unwrap();
 
                 node_1.on_parent_block = true;
-                node_1.parent_block = std::cmp::min(
+                // Find the common ancestor of both blocks. The deepest block after which there are
+                // multiple paths to both blocks.
+                node_1.parent_block = cst.find_parent_block(
                     node_1.parent_block,
                     set_2.expression_memory[node_2_id].borrow().parent_block,
                 );
@@ -132,7 +138,9 @@ impl AvailableExpressionSet {
         let operator = exp.get_ave_operator();
 
         if let Some(exp_id) = self.expr_map.get(&ExpressionType::BinaryOperation(
-            left_id, right_id, operator,
+            left_id,
+            right_id,
+            operator.clone(),
         )) {
             Some(*exp_id)
         } else {
@@ -177,12 +185,27 @@ impl AvailableExpressionSet {
         Some(ave.add_binary_node(exp, self, left_id, right_id))
     }
 
-    /// Add an expression to the graph if it is not there
+    /// Add expression to the graph and check if it is available on a parallel branch.
     pub fn gen_expression(
         &mut self,
         exp: &Expression,
         ave: &mut AvailableExpression,
         cst: &mut CommonSubExpressionTracker,
+    ) -> Option<NodeId> {
+        let id = self.gen_expression_aux(exp, ave, cst);
+        if let Some(id) = id {
+            let node = &*self.expression_memory.get(&id).unwrap().borrow();
+            cst.check_availability_on_branches(&node.expr_type);
+        }
+        id
+    }
+
+    /// Add an expression to the graph if it is not there
+    pub fn gen_expression_aux(
+        &mut self,
+        exp: &Expression,
+        ave: &mut AvailableExpression,
+        cst: &mut CommonSubExpressionTracker,
     ) -> Option<NodeId> {
         if let Some(id) = self.find_expression(exp) {
             self.add_to_cst(exp, &id, cst);
@@ -194,7 +217,6 @@ impl AvailableExpressionSet {
                 return Some(ave.add_variable_node(exp, self));
             }
 
-            //Expression::ConstantVariable(..)
             Expression::NumberLiteral(..)
             | Expression::BoolLiteral(..)
             | Expression::BytesLiteral(..) => {
@@ -383,7 +405,7 @@ impl AvailableExpressionSet {
 
         let operator = exp.get_ave_operator();
         let expr_type_1 =
-            ExpressionType::BinaryOperation(left_id.unwrap(), right_id.unwrap(), operator);
+            ExpressionType::BinaryOperation(left_id.unwrap(), right_id.unwrap(), operator.clone());
         let expr_type_2 =
             ExpressionType::BinaryOperation(right_id.unwrap(), left_id.unwrap(), operator);
 

+ 158 - 14
src/codegen/subexpression_elimination/common_subexpression_tracker.rs

@@ -7,7 +7,8 @@ use crate::parser::pt::OptionalCodeLocation;
 use crate::parser::pt::{Identifier, Loc};
 use crate::sema::ast::RetrieveType;
 use crate::sema::ast::{Namespace, Type};
-use std::collections::HashMap;
+use bitflags::bitflags;
+use std::collections::{HashMap, VecDeque};
 
 #[derive(Clone)]
 struct CommonSubexpression {
@@ -20,6 +21,15 @@ struct CommonSubexpression {
     on_parent_block: Option<usize>,
 }
 
+bitflags! {
+  struct Color: u8 {
+     const WHITE = 0;
+     const BLUE = 2;
+     const YELLOW = 4;
+     const GREEN = 6;
+  }
+}
+
 #[derive(Default, Clone)]
 pub struct CommonSubExpressionTracker {
     inserted_subexpressions: HashMap<ExpressionType, usize>,
@@ -29,9 +39,17 @@ pub struct CommonSubExpressionTracker {
     cur_block: usize,
     new_cfg_instr: Vec<Instr>,
     parent_block_instr: Vec<(usize, Instr)>,
+    /// The CFG is a cyclic graph. In order properly find the lowest common block,
+    /// we transformed it in a DAG, removing cycles from loops.
+    cfg_dag: Vec<Vec<usize>>,
 }
 
 impl CommonSubExpressionTracker {
+    /// Save the DAG to the CST
+    pub fn set_dag(&mut self, dag: Vec<Vec<usize>>) {
+        self.cfg_dag = dag;
+    }
+
     /// Add an expression to the tracker.
     pub fn add_expression(
         &mut self,
@@ -96,6 +114,41 @@ impl CommonSubExpressionTracker {
         }
     }
 
+    /// Check if an expression is available on another branch and find the correct block to place it.
+    /// We must make sure that all paths to both branches pass through such a block.
+    /// eg.
+    /// '''
+    /// if (condition) {
+    ///    x = a + b;
+    /// }
+    ///
+    /// y = a + b;
+    /// '''
+    ///
+    /// This code can be optimized to:
+    ///
+    /// '''
+    /// temp = a + b;
+    /// if (condition) {
+    ///     x = temp;
+    /// }
+    /// y = temp;
+    /// '''
+    ///
+    /// This avoids the repeated calculation of 'a+b'
+    pub fn check_availability_on_branches(&mut self, expr_type: &ExpressionType) {
+        if let Some(expr_id) = self.inserted_subexpressions.get(expr_type) {
+            let expr_block = self.common_subexpressions[*expr_id].block;
+            let expr_block = self.common_subexpressions[*expr_id]
+                .on_parent_block
+                .unwrap_or(expr_block);
+            let ancestor = self.find_parent_block(self.cur_block, expr_block);
+            if ancestor != expr_block {
+                self.common_subexpressions[*expr_id].on_parent_block = Some(ancestor);
+            }
+        }
+    }
+
     /// Try exchanging an expression by a temporary variable.
     pub fn check_variable_available(
         &mut self,
@@ -115,19 +168,6 @@ impl CommonSubExpressionTracker {
         }
 
         if !common_expression.in_cfg {
-            // If there is an expression available, but not for the current block.
-            /*
-            if (condition) {
-                x = a + b;
-            }
-
-            y = a+b;
-             */
-            // 'a+b' is available, but not for the block that contains the branch.
-            if self.cur_block != common_expression.block {
-                return None;
-            }
-
             let new_instr = Instr::Set {
                 loc: Loc::Codegen,
                 res: common_expression.var_no.unwrap(),
@@ -174,4 +214,108 @@ impl CommonSubExpressionTracker {
     pub fn set_cur_block(&mut self, block_no: usize) {
         self.cur_block = block_no;
     }
+
+    /// For common subexpression elimination to work properly, we need to find the common parent of
+    /// two blocks. The parent is the deepest block in which every path from the entry block to both
+    /// 'block_1' and 'block_2' passes through such a block.
+    pub fn find_parent_block(&self, block_1: usize, block_2: usize) -> usize {
+        if block_1 == block_2 {
+            return block_1;
+        }
+        let mut colors: Vec<Color> = vec![Color::WHITE; self.cfg_dag.len()];
+        let mut visited: Vec<bool> = vec![false; self.cfg_dag.len()];
+        /*
+        Given a DAG (directed acyclic graph), we color all the ancestors of 'block_1' with yellow.
+        Then, we color every ancestor of 'block_2' with blue. As the mixture of blue and yellow
+        results in green, green blocks are all possible common ancestors!
+
+        We can't add colors to code. Here, bitwise ORing 2 to a block's color mean painting with yellow.
+        Likewise, bitwise ORing 4 means painting with blue. Green blocks have 6 (2|4) as their color
+        number.
+
+         */
+
+        self.coloring_dfs(block_1, 0, Color::BLUE, &mut colors, &mut visited);
+        visited.fill(false);
+        self.coloring_dfs(block_2, 0, Color::YELLOW, &mut colors, &mut visited);
+
+        /*
+        Having a bunch of green block, which of them are we looking for?
+        We must choose the deepest block, in which all paths from the entry block to both block_1
+        and block_2 pass through this block.
+
+        Have a look at the 'find_ancestor' function to know more about the algorithm.
+         */
+        self.find_ancestor(0, &colors)
+    }
+
+    /// Given a colored graph, find the lowest common ancestor.
+    fn find_ancestor(&self, start_block: usize, colors: &[Color]) -> usize {
+        let mut candidate = start_block;
+        let mut queue: VecDeque<usize> = VecDeque::new();
+        let mut visited: Vec<bool> = vec![false; self.cfg_dag.len()];
+
+        visited[start_block] = true;
+        queue.push_back(start_block);
+
+        let mut six_child: usize = 0;
+        // This is a BFS (breadth first search) traversal
+        while let Some(cur_block) = queue.pop_front() {
+            let mut not_ancestors: usize = 0;
+            for child in &self.cfg_dag[cur_block] {
+                if colors[*child] == Color::WHITE {
+                    // counting the number of children which are not ancestors from neither block_1
+                    // nor block_2
+                    not_ancestors += 1;
+                }
+
+                if colors[*child] == Color::GREEN {
+                    // This is the possible candidate to search next.
+                    six_child = *child;
+                }
+            }
+
+            // If the current block has only one child that leads to both block_1 and block_2, it is
+            // a candidate to be the lowest common ancestor.
+            if not_ancestors + 1 == self.cfg_dag[cur_block].len() && !visited[six_child] {
+                visited[six_child] = true;
+                queue.push_back(six_child);
+                candidate = six_child;
+            }
+        }
+
+        candidate
+    }
+
+    /// This function performs a DFS (depth first search) to color all the ancestors of a block.
+    fn coloring_dfs(
+        &self,
+        search_block: usize,
+        cur_block: usize,
+        color: Color,
+        colors: &mut Vec<Color>,
+        visited: &mut Vec<bool>,
+    ) -> bool {
+        if colors[cur_block].contains(color) {
+            return true;
+        }
+
+        if visited[cur_block] {
+            return false;
+        }
+
+        visited[cur_block] = true;
+        if cur_block == search_block {
+            colors[cur_block].insert(color);
+            return true;
+        }
+
+        for next in &self.cfg_dag[cur_block] {
+            if self.coloring_dfs(search_block, *next, color, colors, visited) {
+                colors[cur_block].insert(color);
+            }
+        }
+
+        colors[cur_block].contains(color)
+    }
 }

+ 35 - 10
src/codegen/subexpression_elimination/expression.rs

@@ -58,15 +58,28 @@ impl Expression {
                 Box::new(left.clone()),
                 Box::new(right.clone()),
             ),
+            Expression::UnsignedDivide(loc, expr_type, ..) => Expression::UnsignedDivide(
+                *loc,
+                expr_type.clone(),
+                Box::new(left.clone()),
+                Box::new(right.clone()),
+            ),
 
-            Expression::Divide(loc, expr_type, ..) => Expression::Divide(
+            Expression::SignedDivide(loc, expr_type, ..) => Expression::SignedDivide(
                 *loc,
                 expr_type.clone(),
                 Box::new(left.clone()),
                 Box::new(right.clone()),
             ),
 
-            Expression::Modulo(loc, expr_type, ..) => Expression::Modulo(
+            Expression::SignedModulo(loc, expr_type, ..) => Expression::SignedModulo(
+                *loc,
+                expr_type.clone(),
+                Box::new(left.clone()),
+                Box::new(right.clone()),
+            ),
+
+            Expression::UnsignedModulo(loc, expr_type, ..) => Expression::UnsignedModulo(
                 *loc,
                 expr_type.clone(),
                 Box::new(left.clone()),
@@ -96,12 +109,20 @@ impl Expression {
                 *check,
             ),
 
-            Expression::More(loc, ..) => {
-                Expression::More(*loc, Box::new(left.clone()), Box::new(right.clone()))
+            Expression::SignedMore(loc, ..) => {
+                Expression::SignedMore(*loc, Box::new(left.clone()), Box::new(right.clone()))
+            }
+
+            Expression::UnsignedMore(loc, ..) => {
+                Expression::UnsignedMore(*loc, Box::new(left.clone()), Box::new(right.clone()))
+            }
+
+            Expression::UnsignedLess(loc, ..) => {
+                Expression::UnsignedLess(*loc, Box::new(left.clone()), Box::new(right.clone()))
             }
 
-            Expression::Less(loc, ..) => {
-                Expression::Less(*loc, Box::new(left.clone()), Box::new(right.clone()))
+            Expression::SignedLess(loc, ..) => {
+                Expression::SignedLess(*loc, Box::new(left.clone()), Box::new(right.clone()))
             }
 
             Expression::MoreEqual(loc, ..) => {
@@ -209,13 +230,17 @@ impl Expression {
     pub fn get_non_commutative_operands(&self) -> Option<(&Expression, &Expression)> {
         match self {
             Expression::Subtract(_, _, _, left, right)
-            | Expression::Divide(_, _, left, right)
-            | Expression::Modulo(_, _, left, right)
+            | Expression::UnsignedDivide(_, _, left, right)
+            | Expression::SignedDivide(_, _, left, right)
+            | Expression::SignedModulo(_, _, left, right)
+            | Expression::UnsignedModulo(_, _, left, right)
             | Expression::Power(_, _, _, left, right)
             | Expression::ShiftLeft(_, _, left, right)
             | Expression::ShiftRight(_, _, left, right, _)
-            | Expression::More(_, left, right)
-            | Expression::Less(_, left, right)
+            | Expression::SignedMore(_, left, right)
+            | Expression::UnsignedMore(_, left, right)
+            | Expression::SignedLess(_, left, right)
+            | Expression::UnsignedLess(_, left, right)
             | Expression::MoreEqual(_, left, right)
             | Expression::LessEqual(_, left, right) => Some((left, right)),
 

+ 26 - 14
src/codegen/subexpression_elimination/mod.rs

@@ -57,7 +57,7 @@ pub struct BasicExpression {
 }
 
 /// Type of constant to streamline the use of a hashmap
-#[derive(Eq, PartialEq, Hash, Clone)]
+#[derive(Eq, PartialEq, Hash, Clone, Debug)]
 pub enum ConstantType {
     Bool(bool),
     Bytes(Vec<u8>),
@@ -65,7 +65,7 @@ pub enum ConstantType {
 }
 
 /// The type of expression that a node represents
-#[derive(Clone, PartialEq, Hash, Eq)]
+#[derive(Clone, PartialEq, Hash, Eq, Debug)]
 pub enum ExpressionType {
     BinaryOperation(NodeId, NodeId, Operator),
     UnaryOperation(NodeId, Operator),
@@ -90,20 +90,22 @@ pub fn common_sub_expression_elimination(cfg: &mut ControlFlowGraph, ns: &mut Na
     let mut cst = CommonSubExpressionTracker::default();
 
     let mut sets: HashMap<usize, AvailableExpressionSet> = HashMap::new();
-    let visiting_order = find_visiting_order(cfg);
+    let (visiting_order, dag) = find_visiting_order(cfg);
+    cst.set_dag(dag);
     sets.insert(0, AvailableExpressionSet::default());
 
     // First pass: identify common subexpressions using available expressiona analysis
     for (block_no, cycle) in &visiting_order {
         let cur_block = &cfg.blocks[*block_no];
         ave.set_cur_block(*block_no);
+        cst.set_cur_block(*block_no);
         let mut cur_set = sets.remove(block_no).unwrap();
         kill_loop_variables(cur_block, &mut cur_set, *cycle);
         for instr in cur_block.instr.iter() {
             cur_set.process_instruction(instr, &mut ave, &mut cst);
         }
 
-        add_neighbor_blocks(cur_block, &cur_set, block_no, &mut sets);
+        add_neighbor_blocks(cur_block, &cur_set, block_no, &mut sets, &cst);
     }
 
     cst.create_variables(ns, cfg);
@@ -127,7 +129,7 @@ pub fn common_sub_expression_elimination(cfg: &mut ControlFlowGraph, ns: &mut Na
         }
 
         cur_block.instr = new_instructions;
-        add_neighbor_blocks(cur_block, &cur_set, block_no, &mut sets);
+        add_neighbor_blocks(cur_block, &cur_set, block_no, &mut sets, &cst);
     }
 
     cst.add_parent_block_instructions(cfg);
@@ -139,10 +141,11 @@ fn add_neighbor_blocks(
     cur_set: &AvailableExpressionSet,
     block_no: &usize,
     sets: &mut HashMap<usize, AvailableExpressionSet>,
+    cst: &CommonSubExpressionTracker,
 ) {
     for edge in block_edges(cur_block) {
         if let Some(set) = sets.get_mut(&edge) {
-            set.intersect_sets(cur_set);
+            set.intersect_sets(cur_set, cst);
         } else {
             sets.insert(edge, cur_set.clone_for_parent_block(*block_no));
         }
@@ -160,14 +163,17 @@ fn kill_loop_variables(block: &BasicBlock, cur_set: &mut AvailableExpressionSet,
     }
 }
 
-/// Find the correct visiting order for the CFG traversal. The visiting order should be the same
-/// as the execution order.
-fn find_visiting_order(cfg: &ControlFlowGraph) -> Vec<(usize, bool)> {
+/// Find the correct visiting order for the CFG traversal, using topological sorting. The visiting
+/// order should be the same as the execution order. This function also returns a DAG for the
+/// execution graph. This helps us find the lowest common ancestor later.
+fn find_visiting_order(cfg: &ControlFlowGraph) -> (Vec<(usize, bool)>, Vec<Vec<usize>>) {
     let mut order: Vec<(usize, bool)> = Vec::with_capacity(cfg.blocks.len());
     let mut visited: HashSet<usize> = HashSet::new();
     let mut stack: HashSet<usize> = HashSet::new();
     let mut has_cycle: Vec<bool> = vec![false; cfg.blocks.len()];
     let mut degrees: Vec<i32> = vec![0; cfg.blocks.len()];
+    let mut dag: Vec<Vec<usize>> = Vec::new();
+    dag.resize(cfg.blocks.len(), vec![]);
 
     cfg_dfs(
         0,
@@ -176,6 +182,7 @@ fn find_visiting_order(cfg: &ControlFlowGraph) -> Vec<(usize, bool)> {
         &mut stack,
         &mut degrees,
         &mut has_cycle,
+        &mut dag,
     );
 
     let mut queue: VecDeque<usize> = VecDeque::new();
@@ -191,7 +198,7 @@ fn find_visiting_order(cfg: &ControlFlowGraph) -> Vec<(usize, bool)> {
         }
     }
 
-    order
+    (order, dag)
 }
 
 /// Run DFS (depth first search) in the CFG to find cycles.
@@ -202,24 +209,29 @@ fn cfg_dfs(
     stack: &mut HashSet<usize>,
     degrees: &mut Vec<i32>,
     has_cycle: &mut Vec<bool>,
-) {
+    dag: &mut Vec<Vec<usize>>,
+) -> bool {
     if visited.contains(&block_no) {
-        return;
+        return true;
     }
 
     if stack.contains(&block_no) {
         degrees[block_no] -= 1;
         has_cycle[block_no] = true;
-        return;
+        return false;
     }
 
     stack.insert(block_no);
 
     for edge in block_edges(&cfg.blocks[block_no]) {
         degrees[edge] += 1;
-        cfg_dfs(edge, cfg, visited, stack, degrees, has_cycle);
+        if cfg_dfs(edge, cfg, visited, stack, degrees, has_cycle, dag) {
+            dag[block_no].push(edge);
+        }
     }
 
     stack.remove(&block_no);
     visited.insert(block_no);
+
+    true
 }

+ 30 - 16
src/codegen/subexpression_elimination/operator.rs

@@ -1,21 +1,30 @@
+use crate::ast::Type;
 use crate::codegen::Expression;
 
 /// This enum defines operator types for the graph
-#[derive(PartialEq, Eq, Hash, Copy, Clone)]
+#[derive(PartialEq, Eq, Hash, Clone, Debug)]
 pub enum Operator {
     Add,
     Subtract,
     Multiply,
-    Divide,
+    SignedDivide,
+    UnsignedDivide,
     Modulo,
+    SignedModulo,
+    UnsignedModulo,
     Power,
     BitwiseOr,
     BitwiseAnd,
     BitwiseXor,
     ShiftLeft,
-    ShiftRight,
+    SignedShiftRight,
+    UnsignedShiftRight,
     More,
+    SignedMore,
+    UnsignedMore,
     Less,
+    SignedLess,
+    UnsignedLess,
     MoreEqual,
     LessEqual,
     Equal,
@@ -24,10 +33,10 @@ pub enum Operator {
     StringCompare,
     //Unary operations
     Not,
-    ZeroExt,
-    SignExt,
-    Trunc,
-    Cast,
+    ZeroExt(Type),
+    SignExt(Type),
+    Trunc(Type),
+    Cast(Type),
     BytesCast,
     UnaryMinus,
     Complement,
@@ -40,23 +49,28 @@ impl Expression {
             Expression::Add(..) => Operator::Add,
             Expression::Subtract(..) => Operator::Subtract,
             Expression::Multiply(..) => Operator::Multiply,
-            Expression::Divide(..) => Operator::Divide,
-            Expression::Modulo(..) => Operator::Modulo,
+            Expression::SignedDivide(..) => Operator::SignedDivide,
+            Expression::UnsignedDivide(..) => Operator::UnsignedDivide,
+            Expression::SignedModulo(..) => Operator::SignedModulo,
+            Expression::UnsignedModulo(..) => Operator::UnsignedModulo,
             Expression::Power(..) => Operator::Power,
             Expression::BitwiseOr(..) => Operator::BitwiseOr,
             Expression::BitwiseAnd(..) => Operator::BitwiseAnd,
             Expression::BitwiseXor(..) => Operator::BitwiseXor,
             Expression::ShiftLeft(..) => Operator::ShiftLeft,
-            Expression::ShiftRight(..) => Operator::ShiftRight,
+            Expression::ShiftRight(_, _, _, _, true) => Operator::SignedShiftRight,
+            Expression::ShiftRight(_, _, _, _, false) => Operator::UnsignedShiftRight,
             Expression::Not(..) => Operator::Not,
-            Expression::ZeroExt(..) => Operator::ZeroExt,
-            Expression::SignExt(..) => Operator::SignExt,
-            Expression::Trunc(..) => Operator::Trunc,
-            Expression::Cast(..) => Operator::Cast,
+            Expression::ZeroExt(_, ty, ..) => Operator::ZeroExt(ty.clone()),
+            Expression::SignExt(_, ty, ..) => Operator::SignExt(ty.clone()),
+            Expression::Trunc(_, ty, ..) => Operator::Trunc(ty.clone()),
+            Expression::Cast(_, ty, ..) => Operator::Cast(ty.clone()),
             Expression::BytesCast(..) => Operator::BytesCast,
             Expression::UnaryMinus(..) => Operator::UnaryMinus,
-            Expression::More(..) => Operator::More,
-            Expression::Less(..) => Operator::Less,
+            Expression::SignedMore(..) => Operator::SignedMore,
+            Expression::UnsignedMore(..) => Operator::UnsignedMore,
+            Expression::SignedLess(..) => Operator::SignedLess,
+            Expression::UnsignedLess(..) => Operator::UnsignedLess,
             Expression::MoreEqual(..) => Operator::MoreEqual,
             Expression::LessEqual(..) => Operator::LessEqual,
             Expression::Equal(..) => Operator::Equal,

+ 18 - 10
src/codegen/subexpression_elimination/tests.rs

@@ -236,7 +236,7 @@ fn complex_expression() {
         Box::new(cte.clone()),
         Box::new(arg.clone()),
     );
-    let div = Expression::Divide(
+    let div = Expression::SignedDivide(
         Loc::Codegen,
         Type::Int(8),
         Box::new(sum.clone()),
@@ -257,7 +257,7 @@ fn complex_expression() {
         Box::new(div.clone()),
         true,
     );
-    let modu = Expression::Modulo(
+    let modu = Expression::SignedModulo(
         Loc::Codegen,
         Type::Int(8),
         Box::new(cte.clone()),
@@ -374,7 +374,7 @@ fn kill() {
         Box::new(cte.clone()),
         Box::new(arg.clone()),
     );
-    let div = Expression::Divide(
+    let div = Expression::SignedDivide(
         Loc::Codegen,
         Type::Int(8),
         Box::new(sum.clone()),
@@ -395,7 +395,7 @@ fn kill() {
         Box::new(div.clone()),
         true,
     );
-    let modu = Expression::Modulo(
+    let modu = Expression::SignedModulo(
         Loc::Codegen,
         Type::Int(8),
         Box::new(cte.clone()),
@@ -463,7 +463,7 @@ fn clone() {
         Box::new(cte.clone()),
         Box::new(arg.clone()),
     );
-    let div = Expression::Divide(
+    let div = Expression::SignedDivide(
         Loc::Codegen,
         Type::Int(8),
         Box::new(sum.clone()),
@@ -484,7 +484,7 @@ fn clone() {
         Box::new(div.clone()),
         true,
     );
-    let modu = Expression::Modulo(
+    let modu = Expression::SignedModulo(
         Loc::Codegen,
         Type::Int(8),
         Box::new(cte.clone()),
@@ -550,7 +550,7 @@ fn intersect() {
         Box::new(cte.clone()),
         Box::new(arg.clone()),
     );
-    let div = Expression::Divide(
+    let div = Expression::SignedDivide(
         Loc::Codegen,
         Type::Int(8),
         Box::new(sum.clone()),
@@ -571,7 +571,7 @@ fn intersect() {
         Box::new(div.clone()),
         true,
     );
-    let modu = Expression::Modulo(
+    let modu = Expression::SignedModulo(
         Loc::Codegen,
         Type::Int(8),
         Box::new(cte.clone()),
@@ -605,10 +605,16 @@ fn intersect() {
     let mut ave = AvailableExpression::default();
     let mut set = AvailableExpressionSet::default();
     let mut cst = CommonSubExpressionTracker::default();
+    let cfg_dag = vec![vec![1, 2], vec![], vec![1]];
+    cst.set_dag(cfg_dag);
 
+    ave.set_cur_block(0);
+    cst.set_cur_block(0);
     set.process_instruction(&instr, &mut ave, &mut cst);
     set.process_instruction(&instr2, &mut ave, &mut cst);
-    let mut set_2 = set.clone_for_parent_block(1);
+    let mut set_2 = set.clone_for_parent_block(0);
+    cst.set_cur_block(2);
+    ave.set_cur_block(2);
     set.kill(1);
 
     let sum2 = Expression::Add(
@@ -634,9 +640,11 @@ fn intersect() {
     };
 
     set.process_instruction(&instr3, &mut ave, &mut cst);
+    cst.set_cur_block(1);
+    ave.set_cur_block(1);
     set_2.process_instruction(&instr3, &mut ave, &mut cst);
 
-    set_2.intersect_sets(&set);
+    set_2.intersect_sets(&set, &cst);
 
     // Available expressions
     assert!(set_2.find_expression(&unary).is_some());

+ 23 - 9
src/codegen/undefined_variable.rs

@@ -1,4 +1,5 @@
-use crate::codegen::cfg::{ControlFlowGraph, Instr};
+use crate::ast::Type;
+use crate::codegen::cfg::{ASTFunction, ControlFlowGraph, Instr};
 use crate::codegen::reaching_definitions::{apply_transfers, VarDefs};
 use crate::codegen::{Builtin, Expression};
 use crate::parser::pt::{Loc, StorageLocation};
@@ -10,7 +11,7 @@ use std::collections::HashMap;
 /// We use this struct in expression.recurse function to provide all the
 /// parameters for detecting undefined variables
 pub struct FindUndefinedVariablesParams<'a> {
-    pub func_no: usize,
+    pub func_no: ASTFunction,
     pub defs: &'a VarDefs,
     pub ns: &'a mut Namespace,
     pub cfg: &'a ControlFlowGraph,
@@ -22,7 +23,7 @@ pub struct FindUndefinedVariablesParams<'a> {
 pub fn find_undefined_variables(
     cfg: &ControlFlowGraph,
     ns: &mut Namespace,
-    func_no: usize,
+    func_no: ASTFunction,
 ) -> bool {
     let mut diagnostics: HashMap<usize, Diagnostic> = HashMap::new();
     for block in &cfg.blocks {
@@ -49,7 +50,7 @@ pub fn find_undefined_variables(
 
 /// Checks for undefined variables in an expression associated to an instruction
 pub fn check_variables_in_expression(
-    func_no: usize,
+    func_no: ASTFunction,
     instr: &Instr,
     defs: &VarDefs,
     ns: &mut Namespace,
@@ -77,10 +78,18 @@ pub fn find_undefined_variables_in_expression(
 ) -> bool {
     match &exp {
         Expression::Variable(_, _, pos) => {
-            if let (Some(def_map), Some(var)) = (
-                ctx.defs.get(pos),
-                ctx.ns.functions[ctx.func_no].symtable.vars.get(pos),
-            ) {
+            let variable = match ctx.func_no {
+                ASTFunction::YulFunction(func_no) => {
+                    ctx.ns.yul_functions[func_no].symtable.vars.get(pos)
+                }
+                ASTFunction::SolidityFunction(func_no) => {
+                    ctx.ns.functions[func_no].symtable.vars.get(pos)
+                }
+
+                ASTFunction::None => None,
+            };
+
+            if let (Some(def_map), Some(var)) = (ctx.defs.get(pos), variable) {
                 for (def, modified) in def_map {
                     if let Instr::Set {
                         expr: instr_expr, ..
@@ -88,7 +97,10 @@ pub fn find_undefined_variables_in_expression(
                     {
                         // If an undefined definition reaches this read and the variable
                         // has not been modified since its definition, it is undefined
-                        if matches!(instr_expr, Expression::Undefined(_)) && !*modified {
+                        if matches!(instr_expr, Expression::Undefined(_))
+                            && !*modified
+                            && !matches!(var.ty, Type::Array(..))
+                        {
                             add_diagnostic(var, pos, &exp.loc(), ctx.diagnostics);
                         }
                     }
@@ -137,3 +149,5 @@ fn add_diagnostic(
         message: "Variable read before being defined".to_string(),
     });
 }
+
+// TODO: undefined variables are not yet compatible with Yul

+ 2 - 0
src/codegen/unused_variable.rs

@@ -76,3 +76,5 @@ pub fn should_remove_variable(pos: &usize, func: &Function, opt: &Options) -> bo
 
     false
 }
+
+// TODO: unused variables should remove Yul assignments!

+ 514 - 0
src/codegen/yul/builtin.rs

@@ -0,0 +1,514 @@
+use crate::ast::{Namespace, RetrieveType, Type};
+use crate::codegen::cfg::{ControlFlowGraph, Instr};
+use crate::codegen::vartable::Vartable;
+use crate::codegen::yul::expression::expression;
+use crate::codegen::{Builtin, Expression, Options};
+use crate::sema::expression::coerce_number;
+use crate::sema::yul::ast;
+use crate::sema::yul::builtin::YulBuiltInFunction;
+use num_bigint::BigInt;
+use num_traits::{FromPrimitive, Zero};
+use solang_parser::pt;
+
+impl Expression {
+    fn to_number_literal(&self) -> Expression {
+        match self {
+            Expression::BoolLiteral(loc, value) => {
+                let val = if *value {
+                    BigInt::from(1)
+                } else {
+                    BigInt::from(0)
+                };
+                Expression::NumberLiteral(*loc, Type::Uint(256), val)
+            }
+            _ => panic!("expression should not be converted into number literal"),
+        }
+    }
+}
+
+/// Transfrom YUL builtin functions into CFG instructions
+pub(crate) fn process_builtin(
+    loc: &pt::Loc,
+    builtin_ty: &YulBuiltInFunction,
+    args: &[ast::YulExpression],
+    contract_no: usize,
+    ns: &Namespace,
+    vartab: &mut Vartable,
+    cfg: &mut ControlFlowGraph,
+    opt: &Options,
+) -> Expression {
+    match builtin_ty {
+        YulBuiltInFunction::Not => {
+            let exp = expression(&args[0], contract_no, ns, vartab, cfg, opt);
+            Expression::Complement(*loc, exp.ty(), Box::new(exp))
+        }
+
+        YulBuiltInFunction::IsZero => {
+            let left = expression(&args[0], contract_no, ns, vartab, cfg, opt);
+            let right = Expression::NumberLiteral(pt::Loc::Codegen, left.ty(), BigInt::from(0));
+
+            Expression::Equal(*loc, Box::new(left), Box::new(right))
+        }
+
+        YulBuiltInFunction::Add
+        | YulBuiltInFunction::Sub
+        | YulBuiltInFunction::Mul
+        | YulBuiltInFunction::Div
+        | YulBuiltInFunction::SDiv
+        | YulBuiltInFunction::Mod
+        | YulBuiltInFunction::SMod
+        | YulBuiltInFunction::Lt
+        | YulBuiltInFunction::Gt
+        | YulBuiltInFunction::Slt
+        | YulBuiltInFunction::Sgt
+        | YulBuiltInFunction::Eq
+        | YulBuiltInFunction::And
+        | YulBuiltInFunction::Or
+        | YulBuiltInFunction::Xor
+        | YulBuiltInFunction::Shl
+        | YulBuiltInFunction::Shr
+        | YulBuiltInFunction::Sar
+        | YulBuiltInFunction::Exp => {
+            process_binary_arithmetic(loc, builtin_ty, args, contract_no, ns, vartab, cfg, opt)
+        }
+
+        YulBuiltInFunction::Byte => {
+            byte_builtin(loc, args, contract_no, ns, cfg, vartab, opt)
+        }
+
+        YulBuiltInFunction::AddMod
+        | YulBuiltInFunction::MulMod => {
+            let left = expression(&args[0], contract_no, ns, vartab, cfg, opt);
+            let right = expression(&args[1], contract_no, ns, vartab, cfg, opt);
+            let (left, right) = equalize_types(left, right, ns);
+
+            let main_expr = if matches!(builtin_ty, YulBuiltInFunction::AddMod) {
+                Expression::Add(*loc, left.ty(), false, Box::new(left), Box::new(right))
+            } else {
+                Expression::Multiply(*loc, left.ty(), false, Box::new(left), Box::new(right))
+            };
+
+            let mod_arg = expression(&args[2], contract_no, ns, vartab, cfg, opt);
+            let (mod_left, mod_right) = equalize_types(main_expr, mod_arg, ns);
+            let codegen_expr = Expression::UnsignedModulo(*loc, mod_left.ty(), Box::new(mod_left), Box::new(mod_right.clone()));
+            branch_if_zero(mod_right, codegen_expr, ns, cfg, vartab)
+        }
+
+        YulBuiltInFunction::SignExtend
+        | YulBuiltInFunction::Keccak256
+        | YulBuiltInFunction::Pop
+        | YulBuiltInFunction::Pc
+        | YulBuiltInFunction::ChainId
+        | YulBuiltInFunction::BaseFee
+        // Memory functions: need to convert between number to pointer type
+        | YulBuiltInFunction::MLoad
+        | YulBuiltInFunction::MStore
+        | YulBuiltInFunction::MStore8
+        | YulBuiltInFunction::MSize
+        // Storage function: need to think about how to deal with pointer size and the size of chunk to load
+        | YulBuiltInFunction::SStore
+        | YulBuiltInFunction::SLoad
+        // Calldata functions: the same problems with other memory functions
+        | YulBuiltInFunction::CallDataLoad
+        | YulBuiltInFunction::CallDataSize
+        | YulBuiltInFunction::CallDataCopy
+        // Functions that manage code memory
+        | YulBuiltInFunction::CodeSize
+        | YulBuiltInFunction::CodeCopy
+        | YulBuiltInFunction::ExtCodeSize
+        | YulBuiltInFunction::ExtCodeCopy
+        | YulBuiltInFunction::ExtCodeHash
+        // Functions that manage return data
+        | YulBuiltInFunction::ReturnDataSize
+        | YulBuiltInFunction::ReturnDataCopy
+        // Functions that manage contracts
+        | YulBuiltInFunction::Create
+        | YulBuiltInFunction::Create2
+        | YulBuiltInFunction::Call
+        | YulBuiltInFunction::CallCode
+        | YulBuiltInFunction::DelegateCall
+        | YulBuiltInFunction::StaticCall
+        // Return and revert also load from memory, so we first need to solve mload and mstore builtins
+        | YulBuiltInFunction::Return
+        | YulBuiltInFunction::Stop // Stop is the same as return(0, 0)
+        | YulBuiltInFunction::Revert
+        // Log functions
+        | YulBuiltInFunction::Log0
+        | YulBuiltInFunction::Log1
+        | YulBuiltInFunction::Log2
+        | YulBuiltInFunction::Log3
+        | YulBuiltInFunction::Log4
+        // origin is the same as tx.origin and is not implemented
+        | YulBuiltInFunction::Origin
+        => {
+            let function_ty = builtin_ty.get_prototype_info();
+            unreachable!("{} yul builtin not implemented", function_ty.name);
+        }
+
+        YulBuiltInFunction::Gas => {
+            Expression::Builtin(*loc, vec![Type::Uint(64)], Builtin::Gasleft, vec![])
+        }
+
+        YulBuiltInFunction::Address => {
+            Expression::Builtin(*loc, vec![Type::Address(false)], Builtin::GetAddress, vec![])
+        }
+
+        YulBuiltInFunction::Balance => {
+            let addr = expression(&args[0], contract_no, ns, vartab, cfg, opt).cast(&Type::Address(false), ns);
+            Expression::Builtin(*loc, vec![Type::Value], Builtin::Balance,vec![addr])
+        }
+
+        YulBuiltInFunction::SelfBalance => {
+            let addr = Expression::Builtin(*loc, vec![Type::Contract(contract_no)], Builtin::GetAddress,vec![]);
+            Expression::Builtin(*loc, vec![Type::Value], Builtin::Balance, vec![addr])
+        }
+
+        YulBuiltInFunction::Caller => {
+            Expression::Builtin(*loc, vec![Type::Address(true)], Builtin::Sender, vec![])
+        }
+
+        YulBuiltInFunction::CallValue => {
+            Expression::Builtin(*loc, vec![Type::Value], Builtin::Value, vec![])
+        }
+
+        YulBuiltInFunction::SelfDestruct => {
+            let recipient = expression(&args[0], contract_no, ns, vartab, cfg, opt).cast(&Type::Address(true), ns);
+            cfg.add(vartab, Instr::SelfDestruct { recipient });
+            Expression::Poison
+        }
+
+        YulBuiltInFunction::Invalid => {
+            cfg.add(vartab, Instr::AssertFailure { expr: None });
+            Expression::Poison
+        }
+
+        YulBuiltInFunction::GasPrice => {
+            Expression::Builtin(*loc, vec![Type::Uint(64)], Builtin::Gasprice, vec![])
+        }
+
+        YulBuiltInFunction::BlockHash => {
+            let arg = expression(&args[0], contract_no, ns, vartab, cfg, opt).cast(&Type::Uint(64), ns);
+            Expression::Builtin(*loc, vec![Type::Uint(256)], Builtin::BlockHash, vec![arg])
+        }
+
+        YulBuiltInFunction::CoinBase => {
+            Expression::Builtin(*loc, vec![Type::Address(false)], Builtin::BlockCoinbase, vec![])
+        }
+
+        YulBuiltInFunction::Timestamp => {
+            Expression::Builtin(*loc, vec![Type::Uint(64)], Builtin::Timestamp, vec![])
+        }
+
+        YulBuiltInFunction::Number => {
+            Expression::Builtin(*loc, vec![Type::Uint(64)], Builtin::BlockNumber, vec![])
+        }
+
+        YulBuiltInFunction::Difficulty => {
+            Expression::Builtin(*loc, vec![Type::Uint(256)], Builtin::BlockDifficulty, vec![])
+        }
+
+        YulBuiltInFunction::GasLimit => {
+            Expression::Builtin(*loc, vec![Type::Uint(64)], Builtin::GasLimit, vec![])
+        }
+    }
+}
+
+/// Process arithmetic operations with two arguments
+fn process_binary_arithmetic(
+    loc: &pt::Loc,
+    builtin_ty: &YulBuiltInFunction,
+    args: &[ast::YulExpression],
+    contract_no: usize,
+    ns: &Namespace,
+    vartab: &mut Vartable,
+    cfg: &mut ControlFlowGraph,
+    opt: &Options,
+) -> Expression {
+    let left = expression(&args[0], contract_no, ns, vartab, cfg, opt);
+    let right = expression(&args[1], contract_no, ns, vartab, cfg, opt);
+    let (left, right) = equalize_types(left, right, ns);
+
+    match builtin_ty {
+        YulBuiltInFunction::Add => {
+            Expression::Add(*loc, left.ty(), true, Box::new(left), Box::new(right))
+        }
+        YulBuiltInFunction::Sub => {
+            Expression::Subtract(*loc, left.ty(), true, Box::new(left), Box::new(right))
+        }
+        YulBuiltInFunction::Mul => {
+            Expression::Multiply(*loc, left.ty(), true, Box::new(left), Box::new(right))
+        }
+        YulBuiltInFunction::Div => {
+            let expr = Expression::UnsignedDivide(
+                *loc,
+                left.ty(),
+                Box::new(left),
+                Box::new(right.clone()),
+            );
+            branch_if_zero(right, expr, ns, cfg, vartab)
+        }
+        YulBuiltInFunction::SDiv => {
+            let expr =
+                Expression::SignedDivide(*loc, left.ty(), Box::new(left), Box::new(right.clone()));
+            branch_if_zero(right, expr, ns, cfg, vartab)
+        }
+        YulBuiltInFunction::Mod => {
+            let expr = Expression::UnsignedModulo(
+                *loc,
+                left.ty(),
+                Box::new(left),
+                Box::new(right.clone()),
+            );
+            branch_if_zero(right, expr, ns, cfg, vartab)
+        }
+        YulBuiltInFunction::SMod => {
+            let expr =
+                Expression::SignedModulo(*loc, left.ty(), Box::new(left), Box::new(right.clone()));
+            branch_if_zero(right, expr, ns, cfg, vartab)
+        }
+        YulBuiltInFunction::Exp => {
+            Expression::Power(*loc, left.ty(), true, Box::new(left), Box::new(right))
+        }
+        YulBuiltInFunction::Lt => Expression::UnsignedLess(*loc, Box::new(left), Box::new(right)),
+        YulBuiltInFunction::Gt => Expression::UnsignedMore(*loc, Box::new(left), Box::new(right)),
+        YulBuiltInFunction::Slt => Expression::SignedLess(*loc, Box::new(left), Box::new(right)),
+        YulBuiltInFunction::Sgt => Expression::SignedMore(*loc, Box::new(left), Box::new(right)),
+        YulBuiltInFunction::Eq => Expression::Equal(*loc, Box::new(left), Box::new(right)),
+        YulBuiltInFunction::And => {
+            Expression::BitwiseAnd(*loc, left.ty(), Box::new(left), Box::new(right))
+        }
+        YulBuiltInFunction::Or => {
+            Expression::BitwiseOr(*loc, left.ty(), Box::new(left), Box::new(right))
+        }
+        YulBuiltInFunction::Xor => {
+            Expression::BitwiseXor(*loc, left.ty(), Box::new(left), Box::new(right))
+        }
+        // For bit shifting, the syntax is the following: shr(x, y) shifts right y by x bits.
+        YulBuiltInFunction::Shl => {
+            Expression::ShiftLeft(*loc, left.ty(), Box::new(right), Box::new(left))
+        }
+        YulBuiltInFunction::Shr => {
+            Expression::ShiftRight(*loc, left.ty(), Box::new(right), Box::new(left), false)
+        }
+        YulBuiltInFunction::Sar => {
+            Expression::ShiftRight(*loc, left.ty(), Box::new(right), Box::new(left), true)
+        }
+
+        _ => panic!("This is not a binary arithmetic operation!"),
+    }
+}
+
+/// This function matches the type between the right and left hand sides of operations
+fn equalize_types(
+    mut left: Expression,
+    mut right: Expression,
+    ns: &Namespace,
+) -> (Expression, Expression) {
+    if matches!(
+        left,
+        Expression::BytesLiteral(..) | Expression::BoolLiteral(..)
+    ) {
+        left = left.to_number_literal();
+    }
+
+    if matches!(
+        right,
+        Expression::BytesLiteral(..) | Expression::BoolLiteral(..)
+    ) {
+        right = right.to_number_literal();
+    }
+
+    let left_ty = left.ty();
+    let right_ty = right.ty();
+    if left_ty != right_ty {
+        let mut vec = Vec::new();
+        let casted_type = coerce_number(
+            &left_ty,
+            &pt::Loc::Codegen,
+            &right_ty,
+            &pt::Loc::Codegen,
+            false,
+            false,
+            ns,
+            &mut vec,
+        )
+        .unwrap();
+
+        left = left.cast(&casted_type, ns);
+        right = right.cast(&casted_type, ns);
+    }
+
+    (left, right)
+}
+
+/// In some Yul functions, we need to branch if the argument is zero.
+/// e.g. 'x := div(y, 0)'. Division by zero returns 0 in Yul.
+fn branch_if_zero(
+    variable: Expression,
+    codegen_expr: Expression,
+    ns: &Namespace,
+    cfg: &mut ControlFlowGraph,
+    vartab: &mut Vartable,
+) -> Expression {
+    let temp = vartab.temp_anonymous(&Type::Uint(256));
+    let cond = Expression::Equal(
+        pt::Loc::Codegen,
+        Box::new(variable.clone()),
+        Box::new(Expression::NumberLiteral(
+            pt::Loc::Codegen,
+            variable.ty(),
+            BigInt::zero(),
+        )),
+    );
+
+    let then = cfg.new_basic_block("then".to_string());
+    let else_ = cfg.new_basic_block("else".to_string());
+    let endif = cfg.new_basic_block("endif".to_string());
+    cfg.add(
+        vartab,
+        Instr::BranchCond {
+            cond,
+            true_block: then,
+            false_block: else_,
+        },
+    );
+
+    cfg.set_basic_block(then);
+    vartab.new_dirty_tracker(ns.next_id);
+    cfg.add(
+        vartab,
+        Instr::Set {
+            loc: pt::Loc::Codegen,
+            res: temp,
+            expr: Expression::NumberLiteral(pt::Loc::Codegen, Type::Uint(256), BigInt::from(0)),
+        },
+    );
+    cfg.add(vartab, Instr::Branch { block: endif });
+
+    cfg.set_basic_block(else_);
+    cfg.add(
+        vartab,
+        Instr::Set {
+            loc: pt::Loc::Codegen,
+            res: temp,
+            expr: codegen_expr,
+        },
+    );
+    cfg.add(vartab, Instr::Branch { block: endif });
+    let mut phis = vartab.pop_dirty_tracker();
+    phis.insert(temp);
+    cfg.set_phis(endif, phis);
+    cfg.set_basic_block(endif);
+
+    Expression::Variable(pt::Loc::Codegen, Type::Uint(256), temp)
+}
+
+/// This function implements the byte builtin
+fn byte_builtin(
+    loc: &pt::Loc,
+    args: &[ast::YulExpression],
+    contract_no: usize,
+    ns: &Namespace,
+    cfg: &mut ControlFlowGraph,
+    vartab: &mut Vartable,
+    opt: &Options,
+) -> Expression {
+    let offset = expression(&args[0], contract_no, ns, vartab, cfg, opt).cast(&Type::Uint(256), ns);
+    let cond = Expression::MoreEqual(
+        *loc,
+        Box::new(offset.clone()),
+        Box::new(Expression::NumberLiteral(
+            *loc,
+            Type::Uint(256),
+            BigInt::from(32),
+        )),
+    );
+
+    let temp = vartab.temp_anonymous(&Type::Uint(256));
+
+    let then = cfg.new_basic_block("then".to_string());
+    let else_ = cfg.new_basic_block("else".to_string());
+    let endif = cfg.new_basic_block("endif".to_string());
+
+    cfg.add(
+        vartab,
+        Instr::BranchCond {
+            cond,
+            true_block: then,
+            false_block: else_,
+        },
+    );
+
+    cfg.set_basic_block(then);
+    vartab.new_dirty_tracker(ns.next_id);
+    cfg.add(
+        vartab,
+        Instr::Set {
+            loc: pt::Loc::Codegen,
+            res: temp,
+            expr: Expression::NumberLiteral(pt::Loc::Codegen, Type::Uint(256), BigInt::zero()),
+        },
+    );
+    cfg.add(vartab, Instr::Branch { block: endif });
+
+    cfg.set_basic_block(else_);
+
+    // The following implements the operation (arg[1] >> (8 * (31 - arg[0]))) & 0xff
+    let op_31_sub_arg0 = Expression::Subtract(
+        *loc,
+        Type::Uint(256),
+        false,
+        Box::new(Expression::NumberLiteral(
+            *loc,
+            Type::Uint(256),
+            BigInt::from(31),
+        )),
+        Box::new(offset),
+    );
+    let op_eight_times = Expression::ShiftLeft(
+        *loc,
+        Type::Uint(256),
+        Box::new(op_31_sub_arg0),
+        Box::new(Expression::NumberLiteral(
+            *loc,
+            Type::Uint(256),
+            BigInt::from(3),
+        )),
+    );
+    let op_shift_right = Expression::ShiftRight(
+        *loc,
+        Type::Uint(256),
+        Box::new(
+            expression(&args[1], contract_no, ns, vartab, cfg, opt).cast(&Type::Uint(256), ns),
+        ),
+        Box::new(op_eight_times),
+        false,
+    );
+    let masked_result = Expression::BitwiseAnd(
+        *loc,
+        Type::Uint(256),
+        Box::new(op_shift_right),
+        Box::new(Expression::NumberLiteral(
+            *loc,
+            Type::Uint(256),
+            BigInt::from_u8(255).unwrap(),
+        )),
+    );
+
+    cfg.add(
+        vartab,
+        Instr::Set {
+            loc: *loc,
+            res: temp,
+            expr: masked_result,
+        },
+    );
+    cfg.add(vartab, Instr::Branch { block: endif });
+
+    let mut phis = vartab.pop_dirty_tracker();
+    phis.insert(temp);
+    cfg.set_phis(endif, phis);
+    cfg.set_basic_block(endif);
+
+    Expression::Variable(pt::Loc::Codegen, Type::Uint(256), temp)
+}

+ 196 - 13
src/codegen/yul/expression.rs

@@ -1,18 +1,19 @@
-use crate::ast::{Function, Namespace, Type};
+use crate::ast::{Namespace, Type};
 use crate::codegen;
-use crate::codegen::cfg::ControlFlowGraph;
+use crate::codegen::cfg::{ControlFlowGraph, Instr, InternalCallTy};
 use crate::codegen::vartable::Vartable;
-use crate::codegen::{Expression, Options};
+use crate::codegen::yul::builtin::process_builtin;
+use crate::codegen::{Builtin, Expression, Options};
 use crate::sema::yul::ast;
-use num_bigint::BigInt;
-use solang_parser::pt::StorageLocation;
+use crate::sema::yul::ast::YulSuffix;
+use num_bigint::{BigInt, Sign};
+use solang_parser::pt;
+use solang_parser::pt::{Loc, StorageLocation};
 
-// TODO: This is a workaround to avoid compiler warnings during development.
-#[allow(dead_code)]
+/// Transform AST expressions into CFG expressions
 pub(crate) fn expression(
     expr: &ast::YulExpression,
     contract_no: usize,
-    func: Option<&Function>,
     ns: &Namespace,
     vartab: &mut Vartable,
     cfg: &mut ControlFlowGraph,
@@ -37,7 +38,7 @@ pub(crate) fn expression(
             Expression::NumberLiteral(*loc, ty.clone(), value.clone())
         }
         ast::YulExpression::StringLiteral(loc, value, ty) => {
-            Expression::BytesLiteral(*loc, ty.clone(), value.clone())
+            Expression::NumberLiteral(*loc, ty.clone(), BigInt::from_bytes_be(Sign::Plus, value))
         }
         ast::YulExpression::YulLocalVariable(loc, ty, var_no) => {
             Expression::Variable(*loc, ty.clone(), *var_no)
@@ -50,7 +51,7 @@ pub(crate) fn expression(
                     .unwrap(),
                 cfg,
                 contract_no,
-                func,
+                None,
                 ns,
                 vartab,
                 opt,
@@ -60,7 +61,7 @@ pub(crate) fn expression(
             ns.constants[*var_no].initializer.as_ref().unwrap(),
             cfg,
             contract_no,
-            func,
+            None,
             ns,
             vartab,
             opt,
@@ -72,8 +73,190 @@ pub(crate) fn expression(
         ast::YulExpression::SolidityLocalVariable(loc, ty, _, var_no) => {
             Expression::Variable(*loc, ty.clone(), *var_no)
         }
+        ast::YulExpression::SuffixAccess(loc, expr, suffix) => {
+            process_suffix_access(loc, expr, suffix, contract_no, vartab, cfg, ns, opt)
+        }
+        ast::YulExpression::FunctionCall(_, function_no, args, _) => {
+            let mut returns =
+                process_function_call(*function_no, args, contract_no, vartab, cfg, ns, opt);
+            assert_eq!(returns.len(), 1);
+            returns.remove(0)
+        }
 
-        // TODO: This is a workaround to avoid compiler errors
-        _ => Expression::Poison,
+        ast::YulExpression::BuiltInCall(loc, builtin_ty, args) => {
+            process_builtin(loc, builtin_ty, args, contract_no, ns, vartab, cfg, opt)
+        }
     }
 }
+
+/// Transform YUL suffixes into CFG instructions
+fn process_suffix_access(
+    loc: &pt::Loc,
+    expr: &ast::YulExpression,
+    suffix: &YulSuffix,
+    contract_no: usize,
+    vartab: &mut Vartable,
+    cfg: &mut ControlFlowGraph,
+    ns: &Namespace,
+    opt: &Options,
+) -> Expression {
+    match suffix {
+        YulSuffix::Slot => match expr {
+            ast::YulExpression::StorageVariable(_, _, contract_no, var_no) => {
+                return ns.contracts[*contract_no].get_storage_slot(
+                    *contract_no,
+                    *var_no,
+                    ns,
+                    Some(Type::Uint(256)),
+                );
+            }
+            ast::YulExpression::SolidityLocalVariable(
+                loc,
+                _,
+                Some(StorageLocation::Storage(_)),
+                var_no,
+            ) => {
+                return Expression::Variable(*loc, Type::Uint(256), *var_no);
+            }
+
+            _ => (),
+        },
+        YulSuffix::Offset => match expr {
+            ast::YulExpression::StorageVariable(..)
+            | ast::YulExpression::SolidityLocalVariable(
+                _,
+                _,
+                Some(StorageLocation::Storage(_)),
+                ..,
+            ) => {
+                return Expression::NumberLiteral(Loc::Codegen, Type::Uint(256), BigInt::from(0));
+            }
+
+            ast::YulExpression::SolidityLocalVariable(
+                _,
+                ty @ Type::Array(_, ref dims),
+                Some(StorageLocation::Calldata(_)),
+                var_no,
+            ) => {
+                if dims.last().unwrap().is_none() {
+                    return Expression::Cast(
+                        *loc,
+                        Type::Uint(256),
+                        Box::new(Expression::Variable(*loc, ty.clone(), *var_no)),
+                    );
+                }
+            }
+
+            _ => (),
+        },
+
+        YulSuffix::Length => {
+            if let ast::YulExpression::SolidityLocalVariable(
+                _,
+                Type::Array(_, ref dims),
+                Some(StorageLocation::Calldata(_)),
+                _,
+            ) = expr
+            {
+                if dims.last().unwrap().is_none() {
+                    return Expression::Builtin(
+                        *loc,
+                        vec![Type::Uint(32)],
+                        Builtin::ArrayLength,
+                        vec![expression(expr, contract_no, ns, vartab, cfg, opt)],
+                    );
+                }
+            }
+        }
+
+        YulSuffix::Address => {
+            if let ast::YulExpression::SolidityLocalVariable(_, Type::ExternalFunction { .. }, ..) =
+                expr
+            {
+                return Expression::Builtin(
+                    *loc,
+                    vec![Type::Address(false)],
+                    Builtin::ExternalFunctionAddress,
+                    vec![expression(expr, contract_no, ns, vartab, cfg, opt)],
+                );
+            }
+        }
+
+        YulSuffix::Selector => {
+            if let ast::YulExpression::SolidityLocalVariable(_, Type::ExternalFunction { .. }, ..) =
+                expr
+            {
+                return Expression::Builtin(
+                    *loc,
+                    vec![Type::Uint(32)],
+                    Builtin::FunctionSelector,
+                    vec![expression(expr, contract_no, ns, vartab, cfg, opt)],
+                );
+            }
+        }
+    }
+
+    unreachable!("Expression does not support suffixes");
+}
+
+/// Add function call instructions to the CFG
+pub(crate) fn process_function_call(
+    function_no: usize,
+    args: &[ast::YulExpression],
+    contract_no: usize,
+    vartab: &mut Vartable,
+    cfg: &mut ControlFlowGraph,
+    ns: &Namespace,
+    opt: &Options,
+) -> Vec<Expression> {
+    let mut codegen_args: Vec<Expression> = Vec::with_capacity(args.len());
+    for (param_no, item) in ns.yul_functions[function_no].params.iter().enumerate() {
+        codegen_args.push(
+            expression(&args[param_no], contract_no, ns, vartab, cfg, opt).cast(&item.ty, ns),
+        );
+    }
+
+    let cfg_no = ns.yul_functions[function_no].cfg_no;
+
+    if ns.yul_functions[function_no].returns.is_empty() {
+        cfg.add(
+            vartab,
+            Instr::Call {
+                res: Vec::new(),
+                return_tys: Vec::new(),
+                call: InternalCallTy::Static(cfg_no),
+                args: codegen_args,
+            },
+        );
+
+        return vec![Expression::Poison];
+    }
+
+    let mut res = Vec::new();
+    let mut returns = Vec::new();
+    let mut return_tys = Vec::new();
+
+    for ret in &*ns.yul_functions[function_no].returns {
+        let id = pt::Identifier {
+            loc: ret.loc,
+            name: ret.name_as_str().to_owned(),
+        };
+
+        let temp_pos = vartab.temp(&id, &ret.ty);
+        return_tys.push(ret.ty.clone());
+        res.push(temp_pos);
+        returns.push(Expression::Variable(id.loc, ret.ty.clone(), temp_pos));
+    }
+
+    cfg.add(
+        vartab,
+        Instr::Call {
+            res,
+            call: InternalCallTy::Static(cfg_no),
+            args: codegen_args,
+            return_tys,
+        },
+    );
+
+    returns
+}

+ 120 - 0
src/codegen/yul/mod.rs

@@ -1,2 +1,122 @@
+use crate::ast::Namespace;
+use crate::codegen::cfg::{
+    optimize_and_check_cfg, populate_arguments, populate_named_returns, ASTFunction,
+    ControlFlowGraph, Instr,
+};
+use crate::codegen::statements::LoopScopes;
+use crate::codegen::vartable::Vartable;
+use crate::codegen::yul::statements::statement;
+use crate::codegen::{Expression, Options};
+use crate::sema::yul::ast::InlineAssembly;
+use solang_parser::pt;
+use solang_parser::pt::FunctionTy;
+
+mod builtin;
 mod expression;
+mod statements;
 mod tests;
+
+/// Create the CFG instructions for inline assembly statements
+pub fn inline_assembly_cfg(
+    inline_assembly: &InlineAssembly,
+    contract_no: usize,
+    ns: &Namespace,
+    cfg: &mut ControlFlowGraph,
+    vartab: &mut Vartable,
+    opt: &Options,
+) {
+    let mut loops = LoopScopes::new();
+    for stmt in &inline_assembly.body {
+        statement(stmt, contract_no, &mut loops, ns, cfg, vartab, &None, opt);
+    }
+}
+
+/// Create the CFG for yul functions
+pub(crate) fn generate_yul_function_cfg(
+    contract_no: usize,
+    function_no: usize,
+    all_cfgs: &mut [ControlFlowGraph],
+    ns: &mut Namespace,
+    opt: &Options,
+) {
+    let mut cfg = yul_function_cfg(contract_no, function_no, ns, opt);
+
+    optimize_and_check_cfg(&mut cfg, ns, ASTFunction::YulFunction(function_no), opt);
+    all_cfgs[ns.yul_functions[function_no].cfg_no] = cfg;
+}
+
+/// Generate the CFG containing all the instructions from a YUL function
+fn yul_function_cfg(
+    contract_no: usize,
+    function_no: usize,
+    ns: &mut Namespace,
+    opt: &Options,
+) -> ControlFlowGraph {
+    let mut vartab =
+        Vartable::from_symbol_table(&ns.yul_functions[function_no].symtable, ns.next_id);
+
+    let mut loops = LoopScopes::new();
+    let yul_func = &ns.yul_functions[function_no];
+
+    let func_name = format!(
+        "{}::yul_function_{}::{}",
+        ns.contracts[contract_no].name, function_no, yul_func.name
+    );
+    let mut cfg = ControlFlowGraph::new(func_name, ASTFunction::YulFunction(function_no));
+
+    cfg.params = yul_func.params.clone();
+    cfg.returns = yul_func.returns.clone();
+    cfg.selector = 0;
+    cfg.public = false;
+    cfg.ty = FunctionTy::Function;
+    cfg.nonpayable = true;
+
+    // populate the arguments
+    populate_arguments(yul_func, &mut cfg, &mut vartab);
+    // populate the returns, if any
+    populate_named_returns(yul_func, ns, &mut cfg, &mut vartab);
+
+    let returns = if yul_func.returns.is_empty() {
+        Instr::Return { value: vec![] }
+    } else {
+        Instr::Return {
+            value: yul_func
+                .symtable
+                .returns
+                .iter()
+                .map(|pos| {
+                    Expression::Variable(
+                        pt::Loc::Codegen,
+                        yul_func.symtable.vars[pos].ty.clone(),
+                        *pos,
+                    )
+                })
+                .collect::<Vec<Expression>>(),
+        }
+    };
+
+    for stmt in &yul_func.body {
+        statement(
+            stmt,
+            contract_no,
+            &mut loops,
+            ns,
+            &mut cfg,
+            &mut vartab,
+            &Some(returns.clone()),
+            opt,
+        );
+    }
+
+    if yul_func.body.is_empty()
+        || (!yul_func.body.is_empty() && yul_func.body.last().unwrap().is_reachable())
+    {
+        cfg.add(&mut vartab, returns);
+    }
+
+    let (vars, next_id) = vartab.drain();
+    cfg.vars = vars;
+    ns.next_id = next_id;
+
+    cfg
+}

+ 443 - 0
src/codegen/yul/statements.rs

@@ -0,0 +1,443 @@
+use crate::ast::{Namespace, RetrieveType, Type};
+use crate::codegen::cfg::{ControlFlowGraph, Instr};
+use crate::codegen::statements::LoopScopes;
+use crate::codegen::vartable::Vartable;
+use crate::codegen::yul::builtin::process_builtin;
+use crate::codegen::yul::expression::{expression, process_function_call};
+use crate::codegen::{Expression, Options};
+use crate::sema::yul::ast;
+use crate::sema::yul::ast::{YulStatement, YulSuffix};
+use num_bigint::BigInt;
+use num_traits::FromPrimitive;
+use solang_parser::pt;
+use solang_parser::pt::StorageLocation;
+
+/// Transform YUL statements into CFG instructions
+pub(crate) fn statement(
+    yul_statement: &YulStatement,
+    contract_no: usize,
+    loops: &mut LoopScopes,
+    ns: &Namespace,
+    cfg: &mut ControlFlowGraph,
+    vartab: &mut Vartable,
+    early_return: &Option<Instr>,
+    opt: &Options,
+) {
+    if !yul_statement.is_reachable() {
+        return;
+    }
+
+    match yul_statement {
+        YulStatement::FunctionCall(_, _, func_no, args) => {
+            let returns = process_function_call(*func_no, args, contract_no, vartab, cfg, ns, opt);
+            assert_eq!(returns.len(), 1);
+            assert_eq!(returns[0], Expression::Poison);
+        }
+
+        YulStatement::BuiltInCall(loc, _, builtin_ty, args) => {
+            let expr = process_builtin(loc, builtin_ty, args, contract_no, ns, vartab, cfg, opt);
+            assert_eq!(expr, Expression::Poison);
+        }
+
+        YulStatement::Block(block) => {
+            for item in &block.body {
+                statement(item, contract_no, loops, ns, cfg, vartab, early_return, opt);
+            }
+        }
+
+        YulStatement::VariableDeclaration(loc, _, vars, init) => {
+            process_variable_declaration(loc, vars, init, contract_no, ns, cfg, vartab, opt);
+        }
+
+        YulStatement::Assignment(loc, _, lhs, rhs) => {
+            process_assignment(loc, lhs, rhs, contract_no, ns, cfg, vartab, opt)
+        }
+
+        YulStatement::IfBlock(_, _, condition, block) => process_if_block(
+            condition,
+            block,
+            contract_no,
+            loops,
+            ns,
+            cfg,
+            vartab,
+            early_return,
+            opt,
+        ),
+
+        YulStatement::Switch { .. } => {
+            // Switch statements should use LLVM switch instruction, which requires changes in emit.
+            unreachable!("Switch statements for yul are not implemented yet");
+        }
+
+        YulStatement::For {
+            loc,
+            init_block,
+            post_block,
+            condition,
+            execution_block,
+            ..
+        } => process_for_block(
+            loc,
+            init_block,
+            condition,
+            post_block,
+            execution_block,
+            contract_no,
+            ns,
+            loops,
+            cfg,
+            vartab,
+            early_return,
+            opt,
+        ),
+
+        YulStatement::Leave(..) => {
+            if let Some(early_leave) = early_return {
+                cfg.add(vartab, early_leave.clone());
+            } else {
+                cfg.add(vartab, Instr::Return { value: vec![] });
+            }
+        }
+
+        YulStatement::Break(..) => {
+            cfg.add(
+                vartab,
+                Instr::Branch {
+                    block: loops.do_break(),
+                },
+            );
+        }
+
+        YulStatement::Continue(..) => {
+            cfg.add(
+                vartab,
+                Instr::Branch {
+                    block: loops.do_continue(),
+                },
+            );
+        }
+    }
+}
+
+/// Add variable declaration to the CFG
+fn process_variable_declaration(
+    loc: &pt::Loc,
+    vars: &[(usize, Type)],
+    init: &Option<ast::YulExpression>,
+    contract_no: usize,
+    ns: &Namespace,
+    cfg: &mut ControlFlowGraph,
+    vartab: &mut Vartable,
+    opt: &Options,
+) {
+    let initializer = if let Some(expr) = init {
+        if let ast::YulExpression::FunctionCall(_, func_no, args, _) = expr {
+            process_function_call(*func_no, args, contract_no, vartab, cfg, ns, opt)
+        } else {
+            vec![expression(expr, contract_no, ns, vartab, cfg, opt)]
+        }
+    } else {
+        let mut inits: Vec<Expression> = Vec::with_capacity(vars.len());
+        for item in vars {
+            inits.push(Expression::Undefined(item.1.clone()));
+        }
+
+        inits
+    };
+
+    for (var_index, item) in vars.iter().enumerate() {
+        cfg.add(
+            vartab,
+            Instr::Set {
+                loc: *loc,
+                res: item.0,
+                expr: initializer[var_index].clone().cast(&item.1, ns),
+            },
+        );
+    }
+}
+
+/// Add assignments to the CFG
+fn process_assignment(
+    loc: &pt::Loc,
+    lhs: &[ast::YulExpression],
+    rhs: &ast::YulExpression,
+    contract_no: usize,
+    ns: &Namespace,
+    cfg: &mut ControlFlowGraph,
+    vartab: &mut Vartable,
+    opt: &Options,
+) {
+    if lhs.len() > 1 {
+        // builtins with multiple returns are not implemented (yet)
+        let returns = if let ast::YulExpression::FunctionCall(_, func_no, args, _) = rhs {
+            process_function_call(*func_no, args, contract_no, vartab, cfg, ns, opt)
+        } else {
+            unreachable!("only function call return multiple values");
+        };
+
+        for (lhs_no, lhs_item) in lhs.iter().enumerate() {
+            cfg_single_assigment(loc, lhs_item, returns[lhs_no].clone(), ns, cfg, vartab);
+        }
+        return;
+    }
+
+    let codegen_rhs = expression(rhs, contract_no, ns, vartab, cfg, opt);
+    cfg_single_assigment(loc, &lhs[0], codegen_rhs, ns, cfg, vartab);
+}
+
+/// As YUL assignments may contain multiple variables, this function treats one assignment at a time.
+fn cfg_single_assigment(
+    loc: &pt::Loc,
+    lhs: &ast::YulExpression,
+    rhs: Expression,
+    ns: &Namespace,
+    cfg: &mut ControlFlowGraph,
+    vartab: &mut Vartable,
+) {
+    match lhs {
+        ast::YulExpression::YulLocalVariable(_, ty, var_no)
+        | ast::YulExpression::SolidityLocalVariable(_, ty, None, var_no) => {
+            // Ensure both types are compatible
+            let rhs = rhs.cast(ty, ns);
+            cfg.add(
+                vartab,
+                Instr::Set {
+                    loc: *loc,
+                    res: *var_no,
+                    expr: rhs,
+                },
+            );
+        }
+
+        ast::YulExpression::SolidityLocalVariable(
+            _,
+            ty,
+            Some(StorageLocation::Memory(_)),
+            var_no,
+        ) => {
+            // This is an assignment to a pointer, so we make sure the rhs has a compatible size
+            let rhs = rhs.cast(ty, ns);
+            cfg.add(
+                vartab,
+                Instr::Set {
+                    loc: *loc,
+                    res: *var_no,
+                    expr: rhs,
+                },
+            )
+        }
+
+        ast::YulExpression::SuffixAccess(_, member, suffix) => {
+            match &**member {
+                ast::YulExpression::SolidityLocalVariable(
+                    _,
+                    _,
+                    Some(StorageLocation::Calldata(_)),
+                    var_no,
+                ) => match suffix {
+                    YulSuffix::Offset => {
+                        let rhs = rhs.cast(&lhs.ty(), ns);
+                        cfg.add(
+                            vartab,
+                            Instr::Set {
+                                loc: *loc,
+                                res: *var_no,
+                                expr: rhs,
+                            },
+                        );
+                    }
+                    YulSuffix::Length => {
+                        unimplemented!("Assignment to calldata array suffix is not implemented");
+                    }
+
+                    _ => unreachable!(),
+                },
+                ast::YulExpression::SolidityLocalVariable(_, Type::ExternalFunction { .. }, ..) => {
+                    if matches!(suffix, YulSuffix::Address | YulSuffix::Selector) {
+                        unimplemented!(
+                            "Assignment to a function's address/selector is no implemented."
+                        )
+                    }
+                }
+
+                ast::YulExpression::SolidityLocalVariable(
+                    _,
+                    _,
+                    Some(StorageLocation::Storage(_)),
+                    var_no,
+                ) => {
+                    // This assignment changes the value of a pointer to storage
+                    if matches!(suffix, YulSuffix::Slot) {
+                        let rhs = rhs.cast(&lhs.ty(), ns);
+                        cfg.add(
+                            vartab,
+                            Instr::Set {
+                                loc: *loc,
+                                res: *var_no,
+                                expr: rhs,
+                            },
+                        );
+                    }
+                }
+
+                _ => unreachable!("There should not exist a suffix for the given expression"),
+            }
+        }
+
+        ast::YulExpression::BoolLiteral(..)
+        | ast::YulExpression::NumberLiteral(..)
+        | ast::YulExpression::StringLiteral(..)
+        | ast::YulExpression::SolidityLocalVariable(..)
+        | ast::YulExpression::StorageVariable(..)
+        | ast::YulExpression::BuiltInCall(..)
+        | ast::YulExpression::FunctionCall(..)
+        | ast::YulExpression::ConstantVariable(..) => {
+            unreachable!("Cannot assign to this expression");
+        }
+    }
+}
+
+/// Add an if statement to the CFG
+fn process_if_block(
+    cond: &ast::YulExpression,
+    block: &ast::YulBlock,
+    contract_no: usize,
+    loops: &mut LoopScopes,
+    ns: &Namespace,
+    cfg: &mut ControlFlowGraph,
+    vartab: &mut Vartable,
+    early_return: &Option<Instr>,
+    opt: &Options,
+) {
+    let cond = expression(cond, contract_no, ns, vartab, cfg, opt);
+
+    let bool_cond = if cond.ty() == Type::Bool {
+        cond
+    } else {
+        Expression::NotEqual(
+            block.loc,
+            Box::new(Expression::NumberLiteral(
+                pt::Loc::Codegen,
+                cond.ty(),
+                BigInt::from_u8(0).unwrap(),
+            )),
+            Box::new(cond),
+        )
+    };
+
+    let then = cfg.new_basic_block("then".to_string());
+    let endif = cfg.new_basic_block("endif".to_string());
+
+    cfg.add(
+        vartab,
+        Instr::BranchCond {
+            cond: bool_cond,
+            true_block: then,
+            false_block: endif,
+        },
+    );
+
+    cfg.set_basic_block(then);
+    vartab.new_dirty_tracker(ns.next_id);
+
+    for stmt in &block.body {
+        statement(stmt, contract_no, loops, ns, cfg, vartab, early_return, opt);
+    }
+
+    if block.is_next_reachable() {
+        cfg.add(vartab, Instr::Branch { block: endif });
+    }
+
+    cfg.set_phis(endif, vartab.pop_dirty_tracker());
+
+    cfg.set_basic_block(endif);
+}
+
+/// Add the for statement to the CFG
+fn process_for_block(
+    loc: &pt::Loc,
+    init_block: &ast::YulBlock,
+    condition: &ast::YulExpression,
+    post_block: &ast::YulBlock,
+    execution_block: &ast::YulBlock,
+    contract_no: usize,
+    ns: &Namespace,
+    loops: &mut LoopScopes,
+    cfg: &mut ControlFlowGraph,
+    vartab: &mut Vartable,
+    early_return: &Option<Instr>,
+    opt: &Options,
+) {
+    for stmt in &init_block.body {
+        statement(stmt, contract_no, loops, ns, cfg, vartab, early_return, opt);
+    }
+
+    if !init_block.is_next_reachable() {
+        return;
+    }
+
+    let cond_block = cfg.new_basic_block("cond".to_string());
+    let next_block = cfg.new_basic_block("next".to_string());
+    let body_block = cfg.new_basic_block("body".to_string());
+    let end_block = cfg.new_basic_block("end_for".to_string());
+
+    cfg.add(vartab, Instr::Branch { block: cond_block });
+    cfg.set_basic_block(cond_block);
+
+    let cond_expr = expression(condition, contract_no, ns, vartab, cfg, opt);
+
+    let cond_expr = if cond_expr.ty() == Type::Bool {
+        cond_expr
+    } else {
+        Expression::NotEqual(
+            *loc,
+            Box::new(Expression::NumberLiteral(
+                pt::Loc::Codegen,
+                cond_expr.ty(),
+                BigInt::from_u8(0).unwrap(),
+            )),
+            Box::new(cond_expr),
+        )
+    };
+
+    cfg.add(
+        vartab,
+        Instr::BranchCond {
+            cond: cond_expr,
+            true_block: body_block,
+            false_block: end_block,
+        },
+    );
+
+    cfg.set_basic_block(body_block);
+    loops.new_scope(end_block, next_block);
+    vartab.new_dirty_tracker(ns.next_id);
+
+    for stmt in &execution_block.body {
+        statement(stmt, contract_no, loops, ns, cfg, vartab, early_return, opt);
+    }
+
+    if execution_block.is_next_reachable() {
+        cfg.add(vartab, Instr::Branch { block: next_block });
+    }
+
+    loops.leave_scope();
+
+    cfg.set_basic_block(next_block);
+
+    for stmt in &post_block.body {
+        statement(stmt, contract_no, loops, ns, cfg, vartab, early_return, opt);
+    }
+
+    if post_block.is_next_reachable() {
+        cfg.add(vartab, Instr::Branch { block: cond_block });
+    }
+
+    cfg.set_basic_block(end_block);
+    let set = vartab.pop_dirty_tracker();
+    cfg.set_phis(next_block, set.clone());
+    cfg.set_phis(end_block, set.clone());
+    cfg.set_phis(cond_block, set);
+}

+ 428 - 15
src/codegen/yul/tests/expression.rs

@@ -1,12 +1,14 @@
 #![cfg(test)]
-use crate::ast::{Contract, Namespace, Type, Variable};
+
+use crate::ast::{Contract, Layout, Mutability, Namespace, Type, Variable};
 use crate::codegen::cfg::ControlFlowGraph;
 use crate::codegen::vartable::Vartable;
 use crate::codegen::yul::expression::expression;
-use crate::codegen::{Expression, Options};
+use crate::codegen::{Builtin, Expression, Options};
 use crate::sema::yul::ast;
+use crate::sema::yul::ast::YulSuffix;
 use crate::{sema, Target};
-use num_bigint::BigInt;
+use num_bigint::{BigInt, Sign};
 use solang_parser::pt::{ContractTy, Loc, StorageLocation, Visibility};
 
 #[test]
@@ -18,18 +20,18 @@ fn bool_literal() {
     let opt = Options::default();
 
     let expr = ast::YulExpression::BoolLiteral(loc, true, Type::Bool);
-    let res = expression(&expr, 0, None, &ns, &mut vartab, &mut cfg, &opt);
+    let res = expression(&expr, 0, &ns, &mut vartab, &mut cfg, &opt);
     assert_eq!(res, Expression::BoolLiteral(loc, true));
 
     let expr = ast::YulExpression::BoolLiteral(loc, true, Type::Uint(32));
-    let res = expression(&expr, 0, None, &ns, &mut vartab, &mut cfg, &opt);
+    let res = expression(&expr, 0, &ns, &mut vartab, &mut cfg, &opt);
     assert_eq!(
         res,
         Expression::NumberLiteral(loc, Type::Uint(32), BigInt::from(1))
     );
 
     let expr = ast::YulExpression::BoolLiteral(loc, false, Type::Uint(32));
-    let res = expression(&expr, 0, None, &ns, &mut vartab, &mut cfg, &opt);
+    let res = expression(&expr, 0, &ns, &mut vartab, &mut cfg, &opt);
     assert_eq!(
         res,
         Expression::NumberLiteral(loc, Type::Uint(32), BigInt::from(0))
@@ -45,7 +47,7 @@ fn number_literal() {
     let opt = Options::default();
 
     let expr = ast::YulExpression::NumberLiteral(loc, BigInt::from(32), Type::Uint(256));
-    let res = expression(&expr, 0, None, &ns, &mut vartab, &mut cfg, &opt);
+    let res = expression(&expr, 0, &ns, &mut vartab, &mut cfg, &opt);
     assert_eq!(
         res,
         Expression::NumberLiteral(loc, Type::Uint(256), BigInt::from(32))
@@ -61,10 +63,14 @@ fn string_literal() {
     let opt = Options::default();
 
     let expr = ast::YulExpression::StringLiteral(loc, vec![0, 3, 255, 127], Type::Uint(128));
-    let res = expression(&expr, 0, None, &ns, &mut vartab, &mut cfg, &opt);
+    let res = expression(&expr, 0, &ns, &mut vartab, &mut cfg, &opt);
     assert_eq!(
         res,
-        Expression::BytesLiteral(loc, Type::Uint(128), vec![0, 3, 255, 127])
+        Expression::NumberLiteral(
+            loc,
+            Type::Uint(128),
+            BigInt::from_bytes_be(Sign::Plus, &[0, 3, 255, 127])
+        )
     );
 }
 
@@ -77,7 +83,7 @@ fn yul_local_variable() {
     let opt = Options::default();
 
     let expr = ast::YulExpression::YulLocalVariable(loc, Type::Int(16), 5);
-    let res = expression(&expr, 0, None, &ns, &mut vartab, &mut cfg, &opt);
+    let res = expression(&expr, 0, &ns, &mut vartab, &mut cfg, &opt);
     assert_eq!(res, Expression::Variable(loc, Type::Int(16), 5));
 }
 
@@ -130,7 +136,7 @@ fn contract_constant_variable() {
     ns.contracts.push(contract);
 
     let expr = ast::YulExpression::ConstantVariable(loc, Type::Uint(64), Some(0), 0);
-    let res = expression(&expr, 0, None, &ns, &mut vartab, &mut cfg, &opt);
+    let res = expression(&expr, 0, &ns, &mut vartab, &mut cfg, &opt);
     assert_eq!(
         res,
         Expression::NumberLiteral(loc, Type::Uint(64), BigInt::from(64))
@@ -163,7 +169,7 @@ fn global_constant_variable() {
     };
     ns.constants.push(var);
     let expr = ast::YulExpression::ConstantVariable(loc, Type::Uint(64), None, 0);
-    let res = expression(&expr, 0, None, &ns, &mut vartab, &mut cfg, &opt);
+    let res = expression(&expr, 0, &ns, &mut vartab, &mut cfg, &opt);
     assert_eq!(
         res,
         Expression::NumberLiteral(loc, Type::Uint(64), BigInt::from(64))
@@ -180,7 +186,7 @@ fn storage_variable() {
     let opt = Options::default();
 
     let expr = ast::YulExpression::StorageVariable(loc, Type::Bool, 0, 0);
-    let _ = expression(&expr, 0, None, &ns, &mut vartab, &mut cfg, &opt);
+    let _ = expression(&expr, 0, &ns, &mut vartab, &mut cfg, &opt);
 }
 
 #[test]
@@ -198,7 +204,7 @@ fn storage_variable_reference() {
         Some(StorageLocation::Storage(loc)),
         0,
     );
-    let _ = expression(&expr, 0, None, &ns, &mut vartab, &mut cfg, &opt);
+    let _ = expression(&expr, 0, &ns, &mut vartab, &mut cfg, &opt);
 }
 
 #[test]
@@ -210,6 +216,413 @@ fn solidity_local_variable() {
     let opt = Options::default();
 
     let expr = ast::YulExpression::SolidityLocalVariable(loc, Type::Uint(32), None, 7);
-    let res = expression(&expr, 0, None, &ns, &mut vartab, &mut cfg, &opt);
+    let res = expression(&expr, 0, &ns, &mut vartab, &mut cfg, &opt);
     assert_eq!(res, Expression::Variable(loc, Type::Uint(32), 7));
 }
+
+#[test]
+fn slot_suffix() {
+    let mut ns = Namespace::new(Target::Solana);
+    let loc = Loc::File(1, 2, 3);
+    let layout = Layout {
+        slot: BigInt::from(2),
+        contract_no: 0,
+        var_no: 0,
+        ty: Type::Uint(256),
+    };
+    let contract = Contract {
+        tags: vec![],
+        loc: Loc::Builtin,
+        ty: ContractTy::Contract(loc),
+        name: "".to_string(),
+        bases: vec![],
+        using: vec![],
+        layout: vec![layout],
+        fixed_layout_size: Default::default(),
+        functions: vec![],
+        all_functions: Default::default(),
+        virtual_functions: Default::default(),
+        yul_functions: vec![],
+        variables: vec![],
+        creates: vec![],
+        sends_events: vec![],
+        initializer: None,
+        default_constructor: None,
+        cfg: vec![],
+        code: vec![],
+    };
+    ns.contracts.push(contract);
+
+    let mut vartab = Vartable::new(2);
+    let mut cfg = ControlFlowGraph::placeholder();
+    let opt = Options::default();
+
+    let expr = ast::YulExpression::SuffixAccess(
+        loc,
+        Box::new(ast::YulExpression::StorageVariable(
+            loc,
+            Type::Uint(256),
+            0,
+            0,
+        )),
+        YulSuffix::Slot,
+    );
+    let res = expression(&expr, 0, &ns, &mut vartab, &mut cfg, &opt);
+    assert_eq!(
+        res,
+        Expression::NumberLiteral(Loc::Codegen, Type::Uint(256), BigInt::from(2))
+    );
+
+    let expr = ast::YulExpression::SuffixAccess(
+        loc,
+        Box::new(ast::YulExpression::SolidityLocalVariable(
+            loc,
+            Type::Uint(16),
+            Some(StorageLocation::Storage(loc)),
+            0,
+        )),
+        YulSuffix::Slot,
+    );
+    let res = expression(&expr, 0, &ns, &mut vartab, &mut cfg, &opt);
+    assert_eq!(res, Expression::Variable(loc, Type::Uint(256), 0));
+}
+
+#[test]
+#[should_panic]
+fn slot_suffix_panic() {
+    let ns = Namespace::new(Target::Solana);
+    let loc = Loc::File(1, 2, 3);
+    let mut vartab = Vartable::new(2);
+    let mut cfg = ControlFlowGraph::placeholder();
+    let opt = Options::default();
+
+    let expr = ast::YulExpression::SuffixAccess(
+        loc,
+        Box::new(ast::YulExpression::SolidityLocalVariable(
+            loc,
+            Type::Int(32),
+            None,
+            2,
+        )),
+        YulSuffix::Slot,
+    );
+
+    let _res = expression(&expr, 0, &ns, &mut vartab, &mut cfg, &opt);
+}
+
+#[test]
+fn offset_suffix() {
+    let ns = Namespace::new(Target::Solana);
+    let loc = Loc::File(1, 2, 3);
+    let mut vartab = Vartable::new(2);
+    let mut cfg = ControlFlowGraph::placeholder();
+    let opt = Options::default();
+
+    let expr = ast::YulExpression::SuffixAccess(
+        loc,
+        Box::new(ast::YulExpression::StorageVariable(
+            loc,
+            Type::Int(32),
+            1,
+            0,
+        )),
+        YulSuffix::Offset,
+    );
+
+    let res = expression(&expr, 0, &ns, &mut vartab, &mut cfg, &opt);
+    assert_eq!(
+        res,
+        Expression::NumberLiteral(Loc::Codegen, Type::Uint(256), BigInt::from(0))
+    );
+
+    let expr = ast::YulExpression::SuffixAccess(
+        loc,
+        Box::new(ast::YulExpression::SolidityLocalVariable(
+            loc,
+            Type::Uint(24),
+            Some(StorageLocation::Storage(loc)),
+            0,
+        )),
+        YulSuffix::Offset,
+    );
+    let res = expression(&expr, 0, &ns, &mut vartab, &mut cfg, &opt);
+    assert_eq!(
+        res,
+        Expression::NumberLiteral(Loc::Codegen, Type::Uint(256), BigInt::from(0))
+    );
+
+    let expr = ast::YulExpression::SuffixAccess(
+        loc,
+        Box::new(ast::YulExpression::SolidityLocalVariable(
+            loc,
+            Type::Array(Box::new(Type::Uint(256)), vec![None]),
+            Some(StorageLocation::Calldata(loc)),
+            1,
+        )),
+        YulSuffix::Offset,
+    );
+    let res = expression(&expr, 0, &ns, &mut vartab, &mut cfg, &opt);
+    assert_eq!(
+        res,
+        Expression::Cast(
+            loc,
+            Type::Uint(256),
+            Box::new(Expression::Variable(
+                loc,
+                Type::Array(Box::new(Type::Uint(256)), vec![None]),
+                1
+            ))
+        )
+    );
+}
+
+#[test]
+#[should_panic]
+fn offset_suffix_panic_calldata() {
+    let ns = Namespace::new(Target::Solana);
+    let loc = Loc::File(1, 2, 3);
+    let mut vartab = Vartable::new(2);
+    let mut cfg = ControlFlowGraph::placeholder();
+    let opt = Options::default();
+
+    let expr = ast::YulExpression::SuffixAccess(
+        loc,
+        Box::new(ast::YulExpression::SolidityLocalVariable(
+            loc,
+            Type::Array(Box::new(Type::Uint(32)), vec![None, Some(BigInt::from(3))]),
+            Some(StorageLocation::Calldata(loc)),
+            3,
+        )),
+        YulSuffix::Offset,
+    );
+
+    let _res = expression(&expr, 0, &ns, &mut vartab, &mut cfg, &opt);
+}
+
+#[test]
+#[should_panic]
+fn offset_suffix_panic_other() {
+    let ns = Namespace::new(Target::Solana);
+    let loc = Loc::File(1, 2, 3);
+    let mut vartab = Vartable::new(2);
+    let mut cfg = ControlFlowGraph::placeholder();
+    let opt = Options::default();
+
+    let expr = ast::YulExpression::SuffixAccess(
+        loc,
+        Box::new(ast::YulExpression::YulLocalVariable(loc, Type::Int(32), 3)),
+        YulSuffix::Offset,
+    );
+
+    let _res = expression(&expr, 0, &ns, &mut vartab, &mut cfg, &opt);
+}
+
+#[test]
+fn length_suffix() {
+    let ns = Namespace::new(Target::Solana);
+    let loc = Loc::File(1, 2, 3);
+    let mut vartab = Vartable::new(2);
+    let mut cfg = ControlFlowGraph::placeholder();
+    let opt = Options::default();
+
+    let expr = ast::YulExpression::SuffixAccess(
+        loc,
+        Box::new(ast::YulExpression::SolidityLocalVariable(
+            loc,
+            Type::Array(
+                Box::new(Type::Uint(32)),
+                vec![None, Some(BigInt::from(3)), None],
+            ),
+            Some(StorageLocation::Calldata(loc)),
+            3,
+        )),
+        YulSuffix::Length,
+    );
+
+    let res = expression(&expr, 0, &ns, &mut vartab, &mut cfg, &opt);
+    assert_eq!(
+        res,
+        Expression::Builtin(
+            loc,
+            vec![Type::Uint(32)],
+            Builtin::ArrayLength,
+            vec![Expression::Variable(
+                loc,
+                Type::Array(
+                    Box::new(Type::Uint(32)),
+                    vec![None, Some(BigInt::from(3)), None]
+                ),
+                3
+            )]
+        )
+    );
+}
+
+#[test]
+#[should_panic]
+fn length_suffix_panic() {
+    let ns = Namespace::new(Target::Solana);
+    let loc = Loc::File(1, 2, 3);
+    let mut vartab = Vartable::new(2);
+    let mut cfg = ControlFlowGraph::placeholder();
+    let opt = Options::default();
+
+    let expr = ast::YulExpression::SuffixAccess(
+        loc,
+        Box::new(ast::YulExpression::SolidityLocalVariable(
+            loc,
+            Type::Array(Box::new(Type::Uint(32)), vec![None, Some(BigInt::from(3))]),
+            Some(StorageLocation::Calldata(loc)),
+            3,
+        )),
+        YulSuffix::Length,
+    );
+
+    let res = expression(&expr, 0, &ns, &mut vartab, &mut cfg, &opt);
+    assert_eq!(
+        res,
+        Expression::Builtin(
+            loc,
+            vec![Type::Uint(256)],
+            Builtin::ArrayLength,
+            vec![Expression::Variable(
+                loc,
+                Type::Array(
+                    Box::new(Type::Uint(32)),
+                    vec![None, Some(BigInt::from(3)), None]
+                ),
+                3
+            )]
+        )
+    );
+}
+
+#[test]
+fn selector_suffix() {
+    let ns = Namespace::new(Target::Solana);
+    let loc = Loc::File(1, 2, 3);
+    let mut vartab = Vartable::new(2);
+    let mut cfg = ControlFlowGraph::placeholder();
+    let opt = Options::default();
+
+    let expr = ast::YulExpression::SuffixAccess(
+        loc,
+        Box::new(ast::YulExpression::SolidityLocalVariable(
+            loc,
+            Type::ExternalFunction {
+                mutability: Mutability::Pure(loc),
+                params: vec![],
+                returns: vec![],
+            },
+            None,
+            4,
+        )),
+        YulSuffix::Selector,
+    );
+    let res = expression(&expr, 0, &ns, &mut vartab, &mut cfg, &opt);
+
+    assert_eq!(
+        res,
+        Expression::Builtin(
+            loc,
+            vec![Type::Uint(32)],
+            Builtin::FunctionSelector,
+            vec![Expression::Variable(
+                loc,
+                Type::ExternalFunction {
+                    mutability: Mutability::Pure(loc),
+                    params: vec![],
+                    returns: vec![]
+                },
+                4
+            )],
+        ),
+    );
+}
+
+#[test]
+#[should_panic]
+fn selector_suffix_panic() {
+    let ns = Namespace::new(Target::Solana);
+    let loc = Loc::File(1, 2, 3);
+    let mut vartab = Vartable::new(2);
+    let mut cfg = ControlFlowGraph::placeholder();
+    let opt = Options::default();
+
+    let expr = ast::YulExpression::SuffixAccess(
+        loc,
+        Box::new(ast::YulExpression::SolidityLocalVariable(
+            loc,
+            Type::Bool,
+            None,
+            4,
+        )),
+        YulSuffix::Selector,
+    );
+    let _res = expression(&expr, 0, &ns, &mut vartab, &mut cfg, &opt);
+}
+
+#[test]
+fn address_suffix() {
+    let ns = Namespace::new(Target::Solana);
+    let loc = Loc::File(1, 2, 3);
+    let mut vartab = Vartable::new(2);
+    let mut cfg = ControlFlowGraph::placeholder();
+    let opt = Options::default();
+
+    let expr = ast::YulExpression::SuffixAccess(
+        loc,
+        Box::new(ast::YulExpression::SolidityLocalVariable(
+            loc,
+            Type::ExternalFunction {
+                mutability: Mutability::Pure(loc),
+                params: vec![],
+                returns: vec![],
+            },
+            None,
+            4,
+        )),
+        YulSuffix::Address,
+    );
+    let res = expression(&expr, 0, &ns, &mut vartab, &mut cfg, &opt);
+
+    assert_eq!(
+        res,
+        Expression::Builtin(
+            loc,
+            vec![Type::Address(false)],
+            Builtin::ExternalFunctionAddress,
+            vec![Expression::Variable(
+                loc,
+                Type::ExternalFunction {
+                    mutability: Mutability::Pure(loc),
+                    params: vec![],
+                    returns: vec![]
+                },
+                4
+            )],
+        ),
+    );
+}
+
+#[test]
+#[should_panic]
+fn address_suffix_panic() {
+    let ns = Namespace::new(Target::Solana);
+    let loc = Loc::File(1, 2, 3);
+    let mut vartab = Vartable::new(2);
+    let mut cfg = ControlFlowGraph::placeholder();
+    let opt = Options::default();
+
+    let expr = ast::YulExpression::SuffixAccess(
+        loc,
+        Box::new(ast::YulExpression::SolidityLocalVariable(
+            loc,
+            Type::Bool,
+            None,
+            4,
+        )),
+        YulSuffix::Address,
+    );
+    let _res = expression(&expr, 0, &ns, &mut vartab, &mut cfg, &opt);
+}

+ 24 - 8
src/emit/mod.rs

@@ -1322,7 +1322,7 @@ pub trait TargetRuntime<'a> {
                 )
                 .into()
             }
-            Expression::Divide(_, _, l, r) if !l.ty().is_signed_int() => {
+            Expression::UnsignedDivide(_, _, l, r) => {
                 let left = self
                     .expression(bin, l, vartab, function, ns)
                     .into_int_value();
@@ -1420,7 +1420,7 @@ pub trait TargetRuntime<'a> {
                     bin.builder.build_int_unsigned_div(left, right, "").into()
                 }
             }
-            Expression::Divide(_, _, l, r) => {
+            Expression::SignedDivide(_, _, l, r) => {
                 let left = self
                     .expression(bin, l, vartab, function, ns)
                     .into_int_value();
@@ -1566,7 +1566,7 @@ pub trait TargetRuntime<'a> {
                     bin.builder.build_int_signed_div(left, right, "").into()
                 }
             }
-            Expression::Modulo(_, _, l, r) if !l.ty().is_signed_int() => {
+            Expression::UnsignedModulo(_, _, l, r) => {
                 let left = self
                     .expression(bin, l, vartab, function, ns)
                     .into_int_value();
@@ -1664,7 +1664,7 @@ pub trait TargetRuntime<'a> {
                     bin.builder.build_int_unsigned_rem(left, right, "").into()
                 }
             }
-            Expression::Modulo(_, _, l, r) => {
+            Expression::SignedModulo(_, _, l, r) => {
                 let left = self
                     .expression(bin, l, vartab, function, ns)
                     .into_int_value();
@@ -1875,7 +1875,7 @@ pub trait TargetRuntime<'a> {
                     .build_int_compare(IntPredicate::NE, left, right, "")
                     .into()
             }
-            Expression::More(_, l, r) => {
+            Expression::SignedMore(_, l, r) | Expression::UnsignedMore(_, l, r) => {
                 if l.ty().is_address() {
                     self.compare_address(bin, l, r, IntPredicate::SGT, vartab, function, ns)
                         .into()
@@ -1889,7 +1889,7 @@ pub trait TargetRuntime<'a> {
 
                     bin.builder
                         .build_int_compare(
-                            if l.ty().is_signed_int() {
+                            if matches!(e, Expression::SignedMore(..)) {
                                 IntPredicate::SGT
                             } else {
                                 IntPredicate::UGT
@@ -1927,7 +1927,7 @@ pub trait TargetRuntime<'a> {
                         .into()
                 }
             }
-            Expression::Less(_, l, r) => {
+            Expression::SignedLess(_, l, r) | Expression::UnsignedLess(_, l, r) => {
                 if l.ty().is_address() {
                     self.compare_address(bin, l, r, IntPredicate::SLT, vartab, function, ns)
                         .into()
@@ -1941,7 +1941,7 @@ pub trait TargetRuntime<'a> {
 
                     bin.builder
                         .build_int_compare(
-                            if l.ty().is_signed_int() {
+                            if matches!(e, Expression::SignedLess(..)) {
                                 IntPredicate::SLT
                             } else {
                                 IntPredicate::ULT
@@ -3222,6 +3222,22 @@ pub trait TargetRuntime<'a> {
             );
 
             bin.builder.build_load(dest, "val")
+        } else if matches!(from, Type::Bool) && matches!(to, Type::Int(_) | Type::Uint(_)) {
+            bin.builder
+                .build_int_cast(
+                    val.into_int_value(),
+                    bin.llvm_type(to, ns).into_int_type(),
+                    "bool_to_int_cast",
+                )
+                .into()
+        } else if from.is_reference_type(ns) && matches!(to, Type::Uint(_)) {
+            bin.builder
+                .build_ptr_to_int(
+                    val.into_pointer_value(),
+                    bin.llvm_type(to, ns).into_int_type(),
+                    "ptr_to_int",
+                )
+                .into()
         } else {
             val
         }

+ 22 - 0
src/sema/ast.rs

@@ -224,6 +224,28 @@ pub struct Function {
     pub emits_events: Vec<usize>,
 }
 
+/// This trait provides a single interface for fetching paramenters, returns and the symbol table
+/// for both yul and solidity functions
+pub trait FunctionAttributes {
+    fn get_symbol_table(&self) -> &Symtable;
+    fn get_parameters(&self) -> &Vec<Parameter>;
+    fn get_returns(&self) -> &Vec<Parameter>;
+}
+
+impl FunctionAttributes for Function {
+    fn get_symbol_table(&self) -> &Symtable {
+        &self.symtable
+    }
+
+    fn get_parameters(&self) -> &Vec<Parameter> {
+        &*self.params
+    }
+
+    fn get_returns(&self) -> &Vec<Parameter> {
+        &*self.returns
+    }
+}
+
 impl Function {
     pub fn new(
         loc: pt::Loc,

+ 5 - 5
src/sema/dotgraphviz.rs

@@ -1685,17 +1685,17 @@ impl Dot {
             YulExpression::BuiltInCall(loc, builtin_ty, args) => {
                 self.add_yul_builtin_call(loc, builtin_ty, args, parent, parent_rel, symtable, ns);
             }
-            YulExpression::FunctionCall(loc, func_no, args) => {
+            YulExpression::FunctionCall(loc, func_no, args, _) => {
                 self.add_yul_function_call(loc, func_no, args, parent, parent_rel, symtable, ns);
             }
-            YulExpression::MemberAccess(loc, member, suffix) => {
+            YulExpression::SuffixAccess(loc, member, suffix) => {
                 let labels = vec![
-                    format!("yul member '{}' access", suffix.to_string()),
+                    format!("yul suffix '{}' access", suffix.to_string()),
                     ns.loc_to_string(loc),
                 ];
 
                 let node = self.add_node(
-                    Node::new("yul_member_access", labels),
+                    Node::new("yul_suffix_access", labels),
                     Some(parent),
                     Some(parent_rel),
                 );
@@ -1798,7 +1798,7 @@ impl Dot {
                 );
 
                 for (decl_no, item) in declared_vars.iter().enumerate() {
-                    let var = &symtable.vars[item];
+                    let var = &symtable.vars[&item.0];
                     self.add_node(
                         Node::new(
                             "var_decl_item",

+ 7 - 1
src/sema/expression.rs

@@ -1240,7 +1240,7 @@ fn get_int_length(
     }
 }
 
-fn coerce_number(
+pub fn coerce_number(
     l: &Type,
     l_loc: &pt::Loc,
     r: &Type,
@@ -1295,6 +1295,12 @@ fn coerce_number(
         (Type::Int(_), Type::Rational) => {
             return Ok(Type::Rational);
         }
+        (Type::Bool, Type::Int(_) | Type::Uint(_)) => {
+            return Ok(r.clone());
+        }
+        (Type::Int(_) | Type::Uint(_), Type::Bool) => {
+            return Ok(l.clone());
+        }
         _ => (),
     }
 

+ 0 - 9
src/sema/mutability.rs

@@ -61,14 +61,6 @@ impl<'a> StateCheck<'a> {
 
         self.does_read_state = true;
     }
-
-    //TODO: This is a temporary solution while inline assembly is not supported in codegen
-    fn has_yul(&mut self, loc: &pt::Loc) {
-        self.diagnostics.push(Diagnostic::error(
-            *loc,
-            "inline assembly is not yet supported".to_string(),
-        ));
-    }
 }
 
 fn check_mutability(func: &Function, ns: &Namespace) -> Vec<Diagnostic> {
@@ -226,7 +218,6 @@ fn recurse_statements(stmts: &[Statement], ns: &Namespace, state: &mut StateChec
             Statement::Emit { loc, .. } => state.write(loc),
             Statement::Break(_) | Statement::Continue(_) | Statement::Underscore(_) => (),
             Statement::Assembly(inline_assembly, _) => {
-                state.has_yul(&inline_assembly.loc);
                 for function_no in inline_assembly.functions.start..inline_assembly.functions.end {
                     recurse_yul_statements(&ns.yul_functions[function_no].body, state);
                 }

+ 1 - 1
src/sema/types.rs

@@ -1252,7 +1252,7 @@ impl Type {
             Type::InternalFunction { .. } => false,
             Type::ExternalFunction { .. } => false,
             Type::UserType(no) => ns.user_types[*no].ty.is_reference_type(ns),
-            _ => unreachable!(),
+            _ => false,
         }
     }
 

+ 80 - 7
src/sema/yul/ast.rs

@@ -1,4 +1,4 @@
-use crate::ast::{Parameter, Type};
+use crate::ast::{FunctionAttributes, Parameter, RetrieveType, Type};
 use crate::sema::symtable::Symtable;
 use crate::sema::yul::builtin::YulBuiltInFunction;
 use crate::sema::Recurse;
@@ -19,9 +19,16 @@ pub struct InlineAssembly {
 pub struct YulBlock {
     pub loc: pt::Loc,
     pub reachable: bool,
+    pub next_reachable: bool,
     pub body: Vec<YulStatement>,
 }
 
+impl YulBlock {
+    pub fn is_next_reachable(&self) -> bool {
+        self.body.is_empty() || (!self.body.is_empty() && self.next_reachable)
+    }
+}
+
 #[derive(PartialEq, Debug, Clone)]
 pub enum YulExpression {
     BoolLiteral(pt::Loc, bool, Type),
@@ -32,8 +39,41 @@ pub enum YulExpression {
     ConstantVariable(pt::Loc, Type, Option<usize>, usize),
     StorageVariable(pt::Loc, Type, usize, usize),
     BuiltInCall(pt::Loc, YulBuiltInFunction, Vec<YulExpression>),
-    FunctionCall(pt::Loc, usize, Vec<YulExpression>),
-    MemberAccess(pt::Loc, Box<YulExpression>, YulSuffix),
+    FunctionCall(pt::Loc, usize, Vec<YulExpression>, Arc<Vec<Parameter>>),
+    SuffixAccess(pt::Loc, Box<YulExpression>, YulSuffix),
+}
+
+impl RetrieveType for YulExpression {
+    fn ty(&self) -> Type {
+        match self {
+            YulExpression::BoolLiteral(_, _, ty)
+            | YulExpression::NumberLiteral(_, _, ty)
+            | YulExpression::StringLiteral(_, _, ty)
+            | YulExpression::YulLocalVariable(_, ty, ..)
+            | YulExpression::SolidityLocalVariable(_, ty, ..)
+            | YulExpression::ConstantVariable(_, ty, ..)
+            | YulExpression::StorageVariable(_, ty, ..) => ty.clone(),
+
+            YulExpression::SuffixAccess(..) => Type::Uint(256),
+
+            YulExpression::BuiltInCall(_, ty, ..) => {
+                let prototype = ty.get_prototype_info();
+                if prototype.no_returns == 1 {
+                    Type::Uint(256)
+                } else {
+                    unreachable!("Expression does not have a type");
+                }
+            }
+
+            YulExpression::FunctionCall(_, _, _, returns) => {
+                if returns.len() == 1 {
+                    returns[0].ty.clone()
+                } else {
+                    unreachable!("Expression does not have a type");
+                }
+            }
+        }
+    }
 }
 
 #[derive(PartialEq, Debug, Clone)]
@@ -70,7 +110,7 @@ impl CodeLocation for YulExpression {
             | YulExpression::ConstantVariable(loc, ..)
             | YulExpression::StorageVariable(loc, ..)
             | YulExpression::BuiltInCall(loc, ..)
-            | YulExpression::MemberAccess(loc, ..)
+            | YulExpression::SuffixAccess(loc, ..)
             | YulExpression::FunctionCall(loc, ..) => *loc,
         }
     }
@@ -90,12 +130,26 @@ pub struct YulFunction {
     pub cfg_no: usize,
 }
 
+impl FunctionAttributes for YulFunction {
+    fn get_symbol_table(&self) -> &Symtable {
+        &self.symtable
+    }
+
+    fn get_parameters(&self) -> &Vec<Parameter> {
+        &*self.params
+    }
+
+    fn get_returns(&self) -> &Vec<Parameter> {
+        &*self.returns
+    }
+}
+
 #[derive(Clone, Debug)]
 pub enum YulStatement {
     FunctionCall(pt::Loc, bool, usize, Vec<YulExpression>),
     BuiltInCall(pt::Loc, bool, YulBuiltInFunction, Vec<YulExpression>),
     Block(Box<YulBlock>),
-    VariableDeclaration(pt::Loc, bool, Vec<usize>, Option<YulExpression>),
+    VariableDeclaration(pt::Loc, bool, Vec<(usize, Type)>, Option<YulExpression>),
     Assignment(pt::Loc, bool, Vec<YulExpression>, YulExpression),
     IfBlock(pt::Loc, bool, YulExpression, Box<YulBlock>),
     Switch {
@@ -118,6 +172,25 @@ pub enum YulStatement {
     Continue(pt::Loc, bool),
 }
 
+impl YulStatement {
+    pub fn is_reachable(&self) -> bool {
+        match self {
+            YulStatement::FunctionCall(_, reachable, ..)
+            | YulStatement::BuiltInCall(_, reachable, ..)
+            | YulStatement::VariableDeclaration(_, reachable, ..)
+            | YulStatement::Assignment(_, reachable, ..)
+            | YulStatement::IfBlock(_, reachable, ..)
+            | YulStatement::Switch { reachable, .. }
+            | YulStatement::For { reachable, .. }
+            | YulStatement::Leave(_, reachable)
+            | YulStatement::Break(_, reachable)
+            | YulStatement::Continue(_, reachable) => *reachable,
+
+            YulStatement::Block(block) => block.reachable,
+        }
+    }
+}
+
 #[derive(Debug, Clone)]
 pub struct CaseBlock {
     pub loc: pt::Loc,
@@ -132,12 +205,12 @@ impl Recurse for YulExpression {
             return;
         }
         match self {
-            YulExpression::BuiltInCall(_, _, args) | YulExpression::FunctionCall(_, _, args) => {
+            YulExpression::BuiltInCall(_, _, args) | YulExpression::FunctionCall(_, _, args, _) => {
                 for arg in args {
                     arg.recurse(cx, f);
                 }
             }
-            YulExpression::MemberAccess(_, expr, _) => {
+            YulExpression::SuffixAccess(_, expr, _) => {
                 expr.recurse(cx, f);
             }
 

+ 1 - 0
src/sema/yul/block.rs

@@ -41,6 +41,7 @@ pub fn resolve_yul_block(
         YulBlock {
             loc: *loc,
             reachable,
+            next_reachable,
             body,
         },
         next_reachable,

+ 89 - 0
src/sema/yul/builtin.rs

@@ -1,3 +1,4 @@
+use crate::Target;
 use phf::{phf_map, phf_set};
 
 pub struct YulBuiltinPrototype {
@@ -7,6 +8,18 @@ pub struct YulBuiltinPrototype {
     pub doc: &'static str,
     pub ty: YulBuiltInFunction,
     pub stops_execution: bool,
+    pub availability: [bool; 3],
+}
+
+impl YulBuiltinPrototype {
+    /// Checks if a certain Yul builtin is available for the given target
+    pub fn is_available(&self, target: &Target) -> bool {
+        match target {
+            Target::Ewasm => self.availability[0],
+            Target::Substrate { .. } => self.availability[1],
+            Target::Solana => self.availability[2],
+        }
+    }
 }
 
 // The enums declaration order should match that of the static vector containing the builtins
@@ -257,6 +270,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "Stop execution",
             ty: YulBuiltInFunction::Stop,
             stops_execution: true,
+            availability: [false, false, false],
         },
         YulBuiltinPrototype {
             name: "add",
@@ -265,6 +279,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "add(x, y) returns x + y",
             ty: YulBuiltInFunction::Add,
             stops_execution: false,
+            availability: [true, true, true],
         },
         YulBuiltinPrototype {
             name: "sub",
@@ -273,6 +288,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "sub(x, y) returns x - y",
             ty: YulBuiltInFunction::Sub,
             stops_execution: false,
+            availability: [true, true, true],
         },
         YulBuiltinPrototype {
             name: "mul",
@@ -281,6 +297,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "mul(x, y) returns x*y",
             ty: YulBuiltInFunction::Mul,
             stops_execution: false,
+            availability: [true, true, true],
         },
         YulBuiltinPrototype {
             name: "div",
@@ -289,6 +306,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "div(x, y) returns x/y or 0 if y == 0",
             ty: YulBuiltInFunction::Div,
             stops_execution: false,
+            availability: [true, true, true],
         },
         YulBuiltinPrototype {
             name: "sdiv",
@@ -297,6 +315,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "sdiv(x, y) returns x/y or 0 if y==0. Used for signed numbers in two's complement",
             ty: YulBuiltInFunction::SDiv,
             stops_execution: false,
+            availability: [true, true, true],
         },
         YulBuiltinPrototype {
             name: "mod",
@@ -305,6 +324,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "mod(x, y) returns x % y or 0 if y == 0",
             ty: YulBuiltInFunction::Mod,
             stops_execution: false,
+            availability: [true, true, true],
         },
         YulBuiltinPrototype {
             name: "smod",
@@ -313,6 +333,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "smod(x, y) returns x % y or 0 if y == 0. Used for signed numbers in two's complement",
             ty: YulBuiltInFunction::SMod,
             stops_execution: false,
+            availability: [true, true, true],
         },
         YulBuiltinPrototype {
             name: "exp",
@@ -321,6 +342,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "exp(x, y) returns x to the power of y",
             ty: YulBuiltInFunction::Exp,
             stops_execution: false,
+            availability: [true, true, true],
         },
         YulBuiltinPrototype {
             name: "not",
@@ -329,6 +351,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "not(x): bitwise \"not\" of x (every bit is negated)",
             ty: YulBuiltInFunction::Not,
             stops_execution: false,
+            availability: [true, true, true],
         },
         YulBuiltinPrototype {
             name: "lt",
@@ -337,6 +360,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "lt(x, y) returns 1 if x < y, 0 otherwise",
             ty: YulBuiltInFunction::Lt,
             stops_execution: false,
+            availability: [true, true, true],
         },
         YulBuiltinPrototype {
             name: "gt",
@@ -345,6 +369,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "gt(x, y) returns 1 if x > y, 0 otherwise",
             ty: YulBuiltInFunction::Gt,
             stops_execution: false,
+            availability: [true, true, true],
         },
         YulBuiltinPrototype {
             name: "slt",
@@ -353,6 +378,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "slt(x, y) returns 1 if x > y, 0 otherwise. Used for signed numbers in two's complement",
             ty: YulBuiltInFunction::Slt,
             stops_execution: false,
+            availability: [true, true, true],
         },
         YulBuiltinPrototype {
             name: "sgt",
@@ -361,6 +387,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "sgt(x, y) returns 1 if x > y, 0 otherwise. Used for signed numbers in two's complement",
             ty: YulBuiltInFunction::Sgt,
             stops_execution: false,
+            availability: [true, true, true],
         },
         YulBuiltinPrototype {
             name: "eq",
@@ -369,6 +396,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "eq(x, y) returns 1 if x == y, 0 otherwise",
             ty: YulBuiltInFunction::Eq,
             stops_execution: false,
+            availability: [true, true, true],
         },
         YulBuiltinPrototype {
             name: "iszero",
@@ -377,6 +405,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "iszero(x) returns 1 if x == 0, 0 otherwise",
             ty: YulBuiltInFunction::IsZero,
             stops_execution: false,
+            availability: [true, true, true],
         },
         YulBuiltinPrototype {
             name: "and",
@@ -385,6 +414,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "and(x, y) returns the bitwise \"and\" between x and y",
             ty: YulBuiltInFunction::And,
             stops_execution: false,
+            availability: [true, true, true],
         },
         YulBuiltinPrototype {
             name: "or",
@@ -393,6 +423,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "or(x, y) returns the bitwise \"or\" between x and y",
             ty: YulBuiltInFunction::Or,
             stops_execution: false,
+            availability: [true, true, true],
         },
         YulBuiltinPrototype {
             name: "xor",
@@ -401,6 +432,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "xor(x, y) returns the bitwise \"xor\" between x and y",
             ty: YulBuiltInFunction::Xor,
             stops_execution: false,
+            availability: [true, true, true],
         },
         YulBuiltinPrototype {
             name: "byte",
@@ -409,6 +441,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "byte(n, x) returns the nth byte of x, where the most significant byte is the 0th",
             ty: YulBuiltInFunction::Byte,
             stops_execution: false,
+            availability: [true, true, true],
         },
         YulBuiltinPrototype {
             name: "shl",
@@ -417,6 +450,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "shl(x, y) returns the logical shift left of y by x bits",
             ty: YulBuiltInFunction::Shl,
             stops_execution: false,
+            availability: [true, true, true],
         },
         YulBuiltinPrototype {
             name: "shr",
@@ -425,6 +459,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "shr(x, y) returns the logical shift right of y by x bits",
             ty: YulBuiltInFunction::Shr,
             stops_execution: false,
+            availability: [true, true, true],
         },
         YulBuiltinPrototype {
             name: "sar",
@@ -433,6 +468,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "signed arithmetic shift right y by x bits",
             ty: YulBuiltInFunction::Sar,
             stops_execution: false,
+            availability: [true, true, true],
         },
         YulBuiltinPrototype {
             name: "addmod",
@@ -441,6 +477,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "addmod(x, y, m) returns (x + y) % m or 0 if m == 0",
             ty: YulBuiltInFunction::AddMod,
             stops_execution: false,
+            availability: [true, true, true],
         },
         YulBuiltinPrototype {
             name: "mulmod",
@@ -449,6 +486,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "mulmod(x, y, m) returns (x * y) % m or 0 if m == 0",
             ty: YulBuiltInFunction::MulMod,
             stops_execution: false,
+            availability: [true, true, true],
         },
         YulBuiltinPrototype {
             name: "signextend",
@@ -457,6 +495,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "signextend(i, x) sign extends from (i*8+7)th bit counting from least significant",
             ty: YulBuiltInFunction::SignExtend,
             stops_execution: false,
+            availability: [false, false, false],
         },
         YulBuiltinPrototype {
             name: "keccak256",
@@ -465,6 +504,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "keccak256(p, n) performs keccak(mem[p...(p+n)])",
             ty: YulBuiltInFunction::Keccak256,
             stops_execution: false,
+            availability: [false, false, false],
         },
         YulBuiltinPrototype {
             name: "pc",
@@ -473,6 +513,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "Returns the current position in code, i.e. the program counter",
             ty: YulBuiltInFunction::Pc,
             stops_execution: false,
+            availability: [false, false, false],
         },
         YulBuiltinPrototype {
             name: "pop",
@@ -481,6 +522,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "pop(x) discard value x",
             ty: YulBuiltInFunction::Pop,
             stops_execution: false,
+            availability: [false, false, false],
         },
         YulBuiltinPrototype {
             name: "mload",
@@ -489,6 +531,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "mload(p) returns mem[p...(p+32)]",
             ty: YulBuiltInFunction::MLoad,
             stops_execution: false,
+            availability: [false, false, false],
         },
         YulBuiltinPrototype {
             name: "mstore",
@@ -497,6 +540,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "mstore(p, v) stores v into mem[p...(p+32)]",
             ty: YulBuiltInFunction::MStore,
             stops_execution: false,
+            availability: [false, false, false],
         },
         YulBuiltinPrototype {
             name: "mstore8",
@@ -505,6 +549,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "mstore8(p, v) stores (v & 0xff) into mem[p] (modified a single byte of v)",
             ty: YulBuiltInFunction::MStore8,
             stops_execution: false,
+            availability: [false, false, false],
         },
         YulBuiltinPrototype {
             name: "sload",
@@ -513,6 +558,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "sload(p) returns storage[p], i.e. memory on contract's storage",
             ty: YulBuiltInFunction::SLoad,
             stops_execution: false,
+            availability: [false, false, false],
         },
         YulBuiltinPrototype {
             name: "sstore",
@@ -521,6 +567,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "sstore(p) stores v into storage[p]",
             ty: YulBuiltInFunction::SStore,
             stops_execution: false,
+            availability: [false, false, false],
         },
         YulBuiltinPrototype {
             name: "msize",
@@ -529,6 +576,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "Returns the size of memory, i.e largest accessed memory index",
             ty: YulBuiltInFunction::MSize,
             stops_execution: false,
+            availability: [false, false, false],
         },
         YulBuiltinPrototype {
             name: "gas",
@@ -537,6 +585,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "Returns gas still available to execution",
             ty: YulBuiltInFunction::Gas,
             stops_execution: false,
+            availability: [true, true, false],
         },
         YulBuiltinPrototype {
             name: "address",
@@ -545,6 +594,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "Returns the address of the current contract / execution context",
             ty: YulBuiltInFunction::Address,
             stops_execution: false,
+            availability: [true, true, true],
         },
         YulBuiltinPrototype {
             name: "balance",
@@ -553,6 +603,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "balance(a) returns the wei balance at address a",
             ty: YulBuiltInFunction::Balance,
             stops_execution: false,
+            availability: [true, true, true],
         },
         YulBuiltinPrototype {
             name: "selfbalance",
@@ -561,6 +612,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "Returns the wei balance at the address of the current contract / execution context",
             ty: YulBuiltInFunction::SelfBalance,
             stops_execution: false,
+            availability: [true, true, true],
         },
         YulBuiltinPrototype {
             name: "caller",
@@ -569,6 +621,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "Returns the call sender",
             ty: YulBuiltInFunction::Caller,
             stops_execution: false,
+            availability: [true, true, true],
         },
         YulBuiltinPrototype {
             name: "callvalue",
@@ -577,6 +630,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "Returns the wei sent together with the current call",
             ty: YulBuiltInFunction::CallValue,
             stops_execution: false,
+            availability: [true, true, true],
         },
         YulBuiltinPrototype {
             name: "calldataload",
@@ -585,6 +639,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "calldataload(p) returns call data starting from position p (32 bytes)",
             ty: YulBuiltInFunction::CallDataLoad,
             stops_execution: false,
+            availability: [false, false, false],
         },
         YulBuiltinPrototype {
             name: "calldatasize",
@@ -593,6 +648,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "Returns the size of call data in bytes",
             ty: YulBuiltInFunction::CallDataSize,
             stops_execution: false,
+            availability: [false, false, false],
         },
         YulBuiltinPrototype {
             name: "calldatacopy",
@@ -601,6 +657,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "calldatacopy(t, f, s) copies s bytes from calldata at position f to mem at position t",
             ty: YulBuiltInFunction::CallDataCopy,
             stops_execution: false,
+            availability: [false, false, false],
         },
         YulBuiltinPrototype {
             name: "codesize",
@@ -609,6 +666,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "Returns the size of the current contract / execution context",
             ty: YulBuiltInFunction::CodeSize,
             stops_execution: false,
+            availability: [false, false, false],
         },
         YulBuiltinPrototype {
             name: "codecopy",
@@ -617,6 +675,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "codecopy(t, f, s) copies s bytes from code at position f to mem at position t",
             ty: YulBuiltInFunction::CodeCopy,
             stops_execution: false,
+            availability: [false, false, false],
         },
         YulBuiltinPrototype {
             name: "extcodesize",
@@ -625,6 +684,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "extcodesize(a) returns the size of the code at address a",
             ty: YulBuiltInFunction::ExtCodeSize,
             stops_execution: false,
+            availability: [false, false, false],
         },
         YulBuiltinPrototype {
             name: "extcodecopy",
@@ -633,6 +693,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "extcodecopy(a, t, f, s) copies s bytes from code located at address a at position f to mem at position t",
             ty: YulBuiltInFunction::ExtCodeCopy,
             stops_execution: false,
+            availability: [false, false, false],
         },
         YulBuiltinPrototype {
             name: "returndatasize",
@@ -641,6 +702,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "Returns the size of the last returndata",
             ty: YulBuiltInFunction::ReturnDataSize,
             stops_execution: false,
+            availability: [false, false, false],
         },
         YulBuiltinPrototype {
             name: "returndatacopy",
@@ -649,6 +711,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "returndatacopy(t, f, s) copy s bytes from return data at position f to mem at position t",
             ty: YulBuiltInFunction::ReturnDataCopy,
             stops_execution: false,
+            availability: [false, false, false],
         },
         YulBuiltinPrototype {
             name: "extcodehash",
@@ -657,6 +720,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "extcodehash(a) returns the code hash of address a",
             ty: YulBuiltInFunction::ExtCodeHash,
             stops_execution: false,
+            availability: [false, false, false],
         },
         YulBuiltinPrototype {
             name: "create",
@@ -665,6 +729,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "create(v, p, n) creates new contract with code mem[p..(p+n)] and sends v wei. It returns the new address or 0 on error",
             ty: YulBuiltInFunction::Create,
             stops_execution: false,
+            availability: [false, false, false],
         },
         YulBuiltinPrototype {
             name: "create2",
@@ -673,6 +738,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "create2(v, p, n, s) new contract with code mem[p...(p+n)] at address keccak256(0xff . this . s . keccak256(mem[p...(p+n)]) and sends v wei.\n 0xff is a 1 byte value, 'this' is the current contract's address as a 20 byte value and 's' is a big endian 256-bit value. it returns 0 on error.",
             ty: YulBuiltInFunction::Create2,
             stops_execution: false,
+            availability: [false, false, false],
         },
         YulBuiltinPrototype {
             name: "call",
@@ -681,6 +747,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "call(g, a, v, in, insize, out, outsize) calls contract at address a with input mem[in...(in+insize)] providing f cas and v wei and outputs area mem[out...(out+outsize)]. It returns 0 on error and 1 on success",
             ty: YulBuiltInFunction::Call,
             stops_execution: false,
+            availability: [false, false, false],
         },
         YulBuiltinPrototype {
             name: "callcode",
@@ -689,6 +756,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "Identical to call(g, a, v, in, insize, out, outsize), but only use the code from a and stay in the context of the current contract otherwise",
             ty: YulBuiltInFunction::CallCode,
             stops_execution: false,
+            availability: [false, false, false],
         },
         YulBuiltinPrototype {
             name: "delegatecall",
@@ -697,6 +765,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "Identical to 'callcode' but also keep caller and callvalue",
             ty: YulBuiltInFunction::DelegateCall,
             stops_execution: false,
+            availability: [false, false, false],
         },
         YulBuiltinPrototype {
             name: "staticcall",
@@ -705,6 +774,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "Identical to call(g, a, 0, in, insize, out, outsize), but do not allow state modifications",
             ty: YulBuiltInFunction::StaticCall,
             stops_execution: false,
+            availability: [false, false, false],
         },
         YulBuiltinPrototype {
             name: "return",
@@ -713,6 +783,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "return(p, s) ends execution and returns data mem[p...(p+s)]",
             ty: YulBuiltInFunction::Return,
             stops_execution: true,
+            availability: [false, false, false],
         },
         YulBuiltinPrototype {
             name: "revert",
@@ -721,6 +792,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "revert(p, s) ends execution, reverts state changes and returns data mem[p...(p+s)]",
             ty: YulBuiltInFunction::Revert,
             stops_execution: true,
+            availability: [false, false, false],
         },
         YulBuiltinPrototype {
             name: "selfdestruct",
@@ -729,6 +801,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "selfdestruct(a) ends execution, destroy current contract and sends funds to a",
             ty: YulBuiltInFunction::SelfDestruct,
             stops_execution: true,
+            availability: [true, true, true],
         },
         YulBuiltinPrototype {
             name: "invalid",
@@ -737,6 +810,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "Ends execution with invalid instruction",
             ty: YulBuiltInFunction::Invalid,
             stops_execution: true,
+            availability: [true, true, true],
         },
         YulBuiltinPrototype {
             name: "log0",
@@ -745,6 +819,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "log(p, s): log without topics and data mem[p...(p+s)]",
             ty: YulBuiltInFunction::Log0,
             stops_execution: false,
+            availability: [false, false, false],
         },
         YulBuiltinPrototype {
             name: "log1",
@@ -753,6 +828,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "log1(p, s, t1): log with topic t1 and data mem[p...(p+s)]",
             ty: YulBuiltInFunction::Log1,
             stops_execution: false,
+            availability: [false, false, false],
         },
         YulBuiltinPrototype {
             name: "log2",
@@ -761,6 +837,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "log2(p, s, t1, t2): log with topics t1, t2 and data mem[p...(p+s)]",
             ty: YulBuiltInFunction::Log2,
             stops_execution: false,
+            availability: [false, false, false],
         },
         YulBuiltinPrototype {
             name: "log3",
@@ -769,6 +846,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "log3(p, s, t1, t2, t3): log with topics t1, t2, t3 and data mem[p...(p+s)]",
             ty: YulBuiltInFunction::Log3,
             stops_execution: false,
+            availability: [false, false, false],
         },
         YulBuiltinPrototype {
             name: "log4",
@@ -777,6 +855,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "log4(p, s, t1, t2, t3, t4): log with topics t1, t2, t3, t4 with data mem[p...(p+s)]",
             ty: YulBuiltInFunction::Log4,
             stops_execution: false,
+            availability: [false, false, false],
         },
         YulBuiltinPrototype {
             name: "chainid",
@@ -785,6 +864,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "Returns the ID of the executing chain",
             ty: YulBuiltInFunction::ChainId,
             stops_execution: false,
+            availability: [true, false, false],
         },
         YulBuiltinPrototype {
             name: "basefee",
@@ -793,6 +873,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "Return the current block's base fee",
             ty: YulBuiltInFunction::BaseFee,
             stops_execution: false,
+            availability: [true, false, false],
         },
         YulBuiltinPrototype {
             name: "origin",
@@ -801,6 +882,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "Returns the transaction sender",
             ty: YulBuiltInFunction::Origin,
             stops_execution: false,
+            availability: [true, true, true],
         },
         YulBuiltinPrototype {
             name: "gasprice",
@@ -809,6 +891,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "Returns the gas price of the transaction",
             ty: YulBuiltInFunction::GasPrice,
             stops_execution: false,
+            availability: [true, true, false],
         },
         YulBuiltinPrototype {
             name: "blockhash",
@@ -817,6 +900,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "blockhash(b) return the hash of block #b - only valid for the last 256 executing block excluding current",
             ty: YulBuiltInFunction::BlockHash,
             stops_execution: false,
+            availability: [true, false, false],
         },
         YulBuiltinPrototype {
             name: "coinbase",
@@ -825,6 +909,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "Returns the current mining beneficiary",
             ty: YulBuiltInFunction::CoinBase,
             stops_execution: false,
+            availability: [true, false, false],
         },
         YulBuiltinPrototype {
             name: "timestamp",
@@ -833,6 +918,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "Returns the timestamp of the current block in seconds since the epoch",
             ty: YulBuiltInFunction::Timestamp,
             stops_execution: false,
+            availability: [true, true, true],
         },
         YulBuiltinPrototype {
             name: "number",
@@ -841,6 +927,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "Returns the current block's number",
             ty: YulBuiltInFunction::Number,
             stops_execution: false,
+            availability: [true, true, true],
         },
         YulBuiltinPrototype {
             name: "difficulty",
@@ -849,6 +936,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "Returns the difficulty of the current block",
             ty: YulBuiltInFunction::Difficulty,
             stops_execution: false,
+            availability: [true, false, false],
         },
         YulBuiltinPrototype {
             name: "gaslimit",
@@ -857,6 +945,7 @@ static YUL_BUILTIN: [YulBuiltinPrototype; 76] =
             doc: "Returns the current block's gas limit",
             ty: YulBuiltInFunction::GasLimit,
             stops_execution: false,
+            availability: [true, false, false],
         },
     ];
 

+ 73 - 17
src/sema/yul/expression.rs

@@ -78,8 +78,8 @@ pub(crate) fn resolve_yul_expression(
             resolve_function_call(function_table, func_call, context, symtable, ns)
         }
 
-        pt::YulExpression::Member(loc, expr, id) => {
-            resolve_member_access(loc, expr, id, context, symtable, function_table, ns)
+        pt::YulExpression::SuffixAccess(loc, expr, id) => {
+            resolve_suffix_access(loc, expr, id, context, symtable, function_table, ns)
         }
     }
 }
@@ -386,6 +386,18 @@ pub(crate) fn resolve_function_call(
 
     if let Some(built_in) = parse_builtin_keyword(func_call.id.name.as_str()) {
         let prototype = &built_in.get_prototype_info();
+        if !prototype.is_available(&ns.target) {
+            ns.diagnostics.push(Diagnostic::error(
+                func_call.loc,
+                format!(
+                    "builtin '{}' is not available for target {}. Please, open a GitHub issue \
+                at https://github.com/hyperledger-labs/solang/issues \
+                if there is need to support this function",
+                    prototype.name, ns.target
+                ),
+            ));
+            return Err(());
+        }
         if prototype.no_args as usize != func_call.arguments.len() {
             ns.diagnostics.push(Diagnostic {
                 level: Level::Error,
@@ -445,6 +457,7 @@ pub(crate) fn resolve_function_call(
             func_call.id.loc,
             fn_no,
             resolved_arguments,
+            func.returns.clone(),
         ));
         function_table.function_called(fn_no);
         return resolved_fn;
@@ -510,7 +523,7 @@ fn check_function_argument(
 }
 
 /// Resolve variables accessed with suffixes (e.g. 'var.slot', 'var.offset')
-fn resolve_member_access(
+fn resolve_suffix_access(
     loc: &pt::Loc,
     expr: &pt::YulExpression,
     id: &Identifier,
@@ -595,7 +608,7 @@ fn resolve_member_access(
             }
         }
 
-        YulExpression::MemberAccess(..) => {
+        YulExpression::SuffixAccess(..) => {
             ns.diagnostics.push(Diagnostic::error(
                 id.loc,
                 "there cannot be multiple suffixes to a name".to_string(),
@@ -624,7 +637,7 @@ fn resolve_member_access(
         }
     }
 
-    Ok(YulExpression::MemberAccess(
+    Ok(YulExpression::SuffixAccess(
         *loc,
         Box::new(resolved_expr),
         suffix_type,
@@ -666,20 +679,47 @@ pub(crate) fn check_type(
                 ));
             }
 
-            YulExpression::MemberAccess(_, _, YulSuffix::Length) => {
-                return Some(Diagnostic::error(
-                    expr.loc(),
-                    "cannot assign a value to length".to_string(),
-                ));
+            YulExpression::SuffixAccess(_, member, YulSuffix::Length) => {
+                return if matches!(
+                    **member,
+                    YulExpression::SolidityLocalVariable(
+                        _,
+                        _,
+                        Some(StorageLocation::Calldata(_)),
+                        _
+                    )
+                ) {
+                    Some(Diagnostic::error(
+                        expr.loc(),
+                        "assignment to length is not implemented. If there is need for this feature, please file a Github issue \
+                        at https://github.com/hyperledger-labs/solang/issues\
+                        ".to_string(),
+                    ))
+                } else {
+                    Some(Diagnostic::error(
+                        expr.loc(),
+                        "this expression does not support the '.length' suffix".to_string(),
+                    ))
+                }
             }
 
-            YulExpression::MemberAccess(_, _, YulSuffix::Offset) => {
-                return Some(Diagnostic::error(
-                    expr.loc(),
-                    "cannot assign a value to offset".to_string(),
-                ));
+            YulExpression::SuffixAccess(_, member, YulSuffix::Offset) => {
+                if !matches!(
+                    **member,
+                    YulExpression::SolidityLocalVariable(
+                        _,
+                        _,
+                        Some(StorageLocation::Calldata(_)),
+                        _
+                    )
+                ) {
+                    return Some(Diagnostic::error(
+                        expr.loc(),
+                        "cannot assign a value to offset".to_string(),
+                    ));
+                }
             }
-            YulExpression::MemberAccess(_, exp, YulSuffix::Slot) => {
+            YulExpression::SuffixAccess(_, exp, YulSuffix::Slot) => {
                 if matches!(**exp, YulExpression::StorageVariable(..)) {
                     return Some(Diagnostic::error(
                         exp.loc(),
@@ -688,6 +728,22 @@ pub(crate) fn check_type(
                 }
             }
 
+            YulExpression::SuffixAccess(_, exp, YulSuffix::Address)
+            | YulExpression::SuffixAccess(_, exp, YulSuffix::Selector) => {
+                if matches!(
+                    **exp,
+                    YulExpression::SolidityLocalVariable(_, Type::ExternalFunction { .. }, _, _)
+                ) {
+                    return Some(Diagnostic::error(
+                        expr.loc(),
+                        "assignment to selector and address is not implemented. \
+                        If there is need for these features, please file a GitHub issue at \
+                        https://github.com/hyperledger-labs/solang/issues"
+                            .to_string(),
+                    ));
+                }
+            }
+
             _ => (),
         }
 
@@ -711,7 +767,7 @@ pub(crate) fn check_type(
             Some(StorageLocation::Calldata(_)),
             ..,
         ) => {
-            if dims[0].is_none() {
+            if dims.last().unwrap().is_none() {
                 return Some(Diagnostic::error(
                     expr.loc(),
                     "Calldata arrays must be accessed with '.offset', '.length' and the 'calldatacopy' function".to_string()

+ 1 - 0
src/sema/yul/for_loop.rs

@@ -113,6 +113,7 @@ fn resolve_for_init_block(
         YulBlock {
             loc: init_block.loc,
             reachable,
+            next_reachable,
             body,
         },
         next_reachable,

+ 2 - 2
src/sema/yul/functions.rs

@@ -48,9 +48,9 @@ impl FunctionsTable {
     pub fn leave_scope(&mut self, ns: &mut Namespace) {
         let scope = self.scopes.pop_back().unwrap();
         for function_no in scope.values() {
-            let header = &self.lookup[*function_no];
+            let header = &self.lookup[*function_no - self.offset];
             if header.called {
-                self.resolved_functions[*function_no].called = true;
+                self.resolved_functions[*function_no - self.offset].called = true;
             } else {
                 ns.diagnostics.push(Diagnostic::warning(
                     header.id.loc,

+ 13 - 7
src/sema/yul/statements.rs

@@ -1,4 +1,4 @@
-use crate::ast::Namespace;
+use crate::ast::{Namespace, Type};
 use crate::sema::expression::ExprContext;
 use crate::sema::symtable::{LoopScopes, Symtable, VariableInitializer, VariableUsage};
 use crate::sema::yul::ast::{YulExpression, YulStatement};
@@ -108,6 +108,13 @@ pub(crate) fn resolve_yul_statement(
                 ns,
             )?;
             resolved_statements.push(resolved_switch.0);
+            ns.diagnostics.push(
+                Diagnostic::error(
+                    switch_statement.loc,
+                    "switch statements have no implementation in code generation yet. Please, file a GitHub issue \
+                    if there is urgent need for such a feature".to_string()
+                )
+            );
             Ok(resolved_switch.1)
         }
 
@@ -189,9 +196,8 @@ fn resolve_top_level_function_call(
                 !func_prototype.stops_execution,
             ))
         }
-        Ok(YulExpression::FunctionCall(loc, function_no, args)) => {
-            let func = function_table.get(function_no).unwrap();
-            if !func.returns.is_empty() {
+        Ok(YulExpression::FunctionCall(loc, function_no, args, returns)) => {
+            if !returns.is_empty() {
                 ns.diagnostics.push(Diagnostic::error(
                     loc,
                     "top level function calls must not return anything".to_string(),
@@ -222,7 +228,7 @@ fn resolve_variable_declaration(
     symtable: &mut Symtable,
     ns: &mut Namespace,
 ) -> Result<YulStatement, ()> {
-    let mut added_variables: Vec<usize> = Vec::with_capacity(variables.len());
+    let mut added_variables: Vec<(usize, Type)> = Vec::with_capacity(variables.len());
     for item in variables {
         if let Some(func) = function_table.find(&item.id.name) {
             ns.diagnostics.push(Diagnostic {
@@ -259,13 +265,13 @@ fn resolve_variable_declaration(
 
         if let Some(pos) = symtable.exclusive_add(
             &item.id,
-            ty,
+            ty.clone(),
             ns,
             VariableInitializer::Yul(initializer.is_some()),
             VariableUsage::YulLocalVariable,
             None,
         ) {
-            added_variables.push(pos);
+            added_variables.push((pos, ty));
         } else {
             return Err(());
         }

+ 15 - 25
src/sema/yul/tests/block.rs

@@ -41,13 +41,10 @@ contract testTypes {
 }
     "#;
     let ns = parse(file);
-    assert_eq!(ns.diagnostics.len(), 3);
+    assert_eq!(ns.diagnostics.len(), 2);
     assert!(ns
         .diagnostics
         .contains_message("found contract 'testTypes'"));
-    assert!(ns
-        .diagnostics
-        .contains_message("inline assembly is not yet supported"));
     assert!(ns
         .diagnostics
         .contains_message("yul function has never been used"));
@@ -97,13 +94,10 @@ contract testTypes {
 }
     "#;
     let ns = parse(file);
-    assert_eq!(ns.diagnostics.len(), 2);
+    assert_eq!(ns.diagnostics.len(), 1);
     assert!(ns
         .diagnostics
         .contains_message("found contract 'testTypes'"));
-    assert!(ns
-        .diagnostics
-        .contains_message("inline assembly is not yet supported"));
 }
 
 #[test]
@@ -150,13 +144,10 @@ contract testTypes {
 }
     "#;
     let ns = parse(file);
-    assert_eq!(ns.diagnostics.len(), 2);
+    assert_eq!(ns.diagnostics.len(), 1);
     assert!(ns
         .diagnostics
         .contains_message("found contract 'testTypes'"));
-    assert!(ns
-        .diagnostics
-        .contains_message("inline assembly is not yet supported"));
 }
 
 #[test]
@@ -193,7 +184,7 @@ contract testTypes {
     "#;
 
     let ns = parse(file);
-    assert!(ns.diagnostics.contains_message("unreachable yul statement"));
+    assert!(ns.diagnostics.contains_message("switch statements have no implementation in code generation yet. Please, file a GitHub issue if there is urgent need for such a feature"));
 
     let file = r#"
 contract testTypes {
@@ -216,13 +207,12 @@ contract testTypes {
     }
 }    "#;
     let ns = parse(file);
-    assert_eq!(ns.diagnostics.len(), 2);
     assert!(ns
         .diagnostics
         .contains_message("found contract 'testTypes'"));
     assert!(ns
         .diagnostics
-        .contains_message("inline assembly is not yet supported"));
+        .contains_message("switch statements have no implementation in code generation yet. Please, file a GitHub issue if there is urgent need for such a feature"));
 
     let file = r#"
     contract testTypes {
@@ -245,20 +235,19 @@ contract testTypes {
 }    "#;
 
     let ns = parse(file);
-    assert_eq!(ns.diagnostics.len(), 2);
     assert!(ns
         .diagnostics
         .contains_message("found contract 'testTypes'"));
     assert!(ns
         .diagnostics
-        .contains_message("inline assembly is not yet supported"));
+        .contains_message("switch statements have no implementation in code generation yet. Please, file a GitHub issue if there is urgent need for such a feature"));
 }
 
 #[test]
 fn unreachable_after_leave() {
     let file = r#"
 contract testTypes {
-    function testAsm() public pure {
+    function testAsm() pure public {
         assembly {
             {
                 function tryThis(b, a) -> c {
@@ -268,7 +257,7 @@ contract testTypes {
                     }
                     b := add(a, 6)
                     c := tryThat(b, 2)
-                    return(b, 0)
+                    invalid()
                 }
 
                 {
@@ -276,7 +265,8 @@ contract testTypes {
                         e := shr(d, 3)
                     }
 
-                    revert(tryThis(foo(3), 2), 4)
+                    let y := tryThis(foo(3), 2)
+                    invalid()
                 }
 
                 function tryThat(b, a) -> c {
@@ -285,24 +275,24 @@ contract testTypes {
                         leave
                     }
                     c := 5
-                    return(b, 0)
+                    invalid()
                 }
                 let x := 5
             }
         }
     }
-}    "#;
+}   "#;
 
     let ns = parse(file);
     assert_eq!(ns.diagnostics.len(), 4);
     assert!(ns
         .diagnostics
         .contains_message("found contract 'testTypes'"));
-    assert!(ns
-        .diagnostics
-        .contains_message("inline assembly is not yet supported"));
     assert!(ns.diagnostics.contains_message("unreachable yul statement"));
     assert!(ns
         .diagnostics
         .contains_message("yul variable 'x' has never been read"));
+    assert!(ns
+        .diagnostics
+        .contains_message("yul variable 'y' has never been read"));
 }

+ 76 - 15
src/sema/yul/tests/expression.rs

@@ -9,13 +9,15 @@ use crate::sema::yul::builtin::YulBuiltInFunction;
 use crate::sema::yul::expression::{check_type, resolve_yul_expression};
 use crate::sema::yul::functions::FunctionsTable;
 use crate::sema::yul::tests::parse;
-use crate::{ast, Target};
+use crate::{ast, parse_and_resolve, FileResolver, Target};
 use num_bigint::BigInt;
 use solang_parser::pt;
 use solang_parser::pt::{
     ContractTy, HexLiteral, Identifier, Loc, StorageLocation, StringLiteral, Visibility,
     YulFunctionCall,
 };
+use std::ffi::OsStr;
+use std::sync::Arc;
 
 #[test]
 fn resolve_bool_literal() {
@@ -662,7 +664,10 @@ fn function_call() {
     }));
     let res = resolve_yul_expression(&expr, &context, &mut symtable, &mut function_table, &mut ns);
     assert!(res.is_ok());
-    assert_eq!(YulExpression::FunctionCall(loc, 0, vec![]), res.unwrap());
+    assert_eq!(
+        YulExpression::FunctionCall(loc, 0, vec![], Arc::new(vec![])),
+        res.unwrap()
+    );
 
     let expr = pt::YulExpression::FunctionCall(Box::new(YulFunctionCall {
         loc,
@@ -764,7 +769,7 @@ fn check_arguments() {
     assert!(!ns.diagnostics.is_empty());
     assert_eq!(
         ns.diagnostics.iter().next().unwrap().message,
-        "builtin function 'pop' returns nothing"
+        "builtin 'pop' is not available for target ewasm. Please, open a GitHub issue at https://github.com/hyperledger-labs/solang/issues if there is need to support this function"
     );
     ns.diagnostics = Diagnostics::default();
 
@@ -853,7 +858,7 @@ fn test_member_access() {
         Symbol::Variable(loc, Some(0), 0),
     );
 
-    let expr = pt::YulExpression::Member(
+    let expr = pt::YulExpression::SuffixAccess(
         loc,
         Box::new(pt::YulExpression::BoolLiteral(loc, true, None)),
         Identifier {
@@ -871,7 +876,7 @@ fn test_member_access() {
     );
     ns.diagnostics = Diagnostics::default();
 
-    let expr = pt::YulExpression::Member(
+    let expr = pt::YulExpression::SuffixAccess(
         loc,
         Box::new(pt::YulExpression::BoolLiteral(loc, true, None)),
         Identifier {
@@ -889,7 +894,7 @@ fn test_member_access() {
     );
     ns.diagnostics = Diagnostics::default();
 
-    let expr = pt::YulExpression::Member(
+    let expr = pt::YulExpression::SuffixAccess(
         loc,
         Box::new(pt::YulExpression::Variable(Identifier {
             loc,
@@ -905,7 +910,7 @@ fn test_member_access() {
     assert!(res.is_ok());
     assert!(ns.diagnostics.is_empty());
     assert_eq!(
-        YulExpression::MemberAccess(
+        YulExpression::SuffixAccess(
             loc,
             Box::new(YulExpression::StorageVariable(loc, Type::Bool, 0, 0)),
             YulSuffix::Slot
@@ -1029,7 +1034,7 @@ contract testTypes {
     let ns = parse(file);
     assert!(ns
         .diagnostics
-        .contains_message("cannot assign a value to length"));
+        .contains_message("assignment to length is not implemented. If there is need for this feature, please file a Github issue at https://github.com/hyperledger-labs/solang/issues"));
 
     let file = r#"
 contract testTypes {
@@ -1114,13 +1119,10 @@ contract testTypes {
 }
     "#;
     let ns = parse(file);
-    assert_eq!(ns.diagnostics.len(), 3);
+    assert_eq!(ns.diagnostics.len(), 2);
     assert!(ns
         .diagnostics
         .contains_message("found contract 'testTypes'"));
-    assert!(ns
-        .diagnostics
-        .contains_message("inline assembly is not yet supported"));
     assert!(ns
         .diagnostics
         .contains_message("function parameter 'vl' has never been read"));
@@ -1411,7 +1413,7 @@ contract C {
     assert_eq!(ns.diagnostics.len(), 2);
     assert!(ns
         .diagnostics
-        .contains_message("inline assembly is not yet supported"));
+        .contains_message("assignment to selector and address is not implemented. If there is need for these features, please file a GitHub issue at https://github.com/hyperledger-labs/solang/issues"));
 
     assert!(ns.diagnostics.contains_message("found contract 'C'"));
 }
@@ -1554,7 +1556,6 @@ fn external_function() {
     function (int) external returns (int) sPtr = this.testing;
         assembly {
             let t := sPtr.address
-            log0(1, t)
         }
         return sPtr(3);
     }
@@ -1564,5 +1565,65 @@ fn external_function() {
     assert!(ns.diagnostics.contains_message("found contract 'test'"));
     assert!(ns
         .diagnostics
-        .contains_message("inline assembly is not yet supported"));
+        .contains_message("yul variable 't' has never been read"));
+}
+
+#[test]
+fn unsupported_builtin() {
+    let file = r#"
+contract foo {
+       function testing() view public {
+       assembly {
+           let f := gaslimit()
+       }
+    }
+}
+    "#;
+    let mut cache = FileResolver::new();
+    cache.set_file_contents("test.sol", file.to_string());
+
+    let ns = parse_and_resolve(OsStr::new("test.sol"), &mut cache, Target::Solana);
+
+    assert!(ns.diagnostics.contains_message("builtin 'gaslimit' is not available for target solana. Please, open a GitHub issue at https://github.com/hyperledger-labs/solang/issues if there is need to support this function"));
+
+    let file = r#"
+contract foo {
+       function testing() view public {
+       assembly {
+           let f := coinbase()
+       }
+    }
+}
+    "#;
+
+    let mut cache = FileResolver::new();
+    cache.set_file_contents("test.sol", file.to_string());
+
+    let ns = parse_and_resolve(
+        OsStr::new("test.sol"),
+        &mut cache,
+        Target::Substrate {
+            address_length: 32,
+            value_length: 16,
+        },
+    );
+
+    assert!(ns.diagnostics.contains_message("builtin 'coinbase' is not available for target substrate. Please, open a GitHub issue at https://github.com/hyperledger-labs/solang/issues if there is need to support this function"));
+
+    let file = r#"
+    contract foo {
+       function testing() public {
+       assembly {
+           log0(1, 2)
+       }
+    }
+}
+    "#;
+
+    let mut cache = FileResolver::new();
+    cache.set_file_contents("test.sol", file.to_string());
+
+    let ns = parse_and_resolve(OsStr::new("test.sol"), &mut cache, Target::Ewasm);
+
+    assert!(ns.diagnostics.contains_message("builtin 'log0' is not available for target ewasm. Please, open a GitHub issue at https://github.com/hyperledger-labs/solang/issues if there is need to support this function"));
 }

+ 5 - 8
src/sema/yul/tests/for_loop.rs

@@ -43,7 +43,7 @@ contract testTypes {
         assembly {
 
             let a := 0
-            stop()
+            invalid()
             for {let i := 11
             } lt(i, 10) {i := add(i, 1)
         } {
@@ -68,7 +68,7 @@ contract testTypes {
 
             let a := 0
             for {let i := 11
-                stop()
+                invalid()
             } lt(i, 10) {i := add(i, 1)
         } {
                 a := shr(i, 2)
@@ -98,7 +98,7 @@ contract testTypes {
                 a := shr(i, 2)
                 let b := shr(6, 5)
                     a := mul(a, b)
-                stop()
+                invalid()
             }
             let x := 5
         }
@@ -117,7 +117,7 @@ contract testTypes {
             let a := 0
             for {let i := 11
             } lt(i, 10) {i := add(i, 1)
-            stop()
+            invalid()
         } {
                 a := shr(i, 2)
                 let b := shr(6, 5)
@@ -131,13 +131,10 @@ contract testTypes {
     "#;
 
     let ns = parse(file);
-    assert_eq!(ns.diagnostics.len(), 3);
+    assert_eq!(ns.diagnostics.len(), 2);
     assert!(ns
         .diagnostics
         .contains_message("found contract 'testTypes'"));
-    assert!(ns
-        .diagnostics
-        .contains_message("inline assembly is not yet supported"));
     assert!(ns
         .diagnostics
         .contains_message("yul variable 'x' has never been read"));

+ 1 - 1
src/sema/yul/tests/mod.rs

@@ -17,5 +17,5 @@ pub(crate) fn parse(src: &'static str) -> ast::Namespace {
     let mut cache = FileResolver::new();
     cache.set_file_contents("test.sol", src.to_string());
 
-    parse_and_resolve(OsStr::new("test.sol"), &mut cache, Target::Solana)
+    parse_and_resolve(OsStr::new("test.sol"), &mut cache, Target::Ewasm)
 }

+ 19 - 16
src/sema/yul/tests/mutability.rs

@@ -48,7 +48,7 @@ fn inside_argument() {
     function testAsm(uint[] calldata vl) public pure {
         assembly {
             {
-                return(balance(4), 5)
+                let y := add(balance(4), 5)
                 function foo(a, b) {
                     let x := 5
                     let ret := add(sub(b, x), a)
@@ -141,7 +141,7 @@ fn if_block() {
         assembly {
             {
                 if balance(5) {
-                    return(0, 1)
+                    invalid()
                 }
             }
         }
@@ -161,7 +161,7 @@ fn if_block() {
                 let x := 2
                 if gt(x, 4) {
                     x := balance(4)
-                    return(0, x)
+                    invalid()
                 }
             }
         }
@@ -176,6 +176,7 @@ fn if_block() {
 
 #[test]
 fn switch() {
+    // TODO: switch statements are not yet supported, so there is no way to test mutability here
     let file = r#"
     contract testTypes {
     function testAsm(uint[] calldata vl) public pure {
@@ -195,7 +196,7 @@ fn switch() {
     let ns = parse(file);
     assert!(ns
         .diagnostics
-        .contains_message("function declared 'pure' but this expression reads from state"));
+        .contains_message("switch statements have no implementation in code generation yet. Please, file a GitHub issue if there is urgent need for such a feature"));
 
     let file = r#"
     contract testTypes {
@@ -217,7 +218,7 @@ fn switch() {
     let ns = parse(file);
     assert!(ns
         .diagnostics
-        .contains_message("function declared 'pure' but this expression reads from state"));
+        .contains_message("switch statements have no implementation in code generation yet. Please, file a GitHub issue if there is urgent need for such a feature"));
 
     let file = r#"
     contract testTypes {
@@ -239,7 +240,7 @@ fn switch() {
     let ns = parse(file);
     assert!(ns
         .diagnostics
-        .contains_message("function declared 'pure' but this expression reads from state"));
+        .contains_message("switch statements have no implementation in code generation yet. Please, file a GitHub issue if there is urgent need for such a feature"));
 }
 
 #[test]
@@ -347,9 +348,10 @@ fn pure_function() {
 }    "#;
 
     let ns = parse(file);
+    // TODO: There is no implemented function that writes to state
     assert!(ns
         .diagnostics
-        .contains_message("function declared 'pure' but this expression writes to state"));
+        .contains_message("builtin 'log0' is not available for target ewasm. Please, open a GitHub issue at https://github.com/hyperledger-labs/solang/issues if there is need to support this function"));
 }
 
 #[test]
@@ -368,10 +370,11 @@ fn view_function() {
 }
     "#;
 
+    // TODO: There is no implemented function that writes to the state, so there is no way to test mutability here
     let ns = parse(file);
     assert!(ns
         .diagnostics
-        .contains_message("function declared 'view' but this expression writes to state"));
+        .contains_message("builtin 'create' is not available for target ewasm. Please, open a GitHub issue at https://github.com/hyperledger-labs/solang/issues if there is need to support this function"));
 
     let file = r#"
     contract testTypes {
@@ -379,7 +382,7 @@ fn view_function() {
         assembly {
             {
                 for {let i := 6} gt(i, 0) {} {
-                    return(selfbalance(), i)
+                    i := add(selfbalance(), i)
                 }
             }
         }
@@ -387,13 +390,10 @@ fn view_function() {
 }    "#;
 
     let ns = parse(file);
-    assert_eq!(ns.diagnostics.len(), 2);
+    assert_eq!(ns.diagnostics.len(), 1);
     assert!(ns
         .diagnostics
         .contains_message("found contract 'testTypes'"));
-    assert!(ns
-        .diagnostics
-        .contains_message("inline assembly is not yet supported"));
 }
 
 #[test]
@@ -404,7 +404,7 @@ fn function_without_modifier() {
         assembly {
             {
                 for {let i := caller()} gt(i, 0) {} {
-                    return(create(1, 2, 3), i)
+                    let x := add(i, 2)
                 }
             }
         }
@@ -412,11 +412,14 @@ fn function_without_modifier() {
 }
     "#;
     let ns = parse(file);
-    assert_eq!(ns.diagnostics.len(), 2);
+    assert_eq!(ns.diagnostics.len(), 3);
     assert!(ns
         .diagnostics
         .contains_message("found contract 'testTypes'"));
     assert!(ns
         .diagnostics
-        .contains_message("inline assembly is not yet supported"));
+        .contains_message("yul variable 'x' has never been read"));
+    assert!(ns
+        .diagnostics
+        .contains_message("function can be declared 'view'"));
 }

+ 6 - 18
src/sema/yul/tests/statements.rs

@@ -189,13 +189,10 @@ contract testTypes {
     "#;
 
     let ns = parse(file);
-    assert_eq!(ns.diagnostics.len(), 2);
+    assert_eq!(ns.diagnostics.len(), 1);
     assert!(ns
         .diagnostics
         .contains_message("found contract 'testTypes'"));
-    assert!(ns
-        .diagnostics
-        .contains_message("inline assembly is not yet supported"));
 }
 
 #[test]
@@ -235,13 +232,10 @@ contract testTypes {
     "#;
 
     let ns = parse(file);
-    assert_eq!(ns.diagnostics.len(), 2);
+    assert_eq!(ns.diagnostics.len(), 1);
     assert!(ns
         .diagnostics
         .contains_message("found contract 'testTypes'"));
-    assert!(ns
-        .diagnostics
-        .contains_message("inline assembly is not yet supported"));
 }
 
 #[test]
@@ -277,7 +271,7 @@ contract testTypes {
                 } lt(i, 0x100) {
                     i := add(i, 0x20)
                 } {
-                    x := add(x, mload(i))
+                    x := add(x, i)
                     if lt(x, 30) {
                         continue
                     }
@@ -290,13 +284,10 @@ contract testTypes {
     "#;
 
     let ns = parse(file);
-    assert_eq!(ns.diagnostics.len(), 2);
+    assert_eq!(ns.diagnostics.len(), 1);
     assert!(ns
         .diagnostics
         .contains_message("found contract 'testTypes'"));
-    assert!(ns
-        .diagnostics
-        .contains_message("inline assembly is not yet supported"));
 }
 
 #[test]
@@ -332,7 +323,7 @@ contract testTypes {
                 } lt(i, 0x100) {
                     i := add(i, 0x20)
                 } {
-                    x := add(x, mload(i))
+                    x := add(x, i)
                     if lt(x, 30) {
                         break
                     }
@@ -345,11 +336,8 @@ contract testTypes {
     "#;
 
     let ns = parse(file);
-    assert_eq!(ns.diagnostics.len(), 2);
+    assert_eq!(ns.diagnostics.len(), 1);
     assert!(ns
         .diagnostics
         .contains_message("found contract 'testTypes'"));
-    assert!(ns
-        .diagnostics
-        .contains_message("inline assembly is not yet supported"));
 }

+ 2 - 5
src/sema/yul/tests/switch.rs

@@ -72,6 +72,7 @@ contract testTypes {
 
 #[test]
 fn correct_switch() {
+    // TODO: switch statements are not yet implemented
     let file = r#"
 contract testTypes {
     function testAsm() public pure {
@@ -98,11 +99,7 @@ contract testTypes {
     "#;
 
     let ns = parse(file);
-    assert_eq!(ns.diagnostics.len(), 2);
     assert!(ns
         .diagnostics
-        .contains_message("found contract 'testTypes'"));
-    assert!(ns
-        .diagnostics
-        .contains_message("inline assembly is not yet supported"));
+        .contains_message("switch statements have no implementation in code generation yet. Please, file a GitHub issue if there is urgent need for such a feature"));
 }

+ 2 - 2
src/sema/yul/tests/types.rs

@@ -26,7 +26,7 @@ fn incompatible_argument() {
 contract testTypes {
     function testAsm() public view {
         assembly {
-            let x := add(log0(0, 1), 5)
+            let x := add(invalid(), 5)
         }
     }
 }
@@ -34,7 +34,7 @@ contract testTypes {
     let ns = parse(file);
     assert!(ns
         .diagnostics
-        .contains_message("builtin function 'log0' returns nothing"));
+        .contains_message("builtin function 'invalid' returns nothing"));
 
     let file = r#"
     contract testTypes {

+ 6 - 9
src/sema/yul/tests/unused_variable.rs

@@ -10,7 +10,7 @@ contract testTypes {
             let a
             for {let i := 11
               } lt(i, 10) {i := add(i, 1)
-            stop()
+            invalid()
         } {
                 let x := shr(i, 2)
                 let b := shr(6, 5)
@@ -35,7 +35,7 @@ contract testTypes {
             for {let i := 11
                 let c :=5
               } lt(i, 10) {i := add(i, 1)
-            stop()
+            invalid()
         } {
                 let x := shr(i, 2)
                 let b := shr(6, 5)
@@ -63,7 +63,7 @@ fn correct_contracts() {
             for {let i := 11
                 let c :=5
               } c {i := add(i, 1)
-            stop()
+            invalid()
         } {
                 let x := shr(i, 2)
                 let b := shr(6, 5)
@@ -100,22 +100,19 @@ fn correct_contracts() {
         int b;
     }
     test tt1;
-    function testAsm(uint256[] calldata vl) public {
+    function testAsm(uint256[] calldata vl) view public {
         test storage tt2 = tt1;
         assembly {
             let g := vl.length
             let adr := add(g, tt2.slot)
-            sstore(adr, b.slot)
+            tt2.slot := sub(adr, b.slot)
         }
     }
 }    "#;
 
     let ns = parse(file);
-    assert_eq!(ns.diagnostics.len(), 2);
+    assert_eq!(ns.diagnostics.len(), 1);
     assert!(ns
         .diagnostics
         .contains_message("found contract 'testTypes'"));
-    assert!(ns
-        .diagnostics
-        .contains_message("inline assembly is not yet supported"));
 }

+ 5 - 5
src/sema/yul/types.rs

@@ -58,7 +58,7 @@ pub(crate) fn verify_type_from_expression(
         | YulExpression::SolidityLocalVariable(_, ty, None, _) => Ok(ty.clone()),
 
         YulExpression::SolidityLocalVariable(_, _, Some(_), _)
-        | YulExpression::MemberAccess(..)
+        | YulExpression::SuffixAccess(..)
         | YulExpression::StorageVariable(..) => Ok(Type::Uint(256)),
 
         YulExpression::BuiltInCall(_, ty, _) => {
@@ -81,14 +81,14 @@ pub(crate) fn verify_type_from_expression(
             }
         }
 
-        YulExpression::FunctionCall(_, function_no, ..) => {
+        YulExpression::FunctionCall(_, function_no, _, returns) => {
             let func = function_table.get(*function_no).unwrap();
-            if func.returns.is_empty() {
+            if returns.is_empty() {
                 Err(Diagnostic::error(
                     expr.loc(),
                     format!("function '{}' returns nothing", func.id.name),
                 ))
-            } else if func.returns.len() > 1 {
+            } else if returns.len() > 1 {
                 Err(Diagnostic::error(
                     expr.loc(),
                     format!(
@@ -97,7 +97,7 @@ pub(crate) fn verify_type_from_expression(
                     ),
                 ))
             } else {
-                Ok(func.returns[0].ty.clone())
+                Ok(returns[0].ty.clone())
             }
         }
     }

+ 2 - 2
src/sema/yul/unused_variable.rs

@@ -15,7 +15,7 @@ pub(crate) fn assigned_variable(ns: &mut Namespace, exp: &YulExpression, symtabl
             ns.contracts[*contract_no].variables[*var_no].assigned = true;
         }
 
-        YulExpression::MemberAccess(_, member, _) => {
+        YulExpression::SuffixAccess(_, member, _) => {
             assigned_variable(ns, member, symtable);
         }
 
@@ -35,7 +35,7 @@ pub(crate) fn used_variable(ns: &mut Namespace, exp: &YulExpression, symtable: &
             ns.contracts[*contract_no].variables[*var_no].read = true;
         }
 
-        YulExpression::MemberAccess(_, member, _) => {
+        YulExpression::SuffixAccess(_, member, _) => {
             used_variable(ns, member, symtable);
         }
 

+ 13 - 2
tests/codegen.rs

@@ -5,9 +5,18 @@ use std::io::{BufRead, BufReader};
 use std::{fs, path::PathBuf};
 
 #[test]
-fn testcases() {
+fn solidity_testcases() {
+    run_test_for_path("./tests/codegen_testcases/solidity/");
+}
+
+#[test]
+fn yul_testcases() {
+    run_test_for_path("./tests/codegen_testcases/yul/")
+}
+
+fn run_test_for_path(path: &str) {
     let ext = OsString::from("sol");
-    for entry in fs::read_dir("./tests/codegen_testcases/").unwrap() {
+    for entry in fs::read_dir(path).unwrap() {
         let path = entry.unwrap().path();
 
         if path.is_file() && path.extension() == Some(&ext) {
@@ -92,6 +101,8 @@ fn testcase(path: PathBuf) {
             Some(Test::NotCheck(needle)) => {
                 if !line.contains(needle) {
                     current_check += 1;
+                    // We should not advance line during a not check
+                    current_line -= 1;
                 }
             }
             Some(Test::Rewind) => {

+ 0 - 0
tests/codegen_testcases/address_cast.sol → tests/codegen_testcases/solidity/address_cast.sol


+ 0 - 0
tests/codegen_testcases/array_of_struct.sol → tests/codegen_testcases/solidity/array_of_struct.sol


+ 23 - 23
tests/codegen_testcases/common_subexpression_elimination.sol → tests/codegen_testcases/solidity/common_subexpression_elimination.sol

@@ -40,7 +40,7 @@ contract c1 {
         // CHECK: ty:int256 %d = (%x * %1.cse_temp)
         // CHECK: ty:int256 %2.cse_temp = (%x + %d)
         // CHECK: ty:int256 %3.cse_temp = ((arg #0) - (arg #1))
-        // CEHCK: branchcond (%2.cse_temp > int256 0), block1, block2
+        // CEHCK: branchcond (signed more %2.cse_temp > int256 0), block1, block2
         if (x + d > 0) {
 			int t = a-b;
             // CHECK: ty:int256 %t = %3.cse_temp
@@ -67,12 +67,12 @@ contract c1 {
         x = a+b-54;
         int d = x*(a+b);
 
-        // CHECK: branchcond (%2.cse_temp > int256 0), block1, block2
+        // CHECK: branchcond (signed more %2.cse_temp > int256 0), block1, block2
         if (x + d > 0) {
             // CHECK: ty:int256 %t = ((arg #0) - (arg #1))
 			int t = a-b;
 			bool e1 = t>3;
-            // CHECK: branchcond (%2.cse_temp < int256 0), block4, block5
+            // CHECK: branchcond (signed less %2.cse_temp < int256 0), block4, block5
             // CHECK: return ((%x - %d) + ((arg #0) - (arg #1)))
 		}
 		 else if (x+d < 0) {
@@ -96,7 +96,7 @@ contract c1 {
         int d = x*(a+b);
         int p = x+d;
         // CHECK: ty:int256 %2.cse_temp = (%x + %d)
-        // CHECK:branchcond (%2.cse_temp > int256 0), block2, block3
+        // CHECK:branchcond (signed more %2.cse_temp > int256 0), block2, block3
         while (x+d > 0) {
             // CHECK: ty:int256 %t = ((arg #0) - (arg #1))
             int t = a-b;
@@ -137,7 +137,7 @@ contract c1 {
 			bool e1 = t > 3;
             // CHECK: ty:int256 %x = (%x + %d)
 			x = x+d;
-        // CHECK: branchcond ((%x + %d) > int256 0), block1, block3
+        // CHECK: branchcond (signed more (%x + %d) > int256 0), block1, block3
 		} while(x+d > 0);
         int t = 3;
         bool p = t < 2;
@@ -218,7 +218,8 @@ contract c1 {
         // CHECK:  ty:int256 %1.cse_temp = ((arg #0) + (arg #1))
         int x = a + b + instance.a;
         // CHECK: ty:int256 %x = (%1.cse_temp + (load (struct %instance field 0)))
-        // CHECK: branchcond ((%x + int256((load (struct %instance field 1)))) < int256 0)
+        // CHECK: ty:int256 %2.cse_temp = ((arg #0) * (arg #1))
+        // CHECK: branchcond (signed less (%x + int256((load (struct %instance field 1)))) < int256 0)
         if(x  + int(instance.b) < 0) {
             // CHECK: ty:uint256 %p = uint256((%1.cse_temp + (load (struct %instance field 0))))
             uint p = uint(a+b+instance.a);
@@ -227,7 +228,7 @@ contract c1 {
 
 
         int8 trunc = int8(x);
-        // CHECK: ty:bool %e2 = ((sext int16 %trunc) > int16 2)
+        // CHECK: ty:bool %e2 = (signed more (sext int16 %trunc) > int16 2)
         bool e2 = trunc > 2;
         int8 trunc2 = 8 + int8(x);
         bool e3 = trunc2 < trunc;
@@ -239,7 +240,6 @@ contract c1 {
             int p2 = a+b;
             int p3 = p2 - x + a + b;
             int p4 = p2-x;
-            // CHECK: ty:int256 %2.cse_temp = ((arg #0) * (arg #1))
             int p5 = p3 + a*b+45;
             // CHECK: ty:int256 %p5 = ((%p3 + %2.cse_temp) + int256 45)
 
@@ -272,7 +272,7 @@ contract c1 {
 			bool e1 = t > 3;
             // CHECK: ty:int256 %x = (%x + %d)
 			x = x+d;
-        // CHECK: branchcond ((%x + %d) > int256 0), block1, block3
+        // CHECK: branchcond (signed more (%x + %d) > int256 0), block1, block3
 		} while(x+d > 0);
         int t = 3;
         bool p = t < 2;
@@ -292,7 +292,7 @@ contract c1 {
         string ast = "Hello!";
         string bst = "from Solang";
         string cst = ast + bst;
-        // CHECK: ty:int256 %1.cse_temp = ((arg #0) / (int256 2 * (arg #1)))
+        // CHECK: ty:int256 %1.cse_temp = (signed divide (arg #0) / (int256 2 * (arg #1)))
         // CHECK: call c1::c1::function::get__int256_int256 %1.cse_temp, (arg #1)
         int p = a + get(a/(2*b), b);
 
@@ -318,7 +318,7 @@ contract c1 {
             ast = ast + "a";
         }
 
-        // CHECK: call c1::c1::function::get__int256_int256 (arg #1), ((arg #0) / (arg #1))
+        // CHECK: call c1::c1::function::get__int256_int256 (arg #1), (signed divide (arg #0) / (arg #1))
         return get(b, a/b);
     }
 
@@ -380,7 +380,7 @@ contract c1 {
             }
         }
 
-        // CHECK: branchcond ((%a + (arg #1)) < int256 0), block14, block15
+        // CHECK: branchcond (signed less (%a + (arg #1)) < int256 0), block14, block15
         while(a+b < 0) {
             // CHECK: branchcond (strcmp (%c) ("a")), block16, block17
             if("a" == c) {
@@ -393,7 +393,7 @@ contract c1 {
             if("a" == c) {
                 a = a+b;
             }
-            // CHECK: branchcond ((%a + (arg #1)) > int256 0), block18, block20
+            // CHECK: branchcond (signed more (%a + (arg #1)) > int256 0), block18, block20
         } while(a+b > 0);
 
         for(int p=0; p<a; ++p) {
@@ -446,6 +446,7 @@ contract c1 {
             return (a << b) + 1;
         }
 
+        // CHECK: ty:uint256 %2.cse_temp = ((arg #0) & (arg #1))
         // CHECK: branchcond %1.cse_temp, block4, block3
         if(!b1 || c > 0) {
             // CHECK: = %b1
@@ -453,18 +454,17 @@ contract c1 {
             return a << b + 1;
         }
 
-        // CHECK: branchcond (%c > uint256 0), block11, block12
+        // CHECK: branchcond (unsigned more %c > uint256 0), block11, block12
         for(int i=0; c > 0 && i<10; ++i) {
             c++;
         }
 
-        // CHECK: ty:uint256 %2.cse_temp = ((arg #0) & (arg #1))
         // CHECK: branchcond (%2.cse_temp == uint256 0), block13, block14
         if (a & b == 0) {
             return c--;
         }
 
-        // CHECK: branchcond (%2.cse_temp > uint256 1), block15, block16
+        // CHECK: branchcond (unsigned more %2.cse_temp > uint256 1), block15, block16
         if (a & b > 1) {
             return a;
         }
@@ -478,13 +478,14 @@ contract c1 {
          bool e = k>0;
 
         for(int i=1; a-b < 0; i++) {
-            // CHECK: ty:int256 %p = ((%1.cse_temp * int256 5) - (%k / (arg #0)))
+            // CHECK: ty:int256 %4.cse_temp = (signed divide %k / (arg #0))
+            // CHECK: ty:int256 %p = ((%1.cse_temp * int256 5) - %4.cse_temp)
             int p = (a-b)*5-k/a;
             b++;
             // CHECK: ty:int256 %1.cse_temp = ((arg #0) - %b)
-            // CHECK: branchcond (%1.cse_temp < int256 0), block1, block4
+            // CHECK: branchcond (signed less %1.cse_temp < int256 0), block1, block4
             // CHECK: 	ty:int256 %2.cse_temp = ((arg #0) - %b)
-            // CHECK: branchcond (%2.cse_temp > int256 0), block6, block7
+            // CHECK: branchcond (signed more %2.cse_temp > int256 0), block6, block7
             while(a-b > 0) {
                 // CHECK: ty:int256 %p = (%2.cse_temp * int256 5)
                 p = (a-b)*5;
@@ -494,12 +495,11 @@ contract c1 {
         }
 
         do {
-            // CHECK: ty:int256 %4.cse_temp = (%k / (arg #0)
             // CHECK: ty:int256 %p.170 = ((((arg #0) - %b) * int256 5) - %4.cse_temp)
             int p = (a-b)*5-k/a;
             b++;
             bool e2 = p<1;
-            // CHECK: branchcond (((arg #0) - %b) < int256 0), block8, block10
+            // CHECK: branchcond (signed less ((arg #0) - %b) < int256 0), block8, block10
         }while(a - b < 0);
 
         int g = b;
@@ -507,13 +507,13 @@ contract c1 {
         uint p1 = uint(a)**uint(g);
         bool e9 = p1 == 0;
         // CHECK: ty:int256 %3.cse_temp = ((arg #0) - %b)
-        // CHECK: branchcond (%3.cse_temp < int256 0), block12, block13
+        // CHECK: branchcond (signed less %3.cse_temp < int256 0), block12, block13
         while(a - b < 0) {
             // CHECK: = ((%3.cse_temp * int256 5) - %4.cse_temp)
             int p = (a-b)*5-k/a;
             b=4;
             // CHECK: ty:int256 %5.cse_temp = ((arg #0) - int256 4)
-            // CHECK: branchcond (%5.cse_temp > int256 0), block14, block15
+            // CHECK: branchcond (signed more %5.cse_temp > int256 0), block14, block15
             if (a-b > 0) {
                 // CHECK: return (%4.cse_temp + int256(%p1))
                 // CHECK:  = (%5.cse_temp * int256 4)

+ 0 - 0
tests/codegen_testcases/const.sol → tests/codegen_testcases/solidity/const.sol


+ 0 - 0
tests/codegen_testcases/const_fail.sol → tests/codegen_testcases/solidity/const_fail.sol


+ 0 - 0
tests/codegen_testcases/constant_folding.sol → tests/codegen_testcases/solidity/constant_folding.sol


+ 0 - 0
tests/codegen_testcases/dead_storage.sol → tests/codegen_testcases/solidity/dead_storage.sol


+ 0 - 0
tests/codegen_testcases/dead_storage_off.sol → tests/codegen_testcases/solidity/dead_storage_off.sol


+ 0 - 0
tests/codegen_testcases/llvm_type.sol → tests/codegen_testcases/solidity/llvm_type.sol


+ 0 - 0
tests/codegen_testcases/multiple.sol → tests/codegen_testcases/solidity/multiple.sol


+ 0 - 0
tests/codegen_testcases/slice1.sol → tests/codegen_testcases/solidity/slice1.sol


+ 0 - 0
tests/codegen_testcases/storage_reference_return.sol → tests/codegen_testcases/solidity/storage_reference_return.sol


+ 4 - 4
tests/codegen_testcases/strength_reduce.sol → tests/codegen_testcases/solidity/strength_reduce.sol

@@ -75,7 +75,7 @@ contract test {
         // we're upcasting to 256 bits, but known bits will track this
         uint i = arg1;
         print("i:{}".format(i / 1e6));
-// CHECK: zext uint256 ((trunc uint64 %i) / uint64 1000000))
+// CHECK: (zext uint256 (unsigned divide (trunc uint64 %i) / uint64 1000000)
     }
 
 // BEGIN-CHECK: test::function::f8
@@ -84,7 +84,7 @@ contract test {
         for (uint i = 1e9; i < 1e9+101; i++) {
             print("i:{}".format(i / 1e6));
         }
-// CHECK: (%i / uint256 1000000)
+// CHECK: (unsigned divide %i / uint256 1000000)
     }
 
 
@@ -106,7 +106,7 @@ contract test {
         for (int i = 30; i >= 0; i--) {
             print("i:{}".format(i % 0x1_0000_0001));
         }
-// CHECK: (sext int256 ((trunc int64 %i) % int64 4294967297))
+// CHECK: sext int256 (signed modulo (trunc int64 %i) % int64 4294967297)
     }
 
 // BEGIN-CHECK: test::function::f11
@@ -114,6 +114,6 @@ contract test {
         for (int i = 0; i != 102; i++) {
             print("i:{}".format(i % 0x1_0000_0001));
         }
-// CHECK: (%i % int256 4294967297)
+// CHECK: (signed modulo %i % int256 4294967297)
     }
 }

+ 0 - 0
tests/codegen_testcases/temp1.sol → tests/codegen_testcases/solidity/temp1.sol


+ 0 - 0
tests/codegen_testcases/temp2.sol → tests/codegen_testcases/solidity/temp2.sol


+ 0 - 0
tests/codegen_testcases/unused_returns.sol → tests/codegen_testcases/solidity/unused_returns.sol


+ 0 - 0
tests/codegen_testcases/unused_variable_elimination.sol → tests/codegen_testcases/solidity/unused_variable_elimination.sol


+ 164 - 0
tests/codegen_testcases/yul/binary_arithmetic_builtins.sol

@@ -0,0 +1,164 @@
+
+// RUN: --target solana --emit cfg -Onone --no-cse
+contract testing {
+// BEGIN-CHECK: testing::testing::function::add_sub_mul__int16_int32_uint256_uint128
+    function add_sub_mul(int16 a, int32 b, uint256 c, uint128 d) public pure {
+        assembly {
+            // CHECK: ty:uint256 %e = (sext uint256 ((sext int32 (arg #0)) + (arg #1)))
+            let e := add(a, b)
+
+            // CHECK: ty:uint256 %f = uint256(((sext int256 (arg #1)) + int256((arg #2))))
+            let f := add(b, c)
+
+            // CHECK: ty:uint256 %g = ((arg #2) + (zext uint256 (arg #3)))
+            let g := add(c, d)
+
+            // CHECK: ty:uint256 %h = (sext uint256 ((sext int136 (arg #0)) + (zext int136 (arg #3))))
+            let h := add(a, d)
+
+            // CHECK: ty:uint256 %i = uint256(((sext int256 (arg #0)) + int256((arg #2))))
+            let i := add(a, c)
+
+            // CHECK: ty:uint256 %j = (sext uint256 ((sext int136 (arg #1)) + (zext int136 (arg #3))))
+            let j := add(b, d)
+
+            // CHECK: ty:int32 %k = ((sext int32 (arg #0)) - (arg #1))
+            let k : s32 := sub(a, b)
+
+            // CHECK: ty:int256 %l = int256(((arg #2) - (zext uint256 (arg #3))))
+            let l : s256 := sub(c, d)
+
+            // CHECK: ty:uint256 %m = ((arg #2) * (zext uint256 (arg #3)))
+            let m := mul(c, d)
+
+            // CHECK: ty:uint256 %n = uint256 43981
+            let n := hex"abcd"
+
+            // CHECK: ty:uint256 %o = uint256 1667327589
+            let o := "cafe"
+
+            // CHECK: ty:uint256 %p = uint256 73330734691809
+            let p := mul(n, o)
+
+            // CHECK: ty:uint256 %q = uint256 22193982385802470
+            let q := mul("cofe", hex"caffee")
+
+            // CHECK: ty:bool %r = false
+            let r : bool := false
+
+            // CHECK: ty:bool %s = true
+            let s : bool := true
+
+            // CHECK: ty:uint256 %t = (uint256 22193982385802470 + uint256(false))
+            let t := add(q, r)
+
+            // CHECK: ty:uint256 %u = uint256 -1
+            let u := sub(false, true)
+
+            // CHECK: ty:uint256 %v = uint256((true + false))
+            let v := add(s, r)
+        }
+    }
+
+// BEGIN-CHECK:  testing::testing::function::op_that_branch__uint256_uint256_int256_int256
+    function op_that_branch(uint256 a, uint256 b, int256 c, int256 d) public view {
+        assembly {
+            let e := div(a, b)
+            // CHECK: branchcond ((arg #1) == uint256 0), block1, block2
+            // CHECK: block1: # then
+            // CHECK: ty:uint256 %temp.49 = uint256 0
+            // CHECK: branch block3
+            // CHECK: block2: # else
+            // CHECK: ty:uint256 %temp.49 = (unsigned divide (arg #0) / (arg #1))
+            // CHECK: branch block3
+            // CHECK: block3: # endif
+            // CHECK: # phis: temp.49
+            // CHECK: ty:uint256 %e = %temp.49
+            
+            let f := sdiv(c, d)
+            // CHECK: branchcond ((arg #3) == int256 0), block4, block5
+            // CHECK: block4: # then
+            // CHECK: ty:uint256 %temp.50 = uint256 0
+            // CHECK: branch block6
+            // CHECK: block5: # else
+            // CHECK: ty:uint256 %temp.50 = (signed divide (arg #2) / (arg #3))
+            // CHECK: branch block6
+            // CHECK: block6: # endif
+            // CHECK: # phis: temp.50
+            // CHECK: ty:uint256 %f = %temp.50
+
+
+            let g := mod(a, b)
+            // CHECK: branchcond ((arg #1) == uint256 0), block7, block8
+            // CHECK: block7: # then
+            // CHECK: ty:uint256 %temp.51 = uint256 0
+            // CHECK: branch block9
+            // CHECK: block8: # else
+            // CHECK: ty:uint256 %temp.51 = (unsigned modulo (arg #0) % (arg #1))
+            // CHECK: branch block9
+            // CHECK: block9: # endif
+            // CHECK: # phis: temp.51
+            // CHECK: ty:uint256 %g = %temp.51
+
+            let h := smod(c, d)
+            // CHECK: branchcond ((arg #3) == int256 0), block10, block11
+            // CHECK: block10: # then
+            // CHECK: ty:uint256 %temp.52 = uint256 0
+            // CHECK: branch block12
+            // CHECK: block11: # else
+            // CHECK: ty:uint256 %temp.52 = (signed modulo (arg #2) % (arg #3))
+            // CHECK: branch block12
+            // CHECK: block12: # endif
+            // CHECK: # phis: temp.52
+            // CHECK: ty:uint256 %h = %temp.52
+        }
+    }
+
+// BEGIN-CHECK: testing::testing::function::exponential__int128_int8
+    function exponential(int128 a, int8 b) public pure {
+        assembly {
+            // CHECK: ty:uint256 %x = (sext uint256 ((arg #0) ** (sext int128 (arg #1))))
+            let x := exp(a, b)
+        }
+    }
+
+// BEGIN-CHECK: testing::testing::function::compare__uint64_uint64_int64_int64
+    function compare(uint64 a, uint64 b, int64 c, int64 d) public pure {
+        assembly {
+            // CHECK: ty:bool %e = (unsigned less (arg #0) < (arg #1))
+            let e : bool := lt(a, b)
+
+            // CHECK: ty:uint8 %f = uint8((unsigned more (arg #0) > (arg #1)))
+            let f : u8 := gt(a, b)
+
+            // CHECK: ty:int8 %h = int8((signed less (arg #2) < (arg #3)))
+            let h : s8 := slt(c, d)
+
+            // CHECK: ty:uint256 %i = uint256((signed more (arg #0) > (arg #1)))
+            let i := sgt(a, b)
+
+            // CHECK: ty:uint256 %j = uint256(((zext int72 (arg #0)) == (sext int72 (arg #3))))
+            let j := eq(a, d)
+        }
+    }
+
+// BEGIN-CHECK: testing::testing::function::bitwise_op__uint256_int256
+    function bitwise_op(uint256 a, int256 b) public pure {
+        assembly {
+            // CHECK: ty:uint256 %c = uint256((int256((arg #0)) | (arg #1)))
+            let c := or(a, b)
+
+            // CHECK: ty:uint256 %d = uint256(((arg #1) ^ int256((arg #0))))
+            let d := xor(b, a)
+
+            // CHECK: ty:uint256 %e = uint256(((arg #1) << int256((arg #0))))
+            let e := shl(a, b)
+
+            // CHECK: ty:uint256 %f = uint256((int256((arg #0)) >> (arg #1)))
+            let f := shr(b, a)
+
+            // CHECK: ty:uint256 %g = uint256(((arg #1) >> int256((arg #0))))
+            let g := sar(a, b)
+        }
+    }
+}

+ 60 - 0
tests/codegen_testcases/yul/common_builtins.sol

@@ -0,0 +1,60 @@
+// RUN: --target solana --emit cfg -Onone --no-cse
+
+contract testing {
+    // BEGIN-CHECK: testing::testing::function::not_isZero__uint64
+    function not_isZero(uint64 a) public pure {
+        assembly {
+            // CHECK: ty:uint256 %x = (zext uint256 ~(arg #0))
+            let x := not(a)
+
+            // CHECK: ty:bool %y = ((arg #0) == uint64 0)
+            let y : bool := iszero(a)
+        }
+    }
+
+// BEGIN-CHECK: testing::testing::function::addMod_mulMod__uint64_uint64_uint64
+    function addMod_mulMod(uint64 a, uint64 b, uint64 c) public pure {
+        assembly {
+            let x := addmod(a, b, c)
+            // CHECK: branchcond ((arg #2) == uint64 0), block1, block2
+            // CHECK: block1: # then
+            // CHECK: ty:uint256 %temp.11 = uint256 0
+            // CHECK: branch block3
+            // CHECK: block2: # else
+            // CHECK: ty:uint256 %temp.11 = (unsigned modulo ((arg #0) + (arg #1)) % (arg #2))
+            // CHECK: branch block3
+            // CHECK: block3: # endif
+            // CHECK: # phis: temp.11
+            // CHECK: ty:uint256 %x = %temp.11
+
+            let y :s32  := mulmod(a, b, c)
+            // CHECK: branchcond ((arg #2) == uint64 0), block4, block5
+            // CHECK: block4: # then
+            // CHECK: ty:uint256 %temp.12 = uint256 0
+            // CHECK: branch block6
+            // CHECK: block5: # else
+            // CHECK: ty:uint256 %temp.12 = (unsigned modulo ((arg #0) * (arg #1)) % (arg #2))
+            // CHECK: branch block6
+            // CHECK: block6: # endif
+            // CHECK: # phis: temp.12
+            // CHECK: ty:int32 %y = (trunc int32 %temp.12)
+        }
+    }
+
+// BEGIN-CHECK: testing::testing::function::byte_builtin__int64_uint256
+    function byte_builtin(int64 a, uint256 b) public pure {
+        assembly {
+            let x := byte(b, a)
+            // CHECK: branchcond ((arg #1) >= uint256 32), block1, block2
+            // CHECK: block1: # then
+            // CHECK: ty:uint256 %temp.13 = uint256 0
+            // CHECK: branch block3
+            // CHECK: block2: # else
+            // CHECK: ty:uint256 %temp.13 = (((sext uint256 (arg #0)) >> ((uint256 31 - (arg #1)) << uint256 3)) & uint256 255)
+            // CHECK: branch block3
+            // CHECK: block3: # endif
+            // CHECK: # phis: temp.13
+            // CHECK: ty:uint256 %x = %temp.13
+        }
+    }
+}

+ 47 - 0
tests/codegen_testcases/yul/common_subexpression_elimination.sol

@@ -0,0 +1,47 @@
+// RUN: --target solana --emit cfg
+
+contract testing  {
+    // BEGIN-CHECK: testing::testing::function::general_test__uint64
+    function general_test(uint64 a) public view returns (uint64, uint256) {
+        uint64 g = 0;
+        uint256 h = 0;
+        assembly {
+            function sum(a, b) -> ret1 {
+                ret1 := add(a, b)
+            }
+
+            function mix(a, b) -> ret1, ret2 {
+                ret1 := mul(a, b)
+                ret2 := add(a, b)
+            }
+
+            // CHECK: block1: # cond
+            // CHECK: ty:uint256 %1.cse_temp = (zext uint256 (arg #0))
+            // CHECK: branch block1
+            for {let i := 0} lt(i, 10) {i := add(i, 1)} {
+                // CHECK: block3: # body
+                // CHECK: branchcond (%1.cse_temp == uint256 259), block5, block6
+                if eq(a, 259) {
+                    break
+                }
+
+                // This is the if-condition after the loop
+                // block4: # end_for
+                // CHECK: branchcond ((unsigned less %1.cse_temp < uint256 10) | (%1.cse_temp == uint256 259)), block9, block10
+                g := sum(g, 2)
+                // CHECK: block6: # endif
+                // CHECK: branchcond (unsigned more %1.cse_temp > uint256 10), block7, block8
+                if gt(a, 10) {
+                    continue
+                }
+                g := sub(g, 1)
+            }
+
+            if or(lt(a, 10), eq(a, 259)) {
+                g, h := mix(g, 10)
+            }
+        }
+
+        return (g, h);
+    }
+}

+ 59 - 0
tests/codegen_testcases/yul/ewasm_builtin.sol

@@ -0,0 +1,59 @@
+// RUN: --target ewasm --emit cfg -Onone --no-cse
+
+contract Testing {
+
+// BEGIN-CHECK: Testing::Testing::function::builtins__uint256
+    function builtins(uint256 arg1) public view  {
+        assembly {
+            // CHECK: ty:uint256 %a = (zext uint256 (builtin Gasleft ()))
+            let a := gas()
+
+            // CHECK: ty:uint256 %b = (sext uint256 uint160((builtin GetAddress ())))
+            let b := address()
+
+            // CHECK: ty:uint256 %c = (sext uint256 (builtin Balance (address((trunc uint160 (arg #0))))))
+            let c := balance(arg1)
+
+            // CHECK: ty:uint256 %d = (sext uint256 (builtin Balance ((builtin GetAddress ()))))
+            let d := selfbalance()
+
+            // CHECK: ty:uint256 %e = (sext uint256 uint160((builtin Sender ())))
+            let e := caller()
+
+            // CHECK: ty:uint256 %f = (sext uint256 (builtin Value ()))
+            let f := callvalue()
+
+            // CHECK: ty:uint256 %g = (zext uint256 (builtin Gasprice ()))
+            let g := gasprice()
+
+            // CHECK: ty:uint256 %h = (builtin BlockHash ((trunc uint64 (arg #0))))
+            let h := blockhash(arg1)
+
+            // CHECK: ty:uint256 %i = (sext uint256 uint160((builtin BlockCoinbase ())))
+            let i := coinbase()
+
+            // ty:uint256 %j = (zext uint256 (builtin Timestamp ()))
+            let j := timestamp()
+
+            // CHECK: ty:uint256 %k = (zext uint256 (builtin BlockNumber ()))
+            let k := number()
+
+            // CHECK: y:uint256 %l = (builtin BlockDifficulty ())
+            let l := difficulty()
+
+            // CHECK: ty:uint256 %m = (zext uint256 (builtin GasLimit ()))
+            let m := gaslimit()
+
+            // CHECK: assert-failure
+            invalid()
+        }
+    }
+
+// BEGIN-CHECK: Testing::Testing::function::test_selfdestruct__uint256
+    function test_selfdestruct(uint256 arg1) public {
+        assembly {
+            // CHECK: selfdestruct address payable((trunc uint160 (arg #0)))
+            selfdestruct(arg1)
+        }
+    }
+}

+ 191 - 0
tests/codegen_testcases/yul/expression.sol

@@ -0,0 +1,191 @@
+// RUN: --target solana --emit cfg -Onone --no-cse
+
+uint128 constant global_cte = 5;
+contract testing {
+    
+// BEGIN-CHECK: testing::testing::function::boolLiteral
+    function boolLiteral() public pure {
+        assembly {
+            // CHECK: ty:uint256 %x = uint256(true)
+            let x := true
+
+            // CHECK: ty:bool %ss = (uint256 0 != uint256 5)
+            let ss : bool := 5
+
+            // CHECK: ty:uint256 %y = uint256(false)
+            let y := false
+
+            // CHECK: uint64(false)
+            let r : u64 := false
+
+            // CHECK: uint256 1
+            let s := true :u128
+
+            // CHECK: ty:uint32 %p = uint32 0
+            let p :u32 := false :u64
+        }
+    }
+
+// BEGIN-CHECK: testing::testing::function::numberLiteral
+    function numberLiteral() public pure {
+        assembly {
+            // CHECK: ty:int32 %x = int32 90
+            let x : s32 := 90
+
+            // CHECK: ty:uint256 %y = uint256 128
+            let y := 0x80
+
+            // ty:uint256 %y = uint256 128
+            let z := 0x80 : u8
+        }
+    }
+
+// BEGIN-CHECK: testing::testing::function::stringLiteral
+    function stringLiteral() public pure {
+        assembly {
+            // CHECK: ty:uint256 %x = uint256 427070744165
+            let x := "coffe"
+
+            // CHECK: ty:uint256 %y = uint256 831486
+            let y := hex"0caffe"
+        }
+    }
+
+    struct LocalTest {
+        uint32 c;
+    }
+
+// BEGIN-CHECK: testing::testing::function::yulLocalVariable__uint256
+    function yulLocalVariable(uint256 a) public pure {
+        assembly {
+            // CHECK: ty:uint256 %x = (uint256 2 + (arg #0))
+            let x := add(2, a)
+
+            // CHECK: ty:uint256 %y = (uint256 2 + (arg #0))
+            let y := x
+        }
+    }
+
+    uint32 constant contract_cte = 787;
+
+    // BEGIN-CHECK: testing::testing::function::constantVariable
+    function constantVariable() public pure {
+        assembly {
+            // CHECK: ty:uint256 %p1 = uint256 5
+            let p1 := global_cte
+
+            // CHECK: ty:uint256 %p2 = uint256 787
+            let p2 := contract_cte
+        }
+    }
+
+// BEGIN-CHECK: testing::testing::function::solidityLocalVariable
+    function solidityLocalVariable() public pure {
+        int32 a = 1;
+        int[] vec;
+        int[] memory mem_vec = vec;
+
+        int[4] cte_vec = [int(1), 2, 3, 4];
+        int[4] memory mem_cte_vec = [int(1), 2, 3, 4];
+
+        string b = "abc";
+
+        LocalTest struct_test = LocalTest({c: 2});
+        LocalTest memory mem_struct_test = LocalTest({c: 2});
+        assembly {
+            // CHECK: ty:uint256 %k = uint256 1
+            let k := a
+
+            // CHECK: ty:uint256 %l = %vec
+            let l := vec
+
+            // CHECK: ty:uint256 %m = %mem_vec
+            let m := mem_vec
+
+            // CHECK: ty:uint256 %n = %cte_vec
+            let n := cte_vec
+
+            // CHECK: ty:uint256 %o = %mem_cte_vec
+            let o := mem_cte_vec
+
+            // CHECK: ty:uint256 %p = %b
+            let p := b
+
+            // CHECK: ty:uint256 %r = %struct_test
+            let r := struct_test
+
+            // CHECK: ty:uint256 %s = %mem_struct_test
+            let s := mem_struct_test
+        }
+    }
+
+    int[] storage_vec;
+    int[] storage_vec_2;
+    // BEGIN-CHECK: testing::testing::function::memberAccess__uint64:_uint64:3
+    function memberAccess(uint64[] calldata vl, uint64[3] calldata vl_2) public view {
+        int[] storage l_storage_vec = storage_vec_2;
+        function () external fPtr = this.solidityLocalVariable;
+        assembly {
+            // CHECK: ty:uint256 %k = uint256 16
+            let k := storage_vec.slot
+
+            // CHECK: ty:uint256 %l = uint32 20
+            let l := l_storage_vec.slot
+
+            // CHECK: ty:uint256 %m = uint256 0
+            let m := storage_vec.offset
+
+            // CHECK: ty:uint256 %n = uint256 0
+            let n := l_storage_vec.offset
+
+            // CHECK: ty:uint256 %o = uint256((arg #0))
+            let o := vl.offset
+
+            // CHECK: ty:uint256 %p = (zext uint256 (builtin ArrayLength ((arg #0))))
+            let p := vl.length
+
+            // CHECK: ty:uint256 %q = uint256((builtin ExternalFunctionAddress (%fPtr)))
+            let q := fPtr.address
+
+            // CHECK: ty:uint256 %r = (zext uint256 (builtin FunctionSelector (%fPtr)))
+            let r := fPtr.selector
+
+            // CHECK: ty:uint256 %s = (arg #1)
+            let s := vl_2
+        }
+    }
+
+// BEGIN-CHECK: testing::testing::function::exprFuncCall__uint32
+    function exprFuncCall(uint32 a) public pure {
+        assembly {
+            function get(a) -> ret {
+                ret := sub(a, 2)
+            }
+
+            function doSmth() {
+                let y := 9
+            }
+
+            // CHECK: ty:int32 %k = (trunc int32 ((zext uint256 (arg #0)) + uint256 2))
+            let k : s32 := add(a, 2)
+
+            // CHECK: %ret.temp.57 = call testing::yul_function_0::get (sext uint256 (trunc int32 ((zext uint256 (arg #0)) + uint256 2)))
+            let x := get(k)
+            // CHECK: ty:uint256 %x = %ret.temp.57
+
+            // CHECK: %ret1.temp.58, %ret2.temp.59 = call testing::yul_function_2::multipleReturns %x, (trunc int32 ((zext uint256 (arg #0)) + uint256 2))
+            let l, m := multipleReturns(x, k)
+            // CHECK: ty:uint256 %l = (zext uint256 %ret1.temp.58)
+            // CHECK: ty:uint256 %m = (sext uint256 %ret2.temp.59)
+
+
+            // CHECK: = call testing::yul_function_1::doSmth
+            doSmth()
+
+            function multipleReturns(a, v : s32) -> ret1 : u8, ret2 : s64 {
+                ret1 := add(a, v)
+                ret2 := mul(a, v)
+            }
+        }
+    }
+}

+ 73 - 0
tests/codegen_testcases/yul/functions.sol

@@ -0,0 +1,73 @@
+// RUN: --target solana --emit cfg -Onone --no-cse
+
+contract testing {
+    function yul_function() public pure returns (uint256) {
+        uint256 c;
+        uint256 d;
+        assembly {
+            // BEGIN-CHECK: # function testing::yul_function_0::early_leave public:false selector:00000000 nonpayable:true
+            // CHECK: # params: uint256,uint256
+            // CHECK: # returns:
+            function early_leave(a, b) {
+                let x := add(a, b)
+                if lt(x, 2) {
+                    // CHECK: block1: # then
+                    x := sub(a, b)
+                    // CHECK: ty:uint256 %x = ((arg #0) - (arg #1))
+                    leave
+                    // CHECK: return
+                }
+                // CHECK: block2: # endif
+                // CHECK: ty:uint256 %x = (uint256 2 << (arg #0))
+                x := shl(a, 2)
+                // CHECK: return
+            }
+
+            // BEGIN-CHECK: function testing::yul_function_1::single_return public:false selector:00000000 nonpayable:true
+            // CHECK: # params: uint256,int32
+            // CHECK: # returns: uint256
+            function single_return(a, b : s32) -> ret1 {
+                if lt(a, 2) {
+                    // CHECK: block1: # then
+                    ret1 := add(sub(a,b), mul(shr(a, 2), 3))
+                    // CHECK: ty:uint256 %ret1 = uint256(((int256((arg #0)) - (sext int256 (arg #1))) + int256(((uint256 2 >> (arg #0)) * uint256 3))))
+                    leave
+                    // CHECK: return %ret1
+                }
+                // CHECK: block2: # endif
+                ret1 := a
+                // CHECK: ty:uint256 %ret1 = (arg #0)
+                // CHECK: return (arg #0)
+            }
+
+            // BEGIN-CHECK: function testing::yul_function_2::multiple_returns public:false selector:00000000 nonpayable:true
+            // CHECK: # params: uint256,uint256
+            // CHECK: # returns: uint64,uint256
+            function multiple_returns(a, b) -> ret1 : u64, ret2 {
+                if lt(a, 2) {
+                // CHECK: block1: # then
+                    ret1 := a
+                // CHECK: ty:uint64 %ret1 = (trunc uint64 (arg #0))
+                    ret2 := b
+                // CHECK: ty:uint256 %ret2 = (arg #1)
+                    leave
+                // CHECK: return (trunc uint64 (arg #0)), (arg #1)
+                }
+                // CHECK: ty:uint64 %ret1 = (trunc uint64 (arg #1))
+                ret1 := b
+                // CHECK: ty:uint256 %ret2 = (arg #0)
+                ret2 := a
+                // CHECK: return (trunc uint64 (arg #1)), (arg #0)
+            }
+
+            c := 1
+            d := 2
+            early_leave(c, d)
+
+            c := single_return(c, d)
+            c, d := multiple_returns(c, d)            
+        }
+
+        return c+d;
+    }
+}

+ 476 - 0
tests/codegen_testcases/yul/statements.sol

@@ -0,0 +1,476 @@
+// RUN: --target solana --emit cfg -Onone --no-cse
+
+contract testing {
+
+    // BEGIN-CHECK: testing::testing::function::calls_and_unreachable__uint64_uint64
+    function calls_and_unreachable(uint64 a, uint64 b) public pure {
+        assembly {
+            function test1(c : u128, d) {
+                let y := mul(c, d)
+            }
+
+            // CHECK:  = call testing::yul_function_0::test1 (zext uint128 (arg #0)), (zext uint256 (arg #1))
+            test1(a, b)
+
+            // CHECK: assert-failure
+            invalid()
+
+            // NOT-CHECK: ty:uint256 %x = 
+            let x := mul(a, b)
+        }
+    }
+
+    // BEGIN-CHECK: testing::testing::function::yul_block__uint16_int16
+    function yul_block(uint16 a, int16 b) public pure {
+        assembly {
+            // CHECK: ty:uint256 %x = (sext uint256 ((sext int24 (arg #1)) >> (zext int24 (arg #0))))
+            let x := shr(a, b)
+            {
+                // CHECK: ty:uint256 %y = ((sext uint256 ((sext int24 (arg #1)) >> (zext int24 (arg #0)))) * (zext uint256 (arg #0)))
+                let y := mul(x, a)
+
+                // CHECK: ty:uint8 %g = uint8((unsigned less int256(((sext uint256 ((sext int24 (arg #1)) >> (zext int24 (arg #0)))) * (zext uint256 (arg #0)))) < (sext int256 (arg #1))))
+                let g : u8 := lt(y, b)
+            }
+        }
+    }
+
+    // BEGIN-CHECK: testing::testing::function::variable_declaration__bool_uint8
+    function variable_declaration(bool a, uint8 b) public pure {
+        assembly {
+            function multiple() -> ret1, ret2 : s32 {
+                ret1 := 4
+                ret2 := 6
+            }
+
+            function unique() -> ret3 {
+                ret3 := 6
+            }
+
+            // CHECK: %ret3.temp.40 = call testing::yul_function_2::unique
+            // CHECK: ty:uint256 %c = %ret3.temp.40
+            let c := unique()
+
+            // CHECK: %ret1.temp.41, %ret2.temp.42 = call testing::yul_function_1::multiple
+            // CHECK: ty:uint256 %d = %ret1.temp.41
+            // CHECK: ty:int8 %e = (trunc int8 %ret2.temp.42)
+            let d, e : s8 := multiple()
+
+            // CHECK: ty:uint256 %f = uint256(((arg #0) == bool 0))
+            let f := iszero(a)
+            
+
+            // CHECK: ty:uint256 %g = undef
+            // CHECK: ty:uint256 %h = undef
+            // CHECK: ty:uint256 %i = undef
+            let g, h, i
+        }
+    }
+
+// BEGIN-CHECK: testing::testing::function::if_statement__bool
+    function if_statement(bool a) public pure {
+        assembly {
+            let x := 5
+            // CHECK: branchcond (arg #0), block1, block2
+            if a {
+            // CHECK: block1: # then
+            // CHECK: ty:uint256 %x = (uint256((arg #0)) + uint256 5)
+                x := add(a, x)
+
+                // CHECK: ty:bool %a = false
+                a := false
+            // CHECK: branch block2
+            }
+            // CHECK: block2: # endif
+
+            // CHECK: branchcond (uint256 0 != %x), block3, block4
+            if x {
+            // CHECK: block3: # then
+
+            // CHECK: ty:uint256 %x = uint256 5
+                x := 5
+            // CHECK: branch block4
+            }
+            // CHECK: block4: # endif
+
+            // CHECK: branchcond (unsigned less %x < uint256 3), block5, block6
+            if lt(x, 3) {
+            // CHECK: block5: # then
+
+            // CHECK: ty:uint256 %x = uint256 4
+                x := 4
+            // CHECK: branch block6
+            }
+            // CHECK: block6: # endif
+
+            // CHECK: branchcond (unsigned less %x < uint256 3), block7, block8
+            if lt(x, 3) {
+            // CHECK: block7: # then
+            // CHECK: ty:uint256 %x = uint256 6
+                x := 6
+            // CHECK: assert-failure
+                invalid()
+            // NOT-CHECK: branch
+            }
+
+            // CHECK: branchcond (%x == uint256 3), block9, block10
+            if eq(x, 3) {
+                // CHECK: block9: # then
+                // CHECK: branch block10
+            }
+
+            // CHECK: block10: # endif
+            // CHECK: ty:uint256 %x = uint256 4
+            x := 4
+
+        }
+    }
+
+// BEGIN-CHECK: testing::testing::function::for_statement__bool
+    function for_statement(bool a) public pure {
+        assembly {
+
+            for {
+            // CHECK: ty:uint256 %i = uint256 1
+                let i := 1
+            // CHECK: branch block1
+            // CHECK: block1: # cond
+            // CHECK: branchcond (uint256 0 != (%i + uint256 1)), block3, block4
+            } add(i, 1) {
+                // CHECK: block2: # next
+                // CHECK: ty:uint256 %i = (%i - uint256 1)
+                i := sub(i, 1)
+                // CHECK: branch block1
+            } {
+                // CHECK: block3: # body
+                // CHECK: ty:uint256 %i = (%i << uint256 1)
+                i := mul(i, 2)
+                // CHECK: branch block2
+            }
+            // CHECK: block4: # end_for
+
+            for {
+                // CHECK: ty:uint256 %i.27 = uint256 1
+                // CHECK: branch block5
+                let i := 1
+                // CHECK: block5: # cond
+                // CHECK: branchcond (unsigned less %i.27 < uint256 10), block7, block8
+            } lt(i, 10) {
+                // CHECK: block6: # next
+                i := add(i, 1)
+                // CHECK: ty:uint256 %i.27 = (%i.27 + uint256 1)
+                // CHECK: branch block5
+            } {
+                // CHECK: block7: # body
+                // CHECK: ty:uint256 %i.27 = (uint256 2 >> %i.27)
+                i := shr(i, 2)
+                // CHECK: branch block6
+            }
+            // CHECK: block8: # end_for
+
+            for {
+                // CHECK: ty:uint256 %i.28 = uint256 1
+                let i := 1
+                // CHECK: branch block9
+                // CHECK: block9: # cond
+                // CHECK: branchcond %a, block11, block12
+            } a {
+                // CHECK: block10: # next
+                // CHECK: ty:uint256 %i.28 = (uint256(%a) + uint256 1)
+                i := add(a, 1)
+                // CHECK: ty:bool %a = false
+                a := false
+            } {
+                // CHECK: block11: # body
+                // CHECK: ty:bool %a = (uint256 0 != (%i.28 + uint256 2))
+                a := add(i, 2)
+                // CHECK: branch block10
+            }
+            // CHECK: block12: # end_for
+
+            for {
+                // CHECK: ty:uint256 %i.29 = uint256 2
+                let i := 2
+                // CHECK: branch block13
+                // CHECK: block13: # cond
+                // CHECK: branchcond (uint256 2 == uint256 0), block15, block16
+            } eq(i, 0) {
+                // CHECK: block14: # next
+                // NOT-CHECK: ty:uint256 %i.29 = 
+                i := sub(i, 2)
+            } {
+                // CHECK: block15: # body
+                i := add(i, 3)
+                // CHECK: ty:uint256 %i.29 = uint256 5
+                invalid()
+                // CHECK: assert-failure
+                // NOT-CHECK: branch
+            }
+            // CHECK: block16: # end_for
+
+            for {
+                // CHECK: ty:uint256 %j = uint256 2
+                let j := 2
+                // CHECK: branch block17
+                // CHECK: block17: # cond
+                // CHECK: branchcond (uint256 2 == uint256 3), block19, block20
+            } eq(j, 3) {
+                // CHECK: block18: # next
+                j := shr(j, 2)
+                // CHECK: ty:uint256 %j = (uint256 2 >> %j)
+                invalid()
+                // CHECK: assert-failure
+            } {
+                // CHECK: block19: # body
+                j := sar(j, 3)
+                // CHECK: branch block18
+            }
+            // CHECK: block20: # end_for
+
+            for {
+                // CHECK: ty:uint256 %i.31 = uint256 0
+                let i := 0
+                // CHECK: branch block21
+                // CHECK: block21: # cond
+                // CHECK: branchcond (unsigned less %i.31 < uint256 10), block23, block24
+            } lt(i, 10) {
+                // CHECK: block22: # next
+                i := add(i, 1)
+                // CHECK: ty:uint256 %i.31 = (%i.31 + uint256 1)
+                // CHECK: branch block21
+            } {
+                // CHECK: block23: # body
+                for {
+                    // CHECK: ty:uint256 %j.32 = uint256 0
+                    let j :=0
+                    // CHECK: branch block25
+// ---- block 24 contains the for-loop with the invalid function
+// CHECK: block24: # end_for
+// CHECK: ty:uint256 %i.33 = uint256 2
+// CHECK: assert-failure
+// NOT-CHECK: branch
+
+                    // CHECK: block25: # cond
+                    // CHECK: branchcond (unsigned less %j.32 < uint256 10), block27, block28
+                } lt(j, 10) {
+                    // CHECK: ty:uint256 %j.32 = (%j.32 + uint256 1)
+                    j := add(j, 1)
+                    // CHECK: branch block25
+                } {
+                    // CHECK: block27: # body
+                    // CHECK: ty:bool %a = (uint256 0 != (%i.31 + %j.32))
+                    a := add(i, j)
+                    // CHECK: branch block26
+                }
+                // CHECK: block28: # end_for
+                // CHECK: branch block22
+            }
+
+            for {
+                let i := 2
+                invalid()
+            } lt(3, 4) {
+                i := add(i, 1)
+            } {
+                i := sub(i, 2)
+            }
+
+        }
+    }
+
+// BEGIN-CHECK: testing::testing::function::break_statement
+    function break_statement() public pure {
+        assembly {
+            for {let i := 1
+            // CHECK: ty:uint256 %i = uint256 1
+            // CHECK: branch block1
+            // CHECK: block1: # cond
+            // CHECK: branchcond (unsigned less %i < uint256 10), block3, block4
+            } lt(i, 10) {i := add(i, 1)
+            // CHECK: block2: # next
+            // CHECK: ty:uint256 %i = (%i + uint256 1)
+            // CHECK: branch block1
+            } {
+                // CHECK: block3: # body
+                i := shr(i, 2)
+                // CHECK: ty:uint256 %i = (uint256 2 >> %i)
+                // CHECK: branchcond (unsigned more %i > uint256 10), block5, block6
+                if gt(i, 10) {
+                    break
+                }
+            }
+            // CHECK: block4: # end_for
+            // CHECK: return
+
+            // IF-block:
+            // CHECK: block5: # then
+            // CHECK: branch block4
+
+            // End of for for loop after IF
+            // CHECK: block6: # endif
+            // CHECK: branch block2
+        }
+    }
+
+    // BEGIN-CHECK: testing::testing::function::break_nested_for
+    function break_nested_for() public pure {
+        assembly {
+            for {
+                // CHECK: ty:uint256 %i = uint256 1
+                let i := 1
+                // CHECK: branch block1
+                // CHECK: branchcond (unsigned less %i < uint256 10), block3, block4
+            } lt(i, 10) {
+                // CHECK: block2: # next
+                i := add(i, 1)
+                // CHECK: ty:uint256 %i = (%i + uint256 1)
+                // CHECK: branch block1
+            } {
+                for {
+                    // CHECK: block3: # body
+                    let j := 2
+                    // CHECK: ty:uint256 %j = uint256 2
+                    // CHECK: branch block5
+                } lt(j, 10) {
+                    // after outer for:
+                    // CHECK: block4: # end_for
+                    // CHECK: return
+
+                    // inner for condition
+                    // CHECK: block5: # cond
+                    // CHECK: branchcond (unsigned less %j < uint256 10), block7, block8
+                    // CHECK: block6: # next
+                    // CHECK: ty:uint256 %j = (%j + uint256 1)
+                    // CHECK: branch block5
+                    j := add(j, 1)
+                } {
+                    // CHECK: block7: # body
+                    // CHECK: branchcond (unsigned more %j > uint256 5), block9, block10
+                    if gt(j, 5) {
+                        break
+                    }
+                    // After inner for:
+                    // CHECK: block8: # end_for
+                    // CHECK: branchcond (unsigned more %i > uint256 5), block11, block12
+                    
+                    // Inside inner if:
+                    // CHECK: block9: # then
+                    // CHECK: branch block8
+
+                    // After inner if:
+                    // CHECK: block10: # endif
+                    // CHECK: ty:uint256 %j = (%i - uint256 2)
+                    j := sub(i, 2)
+                    // CHECK: branch block6
+                }
+                if gt(i, 5) {
+                    // CHECK: block11: # then
+                    break
+                    // CHECK: branch block4
+                }
+                // CHECK: block12: # endif
+                // CHECK: ty:uint256 %i = (%i - uint256 4)
+                i := sub(i, 4)
+                // CHECK: branch block2
+            }
+        }
+    }
+
+    // BEGIN-CHECK: testing::testing::function::continue_statement
+    function continue_statement() public pure {
+        assembly {
+            for {let i := 1
+            // CHECK: ty:uint256 %i = uint256 1
+            // CHECK: branch block1
+            // CHECK: block1: # cond
+            // CHECK: branchcond (unsigned less %i < uint256 10), block3, block4
+            } lt(i, 10) {i := add(i, 1)
+            // CHECK: block2: # next
+            // CHECK: ty:uint256 %i = (%i + uint256 1)
+            // CHECK: branch block1
+            } {
+                // CHECK: block3: # body
+                i := shr(i, 2)
+                // CHECK: ty:uint256 %i = (uint256 2 >> %i)
+                // CHECK: branchcond (unsigned more %i > uint256 10), block5, block6
+                if gt(i, 10) {
+                    continue
+                }
+            }
+            // CHECK: block4: # end_for
+            // CHECK: return
+
+            // IF-block:
+            // CHECK: block5: # then
+            // CHECK: branch block2
+
+            // End of for for loop after IF
+            // CHECK: block6: # endif
+            // CHECK: branch block2
+        }
+    }
+
+    // BEGIN-CHECK: testing::testing::function::continue_nested_for
+    function continue_nested_for() public pure {
+        assembly {
+            for {
+                // CHECK: ty:uint256 %i = uint256 1
+                let i := 1
+                // CHECK: branch block1
+                // CHECK: branchcond (unsigned less %i < uint256 10), block3, block4
+            } lt(i, 10) {
+                // CHECK: block2: # next
+                i := add(i, 1)
+                // CHECK: ty:uint256 %i = (%i + uint256 1)
+                // CHECK: branch block1
+            } {
+                for {
+                    // CHECK: block3: # body
+                    let j := 2
+                    // CHECK: ty:uint256 %j = uint256 2
+                    // CHECK: branch block5
+                } lt(j, 10) {
+                    // after outer for:
+                    // CHECK: block4: # end_for
+                    // CHECK: return
+
+                    // inner for condition
+                    // CHECK: block5: # cond
+                    // CHECK: branchcond (unsigned less %j < uint256 10), block7, block8
+                    // CHECK: block6: # next
+                    // CHECK: ty:uint256 %j = (%j + uint256 1)
+                    // CHECK: branch block5
+                    j := add(j, 1)
+                } {
+                    // CHECK: block7: # body
+                    // CHECK: branchcond (unsigned more %j > uint256 5), block9, block10
+                    if gt(j, 5) {
+                        continue
+                    }
+                    // After inner for:
+                    // CHECK: block8: # end_for
+                    // CHECK: branchcond (unsigned more %i > uint256 5), block11, block12
+                    
+                    // Inside inner if:
+                    // CHECK: block9: # then
+                    // CHECK: branch block6
+
+                    // After inner if:
+                    // CHECK: block10: # endif
+                    // CHECK: ty:uint256 %j = (%i - uint256 2)
+                    j := sub(i, 2)
+                    // CHECK: branch block6
+                }
+                if gt(i, 5) {
+                    // CHECK: block11: # then
+                    continue
+                    // CHECK: branch block2
+                }
+                // CHECK: block12: # endif
+                // CHECK: ty:uint256 %i = (%i - uint256 4)
+                i := sub(i, 4)
+                // CHECK: branch block2
+            }
+        }
+    }
+}

Fichier diff supprimé car celui-ci est trop grand
+ 594 - 638
tests/contract_testcases/ewasm/comment_tests.dot


+ 84 - 90
tests/contract_testcases/solana/yul/function_cal_cond.dot

@@ -27,53 +27,50 @@ strict digraph "tests/contract_testcases/solana/yul/function_cal_cond.sol" {
 	yul_function_call [label="yul function call 'tryThat'\ntests/contract_testcases/solana/yul/function_cal_cond.sol:11:26-33"]
 	yul_variable_28 [label="yul variable: b\nuint256\ntests/contract_testcases/solana/yul/function_cal_cond.sol:11:34-35"]
 	yul_number_literal_29 [label="uint256 literal: 2\ntests/contract_testcases/solana/yul/function_cal_cond.sol:11:37-38"]
-	yul_builtin_call_30 [label="yul builtin call 'return'\ntests/contract_testcases/solana/yul/function_cal_cond.sol:12:21-33"]
-	yul_variable_31 [label="yul variable: b\nuint256\ntests/contract_testcases/solana/yul/function_cal_cond.sol:12:28-29"]
-	yul_number_literal_32 [label="uint256 literal: 0\ntests/contract_testcases/solana/yul/function_cal_cond.sol:12:31-32"]
-	yul_function_definition_33 [label="function definition tryThat\ntests/contract_testcases/solana/yul/function_cal_cond.sol:23:17-30:18"]
-	yul_function_parameter_34 [label="function parameter uint256: b\ntests/contract_testcases/solana/yul/function_cal_cond.sol:23:34-35"]
-	yul_function_parameter_35 [label="function parameter uint256: a\ntests/contract_testcases/solana/yul/function_cal_cond.sol:23:37-38"]
-	yul_function_return_36 [label="return parameter uint256: c\ntests/contract_testcases/solana/yul/function_cal_cond.sol:23:43-44"]
-	yul_assignment_37 [label="yul assignment\ntests/contract_testcases/solana/yul/function_cal_cond.sol:24:21-35"]
-	yul_variable_38 [label="yul variable: a\nuint256\ntests/contract_testcases/solana/yul/function_cal_cond.sol:24:21-22"]
-	yul_builtin_call_39 [label="yul builtin call 'add'\ntests/contract_testcases/solana/yul/function_cal_cond.sol:24:26-35"]
-	yul_variable_40 [label="yul variable: a\nuint256\ntests/contract_testcases/solana/yul/function_cal_cond.sol:24:30-31"]
-	yul_number_literal_41 [label="uint256 literal: 4\ntests/contract_testcases/solana/yul/function_cal_cond.sol:24:33-34"]
-	if_42 [label="yul if\ntests/contract_testcases/solana/yul/function_cal_cond.sol:25:21-27:22"]
-	yul_builtin_call_43 [label="yul builtin call 'gt'\ntests/contract_testcases/solana/yul/function_cal_cond.sol:25:24-32"]
-	yul_variable_44 [label="yul variable: a\nuint256\ntests/contract_testcases/solana/yul/function_cal_cond.sol:25:27-28"]
-	yul_number_literal_45 [label="uint256 literal: 5\ntests/contract_testcases/solana/yul/function_cal_cond.sol:25:30-31"]
-	assembly_block_46 [label="assembly block\ntests/contract_testcases/solana/yul/function_cal_cond.sol:25:21-27:22"]
-	leave_47 [label="leave\ntests/contract_testcases/solana/yul/function_cal_cond.sol:26:25-30"]
-	yul_assignment_48 [label="yul assignment\ntests/contract_testcases/solana/yul/function_cal_cond.sol:28:21-27"]
-	yul_variable_49 [label="yul variable: c\nuint256\ntests/contract_testcases/solana/yul/function_cal_cond.sol:28:21-22"]
-	yul_number_literal_50 [label="uint256 literal: 5\ntests/contract_testcases/solana/yul/function_cal_cond.sol:28:26-27"]
-	yul_builtin_call_51 [label="yul builtin call 'return'\ntests/contract_testcases/solana/yul/function_cal_cond.sol:29:21-33"]
-	yul_variable_52 [label="yul variable: b\nuint256\ntests/contract_testcases/solana/yul/function_cal_cond.sol:29:28-29"]
-	yul_number_literal_53 [label="uint256 literal: 0\ntests/contract_testcases/solana/yul/function_cal_cond.sol:29:31-32"]
-	yul_function_definition_54 [label="function definition foo\ntests/contract_testcases/solana/yul/function_cal_cond.sol:16:21-18:22"]
-	yul_function_parameter_55 [label="function parameter uint256: d\ntests/contract_testcases/solana/yul/function_cal_cond.sol:16:34-35"]
-	yul_function_return_56 [label="return parameter uint256: e\ntests/contract_testcases/solana/yul/function_cal_cond.sol:16:40-41"]
-	yul_assignment_57 [label="yul assignment\ntests/contract_testcases/solana/yul/function_cal_cond.sol:17:25-39"]
-	yul_variable_58 [label="yul variable: e\nuint256\ntests/contract_testcases/solana/yul/function_cal_cond.sol:17:25-26"]
-	yul_builtin_call_59 [label="yul builtin call 'shr'\ntests/contract_testcases/solana/yul/function_cal_cond.sol:17:30-39"]
-	yul_variable_60 [label="yul variable: d\nuint256\ntests/contract_testcases/solana/yul/function_cal_cond.sol:17:34-35"]
-	yul_number_literal_61 [label="uint256 literal: 3\ntests/contract_testcases/solana/yul/function_cal_cond.sol:17:37-38"]
-	assembly_block_62 [label="assembly block\ntests/contract_testcases/solana/yul/function_cal_cond.sol:4:13-32:14"]
-	assembly_block_63 [label="assembly block\ntests/contract_testcases/solana/yul/function_cal_cond.sol:15:17-21:18"]
-	yul_builtin_call_64 [label="yul builtin call 'revert'\ntests/contract_testcases/solana/yul/function_cal_cond.sol:20:21-50"]
-	yul_function_call_65 [label="yul function call 'tryThis'\ntests/contract_testcases/solana/yul/function_cal_cond.sol:20:28-35"]
-	yul_function_call_66 [label="yul function call 'foo'\ntests/contract_testcases/solana/yul/function_cal_cond.sol:20:36-39"]
-	yul_number_literal_67 [label="uint256 literal: 3\ntests/contract_testcases/solana/yul/function_cal_cond.sol:20:40-41"]
-	yul_number_literal_68 [label="uint256 literal: 2\ntests/contract_testcases/solana/yul/function_cal_cond.sol:20:44-45"]
-	yul_number_literal_69 [label="uint256 literal: 4\ntests/contract_testcases/solana/yul/function_cal_cond.sol:20:48-49"]
-	yul_var_decl [label="yul variable declaration\ntests/contract_testcases/solana/yul/function_cal_cond.sol:31:17-27"]
-	var_decl_item [label="yul variable declaration uint256 x\ntests/contract_testcases/solana/yul/function_cal_cond.sol:31:21-22"]
-	yul_number_literal_72 [label="uint256 literal: 5\ntests/contract_testcases/solana/yul/function_cal_cond.sol:31:26-27"]
+	yul_builtin_call_30 [label="yul builtin call 'invalid'\ntests/contract_testcases/solana/yul/function_cal_cond.sol:12:21-30"]
+	yul_function_definition_31 [label="function definition tryThat\ntests/contract_testcases/solana/yul/function_cal_cond.sol:23:17-30:18"]
+	yul_function_parameter_32 [label="function parameter uint256: b\ntests/contract_testcases/solana/yul/function_cal_cond.sol:23:34-35"]
+	yul_function_parameter_33 [label="function parameter uint256: a\ntests/contract_testcases/solana/yul/function_cal_cond.sol:23:37-38"]
+	yul_function_return_34 [label="return parameter uint256: c\ntests/contract_testcases/solana/yul/function_cal_cond.sol:23:43-44"]
+	yul_assignment_35 [label="yul assignment\ntests/contract_testcases/solana/yul/function_cal_cond.sol:24:21-35"]
+	yul_variable_36 [label="yul variable: a\nuint256\ntests/contract_testcases/solana/yul/function_cal_cond.sol:24:21-22"]
+	yul_builtin_call_37 [label="yul builtin call 'add'\ntests/contract_testcases/solana/yul/function_cal_cond.sol:24:26-35"]
+	yul_variable_38 [label="yul variable: a\nuint256\ntests/contract_testcases/solana/yul/function_cal_cond.sol:24:30-31"]
+	yul_number_literal_39 [label="uint256 literal: 4\ntests/contract_testcases/solana/yul/function_cal_cond.sol:24:33-34"]
+	if_40 [label="yul if\ntests/contract_testcases/solana/yul/function_cal_cond.sol:25:21-27:22"]
+	yul_builtin_call_41 [label="yul builtin call 'gt'\ntests/contract_testcases/solana/yul/function_cal_cond.sol:25:24-32"]
+	yul_variable_42 [label="yul variable: a\nuint256\ntests/contract_testcases/solana/yul/function_cal_cond.sol:25:27-28"]
+	yul_number_literal_43 [label="uint256 literal: 5\ntests/contract_testcases/solana/yul/function_cal_cond.sol:25:30-31"]
+	assembly_block_44 [label="assembly block\ntests/contract_testcases/solana/yul/function_cal_cond.sol:25:21-27:22"]
+	leave_45 [label="leave\ntests/contract_testcases/solana/yul/function_cal_cond.sol:26:25-30"]
+	yul_assignment_46 [label="yul assignment\ntests/contract_testcases/solana/yul/function_cal_cond.sol:28:21-27"]
+	yul_variable_47 [label="yul variable: c\nuint256\ntests/contract_testcases/solana/yul/function_cal_cond.sol:28:21-22"]
+	yul_number_literal_48 [label="uint256 literal: 5\ntests/contract_testcases/solana/yul/function_cal_cond.sol:28:26-27"]
+	yul_builtin_call_49 [label="yul builtin call 'invalid'\ntests/contract_testcases/solana/yul/function_cal_cond.sol:29:21-30"]
+	yul_function_definition_50 [label="function definition foo\ntests/contract_testcases/solana/yul/function_cal_cond.sol:16:21-18:22"]
+	yul_function_parameter_51 [label="function parameter uint256: d\ntests/contract_testcases/solana/yul/function_cal_cond.sol:16:34-35"]
+	yul_function_return_52 [label="return parameter uint256: e\ntests/contract_testcases/solana/yul/function_cal_cond.sol:16:40-41"]
+	yul_assignment_53 [label="yul assignment\ntests/contract_testcases/solana/yul/function_cal_cond.sol:17:25-39"]
+	yul_variable_54 [label="yul variable: e\nuint256\ntests/contract_testcases/solana/yul/function_cal_cond.sol:17:25-26"]
+	yul_builtin_call_55 [label="yul builtin call 'shr'\ntests/contract_testcases/solana/yul/function_cal_cond.sol:17:30-39"]
+	yul_variable_56 [label="yul variable: d\nuint256\ntests/contract_testcases/solana/yul/function_cal_cond.sol:17:34-35"]
+	yul_number_literal_57 [label="uint256 literal: 3\ntests/contract_testcases/solana/yul/function_cal_cond.sol:17:37-38"]
+	assembly_block_58 [label="assembly block\ntests/contract_testcases/solana/yul/function_cal_cond.sol:4:13-32:14"]
+	assembly_block_59 [label="assembly block\ntests/contract_testcases/solana/yul/function_cal_cond.sol:15:17-21:18"]
+	yul_var_decl [label="yul variable declaration\ntests/contract_testcases/solana/yul/function_cal_cond.sol:20:21-56"]
+	var_decl_item [label="yul variable declaration uint256 x\ntests/contract_testcases/solana/yul/function_cal_cond.sol:20:25-26"]
+	yul_builtin_call_62 [label="yul builtin call 'sub'\ntests/contract_testcases/solana/yul/function_cal_cond.sol:20:30-56"]
+	yul_function_call_63 [label="yul function call 'tryThis'\ntests/contract_testcases/solana/yul/function_cal_cond.sol:20:34-41"]
+	yul_function_call_64 [label="yul function call 'foo'\ntests/contract_testcases/solana/yul/function_cal_cond.sol:20:42-45"]
+	yul_number_literal_65 [label="uint256 literal: 3\ntests/contract_testcases/solana/yul/function_cal_cond.sol:20:46-47"]
+	yul_number_literal_66 [label="uint256 literal: 2\ntests/contract_testcases/solana/yul/function_cal_cond.sol:20:50-51"]
+	yul_number_literal_67 [label="uint256 literal: 4\ntests/contract_testcases/solana/yul/function_cal_cond.sol:20:54-55"]
+	yul_var_decl_68 [label="yul variable declaration\ntests/contract_testcases/solana/yul/function_cal_cond.sol:31:17-27"]
+	var_decl_item_69 [label="yul variable declaration uint256 x\ntests/contract_testcases/solana/yul/function_cal_cond.sol:31:21-22"]
+	yul_number_literal_70 [label="uint256 literal: 5\ntests/contract_testcases/solana/yul/function_cal_cond.sol:31:26-27"]
 	diagnostic [label="found contract 'testTypes'\nlevel Debug\ntests/contract_testcases/solana/yul/function_cal_cond.sol:1:1-20"]
-	diagnostic_75 [label="inline assembly is not yet supported\nlevel Error\ntests/contract_testcases/solana/yul/function_cal_cond.sol:3:9-33:10"]
-	diagnostic_76 [label="unreachable yul statement\nlevel Warning\ntests/contract_testcases/solana/yul/function_cal_cond.sol:31:17-27"]
-	diagnostic_77 [label="yul variable 'x' has never been read\nlevel Warning\ntests/contract_testcases/solana/yul/function_cal_cond.sol:31:21-22"]
+	diagnostic_73 [label="yul variable 'x' has never been read\nlevel Warning\ntests/contract_testcases/solana/yul/function_cal_cond.sol:20:25-26"]
+	diagnostic_74 [label="yul variable 'x' has never been read\nlevel Warning\ntests/contract_testcases/solana/yul/function_cal_cond.sol:31:21-22"]
 	contracts -> contract
 	contract -> testAsm [label="function"]
 	testAsm -> inline_assembly [label="body"]
@@ -103,50 +100,47 @@ strict digraph "tests/contract_testcases/solana/yul/function_cal_cond.sol" {
 	yul_function_call -> yul_variable_28 [label="arg #0"]
 	yul_function_call -> yul_number_literal_29 [label="arg #1"]
 	yul_assignment_25 -> yul_builtin_call_30 [label="statement #4"]
-	yul_builtin_call_30 -> yul_variable_31 [label="arg #0"]
-	yul_builtin_call_30 -> yul_number_literal_32 [label="arg #1"]
-	inline_assembly -> yul_function_definition_33 [label="func def #2"]
-	yul_function_definition_33 -> yul_function_parameter_34 [label="parameter #0"]
-	yul_function_parameter_34 -> yul_function_parameter_35 [label="parameter #1"]
-	yul_function_definition_33 -> yul_function_return_36 [label="return #0"]
-	yul_function_definition_33 -> yul_assignment_37 [label="statement #0"]
-	yul_assignment_37 -> yul_variable_38 [label="rhs #0"]
-	yul_assignment_37 -> yul_builtin_call_39 [label="lhs"]
-	yul_builtin_call_39 -> yul_variable_40 [label="arg #0"]
-	yul_builtin_call_39 -> yul_number_literal_41 [label="arg #1"]
-	yul_assignment_37 -> if_42 [label="statement #1"]
-	if_42 -> yul_builtin_call_43 [label="cond"]
-	yul_builtin_call_43 -> yul_variable_44 [label="arg #0"]
-	yul_builtin_call_43 -> yul_number_literal_45 [label="arg #1"]
-	if_42 -> assembly_block_46 [label="if-block"]
-	assembly_block_46 -> leave_47 [label="statement #0"]
-	if_42 -> yul_assignment_48 [label="statement #2"]
-	yul_assignment_48 -> yul_variable_49 [label="rhs #0"]
-	yul_assignment_48 -> yul_number_literal_50 [label="lhs"]
-	yul_assignment_48 -> yul_builtin_call_51 [label="statement #3"]
-	yul_builtin_call_51 -> yul_variable_52 [label="arg #0"]
-	yul_builtin_call_51 -> yul_number_literal_53 [label="arg #1"]
-	inline_assembly -> yul_function_definition_54 [label="func def #1"]
-	yul_function_definition_54 -> yul_function_parameter_55 [label="parameter #0"]
-	yul_function_definition_54 -> yul_function_return_56 [label="return #0"]
-	yul_function_definition_54 -> yul_assignment_57 [label="statement #0"]
-	yul_assignment_57 -> yul_variable_58 [label="rhs #0"]
-	yul_assignment_57 -> yul_builtin_call_59 [label="lhs"]
-	yul_builtin_call_59 -> yul_variable_60 [label="arg #0"]
-	yul_builtin_call_59 -> yul_number_literal_61 [label="arg #1"]
-	inline_assembly -> assembly_block_62 [label="statement #0"]
-	assembly_block_62 -> assembly_block_63 [label="statement #0"]
-	assembly_block_63 -> yul_builtin_call_64 [label="statement #0"]
-	yul_builtin_call_64 -> yul_function_call_65 [label="arg #0"]
-	yul_function_call_65 -> yul_function_call_66 [label="arg #0"]
-	yul_function_call_66 -> yul_number_literal_67 [label="arg #0"]
-	yul_function_call_65 -> yul_number_literal_68 [label="arg #1"]
-	yul_builtin_call_64 -> yul_number_literal_69 [label="arg #1"]
-	assembly_block_63 -> yul_var_decl [label="statement #1"]
+	inline_assembly -> yul_function_definition_31 [label="func def #2"]
+	yul_function_definition_31 -> yul_function_parameter_32 [label="parameter #0"]
+	yul_function_parameter_32 -> yul_function_parameter_33 [label="parameter #1"]
+	yul_function_definition_31 -> yul_function_return_34 [label="return #0"]
+	yul_function_definition_31 -> yul_assignment_35 [label="statement #0"]
+	yul_assignment_35 -> yul_variable_36 [label="rhs #0"]
+	yul_assignment_35 -> yul_builtin_call_37 [label="lhs"]
+	yul_builtin_call_37 -> yul_variable_38 [label="arg #0"]
+	yul_builtin_call_37 -> yul_number_literal_39 [label="arg #1"]
+	yul_assignment_35 -> if_40 [label="statement #1"]
+	if_40 -> yul_builtin_call_41 [label="cond"]
+	yul_builtin_call_41 -> yul_variable_42 [label="arg #0"]
+	yul_builtin_call_41 -> yul_number_literal_43 [label="arg #1"]
+	if_40 -> assembly_block_44 [label="if-block"]
+	assembly_block_44 -> leave_45 [label="statement #0"]
+	if_40 -> yul_assignment_46 [label="statement #2"]
+	yul_assignment_46 -> yul_variable_47 [label="rhs #0"]
+	yul_assignment_46 -> yul_number_literal_48 [label="lhs"]
+	yul_assignment_46 -> yul_builtin_call_49 [label="statement #3"]
+	inline_assembly -> yul_function_definition_50 [label="func def #1"]
+	yul_function_definition_50 -> yul_function_parameter_51 [label="parameter #0"]
+	yul_function_definition_50 -> yul_function_return_52 [label="return #0"]
+	yul_function_definition_50 -> yul_assignment_53 [label="statement #0"]
+	yul_assignment_53 -> yul_variable_54 [label="rhs #0"]
+	yul_assignment_53 -> yul_builtin_call_55 [label="lhs"]
+	yul_builtin_call_55 -> yul_variable_56 [label="arg #0"]
+	yul_builtin_call_55 -> yul_number_literal_57 [label="arg #1"]
+	inline_assembly -> assembly_block_58 [label="statement #0"]
+	assembly_block_58 -> assembly_block_59 [label="statement #0"]
+	assembly_block_59 -> yul_var_decl [label="statement #0"]
 	yul_var_decl -> var_decl_item [label="decl item #0"]
-	yul_var_decl -> yul_number_literal_72 [label="init"]
+	yul_var_decl -> yul_builtin_call_62 [label="init"]
+	yul_builtin_call_62 -> yul_function_call_63 [label="arg #0"]
+	yul_function_call_63 -> yul_function_call_64 [label="arg #0"]
+	yul_function_call_64 -> yul_number_literal_65 [label="arg #0"]
+	yul_function_call_63 -> yul_number_literal_66 [label="arg #1"]
+	yul_builtin_call_62 -> yul_number_literal_67 [label="arg #1"]
+	assembly_block_59 -> yul_var_decl_68 [label="statement #1"]
+	yul_var_decl_68 -> var_decl_item_69 [label="decl item #0"]
+	yul_var_decl_68 -> yul_number_literal_70 [label="init"]
 	diagnostics -> diagnostic [label="Debug"]
-	diagnostics -> diagnostic_75 [label="Error"]
-	diagnostics -> diagnostic_76 [label="Warning"]
-	diagnostics -> diagnostic_77 [label="Warning"]
+	diagnostics -> diagnostic_73 [label="Warning"]
+	diagnostics -> diagnostic_74 [label="Warning"]
 }

+ 3 - 3
tests/contract_testcases/solana/yul/function_cal_cond.sol

@@ -9,7 +9,7 @@ contract testTypes {
                     }
                     b := add(a, 6)
                     c := tryThat(b, 2)
-                    return(b, 0)
+                    invalid()
                 }
 
                 {
@@ -17,7 +17,7 @@ contract testTypes {
                         e := shr(d, 3)
                     }
 
-                    revert(tryThis(foo(3), 2), 4)
+                    let x := sub(tryThis(foo(3), 2), 4)
                 }
 
                 function tryThat(b, a) -> c {
@@ -26,7 +26,7 @@ contract testTypes {
                         leave
                     }
                     c := 5
-                    return(b, 0)
+                    invalid()
                 }
                 let x := 5
             }

+ 4 - 24
tests/contract_testcases/solana/yul/parse.dot

@@ -2,33 +2,13 @@ strict digraph "tests/contract_testcases/solana/yul/parse.sol" {
 	contract [label="contract foo\ntests/contract_testcases/solana/yul/parse.sol:2:9-22"]
 	get [label="function get\ncontract: foo\ntests/contract_testcases/solana/yul/parse.sol:3:13-51\nsignature get()\nvisibility public\nmutability nonpayable"]
 	returns [label="returns\nbytes4 "]
-	inline_assembly [label="inline assembly\ntests/contract_testcases/solana/yul/parse.sol:4:17-7:18"]
-	yul_var_decl [label="yul variable declaration\ntests/contract_testcases/solana/yul/parse.sol:5:21-55"]
-	var_decl_item [label="yul variable declaration uint256 returndata_size\ntests/contract_testcases/solana/yul/parse.sol:5:25-40"]
-	yul_builtin_call [label="yul builtin call 'mload'\ntests/contract_testcases/solana/yul/parse.sol:5:44-55"]
-	yul_number_literal [label="uint256 literal: 64\ntests/contract_testcases/solana/yul/parse.sol:5:50-54"]
-	yul_builtin_call_10 [label="yul builtin call 'revert'\ntests/contract_testcases/solana/yul/parse.sol:6:21-59"]
-	yul_builtin_call_11 [label="yul builtin call 'add'\ntests/contract_testcases/solana/yul/parse.sol:6:28-41"]
-	yul_number_literal_12 [label="uint256 literal: 32\ntests/contract_testcases/solana/yul/parse.sol:6:32-34"]
-	yul_number_literal_13 [label="uint256 literal: 64\ntests/contract_testcases/solana/yul/parse.sol:6:36-40"]
-	yul_variable [label="yul variable: returndata_size\nuint256\ntests/contract_testcases/solana/yul/parse.sol:6:43-58"]
 	diagnostic [label="found contract 'foo'\nlevel Debug\ntests/contract_testcases/solana/yul/parse.sol:2:9-22"]
-	diagnostic_17 [label="function can be declared 'pure'\nlevel Warning\ntests/contract_testcases/solana/yul/parse.sol:3:13-51"]
-	diagnostic_18 [label="inline assembly is not yet supported\nlevel Error\ntests/contract_testcases/solana/yul/parse.sol:4:17-7:18"]
+	diagnostic_7 [label="builtin 'mload' is not available for target solana. Please, open a GitHub issue at https://github.com/hyperledger-labs/solang/issues if there is need to support this function\nlevel Error\ntests/contract_testcases/solana/yul/parse.sol:5:44-55"]
+	diagnostic_8 [label="missing return statement\nlevel Error\ntests/contract_testcases/solana/yul/parse.sol:8:14"]
 	contracts -> contract
 	contract -> get [label="function"]
 	get -> returns [label="returns"]
-	get -> inline_assembly [label="body"]
-	inline_assembly -> yul_var_decl [label="statement #0"]
-	yul_var_decl -> var_decl_item [label="decl item #0"]
-	yul_var_decl -> yul_builtin_call [label="init"]
-	yul_builtin_call -> yul_number_literal [label="arg #0"]
-	yul_var_decl -> yul_builtin_call_10 [label="statement #1"]
-	yul_builtin_call_10 -> yul_builtin_call_11 [label="arg #0"]
-	yul_builtin_call_11 -> yul_number_literal_12 [label="arg #0"]
-	yul_builtin_call_11 -> yul_number_literal_13 [label="arg #1"]
-	yul_builtin_call_10 -> yul_variable [label="arg #1"]
 	diagnostics -> diagnostic [label="Debug"]
-	diagnostics -> diagnostic_17 [label="Warning"]
-	diagnostics -> diagnostic_18 [label="Error"]
+	diagnostics -> diagnostic_7 [label="Error"]
+	diagnostics -> diagnostic_8 [label="Error"]
 }

+ 4 - 8
tests/contract_testcases/solana/yul/return_in_asm.dot

@@ -2,17 +2,13 @@ strict digraph "tests/contract_testcases/solana/yul/return_in_asm.sol" {
 	contract [label="contract Contract\ntests/contract_testcases/solana/yul/return_in_asm.sol:1:1-19"]
 	node_3 [label="constructor \ncontract: Contract\ntests/contract_testcases/solana/yul/return_in_asm.sol:2:5-19\nsignature ()\nvisibility public\nmutability nonpayable"]
 	inline_assembly [label="inline assembly\ntests/contract_testcases/solana/yul/return_in_asm.sol:3:9-5:10"]
-	yul_builtin_call [label="yul builtin call 'return'\ntests/contract_testcases/solana/yul/return_in_asm.sol:4:13-25"]
-	yul_number_literal [label="uint256 literal: 0\ntests/contract_testcases/solana/yul/return_in_asm.sol:4:20-21"]
-	yul_number_literal_7 [label="uint256 literal: 0\ntests/contract_testcases/solana/yul/return_in_asm.sol:4:23-24"]
 	diagnostic [label="found contract 'Contract'\nlevel Debug\ntests/contract_testcases/solana/yul/return_in_asm.sol:1:1-19"]
-	diagnostic_10 [label="flag 'memory-safe' not supported\nlevel Error\ntests/contract_testcases/solana/yul/return_in_asm.sol:3:19-32"]
+	diagnostic_7 [label="flag 'memory-safe' not supported\nlevel Error\ntests/contract_testcases/solana/yul/return_in_asm.sol:3:19-32"]
+	diagnostic_8 [label="builtin 'return' is not available for target solana. Please, open a GitHub issue at https://github.com/hyperledger-labs/solang/issues if there is need to support this function\nlevel Error\ntests/contract_testcases/solana/yul/return_in_asm.sol:4:13-25"]
 	contracts -> contract
 	contract -> node_3 [label="constructor"]
 	node_3 -> inline_assembly [label="body"]
-	inline_assembly -> yul_builtin_call [label="statement #0"]
-	yul_builtin_call -> yul_number_literal [label="arg #0"]
-	yul_builtin_call -> yul_number_literal_7 [label="arg #1"]
 	diagnostics -> diagnostic [label="Debug"]
-	diagnostics -> diagnostic_10 [label="Error"]
+	diagnostics -> diagnostic_7 [label="Error"]
+	diagnostics -> diagnostic_8 [label="Error"]
 }

+ 10 - 10
tests/contract_testcases/solana/yul/yul_switch.dot

@@ -8,7 +8,7 @@ strict digraph "tests/contract_testcases/solana/yul/yul_switch.sol" {
 	number_literal [label="uint256 literal: 0\ntests/contract_testcases/solana/yul/yul_switch.sol:4:21-22"]
 	inline_assembly [label="inline assembly\ntests/contract_testcases/solana/yul/yul_switch.sol:5:9-14:10"]
 	switch [label="yul switch\ntests/contract_testcases/solana/yul/yul_switch.sol:6:13-11:14"]
-	yul_member_access [label="yul member 'length' access\ntests/contract_testcases/solana/yul/yul_switch.sol:6:20-29"]
+	yul_suffix_access [label="yul suffix 'length' access\ntests/contract_testcases/solana/yul/yul_switch.sol:6:20-29"]
 	solidity_variable [label="solidity variable: vl\nuint128[]\ntests/contract_testcases/solana/yul/yul_switch.sol:6:20-22"]
 	case [label="yul switch case\ntests/contract_testcases/solana/yul/yul_switch.sol:7:13-41"]
 	yul_number_literal [label="uint256 literal: 1\ntests/contract_testcases/solana/yul/yul_switch.sol:7:18-19"]
@@ -16,7 +16,7 @@ strict digraph "tests/contract_testcases/solana/yul/yul_switch.sol" {
 	yul_assignment [label="yul assignment\ntests/contract_testcases/solana/yul/yul_switch.sol:7:21-40"]
 	solidity_variable_17 [label="solidity variable: y\nuint256\ntests/contract_testcases/solana/yul/yul_switch.sol:7:21-22"]
 	yul_builtin_call [label="yul builtin call 'mul'\ntests/contract_testcases/solana/yul/yul_switch.sol:7:26-40"]
-	yul_member_access_19 [label="yul member 'slot' access\ntests/contract_testcases/solana/yul/yul_switch.sol:7:30-36"]
+	yul_suffix_access_19 [label="yul suffix 'slot' access\ntests/contract_testcases/solana/yul/yul_switch.sol:7:30-36"]
 	storage_var [label="storage variable\ntestTypes.b\nuint256\ntests/contract_testcases/solana/yul/yul_switch.sol:7:30-31"]
 	yul_number_literal_21 [label="uint256 literal: 2\ntests/contract_testcases/solana/yul/yul_switch.sol:7:38-39"]
 	case_22 [label="yul switch case\ntests/contract_testcases/solana/yul/yul_switch.sol:8:13-43"]
@@ -25,7 +25,7 @@ strict digraph "tests/contract_testcases/solana/yul/yul_switch.sol" {
 	yul_assignment_25 [label="yul assignment\ntests/contract_testcases/solana/yul/yul_switch.sol:8:21-42"]
 	solidity_variable_26 [label="solidity variable: y\nuint256\ntests/contract_testcases/solana/yul/yul_switch.sol:8:21-22"]
 	yul_builtin_call_27 [label="yul builtin call 'shr'\ntests/contract_testcases/solana/yul/yul_switch.sol:8:26-42"]
-	yul_member_access_28 [label="yul member 'offset' access\ntests/contract_testcases/solana/yul/yul_switch.sol:8:30-38"]
+	yul_suffix_access_28 [label="yul suffix 'offset' access\ntests/contract_testcases/solana/yul/yul_switch.sol:8:30-38"]
 	storage_var_29 [label="storage variable\ntestTypes.b\nuint256\ntests/contract_testcases/solana/yul/yul_switch.sol:8:30-31"]
 	yul_number_literal_30 [label="uint256 literal: 2\ntests/contract_testcases/solana/yul/yul_switch.sol:8:40-41"]
 	default [label="yul switch default\ntests/contract_testcases/solana/yul/yul_switch.sol:9:13-11:14"]
@@ -41,7 +41,7 @@ strict digraph "tests/contract_testcases/solana/yul/yul_switch.sol" {
 	return [label="return\ntests/contract_testcases/solana/yul/yul_switch.sol:16:9-17"]
 	variable [label="variable: y\nuint256\ntests/contract_testcases/solana/yul/yul_switch.sol:16:16-17"]
 	diagnostic [label="found contract 'testTypes'\nlevel Debug\ntests/contract_testcases/solana/yul/yul_switch.sol:1:1-20"]
-	diagnostic_45 [label="inline assembly is not yet supported\nlevel Error\ntests/contract_testcases/solana/yul/yul_switch.sol:5:9-14:10"]
+	diagnostic_45 [label="switch statements have no implementation in code generation yet. Please, file a GitHub issue if there is urgent need for such a feature\nlevel Error\ntests/contract_testcases/solana/yul/yul_switch.sol:6:13-11:14"]
 	contracts -> contract
 	contract -> var [label="variable"]
 	contract -> testAsm [label="function"]
@@ -51,16 +51,16 @@ strict digraph "tests/contract_testcases/solana/yul/yul_switch.sol" {
 	var_decl -> number_literal [label="init"]
 	var_decl -> inline_assembly [label="next"]
 	inline_assembly -> switch [label="statement #0"]
-	switch -> yul_member_access [label="cond"]
-	yul_member_access -> solidity_variable [label="parent"]
+	switch -> yul_suffix_access [label="cond"]
+	yul_suffix_access -> solidity_variable [label="parent"]
 	switch -> case [label="case #0"]
 	case -> yul_number_literal [label="case-condition"]
 	case -> assembly_block [label="case block"]
 	assembly_block -> yul_assignment [label="statement #0"]
 	yul_assignment -> solidity_variable_17 [label="rhs #0"]
 	yul_assignment -> yul_builtin_call [label="lhs"]
-	yul_builtin_call -> yul_member_access_19 [label="arg #0"]
-	yul_member_access_19 -> storage_var [label="parent"]
+	yul_builtin_call -> yul_suffix_access_19 [label="arg #0"]
+	yul_suffix_access_19 -> storage_var [label="parent"]
 	yul_builtin_call -> yul_number_literal_21 [label="arg #1"]
 	switch -> case_22 [label="case #1"]
 	case_22 -> yul_number_literal_23 [label="case-condition"]
@@ -68,8 +68,8 @@ strict digraph "tests/contract_testcases/solana/yul/yul_switch.sol" {
 	assembly_block_24 -> yul_assignment_25 [label="statement #0"]
 	yul_assignment_25 -> solidity_variable_26 [label="rhs #0"]
 	yul_assignment_25 -> yul_builtin_call_27 [label="lhs"]
-	yul_builtin_call_27 -> yul_member_access_28 [label="arg #0"]
-	yul_member_access_28 -> storage_var_29 [label="parent"]
+	yul_builtin_call_27 -> yul_suffix_access_28 [label="arg #0"]
+	yul_suffix_access_28 -> storage_var_29 [label="parent"]
 	yul_builtin_call_27 -> yul_number_literal_30 [label="arg #1"]
 	switch -> default [label="default"]
 	default -> assembly_block_32 [label="default block"]

+ 1 - 1
tests/contract_testcases/substrate/yul/unreachable_after_asm.dot

@@ -6,7 +6,7 @@ strict digraph "tests/contract_testcases/substrate/yul/unreachable_after_asm.sol
 	diagnostic [label="found contract 'testTypes'\nlevel Debug\ntests/contract_testcases/substrate/yul/unreachable_after_asm.sol:1:1-20"]
 	diagnostic_7 [label="flag 'memory-safe' not supported\nlevel Error\ntests/contract_testcases/substrate/yul/unreachable_after_asm.sol:5:19-32"]
 	diagnostic_8 [label="flag 'meh' not supported\nlevel Error\ntests/contract_testcases/substrate/yul/unreachable_after_asm.sol:5:34-39"]
-	diagnostic_9 [label="unreachable statement\nlevel Error\ntests/contract_testcases/substrate/yul/unreachable_after_asm.sol:17:9-19:10"]
+	diagnostic_9 [label="unreachable statement\nlevel Error\ntests/contract_testcases/substrate/yul/unreachable_after_asm.sol:10:9-12:10"]
 	contracts -> contract
 	contract -> var [label="variable"]
 	contract -> testAsm [label="function"]

+ 1 - 8
tests/contract_testcases/substrate/yul/unreachable_after_asm.sol

@@ -3,15 +3,8 @@ contract testTypes {
     function testAsm(uint128[] calldata vl) public {
         uint256 y = 0;
         assembly ("memory-safe", "meh") {
-            switch vl.length
-            case 1 {y := mul(b.slot, 2)}
-            case 2 {y := shr(b.offset, 2)}
-            default {
-                y := 5
-            }
-
             y := sub(y, 1)
-            revert(y, 2)
+            invalid()
         }
 
         if (vl[0] > 0) { 

+ 1 - 0
tests/ewasm_tests/mod.rs

@@ -2,3 +2,4 @@ mod abi;
 mod crypto;
 mod modifiers;
 mod primitives;
+mod yul;

+ 96 - 0
tests/ewasm_tests/yul.rs

@@ -0,0 +1,96 @@
+use crate::build_solidity;
+use byte_slice_cast::AsByteSlice;
+use ethabi::{Token, Uint};
+
+#[test]
+fn eth_builtins() {
+    let mut runtime = build_solidity(
+        r#"
+contract testing  {
+    function test_address() public view returns (uint256 ret) {
+        assembly {
+            let a := address()
+            ret := a
+        }
+    }
+
+    function test_balance() public view returns (uint256 ret) {
+        assembly {
+            let a := address()
+            ret := balance(a)
+        }
+    }
+
+    function test_selfbalance() public view returns (uint256 ret) {
+        assembly {
+            let a := selfbalance()
+            ret := a
+        }
+    }
+
+    function test_caller() public view returns (uint256 ret) {
+        assembly {
+            let a := caller()
+            ret := a
+        }
+    }
+
+    function test_callvalue() public view returns (uint256 ret) {
+        assembly {
+            let a := callvalue()
+            ret := a
+        }
+    }
+}
+"#,
+    );
+
+    runtime.constructor(&[]);
+    let returns = runtime.function("test_address", &[]);
+    let addr = returns[0].clone().into_uint().unwrap().0;
+    let mut b_vec = addr.as_byte_slice().to_vec();
+    b_vec.reverse();
+    assert_eq!(&b_vec[12..32], runtime.vm.cur.as_ref());
+
+    let returns = runtime.function("test_balance", &[]);
+    assert_eq!(
+        returns,
+        vec![Token::Uint(Uint::from_big_endian(
+            runtime
+                .accounts
+                .get(runtime.vm.cur.as_ref())
+                .unwrap()
+                .1
+                .to_be_bytes()
+                .as_ref()
+        ))]
+    );
+
+    let returns = runtime.function("test_selfbalance", &[]);
+    assert_eq!(
+        returns,
+        vec![Token::Uint(Uint::from_big_endian(
+            runtime
+                .accounts
+                .get(runtime.vm.cur.as_ref())
+                .unwrap()
+                .1
+                .to_be_bytes()
+                .as_ref()
+        ))]
+    );
+
+    let returns = runtime.function("test_caller", &[]);
+    let addr = returns[0].clone().into_uint().unwrap().0;
+    let mut b_vec = addr.as_byte_slice().to_vec();
+    b_vec.reverse();
+    assert_eq!(&b_vec[12..32], runtime.caller.as_slice());
+
+    let returns = runtime.function("test_callvalue", &[]);
+    assert_eq!(
+        returns,
+        vec![Token::Uint(Uint::from_big_endian(
+            runtime.value.to_be_bytes().as_ref()
+        ))]
+    );
+}

+ 1 - 0
tests/solana_tests/mod.rs

@@ -23,3 +23,4 @@ mod storage;
 mod unused_variable_elimination;
 mod using;
 mod vector_to_slice;
+mod yul;

+ 262 - 0
tests/solana_tests/yul.rs

@@ -0,0 +1,262 @@
+use crate::build_solidity;
+use ethabi::{ethereum_types::U256, Token, Uint};
+
+#[test]
+fn suffixes_access() {
+    let mut vm = build_solidity(
+        r#"
+
+contract testing  {
+    struct stru_test {
+        string a;
+        uint b;
+    }
+
+    stru_test ss1; // slot: 16
+    stru_test ss2; // slot: 56
+    function test_slot() public view returns (uint256) {
+        uint256 ret = 98;
+        stru_test storage local_t = ss2;
+        assembly {
+            let a := ss1.slot
+            let b := mul(local_t.slot, 1000)
+            ret := add(a, b)
+            // offset should always be zero
+            ret := sub(ret, ss2.offset)
+            ret := sub(ret, local_t.offset)
+        }
+
+        return ret;
+    }
+
+    function call_data_array(uint32[] calldata vl) public pure returns (uint256, uint256) {
+        uint256 ret1 = 98;
+        uint256 ret2 = 99;
+        assembly {
+            let a := vl.offset
+            let b := vl.length
+            ret1 := a
+            ret2 := b
+        }
+
+        return (ret1, ret2);
+    }
+
+    function selector_address() public view returns(uint256, uint256) {
+        function () external returns (uint256) fPtr = this.test_slot;
+        uint256 ret1 = 256;
+        uint256 ret2 = 129;
+        assembly {
+            ret1 := fPtr.address
+            ret2 := fPtr.selector
+        }
+
+        return (ret1, ret2);
+    }
+}
+      "#,
+    );
+
+    vm.constructor("testing", &[], 0);
+
+    let returns = vm.function("test_slot", &[], &[], 0, None);
+    assert_eq!(returns, vec![Token::Uint(U256::from(56016))]);
+
+    let returns = vm.function(
+        "call_data_array",
+        &[Token::Array(vec![
+            Token::Uint(U256::from(3)),
+            Token::Uint(U256::from(5)),
+            Token::Uint(U256::from(7)),
+            Token::Uint(U256::from(11)),
+        ])],
+        &[],
+        0,
+        None,
+    );
+    assert_eq!(
+        returns,
+        vec![
+            Token::Uint(U256::from(12884901888u64)),
+            Token::Uint(U256::from(4))
+        ]
+    );
+
+    let returns = vm.function("selector_address", &[], &[], 0, None);
+    assert_eq!(
+        returns,
+        vec![
+            Token::Uint(U256::from_big_endian(vm.stack[0].data.as_ref())),
+            Token::Uint(U256::from(2081714652u32))
+        ]
+    );
+}
+
+#[test]
+fn general_test() {
+    let mut vm = build_solidity(
+        r#"
+contract testing  {
+    function general_test(uint64 a) public view returns (uint64, uint256) {
+        uint64 g = 0;
+        uint256 h = 0;
+        assembly {
+            function sum(a, b) -> ret1 {
+                ret1 := add(a, b)
+            }
+
+            function mix(a, b) -> ret1, ret2 {
+                ret1 := mul(a, b)
+                ret2 := add(a, b)
+            }
+
+            for {let i := 0} lt(i, 10) {i := add(i, 1)} {
+                if eq(a, 259) {
+                    break
+                }
+                g := sum(g, 2)
+                if gt(a, 10) {
+                    continue
+                }
+                g := sub(g, 1)
+            }
+
+            if or(lt(a, 10), eq(a, 259)) {
+                g, h := mix(g, 10)
+            }
+        }
+
+        return (g, h);
+    }
+}
+      "#,
+    );
+
+    vm.constructor("testing", &[], 0);
+
+    let returns = vm.function("general_test", &[Token::Uint(Uint::from(5))], &[], 0, None);
+    assert_eq!(
+        returns,
+        vec![Token::Uint(Uint::from(100)), Token::Uint(Uint::from(20))]
+    );
+
+    let returns = vm.function("general_test", &[Token::Uint(Uint::from(78))], &[], 0, None);
+    assert_eq!(
+        returns,
+        vec![Token::Uint(Uint::from(20)), Token::Uint(Uint::from(0))]
+    );
+
+    let returns = vm.function(
+        "general_test",
+        &[Token::Uint(Uint::from(259))],
+        &[],
+        0,
+        None,
+    );
+    assert_eq!(
+        returns,
+        vec![Token::Uint(Uint::from(0)), Token::Uint(Uint::from(10))]
+    );
+}
+
+#[test]
+fn byte_builtin() {
+    let mut vm = build_solidity(
+        r#"
+contract c {
+	function getByte(uint256 bb) public pure returns (uint256) {
+		uint256 ret = 0;
+		assembly {
+			ret := byte(5, bb)
+		}
+		return ret;
+	}
+
+	function divide(uint256 a, uint256 b) public pure returns (uint256 ret1, uint256 ret2) {
+		assembly {
+			ret1 := div(a, b)
+			ret2 := mod(a, b)
+		}
+	}
+
+	function mods(uint256 a, uint256 b, uint256 c) public pure returns (uint256 ret1, uint256 ret2) {
+		assembly {
+			ret1 := addmod(a, b, c)
+			ret2 := mulmod(a, b, c)
+		}
+	}
+}
+        "#,
+    );
+
+    vm.constructor("c", &[], 0);
+    let num: Vec<u8> = vec![
+        0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+        0x11, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e,
+        0x2f, 0x31,
+    ];
+    let returns = vm.function(
+        "getByte",
+        &[Token::Uint(U256::from_big_endian(&num))],
+        &[],
+        0,
+        None,
+    );
+    assert_eq!(returns, vec![Token::Uint(U256::from(6))]);
+
+    let returns = vm.function(
+        "divide",
+        &[Token::Uint(U256::from(4)), Token::Uint(U256::from(3))],
+        &[],
+        0,
+        None,
+    );
+    assert_eq!(
+        returns,
+        vec![Token::Uint(U256::from(1)), Token::Uint(U256::from(1))]
+    );
+
+    let returns = vm.function(
+        "divide",
+        &[Token::Uint(U256::from(4)), Token::Uint(U256::from(0))],
+        &[],
+        0,
+        None,
+    );
+    assert_eq!(
+        returns,
+        vec![Token::Uint(U256::from(0)), Token::Uint(U256::from(0))]
+    );
+
+    let returns = vm.function(
+        "mods",
+        &[
+            Token::Uint(U256::from(4)),
+            Token::Uint(U256::from(2)),
+            Token::Uint(U256::from(3)),
+        ],
+        &[],
+        0,
+        None,
+    );
+    assert_eq!(
+        returns,
+        vec![Token::Uint(U256::from(0)), Token::Uint(U256::from(2))]
+    );
+
+    let returns = vm.function(
+        "mods",
+        &[
+            Token::Uint(U256::from(4)),
+            Token::Uint(U256::from(2)),
+            Token::Uint(U256::from(0)),
+        ],
+        &[],
+        0,
+        None,
+    );
+    assert_eq!(
+        returns,
+        vec![Token::Uint(U256::from(0)), Token::Uint(U256::from(0))]
+    );
+}

+ 1 - 0
tests/substrate_tests/mod.rs

@@ -24,3 +24,4 @@ mod strings;
 mod structs;
 mod value;
 mod variables;
+mod yul;

+ 108 - 0
tests/substrate_tests/yul.rs

@@ -0,0 +1,108 @@
+use crate::build_solidity;
+use ethabi::ethereum_types::U256;
+use parity_scale_codec::{Decode, Encode};
+
+#[derive(Debug, PartialEq, Encode, Decode)]
+struct Val256(U256);
+
+#[test]
+fn assign_suffixes() {
+    #[derive(Debug, Encode, Decode)]
+    struct CallDataInput {
+        pub input: U256,
+        pub vec: Vec<u32>,
+    }
+
+    let mut runtime = build_solidity(
+        r#"
+contract testing  {
+    struct stru_test {
+        string a;
+        uint b;
+    }
+
+
+    stru_test t_s_t;
+    function test_local_vec(uint256 input) public pure returns (uint256 ret) {
+        uint256[] vec;
+        vec.push(uint256(4));
+        assembly {
+            vec := add(input, 7)
+            input := 6
+            ret := vec
+        }
+    }
+
+    function test_struct(uint256 input) public pure returns (uint256 ret) {
+        stru_test tt = stru_test({a: "tea", b: 5});
+        assembly {
+            tt := add(input, 7)
+            input := 6
+            ret := tt
+        }
+    }
+
+    function test_mem_vec(uint256 input) public pure returns (uint256 ret) {
+        uint256[] memory vec;
+        vec.push(uint256(4));
+        assembly {
+            vec := add(input, 7)
+            input := 9
+            ret := vec
+        }
+    }
+
+    function test_mem_struct(uint256 input) public pure returns (uint256 ret) {
+        stru_test memory tt = stru_test({a: "tea", b: 5});
+        assembly {
+            tt := add(input, 7)
+            input := 9
+            ret := tt
+        }
+    }
+
+    function calldata_vec(uint256 input, uint32[] calldata vec) public pure returns (uint256 ret) {
+        assembly {
+            vec.offset := add(input, 7)
+            input := 9
+            ret := vec.offset
+        }
+    }
+
+    function storage_struct(uint256 input) public returns (uint256 ret) {
+        stru_test storage local_t = t_s_t;
+        assembly {
+            local_t.slot := add(input, 7)
+            input := 90
+            ret := local_t.slot
+        }
+    }
+}
+      "#,
+    );
+
+    runtime.function("test_local_vec", Val256(U256::from(7)).encode());
+    assert_eq!(runtime.vm.output, Val256(U256::from(14)).encode());
+
+    runtime.function("test_struct", Val256(U256::from(20)).encode());
+    assert_eq!(runtime.vm.output, Val256(U256::from(27)).encode());
+
+    runtime.function("test_mem_vec", Val256(U256::from(30)).encode());
+    assert_eq!(runtime.vm.output, Val256(U256::from(37)).encode());
+
+    runtime.function("test_mem_struct", Val256(U256::from(8)).encode());
+    assert_eq!(runtime.vm.output, Val256(U256::from(15)).encode());
+
+    runtime.function(
+        "calldata_vec",
+        CallDataInput {
+            input: U256::from(19),
+            vec: vec![0, 1, 2, 3, 4],
+        }
+        .encode(),
+    );
+    assert_eq!(runtime.vm.output, Val256(U256::from(26)).encode());
+
+    runtime.function("storage_struct", Val256(U256::from(17)).encode());
+    assert_eq!(runtime.vm.output, Val256(U256::from(24)).encode());
+}

+ 1 - 8
tests/undefined_variable_detection.rs

@@ -9,7 +9,6 @@ fn parse_and_codegen(src: &'static str) -> Namespace {
     let mut cache = FileResolver::new();
     cache.set_file_contents("test.sol", src.to_string());
     let mut ns = parse_and_resolve(OsStr::new("test.sol"), &mut cache, Target::Ewasm);
-
     let opt = Options {
         dead_storage: false,
         constant_folding: false,
@@ -426,13 +425,7 @@ fn array() {
     "#;
     let ns = parse_and_codegen(file);
     let errors = ns.diagnostics.errors();
-    assert_eq!(errors.len(), 1);
-    assert_eq!(errors[0].message, "Variable 'vec' is undefined");
-    assert_eq!(errors[0].notes.len(), 1);
-    assert_eq!(
-        errors[0].notes[0].message,
-        "Variable read before being defined"
-    );
+    assert_eq!(errors.len(), 0);
 
     let file = r#"
     contract test {

Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff