internalize runtime API function symbols

Signed-off-by: xermicus <cyrill@parity.io>
This commit is contained in:
xermicus
2024-05-06 13:45:58 +02:00
parent a75fc55133
commit 518c03d045
9 changed files with 160 additions and 110 deletions
@@ -1,5 +1,8 @@
//! The LLVM context constants.
/// Runtime API methods.
pub mod runtime_api;
/// The LLVM framework version.
pub const LLVM_VERSION: semver::Version = semver::Version::new(18, 1, 4);
@@ -0,0 +1,37 @@
//! Runtime API import and export symbols.
/// The contract deploy export.
pub static CALL: &str = "call";
/// The contract call export.
pub static DEPLOY: &str = "deploy";
/// All exported symbols.
/// Useful for configuring common attributes and linkage.
pub static EXPORTS: [&str; 2] = [CALL, DEPLOY];
pub static GET_STORAGE: &str = "get_storage";
pub static HASH_KECCAK_256: &str = "hash_keccak_256";
pub static INPUT: &str = "input";
pub static RETURN: &str = "seal_return";
pub static SET_STORAGE: &str = "set_storage";
pub static VALUE_TRANSFERRED: &str = "value_transferred";
/// All imported runtime API symbols..
/// Useful for configuring common attributes and linkage.
pub static IMPORTS: [&str; 6] = [
GET_STORAGE,
HASH_KECCAK_256,
INPUT,
RETURN,
SET_STORAGE,
VALUE_TRANSFERRED,
];
/// PolkaVM __sbrk API symbol to extend the heap memory.
pub static SBRK: &str = "__sbrk";
@@ -5,6 +5,7 @@ use inkwell::types::BasicType;
use crate::polkavm::context::address_space::AddressSpace;
use crate::polkavm::context::function::runtime::Runtime;
use crate::polkavm::context::Context;
use crate::polkavm::r#const::*;
use crate::polkavm::Dependency;
use crate::polkavm::WriteLLVM;
use crate::PolkaVMPointer as Pointer;
@@ -113,11 +114,10 @@ impl Entry {
length_pointer,
context.integer_const(32, Self::MAX_CALLDATA_SIZE as u64),
)?;
context.builder().build_call(
context.module().get_function("input").expect("is declared"),
context.build_runtime_call(
runtime_api::INPUT,
&[input_pointer_casted.into(), length_pointer_casted.into()],
"call_seal_input",
)?;
);
// Store the calldata size
let calldata_size = context
@@ -218,8 +218,9 @@ where
let entry_function_type = context.function_type(entry_arguments, 0, false);
context.add_function(Runtime::FUNCTION_ENTRY, entry_function_type, 0, None)?;
context.declare_extern_function("deploy")?;
context.declare_extern_function("call")?;
for symbol in runtime_api::EXPORTS {
context.declare_extern_function(symbol)?;
}
Ok(())
}
@@ -240,7 +241,7 @@ where
true,
);
context.set_current_function("deploy")?;
context.set_current_function(runtime_api::DEPLOY)?;
context.set_basic_block(context.current_function().borrow().entry_block());
assert!(context
@@ -250,7 +251,7 @@ where
context.set_basic_block(context.current_function().borrow().return_block);
context.build_unreachable();
context.set_current_function("call")?;
context.set_current_function(runtime_api::CALL)?;
context.set_basic_block(context.current_function().borrow().entry_block());
assert!(context
+70 -54
View File
@@ -27,6 +27,7 @@ use inkwell::values::BasicValue;
use crate::optimizer::settings::Settings as OptimizerSettings;
use crate::optimizer::Optimizer;
use crate::polkavm::r#const::*;
use crate::polkavm::DebugConfig;
use crate::polkavm::Dependency;
use crate::target_machine::target::Target;
@@ -36,6 +37,7 @@ use self::address_space::AddressSpace;
use self::attribute::Attribute;
use self::build::Build;
use self::code_type::CodeType;
// TODO
// use self::debug_info::DebugInfo;
use self::evmla_data::EVMLAData;
use self::function::declaration::Declaration as FunctionDeclaration;
@@ -137,25 +139,21 @@ where
.link_in_module(pallet_contracts_pvm_llapi::module(llvm, "polkavm_guest").unwrap())
.expect("the PolkaVM guest API module should be linkable");
let call_function = module.get_function("call").unwrap();
call_function.add_attribute(
inkwell::attributes::AttributeLoc::Function,
llvm.create_enum_attribute(Attribute::NoReturn as u32, 0),
);
assert!(call_function.get_first_basic_block().is_none());
for export in runtime_api::EXPORTS {
module
.get_function(export)
.expect("should be declared")
.add_attribute(
inkwell::attributes::AttributeLoc::Function,
llvm.create_enum_attribute(Attribute::NoReturn as u32, 0),
);
}
let deploy_function = module.get_function("deploy").unwrap();
deploy_function.add_attribute(
inkwell::attributes::AttributeLoc::Function,
llvm.create_enum_attribute(Attribute::NoReturn as u32, 0),
);
assert!(deploy_function.get_first_basic_block().is_none());
// TODO: Factor out a list
// Also should be prefixed with double underscores
for name in ["seal_return", "input", "set_storage", "get_storage"] {
let runtime_api_function = module.get_function(name).expect("should be declared");
runtime_api_function.set_linkage(inkwell::module::Linkage::External);
for import in runtime_api::IMPORTS {
module
.get_function(import)
.expect("should be declared")
.set_linkage(inkwell::module::Linkage::External);
}
}
@@ -335,6 +333,13 @@ where
self.code_type.to_owned()
}
/// Returns the function value of a runtime API method.
pub fn runtime_api_method(&self, name: &'static str) -> inkwell::values::FunctionValue<'ctx> {
self.module()
.get_function(name)
.unwrap_or_else(|| panic!("runtime API method {name} not declared"))
}
/// Returns the pointer to a global variable.
pub fn get_global(&self, name: &str) -> anyhow::Result<Global<'ctx>> {
match self.globals.get(name) {
@@ -675,23 +680,19 @@ where
self.integer_const(32, revive_common::BIT_LENGTH_FIELD as u64),
)?;
let runtime_api = self
.module()
.get_function("get_storage")
.expect("should be declared");
let arguments = &[
storage_key_pointer_casted.into(),
self.integer_const(32, 32).into(),
storage_value_pointer_casted.into(),
storage_value_length_pointer_casted.into(),
];
let _ = self
.builder()
.build_call(runtime_api, arguments, "call_seal_get_storage")?
.try_as_basic_value()
.left()
.expect("should not be a void function type");
// TODO check return value
self.build_runtime_call(
runtime_api::GET_STORAGE,
&[
storage_key_pointer_casted.into(),
self.integer_const(32, 32).into(),
storage_value_pointer_casted.into(),
storage_value_length_pointer_casted.into(),
],
);
// We do not to check the return value.
// Solidity assumes infallible SLOAD.
// If a key doesn't exist the "zero" value is returned.
self.build_load(storage_value_pointer, "storage_value_load")
.map(|value| self.build_byte_swap(value))
@@ -753,8 +754,6 @@ where
}
AddressSpace::TransientStorage => todo!(),
AddressSpace::Storage => {
// TODO: Tests, factor out into dedicated functions
let storage_key_value = self.builder().build_ptr_to_int(
pointer.value,
self.field_type(),
@@ -785,18 +784,15 @@ where
self.builder()
.build_store(storage_value_pointer.value, storage_value_value)?;
let runtime_api = self
.module()
.get_function("set_storage")
.expect("should be declared");
let arguments = &[
storage_key_pointer_casted.into(),
self.integer_const(32, 32).into(),
storage_value_pointer_casted.into(),
self.integer_const(32, 32).into(),
];
self.builder()
.build_call(runtime_api, arguments, "call_seal_set_storage")?;
self.build_runtime_call(
runtime_api::SET_STORAGE,
&[
storage_key_pointer_casted.into(),
self.integer_const(32, 32).into(),
storage_value_pointer_casted.into(),
self.integer_const(32, 32).into(),
],
);
}
AddressSpace::Code | AddressSpace::HeapAuxiliary => {}
AddressSpace::Generic => self.build_store(
@@ -878,6 +874,27 @@ where
.unwrap();
}
/// Builds a call to a runtime API method.
pub fn build_runtime_call(
&self,
name: &'static str,
arguments: &[inkwell::values::BasicValueEnum<'ctx>],
) -> Option<inkwell::values::BasicValueEnum<'ctx>> {
self.builder
.build_direct_call(
self.runtime_api_method(name),
&arguments
.iter()
.copied()
.map(inkwell::values::BasicMetadataValueEnum::from)
.collect::<Vec<_>>(),
&format!("runtime API call {name}"),
)
.unwrap()
.try_as_basic_value()
.left()
}
/// Builds a call.
pub fn build_call(
&self,
@@ -1118,11 +1135,10 @@ where
"return_data_ptr_to_int",
)?;
self.builder().build_call(
self.module().get_function("seal_return").unwrap(),
self.build_runtime_call(
runtime_api::RETURN,
&[flags.into(), offset_pointer.into(), length_pointer.into()],
"seal_return",
)?;
);
self.build_unreachable();
Ok(())
@@ -1173,7 +1189,7 @@ where
Ok(self
.builder()
.build_call(
self.module().get_function("__sbrk").expect("is declared"),
self.runtime_api_method(runtime_api::SBRK),
&[size.into()],
"call_sbrk",
)?
@@ -2,6 +2,7 @@
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
use crate::polkavm_const::runtime_api;
/// Translates the `sha3` instruction.
pub fn sha3<'ctx, D>(
@@ -28,20 +29,14 @@ where
"output_pointer_casted",
)?;
let function = context
.module()
.get_function("hash_keccak_256")
.expect("is declared");
context.builder().build_call(
function,
context.build_runtime_call(
runtime_api::HASH_KECCAK_256,
&[
input_pointer_casted.into(),
length_casted.into(),
output_pointer_casted.into(),
],
"call_seal_hash_keccak_256",
)?;
);
Ok(context.build_byte_swap(context.build_load(output_pointer, "sha3_output")?))
}
@@ -4,6 +4,7 @@ use inkwell::values::BasicValue;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
use crate::polkavm_const::runtime_api;
/// Translates the `gas` instruction.
pub fn gas<'ctx, D>(
@@ -43,17 +44,13 @@ where
),
)?;
context.builder().build_call(
context
.module()
.get_function("value_transferred")
.expect("is declared"),
context.build_runtime_call(
runtime_api::VALUE_TRANSFERRED,
&[
output_pointer_casted.into(),
output_length_pointer_casted.into(),
],
"call_seal_value_transferred",
)?;
);
let value = context.build_load(output_pointer, "transferred_value")?;
let value_extended = context.builder().build_int_z_extend(