Add regspill and regfill instructions.
These are parallels to the existing regmove instruction, but the divert the value to and from a stack slot. Like regmove diversions, this is a temporary diversion that must be local to the EBB.
This commit is contained in:
@@ -700,9 +700,12 @@ allocation pass and beyond.
|
||||
.. autoinst:: fill
|
||||
|
||||
Register values can be temporarily diverted to other registers by the
|
||||
:inst:`regmove` instruction.
|
||||
:inst:`regmove` instruction, and to and from stack slots by :inst:`regspill`
|
||||
and :inst:`regfill`.
|
||||
|
||||
.. autoinst:: regmove
|
||||
.. autoinst:: regspill
|
||||
.. autoinst:: regfill
|
||||
|
||||
Vector operations
|
||||
-----------------
|
||||
|
||||
@@ -166,14 +166,21 @@ ebb0(v1: i32):
|
||||
; Register diversions.
|
||||
; This test file has no ISA, so we can unly use register unit numbers.
|
||||
function %diversion(i32) {
|
||||
ss0 = spill_slot 4
|
||||
|
||||
ebb0(v1: i32):
|
||||
regmove v1, %10 -> %20
|
||||
regmove v1, %20 -> %10
|
||||
regspill v1, %10 -> ss0
|
||||
regfill v1, ss0 -> %10
|
||||
return
|
||||
}
|
||||
; sameln: function %diversion(i32) native {
|
||||
; nextln: ebb0($v1: i32):
|
||||
; nextln: $ss0 = spill_slot 4
|
||||
; check: ebb0($v1: i32):
|
||||
; nextln: regmove $v1, %10 -> %20
|
||||
; nextln: regmove $v1, %20 -> %10
|
||||
; nextln: regspill $v1, %10 -> $ss0
|
||||
; nextln: regfill $v1, $ss0 -> %10
|
||||
; nextln: return
|
||||
; nextln: }
|
||||
|
||||
@@ -58,6 +58,10 @@ StackStore = InstructionFormat(VALUE, stack_slot, offset32)
|
||||
HeapAddr = InstructionFormat(heap, VALUE, uimm32)
|
||||
|
||||
RegMove = InstructionFormat(VALUE, ('src', regunit), ('dst', regunit))
|
||||
RegSpill = InstructionFormat(
|
||||
VALUE, ('src', regunit), ('dst', entities.stack_slot))
|
||||
RegFill = InstructionFormat(
|
||||
VALUE, ('src', entities.stack_slot), ('dst', regunit))
|
||||
|
||||
Trap = InstructionFormat(trapcode)
|
||||
CondTrap = InstructionFormat(VALUE, trapcode)
|
||||
|
||||
@@ -514,6 +514,34 @@ regmove = Instruction(
|
||||
ins=(x, src, dst),
|
||||
other_side_effects=True)
|
||||
|
||||
regspill = Instruction(
|
||||
'regspill', r"""
|
||||
Temporarily divert ``x`` from ``src`` to ``SS``.
|
||||
|
||||
This instruction moves the location of a value from a register to a
|
||||
stack slot without creating a new SSA value. It is used by the register
|
||||
allocator to temporarily rearrange register assignments in order to
|
||||
satisfy instruction constraints.
|
||||
|
||||
See also :inst:`regmove`.
|
||||
""",
|
||||
ins=(x, src, SS),
|
||||
other_side_effects=True)
|
||||
|
||||
|
||||
regfill = Instruction(
|
||||
'regfill', r"""
|
||||
Temporarily divert ``x`` from ``SS`` to ``dst``.
|
||||
|
||||
This instruction moves the location of a value from a stack slot to a
|
||||
register without creating a new SSA value. It is used by the register
|
||||
allocator to temporarily rearrange register assignments in order to
|
||||
satisfy instruction constraints.
|
||||
|
||||
See also :inst:`regmove`.
|
||||
""",
|
||||
ins=(x, SS, dst),
|
||||
other_side_effects=True)
|
||||
#
|
||||
# Vector operations
|
||||
#
|
||||
|
||||
@@ -32,7 +32,7 @@ def gen_recipe(recipe, fmt):
|
||||
for o in recipe.outs)
|
||||
|
||||
# Regmove instructions get special treatment.
|
||||
is_regmove = (recipe.format.name == 'RegMove')
|
||||
is_regmove = (recipe.format.name in ('RegMove', 'RegSpill', 'RegFill'))
|
||||
|
||||
# First unpack the instruction.
|
||||
with fmt.indented(
|
||||
@@ -111,6 +111,10 @@ def gen_recipe(recipe, fmt):
|
||||
# diversion tracker.
|
||||
if recipe.format.name == 'RegMove':
|
||||
fmt.line('divert.regmove(arg, src, dst);')
|
||||
elif recipe.format.name == 'RegSpill':
|
||||
fmt.line('divert.regspill(arg, src, dst);')
|
||||
elif recipe.format.name == 'RegFill':
|
||||
fmt.line('divert.regfill(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
|
||||
|
||||
@@ -213,6 +213,18 @@ pub enum InstructionData {
|
||||
src: RegUnit,
|
||||
dst: RegUnit,
|
||||
},
|
||||
RegSpill {
|
||||
opcode: Opcode,
|
||||
arg: Value,
|
||||
src: RegUnit,
|
||||
dst: StackSlot,
|
||||
},
|
||||
RegFill {
|
||||
opcode: Opcode,
|
||||
arg: Value,
|
||||
src: StackSlot,
|
||||
dst: RegUnit,
|
||||
},
|
||||
Trap { opcode: Opcode, code: ir::TrapCode },
|
||||
CondTrap {
|
||||
opcode: Opcode,
|
||||
|
||||
@@ -317,6 +317,12 @@ impl<'a> Verifier<'a> {
|
||||
HeapAddr { heap, .. } => {
|
||||
self.verify_heap(inst, heap)?;
|
||||
}
|
||||
RegSpill { dst, .. } => {
|
||||
self.verify_stack_slot(inst, dst)?;
|
||||
}
|
||||
RegFill { src, .. } => {
|
||||
self.verify_stack_slot(inst, src)?;
|
||||
}
|
||||
|
||||
// Exhaustive list so we can't forget to add new formats
|
||||
Unary { .. } |
|
||||
|
||||
@@ -385,6 +385,22 @@ pub fn write_operands(
|
||||
write!(w, " {}, %{} -> %{}", arg, src, dst)
|
||||
}
|
||||
}
|
||||
RegSpill { arg, src, dst, .. } => {
|
||||
if let Some(isa) = isa {
|
||||
let regs = isa.register_info();
|
||||
write!(w, " {}, {} -> {}", arg, regs.display_regunit(src), dst)
|
||||
} else {
|
||||
write!(w, " {}, %{} -> {}", arg, src, dst)
|
||||
}
|
||||
}
|
||||
RegFill { arg, src, dst, .. } => {
|
||||
if let Some(isa) = isa {
|
||||
let regs = isa.register_info();
|
||||
write!(w, " {}, {} -> {}", arg, src, regs.display_regunit(dst))
|
||||
} else {
|
||||
write!(w, " {}, {} -> %{}", arg, src, dst)
|
||||
}
|
||||
}
|
||||
Trap { code, .. } => write!(w, " {}", code),
|
||||
CondTrap { arg, code, .. } => write!(w, " {}, {}", arg, code),
|
||||
}
|
||||
|
||||
@@ -2223,6 +2223,46 @@ impl<'a> Parser<'a> {
|
||||
dst,
|
||||
}
|
||||
}
|
||||
InstructionFormat::RegSpill => {
|
||||
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 '->' before destination stack slot",
|
||||
)?;
|
||||
let dst = self.match_ss("expected stack slot number: ss«n»")
|
||||
.and_then(|num| ctx.get_ss(num, &self.loc))?;
|
||||
InstructionData::RegSpill {
|
||||
opcode,
|
||||
arg,
|
||||
src,
|
||||
dst,
|
||||
}
|
||||
}
|
||||
InstructionFormat::RegFill => {
|
||||
let arg = self.match_value("expected SSA value operand")?;
|
||||
self.match_token(
|
||||
Token::Comma,
|
||||
"expected ',' between operands",
|
||||
)?;
|
||||
let src = self.match_ss("expected stack slot number: ss«n»")
|
||||
.and_then(|num| ctx.get_ss(num, &self.loc))?;
|
||||
self.match_token(
|
||||
Token::Arrow,
|
||||
"expected '->' before destination register units",
|
||||
)?;
|
||||
let dst = self.match_regunit(ctx.unique_isa)?;
|
||||
InstructionData::RegFill {
|
||||
opcode,
|
||||
arg,
|
||||
src,
|
||||
dst,
|
||||
}
|
||||
}
|
||||
InstructionFormat::Trap => {
|
||||
let code = self.match_enum("expected trap code")?;
|
||||
InstructionData::Trap { opcode, code }
|
||||
|
||||
Reference in New Issue
Block a user