Skip to content

Commit

Permalink
Track regmove instruction during binemit.
Browse files Browse the repository at this point in the history
Register locations can change throughout an EBB. Make sure the
emit_inst() function considers this when encoding instructions and
update the register diversion tracker.
  • Loading branch information
Jakob Stoklund Olesen committed Jul 18, 2017
1 parent 306ef20 commit 2927878
Show file tree
Hide file tree
Showing 14 changed files with 73 additions and 19 deletions.
15 changes: 15 additions & 0 deletions filetests/isa/riscv/regmove.cton
Original file line number Diff line number Diff line change
@@ -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
}
20 changes: 15 additions & 5 deletions lib/cretonne/meta/gen_binemit.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ def gen_recipe(recipe, fmt):
want_outs = any(isinstance(o, RegClass) or isinstance(o, Stack)
for o in recipe.outs)

# Regmove instructions get special treatment.
is_regmove = (recipe.format.name == 'RegMove')

# First unpack the instruction.
with fmt.indented(
'if let InstructionData::{} {{'.format(iform.name),
Expand All @@ -46,7 +49,7 @@ def gen_recipe(recipe, fmt):
fmt.outdented_line('} = func.dfg[inst] {')

# Normalize to an `args` array.
if want_args:
if want_args and not is_regmove:
if iform.has_value_list:
fmt.line('let args = args.as_slice(&func.dfg.value_lists);')
elif nvops == 1:
Expand All @@ -56,11 +59,11 @@ def gen_recipe(recipe, fmt):
# Don't bother with fixed registers.
args = ''
for i, arg in enumerate(recipe.ins):
if isinstance(arg, RegClass):
if isinstance(arg, RegClass) and not is_regmove:
v = 'in_reg{}'.format(i)
args += ', ' + v
fmt.line(
'let {} = func.locations[args[{}]].unwrap_reg();'
'let {} = divert.reg(args[{}], &func.locations);'
.format(v, i))
elif isinstance(arg, Stack):
v = 'in_ss{}'.format(i)
Expand Down Expand Up @@ -93,6 +96,11 @@ def gen_recipe(recipe, fmt):
'let {} = func.locations[results[{}]].unwrap_stack();'
.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
# that. Otherwise cal a recipe function in the target ISA's binemit
# module.
Expand All @@ -118,13 +126,15 @@ def gen_isa(isa, fmt):
# No encoding recipes: Emit a stub.
with fmt.indented(
'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)')
else:
fmt.line('#[allow(unused_variables, unreachable_code)]')
with fmt.indented(
'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();')
with fmt.indented('match func.encodings[inst].recipe() {', '}'):
for i, recipe in enumerate(isa.all_recipes):
Expand Down
8 changes: 5 additions & 3 deletions lib/cretonne/src/binemit/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub use self::relaxation::relax_branches;
pub use self::memorysink::{MemoryCodeSink, RelocSink};

use ir::{Ebb, FuncRef, JumpTable, Function, Inst};
use regalloc::RegDiversions;

/// Offset in bytes from the beginning of the function.
///
Expand Down Expand Up @@ -64,13 +65,14 @@ pub fn bad_encoding(func: &Function, inst: Inst) -> ! {
/// appropriate instruction emitter.
pub fn emit_function<CS, EI>(func: &Function, emit_inst: EI, sink: &mut CS)
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() {
divert.clear();
assert_eq!(func.offsets[ebb], sink.offset());
for inst in func.layout.ebb_insts(ebb) {
emit_inst(func, inst, sink);
emit_inst(func, inst, &mut divert, sink);
}

}
}
1 change: 1 addition & 0 deletions lib/cretonne/src/isa/arm32/binemit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
use binemit::{CodeSink, bad_encoding};
use ir::{Function, Inst};
use regalloc::RegDiversions;

include!(concat!(env!("OUT_DIR"), "/binemit-arm32.rs"));

Expand Down
8 changes: 6 additions & 2 deletions lib/cretonne/src/isa/arm32/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,12 @@ impl TargetIsa for Isa {
abi::allocatable_registers(func)
}

fn emit_inst(&self, func: &ir::Function, inst: ir::Inst, sink: &mut CodeSink) {
binemit::emit_inst(func, inst, sink)
fn emit_inst(&self,
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) {
Expand Down
1 change: 1 addition & 0 deletions lib/cretonne/src/isa/arm64/binemit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
use binemit::{CodeSink, bad_encoding};
use ir::{Function, Inst};
use regalloc::RegDiversions;

include!(concat!(env!("OUT_DIR"), "/binemit-arm64.rs"));

Expand Down
8 changes: 6 additions & 2 deletions lib/cretonne/src/isa/arm64/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,12 @@ impl TargetIsa for Isa {
abi::allocatable_registers(func)
}

fn emit_inst(&self, func: &ir::Function, inst: ir::Inst, sink: &mut CodeSink) {
binemit::emit_inst(func, inst, sink)
fn emit_inst(&self,
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) {
Expand Down
1 change: 1 addition & 0 deletions lib/cretonne/src/isa/intel/binemit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use binemit::{CodeSink, Reloc, bad_encoding};
use ir::{Function, Inst, InstructionData};
use isa::RegUnit;
use regalloc::RegDiversions;

include!(concat!(env!("OUT_DIR"), "/binemit-intel.rs"));

Expand Down
8 changes: 6 additions & 2 deletions lib/cretonne/src/isa/intel/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,12 @@ impl TargetIsa for Isa {
abi::allocatable_registers(func, &self.shared_flags)
}

fn emit_inst(&self, func: &ir::Function, inst: ir::Inst, sink: &mut CodeSink) {
binemit::emit_inst(func, inst, sink)
fn emit_inst(&self,
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) {
Expand Down
6 changes: 5 additions & 1 deletion lib/cretonne/src/isa/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
/// 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.
///
Expand Down
1 change: 1 addition & 0 deletions lib/cretonne/src/isa/riscv/binemit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use binemit::{CodeSink, Reloc, bad_encoding};
use ir::{Function, Inst, InstructionData};
use isa::RegUnit;
use predicates::is_signed_int;
use regalloc::RegDiversions;

include!(concat!(env!("OUT_DIR"), "/binemit-riscv.rs"));

Expand Down
8 changes: 6 additions & 2 deletions lib/cretonne/src/isa/riscv/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,12 @@ impl TargetIsa for Isa {
abi::allocatable_registers(func, &self.isa_flags)
}

fn emit_inst(&self, func: &ir::Function, inst: ir::Inst, sink: &mut CodeSink) {
binemit::emit_inst(func, inst, sink)
fn emit_inst(&self,
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) {
Expand Down
5 changes: 4 additions & 1 deletion src/filetest/binemit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use cretonne::binemit;
use cretonne::ir;
use cretonne::ir::entities::AnyEntity;
use cretonne::isa::TargetIsa;
use cretonne::regalloc::RegDiversions;
use cton_reader::TestCommand;
use filetest::subtest::{SubTest, Context, Result};
use utils::{match_directive, pretty_error};
Expand Down Expand Up @@ -147,7 +148,9 @@ impl SubTest for TestBinEmit {

// Now emit all instructions.
let mut sink = TextSink::new(isa);
let mut divert = RegDiversions::new();
for ebb in func.layout.ebbs() {
divert.clear();
// Correct header offsets should have been computed by `relax_branches()`.
assert_eq!(sink.offset,
func.offsets[ebb],
Expand All @@ -160,7 +163,7 @@ impl SubTest for TestBinEmit {
// Send legal encodings into the emitter.
if enc.is_legal() {
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;
// Verify the encoding recipe sizes against the ISAs emit_inst implementation.
assert_eq!(emitted,
Expand Down
2 changes: 1 addition & 1 deletion src/filetest/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ impl SubTest for TestCompile {
// Finally verify that the returned code size matches the emitted bytes.
let mut sink = SizeSink { offset: 0 };
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);

if sink.offset != code_size {
Expand Down

0 comments on commit 2927878

Please sign in to comment.