Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,12 @@ private func canPromote(allocBox: AllocBoxInst) -> (promotableArguments: [Functi
fallthrough
case is MarkUninitializedInst, is CopyValueInst, is MoveValueInst:
worklist.pushIfNotVisited(use.instruction as! SingleValueInstruction)
case let markDep as MarkDependenceInstruction:
if markDep.baseOperand == use {
// mark_dependence base value uses will be directly converted to base address uses.
break
}
return nil
case let apply as ApplySite:
if apply.isCallee(operand: use) {
// Calling the closure does not escape the closure value.
Expand Down Expand Up @@ -226,6 +232,9 @@ private struct FunctionSpecializations {
// First, replace the instruction with the original `box`, which adds more uses to `box`.
// In a later iteration those additional uses will be handled.
(user as! SingleValueInstruction).replace(with: box, context)
case let markDep as MarkDependenceInstruction:
assert(markDep.baseOperand == use)
markDep.baseOperand.set(to: stack, context)
case let apply as ApplySite:
specialize(apply: apply, context)
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,8 @@ private struct DiagnoseDependence {
}
onError()

log(" Error: lifetime-dependence violation - escaping use")

// Identify the escaping variable.
let escapingVar = LifetimeVariable(usedBy: operand, context)
if let varDecl = escapingVar.varDecl {
Expand Down
15 changes: 15 additions & 0 deletions include/swift/SIL/SILInstruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -8964,6 +8964,21 @@ class MarkDependenceInstruction {
return SILValue();
}

void setBase(SILValue newVal) {
if (inst) {
switch (inst->getKind()) {
case SILInstructionKind::MarkDependenceInst:
cast<MarkDependenceInst>(inst)->setBase(newVal);
break;
case SILInstructionKind::MarkDependenceAddrInst:
cast<MarkDependenceAddrInst>(inst)->setBase(newVal);
break;
default:
break;
}
}
}

SILValue getDependent() const {
if (inst) {
switch (inst->getKind()) {
Expand Down
13 changes: 13 additions & 0 deletions lib/SILOptimizer/Transforms/AllocBoxToStack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,13 @@ static SILInstruction *recursivelyFindBoxOperandsPromotableToAddress(
isa<EndBorrowInst>(User))
continue;

// mark_dependence base value uses will be directly converted to base
// address uses.
if (auto mdi = MarkDependenceInstruction(User)) {
if (Op->get() == mdi.getBase())
continue;
}

// If our user instruction is a copy_value or a mark_uninitialized, visit
// the users recursively.
if (isa<MarkUninitializedInst>(User) || isa<CopyValueInst>(User) ||
Expand Down Expand Up @@ -738,6 +745,12 @@ static bool rewriteAllocBoxAsAllocStack(AllocBoxInst *ABI) {
Inst->eraseFromParent();
continue;
}
// mark_dependence base value uses will be directly converted to base
// address uses.
if (auto mdi = MarkDependenceInstruction(User)) {
mdi.setBase(StackBox);
continue;
}

assert(isa<StrongReleaseInst>(User) || isa<StrongRetainInst>(User) ||
isa<DeallocBoxInst>(User) || isa<ProjectBoxInst>(User) ||
Expand Down
69 changes: 69 additions & 0 deletions test/SILOptimizer/allocbox_to_stack_ne.sil
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// RUN: %target-sil-opt -enable-experimental-feature LifetimeDependence -allocbox-to-stack %s | %FileCheck %s
// RUN: %target-sil-opt -enable-experimental-feature LifetimeDependence -legacy-allocbox-to-stack %s | %FileCheck %s

// REQUIRES: swift_feature_LifetimeDependence

import Swift
import Builtin

public struct View : ~Escapable {
let _ptr: UnsafeRawPointer
}

sil [ossa] @initFn : $@convention(method) (UnsafeRawPointer, @thin View.Type) -> @lifetime(borrow 0) @owned View
sil [ossa] @initFnAddr : $@convention(thin) (UnsafeRawPointer, @thin View.Type) -> @lifetime(borrow 0) @out View

// CHECK-LABEL: sil [ossa] @testBoxedParam :
// CHECK-NOT: alloc_box
// CHECK-LABEL: } // end sil function 'testBoxedParam'
sil [ossa] @testBoxedParam : $@convention(thin) (@owned View) -> @lifetime(copy 0) @owned View {
bb0(%0 : @noImplicitCopy @_eagerMove @owned $View):
%1 = alloc_box ${ var @moveOnly View }, var, name "that"
%2 = begin_borrow [var_decl] %1
%3 = project_box %2, 0
%4 = moveonlywrapper_to_copyable_addr %3
store %0 to [init] %4
%6 = metatype $@thin View.Type
%7 = begin_access [read] [static] %3
%8 = mark_unresolved_non_copyable_value [no_consume_or_assign] %7
%9 = struct_element_addr %8, #View._ptr
%10 = load_borrow %9
%11 = moveonlywrapper_to_copyable [guaranteed] %10
end_borrow %10
end_access %7
%14 = function_ref @initFn : $@convention(method) (UnsafeRawPointer, @thin View.Type) -> @lifetime(borrow 0) @owned View
%15 = apply %14(%11, %6) : $@convention(method) (UnsafeRawPointer, @thin View.Type) -> @lifetime(borrow 0) @owned View
%16 = mark_dependence [unresolved] %15 on %2
end_borrow %2
destroy_value %1
return %16
}

// CHECK-LABEL: sil [ossa] @testBoxedParamAddr :
// CHECK-NOT: alloc_box
// CHECK-LABEL: } // end sil function 'testBoxedParamAddr'
sil [ossa] @testBoxedParamAddr : $@convention(thin) (@owned View) -> () {
bb0(%0 : @noImplicitCopy @_eagerMove @owned $View):
%1 = alloc_box ${ var @moveOnly View }, var, name "that"
%2 = begin_borrow [var_decl] %1
%3 = project_box %2, 0
%4 = moveonlywrapper_to_copyable_addr %3
store %0 to [init] %4
%6 = metatype $@thin View.Type
%7 = begin_access [read] [static] %3
%8 = mark_unresolved_non_copyable_value [no_consume_or_assign] %7
%9 = struct_element_addr %8, #View._ptr
%10 = load_borrow %9
%11 = moveonlywrapper_to_copyable [guaranteed] %10
end_borrow %10
end_access %7
%14 = alloc_stack $View
%15 = function_ref @initFnAddr : $@convention(thin) (UnsafeRawPointer, @thin View.Type) -> @lifetime(borrow 0) @out View
%16 = apply %15(%14, %11, %6) : $@convention(thin) (UnsafeRawPointer, @thin View.Type) -> @lifetime(borrow 0) @out View
mark_dependence_addr [unresolved] %14 on %2
dealloc_stack %14
end_borrow %2
destroy_value %1
%99 = tuple ()
return %99
}
33 changes: 30 additions & 3 deletions test/SILOptimizer/lifetime_dependence/verify_diagnostics.sil
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,14 @@ sil @useNE : $@convention(thin) (@guaranteed NE) -> ()
sil @reborrowNE : $@convention(thin) (@guaranteed NE) -> @lifetime(borrow 0) @owned NE

sil @initHolder : $@convention(thin) () -> @out Holder
sil @getHolderNE : $@convention(thin) (@guaranteed Holder) -> @lifetime(borrow 0) @owned NE
sil @getNE : $@convention(thin) (@in_guaranteed Holder) -> @lifetime(borrow address_for_deps 0) @owned NE
sil @getNEIndirect : $@convention(thin) (@in_guaranteed Holder) -> @lifetime(borrow address_for_deps 0) @out NE

sil @initTrivialHolder : $@convention(thin) () -> @out TrivialHolder
sil @getTrivialNE : $@convention(thin) (@in_guaranteed TrivialHolder) -> @lifetime(borrow address_for_deps 0) @owned NE

sil @makeHolder: $@convention(method) (@thin Holder.Type) -> @owned Holder // user: %6
sil @makeHolder: $@convention(thin) () -> @owned Holder
sil @getGeneric : $@convention(thin) <τ_0_0 where τ_0_0 : ~Escapable> (@guaranteed Holder, @thick τ_0_0.Type) -> @lifetime(borrow 0) @out τ_0_0 // user: %12

// Test returning a owned dependence on a trivial value
Expand Down Expand Up @@ -196,9 +197,8 @@ sil [ossa] @testBorrowAddress : $@convention(thin) <T where T : ~Escapable> (@th
bb0(%0 : $*T, %1 : $@thick T.Type):
debug_value %1, let, name "type", argno 1
%3 = alloc_stack [lexical] [var_decl] $Holder, var, name "holder", type $Holder
%4 = metatype $@thin Holder.Type

%5 = function_ref @makeHolder : $@convention(method) (@thin Holder.Type) -> @owned Holder
%5 = function_ref @makeHolder : $@convention(thin) () -> @owned Holder
%6 = apply %5(%4) : $@convention(method) (@thin Holder.Type) -> @owned Holder
store %6 to [init] %3
%8 = alloc_stack [lexical] [var_decl] $T, let, name "result"
Expand Down Expand Up @@ -246,3 +246,30 @@ bb0(%0 : $*NE, %1 : $*Holder):
%18 = tuple ()
return %18
}

// Test dependence on an alloc_stack variable.
//
//!!!
sil private [ossa] @testOnStackHolder : $@convention(thin) (@guaranteed Holder) -> () {
bb0(%0 : @guaranteed $Holder):
%6 = alloc_stack [lexical] [var_decl] $Holder, let, name "c", type $Holder

%10 = function_ref @makeHolder : $@convention(thin) () -> @owned Holder
%11 = apply %10() : $@convention(thin) () -> @owned Holder
store %11 to [init] %6
%13 = load_borrow %6

%14 = function_ref @getHolderNE : $@convention(thin) (@guaranteed Holder) -> @lifetime(borrow 0) @owned NE
%15 = apply %14(%13) : $@convention(thin) (@guaranteed Holder) -> @lifetime(borrow 0) @owned NE
%16 = mark_dependence [unresolved] %15 on %6
%17 = move_value [var_decl] %16
end_borrow %13

%20 = function_ref @useNE : $@convention(thin) (@guaranteed NE) -> ()
%21 = apply %20(%17) : $@convention(thin) (@guaranteed NE) -> ()
destroy_value %17
destroy_addr %6
dealloc_stack %6
%26 = tuple ()
return %26
}