Uploaded image for project: 'Swift'
  1. Swift
  2. SR-7242

Usage of global constants to C functions generate suboptimal code (unless manually @convention(c)'d)

    Details

    • Type: Bug
    • Status: Open
    • Priority: Medium
    • Resolution: Unresolved
    • Component/s: Compiler
    • Labels:

      Description

      For this code

      import Darwin
      
      private let conventionCGlobal : @convention(c) (CInt, UnsafeRawPointer?, size_t) -> Int = write
      private let normalGlobal: (CInt, UnsafeRawPointer?, size_t) -> Int = write
      
      public enum System {
          @inline(never)
          public static func writeViaConventionCGlobal(fd: CInt, buffer: UnsafeRawPointer, size: size_t) -> ssize_t {
              return conventionCGlobal(fd, buffer, size)
          }
      
          @inline(never)
          public static func writeViaNormalGlobal(fd: CInt, buffer: UnsafeRawPointer, size: size_t) -> ssize_t {
              return normalGlobal(fd, buffer, size)
          }
      
          @inline(never)
          public static func writeViaModule(fd: CInt, buffer: UnsafeRawPointer, size: size_t) -> ssize_t {
              return Darwin.write(fd, buffer, size)
          }
      }
      

      I would expect the code generated for System.writeViaConventionCGlobal, System. writeViaNormalGlobal and System.writeViaModule to be identical. However, it's totally not:

      This is the command I run

      xcrun -toolchain org.swift.4120180319a swiftc -O -o test test.swift
      

      which is

      $ xcrun -toolchain org.swift.4120180319a swift --version
      Apple Swift version 4.1-dev (LLVM 260a172ffb, Clang cd84be6c42, Swift 04baf31321)
      Target: x86_64-apple-darwin17.4.0
      

      (today's 4.1 snapshot).

      This is the assembly that comes out of this

      xcrun -toolchain org.swift.4120180319a swiftc -O -o test test.swift && otool -tV test | /Library/Developer/Toolchains/swift-4.1-DEVELOPMENT-SNAPSHOT-2018-03-19-a.xctoolchain/usr/bin/swift demangle
      

      First System.writeViaModule

      _static test.System.writeViaModule(fd: Swift.Int32, buffer: Swift.UnsafeRawPointer, size: Swift.Int) -> Swift.Int:
      0000000100000d40        pushq   %rbp
      0000000100000d41        movq    %rsp, %rbp
      0000000100000d44        popq    %rbp
      0000000100000d45        jmp     0x100000f02 ## symbol stub for: _write
      0000000100000d4a        nopw    (%rax,%rax)
      

      which is perfect. Then System.writeViaConventionCGlobal

      _static test.System.writeViaConventionCGlobal(fd: Swift.Int32, buffer: Swift.UnsafeRawPointer, size: Swift.Int) -> Swift.Int:
      0000000100000cb0        pushq   %rbp
      0000000100000cb1        movq    %rsp, %rbp
      0000000100000cb4        movq    _test.(conventionCGlobal in _83378C430F65473055F1BD53F3ADCDB7) : @convention(c) (Swift.Int32, Swift.UnsafeRawPointer?, Swift.Int) -> Swift.Int(%rip), %rax
      0000000100000cbb        popq    %rbp
      0000000100000cbc        jmpq    *%rax
      0000000100000cbe        nop
      

      still almost perfect (but why isn't the constant propagated). But then System.writeViaNormalGlobal

      _static test.System.writeViaNormalGlobal(fd: Swift.Int32, buffer: Swift.UnsafeRawPointer, size: Swift.Int) -> Swift.Int:
      0000000100000cc0        pushq   %rbp
      0000000100000cc1        movq    %rsp, %rbp
      0000000100000cc4        pushq   %r15
      0000000100000cc6        pushq   %r14
      0000000100000cc8        pushq   %r13
      0000000100000cca        pushq   %r12
      0000000100000ccc        pushq   %rbx
      0000000100000ccd        pushq   %rax
      0000000100000cce        movq    %rdx, %r13
      0000000100000cd1        movq    %rsi, %r15
      0000000100000cd4        movl    %edi, %r12d
      0000000100000cd7        movq    _test.(normalGlobal in _83378C430F65473055F1BD53F3ADCDB7) : (Swift.Int32, Swift.UnsafeRawPointer?, Swift.Int) -> Swift.Int(%rip), %r14
      0000000100000cde        movq    0x42b(%rip), %rbx
      0000000100000ce5        movq    %rbx, %rdi
      0000000100000ce8        callq   _swift_rt_swift_retain
      0000000100000ced        movl    %r12d, %edi
      0000000100000cf0        movq    %r15, %rsi
      0000000100000cf3        movq    %r13, %rdx
      0000000100000cf6        movq    %rbx, %r13
      0000000100000cf9        callq   *%r14
      0000000100000cfc        movq    %rax, %r14
      0000000100000cff        movq    %rbx, %rdi
      0000000100000d02        callq   _swift_rt_swift_release
      0000000100000d07        movq    %r14, %rax
      0000000100000d0a        addq    $0x8, %rsp
      0000000100000d0e        popq    %rbx
      0000000100000d0f        popq    %r12
      0000000100000d11        popq    %r13
      0000000100000d13        popq    %r14
      0000000100000d15        popq    %r15
      0000000100000d17        popq    %rbp
      0000000100000d18        retq
      0000000100000d19        nopl    (%rax)
      

      which is pretty terrible. It'll affect SwiftNIO ( https://github.com/apple/swift-nio/blob/master/Sources/NIO/System.swift#L34-L60 ) but I guess we can work around.

      Unfortunately also just writing

      let sysWrite = write
      

      gets you the suboptimal code, so the @convention(c) isn't inferred.

        Attachments

          Activity

            People

            • Assignee:
              Unassigned
              Reporter:
              jw Johannes Weiß
            • Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

              • Created:
                Updated: