* Introduce a `TargetFrontendConfig` type. `TargetFrontendConfig` is information specific to the target which is provided to frontends to allow them to produce Cranelift IR for the target. Currently this includes the pointer size and the default calling convention. The default calling convention is now inferred from the target, rather than being a setting. cranelift-native is now just a provider of target information, rather than also being a provider of settings, which gives it a clearer role. And instead of having cranelift-frontend routines require the whole `TargetIsa`, just require the `TargetFrontendConfig`, and add a way to get the `TargetFrontendConfig` from a `Module`. Fixes #529. Fixes #555.
446 lines
18 KiB
Rust
446 lines
18 KiB
Rust
//! Helper functions to gather information for each of the non-function sections of a
|
|
//! WebAssembly module.
|
|
//!
|
|
//! The code of theses helper function is straightforward since it is only about reading metadata
|
|
//! about linear memories, tables, globals, etc. and storing them for later use.
|
|
//!
|
|
//! The special case of the initialize expressions for table elements offsets or global variables
|
|
//! is handled, according to the semantics of WebAssembly, to only specific expressions that are
|
|
//! interpreted on the fly.
|
|
use cranelift_codegen::ir::{self, AbiParam, Signature};
|
|
use cranelift_entity::EntityRef;
|
|
use environ::{ModuleEnvironment, WasmError, WasmResult};
|
|
use std::str::from_utf8;
|
|
use std::vec::Vec;
|
|
use translation_utils::{
|
|
type_to_type, FuncIndex, Global, GlobalIndex, GlobalInit, Memory, MemoryIndex, SignatureIndex,
|
|
Table, TableElementType, TableIndex,
|
|
};
|
|
use wasmparser;
|
|
use wasmparser::{
|
|
ExternalKind, FuncType, ImportSectionEntryType, MemoryType, Operator, Parser, ParserState,
|
|
WasmDecoder,
|
|
};
|
|
|
|
/// Reads the Type Section of the wasm module and returns the corresponding function signatures.
|
|
pub fn parse_function_signatures(
|
|
parser: &mut Parser,
|
|
environ: &mut ModuleEnvironment,
|
|
) -> WasmResult<()> {
|
|
loop {
|
|
match *parser.read() {
|
|
ParserState::EndSection => break,
|
|
ParserState::TypeSectionEntry(FuncType {
|
|
form: wasmparser::Type::Func,
|
|
ref params,
|
|
ref returns,
|
|
}) => {
|
|
let mut sig = Signature::new(environ.target_config().default_call_conv);
|
|
sig.params.extend(params.iter().map(|ty| {
|
|
let cret_arg: ir::Type = type_to_type(*ty)
|
|
.expect("only numeric types are supported in function signatures");
|
|
AbiParam::new(cret_arg)
|
|
}));
|
|
sig.returns.extend(returns.iter().map(|ty| {
|
|
let cret_arg: ir::Type = type_to_type(*ty)
|
|
.expect("only numeric types are supported in function signatures");
|
|
AbiParam::new(cret_arg)
|
|
}));
|
|
environ.declare_signature(&sig);
|
|
}
|
|
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
ref s => panic!("unexpected section content: {:?}", s),
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
/// Retrieves the imports from the imports section of the binary.
|
|
pub fn parse_import_section<'data>(
|
|
parser: &mut Parser<'data>,
|
|
environ: &mut ModuleEnvironment<'data>,
|
|
) -> WasmResult<()> {
|
|
loop {
|
|
match *parser.read() {
|
|
ParserState::ImportSectionEntry {
|
|
ty: ImportSectionEntryType::Function(sig),
|
|
module,
|
|
field,
|
|
} => {
|
|
// The input has already been validated, so we should be able to
|
|
// assume valid UTF-8 and use `from_utf8_unchecked` if performance
|
|
// becomes a concern here.
|
|
let module_name = from_utf8(module).unwrap();
|
|
let field_name = from_utf8(field).unwrap();
|
|
environ.declare_func_import(
|
|
SignatureIndex::new(sig as usize),
|
|
module_name,
|
|
field_name,
|
|
);
|
|
}
|
|
ParserState::ImportSectionEntry {
|
|
ty:
|
|
ImportSectionEntryType::Memory(MemoryType {
|
|
limits: ref memlimits,
|
|
shared,
|
|
}),
|
|
..
|
|
} => {
|
|
environ.declare_memory(Memory {
|
|
pages_count: memlimits.initial as usize,
|
|
maximum: memlimits.maximum.map(|x| x as usize),
|
|
shared,
|
|
});
|
|
}
|
|
ParserState::ImportSectionEntry {
|
|
ty: ImportSectionEntryType::Global(ref ty),
|
|
..
|
|
} => {
|
|
environ.declare_global(Global {
|
|
ty: type_to_type(ty.content_type).unwrap(),
|
|
mutability: ty.mutable,
|
|
initializer: GlobalInit::Import(),
|
|
});
|
|
}
|
|
ParserState::ImportSectionEntry {
|
|
ty: ImportSectionEntryType::Table(ref tab),
|
|
..
|
|
} => environ.declare_table(Table {
|
|
ty: match type_to_type(tab.element_type) {
|
|
Ok(t) => TableElementType::Val(t),
|
|
Err(()) => TableElementType::Func(),
|
|
},
|
|
size: tab.limits.initial as usize,
|
|
maximum: tab.limits.maximum.map(|x| x as usize),
|
|
}),
|
|
ParserState::EndSection => break,
|
|
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
ref s => panic!("unexpected section content: {:?}", s),
|
|
};
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
/// Retrieves the correspondences between functions and signatures from the function section
|
|
pub fn parse_function_section(
|
|
parser: &mut Parser,
|
|
environ: &mut ModuleEnvironment,
|
|
) -> WasmResult<()> {
|
|
loop {
|
|
match *parser.read() {
|
|
ParserState::FunctionSectionEntry(sigindex) => {
|
|
environ.declare_func_type(SignatureIndex::new(sigindex as usize));
|
|
}
|
|
ParserState::EndSection => break,
|
|
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
ref s => panic!("unexpected section content: {:?}", s),
|
|
};
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
/// Retrieves the names of the functions from the export section
|
|
pub fn parse_export_section<'data>(
|
|
parser: &mut Parser<'data>,
|
|
environ: &mut ModuleEnvironment<'data>,
|
|
) -> WasmResult<()> {
|
|
loop {
|
|
match *parser.read() {
|
|
ParserState::ExportSectionEntry {
|
|
field,
|
|
ref kind,
|
|
index,
|
|
} => {
|
|
// The input has already been validated, so we should be able to
|
|
// assume valid UTF-8 and use `from_utf8_unchecked` if performance
|
|
// becomes a concern here.
|
|
let name = from_utf8(field).unwrap();
|
|
let index = index as usize;
|
|
match *kind {
|
|
ExternalKind::Function => {
|
|
environ.declare_func_export(FuncIndex::new(index), name)
|
|
}
|
|
ExternalKind::Table => {
|
|
environ.declare_table_export(TableIndex::new(index), name)
|
|
}
|
|
ExternalKind::Memory => {
|
|
environ.declare_memory_export(MemoryIndex::new(index), name)
|
|
}
|
|
ExternalKind::Global => {
|
|
environ.declare_global_export(GlobalIndex::new(index), name)
|
|
}
|
|
}
|
|
}
|
|
ParserState::EndSection => break,
|
|
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
ref s => panic!("unexpected section content: {:?}", s),
|
|
};
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
/// Retrieves the start function index from the start section
|
|
pub fn parse_start_section(parser: &mut Parser, environ: &mut ModuleEnvironment) -> WasmResult<()> {
|
|
loop {
|
|
match *parser.read() {
|
|
ParserState::StartSectionEntry(index) => {
|
|
environ.declare_start_func(FuncIndex::new(index as usize));
|
|
}
|
|
ParserState::EndSection => break,
|
|
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
ref s => panic!("unexpected section content: {:?}", s),
|
|
};
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
/// Retrieves the size and maximum fields of memories from the memory section
|
|
pub fn parse_memory_section(
|
|
parser: &mut Parser,
|
|
environ: &mut ModuleEnvironment,
|
|
) -> WasmResult<()> {
|
|
loop {
|
|
match *parser.read() {
|
|
ParserState::MemorySectionEntry(ref ty) => {
|
|
environ.declare_memory(Memory {
|
|
pages_count: ty.limits.initial as usize,
|
|
maximum: ty.limits.maximum.map(|x| x as usize),
|
|
shared: ty.shared,
|
|
});
|
|
}
|
|
ParserState::EndSection => break,
|
|
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
ref s => panic!("unexpected section content: {:?}", s),
|
|
};
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
/// Retrieves the size and maximum fields of memories from the memory section
|
|
pub fn parse_global_section(
|
|
parser: &mut Parser,
|
|
environ: &mut ModuleEnvironment,
|
|
) -> WasmResult<()> {
|
|
loop {
|
|
let (content_type, mutability) = match *parser.read() {
|
|
ParserState::BeginGlobalSectionEntry(ref ty) => (ty.content_type, ty.mutable),
|
|
ParserState::EndSection => break,
|
|
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
ref s => panic!("unexpected section content: {:?}", s),
|
|
};
|
|
match *parser.read() {
|
|
ParserState::BeginInitExpressionBody => (),
|
|
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
ref s => panic!("unexpected section content: {:?}", s),
|
|
}
|
|
let initializer = match *parser.read() {
|
|
ParserState::InitExpressionOperator(Operator::I32Const { value }) => {
|
|
GlobalInit::I32Const(value)
|
|
}
|
|
ParserState::InitExpressionOperator(Operator::I64Const { value }) => {
|
|
GlobalInit::I64Const(value)
|
|
}
|
|
ParserState::InitExpressionOperator(Operator::F32Const { value }) => {
|
|
GlobalInit::F32Const(value.bits())
|
|
}
|
|
ParserState::InitExpressionOperator(Operator::F64Const { value }) => {
|
|
GlobalInit::F64Const(value.bits())
|
|
}
|
|
ParserState::InitExpressionOperator(Operator::GetGlobal { global_index }) => {
|
|
GlobalInit::GlobalRef(GlobalIndex::new(global_index as usize))
|
|
}
|
|
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
ref s => panic!("unexpected section content: {:?}", s),
|
|
};
|
|
match *parser.read() {
|
|
ParserState::EndInitExpressionBody => (),
|
|
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
ref s => panic!("unexpected section content: {:?}", s),
|
|
}
|
|
let global = Global {
|
|
ty: type_to_type(content_type).unwrap(),
|
|
mutability,
|
|
initializer,
|
|
};
|
|
environ.declare_global(global);
|
|
match *parser.read() {
|
|
ParserState::EndGlobalSectionEntry => (),
|
|
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
ref s => panic!("unexpected section content: {:?}", s),
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub fn parse_data_section<'data>(
|
|
parser: &mut Parser<'data>,
|
|
environ: &mut ModuleEnvironment<'data>,
|
|
) -> WasmResult<()> {
|
|
loop {
|
|
let memory_index = match *parser.read() {
|
|
ParserState::BeginDataSectionEntry(memory_index) => memory_index,
|
|
ParserState::EndSection => break,
|
|
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
ref s => panic!("unexpected section content: {:?}", s),
|
|
};
|
|
match *parser.read() {
|
|
ParserState::BeginInitExpressionBody => (),
|
|
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
ref s => panic!("unexpected section content: {:?}", s),
|
|
};
|
|
let (base, offset) = match *parser.read() {
|
|
ParserState::InitExpressionOperator(Operator::I32Const { value }) => {
|
|
(None, value as u32 as usize)
|
|
}
|
|
ParserState::InitExpressionOperator(Operator::GetGlobal { global_index }) => {
|
|
match environ
|
|
.get_global(GlobalIndex::new(global_index as usize))
|
|
.initializer
|
|
{
|
|
GlobalInit::I32Const(value) => (None, value as u32 as usize),
|
|
GlobalInit::Import() => (Some(GlobalIndex::new(global_index as usize)), 0),
|
|
_ => panic!("should not happen"),
|
|
}
|
|
}
|
|
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
ref s => panic!("unexpected section content: {:?}", s),
|
|
};
|
|
match *parser.read() {
|
|
ParserState::EndInitExpressionBody => (),
|
|
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
ref s => panic!("unexpected section content: {:?}", s),
|
|
};
|
|
match *parser.read() {
|
|
ParserState::BeginDataSectionEntryBody(_) => (),
|
|
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
ref s => panic!("unexpected section content: {:?}", s),
|
|
};
|
|
let mut running_offset = offset;
|
|
loop {
|
|
let data = match *parser.read() {
|
|
ParserState::DataSectionEntryBodyChunk(data) => data,
|
|
ParserState::EndDataSectionEntryBody => break,
|
|
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
ref s => panic!("unexpected section content: {:?}", s),
|
|
};
|
|
environ.declare_data_initialization(
|
|
MemoryIndex::new(memory_index as usize),
|
|
base,
|
|
running_offset,
|
|
data,
|
|
);
|
|
running_offset += data.len();
|
|
}
|
|
match *parser.read() {
|
|
ParserState::EndDataSectionEntry => (),
|
|
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
ref s => panic!("unexpected section content: {:?}", s),
|
|
};
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
/// Retrieves the tables from the table section
|
|
pub fn parse_table_section(parser: &mut Parser, environ: &mut ModuleEnvironment) -> WasmResult<()> {
|
|
loop {
|
|
match *parser.read() {
|
|
ParserState::TableSectionEntry(ref table) => environ.declare_table(Table {
|
|
ty: match type_to_type(table.element_type) {
|
|
Ok(t) => TableElementType::Val(t),
|
|
Err(()) => TableElementType::Func(),
|
|
},
|
|
size: table.limits.initial as usize,
|
|
maximum: table.limits.maximum.map(|x| x as usize),
|
|
}),
|
|
ParserState::EndSection => break,
|
|
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
ref s => panic!("unexpected section content: {:?}", s),
|
|
};
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
/// Retrieves the elements from the element section
|
|
pub fn parse_element_section(
|
|
parser: &mut Parser,
|
|
environ: &mut ModuleEnvironment,
|
|
) -> WasmResult<()> {
|
|
loop {
|
|
let table_index = match *parser.read() {
|
|
ParserState::BeginElementSectionEntry(table_index) => {
|
|
TableIndex::new(table_index as usize)
|
|
}
|
|
ParserState::EndSection => break,
|
|
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
ref s => panic!("unexpected section content: {:?}", s),
|
|
};
|
|
match *parser.read() {
|
|
ParserState::BeginInitExpressionBody => (),
|
|
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
ref s => panic!("unexpected section content: {:?}", s),
|
|
};
|
|
let (base, offset) = match *parser.read() {
|
|
ParserState::InitExpressionOperator(Operator::I32Const { value }) => {
|
|
(None, value as u32 as usize)
|
|
}
|
|
ParserState::InitExpressionOperator(Operator::GetGlobal { global_index }) => {
|
|
match environ
|
|
.get_global(GlobalIndex::new(global_index as usize))
|
|
.initializer
|
|
{
|
|
GlobalInit::I32Const(value) => (None, value as u32 as usize),
|
|
GlobalInit::Import() => (Some(GlobalIndex::new(global_index as usize)), 0),
|
|
_ => panic!("should not happen"),
|
|
}
|
|
}
|
|
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
ref s => panic!("unexpected section content: {:?}", s),
|
|
};
|
|
match *parser.read() {
|
|
ParserState::EndInitExpressionBody => (),
|
|
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
ref s => panic!("unexpected section content: {:?}", s),
|
|
};
|
|
match *parser.read() {
|
|
ParserState::ElementSectionEntryBody(ref elements) => {
|
|
let elems: Vec<FuncIndex> = elements
|
|
.iter()
|
|
.map(|&x| FuncIndex::new(x as usize))
|
|
.collect();
|
|
environ.declare_table_elements(table_index, base, offset, elems)
|
|
}
|
|
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
ref s => panic!("unexpected section content: {:?}", s),
|
|
};
|
|
match *parser.read() {
|
|
ParserState::EndElementSectionEntry => (),
|
|
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
ref s => panic!("unexpected section content: {:?}", s),
|
|
};
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
/// Parses every function body in the code section and defines the corresponding function.
|
|
pub fn parse_code_section<'data>(
|
|
parser: &mut Parser<'data>,
|
|
environ: &mut ModuleEnvironment<'data>,
|
|
) -> WasmResult<()> {
|
|
loop {
|
|
match *parser.read() {
|
|
ParserState::BeginFunctionBody { .. } => {}
|
|
ParserState::EndSection => break,
|
|
ParserState::Error(e) => return Err(WasmError::from_binary_reader_error(e)),
|
|
ref s => panic!("wrong content in code section: {:?}", s),
|
|
}
|
|
let mut reader = parser.create_binary_reader();
|
|
let size = reader.bytes_remaining();
|
|
environ.define_function_body(
|
|
reader
|
|
.read_bytes(size)
|
|
.map_err(WasmError::from_binary_reader_error)?,
|
|
)?;
|
|
}
|
|
Ok(())
|
|
}
|