Skip to content

Commit 69431dc

Browse files
author
tnowicki
committed
[Coroutines][NFC] Refactor CoroCloner
* Move CoroCloner to its own header. For now the header is located in lib/Transforms/Coroutines * Change private to protected to allow inheritance * Create CoroSwitchCloner and move some of the switch specific code into this cloner. * This continus previous work done to CoroFrame. The goal is to isolate the cloner code required for each ABI as much as is reasonable. The base class should include a useful set of helpers and base implementations. More changes will follow.
1 parent e3ff649 commit 69431dc

File tree

2 files changed

+163
-133
lines changed

2 files changed

+163
-133
lines changed
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
//
2+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
3+
// See https://llvm.org/LICENSE.txt for license information.
4+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5+
//
6+
//===----------------------------------------------------------------------===//
7+
// Helper class for splitting a coroutine into separate functions. For example
8+
// the returned-continuation coroutine is split into separate continuation
9+
// functions.
10+
//===----------------------------------------------------------------------===//
11+
12+
#pragma once
13+
14+
#include "llvm/IR/Function.h"
15+
#include "llvm/IR/IRBuilder.h"
16+
#include "llvm/Support/TimeProfiler.h"
17+
#include "llvm/Transforms/Coroutines/ABI.h"
18+
#include "llvm/Transforms/Coroutines/CoroInstr.h"
19+
#include "llvm/Transforms/Utils/ValueMapper.h"
20+
21+
using namespace llvm;
22+
23+
class CoroCloner {
24+
public:
25+
enum class Kind {
26+
/// The shared resume function for a switch lowering.
27+
SwitchResume,
28+
29+
/// The shared unwind function for a switch lowering.
30+
SwitchUnwind,
31+
32+
/// The shared cleanup function for a switch lowering.
33+
SwitchCleanup,
34+
35+
/// An individual continuation function.
36+
Continuation,
37+
38+
/// An async resume function.
39+
Async,
40+
};
41+
42+
protected:
43+
Function &OrigF;
44+
const Twine &Suffix;
45+
coro::Shape &Shape;
46+
Kind FKind;
47+
IRBuilder<> Builder;
48+
TargetTransformInfo &TTI;
49+
50+
ValueToValueMapTy VMap;
51+
Function *NewF = nullptr;
52+
Value *NewFramePtr = nullptr;
53+
54+
55+
/// The active suspend instruction; meaningful only for continuation and async
56+
/// ABIs.
57+
AnyCoroSuspendInst *ActiveSuspend = nullptr;
58+
59+
/// Create a cloner for a continuation lowering.
60+
CoroCloner(Function &OrigF, const Twine &Suffix, coro::Shape &Shape,
61+
Function *NewF, AnyCoroSuspendInst *ActiveSuspend,
62+
TargetTransformInfo &TTI)
63+
: OrigF(OrigF), Suffix(Suffix), Shape(Shape),
64+
FKind(Shape.ABI == coro::ABI::Async ? Kind::Async : Kind::Continuation),
65+
Builder(OrigF.getContext()), TTI(TTI), NewF(NewF), ActiveSuspend(ActiveSuspend) {
66+
assert(Shape.ABI == coro::ABI::Retcon ||
67+
Shape.ABI == coro::ABI::RetconOnce || Shape.ABI == coro::ABI::Async);
68+
assert(NewF && "need existing function for continuation");
69+
assert(ActiveSuspend && "need active suspend point for continuation");
70+
}
71+
72+
public:
73+
CoroCloner(Function &OrigF, const Twine &Suffix, coro::Shape &Shape,
74+
Kind FKind, TargetTransformInfo &TTI) :
75+
OrigF(OrigF), Suffix(Suffix), Shape(Shape), FKind(FKind), Builder(OrigF.getContext()), TTI(TTI) {
76+
}
77+
78+
virtual ~CoroCloner() { }
79+
80+
/// Create a clone for a continuation lowering.
81+
static Function *createClone(Function &OrigF, const Twine &Suffix,
82+
coro::Shape &Shape, Function *NewF,
83+
AnyCoroSuspendInst *ActiveSuspend,
84+
TargetTransformInfo &TTI) {
85+
assert(Shape.ABI == coro::ABI::Retcon ||
86+
Shape.ABI == coro::ABI::RetconOnce || Shape.ABI == coro::ABI::Async);
87+
TimeTraceScope FunctionScope("CoroCloner");
88+
89+
CoroCloner Cloner(OrigF, Suffix, Shape, NewF, ActiveSuspend, TTI);
90+
Cloner.create();
91+
return Cloner.getFunction();
92+
}
93+
94+
Function *getFunction() const {
95+
assert(NewF != nullptr && "declaration not yet set");
96+
return NewF;
97+
}
98+
99+
virtual void create();
100+
101+
protected:
102+
bool isSwitchDestroyFunction() {
103+
switch (FKind) {
104+
case Kind::Async:
105+
case Kind::Continuation:
106+
case Kind::SwitchResume:
107+
return false;
108+
case Kind::SwitchUnwind:
109+
case Kind::SwitchCleanup:
110+
return true;
111+
}
112+
llvm_unreachable("Unknown CoroCloner::Kind enum");
113+
}
114+
115+
void replaceEntryBlock();
116+
Value *deriveNewFramePointer();
117+
void replaceRetconOrAsyncSuspendUses();
118+
void replaceCoroSuspends();
119+
void replaceCoroEnds();
120+
void replaceSwiftErrorOps();
121+
void salvageDebugInfo();
122+
void handleFinalSuspend();
123+
};
124+
125+
126+
class CoroSwitchCloner : public CoroCloner {
127+
protected:
128+
/// Create a cloner for a switch lowering.
129+
CoroSwitchCloner(Function &OrigF, const Twine &Suffix, coro::Shape &Shape,
130+
Kind FKind, TargetTransformInfo &TTI)
131+
: CoroCloner(OrigF, Suffix, Shape, FKind, TTI) { }
132+
133+
void create() override;
134+
135+
public:
136+
/// Create a clone for a switch lowering.
137+
static Function *createClone(Function &OrigF, const Twine &Suffix,
138+
coro::Shape &Shape, Kind FKind,
139+
TargetTransformInfo &TTI) {
140+
assert(Shape.ABI == coro::ABI::Switch);
141+
TimeTraceScope FunctionScope("CoroCloner");
142+
143+
CoroSwitchCloner Cloner(OrigF, Suffix, Shape, FKind, TTI);
144+
Cloner.create();
145+
return Cloner.getFunction();
146+
}
147+
};

llvm/lib/Transforms/Coroutines/CoroSplit.cpp

Lines changed: 16 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
#include "llvm/Transforms/Coroutines/CoroSplit.h"
2222
#include "CoroInternal.h"
23+
#include "CoroCloner.h"
2324
#include "llvm/ADT/DenseMap.h"
2425
#include "llvm/ADT/PriorityWorklist.h"
2526
#include "llvm/ADT/STLExtras.h"
@@ -44,10 +45,8 @@
4445
#include "llvm/IR/DataLayout.h"
4546
#include "llvm/IR/DerivedTypes.h"
4647
#include "llvm/IR/Dominators.h"
47-
#include "llvm/IR/Function.h"
4848
#include "llvm/IR/GlobalValue.h"
4949
#include "llvm/IR/GlobalVariable.h"
50-
#include "llvm/IR/IRBuilder.h"
5150
#include "llvm/IR/InstIterator.h"
5251
#include "llvm/IR/InstrTypes.h"
5352
#include "llvm/IR/Instruction.h"
@@ -61,17 +60,13 @@
6160
#include "llvm/Support/Casting.h"
6261
#include "llvm/Support/Debug.h"
6362
#include "llvm/Support/PrettyStackTrace.h"
64-
#include "llvm/Support/TimeProfiler.h"
6563
#include "llvm/Support/raw_ostream.h"
66-
#include "llvm/Transforms/Coroutines/ABI.h"
67-
#include "llvm/Transforms/Coroutines/CoroInstr.h"
6864
#include "llvm/Transforms/Coroutines/MaterializationUtils.h"
6965
#include "llvm/Transforms/Scalar.h"
7066
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
7167
#include "llvm/Transforms/Utils/CallGraphUpdater.h"
7268
#include "llvm/Transforms/Utils/Cloning.h"
7369
#include "llvm/Transforms/Utils/Local.h"
74-
#include "llvm/Transforms/Utils/ValueMapper.h"
7570
#include <cassert>
7671
#include <cstddef>
7772
#include <cstdint>
@@ -82,122 +77,6 @@ using namespace llvm;
8277

8378
#define DEBUG_TYPE "coro-split"
8479

85-
namespace {
86-
87-
/// A little helper class for building
88-
class CoroCloner {
89-
public:
90-
enum class Kind {
91-
/// The shared resume function for a switch lowering.
92-
SwitchResume,
93-
94-
/// The shared unwind function for a switch lowering.
95-
SwitchUnwind,
96-
97-
/// The shared cleanup function for a switch lowering.
98-
SwitchCleanup,
99-
100-
/// An individual continuation function.
101-
Continuation,
102-
103-
/// An async resume function.
104-
Async,
105-
};
106-
107-
private:
108-
Function &OrigF;
109-
Function *NewF;
110-
const Twine &Suffix;
111-
coro::Shape &Shape;
112-
Kind FKind;
113-
ValueToValueMapTy VMap;
114-
IRBuilder<> Builder;
115-
Value *NewFramePtr = nullptr;
116-
117-
/// The active suspend instruction; meaningful only for continuation and async
118-
/// ABIs.
119-
AnyCoroSuspendInst *ActiveSuspend = nullptr;
120-
121-
TargetTransformInfo &TTI;
122-
123-
/// Create a cloner for a switch lowering.
124-
CoroCloner(Function &OrigF, const Twine &Suffix, coro::Shape &Shape,
125-
Kind FKind, TargetTransformInfo &TTI)
126-
: OrigF(OrigF), NewF(nullptr), Suffix(Suffix), Shape(Shape), FKind(FKind),
127-
Builder(OrigF.getContext()), TTI(TTI) {
128-
assert(Shape.ABI == coro::ABI::Switch);
129-
}
130-
131-
/// Create a cloner for a continuation lowering.
132-
CoroCloner(Function &OrigF, const Twine &Suffix, coro::Shape &Shape,
133-
Function *NewF, AnyCoroSuspendInst *ActiveSuspend,
134-
TargetTransformInfo &TTI)
135-
: OrigF(OrigF), NewF(NewF), Suffix(Suffix), Shape(Shape),
136-
FKind(Shape.ABI == coro::ABI::Async ? Kind::Async : Kind::Continuation),
137-
Builder(OrigF.getContext()), ActiveSuspend(ActiveSuspend), TTI(TTI) {
138-
assert(Shape.ABI == coro::ABI::Retcon ||
139-
Shape.ABI == coro::ABI::RetconOnce || Shape.ABI == coro::ABI::Async);
140-
assert(NewF && "need existing function for continuation");
141-
assert(ActiveSuspend && "need active suspend point for continuation");
142-
}
143-
144-
public:
145-
/// Create a clone for a switch lowering.
146-
static Function *createClone(Function &OrigF, const Twine &Suffix,
147-
coro::Shape &Shape, Kind FKind,
148-
TargetTransformInfo &TTI) {
149-
TimeTraceScope FunctionScope("CoroCloner");
150-
151-
CoroCloner Cloner(OrigF, Suffix, Shape, FKind, TTI);
152-
Cloner.create();
153-
return Cloner.getFunction();
154-
}
155-
156-
/// Create a clone for a continuation lowering.
157-
static Function *createClone(Function &OrigF, const Twine &Suffix,
158-
coro::Shape &Shape, Function *NewF,
159-
AnyCoroSuspendInst *ActiveSuspend,
160-
TargetTransformInfo &TTI) {
161-
TimeTraceScope FunctionScope("CoroCloner");
162-
163-
CoroCloner Cloner(OrigF, Suffix, Shape, NewF, ActiveSuspend, TTI);
164-
Cloner.create();
165-
return Cloner.getFunction();
166-
}
167-
168-
Function *getFunction() const {
169-
assert(NewF != nullptr && "declaration not yet set");
170-
return NewF;
171-
}
172-
173-
void create();
174-
175-
private:
176-
bool isSwitchDestroyFunction() {
177-
switch (FKind) {
178-
case Kind::Async:
179-
case Kind::Continuation:
180-
case Kind::SwitchResume:
181-
return false;
182-
case Kind::SwitchUnwind:
183-
case Kind::SwitchCleanup:
184-
return true;
185-
}
186-
llvm_unreachable("Unknown CoroCloner::Kind enum");
187-
}
188-
189-
void replaceEntryBlock();
190-
Value *deriveNewFramePointer();
191-
void replaceRetconOrAsyncSuspendUses();
192-
void replaceCoroSuspends();
193-
void replaceCoroEnds();
194-
void replaceSwiftErrorOps();
195-
void salvageDebugInfo();
196-
void handleFinalSuspend();
197-
};
198-
199-
} // end anonymous namespace
200-
20180
// FIXME:
20281
// Lower the intrinisc in CoroEarly phase if coroutine frame doesn't escape
20382
// and it is known that other transformations, for example, sanitizers
@@ -985,11 +864,7 @@ static void addSwiftSelfAttrs(AttributeList &Attrs, LLVMContext &Context,
985864
/// Clone the body of the original function into a resume function of
986865
/// some sort.
987866
void CoroCloner::create() {
988-
// Create the new function if we don't already have one.
989-
if (!NewF) {
990-
NewF = createCloneDeclaration(OrigF, Shape, Suffix,
991-
OrigF.getParent()->end(), ActiveSuspend);
992-
}
867+
assert(NewF);
993868

994869
// Replace all args with dummy instructions. If an argument is the old frame
995870
// pointer, the dummy will be replaced by the new frame pointer once it is
@@ -1213,12 +1088,20 @@ void CoroCloner::create() {
12131088

12141089
// Salvage debug info that points into the coroutine frame.
12151090
salvageDebugInfo();
1091+
}
1092+
1093+
void CoroSwitchCloner::create() {
1094+
// Create a new function matching the original type
1095+
NewF = createCloneDeclaration(OrigF, Shape, Suffix,
1096+
OrigF.getParent()->end(), ActiveSuspend);
1097+
1098+
// Clone the function
1099+
CoroCloner::create();
12161100

12171101
// Eliminate coro.free from the clones, replacing it with 'null' in cleanup,
12181102
// to suppress deallocation code.
1219-
if (Shape.ABI == coro::ABI::Switch)
1220-
coro::replaceCoroFree(cast<CoroIdInst>(VMap[Shape.CoroBegin->getId()]),
1221-
/*Elide=*/FKind == CoroCloner::Kind::SwitchCleanup);
1103+
coro::replaceCoroFree(cast<CoroIdInst>(VMap[Shape.CoroBegin->getId()]),
1104+
/*Elide=*/FKind == CoroCloner::Kind::SwitchCleanup);
12221105
}
12231106

12241107
static void updateAsyncFuncPointerContextSize(coro::Shape &Shape) {
@@ -1495,11 +1378,11 @@ struct SwitchCoroutineSplitter {
14951378
// setting new entry block and replacing coro.suspend an appropriate value
14961379
// to force resume or cleanup pass for every suspend point.
14971380
createResumeEntryBlock(F, Shape);
1498-
auto *ResumeClone = CoroCloner::createClone(
1381+
auto *ResumeClone = CoroSwitchCloner::createClone(
14991382
F, ".resume", Shape, CoroCloner::Kind::SwitchResume, TTI);
1500-
auto *DestroyClone = CoroCloner::createClone(
1383+
auto *DestroyClone = CoroSwitchCloner::createClone(
15011384
F, ".destroy", Shape, CoroCloner::Kind::SwitchUnwind, TTI);
1502-
auto *CleanupClone = CoroCloner::createClone(
1385+
auto *CleanupClone = CoroSwitchCloner::createClone(
15031386
F, ".cleanup", Shape, CoroCloner::Kind::SwitchCleanup, TTI);
15041387

15051388
postSplitCleanup(*ResumeClone);

0 commit comments

Comments
 (0)