Skip to content

[Playgrounds] Add a new -playground-option flag to control which transforms to apply #69355

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions include/swift/Basic/LangOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "swift/Basic/Feature.h"
#include "swift/Basic/FunctionBodySkipping.h"
#include "swift/Basic/LLVM.h"
#include "swift/Basic/PlaygroundOption.h"
#include "swift/Basic/Version.h"
#include "swift/Config.h"
#include "clang/CAS/CASOptions.h"
Expand Down Expand Up @@ -295,9 +296,8 @@ namespace swift {
/// Indicates whether the playground transformation should be applied.
bool PlaygroundTransform = false;

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

/// Keep comments during lexing and attach them to declarations.
bool AttachCommentsToDecls = false;
Expand Down
50 changes: 50 additions & 0 deletions include/swift/Basic/PlaygroundOption.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//===--- PlaygroundOption.h - Playground Transform Options -----*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2023 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_BASIC_PLAYGROUND_OPTIONS_H
#define SWIFT_BASIC_PLAYGROUND_OPTIONS_H

#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/StringRef.h"

namespace swift {

/// Enumeration describing all of the available playground options.
enum class PlaygroundOption {
#define PLAYGROUND_OPTION(OptionName, Description, DefaultOn, HighPerfOn) \
OptionName,
#include "swift/Basic/PlaygroundOptions.def"
};

constexpr unsigned numPlaygroundOptions() {
enum PlaygroundOptions {
#define PLAYGROUND_OPTION(OptionName, Description, DefaultOn, HighPerfOn) \
OptionName,
#include "swift/Basic/PlaygroundOptions.def"
NumPlaygroundOptions
};
return NumPlaygroundOptions;
}

/// Return the name of the given playground option.
llvm::StringRef getPlaygroundOptionName(PlaygroundOption option);

/// Get the playground option that corresponds to a given name, if there is one.
llvm::Optional<PlaygroundOption> getPlaygroundOption(llvm::StringRef name);

/// Set of enabled playground options.
typedef llvm::SmallSet<PlaygroundOption, 8> PlaygroundOptionSet;

}

#endif // SWIFT_BASIC_PLAYGROUND_OPTIONS_H
40 changes: 40 additions & 0 deletions include/swift/Basic/PlaygroundOptions.def
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//===--- PlaygroundOptions.def - Playground Transform Options ---*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2023 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file defines options that can be specified for the playground transform.
//
//
// PLAYGROUND_OPTION(OptionName, Description, DefaultOn, HighPerfOn)
//
// The PLAYGROUND_OPTION macro describes each named option that controls
// an aspect of the "playground transform" step in Sema.
//
// OptionName: The name of this playground transform option (both in source
// code and as a `-playground` parameter), e.g. ScopeEvents
// Description: A string literal that describes the option, e.g. "Enables
// logging of scope entry and exit events"
// DefaultOn: Whether the option is enabled by default
// HighPerfOn: Whether the option is enabled in "high performance" mode
//
//===----------------------------------------------------------------------===//

#ifndef PLAYGROUND_OPTION
# error define PLAYGROUND_OPTION before including PlaygroundOptions.def
#endif

PLAYGROUND_OPTION(ScopeEvents, "Scope entry/exit events",
/* enabled by default */ true, /* enabled in high-perf mode */ false)

PLAYGROUND_OPTION(FunctionParameters, "Values of function parameters",
/* enabled by default */ true, /* enabled in high-perf mode */ false)

#undef PLAYGROUND_OPTION
4 changes: 4 additions & 0 deletions include/swift/Option/FrontendOptions.td
Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,10 @@ def disable_standard_substitutions_in_reflection_mangling : Flag<["-"], "disable
def playground : Flag<["-"], "playground">,
HelpText<"Apply the playground semantics and transformation">;

def playground_option : Separate<["-"], "playground-option">,
Flags<[FrontendOption]>,
HelpText<"Provide an option to the playground transform (if enabled)">;

def playground_high_performance : Flag<["-"], "playground-high-performance">,
HelpText<"Omit instrumentation that has a high runtime performance impact">;

Expand Down
19 changes: 16 additions & 3 deletions include/swift/Subsystems.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,13 +131,26 @@ namespace swift {
/// be compared against the results from the debugger.
void performDebuggerTestingTransform(SourceFile &SF);

/// Once type checking is complete, this optionally transforms the ASTs to add
/// calls to external logging functions.
/// Once type checking is complete, this optionally transforms the ASTs to
/// insert calls to external logging functions.
///
/// \param Opts The specific set of transforms that should be applied.
void performPlaygroundTransform(SourceFile &SF, PlaygroundOptionSet Opts);

/// Once type checking is complete, this optionally transforms the ASTs to
/// insert calls to external logging functions. This function is provided
/// for backward compatibility with existing code; for new code, the variant
/// that takes an `PlaygroundOptionSet` parameter should be used.
///
/// \param HighPerformance True if the playground transform should omit
/// instrumentation that has a high runtime performance impact.
///
/// This function is provided for backward compatibility with older code, and
/// is a convenience for calling `performPlaygroundTransform()` with the set
/// of options that are enabled in high-performance mode. New uses should call
/// the newer form of this function that takes a `PlaygroundOptionSet`.
void performPlaygroundTransform(SourceFile &SF, bool HighPerformance);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth a comment to call out this method as deprecated and to be removed in the near future.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point — I'll add that as a comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the PR with a comment.


/// Once type checking is complete this optionally walks the ASTs to add calls
/// to externally provided functions that simulate "program counter"-like
/// debugging events. See the comment at the top of lib/Sema/PCMacro.cpp for a
Expand Down
23 changes: 23 additions & 0 deletions lib/Basic/LangOptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "swift/AST/DiagnosticEngine.h"
#include "swift/Basic/Feature.h"
#include "swift/Basic/Platform.h"
#include "swift/Basic/PlaygroundOption.h"
#include "swift/Basic/Range.h"
#include "swift/Config.h"
#include "llvm/ADT/Hashing.h"
Expand All @@ -36,6 +37,11 @@ LangOptions::LangOptions() {
Features.insert(Feature::ParserRoundTrip);
Features.insert(Feature::ParserValidation);
#endif
// Enable any playground options that are enabled by default.
#define PLAYGROUND_OPTION(OptionName, Description, DefaultOn, HighPerfOn) \
if (DefaultOn) \
PlaygroundOptions.insert(PlaygroundOption::OptionName);
#include "swift/Basic/PlaygroundOptions.def"
}

struct SupportedConditionalValue {
Expand Down Expand Up @@ -638,6 +644,23 @@ bool swift::includeInModuleInterface(Feature feature) {
llvm_unreachable("covered switch");
}

llvm::StringRef swift::getPlaygroundOptionName(PlaygroundOption option) {
switch (option) {
#define PLAYGROUND_OPTION(OptionName, Description, DefaultOn, HighPerfOn) \
case PlaygroundOption::OptionName: return #OptionName;
#include "swift/Basic/PlaygroundOptions.def"
}
llvm_unreachable("covered switch");
}

llvm::Optional<PlaygroundOption> swift::getPlaygroundOption(llvm::StringRef name) {
return llvm::StringSwitch<llvm::Optional<PlaygroundOption>>(name)
#define PLAYGROUND_OPTION(OptionName, Description, DefaultOn, HighPerfOn) \
.Case(#OptionName, PlaygroundOption::OptionName)
#include "swift/Basic/PlaygroundOptions.def"
.Default(llvm::None);
}

DiagnosticBehavior LangOptions::getAccessNoteFailureLimit() const {
switch (AccessNoteBehavior) {
case AccessNoteDiagnosticBehavior::Ignore:
Expand Down
22 changes: 20 additions & 2 deletions lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -719,8 +719,26 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
Opts.PlaygroundTransform |= Args.hasArg(OPT_playground);
if (Args.hasArg(OPT_disable_playground_transform))
Opts.PlaygroundTransform = false;
Opts.PlaygroundHighPerformance |=
Args.hasArg(OPT_playground_high_performance);
if (Args.hasArg(OPT_playground_high_performance)) {
// Disable any playground options that are marked as not being enabled in
// high performance mode.
#define PLAYGROUND_OPTION(OptionName, Description, DefaultOn, HighPerfOn) \
if (!HighPerfOn) \
Opts.PlaygroundOptions.erase(PlaygroundOption::OptionName);
#include "swift/Basic/PlaygroundOptions.def"
}
for (const Arg *A : Args.filtered(OPT_playground_option)) {
// Enable the option (or disable if it has a "No" prefix). Any unknown
// options are ignored.
StringRef optionName = A->getValue();
const bool disableOption = optionName.consume_front("No");
if (auto option = getPlaygroundOption(optionName)) {
if (disableOption)
Opts.PlaygroundOptions.erase(*option);
else
Opts.PlaygroundOptions.insert(*option);
}
}

// This can be enabled independently of the playground transform.
Opts.PCMacro |= Args.hasArg(OPT_pc_macro);
Expand Down
52 changes: 38 additions & 14 deletions lib/Sema/PlaygroundTransform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "swift/AST/Module.h"
#include "swift/AST/Pattern.h"
#include "swift/AST/SourceFile.h"
#include "swift/Basic/PlaygroundOption.h"

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

namespace {

struct TransformOptions {
bool LogScopeEvents;
bool LogFunctionParameters;

TransformOptions(const PlaygroundOptionSet &opts) :
LogScopeEvents(opts.contains(PlaygroundOption::ScopeEvents)),
LogFunctionParameters(opts.contains(PlaygroundOption::FunctionParameters)) {}
};

class Instrumenter : InstrumenterBase {
private:
std::mt19937_64 &RNG;
unsigned &TmpNameIndex;
bool HighPerformance;
TransformOptions Options;
bool ExtendedCallbacks;

DeclNameRef DebugPrintName;
Expand Down Expand Up @@ -113,7 +123,7 @@ class Instrumenter : InstrumenterBase {
// all the braces up to its target.
size_t escapeToTarget(BracePair::TargetKinds TargetKind,
ElementVector &Elements, size_t EI) {
if (HighPerformance)
if (!Options.LogScopeEvents)
return EI;

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

public:
Instrumenter(ASTContext &C, DeclContext *DC, std::mt19937_64 &RNG, bool HP,
unsigned &TmpNameIndex)
: InstrumenterBase(C, DC), RNG(RNG), TmpNameIndex(TmpNameIndex),
HighPerformance(HP),
Instrumenter(ASTContext &C, DeclContext *DC, std::mt19937_64 &RNG,
TransformOptions OPT, unsigned &TmpNameIndex)
: InstrumenterBase(C, DC), RNG(RNG), TmpNameIndex(TmpNameIndex), Options(OPT),
ExtendedCallbacks(C.LangOpts.hasFeature(Feature::PlaygroundExtendedCallbacks)),
DebugPrintName(C.getIdentifier("__builtin_debugPrint")),
PrintName(C.getIdentifier("__builtin_print")),
Expand Down Expand Up @@ -609,7 +618,7 @@ class Instrumenter : InstrumenterBase {
// If we were given any parameters that apply to this brace block, we insert
// log calls for them now (before any of the other statements). We only log
// named parameters (not `{ _ in ... }`, for example).
if (PL && !HighPerformance) {
if (PL && Options.LogFunctionParameters) {
size_t EI = 0;
for (const auto &PD : *PL) {
if (PD->hasName()) {
Expand All @@ -627,7 +636,7 @@ class Instrumenter : InstrumenterBase {
}
}

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

} // end anonymous namespace

void swift::performPlaygroundTransform(SourceFile &SF, bool HighPerformance) {
void swift::performPlaygroundTransform(SourceFile &SF, PlaygroundOptionSet Opts) {
class ExpressionFinder : public ASTWalker {
private:
ASTContext &ctx;
TransformOptions Options;
std::mt19937_64 RNG;
bool HighPerformance;
unsigned TmpNameIndex = 0;

public:
ExpressionFinder(ASTContext &ctx, bool HP) : ctx(ctx), HighPerformance(HP) {}
ExpressionFinder(ASTContext &ctx, PlaygroundOptionSet Opts) :
ctx(ctx), Options(Opts) {}

// FIXME: Remove this
bool shouldWalkAccessorsTheOldWay() override { return true; }
Expand All @@ -918,7 +928,7 @@ void swift::performPlaygroundTransform(SourceFile &SF, bool HighPerformance) {
if (!FD->isImplicit()) {
if (BraceStmt *Body = FD->getBody()) {
const ParameterList *PL = FD->getParameters();
Instrumenter I(ctx, FD, RNG, HighPerformance, TmpNameIndex);
Instrumenter I(ctx, FD, RNG, Options, TmpNameIndex);
BraceStmt *NewBody = I.transformBraceStmt(Body, PL);
if (NewBody != Body) {
FD->setBody(NewBody, AbstractFunctionDecl::BodyKind::TypeChecked);
Expand All @@ -930,7 +940,7 @@ void swift::performPlaygroundTransform(SourceFile &SF, bool HighPerformance) {
} else if (auto *TLCD = dyn_cast<TopLevelCodeDecl>(D)) {
if (!TLCD->isImplicit()) {
if (BraceStmt *Body = TLCD->getBody()) {
Instrumenter I(ctx, TLCD, RNG, HighPerformance, TmpNameIndex);
Instrumenter I(ctx, TLCD, RNG, Options, TmpNameIndex);
BraceStmt *NewBody = I.transformBraceStmt(Body, nullptr, true);
if (NewBody != Body) {
TLCD->setBody(NewBody);
Expand All @@ -944,6 +954,20 @@ void swift::performPlaygroundTransform(SourceFile &SF, bool HighPerformance) {
}
};

ExpressionFinder EF(SF.getASTContext(), HighPerformance);
ExpressionFinder EF(SF.getASTContext(), Opts);
SF.walk(EF);
}

/// This function is provided for backward compatibility with the old API, since
/// LLDB and others call it directly, passing it a boolean to control whether to
/// only apply "high performance" options. We emulate that here.
void swift::performPlaygroundTransform(SourceFile &SF, bool HighPerformance) {
PlaygroundOptionSet HighPerfTransformOpts;
// Enable any playground options that are marked as being applicable to high
// performance mode.
#define PLAYGROUND_OPTION(OptionName, Description, DefaultOn, HighPerfOn) \
if (HighPerfOn) \
HighPerfTransformOpts.insert(PlaygroundOption::OptionName);
#include "swift/Basic/PlaygroundOptions.def"
swift::performPlaygroundTransform(SF, HighPerfTransformOpts);
}
2 changes: 1 addition & 1 deletion lib/Sema/TypeChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ TypeCheckSourceFileRequest::evaluate(Evaluator &eval, SourceFile *SF) const {
// Playground transform knows to look out for PCMacro's changes and not
// to playground log them.
if (!Ctx.hadError() && Ctx.LangOpts.PlaygroundTransform)
performPlaygroundTransform(*SF, Ctx.LangOpts.PlaygroundHighPerformance);
performPlaygroundTransform(*SF, Ctx.LangOpts.PlaygroundOptions);

return std::make_tuple<>();
}
Expand Down
15 changes: 13 additions & 2 deletions test/PlaygroundTransform/high_performance.swift
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
// Tests that `-playground-high-performance` turns off expensive logging, and
// that turning off the corresponding options using `-playground-option` with
// a `No` prefix does the same thing.
//
// REQUIRES: executable_test
//
// RUN: %empty-directory(%t)
// RUN: cp %s %t/main.swift
// 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
//
// RUN: %target-build-swift -Xfrontend -playground -Xfrontend -playground-high-performance -o %t/main -I=%t %t/PlaygroundSupport.o %t/main.swift
// RUN: %target-codesign %t/main
// RUN: %target-run %t/main | %FileCheck %s
// RUN: %target-build-swift -Xfrontend -pc-macro -Xfrontend -playground -Xfrontend -playground-high-performance -o %t/main2 -I=%t %t/PlaygroundSupport.o %t/main.swift
//
// RUN: %target-build-swift -Xfrontend -pc-macro -Xfrontend -playground -Xfrontend -playground-high-performance -o %t/main2 -I=%t %t/PlaygroundSupport.o %t/main.swift
// RUN: %target-codesign %t/main2
// RUN: %target-run %t/main2 | %FileCheck %s
// REQUIRES: executable_test
//
// 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
// RUN: %target-codesign %t/main3
// RUN: %target-run %t/main3 | %FileCheck %s

import PlaygroundSupport

Expand Down
Loading