Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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
6 changes: 5 additions & 1 deletion llvm/include/llvm/Analysis/AssumeBundleQueries.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,14 @@ LLVM_ABI void fillMapFromAssume(AssumeInst &Assume,
struct RetainedKnowledge {
Attribute::AttrKind AttrKind = Attribute::None;
uint64_t ArgValue = 0;
Value *IRArgValue = nullptr;
Value *WasOn = nullptr;
RetainedKnowledge(Attribute::AttrKind AttrKind = Attribute::None,
uint64_t ArgValue = 0, Value *WasOn = nullptr)
: AttrKind(AttrKind), ArgValue(ArgValue), WasOn(WasOn) {}
bool operator==(RetainedKnowledge Other) const {
return AttrKind == Other.AttrKind && WasOn == Other.WasOn &&
ArgValue == Other.ArgValue;
ArgValue == Other.ArgValue && IRArgValue == Other.IRArgValue;
}
bool operator!=(RetainedKnowledge Other) const { return !(*this == Other); }
/// This is only intended for use in std::min/std::max between attribute that
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/Analysis/AssumeBundleQueries.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@ llvm::getKnowledgeFromBundle(AssumeInst &Assume,
};
if (BOI.End - BOI.Begin > ABA_Argument)
Result.ArgValue = GetArgOr1(0);
Result.IRArgValue = bundleHasArgument(BOI, ABA_Argument)
? getValueFromBundleOpInfo(Assume, BOI, ABA_Argument)
: nullptr;
if (Result.AttrKind == Attribute::Alignment)
if (BOI.End - BOI.Begin > ABA_Argument + 1)
Result.ArgValue = MinAlign(Result.ArgValue, GetArgOr1(1));
Expand Down
88 changes: 53 additions & 35 deletions llvm/lib/Analysis/Loads.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,40 @@ static bool isAligned(const Value *Base, Align Alignment,
return Base->getPointerAlignment(DL) >= Alignment;
}

static bool isDereferenceableAndAlignedPointerViaAssumption(
const Value *Ptr, Align Alignment,
function_ref<bool(const RetainedKnowledge &RK)> CheckSize,
const DataLayout &DL, const Instruction *CtxI, AssumptionCache *AC,
const DominatorTree *DT) {
// Dereferenceable information from assumptions is only valid if the value
// cannot be freed between the assumption and use. For now just use the
// information for values that cannot be freed in the function.
// TODO: More precisely check if the pointer can be freed between assumption
// and use.
if (!CtxI || Ptr->canBeFreed())
return false;
/// Look through assumes to see if both dereferencability and alignment can
/// be proven by an assume if needed.
RetainedKnowledge AlignRK;
RetainedKnowledge DerefRK;
bool IsAligned = Ptr->getPointerAlignment(DL) >= Alignment;
return getKnowledgeForValue(
Ptr, {Attribute::Dereferenceable, Attribute::Alignment}, *AC,
[&](RetainedKnowledge RK, Instruction *Assume, auto) {
if (!isValidAssumeForContext(Assume, CtxI, DT))
return false;
if (RK.AttrKind == Attribute::Alignment)
AlignRK = std::max(AlignRK, RK);
if (RK.AttrKind == Attribute::Dereferenceable)
DerefRK = std::max(DerefRK, RK);
IsAligned |= AlignRK && AlignRK.ArgValue >= Alignment.value();
if (IsAligned && DerefRK && CheckSize(DerefRK))
return true; // We have found what we needed so we stop looking
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: You can just do

  return IsAligned && DerefRK && CheckSize(DerefRK);

return false; // Other assumes may have better information. so
// keep looking
});
}

/// Test if V is always a pointer to allocated and suitably aligned memory for
/// a simple load or store.
static bool isDereferenceableAndAlignedPointer(
Expand Down Expand Up @@ -169,38 +203,12 @@ static bool isDereferenceableAndAlignedPointer(
Size, DL, CtxI, AC, DT, TLI,
Visited, MaxDepth);

// Dereferenceable information from assumptions is only valid if the value
// cannot be freed between the assumption and use. For now just use the
// information for values that cannot be freed in the function.
// TODO: More precisely check if the pointer can be freed between assumption
// and use.
if (CtxI && AC && !V->canBeFreed()) {
/// Look through assumes to see if both dereferencability and alignment can
/// be proven by an assume if needed.
RetainedKnowledge AlignRK;
RetainedKnowledge DerefRK;
bool IsAligned = V->getPointerAlignment(DL) >= Alignment;
if (getKnowledgeForValue(
V, {Attribute::Dereferenceable, Attribute::Alignment}, *AC,
[&](RetainedKnowledge RK, Instruction *Assume, auto) {
if (!isValidAssumeForContext(Assume, CtxI, DT))
return false;
if (RK.AttrKind == Attribute::Alignment)
AlignRK = std::max(AlignRK, RK);
if (RK.AttrKind == Attribute::Dereferenceable)
DerefRK = std::max(DerefRK, RK);
IsAligned |= AlignRK && AlignRK.ArgValue >= Alignment.value();
if (IsAligned && DerefRK &&
DerefRK.ArgValue >= Size.getZExtValue())
return true; // We have found what we needed so we stop looking
return false; // Other assumes may have better information. so
// keep looking
}))
return true;
}

// If we don't know, assume the worst.
return false;
return AC && isDereferenceableAndAlignedPointerViaAssumption(
V, Alignment,
[Size](const RetainedKnowledge &RK) {
return RK.ArgValue >= Size.getZExtValue();
},
DL, CtxI, AC, DT);
}

bool llvm::isDereferenceableAndAlignedPointer(
Expand Down Expand Up @@ -317,8 +325,8 @@ bool llvm::isDereferenceableAndAlignedInLoop(
return false;

const SCEV *MaxBECount =
Predicates ? SE.getPredicatedConstantMaxBackedgeTakenCount(L, *Predicates)
: SE.getConstantMaxBackedgeTakenCount(L);
Predicates ? SE.getPredicatedSymbolicMaxBackedgeTakenCount(L, *Predicates)
: SE.getSymbolicMaxBackedgeTakenCount(L);
const SCEV *BECount = Predicates
? SE.getPredicatedBackedgeTakenCount(L, *Predicates)
: SE.getBackedgeTakenCount(L);
Expand All @@ -339,9 +347,11 @@ bool llvm::isDereferenceableAndAlignedInLoop(

Value *Base = nullptr;
APInt AccessSize;
const SCEV *AccessSizeSCEV = nullptr;
if (const SCEVUnknown *NewBase = dyn_cast<SCEVUnknown>(AccessStart)) {
Base = NewBase->getValue();
AccessSize = MaxPtrDiff;
AccessSizeSCEV = PtrDiff;
} else if (auto *MinAdd = dyn_cast<SCEVAddExpr>(AccessStart)) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

OT - We may be able to strengthen this code using SCEV's getPointerBase and removePointerBase.

Copy link
Contributor

Choose a reason for hiding this comment

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

this is a good point as a follow-up (I'll try it out). With a pre-loop before the vectorizable loop, I found it bailing out on the (MinAdd->getNumOperands() != 2), but I'm not sure if getPointerBase will help.

Copy link
Contributor

Choose a reason for hiding this comment

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

just to complete the loop here: I got it working for a case where we have more than 2 operands, by using getPointerBase and removePointerBase along with SCEV's APIs for non-constant offset.

I'll place a patch for review.

if (MinAdd->getNumOperands() != 2)
return false;
Expand All @@ -365,12 +375,20 @@ bool llvm::isDereferenceableAndAlignedInLoop(
return false;

AccessSize = MaxPtrDiff + Offset->getAPInt();
AccessSizeSCEV = SE.getAddExpr(PtrDiff, Offset);
Base = NewBase->getValue();
} else
return false;

Instruction *HeaderFirstNonPHI = &*L->getHeader()->getFirstNonPHIIt();
return isDereferenceableAndAlignedPointer(Base, Alignment, AccessSize, DL,
return isDereferenceableAndAlignedPointerViaAssumption(
Base, Alignment,
[&SE, AccessSizeSCEV](const RetainedKnowledge &RK) {
return SE.isKnownPredicate(CmpInst::ICMP_ULE, AccessSizeSCEV,
Copy link
Contributor

Choose a reason for hiding this comment

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

There is a potential to use isKnownPredicateAt with Loop's predecessor as the Ctx instruction if it exists. I don't yet see a benefit for it, but just stating it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep, there's probably a number of improvements we can make as follow ups!

SE.getSCEV(RK.IRArgValue));
},
DL, HeaderFirstNonPHI, AC, &DT) ||
isDereferenceableAndAlignedPointer(Base, Alignment, AccessSize, DL,
HeaderFirstNonPHI, AC, &DT);
}

Expand Down
9 changes: 9 additions & 0 deletions llvm/lib/IR/Verifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5604,6 +5604,15 @@ void Verifier::visitIntrinsicCall(Intrinsic::ID ID, CallBase &Call) {
"third argument should be an integer if present", Call);
continue;
}
if (Kind == Attribute::Dereferenceable) {
Check(ArgCount == 2,
"dereferenceable assumptions should have 2 arguments", Call);
Check(Call.getOperand(Elem.Begin)->getType()->isPointerTy(),
"first argument should be a pointer", Call);
Check(Call.getOperand(Elem.Begin + 1)->getType()->isIntegerTy(),
"second argument should be an integer", Call);
continue;
}
Check(ArgCount <= 2, "too many arguments", Call);
if (Kind == Attribute::None)
break;
Expand Down
Loading