From d002471784f911eef5dfcb9a1af36fe67e164bfa Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Fri, 10 Sep 2021 17:36:32 -0700 Subject: [PATCH 1/2] Added -enable-experimental-defined-lifetimes. The new flag will guard work on the experimental implementation of tracking lexical lifetimes in SIL. --- include/swift/Basic/LangOptions.h | 3 +++ include/swift/Option/FrontendOptions.td | 4 ++++ lib/Frontend/CompilerInvocation.cpp | 3 +++ 3 files changed, 10 insertions(+) diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index 8514f29952d36..4f45cb6137918 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -298,6 +298,9 @@ namespace swift { /// Enable experimental concurrency model. bool EnableExperimentalConcurrency = false; + /// Enable experimental support for emitting defined borrow scopes. + bool EnableExperimentalDefinedLifetimes = false; + /// Enable experimental support for named opaque result types, e.g. /// `func f() -> T`. bool EnableExperimentalNamedOpaqueTypes = false; diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index 7df8f37246a62..f1c0b21df5e20 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -243,6 +243,10 @@ def enable_experimental_concurrency : Flag<["-"], "enable-experimental-concurrency">, HelpText<"Enable experimental concurrency model">; +def enable_experimental_defined_lifetimes : + Flag<["-"], "enable-experimental-defined-lifetimes">, + HelpText<"Enable experimental defined lifetimes">; + def enable_experimental_distributed : Flag<["-"], "enable-experimental-distributed">, HelpText<"Enable experimental 'distributed' actors and functions">; diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 5140c051c961f..711738401acd1 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -422,6 +422,9 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.EnableExperimentalConcurrency |= Args.hasArg(OPT_enable_experimental_concurrency); + Opts.EnableExperimentalDefinedLifetimes |= + Args.hasArg(OPT_enable_experimental_defined_lifetimes); + Opts.EnableExperimentalNamedOpaqueTypes |= Args.hasArg(OPT_enable_experimental_named_opaque_types); From a0c47cb32eb1d5d5c40900f2456b3e95e17b5e52 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Mon, 13 Sep 2021 11:04:17 -0700 Subject: [PATCH 2/2] [SILGen] Emitted [defined] borrow scopes for lets. When -enable-experimental-defined-lifetimes is passed, SILGen emits lexical scopes for lets. --- lib/SILGen/SILGenDecl.cpp | 25 +++++++++++++--- test/SILGen/borrow.swift | 60 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 80 insertions(+), 5 deletions(-) diff --git a/lib/SILGen/SILGenDecl.cpp b/lib/SILGen/SILGenDecl.cpp index da61f262aea2c..4f1c060d56316 100644 --- a/lib/SILGen/SILGenDecl.cpp +++ b/lib/SILGen/SILGenDecl.cpp @@ -262,6 +262,10 @@ class DestroyLocalVariable : public Cleanup { void emit(SILGenFunction &SGF, CleanupLocation l, ForUnwind_t forUnwind) override { + SILValue val = SGF.VarLocs[Var].value; + if (SGF.getASTContext().LangOpts.EnableExperimentalDefinedLifetimes && + val->getOwnershipKind() != OwnershipKind::None) + SGF.B.createEndBorrow(l, val); SGF.destroyLocalVariable(l, Var); } @@ -528,13 +532,18 @@ class LetValueInitialization : public Initialization { // an argument, for example. if (value->getType().isAddress()) address = value; + SILLocation PrologueLoc(vd); + + if (SGF.getASTContext().LangOpts.EnableExperimentalDefinedLifetimes && + value->getOwnershipKind() != OwnershipKind::None) + value = SILValue( + SGF.B.createBeginBorrow(PrologueLoc, value, /*defined*/ true)); SGF.VarLocs[vd] = SILGenFunction::VarLoc::get(value); // Emit a debug_value[_addr] instruction to record the start of this value's // lifetime, if permitted to do so. if (!EmitDebugValueOnInit) return; - SILLocation PrologueLoc(vd); PrologueLoc.markAsPrologue(); SILDebugVariable DbgVar(vd->isLet(), /*ArgNo=*/0); SGF.B.emitDebugDescription(PrologueLoc, value, DbgVar); @@ -1714,8 +1723,16 @@ void SILGenFunction::destroyLocalVariable(SILLocation silLoc, VarDecl *vd) { // For 'let' bindings, we emit a release_value or destroy_addr, depending on // whether we have an address or not. SILValue Val = loc.value; - if (!Val->getType().isAddress()) - B.emitDestroyValueOperation(silLoc, Val); - else + if (!Val->getType().isAddress()) { + SILValue valueToBeDestroyed; + if (getASTContext().LangOpts.EnableExperimentalDefinedLifetimes && + Val->getOwnershipKind() != OwnershipKind::None) { + auto *inst = cast(Val.getDefiningInstruction()); + valueToBeDestroyed = inst->getOperand(); + } else { + valueToBeDestroyed = Val; + } + B.emitDestroyValueOperation(silLoc, valueToBeDestroyed); + } else B.createDestroyAddr(silLoc, Val); } diff --git a/test/SILGen/borrow.swift b/test/SILGen/borrow.swift index 6616d8d3fc4d9..a858ecd8176fe 100644 --- a/test/SILGen/borrow.swift +++ b/test/SILGen/borrow.swift @@ -1,5 +1,5 @@ -// RUN: %target-swift-emit-silgen -module-name borrow -parse-stdlib %s | %FileCheck %s +// RUN: %target-swift-emit-silgen -enable-experimental-defined-lifetimes -module-name borrow -parse-stdlib %s | %FileCheck %s import Swift @@ -8,9 +8,12 @@ final class D {} // Make sure that we insert the borrow for a ref_element_addr lvalue in the // proper place. final class C { + init() {} + init?(failably: ()) {} var d: D = D() } +func use(_ t: T) {} func useD(_ d: D) {} // CHECK-LABEL: sil hidden [ossa] @$s6borrow44lvalueBorrowShouldBeAtEndOfFormalAccessScope{{.*}} : $@convention(thin) () -> () { @@ -33,3 +36,58 @@ func lvalueBorrowShouldBeAtEndOfFormalAccessScope() { var c = C() useD(c.d) } + +// CHECK-LABEL: sil hidden [ossa] @defined_borrow_let_class +// CHECK: [[INIT_C:%[^,]+]] = function_ref @$s6borrow1CCACycfC +// CHECK: [[INSTANCE:%[^,]+]] = apply [[INIT_C]]({{%[0-9]+}}) +// CHECK: [[BORROW:%[^,]+]] = begin_borrow [defined] [[INSTANCE]] : $C +// CHECK: end_borrow [[BORROW:%[^,]+]] +// CHECK-LABEL: } // end sil function 'defined_borrow_let_class' +@_silgen_name("defined_borrow_let_class") +func defined_borrow_let_class() { + let c = C() +} + +// CHECK-LABEL: sil hidden [ossa] @defined_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 [defined] [[INSTANCE]] : $C +// CHECK: end_borrow [[BORROW]] : $C +// CHECK-LABEL: // end sil function 'defined_borrow_if_let_class' +@_silgen_name("defined_borrow_if_let_class") +func defined_borrow_if_let_class() { + if let c = C(failably: ()) { + use(()) + } +} + +struct S { + let c: C +} + +// CHECK-LABEL: sil hidden [ossa] @defined_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 [defined] [[INSTANCE]] : $S +// CHECK: end_borrow [[BORROW:%[^,]+]] +// CHECK-LABEL: } // end sil function 'defined_borrow_let_class_in_struct' +@_silgen_name("defined_borrow_let_class_in_struct") +func defined_borrow_let_class_in_struct() { + let s = S(c: C()) +} + +enum E { + case e(C) +} + +// CHECK-LABEL: sil hidden [ossa] @defined_borrow_let_class_in_enum +// CHECK: [[INSTANCE:%[^,]+]] = enum $E, #E.e!enumelt, {{%[0-9]+}} : $C +// CHECK: [[BORROW:%[^,]+]] = begin_borrow [defined] [[INSTANCE]] : $E +// CHECK: end_borrow [[BORROW:%[^,]+]] +// CHECK-LABEL: } // end sil function 'defined_borrow_let_class_in_enum' +@_silgen_name("defined_borrow_let_class_in_enum") +func defined_borrow_let_class_in_enum() { + let s = E.e(C()) +}