Skip to content

Commit b904133

Browse files
author
Harlan Haskins
committed
[Modules] Add flag to skip non-inlinable function bodies
This flag, currently staged in as `-experimental-skip-non-inlinable-function-bodies`, will cause the typechecker to skip typechecking bodies of functions that will not be serialized in the resulting `.swiftmodule`. This patch also includes a SIL verifier that ensures that we don’t accidentally include a body that we should have skipped. There is still some work left to make sure the emitted .swiftmodule is exactly the same as what’s emitted without the flag, which is what’s causing the benchmark noise above. I’ll be committing follow-up patches to address those, but for now I’m going to land the implementation behind a flag.
1 parent 7733308 commit b904133

22 files changed

+525
-5
lines changed

include/swift/AST/Decl.h

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5689,7 +5689,8 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
56895689
/// Note that a true return value does not imply that the body was actually
56905690
/// parsed.
56915691
bool hasBody() const {
5692-
return getBodyKind() != BodyKind::None;
5692+
return getBodyKind() != BodyKind::None &&
5693+
getBodyKind() != BodyKind::Skipped;
56935694
}
56945695

56955696
/// Returns true if the text of this function's body can be retrieved either
@@ -5716,14 +5717,22 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
57165717
/// Note that the body was skipped for this function. Function body
57175718
/// cannot be attached after this call.
57185719
void setBodySkipped(SourceRange bodyRange) {
5719-
assert(getBodyKind() == BodyKind::None);
5720+
// FIXME: Remove 'Parsed' from this once we can delay parsing function
5721+
// bodies. Right now -experimental-skip-non-inlinable-function-bodies
5722+
// requires being able to change the state from Parsed to Skipped,
5723+
// because we're still eagerly parsing function bodies.
5724+
assert(getBodyKind() == BodyKind::None ||
5725+
getBodyKind() == BodyKind::Unparsed ||
5726+
getBodyKind() == BodyKind::Parsed);
5727+
assert(bodyRange.isValid());
57205728
BodyRange = bodyRange;
57215729
setBodyKind(BodyKind::Skipped);
57225730
}
57235731

57245732
/// Note that parsing for the body was delayed.
57255733
void setBodyDelayed(SourceRange bodyRange) {
57265734
assert(getBodyKind() == BodyKind::None);
5735+
assert(bodyRange.isValid());
57275736
BodyRange = bodyRange;
57285737
setBodyKind(BodyKind::Unparsed);
57295738
}
@@ -5769,6 +5778,10 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
57695778
return getBodyKind() == BodyKind::TypeChecked;
57705779
}
57715780

5781+
bool isBodySkipped() const {
5782+
return getBodyKind() == BodyKind::Skipped;
5783+
}
5784+
57725785
bool isMemberwiseInitializer() const {
57735786
return getBodyKind() == BodyKind::MemberwiseInitializer;
57745787
}

include/swift/AST/DeclContext.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,15 @@ class alignas(1 << DeclContextAlignInBits) DeclContext {
409409
const_cast<DeclContext *>(this)->getInnermostDeclarationDeclContext();
410410
}
411411

412+
/// Returns the innermost context that is an AbstractFunctionDecl whose
413+
/// body has been skipped.
414+
LLVM_READONLY
415+
DeclContext *getInnermostSkippedFunctionContext();
416+
const DeclContext *getInnermostSkippedFunctionContext() const {
417+
return
418+
const_cast<DeclContext *>(this)->getInnermostSkippedFunctionContext();
419+
}
420+
412421
/// Returns the semantic parent of this context. A context has a
413422
/// parent if and only if it is not a module context.
414423
DeclContext *getParent() const {

include/swift/AST/DiagnosticsFrontend.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,9 @@ ERROR(error_mode_cannot_emit_module_source_info,none,
128128
"this mode does not support emitting module source info files", ())
129129
ERROR(error_mode_cannot_emit_interface,none,
130130
"this mode does not support emitting module interface files", ())
131+
ERROR(cannot_emit_ir_skipping_function_bodies,none,
132+
"-experimental-skip-non-inlinable-function-bodies does not support "
133+
"emitting IR", ())
131134

132135
WARNING(emit_reference_dependencies_without_primary_file,none,
133136
"ignoring -emit-reference-dependencies (requires -primary-file)", ())

include/swift/AST/SILOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ class SILOptions {
7070
/// Whether to stop the optimization pipeline after serializing SIL.
7171
bool StopOptimizationAfterSerialization = false;
7272

73+
/// Whether to skip emitting non-inlinable function bodies.
74+
bool SkipNonInlinableFunctionBodies = false;
75+
7376
/// Optimization mode being used.
7477
OptimizationMode OptMode = OptimizationMode::NotSet;
7578

include/swift/Frontend/FrontendOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,8 @@ class FrontendOptions {
180180
/// \sa swift::SharedTimer
181181
bool DebugTimeCompilation = false;
182182

183+
bool SkipNonInlinableFunctionBodies = false;
184+
183185
/// The path to which we should output statistics files.
184186
std::string StatsOutputDir;
185187

@@ -339,6 +341,7 @@ class FrontendOptions {
339341

340342
public:
341343
static bool doesActionGenerateSIL(ActionType);
344+
static bool doesActionGenerateIR(ActionType);
342345
static bool doesActionProduceOutput(ActionType);
343346
static bool doesActionProduceTextualOutput(ActionType);
344347
static bool needsProperModuleName(ActionType);

include/swift/Option/Options.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,10 @@ def stats_output_dir: Separate<["-"], "stats-output-dir">,
254254
def trace_stats_events: Flag<["-"], "trace-stats-events">,
255255
Flags<[FrontendOption, HelpHidden]>,
256256
HelpText<"Trace changes to stats in -stats-output-dir">;
257+
def experimental_skip_non_inlinable_function_bodies:
258+
Flag<["-"], "experimental-skip-non-inlinable-function-bodies">,
259+
Flags<[FrontendOption, HelpHidden]>,
260+
HelpText<"Skip type-checking and SIL generation for non-inlinable function bodies">;
257261
def profile_stats_events: Flag<["-"], "profile-stats-events">,
258262
Flags<[FrontendOption, HelpHidden]>,
259263
HelpText<"Profile changes to stats in -stats-output-dir">;

include/swift/SILOptimizer/PassManager/Passes.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,9 @@ PASS(SimplifyUnreachableContainingBlocks, "simplify-unreachable-containing-block
311311
"Utility pass. Removes all non-term insts from blocks with unreachable terms")
312312
PASS(SerializeSILPass, "serialize-sil",
313313
"Utility pass. Serializes the current SILModule")
314+
PASS(NonInlinableFunctionSkippingChecker, "check-non-inlinable-function-skipping",
315+
"Utility pass to ensure -experimental-skip-non-inlinable-function-bodies "
316+
"skips everything it should")
314317
PASS(YieldOnceCheck, "yield-once-check",
315318
"Check correct usage of yields in yield-once coroutines")
316319
PASS(OSLogOptimization, "os-log-optimization", "Optimize os log calls")

include/swift/Subsystems.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,11 @@ namespace swift {
187187
/// If set, dumps wall time taken to type check each expression to
188188
/// llvm::errs().
189189
DebugTimeExpressions = 1 << 3,
190+
191+
/// If set, the typechecker will skip typechecking non-inlinable function
192+
/// bodies. Set this if you're trying to quickly emit a module or module
193+
/// interface without a full compilation.
194+
SkipNonInlinableFunctionBodies = 1 << 4,
190195
};
191196

192197
/// Once parsing and name-binding are complete, this walks the AST to resolve

lib/AST/DeclContext.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,17 @@ Decl *DeclContext::getInnermostDeclarationDeclContext() {
231231
return nullptr;
232232
}
233233

234+
DeclContext *DeclContext::getInnermostSkippedFunctionContext() {
235+
auto dc = this;
236+
do {
237+
if (auto afd = dyn_cast<AbstractFunctionDecl>(dc))
238+
if (afd->isBodySkipped())
239+
return afd;
240+
} while ((dc = dc->getParent()));
241+
242+
return nullptr;
243+
}
244+
234245
DeclContext *DeclContext::getParentForLookup() const {
235246
if (isa<ProtocolDecl>(this) || isa<ExtensionDecl>(this)) {
236247
// If we are inside a protocol or an extension, skip directly

lib/Frontend/ArgsToFrontendOptionsConverter.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,12 @@ bool ArgsToFrontendOptionsConverter::convert(
161161
if (checkUnusedSupplementaryOutputPaths())
162162
return true;
163163

164+
if (FrontendOptions::doesActionGenerateIR(Opts.RequestedAction)
165+
&& Opts.SkipNonInlinableFunctionBodies) {
166+
Diags.diagnose(SourceLoc(), diag::cannot_emit_ir_skipping_function_bodies);
167+
return true;
168+
}
169+
164170
if (const Arg *A = Args.getLastArg(OPT_module_link_name))
165171
Opts.ModuleLinkName = A->getValue();
166172

@@ -219,6 +225,8 @@ void ArgsToFrontendOptionsConverter::computeDebugTimeOptions() {
219225
Opts.DebugTimeExpressionTypeChecking |=
220226
Args.hasArg(OPT_debug_time_expression_type_checking);
221227
Opts.DebugTimeCompilation |= Args.hasArg(OPT_debug_time_compilation);
228+
Opts.SkipNonInlinableFunctionBodies |=
229+
Args.hasArg(OPT_experimental_skip_non_inlinable_function_bodies);
222230
if (const Arg *A = Args.getLastArg(OPT_stats_output_dir)) {
223231
Opts.StatsOutputDir = A->getValue();
224232
if (Args.getLastArg(OPT_trace_stats_events)) {

0 commit comments

Comments
 (0)