diff --git a/clang/test/Cilk/hyper-destruct2.cpp b/clang/test/Cilk/hyper-destruct2.cpp index 6f796f68d464..c1f3227e8b90 100644 --- a/clang/test/Cilk/hyper-destruct2.cpp +++ b/clang/test/Cilk/hyper-destruct2.cpp @@ -16,8 +16,8 @@ struct Outer { }; // Make sure the appropriate variant of ~Inner() is emitted. -// CHECK: {{^}}define{{.*}} void @_ZN5OuterD2Ev -// CHECK: call void @_ZN5InnerIcED2Ev -// CHECK: {{^}}define linkonce_odr void @_ZN5InnerIcED2Ev +// CHECK: {{^}}define{{.*}} {{void|ptr}} @_ZN5OuterD2Ev +// CHECK: call{{.*}} {{void|ptr}} @_ZN5InnerIcED2Ev +// CHECK: {{^}}define linkonce_odr{{.*}} {{void|ptr}} @_ZN5InnerIcED2Ev Outer::~Outer() { } diff --git a/llvm/include/llvm/Analysis/TapirTaskInfo.h b/llvm/include/llvm/Analysis/TapirTaskInfo.h index fef97f7faabd..6c95c70ee001 100644 --- a/llvm/include/llvm/Analysis/TapirTaskInfo.h +++ b/llvm/include/llvm/Analysis/TapirTaskInfo.h @@ -20,6 +20,7 @@ #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/IR/BasicBlock.h" #include "llvm/IR/CFG.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/Instruction.h" @@ -304,6 +305,17 @@ class Spindle { DenseBlockSet.insert(&B); } + /// Raw method to remove block B from this spindle. + void removeBlock(BasicBlock &B) { + DenseBlockSet.erase(&B); + for (auto Iter = Blocks.begin(); Iter != Blocks.end(); ++Iter) { + if (*Iter == &B) { + Blocks.erase(Iter); + break; + } + } + } + // Returns true if the basic block B predeces this spindle. bool blockPrecedesSpindle(const BasicBlock *B) const { for (const BasicBlock *SB : successors(B)) @@ -1456,6 +1468,27 @@ class TaskInfo { SpindleMap[S] = T; } + // Move spindle S from its task to that task's parent. + // + // NOTE: The resulting TaskInfo may not exactly match the state of a freshly + // computed TaskInfo. + void moveSpindlesToParent(Task *T) { + // Currently this method simply adds T's spindles to T's parent task + // and removes those spindles from T. + // TODO: Update Spindle types and edges. + Task *Parent = T->getParentTask(); + // Add all spindles to parent task. + for (Spindle *S : T->Spindles) { + Parent->addSpindle(*S); + S->setParentTask(Parent); + SpindleMap[S] = Parent; + } + + // Remove all spindles from this task. + while (!T->Spindles.empty()) + T->Spindles.pop_back(); + } + // Add spindle S to task T, where S is a shared exception-handling spindle // among subtasks of T. void addEHSpindleToTask(Spindle *S, Task *T) { @@ -1472,6 +1505,15 @@ class TaskInfo { BBMap[&B] = S; } + // Remove basic block B from its spindle. + void removeBlock(BasicBlock &B) { + if (!BBMap.count(&B)) + return; + Spindle *S = BBMap[&B]; + S->removeBlock(B); + BBMap.erase(&B); + } + // Associate a task T with the spindle TFSpindle that creates its taskframe. void AssociateTaskFrameWithUser(Task *T, Spindle *TFSpindle) { TFSpindle->TaskFrameUser = T; diff --git a/llvm/include/llvm/Transforms/Utils/TapirUtils.h b/llvm/include/llvm/Transforms/Utils/TapirUtils.h index cb7585b4043b..d40a63da486c 100644 --- a/llvm/include/llvm/Transforms/Utils/TapirUtils.h +++ b/llvm/include/llvm/Transforms/Utils/TapirUtils.h @@ -103,7 +103,8 @@ bool MoveStaticAllocasInBlock(BasicBlock *Entry, BasicBlock *Block, /// Inline any taskframe.resume markers associated with the given taskframe. If /// \p DT is provided, then it will be updated to reflect the CFG changes. -void InlineTaskFrameResumes(Value *TaskFrame, DominatorTree *DT = nullptr); +void InlineTaskFrameResumes(Value *TaskFrame, DominatorTree *DT = nullptr, + TaskInfo *TI = nullptr); /// Clone exception-handling blocks EHBlocksToClone, with predecessors /// EHBlockPreds in a given task. Updates EHBlockPreds to point at the cloned @@ -131,7 +132,8 @@ void SerializeDetach(DetachInst *DI, BasicBlock *ParentEntry, SmallPtrSetImpl *InlinedLPads, SmallVectorImpl *DetachedRethrows, bool ReplaceWithTaskFrame = false, - DominatorTree *DT = nullptr, LoopInfo *LI = nullptr); + DominatorTree *DT = nullptr, TaskInfo *TI = nullptr, + LoopInfo *LI = nullptr); /// Analyze a task T for serialization. Gets the reattaches, landing pads, and /// detached rethrows that need special handling during serialization. @@ -145,7 +147,7 @@ void AnalyzeTaskForSerialization( /// Serialize the detach DI that spawns task T. If \p DT is provided, then it /// will be updated to reflect the CFG changes. void SerializeDetach(DetachInst *DI, Task *T, bool ReplaceWithTaskFrame = false, - DominatorTree *DT = nullptr); + DominatorTree *DT = nullptr, TaskInfo *TI = nullptr); /// Get the entry basic block to the detached context that contains /// the specified block. diff --git a/llvm/include/llvm/Transforms/Utils/TaskSimplify.h b/llvm/include/llvm/Transforms/Utils/TaskSimplify.h index 681af4e07ea4..4607cfeb4570 100644 --- a/llvm/include/llvm/Transforms/Utils/TaskSimplify.h +++ b/llvm/include/llvm/Transforms/Utils/TaskSimplify.h @@ -31,7 +31,7 @@ class TaskSimplifyPass : public PassInfoMixin { bool simplifySyncs(Task *T, MaybeParallelTasks &MPTasks); /// Simplify the specified task T. -bool simplifyTask(Task *T); +bool simplifyTask(Task *T, TaskInfo &TI, DominatorTree &DT); /// Simplify the taskframes analyzed by TapirTaskInfo TI. bool simplifyTaskFrames(TaskInfo &TI, DominatorTree &DT); diff --git a/llvm/lib/Transforms/Tapir/LoopStripMine.cpp b/llvm/lib/Transforms/Tapir/LoopStripMine.cpp index 8218f1f37f07..a032fc5b1e6e 100644 --- a/llvm/lib/Transforms/Tapir/LoopStripMine.cpp +++ b/llvm/lib/Transforms/Tapir/LoopStripMine.cpp @@ -1099,7 +1099,7 @@ Loop *llvm::StripMineLoop(Loop *L, unsigned Count, bool AllowExpensiveTripCount, SerializeDetach(ClonedDI, ParentEntry, EHCont, EHContLPadVal, ClonedReattaches, &ClonedEHBlocks, &ClonedEHBlockPreds, &ClonedInlinedLPads, &ClonedDetachedRethrows, - NeedToInsertTaskFrame, DT, LI); + NeedToInsertTaskFrame, DT, nullptr, LI); } // Detach the stripmined loop. diff --git a/llvm/lib/Transforms/Utils/InlineFunction.cpp b/llvm/lib/Transforms/Utils/InlineFunction.cpp index e682739d4906..ed4498307acd 100644 --- a/llvm/lib/Transforms/Utils/InlineFunction.cpp +++ b/llvm/lib/Transforms/Utils/InlineFunction.cpp @@ -675,8 +675,8 @@ static void HandleInlinedTasksHelper( } // Promote any calls in the block to invokes. - if (BasicBlock *NewBB = HandleCallsInBlockInlinedThroughInvoke( - BB, UnwindEdge)) { + if (BasicBlock *NewBB = + HandleCallsInBlockInlinedThroughInvoke(BB, UnwindEdge)) { // If this is the topmost invocation of HandleInlinedTasksHelper, update // any PHI nodes in the exceptional block to indicate that there is now a // new entry in them. @@ -745,7 +745,7 @@ static void HandleInlinedTasksHelper( Invoke.addIncomingPHIValuesFor(SubTaskUnwindEdge); } - } else if (Visited.insert(DI->getUnwindDest()).second) { + } else if (!Visited.contains(DI->getUnwindDest())) { // If the detach-unwind isn't dead, add it to the worklist. Worklist.push_back(DI->getUnwindDest()); } diff --git a/llvm/lib/Transforms/Utils/TapirUtils.cpp b/llvm/lib/Transforms/Utils/TapirUtils.cpp index ad65f8a0ef00..4b1fcc3ba496 100644 --- a/llvm/lib/Transforms/Utils/TapirUtils.cpp +++ b/llvm/lib/Transforms/Utils/TapirUtils.cpp @@ -387,11 +387,16 @@ class LandingPadInliningInfo { /// Dominator tree to update. DominatorTree *DT = nullptr; + + /// TaskInfo to update. + TaskInfo *TI = nullptr; + public: LandingPadInliningInfo(DetachInst *DI, BasicBlock *EHContinue, Value *LPadValInEHContinue, - DominatorTree *DT = nullptr) - : OuterResumeDest(EHContinue), SpawnerLPad(LPadValInEHContinue), DT(DT) { + DominatorTree *DT = nullptr, TaskInfo *TI = nullptr) + : OuterResumeDest(EHContinue), SpawnerLPad(LPadValInEHContinue), DT(DT), + TI(TI) { // Find the predecessor block of OuterResumeDest. BasicBlock *DetachBB = DI->getParent(); BasicBlock *DetachUnwind = DI->getUnwindDest(); @@ -414,9 +419,9 @@ class LandingPadInliningInfo { } LandingPadInliningInfo(InvokeInst *TaskFrameResume, - DominatorTree *DT = nullptr) + DominatorTree *DT = nullptr, TaskInfo *TI = nullptr) : OuterResumeDest(TaskFrameResume->getUnwindDest()), - SpawnerLPad(TaskFrameResume->getLandingPadInst()), DT(DT) { + SpawnerLPad(TaskFrameResume->getLandingPadInst()), DT(DT), TI(TI) { // If there are PHI nodes in the unwind destination block, we need to keep // track of which values came into them from the detach before removing the // edge from this block. @@ -484,6 +489,8 @@ BasicBlock *LandingPadInliningInfo::getInnerResumeDest() { for (DomTreeNode *I : Children) DT->changeImmediateDominator(I, NewNode); } + if (TI) + TI->addBlockToSpindle(*InnerResumeDest, TI->getSpindleFor(OuterResumeDest)); // The number of incoming edges we expect to the inner landing pad. const unsigned PHICapacity = 2; @@ -571,11 +578,15 @@ void LandingPadInliningInfo::forwardTaskResume(InvokeInst *TR) { if (NormalDest) { for (BasicBlock *Succ : successors(NormalDest)) maybeRemovePredecessor(Succ, NormalDest); + if (TI) + TI->removeBlock(*NormalDest); NormalDest->eraseFromParent(); } if (UnwindDest) { for (BasicBlock *Succ : successors(UnwindDest)) maybeRemovePredecessor(Succ, UnwindDest); + if (TI) + TI->removeBlock(*UnwindDest); UnwindDest->eraseFromParent(); } } @@ -584,8 +595,8 @@ static void handleDetachedLandingPads( DetachInst *DI, BasicBlock *EHContinue, Value *LPadValInEHContinue, SmallPtrSetImpl &InlinedLPads, SmallVectorImpl &DetachedRethrows, - DominatorTree *DT = nullptr) { - LandingPadInliningInfo DetUnwind(DI, EHContinue, LPadValInEHContinue, DT); + DominatorTree *DT = nullptr, TaskInfo *TI = nullptr) { + LandingPadInliningInfo DetUnwind(DI, EHContinue, LPadValInEHContinue, DT, TI); // Append the clauses from the outer landing pad instruction into the inlined // landing pad instructions. @@ -815,13 +826,14 @@ static void getTaskFrameLandingPads( // Helper method to handle a given taskframe.resume. static void handleTaskFrameResume(Value *TaskFrame, Instruction *TaskFrameResume, - DominatorTree *DT = nullptr) { + DominatorTree *DT = nullptr, + TaskInfo *TI = nullptr) { // Get landingpads to inline. SmallPtrSet InlinedLPads; getTaskFrameLandingPads(TaskFrame, TaskFrameResume, InlinedLPads); InvokeInst *TFR = cast(TaskFrameResume); - LandingPadInliningInfo TFResumeDest(TFR, DT); + LandingPadInliningInfo TFResumeDest(TFR, DT, TI); // Append the clauses from the outer landing pad instruction into the inlined // landing pad instructions. @@ -839,7 +851,8 @@ static void handleTaskFrameResume(Value *TaskFrame, TFResumeDest.forwardTaskResume(TFR); } -void llvm::InlineTaskFrameResumes(Value *TaskFrame, DominatorTree *DT) { +void llvm::InlineTaskFrameResumes(Value *TaskFrame, DominatorTree *DT, + TaskInfo *TI) { SmallVector TaskFrameResumes; // Record all taskframe.resume markers that use TaskFrame. for (User *U : TaskFrame->users()) @@ -849,12 +862,12 @@ void llvm::InlineTaskFrameResumes(Value *TaskFrame, DominatorTree *DT) { // Handle all taskframe.resume markers. for (Instruction *TFR : TaskFrameResumes) - handleTaskFrameResume(TaskFrame, TFR, DT); + handleTaskFrameResume(TaskFrame, TFR, DT, TI); } static void startSerializingTaskFrame(Value *TaskFrame, SmallVectorImpl &ToErase, - DominatorTree *DT, + DominatorTree *DT, TaskInfo *TI, bool PreserveTaskFrame) { for (User *U : TaskFrame->users()) if (Instruction *UI = dyn_cast(U)) @@ -862,7 +875,7 @@ static void startSerializingTaskFrame(Value *TaskFrame, ToErase.push_back(UI); if (!PreserveTaskFrame) - InlineTaskFrameResumes(TaskFrame, DT); + InlineTaskFrameResumes(TaskFrame, DT, TI); } void llvm::SerializeDetach(DetachInst *DI, BasicBlock *ParentEntry, @@ -873,7 +886,9 @@ void llvm::SerializeDetach(DetachInst *DI, BasicBlock *ParentEntry, SmallPtrSetImpl *InlinedLPads, SmallVectorImpl *DetachedRethrows, bool ReplaceWithTaskFrame, DominatorTree *DT, - LoopInfo *LI) { + TaskInfo *TI, LoopInfo *LI) { + LLVM_DEBUG(dbgs() << "Serializing detach " << *DI << "\n"); + BasicBlock *Spawner = DI->getParent(); BasicBlock *TaskEntry = DI->getDetached(); BasicBlock *Continue = DI->getContinue(); @@ -885,7 +900,7 @@ void llvm::SerializeDetach(DetachInst *DI, BasicBlock *ParentEntry, SmallVector ToErase; Value *TaskFrame = getTaskFrameUsed(TaskEntry); if (TaskFrame) - startSerializingTaskFrame(TaskFrame, ToErase, DT, ReplaceWithTaskFrame); + startSerializingTaskFrame(TaskFrame, ToErase, DT, TI, ReplaceWithTaskFrame); // Clone any EH blocks that need cloning. if (EHBlocksToClone) { @@ -952,7 +967,7 @@ void llvm::SerializeDetach(DetachInst *DI, BasicBlock *ParentEntry, } else { // Otherwise, "inline" the detached landingpads. handleDetachedLandingPads(DI, EHContinue, LPadValInEHContinue, - *InlinedLPads, *DetachedRethrows, DT); + *InlinedLPads, *DetachedRethrows, DT, TI); } } @@ -1059,7 +1074,7 @@ void llvm::AnalyzeTaskForSerialization( /// Serialize the detach DI that spawns task T. If provided, the dominator tree /// DT will be updated to reflect the serialization. void llvm::SerializeDetach(DetachInst *DI, Task *T, bool ReplaceWithTaskFrame, - DominatorTree *DT) { + DominatorTree *DT, TaskInfo *TI) { assert(DI && "SerializeDetach given nullptr for detach."); assert(DI == T->getDetach() && "Task and detach arguments do not match."); SmallVector EHBlocksToClone; @@ -1078,7 +1093,9 @@ void llvm::SerializeDetach(DetachInst *DI, Task *T, bool ReplaceWithTaskFrame, } SerializeDetach(DI, T->getParentTask()->getEntry(), EHContinue, LPadVal, Reattaches, &EHBlocksToClone, &EHBlockPreds, &InlinedLPads, - &DetachedRethrows, ReplaceWithTaskFrame, DT); + &DetachedRethrows, ReplaceWithTaskFrame, DT, TI); + if (TI) + TI->moveSpindlesToParent(T); } static bool isCanonicalTaskFrameEnd(const Instruction *TFEnd) { @@ -2260,7 +2277,8 @@ void llvm::eraseTaskFrame(Value *TaskFrame, DominatorTree *DT) { for (User *U : TaskFrame->users()) { if (Instruction *UI = dyn_cast(U)) if (isTapirIntrinsic(Intrinsic::taskframe_use, UI) || - isTapirIntrinsic(Intrinsic::taskframe_end, UI)) + isTapirIntrinsic(Intrinsic::taskframe_end, UI) || + isTapirIntrinsic(Intrinsic::taskframe_resume, UI)) ToErase.push_back(UI); } diff --git a/llvm/lib/Transforms/Utils/TaskSimplify.cpp b/llvm/lib/Transforms/Utils/TaskSimplify.cpp index f010d4066869..f16749bc10d1 100644 --- a/llvm/lib/Transforms/Utils/TaskSimplify.cpp +++ b/llvm/lib/Transforms/Utils/TaskSimplify.cpp @@ -12,6 +12,7 @@ #include "llvm/Transforms/Utils/TaskSimplify.h" #include "llvm/ADT/PostOrderIterator.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/Statistic.h" #include "llvm/Analysis/AssumptionCache.h" #include "llvm/Analysis/CFG.h" @@ -23,7 +24,9 @@ #include "llvm/Analysis/ScalarEvolution.h" #include "llvm/Analysis/TapirTaskInfo.h" #include "llvm/Analysis/TargetTransformInfo.h" +#include "llvm/IR/Dominators.h" #include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Instruction.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/Intrinsics.h" #include "llvm/IR/LegacyPassManager.h" @@ -114,10 +117,11 @@ static bool removeRedundantSyncs(MaybeParallelTasks &MPTasks, Task *T) { return Changed; } -static bool syncIsDiscriminating(const Value *SyncSR, +static bool syncIsDiscriminating(const SyncInst *Sync, SmallPtrSetImpl &MPTasks) { + const Value *SyncSR = Sync->getSyncRegion(); for (const Task *MPTask : MPTasks) - if (!MPTask->encloses(cast(SyncSR)->getParent()) && + if (!MPTask->encloses(Sync->getParent()) && SyncSR != MPTask->getDetach()->getSyncRegion()) return true; return false; @@ -134,12 +138,13 @@ static bool removeRedundantSyncRegions(MaybeParallelTasks &MPTasks, Task *T) { // Find the unique sync regions in this task. SmallPtrSet UniqueSyncRegs; - Instruction *FirstSyncRegion = nullptr; + // It's possible for a sync region to not be an instruction only when + // debugging reduced test cases. + Value *FirstSyncRegion = nullptr; for (Task *SubT : T->subtasks()) { UniqueSyncRegs.insert(SubT->getDetach()->getSyncRegion()); if (!FirstSyncRegion) - FirstSyncRegion = cast( - SubT->getDetach()->getSyncRegion()); + FirstSyncRegion = SubT->getDetach()->getSyncRegion(); } NumUniqueSyncRegs += UniqueSyncRegs.size(); // Skip this task if there's only one unique sync region. @@ -162,7 +167,7 @@ static bool removeRedundantSyncRegions(MaybeParallelTasks &MPTasks, Task *T) { // Iterate over outgoing edges of S to find discriminating syncs. for (Spindle::SpindleEdge &Edge : S->out_edges()) if (const SyncInst *Y = dyn_cast(Edge.second->getTerminator())) - if (syncIsDiscriminating(Y->getSyncRegion(), LocalTaskList)) { + if (syncIsDiscriminating(Y, LocalTaskList)) { ++NumDiscriminatingSyncs; LLVM_DEBUG(dbgs() << "Found discriminating sync " << *Y << "\n"); NonRedundantSyncRegs.insert(Y->getSyncRegion()); @@ -179,8 +184,9 @@ static bool removeRedundantSyncRegions(MaybeParallelTasks &MPTasks, Task *T) { Changed = true; SR->replaceAllUsesWith(FirstSyncRegion); // Ensure that the first sync region is in the entry block of T. - if (FirstSyncRegion->getParent() != T->getEntry()) - FirstSyncRegion->moveAfter(&*T->getEntry()->getFirstInsertionPt()); + if (Instruction *SyncRegI = dyn_cast(FirstSyncRegion)) + if (SyncRegI->getParent() != T->getEntry()) + SyncRegI->moveAfter(&*T->getEntry()->getFirstInsertionPt()); } } @@ -232,10 +238,11 @@ static bool detachImmediatelySyncs(DetachInst *DI) { return isa(I); } -bool llvm::simplifyTask(Task *T) { +bool llvm::simplifyTask(Task *T, TaskInfo &TI, DominatorTree &DT) { if (T->isRootTask()) return false; + DomTreeUpdater DTU(DT, DomTreeUpdater::UpdateStrategy::Eager); LLVM_DEBUG(dbgs() << "Simplifying task @ " << T->getEntry()->getName() << "\n"); @@ -248,7 +255,8 @@ bool llvm::simplifyTask(Task *T) { // destination from T's detach. if (DI->hasUnwindDest()) { if (!taskCanThrow(T)) { - removeUnwindEdge(DI->getParent()); + LLVM_DEBUG(dbgs() << "Removing unwind edge of " << *DI << "\n"); + removeUnwindEdge(DI->getParent(), &DTU); // removeUnwindEdge will invalidate the DI pointer. Get the new DI // pointer. DI = T->getDetach(); @@ -257,13 +265,17 @@ bool llvm::simplifyTask(Task *T) { } if (!taskCanReachContinuation(T)) { + LLVM_DEBUG(dbgs() << "Task cannot reach continuation. Serializing " << *DI + << "\n"); // This optimization assumes that if a task cannot reach its continuation // then we shouldn't bother spawning it. The task might perform code that // can reach the unwind destination, however. - SerializeDetach(DI, T, NestedSync); + SerializeDetach(DI, T, NestedSync, &DT, &TI); Changed = true; } else if (!PreserveAllSpawns && detachImmediatelySyncs(DI)) { - SerializeDetach(DI, T, NestedSync); + LLVM_DEBUG(dbgs() << "Detach immediately syncs. Serializing " << *DI + << "\n"); + SerializeDetach(DI, T, NestedSync, &DT, &TI); Changed = true; } @@ -342,7 +354,7 @@ static bool canRemoveTaskFrame(const Spindle *TF, MaybeParallelTasks &MPTasks, // so would cause these syncs to sync tasks spawned in the parent // taskframe. if (const SyncInst *SI = dyn_cast(BB->getTerminator())) - if (syncIsDiscriminating(SI->getSyncRegion(), LocalTaskList)) { + if (syncIsDiscriminating(SI, LocalTaskList)) { LLVM_DEBUG(dbgs() << "Can't remove taskframe with distinguishing sync: " << *TFCreate << "\n"); @@ -512,6 +524,30 @@ static bool iterativelySimplifyCFG(Function &F, const TargetTransformInfo &TTI, return Changed; } +static bool removeDeadTapirIntrinsics(Function &F) { + SmallVector ToErase; + for (BasicBlock &B : F) { + for (Instruction &I : B) { + // Look for any now-unused tapir.runtime.start intrinsics + if (isTapirIntrinsic(Intrinsic::tapir_runtime_start, &I)) { + if (!llvm::any_of(I.users(), [&](const User *U) { + return isa(U) && + isTapirIntrinsic(Intrinsic::tapir_runtime_end, + cast(U)); + })) + ToErase.push_back(&I); + } + } + } + + if (ToErase.empty()) + return false; + + for (Instruction *I : ToErase) + I->eraseFromParent(); + return true; +} + static bool simplifyFunctionCFG(Function &F, const TargetTransformInfo &TTI, DominatorTree *DT, const SimplifyCFGOptions &Options) { @@ -519,6 +555,7 @@ static bool simplifyFunctionCFG(Function &F, const TargetTransformInfo &TTI, bool EverChanged = removeUnreachableBlocks(F, DT ? &DTU : nullptr); EverChanged |= iterativelySimplifyCFG(F, TTI, DT ? &DTU : nullptr, Options); + EverChanged |= removeDeadTapirIntrinsics(F); // If neither pass changed anything, we're done. if (!EverChanged) return false; @@ -534,6 +571,7 @@ static bool simplifyFunctionCFG(Function &F, const TargetTransformInfo &TTI, do { EverChanged = iterativelySimplifyCFG(F, TTI, DT ? &DTU : nullptr, Options); EverChanged |= removeUnreachableBlocks(F, DT ? &DTU : nullptr); + EverChanged |= removeDeadTapirIntrinsics(F); } while (EverChanged); return true; @@ -619,7 +657,7 @@ bool TaskSimplify::runOnFunction(Function &F) { // Simplify each task in the function. for (Task *T : post_order(TI.getRootTask())) - Changed |= simplifyTask(T); + Changed |= simplifyTask(T, TI, DT); if (PostCleanupCFG && (Changed | SplitBlocks)) Changed |= simplifyFunctionCFG(F, TTI, nullptr, Options); @@ -697,7 +735,7 @@ PreservedAnalyses TaskSimplifyPass::run(Function &F, // Simplify each task in the function. for (Task *T : post_order(TI.getRootTask())) - Changed |= simplifyTask(T); + Changed |= simplifyTask(T, TI, DT); if (PostCleanupCFG && (Changed | SplitBlocks)) Changed |= simplifyFunctionCFG(F, TTI, nullptr, Options); diff --git a/llvm/test/Transforms/Tapir/inline-detach-unwind.ll b/llvm/test/Transforms/Tapir/inline-detach-unwind.ll new file mode 100644 index 000000000000..368fc091228c --- /dev/null +++ b/llvm/test/Transforms/Tapir/inline-detach-unwind.ll @@ -0,0 +1,81 @@ +; Check that detach-unwind blocks are properly inlined when they are reachable only from the detach instruction itself. +; +; RUN: opt < %s -passes="cgscc(devirt<4>(inline))" -S | FileCheck %s +target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" +target triple = "arm64-apple-macosx15.0.0" + +; CHECK: define void @_ZNK5Graph17pbfs_walk_PennantEP7PennantIiERH3BagIiEjPj() + +; CHECK: pfor.cond: +; CHECK-NEXT: detach within none, label %pfor.body.entry, label %pfor.cond unwind label %lpad59 + +; CHECK: pfor.body.entry: +; CHECK: br label %[[INLINED_PFOR_COND:.+]] + +; CHECK: [[INLINED_PFOR_COND]]: +; CHECK: detach within {{.+}}, label %{{.+}}, label %{{.+}} unwind label %[[INLINED_DETACH_UNWIND:.+]] + +; CHECK: [[INLINED_DETACH_UNWIND]]: +; CHECK-NOT: resume +; CHECK: br label %[[UNWIND_TO_PARENT:.+]] + +; CHECK: [[UNWIND_TO_PARENT]]: +; CHECK: invoke void @llvm.taskframe.resume.sl_p0i32s(token +; CHECK-NEXT: to label %{{.+}} unwind label %lpad49 + +define void @_ZNK5Graph17pbfs_walk_PennantEP7PennantIiERH3BagIiEjPj() personality ptr null { +entry: + %0 = call token @llvm.tapir.runtime.start() + br label %pfor.cond + +pfor.cond: ; preds = %pfor.preattach, %pfor.cond, %entry + detach within none, label %pfor.body.entry, label %pfor.cond unwind label %lpad59 + +pfor.body.entry: ; preds = %pfor.cond + invoke fastcc void @_ZL14pbfs_proc_NodePKiiRH3BagIiEjPjS0_S0_(ptr null, i32 0, ptr null, i32 0, ptr null, ptr null, ptr null) + to label %pfor.preattach unwind label %lpad49 + +pfor.preattach: ; preds = %pfor.body.entry + reattach within none, label %pfor.cond + +lpad49: ; preds = %pfor.body.entry + %1 = landingpad { ptr, i32 } + cleanup + unreachable + +lpad59: ; preds = %pfor.cond + %2 = landingpad { ptr, i32 } + cleanup + call void @llvm.tapir.runtime.end(token %0) + resume { ptr, i32 } zeroinitializer +} + +define fastcc void @_ZL14pbfs_proc_NodePKiiRH3BagIiEjPjS0_S0_(ptr %n, i32 %fillSize, ptr %next, i32 %newdist, ptr %distances, ptr %nodes, ptr %edges) personality ptr null { +entry: + br label %pfor.cond + +pfor.cond: ; preds = %pfor.cond, %entry + detach within none, label %pfor.body.entry, label %pfor.cond unwind label %lpad20 + +pfor.body.entry: ; preds = %pfor.cond + br label %for.cond + +for.cond: ; preds = %for.cond, %pfor.body.entry + br label %for.cond + +lpad20: ; preds = %pfor.cond + %0 = landingpad { ptr, i32 } + cleanup + resume { ptr, i32 } zeroinitializer +} + +; Function Attrs: nounwind willreturn memory(argmem: readwrite) +declare token @llvm.tapir.runtime.start() #0 + +; Function Attrs: nounwind willreturn memory(argmem: readwrite) +declare void @llvm.tapir.runtime.end(token) #0 + +; uselistorder directives +uselistorder ptr null, { 7, 8, 0, 2, 3, 4, 5, 6, 9, 10, 1 } + +attributes #0 = { nounwind willreturn memory(argmem: readwrite) } diff --git a/llvm/test/Transforms/Tapir/inline-spawn-lpads.ll b/llvm/test/Transforms/Tapir/inline-spawn-lpads.ll index 303dff0decbd..a35dc790876a 100644 --- a/llvm/test/Transforms/Tapir/inline-spawn-lpads.ll +++ b/llvm/test/Transforms/Tapir/inline-spawn-lpads.ll @@ -370,6 +370,7 @@ eh.resume: ; preds = %lpad ; CHECK-NEXT: %[[LPADIVAL:.+]] = landingpad [[LPADTYPE]] ; CHECK-NEXT: catch ptr @_ZTIc ; CHECK-NEXT: catch ptr @_ZTIi +; CHECK: call i32 @llvm.eh.typeid.for(ptr @_ZTIc) ; CHECK: br i1 %{{.+}}, label %[[CATCHI:.+]], label %[[RESUMEI:.+]] ; CHECK: [[CATCHI]]: @@ -377,7 +378,7 @@ eh.resume: ; preds = %lpad ; CHECK: br label %[[EXITI:.+]] ; CHECK: [[RESUMEI]]: -; CHECK-NEXT: resume [[LPADTYPE]] %[[LPADIVAL]] +; CHECK: br label %[[LPADBODY:.+]] ; CHECK: [[UNREACHABLEI]]: ; CHECK-NEXT: unreachable @@ -385,9 +386,22 @@ eh.resume: ; preds = %lpad ; CHECK: [[EXITI]]: ; CHECK-NEXT: br label %[[TRYCONT:.+]] +; CHECK: [[LPADBODY]]: +; CHECK-NEXT: %[[EH_LPADBODY:.+]] = phi [[LPADTYPE]] +; CHECK: [ %[[LPADIVAL]], %[[RESUMEI]] ] +; CHECK: call i32 @llvm.eh.typeid.for(ptr @_ZTIi) +; CHECK: br i1 %{{.+}}, label %[[CATCH:.+]], label %[[RESUME:.+]] + +; CHECK: [[CATCH]]: +; CHECK: call void @_Z7catch_ii( +; CHECK: br label %[[TRYCONT]] + ; CHECK: [[TRYCONT]]: ; CHECK-NEXT: ret void +; CHECK: [[RESUME]]: +; CHECK-NEXT: resume [[LPADTYPE]] %[[EH_LPADBODY]] + attributes #0 = { uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } attributes #1 = { nounwind readnone } attributes #2 = { nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } diff --git a/llvm/test/Transforms/Tapir/nested-serialize-detach.ll b/llvm/test/Transforms/Tapir/nested-serialize-detach.ll new file mode 100644 index 000000000000..f83422740dd2 --- /dev/null +++ b/llvm/test/Transforms/Tapir/nested-serialize-detach.ll @@ -0,0 +1,98 @@ +; Check that nested detaches can be serialized. +; +; RUN: opt < %s -passes="function(task-simplify)" -S | FileCheck %s +target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" +target triple = "arm64-apple-macosx15.0.0" + +; Function Attrs: nounwind willreturn memory(argmem: readwrite) +declare token @llvm.syncregion.start() #0 + +; Function Attrs: willreturn memory(argmem: readwrite) +declare void @llvm.sync.unwind(token) #1 + +; Function Attrs: willreturn memory(argmem: readwrite) +declare void @llvm.detached.rethrow.sl_p0i32s(token, { ptr, i32 }) #1 + +; Function Attrs: nounwind willreturn memory(argmem: readwrite) +declare token @llvm.taskframe.create() #0 + +; CHECK: define void @_ZNK5Graph17pbfs_walk_PennantEP7PennantIiERH3BagIiEjPj() +; CHECK-NEXT: entry: +; CHECK-NOT: detach within +; CHECK: unreachable + +define void @_ZNK5Graph17pbfs_walk_PennantEP7PennantIiERH3BagIiEjPj() personality ptr null { +entry: + %syncreg = tail call token @llvm.syncregion.start() + %syncreg45 = tail call token @llvm.syncregion.start() + %0 = tail call token @llvm.tapir.runtime.start() + detach within %syncreg45, label %pfor.body.entry.tf, label %pfor.inc unwind label %lpad59.loopexit + +pfor.body.entry.tf: ; preds = %entry + %tf.i = tail call token @llvm.taskframe.create() + %syncreg.i = tail call token @llvm.syncregion.start() + detach within %syncreg.i, label %pfor.cond.i.strpm.detachloop.entry, label %pfor.cond.cleanup.i unwind label %lpad4924.loopexit.split-lp + +pfor.cond.i.strpm.detachloop.entry: ; preds = %pfor.body.entry.tf + %syncreg.i.strpm.detachloop = tail call token @llvm.syncregion.start() + detach within none, label %pfor.body.entry.i.strpm.outer.1, label %pfor.inc.i.strpm.outer.1 unwind label %lpad4924.loopexit.strpm + +pfor.body.entry.i.strpm.outer.1: ; preds = %pfor.cond.i.strpm.detachloop.entry + invoke void @llvm.detached.rethrow.sl_p0i32s(token none, { ptr, i32 } zeroinitializer) + to label %lpad4924.unreachable unwind label %lpad4924.loopexit.strpm + +pfor.inc.i.strpm.outer.1: ; preds = %pfor.cond.i.strpm.detachloop.entry + sync within none, label %pfor.cond.i.strpm.detachloop.reattach.split + +pfor.cond.i.strpm.detachloop.reattach.split: ; preds = %pfor.inc.i.strpm.outer.1 + reattach within %syncreg.i, label %pfor.cond.cleanup.i + +pfor.cond.cleanup.i: ; preds = %pfor.cond.i.strpm.detachloop.reattach.split, %pfor.body.entry.tf + sync within %syncreg.i, label %sync.continue.i + +sync.continue.i: ; preds = %pfor.cond.cleanup.i + invoke void @llvm.sync.unwind(token none) + to label %pfor.preattach unwind label %lpad4924.loopexit.split-lp + +lpad4924.loopexit.strpm: ; preds = %pfor.body.entry.i.strpm.outer.1, %pfor.cond.i.strpm.detachloop.entry + %lpad.strpm = landingpad { ptr, i32 } + cleanup + invoke void @llvm.detached.rethrow.sl_p0i32s(token %syncreg.i, { ptr, i32 } zeroinitializer) + to label %lpad4924.loopexit.strpm.unreachable unwind label %lpad4924.loopexit.split-lp + +lpad4924.loopexit.strpm.unreachable: ; preds = %lpad4924.loopexit.strpm + unreachable + +lpad4924.loopexit.split-lp: ; preds = %lpad4924.loopexit.strpm, %sync.continue.i, %pfor.body.entry.tf + %lpad.loopexit.split-lp = landingpad { ptr, i32 } + cleanup + call void @llvm.detached.rethrow.sl_p0i32s(token none, { ptr, i32 } zeroinitializer) + unreachable + +lpad4924.unreachable: ; preds = %pfor.body.entry.i.strpm.outer.1 + unreachable + +pfor.preattach: ; preds = %sync.continue.i + reattach within %syncreg45, label %pfor.inc + +pfor.inc: ; preds = %pfor.preattach, %entry + ret void + +lpad59.loopexit: ; preds = %entry + %lpad.loopexit28 = landingpad { ptr, i32 } + cleanup + tail call void @llvm.tapir.runtime.end(token %0) + resume { ptr, i32 } zeroinitializer +} + +; Function Attrs: nounwind willreturn memory(argmem: readwrite) +declare token @llvm.tapir.runtime.start() #0 + +; Function Attrs: nounwind willreturn memory(argmem: readwrite) +declare void @llvm.tapir.runtime.end(token) #0 + +; uselistorder directives +uselistorder ptr null, { 1, 2, 0 } + +attributes #0 = { nounwind willreturn memory(argmem: readwrite) } +attributes #1 = { willreturn memory(argmem: readwrite) } diff --git a/llvm/test/Transforms/Tapir/remove-dead-runtime-start-after-unreachable-blocks.ll b/llvm/test/Transforms/Tapir/remove-dead-runtime-start-after-unreachable-blocks.ll new file mode 100644 index 000000000000..822bbae1d630 --- /dev/null +++ b/llvm/test/Transforms/Tapir/remove-dead-runtime-start-after-unreachable-blocks.ll @@ -0,0 +1,48 @@ +; Check that dead tapir.runtime.start intrinsics are removed after removing unreachable blocks. +; +; RUN: opt < %s -passes="function(task-simplify)" -S | FileCheck %s +target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" +target triple = "arm64-apple-macosx15.0.0" + +; CHECK: define void @_ZNK5Graph17pbfs_walk_PennantEP7PennantIiERH3BagIiEjPj() +; CHECK-NEXT: entry: +; CHECK-NEXT: call ptr @_Znwm +; CHECK-NEXT: unreachable + +define void @_ZNK5Graph17pbfs_walk_PennantEP7PennantIiERH3BagIiEjPj() personality ptr null { +entry: + %0 = tail call token @llvm.tapir.runtime.start() + detach within none, label %pfor.body.entry.tf, label %pfor.inc unwind label %lpad59.loopexit + +pfor.body.entry.tf: ; preds = %entry + %call.i15.i26.1 = call ptr @_Znwm() + unreachable + +pfor.inc: ; preds = %entry + sync within none, label %sync.continue + +common.ret: ; preds = %sync.continue, %lpad59.loopexit + ret void + +lpad59.loopexit: ; preds = %entry + %lpad.loopexit28 = landingpad { ptr, i32 } + cleanup + br label %common.ret + +sync.continue: ; preds = %pfor.inc + tail call void @llvm.tapir.runtime.end(token %0) + br label %common.ret +} + +; Function Attrs: nounwind willreturn memory(argmem: readwrite) +declare token @llvm.tapir.runtime.start() #0 + +; Function Attrs: nounwind willreturn memory(argmem: readwrite) +declare void @llvm.tapir.runtime.end(token) #0 + +declare ptr @_Znwm() + +; uselistorder directives +uselistorder ptr null, { 1, 2, 0 } + +attributes #0 = { nounwind willreturn memory(argmem: readwrite) } diff --git a/llvm/test/Transforms/Tapir/simplify-none-syncregion.ll b/llvm/test/Transforms/Tapir/simplify-none-syncregion.ll new file mode 100644 index 000000000000..86991a3a8e4f --- /dev/null +++ b/llvm/test/Transforms/Tapir/simplify-none-syncregion.ll @@ -0,0 +1,44 @@ +; Check that task-simplify handles sync regions that are not instructions, specifically when debugging. +; +; RUN: opt < %s -passes="function(task-simplify)" -S | FileCheck %s +target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" +target triple = "arm64-apple-macosx15.0.0" + +; CHECK: define void @_ZNK5Graph17pbfs_walk_PennantEP7PennantIiERH3BagIiEjPj() +; CHECK-NEXT: entry: +; CHECK-NEXT: unreachable + +define void @_ZNK5Graph17pbfs_walk_PennantEP7PennantIiERH3BagIiEjPj() personality ptr null { +entry: + %0 = tail call token @llvm.tapir.runtime.start() + detach within none, label %pfor.body.entry.tf, label %pfor.inc unwind label %lpad59.loopexit + +pfor.body.entry.tf: ; preds = %entry + unreachable + +pfor.inc: ; preds = %entry + sync within none, label %sync.continue + +common.ret: ; preds = %sync.continue, %lpad59.loopexit + ret void + +lpad59.loopexit: ; preds = %entry + %lpad.loopexit28 = landingpad { ptr, i32 } + cleanup + br label %common.ret + +sync.continue: ; preds = %pfor.inc + tail call void @llvm.tapir.runtime.end(token %0) + br label %common.ret +} + +; Function Attrs: nounwind willreturn memory(argmem: readwrite) +declare token @llvm.tapir.runtime.start() #0 + +; Function Attrs: nounwind willreturn memory(argmem: readwrite) +declare void @llvm.tapir.runtime.end(token) #0 + +; uselistorder directives +uselistorder ptr null, { 1, 2, 0 } + +attributes #0 = { nounwind willreturn memory(argmem: readwrite) } diff --git a/llvm/test/Transforms/Tapir/simplify-taskframe-with-resume.ll b/llvm/test/Transforms/Tapir/simplify-taskframe-with-resume.ll new file mode 100644 index 000000000000..484bfd97343c --- /dev/null +++ b/llvm/test/Transforms/Tapir/simplify-taskframe-with-resume.ll @@ -0,0 +1,123 @@ +; Check that taskframes using in taskframe.resume calls are properly simplified. +; +; RUN: opt < %s -passes="function(task-simplify)" -S | FileCheck %s +target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" +target triple = "arm64-apple-macosx15.0.0" + +; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +declare void @llvm.lifetime.start.p0(i64 immarg, ptr nocapture) #0 + +; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +declare void @llvm.lifetime.end.p0(i64 immarg, ptr nocapture) #0 + +; Function Attrs: nounwind reducer_register willreturn memory(inaccessiblemem: readwrite) +declare void @llvm.reducer.register.i64(ptr, i64, ptr, ptr) #1 + +; Function Attrs: nounwind willreturn memory(argmem: readwrite) +declare token @llvm.syncregion.start() #2 + +; Function Attrs: willreturn memory(argmem: readwrite) +declare void @llvm.sync.unwind(token) #3 + +; Function Attrs: hyper_view injective nounwind strand_pure willreturn memory(inaccessiblemem: read) +declare ptr @llvm.hyper.lookup.i64(ptr, i64, ptr, ptr) #4 + +; Function Attrs: willreturn memory(argmem: readwrite) +declare void @llvm.detached.rethrow.sl_p0i32s(token, { ptr, i32 }) #3 + +; Function Attrs: nounwind reducer_unregister willreturn memory(inaccessiblemem: readwrite) +declare void @llvm.reducer.unregister(ptr) #5 + +; Function Attrs: nounwind willreturn memory(argmem: readwrite) +declare token @llvm.taskframe.create() #2 + +; CHECK: define void @_ZNK5Graph17pbfs_walk_PennantEP7PennantIiERH3BagIiEjPj() +; CHECK-NOT: %tf.i = tail call token @llvm.taskframe.create() +; CHECK-NOT: {{call|invoke}} void @llvm.taskframe.resume.sl_p0i32s(token + +; Function Attrs: inlinehint mustprogress ssp uwtable(sync) +define void @_ZNK5Graph17pbfs_walk_PennantEP7PennantIiERH3BagIiEjPj() #6 personality ptr null { +entry: + %syncreg = tail call token @llvm.syncregion.start() + %syncreg45 = tail call token @llvm.syncregion.start() + %0 = tail call token @llvm.tapir.runtime.start() + detach within %syncreg45, label %pfor.body.entry.tf, label %pfor.inc unwind label %lpad59.loopexit + +pfor.body.entry.tf: ; preds = %entry + %tf.i = tail call token @llvm.taskframe.create() + %syncreg.i = tail call token @llvm.syncregion.start() + detach within %syncreg.i, label %pfor.cond.i.strpm.detachloop.entry, label %pfor.cond.cleanup.i unwind label %lpad4924.loopexit.strpm.detachloop.unwind + +pfor.cond.i.strpm.detachloop.entry: ; preds = %pfor.body.entry.tf + %syncreg.i.strpm.detachloop = tail call token @llvm.syncregion.start() + reattach within %syncreg.i, label %pfor.cond.cleanup.i + +pfor.cond.cleanup.i: ; preds = %pfor.cond.i.strpm.detachloop.entry, %pfor.body.entry.tf + sync within %syncreg.i, label %sync.continue.i + +lpad4924.loopexit.strpm.detachloop.unwind: ; preds = %pfor.body.entry.tf + %lpad.strpm.detachloop.unwind = landingpad { ptr, i32 } + cleanup + call void @llvm.taskframe.resume.sl_p0i32s(token %tf.i, { ptr, i32 } %lpad.strpm.detachloop.unwind) + unreachable + +sync.continue.i: ; preds = %pfor.cond.cleanup.i + reattach within %syncreg45, label %pfor.inc + +pfor.inc: ; preds = %sync.continue.i, %entry + sync within %syncreg45, label %sync.continue + +lpad59.loopexit: ; preds = %entry + %lpad.loopexit28 = landingpad { ptr, i32 } + cleanup + ret void + +sync.continue: ; preds = %pfor.inc + tail call void @llvm.tapir.runtime.end(token %0) + ret void +} + +; Function Attrs: nounwind willreturn memory(argmem: readwrite) +declare token @llvm.tapir.runtime.start() #2 + +; Function Attrs: willreturn memory(argmem: readwrite) +declare void @llvm.taskframe.resume.sl_p0i32s(token, { ptr, i32 }) #3 + +; Function Attrs: nounwind willreturn memory(argmem: readwrite) +declare void @llvm.tapir.runtime.end(token) #2 + +; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) +declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #7 + +; Function Attrs: nobuiltin allocsize(0) +declare noundef nonnull ptr @_Znwm(i64 noundef) #8 + +; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) +declare i32 @llvm.smax.i32(i32, i32) #9 + +; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) +declare i32 @llvm.umax.i32(i32, i32) #9 + +; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: write) +declare void @llvm.memset.p0.i64(ptr nocapture writeonly, i8, i64, i1 immarg) #10 + +; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) +declare i32 @llvm.umin.i32(i32, i32) #9 + +; Function Attrs: nounwind willreturn memory(argmem: readwrite) +declare void @llvm.taskframe.end(token) #2 + +; uselistorder directives +uselistorder ptr null, { 1, 2, 0 } + +attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +attributes #1 = { nounwind reducer_register willreturn memory(inaccessiblemem: readwrite) } +attributes #2 = { nounwind willreturn memory(argmem: readwrite) } +attributes #3 = { willreturn memory(argmem: readwrite) } +attributes #4 = { hyper_view injective nounwind strand_pure willreturn memory(inaccessiblemem: read) } +attributes #5 = { nounwind reducer_unregister willreturn memory(inaccessiblemem: readwrite) } +attributes #6 = { inlinehint mustprogress ssp uwtable(sync) "frame-pointer"="non-leaf" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="apple-m1" "target-features"="+aes,+complxnum,+crc,+dotprod,+fp-armv8,+fp16fml,+fullfp16,+jsconv,+lse,+neon,+pauth,+ras,+rcpc,+rdm,+sha2,+sha3,+v8.1a,+v8.2a,+v8.3a,+v8.4a,+v8.5a,+v8a,+zcm,+zcz" } +attributes #7 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } +attributes #8 = { nobuiltin allocsize(0) "frame-pointer"="non-leaf" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="apple-m1" "target-features"="+aes,+complxnum,+crc,+dotprod,+fp-armv8,+fp16fml,+fullfp16,+jsconv,+lse,+neon,+pauth,+ras,+rcpc,+rdm,+sha2,+sha3,+v8.1a,+v8.2a,+v8.3a,+v8.4a,+v8.5a,+v8a,+zcm,+zcz" } +attributes #9 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } +attributes #10 = { nocallback nofree nounwind willreturn memory(argmem: write) }