Skip to content

Commit ea2cab3

Browse files
Merge pull request #24925 from aschwaighofer/dynamically_replaceable_closure_fixes_5.1
[5.1] Fix closurescope analysis and capture promotion
2 parents 911cd53 + 4248822 commit ea2cab3

File tree

5 files changed

+99
-2
lines changed

5 files changed

+99
-2
lines changed

include/swift/SIL/SILInstruction.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1806,6 +1806,8 @@ class ApplyInstBase<Impl, Base, false> : public Base {
18061806
/// issues. It is at the bottom of the file.
18071807
SILFunction *getCalleeFunction() const;
18081808

1809+
bool isCalleeDynamicallyReplaceable() const;
1810+
18091811
/// Gets the referenced function if the callee is a function_ref instruction.
18101812
SILFunction *getReferencedFunction() const {
18111813
if (auto *FRI = dyn_cast<FunctionRefBaseInst>(getCallee()))
@@ -7683,6 +7685,27 @@ SILValue ApplyInstBase<Impl, Base, false>::getCalleeOrigin() const {
76837685
}
76847686
}
76857687

7688+
template <class Impl, class Base>
7689+
bool ApplyInstBase<Impl, Base, false>::isCalleeDynamicallyReplaceable() const {
7690+
SILValue Callee = getCalleeOrigin();
7691+
7692+
while (true) {
7693+
if (auto *FRI = dyn_cast<FunctionRefInst>(Callee))
7694+
return false;
7695+
7696+
if (auto *FRI = dyn_cast<DynamicFunctionRefInst>(Callee))
7697+
return true;
7698+
if (auto *FRI = dyn_cast<PreviousDynamicFunctionRefInst>(Callee))
7699+
return true;
7700+
7701+
if (auto *PAI = dyn_cast<PartialApplyInst>(Callee)) {
7702+
Callee = PAI->getCalleeOrigin();
7703+
continue;
7704+
}
7705+
return false;
7706+
}
7707+
}
7708+
76867709
template <class Impl, class Base>
76877710
SILFunction *ApplyInstBase<Impl, Base, false>::getCalleeFunction() const {
76887711
SILValue Callee = getCalleeOrigin();

lib/SILOptimizer/Analysis/ClosureScope.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ class ClosureScopeData {
8282
void recordScope(PartialApplyInst *PAI) {
8383
// Only track scopes of non-escaping closures.
8484
auto closureTy = PAI->getCallee()->getType().castTo<SILFunctionType>();
85-
if (!isNonEscapingClosure(closureTy))
85+
if (!isNonEscapingClosure(closureTy) ||
86+
PAI->isCalleeDynamicallyReplaceable())
8687
return;
8788

8889
auto closureFunc = PAI->getCalleeFunction();

lib/SILOptimizer/IPO/CapturePromotion.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -976,7 +976,7 @@ bool isPartialApplyNonEscapingUser(Operand *CurrentOp, PartialApplyInst *PAI,
976976
unsigned Index = OpNo - 1 + closureConv.getNumSILArguments();
977977

978978
auto *Fn = PAI->getReferencedFunction();
979-
if (!Fn || !Fn->isDefinition()) {
979+
if (!Fn || !Fn->isDefinition() || Fn->isDynamicallyReplaceable()) {
980980
LLVM_DEBUG(llvm::dbgs() << " FAIL! Not a direct function definition "
981981
"reference.\n");
982982
return false;

test/SILOptimizer/access_enforcement_selection.sil

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,24 @@ bb0:
9999
return %9 : $()
100100
}
101101

102+
sil [dynamically_replacable] [ossa] @closureCapturingByStorageAddress2 : $@convention(thin) (@inout_aliasable Builtin.Int64) -> ()
102103

104+
// Make sure that we handle dynamic_function_ref.
105+
sil hidden [ossa] @escapeAsArgumentToPartialApplyDynamic : $@convention(thin) () -> () {
106+
bb0:
107+
%2 = alloc_box ${ var Builtin.Int64 }, var, name "x"
108+
%3 = project_box %2 : ${ var Builtin.Int64 }, 0
109+
%4 = function_ref @takesInoutAndClosure : $@convention(thin) (@inout Builtin.Int64, @guaranteed @callee_guaranteed () -> ()) -> ()
110+
%5 = dynamic_function_ref @closureCapturingByStorageAddress2 : $@convention(thin) (@inout_aliasable Builtin.Int64) -> ()
111+
%6 = partial_apply [callee_guaranteed] %5(%3) : $@convention(thin) (@inout_aliasable Builtin.Int64) -> ()
112+
%7 = begin_access [modify] [unknown] %3 : $*Builtin.Int64
113+
%8 = apply %4(%7, %6) : $@convention(thin) (@inout Builtin.Int64, @guaranteed @callee_guaranteed () -> ()) -> ()
114+
end_access %7 : $*Builtin.Int64
115+
destroy_value %6 : $@callee_guaranteed () -> ()
116+
destroy_value %2 : ${ var Builtin.Int64 }
117+
%9 = tuple ()
118+
return %9 : $()
119+
}
103120
// Test static enforcement of copied boxes.
104121
// FIXME: Oops... We make this dynamic.
105122
//

test/SILOptimizer/capture_promotion.sil

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,3 +474,59 @@ bb0:
474474
%tuple = tuple()
475475
return %tuple : $()
476476
}
477+
478+
sil private [dynamically_replacable] @closure0_guaranteed2 : $@convention(thin) (@guaranteed <τ_0_0> { var τ_0_0 } <Foo>, @guaranteed <τ_0_0> { var τ_0_0 } <Baz>, @guaranteed <τ_0_0> { var τ_0_0 } <Int>) -> Int {
479+
bb0(%0 : $<τ_0_0> { var τ_0_0 } <Foo>, %2 : $<τ_0_0> { var τ_0_0 } <Baz>, %4 : $<τ_0_0> { var τ_0_0 } <Int>):
480+
%1 = project_box %0 : $<τ_0_0> { var τ_0_0 } <Foo>, 0
481+
%3 = project_box %2 : $<τ_0_0> { var τ_0_0 } <Baz>, 0
482+
%5 = project_box %4 : $<τ_0_0> { var τ_0_0 } <Int>, 0
483+
%6 = tuple ()
484+
%7 = function_ref @dummy_func : $@convention(thin) (Int, Int, Int) -> Int
485+
%8 = load %1 : $*Foo
486+
strong_retain %8 : $Foo
487+
%10 = class_method %8 : $Foo, #Foo.foo!1 : (Foo) -> () -> Int, $@convention(method) (@guaranteed Foo) -> Int
488+
%11 = apply %10(%8) : $@convention(method) (@guaranteed Foo) -> Int
489+
%12 = struct_element_addr %3 : $*Baz, #Baz.x
490+
%13 = load %12 : $*Int
491+
%14 = load %5 : $*Int
492+
%15 = apply %7(%11, %13, %14) : $@convention(thin) (Int, Int, Int) -> Int
493+
return %15 : $Int
494+
}
495+
496+
// This should not crash.
497+
sil @test_capture_promotion_dynamic_function_ref : $@convention(thin) () -> () {
498+
bb0:
499+
%0 = tuple ()
500+
%1 = alloc_box $<τ_0_0> { var τ_0_0 } <Foo>
501+
%1a = project_box %1 : $<τ_0_0> { var τ_0_0 } <Foo>, 0
502+
%2 = function_ref @foo_allocating_init : $@convention(thin) (@thick Foo.Type) -> @owned Foo
503+
%3 = metatype $@thick Foo.Type
504+
%4 = apply %2(%3) : $@convention(thin) (@thick Foo.Type) -> @owned Foo
505+
store %4 to %1a : $*Foo
506+
%6 = alloc_box $<τ_0_0> { var τ_0_0 } <Baz>
507+
%6a = project_box %6 : $<τ_0_0> { var τ_0_0 } <Baz>, 0
508+
%7 = function_ref @baz_init : $@convention(thin) (@thin Baz.Type) -> @owned Baz
509+
%8 = metatype $@thin Baz.Type
510+
%9 = apply %7(%8) : $@convention(thin) (@thin Baz.Type) -> @owned Baz
511+
store %9 to %6a : $*Baz
512+
%11 = alloc_box $<τ_0_0> { var τ_0_0 } <Int>
513+
%11a = project_box %11 : $<τ_0_0> { var τ_0_0 } <Int>, 0
514+
%12 = function_ref @convert_from_integer_literal : $@convention(thin) (Builtin.Word, @thin Int.Type) -> Int
515+
%13 = metatype $@thin Int.Type
516+
%14 = integer_literal $Builtin.Word, 3
517+
%15 = apply %12(%14, %13) : $@convention(thin) (Builtin.Word, @thin Int.Type) -> Int
518+
store %15 to %11a : $*Int
519+
520+
%17 = dynamic_function_ref @closure0_guaranteed2 : $@convention(thin) (@guaranteed <τ_0_0> { var τ_0_0 } <Foo>, @guaranteed <τ_0_0> { var τ_0_0 } <Baz>, @guaranteed <τ_0_0> { var τ_0_0 } <Int>) -> Int
521+
%21 = partial_apply [callee_guaranteed] [on_stack] %17(%1, %6, %11) : $@convention(thin) (@guaranteed <τ_0_0> { var τ_0_0 } <Foo>, @guaranteed <τ_0_0> { var τ_0_0 } <Baz>, @guaranteed <τ_0_0> { var τ_0_0 } <Int>) -> Int
522+
%md = mark_dependence %21 :$@noescape @callee_guaranteed () -> Int on %1 : $<τ_0_0> { var τ_0_0 } <Foo>
523+
%md2 = mark_dependence %md :$@noescape @callee_guaranteed () -> Int on %6 : $<τ_0_0> { var τ_0_0 } <Baz>
524+
%md3 = mark_dependence %md2 :$@noescape @callee_guaranteed () -> Int on %11 : $<τ_0_0> { var τ_0_0 } <Int>
525+
apply %md3() : $@noescape @callee_guaranteed () -> Int
526+
dealloc_stack %21 : $@noescape @callee_guaranteed () -> Int
527+
strong_release %11 : $<τ_0_0> { var τ_0_0 } <Int>
528+
strong_release %6 : $<τ_0_0> { var τ_0_0 } <Baz>
529+
strong_release %1 : $<τ_0_0> { var τ_0_0 } <Foo>
530+
%tuple = tuple()
531+
return %tuple : $()
532+
}

0 commit comments

Comments
 (0)