machinst x64: revamp integer immediate emission;
In particular: - try to optimize the integer emission into a 32-bit emission, when the high bits are all zero, and stop relying on the caller of `imm_r` to ensure this. - rename `Inst::imm_r`/`Inst::Imm_R` to `Inst::imm`/`Inst::Imm`. - generate a sign-extending mov 32-bit immediate to 64-bits, whenever possible. - fix a few places where the previous commit did introduce the generation of zero-constants with xor, when calling `put_input_to_reg`, thus clobbering the flags before they were read.
This commit is contained in:
@@ -1001,6 +1001,14 @@ pub enum OperandSize {
|
||||
}
|
||||
|
||||
impl OperandSize {
|
||||
pub(crate) fn from_bytes(num_bytes: u32) -> Self {
|
||||
match num_bytes {
|
||||
1 | 2 | 4 => OperandSize::Size32,
|
||||
8 => OperandSize::Size64,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn to_bytes(&self) -> u8 {
|
||||
match self {
|
||||
Self::Size32 => 4,
|
||||
|
||||
@@ -791,7 +791,11 @@ pub(crate) fn emit(
|
||||
// x % -1 = 0; put the result into the destination, $rdx.
|
||||
let done_label = sink.get_label();
|
||||
|
||||
let inst = Inst::imm_r(*size == 8, 0, Writable::from_reg(regs::rdx()));
|
||||
let inst = Inst::imm(
|
||||
OperandSize::from_bytes(*size as u32),
|
||||
0,
|
||||
Writable::from_reg(regs::rdx()),
|
||||
);
|
||||
inst.emit(sink, flags, state);
|
||||
|
||||
let inst = Inst::jmp_known(BranchTarget::Label(done_label));
|
||||
@@ -803,7 +807,7 @@ pub(crate) fn emit(
|
||||
if *size == 8 {
|
||||
let tmp = tmp.expect("temporary for i64 sdiv");
|
||||
|
||||
let inst = Inst::imm_r(true, 0x8000000000000000, tmp);
|
||||
let inst = Inst::imm(OperandSize::Size64, 0x8000000000000000, tmp);
|
||||
inst.emit(sink, flags, state);
|
||||
|
||||
let inst = Inst::cmp_rmi_r(8, RegMemImm::reg(tmp.to_reg()), regs::rax());
|
||||
@@ -839,7 +843,7 @@ pub(crate) fn emit(
|
||||
inst.emit(sink, flags, state);
|
||||
} else {
|
||||
// zero for unsigned opcodes.
|
||||
let inst = Inst::imm_r(true /* is_64 */, 0, Writable::from_reg(regs::rdx()));
|
||||
let inst = Inst::imm(OperandSize::Size64, 0, Writable::from_reg(regs::rdx()));
|
||||
inst.emit(sink, flags, state);
|
||||
}
|
||||
|
||||
@@ -854,18 +858,30 @@ pub(crate) fn emit(
|
||||
}
|
||||
}
|
||||
|
||||
Inst::Imm_R {
|
||||
Inst::Imm {
|
||||
dst_is_64,
|
||||
simm64,
|
||||
dst,
|
||||
} => {
|
||||
let enc_dst = int_reg_enc(dst.to_reg());
|
||||
if *dst_is_64 {
|
||||
// FIXME JRS 2020Feb10: also use the 32-bit case here when
|
||||
// possible
|
||||
sink.put1(0x48 | ((enc_dst >> 3) & 1));
|
||||
sink.put1(0xB8 | (enc_dst & 7));
|
||||
sink.put8(*simm64);
|
||||
if low32_will_sign_extend_to_64(*simm64) {
|
||||
// Sign-extended move imm32.
|
||||
emit_std_enc_enc(
|
||||
sink,
|
||||
LegacyPrefixes::None,
|
||||
0xC7,
|
||||
1,
|
||||
/* subopcode */ 0,
|
||||
enc_dst,
|
||||
RexFlags::set_w(),
|
||||
);
|
||||
sink.put4(*simm64 as u32);
|
||||
} else {
|
||||
sink.put1(0x48 | ((enc_dst >> 3) & 1));
|
||||
sink.put1(0xB8 | (enc_dst & 7));
|
||||
sink.put8(*simm64);
|
||||
}
|
||||
} else {
|
||||
if ((enc_dst >> 3) & 1) == 1 {
|
||||
sink.put1(0x41);
|
||||
@@ -2223,10 +2239,10 @@ pub(crate) fn emit(
|
||||
|
||||
// Otherwise, put INT_MAX.
|
||||
if *dst_size == OperandSize::Size64 {
|
||||
let inst = Inst::imm_r(true, 0x7fffffffffffffff, *dst);
|
||||
let inst = Inst::imm(OperandSize::Size64, 0x7fffffffffffffff, *dst);
|
||||
inst.emit(sink, flags, state);
|
||||
} else {
|
||||
let inst = Inst::imm_r(false, 0x7fffffff, *dst);
|
||||
let inst = Inst::imm(OperandSize::Size32, 0x7fffffff, *dst);
|
||||
inst.emit(sink, flags, state);
|
||||
}
|
||||
} else {
|
||||
@@ -2248,7 +2264,7 @@ pub(crate) fn emit(
|
||||
match *src_size {
|
||||
OperandSize::Size32 => {
|
||||
let cst = Ieee32::pow2(output_bits - 1).neg().bits();
|
||||
let inst = Inst::imm32_r_unchecked(cst as u64, *tmp_gpr);
|
||||
let inst = Inst::imm(OperandSize::Size32, cst as u64, *tmp_gpr);
|
||||
inst.emit(sink, flags, state);
|
||||
}
|
||||
OperandSize::Size64 => {
|
||||
@@ -2260,7 +2276,7 @@ pub(crate) fn emit(
|
||||
} else {
|
||||
Ieee64::pow2(output_bits - 1).neg()
|
||||
};
|
||||
let inst = Inst::imm_r(true, cst.bits(), *tmp_gpr);
|
||||
let inst = Inst::imm(OperandSize::Size64, cst.bits(), *tmp_gpr);
|
||||
inst.emit(sink, flags, state);
|
||||
}
|
||||
}
|
||||
@@ -2362,15 +2378,14 @@ pub(crate) fn emit(
|
||||
|
||||
let done = sink.get_label();
|
||||
|
||||
if *src_size == OperandSize::Size64 {
|
||||
let cst = Ieee64::pow2(dst_size.to_bits() - 1).bits();
|
||||
let inst = Inst::imm_r(true, cst, *tmp_gpr);
|
||||
inst.emit(sink, flags, state);
|
||||
let cst = if *src_size == OperandSize::Size64 {
|
||||
Ieee64::pow2(dst_size.to_bits() - 1).bits()
|
||||
} else {
|
||||
let cst = Ieee32::pow2(dst_size.to_bits() - 1).bits() as u64;
|
||||
let inst = Inst::imm32_r_unchecked(cst, *tmp_gpr);
|
||||
inst.emit(sink, flags, state);
|
||||
}
|
||||
Ieee32::pow2(dst_size.to_bits() - 1).bits() as u64
|
||||
};
|
||||
|
||||
let inst = Inst::imm(*src_size, cst, *tmp_gpr);
|
||||
inst.emit(sink, flags, state);
|
||||
|
||||
let inst =
|
||||
Inst::gpr_to_xmm(cast_op, RegMem::reg(tmp_gpr.to_reg()), *src_size, *tmp_xmm);
|
||||
@@ -2454,8 +2469,8 @@ pub(crate) fn emit(
|
||||
if *is_saturating {
|
||||
// The input was "large" (>= 2**(width -1)), so the only way to get an integer
|
||||
// overflow is because the input was too large: saturate to the max value.
|
||||
let inst = Inst::imm_r(
|
||||
true,
|
||||
let inst = Inst::imm(
|
||||
OperandSize::Size64,
|
||||
if *dst_size == OperandSize::Size64 {
|
||||
u64::max_value()
|
||||
} else {
|
||||
@@ -2475,7 +2490,7 @@ pub(crate) fn emit(
|
||||
sink.bind_label(next_is_large);
|
||||
|
||||
if *dst_size == OperandSize::Size64 {
|
||||
let inst = Inst::imm_r(true, 1 << 63, *tmp_gpr);
|
||||
let inst = Inst::imm(OperandSize::Size64, 1 << 63, *tmp_gpr);
|
||||
inst.emit(sink, flags, state);
|
||||
|
||||
let inst = Inst::alu_rmi_r(
|
||||
|
||||
@@ -1368,43 +1368,43 @@ fn test_x64_emit() {
|
||||
// Imm_R
|
||||
//
|
||||
insns.push((
|
||||
Inst::imm_r(false, 1234567, w_r14),
|
||||
Inst::imm(OperandSize::Size32, 1234567, w_r14),
|
||||
"41BE87D61200",
|
||||
"movl $1234567, %r14d",
|
||||
));
|
||||
insns.push((
|
||||
Inst::imm_r(false, -126i64 as u64, w_r14),
|
||||
Inst::imm(OperandSize::Size32, -126i64 as u64, w_r14),
|
||||
"41BE82FFFFFF",
|
||||
"movl $-126, %r14d",
|
||||
));
|
||||
insns.push((
|
||||
Inst::imm_r(true, 1234567898765, w_r14),
|
||||
Inst::imm(OperandSize::Size64, 1234567898765, w_r14),
|
||||
"49BE8D26FB711F010000",
|
||||
"movabsq $1234567898765, %r14",
|
||||
));
|
||||
insns.push((
|
||||
Inst::imm_r(true, -126i64 as u64, w_r14),
|
||||
"49BE82FFFFFFFFFFFFFF",
|
||||
Inst::imm(OperandSize::Size64, -126i64 as u64, w_r14),
|
||||
"49C7C682FFFFFF",
|
||||
"movabsq $-126, %r14",
|
||||
));
|
||||
insns.push((
|
||||
Inst::imm_r(false, 1234567, w_rcx),
|
||||
Inst::imm(OperandSize::Size32, 1234567, w_rcx),
|
||||
"B987D61200",
|
||||
"movl $1234567, %ecx",
|
||||
));
|
||||
insns.push((
|
||||
Inst::imm_r(false, -126i64 as u64, w_rcx),
|
||||
Inst::imm(OperandSize::Size32, -126i64 as u64, w_rcx),
|
||||
"B982FFFFFF",
|
||||
"movl $-126, %ecx",
|
||||
));
|
||||
insns.push((
|
||||
Inst::imm_r(true, 1234567898765, w_rsi),
|
||||
Inst::imm(OperandSize::Size64, 1234567898765, w_rsi),
|
||||
"48BE8D26FB711F010000",
|
||||
"movabsq $1234567898765, %rsi",
|
||||
));
|
||||
insns.push((
|
||||
Inst::imm_r(true, -126i64 as u64, w_rbx),
|
||||
"48BB82FFFFFFFFFFFFFF",
|
||||
Inst::imm(OperandSize::Size64, -126i64 as u64, w_rbx),
|
||||
"48C7C382FFFFFF",
|
||||
"movabsq $-126, %rbx",
|
||||
));
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ pub enum Inst {
|
||||
|
||||
/// Constant materialization: (imm32 imm64) reg.
|
||||
/// Either: movl $imm32, %reg32 or movabsq $imm64, %reg32.
|
||||
Imm_R {
|
||||
Imm {
|
||||
dst_is_64: bool,
|
||||
simm64: u64,
|
||||
dst: Writable<Reg>,
|
||||
@@ -579,31 +579,18 @@ impl Inst {
|
||||
Inst::SignExtendData { size }
|
||||
}
|
||||
|
||||
pub(crate) fn imm_r(dst_is_64: bool, simm64: u64, dst: Writable<Reg>) -> Inst {
|
||||
pub(crate) fn imm(size: OperandSize, simm64: u64, dst: Writable<Reg>) -> Inst {
|
||||
debug_assert!(dst.to_reg().get_class() == RegClass::I64);
|
||||
if !dst_is_64 {
|
||||
debug_assert!(
|
||||
low32_will_sign_extend_to_64(simm64),
|
||||
"{} won't sign-extend to 64 bits!",
|
||||
simm64
|
||||
);
|
||||
}
|
||||
Inst::Imm_R {
|
||||
// Try to generate a 32-bit immediate when the upper high bits are zeroed (which matches
|
||||
// the semantics of movl).
|
||||
let dst_is_64 = size == OperandSize::Size64 && simm64 > u32::max_value() as u64;
|
||||
Inst::Imm {
|
||||
dst_is_64,
|
||||
simm64,
|
||||
dst,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn imm32_r_unchecked(simm64: u64, dst: Writable<Reg>) -> Inst {
|
||||
debug_assert!(dst.to_reg().get_class() == RegClass::I64);
|
||||
Inst::Imm_R {
|
||||
dst_is_64: false,
|
||||
simm64,
|
||||
dst,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn mov_r_r(is_64: bool, src: Reg, dst: Writable<Reg>) -> Inst {
|
||||
debug_assert!(src.get_class() == RegClass::I64);
|
||||
debug_assert!(dst.to_reg().get_class() == RegClass::I64);
|
||||
@@ -1424,7 +1411,7 @@ impl ShowWithRRU for Inst {
|
||||
show_ireg_sized(dst.to_reg(), mb_rru, dst_size.to_bytes()),
|
||||
),
|
||||
|
||||
Inst::Imm_R {
|
||||
Inst::Imm {
|
||||
dst_is_64,
|
||||
simm64,
|
||||
dst,
|
||||
@@ -1761,7 +1748,7 @@ fn x64_get_regs(inst: &Inst, collector: &mut RegUsageCollector) {
|
||||
src.get_regs_as_uses(collector);
|
||||
collector.add_use(*dst);
|
||||
}
|
||||
Inst::Imm_R { dst, .. } => {
|
||||
Inst::Imm { dst, .. } => {
|
||||
collector.add_def(*dst);
|
||||
}
|
||||
Inst::Mov_R_R { src, dst, .. } | Inst::XmmToGpr { src, dst, .. } => {
|
||||
@@ -2097,7 +2084,7 @@ fn x64_map_regs<RUM: RegUsageMapper>(inst: &mut Inst, mapper: &RUM) {
|
||||
src.map_uses(mapper);
|
||||
map_use(mapper, dst);
|
||||
}
|
||||
Inst::Imm_R { ref mut dst, .. } => map_def(mapper, dst),
|
||||
Inst::Imm { ref mut dst, .. } => map_def(mapper, dst),
|
||||
Inst::Mov_R_R {
|
||||
ref mut src,
|
||||
ref mut dst,
|
||||
@@ -2407,7 +2394,57 @@ impl MachInst for Inst {
|
||||
mut alloc_tmp: F,
|
||||
) -> SmallVec<[Self; 4]> {
|
||||
let mut ret = SmallVec::new();
|
||||
if ty.is_int() {
|
||||
if ty == types::F32 {
|
||||
if value == 0 {
|
||||
ret.push(Inst::xmm_rm_r(
|
||||
SseOpcode::Xorps,
|
||||
RegMem::reg(to_reg.to_reg()),
|
||||
to_reg,
|
||||
));
|
||||
} else {
|
||||
let tmp = alloc_tmp(RegClass::I64, types::I32);
|
||||
ret.push(Inst::imm(OperandSize::Size32, value, tmp));
|
||||
|
||||
ret.push(Inst::gpr_to_xmm(
|
||||
SseOpcode::Movd,
|
||||
RegMem::reg(tmp.to_reg()),
|
||||
OperandSize::Size32,
|
||||
to_reg,
|
||||
));
|
||||
}
|
||||
} else if ty == types::F64 {
|
||||
if value == 0 {
|
||||
ret.push(Inst::xmm_rm_r(
|
||||
SseOpcode::Xorpd,
|
||||
RegMem::reg(to_reg.to_reg()),
|
||||
to_reg,
|
||||
));
|
||||
} else {
|
||||
let tmp = alloc_tmp(RegClass::I64, types::I64);
|
||||
ret.push(Inst::imm(OperandSize::Size64, value, tmp));
|
||||
|
||||
ret.push(Inst::gpr_to_xmm(
|
||||
SseOpcode::Movq,
|
||||
RegMem::reg(tmp.to_reg()),
|
||||
OperandSize::Size64,
|
||||
to_reg,
|
||||
));
|
||||
}
|
||||
} else {
|
||||
// Must be an integer type.
|
||||
debug_assert!(
|
||||
ty == types::B1
|
||||
|| ty == types::I8
|
||||
|| ty == types::B8
|
||||
|| ty == types::I16
|
||||
|| ty == types::B16
|
||||
|| ty == types::I32
|
||||
|| ty == types::B32
|
||||
|| ty == types::I64
|
||||
|| ty == types::B64
|
||||
|| ty == types::R32
|
||||
|| ty == types::R64
|
||||
);
|
||||
if value == 0 {
|
||||
ret.push(Inst::alu_rmi_r(
|
||||
ty == types::I64,
|
||||
@@ -2416,42 +2453,11 @@ impl MachInst for Inst {
|
||||
to_reg,
|
||||
));
|
||||
} else {
|
||||
let is_64 = ty == types::I64 && value > 0x7fffffff;
|
||||
ret.push(Inst::imm_r(is_64, value, to_reg));
|
||||
}
|
||||
} else if value == 0 {
|
||||
ret.push(Inst::xmm_rm_r(
|
||||
SseOpcode::Xorps,
|
||||
RegMem::reg(to_reg.to_reg()),
|
||||
to_reg,
|
||||
));
|
||||
} else {
|
||||
match ty {
|
||||
types::F32 => {
|
||||
let tmp = alloc_tmp(RegClass::I64, types::I32);
|
||||
ret.push(Inst::imm32_r_unchecked(value, tmp));
|
||||
|
||||
ret.push(Inst::gpr_to_xmm(
|
||||
SseOpcode::Movd,
|
||||
RegMem::reg(tmp.to_reg()),
|
||||
OperandSize::Size32,
|
||||
to_reg,
|
||||
));
|
||||
}
|
||||
|
||||
types::F64 => {
|
||||
let tmp = alloc_tmp(RegClass::I64, types::I64);
|
||||
ret.push(Inst::imm_r(true, value, tmp));
|
||||
|
||||
ret.push(Inst::gpr_to_xmm(
|
||||
SseOpcode::Movq,
|
||||
RegMem::reg(tmp.to_reg()),
|
||||
OperandSize::Size64,
|
||||
to_reg,
|
||||
));
|
||||
}
|
||||
|
||||
_ => panic!("unexpected type {:?} in gen_constant", ty),
|
||||
ret.push(Inst::imm(
|
||||
OperandSize::from_bytes(ty.bytes()),
|
||||
value,
|
||||
to_reg,
|
||||
));
|
||||
}
|
||||
}
|
||||
ret
|
||||
|
||||
Reference in New Issue
Block a user