Swift Compiler: What we can learn

Ali Pourhadi
Nov 4 · 4 min read

This article is about my journey in swift. My journey has begun with a simple question: How method dispatch works in swift?

I was trying to understand what will go into V-Table & what will go to Witness-Table. And there are many articles about method dispatch in swift and I am not gonna cover this subject. Instead, while I was trying to see what is the effect of each situation, I got into a different story which, I believe, could be very interesting to you too. Some will say why not reading swift documents and source code. I kind of agree, but that may take much more time for you, plus you don’t need to have C knowledge.


First of all, How swift compiler works:

Swift Compile ( before getting into LLVM )
  • Your swift file will turn to AST ( Abstract Syntax Tree )
  • The generated tree will turn to SIL ( Swift Intermediate Language )
  • Your intermediate language will turn to IR ( Intermediate Representation )

At this point, LLVM will take care of rest which generating assembly for the machine which is out of our scope. Also, there is an optimization that I did not include in the graph.

There are two different compiler tools that can use and they are almost identical. SwiftC compiles your code and will give you the result whereas Swift can also execute swift codes.

The beautiful part is that with SwiftC you can have a result of each step compile with using different options. If you see all the options that you have you can simply do :

> swiftc -h

So, let's begin with the first step which is transforming our swift code into AST.

AST is a tree representation of the abstract syntactic structure of source code. Each node in this tree denotes a construct. If it’s not clear yet, don’t worry, we will see a sample soon. Basically you can have AST with three different option:

  • dump-ast: Parse and type-check input file(s) and dump AST(s)
  • dump-parse: Parse input file(s) and dump AST(s)
  • print-ast: Parse and type-check input file(s) and pretty print AST(s)

I know when you do print it, it’s not like a real tree :)). But let’s have our fun. I have given these codes as an input :

It’s just two classes with a function that passed through inheritance. Let’s generate the tree and see the generated codes:

If you just read the result you will learn a lot about what is happening behind the scene. I just mention some of the interesting things:

“aFunction(param:)” interface type=’(AClass) -> (Int) -> ()’ access=internal
(parameter “self” interface type=’AClass’)
(parameter_list
(parameter “param” apiName=param interface type=’Int’)

Every time that you create a function swift will pass a parameter which is “self” that’s why we have access to other functions.

I also used a #function to print the function’s name. And the result of the transformation was very interesting:

magic_identifier_literal_expr type=’String’ location

If you do search for “magic_identifier_literal_expr” you will find it in “ParsePattern.cpp” in Swift GitHub repo, And you can see other options like line, file… And finally, the part that my journey has begun because of that: Inheritance

class_decl “BClass” interface type=’BClass.Type’ access=internal non-resilient inherits: AClass

That’s cool, but it didn’t give the information that I was looking for. So then I decided to see the next level which is generating SIL. If you check the help there are two different methods of generating SIL:

  • -emit-silgen: Emit raw SIL file(s)
  • -emit-sil: Emit canonical SIL file(s)

Raw SIL and Canonical SIL are almost identical. Technically some optimization will be applied on raw SIL and Canonical SIL will be generated.

Generated SIL codes

And finally, I found what I was looking for. I could see what exactly happening when I define a function as Final or what will happen in V-Table when I define add a function to class by extension. I highly recommend you try and see.

There is something interesting in SIL codes. They are comments in the code! which can be very useful to read. Beside Swift does name mangling. So if you want to read names you have to use Swift Demangler:

There is also an online version that you can use:

At this point, I had got what I needed but just to satisfy my curiosity I tried to generate IR Codes. This time we have only one option to generate LLVM Intermediate Representation:

  • -emit-ir: Emit LLVM IR file(s)
Generated IR file

As you see since we getting closer to LLVM it becomes less human-readable. From this point, LLVM takes responsibility and in the end, you will have your assembly.


That was my journey to learn how things works, hope you also enjoyed it!

Flawless iOS

🍏 Community around iOS development, mobile design, and marketing

Ali Pourhadi

Written by

iOS Developer / Montreal

Flawless iOS

🍏 Community around iOS development, mobile design, and marketing

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade