Bug 1505777: [Cranelift] Update to Cranelift 0.23. r=bbouvier

This commit is contained in:
Dan Gohman 2018-11-08 09:18:00 +02:00
parent 237a48fbb3
commit 018ae56959
5 changed files with 130 additions and 124 deletions

View File

@ -8,9 +8,9 @@ crate-type = ["rlib"]
name = "baldrdash"
[dependencies]
cranelift-codegen = "0.20.0"
cranelift-wasm = "0.20.1"
target-lexicon = "0.0.3"
cranelift-codegen = "0.23.0"
cranelift-wasm = "0.23.0"
target-lexicon = "0.2.0"
log = { version = "0.4.4", default-features = false, features = ["release_max_level_info"] }
env_logger = "0.5.6"

View File

@ -16,9 +16,6 @@
// Safe wrappers to the low-level ABI. This re-exports all types in
// baldrapi but none of the functions.
// TODO: Should many u32 arguments and return values here really be
// usize, to be more conventional?
use baldrapi::CraneliftModuleEnvironment;
use cranelift_codegen::binemit::CodeOffset;
use cranelift_codegen::cursor::{Cursor, FuncCursor};
@ -200,13 +197,13 @@ impl<'a> ModuleEnvironment<'a> {
unsafe { baldrapi::env_func_is_import(self.env, func_index.index()) }
}
pub fn signature(&self, sig_index: SignatureIndex) -> FuncTypeWithId {
FuncTypeWithId(unsafe { baldrapi::env_signature(self.env, sig_index) })
FuncTypeWithId(unsafe { baldrapi::env_signature(self.env, sig_index.index()) })
}
pub fn table(&self, table_index: TableIndex) -> TableDesc {
TableDesc(unsafe { baldrapi::env_table(self.env, table_index) })
TableDesc(unsafe { baldrapi::env_table(self.env, table_index.index()) })
}
pub fn global(&self, global_index: GlobalIndex) -> GlobalDesc {
GlobalDesc(unsafe { baldrapi::env_global(self.env, global_index) })
GlobalDesc(unsafe { baldrapi::env_global(self.env, global_index.index()) })
}
pub fn min_memory_length(&self) -> i64 {
self.env.min_memory_length as i64

View File

@ -92,9 +92,8 @@ impl<'a, 'b> BatchCompiler<'a, 'b> {
}
pub fn compile(&mut self) -> CodegenResult<()> {
let orig_size = self.context.compile(&*self.isa)?;
let size = self.remove_return_inst(orig_size) as usize;
self.binemit(size)
let size = self.context.compile(&*self.isa)?;
self.binemit(size as usize)
}
/// Translate the WebAssembly code to Cranelift IR.
@ -119,37 +118,6 @@ impl<'a, 'b> BatchCompiler<'a, 'b> {
Ok(wsig)
}
/// Remove the trailing return instruction from the current function to make room for a custom
/// epilogue.
///
/// Return the new function size in bytes, adjusted from size.
fn remove_return_inst(&mut self, size: CodeOffset) -> CodeOffset {
// Get the last instruction in the function.
let mut pos = FuncCursor::new(&mut self.context.func);
// Move to the bottom of the last EBB in the function.
pos.prev_ebb().expect("empty function");
// Move to the last instruction in the last EBB.
let inst = pos.prev_inst().expect("last EBB has not terminator");
// TODO There might be an issue here, if there can be more than one
// IR returns per IR function.
if pos.func.dfg[inst].opcode().is_return() {
let enc = pos.func.encodings[inst];
let ret_size = self.isa.encoding_info().bytes(enc);
// Remove the return instruction. This leaves the IR in an invalid state where the last
// EBB has no terminator. The code emitter shouldn't mind this. If it does want to
// verify the IR in the future, we could use a zero-sized return encoding instead.
pos.remove_inst();
return size - ret_size;
}
// Function doesn't have a return instruction.
size
}
/// Emit binary machine code to `emitter`.
fn binemit(&mut self, size: usize) -> CodegenResult<()> {
let frame_pushed = self.frame_pushed();

View File

@ -33,22 +33,13 @@ use baldrdash::StaticEnvironment;
impl From<isa::LookupError> for BasicError {
fn from(err: isa::LookupError) -> BasicError {
let msg = match err {
isa::LookupError::SupportDisabled => "ISA support is disabled",
isa::LookupError::Unsupported => "unsupported ISA",
};
BasicError::new(msg.to_string())
BasicError::new(err.to_string())
}
}
impl From<settings::SetError> for BasicError {
fn from(err: settings::SetError) -> BasicError {
let msg = match err {
settings::SetError::BadName => "bad setting name",
settings::SetError::BadType => "bad setting type",
settings::SetError::BadValue => "bad setting value",
};
BasicError::new(msg.to_string())
BasicError::new(err.to_string())
}
}
@ -103,10 +94,6 @@ pub fn make_isa(env: &StaticEnvironment) -> DashResult<Box<isa::TargetIsa>> {
fn make_shared_flags() -> settings::SetResult<settings::Flags> {
let mut sb = settings::builder();
// Since we're using SM's epilogue insertion code, we can only handle a single return
// instruction at the end of the function.
sb.enable("return_at_end")?;
// We don't install SIGFPE handlers, but depend on explicit traps around divisions.
sb.enable("avoid_div_traps")?;
@ -135,5 +122,8 @@ fn make_shared_flags() -> settings::SetResult<settings::Flags> {
// Let's optimize for speed.
sb.set("opt_level", "best")?;
// TODO: Enable jump tables (requires emitting readonly data separately from text).
sb.set("jump_tables_enabled", "false")?;
Ok(settings::Flags::new(sb))
}

View File

@ -21,15 +21,15 @@
use baldrdash as bd;
use compile::{symbolic_function_name, wasm_function_name};
use cranelift_codegen::cursor::{Cursor, FuncCursor};
use cranelift_codegen::entity::EntityMap;
use cranelift_codegen::entity::{EntityRef, PrimaryMap, SecondaryMap};
use cranelift_codegen::ir;
use cranelift_codegen::ir::condcodes::IntCC;
use cranelift_codegen::ir::InstBuilder;
use cranelift_codegen::isa::TargetIsa;
use cranelift_codegen::isa::{CallConv, TargetFrontendConfig, TargetIsa};
use cranelift_codegen::packed_option::PackedOption;
use cranelift_codegen::settings::{CallConv, Flags};
use cranelift_codegen::settings::Flags;
use cranelift_wasm::{
self, FuncIndex, GlobalIndex, MemoryIndex, SignatureIndex, TableIndex, WasmResult,
self, FuncIndex, GlobalIndex, MemoryIndex, ReturnMode, SignatureIndex, TableIndex, WasmResult,
};
use std::collections::HashMap;
use target_lexicon::Triple;
@ -58,6 +58,11 @@ fn offset32(offset: usize) -> ir::immediates::Offset32 {
(offset as i32).into()
}
/// Convert a usize offset into a `Imm64` for an iadd_imm.
fn imm64(offset: usize) -> ir::immediates::Imm64 {
(offset as i64).into()
}
/// Initialize a `Signature` from a wasm signature.
fn init_sig_from_wsig(sig: &mut ir::Signature, wsig: bd::FuncTypeWithId) {
sig.clear(CallConv::Baldrdash);
@ -102,7 +107,7 @@ pub struct TransEnv<'a, 'b, 'c> {
/// Information about the function pointer tables `self.env` knowns about. Indexed by table
/// index.
tables: Vec<TableInfo>,
tables: PrimaryMap<TableIndex, TableInfo>,
/// For those signatures whose ID is stored in a global, keep track of the globals we have
/// created so far.
@ -118,7 +123,10 @@ pub struct TransEnv<'a, 'b, 'c> {
/// imported functions are numbered starting from 0.
///
/// Any `None` entries in this table are simply global variables that have not yet been created.
func_gvs: EntityMap<FuncIndex, PackedOption<ir::GlobalValue>>,
func_gvs: SecondaryMap<FuncIndex, PackedOption<ir::GlobalValue>>,
/// The `vmctx` global value.
vmctx_gv: PackedOption<ir::GlobalValue>,
/// Global variable representing the `TlsData::instance` field which points to the current
/// instance.
@ -149,9 +157,10 @@ impl<'a, 'b, 'c> TransEnv<'a, 'b, 'c> {
isa,
env,
static_env,
tables: Vec::new(),
tables: PrimaryMap::new(),
signatures: HashMap::new(),
func_gvs: EntityMap::new(),
func_gvs: SecondaryMap::new(),
vmctx_gv: None.into(),
instance_gv: None.into(),
interrupt_gv: None.into(),
symbolic: [None.into(); 2],
@ -160,22 +169,39 @@ impl<'a, 'b, 'c> TransEnv<'a, 'b, 'c> {
}
}
/// Get the `vmctx` global value.
fn get_vmctx_gv(&mut self, func: &mut ir::Function) -> ir::GlobalValue {
match self.vmctx_gv.expand() {
Some(gv) => gv,
None => {
// We need to allocate the global variable.
let gv = func.create_global_value(ir::GlobalValueData::VMContext);
self.vmctx_gv = Some(gv).into();
gv
}
}
}
/// Get information about `table`.
/// Create it if necessary.
fn get_table(&mut self, func: &mut ir::Function, table: TableIndex) -> TableInfo {
// Allocate all tables up to the requested index.
while self.tables.len() <= table {
let wtab = self.env.table(self.tables.len());
self.tables.push(TableInfo::new(wtab, func));
let vmctx = self.get_vmctx_gv(func);
while self.tables.len() <= table.index() {
let wtab = self.env.table(TableIndex::new(self.tables.len()));
self.tables.push(TableInfo::new(wtab, func, vmctx));
}
self.tables[table].clone()
}
/// Get the global variable storing the ID of the given signature.
fn sig_global(&mut self, func: &mut ir::Function, offset: usize) -> ir::GlobalValue {
let vmctx = self.get_vmctx_gv(func);
*self.signatures.entry(offset as i32).or_insert_with(|| {
func.create_global_value(ir::GlobalValueData::VMContext {
offset: offset32(offset),
func.create_global_value(ir::GlobalValueData::IAddImm {
base: vmctx,
offset: imm64(offset),
global_type: native_pointer_type(),
})
})
}
@ -187,8 +213,11 @@ impl<'a, 'b, 'c> TransEnv<'a, 'b, 'c> {
return gv;
}
// We need to create a global variable for `import_index`.
let gv = func.create_global_value(ir::GlobalValueData::VMContext {
offset: offset32(self.env.func_import_tls_offset(index)),
let vmctx = self.get_vmctx_gv(func);
let gv = func.create_global_value(ir::GlobalValueData::IAddImm {
base: vmctx,
offset: imm64(self.env.func_import_tls_offset(index)),
global_type: native_pointer_type(),
});
// Save it for next time.
self.func_gvs[index] = gv.into();
@ -201,11 +230,12 @@ impl<'a, 'b, 'c> TransEnv<'a, 'b, 'c> {
Some(gv) => gv,
None => {
// We need to allocate the global variable.
let gv = pos
.func
.create_global_value(ir::GlobalValueData::VMContext {
offset: offset32(self.static_env.instanceTlsOffset),
});
let vmctx = self.get_vmctx_gv(pos.func);
let gv = pos.func.create_global_value(ir::GlobalValueData::IAddImm {
base: vmctx,
offset: imm64(self.static_env.instanceTlsOffset),
global_type: native_pointer_type(),
});
self.instance_gv = gv.into();
gv
}
@ -221,11 +251,12 @@ impl<'a, 'b, 'c> TransEnv<'a, 'b, 'c> {
Some(gv) => gv,
None => {
// We need to allocate the global variable.
let gv = pos
.func
.create_global_value(ir::GlobalValueData::VMContext {
offset: offset32(self.static_env.interruptTlsOffset),
});
let vmctx = self.get_vmctx_gv(pos.func);
let gv = pos.func.create_global_value(ir::GlobalValueData::IAddImm {
base: vmctx,
offset: imm64(self.static_env.interruptTlsOffset),
global_type: native_pointer_type(),
});
self.interrupt_gv = gv.into();
gv
}
@ -264,17 +295,23 @@ impl<'a, 'b, 'c> TransEnv<'a, 'b, 'c> {
/// realm value, in case the call has used a different realm.
fn switch_to_wasm_tls_realm(&mut self, pos: &mut FuncCursor) {
if self.cx_addr.is_none() {
let vmctx = self.get_vmctx_gv(&mut pos.func);
self.cx_addr = pos
.func
.create_global_value(ir::GlobalValueData::VMContext {
offset: offset32(self.static_env.cxTlsOffset),
.create_global_value(ir::GlobalValueData::IAddImm {
base: vmctx,
offset: imm64(self.static_env.cxTlsOffset),
global_type: native_pointer_type(),
}).into();
}
if self.realm_addr.is_none() {
let vmctx = self.get_vmctx_gv(&mut pos.func);
self.realm_addr = pos
.func
.create_global_value(ir::GlobalValueData::VMContext {
offset: offset32(self.static_env.realmTlsOffset),
.create_global_value(ir::GlobalValueData::IAddImm {
base: vmctx,
offset: imm64(self.static_env.realmTlsOffset),
global_type: native_pointer_type(),
}).into();
}
@ -337,12 +374,8 @@ impl<'a, 'b, 'c> TransEnv<'a, 'b, 'c> {
}
impl<'a, 'b, 'c> cranelift_wasm::FuncEnvironment for TransEnv<'a, 'b, 'c> {
fn flags(&self) -> &Flags {
self.isa.flags()
}
fn triple(&self) -> &Triple {
self.isa.triple()
fn target_config(&self) -> TargetFrontendConfig {
self.isa.frontend_config()
}
fn pointer_type(&self) -> ir::Type {
@ -364,16 +397,23 @@ impl<'a, 'b, 'c> cranelift_wasm::FuncEnvironment for TransEnv<'a, 'b, 'c> {
cranelift_wasm::GlobalVariable::Const(global.emit_constant(&mut pos))
} else {
// This is a global variable. Here we don't care if it is mutable or not.
let offset = offset32(global.tls_offset());
let mut gv = func.create_global_value(ir::GlobalValueData::VMContext { offset });
let offset = global.tls_offset();
let mut gv = self.get_vmctx_gv(func);
// Some globals are represented as a pointer to the actual data, in which case we
// must do an extra dereference to get to them.
if global.is_indirect() {
gv = func.create_global_value(ir::GlobalValueData::Deref {
gv = func.create_global_value(ir::GlobalValueData::Load {
base: gv,
offset: offset32(0),
memory_type: native_pointer_type(),
offset: offset32(offset),
global_type: native_pointer_type(),
readonly: false,
});
} else {
gv = func.create_global_value(ir::GlobalValueData::IAddImm {
base: gv,
offset: imm64(offset),
global_type: native_pointer_type(),
});
}
@ -387,18 +427,19 @@ impl<'a, 'b, 'c> cranelift_wasm::FuncEnvironment for TransEnv<'a, 'b, 'c> {
}
fn make_heap(&mut self, func: &mut ir::Function, index: MemoryIndex) -> ir::Heap {
assert_eq!(index, 0, "Only one WebAssembly memory supported");
assert_eq!(index.index(), 0, "Only one WebAssembly memory supported");
// Get the address of the `TlsData::memoryBase` field.
let base_addr =
func.create_global_value(ir::GlobalValueData::VMContext { offset: 0.into() });
// Get the `TlsData::memoryBase` field.
let base = func.create_global_value(ir::GlobalValueData::Deref {
let base_addr = self.get_vmctx_gv(func);
// Get the `TlsData::memoryBase` field. We assume this is never modified during execution
// of the function.
let base = func.create_global_value(ir::GlobalValueData::Load {
base: base_addr,
offset: offset32(0),
memory_type: native_pointer_type(),
global_type: native_pointer_type(),
readonly: true,
});
let min_size = ir::immediates::Imm64::new(self.env.min_memory_length());
let guard_size = ir::immediates::Imm64::new(self.static_env.memoryGuardSize as i64);
let guard_size = imm64(self.static_env.memoryGuardSize);
let bound = self.static_env.staticMemoryBound;
let style = if bound > 0 {
@ -406,14 +447,12 @@ impl<'a, 'b, 'c> cranelift_wasm::FuncEnvironment for TransEnv<'a, 'b, 'c> {
let bound = (bound as i64).into();
ir::HeapStyle::Static { bound }
} else {
let offset = native_pointer_size().into();
// Get the address of the `TlsData::boundsCheckLimit` field.
let bound_gv_addr = func.create_global_value(ir::GlobalValueData::VMContext { offset });
// Get the `TlsData::boundsCheckLimit` field.
let bound_gv = func.create_global_value(ir::GlobalValueData::Deref {
base: bound_gv_addr,
offset: offset32(0),
memory_type: ir::types::I32,
let bound_gv = func.create_global_value(ir::GlobalValueData::Load {
base: base_addr,
offset: native_pointer_size().into(),
global_type: ir::types::I32,
readonly: false,
});
ir::HeapStyle::Dynamic { bound_gv }
};
@ -448,16 +487,18 @@ impl<'a, 'b, 'c> cranelift_wasm::FuncEnvironment for TransEnv<'a, 'b, 'c> {
// TODO we'd need a better way to synchronize the shape of GlobalDataDesc and these
// offsets.
let bound_gv = func.create_global_value(ir::GlobalValueData::Deref {
let bound_gv = func.create_global_value(ir::GlobalValueData::Load {
base: table_desc.global,
offset: 0.into(),
memory_type: ir::types::I32,
global_type: ir::types::I32,
readonly: false,
});
let base_gv = func.create_global_value(ir::GlobalValueData::Deref {
let base_gv = func.create_global_value(ir::GlobalValueData::Load {
base: table_desc.global,
offset: native_pointer_size().into(),
memory_type: native_pointer_type(),
offset: offset32(native_pointer_size() as usize),
global_type: native_pointer_type(),
readonly: false,
});
func.create_table(ir::TableData {
@ -496,7 +537,7 @@ impl<'a, 'b, 'c> cranelift_wasm::FuncEnvironment for TransEnv<'a, 'b, 'c> {
// TODO: When compiling asm.js, the table index in inferred from the signature index.
// Currently, WebAssembly doesn't support multiple tables. That may change.
assert_eq!(table_index, 0);
assert_eq!(table_index.index(), 0);
let wtable = self.get_table(pos.func, table_index);
// Follows `MacroAssembler::wasmCallIndirect`:
@ -586,7 +627,7 @@ impl<'a, 'b, 'c> cranelift_wasm::FuncEnvironment for TransEnv<'a, 'b, 'c> {
let call = pos
.ins()
.CallIndirect(ir::Opcode::CallIndirect, ir::types::VOID, sig_ref, args)
.CallIndirect(ir::Opcode::CallIndirect, ir::types::INVALID, sig_ref, args)
.0;
self.switch_to_wasm_tls_realm(&mut pos);
Ok(call)
@ -636,7 +677,7 @@ impl<'a, 'b, 'c> cranelift_wasm::FuncEnvironment for TransEnv<'a, 'b, 'c> {
let sig = pos.func.dfg.ext_funcs[callee].signature;
let call = pos
.ins()
.CallIndirect(ir::Opcode::CallIndirect, ir::types::VOID, sig, args)
.CallIndirect(ir::Opcode::CallIndirect, ir::types::INVALID, sig, args)
.0;
self.switch_to_wasm_tls_realm(&mut pos);
Ok(call)
@ -652,7 +693,7 @@ impl<'a, 'b, 'c> cranelift_wasm::FuncEnvironment for TransEnv<'a, 'b, 'c> {
Ok(pos
.ins()
.Call(ir::Opcode::Call, ir::types::VOID, callee, args)
.Call(ir::Opcode::Call, ir::types::INVALID, callee, args)
.0)
}
}
@ -732,6 +773,12 @@ impl<'a, 'b, 'c> cranelift_wasm::FuncEnvironment for TransEnv<'a, 'b, 'c> {
let interrupt = self.load_interrupt_flag(&mut pos);
pos.ins().trapnz(interrupt, ir::TrapCode::Interrupt);
}
fn return_mode(&self) -> ReturnMode {
// Since we're using SM's epilogue insertion code, we can only handle a single return
// instruction at the end of the function.
ReturnMode::FallthroughReturn
}
}
/// Information about a function table.
@ -749,12 +796,16 @@ struct TableInfo {
impl TableInfo {
/// Create a TableInfo and its global variable in `func`.
pub fn new(wtab: bd::TableDesc, func: &mut ir::Function) -> TableInfo {
pub fn new(wtab: bd::TableDesc, func: &mut ir::Function, vmctx: ir::GlobalValue) -> TableInfo {
// Create the global variable.
let offset = wtab.tls_offset();
assert!(offset < i32::max_value() as usize);
let offset = (offset as i32).into();
let global = func.create_global_value(ir::GlobalValueData::VMContext { offset });
let offset = imm64(offset);
let global = func.create_global_value(ir::GlobalValueData::IAddImm {
base: vmctx,
offset,
global_type: native_pointer_type(),
});
TableInfo {
global,