“This is the 11th day of my participation in the First Challenge 2022. For details: First Challenge 2022”

IOS development language, no matter OC or Swift, is compiled through LLVM, and finally generates.o files. The compilation process is shown as follows:

  • OCthroughclangCompiler, compiles toIR, and then regenerate it into an executable file.o(our machine code);
  • SwiftbySwiftCompiler generationIR, and then regenerate it into an executable file.o;

Swift Compilation process

Swift language compilation flow chart is as follows:

  • SwiftThe code through-dump-parseCommand toSyntax analysisTo generate theAbstract syntax tree AST;
  • Abstract syntax treethrough-dump-astforSemantic analysis(such as type checking for correctness and security);
  • Semantic analysisAfter that,SwiftThe code will be degraded toSIL, that is,Swift intermediate language(Swift Intermediate Language);
  • SILDivided intoRaw SIL(native, no optimization option enabled) andSILOpt Canonical SILOptimized;
  • Finally throughLLVMDowngraded toIR, and then compiled into different architectures through the following codeMachine code;

The commands involved are as follows:

Swiftc main. Swif-dump-parse // Analyze and check the type of output AST swiftc main. Swif-dump-ast // generate intermediate language (SIL), Swiftc main. Swift - EMIT - Silgen // Generate intermediate Language (SIL) Swift-emit -sil // Generates LLVM intermediate language (.ll file) swifTC main. Swift-emit -ir // generates LLVM intermediate language (.bc file) swifTC Swift -emit-assembly // Compilation generates executable. Out file swiftc-o main.o main.swiftCopy the code

In the OC, there is a security check for our code with SIL, as opposed to SIL.

The int8_t type we defined will fail because int8_t is only one byte and its result has been overflowed, giving an incorrect result in OC; Let’s look at what happens in Siwft:

As we have seen, the same code in Swift will report an error at compile time, avoiding unpredictable errors later; This is all because of SIL;

SIL file analysis

So what are the syntax rules for SIL? Let’s generate a SIL file for analysis; Let’s start with a simple code:

Next we generate this code into a SIL file:

For viewing purposes, we can also output sil files as main.sil files using the swiftc main.swift-emma-sil >./main.sil command.

Due to the large SIL file, we select only the important parts for analysis:

The Teacher’s statement

First, let’s take a look at Teacher’s declaration in the SIL phase:

As you can see from the Teacher definition, there are initialized storage attributes age and name, a deinit function identified as @objc, and the default initializer init();

The main function

  • @main: identifies the entry function inSILIn the@As aidentifier;
  • % 0% 9: it’s written in the bookSILIs also known asregister, can be understood as a constant during the development process. Once assigned, it cannot be changed again. It’s important to note that hereregisterisVirtual registerWhen running on a specific device, the real one will be usedregister;
  • alloc_global @$s4main1tAA7TeacherCvp: Assigns a global variable whose name isMix writingYes, we can use terminal commandsxcrun swift-demangleRestore it:

You can see that this corresponds to the t variable in main and Teacher in main.

  • %3 = global_addr @$s4main1tAA7TeacherCvp: to get theThe global variableThe address to% 3;
  • %4 = metatype $@thick Teacher.Type: getTeacher.TypeThe metatype of% 4;
  • According to the notes% 5isTeacher.__allocating_init()A reference to a function, that is, its pointer address;
  • %6 = apply %5(%4)Will:% % 5 (4)The result of theta is thetaTeacherTo the instance variable of% 6;
  • store %6 to %3Will:% 6Which is the memory address of the instance variable% 3In this global variable;
  • inSwiftIn the bottomIntIs aStructType,% 8and% 9It’s building aInt32Integer type of0Similar to usOCIn themainFunction finalreturn 0;

For SIL syntax rules, see the official SIL documentation

__allocating_init () function

In the SIL code above, s4main7TeacherCACycfC (teach.__allocating_init ()) is called to create the current instance object. We locate this function in SIL:

  • The function needs oneTeacher.Type, which we can understand asisa;
  • %1 = alloc_ref $Teacher:alloc_refWill create aTeacherOf the instance variable whose reference count is initialized to1;alloc_refIn fact, that is to goThe heap areaApply for memory space; If identified asobjctheSwift classWill useObjective-Cthe+allocWithZone:Initialization method; How do you test that? We add the following breakpoint to our code:

Run the program to view the assembly instruction:

We see that the teach.__allocating_init () function will be called, so how is this function implemented? Breakpoint to enter the function:

Swift_allocObject and teacher.init () are mainly called in __allocating_init();

  • swift_allocObjectFind the appropriate memory space in the heap to initialize;
  • Teacher.init()Initialize a member variable;

This is a pure Swift class, so what happens if we inherit Teacher from NSObject?

We repeat the assembly debugging steps above and enter the teach.__allocating_init () function:

We see that the initialization function becomes objc_allocWithZone and objc_msgSend;

  • objc_allocWithZonecallmallocFunction to request memory space;
  • objc_msgSendsendinitMessage;

swift_allocObject

Swift_allocObject is called during initialization of the Swift class. What does this function do? We need to use Swift source code for analysis; Find the stdlib->public-> Runtime -> heapObject. CPP file in this directory, which is related to our Swift class initialization; In this file locate the _swift_allocObject_ function, which is a private function called by swift::swift_allocObject:

_swift_allocObject_ is implemented as follows:

This function takes three arguments:

  • HdapMetadata const *metadata: Metadata type;
  • requiredSize: Required size;
  • requiredAlignmentMask: Specifies the mask required for alignmentobjcThat is, the source code7Because is8Byte alignment;

RequiredSize and requiredAlignmentMask are passed to the swift_slowAlloc function, which returns a pointer of type HeapObject; Reinterpret_cast is used to perform a pointer type conversion;

  • new (object) HeapObject(metadata):HeapObjectInitialization;

So what is swift_slowAlloc used for?

swift_slowAlloc

Its implementation is as follows:

In swift_slowAlloc, malloc is called to open up memory;

The process to summarize

So, we can roughly summarize the process of memory allocation for Swift objects:

  1. It first calls_allocating_init(): This function hasThe compilerGenerated;
  2. For pureSwiftThe class will be called againswift_allocObject()Functions;
  3. Then, inswift_allocObjec()Private functions are always called_swift_allocObject;
  4. And then through the functionswift_slowAlloccallmallocTo apply forThe heap areaMemory space;