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

@@ -32,6 +32,7 @@ use cursor::{Cursor, FuncCursor};
use ir::{Function, InstructionData, Opcode};
use isa::{EncInfo, TargetIsa};
use iterators::IteratorExtras;
use regalloc::RegDiversions;
use timing;
use CodegenResult;
@@ -51,6 +52,7 @@ pub fn relax_branches(func: &mut Function, isa: &TargetIsa) -> CodegenResult<Cod
fallthroughs(func);
let mut offset = 0;
let mut divert = RegDiversions::new();
// The relaxation algorithm iterates to convergence.
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
let mut cur = FuncCursor::new(func);
while let Some(ebb) = cur.next_ebb() {
divert.clear();
// Record the offset for `ebb` and make sure we iterate until offsets are stable.
if cur.func.offsets[ebb] != offset {
debug_assert!(
@@ -72,8 +76,9 @@ pub fn relax_branches(func: &mut Function, isa: &TargetIsa) -> CodegenResult<Cod
}
while let Some(inst) = cur.next_inst() {
divert.apply(&cur.func.dfg[inst]);
let enc = cur.func.encodings[inst];
let size = encinfo.bytes(enc);
// See if this might be a branch that is out of range.
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)
&& (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;
}
}
}
offset += size;
offset += encinfo.byte_size(enc, inst, &divert, &cur.func);
}
}
}
@@ -141,6 +147,7 @@ fn fallthroughs(func: &mut Function) {
/// left.
fn relax_branch(
cur: &mut FuncCursor,
divert: &RegDiversions,
offset: CodeOffset,
dest_offset: CodeOffset,
encinfo: &EncInfo,
@@ -181,7 +188,7 @@ fn relax_branch(
}
}) {
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

View File

@@ -48,7 +48,7 @@ pub fn shrink_instructions(func: &mut Function, isa: &TargetIsa) {
let best_enc = isa
.legal_encodings(func, &func.dfg[inst], ctrl_type)
.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();
if best_enc != enc {
@@ -59,8 +59,8 @@ pub fn shrink_instructions(func: &mut Function, isa: &TargetIsa) {
encinfo.display(enc),
encinfo.display(best_enc),
func.dfg.display_inst(inst, isa),
encinfo.bytes(enc),
encinfo.bytes(best_enc)
encinfo.byte_size(enc, inst, &divert, &func),
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)?;
self.verify_if(isa)?;
self.verify_locations_if(isa)?;
Ok(code_size)
}
}

View File

@@ -14,6 +14,7 @@ use ir::{
use ir::{EbbOffsets, InstEncodings, SourceLocs, StackSlots, ValueLocations};
use ir::{JumpTableOffsets, JumpTables};
use isa::{EncInfo, Encoding, Legalize, TargetIsa};
use regalloc::RegDiversions;
use settings::CallConv;
use std::fmt;
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
/// `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!(
!self.offsets.is_empty(),
"Code layout must be computed first"
);
InstOffsetIter {
encinfo: encinfo.clone(),
func,
divert: RegDiversions::new(),
encodings: &self.encodings,
offset: self.offsets[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)`.
pub struct InstOffsetIter<'a> {
encinfo: EncInfo,
divert: RegDiversions,
func: &'a Function,
encodings: &'a InstEncodings,
offset: CodeOffset,
iter: ir::layout::Insts<'a>,
@@ -236,10 +246,13 @@ impl<'a> Iterator for InstOffsetIter<'a> {
fn next(&mut self) -> Option<Self::Item> {
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;
self.offset += size;
(offset, inst, size)
self.offset += byte_size;
(offset, inst, byte_size)
})
}
}

View File

@@ -63,7 +63,7 @@ pub enum StackSlotKind {
/// An emergency spill slot.
///
/// 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,
}

View File

@@ -1,7 +1,9 @@
//! The `Encoding` struct.
use binemit::CodeOffset;
use ir::{Function, Inst};
use isa::constraints::{BranchRange, RecipeConstraints};
use regalloc::RegDiversions;
use std::fmt;
/// 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.
///
/// All encoding recipes correspond to an exact instruction size.
pub struct RecipeSizing {
/// 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.
///
@@ -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.
pub fn bytes(&self, enc: Encoding) -> CodeOffset {
self.sizing
.get(enc.recipe())
.map_or(0, |s| CodeOffset::from(s.bytes))
pub fn byte_size(
&self,
enc: Encoding,
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.

View File

@@ -47,7 +47,7 @@
//! concurrent function compilations.
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::stack::{StackBase, StackBaseMask, StackRef};
@@ -204,7 +204,7 @@ pub trait TargetIsa: fmt::Display {
/// Get a data structure describing the registers in this ISA.
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>(
&'a self,
func: &'a ir::Function,

View File

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

View File

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

View File

@@ -5,15 +5,73 @@ use bitset::BitSet;
use cursor::{Cursor, FuncCursor};
use flowgraph::ControlFlowGraph;
use ir::condcodes::IntCC;
use ir::{self, InstBuilder};
use ir::{self, Function, Inst, InstBuilder};
use isa;
use isa::constraints::*;
use isa::enc_tables::*;
use isa::encoding::base_size;
use isa::encoding::RecipeSizing;
use isa::RegUnit;
use regalloc::RegDiversions;
include!(concat!(env!("OUT_DIR"), "/encoding-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`.
fn expand_sdivrem(
inst: ir::Inst,