]> granicus.if.org Git - llvm/commitdiff
GlobalISel: expand mul-with-overflow into mul-hi on AArch64.
authorTim Northover <tnorthover@apple.com>
Wed, 8 Feb 2017 21:22:15 +0000 (21:22 +0000)
committerTim Northover <tnorthover@apple.com>
Wed, 8 Feb 2017 21:22:15 +0000 (21:22 +0000)
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

include/llvm/Target/GenericOpcodes.td
include/llvm/Target/TargetOpcodes.def
lib/CodeGen/GlobalISel/LegalizerHelper.cpp
lib/Target/AArch64/AArch64LegalizerInfo.cpp
test/CodeGen/AArch64/GlobalISel/legalize-mul.mir

index 6352a6990f220e4da15590cb590e7da7c8654373..8d01bd2c5bdfc27dee59591dcc391d0cf3454334 100644 (file)
@@ -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.
 //------------------------------------------------------------------------------
index a0d6a6309dc9d49ec560822018371c76b1f37fea..8f209ecb03b21d72a1fe61480ce01e2bd6281938 100644 (file)
@@ -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)
 
index aeb89a41aa993c6af30da54902d31ba85583424c..f41af0547a315f0605ebb6ee812e9190a04f37fa 100644 (file)
@@ -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;
+  }
   }
 }
 
index ad0482ac7d5c30f61f11058a9b64e2976eb450e8..32f9c5f80d0858f6e7d900d0ec95670930727988 100644 (file)
@@ -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);
 
index e56eef0bc4fb910c24da4557444282e7b25a7051..36726b7a30cb2d742337b9d74fc6b74bd5938ab5 100644 (file)
@@ -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
+
+...