From a3be84610c566b23f7184a61cf86c1d1ddc0f46e Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Thu, 30 Sep 2021 16:45:38 -0700 Subject: [PATCH 1/3] [SILGen] NFC: Moved EndBorrowCleanup to header. Previously the declaration and definition of EndBorrowCleanup were both within SILGenExpr.cpp. That prevented the usage of cleanups which end borrow scopes within other files. Here, the declaration is moved to Cleanup.h. The necessary changes are made to SILGenExpr.cpp to keep the definition of member functions in place. --- lib/SILGen/Cleanup.h | 8 ++++++++ lib/SILGen/SILGenExpr.cpp | 40 ++++++++++++++++++--------------------- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/lib/SILGen/Cleanup.h b/lib/SILGen/Cleanup.h index 6c21245fb8e6a..ed89eb83e33b1 100644 --- a/lib/SILGen/Cleanup.h +++ b/lib/SILGen/Cleanup.h @@ -133,6 +133,14 @@ class LLVM_LIBRARY_VISIBILITY Cleanup { } }; +struct EndBorrowCleanup final : Cleanup { + SILValue borrowedValue; + EndBorrowCleanup(SILValue borrowedValue); + void emit(SILGenFunction &SGF, CleanupLocation l, + ForUnwind_t forUnwind) override; + void dump(SILGenFunction &) const override; +}; + /// A cleanup depth is generally used to denote the set of cleanups /// between the given cleanup (and not including it) and the top of /// the stack. diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 819efc2582d1f..d61214cd1e00a 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -158,34 +158,30 @@ SILGenFunction::emitManagedBeginBorrow(SILLocation loc, SILValue v, return emitManagedBorrowedRValueWithCleanup(v, bbi, lowering); } -namespace { - -struct EndBorrowCleanup : Cleanup { - SILValue borrowedValue; - - EndBorrowCleanup(SILValue borrowedValue) : borrowedValue(borrowedValue) { - if (auto *arg = dyn_cast(borrowedValue)) { - if (auto *ti = arg->getSingleTerminator()) { - assert(!ti->isTransformationTerminator() && - "Transforming terminators do not have end_borrow"); - } +EndBorrowCleanup::EndBorrowCleanup(SILValue borrowedValue) + : borrowedValue(borrowedValue) { + if (auto *arg = dyn_cast(borrowedValue)) { + if (auto *ti = arg->getSingleTerminator()) { + assert(!ti->isTransformationTerminator() && + "Transforming terminators do not have end_borrow"); } } +} - void emit(SILGenFunction &SGF, CleanupLocation l, - ForUnwind_t forUnwind) override { - SGF.B.createEndBorrow(l, borrowedValue); - } +void EndBorrowCleanup::emit(SILGenFunction &SGF, CleanupLocation l, + ForUnwind_t forUnwind) { + SGF.B.createEndBorrow(l, borrowedValue); +} - void dump(SILGenFunction &) const override { +void EndBorrowCleanup::dump(SILGenFunction &) const { #ifndef NDEBUG - llvm::errs() << "EndBorrowCleanup " - << "State:" << getState() << "\n" - << "borrowed:" << borrowedValue - << "\n"; + llvm::errs() << "EndBorrowCleanup " + << "State:" << getState() << "\n" + << "borrowed:" << borrowedValue << "\n"; #endif - } -}; +} + +namespace { struct FormalEvaluationEndBorrowCleanup : Cleanup { FormalEvaluationContext::stable_iterator Depth; From b63cc13ebb02c3e7d62b2a8b2b4a9b11192aec8a Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Thu, 30 Sep 2021 16:49:08 -0700 Subject: [PATCH 2/3] [SILGen] Test: Moved tests for lexical lifetimes. Previously, tests for the generation of lexical borrow scopes when -enable-experimental-lexical-lifetimes is passed had been dumped into the file testing the creation of all borrow scopes. Here, the file containing tests on general borrow scopes is reverted to its original state and a new file dedicated to lexical lifetimes is added. Subsequent tests for SILGen's creation of lexical lifetimes will go into that new file. --- test/SILGen/borrow.swift | 58 +------------------- test/SILGen/lexical_lifetime.swift | 85 ++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 57 deletions(-) create mode 100644 test/SILGen/lexical_lifetime.swift diff --git a/test/SILGen/borrow.swift b/test/SILGen/borrow.swift index 9688dfb7602e3..02fa3238284ce 100644 --- a/test/SILGen/borrow.swift +++ b/test/SILGen/borrow.swift @@ -1,5 +1,5 @@ -// RUN: %target-swift-emit-silgen -enable-experimental-lexical-lifetimes -module-name borrow -parse-stdlib %s | %FileCheck %s +// RUN: %target-swift-emit-silgen -module-name borrow -parse-stdlib %s | %FileCheck %s import Swift @@ -13,7 +13,6 @@ final class C { var d: D = D() } -func use(_ t: T) {} func useD(_ d: D) {} // CHECK-LABEL: sil hidden [ossa] @$s6borrow44lvalueBorrowShouldBeAtEndOfFormalAccessScope{{.*}} : $@convention(thin) () -> () { @@ -36,58 +35,3 @@ func lvalueBorrowShouldBeAtEndOfFormalAccessScope() { var c = C() useD(c.d) } - -// CHECK-LABEL: sil hidden [ossa] @lexical_borrow_let_class -// CHECK: [[INIT_C:%[^,]+]] = function_ref @$s6borrow1CCACycfC -// CHECK: [[INSTANCE:%[^,]+]] = apply [[INIT_C]]({{%[0-9]+}}) -// CHECK: [[BORROW:%[^,]+]] = begin_borrow [lexical] [[INSTANCE]] : $C -// CHECK: end_borrow [[BORROW:%[^,]+]] -// CHECK-LABEL: } // end sil function 'lexical_borrow_let_class' -@_silgen_name("lexical_borrow_let_class") -func lexical_borrow_let_class() { - let c = C() -} - -// CHECK-LABEL: sil hidden [ossa] @lexical_borrow_if_let_class -// CHECK: [[INIT_C:%[^,]+]] = function_ref @$s6borrow1CC8failablyACSgyt_tcfC -// CHECK: [[INSTANCE:%[^,]+]] = apply [[INIT_C]]({{%[^,]+}}) -// CHECK: switch_enum [[INSTANCE]] : $Optional, case #Optional.some!enumelt: [[BASIC_BLOCK2:bb[^,]+]], case #Optional.none!enumelt: {{bb[^,]+}} -// CHECK: [[BASIC_BLOCK2]]([[INSTANCE:%[^,]+]] : @owned $C): -// CHECK: [[BORROW:%[^,]+]] = begin_borrow [lexical] [[INSTANCE]] : $C -// CHECK: end_borrow [[BORROW]] : $C -// CHECK-LABEL: // end sil function 'lexical_borrow_if_let_class' -@_silgen_name("lexical_borrow_if_let_class") -func lexical_borrow_if_let_class() { - if let c = C(failably: ()) { - use(()) - } -} - -struct S { - let c: C -} - -// CHECK-LABEL: sil hidden [ossa] @lexical_borrow_let_class_in_struct -// CHECK: [[INIT_S:%[^,]+]] = function_ref @$s6borrow1SV1cAcA1CC_tcfC -// CHECK: [[INSTANCE:%[^,]+]] = apply [[INIT_S]]({{%[0-9]+}}, {{%[0-9]+}}) -// CHECK: [[BORROW:%[^,]+]] = begin_borrow [lexical] [[INSTANCE]] : $S -// CHECK: end_borrow [[BORROW:%[^,]+]] -// CHECK-LABEL: } // end sil function 'lexical_borrow_let_class_in_struct' -@_silgen_name("lexical_borrow_let_class_in_struct") -func lexical_borrow_let_class_in_struct() { - let s = S(c: C()) -} - -enum E { - case e(C) -} - -// CHECK-LABEL: sil hidden [ossa] @lexical_borrow_let_class_in_enum -// CHECK: [[INSTANCE:%[^,]+]] = enum $E, #E.e!enumelt, {{%[0-9]+}} : $C -// CHECK: [[BORROW:%[^,]+]] = begin_borrow [lexical] [[INSTANCE]] : $E -// CHECK: end_borrow [[BORROW:%[^,]+]] -// CHECK-LABEL: } // end sil function 'lexical_borrow_let_class_in_enum' -@_silgen_name("lexical_borrow_let_class_in_enum") -func lexical_borrow_let_class_in_enum() { - let s = E.e(C()) -} diff --git a/test/SILGen/lexical_lifetime.swift b/test/SILGen/lexical_lifetime.swift new file mode 100644 index 0000000000000..6797c0c7d6bbe --- /dev/null +++ b/test/SILGen/lexical_lifetime.swift @@ -0,0 +1,85 @@ +// RUN: %target-swift-emit-silgen -enable-experimental-lexical-lifetimes -module-name borrow -parse-stdlib %s | %FileCheck %s + +import Swift + +//////////////////////////////////////////////////////////////////////////////// +// Declarations {{ +//////////////////////////////////////////////////////////////////////////////// + +final class C { + init() {} + init?(failably: ()) {} +} + +struct S { + let c: C +} + +enum E { + case e(C) +} + +func use(_ t: T) {} + +//////////////////////////////////////////////////////////////////////////////// +// Declarations }} +//////////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////////// +// Tests {{ +//////////////////////////////////////////////////////////////////////////////// + +// let bindings: + +// CHECK-LABEL: sil hidden [ossa] @lexical_borrow_let_class +// CHECK: [[INIT_C:%[^,]+]] = function_ref @$s6borrow1CCACycfC +// CHECK: [[INSTANCE:%[^,]+]] = apply [[INIT_C]]({{%[0-9]+}}) +// CHECK: [[BORROW:%[^,]+]] = begin_borrow [lexical] [[INSTANCE]] : $C +// CHECK: end_borrow [[BORROW:%[^,]+]] +// CHECK-LABEL: } // end sil function 'lexical_borrow_let_class' +@_silgen_name("lexical_borrow_let_class") +func lexical_borrow_let_class() { + let c = C() +} + +// CHECK-LABEL: sil hidden [ossa] @lexical_borrow_if_let_class +// CHECK: [[INIT_C:%[^,]+]] = function_ref @$s6borrow1CC8failablyACSgyt_tcfC +// CHECK: [[INSTANCE:%[^,]+]] = apply [[INIT_C]]({{%[^,]+}}) +// CHECK: switch_enum [[INSTANCE]] : $Optional, case #Optional.some!enumelt: [[BASIC_BLOCK2:bb[^,]+]], case #Optional.none!enumelt: {{bb[^,]+}} +// CHECK: [[BASIC_BLOCK2]]([[INSTANCE:%[^,]+]] : @owned $C): +// CHECK: [[BORROW:%[^,]+]] = begin_borrow [lexical] [[INSTANCE]] : $C +// CHECK: end_borrow [[BORROW]] : $C +// CHECK-LABEL: // end sil function 'lexical_borrow_if_let_class' +@_silgen_name("lexical_borrow_if_let_class") +func lexical_borrow_if_let_class() { + if let c = C(failably: ()) { + use(()) + } +} + +// CHECK-LABEL: sil hidden [ossa] @lexical_borrow_let_class_in_struct +// CHECK: [[INIT_S:%[^,]+]] = function_ref @$s6borrow1SV1cAcA1CC_tcfC +// CHECK: [[INSTANCE:%[^,]+]] = apply [[INIT_S]]({{%[0-9]+}}, {{%[0-9]+}}) +// CHECK: [[BORROW:%[^,]+]] = begin_borrow [lexical] [[INSTANCE]] : $S +// CHECK: end_borrow [[BORROW:%[^,]+]] +// CHECK-LABEL: } // end sil function 'lexical_borrow_let_class_in_struct' +@_silgen_name("lexical_borrow_let_class_in_struct") +func lexical_borrow_let_class_in_struct() { + let s = S(c: C()) +} + +// CHECK-LABEL: sil hidden [ossa] @lexical_borrow_let_class_in_enum +// CHECK: [[INSTANCE:%[^,]+]] = enum $E, #E.e!enumelt, {{%[0-9]+}} : $C +// CHECK: [[BORROW:%[^,]+]] = begin_borrow [lexical] [[INSTANCE]] : $E +// CHECK: end_borrow [[BORROW:%[^,]+]] +// CHECK-LABEL: } // end sil function 'lexical_borrow_let_class_in_enum' +@_silgen_name("lexical_borrow_let_class_in_enum") +func lexical_borrow_let_class_in_enum() { + let s = E.e(C()) +} + +//////////////////////////////////////////////////////////////////////////////// +// Test }} +//////////////////////////////////////////////////////////////////////////////// + From 29faebf2b8843d8715bfba20aa288b2981d34d9f Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Thu, 30 Sep 2021 17:24:09 -0700 Subject: [PATCH 3/3] [SILGen] Gave arguments lexical lifetimes. When the frontend flag -enable-experimental-lexical-lifetimes is passed, arguments of non-trivial, non-address types get lexical borrow scopes. The borrow scope begins in the function prolog. Usages of the arguments are actually usages of the borrowed value. The borrow scope is ended when the function's scope ends. --- lib/SILGen/SILGenProlog.cpp | 8 +++++- test/SILGen/lexical_lifetime.swift | 45 ++++++++++++++++++++++++++++-- 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/lib/SILGen/SILGenProlog.cpp b/lib/SILGen/SILGenProlog.cpp index ab734dda666d8..8bd4a02ba9a2e 100644 --- a/lib/SILGen/SILGenProlog.cpp +++ b/lib/SILGen/SILGenProlog.cpp @@ -251,10 +251,15 @@ struct ArgumentInitHelper { // Leave the cleanup on the argument, if any, in place to consume the // argument if we're responsible for it. } - SGF.VarLocs[pd] = SILGenFunction::VarLoc::get(argrv.getValue()); SILValue value = argrv.getValue(); SILDebugVariable varinfo(pd->isImmutable(), ArgNo); if (!argrv.getType().isAddress()) { + if (SGF.getASTContext().LangOpts.EnableExperimentalLexicalLifetimes && + value->getOwnershipKind() != OwnershipKind::None) { + value = + SILValue(SGF.B.createBeginBorrow(loc, value, /*isLexical*/ true)); + SGF.Cleanups.pushCleanup(value); + } SGF.B.createDebugValue(loc, value, varinfo); } else { if (auto AllocStack = dyn_cast(value)) @@ -262,6 +267,7 @@ struct ArgumentInitHelper { else SGF.B.createDebugValueAddr(loc, value, varinfo); } + SGF.VarLocs[pd] = SILGenFunction::VarLoc::get(value); } void emitParam(ParamDecl *PD) { diff --git a/test/SILGen/lexical_lifetime.swift b/test/SILGen/lexical_lifetime.swift index 6797c0c7d6bbe..3ff68703ad39f 100644 --- a/test/SILGen/lexical_lifetime.swift +++ b/test/SILGen/lexical_lifetime.swift @@ -15,11 +15,16 @@ struct S { let c: C } +struct Trivial { + let i: Int +} + enum E { case e(C) } -func use(_ t: T) {} +@_silgen_name("use_generic") +func use_generic(_ t: T) {} //////////////////////////////////////////////////////////////////////////////// // Declarations }} @@ -54,7 +59,7 @@ func lexical_borrow_let_class() { @_silgen_name("lexical_borrow_if_let_class") func lexical_borrow_if_let_class() { if let c = C(failably: ()) { - use(()) + use_generic(()) } } @@ -79,6 +84,42 @@ func lexical_borrow_let_class_in_enum() { let s = E.e(C()) } +// arguments: + +// CHECK-LABEL: sil hidden [ossa] @lexical_borrow_arg_guaranteed_class : $@convention(thin) (@guaranteed C) -> () { +// CHECK: {{bb[^,]+}}([[INSTANCE:%[^,]+]] : @guaranteed $C): +// CHECK: [[LIFETIME:%[^,]+]] = begin_borrow [lexical] [[INSTANCE]] +// CHECK: debug_value [[LIFETIME]] +// CHECK: [[ADDR:%[^,]+]] = alloc_stack $C +// CHECK: store_borrow [[LIFETIME]] to [[ADDR]] +// CHECK: [[USE_GENERIC:%[^,]+]] = function_ref @use_generic +// CHECK: [[REGISTER_6:%[^,]+]] = apply [[USE_GENERIC]]([[ADDR]]) +// CHECK: dealloc_stack [[ADDR]] +// CHECK: end_borrow [[LIFETIME]] +// CHECK: [[RETVAL:%[^,]+]] = tuple () +// CHECK: return [[RETVAL]] +// CHECK-LABEL: } // end sil function 'lexical_borrow_arg_guaranteed_class' +@_silgen_name("lexical_borrow_arg_guaranteed_class") +func lexical_borrow_arg_guaranteed_class(_ c: C) { + use_generic(c) +} + +// CHECK-LABEL: sil hidden [ossa] @lexical_borrow_arg_class_addr : $@convention(thin) (@inout C) -> () { +// CHECK-NOT: begin_borrow [lexical] +// CHECK-LABEL: } // end sil function 'lexical_borrow_arg_class_addr' +@_silgen_name("lexical_borrow_arg_class_addr") +func lexical_borrow_arg_class_addr(_ c: inout C) { + use_generic(c) +} + +// CHECK-LABEL: sil hidden [ossa] @lexical_borrow_arg_trivial : $@convention(thin) (Trivial) -> () { +// CHECK-NOT: begin_borrow [lexical] +// CHECK-LABEL: } // end sil function 'lexical_borrow_arg_trivial' +@_silgen_name("lexical_borrow_arg_trivial") +func lexical_borrow_arg_trivial(_ trivial: Trivial) { + use_generic(trivial) +} + //////////////////////////////////////////////////////////////////////////////// // Test }} ////////////////////////////////////////////////////////////////////////////////