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.
//------------------------------------------------------------------------------
/// 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)
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;
+ }
}
}
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);
entry:
ret void
}
+ define void @test_mul_overflow() { ret void }
...
---
%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
+
+...