Fix #335: Introduce variable size recipes and remove GPR_SAFE reg classes (#552)

* Rename size to base_size and introduce a compute_size function;

* Add infra to inspect in/outs registers when computing the size of an instruction;

* Remove the GPR_SAFE_DEREF and GPR_ZERO_DEREF_SAFE register classes on x86 (fixes #335);
This commit is contained in:
Benjamin Bouvier
2018-10-16 00:43:38 +02:00
committed by Dan Gohman
parent c2069762ef
commit 9d6821d6d9
17 changed files with 498 additions and 240 deletions

View File

@@ -0,0 +1,44 @@
test regalloc
target x86_64
function u0:587() fast {
ebb0:
v97 = iconst.i32 0
v169 = iconst.i32 0
v1729 = iconst.i32 0
jump ebb100(v97, v97, v97, v97, v97)
ebb100(v1758: i32, v1784: i32, v1845: i32, v1856: i32, v1870: i32):
v1762 = iconst.i32 0
v1769 = iconst.i32 0
v1774 = iconst.i32 0
v1864 = iconst.i32 0
v1897 = iconst.i32 0
jump ebb102(v1774, v1784, v1845, v1856, v1870, v1758, v1762, v169, v1729, v97, v169, v169, v169, v169)
ebb102(v1785: i32, v1789: i32, v1843: i32, v1854: i32, v1868: i32, v1882: i32, v1890: i32, v1901: i32, v1921: i32, v1933: i32, v2058: i32, v2124: i32, v2236: i32, v2366: i32):
v1929 = iconst.i32 0
v1943 = iconst.i32 0
v1949 = iconst.i32 0
jump ebb123(v1897, v1769)
ebb123(v1950: i32, v1979: i32):
v1955 = iconst.i32 0
brz v1955, ebb125
jump ebb122(v1929, v1843, v1864, v2058, v1882, v1897, v1943, v1868, v2124, v1901)
ebb125:
v1961 = iadd_imm.i32 v1949, 0
v1952 = iconst.i32 0
v1962 = iconst.i64 0
v1963 = load.i32 v1962
brz v1963, ebb123(v1952, v1961)
jump ebb127
ebb127:
v1966 = iconst.i32 0
jump ebb122(v1963, v1966, v1966, v1966, v1966, v1966, v1966, v1966, v1966, v1966)
ebb122(v1967: i32, v1971: i32, v1972: i32, v1978: i32, v2032: i32, v2041: i32, v2053: i32, v2076: i32, v2085: i32, v2096: i32):
trap user0
}

View File

@@ -316,7 +316,8 @@ class EncRecipe(object):
:param name: Short mnemonic name for this recipe. :param name: Short mnemonic name for this recipe.
:param format: All encoded instructions must have this :param format: All encoded instructions must have this
:py:class:`InstructionFormat`. :py:class:`InstructionFormat`.
:param size: Number of bytes in the binary encoded instruction. :param base_size: Base number of bytes in the binary encoded instruction.
:param compute_size: Function name to use when computing actual size.
:param ins: Tuple of register constraints for value operands. :param ins: Tuple of register constraints for value operands.
:param outs: Tuple of register constraints for results. :param outs: Tuple of register constraints for results.
:param branch_range: `(origin, bits)` range for branches. :param branch_range: `(origin, bits)` range for branches.
@@ -330,9 +331,10 @@ class EncRecipe(object):
self, self,
name, # type: str name, # type: str
format, # type: InstructionFormat format, # type: InstructionFormat
size, # type: int base_size, # type: int
ins, # type: ConstraintSeq ins, # type: ConstraintSeq
outs, # type: ConstraintSeq outs, # type: ConstraintSeq
compute_size=None, # type: str
branch_range=None, # type: BranchRange branch_range=None, # type: BranchRange
clobbers_flags=True, # type: bool clobbers_flags=True, # type: bool
instp=None, # type: PredNode instp=None, # type: PredNode
@@ -342,8 +344,10 @@ class EncRecipe(object):
# type: (...) -> None # type: (...) -> None
self.name = name self.name = name
self.format = format self.format = format
assert size >= 0 assert base_size >= 0
self.size = size self.base_size = base_size
self.compute_size = compute_size if compute_size is not None \
else 'base_size'
self.branch_range = branch_range self.branch_range = branch_range
self.clobbers_flags = clobbers_flags self.clobbers_flags = clobbers_flags
self.instp = instp self.instp = instp

View File

@@ -832,7 +832,8 @@ def emit_recipe_sizing(isa, fmt):
for r in isa.all_recipes: for r in isa.all_recipes:
fmt.comment('Code size information for recipe {}:'.format(r.name)) fmt.comment('Code size information for recipe {}:'.format(r.name))
with fmt.indented('RecipeSizing {', '},'): with fmt.indented('RecipeSizing {', '},'):
fmt.format('bytes: {},', r.size) fmt.format('base_size: {},', r.base_size)
fmt.format('compute_size: {},', r.compute_size)
if r.branch_range: if r.branch_range:
fmt.format( fmt.format(
'branch_range: ' 'branch_range: '

View File

@@ -93,33 +93,33 @@ def LUI():
# R-type 32-bit instructions: These are mostly binary arithmetic instructions. # R-type 32-bit instructions: These are mostly binary arithmetic instructions.
# The encbits are `opcode[6:2] | (funct3 << 5) | (funct7 << 8) # The encbits are `opcode[6:2] | (funct3 << 5) | (funct7 << 8)
R = EncRecipe( R = EncRecipe(
'R', Binary, size=4, ins=(GPR, GPR), outs=GPR, 'R', Binary, base_size=4, ins=(GPR, GPR), outs=GPR,
emit='put_r(bits, in_reg0, in_reg1, out_reg0, sink);') emit='put_r(bits, in_reg0, in_reg1, out_reg0, sink);')
# R-type with an immediate shift amount instead of rs2. # R-type with an immediate shift amount instead of rs2.
Rshamt = EncRecipe( Rshamt = EncRecipe(
'Rshamt', BinaryImm, size=4, ins=GPR, outs=GPR, 'Rshamt', BinaryImm, base_size=4, ins=GPR, outs=GPR,
emit='put_rshamt(bits, in_reg0, imm.into(), out_reg0, sink);') emit='put_rshamt(bits, in_reg0, imm.into(), out_reg0, sink);')
# R-type encoding of an integer comparison. # R-type encoding of an integer comparison.
Ricmp = EncRecipe( Ricmp = EncRecipe(
'Ricmp', IntCompare, size=4, ins=(GPR, GPR), outs=GPR, 'Ricmp', IntCompare, base_size=4, ins=(GPR, GPR), outs=GPR,
emit='put_r(bits, in_reg0, in_reg1, out_reg0, sink);') emit='put_r(bits, in_reg0, in_reg1, out_reg0, sink);')
Ii = EncRecipe( Ii = EncRecipe(
'Ii', BinaryImm, size=4, ins=GPR, outs=GPR, 'Ii', BinaryImm, base_size=4, ins=GPR, outs=GPR,
instp=IsSignedInt(BinaryImm.imm, 12), instp=IsSignedInt(BinaryImm.imm, 12),
emit='put_i(bits, in_reg0, imm.into(), out_reg0, sink);') emit='put_i(bits, in_reg0, imm.into(), out_reg0, sink);')
# I-type instruction with a hardcoded %x0 rs1. # I-type instruction with a hardcoded %x0 rs1.
Iz = EncRecipe( Iz = EncRecipe(
'Iz', UnaryImm, size=4, ins=(), outs=GPR, 'Iz', UnaryImm, base_size=4, ins=(), outs=GPR,
instp=IsSignedInt(UnaryImm.imm, 12), instp=IsSignedInt(UnaryImm.imm, 12),
emit='put_i(bits, 0, imm.into(), out_reg0, sink);') emit='put_i(bits, 0, imm.into(), out_reg0, sink);')
# I-type encoding of an integer comparison. # I-type encoding of an integer comparison.
Iicmp = EncRecipe( Iicmp = EncRecipe(
'Iicmp', IntCompareImm, size=4, ins=GPR, outs=GPR, 'Iicmp', IntCompareImm, base_size=4, ins=GPR, outs=GPR,
instp=IsSignedInt(IntCompareImm.imm, 12), instp=IsSignedInt(IntCompareImm.imm, 12),
emit='put_i(bits, in_reg0, imm.into(), out_reg0, sink);') emit='put_i(bits, in_reg0, imm.into(), out_reg0, sink);')
@@ -127,7 +127,7 @@ Iicmp = EncRecipe(
# immediate offset. # immediate offset.
# The variable return values are not encoded. # The variable return values are not encoded.
Iret = EncRecipe( Iret = EncRecipe(
'Iret', MultiAry, size=4, ins=(), outs=(), 'Iret', MultiAry, base_size=4, ins=(), outs=(),
emit=''' emit='''
// Return instructions are always a jalr to %x1. // Return instructions are always a jalr to %x1.
// The return address is provided as a special-purpose link argument. // The return address is provided as a special-purpose link argument.
@@ -142,7 +142,7 @@ Iret = EncRecipe(
# I-type encoding for `jalr` as a call_indirect. # I-type encoding for `jalr` as a call_indirect.
Icall = EncRecipe( Icall = EncRecipe(
'Icall', CallIndirect, size=4, ins=GPR, outs=(), 'Icall', CallIndirect, base_size=4, ins=GPR, outs=(),
emit=''' emit='''
// call_indirect instructions are jalr with rd=%x1. // call_indirect instructions are jalr with rd=%x1.
put_i( put_i(
@@ -157,23 +157,23 @@ Icall = EncRecipe(
# Copy of a GPR is implemented as addi x, 0. # Copy of a GPR is implemented as addi x, 0.
Icopy = EncRecipe( Icopy = EncRecipe(
'Icopy', Unary, size=4, ins=GPR, outs=GPR, 'Icopy', Unary, base_size=4, ins=GPR, outs=GPR,
emit='put_i(bits, in_reg0, 0, out_reg0, sink);') emit='put_i(bits, in_reg0, 0, out_reg0, sink);')
# Same for a GPR regmove. # Same for a GPR regmove.
Irmov = EncRecipe( Irmov = EncRecipe(
'Irmov', RegMove, size=4, ins=GPR, outs=(), 'Irmov', RegMove, base_size=4, ins=GPR, outs=(),
emit='put_i(bits, src, 0, dst, sink);') emit='put_i(bits, src, 0, dst, sink);')
# U-type instructions have a 20-bit immediate that targets bits 12-31. # U-type instructions have a 20-bit immediate that targets bits 12-31.
U = EncRecipe( U = EncRecipe(
'U', UnaryImm, size=4, ins=(), outs=GPR, 'U', UnaryImm, base_size=4, ins=(), outs=GPR,
instp=IsSignedInt(UnaryImm.imm, 32, 12), instp=IsSignedInt(UnaryImm.imm, 32, 12),
emit='put_u(bits, imm.into(), out_reg0, sink);') emit='put_u(bits, imm.into(), out_reg0, sink);')
# UJ-type unconditional branch instructions. # UJ-type unconditional branch instructions.
UJ = EncRecipe( UJ = EncRecipe(
'UJ', Jump, size=4, ins=(), outs=(), branch_range=(0, 21), 'UJ', Jump, base_size=4, ins=(), outs=(), branch_range=(0, 21),
emit=''' emit='''
let dest = i64::from(func.offsets[destination]); let dest = i64::from(func.offsets[destination]);
let disp = dest - i64::from(sink.offset()); let disp = dest - i64::from(sink.offset());
@@ -181,7 +181,7 @@ UJ = EncRecipe(
''') ''')
UJcall = EncRecipe( UJcall = EncRecipe(
'UJcall', Call, size=4, ins=(), outs=(), 'UJcall', Call, base_size=4, ins=(), outs=(),
emit=''' emit='''
sink.reloc_external(Reloc::RiscvCall, sink.reloc_external(Reloc::RiscvCall,
&func.dfg.ext_funcs[func_ref].name, &func.dfg.ext_funcs[func_ref].name,
@@ -192,7 +192,7 @@ UJcall = EncRecipe(
# SB-type branch instructions. # SB-type branch instructions.
SB = EncRecipe( SB = EncRecipe(
'SB', BranchIcmp, size=4, 'SB', BranchIcmp, base_size=4,
ins=(GPR, GPR), outs=(), ins=(GPR, GPR), outs=(),
branch_range=(0, 13), branch_range=(0, 13),
emit=''' emit='''
@@ -203,7 +203,7 @@ SB = EncRecipe(
# SB-type branch instruction with rs2 fixed to zero. # SB-type branch instruction with rs2 fixed to zero.
SBzero = EncRecipe( SBzero = EncRecipe(
'SBzero', Branch, size=4, 'SBzero', Branch, base_size=4,
ins=(GPR), outs=(), ins=(GPR), outs=(),
branch_range=(0, 13), branch_range=(0, 13),
emit=''' emit='''
@@ -214,12 +214,12 @@ SBzero = EncRecipe(
# Spill of a GPR. # Spill of a GPR.
GPsp = EncRecipe( GPsp = EncRecipe(
'GPsp', Unary, size=4, 'GPsp', Unary, base_size=4,
ins=GPR, outs=Stack(GPR), ins=GPR, outs=Stack(GPR),
emit='unimplemented!();') emit='unimplemented!();')
# Fill of a GPR. # Fill of a GPR.
GPfi = EncRecipe( GPfi = EncRecipe(
'GPfi', Unary, size=4, 'GPfi', Unary, base_size=4,
ins=Stack(GPR), outs=GPR, ins=Stack(GPR), outs=GPR,
emit='unimplemented!();') emit='unimplemented!();')

File diff suppressed because it is too large Load Diff

View File

@@ -46,14 +46,7 @@ FlagRegs = RegBank(
names=['rflags']) names=['rflags'])
GPR = RegClass(IntRegs) GPR = RegClass(IntRegs)
# Certain types of deref encodings cannot be used with all registers.
# R13/RBP cannot be used with zero-offset load or store instructions.
# R12 cannot be used with a non-SIB-byte encoding of all derefs.
GPR_DEREF_SAFE = GPR.without(GPR.rsp, GPR.r12)
GPR_ZERO_DEREF_SAFE = GPR_DEREF_SAFE.without(GPR.rbp, GPR.r13)
GPR8 = GPR[0:8] GPR8 = GPR[0:8]
GPR8_DEREF_SAFE = GPR8.without(GPR.rsp)
GPR8_ZERO_DEREF_SAFE = GPR8_DEREF_SAFE.without(GPR.rbp)
ABCD = GPR[0:4] ABCD = GPR[0:4]
FPR = RegClass(FloatRegs) FPR = RegClass(FloatRegs)
FPR8 = FPR[0:8] FPR8 = FPR[0:8]

View File

@@ -32,6 +32,7 @@ use cursor::{Cursor, FuncCursor};
use ir::{Function, InstructionData, Opcode}; use ir::{Function, InstructionData, Opcode};
use isa::{EncInfo, TargetIsa}; use isa::{EncInfo, TargetIsa};
use iterators::IteratorExtras; use iterators::IteratorExtras;
use regalloc::RegDiversions;
use timing; use timing;
use CodegenResult; use CodegenResult;
@@ -51,6 +52,7 @@ pub fn relax_branches(func: &mut Function, isa: &TargetIsa) -> CodegenResult<Cod
fallthroughs(func); fallthroughs(func);
let mut offset = 0; let mut offset = 0;
let mut divert = RegDiversions::new();
// The relaxation algorithm iterates to convergence. // The relaxation algorithm iterates to convergence.
let mut go_again = true; let mut go_again = true;
@@ -61,6 +63,8 @@ pub fn relax_branches(func: &mut Function, isa: &TargetIsa) -> CodegenResult<Cod
// Visit all instructions in layout order // Visit all instructions in layout order
let mut cur = FuncCursor::new(func); let mut cur = FuncCursor::new(func);
while let Some(ebb) = cur.next_ebb() { while let Some(ebb) = cur.next_ebb() {
divert.clear();
// Record the offset for `ebb` and make sure we iterate until offsets are stable. // Record the offset for `ebb` and make sure we iterate until offsets are stable.
if cur.func.offsets[ebb] != offset { if cur.func.offsets[ebb] != offset {
debug_assert!( debug_assert!(
@@ -72,8 +76,9 @@ pub fn relax_branches(func: &mut Function, isa: &TargetIsa) -> CodegenResult<Cod
} }
while let Some(inst) = cur.next_inst() { while let Some(inst) = cur.next_inst() {
divert.apply(&cur.func.dfg[inst]);
let enc = cur.func.encodings[inst]; let enc = cur.func.encodings[inst];
let size = encinfo.bytes(enc);
// See if this might be a branch that is out of range. // See if this might be a branch that is out of range.
if let Some(range) = encinfo.branch_range(enc) { if let Some(range) = encinfo.branch_range(enc) {
@@ -84,13 +89,14 @@ pub fn relax_branches(func: &mut Function, isa: &TargetIsa) -> CodegenResult<Cod
if !range.contains(offset, dest_offset) if !range.contains(offset, dest_offset)
&& (dest_offset != 0 || Some(dest) == cur.func.layout.entry_block()) && (dest_offset != 0 || Some(dest) == cur.func.layout.entry_block())
{ {
offset += relax_branch(&mut cur, offset, dest_offset, &encinfo, isa); offset +=
relax_branch(&mut cur, &divert, offset, dest_offset, &encinfo, isa);
continue; continue;
} }
} }
} }
offset += size; offset += encinfo.byte_size(enc, inst, &divert, &cur.func);
} }
} }
} }
@@ -141,6 +147,7 @@ fn fallthroughs(func: &mut Function) {
/// left. /// left.
fn relax_branch( fn relax_branch(
cur: &mut FuncCursor, cur: &mut FuncCursor,
divert: &RegDiversions,
offset: CodeOffset, offset: CodeOffset,
dest_offset: CodeOffset, dest_offset: CodeOffset,
encinfo: &EncInfo, encinfo: &EncInfo,
@@ -181,7 +188,7 @@ fn relax_branch(
} }
}) { }) {
cur.func.encodings[inst] = enc; cur.func.encodings[inst] = enc;
return encinfo.bytes(enc); return encinfo.byte_size(enc, inst, &divert, &cur.func);
} }
// Note: On some RISC ISAs, conditional branches have shorter range than unconditional // Note: On some RISC ISAs, conditional branches have shorter range than unconditional

View File

@@ -48,7 +48,7 @@ pub fn shrink_instructions(func: &mut Function, isa: &TargetIsa) {
let best_enc = isa let best_enc = isa
.legal_encodings(func, &func.dfg[inst], ctrl_type) .legal_encodings(func, &func.dfg[inst], ctrl_type)
.filter(|e| encinfo.constraints[e.recipe()].satisfied(inst, &divert, &func)) .filter(|e| encinfo.constraints[e.recipe()].satisfied(inst, &divert, &func))
.min_by_key(|e| encinfo.bytes(*e)) .min_by_key(|e| encinfo.byte_size(*e, inst, &divert, &func))
.unwrap(); .unwrap();
if best_enc != enc { if best_enc != enc {
@@ -59,8 +59,8 @@ pub fn shrink_instructions(func: &mut Function, isa: &TargetIsa) {
encinfo.display(enc), encinfo.display(enc),
encinfo.display(best_enc), encinfo.display(best_enc),
func.dfg.display_inst(inst, isa), func.dfg.display_inst(inst, isa),
encinfo.bytes(enc), encinfo.byte_size(enc, inst, &divert, &func),
encinfo.bytes(best_enc) encinfo.byte_size(best_enc, inst, &divert, &func)
); );
} }
} }

View File

@@ -328,7 +328,6 @@ impl Context {
let code_size = relax_branches(&mut self.func, isa)?; let code_size = relax_branches(&mut self.func, isa)?;
self.verify_if(isa)?; self.verify_if(isa)?;
self.verify_locations_if(isa)?; self.verify_locations_if(isa)?;
Ok(code_size) Ok(code_size)
} }
} }

View File

@@ -14,6 +14,7 @@ use ir::{
use ir::{EbbOffsets, InstEncodings, SourceLocs, StackSlots, ValueLocations}; use ir::{EbbOffsets, InstEncodings, SourceLocs, StackSlots, ValueLocations};
use ir::{JumpTableOffsets, JumpTables}; use ir::{JumpTableOffsets, JumpTables};
use isa::{EncInfo, Encoding, Legalize, TargetIsa}; use isa::{EncInfo, Encoding, Legalize, TargetIsa};
use regalloc::RegDiversions;
use settings::CallConv; use settings::CallConv;
use std::fmt; use std::fmt;
use write::write_function; use write::write_function;
@@ -177,13 +178,20 @@ impl Function {
/// ///
/// This function can only be used after the code layout has been computed by the /// This function can only be used after the code layout has been computed by the
/// `binemit::relax_branches()` function. /// `binemit::relax_branches()` function.
pub fn inst_offsets<'a>(&'a self, ebb: Ebb, encinfo: &EncInfo) -> InstOffsetIter<'a> { pub fn inst_offsets<'a>(
&'a self,
func: &'a Function,
ebb: Ebb,
encinfo: &EncInfo,
) -> InstOffsetIter<'a> {
assert!( assert!(
!self.offsets.is_empty(), !self.offsets.is_empty(),
"Code layout must be computed first" "Code layout must be computed first"
); );
InstOffsetIter { InstOffsetIter {
encinfo: encinfo.clone(), encinfo: encinfo.clone(),
func,
divert: RegDiversions::new(),
encodings: &self.encodings, encodings: &self.encodings,
offset: self.offsets[ebb], offset: self.offsets[ebb],
iter: self.layout.ebb_insts(ebb), iter: self.layout.ebb_insts(ebb),
@@ -226,6 +234,8 @@ impl fmt::Debug for Function {
/// Iterator returning instruction offsets and sizes: `(offset, inst, size)`. /// Iterator returning instruction offsets and sizes: `(offset, inst, size)`.
pub struct InstOffsetIter<'a> { pub struct InstOffsetIter<'a> {
encinfo: EncInfo, encinfo: EncInfo,
divert: RegDiversions,
func: &'a Function,
encodings: &'a InstEncodings, encodings: &'a InstEncodings,
offset: CodeOffset, offset: CodeOffset,
iter: ir::layout::Insts<'a>, iter: ir::layout::Insts<'a>,
@@ -236,10 +246,13 @@ impl<'a> Iterator for InstOffsetIter<'a> {
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(|inst| { self.iter.next().map(|inst| {
let size = self.encinfo.bytes(self.encodings[inst]); self.divert.apply(&self.func.dfg[inst]);
let byte_size =
self.encinfo
.byte_size(self.encodings[inst], inst, &self.divert, self.func);
let offset = self.offset; let offset = self.offset;
self.offset += size; self.offset += byte_size;
(offset, inst, size) (offset, inst, byte_size)
}) })
} }
} }

View File

@@ -63,7 +63,7 @@ pub enum StackSlotKind {
/// An emergency spill slot. /// An emergency spill slot.
/// ///
/// Emergency slots are allocated late when the register's constraint solver needs extra space /// Emergency slots are allocated late when the register's constraint solver needs extra space
/// to shuffle registers around. The are only used briefly, and can be reused. /// to shuffle registers around. They are only used briefly, and can be reused.
EmergencySlot, EmergencySlot,
} }

View File

@@ -1,7 +1,9 @@
//! The `Encoding` struct. //! The `Encoding` struct.
use binemit::CodeOffset; use binemit::CodeOffset;
use ir::{Function, Inst};
use isa::constraints::{BranchRange, RecipeConstraints}; use isa::constraints::{BranchRange, RecipeConstraints};
use regalloc::RegDiversions;
use std::fmt; use std::fmt;
/// Bits needed to encode an instruction as binary machine code. /// Bits needed to encode an instruction as binary machine code.
@@ -78,12 +80,24 @@ impl fmt::Display for DisplayEncoding {
} }
} }
type SizeCalculatorFn = fn(&RecipeSizing, Inst, &RegDiversions, &Function) -> u8;
/// Returns the base size of the Recipe, assuming it's fixed. This is the default for most
/// encodings; others can be variable and longer than this base size, depending on the registers
/// they're using and use a different function, specific per platform.
pub fn base_size(sizing: &RecipeSizing, _: Inst, _2: &RegDiversions, _3: &Function) -> u8 {
sizing.base_size
}
/// Code size information for an encoding recipe. /// Code size information for an encoding recipe.
/// ///
/// All encoding recipes correspond to an exact instruction size. /// All encoding recipes correspond to an exact instruction size.
pub struct RecipeSizing { pub struct RecipeSizing {
/// Size in bytes of instructions encoded with this recipe. /// Size in bytes of instructions encoded with this recipe.
pub bytes: u8, pub base_size: u8,
/// Method computing the real instruction's size, given inputs and outputs.
pub compute_size: SizeCalculatorFn,
/// Allowed branch range in this recipe, if any. /// Allowed branch range in this recipe, if any.
/// ///
@@ -118,13 +132,20 @@ impl EncInfo {
} }
} }
/// Get the exact size in bytes of instructions encoded with `enc`. /// Get the precise size in bytes of instructions encoded with `enc`.
/// ///
/// Returns 0 for illegal encodings. /// Returns 0 for illegal encodings.
pub fn bytes(&self, enc: Encoding) -> CodeOffset { pub fn byte_size(
self.sizing &self,
.get(enc.recipe()) enc: Encoding,
.map_or(0, |s| CodeOffset::from(s.bytes)) inst: Inst,
divert: &RegDiversions,
func: &Function,
) -> CodeOffset {
self.sizing.get(enc.recipe()).map_or(0, |s| {
let compute_size = s.compute_size;
CodeOffset::from(compute_size(&s, inst, divert, func))
})
} }
/// Get the branch range that is supported by `enc`, if any. /// Get the branch range that is supported by `enc`, if any.

View File

@@ -47,7 +47,7 @@
//! concurrent function compilations. //! concurrent function compilations.
pub use isa::constraints::{BranchRange, ConstraintKind, OperandConstraint, RecipeConstraints}; pub use isa::constraints::{BranchRange, ConstraintKind, OperandConstraint, RecipeConstraints};
pub use isa::encoding::{EncInfo, Encoding}; pub use isa::encoding::{base_size, EncInfo, Encoding};
pub use isa::registers::{regs_overlap, RegClass, RegClassIndex, RegInfo, RegUnit}; pub use isa::registers::{regs_overlap, RegClass, RegClassIndex, RegInfo, RegUnit};
pub use isa::stack::{StackBase, StackBaseMask, StackRef}; pub use isa::stack::{StackBase, StackBaseMask, StackRef};
@@ -204,7 +204,7 @@ pub trait TargetIsa: fmt::Display {
/// Get a data structure describing the registers in this ISA. /// Get a data structure describing the registers in this ISA.
fn register_info(&self) -> RegInfo; fn register_info(&self) -> RegInfo;
/// Returns an iterartor over legal encodings for the instruction. /// Returns an iterator over legal encodings for the instruction.
fn legal_encodings<'a>( fn legal_encodings<'a>(
&'a self, &'a self,
func: &'a ir::Function, func: &'a ir::Function,

View File

@@ -5,7 +5,7 @@ use ir;
use isa; use isa;
use isa::constraints::*; use isa::constraints::*;
use isa::enc_tables::*; use isa::enc_tables::*;
use isa::encoding::RecipeSizing; use isa::encoding::{base_size, RecipeSizing};
// Include the generated encoding tables: // Include the generated encoding tables:
// - `LEVEL1_RV32` // - `LEVEL1_RV32`

View File

@@ -1,5 +1,6 @@
//! Emitting binary x86 machine code. //! Emitting binary x86 machine code.
use super::enc_tables::{needs_offset, needs_sib_byte};
use super::registers::RU; use super::registers::RU;
use binemit::{bad_encoding, CodeSink, Reloc}; use binemit::{bad_encoding, CodeSink, Reloc};
use ir::condcodes::{CondCode, FloatCC, IntCC}; use ir::condcodes::{CondCode, FloatCC, IntCC};

View File

@@ -5,15 +5,73 @@ use bitset::BitSet;
use cursor::{Cursor, FuncCursor}; use cursor::{Cursor, FuncCursor};
use flowgraph::ControlFlowGraph; use flowgraph::ControlFlowGraph;
use ir::condcodes::IntCC; use ir::condcodes::IntCC;
use ir::{self, InstBuilder}; use ir::{self, Function, Inst, InstBuilder};
use isa; use isa;
use isa::constraints::*; use isa::constraints::*;
use isa::enc_tables::*; use isa::enc_tables::*;
use isa::encoding::base_size;
use isa::encoding::RecipeSizing; use isa::encoding::RecipeSizing;
use isa::RegUnit;
use regalloc::RegDiversions;
include!(concat!(env!("OUT_DIR"), "/encoding-x86.rs")); include!(concat!(env!("OUT_DIR"), "/encoding-x86.rs"));
include!(concat!(env!("OUT_DIR"), "/legalize-x86.rs")); include!(concat!(env!("OUT_DIR"), "/legalize-x86.rs"));
pub fn needs_sib_byte(reg: RegUnit) -> bool {
reg == RU::r12 as RegUnit || reg == RU::rsp as RegUnit
}
pub fn needs_offset(reg: RegUnit) -> bool {
reg == RU::r13 as RegUnit || reg == RU::rbp as RegUnit
}
fn additional_size_if(
op_index: usize,
inst: Inst,
divert: &RegDiversions,
func: &Function,
condition_func: fn(RegUnit) -> bool,
) -> u8 {
let addr_reg = divert.reg(func.dfg.inst_args(inst)[op_index], &func.locations);
if condition_func(addr_reg) {
1
} else {
0
}
}
fn size_plus_maybe_offset_for_in_reg_0(
sizing: &RecipeSizing,
inst: Inst,
divert: &RegDiversions,
func: &Function,
) -> u8 {
sizing.base_size + additional_size_if(0, inst, divert, func, needs_offset)
}
fn size_plus_maybe_offset_for_in_reg_1(
sizing: &RecipeSizing,
inst: Inst,
divert: &RegDiversions,
func: &Function,
) -> u8 {
sizing.base_size + additional_size_if(1, inst, divert, func, needs_offset)
}
fn size_plus_maybe_sib_for_in_reg_0(
sizing: &RecipeSizing,
inst: Inst,
divert: &RegDiversions,
func: &Function,
) -> u8 {
sizing.base_size + additional_size_if(0, inst, divert, func, needs_sib_byte)
}
fn size_plus_maybe_sib_for_in_reg_1(
sizing: &RecipeSizing,
inst: Inst,
divert: &RegDiversions,
func: &Function,
) -> u8 {
sizing.base_size + additional_size_if(1, inst, divert, func, needs_sib_byte)
}
/// Expand the `sdiv` and `srem` instructions using `x86_sdivmodx`. /// Expand the `sdiv` and `srem` instructions using `x86_sdivmodx`.
fn expand_sdivrem( fn expand_sdivrem(
inst: ir::Inst, inst: ir::Inst,

View File

@@ -149,7 +149,8 @@ impl SubTest for TestBinEmit {
if opt_level == OptLevel::Best { if opt_level == OptLevel::Best {
// Get the smallest legal encoding // Get the smallest legal encoding
legal_encodings.min_by_key(|&e| encinfo.bytes(e)) legal_encodings
.min_by_key(|&e| encinfo.byte_size(e, inst, &divert, &func))
} else { } else {
// If not optimizing, just use the first encoding. // If not optimizing, just use the first encoding.
legal_encodings.next() legal_encodings.next()
@@ -204,7 +205,7 @@ impl SubTest for TestBinEmit {
"Inconsistent {} header offset", "Inconsistent {} header offset",
ebb ebb
); );
for (offset, inst, enc_bytes) in func.inst_offsets(ebb, &encinfo) { for (offset, inst, enc_bytes) in func.inst_offsets(&func, ebb, &encinfo) {
assert_eq!(sink.offset, offset); assert_eq!(sink.offset, offset);
sink.text.clear(); sink.text.clear();
let enc = func.encodings[inst]; let enc = func.encodings[inst];