Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix compiler crash when inserting fixups for taskframe outputs. #297

Merged
merged 5 commits into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions clang/lib/CodeGen/CodeGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -1523,6 +1523,8 @@ class CodeGenFunction : public CodeGenTypeCache {
~DetachScope() {
if (TempInvokeDest && TempInvokeDest->use_empty())
delete TempInvokeDest;
if (TaskFrame && TaskFrame->use_empty())
cast<llvm::Instruction>(TaskFrame)->eraseFromParent();
CGF.CurDetachScope = ParentScope;
}

Expand Down
7 changes: 7 additions & 0 deletions llvm/include/llvm/Analysis/TapirTaskInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include "llvm/IR/PassManager.h"
#include "llvm/Pass.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/Casting.h"
#include <utility>

namespace llvm {
Expand Down Expand Up @@ -101,6 +102,12 @@ class Spindle {
++D;
return D;
}
BasicBlock::iterator getTaskFrameFirstInsertionPt() {
if (Instruction *TFCreate =
dyn_cast_or_null<Instruction>(getTaskFrameCreate()))
return TFCreate->getNextNode()->getIterator();
return getEntry()->getFirstInsertionPt();
}

Task *getTaskFromTaskFrame() const;

Expand Down
96 changes: 96 additions & 0 deletions llvm/lib/IR/Verifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,8 @@ class Verifier : public InstVisitor<Verifier>, VerifierSupport {
void verifyTask(const DetachInst *DI);
void visitDetachInst(DetachInst &DI);
void visitReattachInst(ReattachInst &RI);
void visitSyncInst(SyncInst &SI);
void verifyTaskFrame(const CallBase *TF);

void verifySwiftErrorCall(CallBase &Call, const Value *SwiftErrorVal);
void verifySwiftErrorValue(const Value *SwiftErrorVal);
Expand Down Expand Up @@ -3158,6 +3160,8 @@ void Verifier::verifyTask(const DetachInst *DI) {
}

void Verifier::visitReattachInst(ReattachInst &RI) {
Check(isa<Instruction>(RI.getSyncRegion()),
"reattach has an invalid syncregion", RI);
if (DT.isReachableFromEntry(RI.getParent())) {
// Check that the continuation of the reattach has a detach predecessor.
const BasicBlock *Continue = RI.getDetachContinue();
Expand All @@ -3175,7 +3179,78 @@ void Verifier::visitReattachInst(ReattachInst &RI) {
visitTerminator(RI);
}

void Verifier::visitSyncInst(SyncInst &SI) {
Check(isa<Instruction>(SI.getSyncRegion()), "sync has an invalid syncregion",
SI);
visitTerminator(SI);
}

void Verifier::verifyTaskFrame(const CallBase *TF) {
// Gather endpoints of the taskframe.
SmallPtrSet<const BasicBlock *, 4> TFEnds;
bool IsDead = true;
for (const User *U : TF->users()) {
if (const CallBase *CB = dyn_cast<CallBase>(U)) {
if (const Function *Called = CB->getCalledFunction()) {
// All taskframe.end and taskframe.resume users directly indicate
// taskframe ends. All taskframe.use users identify spawned tasks.
if (Intrinsic::taskframe_end == Called->getIntrinsicID() ||
Intrinsic::taskframe_resume == Called->getIntrinsicID()) {
TFEnds.insert(CB->getParent());
IsDead = false;
} else if (Intrinsic::taskframe_use == Called->getIntrinsicID()) {
// Use the continuation block of the spawned task as the taskframe
// end.
if (const BasicBlock *Detacher = CB->getParent()->getUniquePredecessor()) {
if (const DetachInst *Detach = dyn_cast<DetachInst>(Detacher->getTerminator())) {
TFEnds.insert(Detach->getContinue());
IsDead = false;
}
}
// Also include the block containing the taskframe.use. If this
// taskframe is really used in a spawned task, and is not just dead
// code, then verifyTask() will check the spawned task itself.
TFEnds.insert(CB->getParent());
}
}
}
}
SmallVector<const BasicBlock *, 32> Worklist;
SmallPtrSet<const BasicBlock *, 32> Visited;
Worklist.push_back(TF->getParent());
do {
const BasicBlock *BB = Worklist.pop_back_val();
if (!Visited.insert(BB).second)
continue;

// If this block is a taskframe end, stop the traversal.
if (TFEnds.contains(BB))
continue;

// Check that do not encounter a return or resume in the middle of the
// task.
Check(IsDead || (!isa<ReturnInst>(BB->getTerminator()) &&
!isa<ResumeInst>(BB->getTerminator())),
"Unexpected return or resume in taskframe", TF, BB->getTerminator());

// Ignore the placeholder continuation of a taskframe.resume or
// detached.rethrow.
const BasicBlock *SuccToIgnore = nullptr;
if (const InvokeInst *II = dyn_cast<InvokeInst>(BB->getTerminator()))
if (isTapirIntrinsic(Intrinsic::taskframe_resume, II, TF) ||
isDetachedRethrow(II))
SuccToIgnore = II->getNormalDest();

// Add the successors of this basic block.
for (const BasicBlock *Successor : successors(BB))
if (Successor != SuccToIgnore)
Worklist.push_back(Successor);
} while (!Worklist.empty());
}

void Verifier::visitDetachInst(DetachInst &DI) {
Check(isa<Instruction>(DI.getSyncRegion()),
"detach has an invalid syncregion", DI);
if (DetachesVisited.insert(&DI).second)
verifyTask(&DI);

Expand Down Expand Up @@ -6260,6 +6335,27 @@ void Verifier::visitIntrinsicCall(Intrinsic::ID ID, CallBase &Call) {
"tapir.runtime.start has no associated tapir.runtime.end", &Call);
break;
}
case Intrinsic::taskframe_create: {
if (DT.isReachableFromEntry(Call.getParent()))
verifyTaskFrame(&Call);
break;
}
case Intrinsic::taskframe_resume: {
Check(isa<InvokeInst>(Call), "taskframe.resume is not invoked", &Call);
if (InvokeInst *I = dyn_cast<InvokeInst>(&Call)) {
Check(isa<UnreachableInst>(I->getNormalDest()->getTerminator()),
"taskframe.resume normal destination is not unreachable", &Call);
}
break;
}
case Intrinsic::detached_rethrow: {
Check(isa<InvokeInst>(Call), "detached.rethrow is not invoked", &Call);
if (InvokeInst *I = dyn_cast<InvokeInst>(&Call)) {
Check(isa<UnreachableInst>(I->getNormalDest()->getTerminator()),
"detached.rethrow normal destination is not unreachable", &Call);
}
break;
}
};

// Verify that there aren't any unmediated control transfers between funclets.
Expand Down
2 changes: 1 addition & 1 deletion llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2867,7 +2867,7 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
if (Instruction *I = dyn_cast<Instruction>(U))
if (isTapirIntrinsic(Intrinsic::taskframe_use, I) ||
isTapirIntrinsic(Intrinsic::taskframe_end, I) ||
isTaskFrameResume(I)) {
isTapirIntrinsic(Intrinsic::taskframe_resume, I)) {
++NumUsers;
break;
}
Expand Down
19 changes: 9 additions & 10 deletions llvm/lib/Transforms/Tapir/LoweringUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -923,16 +923,17 @@ Function *llvm::createHelperForTaskFrame(
// values in old function.
AddAlignmentAssumptions(&F, Args, VMap, &Header->front(), &OA.AC, &OA.DT);

SmallVector<Instruction *, 4> TaskEnds;
// Move allocas in the newly cloned detached CFG to the entry block of the
// helper.
{
NamedRegionTimer NRT("MoveAllocas", "Move allocas in cloned helper",
TimerGroupName, TimerGroupDescription,
TimePassesIsEnabled);
// Collect the end instructions of the task.
SmallVector<Instruction *, 4> TaskEnds;
for (BasicBlock *EndBlock : TFEndBlocks)
TaskEnds.push_back(cast<BasicBlock>(VMap[EndBlock])->getTerminator());
TaskEnds.push_back(
cast<BasicBlock>(VMap[EndBlock])->getTerminator()->getPrevNode());
for (BasicBlock *EndBlock : TFResumeBlocks)
TaskEnds.push_back(cast<BasicBlock>(VMap[EndBlock])->getTerminator());

Expand All @@ -953,14 +954,12 @@ Function *llvm::createHelperForTaskFrame(
TimerGroupName, TimerGroupDescription,
TimePassesIsEnabled);
SmallVector<Instruction *, 1> TFEndsToRemove;
for (BasicBlock *EndBlock : TFEndBlocks) {
BasicBlock *ClonedEndBlock = cast<BasicBlock>(VMap[EndBlock]);
if (Instruction *Prev = ClonedEndBlock->getTerminator()->getPrevNode())
if (isTapirIntrinsic(Intrinsic::taskframe_end, Prev))
TFEndsToRemove.push_back(Prev);
}
for (Instruction *ClonedTFEnd : TFEndsToRemove)
ClonedTFEnd->eraseFromParent();
for (Instruction *TFEnd : TaskEnds)
if (isTapirIntrinsic(Intrinsic::taskframe_end, TFEnd))
TFEndsToRemove.push_back(TFEnd);

for (Instruction *TFEnd : TFEndsToRemove)
TFEnd->eraseFromParent();
}

Helper->setMemoryEffects(computeFunctionBodyMemoryAccess(*Helper, OA.AA));
Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/Transforms/Tapir/TapirToTarget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ TapirToTargetImpl::outlineAllTasks(Function &F,
for (Spindle *SubTF : TF->subtaskframes())
TFToOutline[SubTF].remapOutlineInfo(VMap, InputMap);

if (Instruction *ClonedTFCreate =
dyn_cast<Instruction>(VMap[TF->getTaskFrameCreate()]))
ClonedTFCreate->eraseFromParent();

continue;
}

Expand Down
30 changes: 20 additions & 10 deletions llvm/lib/Transforms/Utils/InlineFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2443,6 +2443,16 @@ static void HandleInlinedResumeInTask(BasicBlock *EntryBlock, BasicBlock *Ctx,
}
}

// Simple RAII object for managing creation of taskframe for inlined function.
struct TaskFrameScope {
CallInst *TFCreate = nullptr;
TaskFrameScope() = default;
~TaskFrameScope() {
if (TFCreate && TFCreate->use_empty())
TFCreate->eraseFromParent();
}
};

/// This function inlines the called function into the basic block of the
/// caller. This returns false if it is not possible to inline this call.
/// The program is still in a well defined state if this occurs though.
Expand Down Expand Up @@ -3084,7 +3094,7 @@ llvm::InlineResult llvm::InlineFunction(CallBase &CB, InlineFunctionInfo &IFI,

// If the inlined code contained dynamic alloca instructions, wrap the inlined
// code with llvm.stacksave/llvm.stackrestore intrinsics.
CallInst *TFCreate = nullptr;
TaskFrameScope TFI;
BasicBlock *TFEntryBlock = DetachedCtxEntryBlock;
if (InlinedFunctionInfo.ContainsDetach &&
(InlinedFunctionInfo.ContainsDynamicAllocas || MayBeUnsyncedAtCall)) {
Expand All @@ -3094,9 +3104,9 @@ llvm::InlineResult llvm::InlineFunction(CallBase &CB, InlineFunctionInfo &IFI,
Intrinsic::getDeclaration(M, Intrinsic::taskframe_create);

// Insert the llvm.taskframe.create.
TFCreate = IRBuilder<>(&*FirstNewBlock, FirstNewBlock->begin())
.CreateCall(TFCreateFn, {}, "tf.i");
TFCreate->setDebugLoc(CB.getDebugLoc());
TFI.TFCreate = IRBuilder<>(&*FirstNewBlock, FirstNewBlock->begin())
.CreateCall(TFCreateFn, {}, "tf.i");
TFI.TFCreate->setDebugLoc(CB.getDebugLoc());
TFEntryBlock = &*FirstNewBlock;

// If we're inlining an invoke, insert a taskframe.resume at the unwind
Expand All @@ -3112,9 +3122,9 @@ llvm::InlineResult llvm::InlineFunction(CallBase &CB, InlineFunctionInfo &IFI,
}

// Create an unwind edge for the taskframe.
BasicBlock *TaskFrameUnwindEdge = CreateSubTaskUnwindEdge(
Intrinsic::taskframe_resume, TFCreate, UnwindEdge,
UnreachableBlk, II);
BasicBlock *TaskFrameUnwindEdge =
CreateSubTaskUnwindEdge(Intrinsic::taskframe_resume, TFI.TFCreate,
UnwindEdge, UnreachableBlk, II);

for (PHINode &PN : UnwindEdge->phis())
PN.replaceIncomingBlockWith(II->getParent(), TaskFrameUnwindEdge);
Expand Down Expand Up @@ -3149,7 +3159,7 @@ llvm::InlineResult llvm::InlineFunction(CallBase &CB, InlineFunctionInfo &IFI,
BasicBlock *UnwindDest = II->getUnwindDest();
Instruction *FirstNonPHI = UnwindDest->getFirstNonPHI();
if (isa<LandingPadInst>(FirstNonPHI)) {
HandleInlinedLandingPad(II, &*FirstNewBlock, TFCreate,
HandleInlinedLandingPad(II, &*FirstNewBlock, TFI.TFCreate,
InlinedFunctionInfo);
} else {
HandleInlinedEHPad(II, &*FirstNewBlock, InlinedFunctionInfo);
Expand Down Expand Up @@ -3419,10 +3429,10 @@ llvm::InlineResult llvm::InlineFunction(CallBase &CB, InlineFunctionInfo &IFI,

// If we inserted a taskframe.create, insert a taskframe.end at the start of
// AfterCallBB.
if (TFCreate) {
if (TFI.TFCreate) {
Function *TFEndFn = Intrinsic::getDeclaration(Caller->getParent(),
Intrinsic::taskframe_end);
IRBuilder<>(&AfterCallBB->front()).CreateCall(TFEndFn, TFCreate);
IRBuilder<>(&AfterCallBB->front()).CreateCall(TFEndFn, TFI.TFCreate);
}

// Change the branch that used to go to AfterCallBB to branch to the first
Expand Down
5 changes: 3 additions & 2 deletions llvm/lib/Transforms/Utils/SimplifyCFG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7860,8 +7860,9 @@ static bool serializeDetachToImmediateSync(BasicBlock *BB,

// Move static alloca instructions in the detached block to the
// appropriate entry block.
MoveStaticAllocasInBlock(cast<Instruction>(SyncRegion)->getParent(),
Detached, ReattachPreds);
if (isa<Instruction>(SyncRegion))
MoveStaticAllocasInBlock(cast<Instruction>(SyncRegion)->getParent(),
Detached, ReattachPreds);

// Erase any instructions marked to be erased.
for (Instruction *I : ToErase)
Expand Down
Loading
Loading