swiftlang / swift

The Swift Programming Language
https://swift.org
Apache License 2.0
67.3k stars 10.34k forks source link

[SR-12762] [AutoDiff] runtime segfault when differentiating generic function with tangentvector constrained to array #55207

Open 83b09dda-53f5-4663-b44c-87fd2a9a8717 opened 4 years ago

83b09dda-53f5-4663-b44c-87fd2a9a8717 commented 4 years ago
Previous ID SR-12762
Radar None
Original Reporter @marcrasi
Type Bug
Additional Detail from JIRA | | | |------------------|-----------------| |Votes | 0 | |Component/s | | |Labels | Bug, AutoDiff | |Assignee | @marcrasi | |Priority | Medium | md5: ad25ac5743834fb73c83e843d1b30711

relates to:

Issue Description:

func f(_ v: [Double]) -> Double {
  return 0
}

let g = gradient(at: [0], in: f)
let g2 = g  // Succeeds!

func doGradient<A: Differentiable>(
  of f: @differentiable(A) -> Double,
  at p: A
) where A.TangentVector == Array<Double>.DifferentiableView {
  let g = gradient(at: p, in: f)
  let g2 = g  // Segfaults!
}

doGradient(of: f, at: [0])

I couldn't get a useful backtrace out of LLDB.

83b09dda-53f5-4663-b44c-87fd2a9a8717 commented 4 years ago

I think this is reabstraction troubles.

The call to `doGradient` passes the following differentiable_function:

%77 = differentiable_function [parameters 0] %65 : $@noescape @callee_guaranteed @substituted <τ_0_0> (@in_guaranteed τ_0_0) -> Double for <Array<Double>> with_derivative {
      %71 : $@noescape @callee_guaranteed @substituted <τ_0_0> (@in_guaranteed τ_0_0) -> (Double, @owned @callee_guaranteed (@guaranteed Array<Double>.DifferentiableView) -> Double) for <Array<Double>>,
      %76 : $@noescape @callee_guaranteed @substituted <τ_0_0> (@in_guaranteed τ_0_0) -> (Double, @owned @callee_guaranteed (Double) -> @owned Array<Double>.DifferentiableView) for <Array<Double>>
}

However, the `doGradient` SIL expects the passed-in function to have types:

original: $@convention(thin) <τ_0_0 where τ_0_0 : Differentiable, τ_0_0.TangentVector == Array<Double>.DifferentiableView> (@in_guaranteed τ_0_0) -> Double
jvp: $@convention(thin) <τ_0_0 where τ_0_0 : Differentiable, τ_0_0.TangentVector == Array<Double>.DifferentiableView> (@in_guaranteed τ_0_0) -> (Double, @owned @callee_guaranteed (@in_guaranteed Array<Double>.DifferentiableView) -> Double)
vjp: <τ_0_0 where τ_0_0 : Differentiable, τ_0_0.TangentVector == Array<Double>.DifferentiableView> (@in_guaranteed τ_0_0) -> (Double, @owned @callee_guaranteed (Double) -> @out Array<Double>.DifferentiableView)

Notice that the differential parameter is `@guaranteed` in the caller and `@in_guaranteed` in the callee! Similarly, the pullback result is `@owned` in the caller and `@out` in the callee.

Now I will try to figure out why the caller and callee disagree on the abstraction pattern.

83b09dda-53f5-4663-b44c-87fd2a9a8717 commented 4 years ago

I believe this is another instance of the non-commutativity between type lowering `getAutoDiffDerivativeFunctionType`. (See https://github.com/apple/swift/blob/c721cf12f5c8f107396bb08cdbd6ba009194ca37/include/swift/SIL/TypeSubstCloner.h#L324 for a comment describing and existing instance.)

SILGen for `main` determines the correct abstraction pattern for the JVP/VJP by calling `getAutoDiffAssociatedFunctionType` on the AST abstraction pattern of the `doGradient` parameter. So SILGen creates JVP/VJPs of type `lower(getAutoDiffAssociatedFunctionType(\<AST type of doGradient parameter>))`.

SILGen for `doGradient` determines the JVP/VJP type of the parameter by calling `getAutoDiffAssociatedFunctionType` on the SILFunctionType of the parameter. So SILGen for `doGradient` thinks that the JVP/VJP type is `getAutoDiffAssociatedFunctionType(lower(\<AST type of doGradient parameter>))`.

These operations do not commute when an original function parameter is address-only and its tangentvector is not address-only.

I can't think of any way to fix it so maybe we should add some diagnostic for now.

A workaround is to define concrete overloads of `doGradient` for all the `A` types that you want to use it with. This isn't such a bad workaround.

jkshtj commented 4 months ago

Cause a compiler crash on 05/24 toolchain.

Assertion failed: ((!F || opTI->isABICompatibleWith(resTI, *F).isCompatible()) && "Can not convert in between ABI incompatible function types"), function create, file SILInstructions.cpp, line 2761.
Please submit a bug report (https://swift.org/contributing/#reporting-bugs) and include the crash backtrace.
Stack dump:
0.  Program arguments: /Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2024-05-01-a.xctoolchain/usr/bin/swift-frontend -frontend -c -primary-file /Users/kshitij/workspace/scratch/test.swift -target arm64-apple-macosx13.0 -Xllvm -aarch64-use-tbi -enable-objc-interop -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.2.sdk -color-diagnostics -O -empty-abi-descriptor -Xcc -working-directory -Xcc /Users/kshitij/workspace/scratch -resource-dir /Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2024-05-01-a.xctoolchain/usr/lib/swift -module-name test -target-sdk-version 14.2 -target-sdk-name macosx14.2 -external-plugin-path /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/usr/lib/swift/host/plugins#/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/usr/bin/swift-plugin-server -external-plugin-path /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/usr/local/lib/swift/host/plugins#/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/usr/bin/swift-plugin-server -plugin-path /Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2024-05-01-a.xctoolchain/usr/lib/swift/host/plugins -plugin-path /Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-2024-05-01-a.xctoolchain/usr/local/lib/swift/host/plugins -enable-default-cmo -o /var/folders/5h/9l25ljkd47qct1zm4g4_dxz00000gq/T/TemporaryDirectory.xrQFEa/test-1.o
1.  Apple Swift version 6.0-dev (LLVM b66077aefd3be08, Swift 84d36181a762913)
2.  Compiling with effective version 5.10
3.  While evaluating request ExecuteSILPipelineRequest(Run pipelines { PrepareOptimizationPasses, EarlyModulePasses, HighLevel,Function+EarlyLoopOpt, HighLevel,Module+StackPromote, MidLevel,Function, ClosureSpecialize, LowLevel,Function, LateLoopOpt, SIL Debug Info Generator } on SIL for test)
4.  While running pass #1911 SILFunctionTransform "SILCombine" on SILFunction "@main".
Stack dump without symbol names (ensure you have llvm-symbolizer in your PATH or set the environment var `LLVM_SYMBOLIZER_PATH` to point to it):
0  swift-frontend           0x000000010a04d600 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) + 56
1  swift-frontend           0x000000010a04bdcc llvm::sys::RunSignalHandlers() + 112
2  swift-frontend           0x000000010a04dc48 SignalHandler(int) + 304
3  libsystem_platform.dylib 0x00000001a3286a24 _sigtramp + 56
4  libsystem_pthread.dylib  0x00000001a3257c28 pthread_kill + 288
5  libsystem_c.dylib        0x00000001a3165ae8 abort + 180
6  libsystem_c.dylib        0x00000001a3164e44 err + 0
7  swift-frontend           0x000000010a38e534 swift::ConvertFunctionInst::create(swift::SILDebugLocation, swift::SILValue, swift::SILType, swift::SILModule&, swift::SILFunction*, bool, swift::ValueOwnershipKind) (.cold.4) + 0
8  swift-frontend           0x0000000105a4aa8c swift::ConvertFunctionInst::create(swift::SILDebugLocation, swift::SILValue, swift::SILType, swift::SILModule&, swift::SILFunction*, bool, swift::ValueOwnershipKind) + 452
9  swift-frontend           0x0000000105617d34 swift::SILCombiner::visitConvertFunctionInst(swift::ConvertFunctionInst*)::$_3::operator()(swift::NormalDifferentiableFunctionTypeComponent) const + 320
10 swift-frontend           0x0000000105617aa4 swift::SILCombiner::visitConvertFunctionInst(swift::ConvertFunctionInst*) + 2760
11 swift-frontend           0x0000000105602e0c swift::SILCombiner::doOneIteration(swift::SILFunction&, unsigned int) + 1348
12 swift-frontend           0x0000000105603958 swift::SILCombiner::runOnFunction(swift::SILFunction&) + 260
13 swift-frontend           0x0000000105605da4 (anonymous namespace)::SILCombine::run() + 160
14 swift-frontend           0x00000001055c5830 swift::SILPassManager::runPassOnFunction(unsigned int, swift::SILFunction*) + 1432
15 swift-frontend           0x00000001055c669c swift::SILPassManager::runFunctionPasses(unsigned int, unsigned int) + 1060
16 swift-frontend           0x00000001055c36f8 swift::SILPassManager::executePassPipelinePlan(swift::SILPassPipelinePlan const&) + 72
17 swift-frontend           0x00000001055c3678 swift::ExecuteSILPipelineRequest::evaluate(swift::Evaluator&, swift::SILPipelineExecutionDescriptor) const + 68
18 swift-frontend           0x00000001055ff07c swift::SimpleRequest<swift::ExecuteSILPipelineRequest, std::__1::tuple<> (swift::SILPipelineExecutionDescriptor), (swift::RequestFlags)1>::evaluateRequest(swift::ExecuteSILPipelineRequest const&, swift::Evaluator&) + 28
19 swift-frontend           0x00000001055df2e4 swift::ExecuteSILPipelineRequest::OutputType swift::Evaluator::getResultUncached<swift::ExecuteSILPipelineRequest, swift::ExecuteSILPipelineRequest::OutputType swift::evaluateOrFatal<swift::ExecuteSILPipelineRequest>(swift::Evaluator&, swift::ExecuteSILPipelineRequest)::'lambda'()>(swift::ExecuteSILPipelineRequest const&, swift::ExecuteSILPipelineRequest::OutputType swift::evaluateOrFatal<swift::ExecuteSILPipelineRequest>(swift::Evaluator&, swift::ExecuteSILPipelineRequest)::'lambda'()) + 204
20 swift-frontend           0x00000001055c38d4 swift::executePassPipelinePlan(swift::SILModule*, swift::SILPassPipelinePlan const&, bool, swift::irgen::IRGenModule*) + 64
21 swift-frontend           0x00000001055e1a20 swift::runSILOptimizationPasses(swift::SILModule&) + 156
22 swift-frontend           0x0000000104e241d4 swift::CompilerInstance::performSILProcessing(swift::SILModule*) + 624
23 swift-frontend           0x0000000104be8b44 performCompileStepsPostSILGen(swift::CompilerInstance&, std::__1::unique_ptr<swift::SILModule, std::__1::default_delete<swift::SILModule>>, llvm::PointerUnion<swift::ModuleDecl*, swift::SourceFile*>, swift::PrimarySpecificPaths const&, int&, swift::FrontendObserver*) + 796
24 swift-frontend           0x0000000104be8258 swift::performCompileStepsPostSema(swift::CompilerInstance&, int&, swift::FrontendObserver*) + 612
25 swift-frontend           0x0000000104bf94cc withSemanticAnalysis(swift::CompilerInstance&, swift::FrontendObserver*, llvm::function_ref<bool (swift::CompilerInstance&)>, bool) + 160
26 swift-frontend           0x0000000104bea868 performCompile(swift::CompilerInstance&, int&, swift::FrontendObserver*) + 708
27 swift-frontend           0x0000000104be97d4 swift::performFrontend(llvm::ArrayRef<char const*>, char const*, void*, swift::FrontendObserver*) + 2368
28 swift-frontend           0x0000000104a049cc swift::mainEntry(int, char const**) + 3096
29 dyld                     0x00000001a2efff28 start + 2236