mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-04-22 04:27:58 +00:00
runtime-api: pass call arguments in registers instead of spilling to stack (#174)
Companion to paritytech/polkadot-sdk#7319 Signed-off-by: xermicus <cyrill@parity.io>
This commit is contained in:
@@ -2,6 +2,17 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
## v0.1.0-dev.9
|
||||
|
||||
This is a development pre-release.
|
||||
|
||||
### Added
|
||||
|
||||
### Changed
|
||||
- Syscalls with more than 6 arguments now pack them into registers.
|
||||
|
||||
### Fixed
|
||||
|
||||
## v0.1.0-dev.8
|
||||
|
||||
This is a development pre-release.
|
||||
|
||||
Generated
+366
-382
File diff suppressed because it is too large
Load Diff
+4
-4
@@ -33,10 +33,10 @@ cc = "1.0"
|
||||
libc = "0.2.169"
|
||||
tempfile = "3.8"
|
||||
anyhow = "1.0"
|
||||
semver = { version = "1.0", features = [ "serde" ] }
|
||||
semver = { version = "1.0", features = ["serde"] }
|
||||
itertools = "0.14"
|
||||
serde = { version = "1.0", features = [ "derive" ] }
|
||||
serde_json = { version = "1.0", features = [ "arbitrary_precision" ] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = { version = "1.0", features = ["arbitrary_precision"] }
|
||||
regex = "1.10"
|
||||
once_cell = "1.19"
|
||||
num = "0.4.3"
|
||||
@@ -73,7 +73,7 @@ assert_fs = "1.1.2"
|
||||
# polkadot-sdk and friends
|
||||
codec = { version = "3.6.12", default-features = false, package = "parity-scale-codec" }
|
||||
scale-info = { version = "2.11.6", default-features = false }
|
||||
polkadot-sdk = { git = "https://github.com/paritytech/polkadot-sdk", rev = "d62a90c8c729acd98c7e9a5cab9803b8b211ffc5" }
|
||||
polkadot-sdk = { git = "https://github.com/paritytech/polkadot-sdk", rev = "4302f74f7874e6a894578731142a7b310a1449b0" }
|
||||
|
||||
# llvm
|
||||
[workspace.dependencies.inkwell]
|
||||
|
||||
@@ -1339,11 +1339,17 @@ where
|
||||
self.llvm.custom_width_int_type(bit_length as u32)
|
||||
}
|
||||
|
||||
/// Returns the register witdh sized type.
|
||||
/// Returns the XLEN witdh sized type.
|
||||
pub fn xlen_type(&self) -> inkwell::types::IntType<'ctx> {
|
||||
self.llvm.custom_width_int_type(crate::polkavm::XLEN as u32)
|
||||
}
|
||||
|
||||
/// Returns the PolkaVM native register width sized type.
|
||||
pub fn register_type(&self) -> inkwell::types::IntType<'ctx> {
|
||||
self.llvm
|
||||
.custom_width_int_type(revive_common::BIT_LENGTH_X64 as u32)
|
||||
}
|
||||
|
||||
/// Returns the sentinel pointer value.
|
||||
pub fn sentinel_pointer(&self) -> Pointer<'ctx> {
|
||||
let sentinel_pointer = self
|
||||
|
||||
@@ -58,39 +58,48 @@ where
|
||||
};
|
||||
let flags = context.xlen_type().const_int(flags as u64, false);
|
||||
|
||||
let argument_type = revive_runtime_api::calling_convention::call(context.llvm());
|
||||
let argument_pointer = context.build_alloca_at_entry(argument_type, "call_arguments");
|
||||
let arguments = &[
|
||||
flags.as_basic_value_enum(),
|
||||
address_pointer.value.as_basic_value_enum(),
|
||||
context
|
||||
.integer_const(revive_common::BIT_LENGTH_X64, u64::MAX)
|
||||
.as_basic_value_enum(),
|
||||
context
|
||||
.integer_const(revive_common::BIT_LENGTH_X64, u64::MAX)
|
||||
.as_basic_value_enum(),
|
||||
deposit_pointer.value.as_basic_value_enum(),
|
||||
value_pointer.value.as_basic_value_enum(),
|
||||
input_pointer.value.as_basic_value_enum(),
|
||||
input_length.as_basic_value_enum(),
|
||||
output_pointer.value.as_basic_value_enum(),
|
||||
output_length_pointer.value.as_basic_value_enum(),
|
||||
];
|
||||
revive_runtime_api::calling_convention::spill(
|
||||
let flags_and_callee = revive_runtime_api::calling_convention::pack_hi_lo_reg(
|
||||
context.builder(),
|
||||
argument_pointer.value,
|
||||
argument_type,
|
||||
arguments,
|
||||
context.llvm(),
|
||||
flags,
|
||||
address_pointer.to_int(context),
|
||||
"address_and_callee",
|
||||
)?;
|
||||
let deposit_and_value = revive_runtime_api::calling_convention::pack_hi_lo_reg(
|
||||
context.builder(),
|
||||
context.llvm(),
|
||||
deposit_pointer.to_int(context),
|
||||
value_pointer.to_int(context),
|
||||
"deposit_and_value",
|
||||
)?;
|
||||
let input_data = revive_runtime_api::calling_convention::pack_hi_lo_reg(
|
||||
context.builder(),
|
||||
context.llvm(),
|
||||
input_length,
|
||||
input_pointer.to_int(context),
|
||||
"input_data",
|
||||
)?;
|
||||
let output_data = revive_runtime_api::calling_convention::pack_hi_lo_reg(
|
||||
context.builder(),
|
||||
context.llvm(),
|
||||
output_length_pointer.to_int(context),
|
||||
output_pointer.to_int(context),
|
||||
"output_data",
|
||||
)?;
|
||||
|
||||
let name = revive_runtime_api::polkavm_imports::CALL;
|
||||
let argument_pointer = context.builder().build_ptr_to_int(
|
||||
argument_pointer.value,
|
||||
context.xlen_type(),
|
||||
"call_argument_pointer",
|
||||
)?;
|
||||
let success = context
|
||||
.build_runtime_call(name, &[argument_pointer.into()])
|
||||
.build_runtime_call(
|
||||
name,
|
||||
&[
|
||||
flags_and_callee.into(),
|
||||
context.register_type().const_all_ones().into(),
|
||||
context.register_type().const_all_ones().into(),
|
||||
deposit_and_value.into(),
|
||||
input_data.into(),
|
||||
output_data.into(),
|
||||
],
|
||||
)
|
||||
.unwrap_or_else(|| panic!("{name} should return a value"))
|
||||
.into_int_value();
|
||||
|
||||
@@ -144,38 +153,41 @@ where
|
||||
|
||||
let flags = context.xlen_type().const_int(0u64, false);
|
||||
|
||||
let argument_type = revive_runtime_api::calling_convention::delegate_call(context.llvm());
|
||||
let argument_pointer = context.build_alloca_at_entry(argument_type, "delegate_call_arguments");
|
||||
let arguments = &[
|
||||
flags.as_basic_value_enum(),
|
||||
address_pointer.value.as_basic_value_enum(),
|
||||
context
|
||||
.integer_const(revive_common::BIT_LENGTH_X64, u64::MAX)
|
||||
.as_basic_value_enum(),
|
||||
context
|
||||
.integer_const(revive_common::BIT_LENGTH_X64, u64::MAX)
|
||||
.as_basic_value_enum(),
|
||||
deposit_pointer.value.as_basic_value_enum(),
|
||||
input_pointer.value.as_basic_value_enum(),
|
||||
input_length.as_basic_value_enum(),
|
||||
output_pointer.value.as_basic_value_enum(),
|
||||
output_length_pointer.value.as_basic_value_enum(),
|
||||
];
|
||||
revive_runtime_api::calling_convention::spill(
|
||||
let flags_and_callee = revive_runtime_api::calling_convention::pack_hi_lo_reg(
|
||||
context.builder(),
|
||||
argument_pointer.value,
|
||||
argument_type,
|
||||
arguments,
|
||||
context.llvm(),
|
||||
flags,
|
||||
address_pointer.to_int(context),
|
||||
"address_and_callee",
|
||||
)?;
|
||||
let input_data = revive_runtime_api::calling_convention::pack_hi_lo_reg(
|
||||
context.builder(),
|
||||
context.llvm(),
|
||||
input_length,
|
||||
input_pointer.to_int(context),
|
||||
"input_data",
|
||||
)?;
|
||||
let output_data = revive_runtime_api::calling_convention::pack_hi_lo_reg(
|
||||
context.builder(),
|
||||
context.llvm(),
|
||||
output_length_pointer.to_int(context),
|
||||
output_pointer.to_int(context),
|
||||
"output_data",
|
||||
)?;
|
||||
|
||||
let name = revive_runtime_api::polkavm_imports::DELEGATE_CALL;
|
||||
let argument_pointer = context.builder().build_ptr_to_int(
|
||||
argument_pointer.value,
|
||||
context.xlen_type(),
|
||||
"delegate_call_argument_pointer",
|
||||
)?;
|
||||
let success = context
|
||||
.build_runtime_call(name, &[argument_pointer.into()])
|
||||
.build_runtime_call(
|
||||
name,
|
||||
&[
|
||||
flags_and_callee.into(),
|
||||
context.register_type().const_all_ones().into(),
|
||||
context.register_type().const_all_ones().into(),
|
||||
deposit_pointer.to_int(context).into(),
|
||||
input_data.into(),
|
||||
output_data.into(),
|
||||
],
|
||||
)
|
||||
.unwrap_or_else(|| panic!("{name} should return a value"))
|
||||
.into_int_value();
|
||||
|
||||
|
||||
@@ -26,15 +26,6 @@ where
|
||||
|
||||
let code_hash_pointer = context.build_heap_gep(input_offset, input_length)?;
|
||||
|
||||
let input_data_pointer = context.build_gep(
|
||||
code_hash_pointer,
|
||||
&[context
|
||||
.xlen_type()
|
||||
.const_int(revive_common::BYTE_LENGTH_WORD as u64, false)],
|
||||
context.byte_type(),
|
||||
"input_ptr_parameter_offset",
|
||||
);
|
||||
|
||||
let value_pointer = context.build_alloca_at_entry(context.value_type(), "transferred_value");
|
||||
context.build_store(value_pointer, value)?;
|
||||
|
||||
@@ -56,40 +47,38 @@ where
|
||||
let deposit_pointer = context.build_alloca_at_entry(context.word_type(), "deposit_pointer");
|
||||
context.build_store(deposit_pointer, context.word_type().const_all_ones())?;
|
||||
|
||||
let argument_type = revive_runtime_api::calling_convention::instantiate(context.llvm());
|
||||
let argument_pointer = context.build_alloca_at_entry(argument_type, "instantiate_arguments");
|
||||
let arguments = &[
|
||||
code_hash_pointer.value.as_basic_value_enum(),
|
||||
context
|
||||
.integer_const(revive_common::BIT_LENGTH_X64, u64::MAX)
|
||||
.as_basic_value_enum(),
|
||||
context
|
||||
.integer_const(revive_common::BIT_LENGTH_X64, u64::MAX)
|
||||
.as_basic_value_enum(),
|
||||
deposit_pointer.value.as_basic_value_enum(),
|
||||
value_pointer.value.as_basic_value_enum(),
|
||||
input_data_pointer.value.as_basic_value_enum(),
|
||||
input_length.as_basic_value_enum(),
|
||||
address_pointer.value.as_basic_value_enum(),
|
||||
context.sentinel_pointer().value.as_basic_value_enum(),
|
||||
context.sentinel_pointer().value.as_basic_value_enum(),
|
||||
salt_pointer.value.as_basic_value_enum(),
|
||||
];
|
||||
revive_runtime_api::calling_convention::spill(
|
||||
let deposit_and_value = revive_runtime_api::calling_convention::pack_hi_lo_reg(
|
||||
context.builder(),
|
||||
argument_pointer.value,
|
||||
argument_type,
|
||||
arguments,
|
||||
context.llvm(),
|
||||
deposit_pointer.to_int(context),
|
||||
value_pointer.to_int(context),
|
||||
"deposit_and_value",
|
||||
)?;
|
||||
let input_data = revive_runtime_api::calling_convention::pack_hi_lo_reg(
|
||||
context.builder(),
|
||||
context.llvm(),
|
||||
input_length,
|
||||
code_hash_pointer.to_int(context),
|
||||
"input_data",
|
||||
)?;
|
||||
let address_and_salt = revive_runtime_api::calling_convention::pack_hi_lo_reg(
|
||||
context.builder(),
|
||||
context.llvm(),
|
||||
address_pointer.to_int(context),
|
||||
salt_pointer.to_int(context),
|
||||
"output_data",
|
||||
)?;
|
||||
|
||||
let argument_pointer = context.builder().build_ptr_to_int(
|
||||
argument_pointer.value,
|
||||
context.xlen_type(),
|
||||
"instantiate_argument_pointer",
|
||||
)?;
|
||||
context.build_runtime_call(
|
||||
revive_runtime_api::polkavm_imports::INSTANTIATE,
|
||||
&[argument_pointer.into()],
|
||||
&[
|
||||
context.register_type().const_all_ones().into(),
|
||||
context.register_type().const_all_ones().into(),
|
||||
deposit_and_value.into(),
|
||||
input_data.into(),
|
||||
context.register_type().const_all_ones().into(),
|
||||
address_and_salt.into(),
|
||||
],
|
||||
);
|
||||
|
||||
let address = context.build_byte_swap(context.build_load(address_pointer, "address")?)?;
|
||||
|
||||
@@ -85,6 +85,7 @@ impl ExtBuilder {
|
||||
.unwrap();
|
||||
pallet_balances::GenesisConfig::<Runtime> {
|
||||
balances: self.balance_genesis_config,
|
||||
dev_accounts: None,
|
||||
}
|
||||
.assimilate_storage(&mut t)
|
||||
.unwrap();
|
||||
|
||||
@@ -87,6 +87,5 @@ impl pallet_revive::Config for Runtime {
|
||||
type UploadOrigin = EnsureSigned<AccountId32>;
|
||||
type InstantiateOrigin = EnsureSigned<AccountId32>;
|
||||
type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent;
|
||||
type Debug = ();
|
||||
type ChainId = ConstU64<420_420_420>;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,4 @@
|
||||
use inkwell::{
|
||||
builder::Builder,
|
||||
context::Context,
|
||||
module::Module,
|
||||
types::{BasicType, StructType},
|
||||
values::{BasicValueEnum, PointerValue},
|
||||
};
|
||||
use inkwell::{builder::Builder, context::Context, module::Module, values::IntValue};
|
||||
|
||||
/// Creates a module that sets the PolkaVM minimum stack size to [`size`] if linked in.
|
||||
pub fn min_stack_size<'context>(
|
||||
@@ -21,115 +15,23 @@ pub fn min_stack_size<'context>(
|
||||
module
|
||||
}
|
||||
|
||||
/// Helper for building function calls with stack spilled arguments.
|
||||
/// - `pointer`: points to a struct of the packed argument struct type
|
||||
/// - `type`: the packed argument struct type
|
||||
/// - `arguments`: a correctly ordered list of the struct field values
|
||||
pub fn spill<'ctx>(
|
||||
/// Helper for packing two 32 bit integer values into a 64 bit integer value.
|
||||
pub fn pack_hi_lo_reg<'ctx>(
|
||||
builder: &Builder<'ctx>,
|
||||
pointer: PointerValue<'ctx>,
|
||||
r#type: StructType<'ctx>,
|
||||
arguments: &[BasicValueEnum<'ctx>],
|
||||
) -> anyhow::Result<()> {
|
||||
for index in 0..r#type.get_field_types().len() {
|
||||
let field_pointer = builder.build_struct_gep(
|
||||
r#type,
|
||||
pointer,
|
||||
index as u32,
|
||||
&format!("spill_parameter_{}", index),
|
||||
)?;
|
||||
let field_value = arguments
|
||||
.get(index)
|
||||
.ok_or_else(|| anyhow::anyhow!("invalid index {index} for struct type {}", r#type))?;
|
||||
builder.build_store(field_pointer, *field_value)?;
|
||||
}
|
||||
context: &'ctx Context,
|
||||
hi: IntValue<'ctx>,
|
||||
lo: IntValue<'ctx>,
|
||||
name: &str,
|
||||
) -> anyhow::Result<IntValue<'ctx>> {
|
||||
assert_eq!(hi.get_type(), context.i32_type());
|
||||
assert_eq!(lo.get_type(), context.i32_type());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns a packed struct argument type for the `instantiate` API.
|
||||
pub fn instantiate(context: &Context) -> StructType {
|
||||
context.struct_type(
|
||||
&[
|
||||
// code_hash_ptr: u32,
|
||||
context.i32_type().as_basic_type_enum(),
|
||||
// ref_time_limit: u64,
|
||||
context.i64_type().as_basic_type_enum(),
|
||||
// proof_size_limit: u64,
|
||||
context.i64_type().as_basic_type_enum(),
|
||||
// deposit_ptr: u32,
|
||||
context.i32_type().as_basic_type_enum(),
|
||||
// value_ptr: u32,
|
||||
context.i32_type().as_basic_type_enum(),
|
||||
// input_data_ptr: u32,
|
||||
context.i32_type().as_basic_type_enum(),
|
||||
// input_data_len: u32,
|
||||
context.i32_type().as_basic_type_enum(),
|
||||
// address_ptr: u32,
|
||||
context.i32_type().as_basic_type_enum(),
|
||||
// output_ptr: u32,
|
||||
context.i32_type().as_basic_type_enum(),
|
||||
// output_len_ptr: u32,
|
||||
context.i32_type().as_basic_type_enum(),
|
||||
// salt_ptr: u32,
|
||||
context.i32_type().as_basic_type_enum(),
|
||||
],
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns a packed struct argument type for the `call` API.
|
||||
pub fn call(context: &Context) -> StructType {
|
||||
context.struct_type(
|
||||
&[
|
||||
// flags: u32,
|
||||
context.i32_type().as_basic_type_enum(),
|
||||
// address_ptr:
|
||||
context.i32_type().as_basic_type_enum(),
|
||||
// ref_time_limit: u64,
|
||||
context.i64_type().as_basic_type_enum(),
|
||||
// proof_size_limit: u64,
|
||||
context.i64_type().as_basic_type_enum(),
|
||||
// deposit_ptr: u32,
|
||||
context.i32_type().as_basic_type_enum(),
|
||||
// value_ptr: u32,
|
||||
context.i32_type().as_basic_type_enum(),
|
||||
// input_data_ptr: u32,
|
||||
context.i32_type().as_basic_type_enum(),
|
||||
// input_data_len: u32,
|
||||
context.i32_type().as_basic_type_enum(),
|
||||
// output_ptr: u32,
|
||||
context.i32_type().as_basic_type_enum(),
|
||||
// output_len_ptr: u32,
|
||||
context.i32_type().as_basic_type_enum(),
|
||||
],
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns a packed struct argument type for the `delegate_call` API.
|
||||
pub fn delegate_call(context: &Context) -> StructType {
|
||||
context.struct_type(
|
||||
&[
|
||||
// flags: u32,
|
||||
context.i32_type().as_basic_type_enum(),
|
||||
// address_ptr:
|
||||
context.i32_type().as_basic_type_enum(),
|
||||
// ref_time_limit: u64,
|
||||
context.i64_type().as_basic_type_enum(),
|
||||
// proof_size_limit: u64,
|
||||
context.i64_type().as_basic_type_enum(),
|
||||
// deposit_ptr: u32,
|
||||
context.i32_type().as_basic_type_enum(),
|
||||
// input_data_ptr: u32,
|
||||
context.i32_type().as_basic_type_enum(),
|
||||
// input_data_len: u32,
|
||||
context.i32_type().as_basic_type_enum(),
|
||||
// output_ptr: u32,
|
||||
context.i32_type().as_basic_type_enum(),
|
||||
// output_len_ptr: u32,
|
||||
context.i32_type().as_basic_type_enum(),
|
||||
],
|
||||
false,
|
||||
)
|
||||
let lo_part = builder.build_int_z_extend(lo, context.i64_type(), &format!("{name}_lo_part"))?;
|
||||
let hi_part = builder.build_int_z_extend(hi, context.i64_type(), &format!("{name}_hi_part"))?;
|
||||
let hi_part_shifted = builder.build_left_shift(
|
||||
hi_part,
|
||||
context.i64_type().const_int(32, false),
|
||||
&format!("{name}_hi_part_shifted"),
|
||||
)?;
|
||||
Ok(builder.build_or(hi_part_shifted, lo_part, name)?)
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ POLKAVM_IMPORT(void, block_hash, uint32_t, uint32_t)
|
||||
|
||||
POLKAVM_IMPORT(void, block_number, uint32_t)
|
||||
|
||||
POLKAVM_IMPORT(uint64_t, call, uint32_t)
|
||||
POLKAVM_IMPORT(uint64_t, call, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t)
|
||||
|
||||
POLKAVM_IMPORT(uint64_t, call_data_copy, uint32_t, uint32_t, uint32_t)
|
||||
|
||||
@@ -92,7 +92,7 @@ POLKAVM_IMPORT(uint64_t, code_size, uint32_t)
|
||||
|
||||
POLKAVM_IMPORT(void, code_hash, uint32_t, uint32_t)
|
||||
|
||||
POLKAVM_IMPORT(uint64_t, delegate_call, uint32_t)
|
||||
POLKAVM_IMPORT(uint64_t, delegate_call, uint64_t, uint64_t, uint64_t, uint32_t, uint64_t, uint64_t)
|
||||
|
||||
POLKAVM_IMPORT(void, deposit_event, uint32_t, uint32_t, uint32_t, uint32_t)
|
||||
|
||||
@@ -106,7 +106,7 @@ POLKAVM_IMPORT(uint64_t, get_storage, uint32_t, uint32_t, uint32_t, uint32_t, ui
|
||||
|
||||
POLKAVM_IMPORT(void, hash_keccak_256, uint32_t, uint32_t, uint32_t)
|
||||
|
||||
POLKAVM_IMPORT(uint64_t, instantiate, uint32_t)
|
||||
POLKAVM_IMPORT(uint64_t, instantiate, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t)
|
||||
|
||||
POLKAVM_IMPORT(void, now, uint32_t)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user