[ConstraintElim] Add NUW flag to sub if x >=u y. - #218685
Conversation
|
@llvm/pr-subscribers-llvm-transforms Author: Florian Hahn (fhahn) ChangesEnables simplifications in a number of real-world cases: Note there are a few cases where we create a few more additional Alive2 Proof: https://alive2.llvm.org/ce/z/YqK3i3 Patch is 40.20 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/218685.diff 7 Files Affected:
diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index 054ab3b45e108..2699c1f8c7a5c 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -101,7 +101,7 @@ struct FactOrCheck {
InstFact, /// A fact that holds after Inst executed (e.g. an assume or
/// min/mix intrinsic.
InstCheck, /// An instruction to simplify (e.g. an overflow math
- /// intrinsics).
+ /// intrinsics) or whose flags may be strengthened.
UseCheck /// An use of a compare instruction to simplify.
};
@@ -146,8 +146,8 @@ struct FactOrCheck {
return FactOrCheck(DTN, U);
}
- static FactOrCheck getCheck(DomTreeNode *DTN, CallInst *CI) {
- return FactOrCheck(EntryTy::InstCheck, DTN, CI);
+ static FactOrCheck getCheck(DomTreeNode *DTN, Instruction *I) {
+ return FactOrCheck(EntryTy::InstCheck, DTN, I);
}
bool isCheck() const {
@@ -1291,6 +1291,41 @@ static bool getConstraintFromMemoryAccess(GetElementPtrInst &GEP,
return true;
}
+/// Returns true if \p I is a candidate whose poison-generating flags may be
+/// strengthened using the constraint systems.
+static bool canStrengthenFlags(Instruction *I) {
+ switch (I->getOpcode()) {
+ case Instruction::Sub:
+ // A - B does not wrap unsigned, if A >=u B. Constant operands are handled
+ // by CorrelatedValuePropagation using ranges.
+ return I->getType()->isIntegerTy() && !I->hasNoUnsignedWrap() &&
+ !isa<Constant>(I->getOperand(1));
+ default:
+ return false;
+ }
+}
+
+
+/// Try to strengthen \p I's poison generating flags using \p Info. Returns
+/// true if \p I was modified.
+static bool tryToStrengthenFlags(Instruction *I, ConstraintInfo &Info,
+ SmallVectorImpl<Instruction *> &ToRemove) {
+ assert(canStrengthenFlags(I) && "not a candidate for flag strengthening");
+
+ switch (I->getOpcode()) {
+ case Instruction::Sub: {
+ // Op0 - Op1 does not wrap unsigned, if Op0 >=u Op1.
+ if (!Info.doesHold(CmpInst::ICMP_UGE, I->getOperand(0), I->getOperand(1)))
+ return false;
+ LLVM_DEBUG(dbgs() << "Adding nuw to " << *I << "\n");
+ I->setHasNoUnsignedWrap();
+ return true;
+ }
+ default:
+ return false;
+ }
+}
+
void State::addInfoFor(BasicBlock &BB) {
addBoundsForHeaderInductions(BB);
addInfoForInductions(BB);
@@ -1404,6 +1439,11 @@ void State::addInfoFor(BasicBlock &BB) {
WorkList.push_back(FactOrCheck::getInstFact(DT.getNode(&BB), BO));
}
+ // Queue instructions whose flags may be strengthened based on the facts
+ // that hold on entry to BB.
+ if (canStrengthenFlags(&I))
+ WorkList.push_back(FactOrCheck::getCheck(DT.getNode(&BB), &I));
+
GuaranteedToExecute &= isGuaranteedToTransferExecutionToSuccessor(&I);
}
@@ -2158,6 +2198,10 @@ static bool eliminateConstraints(Function &F, DominatorTree &DT, LoopInfo &LI,
Instruction *Inst = CB.getInstructionToSimplify();
if (!Inst)
continue;
+ if (canStrengthenFlags(Inst)) {
+ Changed |= tryToStrengthenFlags(Inst, Info, ToRemove);
+ continue;
+ }
LLVM_DEBUG(dbgs() << "Processing condition to simplify: " << *Inst
<< "\n");
if (auto *II = dyn_cast<WithOverflowInst>(Inst)) {
diff --git a/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit-postinc.ll b/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit-postinc.ll
index 3d509158c523f..1fa5344c9911d 100644
--- a/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit-postinc.ll
+++ b/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit-postinc.ll
@@ -997,7 +997,7 @@ define i64 @latch_postdec_umin_clamp(ptr %s, i64 %n) {
; CHECK-NEXT: [[EC:%.*]] = icmp eq i64 [[IV_NEXT]], 0
; CHECK-NEXT: br i1 [[EC]], label %[[EXIT]], label %[[LOOP]]
; CHECK: [[IF_FOUND]]:
-; CHECK-NEXT: [[IDX:%.*]] = sub i64 [[N]], [[IV]]
+; CHECK-NEXT: [[IDX:%.*]] = sub nuw i64 [[N]], [[IV]]
; CHECK-NEXT: br label %[[EXIT]]
; CHECK: [[EXIT]]:
; CHECK-NEXT: [[RES:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[N]], %[[LOOP_LATCH]] ], [ [[IDX]], %[[IF_FOUND]] ]
diff --git a/llvm/test/Transforms/ConstraintElimination/materialize-flags-narrow-pointer-index.ll b/llvm/test/Transforms/ConstraintElimination/materialize-flags-narrow-pointer-index.ll
new file mode 100644
index 0000000000000..bcdf22c109743
--- /dev/null
+++ b/llvm/test/Transforms/ConstraintElimination/materialize-flags-narrow-pointer-index.ll
@@ -0,0 +1,208 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes=constraint-elimination -S %s | FileCheck %s
+
+; The offsets of a GEP are computed in the pointer index type. With a 16 bit
+; index type, a stride or struct member offset that does not fit in 15 bits is
+; negative there, so a non-negative index does not imply a non-negative offset
+; and nuw must not be added.
+
+target datalayout = "p:16:16"
+
+%S.big = type { [40000 x i8], i8 }
+%S.small = type { i16, i8 }
+
+; The stride 40000 is negative as an i16, so the offset for a positive index is
+; negative and the GEP may wrap in the unsigned sense.
+define ptr @gep_no_nuw_stride_negative_in_index_type(ptr %p, i16 %i, i16 %j) {
+; CHECK-LABEL: define ptr @gep_no_nuw_stride_negative_in_index_type(
+; CHECK-SAME: ptr [[P:%.*]], i16 [[I:%.*]], i16 [[J:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[C_0:%.*]] = icmp sgt i16 [[I]], [[J]]
+; CHECK-NEXT: br i1 [[C_0]], label %[[BB_1:.*]], label %[[EXIT:.*]]
+; CHECK: [[BB_1]]:
+; CHECK-NEXT: [[C_1:%.*]] = icmp sgt i16 [[J]], 0
+; CHECK-NEXT: br i1 [[C_1]], label %[[BB_2:.*]], label %[[EXIT]]
+; CHECK: [[BB_2]]:
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr nusw [40000 x i8], ptr [[P]], i16 [[I]]
+; CHECK-NEXT: ret ptr [[GEP]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret ptr null
+;
+entry:
+ %c.0 = icmp sgt i16 %i, %j
+ br i1 %c.0, label %bb.1, label %exit
+
+bb.1:
+ %c.1 = icmp sgt i16 %j, 0
+ br i1 %c.1, label %bb.2, label %exit
+
+bb.2:
+ %gep = getelementptr nusw [40000 x i8], ptr %p, i16 %i
+ ret ptr %gep
+
+exit:
+ ret ptr null
+}
+
+; Same for the offset of a struct member, which is 40000 for the second field.
+define ptr @gep_no_nuw_struct_offset_negative_in_index_type(ptr %p, i16 %i, i16 %j) {
+; CHECK-LABEL: define ptr @gep_no_nuw_struct_offset_negative_in_index_type(
+; CHECK-SAME: ptr [[P:%.*]], i16 [[I:%.*]], i16 [[J:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[C_0:%.*]] = icmp sgt i16 [[I]], [[J]]
+; CHECK-NEXT: br i1 [[C_0]], label %[[BB_1:.*]], label %[[EXIT:.*]]
+; CHECK: [[BB_1]]:
+; CHECK-NEXT: [[C_1:%.*]] = icmp sgt i16 [[J]], 0
+; CHECK-NEXT: br i1 [[C_1]], label %[[BB_2:.*]], label %[[EXIT]]
+; CHECK: [[BB_2]]:
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr nusw [[S_BIG:%.*]], ptr [[P]], i16 [[I]], i32 1
+; CHECK-NEXT: ret ptr [[GEP]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret ptr null
+;
+entry:
+ %c.0 = icmp sgt i16 %i, %j
+ br i1 %c.0, label %bb.1, label %exit
+
+bb.1:
+ %c.1 = icmp sgt i16 %j, 0
+ br i1 %c.1, label %bb.2, label %exit
+
+bb.2:
+ %gep = getelementptr nusw %S.big, ptr %p, i16 %i, i32 1
+ ret ptr %gep
+
+exit:
+ ret ptr null
+}
+
+; The stride of the outer array is 160000, which is 28928 and thus non-negative
+; as an i16, but the stride of the inner array is negative.
+define ptr @gep_no_nuw_inner_stride_negative_in_index_type(ptr %p, i16 %i, i16 %j) {
+; CHECK-LABEL: define ptr @gep_no_nuw_inner_stride_negative_in_index_type(
+; CHECK-SAME: ptr [[P:%.*]], i16 [[I:%.*]], i16 [[J:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[C_0:%.*]] = icmp sgt i16 [[I]], [[J]]
+; CHECK-NEXT: br i1 [[C_0]], label %[[BB_1:.*]], label %[[EXIT:.*]]
+; CHECK: [[BB_1]]:
+; CHECK-NEXT: [[C_1:%.*]] = icmp sgt i16 [[J]], 0
+; CHECK-NEXT: br i1 [[C_1]], label %[[BB_2:.*]], label %[[EXIT]]
+; CHECK: [[BB_2]]:
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr nusw [4 x [40000 x i8]], ptr [[P]], i16 [[I]], i16 [[J]]
+; CHECK-NEXT: ret ptr [[GEP]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret ptr null
+;
+entry:
+ %c.0 = icmp sgt i16 %i, %j
+ br i1 %c.0, label %bb.1, label %exit
+
+bb.1:
+ %c.1 = icmp sgt i16 %j, 0
+ br i1 %c.1, label %bb.2, label %exit
+
+bb.2:
+ %gep = getelementptr nusw [4 x [40000 x i8]], ptr %p, i16 %i, i16 %j
+ ret ptr %gep
+
+exit:
+ ret ptr null
+}
+
+; The stride fits in the index type, so nuw can be added.
+define ptr @gep_nuw_stride_fits_index_type(ptr %p, i16 %i, i16 %j) {
+; CHECK-LABEL: define ptr @gep_nuw_stride_fits_index_type(
+; CHECK-SAME: ptr [[P:%.*]], i16 [[I:%.*]], i16 [[J:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[C_0:%.*]] = icmp sgt i16 [[I]], [[J]]
+; CHECK-NEXT: br i1 [[C_0]], label %[[BB_1:.*]], label %[[EXIT:.*]]
+; CHECK: [[BB_1]]:
+; CHECK-NEXT: [[C_1:%.*]] = icmp sgt i16 [[J]], 0
+; CHECK-NEXT: br i1 [[C_1]], label %[[BB_2:.*]], label %[[EXIT]]
+; CHECK: [[BB_2]]:
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr nusw i16, ptr [[P]], i16 [[I]]
+; CHECK-NEXT: ret ptr [[GEP]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret ptr null
+;
+entry:
+ %c.0 = icmp sgt i16 %i, %j
+ br i1 %c.0, label %bb.1, label %exit
+
+bb.1:
+ %c.1 = icmp sgt i16 %j, 0
+ br i1 %c.1, label %bb.2, label %exit
+
+bb.2:
+ %gep = getelementptr nusw i16, ptr %p, i16 %i
+ ret ptr %gep
+
+exit:
+ ret ptr null
+}
+
+; The struct member offset fits in the index type, so nuw can be added.
+define ptr @gep_nuw_struct_offset_fits_index_type(ptr %p, i16 %i, i16 %j) {
+; CHECK-LABEL: define ptr @gep_nuw_struct_offset_fits_index_type(
+; CHECK-SAME: ptr [[P:%.*]], i16 [[I:%.*]], i16 [[J:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[C_0:%.*]] = icmp sgt i16 [[I]], [[J]]
+; CHECK-NEXT: br i1 [[C_0]], label %[[BB_1:.*]], label %[[EXIT:.*]]
+; CHECK: [[BB_1]]:
+; CHECK-NEXT: [[C_1:%.*]] = icmp sgt i16 [[J]], 0
+; CHECK-NEXT: br i1 [[C_1]], label %[[BB_2:.*]], label %[[EXIT]]
+; CHECK: [[BB_2]]:
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr nusw [[S_SMALL:%.*]], ptr [[P]], i16 [[I]], i32 1
+; CHECK-NEXT: ret ptr [[GEP]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret ptr null
+;
+entry:
+ %c.0 = icmp sgt i16 %i, %j
+ br i1 %c.0, label %bb.1, label %exit
+
+bb.1:
+ %c.1 = icmp sgt i16 %j, 0
+ br i1 %c.1, label %bb.2, label %exit
+
+bb.2:
+ %gep = getelementptr nusw %S.small, ptr %p, i16 %i, i32 1
+ ret ptr %gep
+
+exit:
+ ret ptr null
+}
+
+; An index wider than the index type is truncated. nusw guarantees the
+; truncation preserves the signed value, which together with the index being
+; non-negative means it also preserves the unsigned value.
+define ptr @gep_nuw_index_wider_than_index_type(ptr %p, i64 %i, i64 %j) {
+; CHECK-LABEL: define ptr @gep_nuw_index_wider_than_index_type(
+; CHECK-SAME: ptr [[P:%.*]], i64 [[I:%.*]], i64 [[J:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[C_0:%.*]] = icmp sgt i64 [[I]], [[J]]
+; CHECK-NEXT: br i1 [[C_0]], label %[[BB_1:.*]], label %[[EXIT:.*]]
+; CHECK: [[BB_1]]:
+; CHECK-NEXT: [[C_1:%.*]] = icmp sgt i64 [[J]], 0
+; CHECK-NEXT: br i1 [[C_1]], label %[[BB_2:.*]], label %[[EXIT]]
+; CHECK: [[BB_2]]:
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr nusw i16, ptr [[P]], i64 [[I]]
+; CHECK-NEXT: ret ptr [[GEP]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret ptr null
+;
+entry:
+ %c.0 = icmp sgt i64 %i, %j
+ br i1 %c.0, label %bb.1, label %exit
+
+bb.1:
+ %c.1 = icmp sgt i64 %j, 0
+ br i1 %c.1, label %bb.2, label %exit
+
+bb.2:
+ %gep = getelementptr nusw i16, ptr %p, i64 %i
+ ret ptr %gep
+
+exit:
+ ret ptr null
+}
diff --git a/llvm/test/Transforms/ConstraintElimination/materialize-flags.ll b/llvm/test/Transforms/ConstraintElimination/materialize-flags.ll
new file mode 100644
index 0000000000000..d27ff1b5e3068
--- /dev/null
+++ b/llvm/test/Transforms/ConstraintElimination/materialize-flags.ll
@@ -0,0 +1,457 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes=constraint-elimination -S %s | FileCheck %s
+
+define i64 @sext_to_zext_nneg(i32 %n, i32 %m) {
+; CHECK-LABEL: define i64 @sext_to_zext_nneg(
+; CHECK-SAME: i32 [[N:%.*]], i32 [[M:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[C:%.*]] = icmp slt i32 [[N]], [[M]]
+; CHECK-NEXT: br i1 [[C]], label %[[EXIT:.*]], label %[[THEN:.*]]
+; CHECK: [[THEN]]:
+; CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[N]], [[M]]
+; CHECK-NEXT: [[EXT:%.*]] = sext i32 [[SUB]] to i64
+; CHECK-NEXT: ret i64 [[EXT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret i64 0
+;
+entry:
+ %c = icmp slt i32 %n, %m
+ br i1 %c, label %exit, label %then
+
+then:
+ %sub = sub nsw i32 %n, %m
+ %ext = sext i32 %sub to i64
+ ret i64 %ext
+
+exit:
+ ret i64 0
+}
+
+define i64 @sext_to_zext_nneg_negated_condition(i32 %n, i32 %m) {
+; CHECK-LABEL: define i64 @sext_to_zext_nneg_negated_condition(
+; CHECK-SAME: i32 [[N:%.*]], i32 [[M:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[C:%.*]] = icmp slt i32 [[N]], [[M]]
+; CHECK-NEXT: br i1 [[C]], label %[[THEN:.*]], label %[[EXIT:.*]]
+; CHECK: [[THEN]]:
+; CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[N]], [[M]]
+; CHECK-NEXT: [[EXT:%.*]] = sext i32 [[SUB]] to i64
+; CHECK-NEXT: ret i64 [[EXT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret i64 0
+;
+entry:
+ %c = icmp slt i32 %n, %m
+ br i1 %c, label %then, label %exit
+
+then:
+ %sub = sub nsw i32 %n, %m
+ %ext = sext i32 %sub to i64
+ ret i64 %ext
+
+exit:
+ ret i64 0
+}
+
+define i64 @sext_no_conversion_known_via_value_tracking(i32 %n) {
+; CHECK-LABEL: define i64 @sext_no_conversion_known_via_value_tracking(
+; CHECK-SAME: i32 [[N:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[AND:%.*]] = and i32 [[N]], 255
+; CHECK-NEXT: [[EXT:%.*]] = sext i32 [[AND]] to i64
+; CHECK-NEXT: ret i64 [[EXT]]
+;
+entry:
+ %and = and i32 %n, 255
+ %ext = sext i32 %and to i64
+ ret i64 %ext
+}
+
+define i32 @sub_nuw_relational(i32 %a, i32 %b) {
+; CHECK-LABEL: define i32 @sub_nuw_relational(
+; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[C:%.*]] = icmp uge i32 [[A]], [[B]]
+; CHECK-NEXT: br i1 [[C]], label %[[THEN:.*]], label %[[EXIT:.*]]
+; CHECK: [[THEN]]:
+; CHECK-NEXT: [[SUB:%.*]] = sub nuw i32 [[A]], [[B]]
+; CHECK-NEXT: ret i32 [[SUB]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret i32 0
+;
+entry:
+ %c = icmp uge i32 %a, %b
+ br i1 %c, label %then, label %exit
+
+then:
+ %sub = sub i32 %a, %b
+ ret i32 %sub
+
+exit:
+ ret i32 0
+}
+
+define i32 @sub_nuw_transitive(i32 %a, i32 %b, i32 %c) {
+; CHECK-LABEL: define i32 @sub_nuw_transitive(
+; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]], i32 [[C:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[C_0:%.*]] = icmp uge i32 [[A]], [[B]]
+; CHECK-NEXT: br i1 [[C_0]], label %[[BB_1:.*]], label %[[EXIT:.*]]
+; CHECK: [[BB_1]]:
+; CHECK-NEXT: [[C_1:%.*]] = icmp uge i32 [[B]], [[C]]
+; CHECK-NEXT: br i1 [[C_1]], label %[[BB_2:.*]], label %[[EXIT]]
+; CHECK: [[BB_2]]:
+; CHECK-NEXT: [[SUB:%.*]] = sub nuw i32 [[A]], [[C]]
+; CHECK-NEXT: ret i32 [[SUB]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret i32 0
+;
+entry:
+ %c.0 = icmp uge i32 %a, %b
+ br i1 %c.0, label %bb.1, label %exit
+
+bb.1:
+ %c.1 = icmp uge i32 %b, %c
+ br i1 %c.1, label %bb.2, label %exit
+
+bb.2:
+ %sub = sub i32 %a, %c
+ ret i32 %sub
+
+exit:
+ ret i32 0
+}
+
+; A signed fact does not imply the unsigned no-wrap flag.
+define i32 @sub_no_nuw_signed_fact(i32 %a, i32 %b) {
+; CHECK-LABEL: define i32 @sub_no_nuw_signed_fact(
+; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[C:%.*]] = icmp sge i32 [[A]], [[B]]
+; CHECK-NEXT: br i1 [[C]], label %[[THEN:.*]], label %[[EXIT:.*]]
+; CHECK: [[THEN]]:
+; CHECK-NEXT: [[SUB:%.*]] = sub i32 [[A]], [[B]]
+; CHECK-NEXT: ret i32 [[SUB]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret i32 0
+;
+entry:
+ %c = icmp sge i32 %a, %b
+ br i1 %c, label %then, label %exit
+
+then:
+ %sub = sub i32 %a, %b
+ ret i32 %sub
+
+exit:
+ ret i32 0
+}
+
+define ptr @gep_nuw_transitive(ptr %p, i64 %i, i64 %j) {
+; CHECK-LABEL: define ptr @gep_nuw_transitive(
+; CHECK-SAME: ptr [[P:%.*]], i64 [[I:%.*]], i64 [[J:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[C_0:%.*]] = icmp sgt i64 [[I]], [[J]]
+; CHECK-NEXT: br i1 [[C_0]], label %[[BB_1:.*]], label %[[EXIT:.*]]
+; CHECK: [[BB_1]]:
+; CHECK-NEXT: [[C_1:%.*]] = icmp sgt i64 [[J]], 0
+; CHECK-NEXT: br i1 [[C_1]], label %[[BB_2:.*]], label %[[EXIT]]
+; CHECK: [[BB_2]]:
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr nusw i32, ptr [[P]], i64 [[I]]
+; CHECK-NEXT: ret ptr [[GEP]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret ptr null
+;
+entry:
+ %c.0 = icmp sgt i64 %i, %j
+ br i1 %c.0, label %bb.1, label %exit
+
+bb.1:
+ %c.1 = icmp sgt i64 %j, 0
+ br i1 %c.1, label %bb.2, label %exit
+
+bb.2:
+ %gep = getelementptr nusw i32, ptr %p, i64 %i
+ ret ptr %gep
+
+exit:
+ ret ptr null
+}
+
+define ptr @gep_no_nuw_unknown_index(ptr %p, i64 %i, i64 %n) {
+; CHECK-LABEL: define ptr @gep_no_nuw_unknown_index(
+; CHECK-SAME: ptr [[P:%.*]], i64 [[I:%.*]], i64 [[N:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[C:%.*]] = icmp sge i64 [[I]], [[N]]
+; CHECK-NEXT: br i1 [[C]], label %[[EXIT:.*]], label %[[THEN:.*]]
+; CHECK: [[THEN]]:
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr nusw i32, ptr [[P]], i64 [[I]]
+; CHECK-NEXT: ret ptr [[GEP]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret ptr null
+;
+entry:
+ %c = icmp sge i64 %i, %n
+ br i1 %c, label %exit, label %then
+
+then:
+ %gep = getelementptr nusw i32, ptr %p, i64 %i
+ ret ptr %gep
+
+exit:
+ ret ptr null
+}
+
+; The negative constant index means the GEP wraps in the unsigned sense.
+define ptr @gep_no_nuw_negative_constant_index(ptr %p, i64 %i, i64 %j) {
+; CHECK-LABEL: define ptr @gep_no_nuw_negative_constant_index(
+; CHECK-SAME: ptr [[P:%.*]], i64 [[I:%.*]], i64 [[J:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[C_0:%.*]] = icmp sgt i64 [[I]], [[J]]
+; CHECK-NEXT: br i1 [[C_0]], label %[[BB_1:.*]], label %[[EXIT:.*]]
+; CHECK: [[BB_1]]:
+; CHECK-NEXT: [[C_1:%.*]] = icmp sgt i64 [[J]], 0
+; CHECK-NEXT: br i1 [[C_1]], label %[[BB_2:.*]], label %[[EXIT]]
+; CHECK: [[BB_2]]:
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr nusw i32, ptr [[P]], i64 [[I]]
+; CHECK-NEXT: [[GEP_2:%.*]] = getelementptr nusw i32, ptr [[GEP]], i64 -1
+; CHECK-NEXT: ret ptr [[GEP_2]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret ptr null
+;
+entry:
+ %c.0 = icmp sgt i64 %i, %j
+ br i1 %c.0, label %bb.1, label %exit
+
+bb.1:
+ %c.1 = icmp sgt i64 %j, 0
+ br i1 %c.1, label %bb.2, label %exit
+
+bb.2:
+ %gep = getelementptr nusw i32, ptr %p, i64 %i
+ %gep.2 = getelementptr nusw i32, ptr %gep, i64 -1
+ ret ptr %gep.2
+
+exit:
+ ret ptr null
+}
+
+; Without nusw, nuw cannot be implied by non-negative offsets.
+define ptr @gep_no_nusw(ptr %p, i64 %i, i64 %j) {
+; CHECK-LABEL: define ptr @gep_no_nusw(
+; CHECK-SAME: ptr [[P:%.*]], i64 [[I:%.*]], i64 [[J:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[C_0:%.*]] = icmp sgt i64 [[I]], [[J]]
+; CHECK-NEXT: br i1 [[C_0]], label %[[BB_1:.*]], label %[[EXIT:.*]]
+; CHECK: [[BB_1]]:
+; CHECK-NEXT: [[C_1:%.*]] = icmp sgt i64 [[J]], 0
+; CHECK-NEXT: br i1 [[C_1]], label %[[BB_2:.*]], label %[[EXIT]]
+; CHECK: [[BB_2]]:
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr i32, ptr [[P]], i64 [[I]]
+; CHECK-NEXT: ret ptr [[GEP]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret ptr null
+;
+entry:
+ %c.0 = icmp sgt i64 %i, %j
+ br i1 %c.0, label %bb.1, label %exit
+
+bb.1:
+ %c.1 = icmp sgt i64 %j, 0
+ br i1 %c.1, label %bb.2, label %exit
+
+bb.2:
+ %gep = getelementptr i32, ptr %p, i64 %i
+ ret ptr %gep
+
+exit:
+ ret ptr null
+}
+
+define ptr @gep_no_nuw_constant_index(ptr %p) {
+; CHECK-LABEL: define ptr @gep_no_nuw_constant_index(
+; CHECK-SAME: ptr [[P:%.*]]) {
+; CHECK-NEXT: [...
[truncated]
|
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
a058888 to
2f684e1
Compare
🐧 Linux x64 Test Results
Failed Tests(click on a test name to see its output) ClangClang.CodeGen/attr-counted-by-for-pointers.cClang.CodeGen/attr-counted-by-with-sanitizers.cClang.CodeGen/attr-sized-by-for-pointers.cIf these failures are unrelated to your changes (for example tests are broken or flaky at HEAD), please open an issue at https://github.com/llvm/llvm-project/issues and add the |
🪟 Windows x64 Test Results
Failed Tests(click on a test name to see its output) ClangClang.CodeGen/attr-counted-by-for-pointers.cClang.CodeGen/attr-counted-by-with-sanitizers.cClang.CodeGen/attr-sized-by-for-pointers.cIf these failures are unrelated to your changes (for example tests are broken or flaky at HEAD), please open an issue at https://github.com/llvm/llvm-project/issues and add the |
nikic
left a comment
There was a problem hiding this comment.
Does this have compile-time impact?
| switch (I->getOpcode()) { | ||
| case Instruction::Sub: | ||
| // A - B does not wrap unsigned, if A >=u B. Constant operands are handled | ||
| // by CorrelatedValuePropagation using ranges. |
There was a problem hiding this comment.
For sub specifically, a constant on the RHS will get canonicalized to add anyway.
There was a problem hiding this comment.
It looks like this phase ordering test is not affected by the patch. Is that intended?
Enables simplifications in a number of real-world cases:
dtcxzyw/llvm-opt-benchmark-nightly#1039
Note there are a few cases where we create a few more additional
instructions due to second-order effects. In most cases, it is
additional scalar PRE or replacing things like
srem.Alive2 Proof: https://alive2.llvm.org/ce/z/YqK3i3