mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-06-13 13:01:06 +00:00
disable sbrk and emulate EVM linear memory internally (#76)
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com> Signed-off-by: xermicus <cyrill@parity.io>
This commit is contained in:
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"Baseline": 962,
|
"Baseline": 983,
|
||||||
"Computation": 4463,
|
"Computation": 4207,
|
||||||
"DivisionArithmetics": 40756,
|
"DivisionArithmetics": 40509,
|
||||||
"ERC20": 54427,
|
"ERC20": 47068,
|
||||||
"Events": 1792,
|
"Events": 1791,
|
||||||
"FibonacciIterative": 3065,
|
"FibonacciIterative": 3044,
|
||||||
"Flipper": 3665,
|
"Flipper": 3405,
|
||||||
"SHA1": 32923
|
"SHA1": 33583
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,7 @@ pragma solidity ^0.8;
|
|||||||
|
|
||||||
/* runner.json
|
/* runner.json
|
||||||
{
|
{
|
||||||
|
"differential": true,
|
||||||
"actions": [
|
"actions": [
|
||||||
{
|
{
|
||||||
"Instantiate": {
|
"Instantiate": {
|
||||||
@@ -23,12 +24,6 @@ pragma solidity ^0.8;
|
|||||||
"data": "f016832c"
|
"data": "f016832c"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"VerifyCall": {
|
|
||||||
"success": true,
|
|
||||||
"output": "0000000000000000000000000000000000000000000000000000000000000060"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"Call": {
|
"Call": {
|
||||||
"dest": {
|
"dest": {
|
||||||
@@ -36,12 +31,6 @@ pragma solidity ^0.8;
|
|||||||
},
|
},
|
||||||
"data": "f4a63aa5"
|
"data": "f4a63aa5"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
"VerifyCall": {
|
|
||||||
"success": true,
|
|
||||||
"output": "0000000000000000000000000000000000000000000000000000000000000084"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,10 @@ pub mod exports {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub mod imports {
|
pub mod imports {
|
||||||
|
pub static SBRK: &str = "__sbrk_internal";
|
||||||
|
|
||||||
|
pub static MEMORY_SIZE: &str = "__msize";
|
||||||
|
|
||||||
pub static ADDRESS: &str = "address";
|
pub static ADDRESS: &str = "address";
|
||||||
|
|
||||||
pub static BALANCE: &str = "balance";
|
pub static BALANCE: &str = "balance";
|
||||||
@@ -57,7 +61,9 @@ pub mod imports {
|
|||||||
|
|
||||||
/// All imported runtime API symbols.
|
/// All imported runtime API symbols.
|
||||||
/// Useful for configuring common attributes and linkage.
|
/// Useful for configuring common attributes and linkage.
|
||||||
pub static IMPORTS: [&str; 21] = [
|
pub static IMPORTS: [&str; 23] = [
|
||||||
|
SBRK,
|
||||||
|
MEMORY_SIZE,
|
||||||
ADDRESS,
|
ADDRESS,
|
||||||
BALANCE,
|
BALANCE,
|
||||||
BALANCE_OF,
|
BALANCE_OF,
|
||||||
@@ -81,6 +87,3 @@ pub mod imports {
|
|||||||
VALUE_TRANSFERRED,
|
VALUE_TRANSFERRED,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// PolkaVM __sbrk API symbol to extend the heap memory.
|
|
||||||
pub static SBRK: &str = "__sbrk";
|
|
||||||
|
|||||||
@@ -61,7 +61,10 @@ impl Entry {
|
|||||||
context
|
context
|
||||||
.get_global(crate::polkavm::GLOBAL_HEAP_MEMORY_POINTER)?
|
.get_global(crate::polkavm::GLOBAL_HEAP_MEMORY_POINTER)?
|
||||||
.into(),
|
.into(),
|
||||||
context.build_sbrk(context.integer_const(crate::polkavm::XLEN, 0))?,
|
context.build_sbrk(
|
||||||
|
context.xlen_type().const_zero(),
|
||||||
|
context.xlen_type().const_zero(),
|
||||||
|
)?,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
context.set_global(
|
context.set_global(
|
||||||
|
|||||||
@@ -1088,16 +1088,20 @@ where
|
|||||||
Ok(truncated)
|
Ok(truncated)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build a call to PolkaVM `sbrk` for extending the heap by `size`.
|
/// Build a call to PolkaVM `sbrk` for extending the heap from offset by `size`.
|
||||||
|
/// The allocation is aligned to 32 bytes.
|
||||||
|
///
|
||||||
|
/// This emulates the EVM linear memory until the runtime supports metered memory.
|
||||||
pub fn build_sbrk(
|
pub fn build_sbrk(
|
||||||
&self,
|
&self,
|
||||||
|
offset: inkwell::values::IntValue<'ctx>,
|
||||||
size: inkwell::values::IntValue<'ctx>,
|
size: inkwell::values::IntValue<'ctx>,
|
||||||
) -> anyhow::Result<inkwell::values::PointerValue<'ctx>> {
|
) -> anyhow::Result<inkwell::values::PointerValue<'ctx>> {
|
||||||
Ok(self
|
Ok(self
|
||||||
.builder()
|
.builder()
|
||||||
.build_call(
|
.build_call(
|
||||||
self.runtime_api_method(runtime_api::SBRK),
|
self.runtime_api_method(runtime_api::imports::SBRK),
|
||||||
&[size.into()],
|
&[offset.into(), size.into()],
|
||||||
"call_sbrk",
|
"call_sbrk",
|
||||||
)?
|
)?
|
||||||
.try_as_basic_value()
|
.try_as_basic_value()
|
||||||
@@ -1106,14 +1110,29 @@ where
|
|||||||
.into_pointer_value())
|
.into_pointer_value())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call PolkaVM `sbrk` for extending the heap by `size`,
|
/// Build a call to PolkaVM `msize` for querying the linear memory size.
|
||||||
|
pub fn build_msize(&self) -> anyhow::Result<inkwell::values::IntValue<'ctx>> {
|
||||||
|
Ok(self
|
||||||
|
.builder()
|
||||||
|
.build_call(
|
||||||
|
self.runtime_api_method(runtime_api::imports::MEMORY_SIZE),
|
||||||
|
&[],
|
||||||
|
"call_msize",
|
||||||
|
)?
|
||||||
|
.try_as_basic_value()
|
||||||
|
.left()
|
||||||
|
.expect("sbrk returns an int")
|
||||||
|
.into_int_value())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Call PolkaVM `sbrk` for extending the heap by `offset` + `size`,
|
||||||
/// trapping the contract if the call failed.
|
/// trapping the contract if the call failed.
|
||||||
/// Returns the end of memory pointer.
|
|
||||||
pub fn build_heap_alloc(
|
pub fn build_heap_alloc(
|
||||||
&self,
|
&self,
|
||||||
|
offset: inkwell::values::IntValue<'ctx>,
|
||||||
size: inkwell::values::IntValue<'ctx>,
|
size: inkwell::values::IntValue<'ctx>,
|
||||||
) -> anyhow::Result<inkwell::values::PointerValue<'ctx>> {
|
) -> anyhow::Result<()> {
|
||||||
let end_of_memory = self.build_sbrk(size)?;
|
let end_of_memory = self.build_sbrk(offset, size)?;
|
||||||
let return_is_nil = self.builder().build_int_compare(
|
let return_is_nil = self.builder().build_int_compare(
|
||||||
inkwell::IntPredicate::EQ,
|
inkwell::IntPredicate::EQ,
|
||||||
end_of_memory,
|
end_of_memory,
|
||||||
@@ -1131,7 +1150,7 @@ where
|
|||||||
|
|
||||||
self.set_basic_block(continue_block);
|
self.set_basic_block(continue_block);
|
||||||
|
|
||||||
Ok(end_of_memory)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a pointer to `offset` into the heap, allocating
|
/// Returns a pointer to `offset` into the heap, allocating
|
||||||
@@ -1146,40 +1165,12 @@ where
|
|||||||
assert_eq!(offset.get_type(), self.xlen_type());
|
assert_eq!(offset.get_type(), self.xlen_type());
|
||||||
assert_eq!(length.get_type(), self.xlen_type());
|
assert_eq!(length.get_type(), self.xlen_type());
|
||||||
|
|
||||||
|
self.build_heap_alloc(offset, length)?;
|
||||||
|
|
||||||
let heap_start = self
|
let heap_start = self
|
||||||
.get_global(crate::polkavm::GLOBAL_HEAP_MEMORY_POINTER)?
|
.get_global(crate::polkavm::GLOBAL_HEAP_MEMORY_POINTER)?
|
||||||
.value
|
.value
|
||||||
.as_pointer_value();
|
.as_pointer_value();
|
||||||
let heap_end = self.build_sbrk(self.integer_const(crate::polkavm::XLEN, 0))?;
|
|
||||||
let value_end = self.build_gep(
|
|
||||||
Pointer::new(self.byte_type(), AddressSpace::Stack, heap_start),
|
|
||||||
&[self.builder().build_int_nuw_add(offset, length, "end")?],
|
|
||||||
self.byte_type(),
|
|
||||||
"heap_end_gep",
|
|
||||||
);
|
|
||||||
let is_out_of_bounds = self.builder().build_int_compare(
|
|
||||||
inkwell::IntPredicate::UGT,
|
|
||||||
value_end.value,
|
|
||||||
heap_end,
|
|
||||||
"is_value_overflowing_heap",
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let out_of_bounds_block = self.append_basic_block("heap_offset_out_of_bounds");
|
|
||||||
let heap_offset_block = self.append_basic_block("build_heap_pointer");
|
|
||||||
self.build_conditional_branch(is_out_of_bounds, out_of_bounds_block, heap_offset_block)?;
|
|
||||||
|
|
||||||
self.set_basic_block(out_of_bounds_block);
|
|
||||||
let size = self.builder().build_int_nuw_sub(
|
|
||||||
self.builder()
|
|
||||||
.build_ptr_to_int(value_end.value, self.xlen_type(), "value_end")?,
|
|
||||||
self.builder()
|
|
||||||
.build_ptr_to_int(heap_end, self.xlen_type(), "heap_end")?,
|
|
||||||
"heap_alloc_size",
|
|
||||||
)?;
|
|
||||||
self.build_heap_alloc(size)?;
|
|
||||||
self.build_unconditional_branch(heap_offset_block);
|
|
||||||
|
|
||||||
self.set_basic_block(heap_offset_block);
|
|
||||||
Ok(self.build_gep(
|
Ok(self.build_gep(
|
||||||
Pointer::new(self.byte_type(), AddressSpace::Stack, heap_start),
|
Pointer::new(self.byte_type(), AddressSpace::Stack, heap_start),
|
||||||
&[offset],
|
&[offset],
|
||||||
|
|||||||
@@ -107,33 +107,6 @@ where
|
|||||||
Ok(context.word_const(0).as_basic_value_enum())
|
Ok(context.word_const(0).as_basic_value_enum())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Translates the `msize` instruction.
|
|
||||||
pub fn msize<'ctx, D>(
|
|
||||||
context: &mut Context<'ctx, D>,
|
|
||||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
|
||||||
where
|
|
||||||
D: Dependency + Clone,
|
|
||||||
{
|
|
||||||
let heap_end = context.build_sbrk(context.xlen_type().const_zero())?;
|
|
||||||
let heap_start = context
|
|
||||||
.get_global(crate::polkavm::GLOBAL_HEAP_MEMORY_POINTER)?
|
|
||||||
.value
|
|
||||||
.as_pointer_value();
|
|
||||||
let heap_size = context.builder().build_int_nuw_sub(
|
|
||||||
context
|
|
||||||
.builder()
|
|
||||||
.build_ptr_to_int(heap_end, context.xlen_type(), "heap_end")?,
|
|
||||||
context
|
|
||||||
.builder()
|
|
||||||
.build_ptr_to_int(heap_start, context.xlen_type(), "heap_start")?,
|
|
||||||
"heap_size",
|
|
||||||
)?;
|
|
||||||
Ok(context
|
|
||||||
.builder()
|
|
||||||
.build_int_z_extend(heap_size, context.word_type(), "heap_size_extended")?
|
|
||||||
.as_basic_value_enum())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Translates the `address` instruction.
|
/// Translates the `address` instruction.
|
||||||
pub fn address<'ctx, D>(
|
pub fn address<'ctx, D>(
|
||||||
context: &mut Context<'ctx, D>,
|
context: &mut Context<'ctx, D>,
|
||||||
|
|||||||
@@ -1,10 +1,29 @@
|
|||||||
//! Translates the heap memory operations.
|
//! Translates the heap memory operations.
|
||||||
|
|
||||||
|
use inkwell::values::BasicValue;
|
||||||
|
|
||||||
use crate::polkavm::context::address_space::AddressSpace;
|
use crate::polkavm::context::address_space::AddressSpace;
|
||||||
use crate::polkavm::context::pointer::Pointer;
|
use crate::polkavm::context::pointer::Pointer;
|
||||||
use crate::polkavm::context::Context;
|
use crate::polkavm::context::Context;
|
||||||
use crate::polkavm::Dependency;
|
use crate::polkavm::Dependency;
|
||||||
|
|
||||||
|
/// Translates the `msize` instruction.
|
||||||
|
pub fn msize<'ctx, D>(
|
||||||
|
context: &mut Context<'ctx, D>,
|
||||||
|
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||||
|
where
|
||||||
|
D: Dependency + Clone,
|
||||||
|
{
|
||||||
|
Ok(context
|
||||||
|
.builder()
|
||||||
|
.build_int_z_extend(
|
||||||
|
context.build_msize()?,
|
||||||
|
context.word_type(),
|
||||||
|
"heap_size_extended",
|
||||||
|
)?
|
||||||
|
.as_basic_value_enum())
|
||||||
|
}
|
||||||
|
|
||||||
/// Translates the `mload` instruction.
|
/// Translates the `mload` instruction.
|
||||||
/// Uses the main heap.
|
/// Uses the main heap.
|
||||||
pub fn load<'ctx, D>(
|
pub fn load<'ctx, D>(
|
||||||
|
|||||||
@@ -289,16 +289,10 @@ impl Specs {
|
|||||||
else {
|
else {
|
||||||
panic!("the differential runner requires Code::Solidity source");
|
panic!("the differential runner requires Code::Solidity source");
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_ne!(
|
|
||||||
solc_optimizer,
|
|
||||||
Some(false),
|
|
||||||
"solc_optimizer must be enabled in differntial mode"
|
|
||||||
);
|
|
||||||
assert_ne!(
|
assert_ne!(
|
||||||
pipeline,
|
pipeline,
|
||||||
Some(revive_solidity::SolcPipeline::EVMLA),
|
Some(revive_solidity::SolcPipeline::EVMLA),
|
||||||
"yul pipeline must be enabled in differntial mode"
|
"yul pipeline must be enabled in differential mode"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
salt.0.is_none(),
|
salt.0.is_none(),
|
||||||
@@ -311,9 +305,11 @@ impl Specs {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let deploy_code = match std::fs::read_to_string(&path) {
|
let deploy_code = match std::fs::read_to_string(&path) {
|
||||||
Ok(solidity_source) => {
|
Ok(solidity_source) => hex::encode(compile_evm_deploy_code(
|
||||||
hex::encode(compile_evm_deploy_code(&contract, &solidity_source))
|
&contract,
|
||||||
}
|
&solidity_source,
|
||||||
|
solc_optimizer.unwrap_or(true),
|
||||||
|
)),
|
||||||
Err(err) => panic!(
|
Err(err) => panic!(
|
||||||
"failed to read solidity source\n . path: '{}'\n . error: {:?}",
|
"failed to read solidity source\n . path: '{}'\n . error: {:?}",
|
||||||
path.display(),
|
path.display(),
|
||||||
|
|||||||
@@ -3,9 +3,34 @@
|
|||||||
|
|
||||||
#include "polkavm_guest.h"
|
#include "polkavm_guest.h"
|
||||||
|
|
||||||
|
|
||||||
// Missing builtins
|
// Missing builtins
|
||||||
|
|
||||||
|
#define EVM_WORD_SIZE 32
|
||||||
|
#define ALIGN(size) ((size + EVM_WORD_SIZE - 1) & ~(EVM_WORD_SIZE - 1))
|
||||||
|
#define MAX_MEMORY_SIZE (64 * 1024)
|
||||||
|
static char __memory[MAX_MEMORY_SIZE];
|
||||||
|
static uint32_t __memory_size = 0;
|
||||||
|
|
||||||
|
void * __sbrk_internal(uint32_t offset, uint32_t size) {
|
||||||
|
if (offset >= MAX_MEMORY_SIZE || size > MAX_MEMORY_SIZE) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t new_size = ALIGN(offset + size);
|
||||||
|
if (new_size > MAX_MEMORY_SIZE) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (new_size > __memory_size) {
|
||||||
|
__memory_size = new_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (void *)&__memory[__memory_size];
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t __msize() {
|
||||||
|
return __memory_size;
|
||||||
|
}
|
||||||
|
|
||||||
void * memset(void *b, int c, size_t len) {
|
void * memset(void *b, int c, size_t len) {
|
||||||
uint8_t *dest = b;
|
uint8_t *dest = b;
|
||||||
while (len-- > 0) *dest++ = c;
|
while (len-- > 0) *dest++ = c;
|
||||||
@@ -37,18 +62,6 @@ void * memmove(void *dst, const void *src, size_t n) {
|
|||||||
return dst;
|
return dst;
|
||||||
}
|
}
|
||||||
|
|
||||||
void * __sbrk(uint32_t size) {
|
|
||||||
uint32_t address;
|
|
||||||
__asm__ __volatile__(
|
|
||||||
".insn r 0xb, 1, 0, %[dst], %[sz], zero"
|
|
||||||
: [dst] "=r" (address)
|
|
||||||
: [sz] "ir" (size)
|
|
||||||
:
|
|
||||||
);
|
|
||||||
return (void *)address;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Imports
|
// Imports
|
||||||
|
|
||||||
POLKAVM_IMPORT(void, input, uint32_t, uint32_t)
|
POLKAVM_IMPORT(void, input, uint32_t, uint32_t)
|
||||||
|
|||||||
@@ -1206,7 +1206,7 @@ where
|
|||||||
anyhow::bail!("The `BLOBBASEFEE` instruction is not supported until zkVM v1.5.0");
|
anyhow::bail!("The `BLOBBASEFEE` instruction is not supported until zkVM v1.5.0");
|
||||||
}
|
}
|
||||||
InstructionName::MSIZE => {
|
InstructionName::MSIZE => {
|
||||||
revive_llvm_context::polkavm_evm_contract_context::msize(context).map(Some)
|
revive_llvm_context::polkavm_evm_memory::msize(context).map(Some)
|
||||||
}
|
}
|
||||||
|
|
||||||
InstructionName::CALLCODE => {
|
InstructionName::CALLCODE => {
|
||||||
|
|||||||
@@ -276,18 +276,26 @@ pub fn compile_blob(contract_name: &str, source_code: &str) -> Vec<u8> {
|
|||||||
/// Compile the EVM bin-runtime of `contract_name` found in given `source_code`.
|
/// Compile the EVM bin-runtime of `contract_name` found in given `source_code`.
|
||||||
/// The `solc` optimizer will be enabled
|
/// The `solc` optimizer will be enabled
|
||||||
pub fn compile_evm_bin_runtime(contract_name: &str, source_code: &str) -> Vec<u8> {
|
pub fn compile_evm_bin_runtime(contract_name: &str, source_code: &str) -> Vec<u8> {
|
||||||
compile_evm(contract_name, source_code, true)
|
compile_evm(contract_name, source_code, true, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compile the EVM bin of `contract_name` found in given `source_code`.
|
/// Compile the EVM bin of `contract_name` found in given `source_code`.
|
||||||
/// The `solc` optimizer will be enabled
|
/// The `solc` optimizer will be enabled
|
||||||
pub fn compile_evm_deploy_code(contract_name: &str, source_code: &str) -> Vec<u8> {
|
pub fn compile_evm_deploy_code(
|
||||||
compile_evm(contract_name, source_code, false)
|
contract_name: &str,
|
||||||
|
source_code: &str,
|
||||||
|
solc_optimizer_enabled: bool,
|
||||||
|
) -> Vec<u8> {
|
||||||
|
compile_evm(contract_name, source_code, solc_optimizer_enabled, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_evm(contract_name: &str, source_code: &str, runtime: bool) -> Vec<u8> {
|
fn compile_evm(
|
||||||
|
contract_name: &str,
|
||||||
|
source_code: &str,
|
||||||
|
solc_optimizer_enabled: bool,
|
||||||
|
runtime: bool,
|
||||||
|
) -> Vec<u8> {
|
||||||
let pipeline = SolcPipeline::Yul;
|
let pipeline = SolcPipeline::Yul;
|
||||||
let solc_optimizer_enabled = true;
|
|
||||||
let id = CachedBlob {
|
let id = CachedBlob {
|
||||||
contract_name: contract_name.to_owned(),
|
contract_name: contract_name.to_owned(),
|
||||||
pipeline,
|
pipeline,
|
||||||
|
|||||||
@@ -963,9 +963,7 @@ impl FunctionCall {
|
|||||||
location
|
location
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Name::MSize => {
|
Name::MSize => revive_llvm_context::polkavm_evm_memory::msize(context).map(Some),
|
||||||
revive_llvm_context::polkavm_evm_contract_context::msize(context).map(Some)
|
|
||||||
}
|
|
||||||
|
|
||||||
Name::Verbatim {
|
Name::Verbatim {
|
||||||
input_size,
|
input_size,
|
||||||
|
|||||||
Reference in New Issue
Block a user