Add an isa::StackRef type.
This contains encoding details for a stack reference: The base register and offset to use in the specific instruction encoding. Generate StackRef objects called in_stk0 etc for the binemit recipe code. All binemit recipes need to compute base pointer offsets for stack references, so have the automatically generated code do it.
This commit is contained in:
@@ -104,6 +104,13 @@ impl SubTest for TestBinEmit {
|
||||
// value locations. The current error reporting is just crashing...
|
||||
let mut func = func.into_owned();
|
||||
|
||||
// Fix the stack frame layout so we can test spill/fill encodings.
|
||||
let min_offset = func.stack_slots
|
||||
.keys()
|
||||
.map(|ss| func.stack_slots[ss].offset)
|
||||
.min();
|
||||
func.stack_slots.frame_size = min_offset.map(|off| (-off) as u32);
|
||||
|
||||
let is_compressed = isa.flags().is_compressed();
|
||||
|
||||
// Give an encoding to any instruction that doesn't already have one.
|
||||
|
||||
@@ -352,3 +352,13 @@ class Stack(object):
|
||||
def __init__(self, rc):
|
||||
# type: (RegClass) -> None
|
||||
self.regclass = rc
|
||||
|
||||
def stack_base_mask(self):
|
||||
# type: () -> str
|
||||
"""
|
||||
Get the StackBaseMask to use for this operand.
|
||||
|
||||
This is a mask of base registers that can be supported by this operand.
|
||||
"""
|
||||
# TODO: Make this configurable instead of just using the SP.
|
||||
return 'StackBaseMask(1)'
|
||||
|
||||
@@ -66,11 +66,16 @@ def gen_recipe(recipe, fmt):
|
||||
'let {} = divert.reg(args[{}], &func.locations);'
|
||||
.format(v, i))
|
||||
elif isinstance(arg, Stack):
|
||||
v = 'in_ss{}'.format(i)
|
||||
v = 'in_stk{}'.format(i)
|
||||
args += ', ' + v
|
||||
fmt.line(
|
||||
'let {} = func.locations[args[{}]].unwrap_stack();'
|
||||
.format(v, i))
|
||||
with fmt.indented(
|
||||
'let {} = StackRef::masked('.format(v),
|
||||
').unwrap();'):
|
||||
fmt.format(
|
||||
'func.locations[args[{}]].unwrap_stack(),',
|
||||
i)
|
||||
fmt.format('{},', arg.stack_base_mask())
|
||||
fmt.line('&func.stack_slots,')
|
||||
|
||||
# Pass arguments in this order: inputs, imm_fields, outputs.
|
||||
for f in iform.imm_fields:
|
||||
@@ -86,15 +91,20 @@ def gen_recipe(recipe, fmt):
|
||||
if isinstance(res, RegClass):
|
||||
v = 'out_reg{}'.format(i)
|
||||
args += ', ' + v
|
||||
fmt.line(
|
||||
'let {} = func.locations[results[{}]].unwrap_reg();'
|
||||
.format(v, i))
|
||||
fmt.format(
|
||||
'let {} = func.locations[results[{}]].unwrap_reg();',
|
||||
v, i)
|
||||
elif isinstance(res, Stack):
|
||||
v = 'out_ss{}'.format(i)
|
||||
v = 'out_stk{}'.format(i)
|
||||
args += ', ' + v
|
||||
fmt.line(
|
||||
'let {} = func.locations[results[{}]].unwrap_stack();'
|
||||
.format(v, i))
|
||||
with fmt.indented(
|
||||
'let {} = StackRef::masked('.format(v),
|
||||
').unwrap();'):
|
||||
fmt.format(
|
||||
'func.locations[results[{}]].unwrap_stack(),',
|
||||
i)
|
||||
fmt.format('{},', res.stack_base_mask())
|
||||
fmt.line('&func.stack_slots,')
|
||||
|
||||
# Special handling for regmove instructions. Update the register
|
||||
# diversion tracker.
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
pub use isa::constraints::{RecipeConstraints, OperandConstraint, ConstraintKind, BranchRange};
|
||||
pub use isa::encoding::{Encoding, EncInfo};
|
||||
pub use isa::registers::{RegInfo, RegUnit, RegClass, RegClassIndex, regs_overlap};
|
||||
pub use isa::stack::{StackBase, StackBaseMask, StackRef};
|
||||
|
||||
use binemit;
|
||||
use flowgraph;
|
||||
@@ -68,6 +69,7 @@ pub mod registers;
|
||||
mod encoding;
|
||||
mod enc_tables;
|
||||
mod constraints;
|
||||
mod stack;
|
||||
|
||||
/// Returns a builder that can create a corresponding `TargetIsa`
|
||||
/// or `Err(LookupError::Unsupported)` if not enabled.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use binemit::{CodeSink, Reloc, bad_encoding};
|
||||
use ir::{Function, Inst, InstructionData};
|
||||
use isa::RegUnit;
|
||||
use isa::{RegUnit, StackRef, StackBaseMask};
|
||||
use predicates::is_signed_int;
|
||||
use regalloc::RegDiversions;
|
||||
|
||||
|
||||
82
lib/cretonne/src/isa/stack.rs
Normal file
82
lib/cretonne/src/isa/stack.rs
Normal file
@@ -0,0 +1,82 @@
|
||||
//! Low-level details of stack accesses.
|
||||
//!
|
||||
//! The `ir::StackSlots` type deals with stack slots and stack frame layout. The `StackRef` type
|
||||
//! defined in this module expresses the low-level details of accessing a stack slot from an
|
||||
//! encoded instruction.
|
||||
|
||||
use ir::stackslot::{StackSlots, StackOffset};
|
||||
use ir::StackSlot;
|
||||
|
||||
/// A method for referencing a stack slot in the current stack frame.
|
||||
///
|
||||
/// Stack slots are addressed with a constant offset from a base register. The base can be the
|
||||
/// stack pointer, the frame pointer, or (in the future) a zone register pointing to an inner zone
|
||||
/// of a large stack frame.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct StackRef {
|
||||
/// The base register to use for addressing.
|
||||
pub base: StackBase,
|
||||
|
||||
/// Immediate offset from the base register to the first byte of the stack slot.
|
||||
pub offset: StackOffset,
|
||||
}
|
||||
|
||||
impl StackRef {
|
||||
/// Get a reference to the stack slot `ss` using one of the base pointers in `mask`.
|
||||
pub fn masked(ss: StackSlot, mask: StackBaseMask, frame: &StackSlots) -> Option<StackRef> {
|
||||
let size = frame.frame_size.expect(
|
||||
"Stack layout must be computed before referencing stack slots",
|
||||
);
|
||||
// Offsets relative to the caller's stack pointer.
|
||||
let offset = frame[ss].offset;
|
||||
|
||||
// Try an SP-relative reference.
|
||||
if mask.contains(StackBase::SP) {
|
||||
// Offset where SP is pointing. (All ISAs have stacks growing downwards.)
|
||||
let sp_offset = -(size as StackOffset);
|
||||
return Some(StackRef {
|
||||
base: StackBase::SP,
|
||||
offset: offset - sp_offset,
|
||||
});
|
||||
}
|
||||
|
||||
// No reference possible with this mask.
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Generic base register for referencing stack slots.
|
||||
///
|
||||
/// Most ISAs have a stack pointer and an optional frame pointer, so provide generic names for
|
||||
/// those two base pointers.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum StackBase {
|
||||
/// Use the stack pointer.
|
||||
SP = 0,
|
||||
|
||||
/// Use the frame pointer (if one is present).
|
||||
FP = 1,
|
||||
|
||||
/// Use an explicit zone pointer in a general-purpose register.
|
||||
Zone = 2,
|
||||
}
|
||||
|
||||
/// Bit mask of supported stack bases.
|
||||
///
|
||||
/// Many instruction encodings can use different base registers while others only work with the
|
||||
/// stack pointer, say. A `StackBaseMask` is a bit mask of supported stack bases for a given
|
||||
/// instruction encoding.
|
||||
///
|
||||
/// This behaves like a set of `StackBase` variants.
|
||||
///
|
||||
/// The internal representation as a `u8` is public because stack base masks are used in constant
|
||||
/// tables generated from the Python encoding definitions.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct StackBaseMask(pub u8);
|
||||
|
||||
impl StackBaseMask {
|
||||
/// Check if this mask contains the `base` variant.
|
||||
pub fn contains(self, base: StackBase) -> bool {
|
||||
self.0 & (1 << base as usize) != 0
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user