Skip to content
Open
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
49 changes: 46 additions & 3 deletions llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
};

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -1291,6 +1291,40 @@ 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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For sub specifically, a constant on the RHS will get canonicalized to add anyway.

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);
Expand Down Expand Up @@ -1404,6 +1438,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);
}

Expand Down Expand Up @@ -2158,6 +2197,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)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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]] ]
Expand Down
Original file line number Diff line number Diff line change
@@ -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
}
Loading
Loading