diff --git a/cranelift/docs/langref.rst b/cranelift/docs/langref.rst index 911714751f..4156a95aa3 100644 --- a/cranelift/docs/langref.rst +++ b/cranelift/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:: diff --git a/cranelift/filetests/parser/tiny.cton b/cranelift/filetests/parser/tiny.cton index e89efb4cbe..92842cd140 100644 --- a/cranelift/filetests/parser/tiny.cton +++ b/cranelift/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 diff --git a/lib/cretonne/meta/base/formats.py b/lib/cretonne/meta/base/formats.py index c840e1b360..767be3be7e 100644 --- a/lib/cretonne/meta/base/formats.py +++ b/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()) diff --git a/lib/cretonne/meta/base/instructions.py b/lib/cretonne/meta/base/instructions.py index 29d2f44a16..d3de015311 100644 --- a/lib/cretonne/meta/base/instructions.py +++ b/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. # diff --git a/lib/cretonne/src/ir/builder.rs b/lib/cretonne/src/ir/builder.rs index e15a93bf30..8b95f20272 100644 --- a/lib/cretonne/src/ir/builder.rs +++ b/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. diff --git a/lib/cretonne/src/ir/instructions.rs b/lib/cretonne/src/ir/instructions.rs index a0ff3a3be4..5e8d66a09c 100644 --- a/lib/cretonne/src/ir/instructions.rs +++ b/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 diff --git a/lib/cretonne/src/verifier.rs b/lib/cretonne/src/verifier.rs index 29a40d55b9..b1b4429d1a 100644 --- a/lib/cretonne/src/verifier.rs +++ b/lib/cretonne/src/verifier.rs @@ -269,7 +269,9 @@ impl<'a> Verifier<'a> { &ExtractLane { .. } | &IntCompare { .. } | &IntCompareImm { .. } | - &FloatCompare { .. } => {} + &FloatCompare { .. } | + &HeapLoad { .. } | + &HeapStore { .. } => {} } Ok(()) diff --git a/lib/cretonne/src/write.rs b/lib/cretonne/src/write.rs index 1ab481cdd1..f4b7de5f75 100644 --- a/lib/cretonne/src/write.rs +++ b/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), } } diff --git a/lib/reader/src/parser.rs b/lib/reader/src/parser.rs index 8775f1f0b4..e813404130 100644 --- a/lib/reader/src/parser.rs +++ b/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) }