diff --git a/docs/langref.rst b/docs/langref.rst index 24890c8ada..911714751f 100644 --- a/docs/langref.rst +++ b/docs/langref.rst @@ -489,33 +489,8 @@ simply represent a contiguous sequence of bytes in the stack frame. :flag align(N): Request at least N bytes alignment. :result SS: Stack slot index. -.. inst:: a = stack_load SS, Offset - - Load a value from a stack slot at the constant offset. - - This is a polymorphic instruction that can load any value type which has a - memory representation. - - The offset is an immediate constant, not an SSA value. The memory access - cannot go out of bounds, i.e. ``sizeof(a) + Offset <= sizeof(SS)``. - - :arg SS: Stack slot declared with :inst:`stack_slot`. - :arg Offset: Immediate non-negative offset. - :result T a: Value loaded. - -.. inst:: stack_store x, SS, Offset - - Store a value to a stack slot at a constant offset. - - This is a polymorphic instruction that can store any value type with a - memory representation. - - The offset is an immediate constant, not an SSA value. The memory access - cannot go out of bounds, i.e. ``sizeof(a) + Offset <= sizeof(SS)``. - - :arg T x: Value to be stored. - :arg SS: Stack slot declared with :inst:`stack_slot`. - :arg Offset: Immediate non-negative offset. +.. autoinst:: stack_load +.. autoinst:: stack_store The dedicated stack access instructions are easy for the compiler to reason about because stack slots and offsets are fixed at compile time. For example, @@ -525,16 +500,7 @@ and stack slot alignments. It can be necessary to escape from the safety of the restricted instructions by taking the address of a stack slot. -.. inst:: a = stack_addr SS, Offset - - Get the address of a stack slot. - - Compute the absolute address of a byte in a stack slot. The offset must - refer to a byte inside the stack slot: ``0 <= Offset < sizeof(SS)``. - - :arg SS: Stack slot declared with :inst:`stack_slot`. - :arg Offset: Immediate non-negative offset. - :result iPtr a: Address. +.. autoinst:: stack_addr The :inst:`stack_addr` instruction can be used to macro-expand the stack access instructions before instruction selection:: diff --git a/filetests/parser/tiny.cton b/filetests/parser/tiny.cton index 21d3a59815..e89efb4cbe 100644 --- a/filetests/parser/tiny.cton +++ b/filetests/parser/tiny.cton @@ -93,3 +93,24 @@ ebb0(vx0: i32, vx1: f32): ; nextln: v0 = bitcast.i8x4 vx0 ; nextln: v1 = bitcast.i32 vx1 ; nextln: } + +; Stack slot references +function stack() { + ss10 = stack_slot 8 + ss2 = stack_slot 4 + +ebb0: + v1 = stack_load.i32 ss10 + v2 = stack_load.i32 ss10+4 + stack_store v1, ss10+2 + stack_store v2, ss2 +} +; sameln: function stack() { +; nextln: $ss10 = stack_slot 8 +; nextln: $ss2 = stack_slot 4 + +; check: ebb0: +; nextln: $v1 = stack_load.i32 $ss10 +; nextln: $v2 = stack_load.i32 $ss10+4 +; nextln: stack_store $v1, $ss10+2 +; nextln: stack_store $v2, $ss2 diff --git a/lib/cretonne/meta/base/formats.py b/lib/cretonne/meta/base/formats.py index cf1922627f..c840e1b360 100644 --- a/lib/cretonne/meta/base/formats.py +++ b/lib/cretonne/meta/base/formats.py @@ -8,8 +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, intcc, floatcc -from .entities import ebb, sig_ref, func_ref, jump_table +from .immediates import imm64, uimm8, ieee32, ieee64, offset32, intcc, floatcc +from .entities import ebb, sig_ref, func_ref, jump_table, stack_slot Nullary = InstructionFormat() @@ -52,5 +52,8 @@ IndirectCall = InstructionFormat( sig_ref, VALUE, VARIABLE_ARGS, multiple_results=True) +StackLoad = InstructionFormat(stack_slot, offset32) +StackStore = InstructionFormat(VALUE, stack_slot, offset32) + # Finally extract the names of global variables in this module. InstructionFormat.extract_names(globals()) diff --git a/lib/cretonne/meta/base/immediates.py b/lib/cretonne/meta/base/immediates.py index e11ed78e28..ad1bda9989 100644 --- a/lib/cretonne/meta/base/immediates.py +++ b/lib/cretonne/meta/base/immediates.py @@ -21,7 +21,10 @@ uimm8 = ImmediateKind('uimm8', 'An 8-bit immediate unsigned integer.') #: #: This is used to represent an immediate address offset in load/store #: instructions. -offset32 = ImmediateKind('offset32', 'A 32-bit immediate signed offset.') +offset32 = ImmediateKind( + 'offset32', + 'A 32-bit immediate signed offset.', + default_member='offset') #: A 32-bit immediate floating point operand. #: diff --git a/lib/cretonne/meta/base/instructions.py b/lib/cretonne/meta/base/instructions.py index d9eda7a220..29d2f44a16 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 +from base.immediates import imm64, uimm8, ieee32, ieee64, offset32 from base.immediates import intcc, floatcc from base import entities import base.formats # noqa @@ -28,6 +28,12 @@ TxN = TypeVar( Any = TypeVar( 'Any', 'Any integer, float, or boolean scalar or vector type', ints=True, floats=True, bools=True, scalars=True, simd=True) +Mem = TypeVar( + 'Mem', 'Any type that can be stored in memory', + ints=True, floats=True, simd=True) +MemTo = TypeVar( + 'MemTo', 'Any type that can be stored in memory', + ints=True, floats=True, simd=True) # # Control flow @@ -195,6 +201,52 @@ call_indirect = Instruction( """, ins=(SIG, callee, args), outs=rvals, is_call=True) +# +# Memory operations +# + +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') +addr = Operand('addr', iAddr) + +stack_load = Instruction( + 'stack_load', r""" + Load a value from a stack slot at the constant offset. + + This is a polymorphic instruction that can load any value type which + has a memory representation. + + The offset is an immediate constant, not an SSA value. The memory + access cannot go out of bounds, i.e. + :math:`sizeof(a) + Offset <= sizeof(SS)`. + """, + ins=(SS, Offset), outs=a) + +stack_store = Instruction( + 'stack_store', r""" + Store a value to a stack slot at a constant offset. + + This is a polymorphic instruction that can store any value type with a + memory representation. + + The offset is an immediate constant, not an SSA value. The memory + access cannot go out of bounds, i.e. + :math:`sizeof(a) + Offset <= sizeof(SS)`. + """, + ins=(x, SS, Offset)) + +stack_addr = Instruction( + 'stack_addr', r""" + Get the address of a stack slot. + + Compute the absolute address of a byte in a stack slot. The offset must + refer to a byte inside the stack slot: + :math:`0 <= Offset < sizeof(SS)`. + """, + ins=(SS, Offset), outs=addr) + # # Materializing constants. # @@ -1095,13 +1147,6 @@ nearest = Instruction( # Conversions # -Mem = TypeVar( - 'Mem', 'Any type that can be stored in memory', - ints=True, floats=True, simd=True) -MemTo = TypeVar( - 'MemTo', 'Any type that can be stored in memory', - ints=True, floats=True, simd=True) - x = Operand('x', Mem) a = Operand('a', MemTo, 'Bits of `x` reinterpreted') diff --git a/lib/cretonne/src/ir/builder.rs b/lib/cretonne/src/ir/builder.rs index fb5c4ba20a..e15a93bf30 100644 --- a/lib/cretonne/src/ir/builder.rs +++ b/lib/cretonne/src/ir/builder.rs @@ -5,8 +5,8 @@ use ir::types; use ir::{InstructionData, DataFlowGraph, Cursor}; -use ir::{Opcode, Type, Inst, Value, Ebb, JumpTable, SigRef, FuncRef, ValueList}; -use ir::immediates::{Imm64, Uimm8, Ieee32, Ieee64}; +use ir::{Opcode, Type, Inst, Value, Ebb, JumpTable, SigRef, FuncRef, StackSlot, ValueList}; +use ir::immediates::{Imm64, Uimm8, Ieee32, Ieee64, Offset32}; 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 9816ccbc91..a0ff3a3be4 100644 --- a/lib/cretonne/src/ir/instructions.rs +++ b/lib/cretonne/src/ir/instructions.rs @@ -10,8 +10,8 @@ use std::fmt::{self, Display, Formatter}; use std::str::FromStr; use std::ops::{Deref, DerefMut}; -use ir::{Value, Type, Ebb, JumpTable, SigRef, FuncRef}; -use ir::immediates::{Imm64, Uimm8, Ieee32, Ieee64}; +use ir::{Value, Type, Ebb, JumpTable, SigRef, FuncRef, StackSlot}; +use ir::immediates::{Imm64, Uimm8, Ieee32, Ieee64, Offset32}; use ir::condcodes::*; use ir::types; use ir::DataFlowGraph; @@ -227,6 +227,19 @@ pub enum InstructionData { sig_ref: SigRef, args: ValueList, }, + StackLoad { + opcode: Opcode, + ty: Type, + stack_slot: StackSlot, + offset: Offset32, + }, + StackStore { + opcode: Opcode, + ty: Type, + arg: Value, + stack_slot: StackSlot, + offset: Offset32, + }, } /// 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 776755d4c4..29a40d55b9 100644 --- a/lib/cretonne/src/verifier.rs +++ b/lib/cretonne/src/verifier.rs @@ -57,7 +57,8 @@ use dominator_tree::DominatorTree; use flowgraph::ControlFlowGraph; use ir::entities::AnyEntity; use ir::instructions::{InstructionFormat, BranchInfo, ResolvedConstraint, CallInfo}; -use ir::{types, Function, ValueDef, Ebb, Inst, SigRef, FuncRef, ValueList, JumpTable, Value, Type}; +use ir::{types, Function, ValueDef, Ebb, Inst, SigRef, FuncRef, ValueList, JumpTable, StackSlot, + Value, Type}; use Context; use std::fmt::{self, Display, Formatter}; use std::result; @@ -248,6 +249,11 @@ impl<'a> Verifier<'a> { self.verify_sig_ref(inst, sig_ref)?; self.verify_value_list(inst, args)?; } + &StackLoad { stack_slot, .. } | + &StackStore { stack_slot, .. } => { + self.verify_stack_slot(inst, stack_slot)?; + } + // Exhaustive list so we can't forget to add new formats &Nullary { .. } | &Unary { .. } | @@ -293,6 +299,14 @@ impl<'a> Verifier<'a> { } } + fn verify_stack_slot(&self, inst: Inst, ss: StackSlot) -> Result<()> { + if !self.func.stack_slots.is_valid(ss) { + err!(inst, "invalid stack slot {}", ss) + } else { + Ok(()) + } + } + fn verify_value_list(&self, inst: Inst, l: &ValueList) -> Result<()> { if !l.is_valid(&self.func.dfg.value_lists) { err!(inst, "invalid value list reference {:?}", l) diff --git a/lib/cretonne/src/write.rs b/lib/cretonne/src/write.rs index eaa5248408..1ab481cdd1 100644 --- a/lib/cretonne/src/write.rs +++ b/lib/cretonne/src/write.rs @@ -313,6 +313,13 @@ pub fn write_operands(w: &mut Write, dfg: &DataFlowGraph, inst: Inst) -> Result args[0], DisplayValues(&args[1..])) } + StackLoad { stack_slot, offset, .. } => write!(w, " {}{}", stack_slot, offset), + StackStore { + arg, + stack_slot, + offset, + .. + } => write!(w, " {}, {}{}", arg, stack_slot, offset), } } diff --git a/lib/reader/src/parser.rs b/lib/reader/src/parser.rs index 741621ed08..9a36ec2451 100644 --- a/lib/reader/src/parser.rs +++ b/lib/reader/src/parser.rs @@ -11,7 +11,7 @@ use std::{u16, u32}; use std::mem; use cretonne::ir::{Function, Ebb, Opcode, Value, Type, FunctionName, StackSlotData, JumpTable, JumpTableData, Signature, ArgumentType, ArgumentExtension, ExtFuncData, SigRef, - FuncRef, ValueLoc, ArgumentLoc}; + FuncRef, StackSlot, ValueLoc, ArgumentLoc}; use cretonne::ir::types::VOID; use cretonne::ir::immediates::{Imm64, Offset32, Ieee32, Ieee64}; use cretonne::ir::entities::AnyEntity; @@ -124,6 +124,14 @@ impl<'a> Context<'a> { .def_ss(number, self.function.stack_slots.push(data), loc) } + // Resolve a reference to a stack slot. + fn get_ss(&self, number: u32, loc: &Location) -> Result { + match self.map.get_ss(number) { + Some(sig) => Ok(sig), + None => err!(loc, "undefined stack slot ss{}", number), + } + } + // Allocate a new signature and add a mapping number -> SigRef. fn add_sig(&mut self, number: u32, data: Signature, loc: &Location) -> Result<()> { self.map @@ -210,14 +218,16 @@ impl<'a> Context<'a> { InstructionData::Nullary { .. } | InstructionData::UnaryImm { .. } | InstructionData::UnaryIeee32 { .. } | - InstructionData::UnaryIeee64 { .. } => {} + InstructionData::UnaryIeee64 { .. } | + InstructionData::StackLoad { .. } => {} InstructionData::BinaryImm { ref mut arg, .. } | InstructionData::BranchTable { ref mut arg, .. } | InstructionData::ExtractLane { ref mut arg, .. } | InstructionData::IntCompareImm { ref mut arg, .. } | InstructionData::Unary { ref mut arg, .. } | - InstructionData::UnarySplit { ref mut arg, .. } => { + InstructionData::UnarySplit { ref mut arg, .. } | + InstructionData::StackStore { ref mut arg, .. } => { self.map.rewrite_value(arg, loc)?; } @@ -1659,6 +1669,31 @@ impl<'a> Parser<'a> { table: table, } } + InstructionFormat::StackLoad => { + let ss = self.match_ss("expected stack slot number: ss«n»") + .and_then(|num| ctx.get_ss(num, &self.loc))?; + let offset = self.optional_offset32()?; + InstructionData::StackLoad { + opcode: opcode, + ty: VOID, + stack_slot: ss, + offset: offset, + } + } + InstructionFormat::StackStore => { + let arg = self.match_value("expected SSA value operand")?; + self.match_token(Token::Comma, "expected ',' between operands")?; + let ss = self.match_ss("expected stack slot number: ss«n»") + .and_then(|num| ctx.get_ss(num, &self.loc))?; + let offset = self.optional_offset32()?; + InstructionData::StackStore { + opcode: opcode, + ty: VOID, + arg: arg, + stack_slot: ss, + offset: offset, + } + } }; Ok(idata) }