Death by a 1000 Cuts: Bringing Swift to Windows Saleem Abdulrasool - - PowerPoint PPT Presentation

death by a 1000 cuts
SMART_READER_LITE
LIVE PREVIEW

Death by a 1000 Cuts: Bringing Swift to Windows Saleem Abdulrasool - - PowerPoint PPT Presentation

Death by a 1000 Cuts: Bringing Swift to Windows Saleem Abdulrasool ( @ compnerd) Porting by a 1000 Patches: Bringing Swift to Windows Saleem Abdulrasool ( @ compnerd) Why Swift? Why Swift? Safe Why Swift? Safe Flexible


slide-1
SLIDE 1

Death by a 1000 Cuts:


Bringing Swift to Windows

Saleem Abdulrasool (@compnerd)

slide-2
SLIDE 2

Saleem Abdulrasool (@compnerd)

Porting by a 1000 Patches:
 Bringing Swift to Windows

slide-3
SLIDE 3

Why Swift?

slide-4
SLIDE 4

Why Swift?

  • Safe
slide-5
SLIDE 5

Why Swift?

  • Safe
  • Flexible

import SwiftWebUI struct MainPage: View { var body: some View { VStack { Text("🥒 🍟 ") .padding(.all) .background(.green, cornerRadius: 12) .foregroundColor(.white) } } }

slide-6
SLIDE 6

Why Swift?

  • Safe
  • Flexible
  • Multi-paradigm
slide-7
SLIDE 7

Why Swift?

  • Safe
  • Flexible
  • Multi-paradigm
  • Compiled Language
slide-8
SLIDE 8

Why Swift?

  • Safe
  • Flexible
  • Multi-paradigm
  • Compiled Language
  • Break from legacy codebase
slide-9
SLIDE 9

Why Swift?

  • Safe
  • Flexible
  • Multi-paradigm
  • Compiled Language
  • Break from legacy codebase
  • Systems Development
slide-10
SLIDE 10

Why Windows?

slide-11
SLIDE 11

Why Windows?

  • Access to developers and users
slide-12
SLIDE 12

Why Windows?

  • Access to developers and users
  • Enables Portable System and Application Code
slide-13
SLIDE 13

Why Windows?

  • Access to developers and users
  • Enables Portable System and Application Code
  • Improves the Swift and LLVM projects codebases
slide-14
SLIDE 14

Why Windows?

  • Access to developers and users
  • Enables Portable System and Application Code
  • Improves the Swift and LLVM projects codebases
  • Interesting Challenge
slide-15
SLIDE 15

Pawn Takes Queen

slide-16
SLIDE 16

Pawn Takes Queen

  • Compiler
slide-17
SLIDE 17

Pawn Takes Queen

  • Compiler
  • Runtime/Standard Library
slide-18
SLIDE 18

Pawn Takes Queen

  • Compiler
  • Runtime/Standard Library
  • Core Libraries (libdispatch, Foundation, XCTest)
slide-19
SLIDE 19

Pawn Takes Queen

  • Compiler
  • Runtime/Standard Library
  • Core Libraries (libdispatch, Foundation, XCTest)
  • Debugger (lldb)
slide-20
SLIDE 20

Pawn Takes Queen

  • Compiler
  • Runtime/Standard Library
  • Core Libraries (libdispatch, Foundation, XCTest)
  • Debugger (lldb)
  • Developer Tools (SourceKit-LSP

, swift-package-manager)

slide-21
SLIDE 21

The Tortoise & The Hare

slide-22
SLIDE 22

The Tortoise & The Hare

  • The Windows community is interested
slide-23
SLIDE 23

The Tortoise & The Hare

  • The Windows community is interested
  • Previous Attempts
slide-24
SLIDE 24

The Tortoise & The Hare

  • The Windows community is interested
  • Previous Attempts
  • cygwin
  • MinGW
slide-25
SLIDE 25

The Tortoise & The Hare

  • The Windows community is interested
  • Previous Attempts
  • cygwin
  • MinGW
  • WSL
slide-26
SLIDE 26

The Tortoise & The Hare

  • The Windows community is interested
  • Previous Attempts
  • cygwin
  • MinGW
  • WSL
  • Windows Swift
slide-27
SLIDE 27

Instructions Not Included

slide-28
SLIDE 28

Instructions Not Included

  • CMake
slide-29
SLIDE 29

Instructions Not Included

  • CMake
  • autotools
slide-30
SLIDE 30

Instructions Not Included

  • CMake
  • autotools
  • custom build systems
slide-31
SLIDE 31

Instructions Not Included

  • CMake
  • autotools
  • custom build systems
  • build-script
slide-32
SLIDE 32

An Alien Planet

slide-33
SLIDE 33

An Alien Planet

  • bash cmd
  • make nmake
slide-34
SLIDE 34

An Alien Planet

  • bash cmd
  • make nmake
  • Windows’ VFS is slower than Linux’s VFS
slide-35
SLIDE 35

An Alien Planet

  • bash cmd
  • make nmake
  • Windows’ VFS is slower than Linux’s VFS
  • cross-compilation conveniently solves these problems
slide-36
SLIDE 36

All I Have is a Hammer

slide-37
SLIDE 37

All I Have is a Hammer

  • compiler
  • clang, clang-cl - VFS
slide-38
SLIDE 38

All I Have is a Hammer

  • compiler
  • clang, clang-cl - VFS
  • assembler
  • IAS - AT&T vs Intel
slide-39
SLIDE 39

All I Have is a Hammer

  • compiler
  • clang, clang-cl - VFS
  • assembler
  • IAS - AT&T vs Intel
  • linker
  • gold, bfd - ELF only, lack of MS SDK support
slide-40
SLIDE 40

All I Have is a Hammer

  • compiler
  • clang, clang-cl - VFS
  • assembler
  • IAS - AT&T vs Intel
  • linker
  • gold, bfd - ELF only, lack of MS SDK support
  • link - must build on Windows
slide-41
SLIDE 41

All I Have is a Hammer

  • compiler
  • clang, clang-cl - VFS
  • assembler
  • IAS - AT&T vs Intel
  • linker
  • gold, bfd - ELF only, lack of MS SDK support
  • link - must build on Windows
  • lld - couldn’t generate import libraries
slide-42
SLIDE 42

All I Have is a Hammer

  • compiler
  • clang, clang-cl - VFS
  • assembler
  • IAS - AT&T vs Intel
  • linker
  • gold, bfd - ELF only, lack of MS SDK support
  • link - must build on Windows
  • lld - couldn’t generate import libraries
  • Symlink Forest
slide-43
SLIDE 43

99 Standards on The Wall

slide-44
SLIDE 44
  • C++ is dark and full of terrors
  • size = Builder.CreateAdd(
  • Builder.CreateAnd(Builder.CreateAdd(heapHeaderSize, alignmentMask),
  • Builder.CreateNot(alignmentMask)),
  • size);

+ auto *Add = Builder.CreateAdd(heapHeaderSize, alignmentMask); + auto *Not = Builder.CreateNot(alignmentMask); + size = Builder.CreateAdd(Builder.CreateAnd(Add, Not), size);

99 Standards on The Wall

slide-45
SLIDE 45
  • C++ is dark and full of terrors
  • return OwnedString(StringRef(OwnedPtr->getText(), Str.size()),
  • std::move(OwnedPtr));

+ // Allocate the StringRef on the stack first. This is to ensure that the + // order of evaluation of the arguments is specified. The specification + // does not specify the order of evaluation for the arguments. Itanium + // chose to evaluate left to right, while Windows evaluates right to left. + // As such, it is possible that the OwnedPtr has already been `std::move`d + // by the time that the StringRef is attempted to be created. In such a + // case, the offset of the field (+4) is used instead of the pointer to + // the text, resulting in invalid memory references. + StringRef S(OwnedPtr->getText(), Str.size()); + return OwnedString(S, std::move(OwnedPtr));

99 Standards on The Wall

slide-46
SLIDE 46
  • C++ is dark and full of terrors
  • clang-tidy
  • libstdc++ vs libc++ vs msvcprt

99 Standards on The Wall

slide-47
SLIDE 47
  • C++ is dark and full of terrors
  • clang-tidy
  • libstdc++ vs libc++ vs msvcprt

99 Standards on The Wall

+#if os(Windows) +public typealias ThreadHandle = HANDLE +#else +public typealias ThreadHandle = pthread_t +#endif

  • public func _stdlib_pthread_create_block<Argument, Result>(

+public func _stdlib_thread_create_block<Argument, Result>( _ start_routine: @escaping (Argument) -> Result, _ arg: Argument

  • ) -> (CInt, pthread_t?) {

+) -> (CInt, ThreadHandle?) { let context = ThreadBlockContextImpl(block: start_routine, arg: arg) let contextAsVoidPointer = Unmanaged.passRetained(context).toOpaque() +#if os(Windows) + var threadID = + _beginthreadex(nil, 0, { invokeBlockContext($0)! + .assumingMemoryBound(to: UInt32.self).pointee }, + contextAsVoidPointer, 0, nil) + return threadID == 0 ? (errno, nil) + : (0, UnsafeMutablePointer<ThreadHandle>(&threadID).pointee) +#else

slide-48
SLIDE 48
  • C++ is dark and full of terrors
  • clang-tidy
  • libstdc++ vs libc++ vs msvcprt
  • libSystem/BSD libc vs glibc vs msvcrt/ucrt vs bionic

99 Standards on The Wall

slide-49
SLIDE 49

Objective Evaluation

slide-50
SLIDE 50
  • Weak Linking

Objective Evaluation

slide-51
SLIDE 51

+ if (Context.LangOpts.Target.isOSBinFormatCOFF()) { + if (DK == DAK_WeakLinked) { + diagnose(Loc, diag::attr_unsupported_on_target, AttrName, + Context.LangOpts.Target.str()); + DiscardAttribute = true; + } + }

  • Weak Linking

Objective Evaluation

slide-52
SLIDE 52

encodeForceLoadSymbolName(buf, linkLib.getName()); auto ForceImportThunk = Module.getOrInsertFunction(buf, llvm::FunctionType::get(VoidTy, false));

  • ApplyIRLinkage(IRLinkage::ExternalWeakImport)
  • .to(cast<llvm::GlobalValue>(ForceImportThunk));

+ + const IRLinkage IRL = + llvm::Triple(Module.getTargetTriple()).isOSBinFormatCOFF() + ? IRLinkage::ExternalImport + : IRLinkage::ExternalWeakImport; + ApplyIRLinkage(IRL).to(cast<llvm::GlobalValue>(ForceImportThunk));

  • Weak Linking

Objective Evaluation

slide-53
SLIDE 53
  • Weak Linking
  • DLL Storage

Objective Evaluation

slide-54
SLIDE 54

if (auto fn = dyn_cast<llvm::Function>(cache)) { fn->setCallingConv(cc); + bool IsExternal = + fn->getLinkage() == llvm::GlobalValue::AvailableExternallyLinkage || + (fn->getLinkage() == llvm::GlobalValue::ExternalLinkage && + fn->isDeclaration()); + + if (!isStandardLibrary(Module) && IsExternal && + ::useDllStorage(llvm::Triple(Module.getTargetTriple()))) + fn->setDLLStorageClass(llvm::GlobalValue::DLLImportStorageClass);

  • Weak Linking
  • DLL Storage

Objective Evaluation

slide-55
SLIDE 55

+ case SILLinkage::PublicNonABI: + return isDefinition ? RESULT(WeakODR, Hidden, Default) + : RESULT(External, Hidden, Default);

  • Weak Linking
  • DLL Storage
  • Multiple Definitions

Objective Evaluation

slide-56
SLIDE 56
  • Weak Linking
  • DLL Storage
  • Multiple Definitions
  • COMDAT Groups

Objective Evaluation

slide-57
SLIDE 57

What did you call me?

slide-58
SLIDE 58

What did you call me?

  • Calling Conventions
slide-59
SLIDE 59

What did you call me?

  • Calling Conventions
  • PreserveMost
  • SwiftCall
slide-60
SLIDE 60

What did you call me?

  • Calling Conventions
  • PreserveMost
  • SwiftCall
  • Name Decoration

void C(); void __attribute__((__swiftcall__)) Swift(); void __attribute__((__preserve_most__)) PreserveMost(); int CC(void (&)()); template <typename T> int CC(T &); int r = CC(C) // ?CC@@YAHA6AXXZ@Z + CC(Swift) // ??$CC@$$A6SXXZ@@YAHP6SXXZ@Z + CC(PreserveMost); // ??$CC@$$A6UXXZ@@YAHP6UXXZ@Z A S U

slide-61
SLIDE 61

What did you call me?

  • Calling Conventions
  • PreserveMost
  • SwiftCall
  • Name Decoration
  • Vendor Controlled Platform
slide-62
SLIDE 62

What did you call me?

  • Calling Conventions
  • PreserveMost
  • SwiftCall
  • Name Decoration
  • Vendor Controlled Platform
  • clang extensions
slide-63
SLIDE 63

What did you call me?

  • Calling Conventions
  • PreserveMost
  • SwiftCall
  • Name Decoration
  • Vendor Controlled Platform
  • clang extensions
  • Language Extensions
slide-64
SLIDE 64

You‘ve Got Mail

slide-65
SLIDE 65

You‘ve Got Mail

  • Calling conventions and language boundaries
slide-66
SLIDE 66

You‘ve Got Mail

  • Calling conventions and language boundaries

#if !SWIFT_OBJC_INTEROP // __SwiftValue is a native class SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERNAL

  • bool swift_swiftValueConformsTo(const Metadata *);

+bool swift_swiftValueConformsTo(const Metadata *, const Metadata *); @_silgen_name("swift_swiftValueConformsTo") public func _swiftValueConformsTo<T>(_ type: T.Type) -> Bool { if let foundationType = _foundationSwiftValueType { return foundationType is T.Type } else { return __SwiftValue.self is T.Type } }

slide-67
SLIDE 67

Heisen-Jigsaw Puzzles

slide-68
SLIDE 68

Heisen-Jigsaw Puzzles

  • Multiple bugs interact in complicated ways
slide-69
SLIDE 69

Heisen-Jigsaw Puzzles

  • Multiple bugs interact in complicated ways

Sym->Aux[0].AuxType = ATWeakExternal; Sym->Aux[0].Aux.WeakExternal.TagIndex = 0; Sym->Aux[0].Aux.WeakExternal.Characteristics =

  • COFF::IMAGE_WEAK_EXTERN_SEARCH_LIBRARY;

+ COFF::IMAGE_WEAK_EXTERN_SEARCH_ALIAS; } else { if (!Base) Sym->Data.SectionNumber = COFF::IMAGE_SYM_ABSOLUTE; auto addr = getAddrOfLLVMVariable(*entity, ConstantInit(), DbgTy, refKind, defaultVarTy); + if (auto *GV = dyn_cast<llvm::GlobalVariable>(addr.getValue())) + GV->setComdat(nullptr); // FIXME: MC breaks when emitting alias references on some platforms // (rdar://problem/22450593 ). Work around this by referring to the aliasee

L L V M S W I F T

slide-70
SLIDE 70

Creepy Crawlers

slide-71
SLIDE 71

Creepy Crawlers

  • PDB Support
slide-72
SLIDE 72

Creepy Crawlers

  • PDB Support
  • Cross Language Boundaries
slide-73
SLIDE 73

Creepy Crawlers

  • PDB Support
  • Cross Language Boundaries
  • Swift’s Debugging Model
slide-74
SLIDE 74

Aperture Science Lab

slide-75
SLIDE 75

Aperture Science Lab

  • IRGen
slide-76
SLIDE 76

Aperture Science Lab

  • IRGen
  • lit
slide-77
SLIDE 77

Aperture Science Lab

  • IRGen
  • lit
  • Paths
slide-78
SLIDE 78

It’s a Marathon

slide-79
SLIDE 79

It’s a Marathon

slide-80
SLIDE 80

It’s a Marathon

  • CI
  • Testing
slide-81
SLIDE 81

It’s a Marathon

  • CI
  • Testing
  • Components
slide-82
SLIDE 82

It’s a Marathon

  • CI
  • Testing
  • Components
  • Distributions
slide-83
SLIDE 83

The Old, New Thing

slide-84
SLIDE 84

The Old, New Thing

1 import WinSDK 2 import SwiftWin32 // https://github.com/compnerd/swift-win32 3 4 class EventHandler: WindowDelegate { 5 func OnDestroy(_ hWnd: HWND?, _ wParam: WPARAM, _ lParam: LPARAM) 6 -> LRESULT { 7 PostQuitMessage(0) 8 return 0 9 } 10 11 func OnCommand(_ hWnd: HWND?, _ wParam: WPARAM, _ lParam: LPARAM) 12 -> LRESULT { 13 MessageBoxW(nil, "Swift/Win32 Demo!".LPCWSTR, 14 "Swift/Win32 MessageBox!".LPCWSTR, UINT(MB_OK)) 15 return 0 16 } 17 } 18 19 class SwiftApplicationDelegate: ApplicationDelegate { 20 var window = Window(title: "Swift/Win32 Window") 21 var button = Button(frame: .zero, title: "Press Me!") 22 var delegate = EventHandler() 23 24 func application(_: Application, 25 didFinishLaunchingWithOptions options: [Application.LaunchOptionsKey:Any]?) -> Bool { 26 window.addSubview(button) 27 window.delegate = delegate 28 return true 29 } 30 } 31 32 ApplicationMain(CommandLine.argc, CommandLine.unsafeArgv, nil, SwiftApplicationDelegate())

slide-85
SLIDE 85

Quickly Now

slide-86
SLIDE 86

Quickly Now

  • Immediate Feedback
slide-87
SLIDE 87

Quickly Now

  • Immediate Feedback
  • REPL
slide-88
SLIDE 88

Quickly Now

  • Immediate Feedback
  • REPL
  • Rapid Prototyping
slide-89
SLIDE 89

Future Work

slide-90
SLIDE 90

Future Work

  • Simplifications to SDK
slide-91
SLIDE 91

Future Work

  • Simplifications to SDK
  • Improved debugging
slide-92
SLIDE 92

Future Work

  • Simplifications to SDK
  • Improved debugging
  • Porting higher level frameworks (e.g. Swift-NIO, swift-log)
slide-93
SLIDE 93

Thanks

  • Ted Kremenek, Michael Gottesman
  • Jordan Rose
  • John McCall, Doug Gregor, Slava Pestov, Arnold Schwaighofer
  • Mike Ash, Andrew Trick
  • Davide Italiano, Jonas Devlieghere
  • Kim Topley, Pierre Habouzit
  • Lily Vulcano, Gwynne Raskind
  • Ankit Agarwal
  • Mishal Shah
  • The Swift community