Skip to content
This repository has been archived by the owner on Mar 24, 2022. It is now read-only.

Commit

Permalink
Add function import/export information to module data (#177)
Browse files Browse the repository at this point in the history
* add function import/export information to module data

* run (and other functions operating on function names) take &str now, rather than byte slices

* uncomment parts of lucetc tests that are now expressible

* add FunctionIndex newtype instead of passing around raw u32, as well

also note that FunctionMetadata's sym is possibly related to, but not an indicator of, exported- or imported-ness.
  • Loading branch information
awortman-fastly authored May 16, 2019
1 parent fa67673 commit 6806813
Show file tree
Hide file tree
Showing 27 changed files with 359 additions and 230 deletions.
14 changes: 7 additions & 7 deletions benchmarks/lucet-benchmarks/src/modules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ pub fn null_mock() -> Arc<dyn Module> {
extern "C" fn f(_vmctx: *mut lucet_vmctx) {}

MockModuleBuilder::new()
.with_export_func(MockExportBuilder::new(b"f", FunctionPointer::from_usize(f as usize)))
.with_export_func(MockExportBuilder::new("f", FunctionPointer::from_usize(f as usize)))
.build()
}

Expand All @@ -50,7 +50,7 @@ pub fn large_dense_heap_mock(heap_kb: usize) -> Arc<dyn Module> {
});

MockModuleBuilder::new()
.with_export_func(MockExportBuilder::new(b"f", FunctionPointer::from_usize(f as usize)))
.with_export_func(MockExportBuilder::new("f", FunctionPointer::from_usize(f as usize)))
.with_initial_heap(heap.as_slice())
.with_heap_spec(heap_spec)
.build()
Expand Down Expand Up @@ -81,7 +81,7 @@ pub fn large_sparse_heap_mock(heap_kb: usize, stride: usize) -> Arc<dyn Module>
});

MockModuleBuilder::new()
.with_export_func(MockExportBuilder::new(b"f", FunctionPointer::from_usize(f as usize)))
.with_export_func(MockExportBuilder::new("f", FunctionPointer::from_usize(f as usize)))
.with_initial_heap(heap.as_slice())
.with_heap_spec(heap_spec)
.build()
Expand All @@ -102,7 +102,7 @@ pub fn fib_mock() -> Arc<dyn Module> {
}

MockModuleBuilder::new()
.with_export_func(MockExportBuilder::new(b"f", FunctionPointer::from_usize(f as usize)))
.with_export_func(MockExportBuilder::new("f", FunctionPointer::from_usize(f as usize)))
.build()
}

Expand Down Expand Up @@ -179,7 +179,7 @@ pub fn many_args_mock() -> Arc<dyn Module> {
}

MockModuleBuilder::new()
.with_export_func(MockExportBuilder::new(b"f", FunctionPointer::from_usize(f as usize)))
.with_export_func(MockExportBuilder::new("f", FunctionPointer::from_usize(f as usize)))
.build()
}

Expand Down Expand Up @@ -255,8 +255,8 @@ pub fn hostcalls_mock() -> Arc<dyn Module> {

MockModuleBuilder::new()
.with_export_func(
MockExportBuilder::new(b"wrapped", FunctionPointer::from_usize(wrapped as usize)))
MockExportBuilder::new("wrapped", FunctionPointer::from_usize(wrapped as usize)))
.with_export_func(
MockExportBuilder::new(b"raw", FunctionPointer::from_usize(raw as usize)))
MockExportBuilder::new("raw", FunctionPointer::from_usize(raw as usize)))
.build()
}
2 changes: 1 addition & 1 deletion benchmarks/lucet-benchmarks/src/par.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ fn par_run<R: RegionCreate + 'static>(
.unwrap()
.install(|| {
handles.par_iter_mut().for_each(|handle| {
handle.run(b"f", &[]).unwrap();
handle.run("f", &[]).unwrap();
})
})
}
Expand Down
12 changes: 6 additions & 6 deletions benchmarks/lucet-benchmarks/src/seq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ fn drop_instance_with_sparse_heap<R: RegionCreate + 'static>(c: &mut Criterion)
/// switching overhead.
fn run_null<R: RegionCreate + 'static>(c: &mut Criterion) {
fn body(inst: &mut InstanceHandle) {
inst.run(b"f", &[]).unwrap();
inst.run("f", &[]).unwrap();
}

let module = null_mock();
Expand All @@ -222,7 +222,7 @@ fn run_null<R: RegionCreate + 'static>(c: &mut Criterion) {
/// cost of the Lucet runtime.
fn run_fib<R: RegionCreate + 'static>(c: &mut Criterion) {
fn body(inst: &mut InstanceHandle) {
inst.run(b"f", &[]).unwrap();
inst.run("f", &[]).unwrap();
}

let module = fib_mock();
Expand All @@ -240,7 +240,7 @@ fn run_fib<R: RegionCreate + 'static>(c: &mut Criterion) {
/// Run a trivial WASI program.
fn run_hello<R: RegionCreate + 'static>(c: &mut Criterion) {
fn body(inst: &mut InstanceHandle) {
inst.run(b"_start", &[]).unwrap();
inst.run("_start", &[]).unwrap();
}

let workdir = TempDir::new().expect("create working directory");
Expand Down Expand Up @@ -281,7 +281,7 @@ fn run_hello<R: RegionCreate + 'static>(c: &mut Criterion) {
fn run_many_args<R: RegionCreate + 'static>(c: &mut Criterion) {
fn body(inst: &mut InstanceHandle) {
inst.run(
b"f",
"f",
&[
0xAFu8.into(),
0xAFu16.into(),
Expand Down Expand Up @@ -368,7 +368,7 @@ fn run_many_args<R: RegionCreate + 'static>(c: &mut Criterion) {

fn run_hostcall_wrapped<R: RegionCreate + 'static>(c: &mut Criterion) {
fn body(inst: &mut InstanceHandle) {
inst.run(b"wrapped", &[]).unwrap();
inst.run("wrapped", &[]).unwrap();
}

let module = hostcalls_mock();
Expand All @@ -388,7 +388,7 @@ fn run_hostcall_wrapped<R: RegionCreate + 'static>(c: &mut Criterion) {

fn run_hostcall_raw<R: RegionCreate + 'static>(c: &mut Criterion) {
fn body(inst: &mut InstanceHandle) {
inst.run(b"raw", &[]).unwrap();
inst.run("raw", &[]).unwrap();
}

let module = hostcalls_mock();
Expand Down
79 changes: 74 additions & 5 deletions lucet-module-data/src/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,70 @@ use serde::{Deserialize, Serialize};

use std::slice::from_raw_parts;

/// FunctionIndex is an identifier for a function, imported, exported, or external. The space of
/// FunctionIndex is shared for all of these, so `FunctionIndex(N)` may identify exported function
/// #2, `FunctionIndex(N + 1)` may identify an internal function, and `FunctionIndex(N + 2)` may
/// identify an imported function.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
pub struct FunctionIndex(u32);

impl FunctionIndex {
pub fn from_u32(idx: u32) -> FunctionIndex {
FunctionIndex(idx)
}
pub fn as_u32(&self) -> u32 {
self.0
}
}

/// ImportFunction describes an internal function - its internal function index and the name/module
/// pair that function should be found in.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
pub struct ImportFunction<'a> {
pub fn_idx: FunctionIndex,
pub module: &'a str,
pub name: &'a str
}

/// ExportFunction describes an exported function - its internal function index and a name that
/// function has been exported under.
#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
pub struct ExportFunction<'a> {
pub fn_idx: FunctionIndex,
#[serde(borrow)]
pub names: Vec<&'a str>
}

pub struct OwnedExportFunction {
pub fn_idx: FunctionIndex,
pub names: Vec<String>
}

impl OwnedExportFunction {
pub fn to_ref<'a>(&'a self) -> ExportFunction<'a> {
ExportFunction {
fn_idx: self.fn_idx.clone(),
names: self.names.iter().map(|x| x.as_str()).collect()
}
}
}

pub struct OwnedImportFunction {
pub fn_idx: FunctionIndex,
pub module: String,
pub name: String
}

impl OwnedImportFunction {
pub fn to_ref<'a>(&'a self) -> ImportFunction<'a> {
ImportFunction {
fn_idx: self.fn_idx.clone(),
module: self.module.as_str(),
name: self.name.as_str(),
}
}
}

/// UniqueSignatureIndex names a signature after collapsing duplicate signatures to a single
/// identifier, whereas SignatureIndex is directly what the original module specifies, and may
/// specify duplicates of types that are structurally equal.
Expand Down Expand Up @@ -37,28 +101,33 @@ impl FunctionPointer {
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct FunctionMetadata<'a> {
pub signature: UniqueSignatureIndex,
/// the "name" field is some human-friendly name, not necessarily the same as used to reach
/// this function (through an export, for example), and may not even indicate that a function
/// is exported at all.
/// TODO: at some point when possible, this field ought to be set from the names section of a
/// wasm module. At the moment that information is lost at parse time.
#[serde(borrow)]
pub sym: Option<&'a [u8]>,
pub name: Option<&'a str>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct OwnedFunctionMetadata {
pub signature: UniqueSignatureIndex,
pub sym: Option<Vec<u8>>,
pub name: Option<String>,
}

impl OwnedFunctionMetadata {
pub fn to_ref<'a>(&'a self) -> FunctionMetadata<'a> {
pub fn to_ref(&self) -> FunctionMetadata {
FunctionMetadata {
signature: self.signature.clone(),
sym: self.sym.as_ref().map(|s| s.as_slice()).clone(),
name: self.name.as_ref().map(|n| n.as_str()),
}
}
}

pub struct FunctionHandle {
pub ptr: FunctionPointer,
pub id: u32
pub id: FunctionIndex
}

// The layout of this struct is very tightly coupled to lucetc's `write_function_manifest`!
Expand Down
4 changes: 2 additions & 2 deletions lucet-module-data/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub use crate::error::Error;
pub use crate::globals::{Global, GlobalDef, GlobalSpec};
pub use crate::linear_memory::{HeapSpec, SparseData, LinearMemorySpec};
pub use crate::module_data::ModuleData;
pub use crate::functions::{FunctionHandle, FunctionMetadata, FunctionPointer, FunctionSpec, UniqueSignatureIndex};
pub use crate::functions::{ExportFunction, FunctionHandle, FunctionIndex, FunctionMetadata, FunctionPointer, FunctionSpec, ImportFunction, UniqueSignatureIndex};
pub use crate::traps::{TrapManifest, TrapSite, TrapCode};
pub use crate::types::{Signature, ValueType};

Expand All @@ -24,5 +24,5 @@ pub mod owned {
pub use crate::globals::OwnedGlobalSpec;
pub use crate::linear_memory::{OwnedSparseData, OwnedLinearMemorySpec};
pub use crate::module_data::OwnedModuleData;
pub use crate::functions::OwnedFunctionMetadata;
pub use crate::functions::{OwnedFunctionMetadata, OwnedExportFunction, OwnedImportFunction};
}
50 changes: 37 additions & 13 deletions lucet-module-data/src/module_data.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
functions::{FunctionMetadata, OwnedFunctionMetadata},
functions::{ExportFunction, FunctionIndex, FunctionMetadata, ImportFunction, OwnedFunctionMetadata},
globals::GlobalSpec,
linear_memory::{HeapSpec, LinearMemorySpec, SparseData},
types::Signature,
Expand All @@ -23,6 +23,10 @@ pub struct ModuleData<'a> {
globals_spec: Vec<GlobalSpec<'a>>,
#[serde(borrow)]
function_info: Vec<FunctionMetadata<'a>>,
#[serde(borrow)]
import_functions: Vec<ImportFunction<'a>>,
#[serde(borrow)]
export_functions: Vec<ExportFunction<'a>>,
signatures: Vec<Signature>,
}

Expand All @@ -31,12 +35,16 @@ impl<'a> ModuleData<'a> {
linear_memory: Option<LinearMemorySpec<'a>>,
globals_spec: Vec<GlobalSpec<'a>>,
function_info: Vec<FunctionMetadata<'a>>,
import_functions: Vec<ImportFunction<'a>>,
export_functions: Vec<ExportFunction<'a>>,
signatures: Vec<Signature>,
) -> Self {
Self {
linear_memory,
globals_spec,
function_info,
import_functions,
export_functions,
signatures,
}
}
Expand All @@ -61,25 +69,32 @@ impl<'a> ModuleData<'a> {
&self.globals_spec
}

pub fn function_info(&self) -> &[FunctionMetadata<'a>] {
&self.function_info
}

pub fn import_functions(&self) -> &[ImportFunction] {
&self.import_functions
}

pub fn export_functions(&self) -> &[ExportFunction] {
&self.export_functions
}

// Function index here is a different index space than `get_func_from_idx`, which
// uses function index as an index into a table of function elements.
//
// This is an index of all functions in the module.
pub fn get_signature(&self, fn_id: u32) -> &Signature {
let sig_idx = self.function_info[fn_id as usize].signature;
pub fn get_signature(&self, fn_id: FunctionIndex) -> &Signature {
let sig_idx = self.function_info[fn_id.as_u32() as usize].signature;
&self.signatures[sig_idx.as_u32() as usize]
}

pub fn function_id_by_name(&self, name: &[u8]) -> Option<u32> {
self.function_info
pub fn get_export_func_id(&self, name: &str) -> Option<FunctionIndex> {
self.export_functions
.iter()
.enumerate()
.find(|(_, fn_meta)| { fn_meta.sym == Some(name) })
.map(|(i, _)| i as u32)
}

pub fn sym_for(&self, fn_id: u32) -> Option<&[u8]> {
self.function_info.get(fn_id as usize).and_then(|func| func.sym)
.find(|export| export.names.contains(&name))
.map(|export| export.fn_idx)
}

pub fn signatures(&self) -> &[Signature] {
Expand All @@ -100,6 +115,7 @@ impl<'a> ModuleData<'a> {
use crate::{
globals::OwnedGlobalSpec,
linear_memory::{OwnedLinearMemorySpec, OwnedSparseData},
functions::{OwnedExportFunction, OwnedImportFunction},
};

/// The metadata (and some data) for a Lucet module.
Expand All @@ -111,6 +127,8 @@ pub struct OwnedModuleData {
linear_memory: Option<OwnedLinearMemorySpec>,
globals_spec: Vec<OwnedGlobalSpec>,
function_info: Vec<OwnedFunctionMetadata>,
imports: Vec<OwnedImportFunction>,
exports: Vec<OwnedExportFunction>,
signatures: Vec<Signature>,
}

Expand All @@ -119,12 +137,16 @@ impl OwnedModuleData {
linear_memory: Option<OwnedLinearMemorySpec>,
globals_spec: Vec<OwnedGlobalSpec>,
function_info: Vec<OwnedFunctionMetadata>,
imports: Vec<OwnedImportFunction>,
exports: Vec<OwnedExportFunction>,
signatures: Vec<Signature>,
) -> Self {
Self {
linear_memory,
globals_spec,
function_info,
imports,
exports,
signatures,
}
}
Expand All @@ -140,12 +162,14 @@ impl OwnedModuleData {
},
self.globals_spec.iter().map(|gs| gs.to_ref()).collect(),
self.function_info.iter().map(|info| info.to_ref()).collect(),
self.imports.iter().map(|imp| imp.to_ref()).collect(),
self.exports.iter().map(|exp| exp.to_ref()).collect(),
self.signatures.clone(),
)
}

pub fn empty() -> Self {
Self::new(None, vec![], vec![], vec![])
Self::new(None, vec![], vec![], vec![], vec![], vec![])
}

pub fn with_heap_spec(mut self, heap_spec: HeapSpec) -> Self {
Expand Down
6 changes: 3 additions & 3 deletions lucet-runtime/lucet-runtime-internals/src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -286,11 +286,11 @@ impl Instance {
/// # use lucet_runtime_internals::instance::InstanceHandle;
/// # let instance: InstanceHandle = unimplemented!();
/// // regular execution yields `Ok(UntypedRetVal)`
/// let retval = instance.run(b"factorial", &[5u64.into()]).unwrap();
/// let retval = instance.run("factorial", &[5u64.into()]).unwrap();
/// assert_eq!(u64::from(retval), 120u64);
///
/// // runtime faults yield `Err(Error)`
/// let result = instance.run(b"faulting_function", &[]);
/// let result = instance.run("faulting_function", &[]);
/// assert!(result.is_err());
/// ```
///
Expand All @@ -310,7 +310,7 @@ impl Instance {
///
/// For the moment, we do not mark this as `unsafe` in the Rust type system, but that may change
/// in the future.
pub fn run(&mut self, entrypoint: &[u8], args: &[Val]) -> Result<UntypedRetVal, Error> {
pub fn run(&mut self, entrypoint: &str, args: &[Val]) -> Result<UntypedRetVal, Error> {
let func = self.module.get_export_func(entrypoint)?;
self.run_func(func, &args)
}
Expand Down
Loading

0 comments on commit 6806813

Please sign in to comment.