///|
fn edge_parallel_moves(
function : @vcode.Function[X64Inst],
allocation : @vcode.Allocation,
block : @vcode.Block,
successor_index : Int,
) -> Array[@vcode.ParallelMove] {
let transfers : Array[@vcode.ParallelMove] = []
for edit in allocation.edits() {
match edit.kind() {
EdgeMove(source~, successor_index=edge_index, value~, from~, to~) =>
if source == block && edge_index == successor_index {
transfers.push(
@vcode.ParallelMove::new(
function.value_type(value).unwrap(),
from,
to,
),
)
}
_ => ()
}
}
transfers
}
///|
fn resolve_edge_moves(
function : @vcode.Function[X64Inst],
allocation : @vcode.Allocation,
block : @vcode.Block,
successor_index : Int,
) -> Array[@vcode.ParallelMove] raise X64EmitError {
@vcode.resolve_parallel_moves(
edge_parallel_moves(function, allocation, block, successor_index),
[@vcode.PhysicalReg::new(10, Int), @vcode.PhysicalReg::new(11, Int)],
[
@vcode.PhysicalReg::new(14, FpVector),
@vcode.PhysicalReg::new(15, FpVector),
],
) catch {
error => raise InvalidParallelMove(cause=error)
}
}
///|
fn emit_location_move(
buffer : CodeBuffer,
frame : X64Frame,
transfer : @vcode.ParallelMove,
) -> Unit raise X64EmitError {
match (transfer.from, transfer.to) {
(Register(from), Register(to)) => emit_move(buffer, transfer.ty, to, from)
(Stack(slot), Register(to)) =>
emit_stack_access(
buffer,
true,
transfer.ty,
to,
frame.slot_offset(slot).unwrap(),
)
(Register(from), Stack(slot)) =>
emit_stack_access(
buffer,
false,
transfer.ty,
from,
frame.slot_offset(slot).unwrap(),
)
(Stack(_), Stack(_)) => raise UnresolvedStackMove
}
}
///|
fn emit_resolved_moves(
buffer : CodeBuffer,
frame : X64Frame,
transfers : Array[@vcode.ParallelMove],
) -> Unit raise X64EmitError {
for transfer in transfers {
emit_location_move(buffer, frame, transfer)
}
}
///|
fn emit_edge_moves(
buffer : CodeBuffer,
function : @vcode.Function[X64Inst],
allocation : @vcode.Allocation,
frame : X64Frame,
block : @vcode.Block,
successor_index : Int,
) -> Unit raise X64EmitError {
emit_resolved_moves(
buffer,
frame,
resolve_edge_moves(function, allocation, block, successor_index),
)
}
///|
fn emit_conditional_edges(
buffer : CodeBuffer,
function : @vcode.Function[X64Inst],
allocation : @vcode.Allocation,
frame : X64Frame,
block : @vcode.Block,
instruction : @vcode.Instruction,
condition : @vcode.PhysicalReg,
next_block : @vcode.Block?,
) -> Unit raise X64EmitError {
let when_true = function.instruction_successor_at(instruction, 0).unwrap().target
let when_false = function.instruction_successor_at(instruction, 1).unwrap().target
let true_moves = resolve_edge_moves(function, allocation, block, 0)
let false_moves = resolve_edge_moves(function, allocation, block, 1)
buffer.x86_emit_test_rr32(condition.id, condition.id)
if next_block == Some(when_false) {
if true_moves.is_empty() {
buffer.x86_emit_jcc_rel32(RawNe, function.block_index(when_true).unwrap())
emit_resolved_moves(buffer, frame, false_moves)
} else {
buffer.emit_byte(0x0F)
buffer.emit_byte(x86_cond_to_jcc_opcode(RawEq))
let local_false_branch = buffer.position()
emit_u32_le(buffer, 0)
emit_resolved_moves(buffer, frame, true_moves)
buffer.x86_emit_jmp_rel32(function.block_index(when_true).unwrap())
patch_relative_branch(buffer, local_false_branch, buffer.position(), 32)
emit_resolved_moves(buffer, frame, false_moves)
}
} else if next_block == Some(when_true) {
if false_moves.is_empty() {
buffer.x86_emit_jcc_rel32(
RawEq,
function.block_index(when_false).unwrap(),
)
emit_resolved_moves(buffer, frame, true_moves)
} else {
buffer.emit_byte(0x0F)
buffer.emit_byte(x86_cond_to_jcc_opcode(RawNe))
let local_true_branch = buffer.position()
emit_u32_le(buffer, 0)
emit_resolved_moves(buffer, frame, false_moves)
buffer.x86_emit_jmp_rel32(function.block_index(when_false).unwrap())
patch_relative_branch(buffer, local_true_branch, buffer.position(), 32)
emit_resolved_moves(buffer, frame, true_moves)
}
} else if true_moves.is_empty() {
buffer.x86_emit_jcc_rel32(RawNe, function.block_index(when_true).unwrap())
emit_resolved_moves(buffer, frame, false_moves)
buffer.x86_emit_jmp_rel32(function.block_index(when_false).unwrap())
} else if false_moves.is_empty() {
buffer.x86_emit_jcc_rel32(RawEq, function.block_index(when_false).unwrap())
emit_resolved_moves(buffer, frame, true_moves)
buffer.x86_emit_jmp_rel32(function.block_index(when_true).unwrap())
} else {
buffer.emit_byte(0x0F)
buffer.emit_byte(x86_cond_to_jcc_opcode(RawEq))
let local_false_branch = buffer.position()
emit_u32_le(buffer, 0)
emit_resolved_moves(buffer, frame, true_moves)
buffer.x86_emit_jmp_rel32(function.block_index(when_true).unwrap())
patch_relative_branch(buffer, local_false_branch, buffer.position(), 32)
emit_resolved_moves(buffer, frame, false_moves)
buffer.x86_emit_jmp_rel32(function.block_index(when_false).unwrap())
}
}
///|
fn emit_switch_compare(
buffer : CodeBuffer,
width : GprWidth,
index : @vcode.PhysicalReg,
bits : UInt64,
) -> Unit {
if bits <= 0x7FFFFFFFUL {
if width == W32 {
buffer.x86_emit_cmp_r32_imm32(index.id, bits.to_int())
} else {
buffer.x86_emit_cmp_r_imm32(index.id, bits.to_int())
}
} else {
let scratch = @vcode.PhysicalReg::new(11, Int)
emit_constant(buffer, width, scratch, bits)
if width == W32 {
buffer.x86_emit_cmp_rr32(index.id, scratch.id)
} else {
buffer.x86_emit_cmp_rr(index.id, scratch.id)
}
}
}
///|
fn emit_switch_edges(
buffer : CodeBuffer,
function : @vcode.Function[X64Inst],
allocation : @vcode.Allocation,
frame : X64Frame,
block : @vcode.Block,
instruction : @vcode.Instruction,
width : GprWidth,
cases : Array[UInt64],
index : @vcode.PhysicalReg,
) -> Unit raise X64EmitError {
if cases.is_empty() {
emit_edge_moves(buffer, function, allocation, frame, block, 0)
buffer.x86_emit_jmp_rel32(
function
.block_index(
function.instruction_successor_at(instruction, 0).unwrap().target,
)
.unwrap(),
)
return
}
let case_branches : Array[(Int, Int)] = []
for successor_index, bits in cases {
emit_switch_compare(buffer, width, index, bits)
buffer.emit_byte(0x0F)
buffer.emit_byte(x86_cond_to_jcc_opcode(RawEq))
case_branches.push((buffer.position(), successor_index))
emit_u32_le(buffer, 0)
}
emit_edge_moves(buffer, function, allocation, frame, block, cases.length())
buffer.x86_emit_jmp_rel32(
function
.block_index(
function.instruction_successor_at(instruction, cases.length()).unwrap().target,
)
.unwrap(),
)
for entry in case_branches {
let (branch, successor_index) = entry
patch_relative_branch(buffer, branch, buffer.position(), 32)
emit_edge_moves(buffer, function, allocation, frame, block, successor_index)
buffer.x86_emit_jmp_rel32(
function
.block_index(
function.instruction_successor_at(instruction, successor_index).unwrap().target,
)
.unwrap(),
)
}
}