Top Banner
Swift Intermediate Language A high level IR to complement LLVM Joe Groand Chris Lattner
155

Swift Intermediate Language

Dec 31, 2016

Download

Documents

vukhanh
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Swift Intermediate Language

Swift Intermediate LanguageA high level IR to complement LLVM

Joe Groff and Chris Lattner

Page 2: Swift Intermediate Language

Why SIL?

Page 3: Swift Intermediate Language

Clang

Parse Sema CodeGen LLVM*.c AST AST' IR *.o

Page 4: Swift Intermediate Language

Clang

Parse Sema CodeGen LLVM*.c AST AST' IR *.o

Page 5: Swift Intermediate Language

Clang

Parse Sema CodeGen LLVM*.c AST AST' IR *.o

Page 6: Swift Intermediate Language

Clang

CodeGen

CodeGen

🌲🌲🌲🌲

🌲🌲🌲🌲🌲

Parse Sema LLVM*.c AST AST' IR *.o

Page 7: Swift Intermediate Language

Clang

CodeGen

CodeGen

🌲🌲🌲🌲

🌲🌲🌲🌲🌲

Parse Sema LLVM*.c AST AST' IR *.o

Page 8: Swift Intermediate Language

Clang-Wunreachable-code

-Wuninitialized

Static Analyzer

CodeGen

CodeGen

🌲🌲🌲🌲

🌲🌲🌲🌲🌲

Parse Sema LLVM*.c AST AST' IR *.o

Page 9: Swift Intermediate Language

Clang

Analysis CFG

-Wunreachable-code

-Wuninitialized

Static Analyzer

CodeGen

CodeGen

🌲🌲🌲🌲

🌲🌲🌲🌲🌲

Parse Sema LLVM*.c AST AST' IR *.o

Page 10: Swift Intermediate Language

Clang

Wide abstraction gap between source and LLVM IR IR isn't suitable for source-level analysis CFG lacks fidelity CFG is off the hot path Duplicated effort in CFG and IR lowering

Page 11: Swift Intermediate Language

Swift

Page 12: Swift Intermediate Language

Swift

Higher-level language

Page 13: Swift Intermediate Language

Swift

Higher-level language• Move more of the language into code

Page 14: Swift Intermediate Language

Swift

Higher-level language• Move more of the language into code• Protocol-based generics

Page 15: Swift Intermediate Language

Swift

Higher-level language• Move more of the language into code• Protocol-based genericsSafe language

Page 16: Swift Intermediate Language

Swift

Higher-level language• Move more of the language into code• Protocol-based genericsSafe language• Uninitialized vars, unreachable code should be compiler errors

Page 17: Swift Intermediate Language

Swift

Higher-level language• Move more of the language into code• Protocol-based genericsSafe language• Uninitialized vars, unreachable code should be compiler errors• Bounds and overflow checks

Page 18: Swift Intermediate Language

Parse SemaAST IRGen IR LLVM *.o

Swift

Page 19: Swift Intermediate Language

SILSILGenParse SemaAST IRGen IR LLVM *.o

Swift

Page 20: Swift Intermediate Language

SILSILGenParse SemaAST IRGen IR

Swift

Analysis

Page 21: Swift Intermediate Language

SIL

Fully represents program semantics Designed for both code generation and analysis Sits on the hot path of the compiler pipeline Bridges the abstraction gap between source and LLVM

Page 22: Swift Intermediate Language

Design of SIL

Page 23: Swift Intermediate Language

Fibonacci

func fibonacci(lim: Int) { var a = 0, b = 1 while b < lim { print(b) (a, b) = (b, a + b) } }

Page 24: Swift Intermediate Language

Fibonaccisil @fibonacci: $(Swift.Int) -> () { entry(%limi: $Swift.Int):

Page 25: Swift Intermediate Language

Fibonaccisil @fibonacci: $(Swift.Int) -> () { entry(%limi: $Swift.Int):

Page 26: Swift Intermediate Language

Fibonaccisil @fibonacci: $(Swift.Int) -> () { entry(%limi: $Swift.Int):

Page 27: Swift Intermediate Language

High-Level Type Systemsil @fibonacci: $(Swift.Int) -> () { entry(%limi: $Swift.Int):

Page 28: Swift Intermediate Language

High-Level Type Systemsil @fibonacci: $(Swift.Int) -> () { entry(%limi: $Swift.Int):

Page 29: Swift Intermediate Language

High-Level Type Systemsil @fibonacci: $(Swift.Int) -> () { entry(%limi: $Swift.Int):

Page 30: Swift Intermediate Language

High-Level Type System

Page 31: Swift Intermediate Language

High-Level Type System

Keeps machine-level type layout abstract

Page 32: Swift Intermediate Language

High-Level Type System

Keeps machine-level type layout abstractType-related information is implicit

Page 33: Swift Intermediate Language

High-Level Type System

Keeps machine-level type layout abstractType-related information is implicit• TBAA

Page 34: Swift Intermediate Language

High-Level Type System

Keeps machine-level type layout abstractType-related information is implicit• TBAA• Type parameters for generic specialization

Page 35: Swift Intermediate Language

High-Level Type System

Keeps machine-level type layout abstractType-related information is implicit• TBAA• Type parameters for generic specialization• Classes and protocol conformances for devirtualization

Page 36: Swift Intermediate Language

High-Level Type System

Keeps machine-level type layout abstractType-related information is implicit• TBAA• Type parameters for generic specialization• Classes and protocol conformances for devirtualizationStrongly-typed IR helps validate compiler correctness

Page 37: Swift Intermediate Language

Builtinssil @fibonacci: $(Swift.Int) -> () { entry(%limi: $Swift.Int):

Page 38: Swift Intermediate Language

Builtinssil @fibonacci: $(Swift.Int) -> () { entry(%limi: $Swift.Int): %lim = struct_extract %limi: $Swift.Int, #Int.value %print = function_ref @print: $(Swift.Int) -> () %a0 = integer_literal $Builtin.Int64, 0 %b0 = integer_literal $Builtin.Int64, 1

Page 39: Swift Intermediate Language

Builtinssil @fibonacci: $(Swift.Int) -> () { entry(%limi: $Swift.Int): %lim = struct_extract %limi: $Swift.Int, #Int.value %print = function_ref @print: $(Swift.Int) -> () %a0 = integer_literal $Builtin.Int64, 0 %b0 = integer_literal $Builtin.Int64, 1

%lt = builtin "icmp_lt_Int64"(%b: $Builtin.Int64, %lim: $Builtin.Int64): $Builtin.Int1 cond_br %lt: $Builtin.Int1, body, exit

Page 40: Swift Intermediate Language

Builtinssil @fibonacci: $(Swift.Int) -> () { entry(%limi: $Swift.Int): %lim = struct_extract %limi: $Swift.Int, #Int.value %print = function_ref @print: $(Swift.Int) -> () %a0 = integer_literal $Builtin.Int64, 0 %b0 = integer_literal $Builtin.Int64, 1

%lt = builtin "icmp_lt_Int64"(%b: $Builtin.Int64, %lim: $Builtin.Int64): $Builtin.Int1 cond_br %lt: $Builtin.Int1, body, exit

Page 41: Swift Intermediate Language

Builtinssil @fibonacci: $(Swift.Int) -> () { entry(%limi: $Swift.Int): %lim = struct_extract %limi: $Swift.Int, #Int.value %print = function_ref @print: $(Swift.Int) -> () %a0 = integer_literal $Builtin.Int64, 0 %b0 = integer_literal $Builtin.Int64, 1

%lt = builtin "icmp_lt_Int64"(%b: $Builtin.Int64, %lim: $Builtin.Int64): $Builtin.Int1 cond_br %lt: $Builtin.Int1, body, exit

Page 42: Swift Intermediate Language

Builtinssil @fibonacci: $(Swift.Int) -> () { entry(%limi: $Swift.Int): %lim = struct_extract %limi: $Swift.Int, #Int.value %print = function_ref @print: $(Swift.Int) -> () %a0 = integer_literal $Builtin.Int64, 0 %b0 = integer_literal $Builtin.Int64, 1

%lt = builtin "icmp_lt_Int64"(%b: $Builtin.Int64, %lim: $Builtin.Int64): $Builtin.Int1 cond_br %lt: $Builtin.Int1, body, exit

Page 43: Swift Intermediate Language

Builtinssil @fibonacci: $(Swift.Int) -> () { entry(%limi: $Swift.Int): %lim = struct_extract %limi: $Swift.Int, #Int.value %print = function_ref @print: $(Swift.Int) -> () %a0 = integer_literal $Builtin.Int64, 0 %b0 = integer_literal $Builtin.Int64, 1

%lt = builtin "icmp_lt_Int64"(%b: $Builtin.Int64, %lim: $Builtin.Int64): $Builtin.Int1 cond_br %lt: $Builtin.Int1, body, exit

Page 44: Swift Intermediate Language

Builtins

Page 45: Swift Intermediate Language

Builtins

Builtins opaquely represent types and operations of the layer below SIL

Page 46: Swift Intermediate Language

Builtins

Builtins opaquely represent types and operations of the layer below SILSwift's standard library implements user-level interfaces on top of builtins

Page 47: Swift Intermediate Language

Builtins

Builtins opaquely represent types and operations of the layer below SILSwift's standard library implements user-level interfaces on top of builtins

struct Int { var value: Builtin.Int64 } struct Bool { var value: Builtin.Int1 } func ==(lhs: Int, rhs: Int) -> Bool { return Bool(value: Builtin.icmp_eq_Word(lhs.value, rhs.value)) }

Page 48: Swift Intermediate Language

Builtins

Builtins opaquely represent types and operations of the layer below SILSwift's standard library implements user-level interfaces on top of builtins

struct Int { var value: Builtin.Int64 } struct Bool { var value: Builtin.Int1 } func ==(lhs: Int, rhs: Int) -> Bool { return Bool(value: Builtin.icmp_eq_Word(lhs.value, rhs.value)) }

SIL is intentionally ignorant of: • Machine-level type layout • Arithmetic, comparison, etc. machine-level operations

Page 49: Swift Intermediate Language

Literal Instructionssil @fibonacci: $(Swift.Int) -> () { entry(%limi: $Swift.Int): %lim = struct_extract %limi: $Swift.Int, #Int.value %print = function_ref @print: $(Swift.Int) -> () %a0 = integer_literal $Builtin.Word, 0 %b0 = integer_literal $Builtin.Word, 1

%lt = builtin "icmp_lt_Word"(%b: $Builtin.Word, %lim: $Builtin.Word): $Builtin.Int1 cond_br %lt: $Builtin.Int1, body, exit

Page 50: Swift Intermediate Language

Literal Instructionssil @fibonacci: $(Swift.Int) -> () { entry(%limi: $Swift.Int): %lim = struct_extract %limi: $Swift.Int, #Int.value %print = function_ref @print: $(Swift.Int) -> () %a0 = integer_literal $Builtin.Word, 0 %b0 = integer_literal $Builtin.Word, 1

%lt = builtin "icmp_lt_Word"(%b: $Builtin.Word, %lim: $Builtin.Word): $Builtin.Int1 cond_br %lt: $Builtin.Int1, body, exit

Page 51: Swift Intermediate Language

Literal Instructionssil @fibonacci: $(Swift.Int) -> () { entry(%limi: $Swift.Int): %lim = struct_extract %limi: $Swift.Int, #Int.value %print = function_ref @print: $(Swift.Int) -> () %a0 = integer_literal $Builtin.Word, 0 %b0 = integer_literal $Builtin.Word, 1

%lt = builtin "icmp_lt_Word"(%b: $Builtin.Word, %lim: $Builtin.Word): $Builtin.Int1 cond_br %lt: $Builtin.Int1, body, exit

Page 52: Swift Intermediate Language

Literal Instructions

Page 53: Swift Intermediate Language

Literal Instructions

More uniform IR representation

Page 54: Swift Intermediate Language

Literal Instructions

More uniform IR representation• No Value/Constant divide

Page 55: Swift Intermediate Language

Literal Instructions

More uniform IR representation• No Value/Constant divideAll instructions carry source location information for diagnostics

Page 56: Swift Intermediate Language

Literal Instructions

More uniform IR representation• No Value/Constant divideAll instructions carry source location information for diagnosticsEspecially important for numbers, which need to be statically checked for overflow

Page 57: Swift Intermediate Language

Phi Nodes?sil @fibonacci: $(Swift.Int) -> () { entry(%limi: $Swift.Int): %lim = struct_extract %limi: $Swift.Int, #Int.value %print = function_ref @print: $(Swift.Int) -> () %a0 = integer_literal $Builtin.Int64, 0 %b0 = integer_literal $Builtin.Int64, 1

%lt = builtin "icmp_lt_Int64"(%b: $Builtin.Int64, %lim: $Builtin.Int64): $Builtin.Int1 cond_br %lt: $Builtin.Int1, body, exit

Page 58: Swift Intermediate Language

Phi Nodes?sil @fibonacci: $(Swift.Int) -> () { entry(%limi: $Swift.Int): %lim = struct_extract %limi: $Swift.Int, #Int.value %print = function_ref @print: $(Swift.Int) -> () %a0 = integer_literal $Builtin.Int64, 0 %b0 = integer_literal $Builtin.Int64, 1

%lt = builtin "icmp_lt_Int64"(%b: $Builtin.Int64, %lim: $Builtin.Int64): $Builtin.Int1 cond_br %lt: $Builtin.Int1, body, exit

br loop(%a0: $Builtin.Int64, %b0: $Builtin.Int64)loop(%a: $Builtin.Int64, %b: $Builtin.Int64):

body: %b1 = struct $Swift.Int (%b: $Builtin.Int64) apply %print(%b1) : $(Swift.Int) -> () %c = builtin "add_Int64"(%a: $Builtin.Int64, %b: $Builtin.Int64): $Builtin.Int64 br loop(%b: $Builtin.Int64, %c: $Builtin.Int64)exit: %unit = tuple () return %unit: $()}

Page 59: Swift Intermediate Language

Phi Nodes?sil @fibonacci: $(Swift.Int) -> () { entry(%limi: $Swift.Int): %lim = struct_extract %limi: $Swift.Int, #Int.value %print = function_ref @print: $(Swift.Int) -> () %a0 = integer_literal $Builtin.Int64, 0 %b0 = integer_literal $Builtin.Int64, 1

%lt = builtin "icmp_lt_Int64"(%b: $Builtin.Int64, %lim: $Builtin.Int64): $Builtin.Int1 cond_br %lt: $Builtin.Int1, body, exit

br loop(%a0: $Builtin.Int64, %b0: $Builtin.Int64)loop(%a: $Builtin.Int64, %b: $Builtin.Int64):

body: %b1 = struct $Swift.Int (%b: $Builtin.Int64) apply %print(%b1) : $(Swift.Int) -> () %c = builtin "add_Int64"(%a: $Builtin.Int64, %b: $Builtin.Int64): $Builtin.Int64 br loop(%b: $Builtin.Int64, %c: $Builtin.Int64)exit: %unit = tuple () return %unit: $()}

Page 60: Swift Intermediate Language

Basic Block Argumentssil @fibonacci: $(Swift.Int) -> () { entry(%limi: $Swift.Int): %lim = struct_extract %limi: $Swift.Int, #Int.value %print = function_ref @print: $(Swift.Int) -> () %a0 = integer_literal $Builtin.Int64, 0 %b0 = integer_literal $Builtin.Int64, 1

%lt = builtin "icmp_lt_Int64"(%b: $Builtin.Int64, %lim: $Builtin.Int64): $Builtin.Int1 cond_br %lt: $Builtin.Int1, body, exit

br loop(%a0: $Builtin.Int64, %b0: $Builtin.Int64)loop(%a: $Builtin.Int64, %b: $Builtin.Int64):

body: %b1 = struct $Swift.Int (%b: $Builtin.Int64) apply %print(%b1) : $(Swift.Int) -> () %c = builtin "add_Int64"(%a: $Builtin.Int64, %b: $Builtin.Int64): $Builtin.Int64 br loop(%b: $Builtin.Int64, %c: $Builtin.Int64)exit: %unit = tuple () return %unit: $()}

Page 61: Swift Intermediate Language

Basic Block Argumentssil @fibonacci: $(Swift.Int) -> () { entry(%limi: $Swift.Int): %lim = struct_extract %limi: $Swift.Int, #Int.value %print = function_ref @print: $(Swift.Int) -> () %a0 = integer_literal $Builtin.Int64, 0 %b0 = integer_literal $Builtin.Int64, 1

%lt = builtin "icmp_lt_Int64"(%b: $Builtin.Int64, %lim: $Builtin.Int64): $Builtin.Int1 cond_br %lt: $Builtin.Int1, body, exit

br loop(%a0: $Builtin.Int64, %b0: $Builtin.Int64)loop(%a: $Builtin.Int64, %b: $Builtin.Int64):

body: %b1 = struct $Swift.Int (%b: $Builtin.Int64) apply %print(%b1) : $(Swift.Int) -> () %c = builtin "add_Int64"(%a: $Builtin.Int64, %b: $Builtin.Int64): $Builtin.Int64 br loop(%b: $Builtin.Int64, %c: $Builtin.Int64)exit: %unit = tuple () return %unit: $()}

Page 62: Swift Intermediate Language

Basic Block Arguments

Page 63: Swift Intermediate Language

Basic Block Arguments

More uniform IR representation

Page 64: Swift Intermediate Language

Basic Block Arguments

More uniform IR representation• Entry block is no longer a special case

Page 65: Swift Intermediate Language

Basic Block Arguments

More uniform IR representation• Entry block is no longer a special case• No special case code for managing phis

Page 66: Swift Intermediate Language

Basic Block Arguments

More uniform IR representation• Entry block is no longer a special case• No special case code for managing phisProvides natural notation for conditional defs

Page 67: Swift Intermediate Language

entry:

success

failure

Basic Block Arguments

More uniform IR representation• Entry block is no longer a special case• No special case code for managing phisProvides natural notation for conditional defs

%s =

/* can only use %s here */

%e = landingpad

:

:

invoke @mayThrowException(), label %success, label %failure

Page 68: Swift Intermediate Language

entry:

success

failure

Basic Block Arguments

More uniform IR representation• Entry block is no longer a special case• No special case code for managing phisProvides natural notation for conditional defs

/* can only use %s here */(%s):

(%e):

invoke @mayThrowException(), label %success, label %failure

Page 69: Swift Intermediate Language

Fibonaccisil @fibonacci: $(Swift.Int) -> () { entry(%limi: $Swift.Int): %lim = struct_extract %limi: $Swift.Int, #Int.value %print = function_ref @print: $(Swift.Int) -> () %a0 = integer_literal $Builtin.Int64, 0 %b0 = integer_literal $Builtin.Int64, 1 br loop(%a0: $Builtin.Int64, %b0: $Builtin.Int64) loop(%a: $Builtin.Int64, %b: $Builtin.Int64): %lt = builtin "icmp_lt_Int64"(%b: $Builtin.Int64, %lim: $Builtin.Int64): $Builtin.Int1 cond_br %lt: $Builtin.Int1, body, exit body: %b1 = struct $Swift.Int (%b: $Builtin.Int64) apply %print(%b1) : $(Swift.Int) -> () %c = builtin "add_Int64"(%a: $Builtin.Int64, %b: $Builtin.Int64): $Builtin.Int64 br loop(%b: $Builtin.Int64, %c: $Builtin.Int64) exit: %unit = tuple () return %unit: $() }

Page 70: Swift Intermediate Language

Method Lookup

entry(%c: $SomeClass): %foo = class_method %c: $SomeClass, #SomeClass.foo : $(SomeClass) -> () apply %foo(%c) : $(SomeClass) -> ()

Page 71: Swift Intermediate Language

Method Lookup

entry(%c: $SomeClass): %foo = class_method %c: $SomeClass, #SomeClass.foo : $(SomeClass) -> () apply %foo(%c) : $(SomeClass) -> ()

Page 72: Swift Intermediate Language

Method Lookup

entry(%c: $SomeClass): %foo = class_method %c: $SomeClass, #SomeClass.foo : $(SomeClass) -> () apply %foo(%c) : $(SomeClass) -> ()

sil_vtable SomeClass { #SomeClass.foo : @SomeClass_foo }

Page 73: Swift Intermediate Language

Method Lookup

entry(%c: $SomeClass): %foo = class_method %c: $SomeClass, #SomeClass.foo : $(SomeClass) -> () apply %foo(%c) : $(SomeClass) -> ()

sil_vtable SomeClass { #SomeClass.foo : @SomeClass_foo }

sil @SomeClass_foo : $(SomeClass) -> ()

Page 74: Swift Intermediate Language

Method Lookup

entry(%c: $SomeClass): %foo = function_ref @SomeClass_foo : $(SomeClass) -> () apply %foo(%c) : $(SomeClass) -> ()

Page 75: Swift Intermediate Language

Method Lookup

entry(%x: $T, %y: $T): %plus = witness_method $T, #Addable.+ : $<U: Addable> (U, U) -> U %z = apply %plus<T>(%x, %y) : $<U: Addable> (U, U) -> U

Page 76: Swift Intermediate Language

Method Lookup

entry(%x: $T, %y: $T): %plus = witness_method $T, #Addable.+ : $<U: Addable> (U, U) -> U %z = apply %plus<T>(%x, %y) : $<U: Addable> (U, U) -> U

Page 77: Swift Intermediate Language

Method Lookup

entry(%x: $T, %y: $T): %plus = witness_method $T, #Addable.+ : $<U: Addable> (U, U) -> U %z = apply %plus<T>(%x, %y) : $<U: Addable> (U, U) -> U

sil_witness_table Int: Addable { #Addable.+ : @Int_plus }

Page 78: Swift Intermediate Language

Method Lookup

entry(%x: $T, %y: $T): %plus = witness_method $T, #Addable.+ : $<U: Addable> (U, U) -> U %z = apply %plus<T>(%x, %y) : $<U: Addable> (U, U) -> U

sil_witness_table Int: Addable { #Addable.+ : @Int_plus }

sil @Int_plus : $(Int, Int) -> Int

Page 79: Swift Intermediate Language

Method Lookup

entry(%x: $Int, %y: $Int): %plus = function_ref @Int_plus : $(Int, Int) -> Int %z = apply %plus(%x, %y) : $(Int, Int) -> Int

Page 80: Swift Intermediate Language

Memory Allocation

Page 81: Swift Intermediate Language

Memory Allocation

%stack = alloc_stack $Int

Page 82: Swift Intermediate Language

Memory Allocation

%stack = alloc_stack $Int store %x to %stack: $*Int %y = load %stack: $*Int

Page 83: Swift Intermediate Language

Memory Allocation

%stack = alloc_stack $Int store %x to %stack: $*Int %y = load %stack: $*Int dealloc_stack %stack: $*Int

Page 84: Swift Intermediate Language

Memory Allocation

%stack = alloc_stack $Int store %x to %stack: $*Int %y = load %stack: $*Int dealloc_stack %stack: $*Int

%box = alloc_box $Int

Page 85: Swift Intermediate Language

Memory Allocation

%stack = alloc_stack $Int store %x to %stack: $*Int %y = load %stack: $*Int dealloc_stack %stack: $*Int

%box = alloc_box $Int

%object = alloc_ref $SomeClass

Page 86: Swift Intermediate Language

Memory Allocation

%stack = alloc_stack $Int store %x to %stack: $*Int %y = load %stack: $*Int dealloc_stack %stack: $*Int

%box = alloc_box $Int

%object = alloc_ref $SomeClass

strong_retain %object : $SomeClass strong_release %object : $SomeClass

Page 87: Swift Intermediate Language

Control Flow

Page 88: Swift Intermediate Language

Control Flow

br loop cond_br %flag: $Builtin.Int1, yes, no return %x: $Int unreachable

Page 89: Swift Intermediate Language

Control Flow

br loop cond_br %flag: $Builtin.Int1, yes, no return %x: $Int unreachable

switch_enum %e: $Optional<Int>, case #Optional.Some: some, case #Optional.None: nonesome(%x: $Int):

Page 90: Swift Intermediate Language

Control Flow

br loop cond_br %flag: $Builtin.Int1, yes, no return %x: $Int unreachable

switch_enum %e: $Optional<Int>, case #Optional.Some: some, case #Optional.None: nonesome(%x: $Int):

checked_cast_br %c: $BaseClass, $DerivedClass, success, failuresuccess(%d: $DerivedClass):

Page 91: Swift Intermediate Language

Program Failure

Page 92: Swift Intermediate Language

Program Failure

%result = builtin "sadd_with_overflow_Int64" (%x : $Builtin.Int64, %y : $Builtin.Int64) : $(Builtin.Int64, Builtin.Int1) %overflow = tuple_extract %result, 1

Page 93: Swift Intermediate Language

Program Failure

%result = builtin "sadd_with_overflow_Int64" (%x : $Builtin.Int64, %y : $Builtin.Int64) : $(Builtin.Int64, Builtin.Int1) %overflow = tuple_extract %result, 1 cond_br %overflow : $Builtin.Int1, fail, contcont: %z = tuple_extract %result, 0

/* ... */

fail: builtin "int_trap"() unreachable

Page 94: Swift Intermediate Language

Program Failure

%result = builtin "sadd_with_overflow_Int64" (%x : $Builtin.Int64, %y : $Builtin.Int64) : $(Builtin.Int64, Builtin.Int1) %overflow = tuple_extract %result, 1

%z = tuple_extract %result, 0

Page 95: Swift Intermediate Language

Program Failure

%result = builtin "sadd_with_overflow_Int64" (%x : $Builtin.Int64, %y : $Builtin.Int64) : $(Builtin.Int64, Builtin.Int1) %overflow = tuple_extract %result, 1

%z = tuple_extract %result, 0 cond_fail %overflow : $Builtin.Int1

Page 96: Swift Intermediate Language

Swift’s use of SIL

Page 97: Swift Intermediate Language

Two Phases of SIL Passes

Early SIL: Data flow sensitive lowering SSA-based diagnostics “Guaranteed” optimizations

Late SIL: Performance optimizations Serialization LLVM IRGen

AST IRGen LLVMIR *.oSIL AnalysisSILGen Optimization

Page 98: Swift Intermediate Language

Early SILMany individual passes:

Page 99: Swift Intermediate Language

Early SIL

Mandatory inlining

Capture promotion

Box-to-stack promotion inout argument deshadowing

Diagnose unreachable code

Definitive initialization

Guaranteed memory optimizations

Constant folding / overflow diagnostics

Many individual passes:

Page 100: Swift Intermediate Language

Early SIL

Mandatory inlining

Capture promotion

Box-to-stack promotion inout argument deshadowing

Diagnose unreachable code

Definitive initialization

Guaranteed memory optimizations

Constant folding / overflow diagnostics

Problems we’ll look at: - Diagnosing Overflow - Enabling natural closure semantics with memory safety - Removing requirement for default construction

Many individual passes:

Page 101: Swift Intermediate Language

Diagnosing Overflow

Arithmetic overflow is guaranteed to trap in Swift Not undefined behavior Not 2’s complement (unless explicitly using &+ operator)

let v = Int8(127)+1

Page 102: Swift Intermediate Language

Diagnosing Overflow

Arithmetic overflow is guaranteed to trap in Swift Not undefined behavior Not 2’s complement (unless explicitly using &+ operator)

How can we statically diagnose overflow? … and produce a useful error message?

let v = Int8(127)+1

Page 103: Swift Intermediate Language

Output of SILGenlet v = Int8(127)+1

%1 = integer_literal $Builtin.Int2048, 127

%2 = function_ref @“Swift.Int8.init" : $(Builtin.Int2048) -> Int8

%4 = apply [transparent] %2(%1) : $(Builtin.Int2048) -> Int8

%9 = function_ref @“Swift.+” : $(Int8, Int8) -> Int8

%10 = apply [transparent] %0(%4, %8) : $(Int8, Int8) -> Int8

debug_value %10 : $Int8 // let v

%5 = integer_literal $Builtin.Int2048, 1

%6 = function_ref @“Swift.Int8.init" : $(Builtin.Int2048) -> Int8

%8 = apply [transparent] %6(%5) : $(Builtin.Int2048) -> Int8

Page 104: Swift Intermediate Language

Output of SILGenlet v = Int8(127)+1

%1 = integer_literal $Builtin.Int2048, 127

%2 = function_ref @“Swift.Int8.init" : $(Builtin.Int2048) -> Int8

%4 = apply [transparent] %2(%1) : $(Builtin.Int2048) -> Int8

%9 = function_ref @“Swift.+” : $(Int8, Int8) -> Int8

%10 = apply [transparent] %0(%4, %8) : $(Int8, Int8) -> Int8

debug_value %10 : $Int8 // let v

%5 = integer_literal $Builtin.Int2048, 1

%6 = function_ref @“Swift.Int8.init" : $(Builtin.Int2048) -> Int8

%8 = apply [transparent] %6(%5) : $(Builtin.Int2048) -> Int8

Page 105: Swift Intermediate Language

Output of SILGenlet v = Int8(127)+1

%1 = integer_literal $Builtin.Int2048, 127

%2 = function_ref @“Swift.Int8.init" : $(Builtin.Int2048) -> Int8

%4 = apply [transparent] %2(%1) : $(Builtin.Int2048) -> Int8

%9 = function_ref @“Swift.+” : $(Int8, Int8) -> Int8

%10 = apply [transparent] %0(%4, %8) : $(Int8, Int8) -> Int8

debug_value %10 : $Int8 // let v

%5 = integer_literal $Builtin.Int2048, 1

%6 = function_ref @“Swift.Int8.init" : $(Builtin.Int2048) -> Int8

%8 = apply [transparent] %6(%5) : $(Builtin.Int2048) -> Int8

Page 106: Swift Intermediate Language

Output of SILGenlet v = Int8(127)+1

%1 = integer_literal $Builtin.Int2048, 127

%2 = function_ref @“Swift.Int8.init" : $(Builtin.Int2048) -> Int8

%4 = apply [transparent] %2(%1) : $(Builtin.Int2048) -> Int8

%9 = function_ref @“Swift.+” : $(Int8, Int8) -> Int8

%10 = apply [transparent] %0(%4, %8) : $(Int8, Int8) -> Int8

debug_value %10 : $Int8 // let v

%5 = integer_literal $Builtin.Int2048, 1

%6 = function_ref @“Swift.Int8.init" : $(Builtin.Int2048) -> Int8

%8 = apply [transparent] %6(%5) : $(Builtin.Int2048) -> Int8

Page 107: Swift Intermediate Language

After mandatory inlininglet v = Int8(127)+1

Page 108: Swift Intermediate Language

After mandatory inlining

%0 = integer_literal $Builtin.Int8, 127

%4 = integer_literal $Builtin.Int8, 1

%11 = builtin "sadd_with_overflow_Int8"(%0 : $Builtin.Int8, %4 : $Builtin.Int8)

%12 = tuple_extract %11 : $(Builtin.Int8, Builtin.Int1), 1

cond_fail %12 : $Builtin.Int1

%13 = tuple_extract %11 : $(Builtin.Int8, Builtin.Int1), 0

%15 = struct $Int8 (%13 : $Builtin.Int8)

debug_value %15 : $Int8 // let v

let v = Int8(127)+1

Page 109: Swift Intermediate Language

After mandatory inlining

%0 = integer_literal $Builtin.Int8, 127

%4 = integer_literal $Builtin.Int8, 1

%11 = builtin "sadd_with_overflow_Int8"(%0 : $Builtin.Int8, %4 : $Builtin.Int8)

%12 = tuple_extract %11 : $(Builtin.Int8, Builtin.Int1), 1

cond_fail %12 : $Builtin.Int1

%13 = tuple_extract %11 : $(Builtin.Int8, Builtin.Int1), 0

%15 = struct $Int8 (%13 : $Builtin.Int8)

debug_value %15 : $Int8 // let v

let v = Int8(127)+1

Page 110: Swift Intermediate Language

After mandatory inlining

%0 = integer_literal $Builtin.Int8, 127

%4 = integer_literal $Builtin.Int8, 1

%11 = builtin "sadd_with_overflow_Int8"(%0 : $Builtin.Int8, %4 : $Builtin.Int8)

%12 = tuple_extract %11 : $(Builtin.Int8, Builtin.Int1), 1

cond_fail %12 : $Builtin.Int1

%13 = tuple_extract %11 : $(Builtin.Int8, Builtin.Int1), 0

%15 = struct $Int8 (%13 : $Builtin.Int8)

debug_value %15 : $Int8 // let v

let v = Int8(127)+1

Page 111: Swift Intermediate Language

Diagnostic constant folding

%0 = integer_literal $Builtin.Int8, -128

%1 = integer_literal $Builtin.Int1, -1

%2 = tuple (%0 : $Builtin.Int8, %1 : $Builtin.Int1) // folded “sadd_overflow”

cond_fail %1 : $Builtin.Int1 // unconditional failure

let v = Int8(127)+1

Page 112: Swift Intermediate Language

Diagnostic constant folding

%0 = integer_literal $Builtin.Int8, -128

%1 = integer_literal $Builtin.Int1, -1

%2 = tuple (%0 : $Builtin.Int8, %1 : $Builtin.Int1) // folded “sadd_overflow”

cond_fail %1 : $Builtin.Int1 // unconditional failure

let v = Int8(127)+1

Page 113: Swift Intermediate Language

Diagnostic constant folding

Each SIL instruction maintains full location information: - Pointer back to AST node it came from - Including SIL inlining information

t.swift:2:20: error: arithmetic operation '127 + 1' (on type 'Int8') results in an overflow let v = Int8(127) + 1 ~~~~~~~~~ ^ ~

%0 = integer_literal $Builtin.Int8, -128

%1 = integer_literal $Builtin.Int1, -1

%2 = tuple (%0 : $Builtin.Int8, %1 : $Builtin.Int1) // folded “sadd_overflow”

cond_fail %1 : $Builtin.Int1 // unconditional failure

let v = Int8(127)+1

Page 114: Swift Intermediate Language

Local variable optimizationMemory safety with closures provides challenges: - Closures can capture references to local variables - Closure lifetime is not limited to a stack discipline

func doSomething() -> Int {

var x = 1

takeClosure { x = 2 }

return x

}

Page 115: Swift Intermediate Language

Local variable optimizationMemory safety with closures provides challenges: - Closures can capture references to local variables - Closure lifetime is not limited to a stack discipline

func doSomething() -> Int {

var x = 1

takeClosure { x = 2 }

return x

}

Solution: Semantic model is for all stack variables to be on the heap

Code

x

Closure

Page 116: Swift Intermediate Language

Local variables after SILGenSILGen emits all local ‘var’iables as heap boxes with alloc_box

func f() -> Int {

var x = 42

return x

}

Page 117: Swift Intermediate Language

Local variables after SILGenSILGen emits all local ‘var’iables as heap boxes with alloc_box

func f() -> Int {

var x = 42

return x

}

%0 = alloc_box $Int // var x

%4 = ...

store %4 to %0#1 : $*Int

Page 118: Swift Intermediate Language

Local variables after SILGenSILGen emits all local ‘var’iables as heap boxes with alloc_box

func f() -> Int {

var x = 42

return x

}

%0 = alloc_box $Int // var x

%4 = ...

store %4 to %0#1 : $*Int

%6 = load %0#1 : $*Int

strong_release %0#0

return %6 : $Int

Page 119: Swift Intermediate Language

Local variables after SILGenSILGen emits all local ‘var’iables as heap boxes with alloc_box

func f() -> Int {

var x = 42

return x

}

%0 = alloc_box $Int // var x

%4 = ...

store %4 to %0#1 : $*Int

Box-to-stack promotes heap boxes to stack allocations All closure captures are by reference

- Not acceptable to leave them on the heap!

%6 = load %0#1 : $*Int

strong_release %0#0

return %6 : $Int

Page 120: Swift Intermediate Language

Promotion eliminates byref captureSafe to promote to by-value capture in many cases: … e.g. when no mutations happen after closure formation This enables the captured value to be promoted to the stack/registers

Page 121: Swift Intermediate Language

Promotion eliminates byref captureSafe to promote to by-value capture in many cases: … e.g. when no mutations happen after closure formation This enables the captured value to be promoted to the stack/registers

var x = …

x += 42

arr1 = arr2.map { elt in elt+x }

Page 122: Swift Intermediate Language

Promotion eliminates byref captureSafe to promote to by-value capture in many cases: … e.g. when no mutations happen after closure formation This enables the captured value to be promoted to the stack/registers

var x = …

x += 42

arr1 = arr2.map { elt in elt+x }

var x = …

x += 42

let x2 = x

arr1 = arr2.map { elt in elt+x2 }

Page 123: Swift Intermediate Language

Naive SIL for by-ref closure capturearr = arr.map { elt in elt+x }

Page 124: Swift Intermediate Language

Naive SIL for by-ref closure capture

%2 = alloc_box $Int // var x

arr = arr.map { elt in elt+x }

Page 125: Swift Intermediate Language

Naive SIL for by-ref closure capture

%2 = alloc_box $Int // var x

arr = arr.map { elt in elt+x }

%11 = function_ref @“Array.map” : $((Int) -> Int, Array) -> Array

%12 = apply %11(%10, %0) : $((Int) -> Int, Array) -> Array

Page 126: Swift Intermediate Language

%9 = function_ref @“closure1” : $(Int, @owned Builtin.NativeObject, @inout Int) -> Int

%10 = partial_apply %9(%2#0, %2#1) : $(Int, @owned Builtin.NativeObject, @inout Int) -> Int

Naive SIL for by-ref closure capture

%2 = alloc_box $Int // var x

arr = arr.map { elt in elt+x }

%11 = function_ref @“Array.map” : $((Int) -> Int, Array) -> Array

%12 = apply %11(%10, %0) : $((Int) -> Int, Array) -> Array

Page 127: Swift Intermediate Language

%9 = function_ref @“closure1” : $(Int, @owned Builtin.NativeObject, @inout Int) -> Int

%10 = partial_apply %9(%2#0, %2#1) : $(Int, @owned Builtin.NativeObject, @inout Int) -> Int

Naive SIL for by-ref closure capture

%2 = alloc_box $Int // var x

arr = arr.map { elt in elt+x }

%11 = function_ref @“Array.map” : $((Int) -> Int, Array) -> Array

%12 = apply %11(%10, %0) : $((Int) -> Int, Array) -> Array

Page 128: Swift Intermediate Language

%9 = function_ref @“closure1” : $(Int, @owned Builtin.NativeObject, @inout Int) -> Int

%10 = partial_apply %9(%2#0, %2#1) : $(Int, @owned Builtin.NativeObject, @inout Int) -> Int

Naive SIL for by-ref closure capture

%2 = alloc_box $Int // var x

arr = arr.map { elt in elt+x }

sil @“closure1” {

bb0(%0 : $Int, %1 : $Builtin.NativeObject, %2 : $*Int):

debug_value %0 : $Int // let elt

%4 = load %2 : $*Int

...

%11 = function_ref @“Array.map” : $((Int) -> Int, Array) -> Array

%12 = apply %11(%10, %0) : $((Int) -> Int, Array) -> Array

Page 129: Swift Intermediate Language

%9 = function_ref @“closure1” : $(Int, @owned Builtin.NativeObject, @inout Int) -> Int

%10 = partial_apply %9(%2#0, %2#1) : $(Int, @owned Builtin.NativeObject, @inout Int) -> Int

Naive SIL for by-ref closure capture

%2 = alloc_box $Int // var x

arr = arr.map { elt in elt+x }

sil @“closure1” {

bb0(%0 : $Int, %1 : $Builtin.NativeObject, %2 : $*Int):

debug_value %0 : $Int // let elt

%4 = load %2 : $*Int

...

%11 = function_ref @“Array.map” : $((Int) -> Int, Array) -> Array

%12 = apply %11(%10, %0) : $((Int) -> Int, Array) -> Array

Page 130: Swift Intermediate Language

%9 = function_ref @“closure1” : $(Int, @owned Builtin.NativeObject, @inout Int) -> Int

%10 = partial_apply %9(%2#0, %2#1) : $(Int, @owned Builtin.NativeObject, @inout Int) -> Int

Naive SIL for by-ref closure capture

%2 = alloc_box $Int // var x

arr = arr.map { elt in elt+x }

sil @“closure1” {

bb0(%0 : $Int, %1 : $Builtin.NativeObject, %2 : $*Int):

debug_value %0 : $Int // let elt

%4 = load %2 : $*Int

...

%11 = function_ref @“Array.map” : $((Int) -> Int, Array) -> Array

%12 = apply %11(%10, %0) : $((Int) -> Int, Array) -> Array

Page 131: Swift Intermediate Language

SIL after capture promotion

%2 = alloc_box $Int // var x

%11 = function_ref @“Array.map” : $((Int) -> Int, Array) -> Array

%12 = apply %11(%10, %0) : $((Int) -> Int, Array) -> Array

arr = arr.map { elt in elt+x }

Page 132: Swift Intermediate Language

SIL after capture promotion

%4 = load %2#1 : $*Int

%7 = function_ref @“closure1” : $(Int, Int) -> Int

%10 = partial_apply %7(%4) : $(Int, Int) -> Int

%2 = alloc_box $Int // var x

%11 = function_ref @“Array.map” : $((Int) -> Int, Array) -> Array

%12 = apply %11(%10, %0) : $((Int) -> Int, Array) -> Array

arr = arr.map { elt in elt+x }

Page 133: Swift Intermediate Language

SIL after capture promotion

%4 = load %2#1 : $*Int

%7 = function_ref @“closure1” : $(Int, Int) -> Int

%10 = partial_apply %7(%4) : $(Int, Int) -> Int

%2 = alloc_box $Int // var x

%11 = function_ref @“Array.map” : $((Int) -> Int, Array) -> Array

%12 = apply %11(%10, %0) : $((Int) -> Int, Array) -> Array

arr = arr.map { elt in elt+x }

Page 134: Swift Intermediate Language

SIL after capture promotion

%4 = load %2#1 : $*Int

%7 = function_ref @“closure1” : $(Int, Int) -> Int

%10 = partial_apply %7(%4) : $(Int, Int) -> Int

%2 = alloc_box $Int // var x

%11 = function_ref @“Array.map” : $((Int) -> Int, Array) -> Array

%12 = apply %11(%10, %0) : $((Int) -> Int, Array) -> Array

sil @“closure1” {

bb0(%0 : $Int, %1 : $Int):

debug_value %0 : $Int // let elt

debug_value %1 : $Int // var x

...

arr = arr.map { elt in elt+x }

Page 135: Swift Intermediate Language

Definitive InitializationProblem: Not all values can be default initialized

func testDI(cond : Bool) {

var v : SomeClass

if cond {

v = SomeClass(1234)

}

v.foo()

}

else {

v = SomeClass(4321)

}

Page 136: Swift Intermediate Language

Definitive InitializationProblem: Not all values can be default initialized

func testDI(cond : Bool) {

var v : SomeClass

if cond {

v = SomeClass(1234)

}

v.foo()

}

else {

v = SomeClass(4321)

}

Desires: Don’t want magic numbers for primitive types Want to allow flexible initialization patterns

Page 137: Swift Intermediate Language

Definitive InitializationProblem: Not all values can be default initialized

func testDI(cond : Bool) {

var v : SomeClass

if cond {

v = SomeClass(1234)

}

v.foo()

}

else {

v = SomeClass(4321)

} Solution: Dataflow driven liveness analysis

Desires: Don’t want magic numbers for primitive types Want to allow flexible initialization patterns

Page 138: Swift Intermediate Language

Definitive InitializationProblem: Not all values can be default initialized

func testDI(cond : Bool) {

var v : SomeClass

if cond {

v = SomeClass(1234)

}

v.foo()

}

error: 'v' used before being initialized

Solution: Dataflow driven liveness analysis

Desires: Don’t want magic numbers for primitive types Want to allow flexible initialization patterns

Page 139: Swift Intermediate Language

Definitive Initialization AlgorithmCheck each use of value to determine: Guaranteed initialized Guaranteed uninitialized Initialized only on some paths struct Pair {

var a, b : Int

init() {

a = 42

}

}

Page 140: Swift Intermediate Language

Definitive Initialization AlgorithmCheck each use of value to determine: Guaranteed initialized Guaranteed uninitialized Initialized only on some paths struct Pair {

var a, b : Int

init() {

a = 42

}

}

error: return from initializer without initializing all stored properties

note: 'self.b' not initializedDiagnostics must be great

Page 141: Swift Intermediate Language

DI covers many similar cases

func test() -> Float {

var local : (Int, Float)

local.0 = 42

return local.1

}

class Base {

init(x : Int) {}

}

class Derived : Base {

var x, y : Int

init() {

x = 42; y = 1

}

}

Page 142: Swift Intermediate Language

DI covers many similar cases

func test() -> Float {

var local : (Int, Float)

local.0 = 42

return local.1

}

class Base {

init(x : Int) {}

}

class Derived : Base {

var x, y : Int

init() {

x = 42; y = 1

}

}

error: 'local.1' used before being initialized

error: super.init isn't called before returning from initializer

Page 143: Swift Intermediate Language

DI Lowering: Initialization vs Assignment

Semantics depend on data flow properties

First assignment is initialization: - Raw memory → Valid value

Subsequent assignments are replacements: - Valid value → Valid value

x = y

Page 144: Swift Intermediate Language

DI Lowering: Initialization vs Assignment

Semantics depend on data flow properties

First assignment is initialization: - Raw memory → Valid value

Subsequent assignments are replacements: - Valid value → Valid value

x = y

strong_retain %y : $C

store %y to %x : $*C

Page 145: Swift Intermediate Language

DI Lowering: Initialization vs Assignment

Semantics depend on data flow properties

First assignment is initialization: - Raw memory → Valid value

Subsequent assignments are replacements: - Valid value → Valid value

x = y

strong_retain %y : $C

store %y to %x : $*C

strong_retain %y : $C

%tmp = load %x : $*C

strong_release %tmp : $C

store %y to %x : $*C

Page 146: Swift Intermediate Language

Conditional LivenessInherently a dataflow problem

Requires dynamic logic in some cases

Conditional destruction too

func testDI(cond : Bool) {

var c : SomeClass

c = SomeClass(4321) // init or assign?

c.foo()

}

Page 147: Swift Intermediate Language

Conditional LivenessInherently a dataflow problem

Requires dynamic logic in some cases

Conditional destruction too

func testDI(cond : Bool) {

var c : SomeClass

c = SomeClass(4321) // init or assign?

c.foo()

}

if cond {

c = SomeClass(1234)

}

Page 148: Swift Intermediate Language

Language-Specific IR: Retrospective

Page 149: Swift Intermediate Language

DiagnosticsClear improvement over Clang CFG for data flow diagnostics:

- Diagnostics always up to date as language evolves - Great location information, source level type information - DCE before diagnostics eliminates “false” positives

IMHO Clang should pull clang::CFG (or something better) into its IRGen path

Page 150: Swift Intermediate Language

LoweringNice separation between SILGen and IRGen:

- SILGen handles operational lowering - IRGen handles type lowering & concretization of the target

Page 151: Swift Intermediate Language

LoweringNice separation between SILGen and IRGen:

- SILGen handles operational lowering - IRGen handles type lowering & concretization of the target

Dataflow Lowering: - Great way to handle things like swift assignment vs initialization - Can be emulated by generating LLVM intrinsics and lowering on IR

Page 152: Swift Intermediate Language

Performance OptimizationsNecessary for generics specialization:

Requires full source level type system Specialization produces extreme changes to generated IR

Page 153: Swift Intermediate Language

Performance OptimizationsNecessary for generics specialization:

Requires full source level type system Specialization produces extreme changes to generated IR

Less clear for other optimizations ARC Optimization, devirt, etc could all be done on IR (with tradeoffs)

Page 154: Swift Intermediate Language

Performance OptimizationsNecessary for generics specialization:

Requires full source level type system Specialization produces extreme changes to generated IR

Required a ton of infrastructure: SILCombine Passmanager for analyses …

Less clear for other optimizations ARC Optimization, devirt, etc could all be done on IR (with tradeoffs)

Page 155: Swift Intermediate Language

SummarySIL was a lot of work, but necessary given the scope of Swift

May make sense (or not) based on your language

We’re pretty happy with it… …but there is still a ton of work left to do

Know LLVM and use it for what it is good for … don’t reinvent everything just for fun :-)