diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst index b956e7ca209ae..c9a87fca95cdf 100644 --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -12329,6 +12329,7 @@ Syntax: :: = icmp , ; yields i1 or :result + = icmp samesign , ; yields i1 or :result Overview: """"""""" @@ -12398,6 +12399,9 @@ If the operands are integer vectors, then they are compared element by element. The result is an ``i1`` vector with the same number of elements as the values being compared. Otherwise, the result is an ``i1``. +If the ``samesign`` keyword is present and the operands are not of the +same sign then the result is a :ref:`poison value `. + Example: """""""" diff --git a/llvm/include/llvm/AsmParser/LLToken.h b/llvm/include/llvm/AsmParser/LLToken.h index 19029842a572a..178c911120b4c 100644 --- a/llvm/include/llvm/AsmParser/LLToken.h +++ b/llvm/include/llvm/AsmParser/LLToken.h @@ -114,6 +114,7 @@ enum Kind { kw_disjoint, kw_inbounds, kw_nneg, + kw_samesign, kw_inrange, kw_addrspace, kw_section, diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h index 41d1dc4c22447..08574cc356e51 100644 --- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -540,6 +540,10 @@ enum GetElementPtrOptionalFlags { GEP_NUW = 2, }; +/// ICmpOptionalFlags - Flags for serializing +/// ICmpOptionalFlags's SubclassOptionalData contents. +enum ICmpOptionalFlags { ICMP_SAME_SIGN = 0 }; + /// Encoded AtomicOrdering values. enum AtomicOrderingCodes { ORDERING_NOTATOMIC = 0, diff --git a/llvm/include/llvm/IR/Instructions.h b/llvm/include/llvm/IR/Instructions.h index 695a7a6aa9f25..88c8c709c306d 100644 --- a/llvm/include/llvm/IR/Instructions.h +++ b/llvm/include/llvm/IR/Instructions.h @@ -1165,6 +1165,8 @@ class ICmpInst: public CmpInst { "Invalid operand types for ICmp instruction"); } + enum { SameSign = (1 << 0) }; + protected: // Note: Instruction needs to be a friend here to call cloneImpl. friend class Instruction; @@ -1224,6 +1226,15 @@ class ICmpInst: public CmpInst { /// Return the unsigned version of the predicate. static Predicate getUnsignedPredicate(Predicate pred); + void setSameSign(bool B = true) { + SubclassOptionalData = (SubclassOptionalData & ~SameSign) | (B * SameSign); + } + + /// An icmp instruction, which can be marked as "samesign", indicating that + /// the two operands have the same sign. This means that we can convert + /// "slt" to "ult" and vice versa, which enables more optimizations. + bool hasSameSign() const { return SubclassOptionalData & SameSign; } + /// Return true if this predicate is either EQ or NE. This also /// tests for commutativity. static bool isEquality(Predicate P) { diff --git a/llvm/include/llvm/Transforms/Utils/ScalarEvolutionExpander.h b/llvm/include/llvm/Transforms/Utils/ScalarEvolutionExpander.h index 2806326593649..5697d983c9ad4 100644 --- a/llvm/include/llvm/Transforms/Utils/ScalarEvolutionExpander.h +++ b/llvm/include/llvm/Transforms/Utils/ScalarEvolutionExpander.h @@ -47,6 +47,7 @@ struct PoisonFlags { unsigned Exact : 1; unsigned Disjoint : 1; unsigned NNeg : 1; + unsigned SameSign : 1; GEPNoWrapFlags GEPNW; PoisonFlags(const Instruction *I); diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp index f71cbe1b4b1e8..759db6db60774 100644 --- a/llvm/lib/AsmParser/LLLexer.cpp +++ b/llvm/lib/AsmParser/LLLexer.cpp @@ -583,6 +583,7 @@ lltok::Kind LLLexer::LexIdentifier() { KEYWORD(disjoint); KEYWORD(inbounds); KEYWORD(nneg); + KEYWORD(samesign); KEYWORD(inrange); KEYWORD(addrspace); KEYWORD(section); diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp index 5b9bddeb7cfe8..6a2372c975140 100644 --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -6952,8 +6952,14 @@ int LLParser::parseInstruction(Instruction *&Inst, BasicBlock *BB, case lltok::kw_and: case lltok::kw_xor: return parseLogical(Inst, PFS, KeywordVal); - case lltok::kw_icmp: - return parseCompare(Inst, PFS, KeywordVal); + case lltok::kw_icmp: { + bool SameSign = EatIfPresent(lltok::kw_samesign); + if (parseCompare(Inst, PFS, KeywordVal)) + return true; + if (SameSign) + cast(Inst)->setSameSign(); + return false; + } case lltok::kw_fcmp: { FastMathFlags FMF = EatFastMathFlagsIfPresent(); int Res = parseCompare(Inst, PFS, KeywordVal); diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp index a8b5f96d850b5..e09532dc4735f 100644 --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -5471,9 +5471,6 @@ Error BitcodeReader::parseFunctionBody(Function *F) { if (IsFP && Record.size() > OpNum+1) FMF = getDecodedFastMathFlags(Record[++OpNum]); - if (OpNum+1 != Record.size()) - return error("Invalid record"); - if (IsFP) { if (!CmpInst::isFPPredicate(PredVal)) return error("Invalid fcmp predicate"); @@ -5482,8 +5479,14 @@ Error BitcodeReader::parseFunctionBody(Function *F) { if (!CmpInst::isIntPredicate(PredVal)) return error("Invalid icmp predicate"); I = new ICmpInst(PredVal, LHS, RHS); + if (Record.size() > OpNum + 1 && + (Record[++OpNum] & (1 << bitc::ICMP_SAME_SIGN))) + cast(I)->setSameSign(); } + if (OpNum + 1 != Record.size()) + return error("Invalid record"); + ResTypeID = getVirtualTypeID(I->getType()->getScalarType()); if (LHS->getType()->isVectorTy()) ResTypeID = getVirtualTypeID(I->getType(), ResTypeID); diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp index c3bd107954c64..d9002149fba55 100644 --- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -1718,6 +1718,9 @@ static uint64_t getOptimizationFlags(const Value *V) { Flags |= 1 << bitc::GEP_NUSW; if (GEP->hasNoUnsignedWrap()) Flags |= 1 << bitc::GEP_NUW; + } else if (const auto *ICmp = dyn_cast(V)) { + if (ICmp->hasSameSign()) + Flags |= 1 << bitc::ICMP_SAME_SIGN; } return Flags; diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp index 857d8b3745434..6adb8d6a74225 100644 --- a/llvm/lib/IR/AsmWriter.cpp +++ b/llvm/lib/IR/AsmWriter.cpp @@ -1433,6 +1433,9 @@ static void WriteOptimizationInfo(raw_ostream &Out, const User *U) { Out << " nuw"; if (TI->hasNoSignedWrap()) Out << " nsw"; + } else if (const auto *ICmp = dyn_cast(U)) { + if (ICmp->hasSameSign()) + Out << " samesign"; } } diff --git a/llvm/lib/IR/Instruction.cpp b/llvm/lib/IR/Instruction.cpp index eec751078698b..0137bb281e7ec 100644 --- a/llvm/lib/IR/Instruction.cpp +++ b/llvm/lib/IR/Instruction.cpp @@ -441,6 +441,10 @@ void Instruction::dropPoisonGeneratingFlags() { cast(this)->setHasNoUnsignedWrap(false); cast(this)->setHasNoSignedWrap(false); break; + + case Instruction::ICmp: + cast(this)->setSameSign(false); + break; } if (isa(this)) { @@ -654,6 +658,10 @@ void Instruction::copyIRFlags(const Value *V, bool IncludeWrapFlags) { if (auto *NNI = dyn_cast(V)) if (isa(this)) setNonNeg(NNI->hasNonNeg()); + + if (auto *SrcICmp = dyn_cast(V)) + if (auto *DestICmp = dyn_cast(this)) + DestICmp->setSameSign(SrcICmp->hasSameSign()); } void Instruction::andIRFlags(const Value *V) { @@ -695,6 +703,10 @@ void Instruction::andIRFlags(const Value *V) { if (auto *NNI = dyn_cast(V)) if (isa(this)) setNonNeg(hasNonNeg() && NNI->hasNonNeg()); + + if (auto *SrcICmp = dyn_cast(V)) + if (auto *DestICmp = dyn_cast(this)) + DestICmp->setSameSign(DestICmp->hasSameSign() && SrcICmp->hasSameSign()); } const char *Instruction::getOpcodeName(unsigned OpCode) { diff --git a/llvm/lib/Transforms/Utils/ScalarEvolutionExpander.cpp b/llvm/lib/Transforms/Utils/ScalarEvolutionExpander.cpp index de1864ef5b8d9..bc619e5098e09 100644 --- a/llvm/lib/Transforms/Utils/ScalarEvolutionExpander.cpp +++ b/llvm/lib/Transforms/Utils/ScalarEvolutionExpander.cpp @@ -49,6 +49,7 @@ PoisonFlags::PoisonFlags(const Instruction *I) { Exact = false; Disjoint = false; NNeg = false; + SameSign = false; GEPNW = GEPNoWrapFlags::none(); if (auto *OBO = dyn_cast(I)) { NUW = OBO->hasNoUnsignedWrap(); @@ -66,6 +67,8 @@ PoisonFlags::PoisonFlags(const Instruction *I) { } if (auto *GEP = dyn_cast(I)) GEPNW = GEP->getNoWrapFlags(); + if (auto *ICmp = dyn_cast(I)) + SameSign = ICmp->hasSameSign(); } void PoisonFlags::apply(Instruction *I) { @@ -85,6 +88,8 @@ void PoisonFlags::apply(Instruction *I) { } if (auto *GEP = dyn_cast(I)) GEP->setNoWrapFlags(GEPNW); + if (auto *ICmp = dyn_cast(I)) + ICmp->setSameSign(SameSign); } /// ReuseOrCreateCast - Arrange for there to be a cast of V to Ty at IP, diff --git a/llvm/test/Assembler/flags.ll b/llvm/test/Assembler/flags.ll index 84209500d27a5..acc8874aef443 100644 --- a/llvm/test/Assembler/flags.ll +++ b/llvm/test/Assembler/flags.ll @@ -312,6 +312,18 @@ define <2 x i32> @test_trunc_both_reversed_vector(<2 x i64> %a) { ret <2 x i32> %res } +define i1 @test_icmp_samesign(i32 %a, i32 %b) { + ; CHECK: %res = icmp samesign ult i32 %a, %b + %res = icmp samesign ult i32 %a, %b + ret i1 %res +} + +define <2 x i1> @test_icmp_samesign2(<2 x i32> %a, <2 x i32> %b) { + ; CHECK: %res = icmp samesign ult <2 x i32> %a, %b + %res = icmp samesign ult <2 x i32> %a, %b + ret <2 x i1> %res +} + define ptr @gep_nuw(ptr %p, i64 %idx) { ; CHECK: %gep = getelementptr nuw i8, ptr %p, i64 %idx %gep = getelementptr nuw i8, ptr %p, i64 %idx diff --git a/llvm/test/Bitcode/flags.ll b/llvm/test/Bitcode/flags.ll index fd56694ccceb2..99988c9ba3d3d 100644 --- a/llvm/test/Bitcode/flags.ll +++ b/llvm/test/Bitcode/flags.ll @@ -30,6 +30,8 @@ second: ; preds = %first %tsv = trunc nsw <2 x i32> %aa to <2 x i16> %tusv = trunc nuw nsw <2 x i32> %aa to <2 x i16> %tv = trunc <2 x i32> %aa to <2 x i16> + %ii = icmp samesign ult i32 %a, %z + %iv = icmp samesign ult <2 x i32> %aa, %aa unreachable first: ; preds = %entry @@ -53,5 +55,7 @@ first: ; preds = %entry %ttsv = trunc nsw <2 x i32> %aa to <2 x i16> %ttusv = trunc nuw nsw <2 x i32> %aa to <2 x i16> %ttv = trunc <2 x i32> %aa to <2 x i16> + %icm = icmp samesign ult i32 %a, %zz + %icv = icmp samesign ult <2 x i32> %aa, %aa br label %second } diff --git a/llvm/test/Transforms/InstCombine/freeze.ll b/llvm/test/Transforms/InstCombine/freeze.ll index 5fedb1f857503..23785eba05554 100644 --- a/llvm/test/Transforms/InstCombine/freeze.ll +++ b/llvm/test/Transforms/InstCombine/freeze.ll @@ -1182,6 +1182,17 @@ define ptr @propagate_drop_flags_gep_nuw(ptr %p) { ret ptr %gep.fr } +define i1 @propagate_drop_flags_icmp(i32 %a, i32 %b) { +; CHECK-LABEL: @propagate_drop_flags_icmp( +; CHECK-NEXT: [[A_FR:%.*]] = freeze i32 [[A:%.*]] +; CHECK-NEXT: [[RET:%.*]] = icmp ult i32 [[A_FR]], 3 +; CHECK-NEXT: ret i1 [[RET]] +; + %ret = icmp samesign ult i32 %a, 3 + %ret.fr = freeze i1 %ret + ret i1 %ret.fr +} + declare i32 @llvm.umax.i32(i32 %a, i32 %b) define i32 @freeze_call_with_range_attr(i32 %a) { diff --git a/llvm/test/Transforms/SimplifyCFG/HoistCode.ll b/llvm/test/Transforms/SimplifyCFG/HoistCode.ll index e6a255a4b8f08..fe0b48028a3b6 100644 --- a/llvm/test/Transforms/SimplifyCFG/HoistCode.ll +++ b/llvm/test/Transforms/SimplifyCFG/HoistCode.ll @@ -275,3 +275,33 @@ F: %gep2 = getelementptr nuw i8, ptr %p, i64 1 ret ptr %gep2 } + +define i1 @hoist_icmp_flags_preserve(i1 %C, i32 %x, i32 %y) { +; CHECK-LABEL: @hoist_icmp_flags_preserve( +; CHECK-NEXT: common.ret: +; CHECK-NEXT: [[Z1:%.*]] = icmp samesign ult i32 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: ret i1 [[Z1]] +; + br i1 %C, label %T, label %F +T: + %z1 = icmp samesign ult i32 %x, %y + ret i1 %z1 +F: + %z2 = icmp samesign ult i32 %x, %y + ret i1 %z2 +} + +define i1 @hoist_icmp_flags_drop(i1 %C, i32 %x, i32 %y) { +; CHECK-LABEL: @hoist_icmp_flags_drop( +; CHECK-NEXT: common.ret: +; CHECK-NEXT: [[Z1:%.*]] = icmp ult i32 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: ret i1 [[Z1]] +; + br i1 %C, label %T, label %F +T: + %z1 = icmp ult i32 %x, %y + ret i1 %z1 +F: + %z2 = icmp samesign ult i32 %x, %y + ret i1 %z2 +}