Add heap_load, heap_store, and heap_addr instructions.

These are used when lowering WebAssembly sandbox code.
This commit is contained in:
Jakob Stoklund Olesen
2017-04-10 15:03:10 -07:00
parent e78e4ea4ec
commit b474485c0d
9 changed files with 110 additions and 46 deletions

View File

@@ -533,49 +533,13 @@ than the native pointer size, for example unsigned :type:`i32` offsets on a
:arg Name: String identifying the heap in the runtime environment. :arg Name: String identifying the heap in the runtime environment.
:result H: Heap identifier. :result H: Heap identifier.
.. inst:: a = heap_load H, p, Offset .. autoinst:: heap_load
.. autoinst:: heap_store
Load a value at the address ``p + Offset`` in the heap H.
Trap if the heap access would be out of bounds.
:arg H: Heap identifier created by :inst:`heap`.
:arg iN p: Unsigned base address in heap.
:arg Offset: Immediate signed offset.
:flag align(N): Expected alignment of ``p + Offset``. Power of two.
:flag aligntrap: Always trap if the memory access is misaligned.
:result T a: Loaded value.
.. inst:: a = heap_store H, x, p, Offset
Store a value at the address ``p + Offset`` in the heap H.
Trap if the heap access would be out of bounds.
:arg H: Heap identifier created by :inst:`heap`.
:arg T x: Value to be stored.
:arg iN p: Unsigned base address in heap.
:arg Offset: Immediate signed offset.
:flag align(N): Expected alignment of ``p + Offset``. Power of two.
:flag aligntrap: Always trap if the memory access is misaligned.
When optimizing heap accesses, Cretonne may separate the heap bounds checking When optimizing heap accesses, Cretonne may separate the heap bounds checking
and address computations from the memory accesses. and address computations from the memory accesses.
.. inst:: a = heap_addr H, p, Size .. autoinst:: heap_addr
Bounds check and compute absolute address of heap memory.
Verify that the address range ``p .. p + Size - 1`` is valid in the heap H,
and trap if not.
Convert the heap-relative address in ``p`` to a real absolute address and
return it.
:arg H: Heap identifier created by :inst:`heap`.
:arg iN p: Unsigned base address in heap.
:arg Size: Immediate unsigned byte count for range to verify.
:result iPtr a: Absolute address corresponding to ``p``.
A small example using heaps:: A small example using heaps::

View File

@@ -114,3 +114,17 @@ ebb0:
; nextln: $v2 = stack_load.i32 $ss10+4 ; nextln: $v2 = stack_load.i32 $ss10+4
; nextln: stack_store $v1, $ss10+2 ; nextln: stack_store $v1, $ss10+2
; nextln: stack_store $v2, $ss2 ; nextln: stack_store $v2, $ss2
; Heap access instructions.
function heap(i32) {
; TODO: heap0 = heap %foo
ebb0(v1: i32):
v2 = heap_load.f32 v1
v3 = heap_load.f32 v1+12
heap_store v3, v1
}
; sameln: function heap(i32) {
; nextln: ebb0($v1: i32):
; nextln: $v2 = heap_load.f32 $v1
; nextln: $v3 = heap_load.f32 $v1+12
; nextln: heap_store $v3, $v1

View File

@@ -8,7 +8,8 @@ in this module.
from __future__ import absolute_import from __future__ import absolute_import
from cdsl.formats import InstructionFormat from cdsl.formats import InstructionFormat
from cdsl.operands import VALUE, VARIABLE_ARGS from cdsl.operands import VALUE, VARIABLE_ARGS
from .immediates import imm64, uimm8, ieee32, ieee64, offset32, intcc, floatcc from .immediates import imm64, uimm8, ieee32, ieee64, offset32, uoffset32
from .immediates import intcc, floatcc
from .entities import ebb, sig_ref, func_ref, jump_table, stack_slot from .entities import ebb, sig_ref, func_ref, jump_table, stack_slot
Nullary = InstructionFormat() Nullary = InstructionFormat()
@@ -55,5 +56,10 @@ IndirectCall = InstructionFormat(
StackLoad = InstructionFormat(stack_slot, offset32) StackLoad = InstructionFormat(stack_slot, offset32)
StackStore = InstructionFormat(VALUE, stack_slot, offset32) StackStore = InstructionFormat(VALUE, stack_slot, offset32)
# Accessing a WebAssembly heap.
# TODO: Add a reference to a `heap` declared in the preamble.
HeapLoad = InstructionFormat(VALUE, uoffset32)
HeapStore = InstructionFormat(VALUE, VALUE, uoffset32)
# Finally extract the names of global variables in this module. # Finally extract the names of global variables in this module.
InstructionFormat.extract_names(globals()) InstructionFormat.extract_names(globals())

View File

@@ -9,7 +9,7 @@ from cdsl.operands import Operand, VARIABLE_ARGS
from cdsl.typevar import TypeVar from cdsl.typevar import TypeVar
from cdsl.instructions import Instruction, InstructionGroup from cdsl.instructions import Instruction, InstructionGroup
from base.types import i8, f32, f64, b1 from base.types import i8, f32, f64, b1
from base.immediates import imm64, uimm8, ieee32, ieee64, offset32 from base.immediates import imm64, uimm8, ieee32, ieee64, offset32, uoffset32
from base.immediates import intcc, floatcc from base.immediates import intcc, floatcc
from base import entities from base import entities
import base.formats # noqa import base.formats # noqa
@@ -209,6 +209,7 @@ SS = Operand('SS', entities.stack_slot)
Offset = Operand('Offset', offset32, 'In-bounds offset into stack slot') Offset = Operand('Offset', offset32, 'In-bounds offset into stack slot')
x = Operand('x', Mem, doc='Value to be stored') x = Operand('x', Mem, doc='Value to be stored')
a = Operand('a', Mem, doc='Value loaded') a = Operand('a', Mem, doc='Value loaded')
p = Operand('p', iAddr)
addr = Operand('addr', iAddr) addr = Operand('addr', iAddr)
stack_load = Instruction( stack_load = Instruction(
@@ -247,6 +248,44 @@ stack_addr = Instruction(
""", """,
ins=(SS, Offset), outs=addr) ins=(SS, Offset), outs=addr)
#
# WebAssembly bounds-checked heap accesses.
#
# TODO: Add a `heap` operand that selects between multiple heaps.
# TODO: Should the immediate offset be a `u32`?
# TODO: Distinguish between `iAddr` for a heap and for a target address? i.e.,
# 32-bit WebAssembly on a 64-bit target has two different types.
Offset = Operand('Offset', uoffset32, 'Unsigned offset to effective address')
heap_load = Instruction(
'heap_load', r"""
Load a value at the address :math:`p + Offset` in the heap H.
Trap if the heap access would be out of bounds.
""",
ins=(p, Offset), outs=a)
heap_store = Instruction(
'heap_store', r"""
Store a value at the address :math:`p + Offset` in the heap H.
Trap if the heap access would be out of bounds.
""",
ins=(x, p, Offset))
heap_addr = Instruction(
'heap_addr', r"""
Bounds check and compute absolute address of heap memory.
Verify that the address range ``p .. p + Size - 1`` is valid in the
heap H, and trap if not.
Convert the heap-relative address in ``p`` to a real absolute address
and return it.
""",
ins=(p, Offset), outs=addr)
# #
# Materializing constants. # Materializing constants.
# #

View File

@@ -6,7 +6,7 @@
use ir::types; use ir::types;
use ir::{InstructionData, DataFlowGraph, Cursor}; use ir::{InstructionData, DataFlowGraph, Cursor};
use ir::{Opcode, Type, Inst, Value, Ebb, JumpTable, SigRef, FuncRef, StackSlot, ValueList}; use ir::{Opcode, Type, Inst, Value, Ebb, JumpTable, SigRef, FuncRef, StackSlot, ValueList};
use ir::immediates::{Imm64, Uimm8, Ieee32, Ieee64, Offset32}; use ir::immediates::{Imm64, Uimm8, Ieee32, Ieee64, Offset32, Uoffset32};
use ir::condcodes::{IntCC, FloatCC}; use ir::condcodes::{IntCC, FloatCC};
/// Base trait for instruction builders. /// Base trait for instruction builders.

View File

@@ -11,7 +11,7 @@ use std::str::FromStr;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use ir::{Value, Type, Ebb, JumpTable, SigRef, FuncRef, StackSlot}; use ir::{Value, Type, Ebb, JumpTable, SigRef, FuncRef, StackSlot};
use ir::immediates::{Imm64, Uimm8, Ieee32, Ieee64, Offset32}; use ir::immediates::{Imm64, Uimm8, Ieee32, Ieee64, Offset32, Uoffset32};
use ir::condcodes::*; use ir::condcodes::*;
use ir::types; use ir::types;
use ir::DataFlowGraph; use ir::DataFlowGraph;
@@ -240,6 +240,18 @@ pub enum InstructionData {
stack_slot: StackSlot, stack_slot: StackSlot,
offset: Offset32, offset: Offset32,
}, },
HeapLoad {
opcode: Opcode,
ty: Type,
arg: Value,
offset: Uoffset32,
},
HeapStore {
opcode: Opcode,
ty: Type,
args: [Value; 2],
offset: Uoffset32,
},
} }
/// A variable list of `Value` operands used for function call arguments and passing arguments to /// A variable list of `Value` operands used for function call arguments and passing arguments to

View File

@@ -269,7 +269,9 @@ impl<'a> Verifier<'a> {
&ExtractLane { .. } | &ExtractLane { .. } |
&IntCompare { .. } | &IntCompare { .. } |
&IntCompareImm { .. } | &IntCompareImm { .. } |
&FloatCompare { .. } => {} &FloatCompare { .. } |
&HeapLoad { .. } |
&HeapStore { .. } => {}
} }
Ok(()) Ok(())

View File

@@ -320,6 +320,8 @@ pub fn write_operands(w: &mut Write, dfg: &DataFlowGraph, inst: Inst) -> Result
offset, offset,
.. ..
} => write!(w, " {}, {}{}", arg, stack_slot, offset), } => write!(w, " {}, {}{}", arg, stack_slot, offset),
HeapLoad { arg, offset, .. } => write!(w, " {}{}", arg, offset),
HeapStore { args, offset, .. } => write!(w, " {}, {}{}", args[0], args[1], offset),
} }
} }

View File

@@ -227,15 +227,18 @@ impl<'a> Context<'a> {
InstructionData::IntCompareImm { ref mut arg, .. } | InstructionData::IntCompareImm { ref mut arg, .. } |
InstructionData::Unary { ref mut arg, .. } | InstructionData::Unary { ref mut arg, .. } |
InstructionData::UnarySplit { ref mut arg, .. } | InstructionData::UnarySplit { ref mut arg, .. } |
InstructionData::StackStore { ref mut arg, .. } => { InstructionData::StackStore { ref mut arg, .. } |
InstructionData::HeapLoad { ref mut arg, .. } => {
self.map.rewrite_value(arg, loc)?; self.map.rewrite_value(arg, loc)?;
} }
// `args: Value[2]`
InstructionData::Binary { ref mut args, .. } | InstructionData::Binary { ref mut args, .. } |
InstructionData::BinaryOverflow { ref mut args, .. } | InstructionData::BinaryOverflow { ref mut args, .. } |
InstructionData::InsertLane { ref mut args, .. } | InstructionData::InsertLane { ref mut args, .. } |
InstructionData::IntCompare { ref mut args, .. } | InstructionData::IntCompare { ref mut args, .. } |
InstructionData::FloatCompare { ref mut args, .. } => { InstructionData::FloatCompare { ref mut args, .. } |
InstructionData::HeapStore { ref mut args, .. } => {
self.map.rewrite_values(args, loc)?; self.map.rewrite_values(args, loc)?;
} }
@@ -1710,6 +1713,28 @@ impl<'a> Parser<'a> {
offset: offset, offset: offset,
} }
} }
InstructionFormat::HeapLoad => {
let addr = self.match_value("expected SSA value address")?;
let offset = self.optional_uoffset32()?;
InstructionData::HeapLoad {
opcode: opcode,
ty: VOID,
arg: addr,
offset: offset,
}
}
InstructionFormat::HeapStore => {
let arg = self.match_value("expected SSA value operand")?;
self.match_token(Token::Comma, "expected ',' between operands")?;
let addr = self.match_value("expected SSA value address")?;
let offset = self.optional_uoffset32()?;
InstructionData::HeapStore {
opcode: opcode,
ty: VOID,
args: [arg, addr],
offset: offset,
}
}
}; };
Ok(idata) Ok(idata)
} }