Define stack_load, stack_store, and stack_addr instructions.

This commit is contained in:
Jakob Stoklund Olesen
2017-04-10 13:20:41 -07:00
parent af2516e996
commit c52e3e0b3f
10 changed files with 163 additions and 56 deletions

View File

@@ -489,33 +489,8 @@ simply represent a contiguous sequence of bytes in the stack frame.
:flag align(N): Request at least N bytes alignment. :flag align(N): Request at least N bytes alignment.
:result SS: Stack slot index. :result SS: Stack slot index.
.. inst:: a = stack_load SS, Offset .. autoinst:: stack_load
.. autoinst:: stack_store
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.
The dedicated stack access instructions are easy for the compiler to reason 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, 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 It can be necessary to escape from the safety of the restricted instructions by
taking the address of a stack slot. taking the address of a stack slot.
.. inst:: a = stack_addr SS, Offset .. autoinst:: stack_addr
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.
The :inst:`stack_addr` instruction can be used to macro-expand the stack access The :inst:`stack_addr` instruction can be used to macro-expand the stack access
instructions before instruction selection:: instructions before instruction selection::

View File

@@ -93,3 +93,24 @@ ebb0(vx0: i32, vx1: f32):
; nextln: v0 = bitcast.i8x4 vx0 ; nextln: v0 = bitcast.i8x4 vx0
; nextln: v1 = bitcast.i32 vx1 ; nextln: v1 = bitcast.i32 vx1
; nextln: } ; 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

View File

@@ -8,8 +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, intcc, floatcc from .immediates import imm64, uimm8, ieee32, ieee64, offset32, intcc, floatcc
from .entities import ebb, sig_ref, func_ref, jump_table from .entities import ebb, sig_ref, func_ref, jump_table, stack_slot
Nullary = InstructionFormat() Nullary = InstructionFormat()
@@ -52,5 +52,8 @@ IndirectCall = InstructionFormat(
sig_ref, VALUE, VARIABLE_ARGS, sig_ref, VALUE, VARIABLE_ARGS,
multiple_results=True) multiple_results=True)
StackLoad = InstructionFormat(stack_slot, offset32)
StackStore = InstructionFormat(VALUE, stack_slot, offset32)
# 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

@@ -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 #: This is used to represent an immediate address offset in load/store
#: instructions. #: 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. #: A 32-bit immediate floating point operand.
#: #:

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 from base.immediates import imm64, uimm8, ieee32, ieee64, offset32
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
@@ -28,6 +28,12 @@ TxN = TypeVar(
Any = TypeVar( Any = TypeVar(
'Any', 'Any integer, float, or boolean scalar or vector type', 'Any', 'Any integer, float, or boolean scalar or vector type',
ints=True, floats=True, bools=True, scalars=True, simd=True) 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 # Control flow
@@ -195,6 +201,52 @@ call_indirect = Instruction(
""", """,
ins=(SIG, callee, args), outs=rvals, is_call=True) 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. # Materializing constants.
# #
@@ -1095,13 +1147,6 @@ nearest = Instruction(
# Conversions # 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) x = Operand('x', Mem)
a = Operand('a', MemTo, 'Bits of `x` reinterpreted') a = Operand('a', MemTo, 'Bits of `x` reinterpreted')

View File

@@ -5,8 +5,8 @@
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, ValueList}; use ir::{Opcode, Type, Inst, Value, Ebb, JumpTable, SigRef, FuncRef, StackSlot, ValueList};
use ir::immediates::{Imm64, Uimm8, Ieee32, Ieee64}; use ir::immediates::{Imm64, Uimm8, Ieee32, Ieee64, Offset32};
use ir::condcodes::{IntCC, FloatCC}; use ir::condcodes::{IntCC, FloatCC};
/// Base trait for instruction builders. /// Base trait for instruction builders.

View File

@@ -10,8 +10,8 @@ use std::fmt::{self, Display, Formatter};
use std::str::FromStr; use std::str::FromStr;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use ir::{Value, Type, Ebb, JumpTable, SigRef, FuncRef}; use ir::{Value, Type, Ebb, JumpTable, SigRef, FuncRef, StackSlot};
use ir::immediates::{Imm64, Uimm8, Ieee32, Ieee64}; use ir::immediates::{Imm64, Uimm8, Ieee32, Ieee64, Offset32};
use ir::condcodes::*; use ir::condcodes::*;
use ir::types; use ir::types;
use ir::DataFlowGraph; use ir::DataFlowGraph;
@@ -227,6 +227,19 @@ pub enum InstructionData {
sig_ref: SigRef, sig_ref: SigRef,
args: ValueList, 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 /// A variable list of `Value` operands used for function call arguments and passing arguments to

View File

@@ -57,7 +57,8 @@ use dominator_tree::DominatorTree;
use flowgraph::ControlFlowGraph; use flowgraph::ControlFlowGraph;
use ir::entities::AnyEntity; use ir::entities::AnyEntity;
use ir::instructions::{InstructionFormat, BranchInfo, ResolvedConstraint, CallInfo}; 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 Context;
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
use std::result; use std::result;
@@ -248,6 +249,11 @@ impl<'a> Verifier<'a> {
self.verify_sig_ref(inst, sig_ref)?; self.verify_sig_ref(inst, sig_ref)?;
self.verify_value_list(inst, args)?; 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 // Exhaustive list so we can't forget to add new formats
&Nullary { .. } | &Nullary { .. } |
&Unary { .. } | &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<()> { fn verify_value_list(&self, inst: Inst, l: &ValueList) -> Result<()> {
if !l.is_valid(&self.func.dfg.value_lists) { if !l.is_valid(&self.func.dfg.value_lists) {
err!(inst, "invalid value list reference {:?}", l) err!(inst, "invalid value list reference {:?}", l)

View File

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

View File

@@ -11,7 +11,7 @@ use std::{u16, u32};
use std::mem; use std::mem;
use cretonne::ir::{Function, Ebb, Opcode, Value, Type, FunctionName, StackSlotData, JumpTable, use cretonne::ir::{Function, Ebb, Opcode, Value, Type, FunctionName, StackSlotData, JumpTable,
JumpTableData, Signature, ArgumentType, ArgumentExtension, ExtFuncData, SigRef, JumpTableData, Signature, ArgumentType, ArgumentExtension, ExtFuncData, SigRef,
FuncRef, ValueLoc, ArgumentLoc}; FuncRef, StackSlot, ValueLoc, ArgumentLoc};
use cretonne::ir::types::VOID; use cretonne::ir::types::VOID;
use cretonne::ir::immediates::{Imm64, Offset32, Ieee32, Ieee64}; use cretonne::ir::immediates::{Imm64, Offset32, Ieee32, Ieee64};
use cretonne::ir::entities::AnyEntity; use cretonne::ir::entities::AnyEntity;
@@ -124,6 +124,14 @@ impl<'a> Context<'a> {
.def_ss(number, self.function.stack_slots.push(data), loc) .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<StackSlot> {
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. // Allocate a new signature and add a mapping number -> SigRef.
fn add_sig(&mut self, number: u32, data: Signature, loc: &Location) -> Result<()> { fn add_sig(&mut self, number: u32, data: Signature, loc: &Location) -> Result<()> {
self.map self.map
@@ -210,14 +218,16 @@ impl<'a> Context<'a> {
InstructionData::Nullary { .. } | InstructionData::Nullary { .. } |
InstructionData::UnaryImm { .. } | InstructionData::UnaryImm { .. } |
InstructionData::UnaryIeee32 { .. } | InstructionData::UnaryIeee32 { .. } |
InstructionData::UnaryIeee64 { .. } => {} InstructionData::UnaryIeee64 { .. } |
InstructionData::StackLoad { .. } => {}
InstructionData::BinaryImm { ref mut arg, .. } | InstructionData::BinaryImm { ref mut arg, .. } |
InstructionData::BranchTable { ref mut arg, .. } | InstructionData::BranchTable { ref mut arg, .. } |
InstructionData::ExtractLane { ref mut arg, .. } | InstructionData::ExtractLane { ref mut arg, .. } |
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, .. } => {
self.map.rewrite_value(arg, loc)?; self.map.rewrite_value(arg, loc)?;
} }
@@ -1659,6 +1669,31 @@ impl<'a> Parser<'a> {
table: table, 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) Ok(idata)
} }