Skip to content

Commit 12faf79

Browse files
committed
[Autodiff] Adds logic to rewrite call-sites using functions specialized by the closure-spec optimization
1 parent 31337dd commit 12faf79

File tree

12 files changed

+515
-80
lines changed

12 files changed

+515
-80
lines changed

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/ClosureSpecialization.swift

Lines changed: 220 additions & 37 deletions
Large diffs are not rendered by default.

SwiftCompilerSources/Sources/Optimizer/PassManager/Context.swift

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ extension Context {
6060
_bridged.lookupFunction($0).function
6161
}
6262
}
63+
64+
func notifyNewFunction(function: Function, derivedFrom: Function) {
65+
_bridged.addFunctionToPassManagerWorklist(function.bridged, derivedFrom.bridged)
66+
}
6367
}
6468

6569
/// A context which allows mutation of a function's SIL.
@@ -357,7 +361,7 @@ struct FunctionPassContext : MutatingContext {
357361
return String(taking: _bridged.mangleOutlinedVariable(function.bridged))
358362
}
359363

360-
func mangle(withClosureArgs closureArgs: [Value], closureArgIndices: [Int], from applySiteCallee: Function) -> String {
364+
func mangle(withClosureArguments closureArgs: [Value], closureArgIndices: [Int], from applySiteCallee: Function) -> String {
361365
closureArgs.withBridgedValues { bridgedClosureArgsRef in
362366
closureArgIndices.withBridgedArrayRef{bridgedClosureArgIndicesRef in
363367
String(taking: _bridged.mangleWithClosureArgs(
@@ -392,13 +396,13 @@ struct FunctionPassContext : MutatingContext {
392396
}
393397
}
394398

395-
func buildSpecializedFunction(specializedFunction: Function, buildFn: (Function, FunctionPassContext) -> ()) {
399+
func buildSpecializedFunction<T>(specializedFunction: Function, buildFn: (Function, FunctionPassContext) -> T) -> T {
396400
let nestedFunctionPassContext =
397401
FunctionPassContext(_bridged: _bridged.initializeNestedPassContext(specializedFunction.bridged))
398402

399403
defer { _bridged.deinitializedNestedPassContext() }
400404

401-
buildFn(specializedFunction, nestedFunctionPassContext)
405+
return buildFn(specializedFunction, nestedFunctionPassContext)
402406
}
403407
}
404408

SwiftCompilerSources/Sources/Optimizer/Utilities/Test.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,8 @@ public func registerOptimizerTests() {
166166
parseTestSpecificationTest,
167167
variableIntroducerTest,
168168
gatherCallSitesTest,
169-
specializedFunctionSignatureAndBodyTest
169+
specializedFunctionSignatureAndBodyTest,
170+
rewrittenCallerBodyTest
170171
)
171172

172173
// Finally register the thunk they all call through.

SwiftCompilerSources/Sources/SIL/Operand.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ public struct OperandArray : RandomAccessCollection, CustomReflectable {
6262
self.count = count
6363
}
6464

65+
static public var empty: OperandArray {
66+
OperandArray(base: OptionalBridgedOperand(bridged: nil), count: 0)
67+
}
68+
6569
public var startIndex: Int { return 0 }
6670
public var endIndex: Int { return count }
6771

include/swift/SIL/SILBridgingImpl.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#ifndef SWIFT_SIL_SILBRIDGING_IMPL_H
2020
#define SWIFT_SIL_SILBRIDGING_IMPL_H
2121

22+
#include "SILBridging.h"
2223
#include "swift/AST/Builtins.h"
2324
#include "swift/AST/Decl.h"
2425
#include "swift/AST/SubstitutionMap.h"

include/swift/SILOptimizer/OptimizerBridging.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,9 @@ struct BridgedPassContext {
337337
BRIDGED_INLINE bool continueWithNextSubpassRun(OptionalBridgedInstruction inst) const;
338338
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedPassContext initializeNestedPassContext(BridgedFunction newFunction) const;
339339
BRIDGED_INLINE void deinitializedNestedPassContext() const;
340+
BRIDGED_INLINE void
341+
addFunctionToPassManagerWorklist(BridgedFunction newFunction,
342+
BridgedFunction oldFunction) const;
340343

341344
// SSAUpdater
342345

include/swift/SILOptimizer/OptimizerBridgingImpl.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,13 @@ void BridgedPassContext::SSAUpdater_initialize(
454454
BridgedValue::castToOwnership(ownership));
455455
}
456456

457+
void BridgedPassContext::addFunctionToPassManagerWorklist(
458+
BridgedFunction newFunction, BridgedFunction oldFunction) const {
459+
swift::SILPassManager *pm = invocation->getPassManager();
460+
pm->addFunctionToWorklist(newFunction.getFunction(),
461+
oldFunction.getFunction());
462+
}
463+
457464
void BridgedPassContext::SSAUpdater_addAvailableValue(BridgedBasicBlock block, BridgedValue value) const {
458465
invocation->getSSAUpdater()->addAvailableValue(block.unbridged(),
459466
value.getSILValue());

lib/SILOptimizer/PassManager/PassPipeline.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1008,10 +1008,14 @@ SILPassPipelinePlan::getPerformancePassPipeline(const SILOptions &Options) {
10081008
if (Options.StopOptimizationAfterSerialization)
10091009
return P;
10101010

1011+
P.addAutodiffClosureSpecialization();
1012+
10111013
// After serialization run the function pass pipeline to iteratively lower
10121014
// high-level constructs like @_semantics calls.
10131015
addMidLevelFunctionPipeline(P);
10141016

1017+
P.addAutodiffClosureSpecialization();
1018+
10151019
// Perform optimizations that specialize.
10161020
addClosureSpecializePassPipeline(P);
10171021

lib/SILOptimizer/Transforms/PerformanceInliner.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,70 @@ bool SILPerformanceInliner::isTupleWithAllocsOrPartialApplies(SILValue val) {
387387
return false;
388388
}
389389

390+
// Uses a function's mangled name to determine if it is an Autodiff VJP
391+
// function.
392+
//
393+
// TODO: VJPs of differentiable functions with custom silgen names are not
394+
// recognized as VJPs by this function. However, this is not a hard limitation
395+
// and can be fixed.
396+
bool isFunctionAutodiffVJP(SILFunction *callee) {
397+
swift::Demangle::Context Ctx;
398+
if (auto *Root = Ctx.demangleSymbolAsNode(callee->getName())) {
399+
if (auto *node = Root->findByKind(
400+
swift::Demangle::Node::Kind::AutoDiffFunctionKind, 3)) {
401+
if (node->hasIndex()) {
402+
auto index = (char)node->getIndex();
403+
auto ADFunctionKind = swift::Demangle::AutoDiffFunctionKind(index);
404+
if (ADFunctionKind == swift::Demangle::AutoDiffFunctionKind::VJP) {
405+
return true;
406+
}
407+
}
408+
}
409+
}
410+
411+
return false;
412+
}
413+
414+
bool isProfitableToInlineAutodiffVJP(SILFunction *vjp, SILFunction *caller,
415+
InlineSelection whatToInline,
416+
StringRef stageName) {
417+
bool isLowLevelFunctionPassPipeline = stageName == "LowLevel,Function";
418+
auto isHighLevelFunctionPassPipeline =
419+
stageName == "HighLevel,Function+EarlyLoopOpt";
420+
auto calleeHasControlFlow = vjp->size() > 1;
421+
auto isCallerVJP = isFunctionAutodiffVJP(caller);
422+
auto callerHasControlFlow = caller->size() > 1;
423+
424+
// If the pass is being run as part of the low-level function pass pipeline,
425+
// the autodiff closure-spec optimization is done doing its work. Therefore,
426+
// all VJPs should be considered for inlining.
427+
if (isLowLevelFunctionPassPipeline) {
428+
return true;
429+
}
430+
431+
// If callee has control-flow it will definitely not be handled by the
432+
// Autodiff closure-spec optimization. Therefore, we should consider it for
433+
// inlining.
434+
if (calleeHasControlFlow) {
435+
return true;
436+
}
437+
438+
// If this is the EarlyPerfInline pass we want to have the Autodiff
439+
// closure-spec optimization pass optimize VJPs in isolation before they are
440+
// inlined into other VJPs.
441+
if (isHighLevelFunctionPassPipeline) {
442+
return false;
443+
}
444+
445+
// If this is not the EarlyPerfInline pass, VJPs should only be inlined into
446+
// other VJPs that do not contain any control-flow.
447+
if (!isCallerVJP || (isCallerVJP && callerHasControlFlow)) {
448+
return false;
449+
}
450+
451+
return true;
452+
}
453+
390454
bool SILPerformanceInliner::isProfitableToInline(
391455
FullApplySite AI, Weight CallerWeight, ConstantTracker &callerTracker,
392456
int &NumCallerBlocks,
@@ -395,6 +459,12 @@ bool SILPerformanceInliner::isProfitableToInline(
395459
assert(Callee);
396460
bool IsGeneric = AI.hasSubstitutions();
397461

462+
if (isFunctionAutodiffVJP(Callee) &&
463+
!isProfitableToInlineAutodiffVJP(Callee, AI.getFunction(), WhatToInline,
464+
this->pm->getStageName())) {
465+
return false;
466+
}
467+
398468
// Start with a base benefit.
399469
int BaseBenefit = isa<BeginApplyInst>(AI) ? RemovedCoroutineCallBenefit
400470
: RemovedCallBenefit;

test/AutoDiff/SILOptimizer/closure_specialization.sil

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,22 @@ bb0(%0 : $Float):
5151
// CHECK: strong_release %4 : $@callee_guaranteed (Float) -> (Float, Float) // id: %6
5252
// CHECK: return
5353

54+
//=========== Test rewritten body ===========//
55+
specify_test "closure_specialize_rewritten_caller_body"
56+
// CHECK-LABEL: Rewritten caller body for: $s4test1fyS2fFTJrSpSr
57+
// CHECK: sil hidden @$s4test1fyS2fFTJrSpSr : $@convention(thin) (Float) -> (Float, @owned @callee_guaranteed (Float) -> Float) {
58+
// CHECK: bb0(%0 : $Float):
59+
// CHECK: %2 = struct_extract %0 : $Float, #Float._value // users: %3, %3
60+
// CHECK: %3 = builtin "fmul_FPIEEE32"(%2 : $Builtin.FPIEEE32, %2 : $Builtin.FPIEEE32) : $Builtin.FPIEEE32 // user: %4
61+
// CHECK: %4 = struct $Float (%3 : $Builtin.FPIEEE32) // user: %11
62+
// CHECK: %5 = function_ref @$vjpMultiply : $@convention(thin) (Float, Float, Float) -> (Float, Float) // user: %6
63+
// CHECK: %6 = partial_apply [callee_guaranteed] %5(%0, %0) : $@convention(thin) (Float, Float, Float) -> (Float, Float) // user: %10
64+
// CHECK: %8 = function_ref @$s11$pullback_f12$vjpMultiplyS2fTf1nc_n : $@convention(thin) (Float, Float, Float) -> Float // user: %9
65+
// CHECK: %9 = partial_apply [callee_guaranteed] %8(%0, %0) : $@convention(thin) (Float, Float, Float) -> Float // user: %11
66+
// CHECK: release_value %6 : $@callee_guaranteed (Float) -> (Float, Float) // id: %10
67+
// CHECK: %11 = tuple (%4 : $Float, %9 : $@callee_guaranteed (Float) -> Float) // user: %12
68+
// CHECK: return %11
69+
5470
debug_value %0 : $Float, let, name "x", argno 1 // id: %1
5571
%2 = struct_extract %0 : $Float, #Float._value // users: %3, %3
5672
%3 = builtin "fmul_FPIEEE32"(%2 : $Builtin.FPIEEE32, %2 : $Builtin.FPIEEE32) : $Builtin.FPIEEE32 // user: %4
@@ -121,6 +137,32 @@ bb0(%0 : $Float):
121137
// CHECK: strong_release %6 : $@callee_guaranteed (Float) -> Float // id: %18
122138
// CHECK: return
123139

140+
//=========== Test rewritten body ===========//
141+
specify_test "closure_specialize_rewritten_caller_body"
142+
// CHECK-LABEL: Rewritten caller body for: $s4test1gyS2fFTJrSpSr
143+
// CHECK: sil hidden @$s4test1gyS2fFTJrSpSr : $@convention(thin) (Float) -> (Float, @owned @callee_guaranteed (Float) -> Float) {
144+
// CHECK: bb0(%0 : $Float):
145+
// CHECK: %2 = struct_extract %0 : $Float, #Float._value // users: %7, %3
146+
// CHECK: %3 = builtin "int_sin_FPIEEE32"(%2 : $Builtin.FPIEEE32) : $Builtin.FPIEEE32 // users: %11, %4
147+
// CHECK: %4 = struct $Float (%3 : $Builtin.FPIEEE32) // users: %17, %14
148+
// CHECK: %5 = function_ref @$vjpSin : $@convention(thin) (Float, Float) -> Float // user: %6
149+
// CHECK: %6 = partial_apply [callee_guaranteed] %5(%0) : $@convention(thin) (Float, Float) -> Float // user: %18
150+
// CHECK: %7 = builtin "int_cos_FPIEEE32"(%2 : $Builtin.FPIEEE32) : $Builtin.FPIEEE32 // users: %11, %8
151+
// CHECK: %8 = struct $Float (%7 : $Builtin.FPIEEE32) // users: %17, %14
152+
// CHECK: %9 = function_ref @$vjpCos : $@convention(thin) (Float, Float) -> Float // user: %10
153+
// CHECK: %10 = partial_apply [callee_guaranteed] %9(%0) : $@convention(thin) (Float, Float) -> Float // user: %19
154+
// CHECK: %11 = builtin "fmul_FPIEEE32"(%3 : $Builtin.FPIEEE32, %7 : $Builtin.FPIEEE32) : $Builtin.FPIEEE32 // user: %12
155+
// CHECK: %12 = struct $Float (%11 : $Builtin.FPIEEE32) // user: %21
156+
// CHECK: %13 = function_ref @$vjpMultiply : $@convention(thin) (Float, Float, Float) -> (Float, Float) // user: %14
157+
// CHECK: %14 = partial_apply [callee_guaranteed] %13(%8, %4) : $@convention(thin) (Float, Float, Float) -> (Float, Float) // user: %20
158+
// CHECK: %16 = function_ref @$s11$pullback_g7$vjpSinSf0B3CosSf0B8MultiplyS2fTf1nccc_n : $@convention(thin) (Float, Float, Float, Float, Float) -> Float // user: %17
159+
// CHECK: %17 = partial_apply [callee_guaranteed] %16(%0, %0, %8, %4) : $@convention(thin) (Float, Float, Float, Float, Float) -> Float // user: %21
160+
// CHECK: release_value %6 : $@callee_guaranteed (Float) -> Float // id: %18
161+
// CHECK: release_value %10 : $@callee_guaranteed (Float) -> Float // id: %19
162+
// CHECK: release_value %14 : $@callee_guaranteed (Float) -> (Float, Float) // id: %20
163+
// CHECK: %21 = tuple (%12 : $Float, %17 : $@callee_guaranteed (Float) -> Float) // user: %22
164+
// CHECK: return %21
165+
124166
debug_value %0 : $Float, let, name "x", argno 1 // id: %1
125167
%2 = struct_extract %0 : $Float, #Float._value // users: %7, %3
126168
%3 = builtin "int_sin_FPIEEE32"(%2 : $Builtin.FPIEEE32) : $Builtin.FPIEEE32 // users: %11, %4
@@ -197,7 +239,23 @@ bb0(%0 : $X):
197239
// CHECK: %5 = apply %4(%0) : $@callee_guaranteed (Float) -> X.TangentVector // user: %7
198240
// CHECK: strong_release %4 : $@callee_guaranteed (Float) -> X.TangentVector // id: %6
199241
// CHECK: return %5 : $X.TangentVector // id: %7
200-
242+
243+
//=========== Test rewritten body ===========//
244+
specify_test "closure_specialize_rewritten_caller_body"
245+
// CHECK-LABEL: Rewritten caller body for: $s5test21g1xSfAA1XV_tFTJrSpSr
246+
// CHECK: sil hidden @$s5test21g1xSfAA1XV_tFTJrSpSr : $@convention(thin) (X) -> (Float, @owned @callee_guaranteed (Float) -> X.TangentVector) {
247+
// CHECK: bb0(%0 : $X):
248+
// CHECK: %1 = struct_extract %0 : $X, #X.a // user: %10
249+
// CHECK: %2 = function_ref @pullback_f : $@convention(thin) (Float, Double) -> X.TangentVector // user: %3
250+
// CHECK: %3 = thin_to_thick_function %2 : $@convention(thin) (Float, Double) -> X.TangentVector to $@callee_guaranteed (Float, Double) -> X.TangentVector // users: %9, %5
251+
// CHECK: %4 = function_ref @subset_parameter_thunk : $@convention(thin) (Float, @guaranteed @callee_guaranteed (Float, Double) -> X.TangentVector) -> X.TangentVector // user: %5
252+
// CHECK: %5 = partial_apply [callee_guaranteed] %4(%3) : $@convention(thin) (Float, @guaranteed @callee_guaranteed (Float, Double) -> X.TangentVector) -> X.TangentVector
253+
// CHECK: %7 = function_ref @$s10pullback_g0A2_fTf1nc_n : $@convention(thin) (Float) -> X.TangentVector // user: %8
254+
// CHECK: %8 = partial_apply [callee_guaranteed] %7() : $@convention(thin) (Float) -> X.TangentVector // user: %10
255+
// CHECK: release_value %3 : $@callee_guaranteed (Float, Double) -> X.TangentVector // id: %9
256+
// CHECK: %10 = tuple (%1 : $Float, %8 : $@callee_guaranteed (Float) -> X.TangentVector) // user: %11
257+
// CHECK: return %10
258+
201259
%1 = struct_extract %0 : $X, #X.a // user: %8
202260
// function_ref pullback_f
203261
%2 = function_ref @pullback_f : $@convention(thin) (Float, Double) -> X.TangentVector // user: %3
@@ -263,6 +321,29 @@ bb0(%0 : $Float):
263321
// CHECK: strong_release %11 : $@callee_guaranteed (Float) -> Float // id: %13
264322
// CHECK: return %12 : $Float
265323

324+
//=========== Test rewritten body ===========//
325+
specify_test "closure_specialize_rewritten_caller_body"
326+
// CHECK-LABEL: Rewritten caller body for: $s5test21h1xS2f_tFTJrSpSr
327+
// CHECK:sil hidden @$s5test21h1xS2f_tFTJrSpSr : $@convention(thin) (Float) -> (Float, @owned @callee_guaranteed (Float) -> Float) {
328+
// CHECK:bb0(%0 : $Float):
329+
// CHECK: %1 = struct_extract %0 : $Float, #Float._value // users: %2, %2
330+
// CHECK: %2 = builtin "fmul_FPIEEE32"(%1 : $Builtin.FPIEEE32, %1 : $Builtin.FPIEEE32) : $Builtin.FPIEEE32 // user: %12
331+
// CHECK: %3 = function_ref @$sSf16_DifferentiationE12_vjpMultiply3lhs3rhsSf5value_Sf_SftSfc8pullbacktSf_SftFZSf_SftSfcfU_ : $@convention(thin) (Float, Float, Float) -> (Float, Float) // user: %4
332+
// CHECK: %4 = partial_apply [callee_guaranteed] %3(%0, %0) : $@convention(thin) (Float, Float, Float) -> (Float, Float) // users: %16, %6
333+
// CHECK: %5 = function_ref @$sS3fIegydd_S3fIegnrr_TR : $@convention(thin) (@in_guaranteed Float, @guaranteed @callee_guaranteed (Float) -> (Float, Float)) -> (@out Float, @out Float) // user: %6
334+
// CHECK: %6 = partial_apply [callee_guaranteed] %5(%4) : $@convention(thin) (@in_guaranteed Float, @guaranteed @callee_guaranteed (Float) -> (Float, Float)) -> (@out Float, @out Float) // user: %7
335+
// CHECK: %7 = convert_function %6 : $@callee_guaranteed (@in_guaranteed Float) -> (@out Float, @out Float) to $@callee_guaranteed @substituted <τ_0_0, τ_0_1, τ_0_2> (@in_guaranteed τ_0_0) -> (@out τ_0_1, @out τ_0_2) for <Float, Float, Float> // user: %9
336+
// CHECK: %8 = function_ref @pullback_f_specialized : $@convention(thin) (@in_guaranteed Float, @owned @callee_guaranteed @substituted <τ_0_0, τ_0_1, τ_0_2> (@in_guaranteed τ_0_0) -> (@out τ_0_1, @out τ_0_2) for <Float, Float, Float>) -> @out Float // user: %9
337+
// CHECK: %9 = partial_apply [callee_guaranteed] %8(%7) : $@convention(thin) (@in_guaranteed Float, @owned @callee_guaranteed @substituted <τ_0_0, τ_0_1, τ_0_2> (@in_guaranteed τ_0_0) -> (@out τ_0_1, @out τ_0_2) for <Float, Float, Float>) -> @out Float // user: %11
338+
// CHECK: %10 = function_ref @$sS2fIegnr_S2fIegyd_TR : $@convention(thin) (Float, @guaranteed @callee_guaranteed (@in_guaranteed Float) -> @out Float) -> Float // user: %11
339+
// CHECK: %11 = partial_apply [callee_guaranteed] %10(%9) : $@convention(thin) (Float, @guaranteed @callee_guaranteed (@in_guaranteed Float) -> @out Float) -> Float
340+
// CHECK: %12 = struct $Float (%2 : $Builtin.FPIEEE32) // user: %17
341+
// CHECK: %14 = function_ref @$s10pullback_h073$sSf16_DifferentiationE12_vjpMultiply3lhs3rhsSf5value_Sf_SftSfc8pullbackti1_j5FZSf_J6SfcfU_S2fTf1nc_n : $@convention(thin) (Float, Float, Float) -> Float // user: %15
342+
// CHECK: %15 = partial_apply [callee_guaranteed] %14(%0, %0) : $@convention(thin) (Float, Float, Float) -> Float // user: %17
343+
// CHECK: release_value %4 : $@callee_guaranteed (Float) -> (Float, Float) // id: %16
344+
// CHECK: %17 = tuple (%12 : $Float, %15 : $@callee_guaranteed (Float) -> Float) // user: %18
345+
// CHECK: return %17
346+
266347
%1 = struct_extract %0 : $Float, #Float._value // users: %2, %2
267348
%2 = builtin "fmul_FPIEEE32"(%1 : $Builtin.FPIEEE32, %1 : $Builtin.FPIEEE32) : $Builtin.FPIEEE32 // user: %12
268349

@@ -328,6 +409,21 @@ bb0(%0 : $Float):
328409
// CHECK: strong_release %4 : $@callee_guaranteed (Float) -> Float // id: %6
329410
// CHECK: return %5 : $Float
330411

412+
//=========== Test rewritten body ===========//
413+
specify_test "closure_specialize_rewritten_caller_body"
414+
// CHECK-LABEL: Rewritten caller body for: $s5test21z1xS2f_tFTJrSpSr
415+
// CHECK: sil hidden @$s5test21z1xS2f_tFTJrSpSr : $@convention(thin) (Float) -> (Float, @owned @callee_guaranteed (Float) -> Float) {
416+
// CHECK: bb0(%0 : $Float):
417+
// CHECK: %1 = function_ref @pullback_y_specialized : $@convention(thin) (@in_guaranteed Float) -> @out Float // user: %2
418+
// CHECK: %2 = thin_to_thick_function %1 : $@convention(thin) (@in_guaranteed Float) -> @out Float to $@callee_guaranteed (@in_guaranteed Float) -> @out Float // users: %8, %4
419+
// CHECK: %3 = function_ref @reabstraction_thunk : $@convention(thin) (Float, @guaranteed @callee_guaranteed (@in_guaranteed Float) -> @out Float) -> Float // user: %4
420+
// CHECK: %4 = partial_apply [callee_guaranteed] %3(%2) : $@convention(thin) (Float, @guaranteed @callee_guaranteed (@in_guaranteed Float) -> @out Float) -> Float
421+
// CHECK: %6 = function_ref @$s10pullback_z0A14_y_specializedTf1nc_n : $@convention(thin) (Float) -> Float // user: %7
422+
// CHECK: %7 = partial_apply [callee_guaranteed] %6() : $@convention(thin) (Float) -> Float // user: %9
423+
// CHECK: release_value %2 : $@callee_guaranteed (@in_guaranteed Float) -> @out Float // id: %8
424+
// CHECK: %9 = tuple (%0 : $Float, %7 : $@callee_guaranteed (Float) -> Float) // user: %10
425+
// CHECK: return %9
426+
331427
// function_ref pullback_y_specialized
332428
%1 = function_ref @pullback_y_specialized : $@convention(thin) (@in_guaranteed Float) -> @out Float // user: %2
333429
%2 = thin_to_thick_function %1 : $@convention(thin) (@in_guaranteed Float) -> @out Float to $@callee_guaranteed (@in_guaranteed Float) -> @out Float // user: %4

0 commit comments

Comments
 (0)