mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-25 04:02:41 +01:00
[Attributor] Attributor call site specific AAValueConstantRange
This patch makes uses of the context bridges introduced in D83299 to make AAValueConstantRange call site specific. Reviewed By: jdoerfert Differential Revision: https://reviews.llvm.org/D83744
This commit is contained in:
parent
663581df12
commit
682dd42aa3
@ -229,6 +229,9 @@ struct AADepGraph {
|
||||
/// are floating values that do not have a corresponding attribute list
|
||||
/// position.
|
||||
struct IRPosition {
|
||||
// NOTE: In the future this definition can be changed to support recursive
|
||||
// functions.
|
||||
using CallBaseContext = CallBase;
|
||||
|
||||
/// The positions we distinguish in the IR.
|
||||
enum Kind : char {
|
||||
@ -249,27 +252,34 @@ struct IRPosition {
|
||||
IRPosition() : Enc(nullptr, ENC_VALUE) { verify(); }
|
||||
|
||||
/// Create a position describing the value of \p V.
|
||||
static const IRPosition value(const Value &V) {
|
||||
static const IRPosition value(const Value &V,
|
||||
const CallBaseContext *CBContext = nullptr) {
|
||||
if (auto *Arg = dyn_cast<Argument>(&V))
|
||||
return IRPosition::argument(*Arg);
|
||||
return IRPosition::argument(*Arg, CBContext);
|
||||
if (auto *CB = dyn_cast<CallBase>(&V))
|
||||
return IRPosition::callsite_returned(*CB);
|
||||
return IRPosition(const_cast<Value &>(V), IRP_FLOAT);
|
||||
return IRPosition(const_cast<Value &>(V), IRP_FLOAT, CBContext);
|
||||
}
|
||||
|
||||
/// Create a position describing the function scope of \p F.
|
||||
static const IRPosition function(const Function &F) {
|
||||
return IRPosition(const_cast<Function &>(F), IRP_FUNCTION);
|
||||
/// \p CBContext is used for call base specific analysis.
|
||||
static const IRPosition function(const Function &F,
|
||||
const CallBaseContext *CBContext = nullptr) {
|
||||
return IRPosition(const_cast<Function &>(F), IRP_FUNCTION, CBContext);
|
||||
}
|
||||
|
||||
/// Create a position describing the returned value of \p F.
|
||||
static const IRPosition returned(const Function &F) {
|
||||
return IRPosition(const_cast<Function &>(F), IRP_RETURNED);
|
||||
/// \p CBContext is used for call base specific analysis.
|
||||
static const IRPosition returned(const Function &F,
|
||||
const CallBaseContext *CBContext = nullptr) {
|
||||
return IRPosition(const_cast<Function &>(F), IRP_RETURNED, CBContext);
|
||||
}
|
||||
|
||||
/// Create a position describing the argument \p Arg.
|
||||
static const IRPosition argument(const Argument &Arg) {
|
||||
return IRPosition(const_cast<Argument &>(Arg), IRP_ARGUMENT);
|
||||
/// \p CBContext is used for call base specific analysis.
|
||||
static const IRPosition argument(const Argument &Arg,
|
||||
const CallBaseContext *CBContext = nullptr) {
|
||||
return IRPosition(const_cast<Argument &>(Arg), IRP_ARGUMENT, CBContext);
|
||||
}
|
||||
|
||||
/// Create a position describing the function scope of \p CB.
|
||||
@ -305,16 +315,20 @@ struct IRPosition {
|
||||
/// If \p IRP is a call site (see isAnyCallSitePosition()) then the result
|
||||
/// will be a call site position, otherwise the function position of the
|
||||
/// associated function.
|
||||
static const IRPosition function_scope(const IRPosition &IRP) {
|
||||
static const IRPosition
|
||||
function_scope(const IRPosition &IRP,
|
||||
const CallBaseContext *CBContext = nullptr) {
|
||||
if (IRP.isAnyCallSitePosition()) {
|
||||
return IRPosition::callsite_function(
|
||||
cast<CallBase>(IRP.getAnchorValue()));
|
||||
}
|
||||
assert(IRP.getAssociatedFunction());
|
||||
return IRPosition::function(*IRP.getAssociatedFunction());
|
||||
return IRPosition::function(*IRP.getAssociatedFunction(), CBContext);
|
||||
}
|
||||
|
||||
bool operator==(const IRPosition &RHS) const { return Enc == RHS.Enc; }
|
||||
bool operator==(const IRPosition &RHS) const {
|
||||
return Enc == RHS.Enc && RHS.CBContext == CBContext;
|
||||
}
|
||||
bool operator!=(const IRPosition &RHS) const { return !(*this == RHS); }
|
||||
|
||||
/// Return the value this abstract attribute is anchored with.
|
||||
@ -535,6 +549,19 @@ struct IRPosition {
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the same position without the call base context.
|
||||
IRPosition stripCallBaseContext() const {
|
||||
IRPosition Result = *this;
|
||||
Result.CBContext = nullptr;
|
||||
return Result;
|
||||
}
|
||||
|
||||
/// Get the call base context from the position.
|
||||
const CallBaseContext *getCallBaseContext() const { return CBContext; }
|
||||
|
||||
/// Check if the position has any call base context.
|
||||
bool hasCallBaseContext() const { return CBContext != nullptr; }
|
||||
|
||||
/// Special DenseMap key values.
|
||||
///
|
||||
///{
|
||||
@ -547,10 +574,15 @@ struct IRPosition {
|
||||
|
||||
private:
|
||||
/// Private constructor for special values only!
|
||||
explicit IRPosition(void *Ptr) { Enc.setFromOpaqueValue(Ptr); }
|
||||
explicit IRPosition(void *Ptr, const CallBaseContext *CBContext = nullptr)
|
||||
: CBContext(CBContext) {
|
||||
Enc.setFromOpaqueValue(Ptr);
|
||||
}
|
||||
|
||||
/// IRPosition anchored at \p AnchorVal with kind/argument numbet \p PK.
|
||||
explicit IRPosition(Value &AnchorVal, Kind PK) {
|
||||
explicit IRPosition(Value &AnchorVal, Kind PK,
|
||||
const CallBaseContext *CBContext = nullptr)
|
||||
: CBContext(CBContext) {
|
||||
switch (PK) {
|
||||
case IRPosition::IRP_INVALID:
|
||||
llvm_unreachable("Cannot create invalid IRP with an anchor value!");
|
||||
@ -672,16 +704,27 @@ private:
|
||||
PointerIntPair<void *, NumEncodingBits, char> Enc;
|
||||
///}
|
||||
|
||||
/// Call base context. Used for callsite specific analysis.
|
||||
const CallBaseContext *CBContext = nullptr;
|
||||
|
||||
/// Return the encoding bits.
|
||||
char getEncodingBits() const { return Enc.getInt(); }
|
||||
};
|
||||
|
||||
/// Helper that allows IRPosition as a key in a DenseMap.
|
||||
template <> struct DenseMapInfo<IRPosition> : DenseMapInfo<void *> {
|
||||
template <> struct DenseMapInfo<IRPosition> {
|
||||
static inline IRPosition getEmptyKey() { return IRPosition::EmptyKey; }
|
||||
static inline IRPosition getTombstoneKey() {
|
||||
return IRPosition::TombstoneKey;
|
||||
}
|
||||
static unsigned getHashValue(const IRPosition &IRP) {
|
||||
return (DenseMapInfo<void *>::getHashValue(IRP) << 4) ^
|
||||
(DenseMapInfo<Value *>::getHashValue(IRP.getCallBaseContext()));
|
||||
}
|
||||
|
||||
static bool isEqual(const IRPosition &a, const IRPosition &b) {
|
||||
return a == b;
|
||||
}
|
||||
};
|
||||
|
||||
/// A visitor class for IR positions.
|
||||
@ -1080,8 +1123,22 @@ struct Attributor {
|
||||
/// NOTE: ForceUpdate is ignored in any stage other than the update stage.
|
||||
template <typename AAType>
|
||||
const AAType &
|
||||
getOrCreateAAFor(const IRPosition &IRP, const AbstractAttribute *QueryingAA,
|
||||
getOrCreateAAFor(IRPosition IRP, const AbstractAttribute *QueryingAA,
|
||||
DepClassTy DepClass, bool ForceUpdate = false) {
|
||||
#ifdef EXPENSIVE_CHECKS
|
||||
// Don't allow callbase information to leak.
|
||||
if (auto CBContext = IRP.getCallBaseContext()) {
|
||||
assert(
|
||||
((CBContext->getCalledFunction() == IRP.getAnchorScope() ||
|
||||
QueryingAA ||
|
||||
!QueryingAA.getIRPosition().isAnyCallSitePosition())) &&
|
||||
"non callsite positions are not allowed to propagate CallBaseContext "
|
||||
"across functions");
|
||||
}
|
||||
#endif
|
||||
if (!shouldPropagateCallBaseContext(IRP))
|
||||
IRP = IRP.stripCallBaseContext();
|
||||
|
||||
if (AAType *AAPtr = lookupAAFor<AAType>(IRP, QueryingAA, DepClass)) {
|
||||
if (ForceUpdate && Phase == AttributorPhase::UPDATE)
|
||||
updateAA(*AAPtr);
|
||||
@ -1600,6 +1657,9 @@ private:
|
||||
const AbstractAttribute *QueryingAA,
|
||||
bool &AllCallSitesKnown);
|
||||
|
||||
/// Determine if CallBase context in \p IRP should be propagated.
|
||||
bool shouldPropagateCallBaseContext(const IRPosition &IRP);
|
||||
|
||||
/// Apply all requested function signature rewrites
|
||||
/// (\see registerFunctionSignatureRewrite) and return Changed if the module
|
||||
/// was altered.
|
||||
|
@ -142,6 +142,11 @@ static cl::opt<bool> PrintDependencies("attributor-print-dep", cl::Hidden,
|
||||
cl::desc("Print attribute dependencies"),
|
||||
cl::init(false));
|
||||
|
||||
static cl::opt<bool> EnableCallSiteSpecific(
|
||||
"attributor-enable-call-site-specific-deduction", cl::Hidden,
|
||||
cl::desc("Allow the Attributor to do call site specific analysis"),
|
||||
cl::init(false));
|
||||
|
||||
/// Logic operators for the change status enum class.
|
||||
///
|
||||
///{
|
||||
@ -476,6 +481,8 @@ void IRPosition::verify() {
|
||||
#ifdef EXPENSIVE_CHECKS
|
||||
switch (getPositionKind()) {
|
||||
case IRP_INVALID:
|
||||
assert((CBContext == nullptr) &&
|
||||
"Invalid position must not have CallBaseContext!");
|
||||
assert(!Enc.getOpaqueValue() &&
|
||||
"Expected a nullptr for an invalid position!");
|
||||
return;
|
||||
@ -491,12 +498,16 @@ void IRPosition::verify() {
|
||||
"Associated value mismatch!");
|
||||
return;
|
||||
case IRP_CALL_SITE_RETURNED:
|
||||
assert((CBContext == nullptr) &&
|
||||
"'call site returned' position must not have CallBaseContext!");
|
||||
assert((isa<CallBase>(getAsValuePtr())) &&
|
||||
"Expected call base for 'call site returned' position!");
|
||||
assert(getAsValuePtr() == &getAssociatedValue() &&
|
||||
"Associated value mismatch!");
|
||||
return;
|
||||
case IRP_CALL_SITE:
|
||||
assert((CBContext == nullptr) &&
|
||||
"'call site function' position must not have CallBaseContext!");
|
||||
assert((isa<CallBase>(getAsValuePtr())) &&
|
||||
"Expected call base for 'call site function' position!");
|
||||
assert(getAsValuePtr() == &getAssociatedValue() &&
|
||||
@ -515,6 +526,8 @@ void IRPosition::verify() {
|
||||
"Associated value mismatch!");
|
||||
return;
|
||||
case IRP_CALL_SITE_ARGUMENT: {
|
||||
assert((CBContext == nullptr) &&
|
||||
"'call site argument' position must not have CallBaseContext!");
|
||||
Use *U = getAsUsePtr();
|
||||
assert(U && "Expected use for a 'call site argument' position!");
|
||||
assert(isa<CallBase>(U->getUser()) &&
|
||||
@ -849,6 +862,13 @@ bool Attributor::checkForAllCallSites(function_ref<bool(AbstractCallSite)> Pred,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Attributor::shouldPropagateCallBaseContext(const IRPosition &IRP) {
|
||||
// TODO: Maintain a cache of Values that are
|
||||
// on the pathway from a Argument to a Instruction that would effect the
|
||||
// liveness/return state etc.
|
||||
return EnableCallSiteSpecific;
|
||||
}
|
||||
|
||||
bool Attributor::checkForAllReturnedValuesAndReturnInsts(
|
||||
function_ref<bool(Value &, const SmallSetVector<ReturnInst *, 4> &)> Pred,
|
||||
const AbstractAttribute &QueryingAA) {
|
||||
@ -1125,6 +1145,9 @@ ChangeStatus Attributor::manifestAttributes() {
|
||||
if (!State.isAtFixpoint())
|
||||
State.indicateOptimisticFixpoint();
|
||||
|
||||
// We must not manifest Attributes that use Callbase info.
|
||||
if (AA->hasCallBaseContext())
|
||||
continue;
|
||||
// If the state is invalid, we do not try to manifest it.
|
||||
if (!State.isValidState())
|
||||
continue;
|
||||
@ -2221,9 +2244,12 @@ raw_ostream &llvm::operator<<(raw_ostream &OS, IRPosition::Kind AP) {
|
||||
|
||||
raw_ostream &llvm::operator<<(raw_ostream &OS, const IRPosition &Pos) {
|
||||
const Value &AV = Pos.getAssociatedValue();
|
||||
return OS << "{" << Pos.getPositionKind() << ":" << AV.getName() << " ["
|
||||
<< Pos.getAnchorValue().getName() << "@" << Pos.getCallSiteArgNo()
|
||||
<< "]}";
|
||||
OS << "{" << Pos.getPositionKind() << ":" << AV.getName() << " ["
|
||||
<< Pos.getAnchorValue().getName() << "@" << Pos.getCallSiteArgNo() << "]";
|
||||
|
||||
if (Pos.hasCallBaseContext())
|
||||
OS << "[cb_context:" << *Pos.getCallBaseContext() << "]";
|
||||
return OS << "}";
|
||||
}
|
||||
|
||||
raw_ostream &llvm::operator<<(raw_ostream &OS, const IntegerRangeState &S) {
|
||||
|
@ -431,8 +431,9 @@ ChangeStatus clampStateAndIndicateChange(StateType &S, const StateType &R) {
|
||||
/// Clamp the information known for all returned values of a function
|
||||
/// (identified by \p QueryingAA) into \p S.
|
||||
template <typename AAType, typename StateType = typename AAType::StateType>
|
||||
static void clampReturnedValueStates(Attributor &A, const AAType &QueryingAA,
|
||||
StateType &S) {
|
||||
static void clampReturnedValueStates(
|
||||
Attributor &A, const AAType &QueryingAA, StateType &S,
|
||||
const IRPosition::CallBaseContext *CBContext = nullptr) {
|
||||
LLVM_DEBUG(dbgs() << "[Attributor] Clamp return value states for "
|
||||
<< QueryingAA << " into " << S << "\n");
|
||||
|
||||
@ -449,7 +450,7 @@ static void clampReturnedValueStates(Attributor &A, const AAType &QueryingAA,
|
||||
|
||||
// Callback for each possibly returned value.
|
||||
auto CheckReturnValue = [&](Value &RV) -> bool {
|
||||
const IRPosition &RVPos = IRPosition::value(RV);
|
||||
const IRPosition &RVPos = IRPosition::value(RV, CBContext);
|
||||
const AAType &AA =
|
||||
A.getAAFor<AAType>(QueryingAA, RVPos, DepClassTy::REQUIRED);
|
||||
LLVM_DEBUG(dbgs() << "[Attributor] RV: " << RV << " AA: " << AA.getAsStr()
|
||||
@ -472,7 +473,8 @@ static void clampReturnedValueStates(Attributor &A, const AAType &QueryingAA,
|
||||
|
||||
/// Helper class for generic deduction: return value -> returned position.
|
||||
template <typename AAType, typename BaseType,
|
||||
typename StateType = typename BaseType::StateType>
|
||||
typename StateType = typename BaseType::StateType,
|
||||
bool PropagateCallBaseContext = false>
|
||||
struct AAReturnedFromReturnedValues : public BaseType {
|
||||
AAReturnedFromReturnedValues(const IRPosition &IRP, Attributor &A)
|
||||
: BaseType(IRP, A) {}
|
||||
@ -480,7 +482,9 @@ struct AAReturnedFromReturnedValues : public BaseType {
|
||||
/// See AbstractAttribute::updateImpl(...).
|
||||
ChangeStatus updateImpl(Attributor &A) override {
|
||||
StateType S(StateType::getBestState(this->getState()));
|
||||
clampReturnedValueStates<AAType, StateType>(A, *this, S);
|
||||
clampReturnedValueStates<AAType, StateType>(
|
||||
A, *this, S,
|
||||
PropagateCallBaseContext ? this->getCallBaseContext() : nullptr);
|
||||
// TODO: If we know we visited all returned values, thus no are assumed
|
||||
// dead, we can take the known information from the state T.
|
||||
return clampStateAndIndicateChange<StateType>(this->getState(), S);
|
||||
@ -535,17 +539,58 @@ static void clampCallSiteArgumentStates(Attributor &A, const AAType &QueryingAA,
|
||||
S ^= *T;
|
||||
}
|
||||
|
||||
/// Helper class for generic deduction: call site argument -> argument position.
|
||||
/// This function is the bridge between argument position and the call base
|
||||
/// context.
|
||||
template <typename AAType, typename BaseType,
|
||||
typename StateType = typename AAType::StateType>
|
||||
bool getArgumentStateFromCallBaseContext(Attributor &A,
|
||||
BaseType &QueryingAttribute,
|
||||
IRPosition &Pos, StateType &State) {
|
||||
assert((Pos.getPositionKind() == IRPosition::IRP_ARGUMENT) &&
|
||||
"Expected an 'argument' position !");
|
||||
const CallBase *CBContext = Pos.getCallBaseContext();
|
||||
if (!CBContext)
|
||||
return false;
|
||||
|
||||
int ArgNo = Pos.getCallSiteArgNo();
|
||||
assert(ArgNo >= 0 && "Invalid Arg No!");
|
||||
|
||||
const auto &AA = A.getAAFor<AAType>(
|
||||
QueryingAttribute, IRPosition::callsite_argument(*CBContext, ArgNo),
|
||||
DepClassTy::REQUIRED);
|
||||
const StateType &CBArgumentState =
|
||||
static_cast<const StateType &>(AA.getState());
|
||||
|
||||
LLVM_DEBUG(dbgs() << "[Attributor] Briding Call site context to argument"
|
||||
<< "Position:" << Pos << "CB Arg state:" << CBArgumentState
|
||||
<< "\n");
|
||||
|
||||
// NOTE: If we want to do call site grouping it should happen here.
|
||||
State ^= CBArgumentState;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Helper class for generic deduction: call site argument -> argument position.
|
||||
template <typename AAType, typename BaseType,
|
||||
typename StateType = typename AAType::StateType,
|
||||
bool BridgeCallBaseContext = false>
|
||||
struct AAArgumentFromCallSiteArguments : public BaseType {
|
||||
AAArgumentFromCallSiteArguments(const IRPosition &IRP, Attributor &A)
|
||||
: BaseType(IRP, A) {}
|
||||
|
||||
/// See AbstractAttribute::updateImpl(...).
|
||||
ChangeStatus updateImpl(Attributor &A) override {
|
||||
StateType S(StateType::getBestState(this->getState()));
|
||||
StateType S = StateType::getBestState(this->getState());
|
||||
|
||||
if (BridgeCallBaseContext) {
|
||||
bool Success =
|
||||
getArgumentStateFromCallBaseContext<AAType, BaseType, StateType>(
|
||||
A, *this, this->getIRPosition(), S);
|
||||
if (Success)
|
||||
return clampStateAndIndicateChange<StateType>(this->getState(), S);
|
||||
}
|
||||
clampCallSiteArgumentStates<AAType, StateType>(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<StateType>(this->getState(), S);
|
||||
@ -554,7 +599,8 @@ struct AAArgumentFromCallSiteArguments : public BaseType {
|
||||
|
||||
/// Helper class for generic replication: function returned -> cs returned.
|
||||
template <typename AAType, typename BaseType,
|
||||
typename StateType = typename BaseType::StateType>
|
||||
typename StateType = typename BaseType::StateType,
|
||||
bool IntroduceCallBaseContext = false>
|
||||
struct AACallSiteReturnedFromReturned : public BaseType {
|
||||
AACallSiteReturnedFromReturned(const IRPosition &IRP, Attributor &A)
|
||||
: BaseType(IRP, A) {}
|
||||
@ -572,7 +618,13 @@ struct AACallSiteReturnedFromReturned : public BaseType {
|
||||
if (!AssociatedFunction)
|
||||
return S.indicatePessimisticFixpoint();
|
||||
|
||||
IRPosition FnPos = IRPosition::returned(*AssociatedFunction);
|
||||
CallBase &CBContext = static_cast<CallBase &>(this->getAnchorValue());
|
||||
if (IntroduceCallBaseContext)
|
||||
LLVM_DEBUG(dbgs() << "[Attributor] Introducing call base context:"
|
||||
<< CBContext << "\n");
|
||||
|
||||
IRPosition FnPos = IRPosition::returned(
|
||||
*AssociatedFunction, IntroduceCallBaseContext ? &CBContext : nullptr);
|
||||
const AAType &AA = A.getAAFor<AAType>(*this, FnPos, DepClassTy::REQUIRED);
|
||||
return clampStateAndIndicateChange(S, AA.getState());
|
||||
}
|
||||
@ -7126,9 +7178,11 @@ struct AAValueConstantRangeImpl : AAValueConstantRange {
|
||||
|
||||
struct AAValueConstantRangeArgument final
|
||||
: AAArgumentFromCallSiteArguments<
|
||||
AAValueConstantRange, AAValueConstantRangeImpl, IntegerRangeState> {
|
||||
AAValueConstantRange, AAValueConstantRangeImpl, IntegerRangeState,
|
||||
true /* BridgeCallBaseContext */> {
|
||||
using Base = AAArgumentFromCallSiteArguments<
|
||||
AAValueConstantRange, AAValueConstantRangeImpl, IntegerRangeState>;
|
||||
AAValueConstantRange, AAValueConstantRangeImpl, IntegerRangeState,
|
||||
true /* BridgeCallBaseContext */>;
|
||||
AAValueConstantRangeArgument(const IRPosition &IRP, Attributor &A)
|
||||
: Base(IRP, A) {}
|
||||
|
||||
@ -7149,9 +7203,14 @@ struct AAValueConstantRangeArgument final
|
||||
|
||||
struct AAValueConstantRangeReturned
|
||||
: AAReturnedFromReturnedValues<AAValueConstantRange,
|
||||
AAValueConstantRangeImpl> {
|
||||
using Base = AAReturnedFromReturnedValues<AAValueConstantRange,
|
||||
AAValueConstantRangeImpl>;
|
||||
AAValueConstantRangeImpl,
|
||||
AAValueConstantRangeImpl::StateType,
|
||||
/* PropogateCallBaseContext */ true> {
|
||||
using Base =
|
||||
AAReturnedFromReturnedValues<AAValueConstantRange,
|
||||
AAValueConstantRangeImpl,
|
||||
AAValueConstantRangeImpl::StateType,
|
||||
/* PropogateCallBaseContext */ true>;
|
||||
AAValueConstantRangeReturned(const IRPosition &IRP, Attributor &A)
|
||||
: Base(IRP, A) {}
|
||||
|
||||
@ -7221,12 +7280,14 @@ struct AAValueConstantRangeFloating : AAValueConstantRangeImpl {
|
||||
return false;
|
||||
|
||||
auto &LHSAA = A.getAAFor<AAValueConstantRange>(
|
||||
*this, IRPosition::value(*LHS), DepClassTy::REQUIRED);
|
||||
*this, IRPosition::value(*LHS, getCallBaseContext()),
|
||||
DepClassTy::REQUIRED);
|
||||
QuerriedAAs.push_back(&LHSAA);
|
||||
auto LHSAARange = LHSAA.getAssumedConstantRange(A, CtxI);
|
||||
|
||||
auto &RHSAA = A.getAAFor<AAValueConstantRange>(
|
||||
*this, IRPosition::value(*RHS), DepClassTy::REQUIRED);
|
||||
*this, IRPosition::value(*RHS, getCallBaseContext()),
|
||||
DepClassTy::REQUIRED);
|
||||
QuerriedAAs.push_back(&RHSAA);
|
||||
auto RHSAARange = RHSAA.getAssumedConstantRange(A, CtxI);
|
||||
|
||||
@ -7249,8 +7310,9 @@ struct AAValueConstantRangeFloating : AAValueConstantRangeImpl {
|
||||
if (!OpV.getType()->isIntegerTy())
|
||||
return false;
|
||||
|
||||
auto &OpAA = A.getAAFor<AAValueConstantRange>(*this, IRPosition::value(OpV),
|
||||
DepClassTy::REQUIRED);
|
||||
auto &OpAA = A.getAAFor<AAValueConstantRange>(
|
||||
*this, IRPosition::value(OpV, getCallBaseContext()),
|
||||
DepClassTy::REQUIRED);
|
||||
QuerriedAAs.push_back(&OpAA);
|
||||
T.unionAssumed(
|
||||
OpAA.getAssumed().castOp(CastI->getOpcode(), getState().getBitWidth()));
|
||||
@ -7268,12 +7330,12 @@ struct AAValueConstantRangeFloating : AAValueConstantRangeImpl {
|
||||
return false;
|
||||
|
||||
auto &LHSAA = A.getAAFor<AAValueConstantRange>(
|
||||
*this, IRPosition::value(*LHS), DepClassTy::REQUIRED);
|
||||
*this, IRPosition::value(*LHS, getCallBaseContext()),
|
||||
DepClassTy::REQUIRED);
|
||||
QuerriedAAs.push_back(&LHSAA);
|
||||
auto &RHSAA = A.getAAFor<AAValueConstantRange>(
|
||||
*this, IRPosition::value(*RHS), DepClassTy::REQUIRED);
|
||||
QuerriedAAs.push_back(&RHSAA);
|
||||
|
||||
*this, IRPosition::value(*RHS, getCallBaseContext()),
|
||||
DepClassTy::REQUIRED);
|
||||
auto LHSAARange = LHSAA.getAssumedConstantRange(A, CtxI);
|
||||
auto RHSAARange = RHSAA.getAssumedConstantRange(A, CtxI);
|
||||
|
||||
@ -7402,10 +7464,16 @@ struct AAValueConstantRangeCallSite : AAValueConstantRangeFunction {
|
||||
|
||||
struct AAValueConstantRangeCallSiteReturned
|
||||
: AACallSiteReturnedFromReturned<AAValueConstantRange,
|
||||
AAValueConstantRangeImpl> {
|
||||
AAValueConstantRangeImpl,
|
||||
AAValueConstantRangeImpl::StateType,
|
||||
/* IntroduceCallBaseContext */ true> {
|
||||
AAValueConstantRangeCallSiteReturned(const IRPosition &IRP, Attributor &A)
|
||||
: AACallSiteReturnedFromReturned<AAValueConstantRange,
|
||||
AAValueConstantRangeImpl>(IRP, A) {}
|
||||
AAValueConstantRangeImpl,
|
||||
AAValueConstantRangeImpl::StateType,
|
||||
/* IntroduceCallBaseContext */ true>(IRP,
|
||||
A) {
|
||||
}
|
||||
|
||||
/// See AbstractAttribute::initialize(...).
|
||||
void initialize(Attributor &A) override {
|
||||
|
132
test/Transforms/Attributor/cb_range_disabled.ll
Normal file
132
test/Transforms/Attributor/cb_range_disabled.ll
Normal file
@ -0,0 +1,132 @@
|
||||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes
|
||||
; call site specific analysis is disabled
|
||||
|
||||
; RUN: opt -attributor -enable-new-pm=0 -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=1 -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_NPM,NOT_CGSCC_OPM,NOT_TUNIT_NPM,IS__TUNIT____,IS________OPM,IS__TUNIT_OPM
|
||||
|
||||
; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=1 -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_OPM,NOT_CGSCC_NPM,NOT_TUNIT_OPM,IS__TUNIT____,IS________NPM,IS__TUNIT_NPM
|
||||
|
||||
; RUN: opt -attributor-cgscc -enable-new-pm=0 -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_TUNIT_NPM,NOT_TUNIT_OPM,NOT_CGSCC_NPM,IS__CGSCC____,IS________OPM,IS__CGSCC_OPM
|
||||
|
||||
; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_TUNIT_NPM,NOT_TUNIT_OPM,NOT_CGSCC_OPM,IS__CGSCC____,IS________NPM,IS__CGSCC_NPM
|
||||
|
||||
define i32 @test_range(i32 %unknown) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@test_range
|
||||
; CHECK-SAME: (i32 [[UNKNOWN:%.*]]) [[ATTR0:#.*]] {
|
||||
; CHECK-NEXT: [[TMP1:%.*]] = icmp sgt i32 [[UNKNOWN]], 100
|
||||
; CHECK-NEXT: [[TMP2:%.*]] = select i1 [[TMP1]], i32 100, i32 0
|
||||
; CHECK-NEXT: ret i32 [[TMP2]]
|
||||
;
|
||||
%1 = icmp sgt i32 %unknown, 100
|
||||
%2 = select i1 %1, i32 100, i32 0
|
||||
ret i32 %2
|
||||
}
|
||||
|
||||
define i32 @test1(i32 %unknown, i32 %b) {
|
||||
; IS__TUNIT____-LABEL: define {{[^@]+}}@test1
|
||||
; IS__TUNIT____-SAME: (i32 [[UNKNOWN:%.*]], i32 [[B:%.*]]) [[ATTR0:#.*]] {
|
||||
; IS__TUNIT____-NEXT: [[TMP1:%.*]] = call i32 @test_range(i32 [[UNKNOWN]]) [[ATTR0]], [[RNG0:!range !.*]]
|
||||
; IS__TUNIT____-NEXT: [[TMP2:%.*]] = sub nsw i32 [[TMP1]], [[B]]
|
||||
; IS__TUNIT____-NEXT: ret i32 [[TMP2]]
|
||||
;
|
||||
; IS__CGSCC____-LABEL: define {{[^@]+}}@test1
|
||||
; IS__CGSCC____-SAME: (i32 [[UNKNOWN:%.*]], i32 [[B:%.*]]) [[ATTR0:#.*]] {
|
||||
; IS__CGSCC____-NEXT: [[TMP1:%.*]] = call i32 @test_range(i32 [[UNKNOWN]]) [[ATTR1:#.*]], [[RNG0:!range !.*]]
|
||||
; IS__CGSCC____-NEXT: [[TMP2:%.*]] = sub nsw i32 [[TMP1]], [[B]]
|
||||
; IS__CGSCC____-NEXT: ret i32 [[TMP2]]
|
||||
;
|
||||
%1 = call i32 @test_range(i32 %unknown)
|
||||
%2 = sub nsw i32 %1, %b
|
||||
ret i32 %2
|
||||
}
|
||||
|
||||
define i32 @test2(i32 %unknown, i32 %b) {
|
||||
; IS__TUNIT____-LABEL: define {{[^@]+}}@test2
|
||||
; IS__TUNIT____-SAME: (i32 [[UNKNOWN:%.*]], i32 [[B:%.*]]) [[ATTR0]] {
|
||||
; IS__TUNIT____-NEXT: [[TMP1:%.*]] = call i32 @test_range(i32 [[UNKNOWN]]) [[ATTR0]], [[RNG0]]
|
||||
; IS__TUNIT____-NEXT: [[TMP2:%.*]] = add nsw i32 [[TMP1]], [[B]]
|
||||
; IS__TUNIT____-NEXT: ret i32 [[TMP2]]
|
||||
;
|
||||
; IS__CGSCC____-LABEL: define {{[^@]+}}@test2
|
||||
; IS__CGSCC____-SAME: (i32 [[UNKNOWN:%.*]], i32 [[B:%.*]]) [[ATTR0]] {
|
||||
; IS__CGSCC____-NEXT: [[TMP1:%.*]] = call i32 @test_range(i32 [[UNKNOWN]]) [[ATTR1]], [[RNG0]]
|
||||
; IS__CGSCC____-NEXT: [[TMP2:%.*]] = add nsw i32 [[TMP1]], [[B]]
|
||||
; IS__CGSCC____-NEXT: ret i32 [[TMP2]]
|
||||
;
|
||||
%1 = call i32 @test_range(i32 %unknown)
|
||||
%2 = add nsw i32 %1, %b
|
||||
ret i32 %2
|
||||
}
|
||||
|
||||
; Positive checks
|
||||
|
||||
define i32 @test1_pcheck(i32 %unknown) {
|
||||
; NOT_CGSCC_NPM-LABEL: define {{[^@]+}}@test1_pcheck
|
||||
; NOT_CGSCC_NPM-SAME: (i32 [[UNKNOWN:%.*]]) [[ATTR0:#.*]] {
|
||||
; NOT_CGSCC_NPM-NEXT: [[TMP1:%.*]] = call i32 @test1(i32 [[UNKNOWN]], i32 noundef 20)
|
||||
; NOT_CGSCC_NPM-NEXT: [[TMP2:%.*]] = icmp sle i32 [[TMP1]], 90
|
||||
; NOT_CGSCC_NPM-NEXT: [[TMP3:%.*]] = zext i1 [[TMP2]] to i32
|
||||
; NOT_CGSCC_NPM-NEXT: ret i32 [[TMP3]]
|
||||
;
|
||||
; IS__CGSCC_NPM-LABEL: define {{[^@]+}}@test1_pcheck
|
||||
; IS__CGSCC_NPM-SAME: (i32 [[UNKNOWN:%.*]]) [[ATTR0:#.*]] {
|
||||
; IS__CGSCC_NPM-NEXT: [[TMP1:%.*]] = call i32 @test1(i32 [[UNKNOWN]], i32 noundef 20) [[ATTR1:#.*]], [[RNG1:!range !.*]]
|
||||
; IS__CGSCC_NPM-NEXT: [[TMP2:%.*]] = icmp sle i32 [[TMP1]], 90
|
||||
; IS__CGSCC_NPM-NEXT: [[TMP3:%.*]] = zext i1 [[TMP2]] to i32
|
||||
; IS__CGSCC_NPM-NEXT: ret i32 [[TMP3]]
|
||||
;
|
||||
%1 = call i32 @test1(i32 %unknown, i32 20)
|
||||
%2 = icmp sle i32 %1, 90
|
||||
%3 = zext i1 %2 to i32
|
||||
ret i32 %3
|
||||
}
|
||||
|
||||
define i32 @test2_pcheck(i32 %unknown) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@test2_pcheck
|
||||
; CHECK-SAME: (i32 [[UNKNOWN:%.*]]) [[ATTR0]] {
|
||||
; CHECK-NEXT: [[TMP1:%.*]] = call i32 @test2(i32 [[UNKNOWN]], i32 noundef 20)
|
||||
; CHECK-NEXT: [[TMP2:%.*]] = icmp sge i32 [[TMP1]], 20
|
||||
; CHECK-NEXT: [[TMP3:%.*]] = zext i1 [[TMP2]] to i32
|
||||
; CHECK-NEXT: ret i32 [[TMP3]]
|
||||
;
|
||||
%1 = call i32 @test2(i32 %unknown, i32 20)
|
||||
%2 = icmp sge i32 %1, 20
|
||||
%3 = zext i1 %2 to i32
|
||||
ret i32 %3
|
||||
}
|
||||
|
||||
; Negative checks
|
||||
|
||||
define i32 @test1_ncheck(i32 %unknown) {
|
||||
; NOT_CGSCC_NPM-LABEL: define {{[^@]+}}@test1_ncheck
|
||||
; NOT_CGSCC_NPM-SAME: (i32 [[UNKNOWN:%.*]]) [[ATTR0]] {
|
||||
; NOT_CGSCC_NPM-NEXT: [[TMP1:%.*]] = call i32 @test1(i32 [[UNKNOWN]], i32 noundef 20)
|
||||
; NOT_CGSCC_NPM-NEXT: [[TMP2:%.*]] = icmp sle i32 [[TMP1]], 10
|
||||
; NOT_CGSCC_NPM-NEXT: [[TMP3:%.*]] = zext i1 [[TMP2]] to i32
|
||||
; NOT_CGSCC_NPM-NEXT: ret i32 [[TMP3]]
|
||||
;
|
||||
; IS__CGSCC_NPM-LABEL: define {{[^@]+}}@test1_ncheck
|
||||
; IS__CGSCC_NPM-SAME: (i32 [[UNKNOWN:%.*]]) [[ATTR0]] {
|
||||
; IS__CGSCC_NPM-NEXT: [[TMP1:%.*]] = call i32 @test1(i32 [[UNKNOWN]], i32 noundef 20) [[ATTR1]], [[RNG1]]
|
||||
; IS__CGSCC_NPM-NEXT: [[TMP2:%.*]] = icmp sle i32 [[TMP1]], 10
|
||||
; IS__CGSCC_NPM-NEXT: [[TMP3:%.*]] = zext i1 [[TMP2]] to i32
|
||||
; IS__CGSCC_NPM-NEXT: ret i32 [[TMP3]]
|
||||
;
|
||||
%1 = call i32 @test1(i32 %unknown, i32 20)
|
||||
%2 = icmp sle i32 %1, 10
|
||||
%3 = zext i1 %2 to i32
|
||||
ret i32 %3
|
||||
}
|
||||
|
||||
define i32 @test2_ncheck(i32 %unknown) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@test2_ncheck
|
||||
; CHECK-SAME: (i32 [[UNKNOWN:%.*]]) [[ATTR0]] {
|
||||
; CHECK-NEXT: [[TMP1:%.*]] = call i32 @test2(i32 [[UNKNOWN]], i32 noundef 20)
|
||||
; CHECK-NEXT: [[TMP2:%.*]] = icmp sge i32 [[TMP1]], 30
|
||||
; CHECK-NEXT: [[TMP3:%.*]] = zext i1 [[TMP2]] to i32
|
||||
; CHECK-NEXT: ret i32 [[TMP3]]
|
||||
;
|
||||
%1 = call i32 @test2(i32 %unknown, i32 20)
|
||||
%2 = icmp sge i32 %1, 30
|
||||
%3 = zext i1 %2 to i32
|
||||
ret i32 %3
|
||||
}
|
126
test/Transforms/Attributor/cb_range_enabled.ll
Normal file
126
test/Transforms/Attributor/cb_range_enabled.ll
Normal file
@ -0,0 +1,126 @@
|
||||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes
|
||||
; call site specific analysis is enabled
|
||||
|
||||
; RUN: opt -attributor -enable-new-pm=0 -attributor-enable-call-site-specific-deduction=true -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=1 -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_NPM,NOT_CGSCC_OPM,NOT_TUNIT_NPM,IS__TUNIT____,IS________OPM,IS__TUNIT_OPM
|
||||
|
||||
; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-enable-call-site-specific-deduction=true -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=1 -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_OPM,NOT_CGSCC_NPM,NOT_TUNIT_OPM,IS__TUNIT____,IS________NPM,IS__TUNIT_NPM
|
||||
|
||||
; RUN: opt -attributor-cgscc -attributor-enable-call-site-specific-deduction=true -enable-new-pm=0 -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_TUNIT_NPM,NOT_TUNIT_OPM,NOT_CGSCC_NPM,IS__CGSCC____,IS________OPM,IS__CGSCC_OPM
|
||||
|
||||
; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-enable-call-site-specific-deduction=true -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_TUNIT_NPM,NOT_TUNIT_OPM,NOT_CGSCC_OPM,IS__CGSCC____,IS________NPM,IS__CGSCC_NPM
|
||||
|
||||
define i32 @test_range(i32 %unknown) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@test_range
|
||||
; CHECK-SAME: (i32 [[UNKNOWN:%.*]]) [[ATTR0:#.*]] {
|
||||
; CHECK-NEXT: [[TMP1:%.*]] = icmp sgt i32 [[UNKNOWN]], 100
|
||||
; CHECK-NEXT: [[TMP2:%.*]] = select i1 [[TMP1]], i32 100, i32 0
|
||||
; CHECK-NEXT: ret i32 [[TMP2]]
|
||||
;
|
||||
%1 = icmp sgt i32 %unknown, 100
|
||||
%2 = select i1 %1, i32 100, i32 0
|
||||
ret i32 %2
|
||||
}
|
||||
|
||||
define i32 @test1(i32 %unknown, i32 %b) {
|
||||
; IS__TUNIT____-LABEL: define {{[^@]+}}@test1
|
||||
; IS__TUNIT____-SAME: (i32 [[UNKNOWN:%.*]], i32 [[B:%.*]]) [[ATTR0:#.*]] {
|
||||
; IS__TUNIT____-NEXT: [[TMP1:%.*]] = call i32 @test_range(i32 [[UNKNOWN]]) [[ATTR0]], [[RNG0:!range !.*]]
|
||||
; IS__TUNIT____-NEXT: [[TMP2:%.*]] = sub nsw i32 [[TMP1]], [[B]]
|
||||
; IS__TUNIT____-NEXT: ret i32 [[TMP2]]
|
||||
;
|
||||
; IS__CGSCC____-LABEL: define {{[^@]+}}@test1
|
||||
; IS__CGSCC____-SAME: (i32 [[UNKNOWN:%.*]], i32 [[B:%.*]]) [[ATTR0:#.*]] {
|
||||
; IS__CGSCC____-NEXT: [[TMP1:%.*]] = call i32 @test_range(i32 [[UNKNOWN]]) [[ATTR1:#.*]], [[RNG0:!range !.*]]
|
||||
; IS__CGSCC____-NEXT: [[TMP2:%.*]] = sub nsw i32 [[TMP1]], [[B]]
|
||||
; IS__CGSCC____-NEXT: ret i32 [[TMP2]]
|
||||
;
|
||||
%1 = call i32 @test_range(i32 %unknown)
|
||||
%2 = sub nsw i32 %1, %b
|
||||
ret i32 %2
|
||||
}
|
||||
|
||||
define i32 @test2(i32 %unknown, i32 %b) {
|
||||
; IS__TUNIT____-LABEL: define {{[^@]+}}@test2
|
||||
; IS__TUNIT____-SAME: (i32 [[UNKNOWN:%.*]], i32 [[B:%.*]]) [[ATTR0]] {
|
||||
; IS__TUNIT____-NEXT: [[TMP1:%.*]] = call i32 @test_range(i32 [[UNKNOWN]]) [[ATTR0]], [[RNG0]]
|
||||
; IS__TUNIT____-NEXT: [[TMP2:%.*]] = add nsw i32 [[TMP1]], [[B]]
|
||||
; IS__TUNIT____-NEXT: ret i32 [[TMP2]]
|
||||
;
|
||||
; IS__CGSCC____-LABEL: define {{[^@]+}}@test2
|
||||
; IS__CGSCC____-SAME: (i32 [[UNKNOWN:%.*]], i32 [[B:%.*]]) [[ATTR0]] {
|
||||
; IS__CGSCC____-NEXT: [[TMP1:%.*]] = call i32 @test_range(i32 [[UNKNOWN]]) [[ATTR1]], [[RNG0]]
|
||||
; IS__CGSCC____-NEXT: [[TMP2:%.*]] = add nsw i32 [[TMP1]], [[B]]
|
||||
; IS__CGSCC____-NEXT: ret i32 [[TMP2]]
|
||||
;
|
||||
%1 = call i32 @test_range(i32 %unknown)
|
||||
%2 = add nsw i32 %1, %b
|
||||
ret i32 %2
|
||||
}
|
||||
|
||||
; Positive checks
|
||||
|
||||
define i32 @test1_pcheck(i32 %unknown) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@test1_pcheck
|
||||
; CHECK-SAME: (i32 [[UNKNOWN:%.*]]) [[ATTR0]] {
|
||||
; CHECK-NEXT: ret i32 1
|
||||
;
|
||||
%1 = call i32 @test1(i32 %unknown, i32 20)
|
||||
%2 = icmp sle i32 %1, 90
|
||||
%3 = zext i1 %2 to i32
|
||||
ret i32 %3
|
||||
}
|
||||
|
||||
define i32 @test2_pcheck(i32 %unknown) {
|
||||
; CHECK-LABEL: define {{[^@]+}}@test2_pcheck
|
||||
; CHECK-SAME: (i32 [[UNKNOWN:%.*]]) [[ATTR0]] {
|
||||
; CHECK-NEXT: ret i32 1
|
||||
;
|
||||
%1 = call i32 @test2(i32 %unknown, i32 20)
|
||||
%2 = icmp sge i32 %1, 20
|
||||
%3 = zext i1 %2 to i32
|
||||
ret i32 %3
|
||||
}
|
||||
|
||||
; Negative checks
|
||||
|
||||
define i32 @test1_ncheck(i32 %unknown) {
|
||||
; IS__TUNIT____-LABEL: define {{[^@]+}}@test1_ncheck
|
||||
; IS__TUNIT____-SAME: (i32 [[UNKNOWN:%.*]]) [[ATTR0]] {
|
||||
; IS__TUNIT____-NEXT: [[TMP1:%.*]] = call i32 @test1(i32 [[UNKNOWN]], i32 noundef 20) [[ATTR0]], [[RNG1:!range !.*]]
|
||||
; IS__TUNIT____-NEXT: [[TMP2:%.*]] = icmp sle i32 [[TMP1]], 10
|
||||
; IS__TUNIT____-NEXT: [[TMP3:%.*]] = zext i1 [[TMP2]] to i32
|
||||
; IS__TUNIT____-NEXT: ret i32 [[TMP3]]
|
||||
;
|
||||
; IS__CGSCC____-LABEL: define {{[^@]+}}@test1_ncheck
|
||||
; IS__CGSCC____-SAME: (i32 [[UNKNOWN:%.*]]) [[ATTR0]] {
|
||||
; IS__CGSCC____-NEXT: [[TMP1:%.*]] = call i32 @test1(i32 [[UNKNOWN]], i32 noundef 20) [[ATTR1]], [[RNG1:!range !.*]]
|
||||
; IS__CGSCC____-NEXT: [[TMP2:%.*]] = icmp sle i32 [[TMP1]], 10
|
||||
; IS__CGSCC____-NEXT: [[TMP3:%.*]] = zext i1 [[TMP2]] to i32
|
||||
; IS__CGSCC____-NEXT: ret i32 [[TMP3]]
|
||||
;
|
||||
%1 = call i32 @test1(i32 %unknown, i32 20)
|
||||
%2 = icmp sle i32 %1, 10
|
||||
%3 = zext i1 %2 to i32
|
||||
ret i32 %3
|
||||
}
|
||||
|
||||
define i32 @test2_ncheck(i32 %unknown) {
|
||||
; IS__TUNIT____-LABEL: define {{[^@]+}}@test2_ncheck
|
||||
; IS__TUNIT____-SAME: (i32 [[UNKNOWN:%.*]]) [[ATTR0]] {
|
||||
; IS__TUNIT____-NEXT: [[TMP1:%.*]] = call i32 @test2(i32 [[UNKNOWN]], i32 noundef 20) [[ATTR0]], [[RNG2:!range !.*]]
|
||||
; IS__TUNIT____-NEXT: [[TMP2:%.*]] = icmp sge i32 [[TMP1]], 30
|
||||
; IS__TUNIT____-NEXT: [[TMP3:%.*]] = zext i1 [[TMP2]] to i32
|
||||
; IS__TUNIT____-NEXT: ret i32 [[TMP3]]
|
||||
;
|
||||
; IS__CGSCC____-LABEL: define {{[^@]+}}@test2_ncheck
|
||||
; IS__CGSCC____-SAME: (i32 [[UNKNOWN:%.*]]) [[ATTR0]] {
|
||||
; IS__CGSCC____-NEXT: [[TMP1:%.*]] = call i32 @test2(i32 [[UNKNOWN]], i32 noundef 20) [[ATTR1]], [[RNG2:!range !.*]]
|
||||
; IS__CGSCC____-NEXT: [[TMP2:%.*]] = icmp sge i32 [[TMP1]], 30
|
||||
; IS__CGSCC____-NEXT: [[TMP3:%.*]] = zext i1 [[TMP2]] to i32
|
||||
; IS__CGSCC____-NEXT: ret i32 [[TMP3]]
|
||||
;
|
||||
%1 = call i32 @test2(i32 %unknown, i32 20)
|
||||
%2 = icmp sge i32 %1, 30
|
||||
%3 = zext i1 %2 to i32
|
||||
ret i32 %3
|
||||
}
|
@ -21,6 +21,22 @@
|
||||
|
||||
namespace llvm {
|
||||
|
||||
TEST_F(AttributorTestBase, IRPPositionCallBaseContext) {
|
||||
const char *ModuleString = R"(
|
||||
define i32 @foo(i32 %a) {
|
||||
entry:
|
||||
ret i32 %a
|
||||
}
|
||||
)";
|
||||
|
||||
parseModule(ModuleString);
|
||||
|
||||
Function *F = M->getFunction("foo");
|
||||
IRPosition Pos = IRPosition::function(*F, (const llvm::CallBase *)0xDEADBEEF);
|
||||
EXPECT_TRUE(Pos.hasCallBaseContext());
|
||||
EXPECT_FALSE(Pos.stripCallBaseContext().hasCallBaseContext());
|
||||
}
|
||||
|
||||
TEST_F(AttributorTestBase, TestCast) {
|
||||
const char *ModuleString = R"(
|
||||
define i32 @foo(i32 %a, i32 %b) {
|
||||
|
Loading…
Reference in New Issue
Block a user