diff --git a/llvm/include/llvm/CodeGen/GlobalISel/GenericMachineInstrs.h b/llvm/include/llvm/CodeGen/GlobalISel/GenericMachineInstrs.h index b6309a9ea0ec7..cd7ebcf54c9e1 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/GenericMachineInstrs.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/GenericMachineInstrs.h @@ -28,7 +28,7 @@ namespace llvm { class GenericMachineInstr : public MachineInstr { constexpr static unsigned PoisonFlags = NoUWrap | NoSWrap | NoUSWrap | IsExact | Disjoint | NonNeg | - FmNoNans | FmNoInfs; + FmNoNans | FmNoInfs | SameSign; public: GenericMachineInstr() = delete; diff --git a/llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h b/llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h index c41e74ec7ebdc..14a641512a67d 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h @@ -1266,7 +1266,8 @@ class MachineIRBuilder { /// /// \return a MachineInstrBuilder for the newly created instruction. MachineInstrBuilder buildICmp(CmpInst::Predicate Pred, const DstOp &Res, - const SrcOp &Op0, const SrcOp &Op1); + const SrcOp &Op0, const SrcOp &Op1, + std::optional Flgs = std::nullopt); /// Build and insert a \p Res = G_FCMP \p Pred\p Op0, \p Op1 /// diff --git a/llvm/include/llvm/CodeGen/MachineInstr.h b/llvm/include/llvm/CodeGen/MachineInstr.h index 3605173247463..ead6bbe1d5f64 100644 --- a/llvm/include/llvm/CodeGen/MachineInstr.h +++ b/llvm/include/llvm/CodeGen/MachineInstr.h @@ -119,6 +119,7 @@ class MachineInstr Disjoint = 1 << 19, // Each bit is zero in at least one of the inputs. NoUSWrap = 1 << 20, // Instruction supports geps // no unsigned signed wrap. + SameSign = 1 << 21 // Both operands have the same sign. }; private: diff --git a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp index 5381dce58f9e6..a87754389cc8e 100644 --- a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp +++ b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp @@ -340,20 +340,17 @@ bool IRTranslator::translateCompare(const User &U, Register Op1 = getOrCreateVReg(*U.getOperand(1)); Register Res = getOrCreateVReg(U); CmpInst::Predicate Pred = CI->getPredicate(); + uint32_t Flags = MachineInstr::copyFlagsFromInstruction(*CI); if (CmpInst::isIntPredicate(Pred)) - MIRBuilder.buildICmp(Pred, Res, Op0, Op1); + MIRBuilder.buildICmp(Pred, Res, Op0, Op1, Flags); else if (Pred == CmpInst::FCMP_FALSE) MIRBuilder.buildCopy( Res, getOrCreateVReg(*Constant::getNullValue(U.getType()))); else if (Pred == CmpInst::FCMP_TRUE) MIRBuilder.buildCopy( Res, getOrCreateVReg(*Constant::getAllOnesValue(U.getType()))); - else { - uint32_t Flags = 0; - if (CI) - Flags = MachineInstr::copyFlagsFromInstruction(*CI); + else MIRBuilder.buildFCmp(Pred, Res, Op0, Op1, Flags); - } return true; } diff --git a/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp b/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp index 59f2fc633f5de..15b9164247846 100644 --- a/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp +++ b/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp @@ -898,8 +898,9 @@ MachineIRBuilder::buildFPTrunc(const DstOp &Res, const SrcOp &Op, MachineInstrBuilder MachineIRBuilder::buildICmp(CmpInst::Predicate Pred, const DstOp &Res, const SrcOp &Op0, - const SrcOp &Op1) { - return buildInstr(TargetOpcode::G_ICMP, Res, {Pred, Op0, Op1}); + const SrcOp &Op1, + std::optional Flags) { + return buildInstr(TargetOpcode::G_ICMP, Res, {Pred, Op0, Op1}, Flags); } MachineInstrBuilder MachineIRBuilder::buildFCmp(CmpInst::Predicate Pred, diff --git a/llvm/lib/CodeGen/MIRParser/MILexer.cpp b/llvm/lib/CodeGen/MIRParser/MILexer.cpp index 5a3806ce57335..1c450b05f49e9 100644 --- a/llvm/lib/CodeGen/MIRParser/MILexer.cpp +++ b/llvm/lib/CodeGen/MIRParser/MILexer.cpp @@ -216,6 +216,7 @@ static MIToken::TokenKind getIdentifierKind(StringRef Identifier) { .Case("exact", MIToken::kw_exact) .Case("nneg", MIToken::kw_nneg) .Case("disjoint", MIToken::kw_disjoint) + .Case("samesign", MIToken::kw_samesign) .Case("nofpexcept", MIToken::kw_nofpexcept) .Case("unpredictable", MIToken::kw_unpredictable) .Case("debug-location", MIToken::kw_debug_location) diff --git a/llvm/lib/CodeGen/MIRParser/MILexer.h b/llvm/lib/CodeGen/MIRParser/MILexer.h index 3931da3eaae1d..d7cd06759cfbb 100644 --- a/llvm/lib/CodeGen/MIRParser/MILexer.h +++ b/llvm/lib/CodeGen/MIRParser/MILexer.h @@ -77,6 +77,7 @@ struct MIToken { kw_unpredictable, kw_nneg, kw_disjoint, + kw_samesign, kw_debug_location, kw_debug_instr_number, kw_dbg_instr_ref, diff --git a/llvm/lib/CodeGen/MIRParser/MIParser.cpp b/llvm/lib/CodeGen/MIRParser/MIParser.cpp index 45847b5830da6..059814c70f828 100644 --- a/llvm/lib/CodeGen/MIRParser/MIParser.cpp +++ b/llvm/lib/CodeGen/MIRParser/MIParser.cpp @@ -1476,7 +1476,8 @@ bool MIParser::parseInstruction(unsigned &OpCode, unsigned &Flags) { Token.is(MIToken::kw_noconvergent) || Token.is(MIToken::kw_unpredictable) || Token.is(MIToken::kw_nneg) || - Token.is(MIToken::kw_disjoint)) { + Token.is(MIToken::kw_disjoint) || + Token.is(MIToken::kw_samesign)) { // clang-format on // Mine frame and fast math flags if (Token.is(MIToken::kw_frame_setup)) @@ -1513,6 +1514,8 @@ bool MIParser::parseInstruction(unsigned &OpCode, unsigned &Flags) { Flags |= MachineInstr::NonNeg; if (Token.is(MIToken::kw_disjoint)) Flags |= MachineInstr::Disjoint; + if (Token.is(MIToken::kw_samesign)) + Flags |= MachineInstr::SameSign; lex(); } diff --git a/llvm/lib/CodeGen/MIRPrinter.cpp b/llvm/lib/CodeGen/MIRPrinter.cpp index a015cd3c2a55f..658bbe0e577e5 100644 --- a/llvm/lib/CodeGen/MIRPrinter.cpp +++ b/llvm/lib/CodeGen/MIRPrinter.cpp @@ -837,6 +837,8 @@ void MIPrinter::print(const MachineInstr &MI) { OS << "disjoint "; if (MI.getFlag(MachineInstr::NoUSWrap)) OS << "nusw "; + if (MI.getFlag(MachineInstr::SameSign)) + OS << "samesign "; OS << TII->getName(MI.getOpcode()); if (I < E) diff --git a/llvm/lib/CodeGen/MachineInstr.cpp b/llvm/lib/CodeGen/MachineInstr.cpp index c1bd0bb5b7162..941861da5c569 100644 --- a/llvm/lib/CodeGen/MachineInstr.cpp +++ b/llvm/lib/CodeGen/MachineInstr.cpp @@ -596,6 +596,11 @@ uint32_t MachineInstr::copyFlagsFromInstruction(const Instruction &I) { MIFlags |= MachineInstr::MIFlag::Disjoint; } + // Copy the samesign flag. + if (const ICmpInst *ICmp = dyn_cast(&I)) + if (ICmp->hasSameSign()) + MIFlags |= MachineInstr::MIFlag::SameSign; + // Copy the exact flag. if (const PossiblyExactOperator *PE = dyn_cast(&I)) if (PE->isExact()) @@ -1770,6 +1775,8 @@ void MachineInstr::print(raw_ostream &OS, ModuleSlotTracker &MST, OS << "nneg "; if (getFlag(MachineInstr::Disjoint)) OS << "disjoint "; + if (getFlag(MachineInstr::SameSign)) + OS << "samesign "; // Print the opcode name. if (TII) diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/icmp-flags.mir b/llvm/test/CodeGen/AArch64/GlobalISel/icmp-flags.mir new file mode 100644 index 0000000000000..59e4de9440416 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/GlobalISel/icmp-flags.mir @@ -0,0 +1,45 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py +# RUN: llc -mtriple aarch64 -run-pass=none -verify-machineinstrs %s -o - | FileCheck %s + +--- +name: icmp_samesign +body: | + bb.0: + liveins: $w0, $w1 + ; CHECK-LABEL: name: icmp_samesign + ; CHECK: liveins: $w0, $w1 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: %x:_(s32) = COPY $w0 + ; CHECK-NEXT: %y:_(s32) = COPY $w1 + ; CHECK-NEXT: %cmp:_(s1) = samesign G_ICMP intpred(eq), %y(s32), %y + ; CHECK-NEXT: %zext:_(s32) = G_ZEXT %cmp(s1) + ; CHECK-NEXT: $w0 = COPY %zext(s32) + ; CHECK-NEXT: RET_ReallyLR implicit $w0 + %x:_(s32) = COPY $w0 + %y:_(s32) = COPY $w1 + %cmp:_(s1) = samesign G_ICMP intpred(eq), %y:_(s32), %y:_ + %zext:_(s32) = G_ZEXT %cmp:_(s1) + $w0 = COPY %zext + RET_ReallyLR implicit $w0 +... +--- +name: icmp_differentsign +body: | + bb.0: + liveins: $w0, $w1 + ; CHECK-LABEL: name: icmp_differentsign + ; CHECK: liveins: $w0, $w1 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: %x:_(s32) = COPY $w0 + ; CHECK-NEXT: %y:_(s32) = COPY $w1 + ; CHECK-NEXT: %cmp:_(s1) = G_ICMP intpred(eq), %y(s32), %y + ; CHECK-NEXT: %zext:_(s32) = G_ZEXT %cmp(s1) + ; CHECK-NEXT: $w0 = COPY %zext(s32) + ; CHECK-NEXT: RET_ReallyLR implicit $w0 + %x:_(s32) = COPY $w0 + %y:_(s32) = COPY $w1 + %cmp:_(s1) = G_ICMP intpred(eq), %y:_(s32), %y:_ + %zext:_(s32) = G_ZEXT %cmp:_(s1) + $w0 = COPY %zext + RET_ReallyLR implicit $w0 +--- diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/irtranslater-samesign.ll b/llvm/test/CodeGen/AArch64/GlobalISel/irtranslater-samesign.ll new file mode 100644 index 0000000000000..0173f92c98220 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/GlobalISel/irtranslater-samesign.ll @@ -0,0 +1,69 @@ +; NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 4 +; RUN: llc -global-isel -mtriple=aarch64-linux-gnu -O0 -stop-after=irtranslator < %s | FileCheck %s + + +define <2 x i1> @call_icmp_samesign_vector(<2 x i32> %a, <2 x i32> %b) { + ; CHECK-LABEL: name: call_icmp_samesign_vector + ; CHECK: bb.1.entry: + ; CHECK-NEXT: liveins: $d0, $d1 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(<2 x s32>) = COPY $d0 + ; CHECK-NEXT: [[COPY1:%[0-9]+]]:_(<2 x s32>) = COPY $d1 + ; CHECK-NEXT: %2:_(<2 x s1>) = samesign G_ICMP intpred(ult), [[COPY]](<2 x s32>), [[COPY1]] + ; CHECK-NEXT: [[ANYEXT:%[0-9]+]]:_(<2 x s32>) = G_ANYEXT %2(<2 x s1>) + ; CHECK-NEXT: $d0 = COPY [[ANYEXT]](<2 x s32>) + ; CHECK-NEXT: RET_ReallyLR implicit $d0 +entry: + %result = icmp samesign ult <2 x i32> %a, %b + ret <2 x i1> %result +} + +define <2 x i1> @call_icmp_vector(<2 x i32> %a, <2 x i32> %b) { + ; CHECK-LABEL: name: call_icmp_vector + ; CHECK: bb.1.entry: + ; CHECK-NEXT: liveins: $d0, $d1 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(<2 x s32>) = COPY $d0 + ; CHECK-NEXT: [[COPY1:%[0-9]+]]:_(<2 x s32>) = COPY $d1 + ; CHECK-NEXT: [[ICMP:%[0-9]+]]:_(<2 x s1>) = G_ICMP intpred(ult), [[COPY]](<2 x s32>), [[COPY1]] + ; CHECK-NEXT: [[ANYEXT:%[0-9]+]]:_(<2 x s32>) = G_ANYEXT [[ICMP]](<2 x s1>) + ; CHECK-NEXT: $d0 = COPY [[ANYEXT]](<2 x s32>) + ; CHECK-NEXT: RET_ReallyLR implicit $d0 +entry: + %result = icmp ult <2 x i32> %a, %b + ret <2 x i1> %result +} + +define i1 @call_icmp(i32 %a) { + ; CHECK-LABEL: name: call_icmp + ; CHECK: bb.1.entry: + ; CHECK-NEXT: liveins: $w0 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $w0 + ; CHECK-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 3 + ; CHECK-NEXT: [[ICMP:%[0-9]+]]:_(s1) = G_ICMP intpred(ult), [[COPY]](s32), [[C]] + ; CHECK-NEXT: [[ZEXT:%[0-9]+]]:_(s8) = G_ZEXT [[ICMP]](s1) + ; CHECK-NEXT: [[ANYEXT:%[0-9]+]]:_(s32) = G_ANYEXT [[ZEXT]](s8) + ; CHECK-NEXT: $w0 = COPY [[ANYEXT]](s32) + ; CHECK-NEXT: RET_ReallyLR implicit $w0 +entry: + %result = icmp ult i32 %a, 3 + ret i1 %result +} + +define i1 @call_icmp_samesign(i32 %a) { + ; CHECK-LABEL: name: call_icmp_samesign + ; CHECK: bb.1.entry: + ; CHECK-NEXT: liveins: $w0 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $w0 + ; CHECK-NEXT: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 3 + ; CHECK-NEXT: %2:_(s1) = samesign G_ICMP intpred(ult), [[COPY]](s32), [[C]] + ; CHECK-NEXT: [[ZEXT:%[0-9]+]]:_(s8) = G_ZEXT %2(s1) + ; CHECK-NEXT: [[ANYEXT:%[0-9]+]]:_(s32) = G_ANYEXT [[ZEXT]](s8) + ; CHECK-NEXT: $w0 = COPY [[ANYEXT]](s32) + ; CHECK-NEXT: RET_ReallyLR implicit $w0 +entry: + %result = icmp samesign ult i32 %a, 3 + ret i1 %result +}