Mark loads from globals aligned and notrap.
Mark loads from globals generated by cton_wasm or by legalization as `aligned` and `notrap`, since memory for these globals should be allocated by the runtime environment for that purpose. This reduces the number of potentially trapping instructions, which can reduce the amount of metadata required by embedding environments.
This commit is contained in:
@@ -588,6 +588,9 @@ stack_check = Instruction(
|
||||
Read the stack limit from ``GV`` and compare it to the stack pointer. If
|
||||
the stack pointer has reached or exceeded the limit, generate a trap with a
|
||||
``stk_ovf`` code.
|
||||
|
||||
The global variable must be accessible and naturally aligned for a
|
||||
pointer-sized value.
|
||||
""",
|
||||
ins=GV, can_trap=True)
|
||||
|
||||
|
||||
@@ -17,7 +17,8 @@ pub enum GlobalVarData {
|
||||
/// Variable is part of a struct pointed to by another global variable.
|
||||
///
|
||||
/// The `base` global variable is assumed to contain a pointer to a struct. This global
|
||||
/// variable lives at an offset into the struct.
|
||||
/// variable lives at an offset into the struct. The memory must be accessible, and
|
||||
/// naturally aligned to hold a pointer value.
|
||||
Deref {
|
||||
/// The base pointer global variable.
|
||||
base: GlobalVar,
|
||||
|
||||
@@ -29,7 +29,8 @@ pub enum HeapBase {
|
||||
/// This feature is not yet implemented.
|
||||
ReservedReg,
|
||||
|
||||
/// The heap base is in a global variable.
|
||||
/// The heap base is in a global variable. The variable must be accessible and naturally
|
||||
/// aligned for a pointer.
|
||||
GlobalVar(GlobalVar),
|
||||
}
|
||||
|
||||
@@ -38,7 +39,8 @@ pub enum HeapBase {
|
||||
pub enum HeapStyle {
|
||||
/// A dynamic heap can be relocated to a different base address when it is grown.
|
||||
Dynamic {
|
||||
/// Global variable holding the current bound of the heap in bytes.
|
||||
/// Global variable holding the current bound of the heap in bytes. It is
|
||||
/// required to be accessible and naturally aligned for a pointer-sized integer.
|
||||
bound_gv: GlobalVar,
|
||||
},
|
||||
|
||||
|
||||
@@ -52,8 +52,11 @@ fn deref_addr(inst: ir::Inst, func: &mut ir::Function, base: ir::GlobalVar, offs
|
||||
pos.use_srcloc(inst);
|
||||
|
||||
let base_addr = pos.ins().global_addr(ptr_ty, base);
|
||||
// TODO: We could probably set both `notrap` and `aligned` on this load instruction.
|
||||
let base_ptr = pos.ins().load(ptr_ty, ir::MemFlags::new(), base_addr, 0);
|
||||
let mut mflags = ir::MemFlags::new();
|
||||
// Deref globals are required to be accessible and aligned.
|
||||
mflags.set_notrap();
|
||||
mflags.set_aligned();
|
||||
let base_ptr = pos.ins().load(ptr_ty, mflags, base_addr, 0);
|
||||
pos.func.dfg.replace(inst).iadd_imm(base_ptr, offset);
|
||||
}
|
||||
|
||||
|
||||
@@ -58,7 +58,11 @@ fn dynamic_addr(
|
||||
|
||||
// Start with the bounds check. Trap if `offset + size > bound`.
|
||||
let bound_addr = pos.ins().global_addr(addr_ty, bound_gv);
|
||||
let bound = pos.ins().load(offset_ty, MemFlags::new(), bound_addr, 0);
|
||||
let mut mflags = MemFlags::new();
|
||||
// The bound variable is requied to be accessible and aligned.
|
||||
mflags.set_notrap();
|
||||
mflags.set_aligned();
|
||||
let bound = pos.ins().load(offset_ty, mflags, bound_addr, 0);
|
||||
|
||||
let oob;
|
||||
if size == 1 {
|
||||
@@ -175,7 +179,11 @@ fn offset_addr(
|
||||
ir::HeapBase::ReservedReg => unimplemented!(),
|
||||
ir::HeapBase::GlobalVar(base_gv) => {
|
||||
let base_addr = pos.ins().global_addr(addr_ty, base_gv);
|
||||
let base = pos.ins().load(addr_ty, MemFlags::new(), base_addr, 0);
|
||||
let mut mflags = MemFlags::new();
|
||||
// The base address variable is requied to be accessible and aligned.
|
||||
mflags.set_notrap();
|
||||
mflags.set_aligned();
|
||||
let base = pos.ins().load(addr_ty, mflags, base_addr, 0);
|
||||
pos.func.dfg.replace(inst).iadd(base, offset);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,8 +75,9 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
||||
GlobalValue::Const(val) => val,
|
||||
GlobalValue::Memory { gv, ty } => {
|
||||
let addr = builder.ins().global_addr(environ.native_pointer(), gv);
|
||||
// TODO: It is likely safe to set `aligned notrap` flags on a global load.
|
||||
let flags = ir::MemFlags::new();
|
||||
let mut flags = ir::MemFlags::new();
|
||||
flags.set_notrap();
|
||||
flags.set_aligned();
|
||||
builder.ins().load(ty, flags, addr, 0)
|
||||
}
|
||||
};
|
||||
@@ -87,8 +88,9 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
||||
GlobalValue::Const(_) => panic!("global #{} is a constant", global_index),
|
||||
GlobalValue::Memory { gv, .. } => {
|
||||
let addr = builder.ins().global_addr(environ.native_pointer(), gv);
|
||||
// TODO: It is likely safe to set `aligned notrap` flags on a global store.
|
||||
let flags = ir::MemFlags::new();
|
||||
let mut flags = ir::MemFlags::new();
|
||||
flags.set_notrap();
|
||||
flags.set_aligned();
|
||||
let val = state.pop1();
|
||||
builder.ins().store(flags, val, addr, 0);
|
||||
}
|
||||
@@ -992,6 +994,9 @@ fn translate_load<FE: FuncEnvironment + ?Sized>(
|
||||
// We don't yet support multiple linear memories.
|
||||
let heap = state.get_heap(builder.func, 0, environ);
|
||||
let (base, offset) = get_heap_addr(heap, addr32, offset, environ.native_pointer(), builder);
|
||||
// Note that we don't set `is_aligned` here, even if the load instruction's
|
||||
// alignment immediate says it's aligned, because WebAssembly's immediate
|
||||
// field is just a hint, while Cretonne's aligned flag needs a guarantee.
|
||||
let flags = MemFlags::new();
|
||||
let (load, dfg) = builder.ins().Load(
|
||||
opcode,
|
||||
@@ -1017,6 +1022,7 @@ fn translate_store<FE: FuncEnvironment + ?Sized>(
|
||||
// We don't yet support multiple linear memories.
|
||||
let heap = state.get_heap(builder.func, 0, environ);
|
||||
let (base, offset) = get_heap_addr(heap, addr32, offset, environ.native_pointer(), builder);
|
||||
// See the comments in `translate_load` about the flags.
|
||||
let flags = MemFlags::new();
|
||||
builder.ins().Store(
|
||||
opcode,
|
||||
|
||||
@@ -209,7 +209,10 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ
|
||||
let ext = pos.ins().uextend(I64, callee);
|
||||
pos.ins().imul_imm(ext, 4)
|
||||
};
|
||||
let func_ptr = pos.ins().load(ptr, ir::MemFlags::new(), callee_offset, 0);
|
||||
let mut mflags = ir::MemFlags::new();
|
||||
mflags.set_notrap();
|
||||
mflags.set_aligned();
|
||||
let func_ptr = pos.ins().load(ptr, mflags, callee_offset, 0);
|
||||
|
||||
// Build a value list for the indirect call instruction containing the callee, call_args,
|
||||
// and the vmctx parameter.
|
||||
|
||||
Reference in New Issue
Block a user