Add heap_load, heap_store, and heap_addr instructions.
These are used when lowering WebAssembly sandbox code.
This commit is contained in:
@@ -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::
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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())
|
||||||
|
|||||||
@@ -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.
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -269,7 +269,9 @@ impl<'a> Verifier<'a> {
|
|||||||
&ExtractLane { .. } |
|
&ExtractLane { .. } |
|
||||||
&IntCompare { .. } |
|
&IntCompare { .. } |
|
||||||
&IntCompareImm { .. } |
|
&IntCompareImm { .. } |
|
||||||
&FloatCompare { .. } => {}
|
&FloatCompare { .. } |
|
||||||
|
&HeapLoad { .. } |
|
||||||
|
&HeapStore { .. } => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user