Skip to content

[moveOnly] Add a new pass called the LexicalLifetimeEliminator that runs after we finish lexical diagnostics. #40162

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
8 changes: 8 additions & 0 deletions include/swift/SIL/SILInstruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -1949,6 +1949,10 @@ class AllocStackInst final
/// Whether the alloc_stack instruction corresponds to a source-level VarDecl.
bool isLexical() const { return lexical; }

/// If this is a lexical borrow, eliminate the lexical bit. If this borrow
/// doesn't have a lexical bit, do not do anything.
void removeIsLexical() { lexical = false; }

/// Return the debug variable information attached to this instruction.
Optional<SILDebugVariable> getVarInfo() const {
Optional<SILType> AuxVarType;
Expand Down Expand Up @@ -4033,6 +4037,10 @@ class BeginBorrowInst
/// source-level lexical scope.
bool isLexical() const { return lexical; }

/// If this is a lexical borrow, eliminate the lexical bit. If this borrow
/// doesn't have a lexical bit, do not do anything.
void removeIsLexical() { lexical = false; }

/// Return a range over all EndBorrow instructions for this BeginBorrow.
EndBorrowRange getEndBorrows() const;

Expand Down
2 changes: 2 additions & 0 deletions include/swift/SILOptimizer/PassManager/Passes.def
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,8 @@ PASS(MoveOnlyChecker, "sil-move-only-checker",
PASS(MoveKillsCopyableValuesChecker, "sil-move-kills-copyable-values-checker",
"Pass that checks that any copyable (non-move only) value that is passed "
"to _move do not have any uses later than the _move")
PASS(LexicalLifetimeEliminator, "sil-lexical-lifetime-eliminator",
"Pass that removes lexical lifetime markers from borrows and alloc stack")
PASS(PruneVTables, "prune-vtables",
"Mark class methods that do not require vtable dispatch")
PASS_RANGE(AllPasses, AADumper, PruneVTables)
Expand Down
1 change: 1 addition & 0 deletions lib/SILOptimizer/Mandatory/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ target_sources(swiftSILOptimizer PRIVATE
DiagnoseUnreachable.cpp
Differentiation.cpp
IRGenPrepare.cpp
LexicalLifetimeEliminator.cpp
LowerHopToActor.cpp
MandatoryInlining.cpp
MoveOnlyChecker.cpp
Expand Down
65 changes: 65 additions & 0 deletions lib/SILOptimizer/Mandatory/LexicalLifetimeEliminator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//===--- LexicalLifetimeEliminator.cpp ------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2021 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
//
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "sil-lexical-lifetime-eliminator"

#include "swift/SILOptimizer/PassManager/Transforms.h"

using namespace swift;

namespace {

class LexicalLifetimeEliminatorPass : public SILFunctionTransform {
void run() override {
auto *fn = getFunction();

// If we are already canonical, we do not have any diagnostics to emit.
if (fn->wasDeserializedCanonical())
return;

// If we have experimental lexical lifetimes enabled, we do not want to run
// this pass since we want lexical lifetimes to exist later in the pipeline.
if (fn->getModule().getOptions().EnableExperimentalLexicalLifetimes)
return;

bool madeChange = false;
for (auto &block : *fn) {
for (auto &inst : block) {
if (auto *bbi = dyn_cast<BeginBorrowInst>(&inst)) {
if (bbi->isLexical()) {
bbi->removeIsLexical();
madeChange = true;
}
continue;
}

if (auto *asi = dyn_cast<AllocStackInst>(&inst)) {
if (asi->isLexical()) {
asi->removeIsLexical();
madeChange = true;
}
continue;
}
}
}

if (madeChange) {
invalidateAnalysis(SILAnalysis::InvalidationKind::Instructions);
}
}
};

} // anonymous namespace

SILTransform *swift::createLexicalLifetimeEliminator() {
return new LexicalLifetimeEliminatorPass();
}
6 changes: 6 additions & 0 deletions lib/SILOptimizer/PassManager/PassPipeline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,12 @@ static void addMandatoryDiagnosticOptPipeline(SILPassPipelinePlan &P) {
// value.
P.addMoveOnlyChecker(); // Check noImplicitCopy isn't copied.

// Now that we have finished performing diagnostics that rely on lexical
// scopes, if lexical lifetimes are not enabled, eliminate lexical lfietimes.
if (!Options.EnableExperimentalLexicalLifetimes) {
P.addLexicalLifetimeEliminator();
}

P.addOptimizeHopToExecutor();
P.addMandatoryGenericSpecializer();

Expand Down
43 changes: 43 additions & 0 deletions test/SILOptimizer/lexical_lifetime_elim.sil
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// RUN: %target-sil-opt -sil-lexical-lifetime-eliminator %s -enable-sil-verify-all | %FileCheck %s

sil_stage raw

import Builtin

class Klass {}

// CHECK-LABEL: sil [ossa] @lexical_lifetime_object : $@convention(thin) (@owned Klass) -> () {
// CHECK: bb0(%0 : @owned $Klass):
// CHECK-NEXT: %1 = begin_borrow %0 : $Klass
// CHECK-NEXT: end_borrow %1 : $Klass
// CHECK-NEXT: destroy_value %0 : $Klass
// CHECK-NEXT: tuple ()
// CHECK-NEXT: return
// CHECK-NEXT: }
sil [ossa] @lexical_lifetime_object : $@convention(thin) (@owned Klass) -> () {
bb0(%0 : @owned $Klass):
%1 = begin_borrow [lexical] %0 : $Klass
end_borrow %1 : $Klass
destroy_value %0 : $Klass
%9999 = tuple()
return %9999 : $()
}

// CHECK-LABEL: sil [ossa] @lexical_lifetime_address : $@convention(thin) (@in Klass) -> () {
// CHECK: bb0(%0 : $*Klass):
// CHECK-NEXT: %1 = alloc_stack $Klass
// CHECK-NEXT: copy_addr [take] %0 to [initialization] %1 : $*Klass
// CHECK-NEXT: destroy_addr %1 : $*Klass
// CHECK-NEXT: dealloc_stack %1 : $*Klass
// CHECK-NEXT: tuple ()
// CHECK-NEXT: return
// CHECK-NEXT: }
sil [ossa] @lexical_lifetime_address : $@convention(thin) (@in Klass) -> () {
bb0(%0 : $*Klass):
%1 = alloc_stack [lexical] $Klass
copy_addr [take] %0 to [initialization] %1 : $*Klass
destroy_addr %1 : $*Klass
dealloc_stack %1 : $*Klass
%9999 = tuple()
return %9999 : $()
}