Vandad Nahavandipoor
http://www.oreilly.com/pub/au/4596
Email: [email protected]
Blog: http://vandadnp.wordpress.com
Skype: vandad.np
I have always been interested in finding out how different compilers work with basic operators such as +, -, % and so on. This week on the train I was thinking that it would be nice if somebody could explore how Swift deals with operators so, long story short, I decided to do it myself.
In this edition of Swift Weekly, I will show you how the Swift compiler works deals with (system and your own) operators and how to use operators to ensure you get the maximum performance.
Note: in this edition of Swift Weekly, I'm going to change things a little bit and instead of building for the debug configuration, I am going to build for Release to ensure that the assembly code that we are going to analyze is as optimized as what you will get when you release the app for the App Store. Optimization is hence enabled and the assembly output is long. That means setting the Optimization Level
in your build settings to Fastest, Smallest [-Os]
to ensure you get the export GCC_OPTIMIZATION_LEVEL=s
export when you build your project.
Note: to ensure that the assembly code which we will look at is clean and nice without too much unnecessary code, I will remove bits and pieces of it but will keep all the assembly code that is relevant.
The +
operator is usually used for integers so let's have a look at a basic usage:
func example1(){
let int1 = 0xabcdefa
let int2 = 0xabcdefb
let int3 = int1 + int2
println(int3)
let int4 = 0xabcdefa + 0xabcdefb
println(int4)
}
Note that the reason I've done the same operation twice is to see if the compiler is intelligent enough to compile the first sequence into the same code as the second sequence. Both are doing the same thing after all! And here is our output (long output, due to optimization level being set to Fastest, Smallest [-Os]
):
push rbp
mov rbp, rsp
push rbx
sub rsp, 0x18
mov rbx, rdi
mov qword [ss:rbp+var_10], 0x1579bdf5
lea rdi, qword [ss:rbp+var_10]
call __TTSSi_VSs7_StdoutS_Ss16OutputStreamType___TFSs5printU_Ss16OutputStreamType__FTQ_RQ0__T_
mov edi, 0xa ; argument #1 for method imp___stubs__putchar
call imp___stubs__putchar
xor edi, edi
call imp___stubs__swift_unknownRelease
mov qword [ss:rbp+var_18], 0x1579bdf5
lea rdi, qword [ss:rbp+var_18]
call __TTSSi_VSs7_StdoutS_Ss16OutputStreamType___TFSs5printU_Ss16OutputStreamType__FTQ_RQ0__T_
mov edi, 0xa ; argument #1 for method imp___stubs__putchar
call imp___stubs__putchar
xor edi, edi
call imp___stubs__swift_unknownRelease
mov rdi, rbx
add rsp, 0x18
pop rbx
pop rbp
jmp imp___stubs__swift_release
So what is happening in this monster of a code? I'll explain:
-
The stack is being set up
-
The values of
0xabcdefa
and0xabcdefb
are added at compile time and placed inside the stack. The result is the value of0x1579bdf5
as you can see here:mov qword [ss:rbp+var_10], 0x1579bdf5
-
Then the
rdi
register is being set to the address in the stack that points to the calculated value (lea
means load effective address, pretty much setting uprdi
as a pointer to the value in the stack):lea rdi, qword [ss:rbp+var_10]
-
After that, the call is made to a function whose name is ginormous (
__TTSSi_VSs7_StdoutS_Ss16OutputStreamType___TFSs5printU_Ss16OutputStreamType__FTQ_RQ0__T_
). I'm going to break this name down into smaller pieces to see if I can understand it, ignoring anything that I don't understand.Std
+out
+S
. That tells me it outputs a stream to the standard output. ThenOutputStreamType
followed byprintU
telling me that this is a function that prepares the output for theprintln()
function based on the value that it is given. So to cut the story short, this long function name is a function that takes any value in, and then outputs it as a stream to be digested by theprintln()
function. -
As we have seen in the previous editions of Swift Weekly, Swift uses the System V calling convention for calling functions which states that:
...additional arguments are passed on the stack and the return value is stored in RAX.
So when we the function is called, its return value is stored in the
rax
register.
I just remembered that this edition is only about how Swift deals with operators so I am going to ignore the rest of the details. For now, bear in mind that for constant integers whose addition can be calculated at compile-time, Swift does this elegently as you would expect.
So that was the first piece of code that added two constant Int
values together. Then later we added two constant values inline, without using the let
syntax. The output of that code was, as you saw, the same:
mov qword [ss:rbp+var_18], 0x1579bdf5
lea rdi, qword [ss:rbp+var_18]
So Swift compiles this code:
let int1 = 0xabcdefa
let int2 = 0xabcdefb
let int3 = int1 + int2
println(int3)
and this code:
let int4 = 0xabcdefa + 0xabcdefb
println(int4)
in the exact same way. Lesson? Don't be afraid to use additional constant integers if it makes your code more readable.
Let's see how the Swift compiler deals with Int
variables and the +
operator though:
func example2(){
var int1 = 0xabcdefa
var int2 = 0xabcdefb
var int3 = int1 + int2
println(int3)
var int4 = 0xabcdefa + 0xabcdefb
println(int4)
}
the output assembly code will look like this:
push rbp
mov rbp, rsp
push rbx
sub rsp, 0x18
mov rbx, rdi
mov qword [ss:rbp+var_10], 0x1579bdf5
lea rdi, qword [ss:rbp+var_10]
call __TTSSi_VSs7_StdoutS_Ss16OutputStreamType___TFSs5printU_Ss16OutputStreamType__FTQ_RQ0__T_
mov edi, 0xa ; argument #1 for method imp___stubs__putchar
call imp___stubs__putchar
xor edi, edi
call imp___stubs__swift_unknownRelease
mov qword [ss:rbp+var_18], 0x1579bdf5
lea rdi, qword [ss:rbp+var_18]
call __TTSSi_VSs7_StdoutS_Ss16OutputStreamType___TFSs5printU_Ss16OutputStreamType__FTQ_RQ0__T_
mov edi, 0xa ; argument #1 for method imp___stubs__putchar
call imp___stubs__putchar
xor edi, edi
call imp___stubs__swift_unknownRelease
mov rdi, rbx
add rsp, 0x18
pop rbx
pop rbp
jmp imp___stubs__swift_release
Look closely at this line of code:
mov qword [ss:rbp+var_10], 0x1579bdf5
lea rdi, qword [ss:rbp+var_10]
call __TTSSi_VSs7_StdoutS_Ss16OutputStreamType___TFSs5printU_Ss16OutputStreamType__FTQ_RQ0__T_
Yes, you guessed it. The variables didn't make any difference. Swift realized that even though we had variables, the values that we assigned to them were constants and the +
operator also added two constant values assigned to variables, and assigned the result back to a variable instead of a constant. Sooooo, cutting the long story short (probably all that what I said in the previous sentence was so difficult to get anyways), in this case, creating variables and constants is exactly the same. No stack variable was created. No constant was created in the stack. All that happened was that the addition was performed at compile time and the results were placed in the stack. So again, don't be afraid to use variables if you have to. They are very well optimized.
The above example has one simple flaw: even though we are using var
to create variables, the values we are using are constants, and the compiler knows that. But what if we change this example to this?
func randomInt() -> Int{
return Int(arc4random_uniform(UInt32.max))
}
func example3(){
var int1 = randomInt()
var int2 = randomInt()
var int3 = int1 + int2
}
now what is happening here is obviously that our var
variables are definitely not set to compile-time constants. We are going to check out how the compiler deals with additions on real variables:
0x0000000100002a70 55 push rbp
0x0000000100002a71 4889E5 mov rbp, rsp
0x0000000100002a74 4157 push r15
0x0000000100002a76 4156 push r14
0x0000000100002a78 4154 push r12
0x0000000100002a7a 53 push rbx
0x0000000100002a7b 4989FF mov r15, rdi
0x0000000100002a7e 498B07 mov rax, qword [ds:r15]
0x0000000100002a81 4C8D2528370100 lea r12, qword [ds:objc_class__TtC12swift_weekly7Example] ; objc_class__TtC12swift_weekly7Example
0x0000000100002a88 4C39E0 cmp rax, r12
0x0000000100002a8b 7514 jne 0x100002aa1
0x0000000100002a8d 4D85FF test r15, r15
0x0000000100002a90 740F je 0x100002aa1
0x0000000100002a92 BFFFFFFFFF mov edi, 0xffffffff ; argument #1 for method imp___stubs__arc4random_uniform
0x0000000100002a97 E862060100 call imp___stubs__arc4random_uniform
0x0000000100002a9c 4189C6 mov r14d, eax
0x0000000100002a9f EB14 jmp 0x100002ab5
0x0000000100002aa1 488B5858 mov rbx, qword [ds:rax+0x58] ; XREF=__TFC12swift_weekly7Example8example3fS0_FT_T_+27, __TFC12swift_weekly7Example8example3fS0_FT_T_+32
0x0000000100002aa5 4C89FF mov rdi, r15
0x0000000100002aa8 E8ED060100 call imp___stubs__swift_retain
0x0000000100002aad 4C89FF mov rdi, r15
0x0000000100002ab0 FFD3 call rbx
0x0000000100002ab2 4989C6 mov r14, rax
0x0000000100002ab5 498B07 mov rax, qword [ds:r15] ; XREF=__TFC12swift_weekly7Example8example3fS0_FT_T_+47
0x0000000100002ab8 31DB xor ebx, ebx
0x0000000100002aba 4C39E0 cmp rax, r12
0x0000000100002abd 490F44DF cmove rbx, r15
0x0000000100002ac1 4885DB test rbx, rbx
0x0000000100002ac4 7417 je 0x100002add
0x0000000100002ac6 BFFFFFFFFF mov edi, 0xffffffff ; argument #1 for method imp___stubs__arc4random_uniform
0x0000000100002acb E82E060100 call imp___stubs__arc4random_uniform
0x0000000100002ad0 4189C7 mov r15d, eax
0x0000000100002ad3 4889DF mov rdi, rbx
0x0000000100002ad6 E8B9060100 call imp___stubs__swift_release
0x0000000100002adb EB09 jmp 0x100002ae6
0x0000000100002add 4C89FF mov rdi, r15 ; XREF=__TFC12swift_weekly7Example8example3fS0_FT_T_+84
0x0000000100002ae0 FF5058 call qword [ds:rax+0x58]
0x0000000100002ae3 4989C7 mov r15, rax
0x0000000100002ae6 4D01FE add r14, r15 ; XREF=__TFC12swift_weekly7Example8example3fS0_FT_T_+107
0x0000000100002ae9 7009 jo 0x100002af4
0x0000000100002aeb 5B pop rbx
0x0000000100002aec 415C pop r12
0x0000000100002aee 415E pop r14
0x0000000100002af0 415F pop r15
0x0000000100002af2 5D pop rbp
0x0000000100002af3 C3 ret
0x0000000100002af4 0F0B ud2 ; XREF=__TFC12swift_weekly7Example8example3fS0_FT_T_+121
0x0000000100002af6 662E0F1F840000000000 nop qword [cs:rax+rax+0x0]
__TFC12swift_weekly7Exampled:
0x0000000100002b00 55 push rbp
0x0000000100002b01 4889E5 mov rbp, rsp
0x0000000100002b04 4889F8 mov rax, rdi
0x0000000100002b07 5D pop rbp
0x0000000100002b08 C3 ret
Jesus. I have no clue as of yet what this code does but I am going to try and understand it now. Here is an explanation of the important (+
operation related) code:
-
The
UInt32.max
value is placed right into the code and thearc4random_uniform()
function is called directly for every single instance of our variable. You can see that we call therandomInt()
function twice for two variables but there is no mention of this function in our code. The compiler has made the functioninline
. But that's a whole other story. So for this:var int1 = randomInt()
we got this output:
mov edi, 0xffffffff ; argument #1 for method imp___stubs__arc4random_uniform call imp___stubs__arc4random_uniform mov r14d, eax jmp 0x100002ab5
so the random number is now placed in the
r14d
register.r14
is a 64-bit register and its lower 32-bits are accessibly through ther14d
register. Okay that's great. -
Later in the code you can see this:
mov edi, 0xffffffff ; argument #1 for method imp___stubs__arc4random_uniform call imp___stubs__arc4random_uniform mov r15d, eax mov rdi, rbx call imp___stubs__swift_release jmp 0x100002ae6
that's the generated code for our
var int2 = randomInt()
code. In here you can see another call to the random function and the results are placed inside ther15d
register. Up to now,r14d
is keeping the first random number andr15d
is keeping the second. -
The
jmp
instruction then jumps to this code:add r14, r15 ; XREF=__TFC12swift_weekly7Example8example3fS0_FT_T_+107 jo 0x100002af4
thank God! finally an
add
instruction! Seriously. I thought I wouldn't see this eventually. So that's how the add is then performed.
Let's write this Swift code:
func example4(){
let int1 = randomInt()
let int2 = randomInt()
let int3 = int1 - int2
println(int3)
}
and see the output:
0x00000001000029f0 55 push rbp
0x00000001000029f1 4889E5 mov rbp, rsp
0x00000001000029f4 4157 push r15
0x00000001000029f6 4156 push r14
0x00000001000029f8 53 push rbx
0x00000001000029f9 50 push rax
0x00000001000029fa 4989FE mov r14, rdi
0x00000001000029fd 498B06 mov rax, qword [ds:r14]
0x0000000100002a00 4C8D3DA9370100 lea r15, qword [ds:objc_class__TtC12swift_weekly7Example] ; objc_class__TtC12swift_weekly7Example
0x0000000100002a07 4C39F8 cmp rax, r15
0x0000000100002a0a 7513 jne 0x100002a1f
0x0000000100002a0c 4D85F6 test r14, r14
0x0000000100002a0f 740E je 0x100002a1f
0x0000000100002a11 BFFFFFFFFF mov edi, 0xffffffff ; argument #1 for method imp___stubs__arc4random_uniform
0x0000000100002a16 E8B3060100 call imp___stubs__arc4random_uniform
0x0000000100002a1b 89C3 mov ebx, eax
0x0000000100002a1d EB14 jmp 0x100002a33
0x0000000100002a1f 488B5858 mov rbx, qword [ds:rax+0x58] ; XREF=__TFC12swift_weekly7Example8example4fS0_FT_T_+26, __TFC12swift_weekly7Example8example4fS0_FT_T_+31
0x0000000100002a23 4C89F7 mov rdi, r14
0x0000000100002a26 E83F070100 call imp___stubs__swift_retain
0x0000000100002a2b 4C89F7 mov rdi, r14
0x0000000100002a2e FFD3 call rbx
0x0000000100002a30 4889C3 mov rbx, rax
0x0000000100002a33 498B06 mov rax, qword [ds:r14] ; XREF=__TFC12swift_weekly7Example8example4fS0_FT_T_+45
0x0000000100002a36 4C39F8 cmp rax, r15
0x0000000100002a39 7513 jne 0x100002a4e
0x0000000100002a3b 4D85F6 test r14, r14
0x0000000100002a3e 740E je 0x100002a4e
0x0000000100002a40 BFFFFFFFFF mov edi, 0xffffffff ; argument #1 for method imp___stubs__arc4random_uniform
0x0000000100002a45 E884060100 call imp___stubs__arc4random_uniform
0x0000000100002a4a 89C0 mov eax, eax
0x0000000100002a4c EB12 jmp 0x100002a60
0x0000000100002a4e 4C8B7858 mov r15, qword [ds:rax+0x58] ; XREF=__TFC12swift_weekly7Example8example4fS0_FT_T_+73, __TFC12swift_weekly7Example8example4fS0_FT_T_+78
0x0000000100002a52 4C89F7 mov rdi, r14
0x0000000100002a55 E810070100 call imp___stubs__swift_retain
0x0000000100002a5a 4C89F7 mov rdi, r14
0x0000000100002a5d 41FFD7 call r15
0x0000000100002a60 4829C3 sub rbx, rax ; XREF=__TFC12swift_weekly7Example8example4fS0_FT_T_+92
0x0000000100002a63 7068 jo 0x100002acd
0x0000000100002a65 48895DE0 mov qword [ss:rbp+var_20], rbx
0x0000000100002a69 488D7DE0 lea rdi, qword [ss:rbp+var_20]
0x0000000100002a6d E88E090000 call __TTSSi_VSs7_StdoutS_Ss16OutputStreamType___TFSs5printU_Ss16OutputStreamType__FTQ_RQ0__T_
0x0000000100002aaa BF0A000000 mov edi, 0xa ; argument #1 for method imp___stubs__putchar
0x0000000100002aaf E826060100 call imp___stubs__putchar
0x0000000100002ab4 31FF xor edi, edi
0x0000000100002ab6 E8C7060100 call imp___stubs__swift_unknownRelease
0x0000000100002abb 4C89F7 mov rdi, r14
0x0000000100002abe 4883C408 add rsp, 0x8
0x0000000100002ac2 5B pop rbx
0x0000000100002ac3 415E pop r14
0x0000000100002ac5 415F pop r15
0x0000000100002ac7 5D pop rbp
0x0000000100002ac8 E997060100 jmp imp___stubs__swift_release
0x0000000100002acd 0F0B ud2 ; XREF=__TFC12swift_weekly7Example8example4fS0_FT_T_+115
0x0000000100002acf 90 nop
__TFC12swift_weekly7Exampled:
0x0000000100002ad0 55 push rbp
0x0000000100002ad1 4889E5 mov rbp, rsp
0x0000000100002ad4 4889F8 mov rax, rdi
0x0000000100002ad7 5D pop rbp
0x0000000100002ad8 C3 ret
I will point out the important parts of the code:
-
Here is the code generated for
let int1 = randomInt()
:0x0000000100002a11 BFFFFFFFFF mov edi, 0xffffffff ; argument #1 for method imp___stubs__arc4random_uniform 0x0000000100002a16 E8B3060100 call imp___stubs__arc4random_uniform 0x0000000100002a1b 89C3 mov ebx, eax 0x0000000100002a1d EB14 jmp 0x100002a33
The value of
UInt32.max
is placed right into the code segment, thearc4random_uniform()
function is called inline and then the return value is placed inebx
, which is a 64-bit register. -
Then the code for
let int2 = randomInt()
is compiled:0x0000000100002a40 BFFFFFFFFF mov edi, 0xffffffff ; argument #1 for method imp___stubs__arc4random_uniform 0x0000000100002a45 E884060100 call imp___stubs__arc4random_uniform 0x0000000100002a4a 89C0 mov eax, eax 0x0000000100002a4c EB12 jmp 0x100002a60
again this is similar to what we discussed before. This time there is a bit of a sillyness to the code though and that's the
mov eax, eax
instruction. What a strange thing to do. So what happened here is that the compiler calculates the random value, and obviously theimp___stubs__arc4random_uniform
function returns the value in theeax
register but the compiler doesn't care that the value is already ineax
. Instead, it tries to move the value fromeax
intoeax
again. Why could this be though? If you know, please correct this article and send a pull request. -
The subtraction is finally calculated using this code:
0x0000000100002a60 4829C3 sub rbx, rax ; XREF=__TFC12swift_weekly7Example8example4fS0_FT_T_+92 0x0000000100002a63 7068 jo 0x100002acd
Nothing magical. So that's good to know!
Ternary operator... I remember discussing this with my co-workers a few years back. I didn't like them, but now I have grown to like them more than I ever did before. They make the code clean(er) sometimes and can also make it messy, depending on how you use them. Enough ranting. Now let's see how Swift deals with a simple ternary:
func example5(){
let int1 = random()
let int2 = random()
let int3 = int1 > int2 ? 0xabcdefa : 0xabcdefb
}
Note that I started using the random()
function instead of our custom randomInt()
function because the output assembly code with random()
will just be much cleaner.
this code compiles into this:
push rbp
mov rbp, rsp
push r14
push rbx
sub rsp, 0x10
mov r14, rdi
call imp___stubs__random
mov rbx, rax
call imp___stubs__random
cmp rbx, rax
setle al
movzx eax, al
or rax, 0xabcdefa
mov qword [ss:rbp+var_18], rax
lea rdi, qword [ss:rbp+var_18]
call __TTSSi_VSs7_StdoutS_Ss16OutputStreamType___TFSs5printU_Ss16OutputStreamType__FTQ_RQ0__T_
mov edi, 0xa ; argument #1 for method imp___stubs__putchar
call imp___stubs__putchar
xor edi, edi
mov rdi, r14
add rsp, 0x10
pop rbx
pop r14
pop rbp
jmp imp___stubs__swift_release
Okay that's quite nice and clean but, wait a minute, there is no mention of 0xabcdefb
. What happened to our second value...? So if you look at the output, you can see or rax, 0xabcdefa
but there is no mention of 0xabcdefb
. Let's ee what happened:
- The 2 random values are placed inside
rbx
andrax
, respectively. - The
cmp
instruction is called comparing the two values.cmp
really does a subtraction under the hoods (sub
) and then sets the values for theEFLAGS
. Read the Intel manuals in the References section of this article to find out more. - Then the
setle
instruction is called. This isset if less than or equal
which then sets theal
register to0x01
if the two random values are the same. - The
movzx
is caleld on theeax
register withal
, basically setting the rest of theeax
register to zero. Remember thatal
is the lower 8-bits of theeax
64-bit register on x86_64. So noweax
is 1 if our random numbers are equal or zero if they aren't. - Then the
or rax, 0xabcdefa
instruction is called. HOLY SHIT. Do you understand what the compiler did? Soeax
is right now set to 1 if the two random numbers are equal to each other and then boom, the whole register isor
ed with the value of0xabcdefa
. In other words, if the two random numbers are the same,eax
will become0x00000001 | 0xabcdefa
effectively making it0xabcdefb
and if they are not equal,eax
would have been zero from the comparison and will now beor
ed with0xabcdefa
, effectively making it0xabcdefa
. Shit! that's some serious optimization the Swift compiler did right there. So instead of writing an if statement, it solved the problem with the `EFLAGS.
So we saw the Int
ternary operator but how does that work on Bool
values? Let's see the Swift code first:
func example6(){
let bool = Bool(random()) ? 0xabcdefa : 0xabcdefb
println(bool)
}
Create a boolean value out of a random number, then if it's true
, put the value of 0xabcdefa
into our constant and if not, put the value of 0xabcdefb
into it and then print the results.
Assembly output is this:
push rbp
mov rbp, rsp
push rbx
push rax
mov rbx, rdi
call imp___stubs__random
mov rdi, rax
call imp___stubs___TFE10FoundationSi19_bridgeToObjectiveCfSiFT_CSo8NSNumber
mov rdi, rax
call imp___stubs___TFE10FoundationSbCfMSbFCSo8NSNumberSb
test al, 0x1
sete al
movzx eax, al
or rax, 0xabcdefa
mov qword [ss:rbp+var_10], rax
lea rdi, qword [ss:rbp+var_10]
call __TTSSi_VSs7_StdoutS_Ss16OutputStreamType___TFSs5printU_Ss16OutputStreamType__FTQ_RQ0__T_
mov edi, 0xa ; argument #1 for method imp___stubs__putchar
call imp___stubs__putchar
xor edi, edi
call imp___stubs__swift_unknownRelease
mov rdi, rbx
add rsp, 0x8
pop rbx
pop rbp
jmp imp___stubs__swift_release
Here is what happened:
- The
imp___stubs__random
function is called, generating a random number in theeax
register. - The
imp___stubs___TFE10FoundationSi19_bridgeToObjectiveCfSiFT_CSo8NSNumber
function is called, translating the random number into anNSNumber
instance. Why the hell would the compiler do this? If you know, please send a pull request through. - The
imp___stubs___TFE10FoundationSbCfMSbFCSo8NSNumberSb
function is called to translate theNSNumber
to a boolean. Well this is awkward. Why would the compiler go through this length? - So now the
eax
register contains a boolean value (0 or 1) out of the random number. Thentest al, 0x1
instruction is called comparing the boolean value withtrue
. Let's assume that the boolean value was true.. Thetest
instructionand
s the true (0x01
) with the value of0x01
and if the result is zero (which in this case it won't be) it will set the zero flag to 1, which in this case, the zero flag will be set to0x00
since theand
will yield0x01
. - With our assumption in the previous step, the
sete al
instruction is called, setting the value ofal
to0x00
since the zero flag is set to 0. - Then the
movzx eax, al
is called making sure that the rest of theeax
register is zero, keeping the lower 8-bits of this register (al
) untouched. With our previous assumption, the wholeeax
register is now 0. - And then
or rax, 0xabcdefa
is executed. With our previous assumtpion andeax
now being 0, this will makerax
equal to0xabcdefa
. If oureax
was set to 1 from the previous operations, this would have yielded0xabcdefb
.
This is a very complicated way of dealing with a simple ternary but shows you how mature the Swift compiler has already become in terms of optimizing your code. No conditional jumps were used in this case.
-
If you use the
let
syntax to create two compile-timeInt
constants and add them together using the+
operator, the result is calculated at compile time and no stack variable is used. The addition is performed at compile-time and the result is placed right into the code segment, not the data-segment. No additionadd
instructions are created in the assembly output. -
Adding two constant inline
Int
values in one line of code and assigning the result to a constantInt
value with thelet
statement will also create the results as a compile time constant placed inside the code segment. -
If two
var
of typeInt
point to a compile-timeInt
constant and are added into another variable of typeInt
using the+
operator, no variable is created for the compiled code. The addition between the constants are calculated at compile time and placed right inside the code segment. -
A simple function similar to this:
func randomInt() -> Int{ return Int(arc4random_uniform(UInt32.max)) }
will most probably get inlined in Swift. In other words, no
randomInt()
function will be created, but rather the code for this function will be called directly inside the code segment (cs
) whenever this function is used. -
Addition of two local
Int
variables is performed using theadd
instruction, as expected. The values, dependent on how many registers are being used at the moment, will be placed inside one of ther
registers, such as ther15
orr14
. -
Int
variables on x86_64 are treated as true native 64-bit values, placed inside registers such aseax
andebx
. -
Subtraction of variable
Int
values is done using thesub
instruction, as expected. -
A ternary operator comparing two
Int
values and placing anInt
into another variable or constant is solved usingEFLAGS
. No if statement is compiled by the compiler, further optimizing the output. -
No conditional jumps are used for ternary operator between two
Int
values.