x64: Implement ineg and bnot
This commit is contained in:
@@ -2782,7 +2782,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Opcode::TlsValue => unimplemented!(),
|
Opcode::TlsValue => unimplemented!("tls_value"),
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -628,6 +628,34 @@ pub(crate) fn emit(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Inst::Not { size, src } => {
|
||||||
|
let (opcode, prefix, rex_flags) = match size {
|
||||||
|
1 => (0xF6, LegacyPrefixes::None, RexFlags::clear_w()),
|
||||||
|
2 => (0xF7, LegacyPrefixes::_66, RexFlags::clear_w()),
|
||||||
|
4 => (0xF7, LegacyPrefixes::None, RexFlags::clear_w()),
|
||||||
|
8 => (0xF7, LegacyPrefixes::None, RexFlags::set_w()),
|
||||||
|
_ => unreachable!("{}", size),
|
||||||
|
};
|
||||||
|
|
||||||
|
let subopcode = 2;
|
||||||
|
let src = int_reg_enc(src.to_reg());
|
||||||
|
emit_std_enc_enc(sink, prefix, opcode, 1, subopcode, src, rex_flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
Inst::Neg { size, src } => {
|
||||||
|
let (opcode, prefix, rex_flags) = match size {
|
||||||
|
1 => (0xF6, LegacyPrefixes::None, RexFlags::clear_w()),
|
||||||
|
2 => (0xF7, LegacyPrefixes::_66, RexFlags::clear_w()),
|
||||||
|
4 => (0xF7, LegacyPrefixes::None, RexFlags::clear_w()),
|
||||||
|
8 => (0xF7, LegacyPrefixes::None, RexFlags::set_w()),
|
||||||
|
_ => unreachable!("{}", size),
|
||||||
|
};
|
||||||
|
|
||||||
|
let subopcode = 3;
|
||||||
|
let src = int_reg_enc(src.to_reg());
|
||||||
|
emit_std_enc_enc(sink, prefix, opcode, 1, subopcode, src, rex_flags)
|
||||||
|
}
|
||||||
|
|
||||||
Inst::Div {
|
Inst::Div {
|
||||||
size,
|
size,
|
||||||
signed,
|
signed,
|
||||||
|
|||||||
@@ -1242,6 +1242,52 @@ fn test_x64_emit() {
|
|||||||
"bsrq %r15, %rax",
|
"bsrq %r15, %rax",
|
||||||
));
|
));
|
||||||
|
|
||||||
|
// ========================================================
|
||||||
|
// Not
|
||||||
|
insns.push((
|
||||||
|
Inst::not(4, Writable::from_reg(regs::rsi())),
|
||||||
|
"F7D6",
|
||||||
|
"notl %esi",
|
||||||
|
));
|
||||||
|
insns.push((
|
||||||
|
Inst::not(8, Writable::from_reg(regs::r15())),
|
||||||
|
"49F7D7",
|
||||||
|
"notq %r15",
|
||||||
|
));
|
||||||
|
insns.push((
|
||||||
|
Inst::not(4, Writable::from_reg(regs::r14())),
|
||||||
|
"41F7D6",
|
||||||
|
"notl %r14d",
|
||||||
|
));
|
||||||
|
insns.push((
|
||||||
|
Inst::not(2, Writable::from_reg(regs::rdi())),
|
||||||
|
"66F7D7",
|
||||||
|
"notw %di",
|
||||||
|
));
|
||||||
|
|
||||||
|
// ========================================================
|
||||||
|
// Neg
|
||||||
|
insns.push((
|
||||||
|
Inst::neg(4, Writable::from_reg(regs::rsi())),
|
||||||
|
"F7DE",
|
||||||
|
"negl %esi",
|
||||||
|
));
|
||||||
|
insns.push((
|
||||||
|
Inst::neg(8, Writable::from_reg(regs::r15())),
|
||||||
|
"49F7DF",
|
||||||
|
"negq %r15",
|
||||||
|
));
|
||||||
|
insns.push((
|
||||||
|
Inst::neg(4, Writable::from_reg(regs::r14())),
|
||||||
|
"41F7DE",
|
||||||
|
"negl %r14d",
|
||||||
|
));
|
||||||
|
insns.push((
|
||||||
|
Inst::neg(2, Writable::from_reg(regs::rdi())),
|
||||||
|
"66F7DF",
|
||||||
|
"negw %di",
|
||||||
|
));
|
||||||
|
|
||||||
// ========================================================
|
// ========================================================
|
||||||
// Div
|
// Div
|
||||||
insns.push((
|
insns.push((
|
||||||
|
|||||||
@@ -56,6 +56,18 @@ pub enum Inst {
|
|||||||
dst: Writable<Reg>,
|
dst: Writable<Reg>,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Bitwise negation
|
||||||
|
Not {
|
||||||
|
size: u8, // 1, 2, 4 or 8
|
||||||
|
src: Writable<Reg>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Integer negation
|
||||||
|
Neg {
|
||||||
|
size: u8, // 1, 2, 4 or 8
|
||||||
|
src: Writable<Reg>,
|
||||||
|
},
|
||||||
|
|
||||||
/// Integer quotient and remainder: (div idiv) $rax $rdx (reg addr)
|
/// Integer quotient and remainder: (div idiv) $rax $rdx (reg addr)
|
||||||
Div {
|
Div {
|
||||||
size: u8, // 1, 2, 4 or 8
|
size: u8, // 1, 2, 4 or 8
|
||||||
@@ -512,6 +524,18 @@ impl Inst {
|
|||||||
Self::UnaryRmR { size, op, src, dst }
|
Self::UnaryRmR { size, op, src, dst }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn not(size: u8, src: Writable<Reg>) -> Inst {
|
||||||
|
debug_assert_eq!(src.to_reg().get_class(), RegClass::I64);
|
||||||
|
debug_assert!(size == 8 || size == 4 || size == 2 || size == 1);
|
||||||
|
Inst::Not { size, src }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn neg(size: u8, src: Writable<Reg>) -> Inst {
|
||||||
|
debug_assert_eq!(src.to_reg().get_class(), RegClass::I64);
|
||||||
|
debug_assert!(size == 8 || size == 4 || size == 2 || size == 1);
|
||||||
|
Inst::Neg { size, src }
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn div(size: u8, signed: bool, divisor: RegMem, loc: SourceLoc) -> Inst {
|
pub(crate) fn div(size: u8, signed: bool, divisor: RegMem, loc: SourceLoc) -> Inst {
|
||||||
divisor.assert_regclass_is(RegClass::I64);
|
divisor.assert_regclass_is(RegClass::I64);
|
||||||
debug_assert!(size == 8 || size == 4 || size == 2 || size == 1);
|
debug_assert!(size == 8 || size == 4 || size == 2 || size == 1);
|
||||||
@@ -1180,6 +1204,18 @@ impl ShowWithRRU for Inst {
|
|||||||
show_ireg_sized(dst.to_reg(), mb_rru, *size),
|
show_ireg_sized(dst.to_reg(), mb_rru, *size),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
Inst::Not { size, src } => format!(
|
||||||
|
"{} {}",
|
||||||
|
ljustify2("not".to_string(), suffixBWLQ(*size)),
|
||||||
|
show_ireg_sized(src.to_reg(), mb_rru, *size)
|
||||||
|
),
|
||||||
|
|
||||||
|
Inst::Neg { size, src } => format!(
|
||||||
|
"{} {}",
|
||||||
|
ljustify2("neg".to_string(), suffixBWLQ(*size)),
|
||||||
|
show_ireg_sized(src.to_reg(), mb_rru, *size)
|
||||||
|
),
|
||||||
|
|
||||||
Inst::Div {
|
Inst::Div {
|
||||||
size,
|
size,
|
||||||
signed,
|
signed,
|
||||||
@@ -1645,6 +1681,12 @@ fn x64_get_regs(inst: &Inst, collector: &mut RegUsageCollector) {
|
|||||||
collector.add_mod(*dst);
|
collector.add_mod(*dst);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Inst::Not { src, .. } => {
|
||||||
|
collector.add_mod(*src);
|
||||||
|
}
|
||||||
|
Inst::Neg { src, .. } => {
|
||||||
|
collector.add_mod(*src);
|
||||||
|
}
|
||||||
Inst::Div { divisor, .. } => {
|
Inst::Div { divisor, .. } => {
|
||||||
collector.add_mod(Writable::from_reg(regs::rax()));
|
collector.add_mod(Writable::from_reg(regs::rax()));
|
||||||
collector.add_mod(Writable::from_reg(regs::rdx()));
|
collector.add_mod(Writable::from_reg(regs::rdx()));
|
||||||
@@ -1961,6 +2003,7 @@ fn x64_map_regs<RUM: RegUsageMapper>(inst: &mut Inst, mapper: &RUM) {
|
|||||||
map_mod(mapper, dst);
|
map_mod(mapper, dst);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Inst::Not { src, .. } | Inst::Neg { src, .. } => map_mod(mapper, src),
|
||||||
Inst::Div { divisor, .. } => divisor.map_uses(mapper),
|
Inst::Div { divisor, .. } => divisor.map_uses(mapper),
|
||||||
Inst::MulHi { rhs, .. } => rhs.map_uses(mapper),
|
Inst::MulHi { rhs, .. } => rhs.map_uses(mapper),
|
||||||
Inst::CheckedDivOrRemSeq { divisor, tmp, .. } => {
|
Inst::CheckedDivOrRemSeq { divisor, tmp, .. } => {
|
||||||
|
|||||||
@@ -614,6 +614,21 @@ fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Opcode::Bnot => {
|
||||||
|
let ty = ty.unwrap();
|
||||||
|
if ty.is_vector() {
|
||||||
|
unimplemented!("vector bnot");
|
||||||
|
} else if ty.is_bool() {
|
||||||
|
unimplemented!("bool bnot")
|
||||||
|
} else {
|
||||||
|
let size = ty.bytes() as u8;
|
||||||
|
let src = input_to_reg(ctx, inputs[0]);
|
||||||
|
let dst = output_to_reg(ctx, outputs[0]);
|
||||||
|
ctx.emit(Inst::gen_move(dst, src, ty));
|
||||||
|
ctx.emit(Inst::not(size, dst));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Opcode::Ishl | Opcode::Ushr | Opcode::Sshr | Opcode::Rotl | Opcode::Rotr => {
|
Opcode::Ishl | Opcode::Ushr | Opcode::Sshr | Opcode::Rotl | Opcode::Rotr => {
|
||||||
let dst_ty = ctx.output_ty(insn, 0);
|
let dst_ty = ctx.output_ty(insn, 0);
|
||||||
debug_assert_eq!(ctx.input_ty(insn, 0), dst_ty);
|
debug_assert_eq!(ctx.input_ty(insn, 0), dst_ty);
|
||||||
@@ -654,34 +669,43 @@ fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Opcode::Ineg => {
|
Opcode::Ineg => {
|
||||||
// Zero's out a register and then does a packed subtraction
|
|
||||||
// of the input from the register.
|
|
||||||
let src = input_to_reg_mem(ctx, inputs[0]);
|
|
||||||
let dst = output_to_reg(ctx, outputs[0]);
|
let dst = output_to_reg(ctx, outputs[0]);
|
||||||
let tmp = ctx.alloc_tmp(RegClass::V128, types::I32X4);
|
|
||||||
let ty = ty.unwrap();
|
let ty = ty.unwrap();
|
||||||
|
|
||||||
let subtract_opcode = match ty {
|
if ty.is_vector() {
|
||||||
types::I8X16 => SseOpcode::Psubb,
|
// Zero's out a register and then does a packed subtraction
|
||||||
types::I16X8 => SseOpcode::Psubw,
|
// of the input from the register.
|
||||||
types::I32X4 => SseOpcode::Psubd,
|
|
||||||
types::I64X2 => SseOpcode::Psubq,
|
|
||||||
_ => panic!("Unsupported type for Ineg instruction, found {}", ty),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Note we must zero out a tmp instead of using the destination register since
|
let src = input_to_reg_mem(ctx, inputs[0]);
|
||||||
// the desitnation could be an alias for the source input register
|
let tmp = ctx.alloc_tmp(RegClass::V128, types::I32X4);
|
||||||
ctx.emit(Inst::xmm_rm_r(
|
|
||||||
SseOpcode::Pxor,
|
let subtract_opcode = match ty {
|
||||||
RegMem::reg(tmp.to_reg()),
|
types::I8X16 => SseOpcode::Psubb,
|
||||||
tmp,
|
types::I16X8 => SseOpcode::Psubw,
|
||||||
));
|
types::I32X4 => SseOpcode::Psubd,
|
||||||
ctx.emit(Inst::xmm_rm_r(subtract_opcode, src, tmp));
|
types::I64X2 => SseOpcode::Psubq,
|
||||||
ctx.emit(Inst::xmm_unary_rm_r(
|
_ => panic!("Unsupported type for Ineg instruction, found {}", ty),
|
||||||
SseOpcode::Movapd,
|
};
|
||||||
RegMem::reg(tmp.to_reg()),
|
|
||||||
dst,
|
// Note we must zero out a tmp instead of using the destination register since
|
||||||
));
|
// the desitnation could be an alias for the source input register
|
||||||
|
ctx.emit(Inst::xmm_rm_r(
|
||||||
|
SseOpcode::Pxor,
|
||||||
|
RegMem::reg(tmp.to_reg()),
|
||||||
|
tmp,
|
||||||
|
));
|
||||||
|
ctx.emit(Inst::xmm_rm_r(subtract_opcode, src, tmp));
|
||||||
|
ctx.emit(Inst::xmm_unary_rm_r(
|
||||||
|
SseOpcode::Movapd,
|
||||||
|
RegMem::reg(tmp.to_reg()),
|
||||||
|
dst,
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
let size = ty.bytes() as u8;
|
||||||
|
let src = input_to_reg(ctx, inputs[0]);
|
||||||
|
ctx.emit(Inst::gen_move(dst, src, ty));
|
||||||
|
ctx.emit(Inst::neg(size, dst));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Opcode::Clz => {
|
Opcode::Clz => {
|
||||||
|
|||||||
Reference in New Issue
Block a user