diff --git a/llvm/include/llvm/Transforms/IPO/Attributor.h b/llvm/include/llvm/Transforms/IPO/Attributor.h index 81459eda4cadb..5dc776f08e955 100644 --- a/llvm/include/llvm/Transforms/IPO/Attributor.h +++ b/llvm/include/llvm/Transforms/IPO/Attributor.h @@ -209,9 +209,51 @@ Optional combineOptionalValuesInAAValueLatice(const Optional &A, const Optional &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 { + using BaseTy = std::pair; + 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 @@ -5036,47 +5078,13 @@ struct AAPointerInfo : public AbstractAttribute { /// 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 { - using BaseTy = std::pair; - 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 CB) const = 0; + AA::OffsetAndSize OAS, + function_ref 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 @@ -5085,11 +5093,10 @@ struct AAPointerInfo : public AbstractAttribute { /// 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 CB, - bool &HasBeenWrittenTo) const = 0; + virtual bool forallInterferingAccesses( + Attributor &A, const AbstractAttribute &QueryingAA, Instruction &I, + function_ref 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) { diff --git a/llvm/lib/Target/AMDGPU/AMDGPUAttributor.cpp b/llvm/lib/Target/AMDGPU/AMDGPUAttributor.cpp index a3634d2440c3d..22cda5c120c8a 100644 --- a/llvm/lib/Target/AMDGPU/AMDGPUAttributor.cpp +++ b/llvm/lib/Target/AMDGPU/AMDGPUAttributor.cpp @@ -547,32 +547,32 @@ struct AAAMDAttributesFunction : public AAAMDAttributes { 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 diff --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp index fdbed1b62fdcd..c33a2ea8d9944 100644 --- a/llvm/lib/Transforms/IPO/Attributor.cpp +++ b/llvm/lib/Transforms/IPO/Attributor.cpp @@ -27,7 +27,9 @@ #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" @@ -45,6 +47,7 @@ #include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/Cloning.h" #include "llvm/Transforms/Utils/Local.h" +#include #ifdef EXPENSIVE_CHECKS #include "llvm/IR/Verifier.h" @@ -219,7 +222,9 @@ bool AA::isDynamicallyUnique(Attributor &A, const AbstractAttribute &QueryingAA, } Constant *AA::getInitialValueForObj(Value &Obj, Type &Ty, - const TargetLibraryInfo *TLI) { + const TargetLibraryInfo *TLI, + const DataLayout &DL, + AA::OffsetAndSize *OASPtr) { if (isa(Obj)) return UndefValue::get(&Ty); if (Constant *Init = getInitialValueOfAllocation(&Obj, TLI, &Ty)) @@ -231,7 +236,25 @@ Constant *AA::getInitialValueForObj(Value &Obj, Type &Ty, return nullptr; if (!GV->hasInitializer()) return UndefValue::get(&Ty); - return dyn_cast_or_null(getWithType(*GV->getInitializer(), Ty)); + + // Handle constant initializers by extracting the relevant parts for + // aggregates. + Constant *C = GV->getInitializer(); + if (OASPtr && !OASPtr->offsetOrSizeAreUnknown() && + isa(C)) { + Type *CTy = C->getType(); + APInt Offset = APInt(64, OASPtr->getOffset()); + Optional 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(getWithType(*C, Ty)); } bool AA::isValidInScope(const Value &V, const Function *Scope) { @@ -441,10 +464,11 @@ static bool getPotentialCopiesOfMemoryValue( // object. bool HasBeenWrittenTo = false; + AA::OffsetAndSize OAS = AA::OffsetAndSize::getUnknown(); auto &PI = A.getAAFor(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: " @@ -452,10 +476,15 @@ static bool getPotentialCopiesOfMemoryValue( 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 " @@ -593,8 +622,7 @@ isPotentiallyReachable(Attributor &A, const Instruction &FromI, // Check if the current instruction is already known to reach the ToFn. const auto &FnReachabilityAA = A.getAAFor( 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"); diff --git a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp index f4c6773da639c..76492182b05c4 100644 --- a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp +++ b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp @@ -720,7 +720,7 @@ struct DenseMapInfo : DenseMapInfo { /// Helper that allows OffsetAndSize as a key in a DenseMap. template <> -struct DenseMapInfo +struct DenseMapInfo : DenseMapInfo> {}; /// Helper for AA::PointerInfo::Access DenseMap/Set usage ignoring everythign @@ -847,7 +847,7 @@ struct AA::PointerInfo::State : public AbstractState { }; /// We store all accesses in bins denoted by their offset and size. - using AccessBinsTy = DenseMap; + using AccessBinsTy = DenseMap; AccessBinsTy::const_iterator begin() const { return AccessBins.begin(); } AccessBinsTy::const_iterator end() const { return AccessBins.end(); } @@ -865,7 +865,7 @@ struct AA::PointerInfo::State : public AbstractState { 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; @@ -887,13 +887,13 @@ struct AA::PointerInfo::State : public AbstractState { /// See AAPointerInfo::forallInterferingAccesses. bool forallInterferingAccesses( - AAPointerInfo::OffsetAndSize OAS, + AA::OffsetAndSize OAS, function_ref 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(); @@ -907,12 +907,13 @@ struct AA::PointerInfo::State : public AbstractState { /// See AAPointerInfo::forallInterferingAccesses. bool forallInterferingAccesses( Instruction &I, - function_ref CB) const { + function_ref 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) { @@ -920,11 +921,15 @@ struct AA::PointerInfo::State : public AbstractState { 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 @@ -957,17 +962,16 @@ struct AAPointerInfoImpl } bool forallInterferingAccesses( - OffsetAndSize OAS, + AA::OffsetAndSize OAS, function_ref CB) const override { return State::forallInterferingAccesses(OAS, CB); } - bool - forallInterferingAccesses(Attributor &A, const AbstractAttribute &QueryingAA, - Instruction &I, - function_ref UserCB, - bool &HasBeenWrittenTo) const override { + bool forallInterferingAccesses( + Attributor &A, const AbstractAttribute &QueryingAA, Instruction &I, + function_ref UserCB, bool &HasBeenWrittenTo, + AA::OffsetAndSize *OASPtr = nullptr) const override { HasBeenWrittenTo = false; SmallPtrSet DominatingWrites; @@ -1082,7 +1086,7 @@ struct AAPointerInfoImpl InterferingAccesses.push_back({&Acc, Exact}); return true; }; - if (!State::forallInterferingAccesses(I, AccessCB)) + if (!State::forallInterferingAccesses(I, AccessCB, OASPtr)) return false; if (HasBeenWrittenTo) { @@ -1150,9 +1154,10 @@ struct AAPointerInfoImpl // 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()) @@ -1210,11 +1215,10 @@ struct AAPointerInfoFloating : public AAPointerInfoImpl { bool handleAccess(Attributor &A, Instruction &I, Value &Ptr, Optional 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()) @@ -1226,7 +1230,7 @@ struct AAPointerInfoFloating : public AAPointerInfoImpl { /// 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; } }; @@ -1281,9 +1285,9 @@ struct AAPointerInfoFloating : public AAPointerInfoImpl { // 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; } @@ -1312,7 +1316,7 @@ struct AAPointerInfoFloating : public AAPointerInfoImpl { // 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; @@ -1339,7 +1343,7 @@ struct AAPointerInfoFloating : public AAPointerInfoImpl { // 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; } @@ -1482,7 +1486,7 @@ struct AAPointerInfoCallSiteArgument final : AAPointerInfoFloating { // accessed. if (auto *MI = dyn_cast_or_null(getCtxI())) { ConstantInt *Length = dyn_cast(MI->getLength()); - int64_t LengthVal = OffsetAndSize::Unknown; + int64_t LengthVal = AA::OffsetAndSize::Unknown; if (Length) LengthVal = Length->getSExtValue(); Value &Ptr = getAssociatedValue(); diff --git a/llvm/test/Transforms/Attributor/ArgumentPromotion/aggregate-promote.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/aggregate-promote.ll index 5eea4d2aa00ea..9ef8a2eddbcc8 100644 --- a/llvm/test/Transforms/Attributor/ArgumentPromotion/aggregate-promote.ll +++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/aggregate-promote.ll @@ -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=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 @@ -11,14 +11,11 @@ ; 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 @@ -32,16 +29,15 @@ entry: 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: @@ -50,7 +46,6 @@ 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 } diff --git a/llvm/test/Transforms/Attributor/ArgumentPromotion/fp80.ll b/llvm/test/Transforms/Attributor/ArgumentPromotion/fp80.ll index 1f4efd6a71909..c371fef4e16d8 100644 --- a/llvm/test/Transforms/Attributor/ArgumentPromotion/fp80.ll +++ b/llvm/test/Transforms/Attributor/ArgumentPromotion/fp80.ll @@ -28,13 +28,10 @@ define void @run() { ; 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: @@ -124,7 +121,7 @@ loop: ;. ; 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 } ;. diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/openmp_parallel_for.ll b/llvm/test/Transforms/Attributor/IPConstantProp/openmp_parallel_for.ll index c9d85f4d01389..fd831e6b71fb7 100644 --- a/llvm/test/Transforms/Attributor/IPConstantProp/openmp_parallel_for.ll +++ b/llvm/test/Transforms/Attributor/IPConstantProp/openmp_parallel_for.ll @@ -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=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 ; @@ -179,7 +179,7 @@ define internal void @.omp_outlined.(i32* noalias %.global_tid., i32* noalias %. ; 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]] @@ -301,7 +301,7 @@ define internal void @.omp_outlined.(i32* noalias %.global_tid., i32* noalias %. ; 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]] diff --git a/llvm/test/Transforms/Attributor/value-simplify-pointer-info-struct.ll b/llvm/test/Transforms/Attributor/value-simplify-pointer-info-struct.ll new file mode 100644 index 0000000000000..57c0ac8b85adf --- /dev/null +++ b/llvm/test/Transforms/Attributor/value-simplify-pointer-info-struct.ll @@ -0,0 +1,221 @@ +; 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 } +;. diff --git a/llvm/test/Transforms/OpenMP/remove_globalization.ll b/llvm/test/Transforms/OpenMP/remove_globalization.ll index 5d1cf6e20586b..204cfc96bb478 100644 --- a/llvm/test/Transforms/OpenMP/remove_globalization.ll +++ b/llvm/test/Transforms/OpenMP/remove_globalization.ll @@ -56,13 +56,13 @@ define internal void @foo() { ; 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: @@ -132,7 +132,7 @@ 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 ;