Skip to content

[IR] Add samesign flag to icmp instruction #111419

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions llvm/docs/LangRef.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12329,6 +12329,7 @@ Syntax:
::

<result> = icmp <cond> <ty> <op1>, <op2> ; yields i1 or <N x i1>:result
<result> = icmp samesign <cond> <ty> <op1>, <op2> ; yields i1 or <N x i1>:result

Overview:
"""""""""
Expand Down Expand Up @@ -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 <poisonvalues>`.

Example:
""""""""

Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/AsmParser/LLToken.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ enum Kind {
kw_disjoint,
kw_inbounds,
kw_nneg,
kw_samesign,
kw_inrange,
kw_addrspace,
kw_section,
Expand Down
4 changes: 4 additions & 0 deletions llvm/include/llvm/Bitcode/LLVMBitCodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
11 changes: 11 additions & 0 deletions llvm/include/llvm/IR/Instructions.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ struct PoisonFlags {
unsigned Exact : 1;
unsigned Disjoint : 1;
unsigned NNeg : 1;
unsigned SameSign : 1;
GEPNoWrapFlags GEPNW;

PoisonFlags(const Instruction *I);
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/AsmParser/LLLexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,7 @@ lltok::Kind LLLexer::LexIdentifier() {
KEYWORD(disjoint);
KEYWORD(inbounds);
KEYWORD(nneg);
KEYWORD(samesign);
KEYWORD(inrange);
KEYWORD(addrspace);
KEYWORD(section);
Expand Down
10 changes: 8 additions & 2 deletions llvm/lib/AsmParser/LLParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<ICmpInst>(Inst)->setSameSign();
return false;
}
case lltok::kw_fcmp: {
FastMathFlags FMF = EatFastMathFlagsIfPresent();
int Res = parseCompare(Inst, PFS, KeywordVal);
Expand Down
9 changes: 6 additions & 3 deletions llvm/lib/Bitcode/Reader/BitcodeReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -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<ICmpInst>(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);
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<ICmpInst>(V)) {
if (ICmp->hasSameSign())
Flags |= 1 << bitc::ICMP_SAME_SIGN;
}

return Flags;
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/IR/AsmWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<ICmpInst>(U)) {
if (ICmp->hasSameSign())
Out << " samesign";
}
}

Expand Down
12 changes: 12 additions & 0 deletions llvm/lib/IR/Instruction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,10 @@ void Instruction::dropPoisonGeneratingFlags() {
cast<TruncInst>(this)->setHasNoUnsignedWrap(false);
cast<TruncInst>(this)->setHasNoSignedWrap(false);
break;

case Instruction::ICmp:
cast<ICmpInst>(this)->setSameSign(false);
break;
}

if (isa<FPMathOperator>(this)) {
Expand Down Expand Up @@ -654,6 +658,10 @@ void Instruction::copyIRFlags(const Value *V, bool IncludeWrapFlags) {
if (auto *NNI = dyn_cast<PossiblyNonNegInst>(V))
if (isa<PossiblyNonNegInst>(this))
setNonNeg(NNI->hasNonNeg());

if (auto *SrcICmp = dyn_cast<ICmpInst>(V))
if (auto *DestICmp = dyn_cast<ICmpInst>(this))
DestICmp->setSameSign(SrcICmp->hasSameSign());
}

void Instruction::andIRFlags(const Value *V) {
Expand Down Expand Up @@ -695,6 +703,10 @@ void Instruction::andIRFlags(const Value *V) {
if (auto *NNI = dyn_cast<PossiblyNonNegInst>(V))
if (isa<PossiblyNonNegInst>(this))
setNonNeg(hasNonNeg() && NNI->hasNonNeg());

if (auto *SrcICmp = dyn_cast<ICmpInst>(V))
if (auto *DestICmp = dyn_cast<ICmpInst>(this))
DestICmp->setSameSign(DestICmp->hasSameSign() && SrcICmp->hasSameSign());
}

const char *Instruction::getOpcodeName(unsigned OpCode) {
Expand Down
5 changes: 5 additions & 0 deletions llvm/lib/Transforms/Utils/ScalarEvolutionExpander.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<OverflowingBinaryOperator>(I)) {
NUW = OBO->hasNoUnsignedWrap();
Expand All @@ -66,6 +67,8 @@ PoisonFlags::PoisonFlags(const Instruction *I) {
}
if (auto *GEP = dyn_cast<GetElementPtrInst>(I))
GEPNW = GEP->getNoWrapFlags();
if (auto *ICmp = dyn_cast<ICmpInst>(I))
SameSign = ICmp->hasSameSign();
}

void PoisonFlags::apply(Instruction *I) {
Expand All @@ -85,6 +88,8 @@ void PoisonFlags::apply(Instruction *I) {
}
if (auto *GEP = dyn_cast<GetElementPtrInst>(I))
GEP->setNoWrapFlags(GEPNW);
if (auto *ICmp = dyn_cast<ICmpInst>(I))
ICmp->setSameSign(SameSign);
}

/// ReuseOrCreateCast - Arrange for there to be a cast of V to Ty at IP,
Expand Down
12 changes: 12 additions & 0 deletions llvm/test/Assembler/flags.ll
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions llvm/test/Bitcode/flags.ll
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
}
11 changes: 11 additions & 0 deletions llvm/test/Transforms/InstCombine/freeze.ll
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
30 changes: 30 additions & 0 deletions llvm/test/Transforms/SimplifyCFG/HoistCode.ll
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Loading