Sfoglia il codice sorgente

Refactor substrate dispatcher into codegen (#1249)

Cyrill Leutwiler 2 anni fa
parent
commit
d72d82c9d6

+ 5 - 67
src/codegen/cfg.rs

@@ -147,16 +147,6 @@ pub enum Instr {
         address: Expression,
         value: Expression,
     },
-    /// ABI decoder encoded data. If decoding fails, either jump to exception
-    /// or abort if this is None.
-    AbiDecode {
-        res: Vec<usize>,
-        selector: Option<u32>,
-        exception_block: Option<usize>,
-        tys: Vec<Parameter>,
-        data: Expression,
-        data_len: Option<Expression>,
-    },
     /// Insert unreachable instruction after e.g. self-destruct
     Unreachable,
     /// Self destruct
@@ -223,7 +213,6 @@ impl Instr {
                 encoded_args: Some(expr),
             }
             | Instr::PopStorage { storage: expr, .. }
-            | Instr::AbiDecode { data: expr, .. }
             | Instr::SelfDestruct { recipient: expr }
             | Instr::Set { expr, .. } => {
                 expr.recurse(cx, f);
@@ -439,12 +428,6 @@ impl BasicBlock {
                     out.push(*true_block);
                     out.push(*false_block);
                 }
-                Instr::AbiDecode {
-                    exception_block: Some(block),
-                    ..
-                } => {
-                    out.push(*block);
-                }
                 Instr::Switch { default, cases, .. } => {
                     out.push(*default);
                     for (_, goto) in cases {
@@ -881,18 +864,6 @@ impl ControlFlowGraph {
                     .collect::<Vec<String>>()
                     .join(", ")
             ),
-            Expression::AbiEncode { packed, args, .. } => format!(
-                "(abiencode packed:{} non-packed:{})",
-                packed
-                    .iter()
-                    .map(|expr| self.expr_to_string(contract, ns, expr))
-                    .collect::<Vec<String>>()
-                    .join(", "),
-                args.iter()
-                    .map(|expr| self.expr_to_string(contract, ns, expr))
-                    .collect::<Vec<String>>()
-                    .join(", ")
-            ),
             Expression::Undefined(_) => "undef".to_string(),
             Expression::AdvancePointer {
                 pointer,
@@ -1154,44 +1125,6 @@ impl ControlFlowGraph {
                     self.expr_to_string(contract, ns, value),
                 )
             }
-            Instr::AbiDecode {
-                res,
-                tys,
-                selector,
-                exception_block: exception,
-                data,
-                data_len,
-            } => {
-                let mut val = format!(
-                    "{} = (abidecode:(%{}, {} {} ({}))",
-                    res.iter()
-                        .map(|local| format!("%{}", self.vars[local].id.name))
-                        .collect::<Vec<String>>()
-                        .join(", "),
-                    self.expr_to_string(contract, ns, data),
-                    selector
-                        .iter()
-                        .map(|s| format!("selector:0x{s:08x} "))
-                        .collect::<String>(),
-                    exception
-                        .iter()
-                        .map(|block| format!("exception: block{block} "))
-                        .collect::<String>(),
-                    tys.iter()
-                        .map(|ty| ty.ty.to_string(ns))
-                        .collect::<Vec<String>>()
-                        .join(", "),
-                );
-
-                if let Some(len) = data_len {
-                    val.push_str(
-                        format!(" data len: {}", self.expr_to_string(contract, ns, len)).as_str(),
-                    );
-                }
-
-                val
-            }
-
             Instr::Store { dest, data } => format!(
                 "store {}, {}",
                 self.expr_to_string(contract, ns, dest),
@@ -2063,6 +1996,11 @@ impl Namespace {
         }
     }
 
+    /// Return the value type
+    pub fn value_type(&self) -> Type {
+        Type::Uint(8 * self.value_length as u16)
+    }
+
     /// Checks if struct contains only primitive types and returns its memory non-padded size
     pub fn calculate_struct_non_padded_size(&self, struct_type: &StructType) -> Option<BigInt> {
         let mut size = BigInt::from(0u8);

+ 0 - 48
src/codegen/constant_folding.rs

@@ -264,28 +264,6 @@ pub fn constant_folding(cfg: &mut ControlFlowGraph, ns: &mut Namespace) {
                         contract_function_no: *contract_function_no,
                     };
                 }
-                Instr::AbiDecode {
-                    res,
-                    selector,
-                    exception_block,
-                    tys,
-                    data,
-                    data_len,
-                } => {
-                    let (data, _) = expression(data, Some(&vars), cfg, ns);
-                    let data_len = data_len
-                        .as_ref()
-                        .map(|e| expression(e, Some(&vars), cfg, ns).0);
-
-                    cfg.blocks[block_no].instr[instr_no] = Instr::AbiDecode {
-                        res: res.clone(),
-                        selector: *selector,
-                        exception_block: *exception_block,
-                        tys: tys.clone(),
-                        data,
-                        data_len,
-                    }
-                }
                 Instr::SelfDestruct { recipient } => {
                     let (recipient, _) = expression(recipient, Some(&vars), cfg, ns);
 
@@ -1160,32 +1138,6 @@ fn expression(
                 false,
             )
         }
-        Expression::AbiEncode {
-            loc,
-            tys,
-            packed,
-            args,
-        } => {
-            let packed = packed
-                .iter()
-                .map(|expr| expression(expr, vars, cfg, ns).0)
-                .collect();
-            let args = args
-                .iter()
-                .map(|expr| expression(expr, vars, cfg, ns).0)
-                .collect();
-
-            (
-                Expression::AbiEncode {
-                    loc: *loc,
-                    tys: tys.clone(),
-                    packed,
-                    args,
-                },
-                false,
-            )
-        }
-
         Expression::AllocDynamicBytes(loc, ty, len, init) => (
             Expression::AllocDynamicBytes(
                 *loc,

+ 1 - 6
src/codegen/dead_storage.rs

@@ -229,7 +229,6 @@ fn instr_transfers(block_no: usize, block: &BasicBlock) -> Vec<Vec<Transfer>> {
 
                 v
             }
-            Instr::AbiDecode { res, .. } => set_var(res),
             Instr::LoadStorage { res, .. } => set_var(&[*res]),
             Instr::PushMemory { array, res, .. } => {
                 let mut v = set_var(&[*res]);
@@ -591,11 +590,7 @@ pub fn dead_storage(cfg: &mut ControlFlowGraph, _ns: &mut Namespace) {
             } = def
             {
                 // Function calls should never be eliminated from the CFG, as they might have side effects
-                // In addition, AbiDecode might fail and halt the execution.
-                if !matches!(
-                    cfg.blocks[*block_no].instr[*instr_no],
-                    Instr::Call { .. } | Instr::AbiDecode { .. }
-                ) {
+                if !matches!(cfg.blocks[*block_no].instr[*instr_no], Instr::Call { .. }) {
                     cfg.blocks[*block_no].instr[*instr_no] = Instr::Nop;
                 }
             }

+ 21 - 0
src/codegen/dispatch/mod.rs

@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: Apache-2.0
+
+use super::{cfg::ControlFlowGraph, Options};
+use crate::{sema::ast::Namespace, Target};
+
+pub(super) mod solana;
+pub(super) mod substrate;
+
+pub(super) fn function_dispatch(
+    contract_no: usize,
+    all_cfg: &[ControlFlowGraph],
+    ns: &mut Namespace,
+    opt: &Options,
+) -> ControlFlowGraph {
+    match &ns.target {
+        Target::Solana => solana::function_dispatch(contract_no, all_cfg, ns, opt),
+        Target::Substrate { .. } | Target::EVM => {
+            substrate::function_dispatch(contract_no, all_cfg, ns, opt)
+        }
+    }
+}

+ 2 - 2
src/codegen/dispatch.rs → src/codegen/dispatch/solana.rs

@@ -14,10 +14,10 @@ use num_bigint::{BigInt, Sign};
 use num_traits::Zero;
 use solang_parser::{pt, pt::Loc};
 
-use super::encoding::{abi_decode, abi_encode};
+use crate::codegen::encoding::{abi_decode, abi_encode};
 
 /// Create the dispatch for the Solana target
-pub(super) fn function_dispatch(
+pub(crate) fn function_dispatch(
     contract_no: usize,
     all_cfg: &[ControlFlowGraph],
     ns: &mut Namespace,

+ 387 - 0
src/codegen/dispatch/substrate.rs

@@ -0,0 +1,387 @@
+// SPDX-License-Identifier: Apache-2.0
+
+use crate::{
+    codegen::{
+        cfg::{ASTFunction, ControlFlowGraph, Instr, InternalCallTy, ReturnCode},
+        encoding::{abi_decode, abi_encode},
+        expression::log_runtime_error,
+        vartable::Vartable,
+        Builtin, Expression, Options,
+    },
+    sema::ast::{Namespace, Parameter, Type, Type::Uint},
+};
+use num_bigint::{BigInt, Sign};
+use solang_parser::pt::{FunctionTy, Loc::Codegen};
+
+/// The dispatch algorithm consists of these steps:
+/// 1. If the input is less than the expected selector length (default 4 bytes), fallback or receive.
+/// 2. Match the function selector
+///     - If no selector matches, fallback or receive.
+///     - If the function is non-payable but the call features endowment, revert.
+/// 3. ABI decode the arguments.
+/// 4. Call the matching function.
+/// 5. Return the result:
+///     - On success, ABI encode the result (if any) and return.
+///     - On failure, trap the contract.
+pub(crate) fn function_dispatch(
+    _contract_no: usize,
+    all_cfg: &[ControlFlowGraph],
+    ns: &mut Namespace,
+    opt: &Options,
+) -> ControlFlowGraph {
+    Dispatch::new(all_cfg, ns, opt).build()
+}
+
+struct Dispatch<'a> {
+    start: usize,
+    input_len: usize,
+    input_ptr: Expression,
+    value: usize,
+    vartab: Vartable,
+    cfg: ControlFlowGraph,
+    all_cfg: &'a [ControlFlowGraph],
+    ns: &'a mut Namespace,
+    selector_len: Box<Expression>,
+    opt: &'a Options,
+}
+
+fn new_cfg(ns: &Namespace) -> ControlFlowGraph {
+    let mut cfg = ControlFlowGraph::new("substrate_dispatch".into(), ASTFunction::None);
+    let input_ptr = Parameter {
+        loc: Codegen,
+        id: None,
+        ty: Type::BufferPointer,
+        ty_loc: None,
+        indexed: false,
+        readonly: true,
+        infinite_size: false,
+        recursive: false,
+    };
+    let mut input_len = input_ptr.clone();
+    input_len.ty = Uint(32);
+    let mut value = input_ptr.clone();
+    value.ty = ns.value_type();
+    let mut selector_ptr = input_ptr.clone();
+    selector_ptr.ty = Type::Ref(Uint(8 * ns.target.selector_length() as u16).into());
+    cfg.params = vec![input_ptr, input_len, value, selector_ptr].into();
+    cfg
+}
+
+impl<'a> Dispatch<'a> {
+    /// Create a new `Dispatch` struct that has all the data needed for building the dispatch logic.
+    fn new(all_cfg: &'a [ControlFlowGraph], ns: &'a mut Namespace, opt: &'a Options) -> Self {
+        let mut vartab = Vartable::new(ns.next_id);
+        let mut cfg = new_cfg(ns);
+
+        // Read input length from args
+        let input_len = vartab.temp_name("input_len", &Uint(32));
+        cfg.add(
+            &mut vartab,
+            Instr::Set {
+                loc: Codegen,
+                res: input_len,
+                expr: Expression::FunctionArg(Codegen, Uint(32), 1),
+            },
+        );
+
+        // Read transferred value from args
+        let value = vartab.temp_name("value", &ns.value_type());
+        cfg.add(
+            &mut vartab,
+            Instr::Set {
+                loc: Codegen,
+                res: value,
+                expr: Expression::FunctionArg(Codegen, ns.value_type(), 2),
+            },
+        );
+
+        // Calculate input pointer offset
+        let input_ptr_var = vartab.temp_name("input_ptr", &Type::BufferPointer);
+        cfg.add(
+            &mut vartab,
+            Instr::Set {
+                loc: Codegen,
+                res: input_ptr_var,
+                expr: Expression::FunctionArg(Codegen, Type::BufferPointer, 0),
+            },
+        );
+        let input_ptr = Expression::Variable(Codegen, Type::BufferPointer, input_ptr_var);
+        let selector_len: Box<Expression> =
+            Expression::NumberLiteral(Codegen, Uint(32), ns.target.selector_length().into()).into();
+        let input_ptr = Expression::AdvancePointer {
+            pointer: input_ptr.into(),
+            bytes_offset: selector_len.clone(),
+        };
+
+        Self {
+            start: cfg.new_basic_block("start_dispatch".into()),
+            input_len,
+            input_ptr,
+            vartab,
+            value,
+            cfg,
+            all_cfg,
+            ns,
+            selector_len,
+            opt,
+        }
+    }
+
+    /// Build the dispatch logic into the returned control flow graph.
+    fn build(mut self) -> ControlFlowGraph {
+        // Go to fallback or receive if there is no selector in the call input
+        let cond = Expression::Less {
+            loc: Codegen,
+            signed: false,
+            left: Expression::Variable(Codegen, Uint(32), self.input_len).into(),
+            right: self.selector_len.clone(),
+        };
+        let default = self.cfg.new_basic_block("fb_or_recv".into());
+        self.add(Instr::BranchCond {
+            cond,
+            true_block: default,
+            false_block: self.start,
+        });
+
+        // Build all cases
+        let selector_ty = Uint(8 * self.ns.target.selector_length() as u16);
+        let cases = self
+            .all_cfg
+            .iter()
+            .enumerate()
+            .filter_map(|(func_no, func_cfg)| match func_cfg.ty {
+                FunctionTy::Function | FunctionTy::Constructor if func_cfg.public => {
+                    let selector = BigInt::from_bytes_le(Sign::Plus, &func_cfg.selector);
+                    let case = Expression::NumberLiteral(Codegen, selector_ty.clone(), selector);
+                    Some((case, self.dispatch_case(func_no)))
+                }
+                _ => None,
+            })
+            .collect();
+
+        // Read selector
+        self.cfg.set_basic_block(self.start);
+        let selector_var = self.vartab.temp_name("selector", &selector_ty);
+        self.add(Instr::Set {
+            loc: Codegen,
+            res: selector_var,
+            expr: Expression::Builtin(
+                Codegen,
+                vec![selector_ty.clone()],
+                Builtin::ReadFromBuffer,
+                vec![
+                    Expression::FunctionArg(Codegen, Type::BufferPointer, 0),
+                    Expression::NumberLiteral(Codegen, selector_ty.clone(), 0.into()),
+                ],
+            ),
+        });
+        let selector = Expression::Variable(Codegen, selector_ty.clone(), selector_var);
+        self.add(Instr::Store {
+            dest: Expression::FunctionArg(Codegen, selector_ty.clone(), 3),
+            data: selector.clone(),
+        });
+        self.add(Instr::Switch {
+            cond: selector,
+            cases,
+            default,
+        });
+
+        // Handle fallback or receive case
+        self.cfg.set_basic_block(default);
+        self.fallback_or_receive();
+
+        self.vartab.finalize(self.ns, &mut self.cfg);
+        self.cfg
+    }
+
+    /// Insert the dispatch logic for `func_no`. `func_no` may be a function or constructor.
+    /// Returns the basic block number in which the dispatch logic was inserted.
+    fn dispatch_case(&mut self, func_no: usize) -> usize {
+        let case_bb = self.cfg.new_basic_block(format!("func_{func_no}_dispatch"));
+        self.cfg.set_basic_block(case_bb);
+        self.abort_if_value_transfer(func_no);
+
+        // Decode input data if necessary
+        let cfg = &self.all_cfg[func_no];
+        let mut args = vec![];
+        if !cfg.params.is_empty() {
+            let buf_len = Expression::Variable(Codegen, Uint(32), self.input_len);
+            let arg_len = Expression::Subtract(
+                Codegen,
+                Uint(32),
+                false,
+                buf_len.into(),
+                self.selector_len.clone(),
+            );
+            args = abi_decode(
+                &Codegen,
+                &self.input_ptr,
+                &cfg.params.iter().map(|p| p.ty.clone()).collect::<Vec<_>>(),
+                self.ns,
+                &mut self.vartab,
+                &mut self.cfg,
+                Some(Expression::Trunc(Codegen, Uint(32), arg_len.into())),
+            );
+        }
+
+        let mut returns: Vec<usize> = Vec::with_capacity(cfg.returns.len());
+        let mut return_tys: Vec<Type> = Vec::with_capacity(cfg.returns.len());
+        let mut returns_expr: Vec<Expression> = Vec::with_capacity(cfg.returns.len());
+        for item in cfg.returns.iter() {
+            let new_var = self.vartab.temp_anonymous(&item.ty);
+            returns.push(new_var);
+            return_tys.push(item.ty.clone());
+            returns_expr.push(Expression::Variable(Codegen, item.ty.clone(), new_var));
+        }
+
+        self.add(Instr::Call {
+            res: returns,
+            call: InternalCallTy::Static { cfg_no: func_no },
+            args,
+            return_tys,
+        });
+
+        if cfg.returns.is_empty() {
+            let data_len = Expression::NumberLiteral(Codegen, Uint(32), 0.into());
+            let data = Expression::AllocDynamicBytes(
+                Codegen,
+                Type::DynamicBytes,
+                data_len.clone().into(),
+                None,
+            );
+            self.add(Instr::ReturnData { data, data_len })
+        } else {
+            let (data, data_len) = abi_encode(
+                &Codegen,
+                returns_expr,
+                self.ns,
+                &mut self.vartab,
+                &mut self.cfg,
+                false,
+            );
+            self.add(Instr::ReturnData { data, data_len });
+        }
+
+        case_bb
+    }
+
+    /// Insert a trap into the cfg, if the function `func_no` is not payable but received value anyways.
+    /// Constructors always receive endowment.
+    fn abort_if_value_transfer(&mut self, func_no: usize) {
+        if !self.all_cfg[func_no].nonpayable || self.all_cfg[func_no].ty == FunctionTy::Constructor
+        {
+            return;
+        }
+
+        let true_block = self
+            .cfg
+            .new_basic_block(format!("func_{func_no}_got_value"));
+        let false_block = self.cfg.new_basic_block(format!("func_{func_no}_no_value"));
+        self.add(Instr::BranchCond {
+            cond: Expression::More {
+                loc: Codegen,
+                signed: false,
+                left: Expression::Variable(Codegen, self.ns.value_type(), self.value).into(),
+                right: Expression::NumberLiteral(Codegen, self.ns.value_type(), 0.into()).into(),
+            },
+            true_block,
+            false_block,
+        });
+
+        self.cfg.set_basic_block(true_block);
+        let function_name = self.all_cfg[func_no].name.split("::").last().unwrap();
+        log_runtime_error(
+            self.opt.log_runtime_errors,
+            &format!("runtime_error: non payable function {function_name} received value"),
+            Codegen,
+            &mut self.cfg,
+            &mut self.vartab,
+            self.ns,
+        );
+        self.add(Instr::AssertFailure { encoded_args: None });
+
+        self.cfg.set_basic_block(false_block);
+    }
+
+    /// Build calls to fallback or receive functions (if they are present in the contract).
+    fn fallback_or_receive(&mut self) {
+        let (fallback_cfg, receive_cfg) = self.all_cfg.iter().enumerate().fold(
+            (None, None),
+            |(mut fallback_cfg, mut receive_cfg), (no, cfg)| {
+                match cfg.ty {
+                    FunctionTy::Fallback if cfg.public => fallback_cfg = Some(no),
+                    FunctionTy::Receive if cfg.public => receive_cfg = Some(no),
+                    _ => {}
+                }
+                (fallback_cfg, receive_cfg)
+            },
+        );
+
+        // No need to check value transferred; we will abort either way
+        if fallback_cfg.is_none() && receive_cfg.is_none() {
+            return self.selector_invalid();
+        }
+
+        let fallback_block = self.cfg.new_basic_block("fallback".into());
+        let receive_block = self.cfg.new_basic_block("receive".into());
+        self.add(Instr::BranchCond {
+            cond: Expression::More {
+                loc: Codegen,
+                signed: false,
+                left: Expression::Variable(Codegen, self.ns.value_type(), self.value).into(),
+                right: Expression::NumberLiteral(Codegen, self.ns.value_type(), 0.into()).into(),
+            },
+            true_block: receive_block,
+            false_block: fallback_block,
+        });
+
+        self.cfg.set_basic_block(fallback_block);
+        if let Some(cfg_no) = fallback_cfg {
+            self.add(Instr::Call {
+                res: vec![],
+                return_tys: vec![],
+                call: InternalCallTy::Static { cfg_no },
+                args: vec![],
+            });
+            let data_len = Expression::NumberLiteral(Codegen, Uint(32), 0.into());
+            let data = Expression::AllocDynamicBytes(
+                Codegen,
+                Type::DynamicBytes,
+                data_len.clone().into(),
+                None,
+            );
+            self.add(Instr::ReturnData { data, data_len })
+        } else {
+            self.selector_invalid();
+        }
+
+        self.cfg.set_basic_block(receive_block);
+        if let Some(cfg_no) = receive_cfg {
+            self.add(Instr::Call {
+                res: vec![],
+                return_tys: vec![],
+                call: InternalCallTy::Static { cfg_no },
+                args: vec![],
+            });
+            let data_len = Expression::NumberLiteral(Codegen, Uint(32), 0.into());
+            let data = Expression::AllocDynamicBytes(
+                Codegen,
+                Type::DynamicBytes,
+                data_len.clone().into(),
+                None,
+            );
+            self.add(Instr::ReturnData { data, data_len })
+        } else {
+            self.selector_invalid()
+        }
+    }
+
+    fn selector_invalid(&mut self) {
+        let code = ReturnCode::FunctionSelectorInvalid;
+        self.add(Instr::ReturnCode { code });
+    }
+
+    fn add(&mut self, ins: Instr) {
+        self.cfg.add(&mut self.vartab, ins);
+    }
+}

+ 1 - 2
src/codegen/encoding/mod.rs

@@ -176,8 +176,7 @@ pub(super) trait AbiEncoding {
                 self.encode_int(expr, buffer, offset, ns, vartab, cfg, *width)
             }
             Type::Value => {
-                let size = ns.value_length.into();
-                self.encode_directly(expr, buffer, offset, vartab, cfg, size)
+                self.encode_directly(expr, buffer, offset, vartab, cfg, ns.value_length.into())
             }
             Type::Bytes(length) => {
                 self.encode_directly(expr, buffer, offset, vartab, cfg, (*length).into())

+ 4 - 12
src/codegen/events/substrate.rs

@@ -104,18 +104,10 @@ impl EventEmitter for SubstrateEventEmitter<'_> {
                 continue;
             }
 
-            let encoded = Expression::AbiEncode {
-                loc,
-                tys: vec![value.ty()],
-                packed: vec![],
-                args: vec![value],
-            };
-            let concatenated = Expression::StringConcat(
-                loc,
-                Type::DynamicBytes,
-                StringLocation::CompileTime(topic_prefixes.pop_front().unwrap()),
-                StringLocation::RunTime(encoded.into()),
-            );
+            let encoded = abi_encode(&loc, vec![value], self.ns, vartab, cfg, false).0;
+            let prefix = StringLocation::CompileTime(topic_prefixes.pop_front().unwrap());
+            let value = StringLocation::RunTime(encoded.into());
+            let concatenated = Expression::StringConcat(loc, Type::DynamicBytes, prefix, value);
 
             vartab.new_dirty_tracker();
             let var_buffer = vartab.temp_anonymous(&Type::DynamicBytes);

+ 7 - 28
src/codegen/mod.rs

@@ -26,7 +26,9 @@ mod yul;
 
 use self::{
     cfg::{optimize_and_check_cfg, ControlFlowGraph, Instr},
+    dispatch::function_dispatch,
     expression::expression,
+    solana_accounts::collect_accounts_from_contract,
     vartable::Vartable,
 };
 use crate::sema::ast::{
@@ -36,8 +38,6 @@ use crate::{sema::ast, Target};
 use std::cmp::Ordering;
 
 use crate::codegen::cfg::ASTFunction;
-use crate::codegen::dispatch::function_dispatch;
-use crate::codegen::solana_accounts::collect_accounts_from_contract;
 use crate::codegen::yul::generate_yul_function_cfg;
 use crate::sema::Recurse;
 use num_bigint::{BigInt, Sign};
@@ -223,13 +223,9 @@ fn contract(contract_no: usize, ns: &mut Namespace, opt: &Options) {
             ns.contracts[contract_no].default_constructor = Some((func, cfg_no));
         }
 
-        // TODO: This is a temporary solution. Once Substrate's dispatch moves to codegen,
-        // we can remove this if-condition.
-        if ns.target == Target::Solana {
-            let dispatch_cfg = function_dispatch(contract_no, &all_cfg, ns, opt);
-            ns.contracts[contract_no].dispatch_no = all_cfg.len();
-            all_cfg.push(dispatch_cfg);
-        }
+        let dispatch_cfg = function_dispatch(contract_no, &all_cfg, ns, opt);
+        ns.contracts[contract_no].dispatch_no = all_cfg.len();
+        all_cfg.push(dispatch_cfg);
 
         ns.contracts[contract_no].cfg = all_cfg;
     }
@@ -344,12 +340,6 @@ impl LLVMName for Function {
 
 #[derive(Clone, Debug, PartialEq, Eq)]
 pub enum Expression {
-    AbiEncode {
-        loc: pt::Loc,
-        tys: Vec<Type>,
-        packed: Vec<Expression>,
-        args: Vec<Expression>,
-    },
     Add(pt::Loc, Type, bool, Box<Expression>, Box<Expression>),
     AllocDynamicBytes(pt::Loc, Type, Box<Expression>, Option<Vec<u8>>),
     ArrayLiteral(pt::Loc, Type, Vec<u32>, Vec<Expression>),
@@ -445,8 +435,7 @@ pub enum Expression {
 impl CodeLocation for Expression {
     fn loc(&self) -> pt::Loc {
         match self {
-            Expression::AbiEncode { loc, .. }
-            | Expression::StorageArrayLength { loc, .. }
+            Expression::StorageArrayLength { loc, .. }
             | Expression::Builtin(loc, ..)
             | Expression::Cast(loc, ..)
             | Expression::NumberLiteral(loc, ..)
@@ -511,16 +500,6 @@ impl Recurse for Expression {
             return;
         }
         match self {
-            Expression::AbiEncode { packed, args, .. } => {
-                for item in packed {
-                    item.recurse(cx, f);
-                }
-
-                for arg in args {
-                    arg.recurse(cx, f);
-                }
-            }
-
             Expression::BitwiseAnd(_, _, left, right)
             | Expression::BitwiseOr(_, _, left, right)
             | Expression::UnsignedDivide(_, _, left, right)
@@ -599,7 +578,7 @@ impl Recurse for Expression {
 impl RetrieveType for Expression {
     fn ty(&self) -> Type {
         match self {
-            Expression::AbiEncode { .. } | Expression::ReturnData(_) => Type::DynamicBytes,
+            Expression::ReturnData(_) => Type::DynamicBytes,
             Expression::Builtin(_, returns, ..) => {
                 assert_eq!(returns.len(), 1);
                 returns[0].clone()

+ 0 - 1
src/codegen/reaching_definitions.rs

@@ -134,7 +134,6 @@ fn instr_transfers(block_no: usize, block: &BasicBlock) -> Vec<Vec<Transfer>> {
             }
             Instr::Set { res, .. } => set_var(&[*res]),
             Instr::Call { res, .. } => set_var(res),
-            Instr::AbiDecode { res, .. } => set_var(res),
             Instr::LoadStorage { res, .. } | Instr::PopStorage { res: Some(res), .. } => {
                 set_var(&[*res])
             }

+ 1 - 7
src/codegen/solana_accounts.rs

@@ -352,13 +352,7 @@ fn check_instruction(instr: &Instr, data: &mut RecurseData) {
             expr_2.recurse(data, check_expression);
             expr_3.recurse(data, check_expression);
         }
-
-        Instr::AbiDecode {
-            data: expr,
-            data_len: opt_expr,
-            ..
-        }
-        | Instr::PushStorage {
+        Instr::PushStorage {
             value: opt_expr,
             storage: expr,
             ..

+ 63 - 83
src/codegen/statements.rs

@@ -2,7 +2,7 @@
 
 use num_bigint::BigInt;
 
-use super::encoding::abi_encode;
+use super::encoding::{abi_decode, abi_encode};
 use super::expression::{assign_single, default_gas, emit_function_call, expression};
 use super::Options;
 use super::{
@@ -16,16 +16,13 @@ use crate::codegen::unused_variable::{
 };
 use crate::codegen::yul::inline_assembly_cfg;
 use crate::codegen::Expression;
-use crate::sema::ast;
-use crate::sema::ast::RetrieveType;
 use crate::sema::ast::{
-    ArrayLength, CallTy, DestructureField, Function, Namespace, Parameter, Statement, TryCatch,
-    Type,
+    self, ArrayLength, CallTy, DestructureField, Function, Namespace, RetrieveType, Statement,
+    TryCatch, Type, Type::Uint,
 };
 use crate::sema::Recurse;
 use num_traits::Zero;
-use solang_parser::pt;
-use solang_parser::pt::CodeLocation;
+use solang_parser::pt::{self, CodeLocation, Loc::Codegen};
 
 /// Resolve a statement, which might be a block of statements or an entire body of a function
 pub(crate) fn statement(
@@ -82,7 +79,7 @@ pub(crate) fn statement(
                 opt,
             ) = expression
             {
-                let temp_res = vartab.temp_name("array_length", &Type::Uint(32));
+                let temp_res = vartab.temp_name("array_length", &Uint(32));
 
                 cfg.add(
                     vartab,
@@ -96,7 +93,7 @@ pub(crate) fn statement(
                 expression = Expression::AllocDynamicBytes(
                     loc_dyn_arr,
                     ty_dyn_arr,
-                    Box::new(Expression::Variable(*loc, Type::Uint(32), temp_res)),
+                    Box::new(Expression::Variable(*loc, Uint(32), temp_res)),
                     opt,
                 );
                 cfg.array_lengths_temps.insert(*pos, temp_res);
@@ -135,9 +132,8 @@ pub(crate) fn statement(
             // Handling arrays without size, defaulting the initial size with zero
 
             if matches!(param.ty, Type::Array(..)) {
-                let num =
-                    Expression::NumberLiteral(pt::Loc::Codegen, Type::Uint(32), BigInt::zero());
-                let temp_res = vartab.temp_name("array_length", &Type::Uint(32));
+                let num = Expression::NumberLiteral(Codegen, Uint(32), BigInt::zero());
+                let temp_res = vartab.temp_name("array_length", &Uint(32));
                 cfg.add(
                     vartab,
                     Instr::Set {
@@ -1031,7 +1027,7 @@ fn try_catch(
                 let value = if let Some(value) = &call_args.value {
                     expression(value, cfg, callee_contract_no, Some(func), ns, vartab, opt)
                 } else {
-                    Expression::NumberLiteral(pt::Loc::Codegen, Type::Value, BigInt::zero())
+                    Expression::NumberLiteral(Codegen, Type::Value, BigInt::zero())
                 };
                 let gas = if let Some(gas) = &call_args.gas {
                     expression(gas, cfg, callee_contract_no, Some(func), ns, vartab, opt)
@@ -1096,31 +1092,15 @@ fn try_catch(
                         });
                     }
 
-                    let tys = func_returns
-                        .iter()
-                        .map(|ty| Parameter {
-                            ty: ty.clone(),
-                            id: None,
-                            ty_loc: Some(pt::Loc::Codegen),
-                            loc: pt::Loc::Codegen,
-                            indexed: false,
-                            readonly: false,
-                            infinite_size: false,
-                            recursive: false,
-                        })
-                        .collect();
-
-                    cfg.add(
-                        vartab,
-                        Instr::AbiDecode {
-                            res,
-                            selector: None,
-                            exception_block: None,
-                            tys,
-                            data: Expression::ReturnData(pt::Loc::Codegen),
-                            data_len: None,
-                        },
-                    );
+                    let buf = &Expression::ReturnData(Codegen);
+                    let decoded = abi_decode(&Codegen, buf, &func_returns, ns, vartab, cfg, None);
+                    for instruction in res.iter().zip(decoded).map(|(var, expr)| Instr::Set {
+                        loc: Codegen,
+                        res: *var,
+                        expr,
+                    }) {
+                        cfg.add(vartab, instruction)
+                    }
                 }
             } else {
                 // dynamic dispatch
@@ -1210,17 +1190,31 @@ fn try_catch(
             _ => vartab.temp_anonymous(&Type::String),
         };
 
-        cfg.add(
-            vartab,
-            Instr::AbiDecode {
-                selector: Some(0x08c3_79a0),
-                exception_block: Some(no_reason_block),
-                res: vec![error_var],
-                tys: vec![error_param.clone()],
-                data: Expression::ReturnData(pt::Loc::Codegen),
-                data_len: None,
-            },
-        );
+        // Expect the returned data to match the 4 bytes function selector for "Error(string)"
+        let buf = &Expression::ReturnData(Codegen);
+        let tys = &[Uint(32), error_param.ty.clone()];
+        let decoded = abi_decode(&Codegen, buf, tys, ns, vartab, cfg, None);
+        let err_id = Expression::NumberLiteral(Codegen, Uint(32), 0x08c3_79a0.into()).into();
+        let cond = Expression::Equal(Codegen, decoded[0].clone().into(), err_id);
+        let match_err_id = cfg.new_basic_block("match_err_id".into());
+        let no_match_err_id = cfg.new_basic_block("no_match_err_id".into());
+        let instruction = Instr::BranchCond {
+            cond,
+            true_block: match_err_id,
+            false_block: no_match_err_id,
+        };
+
+        cfg.add(vartab, instruction);
+        cfg.set_basic_block(no_match_err_id);
+
+        cfg.add(vartab, Instr::AssertFailure { encoded_args: None });
+        cfg.set_basic_block(match_err_id);
+        let instruction = Instr::Set {
+            loc: Codegen,
+            res: error_var,
+            expr: decoded[1].clone(),
+        };
+        cfg.add(vartab, instruction);
 
         let mut reachable = true;
 
@@ -1253,14 +1247,12 @@ fn try_catch(
     }
 
     if let Some(res) = try_stmt.catch_param_pos {
-        cfg.add(
-            vartab,
-            Instr::Set {
-                loc: pt::Loc::Codegen,
-                res,
-                expr: Expression::ReturnData(pt::Loc::Codegen),
-            },
-        );
+        let instruction = Instr::Set {
+            loc: Codegen,
+            res,
+            expr: Expression::ReturnData(Codegen),
+        };
+        cfg.add(vartab, instruction);
     }
 
     let mut reachable = true;
@@ -1342,16 +1334,16 @@ impl Type {
     /// for example a reference to a variable in storage.
     pub fn default(&self, ns: &Namespace) -> Option<Expression> {
         match self {
-            Type::Address(_) | Type::Uint(_) | Type::Int(_) => Some(Expression::NumberLiteral(
-                pt::Loc::Codegen,
+            Type::Address(_) | Uint(_) | Type::Int(_) => Some(Expression::NumberLiteral(
+                Codegen,
                 self.clone(),
                 BigInt::from(0),
             )),
-            Type::Bool => Some(Expression::BoolLiteral(pt::Loc::Codegen, false)),
+            Type::Bool => Some(Expression::BoolLiteral(Codegen, false)),
             Type::Bytes(n) => {
                 let mut l = Vec::new();
                 l.resize(*n as usize, 0);
-                Some(Expression::BytesLiteral(pt::Loc::Codegen, self.clone(), l))
+                Some(Expression::BytesLiteral(Codegen, self.clone(), l))
             }
             Type::Enum(e) => ns.enums[*e].ty.default(ns),
             Type::Struct(struct_ty) => {
@@ -1360,20 +1352,16 @@ impl Type {
                     field.ty.default(ns)?;
                 }
 
-                Some(Expression::StructLiteral(
-                    pt::Loc::Codegen,
-                    self.clone(),
-                    Vec::new(),
-                ))
+                Some(Expression::StructLiteral(Codegen, self.clone(), Vec::new()))
             }
             Type::Ref(ty) => {
                 assert!(matches!(ty.as_ref(), Type::Address(_)));
 
                 Some(Expression::GetRef(
-                    pt::Loc::Codegen,
+                    Codegen,
                     Type::Ref(Box::new(ty.as_ref().clone())),
                     Box::new(Expression::NumberLiteral(
-                        pt::Loc::Codegen,
+                        Codegen,
                         ty.as_ref().clone(),
                         BigInt::from(0),
                     )),
@@ -1381,13 +1369,9 @@ impl Type {
             }
             Type::StorageRef(..) => None,
             Type::String | Type::DynamicBytes => Some(Expression::AllocDynamicBytes(
-                pt::Loc::Codegen,
+                Codegen,
                 self.clone(),
-                Box::new(Expression::NumberLiteral(
-                    pt::Loc::Codegen,
-                    Type::Uint(32),
-                    BigInt::zero(),
-                )),
+                Box::new(Expression::NumberLiteral(Codegen, Uint(32), BigInt::zero())),
                 None,
             )),
             Type::InternalFunction { .. } | Type::Contract(_) | Type::ExternalFunction { .. } => {
@@ -1398,18 +1382,14 @@ impl Type {
 
                 if dims.last() == Some(&ArrayLength::Dynamic) {
                     Some(Expression::AllocDynamicBytes(
-                        pt::Loc::Codegen,
+                        Codegen,
                         self.clone(),
-                        Box::new(Expression::NumberLiteral(
-                            pt::Loc::Codegen,
-                            Type::Uint(32),
-                            BigInt::zero(),
-                        )),
+                        Box::new(Expression::NumberLiteral(Codegen, Uint(32), BigInt::zero())),
                         None,
                     ))
                 } else {
                     Some(Expression::ArrayLiteral(
-                        pt::Loc::Codegen,
+                        Codegen,
                         self.clone(),
                         Vec::new(),
                         Vec::new(),
@@ -1425,7 +1405,7 @@ impl Namespace {
     /// Phoney default constructor
     pub fn default_constructor(&self, contract_no: usize) -> Function {
         let mut func = Function::new(
-            pt::Loc::Codegen,
+            Codegen,
             "".to_owned(),
             Some(contract_no),
             vec![],
@@ -1437,7 +1417,7 @@ impl Namespace {
             self,
         );
 
-        func.body = vec![Statement::Return(pt::Loc::Codegen, None)];
+        func.body = vec![Statement::Return(Codegen, None)];
         func.has_body = true;
 
         func

+ 0 - 3
src/codegen/strength_reduce/mod.rs

@@ -195,9 +195,6 @@ fn block_reduce(
                 *address = expression_reduce(address, &vars, ns);
                 *value = expression_reduce(value, &vars, ns);
             }
-            Instr::AbiDecode { data, .. } => {
-                *data = expression_reduce(data, &vars, ns);
-            }
             Instr::EmitEvent { topics, data, .. } => {
                 *topics = topics
                     .iter()

+ 0 - 24
src/codegen/strength_reduce/reaching_values.rs

@@ -80,15 +80,6 @@ pub(super) fn reaching_values(
 
                 reaching_values(*false_block, cfg, vars, block_vars, ns);
             }
-            Instr::AbiDecode {
-                exception_block: Some(block),
-                ..
-            } => {
-                let mut vars = vars.clone();
-
-                reaching_values(*block, cfg, &mut vars, block_vars, ns);
-            }
-
             _ => (),
         }
     }
@@ -158,21 +149,6 @@ pub(super) fn transfer(instr: &Instr, vars: &mut Variables, ns: &Namespace) {
 
             vars.insert(*res, v);
         }
-        Instr::AbiDecode { res, tys, .. } => {
-            for (i, var_no) in res.iter().enumerate() {
-                let ty = &tys[i].ty;
-
-                if track(ty) {
-                    let mut set = HashSet::new();
-
-                    let bits = ty.bits(ns) as usize;
-
-                    set.insert(Value::unknown(bits));
-
-                    vars.insert(*var_no, set);
-                }
-            }
-        }
         Instr::Call {
             res, return_tys, ..
         } => {

+ 0 - 20
src/codegen/subexpression_elimination/instruction.rs

@@ -23,7 +23,6 @@ impl<'a, 'b: 'a> AvailableExpressionSet<'a> {
                 encoded_args: Some(expr),
             }
             | Instr::PopStorage { storage: expr, .. }
-            | Instr::AbiDecode { data: expr, .. }
             | Instr::SelfDestruct { recipient: expr } => {
                 let _ = self.gen_expression(expr, ave, cst);
             }
@@ -411,25 +410,6 @@ impl<'a, 'b: 'a> AvailableExpressionSet<'a> {
                 address: self.regenerate_expression(address, ave, cst).1,
                 value: self.regenerate_expression(value, ave, cst).1,
             },
-
-            Instr::AbiDecode {
-                res,
-                selector,
-                exception_block,
-                tys,
-                data,
-                data_len,
-            } => Instr::AbiDecode {
-                res: res.clone(),
-                selector: *selector,
-                exception_block: *exception_block,
-                tys: tys.clone(),
-                data: self.regenerate_expression(data, ave, cst).1,
-                data_len: data_len
-                    .as_ref()
-                    .map(|e| self.regenerate_expression(e, ave, cst).1),
-            },
-
             Instr::SelfDestruct { recipient } => Instr::SelfDestruct {
                 recipient: self.regenerate_expression(recipient, ave, cst).1,
             },

+ 0 - 24
src/codegen/subexpression_elimination/tests.rs

@@ -197,30 +197,6 @@ fn not_tracked() {
     assert!(set.find_expression(&load).is_none());
 }
 
-#[test]
-fn invalid() {
-    let arg = Expression::FunctionArg(Loc::Codegen, Type::Int(1), 1);
-    let exp = Expression::List(Loc::Codegen, vec![arg.clone()]);
-
-    let instr = Instr::AbiDecode {
-        res: vec![],
-        selector: None,
-        exception_block: None,
-        tys: vec![],
-        data: exp.clone(),
-        data_len: None,
-    };
-
-    let mut ave = AvailableExpression::default();
-    let mut set = AvailableExpressionSet::default();
-    let mut cst = CommonSubExpressionTracker::default();
-
-    set.process_instruction(&instr, &mut ave, &mut Some(&mut cst));
-
-    assert!(set.find_expression(&arg).is_none());
-    assert!(set.find_expression(&exp).is_none());
-}
-
 #[test]
 fn complex_expression() {
     let var = Expression::Variable(Loc::Codegen, Type::Int(8), 2);

+ 0 - 1
src/codegen/vector_to_slice.rs

@@ -112,7 +112,6 @@ fn find_writable_vectors(
             | Instr::PopStorage { .. }
             | Instr::SelfDestruct { .. }
             | Instr::EmitEvent { .. }
-            | Instr::AbiDecode { .. }
             | Instr::ExternalCall { .. }
             | Instr::Constructor { .. }
             | Instr::Unreachable

+ 0 - 21
src/emit/binary.rs

@@ -702,20 +702,6 @@ impl<'a> Binary<'a> {
         }
     }
 
-    /// Default empty value
-    pub(crate) fn default_value(&self, ty: &Type, ns: &Namespace) -> BasicValueEnum<'a> {
-        let llvm_ty = self.llvm_var_ty(ty, ns);
-
-        // const_zero() on BasicTypeEnum yet. Should be coming to inkwell soon
-        if llvm_ty.is_pointer_type() {
-            llvm_ty.into_pointer_type().const_null().into()
-        } else if llvm_ty.is_array_type() {
-            self.address_type(ns).const_zero().into()
-        } else {
-            llvm_ty.into_int_type().const_zero().into()
-        }
-    }
-
     /// Return the llvm type for field in struct or array
     pub(crate) fn llvm_field_ty(&self, ty: &Type, ns: &Namespace) -> BasicTypeEnum<'a> {
         let llvm_ty = self.llvm_type(ty, ns);
@@ -1047,12 +1033,6 @@ fn load_stdlib<'a>(context: &'a Context, target: &Target) -> Module<'a> {
     }
 
     if let Target::Substrate { .. } = *target {
-        let memory = MemoryBuffer::create_from_memory_range(SUBSTRATE_IR, "substrate");
-
-        module
-            .link_in_module(Module::parse_bitcode_from_buffer(&memory, context).unwrap())
-            .unwrap();
-
         // substrate does not provide ripemd160
         let memory = MemoryBuffer::create_from_memory_range(RIPEMD160_IR, "ripemd160");
 
@@ -1081,4 +1061,3 @@ static WASM_IR: [&[u8]; 4] = [
 ];
 
 static RIPEMD160_IR: &[u8] = include_bytes!("../../stdlib/wasm/ripemd160.bc");
-static SUBSTRATE_IR: &[u8] = include_bytes!("../../stdlib/wasm/substrate.bc");

+ 0 - 18
src/emit/expression.rs

@@ -1473,24 +1473,6 @@ pub(super) fn expression<'a, T: TargetRuntime<'a> + ?Sized>(
                 .storage_array_length(bin, function, slot, elem_ty, ns)
                 .into()
         }
-        Expression::AbiEncode {
-            tys, packed, args, ..
-        } => target
-            .abi_encode_to_vector(
-                bin,
-                function,
-                &packed
-                    .iter()
-                    .map(|a| expression(target, bin, a, vartab, function, ns))
-                    .collect::<Vec<BasicValueEnum>>(),
-                &args
-                    .iter()
-                    .map(|a| expression(target, bin, a, vartab, function, ns))
-                    .collect::<Vec<BasicValueEnum>>(),
-                tys,
-                ns,
-            )
-            .into(),
         Expression::Builtin(_, _, Builtin::Signature, _) if ns.target != Target::Solana => {
             // need to byte-reverse selector
             let selector_type = bin.context.i32_type();

+ 1 - 54
src/emit/functions.rs

@@ -4,11 +4,7 @@ use crate::{
     emit::{binary::Binary, cfg::emit_cfg, TargetRuntime},
     sema::ast::{Contract, Namespace, Type},
 };
-use inkwell::{
-    module::Linkage,
-    values::FunctionValue,
-    {AddressSpace, IntPredicate},
-};
+use inkwell::{module::Linkage, values::FunctionValue};
 
 /// Emit all functions, constructors, fallback and receiver
 pub(super) fn emit_functions<'a, T: TargetRuntime<'a>>(
@@ -75,52 +71,3 @@ pub(super) fn emit_initializer<'a, T: TargetRuntime<'a>>(
 
     function
 }
-
-/// If we receive a value transfer, and we are not "payable", abort with revert
-pub(super) fn abort_if_value_transfer<'a, T: TargetRuntime<'a> + ?Sized>(
-    target: &T,
-    binary: &Binary,
-    function: FunctionValue,
-    ns: &Namespace,
-    function_name: &str,
-) {
-    let value = target.value_transferred(binary, ns);
-
-    let got_value = binary.builder.build_int_compare(
-        IntPredicate::NE,
-        value,
-        binary.value_type(ns).const_zero(),
-        "is_value_transfer",
-    );
-
-    let not_value_transfer = binary
-        .context
-        .append_basic_block(function, "not_value_transfer");
-    let abort_value_transfer = binary
-        .context
-        .append_basic_block(function, "abort_value_transfer");
-
-    binary
-        .builder
-        .build_conditional_branch(got_value, abort_value_transfer, not_value_transfer);
-
-    binary.builder.position_at_end(abort_value_transfer);
-
-    target.log_runtime_error(
-        binary,
-        format!("runtime_error: non payable function {function_name} received value"),
-        None,
-        ns,
-    );
-    target.assert_failure(
-        binary,
-        binary
-            .context
-            .i8_type()
-            .ptr_type(AddressSpace::default())
-            .const_null(),
-        binary.context.i32_type().const_zero(),
-    );
-
-    binary.builder.position_at_end(not_value_transfer);
-}

+ 0 - 102
src/emit/instructions.rs

@@ -953,108 +953,6 @@ pub(super) fn process_instruction<'a, T: TargetRuntime<'a> + ?Sized>(
 
             target.value_transfer(bin, function, success, addr, value, ns, loc);
         }
-        Instr::AbiDecode {
-            res,
-            selector,
-            exception_block: exception,
-            tys,
-            data,
-            data_len,
-        } => {
-            let v = expression(target, bin, data, &w.vars, function, ns);
-
-            let mut data = if data.ty().is_reference_type(ns) {
-                bin.vector_bytes(v)
-            } else {
-                v.into_pointer_value()
-            };
-
-            let mut data_len = if let Some(len) = data_len {
-                expression(target, bin, len, &w.vars, function, ns).into_int_value()
-            } else {
-                bin.vector_len(v)
-            };
-
-            if let Some(selector) = selector {
-                let exception = exception.unwrap();
-
-                let pos = bin.builder.get_insert_block().unwrap();
-
-                blocks.entry(exception).or_insert({
-                    work.push_back(Work {
-                        block_no: exception,
-                        vars: w.vars.clone(),
-                    });
-
-                    create_block(exception, bin, cfg, function, ns)
-                });
-
-                bin.builder.position_at_end(pos);
-
-                let exception_block = blocks.get(&exception).unwrap();
-
-                let has_selector = bin.builder.build_int_compare(
-                    IntPredicate::UGT,
-                    data_len,
-                    bin.context.i32_type().const_int(4, false),
-                    "has_selector",
-                );
-
-                let ok1 = bin.context.append_basic_block(function, "ok1");
-
-                bin.builder
-                    .build_conditional_branch(has_selector, ok1, exception_block.bb);
-                bin.builder.position_at_end(ok1);
-
-                let selector_data = bin
-                    .builder
-                    .build_load(bin.context.i32_type(), data, "selector")
-                    .into_int_value();
-
-                let selector = if ns.target.is_substrate() {
-                    *selector
-                } else {
-                    selector.to_be()
-                };
-
-                let correct_selector = bin.builder.build_int_compare(
-                    IntPredicate::EQ,
-                    selector_data,
-                    bin.context.i32_type().const_int(selector as u64, false),
-                    "correct_selector",
-                );
-
-                let ok2 = bin.context.append_basic_block(function, "ok2");
-
-                bin.builder
-                    .build_conditional_branch(correct_selector, ok2, exception_block.bb);
-
-                bin.builder.position_at_end(ok2);
-
-                data_len = bin.builder.build_int_sub(
-                    data_len,
-                    bin.context.i32_type().const_int(4, false),
-                    "data_len",
-                );
-
-                data = unsafe {
-                    bin.builder.build_gep(
-                        bin.context.i8_type(),
-                        data,
-                        &[bin.context.i32_type().const_int(4, false)],
-                        "data",
-                    )
-                };
-            }
-
-            let mut returns = Vec::new();
-
-            target.abi_decode(bin, function, &mut returns, data, data_len, tys, ns);
-
-            for (i, ret) in returns.into_iter().enumerate() {
-                w.vars.get_mut(&res[i]).unwrap().value = ret;
-            }
-        }
         Instr::Unreachable => {
             // Nothing to do; unreachable instruction should have already been inserteds
         }

+ 1 - 36
src/emit/mod.rs

@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: Apache-2.0
 
 use crate::codegen::Expression;
-use crate::sema::ast::{CallTy, Function, Namespace, Parameter, Type};
+use crate::sema::ast::{CallTy, Function, Namespace, Type};
 use std::collections::HashMap;
 use std::fmt;
 use std::str;
@@ -65,41 +65,6 @@ impl fmt::Display for BinaryOp {
 }
 
 pub trait TargetRuntime<'a> {
-    fn abi_decode<'b>(
-        &self,
-        bin: &Binary<'b>,
-        function: FunctionValue<'b>,
-        args: &mut Vec<BasicValueEnum<'b>>,
-        data: PointerValue<'b>,
-        length: IntValue<'b>,
-        spec: &[Parameter],
-        ns: &Namespace,
-    );
-
-    /// Abi encode with optional four bytes selector. The load parameter should be set if the args are
-    /// pointers to data, not the actual data  itself.
-    fn abi_encode(
-        &self,
-        bin: &Binary<'a>,
-        selector: Option<IntValue<'a>>,
-        load: bool,
-        function: FunctionValue<'a>,
-        args: &[BasicValueEnum<'a>],
-        tys: &[Type],
-        ns: &Namespace,
-    ) -> (PointerValue<'a>, IntValue<'a>);
-
-    /// ABI encode into a vector for abi.encode* style builtin functions
-    fn abi_encode_to_vector<'b>(
-        &self,
-        binary: &Binary<'b>,
-        function: FunctionValue<'b>,
-        packed: &[BasicValueEnum<'b>],
-        args: &[BasicValueEnum<'b>],
-        tys: &[Type],
-        ns: &Namespace,
-    ) -> PointerValue<'b>;
-
     fn get_storage_int(
         &self,
         bin: &Binary<'a>,

+ 0 - 39
src/emit/solana/target.rs

@@ -1229,45 +1229,6 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
         ));
     }
 
-    /// ABI encode into a vector for abi.encode* style builtin functions
-    fn abi_encode_to_vector<'b>(
-        &self,
-        _binary: &Binary<'b>,
-        _function: FunctionValue<'b>,
-        _packed: &[BasicValueEnum<'b>],
-        _args: &[BasicValueEnum<'b>],
-        _tys: &[ast::Type],
-        _ns: &ast::Namespace,
-    ) -> PointerValue<'b> {
-        unreachable!("ABI encoding is implemented in code generation for Solana")
-    }
-
-    fn abi_encode(
-        &self,
-        _binary: &Binary<'a>,
-        _selector: Option<IntValue<'a>>,
-        _load: bool,
-        _function: FunctionValue<'a>,
-        _args: &[BasicValueEnum<'a>],
-        _tys: &[ast::Type],
-        _ns: &ast::Namespace,
-    ) -> (PointerValue<'a>, IntValue<'a>) {
-        unreachable!("ABI encoding is implemented in code generation for Solana")
-    }
-
-    fn abi_decode<'b>(
-        &self,
-        _binary: &Binary<'b>,
-        _function: FunctionValue<'b>,
-        _args: &mut Vec<BasicValueEnum<'b>>,
-        _data: PointerValue<'b>,
-        _length: IntValue<'b>,
-        _spec: &[ast::Parameter],
-        _ns: &ast::Namespace,
-    ) {
-        unreachable!("ABI encoding is implemented in code generation for Solana.")
-    }
-
     fn print(&self, binary: &Binary, string_ptr: PointerValue, string_len: IntValue) {
         let string_len64 =
             binary

+ 0 - 287
src/emit/substrate/dispatch.rs

@@ -1,287 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-use crate::codegen::cfg::{ControlFlowGraph, ReturnCode};
-use crate::emit::binary::Binary;
-use crate::emit::functions::abort_if_value_transfer;
-use crate::emit::substrate::SubstrateTarget;
-use crate::emit::TargetRuntime;
-use crate::sema::ast::{Contract, Namespace, Type};
-use crate::Target;
-use inkwell::types::BasicType;
-use inkwell::values::{BasicMetadataValueEnum, FunctionValue};
-use inkwell::{AddressSpace, IntPredicate};
-use solang_parser::pt;
-use std::collections::HashMap;
-
-impl SubstrateTarget {
-    /// Create function dispatch based on abi encoded argsdata. The dispatcher loads the leading function selector,
-    /// and dispatches based on that. If no function matches this, or no selector is in the argsdata, then fallback
-    /// code is executed. This is either a fallback block provided to this function, or it automatically dispatches
-    /// to the fallback function or receive function, if any.
-    pub(super) fn emit_function_dispatch<'a, F>(
-        &self,
-        bin: &Binary<'a>,
-        contract: &Contract,
-        ns: &Namespace,
-        function_ty: pt::FunctionTy,
-        argsdata: inkwell::values::PointerValue<'a>,
-        argslen: inkwell::values::IntValue<'a>,
-        function: inkwell::values::FunctionValue<'a>,
-        functions: &HashMap<usize, FunctionValue<'a>>,
-        fallback: Option<inkwell::basic_block::BasicBlock>,
-        nonpayable: F,
-    ) where
-        F: Fn(&ControlFlowGraph) -> bool,
-    {
-        // create start function
-        let no_function_matched = match fallback {
-            Some(block) => block,
-            None => bin
-                .context
-                .append_basic_block(function, "no_function_matched"),
-        };
-
-        let switch_block = bin.context.append_basic_block(function, "switch");
-
-        let not_fallback = bin.builder.build_int_compare(
-            IntPredicate::UGE,
-            argslen,
-            argslen.get_type().const_int(4, false),
-            "",
-        );
-
-        bin.builder
-            .build_conditional_branch(not_fallback, switch_block, no_function_matched);
-
-        bin.builder.position_at_end(switch_block);
-
-        let fid = bin
-            .builder
-            .build_load(bin.context.i32_type(), argsdata, "function_selector")
-            .into_int_value();
-
-        // TODO: solana does not support bss, so different solution is needed
-        bin.builder
-            .build_store(bin.selector.as_pointer_value(), fid);
-
-        // step over the function selector
-        let argsdata = unsafe {
-            bin.builder.build_gep(
-                bin.context.i32_type(),
-                argsdata,
-                &[bin.context.i32_type().const_int(1, false)],
-                "argsdata",
-            )
-        };
-
-        let argslen =
-            bin.builder
-                .build_int_sub(argslen, argslen.get_type().const_int(4, false), "argslen");
-
-        let mut cases = Vec::new();
-
-        for (cfg_no, cfg) in contract.cfg.iter().enumerate() {
-            if cfg.ty != function_ty || !cfg.public {
-                continue;
-            }
-
-            self.add_dispatch_case(
-                bin,
-                cfg,
-                ns,
-                &mut cases,
-                argsdata,
-                argslen,
-                function,
-                functions[&cfg_no],
-                &nonpayable,
-            );
-        }
-
-        bin.builder.position_at_end(switch_block);
-
-        bin.builder.build_switch(fid, no_function_matched, &cases);
-
-        if fallback.is_some() {
-            return; // caller will generate fallback code
-        }
-
-        // emit fallback code
-        bin.builder.position_at_end(no_function_matched);
-
-        let fallback = contract
-            .cfg
-            .iter()
-            .enumerate()
-            .find(|(_, cfg)| cfg.public && cfg.ty == pt::FunctionTy::Fallback);
-
-        let receive = contract
-            .cfg
-            .iter()
-            .enumerate()
-            .find(|(_, cfg)| cfg.public && cfg.ty == pt::FunctionTy::Receive);
-
-        if fallback.is_none() && receive.is_none() {
-            // no need to check value transferred; we will abort either way
-            self.return_code(bin, bin.return_values[&ReturnCode::FunctionSelectorInvalid]);
-
-            return;
-        }
-
-        let got_value = if bin.function_abort_value_transfers {
-            bin.context.bool_type().const_zero()
-        } else {
-            let value = self.value_transferred(bin, ns);
-
-            bin.builder.build_int_compare(
-                IntPredicate::NE,
-                value,
-                bin.value_type(ns).const_zero(),
-                "is_value_transfer",
-            )
-        };
-
-        let fallback_block = bin.context.append_basic_block(function, "fallback");
-        let receive_block = bin.context.append_basic_block(function, "receive");
-
-        bin.builder
-            .build_conditional_branch(got_value, receive_block, fallback_block);
-
-        bin.builder.position_at_end(fallback_block);
-
-        match fallback {
-            Some((cfg_no, _)) => {
-                let args = vec![];
-                bin.builder.build_call(functions[&cfg_no], &args, "");
-                self.return_empty_abi(bin);
-            }
-            None => {
-                self.return_code(bin, bin.context.i32_type().const_int(2, false));
-            }
-        }
-
-        bin.builder.position_at_end(receive_block);
-
-        match receive {
-            Some((cfg_no, _)) => {
-                let args = if ns.target == Target::Solana {
-                    vec![function.get_last_param().unwrap().into()]
-                } else {
-                    vec![]
-                };
-
-                bin.builder.build_call(functions[&cfg_no], &args, "");
-
-                self.return_empty_abi(bin);
-            }
-            None => {
-                self.return_code(bin, bin.context.i32_type().const_int(2, false));
-            }
-        }
-    }
-
-    /// Add single case for emit_function_dispatch
-    fn add_dispatch_case<'a, F>(
-        &self,
-        bin: &Binary<'a>,
-        f: &ControlFlowGraph,
-        ns: &Namespace,
-        cases: &mut Vec<(
-            inkwell::values::IntValue<'a>,
-            inkwell::basic_block::BasicBlock<'a>,
-        )>,
-        argsdata: inkwell::values::PointerValue<'a>,
-        argslen: inkwell::values::IntValue<'a>,
-        function: inkwell::values::FunctionValue<'a>,
-        dest: inkwell::values::FunctionValue<'a>,
-        nonpayable: &F,
-    ) where
-        F: Fn(&ControlFlowGraph) -> bool,
-    {
-        let bb = bin.context.append_basic_block(function, "");
-
-        bin.builder.position_at_end(bb);
-
-        let function_name: Vec<&str> = f.name.split("::").collect();
-        if nonpayable(f) {
-            abort_if_value_transfer(self, bin, function, ns, function_name.last().unwrap());
-        }
-
-        let mut args = Vec::new();
-
-        // insert abi decode
-        self.abi_decode(bin, function, &mut args, argsdata, argslen, &f.params, ns);
-
-        // add return values as pointer arguments at the end
-        if !f.returns.is_empty() {
-            for v in f.returns.iter() {
-                args.push(if !v.ty.is_reference_type(ns) {
-                    bin.build_alloca(function, bin.llvm_type(&v.ty, ns), v.name_as_str())
-                        .into()
-                } else {
-                    bin.build_alloca(
-                        function,
-                        bin.llvm_type(&v.ty, ns).ptr_type(AddressSpace::default()),
-                        v.name_as_str(),
-                    )
-                    .into()
-                });
-            }
-        }
-
-        let meta_args: Vec<BasicMetadataValueEnum> = args.iter().map(|arg| (*arg).into()).collect();
-
-        let ret = bin
-            .builder
-            .build_call(dest, &meta_args, "")
-            .try_as_basic_value()
-            .left()
-            .unwrap();
-
-        let success = bin.builder.build_int_compare(
-            IntPredicate::EQ,
-            ret.into_int_value(),
-            bin.return_values[&ReturnCode::Success],
-            "success",
-        );
-
-        let success_block = bin.context.append_basic_block(function, "success");
-        let bail_block = bin.context.append_basic_block(function, "bail");
-
-        bin.builder
-            .build_conditional_branch(success, success_block, bail_block);
-
-        bin.builder.position_at_end(success_block);
-
-        if f.returns.is_empty() {
-            // return ABI of length 0
-            self.return_empty_abi(bin);
-        } else {
-            let tys: Vec<Type> = f.returns.iter().map(|p| p.ty.clone()).collect();
-
-            let (data, length) = self.abi_encode(
-                bin,
-                None,
-                true,
-                function,
-                &args[f.params.len()..f.params.len() + f.returns.len()],
-                &tys,
-                ns,
-            );
-
-            self.return_abi(bin, data, length);
-        }
-
-        bin.builder.position_at_end(bail_block);
-
-        self.return_code(bin, ret.into_int_value());
-
-        cases.push((
-            bin.context.i32_type().const_int(
-                u32::from_le_bytes(f.selector.as_slice().try_into().unwrap()) as u64,
-                false,
-            ),
-            bb,
-        ));
-    }
-}

+ 26 - 1479
src/emit/substrate/mod.rs

@@ -1,19 +1,15 @@
 // SPDX-License-Identifier: Apache-2.0
 
-use crate::{codegen::Options, sema::ast};
+use crate::codegen::Options;
+use crate::sema::ast::{Contract, Namespace};
 use inkwell::context::Context;
 use inkwell::module::{Linkage, Module};
-use inkwell::types::BasicType;
-use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue};
+use inkwell::values::{BasicMetadataValueEnum, FunctionValue, IntValue, PointerValue};
 use inkwell::AddressSpace;
-use inkwell::IntPredicate;
-use num_traits::ToPrimitive;
-use solang_parser::pt;
 
-use crate::emit::functions::{abort_if_value_transfer, emit_functions, emit_initializer};
+use crate::emit::functions::{emit_functions, emit_initializer};
 use crate::emit::{Binary, TargetRuntime};
 
-mod dispatch;
 mod storage;
 pub(super) mod target;
 
@@ -106,8 +102,8 @@ impl SubstrateTarget {
     pub fn build<'a>(
         context: &'a Context,
         std_lib: &Module<'a>,
-        contract: &'a ast::Contract,
-        ns: &'a ast::Namespace,
+        contract: &'a Contract,
+        ns: &'a Namespace,
         opt: &'a Options,
     ) -> Binary<'a> {
         let filename = ns.files[contract.loc.file_no()].file_name();
@@ -148,8 +144,9 @@ impl SubstrateTarget {
 
         emit_functions(&mut target, &mut binary, contract, ns);
 
-        target.emit_deploy(&mut binary, contract, ns);
-        target.emit_call(&binary, contract, ns);
+        let storage_initializer = emit_initializer(&mut target, &mut binary, contract, ns);
+        target.emit_dispatch(Some(storage_initializer), &mut binary, ns);
+        target.emit_dispatch(None, &mut binary, ns);
 
         binary.internalize(&[
             "deploy",
@@ -189,19 +186,11 @@ impl SubstrateTarget {
         &self,
         binary: &Binary<'a>,
         function: FunctionValue,
-        abort_value_transfers: bool,
-        ns: &ast::Namespace,
-        function_name: &str,
     ) -> (PointerValue<'a>, IntValue<'a>) {
         let entry = binary.context.append_basic_block(function, "entry");
 
         binary.builder.position_at_end(entry);
 
-        // after copying stratch, first thing to do is abort value transfers if constructors not payable
-        if abort_value_transfers {
-            abort_if_value_transfer(self, binary, function, ns, function_name);
-        }
-
         // init our heap
         binary
             .builder
@@ -329,1466 +318,24 @@ impl SubstrateTarget {
         );
     }
 
-    fn emit_deploy(&mut self, binary: &mut Binary, contract: &ast::Contract, ns: &ast::Namespace) {
-        let initializer = emit_initializer(self, binary, contract, ns);
-
-        // create deploy function
-        let function = binary.module.add_function(
-            "deploy",
-            binary.context.void_type().fn_type(&[], false),
-            None,
-        );
-
-        // deploy always receives an endowment so no value check here
-        let (deploy_args, deploy_args_length) =
-            self.public_function_prelude(binary, function, false, ns, "deploy");
-
-        // init our storage vars
-        binary.builder.build_call(initializer, &[], "");
-
-        let fallback_block = binary.context.append_basic_block(function, "fallback");
-
-        self.emit_function_dispatch(
-            binary,
-            contract,
-            ns,
-            pt::FunctionTy::Constructor,
-            deploy_args,
-            deploy_args_length,
-            function,
-            &binary.functions,
-            Some(fallback_block),
-            |_| false,
-        );
-
-        // emit fallback code
-        binary.builder.position_at_end(fallback_block);
-
-        self.assert_failure(
-            binary,
-            binary
-                .context
-                .i8_type()
-                .ptr_type(AddressSpace::default())
-                .const_null(),
-            binary.context.i32_type().const_zero(),
-        );
-    }
-
-    fn emit_call(&mut self, binary: &Binary, contract: &ast::Contract, ns: &ast::Namespace) {
-        // create call function
-        let function = binary.module.add_function(
-            "call",
-            binary.context.void_type().fn_type(&[], false),
-            None,
-        );
-
-        let (contract_args, contract_args_length) = self.public_function_prelude(
-            binary,
-            function,
-            binary.function_abort_value_transfers,
-            ns,
-            "call",
-        );
-
-        self.emit_function_dispatch(
-            binary,
-            contract,
-            ns,
-            pt::FunctionTy::Function,
-            contract_args,
-            contract_args_length,
-            function,
-            &binary.functions,
-            None,
-            |func| !binary.function_abort_value_transfers && func.nonpayable,
-        );
-    }
-
-    /// ABI decode a single primitive
-    fn decode_primitive<'b>(
-        &self,
-        binary: &Binary<'b>,
-        ty: &ast::Type,
-        src: PointerValue<'b>,
-        ns: &ast::Namespace,
-    ) -> (BasicValueEnum<'b>, u64) {
-        match ty {
-            ast::Type::Bool => {
-                let val = binary.builder.build_int_compare(
-                    IntPredicate::EQ,
-                    binary
-                        .builder
-                        .build_load(binary.context.i8_type(), src, "abi_bool")
-                        .into_int_value(),
-                    binary.context.i8_type().const_int(1, false),
-                    "bool",
-                );
-                (val.into(), 1)
-            }
-            ast::Type::Uint(bits) | ast::Type::Int(bits) => {
-                let int_type = binary.context.custom_width_int_type(*bits as u32);
-
-                let val = binary.builder.build_load(int_type, src, "");
-
-                // substrate only supports power-of-two types; step over the
-                // the remainer
-
-                // FIXME: we should do some type-checking here and ensure that the
-                // encoded value fits into our smaller type
-                let len = bits.next_power_of_two() as u64 / 8;
-
-                (val, len)
-            }
-            ast::Type::Contract(_) | ast::Type::Address(_) => {
-                let val = binary.builder.build_load(binary.address_type(ns), src, "");
-
-                let len = ns.address_length as u64;
-
-                (val, len)
-            }
-            ast::Type::Bytes(len) => {
-                let int_type = binary.context.custom_width_int_type(*len as u32 * 8);
-
-                let buf = binary.builder.build_alloca(int_type, "buf");
-
-                // byte order needs to be reversed. e.g. hex"11223344" should be 0x10 0x11 0x22 0x33 0x44
-                binary.builder.build_call(
-                    binary.module.get_function("__beNtoleN").unwrap(),
-                    &[
-                        src.into(),
-                        buf.into(),
-                        binary
-                            .context
-                            .i32_type()
-                            .const_int(*len as u64, false)
-                            .into(),
-                    ],
-                    "",
-                );
-
-                (
-                    binary
-                        .builder
-                        .build_load(int_type, buf, &format!("bytes{len}")),
-                    *len as u64,
-                )
-            }
-            _ => unreachable!(),
-        }
-    }
-
-    /// Check that data has not overrun end, and whether end == data to check we do not have
-    /// trailing data
-    fn check_overrun(
-        &self,
-        binary: &Binary,
-        function: FunctionValue,
-        data: PointerValue,
-        end: PointerValue,
-        end_is_data: bool,
-    ) {
-        let in_bounds = binary.builder.build_int_compare(
-            if end_is_data {
-                IntPredicate::EQ
-            } else {
-                IntPredicate::ULE
-            },
-            binary
-                .builder
-                .build_ptr_to_int(data, binary.context.i32_type(), "args"),
-            binary
-                .builder
-                .build_ptr_to_int(end, binary.context.i32_type(), "end"),
-            "is_done",
-        );
-
-        let success_block = binary.context.append_basic_block(function, "success");
-        let bail_block = binary.context.append_basic_block(function, "bail");
-        binary
-            .builder
-            .build_conditional_branch(in_bounds, success_block, bail_block);
-
-        binary.builder.position_at_end(bail_block);
-
-        self.assert_failure(
-            binary,
-            binary
-                .context
-                .i8_type()
-                .ptr_type(AddressSpace::default())
-                .const_null(),
-            binary.context.i32_type().const_zero(),
-        );
-
-        binary.builder.position_at_end(success_block);
-    }
-
-    /// recursively encode a single ty
-    fn decode_ty<'b>(
-        &self,
-        binary: &Binary<'b>,
-        function: FunctionValue,
-        ty: &ast::Type,
-        data: &mut PointerValue<'b>,
-        end: PointerValue<'b>,
-        ns: &ast::Namespace,
-    ) -> BasicValueEnum<'b> {
-        match &ty {
-            ast::Type::Bool
-            | ast::Type::Address(_)
-            | ast::Type::Contract(_)
-            | ast::Type::Int(_)
-            | ast::Type::Uint(_)
-            | ast::Type::Bytes(_) => {
-                let (arg, arglen) = self.decode_primitive(binary, ty, *data, ns);
-
-                *data = unsafe {
-                    binary.builder.build_gep(
-                        binary.context.i8_type(),
-                        *data,
-                        &[binary.context.i32_type().const_int(arglen, false)],
-                        "abi_ptr",
-                    )
-                };
-
-                self.check_overrun(binary, function, *data, end, false);
-
-                arg
-            }
-            ast::Type::Enum(n) => self.decode_ty(binary, function, &ns.enums[*n].ty, data, end, ns),
-            ast::Type::UserType(n) => {
-                self.decode_ty(binary, function, &ns.user_types[*n].ty, data, end, ns)
-            }
-            ast::Type::Struct(str_ty) => {
-                let llvm_ty = binary.llvm_type(ty.deref_any(), ns);
-
-                let size = llvm_ty
-                    .size_of()
-                    .unwrap()
-                    .const_cast(binary.context.i32_type(), false);
-
-                let new = binary
-                    .builder
-                    .build_call(
-                        binary.module.get_function("__malloc").unwrap(),
-                        &[size.into()],
-                        "",
-                    )
-                    .try_as_basic_value()
-                    .left()
-                    .unwrap()
-                    .into_pointer_value();
-
-                for (i, field) in str_ty.definition(ns).fields.iter().enumerate() {
-                    let elem = unsafe {
-                        binary.builder.build_gep(
-                            llvm_ty,
-                            new,
-                            &[
-                                binary.context.i32_type().const_zero(),
-                                binary.context.i32_type().const_int(i as u64, false),
-                            ],
-                            field.name_as_str(),
-                        )
-                    };
-
-                    let val = self.decode_ty(binary, function, &field.ty, data, end, ns);
-
-                    let val = if field.ty.deref_memory().is_fixed_reference_type(ns) {
-                        let field_ty = binary.llvm_type(&field.ty, ns);
-                        binary.builder.build_load(
-                            field_ty,
-                            val.into_pointer_value(),
-                            field.name_as_str(),
-                        )
-                    } else {
-                        val
-                    };
-
-                    binary.builder.build_store(elem, val);
-                }
-
-                new.into()
-            }
-            ast::Type::Array(_, dim) => {
-                if let Some(ast::ArrayLength::Fixed(d)) = dim.last() {
-                    let llvm_ty = binary.llvm_type(ty.deref_any(), ns);
-
-                    let size = llvm_ty
-                        .size_of()
-                        .unwrap()
-                        .const_cast(binary.context.i32_type(), false);
-
-                    let ty = ty.array_deref();
-
-                    let new = binary
-                        .builder
-                        .build_call(
-                            binary.module.get_function("__malloc").unwrap(),
-                            &[size.into()],
-                            "",
-                        )
-                        .try_as_basic_value()
-                        .left()
-                        .unwrap()
-                        .into_pointer_value();
-
-                    binary.emit_static_loop_with_pointer(
-                        function,
-                        binary.context.i64_type().const_zero(),
-                        binary
-                            .context
-                            .i64_type()
-                            .const_int(d.to_u64().unwrap(), false),
-                        data,
-                        |index: IntValue<'b>, data: &mut PointerValue<'b>| {
-                            let elem = unsafe {
-                                binary.builder.build_gep(
-                                    llvm_ty,
-                                    new,
-                                    &[binary.context.i32_type().const_zero(), index],
-                                    "index_access",
-                                )
-                            };
-
-                            let val = self.decode_ty(binary, function, &ty, data, end, ns);
-
-                            let val = if ty.deref_memory().is_fixed_reference_type(ns) {
-                                let field_ty = binary.llvm_type(ty.deref_memory(), ns);
-                                binary.builder.build_load(
-                                    field_ty,
-                                    val.into_pointer_value(),
-                                    "elem",
-                                )
-                            } else {
-                                val
-                            };
-
-                            binary.builder.build_store(elem, val);
-                        },
-                    );
-
-                    new.into()
-                } else {
-                    let len = binary
-                        .builder
-                        .build_alloca(binary.context.i32_type(), "length");
-
-                    *data = binary
-                        .builder
-                        .build_call(
-                            binary.module.get_function("compact_decode_u32").unwrap(),
-                            &[(*data).into(), len.into()],
-                            "",
-                        )
-                        .try_as_basic_value()
-                        .left()
-                        .unwrap()
-                        .into_pointer_value();
-
-                    let len = binary
-                        .builder
-                        .build_load(binary.context.i32_type(), len, "array.len")
-                        .into_int_value();
-
-                    // details about our array elements
-                    let elem_ty = binary.llvm_field_ty(&ty.array_elem(), ns);
-                    let elem_size = elem_ty
-                        .size_of()
-                        .unwrap()
-                        .const_cast(binary.context.i32_type(), false);
-
-                    let init = binary.builder.build_int_to_ptr(
-                        binary.context.i32_type().const_all_ones(),
-                        binary.context.i8_type().ptr_type(AddressSpace::default()),
-                        "invalid",
-                    );
-
-                    let v = binary
-                        .builder
-                        .build_call(
-                            binary.module.get_function("vector_new").unwrap(),
-                            &[len.into(), elem_size.into(), init.into()],
-                            "",
-                        )
-                        .try_as_basic_value()
-                        .left()
-                        .unwrap()
-                        .into_pointer_value();
-
-                    binary.emit_loop_cond_first_with_pointer(
-                        function,
-                        binary.context.i32_type().const_zero(),
-                        len,
-                        data,
-                        |elem_no: IntValue<'b>, data: &mut PointerValue<'b>| {
-                            let index = binary.builder.build_int_mul(elem_no, elem_size, "");
-
-                            let element_start = unsafe {
-                                binary.builder.build_gep(
-                                    binary.context.get_struct_type("struct.vector").unwrap(),
-                                    v,
-                                    &[
-                                        binary.context.i32_type().const_zero(),
-                                        binary.context.i32_type().const_int(2, false),
-                                        index,
-                                    ],
-                                    "data",
-                                )
-                            };
-
-                            let ty = ty.array_deref();
-
-                            let val = self.decode_ty(binary, function, &ty, data, end, ns);
-
-                            let val = if ty.deref_memory().is_fixed_reference_type(ns) {
-                                let load_ty = binary.llvm_type(ty.deref_memory(), ns);
-                                binary
-                                    .builder
-                                    .build_load(load_ty, val.into_pointer_value(), "elem")
-                            } else {
-                                val
-                            };
-
-                            binary.builder.build_store(element_start, val);
-                        },
-                    );
-                    v.into()
-                }
-            }
-            ast::Type::String | ast::Type::DynamicBytes => {
-                let from = binary.builder.build_alloca(
-                    binary.context.i8_type().ptr_type(AddressSpace::default()),
-                    "from",
-                );
-
-                binary.builder.build_store(from, *data);
-
-                let v = binary
-                    .builder
-                    .build_call(
-                        binary.module.get_function("scale_decode_string").unwrap(),
-                        &[from.into()],
-                        "",
-                    )
-                    .try_as_basic_value()
-                    .left()
-                    .unwrap();
-
-                *data = binary
-                    .builder
-                    .build_load(
-                        binary.context.i8_type().ptr_type(AddressSpace::default()),
-                        from,
-                        "data",
-                    )
-                    .into_pointer_value();
-
-                self.check_overrun(binary, function, *data, end, false);
-
-                v
-            }
-            ast::Type::Ref(ty) => self.decode_ty(binary, function, ty, data, end, ns),
-            ast::Type::ExternalFunction { .. } => {
-                let address =
-                    self.decode_ty(binary, function, &ast::Type::Address(false), data, end, ns);
-                let selector =
-                    self.decode_ty(binary, function, &ast::Type::Bytes(4), data, end, ns);
-
-                let ty = binary.llvm_type(ty, ns);
-
-                let ef = binary
-                    .builder
-                    .build_call(
-                        binary.module.get_function("__malloc").unwrap(),
-                        &[ty.size_of()
-                            .unwrap()
-                            .const_cast(binary.context.i32_type(), false)
-                            .into()],
-                        "",
-                    )
-                    .try_as_basic_value()
-                    .left()
-                    .unwrap()
-                    .into_pointer_value();
-
-                let address_member = unsafe {
-                    binary.builder.build_gep(
-                        ty,
-                        ef,
-                        &[
-                            binary.context.i32_type().const_zero(),
-                            binary.context.i32_type().const_int(1, false),
-                        ],
-                        "address",
-                    )
-                };
-
-                binary.builder.build_store(address_member, address);
-
-                let selector_member = unsafe {
-                    binary.builder.build_gep(
-                        ty,
-                        ef,
-                        &[
-                            binary.context.i32_type().const_zero(),
-                            binary.context.i32_type().const_zero(),
-                        ],
-                        "selector",
-                    )
-                };
-
-                binary.builder.build_store(selector_member, selector);
-
-                ef.into()
-            }
-            _ => unreachable!(),
-        }
-    }
-
-    /// ABI encode a single primitive
-    fn encode_primitive(
-        &self,
-        binary: &Binary,
-        load: bool,
-        ty: &ast::Type,
-        dest: PointerValue,
-        arg: BasicValueEnum,
-        ns: &ast::Namespace,
-    ) -> u64 {
-        match ty {
-            ast::Type::Bool => {
-                let arg = if load {
-                    let load_ty = binary.llvm_type(ty, ns);
-                    binary
-                        .builder
-                        .build_load(load_ty, arg.into_pointer_value(), "")
-                } else {
-                    arg
-                };
-
-                binary.builder.build_store(
-                    dest,
-                    binary.builder.build_int_z_extend(
-                        arg.into_int_value(),
-                        binary.context.i8_type(),
-                        "bool",
-                    ),
-                );
-
-                1
-            }
-            ast::Type::Uint(_) | ast::Type::Int(_) => {
-                let len = match ty {
-                    ast::Type::Uint(n) | ast::Type::Int(n) => *n as u64 / 8,
-                    _ => ns.address_length as u64,
-                };
-
-                let arg = if load {
-                    let load_ty = binary.llvm_type(ty, ns);
-                    binary
-                        .builder
-                        .build_load(load_ty, arg.into_pointer_value(), "")
-                } else {
-                    arg
-                };
-
-                // substrate only supports power-of-two types; upcast to correct type
-                let power_of_two_len = len.next_power_of_two();
-
-                let arg = if len == power_of_two_len {
-                    arg.into_int_value()
-                } else if ty.is_signed_int(ns) {
-                    binary.builder.build_int_s_extend(
-                        arg.into_int_value(),
-                        binary
-                            .context
-                            .custom_width_int_type(power_of_two_len as u32 * 8),
-                        "",
-                    )
-                } else {
-                    binary.builder.build_int_z_extend(
-                        arg.into_int_value(),
-                        binary
-                            .context
-                            .custom_width_int_type(power_of_two_len as u32 * 8),
-                        "",
-                    )
-                };
-
-                binary.builder.build_store(dest, arg);
-
-                power_of_two_len
-            }
-            ast::Type::Contract(_) | ast::Type::Address(_) => {
-                let arg = if load {
-                    binary
-                        .builder
-                        .build_load(binary.address_type(ns), arg.into_pointer_value(), "")
-                } else {
-                    arg
-                };
-
-                binary.builder.build_store(dest, arg.into_array_value());
-
-                ns.address_length as u64
-            }
-            ast::Type::Bytes(n) => {
-                let val = if load {
-                    arg.into_pointer_value()
-                } else {
-                    let temp = binary
-                        .builder
-                        .build_alloca(arg.into_int_value().get_type(), &format!("bytes{n}"));
-
-                    binary.builder.build_store(temp, arg.into_int_value());
-
-                    temp
-                };
-
-                // byte order needs to be reversed. e.g. hex"11223344" should be 0x10 0x11 0x22 0x33 0x44
-                binary.builder.build_call(
-                    binary.module.get_function("__leNtobeN").unwrap(),
-                    &[
-                        val.into(),
-                        dest.into(),
-                        binary.context.i32_type().const_int(*n as u64, false).into(),
-                    ],
-                    "",
-                );
-
-                *n as u64
-            }
-            _ => unimplemented!(),
-        }
-    }
-
-    /// recursively encode argument. The encoded data is written to the data pointer,
-    /// and the pointer is updated point after the encoded data.
-    ///
-    /// FIXME: this function takes a "load" arguments, which tells the encoded whether the data should be
-    /// dereferenced. However, this is already encoded by the fact it is a Type::Ref(..) type. So, the load
-    /// argument should be removed from this function.
-    pub fn encode_ty<'x>(
-        &self,
-        binary: &Binary<'x>,
-        ns: &ast::Namespace,
-        load: bool,
-        packed: bool,
-        function: FunctionValue,
-        ty: &ast::Type,
-        arg: BasicValueEnum<'x>,
-        data: &mut PointerValue<'x>,
-    ) {
-        match &ty {
-            ast::Type::Bool
-            | ast::Type::Address(_)
-            | ast::Type::Contract(_)
-            | ast::Type::Int(_)
-            | ast::Type::Uint(_)
-            | ast::Type::Bytes(_) => {
-                let arglen = self.encode_primitive(binary, load, ty, *data, arg, ns);
-
-                *data = unsafe {
-                    binary.builder.build_gep(
-                        binary.context.i8_type(),
-                        *data,
-                        &[binary.context.i32_type().const_int(arglen, false)],
-                        "",
-                    )
-                };
-            }
-            ast::Type::UserType(no) => self.encode_ty(
-                binary,
-                ns,
-                load,
-                packed,
-                function,
-                &ns.user_types[*no].ty,
-                arg,
-                data,
-            ),
-            ast::Type::Enum(no) => self.encode_ty(
-                binary,
-                ns,
-                load,
-                packed,
-                function,
-                &ns.enums[*no].ty,
-                arg,
-                data,
-            ),
-            ast::Type::Array(_, dim) if matches!(dim.last(), Some(ast::ArrayLength::Fixed(_))) => {
-                let arg = if load {
-                    let load_ty = binary.llvm_type(ty, ns).ptr_type(AddressSpace::default());
-                    binary
-                        .builder
-                        .build_load(load_ty, arg.into_pointer_value(), "")
-                        .into_pointer_value()
-                } else {
-                    arg.into_pointer_value()
-                };
-
-                let null_array = binary.context.append_basic_block(function, "null_array");
-                let normal_array = binary.context.append_basic_block(function, "normal_array");
-                let done_array = binary.context.append_basic_block(function, "done_array");
-
-                let dim = ty.array_length().unwrap().to_u64().unwrap();
-
-                let elem_ty = ty.array_deref();
-
-                let is_null = binary.builder.build_is_null(arg, "is_null");
-
-                binary
-                    .builder
-                    .build_conditional_branch(is_null, null_array, normal_array);
-
-                binary.builder.position_at_end(normal_array);
-
-                let mut normal_data = *data;
-
-                binary.emit_static_loop_with_pointer(
-                    function,
-                    binary.context.i64_type().const_zero(),
-                    binary.context.i64_type().const_int(dim, false),
-                    &mut normal_data,
-                    |index, elem_data| {
-                        let elem = unsafe {
-                            binary.builder.build_gep(
-                                binary.llvm_type(ty, ns),
-                                arg,
-                                &[binary.context.i32_type().const_zero(), index],
-                                "index_access",
-                            )
-                        };
-
-                        self.encode_ty(
-                            binary,
-                            ns,
-                            !elem_ty.is_fixed_reference_type(ns),
-                            packed,
-                            function,
-                            &elem_ty,
-                            elem.into(),
-                            elem_data,
-                        );
-                    },
-                );
-
-                binary.builder.build_unconditional_branch(done_array);
-
-                let normal_array = binary.builder.get_insert_block().unwrap();
-
-                binary.builder.position_at_end(null_array);
-
-                let mut null_data = *data;
-
-                let elem = binary.default_value(elem_ty.deref_any(), ns);
-
-                binary.emit_static_loop_with_pointer(
-                    function,
-                    binary.context.i64_type().const_zero(),
-                    binary.context.i64_type().const_int(dim, false),
-                    &mut null_data,
-                    |_, elem_data| {
-                        self.encode_ty(
-                            binary,
-                            ns,
-                            false,
-                            packed,
-                            function,
-                            elem_ty.deref_any(),
-                            elem,
-                            elem_data,
-                        );
-                    },
-                );
-
-                binary.builder.build_unconditional_branch(done_array);
-
-                let null_array = binary.builder.get_insert_block().unwrap();
-
-                binary.builder.position_at_end(done_array);
-
-                let either_data = binary.builder.build_phi(
-                    binary.context.i8_type().ptr_type(AddressSpace::default()),
-                    "either_data",
-                );
-
-                either_data.add_incoming(&[(&normal_data, normal_array), (&null_data, null_array)]);
-
-                *data = either_data.as_basic_value().into_pointer_value()
-            }
-            ast::Type::Array(..) => {
-                let arg = if load {
-                    let load_ty = binary.llvm_type(ty, ns).ptr_type(AddressSpace::default());
-                    binary
-                        .builder
-                        .build_load(load_ty, arg.into_pointer_value(), "")
-                } else {
-                    arg
-                };
-
-                let len = binary.vector_len(arg);
-
-                if !packed {
-                    *data = binary
-                        .builder
-                        .build_call(
-                            binary.module.get_function("compact_encode_u32").unwrap(),
-                            &[(*data).into(), len.into()],
-                            "",
-                        )
-                        .try_as_basic_value()
-                        .left()
-                        .unwrap()
-                        .into_pointer_value();
-                }
-
-                let elem_ty = ty.array_deref();
-
-                binary.emit_loop_cond_first_with_pointer(
-                    function,
-                    binary.context.i32_type().const_zero(),
-                    len,
-                    data,
-                    |elem_no, data| {
-                        let elem =
-                            binary.array_subscript(ty, arg.into_pointer_value(), elem_no, ns);
-
-                        self.encode_ty(
-                            binary,
-                            ns,
-                            !elem_ty.deref_any().is_fixed_reference_type(ns),
-                            packed,
-                            function,
-                            elem_ty.deref_any(),
-                            elem.into(),
-                            data,
-                        );
-                    },
-                );
-            }
-            ast::Type::Struct(str_ty) => {
-                let arg = if load {
-                    let load_ty = binary.llvm_type(ty, ns).ptr_type(AddressSpace::default());
-                    binary
-                        .builder
-                        .build_load(
-                            load_ty,
-                            arg.into_pointer_value(),
-                            &format!("encode_{}", str_ty.definition(ns).name),
-                        )
-                        .into_pointer_value()
-                } else {
-                    arg.into_pointer_value()
-                };
-
-                let null_struct = binary.context.append_basic_block(function, "null_struct");
-                let normal_struct = binary.context.append_basic_block(function, "normal_struct");
-                let done_struct = binary.context.append_basic_block(function, "done_struct");
-
-                let is_null = binary.builder.build_is_null(arg, "is_null");
-
-                binary
-                    .builder
-                    .build_conditional_branch(is_null, null_struct, normal_struct);
-
-                binary.builder.position_at_end(normal_struct);
-
-                let mut normal_data = *data;
-                for (i, field) in str_ty.definition(ns).fields.iter().enumerate() {
-                    let elem = unsafe {
-                        binary.builder.build_gep(
-                            binary.llvm_type(ty, ns),
-                            arg,
-                            &[
-                                binary.context.i32_type().const_zero(),
-                                binary.context.i32_type().const_int(i as u64, false),
-                            ],
-                            field.name_as_str(),
-                        )
-                    };
-
-                    self.encode_ty(
-                        binary,
-                        ns,
-                        !field.ty.is_fixed_reference_type(ns),
-                        packed,
-                        function,
-                        &field.ty,
-                        elem.into(),
-                        &mut normal_data,
-                    );
-                }
-
-                binary.builder.build_unconditional_branch(done_struct);
-
-                let normal_struct = binary.builder.get_insert_block().unwrap();
-
-                binary.builder.position_at_end(null_struct);
-
-                let mut null_data = *data;
-
-                for field in &str_ty.definition(ns).fields {
-                    let elem = binary.default_value(&field.ty, ns);
-
-                    self.encode_ty(
-                        binary,
-                        ns,
-                        false,
-                        packed,
-                        function,
-                        &field.ty,
-                        elem,
-                        &mut null_data,
-                    );
-                }
-
-                binary.builder.build_unconditional_branch(done_struct);
-
-                let null_struct = binary.builder.get_insert_block().unwrap();
-
-                binary.builder.position_at_end(done_struct);
-
-                let either_data = binary.builder.build_phi(
-                    binary.context.i8_type().ptr_type(AddressSpace::default()),
-                    "either_data",
-                );
-
-                either_data
-                    .add_incoming(&[(&normal_data, normal_struct), (&null_data, null_struct)]);
-
-                *data = either_data.as_basic_value().into_pointer_value()
-            }
-            ast::Type::Ref(ty) => {
-                self.encode_ty(
-                    binary,
-                    ns,
-                    !ty.is_fixed_reference_type(ns),
-                    packed,
-                    function,
-                    ty,
-                    arg,
-                    data,
-                );
-            }
-            ast::Type::String | ast::Type::DynamicBytes => {
-                let arg = if load {
-                    let load_ty = binary.llvm_type(ty, ns).ptr_type(AddressSpace::default());
-                    binary
-                        .builder
-                        .build_load(load_ty, arg.into_pointer_value(), "")
-                } else {
-                    arg
-                };
-
-                let string_len = binary.vector_len(arg);
-
-                let string_data = binary.vector_bytes(arg);
-
-                if !packed {
-                    let function = binary.module.get_function("scale_encode_string").unwrap();
-
-                    *data = binary
-                        .builder
-                        .build_call(
-                            function,
-                            &[(*data).into(), string_data.into(), string_len.into()],
-                            "",
-                        )
-                        .try_as_basic_value()
-                        .left()
-                        .unwrap()
-                        .into_pointer_value();
-                } else {
-                    binary.builder.build_call(
-                        binary.module.get_function("__memcpy").unwrap(),
-                        &[(*data).into(), string_data.into(), string_len.into()],
-                        "",
-                    );
-
-                    *data = unsafe {
-                        binary
-                            .builder
-                            .build_gep(binary.context.i8_type(), *data, &[string_len], "")
-                    };
-                }
-            }
-            ast::Type::ExternalFunction { .. } => {
-                let arg = if load {
-                    let load_ty = binary.llvm_type(ty, ns).ptr_type(AddressSpace::default());
-                    binary
-                        .builder
-                        .build_load(load_ty, arg.into_pointer_value(), "")
-                } else {
-                    arg
-                };
-
-                let address_member = unsafe {
-                    binary.builder.build_gep(
-                        binary.llvm_type(ty, ns),
-                        arg.into_pointer_value(),
-                        &[
-                            binary.context.i32_type().const_zero(),
-                            binary.context.i32_type().const_int(1, false),
-                        ],
-                        "address",
-                    )
-                };
-
-                let address =
-                    binary
-                        .builder
-                        .build_load(binary.address_type(ns), address_member, "address");
-
-                self.encode_ty(
-                    binary,
-                    ns,
-                    false,
-                    false,
-                    function,
-                    &ast::Type::Address(false),
-                    address,
-                    data,
-                );
-
-                let selector_member = unsafe {
-                    binary.builder.build_gep(
-                        binary.llvm_type(ty, ns),
-                        arg.into_pointer_value(),
-                        &[
-                            binary.context.i32_type().const_zero(),
-                            binary.context.i32_type().const_zero(),
-                        ],
-                        "selector",
-                    )
-                };
-
-                let selector = binary.builder.build_load(
-                    binary.context.i32_type(),
-                    selector_member,
-                    "selector",
-                );
-
-                self.encode_ty(
-                    binary,
-                    ns,
-                    false,
-                    false,
-                    function,
-                    &ast::Type::Bytes(4),
-                    selector,
-                    data,
-                );
-            }
-            ast::Type::FunctionSelector => self.encode_ty(
-                binary,
-                ns,
-                load,
-                packed,
-                function,
-                &ast::Type::Bytes(4),
-                arg,
-                data,
-            ),
-            _ => unreachable!(),
-        };
-    }
-
-    /// Calculate the maximum space a type will need when encoded. This is used for
-    /// allocating enough space to do abi encoding. The length for vectors is always
-    /// assumed to be five, even when it can be encoded in less bytes. The overhead
-    /// of calculating the exact size is not worth reducing the malloc by a few bytes.
-    ///
-    /// FIXME: this function takes a "load" arguments, which tells the encoded whether the data should be
-    /// dereferenced. However, this is already encoded by the fact it is a Type::Ref(..) type. So, the load
-    /// argument should be removed from this function.
-    pub fn encoded_length<'x>(
-        arg: BasicValueEnum<'x>,
-        load: bool,
-        packed: bool,
-        ty: &ast::Type,
-        function: FunctionValue,
-        binary: &Binary<'x>,
-        ns: &ast::Namespace,
-    ) -> IntValue<'x> {
-        match ty {
-            ast::Type::Bool => binary.context.i32_type().const_int(1, false),
-            ast::Type::Uint(n) | ast::Type::Int(n) => {
-                binary.context.i32_type().const_int(*n as u64 / 8, false)
-            }
-            ast::Type::Bytes(n) => binary.context.i32_type().const_int(*n as u64, false),
-            ast::Type::FunctionSelector => binary
-                .context
-                .i32_type()
-                .const_int(ns.target.selector_length() as u64, false),
-            ast::Type::Address(_) | ast::Type::Contract(_) => binary
-                .context
-                .i32_type()
-                .const_int(ns.address_length as u64, false),
-            ast::Type::Enum(n) => SubstrateTarget::encoded_length(
-                arg,
-                load,
-                packed,
-                &ns.enums[*n].ty,
-                function,
-                binary,
-                ns,
-            ),
-            ast::Type::Struct(str_ty) => {
-                let arg = if load {
-                    let load_ty = binary.llvm_type(ty, ns).ptr_type(AddressSpace::default());
-                    binary
-                        .builder
-                        .build_load(
-                            load_ty,
-                            arg.into_pointer_value(),
-                            &format!("encoded_length_struct_{}", str_ty.definition(ns).name),
-                        )
-                        .into_pointer_value()
-                } else {
-                    arg.into_pointer_value()
-                };
-
-                let normal_struct = binary.context.append_basic_block(function, "normal_struct");
-                let null_struct = binary.context.append_basic_block(function, "null_struct");
-                let done_struct = binary.context.append_basic_block(function, "done_struct");
-
-                let is_null = binary.builder.build_is_null(arg, "is_null");
-
-                binary
-                    .builder
-                    .build_conditional_branch(is_null, null_struct, normal_struct);
-
-                binary.builder.position_at_end(normal_struct);
-
-                let mut normal_sum = binary.context.i32_type().const_zero();
-
-                // avoid generating load instructions for structs with only fixed fields
-                for (i, field) in str_ty.definition(ns).fields.iter().enumerate() {
-                    let elem = unsafe {
-                        binary.builder.build_gep(
-                            binary.llvm_type(ty, ns),
-                            arg,
-                            &[
-                                binary.context.i32_type().const_zero(),
-                                binary.context.i32_type().const_int(i as u64, false),
-                            ],
-                            field.name_as_str(),
-                        )
-                    };
-
-                    normal_sum = binary.builder.build_int_add(
-                        normal_sum,
-                        SubstrateTarget::encoded_length(
-                            elem.into(),
-                            !field.ty.is_fixed_reference_type(ns),
-                            packed,
-                            &field.ty,
-                            function,
-                            binary,
-                            ns,
-                        ),
-                        "",
-                    );
-                }
-
-                binary.builder.build_unconditional_branch(done_struct);
-
-                let normal_struct = binary.builder.get_insert_block().unwrap();
-
-                binary.builder.position_at_end(null_struct);
-
-                let mut null_sum = binary.context.i32_type().const_zero();
-
-                for field in &str_ty.definition(ns).fields {
-                    null_sum = binary.builder.build_int_add(
-                        null_sum,
-                        SubstrateTarget::encoded_length(
-                            binary.default_value(&field.ty, ns),
-                            false,
-                            packed,
-                            &field.ty,
-                            function,
-                            binary,
-                            ns,
-                        ),
-                        "",
-                    );
-                }
-
-                binary.builder.build_unconditional_branch(done_struct);
-
-                let null_struct = binary.builder.get_insert_block().unwrap();
-
-                binary.builder.position_at_end(done_struct);
-
-                let sum = binary.builder.build_phi(binary.context.i32_type(), "sum");
-
-                sum.add_incoming(&[(&normal_sum, normal_struct), (&null_sum, null_struct)]);
-
-                sum.as_basic_value().into_int_value()
-            }
-            ast::Type::Array(_, dims)
-                if matches!(dims.last(), Some(ast::ArrayLength::Fixed(_))) =>
-            {
-                let array_length = binary
-                    .context
-                    .i32_type()
-                    .const_int(ty.array_length().unwrap().to_u64().unwrap(), false);
-
-                let elem_ty = ty.array_deref();
-
-                if elem_ty.is_dynamic(ns) {
-                    let arg = if load {
-                        let load_ty = binary.llvm_var_ty(ty, ns);
-                        binary
-                            .builder
-                            .build_load(load_ty, arg.into_pointer_value(), "")
-                            .into_pointer_value()
-                    } else {
-                        arg.into_pointer_value()
-                    };
-
-                    let normal_array = binary.context.append_basic_block(function, "normal_array");
-                    let null_array = binary.context.append_basic_block(function, "null_array");
-                    let done_array = binary.context.append_basic_block(function, "done_array");
-
-                    let is_null = binary.builder.build_is_null(arg, "is_null");
-
-                    binary
-                        .builder
-                        .build_conditional_branch(is_null, null_array, normal_array);
-
-                    binary.builder.position_at_end(normal_array);
-
-                    let mut normal_length = binary.context.i32_type().const_zero();
-
-                    // if the array contains dynamic elements, we have to iterate over
-                    // every one and calculate its length
-                    binary.emit_static_loop_with_int(
-                        function,
-                        binary.context.i32_type().const_zero(),
-                        array_length,
-                        &mut normal_length,
-                        |index, sum| {
-                            let elem = unsafe {
-                                binary.builder.build_gep(
-                                    binary.llvm_type(ty, ns),
-                                    arg,
-                                    &[binary.context.i32_type().const_zero(), index],
-                                    "index_access",
-                                )
-                            };
-
-                            *sum = binary.builder.build_int_add(
-                                SubstrateTarget::encoded_length(
-                                    elem.into(),
-                                    !elem_ty.deref_memory().is_fixed_reference_type(ns),
-                                    packed,
-                                    &elem_ty,
-                                    function,
-                                    binary,
-                                    ns,
-                                ),
-                                *sum,
-                                "",
-                            );
-                        },
-                    );
-
-                    binary.builder.build_unconditional_branch(done_array);
-
-                    let normal_array = binary.builder.get_insert_block().unwrap();
-
-                    binary.builder.position_at_end(null_array);
-
-                    let elem = binary.default_value(elem_ty.deref_any(), ns);
-
-                    let null_length = binary.builder.build_int_mul(
-                        SubstrateTarget::encoded_length(
-                            elem,
-                            false,
-                            packed,
-                            elem_ty.deref_any(),
-                            function,
-                            binary,
-                            ns,
-                        ),
-                        array_length,
-                        "",
-                    );
-
-                    binary.builder.build_unconditional_branch(done_array);
-
-                    let null_array = binary.builder.get_insert_block().unwrap();
-
-                    binary.builder.position_at_end(done_array);
-
-                    let encoded_length = binary
-                        .builder
-                        .build_phi(binary.context.i32_type(), "encoded_length");
-
-                    encoded_length.add_incoming(&[
-                        (&normal_length, normal_array),
-                        (&null_length, null_array),
-                    ]);
-
-                    encoded_length.as_basic_value().into_int_value()
-                } else {
-                    // elements have static length
-                    let elem = binary.default_value(elem_ty.deref_any(), ns);
-
-                    binary.builder.build_int_mul(
-                        SubstrateTarget::encoded_length(
-                            elem,
-                            false,
-                            packed,
-                            elem_ty.deref_any(),
-                            function,
-                            binary,
-                            ns,
-                        ),
-                        array_length,
-                        "",
-                    )
-                }
-            }
-            ast::Type::Array(_, dims) if dims.last() == Some(&ast::ArrayLength::Dynamic) => {
-                let arg = if load {
-                    let load_ty = binary.llvm_type(ty, ns).ptr_type(AddressSpace::default());
-                    binary
-                        .builder
-                        .build_load(load_ty, arg.into_pointer_value(), "")
-                } else {
-                    arg
-                };
-
-                let mut encoded_length = binary.context.i32_type().const_int(5, false);
-
-                let array_length = binary.vector_len(arg);
-
-                let elem_ty = ty.array_deref();
-                let llvm_elem_ty = binary.llvm_field_ty(&elem_ty, ns);
-
-                if elem_ty.is_dynamic(ns) {
-                    // if the array contains elements of dynamic length, we have to iterate over all of them
-                    binary.emit_loop_cond_first_with_int(
-                        function,
-                        binary.context.i32_type().const_zero(),
-                        array_length,
-                        &mut encoded_length,
-                        |index, sum| {
-                            let index = binary.builder.build_int_mul(
-                                index,
-                                llvm_elem_ty
-                                    .size_of()
-                                    .unwrap()
-                                    .const_cast(binary.context.i32_type(), false),
-                                "",
-                            );
-
-                            let p = unsafe {
-                                binary.builder.build_gep(
-                                    binary.llvm_type(ty, ns),
-                                    arg.into_pointer_value(),
-                                    &[
-                                        binary.context.i32_type().const_zero(),
-                                        binary.context.i32_type().const_int(2, false),
-                                        index,
-                                    ],
-                                    "index_access",
-                                )
-                            };
-
-                            *sum = binary.builder.build_int_add(
-                                SubstrateTarget::encoded_length(
-                                    p.into(),
-                                    !elem_ty.deref_memory().is_fixed_reference_type(ns),
-                                    packed,
-                                    &elem_ty,
-                                    function,
-                                    binary,
-                                    ns,
-                                ),
-                                *sum,
-                                "",
-                            );
-                        },
-                    );
-
-                    encoded_length
-                } else {
-                    // elements have static length
-                    let elem = binary.default_value(elem_ty.deref_any(), ns);
-
-                    binary.builder.build_int_add(
-                        encoded_length,
-                        binary.builder.build_int_mul(
-                            SubstrateTarget::encoded_length(
-                                elem,
-                                false,
-                                packed,
-                                elem_ty.deref_any(),
-                                function,
-                                binary,
-                                ns,
-                            ),
-                            array_length,
-                            "",
-                        ),
-                        "",
-                    )
-                }
-            }
-            ast::Type::Ref(r) => {
-                SubstrateTarget::encoded_length(arg, load, packed, r, function, binary, ns)
-            }
-            ast::Type::String | ast::Type::DynamicBytes => {
-                let arg = if load {
-                    let load_ty = binary.llvm_type(ty, ns).ptr_type(AddressSpace::default());
-                    binary
-                        .builder
-                        .build_load(load_ty, arg.into_pointer_value(), "")
-                } else {
-                    arg
-                };
-
-                // A string or bytes type has to be encoded by: one compact integer for
-                // the length, followed by the bytes themselves. Here we assume that the
-                // length requires 5 bytes.
-                let len = binary.vector_len(arg);
-
-                if packed {
-                    len
-                } else {
-                    binary.builder.build_int_add(
-                        len,
-                        binary.context.i32_type().const_int(5, false),
-                        "",
-                    )
-                }
-            }
-            ast::Type::ExternalFunction { .. } => {
-                // address + 4 bytes selector
-                binary
-                    .context
-                    .i32_type()
-                    .const_int(ns.address_length as u64 + 4, false)
-            }
-            ast::Type::UserType(user_type) => Self::encoded_length(
-                arg,
-                load,
-                packed,
-                &ns.user_types[*user_type].ty,
-                function,
-                binary,
-                ns,
-            ),
-            _ => unreachable!(),
+    /// Emits the "deploy" function if `init` is `Some`, otherwise emits the "call" function.
+    fn emit_dispatch(&mut self, init: Option<FunctionValue>, bin: &mut Binary, ns: &Namespace) {
+        let ty = bin.context.void_type().fn_type(&[], false);
+        let name = if init.is_some() { "deploy" } else { "call" };
+        let func = bin.module.add_function(name, ty, None);
+        let (input, input_length) = self.public_function_prelude(bin, func);
+        if let Some(initializer) = init {
+            bin.builder.build_call(initializer, &[], "");
         }
+        let func = bin.module.get_function("substrate_dispatch").unwrap();
+        let args = vec![
+            BasicMetadataValueEnum::PointerValue(input),
+            BasicMetadataValueEnum::IntValue(input_length),
+            BasicMetadataValueEnum::IntValue(self.value_transferred(bin, ns)),
+            BasicMetadataValueEnum::PointerValue(bin.selector.as_pointer_value()),
+        ];
+        bin.builder.build_call(func, &args, "substrate_dispatch");
+        bin.builder.build_unreachable();
     }
 }
 

+ 3 - 253
src/emit/substrate/target.rs

@@ -804,253 +804,6 @@ impl<'a> TargetRuntime<'a> for SubstrateTarget {
         binary.builder.build_unreachable();
     }
 
-    fn abi_decode<'b>(
-        &self,
-        binary: &Binary<'b>,
-        function: FunctionValue,
-        args: &mut Vec<BasicValueEnum<'b>>,
-        mut data: PointerValue<'b>,
-        datalength: IntValue<'b>,
-        spec: &[ast::Parameter],
-        ns: &ast::Namespace,
-    ) {
-        let argsend = unsafe {
-            binary
-                .builder
-                .build_gep(binary.context.i8_type(), data, &[datalength], "argsend")
-        };
-
-        for param in spec {
-            args.push(self.decode_ty(binary, function, &param.ty, &mut data, argsend, ns));
-        }
-
-        self.check_overrun(binary, function, data, argsend, true);
-    }
-
-    /// ABI encode into a vector for abi.encode* style builtin functions
-    fn abi_encode_to_vector<'b>(
-        &self,
-        binary: &Binary<'b>,
-        function: FunctionValue<'b>,
-        packed: &[BasicValueEnum<'b>],
-        args: &[BasicValueEnum<'b>],
-        tys: &[ast::Type],
-        ns: &ast::Namespace,
-    ) -> PointerValue<'b> {
-        emit_context!(binary);
-
-        // first calculate how much memory we need to allocate
-        let mut length = i32_zero!();
-
-        debug_assert_eq!(packed.len() + args.len(), tys.len());
-
-        let mut tys_iter = tys.iter();
-
-        // note that encoded_length return the exact value for packed encoding
-        for arg in packed {
-            let ty = tys_iter.next().unwrap();
-
-            length = binary.builder.build_int_add(
-                length,
-                SubstrateTarget::encoded_length(*arg, false, true, ty, function, binary, ns),
-                "",
-            );
-        }
-
-        for arg in args {
-            let ty = tys_iter.next().unwrap();
-
-            length = binary.builder.build_int_add(
-                length,
-                SubstrateTarget::encoded_length(*arg, false, false, ty, function, binary, ns),
-                "",
-            );
-        }
-
-        let malloc_length = binary.builder.build_int_add(
-            length,
-            binary
-                .module
-                .get_struct_type("struct.vector")
-                .unwrap()
-                .size_of()
-                .unwrap()
-                .const_cast(binary.context.i32_type(), false),
-            "size",
-        );
-
-        let p = call!("__malloc", &[malloc_length.into()])
-            .try_as_basic_value()
-            .left()
-            .unwrap()
-            .into_pointer_value();
-
-        // if it's packed, we have the correct length already
-        if args.is_empty() {
-            let data_len = unsafe {
-                binary.builder.build_gep(
-                    binary.module.get_struct_type("struct.vector").unwrap(),
-                    p,
-                    &[i32_zero!(), i32_zero!()],
-                    "data_len",
-                )
-            };
-
-            binary.builder.build_store(data_len, length);
-        }
-
-        let data_size = unsafe {
-            binary.builder.build_gep(
-                binary.module.get_struct_type("struct.vector").unwrap(),
-                p,
-                &[i32_zero!(), i32_const!(1)],
-                "data_size",
-            )
-        };
-
-        binary.builder.build_store(data_size, length);
-
-        let data = unsafe {
-            binary.builder.build_gep(
-                binary.module.get_struct_type("struct.vector").unwrap(),
-                p,
-                &[i32_zero!(), i32_const!(2)],
-                "data",
-            )
-        };
-
-        // now encode each of the arguments
-
-        let mut argsdata = data;
-
-        let mut tys_iter = tys.iter();
-
-        for arg in packed {
-            let ty = tys_iter.next().unwrap();
-
-            self.encode_ty(binary, ns, false, true, function, ty, *arg, &mut argsdata);
-        }
-
-        for arg in args {
-            let ty = tys_iter.next().unwrap();
-
-            self.encode_ty(binary, ns, false, false, function, ty, *arg, &mut argsdata);
-        }
-
-        if !args.is_empty() {
-            let length = binary.builder.build_int_sub(
-                binary
-                    .builder
-                    .build_ptr_to_int(argsdata, binary.context.i32_type(), "end"),
-                binary
-                    .builder
-                    .build_ptr_to_int(data, binary.context.i32_type(), "begin"),
-                "datalength",
-            );
-
-            let data_len = unsafe {
-                binary.builder.build_gep(
-                    binary.module.get_struct_type("struct.vector").unwrap(),
-                    p,
-                    &[i32_zero!(), i32_zero!()],
-                    "data_len",
-                )
-            };
-
-            binary.builder.build_store(data_len, length);
-        }
-
-        p
-    }
-
-    ///  ABI encode the return values for the function
-    fn abi_encode<'b>(
-        &self,
-        binary: &Binary<'b>,
-        selector: Option<IntValue<'b>>,
-        load: bool,
-        function: FunctionValue,
-        args: &[BasicValueEnum<'b>],
-        tys: &[ast::Type],
-        ns: &ast::Namespace,
-    ) -> (PointerValue<'b>, IntValue<'b>) {
-        emit_context!(binary);
-
-        // first calculate how much memory we need to allocate
-        let mut length = i32_zero!();
-
-        // note that encoded_length overestimates how data we need
-        for (i, ty) in tys.iter().enumerate() {
-            length = binary.builder.build_int_add(
-                length,
-                SubstrateTarget::encoded_length(args[i], load, false, ty, function, binary, ns),
-                "",
-            );
-        }
-
-        if let Some(selector) = selector {
-            length = binary.builder.build_int_add(
-                length,
-                selector
-                    .get_type()
-                    .size_of()
-                    .const_cast(binary.context.i32_type(), false),
-                "",
-            );
-        }
-
-        let data = call!("__malloc", &[length.into()])
-            .try_as_basic_value()
-            .left()
-            .unwrap()
-            .into_pointer_value();
-
-        // now encode each of the arguments
-        let mut argsdata = data;
-
-        if let Some(selector) = selector {
-            binary.builder.build_store(data, selector);
-
-            argsdata = unsafe {
-                binary.builder.build_gep(
-                    binary.context.i8_type(),
-                    data,
-                    &[selector
-                        .get_type()
-                        .size_of()
-                        .const_cast(binary.context.i32_type(), false)],
-                    "",
-                )
-            };
-        }
-
-        for (i, ty) in tys.iter().enumerate() {
-            self.encode_ty(
-                binary,
-                ns,
-                load,
-                false,
-                function,
-                ty,
-                args[i],
-                &mut argsdata,
-            );
-        }
-
-        // we cannot use the length returned by encoded_length; calculate actual length
-        let length = binary.builder.build_int_sub(
-            binary
-                .builder
-                .build_ptr_to_int(argsdata, binary.context.i32_type(), "end"),
-            binary
-                .builder
-                .build_ptr_to_int(data, binary.context.i32_type(), "begin"),
-            "datalength",
-        );
-
-        (data, length)
-    }
-
     fn print(&self, binary: &Binary, string_ptr: PointerValue, string_len: IntValue) {
         emit_context!(binary);
 
@@ -1358,13 +1111,10 @@ impl<'a> TargetRuntime<'a> for SubstrateTarget {
     fn return_data<'b>(&self, binary: &Binary<'b>, _function: FunctionValue) -> PointerValue<'b> {
         emit_context!(binary);
 
+        // The `seal_call` syscall leaves the return data in the scratch buffer
         let (scratch_buf, scratch_len) = scratch_buf!();
-
-        let length =
-            binary
-                .builder
-                .build_load(binary.context.i32_type(), scratch_len, "string_len");
-
+        let ty = binary.context.i32_type();
+        let length = binary.builder.build_load(ty, scratch_len, "scratch_len");
         call!(
             "vector_new",
             &[length.into(), i32_const!(1).into(), scratch_buf.into(),]

+ 0 - 1
src/sema/ast.rs

@@ -1434,7 +1434,6 @@ impl CodeLocation for Instr {
             Instr::PushMemory { value, .. } => value.loc(),
             Instr::Constructor { gas, .. } => gas.loc(),
             Instr::ValueTransfer { address, .. } => address.loc(),
-            Instr::AbiDecode { data, .. } => data.loc(),
             Instr::SelfDestruct { recipient } => recipient.loc(),
             Instr::WriteBuffer { buf, .. } => buf.loc(),
             Instr::Print { expr } => expr.loc(),

+ 1 - 1
stdlib/Makefile

@@ -6,7 +6,7 @@ bpf/%.bc wasm/%.bc: %.c
 	$(CC) -c $(CFLAGS) $< -o $@
 
 SOLANA=$(addprefix bpf/,solana.bc bigint.bc format.bc stdlib.bc ripemd160.bc heap.bc)
-WASM=$(addprefix wasm/,ripemd160.bc stdlib.bc substrate.bc bigint.bc format.bc heap.bc)
+WASM=$(addprefix wasm/,ripemd160.bc stdlib.bc bigint.bc format.bc heap.bc)
 
 all: $(SOLANA) $(WASM)
 

+ 0 - 94
stdlib/substrate.c

@@ -1,94 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-#include <stdint.h>
-#include <stddef.h>
-
-#include "stdlib.h"
-
-// 32 bit integer as as scale compact integer
-// https://docs.substrate.io/reference/scale-codec/#fn-1
-uint8_t *compact_encode_u32(uint8_t *dest, uint32_t val)
-{
-    if (val < 64)
-    {
-        *dest++ = val << 2;
-    }
-    else if (val < 0x4000)
-    {
-        *((uint16_t *)dest) = (val << 2) | 1;
-        dest += 2;
-    }
-    else if (val < 0x40000000)
-    {
-        *((uint32_t *)dest) = (val << 2) | 2;
-        dest += 4;
-    }
-    else
-    {
-        *dest++ = 3;
-        *((uint32_t *)dest) = val;
-        dest += 4;
-    }
-
-    return dest;
-}
-
-uint8_t *compact_decode_u32(uint8_t *dest, uint32_t *val)
-{
-    switch (*dest & 3)
-    {
-    case 0:
-        *val = *dest >> 2;
-        dest += 1;
-        break;
-    case 1:
-        *val = *((uint16_t *)dest) >> 2;
-        dest += 2;
-        break;
-    case 2:
-        *val = *((uint32_t *)dest) >> 2;
-        dest += 4;
-        break;
-    default:
-        // sizes of 2**30 (1GB) or larger are not allowed
-        __builtin_unreachable();
-    }
-
-    return dest;
-}
-
-uint8_t *scale_encode_string(uint8_t *dest, uint8_t *data, uint32_t len)
-{
-    uint8_t *data_dst = compact_encode_u32(dest, len);
-
-    while (len--)
-    {
-        *data_dst++ = *data++;
-    }
-
-    return data_dst;
-}
-
-struct vector *scale_decode_string(uint8_t **from)
-{
-    uint8_t *src = *from;
-    uint32_t size_array;
-
-    src = compact_decode_u32(src, &size_array);
-
-    struct vector *v = __malloc(sizeof(*v) + size_array);
-
-    v->len = size_array;
-    v->size = size_array;
-
-    uint8_t *data = v->data;
-
-    while (size_array--)
-    {
-        *data++ = *src++;
-    }
-
-    *from = src;
-
-    return v;
-}

BIN
stdlib/wasm/substrate.bc


+ 6 - 6
tests/codegen_testcases/solidity/constant_folding.sol

@@ -28,12 +28,12 @@ contract Enum {
         lenders[Lender.USDC] = usdc;
         lenders[Lender.DAI] = dai;
 
-        // CHECK: ty:address storage %temp.7 = (arg #0)
-        // CHECK: store storage slot(hex"f31349e4056d5e5c8ce6d8359404f2ca89b2a6884691bff0f55ce7629f869af3") ty:address = %temp.7
-        // CHECK: ty:address storage %temp.8 = (arg #1)
-        // CHECK: store storage slot(hex"e062efc721ea447b5e3918617d57f26130f3d8bc01b883eed1efcb4864d73ac1") ty:address = %temp.8
-        // CHECK: ty:address storage %temp.9 = (arg #2)
-        // CHECK: store storage slot(hex"b2573af2738ebd4810a3198e92bab190f29b8718f1d5ed1b83e468f2bb322d10") ty:address = %temp.9
+        // CHECK: ty:address storage %temp.13 = (arg #0)
+        // CHECK: store storage slot(hex"f31349e4056d5e5c8ce6d8359404f2ca89b2a6884691bff0f55ce7629f869af3") ty:address = %temp.13
+        // CHECK: ty:address storage %temp.14 = (arg #1)
+        // CHECK: store storage slot(hex"e062efc721ea447b5e3918617d57f26130f3d8bc01b883eed1efcb4864d73ac1") ty:address = %temp.14
+        // CHECK: ty:address storage %temp.15 = (arg #2)
+        // CHECK: store storage slot(hex"b2573af2738ebd4810a3198e92bab190f29b8718f1d5ed1b83e468f2bb322d10") ty:address = %temp.15
     }
 
     function foo(Lender lender) public view returns (address) {

+ 3 - 3
tests/codegen_testcases/solidity/dead_storage.sol

@@ -163,9 +163,9 @@ contract foo {
         S[] storage ptrArr = arr;
         ptrArr.push(S({f1: 1}));
         ptrArr.push(S({f1: 2}));
-// CHECK: %.temp.76, %.temp.77 = call foo::foo::function::g
-// CHECK: %temp.78 = load storage slot(%.temp.76) ty:struct foo.S
-// CHECK: %temp.79 = load storage slot(%.temp.77) ty:struct foo.S
+// CHECK: %.temp.115, %.temp.116 = call foo::foo::function::g
+// CHECK: %temp.117 = load storage slot(%.temp.115) ty:struct foo.S
+// CHECK: %temp.118 = load storage slot(%.temp.116) ty:struct foo.S
         return g();
     }
 }

+ 30 - 30
tests/codegen_testcases/solidity/scale.sol

@@ -63,24 +63,24 @@ contract CompactEncoding {
         // CHECK: branchcond (unsigned more (builtin ArrayLength ((arg #0))) > uint32 1073741823), block6, block7
 
         // CHECK: block1: # small
-        // CHECK: ty:uint32 %temp.10 = uint32 1
+        // CHECK: ty:uint32 %temp.19 = uint32 1
         // CHECK: branch block5
 
         // CHECK: block2: # medium
-        // CHECK: ty:uint32 %temp.10 = uint32 2
+        // CHECK: ty:uint32 %temp.19 = uint32 2
         // CHECK: branch block5
 
         // CHECK: block3: # medium_or_big
         // CHECK: branchcond (unsigned more (builtin ArrayLength ((arg #0))) > uint32 16383), block4, block2
 
         // CHECK: block4: # big
-        // CHECK: ty:uint32 %temp.10 = uint32 4
+        // CHECK: ty:uint32 %temp.19 = uint32 4
         // CHECK: branch block5
 
         // CHECK: block5: # done
-        // CHECK: ty:bytes %abi_encoded.temp.11 = (alloc bytes len (%temp.10 + (builtin ArrayLength ((arg #0)))))
-        // CHECK: ty:uint32 %temp.12 = (builtin ArrayLength ((arg #0)))
-        // CHECK: branchcond (unsigned more %temp.12 > uint32 1073741823), block13, block14
+        // CHECK: ty:bytes %abi_encoded.temp.20 = (alloc bytes len (%temp.19 + (builtin ArrayLength ((arg #0)))))
+        // CHECK: ty:uint32 %temp.21 = (builtin ArrayLength ((arg #0)))
+        // CHECK: branchcond (unsigned more %temp.21 > uint32 1073741823), block13, block14
 
         // CHECK: block6: # fail
         // CHECK: assert-failure
@@ -89,29 +89,29 @@ contract CompactEncoding {
         // CHECK: branchcond (unsigned more (builtin ArrayLength ((arg #0))) > uint32 63), block3, block1
 
         // CHECK: block8: # small
-        // CHECK: writebuffer buffer:%abi_encoded.temp.11 offset:uint32 0 value:uint8((%temp.12 * uint32 4))
-        // CHECK: ty:uint32 %temp.13 = uint32 1
+        // CHECK: writebuffer buffer:%abi_encoded.temp.20 offset:uint32 0 value:uint8((%temp.21 * uint32 4))
+        // CHECK: ty:uint32 %temp.22 = uint32 1
         // CHECK: branch block12
 
         // CHECK: block9: # medium
-        // CHECK: writebuffer buffer:%abi_encoded.temp.11 offset:uint32 0 value:uint16(((%temp.12 * uint32 4) | uint32 1))
-        // CHECK: ty:uint32 %temp.13 = uint32 2
+        // CHECK: writebuffer buffer:%abi_encoded.temp.20 offset:uint32 0 value:uint16(((%temp.21 * uint32 4) | uint32 1))
+        // CHECK: ty:uint32 %temp.22 = uint32 2
         // CHECK: branch block12
 
         // CHECK: block10: # medium_or_big
-        // CHECK: branchcond (unsigned more %temp.12 > uint32 16383), block11, block9
+        // CHECK: branchcond (unsigned more %temp.21 > uint32 16383), block11, block9
 
         // CHECK: block11: # big
-        // CHECK: writebuffer buffer:%abi_encoded.temp.11 offset:uint32 0 value:((%temp.12 * uint32 4) | uint32 2)
-        // CHECK: ty:uint32 %temp.13 = uint32 4
+        // CHECK: writebuffer buffer:%abi_encoded.temp.20 offset:uint32 0 value:((%temp.21 * uint32 4) | uint32 2)
+        // CHECK: ty:uint32 %temp.22 = uint32 4
         // CHECK: branch block12
 
         // CHECK: block12: # done
-        // CHECK: memcpy src: (arg #0), dest: (advance ptr: %abi_encoded.temp.11, by: (uint32 0 + %temp.13)), bytes_len: %temp.12
-        // CHECK: ty:bytes %enc = %abi_encoded.temp.11
-        // CHECK: ty:uint32 %temp.14 = (builtin ArrayLength (%enc))
-        // CHECK: ty:uint32 %temp.16 = (zext uint32 (builtin ReadFromBuffer (%enc, uint32 0)))
-        // CHECK: switch (%temp.16 & uint32 3):
+        // CHECK: memcpy src: (arg #0), dest: (advance ptr: %abi_encoded.temp.20, by: (uint32 0 + %temp.22)), bytes_len: %temp.21
+        // CHECK: ty:bytes %enc = %abi_encoded.temp.20
+        // CHECK: ty:uint32 %temp.23 = (builtin ArrayLength (%enc))
+        // CHECK: ty:uint32 %temp.25 = (zext uint32 (builtin ReadFromBuffer (%enc, uint32 0)))
+        // CHECK: switch (%temp.25 & uint32 3):
         // CHECK:         case uint32 0: goto block #15
         // CHECK:         case uint32 1: goto block #16
         // CHECK:         case uint32 2: goto block #17
@@ -121,39 +121,39 @@ contract CompactEncoding {
         // CHECK: assert-failure
 
         // CHECK: block14: # prepare
-        // CHECK: branchcond (unsigned more %temp.12 > uint32 63), block10, block8
+        // CHECK: branchcond (unsigned more %temp.21 > uint32 63), block10, block8
 
         // CHECK: block15: # case_0
-        // CHECK: ty:uint32 %temp.15 = (%temp.16 >> uint32 2)
-        // CHECK: ty:uint32 %temp.16 = uint32 1
+        // CHECK: ty:uint32 %temp.24 = (%temp.25 >> uint32 2)
+        // CHECK: ty:uint32 %temp.25 = uint32 1
         // CHECK: branch block19
 
         // CHECK: block16: # case_1
-        // CHECK: ty:uint32 %temp.15 = ((zext uint32 (builtin ReadFromBuffer (%enc, uint32 0))) >> uint32 2)
-        // CHECK: ty:uint32 %temp.16 = uint32 2
+        // CHECK: ty:uint32 %temp.24 = ((zext uint32 (builtin ReadFromBuffer (%enc, uint32 0))) >> uint32 2)
+        // CHECK: ty:uint32 %temp.25 = uint32 2
         // CHECK: branch block19
 
         // CHECK: block17: # case_2
-        // CHECK: ty:uint32 %temp.15 = ((builtin ReadFromBuffer (%enc, uint32 0)) >> uint32 2)
-        // CHECK: ty:uint32 %temp.16 = uint32 4
+        // CHECK: ty:uint32 %temp.24 = ((builtin ReadFromBuffer (%enc, uint32 0)) >> uint32 2)
+        // CHECK: ty:uint32 %temp.25 = uint32 4
         // CHECK: branch block19
 
         // CHECK: block18: # case_default
         // CHECK: assert-failure
 
         // CHECK: block19: # done
-        // CHECK: branchcond (unsigned (uint32 0 + %temp.16) <= %temp.14), block20, block21
+        // CHECK: branchcond (unsigned (uint32 0 + %temp.25) <= %temp.23), block20, block21
 
         // CHECK: block20: # inbounds
-        // CHECK: branchcond (unsigned (uint32 0 + (%temp.15 + %temp.16)) <= %temp.14), block22, block23
+        // CHECK: branchcond (unsigned (uint32 0 + (%temp.24 + %temp.25)) <= %temp.23), block22, block23
 
         // CHECK: block21: # out_of_bounds
         // CHECK: assert-failure
 
         // CHECK: block22: # inbounds
-        // CHECK: ty:string %temp.17 = (alloc string len %temp.15)
-        // CHECK: memcpy src: (advance ptr: %enc, by: (uint32 0 + %temp.16)), dest: %temp.17, bytes_len: %temp.15
-        // CHECK: branchcond (unsigned less (uint32 0 + (%temp.15 + %temp.16)) < %temp.14), block24, block25
+        // CHECK: ty:string %temp.26 = (alloc string len %temp.24)
+        // CHECK: memcpy src: (advance ptr: %enc, by: (uint32 0 + %temp.25)), dest: %temp.26, bytes_len: %temp.24
+        // CHECK: branchcond (unsigned less (uint32 0 + (%temp.24 + %temp.25)) < %temp.23), block24, block25
 
         // CHECK: block23: # out_of_bounds
         // CHECK: assert-failure

+ 203 - 0
tests/codegen_testcases/solidity/substrate_dispatch.sol

@@ -0,0 +1,203 @@
+// RUN: --target substrate --emit cfg
+
+contract has_fallback_and_receive {
+	// BEGIN-CHECK: Contract: has_fallback_and_receive
+	// CHECK: block0: # entry
+	// CHECK:         ty:uint32 %input_len.temp.1 = (arg #1)
+	// CHECK:         ty:uint128 %value.temp.2 = (arg #2)
+	// CHECK:         ty:buffer_pointer %input_ptr.temp.3 = (arg #0)
+	// CHECK:         branchcond (unsigned less %input_len.temp.1 < uint32 4), block2, block1
+	// CHECK: block1: # start_dispatch
+	// CHECK:         ty:uint32 %selector.temp.4 = (builtin ReadFromBuffer ((arg #0), uint32 0))
+	// CHECK:         store (arg #3), %selector.temp.4
+	// CHECK:         switch %selector.temp.4:
+	// CHECK:                 case uint32 3576764294: goto block #3
+	// CHECK:                 default: goto block #2
+	// CHECK: block2: # fb_or_recv
+	// CHECK:         branchcond (unsigned more %value.temp.2 > uint128 0), block5, block4
+	// CHECK: block3: # func_3_dispatch
+	// CHECK:          = call has_fallback_and_receive::has_fallback_and_receive::constructor::861731d5 
+	// CHECK:         return data (alloc bytes len uint32 0), data length: uint32 0
+	// CHECK: block4: # fallback
+	// CHECK:          = call has_fallback_and_receive::has_fallback_and_receive::fallback 
+	// CHECK:         return data (alloc bytes len uint32 0), data length: uint32 0
+	// CHECK: block5: # receive
+	// CHECK:          = call has_fallback_and_receive::has_fallback_and_receive::receive 
+	// CHECK:         return data (alloc bytes len uint32 0), data length: uint32 0
+	fallback() external {}
+	receive() payable external {}
+}
+
+contract has_fallback {
+	// BEGIN-CHECK: Contract: has_fallback
+	// CHECK: block0: # entry
+	// CHECK:         ty:uint32 %input_len.temp.5 = (arg #1)
+	// CHECK:         ty:uint128 %value.temp.6 = (arg #2)
+	// CHECK:         ty:buffer_pointer %input_ptr.temp.7 = (arg #0)
+	// CHECK:         branchcond (unsigned less %input_len.temp.5 < uint32 4), block2, block1
+	// CHECK: block1: # start_dispatch
+	// CHECK:         ty:uint32 %selector.temp.8 = (builtin ReadFromBuffer ((arg #0), uint32 0))
+	// CHECK:         store (arg #3), %selector.temp.8
+	// CHECK:         switch %selector.temp.8:
+	// CHECK:                 case uint32 3576764294: goto block #3
+	// CHECK:                 default: goto block #2
+	// CHECK: block2: # fb_or_recv
+	// CHECK:         branchcond (unsigned more %value.temp.6 > uint128 0), block5, block4
+	// CHECK: block3: # func_2_dispatch
+	// CHECK:          = call has_fallback::has_fallback::constructor::861731d5 
+	// CHECK:         return data (alloc bytes len uint32 0), data length: uint32 0
+	// CHECK: block4: # fallback
+	// CHECK:          = call has_fallback::has_fallback::fallback 
+	// CHECK:         return data (alloc bytes len uint32 0), data length: uint32 0
+	// CHECK: block5: # receive
+	// CHECK:         return code: function selector invalid
+	fallback() external {}
+}
+
+contract has_receive {
+	// BEGIN-CHECK: Contract: has_receive
+	// CHECK: block0: # entry
+	// CHECK:         ty:uint32 %input_len.temp.9 = (arg #1)
+	// CHECK:         ty:uint128 %value.temp.10 = (arg #2)
+	// CHECK:         ty:buffer_pointer %input_ptr.temp.11 = (arg #0)
+	// CHECK:         branchcond (unsigned less %input_len.temp.9 < uint32 4), block2, block1
+	// CHECK: block1: # start_dispatch
+	// CHECK:         ty:uint32 %selector.temp.12 = (builtin ReadFromBuffer ((arg #0), uint32 0))
+	// CHECK:         store (arg #3), %selector.temp.12
+	// CHECK:         switch %selector.temp.12:
+	// CHECK:                 case uint32 3576764294: goto block #3
+	// CHECK:                 default: goto block #2
+	// CHECK: block2: # fb_or_recv
+	// CHECK:         branchcond (unsigned more %value.temp.10 > uint128 0), block5, block4
+	// CHECK: block3: # func_2_dispatch
+	// CHECK:          = call has_receive::has_receive::constructor::861731d5 
+	// CHECK:         return data (alloc bytes len uint32 0), data length: uint32 0
+	// CHECK: block4: # fallback
+	// CHECK:         return code: function selector invalid
+	// CHECK: block5: # receive
+	// CHECK:          = call has_receive::has_receive::receive 
+	// CHECK:         return data (alloc bytes len uint32 0), data length: uint32 0
+	receive() payable external {}
+}
+
+
+contract is_payable {
+	// BEGIN-CHECK: Contract: is_payable
+	// CHECK: block0: # entry
+	// CHECK:         ty:uint32 %input_len.temp.13 = (arg #1)
+	// CHECK:         ty:uint128 %value.temp.14 = (arg #2)
+	// CHECK:         ty:buffer_pointer %input_ptr.temp.15 = (arg #0)
+	// CHECK:         branchcond (unsigned less %input_len.temp.13 < uint32 4), block2, block1
+	// CHECK: block1: # start_dispatch
+	// CHECK:         ty:uint32 %selector.temp.16 = (builtin ReadFromBuffer ((arg #0), uint32 0))
+	// CHECK:         store (arg #3), %selector.temp.16
+	// CHECK:         switch %selector.temp.16:
+	// CHECK:                 case uint32 2018875586: goto block #3
+	// CHECK:                 case uint32 2114960382: goto block #6
+	// CHECK:                 case uint32 3576764294: goto block #7
+	// CHECK:                 default: goto block #2
+	// CHECK: block2: # fb_or_recv
+	// CHECK:         return code: function selector invalid
+	// CHECK: block3: # func_0_dispatch
+	// CHECK:         branchcond (unsigned more %value.temp.14 > uint128 0), block4, block5
+	// CHECK: block4: # func_0_got_value
+	// CHECK:         assert-failure
+	// CHECK: block5: # func_0_no_value
+	// CHECK:          = call is_payable::is_payable::function::foo 
+	// CHECK:         return data (alloc bytes len uint32 0), data length: uint32 0
+	// CHECK: block6: # func_1_dispatch
+	// CHECK:          = call is_payable::is_payable::function::bar 
+	// CHECK:         return data (alloc bytes len uint32 0), data length: uint32 0
+	// CHECK: block7: # func_3_dispatch
+	// CHECK:          = call is_payable::is_payable::constructor::861731d5 
+	// CHECK:         return data (alloc bytes len uint32 0), data length: uint32 0
+	function foo() public pure {}
+	function bar() public payable { require(msg.value > 0); }
+}
+
+contract overloaded {
+	// BEGIN-CHECK: Contract: overloaded
+	// CHECK: block0: # entry
+	// CHECK:         ty:uint32 %input_len.temp.17 = (arg #1)
+	// CHECK:         ty:uint128 %value.temp.18 = (arg #2)
+	// CHECK:         ty:buffer_pointer %input_ptr.temp.19 = (arg #0)
+	// CHECK:         branchcond (unsigned less %input_len.temp.17 < uint32 4), block2, block1
+	// CHECK: block1: # start_dispatch
+	// CHECK:         ty:uint32 %selector.temp.22 = (builtin ReadFromBuffer ((arg #0), uint32 0))
+	// CHECK:         store (arg #3), %selector.temp.22
+	// CHECK:         switch %selector.temp.22:
+	// CHECK:                 case uint32 2018875586: goto block #3
+	// CHECK:                 case uint32 2114960382: goto block #4
+	// CHECK:                 case uint32 4028568102: goto block #5
+	// CHECK:                 case uint32 2338643635: goto block #6
+	// CHECK:                 default: goto block #2
+	// CHECK: block2: # fb_or_recv
+	// CHECK:         branchcond (unsigned more %value.temp.18 > uint128 0), block14, block13
+	// CHECK: block3: # func_0_dispatch
+	// CHECK:          = call overloaded::overloaded::constructor::c2985578 
+	// CHECK:         return data (alloc bytes len uint32 0), data length: uint32 0
+	// CHECK: block4: # func_1_dispatch
+	// CHECK:          = call overloaded::overloaded::constructor::febb0f7e 
+	// CHECK:         return data (alloc bytes len uint32 0), data length: uint32 0
+	// CHECK: block5: # func_2_dispatch
+	// CHECK:          = call overloaded::overloaded::function::f 
+	// CHECK:         return data (alloc bytes len uint32 0), data length: uint32 0
+	// CHECK: block6: # func_3_dispatch
+	// CHECK:         branchcond (unsigned more %value.temp.18 > uint128 0), block7, block8
+	// CHECK: block7: # func_3_got_value
+	// CHECK:         assert-failure
+	// CHECK: block8: # func_3_no_value
+	// CHECK:         ty:uint32 %temp.20 = (trunc uint32 (%input_len.temp.17 - uint32 4))
+	// CHECK:         branchcond (unsigned (uint32 0 + uint32 32) <= %temp.20), block9, block10
+	// CHECK: block9: # inbounds
+	// CHECK:         ty:uint256 %temp.21 = (builtin ReadFromBuffer ((advance ptr: %input_ptr.temp.19, by: uint32 4), uint32 0))
+	// CHECK:         branchcond (unsigned less (uint32 0 + uint32 32) < %temp.20), block11, block12
+	// CHECK: block10: # out_of_bounds
+	// CHECK:         assert-failure
+	// CHECK: block11: # not_all_bytes_read
+	// CHECK:         assert-failure
+	// CHECK: block12: # buffer_read
+	// CHECK:          = call overloaded::overloaded::function::f__uint256 %temp.21
+	// CHECK:         return data (alloc bytes len uint32 0), data length: uint32 0
+	// CHECK: block13: # fallback
+	// CHECK:          = call overloaded::overloaded::fallback 
+	// CHECK:         return data (alloc bytes len uint32 0), data length: uint32 0
+	// CHECK: block14: # receive
+	// CHECK:          = call overloaded::overloaded::receive 
+	// CHECK:         return data (alloc bytes len uint32 0), data length: uint32 0
+	constructor foo() {}
+	constructor bar() {}
+	function f() public payable {}
+	function f(uint256 i) public pure {}
+	fallback() external {}
+	receive() payable external {}
+}
+
+contract simple {
+	// BEGIN-CHECK: Contract: simple
+	// CHECK: block0: # entry
+	// CHECK:         ty:uint32 %input_len.temp.23 = (arg #1)
+	// CHECK:         ty:uint128 %value.temp.24 = (arg #2)
+	// CHECK:         ty:buffer_pointer %input_ptr.temp.25 = (arg #0)
+	// CHECK:         branchcond (unsigned less %input_len.temp.23 < uint32 4), block2, block1
+	// CHECK: block1: # start_dispatch
+	// CHECK:         ty:uint32 %selector.temp.26 = (builtin ReadFromBuffer ((arg #0), uint32 0))
+	// CHECK:         store (arg #3), %selector.temp.26
+	// CHECK:         switch %selector.temp.26:
+	// CHECK:                 case uint32 2018875586: goto block #3
+	// CHECK:                 case uint32 3576764294: goto block #6
+	// CHECK:                 default: goto block #2
+	// CHECK: block2: # fb_or_recv
+	// CHECK:         return code: function selector invalid
+	// CHECK: block3: # func_0_dispatch
+	// CHECK:         branchcond (unsigned more %value.temp.24 > uint128 0), block4, block5
+	// CHECK: block4: # func_0_got_value
+	// CHECK:         assert-failure
+	// CHECK: block5: # func_0_no_value
+	// CHECK:          = call simple::simple::function::foo 
+	// CHECK:         return data (alloc bytes len uint32 0), data length: uint32 0
+	// CHECK: block6: # func_2_dispatch
+	// CHECK:          = call simple::simple::constructor::861731d5 
+	// CHECK:         return data (alloc bytes len uint32 0), data length: uint32 0
+	function foo() public pure {}
+}

+ 5 - 5
tests/codegen_testcases/solidity/unused_variable_elimination.sol

@@ -66,7 +66,7 @@ contract c {
         x = 102 + t*y/(t+5*y) + g + test3() - vec.push(2) + ct.sum(1, 2);
 		return 2;
 // CHECK: push array ty:int32[] value:int32 2
-// CHECK: _ = external call::regular address:%ct payload:%abi_encoded.temp.75 value:uint128 0 gas:uint64 0 accounts: seeds:
+// CHECK: _ = external call::regular address:%ct payload:%abi_encoded.temp.89 value:uint128 0 gas:uint64 0 accounts: seeds:
 }
 
 }
@@ -77,13 +77,13 @@ contract c3 {
         c2 ct = new c2();
 
         return 3;
-// CHECK: constructor salt: value: gas:uint64 0 address: seeds: c2 (encoded buffer: %abi_encoded.temp.80, buffer len: uint32 4)
+// CHECK: constructor salt: value: gas:uint64 0 address: seeds: c2 (encoded buffer: %abi_encoded.temp.108, buffer len: uint32 4)
     }
 
 // BEGIN-CHECK: c3::function::test7
     function test7() public returns (int32) {
         c2 ct = new c2();
-// constructor salt: value: gas:uint64 0 address: seeds: c2 (encoded buffer: %abi_encoded.temp.82, buffer len: uint32 4)
+// constructor salt: value: gas:uint64 0 address: seeds: c2 (encoded buffer: %abi_encoded.temp.110, buffer len: uint32 4)
         address ad = address(ct);
         (bool p, ) = ad.call(hex'ba');
 // CHECK: external call::regular address:%ad payload:(alloc bytes uint32 1 hex"ba") value:uint128 0 gas:uint64 0
@@ -136,7 +136,7 @@ return 3;
         int f = 4;
 
         int c = 32 +4 *(f = it1+it2);
-// CHECK: ty:int256 %c = (int256 32 + (sext int256 (int64 4 * (trunc int64 (%temp.89 + %temp.90)))))
+// CHECK: ty:int256 %c = (int256 32 + (sext int256 (int64 4 * (trunc int64 (%temp.117 + %temp.118)))))
 // NOT-CHECK: ty:int256 %f = (%temp.10 + %temp.11)
         return c;
     }
@@ -177,7 +177,7 @@ return 3;
     function test14() public returns (int) {
         int[] storage ptrArr = testArr;
 
-// CHECK: store storage slot(%temp.109) ty:int256 storage = int256 3
+// CHECK: store storage slot(%temp.137) ty:int256 storage = int256 3
         ptrArr.push(3);
 
         return ptrArr[0];