Add a regmove instruction.

This will be used to locally change the register locations of values in
order to satisfy instruction constraints.
This commit is contained in:
Jakob Stoklund Olesen
2017-05-02 11:32:12 -07:00
parent 6fe4aa2f8d
commit 8cd67f08a9
11 changed files with 114 additions and 7 deletions

View File

@@ -583,6 +583,11 @@ allocation pass and beyond.
.. autoinst:: spill .. autoinst:: spill
.. autoinst:: fill .. autoinst:: fill
Register values can be temporarily diverted to other registers by the
:inst:`regmove` instruction.
.. autoinst:: regmove
Vector operations Vector operations
----------------- -----------------

View File

@@ -155,3 +155,18 @@ ebb0(v1: i32):
; nextln: store $v2, $v1 ; nextln: store $v2, $v1
; nextln: store aligned $v3, $v1+12 ; nextln: store aligned $v3, $v1+12
; nextln: store notrap 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: }

View File

@@ -9,7 +9,7 @@ from __future__ import absolute_import
from cdsl.formats import InstructionFormat from cdsl.formats import InstructionFormat
from cdsl.operands import VALUE, VARIABLE_ARGS from cdsl.operands import VALUE, VARIABLE_ARGS
from .immediates import imm64, uimm8, ieee32, ieee64, offset32, uoffset32 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 from .entities import ebb, sig_ref, func_ref, jump_table, stack_slot
Nullary = InstructionFormat() Nullary = InstructionFormat()
@@ -57,5 +57,7 @@ StackStore = InstructionFormat(VALUE, stack_slot, offset32)
HeapLoad = InstructionFormat(VALUE, uoffset32) HeapLoad = InstructionFormat(VALUE, uoffset32)
HeapStore = InstructionFormat(VALUE, VALUE, uoffset32) HeapStore = InstructionFormat(VALUE, VALUE, uoffset32)
RegMove = InstructionFormat(VALUE, ('src', regunit), ('dst', regunit))
# Finally extract the names of global variables in this module. # Finally extract the names of global variables in this module.
InstructionFormat.extract_names(globals()) InstructionFormat.extract_names(globals())

View File

@@ -96,3 +96,9 @@ memflags = ImmediateKind(
'memflags', 'memflags',
'Memory operation flags', 'Memory operation flags',
default_member='flags', rust_type='MemFlags') 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')

View File

@@ -10,7 +10,7 @@ from cdsl.typevar import TypeVar
from cdsl.instructions import Instruction, InstructionGroup from cdsl.instructions import Instruction, InstructionGroup
from base.types import i8, f32, f64, b1 from base.types import i8, f32, f64, b1
from base.immediates import imm64, uimm8, ieee32, ieee64, offset32, uoffset32 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 from base import entities
import base.formats # noqa import base.formats # noqa
@@ -467,6 +467,23 @@ fill = Instruction(
""", """,
ins=x, outs=a) 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 # Vector operations

View File

@@ -9,6 +9,7 @@ use ir::{Opcode, Type, Inst, Value, Ebb, JumpTable, SigRef, FuncRef, StackSlot,
MemFlags}; MemFlags};
use ir::immediates::{Imm64, Uimm8, Ieee32, Ieee64, Offset32, Uoffset32}; use ir::immediates::{Imm64, Uimm8, Ieee32, Ieee64, Offset32, Uoffset32};
use ir::condcodes::{IntCC, FloatCC}; use ir::condcodes::{IntCC, FloatCC};
use isa::RegUnit;
/// Base trait for instruction builders. /// Base trait for instruction builders.
/// ///

View File

@@ -687,7 +687,7 @@ impl<'a> fmt::Display for DisplayInst<'a> {
} else { } else {
write!(f, "{}.{}", inst.opcode(), typevar)?; write!(f, "{}.{}", inst.opcode(), typevar)?;
} }
write_operands(f, dfg, self.1) write_operands(f, dfg, None, self.1)
} }
} }

View File

@@ -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::immediates::{Imm64, Uimm8, Ieee32, Ieee64, Offset32, Uoffset32};
use ir::condcodes::*; use ir::condcodes::*;
use ir::types; use ir::types;
use isa::RegUnit;
use entity_list; use entity_list;
use ref_slice::{ref_slice, ref_slice_mut}; use ref_slice::{ref_slice, ref_slice_mut};
@@ -203,6 +204,12 @@ pub enum InstructionData {
args: [Value; 2], args: [Value; 2],
offset: Offset32, 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 /// A variable list of `Value` operands used for function call arguments and passing arguments to

View File

@@ -284,7 +284,8 @@ impl<'a> Verifier<'a> {
&HeapLoad { .. } | &HeapLoad { .. } |
&HeapStore { .. } | &HeapStore { .. } |
&Load { .. } | &Load { .. } |
&Store { .. } => {} &Store { .. } |
&RegMove { .. } => {}
} }
Ok(()) Ok(())

View File

@@ -225,12 +225,16 @@ fn write_instruction(w: &mut Write,
None => write!(w, "{}", opcode)?, None => write!(w, "{}", opcode)?,
} }
write_operands(w, &func.dfg, inst)?; write_operands(w, &func.dfg, isa, inst)?;
writeln!(w, "") writeln!(w, "")
} }
/// Write the operands of `inst` to `w` with a prepended space. /// 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; let pool = &dfg.value_lists;
use ir::instructions::InstructionData::*; use ir::instructions::InstructionData::*;
match dfg[inst] { match dfg[inst] {
@@ -321,6 +325,19 @@ pub fn write_operands(w: &mut Write, dfg: &DataFlowGraph, inst: Inst) -> Result
offset, offset,
.. ..
} => write!(w, "{} {}, {}{}", flags, args[0], args[1], 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)
}
}
} }
} }

View File

@@ -16,7 +16,7 @@ use cretonne::ir::types::VOID;
use cretonne::ir::immediates::{Imm64, Offset32, Uoffset32, Ieee32, Ieee64}; use cretonne::ir::immediates::{Imm64, Offset32, Uoffset32, Ieee32, Ieee64};
use cretonne::ir::entities::AnyEntity; use cretonne::ir::entities::AnyEntity;
use cretonne::ir::instructions::{InstructionFormat, InstructionData, VariableArgs}; 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 cretonne::settings::{self, Configurable};
use testfile::{TestFile, Details, Comment}; use testfile::{TestFile, Details, Comment};
use error::{Location, Error, Result}; 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<RegUnit> {
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. /// Parse a list of test commands.
pub fn parse_test_commands(&mut self) -> Vec<TestCommand<'a>> { pub fn parse_test_commands(&mut self) -> Vec<TestCommand<'a>> {
let mut list = Vec::new(); let mut list = Vec::new();
@@ -1664,6 +1687,19 @@ impl<'a> Parser<'a> {
offset, 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) Ok(idata)
} }