|
|
@@ -4,11 +4,8 @@ use crate::sema::ast::RetrieveType;
|
|
|
use crate::sema::ast::{
|
|
|
BuiltinStruct, CallTy, Contract, FormatArg, Namespace, Parameter, StringLocation, Type,
|
|
|
};
|
|
|
-use std::cell::RefCell;
|
|
|
use std::convert::TryFrom;
|
|
|
-use std::ffi::CStr;
|
|
|
use std::fmt;
|
|
|
-use std::path::Path;
|
|
|
use std::str;
|
|
|
|
|
|
use num_bigint::{BigInt, Sign};
|
|
|
@@ -17,39 +14,28 @@ use num_traits::ToPrimitive;
|
|
|
use std::collections::{HashMap, VecDeque};
|
|
|
|
|
|
use crate::Target;
|
|
|
-use inkwell::builder::Builder;
|
|
|
-use inkwell::context::Context;
|
|
|
-use inkwell::memory_buffer::MemoryBuffer;
|
|
|
-use inkwell::module::{Linkage, Module};
|
|
|
-use inkwell::passes::PassManager;
|
|
|
-use inkwell::targets::{CodeModel, FileType, RelocMode, TargetTriple};
|
|
|
-use inkwell::types::{
|
|
|
- ArrayType, BasicMetadataTypeEnum, BasicType, BasicTypeEnum, FunctionType, IntType, StringRadix,
|
|
|
-};
|
|
|
+use inkwell::module::Linkage;
|
|
|
+use inkwell::targets::TargetTriple;
|
|
|
+use inkwell::types::{BasicType, IntType, StringRadix};
|
|
|
use inkwell::values::{
|
|
|
- ArrayValue, BasicMetadataValueEnum, BasicValueEnum, CallableValue, FunctionValue, GlobalValue,
|
|
|
- IntValue, PhiValue, PointerValue,
|
|
|
+ ArrayValue, BasicMetadataValueEnum, BasicValueEnum, CallableValue, FunctionValue, IntValue,
|
|
|
+ PhiValue, PointerValue,
|
|
|
};
|
|
|
use inkwell::AddressSpace;
|
|
|
use inkwell::IntPredicate;
|
|
|
-use inkwell::OptimizationLevel;
|
|
|
|
|
|
+pub mod binary;
|
|
|
mod ethabiencoder;
|
|
|
-mod ewasm;
|
|
|
+pub mod ewasm;
|
|
|
mod loop_builder;
|
|
|
-mod solana;
|
|
|
-mod substrate;
|
|
|
-use once_cell::sync::OnceCell;
|
|
|
-
|
|
|
-use crate::{
|
|
|
- codegen::{
|
|
|
- cfg::{ControlFlowGraph, HashTy, Instr, InternalCallTy},
|
|
|
- vartable::Storage,
|
|
|
- },
|
|
|
- linker::link,
|
|
|
-};
|
|
|
+pub mod solana;
|
|
|
+pub mod substrate;
|
|
|
|
|
|
-static LLVM_INIT: OnceCell<()> = OnceCell::new();
|
|
|
+use crate::codegen::{
|
|
|
+ cfg::{ControlFlowGraph, HashTy, Instr, InternalCallTy},
|
|
|
+ vartable::Storage,
|
|
|
+};
|
|
|
+use crate::emit::binary::Binary;
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
pub struct Variable<'a> {
|
|
|
@@ -5567,31 +5553,9 @@ pub trait TargetRuntime<'a> {
|
|
|
.into_int_value()
|
|
|
}
|
|
|
}
|
|
|
-pub struct Binary<'a> {
|
|
|
- pub name: String,
|
|
|
- pub module: Module<'a>,
|
|
|
- pub runtime: Option<Box<Binary<'a>>>,
|
|
|
- target: Target,
|
|
|
- function_abort_value_transfers: bool,
|
|
|
- constructor_abort_value_transfers: bool,
|
|
|
- math_overflow_check: bool,
|
|
|
- builder: Builder<'a>,
|
|
|
- context: &'a Context,
|
|
|
- functions: HashMap<usize, FunctionValue<'a>>,
|
|
|
- code: RefCell<Vec<u8>>,
|
|
|
- opt: OptimizationLevel,
|
|
|
- code_size: RefCell<Option<IntValue<'a>>>,
|
|
|
- selector: GlobalValue<'a>,
|
|
|
- calldata_data: GlobalValue<'a>,
|
|
|
- calldata_len: GlobalValue<'a>,
|
|
|
- scratch_len: Option<GlobalValue<'a>>,
|
|
|
- scratch: Option<GlobalValue<'a>>,
|
|
|
- parameters: Option<PointerValue<'a>>,
|
|
|
- return_values: HashMap<ReturnCode, IntValue<'a>>,
|
|
|
-}
|
|
|
|
|
|
#[derive(PartialEq, Eq, Hash)]
|
|
|
-enum ReturnCode {
|
|
|
+pub(crate) enum ReturnCode {
|
|
|
Success,
|
|
|
FunctionSelectorInvalid,
|
|
|
AbiEncodingInvalid,
|
|
|
@@ -5604,1105 +5568,6 @@ pub enum Generate {
|
|
|
Linked,
|
|
|
}
|
|
|
|
|
|
-impl<'a> Binary<'a> {
|
|
|
- /// Build the LLVM IR for a single contract
|
|
|
- pub fn build(
|
|
|
- context: &'a Context,
|
|
|
- contract: &'a Contract,
|
|
|
- ns: &'a Namespace,
|
|
|
- filename: &'a str,
|
|
|
- opt: OptimizationLevel,
|
|
|
- math_overflow_check: bool,
|
|
|
- ) -> Self {
|
|
|
- match ns.target {
|
|
|
- Target::Substrate { .. } => substrate::SubstrateTarget::build(
|
|
|
- context,
|
|
|
- contract,
|
|
|
- ns,
|
|
|
- filename,
|
|
|
- opt,
|
|
|
- math_overflow_check,
|
|
|
- ),
|
|
|
- Target::Ewasm => {
|
|
|
- ewasm::EwasmTarget::build(context, contract, ns, filename, opt, math_overflow_check)
|
|
|
- }
|
|
|
- Target::Solana => solana::SolanaTarget::build(
|
|
|
- context,
|
|
|
- contract,
|
|
|
- ns,
|
|
|
- filename,
|
|
|
- opt,
|
|
|
- math_overflow_check,
|
|
|
- ),
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /// Build the LLVM IR for a set of contracts in a single namespace
|
|
|
- pub fn build_bundle(
|
|
|
- context: &'a Context,
|
|
|
- namespaces: &'a [Namespace],
|
|
|
- filename: &str,
|
|
|
- opt: OptimizationLevel,
|
|
|
- math_overflow_check: bool,
|
|
|
- ) -> Self {
|
|
|
- assert!(namespaces.iter().all(|ns| ns.target == Target::Solana));
|
|
|
-
|
|
|
- solana::SolanaTarget::build_bundle(context, namespaces, filename, opt, math_overflow_check)
|
|
|
- }
|
|
|
-
|
|
|
- /// Compile the bin and return the code as bytes. The result is
|
|
|
- /// cached, since this function can be called multiple times (e.g. one for
|
|
|
- /// each time a bin of this type is created).
|
|
|
- /// Pass our module to llvm for optimization and compilation
|
|
|
- pub fn code(&self, generate: Generate) -> Result<Vec<u8>, String> {
|
|
|
- // return cached result if available
|
|
|
- if !self.code.borrow().is_empty() {
|
|
|
- return Ok(self.code.borrow().clone());
|
|
|
- }
|
|
|
-
|
|
|
- match self.opt {
|
|
|
- OptimizationLevel::Default | OptimizationLevel::Aggressive => {
|
|
|
- let pass_manager = PassManager::create(());
|
|
|
-
|
|
|
- pass_manager.add_promote_memory_to_register_pass();
|
|
|
- pass_manager.add_function_inlining_pass();
|
|
|
- pass_manager.add_global_dce_pass();
|
|
|
- pass_manager.add_constant_merge_pass();
|
|
|
-
|
|
|
- pass_manager.run_on(&self.module);
|
|
|
- }
|
|
|
- _ => {}
|
|
|
- }
|
|
|
-
|
|
|
- let target = inkwell::targets::Target::from_name(self.target.llvm_target_name()).unwrap();
|
|
|
-
|
|
|
- let target_machine = target
|
|
|
- .create_target_machine(
|
|
|
- &self.target.llvm_target_triple(),
|
|
|
- "",
|
|
|
- self.target.llvm_features(),
|
|
|
- self.opt,
|
|
|
- RelocMode::Default,
|
|
|
- CodeModel::Default,
|
|
|
- )
|
|
|
- .unwrap();
|
|
|
-
|
|
|
- loop {
|
|
|
- // we need to loop here to support ewasm deployer. It needs to know the size
|
|
|
- // of itself. Note that in webassembly, the constants are LEB128 encoded so
|
|
|
- // patching the length might actually change the length. So we need to loop
|
|
|
- // until it is right.
|
|
|
-
|
|
|
- // The correct solution is to make ewasm less insane.
|
|
|
- match target_machine.write_to_memory_buffer(
|
|
|
- &self.module,
|
|
|
- if generate == Generate::Assembly {
|
|
|
- FileType::Assembly
|
|
|
- } else {
|
|
|
- FileType::Object
|
|
|
- },
|
|
|
- ) {
|
|
|
- Ok(out) => {
|
|
|
- let slice = out.as_slice();
|
|
|
-
|
|
|
- if generate == Generate::Linked {
|
|
|
- let bs = link(slice, &self.name, self.target);
|
|
|
-
|
|
|
- if !self.patch_code_size(bs.len() as u64) {
|
|
|
- self.code.replace(bs.to_vec());
|
|
|
-
|
|
|
- return Ok(bs.to_vec());
|
|
|
- }
|
|
|
- } else {
|
|
|
- return Ok(slice.to_vec());
|
|
|
- }
|
|
|
- }
|
|
|
- Err(s) => {
|
|
|
- return Err(s.to_string());
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /// Mark all functions as internal unless they're in the export_list. This helps the
|
|
|
- /// llvm globaldce pass eliminate unnecessary functions and reduce the wasm output.
|
|
|
- fn internalize(&self, export_list: &[&str]) {
|
|
|
- let mut func = self.module.get_first_function();
|
|
|
-
|
|
|
- // FIXME: these functions are called from code generated by lowering into wasm,
|
|
|
- // so eliminating them now will cause link errors. Either we should prevent these
|
|
|
- // calls from being done in the first place or do dce at link time
|
|
|
- let mut export_list = export_list.to_vec();
|
|
|
- export_list.push("__ashlti3");
|
|
|
- export_list.push("__lshrti3");
|
|
|
- export_list.push("__ashrti3");
|
|
|
-
|
|
|
- while let Some(f) = func {
|
|
|
- let name = f.get_name().to_str().unwrap();
|
|
|
-
|
|
|
- if !name.starts_with("llvm.")
|
|
|
- && export_list.iter().all(|e| {
|
|
|
- // symbols may get renamed foo.1 or foo.2, also export those
|
|
|
- if let Some(tail) = name.strip_prefix(e) {
|
|
|
- if let Some(no) = tail.strip_prefix('.') {
|
|
|
- no.parse::<u32>().is_ok()
|
|
|
- } else {
|
|
|
- tail.is_empty()
|
|
|
- }
|
|
|
- } else {
|
|
|
- false
|
|
|
- }
|
|
|
- })
|
|
|
- {
|
|
|
- f.set_linkage(Linkage::Internal);
|
|
|
- }
|
|
|
-
|
|
|
- func = f.get_next_function();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- pub fn bitcode(&self, path: &Path) {
|
|
|
- self.module.write_bitcode_to_path(path);
|
|
|
- }
|
|
|
-
|
|
|
- pub fn dump_llvm(&self, path: &Path) -> Result<(), String> {
|
|
|
- if let Err(s) = self.module.print_to_file(path) {
|
|
|
- return Err(s.to_string());
|
|
|
- }
|
|
|
-
|
|
|
- Ok(())
|
|
|
- }
|
|
|
-
|
|
|
- pub fn new(
|
|
|
- context: &'a Context,
|
|
|
- target: Target,
|
|
|
- name: &str,
|
|
|
- filename: &str,
|
|
|
- opt: OptimizationLevel,
|
|
|
- math_overflow_check: bool,
|
|
|
- runtime: Option<Box<Binary<'a>>>,
|
|
|
- ) -> Self {
|
|
|
- LLVM_INIT.get_or_init(|| {
|
|
|
- inkwell::targets::Target::initialize_webassembly(&Default::default());
|
|
|
- inkwell::targets::Target::initialize_bpf(&Default::default());
|
|
|
- });
|
|
|
-
|
|
|
- let triple = target.llvm_target_triple();
|
|
|
- let module = context.create_module(name);
|
|
|
-
|
|
|
- module.set_triple(&triple);
|
|
|
- module.set_source_file_name(filename);
|
|
|
-
|
|
|
- // stdlib
|
|
|
- let intr = load_stdlib(context, &target);
|
|
|
- module.link_in_module(intr).unwrap();
|
|
|
-
|
|
|
- let selector =
|
|
|
- module.add_global(context.i32_type(), Some(AddressSpace::Generic), "selector");
|
|
|
- selector.set_linkage(Linkage::Internal);
|
|
|
- selector.set_initializer(&context.i32_type().const_zero());
|
|
|
-
|
|
|
- let calldata_len = module.add_global(
|
|
|
- context.i32_type(),
|
|
|
- Some(AddressSpace::Generic),
|
|
|
- "calldata_len",
|
|
|
- );
|
|
|
- calldata_len.set_linkage(Linkage::Internal);
|
|
|
- calldata_len.set_initializer(&context.i32_type().const_zero());
|
|
|
-
|
|
|
- let calldata_data = module.add_global(
|
|
|
- context.i8_type().ptr_type(AddressSpace::Generic),
|
|
|
- Some(AddressSpace::Generic),
|
|
|
- "calldata_data",
|
|
|
- );
|
|
|
- calldata_data.set_linkage(Linkage::Internal);
|
|
|
- calldata_data.set_initializer(
|
|
|
- &context
|
|
|
- .i8_type()
|
|
|
- .ptr_type(AddressSpace::Generic)
|
|
|
- .const_zero(),
|
|
|
- );
|
|
|
-
|
|
|
- let mut return_values = HashMap::new();
|
|
|
-
|
|
|
- return_values.insert(ReturnCode::Success, context.i32_type().const_zero());
|
|
|
- return_values.insert(
|
|
|
- ReturnCode::FunctionSelectorInvalid,
|
|
|
- context.i32_type().const_int(3, false),
|
|
|
- );
|
|
|
- return_values.insert(
|
|
|
- ReturnCode::AbiEncodingInvalid,
|
|
|
- context.i32_type().const_int(2, false),
|
|
|
- );
|
|
|
-
|
|
|
- Binary {
|
|
|
- name: name.to_owned(),
|
|
|
- module,
|
|
|
- runtime,
|
|
|
- function_abort_value_transfers: false,
|
|
|
- constructor_abort_value_transfers: false,
|
|
|
- math_overflow_check,
|
|
|
- builder: context.create_builder(),
|
|
|
- context,
|
|
|
- target,
|
|
|
- functions: HashMap::new(),
|
|
|
- code: RefCell::new(Vec::new()),
|
|
|
- opt,
|
|
|
- code_size: RefCell::new(None),
|
|
|
- selector,
|
|
|
- calldata_data,
|
|
|
- calldata_len,
|
|
|
- scratch: None,
|
|
|
- scratch_len: None,
|
|
|
- parameters: None,
|
|
|
- return_values,
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /// Set flags for early aborts if a value transfer is done and no function/constructor can handle it
|
|
|
- pub fn set_early_value_aborts(&mut self, contract: &Contract, ns: &Namespace) {
|
|
|
- // if there is no payable function, fallback or receive then abort all value transfers at the top
|
|
|
- // note that receive() is always payable so this just checkes for presence.
|
|
|
- self.function_abort_value_transfers = !contract.functions.iter().any(|function_no| {
|
|
|
- let f = &ns.functions[*function_no];
|
|
|
- !f.is_constructor() && f.is_payable()
|
|
|
- });
|
|
|
-
|
|
|
- self.constructor_abort_value_transfers = !contract.functions.iter().any(|function_no| {
|
|
|
- let f = &ns.functions[*function_no];
|
|
|
- f.is_constructor() && f.is_payable()
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- /// llvm value type, as in chain currency (usually 128 bits int)
|
|
|
- fn value_type(&self, ns: &Namespace) -> IntType<'a> {
|
|
|
- self.context
|
|
|
- .custom_width_int_type(ns.value_length as u32 * 8)
|
|
|
- }
|
|
|
-
|
|
|
- /// llvm address type
|
|
|
- fn address_type(&self, ns: &Namespace) -> ArrayType<'a> {
|
|
|
- self.context.i8_type().array_type(ns.address_length as u32)
|
|
|
- }
|
|
|
-
|
|
|
- /// Creates global string in the llvm module with initializer
|
|
|
- ///
|
|
|
- fn emit_global_string(&self, name: &str, data: &[u8], constant: bool) -> PointerValue<'a> {
|
|
|
- let ty = self.context.i8_type().array_type(data.len() as u32);
|
|
|
-
|
|
|
- let gv = self
|
|
|
- .module
|
|
|
- .add_global(ty, Some(AddressSpace::Generic), name);
|
|
|
-
|
|
|
- gv.set_linkage(Linkage::Internal);
|
|
|
-
|
|
|
- gv.set_initializer(&self.context.const_string(data, false));
|
|
|
-
|
|
|
- if constant {
|
|
|
- gv.set_constant(true);
|
|
|
- gv.set_unnamed_addr(true);
|
|
|
- }
|
|
|
-
|
|
|
- self.builder.build_pointer_cast(
|
|
|
- gv.as_pointer_value(),
|
|
|
- self.context.i8_type().ptr_type(AddressSpace::Generic),
|
|
|
- name,
|
|
|
- )
|
|
|
- }
|
|
|
-
|
|
|
- /// Wrapper for alloca. Ensures that the alloca is done on the first basic block.
|
|
|
- /// If alloca is not on the first basic block, llvm will get to llvm_unreachable
|
|
|
- /// for the BPF target.
|
|
|
- fn build_alloca<T: BasicType<'a>>(
|
|
|
- &self,
|
|
|
- function: inkwell::values::FunctionValue<'a>,
|
|
|
- ty: T,
|
|
|
- name: &str,
|
|
|
- ) -> PointerValue<'a> {
|
|
|
- let entry = function
|
|
|
- .get_first_basic_block()
|
|
|
- .expect("function missing entry block");
|
|
|
- let current = self.builder.get_insert_block().unwrap();
|
|
|
-
|
|
|
- if let Some(instr) = &entry.get_first_instruction() {
|
|
|
- self.builder.position_before(instr);
|
|
|
- } else {
|
|
|
- // if there is no instruction yet, then nothing was built
|
|
|
- self.builder.position_at_end(entry);
|
|
|
- }
|
|
|
-
|
|
|
- let res = self.builder.build_alloca(ty, name);
|
|
|
-
|
|
|
- self.builder.position_at_end(current);
|
|
|
-
|
|
|
- res
|
|
|
- }
|
|
|
-
|
|
|
- fn build_array_alloca<T: BasicType<'a>>(
|
|
|
- &self,
|
|
|
- function: inkwell::values::FunctionValue<'a>,
|
|
|
- ty: T,
|
|
|
- length: IntValue<'a>,
|
|
|
- name: &str,
|
|
|
- ) -> PointerValue<'a> {
|
|
|
- let entry = function
|
|
|
- .get_first_basic_block()
|
|
|
- .expect("function missing entry block");
|
|
|
- let current = self.builder.get_insert_block().unwrap();
|
|
|
-
|
|
|
- if let Some(instr) = entry.get_first_instruction() {
|
|
|
- self.builder.position_before(&instr);
|
|
|
- } else {
|
|
|
- self.builder.position_at_end(entry);
|
|
|
- }
|
|
|
-
|
|
|
- let res = self.builder.build_array_alloca(ty, length, name);
|
|
|
-
|
|
|
- self.builder.position_at_end(current);
|
|
|
-
|
|
|
- res
|
|
|
- }
|
|
|
-
|
|
|
- /// Emit a loop from `from` to `to`. The closure exists to insert the body of the loop; the closure
|
|
|
- /// gets the loop variable passed to it as an IntValue, and a userdata PointerValue
|
|
|
- pub fn emit_static_loop_with_pointer<F>(
|
|
|
- &self,
|
|
|
- function: FunctionValue,
|
|
|
- from: IntValue<'a>,
|
|
|
- to: IntValue<'a>,
|
|
|
- data_ref: &mut PointerValue<'a>,
|
|
|
- mut insert_body: F,
|
|
|
- ) where
|
|
|
- F: FnMut(IntValue<'a>, &mut PointerValue<'a>),
|
|
|
- {
|
|
|
- let body = self.context.append_basic_block(function, "body");
|
|
|
- let done = self.context.append_basic_block(function, "done");
|
|
|
- let entry = self.builder.get_insert_block().unwrap();
|
|
|
-
|
|
|
- self.builder.build_unconditional_branch(body);
|
|
|
- self.builder.position_at_end(body);
|
|
|
-
|
|
|
- let loop_ty = from.get_type();
|
|
|
- let loop_phi = self.builder.build_phi(loop_ty, "index");
|
|
|
- let data_phi = self.builder.build_phi(data_ref.get_type(), "data");
|
|
|
- let mut data = data_phi.as_basic_value().into_pointer_value();
|
|
|
-
|
|
|
- let loop_var = loop_phi.as_basic_value().into_int_value();
|
|
|
-
|
|
|
- // add loop body
|
|
|
- insert_body(loop_var, &mut data);
|
|
|
-
|
|
|
- let next = self
|
|
|
- .builder
|
|
|
- .build_int_add(loop_var, loop_ty.const_int(1, false), "next_index");
|
|
|
-
|
|
|
- let comp = self
|
|
|
- .builder
|
|
|
- .build_int_compare(IntPredicate::ULT, next, to, "loop_cond");
|
|
|
- self.builder.build_conditional_branch(comp, body, done);
|
|
|
-
|
|
|
- let body = self.builder.get_insert_block().unwrap();
|
|
|
- loop_phi.add_incoming(&[(&from, entry), (&next, body)]);
|
|
|
- data_phi.add_incoming(&[(&*data_ref, entry), (&data, body)]);
|
|
|
-
|
|
|
- self.builder.position_at_end(done);
|
|
|
-
|
|
|
- *data_ref = data;
|
|
|
- }
|
|
|
-
|
|
|
- /// Emit a loop from `from` to `to`. The closure exists to insert the body of the loop; the closure
|
|
|
- /// gets the loop variable passed to it as an IntValue, and a userdata IntValue
|
|
|
- pub fn emit_static_loop_with_int<F>(
|
|
|
- &self,
|
|
|
- function: FunctionValue,
|
|
|
- from: IntValue<'a>,
|
|
|
- to: IntValue<'a>,
|
|
|
- data_ref: &mut IntValue<'a>,
|
|
|
- mut insert_body: F,
|
|
|
- ) where
|
|
|
- F: FnMut(IntValue<'a>, &mut IntValue<'a>),
|
|
|
- {
|
|
|
- let body = self.context.append_basic_block(function, "body");
|
|
|
- let done = self.context.append_basic_block(function, "done");
|
|
|
- let entry = self.builder.get_insert_block().unwrap();
|
|
|
-
|
|
|
- self.builder.build_unconditional_branch(body);
|
|
|
- self.builder.position_at_end(body);
|
|
|
-
|
|
|
- let loop_ty = from.get_type();
|
|
|
- let loop_phi = self.builder.build_phi(loop_ty, "index");
|
|
|
- let data_phi = self.builder.build_phi(data_ref.get_type(), "data");
|
|
|
- let mut data = data_phi.as_basic_value().into_int_value();
|
|
|
-
|
|
|
- let loop_var = loop_phi.as_basic_value().into_int_value();
|
|
|
-
|
|
|
- // add loop body
|
|
|
- insert_body(loop_var, &mut data);
|
|
|
-
|
|
|
- let next = self
|
|
|
- .builder
|
|
|
- .build_int_add(loop_var, loop_ty.const_int(1, false), "next_index");
|
|
|
-
|
|
|
- let comp = self
|
|
|
- .builder
|
|
|
- .build_int_compare(IntPredicate::ULT, next, to, "loop_cond");
|
|
|
- self.builder.build_conditional_branch(comp, body, done);
|
|
|
-
|
|
|
- let body = self.builder.get_insert_block().unwrap();
|
|
|
- loop_phi.add_incoming(&[(&from, entry), (&next, body)]);
|
|
|
- data_phi.add_incoming(&[(&*data_ref, entry), (&data, body)]);
|
|
|
-
|
|
|
- self.builder.position_at_end(done);
|
|
|
-
|
|
|
- *data_ref = data;
|
|
|
- }
|
|
|
-
|
|
|
- /// Emit a loop from `from` to `to`, checking the condition _before_ the body.
|
|
|
- pub fn emit_loop_cond_first_with_int<F>(
|
|
|
- &self,
|
|
|
- function: FunctionValue,
|
|
|
- from: IntValue<'a>,
|
|
|
- to: IntValue<'a>,
|
|
|
- data_ref: &mut IntValue<'a>,
|
|
|
- mut insert_body: F,
|
|
|
- ) where
|
|
|
- F: FnMut(IntValue<'a>, &mut IntValue<'a>),
|
|
|
- {
|
|
|
- let cond = self.context.append_basic_block(function, "cond");
|
|
|
- let body = self.context.append_basic_block(function, "body");
|
|
|
- let done = self.context.append_basic_block(function, "done");
|
|
|
- let entry = self.builder.get_insert_block().unwrap();
|
|
|
-
|
|
|
- self.builder.build_unconditional_branch(cond);
|
|
|
- self.builder.position_at_end(cond);
|
|
|
-
|
|
|
- let loop_ty = from.get_type();
|
|
|
- let loop_phi = self.builder.build_phi(loop_ty, "index");
|
|
|
- let data_phi = self.builder.build_phi(data_ref.get_type(), "data");
|
|
|
- let mut data = data_phi.as_basic_value().into_int_value();
|
|
|
-
|
|
|
- let loop_var = loop_phi.as_basic_value().into_int_value();
|
|
|
-
|
|
|
- let next = self
|
|
|
- .builder
|
|
|
- .build_int_add(loop_var, loop_ty.const_int(1, false), "next_index");
|
|
|
-
|
|
|
- let comp = self
|
|
|
- .builder
|
|
|
- .build_int_compare(IntPredicate::ULT, loop_var, to, "loop_cond");
|
|
|
- self.builder.build_conditional_branch(comp, body, done);
|
|
|
-
|
|
|
- self.builder.position_at_end(body);
|
|
|
- // add loop body
|
|
|
- insert_body(loop_var, &mut data);
|
|
|
-
|
|
|
- let body = self.builder.get_insert_block().unwrap();
|
|
|
-
|
|
|
- loop_phi.add_incoming(&[(&from, entry), (&next, body)]);
|
|
|
- data_phi.add_incoming(&[(&*data_ref, entry), (&data, body)]);
|
|
|
-
|
|
|
- self.builder.build_unconditional_branch(cond);
|
|
|
-
|
|
|
- self.builder.position_at_end(done);
|
|
|
-
|
|
|
- *data_ref = data_phi.as_basic_value().into_int_value();
|
|
|
- }
|
|
|
-
|
|
|
- /// Emit a loop from `from` to `to`, checking the condition _before_ the body.
|
|
|
- pub fn emit_loop_cond_first_with_pointer<F>(
|
|
|
- &self,
|
|
|
- function: FunctionValue,
|
|
|
- from: IntValue<'a>,
|
|
|
- to: IntValue<'a>,
|
|
|
- data_ref: &mut PointerValue<'a>,
|
|
|
- mut insert_body: F,
|
|
|
- ) where
|
|
|
- F: FnMut(IntValue<'a>, &mut PointerValue<'a>),
|
|
|
- {
|
|
|
- let cond = self.context.append_basic_block(function, "cond");
|
|
|
- let body = self.context.append_basic_block(function, "body");
|
|
|
- let done = self.context.append_basic_block(function, "done");
|
|
|
- let entry = self.builder.get_insert_block().unwrap();
|
|
|
-
|
|
|
- self.builder.build_unconditional_branch(cond);
|
|
|
- self.builder.position_at_end(cond);
|
|
|
-
|
|
|
- let loop_ty = from.get_type();
|
|
|
- let loop_phi = self.builder.build_phi(loop_ty, "index");
|
|
|
- let data_phi = self.builder.build_phi(data_ref.get_type(), "data");
|
|
|
- let mut data = data_phi.as_basic_value().into_pointer_value();
|
|
|
-
|
|
|
- let loop_var = loop_phi.as_basic_value().into_int_value();
|
|
|
-
|
|
|
- let next = self
|
|
|
- .builder
|
|
|
- .build_int_add(loop_var, loop_ty.const_int(1, false), "next_index");
|
|
|
-
|
|
|
- let comp = self
|
|
|
- .builder
|
|
|
- .build_int_compare(IntPredicate::ULT, loop_var, to, "loop_cond");
|
|
|
- self.builder.build_conditional_branch(comp, body, done);
|
|
|
-
|
|
|
- self.builder.position_at_end(body);
|
|
|
- // add loop body
|
|
|
- insert_body(loop_var, &mut data);
|
|
|
-
|
|
|
- let body = self.builder.get_insert_block().unwrap();
|
|
|
-
|
|
|
- loop_phi.add_incoming(&[(&from, entry), (&next, body)]);
|
|
|
- data_phi.add_incoming(&[(&*data_ref, entry), (&data, body)]);
|
|
|
-
|
|
|
- self.builder.build_unconditional_branch(cond);
|
|
|
-
|
|
|
- self.builder.position_at_end(done);
|
|
|
-
|
|
|
- *data_ref = data_phi.as_basic_value().into_pointer_value();
|
|
|
- }
|
|
|
-
|
|
|
- /// Convert a BigInt number to llvm const value
|
|
|
- fn number_literal(&self, bits: u32, n: &BigInt, _ns: &Namespace) -> IntValue<'a> {
|
|
|
- let ty = self.context.custom_width_int_type(bits);
|
|
|
- let s = n.to_string();
|
|
|
-
|
|
|
- ty.const_int_from_string(&s, StringRadix::Decimal).unwrap()
|
|
|
- }
|
|
|
-
|
|
|
- /// Emit function prototype
|
|
|
- fn function_type(&self, params: &[Type], returns: &[Type], ns: &Namespace) -> FunctionType<'a> {
|
|
|
- // function parameters
|
|
|
- let mut args = params
|
|
|
- .iter()
|
|
|
- .map(|ty| self.llvm_var_ty(ty, ns).into())
|
|
|
- .collect::<Vec<BasicMetadataTypeEnum>>();
|
|
|
-
|
|
|
- // add return values
|
|
|
- for ty in returns {
|
|
|
- args.push(if ty.is_reference_type(ns) && !ty.is_contract_storage() {
|
|
|
- self.llvm_type(ty, ns)
|
|
|
- .ptr_type(AddressSpace::Generic)
|
|
|
- .ptr_type(AddressSpace::Generic)
|
|
|
- .into()
|
|
|
- } else {
|
|
|
- self.llvm_type(ty, ns)
|
|
|
- .ptr_type(AddressSpace::Generic)
|
|
|
- .into()
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- // On Solana, we need to pass around the accounts
|
|
|
- if ns.target == Target::Solana {
|
|
|
- args.push(
|
|
|
- self.module
|
|
|
- .get_struct_type("struct.SolParameters")
|
|
|
- .unwrap()
|
|
|
- .ptr_type(AddressSpace::Generic)
|
|
|
- .into(),
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- // Solana return type should be 64 bit, 32 bit on wasm
|
|
|
- self.return_values[&ReturnCode::Success]
|
|
|
- .get_type()
|
|
|
- .fn_type(&args, false)
|
|
|
- }
|
|
|
-
|
|
|
- // Create the llvm intrinsic for counting leading zeros
|
|
|
- pub fn llvm_ctlz(&self, bit: u32) -> FunctionValue<'a> {
|
|
|
- let name = format!("llvm.ctlz.i{}", bit);
|
|
|
- let ty = self.context.custom_width_int_type(bit);
|
|
|
-
|
|
|
- if let Some(f) = self.module.get_function(&name) {
|
|
|
- return f;
|
|
|
- }
|
|
|
-
|
|
|
- self.module.add_function(
|
|
|
- &name,
|
|
|
- ty.fn_type(&[ty.into(), self.context.bool_type().into()], false),
|
|
|
- None,
|
|
|
- )
|
|
|
- }
|
|
|
-
|
|
|
- // Create the llvm intrinsic for bswap
|
|
|
- pub fn llvm_bswap(&self, bit: u32) -> FunctionValue<'a> {
|
|
|
- let name = format!("llvm.bswap.i{}", bit);
|
|
|
- let ty = self.context.custom_width_int_type(bit);
|
|
|
-
|
|
|
- if let Some(f) = self.module.get_function(&name) {
|
|
|
- return f;
|
|
|
- }
|
|
|
-
|
|
|
- self.module
|
|
|
- .add_function(&name, ty.fn_type(&[ty.into()], false), None)
|
|
|
- }
|
|
|
-
|
|
|
- // Create the llvm intrinsic for overflows
|
|
|
- pub fn llvm_overflow(
|
|
|
- &self,
|
|
|
- ret_ty: BasicTypeEnum<'a>,
|
|
|
- ty: IntType<'a>,
|
|
|
- signed: bool,
|
|
|
- op: BinaryOp,
|
|
|
- ) -> FunctionValue<'a> {
|
|
|
- let bit = ty.get_bit_width();
|
|
|
- let name = format!(
|
|
|
- "llvm.{}{}.with.overflow.i{}",
|
|
|
- if signed { "s" } else { "u" },
|
|
|
- op,
|
|
|
- bit,
|
|
|
- );
|
|
|
-
|
|
|
- if let Some(f) = self.module.get_function(&name) {
|
|
|
- return f;
|
|
|
- }
|
|
|
-
|
|
|
- self.module
|
|
|
- .add_function(&name, ret_ty.fn_type(&[ty.into(), ty.into()], false), None)
|
|
|
- }
|
|
|
-
|
|
|
- /// Return the llvm type for a variable holding the type, not the type itself
|
|
|
- fn llvm_var_ty(&self, ty: &Type, ns: &Namespace) -> BasicTypeEnum<'a> {
|
|
|
- let llvm_ty = self.llvm_type(ty, ns);
|
|
|
- match ty.deref_memory() {
|
|
|
- Type::Struct(_) | Type::Array(..) | Type::DynamicBytes | Type::String => {
|
|
|
- llvm_ty.ptr_type(AddressSpace::Generic).as_basic_type_enum()
|
|
|
- }
|
|
|
- _ => llvm_ty,
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /// Default empty value
|
|
|
- fn default_value(&self, ty: &Type, ns: &Namespace) -> BasicValueEnum<'a> {
|
|
|
- let llvm_ty = self.llvm_var_ty(ty, ns);
|
|
|
-
|
|
|
- // const_zero() on BasicTypeEnum yet. Should be coming to inkwell soon
|
|
|
- if llvm_ty.is_pointer_type() {
|
|
|
- llvm_ty.into_pointer_type().const_null().into()
|
|
|
- } else if llvm_ty.is_array_type() {
|
|
|
- self.address_type(ns).const_zero().into()
|
|
|
- } else {
|
|
|
- llvm_ty.into_int_type().const_zero().into()
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /// Return the llvm type for field in struct or array
|
|
|
- fn llvm_field_ty(&self, ty: &Type, ns: &Namespace) -> BasicTypeEnum<'a> {
|
|
|
- let llvm_ty = self.llvm_type(ty, ns);
|
|
|
- match ty.deref_memory() {
|
|
|
- Type::Array(_, dim) if dim[0].is_none() => {
|
|
|
- llvm_ty.ptr_type(AddressSpace::Generic).as_basic_type_enum()
|
|
|
- }
|
|
|
- Type::DynamicBytes | Type::String => {
|
|
|
- llvm_ty.ptr_type(AddressSpace::Generic).as_basic_type_enum()
|
|
|
- }
|
|
|
- _ => llvm_ty,
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /// Return the llvm type for the resolved type.
|
|
|
- fn llvm_type(&self, ty: &Type, ns: &Namespace) -> BasicTypeEnum<'a> {
|
|
|
- if ty.builtin_struct(ns) == BuiltinStruct::AccountInfo {
|
|
|
- return self
|
|
|
- .module
|
|
|
- .get_struct_type("struct.SolAccountInfo")
|
|
|
- .unwrap()
|
|
|
- .into();
|
|
|
- } else {
|
|
|
- match ty {
|
|
|
- Type::Bool => BasicTypeEnum::IntType(self.context.bool_type()),
|
|
|
- Type::Int(n) | Type::Uint(n) => {
|
|
|
- BasicTypeEnum::IntType(self.context.custom_width_int_type(*n as u32))
|
|
|
- }
|
|
|
- Type::Value => BasicTypeEnum::IntType(
|
|
|
- self.context
|
|
|
- .custom_width_int_type(ns.value_length as u32 * 8),
|
|
|
- ),
|
|
|
- Type::Contract(_) | Type::Address(_) => {
|
|
|
- BasicTypeEnum::ArrayType(self.address_type(ns))
|
|
|
- }
|
|
|
- Type::Bytes(n) => {
|
|
|
- BasicTypeEnum::IntType(self.context.custom_width_int_type(*n as u32 * 8))
|
|
|
- }
|
|
|
- Type::Enum(n) => self.llvm_type(&ns.enums[*n].ty, ns),
|
|
|
- Type::String | Type::DynamicBytes => {
|
|
|
- self.module.get_struct_type("struct.vector").unwrap().into()
|
|
|
- }
|
|
|
- Type::Array(base_ty, dims) => {
|
|
|
- let ty = self.llvm_field_ty(base_ty, ns);
|
|
|
-
|
|
|
- let mut dims = dims.iter();
|
|
|
-
|
|
|
- let mut aty = match dims.next().unwrap() {
|
|
|
- Some(d) => ty.array_type(d.to_u32().unwrap()),
|
|
|
- None => {
|
|
|
- return self.module.get_struct_type("struct.vector").unwrap().into()
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- for dim in dims {
|
|
|
- match dim {
|
|
|
- Some(d) => aty = aty.array_type(d.to_u32().unwrap()),
|
|
|
- None => {
|
|
|
- return self.module.get_struct_type("struct.vector").unwrap().into()
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- BasicTypeEnum::ArrayType(aty)
|
|
|
- }
|
|
|
- Type::Struct(n) => self
|
|
|
- .context
|
|
|
- .struct_type(
|
|
|
- &ns.structs[*n]
|
|
|
- .fields
|
|
|
- .iter()
|
|
|
- .map(|f| self.llvm_field_ty(&f.ty, ns))
|
|
|
- .collect::<Vec<BasicTypeEnum>>(),
|
|
|
- false,
|
|
|
- )
|
|
|
- .as_basic_type_enum(),
|
|
|
- Type::Mapping(..) => unreachable!(),
|
|
|
- Type::Ref(r) => self
|
|
|
- .llvm_type(r, ns)
|
|
|
- .ptr_type(AddressSpace::Generic)
|
|
|
- .as_basic_type_enum(),
|
|
|
- Type::StorageRef(..) => self.llvm_type(&ns.storage_type(), ns),
|
|
|
- Type::InternalFunction {
|
|
|
- params, returns, ..
|
|
|
- } => {
|
|
|
- let ftype = self.function_type(params, returns, ns);
|
|
|
-
|
|
|
- BasicTypeEnum::PointerType(ftype.ptr_type(AddressSpace::Generic))
|
|
|
- }
|
|
|
- Type::ExternalFunction { .. } => {
|
|
|
- let address = self.llvm_type(&Type::Address(false), ns);
|
|
|
- let selector = self.llvm_type(&Type::Uint(32), ns);
|
|
|
-
|
|
|
- BasicTypeEnum::PointerType(
|
|
|
- self.context
|
|
|
- .struct_type(&[address, selector], false)
|
|
|
- .ptr_type(AddressSpace::Generic),
|
|
|
- )
|
|
|
- }
|
|
|
- Type::Slice => BasicTypeEnum::StructType(
|
|
|
- self.context.struct_type(
|
|
|
- &[
|
|
|
- self.context
|
|
|
- .i8_type()
|
|
|
- .ptr_type(AddressSpace::Generic)
|
|
|
- .into(),
|
|
|
- self.context.i32_type().into(),
|
|
|
- ],
|
|
|
- false,
|
|
|
- ),
|
|
|
- ),
|
|
|
- Type::UserType(no) => self.llvm_type(&ns.user_types[*no].ty, ns),
|
|
|
- _ => unreachable!(),
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /// ewasm deployer needs to know what its own code size is, so we compile once to
|
|
|
- /// get the size, patch in the value and then recompile.
|
|
|
- fn patch_code_size(&self, code_size: u64) -> bool {
|
|
|
- let current_size = {
|
|
|
- let current_size_opt = self.code_size.borrow();
|
|
|
-
|
|
|
- if let Some(current_size) = *current_size_opt {
|
|
|
- if code_size == current_size.get_zero_extended_constant().unwrap() {
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- current_size
|
|
|
- } else {
|
|
|
- return false;
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- let new_size = self.context.i32_type().const_int(code_size, false);
|
|
|
-
|
|
|
- current_size.replace_all_uses_with(new_size);
|
|
|
-
|
|
|
- self.code_size.replace(Some(new_size));
|
|
|
-
|
|
|
- true
|
|
|
- }
|
|
|
-
|
|
|
- /// Allocate vector
|
|
|
- fn vector_new(
|
|
|
- &self,
|
|
|
- size: IntValue<'a>,
|
|
|
- elem_size: IntValue<'a>,
|
|
|
- init: Option<&Vec<u8>>,
|
|
|
- ) -> PointerValue<'a> {
|
|
|
- if let Some(init) = init {
|
|
|
- if init.is_empty() {
|
|
|
- return self
|
|
|
- .module
|
|
|
- .get_struct_type("struct.vector")
|
|
|
- .unwrap()
|
|
|
- .ptr_type(AddressSpace::Generic)
|
|
|
- .const_null();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- let init = match init {
|
|
|
- None => self.builder.build_int_to_ptr(
|
|
|
- self.context.i32_type().const_all_ones(),
|
|
|
- self.context.i8_type().ptr_type(AddressSpace::Generic),
|
|
|
- "invalid",
|
|
|
- ),
|
|
|
- Some(s) => self.emit_global_string("const_string", s, true),
|
|
|
- };
|
|
|
-
|
|
|
- let v = self
|
|
|
- .builder
|
|
|
- .build_call(
|
|
|
- self.module.get_function("vector_new").unwrap(),
|
|
|
- &[size.into(), elem_size.into(), init.into()],
|
|
|
- "",
|
|
|
- )
|
|
|
- .try_as_basic_value()
|
|
|
- .left()
|
|
|
- .unwrap();
|
|
|
-
|
|
|
- self.builder.build_pointer_cast(
|
|
|
- v.into_pointer_value(),
|
|
|
- self.module
|
|
|
- .get_struct_type("struct.vector")
|
|
|
- .unwrap()
|
|
|
- .ptr_type(AddressSpace::Generic),
|
|
|
- "vector",
|
|
|
- )
|
|
|
- }
|
|
|
-
|
|
|
- /// Number of element in a vector
|
|
|
- fn vector_len(&self, vector: BasicValueEnum<'a>) -> IntValue<'a> {
|
|
|
- if vector.is_struct_value() {
|
|
|
- // slice
|
|
|
- let slice = vector.into_struct_value();
|
|
|
-
|
|
|
- self.builder
|
|
|
- .build_extract_value(slice, 1, "slice_len")
|
|
|
- .unwrap()
|
|
|
- .into_int_value()
|
|
|
- } else {
|
|
|
- let struct_ty = vector
|
|
|
- .into_pointer_value()
|
|
|
- .get_type()
|
|
|
- .get_element_type()
|
|
|
- .into_struct_type();
|
|
|
- let name = struct_ty.get_name().unwrap();
|
|
|
-
|
|
|
- if name == CStr::from_bytes_with_nul(b"struct.SolAccountInfo\0").unwrap() {
|
|
|
- // load the data pointer
|
|
|
- let data = self
|
|
|
- .builder
|
|
|
- .build_load(
|
|
|
- self.builder
|
|
|
- .build_struct_gep(vector.into_pointer_value(), 3, "data")
|
|
|
- .unwrap(),
|
|
|
- "data",
|
|
|
- )
|
|
|
- .into_pointer_value();
|
|
|
-
|
|
|
- // get the offset of the return data
|
|
|
- let header_ptr = self.builder.build_pointer_cast(
|
|
|
- data,
|
|
|
- self.context.i32_type().ptr_type(AddressSpace::Generic),
|
|
|
- "header_ptr",
|
|
|
- );
|
|
|
-
|
|
|
- let data_len_ptr = unsafe {
|
|
|
- self.builder.build_gep(
|
|
|
- header_ptr,
|
|
|
- &[self.context.i64_type().const_int(1, false)],
|
|
|
- "data_len_ptr",
|
|
|
- )
|
|
|
- };
|
|
|
-
|
|
|
- self.builder
|
|
|
- .build_load(data_len_ptr, "len")
|
|
|
- .into_int_value()
|
|
|
- } else {
|
|
|
- // field 0 is the length
|
|
|
- let vector = vector.into_pointer_value();
|
|
|
-
|
|
|
- let len = unsafe {
|
|
|
- self.builder.build_gep(
|
|
|
- vector,
|
|
|
- &[
|
|
|
- self.context.i32_type().const_zero(),
|
|
|
- self.context.i32_type().const_zero(),
|
|
|
- ],
|
|
|
- "vector_len",
|
|
|
- )
|
|
|
- };
|
|
|
-
|
|
|
- self.builder
|
|
|
- .build_select(
|
|
|
- self.builder.build_is_null(vector, "vector_is_null"),
|
|
|
- self.context.i32_type().const_zero(),
|
|
|
- self.builder.build_load(len, "vector_len").into_int_value(),
|
|
|
- "length",
|
|
|
- )
|
|
|
- .into_int_value()
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /// Return the pointer to the actual bytes in the vector
|
|
|
- fn vector_bytes(&self, vector: BasicValueEnum<'a>) -> PointerValue<'a> {
|
|
|
- if vector.is_struct_value() {
|
|
|
- // slice
|
|
|
- let slice = vector.into_struct_value();
|
|
|
-
|
|
|
- self.builder
|
|
|
- .build_extract_value(slice, 0, "slice_data")
|
|
|
- .unwrap()
|
|
|
- .into_pointer_value()
|
|
|
- } else {
|
|
|
- let data = unsafe {
|
|
|
- self.builder.build_gep(
|
|
|
- vector.into_pointer_value(),
|
|
|
- &[
|
|
|
- self.context.i32_type().const_zero(),
|
|
|
- self.context.i32_type().const_int(2, false),
|
|
|
- ],
|
|
|
- "data",
|
|
|
- )
|
|
|
- };
|
|
|
-
|
|
|
- self.builder.build_pointer_cast(
|
|
|
- data,
|
|
|
- self.context.i8_type().ptr_type(AddressSpace::Generic),
|
|
|
- "data",
|
|
|
- )
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /// Dereference an array
|
|
|
- fn array_subscript(
|
|
|
- &self,
|
|
|
- array_ty: &Type,
|
|
|
- array: PointerValue<'a>,
|
|
|
- index: IntValue<'a>,
|
|
|
- ns: &Namespace,
|
|
|
- ) -> PointerValue<'a> {
|
|
|
- match array_ty {
|
|
|
- Type::Array(_, dim) => {
|
|
|
- if dim[0].is_some() {
|
|
|
- // fixed size array
|
|
|
- unsafe {
|
|
|
- self.builder.build_gep(
|
|
|
- array,
|
|
|
- &[self.context.i32_type().const_zero(), index],
|
|
|
- "index_access",
|
|
|
- )
|
|
|
- }
|
|
|
- } else {
|
|
|
- let elem_ty = array_ty.array_deref();
|
|
|
- let llvm_elem_ty = self.llvm_field_ty(&elem_ty, ns);
|
|
|
-
|
|
|
- // dynamic length array or vector
|
|
|
- let index = self.builder.build_int_mul(
|
|
|
- index,
|
|
|
- llvm_elem_ty
|
|
|
- .into_pointer_type()
|
|
|
- .get_element_type()
|
|
|
- .size_of()
|
|
|
- .unwrap()
|
|
|
- .const_cast(self.context.i32_type(), false),
|
|
|
- "",
|
|
|
- );
|
|
|
-
|
|
|
- let elem = unsafe {
|
|
|
- self.builder.build_gep(
|
|
|
- array,
|
|
|
- &[
|
|
|
- self.context.i32_type().const_zero(),
|
|
|
- self.context.i32_type().const_int(2, false),
|
|
|
- index,
|
|
|
- ],
|
|
|
- "index_access",
|
|
|
- )
|
|
|
- };
|
|
|
-
|
|
|
- self.builder
|
|
|
- .build_pointer_cast(elem, llvm_elem_ty.into_pointer_type(), "elem")
|
|
|
- }
|
|
|
- }
|
|
|
- _ => unreachable!(),
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-static BPF_IR: [&[u8]; 5] = [
|
|
|
- include_bytes!("../../stdlib/bpf/stdlib.bc"),
|
|
|
- include_bytes!("../../stdlib/bpf/bigint.bc"),
|
|
|
- include_bytes!("../../stdlib/bpf/format.bc"),
|
|
|
- include_bytes!("../../stdlib/bpf/solana.bc"),
|
|
|
- include_bytes!("../../stdlib/bpf/ripemd160.bc"),
|
|
|
-];
|
|
|
-
|
|
|
-static WASM_IR: [&[u8]; 4] = [
|
|
|
- include_bytes!("../../stdlib/wasm/stdlib.bc"),
|
|
|
- include_bytes!("../../stdlib/wasm/wasmheap.bc"),
|
|
|
- include_bytes!("../../stdlib/wasm/bigint.bc"),
|
|
|
- include_bytes!("../../stdlib/wasm/format.bc"),
|
|
|
-];
|
|
|
-
|
|
|
-static RIPEMD160_IR: &[u8] = include_bytes!("../../stdlib/wasm/ripemd160.bc");
|
|
|
-static SUBSTRATE_IR: &[u8] = include_bytes!("../../stdlib/wasm/substrate.bc");
|
|
|
-
|
|
|
-/// Return the stdlib as parsed llvm module. The solidity standard library is hardcoded into
|
|
|
-/// the solang library
|
|
|
-fn load_stdlib<'a>(context: &'a Context, target: &Target) -> Module<'a> {
|
|
|
- if *target == Target::Solana {
|
|
|
- let memory = MemoryBuffer::create_from_memory_range(BPF_IR[0], "bpf_bc");
|
|
|
-
|
|
|
- let module = Module::parse_bitcode_from_buffer(&memory, context).unwrap();
|
|
|
-
|
|
|
- for bc in BPF_IR.iter().skip(1) {
|
|
|
- let memory = MemoryBuffer::create_from_memory_range(bc, "bpf_bc");
|
|
|
-
|
|
|
- module
|
|
|
- .link_in_module(Module::parse_bitcode_from_buffer(&memory, context).unwrap())
|
|
|
- .unwrap();
|
|
|
- }
|
|
|
-
|
|
|
- return module;
|
|
|
- }
|
|
|
-
|
|
|
- let memory = MemoryBuffer::create_from_memory_range(WASM_IR[0], "wasm_bc");
|
|
|
-
|
|
|
- let module = Module::parse_bitcode_from_buffer(&memory, context).unwrap();
|
|
|
-
|
|
|
- for bc in WASM_IR.iter().skip(1) {
|
|
|
- let memory = MemoryBuffer::create_from_memory_range(bc, "wasm_bc");
|
|
|
-
|
|
|
- module
|
|
|
- .link_in_module(Module::parse_bitcode_from_buffer(&memory, context).unwrap())
|
|
|
- .unwrap();
|
|
|
- }
|
|
|
-
|
|
|
- if let Target::Substrate { .. } = *target {
|
|
|
- let memory = MemoryBuffer::create_from_memory_range(SUBSTRATE_IR, "substrate");
|
|
|
-
|
|
|
- module
|
|
|
- .link_in_module(Module::parse_bitcode_from_buffer(&memory, context).unwrap())
|
|
|
- .unwrap();
|
|
|
-
|
|
|
- // substrate does not provide ripemd160
|
|
|
- let memory = MemoryBuffer::create_from_memory_range(RIPEMD160_IR, "ripemd160");
|
|
|
-
|
|
|
- module
|
|
|
- .link_in_module(Module::parse_bitcode_from_buffer(&memory, context).unwrap())
|
|
|
- .unwrap();
|
|
|
- }
|
|
|
-
|
|
|
- module
|
|
|
-}
|
|
|
-
|
|
|
impl Target {
|
|
|
/// LLVM Target name
|
|
|
fn llvm_target_name(&self) -> &'static str {
|