forked from OSchip/llvm-project
[Attributor] AAValueConstantRange: Value range analysis using constant range
This patch introduces `AAValueConstantRange`, which answers a possible range for integer value in a specific program point. One of the motivations is propagating existing `range` metadata. (I think we need to change the situation that `range` metadata cannot be put to Argument). The state is a tuple of `ConstantRange` and it is initialized to (known, assumed) = ([-∞, +∞], empty). Currently, AAValueConstantRange is created when AAValueSimplify cannot simplify the value. Supported - BinaryOperator(add, sub, ...) - CmpInst(icmp eq, ...) - !range metadata `AAValueConstantRange` is not intended to extend to polyhedral range value analysis. Reviewed By: jdoerfert Differential Revision: https://reviews.llvm.org/D71620
This commit is contained in:
parent
86f48999f4
commit
e996303431
|
@ -105,6 +105,7 @@
|
|||
#include "llvm/Analysis/MustExecute.h"
|
||||
#include "llvm/Analysis/TargetLibraryInfo.h"
|
||||
#include "llvm/IR/CallSite.h"
|
||||
#include "llvm/IR/ConstantRange.h"
|
||||
#include "llvm/IR/PassManager.h"
|
||||
|
||||
namespace llvm {
|
||||
|
@ -1493,6 +1494,116 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
/// State for an integer range.
|
||||
struct IntegerRangeState : public AbstractState {
|
||||
|
||||
/// Bitwidth of the associated value.
|
||||
uint32_t BitWidth;
|
||||
|
||||
/// State representing assumed range, initially set to empty.
|
||||
ConstantRange Assumed;
|
||||
|
||||
/// State representing known range, initially set to [-inf, inf].
|
||||
ConstantRange Known;
|
||||
|
||||
IntegerRangeState(uint32_t BitWidth)
|
||||
: BitWidth(BitWidth), Assumed(ConstantRange::getEmpty(BitWidth)),
|
||||
Known(ConstantRange::getFull(BitWidth)) {}
|
||||
|
||||
/// Return the worst possible representable state.
|
||||
static ConstantRange getWorstState(uint32_t BitWidth) {
|
||||
return ConstantRange::getFull(BitWidth);
|
||||
}
|
||||
|
||||
/// Return the best possible representable state.
|
||||
static ConstantRange getBestState(uint32_t BitWidth) {
|
||||
return ConstantRange::getEmpty(BitWidth);
|
||||
}
|
||||
|
||||
/// Return associated values' bit width.
|
||||
uint32_t getBitWidth() const { return BitWidth; }
|
||||
|
||||
/// See AbstractState::isValidState()
|
||||
bool isValidState() const override {
|
||||
return BitWidth > 0 && !Assumed.isFullSet();
|
||||
}
|
||||
|
||||
/// See AbstractState::isAtFixpoint()
|
||||
bool isAtFixpoint() const override { return Assumed == Known; }
|
||||
|
||||
/// See AbstractState::indicateOptimisticFixpoint(...)
|
||||
ChangeStatus indicateOptimisticFixpoint() override {
|
||||
Known = Assumed;
|
||||
return ChangeStatus::CHANGED;
|
||||
}
|
||||
|
||||
/// See AbstractState::indicatePessimisticFixpoint(...)
|
||||
ChangeStatus indicatePessimisticFixpoint() override {
|
||||
Assumed = Known;
|
||||
return ChangeStatus::CHANGED;
|
||||
}
|
||||
|
||||
/// Return the known state encoding
|
||||
ConstantRange getKnown() const { return Known; }
|
||||
|
||||
/// Return the assumed state encoding.
|
||||
ConstantRange getAssumed() const { return Assumed; }
|
||||
|
||||
/// Unite assumed range with the passed state.
|
||||
void unionAssumed(const ConstantRange &R) {
|
||||
// Don't loose a known range.
|
||||
Assumed = Assumed.unionWith(R).intersectWith(Known);
|
||||
}
|
||||
|
||||
/// See IntegerRangeState::unionAssumed(..).
|
||||
void unionAssumed(const IntegerRangeState &R) {
|
||||
unionAssumed(R.getAssumed());
|
||||
}
|
||||
|
||||
/// Unite known range with the passed state.
|
||||
void unionKnown(const ConstantRange &R) {
|
||||
// Don't loose a known range.
|
||||
Known = Known.unionWith(R);
|
||||
Assumed = Assumed.unionWith(Known);
|
||||
}
|
||||
|
||||
/// See IntegerRangeState::unionKnown(..).
|
||||
void unionKnown(const IntegerRangeState &R) { unionKnown(R.getKnown()); }
|
||||
|
||||
/// Intersect known range with the passed state.
|
||||
void intersectKnown(const ConstantRange &R) {
|
||||
Assumed = Assumed.intersectWith(R);
|
||||
Known = Known.intersectWith(R);
|
||||
}
|
||||
|
||||
/// See IntegerRangeState::intersectKnown(..).
|
||||
void intersectKnown(const IntegerRangeState &R) {
|
||||
intersectKnown(R.getKnown());
|
||||
}
|
||||
|
||||
/// Equality for IntegerRangeState.
|
||||
bool operator==(const IntegerRangeState &R) const {
|
||||
return getAssumed() == R.getAssumed() && getKnown() == R.getKnown();
|
||||
}
|
||||
|
||||
/// "Clamp" this state with \p R. The result is subtype dependent but it is
|
||||
/// intended that only information assumed in both states will be assumed in
|
||||
/// this one afterwards.
|
||||
IntegerRangeState operator^=(const IntegerRangeState &R) {
|
||||
// NOTE: `^=` operator seems like `intersect` but in this case, we need to
|
||||
// take `union`.
|
||||
unionAssumed(R);
|
||||
return *this;
|
||||
}
|
||||
|
||||
IntegerRangeState operator&=(const IntegerRangeState &R) {
|
||||
// NOTE: `&=` operator seems like `intersect` but in this case, we need to
|
||||
// take `union`.
|
||||
unionKnown(R);
|
||||
unionAssumed(R);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
/// Helper struct necessary as the modular build fails if the virtual method
|
||||
/// IRAttribute::manifest is defined in the Attributor.cpp.
|
||||
struct IRAttributeManifest {
|
||||
|
@ -1686,6 +1797,7 @@ template <typename base_ty, base_ty BestState, base_ty WorstState>
|
|||
raw_ostream &
|
||||
operator<<(raw_ostream &OS,
|
||||
const IntegerStateBase<base_ty, BestState, WorstState> &State);
|
||||
raw_ostream &operator<<(raw_ostream &OS, const IntegerRangeState &State);
|
||||
///}
|
||||
|
||||
struct AttributorPass : public PassInfoMixin<AttributorPass> {
|
||||
|
@ -2321,6 +2433,55 @@ struct AAMemoryBehavior
|
|||
static const char ID;
|
||||
};
|
||||
|
||||
/// An abstract interface for range value analysis.
|
||||
struct AAValueConstantRange : public IntegerRangeState,
|
||||
public AbstractAttribute,
|
||||
public IRPosition {
|
||||
AAValueConstantRange(const IRPosition &IRP)
|
||||
: IntegerRangeState(
|
||||
IRP.getAssociatedValue().getType()->getIntegerBitWidth()),
|
||||
IRPosition(IRP) {}
|
||||
|
||||
/// Return an IR position, see struct IRPosition.
|
||||
const IRPosition &getIRPosition() const override { return *this; }
|
||||
|
||||
/// See AbstractAttribute::getState(...).
|
||||
IntegerRangeState &getState() override { return *this; }
|
||||
const AbstractState &getState() const override { return *this; }
|
||||
|
||||
/// Create an abstract attribute view for the position \p IRP.
|
||||
static AAValueConstantRange &createForPosition(const IRPosition &IRP,
|
||||
Attributor &A);
|
||||
|
||||
/// Return an assumed range for the assocaited value a program point \p CtxI.
|
||||
/// If \p I is nullptr, simply return an assumed range.
|
||||
virtual ConstantRange
|
||||
getAssumedConstantRange(Attributor &A,
|
||||
const Instruction *CtxI = nullptr) const = 0;
|
||||
|
||||
/// Return a known range for the assocaited value at a program point \p CtxI.
|
||||
/// If \p I is nullptr, simply return a known range.
|
||||
virtual ConstantRange
|
||||
getKnownConstantRange(Attributor &A,
|
||||
const Instruction *CtxI = nullptr) const = 0;
|
||||
|
||||
/// Return an assumed constant for the assocaited value a program point \p
|
||||
/// CtxI.
|
||||
Optional<ConstantInt *>
|
||||
getAssumedConstantInt(Attributor &A, const Instruction *CtxI = nullptr) const {
|
||||
ConstantRange RangeV = getAssumedConstantRange(A, CtxI);
|
||||
if (auto *C = RangeV.getSingleElement())
|
||||
return cast<ConstantInt>(
|
||||
ConstantInt::get(getAssociatedValue().getType(), *C));
|
||||
if (RangeV.isEmptySet())
|
||||
return llvm::None;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/// Unique ID (due to the unique address)
|
||||
static const char ID;
|
||||
};
|
||||
|
||||
} // end namespace llvm
|
||||
|
||||
#endif // LLVM_TRANSFORMS_IPO_FUNCTIONATTRS_H
|
||||
|
|
|
@ -23,8 +23,10 @@
|
|||
#include "llvm/Analysis/CaptureTracking.h"
|
||||
#include "llvm/Analysis/EHPersonalities.h"
|
||||
#include "llvm/Analysis/GlobalsModRef.h"
|
||||
#include "llvm/Analysis/LazyValueInfo.h"
|
||||
#include "llvm/Analysis/Loads.h"
|
||||
#include "llvm/Analysis/MemoryBuiltins.h"
|
||||
#include "llvm/Analysis/ScalarEvolution.h"
|
||||
#include "llvm/Analysis/ValueTracking.h"
|
||||
#include "llvm/IR/Argument.h"
|
||||
#include "llvm/IR/Attributes.h"
|
||||
|
@ -126,6 +128,7 @@ PIPE_OPERATOR(AANoFree)
|
|||
PIPE_OPERATOR(AAHeapToStack)
|
||||
PIPE_OPERATOR(AAReachability)
|
||||
PIPE_OPERATOR(AAMemoryBehavior)
|
||||
PIPE_OPERATOR(AAValueConstantRange)
|
||||
|
||||
#undef PIPE_OPERATOR
|
||||
} // namespace llvm
|
||||
|
@ -3375,6 +3378,8 @@ struct AADereferenceableFloating
|
|||
T.GlobalState &= DS.GlobalState;
|
||||
}
|
||||
|
||||
// TODO: Use `AAConstantRange` to infer dereferenceable bytes.
|
||||
|
||||
// For now we do not try to "increase" dereferenceability due to negative
|
||||
// indices as we first have to come up with code to deal with loops and
|
||||
// for overflows of the dereferenceable bytes.
|
||||
|
@ -4248,6 +4253,28 @@ struct AAValueSimplifyImpl : AAValueSimplify {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool askSimplifiedValueForAAValueConstantRange(Attributor &A) {
|
||||
if (!getAssociatedValue().getType()->isIntegerTy())
|
||||
return false;
|
||||
|
||||
const auto &ValueConstantRangeAA =
|
||||
A.getAAFor<AAValueConstantRange>(*this, getIRPosition());
|
||||
|
||||
Optional<ConstantInt *> COpt =
|
||||
ValueConstantRangeAA.getAssumedConstantInt(A);
|
||||
if (COpt.hasValue()) {
|
||||
if (auto *C = COpt.getValue())
|
||||
SimplifiedAssociatedValue = C;
|
||||
else
|
||||
return false;
|
||||
} else {
|
||||
// FIXME: It should be llvm::None but if you set llvm::None,
|
||||
// values are mistakenly infered as `undef` now.
|
||||
SimplifiedAssociatedValue = &getAssociatedValue();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// See AbstractAttribute::manifest(...).
|
||||
ChangeStatus manifest(Attributor &A) override {
|
||||
ChangeStatus Changed = ChangeStatus::UNCHANGED;
|
||||
|
@ -4330,7 +4357,8 @@ struct AAValueSimplifyArgument final : AAValueSimplifyImpl {
|
|||
};
|
||||
|
||||
if (!A.checkForAllCallSites(PredForCallSite, *this, true))
|
||||
return indicatePessimisticFixpoint();
|
||||
if (!askSimplifiedValueForAAValueConstantRange(A))
|
||||
return indicatePessimisticFixpoint();
|
||||
|
||||
// If a candicate was found in this update, return CHANGED.
|
||||
return HasValueBefore == SimplifiedAssociatedValue.hasValue()
|
||||
|
@ -4356,7 +4384,8 @@ struct AAValueSimplifyReturned : AAValueSimplifyImpl {
|
|||
};
|
||||
|
||||
if (!A.checkForAllReturnedValues(PredForReturned, *this))
|
||||
return indicatePessimisticFixpoint();
|
||||
if (!askSimplifiedValueForAAValueConstantRange(A))
|
||||
return indicatePessimisticFixpoint();
|
||||
|
||||
// If a candicate was found in this update, return CHANGED.
|
||||
return HasValueBefore == SimplifiedAssociatedValue.hasValue()
|
||||
|
@ -4389,10 +4418,10 @@ struct AAValueSimplifyFloating : AAValueSimplifyImpl {
|
|||
auto &AA = A.getAAFor<AAValueSimplify>(*this, IRPosition::value(V));
|
||||
if (!Stripped && this == &AA) {
|
||||
// TODO: Look the instruction and check recursively.
|
||||
|
||||
LLVM_DEBUG(
|
||||
dbgs() << "[Attributor][ValueSimplify] Can't be stripped more : "
|
||||
<< V << "\n");
|
||||
indicatePessimisticFixpoint();
|
||||
return false;
|
||||
}
|
||||
return checkAndUpdate(A, *this, V, SimplifiedAssociatedValue);
|
||||
|
@ -4401,7 +4430,8 @@ struct AAValueSimplifyFloating : AAValueSimplifyImpl {
|
|||
if (!genericValueTraversal<AAValueSimplify, BooleanState>(
|
||||
A, getIRPosition(), *this, static_cast<BooleanState &>(*this),
|
||||
VisitValueCB))
|
||||
return indicatePessimisticFixpoint();
|
||||
if (!askSimplifiedValueForAAValueConstantRange(A))
|
||||
return indicatePessimisticFixpoint();
|
||||
|
||||
// If a candicate was found in this update, return CHANGED.
|
||||
|
||||
|
@ -5172,7 +5202,451 @@ void AAMemoryBehaviorFloating::analyzeUseIn(Attributor &A, const Use *U,
|
|||
if (UserI->mayWriteToMemory())
|
||||
removeAssumedBits(NO_WRITES);
|
||||
}
|
||||
/// ------------------ Value Constant Range Attribute -------------------------
|
||||
|
||||
struct AAValueConstantRangeImpl : AAValueConstantRange {
|
||||
using StateType = IntegerRangeState;
|
||||
AAValueConstantRangeImpl(const IRPosition &IRP) : AAValueConstantRange(IRP) {}
|
||||
|
||||
/// See AbstractAttribute::getAsStr().
|
||||
const std::string getAsStr() const override {
|
||||
std::string Str;
|
||||
llvm::raw_string_ostream OS(Str);
|
||||
OS << "range(" << getBitWidth() << ")<";
|
||||
getKnown().print(OS);
|
||||
OS << " / ";
|
||||
getAssumed().print(OS);
|
||||
OS << ">";
|
||||
return OS.str();
|
||||
}
|
||||
|
||||
/// Helper function to get a SCEV expr for the associated value at program
|
||||
/// point \p I.
|
||||
const SCEV *getSCEV(Attributor &A, const Instruction *I = nullptr) const {
|
||||
if (!getAnchorScope())
|
||||
return nullptr;
|
||||
|
||||
ScalarEvolution *SE =
|
||||
A.getInfoCache().getAnalysisResultForFunction<ScalarEvolutionAnalysis>(
|
||||
*getAnchorScope());
|
||||
|
||||
LoopInfo *LI = A.getInfoCache().getAnalysisResultForFunction<LoopAnalysis>(
|
||||
*getAnchorScope());
|
||||
|
||||
if (!SE || !LI)
|
||||
return nullptr;
|
||||
|
||||
const SCEV *S = SE->getSCEV(&getAssociatedValue());
|
||||
if (!I)
|
||||
return S;
|
||||
|
||||
return SE->getSCEVAtScope(S, LI->getLoopFor(I->getParent()));
|
||||
}
|
||||
|
||||
/// Helper function to get a range from SCEV for the associated value at
|
||||
/// program point \p I.
|
||||
ConstantRange getConstantRangeFromSCEV(Attributor &A,
|
||||
const Instruction *I = nullptr) const {
|
||||
if (!getAnchorScope())
|
||||
return getWorstState(getBitWidth());
|
||||
|
||||
ScalarEvolution *SE =
|
||||
A.getInfoCache().getAnalysisResultForFunction<ScalarEvolutionAnalysis>(
|
||||
*getAnchorScope());
|
||||
|
||||
const SCEV *S = getSCEV(A, I);
|
||||
if (!SE || !S)
|
||||
return getWorstState(getBitWidth());
|
||||
|
||||
return SE->getUnsignedRange(S);
|
||||
}
|
||||
|
||||
/// Helper function to get a range from LVI for the associated value at
|
||||
/// program point \p I.
|
||||
ConstantRange
|
||||
getConstantRangeFromLVI(Attributor &A,
|
||||
const Instruction *CtxI = nullptr) const {
|
||||
if (!getAnchorScope())
|
||||
return getWorstState(getBitWidth());
|
||||
|
||||
LazyValueInfo *LVI =
|
||||
A.getInfoCache().getAnalysisResultForFunction<LazyValueAnalysis>(
|
||||
*getAnchorScope());
|
||||
|
||||
if (!LVI || !CtxI)
|
||||
return getWorstState(getBitWidth());
|
||||
return LVI->getConstantRange(&getAssociatedValue(),
|
||||
const_cast<BasicBlock *>(CtxI->getParent()),
|
||||
const_cast<Instruction *>(CtxI));
|
||||
}
|
||||
|
||||
/// See AAValueConstantRange::getKnownConstantRange(..).
|
||||
ConstantRange
|
||||
getKnownConstantRange(Attributor &A,
|
||||
const Instruction *CtxI = nullptr) const override {
|
||||
if (!CtxI || CtxI == getCtxI())
|
||||
return getKnown();
|
||||
|
||||
ConstantRange LVIR = getConstantRangeFromLVI(A, CtxI);
|
||||
ConstantRange SCEVR = getConstantRangeFromSCEV(A, CtxI);
|
||||
return getKnown().intersectWith(SCEVR).intersectWith(LVIR);
|
||||
}
|
||||
|
||||
/// See AAValueConstantRange::getAssumedConstantRange(..).
|
||||
ConstantRange
|
||||
getAssumedConstantRange(Attributor &A,
|
||||
const Instruction *CtxI = nullptr) const override {
|
||||
// TODO: Make SCEV use Attributor assumption.
|
||||
// We may be able to bound a variable range via assumptions in
|
||||
// Attributor. ex.) If x is assumed to be in [1, 3] and y is known to
|
||||
// evolve to x^2 + x, then we can say that y is in [2, 12].
|
||||
|
||||
if (!CtxI || CtxI == getCtxI())
|
||||
return getAssumed();
|
||||
|
||||
ConstantRange LVIR = getConstantRangeFromLVI(A, CtxI);
|
||||
ConstantRange SCEVR = getConstantRangeFromSCEV(A, CtxI);
|
||||
return getAssumed().intersectWith(SCEVR).intersectWith(LVIR);
|
||||
}
|
||||
|
||||
/// See AbstractAttribute::initialize(..).
|
||||
void initialize(Attributor &A) override {
|
||||
// Intersect a range given by SCEV.
|
||||
intersectKnown(getConstantRangeFromSCEV(A, getCtxI()));
|
||||
|
||||
// Intersect a range given by LVI.
|
||||
intersectKnown(getConstantRangeFromLVI(A, getCtxI()));
|
||||
}
|
||||
|
||||
/// Helper function to create MDNode for range metadata.
|
||||
static MDNode *
|
||||
getMDNodeForConstantRange(Type *Ty, LLVMContext &Ctx,
|
||||
const ConstantRange &AssumedConstantRange) {
|
||||
Metadata *LowAndHigh[] = {ConstantAsMetadata::get(ConstantInt::get(
|
||||
Ty, AssumedConstantRange.getLower())),
|
||||
ConstantAsMetadata::get(ConstantInt::get(
|
||||
Ty, AssumedConstantRange.getUpper()))};
|
||||
return MDNode::get(Ctx, LowAndHigh);
|
||||
}
|
||||
|
||||
/// Return true if \p Assumed is included in \p KnownRanges.
|
||||
static bool isBetterRange(const ConstantRange &Assumed, MDNode *KnownRanges) {
|
||||
|
||||
if (Assumed.isFullSet())
|
||||
return false;
|
||||
|
||||
if (!KnownRanges)
|
||||
return true;
|
||||
|
||||
// If multiple ranges are annotated in IR, we give up to annotate assumed
|
||||
// range for now.
|
||||
|
||||
// TODO: If there exists a known range which containts assumed range, we
|
||||
// can say assumed range is better.
|
||||
if (KnownRanges->getNumOperands() > 2)
|
||||
return false;
|
||||
|
||||
ConstantInt *Lower =
|
||||
mdconst::extract<ConstantInt>(KnownRanges->getOperand(0));
|
||||
ConstantInt *Upper =
|
||||
mdconst::extract<ConstantInt>(KnownRanges->getOperand(1));
|
||||
|
||||
ConstantRange Known(Lower->getValue(), Upper->getValue());
|
||||
return Known.contains(Assumed) && Known != Assumed;
|
||||
}
|
||||
|
||||
/// Helper function to set range metadata.
|
||||
static bool
|
||||
setRangeMetadataIfisBetterRange(Instruction *I,
|
||||
const ConstantRange &AssumedConstantRange) {
|
||||
auto *OldRangeMD = I->getMetadata(LLVMContext::MD_range);
|
||||
if (isBetterRange(AssumedConstantRange, OldRangeMD)) {
|
||||
if (!AssumedConstantRange.isEmptySet()) {
|
||||
I->setMetadata(LLVMContext::MD_range,
|
||||
getMDNodeForConstantRange(I->getType(), I->getContext(),
|
||||
AssumedConstantRange));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// See AbstractAttribute::manifest()
|
||||
ChangeStatus manifest(Attributor &A) override {
|
||||
ChangeStatus Changed = ChangeStatus::UNCHANGED;
|
||||
ConstantRange AssumedConstantRange = getAssumedConstantRange(A);
|
||||
assert(!AssumedConstantRange.isFullSet() && "Invalid state");
|
||||
|
||||
auto &V = getAssociatedValue();
|
||||
if (!AssumedConstantRange.isEmptySet() &&
|
||||
!AssumedConstantRange.isSingleElement()) {
|
||||
if (Instruction *I = dyn_cast<Instruction>(&V))
|
||||
if (isa<CallInst>(I) || isa<LoadInst>(I))
|
||||
if (setRangeMetadataIfisBetterRange(I, AssumedConstantRange))
|
||||
Changed = ChangeStatus::CHANGED;
|
||||
}
|
||||
|
||||
return Changed;
|
||||
}
|
||||
};
|
||||
|
||||
struct AAValueConstantRangeArgument final : public AAValueConstantRangeImpl {
|
||||
|
||||
AAValueConstantRangeArgument(const IRPosition &IRP)
|
||||
: AAValueConstantRangeImpl(IRP) {}
|
||||
|
||||
/// See AbstractAttribute::updateImpl(...).
|
||||
ChangeStatus updateImpl(Attributor &A) override {
|
||||
// TODO: Use AAArgumentFromCallSiteArguments
|
||||
|
||||
IntegerRangeState S(getBitWidth());
|
||||
clampCallSiteArgumentStates<AAValueConstantRange, IntegerRangeState>(
|
||||
A, *this, S);
|
||||
|
||||
// TODO: If we know we visited all incoming values, thus no are assumed
|
||||
// dead, we can take the known information from the state T.
|
||||
return clampStateAndIndicateChange<IntegerRangeState>(this->getState(), S);
|
||||
}
|
||||
|
||||
/// See AbstractAttribute::trackStatistics()
|
||||
void trackStatistics() const override {
|
||||
STATS_DECLTRACK_ARG_ATTR(value_range)
|
||||
}
|
||||
};
|
||||
|
||||
struct AAValueConstantRangeReturned : AAValueConstantRangeImpl {
|
||||
AAValueConstantRangeReturned(const IRPosition &IRP)
|
||||
: AAValueConstantRangeImpl(IRP) {}
|
||||
|
||||
/// See AbstractAttribute::updateImpl(...).
|
||||
ChangeStatus updateImpl(Attributor &A) override {
|
||||
// TODO: Use AAReturnedFromReturnedValues
|
||||
|
||||
// TODO: If we know we visited all returned values, thus no are assumed
|
||||
// dead, we can take the known information from the state T.
|
||||
|
||||
IntegerRangeState S(getBitWidth());
|
||||
|
||||
clampReturnedValueStates<AAValueConstantRange, IntegerRangeState>(A, *this,
|
||||
S);
|
||||
return clampStateAndIndicateChange<StateType>(this->getState(), S);
|
||||
}
|
||||
|
||||
/// See AbstractAttribute::trackStatistics()
|
||||
void trackStatistics() const override {
|
||||
STATS_DECLTRACK_FNRET_ATTR(value_range)
|
||||
}
|
||||
};
|
||||
|
||||
struct AAValueConstantRangeFloating : AAValueConstantRangeImpl {
|
||||
AAValueConstantRangeFloating(const IRPosition &IRP)
|
||||
: AAValueConstantRangeImpl(IRP) {}
|
||||
|
||||
/// See AbstractAttribute::initialize(...).
|
||||
void initialize(Attributor &A) override {
|
||||
AAValueConstantRange::initialize(A);
|
||||
Value &V = getAssociatedValue();
|
||||
|
||||
if (auto *C = dyn_cast<ConstantInt>(&V)) {
|
||||
unionAssumed(ConstantRange(C->getValue()));
|
||||
indicateOptimisticFixpoint();
|
||||
return;
|
||||
}
|
||||
|
||||
if (isa<UndefValue>(&V)) {
|
||||
indicateOptimisticFixpoint();
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto *I = dyn_cast<Instruction>(&V))
|
||||
if (isa<BinaryOperator>(I) || isa<CmpInst>(I)) {
|
||||
Value *LHS = I->getOperand(0);
|
||||
Value *RHS = I->getOperand(1);
|
||||
|
||||
if (LHS->getType()->isIntegerTy() && RHS->getType()->isIntegerTy())
|
||||
return;
|
||||
}
|
||||
|
||||
// If it is a load instruction with range metadata, use it.
|
||||
if (LoadInst *LI = dyn_cast<LoadInst>(&V))
|
||||
if (auto *RangeMD = LI->getMetadata(LLVMContext::MD_range)) {
|
||||
intersectKnown(getConstantRangeFromMetadata(*RangeMD));
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise we give up.
|
||||
indicatePessimisticFixpoint();
|
||||
|
||||
LLVM_DEBUG(dbgs() << "[Attributor][AAValueConstantRange] We give up: "
|
||||
<< getAssociatedValue());
|
||||
}
|
||||
|
||||
bool calculateBinaryOperator(Attributor &A, BinaryOperator *BinOp,
|
||||
IntegerRangeState &T, Instruction *CtxI) {
|
||||
Value *LHS = BinOp->getOperand(0);
|
||||
Value *RHS = BinOp->getOperand(1);
|
||||
|
||||
auto &LHSAA =
|
||||
A.getAAFor<AAValueConstantRange>(*this, IRPosition::value(*LHS));
|
||||
auto LHSAARange = LHSAA.getAssumedConstantRange(A, CtxI);
|
||||
|
||||
auto &RHSAA =
|
||||
A.getAAFor<AAValueConstantRange>(*this, IRPosition::value(*RHS));
|
||||
auto RHSAARange = RHSAA.getAssumedConstantRange(A, CtxI);
|
||||
|
||||
auto AssumedRange = LHSAARange.binaryOp(BinOp->getOpcode(), RHSAARange);
|
||||
|
||||
T.unionAssumed(AssumedRange);
|
||||
|
||||
// TODO: Track a known state too.
|
||||
|
||||
return T.isValidState();
|
||||
}
|
||||
|
||||
bool calculateCmpInst(Attributor &A, CmpInst *CmpI, IntegerRangeState &T,
|
||||
Instruction *CtxI) {
|
||||
Value *LHS = CmpI->getOperand(0);
|
||||
Value *RHS = CmpI->getOperand(1);
|
||||
|
||||
auto &LHSAA =
|
||||
A.getAAFor<AAValueConstantRange>(*this, IRPosition::value(*LHS));
|
||||
auto &RHSAA =
|
||||
A.getAAFor<AAValueConstantRange>(*this, IRPosition::value(*RHS));
|
||||
|
||||
auto LHSAARange = LHSAA.getAssumedConstantRange(A, CtxI);
|
||||
auto RHSAARange = RHSAA.getAssumedConstantRange(A, CtxI);
|
||||
|
||||
// If one of them is empty set, we can't decide.
|
||||
if (LHSAARange.isEmptySet() || RHSAARange.isEmptySet())
|
||||
return true;
|
||||
|
||||
bool MustTrue = false, MustFalse = false;
|
||||
|
||||
auto AllowedRegion =
|
||||
ConstantRange::makeAllowedICmpRegion(CmpI->getPredicate(), RHSAARange);
|
||||
|
||||
auto SatisfyingRegion = ConstantRange::makeSatisfyingICmpRegion(
|
||||
CmpI->getPredicate(), RHSAARange);
|
||||
|
||||
if (AllowedRegion.intersectWith(LHSAARange).isEmptySet())
|
||||
MustFalse = true;
|
||||
|
||||
if (SatisfyingRegion.contains(LHSAARange))
|
||||
MustTrue = true;
|
||||
|
||||
assert((!MustTrue || !MustFalse) &&
|
||||
"Either MustTrue or MustFalse should be false!");
|
||||
|
||||
if (MustTrue)
|
||||
T.unionAssumed(ConstantRange(APInt(/* numBits */ 1, /* val */ 1)));
|
||||
else if (MustFalse)
|
||||
T.unionAssumed(ConstantRange(APInt(/* numBits */ 1, /* val */ 0)));
|
||||
else
|
||||
T.unionAssumed(ConstantRange(/* BitWidth */ 1, /* isFullSet */ true));
|
||||
|
||||
LLVM_DEBUG(dbgs() << "[AAValueConstantRange] " << *CmpI << " " << LHSAA
|
||||
<< " " << RHSAA << "\n");
|
||||
|
||||
// TODO: Track a known state too.
|
||||
return T.isValidState();
|
||||
}
|
||||
|
||||
/// See AbstractAttribute::updateImpl(...).
|
||||
ChangeStatus updateImpl(Attributor &A) override {
|
||||
Instruction *CtxI = getCtxI();
|
||||
auto VisitValueCB = [&](Value &V, IntegerRangeState &T,
|
||||
bool Stripped) -> bool {
|
||||
Instruction *I = dyn_cast<Instruction>(&V);
|
||||
if (!I) {
|
||||
|
||||
// If the value is not instruction, we query AA to Attributor.
|
||||
const auto &AA =
|
||||
A.getAAFor<AAValueConstantRange>(*this, IRPosition::value(V));
|
||||
|
||||
// Clamp operator is not used to utilize a program point CtxI.
|
||||
T.unionAssumed(AA.getAssumedConstantRange(A, CtxI));
|
||||
|
||||
return T.isValidState();
|
||||
}
|
||||
|
||||
if (auto *BinOp = dyn_cast<BinaryOperator>(I))
|
||||
return calculateBinaryOperator(A, BinOp, T, CtxI);
|
||||
else if (auto *CmpI = dyn_cast<CmpInst>(I))
|
||||
return calculateCmpInst(A, CmpI, T, CtxI);
|
||||
else {
|
||||
// Give up with other instructions.
|
||||
// TODO: Add other instructions
|
||||
|
||||
T.indicatePessimisticFixpoint();
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
IntegerRangeState T(getBitWidth());
|
||||
|
||||
if (!genericValueTraversal<AAValueConstantRange, IntegerRangeState>(
|
||||
A, getIRPosition(), *this, T, VisitValueCB))
|
||||
return indicatePessimisticFixpoint();
|
||||
|
||||
return clampStateAndIndicateChange(getState(), T);
|
||||
}
|
||||
|
||||
/// See AbstractAttribute::trackStatistics()
|
||||
void trackStatistics() const override {
|
||||
STATS_DECLTRACK_FLOATING_ATTR(value_range)
|
||||
}
|
||||
};
|
||||
|
||||
struct AAValueConstantRangeFunction : AAValueConstantRangeImpl {
|
||||
AAValueConstantRangeFunction(const IRPosition &IRP)
|
||||
: AAValueConstantRangeImpl(IRP) {}
|
||||
|
||||
/// See AbstractAttribute::initialize(...).
|
||||
ChangeStatus updateImpl(Attributor &A) override {
|
||||
llvm_unreachable("AAValueConstantRange(Function|CallSite)::updateImpl will "
|
||||
"not be called");
|
||||
}
|
||||
|
||||
/// See AbstractAttribute::trackStatistics()
|
||||
void trackStatistics() const override { STATS_DECLTRACK_FN_ATTR(value_range) }
|
||||
};
|
||||
|
||||
struct AAValueConstantRangeCallSite : AAValueConstantRangeFunction {
|
||||
AAValueConstantRangeCallSite(const IRPosition &IRP)
|
||||
: AAValueConstantRangeFunction(IRP) {}
|
||||
|
||||
/// See AbstractAttribute::trackStatistics()
|
||||
void trackStatistics() const override { STATS_DECLTRACK_CS_ATTR(value_range) }
|
||||
};
|
||||
|
||||
struct AAValueConstantRangeCallSiteReturned : AAValueConstantRangeReturned {
|
||||
AAValueConstantRangeCallSiteReturned(const IRPosition &IRP)
|
||||
: AAValueConstantRangeReturned(IRP) {}
|
||||
|
||||
/// See AbstractAttribute::initialize(...).
|
||||
void initialize(Attributor &A) override {
|
||||
// If it is a load instruction with range metadata, use the metadata.
|
||||
if (CallInst *CI = dyn_cast<CallInst>(&getAssociatedValue()))
|
||||
if (auto *RangeMD = CI->getMetadata(LLVMContext::MD_range))
|
||||
intersectKnown(getConstantRangeFromMetadata(*RangeMD));
|
||||
|
||||
AAValueConstantRangeReturned::initialize(A);
|
||||
}
|
||||
|
||||
/// See AbstractAttribute::trackStatistics()
|
||||
void trackStatistics() const override {
|
||||
STATS_DECLTRACK_CSRET_ATTR(value_range)
|
||||
}
|
||||
};
|
||||
struct AAValueConstantRangeCallSiteArgument : AAValueConstantRangeFloating {
|
||||
AAValueConstantRangeCallSiteArgument(const IRPosition &IRP)
|
||||
: AAValueConstantRangeFloating(IRP) {}
|
||||
|
||||
/// See AbstractAttribute::trackStatistics()
|
||||
void trackStatistics() const override {
|
||||
STATS_DECLTRACK_CSARG_ATTR(value_range)
|
||||
}
|
||||
};
|
||||
/// ----------------------------------------------------------------------------
|
||||
/// Attributor
|
||||
/// ----------------------------------------------------------------------------
|
||||
|
@ -6180,10 +6654,16 @@ void Attributor::identifyDefaultAbstractAttributes(Function &F) {
|
|||
return true;
|
||||
|
||||
if (!Callee->getReturnType()->isVoidTy() && !CS->use_empty()) {
|
||||
|
||||
IRPosition CSRetPos = IRPosition::callsite_returned(CS);
|
||||
|
||||
// Call site return values might be dead.
|
||||
getOrCreateAAFor<AAIsDead>(CSRetPos);
|
||||
|
||||
// Call site return integer values might be limited by a constant range.
|
||||
if (Callee->getReturnType()->isIntegerTy()) {
|
||||
getOrCreateAAFor<AAValueConstantRange>(CSRetPos);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0, e = CS.getNumArgOperands(); i < e; i++) {
|
||||
|
@ -6283,13 +6763,23 @@ raw_ostream &llvm::operator<<(raw_ostream &OS, const IRPosition &Pos) {
|
|||
}
|
||||
|
||||
template <typename base_ty, base_ty BestState, base_ty WorstState>
|
||||
raw_ostream &llvm::
|
||||
operator<<(raw_ostream &OS,
|
||||
const IntegerStateBase<base_ty, BestState, WorstState> &S) {
|
||||
raw_ostream &
|
||||
llvm::operator<<(raw_ostream &OS,
|
||||
const IntegerStateBase<base_ty, BestState, WorstState> &S) {
|
||||
return OS << "(" << S.getKnown() << "-" << S.getAssumed() << ")"
|
||||
<< static_cast<const AbstractState &>(S);
|
||||
}
|
||||
|
||||
raw_ostream &llvm::operator<<(raw_ostream &OS, const IntegerRangeState &S) {
|
||||
OS << "range-state(" << S.getBitWidth() << ")<";
|
||||
S.getKnown().print(OS);
|
||||
OS << " / ";
|
||||
S.getAssumed().print(OS);
|
||||
OS << ">";
|
||||
|
||||
return OS << static_cast<const AbstractState &>(S);
|
||||
}
|
||||
|
||||
raw_ostream &llvm::operator<<(raw_ostream &OS, const AbstractState &S) {
|
||||
return OS << (!S.isValidState() ? "top" : (S.isAtFixpoint() ? "fix" : ""));
|
||||
}
|
||||
|
@ -6404,6 +6894,7 @@ const char AANoCapture::ID = 0;
|
|||
const char AAValueSimplify::ID = 0;
|
||||
const char AAHeapToStack::ID = 0;
|
||||
const char AAMemoryBehavior::ID = 0;
|
||||
const char AAValueConstantRange::ID = 0;
|
||||
|
||||
// Macro magic to create the static generator function for attributes that
|
||||
// follow the naming scheme.
|
||||
|
@ -6509,6 +7000,7 @@ CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANoAlias)
|
|||
CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AADereferenceable)
|
||||
CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAAlign)
|
||||
CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANoCapture)
|
||||
CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAValueConstantRange)
|
||||
|
||||
CREATE_ALL_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAValueSimplify)
|
||||
CREATE_ALL_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAIsDead)
|
||||
|
|
|
@ -11,8 +11,7 @@ define i1 @invokecaller(i1 %C) personality i32 (...)* @__gxx_personality_v0 {
|
|||
; CHECK: .i2c:
|
||||
; CHECK-NEXT: unreachable
|
||||
; CHECK: OK:
|
||||
; CHECK-NEXT: [[Y:%.*]] = icmp ne i32 52, 0
|
||||
; CHECK-NEXT: ret i1 [[Y]]
|
||||
; CHECK-NEXT: ret i1 true
|
||||
; CHECK: FAIL:
|
||||
; CHECK-NEXT: unreachable
|
||||
;
|
||||
|
@ -48,8 +47,7 @@ define i1 @caller(i1 %C) {
|
|||
; CHECK-LABEL: define {{[^@]+}}@caller
|
||||
; CHECK-SAME: (i1 [[C:%.*]])
|
||||
; CHECK-NEXT: [[X:%.*]] = call i32 @foo(i1 [[C]])
|
||||
; CHECK-NEXT: [[Y:%.*]] = icmp ne i32 52, 0
|
||||
; CHECK-NEXT: ret i1 [[Y]]
|
||||
; CHECK-NEXT: ret i1 true
|
||||
;
|
||||
%X = call i32 @foo( i1 %C ) ; <i32> [#uses=1]
|
||||
%Y = icmp ne i32 %X, 0 ; <i1> [#uses=1]
|
||||
|
|
|
@ -33,12 +33,11 @@ define internal i32 @test1(i1 %c) {
|
|||
; CHECK-NEXT: br label [[IF_THEN:%.*]]
|
||||
; CHECK: if.then:
|
||||
; CHECK-NEXT: [[CALL:%.*]] = call i32 @testf(i1 [[C]])
|
||||
; CHECK-NEXT: [[RES:%.*]] = icmp eq i32 10, 10
|
||||
; CHECK-NEXT: br i1 [[RES]], label [[RET1:%.*]], label [[RET2:%.*]]
|
||||
; CHECK-NEXT: br label [[RET1:%.*]]
|
||||
; CHECK: ret1:
|
||||
; CHECK-NEXT: ret i32 99
|
||||
; CHECK: ret2:
|
||||
; CHECK-NEXT: ret i32 0
|
||||
; CHECK-NEXT: unreachable
|
||||
;
|
||||
entry:
|
||||
br label %if.then
|
||||
|
@ -59,7 +58,7 @@ define i32 @main(i1 %c) {
|
|||
; CHECK-LABEL: define {{[^@]+}}@main
|
||||
; CHECK-SAME: (i1 [[C:%.*]])
|
||||
; CHECK-NEXT: [[RES:%.*]] = call i32 @test1(i1 [[C]])
|
||||
; CHECK-NEXT: ret i32 [[RES]]
|
||||
; CHECK-NEXT: ret i32 99
|
||||
;
|
||||
%res = call i32 @test1(i1 %c)
|
||||
ret i32 %res
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
; RUN: opt -attributor -attributor-manifest-internal --attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=2 -S < %s | FileCheck %s --check-prefix=ATTRIBUTOR
|
||||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
|
||||
; RUN: opt -attributor -attributor-manifest-internal --attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=4 -S < %s | FileCheck %s --check-prefix=ATTRIBUTOR
|
||||
|
||||
|
||||
declare void @deref_phi_user(i32* %a);
|
||||
|
@ -207,3 +208,105 @@ define void @deref_or_null_and_nonnull(i32* dereferenceable_or_null(100) %0) {
|
|||
store i32 1, i32* %0
|
||||
ret void
|
||||
}
|
||||
|
||||
; TEST 8
|
||||
; Use Constant range in deereferenceable
|
||||
; void g(int *p, long long int *range){
|
||||
; int r = *range ; // [10, 99]
|
||||
; fill_range(p, *range);
|
||||
; }
|
||||
|
||||
; void fill_range(int* p, long long int start){
|
||||
; for(long long int i = start;i<start+10;i++){
|
||||
; // If p[i] is inbounds, p is dereferenceable(40) at least.
|
||||
; p[i] = i;
|
||||
; }
|
||||
; }
|
||||
|
||||
define internal void @fill_range_not_inbounds(i32* %p, i64 %start){
|
||||
; ATTRIBUTOR-LABEL: define {{[^@]+}}@fill_range_not_inbounds
|
||||
; NOTE: %p should not be dereferenceable
|
||||
; ATTRIBUTOR-SAME: (i32* nocapture nofree writeonly [[P:%.*]], i64 [[START:%.*]])
|
||||
; ATTRIBUTOR-NEXT: entry:
|
||||
; ATTRIBUTOR-NEXT: [[TMP0:%.*]] = add nsw i64 [[START:%.*]], 9
|
||||
; ATTRIBUTOR-NEXT: br label [[FOR_BODY:%.*]]
|
||||
; ATTRIBUTOR: for.cond.cleanup:
|
||||
; ATTRIBUTOR-NEXT: ret void
|
||||
; ATTRIBUTOR: for.body:
|
||||
; ATTRIBUTOR-NEXT: [[I_06:%.*]] = phi i64 [ [[START]], [[ENTRY:%.*]] ], [ [[INC:%.*]], [[FOR_BODY]] ]
|
||||
; ATTRIBUTOR-NEXT: [[CONV:%.*]] = trunc i64 [[I_06]] to i32
|
||||
; ATTRIBUTOR-NEXT: [[ARRAYIDX:%.*]] = getelementptr i32, i32* [[P:%.*]], i64 [[I_06]]
|
||||
; ATTRIBUTOR-NEXT: store i32 [[CONV]], i32* [[ARRAYIDX]], align 4
|
||||
; ATTRIBUTOR-NEXT: [[INC]] = add nsw i64 [[I_06]], 1
|
||||
; ATTRIBUTOR-NEXT: [[CMP:%.*]] = icmp slt i64 [[I_06]], [[TMP0]]
|
||||
; ATTRIBUTOR-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_COND_CLEANUP:%.*]]
|
||||
;
|
||||
entry:
|
||||
%0 = add nsw i64 %start, 9
|
||||
br label %for.body
|
||||
|
||||
for.cond.cleanup: ; preds = %for.body
|
||||
ret void
|
||||
|
||||
for.body: ; preds = %entry, %for.body
|
||||
%i.06 = phi i64 [ %start, %entry ], [ %inc, %for.body ]
|
||||
%conv = trunc i64 %i.06 to i32
|
||||
%arrayidx = getelementptr i32, i32* %p, i64 %i.06
|
||||
store i32 %conv, i32* %arrayidx, align 4
|
||||
%inc = add nsw i64 %i.06, 1
|
||||
%cmp = icmp slt i64 %i.06, %0
|
||||
br i1 %cmp, label %for.body, label %for.cond.cleanup
|
||||
}
|
||||
define internal void @fill_range_inbounds(i32* %p, i64 %start){
|
||||
; ATTRIBUTOR-LABEL: define {{[^@]+}}@fill_range_inbounds
|
||||
; FIXME: %p should be dereferenceable(40)
|
||||
; ATTRIBUTOR-SAME: (i32* nocapture nofree writeonly [[P:%.*]], i64 [[START:%.*]])
|
||||
; ATTRIBUTOR-NEXT: entry:
|
||||
; ATTRIBUTOR-NEXT: [[TMP0:%.*]] = add nsw i64 [[START:%.*]], 9
|
||||
; ATTRIBUTOR-NEXT: br label [[FOR_BODY:%.*]]
|
||||
; ATTRIBUTOR: for.cond.cleanup:
|
||||
; ATTRIBUTOR-NEXT: ret void
|
||||
; ATTRIBUTOR: for.body:
|
||||
; ATTRIBUTOR-NEXT: [[I_06:%.*]] = phi i64 [ [[START]], [[ENTRY:%.*]] ], [ [[INC:%.*]], [[FOR_BODY]] ]
|
||||
; ATTRIBUTOR-NEXT: [[CONV:%.*]] = trunc i64 [[I_06]] to i32
|
||||
; ATTRIBUTOR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, i32* [[P:%.*]], i64 [[I_06]]
|
||||
; ATTRIBUTOR-NEXT: store i32 [[CONV]], i32* [[ARRAYIDX]], align 4
|
||||
; ATTRIBUTOR-NEXT: [[INC]] = add nsw i64 [[I_06]], 1
|
||||
; ATTRIBUTOR-NEXT: [[CMP:%.*]] = icmp slt i64 [[I_06]], [[TMP0]]
|
||||
; ATTRIBUTOR-NEXT: br i1 [[CMP]], label [[FOR_BODY]], label [[FOR_COND_CLEANUP:%.*]]
|
||||
;
|
||||
entry:
|
||||
%0 = add nsw i64 %start, 9
|
||||
br label %for.body
|
||||
|
||||
for.cond.cleanup: ; preds = %for.body
|
||||
ret void
|
||||
|
||||
for.body: ; preds = %entry, %for.body
|
||||
%i.06 = phi i64 [ %start, %entry ], [ %inc, %for.body ]
|
||||
%conv = trunc i64 %i.06 to i32
|
||||
%arrayidx = getelementptr inbounds i32, i32* %p, i64 %i.06
|
||||
store i32 %conv, i32* %arrayidx, align 4
|
||||
%inc = add nsw i64 %i.06, 1
|
||||
%cmp = icmp slt i64 %i.06, %0
|
||||
br i1 %cmp, label %for.body, label %for.cond.cleanup
|
||||
}
|
||||
|
||||
define void @call_fill_range(i32* nocapture %p, i64* nocapture readonly %range) {
|
||||
; ATTRIBUTOR-LABEL: define {{[^@]+}}@call_fill_range
|
||||
; ATTRIBUTOR-SAME: (i32* nocapture nofree writeonly [[P:%.*]], i64* nocapture nofree nonnull readonly align 8 dereferenceable(8) [[RANGE:%.*]])
|
||||
; ATTRIBUTOR-NEXT: entry:
|
||||
; ATTRIBUTOR-NEXT: [[TMP0:%.*]] = load i64, i64* [[RANGE:%.*]], align 8, !range !0
|
||||
; ATTRIBUTOR-NEXT: tail call void @fill_range_inbounds(i32* nocapture nofree writeonly [[P:%.*]], i64 [[TMP0]])
|
||||
; ATTRIBUTOR-NEXT: tail call void @fill_range_not_inbounds(i32* nocapture nofree writeonly [[P]], i64 [[TMP0]])
|
||||
; ATTRIBUTOR-NEXT: ret void
|
||||
;
|
||||
entry:
|
||||
%0 = load i64, i64* %range, align 8, !range !0
|
||||
tail call void @fill_range_inbounds(i32* %p, i64 %0)
|
||||
tail call void @fill_range_not_inbounds(i32* %p, i64 %0)
|
||||
ret void
|
||||
}
|
||||
|
||||
!0 = !{i64 10, i64 100}
|
||||
|
||||
|
|
|
@ -0,0 +1,181 @@
|
|||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
|
||||
; RUN: opt -passes=attributor -attributor-disable=false -S < %s | FileCheck %s
|
||||
|
||||
define i8 @test1(i32 %a, i32 %length) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@test1
|
||||
; CHECK-SAME: (i32 [[A:%.*]], i32 [[LENGTH:%.*]])
|
||||
; CHECK-NEXT: entry:
|
||||
; CHECK-NEXT: br label [[LOOP:%.*]]
|
||||
; CHECK: loop:
|
||||
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
|
||||
; CHECK-NEXT: br label [[BACKEDGE]]
|
||||
; CHECK: backedge:
|
||||
; CHECK-NEXT: [[IV_NEXT]] = add nsw i32 [[IV]], 1
|
||||
; CHECK-NEXT: [[CONT:%.*]] = icmp slt i32 [[IV_NEXT]], 400
|
||||
; CHECK-NEXT: br i1 [[CONT]], label [[LOOP]], label [[EXIT:%.*]]
|
||||
; CHECK: exit:
|
||||
; CHECK-NEXT: ret i8 0
|
||||
;
|
||||
entry:
|
||||
br label %loop
|
||||
|
||||
loop:
|
||||
%iv = phi i32 [0, %entry], [%iv.next, %backedge]
|
||||
%cnd = icmp sge i32 %iv, 0
|
||||
br i1 %cnd, label %backedge, label %exit
|
||||
|
||||
backedge:
|
||||
%iv.next = add nsw i32 %iv, 1
|
||||
%cont = icmp slt i32 %iv.next, 400
|
||||
br i1 %cont, label %loop, label %exit
|
||||
|
||||
exit:
|
||||
ret i8 0
|
||||
}
|
||||
|
||||
define i8 @test2(i32 %n) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@test2
|
||||
; CHECK-SAME: (i32 [[N:%.*]])
|
||||
; CHECK-NEXT: entry:
|
||||
; CHECK-NEXT: br label [[LOOP:%.*]]
|
||||
; CHECK: loop:
|
||||
; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
|
||||
; CHECK-NEXT: [[IV2:%.*]] = phi i32 [ [[N:%.*]], [[ENTRY]] ], [ [[IV2_NEXT:%.*]], [[BACKEDGE]] ]
|
||||
; CHECK-NEXT: [[CND1:%.*]] = icmp sge i32 [[IV]], 0
|
||||
; CHECK-NEXT: [[CND2:%.*]] = icmp sgt i32 [[IV2]], 0
|
||||
; CHECK-NEXT: [[CND:%.*]] = and i1 [[CND1]], [[CND2]]
|
||||
; CHECK-NEXT: br i1 [[CND]], label [[BACKEDGE]], label [[EXIT:%.*]]
|
||||
; CHECK: backedge:
|
||||
; CHECK-NEXT: [[IV_NEXT]] = add nsw i32 [[IV]], 1
|
||||
; CHECK-NEXT: [[IV2_NEXT]] = sub nsw i32 [[IV2]], 1
|
||||
; CHECK-NEXT: [[CONT1:%.*]] = icmp slt i32 [[IV_NEXT]], 400
|
||||
; CHECK-NEXT: [[CONT2:%.*]] = icmp sgt i32 [[IV2_NEXT]], 0
|
||||
; CHECK-NEXT: [[CONT:%.*]] = and i1 [[CONT1]], [[CONT2]]
|
||||
; CHECK-NEXT: br i1 [[CONT]], label [[LOOP]], label [[EXIT]]
|
||||
; CHECK: exit:
|
||||
; CHECK-NEXT: ret i8 0
|
||||
;
|
||||
entry:
|
||||
br label %loop
|
||||
|
||||
loop:
|
||||
%iv = phi i32 [0, %entry], [%iv.next, %backedge]
|
||||
%iv2 = phi i32 [%n, %entry], [%iv2.next, %backedge]
|
||||
|
||||
%cnd1 = icmp sge i32 %iv, 0
|
||||
%cnd2 = icmp sgt i32 %iv2, 0
|
||||
%cnd = and i1 %cnd1, %cnd2
|
||||
br i1 %cnd, label %backedge, label %exit
|
||||
|
||||
backedge:
|
||||
%iv.next = add nsw i32 %iv, 1
|
||||
%iv2.next = sub nsw i32 %iv2, 1
|
||||
%cont1 = icmp slt i32 %iv.next, 400
|
||||
%cont2 = icmp sgt i32 %iv2.next, 0
|
||||
%cont = and i1 %cont1, %cont2
|
||||
br i1 %cont, label %loop, label %exit
|
||||
|
||||
exit:
|
||||
ret i8 0
|
||||
}
|
||||
|
||||
; Merging cont block into do block.
|
||||
define i32 @test3(i32 %i, i1 %f, i32 %n) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@test3
|
||||
; CHECK-SAME: (i32 [[I:%.*]], i1 [[F:%.*]], i32 [[N:%.*]])
|
||||
; CHECK-NEXT: entry:
|
||||
; CHECK-NEXT: [[C:%.*]] = icmp ne i32 [[I:%.*]], -2134
|
||||
; CHECK-NEXT: br i1 [[C]], label [[DO:%.*]], label [[EXIT:%.*]]
|
||||
; CHECK: exit:
|
||||
; CHECK-NEXT: [[C1:%.*]] = icmp ne i32 [[I]], -42
|
||||
; CHECK-NEXT: br i1 [[C1]], label [[EXIT2:%.*]], label [[EXIT]]
|
||||
; CHECK: cont:
|
||||
; CHECK-NEXT: [[COND_3:%.*]] = icmp sgt i32 [[I]], [[N:%.*]]
|
||||
; CHECK-NEXT: br i1 [[COND_3]], label [[EXIT2]], label [[EXIT]]
|
||||
; CHECK: do:
|
||||
; CHECK-NEXT: [[COND_0:%.*]] = icmp sgt i32 [[I]], 0
|
||||
; CHECK-NEXT: [[CONSUME:%.*]] = call i32 @consume(i1 [[COND_0]])
|
||||
; CHECK-NEXT: [[COND:%.*]] = icmp eq i32 [[I]], 0
|
||||
; CHECK-NEXT: call void (i1, ...) @llvm.experimental.guard(i1 [[COND]]) [ "deopt"() ]
|
||||
; CHECK-NEXT: br label [[CONT:%.*]]
|
||||
; CHECK: exit2:
|
||||
; CHECK-NEXT: ret i32 30
|
||||
;
|
||||
entry:
|
||||
%c = icmp ne i32 %i, -2134
|
||||
br i1 %c, label %do, label %exit
|
||||
|
||||
exit:
|
||||
%c1 = icmp ne i32 %i, -42
|
||||
br i1 %c1, label %exit2, label %exit
|
||||
|
||||
; Here cont is merged to do and i is any value except -2134.
|
||||
; i is not the single value: zero.
|
||||
cont:
|
||||
%cond.3 = icmp sgt i32 %i, %n
|
||||
br i1 %cond.3, label %exit2, label %exit
|
||||
|
||||
do:
|
||||
%cond.0 = icmp sgt i32 %i, 0
|
||||
%consume = call i32 @consume(i1 %cond.0)
|
||||
%cond = icmp eq i32 %i, 0
|
||||
call void (i1, ...) @llvm.experimental.guard(i1 %cond) [ "deopt"() ]
|
||||
%cond.2 = icmp sgt i32 %i, 0
|
||||
br i1 %cond.2, label %exit, label %cont
|
||||
|
||||
exit2:
|
||||
; LatticeVal for: 'i32 %i' is: constantrange<-2134, 1>
|
||||
ret i32 30
|
||||
}
|
||||
|
||||
; FIXME: We should be able to merge cont into do.
|
||||
define i32 @test4(i32 %i, i1 %f, i32 %n) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@test4
|
||||
; CHECK-SAME: (i32 [[I:%.*]], i1 [[F:%.*]], i32 [[N:%.*]])
|
||||
; CHECK-NEXT: entry:
|
||||
; CHECK-NEXT: [[C:%.*]] = icmp ne i32 [[I:%.*]], -2134
|
||||
; CHECK-NEXT: br i1 [[C]], label [[DO:%.*]], label [[EXIT:%.*]]
|
||||
; CHECK: exit:
|
||||
; CHECK-NEXT: [[C1:%.*]] = icmp ne i32 [[I]], -42
|
||||
; CHECK-NEXT: br i1 [[C1]], label [[EXIT2:%.*]], label [[EXIT]]
|
||||
; CHECK: cont:
|
||||
; CHECK-NEXT: call void @dummy(i1 [[F:%.*]])
|
||||
; CHECK-NEXT: br label [[EXIT2]]
|
||||
; CHECK: do:
|
||||
; CHECK-NEXT: call void @dummy(i1 [[F]])
|
||||
; CHECK-NEXT: [[CONSUME:%.*]] = call i32 @exit()
|
||||
; CHECK-NEXT: call void @llvm.assume(i1 [[F]])
|
||||
; CHECK-NEXT: [[COND:%.*]] = icmp eq i1 [[F]], false
|
||||
; CHECK-NEXT: br i1 [[COND]], label [[EXIT]], label [[CONT:%.*]]
|
||||
; CHECK: exit2:
|
||||
; CHECK-NEXT: ret i32 30
|
||||
;
|
||||
; FIXME: COND should be replaced with false. This will be fixed by improving LVI.
|
||||
entry:
|
||||
%c = icmp ne i32 %i, -2134
|
||||
br i1 %c, label %do, label %exit
|
||||
|
||||
exit: ; preds = %do, %cont, %exit, %entry
|
||||
%c1 = icmp ne i32 %i, -42
|
||||
br i1 %c1, label %exit2, label %exit
|
||||
|
||||
cont: ; preds = %do
|
||||
call void @dummy(i1 %f)
|
||||
br label %exit2
|
||||
|
||||
do: ; preds = %entry
|
||||
call void @dummy(i1 %f)
|
||||
%consume = call i32 @exit()
|
||||
call void @llvm.assume(i1 %f)
|
||||
%cond = icmp eq i1 %f, false
|
||||
br i1 %cond, label %exit, label %cont
|
||||
|
||||
exit2: ; preds = %cont, %exit
|
||||
ret i32 30
|
||||
}
|
||||
|
||||
declare i32 @exit()
|
||||
declare i32 @consume(i1)
|
||||
declare void @llvm.assume(i1) nounwind
|
||||
declare void @dummy(i1) nounwind
|
||||
declare void @llvm.experimental.guard(i1, ...)
|
|
@ -0,0 +1,46 @@
|
|||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
|
||||
; RUN: opt -passes=attributor -attributor-disable=false -S < %s | FileCheck %s
|
||||
define i32 @test-ashr(i32 %c) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@test-ashr
|
||||
; CHECK-SAME: (i32 [[C:%.*]])
|
||||
; CHECK-NEXT: chk65:
|
||||
; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[C:%.*]], 65
|
||||
; CHECK-NEXT: br i1 [[CMP]], label [[RETURN:%.*]], label [[CHK0:%.*]]
|
||||
; CHECK: chk0:
|
||||
; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i32 [[C]], 0
|
||||
; CHECK-NEXT: br i1 [[CMP]], label [[RETURN]], label [[BB_IF:%.*]]
|
||||
; CHECK: bb_if:
|
||||
; CHECK-NEXT: [[ASHR_VAL:%.*]] = ashr exact i32 [[C]], 2
|
||||
; CHECK-NEXT: [[CMP2:%.*]] = icmp sgt i32 [[ASHR_VAL]], 15
|
||||
; CHECK-NEXT: br i1 [[CMP2]], label [[BB_THEN:%.*]], label [[RETURN]]
|
||||
; CHECK: bb_then:
|
||||
; CHECK-NEXT: [[CMP3:%.*]] = icmp eq i32 [[ASHR_VAL]], 16
|
||||
; CHECK-NEXT: [[DOT:%.*]] = select i1 [[CMP3]], i32 3, i32 2
|
||||
; CHECK-NEXT: br label [[RETURN]]
|
||||
; CHECK: return:
|
||||
; CHECK-NEXT: [[RETVAL:%.*]] = phi i32 [ 0, [[CHK65:%.*]] ], [ 1, [[CHK0]] ], [ [[DOT]], [[BB_THEN]] ], [ 4, [[BB_IF]] ]
|
||||
; CHECK-NEXT: ret i32 [[RETVAL]]
|
||||
;
|
||||
; FIXME: DOT should be replaced with 3
|
||||
chk65:
|
||||
%cmp = icmp sgt i32 %c, 65
|
||||
br i1 %cmp, label %return, label %chk0
|
||||
|
||||
chk0:
|
||||
%cmp1 = icmp slt i32 %c, 0
|
||||
br i1 %cmp, label %return, label %bb_if
|
||||
|
||||
bb_if:
|
||||
%ashr.val = ashr exact i32 %c, 2
|
||||
%cmp2 = icmp sgt i32 %ashr.val, 15
|
||||
br i1 %cmp2, label %bb_then, label %return
|
||||
|
||||
bb_then:
|
||||
%cmp3 = icmp eq i32 %ashr.val, 16
|
||||
%. = select i1 %cmp3, i32 3, i32 2
|
||||
br label %return
|
||||
|
||||
return:
|
||||
%retval = phi i32 [0, %chk65], [1, %chk0], [%., %bb_then], [4, %bb_if]
|
||||
ret i32 %retval
|
||||
}
|
|
@ -0,0 +1,446 @@
|
|||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
|
||||
; RUN: opt -passes=attributor -attributor-disable=false -S < %s | FileCheck %s
|
||||
|
||||
define i32 @test0(i32* %p) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@test0
|
||||
; CHECK-SAME: (i32* nocapture nofree nonnull readonly dereferenceable(4) [[P:%.*]])
|
||||
; CHECK-NEXT: [[A:%.*]] = load i32, i32* [[P]], !range !0
|
||||
; CHECK-NEXT: ret i32 [[A]]
|
||||
;
|
||||
%a = load i32, i32* %p, !range !0
|
||||
ret i32 %a
|
||||
}
|
||||
|
||||
define i32 @test0-range-check(i32* %p) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@test0-range-check
|
||||
; CHECK-SAME: (i32* nocapture nofree readonly [[P:%.*]])
|
||||
; CHECK-NEXT: [[A:%.*]] = tail call i32 @test0(i32* nocapture nofree readonly [[P]])
|
||||
; CHECK-SAME: !range !0
|
||||
; CHECK-NEXT: ret i32 [[A]]
|
||||
;
|
||||
%a = tail call i32 @test0(i32* %p)
|
||||
ret i32 %a
|
||||
}
|
||||
|
||||
declare void @use3-dummy(i1, i1, i1)
|
||||
define void @use3(i1, i1, i1) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@use3
|
||||
; CHECK-SAME: (i1 [[TMP0:%.*]], i1 [[TMP1:%.*]], i1 [[TMP2:%.*]])
|
||||
; CHECK-NEXT: tail call void @use3-dummy(i1 [[TMP0]], i1 [[TMP1]], i1 [[TMP2]])
|
||||
; CHECK-NEXT: ret void
|
||||
;
|
||||
tail call void @use3-dummy(i1 %0, i1 %1, i1 %2)
|
||||
ret void
|
||||
}
|
||||
|
||||
; TEST0 icmp test
|
||||
define void @test0-icmp-check(i32* %p){
|
||||
; CHECK-LABEL: define {{[^@]+}}@test0-icmp-check
|
||||
; CHECK-SAME: (i32* nocapture nofree readonly [[P:%.*]])
|
||||
; CHECK-NEXT: [[RET:%.*]] = tail call i32 @test0(i32* nocapture nofree readonly [[P]])
|
||||
; CHECK-SAME: !range !0
|
||||
; CHECK-NEXT: [[CMP_EQ_2:%.*]] = icmp eq i32 [[RET]], 9
|
||||
; CHECK-NEXT: [[CMP_EQ_3:%.*]] = icmp eq i32 [[RET]], 8
|
||||
; CHECK-NEXT: [[CMP_EQ_4:%.*]] = icmp eq i32 [[RET]], 1
|
||||
; CHECK-NEXT: [[CMP_EQ_5:%.*]] = icmp eq i32 [[RET]], 0
|
||||
; CHECK-NEXT: tail call void @use3(i1 false, i1 [[CMP_EQ_2]], i1 [[CMP_EQ_3]])
|
||||
; CHECK-NEXT: tail call void @use3(i1 [[CMP_EQ_4]], i1 [[CMP_EQ_5]], i1 false)
|
||||
; CHECK-NEXT: [[CMP_NE_2:%.*]] = icmp ne i32 [[RET]], 9
|
||||
; CHECK-NEXT: [[CMP_NE_3:%.*]] = icmp ne i32 [[RET]], 8
|
||||
; CHECK-NEXT: [[CMP_NE_4:%.*]] = icmp ne i32 [[RET]], 1
|
||||
; CHECK-NEXT: [[CMP_NE_5:%.*]] = icmp ne i32 [[RET]], 0
|
||||
; CHECK-NEXT: tail call void @use3(i1 true, i1 [[CMP_NE_2]], i1 [[CMP_NE_3]])
|
||||
; CHECK-NEXT: tail call void @use3(i1 [[CMP_NE_4]], i1 [[CMP_NE_5]], i1 true)
|
||||
; CHECK-NEXT: [[CMP_UGT_3:%.*]] = icmp ugt i32 [[RET]], 8
|
||||
; CHECK-NEXT: [[CMP_UGT_4:%.*]] = icmp ugt i32 [[RET]], 1
|
||||
; CHECK-NEXT: [[CMP_UGT_5:%.*]] = icmp ugt i32 [[RET]], 0
|
||||
; CHECK-NEXT: tail call void @use3(i1 false, i1 false, i1 [[CMP_UGT_3]])
|
||||
; CHECK-NEXT: tail call void @use3(i1 [[CMP_UGT_4]], i1 [[CMP_UGT_5]], i1 false)
|
||||
; CHECK-NEXT: [[CMP_UGE_2:%.*]] = icmp uge i32 [[RET]], 9
|
||||
; CHECK-NEXT: [[CMP_UGE_3:%.*]] = icmp uge i32 [[RET]], 8
|
||||
; CHECK-NEXT: [[CMP_UGE_4:%.*]] = icmp uge i32 [[RET]], 1
|
||||
; CHECK-NEXT: tail call void @use3(i1 false, i1 [[CMP_UGE_2]], i1 [[CMP_UGE_3]])
|
||||
; CHECK-NEXT: tail call void @use3(i1 [[CMP_UGE_4]], i1 true, i1 false)
|
||||
; CHECK-NEXT: [[CMP_SGT_3:%.*]] = icmp sgt i32 [[RET]], 8
|
||||
; CHECK-NEXT: [[CMP_SGT_4:%.*]] = icmp sgt i32 [[RET]], 1
|
||||
; CHECK-NEXT: [[CMP_SGT_5:%.*]] = icmp sgt i32 [[RET]], 0
|
||||
; CHECK-NEXT: tail call void @use3(i1 false, i1 false, i1 [[CMP_SGT_3]])
|
||||
; CHECK-NEXT: tail call void @use3(i1 [[CMP_SGT_4]], i1 [[CMP_SGT_5]], i1 true)
|
||||
; CHECK-NEXT: [[CMP_GTE_2:%.*]] = icmp sge i32 [[RET]], 9
|
||||
; CHECK-NEXT: [[CMP_GTE_3:%.*]] = icmp sge i32 [[RET]], 8
|
||||
; CHECK-NEXT: [[CMP_GTE_4:%.*]] = icmp sge i32 [[RET]], 1
|
||||
; CHECK-NEXT: tail call void @use3(i1 false, i1 [[CMP_GTE_2]], i1 [[CMP_GTE_3]])
|
||||
; CHECK-NEXT: tail call void @use3(i1 [[CMP_GTE_4]], i1 true, i1 true)
|
||||
; CHECK-NEXT: [[CMP_SLT_2:%.*]] = icmp slt i32 [[RET]], 9
|
||||
; CHECK-NEXT: [[CMP_SLT_3:%.*]] = icmp slt i32 [[RET]], 8
|
||||
; CHECK-NEXT: [[CMP_SLT_4:%.*]] = icmp slt i32 [[RET]], 1
|
||||
; CHECK-NEXT: tail call void @use3(i1 true, i1 [[CMP_SLT_2]], i1 [[CMP_SLT_3]])
|
||||
; CHECK-NEXT: tail call void @use3(i1 [[CMP_SLT_4]], i1 false, i1 false)
|
||||
; CHECK-NEXT: [[CMP_LTE_3:%.*]] = icmp sle i32 [[RET]], 8
|
||||
; CHECK-NEXT: [[CMP_LTE_4:%.*]] = icmp sle i32 [[RET]], 1
|
||||
; CHECK-NEXT: [[CMP_LTE_5:%.*]] = icmp sle i32 [[RET]], 0
|
||||
; CHECK-NEXT: tail call void @use3(i1 true, i1 true, i1 [[CMP_LTE_3]])
|
||||
; CHECK-NEXT: tail call void @use3(i1 [[CMP_LTE_4]], i1 [[CMP_LTE_5]], i1 false)
|
||||
; CHECK-NEXT: ret void
|
||||
;
|
||||
; ret = [0, 10)
|
||||
%ret = tail call i32 @test0(i32 *%p)
|
||||
|
||||
; ret = [0, 10), eq
|
||||
%cmp-eq-1 = icmp eq i32 %ret, 10
|
||||
%cmp-eq-2 = icmp eq i32 %ret, 9
|
||||
%cmp-eq-3 = icmp eq i32 %ret, 8
|
||||
%cmp-eq-4 = icmp eq i32 %ret, 1
|
||||
%cmp-eq-5 = icmp eq i32 %ret, 0
|
||||
%cmp-eq-6 = icmp eq i32 %ret, -1
|
||||
tail call void @use3(i1 %cmp-eq-1, i1 %cmp-eq-2, i1 %cmp-eq-3)
|
||||
tail call void @use3(i1 %cmp-eq-4, i1 %cmp-eq-5, i1 %cmp-eq-6)
|
||||
|
||||
; ret = [0, 10), ne
|
||||
%cmp-ne-1 = icmp ne i32 %ret, 10
|
||||
%cmp-ne-2 = icmp ne i32 %ret, 9
|
||||
%cmp-ne-3 = icmp ne i32 %ret, 8
|
||||
%cmp-ne-4 = icmp ne i32 %ret, 1
|
||||
%cmp-ne-5 = icmp ne i32 %ret, 0
|
||||
%cmp-ne-6 = icmp ne i32 %ret, -1
|
||||
tail call void @use3(i1 %cmp-ne-1, i1 %cmp-ne-2, i1 %cmp-ne-3)
|
||||
tail call void @use3(i1 %cmp-ne-4, i1 %cmp-ne-5, i1 %cmp-ne-6)
|
||||
|
||||
; ret = [0, 10), ugt
|
||||
%cmp-ugt-1 = icmp ugt i32 %ret, 10
|
||||
%cmp-ugt-2 = icmp ugt i32 %ret, 9
|
||||
%cmp-ugt-3 = icmp ugt i32 %ret, 8
|
||||
%cmp-ugt-4 = icmp ugt i32 %ret, 1
|
||||
%cmp-ugt-5 = icmp ugt i32 %ret, 0
|
||||
%cmp-ugt-6 = icmp ugt i32 %ret, -1
|
||||
tail call void @use3(i1 %cmp-ugt-1, i1 %cmp-ugt-2, i1 %cmp-ugt-3)
|
||||
tail call void @use3(i1 %cmp-ugt-4, i1 %cmp-ugt-5, i1 %cmp-ugt-6)
|
||||
|
||||
; ret = [0, 10), uge
|
||||
%cmp-uge-1 = icmp uge i32 %ret, 10
|
||||
%cmp-uge-2 = icmp uge i32 %ret, 9
|
||||
%cmp-uge-3 = icmp uge i32 %ret, 8
|
||||
%cmp-uge-4 = icmp uge i32 %ret, 1
|
||||
%cmp-uge-5 = icmp uge i32 %ret, 0
|
||||
%cmp-uge-6 = icmp uge i32 %ret, -1
|
||||
tail call void @use3(i1 %cmp-uge-1, i1 %cmp-uge-2, i1 %cmp-uge-3)
|
||||
tail call void @use3(i1 %cmp-uge-4, i1 %cmp-uge-5, i1 %cmp-uge-6)
|
||||
|
||||
; ret = [0, 10), sgt
|
||||
%cmp-sgt-1 = icmp sgt i32 %ret, 10
|
||||
%cmp-sgt-2 = icmp sgt i32 %ret, 9
|
||||
%cmp-sgt-3 = icmp sgt i32 %ret, 8
|
||||
%cmp-sgt-4 = icmp sgt i32 %ret, 1
|
||||
%cmp-sgt-5 = icmp sgt i32 %ret, 0
|
||||
%cmp-sgt-6 = icmp sgt i32 %ret, -1
|
||||
tail call void @use3(i1 %cmp-sgt-1, i1 %cmp-sgt-2, i1 %cmp-sgt-3)
|
||||
tail call void @use3(i1 %cmp-sgt-4, i1 %cmp-sgt-5, i1 %cmp-sgt-6)
|
||||
|
||||
; ret = [0, 10), sge
|
||||
%cmp-gte-1 = icmp sge i32 %ret, 10
|
||||
%cmp-gte-2 = icmp sge i32 %ret, 9
|
||||
%cmp-gte-3 = icmp sge i32 %ret, 8
|
||||
%cmp-gte-4 = icmp sge i32 %ret, 1
|
||||
%cmp-gte-5 = icmp sge i32 %ret, 0
|
||||
%cmp-gte-6 = icmp sge i32 %ret, -1
|
||||
tail call void @use3(i1 %cmp-gte-1, i1 %cmp-gte-2, i1 %cmp-gte-3)
|
||||
tail call void @use3(i1 %cmp-gte-4, i1 %cmp-gte-5, i1 %cmp-gte-6)
|
||||
|
||||
; ret = [0, 10), slt
|
||||
%cmp-slt-1 = icmp slt i32 %ret, 10
|
||||
%cmp-slt-2 = icmp slt i32 %ret, 9
|
||||
%cmp-slt-3 = icmp slt i32 %ret, 8
|
||||
%cmp-slt-4 = icmp slt i32 %ret, 1
|
||||
%cmp-slt-5 = icmp slt i32 %ret, 0
|
||||
%cmp-slt-6 = icmp slt i32 %ret, -1
|
||||
tail call void @use3(i1 %cmp-slt-1, i1 %cmp-slt-2, i1 %cmp-slt-3)
|
||||
tail call void @use3(i1 %cmp-slt-4, i1 %cmp-slt-5, i1 %cmp-slt-6)
|
||||
|
||||
; ret = [0, 10), sle
|
||||
%cmp-lte-1 = icmp sle i32 %ret, 10
|
||||
%cmp-lte-2 = icmp sle i32 %ret, 9
|
||||
%cmp-lte-3 = icmp sle i32 %ret, 8
|
||||
%cmp-lte-4 = icmp sle i32 %ret, 1
|
||||
%cmp-lte-5 = icmp sle i32 %ret, 0
|
||||
%cmp-lte-6 = icmp sle i32 %ret, -1
|
||||
tail call void @use3(i1 %cmp-lte-1, i1 %cmp-lte-2, i1 %cmp-lte-3)
|
||||
tail call void @use3(i1 %cmp-lte-4, i1 %cmp-lte-5, i1 %cmp-lte-6)
|
||||
|
||||
ret void
|
||||
}
|
||||
define i32 @test1(i32* %p) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@test1
|
||||
; CHECK-SAME: (i32* nocapture nofree nonnull readonly dereferenceable(4) [[P:%.*]])
|
||||
; CHECK-NEXT: [[LOAD_10_100:%.*]] = load i32, i32* [[P]], !range !1
|
||||
; CHECK-NEXT: [[ADD_10_THEN_20_110:%.*]] = add i32 [[LOAD_10_100]], 10
|
||||
; CHECK-NEXT: [[MUL_10_THEN_200_1091:%.*]] = mul i32 [[ADD_10_THEN_20_110]], 10
|
||||
; CHECK-NEXT: ret i32 [[MUL_10_THEN_200_1091]]
|
||||
;
|
||||
%load-10-100 = load i32, i32* %p, !range !1
|
||||
%add-10-then-20-110 = add i32 %load-10-100, 10
|
||||
%mul-10-then-200-1091 = mul i32 %add-10-then-20-110, 10
|
||||
ret i32 %mul-10-then-200-1091
|
||||
}
|
||||
|
||||
define i1 @test1-check(i32* %p) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@test1-check
|
||||
; CHECK-SAME: (i32* nocapture nofree readonly [[P:%.*]])
|
||||
; CHECK-NEXT: [[RES:%.*]] = tail call i32 @test1(i32* nocapture nofree readonly [[P]])
|
||||
; CHECK-SAME: !range !2
|
||||
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[RES]], 500
|
||||
; CHECK-NEXT: ret i1 [[CMP]]
|
||||
;
|
||||
%res = tail call i32 @test1(i32* %p)
|
||||
%cmp = icmp eq i32 %res, 500
|
||||
ret i1 %cmp
|
||||
}
|
||||
|
||||
; TEST2
|
||||
; int test2(int *p) { return *p == 0 ? 4 : 3; }
|
||||
; int test2_check(int *p) {
|
||||
; int call = test2(p);
|
||||
; if (call == 5) {
|
||||
; // dead block
|
||||
; return 2;
|
||||
; } else {
|
||||
; return 3;
|
||||
; }
|
||||
; }
|
||||
|
||||
define i32 @test2(i32* %p) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@test2
|
||||
; CHECK-SAME: (i32* nocapture nofree nonnull readonly align 4 dereferenceable(4) [[P:%.*]])
|
||||
; CHECK-NEXT: entry:
|
||||
; CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* [[P]], align 4
|
||||
; CHECK-NEXT: [[TOBOOL:%.*]] = icmp eq i32 [[TMP0]], 0
|
||||
; CHECK-NEXT: [[COND:%.*]] = select i1 [[TOBOOL]], i32 4, i32 3
|
||||
; CHECK-NEXT: ret i32 [[COND]]
|
||||
;
|
||||
entry:
|
||||
%0 = load i32, i32* %p, align 4
|
||||
%tobool = icmp eq i32 %0, 0
|
||||
%cond = select i1 %tobool, i32 4, i32 3
|
||||
ret i32 %cond
|
||||
}
|
||||
|
||||
define i32 @test2_check(i32* %p) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@test2_check
|
||||
; CHECK-SAME: (i32* nocapture nofree readonly align 4 [[P:%.*]])
|
||||
; CHECK-NEXT: entry:
|
||||
; CHECK-NEXT: br label [[IF_THEN:%.*]]
|
||||
; CHECK: if.then:
|
||||
; CHECK-NEXT: br label [[RETURN:%.*]]
|
||||
; CHECK: if.end:
|
||||
; CHECK-NEXT: unreachable
|
||||
; CHECK: return:
|
||||
; CHECK-NEXT: ret i32 2
|
||||
;
|
||||
entry:
|
||||
%call = tail call i32 @test2(i32* %p)
|
||||
%cmp = icmp slt i32 %call, 5
|
||||
br i1 %cmp, label %if.then, label %if.end
|
||||
|
||||
if.then: ; preds = %entry
|
||||
br label %return
|
||||
|
||||
if.end: ; preds = %entry
|
||||
br label %return
|
||||
|
||||
return: ; preds = %if.end, %if.then
|
||||
%retval.0 = phi i32 [ 2, %if.then ], [ 3, %if.end ]
|
||||
ret i32 %retval.0
|
||||
}
|
||||
|
||||
; TEST 3 SECV test
|
||||
|
||||
; void unkown();
|
||||
; int r1(unsigned int u){
|
||||
; int sum = 0;
|
||||
; for(int i = 0; i<100;i++){
|
||||
; sum += i;
|
||||
; }
|
||||
; // sum = 50 * 49 / 2
|
||||
; if(sum > 10000){
|
||||
; // dead block
|
||||
; return 20;
|
||||
; }else {
|
||||
; return 10;
|
||||
; }
|
||||
; }
|
||||
; void f1(int u){
|
||||
; if(r1(u) > 15){
|
||||
; // deadblock
|
||||
; unkown();
|
||||
; }else {
|
||||
; return;
|
||||
; }
|
||||
; }
|
||||
|
||||
declare dso_local void @unkown()
|
||||
|
||||
define internal i32 @r1(i32) local_unnamed_addr {
|
||||
; CHECK-LABEL: define {{[^@]+}}@r1() local_unnamed_addr
|
||||
; CHECK-NEXT: br label [[TMP3:%.*]]
|
||||
; CHECK: 1:
|
||||
; CHECK-NEXT: br label [[F:%.*]]
|
||||
; CHECK: 2:
|
||||
; CHECK-NEXT: unreachable
|
||||
; CHECK: f:
|
||||
; CHECK-NEXT: ret i32 10
|
||||
; CHECK: 3:
|
||||
; CHECK-NEXT: [[TMP4:%.*]] = phi i32 [ 0, [[TMP0:%.*]] ], [ [[TMP7:%.*]], [[TMP3]] ]
|
||||
; CHECK-NEXT: [[TMP5:%.*]] = phi i32 [ 0, [[TMP0]] ], [ [[TMP6:%.*]], [[TMP3]] ]
|
||||
; CHECK-NEXT: [[TMP6]] = add nuw nsw i32 [[TMP4]], [[TMP5]]
|
||||
; CHECK-NEXT: [[TMP7]] = add nuw nsw i32 [[TMP4]], 1
|
||||
; CHECK-NEXT: [[TMP8:%.*]] = icmp eq i32 [[TMP7]], 100
|
||||
; CHECK-NEXT: br i1 [[TMP8]], label [[TMP1:%.*]], label [[TMP3]]
|
||||
;
|
||||
br label %5
|
||||
|
||||
2: ; preds = %5
|
||||
%3 = icmp sgt i32 %8, 10000
|
||||
br i1 %3, label %4, label %f
|
||||
4:
|
||||
ret i32 20
|
||||
f:
|
||||
ret i32 10
|
||||
5: ; preds = %5, %1
|
||||
%6 = phi i32 [ 0, %1 ], [ %9, %5 ]
|
||||
%7 = phi i32 [ 0, %1 ], [ %8, %5 ]
|
||||
%8 = add nuw nsw i32 %6, %7
|
||||
%9 = add nuw nsw i32 %6, 1
|
||||
%10 = icmp eq i32 %9, 100
|
||||
br i1 %10, label %2, label %5
|
||||
}
|
||||
|
||||
define void @f1(i32){
|
||||
; CHECK-LABEL: define {{[^@]+}}@f1
|
||||
; CHECK-SAME: (i32 [[TMP0:%.*]])
|
||||
; CHECK-NEXT: [[TMP2:%.*]] = tail call i32 @r1()
|
||||
; CHECK-NEXT: br label [[TMP4:%.*]]
|
||||
; CHECK: 3:
|
||||
; CHECK-NEXT: unreachable
|
||||
; CHECK: 4:
|
||||
; CHECK-NEXT: ret void
|
||||
;
|
||||
%2 = tail call i32 @r1(i32 %0)
|
||||
%3 = icmp sgt i32 %2, 15
|
||||
br i1 %3, label %4, label %5
|
||||
|
||||
4: ; preds = %1
|
||||
tail call void @unkown()
|
||||
br label %5
|
||||
|
||||
5: ; preds = %1, %4
|
||||
ret void
|
||||
}
|
||||
|
||||
; TEST4 LVI test
|
||||
|
||||
; f1
|
||||
; int test4-f1(int u){
|
||||
; if(u>=0) {
|
||||
; return u;
|
||||
; }else{
|
||||
; return 0;
|
||||
; }
|
||||
; }
|
||||
define dso_local i32 @test4-f1(i32 %u) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@test4-f1
|
||||
; CHECK-SAME: (i32 [[U:%.*]])
|
||||
; CHECK-NEXT: entry:
|
||||
; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[U]], -1
|
||||
; CHECK-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[RETURN:%.*]]
|
||||
; CHECK: if.then:
|
||||
; CHECK-NEXT: br label [[RETURN]]
|
||||
; CHECK: return:
|
||||
; CHECK-NEXT: [[RETVAL_0:%.*]] = phi i32 [ [[U]], [[IF_THEN]] ], [ 0, [[ENTRY:%.*]] ]
|
||||
; CHECK-NEXT: ret i32 [[RETVAL_0]]
|
||||
;
|
||||
; FIXME: RETVAL_0 >= 0
|
||||
entry:
|
||||
%cmp = icmp sgt i32 %u, -1
|
||||
br i1 %cmp, label %if.then, label %return
|
||||
|
||||
if.then: ; preds = %entry
|
||||
br label %return
|
||||
|
||||
return: ; preds = %entry, %if.then
|
||||
%retval.0 = phi i32 [ %u, %if.then ], [ 0, %entry ]
|
||||
ret i32 %retval.0
|
||||
}
|
||||
|
||||
|
||||
define dso_local i32 @test4-g1(i32 %u) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@test4-g1
|
||||
; CHECK-SAME: (i32 [[U:%.*]])
|
||||
; CHECK-NEXT: entry:
|
||||
; CHECK-NEXT: [[CALL:%.*]] = tail call i32 @test4-f1(i32 [[U]])
|
||||
; CHECK-NEXT: ret i32 [[CALL]]
|
||||
;
|
||||
; FIXME: %call should have range [0, inf]
|
||||
|
||||
entry:
|
||||
%call = tail call i32 @test4-f1(i32 %u)
|
||||
ret i32 %call
|
||||
}
|
||||
|
||||
; f2
|
||||
; int test4-f1(int u){
|
||||
; if(u>-1) {
|
||||
; return u+1;
|
||||
; }else{
|
||||
; return 1;
|
||||
; }
|
||||
; }
|
||||
define dso_local i32 @test4-f2(i32 %u) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@test4-f2
|
||||
; CHECK-SAME: (i32 [[U:%.*]])
|
||||
; CHECK-NEXT: entry:
|
||||
; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[U]], -1
|
||||
; CHECK-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_ELSE:%.*]]
|
||||
; CHECK: if.then:
|
||||
; CHECK-NEXT: [[ADD:%.*]] = add nuw nsw i32 [[U]], 1
|
||||
; CHECK-NEXT: br label [[RETURN:%.*]]
|
||||
; CHECK: if.else:
|
||||
; CHECK-NEXT: br label [[RETURN]]
|
||||
; CHECK: return:
|
||||
; CHECK-NEXT: [[RETVAL_0:%.*]] = phi i32 [ [[ADD]], [[IF_THEN]] ], [ 1, [[IF_ELSE]] ]
|
||||
; CHECK-NEXT: ret i32 [[RETVAL_0]]
|
||||
;
|
||||
entry:
|
||||
%cmp = icmp sgt i32 %u, -1
|
||||
br i1 %cmp, label %if.then, label %if.else
|
||||
|
||||
if.then: ; preds = %entry
|
||||
%add = add nuw nsw i32 %u, 1
|
||||
br label %return
|
||||
|
||||
if.else: ; preds = %entry
|
||||
br label %return
|
||||
|
||||
return: ; preds = %if.else, %if.then
|
||||
%retval.0 = phi i32 [ %add, %if.then ], [ 1, %if.else ]
|
||||
ret i32 %retval.0
|
||||
}
|
||||
|
||||
|
||||
define dso_local i32 @test4-g2(i32 %u) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@test4-g2
|
||||
; CHECK-SAME: (i32 [[U:%.*]])
|
||||
; CHECK-NEXT: entry:
|
||||
; CHECK-NEXT: [[CALL:%.*]] = tail call i32 @test4-f2(i32 [[U]])
|
||||
; CHECK-NEXT: ret i32 [[CALL]]
|
||||
;
|
||||
; FIXME: %call should have range [1, inf]
|
||||
entry:
|
||||
%call = tail call i32 @test4-f2(i32 %u)
|
||||
ret i32 %call
|
||||
}
|
||||
|
||||
|
||||
!0 = !{i32 0, i32 10}
|
||||
!1 = !{i32 10, i32 100}
|
||||
;CHECK: !0 = !{i32 0, i32 10}
|
||||
;CHECK-NEXT: !1 = !{i32 10, i32 100}
|
||||
;CHECK-NEXT: !2 = !{i32 200, i32 1091}
|
|
@ -36,9 +36,6 @@ define i32 @test2_1(i1 %c) {
|
|||
br i1 %c, label %if.true, label %if.false
|
||||
if.true:
|
||||
%call = tail call i32 @return0()
|
||||
|
||||
; FIXME: %ret0 should be replaced with i32 1.
|
||||
; CHECK: %ret0 = add i32 0, 1
|
||||
%ret0 = add i32 %call, 1
|
||||
br label %end
|
||||
if.false:
|
||||
|
@ -46,23 +43,19 @@ if.false:
|
|||
br label %end
|
||||
end:
|
||||
|
||||
; FIXME: %ret should be replaced with i32 1.
|
||||
; CHECK: %ret = phi i32 [ %ret0, %if.true ], [ 1, %if.false ]
|
||||
%ret = phi i32 [ %ret0, %if.true ], [ %ret1, %if.false ]
|
||||
|
||||
; FIXME: ret i32 1
|
||||
; CHECK: ret i32 %ret
|
||||
ret i32 %ret
|
||||
; CHECK: ret i32 1
|
||||
ret i32 1
|
||||
}
|
||||
|
||||
|
||||
|
||||
; CHECK: define i32 @test2_2(i1 %c)
|
||||
define i32 @test2_2(i1 %c) {
|
||||
; FIXME: %ret should be replaced with i32 1.
|
||||
%ret = tail call i32 @test2_1(i1 %c)
|
||||
; FIXME: ret i32 1
|
||||
; CHECK: ret i32 %ret
|
||||
; CHECK: ret i32 1
|
||||
ret i32 %ret
|
||||
}
|
||||
|
||||
|
@ -127,7 +120,7 @@ end:
|
|||
|
||||
define i32 @ipccp1(i32 %a) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@ipccp1
|
||||
; CHECK-SAME: (i32 returned [[A:%.*]]) #0
|
||||
; CHECK-SAME: (i32 returned [[A:%.*]])
|
||||
; CHECK-NEXT: br i1 true, label [[T:%.*]], label [[F:%.*]]
|
||||
; CHECK: t:
|
||||
; CHECK-NEXT: ret i32 [[A:%.*]]
|
||||
|
@ -144,7 +137,7 @@ f:
|
|||
|
||||
define internal i1 @ipccp2i(i1 %a) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@ipccp2i
|
||||
; CHECK-SAME: (i1 returned [[A:%.*]]) #0
|
||||
; CHECK-SAME: (i1 returned [[A:%.*]])
|
||||
; CHECK-NEXT: br label %t
|
||||
; CHECK: t:
|
||||
; CHECK-NEXT: ret i1 true
|
||||
|
@ -160,8 +153,8 @@ f:
|
|||
}
|
||||
|
||||
define i1 @ipccp2() {
|
||||
; CHECK-LABEL: define {{[^@]+}}@ipccp2() #1
|
||||
; CHECK-NEXT: [[R:%.*]] = call i1 @ipccp2i(i1 true) #0
|
||||
; CHECK-LABEL: define {{[^@]+}}@ipccp2()
|
||||
; CHECK-NEXT: [[R:%.*]] = call i1 @ipccp2i(i1 true)
|
||||
; CHECK-NEXT: ret i1 [[R]]
|
||||
;
|
||||
%r = call i1 @ipccp2i(i1 true)
|
||||
|
@ -170,14 +163,12 @@ define i1 @ipccp2() {
|
|||
|
||||
define internal i32 @ipccp3i(i32 %a) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@ipccp3i
|
||||
; CHECK-SAME: (i32 [[A:%.*]]) #1
|
||||
; CHECK-NEXT: [[C:%.*]] = icmp eq i32 [[A:%.*]], 7
|
||||
; CHECK-NEXT: br i1 [[C]], label [[T:%.*]], label [[F:%.*]]
|
||||
; CHECK-SAME: (i32 returned [[A:%.*]])
|
||||
; CHECK-NEXT: br label [[T:%.*]]
|
||||
; CHECK: t:
|
||||
; CHECK-NEXT: ret i32 [[A]]
|
||||
; CHECK-NEXT: ret i32 7
|
||||
; CHECK: f:
|
||||
; CHECK-NEXT: [[R:%.*]] = call i32 @ipccp3i(i32 5) #1
|
||||
; CHECK-NEXT: ret i32 [[R]]
|
||||
; CHECK-NEXT: unreachable
|
||||
;
|
||||
%c = icmp eq i32 %a, 7
|
||||
br i1 %c, label %t, label %f
|
||||
|
@ -189,10 +180,10 @@ f:
|
|||
}
|
||||
|
||||
define i32 @ipccp3() {
|
||||
; CHECK-LABEL: define {{[^@]+}}@ipccp3() #1
|
||||
; CHECK-NEXT: [[R:%.*]] = call i32 @ipccp3i(i32 7) #1
|
||||
; CHECK-LABEL: define {{[^@]+}}@ipccp3()
|
||||
; CHECK-NEXT: [[R:%.*]] = call i32 @ipccp3i(i32 7)
|
||||
; CHECK-NEXT: ret i32 [[R]]
|
||||
;
|
||||
; FIXME: R should be replaced with 7
|
||||
%r = call i32 @ipccp3i(i32 7)
|
||||
ret i32 %r
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue