From: Tim Northover Date: Wed, 8 Feb 2017 21:22:15 +0000 (+0000) Subject: GlobalISel: expand mul-with-overflow into mul-hi on AArch64. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=063022b81a07430065992545a619835893b24d96;p=llvm GlobalISel: expand mul-with-overflow into mul-hi on AArch64. AArch64 has specific instructions to multiply two numbers at double the width and produce the high part of the result. These can be used to implement LLVM's mul.with.overflow instructions fairly simply. Helps with C++ operator new[]. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@294519 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/llvm/Target/GenericOpcodes.td b/include/llvm/Target/GenericOpcodes.td index 6352a6990f2..8d01bd2c5bd 100644 --- a/include/llvm/Target/GenericOpcodes.td +++ b/include/llvm/Target/GenericOpcodes.td @@ -280,6 +280,24 @@ def G_SMULO : Instruction { let isCommutable = 1; } +// Multiply two numbers at twice the incoming bit width (unsigned) and return +// the high half of the result. +def G_UMULH : Instruction { + let OutOperandList = (outs type0:$dst); + let InOperandList = (ins type0:$src1, type0:$src2); + let hasSideEffects = 0; + let isCommutable = 1; +} + +// Multiply two numbers at twice the incoming bit width (signed) and return +// the high half of the result. +def G_SMULH : Instruction { + let OutOperandList = (outs type0:$dst); + let InOperandList = (ins type0:$src1, type0:$src2); + let hasSideEffects = 0; + let isCommutable = 1; +} + //------------------------------------------------------------------------------ // Floating Point Unary Ops. //------------------------------------------------------------------------------ diff --git a/include/llvm/Target/TargetOpcodes.def b/include/llvm/Target/TargetOpcodes.def index a0d6a6309dc..8f209ecb03b 100644 --- a/include/llvm/Target/TargetOpcodes.def +++ b/include/llvm/Target/TargetOpcodes.def @@ -331,6 +331,14 @@ HANDLE_TARGET_OPCODE(G_UMULO) /// overflow flag. HANDLE_TARGET_OPCODE(G_SMULO) +// Multiply two numbers at twice the incoming bit width (unsigned) and return +// the high half of the result. +HANDLE_TARGET_OPCODE(G_UMULH) + +// Multiply two numbers at twice the incoming bit width (signed) and return +// the high half of the result. +HANDLE_TARGET_OPCODE(G_SMULH) + /// Generic FP addition. HANDLE_TARGET_OPCODE(G_FADD) diff --git a/lib/CodeGen/GlobalISel/LegalizerHelper.cpp b/lib/CodeGen/GlobalISel/LegalizerHelper.cpp index aeb89a41aa9..f41af0547a3 100644 --- a/lib/CodeGen/GlobalISel/LegalizerHelper.cpp +++ b/lib/CodeGen/GlobalISel/LegalizerHelper.cpp @@ -519,6 +519,33 @@ LegalizerHelper::lower(MachineInstr &MI, unsigned TypeIdx, LLT Ty) { MI.eraseFromParent(); return Legalized; } + case TargetOpcode::G_SMULO: + case TargetOpcode::G_UMULO: { + // Generate G_UMULH/G_SMULH to check for overflow and a normal G_MUL for the + // result. + unsigned Res = MI.getOperand(0).getReg(); + unsigned Overflow = MI.getOperand(1).getReg(); + unsigned LHS = MI.getOperand(2).getReg(); + unsigned RHS = MI.getOperand(3).getReg(); + + MIRBuilder.buildMul(Res, LHS, RHS); + + unsigned Opcode = MI.getOpcode() == TargetOpcode::G_SMULO + ? TargetOpcode::G_SMULH + : TargetOpcode::G_UMULH; + + unsigned HiPart = MRI.createGenericVirtualRegister(Ty); + MIRBuilder.buildInstr(Opcode) + .addDef(HiPart) + .addUse(LHS) + .addUse(RHS); + + unsigned Zero = MRI.createGenericVirtualRegister(Ty); + MIRBuilder.buildConstant(Zero, 0); + MIRBuilder.buildICmp(CmpInst::ICMP_NE, Overflow, HiPart, Zero); + MI.eraseFromParent(); + return Legalized; + } } } diff --git a/lib/Target/AArch64/AArch64LegalizerInfo.cpp b/lib/Target/AArch64/AArch64LegalizerInfo.cpp index ad0482ac7d5..32f9c5f80d0 100644 --- a/lib/Target/AArch64/AArch64LegalizerInfo.cpp +++ b/lib/Target/AArch64/AArch64LegalizerInfo.cpp @@ -64,7 +64,10 @@ AArch64LegalizerInfo::AArch64LegalizerInfo() { for (auto Ty : { s1, s8, s16, s32, s64 }) setAction({BinOp, Ty}, Lower); - for (unsigned Op : {G_UADDE, G_USUBE, G_SADDO, G_SSUBO, G_SMULO, G_UMULO}) { + for (unsigned Op : {G_SMULO, G_UMULO}) + setAction({Op, s64}, Lower); + + for (unsigned Op : {G_UADDE, G_USUBE, G_SADDO, G_SSUBO, G_SMULH, G_UMULH}) { for (auto Ty : { s32, s64 }) setAction({Op, Ty}, Legal); diff --git a/test/CodeGen/AArch64/GlobalISel/legalize-mul.mir b/test/CodeGen/AArch64/GlobalISel/legalize-mul.mir index e56eef0bc4f..36726b7a30c 100644 --- a/test/CodeGen/AArch64/GlobalISel/legalize-mul.mir +++ b/test/CodeGen/AArch64/GlobalISel/legalize-mul.mir @@ -7,6 +7,7 @@ entry: ret void } + define void @test_mul_overflow() { ret void } ... --- @@ -35,3 +36,22 @@ body: | %5(s64) = G_ANYEXT %2 %x0 = COPY %5 ... + + +--- +name: test_mul_overflow +body: | + bb.0: + liveins: %x0, %x1, %w2, %w3 + + %0:_(s64) = COPY %x0 + %1:_(s64) = COPY %x1 + + ; CHECK-LABEL: name: test_mul_overflow + ; CHECK: %2(s64) = G_MUL %0, %1 + ; CHECK: [[HI:%[0-9]+]](s64) = G_SMULH %0, %1 + ; CHECK: [[ZERO:%[0-9]+]](s64) = G_CONSTANT i64 0 + ; CHECK: %3(s1) = G_ICMP intpred(ne), [[HI]](s64), [[ZERO]] + %2:_(s64), %3:_(s1) = G_SMULO %0, %1 + +...