Jelajahi Sumber

Implement function dispatch in codegen for Solana (#1029)

Signed-off-by: Lucas Steuernagel <lucas.tnagel@gmail.com>

Signed-off-by: Lucas Steuernagel <lucas.tnagel@gmail.com>
Lucas Steuernagel 3 tahun lalu
induk
melakukan
7a9a6dc074

+ 1 - 0
Cargo.toml

@@ -53,6 +53,7 @@ rust-lapper = "1.0"
 bitflags = "1.3"
 anchor-syn = { version = "0.25", features = ["idl"] }
 convert_case = "0.6"
+parse-display = "0.6.0"
 
 [dev-dependencies]
 num-derive = "0.3"

+ 113 - 74
src/codegen/cfg.rs

@@ -11,14 +11,15 @@ use super::{
 use crate::codegen::subexpression_elimination::common_sub_expression_elimination;
 use crate::codegen::{undefined_variable, Expression, LLVMName};
 use crate::sema::ast::{
-    CallTy, Contract, Function, FunctionAttributes, Namespace, Parameter, RetrieveType,
-    StringLocation, StructType, Type,
+    CallTy, Contract, FunctionAttributes, Namespace, Parameter, RetrieveType, StringLocation,
+    StructType, Type,
 };
 use crate::sema::{contracts::collect_base_args, diagnostics::Diagnostics, Recurse};
 use crate::{sema::ast, Target};
 use indexmap::IndexMap;
 use num_bigint::BigInt;
 use num_traits::One;
+use parse_display::Display;
 use solang_parser::pt;
 use solang_parser::pt::CodeLocation;
 use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
@@ -26,6 +27,7 @@ use std::ops::AddAssign;
 use std::str;
 use std::sync::Arc;
 use std::{fmt, fmt::Write};
+
 // IndexMap <ArrayVariable res , res of temp variable>
 pub type ArrayLengthVars = IndexMap<usize, usize>;
 
@@ -144,6 +146,7 @@ pub enum Instr {
         exception_block: Option<usize>,
         tys: Vec<Parameter>,
         data: Expression,
+        data_len: Option<Expression>,
     },
     /// Insert unreachable instruction after e.g. self-destruct
     Unreachable,
@@ -176,6 +179,24 @@ pub enum Instr {
     },
     /// Do nothing
     Nop,
+    /// Return AbiEncoded data via an environment system call
+    ReturnData {
+        data: Expression,
+        data_len: Expression,
+    },
+    /// Return a code at the end of a function
+    ReturnCode { code: ReturnCode },
+}
+
+/// This struct defined the return codes that we send to the execution environment when we return
+/// from a function.
+#[derive(PartialEq, Eq, Hash, Clone, Debug, Display)]
+#[display(style = "title case")]
+pub enum ReturnCode {
+    Success,
+    FunctionSelectorInvalid,
+    AbiEncodingInvalid,
+    InvalidDataError,
 }
 
 impl Instr {
@@ -209,6 +230,10 @@ impl Instr {
             | Instr::Store {
                 dest: item_1,
                 data: item_2,
+            }
+            | Instr::ReturnData {
+                data: item_1,
+                data_len: item_2,
             } => {
                 item_1.recurse(cx, f);
                 item_2.recurse(cx, f);
@@ -316,6 +341,7 @@ impl Instr {
             Instr::AssertFailure { expr: None }
             | Instr::Unreachable
             | Instr::Nop
+            | Instr::ReturnCode { .. }
             | Instr::Branch { .. }
             | Instr::PopMemory { .. } => {}
         }
@@ -1070,26 +1096,37 @@ impl ControlFlowGraph {
                 selector,
                 exception_block: exception,
                 data,
-            } => 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{:08x} ", s))
-                    .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(", "),
-            ),
+                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{:08x} ", s))
+                        .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 {}, {}",
@@ -1196,6 +1233,18 @@ impl ControlFlowGraph {
                 description.push_str(format!("\n\t\tdefault: goto block #{}", default).as_str());
                 description
             }
+
+            Instr::ReturnData { data, data_len } => {
+                format!(
+                    "return data {}, data length: {}",
+                    self.expr_to_string(contract, ns, data),
+                    self.expr_to_string(contract, ns, data_len)
+                )
+            }
+
+            Instr::ReturnCode { code } => {
+                format!("return code: {}", code)
+            }
         }
     }
 
@@ -1308,48 +1357,36 @@ pub fn generate_cfg(
 
     let mut cfg = function_cfg(contract_no, function_no, ns, opt);
 
-    let default_constructor = &ns.default_constructor(contract_no);
-    let func = match function_no {
-        Some(function_no) => &ns.functions[function_no],
-        None => default_constructor,
-    };
-
-    // if the function is a modifier, generate the modifier chain
-    if !func.modifiers.is_empty() {
-        // only function can have modifiers
-        assert_eq!(func.ty, pt::FunctionTy::Function);
-        let public = cfg.public;
-        let nonpayable = cfg.nonpayable;
-
-        cfg.public = false;
-
-        for (chain_no, call) in func.modifiers.iter().enumerate().rev() {
-            let modifier_cfg_no = all_cfgs.len();
-
-            all_cfgs.push(cfg);
-
-            let (modifier_no, args) = resolve_modifier_call(call, &ns.contracts[contract_no]);
-
-            let modifier = &ns.functions[modifier_no];
-
-            let (new_cfg, next_id) = generate_modifier_dispatch(
-                contract_no,
-                func,
-                modifier,
-                modifier_cfg_no,
-                chain_no,
-                args,
-                ns,
-                opt,
-            );
+    if let Some(func_no) = function_no {
+        let func = &ns.functions[func_no];
+        // if the function has any modifiers, generate the modifier chain
+        if !func.modifiers.is_empty() {
+            // only function can have modifiers
+            assert_eq!(func.ty, pt::FunctionTy::Function);
+            let public = cfg.public;
+            let nonpayable = cfg.nonpayable;
+
+            cfg.public = false;
+
+            for chain_no in (0..func.modifiers.len()).rev() {
+                let modifier_cfg_no = all_cfgs.len();
+
+                all_cfgs.push(cfg);
+
+                cfg = generate_modifier_dispatch(
+                    contract_no,
+                    func_no,
+                    modifier_cfg_no,
+                    chain_no,
+                    ns,
+                    opt,
+                );
+            }
 
-            cfg = new_cfg;
-            ns.next_id = next_id;
+            cfg.public = public;
+            cfg.nonpayable = nonpayable;
+            cfg.selector = ns.functions[func_no].selector();
         }
-
-        cfg.public = public;
-        cfg.nonpayable = nonpayable;
-        cfg.selector = func.selector();
     }
 
     optimize_and_check_cfg(
@@ -1672,9 +1709,7 @@ fn function_cfg(
         );
     }
 
-    let (vars, next_id) = vartab.drain();
-    cfg.vars = vars;
-    ns.next_id = next_id;
+    vartab.finalize(ns, &mut cfg);
 
     // walk cfg to check for use for before initialize
     cfg
@@ -1725,16 +1760,20 @@ pub(crate) fn populate_named_returns<T: FunctionAttributes>(
 }
 
 /// Generate the CFG for a modifier on a function
-pub fn generate_modifier_dispatch(
+fn generate_modifier_dispatch(
     contract_no: usize,
-    func: &Function,
-    modifier: &Function,
+    func_no: usize,
     cfg_no: usize,
     chain_no: usize,
-    args: &[ast::Expression],
-    ns: &Namespace,
+    ns: &mut Namespace,
     opt: &Options,
-) -> (ControlFlowGraph, usize) {
+) -> ControlFlowGraph {
+    let (modifier_no, args) = resolve_modifier_call(
+        &ns.functions[func_no].modifiers[chain_no],
+        &ns.contracts[contract_no],
+    );
+    let func = &ns.functions[func_no];
+    let modifier = &ns.functions[modifier_no];
     let name = format!(
         "{}::{}::{}::modifier{}::{}",
         &ns.contracts[contract_no].name,
@@ -1859,10 +1898,10 @@ pub fn generate_modifier_dispatch(
             },
         );
     }
-    let (vars, next_id) = vartab.drain();
-    cfg.vars = vars;
 
-    (cfg, next_id)
+    vartab.finalize(ns, &mut cfg);
+
+    cfg
 }
 
 impl Contract {

+ 13 - 0
src/codegen/constant_folding.rs

@@ -260,8 +260,12 @@ pub fn constant_folding(cfg: &mut ControlFlowGraph, ns: &mut Namespace) {
                     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].1 = Instr::AbiDecode {
                         res: res.clone(),
@@ -269,6 +273,7 @@ pub fn constant_folding(cfg: &mut ControlFlowGraph, ns: &mut Namespace) {
                         exception_block: *exception_block,
                         tys: tys.clone(),
                         data,
+                        data_len,
                     }
                 }
                 Instr::SelfDestruct { recipient } => {
@@ -331,6 +336,14 @@ pub fn constant_folding(cfg: &mut ControlFlowGraph, ns: &mut Namespace) {
                         default: *default,
                     };
                 }
+                Instr::ReturnData { data, data_len } => {
+                    let data = expression(data, Some(&vars), cfg, ns);
+                    let data_len = expression(data_len, Some(&vars), cfg, ns);
+                    cfg.blocks[block_no].instr[instr_no].1 = Instr::ReturnData {
+                        data: data.0,
+                        data_len: data_len.0,
+                    };
+                }
                 _ => (),
             }
 

+ 360 - 0
src/codegen/dispatch.rs

@@ -0,0 +1,360 @@
+// SPDX-License-Identifier: Apache-2.0
+
+use crate::codegen::cfg::{ASTFunction, ControlFlowGraph, Instr, InternalCallTy, ReturnCode};
+use crate::codegen::vartable::Vartable;
+use crate::codegen::{Builtin, Expression};
+use crate::sema::ast::{Namespace, Parameter, RetrieveType, Type};
+use num_bigint::{BigInt, Sign};
+use num_traits::Zero;
+use solang_parser::pt;
+use solang_parser::pt::{FunctionTy, Loc};
+use std::sync::Arc;
+
+/// Create the dispatch for the Solana target
+pub(super) fn function_dispatch(
+    contract_no: usize,
+    all_cfg: &[ControlFlowGraph],
+    ns: &mut Namespace,
+) -> ControlFlowGraph {
+    let mut vartab = Vartable::new(ns.next_id);
+    let mut cfg = ControlFlowGraph::new(
+        format!("dispatch_{}", ns.contracts[contract_no].name),
+        ASTFunction::None,
+    );
+
+    cfg.params = Arc::new(vec![
+        Parameter {
+            loc: Loc::Codegen,
+            id: None,
+            ty: Type::BufferPointer,
+            ty_loc: None,
+            indexed: false,
+            readonly: false,
+            recursive: false,
+        },
+        Parameter {
+            loc: Loc::Codegen,
+            id: None,
+            ty: Type::Uint(64),
+            ty_loc: None,
+            indexed: false,
+            readonly: false,
+            recursive: false,
+        },
+    ]);
+
+    let switch_block = cfg.new_basic_block("switch".to_string());
+    let no_function_matched = cfg.new_basic_block("no_function_matched".to_string());
+
+    let not_fallback = Expression::MoreEqual(
+        Loc::Codegen,
+        Box::new(Expression::FunctionArg(Loc::Codegen, Type::Uint(64), 1)),
+        Box::new(Expression::NumberLiteral(
+            Loc::Codegen,
+            Type::Uint(64),
+            BigInt::from(4u8),
+        )),
+    );
+
+    cfg.add(
+        &mut vartab,
+        Instr::BranchCond {
+            cond: not_fallback,
+            true_block: switch_block,
+            false_block: no_function_matched,
+        },
+    );
+    cfg.set_basic_block(switch_block);
+    let argsdata = Expression::FunctionArg(Loc::Codegen, Type::BufferPointer, 0);
+    let argslen = Expression::FunctionArg(Loc::Codegen, Type::Uint(64), 1);
+    let fid = Expression::Builtin(
+        Loc::Codegen,
+        vec![Type::Uint(32)],
+        Builtin::ReadFromBuffer,
+        vec![
+            argsdata.clone(),
+            Expression::NumberLiteral(Loc::Codegen, Type::Uint(32), BigInt::zero()),
+        ],
+    );
+
+    let argsdata = Expression::AdvancePointer {
+        pointer: Box::new(argsdata),
+        bytes_offset: Box::new(Expression::NumberLiteral(
+            Loc::Codegen,
+            Type::Uint(32),
+            BigInt::from(4u8),
+        )),
+    };
+    let argslen = Expression::Subtract(
+        Loc::Codegen,
+        Type::Uint(64),
+        false,
+        Box::new(argslen),
+        Box::new(Expression::NumberLiteral(
+            Loc::Codegen,
+            Type::Uint(64),
+            BigInt::from(4u8),
+        )),
+    );
+
+    let mut cases = Vec::new();
+    for (cfg_no, func_cfg) in all_cfg.iter().enumerate() {
+        if func_cfg.ty != pt::FunctionTy::Function || !func_cfg.public {
+            continue;
+        }
+
+        add_dispatch_case(
+            cfg_no,
+            func_cfg,
+            &argsdata,
+            argslen.clone(),
+            &mut cases,
+            &mut vartab,
+            &mut cfg,
+        );
+    }
+
+    cfg.set_basic_block(switch_block);
+
+    cfg.add(
+        &mut vartab,
+        Instr::Switch {
+            cond: fid,
+            cases,
+            default: no_function_matched,
+        },
+    );
+
+    cfg.set_basic_block(no_function_matched);
+
+    let fallback = all_cfg
+        .iter()
+        .enumerate()
+        .find(|(_, cfg)| cfg.public && cfg.ty == pt::FunctionTy::Fallback);
+
+    let receive = all_cfg
+        .iter()
+        .enumerate()
+        .find(|(_, cfg)| cfg.public && cfg.ty == pt::FunctionTy::Receive);
+
+    if fallback.is_none() && receive.is_none() {
+        cfg.add(
+            &mut vartab,
+            Instr::ReturnCode {
+                code: ReturnCode::FunctionSelectorInvalid,
+            },
+        );
+
+        vartab.finalize(ns, &mut cfg);
+
+        return cfg;
+    }
+
+    match fallback {
+        Some((cfg_no, _)) => {
+            cfg.add(
+                &mut vartab,
+                Instr::Call {
+                    res: vec![],
+                    return_tys: vec![],
+                    args: vec![],
+                    call: InternalCallTy::Static { cfg_no },
+                },
+            );
+
+            cfg.add(
+                &mut vartab,
+                Instr::ReturnCode {
+                    code: ReturnCode::Success,
+                },
+            );
+        }
+        None => {
+            cfg.add(
+                &mut vartab,
+                Instr::ReturnCode {
+                    code: ReturnCode::InvalidDataError,
+                },
+            );
+        }
+    }
+
+    vartab.finalize(ns, &mut cfg);
+
+    cfg
+}
+
+/// Add the dispatch for function given a matched selector
+fn add_dispatch_case(
+    cfg_no: usize,
+    func_cfg: &ControlFlowGraph,
+    argsdata: &Expression,
+    argslen: Expression,
+    cases: &mut Vec<(Expression, usize)>,
+    vartab: &mut Vartable,
+    cfg: &mut ControlFlowGraph,
+) {
+    let bb = cfg.new_basic_block(format!("function_cfg_{}", cfg_no));
+    cfg.set_basic_block(bb);
+
+    let truncated_len = Expression::Trunc(Loc::Codegen, Type::Uint(32), Box::new(argslen));
+
+    let mut vars: Vec<usize> = Vec::with_capacity(func_cfg.params.len());
+    let mut decoded: Vec<Expression> = Vec::with_capacity(func_cfg.params.len());
+    for item in func_cfg.params.iter() {
+        let new_var = vartab.temp_anonymous(&item.ty);
+        vars.push(new_var);
+        decoded.push(Expression::Variable(Loc::Codegen, item.ty.clone(), new_var));
+    }
+    cfg.add(
+        vartab,
+        Instr::AbiDecode {
+            res: vars,
+            selector: None,
+            exception_block: None,
+            tys: (*func_cfg.params).clone(),
+            data: argsdata.clone(),
+            data_len: Some(truncated_len),
+        },
+    );
+
+    let mut returns: Vec<usize> = Vec::with_capacity(func_cfg.returns.len());
+    let mut return_tys: Vec<Type> = Vec::with_capacity(func_cfg.returns.len());
+    let mut returns_expr: Vec<Expression> = Vec::with_capacity(func_cfg.returns.len());
+    for item in func_cfg.returns.iter() {
+        let new_var = vartab.temp_anonymous(&item.ty);
+        returns.push(new_var);
+        return_tys.push(item.ty.clone());
+        returns_expr.push(Expression::Variable(Loc::Codegen, item.ty.clone(), new_var));
+    }
+
+    cfg.add(
+        vartab,
+        Instr::Call {
+            res: returns,
+            call: InternalCallTy::Static { cfg_no },
+            args: decoded,
+            return_tys,
+        },
+    );
+
+    if !func_cfg.returns.is_empty() {
+        let data = Expression::AbiEncode {
+            loc: Loc::Codegen,
+            tys: returns_expr.iter().map(|e| e.ty()).collect::<Vec<Type>>(),
+            packed: vec![],
+            args: returns_expr,
+        };
+        let data_len = Expression::Builtin(
+            Loc::Codegen,
+            vec![Type::Uint(32)],
+            Builtin::ArrayLength,
+            vec![data.clone()],
+        );
+        let zext_len = Expression::ZeroExt(Loc::Codegen, Type::Uint(64), Box::new(data_len));
+        cfg.add(
+            vartab,
+            Instr::ReturnData {
+                data,
+                data_len: zext_len,
+            },
+        );
+    }
+
+    cfg.add(vartab, Instr::Return { value: vec![] });
+
+    cases.push((
+        Expression::NumberLiteral(
+            Loc::Codegen,
+            Type::Uint(32),
+            BigInt::from_bytes_le(Sign::Plus, &*func_cfg.selector),
+        ),
+        bb,
+    ));
+}
+
+/// Create the dispatch for a contract constructor. This case needs creates a new function in
+/// the CFG because we want to use de abi decoding implementation from codegen.
+pub(super) fn constructor_dispatch(
+    constructor_cfg_no: usize,
+    all_cfg: &[ControlFlowGraph],
+    ns: &mut Namespace,
+) -> ControlFlowGraph {
+    let mut vartab = Vartable::new(ns.next_id);
+    let mut func_name = format!("constructor_dispatch_{}", all_cfg[constructor_cfg_no].name);
+    for params in all_cfg[constructor_cfg_no].params.iter() {
+        func_name.push_str(format!("_{}", params.ty.to_string(ns)).as_str());
+    }
+    let mut cfg = ControlFlowGraph::new(func_name, ASTFunction::None);
+    cfg.ty = FunctionTy::Function;
+    cfg.public = all_cfg[constructor_cfg_no].public;
+
+    cfg.params = Arc::new(vec![
+        Parameter {
+            loc: Loc::Codegen,
+            id: None,
+            ty: Type::BufferPointer,
+            ty_loc: None,
+            indexed: false,
+            readonly: false,
+            recursive: false,
+        },
+        Parameter {
+            loc: Loc::Codegen,
+            id: None,
+            ty: Type::Uint(64),
+            ty_loc: None,
+            indexed: false,
+            readonly: false,
+            recursive: false,
+        },
+    ]);
+
+    let mut res: Vec<usize> = Vec::with_capacity(all_cfg[constructor_cfg_no].params.len());
+    let mut returns: Vec<Expression> = Vec::with_capacity(all_cfg[constructor_cfg_no].params.len());
+    for item in all_cfg[constructor_cfg_no].params.iter() {
+        let new_var = vartab.temp_anonymous(&item.ty);
+        res.push(new_var);
+        returns.push(Expression::Variable(Loc::Codegen, item.ty.clone(), new_var));
+    }
+
+    let data = Expression::FunctionArg(Loc::Codegen, Type::BufferPointer, 0);
+    let data_len = Expression::FunctionArg(Loc::Codegen, Type::Uint(64), 1);
+
+    if !res.is_empty() {
+        cfg.add(
+            &mut vartab,
+            Instr::AbiDecode {
+                res,
+                selector: None,
+                exception_block: None,
+                tys: (*all_cfg[constructor_cfg_no].params).clone(),
+                data,
+                data_len: Some(data_len),
+            },
+        );
+    }
+
+    cfg.add(
+        &mut vartab,
+        Instr::Call {
+            res: vec![],
+            return_tys: vec![],
+            call: InternalCallTy::Static {
+                cfg_no: constructor_cfg_no,
+            },
+            args: returns,
+        },
+    );
+
+    cfg.add(
+        &mut vartab,
+        Instr::ReturnCode {
+            code: ReturnCode::Success,
+        },
+    );
+
+    vartab.finalize(ns, &mut cfg);
+
+    cfg
+}

+ 25 - 13
src/codegen/encoding/borsh_encoding.rs

@@ -84,21 +84,33 @@ impl AbiEncoding for BorshEncoding {
         ns: &Namespace,
         vartab: &mut Vartable,
         cfg: &mut ControlFlowGraph,
+        buffer_size_expr: Option<Expression>,
     ) -> Vec<Expression> {
         let buffer_size = vartab.temp_anonymous(&Type::Uint(32));
-        cfg.add(
-            vartab,
-            Instr::Set {
-                loc: Loc::Codegen,
-                res: buffer_size,
-                expr: Expression::Builtin(
-                    Loc::Codegen,
-                    vec![Type::Uint(32)],
-                    Builtin::ArrayLength,
-                    vec![buffer.clone()],
-                ),
-            },
-        );
+        if let Some(length_expression) = buffer_size_expr {
+            cfg.add(
+                vartab,
+                Instr::Set {
+                    loc: Loc::Codegen,
+                    res: buffer_size,
+                    expr: length_expression,
+                },
+            );
+        } else {
+            cfg.add(
+                vartab,
+                Instr::Set {
+                    loc: Loc::Codegen,
+                    res: buffer_size,
+                    expr: Expression::Builtin(
+                        Loc::Codegen,
+                        vec![Type::Uint(32)],
+                        Builtin::ArrayLength,
+                        vec![buffer.clone()],
+                    ),
+                },
+            );
+        }
 
         let mut validator = BufferValidator::new(buffer_size, types);
 

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

@@ -37,6 +37,7 @@ pub(super) trait AbiEncoding {
         ns: &Namespace,
         vartab: &mut Vartable,
         cfg: &mut ControlFlowGraph,
+        buffer_size: Option<Expression>,
     ) -> Vec<Expression>;
 
     /// Cache items loaded from storage to reuse them later, so we avoid the expensive operation

+ 5 - 1
src/codegen/expression.rs

@@ -2495,6 +2495,7 @@ pub fn emit_function_call(
                             ns,
                             vartab,
                             cfg,
+                            None,
                         );
                     } else {
                         returns = Vec::new();
@@ -2518,6 +2519,7 @@ pub fn emit_function_call(
                                 exception_block: None,
                                 tys: (*dest_func.returns).clone(),
                                 data: Expression::ReturnData(*loc),
+                                data_len: None,
                             },
                         );
                     }
@@ -2636,6 +2638,7 @@ pub fn emit_function_call(
                             exception_block: None,
                             tys,
                             data: Expression::ReturnData(*loc),
+                            data_len: None,
                         },
                     );
 
@@ -2650,7 +2653,7 @@ pub fn emit_function_call(
         ast::Expression::Builtin(loc, tys, ast::Builtin::AbiBorshDecode, args) => {
             let data = expression(&args[0], cfg, callee_contract_no, func, ns, vartab, opt);
             let encoder = create_encoder(ns);
-            encoder.abi_decode(loc, &data, tys, ns, vartab, cfg)
+            encoder.abi_decode(loc, &data, tys, ns, vartab, cfg, None)
         }
         ast::Expression::Builtin(loc, tys, ast::Builtin::AbiDecode, args) => {
             let data = expression(&args[0], cfg, callee_contract_no, func, ns, vartab, opt);
@@ -2683,6 +2686,7 @@ pub fn emit_function_call(
                         })
                         .collect(),
                     data,
+                    data_len: None,
                 },
             );
 

+ 21 - 4
src/codegen/mod.rs

@@ -4,6 +4,7 @@ mod array_boundary;
 pub mod cfg;
 mod constant_folding;
 mod dead_storage;
+mod dispatch;
 mod encoding;
 mod events;
 mod expression;
@@ -34,13 +35,14 @@ use crate::{sema::ast, Target};
 use std::cmp::Ordering;
 
 use crate::codegen::cfg::ASTFunction;
+use crate::codegen::dispatch::{constructor_dispatch, function_dispatch};
 use crate::codegen::yul::generate_yul_function_cfg;
 use crate::sema::Recurse;
 use num_bigint::{BigInt, Sign};
 use num_rational::BigRational;
 use num_traits::{FromPrimitive, Zero};
 use solang_parser::pt;
-use solang_parser::pt::CodeLocation;
+use solang_parser::pt::{CodeLocation, FunctionTy};
 
 // The sizeof(struct account_data_header)
 pub const SOLANA_FIRST_OFFSET: u64 = 16;
@@ -237,6 +239,23 @@ 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);
+            ns.contracts[contract_no].dispatch_no = all_cfg.len();
+            all_cfg.push(dispatch_cfg);
+
+            for cfg_no in 0..all_cfg.len() {
+                if all_cfg[cfg_no].ty == FunctionTy::Constructor && all_cfg[cfg_no].public {
+                    let dispatch_cfg = constructor_dispatch(cfg_no, &all_cfg, ns);
+                    ns.contracts[contract_no].constructor_dispatch = Some(all_cfg.len());
+                    all_cfg.push(dispatch_cfg);
+                    break;
+                }
+            }
+        }
+
         ns.contracts[contract_no].cfg = all_cfg;
     }
 }
@@ -277,9 +296,7 @@ fn storage_initializer(contract_no: usize, ns: &mut Namespace, opt: &Options) ->
 
     cfg.add(&mut vartab, Instr::Return { value: Vec::new() });
 
-    let (vars, next_id) = vartab.drain();
-    cfg.vars = vars;
-    ns.next_id = next_id;
+    vartab.finalize(ns, &mut cfg);
 
     optimize_and_check_cfg(&mut cfg, ns, ASTFunction::None, opt);
 

+ 2 - 0
src/codegen/statements.rs

@@ -1106,6 +1106,7 @@ fn try_catch(
                             exception_block: None,
                             tys,
                             data: Expression::ReturnData(pt::Loc::Codegen),
+                            data_len: None,
                         },
                     );
                 }
@@ -1225,6 +1226,7 @@ fn try_catch(
                 res: vec![error_var],
                 tys: vec![error_param.clone()],
                 data: Expression::ReturnData(pt::Loc::Codegen),
+                data_len: None,
             },
         );
 

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

@@ -51,6 +51,10 @@ impl AvailableExpressionSet {
                 storage: item_2,
                 ..
             }
+            | Instr::ReturnData {
+                data: item_1,
+                data_len: item_2,
+            }
             | Instr::Store {
                 dest: item_1,
                 data: item_2,
@@ -174,6 +178,7 @@ impl AvailableExpressionSet {
             Instr::AssertFailure { expr: None }
             | Instr::Unreachable
             | Instr::Nop
+            | Instr::ReturnCode { .. }
             | Instr::Branch { .. }
             | Instr::PopMemory { .. } => {}
         }
@@ -390,12 +395,16 @@ impl AvailableExpressionSet {
                 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 {
@@ -451,6 +460,11 @@ impl AvailableExpressionSet {
                 value: self.regenerate_expression(value, ave, cst).1,
             },
 
+            Instr::ReturnData { data, data_len } => Instr::ReturnData {
+                data: self.regenerate_expression(data, ave, cst).1,
+                data_len: self.regenerate_expression(data_len, ave, cst).1,
+            },
+
             _ => instr.clone(),
         }
     }

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

@@ -206,6 +206,7 @@ fn invalid() {
         exception_block: None,
         tys: vec![],
         data: exp.clone(),
+        data_len: None,
     };
 
     let mut ave = AvailableExpression::default();

+ 5 - 2
src/codegen/vartable.rs

@@ -1,5 +1,7 @@
 // SPDX-License-Identifier: Apache-2.0
 
+use crate::codegen::cfg::ControlFlowGraph;
+use crate::sema::ast::Namespace;
 use crate::sema::{ast::Type, symtable::Symtable};
 use indexmap::IndexMap;
 use num_bigint::BigInt;
@@ -150,8 +152,9 @@ impl Vartable {
         var_no
     }
 
-    pub fn drain(self) -> (Vars, usize) {
-        (self.vars, self.next_id)
+    pub fn finalize(self, ns: &mut Namespace, cfg: &mut ControlFlowGraph) {
+        ns.next_id = self.next_id;
+        cfg.vars = self.vars;
     }
 
     // In order to create phi nodes, we need to track what vars are set in a certain scope

+ 2 - 0
src/codegen/vector_to_slice.rs

@@ -99,6 +99,7 @@ fn find_writable_vectors(
             // These instructions are fine with vectors
             Instr::Set { .. }
             | Instr::Nop
+            | Instr::ReturnCode { .. }
             | Instr::Branch { .. }
             | Instr::BranchCond { .. }
             | Instr::Switch { .. }
@@ -117,6 +118,7 @@ fn find_writable_vectors(
             | Instr::Unreachable
             | Instr::Print { .. }
             | Instr::AssertFailure { .. }
+            | Instr::ReturnData { .. }
             | Instr::ValueTransfer { .. } => {
                 apply_transfers(&block.transfers[instr_no], vars, writable);
             }

+ 1 - 4
src/codegen/yul/mod.rs

@@ -116,9 +116,6 @@ fn yul_function_cfg(
         cfg.add_yul(&mut vartab, returns);
     }
 
-    let (vars, next_id) = vartab.drain();
-    cfg.vars = vars;
-    ns.next_id = next_id;
-
+    vartab.finalize(ns, &mut cfg);
     cfg
 }

+ 4 - 0
src/codegen/yul/tests/expression.rs

@@ -136,6 +136,8 @@ fn contract_constant_variable() {
         cfg: vec![],
         code: vec![],
         instantiable: true,
+        dispatch_no: 0,
+        constructor_dispatch: None,
     };
     ns.contracts.push(contract);
 
@@ -255,6 +257,8 @@ fn slot_suffix() {
         cfg: vec![],
         code: vec![],
         instantiable: true,
+        dispatch_no: 0,
+        constructor_dispatch: None,
     };
     ns.contracts.push(contract);
 

+ 12 - 1
src/emit/binary.rs

@@ -10,8 +10,9 @@ use num_bigint::BigInt;
 use num_traits::ToPrimitive;
 use std::collections::HashMap;
 
+use crate::codegen::cfg::ReturnCode;
 use crate::emit::substrate;
-use crate::emit::{solana, BinaryOp, Generate, ReturnCode};
+use crate::emit::{solana, BinaryOp, Generate};
 use crate::linker::link;
 use crate::Target;
 use inkwell::builder::Builder;
@@ -832,6 +833,11 @@ impl<'a> Binary<'a> {
 
                     BasicTypeEnum::ArrayType(aty)
                 }
+                Type::Struct(StructType::SolParameters) => self
+                    .module
+                    .get_struct_type("struct.SolParameters")
+                    .unwrap()
+                    .as_basic_type_enum(),
                 Type::Struct(str_ty) => self
                     .context
                     .struct_type(
@@ -881,6 +887,11 @@ impl<'a> Binary<'a> {
                     ),
                 ),
                 Type::UserType(no) => self.llvm_type(&ns.user_types[*no].ty, ns),
+                Type::BufferPointer => self
+                    .context
+                    .i8_type()
+                    .ptr_type(AddressSpace::Generic)
+                    .as_basic_type_enum(),
                 _ => unreachable!(),
             }
         }

+ 0 - 329
src/emit/dispatch.rs

@@ -1,329 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-use crate::codegen::cfg::ControlFlowGraph;
-use crate::emit::binary::Binary;
-use crate::emit::functions::abort_if_value_transfer;
-use crate::emit::{ReturnCode, 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;
-
-/// 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, T: TargetRuntime<'a> + ?Sized>(
-    target: &T,
-    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(argsdata, "function_selector")
-        .into_int_value();
-
-    if ns.target != Target::Solana {
-        // 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(
-            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;
-        }
-
-        add_dispatch_case(
-            target,
-            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
-        target.return_code(bin, bin.return_values[&ReturnCode::FunctionSelectorInvalid]);
-
-        return;
-    }
-
-    if ns.target == Target::Solana {
-        match fallback {
-            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, "");
-
-                target.return_empty_abi(bin);
-            }
-            None => {
-                target.return_code(bin, bin.context.i32_type().const_int(2, false));
-            }
-        }
-    } else {
-        let got_value = if bin.function_abort_value_transfers {
-            bin.context.bool_type().const_zero()
-        } else {
-            let value = target.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 = if ns.target == Target::Solana {
-                    vec![function.get_last_param().unwrap().into()]
-                } else {
-                    vec![]
-                };
-
-                bin.builder.build_call(functions[&cfg_no], &args, "");
-
-                target.return_empty_abi(bin);
-            }
-            None => {
-                target.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, "");
-
-                target.return_empty_abi(bin);
-            }
-            None => {
-                target.return_code(bin, bin.context.i32_type().const_int(2, false));
-            }
-        }
-    }
-}
-
-///Add single case for emit_function_dispatch
-fn add_dispatch_case<'a, F, T: TargetRuntime<'a> + ?Sized>(
-    target: &T,
-    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);
-
-    if nonpayable(f) {
-        abort_if_value_transfer(target, bin, function, ns);
-    }
-
-    let mut args = Vec::new();
-
-    // insert abi decode
-    target.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::Generic),
-                    v.name_as_str(),
-                )
-                .into()
-            });
-        }
-    }
-
-    if ns.target == Target::Solana {
-        let params_ty = dest
-            .get_type()
-            .get_param_types()
-            .last()
-            .unwrap()
-            .into_pointer_type();
-
-        args.push(
-            bin.builder
-                .build_pointer_cast(
-                    function.get_last_param().unwrap().into_pointer_value(),
-                    params_ty,
-                    "",
-                )
-                .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
-        target.return_empty_abi(bin);
-    } else {
-        let tys: Vec<Type> = f.returns.iter().map(|p| p.ty.clone()).collect();
-
-        let (data, length) = target.abi_encode(
-            bin,
-            None,
-            true,
-            function,
-            &args[f.params.len()..f.params.len() + f.returns.len()],
-            &tys,
-            ns,
-        );
-
-        target.return_abi(bin, data, length);
-    }
-
-    bin.builder.position_at_end(bail_block);
-
-    target.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,
-    ));
-}

+ 2 - 1
src/emit/ethabiencoder.rs

@@ -1,5 +1,6 @@
 // SPDX-License-Identifier: Apache-2.0
 
+use crate::codegen::cfg::ReturnCode;
 use crate::{sema::ast, Target};
 use inkwell::types::BasicType;
 use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue};
@@ -8,7 +9,7 @@ use inkwell::IntPredicate;
 use num_traits::ToPrimitive;
 
 use super::loop_builder::LoopBuilder;
-use super::{Binary, ReturnCode};
+use super::Binary;
 
 /// Generate an in-place abi encoder. This is done in several stages:
 /// 1) EncoderBuilder::new() generates the code which calculates the required encoded length at runtime

+ 7 - 3
src/emit/expression.rs

@@ -1,11 +1,11 @@
 // SPDX-License-Identifier: Apache-2.0
 
-use crate::codegen::cfg::HashTy;
+use crate::codegen::cfg::{HashTy, ReturnCode};
 use crate::codegen::{Builtin, Expression};
 use crate::emit::binary::Binary;
 use crate::emit::math::{build_binary_op_with_overflow_check, multiply, power};
 use crate::emit::strings::{format_string, string_location};
-use crate::emit::{BinaryOp, Generate, ReturnCode, TargetRuntime, Variable};
+use crate::emit::{BinaryOp, Generate, TargetRuntime, Variable};
 use crate::sema::ast::{Namespace, RetrieveType, StructType, Type};
 use crate::Target;
 use inkwell::module::Linkage;
@@ -1340,7 +1340,11 @@ pub(super) fn expression<'a, T: TargetRuntime<'a> + ?Sized>(
             let v = expression(target, bin, &args[0], vartab, function, ns);
             let offset = expression(target, bin, &args[1], vartab, function, ns).into_int_value();
 
-            let data = bin.vector_bytes(v);
+            let data = if args[0].ty().is_dynamic_memory() {
+                bin.vector_bytes(v)
+            } else {
+                v.into_pointer_value()
+            };
 
             let start = unsafe { bin.builder.build_gep(data, &[offset], "start") };
 

+ 30 - 35
src/emit/functions.rs

@@ -4,7 +4,6 @@ use crate::emit::binary::Binary;
 use crate::emit::cfg::emit_cfg;
 use crate::emit::TargetRuntime;
 use crate::sema::ast::{Contract, Namespace, Type};
-use crate::Target;
 use inkwell::module::Linkage;
 use inkwell::values::FunctionValue;
 use inkwell::{AddressSpace, IntPredicate};
@@ -78,41 +77,37 @@ pub(super) fn abort_if_value_transfer<'a, T: TargetRuntime<'a> + ?Sized>(
     function: FunctionValue,
     ns: &Namespace,
 ) {
-    if ns.target != Target::Solana {
-        let value = target.value_transferred(binary, ns);
+    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 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
+    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.assert_failure(
+        binary,
+        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.assert_failure(
-            binary,
-            binary
-                .context
-                .i8_type()
-                .ptr_type(AddressSpace::Generic)
-                .const_null(),
-            binary.context.i32_type().const_zero(),
-        );
-
-        binary.builder.position_at_end(not_value_transfer);
-    }
+            .i8_type()
+            .ptr_type(AddressSpace::Generic)
+            .const_null(),
+        binary.context.i32_type().const_zero(),
+    );
+
+    binary.builder.position_at_end(not_value_transfer);
 }

+ 28 - 4
src/emit/instructions.rs

@@ -1,11 +1,11 @@
 // SPDX-License-Identifier: Apache-2.0
 
-use crate::codegen::cfg::{ControlFlowGraph, Instr, InternalCallTy};
+use crate::codegen::cfg::{ControlFlowGraph, Instr, InternalCallTy, ReturnCode};
 use crate::codegen::Expression;
 use crate::emit::binary::Binary;
 use crate::emit::cfg::{create_block, BasicBlock, Work};
 use crate::emit::expression::expression;
-use crate::emit::{ReturnCode, TargetRuntime};
+use crate::emit::TargetRuntime;
 use crate::sema::ast::{Contract, Namespace, RetrieveType, Type};
 use crate::Target;
 use inkwell::types::BasicType;
@@ -930,12 +930,21 @@ pub(super) fn process_instruction<'a, T: TargetRuntime<'a> + ?Sized>(
             exception_block: exception,
             tys,
             data,
+            data_len,
         } => {
             let v = expression(target, bin, data, &w.vars, function, ns);
 
-            let mut data = bin.vector_bytes(v);
+            let mut data = if data.ty().is_reference_type(ns) {
+                bin.vector_bytes(v)
+            } else {
+                v.into_pointer_value()
+            };
 
-            let mut data_len = bin.vector_len(v);
+            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();
@@ -1169,6 +1178,21 @@ pub(super) fn process_instruction<'a, T: TargetRuntime<'a> + ?Sized>(
             bin.builder
                 .build_switch(cond.into_int_value(), default_bb, cases.as_ref());
         }
+
+        Instr::ReturnData { data, data_len } => {
+            let data = if data.ty().is_reference_type(ns) {
+                bin.vector_bytes(expression(target, bin, data, &w.vars, function, ns))
+            } else {
+                expression(target, bin, data, &w.vars, function, ns).into_pointer_value()
+            };
+
+            let data_len = expression(target, bin, data_len, &w.vars, function, ns);
+            target.return_abi_data(bin, data, data_len);
+        }
+
+        Instr::ReturnCode { code } => {
+            target.return_code(bin, bin.return_values[code]);
+        }
     }
 }
 

+ 7 - 7
src/emit/mod.rs

@@ -15,7 +15,6 @@ use inkwell::values::{
 
 pub mod binary;
 mod cfg;
-mod dispatch;
 mod ethabiencoder;
 mod expression;
 mod functions;
@@ -342,13 +341,14 @@ pub trait TargetRuntime<'a> {
         topic_tys: &[Type],
         ns: &Namespace,
     );
-}
 
-#[derive(PartialEq, Eq, Hash)]
-pub(crate) enum ReturnCode {
-    Success,
-    FunctionSelectorInvalid,
-    AbiEncodingInvalid,
+    /// Return ABI encoded data
+    fn return_abi_data<'b>(
+        &self,
+        binary: &Binary<'b>,
+        data: PointerValue<'b>,
+        data_len: BasicValueEnum<'b>,
+    );
 }
 
 #[derive(PartialEq, Eq)]

+ 20 - 110
src/emit/solana/mod.rs

@@ -4,25 +4,22 @@ pub(super) mod target;
 
 use crate::sema::ast;
 use crate::Target;
-use solang_parser::pt;
 use std::collections::HashMap;
 use std::str;
 
+use crate::codegen::cfg::ReturnCode;
 use crate::sema::ast::Type;
 use inkwell::module::{Linkage, Module};
 use inkwell::types::BasicType;
-use inkwell::values::{
-    BasicMetadataValueEnum, BasicValueEnum, FunctionValue, IntValue, PointerValue, UnnamedAddress,
-};
+use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, UnnamedAddress};
 use inkwell::{context::Context, types::BasicTypeEnum};
 use inkwell::{AddressSpace, IntPredicate, OptimizationLevel};
 use num_traits::ToPrimitive;
 
-use crate::emit::dispatch::emit_function_dispatch;
 use crate::emit::ethabiencoder;
-use crate::emit::functions::{abort_if_value_transfer, emit_functions, emit_initializer};
+use crate::emit::functions::{emit_functions, emit_initializer};
 use crate::emit::loop_builder::LoopBuilder;
-use crate::emit::{Binary, ReturnCode, TargetRuntime};
+use crate::emit::{Binary, TargetRuntime};
 
 pub struct SolanaTarget {
     abi: ethabiencoder::EthAbiDecoder,
@@ -32,9 +29,8 @@ pub struct SolanaTarget {
 pub struct Contract<'a> {
     magic: u32,
     contract: &'a ast::Contract,
-    ns: &'a ast::Namespace,
     storage_initializer: FunctionValue<'a>,
-    constructor: Option<(FunctionValue<'a>, &'a Vec<ast::Parameter>)>,
+    constructor: Option<FunctionValue<'a>>,
     functions: HashMap<usize, FunctionValue<'a>>,
 }
 
@@ -78,6 +74,10 @@ impl SolanaTarget {
             ReturnCode::AbiEncodingInvalid,
             context.i64_type().const_int(2u64 << 32, false),
         );
+        binary.return_values.insert(
+            ReturnCode::InvalidDataError,
+            context.i32_type().const_int(2, false),
+        );
         // externals
         target.declare_externals(&mut binary, ns);
 
@@ -86,11 +86,8 @@ impl SolanaTarget {
         let storage_initializer = emit_initializer(&mut target, &mut binary, contract, ns);
 
         let constructor = contract
-            .cfg
-            .iter()
-            .enumerate()
-            .find(|(_, cfg)| cfg.ty == pt::FunctionTy::Constructor && cfg.public)
-            .map(|(cfg_no, cfg)| (binary.functions[&cfg_no], &*cfg.params));
+            .constructor_dispatch
+            .map(|cfg_no| binary.functions[&cfg_no]);
 
         let mut functions = HashMap::new();
 
@@ -101,7 +98,6 @@ impl SolanaTarget {
             &[Contract {
                 magic: target.magic,
                 contract,
-                ns,
                 storage_initializer,
                 constructor,
                 functions,
@@ -172,11 +168,8 @@ impl SolanaTarget {
                 let storage_initializer = emit_initializer(&mut target, &mut binary, contract, ns);
 
                 let constructor = contract
-                    .cfg
-                    .iter()
-                    .enumerate()
-                    .find(|(_, cfg)| cfg.ty == pt::FunctionTy::Constructor && cfg.public)
-                    .map(|(cfg_no, cfg)| (binary.functions[&cfg_no], &*cfg.params));
+                    .constructor_dispatch
+                    .map(|cfg_no| binary.functions[&cfg_no]);
 
                 let mut functions = HashMap::new();
 
@@ -184,7 +177,6 @@ impl SolanaTarget {
 
                 contracts.push(Contract {
                     magic: target.magic,
-                    ns,
                     contract,
                     storage_initializer,
                     constructor,
@@ -532,50 +524,8 @@ impl SolanaTarget {
 
         let mut cases = vec![(binary.context.i32_type().const_zero(), constructor_block)];
 
-        let input = binary.builder.build_pointer_cast(
-            input,
-            binary.context.i32_type().ptr_type(AddressSpace::Generic),
-            "input_ptr32",
-        );
-
-        let dispatch_function_ty = binary.context.i64_type().fn_type(
-            &[
-                input.get_type().into(),
-                input_len.get_type().into(),
-                sol_params.get_type().into(),
-            ],
-            false,
-        );
-
         for contract in contracts {
-            let dispatch_function = binary.module.add_function(
-                &format!("dispatch_{}", contract.contract.name),
-                dispatch_function_ty,
-                None,
-            );
-
-            let entry = binary
-                .context
-                .append_basic_block(dispatch_function, "entry");
-
-            binary.builder.position_at_end(entry);
-
-            emit_function_dispatch(
-                self,
-                binary,
-                contract.contract,
-                contract.ns,
-                pt::FunctionTy::Function,
-                dispatch_function
-                    .get_nth_param(0)
-                    .unwrap()
-                    .into_pointer_value(),
-                dispatch_function.get_nth_param(1).unwrap().into_int_value(),
-                dispatch_function,
-                &contract.functions,
-                None,
-                |_| false,
-            );
+            let dispatch_function = contract.functions[&contract.contract.dispatch_no];
 
             let function_block = binary
                 .context
@@ -614,12 +564,6 @@ impl SolanaTarget {
 
         binary.builder.position_at_end(function_block);
 
-        let input = binary.builder.build_pointer_cast(
-            input,
-            binary.context.i32_type().ptr_type(AddressSpace::Generic),
-            "input_ptr32",
-        );
-
         binary
             .builder
             .build_switch(magic_value, badmagic_block, &cases);
@@ -712,49 +656,15 @@ impl SolanaTarget {
                 "",
             );
 
-            // is there a not a payable constructor
-            if !contract.contract.functions.iter().any(|function_no| {
-                let f = &contract.ns.functions[*function_no];
-                f.is_constructor() && f.is_payable()
-            }) {
-                abort_if_value_transfer(self, binary, function, contract.ns);
-            }
-
             // There is only one possible constructor
-            let ret = if let Some((constructor_function, params)) = contract.constructor {
-                let mut args = Vec::new();
-
-                // insert abi decode
-                self.abi.decode(
-                    binary,
-                    function,
-                    &mut args,
-                    input,
-                    input_len,
-                    params,
-                    contract.ns,
-                );
-
-                let params_ty = constructor_function
-                    .get_type()
-                    .get_param_types()
-                    .last()
-                    .unwrap()
-                    .into_pointer_type();
-
-                args.push(
-                    binary
-                        .builder
-                        .build_pointer_cast(sol_params, params_ty, "")
-                        .into(),
-                );
-
-                let args: Vec<BasicMetadataValueEnum> =
-                    args.iter().map(|arg| (*arg).into()).collect();
-
+            let ret = if let Some(constructor_function) = contract.constructor {
                 binary
                     .builder
-                    .build_call(constructor_function, &args, "")
+                    .build_call(
+                        constructor_function,
+                        &[input.into(), input_len.into(), sol_params.into()],
+                        "constructor_dispatch_call",
+                    )
                     .try_as_basic_value()
                     .left()
                     .unwrap()

+ 18 - 1
src/emit/solana/target.rs

@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: Apache-2.0
 
 use crate::codegen;
-use crate::codegen::cfg::HashTy;
+use crate::codegen::cfg::{HashTy, ReturnCode};
 use crate::emit::binary::Binary;
 use crate::emit::expression::expression;
 use crate::emit::loop_builder::LoopBuilder;
@@ -2480,4 +2480,21 @@ impl<'a> TargetRuntime<'a> for SolanaTarget {
 
         binary.builder.build_load(temp, "hash").into_int_value()
     }
+
+    fn return_abi_data<'b>(
+        &self,
+        binary: &Binary<'b>,
+        data: PointerValue<'b>,
+        data_len: BasicValueEnum<'b>,
+    ) {
+        binary.builder.build_call(
+            binary.module.get_function("sol_set_return_data").unwrap(),
+            &[data.into(), data_len.into()],
+            "",
+        );
+
+        binary
+            .builder
+            .build_return(Some(&binary.return_values[&ReturnCode::Success]));
+    }
 }

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

@@ -0,0 +1,285 @@
+// 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(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(
+                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);
+
+        if nonpayable(f) {
+            abort_if_value_transfer(self, bin, function, ns);
+        }
+
+        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::Generic),
+                        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,
+        ));
+    }
+}

+ 3 - 5
src/emit/substrate/mod.rs

@@ -12,10 +12,10 @@ use num_traits::ToPrimitive;
 use solang_parser::pt;
 use std::collections::HashMap;
 
-use crate::emit::dispatch::emit_function_dispatch;
 use crate::emit::functions::{abort_if_value_transfer, emit_functions, emit_initializer};
 use crate::emit::{Binary, TargetRuntime};
 
+mod dispatch;
 mod storage;
 pub(super) mod target;
 
@@ -379,8 +379,7 @@ impl SubstrateTarget {
 
         let fallback_block = binary.context.append_basic_block(function, "fallback");
 
-        emit_function_dispatch(
-            self,
+        self.emit_function_dispatch(
             binary,
             contract,
             ns,
@@ -422,8 +421,7 @@ impl SubstrateTarget {
             ns,
         );
 
-        emit_function_dispatch(
-            self,
+        self.emit_function_dispatch(
             binary,
             contract,
             ns,

+ 19 - 1
src/emit/substrate/target.rs

@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: Apache-2.0
 
-use crate::codegen::cfg::HashTy;
+use crate::codegen::cfg::{HashTy, ReturnCode};
 use crate::emit::binary::Binary;
 use crate::emit::expression::expression;
 use crate::emit::storage::StorageSlot;
@@ -703,6 +703,24 @@ impl<'a> TargetRuntime<'a> for SubstrateTarget {
         binary.builder.build_unreachable();
     }
 
+    fn return_abi_data<'b>(
+        &self,
+        binary: &Binary<'b>,
+        data: PointerValue<'b>,
+        data_len: BasicValueEnum<'b>,
+    ) {
+        emit_context!(binary);
+
+        call!(
+            "seal_return",
+            &[i32_zero!().into(), data.into(), data_len.into()]
+        );
+
+        binary
+            .builder
+            .build_return(Some(&binary.return_values[&ReturnCode::Success]));
+    }
+
     fn assert_failure<'b>(&self, binary: &'b Binary, _data: PointerValue, _length: IntValue) {
         // insert "unreachable" instruction; not that build_unreachable() tells the compiler
         // that this code path is not reachable and may be discarded.

+ 10 - 3
src/sema/ast.rs

@@ -120,6 +120,7 @@ pub enum StructType {
     AccountInfo,
     AccountMeta,
     ExternalFunction,
+    SolParameters,
 }
 
 #[derive(PartialEq, Eq, Clone, Debug)]
@@ -600,16 +601,20 @@ pub struct Contract {
     pub virtual_functions: HashMap<String, usize>,
     pub yul_functions: Vec<usize>,
     pub variables: Vec<Variable>,
-    // List of contracts this contract instantiates
+    /// List of contracts this contract instantiates
     pub creates: Vec<usize>,
-    // List of events this contract produces
+    /// List of events this contract produces
     pub sends_events: Vec<usize>,
     pub initializer: Option<usize>,
     pub default_constructor: Option<(Function, usize)>,
     pub cfg: Vec<ControlFlowGraph>,
     pub code: Vec<u8>,
-    // Can the contract be instantiated, i.e. not abstract, no errors, etc.
+    /// Can the contract be instantiated, i.e. not abstract, no errors, etc.
     pub instantiable: bool,
+    /// CFG number of this contract's dispatch function
+    pub dispatch_no: usize,
+    /// CFG number of this contract's constructor dispatch
+    pub constructor_dispatch: Option<usize>,
 }
 
 impl Contract {
@@ -1068,8 +1073,10 @@ impl CodeLocation for Instr {
                 _ => destination.loc(),
             },
             Instr::Switch { cond, .. } => cond.loc(),
+            Instr::ReturnData { data, .. } => data.loc(),
             Instr::Branch { .. }
             | Instr::Unreachable
+            | Instr::ReturnCode { .. }
             | Instr::Nop
             | Instr::AssertFailure { .. }
             | Instr::PopMemory { .. } => pt::Loc::Codegen,

+ 1 - 0
src/sema/builtin_structs.rs

@@ -194,6 +194,7 @@ impl StructType {
             StructType::AccountInfo => &BUILTIN_STRUCTS[0],
             StructType::AccountMeta => &BUILTIN_STRUCTS[1],
             StructType::ExternalFunction => &BUILTIN_STRUCTS[2],
+            StructType::SolParameters => unreachable!("SolParameters is defined in a solana.c"),
         }
     }
 }

+ 2 - 0
src/sema/contracts.rs

@@ -47,6 +47,8 @@ impl ast::Contract {
             cfg: Vec::new(),
             code: Vec::new(),
             instantiable,
+            dispatch_no: 0,
+            constructor_dispatch: None,
         }
     }