Преглед изворни кода

Add Ethereum ewasm support

Signed-off-by: Sean Young <sean@mess.org>
Sean Young пре 5 година
родитељ
комит
77ac52d95e
15 измењених фајлова са 829 додато и 41 уклоњено
  1. 4 2
      README.md
  2. 7 5
      docs/index.rst
  3. 2 2
      docs/language.rst
  4. 5 4
      docs/running.rst
  5. 2 1
      src/abi/mod.rs
  6. 55 13
      src/bin/solang.rs
  7. 1 1
      src/emit/burrow.rs
  8. 720 0
      src/emit/ewasm.rs
  9. 6 2
      src/emit/mod.rs
  10. 1 1
      src/emit/substrate.rs
  11. 8 5
      src/lib.rs
  12. 14 1
      src/link.rs
  13. 2 2
      src/resolver/mod.rs
  14. 1 1
      tests/burrow.rs
  15. 1 1
      tests/substrate.rs

+ 4 - 2
README.md

@@ -5,11 +5,13 @@
 
 [<img align="right" width="640" src="docs/web3_foundation_grants_badge_black.svg" alt="Funded by the web3 foundation">](https://github.com/w3f/Web3-collaboration/blob/master/grants/accepted_grant_applications.md#wave-4)
 
-Welcome to solang, a new Solidity compiler written in rust which uses
+Welcome to Solang, a new Solidity compiler written in rust which uses
 llvm as the compiler backend. As a result, only the compiler front end
 needs to be written in rust.
 
-solang is under active development right now, and should be documented at
+Solang targets Substrate, Hyperledger Burrow and ewasm.
+
+Solang is under active development right now, and should be documented at
 the same time as the implementation. Please have a look at
 [our documentation](https://solang.readthedocs.io/en/latest/).
 

+ 7 - 5
docs/index.rst

@@ -7,15 +7,17 @@ Solang Solidity Compiler
     :align: center
     :target: https://github.com/w3f/Web3-collaboration/blob/master/grants/accepted_grant_applications.md#wave-4
 
-Welcome to the Solang Solidity compiler. Using Solang, you can compile
-smart contracts written in `Solidity <https://en.wikipedia.org/wiki/Solidity>`_
-for `Substrate <https://substrate.dev/>`_ or
+Welcome to the Solang Solidity compiler, the portable Solidity compiler.
+Using Solang, you can compile smart contracts written in
+`Solidity <https://en.wikipedia.org/wiki/Solidity>`_
+for `Substrate <https://substrate.dev/>`_,
+`Ethereum ewasm <https://github.com/ewasm/design>`_,  and
 `Hyperledger Burrow <https://github.com/hyperledger/burrow>`_. It uses the
 `llvm <https://www.llvm.org/>`_ compiler framework to produce WebAssembly
 (wasm). As result, the output is highly optimized, which saves you in gas costs.
 
-Solang aims for compatibility with the Ethereum EVM Solidity compiler. Where
-differences exists, this is noted in the documentation.
+Solang aims for source file compatibility with the Ethereum EVM Solidity compiler.
+Where differences exists, this is noted in the documentation.
 
 Many language features are not implemented yet. Anything which is documented
 is supported, though.

+ 2 - 2
docs/language.rst

@@ -1,7 +1,7 @@
 Solidity Language
 =================
 
-The Solidity language support by Solang is compatible with the
+The Solidity language supported by Solang is compatible with the
 `Ethereum Foundation Solidity Compiler <https://github.com/ethereum/solidity/>`_ with
 these caveats:
 
@@ -469,7 +469,7 @@ A constructor must be declared ``public``.
 .. note::
 
   Parity Substrate allows multiple constructors to be defined, which is not true for Hyperledge Burrow
-  or other Ethereum Style blockchains. So, when building for Substrate, multiple constructors can be
+  or ewasm. So, when building for Substrate, multiple constructors can be
   defined as long as their argument list is different (i.e. overloaded).
 
   When the contract is deployed in the Polkadot UI, the user can select the constructor to be used.

+ 5 - 4
docs/running.rst

@@ -5,8 +5,9 @@ The Solang compiler is run on the command line. The solidity source file
 names are provided as command line arguments; the output is an optimized
 wasm file which is ready for deployment on a chain, and an abi file.
 
-Two blockchains are supported right now:
-`Hyperledger Burrow <https://github.com/hyperledger/burrow>`_ and
+Three blockchains are supported right now:
+`Hyperledger Burrow <https://github.com/hyperledger/burrow>`_,
+`Ethereum ewasm <https://github.com/ewasm/design>`_,  and
 `Parity Substrate <https://substrate.dev/>`_.
 
 .. note::
@@ -14,7 +15,7 @@ Two blockchains are supported right now:
   Depending on which target Solang is compiling for, different language
   features are supported. For example, when compiling for substrate, the
   constructor can be overloaded with different prototypes. With Hyperledger
-  Burrow, only one constructor prototype is allowed.
+  Burrow or ewasm, only one constructor prototype is allowed.
 
 Using Solang on the command line
 --------------------------------
@@ -34,7 +35,7 @@ Options:
   will be silent if there are no errors or warnings.
 
 \\-\\-target *target*
-  This takes one argument, which can either be ``burrow`` or ``substrate``.
+  This takes one argument, which can either be ``burrow``, ``ewasm``, or ``substrate``.
   The default is substrate.
 
 -o, \\-\\-output *directory*

+ 2 - 1
src/abi/mod.rs

@@ -7,9 +7,10 @@ pub mod substrate;
 
 pub fn generate_abi(contract: &Contract, verbose: bool) -> (String, &'static str) {
     match contract.target {
+        Target::Ewasm |
         Target::Burrow => {
             if verbose {
-                eprintln!("info: Generating Burrow ABI for contract {}", contract.name);
+                eprintln!("info: Generating Ethereum ABI for contract {}", contract.name);
             }
 
             let abi = ethabi::gen_abi(contract);

+ 55 - 13
src/bin/solang.rs

@@ -61,7 +61,7 @@ fn main() {
                 .help("Target to build for")
                 .long("target")
                 .takes_value(true)
-                .possible_values(&["substrate", "burrow"])
+                .possible_values(&["substrate", "burrow", "ewasm"])
                 .default_value("substrate")
         )
         .arg(
@@ -103,10 +103,12 @@ fn main() {
         match matches.value_of("TARGET") {
             Some("substrate") => solang::Target::Substrate,
             Some("burrow") => solang::Target::Burrow,
+            Some("ewasm") => solang::Target::Ewasm,
             _ => unreachable!()
         }
     };
     let verbose = matches.is_present("VERBOSE");
+    let opt = matches.value_of("OPT").unwrap();
 
     if verbose {
         eprintln!("info: Solang commit {}", env!("GIT_HASH"));
@@ -147,31 +149,71 @@ fn main() {
                 eprintln!("info: Generating LLVM IR for contract {} with target {}", resolved_contract.name, resolved_contract.target);
             }
 
-            let contract = resolved_contract.emit(&context, &filename);
+            let contract = resolved_contract.emit(&context, &filename, &opt);
 
             if let Some("llvm") = matches.value_of("EMIT") {
-                let llvm_filename = output_file(&contract.name, "ll");
+                if let Some(runtime) = &contract.runtime {
+                    // In Ethereum, an ewasm contract has two parts, deployer and runtime. The deployer code returns the runtime wasm
+                    // as a byte string
+                    let llvm_filename = output_file(&format!("{}_deploy", contract.name), "ll");
 
-                if verbose {
-                    eprintln!("info: Saving LLVM {} for contract {}", llvm_filename.display(), contract.name);
-                }
+                    if verbose {
+                        eprintln!("info: Saving deployer LLVM {} for contract {}", llvm_filename.display(), contract.name);
+                    }
+
+                    contract.dump_llvm(&llvm_filename).unwrap();
+
+                    let llvm_filename = output_file(&format!("{}_runtime", contract.name), "ll");
 
-                contract.dump_llvm(&llvm_filename).unwrap();
+                    if verbose {
+                        eprintln!("info: Saving runtime LLVM {} for contract {}", llvm_filename.display(), contract.name);
+                    }
+
+                    runtime.dump_llvm(&llvm_filename).unwrap();
+                } else {
+                    let llvm_filename = output_file(&contract.name, "ll");
+
+                    if verbose {
+                        eprintln!("info: Saving LLVM {} for contract {}", llvm_filename.display(), contract.name);
+                    }
+
+                    contract.dump_llvm(&llvm_filename).unwrap();
+                }
                 continue;
             }
 
             if let Some("bc") = matches.value_of("EMIT") {
-                let bc_filename = output_file(&contract.name, "bc");
+                // In Ethereum, an ewasm contract has two parts, deployer and runtime. The deployer code returns the runtime wasm
+                // as a byte string
+                if let Some(runtime) = &contract.runtime {
+                    let bc_filename = output_file(&format!("{}_deploy", contract.name), "bc");
 
-                if verbose {
-                    eprintln!("info: Saving LLVM BC {} for contract {}", bc_filename.display(), contract.name);
-                }
+                    if verbose {
+                        eprintln!("info: Saving deploy LLVM BC {} for contract {}", bc_filename.display(), contract.name);
+                    }
+
+                    contract.bitcode(&bc_filename);
+
+                    let bc_filename = output_file(&format!("{}_runtime", contract.name), "bc");
 
-                contract.bitcode(&bc_filename);
+                    if verbose {
+                        eprintln!("info: Saving runtime LLVM BC {} for contract {}", bc_filename.display(), contract.name);
+                    }
+
+                    runtime.bitcode(&bc_filename);
+                } else {
+                    let bc_filename = output_file(&contract.name, "bc");
+
+                    if verbose {
+                        eprintln!("info: Saving LLVM BC {} for contract {}", bc_filename.display(), contract.name);
+                    }
+
+                    contract.bitcode(&bc_filename);
+                }
                 continue;
             }
 
-            let obj = match contract.wasm(matches.value_of("OPT").unwrap()) {
+            let obj = match contract.wasm(&opt) {
                 Ok(o) => o,
                 Err(s) => {
                     println!("error: {}", s);

+ 1 - 1
src/emit/burrow.rs

@@ -17,7 +17,7 @@ pub struct BurrowTarget {
 
 impl BurrowTarget {
     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 mut c = Contract::new(context, contract, filename, None);
         let b = BurrowTarget{};
 
         // externals

+ 720 - 0
src/emit/ewasm.rs

@@ -0,0 +1,720 @@
+
+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 inkwell::attributes::{Attribute, AttributeLoc};
+
+use std::collections::HashMap;
+
+use super::{TargetRuntime, Contract};
+
+pub struct EwasmTarget {
+    /// This field maps a storage slot to llvm global
+    slot_mapping: HashMap<usize, usize>
+}
+
+impl EwasmTarget {
+    pub fn build<'a>(context: &'a Context, contract: &'a resolver::Contract, filename: &'a str, opt: &str) -> Contract<'a> {
+        // first emit runtime code
+        let mut runtime_code = Contract::new(context, contract, filename, None);
+        let mut b = EwasmTarget{
+            slot_mapping: HashMap::new()
+        };
+
+        // externals
+        b.storage_keys(&mut runtime_code);
+        b.declare_externals(&mut runtime_code);
+
+        // FIXME: this also emits the constructors. We can either rely on lto linking
+        // to optimize them away or do not emit them.
+        runtime_code.emit_functions(&b);
+
+        b.emit_function_dispatch(&runtime_code);
+
+        let runtime_bs = runtime_code.wasm(opt).unwrap();
+
+        // Now we have the runtime code, create the deployer
+        let mut deploy_code = Contract::new(context, contract, filename, Some(Box::new(runtime_code)));
+        let mut b = EwasmTarget{
+            slot_mapping: HashMap::new()
+        };
+
+        // externals
+        b.storage_keys(&mut deploy_code);
+        b.declare_externals(&mut deploy_code);
+
+        // FIXME: this emits the constructors, as well as the functions. In Ethereum Solidity,
+        // no functions can be called from the constructor. We should either disallow this too
+        // and not emit functions, or use lto linking to optimize any unused functions away.
+        deploy_code.emit_functions(&b);
+
+        b.emit_constructor_dispatch(&mut deploy_code, &runtime_bs);
+
+        deploy_code
+    }
+
+    fn storage_keys<'a>(&mut self, contract: &'a mut Contract) {
+        for var in &contract.ns.variables {
+            if let resolver::ContractVariableType::Storage(slot) = var.var {
+                let mut key = slot.to_be_bytes().to_vec();
+
+                // pad to the left
+                let mut padding = Vec::new();
+
+                padding.resize(32 - key.len(), 0u8);
+
+                key = padding.into_iter().chain(key.into_iter()).collect();
+
+                let v = contract.emit_global_string(&format!("sol::key::{}", var.name), &key, true);
+
+                self.slot_mapping.insert(slot, v);
+            }
+        }
+    }
+
+    fn main_prelude<'a>(&self, contract: &'a Contract, function: FunctionValue) -> (PointerValue<'a>, IntValue<'a>) {
+        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 args_length = contract.builder.build_call(
+            contract.module.get_function("getCallDataSize").unwrap(),
+            &[],
+            "calldatasize").try_as_basic_value().left().unwrap();
+
+        let args = contract.builder.build_call(
+            contract.module.get_function("__malloc").unwrap(),
+            &[args_length],
+            ""
+        ).try_as_basic_value().left().unwrap().into_pointer_value();
+
+        contract.builder.build_call(
+            contract.module.get_function("callDataCopy").unwrap(),
+            &[
+                args.into(),
+                contract.context.i32_type().const_zero().into(),
+                args_length.into(),
+            ],
+            ""
+        );
+
+        let args = contract.builder.build_pointer_cast(args,
+            contract.context.i32_type().ptr_type(AddressSpace::Generic), "").into();
+
+        (args, args_length.into_int_value())
+    }
+
+    fn declare_externals(&self, contract: &mut Contract) {
+        let ret = contract.context.void_type();
+        let args: Vec<BasicTypeEnum> = vec![
+            contract.context.i8_type().ptr_type(AddressSpace::Generic).into(),
+            contract.context.i8_type().ptr_type(AddressSpace::Generic).into(),
+        ];
+
+        let ftype = ret.fn_type(&args, false);
+
+        contract.module.add_function("storageStore", ftype, Some(Linkage::External));
+        contract.module.add_function("storageLoad", ftype, Some(Linkage::External));
+
+        contract.module.add_function(
+            "getCallDataSize",
+            contract.context.i32_type().fn_type(&[], false),
+            Some(Linkage::External)
+        );
+
+        contract.module.add_function(
+            "callDataCopy",
+            contract.context.void_type().fn_type(&[
+                contract.context.i8_type().ptr_type(AddressSpace::Generic).into(),  // resultOffset
+                contract.context.i32_type().into(), // dataOffset
+                contract.context.i32_type().into(), // length
+            ], false),
+            Some(Linkage::External)
+        );
+
+        let noreturn = contract.context.create_enum_attribute(Attribute::get_named_enum_kind_id("noreturn"), 0);
+
+        // mark as noreturn
+        contract.module.add_function(
+            "finish",
+            contract.context.void_type().fn_type(&[
+                contract.context.i8_type().ptr_type(AddressSpace::Generic).into(), // data_ptr
+                contract.context.i32_type().into(), // data_len
+            ], false),
+            Some(Linkage::External)
+        ).add_attribute(AttributeLoc::Function, noreturn);
+
+        // mark as noreturn
+        contract.module.add_function(
+            "revert",
+            contract.context.void_type().fn_type(&[
+                contract.context.i8_type().ptr_type(AddressSpace::Generic).into(), // data_ptr
+                contract.context.i32_type().into(), // data_len
+            ], false),
+            Some(Linkage::External)
+        ).add_attribute(AttributeLoc::Function, noreturn);
+    }
+
+    fn emit_constructor_dispatch(&self, contract: &mut Contract, runtime: &[u8]) {
+        let initializer = contract.emit_initializer(self);
+
+        // create start function
+        let ret = contract.context.void_type();
+        let ftype = ret.fn_type(&[], false);
+        let function = contract.module.add_function("main", ftype, None);
+
+        // FIXME: If there is no constructor, do not copy the calldata (but check calldatasize == 0)
+        let (argsdata, length) = self.main_prelude(contract, function);
+
+        // init our storage vars
+        contract.builder.build_call(initializer, &[], "");
+
+        if let Some(con) = contract.ns.constructors.get(0) {
+            let mut args = Vec::new();
+
+            // insert abi decode
+            self.abi_decode(
+                contract,
+                function,
+                &mut args,
+                argsdata,
+                length,
+                con,
+            );
+
+            contract.builder.build_call(contract.constructors[0], &args, "");
+        }
+
+        // the deploy code should return the runtime wasm code
+        let runtime_code = contract.emit_global_string("runtime_code", runtime, true);
+
+        let runtime_ptr = contract.builder.build_pointer_cast(
+            contract.globals[runtime_code].as_pointer_value(),
+            contract.context.i8_type().ptr_type(AddressSpace::Generic),
+            "runtime_code");
+
+        contract.builder.build_call(
+            contract.module.get_function("finish").unwrap(),
+            &[
+                runtime_ptr.into(),
+                contract.context.i32_type().const_int(runtime.len() as u64, false).into()
+            ],
+            ""
+        );
+
+        // since finish is marked noreturn, this should be optimized away
+        // however it is needed to create valid LLVM IR
+        contract.builder.build_unreachable();
+    }
+
+    fn emit_function_dispatch(&self, contract: &Contract) {
+        // create start function
+        let ret = contract.context.void_type();
+        let ftype = ret.fn_type(&[], false);
+        let function = contract.module.add_function("main", ftype, None);
+
+        let (argsdata, argslen) = self.main_prelude(contract, function);
+
+        let fallback_block = contract.context.append_basic_block(function, "fallback");
+
+        contract.emit_function_dispatch(&contract.ns.functions, &contract.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],
+                    &[],
+                    "");
+
+                contract.builder.build_return(None);
+            }
+            None => {
+                contract.builder.build_unreachable();
+            },
+        }
+    }
+
+    fn emit_abi_encode_single_val(
+        &self,
+        contract: &Contract,
+        ty: &ast::PrimitiveType,
+        dest: PointerValue,
+        val: BasicValueEnum,
+    ) {
+        match ty {
+            ast::PrimitiveType::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.into_int_value(),
+                    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::PrimitiveType::Int(8) | ast::PrimitiveType::Uint(8) => {
+                let signval = if let ast::PrimitiveType::Int(8) = ty {
+                    let negative = contract.builder.build_int_compare(IntPredicate::SLT,
+                            val.into_int_value(), 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::PrimitiveType::Address |
+            ast::PrimitiveType::Uint(_) |
+            ast::PrimitiveType::Int(_) => {
+                let n = match ty {
+                    ast::PrimitiveType::Address => 160,
+                    ast::PrimitiveType::Uint(b) => *b,
+                    ast::PrimitiveType::Int(b) => *b,
+                    _ => unreachable!()
+                };
+
+                // first clear/set the upper bits
+                if n < 256 {
+                    let signval = if let ast::PrimitiveType::Int(8) = ty {
+                        let negative = contract.builder.build_int_compare(IntPredicate::SLT,
+                                val.into_int_value(), 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 = if ty.stack_based() {
+                    val.into_pointer_value()
+                } else {
+                    let store = contract.builder.build_alloca(int_type, "stack");
+
+                    contract.builder.build_store(store, val);
+
+                    store
+                };
+
+                contract.builder.build_call(
+                    contract.module.get_function("__leNtobe32").unwrap(),
+                    &[
+                        contract.builder.build_pointer_cast(store,
+                            contract.context.i8_type().ptr_type(AddressSpace::Generic),
+                            "store").into(),
+                        contract.builder.build_pointer_cast(dest,
+                            contract.context.i8_type().ptr_type(AddressSpace::Generic),
+                            "dest").into(),
+                        contract.builder.build_int_truncate(type_size,
+                            contract.context.i32_type(), "").into()
+                    ],
+                    "");
+            }
+            ast::PrimitiveType::Bytes(1) => {
+                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() ],
+                    "");
+
+                contract.builder.build_store(dest8, val);
+            },
+            ast::PrimitiveType::Bytes(b) => {
+                // first clear/set the upper bits
+                if *b < 32 {
+                    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() ],
+                        "");
+                }
+
+                // no need to allocate space for each uint64
+                // allocate enough for type
+                let int_type = contract.context.custom_width_int_type(*b as u32 * 8);
+                let type_size = int_type.size_of();
+
+                let store = if ty.stack_based() {
+                    val.into_pointer_value()
+                } else {
+                    let store = contract.builder.build_alloca(int_type, "stack");
+
+                    contract.builder.build_store(store, val);
+
+                    store
+                };
+
+                contract.builder.build_call(
+                    contract.module.get_function("__leNtobeN").unwrap(),
+                    &[
+                        contract.builder.build_pointer_cast(store,
+                            contract.context.i8_type().ptr_type(AddressSpace::Generic),
+                            "store").into(),
+                        contract.builder.build_pointer_cast(dest,
+                            contract.context.i8_type().ptr_type(AddressSpace::Generic),
+                            "dest").into(),
+                        contract.builder.build_int_truncate(type_size,
+                            contract.context.i32_type(), "").into()
+                    ],
+                    "");
+            }
+            _ => unimplemented!(),
+        }
+    }
+}
+
+impl TargetRuntime for EwasmTarget {
+    fn set_storage<'a>(&self, contract: &'a Contract, _function: FunctionValue, slot: u32, dest: inkwell::values::PointerValue<'a>) {
+        let key = contract.globals[self.slot_mapping[&(slot as usize)]];
+        // FIXME: no need to alloca for 256 bit value
+        let value = contract.builder.build_alloca(contract.context.custom_width_int_type(160), "value");
+
+        let value8 = contract.builder.build_pointer_cast(value,
+            contract.context.i8_type().ptr_type(AddressSpace::Generic),
+            "value8");
+
+        contract.builder.build_call(
+            contract.module.get_function("__bzero8").unwrap(),
+            &[ value8.into(), contract.context.i32_type().const_int(4, false).into() ],
+            "");
+
+        let val = contract.builder.build_load(dest, "value");
+
+        contract.builder.build_store(
+            contract.builder.build_pointer_cast(value, dest.get_type(), ""),
+            val);
+
+        contract.builder.build_call(
+            contract.module.get_function("storageLoad").unwrap(),
+            &[
+                contract.builder.build_pointer_cast(key.as_pointer_value(),
+                    contract.context.i8_type().ptr_type(AddressSpace::Generic), "").into(),
+                value8.into()
+            ], "");
+    }
+
+    fn get_storage<'a>(&self, contract: &'a Contract, _function: FunctionValue, slot: u32, dest: inkwell::values::PointerValue<'a>) {
+        let key = contract.globals[self.slot_mapping[&(slot as usize)]];
+        // FIXME: no need to alloca for 256 bit value
+        let value = contract.builder.build_alloca(contract.context.custom_width_int_type(256), "value");
+
+        contract.builder.build_call(
+            contract.module.get_function("storageLoad").unwrap(),
+            &[
+                contract.builder.build_pointer_cast(key.as_pointer_value(),
+                    contract.context.i8_type().ptr_type(AddressSpace::Generic), "").into(),
+                contract.builder.build_pointer_cast(value,
+                    contract.context.i8_type().ptr_type(AddressSpace::Generic), "").into()
+            ], "");
+
+        let val = contract.builder.build_load(
+            contract.builder.build_pointer_cast(value, dest.get_type(), ""),
+            "");
+
+        contract.builder.build_store(dest, val);
+    }
+
+    fn return_empty_abi(&self, contract: &Contract) {
+        contract.builder.build_call(
+            contract.module.get_function("finish").unwrap(),
+            &[
+                contract.context.i8_type().ptr_type(AddressSpace::Generic).const_zero().into(),
+                contract.context.i32_type().const_zero().into(),
+            ],
+            ""
+        );
+
+        contract.builder.build_return(None);
+    }
+
+    fn return_abi<'b>(&self, contract: &'b Contract, data: PointerValue<'b>, length: IntValue) {
+        contract.builder.build_call(
+            contract.module.get_function("finish").unwrap(),
+            &[ data.into(), length.into() ],
+            ""
+        );
+
+        contract.builder.build_return(None);
+    }
+
+    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 mut data = contract.builder.build_call(
+            contract.module.get_function("__malloc").unwrap(),
+            &[contract.context.i32_type().const_int(32 * args.len() as u64, false).into()],
+            ""
+        ).try_as_basic_value().left().unwrap().into_pointer_value();
+
+        // malloc returns u8*
+        for (i, arg) in spec.returns.iter().enumerate() {
+            // insert abi decode
+            let ty = match arg.ty {
+                resolver::Type::Primitive(e) => e,
+                resolver::Type::Enum(n) => contract.ns.enums[n].ty,
+                resolver::Type::Noreturn => unreachable!(),
+            };
+
+            self.emit_abi_encode_single_val(contract, &ty, data, args[i]);
+
+            data = unsafe {
+                contract.builder.build_gep(
+                    data,
+                    &[ contract.context.i32_type().const_int(32, false).into()],
+                    &format!("abi{}", i)
+                )
+            };
+        }
+
+        (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::Type::Primitive(e) => e,
+                resolver::Type::Enum(n) => &contract.ns.enums[*n].ty,
+                resolver::Type::Noreturn => unreachable!(),
+            };
+
+            args.push(match ty {
+                ast::PrimitiveType::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::NE,
+                        contract.builder.build_load(bool_ptr, "abi_bool").into_int_value(),
+                        contract.context.i64_type().const_zero(), "bool").into()
+                }
+                ast::PrimitiveType::Uint(8) | ast::PrimitiveType::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::PrimitiveType::Address => {
+                    let int_type = contract.context.custom_width_int_type(160);
+                    let type_size = int_type.size_of();
+
+                    let store = contract.builder.build_alloca(int_type, "address");
+
+                    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()
+                        ],
+                        ""
+                    );
+
+                    store.into()
+                }
+                ast::PrimitiveType::Uint(n) | ast::PrimitiveType::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()
+                    }
+                }
+                ast::PrimitiveType::Bytes(1) => {
+                    contract.builder.build_load(
+                        contract.builder.build_pointer_cast(
+                            data,
+                            contract.context.i8_type().ptr_type(AddressSpace::Generic), "").into(),
+                        "bytes1")
+                },
+                ast::PrimitiveType::Bytes(b) => {
+                    let int_type = contract.context.custom_width_int_type(*b as u32 * 8);
+                    let type_size = int_type.size_of();
+
+                    let store = contract.builder.build_alloca(int_type, "stack");
+
+                    contract.builder.build_call(
+                        contract.module.get_function("__beNtoleN").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 *b <= 8 {
+                        contract.builder.build_load(store, &format!("bytes{}", *b))
+                    } 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);
+    }
+
+    fn assert_failure<'b>(&self, contract: &'b Contract) {
+        contract.builder.build_call(
+            contract.module.get_function("revert").unwrap(),
+            &[
+                contract.context.i8_type().ptr_type(AddressSpace::Generic).const_zero().into(),
+                contract.context.i32_type().const_zero().into(),
+            ],
+            ""
+        );
+
+        // since revert is marked noreturn, this should be optimized away
+        // however it is needed to create valid LLVM IR
+        contract.builder.build_unreachable();
+    }
+}

+ 6 - 2
src/emit/mod.rs

@@ -25,6 +25,7 @@ const WASMTRIPLE: &str = "wasm32-unknown-unknown-wasm";
 
 mod burrow;
 mod substrate;
+mod ewasm;
 
 lazy_static::lazy_static! {
     static ref LLVM_INIT: () = {
@@ -72,6 +73,7 @@ pub trait TargetRuntime {
 pub struct Contract<'a> {
     pub name: String,
     pub module: Module<'a>,
+    pub runtime: Option<Box<Contract<'a>>>,
     builder: Builder<'a>,
     context: &'a Context,
     target: Target,
@@ -82,10 +84,11 @@ pub struct Contract<'a> {
 }
 
 impl<'a> Contract<'a> {
-    pub fn build(context: &'a Context, contract: &'a resolver::Contract, filename: &'a str) -> Self {
+    pub fn build(context: &'a Context, contract: &'a resolver::Contract, filename: &'a str, opt: &str) -> Self {
         match contract.target {
             super::Target::Burrow => burrow::BurrowTarget::build(context, contract, filename),
             super::Target::Substrate => substrate::SubstrateTarget::build(context, contract, filename),
+            super::Target::Ewasm => ewasm::EwasmTarget::build(context, contract, filename, opt),
         }
     }
 
@@ -118,7 +121,7 @@ impl<'a> Contract<'a> {
         Ok(())
     }
 
-    pub fn new(context: &'a Context, contract: &'a resolver::Contract, filename: &'a str) -> Self {
+    pub fn new(context: &'a Context, contract: &'a resolver::Contract, filename: &'a str, runtime: Option<Box<Contract<'a>>>) -> Self {
         lazy_static::initialize(&LLVM_INIT);
 
         let target = Target::from_triple(WASMTRIPLE).unwrap();
@@ -134,6 +137,7 @@ impl<'a> Contract<'a> {
         Contract {
             name: contract.name.to_owned(),
             module: module,
+            runtime: runtime,
             builder: context.create_builder(),
             target: target,
             context: context,

+ 1 - 1
src/emit/substrate.rs

@@ -21,7 +21,7 @@ const ADDRESS_LENGTH: u64 = 20;
 
 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 mut c = Contract::new(context, contract, filename, None);
         let mut b = SubstrateTarget{
             slot_mapping: HashMap::new()
         };

+ 8 - 5
src/lib.rs

@@ -28,14 +28,17 @@ pub enum Target {
     /// Parity Substrate, see https://substrate.dev/
     Substrate,
     /// Hyperledger Burrow, see https://github.com/hyperledger/burrow/
-    Burrow
+    Burrow,
+    /// Ethereum ewasm, see https://github.com/ewasm/design
+    Ewasm
 }
 
 impl fmt::Display for Target {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match self {
             Target::Substrate => write!(f, "Substrate"),
-            Target::Burrow => write!(f, "Burrow")
+            Target::Burrow => write!(f, "Burrow"),
+            Target::Ewasm => write!(f, "ewasm")
         }
     }
 }
@@ -47,7 +50,7 @@ impl fmt::Display for Target {
 /// compiler warnings, errors and informational messages are also provided.
 ///
 /// The ctx is the inkwell llvm context.
-pub fn compile(src: &str, filename: &str, target: &Target) -> (Vec<(Vec<u8>, String)>, Vec<output::Output>) {
+pub fn compile(src: &str, filename: &str, opt: &str, target: &Target) -> (Vec<(Vec<u8>, String)>, Vec<output::Output>) {
     let ctx = inkwell::context::Context::create();
 
     let ast = match parser::parse(src) {
@@ -64,9 +67,9 @@ pub fn compile(src: &str, filename: &str, target: &Target) -> (Vec<(Vec<u8>, Str
         let (abistr, _) = abi::generate_abi(c, false);
 
         // codegen
-        let contract = emit::Contract::build(&ctx, c, filename);
+        let contract = emit::Contract::build(&ctx, c, filename, opt);
 
-        let obj = contract.wasm("default").expect("llvm wasm emit should work");
+        let obj = contract.wasm(opt).expect("llvm wasm emit should work");
 
         let bc = link::link(&obj, target);
 

+ 14 - 1
src/link.rs

@@ -30,6 +30,7 @@ pub fn link(input: &[u8], target: &Target) -> Vec<u8> {
     // or something like that.
     let allowed_externs = |name: &str| {
         match target {
+            Target::Ewasm => name == "main",
             Target::Burrow => name == "constructor" || name == "function",
             Target::Substrate => name == "deploy" || name == "call"
         }
@@ -77,12 +78,24 @@ pub fn link(input: &[u8], target: &Target) -> Vec<u8> {
             if imports[ind].field().starts_with("__") {
                 imports.remove(ind);
             } else {
+                if let Target::Ewasm = target {
+                    let module = imports[ind].module_mut();
+                    
+                    *module = "ethereum".to_string();
+                }
+
                 ind += 1;
             }
         }
 
+        let module = if let Target::Ewasm = target {
+            "memory"
+        } else {
+            "env"
+        };
+
         imports.push(ImportEntry::new(
-            "env".into(),
+            module.into(),
             "memory".into(),
             elements::External::Memory(elements::MemoryType::new(2, Some(2))),
         ));

+ 2 - 2
src/resolver/mod.rs

@@ -387,8 +387,8 @@ impl Contract {
         abi::generate_abi(self, verbose)
     }
 
-    pub fn emit<'a>(&'a self, context: &'a inkwell::context::Context, filename: &'a str) -> emit::Contract {
-        emit::Contract::build(context, self, filename)
+    pub fn emit<'a>(&'a self, context: &'a inkwell::context::Context, filename: &'a str, opt: &str) -> emit::Contract {
+        emit::Contract::build(context, self, filename, opt)
     }
 }
 

+ 1 - 1
tests/burrow.rs

@@ -138,7 +138,7 @@ impl TestRuntime {
 }
 
 fn build_solidity(src: &'static str) -> (TestRuntime, ContractStorage) {
-    let (mut res, errors) = compile(src, "test.sol", &Target::Burrow);
+    let (mut res, errors) = compile(src, "test.sol", "default", &Target::Burrow);
 
     output::print_messages("test.sol", src, &errors, false);
 

+ 1 - 1
tests/substrate.rs

@@ -217,7 +217,7 @@ impl TestRuntime {
 }
 
 pub fn build_solidity(src: &'static str) -> (TestRuntime, ContractStorage) {
-    let (mut res, errors) = compile(src, "test.sol", &Target::Substrate);
+    let (mut res, errors) = compile(src, "test.sol", "default", &Target::Substrate);
 
     output::print_messages("test.sol", src, &errors, false);