Ver Fonte

Only emit in codegen when needed (#1100)

The contract binary is always generated in codegen, but never used in
codegen. We can replace type(contract).runtimeCode with the binary code
in codegen, which removes the need for Expression::CodeLiteral in codegen.
We only generate this if needed.

This has an added bonus for debugging issues: if there is an issue in emit,
then --emit cfg will still work and not crash before emitting the cfg
(unless type(foo).runtimeCode is present).

Signed-off-by: Sean Young <sean@mess.org>
Sean Young há 2 anos atrás
pai
commit
144c43207d

+ 1 - 2
Cargo.toml

@@ -45,7 +45,7 @@ funty = "2.0"
 itertools = "0.10"
 itertools = "0.10"
 num-rational = "0.4"
 num-rational = "0.4"
 indexmap = "1.8"
 indexmap = "1.8"
-once_cell = "1.10"
+once_cell = "1.16"
 solang-parser = { path = "solang-parser", version = "0.2.0" }
 solang-parser = { path = "solang-parser", version = "0.2.0" }
 codespan-reporting = "0.11"
 codespan-reporting = "0.11"
 phf = { version = "0.11", features = ["macros"] }
 phf = { version = "0.11", features = ["macros"] }
@@ -58,7 +58,6 @@ parity-scale-codec = "3.1"
 ink = "4.0.0-beta"
 ink = "4.0.0-beta"
 scale-info = "2.3"
 scale-info = "2.3"
 
 
-
 [dev-dependencies]
 [dev-dependencies]
 num-derive = "0.3"
 num-derive = "0.3"
 ethabi = "17.0"
 ethabi = "17.0"

+ 7 - 13
src/bin/solang.rs

@@ -538,27 +538,21 @@ fn process_file(
         let context = inkwell::context::Context::create();
         let context = inkwell::context::Context::create();
         let filename_string = filename.to_string_lossy();
         let filename_string = filename.to_string_lossy();
 
 
-        let binary = resolved_contract.emit(
-            &ns,
-            &context,
-            &filename_string,
-            opt.opt_level.into(),
-            opt.math_overflow_check,
-            opt.generate_debug_information,
-            opt.log_api_return_codes,
-        );
+        let binary = resolved_contract.binary(&ns, &context, &filename_string, opt);
 
 
         if save_intermediates(&binary, matches) {
         if save_intermediates(&binary, matches) {
             continue;
             continue;
         }
         }
 
 
+        let code = binary.code(Generate::Linked).expect("llvm build");
+
         if matches.contains_id("STD-JSON") {
         if matches.contains_id("STD-JSON") {
             json_contracts.insert(
             json_contracts.insert(
                 binary.name.to_owned(),
                 binary.name.to_owned(),
                 JsonContract {
                 JsonContract {
                     abi: abi::ethereum::gen_abi(contract_no, &ns),
                     abi: abi::ethereum::gen_abi(contract_no, &ns),
                     ewasm: Some(EwasmContract {
                     ewasm: Some(EwasmContract {
-                        wasm: hex::encode_upper(&resolved_contract.code),
+                        wasm: hex::encode_upper(code),
                     }),
                     }),
                     minimum_space: None,
                     minimum_space: None,
                 },
                 },
@@ -575,10 +569,10 @@ fn process_file(
             }
             }
 
 
             let mut file = create_file(&bin_filename);
             let mut file = create_file(&bin_filename);
-            file.write_all(&resolved_contract.code).unwrap();
 
 
-            let (abi_bytes, abi_ext) =
-                abi::generate_abi(contract_no, &ns, &resolved_contract.code, verbose);
+            file.write_all(&code).unwrap();
+
+            let (abi_bytes, abi_ext) = abi::generate_abi(contract_no, &ns, &code, verbose);
             let abi_filename = output_file(matches, &binary.name, abi_ext);
             let abi_filename = output_file(matches, &binary.name, abi_ext);
 
 
             if verbose {
             if verbose {

+ 0 - 9
src/codegen/cfg.rs

@@ -816,15 +816,6 @@ impl ControlFlowGraph {
             Expression::InternalFunctionCfg(cfg_no) => {
             Expression::InternalFunctionCfg(cfg_no) => {
                 format!("function {}", contract.cfg[*cfg_no].name)
                 format!("function {}", contract.cfg[*cfg_no].name)
             }
             }
-            Expression::CodeLiteral(_, contract_no, runtime) => format!(
-                "({} code contract {})",
-                if *runtime {
-                    "runtimeCode"
-                } else {
-                    "creationCode"
-                },
-                ns.contracts[*contract_no].name,
-            ),
             Expression::ReturnData(_) => "(external call return data)".to_string(),
             Expression::ReturnData(_) => "(external call return data)".to_string(),
             Expression::Cast(_, ty, e) => format!(
             Expression::Cast(_, ty, e) => format!(
                 "{}({})",
                 "{}({})",

+ 0 - 1
src/codegen/constant_folding.rs

@@ -1166,7 +1166,6 @@ fn expression(
         | Expression::RationalNumberLiteral(..)
         | Expression::RationalNumberLiteral(..)
         | Expression::BoolLiteral(..)
         | Expression::BoolLiteral(..)
         | Expression::BytesLiteral(..)
         | Expression::BytesLiteral(..)
-        | Expression::CodeLiteral(..)
         | Expression::FunctionArg(..) => (expr.clone(), true),
         | Expression::FunctionArg(..) => (expr.clone(), true),
 
 
         Expression::ReturnData(_)
         Expression::ReturnData(_)

+ 12 - 3
src/codegen/expression.rs

@@ -722,9 +722,7 @@ pub fn expression(
         ast::Expression::BytesLiteral(loc, ty, arr) => {
         ast::Expression::BytesLiteral(loc, ty, arr) => {
             Expression::BytesLiteral(*loc, ty.clone(), arr.clone())
             Expression::BytesLiteral(*loc, ty.clone(), arr.clone())
         }
         }
-        ast::Expression::CodeLiteral(loc, pos, boolean) => {
-            Expression::CodeLiteral(*loc, *pos, *boolean)
-        }
+        ast::Expression::CodeLiteral(loc, contract_no, _) => code(loc, *contract_no, ns, opt),
         ast::Expression::NumberLiteral(loc, ty, n) => {
         ast::Expression::NumberLiteral(loc, ty, n) => {
             Expression::NumberLiteral(*loc, ty.clone(), n.clone())
             Expression::NumberLiteral(*loc, ty.clone(), n.clone())
         }
         }
@@ -2927,3 +2925,14 @@ pub(super) fn assert_failure(
         },
         },
     )
     )
 }
 }
+
+/// Generate the binary code for a contract
+fn code(loc: &Loc, contract_no: usize, ns: &Namespace, opt: &Options) -> Expression {
+    let contract = &ns.contracts[contract_no];
+
+    let code = contract.emit(ns, opt);
+
+    let size = Expression::NumberLiteral(*loc, Type::Uint(32), code.len().into());
+
+    Expression::AllocDynamicBytes(*loc, Type::DynamicBytes, size.into(), Some(code))
+}

+ 1 - 35
src/codegen/mod.rs

@@ -28,8 +28,6 @@ use self::{
     expression::expression,
     expression::expression,
     vartable::Vartable,
     vartable::Vartable,
 };
 };
-#[cfg(feature = "llvm")]
-use crate::emit::Generate;
 use crate::sema::ast::{
 use crate::sema::ast::{
     FormatArg, Function, Layout, Namespace, RetrieveType, StringLocation, Type,
     FormatArg, Function, Layout, Namespace, RetrieveType, StringLocation, Type,
 };
 };
@@ -148,34 +146,6 @@ pub fn codegen(ns: &mut Namespace, opt: &Options) {
                 return;
                 return;
             }
             }
 
 
-            // EVM has no emitter implemented yet
-            if ns.target != Target::EVM {
-                #[cfg(not(feature = "llvm"))]
-                panic!("LLVM feature is not enabled");
-                #[cfg(feature = "llvm")]
-                {
-                    let context = inkwell::context::Context::create();
-
-                    let filename = ns.files[0].path.to_string_lossy();
-
-                    let binary = ns.contracts[contract_no].emit(
-                        ns,
-                        &context,
-                        &filename,
-                        opt.opt_level.into(),
-                        opt.math_overflow_check,
-                        opt.generate_debug_information,
-                        opt.log_api_return_codes,
-                    );
-
-                    let code = binary.code(Generate::Linked).expect("llvm build");
-
-                    drop(binary);
-
-                    ns.contracts[contract_no].code = code;
-                }
-            }
-
             contracts_done[contract_no] = true;
             contracts_done[contract_no] = true;
         }
         }
     }
     }
@@ -391,7 +361,6 @@ pub enum Expression {
     BytesCast(pt::Loc, Type, Type, Box<Expression>),
     BytesCast(pt::Loc, Type, Type, Box<Expression>),
     BytesLiteral(pt::Loc, Type, Vec<u8>),
     BytesLiteral(pt::Loc, Type, Vec<u8>),
     Cast(pt::Loc, Type, Box<Expression>),
     Cast(pt::Loc, Type, Box<Expression>),
-    CodeLiteral(pt::Loc, usize, bool),
     Complement(pt::Loc, Type, Box<Expression>),
     Complement(pt::Loc, Type, Box<Expression>),
     ConstArrayLiteral(pt::Loc, Type, Vec<u32>, Vec<Expression>),
     ConstArrayLiteral(pt::Loc, Type, Vec<u32>, Vec<Expression>),
     UnsignedDivide(pt::Loc, Type, Box<Expression>, Box<Expression>),
     UnsignedDivide(pt::Loc, Type, Box<Expression>, Box<Expression>),
@@ -501,7 +470,6 @@ impl CodeLocation for Expression {
             | Expression::StringCompare(loc, ..)
             | Expression::StringCompare(loc, ..)
             | Expression::StringConcat(loc, ..)
             | Expression::StringConcat(loc, ..)
             | Expression::FunctionArg(loc, ..)
             | Expression::FunctionArg(loc, ..)
-            | Expression::CodeLiteral(loc, ..)
             | Expression::List(loc, ..)
             | Expression::List(loc, ..)
             | Expression::ShiftRight(loc, ..)
             | Expression::ShiftRight(loc, ..)
             | Expression::ShiftLeft(loc, ..)
             | Expression::ShiftLeft(loc, ..)
@@ -617,9 +585,7 @@ impl Recurse for Expression {
 impl RetrieveType for Expression {
 impl RetrieveType for Expression {
     fn ty(&self) -> Type {
     fn ty(&self) -> Type {
         match self {
         match self {
-            Expression::AbiEncode { .. }
-            | Expression::CodeLiteral(..)
-            | Expression::ReturnData(_) => Type::DynamicBytes,
+            Expression::AbiEncode { .. } | Expression::ReturnData(_) => Type::DynamicBytes,
             Expression::Builtin(_, returns, ..) => {
             Expression::Builtin(_, returns, ..) => {
                 assert_eq!(returns.len(), 1);
                 assert_eq!(returns.len(), 1);
                 returns[0].clone()
                 returns[0].clone()

+ 3 - 2
src/codegen/yul/tests/expression.rs

@@ -12,6 +12,7 @@ use crate::sema::yul::ast;
 use crate::sema::yul::ast::YulSuffix;
 use crate::sema::yul::ast::YulSuffix;
 use crate::{sema, Target};
 use crate::{sema, Target};
 use num_bigint::{BigInt, Sign};
 use num_bigint::{BigInt, Sign};
+use once_cell::unsync::OnceCell;
 use solang_parser::pt::{ContractTy, Loc, StorageLocation, Visibility};
 use solang_parser::pt::{ContractTy, Loc, StorageLocation, Visibility};
 
 
 #[test]
 #[test]
@@ -134,7 +135,7 @@ fn contract_constant_variable() {
         initializer: None,
         initializer: None,
         default_constructor: None,
         default_constructor: None,
         cfg: vec![],
         cfg: vec![],
-        code: vec![],
+        code: OnceCell::new(),
         instantiable: true,
         instantiable: true,
         dispatch_no: 0,
         dispatch_no: 0,
         constructor_dispatch: None,
         constructor_dispatch: None,
@@ -256,7 +257,7 @@ fn slot_suffix() {
         initializer: None,
         initializer: None,
         default_constructor: None,
         default_constructor: None,
         cfg: vec![],
         cfg: vec![],
-        code: vec![],
+        code: OnceCell::new(),
         instantiable: true,
         instantiable: true,
         dispatch_no: 0,
         dispatch_no: 0,
         constructor_dispatch: None,
         constructor_dispatch: None,

+ 13 - 41
src/emit/binary.rs

@@ -10,7 +10,7 @@ use num_bigint::BigInt;
 use num_traits::ToPrimitive;
 use num_traits::ToPrimitive;
 use std::collections::HashMap;
 use std::collections::HashMap;
 
 
-use crate::codegen::cfg::ReturnCode;
+use crate::codegen::{cfg::ReturnCode, Options};
 use crate::emit::substrate;
 use crate::emit::substrate;
 use crate::emit::{solana, BinaryOp, Generate};
 use crate::emit::{solana, BinaryOp, Generate};
 use crate::linker::link;
 use crate::linker::link;
@@ -37,20 +37,17 @@ static LLVM_INIT: OnceCell<()> = OnceCell::new();
 pub struct Binary<'a> {
 pub struct Binary<'a> {
     pub name: String,
     pub name: String,
     pub module: Module<'a>,
     pub module: Module<'a>,
+    pub(crate) options: &'a Options,
     pub runtime: Option<Box<Binary<'a>>>,
     pub runtime: Option<Box<Binary<'a>>>,
     target: Target,
     target: Target,
     pub(crate) function_abort_value_transfers: bool,
     pub(crate) function_abort_value_transfers: bool,
     pub(crate) constructor_abort_value_transfers: bool,
     pub(crate) constructor_abort_value_transfers: bool,
-    pub(crate) math_overflow_check: bool,
-    pub(crate) generate_debug_info: bool,
-    pub(crate) log_api_return_codes: bool,
     pub builder: Builder<'a>,
     pub builder: Builder<'a>,
     pub dibuilder: DebugInfoBuilder<'a>,
     pub dibuilder: DebugInfoBuilder<'a>,
     pub compile_unit: DICompileUnit<'a>,
     pub compile_unit: DICompileUnit<'a>,
     pub(crate) context: &'a Context,
     pub(crate) context: &'a Context,
     pub(crate) functions: HashMap<usize, FunctionValue<'a>>,
     pub(crate) functions: HashMap<usize, FunctionValue<'a>>,
     code: RefCell<Vec<u8>>,
     code: RefCell<Vec<u8>>,
-    pub(crate) opt: OptimizationLevel,
     pub(crate) selector: GlobalValue<'a>,
     pub(crate) selector: GlobalValue<'a>,
     pub(crate) calldata_len: GlobalValue<'a>,
     pub(crate) calldata_len: GlobalValue<'a>,
     pub(crate) scratch_len: Option<GlobalValue<'a>>,
     pub(crate) scratch_len: Option<GlobalValue<'a>>,
@@ -66,35 +63,16 @@ impl<'a> Binary<'a> {
         contract: &'a Contract,
         contract: &'a Contract,
         ns: &'a Namespace,
         ns: &'a Namespace,
         filename: &'a str,
         filename: &'a str,
-        opt: OptimizationLevel,
-        math_overflow_check: bool,
-        generate_debug_info: bool,
-        log_api_return_codes: bool,
+        opt: &'a Options,
     ) -> Self {
     ) -> Self {
         let std_lib = load_stdlib(context, &ns.target);
         let std_lib = load_stdlib(context, &ns.target);
         match ns.target {
         match ns.target {
-            Target::Substrate { .. } => substrate::SubstrateTarget::build(
-                context,
-                &std_lib,
-                contract,
-                ns,
-                filename,
-                opt,
-                math_overflow_check,
-                generate_debug_info,
-                log_api_return_codes,
-            ),
-            Target::Solana => solana::SolanaTarget::build(
-                context,
-                &std_lib,
-                contract,
-                ns,
-                filename,
-                opt,
-                math_overflow_check,
-                generate_debug_info,
-                log_api_return_codes,
-            ),
+            Target::Substrate { .. } => {
+                substrate::SubstrateTarget::build(context, &std_lib, contract, ns, filename, opt)
+            }
+            Target::Solana => {
+                solana::SolanaTarget::build(context, &std_lib, contract, ns, filename, opt)
+            }
             Target::EVM => unimplemented!(),
             Target::EVM => unimplemented!(),
         }
         }
     }
     }
@@ -109,7 +87,7 @@ impl<'a> Binary<'a> {
             return Ok(self.code.borrow().clone());
             return Ok(self.code.borrow().clone());
         }
         }
 
 
-        match self.opt {
+        match self.options.opt_level.into() {
             OptimizationLevel::Default | OptimizationLevel::Aggressive => {
             OptimizationLevel::Default | OptimizationLevel::Aggressive => {
                 let pass_manager = PassManager::create(());
                 let pass_manager = PassManager::create(());
 
 
@@ -130,7 +108,7 @@ impl<'a> Binary<'a> {
                 &self.target.llvm_target_triple(),
                 &self.target.llvm_target_triple(),
                 "",
                 "",
                 self.target.llvm_features(),
                 self.target.llvm_features(),
-                self.opt,
+                self.options.opt_level.into(),
                 RelocMode::Default,
                 RelocMode::Default,
                 CodeModel::Default,
                 CodeModel::Default,
             )
             )
@@ -213,12 +191,9 @@ impl<'a> Binary<'a> {
         target: Target,
         target: Target,
         name: &str,
         name: &str,
         filename: &str,
         filename: &str,
-        opt: OptimizationLevel,
-        math_overflow_check: bool,
+        opt: &'a Options,
         std_lib: &Module<'a>,
         std_lib: &Module<'a>,
         runtime: Option<Box<Binary<'a>>>,
         runtime: Option<Box<Binary<'a>>>,
-        generate_debug_info: bool,
-        log_api_return_codes: bool,
     ) -> Self {
     ) -> Self {
         LLVM_INIT.get_or_init(|| {
         LLVM_INIT.get_or_init(|| {
             inkwell::targets::Target::initialize_webassembly(&Default::default());
             inkwell::targets::Target::initialize_webassembly(&Default::default());
@@ -290,9 +265,6 @@ impl<'a> Binary<'a> {
             runtime,
             runtime,
             function_abort_value_transfers: false,
             function_abort_value_transfers: false,
             constructor_abort_value_transfers: false,
             constructor_abort_value_transfers: false,
-            math_overflow_check,
-            generate_debug_info,
-            log_api_return_codes,
             builder,
             builder,
             dibuilder,
             dibuilder,
             compile_unit,
             compile_unit,
@@ -300,7 +272,7 @@ impl<'a> Binary<'a> {
             target,
             target,
             functions: HashMap::new(),
             functions: HashMap::new(),
             code: RefCell::new(Vec::new()),
             code: RefCell::new(Vec::new()),
-            opt,
+            options: opt,
             selector,
             selector,
             calldata_len,
             calldata_len,
             scratch: None,
             scratch: None,

+ 3 - 4
src/emit/cfg.rs

@@ -1,7 +1,6 @@
 // SPDX-License-Identifier: Apache-2.0
 // SPDX-License-Identifier: Apache-2.0
 
 
-use crate::codegen::cfg::ControlFlowGraph;
-use crate::codegen::vartable::Storage;
+use crate::codegen::{cfg::ControlFlowGraph, vartable::Storage};
 use crate::emit::binary::Binary;
 use crate::emit::binary::Binary;
 use crate::emit::instructions::process_instruction;
 use crate::emit::instructions::process_instruction;
 use crate::emit::{TargetRuntime, Variable};
 use crate::emit::{TargetRuntime, Variable};
@@ -39,7 +38,7 @@ pub(super) fn emit_cfg<'a, T: TargetRuntime<'a> + ?Sized>(
     let file = compile_unit.get_file();
     let file = compile_unit.get_file();
     let mut di_func_scope: Option<DISubprogram<'_>> = None;
     let mut di_func_scope: Option<DISubprogram<'_>> = None;
 
 
-    if bin.generate_debug_info {
+    if bin.options.generate_debug_information {
         let return_type = function.get_type().get_return_type();
         let return_type = function.get_type().get_return_type();
         match return_type {
         match return_type {
             None => {}
             None => {}
@@ -182,7 +181,7 @@ pub(super) fn emit_cfg<'a, T: TargetRuntime<'a> + ?Sized>(
         }
         }
 
 
         for (_, ins) in &cfg.blocks[w.block_no].instr {
         for (_, ins) in &cfg.blocks[w.block_no].instr {
-            if bin.generate_debug_info {
+            if bin.options.generate_debug_information {
                 let debug_loc = ins.loc();
                 let debug_loc = ins.loc();
                 if let pt::Loc::File(file_offset, offset, _) = debug_loc {
                 if let pt::Loc::File(file_offset, offset, _) = debug_loc {
                     let (line, col) = ns.files[file_offset].offset_to_line_column(offset);
                     let (line, col) = ns.files[file_offset].offset_to_line_column(offset);

+ 4 - 54
src/emit/expression.rs

@@ -5,7 +5,7 @@ use crate::codegen::{Builtin, Expression};
 use crate::emit::binary::Binary;
 use crate::emit::binary::Binary;
 use crate::emit::math::{build_binary_op_with_overflow_check, multiply, power};
 use crate::emit::math::{build_binary_op_with_overflow_check, multiply, power};
 use crate::emit::strings::{format_string, string_location};
 use crate::emit::strings::{format_string, string_location};
-use crate::emit::{BinaryOp, Generate, TargetRuntime, Variable};
+use crate::emit::{BinaryOp, TargetRuntime, Variable};
 use crate::sema::ast::{Namespace, RetrieveType, StructType, Type};
 use crate::sema::ast::{Namespace, RetrieveType, StructType, Type};
 use crate::Target;
 use crate::Target;
 use inkwell::module::Linkage;
 use inkwell::module::Linkage;
@@ -120,61 +120,11 @@ pub(super) fn expression<'a, T: TargetRuntime<'a> + ?Sized>(
                 .unwrap()
                 .unwrap()
                 .into()
                 .into()
         }
         }
-        Expression::CodeLiteral(_, bin_no, runtime) => {
-            let codegen_bin = &ns.contracts[*bin_no];
-
-            let target_bin = Binary::build(
-                bin.context,
-                codegen_bin,
-                ns,
-                "",
-                bin.opt,
-                bin.math_overflow_check,
-                bin.generate_debug_info,
-                bin.log_api_return_codes,
-            );
-
-            let code = if *runtime && target_bin.runtime.is_some() {
-                target_bin
-                    .runtime
-                    .unwrap()
-                    .code(Generate::Linked)
-                    .expect("compile should succeeed")
-            } else {
-                target_bin
-                    .code(Generate::Linked)
-                    .expect("compile should succeeed")
-            };
-
-            let size = bin.context.i32_type().const_int(code.len() as u64, false);
-
-            let elem_size = bin.context.i32_type().const_int(1, false);
-
-            let init = bin.emit_global_string(
-                &format!(
-                    "code_{}_{}",
-                    if *runtime { "runtime" } else { "deployer" },
-                    &codegen_bin.name
-                ),
-                &code,
-                true,
-            );
-
-            bin.builder
-                .build_call(
-                    bin.module.get_function("vector_new").unwrap(),
-                    &[size.into(), elem_size.into(), init.into()],
-                    "",
-                )
-                .try_as_basic_value()
-                .left()
-                .unwrap()
-        }
         Expression::Add(_, _, unchecked, l, r) => {
         Expression::Add(_, _, unchecked, l, r) => {
             let left = expression(target, bin, l, vartab, function, ns).into_int_value();
             let left = expression(target, bin, l, vartab, function, ns).into_int_value();
             let right = expression(target, bin, r, vartab, function, ns).into_int_value();
             let right = expression(target, bin, r, vartab, function, ns).into_int_value();
 
 
-            if bin.math_overflow_check && !*unchecked {
+            if bin.options.math_overflow_check && !*unchecked {
                 let signed = l.ty().is_signed_int();
                 let signed = l.ty().is_signed_int();
                 build_binary_op_with_overflow_check(
                 build_binary_op_with_overflow_check(
                     target,
                     target,
@@ -194,7 +144,7 @@ pub(super) fn expression<'a, T: TargetRuntime<'a> + ?Sized>(
             let left = expression(target, bin, l, vartab, function, ns).into_int_value();
             let left = expression(target, bin, l, vartab, function, ns).into_int_value();
             let right = expression(target, bin, r, vartab, function, ns).into_int_value();
             let right = expression(target, bin, r, vartab, function, ns).into_int_value();
 
 
-            if bin.math_overflow_check && !*unchecked {
+            if bin.options.math_overflow_check && !*unchecked {
                 let signed = l.ty().is_signed_int();
                 let signed = l.ty().is_signed_int();
                 build_binary_op_with_overflow_check(
                 build_binary_op_with_overflow_check(
                     target,
                     target,
@@ -704,7 +654,7 @@ pub(super) fn expression<'a, T: TargetRuntime<'a> + ?Sized>(
             // Load the result pointer
             // Load the result pointer
             let res = bin.builder.build_load(o, "");
             let res = bin.builder.build_load(o, "");
 
 
-            if !bin.math_overflow_check || *unchecked || ns.target != Target::Solana {
+            if !bin.options.math_overflow_check || *unchecked || ns.target != Target::Solana {
                 // In Substrate, overflow case will hit an unreachable expression, so no additional checks are needed.
                 // In Substrate, overflow case will hit an unreachable expression, so no additional checks are needed.
                 res
                 res
             } else {
             } else {

+ 9 - 7
src/emit/functions.rs

@@ -1,12 +1,14 @@
 // SPDX-License-Identifier: Apache-2.0
 // SPDX-License-Identifier: Apache-2.0
 
 
-use crate::emit::binary::Binary;
-use crate::emit::cfg::emit_cfg;
-use crate::emit::TargetRuntime;
-use crate::sema::ast::{Contract, Namespace, Type};
-use inkwell::module::Linkage;
-use inkwell::values::FunctionValue;
-use inkwell::{AddressSpace, IntPredicate};
+use crate::{
+    emit::{binary::Binary, cfg::emit_cfg, TargetRuntime},
+    sema::ast::{Contract, Namespace, Type},
+};
+use inkwell::{
+    module::Linkage,
+    values::FunctionValue,
+    {AddressSpace, IntPredicate},
+};
 
 
 /// Emit all functions, constructors, fallback and receiver
 /// Emit all functions, constructors, fallback and receiver
 pub(super) fn emit_functions<'a, T: TargetRuntime<'a>>(
 pub(super) fn emit_functions<'a, T: TargetRuntime<'a>>(

+ 4 - 2
src/emit/instructions.rs

@@ -1,7 +1,9 @@
 // SPDX-License-Identifier: Apache-2.0
 // SPDX-License-Identifier: Apache-2.0
 
 
-use crate::codegen::cfg::{ControlFlowGraph, Instr, InternalCallTy, ReturnCode};
-use crate::codegen::Expression;
+use crate::codegen::{
+    cfg::{ControlFlowGraph, Instr, InternalCallTy, ReturnCode},
+    Expression,
+};
 use crate::emit::binary::Binary;
 use crate::emit::binary::Binary;
 use crate::emit::cfg::{create_block, BasicBlock, Work};
 use crate::emit::cfg::{create_block, BasicBlock, Work};
 use crate::emit::expression::expression;
 use crate::emit::expression::expression;

+ 2 - 2
src/emit/math.rs

@@ -311,7 +311,7 @@ pub(super) fn multiply<'a, T: TargetRuntime<'a> + ?Sized>(
                 .build_store(r, bin.builder.build_int_z_extend(right, mul_ty, ""));
                 .build_store(r, bin.builder.build_int_z_extend(right, mul_ty, ""));
         }
         }
 
 
-        if bin.math_overflow_check && !unchecked {
+        if bin.options.math_overflow_check && !unchecked {
             if signed {
             if signed {
                 return signed_ovf_detect(
                 return signed_ovf_detect(
                     target, bin, mul_ty, mul_bits, left, right, bits, function,
                     target, bin, mul_ty, mul_bits, left, right, bits, function,
@@ -420,7 +420,7 @@ pub(super) fn multiply<'a, T: TargetRuntime<'a> + ?Sized>(
         } else {
         } else {
             return call_mul32_without_ovf(bin, l, r, o, mul_bits, left.get_type());
             return call_mul32_without_ovf(bin, l, r, o, mul_bits, left.get_type());
         }
         }
-    } else if bin.math_overflow_check && !unchecked {
+    } else if bin.options.math_overflow_check && !unchecked {
         build_binary_op_with_overflow_check(
         build_binary_op_with_overflow_check(
             target,
             target,
             bin,
             bin,

+ 31 - 1
src/emit/mod.rs

@@ -25,7 +25,7 @@ mod storage;
 mod strings;
 mod strings;
 pub mod substrate;
 pub mod substrate;
 
 
-use crate::codegen::cfg::HashTy;
+use crate::codegen::{cfg::HashTy, Options};
 use crate::emit::binary::Binary;
 use crate::emit::binary::Binary;
 use crate::sema::ast;
 use crate::sema::ast;
 
 
@@ -385,3 +385,33 @@ impl Target {
         }
         }
     }
     }
 }
 }
+
+impl ast::Contract {
+    /// Generate the binary. This can be used to generate llvm text, object file
+    /// or final linked binary.
+    pub fn binary<'a>(
+        &'a self,
+        ns: &'a ast::Namespace,
+        context: &'a inkwell::context::Context,
+        filename: &'a str,
+        opt: &'a Options,
+    ) -> binary::Binary {
+        binary::Binary::build(context, self, ns, filename, opt)
+    }
+
+    /// Generate the final program code for the contract
+    pub fn emit(&self, ns: &ast::Namespace, opt: &Options) -> Vec<u8> {
+        if ns.target == Target::EVM {
+            return vec![];
+        }
+
+        self.code
+            .get_or_init(move || {
+                let context = inkwell::context::Context::create();
+                let filename = ns.files[self.loc.file_no()].path.to_string_lossy();
+                let binary = self.binary(ns, &context, &filename, opt);
+                binary.code(Generate::Linked).expect("llvm build")
+            })
+            .to_vec()
+    }
+}

+ 3 - 107
src/emit/solana/mod.rs

@@ -7,13 +7,13 @@ use crate::Target;
 use std::collections::HashMap;
 use std::collections::HashMap;
 use std::str;
 use std::str;
 
 
-use crate::codegen::cfg::ReturnCode;
+use crate::codegen::{cfg::ReturnCode, Options};
 use crate::sema::ast::Type;
 use crate::sema::ast::Type;
 use inkwell::module::{Linkage, Module};
 use inkwell::module::{Linkage, Module};
 use inkwell::types::BasicType;
 use inkwell::types::BasicType;
 use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, UnnamedAddress};
 use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, UnnamedAddress};
 use inkwell::{context::Context, types::BasicTypeEnum};
 use inkwell::{context::Context, types::BasicTypeEnum};
-use inkwell::{AddressSpace, IntPredicate, OptimizationLevel};
+use inkwell::{AddressSpace, IntPredicate};
 use num_traits::ToPrimitive;
 use num_traits::ToPrimitive;
 
 
 use crate::emit::functions::emit_functions;
 use crate::emit::functions::emit_functions;
@@ -39,10 +39,7 @@ impl SolanaTarget {
         contract: &'a ast::Contract,
         contract: &'a ast::Contract,
         ns: &'a ast::Namespace,
         ns: &'a ast::Namespace,
         filename: &'a str,
         filename: &'a str,
-        opt: OptimizationLevel,
-        math_overflow_check: bool,
-        generate_debug_info: bool,
-        log_api_return_codes: bool,
+        opt: &'a Options,
     ) -> Binary<'a> {
     ) -> Binary<'a> {
         let mut target = SolanaTarget {
         let mut target = SolanaTarget {
             magic: contract.selector(),
             magic: contract.selector(),
@@ -54,11 +51,8 @@ impl SolanaTarget {
             &contract.name,
             &contract.name,
             filename,
             filename,
             opt,
             opt,
-            math_overflow_check,
             std_lib,
             std_lib,
             None,
             None,
-            generate_debug_info,
-            log_api_return_codes,
         );
         );
 
 
         binary
         binary
@@ -112,104 +106,6 @@ impl SolanaTarget {
         binary
         binary
     }
     }
 
 
-    /// Build a bundle of contracts from the same namespace
-    pub fn build_bundle<'a>(
-        context: &'a Context,
-        std_lib: &Module<'a>,
-        namespaces: &'a [&ast::Namespace],
-        filename: &str,
-        opt: OptimizationLevel,
-        math_overflow_check: bool,
-        generate_debug_info: bool,
-        log_api_return_codes: bool,
-    ) -> Binary<'a> {
-        let mut target = SolanaTarget { magic: 0 };
-
-        let mut binary = Binary::new(
-            context,
-            Target::Solana,
-            "bundle",
-            filename,
-            opt,
-            math_overflow_check,
-            std_lib,
-            None,
-            generate_debug_info,
-            log_api_return_codes,
-        );
-
-        binary
-            .return_values
-            .insert(ReturnCode::Success, context.i64_type().const_zero());
-        binary.return_values.insert(
-            ReturnCode::FunctionSelectorInvalid,
-            context.i64_type().const_int(2u64 << 32, false),
-        );
-        binary.return_values.insert(
-            ReturnCode::AbiEncodingInvalid,
-            context.i64_type().const_int(2u64 << 32, false),
-        );
-        binary.return_values.insert(
-            ReturnCode::AccountDataTooSmall,
-            context.i64_type().const_int(5u64 << 32, false),
-        );
-
-        // externals
-        target.declare_externals(&mut binary, namespaces[0]);
-
-        let mut contracts: Vec<Contract> = Vec::new();
-
-        for ns in namespaces {
-            for contract in &ns.contracts {
-                // We need a magic number for our contract.
-                target.magic = contract.selector();
-
-                // Ignore abstract contracts or contract names we have already seen
-                if !contract.is_concrete() || contracts.iter().any(|c| c.magic == target.magic) {
-                    continue;
-                }
-
-                emit_functions(&mut target, &mut binary, contract, ns);
-
-                let constructor = contract
-                    .constructor_dispatch
-                    .map(|cfg_no| binary.functions[&cfg_no]);
-
-                let mut functions = HashMap::new();
-
-                std::mem::swap(&mut functions, &mut binary.functions);
-
-                contracts.push(Contract {
-                    magic: target.magic,
-                    contract,
-                    constructor,
-                    functions,
-                });
-
-                binary.functions.drain();
-            }
-        }
-
-        target.emit_dispatch(&mut binary, &contracts);
-
-        binary.internalize(&[
-            "entrypoint",
-            "sol_log_",
-            "sol_log_pubkey",
-            "sol_invoke_signed_c",
-            "sol_panic_",
-            "sol_get_return_data",
-            "sol_set_return_data",
-            "sol_create_program_address",
-            "sol_try_find_program_address",
-            "sol_sha256",
-            "sol_keccak256",
-            "sol_log_data",
-        ]);
-
-        binary
-    }
-
     fn declare_externals(&self, binary: &mut Binary, ns: &ast::Namespace) {
     fn declare_externals(&self, binary: &mut Binary, ns: &ast::Namespace) {
         let void_ty = binary.context.void_type();
         let void_ty = binary.context.void_type();
         let u8_ptr = binary.context.i8_type().ptr_type(AddressSpace::Generic);
         let u8_ptr = binary.context.i8_type().ptr_type(AddressSpace::Generic);

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

@@ -1,13 +1,12 @@
 // SPDX-License-Identifier: Apache-2.0
 // SPDX-License-Identifier: Apache-2.0
 
 
-use crate::sema::ast;
+use crate::{codegen::Options, sema::ast};
 use inkwell::context::Context;
 use inkwell::context::Context;
 use inkwell::module::{Linkage, Module};
 use inkwell::module::{Linkage, Module};
 use inkwell::types::BasicType;
 use inkwell::types::BasicType;
 use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue};
 use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue};
 use inkwell::AddressSpace;
 use inkwell::AddressSpace;
 use inkwell::IntPredicate;
 use inkwell::IntPredicate;
-use inkwell::OptimizationLevel;
 use num_traits::ToPrimitive;
 use num_traits::ToPrimitive;
 use solang_parser::pt;
 use solang_parser::pt;
 use std::collections::HashMap;
 use std::collections::HashMap;
@@ -129,10 +128,7 @@ impl SubstrateTarget {
         contract: &'a ast::Contract,
         contract: &'a ast::Contract,
         ns: &'a ast::Namespace,
         ns: &'a ast::Namespace,
         filename: &'a str,
         filename: &'a str,
-        opt: OptimizationLevel,
-        math_overflow_check: bool,
-        generate_debug_info: bool,
-        log_api_return_codes: bool,
+        opt: &'a Options,
     ) -> Binary<'a> {
     ) -> Binary<'a> {
         let mut binary = Binary::new(
         let mut binary = Binary::new(
             context,
             context,
@@ -140,11 +136,8 @@ impl SubstrateTarget {
             &contract.name,
             &contract.name,
             filename,
             filename,
             opt,
             opt,
-            math_overflow_check,
             std_lib,
             std_lib,
             None,
             None,
-            generate_debug_info,
-            log_api_return_codes,
         );
         );
 
 
         binary.set_early_value_aborts(contract, ns);
         binary.set_early_value_aborts(contract, ns);
@@ -1870,7 +1863,7 @@ fn event_id<'b>(
 
 
 /// Print the return code of API calls to the debug buffer.
 /// Print the return code of API calls to the debug buffer.
 fn log_return_code<'b>(binary: &Binary<'b>, api: &'static str, code: IntValue) {
 fn log_return_code<'b>(binary: &Binary<'b>, api: &'static str, code: IntValue) {
-    if !binary.log_api_return_codes {
+    if !binary.options.log_api_return_codes {
         return;
         return;
     }
     }
 
 

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

@@ -1064,6 +1064,8 @@ impl<'a> TargetRuntime<'a> for SubstrateTarget {
 
 
         let created_contract = &ns.contracts[contract_no];
         let created_contract = &ns.contracts[contract_no];
 
 
+        let code = created_contract.emit(ns, binary.options);
+
         let (scratch_buf, scratch_len) = scratch_buf!();
         let (scratch_buf, scratch_len) = scratch_buf!();
 
 
         // salt
         // salt
@@ -1122,12 +1124,10 @@ impl<'a> TargetRuntime<'a> for SubstrateTarget {
             );
             );
         }
         }
 
 
-        assert!(!created_contract.code.is_empty());
-
         // code hash
         // code hash
         let codehash = binary.emit_global_string(
         let codehash = binary.emit_global_string(
             &format!("binary_{}_codehash", created_contract.name),
             &format!("binary_{}_codehash", created_contract.name),
-            blake2_rfc::blake2b::blake2b(32, &[], &created_contract.code).as_bytes(),
+            blake2_rfc::blake2b::blake2b(32, &[], &code).as_bytes(),
             true,
             true,
         );
         );
 
 

+ 26 - 22
src/lib.rs

@@ -129,34 +129,38 @@ pub fn compile(
     log_api_return_codes: bool,
     log_api_return_codes: bool,
 ) -> (Vec<(Vec<u8>, String)>, sema::ast::Namespace) {
 ) -> (Vec<(Vec<u8>, String)>, sema::ast::Namespace) {
     let mut ns = parse_and_resolve(filename, resolver, target);
     let mut ns = parse_and_resolve(filename, resolver, target);
+    let opts = codegen::Options {
+        math_overflow_check,
+        log_api_return_codes,
+        opt_level: opt_level.into(),
+        ..Default::default()
+    };
 
 
     if ns.diagnostics.any_errors() {
     if ns.diagnostics.any_errors() {
         return (Vec::new(), ns);
         return (Vec::new(), ns);
     }
     }
 
 
     // codegen all the contracts
     // codegen all the contracts
-    codegen::codegen(
-        &mut ns,
-        &codegen::Options {
-            math_overflow_check,
-            log_api_return_codes,
-            opt_level: opt_level.into(),
-            ..Default::default()
-        },
-    );
-
-    let results = (0..ns.contracts.len())
-        .filter(|c| ns.contracts[*c].instantiable)
-        .map(|c| {
-            // codegen has already happened
-            assert!(!ns.contracts[c].code.is_empty());
-
-            let code = &ns.contracts[c].code;
-            let (abistr, _) = abi::generate_abi(c, &ns, code, false);
-
-            (code.clone(), abistr)
-        })
-        .collect();
+    codegen::codegen(&mut ns, &opts);
+
+    if ns.diagnostics.any_errors() {
+        return (Vec::new(), ns);
+    }
+
+    // emit the contracts
+    let mut results = Vec::new();
+
+    for contract_no in 0..ns.contracts.len() {
+        let contract = &ns.contracts[contract_no];
+
+        if contract.instantiable {
+            let code = contract.emit(&ns, &opts);
+
+            let (abistr, _) = abi::generate_abi(contract_no, &ns, &code, false);
+
+            results.push((code, abistr));
+        };
+    }
 
 
     (results, ns)
     (results, ns)
 }
 }

+ 3 - 1
src/sema/ast.rs

@@ -10,6 +10,7 @@ use crate::{codegen, Target};
 use indexmap::IndexMap;
 use indexmap::IndexMap;
 use num_bigint::BigInt;
 use num_bigint::BigInt;
 use num_rational::BigRational;
 use num_rational::BigRational;
+use once_cell::unsync::OnceCell;
 pub use solang_parser::diagnostics::*;
 pub use solang_parser::diagnostics::*;
 use solang_parser::pt;
 use solang_parser::pt;
 use solang_parser::pt::{CodeLocation, FunctionTy, OptionalCodeLocation};
 use solang_parser::pt::{CodeLocation, FunctionTy, OptionalCodeLocation};
@@ -649,7 +650,8 @@ pub struct Contract {
     pub initializer: Option<usize>,
     pub initializer: Option<usize>,
     pub default_constructor: Option<(Function, usize)>,
     pub default_constructor: Option<(Function, usize)>,
     pub cfg: Vec<ControlFlowGraph>,
     pub cfg: Vec<ControlFlowGraph>,
-    pub code: Vec<u8>,
+    /// Compiled program. Only available after emit.
+    pub code: OnceCell<Vec<u8>>,
     /// Can the contract be instantiated, i.e. not abstract, no errors, etc.
     /// Can the contract be instantiated, i.e. not abstract, no errors, etc.
     pub instantiable: bool,
     pub instantiable: bool,
     /// CFG number of this contract's dispatch function
     /// CFG number of this contract's dispatch function

+ 11 - 38
src/sema/contracts.rs

@@ -1,14 +1,5 @@
 // SPDX-License-Identifier: Apache-2.0
 // SPDX-License-Identifier: Apache-2.0
 
 
-use num_bigint::BigInt;
-use num_traits::Zero;
-use solang_parser::diagnostics::Diagnostic;
-use solang_parser::pt::FunctionTy;
-use solang_parser::pt::{self, CodeLocation};
-use std::collections::{BTreeMap, HashMap, HashSet};
-use std::convert::TryInto;
-use tiny_keccak::{Hasher, Keccak};
-
 use super::{
 use super::{
     annotions_not_allowed, ast,
     annotions_not_allowed, ast,
     diagnostics::Diagnostics,
     diagnostics::Diagnostics,
@@ -17,10 +8,16 @@ use super::{
     symtable::Symtable,
     symtable::Symtable,
     using, variables, ContractDefinition,
     using, variables, ContractDefinition,
 };
 };
-#[cfg(feature = "llvm")]
-use crate::emit;
-use crate::sema::ast::Namespace;
-use crate::sema::unused_variable::emit_warning_local_variable;
+use crate::{sema::ast::Namespace, sema::unused_variable::emit_warning_local_variable};
+use num_bigint::BigInt;
+use num_traits::Zero;
+use once_cell::unsync::OnceCell;
+use solang_parser::diagnostics::Diagnostic;
+use solang_parser::pt::FunctionTy;
+use solang_parser::pt::{self, CodeLocation};
+use std::collections::{BTreeMap, HashMap, HashSet};
+use std::convert::TryInto;
+use tiny_keccak::{Hasher, Keccak};
 
 
 impl ast::Contract {
 impl ast::Contract {
     /// Create a new contract, abstract contract, interface or library
     /// Create a new contract, abstract contract, interface or library
@@ -46,7 +43,7 @@ impl ast::Contract {
             initializer: None,
             initializer: None,
             default_constructor: None,
             default_constructor: None,
             cfg: Vec::new(),
             cfg: Vec::new(),
-            code: Vec::new(),
+            code: OnceCell::new(),
             instantiable,
             instantiable,
             dispatch_no: 0,
             dispatch_no: 0,
             constructor_dispatch: None,
             constructor_dispatch: None,
@@ -54,30 +51,6 @@ impl ast::Contract {
         }
         }
     }
     }
 
 
-    /// Generate contract code for this contract
-    #[cfg(feature = "llvm")]
-    pub fn emit<'a>(
-        &'a self,
-        ns: &'a ast::Namespace,
-        context: &'a inkwell::context::Context,
-        filename: &'a str,
-        opt: inkwell::OptimizationLevel,
-        math_overflow_check: bool,
-        generate_debug_info: bool,
-        log_api_return_codes: bool,
-    ) -> emit::binary::Binary {
-        emit::binary::Binary::build(
-            context,
-            self,
-            ns,
-            filename,
-            opt,
-            math_overflow_check,
-            generate_debug_info,
-            log_api_return_codes,
-        )
-    }
-
     /// Selector for this contract. This is used by Solana contract bundle
     /// Selector for this contract. This is used by Solana contract bundle
     pub fn selector(&self) -> u32 {
     pub fn selector(&self) -> u32 {
         let mut hasher = Keccak::v256();
         let mut hasher = Keccak::v256();

+ 1 - 12
tests/contract.rs

@@ -87,19 +87,8 @@ fn parse_file(path: PathBuf, target: Target) -> io::Result<()> {
         match ns.target {
         match ns.target {
             Target::Solana | Target::Substrate { .. } => {
             Target::Solana | Target::Substrate { .. } => {
                 for contract in &ns.contracts {
                 for contract in &ns.contracts {
-                    let context = inkwell::context::Context::create();
-
                     if contract.instantiable {
                     if contract.instantiable {
-                        solang::emit::binary::Binary::build(
-                            &context,
-                            contract,
-                            &ns,
-                            &filename,
-                            Default::default(),
-                            false,
-                            false,
-                            false,
-                        );
+                        let _ = contract.emit(&ns, &Default::default());
                     }
                     }
                 }
                 }
             }
             }

+ 5 - 3
tests/solana.rs

@@ -146,11 +146,13 @@ fn build_solidity_with_overflow_check(src: &str, math_overflow_flag: bool) -> Vi
     for contract_no in 0..ns.contracts.len() {
     for contract_no in 0..ns.contracts.len() {
         let contract = &ns.contracts[contract_no];
         let contract = &ns.contracts[contract_no];
 
 
-        if contract.code.is_empty() {
+        if !contract.instantiable {
             continue;
             continue;
         }
         }
 
 
-        let (abistr, _) = solang::abi::generate_abi(contract_no, &ns, &contract.code, false);
+        let code = contract.code.get().unwrap();
+
+        let (abistr, _) = solang::abi::generate_abi(contract_no, &ns, code, false);
         let abi = ethabi::Contract::load(abistr.as_bytes()).unwrap();
         let abi = ethabi::Contract::load(abistr.as_bytes()).unwrap();
 
 
         let program = if let Some(program_id) = &contract.program_id {
         let program = if let Some(program_id) = &contract.program_id {
@@ -162,7 +164,7 @@ fn build_solidity_with_overflow_check(src: &str, math_overflow_flag: bool) -> Vi
         account_data.insert(
         account_data.insert(
             program,
             program,
             AccountState {
             AccountState {
-                data: contract.code.clone(),
+                data: code.clone(),
                 owner: None,
                 owner: None,
                 lamports: 0,
                 lamports: 0,
             },
             },