Browse Source

Add heap_load, heap_store, and heap_addr instructions.

These are used when lowering WebAssembly sandbox code.
pull/3/head
Jakob Stoklund Olesen 8 years ago
parent
commit
b5237b6a4a
  1. 42
      docs/langref.rst
  2. 14
      filetests/parser/tiny.cton
  3. 8
      lib/cretonne/meta/base/formats.py
  4. 41
      lib/cretonne/meta/base/instructions.py
  5. 2
      lib/cretonne/src/ir/builder.rs
  6. 14
      lib/cretonne/src/ir/instructions.rs
  7. 4
      lib/cretonne/src/verifier.rs
  8. 2
      lib/cretonne/src/write.rs
  9. 29
      lib/reader/src/parser.rs

42
docs/langref.rst

@ -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.
:result H: Heap identifier.
.. inst:: a = heap_load H, p, Offset
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.
.. autoinst:: heap_load
.. autoinst:: heap_store
When optimizing heap accesses, Cretonne may separate the heap bounds checking
and address computations from the memory accesses.
.. inst:: a = heap_addr H, p, Size
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``.
.. autoinst:: heap_addr
A small example using heaps::

14
filetests/parser/tiny.cton

@ -114,3 +114,17 @@ ebb0:
; nextln: $v2 = stack_load.i32 $ss10+4
; nextln: stack_store $v1, $ss10+2
; 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
lib/cretonne/meta/base/formats.py

@ -8,7 +8,8 @@ in this module.
from __future__ import absolute_import
from cdsl.formats import InstructionFormat
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
Nullary = InstructionFormat()
@ -55,5 +56,10 @@ IndirectCall = InstructionFormat(
StackLoad = InstructionFormat(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.
InstructionFormat.extract_names(globals())

41
lib/cretonne/meta/base/instructions.py

@ -9,7 +9,7 @@ from cdsl.operands import Operand, VARIABLE_ARGS
from cdsl.typevar import TypeVar
from cdsl.instructions import Instruction, InstructionGroup
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 import entities
import base.formats # noqa
@ -209,6 +209,7 @@ SS = Operand('SS', entities.stack_slot)
Offset = Operand('Offset', offset32, 'In-bounds offset into stack slot')
x = Operand('x', Mem, doc='Value to be stored')
a = Operand('a', Mem, doc='Value loaded')
p = Operand('p', iAddr)
addr = Operand('addr', iAddr)
stack_load = Instruction(
@ -247,6 +248,44 @@ stack_addr = Instruction(
""",
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.
#

2
lib/cretonne/src/ir/builder.rs

@ -6,7 +6,7 @@
use ir::types;
use ir::{InstructionData, DataFlowGraph, Cursor};
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};
/// Base trait for instruction builders.

14
lib/cretonne/src/ir/instructions.rs

@ -11,7 +11,7 @@ use std::str::FromStr;
use std::ops::{Deref, DerefMut};
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::types;
use ir::DataFlowGraph;
@ -240,6 +240,18 @@ pub enum InstructionData {
stack_slot: StackSlot,
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

4
lib/cretonne/src/verifier.rs

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

2
lib/cretonne/src/write.rs

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

29
lib/reader/src/parser.rs

@ -227,15 +227,18 @@ impl<'a> Context<'a> {
InstructionData::IntCompareImm { ref mut arg, .. } |
InstructionData::Unary { 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)?;
}
// `args: Value[2]`
InstructionData::Binary { ref mut args, .. } |
InstructionData::BinaryOverflow { ref mut args, .. } |
InstructionData::InsertLane { 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)?;
}
@ -1710,6 +1713,28 @@ impl<'a> Parser<'a> {
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)
}

Loading…
Cancel
Save