Skip to content

Commit a363603

Browse files
authored
[Playgrounds] Add a new -playground-option flag to control which transforms to apply (#69355)
Generalize the existing `-playground-high-performance` flag into a set of options that control various aspects of the "playground transformation" in Sema. This commit adds the first two of those controllable parts of the transform, matching what the existing flag already controls (scope entry/exit and function arguments), but in an extensible way. The intent is for this to be a scalable way to control a larger set of upcoming options. So instead of a single flag, we represent the playground transform options as a set of well-defined choices, with a new `-playground-option` flag to individually enable or disable those options (when prefixed with "No", the corresponding option is instead disabled). Enabling an already-enabled option or disabling an already-disabled option is a no-op. For compatibility, the existing `-playground-high-performance` flag causes "expensive" transforms to be disabled, as before. We can also leave it as a useful shorthand to include or exclude new options even in the future, based on their cost. There is a comment on the old function indicating that new code should use the more general form, but it remains for clients like LLDB until they can switch over. The machinery for implementing the playground options is similar to how `Features.def` works, with a new `PlaygroundOptions.def` that defines the supported playground transform options. Each playground definition specifies the name and description, as well as whether the option is enabled by default, and whether it's also enabled in the "high performance" case. Adding a new option in the future only requires adding it to `PlaygroundOptions.def`, deciding whether it should be on or off by default, deciding whether it should also be on or off in `-playground-high-performance` mode, and checking for its presence from the appropriate places in `PlaygroundTransform.cpp`. Note that this is intended to control the types of user-visible results that the invoker of the compiler wants, from an externally detectable standpoint. Other flags, such as whether or not to use the extended form of the callbacks, remain as experimental features, since those deal with the mechanics and not the desired observed behavior. rdar://109911673
1 parent bfcdfaf commit a363603

File tree

11 files changed

+273
-25
lines changed

11 files changed

+273
-25
lines changed

include/swift/Basic/LangOptions.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "swift/Basic/FixedBitSet.h"
2323
#include "swift/Basic/FunctionBodySkipping.h"
2424
#include "swift/Basic/LLVM.h"
25+
#include "swift/Basic/PlaygroundOption.h"
2526
#include "swift/Basic/Version.h"
2627
#include "swift/Config.h"
2728
#include "clang/CAS/CASOptions.h"
@@ -302,9 +303,8 @@ namespace swift {
302303
/// Indicates whether the playground transformation should be applied.
303304
bool PlaygroundTransform = false;
304305

305-
/// Indicates whether the playground transformation should omit
306-
/// instrumentation that has a high runtime performance impact.
307-
bool PlaygroundHighPerformance = false;
306+
/// Indicates the specific playground transformations to apply.
307+
PlaygroundOptionSet PlaygroundOptions;
308308

309309
/// Keep comments during lexing and attach them to declarations.
310310
bool AttachCommentsToDecls = false;
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//===--- PlaygroundOption.h - Playground Transform Options -----*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef SWIFT_BASIC_PLAYGROUND_OPTIONS_H
14+
#define SWIFT_BASIC_PLAYGROUND_OPTIONS_H
15+
16+
#include "llvm/ADT/Optional.h"
17+
#include "llvm/ADT/SmallSet.h"
18+
#include "llvm/ADT/StringRef.h"
19+
20+
namespace swift {
21+
22+
/// Enumeration describing all of the available playground options.
23+
enum class PlaygroundOption {
24+
#define PLAYGROUND_OPTION(OptionName, Description, DefaultOn, HighPerfOn) \
25+
OptionName,
26+
#include "swift/Basic/PlaygroundOptions.def"
27+
};
28+
29+
constexpr unsigned numPlaygroundOptions() {
30+
enum PlaygroundOptions {
31+
#define PLAYGROUND_OPTION(OptionName, Description, DefaultOn, HighPerfOn) \
32+
OptionName,
33+
#include "swift/Basic/PlaygroundOptions.def"
34+
NumPlaygroundOptions
35+
};
36+
return NumPlaygroundOptions;
37+
}
38+
39+
/// Return the name of the given playground option.
40+
llvm::StringRef getPlaygroundOptionName(PlaygroundOption option);
41+
42+
/// Get the playground option that corresponds to a given name, if there is one.
43+
llvm::Optional<PlaygroundOption> getPlaygroundOption(llvm::StringRef name);
44+
45+
/// Set of enabled playground options.
46+
typedef llvm::SmallSet<PlaygroundOption, 8> PlaygroundOptionSet;
47+
48+
}
49+
50+
#endif // SWIFT_BASIC_PLAYGROUND_OPTIONS_H
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//===--- PlaygroundOptions.def - Playground Transform Options ---*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// This file defines options that can be specified for the playground transform.
14+
//
15+
//
16+
// PLAYGROUND_OPTION(OptionName, Description, DefaultOn, HighPerfOn)
17+
//
18+
// The PLAYGROUND_OPTION macro describes each named option that controls
19+
// an aspect of the "playground transform" step in Sema.
20+
//
21+
// OptionName: The name of this playground transform option (both in source
22+
// code and as a `-playground` parameter), e.g. ScopeEvents
23+
// Description: A string literal that describes the option, e.g. "Enables
24+
// logging of scope entry and exit events"
25+
// DefaultOn: Whether the option is enabled by default
26+
// HighPerfOn: Whether the option is enabled in "high performance" mode
27+
//
28+
//===----------------------------------------------------------------------===//
29+
30+
#ifndef PLAYGROUND_OPTION
31+
# error define PLAYGROUND_OPTION before including PlaygroundOptions.def
32+
#endif
33+
34+
PLAYGROUND_OPTION(ScopeEvents, "Scope entry/exit events",
35+
/* enabled by default */ true, /* enabled in high-perf mode */ false)
36+
37+
PLAYGROUND_OPTION(FunctionParameters, "Values of function parameters",
38+
/* enabled by default */ true, /* enabled in high-perf mode */ false)
39+
40+
#undef PLAYGROUND_OPTION

include/swift/Option/FrontendOptions.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -848,6 +848,10 @@ def disable_standard_substitutions_in_reflection_mangling : Flag<["-"], "disable
848848
def playground : Flag<["-"], "playground">,
849849
HelpText<"Apply the playground semantics and transformation">;
850850

851+
def playground_option : Separate<["-"], "playground-option">,
852+
Flags<[FrontendOption]>,
853+
HelpText<"Provide an option to the playground transform (if enabled)">;
854+
851855
def playground_high_performance : Flag<["-"], "playground-high-performance">,
852856
HelpText<"Omit instrumentation that has a high runtime performance impact">;
853857

include/swift/Subsystems.h

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,13 +131,26 @@ namespace swift {
131131
/// be compared against the results from the debugger.
132132
void performDebuggerTestingTransform(SourceFile &SF);
133133

134-
/// Once type checking is complete, this optionally transforms the ASTs to add
135-
/// calls to external logging functions.
134+
/// Once type checking is complete, this optionally transforms the ASTs to
135+
/// insert calls to external logging functions.
136+
///
137+
/// \param Opts The specific set of transforms that should be applied.
138+
void performPlaygroundTransform(SourceFile &SF, PlaygroundOptionSet Opts);
139+
140+
/// Once type checking is complete, this optionally transforms the ASTs to
141+
/// insert calls to external logging functions. This function is provided
142+
/// for backward compatibility with existing code; for new code, the variant
143+
/// that takes an `PlaygroundOptionSet` parameter should be used.
136144
///
137145
/// \param HighPerformance True if the playground transform should omit
138146
/// instrumentation that has a high runtime performance impact.
147+
///
148+
/// This function is provided for backward compatibility with older code, and
149+
/// is a convenience for calling `performPlaygroundTransform()` with the set
150+
/// of options that are enabled in high-performance mode. New uses should call
151+
/// the newer form of this function that takes a `PlaygroundOptionSet`.
139152
void performPlaygroundTransform(SourceFile &SF, bool HighPerformance);
140-
153+
141154
/// Once type checking is complete this optionally walks the ASTs to add calls
142155
/// to externally provided functions that simulate "program counter"-like
143156
/// debugging events. See the comment at the top of lib/Sema/PCMacro.cpp for a

lib/Basic/LangOptions.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "swift/AST/DiagnosticEngine.h"
2020
#include "swift/Basic/Feature.h"
2121
#include "swift/Basic/Platform.h"
22+
#include "swift/Basic/PlaygroundOption.h"
2223
#include "swift/Basic/Range.h"
2324
#include "swift/Config.h"
2425
#include "llvm/ADT/Hashing.h"
@@ -36,6 +37,11 @@ LangOptions::LangOptions() {
3637
Features.insert(Feature::ParserRoundTrip);
3738
Features.insert(Feature::ParserValidation);
3839
#endif
40+
// Enable any playground options that are enabled by default.
41+
#define PLAYGROUND_OPTION(OptionName, Description, DefaultOn, HighPerfOn) \
42+
if (DefaultOn) \
43+
PlaygroundOptions.insert(PlaygroundOption::OptionName);
44+
#include "swift/Basic/PlaygroundOptions.def"
3945
}
4046

4147
struct SupportedConditionalValue {
@@ -638,6 +644,23 @@ bool swift::includeInModuleInterface(Feature feature) {
638644
llvm_unreachable("covered switch");
639645
}
640646

647+
llvm::StringRef swift::getPlaygroundOptionName(PlaygroundOption option) {
648+
switch (option) {
649+
#define PLAYGROUND_OPTION(OptionName, Description, DefaultOn, HighPerfOn) \
650+
case PlaygroundOption::OptionName: return #OptionName;
651+
#include "swift/Basic/PlaygroundOptions.def"
652+
}
653+
llvm_unreachable("covered switch");
654+
}
655+
656+
llvm::Optional<PlaygroundOption> swift::getPlaygroundOption(llvm::StringRef name) {
657+
return llvm::StringSwitch<llvm::Optional<PlaygroundOption>>(name)
658+
#define PLAYGROUND_OPTION(OptionName, Description, DefaultOn, HighPerfOn) \
659+
.Case(#OptionName, PlaygroundOption::OptionName)
660+
#include "swift/Basic/PlaygroundOptions.def"
661+
.Default(llvm::None);
662+
}
663+
641664
DiagnosticBehavior LangOptions::getAccessNoteFailureLimit() const {
642665
switch (AccessNoteBehavior) {
643666
case AccessNoteDiagnosticBehavior::Ignore:

lib/Frontend/CompilerInvocation.cpp

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -725,8 +725,26 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
725725
Opts.PlaygroundTransform |= Args.hasArg(OPT_playground);
726726
if (Args.hasArg(OPT_disable_playground_transform))
727727
Opts.PlaygroundTransform = false;
728-
Opts.PlaygroundHighPerformance |=
729-
Args.hasArg(OPT_playground_high_performance);
728+
if (Args.hasArg(OPT_playground_high_performance)) {
729+
// Disable any playground options that are marked as not being enabled in
730+
// high performance mode.
731+
#define PLAYGROUND_OPTION(OptionName, Description, DefaultOn, HighPerfOn) \
732+
if (!HighPerfOn) \
733+
Opts.PlaygroundOptions.erase(PlaygroundOption::OptionName);
734+
#include "swift/Basic/PlaygroundOptions.def"
735+
}
736+
for (const Arg *A : Args.filtered(OPT_playground_option)) {
737+
// Enable the option (or disable if it has a "No" prefix). Any unknown
738+
// options are ignored.
739+
StringRef optionName = A->getValue();
740+
const bool disableOption = optionName.consume_front("No");
741+
if (auto option = getPlaygroundOption(optionName)) {
742+
if (disableOption)
743+
Opts.PlaygroundOptions.erase(*option);
744+
else
745+
Opts.PlaygroundOptions.insert(*option);
746+
}
747+
}
730748

731749
// This can be enabled independently of the playground transform.
732750
Opts.PCMacro |= Args.hasArg(OPT_pc_macro);

lib/Sema/PlaygroundTransform.cpp

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "swift/AST/Module.h"
2424
#include "swift/AST/Pattern.h"
2525
#include "swift/AST/SourceFile.h"
26+
#include "swift/Basic/PlaygroundOption.h"
2627

2728
#include <random>
2829
#include <forward_list>
@@ -36,11 +37,20 @@ using namespace swift::instrumenter_support;
3637

3738
namespace {
3839

40+
struct TransformOptions {
41+
bool LogScopeEvents;
42+
bool LogFunctionParameters;
43+
44+
TransformOptions(const PlaygroundOptionSet &opts) :
45+
LogScopeEvents(opts.contains(PlaygroundOption::ScopeEvents)),
46+
LogFunctionParameters(opts.contains(PlaygroundOption::FunctionParameters)) {}
47+
};
48+
3949
class Instrumenter : InstrumenterBase {
4050
private:
4151
std::mt19937_64 &RNG;
4252
unsigned &TmpNameIndex;
43-
bool HighPerformance;
53+
TransformOptions Options;
4454
bool ExtendedCallbacks;
4555

4656
DeclNameRef DebugPrintName;
@@ -113,7 +123,7 @@ class Instrumenter : InstrumenterBase {
113123
// all the braces up to its target.
114124
size_t escapeToTarget(BracePair::TargetKinds TargetKind,
115125
ElementVector &Elements, size_t EI) {
116-
if (HighPerformance)
126+
if (!Options.LogScopeEvents)
117127
return EI;
118128

119129
for (const BracePair &BP : BracePairs) {
@@ -127,10 +137,9 @@ class Instrumenter : InstrumenterBase {
127137
}
128138

129139
public:
130-
Instrumenter(ASTContext &C, DeclContext *DC, std::mt19937_64 &RNG, bool HP,
131-
unsigned &TmpNameIndex)
132-
: InstrumenterBase(C, DC), RNG(RNG), TmpNameIndex(TmpNameIndex),
133-
HighPerformance(HP),
140+
Instrumenter(ASTContext &C, DeclContext *DC, std::mt19937_64 &RNG,
141+
TransformOptions OPT, unsigned &TmpNameIndex)
142+
: InstrumenterBase(C, DC), RNG(RNG), TmpNameIndex(TmpNameIndex), Options(OPT),
134143
ExtendedCallbacks(C.LangOpts.hasFeature(Feature::PlaygroundExtendedCallbacks)),
135144
DebugPrintName(C.getIdentifier("__builtin_debugPrint")),
136145
PrintName(C.getIdentifier("__builtin_print")),
@@ -609,7 +618,7 @@ class Instrumenter : InstrumenterBase {
609618
// If we were given any parameters that apply to this brace block, we insert
610619
// log calls for them now (before any of the other statements). We only log
611620
// named parameters (not `{ _ in ... }`, for example).
612-
if (PL && !HighPerformance) {
621+
if (PL && Options.LogFunctionParameters) {
613622
size_t EI = 0;
614623
for (const auto &PD : *PL) {
615624
if (PD->hasName()) {
@@ -627,7 +636,7 @@ class Instrumenter : InstrumenterBase {
627636
}
628637
}
629638

630-
if (!TopLevel && !HighPerformance && !BS->isImplicit()) {
639+
if (!TopLevel && Options.LogScopeEvents && !BS->isImplicit()) {
631640
Elements.insert(Elements.begin(), *buildScopeEntry(BS->getSourceRange()));
632641
Elements.insert(Elements.end(), *buildScopeExit(BS->getSourceRange()));
633642
}
@@ -895,16 +904,17 @@ class Instrumenter : InstrumenterBase {
895904

896905
} // end anonymous namespace
897906

898-
void swift::performPlaygroundTransform(SourceFile &SF, bool HighPerformance) {
907+
void swift::performPlaygroundTransform(SourceFile &SF, PlaygroundOptionSet Opts) {
899908
class ExpressionFinder : public ASTWalker {
900909
private:
901910
ASTContext &ctx;
911+
TransformOptions Options;
902912
std::mt19937_64 RNG;
903-
bool HighPerformance;
904913
unsigned TmpNameIndex = 0;
905914

906915
public:
907-
ExpressionFinder(ASTContext &ctx, bool HP) : ctx(ctx), HighPerformance(HP) {}
916+
ExpressionFinder(ASTContext &ctx, PlaygroundOptionSet Opts) :
917+
ctx(ctx), Options(Opts) {}
908918

909919
// FIXME: Remove this
910920
bool shouldWalkAccessorsTheOldWay() override { return true; }
@@ -918,7 +928,7 @@ void swift::performPlaygroundTransform(SourceFile &SF, bool HighPerformance) {
918928
if (!FD->isImplicit()) {
919929
if (BraceStmt *Body = FD->getBody()) {
920930
const ParameterList *PL = FD->getParameters();
921-
Instrumenter I(ctx, FD, RNG, HighPerformance, TmpNameIndex);
931+
Instrumenter I(ctx, FD, RNG, Options, TmpNameIndex);
922932
BraceStmt *NewBody = I.transformBraceStmt(Body, PL);
923933
if (NewBody != Body) {
924934
FD->setBody(NewBody, AbstractFunctionDecl::BodyKind::TypeChecked);
@@ -930,7 +940,7 @@ void swift::performPlaygroundTransform(SourceFile &SF, bool HighPerformance) {
930940
} else if (auto *TLCD = dyn_cast<TopLevelCodeDecl>(D)) {
931941
if (!TLCD->isImplicit()) {
932942
if (BraceStmt *Body = TLCD->getBody()) {
933-
Instrumenter I(ctx, TLCD, RNG, HighPerformance, TmpNameIndex);
943+
Instrumenter I(ctx, TLCD, RNG, Options, TmpNameIndex);
934944
BraceStmt *NewBody = I.transformBraceStmt(Body, nullptr, true);
935945
if (NewBody != Body) {
936946
TLCD->setBody(NewBody);
@@ -944,6 +954,20 @@ void swift::performPlaygroundTransform(SourceFile &SF, bool HighPerformance) {
944954
}
945955
};
946956

947-
ExpressionFinder EF(SF.getASTContext(), HighPerformance);
957+
ExpressionFinder EF(SF.getASTContext(), Opts);
948958
SF.walk(EF);
949959
}
960+
961+
/// This function is provided for backward compatibility with the old API, since
962+
/// LLDB and others call it directly, passing it a boolean to control whether to
963+
/// only apply "high performance" options. We emulate that here.
964+
void swift::performPlaygroundTransform(SourceFile &SF, bool HighPerformance) {
965+
PlaygroundOptionSet HighPerfTransformOpts;
966+
// Enable any playground options that are marked as being applicable to high
967+
// performance mode.
968+
#define PLAYGROUND_OPTION(OptionName, Description, DefaultOn, HighPerfOn) \
969+
if (HighPerfOn) \
970+
HighPerfTransformOpts.insert(PlaygroundOption::OptionName);
971+
#include "swift/Basic/PlaygroundOptions.def"
972+
swift::performPlaygroundTransform(SF, HighPerfTransformOpts);
973+
}

lib/Sema/TypeChecker.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ TypeCheckSourceFileRequest::evaluate(Evaluator &eval, SourceFile *SF) const {
345345
// Playground transform knows to look out for PCMacro's changes and not
346346
// to playground log them.
347347
if (!Ctx.hadError() && Ctx.LangOpts.PlaygroundTransform)
348-
performPlaygroundTransform(*SF, Ctx.LangOpts.PlaygroundHighPerformance);
348+
performPlaygroundTransform(*SF, Ctx.LangOpts.PlaygroundOptions);
349349

350350
return std::make_tuple<>();
351351
}

test/PlaygroundTransform/high_performance.swift

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,24 @@
1+
// Tests that `-playground-high-performance` turns off expensive logging, and
2+
// that turning off the corresponding options using `-playground-option` with
3+
// a `No` prefix does the same thing.
4+
//
5+
// REQUIRES: executable_test
6+
//
17
// RUN: %empty-directory(%t)
28
// RUN: cp %s %t/main.swift
39
// RUN: %target-build-swift -whole-module-optimization -module-name PlaygroundSupport -emit-module-path %t/PlaygroundSupport.swiftmodule -parse-as-library -c -o %t/PlaygroundSupport.o %S/Inputs/SilentPCMacroRuntime.swift %S/Inputs/PlaygroundsRuntime.swift
10+
//
411
// RUN: %target-build-swift -Xfrontend -playground -Xfrontend -playground-high-performance -o %t/main -I=%t %t/PlaygroundSupport.o %t/main.swift
512
// RUN: %target-codesign %t/main
613
// RUN: %target-run %t/main | %FileCheck %s
7-
// RUN: %target-build-swift -Xfrontend -pc-macro -Xfrontend -playground -Xfrontend -playground-high-performance -o %t/main2 -I=%t %t/PlaygroundSupport.o %t/main.swift
14+
//
15+
// RUN: %target-build-swift -Xfrontend -pc-macro -Xfrontend -playground -Xfrontend -playground-high-performance -o %t/main2 -I=%t %t/PlaygroundSupport.o %t/main.swift
816
// RUN: %target-codesign %t/main2
917
// RUN: %target-run %t/main2 | %FileCheck %s
10-
// REQUIRES: executable_test
18+
//
19+
// RUN: %target-build-swift -Xfrontend -playground -Xfrontend -playground-option -Xfrontend NoScopeEvents -Xfrontend -playground-option -Xfrontend NoFunctionParameters -o %t/main3 -I=%t %t/PlaygroundSupport.o %t/main.swift
20+
// RUN: %target-codesign %t/main3
21+
// RUN: %target-run %t/main3 | %FileCheck %s
1122

1223
import PlaygroundSupport
1324

0 commit comments

Comments
 (0)