Skip to content

Commit

Permalink
Merge pull request #6 from ElrondNetwork/instance-caching
Browse files Browse the repository at this point in the history
Add instance caching functionality and C API (WIP)
  • Loading branch information
sasurobert authored Nov 2, 2020
2 parents 4707e0a + 5b565a4 commit 100fc1b
Show file tree
Hide file tree
Showing 9 changed files with 193 additions and 31 deletions.
16 changes: 10 additions & 6 deletions lib/middleware-common/src/metering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::metering_costs::{get_opcode_index, get_local_allocate_cost_index};
use crate::runtime_breakpoints::push_runtime_breakpoint;

static FIELD_USED_POINTS: InternalField = InternalField::allocate();
static FIELD_POINTS_LIMIT: InternalField = InternalField::allocate();
pub const BREAKPOINT_VALUE__OUT_OF_GAS: u64 = 4;

/// Metering is a compiler middleware that calculates the cost of WebAssembly instructions at compile
Expand All @@ -26,17 +27,15 @@ pub const BREAKPOINT_VALUE__OUT_OF_GAS: u64 = 4;
///
pub struct Metering<'a> {
limit: u64,
unmetered_locals: usize,
current_block: u64,
func_locals_costs: u32,
opcode_costs: &'a [u32],
}

impl<'a> Metering<'a> {
pub fn new(limit: u64, opcode_costs: &'a [u32], unmetered_locals: usize) -> Metering<'a> {
pub fn new(opcode_costs: &'a [u32], unmetered_locals: usize) -> Metering<'a> {
Metering {
limit,
unmetered_locals,
current_block: 0,
func_locals_costs: 0,
Expand Down Expand Up @@ -101,9 +100,9 @@ impl<'q> FunctionMiddleware for Metering<'q> {
sink.push(Event::Internal(InternalEvent::GetInternal(
FIELD_USED_POINTS.index() as _,
)));
sink.push(Event::WasmOwned(Operator::I64Const {
value: self.limit as i64,
}));
sink.push(Event::Internal(InternalEvent::GetInternal(
FIELD_POINTS_LIMIT.index() as _,
)));
sink.push(Event::WasmOwned(Operator::I64GeU));
sink.push(Event::WasmOwned(Operator::If {
ty: WpTypeOrFuncType::Type(WpType::EmptyBlockType),
Expand Down Expand Up @@ -150,6 +149,11 @@ pub fn set_points_used(instance: &mut Instance, value: u64) {
instance.set_internal(&FIELD_USED_POINTS, value);
}

/// Sets the limit of points to be used by an Instance.
pub fn set_points_limit(instance: &mut Instance, value: u64) {
instance.set_internal(&FIELD_POINTS_LIMIT, value);
}

/// Returns the number of points used in a Ctx.
pub fn get_points_used_ctx(ctx: &Ctx) -> u64 {
ctx.get_internal(&FIELD_USED_POINTS)
Expand Down
8 changes: 4 additions & 4 deletions lib/runtime-c-api/src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,18 +236,19 @@ pub unsafe extern "C" fn wasmer_instantiate_with_options(

let import_object: &mut ImportObject = &mut *(GLOBAL_IMPORT_OBJECT as *mut ImportObject);
let result_instantiation = new_module.instantiate(&import_object);
let new_instance = match result_instantiation {
let mut new_instance = match result_instantiation {
Ok(instance) => instance,
Err(error) => {
update_last_error(error);
return wasmer_result_t::WASMER_ERROR;
}
};
metering::set_points_limit(&mut new_instance, options.gas_limit);
*instance = Box::into_raw(Box::new(new_instance)) as *mut wasmer_instance_t;
wasmer_result_t::WASMER_OK
}

unsafe fn prepare_middleware_chain_generator(
pub unsafe fn prepare_middleware_chain_generator(
options: &CompilationOptions
) -> impl Fn() -> MiddlewareChain + '_ {
let options = options.clone();
Expand All @@ -258,7 +259,6 @@ unsafe fn prepare_middleware_chain_generator(
if options.metering {
#[cfg(feature = "metering")]
chain.push(metering::Metering::new(
options.gas_limit,
&OPCODE_COSTS,
options.unmetered_locals
));
Expand All @@ -278,7 +278,7 @@ unsafe fn prepare_middleware_chain_generator(
chain_generator
}

unsafe fn get_compiler(chain_generator: impl Fn() -> MiddlewareChain) -> impl Compiler {
pub unsafe fn get_compiler(chain_generator: impl Fn() -> MiddlewareChain) -> impl Compiler {
#[cfg(feature = "llvm-backend")]
use wasmer_llvm_backend::ModuleCodeGenerator as MeteredMCG;

Expand Down
111 changes: 111 additions & 0 deletions lib/runtime-c-api/src/instance_cache.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
use crate::{
error::{update_last_error, CApiError},
instance::{wasmer_instance_t, wasmer_compilation_options_t, CompilationOptions, prepare_middleware_chain_generator, get_compiler},
wasmer_result_t,
};
use wasmer_runtime_core::{cache::Artifact, import::ImportObject};
use std::slice;
use crate::import::GLOBAL_IMPORT_OBJECT;

#[cfg(not(feature = "cranelift-backend"))]
use wasmer_middleware_common::metering;

#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub unsafe extern "C" fn wasmer_instance_cache(
instance: *mut wasmer_instance_t,
cache_bytes: *mut *const u8,
cache_len: *mut u32,
) -> wasmer_result_t {
if instance.is_null() {
update_last_error(CApiError {
msg: "null instance".to_string(),
});
return wasmer_result_t::WASMER_ERROR;
}

let instance = &mut *(instance as *mut wasmer_runtime::Instance);
let module = instance.module();
match module.cache() {
Err(error) => {
update_last_error(CApiError {
msg: format!("{:?}", error),
});
return wasmer_result_t::WASMER_ERROR;
}
Ok(artifact) => {
match artifact.serialize() {
Err(error) => {
update_last_error(CApiError {
msg: format!("{:?}", error),
});
return wasmer_result_t::WASMER_ERROR;
}
Ok(bytes_vec) => {
if !bytes_vec.is_empty() {
let buf = bytes_vec.into_boxed_slice();
*cache_bytes = buf.as_ptr();
*cache_len = buf.len() as u32;
std::mem::forget(buf);
}
}
}
}
};

wasmer_result_t::WASMER_OK
}

#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
pub unsafe extern "C" fn wasmer_instance_from_cache(
instance: *mut *mut wasmer_instance_t,
cache_bytes: *mut u8,
cache_len: u32,
options: *const wasmer_compilation_options_t,
) -> wasmer_result_t {
if cache_bytes.is_null() {
update_last_error(CApiError {
msg: "cache bytes ptr is null".to_string(),
});
return wasmer_result_t::WASMER_ERROR;
}

let bytes: &[u8] = slice::from_raw_parts(cache_bytes, cache_len as usize);
let options: &CompilationOptions = &*(options as *const CompilationOptions);
let compiler_chain_generator = prepare_middleware_chain_generator(&options);
let compiler = get_compiler(compiler_chain_generator);
let new_module = match Artifact::deserialize(bytes) {
Ok(serialized_cache) => match wasmer_runtime_core::load_cache_with(serialized_cache, &compiler) {
Ok(deserialized_module) => {
deserialized_module
}
Err(_) => {
update_last_error(CApiError {
msg: "Failed to compile the serialized module".to_string(),
});
return wasmer_result_t::WASMER_ERROR;
}
},
Err(err) => {
println!("{:?}", err);
update_last_error(CApiError {
msg: "Failed to deserialize the module".to_string(),
});
return wasmer_result_t::WASMER_ERROR;
}
};

let import_object: &mut ImportObject = &mut *(GLOBAL_IMPORT_OBJECT as *mut ImportObject);
let result_instantiation = new_module.instantiate(&import_object);
let mut new_instance = match result_instantiation {
Ok(instance) => instance,
Err(error) => {
update_last_error(error);
return wasmer_result_t::WASMER_ERROR;
}
};
metering::set_points_limit(&mut new_instance, options.gas_limit);
*instance = Box::into_raw(Box::new(new_instance)) as *mut wasmer_instance_t;
wasmer_result_t::WASMER_OK
}
2 changes: 2 additions & 0 deletions lib/runtime-c-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ pub mod memory;
#[cfg(feature = "metering")]
pub mod metering;

pub mod instance_cache;

pub mod runtime_breakpoints;

pub mod module;
Expand Down
24 changes: 19 additions & 5 deletions lib/runtime-c-api/src/metering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,22 @@ pub unsafe extern "C" fn wasmer_instance_set_points_used(
metering::set_points_used(instance, new_gas)
}

// sets gas limit
#[allow(clippy::cast_ptr_alignment)]
#[no_mangle]
#[cfg(feature = "metering")]
pub unsafe extern "C" fn wasmer_instance_set_points_limit(
instance: *mut wasmer_instance_t,
limit: u64,
) {
if instance.is_null() {
return;
}
let instance = &mut *(instance as *mut wasmer_runtime::Instance);
metering::set_points_limit(instance, limit)
}


/// Creates a new Module with gas limit from the given wasm bytes.
///
/// Returns `wasmer_result_t::WASMER_OK` upon success.
Expand All @@ -69,7 +85,6 @@ pub unsafe extern "C" fn wasmer_compile_with_gas_metering(
module: *mut *mut wasmer_module_t,
wasm_bytes: *mut u8,
wasm_bytes_len: u32,
gas_limit: u64,
) -> wasmer_result_t {
if module.is_null() {
update_last_error(CApiError {
Expand All @@ -84,7 +99,7 @@ pub unsafe extern "C" fn wasmer_compile_with_gas_metering(
return wasmer_result_t::WASMER_ERROR;
}

let compiler = get_metered_compiler(gas_limit);
let compiler = get_metered_compiler();

let bytes: &[u8] = slice::from_raw_parts_mut(wasm_bytes, wasm_bytes_len as usize);
let result = wasmer_runtime_core::compile_with(bytes, &compiler);
Expand All @@ -102,7 +117,7 @@ pub unsafe extern "C" fn wasmer_compile_with_gas_metering(
}

#[cfg(feature = "metering")]
unsafe fn get_metered_compiler(limit: u64) -> impl Compiler {
unsafe fn get_metered_compiler() -> impl Compiler {
use wasmer_runtime_core::codegen::{MiddlewareChain, StreamingCompiler};

#[cfg(feature = "llvm-backend")]
Expand All @@ -119,7 +134,7 @@ unsafe fn get_metered_compiler(limit: u64) -> impl Compiler {
let c: StreamingCompiler<MeteredMCG, _, _, _, _> = StreamingCompiler::new(move || {
let mut chain = MiddlewareChain::new();

chain.push(metering::Metering::new(limit, &OPCODE_COSTS, 0));
chain.push(metering::Metering::new(&OPCODE_COSTS, 0));
chain.push(runtime_breakpoints::RuntimeBreakpointHandler::new());

chain
Expand All @@ -137,7 +152,6 @@ pub unsafe extern "C" fn wasmer_compile_with_gas_metering(
module: *mut *mut wasmer_module_t,
wasm_bytes: *mut u8,
wasm_bytes_len: u32,
_: u64,
) -> wasmer_result_t {
if module.is_null() {
update_last_error(CApiError {
Expand Down
2 changes: 1 addition & 1 deletion lib/runtime-c-api/tests/test-module-metering-serialize.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ int main()

wasmer_module_t *module_one = NULL;
unsigned long long gas_limit = 100;
wasmer_result_t compile_result = wasmer_compile_with_gas_metering(&module_one, bytes, len, gas_limit);
wasmer_result_t compile_result = wasmer_compile_with_gas_metering(&module_one, bytes, len);
printf("Compile result: %d\n", compile_result);
assert(compile_result == WASMER_OK);

Expand Down
18 changes: 16 additions & 2 deletions lib/runtime-c-api/wasmer.h
Original file line number Diff line number Diff line change
Expand Up @@ -431,8 +431,11 @@ wasmer_result_t wasmer_compile(wasmer_module_t **module,
*/
wasmer_result_t wasmer_compile_with_gas_metering(wasmer_module_t **module,
uint8_t *wasm_bytes,
uint32_t wasm_bytes_len,
uint64_t gas_limit);
uint32_t wasm_bytes_len);

wasmer_result_t wasmer_compile_with_gas_metering(wasmer_module_t **module,
uint8_t *wasm_bytes,
uint32_t wasm_bytes_len);

#if defined(WASMER_EMSCRIPTEN_ENABLED)
/**
Expand Down Expand Up @@ -854,6 +857,10 @@ wasmer_import_object_iter_t *wasmer_import_object_iterate_functions(const wasmer
*/
wasmer_import_object_t *wasmer_import_object_new(void);

wasmer_result_t wasmer_instance_cache(wasmer_instance_t *instance,
const uint8_t **cache_bytes,
uint32_t *cache_len);

/**
* Calls an exported function of a WebAssembly instance by `name`
* with the provided parameters. The exported function results are
Expand Down Expand Up @@ -1067,10 +1074,17 @@ void wasmer_instance_destroy(wasmer_instance_t *instance);
*/
void wasmer_instance_exports(wasmer_instance_t *instance, wasmer_exports_t **exports);

wasmer_result_t wasmer_instance_from_cache(wasmer_instance_t **instance,
uint8_t *cache_bytes,
uint32_t cache_len,
const wasmer_compilation_options_t *options);

uint64_t wasmer_instance_get_points_used(wasmer_instance_t *instance);

uint64_t wasmer_instance_get_runtime_breakpoint_value(wasmer_instance_t *instance);

void wasmer_instance_set_points_limit(wasmer_instance_t *instance, uint64_t limit);

void wasmer_instance_set_points_used(wasmer_instance_t *instance, uint64_t new_gas);

void wasmer_instance_set_runtime_breakpoint_value(wasmer_instance_t *instance, uint64_t value);
Expand Down
18 changes: 16 additions & 2 deletions lib/runtime-c-api/wasmer.hh
Original file line number Diff line number Diff line change
Expand Up @@ -346,8 +346,11 @@ wasmer_result_t wasmer_compile(wasmer_module_t **module,
/// and `wasmer_last_error_message` to get an error message.
wasmer_result_t wasmer_compile_with_gas_metering(wasmer_module_t **module,
uint8_t *wasm_bytes,
uint32_t wasm_bytes_len,
uint64_t gas_limit);
uint32_t wasm_bytes_len);

wasmer_result_t wasmer_compile_with_gas_metering(wasmer_module_t **module,
uint8_t *wasm_bytes,
uint32_t wasm_bytes_len);

#if defined(WASMER_EMSCRIPTEN_ENABLED)
/// Convenience function for setting up arguments and calling the Emscripten
Expand Down Expand Up @@ -669,6 +672,10 @@ wasmer_import_object_iter_t *wasmer_import_object_iterate_functions(const wasmer
/// See also `wasmer_import_object_append`
wasmer_import_object_t *wasmer_import_object_new();

wasmer_result_t wasmer_instance_cache(wasmer_instance_t *instance,
const uint8_t **cache_bytes,
uint32_t *cache_len);

/// Calls an exported function of a WebAssembly instance by `name`
/// with the provided parameters. The exported function results are
/// stored on the provided `results` pointer.
Expand Down Expand Up @@ -868,10 +875,17 @@ void wasmer_instance_destroy(wasmer_instance_t *instance);
/// ```
void wasmer_instance_exports(wasmer_instance_t *instance, wasmer_exports_t **exports);

wasmer_result_t wasmer_instance_from_cache(wasmer_instance_t **instance,
uint8_t *cache_bytes,
uint32_t cache_len,
const wasmer_compilation_options_t *options);

uint64_t wasmer_instance_get_points_used(wasmer_instance_t *instance);

uint64_t wasmer_instance_get_runtime_breakpoint_value(wasmer_instance_t *instance);

void wasmer_instance_set_points_limit(wasmer_instance_t *instance, uint64_t limit);

void wasmer_instance_set_points_used(wasmer_instance_t *instance, uint64_t new_gas);

void wasmer_instance_set_runtime_breakpoint_value(wasmer_instance_t *instance, uint64_t value);
Expand Down
Loading

0 comments on commit 100fc1b

Please sign in to comment.