Roughly update for the recent Cretonne API changes.
Everything builds and simple modules run, though there's still lots more to do.
This commit is contained in:
@@ -13,9 +13,10 @@ name = "wasmstandalone"
|
|||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cretonne = { git = "https://github.com/stoklund/cretonne.git" }
|
cretonne = { path = "/home/sunfish/rust/cretonne/lib/cretonne" }
|
||||||
cretonne-reader = { git = "https://github.com/stoklund/cretonne.git" }
|
cretonne-reader = { path = "/home/sunfish/rust/cretonne/lib/reader" }
|
||||||
cretonne-wasm = { git = "https://github.com/stoklund/cretonne.git" }
|
cretonne-wasm = { path = "/home/sunfish/rust/cretonne/lib/wasm" }
|
||||||
|
cretonne-native = { path = "/home/sunfish/rust/cretonne/lib/native" }
|
||||||
wasmstandalone = { path = "lib/wasmstandalone" }
|
wasmstandalone = { path = "lib/wasmstandalone" }
|
||||||
wasmparser = "0.8.2"
|
wasmparser = "0.8.2"
|
||||||
wasmtext = { git = "https://github.com/yurydelendik/wasmtext" }
|
wasmtext = { git = "https://github.com/yurydelendik/wasmtext" }
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ repository = "https://github.com/stoklund/cretonne"
|
|||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cretonne = { git = "https://github.com/stoklund/cretonne.git" }
|
cretonne = { path = "/home/sunfish/rust/cretonne/lib/cretonne" }
|
||||||
cretonne-frontend = { git = "https://github.com/stoklund/cretonne.git" }
|
cretonne-frontend = { path = "/home/sunfish/rust/cretonne/lib/frontend" }
|
||||||
cretonne-wasm = { git = "https://github.com/stoklund/cretonne.git" }
|
cretonne-wasm = { path = "/home/sunfish/rust/cretonne/lib/wasm" }
|
||||||
region = "0.0.8"
|
region = "0.0.8"
|
||||||
|
|||||||
@@ -8,13 +8,14 @@ use cretonne::result::CtonError;
|
|||||||
use cretonne::ir::entities::AnyEntity;
|
use cretonne::ir::entities::AnyEntity;
|
||||||
use cretonne::ir::{Ebb, FuncRef, JumpTable, Function};
|
use cretonne::ir::{Ebb, FuncRef, JumpTable, Function};
|
||||||
use cretonne::binemit::{RelocSink, Reloc, CodeOffset};
|
use cretonne::binemit::{RelocSink, Reloc, CodeOffset};
|
||||||
use cton_wasm::{TranslationResult, FunctionTranslation, ImportMappings, FunctionIndex};
|
use cton_wasm::{TranslationResult, FunctionIndex, WasmRuntime};
|
||||||
use std::mem::transmute;
|
use std::mem::transmute;
|
||||||
use region::Protection;
|
use region::Protection;
|
||||||
use region::protect;
|
use region::protect;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::ptr::write_unaligned;
|
use std::ptr::write_unaligned;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
use standalone::StandaloneRuntime;
|
||||||
|
|
||||||
type RelocRef = u16;
|
type RelocRef = u16;
|
||||||
|
|
||||||
@@ -26,13 +27,9 @@ struct StandaloneRelocSink {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Contains all the metadata necessary to perform relocations
|
// Contains all the metadata necessary to perform relocations
|
||||||
enum FunctionMetaData {
|
struct FunctionMetaData {
|
||||||
Import(),
|
|
||||||
Local {
|
|
||||||
relocs: StandaloneRelocSink,
|
relocs: StandaloneRelocSink,
|
||||||
imports: ImportMappings,
|
|
||||||
il_func: Function,
|
il_func: Function,
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RelocSink for StandaloneRelocSink {
|
impl RelocSink for StandaloneRelocSink {
|
||||||
@@ -64,7 +61,17 @@ pub struct ExecutableCode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Executes a module that has been translated with the `StandaloneRuntime` runtime implementation.
|
/// Executes a module that has been translated with the `StandaloneRuntime` runtime implementation.
|
||||||
pub fn compile_module(trans_result: &TranslationResult) -> Result<ExecutableCode, String> {
|
pub fn compile_module(
|
||||||
|
trans_result: &TranslationResult,
|
||||||
|
isa: &TargetIsa,
|
||||||
|
runtime: &StandaloneRuntime,
|
||||||
|
) -> Result<ExecutableCode, String> {
|
||||||
|
debug_assert!(
|
||||||
|
trans_result.start_index.is_none() ||
|
||||||
|
trans_result.start_index.unwrap() >= trans_result.function_imports_count,
|
||||||
|
"imported start functions not supported yet"
|
||||||
|
);
|
||||||
|
|
||||||
let mut shared_builder = settings::builder();
|
let mut shared_builder = settings::builder();
|
||||||
shared_builder.enable("enable_verifier").expect(
|
shared_builder.enable("enable_verifier").expect(
|
||||||
"Missing enable_verifier setting",
|
"Missing enable_verifier setting",
|
||||||
@@ -72,43 +79,14 @@ pub fn compile_module(trans_result: &TranslationResult) -> Result<ExecutableCode
|
|||||||
shared_builder.set("is_64bit", "1").expect(
|
shared_builder.set("is_64bit", "1").expect(
|
||||||
"Missing 64bits setting",
|
"Missing 64bits setting",
|
||||||
);
|
);
|
||||||
let isa = match isa::lookup("intel") {
|
|
||||||
Err(_) => {
|
|
||||||
panic!() // The target ISA is not available.
|
|
||||||
}
|
|
||||||
Ok(mut isa_builder) => {
|
|
||||||
isa_builder.enable("haswell").expect(
|
|
||||||
"Missing haswell setting",
|
|
||||||
);
|
|
||||||
isa_builder.finish(settings::Flags::new(&shared_builder))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let mut functions_metatada = Vec::new();
|
let mut functions_metatada = Vec::new();
|
||||||
let mut functions_code = Vec::new();
|
let mut functions_code = Vec::new();
|
||||||
for (function_index, function) in trans_result.functions.iter().enumerate() {
|
for (function_index, function) in trans_result.functions.iter().enumerate() {
|
||||||
let mut context = Context::new();
|
let mut context = Context::new();
|
||||||
let (il, imports) = match function {
|
verify_function(&function, isa).unwrap();
|
||||||
&FunctionTranslation::Import() => {
|
context.func = function.clone(); // TODO: Avoid this clone.
|
||||||
if trans_result.start_index.is_some() &&
|
let code_size = context.compile(isa).map_err(|e| {
|
||||||
trans_result.start_index.unwrap() == function_index
|
pretty_error(&context.func, Some(isa), e)
|
||||||
{
|
|
||||||
return Err(String::from("start function should not be an import"));
|
|
||||||
} else {
|
|
||||||
functions_code.push(Vec::new());
|
|
||||||
functions_metatada.push(FunctionMetaData::Import());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&FunctionTranslation::Code {
|
|
||||||
ref il,
|
|
||||||
ref imports,
|
|
||||||
..
|
|
||||||
} => (il.clone(), imports.clone()),
|
|
||||||
};
|
|
||||||
verify_function(&il, None).unwrap();
|
|
||||||
context.func = il;
|
|
||||||
let code_size = context.compile(&*isa).map_err(|e| {
|
|
||||||
pretty_error(&context.func, Some(&*isa), e)
|
|
||||||
})? as usize;
|
})? as usize;
|
||||||
if code_size == 0 {
|
if code_size == 0 {
|
||||||
return Err(String::from("no code generated by Cretonne"));
|
return Err(String::from("no code generated by Cretonne"));
|
||||||
@@ -116,15 +94,19 @@ pub fn compile_module(trans_result: &TranslationResult) -> Result<ExecutableCode
|
|||||||
let mut code_buf: Vec<u8> = Vec::with_capacity(code_size);
|
let mut code_buf: Vec<u8> = Vec::with_capacity(code_size);
|
||||||
code_buf.resize(code_size, 0);
|
code_buf.resize(code_size, 0);
|
||||||
let mut relocsink = StandaloneRelocSink::new();
|
let mut relocsink = StandaloneRelocSink::new();
|
||||||
context.emit_to_memory(code_buf.as_mut_ptr(), &mut relocsink, &*isa);
|
context.emit_to_memory(code_buf.as_mut_ptr(), &mut relocsink, isa);
|
||||||
functions_metatada.push(FunctionMetaData::Local {
|
functions_metatada.push(FunctionMetaData {
|
||||||
relocs: relocsink,
|
relocs: relocsink,
|
||||||
imports: imports,
|
|
||||||
il_func: context.func,
|
il_func: context.func,
|
||||||
});
|
});
|
||||||
functions_code.push(code_buf);
|
functions_code.push(code_buf);
|
||||||
}
|
}
|
||||||
relocate(&functions_metatada, &mut functions_code);
|
relocate(
|
||||||
|
trans_result.function_imports_count,
|
||||||
|
&functions_metatada,
|
||||||
|
&mut functions_code,
|
||||||
|
runtime,
|
||||||
|
);
|
||||||
// After having emmitted the code to memory, we deal with relocations
|
// After having emmitted the code to memory, we deal with relocations
|
||||||
match trans_result.start_index {
|
match trans_result.start_index {
|
||||||
None => Err(String::from(
|
None => Err(String::from(
|
||||||
@@ -192,37 +174,37 @@ pub fn execute(exec: &ExecutableCode) -> Result<(), String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Performs the relocations inside the function bytecode, provided the necessary metadata
|
/// Performs the relocations inside the function bytecode, provided the necessary metadata
|
||||||
fn relocate(functions_metatada: &[FunctionMetaData], functions_code: &mut Vec<Vec<u8>>) {
|
fn relocate(
|
||||||
|
function_imports_count: usize,
|
||||||
|
functions_metatada: &[FunctionMetaData],
|
||||||
|
functions_code: &mut Vec<Vec<u8>>,
|
||||||
|
runtime: &StandaloneRuntime,
|
||||||
|
) {
|
||||||
// The relocations are relative to the relocation's address plus four bytes
|
// The relocations are relative to the relocation's address plus four bytes
|
||||||
for (func_index, function_in_memory) in functions_metatada.iter().enumerate() {
|
for (func_index, function_in_memory) in functions_metatada.iter().enumerate() {
|
||||||
match *function_in_memory {
|
let FunctionMetaData {
|
||||||
FunctionMetaData::Import() => continue,
|
|
||||||
FunctionMetaData::Local {
|
|
||||||
ref relocs,
|
ref relocs,
|
||||||
ref imports,
|
|
||||||
ref il_func,
|
ref il_func,
|
||||||
} => {
|
} = *function_in_memory;
|
||||||
for &(func_ref, offset) in relocs.funcs.values() {
|
for &(func_ref, offset) in relocs.funcs.values() {
|
||||||
let target_func_index = imports.functions[&func_ref];
|
let target_func_index = runtime.func_indices[func_ref] - function_imports_count;
|
||||||
let target_func_address: isize = functions_code[target_func_index].as_ptr() as
|
let target_func_address: isize = functions_code[target_func_index].as_ptr() as isize;
|
||||||
isize;
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let reloc_address: isize = functions_code[func_index]
|
let reloc_address: isize = functions_code[func_index].as_mut_ptr().offset(
|
||||||
.as_mut_ptr()
|
offset as isize +
|
||||||
.offset(offset as isize + 4) as
|
4,
|
||||||
isize;
|
) as isize;
|
||||||
let reloc_delta_i32: i32 = (target_func_address - reloc_address) as i32;
|
let reloc_delta_i32: i32 = (target_func_address - reloc_address) as i32;
|
||||||
write_unaligned(reloc_address as *mut i32, reloc_delta_i32);
|
write_unaligned(reloc_address as *mut i32, reloc_delta_i32);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for &(ebb, offset) in relocs.ebbs.values() {
|
for &(ebb, offset) in relocs.ebbs.values() {
|
||||||
unsafe {
|
unsafe {
|
||||||
let reloc_address: isize = functions_code[func_index]
|
let reloc_address: isize = functions_code[func_index].as_mut_ptr().offset(
|
||||||
.as_mut_ptr()
|
offset as isize +
|
||||||
.offset(offset as isize + 4) as
|
4,
|
||||||
isize;
|
) as isize;
|
||||||
let target_ebb_address: isize =
|
let target_ebb_address: isize = functions_code[func_index].as_ptr().offset(
|
||||||
functions_code[func_index].as_ptr().offset(
|
|
||||||
il_func.offsets[ebb] as
|
il_func.offsets[ebb] as
|
||||||
isize,
|
isize,
|
||||||
) as isize;
|
) as isize;
|
||||||
@@ -232,8 +214,6 @@ fn relocate(functions_metatada: &[FunctionMetaData], functions_code: &mut Vec<Ve
|
|||||||
}
|
}
|
||||||
// TODO: deal with jumptable relocations
|
// TODO: deal with jumptable relocations
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pretty-print a verifier error.
|
/// Pretty-print a verifier error.
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
use cton_wasm::{Local, FunctionIndex, GlobalIndex, TableIndex, MemoryIndex, RawByte,
|
use cton_wasm::{FunctionIndex, GlobalIndex, TableIndex, MemoryIndex, Global, GlobalInit, Table,
|
||||||
MemoryAddress, Global, GlobalInit, Table, Memory, WasmRuntime};
|
Memory, WasmRuntime, FuncEnvironment, GlobalValue, SignatureIndex};
|
||||||
use cton_frontend::FunctionBuilder;
|
use cton_frontend::FunctionBuilder;
|
||||||
use cretonne::ir::{MemFlags, Value, InstBuilder, SigRef, FuncRef, ExtFuncData, FunctionName,
|
use cretonne::ir::{MemFlags, Value, InstBuilder, SigRef, FuncRef, ExtFuncData, FunctionName,
|
||||||
Signature, ArgumentType, CallConv};
|
Signature, ArgumentType, CallConv};
|
||||||
use cretonne::ir::types::*;
|
use cretonne::ir::types::*;
|
||||||
use cretonne::ir::condcodes::IntCC;
|
use cretonne::ir::condcodes::IntCC;
|
||||||
use cretonne::ir::immediates::Offset32;
|
use cretonne::ir::immediates::Offset32;
|
||||||
|
use cretonne::cursor::FuncCursor;
|
||||||
|
use cretonne::packed_option::PackedOption;
|
||||||
|
use cretonne::ir;
|
||||||
|
use cretonne::settings;
|
||||||
|
use cretonne::entity::EntityMap;
|
||||||
use std::mem::transmute;
|
use std::mem::transmute;
|
||||||
use std::ptr::copy_nonoverlapping;
|
use std::ptr::copy_nonoverlapping;
|
||||||
use std::ptr::write;
|
use std::ptr::write;
|
||||||
@@ -22,18 +27,18 @@ struct GlobalInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct GlobalsData {
|
struct GlobalsData {
|
||||||
data: Vec<RawByte>,
|
data: Vec<u8>,
|
||||||
info: Vec<GlobalInfo>,
|
info: Vec<GlobalInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TableData {
|
struct TableData {
|
||||||
data: Vec<MemoryAddress>,
|
data: Vec<usize>,
|
||||||
elements: Vec<TableElement>,
|
elements: Vec<TableElement>,
|
||||||
info: Table,
|
info: Table,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MemoryData {
|
struct MemoryData {
|
||||||
data: Vec<RawByte>,
|
data: Vec<u8>,
|
||||||
info: Memory,
|
info: Memory,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,18 +47,43 @@ const PAGE_SIZE: usize = 65536;
|
|||||||
/// Object containing the standalone runtime information. To be passed after creation as argument
|
/// Object containing the standalone runtime information. To be passed after creation as argument
|
||||||
/// to `cton_wasm::translatemodule`.
|
/// to `cton_wasm::translatemodule`.
|
||||||
pub struct StandaloneRuntime {
|
pub struct StandaloneRuntime {
|
||||||
|
// Compilation setting flags.
|
||||||
|
flags: settings::Flags,
|
||||||
|
|
||||||
|
// Unprocessed signatures exactly as provided by `declare_signature()`.
|
||||||
|
signatures: Vec<ir::Signature>,
|
||||||
|
// Types of functions, imported and local.
|
||||||
|
func_types: Vec<SignatureIndex>,
|
||||||
|
// Names of imported functions.
|
||||||
|
imported_funcs: Vec<ir::FunctionName>,
|
||||||
|
|
||||||
globals: GlobalsData,
|
globals: GlobalsData,
|
||||||
tables: Vec<TableData>,
|
tables: Vec<TableData>,
|
||||||
memories: Vec<MemoryData>,
|
memories: Vec<MemoryData>,
|
||||||
instantiated: bool,
|
instantiated: bool,
|
||||||
|
|
||||||
has_current_memory: Option<FuncRef>,
|
has_current_memory: Option<FuncRef>,
|
||||||
has_grow_memory: Option<FuncRef>,
|
has_grow_memory: Option<FuncRef>,
|
||||||
|
|
||||||
|
/// Mapping from cretonne FuncRef to wasm FunctionIndex.
|
||||||
|
pub func_indices: EntityMap<FuncRef, FunctionIndex>,
|
||||||
|
|
||||||
|
the_heap: PackedOption<ir::Heap>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StandaloneRuntime {
|
impl StandaloneRuntime {
|
||||||
/// Allocates the runtime data structures.
|
/// Allocates the runtime data structures with default flags.
|
||||||
pub fn new() -> StandaloneRuntime {
|
pub fn default() -> Self {
|
||||||
StandaloneRuntime {
|
Self::with_flags(settings::Flags::new(&settings::builder()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allocates the runtime data structures with the given flags.
|
||||||
|
pub fn with_flags(flags: settings::Flags) -> Self {
|
||||||
|
Self {
|
||||||
|
flags,
|
||||||
|
signatures: Vec::new(),
|
||||||
|
func_types: Vec::new(),
|
||||||
|
imported_funcs: Vec::new(),
|
||||||
globals: GlobalsData {
|
globals: GlobalsData {
|
||||||
data: Vec::new(),
|
data: Vec::new(),
|
||||||
info: Vec::new(),
|
info: Vec::new(),
|
||||||
@@ -63,145 +93,172 @@ impl StandaloneRuntime {
|
|||||||
instantiated: false,
|
instantiated: false,
|
||||||
has_current_memory: None,
|
has_current_memory: None,
|
||||||
has_grow_memory: None,
|
has_grow_memory: None,
|
||||||
|
func_indices: EntityMap::new(),
|
||||||
|
the_heap: PackedOption::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FuncEnvironment for StandaloneRuntime {
|
||||||
|
fn flags(&self) -> &settings::Flags {
|
||||||
|
&self.flags
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_global(&mut self, func: &mut ir::Function, index: GlobalIndex) -> GlobalValue {
|
||||||
|
// Just create a dummy `vmctx` global.
|
||||||
|
let offset = ((index * 8) as i32 + 8).into();
|
||||||
|
let gv = func.create_global_var(ir::GlobalVarData::VmCtx { offset });
|
||||||
|
GlobalValue::Memory {
|
||||||
|
gv,
|
||||||
|
ty: self.globals.info[index].global.ty,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_heap(&mut self, func: &mut ir::Function, _index: MemoryIndex) -> ir::Heap {
|
||||||
|
debug_assert!(self.the_heap.is_none(), "multiple heaps not supported yet");
|
||||||
|
|
||||||
|
let heap = func.create_heap(ir::HeapData {
|
||||||
|
base: ir::HeapBase::ReservedReg,
|
||||||
|
min_size: 0.into(),
|
||||||
|
guard_size: 0x8000_0000.into(),
|
||||||
|
style: ir::HeapStyle::Static { bound: 0x1_0000_0000.into() },
|
||||||
|
});
|
||||||
|
|
||||||
|
self.the_heap = PackedOption::from(heap);
|
||||||
|
|
||||||
|
heap
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_indirect_sig(&mut self, func: &mut ir::Function, index: SignatureIndex) -> ir::SigRef {
|
||||||
|
// A real implementation would probably change the calling convention and add `vmctx` and
|
||||||
|
// signature index arguments.
|
||||||
|
func.import_signature(self.signatures[index].clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_direct_func(&mut self, func: &mut ir::Function, index: FunctionIndex) -> ir::FuncRef {
|
||||||
|
let sigidx = self.func_types[index];
|
||||||
|
// A real implementation would probably add a `vmctx` argument.
|
||||||
|
// And maybe attempt some signature de-duplication.
|
||||||
|
let signature = func.import_signature(self.signatures[sigidx].clone());
|
||||||
|
|
||||||
|
let name = match self.imported_funcs.get(index) {
|
||||||
|
Some(name) => name.clone(),
|
||||||
|
None => ir::FunctionName::new(format!("localfunc{}", index)),
|
||||||
|
};
|
||||||
|
|
||||||
|
let func_ref = func.import_function(ir::ExtFuncData { name, signature });
|
||||||
|
|
||||||
|
self.func_indices[func_ref] = index;
|
||||||
|
|
||||||
|
func_ref
|
||||||
|
}
|
||||||
|
|
||||||
|
fn translate_call_indirect(
|
||||||
|
&mut self,
|
||||||
|
mut pos: FuncCursor,
|
||||||
|
table_index: TableIndex,
|
||||||
|
_sig_index: SignatureIndex,
|
||||||
|
sig_ref: ir::SigRef,
|
||||||
|
callee: ir::Value,
|
||||||
|
call_args: &[ir::Value],
|
||||||
|
) -> ir::Inst {
|
||||||
|
debug_assert!(table_index == 0, "non-default tables not supported yet");
|
||||||
|
pos.ins().call_indirect(sig_ref, callee, call_args)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn translate_grow_memory(
|
||||||
|
&mut self,
|
||||||
|
mut pos: FuncCursor,
|
||||||
|
index: MemoryIndex,
|
||||||
|
heap: ir::Heap,
|
||||||
|
val: ir::Value,
|
||||||
|
) -> ir::Value {
|
||||||
|
debug_assert!(self.instantiated);
|
||||||
|
debug_assert!(index == 0, "non-default memories not supported yet");
|
||||||
|
debug_assert!(
|
||||||
|
heap == self.the_heap.unwrap(),
|
||||||
|
"multiple heaps not supported yet"
|
||||||
|
);
|
||||||
|
let grow_mem_func = match self.has_grow_memory {
|
||||||
|
Some(grow_mem_func) => grow_mem_func,
|
||||||
|
None => {
|
||||||
|
let sig_ref = pos.func.import_signature(Signature {
|
||||||
|
call_conv: CallConv::Native,
|
||||||
|
argument_bytes: None,
|
||||||
|
argument_types: vec![ArgumentType::new(I32)],
|
||||||
|
return_types: vec![ArgumentType::new(I32)],
|
||||||
|
});
|
||||||
|
pos.func.import_function(ExtFuncData {
|
||||||
|
name: FunctionName::new("grow_memory"),
|
||||||
|
signature: sig_ref,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.has_grow_memory = Some(grow_mem_func);
|
||||||
|
let call_inst = pos.ins().call(grow_mem_func, &[val]);
|
||||||
|
*pos.func.dfg.inst_results(call_inst).first().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn translate_current_memory(
|
||||||
|
&mut self,
|
||||||
|
mut pos: FuncCursor,
|
||||||
|
index: MemoryIndex,
|
||||||
|
heap: ir::Heap,
|
||||||
|
) -> ir::Value {
|
||||||
|
debug_assert!(self.instantiated);
|
||||||
|
debug_assert!(index == 0, "non-default memories not supported yet");
|
||||||
|
debug_assert!(
|
||||||
|
heap == self.the_heap.unwrap(),
|
||||||
|
"multiple heaps not supported yet"
|
||||||
|
);
|
||||||
|
let cur_mem_func = match self.has_current_memory {
|
||||||
|
Some(cur_mem_func) => cur_mem_func,
|
||||||
|
None => {
|
||||||
|
let sig_ref = pos.func.import_signature(Signature {
|
||||||
|
call_conv: CallConv::Native,
|
||||||
|
argument_bytes: None,
|
||||||
|
argument_types: Vec::new(),
|
||||||
|
return_types: vec![ArgumentType::new(I32)],
|
||||||
|
});
|
||||||
|
pos.func.import_function(ExtFuncData {
|
||||||
|
name: FunctionName::new("current_memory"),
|
||||||
|
signature: sig_ref,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.has_current_memory = Some(cur_mem_func);
|
||||||
|
let call_inst = pos.ins().call(cur_mem_func, &[]);
|
||||||
|
*pos.func.dfg.inst_results(call_inst).first().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// This trait is useful for
|
/// This trait is useful for
|
||||||
/// `cton_wasm::translatemodule` because it
|
/// `cton_wasm::translatemodule` because it
|
||||||
/// tells how to translate runtime-dependent wasm instructions. These functions should not be
|
/// tells how to translate runtime-dependent wasm instructions. These functions should not be
|
||||||
/// called by the user.
|
/// called by the user.
|
||||||
impl WasmRuntime for StandaloneRuntime {
|
impl WasmRuntime for StandaloneRuntime {
|
||||||
fn translate_get_global(
|
fn declare_signature(&mut self, sig: &ir::Signature) {
|
||||||
&self,
|
self.signatures.push(sig.clone());
|
||||||
builder: &mut FunctionBuilder<Local>,
|
|
||||||
global_index: GlobalIndex,
|
|
||||||
) -> Value {
|
|
||||||
debug_assert!(self.instantiated);
|
|
||||||
let ty = self.globals.info[global_index].global.ty;
|
|
||||||
let offset = self.globals.info[global_index].offset;
|
|
||||||
let memflags = MemFlags::new();
|
|
||||||
let memoffset = Offset32::new(offset as i32);
|
|
||||||
let addr: i64 = unsafe { transmute(self.globals.data.as_ptr()) };
|
|
||||||
let addr_val = builder.ins().iconst(I64, addr);
|
|
||||||
builder.ins().load(ty, memflags, addr_val, memoffset)
|
|
||||||
}
|
}
|
||||||
fn translate_set_global(
|
|
||||||
&self,
|
fn declare_func_import(&mut self, sig_index: SignatureIndex, module: &[u8], field: &[u8]) {
|
||||||
builder: &mut FunctionBuilder<Local>,
|
debug_assert_eq!(
|
||||||
global_index: GlobalIndex,
|
self.func_types.len(),
|
||||||
val: Value,
|
self.imported_funcs.len(),
|
||||||
) {
|
"Imported functions must be declared first"
|
||||||
let offset = self.globals.info[global_index].offset;
|
|
||||||
let memflags = MemFlags::new();
|
|
||||||
let memoffset = Offset32::new(offset as i32);
|
|
||||||
let addr: i64 = unsafe { transmute(self.globals.data.as_ptr()) };
|
|
||||||
let addr_val = builder.ins().iconst(I64, addr);
|
|
||||||
builder.ins().store(memflags, val, addr_val, memoffset);
|
|
||||||
}
|
|
||||||
fn translate_memory_base_address(
|
|
||||||
&self,
|
|
||||||
builder: &mut FunctionBuilder<Local>,
|
|
||||||
memory_index: MemoryIndex,
|
|
||||||
) -> Value {
|
|
||||||
let addr: i64 = unsafe { transmute(self.memories[memory_index].data.as_ptr()) };
|
|
||||||
builder.ins().iconst(I64, addr)
|
|
||||||
}
|
|
||||||
fn translate_grow_memory(
|
|
||||||
&mut self,
|
|
||||||
builder: &mut FunctionBuilder<Local>,
|
|
||||||
pages: Value,
|
|
||||||
) -> Value {
|
|
||||||
debug_assert!(self.instantiated);
|
|
||||||
let grow_mem_func = match self.has_grow_memory {
|
|
||||||
Some(grow_mem_func) => grow_mem_func,
|
|
||||||
None => {
|
|
||||||
let sig_ref = builder.import_signature(Signature {
|
|
||||||
call_conv: CallConv::Native,
|
|
||||||
argument_bytes: None,
|
|
||||||
argument_types: vec![ArgumentType::new(I32)],
|
|
||||||
return_types: vec![ArgumentType::new(I32)],
|
|
||||||
});
|
|
||||||
builder.import_function(ExtFuncData {
|
|
||||||
name: FunctionName::new("grow_memory"),
|
|
||||||
signature: sig_ref,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
};
|
|
||||||
self.has_grow_memory = Some(grow_mem_func);
|
|
||||||
let call_inst = builder.ins().call(grow_mem_func, &[pages]);
|
|
||||||
*builder.inst_results(call_inst).first().unwrap()
|
|
||||||
}
|
|
||||||
fn translate_current_memory(&mut self, builder: &mut FunctionBuilder<Local>) -> Value {
|
|
||||||
debug_assert!(self.instantiated);
|
|
||||||
let cur_mem_func = match self.has_current_memory {
|
|
||||||
Some(cur_mem_func) => cur_mem_func,
|
|
||||||
None => {
|
|
||||||
let sig_ref = builder.import_signature(Signature {
|
|
||||||
call_conv: CallConv::Native,
|
|
||||||
argument_bytes: None,
|
|
||||||
argument_types: Vec::new(),
|
|
||||||
return_types: vec![ArgumentType::new(I32)],
|
|
||||||
});
|
|
||||||
builder.import_function(ExtFuncData {
|
|
||||||
name: FunctionName::new("current_memory"),
|
|
||||||
signature: sig_ref,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
};
|
|
||||||
self.has_current_memory = Some(cur_mem_func);
|
|
||||||
let call_inst = builder.ins().call(cur_mem_func, &[]);
|
|
||||||
*builder.inst_results(call_inst).first().unwrap()
|
|
||||||
}
|
|
||||||
fn translate_call_indirect<'a>(
|
|
||||||
&self,
|
|
||||||
builder: &'a mut FunctionBuilder<Local>,
|
|
||||||
sig_ref: SigRef,
|
|
||||||
index_val: Value,
|
|
||||||
call_args: &[Value],
|
|
||||||
) -> &'a [Value] {
|
|
||||||
let trap_ebb = builder.create_ebb();
|
|
||||||
let continue_ebb = builder.create_ebb();
|
|
||||||
let size_val = builder.ins().iconst(I32, self.tables[0].info.size as i64);
|
|
||||||
let zero_val = builder.ins().iconst(I32, 0);
|
|
||||||
builder.ins().br_icmp(
|
|
||||||
IntCC::UnsignedLessThan,
|
|
||||||
index_val,
|
|
||||||
zero_val,
|
|
||||||
trap_ebb,
|
|
||||||
&[],
|
|
||||||
);
|
);
|
||||||
builder.ins().br_icmp(
|
self.func_types.push(sig_index);
|
||||||
IntCC::UnsignedGreaterThanOrEqual,
|
|
||||||
index_val,
|
// TODO: name_fold and concatenation with '_' are lossy; figure out something better.
|
||||||
size_val,
|
let mut name = Vec::new();
|
||||||
trap_ebb,
|
name.extend(module.iter().cloned().map(name_fold));
|
||||||
&[],
|
name.push(b'_');
|
||||||
);
|
name.extend(field.iter().cloned().map(name_fold));
|
||||||
builder.seal_block(trap_ebb);
|
self.imported_funcs.push(ir::FunctionName::new(name));
|
||||||
let offset_val = builder.ins().imul_imm(index_val, 4);
|
}
|
||||||
let base_table_addr: i64 = unsafe { transmute(self.tables[0].data.as_ptr()) };
|
|
||||||
let table_addr_val = builder.ins().iconst(I32, base_table_addr);
|
fn declare_func_type(&mut self, sig_index: SignatureIndex) {
|
||||||
let table_entry_addr_val = builder.ins().iadd(table_addr_val, offset_val);
|
self.func_types.push(sig_index);
|
||||||
let memflags = MemFlags::new();
|
|
||||||
let memoffset = Offset32::new(0);
|
|
||||||
let table_entry_val = builder.ins().load(
|
|
||||||
I32,
|
|
||||||
memflags,
|
|
||||||
table_entry_addr_val,
|
|
||||||
memoffset,
|
|
||||||
);
|
|
||||||
let call_inst = builder.ins().call_indirect(
|
|
||||||
sig_ref,
|
|
||||||
table_entry_val,
|
|
||||||
call_args,
|
|
||||||
);
|
|
||||||
builder.ins().jump(continue_ebb, &[]);
|
|
||||||
builder.seal_block(continue_ebb);
|
|
||||||
builder.switch_to_block(trap_ebb, &[]);
|
|
||||||
builder.ins().trap();
|
|
||||||
builder.switch_to_block(continue_ebb, &[]);
|
|
||||||
builder.inst_results(call_inst)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn begin_translation(&mut self) {
|
fn begin_translation(&mut self) {
|
||||||
@@ -270,9 +327,14 @@ impl WasmRuntime for StandaloneRuntime {
|
|||||||
fn next_function(&mut self) {
|
fn next_function(&mut self) {
|
||||||
self.has_current_memory = None;
|
self.has_current_memory = None;
|
||||||
self.has_grow_memory = None;
|
self.has_grow_memory = None;
|
||||||
|
self.func_indices.clear();
|
||||||
}
|
}
|
||||||
fn declare_global(&mut self, global: Global) {
|
fn declare_global(&mut self, global: Global) {
|
||||||
debug_assert!(!self.instantiated);
|
debug_assert!(!self.instantiated);
|
||||||
|
debug_assert!(
|
||||||
|
self.globals.info.is_empty(),
|
||||||
|
"multiple globals not supported yet"
|
||||||
|
);
|
||||||
self.globals.info.push(GlobalInfo {
|
self.globals.info.push(GlobalInfo {
|
||||||
global: global,
|
global: global,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
@@ -343,3 +405,12 @@ impl StandaloneRuntime {
|
|||||||
&self.globals.data[offset..offset + len]
|
&self.globals.data[offset..offset + len]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate characters suitable for printable `FuncName`s.
|
||||||
|
fn name_fold(c: u8) -> u8 {
|
||||||
|
if (c as char).is_alphanumeric() {
|
||||||
|
c
|
||||||
|
} else {
|
||||||
|
b'_'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
83
src/main.rs
83
src/main.rs
@@ -6,6 +6,7 @@
|
|||||||
//! and tables, then emitting the translated code with hardcoded addresses to memory.
|
//! and tables, then emitting the translated code with hardcoded addresses to memory.
|
||||||
|
|
||||||
extern crate cton_wasm;
|
extern crate cton_wasm;
|
||||||
|
extern crate cton_native;
|
||||||
extern crate wasmstandalone;
|
extern crate wasmstandalone;
|
||||||
extern crate wasmparser;
|
extern crate wasmparser;
|
||||||
extern crate cretonne;
|
extern crate cretonne;
|
||||||
@@ -16,8 +17,7 @@ extern crate serde_derive;
|
|||||||
extern crate term;
|
extern crate term;
|
||||||
extern crate tempdir;
|
extern crate tempdir;
|
||||||
|
|
||||||
use cton_wasm::{translate_module, TranslationResult, FunctionTranslation, DummyRuntime,
|
use cton_wasm::{translate_module, TranslationResult};
|
||||||
WasmRuntime};
|
|
||||||
use wasmstandalone::{StandaloneRuntime, compile_module, execute};
|
use wasmstandalone::{StandaloneRuntime, compile_module, execute};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use wasmparser::{Parser, ParserState, WasmDecoder, SectionCode};
|
use wasmparser::{Parser, ParserState, WasmDecoder, SectionCode};
|
||||||
@@ -31,6 +31,7 @@ use cretonne::ir;
|
|||||||
use cretonne::ir::entities::AnyEntity;
|
use cretonne::ir::entities::AnyEntity;
|
||||||
use cretonne::isa::TargetIsa;
|
use cretonne::isa::TargetIsa;
|
||||||
use cretonne::verifier;
|
use cretonne::verifier;
|
||||||
|
use cretonne::settings;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::io;
|
use std::io;
|
||||||
@@ -106,10 +107,14 @@ fn main() {
|
|||||||
})
|
})
|
||||||
.unwrap_or_else(|e| e.exit());
|
.unwrap_or_else(|e| e.exit());
|
||||||
let mut terminal = term::stdout().unwrap();
|
let mut terminal = term::stdout().unwrap();
|
||||||
|
let (flag_builder, isa_builder) = cton_native::builders().unwrap_or_else(|_| {
|
||||||
|
panic!("host machine is not a supported target");
|
||||||
|
});
|
||||||
|
let isa = isa_builder.finish(settings::Flags::new(&flag_builder));
|
||||||
for filename in &args.arg_file {
|
for filename in &args.arg_file {
|
||||||
let path = Path::new(&filename);
|
let path = Path::new(&filename);
|
||||||
let name = path.as_os_str().to_string_lossy();
|
let name = path.as_os_str().to_string_lossy();
|
||||||
match handle_module(&args, path.to_path_buf(), &name) {
|
match handle_module(&args, path.to_path_buf(), &name, &*isa) {
|
||||||
Ok(()) => {}
|
Ok(()) => {}
|
||||||
Err(message) => {
|
Err(message) => {
|
||||||
terminal.fg(term::color::RED).unwrap();
|
terminal.fg(term::color::RED).unwrap();
|
||||||
@@ -121,7 +126,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_module(args: &Args, path: PathBuf, name: &str) -> Result<(), String> {
|
fn handle_module(args: &Args, path: PathBuf, name: &str, isa: &TargetIsa) -> Result<(), String> {
|
||||||
let mut terminal = term::stdout().unwrap();
|
let mut terminal = term::stdout().unwrap();
|
||||||
terminal.fg(term::color::YELLOW).unwrap();
|
terminal.fg(term::color::YELLOW).unwrap();
|
||||||
vprint!(args.flag_verbose, "Handling: ");
|
vprint!(args.flag_verbose, "Handling: ");
|
||||||
@@ -172,15 +177,9 @@ fn handle_module(args: &Args, path: PathBuf, name: &str) -> Result<(), String> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let mut dummy_runtime = DummyRuntime::new();
|
let mut runtime = StandaloneRuntime::with_flags(isa.flags().clone());
|
||||||
let mut standalone_runtime = StandaloneRuntime::new();
|
|
||||||
let translation = {
|
let translation = {
|
||||||
let runtime: &mut WasmRuntime = if args.flag_execute {
|
match translate_module(&data, &mut runtime) {
|
||||||
&mut standalone_runtime
|
|
||||||
} else {
|
|
||||||
&mut dummy_runtime
|
|
||||||
};
|
|
||||||
match translate_module(&data, runtime) {
|
|
||||||
Ok(x) => x,
|
Ok(x) => x,
|
||||||
Err(string) => {
|
Err(string) => {
|
||||||
return Err(string);
|
return Err(string);
|
||||||
@@ -195,14 +194,9 @@ fn handle_module(args: &Args, path: PathBuf, name: &str) -> Result<(), String> {
|
|||||||
vprint!(args.flag_verbose, "Checking... ");
|
vprint!(args.flag_verbose, "Checking... ");
|
||||||
terminal.reset().unwrap();
|
terminal.reset().unwrap();
|
||||||
for func in &translation.functions {
|
for func in &translation.functions {
|
||||||
let il = match *func {
|
verifier::verify_function(func, isa).map_err(|err| {
|
||||||
FunctionTranslation::Import() => continue,
|
pretty_verifier_error(func, Some(isa), &err)
|
||||||
FunctionTranslation::Code { ref il, .. } => il.clone(),
|
})?;
|
||||||
};
|
|
||||||
match verifier::verify_function(&il, None) {
|
|
||||||
Ok(()) => (),
|
|
||||||
Err(ref err) => return Err(pretty_verifier_error(&il, None, err)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
terminal.fg(term::color::GREEN).unwrap();
|
terminal.fg(term::color::GREEN).unwrap();
|
||||||
vprintln!(args.flag_verbose, " ok");
|
vprintln!(args.flag_verbose, " ok");
|
||||||
@@ -211,7 +205,14 @@ fn handle_module(args: &Args, path: PathBuf, name: &str) -> Result<(), String> {
|
|||||||
if args.flag_print {
|
if args.flag_print {
|
||||||
let mut writer1 = stdout();
|
let mut writer1 = stdout();
|
||||||
let mut writer2 = stdout();
|
let mut writer2 = stdout();
|
||||||
match pretty_print_translation(&name, &data, &translation, &mut writer1, &mut writer2) {
|
match pretty_print_translation(
|
||||||
|
&name,
|
||||||
|
&data,
|
||||||
|
&translation,
|
||||||
|
&mut writer1,
|
||||||
|
&mut writer2,
|
||||||
|
isa,
|
||||||
|
) {
|
||||||
Err(error) => return Err(String::from(error.description())),
|
Err(error) => return Err(String::from(error.description())),
|
||||||
Ok(()) => (),
|
Ok(()) => (),
|
||||||
}
|
}
|
||||||
@@ -221,33 +222,29 @@ fn handle_module(args: &Args, path: PathBuf, name: &str) -> Result<(), String> {
|
|||||||
vprint!(args.flag_verbose, "Optimizing... ");
|
vprint!(args.flag_verbose, "Optimizing... ");
|
||||||
terminal.reset().unwrap();
|
terminal.reset().unwrap();
|
||||||
for func in &translation.functions {
|
for func in &translation.functions {
|
||||||
let il = match *func {
|
|
||||||
FunctionTranslation::Import() => continue,
|
|
||||||
FunctionTranslation::Code { ref il, .. } => il.clone(),
|
|
||||||
};
|
|
||||||
let mut loop_analysis = LoopAnalysis::new();
|
let mut loop_analysis = LoopAnalysis::new();
|
||||||
let mut cfg = ControlFlowGraph::new();
|
let mut cfg = ControlFlowGraph::new();
|
||||||
cfg.compute(&il);
|
cfg.compute(&func);
|
||||||
let mut domtree = DominatorTree::new();
|
let mut domtree = DominatorTree::new();
|
||||||
domtree.compute(&il, &cfg);
|
domtree.compute(&func, &cfg);
|
||||||
loop_analysis.compute(&il, &cfg, &domtree);
|
loop_analysis.compute(&func, &cfg, &domtree);
|
||||||
let mut context = Context::new();
|
let mut context = Context::new();
|
||||||
context.func = il;
|
context.func = func.clone(); // TODO: Avoid this clone.
|
||||||
context.cfg = cfg;
|
context.cfg = cfg;
|
||||||
context.domtree = domtree;
|
context.domtree = domtree;
|
||||||
context.loop_analysis = loop_analysis;
|
context.loop_analysis = loop_analysis;
|
||||||
match verifier::verify_context(&context.func, &context.cfg, &context.domtree, None) {
|
match verifier::verify_context(&context.func, &context.cfg, &context.domtree, isa) {
|
||||||
Ok(()) => (),
|
Ok(()) => (),
|
||||||
Err(ref err) => {
|
Err(ref err) => {
|
||||||
return Err(pretty_verifier_error(&context.func, None, err));
|
return Err(pretty_verifier_error(&context.func, Some(isa), err));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
match context.licm() {
|
match context.licm(isa) {
|
||||||
Ok(())=> (),
|
Ok(())=> (),
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
match error {
|
match error {
|
||||||
CtonError::Verifier(ref err) => {
|
CtonError::Verifier(ref err) => {
|
||||||
return Err(pretty_verifier_error(&context.func, None, err));
|
return Err(pretty_verifier_error(&context.func, Some(isa), err));
|
||||||
}
|
}
|
||||||
CtonError::InvalidInput |
|
CtonError::InvalidInput |
|
||||||
CtonError::ImplLimitExceeded |
|
CtonError::ImplLimitExceeded |
|
||||||
@@ -255,9 +252,9 @@ fn handle_module(args: &Args, path: PathBuf, name: &str) -> Result<(), String> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
match verifier::verify_context(&context.func, &context.cfg, &context.domtree, None) {
|
match verifier::verify_context(&context.func, &context.cfg, &context.domtree, isa) {
|
||||||
Ok(()) => (),
|
Ok(()) => (),
|
||||||
Err(ref err) => return Err(pretty_verifier_error(&context.func, None, err)),
|
Err(ref err) => return Err(pretty_verifier_error(&context.func, Some(isa), err)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
terminal.fg(term::color::GREEN).unwrap();
|
terminal.fg(term::color::GREEN).unwrap();
|
||||||
@@ -268,7 +265,7 @@ fn handle_module(args: &Args, path: PathBuf, name: &str) -> Result<(), String> {
|
|||||||
terminal.fg(term::color::MAGENTA).unwrap();
|
terminal.fg(term::color::MAGENTA).unwrap();
|
||||||
vprint!(args.flag_verbose, "Compiling... ");
|
vprint!(args.flag_verbose, "Compiling... ");
|
||||||
terminal.reset().unwrap();
|
terminal.reset().unwrap();
|
||||||
match compile_module(&translation) {
|
match compile_module(&translation, isa, &runtime) {
|
||||||
Ok(ref exec) => {
|
Ok(ref exec) => {
|
||||||
terminal.fg(term::color::GREEN).unwrap();
|
terminal.fg(term::color::GREEN).unwrap();
|
||||||
vprintln!(args.flag_verbose, "ok");
|
vprintln!(args.flag_verbose, "ok");
|
||||||
@@ -314,7 +311,7 @@ fn handle_module(args: &Args, path: PathBuf, name: &str) -> Result<(), String> {
|
|||||||
if split.len() != 3 {
|
if split.len() != 3 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let memory = standalone_runtime.inspect_memory(
|
let memory = runtime.inspect_memory(
|
||||||
str::parse(split[0]).unwrap(),
|
str::parse(split[0]).unwrap(),
|
||||||
str::parse(split[1]).unwrap(),
|
str::parse(split[1]).unwrap(),
|
||||||
str::parse(split[2]).unwrap(),
|
str::parse(split[2]).unwrap(),
|
||||||
@@ -341,14 +338,12 @@ fn pretty_print_translation(
|
|||||||
translation: &TranslationResult,
|
translation: &TranslationResult,
|
||||||
writer_wast: &mut Write,
|
writer_wast: &mut Write,
|
||||||
writer_cretonne: &mut Write,
|
writer_cretonne: &mut Write,
|
||||||
|
isa: &TargetIsa,
|
||||||
) -> Result<(), io::Error> {
|
) -> Result<(), io::Error> {
|
||||||
let mut terminal = term::stdout().unwrap();
|
let mut terminal = term::stdout().unwrap();
|
||||||
let mut parser = Parser::new(data);
|
let mut parser = Parser::new(data);
|
||||||
let mut parser_writer = Writer::new(writer_wast);
|
let mut parser_writer = Writer::new(writer_wast);
|
||||||
let imports_count = translation.functions.iter().fold(0, |acc, f| match f {
|
let imports_count = translation.function_imports_count;
|
||||||
&FunctionTranslation::Import() => acc + 1,
|
|
||||||
&FunctionTranslation::Code { .. } => acc,
|
|
||||||
});
|
|
||||||
match parser.read() {
|
match parser.read() {
|
||||||
s @ &ParserState::BeginWasm { .. } => parser_writer.write(s)?,
|
s @ &ParserState::BeginWasm { .. } => parser_writer.write(s)?,
|
||||||
_ => panic!("modules should begin properly"),
|
_ => panic!("modules should begin properly"),
|
||||||
@@ -401,10 +396,8 @@ fn pretty_print_translation(
|
|||||||
}
|
}
|
||||||
let mut function_string = format!(
|
let mut function_string = format!(
|
||||||
" {}",
|
" {}",
|
||||||
match translation.functions[function_index + imports_count] {
|
translation.functions[function_index + imports_count]
|
||||||
FunctionTranslation::Code { ref il, .. } => il,
|
.display(isa)
|
||||||
FunctionTranslation::Import() => panic!("should not happen"),
|
|
||||||
}.display(None)
|
|
||||||
);
|
);
|
||||||
function_string.pop();
|
function_string.pop();
|
||||||
let function_str = str::replace(function_string.as_str(), "\n", "\n ");
|
let function_str = str::replace(function_string.as_str(), "\n", "\n ");
|
||||||
|
|||||||
Reference in New Issue
Block a user