Hello,
I'm trying to get better understanding of expression
evaluation in Swift contexts. I have built the toolchain main
branches in debug mode using:
$ utils/build-script --debug --skip-early-swift-driver --skip-early-swiftsyntax --debug-lldb
The issue does not manifest, in release mode:
$ utils/build-script --release-debuginfo --skip-early-swift-driver --skip-early-swiftsyntax --lldb
Any Swift expression
that can not be resolved merely by an interpreter fails to compile and crashes the LLDB.
I would like to ask, whether someone else can also reproduce the issue and ask for advice.
Reproducing the issue
- Build the Toolchain in debug mode.
- Create a simple Swift program, for example
minimal.swift
:
let x = 0
print(x)
- Build it
$ build/Ninja-DebugAssert/swift-linux-x86_64/bin/swiftc -g minimal.swift
- Run LLDB:
$ build/Ninja-DebugAssert/lldb-linux-x86_64/bin/lldb minimal
- Set a breakpoint
(lldb) b minimal.swift:2
- Run the program
(lldb) r
- Execute an expression that can not be resolved only by an interpreter
(lldb) e -- x
The LLDB crashes.
Thread backtrace
thread backtrace
* thread #1, name = 'lldb', stop reason = signal SIGSEGV: invalid address (fault address: 0x0)
* frame #0: 0x00007fffe3037170 libswiftCore.so`swift::TargetMetadata<swift::InProcess>::getKind(this=0x0000000000000000) const at Metadata.h:270:38
frame #1: 0x00007fffe303f4c9 libswiftCore.so`swift::TargetMetadata<swift::InProcess>::getTypeContextDescriptor(this=0x0000000000000000) const at Metadata.h:428:13
frame #2: 0x00007fffe303f3f9 libswiftCore.so`swift::TargetMetadata<swift::InProcess>::getGenericArgs(this=0x0000000000000000) const at Metadata.h:462:24
frame #3: 0x00007fffe30b196e libswiftCore.so`instantiateWitnessTable(Type=0x0000000000000000, conformance=0x00007fffe31c3a48, instantiationArgs=0x0000000000000000, fullTable=0x00007fffe3479870) at Metadata.cpp:5690:29
frame #4: 0x00007fffe30ee35c libswiftCore.so`(anonymous namespace)::WitnessTableCacheEntry::allocate(this=0x00007fffe3479858, conformance=0x00007fffe31c3a48, instantiationArgs=0x0000000000000000) at Metadata.cpp:5791:10
frame #5: 0x00007fffe30eafbb libswiftCore.so`std::optional<swift::TargetWitnessTable<swift::InProcess>*> swift::SimpleLockingCacheEntryBase<(anonymous namespace)::WitnessTableCacheEntry, swift::TargetWitnessTable<swift::InProcess>*>::beginAllocation<swift::TargetProtocolConformanceDescriptor<swift::InProcess> const*&, void const* const*&>(this=0x00007fffe3479858, worker=0x00007fffffff8728, args=0x00007fffffff87c0, args=0x00007fffffff87b0) at MetadataCache.h:391:16
frame #6: 0x00007fffe30a00b9 libswiftCore.so`std::pair<(anonymous namespace)::WitnessTableCacheEntry*, swift::TargetWitnessTable<swift::InProcess>*> swift::LockingConcurrentMap<(anonymous namespace)::WitnessTableCacheEntry, swift::LockingConcurrentMapStorage<(anonymous namespace)::WitnessTableCacheEntry, (unsigned short)16>>::getOrInsert<swift::TargetMetadata<swift::InProcess> const*, swift::TargetProtocolConformanceDescriptor<swift::InProcess> const*&, void const* const*&>(this=0x00007fffe3464cd0, key=0x0000000000000000, args=0x00007fffffff87c0, args=0x00007fffffff87b0) at MetadataCache.h:276:30
frame #7: 0x00007fffe309fc64 libswiftCore.so`swift_getWitnessTable(conformance=0x00007fffe31c3a48, type=0x0000000000000000, instantiationArgs=0x0000000000000000) at Metadata.cpp:5873:23
frame #8: 0x00007fffe64bed74 liblldb.so.13git`lazy protocol witness table accessor for type LazySequence<ReverseBasicBlockList> and conformance LazySequence<A> at <compiler-generated>:0
frame #9: 0x00007fffe6488da1 liblldb.so.13git`Function.reversedInstructions.getter(self=0x0000555556169968) at Function.swift:56:28
frame #10: 0x00007fffe63e1139 liblldb.so.13git`runSimplification(function=0x0000555556169968, context=Optimizer.FunctionPassContext @ 0x00007fffffff8ac8, preserveDebugInfo=true, simplify=()) at SimplificationPasses.swift:88:50
frame #11: 0x00007fffe640e096 liblldb.so.13git`closure #1 in variable initialization expression of ononeSimplificationPass(function=0x0000555556169968, context=Optimizer.FunctionPassContext @ 0x00007fffffff8af0) at SimplificationPasses.swift:40:7
frame #12: 0x00007fffe63dc975 liblldb.so.13git`FunctionPass.run(bridgedCtxt=OptimizerBridging.BridgedFunctionPassCtxt @ 0x00007fffffff8b70, self=Optimizer.FunctionPass @ 0x00007fffffff8b50) at Passes.swift:29:5
frame #13: 0x00007fffe63deaae liblldb.so.13git`closure #15 in registerSwiftPasses($0=OptimizerBridging.BridgedFunctionPassCtxt @ 0x00007fffffff8bd0) at PassRegistration.swift:80:67
frame #14: 0x00007fffe63dead9 liblldb.so.13git`@objc closure #15 in registerSwiftPasses() at <compiler-generated>:0
frame #15: 0x00007fffe6d1f6a6 liblldb.so.13git`runBridgedFunctionPass(runFunction=0x00007ffff7d78858, passManager=0x00007fffffff9500, f=0x0000555556169958, passName=(Data = "onone-simplification", Length = 20)) at Passes.cpp:273:3
frame #16: 0x00007fffe6d2153d liblldb.so.13git`OnoneSimplificationPass::run(this=0x000055555615b710) at Passes.def:394:1
frame #17: 0x00007fffe6ce60bf liblldb.so.13git`swift::SILPassManager::runPassOnFunction(this=0x00007fffffff9500, TransIdx=34, F=0x0000555556169958) at PassManager.cpp:612:10
frame #18: 0x00007fffe6ce725a liblldb.so.13git`swift::SILPassManager::runFunctionPasses(this=0x00007fffffff9500, FromTransIdx=34, ToTransIdx=36) at PassManager.cpp:732:5
frame #19: 0x00007fffe6ceae0a liblldb.so.13git`swift::SILPassManager::execute(this=0x00007fffffff9500) at PassManager.cpp:890:5
frame #20: 0x00007fffe6ce4a34 liblldb.so.13git`swift::SILPassManager::executePassPipelinePlan(this=0x00007fffffff9500, Plan=0x00007fffffff9bf8) at PassManager.cpp:859:5
frame #21: 0x00007fffe6ce4870 liblldb.so.13git`swift::ExecuteSILPipelineRequest::evaluate(this=0x00007fffffff9b58, evaluator=0x0000555556652c40, desc=SILPipelineExecutionDescriptor @ 0x00007fffffff9960) const at PassManager.cpp:360:6
frame #22: 0x00007fffe6d34507 liblldb.so.13git`std::tuple<> swift::SimpleRequest<swift::ExecuteSILPipelineRequest, std::tuple<> (swift::SILPipelineExecutionDescriptor), (swift::RequestFlags)1>::callDerived<0ul>(this=0x00007fffffff9b58, evaluator=0x0000555556652c40, (null)=std::index_sequence<0UL> @ 0x00007fffffff99d8) const at SimpleRequest.h:267:24
frame #23: 0x00007fffe6d3442d liblldb.so.13git`swift::SimpleRequest<swift::ExecuteSILPipelineRequest, std::tuple<> (swift::SILPipelineExecutionDescriptor), (swift::RequestFlags)1>::evaluateRequest(request=0x00007fffffff9b58, evaluator=0x0000555556652c40) at SimpleRequest.h:290:20
frame #24: 0x00007fffe6d149a5 liblldb.so.13git`llvm::Expected<swift::ExecuteSILPipelineRequest::OutputType> swift::Evaluator::getResultUncached<swift::ExecuteSILPipelineRequest>(this=0x0000555556652c40, request=0x00007fffffff9b58) at Evaluator.h:368:21
frame #25: 0x00007fffe6cf7e28 liblldb.so.13git`llvm::Expected<swift::ExecuteSILPipelineRequest::OutputType> swift::Evaluator::operator()<swift::ExecuteSILPipelineRequest, (void*)0>(this=0x0000555556652c40, request=0x00007fffffff9b58) at Evaluator.h:273:12
frame #26: 0x00007fffe6ce4acb liblldb.so.13git`swift::executePassPipelinePlan(SM=0x0000555556159bb0, plan=0x00007fffffff9bf8, isMandatory=true, IRMod=0x0000000000000000) at PassManager.cpp:370:24
frame #27: 0x00007fffe6d19b54 liblldb.so.13git`swift::runSILDiagnosticPasses(Module=0x0000555556159bb0) at Passes.cpp:65:3
frame #28: 0x00007fffe5d7fd8c liblldb.so.13git`lldb_private::SwiftExpressionParser::Parse(this=0x0000555556085bc0, diagnostic_manager=0x00007fffffffbda0, first_line=4, last_line=6) at SwiftExpressionParser.cpp:1943:3
frame #29: 0x00007fffe5d75549 liblldb.so.13git`lldb_private::SwiftUserExpression::GetTextAndSetExpressionParser(this=0x00005555566b5450, diagnostic_manager=0x00007fffffffbda0, source_code=0x5555566ae3e0, exe_ctx=0x00007fffffffc2b0, exe_scope=0x00007fff6005d990) at SwiftUserExpression.cpp:642:21
frame #30: 0x00007fffe5d767c0 liblldb.so.13git`lldb_private::SwiftUserExpression::Parse(this=0x00005555566b5450, diagnostic_manager=0x00007fffffffbda0, exe_ctx=0x00007fffffffc2b0, execution_policy=eExecutionPolicyOnlyWhenNeeded, keep_result_in_memory=true, generate_debug_info=false) at SwiftUserExpression.cpp:747:9
frame #31: 0x00007fffe50653cc liblldb.so.13git`lldb_private::UserExpression::Evaluate(exe_ctx=0x00007fffffffc2b0, options=0x00007fffffffc6a0, expr=(Data = "x", Length = 1), prefix=(Data = "", Length = 0), result_valobj_sp=nullptr, error=0x00007fffffffc188, fixed_expression=<parent failed to evaluate: parent is NULL>, ctx_obj=0x0000000000000000) at UserExpression.cpp:285:27
frame #32: 0x00007fffe52ae6a7 liblldb.so.13git`lldb_private::Target::EvaluateExpression(this=0x0000555555905d70, expr=(Data = "x", Length = 1), exe_scope=0x00007fff6005d990, result_valobj_sp=nullptr, options=0x00007fffffffc6a0, fixed_expression=<parent failed to evaluate: parent is NULL>, ctx_obj=0x0000000000000000) at Target.cpp:3063:25
frame #33: 0x00007fffe5de6bb2 liblldb.so.13git`lldb_private::CommandObjectDWIMPrint::DoExecute(this=0x0000555556526190, command=(Data = "-O -- x", Length = 8), result=0x00007fffffffcdb0) at CommandObjectDWIMPrint.cpp:178:16
frame #34: 0x00007fffe5101d99 liblldb.so.13git`lldb_private::CommandObjectRaw::Execute(this=0x0000555556526190, args_string="-O -- x", result=0x00007fffffffcdb0) at CommandObject.cpp:773:17
frame #35: 0x00007fffe50dd4e1 liblldb.so.13git`lldb_private::CommandInterpreter::HandleCommand(this=0x000055555592a490, command_line="po x", lazy_add_to_history=eLazyBoolCalculate, result=0x00007fffffffcdb0, force_repeat_command=false) at CommandInterpreter.cpp:2063:14
frame #36: 0x00007fffe50e179d liblldb.so.13git`lldb_private::CommandInterpreter::IOHandlerInputComplete(this=0x000055555592a490, io_handler=0x00005555563a9410, line="po x") at CommandInterpreter.cpp:3143:3
frame #37: 0x00007fffe4f8491c liblldb.so.13git`lldb_private::IOHandlerEditline::Run(this=0x00005555563a9410) at IOHandler.cpp:580:22
frame #38: 0x00007fffe4f40061 liblldb.so.13git`lldb_private::Debugger::RunIOHandlers(this=0x0000555556432a10) at Debugger.cpp:1018:16
frame #39: 0x00007fffe50e2b2d liblldb.so.13git`lldb_private::CommandInterpreter::RunCommandInterpreter(this=0x000055555592a490, options=0x00007fffffffd168) at CommandInterpreter.cpp:3402:16
frame #40: 0x00007fffe4cc5b08 liblldb.so.13git`lldb::SBDebugger::RunCommandInterpreter(this=0x00007fffffffd5c0, auto_handle_events=true, spawn_thread=false) at SBDebugger.cpp:1249:42
frame #41: 0x000055555556b062 lldb`Driver::MainLoop(this=0x00007fffffffd5a0) at Driver.cpp:643:18
frame #42: 0x000055555556b90e lldb`main(argc=1, argv=0x00007fffffffdbc8) at Driver.cpp:823:26
frame #43: 0x00007fffe3623a90 libc.so.6`__libc_start_call_main(main=(lldb`main at Driver.cpp:754), argc=1, argv=0x00007fffffffdbc8) at libc_start_call_main.h:58:16
frame #44: 0x00007fffe3623b49 libc.so.6`__libc_start_main_impl(main=(lldb`main at Driver.cpp:754), argc=1, argv=0x00007fffffffdbc8, init=<unavailable>, fini=<unavailable>, rtld_fini=<unavailable>, stack_end=0x00007fffffffdbb8) at libc-start.c:360:3
frame #45: 0x00005555555680f5 lldb`_start + 37
My analysis of the issue so far
I have started by looking at the point of crash: swift::TargetMetadata<swift::InProcess>::getKind(this=0x0000000000000000)
where this
should obviously be non-0x0. My next move was to look where the 0x0
value originates.
The closest "human readable" piece of code is a line in swift/SwiftCompilerSources/Sources/SIL/Function.swift:56
(this will be relevant later):
public var reversedInstructions: LazySequence<FlattenSequence<LazyMapSequence<ReverseBasicBlockList, ReverseInstructionList>>> {
blocks.reversed().lazy.flatMap { $0.instructions.reversed() }
}
I have decided to summarize my finding into the diagram below, please look at the image for more context. The important assembly is in black.
I have tried to replicate this issue in a minimal example and it looks like there should be a non-0 value in demangling cache variable for type metadata for LazySequence<ReverseBasicBlockList>
.
The assemly looked the same (I have compared every instruction in the execution path). The only difference was, that there was a non-0 value.
Minimal example
// File: crash_dl.swift
// Build command: swiftc -emit-module -parse-as-library -g crash_dl.swift
protocol MySeq: Sequence {}
extension LazySequence: MySeq where Base: MySeq {}
public struct TheSeq: MySeq, IteratorProtocol {
public typealias Element = Int
public mutating func next() -> Int? { 0 }
public init() {}
public var smth: [Int] {
lazy.prefix(2).map { $0 + 1 }
}
}
// File: exec.swift
// Build command: swiftc -I. -L. -lcrash_dl -Xlinker -rpath=. -g exec.swift
import crash_dl
print(TheSeq().smth)
At this point, I don't know where to look. I think, that the locally built release
version works, because this whole "thing" may be optimized-out. I'm wondering whether this is caused by bootstrapping. I have tried to verify this by providing "hosttools" option, but the compilation failed.
Also, since I'm just getting started, I don't have a good idea about what even is a "demangling cache" and how it all works. I have watched https://www.youtube.com/watch?v=ctS8FzqcRug but it does not go into such a detail. I think the related code might be in IRGen...