Skip to content

Commit 1010f1e

Browse files
committed
libswift: infrastructure to define "instruction" passes.
Instruction passes are basically visit functions in SILCombine for a specific instruction type. With the macro SWIFT_INSTRUCTION_PASS such a pass can be declared in Passes.def. SILCombine then calls the run function of the pass in libswift.
1 parent 6fe7196 commit 1010f1e

File tree

9 files changed

+196
-10
lines changed

9 files changed

+196
-10
lines changed

include/swift/SIL/SILInstructionWorklist.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535

3636
#include "swift/Basic/BlotSetVector.h"
3737
#include "swift/SIL/SILInstruction.h"
38+
#include "swift/SIL/InstructionUtils.h"
3839
#include "swift/SIL/SILValue.h"
3940
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
4041
#include "llvm/ADT/DenseMap.h"
@@ -65,13 +66,20 @@ template <typename VectorT = std::vector<SILInstruction *>,
6566
class SILInstructionWorklist : SILInstructionWorklistBase {
6667
BlotSetVector<SILInstruction *, VectorT, MapT> worklist;
6768

69+
/// For invoking Swift instruction passes in libswift.
70+
LibswiftPassInvocation *libswiftPassInvocation = nullptr;
71+
6872
void operator=(const SILInstructionWorklist &rhs) = delete;
6973
SILInstructionWorklist(const SILInstructionWorklist &worklist) = delete;
7074

7175
public:
7276
SILInstructionWorklist(const char *loggingName = "InstructionWorklist")
7377
: SILInstructionWorklistBase(loggingName) {}
7478

79+
void setLibswiftPassInvocation(LibswiftPassInvocation *invocation) {
80+
libswiftPassInvocation = invocation;
81+
}
82+
7583
/// Returns true if the worklist is empty.
7684
bool isEmpty() const { return worklist.empty(); }
7785

include/swift/SILOptimizer/OptimizerBridging.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,20 @@ typedef struct {
2424
BridgedPassContext passContext;
2525
} BridgedFunctionPassCtxt;
2626

27+
typedef struct {
28+
BridgedInstruction instruction;
29+
BridgedPassContext passContext;
30+
} BridgedInstructionPassCtxt;
31+
2732
typedef void (*BridgedFunctionPassRunFn)(BridgedFunctionPassCtxt);
33+
typedef void (*BridgedInstructionPassRunFn)(BridgedInstructionPassCtxt);
34+
2835
void SILPassManager_registerFunctionPass(BridgedStringRef name,
2936
BridgedFunctionPassRunFn runFn);
3037

38+
void SILCombine_registerInstructionPass(BridgedStringRef name,
39+
BridgedInstructionPassRunFn runFn);
40+
3141
#ifdef __cplusplus
3242
} // extern "C"
3343
#endif

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,31 @@
6464
SWIFT_FUNCTION_PASS(Id, Tag, Description)
6565
#endif
6666

67+
/// SWIFT_INSTRUCTION_PASS(Inst, Tag)
68+
/// Similar to SWIFT_FUNCTION_PASS, but defines an instruction pass which is
69+
/// implemented in libswift and is run by the SILCombiner.
70+
/// The \p Inst argument specifies the instruction class, and \p Tag a name
71+
/// for the pass.
72+
///
73+
/// No further code is need on the C++ side. In libswift an instruction pass
74+
/// with the same name must be registered with 'registerPass()'.
75+
///
76+
#ifndef SWIFT_INSTRUCTION_PASS
77+
#define SWIFT_INSTRUCTION_PASS(Inst, Tag)
78+
#endif
79+
80+
/// SWIFT_INSTRUCTION_PASS_WITH_LEGACY(Inst, Tag)
81+
/// Like SWIFT_INSTRUCTION_PASS, but the a C++ legacy SILCombine visit
82+
/// function is used if the not
83+
/// built with libswift.
84+
/// The C++ legacy visit function must be named
85+
/// 'SILCombiner::legacyVisit<Inst>'.
86+
///
87+
#ifndef SWIFT_INSTRUCTION_PASS_WITH_LEGACY
88+
#define SWIFT_INSTRUCTION_PASS_WITH_LEGACY(Inst, Tag) \
89+
SWIFT_INSTRUCTION_PASS(Inst, Tag)
90+
#endif
91+
6792
/// PASS_RANGE(RANGE_ID, START, END)
6893
/// Pass IDs between PassKind::START and PassKind::END, inclusive,
6994
/// fall within the set known as
@@ -399,5 +424,7 @@ PASS_RANGE(AllPasses, AADumper, PruneVTables)
399424
#undef IRGEN_PASS
400425
#undef SWIFT_FUNCTION_PASS
401426
#undef SWIFT_FUNCTION_PASS_WITH_LEGACY
427+
#undef SWIFT_INSTRUCTION_PASS
428+
#undef SWIFT_INSTRUCTION_PASS_WITH_LEGACY
402429
#undef PASS
403430
#undef PASS_RANGE

lib/SILOptimizer/PassManager/PassManager.cpp

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1099,14 +1099,6 @@ FixedSizeSlab *LibswiftPassInvocation::freeSlab(FixedSizeSlab *slab) {
10991099
return prev;
11001100
}
11011101

1102-
void LibswiftPassInvocation::eraseInstruction(SILInstruction *inst) {
1103-
if (silCombiner) {
1104-
// TODO
1105-
} else {
1106-
inst->eraseFromParent();
1107-
}
1108-
}
1109-
11101102
void LibswiftPassInvocation::finishedPassRun() {
11111103
assert(allocatedSlabs.empty() && "StackList is leaking slabs");
11121104
}

lib/SILOptimizer/SILCombiner/SILCombine.cpp

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,14 @@
2323
#include "SILCombiner.h"
2424
#include "swift/SIL/BasicBlockDatastructures.h"
2525
#include "swift/SIL/DebugUtils.h"
26+
#include "swift/SIL/SILBridgingUtils.h"
2627
#include "swift/SIL/SILBuilder.h"
2728
#include "swift/SIL/SILVisitor.h"
2829
#include "swift/SILOptimizer/Analysis/AliasAnalysis.h"
2930
#include "swift/SILOptimizer/Analysis/DominanceAnalysis.h"
3031
#include "swift/SILOptimizer/Analysis/NonLocalAccessBlockAnalysis.h"
3132
#include "swift/SILOptimizer/Analysis/SimplifyInstruction.h"
32-
#include "swift/SILOptimizer/PassManager/Passes.h"
33+
#include "swift/SILOptimizer/PassManager/PassManager.h"
3334
#include "swift/SILOptimizer/PassManager/Transforms.h"
3435
#include "swift/SILOptimizer/Utils/CanonicalOSSALifetime.h"
3536
#include "swift/SILOptimizer/Utils/CanonicalizeInstruction.h"
@@ -378,6 +379,56 @@ void SILCombiner::eraseInstIncludingUsers(SILInstruction *inst) {
378379
eraseInstFromFunction(*inst);
379380
}
380381

382+
/// Runs an instruction pass in libswift.
383+
void SILCombiner::runSwiftInstructionPass(SILInstruction *inst,
384+
void (*runFunction)(BridgedInstructionPassCtxt)) {
385+
Worklist.setLibswiftPassInvocation(&libswiftPassInvocation);
386+
runFunction({ {inst->asSILNode()}, {&libswiftPassInvocation} });
387+
Worklist.setLibswiftPassInvocation(nullptr);
388+
libswiftPassInvocation.finishedPassRun();
389+
}
390+
391+
/// Registered briged instruction pass run functions.
392+
static llvm::StringMap<BridgedInstructionPassRunFn> libswiftInstPasses;
393+
static bool passesRegistered = false;
394+
395+
// Called from libswift's initializeLibSwift().
396+
void SILCombine_registerInstructionPass(BridgedStringRef name,
397+
BridgedInstructionPassRunFn runFn) {
398+
libswiftInstPasses[getStringRef(name)] = runFn;
399+
passesRegistered = true;
400+
}
401+
402+
#define SWIFT_INSTRUCTION_PASS_COMMON(INST, TAG, LEGACY_RUN) \
403+
SILInstruction *SILCombiner::visit##INST(INST *inst) { \
404+
static BridgedInstructionPassRunFn runFunction = nullptr; \
405+
static bool runFunctionSet = false; \
406+
if (!runFunctionSet) { \
407+
runFunction = libswiftInstPasses[TAG]; \
408+
if (!runFunction && passesRegistered) { \
409+
llvm::errs() << "Swift pass " << TAG << " is not registered\n"; \
410+
abort(); \
411+
} \
412+
runFunctionSet = true; \
413+
} \
414+
if (!runFunction) { \
415+
LEGACY_RUN; \
416+
} \
417+
runSwiftInstructionPass(inst, runFunction); \
418+
return nullptr; \
419+
} \
420+
421+
#define PASS(ID, TAG, DESCRIPTION)
422+
423+
#define SWIFT_INSTRUCTION_PASS(INST, TAG) \
424+
SWIFT_INSTRUCTION_PASS_COMMON(INST, TAG, { return nullptr; })
425+
426+
#define SWIFT_INSTRUCTION_PASS_WITH_LEGACY(INST, TAG) \
427+
SWIFT_INSTRUCTION_PASS_COMMON(INST, TAG, { return legacyVisit##INST(inst); })
428+
429+
#include "swift/SILOptimizer/PassManager/Passes.def"
430+
431+
#undef SWIFT_INSTRUCTION_PASS_COMMON
381432

382433
//===----------------------------------------------------------------------===//
383434
// Entry Points
@@ -419,3 +470,15 @@ class SILCombine : public SILFunctionTransform {
419470
SILTransform *swift::createSILCombine() {
420471
return new SILCombine();
421472
}
473+
474+
//===----------------------------------------------------------------------===//
475+
// SwiftFunctionPassContext
476+
//===----------------------------------------------------------------------===//
477+
478+
void LibswiftPassInvocation::eraseInstruction(SILInstruction *inst) {
479+
if (silCombiner) {
480+
silCombiner->eraseInstFromFunction(*inst);
481+
} else {
482+
inst->eraseFromParent();
483+
}
484+
}

lib/SILOptimizer/SILCombiner/SILCombiner.h

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,17 @@
2828
#include "swift/SIL/SILInstructionWorklist.h"
2929
#include "swift/SIL/SILValue.h"
3030
#include "swift/SIL/SILVisitor.h"
31+
#include "swift/SIL/InstructionUtils.h"
3132
#include "swift/SILOptimizer/Analysis/ClassHierarchyAnalysis.h"
3233
#include "swift/SILOptimizer/Analysis/NonLocalAccessBlockAnalysis.h"
3334
#include "swift/SILOptimizer/Analysis/ProtocolConformanceAnalysis.h"
35+
#include "swift/SILOptimizer/OptimizerBridging.h"
3436
#include "swift/SILOptimizer/Utils/CastOptimizer.h"
3537
#include "swift/SILOptimizer/Utils/Existential.h"
3638
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
3739
#include "swift/SILOptimizer/Utils/OwnershipOptUtils.h"
40+
#include "swift/SILOptimizer/PassManager/PassManager.h"
41+
3842
#include "llvm/ADT/DenseMap.h"
3943
#include "llvm/ADT/SmallVector.h"
4044

@@ -101,6 +105,9 @@ class SILCombiner :
101105

102106
/// External context struct used by \see ownershipRAUWHelper.
103107
OwnershipFixupContext ownershipFixupContext;
108+
109+
/// For invoking Swift instruction passes in libswift.
110+
LibswiftPassInvocation libswiftPassInvocation;
104111

105112
public:
106113
SILCombiner(SILFunctionTransform *parentTransform,
@@ -145,7 +152,8 @@ class SILCombiner :
145152
/* EraseAction */
146153
[&](SILInstruction *I) { eraseInstFromFunction(*I); }),
147154
deBlocks(&B.getFunction()),
148-
ownershipFixupContext(getInstModCallbacks(), deBlocks) {}
155+
ownershipFixupContext(getInstModCallbacks(), deBlocks),
156+
libswiftPassInvocation(parentTransform->getPassManager(), this) {}
149157

150158
bool runOnFunction(SILFunction &F);
151159

@@ -323,6 +331,12 @@ class SILCombiner :
323331
SILInstruction *
324332
visitConvertEscapeToNoEscapeInst(ConvertEscapeToNoEscapeInst *Cvt);
325333

334+
#define PASS(ID, TAG, DESCRIPTION)
335+
#define SWIFT_FUNCTION_PASS(ID, TAG, DESCRIPTION)
336+
#define SWIFT_INSTRUCTION_PASS(INST, TAG) \
337+
SILInstruction *visit##INST(INST *);
338+
#include "swift/SILOptimizer/PassManager/Passes.def"
339+
326340
/// Instruction visitor helpers.
327341
SILInstruction *optimizeBuiltinCanBeObjCClass(BuiltinInst *AI);
328342

@@ -453,6 +467,10 @@ class SILCombiner :
453467
bool hasOwnership() const {
454468
return Builder.hasOwnership();
455469
}
470+
471+
void runSwiftInstructionPass(SILInstruction *inst,
472+
void (*runFunction)(BridgedInstructionPassCtxt));
473+
456474
};
457475

458476
} // end namespace swift

libswift/README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,20 @@ To add a new function pass:
8383

8484
All SIL modifications, which a pass can do, are going through the `FunctionPassContext` - the second parameter of its run-function. In other words, the context is the central place to make modifications. This enables automatic change notifications to the pass manager. Also, it makes it easier to build a concurrent pass manager in future.
8585

86+
### Instruction Passes
87+
88+
In addition to function passes, _libswift_ provides the infrastructure for instruction passes. Instruction passes are invoked from SILCombine (in the C++ SILOptimizer) and correspond to a visit-function in SILCombine.
89+
90+
With instruction passes it's possible to implement small peephole optimizations for certain instruction classes.
91+
92+
To add a new instruction pass:
93+
94+
* add a `SWIFT_INSTRUCTION_PASS` entry in `Passes.def`
95+
* create a new Swift file in `libswift/Sources/Optimizer/InstructionPasses`
96+
* add an `InstructionPass` global
97+
* register the pass in `registerSwiftPasses()`
98+
* if this passes replaces an existing `SILCombiner` visit function, remove the old visit function
99+
86100
## Performance
87101

88102
The performance of _libswift_ is very important, because compile time is critical.

libswift/Sources/Optimizer/PassManager/PassRegistration.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@ private func registerPass(
2727
}
2828
}
2929

30+
private func registerPass<InstType: Instruction>(
31+
_ pass: InstructionPass<InstType>,
32+
_ runFn: @escaping (@convention(c) (BridgedInstructionPassCtxt) -> ())) {
33+
pass.name.withBridgedStringRef { nameStr in
34+
SILCombine_registerInstructionPass(nameStr, runFn)
35+
}
36+
}
37+
3038
private func registerSwiftPasses() {
3139
registerPass(silPrinterPass, { silPrinterPass.run($0) })
3240
registerPass(mergeCondFailsPass, { mergeCondFailsPass.run($0) })

libswift/Sources/Optimizer/PassManager/PassUtils.swift

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import OptimizerBridging
1515

1616
public typealias BridgedFunctionPassCtxt =
1717
OptimizerBridging.BridgedFunctionPassCtxt
18+
public typealias BridgedInstructionPassCtxt =
19+
OptimizerBridging.BridgedInstructionPassCtxt
1820

1921
struct FunctionPassContext {
2022

@@ -49,10 +51,43 @@ struct FunctionPass {
4951
}
5052
}
5153

54+
struct InstructionPassContext {
55+
fileprivate let passContext: BridgedPassContext
56+
57+
func erase(instruction: Instruction) {
58+
PassContext_eraseInstruction(passContext, instruction.bridged)
59+
}
60+
61+
private func notifyChanges(_ kind: ChangeNotificationKind) {
62+
PassContext_notifyChanges(passContext, kind)
63+
}
64+
}
65+
66+
struct InstructionPass<InstType: Instruction> {
67+
68+
let name: String
69+
let runFunction: (InstType, InstructionPassContext) -> ()
70+
71+
public init(name: String, _ runFunction: @escaping (InstType, InstructionPassContext) -> ()) {
72+
self.name = name
73+
self.runFunction = runFunction
74+
}
75+
76+
func run(_ bridgedCtxt: BridgedInstructionPassCtxt) {
77+
let inst = bridgedCtxt.instruction.getAs(InstType.self)
78+
let context = InstructionPassContext(passContext: bridgedCtxt.passContext)
79+
runFunction(inst, context)
80+
}
81+
}
82+
5283
extension StackList {
5384
init(_ context: FunctionPassContext) {
5485
self.init(context: context.passContext)
5586
}
87+
88+
init(_ context: InstructionPassContext) {
89+
self.init(context: context.passContext)
90+
}
5691
}
5792

5893
extension Builder {
@@ -66,4 +101,15 @@ extension Builder {
66101
self.init(insertionPoint: insPnt, location: insPnt.location,
67102
passContext: context.passContext)
68103
}
104+
105+
init(at insPnt: Instruction, location: Location,
106+
_ context: InstructionPassContext) {
107+
self.init(insertionPoint: insPnt, location: location,
108+
passContext: context.passContext)
109+
}
110+
111+
init(at insPnt: Instruction, _ context: InstructionPassContext) {
112+
self.init(insertionPoint: insPnt, location: insPnt.location,
113+
passContext: context.passContext)
114+
}
69115
}

0 commit comments

Comments
 (0)