From 74642b166f6136db9c87a9f8041d2921a9339b45 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Sat, 22 Aug 2020 10:41:29 +0200 Subject: [PATCH] x64: Implement ineg and bnot --- .../codegen/src/isa/aarch64/lower_inst.rs | 2 +- cranelift/codegen/src/isa/x64/inst/emit.rs | 28 ++++++++ .../codegen/src/isa/x64/inst/emit_tests.rs | 46 ++++++++++++ cranelift/codegen/src/isa/x64/inst/mod.rs | 43 +++++++++++ cranelift/codegen/src/isa/x64/lower.rs | 72 ++++++++++++------- 5 files changed, 166 insertions(+), 25 deletions(-) diff --git a/cranelift/codegen/src/isa/aarch64/lower_inst.rs b/cranelift/codegen/src/isa/aarch64/lower_inst.rs index b52f01364d..54fbd5671a 100644 --- a/cranelift/codegen/src/isa/aarch64/lower_inst.rs +++ b/cranelift/codegen/src/isa/aarch64/lower_inst.rs @@ -2782,7 +2782,7 @@ pub(crate) fn lower_insn_to_regs>( }); } - Opcode::TlsValue => unimplemented!(), + Opcode::TlsValue => unimplemented!("tls_value"), } Ok(()) diff --git a/cranelift/codegen/src/isa/x64/inst/emit.rs b/cranelift/codegen/src/isa/x64/inst/emit.rs index 4d76eec23f..002a97337d 100644 --- a/cranelift/codegen/src/isa/x64/inst/emit.rs +++ b/cranelift/codegen/src/isa/x64/inst/emit.rs @@ -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 { size, signed, diff --git a/cranelift/codegen/src/isa/x64/inst/emit_tests.rs b/cranelift/codegen/src/isa/x64/inst/emit_tests.rs index 12c2ecf707..32bf521203 100644 --- a/cranelift/codegen/src/isa/x64/inst/emit_tests.rs +++ b/cranelift/codegen/src/isa/x64/inst/emit_tests.rs @@ -1242,6 +1242,52 @@ fn test_x64_emit() { "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 insns.push(( diff --git a/cranelift/codegen/src/isa/x64/inst/mod.rs b/cranelift/codegen/src/isa/x64/inst/mod.rs index 74355dff40..268149a238 100644 --- a/cranelift/codegen/src/isa/x64/inst/mod.rs +++ b/cranelift/codegen/src/isa/x64/inst/mod.rs @@ -56,6 +56,18 @@ pub enum Inst { dst: Writable, }, + /// Bitwise negation + Not { + size: u8, // 1, 2, 4 or 8 + src: Writable, + }, + + /// Integer negation + Neg { + size: u8, // 1, 2, 4 or 8 + src: Writable, + }, + /// Integer quotient and remainder: (div idiv) $rax $rdx (reg addr) Div { size: u8, // 1, 2, 4 or 8 @@ -512,6 +524,18 @@ impl Inst { Self::UnaryRmR { size, op, src, dst } } + pub(crate) fn not(size: u8, src: Writable) -> 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) -> 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 { divisor.assert_regclass_is(RegClass::I64); 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), ), + 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 { size, signed, @@ -1645,6 +1681,12 @@ fn x64_get_regs(inst: &Inst, collector: &mut RegUsageCollector) { collector.add_mod(*dst); } } + Inst::Not { src, .. } => { + collector.add_mod(*src); + } + Inst::Neg { src, .. } => { + collector.add_mod(*src); + } Inst::Div { divisor, .. } => { collector.add_mod(Writable::from_reg(regs::rax())); collector.add_mod(Writable::from_reg(regs::rdx())); @@ -1961,6 +2003,7 @@ fn x64_map_regs(inst: &mut Inst, mapper: &RUM) { map_mod(mapper, dst); } } + Inst::Not { src, .. } | Inst::Neg { src, .. } => map_mod(mapper, src), Inst::Div { divisor, .. } => divisor.map_uses(mapper), Inst::MulHi { rhs, .. } => rhs.map_uses(mapper), Inst::CheckedDivOrRemSeq { divisor, tmp, .. } => { diff --git a/cranelift/codegen/src/isa/x64/lower.rs b/cranelift/codegen/src/isa/x64/lower.rs index 41af65cfcc..4c8e5988cb 100644 --- a/cranelift/codegen/src/isa/x64/lower.rs +++ b/cranelift/codegen/src/isa/x64/lower.rs @@ -614,6 +614,21 @@ fn lower_insn_to_regs>( } } + 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 => { let dst_ty = ctx.output_ty(insn, 0); debug_assert_eq!(ctx.input_ty(insn, 0), dst_ty); @@ -654,34 +669,43 @@ fn lower_insn_to_regs>( } 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 tmp = ctx.alloc_tmp(RegClass::V128, types::I32X4); let ty = ty.unwrap(); - let subtract_opcode = match ty { - types::I8X16 => SseOpcode::Psubb, - types::I16X8 => SseOpcode::Psubw, - types::I32X4 => SseOpcode::Psubd, - types::I64X2 => SseOpcode::Psubq, - _ => panic!("Unsupported type for Ineg instruction, found {}", ty), - }; + if ty.is_vector() { + // Zero's out a register and then does a packed subtraction + // of the input from the register. - // 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, - )); + let src = input_to_reg_mem(ctx, inputs[0]); + let tmp = ctx.alloc_tmp(RegClass::V128, types::I32X4); + + let subtract_opcode = match ty { + types::I8X16 => SseOpcode::Psubb, + types::I16X8 => SseOpcode::Psubw, + 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 + // 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 => {