Skip to content

Commit

Permalink
[Attributor] Dominating must-write accesses allow unknown initial values
Browse files Browse the repository at this point in the history
If we have a dominating must-write access we do not need to know the
initial value of some object to perform reasoning about the potential
values. The dominating must-write has overwritten the initial value.
  • Loading branch information
jdoerfert committed Jul 22, 2022
1 parent dfac030 commit 62f7888
Show file tree
Hide file tree
Showing 14 changed files with 220 additions and 129 deletions.
51 changes: 40 additions & 11 deletions llvm/include/llvm/Transforms/IPO/Attributor.h
Expand Up @@ -4913,19 +4913,36 @@ struct AAPointerInfo : public AbstractAttribute {
AAPointerInfo(const IRPosition &IRP) : AbstractAttribute(IRP) {}

enum AccessKind {
AK_READ = 1 << 0,
AK_WRITE = 1 << 1,
AK_READ_WRITE = AK_READ | AK_WRITE,
// First two bits to distinguish may and must accesses
AK_MUST = 1 << 0,
AK_MAY = 1 << 1,

// Then two bits for read and write. These are not exclusive.
AK_R = 1 << 2,
AK_W = 1 << 3,
AK_RW = AK_R | AK_W,

// Helper for easy access.
AK_MAY_READ = AK_MAY | AK_R,
AK_MAY_WRITE = AK_MAY | AK_W,
AK_MAY_READ_WRITE = AK_MAY | AK_R | AK_W,
AK_MUST_READ = AK_MUST | AK_R,
AK_MUST_WRITE = AK_MUST | AK_W,
AK_MUST_READ_WRITE = AK_MUST | AK_R | AK_W,
};

/// An access description.
struct Access {
Access(Instruction *I, Optional<Value *> Content, AccessKind Kind, Type *Ty)
: LocalI(I), RemoteI(I), Content(Content), Kind(Kind), Ty(Ty) {}
: LocalI(I), RemoteI(I), Content(Content), Kind(Kind), Ty(Ty) {
verify();
}
Access(Instruction *LocalI, Instruction *RemoteI, Optional<Value *> Content,
AccessKind Kind, Type *Ty)
: LocalI(LocalI), RemoteI(RemoteI), Content(Content), Kind(Kind),
Ty(Ty) {}
Ty(Ty) {
verify();
}
Access(const Access &Other) = default;
Access(const Access &&Other)
: LocalI(Other.LocalI), RemoteI(Other.RemoteI), Content(Other.Content),
Expand All @@ -4946,14 +4963,22 @@ struct AAPointerInfo : public AbstractAttribute {
return *this;
}

void verify() {
assert(isMustAccess() + isMayAccess() == 1 &&
"Expect must or may access, not both.");
}

/// Return the access kind.
AccessKind getKind() const { return Kind; }

/// Return true if this is a read access.
bool isRead() const { return Kind & AK_READ; }
bool isRead() const { return Kind & AK_R; }

/// Return true if this is a write access.
bool isWrite() const { return Kind & AK_WRITE; }
bool isWrite() const { return Kind & AK_W; }

bool isMustAccess() const { return Kind & AK_MUST; }
bool isMayAccess() const { return Kind & AK_MAY; }

/// Return the instruction that causes the access with respect to the local
/// scope of the associated attribute.
Expand Down Expand Up @@ -5057,10 +5082,14 @@ struct AAPointerInfo : public AbstractAttribute {
/// return true if all such accesses were known and the callback returned true
/// for all of them, false otherwise. In contrast to forallInterferingAccesses
/// this function will perform reasoning to exclude write accesses that cannot
/// affect the load even if they on the surface look as if they would.
virtual bool forallInterferingAccesses(
Attributor &A, const AbstractAttribute &QueryingAA, Instruction &I,
function_ref<bool(const Access &, bool)> CB) const = 0;
/// affect the load even if they on the surface look as if they would. The
/// flag \p HasBeenWrittenTo will be set to true if we know that \p I does not
/// read the intial value of the underlying memory.
virtual bool
forallInterferingAccesses(Attributor &A, const AbstractAttribute &QueryingAA,
Instruction &I,
function_ref<bool(const Access &, bool)> CB,
bool &HasBeenWrittenTo) const = 0;

/// This function should return true if the type of the \p AA is AAPointerInfo
static bool classof(const AbstractAttribute *AA) {
Expand Down
34 changes: 22 additions & 12 deletions llvm/lib/Transforms/IPO/Attributor.cpp
Expand Up @@ -389,17 +389,6 @@ static bool getPotentialCopiesOfMemoryValue(
NullOnly = false;
};

if (IsLoad) {
Value *InitialValue = AA::getInitialValueForObj(*Obj, *I.getType(), TLI);
if (!InitialValue) {
LLVM_DEBUG(dbgs() << "Failed to get initial value: " << *Obj << "\n");
return false;
}
CheckForNullOnlyAndUndef(InitialValue, /* IsExact */ true);
NewCopies.push_back(InitialValue);
NewCopyOrigins.push_back(nullptr);
}

auto CheckAccess = [&](const AAPointerInfo::Access &Acc, bool IsExact) {
if ((IsLoad && !Acc.isWrite()) || (!IsLoad && !Acc.isRead()))
return true;
Expand Down Expand Up @@ -448,15 +437,36 @@ static bool getPotentialCopiesOfMemoryValue(
return true;
};

// If the value has been written to we don't need the initial value of the
// object.
bool HasBeenWrittenTo = false;

auto &PI = A.getAAFor<AAPointerInfo>(QueryingAA, IRPosition::value(*Obj),
DepClassTy::NONE);
if (!PI.forallInterferingAccesses(A, QueryingAA, I, CheckAccess)) {
if (!PI.forallInterferingAccesses(A, QueryingAA, I, CheckAccess,
HasBeenWrittenTo)) {
LLVM_DEBUG(
dbgs()
<< "Failed to verify all interfering accesses for underlying object: "
<< *Obj << "\n");
return false;
}

if (IsLoad && !HasBeenWrittenTo) {
Value *InitialValue = AA::getInitialValueForObj(*Obj, *I.getType(), TLI);
if (!InitialValue)
return false;
CheckForNullOnlyAndUndef(InitialValue, /* IsExact */ true);
if (NullRequired && !NullOnly) {
LLVM_DEBUG(dbgs() << "Non exact access but initial value that is not "
"null or undef, abort!\n");
return false;
}

NewCopies.push_back(InitialValue);
NewCopyOrigins.push_back(nullptr);
}

PIs.push_back(&PI);
}

Expand Down
72 changes: 48 additions & 24 deletions llvm/lib/Transforms/IPO/AttributorAttributes.cpp
Expand Up @@ -961,9 +961,14 @@ struct AAPointerInfoImpl
const override {
return State::forallInterferingAccesses(OAS, CB);
}
bool forallInterferingAccesses(
Attributor &A, const AbstractAttribute &QueryingAA, Instruction &I,
function_ref<bool(const Access &, bool)> UserCB) const override {

bool
forallInterferingAccesses(Attributor &A, const AbstractAttribute &QueryingAA,
Instruction &I,
function_ref<bool(const Access &, bool)> UserCB,
bool &HasBeenWrittenTo) const override {
HasBeenWrittenTo = false;

SmallPtrSet<const Access *, 8> DominatingWrites;
SmallVector<std::pair<const Access *, bool>, 8> InterferingAccesses;

Expand Down Expand Up @@ -999,14 +1004,12 @@ struct AAPointerInfoImpl

const bool FindInterferingWrites = I.mayReadFromMemory();
const bool FindInterferingReads = I.mayWriteToMemory();
const bool UseDominanceReasoning = FindInterferingWrites;
const bool UseDominanceReasoning =
FindInterferingWrites && NoRecurseAA.isKnownNoRecurse();
const bool CanUseCFGResoning = CanIgnoreThreading(I);
InformationCache &InfoCache = A.getInfoCache();
const DominatorTree *DT =
NoRecurseAA.isKnownNoRecurse() && UseDominanceReasoning
? InfoCache.getAnalysisResultForFunction<DominatorTreeAnalysis>(
Scope)
: nullptr;
InfoCache.getAnalysisResultForFunction<DominatorTreeAnalysis>(Scope);

enum GPUAddressSpace : unsigned {
Generic = 0,
Expand Down Expand Up @@ -1063,6 +1066,12 @@ struct AAPointerInfoImpl
(!FindInterferingReads || !Acc.isRead()))
return true;

bool Dominates = DT && Exact && Acc.isMustAccess() &&
(Acc.getLocalInst()->getFunction() == &Scope) &&
DT->dominates(Acc.getRemoteInst(), &I);
if (Dominates)
HasBeenWrittenTo = true;

// For now we only filter accesses based on CFG reasoning which does not
// work yet if we have threading effects, or the access is complicated.
if (CanUseCFGResoning) {
Expand All @@ -1073,11 +1082,8 @@ struct AAPointerInfoImpl
!AA::isPotentiallyReachable(A, I, *Acc.getLocalInst(), QueryingAA,
IsLiveInCalleeCB)))
return true;
if (DT && Exact && (Acc.getLocalInst()->getFunction() == &Scope) &&
IsSameThreadAsLoad(Acc)) {
if (DT->dominates(Acc.getLocalInst(), &I))
DominatingWrites.insert(&Acc);
}
if (Dominates && UseDominanceReasoning && IsSameThreadAsLoad(Acc))
DominatingWrites.insert(&Acc);
}

InterferingAccesses.push_back({&Acc, Exact});
Expand Down Expand Up @@ -1120,7 +1126,8 @@ struct AAPointerInfoImpl
// succeeded for all or not.
unsigned NumInterferingAccesses = InterferingAccesses.size();
for (auto &It : InterferingAccesses) {
if (!DT || NumInterferingAccesses > MaxInterferingAccesses ||
if (!DT || !UseDominanceReasoning ||
NumInterferingAccesses > MaxInterferingAccesses ||
!CanSkipAccess(*It.first, It.second)) {
if (!UserCB(*It.first, It.second))
return false;
Expand Down Expand Up @@ -1156,8 +1163,9 @@ struct AAPointerInfoImpl
if (FromCallee) {
Content = A.translateArgumentToCallSiteContent(
RAcc.getContent(), CB, *this, UsedAssumedInformation);
AK = AccessKind(
AK & (IsByval ? AccessKind::AK_READ : AccessKind::AK_READ_WRITE));
AK =
AccessKind(AK & (IsByval ? AccessKind::AK_R : AccessKind::AK_RW));
AK = AccessKind(AK | (RAcc.isMayAccess() ? AK_MAY : AK_MUST));
}
Changed =
Changed | addAccess(A, OAS.getOffset(), OAS.getSize(), CB, Content,
Expand Down Expand Up @@ -1345,21 +1353,37 @@ struct AAPointerInfoFloating : public AAPointerInfoImpl {
return true;
}

if (auto *LoadI = dyn_cast<LoadInst>(Usr))
return handleAccess(A, *LoadI, *CurPtr, /* Content */ nullptr,
AccessKind::AK_READ, OffsetInfoMap[CurPtr].Offset,
Changed, LoadI->getType());
if (auto *LoadI = dyn_cast<LoadInst>(Usr)) {
// If the access is to a pointer that may or may not be the associated
// value, e.g. due to a PHI, we cannot assume it will be read.
AccessKind AK = AccessKind::AK_R;
if (getUnderlyingObject(CurPtr) == &AssociatedValue)
AK = AccessKind(AK | AccessKind::AK_MUST);
else
AK = AccessKind(AK | AccessKind::AK_MAY);
return handleAccess(A, *LoadI, *CurPtr, /* Content */ nullptr, AK,
OffsetInfoMap[CurPtr].Offset, Changed,
LoadI->getType());
}

if (auto *StoreI = dyn_cast<StoreInst>(Usr)) {
if (StoreI->getValueOperand() == CurPtr) {
LLVM_DEBUG(dbgs() << "[AAPointerInfo] Escaping use in store "
<< *StoreI << "\n");
return false;
}
// If the access is to a pointer that may or may not be the associated
// value, e.g. due to a PHI, we cannot assume it will be written.
AccessKind AK = AccessKind::AK_W;
if (getUnderlyingObject(CurPtr) == &AssociatedValue)
AK = AccessKind(AK | AccessKind::AK_MUST);
else
AK = AccessKind(AK | AccessKind::AK_MAY);
bool UsedAssumedInformation = false;
Optional<Value *> Content =
A.getAssumedSimplified(*StoreI->getValueOperand(), *this,
UsedAssumedInformation, AA::Interprocedural);
return handleAccess(A, *StoreI, *CurPtr, Content, AccessKind::AK_WRITE,
return handleAccess(A, *StoreI, *CurPtr, Content, AK,
OffsetInfoMap[CurPtr].Offset, Changed,
StoreI->getValueOperand()->getType());
}
Expand Down Expand Up @@ -1474,10 +1498,10 @@ struct AAPointerInfoCallSiteArgument final : AAPointerInfoFloating {
unsigned ArgNo = getIRPosition().getCallSiteArgNo();
ChangeStatus Changed = ChangeStatus::UNCHANGED;
if (ArgNo == 0) {
handleAccess(A, *MI, Ptr, nullptr, AccessKind::AK_WRITE, 0, Changed,
nullptr, LengthVal);
handleAccess(A, *MI, Ptr, nullptr, AccessKind::AK_MUST_WRITE, 0,
Changed, nullptr, LengthVal);
} else if (ArgNo == 1) {
handleAccess(A, *MI, Ptr, nullptr, AccessKind::AK_READ, 0, Changed,
handleAccess(A, *MI, Ptr, nullptr, AccessKind::AK_MUST_READ, 0, Changed,
nullptr, LengthVal);
} else {
LLVM_DEBUG(dbgs() << "[AAPointerInfo] Unhandled memory intrinsic "
Expand Down
@@ -1,6 +1,6 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes --check-globals
; RUN: opt -attributor -enable-new-pm=0 -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=2 -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=2 -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 -enable-new-pm=0 -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=5 -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=5 -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

Expand Down
Expand Up @@ -110,7 +110,7 @@ define internal i32 @caller(i32* %A) {
; IS__CGSCC_NPM-SAME: (i32 [[TMP0:%.*]]) #[[ATTR1:[0-9]+]] {
; IS__CGSCC_NPM-NEXT: [[A_PRIV:%.*]] = alloca i32, align 4
; IS__CGSCC_NPM-NEXT: store i32 [[TMP0]], i32* [[A_PRIV]], align 4
; IS__CGSCC_NPM-NEXT: [[C:%.*]] = call i32 @test(i32* noalias nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[A_PRIV]], i64 1) #[[ATTR3:[0-9]+]]
; IS__CGSCC_NPM-NEXT: [[C:%.*]] = call i32 @test(i32* noalias nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[A_PRIV]], i64 noundef 1) #[[ATTR3:[0-9]+]]
; IS__CGSCC_NPM-NEXT: ret i32 [[C]]
;
%B = alloca i64
Expand All @@ -137,7 +137,7 @@ define i32 @callercaller() {
; IS__CGSCC_NPM: Function Attrs: nofree nosync nounwind readnone willreturn
; IS__CGSCC_NPM-LABEL: define {{[^@]+}}@callercaller
; IS__CGSCC_NPM-SAME: () #[[ATTR2:[0-9]+]] {
; IS__CGSCC_NPM-NEXT: [[X:%.*]] = call i32 @caller(i32 2) #[[ATTR4:[0-9]+]]
; IS__CGSCC_NPM-NEXT: [[X:%.*]] = call i32 @caller(i32 noundef 2) #[[ATTR4:[0-9]+]]
; IS__CGSCC_NPM-NEXT: ret i32 [[X]]
;
%B = alloca i32
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/Transforms/Attributor/ArgumentPromotion/attrs.ll
Expand Up @@ -105,7 +105,7 @@ define i32 @test(i32* %X) {
; IS__CGSCC_NPM-NEXT: [[TMP1:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[S]], i32 0, i32 0
; IS__CGSCC_NPM-NEXT: [[TMP4:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[S]], i32 0, i32 1
; IS__CGSCC_NPM-NEXT: [[TMP0:%.*]] = load i32, i32* [[X]], align 4
; IS__CGSCC_NPM-NEXT: [[C:%.*]] = call i32 @f(i32 1, i64 2, i32 [[TMP0]]) #[[ATTR2:[0-9]+]]
; IS__CGSCC_NPM-NEXT: [[C:%.*]] = call i32 @f(i32 noundef 1, i64 noundef 2, i32 [[TMP0]]) #[[ATTR2:[0-9]+]]
; IS__CGSCC_NPM-NEXT: ret i32 [[C]]
;
entry:
Expand Down
Expand Up @@ -44,7 +44,7 @@ define internal i32 @caller(i32* %B) {
; IS__CGSCC_NPM-SAME: (i32 [[TMP0:%.*]]) #[[ATTR1:[0-9]+]] {
; IS__CGSCC_NPM-NEXT: [[B_PRIV:%.*]] = alloca i32, align 4
; IS__CGSCC_NPM-NEXT: store i32 [[TMP0]], i32* [[B_PRIV]], align 4
; IS__CGSCC_NPM-NEXT: [[C:%.*]] = call i32 @test(i32 1, i32* noalias nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[B_PRIV]]) #[[ATTR3:[0-9]+]]
; IS__CGSCC_NPM-NEXT: [[C:%.*]] = call i32 @test(i32 noundef 1, i32* noalias nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[B_PRIV]]) #[[ATTR3:[0-9]+]]
; IS__CGSCC_NPM-NEXT: ret i32 [[C]]
;
%A = alloca i32
Expand All @@ -71,7 +71,7 @@ define i32 @callercaller() {
; IS__CGSCC_NPM: Function Attrs: nofree nosync nounwind readnone willreturn
; IS__CGSCC_NPM-LABEL: define {{[^@]+}}@callercaller
; IS__CGSCC_NPM-SAME: () #[[ATTR2:[0-9]+]] {
; IS__CGSCC_NPM-NEXT: [[X:%.*]] = call i32 @caller(i32 2) #[[ATTR4:[0-9]+]]
; IS__CGSCC_NPM-NEXT: [[X:%.*]] = call i32 @caller(i32 noundef 2) #[[ATTR4:[0-9]+]]
; IS__CGSCC_NPM-NEXT: ret i32 [[X]]
;
%B = alloca i32
Expand Down
Expand Up @@ -95,7 +95,7 @@ define i32 @test(i32* %X) {
; IS__CGSCC_NPM-NEXT: [[TMP1:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[S]], i32 0, i32 0
; IS__CGSCC_NPM-NEXT: [[TMP4:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[S]], i32 0, i32 1
; IS__CGSCC_NPM-NEXT: [[TMP0:%.*]] = load i32, i32* [[X]], align 4
; IS__CGSCC_NPM-NEXT: call void @f(i32 1, i64 2, i32 [[TMP0]]) #[[ATTR2:[0-9]+]]
; IS__CGSCC_NPM-NEXT: call void @f(i32 noundef 1, i64 noundef 2, i32 [[TMP0]]) #[[ATTR2:[0-9]+]]
; IS__CGSCC_NPM-NEXT: ret i32 0
;
entry:
Expand Down
4 changes: 2 additions & 2 deletions llvm/test/Transforms/Attributor/ArgumentPromotion/byval.ll
Expand Up @@ -134,8 +134,8 @@ define i32 @main() nounwind {
; IS__CGSCC_NPM-NEXT: [[S:%.*]] = alloca [[STRUCT_SS:%.*]], align 4
; IS__CGSCC_NPM-NEXT: [[TMP1:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[S]], i32 0, i32 0
; IS__CGSCC_NPM-NEXT: [[TMP4:%.*]] = getelementptr [[STRUCT_SS]], %struct.ss* [[S]], i32 0, i32 1
; IS__CGSCC_NPM-NEXT: [[C0:%.*]] = call i32 @f(i32 1, i64 2) #[[ATTR2:[0-9]+]]
; IS__CGSCC_NPM-NEXT: [[C1:%.*]] = call i32 @g(i32 1, i64 2) #[[ATTR2]]
; IS__CGSCC_NPM-NEXT: [[C0:%.*]] = call i32 @f(i32 noundef 1, i64 noundef 2) #[[ATTR2:[0-9]+]]
; IS__CGSCC_NPM-NEXT: [[C1:%.*]] = call i32 @g(i32 noundef 1, i64 noundef 2) #[[ATTR2]]
; IS__CGSCC_NPM-NEXT: [[A:%.*]] = add i32 [[C0]], [[C1]]
; IS__CGSCC_NPM-NEXT: ret i32 [[A]]
;
Expand Down
Expand Up @@ -57,7 +57,7 @@ define i32 @foo() {
; IS__CGSCC_NPM: Function Attrs: nofree nosync nounwind readnone willreturn
; IS__CGSCC_NPM-LABEL: define {{[^@]+}}@foo
; IS__CGSCC_NPM-SAME: () #[[ATTR1:[0-9]+]] {
; IS__CGSCC_NPM-NEXT: [[X:%.*]] = call i32 @callee(i32 17) #[[ATTR2:[0-9]+]]
; IS__CGSCC_NPM-NEXT: [[X:%.*]] = call i32 @callee(i32 noundef 17) #[[ATTR2:[0-9]+]]
; IS__CGSCC_NPM-NEXT: ret i32 [[X]]
;
%A = alloca i32 ; <i32*> [#uses=2]
Expand Down
Expand Up @@ -22,7 +22,7 @@ define void @caller() #0 {
; IS__TUNIT_NPM-NEXT: ret void
;
; IS__CGSCC_NPM-LABEL: define {{[^@]+}}@caller() {
; IS__CGSCC_NPM-NEXT: call void @promote_i32_ptr(i32 42), !prof [[PROF0:![0-9]+]]
; IS__CGSCC_NPM-NEXT: call void @promote_i32_ptr(i32 noundef 42), !prof [[PROF0:![0-9]+]]
; IS__CGSCC_NPM-NEXT: ret void
;
%x = alloca i32
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/Transforms/Attributor/internal-noalias.ll
Expand Up @@ -166,7 +166,7 @@ define i32 @visible_local_2() {
; IS__CGSCC_NPM: Function Attrs: nofree nosync nounwind readnone willreturn
; IS__CGSCC_NPM-LABEL: define {{[^@]+}}@visible_local_2
; IS__CGSCC_NPM-SAME: () #[[ATTR3:[0-9]+]] {
; IS__CGSCC_NPM-NEXT: [[CALL:%.*]] = call i32 @noalias_args_argmem_ro(i32 5, i32 5) #[[ATTR6:[0-9]+]]
; IS__CGSCC_NPM-NEXT: [[CALL:%.*]] = call i32 @noalias_args_argmem_ro(i32 noundef 5, i32 noundef 5) #[[ATTR6:[0-9]+]]
; IS__CGSCC_NPM-NEXT: ret i32 [[CALL]]
;
%B = alloca i32, align 4
Expand Down

0 comments on commit 62f7888

Please sign in to comment.