Skip to content

Commit 9bd4175

Browse files
authored
Merge pull request #60258 from eeckstein/measure-optimizer-performance
SILOptimizer: add an option to profile and measure the runtime of optimization passes.
2 parents b3ac2f8 + c9c4e10 commit 9bd4175

File tree

6 files changed

+235
-30
lines changed

6 files changed

+235
-30
lines changed

include/swift/SIL/SILFunction.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,16 @@ class SILFunction
181181
/// to the binary. A pointer into the module's lookup table.
182182
StringRef Name;
183183

184+
/// A single-linked list of snapshots of the function.
185+
///
186+
/// Snapshots are copies of the current function at a given point in time.
187+
SILFunction *snapshots = nullptr;
188+
189+
/// The snapshot ID of this function.
190+
///
191+
/// 0 means, it's not a snapshot, but the original function.
192+
int snapshotID = 0;
193+
184194
/// The lowered type of the function.
185195
CanSILFunctionType LoweredType;
186196

@@ -437,11 +447,31 @@ class SILFunction
437447
/// Only for use by FunctionBuilders!
438448
void setHasOwnership(bool newValue) { HasOwnership = newValue; }
439449

450+
void setName(StringRef name) {
451+
// All the snapshots share the same name.
452+
SILFunction *sn = this;
453+
do {
454+
sn->Name = name;
455+
} while ((sn = sn->snapshots) != nullptr);
456+
}
457+
440458
public:
441459
~SILFunction();
442460

443461
SILModule &getModule() const { return Module; }
444462

463+
/// Creates a snapshot with a given `ID` from the current function.
464+
void createSnapshot(int ID);
465+
466+
/// Returns the snapshot with the given `ID` or null if no such snapshot exists.
467+
SILFunction *getSnapshot(int ID);
468+
469+
/// Restores the current function from a given snapshot.
470+
void restoreFromSnapshot(int ID);
471+
472+
/// Deletes a snapshot with the `ID`.
473+
void deleteSnapshot(int ID);
474+
445475
SILType getLoweredType() const {
446476
return SILType::getPrimitiveObjectType(LoweredType);
447477
}

include/swift/SILOptimizer/PassManager/PassManager.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "llvm/ADT/DenseMap.h"
2121
#include "llvm/ADT/SmallVector.h"
2222
#include "llvm/Support/Casting.h"
23+
#include "llvm/Support/Chrono.h"
2324
#include "llvm/Support/ErrorHandling.h"
2425
#include <vector>
2526

@@ -203,6 +204,8 @@ class SILPassManager {
203204
/// pass manager is destroyed.
204205
DeserializationNotificationHandler *deserializationNotificationHandler;
205206

207+
std::chrono::nanoseconds totalPassRuntime = std::chrono::nanoseconds(0);
208+
206209
/// C'tor. It creates and registers all analysis passes, which are defined
207210
/// in Analysis.def. This is private as it should only be used by
208211
/// ExecuteSILPipelineRequest.

lib/SIL/IR/SILFunction.cpp

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "swift/SIL/SILArgument.h"
1616
#include "swift/SIL/SILBasicBlock.h"
1717
#include "swift/SIL/SILBridgingUtils.h"
18+
#include "swift/SIL/SILCloner.h"
1819
#include "swift/SIL/SILFunction.h"
1920
#include "swift/SIL/SILInstruction.h"
2021
#include "swift/SIL/SILModule.h"
@@ -175,7 +176,7 @@ void SILFunction::init(SILLinkage Linkage, StringRef Name,
175176
IsDynamicallyReplaceable_t isDynamic,
176177
IsExactSelfClass_t isExactSelfClass,
177178
IsDistributed_t isDistributed) {
178-
this->Name = Name;
179+
setName(Name);
179180
this->LoweredType = LoweredType;
180181
this->GenericEnv = genericEnv;
181182
this->SpecializationInfo = nullptr;
@@ -215,6 +216,8 @@ SILFunction::~SILFunction() {
215216
// We also need to drop all references if instructions are allocated using
216217
// an allocator that may recycle freed memory.
217218
dropAllReferences();
219+
if (snapshots)
220+
snapshots->~SILFunction();
218221

219222
if (ReplacedFunction) {
220223
ReplacedFunction->decrementRefCount();
@@ -237,6 +240,96 @@ SILFunction::~SILFunction() {
237240
destroyFunction({this}, &libswiftSpecificData, sizeof(libswiftSpecificData));
238241
}
239242

243+
void SILFunction::createSnapshot(int id) {
244+
assert(id != 0 && "invalid snapshot ID");
245+
assert(!getSnapshot(id) && "duplicate snapshot");
246+
247+
SILFunction *newSnapshot = new (Module) SILFunction(Module,
248+
getLinkage(), getName(), getLoweredFunctionType(), getGenericEnvironment(),
249+
getLocation(), isBare(), isTransparent(), isSerialized(),
250+
getEntryCount(), isThunk(), getClassSubclassScope(),
251+
getInlineStrategy(), getEffectsKind(), getDebugScope(),
252+
isDynamicallyReplaceable(), isExactSelfClass(), isDistributed());
253+
254+
// Copy all relevant properties.
255+
// TODO: It's really unfortunate that this needs to be done manually. It would
256+
// be nice if all the properties are encapsulated into a single state,
257+
// which can be copied at once.
258+
newSnapshot->SpecializationInfo = SpecializationInfo;
259+
newSnapshot->ClangNodeOwner = ClangNodeOwner;
260+
newSnapshot->DeclCtxt = DeclCtxt;
261+
newSnapshot->Profiler = Profiler;
262+
newSnapshot->ReplacedFunction = ReplacedFunction;
263+
newSnapshot->RefAdHocRequirementFunction = RefAdHocRequirementFunction;
264+
newSnapshot->ObjCReplacementFor = ObjCReplacementFor;
265+
newSnapshot->SemanticsAttrSet = SemanticsAttrSet;
266+
newSnapshot->SpecializeAttrSet = SpecializeAttrSet;
267+
newSnapshot->Availability = Availability;
268+
newSnapshot->specialPurpose = specialPurpose;
269+
newSnapshot->perfConstraints = perfConstraints;
270+
newSnapshot->GlobalInitFlag = GlobalInitFlag;
271+
newSnapshot->HasCReferences = HasCReferences;
272+
newSnapshot->IsWeakImported = IsWeakImported;
273+
newSnapshot->HasOwnership = HasOwnership;
274+
newSnapshot->IsWithoutActuallyEscapingThunk = IsWithoutActuallyEscapingThunk;
275+
newSnapshot->OptMode = OptMode;
276+
newSnapshot->IsStaticallyLinked = IsStaticallyLinked;
277+
newSnapshot->copyEffects(this);
278+
279+
SILFunctionCloner cloner(newSnapshot);
280+
cloner.cloneFunction(this);
281+
282+
newSnapshot->snapshotID = id;
283+
newSnapshot->snapshots = this->snapshots;
284+
this->snapshots = newSnapshot;
285+
286+
// The cloner sometimes removes temporary instructions.
287+
getModule().flushDeletedInsts();
288+
}
289+
290+
SILFunction *SILFunction::getSnapshot(int ID) {
291+
SILFunction *sn = this;
292+
do {
293+
if (sn->snapshotID == ID)
294+
return sn;
295+
sn = sn->snapshots;
296+
} while (sn);
297+
return nullptr;
298+
}
299+
300+
void SILFunction::restoreFromSnapshot(int ID) {
301+
SILFunction *sn = getSnapshot(ID);
302+
assert(sn && "no snapshot found");
303+
304+
clear();
305+
SILFunctionCloner cloner(this);
306+
cloner.cloneFunction(sn);
307+
308+
// Beside the function body, only restore those properties, which are/can be
309+
// modified by passes.
310+
// TODO: There should be a clear sepratation from initialize-once properties
311+
// (`let`) and properties which can be modified by passes (`var`).
312+
copyEffects(sn);
313+
314+
// The cloner sometimes removes temporary instructions.
315+
getModule().flushDeletedInsts();
316+
}
317+
318+
void SILFunction::deleteSnapshot(int ID) {
319+
SILFunction *f = this;
320+
do {
321+
if (SILFunction *sn = f->snapshots) {
322+
if (sn->snapshotID == ID) {
323+
f->snapshots = sn->snapshots;
324+
sn->snapshots = nullptr;
325+
sn->~SILFunction();
326+
getModule().flushDeletedInsts();
327+
return;
328+
}
329+
}
330+
} while ((f = f->snapshots) != nullptr);
331+
}
332+
240333
void SILFunction::createProfiler(ASTNode Root, SILDeclRef forDecl,
241334
ForDefinition_t forDefinition) {
242335
assert(!Profiler && "Function already has a profiler");

lib/SIL/IR/SILModule.cpp

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -161,14 +161,20 @@ void SILModule::checkForLeaks() const {
161161
int instsInModule = std::distance(scheduledForDeletion.begin(),
162162
scheduledForDeletion.end());
163163
for (const SILFunction &F : *this) {
164-
for (const SILBasicBlock &block : F) {
165-
instsInModule += std::distance(block.begin(), block.end());
166-
}
164+
const SILFunction *sn = &F;
165+
do {
166+
for (const SILBasicBlock &block : *sn) {
167+
instsInModule += std::distance(block.begin(), block.end());
168+
}
169+
} while ((sn = sn->snapshots) != nullptr);
167170
}
168171
for (const SILFunction &F : zombieFunctions) {
169-
for (const SILBasicBlock &block : F) {
170-
instsInModule += std::distance(block.begin(), block.end());
171-
}
172+
const SILFunction *sn = &F;
173+
do {
174+
for (const SILBasicBlock &block : F) {
175+
instsInModule += std::distance(block.begin(), block.end());
176+
}
177+
} while ((sn = sn->snapshots) != nullptr);
172178
}
173179
for (const SILGlobalVariable &global : getSILGlobals()) {
174180
instsInModule += std::distance(global.StaticInitializerBlock.begin(),
@@ -434,13 +440,14 @@ void SILModule::invalidateSILLoaderCaches() {
434440

435441
SILFunction *SILModule::removeFromZombieList(StringRef Name) {
436442
if (auto *Zombie = ZombieFunctionTable.lookup(Name)) {
443+
assert(Zombie->snapshotID == 0 && "zombie cannot be a snapthot function");
437444
ZombieFunctionTable.erase(Name);
438445
zombieFunctions.remove(Zombie);
439446

440447
// The owner of the function's Name is the ZombieFunctionTable key, which is
441448
// freed by erase().
442449
// Make sure nobody accesses the name string after it is freed.
443-
Zombie->Name = StringRef();
450+
Zombie->setName(StringRef());
444451
return Zombie;
445452
}
446453
return nullptr;
@@ -449,6 +456,7 @@ SILFunction *SILModule::removeFromZombieList(StringRef Name) {
449456
/// Erase a function from the module.
450457
void SILModule::eraseFunction(SILFunction *F) {
451458
assert(!F->isZombie() && "zombie function is in list of alive functions");
459+
assert(F->snapshotID == 0 && "cannot erase a snapshot function");
452460

453461
llvm::StringMapEntry<SILFunction*> *entry =
454462
&*ZombieFunctionTable.insert(std::make_pair(F->getName(), nullptr)).first;
@@ -459,7 +467,7 @@ void SILModule::eraseFunction(SILFunction *F) {
459467
// the function from the table we need to use the allocated name string from
460468
// the ZombieFunctionTable.
461469
FunctionTable.erase(F->getName());
462-
F->Name = zombieName;
470+
F->setName(zombieName);
463471

464472
// The function is dead, but we need it later (at IRGen) for debug info
465473
// or vtable stub generation. So we move it into the zombie list.

0 commit comments

Comments
 (0)