mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-04-22 02:07:55 +00:00
94ec34c4d5
Separate between compilation and linker phases to allow deploy time linking and back-porting era compiler changes to fix #91. Unlinked contract binaries (caused by missing libraries or missing factory dependencies in turn) are emitted as raw ELF object. Few drive by fixes: - #98 - A compiler panic on missing libraries definitions. - Fixes some incosistent type forwarding in JSON output (empty string vs. null object). - Remove the unused fallback for size optimization setting. - Remove the broken `--lvm-ir` mode. - CI workflow fixes. --------- Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com> Signed-off-by: xermicus <bigcyrill@hotmail.com> Signed-off-by: xermicus <cyrill@parity.io>
253 lines
7.4 KiB
Rust
253 lines
7.4 KiB
Rust
//! The revive storage pointer functions.
|
|
|
|
use inkwell::values::BasicValueEnum;
|
|
|
|
use crate::polkavm::context::runtime::RuntimeFunction;
|
|
use crate::polkavm::context::Context;
|
|
use crate::polkavm::WriteLLVM;
|
|
|
|
/// Load a word size value from a storage pointer.
|
|
pub struct LoadWord;
|
|
|
|
impl RuntimeFunction for LoadWord {
|
|
const NAME: &'static str = "__revive_load_storage_word";
|
|
|
|
fn r#type<'ctx>(context: &Context<'ctx>) -> inkwell::types::FunctionType<'ctx> {
|
|
context
|
|
.word_type()
|
|
.fn_type(&[context.llvm().ptr_type(Default::default()).into()], false)
|
|
}
|
|
|
|
fn emit_body<'ctx>(
|
|
&self,
|
|
context: &mut Context<'ctx>,
|
|
) -> anyhow::Result<Option<BasicValueEnum<'ctx>>> {
|
|
Ok(Some(emit_load(
|
|
context,
|
|
Self::paramater(context, 0),
|
|
false,
|
|
)?))
|
|
}
|
|
}
|
|
|
|
impl WriteLLVM for LoadWord {
|
|
fn declare(&mut self, context: &mut Context) -> anyhow::Result<()> {
|
|
<Self as RuntimeFunction>::declare(self, context)
|
|
}
|
|
|
|
fn into_llvm(self, context: &mut Context) -> anyhow::Result<()> {
|
|
<Self as RuntimeFunction>::emit(&self, context)
|
|
}
|
|
}
|
|
|
|
/// Load a word size value from a transient storage pointer.
|
|
pub struct LoadTransientWord;
|
|
|
|
impl RuntimeFunction for LoadTransientWord {
|
|
const NAME: &'static str = "__revive_load_transient_storage_word";
|
|
|
|
fn r#type<'ctx>(context: &Context<'ctx>) -> inkwell::types::FunctionType<'ctx> {
|
|
context
|
|
.word_type()
|
|
.fn_type(&[context.llvm().ptr_type(Default::default()).into()], false)
|
|
}
|
|
|
|
fn emit_body<'ctx>(
|
|
&self,
|
|
context: &mut Context<'ctx>,
|
|
) -> anyhow::Result<Option<BasicValueEnum<'ctx>>> {
|
|
Ok(Some(emit_load(context, Self::paramater(context, 0), true)?))
|
|
}
|
|
}
|
|
|
|
impl WriteLLVM for LoadTransientWord {
|
|
fn declare(&mut self, context: &mut Context) -> anyhow::Result<()> {
|
|
<Self as RuntimeFunction>::declare(self, context)
|
|
}
|
|
|
|
fn into_llvm(self, context: &mut Context) -> anyhow::Result<()> {
|
|
<Self as RuntimeFunction>::emit(&self, context)
|
|
}
|
|
}
|
|
|
|
/// Store a word size value through a storage pointer.
|
|
pub struct StoreWord;
|
|
|
|
impl RuntimeFunction for StoreWord {
|
|
const NAME: &'static str = "__revive_store_storage_word";
|
|
|
|
fn r#type<'ctx>(context: &Context<'ctx>) -> inkwell::types::FunctionType<'ctx> {
|
|
context.void_type().fn_type(
|
|
&[
|
|
context.llvm().ptr_type(Default::default()).into(),
|
|
context.llvm().ptr_type(Default::default()).into(),
|
|
],
|
|
false,
|
|
)
|
|
}
|
|
|
|
fn emit_body<'ctx>(
|
|
&self,
|
|
context: &mut Context<'ctx>,
|
|
) -> anyhow::Result<Option<BasicValueEnum<'ctx>>> {
|
|
emit_store(
|
|
context,
|
|
Self::paramater(context, 0),
|
|
Self::paramater(context, 1),
|
|
false,
|
|
)?;
|
|
|
|
Ok(None)
|
|
}
|
|
}
|
|
|
|
impl WriteLLVM for StoreWord {
|
|
fn declare(&mut self, context: &mut Context) -> anyhow::Result<()> {
|
|
<Self as RuntimeFunction>::declare(self, context)
|
|
}
|
|
|
|
fn into_llvm(self, context: &mut Context) -> anyhow::Result<()> {
|
|
<Self as RuntimeFunction>::emit(&self, context)
|
|
}
|
|
}
|
|
|
|
/// Store a word size value through a transient storage pointer.
|
|
pub struct StoreTransientWord;
|
|
|
|
impl RuntimeFunction for StoreTransientWord {
|
|
const NAME: &'static str = "__revive_store_transient_storage_word";
|
|
|
|
fn r#type<'ctx>(context: &Context<'ctx>) -> inkwell::types::FunctionType<'ctx> {
|
|
context.void_type().fn_type(
|
|
&[
|
|
context.llvm().ptr_type(Default::default()).into(),
|
|
context.llvm().ptr_type(Default::default()).into(),
|
|
],
|
|
false,
|
|
)
|
|
}
|
|
|
|
fn emit_body<'ctx>(
|
|
&self,
|
|
context: &mut Context<'ctx>,
|
|
) -> anyhow::Result<Option<BasicValueEnum<'ctx>>> {
|
|
emit_store(
|
|
context,
|
|
Self::paramater(context, 0),
|
|
Self::paramater(context, 1),
|
|
true,
|
|
)?;
|
|
|
|
Ok(None)
|
|
}
|
|
}
|
|
|
|
impl WriteLLVM for StoreTransientWord {
|
|
fn declare(&mut self, context: &mut Context) -> anyhow::Result<()> {
|
|
<Self as RuntimeFunction>::declare(self, context)
|
|
}
|
|
|
|
fn into_llvm(self, context: &mut Context) -> anyhow::Result<()> {
|
|
<Self as RuntimeFunction>::emit(&self, context)
|
|
}
|
|
}
|
|
|
|
fn emit_load<'ctx>(
|
|
context: &mut Context<'ctx>,
|
|
key: BasicValueEnum<'ctx>,
|
|
transient: bool,
|
|
) -> anyhow::Result<BasicValueEnum<'ctx>> {
|
|
let mut key = context.build_load(
|
|
super::Pointer::new(
|
|
context.word_type(),
|
|
Default::default(),
|
|
key.into_pointer_value(),
|
|
),
|
|
"key",
|
|
)?;
|
|
if !transient {
|
|
key = context.build_byte_swap(key)?;
|
|
}
|
|
|
|
let key_pointer = context.build_alloca_at_entry(context.word_type(), "key_pointer");
|
|
let value_pointer = context.build_alloca_at_entry(context.word_type(), "value_pointer");
|
|
let length_pointer = context.build_alloca_at_entry(context.xlen_type(), "length_pointer");
|
|
|
|
context.builder().build_store(key_pointer.value, key)?;
|
|
context.build_store(value_pointer, context.word_const(0))?;
|
|
context.build_store(
|
|
length_pointer,
|
|
context
|
|
.xlen_type()
|
|
.const_int(revive_common::BYTE_LENGTH_WORD as u64, false),
|
|
)?;
|
|
|
|
let is_transient = context.xlen_type().const_int(transient as u64, false);
|
|
|
|
let arguments = [
|
|
is_transient.into(),
|
|
key_pointer.to_int(context).into(),
|
|
context.xlen_type().const_all_ones().into(),
|
|
value_pointer.to_int(context).into(),
|
|
length_pointer.to_int(context).into(),
|
|
];
|
|
context.build_runtime_call(revive_runtime_api::polkavm_imports::GET_STORAGE, &arguments);
|
|
|
|
// We do not to check the return value: Solidity assumes infallible loads.
|
|
// If a key doesn't exist the "zero" value is returned (ensured by above write).
|
|
|
|
let value = context.build_load(value_pointer, "storage_value")?;
|
|
Ok(if transient {
|
|
value
|
|
} else {
|
|
context.build_byte_swap(value)?
|
|
})
|
|
}
|
|
|
|
fn emit_store<'ctx>(
|
|
context: &mut Context<'ctx>,
|
|
key: BasicValueEnum<'ctx>,
|
|
value: BasicValueEnum<'ctx>,
|
|
transient: bool,
|
|
) -> anyhow::Result<()> {
|
|
let mut key = context.build_load(
|
|
super::Pointer::new(
|
|
context.word_type(),
|
|
Default::default(),
|
|
key.into_pointer_value(),
|
|
),
|
|
"key",
|
|
)?;
|
|
let mut value = context.build_load(
|
|
super::Pointer::new(
|
|
context.word_type(),
|
|
Default::default(),
|
|
value.into_pointer_value(),
|
|
),
|
|
"key",
|
|
)?;
|
|
if !transient {
|
|
key = context.build_byte_swap(key)?;
|
|
value = context.build_byte_swap(value)?;
|
|
}
|
|
|
|
let key_pointer = context.build_alloca_at_entry(context.word_type(), "key_pointer");
|
|
let value_pointer = context.build_alloca_at_entry(context.word_type(), "value_pointer");
|
|
|
|
context.build_store(key_pointer, key)?;
|
|
context.build_store(value_pointer, value)?;
|
|
|
|
let is_transient = context.xlen_type().const_int(transient as u64, false);
|
|
|
|
let arguments = [
|
|
is_transient.into(),
|
|
key_pointer.to_int(context).into(),
|
|
context.xlen_type().const_all_ones().into(),
|
|
value_pointer.to_int(context).into(),
|
|
context.integer_const(crate::polkavm::XLEN, 32).into(),
|
|
];
|
|
context.build_runtime_call(revive_runtime_api::polkavm_imports::SET_STORAGE, &arguments);
|
|
|
|
Ok(())
|
|
}
|