combineOptionalValuesInAAValueLatice(const Optional<Value *> &A,
const Optional<Value *> &B, Type *Ty);
+/// Helper to represent an access offset and size, with logic to deal with
+/// uncertainty and check for overlapping accesses.
+struct OffsetAndSize : public std::pair<int64_t, int64_t> {
+ using BaseTy = std::pair<int64_t, int64_t>;
+ OffsetAndSize(int64_t Offset, int64_t Size) : BaseTy(Offset, Size) {}
+ OffsetAndSize(const BaseTy &P) : BaseTy(P) {}
+ int64_t getOffset() const { return first; }
+ int64_t getSize() const { return second; }
+ static OffsetAndSize getUnknown() { return OffsetAndSize(Unknown, Unknown); }
+
+ /// Return true if offset or size are unknown.
+ bool offsetOrSizeAreUnknown() const {
+ return getOffset() == OffsetAndSize::Unknown ||
+ getSize() == OffsetAndSize::Unknown;
+ }
+
+ /// Return true if offset and size are unknown, thus this is the default
+ /// unknown object.
+ bool offsetAndSizeAreUnknown() const {
+ return getOffset() == OffsetAndSize::Unknown &&
+ getSize() == OffsetAndSize::Unknown;
+ }
+
+ /// Return true if this offset and size pair might describe an address that
+ /// overlaps with \p OAS.
+ bool mayOverlap(const OffsetAndSize &OAS) const {
+ // Any unknown value and we are giving up -> overlap.
+ if (offsetOrSizeAreUnknown() || OAS.offsetOrSizeAreUnknown())
+ return true;
+
+ // Check if one offset point is in the other interval [offset,
+ // offset+size].
+ return OAS.getOffset() + OAS.getSize() > getOffset() &&
+ OAS.getOffset() < getOffset() + getSize();
+ }
+
+ /// Constant used to represent unknown offset or sizes.
+ static constexpr int64_t Unknown = 1 << 31;
+};
+
/// Return the initial value of \p Obj with type \p Ty if that is a constant.
Constant *getInitialValueForObj(Value &Obj, Type &Ty,
- const TargetLibraryInfo *TLI);
+ const TargetLibraryInfo *TLI,
+ const DataLayout &DL,
+ OffsetAndSize *OASPtr = nullptr);
/// Collect all potential underlying objects of \p Ptr at position \p CtxI in
/// \p Objects. Assumed information is used and dependences onto \p QueryingAA
/// See AbstractAttribute::getIdAddr()
const char *getIdAddr() const override { return &ID; }
- /// Helper to represent an access offset and size, with logic to deal with
- /// uncertainty and check for overlapping accesses.
- struct OffsetAndSize : public std::pair<int64_t, int64_t> {
- using BaseTy = std::pair<int64_t, int64_t>;
- OffsetAndSize(int64_t Offset, int64_t Size) : BaseTy(Offset, Size) {}
- OffsetAndSize(const BaseTy &P) : BaseTy(P) {}
- int64_t getOffset() const { return first; }
- int64_t getSize() const { return second; }
- static OffsetAndSize getUnknown() {
- return OffsetAndSize(Unknown, Unknown);
- }
-
- /// Return true if offset or size are unknown.
- bool offsetOrSizeAreUnknown() const {
- return getOffset() == OffsetAndSize::Unknown ||
- getSize() == OffsetAndSize::Unknown;
- }
-
- /// Return true if this offset and size pair might describe an address that
- /// overlaps with \p OAS.
- bool mayOverlap(const OffsetAndSize &OAS) const {
- // Any unknown value and we are giving up -> overlap.
- if (offsetOrSizeAreUnknown() || OAS.offsetOrSizeAreUnknown())
- return true;
-
- // Check if one offset point is in the other interval [offset,
- // offset+size].
- return OAS.getOffset() + OAS.getSize() > getOffset() &&
- OAS.getOffset() < getOffset() + getSize();
- }
-
- /// Constant used to represent unknown offset or sizes.
- static constexpr int64_t Unknown = 1 << 31;
- };
-
/// Call \p CB on all accesses that might interfere with \p OAS and return
/// true if all such accesses were known and the callback returned true for
/// all of them, false otherwise. An access interferes with an offset-size
/// pair if it might read or write that memory region.
virtual bool forallInterferingAccesses(
- OffsetAndSize OAS, function_ref<bool(const Access &, bool)> CB) const = 0;
+ AA::OffsetAndSize OAS,
+ function_ref<bool(const Access &, bool)> CB) const = 0;
/// Call \p CB on all accesses that might interfere with \p I and
/// return true if all such accesses were known and the callback returned true
/// 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;
+ virtual bool forallInterferingAccesses(
+ Attributor &A, const AbstractAttribute &QueryingAA, Instruction &I,
+ function_ref<bool(const Access &, bool)> CB, bool &HasBeenWrittenTo,
+ AA::OffsetAndSize *OASPtr = nullptr) const = 0;
/// This function should return true if the type of the \p AA is AAPointerInfo
static bool classof(const AbstractAttribute *AA) {
bool funcRetrievesMultigridSyncArg(Attributor &A) {
auto Pos = llvm::AMDGPU::getMultigridSyncArgImplicitArgPosition();
- AAPointerInfo::OffsetAndSize OAS(Pos, 8);
+ AA::OffsetAndSize OAS(Pos, 8);
return funcRetrievesImplicitKernelArg(A, OAS);
}
bool funcRetrievesHostcallPtr(Attributor &A) {
auto Pos = llvm::AMDGPU::getHostcallImplicitArgPosition();
- AAPointerInfo::OffsetAndSize OAS(Pos, 8);
+ AA::OffsetAndSize OAS(Pos, 8);
return funcRetrievesImplicitKernelArg(A, OAS);
}
bool funcRetrievesHeapPtr(Attributor &A) {
if (AMDGPU::getAmdhsaCodeObjectVersion() != 5)
return false;
- AAPointerInfo::OffsetAndSize OAS(AMDGPU::ImplicitArg::HEAP_PTR_OFFSET, 8);
+ AA::OffsetAndSize OAS(AMDGPU::ImplicitArg::HEAP_PTR_OFFSET, 8);
return funcRetrievesImplicitKernelArg(A, OAS);
}
bool funcRetrievesQueuePtr(Attributor &A) {
if (AMDGPU::getAmdhsaCodeObjectVersion() != 5)
return false;
- AAPointerInfo::OffsetAndSize OAS(AMDGPU::ImplicitArg::QUEUE_PTR_OFFSET, 8);
+ AA::OffsetAndSize OAS(AMDGPU::ImplicitArg::QUEUE_PTR_OFFSET, 8);
return funcRetrievesImplicitKernelArg(A, OAS);
}
bool funcRetrievesImplicitKernelArg(Attributor &A,
- AAPointerInfo::OffsetAndSize OAS) {
+ AA::OffsetAndSize OAS) {
// Check if this is a call to the implicitarg_ptr builtin and it
// is used to retrieve the hostcall pointer. The implicit arg for
// hostcall is not used only if every use of the implicitarg_ptr
#include "llvm/Analysis/MustExecute.h"
#include "llvm/IR/Attributes.h"
#include "llvm/IR/Constant.h"
+#include "llvm/IR/ConstantFold.h"
#include "llvm/IR/Constants.h"
+#include "llvm/IR/DataLayout.h"
#include "llvm/IR/GlobalValue.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/Instruction.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include "llvm/Transforms/Utils/Cloning.h"
#include "llvm/Transforms/Utils/Local.h"
+#include <cstdint>
#ifdef EXPENSIVE_CHECKS
#include "llvm/IR/Verifier.h"
}
Constant *AA::getInitialValueForObj(Value &Obj, Type &Ty,
- const TargetLibraryInfo *TLI) {
+ const TargetLibraryInfo *TLI,
+ const DataLayout &DL,
+ AA::OffsetAndSize *OASPtr) {
if (isa<AllocaInst>(Obj))
return UndefValue::get(&Ty);
if (Constant *Init = getInitialValueOfAllocation(&Obj, TLI, &Ty))
return nullptr;
if (!GV->hasInitializer())
return UndefValue::get(&Ty);
- return dyn_cast_or_null<Constant>(getWithType(*GV->getInitializer(), Ty));
+
+ // Handle constant initializers by extracting the relevant parts for
+ // aggregates.
+ Constant *C = GV->getInitializer();
+ if (OASPtr && !OASPtr->offsetOrSizeAreUnknown() &&
+ isa<ConstantAggregate>(C)) {
+ Type *CTy = C->getType();
+ APInt Offset = APInt(64, OASPtr->getOffset());
+ Optional<APInt> Idx = DL.getGEPIndexForOffset(CTy, Offset);
+ // Check if the indexing worked out properly.
+ // TODO: Handle partial accesses, e.g., Offset is > 0 or Size < CTy.size().
+ if (Idx && Offset.isZero() &&
+ DL.getTypeSizeInBits(CTy) == uint64_t(OASPtr->getSize() * 8)) {
+ if (auto *Folded =
+ ConstantFoldExtractValueInstruction(C, Idx->getZExtValue()))
+ C = Folded;
+ }
+ }
+ return dyn_cast_or_null<Constant>(getWithType(*C, Ty));
}
bool AA::isValidInScope(const Value &V, const Function *Scope) {
// object.
bool HasBeenWrittenTo = false;
+ AA::OffsetAndSize OAS = AA::OffsetAndSize::getUnknown();
auto &PI = A.getAAFor<AAPointerInfo>(QueryingAA, IRPosition::value(*Obj),
DepClassTy::NONE);
if (!PI.forallInterferingAccesses(A, QueryingAA, I, CheckAccess,
- HasBeenWrittenTo)) {
+ HasBeenWrittenTo, &OAS)) {
LLVM_DEBUG(
dbgs()
<< "Failed to verify all interfering accesses for underlying object: "
return false;
}
- if (IsLoad && !HasBeenWrittenTo) {
- Value *InitialValue = AA::getInitialValueForObj(*Obj, *I.getType(), TLI);
- if (!InitialValue)
+ if (IsLoad && !HasBeenWrittenTo && !OAS.offsetAndSizeAreUnknown()) {
+ const DataLayout &DL = A.getDataLayout();
+ Value *InitialValue =
+ AA::getInitialValueForObj(*Obj, *I.getType(), TLI, DL, &OAS);
+ if (!InitialValue) {
+ LLVM_DEBUG(dbgs() << "Could not determine required initial value of "
+ "underlying object, abort!\n");
return false;
+ }
CheckForNullOnlyAndUndef(InitialValue, /* IsExact */ true);
if (NullRequired && !NullOnly) {
LLVM_DEBUG(dbgs() << "Non exact access but initial value that is not "
// Check if the current instruction is already known to reach the ToFn.
const auto &FnReachabilityAA = A.getAAFor<AAFunctionReachability>(
QueryingAA, IRPosition::function(*FromFn), DepClassTy::OPTIONAL);
- bool Result = FnReachabilityAA.instructionCanReach(
- A, *CurFromI, ToFn);
+ bool Result = FnReachabilityAA.instructionCanReach(A, *CurFromI, ToFn);
LLVM_DEBUG(dbgs() << "[AA] " << *CurFromI << " in @" << FromFn->getName()
<< " " << (Result ? "can potentially " : "cannot ")
<< "reach @" << ToFn.getName() << " [FromFn]\n");
/// Helper that allows OffsetAndSize as a key in a DenseMap.
template <>
-struct DenseMapInfo<AAPointerInfo ::OffsetAndSize>
+struct DenseMapInfo<AA::OffsetAndSize>
: DenseMapInfo<std::pair<int64_t, int64_t>> {};
/// Helper for AA::PointerInfo::Access DenseMap/Set usage ignoring everythign
};
/// We store all accesses in bins denoted by their offset and size.
- using AccessBinsTy = DenseMap<AAPointerInfo::OffsetAndSize, Accesses *>;
+ using AccessBinsTy = DenseMap<AA::OffsetAndSize, Accesses *>;
AccessBinsTy::const_iterator begin() const { return AccessBins.begin(); }
AccessBinsTy::const_iterator end() const { return AccessBins.end(); }
AAPointerInfo::AccessKind Kind, Type *Ty,
Instruction *RemoteI = nullptr,
Accesses *BinPtr = nullptr) {
- AAPointerInfo::OffsetAndSize Key{Offset, Size};
+ AA::OffsetAndSize Key{Offset, Size};
Accesses *&Bin = BinPtr ? BinPtr : AccessBins[Key];
if (!Bin)
Bin = new (A.Allocator) Accesses;
/// See AAPointerInfo::forallInterferingAccesses.
bool forallInterferingAccesses(
- AAPointerInfo::OffsetAndSize OAS,
+ AA::OffsetAndSize OAS,
function_ref<bool(const AAPointerInfo::Access &, bool)> CB) const {
if (!isValidState())
return false;
for (const auto &It : AccessBins) {
- AAPointerInfo::OffsetAndSize ItOAS = It.getFirst();
+ AA::OffsetAndSize ItOAS = It.getFirst();
if (!OAS.mayOverlap(ItOAS))
continue;
bool IsExact = OAS == ItOAS && !OAS.offsetOrSizeAreUnknown();
/// See AAPointerInfo::forallInterferingAccesses.
bool forallInterferingAccesses(
Instruction &I,
- function_ref<bool(const AAPointerInfo::Access &, bool)> CB) const {
+ function_ref<bool(const AAPointerInfo::Access &, bool)> CB,
+ AA::OffsetAndSize *OASPtr) const {
if (!isValidState())
return false;
// First find the offset and size of I.
- AAPointerInfo::OffsetAndSize OAS(-1, -1);
+ AA::OffsetAndSize OAS = AA::OffsetAndSize::getUnknown();
for (const auto &It : AccessBins) {
for (auto &Access : *It.getSecond()) {
if (Access.getRemoteInst() == &I) {
break;
}
}
- if (OAS.getSize() != -1)
+ if (OAS.getSize() != AA::OffsetAndSize::Unknown)
break;
}
+
+ if (OASPtr)
+ *OASPtr = OAS;
+
// No access for I was found, we are done.
- if (OAS.getSize() == -1)
+ if (OAS.getSize() == AA::OffsetAndSize::Unknown)
return true;
// Now that we have an offset and size, find all overlapping ones and use
}
bool forallInterferingAccesses(
- OffsetAndSize OAS,
+ AA::OffsetAndSize OAS,
function_ref<bool(const AAPointerInfo::Access &, bool)> CB)
const override {
return State::forallInterferingAccesses(OAS, CB);
}
- bool
- forallInterferingAccesses(Attributor &A, const AbstractAttribute &QueryingAA,
- Instruction &I,
- function_ref<bool(const Access &, bool)> UserCB,
- bool &HasBeenWrittenTo) const override {
+ bool forallInterferingAccesses(
+ Attributor &A, const AbstractAttribute &QueryingAA, Instruction &I,
+ function_ref<bool(const Access &, bool)> UserCB, bool &HasBeenWrittenTo,
+ AA::OffsetAndSize *OASPtr = nullptr) const override {
HasBeenWrittenTo = false;
SmallPtrSet<const Access *, 8> DominatingWrites;
InterferingAccesses.push_back({&Acc, Exact});
return true;
};
- if (!State::forallInterferingAccesses(I, AccessCB))
+ if (!State::forallInterferingAccesses(I, AccessCB, OASPtr))
return false;
if (HasBeenWrittenTo) {
// Combine the accesses bin by bin.
ChangeStatus Changed = ChangeStatus::UNCHANGED;
for (const auto &It : OtherAAImpl.getState()) {
- OffsetAndSize OAS = OffsetAndSize::getUnknown();
- if (Offset != OffsetAndSize::Unknown)
- OAS = OffsetAndSize(It.first.getOffset() + Offset, It.first.getSize());
+ AA::OffsetAndSize OAS = AA::OffsetAndSize::getUnknown();
+ if (Offset != AA::OffsetAndSize::Unknown)
+ OAS = AA::OffsetAndSize(It.first.getOffset() + Offset,
+ It.first.getSize());
Accesses *Bin = AccessBins.lookup(OAS);
for (const AAPointerInfo::Access &RAcc : *It.second) {
if (IsByval && !RAcc.isRead())
bool handleAccess(Attributor &A, Instruction &I, Value &Ptr,
Optional<Value *> Content, AccessKind Kind, int64_t Offset,
ChangeStatus &Changed, Type *Ty,
- int64_t Size = OffsetAndSize::Unknown) {
+ int64_t Size = AA::OffsetAndSize::Unknown) {
using namespace AA::PointerInfo;
- // No need to find a size if one is given or the offset is unknown.
- if (Offset != OffsetAndSize::Unknown && Size == OffsetAndSize::Unknown &&
- Ty) {
+ // No need to find a size if one is given.
+ if (Size == AA::OffsetAndSize::Unknown && Ty) {
const DataLayout &DL = A.getDataLayout();
TypeSize AccessSize = DL.getTypeStoreSize(Ty);
if (!AccessSize.isScalable())
/// Helper struct, will support ranges eventually.
struct OffsetInfo {
- int64_t Offset = OffsetAndSize::Unknown;
+ int64_t Offset = AA::OffsetAndSize::Unknown;
bool operator==(const OffsetInfo &OI) const { return Offset == OI.Offset; }
};
// TODO: Use range information.
APInt GEPOffset(DL.getIndexTypeSizeInBits(GEP->getType()), 0);
- if (PtrOI.Offset == OffsetAndSize::Unknown ||
+ if (PtrOI.Offset == AA::OffsetAndSize::Unknown ||
!GEP->accumulateConstantOffset(DL, GEPOffset)) {
- UsrOI.Offset = OffsetAndSize::Unknown;
+ UsrOI.Offset = AA::OffsetAndSize::Unknown;
Follow = true;
return true;
}
// Check if the PHI operand has already an unknown offset as we can't
// improve on that anymore.
- if (PtrOI.Offset == OffsetAndSize::Unknown) {
+ if (PtrOI.Offset == AA::OffsetAndSize::Unknown) {
UsrOI = PtrOI;
Follow = true;
return true;
// TODO: Approximate in case we know the direction of the recurrence.
UsrOI = PtrOI;
- UsrOI.Offset = OffsetAndSize::Unknown;
+ UsrOI.Offset = AA::OffsetAndSize::Unknown;
Follow = true;
return true;
}
// accessed.
if (auto *MI = dyn_cast_or_null<MemIntrinsic>(getCtxI())) {
ConstantInt *Length = dyn_cast<ConstantInt>(MI->getLength());
- int64_t LengthVal = OffsetAndSize::Unknown;
+ int64_t LengthVal = AA::OffsetAndSize::Unknown;
if (Length)
LengthVal = Length->getSExtValue();
Value &Ptr = getAssociatedValue();
; 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=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 -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-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
; CHECK: @[[G:[a-zA-Z0-9_$"\\.-]+]] = constant [[T:%.*]] { i32 0, i32 0, i32 17, i32 25 }
;.
define internal i32 @test(%T* %p) {
-; CHECK: Function Attrs: nofree norecurse nosync nounwind readnone willreturn
-; CHECK-LABEL: define {{[^@]+}}@test
-; CHECK-SAME: () #[[ATTR0:[0-9]+]] {
-; CHECK-NEXT: entry:
-; CHECK-NEXT: [[A:%.*]] = load i32, i32* getelementptr inbounds ([[T:%.*]], %T* @G, i64 0, i32 3), align 4
-; CHECK-NEXT: [[B:%.*]] = load i32, i32* getelementptr inbounds ([[T]], %T* @G, i64 0, i32 2), align 8
-; CHECK-NEXT: [[V:%.*]] = add i32 [[A]], [[B]]
-; CHECK-NEXT: ret i32 [[V]]
+; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn
+; IS__CGSCC____-LABEL: define {{[^@]+}}@test
+; IS__CGSCC____-SAME: () #[[ATTR0:[0-9]+]] {
+; IS__CGSCC____-NEXT: entry:
+; IS__CGSCC____-NEXT: ret i32 42
;
entry:
%a.gep = getelementptr %T, %T* %p, i64 0, i32 3
define i32 @caller() {
; IS__TUNIT____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn
; IS__TUNIT____-LABEL: define {{[^@]+}}@caller
-; IS__TUNIT____-SAME: () #[[ATTR0]] {
+; IS__TUNIT____-SAME: () #[[ATTR0:[0-9]+]] {
; IS__TUNIT____-NEXT: entry:
-; IS__TUNIT____-NEXT: [[V:%.*]] = call i32 @test() #[[ATTR1:[0-9]+]]
-; IS__TUNIT____-NEXT: ret i32 [[V]]
+; IS__TUNIT____-NEXT: ret i32 42
;
; IS__CGSCC____: Function Attrs: nofree nosync nounwind readnone willreturn
; IS__CGSCC____-LABEL: define {{[^@]+}}@caller
; IS__CGSCC____-SAME: () #[[ATTR1:[0-9]+]] {
; IS__CGSCC____-NEXT: entry:
-; IS__CGSCC____-NEXT: [[V:%.*]] = call i32 @test() #[[ATTR2:[0-9]+]]
+; IS__CGSCC____-NEXT: [[V:%.*]] = call noundef i32 @test() #[[ATTR2:[0-9]+]]
; IS__CGSCC____-NEXT: ret i32 [[V]]
;
entry:
}
;.
; IS__TUNIT____: attributes #[[ATTR0]] = { nofree norecurse nosync nounwind readnone willreturn }
-; IS__TUNIT____: attributes #[[ATTR1]] = { nofree nosync nounwind readnone willreturn }
;.
; IS__CGSCC____: attributes #[[ATTR0]] = { nofree norecurse nosync nounwind readnone willreturn }
; IS__CGSCC____: attributes #[[ATTR1]] = { nofree nosync nounwind readnone willreturn }
; NOT_CGSCC_NPM-NEXT: entry:
; NOT_CGSCC_NPM-NEXT: unreachable
;
-; IS__CGSCC____: Function Attrs: nofree nosync nounwind readonly willreturn
+; IS__CGSCC____: Function Attrs: nofree nosync nounwind readnone willreturn
; IS__CGSCC____-LABEL: define {{[^@]+}}@run
; IS__CGSCC____-SAME: () #[[ATTR0:[0-9]+]] {
; IS__CGSCC____-NEXT: entry:
-; IS__CGSCC____-NEXT: [[TMP0:%.*]] = load i32, i32* getelementptr inbounds ([[STRUCT_FOO:%.*]], %struct.Foo* @a, i32 0, i32 0), align 8
-; IS__CGSCC____-NEXT: [[A_0_1:%.*]] = getelementptr [[STRUCT_FOO]], %struct.Foo* @a, i64 0, i32 1
-; IS__CGSCC____-NEXT: [[TMP1:%.*]] = load i64, i64* [[A_0_1]], align 8
; IS__CGSCC____-NEXT: unreachable
;
entry:
;.
; NOT_CGSCC_NPM: attributes #[[ATTR0]] = { nofree norecurse nosync nounwind readnone willreturn }
;.
-; IS__CGSCC____: attributes #[[ATTR0]] = { nofree nosync nounwind readonly willreturn }
+; IS__CGSCC____: attributes #[[ATTR0]] = { nofree nosync nounwind readnone willreturn }
; IS__CGSCC____: attributes #[[ATTR1]] = { nofree norecurse nosync nounwind readnone willreturn }
; IS__CGSCC____: attributes #[[ATTR2]] = { nofree norecurse noreturn nosync nounwind readnone }
;.
; 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=15 -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=15 -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=16 -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=16 -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__TUNIT_NPM: omp.inner.for.body:
; IS__TUNIT_NPM-NEXT: [[ADD10:%.*]] = add nsw i32 [[DOTOMP_IV_0]], 2
; IS__TUNIT_NPM-NEXT: [[TMP11:%.*]] = load double, double* [[CONV]], align 8
-; IS__TUNIT_NPM-NEXT: call void @bar(i32 [[ADD10]], float 3.000000e+00, double [[TMP11]])
+; IS__TUNIT_NPM-NEXT: call void @bar(i32 [[ADD10]], float 3.000000e+00, double noundef [[TMP11]])
; IS__TUNIT_NPM-NEXT: br label [[OMP_BODY_CONTINUE:%.*]]
; IS__TUNIT_NPM: omp.body.continue:
; IS__TUNIT_NPM-NEXT: br label [[OMP_INNER_FOR_INC]]
; IS__CGSCC_NPM-NEXT: [[ADD10:%.*]] = add nsw i32 [[DOTOMP_IV_0]], 2
; IS__CGSCC_NPM-NEXT: [[TMP10:%.*]] = load float, float* [[P]], align 4
; IS__CGSCC_NPM-NEXT: [[TMP11:%.*]] = load double, double* [[CONV]], align 8
-; IS__CGSCC_NPM-NEXT: call void @bar(i32 [[ADD10]], float [[TMP10]], double [[TMP11]])
+; IS__CGSCC_NPM-NEXT: call void @bar(i32 [[ADD10]], float [[TMP10]], double noundef [[TMP11]])
; IS__CGSCC_NPM-NEXT: br label [[OMP_BODY_CONTINUE:%.*]]
; IS__CGSCC_NPM: omp.body.continue:
; IS__CGSCC_NPM-NEXT: br label [[OMP_INNER_FOR_INC]]
--- /dev/null
+; 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=9 -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=9 -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
+;
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+
+%struct.S = type { i32, double, ptr }
+
+; struct S {
+; int a;
+; double b;
+; struct S* c;
+; };
+;
+; static const struct S GlobalS = {42, 3.14, 0};
+;
+; int testOneFieldGlobalS() {
+; int r = 0;
+; if (GlobalS.a != 42)
+; r += 1;
+; if (GlobalS.b == 3.14)
+; r += 2;
+; if (GlobalS.c)
+; r += 4;
+; return r;
+; }
+;
+@GlobalS = internal constant %struct.S { i32 42, double 3.140000e+00, ptr null }, align 8
+
+;.
+; CHECK: @[[GLOBALS:[a-zA-Z0-9_$"\\.-]+]] = internal constant [[STRUCT_S:%.*]] { i32 42, double 3.140000e+00, ptr null }, align 8
+;.
+define i32 @testOneFieldGlobalS() {
+; CHECK: Function Attrs: nofree norecurse nosync nounwind readnone willreturn
+; CHECK-LABEL: define {{[^@]+}}@testOneFieldGlobalS
+; CHECK-SAME: () #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br label [[IF_END:%.*]]
+; CHECK: if.then:
+; CHECK-NEXT: unreachable
+; CHECK: if.end:
+; CHECK-NEXT: br label [[IF_THEN2:%.*]]
+; CHECK: if.then2:
+; CHECK-NEXT: [[ADD3:%.*]] = add nsw i32 0, 2
+; CHECK-NEXT: br label [[IF_END4:%.*]]
+; CHECK: if.end4:
+; CHECK-NEXT: br label [[IF_END7:%.*]]
+; CHECK: if.then5:
+; CHECK-NEXT: unreachable
+; CHECK: if.end7:
+; CHECK-NEXT: ret i32 2
+;
+entry:
+ %i = load i32, ptr @GlobalS, align 8
+ %cmp = icmp ne i32 %i, 42
+ br i1 %cmp, label %if.then, label %if.end
+
+if.then: ; preds = %entry
+ %add = add nsw i32 0, 1
+ br label %if.end
+
+if.end: ; preds = %if.then, %entry
+ %r.0 = phi i32 [ %add, %if.then ], [ 0, %entry ]
+ %i1 = load double, ptr getelementptr inbounds (%struct.S, ptr @GlobalS, i32 0, i32 1), align 8
+ %cmp1 = fcmp oeq double %i1, 3.140000e+00
+ br i1 %cmp1, label %if.then2, label %if.end4
+
+if.then2: ; preds = %if.end
+ %add3 = add nsw i32 %r.0, 2
+ br label %if.end4
+
+if.end4: ; preds = %if.then2, %if.end
+ %r.1 = phi i32 [ %add3, %if.then2 ], [ %r.0, %if.end ]
+ %i2 = load ptr, ptr getelementptr inbounds (%struct.S, ptr @GlobalS, i32 0, i32 2), align 8
+ %tobool = icmp ne ptr %i2, null
+ br i1 %tobool, label %if.then5, label %if.end7
+
+if.then5: ; preds = %if.end4
+ %add6 = add nsw i32 %r.1, 4
+ br label %if.end7
+
+if.end7: ; preds = %if.then5, %if.end4
+ %r.2 = phi i32 [ %add6, %if.then5 ], [ %r.1, %if.end4 ]
+ ret i32 %r.2
+}
+
+define i32 @testOneFieldGlobalS_type_mismatch() {
+; CHECK: Function Attrs: nofree norecurse nosync nounwind readnone willreturn
+; CHECK-LABEL: define {{[^@]+}}@testOneFieldGlobalS_type_mismatch
+; CHECK-SAME: () #[[ATTR0]] {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[I:%.*]] = load double, ptr @GlobalS, align 8
+; CHECK-NEXT: [[IC:%.*]] = fptosi double [[I]] to i32
+; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32 [[IC]], 42
+; CHECK-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
+; CHECK: if.then:
+; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 0, 1
+; CHECK-NEXT: br label [[IF_END]]
+; CHECK: if.end:
+; CHECK-NEXT: [[R_0:%.*]] = phi i32 [ [[ADD]], [[IF_THEN]] ], [ 0, [[ENTRY:%.*]] ]
+; CHECK-NEXT: [[I1:%.*]] = load i64, ptr getelementptr inbounds ([[STRUCT_S:%.*]], ptr @GlobalS, i32 0, i32 1), align 8
+; CHECK-NEXT: [[I1C:%.*]] = sitofp i64 [[I1]] to double
+; CHECK-NEXT: [[CMP1:%.*]] = fcmp oeq double [[I1C]], 3.140000e+00
+; CHECK-NEXT: br i1 [[CMP1]], label [[IF_THEN2:%.*]], label [[IF_END4:%.*]]
+; CHECK: if.then2:
+; CHECK-NEXT: [[ADD3:%.*]] = add nsw i32 [[R_0]], 2
+; CHECK-NEXT: br label [[IF_END4]]
+; CHECK: if.end4:
+; CHECK-NEXT: [[R_1:%.*]] = phi i32 [ [[ADD3]], [[IF_THEN2]] ], [ [[R_0]], [[IF_END]] ]
+; CHECK-NEXT: br label [[IF_END7:%.*]]
+; CHECK: if.then5:
+; CHECK-NEXT: unreachable
+; CHECK: if.end7:
+; CHECK-NEXT: ret i32 [[R_1]]
+;
+entry:
+ %i = load double, ptr @GlobalS, align 8
+ %ic = fptosi double %i to i32
+ %cmp = icmp ne i32 %ic, 42
+ br i1 %cmp, label %if.then, label %if.end
+
+if.then: ; preds = %entry
+ %add = add nsw i32 0, 1
+ br label %if.end
+
+if.end: ; preds = %if.then, %entry
+ %r.0 = phi i32 [ %add, %if.then ], [ 0, %entry ]
+ %i1 = load i64, ptr getelementptr inbounds (%struct.S, ptr @GlobalS, i32 0, i32 1), align 8
+ %i1c = sitofp i64 %i1 to double
+ %cmp1 = fcmp oeq double %i1c, 3.140000e+00
+ br i1 %cmp1, label %if.then2, label %if.end4
+
+if.then2: ; preds = %if.end
+ %add3 = add nsw i32 %r.0, 2
+ br label %if.end4
+
+if.end4: ; preds = %if.then2, %if.end
+ %r.1 = phi i32 [ %add3, %if.then2 ], [ %r.0, %if.end ]
+ %i2 = load i64, ptr getelementptr inbounds (%struct.S, ptr @GlobalS, i32 0, i32 2), align 8
+ %i2c = inttoptr i64 %i2 to ptr
+ %tobool = icmp ne ptr %i2c, null
+ br i1 %tobool, label %if.then5, label %if.end7
+
+if.then5: ; preds = %if.end4
+ %add6 = add nsw i32 %r.1, 4
+ br label %if.end7
+
+if.end7: ; preds = %if.then5, %if.end4
+ %r.2 = phi i32 [ %add6, %if.then5 ], [ %r.1, %if.end4 ]
+ ret i32 %r.2
+}
+
+define i32 @testOneFieldGlobalS_byte_offset_wrong() {
+; CHECK: Function Attrs: nofree norecurse nosync nounwind readnone willreturn
+; CHECK-LABEL: define {{[^@]+}}@testOneFieldGlobalS_byte_offset_wrong
+; CHECK-SAME: () #[[ATTR0]] {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[I:%.*]] = load i32, ptr getelementptr inbounds (i32, ptr @GlobalS, i32 1), align 8
+; CHECK-NEXT: [[CMP:%.*]] = icmp ne i32 [[I]], 42
+; CHECK-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
+; CHECK: if.then:
+; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 0, 1
+; CHECK-NEXT: br label [[IF_END]]
+; CHECK: if.end:
+; CHECK-NEXT: [[R_0:%.*]] = phi i32 [ [[ADD]], [[IF_THEN]] ], [ 0, [[ENTRY:%.*]] ]
+; CHECK-NEXT: [[I1:%.*]] = load double, ptr getelementptr (double, ptr @GlobalS, i32 3), align 8
+; CHECK-NEXT: [[CMP1:%.*]] = fcmp oeq double [[I1]], 3.140000e+00
+; CHECK-NEXT: br i1 [[CMP1]], label [[IF_THEN2:%.*]], label [[IF_END4:%.*]]
+; CHECK: if.then2:
+; CHECK-NEXT: [[ADD3:%.*]] = add nsw i32 [[R_0]], 2
+; CHECK-NEXT: br label [[IF_END4]]
+; CHECK: if.end4:
+; CHECK-NEXT: [[R_1:%.*]] = phi i32 [ [[ADD3]], [[IF_THEN2]] ], [ [[R_0]], [[IF_END]] ]
+; CHECK-NEXT: [[I2:%.*]] = load ptr, ptr getelementptr (ptr, ptr @GlobalS, i32 11), align 8
+; CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[I2]], null
+; CHECK-NEXT: br i1 [[TOBOOL]], label [[IF_THEN5:%.*]], label [[IF_END7:%.*]]
+; CHECK: if.then5:
+; CHECK-NEXT: [[ADD6:%.*]] = add nsw i32 [[R_1]], 4
+; CHECK-NEXT: br label [[IF_END7]]
+; CHECK: if.end7:
+; CHECK-NEXT: [[R_2:%.*]] = phi i32 [ [[ADD6]], [[IF_THEN5]] ], [ [[R_1]], [[IF_END4]] ]
+; CHECK-NEXT: ret i32 [[R_2]]
+;
+entry:
+ %i = load i32, ptr getelementptr (i32, ptr @GlobalS, i32 1), align 8
+ %cmp = icmp ne i32 %i, 42
+ br i1 %cmp, label %if.then, label %if.end
+
+if.then: ; preds = %entry
+ %add = add nsw i32 0, 1
+ br label %if.end
+
+if.end: ; preds = %if.then, %entry
+ %r.0 = phi i32 [ %add, %if.then ], [ 0, %entry ]
+ %i1 = load double, ptr getelementptr (double, ptr @GlobalS, i32 3), align 8
+ %cmp1 = fcmp oeq double %i1, 3.140000e+00
+ br i1 %cmp1, label %if.then2, label %if.end4
+
+if.then2: ; preds = %if.end
+ %add3 = add nsw i32 %r.0, 2
+ br label %if.end4
+
+if.end4: ; preds = %if.then2, %if.end
+ %r.1 = phi i32 [ %add3, %if.then2 ], [ %r.0, %if.end ]
+ %i2 = load ptr, ptr getelementptr (ptr, ptr @GlobalS, i32 11), align 8
+ %tobool = icmp ne ptr %i2, null
+ br i1 %tobool, label %if.then5, label %if.end7
+
+if.then5: ; preds = %if.end4
+ %add6 = add nsw i32 %r.1, 4
+ br label %if.end7
+
+if.end7: ; preds = %if.then5, %if.end4
+ %r.2 = phi i32 [ %add6, %if.then5 ], [ %r.1, %if.end4 ]
+ ret i32 %r.2
+}
+;.
+; CHECK: attributes #[[ATTR0]] = { nofree norecurse nosync nounwind readnone willreturn }
+;.
; CHECK-LABEL: define {{[^@]+}}@foo
; CHECK-SAME: () #[[ATTR0]] {
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[TMP0:%.*]] = alloca i8, i64 4, align 1
+; CHECK-NEXT: [[DOTH2S:%.*]] = alloca i8, i64 4, align 1
; CHECK-NEXT: ret void
;
; CHECK-DISABLED-LABEL: define {{[^@]+}}@foo
; CHECK-DISABLED-SAME: () #[[ATTR0]] {
; CHECK-DISABLED-NEXT: entry:
-; CHECK-DISABLED-NEXT: [[TMP0:%.*]] = alloca i8, i64 4, align 1
+; CHECK-DISABLED-NEXT: [[DOTH2S:%.*]] = alloca i8, i64 4, align 1
; CHECK-DISABLED-NEXT: ret void
;
entry:
define void @unused() {
; CHECK-LABEL: define {{[^@]+}}@unused() {
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[TMP0:%.*]] = alloca i8, i64 4, align 1
+; CHECK-NEXT: [[DOTH2S:%.*]] = alloca i8, i64 4, align 1
; CHECK-NEXT: call void @use(i8* undef)
; CHECK-NEXT: ret void
;