"For" loop iterator and "enum"

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.

2 Likes

@Arnold

Could you please take for _ in ARRAY for an example?
The swift code is:

     var strings = [10,11]
     for  _ in strings { }

When next() gets a nil,
on x86_64 is nil: 00 00 00 00 • 00 00 00 00 • 01,
but on s390x is nil: 00 00 00 00 • 00 00 00 00 • 00
We compare the ir files of x_86_64 and s390x, no difference except some notations of x86_64 and s390x. Would like to know what part compiler code that sets the the 9th byte 01 on x86_64. Is it from IRGen code or from the runtime code.
As far as this example, the debugging does not show the runtime code except Exclusive.cpp ( swift::swift_beginAccess and swift::swift_endAccess)
which seem not to do the job.

By the way, on v4.1 we found that on s390x

  1. the loop works if ARRAY element is an optional (e.g var strings:[Int?] = [10,11];
  2. the loop works if ARRAY element is a struct;
  3. the above example works in v4.0.3 on s390x;

Thank you for your help.

Sam

Which value is returned in my example in testNil() on s390x? If you fix the smaller example then returning nil from the array will also start working. I find debugging and looking at the IR of smaller examples is usually easier.

Here is how I would debug this on x86_64. Let's look at the generated IR first:

$ swift-macosx-x86_64/bin/swiftc -module-name Foo TestOptional.swift -emit-ir | swift-macosx-x86_64/bin/swift-demangle | less
...
define hidden swiftcc void @"Foo.testNil() -> ()"() #0 {
entry:
  %0 = alloca %TSiSg, align 8
  %1 = bitcast %TSiSg* %0 to i8*
  call void @llvm.lifetime.start.p0i8(i64 9, i8* %1)
  %2 = bitcast %TSiSg* %0 to %TSq*

  // This call will return the Optional<A> indirectly in the first argument.
  call swiftcc void @"Foo.createOptionalNil<A>(A.Type) -> A?"(%TSq* noalias nocapture sret %2, %swift.type* @"type metadata for Swift.Int", %swift.type* @"type metadata for Swift.Int")

  %3 = bitcast %TSiSg* %0 to i64*
  %4 = load i64, i64* %3, align 8
  %5 = getelementptr inbounds %TSiSg, %TSiSg* %0, i32 0, i32 1
  %6 = bitcast [1 x i8]* %5 to i1*
  %7 = load i1, i1* %6, align 8
  %8 = bitcast %TSiSg* %0 to i8*
  call void @llvm.lifetime.end.p0i8(i64 9, i8* %8)
  ret void
}

Alright, lets set things up in the debugger. I am going to set two breakpoints: the first in testNil — I want to observe what the nil value is that is returned, the second I am going to set in all functions that have storeEnumTagSinglePayload in their name.

$ swift-macosx-x86_64/bin/swiftc -module-name Foo TestOptional.swift
$ lldb ./Foo
(lldb) target create "./Foo"
Current executable set to './Foo' (x86_64).
(lldb) breakpoint set --func testNil
Breakpoint 1: where = Foo`Foo.testNil() -> (), address = 0x0000000100000f80
(lldb) breakpoint set --func-regex storeEnumTagSinglePayload
Breakpoint 2: 273 locations.

Okay, next lets run until we are in testNil. And look for the address we are returning the Optional<A> in.

(lldb) run
Process 42737 launched: './Foo' (x86_64)
Process 42737 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x0000000100000f80 Foo`Foo.testNil() -> ()
Foo`Foo.testNil() -> ():
->  0x100000f80 <+0>: pushq  %rbp
    0x100000f81 <+1>: movq   %rsp, %rbp
    0x100000f84 <+4>: subq   $0x20, %rsp
    0x100000f88 <+8>: movq   0x71(%rip), %rax          ; (void *)0x000000010054b2e0: type metadata for Swift.Int
Target 0: (Foo) stopped.
(lldb) ni
…
Process 42737 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step over
    frame #0: 0x0000000100000f8f Foo`Foo.testNil() -> () + 15
Foo`Foo.testNil() -> ():
->  0x100000f8f <+15>: leaq   -0x10(%rbp), %rcx // This is our address.
    0x100000f93 <+19>: movq   %rax, -0x18(%rbp)
    0x100000f97 <+23>: movq   %rcx, %rax
    0x100000f9a <+26>: movq   -0x18(%rbp), %rdi
Target 0: (Foo) stopped.
(lldb) ni
Process 42737 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step over
    frame #0: 0x0000000100000f93 Foo`Foo.testNil() -> () + 19
Foo`Foo.testNil() -> ():
->  0x100000f93 <+19>: movq   %rax, -0x18(%rbp)
    0x100000f97 <+23>: movq   %rcx, %rax
    0x100000f9a <+26>: movq   -0x18(%rbp), %rdi
    0x100000f9e <+30>: movq   -0x18(%rbp), %rsi
Target 0: (Foo) stopped.
(lldb) register read $rcx
     rcx = 0x00007ffeefbff910

The address in my run happens to be: rcx = 0x00007ffeefbff910

Okay, let's run until the call to createOptional and step over it. Notice, I have set a breakpoint in all storeEnumTagSinglePayload witnesses so it is going to break inside of a witness (same as outlined in my previous post). This is (one of) the function you need to look at.

(lldb) 
Process 42737 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step over
    frame #0: 0x0000000100000fa2 Foo`Foo.testNil() -> () + 34
Foo`Foo.testNil() -> ():
->  0x100000fa2 <+34>: callq  0x100000f40               ; Foo.createOptionalNil<A>(A.Type) -> Swift.Optional<A>
    0x100000fa7 <+39>: addq   $0x20, %rsp
    0x100000fab <+43>: popq   %rbp
    0x100000fac <+44>: retq   
Target 0: (Foo) stopped.
(lldb) ni
libswiftCore.dylib was compiled with optimization - stepping may behave oddly; variables may not be available.
Process 42737 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.7
    frame #0: 0x00000001004875d6 libswiftCore.dylib`swift::metadataimpl::FixedSizeBufferValueWitnesses<swift::metadataimpl::ValueWitnesses<swift::metadataimpl::NativeBox<unsigned long long, 8ul, 8ul, 8ul> >, 8ul, 8ul, false>::storeEnumTagSinglePayload(swift::OpaqueValue*, unsigned int, unsigned int, swift::TargetMetadata<swift::InProcess> const*) [inlined] swift::storeEnumTagSinglePayloadImpl(value=0x00007ffeefbff910, whichCase=1, emptyCases=1, payload=0x000000010054b2e0, paylo
adSize=8, payloadNumExtraInhabitants=0)(swift::OpaqueValue*, int, swift::TargetMetadata<swift::InProcess> const*)) at EnumImpl.h:160 [opt]
   157 
   158    // For payload or extra inhabitant cases, zero-initialize the extra tag bits,
   159    // if any.
-> 160    if (whichCase <= payloadNumExtraInhabitants) {
   161      if (numExtraTagBytes != 0)
   162        small_memset(extraTagBitAddr, 0, numExtraTagBytes);
   163 
Target 0: (Foo) stopped.

We ended up in the runtime's swift::metadataimpl::FixedSizeBufferValueWitnesses<swift::metadataimpl::ValueWitnesses<swift::metadataimpl::NativeBox<unsigned long long, 8ul, 8ul, 8ul> >, 8ul, 8ul, false>::storeEnumTagSinglePayload.

Let's step back out of this function again. To see which value was ultimately returned. (I strongly suspect it will be nine zero bytes on your platform).

(lldb) fini
Process 42737 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = step out
    frame #0: 0x0000000100000f70 Foo`Foo.createOptionalNil<A>(A.Type) -> Swift.Optional<A> + 48
Foo`Foo.createOptionalNil<A>(A.Type) -> Swift.Optional<A>:
->  0x100000f70 <+48>: addq   $0x10, %rsp
    0x100000f74 <+52>: popq   %rbp
    0x100000f75 <+53>: retq   
    0x100000f76 <+54>: nopw   %cs:(%rax,%rax)
Target 0: (Foo) stopped.

Looking at the address we previously identified:

(lldb) memory read --count 9 --type uint8_t 0x00007ffeefbff910
(uint8_t) 0x7ffeefbff910 = '\0'
(uint8_t) 0x7ffeefbff911 = '\0'
(uint8_t) 0x7ffeefbff912 = '\0'
(uint8_t) 0x7ffeefbff913 = '\0'
(uint8_t) 0x7ffeefbff914 = '\0'
(uint8_t) 0x7ffeefbff915 = '\0'
(uint8_t) 0x7ffeefbff916 = '\0'
(uint8_t) 0x7ffeefbff917 = '\0'
(uint8_t) 0x7ffeefbff918 = '\x01'

So in this example -- of Optional<Int> -- we hit the runtime function swift::metadataimpl::FixedSizeBufferValueWitnesses<swift::metadataimpl::ValueWitnesses<swift::metadataimpl::NativeBox<unsigned long long, 8ul, 8ul, 8ul> >, 8ul, 8ul, false>::storeEnumTagSinglePayload(swift::OpaqueValue*, unsigned int, unsigned int, swift::TargetMetadata<swift::InProcess> const*).

If I had debugged my example with MyType instead of Int the I would have ended up in the compiler generated witness:

Process 42750 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
    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

To assure you that the array example hits the same code path, here is the array example in the debugger. We are also in the same witness in the runtime as in my example:

$ swift-macosx-x86_64/bin/swiftc TestArray.swift -module-name A 
$ lldb ./A 
(lldb) target create "./A"
Current executable set to './A' (x86_64).
(lldb) breakpoint set --func-regex storeEnumTagSinglePayload
Breakpoint 1: 274 locations.
(lldb) run
Process 42718 launched: './A' (x86_64)
libswiftCore.dylib was compiled with optimization - stepping may behave oddly; variables may not be available.
Process 42718 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.7
    frame #0: 0x00000001004875d6 libswiftCore.dylib`swift::metadataimpl::FixedSizeBufferValueWitnesses<swift::metadataimpl::ValueWitnesses<swift::metadataimpl::NativeBox<unsigned long long, 8ul, 8ul, 8ul> >, 8ul, 8ul, false>::storeEnumTagSinglePayload(swift::OpaqueValue*, unsigned int, unsigned int, swift::TargetMetadata<swift::InProcess> const*) [inlined] swift::storeEnumTagSinglePayloadImpl(value=0x00007ffeefbff908, whichCase=0, emptyCases=1, payload=0x000000010054b2e0, payload
Size=8, payloadNumExtraInhabitants=0)(swift::OpaqueValue*, int, swift::TargetMetadata<swift::InProcess> const*)) at EnumImpl.h:160 [opt]
   157 
   158    // For payload or extra inhabitant cases, zero-initialize the extra tag bits,
   159    // if any.
-> 160    if (whichCase <= payloadNumExtraInhabitants) {
   161      if (numExtraTagBytes != 0)
   162        small_memset(extraTagBitAddr, 0, numExtraTagBytes);
   163 
Target 0: (A) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.7
  * frame #0: 0x00000001004875d6 libswiftCore.dylib`swift::metadataimpl::FixedSizeBufferValueWitnesses<swift::metadataimpl::ValueWitnesses<swift::metadataimpl::NativeBox<unsigned long long, 8ul, 8ul, 8ul> >, 8ul, 8ul, false>::storeEnumTagSinglePayload(swift::OpaqueValue*, unsigned int, unsigned int, swift::TargetMetadata<swift::InProcess> const*) [inlined] swift::storeEnumTagSinglePayloadImpl(value=0x00007ffeefbff908, whichCase=0, emptyCases=1, payload=0x000000010054b2e0, payload
Size=8, payloadNumExtraInhabitants=0)(swift::OpaqueValue*, int, swift::TargetMetadata<swift::InProcess> const*)) at EnumImpl.h:160 [opt]
    frame #1: 0x00000001004875d6 libswiftCore.dylib`swift::metadataimpl::FixedSizeBufferValueWitnesses<swift::metadataimpl::ValueWitnesses<swift::metadataimpl::NativeBox<unsigned long long, 8ul, 8ul, 8ul> >, 8ul, 8ul, false>::storeEnumTagSinglePayload(enumAddr=0x00007ffeefbff908, whichCase=0, numEmptyCases=1, self=0x000000010054b2e0) at MetadataImpl.h:790 [opt]
    frame #2: 0x00000001000a7132 libswiftCore.dylib`IndexingIterator.next(self=<unavailable>) at Collection.swift:0 [opt]
    frame #3: 0x0000000100000d05 A`main + 357
    frame #4: 0x00007fff6eef9015 libdyld.dylib`start + 1

The functions in the runtime that need to be adjusted for big endian are (note those are always inline functions that are used by other functions in the runtime, e.g. swift::metadataimpl::FixedSizeBufferValueWitnesses<swift::metadataimpl::ValueWitnesses<swift::metadataimpl::NativeBox<unsigned long long, 8ul, 8ul, 8ul> >, 8ul, 8ul, false>::storeEnumTagSinglePayload):

And for compiler generated witnesses:

Hope this helps.

If you want to debug the getEnumTageSinglePayload witnesses then the following code would be a good test case:

func createOptionalNil<T>(_ t: T.Type) -> T? {
  return nil
}

public func isNone<T>(_ t: T?) -> Bool {
  if t == nil {
    return true
  } else {
    return false
  }
}

struct MyType {
  var x = 1
  var y = 2
}

func testNil() {
  let opt = createOptionalNil(MyType.self)
  isNone(opt)
}

testNil()

The IR for isNone<T> is going to call the getEnumTagSinglePayload witness on T to determine whether we have .Some or .None.

define swiftcc i1 @"Foo.isNone<A>(A?) -> Swift.Bool"(%TSq* noalias nocapture, %swift.type* %T) #0 {
entry:
  %1 = call swiftcc %swift.metadata_response @"type metadata accessor for Swift.Optional"(i64 0, %swift.type* %T) #8
  %2 = extractvalue %swift.metadata_response %1, 0
  %3 = bitcast %swift.type* %2 to i8***
  %4 = getelementptr inbounds i8**, i8*** %3, i64 -1
  %.valueWitnesses = load i8**, i8*** %4, align 8, !invariant.load !11, !dereferenceable !12
  %5 = getelementptr inbounds i8*, i8** %.valueWitnesses, i32 9
  %6 = load i8*, i8** %5, align 8, !invariant.load !11
  %size = ptrtoint i8* %6 to i64
  %7 = alloca i8, i64 %size, align 16
  call void @llvm.lifetime.start.p0i8(i64 -1, i8* %7)
  %8 = bitcast i8* %7 to %TSq*
  %9 = call swiftcc %swift.metadata_response @"type metadata accessor for Swift.Optional"(i64 0, %swift.type* %T) #8
  %10 = extractvalue %swift.metadata_response %9, 0
  %11 = call %TSq* @"outlined init with copy of A?"(%TSq* %0, %TSq* %8, %swift.type* %T, %swift.type* %10)
  %12 = bitcast %TSq* %8 to %swift.opaque*

  // Get the getEnumTagSinglePayload witness of T.
  %13 = bitcast %swift.type* %T to i8***
  %14 = getelementptr inbounds i8**, i8*** %13, i64 -1
  %T.valueWitnesses = load i8**, i8*** %14, align 8, !invariant.load !11, !dereferenceable !12
  %15 = getelementptr inbounds i8*, i8** %T.valueWitnesses, i32 7
  %16 = load i8*, i8** %15, align 8, !invariant.load !11
  %getEnumTagSinglePayload = bitcast i8* %16 to i32 (%swift.opaque*, i32, %swift.type*)*
   // Cal the witness.
  %17 = call i32 %getEnumTagSinglePayload(%swift.opaque* noalias %12, i32 1, %swift.type* %T) #9
  // the witness returns the enum tag index: 0 is the payload, 1 is the first non payload case in case of the optional this is .None
  %18 = icmp ne i32 %17, 1 
  br i1 %18, label %20, label %26
                                                  ; No predecessors!
  unreachable

; <label>:20:                                     ; preds = %entry
  %21 = bitcast %TSq* %8 to %swift.opaque*
  %22 = getelementptr inbounds i8*, i8** %T.valueWitnesses, i32 1
  %23 = load i8*, i8** %22, align 8, !invariant.load !11
  %destroy = bitcast i8* %23 to void (%swift.opaque*, %swift.type*)*
  call void %destroy(%swift.opaque* noalias %21, %swift.type* %T) #4
  br label %24

; <label>:24:                                     ; preds = %20, %26
  %25 = phi i1 [ true, %26 ], [ false, %20 ]
  br label %27

Running this in the debugger:

$ swift-macosx-x86_64/bin/swiftc -module-name Foo TestOptional.swift 
$ lldb ./Foo 
(lldb) target create "./Foo"
Current executable set to './Foo' (x86_64).
(lldb) breakpoint set --func-regex isNone
Breakpoint 1: where = Foo`Foo.isNone<A>(Swift.Optional<A>) -> Swift.Bool, address = 0x0000000100000a00
(lldb) breakpoint set --func-regex getEnumTagSinglePayload
Breakpoint 2: 273 locations.
(lldb) run
Process 42962 launched: './Foo' (x86_64)
Process 42962 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x0000000100000a00 Foo`Foo.isNone<A>(Swift.Optional<A>) -> Swift.Bool
Foo`Foo.isNone<A>(Swift.Optional<A>) -> Swift.Bool:
->  0x100000a00 <+0>: pushq  %rbp
    0x100000a01 <+1>: movq   %rsp, %rbp
    0x100000a04 <+4>: subq   $0x50, %rsp
    0x100000a08 <+8>: xorl   %eax, %eax
Target 0: (Foo) stopped.
(lldb) continue
Process 42962 resuming
Process 42962 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
    frame #0: 0x0000000100000cf0 Foo`getEnumTagSinglePayload value witness for Foo.MyType
Foo`getEnumTagSinglePayload value witness for Foo.MyType:
->  0x100000cf0 <+0>: pushq  %rbp
    0x100000cf1 <+1>: movq   %rsp, %rbp
    0x100000cf4 <+4>: xorl   %eax, %eax
    0x100000cf6 <+6>: cmpl   %esi, %eax
Target 0: (Foo) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
  * frame #0: 0x0000000100000cf0 Foo`getEnumTagSinglePayload value witness for Foo.MyType
    frame #1: 0x0000000100000c52 Foo`$SxSglWOc + 66
    frame #2: 0x0000000100000a6d Foo`Foo.isNone<A>(Swift.Optional<A>) -> Swift.Bool + 109
    frame #3: 0x0000000100000bfa Foo`Foo.testNil() -> () + 74
    frame #4: 0x00000001000009b4 Foo`main + 20
    frame #5: 0x00007fff6eef9015 libdyld.dylib`start + 1
    frame #6: 0x00007fff6eef9015 libdyld.dylib`start + 1

I have used MyType.self in this example. So I ended up in the compiler generated witness -- the code that ultimate generates the witness is GenType.cpp's FixedTypeInfo::getEnumTagSinglePayload.

Thank @Arnold for the detailed instructions.

  1. I have tried your examples (3 ) and instructions on both x86_64 and s390x. The problem for me is that the lldb does not show swift/cpp code when it stop at storeEnumTagSinglePayload, here is the stack look like:
* thread #1, name = 'Foo', stop reason = breakpoint 2.1
  * frame #0: 0x000002aa00001344 Foo`storeEnumTagSinglePayload value witness for Foo.MyType
    frame #1: 0x000002aa00000c66 Foo`Foo.createOptionalNil<A>(A.Type) -> Swift.Optional<A> + 90
    frame #2: 0x000002aa0000105c Foo`Foo.testNil() -> () + 48
    frame #3: 0x000002aa00000bfe Foo`main + 26
    frame #4: 0x000003fffd2a2ece libc.so.6`__libc_start_main + 270

It shows only assemble code on both x86_64 and s390x. Do we miss something in building or configuration that prevent showing us the runtime source code?

  1. As for your 3rd example code, we found that opt with following values:
    On x:
(lldb) memory read --size 1 --format x --count 17 0x00007fffffffe430
0x7fffffffe430: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7fffffffe438: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7fffffffe440: 0x01
(lldb) fr v -L opt
0x00007fffffffe468: (aa3.MyType?) opt = nil

and on z (s390x):

0x3fffffff268: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x3fffffff270: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x3fffffff278: 0x00
fr v opt -L
0x000003fffffff3c8: (aa3.MyType?) opt = (x = 0, y = 0)

It is clear that the tag (17th byte) is not set for s390x

Thanks,

Sam

Depending on whether you use let _ = createOptionalNil(MyType.self) or let _ = createOptionalNil(Int.self) in my example, you will end up in a compiler generated witness table (former) or the runtime (latter).

It is expected that you don't see a runtime function with the former. In this case (compiler generated witness table, let _ = createOptionalNil(MyType.self)) the witness table was generated by the compiler by the code in GenType.cpp's storeEnumTagSinglePayload. You can look at the IR with the command swift-macosx-x86_64/bin/swiftc -module-name Foo TestOptional.swift -emit-ir | swift-macosx-x86_64/bin/swift-demangle | less.

To get single payload enum's working both the runtime implementation and the compiler generated implementations need to work for big endian.

You can test either code path by using either MyType.self or Int.self in my example.

(Debugging the runtime will work better if you build a debug standard library -- which also builds a debug runtime ; if you use utils/build-script the flag to add would be utils/build-script --debug-swift-stdlib ...)

Thanks @Arnold

In fact, we use following options to build swift:

  ./utils/build-script -j 2  -r \                      
        --lldb --foundation --xctest --llbuild  --libdispatch -- \
        --verbose-build=1   \
        --install-swift --install-foundation --install-xctest --install-llbuild --install-swiftpm  --install-libdispatch --install-lldb \
        --swift-install-components='autolink-driver;compiler;clang-builtin-headers;stdlib;sdk-overlay;license' \
        --build-swift-static-stdlib=1 \
        --install-prefix=/usr \
        --install-destdir=$MYDESTDIR

After adding --debug-swift-stdlib, it keeps the same (not showing runtime code) for your 3 examples.
We also tested a build with -d that contains the meaning of --debug-swift-stdlib and got the same result. Does --build-swift-static-stdlib=1 conflict with --debug-swift-stdlib?

By the way, we inserted printf in the two functions of EnumImpl.h, but running your examples directly does not show that print out. Does this means in our environment, the runtime code is not called? We are using v4.1 branch.

Thanks,

I am using the master branch. If you are using swift-4.1-branch to reproduce you will end up in the compiler generated witness in either case.

If you don't hit the runtime it means you hit a witness defined by the compiler. The code in this case is generated by GenType.cpp's FixedTypeInfo::storeEnumTagSinglePayload.

@Arnold
One question.
I inserted printf into GenType.cpp’s FixedTypeInfo::storeEnumTagSinglePayload and
GenType.cpp’s FixedTypeInfo::getEnumTagSinglePayload, when compiling your 3 examples, the 2nd and 3rd have print out messages that shows the 2 functions are called once, respectively. However, the 1st example does not show any print out message, which means the 2 functions are not called during compiling, how/what the tag is generated in the 1st example?

We are using swift-4.1 branch.

Thanks,

Hi Sam,

I assume the example that does not print anything is the one involving let _ = createOptionalNil(Int.self)?

The value witness gets generated in the module that defines the type. In the case of Int this is the standard library. So when we compile the standard library the witness table for Int including the witness for storeEnumTagSinglePayload value witness for Int gets generated.

The code that starts emission of witness tables is here:

The file that predefines some known witness tables in the runtime is here:

If you compare emitValueWitnessTable from swift-4.1-branch with master:

and

You will notice the additional call to getAddrOfKnownValueWitnessTable on master:

getAddrOfKnownValueWitnessTable tries to find an existing value witness that we know is defined in the runtime and return that instead of creating a new witness table. This call is why on master for Int we end up in the runtime rather than in a compiler generated witness.

Thanks @Arnold,
To make clear, here are your 3 examples:
1st : has let _ = createOptionalNil(Int.self);
2nd: has let _ = createOptionalNil(MyType.self);
3rd: has func isNone<T>

I set breakpoint at lib/IRGen/GenValueWitness.cpp:1071 that is llvm::Constant *irgen::emitValueWitnessTable, the 2nd and 3rd examples stop at this breakpoint, the BT look like:

(gdb) bt
#0  swift::irgen::emitValueWitnessTable (IGM=..., abstractType=...)
    at /home/work/sw/swift4/swift/lib/IRGen/GenValueWitness.cpp:1071
#1  0x000000000065086c in (anonymous namespace)::StructMetadataBuilder::addValueWitnessTable (this=0x7fffffff7b40)
    at /home/work/sw/swift4/swift/lib/IRGen/GenMeta.cpp:4482
#2  0x0000000000650491 in swift::irgen::NominalMetadataVisitor<(anonymous namespace)::StructMetadataBuilder>::layout (
    this=0x7fffffff7b40) at /home/work/sw/swift4/swift/lib/IRGen/NominalMetadataVisitor.h:46
#3  0x0000000000636e85 in swift::irgen::StructMetadataVisitor<(anonymous namespace)::StructMetadataBuilder>::layout (
    this=0x7fffffff7b40) at /home/work/sw/swift4/swift/lib/IRGen/StructMetadataVisitor.h:46
#4  0x0000000000636808 in swift::irgen::emitStructMetadata (IGM=..., structDecl=0xa23ca30)
    at /home/work/sw/swift4/swift/lib/IRGen/GenMeta.cpp:4570
#5  0x00000000006ad8dc in swift::irgen::IRGenModule::emitStructDecl (this=0x7fffffff81e0, st=0xa23ca30)
    at /home/work/sw/swift4/swift/lib/IRGen/GenStruct.cpp:845
#6  0x000000000058fce6 in swift::irgen::IRGenModule::emitGlobalDecl (this=0x7fffffff81e0, D=0xa23ca30)
    at /home/work/sw/swift4/swift/lib/IRGen/GenDecl.cpp:1678
#7  0x000000000058f9d1 in swift::irgen::IRGenModule::emitSourceFile (this=0x7fffffff81e0, SF=..., StartElem=0)
    at /home/work/sw/swift4/swift/lib/IRGen/GenDecl.cpp:470
#8  0x00000000006eb517 in performIRGeneration (Opts=..., M=0xa1be510,
    SILMod=std::unique_ptr<swift::SILModule> containing 0xa21df60, ModuleName=..., LLVMContext=..., SF=0x0,
    outModuleHash=0x0, StartElem=0) at /home/work/sw/swift4/swift/lib/IRGen/IRGen.cpp:743
#9  0x00000000006ea12f in swift::performIRGeneration (Opts=..., M=0xa1be510,
    SILMod=std::unique_ptr<swift::SILModule> containing 0x0, ModuleName=..., LLVMContext=..., outModuleHash=0x0)
    at /home/work/sw/swift4/swift/lib/IRGen/IRGen.cpp:1077
#10 0x0000000000579d19 in swift::RunImmediately (CI=..., CmdLine=std::vector of length 1, capacity 1 = {...},
    IRGenOpts=..., SILOpts=...) at /home/work/sw/swift4/swift/lib/Immediate/Immediate.cpp:304
#11 0x00000000005406a0 in performCompile (Instance=..., Invocation=..., Args=..., ReturnValue=@0x7fffffffb9ac: 0,
    observer=0x0, Stats=0x0) at /home/work/sw/swift4/swift/lib/FrontendTool/FrontendTool.cpp:1022
#12 0x000000000053d543 in swift::performFrontend (Args=..., Argv0=0x7fffffffe748 "/home/work/sw/usr/bin/swift",
    MainAddr=0x4a8d40 <getExecutablePath[abi:cxx11](char const*)>, observer=0x0)
    at /home/work/sw/swift4/swift/lib/FrontendTool/FrontendTool.cpp:1424
#13 0x00000000004a9b40 in main (argc_=9, argv_=0x7fffffffe4d8)
    at /home/work/sw/swift4/swift/tools/driver/driver.cpp:160

Then it calls FixedTypeInfo::storeEnumTagSinglePayloa.
But the 1st example does not stop at ::emitValueWitnessTable breakpoint.

Thanks,

Sam

Yes that is expected.

Int is defined in the standard library. The value witness table definition will be emitted when you compile the standard library (when you build the swift compiler).

The value witness table is like a v-table, containing generic 'value' operations like copy, destroy, size, alignment of the type -- and also storeEnumTagSinglePayload/getEnumTagSinglePayload. It will be generated along the type metadata of type. This is generally done once per type definition in the module that defines the type.

MyType is defined in TestOptional.swift. So you can observe the call to emitValueWitnessTable when compiling TestOptional.swift. Int, on the other hand, is defined in the standard library: emitValueWitnessTable will be called when we compile the standard library (libswiftCore.dylib) as part of building the compiler.

So how does this all fit together? Type metadata has a reference to the value witness table. In C this would look something like that:

struct Metadata {
  struct ValueWitnessTable {
    void (*copy)(void *to, void *from, Metadata *m);
    void (*destroy) (void *object, Metadata *m);
    void (*storeEnumTagSinglePayload (void *enumAddr, size_t tagNumber, size_t numEmptyCases, Metadata *m);
    ...
    unsigned size;
  } *vwt;

  ...
}

When a type is defined the compiler will emit an instance of the metadata structure for the type in the module that defines the type.

When you have a generic function we pass a reference to the type metadata as an argument. From the type metadata the generic implementation can get to the value witness.

When you compile testNil:

func testNil() {
  let _ = createOptionalNil(Int.self)
}

The compiler will emit the following code:

define hidden swiftcc void @TestOptional.testNil() -> ()() #0 {
entry:
  %0 = alloca %TSiSg, align 8
  %1 = bitcast %TSiSg* %0 to i8*
  call void @llvm.lifetime.start.p0i8(i64 9, i8* %1)
  %2 = bitcast %TSiSg* %0 to %TSq*
  call swiftcc void @TestOptional.createOptionalNil<A>(A.Type) -> A?(%TSq* noalias nocapture sret %2, %swift.type* @type metadata for Swift.Int, %swift.type* @type metadata for Swift.Int)
  %3 = bitcast %TSiSg* %0 to i64*
  %4 = load i64, i64* %3, align 8
  %5 = getelementptr inbounds %TSiSg, %TSiSg* %0, i32 0, i32 1
  %6 = bitcast [1 x i8]* %5 to i1*
  %7 = load i1, i1* %6, align 8
  %8 = bitcast %TSiSg* %0 to i8*
  call void @llvm.lifetime.end.p0i8(i64 9, i8* %8)
  ret void
}

Note the line:

  call swiftcc void @TestOptional.createOptionalNil<A>(A.Type) -> A?(%TSq* noalias nocapture sret %2, %swift.type* @type metadata for Swift.Int, %swift.type* @type metadata for Swift.Int)

This code passes a reference to the type metadata for Swift.Int to the generic implementation. The definition of that reference lives in the standard library and gets emitted when you compile the standard library as part of building swift.

If you compile the let _ = createOptionalNil(Int.self) example there will only be a reference to the type metadata of Int which is assume to be defined in the standard library.

@type metadata for Swift.Int = external global %swift.type, align 8

For completeness, lets quickly look at the implementation of createOptionalNil:

define hidden swiftcc void @TestOptional.createOptionalNil<A>(A.Type) -> A?(%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
  %6 = load i8*, i8** %5, align 8, !invariant.load !11
  %storeEnumTagSinglePayload = bitcast i8* %6 to void (%swift.opaque*, i32, i32, %swift.type*)*
  call void %storeEnumTagSinglePayload(%swift.opaque* noalias %2, i32 0, i32 1, %swift.type* %T) #2
  ret void
}

Note, how from the parameter T -- the type metadata passed by the caller -- the code loads the value witness table. It is slightly different than the C definition I showed above because the value witness reference is stored at a negative offset of the type metadata -- but that does not really add anything interesting. Once we have loaded the value witness table reference, we load an entry storeEnumTagSinglePayload at a certain offset and call it:

call void %storeEnumTagSinglePayload(%swift.opaque* noalias %2, i32 0, i32 1, %swift.type* %T) #2

Unfortunately, you are looking at swift-4.1-branch which further complicates explaining this call. It uses resilient tag indices (this changed in master). A resilient tag index works as follows: it is a signed integer, negative values are indices for tags that indicate a payload case, postive indices indicates tags for non-payload case. Single payload enums (like Optional) have only one payload case. It would use the tag index -1. The only payload case is 0.

So if you would return Some(T), the above call should look like:

call void %storeEnumTagSinglePayload(%swift.opaque* noalias %2, i32 -1, i32 1, %swift.type* %T) #2
func createSome<T>(_ t: T) -> T? {
  return t
}
define hidden swiftcc void @TestOptional.createSome<A>(A) -> A?(%TSq* noalias nocapture sret, %swift.opaque* noalias nocapture, %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 2
  %6 = load i8*, i8** %5, align 8, !invariant.load !11
  %initializeWithCopy = bitcast i8* %6 to %swift.opaque* (%swift.opaque*, %swift.opaque*, %swift.type*)*
  %7 = call %swift.opaque* %initializeWithCopy(%swift.opaque* noalias %2, %swift.opaque* noalias %1, %swift.type* %T) #2
  %8 = bitcast %TSq* %0 to %swift.opaque*
  %9 = getelementptr inbounds i8*, i8** %T.valueWitnesses, i32 8
  %10 = load i8*, i8** %9, align 8, !invariant.load !11
  %storeEnumTagSinglePayload = bitcast i8* %10 to void (%swift.opaque*, i32, i32, %swift.type*)*
  call void %storeEnumTagSinglePayload(%swift.opaque* noalias %8, i32 -1, i32 1, %swift.type* %T) #2
  %11 = getelementptr inbounds i8*, i8** %T.valueWitnesses, i32 1
  %12 = load i8*, i8** %11, align 8, !invariant.load !11
  %destroy = bitcast i8* %12 to void (%swift.opaque*, %swift.type*)*
  call void %destroy(%swift.opaque* noalias %1, %swift.type* %T) #2
  ret void
}

If you compile the example with the MyType definition you will see the definition of metadata and value witness table for MyType in the resulting IR.

$ cat TestOptional.swift 
struct MyType {
  var x = 1
  var y = 2
}

func createOptionalNil<T>(_ t: T.Type) -> T? {
  return nil
}

func testNil() {
  let _ = createOptionalNil(MyType.self)
}

testNil()
$ /Library/Developer/Toolchains/swift-4.1-DEVELOPMENT-SNAPSHOT-2018-03-28-a.xctoolchain/usr/bin/swiftc TestOptional.swift -emit-ir | /Library/Developer/Toolchains/swift-4.1-DEVELOPMENT-SNAPSHOT-2018-03-28-a.xctoolchain/usr/bin/swift-demangle | less

...
// The value witness table definition:
@value witness table for TestOptional.MyType = internal constant [12 x i8*] [i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy16_8 to i8*), i8* bitcast (void (i8*, %swift.type*)* @__swift_noop_void_return to i8*), i8* bitcast (i8* (i8*,
 i8*, %swift.type*)* @__swift_memcpy16_8 to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy16_8 to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy16_8 to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift
_memcpy16_8 to i8*), i8* bitcast (i8* (i8*, i8*, %swift.type*)* @__swift_memcpy16_8 to i8*), i8* bitcast (i32 (%swift.opaque*, i32, %swift.type*)* @getEnumTagSinglePayload value witness for TestOptional.MyType to i8*), i8* bitcast (void (%swift.
opaque*, i32, i32, %swift.type*)* @storeEnumTagSinglePayload value witness for TestOptional.MyType to i8*), i8* inttoptr (i64 16 to i8*), i8* inttoptr (i64 7 to i8*), i8* inttoptr (i64 16 to i8*)], align 8

// The full type metadata definition, including a reference to the witness table, this would somewhat resemble the C struct I showed above.
@full type metadata for TestOptional.MyType = internal constant <{ i8**, i64, <{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i32 }>*, i64, i64 }> <{ i8** getelementptr inbounds ([12 x i8*], [12 x i8*]* @value witness table for TestOptional.MyType, i32 0, i32 0), i64 1, <{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i32 }>* @nominal type descriptor for TestOptional.MyType, i64 0, i64 8 }>, align 8

// The type metadata which projects away the witness.
@type metadata for TestOptional.MyType = hidden alias %swift.type, bitcast (i64* getelementptr inbounds (<{ i8**, i64, <{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i16, i16, i32 }>*, i64, i64 }>, <{ i8**, i64, <{ i32, i32, i32, i32, i32,
 i32, i32, i32, i32, i32, i16, i16, i32 }>*, i64, i64 }>* @full type metadata for TestOptional.MyType, i32 0, i32 1) to %swift.type*)

This is why you hit the debugger in some examples (the ones which define a type in TestOptional.swift) and not in others (the ones which only use predefined types from the standard library).

Thank you @Arnold for explaining my questions.
As the 3 examples are working on X but not on Z (s390x), we have to look into the code that generates (predefined) witness table in standard lib first, can you let us know the functions? Is there any way to debug this code?

By the way, irgen::emitValueWitnessTable is not called
if we run the compiling with option -O for the 2nd and 3rd examples,
and the runtime code is not called either. Does this mean that these 2 examples also use the predefined witness table ?

Thanks,

Hi @Arnold, First of all that you so much for painstakingly going over the codegen/runtime steps. With every response of your's our comprehension of the code is improving.

However, I think we may still be missing some fundamental aspects of compiler - and I personally have difficulties mapping the Swift example code to some key compiler objects. What may also add to this difficulty is the fact that our examples are based on 2 separate branches (4.1 for us and master in your case).

As you may know, s390x architecture already has 4.0.3 version ported over and our goal is to be in step with latest Swift release. It is for this reason we have to use 4.1 branch.

In your examples we can clearly see calls to store/getEnum (in fact it appears in the IR gen code). However, we cannot say the same for our sample code. There are some assumptions being made which are not so clear in our case (for example, why would "var intarray = [11,12]" be treated as Enum by compiler and hence the trajectory of store/getEnum/valuwitness etc.).

Do you think it would be instructive for us to take the following approach:

  1. Base our discussions on Swift 4.1 code base

  2. Base our discussions in the context of code generated for the follwing sample code:
    cat inttest.swift
    var intarray = [11,12]
    for _ in intarray {
    }

  3. Lay down the mapping of payload/tag byte/value witness for the Swift sample code - ie., what causes arrays to be interpreted as Enum by compiler and what is a payload for the above array (I suspect 3 payloads - one for value 11 with tag byte containing 0, and one of value 12 with a tag byte containing 0, and one for None with a tag byte containing 1)

  4. Discuss the relationship between value witness/payload at compile time and runtime with possibly references to the code base.

I fully understand this is a big ask and extremely appreciative of your efforts in helping us see this one through :slight_smile:

Thanks again for your continued support.

My last post was based on the swift-4.1-branch.

I think the confusion is that some of the code that gets executed is compiled when you build the standard library.

An examples of that is the value witness and metadata emission of Int.

Where can a witness definition come from?

Either, from a library like libswiftCore.dylib. The code for the witness got generated at the time we build the library or is predefined (in a .cpp file).

Or from the executable, say TestOptional. The code for the witness gets generated (GenType.cpp) at the time we build the executable. This is the case for struct MyType {...} because it is defined in TestOptional.swift.

If your type is defined in the standard library, you will not hit a breakpoint or printf in GenType.cpp at the time you call swiftc inttest.swift.

This is the case if you compile inttest.swift. Or also with TestOptional.swift with createOptional(Int.self).

Both examples use the Int type. All type metadata data structures of Int are generated at the time we compile the standard library (libswiftCore.dylib). Int is defined in the standard library. An executable using the type will only reference a symbol to the metadata.

Rather you would hit that breakpoint or see the printf if you build the standard library as part of building the compiler.

I mentioned in one of my previous posts which functions need to be fixed: look for the sentence, "The functions in the runtime that need to be adjusted for big endian are" and "And for compiler generated witnesses: ". This holds true for swift-4.1-branch and master.

Repeating which functions need to be fixed:

  • GenType.cpp FixedTypeInfo::storeEnumTagSinglePayload/getEnumTagSinglePayload
  • EnumImpl.cpp: storeEnumTagSinglePayloadImpl, getEnumTagSinglePayloadImpl

In the same post I showed a back trace that shows that in inttest is calling
storeEnumTagSinglePayload witness. Here it is on swift-4.1-branch.

$ /Library/Developer/Toolchains/swift-4.1-DEVELOPMENT-SNAPSHOT-2018-03-28-a.xctoolchain/usr/bin/swiftc inttest.swift
$ lldb ./inttest
(lldb) target create "./inttest"
Current executable set to './inttest' (x86_64).
(lldb) breakpoint set --func-regex storeEnumTagSinglePayload
Breakpoint 1: 322 locations.
(lldb) run
Process 70037 launched: './inttest' (x86_64)
Process 70037 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.257
    frame #0: 0x000000010036de00 libswiftCore.dylib`storeEnumTagSinglePayload value witness for Swift.Int
libswiftCore.dylib`storeEnumTagSinglePayload value witness for Swift.Int:
->  0x10036de00 <+0>: pushq  %rbp
    0x10036de01 <+1>: movq   %rsp, %rbp
    0x10036de04 <+4>: popq   %rbp
    0x10036de05 <+5>: jmp    0x1003473b0               ; storeEnumTagSinglePayload value witness for Swift.Double
Target 0: (inttest) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.257
  * frame #0: 0x000000010036de00 libswiftCore.dylib`storeEnumTagSinglePayload value witness for Swift.Int
    frame #1: 0x00000001000a729e libswiftCore.dylib`Swift.IndexingIterator.next() -> Swift.Optional<A.Element> + 558
    frame #2: 0x0000000100000dad inttest`main + 317
    frame #3: 0x00007fff6eef9015 libdyld.dylib`start + 1

Note how both IndexingIterator.next and the witness is defined in libswiftCore.dylib. The code for it was generated by the compiler when libswiftCore.dylib was compiled.

This is the reason why I keep going back to my example -- it is easier to show all parts because both the type MyType and the use createOptional(MyType.self) is in the same file. So you can see all the pieces in the generated IR/object file.

If you wanted so see the emission for Int you would have to debug the compiler generating the code for libswiftCore.dylib.

You could observe this if you rebuild the standard library. If you invoke the build-script with --verbose you should find a line in the build log that looks something like:

/home/aschwaighofer/LLVMCompiler/build/Ninja-RelWithDebInfoAssert/swift-linux-x86_64/./bin/swiftc -c -sdk / -target x86_64-unknown-linux-gnu -resource-dir /home/aschwaighofer/LLVMCompiler/build/Ninja-RelWithDebInfoAssert/swift-linux-x86_64/./lib/swift -O -g -D INTERNAL_CHECKS_ENABLED -D SWIFT_ENABLE_RUNTIME_FUNCTION_COUNTERS -I /home/aschwaighofer/LLVMCompiler/build/Ninja-RelWithDebInfoAssert/swift-linux-x86_64/./lib/swift/linux/x86_64 -module-cache-path /home/aschwaighofer/LLVMCompiler/build/Ninja-RelWithDebInfoAssert/swift-linux-x86_64/./module-cache -no-link-objc-runtime -Xfrontend -enable-resilience -nostdimport -parse-stdlib -module-name Swift -Xfrontend -group-info-path -Xfrontend /home/aschwaighofer/LLVMCompiler/swift/stdlib/public/core/GroupInfo.json -swift-version 3 -warn-swift3-objc-inference-complete -Xllvm -sil-inline-generics -Xllvm -sil-partial-specialization -Xfrontend -enable-sil-ownership -module-link-name swiftCore -force-single-frontend-invocation -Xcc -D__SWIFT_CURRENT_DYLIB=swiftCore -parse-as-library -o /home/aschwaighofer/LLVMCompiler/build/Ninja-RelWithDebInfoAssert/swift-linux-x86_64/stdlib/public/core/linux/x86_64/Swift.o @/home/aschwaighofer/LLVMCompiler/build/Ninja-RelWithDebInfoAssert/swift-linux-x86_64/stdlib/public/core/ZUZCy.txt

The standard library contains many types so I don't think you would want to debug this command.

Here is an example that will hit a value witness predefined in the runtime (in a .cpp file) also on swift-4.1-branch:

$ cat TestOptional.swift 
func createOptionalNil<T>(_ t: T.Type) -> T? {
  return nil
}

func testNil() {
  typealias Callback = () -> ()
  let _ = createOptionalNil(Callback.self)
}

testNil()
$ /Library/Developer/Toolchains/swift-4.1-DEVELOPMENT-SNAPSHOT-2018-03-28-a.xctoolchain/usr/bin/swiftc TestOptional.swift 
$ lldb ./TestOptional
(lldb) target create "./TestOptional"
Current executable set to './TestOptional' (x86_64).
(lldb) breakpoint set --func-regex storeEnumTagSinglePayload
Breakpoint 1: 322 locations.
(lldb) run
Process 70220 launched: './TestOptional' (x86_64)
Process 70220 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.304
    frame #0: 0x000000010037fb70 libswiftCore.dylib`swift::metadataimpl::FixedSizeBufferValueWitnesses<swift::metadataimpl::ValueWitnesses<(anonymous namespace)::ThickFunctionBox>, 16ul, 8ul, true>::storeEnumTagSinglePayload(swift::OpaqueValue*, int, unsigned int, swift::TargetMetadata<swift::InProcess> const*)
libswiftCore.dylib`swift::metadataimpl::FixedSizeBufferValueWitnesses<swift::metadataimpl::ValueWitnesses<(anonymous namespace)::ThickFunctionBox>, 16ul, 8ul, true>::storeEnumTagSinglePayload:
->  0x10037fb70 <+0>:  pushq  %rbp
    0x10037fb71 <+1>:  movq   %rsp, %rbp
    0x10037fb74 <+4>:  cmpl   $0x7fffffff, %esi         ; imm = 0x7FFFFFFF 
    0x10037fb7a <+10>: jne    0x10037fb95               ; <+37>
Target 0: (TestOptional) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.304
  * frame #0: 0x000000010037fb70 libswiftCore.dylib`swift::metadataimpl::FixedSizeBufferValueWitnesses<swift::metadataimpl::ValueWitnesses<(anonymous namespace)::ThickFunctionBox>, 16ul, 8ul, true>::storeEnumTagSinglePayload(swift::OpaqueValue*, int, unsigned int, swift::TargetMetadata<swift::InProcess> const*)
    frame #1: 0x0000000100000d98 TestOptional`_T012TestOptional06createB3NilxSgxmlF + 56
    frame #2: 0x0000000100000dc5 TestOptional`TestOptional.testNil() -> () + 37
    frame #3: 0x0000000100000d54 TestOptional`main + 20
    frame #4: 0x00007fff6eef9015 libdyld.dylib`start + 1
    frame #5: 0x00007fff6eef9015 libdyld.dylib`start + 1

To understand why you see an Optional instantiated here it is instructive to look at the SIL generated for this code. This is translated into something like: let seq = Sequence(intarray); while (seq.next() == Some(e)) { // use e }

$ /Library/Developer/Toolchains/swift-4.1-DEVELOPMENT-SNAPSHOT-2018-03-28-a.xctoolchain/usr/bin/swiftc inttest.swift -emit-sil |  /Library/Developer/Toolchains/swift-4.1-DEVELOPMENT-SNAPSHOT-2018-03-28-a.xctoolchain/usr/bin/swift-demangle | less

sil @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):
  alloc_global @inttest.intarray : [Swift.Int]      // id: %2
  %3 = global_addr @inttest.intarray : [Swift.Int] : $*Array<Int> // users: %23, %25
  %4 = metatype $@thin Array<Int>.Type            // user: %22
  %5 = integer_literal $Builtin.Word, 2           // user: %7
  // function_ref _allocateUninitializedArray<A>(_:)
  %6 = function_ref @Swift._allocateUninitializedArray<A>(Builtin.Word) -> ([A], Builtin.RawPointer) : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) // user: %7
  %7 = apply %6<Int>(%5) : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) // users: %11, %8, %10
  %8 = tuple_extract %7 : $(Array<Int>, Builtin.RawPointer), 0 // users: %22, %9
  retain_value %8 : $Array<Int>                   // id: %9
  %10 = tuple_extract %7 : $(Array<Int>, Builtin.RawPointer), 1 // user: %12
  release_value %7 : $(Array<Int>, Builtin.RawPointer) // id: %11
  %12 = pointer_to_address %10 : $Builtin.RawPointer to [strict] $*Int // users: %15, %17
  %13 = integer_literal $Builtin.Int64, 11        // user: %14
  %14 = struct $Int (%13 : $Builtin.Int64)        // user: %15
  store %14 to %12 : $*Int                        // id: %15
  %16 = integer_literal $Builtin.Word, 1          // user: %17
  %17 = index_addr %12 : $*Int, %16 : $Builtin.Word // user: %20
  %18 = integer_literal $Builtin.Int64, 12        // user: %19
  %19 = struct $Int (%18 : $Builtin.Int64)        // user: %20
  store %19 to %17 : $*Int                        // id: %20
  // function_ref specialized Array.init(arrayLiteral:)
  %21 = function_ref @generic specialization <Swift.Int> of Swift.Array.init(arrayLiteral: A...) -> [A] : $@convention(method) (@owned Array<Int>, @thin Array<Int>.Type) -> @owned Array<Int> // user: %22
  %22 = apply %21(%8, %4) : $@convention(method) (@owned Array<Int>, @thin Array<Int>.Type) -> @owned Array<Int> // user: %23
  store %22 to %3 : $*Array<Int>                  // id: %23
  %24 = alloc_stack $IndexingIterator<Array<Int>>, var, name "$generator" // users: %49, %48, %32, %38
  %25 = begin_access [read] [dynamic] %3 : $*Array<Int> // users: %26, %28
  %26 = load %25 : $*Array<Int>                   // users: %30, %27
  retain_value %26 : $Array<Int>                  // id: %27
  end_access %25 : $*Array<Int>                   // id: %28
  %29 = alloc_stack $Array<Int>                   // users: %33, %30, %35, %32
  store %26 to %29 : $*Array<Int>                 // id: %30
  // function_ref Collection<>.makeIterator()
  %31 = function_ref @(extension in Swift):Swift.Collection< where A.Iterator == Swift.IndexingIterator<A>>.makeIterator() -> Swift.IndexingIterator<A> : $@convention(method) <τ_0_0 where τ_0_0 : Collection, τ_0_0.Iterator == IndexingIterator<τ_0_0>> (@in_guaranteed τ_0_0) -> @out IndexingIterator<τ_0_0> // user: %32
  %32 = apply %31<[Int]>(%24, %29) : $@convention(method) <τ_0_0 where τ_0_0 : Collection, τ_0_0.Iterator == IndexingIterator<τ_0_0>> (@in_guaranteed τ_0_0) -> @out IndexingIterator<τ_0_0>
  %33 = load %29 : $*Array<Int>                   // user: %34
  release_value %33 : $Array<Int>                 // id: %34
  dealloc_stack %29 : $*Array<Int>                // id: %35
  br bb1                                          // id: %36

bb1:                                              // Preds: bb3 bb0
  %37 = alloc_stack $Optional<Int>                // users: %42, %43, %40
  %38 = begin_access [modify] [static] %24 : $*IndexingIterator<Array<Int>> // users: %41, %40
  // function_ref IndexingIterator.next()
  %39 = function_ref @Swift.IndexingIterator.next() -> A.Element? : $@convention(method) <τ_0_0 where τ_0_0 : Collection> (@inout IndexingIterator<τ_0_0>) -> @out Optional<τ_0_0.Element> // user: %40
  %40 = apply %39<[Int]>(%37, %38) : $@convention(method) <τ_0_0 where τ_0_0 : Collection> (@inout IndexingIterator<τ_0_0>) -> @out Optional<τ_0_0.Element>
  end_access %38 : $*IndexingIterator<Array<Int>> // id: %41
  %42 = load %37 : $*Optional<Int>                // user: %44
  dealloc_stack %37 : $*Optional<Int>             // id: %43
  switch_enum %42 : $Optional<Int>, case #Optional.some!enumelt.1: bb3, case #Optional.none!enumelt: bb2 // id: %44

bb2:                                              // Preds: bb1
  br bb4                                          // id: %45

bb3(%46 : $Int):                                  // Preds: bb1
  br bb1                                          // id: %47

bb4:                                              // Preds: bb2

No, in this case the compiler has optimized away the code. Look at the output of -emit-sil. For inttest.swift you will see the the loop in main has disappeared:

sil @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):
  alloc_global @inttest.intarray : [Swift.Int]      // id: %2
  %3 = global_addr @inttest.intarray : [Swift.Int] : $*Array<Int> // user: %9
  %4 = global_value @mainTv_ : $_ContiguousArrayStorage<Int> // users: %12, %5
  %5 = unchecked_ref_cast %4 : $_ContiguousArrayStorage<Int> to $Builtin.BridgeObject // user: %6
  %6 = struct $_BridgeStorage<_ContiguousArrayStorageBase, _NSArrayCore> (%5 : $Builtin.BridgeObject) // user: %7
  %7 = struct $_ArrayBuffer<Int> (%6 : $_BridgeStorage<_ContiguousArrayStorageBase, _NSArrayCore>) // user: %8
  %8 = struct $Array<Int> (%7 : $_ArrayBuffer<Int>) // user: %9
  store %8 to %3 : $*Array<Int>                   // id: %9
  %10 = integer_literal $Builtin.Int32, 0         // user: %11
  %11 = struct $Int32 (%10 : $Builtin.Int32)      // user: %13
  strong_retain %4 : $_ContiguousArrayStorage<Int> // id: %12
  return %11 : $Int32                             // id: %13
}

Similar for $ /Library/Developer/Toolchains/swift-4.1-DEVELOPMENT-SNAPSHOT-2018-03-28-a.xctoolchain/usr/bin/swiftc TestOptional.swift -emit-sil -O | /Library/Developer/Toolchains/swift-4.1-DEVELOPMENT-SNAPSHOT-2018-03-28-a.xctoolchain/usr/bin/swift-demangle | less

Thanks @Arnold.

I stumbled upon this line which zeros the tag bit for Payload cases:

emitMemZero

I went on to modify the code so instead of emitting zeros it wrote 5 to the tag bit. This caused the infinite loop in our example to exit albeit prematurely (just after the first iteration). This is clearly not a solution but I think it shows where in the code that bit is being written.

Following the logic I deduced (maybe incorrectly) that it was at 639 where the jump decision was made to write this byte out (payload case). For null case it would probably jump at isExtraTagBitsCaseBB - Maybe it is this region where the problem may be (I tried adding a custom function emitMemOne which writes 1 to the tag bit at this location but it overwrote the valid 0 set earlier) - I'll continue to poke around.

So this change seems to have addressed it .. looking at implications:

/============================================/

index 1317565..0673779 100644
--- a/lib/IRGen/GenType.cpp
+++ b/lib/IRGen/GenType.cpp
@@ -574,6 +574,17 @@ static void emitMemZero(IRGenFunction &IGF, llvm::Value *addr,
                               size);
 }

+/// Emit a memset of one operation for a \p size of 0 to 4 bytes.
+static void emitMemOne(IRGenFunction &IGF, llvm::Value *addr,
+                        llvm::Value *size) {
+  auto *oneByte = llvm::ConstantInt::get(IGF.IGM.Int8Ty, 1U);
+  emitSpecializedMemOperation(IGF,
+                              [=](IRBuilder &B, uint64_t numBytes) {
+                                B.CreateMemSet(addr, oneByte, numBytes, 1);
+                              },
+                              size);
+}
+
 /// Emit a memcpy operation for a \p size of 0 to 4 bytes.
 static void emitMemCpy(IRGenFunction &IGF, llvm::Value *to, llvm::Value *from,
                        llvm::Value *size) {
@@ -701,6 +712,7 @@ void FixedTypeInfo::storeEnumTagSinglePayload(IRGenFunction &IGF,
         extraZeroAddr, llvm::ConstantInt::get(IGM.Int8Ty, 0),
         Builder.CreateSub(size, llvm::ConstantInt::get(size->getType(), 4)), 1);
   emitMemCpy(IGF, extraTagBitsAddr, extraTagIndexAddr, numExtraTagBytes);
+  emitMemOne(IGF, extraTagBitsAddr, numExtraTagBytes);
   Builder.CreateBr(returnBB);

   Builder.emitBlock(returnBB);

/============================================/

@Arnold.
When debugging the two function FixedTypeInfo::getEnumTagSinglePayload and FixedTypeInfo::storeEnumTagSinglePayload, we found some local variable pointers like extraTagBits or numExtraTagBytes points to llvm::Value type, and value has

(gdb)  p *extraTagBits
$5 = {VTy = 0xa28c240, UseList = 0x0, SubclassID = 53 '5', HasValueHandle = 0 '\000', SubclassOptionalData = 0 '\000',
  SubclassData = 0, NumUserOperands = 1, IsUsedByMD = 0, HasName = 0, HasHungOffUses = 0, HasDescriptor = 0,
  static MaxAlignmentExponent = 29, static MaximumAlignment = 536870912}

We also found NumUserOperands difference bwt x and z, on x it is 1/2, but on z it is 16/32, what does this values means here (NumUserOperands, SubclassData etc). does any one relate to the tag?

Thanks.