New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[SR-10994] default arguments broken in LLDB REPL #4483
Comments
Looking. |
Can reproduce this on macOS too 1> func foo(_ a: String, b: String? = nil) { print(a) }
2> foo("asdf")
error: Couldn't lookup symbols:
default argument 1 of __lldb_expr_1.foo(_: Swift.String, b: Swift.Optional<Swift.String>) -> () |
Hmm, I think that it seems like an IRGen issue where we don’t generate the default value thunk and mark it for emission ({{@llvm.compiler.used}}). |
; ModuleID = 'lldb_module' The interesting bit to follow here is `main`, which does the follow: %8 = call swiftcc { i64, i64 } @"$s13_lldb_expr_13foo_1bySS_SSSgtFfA0"(), 33 The problem is that $s13_lldb_expr_13foo_1bySS_SSSgtFfA0 is never defined. This is a bug in the lowering of the expression. The default argument construct thunk should have been materialised during the emission of the callee and placed into the global Swift module (unless the default value thunk is marked as @__inline(always), where in it would need to be materialised at the caller side). This would then invoke that default value generator thunk that was previously emitted into the module. |
public func f(_ s: String, _ t: String? = nil) { }
What this shows is the interesting stuff. In particular what is interesting here is:
That is the default value thunk. (You can verify this with swift demangle: default argument 1 of __lldb_expr.f(Swift.String, Swift.String?) -> ()) SILGen is generating it, the question is why is this not happening in the SwiftExpressionParser plugin in lldb. |
So, thanks to some help from @dcci - we have the log from lldb on macOS. The interesting thing to look at here is the SILGen output. It appears that the SILGen phase will generate the declaration not the definition of the default value thunk, which is why the body is never materialised and thus the linking fails. |
CC: @slavapestov |
The thunk has PublicNonABI linkage so I would not expect it to show in the generated IR when its owner function is emitted, unless something references it. In the normal case, the default argument is serialized as part of the SIL for the module defining the function with the default argument. When you call the function we deserialize the default argument generator from the SIL module and emit it as part of the caller. I'm guessing we don't preserve SIL in this case in lldb. You can see if this is the case by trying the following:
If you enter the two lines as separate REPL inputs it will probably show the same problem. |
Indeed LLDB does not preserve SIL for the lines that it compiles and executes. There was some discussion about LLDB preserving SIL here: https://forums.swift.org/t/generating-serialized-sil-for-inlinable-transparent-functions-in-repl-expressions/16237?u=marcrasi |
FWIW, the other example works: % ./lldb --repl
Welcome to Swift version 5.1-dev (LLVM af1f73e9e9, Swift b2ce4a2ba1).
Type :help for assistance.
1> @_alwaysEmitIntoClient func f() {}
2> f() |
What if 'f' is public? So I thought about it some more and my explanation of what happens in the non-REPL case is not completely accurate. When building without whole module optimization, we can reference default arguments from other translation units, and we don't have serialized SIL (since each TU is built in parallel). So they should be emitted with hidden linkage. Does the REPL think it's doing "whole module" build? |
Making `f` public doesn't seem to change anything. % ./lldb --repl
Welcome to Swift version 5.1-dev (LLVM af1f73e9e9, Swift b2ce4a2ba1).
Type :help for assistance.
1> @_alwaysEmitIntoClient public func f() {}
2> f()
3> I don't think the REPL is doing whole module build, I would be surprised, but you never know. Is there a quick way to check? [e.g. the compiler invocation has some flag set?] |
The given example is uninteresting as there are no default value thunks involved. @slavapestov, actually, the SIL is preserved. I had a bit of an epiphany last night, drifting off to sleep: the problem is not in the SILGen or IRGen, but rather in the implementation of LLDB, and in particular, LLDB's expression JIT handling. Consider the following abbreviated log from lldb: > func f(_ s: String, _ t: String? = nil) { } This generates the following SIL: sil_stage raw import Builtin func f(_ s: String, _ t: String? = nil) // main // default argument 1 of f(*:*🙂 // f(*:*🙂 Note that the default argument thunk is emitted. It is then lowered in the following IR: define weak_odr hidden swiftcc { i64, i64 } @"$s13_lldb_expr_11fyySS_SSSgtFfA0"() #0 comdat 28 { define dllexport swiftcc void @"$s13__lldb_expr_11fyySS_SSSgtF"(i64, %swift.bridge*, i64, i64) #0 34 { Furthermore, it appears that it is even materialized and emitted into the module. And, now, the pièce de résistance > f("f") error: Couldn't lookup symbols: Lets take a closer look at the IRGen for the `main` expression: define dllexport i32 @main(i32, i8**) #0 21 { ; <label>:4: ; preds = %2 ; <label>:11: ; preds = %4 We are interested in the def of value 8: %8 = call swiftcc { i64, i64 } @"$s13_lldb_expr_11fyySS_SSSgtFfA0"(), 33 So, the call is to the proper function. Why then does this fail? We have materialised the value, the name of the symbols are the same, so the JIT should be able to resolve the symbol. And the answer to that lies burried in the mess of lldb's output: When we initially declared the function: Registering JITted Functions: Function: $s13__lldb_expr_11fyySS_SSSgtF at 0x1b0a7fa0030. 000002F1DEC778F0 ObjectFile::ObjectFile() module = 000002F1E8CD4F70 (), file = <NULL>, file_offset = 0x00000000, size = 0 And when we finally invoked the function: Registering JITted Functions: Registering JIIted Symbols: 000002F1DEC793F0 ObjectFile::ObjectFile() module = 000002F1E8CD5630 (), file = <NULL>, file_offset = 0x00000000, size = 0 There are two interesting things to realize here:
The key thing to realize here is that because the symbols are in a different module, the lookup will fail in MCJIT. This means that the call will bubble up to the dyld handler, which I believe is |
So, solving this is a bit more complicated. The information that we need is only in the AST. However, the AST does not have the declarations that are needed as they are materialized only during SILGen. Furthermore, the definitions which need to be preserved are only available after IRGen. |
> The given example is uninteresting as there are no default value thunks involved. A public @_alwaysEmitIntoClient function should behave identically to a default argument generator for a public function. |
Oh, interesting. Didn't know that impact of the Come to think of it, we could/should just emit the default value thunks into an ELF group ( |
Additional Detail from JIRA
md5: efd3e2805e709b6935327dc6f2ce1e32
relates to:
Issue Description:
On Ubuntu 18.04, swift development snapshot 2019-06-20:
This error does not occur on swift-5.0.1-RELEASE-ubuntu18.04.
I can fix it by reverting apple/swift@846a64b538145ba70bd76185a080876a34c93991, but I don't know enough about linkage to understand what a proper fix would be.
The text was updated successfully, but these errors were encountered: