Track regmove instruction during binemit.

Register locations can change throughout an EBB. Make sure the
emit_inst() function considers this when encoding instructions and
update the register diversion tracker.
This commit is contained in:
Jakob Stoklund Olesen
2017-07-18 12:52:53 -07:00
parent c4db4c124b
commit 53d9232d39
14 changed files with 73 additions and 19 deletions

View File

@@ -0,0 +1,15 @@
; Test tracking of register moves.
test binemit
isa riscv
function %regmoves(i32 link [%x1]) -> i32 link [%x1] {
ebb0(v9999: i32):
[-,%x10] v1 = iconst.i32 1
[-,%x7] v2 = iadd_imm v1, 1000 ; bin: 3e850393
regmove v1, %x10 -> %x11 ; bin: 00050593
[-,%x7] v3 = iadd_imm v1, 1000 ; bin: 3e858393
regmove v1, %x11 -> %x10 ; bin: 00058513
[-,%x7] v4 = iadd_imm v1, 1000 ; bin: 3e850393
return v9999
}

View File

@@ -10,6 +10,7 @@ use cretonne::binemit;
use cretonne::ir; use cretonne::ir;
use cretonne::ir::entities::AnyEntity; use cretonne::ir::entities::AnyEntity;
use cretonne::isa::TargetIsa; use cretonne::isa::TargetIsa;
use cretonne::regalloc::RegDiversions;
use cton_reader::TestCommand; use cton_reader::TestCommand;
use filetest::subtest::{SubTest, Context, Result}; use filetest::subtest::{SubTest, Context, Result};
use utils::{match_directive, pretty_error}; use utils::{match_directive, pretty_error};
@@ -147,7 +148,9 @@ impl SubTest for TestBinEmit {
// Now emit all instructions. // Now emit all instructions.
let mut sink = TextSink::new(isa); let mut sink = TextSink::new(isa);
let mut divert = RegDiversions::new();
for ebb in func.layout.ebbs() { for ebb in func.layout.ebbs() {
divert.clear();
// Correct header offsets should have been computed by `relax_branches()`. // Correct header offsets should have been computed by `relax_branches()`.
assert_eq!(sink.offset, assert_eq!(sink.offset,
func.offsets[ebb], func.offsets[ebb],
@@ -160,7 +163,7 @@ impl SubTest for TestBinEmit {
// Send legal encodings into the emitter. // Send legal encodings into the emitter.
if enc.is_legal() { if enc.is_legal() {
let before = sink.offset; let before = sink.offset;
isa.emit_inst(&func, inst, &mut sink); isa.emit_inst(&func, inst, &mut divert, &mut sink);
let emitted = sink.offset - before; let emitted = sink.offset - before;
// Verify the encoding recipe sizes against the ISAs emit_inst implementation. // Verify the encoding recipe sizes against the ISAs emit_inst implementation.
assert_eq!(emitted, assert_eq!(emitted,

View File

@@ -52,7 +52,7 @@ impl SubTest for TestCompile {
// Finally verify that the returned code size matches the emitted bytes. // Finally verify that the returned code size matches the emitted bytes.
let mut sink = SizeSink { offset: 0 }; let mut sink = SizeSink { offset: 0 };
binemit::emit_function(&comp_ctx.func, binemit::emit_function(&comp_ctx.func,
|func, inst, sink| isa.emit_inst(func, inst, sink), |func, inst, div, sink| isa.emit_inst(func, inst, div, sink),
&mut sink); &mut sink);
if sink.offset != code_size { if sink.offset != code_size {

View File

@@ -31,6 +31,9 @@ def gen_recipe(recipe, fmt):
want_outs = any(isinstance(o, RegClass) or isinstance(o, Stack) want_outs = any(isinstance(o, RegClass) or isinstance(o, Stack)
for o in recipe.outs) for o in recipe.outs)
# Regmove instructions get special treatment.
is_regmove = (recipe.format.name == 'RegMove')
# First unpack the instruction. # First unpack the instruction.
with fmt.indented( with fmt.indented(
'if let InstructionData::{} {{'.format(iform.name), 'if let InstructionData::{} {{'.format(iform.name),
@@ -46,7 +49,7 @@ def gen_recipe(recipe, fmt):
fmt.outdented_line('} = func.dfg[inst] {') fmt.outdented_line('} = func.dfg[inst] {')
# Normalize to an `args` array. # Normalize to an `args` array.
if want_args: if want_args and not is_regmove:
if iform.has_value_list: if iform.has_value_list:
fmt.line('let args = args.as_slice(&func.dfg.value_lists);') fmt.line('let args = args.as_slice(&func.dfg.value_lists);')
elif nvops == 1: elif nvops == 1:
@@ -56,11 +59,11 @@ def gen_recipe(recipe, fmt):
# Don't bother with fixed registers. # Don't bother with fixed registers.
args = '' args = ''
for i, arg in enumerate(recipe.ins): for i, arg in enumerate(recipe.ins):
if isinstance(arg, RegClass): if isinstance(arg, RegClass) and not is_regmove:
v = 'in_reg{}'.format(i) v = 'in_reg{}'.format(i)
args += ', ' + v args += ', ' + v
fmt.line( fmt.line(
'let {} = func.locations[args[{}]].unwrap_reg();' 'let {} = divert.reg(args[{}], &func.locations);'
.format(v, i)) .format(v, i))
elif isinstance(arg, Stack): elif isinstance(arg, Stack):
v = 'in_ss{}'.format(i) v = 'in_ss{}'.format(i)
@@ -93,6 +96,11 @@ def gen_recipe(recipe, fmt):
'let {} = func.locations[results[{}]].unwrap_stack();' 'let {} = func.locations[results[{}]].unwrap_stack();'
.format(v, i)) .format(v, i))
# Special handling for regmove instructions. Update the register
# diversion tracker.
if recipe.format.name == 'RegMove':
fmt.line('divert.regmove(arg, src, dst);')
# Call hand-written code. If the recipe contains a code snippet, use # Call hand-written code. If the recipe contains a code snippet, use
# that. Otherwise cal a recipe function in the target ISA's binemit # that. Otherwise cal a recipe function in the target ISA's binemit
# module. # module.
@@ -118,13 +126,15 @@ def gen_isa(isa, fmt):
# No encoding recipes: Emit a stub. # No encoding recipes: Emit a stub.
with fmt.indented( with fmt.indented(
'pub fn emit_inst<CS: CodeSink + ?Sized>' 'pub fn emit_inst<CS: CodeSink + ?Sized>'
'(func: &Function, inst: Inst, _sink: &mut CS) {', '}'): '(func: &Function, inst: Inst, '
'_divert: &mut RegDiversions, _sink: &mut CS) {', '}'):
fmt.line('bad_encoding(func, inst)') fmt.line('bad_encoding(func, inst)')
else: else:
fmt.line('#[allow(unused_variables, unreachable_code)]') fmt.line('#[allow(unused_variables, unreachable_code)]')
with fmt.indented( with fmt.indented(
'pub fn emit_inst<CS: CodeSink + ?Sized>' 'pub fn emit_inst<CS: CodeSink + ?Sized>'
'(func: &Function, inst: Inst, sink: &mut CS) {', '}'): '(func: &Function, inst: Inst, '
'divert: &mut RegDiversions, sink: &mut CS) {', '}'):
fmt.line('let bits = func.encodings[inst].bits();') fmt.line('let bits = func.encodings[inst].bits();')
with fmt.indented('match func.encodings[inst].recipe() {', '}'): with fmt.indented('match func.encodings[inst].recipe() {', '}'):
for i, recipe in enumerate(isa.all_recipes): for i, recipe in enumerate(isa.all_recipes):

View File

@@ -10,6 +10,7 @@ pub use self::relaxation::relax_branches;
pub use self::memorysink::{MemoryCodeSink, RelocSink}; pub use self::memorysink::{MemoryCodeSink, RelocSink};
use ir::{Ebb, FuncRef, JumpTable, Function, Inst}; use ir::{Ebb, FuncRef, JumpTable, Function, Inst};
use regalloc::RegDiversions;
/// Offset in bytes from the beginning of the function. /// Offset in bytes from the beginning of the function.
/// ///
@@ -64,13 +65,14 @@ pub fn bad_encoding(func: &Function, inst: Inst) -> ! {
/// appropriate instruction emitter. /// appropriate instruction emitter.
pub fn emit_function<CS, EI>(func: &Function, emit_inst: EI, sink: &mut CS) pub fn emit_function<CS, EI>(func: &Function, emit_inst: EI, sink: &mut CS)
where CS: CodeSink, where CS: CodeSink,
EI: Fn(&Function, Inst, &mut CS) EI: Fn(&Function, Inst, &mut RegDiversions, &mut CS)
{ {
let mut divert = RegDiversions::new();
for ebb in func.layout.ebbs() { for ebb in func.layout.ebbs() {
divert.clear();
assert_eq!(func.offsets[ebb], sink.offset()); assert_eq!(func.offsets[ebb], sink.offset());
for inst in func.layout.ebb_insts(ebb) { for inst in func.layout.ebb_insts(ebb) {
emit_inst(func, inst, sink); emit_inst(func, inst, &mut divert, sink);
} }
} }
} }

View File

@@ -2,6 +2,7 @@
use binemit::{CodeSink, bad_encoding}; use binemit::{CodeSink, bad_encoding};
use ir::{Function, Inst}; use ir::{Function, Inst};
use regalloc::RegDiversions;
include!(concat!(env!("OUT_DIR"), "/binemit-arm32.rs")); include!(concat!(env!("OUT_DIR"), "/binemit-arm32.rs"));

View File

@@ -91,8 +91,12 @@ impl TargetIsa for Isa {
abi::allocatable_registers(func) abi::allocatable_registers(func)
} }
fn emit_inst(&self, func: &ir::Function, inst: ir::Inst, sink: &mut CodeSink) { fn emit_inst(&self,
binemit::emit_inst(func, inst, sink) func: &ir::Function,
inst: ir::Inst,
divert: &mut regalloc::RegDiversions,
sink: &mut CodeSink) {
binemit::emit_inst(func, inst, divert, sink)
} }
fn emit_function(&self, func: &ir::Function, sink: &mut MemoryCodeSink) { fn emit_function(&self, func: &ir::Function, sink: &mut MemoryCodeSink) {

View File

@@ -2,6 +2,7 @@
use binemit::{CodeSink, bad_encoding}; use binemit::{CodeSink, bad_encoding};
use ir::{Function, Inst}; use ir::{Function, Inst};
use regalloc::RegDiversions;
include!(concat!(env!("OUT_DIR"), "/binemit-arm64.rs")); include!(concat!(env!("OUT_DIR"), "/binemit-arm64.rs"));

View File

@@ -84,8 +84,12 @@ impl TargetIsa for Isa {
abi::allocatable_registers(func) abi::allocatable_registers(func)
} }
fn emit_inst(&self, func: &ir::Function, inst: ir::Inst, sink: &mut CodeSink) { fn emit_inst(&self,
binemit::emit_inst(func, inst, sink) func: &ir::Function,
inst: ir::Inst,
divert: &mut regalloc::RegDiversions,
sink: &mut CodeSink) {
binemit::emit_inst(func, inst, divert, sink)
} }
fn emit_function(&self, func: &ir::Function, sink: &mut MemoryCodeSink) { fn emit_function(&self, func: &ir::Function, sink: &mut MemoryCodeSink) {

View File

@@ -3,6 +3,7 @@
use binemit::{CodeSink, Reloc, bad_encoding}; use binemit::{CodeSink, Reloc, bad_encoding};
use ir::{Function, Inst, InstructionData}; use ir::{Function, Inst, InstructionData};
use isa::RegUnit; use isa::RegUnit;
use regalloc::RegDiversions;
include!(concat!(env!("OUT_DIR"), "/binemit-intel.rs")); include!(concat!(env!("OUT_DIR"), "/binemit-intel.rs"));

View File

@@ -91,8 +91,12 @@ impl TargetIsa for Isa {
abi::allocatable_registers(func, &self.shared_flags) abi::allocatable_registers(func, &self.shared_flags)
} }
fn emit_inst(&self, func: &ir::Function, inst: ir::Inst, sink: &mut CodeSink) { fn emit_inst(&self,
binemit::emit_inst(func, inst, sink) func: &ir::Function,
inst: ir::Inst,
divert: &mut regalloc::RegDiversions,
sink: &mut CodeSink) {
binemit::emit_inst(func, inst, divert, sink)
} }
fn emit_function(&self, func: &ir::Function, sink: &mut MemoryCodeSink) { fn emit_function(&self, func: &ir::Function, sink: &mut MemoryCodeSink) {

View File

@@ -215,7 +215,11 @@ pub trait TargetIsa {
/// ///
/// Note that this will call `put*` methods on the trait object via its vtable which is not the /// Note that this will call `put*` methods on the trait object via its vtable which is not the
/// fastest way of emitting code. /// fastest way of emitting code.
fn emit_inst(&self, func: &ir::Function, inst: ir::Inst, sink: &mut binemit::CodeSink); fn emit_inst(&self,
func: &ir::Function,
inst: ir::Inst,
divert: &mut regalloc::RegDiversions,
sink: &mut binemit::CodeSink);
/// Emit a whole function into memory. /// Emit a whole function into memory.
/// ///

View File

@@ -4,6 +4,7 @@ use binemit::{CodeSink, Reloc, bad_encoding};
use ir::{Function, Inst, InstructionData}; use ir::{Function, Inst, InstructionData};
use isa::RegUnit; use isa::RegUnit;
use predicates::is_signed_int; use predicates::is_signed_int;
use regalloc::RegDiversions;
include!(concat!(env!("OUT_DIR"), "/binemit-riscv.rs")); include!(concat!(env!("OUT_DIR"), "/binemit-riscv.rs"));

View File

@@ -91,8 +91,12 @@ impl TargetIsa for Isa {
abi::allocatable_registers(func, &self.isa_flags) abi::allocatable_registers(func, &self.isa_flags)
} }
fn emit_inst(&self, func: &ir::Function, inst: ir::Inst, sink: &mut CodeSink) { fn emit_inst(&self,
binemit::emit_inst(func, inst, sink) func: &ir::Function,
inst: ir::Inst,
divert: &mut regalloc::RegDiversions,
sink: &mut CodeSink) {
binemit::emit_inst(func, inst, divert, sink)
} }
fn emit_function(&self, func: &ir::Function, sink: &mut MemoryCodeSink) { fn emit_function(&self, func: &ir::Function, sink: &mut MemoryCodeSink) {