From eda2d143eddcd7a43ba87d9137f478e662108076 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Fri, 3 Jul 2020 16:15:27 +0200 Subject: [PATCH] machinst x64: add support for umulhi/smulhi; --- cranelift/codegen/src/isa/x64/inst/emit.rs | 26 +++++++++++++++++++ .../codegen/src/isa/x64/inst/emit_tests.rs | 23 ++++++++++++++++ cranelift/codegen/src/isa/x64/inst/mod.rs | 26 +++++++++++++++++++ cranelift/codegen/src/isa/x64/lower.rs | 23 ++++++++++++++++ 4 files changed, 98 insertions(+) diff --git a/cranelift/codegen/src/isa/x64/inst/emit.rs b/cranelift/codegen/src/isa/x64/inst/emit.rs index b38a7d8846..414f033194 100644 --- a/cranelift/codegen/src/isa/x64/inst/emit.rs +++ b/cranelift/codegen/src/isa/x64/inst/emit.rs @@ -589,6 +589,32 @@ pub(crate) fn emit( } } + Inst::MulHi { size, signed, rhs } => { + let (prefix, rex_flags) = match size { + 2 => (LegacyPrefix::_66, RexFlags::clear_w()), + 4 => (LegacyPrefix::None, RexFlags::clear_w()), + 8 => (LegacyPrefix::None, RexFlags::set_w()), + _ => unreachable!(), + }; + + let subopcode = if *signed { 5 } else { 4 }; + match rhs { + RegMem::Reg { reg } => { + let src = int_reg_enc(*reg); + emit_std_enc_enc(sink, prefix, 0xF7, 1, subopcode, src, rex_flags) + } + RegMem::Mem { addr: src } => emit_std_enc_mem( + sink, + prefix, + 0xF7, + 1, + subopcode, + &src.finalize(state), + rex_flags, + ), + } + } + Inst::SignExtendRaxRdx { size } => { match size { 2 => sink.put1(0x66), diff --git a/cranelift/codegen/src/isa/x64/inst/emit_tests.rs b/cranelift/codegen/src/isa/x64/inst/emit_tests.rs index 77b1197100..b18115a814 100644 --- a/cranelift/codegen/src/isa/x64/inst/emit_tests.rs +++ b/cranelift/codegen/src/isa/x64/inst/emit_tests.rs @@ -1197,6 +1197,29 @@ fn test_x64_emit() { "div %rdi", )); + // ======================================================== + // MulHi + insns.push(( + Inst::mul_hi(4, true /*signed*/, RegMem::reg(regs::rsi())), + "F7EE", + "imul %esi", + )); + insns.push(( + Inst::mul_hi(8, true /*signed*/, RegMem::reg(regs::r15())), + "49F7EF", + "imul %r15", + )); + insns.push(( + Inst::mul_hi(4, false /*signed*/, RegMem::reg(regs::r14())), + "41F7E6", + "mul %r14d", + )); + insns.push(( + Inst::mul_hi(8, false /*signed*/, RegMem::reg(regs::rdi())), + "48F7E7", + "mul %rdi", + )); + // ======================================================== // cdq family: SignExtendRaxRdx insns.push((Inst::sign_extend_rax_to_rdx(2), "6699", "cwd")); diff --git a/cranelift/codegen/src/isa/x64/inst/mod.rs b/cranelift/codegen/src/isa/x64/inst/mod.rs index ca222ebbe5..327d35f828 100644 --- a/cranelift/codegen/src/isa/x64/inst/mod.rs +++ b/cranelift/codegen/src/isa/x64/inst/mod.rs @@ -57,6 +57,9 @@ pub enum Inst { loc: SourceLoc, }, + /// The high result of a (un)signed multiply: imul/mul using RAX:RDX. + MulHi { size: u8, signed: bool, rhs: RegMem }, + /// A synthetic sequence to implement the right inline checks for remainder and division, /// assuming the dividend is in $rax. /// Puts the result back into $rax if is_div, $rdx if !is_div, to mimic what the div @@ -301,6 +304,12 @@ impl Inst { loc, } } + + pub(crate) fn mul_hi(size: u8, signed: bool, rhs: RegMem) -> Inst { + debug_assert!(size == 8 || size == 4 || size == 2 || size == 1); + Inst::MulHi { size, signed, rhs } + } + pub(crate) fn sign_extend_rax_to_rdx(size: u8) -> Inst { debug_assert!(size == 8 || size == 4 || size == 2); Inst::SignExtendRaxRdx { size } @@ -581,6 +590,17 @@ impl ShowWithRRU for Inst { }), divisor.show_rru_sized(mb_rru, *size) ), + Inst::MulHi { + size, signed, rhs, .. + } => format!( + "{} {}", + ljustify(if *signed { + "imul".to_string() + } else { + "mul".to_string() + }), + rhs.show_rru_sized(mb_rru, *size) + ), Inst::CheckedDivOrRemSeq { is_div, is_signed, @@ -796,6 +816,11 @@ fn x64_get_regs(inst: &Inst, collector: &mut RegUsageCollector) { collector.add_mod(Writable::from_reg(regs::rdx())); divisor.get_regs_as_uses(collector); } + Inst::MulHi { rhs, .. } => { + collector.add_mod(Writable::from_reg(regs::rax())); + collector.add_def(Writable::from_reg(regs::rdx())); + rhs.get_regs_as_uses(collector); + } Inst::CheckedDivOrRemSeq { divisor, .. } => { collector.add_mod(Writable::from_reg(regs::rax())); collector.add_mod(Writable::from_reg(regs::rdx())); @@ -994,6 +1019,7 @@ fn x64_map_regs(inst: &mut Inst, mapper: &RUM) { map_mod(mapper, dst); } Inst::Div { divisor, .. } => divisor.map_uses(mapper), + Inst::MulHi { rhs, .. } => rhs.map_uses(mapper), Inst::CheckedDivOrRemSeq { divisor, .. } => { map_use(mapper, divisor); } diff --git a/cranelift/codegen/src/isa/x64/lower.rs b/cranelift/codegen/src/isa/x64/lower.rs index f7073cacaa..a2034e754c 100644 --- a/cranelift/codegen/src/isa/x64/lower.rs +++ b/cranelift/codegen/src/isa/x64/lower.rs @@ -747,6 +747,29 @@ fn lower_insn_to_regs>( } } + Opcode::Umulhi | Opcode::Smulhi => { + let input_ty = ctx.input_ty(insn, 0); + let size = input_ty.bytes() as u8; + + let lhs = input_to_reg(ctx, inputs[0]); + let rhs = input_to_reg_mem(ctx, inputs[1]); + let dst = output_to_reg(ctx, outputs[0]); + + // Move lhs in %rax. + ctx.emit(Inst::gen_move( + Writable::from_reg(regs::rax()), + lhs, + input_ty, + )); + + // Emit the actual mul or imul. + let signed = op == Opcode::Smulhi; + ctx.emit(Inst::mul_hi(size, signed, rhs)); + + // Read the result from the high part (stored in %rdx). + ctx.emit(Inst::gen_move(dst, regs::rdx(), input_ty)); + } + Opcode::IaddImm | Opcode::ImulImm | Opcode::UdivImm