machinst: allow passing constant information to the instruction emitter;
A new associated type Info is added to MachInstEmit, which is the immutable counterpart to State. It can't easily be constructed from an ABICallee, since it would require adding an associated type to the latter, and making so leaks the associated type in a lot of places in the code base and makes the code harder to read. Instead, the EmitInfo state can simply be passed to the `Vcode::emit` function directly.
This commit is contained in:
@@ -390,7 +390,7 @@ fn emit_simm(sink: &mut MachBuffer<Inst>, size: u8, simm32: u32) {
|
||||
/// A small helper to generate a signed conversion instruction.
|
||||
fn emit_signed_cvt(
|
||||
sink: &mut MachBuffer<Inst>,
|
||||
flags: &settings::Flags,
|
||||
info: &EmitInfo,
|
||||
state: &mut EmitState,
|
||||
src: Reg,
|
||||
dst: Writable<Reg>,
|
||||
@@ -404,7 +404,7 @@ fn emit_signed_cvt(
|
||||
SseOpcode::Cvtsi2ss
|
||||
};
|
||||
let inst = Inst::gpr_to_xmm(op, RegMem::reg(src), OperandSize::Size64, dst);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
}
|
||||
|
||||
/// Emits a one way conditional jump if CC is set (true).
|
||||
@@ -472,7 +472,7 @@ fn one_way_jmp(sink: &mut MachBuffer<Inst>, cc: CC, label: MachLabel) {
|
||||
pub(crate) fn emit(
|
||||
inst: &Inst,
|
||||
sink: &mut MachBuffer<Inst>,
|
||||
flags: &settings::Flags,
|
||||
info: &EmitInfo,
|
||||
state: &mut EmitState,
|
||||
) {
|
||||
match inst {
|
||||
@@ -767,19 +767,19 @@ pub(crate) fn emit(
|
||||
// idiv %divisor
|
||||
//
|
||||
// $done:
|
||||
debug_assert!(flags.avoid_div_traps());
|
||||
debug_assert!(info.flags().avoid_div_traps());
|
||||
|
||||
// Check if the divisor is zero, first.
|
||||
let inst = Inst::cmp_rmi_r(*size, RegMemImm::imm(0), divisor.to_reg());
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
let inst = Inst::trap_if(CC::Z, TrapCode::IntegerDivisionByZero, *loc);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
let (do_op, done_label) = if kind.is_signed() {
|
||||
// Now check if the divisor is -1.
|
||||
let inst = Inst::cmp_rmi_r(*size, RegMemImm::imm(0xffffffff), divisor.to_reg());
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
let do_op = sink.get_label();
|
||||
|
||||
@@ -796,10 +796,10 @@ pub(crate) fn emit(
|
||||
0,
|
||||
Writable::from_reg(regs::rdx()),
|
||||
);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
let inst = Inst::jmp_known(BranchTarget::Label(done_label));
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
(Some(do_op), Some(done_label))
|
||||
} else {
|
||||
@@ -808,18 +808,18 @@ pub(crate) fn emit(
|
||||
let tmp = tmp.expect("temporary for i64 sdiv");
|
||||
|
||||
let inst = Inst::imm(OperandSize::Size64, 0x8000000000000000, tmp);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
let inst = Inst::cmp_rmi_r(8, RegMemImm::reg(tmp.to_reg()), regs::rax());
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
} else {
|
||||
let inst = Inst::cmp_rmi_r(*size, RegMemImm::imm(0x80000000), regs::rax());
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
}
|
||||
|
||||
// If not equal, jump over the trap.
|
||||
let inst = Inst::trap_if(CC::Z, TrapCode::IntegerOverflow, *loc);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
(Some(do_op), None)
|
||||
}
|
||||
@@ -840,15 +840,15 @@ pub(crate) fn emit(
|
||||
if kind.is_signed() {
|
||||
// sign-extend the sign-bit of rax into rdx, for signed opcodes.
|
||||
let inst = Inst::sign_extend_data(*size);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
} else {
|
||||
// zero for unsigned opcodes.
|
||||
let inst = Inst::imm(OperandSize::Size64, 0, Writable::from_reg(regs::rdx()));
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
}
|
||||
|
||||
let inst = Inst::div(*size, kind.is_signed(), RegMem::reg(divisor.to_reg()), *loc);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
// Lowering takes care of moving the result back into the right register, see comment
|
||||
// there.
|
||||
@@ -1382,7 +1382,7 @@ pub(crate) fn emit(
|
||||
SseOpcode::Movss
|
||||
};
|
||||
let inst = Inst::xmm_unary_rm_r(op, src.clone(), *dst);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
sink.bind_label(next);
|
||||
}
|
||||
@@ -1623,7 +1623,7 @@ pub(crate) fn emit(
|
||||
|
||||
// Copy the index (and make sure to clear the high 32-bits lane of tmp2).
|
||||
let inst = Inst::movzx_rm_r(ExtMode::LQ, RegMem::reg(*idx), *tmp2, None);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
// Load base address of jump table.
|
||||
let start_of_jumptable = sink.get_label();
|
||||
@@ -1631,7 +1631,7 @@ pub(crate) fn emit(
|
||||
Amode::rip_relative(BranchTarget::Label(start_of_jumptable)),
|
||||
*tmp1,
|
||||
);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
// Load value out of the jump table. It's a relative offset to the target block, so it
|
||||
// might be negative; use a sign-extension.
|
||||
@@ -1641,7 +1641,7 @@ pub(crate) fn emit(
|
||||
*tmp2,
|
||||
None,
|
||||
);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
// Add base of jump table to jump-table-sourced block offset.
|
||||
let inst = Inst::alu_rmi_r(
|
||||
@@ -1650,11 +1650,11 @@ pub(crate) fn emit(
|
||||
RegMemImm::reg(tmp2.to_reg()),
|
||||
*tmp1,
|
||||
);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
// Branch to computed address.
|
||||
let inst = Inst::jmp_unknown(RegMem::reg(tmp1.to_reg()));
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
// Emit jump table (table of 32-bit offsets).
|
||||
sink.bind_label(start_of_jumptable);
|
||||
@@ -1683,7 +1683,7 @@ pub(crate) fn emit(
|
||||
|
||||
// Trap!
|
||||
let inst = Inst::trap(*srcloc, *trap_code);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
sink.bind_label(else_label);
|
||||
}
|
||||
@@ -1890,7 +1890,7 @@ pub(crate) fn emit(
|
||||
};
|
||||
|
||||
let inst = Inst::xmm_cmp_rm_r(cmp_op, RegMem::reg(*lhs), rhs_dst.to_reg());
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
one_way_jmp(sink, CC::NZ, do_min_max);
|
||||
one_way_jmp(sink, CC::P, propagate_nan);
|
||||
@@ -1900,23 +1900,23 @@ pub(crate) fn emit(
|
||||
// case, and are no-ops otherwise.
|
||||
let op = if *is_min { or_op } else { and_op };
|
||||
let inst = Inst::xmm_rm_r(op, RegMem::reg(*lhs), *rhs_dst);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
let inst = Inst::jmp_known(BranchTarget::Label(done));
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
// x86's min/max are not symmetric; if either operand is a NaN, they return the
|
||||
// read-only operand: perform an addition between the two operands, which has the
|
||||
// desired NaN propagation effects.
|
||||
sink.bind_label(propagate_nan);
|
||||
let inst = Inst::xmm_rm_r(add_op, RegMem::reg(*lhs), *rhs_dst);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
one_way_jmp(sink, CC::P, done);
|
||||
|
||||
sink.bind_label(do_min_max);
|
||||
let inst = Inst::xmm_rm_r(min_max_op, RegMem::reg(*lhs), *rhs_dst);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
sink.bind_label(done);
|
||||
}
|
||||
@@ -1986,13 +1986,13 @@ pub(crate) fn emit(
|
||||
let constant_start_label = sink.get_label();
|
||||
let load_offset = Amode::rip_relative(BranchTarget::Label(constant_start_label));
|
||||
let load = Inst::load(*ty, load_offset, *dst, ExtKind::None, None);
|
||||
load.emit(sink, flags, state);
|
||||
load.emit(sink, info, state);
|
||||
|
||||
// Jump over the constant.
|
||||
let constant_end_label = sink.get_label();
|
||||
let continue_at_offset = BranchTarget::Label(constant_end_label);
|
||||
let jump = Inst::jmp_known(continue_at_offset);
|
||||
jump.emit(sink, flags, state);
|
||||
jump.emit(sink, info, state);
|
||||
|
||||
// Emit the constant.
|
||||
sink.bind_label(constant_start_label);
|
||||
@@ -2151,30 +2151,30 @@ pub(crate) fn emit(
|
||||
// thing.
|
||||
// TODO use tst src, src here.
|
||||
let inst = Inst::cmp_rmi_r(8, RegMemImm::imm(0), src.to_reg());
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
one_way_jmp(sink, CC::L, handle_negative);
|
||||
|
||||
// Handle a positive int64, which is the "easy" case: a signed conversion will do the
|
||||
// right thing.
|
||||
emit_signed_cvt(sink, flags, state, src.to_reg(), *dst, *to_f64);
|
||||
emit_signed_cvt(sink, info, state, src.to_reg(), *dst, *to_f64);
|
||||
|
||||
let inst = Inst::jmp_known(BranchTarget::Label(done));
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
sink.bind_label(handle_negative);
|
||||
|
||||
// Divide x by two to get it in range for the signed conversion, keep the LSB, and
|
||||
// scale it back up on the FP side.
|
||||
let inst = Inst::gen_move(*tmp_gpr1, src.to_reg(), types::I64);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
// tmp_gpr1 := src >> 1
|
||||
let inst = Inst::shift_r(8, ShiftKind::ShiftRightLogical, Some(1), *tmp_gpr1);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
let inst = Inst::gen_move(*tmp_gpr2, src.to_reg(), types::I64);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
let inst = Inst::alu_rmi_r(
|
||||
true, /* 64bits */
|
||||
@@ -2182,7 +2182,7 @@ pub(crate) fn emit(
|
||||
RegMemImm::imm(1),
|
||||
*tmp_gpr2,
|
||||
);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
let inst = Inst::alu_rmi_r(
|
||||
true, /* 64bits */
|
||||
@@ -2190,9 +2190,9 @@ pub(crate) fn emit(
|
||||
RegMemImm::reg(tmp_gpr1.to_reg()),
|
||||
*tmp_gpr2,
|
||||
);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
emit_signed_cvt(sink, flags, state, tmp_gpr2.to_reg(), *dst, *to_f64);
|
||||
emit_signed_cvt(sink, info, state, tmp_gpr2.to_reg(), *dst, *to_f64);
|
||||
|
||||
let add_op = if *to_f64 {
|
||||
SseOpcode::Addsd
|
||||
@@ -2200,7 +2200,7 @@ pub(crate) fn emit(
|
||||
SseOpcode::Addss
|
||||
};
|
||||
let inst = Inst::xmm_rm_r(add_op, RegMem::reg(dst.to_reg()), *dst);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
sink.bind_label(done);
|
||||
}
|
||||
@@ -2273,18 +2273,18 @@ pub(crate) fn emit(
|
||||
|
||||
// The truncation.
|
||||
let inst = Inst::xmm_to_gpr(trunc_op, src, *dst, *dst_size);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
// Compare against 1, in case of overflow the dst operand was INT_MIN.
|
||||
let inst = Inst::cmp_rmi_r(dst_size.to_bytes(), RegMemImm::imm(1), dst.to_reg());
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
one_way_jmp(sink, CC::NO, done); // no overflow => done
|
||||
|
||||
// Check for NaN.
|
||||
|
||||
let inst = Inst::xmm_cmp_rm_r(cmp_op, RegMem::reg(src), src);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
one_way_jmp(sink, CC::NP, not_nan); // go to not_nan if not a NaN
|
||||
|
||||
@@ -2296,10 +2296,10 @@ pub(crate) fn emit(
|
||||
RegMemImm::reg(dst.to_reg()),
|
||||
*dst,
|
||||
);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
let inst = Inst::jmp_known(BranchTarget::Label(done));
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
sink.bind_label(not_nan);
|
||||
|
||||
@@ -2308,10 +2308,10 @@ pub(crate) fn emit(
|
||||
// Zero out tmp_xmm.
|
||||
let inst =
|
||||
Inst::xmm_rm_r(SseOpcode::Xorpd, RegMem::reg(tmp_xmm.to_reg()), *tmp_xmm);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
let inst = Inst::xmm_cmp_rm_r(cmp_op, RegMem::reg(src), tmp_xmm.to_reg());
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
// Jump if >= to done.
|
||||
one_way_jmp(sink, CC::NB, done);
|
||||
@@ -2319,16 +2319,16 @@ pub(crate) fn emit(
|
||||
// Otherwise, put INT_MAX.
|
||||
if *dst_size == OperandSize::Size64 {
|
||||
let inst = Inst::imm(OperandSize::Size64, 0x7fffffffffffffff, *dst);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
} else {
|
||||
let inst = Inst::imm(OperandSize::Size32, 0x7fffffff, *dst);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
}
|
||||
} else {
|
||||
let check_positive = sink.get_label();
|
||||
|
||||
let inst = Inst::trap(*srcloc, TrapCode::BadConversionToInteger);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
// Check if INT_MIN was the correct result: determine the smallest floating point
|
||||
// number that would convert to INT_MIN, put it in a temporary register, and compare
|
||||
@@ -2344,7 +2344,7 @@ pub(crate) fn emit(
|
||||
OperandSize::Size32 => {
|
||||
let cst = Ieee32::pow2(output_bits - 1).neg().bits();
|
||||
let inst = Inst::imm(OperandSize::Size32, cst as u64, *tmp_gpr);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
}
|
||||
OperandSize::Size64 => {
|
||||
// An f64 can represent `i32::min_value() - 1` exactly with precision to spare,
|
||||
@@ -2356,22 +2356,22 @@ pub(crate) fn emit(
|
||||
Ieee64::pow2(output_bits - 1).neg()
|
||||
};
|
||||
let inst = Inst::imm(OperandSize::Size64, cst.bits(), *tmp_gpr);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
}
|
||||
}
|
||||
|
||||
let inst =
|
||||
Inst::gpr_to_xmm(cast_op, RegMem::reg(tmp_gpr.to_reg()), *src_size, *tmp_xmm);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
let inst = Inst::xmm_cmp_rm_r(cmp_op, RegMem::reg(tmp_xmm.to_reg()), src);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
// jump over trap if src >= or > threshold
|
||||
one_way_jmp(sink, no_overflow_cc, check_positive);
|
||||
|
||||
let inst = Inst::trap(*srcloc, TrapCode::IntegerOverflow);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
// If positive, it was a real overflow.
|
||||
|
||||
@@ -2380,15 +2380,15 @@ pub(crate) fn emit(
|
||||
// Zero out the tmp_xmm register.
|
||||
let inst =
|
||||
Inst::xmm_rm_r(SseOpcode::Xorpd, RegMem::reg(tmp_xmm.to_reg()), *tmp_xmm);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
let inst = Inst::xmm_cmp_rm_r(cmp_op, RegMem::reg(src), tmp_xmm.to_reg());
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
one_way_jmp(sink, CC::NB, done); // jump over trap if 0 >= src
|
||||
|
||||
let inst = Inst::trap(*srcloc, TrapCode::IntegerOverflow);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
}
|
||||
|
||||
sink.bind_label(done);
|
||||
@@ -2464,14 +2464,14 @@ pub(crate) fn emit(
|
||||
};
|
||||
|
||||
let inst = Inst::imm(*src_size, cst, *tmp_gpr);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
let inst =
|
||||
Inst::gpr_to_xmm(cast_op, RegMem::reg(tmp_gpr.to_reg()), *src_size, *tmp_xmm);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
let inst = Inst::xmm_cmp_rm_r(cmp_op, RegMem::reg(tmp_xmm.to_reg()), src.to_reg());
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
let handle_large = sink.get_label();
|
||||
one_way_jmp(sink, CC::NB, handle_large); // jump to handle_large if src >= large_threshold
|
||||
@@ -2487,14 +2487,14 @@ pub(crate) fn emit(
|
||||
RegMemImm::reg(dst.to_reg()),
|
||||
*dst,
|
||||
);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
let inst = Inst::jmp_known(BranchTarget::Label(done));
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
} else {
|
||||
// Trap.
|
||||
let inst = Inst::trap(*srcloc, TrapCode::BadConversionToInteger);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
}
|
||||
|
||||
sink.bind_label(not_nan);
|
||||
@@ -2503,10 +2503,10 @@ pub(crate) fn emit(
|
||||
// overflow.
|
||||
|
||||
let inst = Inst::xmm_to_gpr(trunc_op, src.to_reg(), *dst, *dst_size);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
let inst = Inst::cmp_rmi_r(dst_size.to_bytes(), RegMemImm::imm(0), dst.to_reg());
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
one_way_jmp(sink, CC::NL, done); // if dst >= 0, jump to done
|
||||
|
||||
@@ -2519,14 +2519,14 @@ pub(crate) fn emit(
|
||||
RegMemImm::reg(dst.to_reg()),
|
||||
*dst,
|
||||
);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
let inst = Inst::jmp_known(BranchTarget::Label(done));
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
} else {
|
||||
// Trap.
|
||||
let inst = Inst::trap(*srcloc, TrapCode::IntegerOverflow);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
}
|
||||
|
||||
// Now handle large inputs.
|
||||
@@ -2534,13 +2534,13 @@ pub(crate) fn emit(
|
||||
sink.bind_label(handle_large);
|
||||
|
||||
let inst = Inst::xmm_rm_r(sub_op, RegMem::reg(tmp_xmm.to_reg()), *src);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
let inst = Inst::xmm_to_gpr(trunc_op, src.to_reg(), *dst, *dst_size);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
let inst = Inst::cmp_rmi_r(dst_size.to_bytes(), RegMemImm::imm(0), dst.to_reg());
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
let next_is_large = sink.get_label();
|
||||
one_way_jmp(sink, CC::NL, next_is_large); // if dst >= 0, jump to next_is_large
|
||||
@@ -2557,20 +2557,20 @@ pub(crate) fn emit(
|
||||
},
|
||||
*dst,
|
||||
);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
let inst = Inst::jmp_known(BranchTarget::Label(done));
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
} else {
|
||||
let inst = Inst::trap(*srcloc, TrapCode::IntegerOverflow);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
}
|
||||
|
||||
sink.bind_label(next_is_large);
|
||||
|
||||
if *dst_size == OperandSize::Size64 {
|
||||
let inst = Inst::imm(OperandSize::Size64, 1 << 63, *tmp_gpr);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
|
||||
let inst = Inst::alu_rmi_r(
|
||||
true,
|
||||
@@ -2578,11 +2578,11 @@ pub(crate) fn emit(
|
||||
RegMemImm::reg(tmp_gpr.to_reg()),
|
||||
*dst,
|
||||
);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
} else {
|
||||
let inst =
|
||||
Inst::alu_rmi_r(false, AluRmiROpcode::Add, RegMemImm::imm(1 << 31), *dst);
|
||||
inst.emit(sink, flags, state);
|
||||
inst.emit(sink, info, state);
|
||||
}
|
||||
|
||||
sink.bind_label(done);
|
||||
@@ -2600,7 +2600,7 @@ pub(crate) fn emit(
|
||||
sink.put1(0x48 | ((enc_dst >> 3) & 1));
|
||||
sink.put1(0xB8 | (enc_dst & 7));
|
||||
sink.add_reloc(*srcloc, Reloc::Abs8, name, *offset);
|
||||
if flags.emit_all_ones_funcaddrs() {
|
||||
if info.flags().emit_all_ones_funcaddrs() {
|
||||
sink.put8(u64::max_value());
|
||||
} else {
|
||||
sink.put8(0);
|
||||
@@ -2664,14 +2664,14 @@ pub(crate) fn emit(
|
||||
// mov{zbq,zwq,zlq,q} (%r9), %rax
|
||||
// No need to call `add_trap` here, since the `i1` emit will do that.
|
||||
let i1 = Inst::load(*ty, amode.clone(), rax_w, ExtKind::ZeroExtend, *srcloc);
|
||||
i1.emit(sink, flags, state);
|
||||
i1.emit(sink, info, state);
|
||||
|
||||
// again:
|
||||
sink.bind_label(again_label);
|
||||
|
||||
// movq %rax, %r11
|
||||
let i2 = Inst::mov_r_r(true, rax, r11_w);
|
||||
i2.emit(sink, flags, state);
|
||||
i2.emit(sink, info, state);
|
||||
|
||||
// opq %r10, %r11
|
||||
let r10_rmi = RegMemImm::reg(r10);
|
||||
@@ -2688,7 +2688,7 @@ pub(crate) fn emit(
|
||||
};
|
||||
Inst::alu_rmi_r(true, alu_op, r10_rmi, r11_w)
|
||||
};
|
||||
i3.emit(sink, flags, state);
|
||||
i3.emit(sink, info, state);
|
||||
|
||||
// lock cmpxchg{b,w,l,q} %r11, (%r9)
|
||||
// No need to call `add_trap` here, since the `i4` emit will do that.
|
||||
@@ -2698,7 +2698,7 @@ pub(crate) fn emit(
|
||||
dst: amode.into(),
|
||||
srcloc: *srcloc,
|
||||
};
|
||||
i4.emit(sink, flags, state);
|
||||
i4.emit(sink, info, state);
|
||||
|
||||
// jnz again
|
||||
one_way_jmp(sink, CC::NZ, again_label);
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
use super::*;
|
||||
use crate::isa::test_utils;
|
||||
use crate::isa::x64;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
#[test]
|
||||
@@ -3660,14 +3661,22 @@ fn test_x64_emit() {
|
||||
// ========================================================
|
||||
// Actually run the tests!
|
||||
let flags = settings::Flags::new(settings::builder());
|
||||
|
||||
use crate::settings::Configurable;
|
||||
let mut isa_flag_builder = x64::settings::builder();
|
||||
isa_flag_builder.enable("has_ssse3").unwrap();
|
||||
isa_flag_builder.enable("has_sse41").unwrap();
|
||||
let isa_flags = x64::settings::Flags::new(&flags, isa_flag_builder);
|
||||
|
||||
let rru = regs::create_reg_universe_systemv(&flags);
|
||||
let emit_info = EmitInfo::new(flags, isa_flags);
|
||||
for (insn, expected_encoding, expected_printing) in insns {
|
||||
// Check the printed text is as expected.
|
||||
let actual_printing = insn.show_rru(Some(&rru));
|
||||
assert_eq!(expected_printing, actual_printing);
|
||||
let mut sink = test_utils::TestCodeSink::new();
|
||||
let mut buffer = MachBuffer::new();
|
||||
insn.emit(&mut buffer, &flags, &mut Default::default());
|
||||
insn.emit(&mut buffer, &emit_info, &mut Default::default());
|
||||
let buffer = buffer.finish();
|
||||
buffer.emit(&mut sink);
|
||||
let actual_encoding = &sink.stringify();
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
use crate::binemit::{CodeOffset, StackMap};
|
||||
use crate::ir::{types, ExternalName, Opcode, SourceLoc, TrapCode, Type};
|
||||
use crate::isa::x64::settings as x64_settings;
|
||||
use crate::machinst::*;
|
||||
use crate::{settings, settings::Flags, CodegenError, CodegenResult};
|
||||
use alloc::boxed::Box;
|
||||
@@ -2559,11 +2560,30 @@ pub struct EmitState {
|
||||
stack_map: Option<StackMap>,
|
||||
}
|
||||
|
||||
/// Constant state used during emissions of a sequence of instructions.
|
||||
pub struct EmitInfo {
|
||||
flags: settings::Flags,
|
||||
isa_flags: x64_settings::Flags,
|
||||
}
|
||||
|
||||
impl EmitInfo {
|
||||
pub(crate) fn new(flags: settings::Flags, isa_flags: x64_settings::Flags) -> Self {
|
||||
Self { flags, isa_flags }
|
||||
}
|
||||
}
|
||||
|
||||
impl MachInstEmitInfo for EmitInfo {
|
||||
fn flags(&self) -> &Flags {
|
||||
&self.flags
|
||||
}
|
||||
}
|
||||
|
||||
impl MachInstEmit for Inst {
|
||||
type State = EmitState;
|
||||
type Info = EmitInfo;
|
||||
|
||||
fn emit(&self, sink: &mut MachBuffer<Inst>, flags: &settings::Flags, state: &mut Self::State) {
|
||||
emit::emit(self, sink, flags, state);
|
||||
fn emit(&self, sink: &mut MachBuffer<Inst>, info: &Self::Info, state: &mut Self::State) {
|
||||
emit::emit(self, sink, info, state);
|
||||
}
|
||||
|
||||
fn pretty_print(&self, mb_rru: Option<&RealRegUniverse>, _: &mut Self::State) -> String {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
//! X86_64-bit Instruction Set Architecture.
|
||||
|
||||
use self::inst::EmitInfo;
|
||||
|
||||
use super::TargetIsa;
|
||||
use crate::ir::{condcodes::IntCC, Function};
|
||||
use crate::isa::x64::{inst::regs::create_reg_universe_systemv, settings as x64_settings};
|
||||
@@ -20,7 +22,7 @@ mod settings;
|
||||
pub(crate) struct X64Backend {
|
||||
triple: Triple,
|
||||
flags: Flags,
|
||||
_x64_flags: x64_settings::Flags,
|
||||
x64_flags: x64_settings::Flags,
|
||||
reg_universe: RealRegUniverse,
|
||||
}
|
||||
|
||||
@@ -31,7 +33,7 @@ impl X64Backend {
|
||||
Self {
|
||||
triple,
|
||||
flags,
|
||||
_x64_flags: x64_flags,
|
||||
x64_flags,
|
||||
reg_universe,
|
||||
}
|
||||
}
|
||||
@@ -39,8 +41,9 @@ impl X64Backend {
|
||||
fn compile_vcode(&self, func: &Function, flags: Flags) -> CodegenResult<VCode<inst::Inst>> {
|
||||
// This performs lowering to VCode, register-allocates the code, computes
|
||||
// block layout and finalizes branches. The result is ready for binary emission.
|
||||
let emit_info = EmitInfo::new(flags.clone(), self.x64_flags.clone());
|
||||
let abi = Box::new(abi::X64ABICallee::new(&func, flags)?);
|
||||
compile::compile::<Self>(&func, self, abi)
|
||||
compile::compile::<Self>(&func, self, abi, emit_info)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +55,7 @@ impl MachBackend for X64Backend {
|
||||
) -> CodegenResult<MachCompileResult> {
|
||||
let flags = self.flags();
|
||||
let vcode = self.compile_vcode(func, flags.clone())?;
|
||||
|
||||
let buffer = vcode.emit();
|
||||
let buffer = buffer.finish();
|
||||
let frame_size = vcode.frame_size();
|
||||
|
||||
Reference in New Issue
Block a user