瀏覽代碼

Add WIP substrate target emitting

Signed-off-by: Sean Young <sean@mess.org>
Sean Young 6 年之前
父節點
當前提交
4b416da6dc
共有 7 個文件被更改,包括 764 次插入417 次删除
  1. 335 9
      src/emit/burrow.rs
  2. 132 398
      src/emit/mod.rs
  3. 286 0
      src/emit/substrate.rs
  4. 1 1
      src/main.rs
  5. 5 5
      src/resolver/functions.rs
  6. 4 3
      src/resolver/mod.rs
  7. 1 1
      src/test.rs

+ 335 - 9
src/emit/burrow.rs

@@ -1,10 +1,14 @@
 
+use parser::ast;
 use resolver;
+use std::str;
 
 use inkwell::types::BasicTypeEnum;
 use inkwell::context::Context;
 use inkwell::module::Linkage;
 use inkwell::AddressSpace;
+use inkwell::values::{PointerValue, IntValue, FunctionValue, BasicValueEnum};
+use inkwell::IntPredicate;
 
 use super::{TargetRuntime, Contract};
 
@@ -22,7 +26,7 @@ impl BurrowTarget {
         c.emit_functions(&b);
     
         b.emit_constructor_dispatch(&c);
-        c.emit_function_dispatch(contract);
+        b.emit_function_dispatch(&c); 
 
         c
     }
@@ -36,11 +40,9 @@ impl BurrowTarget {
         ];
 
         let ftype = ret.fn_type(&args, false);
-        let func = contract.module.add_function("get_storage32", ftype, Some(Linkage::External));
-        contract.externals.insert("get_storage32".to_owned(), func);
-
-        let func = contract.module.add_function("set_storage32", ftype, Some(Linkage::External));
-        contract.externals.insert("set_storage32".to_owned(), func);
+        
+        contract.module.add_function("get_storage32", ftype, Some(Linkage::External));
+        contract.module.add_function("set_storage32", ftype, Some(Linkage::External));
     }
 
     fn emit_constructor_dispatch(&self, contract: &Contract) {
@@ -73,7 +75,8 @@ impl BurrowTarget {
             };
 
             // insert abi decode
-            contract.emit_abi_decode(
+            self.abi_decode(
+                contract,
                 function,
                 &mut args,
                 args_ptr,
@@ -86,12 +89,172 @@ impl BurrowTarget {
 
         contract.builder.build_return(None);
     }
+
+    fn emit_function_dispatch(&self, contract: &Contract) {
+        // create start function
+        let ret = contract.context.i32_type().ptr_type(AddressSpace::Generic);
+        let ftype = ret.fn_type(&[contract.context.i32_type().ptr_type(AddressSpace::Generic).into()], false);
+        let function = contract.module.add_function("function", ftype, None);
+
+        let entry = contract.context.append_basic_block(function, "entry");
+        let fallback_block = contract.context.append_basic_block(function, "fallback");
+
+        contract.builder.position_at_end(&entry);
+
+        let data = function.get_first_param().unwrap().into_pointer_value();
+        let argslen = contract.builder.build_load(data, "length").into_int_value();
+
+        let argsdata = unsafe {
+            contract.builder.build_gep(
+                data,
+                &[contract.context.i32_type().const_int(1, false).into()],
+                "argsdata")
+        };
+
+        contract.emit_function_dispatch(&contract.ns.functions, argsdata, argslen, function, &fallback_block, self);
+
+        // emit fallback code
+        contract.builder.position_at_end(&fallback_block);
+
+        match contract.ns.fallback_function() {
+            Some(f) => {
+                contract.builder.build_call(
+                    contract.functions[f].value_ref,
+                    &[],
+                    "");
+
+                contract.builder.build_return(None);
+            }
+            None => {
+                contract.builder.build_unreachable();
+            },
+        }
+    }
+
+    fn emit_abi_encode_single_val(
+        &self,
+        contract: &Contract,
+        ty: &ast::ElementaryTypeName,
+        dest: PointerValue,
+        val: IntValue,
+    ) {
+        match ty {
+            ast::ElementaryTypeName::Bool => {
+                // first clear
+                let dest8 = contract.builder.build_pointer_cast(dest,
+                    contract.context.i8_type().ptr_type(AddressSpace::Generic),
+                    "destvoid");
+
+                contract.builder.build_call(
+                    contract.module.get_function("__bzero8").unwrap(),
+                    &[ dest8.into(),
+                       contract.context.i32_type().const_int(4, false).into() ],
+                    "");
+
+                let value = contract.builder.build_select(val,
+                    contract.context.i8_type().const_int(1, false),
+                    contract.context.i8_type().const_zero(),
+                    "bool_val");
+
+                let dest = unsafe {
+                    contract.builder.build_gep(
+                        dest8,
+                        &[ contract.context.i32_type().const_int(31, false).into() ],
+                        "")
+                };
+
+                contract.builder.build_store(dest, value);
+            }
+            ast::ElementaryTypeName::Int(8) | ast::ElementaryTypeName::Uint(8) => {
+                let signval = if let ast::ElementaryTypeName::Int(8) = ty {
+                    let negative = contract.builder.build_int_compare(IntPredicate::SLT,
+                            val, contract.context.i8_type().const_zero(), "neg");
+
+                            contract.builder.build_select(negative,
+                        contract.context.i64_type().const_zero(),
+                        contract.context.i64_type().const_int(std::u64::MAX, true),
+                        "val").into_int_value()
+                } else {
+                    contract.context.i64_type().const_zero()
+                };
+
+                let dest8 = contract.builder.build_pointer_cast(dest,
+                    contract.context.i8_type().ptr_type(AddressSpace::Generic),
+                    "destvoid");
+
+                    contract.builder.build_call(
+                    contract.module.get_function("__memset8").unwrap(),
+                    &[ dest8.into(), signval.into(),
+                       contract.context.i32_type().const_int(4, false).into() ],
+                    "");
+
+                let dest = unsafe {
+                    contract.builder.build_gep(
+                        dest8,
+                        &[ contract.context.i32_type().const_int(31, false).into() ],
+                        "")
+                };
+
+                contract.builder.build_store(dest, val);
+            }
+            ast::ElementaryTypeName::Uint(n) | ast::ElementaryTypeName::Int(n) => {
+                // first clear/set the upper bits
+                if *n < 256 {
+                    let signval = if let ast::ElementaryTypeName::Int(8) = ty {
+                        let negative = contract.builder.build_int_compare(IntPredicate::SLT,
+                                val, contract.context.i8_type().const_zero(), "neg");
+
+                        contract.builder.build_select(negative,
+                            contract.context.i64_type().const_zero(),
+                            contract.context.i64_type().const_int(std::u64::MAX, true),
+                            "val").into_int_value()
+                    } else {
+                        contract.context.i64_type().const_zero()
+                    };
+
+                    let dest8 = contract.builder.build_pointer_cast(dest,
+                        contract.context.i8_type().ptr_type(AddressSpace::Generic),
+                        "destvoid");
+
+                    contract.builder.build_call(
+                        contract.module.get_function("__memset8").unwrap(),
+                        &[ dest8.into(), signval.into(),
+                            contract.context.i32_type().const_int(4, false).into() ],
+                        "");
+                }
+
+                // no need to allocate space for each uint64
+                // allocate enough for type
+                let int_type = contract.context.custom_width_int_type(*n as u32);
+                let type_size = int_type.size_of();
+
+                let store = contract.builder.build_alloca(int_type, "stack");
+
+                contract.builder.build_store(store, val);
+
+                contract.builder.build_call(
+                    contract.module.get_function("__leNtobe32").unwrap(),
+                    &[ contract.builder.build_pointer_cast(store,
+                            contract.context.i8_type().ptr_type(AddressSpace::Generic),
+                            "destvoid").into(),
+                        contract.builder.build_pointer_cast(dest,
+                            contract.context.i8_type().ptr_type(AddressSpace::Generic),
+                            "destvoid").into(),
+                        contract.builder.build_int_truncate(type_size,
+                            contract.context.i32_type(), "").into()
+                    ],
+                    "");
+            }
+            _ => unimplemented!(),
+        }
+    }
+
 }
 
 impl TargetRuntime for BurrowTarget {
     fn set_storage<'a>(&self, contract: &'a Contract, slot: u32, dest: inkwell::values::PointerValue<'a>) {
         contract.builder.build_call(
-            contract.externals["set_storage32"],
+            contract.module.get_function("set_storage32").unwrap(),
             &[
                 contract.context.i32_type().const_int(slot as u64, false).into(),
                 contract.builder.build_pointer_cast(dest,
@@ -104,7 +267,7 @@ impl TargetRuntime for BurrowTarget {
 
     fn get_storage<'a>(&self, contract: &'a Contract, slot: u32, dest: inkwell::values::PointerValue<'a>) {
         contract.builder.build_call(
-            contract.externals["get_storage32"],
+            contract.module.get_function("get_storage32").unwrap(),
             &[
                 contract.context.i32_type().const_int(slot as u64, false).into(),
                 contract.builder.build_pointer_cast(dest,
@@ -114,4 +277,167 @@ impl TargetRuntime for BurrowTarget {
             ],
             "");
     }
+
+    fn return_empty_abi(&self, contract: &Contract) {
+        let dest = contract.builder.build_call(
+            contract.module.get_function("__malloc").unwrap(),
+            &[contract.context.i32_type().const_int(4, false).into()],
+            ""
+        ).try_as_basic_value().left().unwrap().into_pointer_value();
+
+        contract.builder.build_store(
+            contract.builder.build_pointer_cast(dest,
+                contract.context.i32_type().ptr_type(AddressSpace::Generic),
+                ""),
+            contract.context.i32_type().const_zero());
+
+        contract.builder.build_return(Some(&dest));
+    }
+
+    fn return_abi<'b>(&self, contract: &'b Contract, data: PointerValue<'b>, _length: IntValue) {
+        contract.builder.build_return(Some(&data));
+    }
+
+    fn abi_encode<'b>(
+        &self,
+        contract: &'b Contract,
+        args: &[ BasicValueEnum<'b> ],
+        spec: &resolver::FunctionDecl,
+    ) -> (PointerValue<'b>, IntValue<'b>) {
+        let length = contract.context.i32_type().const_int(32 * args.len() as u64, false);
+        let data = contract.builder.build_call(
+            contract.module.get_function("__malloc").unwrap(),
+            &[contract.context.i32_type().const_int(4 + 32 * args.len() as u64, false).into()],
+            ""
+        ).try_as_basic_value().left().unwrap().into_pointer_value();
+
+        // write length
+        contract.builder.build_store(
+            contract.builder.build_pointer_cast(data,
+                contract.context.i32_type().ptr_type(AddressSpace::Generic),
+                ""),
+            length);
+
+        // malloc returns u8*
+        let abi_ptr = unsafe {
+            contract.builder.build_gep(
+                data,
+                &[ contract.context.i32_type().const_int(4, false).into()],
+                "abi_ptr")
+        };
+
+        for (i, arg) in spec.returns.iter().enumerate() {
+            // insert abi decode
+            let ty = match arg.ty {
+                resolver::TypeName::Elementary(e) => e,
+                resolver::TypeName::Enum(n) => contract.ns.enums[n].ty,
+                resolver::TypeName::Noreturn => unreachable!(),
+            };
+
+            self.emit_abi_encode_single_val(contract, &ty, abi_ptr, args[i].into_int_value());
+        }
+
+        (data, length)
+    }
+
+    fn abi_decode<'b>(
+        &self,
+        contract: &'b Contract,
+        function: FunctionValue,
+        args: &mut Vec<BasicValueEnum<'b>>,
+        data: PointerValue<'b>,
+        length: IntValue,
+        spec: &resolver::FunctionDecl,
+    ) {
+        let mut data = data;
+        let decode_block = contract.context.append_basic_block(function, "abi_decode");
+        let wrong_length_block = contract.context.append_basic_block(function, "wrong_abi_length");
+
+        let is_ok = contract.builder.build_int_compare(IntPredicate::EQ, length,
+            contract.context.i32_type().const_int(32  * spec.params.len() as u64, false),
+            "correct_length");
+
+        contract.builder.build_conditional_branch(is_ok, &decode_block, &wrong_length_block);
+
+        contract.builder.position_at_end(&decode_block);
+
+        for arg in &spec.params {
+            let ty = match &arg.ty {
+                resolver::TypeName::Elementary(e) => e,
+                resolver::TypeName::Enum(n) => &contract.ns.enums[*n].ty,
+                resolver::TypeName::Noreturn => unreachable!(),
+            };
+
+            args.push(match ty {
+                ast::ElementaryTypeName::Bool => {
+                    // solidity checks all the 32 bytes for being non-zero; we will just look at the upper 8 bytes, else we would need four loads
+                    // which is unneeded (hopefully)
+                    // cast to 64 bit pointer
+                    let bool_ptr = contract.builder.build_pointer_cast(data,
+                        contract.context.i64_type().ptr_type(AddressSpace::Generic), "");
+
+                    let bool_ptr = unsafe {
+                        contract.builder.build_gep(bool_ptr,
+                            &[ contract.context.i32_type().const_int(3, false) ],
+                            "bool_ptr")
+                    };
+
+                    contract.builder.build_int_compare(IntPredicate::EQ,
+                        contract.builder.build_load(bool_ptr, "abi_bool").into_int_value(),
+                        contract.context.i64_type().const_zero(), "bool").into()
+                }
+                ast::ElementaryTypeName::Uint(8) | ast::ElementaryTypeName::Int(8) => {
+                    let int8_ptr = contract.builder.build_pointer_cast(data,
+                        contract.context.i8_type().ptr_type(AddressSpace::Generic), "");
+
+                    let int8_ptr = unsafe {
+                        contract.builder.build_gep(int8_ptr,
+                        &[ contract.context.i32_type().const_int(31, false) ],
+                        "bool_ptr")
+                    };
+
+                    contract.builder.build_load(int8_ptr, "abi_int8")
+                }
+                ast::ElementaryTypeName::Uint(n) | ast::ElementaryTypeName::Int(n) => {
+                    let int_type = contract.context.custom_width_int_type(*n as u32);
+                    let type_size = int_type.size_of();
+
+                    let store = contract.builder.build_alloca(int_type, "stack");
+
+                    contract.builder.build_call(
+                        contract.module.get_function("__be32toleN").unwrap(),
+                        &[
+                            contract.builder.build_pointer_cast(data,
+                                contract.context.i8_type().ptr_type(AddressSpace::Generic), "").into(),
+                            contract.builder.build_pointer_cast(store,
+                                contract.context.i8_type().ptr_type(AddressSpace::Generic), "").into(),
+                            contract.builder.build_int_truncate(type_size,
+                                contract.context.i32_type(), "size").into()
+                        ],
+                        ""
+                    );
+
+                    if *n <= 64 {
+                        contract.builder.build_load(store, &format!("abi_int{}", *n))
+                    } else {
+                        store.into()
+                    }
+                }
+                _ => panic!(),
+            });
+
+            data = unsafe {
+                contract.builder.build_gep(data,
+                    &[ contract.context.i32_type().const_int(8, false)],
+                    "data_next")
+            };
+        }
+
+        // FIXME: generate a call to revert/abort with some human readable error or error code
+        contract.builder.position_at_end(&wrong_length_block);
+        contract.builder.build_unreachable();
+
+        contract.builder.position_at_end(&decode_block);
+    }
+
 }

+ 132 - 398
src/emit/mod.rs

@@ -24,7 +24,8 @@ use inkwell::IntPredicate;
 
 const WASMTRIPLE: &str = "wasm32-unknown-unknown-wasm";
 
-pub mod burrow;
+mod burrow;
+mod substrate;
 
 lazy_static::lazy_static! {
     static ref LLVM_INIT: () = {
@@ -45,8 +46,28 @@ struct Function<'a> {
 }
 
 pub trait TargetRuntime {
+    //
     fn set_storage<'a>(&self, contract: &'a Contract, slot: u32, dest: inkwell::values::PointerValue<'a>);
     fn get_storage<'a>(&self, contract: &'a Contract, slot: u32, dest: inkwell::values::PointerValue<'a>);
+    
+    fn abi_decode<'b>(
+        &self,
+        contract: &'b Contract,
+        function: FunctionValue,
+        args: &mut Vec<BasicValueEnum<'b>>,
+        data: PointerValue<'b>,
+        length: IntValue,
+        spec: &resolver::FunctionDecl,
+    );
+    fn abi_encode<'b>(
+        &self,
+        contract: &'b Contract,
+        args: &[BasicValueEnum<'b>],
+        spec: &resolver::FunctionDecl,
+    ) -> (PointerValue<'b>, IntValue<'b>);
+
+    fn return_empty_abi(&self, contract: &Contract);
+    fn return_abi<'b>(&self, contract: &'b Contract, data: PointerValue<'b>, length: IntValue);
 }
 
 pub struct Contract<'a> {
@@ -58,10 +79,16 @@ pub struct Contract<'a> {
     ns: &'a resolver::Contract,
     constructors: Vec<Function<'a>>,
     functions: Vec<Function<'a>>,
-    externals: HashMap<String, FunctionValue<'a>>,
 }
 
 impl<'a> Contract<'a> {
+    pub fn build(context: &'a Context, contract: &'a resolver::Contract, filename: &'a str) -> Self {
+        match contract.target {
+            resolver::Target::Burrow => burrow::BurrowTarget::build(context, contract, filename),
+            resolver::Target::Substrate => substrate::SubstrateTarget::build(context, contract, filename),
+        }
+    }
+
     pub fn wasm(&self, opt: &str) -> Result<Vec<u8>, String> {
         let opt = match opt {
             "none" => OptimizationLevel::None,
@@ -113,7 +140,6 @@ impl<'a> Contract<'a> {
             ns: contract,
             constructors: Vec::new(),
             functions: Vec::new(),
-            externals: HashMap::new(),
         }
     }
 
@@ -225,401 +251,6 @@ impl<'a> Contract<'a> {
         }
     }
 
-    fn emit_function_dispatch(&self, contract: &resolver::Contract) {
-        // create start function
-        let ret = self.context.i32_type().ptr_type(AddressSpace::Generic);
-        let ftype = ret.fn_type(&[self.context.i32_type().ptr_type(AddressSpace::Generic).into()], false);
-        let function = self.module.add_function("function", ftype, None);
-
-        let entry = self.context.append_basic_block(function, "entry");
-        let fallback_block = self.context.append_basic_block(function, "fallback");
-        let switch_block = self.context.append_basic_block(function, "switch");
-
-        self.builder.position_at_end(&entry);
-
-        let arg = function.get_first_param().unwrap().into_pointer_value();
-        let length = self.builder.build_load(arg, "length").into_int_value();
-
-        let not_fallback = self.builder.build_int_compare(
-            IntPredicate::UGE,
-            length,
-            self.context.i32_type().const_int(4, false).into(),
-            "");
-
-        self.builder.build_conditional_branch(not_fallback, &switch_block, &fallback_block);
-
-        self.builder.position_at_end(&switch_block);
-
-        let fid_ptr = unsafe {
-            self.builder.build_gep(
-                arg,
-                &[self.context.i32_type().const_int(1, false).into()],
-                "fid_ptr")
-        };
-
-        let fid = self.builder.build_load(fid_ptr, "fid");
-
-        // pointer/size for abi decoding
-        let args_ptr = unsafe {
-            self.builder.build_gep(
-                arg,
-                &[self.context.i32_type().const_int(2, false).into()],
-                "fid_ptr")
-        };
-
-        let args_len = self.builder.build_int_sub(
-            length.into(),
-            self.context.i32_type().const_int(4, false).into(),
-            "args_len"
-        );
-
-        let nomatch = self.context.append_basic_block(function, "nomatch");
-
-        self.builder.position_at_end(&nomatch);
-
-        self.builder.build_unreachable();
-
-        let mut cases = Vec::new();
-
-        let mut fallback = None;
-
-        for (i, f) in contract.functions.iter().enumerate() {
-            match &f.visibility {
-                ast::Visibility::Internal(_) | ast::Visibility::Private(_) => {
-                    continue;
-                }
-                _ => (),
-            }
-
-            if f.name == "" {
-                fallback = Some(i);
-                continue;
-            }
-
-            let res = keccak256(f.signature.as_bytes());
-
-            let bb = self.context.append_basic_block(function, "");
-            let id = u32::from_le_bytes([res[0], res[1], res[2], res[3]]);
-
-            self.builder.position_at_end(&bb);
-
-            let mut args = Vec::new();
-
-            // insert abi decode
-            self.emit_abi_decode(function, &mut args, args_ptr, args_len, f);
-
-            let ret = self.builder.build_call(
-                self.functions[i].value_ref,
-                &args,
-                "").try_as_basic_value().left();
-
-            if f.returns.is_empty() {
-                // return ABI of length 0
-
-                // malloc 4 bytes
-                let dest = self.builder.build_call(
-                    self.module.get_function("__malloc").unwrap(),
-                    &[self.context.i32_type().const_int(4, false).into()],
-                    ""
-                ).try_as_basic_value().left().unwrap().into_pointer_value();
-
-                self.builder.build_store(
-                    self.builder.build_pointer_cast(dest,
-                        self.context.i32_type().ptr_type(AddressSpace::Generic),
-                        ""),
-                    self.context.i32_type().const_zero());
-
-                self.builder.build_return(Some(&dest));
-            } else if self.functions[i].wasm_return {
-                // malloc 36 bytes
-                let dest = self.builder.build_call(
-                    self.module.get_function("__malloc").unwrap(),
-                    &[self.context.i32_type().const_int(36, false).into()],
-                    ""
-                ).try_as_basic_value().left().unwrap().into_pointer_value();
-
-                // write length
-                self.builder.build_store(
-                    self.builder.build_pointer_cast(dest,
-                        self.context.i32_type().ptr_type(AddressSpace::Generic),
-                        ""),
-                    self.context.i32_type().const_int(32, false));
-
-                // malloc returns u8*
-                let abi_ptr = unsafe {
-                    self.builder.build_gep(
-                        dest,
-                        &[ self.context.i32_type().const_int(4, false).into()],
-                        "abi_ptr")
-                };
-
-                // insert abi decode
-                let ty = match &f.returns[0].ty {
-                    resolver::TypeName::Elementary(e) => e,
-                    resolver::TypeName::Enum(n) => &self.ns.enums[*n].ty,
-                    resolver::TypeName::Noreturn => unreachable!(),
-                };
-
-                self.emit_abi_encode_single_val(&ty, abi_ptr, ret.unwrap().into_int_value());
-
-                self.builder.build_return(Some(&dest));
-            } else {
-                // FIXME: abi encode all the arguments
-            }
-
-            cases.push((self.context.i32_type().const_int(id as u64, false), bb));
-        }
-
-        self.builder.position_at_end(&switch_block);
-
-        let mut c = Vec::new();
-
-        for (id, bb) in cases.iter() {
-            c.push((*id, bb));
-        }
-
-        //let c = cases.into_iter().map(|(id, bb)| (id, &bb)).collect();
-
-        self.builder.build_switch(
-            fid.into_int_value(), &nomatch,
-            &c);
-
-        // FIXME: emit code for public contract variables
-
-        // emit fallback code
-        self.builder.position_at_end(&fallback_block);
-
-        match fallback {
-            Some(f) => {
-                self.builder.build_call(
-                    self.functions[f].value_ref,
-                    &[],
-                    "");
-
-                self.builder.build_return(None);
-            }
-            None => {
-                self.builder.build_unreachable();
-            },
-        }
-    }
-
-    fn emit_abi_encode_single_val(
-        &self,
-        ty: &ast::ElementaryTypeName,
-        dest: PointerValue,
-        val: IntValue,
-    ) {
-        match ty {
-            ast::ElementaryTypeName::Bool => {
-                // first clear
-                let dest8 = self.builder.build_pointer_cast(dest,
-                    self.context.i8_type().ptr_type(AddressSpace::Generic),
-                    "destvoid");
-
-                self.builder.build_call(
-                    self.module.get_function("__bzero8").unwrap(),
-                    &[ dest8.into(),
-                       self.context.i32_type().const_int(4, false).into() ],
-                    "");
-
-                let value = self.builder.build_select(val,
-                    self.context.i8_type().const_int(1, false),
-                    self.context.i8_type().const_zero(),
-                    "bool_val");
-
-                let dest = unsafe {
-                    self.builder.build_gep(
-                        dest8,
-                        &[ self.context.i32_type().const_int(31, false).into() ],
-                        "")
-                };
-
-                self.builder.build_store(dest, value);
-            }
-            ast::ElementaryTypeName::Int(8) | ast::ElementaryTypeName::Uint(8) => {
-                let signval = if let ast::ElementaryTypeName::Int(8) = ty {
-                    let negative = self.builder.build_int_compare(IntPredicate::SLT,
-                            val, self.context.i8_type().const_zero(), "neg");
-
-                            self.builder.build_select(negative,
-                        self.context.i64_type().const_zero(),
-                        self.context.i64_type().const_int(std::u64::MAX, true),
-                        "val").into_int_value()
-                } else {
-                    self.context.i64_type().const_zero()
-                };
-
-                let dest8 = self.builder.build_pointer_cast(dest,
-                    self.context.i8_type().ptr_type(AddressSpace::Generic),
-                    "destvoid");
-
-                    self.builder.build_call(
-                    self.module.get_function("__memset8").unwrap(),
-                    &[ dest8.into(), signval.into(),
-                       self.context.i32_type().const_int(4, false).into() ],
-                    "");
-
-                let dest = unsafe {
-                    self.builder.build_gep(
-                        dest8,
-                        &[ self.context.i32_type().const_int(31, false).into() ],
-                        "")
-                };
-
-                self.builder.build_store(dest, val);
-            }
-            ast::ElementaryTypeName::Uint(n) | ast::ElementaryTypeName::Int(n) => {
-                // first clear/set the upper bits
-                if *n < 256 {
-                    let signval = if let ast::ElementaryTypeName::Int(8) = ty {
-                        let negative = self.builder.build_int_compare(IntPredicate::SLT,
-                                val, self.context.i8_type().const_zero(), "neg");
-
-                        self.builder.build_select(negative,
-                            self.context.i64_type().const_zero(),
-                            self.context.i64_type().const_int(std::u64::MAX, true),
-                            "val").into_int_value()
-                    } else {
-                        self.context.i64_type().const_zero()
-                    };
-
-                    let dest8 = self.builder.build_pointer_cast(dest,
-                        self.context.i8_type().ptr_type(AddressSpace::Generic),
-                        "destvoid");
-
-                    self.builder.build_call(
-                        self.module.get_function("__memset8").unwrap(),
-                        &[ dest8.into(), signval.into(),
-                            self.context.i32_type().const_int(4, false).into() ],
-                        "");
-                }
-
-                // no need to allocate space for each uint64
-                // allocate enough for type
-                let int_type = self.context.custom_width_int_type(*n as u32);
-                let type_size = int_type.size_of();
-
-                let store = self.builder.build_alloca(int_type, "stack");
-
-                self.builder.build_store(store, val);
-
-                self.builder.build_call(
-                    self.module.get_function("__leNtobe32").unwrap(),
-                    &[ self.builder.build_pointer_cast(store,
-                            self.context.i8_type().ptr_type(AddressSpace::Generic),
-                            "destvoid").into(),
-                        self.builder.build_pointer_cast(dest,
-                            self.context.i8_type().ptr_type(AddressSpace::Generic),
-                            "destvoid").into(),
-                        self.builder.build_int_truncate(type_size,
-                            self.context.i32_type(), "").into()
-                    ],
-                    "");
-            }
-            _ => unimplemented!(),
-        }
-    }
-
-    fn emit_abi_decode(
-        &self,
-        function: FunctionValue,
-        args: &mut Vec<BasicValueEnum<'a>>,
-        data: PointerValue<'a>,
-        length: IntValue,
-        spec: &resolver::FunctionDecl,
-    ) {
-        let mut data = data;
-        let decode_block = self.context.append_basic_block(function, "abi_decode");
-        let wrong_length_block = self.context.append_basic_block(function, "wrong_abi_length");
-
-        let is_ok = self.builder.build_int_compare(IntPredicate::EQ, length,
-            self.context.i32_type().const_int(32  * spec.params.len() as u64, false),
-            "correct_length");
-
-        self.builder.build_conditional_branch(is_ok, &decode_block, &wrong_length_block);
-
-        self.builder.position_at_end(&decode_block);
-
-        for arg in &spec.params {
-            let ty = match &arg.ty {
-                resolver::TypeName::Elementary(e) => e,
-                resolver::TypeName::Enum(n) => &self.ns.enums[*n].ty,
-                resolver::TypeName::Noreturn => unreachable!(),
-            };
-
-            args.push(match ty {
-                ast::ElementaryTypeName::Bool => {
-                    // solidity checks all the 32 bytes for being non-zero; we will just look at the upper 8 bytes, else we would need four loads
-                    // which is unneeded (hopefully)
-                    // cast to 64 bit pointer
-                    let bool_ptr = self.builder.build_pointer_cast(data,
-                        self.context.i64_type().ptr_type(AddressSpace::Generic), "");
-
-                    let bool_ptr = unsafe {
-                        self.builder.build_gep(bool_ptr,
-                            &[ self.context.i32_type().const_int(3, false) ],
-                            "bool_ptr")
-                    };
-
-                    self.builder.build_int_compare(IntPredicate::EQ,
-                        self.builder.build_load(bool_ptr, "abi_bool").into_int_value(),
-                        self.context.i64_type().const_zero(), "bool").into()
-                }
-                ast::ElementaryTypeName::Uint(8) | ast::ElementaryTypeName::Int(8) => {
-                    let int8_ptr = self.builder.build_pointer_cast(data,
-                        self.context.i8_type().ptr_type(AddressSpace::Generic), "");
-
-                    let int8_ptr = unsafe {
-                        self.builder.build_gep(int8_ptr,
-                        &[ self.context.i32_type().const_int(31, false) ],
-                        "bool_ptr")
-                    };
-
-                    self.builder.build_load(int8_ptr, "abi_int8")
-                }
-                ast::ElementaryTypeName::Uint(n) | ast::ElementaryTypeName::Int(n) => {
-                    let int_type = self.context.custom_width_int_type(*n as u32);
-                    let type_size = int_type.size_of();
-
-                    let store = self.builder.build_alloca(int_type, "stack");
-
-                    self.builder.build_call(
-                        self.module.get_function("__be32toleN").unwrap(),
-                        &[
-                            self.builder.build_pointer_cast(data,
-                                self.context.i8_type().ptr_type(AddressSpace::Generic), "").into(),
-                            self.builder.build_pointer_cast(store,
-                                self.context.i8_type().ptr_type(AddressSpace::Generic), "").into(),
-                            self.builder.build_int_truncate(type_size,
-                                self.context.i32_type(), "size").into()
-                        ],
-                        ""
-                    );
-
-                    if *n <= 64 {
-                        self.builder.build_load(store, &format!("abi_int{}", *n))
-                    } else {
-                        store.into()
-                    }
-                }
-                _ => panic!(),
-            });
-
-            data = unsafe {
-                self.builder.build_gep(data,
-                    &[ self.context.i32_type().const_int(8, false)],
-                    "data_next")
-            };
-        }
-
-        // FIXME: generate a call to revert/abort with some human readable error or error code
-        self.builder.position_at_end(&wrong_length_block);
-        self.builder.build_unreachable();
-
-        self.builder.position_at_end(&decode_block);
-    }
-
     fn emit_func(&self, fname: &str, f: &resolver::FunctionDecl, runtime: &dyn TargetRuntime) -> Function<'a> {
         let mut args: Vec<BasicTypeEnum> = Vec::new();
         let mut wasm_return = false;
@@ -864,6 +495,109 @@ impl<'a> Contract<'a> {
             wasm_return,
         }
     }
+
+    pub fn emit_function_dispatch(&self, 
+                functions: &Vec<resolver::FunctionDecl>, 
+                argsdata: inkwell::values::PointerValue,
+                argslen: inkwell::values::IntValue,
+                function: inkwell::values::FunctionValue, 
+                fallback_block: &inkwell::basic_block::BasicBlock,
+                runtime: &dyn TargetRuntime) {
+        // create start function
+        let switch_block = self.context.append_basic_block(function, "switch");
+
+        let not_fallback = self.builder.build_int_compare(
+            IntPredicate::UGE,
+            argslen,
+            self.context.i32_type().const_int(4, false).into(),
+            "");
+
+        self.builder.build_conditional_branch(not_fallback, &switch_block, fallback_block);
+
+        self.builder.position_at_end(&switch_block);
+
+        let fid = self.builder.build_load(argsdata, "function_selector");
+
+        // step over the function selector
+        let argsdata = unsafe {
+            self.builder.build_gep(
+                argsdata,
+                &[self.context.i32_type().const_int(1, false).into()],
+                "fid_ptr")
+        };
+
+        let argslen = self.builder.build_int_sub(
+            argslen.into(),
+            self.context.i32_type().const_int(4, false).into(),
+            "argslen"
+        );
+
+        let nomatch = self.context.append_basic_block(function, "nomatch");
+
+        self.builder.position_at_end(&nomatch);
+
+        self.builder.build_unreachable();
+
+        let mut cases = Vec::new();
+
+        for (i, f) in functions.iter().enumerate() {
+            match &f.visibility {
+                ast::Visibility::Internal(_) | ast::Visibility::Private(_) => {
+                    continue;
+                }
+                _ => (),
+            }
+
+            if f.fallback {
+                continue;
+            }
+
+            let res = keccak256(f.signature.as_bytes());
+
+            let bb = self.context.append_basic_block(function, "");
+            let id = u32::from_le_bytes([res[0], res[1], res[2], res[3]]);
+
+            self.builder.position_at_end(&bb);
+
+            let mut args = Vec::new();
+
+            // insert abi decode
+            runtime.abi_decode(&self, function, &mut args, argsdata, argslen, f);
+
+            let ret = self.builder.build_call(
+                self.functions[i].value_ref,
+                &args,
+                "").try_as_basic_value().left();
+
+            if f.returns.is_empty() {
+                // return ABI of length 0
+                runtime.return_empty_abi(&self);
+            } else if self.functions[i].wasm_return {
+                let (data, length) = runtime.abi_encode(&self, &[ ret.unwrap() ], &f);
+
+                runtime.return_abi(&self, data, length);
+            } else {
+                // FIXME: abi encode all the arguments
+                unimplemented!();
+            }
+
+            cases.push((self.context.i32_type().const_int(id as u64, false), bb));
+        }
+
+        self.builder.position_at_end(&switch_block);
+
+        let mut c = Vec::new();
+
+        for (id, bb) in cases.iter() {
+            c.push((*id, bb));
+        }
+
+        //let c = cases.into_iter().map(|(id, bb)| (id, &bb)).collect();
+
+        self.builder.build_switch(
+            fid.into_int_value(), &nomatch,
+            &c);
+    }
 }
 
 impl ast::ElementaryTypeName {

+ 286 - 0
src/emit/substrate.rs

@@ -0,0 +1,286 @@
+
+use resolver;
+use parser::ast;
+
+use inkwell::context::Context;
+use inkwell::module::Linkage;
+use inkwell::AddressSpace;
+use inkwell::values::{BasicValueEnum, IntValue, PointerValue, FunctionValue};
+use inkwell::IntPredicate;
+
+use super::{TargetRuntime, Contract};
+
+pub struct SubstrateTarget {
+}
+
+impl SubstrateTarget {
+    pub fn build<'a>(context: &'a Context, contract: &'a resolver::Contract, filename: &'a str) -> Contract<'a> {
+        let mut c = Contract::new(context, contract, filename);
+        let b = SubstrateTarget{};
+
+        // externals
+        b.declare_externals(&mut c);
+
+        c.emit_functions(&b);
+    
+        b.emit_deploy(&c);
+
+        c
+    }
+
+    fn declare_externals(&self, contract: &mut Contract) {
+        // Access to scratch buffer
+        contract.module.add_function(
+            "ext_scratch_size",
+            contract.context.i32_type().fn_type(&[], false),
+            Some(Linkage::External)
+        );
+
+        contract.module.add_function(
+            "ext_scratch_read",
+            contract.context.void_type().fn_type(&[
+                contract.context.i8_type().ptr_type(AddressSpace::Generic).into(),  // dest_ptr
+                contract.context.i32_type().into(), // offset
+                contract.context.i32_type().into(), // len
+            ], false),
+            Some(Linkage::External)
+        );
+
+        contract.module.add_function(
+            "ext_scratch_write",
+            contract.context.void_type().fn_type(&[
+                contract.context.i8_type().ptr_type(AddressSpace::Generic).into(),  // dest_ptr
+                contract.context.i32_type().into(), // len
+            ], false),
+            Some(Linkage::External)
+        );
+    }
+
+    fn emit_deploy(&self, contract: &Contract) {
+        // create deploy function
+        let ret = contract.context.void_type();
+        let ftype = ret.fn_type(&[contract.context.i32_type().ptr_type(AddressSpace::Generic).into()], false);
+        let function = contract.module.add_function("deploy", ftype, None);
+
+        let entry = contract.context.append_basic_block(function, "entry");
+
+        contract.builder.position_at_end(&entry);
+
+        // init our heap
+        contract.builder.build_call(
+            contract.module.get_function("__init_heap").unwrap(),
+            &[],
+            "");
+        
+        // copy arguments from scratch buffer
+        let deploy_args_length = contract.builder.build_call(
+            contract.module.get_function("ext_scratch_size").unwrap(),
+            &[],
+            "scratch_size").try_as_basic_value().left().unwrap();
+        
+        let deploy_args = contract.builder.build_call(
+            contract.module.get_function("__malloc").unwrap(),
+            &[deploy_args_length],
+            ""
+        ).try_as_basic_value().left().unwrap().into_pointer_value();
+
+        contract.builder.build_call(
+            contract.module.get_function("ext_scratch_read").unwrap(),
+            &[
+                deploy_args.into(),
+                contract.context.i32_type().const_zero().into(),
+                deploy_args_length.into(),
+            ],
+            ""
+        );
+
+        let fallback_block = contract.context.append_basic_block(function, "fallback");
+
+        contract.emit_function_dispatch(&contract.ns.constructors, deploy_args, deploy_args_length.into_int_value(), function, &fallback_block, self);
+        
+        // emit fallback code
+        contract.builder.position_at_end(&fallback_block);
+        contract.builder.build_unreachable();
+    }
+}
+
+impl TargetRuntime for SubstrateTarget {
+    // TODO
+    fn set_storage<'a>(&self, _contract: &'a Contract, _slot: u32, _dest: inkwell::values::PointerValue<'a>) {
+    }
+
+    // TODO
+    fn get_storage<'a>(&self, _contract: &'a Contract, _slot: u32, _dest: inkwell::values::PointerValue<'a>) {
+    }
+
+    fn abi_decode<'b>(
+        &self,
+        contract: &'b Contract,
+        function: FunctionValue,
+        args: &mut Vec<BasicValueEnum<'b>>,
+        data: PointerValue<'b>,
+        datalength: IntValue,
+        spec: &resolver::FunctionDecl,
+    ) {
+        let mut length = 0;
+
+        for arg in spec.returns.iter() {
+            let ty = match arg.ty {
+                resolver::TypeName::Elementary(e) => e,
+                resolver::TypeName::Enum(n) => contract.ns.enums[n].ty,
+                resolver::TypeName::Noreturn => unreachable!(),
+            };
+
+            match ty {
+                ast::ElementaryTypeName::Bool => length += 1,
+                ast::ElementaryTypeName::Uint(n) |
+                ast::ElementaryTypeName::Int(n) => length += n as u64 / 8,
+                _ => unimplemented!()
+            }
+        }
+
+        let decode_block = contract.context.append_basic_block(function, "abi_decode");
+        let wrong_length_block = contract.context.append_basic_block(function, "wrong_abi_length");
+
+        let is_ok = contract.builder.build_int_compare(IntPredicate::EQ, datalength,
+            contract.context.i32_type().const_int(length, false),
+            "correct_length");
+
+        contract.builder.build_conditional_branch(is_ok, &decode_block, &wrong_length_block);
+    
+        contract.builder.position_at_end(&decode_block);
+
+        let mut argsdata = data;
+
+        for (i, arg) in spec.returns.iter().enumerate() {
+            let ty = match arg.ty {
+                resolver::TypeName::Elementary(e) => e,
+                resolver::TypeName::Enum(n) => contract.ns.enums[n].ty,
+                resolver::TypeName::Noreturn => unreachable!(),
+            };
+
+            let arglen;
+
+            match ty {
+                ast::ElementaryTypeName::Bool => {
+                    // FIXME: check for not 0 or 1
+                    args[i] = contract.builder.build_int_compare(IntPredicate::EQ,
+                        contract.builder.build_load(argsdata, "abi_bool").into_int_value(),
+                        contract.context.i64_type().const_zero(), "bool").into();
+                    arglen = 1;
+                },
+                ast::ElementaryTypeName::Uint(n) |
+                ast::ElementaryTypeName::Int(n) => {
+                    args[i] = contract.builder.build_load(
+                        contract.builder.build_pointer_cast(argsdata,
+                            args[i].into_int_value().get_type().ptr_type(AddressSpace::Generic),
+                            ""),
+                        "");
+                    arglen = n as u64 / 8;
+                },
+                _ => unimplemented!()
+            }
+
+            argsdata = unsafe {
+                contract.builder.build_gep(
+                    argsdata,
+                    &[ contract.context.i32_type().const_int(arglen, false).into()],
+                    "abi_ptr")
+            };
+        }
+    }
+
+    fn abi_encode<'b>(
+        &self,
+        contract: &'b Contract,
+        args: &[BasicValueEnum<'b>],
+        spec: &resolver::FunctionDecl,
+    ) -> (PointerValue<'b>, IntValue<'b>) {
+        let mut length = 0;
+
+        for arg in spec.returns.iter() {
+            let ty = match arg.ty {
+                resolver::TypeName::Elementary(e) => e,
+                resolver::TypeName::Enum(n) => contract.ns.enums[n].ty,
+                resolver::TypeName::Noreturn => unreachable!(),
+            };
+
+            match ty {
+                ast::ElementaryTypeName::Bool => length += 1,
+                ast::ElementaryTypeName::Uint(n) |
+                ast::ElementaryTypeName::Int(n) => length += n as u64 / 8,
+                _ => unimplemented!()
+            }
+        }
+
+        let length = contract.context.i32_type().const_int(length, false);
+
+        let data = contract.builder.build_call(
+            contract.module.get_function("__malloc").unwrap(),
+            &[ length.into() ],
+            ""
+        ).try_as_basic_value().left().unwrap().into_pointer_value();
+
+        let mut argsdata = data;
+
+        for (i, arg) in spec.returns.iter().enumerate() {
+            let ty = match arg.ty {
+                resolver::TypeName::Elementary(e) => e,
+                resolver::TypeName::Enum(n) => contract.ns.enums[n].ty,
+                resolver::TypeName::Noreturn => unreachable!(),
+            };
+
+            let arglen;
+
+            match ty {
+                ast::ElementaryTypeName::Bool => {
+                    contract.builder.build_store(argsdata, 
+                        contract.builder.build_int_cast(args[i].into_int_value(), contract.context.i8_type(), "bool")
+                    );
+                    arglen = 1;
+                },
+                ast::ElementaryTypeName::Uint(n) |
+                ast::ElementaryTypeName::Int(n) => {
+                    contract.builder.build_store(
+                        contract.builder.build_pointer_cast(argsdata,
+                            args[i].into_int_value().get_type().ptr_type(AddressSpace::Generic),
+                            ""),
+                        args[i].into_int_value()
+                    );
+                    arglen = n as u64 / 8;
+                }
+                _ => unimplemented!()
+            }
+
+            argsdata = unsafe {
+                contract.builder.build_gep(
+                    argsdata,
+                    &[ contract.context.i32_type().const_int(arglen, false).into()],
+                    "abi_ptr")
+            };
+        }
+
+        (data, length)
+    }
+
+    fn return_empty_abi(&self, contract: &Contract) {
+        contract.builder.build_call(
+            contract.module.get_function("ext_scratch_write").unwrap(),
+            &[
+                contract.context.i8_type().ptr_type(AddressSpace::Generic).const_zero().into(),
+                contract.context.i32_type().const_zero().into(),
+            ],
+            ""
+        );
+        contract.builder.build_return(Some(&contract.context.i32_type().const_zero()));
+    }
+
+    fn return_abi<'b>(&self, contract: &'b Contract, data: PointerValue<'b>, length: IntValue) {
+        contract.builder.build_call(
+            contract.module.get_function("ext_scratch_write").unwrap(),
+            &[ data.into(), length.into() ],
+            ""
+        );
+        contract.builder.build_return(Some(&contract.context.i32_type().const_zero()));
+    }
+}

+ 1 - 1
src/main.rs

@@ -167,7 +167,7 @@ fn main() {
 
             let abi = contract.generate_abi();
 
-            let contract = emit::burrow::BurrowTarget::build(&context, contract, &filename);
+            let contract = emit::Contract::build(&context, contract, &filename);
 
             if let Some("llvm") = matches.value_of("EMIT") {
                 contract.dump_llvm(&output_file(&contract.name, "ll")).unwrap();

+ 5 - 5
src/resolver/functions.rs

@@ -120,12 +120,12 @@ pub fn function_decl(
         return false;
     }
 
-    let name = match f.name {
-        Some(ref n) => n.name.to_owned(),
-        None => "".to_owned()
+    let (name, fallback) = match f.name {
+        Some(ref n) => (n.name.to_owned(), false),
+        None => ("".to_owned(), !f.constructor),
     };
 
-    let fdecl = FunctionDecl::new(f.loc, name, i, mutability, visibility.unwrap(), params, returns, &ns);
+    let fdecl = FunctionDecl::new(f.loc, name, fallback, i, mutability, visibility.unwrap(), params, returns, &ns);
 
     if f.constructor {
         // In the eth solidity, only one constructor is allowed
@@ -260,7 +260,7 @@ fn signatures() {
     };
 
     let fdecl = FunctionDecl::new(
-        ast::Loc(0, 0), "foo".to_owned(), 0, None, ast::Visibility::Public(ast::Loc(0, 0)),
+        ast::Loc(0, 0), "foo".to_owned(), false, 0, None, ast::Visibility::Public(ast::Loc(0, 0)),
         vec!(
             Parameter {
                 name: "".to_string(),

+ 4 - 3
src/resolver/mod.rs

@@ -105,6 +105,7 @@ impl Parameter {
 pub struct FunctionDecl {
     pub loc: ast::Loc,
     pub name: String,
+    pub fallback: bool,
     pub signature: String,
     pub ast_index: usize,
     pub mutability: Option<ast::StateMutability>,
@@ -115,7 +116,7 @@ pub struct FunctionDecl {
 }
 
 impl FunctionDecl {
-    fn new(loc: ast::Loc, name: String, ast_index: usize, mutability: Option<ast::StateMutability>,
+    fn new(loc: ast::Loc, name: String, fallback: bool, ast_index: usize, mutability: Option<ast::StateMutability>,
         visibility: ast::Visibility, params: Vec<Parameter>, returns: Vec<Parameter>, ns: &Contract) -> Self {
         let mut signature = name.to_owned();
 
@@ -136,7 +137,7 @@ impl FunctionDecl {
         signature.push(')');
 
         FunctionDecl{
-            loc, name, signature, ast_index, mutability, visibility, params, returns, cfg: None
+            loc, name, fallback, signature, ast_index, mutability, visibility, params, returns, cfg: None
         }
     }
 
@@ -351,7 +352,7 @@ impl Contract {
 
     pub fn fallback_function(&self) -> Option<usize> {
         for (i, f) in self.functions.iter().enumerate() {
-            if f.name == "" {
+            if f.fallback {
                 return Some(i);
             }
         }

+ 1 - 1
src/test.rs

@@ -103,7 +103,7 @@ mod tests {
         let abi = contracts[0].generate_abi();
 
         // codegen
-        let contract = emit::burrow::BurrowTarget::build(ctx, &contracts[0], &"foo.sol");
+        let contract = emit::Contract::build(ctx, &contracts[0], &"foo.sol");
 
         let obj = contract.wasm("default").expect("llvm wasm emit should work");