Define stack_load, stack_store, and stack_addr instructions.
This commit is contained in:
@@ -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::
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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())
|
||||||
|
|||||||
@@ -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.
|
||||||
#:
|
#:
|
||||||
|
|||||||
@@ -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')
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user