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),
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.
/// 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) {
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;
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);
}
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;
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,
(!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) {
!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});
// 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;
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,
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());
}
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 "
; 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
; 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
; 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
; 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:
; 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
; 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
; 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:
; 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]]
;
; 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]
; 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
; 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
; return Flag3;
; }
define i32 @static_global_simplifiable_3() {
-; NOT_TUNIT_NPM: Function Attrs: nofree norecurse nosync nounwind willreturn
-; NOT_TUNIT_NPM-LABEL: define {{[^@]+}}@static_global_simplifiable_3
-; NOT_TUNIT_NPM-SAME: () #[[ATTR5:[0-9]+]] {
-; NOT_TUNIT_NPM-NEXT: store i32 1, i32* @Flag3, align 4, !tbaa [[TBAA3]]
-; NOT_TUNIT_NPM-NEXT: [[I:%.*]] = load i32, i32* @Flag3, align 4, !tbaa [[TBAA3]]
-; NOT_TUNIT_NPM-NEXT: ret i32 [[I]]
+; IS________OPM: Function Attrs: nofree norecurse nosync nounwind willreturn
+; IS________OPM-LABEL: define {{[^@]+}}@static_global_simplifiable_3
+; IS________OPM-SAME: () #[[ATTR5:[0-9]+]] {
+; IS________OPM-NEXT: store i32 1, i32* @Flag3, align 4, !tbaa [[TBAA3]]
+; IS________OPM-NEXT: [[I:%.*]] = load i32, i32* @Flag3, align 4, !tbaa [[TBAA3]]
+; IS________OPM-NEXT: ret i32 [[I]]
;
-; IS__TUNIT_NPM: Function Attrs: nofree norecurse nosync nounwind willreturn
+; IS__TUNIT_NPM: Function Attrs: nofree norecurse nosync nounwind willreturn writeonly
; IS__TUNIT_NPM-LABEL: define {{[^@]+}}@static_global_simplifiable_3
-; IS__TUNIT_NPM-SAME: () #[[ATTR3]] {
+; IS__TUNIT_NPM-SAME: () #[[ATTR5]] {
; IS__TUNIT_NPM-NEXT: store i32 1, i32* @Flag3, align 4, !tbaa [[TBAA3]]
-; IS__TUNIT_NPM-NEXT: [[I:%.*]] = load i32, i32* @Flag3, align 4, !tbaa [[TBAA3]]
-; IS__TUNIT_NPM-NEXT: ret i32 [[I]]
+; IS__TUNIT_NPM-NEXT: ret i32 1
+;
+; IS__CGSCC_NPM: Function Attrs: nofree norecurse nosync nounwind willreturn writeonly
+; IS__CGSCC_NPM-LABEL: define {{[^@]+}}@static_global_simplifiable_3
+; IS__CGSCC_NPM-SAME: () #[[ATTR6:[0-9]+]] {
+; IS__CGSCC_NPM-NEXT: store i32 1, i32* @Flag3, align 4, !tbaa [[TBAA3]]
+; IS__CGSCC_NPM-NEXT: ret i32 1
;
store i32 1, i32* @Flag3, align 4, !tbaa !3
%i = load i32, i32* @Flag3, align 4, !tbaa !3
; IS__TUNIT_OPM-NEXT: [[I:%.*]] = load i32, i32* @Flag0, align 4, !tbaa [[TBAA3]]
; IS__TUNIT_OPM-NEXT: ret i32 [[I]]
;
-; IS________NPM: Function Attrs: nofree norecurse nosync nounwind readonly willreturn
-; IS________NPM-LABEL: define {{[^@]+}}@global_not_simplifiable_1
-; IS________NPM-SAME: (i32 [[CND:%.*]]) #[[ATTR6:[0-9]+]] {
-; IS________NPM-NEXT: entry:
-; IS________NPM-NEXT: [[I:%.*]] = load i32, i32* @Flag0, align 4, !tbaa [[TBAA3]]
-; IS________NPM-NEXT: ret i32 [[I]]
+; IS__TUNIT_NPM: Function Attrs: nofree norecurse nosync nounwind readonly willreturn
+; IS__TUNIT_NPM-LABEL: define {{[^@]+}}@global_not_simplifiable_1
+; IS__TUNIT_NPM-SAME: (i32 [[CND:%.*]]) #[[ATTR6:[0-9]+]] {
+; IS__TUNIT_NPM-NEXT: entry:
+; IS__TUNIT_NPM-NEXT: [[I:%.*]] = load i32, i32* @Flag0, align 4, !tbaa [[TBAA3]]
+; IS__TUNIT_NPM-NEXT: ret i32 [[I]]
;
-; IS__CGSCC_OPM: Function Attrs: nofree norecurse nosync nounwind readonly willreturn
-; IS__CGSCC_OPM-LABEL: define {{[^@]+}}@global_not_simplifiable_1
-; IS__CGSCC_OPM-SAME: (i32 [[CND:%.*]]) #[[ATTR7:[0-9]+]] {
-; IS__CGSCC_OPM-NEXT: entry:
-; IS__CGSCC_OPM-NEXT: [[I:%.*]] = load i32, i32* @Flag0, align 4, !tbaa [[TBAA3]]
-; IS__CGSCC_OPM-NEXT: ret i32 [[I]]
+; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readonly willreturn
+; IS__CGSCC____-LABEL: define {{[^@]+}}@global_not_simplifiable_1
+; IS__CGSCC____-SAME: (i32 [[CND:%.*]]) #[[ATTR7:[0-9]+]] {
+; IS__CGSCC____-NEXT: entry:
+; IS__CGSCC____-NEXT: [[I:%.*]] = load i32, i32* @Flag0, align 4, !tbaa [[TBAA3]]
+; IS__CGSCC____-NEXT: ret i32 [[I]]
;
entry:
%i = load i32, i32* @Flag0, align 4, !tbaa !3
ret i32 %i
}
+; Similiar to static_global_simplifiable_3 but with a may-store.
+define i32 @static_global_not_simplifiable_3(i1 %c, i32* %p) {
+; NOT_TUNIT_NPM: Function Attrs: nofree norecurse nosync nounwind willreturn
+; NOT_TUNIT_NPM-LABEL: define {{[^@]+}}@static_global_not_simplifiable_3
+; NOT_TUNIT_NPM-SAME: (i1 [[C:%.*]], i32* nocapture nofree writeonly [[P:%.*]]) #[[ATTR5:[0-9]+]] {
+; NOT_TUNIT_NPM-NEXT: [[SEL:%.*]] = select i1 [[C]], i32* @Flag3, i32* [[P]]
+; NOT_TUNIT_NPM-NEXT: store i32 1, i32* [[SEL]], align 4, !tbaa [[TBAA3]]
+; NOT_TUNIT_NPM-NEXT: [[I:%.*]] = load i32, i32* @Flag3, align 4, !tbaa [[TBAA3]]
+; NOT_TUNIT_NPM-NEXT: ret i32 [[I]]
+;
+; IS__TUNIT_NPM: Function Attrs: nofree norecurse nosync nounwind willreturn
+; IS__TUNIT_NPM-LABEL: define {{[^@]+}}@static_global_not_simplifiable_3
+; IS__TUNIT_NPM-SAME: (i1 [[C:%.*]], i32* nocapture nofree writeonly [[P:%.*]]) #[[ATTR3]] {
+; IS__TUNIT_NPM-NEXT: [[SEL:%.*]] = select i1 [[C]], i32* @Flag3, i32* [[P]]
+; IS__TUNIT_NPM-NEXT: store i32 1, i32* [[SEL]], align 4, !tbaa [[TBAA3]]
+; IS__TUNIT_NPM-NEXT: [[I:%.*]] = load i32, i32* @Flag3, align 4, !tbaa [[TBAA3]]
+; IS__TUNIT_NPM-NEXT: ret i32 [[I]]
+;
+ %sel = select i1 %c, i32* @Flag3, i32* %p
+ store i32 1, i32* %sel, align 4, !tbaa !3
+ %i = load i32, i32* @Flag3, align 4, !tbaa !3
+ ret i32 %i
+}
+
+
; int write_read{,_static,_static_undef}_global(void) {
; Gint{,static,_static_undef}1 = 7;
; return Gint1;
;
; IS__CGSCC_NPM: Function Attrs: nofree norecurse nosync nounwind willreturn writeonly
; IS__CGSCC_NPM-LABEL: define {{[^@]+}}@write_global
-; IS__CGSCC_NPM-SAME: () #[[ATTR7:[0-9]+]] {
+; IS__CGSCC_NPM-SAME: () #[[ATTR6]] {
; IS__CGSCC_NPM-NEXT: store i32 7, i32* @Gint2, align 4
; IS__CGSCC_NPM-NEXT: ret void
;
; IS__TUNIT_OPM-NEXT: [[L:%.*]] = load i32, i32* @Gint2, align 4
; IS__TUNIT_OPM-NEXT: ret i32 [[L]]
;
-; IS________NPM: Function Attrs: nofree norecurse nosync nounwind readonly willreturn
-; IS________NPM-LABEL: define {{[^@]+}}@read_global
-; IS________NPM-SAME: () #[[ATTR6]] {
-; IS________NPM-NEXT: [[L:%.*]] = load i32, i32* @Gint2, align 4
-; IS________NPM-NEXT: ret i32 [[L]]
+; IS__TUNIT_NPM: Function Attrs: nofree norecurse nosync nounwind readonly willreturn
+; IS__TUNIT_NPM-LABEL: define {{[^@]+}}@read_global
+; IS__TUNIT_NPM-SAME: () #[[ATTR6]] {
+; IS__TUNIT_NPM-NEXT: [[L:%.*]] = load i32, i32* @Gint2, align 4
+; IS__TUNIT_NPM-NEXT: ret i32 [[L]]
;
-; IS__CGSCC_OPM: Function Attrs: nofree norecurse nosync nounwind readonly willreturn
-; IS__CGSCC_OPM-LABEL: define {{[^@]+}}@read_global
-; IS__CGSCC_OPM-SAME: () #[[ATTR7]] {
-; IS__CGSCC_OPM-NEXT: [[L:%.*]] = load i32, i32* @Gint2, align 4
-; IS__CGSCC_OPM-NEXT: ret i32 [[L]]
+; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readonly willreturn
+; IS__CGSCC____-LABEL: define {{[^@]+}}@read_global
+; IS__CGSCC____-SAME: () #[[ATTR7]] {
+; IS__CGSCC____-NEXT: [[L:%.*]] = load i32, i32* @Gint2, align 4
+; IS__CGSCC____-NEXT: ret i32 [[L]]
;
%l = load i32, i32* @Gint2
ret i32 %l
}
-; FIXME: We could replace these loads.
define i32 @write_read_static_global() {
-; NOT_TUNIT_NPM: Function Attrs: nofree norecurse nosync nounwind willreturn
-; NOT_TUNIT_NPM-LABEL: define {{[^@]+}}@write_read_static_global
-; NOT_TUNIT_NPM-SAME: () #[[ATTR5]] {
-; NOT_TUNIT_NPM-NEXT: store i32 7, i32* @Gstatic_int1, align 4
-; NOT_TUNIT_NPM-NEXT: [[L:%.*]] = load i32, i32* @Gstatic_int1, align 4
-; NOT_TUNIT_NPM-NEXT: ret i32 [[L]]
+; IS________OPM: Function Attrs: nofree norecurse nosync nounwind willreturn
+; IS________OPM-LABEL: define {{[^@]+}}@write_read_static_global
+; IS________OPM-SAME: () #[[ATTR5]] {
+; IS________OPM-NEXT: store i32 7, i32* @Gstatic_int1, align 4
+; IS________OPM-NEXT: [[L:%.*]] = load i32, i32* @Gstatic_int1, align 4
+; IS________OPM-NEXT: ret i32 [[L]]
;
-; IS__TUNIT_NPM: Function Attrs: nofree norecurse nosync nounwind willreturn
+; IS__TUNIT_NPM: Function Attrs: nofree norecurse nosync nounwind willreturn writeonly
; IS__TUNIT_NPM-LABEL: define {{[^@]+}}@write_read_static_global
-; IS__TUNIT_NPM-SAME: () #[[ATTR3]] {
-; IS__TUNIT_NPM-NEXT: store i32 7, i32* @Gstatic_int1, align 4
-; IS__TUNIT_NPM-NEXT: [[L:%.*]] = load i32, i32* @Gstatic_int1, align 4
-; IS__TUNIT_NPM-NEXT: ret i32 [[L]]
+; IS__TUNIT_NPM-SAME: () #[[ATTR5]] {
+; IS__TUNIT_NPM-NEXT: ret i32 7
+;
+; IS__CGSCC_NPM: Function Attrs: nofree norecurse nosync nounwind willreturn writeonly
+; IS__CGSCC_NPM-LABEL: define {{[^@]+}}@write_read_static_global
+; IS__CGSCC_NPM-SAME: () #[[ATTR6]] {
+; IS__CGSCC_NPM-NEXT: ret i32 7
;
store i32 7, i32* @Gstatic_int1
%l = load i32, i32* @Gstatic_int1
;
; IS__CGSCC_NPM: Function Attrs: nofree norecurse nosync nounwind willreturn writeonly
; IS__CGSCC_NPM-LABEL: define {{[^@]+}}@write_static_global
-; IS__CGSCC_NPM-SAME: () #[[ATTR7]] {
+; IS__CGSCC_NPM-SAME: () #[[ATTR6]] {
; IS__CGSCC_NPM-NEXT: store i32 7, i32* @Gstatic_int2, align 4
; IS__CGSCC_NPM-NEXT: ret void
;
; IS__TUNIT_OPM-NEXT: [[L:%.*]] = load i32, i32* @Gstatic_int2, align 4
; IS__TUNIT_OPM-NEXT: ret i32 [[L]]
;
-; IS________NPM: Function Attrs: nofree norecurse nosync nounwind readonly willreturn
-; IS________NPM-LABEL: define {{[^@]+}}@read_static_global
-; IS________NPM-SAME: () #[[ATTR6]] {
-; IS________NPM-NEXT: [[L:%.*]] = load i32, i32* @Gstatic_int2, align 4
-; IS________NPM-NEXT: ret i32 [[L]]
+; IS__TUNIT_NPM: Function Attrs: nofree norecurse nosync nounwind readonly willreturn
+; IS__TUNIT_NPM-LABEL: define {{[^@]+}}@read_static_global
+; IS__TUNIT_NPM-SAME: () #[[ATTR6]] {
+; IS__TUNIT_NPM-NEXT: [[L:%.*]] = load i32, i32* @Gstatic_int2, align 4
+; IS__TUNIT_NPM-NEXT: ret i32 [[L]]
;
-; IS__CGSCC_OPM: Function Attrs: nofree norecurse nosync nounwind readonly willreturn
-; IS__CGSCC_OPM-LABEL: define {{[^@]+}}@read_static_global
-; IS__CGSCC_OPM-SAME: () #[[ATTR7]] {
-; IS__CGSCC_OPM-NEXT: [[L:%.*]] = load i32, i32* @Gstatic_int2, align 4
-; IS__CGSCC_OPM-NEXT: ret i32 [[L]]
+; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readonly willreturn
+; IS__CGSCC____-LABEL: define {{[^@]+}}@read_static_global
+; IS__CGSCC____-SAME: () #[[ATTR7]] {
+; IS__CGSCC____-NEXT: [[L:%.*]] = load i32, i32* @Gstatic_int2, align 4
+; IS__CGSCC____-NEXT: ret i32 [[L]]
;
%l = load i32, i32* @Gstatic_int2
ret i32 %l
;
; IS__CGSCC_NPM: Function Attrs: nofree norecurse nosync nounwind willreturn writeonly
; IS__CGSCC_NPM-LABEL: define {{[^@]+}}@write_read_static_undef_global
-; IS__CGSCC_NPM-SAME: () #[[ATTR7]] {
+; IS__CGSCC_NPM-SAME: () #[[ATTR6]] {
; IS__CGSCC_NPM-NEXT: ret i32 7
;
store i32 7, i32* @Gstatic_undef_int1
;
; IS__CGSCC_NPM: Function Attrs: nofree norecurse nosync nounwind willreturn writeonly
; IS__CGSCC_NPM-LABEL: define {{[^@]+}}@write_static_undef_global
-; IS__CGSCC_NPM-SAME: () #[[ATTR7]] {
+; IS__CGSCC_NPM-SAME: () #[[ATTR6]] {
; IS__CGSCC_NPM-NEXT: store i32 7, i32* @Gstatic_undef_int2, align 4
; IS__CGSCC_NPM-NEXT: ret void
;
; IS__TUNIT_OPM-NEXT: ret i32 [[TMP1]]
;
; IS________NPM-LABEL: define {{[^@]+}}@round_trip_calloc
-; IS________NPM-SAME: (i32 [[X:%.*]]) {
+; IS________NPM-SAME: (i32 returned [[X:%.*]]) {
; IS________NPM-NEXT: entry:
; IS________NPM-NEXT: [[CALL_H2S:%.*]] = alloca i8, i64 4, align 1
; IS________NPM-NEXT: call void @llvm.memset.p0i8.i64(i8* [[CALL_H2S]], i8 0, i64 4, i1 false)
; IS________NPM-NEXT: [[TMP0:%.*]] = bitcast i8* [[CALL_H2S]] to i32*
; IS________NPM-NEXT: store i32 [[X]], i32* [[TMP0]], align 4
-; IS________NPM-NEXT: [[TMP1:%.*]] = load i32, i32* [[TMP0]], align 4
-; IS________NPM-NEXT: ret i32 [[TMP1]]
+; IS________NPM-NEXT: ret i32 [[X]]
;
; IS__CGSCC_OPM-LABEL: define {{[^@]+}}@round_trip_calloc
; IS__CGSCC_OPM-SAME: (i32 [[X:%.*]]) {
; IS________NPM-NEXT: entry:
; IS________NPM-NEXT: [[CALL_H2S:%.*]] = alloca i8, i64 4, align 1
; IS________NPM-NEXT: call void @llvm.memset.p0i8.i64(i8* [[CALL_H2S]], i8 0, i64 4, i1 false)
-; IS________NPM-NEXT: [[TMP0:%.*]] = bitcast i8* [[CALL_H2S]] to i32*
-; IS________NPM-NEXT: store i32 11, i32* [[TMP0]], align 4
-; IS________NPM-NEXT: [[TMP1:%.*]] = load i32, i32* [[TMP0]], align 4
-; IS________NPM-NEXT: ret i32 [[TMP1]]
+; IS________NPM-NEXT: ret i32 11
;
; IS__CGSCC_OPM-LABEL: define {{[^@]+}}@round_trip_calloc_constant() {
; IS__CGSCC_OPM-NEXT: entry:
; IS__CGSCC_NPM-NEXT: [[TMP1:%.*]] = bitcast i8* [[SRC2]] to double**
; IS__CGSCC_NPM-NEXT: store double* [[SRC]], double** [[TMP1]], align 8
; IS__CGSCC_NPM-NEXT: store i8* [[CALL]], i8** bitcast (%struct.STy** getelementptr inbounds ([[STRUCT_STY]], %struct.STy* @global, i64 0, i32 2) to i8**), align 8
-; IS__CGSCC_NPM-NEXT: call fastcc void @nested_memory_callee(float* nofree nonnull align 4294967296 undef, double* nofree nonnull align 4294967296 undef, %struct.STy* nofree nonnull align 8 @global) #[[ATTR17:[0-9]+]]
+; IS__CGSCC_NPM-NEXT: call fastcc void @nested_memory_callee(float* nofree nonnull align 4294967296 undef, double* nofree nonnull align 4294967296 undef, %struct.STy* nofree noundef nonnull align 8 dereferenceable(24) @global) #[[ATTR17:[0-9]+]]
; IS__CGSCC_NPM-NEXT: ret void
;
entry:
; IS__CGSCC_NPM: attributes #[[ATTR3]] = { nofree nosync nounwind willreturn }
; IS__CGSCC_NPM: attributes #[[ATTR4]] = { nofree norecurse nosync nounwind readnone willreturn }
; IS__CGSCC_NPM: attributes #[[ATTR5]] = { nofree norecurse nosync nounwind willreturn }
-; IS__CGSCC_NPM: attributes #[[ATTR6]] = { nofree norecurse nosync nounwind readonly willreturn }
-; IS__CGSCC_NPM: attributes #[[ATTR7]] = { nofree norecurse nosync nounwind willreturn writeonly }
+; IS__CGSCC_NPM: attributes #[[ATTR6]] = { nofree norecurse nosync nounwind willreturn writeonly }
+; IS__CGSCC_NPM: attributes #[[ATTR7]] = { nofree norecurse nosync nounwind readonly willreturn }
; IS__CGSCC_NPM: attributes #[[ATTR8]] = { nofree norecurse nosync nounwind writeonly }
; IS__CGSCC_NPM: attributes #[[ATTR9]] = { nofree norecurse nosync nounwind willreturn uwtable }
; IS__CGSCC_NPM: attributes #[[ATTR10]] = { argmemonly nofree norecurse nosync nounwind willreturn }
;
; Check the original function, which is wrapped and becomes anonymous
; CHECK: Function Attrs: nofree norecurse nosync nounwind readnone willreturn
-; CHECK: define internal i32 @0()
+; CHECK: define internal noundef i32 @0()
; CHECK: ret i32 1
define linkonce i32 @inner1() {
entry: