diff --git a/docs/langref.rst b/docs/langref.rst index 008f2a74b1..e6a0e2b3d5 100644 --- a/docs/langref.rst +++ b/docs/langref.rst @@ -583,6 +583,11 @@ allocation pass and beyond. .. autoinst:: spill .. autoinst:: fill +Register values can be temporarily diverted to other registers by the +:inst:`regmove` instruction. + +.. autoinst:: regmove + Vector operations ----------------- diff --git a/filetests/parser/tiny.cton b/filetests/parser/tiny.cton index 87140d9aad..5ad2f7a39d 100644 --- a/filetests/parser/tiny.cton +++ b/filetests/parser/tiny.cton @@ -155,3 +155,18 @@ ebb0(v1: i32): ; nextln: store $v2, $v1 ; nextln: store aligned $v3, $v1+12 ; nextln: store notrap aligned $v3, $v1-12 + +; Register diversions. +; This test file has no ISA, so we can unly use register unit numbers. +function diversion(i32) { +ebb0(v1: i32): + regmove v1, %10 -> %20 + regmove v1, %20 -> %10 + return +} +; sameln: function diversion(i32) { +; nextln: ebb0($v1: i32): +; nextln: regmove $v1, %10 -> %20 +; nextln: regmove $v1, %20 -> %10 +; nextln: return +; nextln: } diff --git a/lib/cretonne/meta/base/formats.py b/lib/cretonne/meta/base/formats.py index 6e64e0e3f2..368fb13078 100644 --- a/lib/cretonne/meta/base/formats.py +++ b/lib/cretonne/meta/base/formats.py @@ -9,7 +9,7 @@ from __future__ import absolute_import from cdsl.formats import InstructionFormat from cdsl.operands import VALUE, VARIABLE_ARGS from .immediates import imm64, uimm8, ieee32, ieee64, offset32, uoffset32 -from .immediates import intcc, floatcc, memflags +from .immediates import intcc, floatcc, memflags, regunit from .entities import ebb, sig_ref, func_ref, jump_table, stack_slot Nullary = InstructionFormat() @@ -57,5 +57,7 @@ StackStore = InstructionFormat(VALUE, stack_slot, offset32) HeapLoad = InstructionFormat(VALUE, uoffset32) HeapStore = InstructionFormat(VALUE, VALUE, uoffset32) +RegMove = InstructionFormat(VALUE, ('src', regunit), ('dst', regunit)) + # Finally extract the names of global variables in this module. InstructionFormat.extract_names(globals()) diff --git a/lib/cretonne/meta/base/immediates.py b/lib/cretonne/meta/base/immediates.py index 8e643bb9ee..2458b76e70 100644 --- a/lib/cretonne/meta/base/immediates.py +++ b/lib/cretonne/meta/base/immediates.py @@ -96,3 +96,9 @@ memflags = ImmediateKind( 'memflags', 'Memory operation flags', default_member='flags', rust_type='MemFlags') + +#: A register unit in the current target ISA. +regunit = ImmediateKind( + 'regunit', + 'A register unit in the target ISA', + rust_type='RegUnit') diff --git a/lib/cretonne/meta/base/instructions.py b/lib/cretonne/meta/base/instructions.py index 3035f2b98e..b9d2d5a5af 100644 --- a/lib/cretonne/meta/base/instructions.py +++ b/lib/cretonne/meta/base/instructions.py @@ -10,7 +10,7 @@ from cdsl.typevar import TypeVar from cdsl.instructions import Instruction, InstructionGroup from base.types import i8, f32, f64, b1 from base.immediates import imm64, uimm8, ieee32, ieee64, offset32, uoffset32 -from base.immediates import intcc, floatcc, memflags +from base.immediates import intcc, floatcc, memflags, regunit from base import entities import base.formats # noqa @@ -467,6 +467,23 @@ fill = Instruction( """, ins=x, outs=a) +src = Operand('src', regunit) +dst = Operand('dst', regunit) + +regmove = Instruction( + 'regmove', r""" + Temporarily divert ``x`` from ``src`` to ``dst``. + + This instruction moves the location of a value from one register to + another without creating a new SSA value. It is used by the register + allocator to temporarily rearrange register assignments in order to + satisfy instruction constraints. + + The register diversions created by this instruction must be undone + before the value leaves the EBB. At the entry to a new EBB, all live + values must be in their originally assigned registers. + """, + ins=(x, src, dst)) # # Vector operations diff --git a/lib/cretonne/src/ir/builder.rs b/lib/cretonne/src/ir/builder.rs index ae71f754c7..a5a3719299 100644 --- a/lib/cretonne/src/ir/builder.rs +++ b/lib/cretonne/src/ir/builder.rs @@ -9,6 +9,7 @@ use ir::{Opcode, Type, Inst, Value, Ebb, JumpTable, SigRef, FuncRef, StackSlot, MemFlags}; use ir::immediates::{Imm64, Uimm8, Ieee32, Ieee64, Offset32, Uoffset32}; use ir::condcodes::{IntCC, FloatCC}; +use isa::RegUnit; /// Base trait for instruction builders. /// diff --git a/lib/cretonne/src/ir/dfg.rs b/lib/cretonne/src/ir/dfg.rs index 063d7cbbd7..6e33c9d592 100644 --- a/lib/cretonne/src/ir/dfg.rs +++ b/lib/cretonne/src/ir/dfg.rs @@ -687,7 +687,7 @@ impl<'a> fmt::Display for DisplayInst<'a> { } else { write!(f, "{}.{}", inst.opcode(), typevar)?; } - write_operands(f, dfg, self.1) + write_operands(f, dfg, None, self.1) } } diff --git a/lib/cretonne/src/ir/instructions.rs b/lib/cretonne/src/ir/instructions.rs index 9484110d83..0b32929af6 100644 --- a/lib/cretonne/src/ir/instructions.rs +++ b/lib/cretonne/src/ir/instructions.rs @@ -14,6 +14,7 @@ use ir::{Value, Type, Ebb, JumpTable, SigRef, FuncRef, StackSlot, MemFlags}; use ir::immediates::{Imm64, Uimm8, Ieee32, Ieee64, Offset32, Uoffset32}; use ir::condcodes::*; use ir::types; +use isa::RegUnit; use entity_list; use ref_slice::{ref_slice, ref_slice_mut}; @@ -203,6 +204,12 @@ pub enum InstructionData { args: [Value; 2], offset: Offset32, }, + RegMove { + opcode: Opcode, + arg: Value, + src: RegUnit, + dst: RegUnit, + }, } /// A variable list of `Value` operands used for function call arguments and passing arguments to diff --git a/lib/cretonne/src/verifier/mod.rs b/lib/cretonne/src/verifier/mod.rs index 82e95d7f1f..e1568d5bab 100644 --- a/lib/cretonne/src/verifier/mod.rs +++ b/lib/cretonne/src/verifier/mod.rs @@ -284,7 +284,8 @@ impl<'a> Verifier<'a> { &HeapLoad { .. } | &HeapStore { .. } | &Load { .. } | - &Store { .. } => {} + &Store { .. } | + &RegMove { .. } => {} } Ok(()) diff --git a/lib/cretonne/src/write.rs b/lib/cretonne/src/write.rs index 5d46ccb624..af66cc8868 100644 --- a/lib/cretonne/src/write.rs +++ b/lib/cretonne/src/write.rs @@ -225,12 +225,16 @@ fn write_instruction(w: &mut Write, None => write!(w, "{}", opcode)?, } - write_operands(w, &func.dfg, inst)?; + write_operands(w, &func.dfg, isa, inst)?; writeln!(w, "") } /// Write the operands of `inst` to `w` with a prepended space. -pub fn write_operands(w: &mut Write, dfg: &DataFlowGraph, inst: Inst) -> Result { +pub fn write_operands(w: &mut Write, + dfg: &DataFlowGraph, + isa: Option<&TargetIsa>, + inst: Inst) + -> Result { let pool = &dfg.value_lists; use ir::instructions::InstructionData::*; match dfg[inst] { @@ -321,6 +325,19 @@ pub fn write_operands(w: &mut Write, dfg: &DataFlowGraph, inst: Inst) -> Result offset, .. } => write!(w, "{} {}, {}{}", flags, args[0], args[1], offset), + RegMove { arg, src, dst, .. } => { + if let Some(isa) = isa { + let regs = isa.register_info(); + write!(w, + " {}, {} -> {}", + arg, + regs.display_regunit(src), + regs.display_regunit(dst)) + } else { + write!(w, " {}, %{} -> %{}", arg, src, dst) + } + } + } } diff --git a/lib/reader/src/parser.rs b/lib/reader/src/parser.rs index 02ec88196e..b907ee426f 100644 --- a/lib/reader/src/parser.rs +++ b/lib/reader/src/parser.rs @@ -16,7 +16,7 @@ use cretonne::ir::types::VOID; use cretonne::ir::immediates::{Imm64, Offset32, Uoffset32, Ieee32, Ieee64}; use cretonne::ir::entities::AnyEntity; use cretonne::ir::instructions::{InstructionFormat, InstructionData, VariableArgs}; -use cretonne::isa::{self, TargetIsa, Encoding}; +use cretonne::isa::{self, TargetIsa, Encoding, RegUnit}; use cretonne::settings::{self, Configurable}; use testfile::{TestFile, Details, Comment}; use error::{Location, Error, Result}; @@ -551,6 +551,29 @@ impl<'a> Parser<'a> { } } + // Match and consume a register unit either by number `%15` or by name `%rax`. + fn match_regunit(&mut self, isa: Option<&TargetIsa>) -> Result { + if let Some(Token::Name(name)) = self.token() { + self.consume(); + match isa { + Some(isa) => { + isa.register_info() + .parse_regunit(name) + .ok_or_else(|| self.error("invalid register name")) + } + None => { + name.parse() + .map_err(|_| self.error("invalid register number")) + } + } + } else { + match isa { + Some(isa) => err!(self.loc, "Expected {} register unit", isa.name()), + None => err!(self.loc, "Expected register unit number"), + } + } + } + /// Parse a list of test commands. pub fn parse_test_commands(&mut self) -> Vec> { let mut list = Vec::new(); @@ -1664,6 +1687,19 @@ impl<'a> Parser<'a> { offset, } } + InstructionFormat::RegMove => { + let arg = self.match_value("expected SSA value operand")?; + self.match_token(Token::Comma, "expected ',' between operands")?; + let src = self.match_regunit(ctx.unique_isa)?; + self.match_token(Token::Arrow, "expected '->' between register units")?; + let dst = self.match_regunit(ctx.unique_isa)?; + InstructionData::RegMove { + opcode, + arg, + src, + dst, + } + } }; Ok(idata) }