I recommend looking at this example:
$ cat TestOptional.swift
func createOptionalNil<T>(_ t: T.Type) -> T? {
return nil
}
func testNil() {
let _ = createOptionalNil(Int.self)
}
testNil()
Looking at the IR generated with swift-macosx-x86_64/bin/swiftc -module-name Foo -emit-ir TestOptional.swift | less
, specifically the following function:
define hidden swiftcc void @"$S3Foo17createOptionalNilyxSgxmlF"(%TSq* noalias nocapture sret, %swift.type*, %swift.type* %T) #0 {
entry:
%2 = bitcast %TSq* %0 to %swift.opaque*
%3 = bitcast %swift.type* %T to i8***
%4 = getelementptr inbounds i8**, i8*** %3, i64 -1
%T.valueWitnesses = load i8**, i8*** %4, align 8, !invariant.load !11, !dereferenceable !12
%5 = getelementptr inbounds i8*, i8** %T.valueWitnesses, i32 8
// Here we load the storeEnumTag witness from T's witness table. In my example this is Int's witness table.
%6 = load i8*, i8** %5, align 8, !invariant.load !11
%storeEnumTagSinglePayload = bitcast i8* %6 to void (%swift.opaque*, i32, i32, %swift.type*)*
// Store the tag 1 (tag zero would be the .some/ payload case)
call void %storeEnumTagSinglePayload(%swift.opaque* noalias %2, i32 1, i32 1, %swift.type* %T) #3
ret void
}
Okay let's compile this so that we can step through the debugger swift-macosx-x86_64/bin/swift TestOptional.swift
.
(lldb) breakpoint set --func-regex createOptional
Breakpoint 1: where = TestOptional`TestOptional.createOptionalNil<A>(A.Type) -> Swift.Optional<A>, address = 0x0000000100000f40
(lldb) run
Process 74323 launched: './TestOptional' (x86_64)
Process 74323 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x0000000100000f40 TestOptional`TestOptional.createOptionalNil<A>(A.Type) -> Swift.Optional<A>
TestOptional`TestOptional.createOptionalNil<A>(A.Type) -> Swift.Optional<A>:
-> 0x100000f40 <+0>: pushq %rbp
0x100000f41 <+1>: movq %rsp, %rbp
0x100000f44 <+4>: subq $0x10, %rsp
0x100000f48 <+8>: movq -0x8(%rsi), %rcx
Target 0: (TestOptional) stopped.
(lldb) dis
TestOptional`TestOptional.createOptionalNil<A>(A.Type) -> Swift.Optional<A>:
-> 0x100000f40 <+0>: pushq %rbp
0x100000f41 <+1>: movq %rsp, %rbp
0x100000f44 <+4>: subq $0x10, %rsp
0x100000f48 <+8>: movq -0x8(%rsi), %rcx
0x100000f4c <+12>: movq 0x40(%rcx), %rcx
0x100000f50 <+16>: movl $0x1, %edx
0x100000f55 <+21>: movq %rax, %rdi
0x100000f58 <+24>: movq %rsi, -0x8(%rbp)
0x100000f5c <+28>: movl %edx, %esi
0x100000f5e <+30>: movq -0x8(%rbp), %rax
0x100000f62 <+34>: movq %rcx, -0x10(%rbp)
0x100000f66 <+38>: movq %rax, %rcx
0x100000f69 <+41>: movq -0x10(%rbp), %r8
0x100000f6d <+45>: callq *%r8
0x100000f70 <+48>: addq $0x10, %rsp
0x100000f74 <+52>: popq %rbp
0x100000f75 <+53>: retq
0x100000f76 <+54>: nopw %cs:(%rax,%rax)
Stepping a few times until we are at the witness call:
(lldb)
Process 74323 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step over
frame #0: 0x0000000100000f6d TestOptional`TestOptional.createOptionalNil<A>(A.Type) -> Swift.Optional<A> + 45
TestOptional`TestOptional.createOptionalNil<A>(A.Type) -> Swift.Optional<A>:
-> 0x100000f6d <+45>: callq *%r8
0x100000f70 <+48>: addq $0x10, %rsp
0x100000f74 <+52>: popq %rbp
0x100000f75 <+53>: retq
Stepping into this call ends up in witness (Int's) which happens to be defined in the runtime (i.e cpp files):
(lldb) si
libswiftCore.dylib was compiled with optimization - stepping may behave oddly; variables may not be available.
Process 74334 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step into
frame #0: 0x00000001004865d0 libswiftCore.dylib`swift::metadataimpl::FixedSizeBufferValueWitnesses<swift::metadataimpl::ValueWitnesses<swift::metadataimpl::NativeBox<unsigned long long, 8ul, 8ul, 8ul> >, 8ul, 8ul, false>::storeEnumTagSinglePayload(enumAddr=0x00007ffeefbff900, whichCase=1, numEmptyCases=1, self=0x000000010054a2e0) at MetadataImpl.h:789 [opt]
786 static void storeEnumTagSinglePayload(OpaqueValue *enumAddr,
787 unsigned whichCase,
788 unsigned numEmptyCases,
-> 789 const Metadata *self) {
790 return storeEnumTagSinglePayloadImpl(enumAddr, whichCase, numEmptyCases,
791 self, Size, 0, nullptr);
792 }
Okay, so we have seen a case were the value witness is defined in the runtime (i.e cpp files). How about compiler generated ones (the ones generated by the code in GenType.cpp)? Lets slightly modify our example!
func createOptionalNil<T>(_ t: T.Type) -> T? {
return nil
}
struct MyType {
var x = 1
var y = 2
}
func testNil() {
let _ = createOptionalNil(MyType.self)
}
testNil()
Compile swift-macosx-x86_64/bin/swiftc -module-name Foo TestOptional.swift
and run it in the debugger.
$ lldb ./Foo
(lldb) target create "./Foo"
Current executable set to './Foo' (x86_64).
(lldb) breakpoint set --func-regex createOptional
Breakpoint 1: where = Foo`Foo.createOptionalNil<A>(A.Type) -> Swift.Optional<A>, address = 0x0000000100000ba0
(lldb) run
Process 74527 launched: './Foo' (x86_64)
Process 74527 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x0000000100000ba0 Foo`Foo.createOptionalNil<A>(A.Type) -> Swift.Optional<A>
Foo`Foo.createOptionalNil<A>(A.Type) -> Swift.Optional<A>:
-> 0x100000ba0 <+0>: pushq %rbp
0x100000ba1 <+1>: movq %rsp, %rbp
0x100000ba4 <+4>: subq $0x10, %rsp
0x100000ba8 <+8>: movq -0x8(%rsi), %rcx
Target 0: (Foo) stopped.
(lldb) si
...
(lldb) si
Process 74527 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step into
frame #0: 0x0000000100000bcd Foo`Foo.createOptionalNil<A>(A.Type) -> Swift.Optional<A> + 45
Foo`Foo.createOptionalNil<A>(A.Type) -> Swift.Optional<A>:
-> 0x100000bcd <+45>: callq *%r8
0x100000bd0 <+48>: addq $0x10, %rsp
0x100000bd4 <+52>: popq %rbp
0x100000bd5 <+53>: retq
Target 0: (Foo) stopped.
(lldb) si
Process 74527 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step into
frame #0: 0x0000000100000db0 Foo`storeEnumTagSinglePayload value witness for Foo.MyType
Foo`storeEnumTagSinglePayload value witness for Foo.MyType:
-> 0x100000db0 <+0>: pushq %rbp
0x100000db1 <+1>: movq %rsp, %rbp
0x100000db4 <+4>: subq $0x40, %rsp
0x100000db8 <+8>: xorl %eax, %eax
This time we ended up in Foo`storeEnumTagSinglePayload value witness for Foo.MyType
-- a compiler generated witness. Let's look at the IR : swift-macosx-x86_64/bin/swiftc -module-name Foo TestOptional.swift -emit-ir | swift-macosx-x86_64/bin/swift-demangle | less
define linkonce_odr hidden void @"storeEnumTagSinglePayload value witness for Foo.MyType"(%swift.opaque* noalias %value, i32 %whichCase, i32 %numEmptyCases, %swift.type* %MyType) #5 {
entry:
%0 = alloca i32, align 4
%1 = alloca i32, align 4
%2 = bitcast %swift.opaque* %value to %T3Foo6MyTypeV*
%3 = bitcast %T3Foo6MyTypeV* %2 to i8*
%4 = getelementptr inbounds i8, i8* %3, i32 16
%5 = icmp ugt i32 %numEmptyCases, 0
br i1 %5, label %6, label %8
; <label>:6: ; preds = %entry
%7 = sub i32 %numEmptyCases, 0
br label %8
...
This code was generated by the compiler specifically by GenType.cpp's storeEnumTagSinglePayload.