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:
@@ -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
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
|||||||
@@ -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: }
|
||||||
|
|||||||
@@ -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())
|
||||||
|
|||||||
@@ -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')
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -284,7 +284,8 @@ impl<'a> Verifier<'a> {
|
|||||||
&HeapLoad { .. } |
|
&HeapLoad { .. } |
|
||||||
&HeapStore { .. } |
|
&HeapStore { .. } |
|
||||||
&Load { .. } |
|
&Load { .. } |
|
||||||
&Store { .. } => {}
|
&Store { .. } |
|
||||||
|
&RegMove { .. } => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user