Vandad Nahavandipoor
http://www.oreilly.com/pub/au/4596
Email: [email protected]
Blog: http://vandadnp.wordpress.com
Skype: vandad.np
Generics are pretty cool. They let us do complicated stuff that many programmers don't want to deal with sometimes and want to stick with traditional means of achieving the same goals but using basic ideas in OOP. In this edition of Swift Weekly, I won't teach you about generics eventhough you may just see the examples and learn generics anyways. What I will teach you however is how generics are compiled at the assembly level buy the Swift compiler.
I am going to use the release version of the code to make sure the output assembly is as optimized as possible so that the optimization level is set to -O
in the output when your Swift files are being compiled. Also my swift -version
shows this:
Swift version 1.1 (swift-600.0.54.20)
Target: x86_64-apple-darwin14.0.0
I am using the latest beta of Xcode, aka Version 6.2 (6C86e)
. Let's get started.
Note: I am going to get rid of some of the assembly code that is not relevant to the main point of this week's objective.
So here is the function I am going to write:
func add<T: IntegerType>(l: T, r: T) -> T{
return l + r
}
and I'm going to use it like so:
func example1(){
let a = 0xabcdefa
let b = 0xabcdefb
let c = add(a, b)
println(c)
}
Let's have a look at the assembly output of example1()
function:
push rbp
mov rbp, rsp
push rbx
push rax
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
mov rdi, rbx
add rsp, 0x8
pop rbx
pop rbp
jmp imp___stubs__swift_release
nop qword [cs:rax+rax+0x0]
Well, this is a crap load of code. What is happening here? Let me explain:
-
The stack is being set up.
-
The result of the addition of the value
a
andb
which were0xabcdefa
and0xabcdefb
are added and directly put into the code segment as you can see here:mov qword [ss:rbp+var_10], 0x1579bdf5 lea rdi, qword [ss:rbp+var_10]
and you can do the maths yourself if you want or use Calc.app on your mac to find the results
-
the
imp___stubs__putchar
internal function is then called to do theprintln()
for us. -
The stack is put back together ready to return to the caller.
Well this was really easy. This tells me that the compiler noticed that we called this generic function only once so didn't feel the need to make it a real function. How about if we call this function a few times? Let's see:
func example2(){
var a = 0
for _ in 0..<10{
add(a, randomInt())
}
}
before I show you the output assembly, please grab a cup of tea or coffee because this is getting quite interesting. Also make sure that you have a diaper around because this code that the compiler has generated is just... wow:
push rbp
mov rbp, rsp
push r15
push r14
push r12
push rbx
mov rbx, rdi
test rbx, rbx
mov rax, qword [ds:rbx]
je 0x1000027a0
lea r15, qword [ds:objc_class__TtC12swift_weekly4Test] ; objc_class__TtC12swift_weekly4Test
cmp rax, r15
je 0x100002887
mov r12, qword [ds:rax+0x48]
mov rdi, rbx
call imp___stubs__swift_retain
mov r14, rax
mov rdi, rbx
call r12
jmp 0x100002894
mov r15, qword [ds:rax+0x48] ; XREF=__TFC12swift_weekly4Test8example2fS0_FT_T_+20
mov rdi, rbx
mov edi, 0xffffffff ; argument #1 for method imp___stubs__arc4random_uniform, XREF=__TFC12swift_weekly4Test8example2fS0_FT_T_+32
call imp___stubs__arc4random_uniform
mov r14, rbx
mov rax, qword [ds:rbx] ; XREF=__TFC12swift_weekly4Test8example2fS0_FT_T_+59
cmp rax, r15
je 0x1000028b3
mov r12, qword [ds:rax+0x48]
mov rdi, r14
call imp___stubs__swift_retain
mov r14, rax
mov rdi, rbx
call r12
jmp 0x1000028bd
mov edi, 0xffffffff ; argument #1 for method imp___stubs__arc4random_uniform, XREF=__TFC12swift_weekly4Test8example2fS0_FT_T_+314
call imp___stubs__arc4random_uniform
mov rax, qword [ds:rbx] ; XREF=__TFC12swift_weekly4Test8example2fS0_FT_T_+337
cmp rax, r15
je 0x1000028dc
mov r12, qword [ds:rax+0x48]
mov rdi, r14
call imp___stubs__swift_retain
mov r14, rax
mov rdi, rbx
call r12
jmp 0x1000028e6
mov edi, 0xffffffff ; argument #1 for method imp___stubs__arc4random_uniform, XREF=__TFC12swift_weekly4Test8example2fS0_FT_T_+355
call imp___stubs__arc4random_uniform
mov rax, qword [ds:rbx] ; XREF=__TFC12swift_weekly4Test8example2fS0_FT_T_+378
cmp rax, r15
je 0x100002905
mov r12, qword [ds:rax+0x48]
mov rdi, r14
call imp___stubs__swift_retain
mov r14, rax
mov rdi, rbx
call r12
jmp 0x10000290f
mov edi, 0xffffffff ; argument #1 for method imp___stubs__arc4random_uniform, XREF=__TFC12swift_weekly4Test8example2fS0_FT_T_+396
call imp___stubs__arc4random_uniform
mov rax, qword [ds:rbx] ; XREF=__TFC12swift_weekly4Test8example2fS0_FT_T_+419
cmp rax, r15
je 0x10000292e
mov r12, qword [ds:rax+0x48]
mov rdi, r14
call imp___stubs__swift_retain
mov r14, rax
mov rdi, rbx
call r12
jmp 0x100002938
mov edi, 0xffffffff ; argument #1 for method imp___stubs__arc4random_uniform, XREF=__TFC12swift_weekly4Test8example2fS0_FT_T_+437
call imp___stubs__arc4random_uniform
mov rax, qword [ds:rbx] ; XREF=__TFC12swift_weekly4Test8example2fS0_FT_T_+460
cmp rax, r15
je 0x100002957
mov r12, qword [ds:rax+0x48]
mov rdi, r14
call imp___stubs__swift_retain
mov r14, rax
mov rdi, rbx
call r12
jmp 0x100002961
mov edi, 0xffffffff ; argument #1 for method imp___stubs__arc4random_uniform, XREF=__TFC12swift_weekly4Test8example2fS0_FT_T_+478
call imp___stubs__arc4random_uniform
mov rax, qword [ds:rbx] ; XREF=__TFC12swift_weekly4Test8example2fS0_FT_T_+501
cmp rax, r15
je 0x100002980
mov r12, qword [ds:rax+0x48]
mov rdi, r14
call imp___stubs__swift_retain
mov r14, rax
mov rdi, rbx
call r12
jmp 0x10000298a
mov edi, 0xffffffff ; argument #1 for method imp___stubs__arc4random_uniform, XREF=__TFC12swift_weekly4Test8example2fS0_FT_T_+519
call imp___stubs__arc4random_uniform
mov rax, qword [ds:rbx] ; XREF=__TFC12swift_weekly4Test8example2fS0_FT_T_+542
cmp rax, r15
je 0x1000029a9
mov r12, qword [ds:rax+0x48]
mov rdi, r14
call imp___stubs__swift_retain
mov r14, rax
mov rdi, rbx
call r12
jmp 0x1000029b3
mov edi, 0xffffffff ; argument #1 for method imp___stubs__arc4random_uniform, XREF=__TFC12swift_weekly4Test8example2fS0_FT_T_+560
call imp___stubs__arc4random_uniform
mov rax, qword [ds:rbx] ; XREF=__TFC12swift_weekly4Test8example2fS0_FT_T_+583
cmp rax, r15
je 0x1000029d2
mov r12, qword [ds:rax+0x48]
mov rdi, r14
call imp___stubs__swift_retain
mov r14, rax
mov rdi, rbx
call r12
jmp 0x1000029dc
mov edi, 0xffffffff ; argument #1 for method imp___stubs__arc4random_uniform, XREF=__TFC12swift_weekly4Test8example2fS0_FT_T_+601
call imp___stubs__arc4random_uniform
mov rax, qword [ds:rbx] ; XREF=__TFC12swift_weekly4Test8example2fS0_FT_T_+624
cmp rax, r15
je 0x1000029fb
mov r15, qword [ds:rax+0x48]
mov rdi, r14
call imp___stubs__swift_retain
mov r14, rax
mov rdi, rbx
call r15
jmp 0x100002a05
mov edi, 0xffffffff ; argument #1 for method imp___stubs__arc4random_uniform, XREF=__TFC12swift_weekly4Test8example2fS0_FT_T_+642
call imp___stubs__arc4random_uniform
mov rdi, r14 ; XREF=__TFC12swift_weekly4Test8example2fS0_FT_T_+290, __TFC12swift_weekly4Test8example2fS0_FT_T_+665
pop rbx
pop r12
pop r14
pop r15
pop rbp
jmp imp___stubs__swift_release
nop qword [cs:rax+rax+0x0]
Note: I've removed the code-segment addresses from the output assembly to make it neater to look at. If you are interested in reading the output assembly, contact me and I will email them to you.
Yes ladies and gents, you guessed it right. The compiler basically wrote the same code for 1 of the loops, but x10 times. So this means that the compiler didn't really understand that we had a generic function and did not respect it as a function, instead, it wrote the code for the function and basically for the whole loop, X
times where X
in our example is 10. This is not good. I will now break down the code:
-
The stack is being set up
-
The code then executes this instruction:
lea r15, qword [ds:objc_class__TtC12swift_weekly4Test]
I was curious as to what that whole
ds:objc_class__TtC12swift_weekly4Test
points to, which is basically an address in the data segment so I followed it along in the output assembly to reach this gem:dq __TMmC12swift_weekly4Test ; metaclass, XREF=__TFC12swift_weekly4Test8example2fS0_FT_T_+22, __TFC12swift_weekly4TestCfMS0_FT_S0_+16, __TMaC12swift_weekly4Test+16, _objc_classes_100015288, _objc_non_lazy_classes_1000152a0 dq _OBJC_CLASS_$_SwiftObject ; superclass dq __objc_empty_cache ; cache dq 0x0 ; vtable dq 0x100015f21 ; data (Swift class)
oh looky looky what we found!
objc
references in a Swift class? What is going on here? It looks like the Swift compiler has turned out wholeTest
class into an Objective-C class which its super class set toSwiftObject
. So I am guessing that this is some sort of a private class that Apple has shipped inside the SDK but never told us. The problem here is that Apple actually said that Swift classes do not need a super class. I don't know the answer to these two questions:- Why is it that we have a metadata for our class generated in the data segment telling us that our class is actually turned into an Objective-C class?
- Why is it that our class which has no super-class actually has been given a super-class with the private name of
SwiftObject
?
If you know the anser to these two questions, please submit a pull request and correct this article.
So the compiler generated the code for our generic function 10
times because we were looping that code 10
times. That makes me curious as to whether this is because it treated the generic function as an inline function because of its simplicity or not. So let's change that function to make it a bit more complicated and see if we get the same results:
//make the function more complicated for the compiler to inline in a very silly way. the goal is to make the function longer
func moreComplextGenericFunction<T: IntegerType>(l: T, r: T) -> T{
if l > r{
UIViewController()
UIView()
UILabel()
UIButton()
}
else{
UILabel()
UIButton()
UIViewController()
UIView()
}
return 0
}
and then I am going to go ahead and use this function in the same fashion as before:
func example3(){
var a = 0
for _ in 0..<randomInt(){
moreComplextGenericFunction(a, randomInt())
}
}
note that I am setting the higher boundary of the loop to a random number that the compiler cannot/will not determine during compile time even though it could. So this will allow us to circumvent any smart tricks that the compiler may do in code generation (pre-determining the loop count and pasting code in the loop N
times where N
is the higher boundary of our loop), just so that we can really see how the compiler deals with generics, not loops. This is going to be a long chunk of asm code which I will do my best to break down so bear with me please:
00000001000025a0 push rbp
00000001000025a1 mov rbp, rsp
00000001000025a4 push r15
00000001000025a6 push r14
00000001000025a8 push r13
00000001000025aa push r12
00000001000025ac push rbx
00000001000025ad push rax
00000001000025ae mov r14, rdi
00000001000025b1 mov rax, qword [ds:r14]
00000001000025b4 lea r13, qword [ds:objc_class__TtC12swift_weekly4Test] ; objc_class__TtC12swift_weekly4Test
00000001000025bb cmp rax, r13
00000001000025be jne 0x1000025d3
00000001000025c0 test r14, r14
00000001000025c3 je 0x1000025d3
00000001000025c5 mov edi, 0xffffffff ; argument #1 for method imp___stubs__arc4random_uniform
00000001000025ca call imp___stubs__arc4random_uniform
00000001000025cf mov ebx, eax
00000001000025d1 jmp 0x1000025f0
00000001000025d3 mov rbx, qword [ds:rax+0x48] ; XREF=__TFC12swift_weekly4Test8example3fS0_FT_T_+30, __TFC12swift_weekly4Test8example3fS0_FT_T_+35
00000001000025d7 mov rdi, r14
00000001000025da call imp___stubs__swift_retain
00000001000025df mov rdi, r14
00000001000025e2 call rbx
00000001000025e4 mov rbx, rax
00000001000025e7 test rbx, rbx
00000001000025ea js 0x1000029ab
00000001000025f0 test rbx, rbx ; XREF=__TFC12swift_weekly4Test8example3fS0_FT_T_+49
00000001000025f3 mov r15, r14
00000001000025f6 je 0x100002995
00000001000025fc test r14, r14
00000001000025ff je 0x1000027d8
0000000100002605 mov r15, r14
0000000100002608 nop qword [ds:rax+rax+0x0]
0000000100002610 mov rax, qword [ds:r14] ; XREF=__TFC12swift_weekly4Test8example3fS0_FT_T_+557
0000000100002613 cmp rax, r13
0000000100002616 je 0x1000026f0
000000010000261c mov r12, qword [ds:rax+0x48]
0000000100002620 mov rdi, r15
0000000100002623 call imp___stubs__swift_retain
0000000100002628 mov r15, rax
000000010000262b mov rdi, r14
000000010000262e call r12
0000000100002631 test rax, rax
0000000100002634 jns 0x1000026fa
000000010000263a mov rdi, qword [ds:imp___got__OBJC_CLASS_$_UIViewController] ; imp___got__OBJC_CLASS_$_UIViewController
0000000100002641 call imp___stubs__swift_getInitializedObjCClass
0000000100002646 mov rsi, qword [ds:0x100015fa8] ; @selector(allocWithZone:), argument "selector" for method imp___stubs__objc_msgSend
000000010000264d xor edx, edx
000000010000264f mov rdi, rax ; argument "instance" for method imp___stubs__objc_msgSend
0000000100002652 call imp___stubs__objc_msgSend
0000000100002657 mov rsi, qword [ds:0x100015fb0] ; @selector(init), argument "selector" for method imp___stubs__objc_msgSend
000000010000265e mov rdi, rax ; argument "instance" for method imp___stubs__objc_msgSend
0000000100002661 call imp___stubs__objc_msgSend
0000000100002666 mov rdi, rax ; argument "instance" for method imp___stubs__objc_release
0000000100002669 call imp___stubs__objc_release
000000010000266e mov rdi, qword [ds:imp___got__OBJC_CLASS_$_UIView] ; imp___got__OBJC_CLASS_$_UIView
0000000100002675 call imp___stubs__swift_getInitializedObjCClass
000000010000267a mov rsi, qword [ds:0x100015fa8] ; @selector(allocWithZone:), argument "selector" for method imp___stubs__objc_msgSend
0000000100002681 xor edx, edx
0000000100002683 mov rdi, rax ; argument "instance" for method imp___stubs__objc_msgSend
0000000100002686 call imp___stubs__objc_msgSend
000000010000268b mov rsi, qword [ds:0x100015fb0] ; @selector(init), argument "selector" for method imp___stubs__objc_msgSend
0000000100002692 mov rdi, rax ; argument "instance" for method imp___stubs__objc_msgSend
0000000100002695 call imp___stubs__objc_msgSend
000000010000269a mov rdi, rax ; argument "instance" for method imp___stubs__objc_release
000000010000269d call imp___stubs__objc_release
00000001000026a2 mov rdi, qword [ds:imp___got__OBJC_CLASS_$_UILabel] ; imp___got__OBJC_CLASS_$_UILabel
00000001000026a9 call imp___stubs__swift_getInitializedObjCClass
00000001000026ae mov rsi, qword [ds:0x100015fa8] ; @selector(allocWithZone:), argument "selector" for method imp___stubs__objc_msgSend
00000001000026b5 xor edx, edx
00000001000026b7 mov rdi, rax ; argument "instance" for method imp___stubs__objc_msgSend
00000001000026ba call imp___stubs__objc_msgSend
00000001000026bf mov rsi, qword [ds:0x100015fb0] ; @selector(init), argument "selector" for method imp___stubs__objc_msgSend
00000001000026c6 mov rdi, rax ; argument "instance" for method imp___stubs__objc_msgSend
00000001000026c9 call imp___stubs__objc_msgSend
00000001000026ce mov rdi, rax ; argument "instance" for method imp___stubs__objc_release
00000001000026d1 call imp___stubs__objc_release
00000001000026d6 mov rdi, qword [ds:imp___got__OBJC_CLASS_$_UIButton] ; imp___got__OBJC_CLASS_$_UIButton
00000001000026dd jmp 0x10000279d
00000001000026e2 nop qword [cs:rax+rax+0x0]
00000001000026f0 mov edi, 0xffffffff ; argument #1 for method imp___stubs__arc4random_uniform, XREF=__TFC12swift_weekly4Test8example3fS0_FT_T_+118
00000001000026f5 call imp___stubs__arc4random_uniform
00000001000026fa mov rdi, qword [ds:imp___got__OBJC_CLASS_$_UILabel] ; imp___got__OBJC_CLASS_$_UILabel, XREF=__TFC12swift_weekly4Test8example3fS0_FT_T_+148
0000000100002701 call imp___stubs__swift_getInitializedObjCClass
0000000100002706 mov rsi, qword [ds:0x100015fa8] ; @selector(allocWithZone:), argument "selector" for method imp___stubs__objc_msgSend
000000010000270d xor edx, edx
000000010000270f mov rdi, rax ; argument "instance" for method imp___stubs__objc_msgSend
0000000100002712 call imp___stubs__objc_msgSend
0000000100002717 mov rsi, qword [ds:0x100015fb0] ; @selector(init), argument "selector" for method imp___stubs__objc_msgSend
000000010000271e mov rdi, rax ; argument "instance" for method imp___stubs__objc_msgSend
0000000100002721 call imp___stubs__objc_msgSend
0000000100002726 mov rdi, rax ; argument "instance" for method imp___stubs__objc_release
0000000100002729 call imp___stubs__objc_release
000000010000272e mov rdi, qword [ds:imp___got__OBJC_CLASS_$_UIButton] ; imp___got__OBJC_CLASS_$_UIButton
0000000100002735 call imp___stubs__swift_getInitializedObjCClass
000000010000273a mov rsi, qword [ds:0x100015fa8] ; @selector(allocWithZone:), argument "selector" for method imp___stubs__objc_msgSend
0000000100002741 xor edx, edx
0000000100002743 mov rdi, rax ; argument "instance" for method imp___stubs__objc_msgSend
0000000100002746 call imp___stubs__objc_msgSend
000000010000274b mov rsi, qword [ds:0x100015fb0] ; @selector(init), argument "selector" for method imp___stubs__objc_msgSend
0000000100002752 mov rdi, rax ; argument "instance" for method imp___stubs__objc_msgSend
0000000100002755 call imp___stubs__objc_msgSend
000000010000275a mov rdi, rax ; argument "instance" for method imp___stubs__objc_release
000000010000275d call imp___stubs__objc_release
0000000100002762 mov rdi, qword [ds:imp___got__OBJC_CLASS_$_UIViewController] ; imp___got__OBJC_CLASS_$_UIViewController
0000000100002769 call imp___stubs__swift_getInitializedObjCClass
000000010000276e mov rsi, qword [ds:0x100015fa8] ; @selector(allocWithZone:), argument "selector" for method imp___stubs__objc_msgSend
0000000100002775 xor edx, edx
0000000100002777 mov rdi, rax ; argument "instance" for method imp___stubs__objc_msgSend
000000010000277a call imp___stubs__objc_msgSend
000000010000277f mov rsi, qword [ds:0x100015fb0] ; @selector(init), argument "selector" for method imp___stubs__objc_msgSend
0000000100002786 mov rdi, rax ; argument "instance" for method imp___stubs__objc_msgSend
0000000100002789 call imp___stubs__objc_msgSend
000000010000278e mov rdi, rax ; argument "instance" for method imp___stubs__objc_release
0000000100002791 call imp___stubs__objc_release
0000000100002796 mov rdi, qword [ds:imp___got__OBJC_CLASS_$_UIView] ; imp___got__OBJC_CLASS_$_UIView
000000010000279d call imp___stubs__swift_getInitializedObjCClass ; XREF=__TFC12swift_weekly4Test8example3fS0_FT_T_+317
00000001000027a2 mov rsi, qword [ds:0x100015fa8] ; @selector(allocWithZone:), argument "selector" for method imp___stubs__objc_msgSend
00000001000027a9 xor edx, edx
00000001000027ab mov rdi, rax ; argument "instance" for method imp___stubs__objc_msgSend
00000001000027ae call imp___stubs__objc_msgSend
00000001000027b3 mov rsi, qword [ds:0x100015fb0] ; @selector(init), argument "selector" for method imp___stubs__objc_msgSend
00000001000027ba mov rdi, rax ; argument "instance" for method imp___stubs__objc_msgSend
00000001000027bd call imp___stubs__objc_msgSend
00000001000027c2 mov rdi, rax ; argument "instance" for method imp___stubs__objc_release
00000001000027c5 call imp___stubs__objc_release
00000001000027ca dec rbx
00000001000027cd jne 0x100002610
00000001000027d3 jmp 0x100002995
00000001000027d8 mov r13, qword [ds:imp___got__OBJC_CLASS_$_UIButton] ; imp___got__OBJC_CLASS_$_UIButton, XREF=__TFC12swift_weekly4Test8example3fS0_FT_T_+95
00000001000027df mov r15, r14
00000001000027e2 nop qword [cs:rax+rax+0x0]
00000001000027f0 mov rax, qword [ds:r14] ; XREF=__TFC12swift_weekly4Test8example3fS0_FT_T_+1007
00000001000027f3 mov r12, qword [ds:rax+0x48]
00000001000027f7 mov rdi, r15
00000001000027fa call imp___stubs__swift_retain
00000001000027ff mov r15, rax
0000000100002802 xor edi, edi
0000000100002804 call r12
0000000100002807 test rax, rax
000000010000280a js 0x1000028c0
0000000100002810 mov rdi, qword [ds:imp___got__OBJC_CLASS_$_UILabel] ; imp___got__OBJC_CLASS_$_UILabel
0000000100002817 call imp___stubs__swift_getInitializedObjCClass
000000010000281c mov rsi, qword [ds:0x100015fa8] ; @selector(allocWithZone:), argument "selector" for method imp___stubs__objc_msgSend
0000000100002823 xor edx, edx
0000000100002825 mov rdi, rax ; argument "instance" for method imp___stubs__objc_msgSend
0000000100002828 call imp___stubs__objc_msgSend
000000010000282d mov rsi, qword [ds:0x100015fb0] ; @selector(init), argument "selector" for method imp___stubs__objc_msgSend
0000000100002834 mov rdi, rax ; argument "instance" for method imp___stubs__objc_msgSend
0000000100002837 call imp___stubs__objc_msgSend
000000010000283c mov rdi, rax ; argument "instance" for method imp___stubs__objc_release
000000010000283f call imp___stubs__objc_release
0000000100002844 mov rdi, r13
0000000100002847 call imp___stubs__swift_getInitializedObjCClass
000000010000284c mov rsi, qword [ds:0x100015fa8] ; @selector(allocWithZone:), argument "selector" for method imp___stubs__objc_msgSend
0000000100002853 xor edx, edx
0000000100002855 mov rdi, rax ; argument "instance" for method imp___stubs__objc_msgSend
0000000100002858 call imp___stubs__objc_msgSend
000000010000285d mov rsi, qword [ds:0x100015fb0] ; @selector(init), argument "selector" for method imp___stubs__objc_msgSend
0000000100002864 mov rdi, rax ; argument "instance" for method imp___stubs__objc_msgSend
0000000100002867 call imp___stubs__objc_msgSend
000000010000286c mov rdi, rax ; argument "instance" for method imp___stubs__objc_release
000000010000286f call imp___stubs__objc_release
0000000100002874 mov rdi, qword [ds:imp___got__OBJC_CLASS_$_UIViewController] ; imp___got__OBJC_CLASS_$_UIViewController
000000010000287b call imp___stubs__swift_getInitializedObjCClass
0000000100002880 mov rsi, qword [ds:0x100015fa8] ; @selector(allocWithZone:), argument "selector" for method imp___stubs__objc_msgSend
0000000100002887 xor edx, edx
0000000100002889 mov rdi, rax ; argument "instance" for method imp___stubs__objc_msgSend
000000010000288c call imp___stubs__objc_msgSend
0000000100002891 mov rsi, qword [ds:0x100015fb0] ; @selector(init), argument "selector" for method imp___stubs__objc_msgSend
0000000100002898 mov rdi, rax ; argument "instance" for method imp___stubs__objc_msgSend
000000010000289b call imp___stubs__objc_msgSend
00000001000028a0 mov rdi, rax ; argument "instance" for method imp___stubs__objc_release
00000001000028a3 call imp___stubs__objc_release
00000001000028a8 mov rdi, qword [ds:imp___got__OBJC_CLASS_$_UIView] ; imp___got__OBJC_CLASS_$_UIView
00000001000028af jmp 0x10000295f
00000001000028b4 nop qword [cs:rax+rax+0x0]
00000001000028c0 mov rdi, qword [ds:imp___got__OBJC_CLASS_$_UIViewController] ; imp___got__OBJC_CLASS_$_UIViewController, XREF=__TFC12swift_weekly4Test8example3fS0_FT_T_+618
00000001000028c7 call imp___stubs__swift_getInitializedObjCClass
00000001000028cc mov rsi, qword [ds:0x100015fa8] ; @selector(allocWithZone:), argument "selector" for method imp___stubs__objc_msgSend
00000001000028d3 xor edx, edx
00000001000028d5 mov rdi, rax ; argument "instance" for method imp___stubs__objc_msgSend
00000001000028d8 call imp___stubs__objc_msgSend
00000001000028dd mov rsi, qword [ds:0x100015fb0] ; @selector(init), argument "selector" for method imp___stubs__objc_msgSend
00000001000028e4 mov rdi, rax ; argument "instance" for method imp___stubs__objc_msgSend
00000001000028e7 call imp___stubs__objc_msgSend
00000001000028ec mov rdi, rax ; argument "instance" for method imp___stubs__objc_release
00000001000028ef call imp___stubs__objc_release
00000001000028f4 mov rdi, qword [ds:imp___got__OBJC_CLASS_$_UIView] ; imp___got__OBJC_CLASS_$_UIView
00000001000028fb call imp___stubs__swift_getInitializedObjCClass
0000000100002900 mov rsi, qword [ds:0x100015fa8] ; @selector(allocWithZone:), argument "selector" for method imp___stubs__objc_msgSend
0000000100002907 xor edx, edx
0000000100002909 mov rdi, rax ; argument "instance" for method imp___stubs__objc_msgSend
000000010000290c call imp___stubs__objc_msgSend
0000000100002911 mov rsi, qword [ds:0x100015fb0] ; @selector(init), argument "selector" for method imp___stubs__objc_msgSend
0000000100002918 mov rdi, rax ; argument "instance" for method imp___stubs__objc_msgSend
000000010000291b call imp___stubs__objc_msgSend
0000000100002920 mov rdi, rax ; argument "instance" for method imp___stubs__objc_release
0000000100002923 call imp___stubs__objc_release
0000000100002928 mov rdi, qword [ds:imp___got__OBJC_CLASS_$_UILabel] ; imp___got__OBJC_CLASS_$_UILabel
000000010000292f call imp___stubs__swift_getInitializedObjCClass
0000000100002934 mov rsi, qword [ds:0x100015fa8] ; @selector(allocWithZone:), argument "selector" for method imp___stubs__objc_msgSend
000000010000293b xor edx, edx
000000010000293d mov rdi, rax ; argument "instance" for method imp___stubs__objc_msgSend
0000000100002940 call imp___stubs__objc_msgSend
0000000100002945 mov rsi, qword [ds:0x100015fb0] ; @selector(init), argument "selector" for method imp___stubs__objc_msgSend
000000010000294c mov rdi, rax ; argument "instance" for method imp___stubs__objc_msgSend
000000010000294f call imp___stubs__objc_msgSend
0000000100002954 mov rdi, rax ; argument "instance" for method imp___stubs__objc_release
0000000100002957 call imp___stubs__objc_release
000000010000295c mov rdi, r13
000000010000295f call imp___stubs__swift_getInitializedObjCClass ; XREF=__TFC12swift_weekly4Test8example3fS0_FT_T_+783
0000000100002964 mov rsi, qword [ds:0x100015fa8] ; @selector(allocWithZone:), argument "selector" for method imp___stubs__objc_msgSend
000000010000296b xor edx, edx
000000010000296d mov rdi, rax ; argument "instance" for method imp___stubs__objc_msgSend
0000000100002970 call imp___stubs__objc_msgSend
0000000100002975 mov rsi, qword [ds:0x100015fb0] ; @selector(init), argument "selector" for method imp___stubs__objc_msgSend
000000010000297c mov rdi, rax ; argument "instance" for method imp___stubs__objc_msgSend
000000010000297f call imp___stubs__objc_msgSend
0000000100002984 mov rdi, rax ; argument "instance" for method imp___stubs__objc_release
0000000100002987 call imp___stubs__objc_release
000000010000298c dec rbx
000000010000298f jne 0x1000027f0
0000000100002995 mov rdi, r15 ; XREF=__TFC12swift_weekly4Test8example3fS0_FT_T_+86, __TFC12swift_weekly4Test8example3fS0_FT_T_+563
0000000100002998 add rsp, 0x8
000000010000299c pop rbx
000000010000299d pop r12
000000010000299f pop r13
00000001000029a1 pop r14
00000001000029a3 pop r15
00000001000029a5 pop rbp
00000001000029a6 jmp imp___stubs__swift_release
00000001000029ab ud2 ; XREF=__TFC12swift_weekly4Test8example3fS0_FT_T_+74
00000001000029ad nop qword [ds:rax]
__TFC12swift_weekly4Testd:
00000001000029b0 push rbp
00000001000029b1 mov rbp, rsp
00000001000029b4 mov rax, rdi
00000001000029b7 pop rbp
00000001000029b8 ret
; endp
00000001000029b9 nop qword [ds:rax+0x0]
I've left the code segment addresses intact to let you also have a look at how the conditional and unconfitional jump statements are working.
Here is a breakdown of what the code is doing:
-
The random number gets generated and placed inside the
ebx
32-bit register:00000001000025c5 mov edi, 0xffffffff ; argument #1 for method imp___stubs__arc4random_uniform 00000001000025ca call imp___stubs__arc4random_uniform 00000001000025cf mov ebx, eax 00000001000025d1 jmp 0x1000025f0
-
an unconditional
jmp
statement jumps to the following section of the code:00000001000025f0 test rbx, rbx ; XREF=__TFC12swift_weekly4Test8example3fS0_FT_T_+49 00000001000025f3 mov r15, r14 00000001000025f6 je 0x100002995
this is checking if the generated random number is
0
and if yes, it jumps over all the other code in theexample3()
function and straight to the end. So this is what we expect. 0 loops, 0 execution of the loop code. -
Almost near the end of the code, you can see where the loop is happening:
000000010000298c dec rbx 000000010000298f jne 0x1000027f0
It seems like Swift generally perfers to keep the integer counter inside a loop in the
ebx
register. If you have a loop that goes from0
to1000
, that number range that moves from0
to1000
is usually kept inside theebx
register. I have done some researching around the web and it seems likeebx
or its 64-bit counterpart ofrbx
are just general purpose registers and they are not designated to be a counter as such. But it's good to know that Swift usesebx
usually for counters. -
If you look at the code, you will realize what the Swift compiler has done. It has noticed that the code for the generic function of
moreComplextGenericFunction()
is actually quite long so it has done what it did before, that is to say that it brought the code in-line, however, it did not write the same codeN
times. It created conditional jumps around it so that the code is inline but not repeated. This is quite clever.
I am going to define a simple type like so:
struct Finder<T: Equatable>{
let array: [T]
let item: T
func isItemInArray() -> Bool{
for i in array{
if i == item{
return true
}
}
return false
}
}
I know already that there are tons of other ways to look for items in an array but this is to test generics. I am then going to use it:
func example4(){
let int1 = 0xabcdefa
let int2 = 0xabcdefb
let array = [int1, int2]
if Finder<Int>(array: array, item: int1).isItemInArray(){
println("Found int1 in array")
} else {
println("Could not find int1")
}
}
let's see the output assembly code:
0000000100003170 push rbp
0000000100003171 mov rbp, rsp
0000000100003174 push r15
0000000100003176 push r14
0000000100003178 push r12
000000010000317a push rbx
000000010000317b sub rsp, 0x40
000000010000317f mov r14, rdi
0000000100003182 lea rdi, qword [ds:0x10001a2f0] ; 0x10001a2f0 (_metadata + 0x10)
0000000100003189 mov esi, 0x28
000000010000318e mov edx, 0x7
0000000100003193 call imp___stubs__swift_allocObject
0000000100003198 mov r15, rax
000000010000319b mov qword [ds:r15+0x10], 0x2
00000001000031a3 mov qword [ds:r15+0x18], 0xabcdefa
00000001000031ab mov qword [ds:r15+0x20], 0xabcdefb
00000001000031b3 mov rdi, qword [ds:__TMLGCSs23_ContiguousArrayStorageSi_] ; __TMLGCSs23_ContiguousArrayStorageSi_
00000001000031ba test rdi, rdi
00000001000031bd jne 0x1000031e0
00000001000031bf mov rsi, qword [ds:imp___got___TMdSi] ; imp___got___TMdSi
00000001000031c6 add rsi, 0x8
00000001000031ca mov rdi, qword [ds:imp___got___TMPdCSs23_ContiguousArrayStorage] ; imp___got___TMPdCSs23_ContiguousArrayStorage
00000001000031d1 call imp___stubs__swift_getGenericMetadata1
00000001000031d6 mov rdi, rax
00000001000031d9 mov qword [ds:__TMLGCSs23_ContiguousArrayStorageSi_], rdi ; __TMLGCSs23_ContiguousArrayStorageSi_
00000001000031e0 mov esi, 0x30 ; XREF=__TFC12swift_weekly4Test8example4fS0_FT_T_+77
00000001000031e5 mov edx, 0x7
00000001000031ea call imp___stubs__swift_bufferAllocate
00000001000031ef mov rbx, rax
00000001000031f2 mov qword [ds:rbx+0x18], 0x0
00000001000031fa mov qword [ds:rbx+0x10], 0x0
0000000100003202 mov rdi, rbx ; argument "ptr" for method imp___stubs__malloc_size
0000000100003205 call imp___stubs__malloc_size
000000010000320a sub rax, 0x20
000000010000320e jo 0x10000336e
0000000100003214 cmp rax, 0xfffffffffffffff9
0000000100003218 jl 0x10000336e
000000010000321e mov rcx, rax
0000000100003221 sar rcx, 0x3f
0000000100003225 shr rcx, 0x3d
0000000100003229 add rcx, rax
000000010000322c sar rcx, 0x3
0000000100003230 add rcx, rcx
0000000100003233 mov qword [ds:rbx+0x10], 0x2
000000010000323b mov qword [ds:rbx+0x18], rcx
000000010000323f mov rax, qword [ds:r15+0x18]
0000000100003243 mov qword [ds:rbx+0x20], rax
0000000100003247 mov rax, qword [ds:r15+0x20]
000000010000324b mov qword [ds:rbx+0x28], rax
000000010000324f mov qword [ss:rbp+var_28], r15
0000000100003253 lea rdi, qword [ss:rbp+var_28]
0000000100003257 call imp___stubs__swift_fixLifetime
000000010000325c mov rdi, qword [ss:rbp+var_28]
0000000100003260 call imp___stubs__swift_release
0000000100003265 test rbx, rbx
0000000100003268 je 0x1000032c9
000000010000326a mov r12, qword [ds:rbx+0x10]
000000010000326e mov rdi, rbx
0000000100003271 call imp___stubs__swift_retain
0000000100003276 mov rdi, rax
0000000100003279 call imp___stubs__swift_retain
000000010000327e mov r15, rax
0000000100003281 test r12, r12
0000000100003284 je 0x1000032ec
0000000100003286 add rbx, 0x20
000000010000328a xor eax, eax
000000010000328c mov rcx, 0x7fffffffffffffff
0000000100003296 nop qword [cs:rax+rax+0x0]
00000001000032a0 cmp rax, r12 ; XREF=__TFC12swift_weekly4Test8example4fS0_FT_T_+341
00000001000032a3 jge 0x10000336e
00000001000032a9 cmp rax, rcx
00000001000032ac je 0x10000336e
00000001000032b2 cmp qword [ds:rbx], 0xabcdefa
00000001000032b9 je 0x10000331d
00000001000032bb inc rax
00000001000032be add rbx, 0x8
00000001000032c2 cmp r12, rax
00000001000032c5 jne 0x1000032a0
00000001000032c7 jmp 0x1000032ec
00000001000032c9 mov rdi, rbx ; XREF=__TFC12swift_weekly4Test8example4fS0_FT_T_+248
00000001000032cc call imp___stubs__swift_retain
00000001000032d1 mov rdi, rax
00000001000032d4 call imp___stubs__swift_retain
00000001000032d9 mov rdi, rax
00000001000032dc call imp___stubs__swift_retain
00000001000032e1 mov rdi, rax
00000001000032e4 call imp___stubs__swift_retain
00000001000032e9 mov r15, rax
00000001000032ec mov rdi, r15 ; XREF=__TFC12swift_weekly4Test8example4fS0_FT_T_+276, __TFC12swift_weekly4Test8example4fS0_FT_T_+343
00000001000032ef call imp___stubs__swift_release
00000001000032f4 mov rdi, r15
00000001000032f7 call imp___stubs__swift_release
00000001000032fc lea rax, qword [ds:___unnamed_1_1000186d0] ; "Could not find int1"
0000000100003303 mov qword [ss:rbp+var_58], rax
0000000100003307 mov qword [ss:rbp+var_50], 0x13
000000010000330f mov qword [ss:rbp+var_48], 0x0
0000000100003317 lea rdi, qword [ss:rbp+var_58]
000000010000331b jmp 0x10000334c
000000010000331d mov rdi, r15 ; XREF=__TFC12swift_weekly4Test8example4fS0_FT_T_+329
0000000100003320 call imp___stubs__swift_release
0000000100003325 mov rdi, r15
0000000100003328 call imp___stubs__swift_release
000000010000332d lea rax, qword [ds:___unnamed_2_1000186b0] ; "Found int1 in array"
0000000100003334 mov qword [ss:rbp+var_40], rax
0000000100003338 mov qword [ss:rbp+var_38], 0x13
0000000100003340 mov qword [ss:rbp+var_30], 0x0
0000000100003348 lea rdi, qword [ss:rbp+var_40]
000000010000334c call __TTSSS___TFSs7printlnU__FQ_T_ ; XREF=__TFC12swift_weekly4Test8example4fS0_FT_T_+427
0000000100003351 mov rdi, r15
0000000100003354 call imp___stubs__swift_release
0000000100003359 mov rdi, r14
000000010000335c call imp___stubs__swift_release
0000000100003361 add rsp, 0x40
0000000100003365 pop rbx
0000000100003366 pop r12
0000000100003368 pop r14
000000010000336a pop r15
000000010000336c pop rbp
000000010000336d ret
here are the things that I understand about this code:
-
Our code is again inlined. the whole generic type brought inline into the code. This can be good and bad.
-
The loop is happening here:
00000001000032a0 cmp rax, r12 ; XREF=__TFC12swift_weekly4Test8example4fS0_FT_T_+341 00000001000032a3 jge 0x10000336e 00000001000032a9 cmp rax, rcx 00000001000032ac je 0x10000336e 00000001000032b2 cmp qword [ds:rbx], 0xabcdefa 00000001000032b9 je 0x10000331d 00000001000032bb inc rax 00000001000032be add rbx, 0x8 00000001000032c2 cmp r12, rax 00000001000032c5 jne 0x1000032a0
-
rax
is the 64-bit register that Swift has designated to be equal to thei
variable in our generic type'sisItemInArray()
function. So this time Swift did not keep the counter inebx
. The compiler's choice of the counter register seems sporadic to say the least. -
the
rbx
generic purpose register is really working as a base address register here and it is the index into the array and is incremented by0x08
every time. Remember everyInt
is 8 bytes long (64-bits) hence the wholeadd rbx, 0x08
in the asm code. -
The value inside the current index of the array is being compared to
0xabcdefa
(the value we are trying to find), straight in the code. That is herecmp qword [ds:rbx], 0xabcdefa
. I can see here that even though our app is compiled for the release configuration and the optimization level is at highest, the compiler was not intelligent enough to store our array in the code segment. Instead, it has stored the array in the data segment. That happened pretty much at the head of our asm code:000000010000319b mov qword [ds:r15+0x10], 0x2 00000001000031a3 mov qword [ds:r15+0x18], 0xabcdefa 00000001000031ab mov qword [ds:r15+0x20], 0xabcdefb
there is a chunk of the output asm code that I have no idea (as of yet) about:
000000010000321e mov rcx, rax
0000000100003221 sar rcx, 0x3f
0000000100003225 shr rcx, 0x3d
0000000100003229 add rcx, rax
000000010000322c sar rcx, 0x3
0000000100003230 add rcx, rcx
0000000100003233 mov qword [ds:rbx+0x10], 0x2
000000010000323b mov qword [ds:rbx+0x18], rcx
000000010000323f mov rax, qword [ds:r15+0x18]
0000000100003243 mov qword [ds:rbx+0x20], rax
0000000100003247 mov rax, qword [ds:r15+0x20]
000000010000324b mov qword [ds:rbx+0x28], rax
000000010000324f mov qword [ss:rbp+var_28], r15
0000000100003253 lea rdi, qword [ss:rbp+var_28]
0000000100003257 call imp___stubs__swift_fixLifetime
000000010000325c mov rdi, qword [ss:rbp+var_28]
0000000100003260 call imp___stubs__swift_release
0000000100003265 test rbx, rbx
0000000100003268 je 0x1000032c9
I can see a lot of logical and arithmatic shift operations to the left and right all, and I see a sar
instruction shifting the rcx
register to the right 3 bits. Shiting a register to the right 3 bits is the fastest way for a compiler to divide a value by 8. If you shift right by 1 bit, you divide by 2, shift twice you divide by 4 and 3 bits divides by 8. If you know what this code is doing, please send a pull request so that everybody else will get to learn.
So yeah, generic extensions are a thing also! Here is one:
extension Finder{
func isAtEndOfArray(item: T) -> Bool{
if let last = array.last{
//this 0xabcdefa is placed here so that we can find this code easier in the output assembly
if last == item && randomInt() == 0xabcdefa{
return true
}
}
return false
}
}
And I'll start using it like so:
func example5(){
let int1 = 0xabcdefa
let int2 = 0xabcdefb
let array = [int1, int2]
if Finder<Int>(array: array, item: int1).isAtEndOfArray(int2){
println("Found int1 at the end of array")
} else {
println("Could not find int2 at the end of array")
}
}
here is the output assembly:
0000000100003a50 push rbp
0000000100003a51 mov rbp, rsp
0000000100003a54 push r15
0000000100003a56 push r14
0000000100003a58 push r12
0000000100003a5a push rbx
0000000100003a5b sub rsp, 0x40
0000000100003a5f mov r14, rdi
0000000100003a62 lea rdi, qword [ds:0x10001b330] ; 0x10001b330 (_metadata2 + 0x10)
0000000100003a69 mov esi, 0x28
0000000100003a6e mov edx, 0x7
0000000100003a73 call imp___stubs__swift_allocObject
0000000100003a78 mov r15, rax
0000000100003a7b mov qword [ds:r15+0x10], 0x2
0000000100003a83 mov qword [ds:r15+0x18], 0xabcdefa
0000000100003a8b mov qword [ds:r15+0x20], 0xabcdefb
0000000100003a93 mov rdi, qword [ds:__TMLGCSs23_ContiguousArrayStorageSi_] ; __TMLGCSs23_ContiguousArrayStorageSi_
0000000100003a9a test rdi, rdi
0000000100003a9d jne 0x100003ac0
0000000100003a9f mov rsi, qword [ds:imp___got___TMdSi] ; imp___got___TMdSi
0000000100003aa6 add rsi, 0x8
0000000100003aaa mov rdi, qword [ds:imp___got___TMPdCSs23_ContiguousArrayStorage] ; imp___got___TMPdCSs23_ContiguousArrayStorage
0000000100003ab1 call imp___stubs__swift_getGenericMetadata1
0000000100003ab6 mov rdi, rax
0000000100003ab9 mov qword [ds:__TMLGCSs23_ContiguousArrayStorageSi_], rdi ; __TMLGCSs23_ContiguousArrayStorageSi_
0000000100003ac0 mov esi, 0x30 ; XREF=__TFC12swift_weekly4Test8example5fS0_FT_T_+77
0000000100003ac5 mov edx, 0x7
0000000100003aca call imp___stubs__swift_bufferAllocate
0000000100003acf mov rbx, rax
0000000100003ad2 mov qword [ds:rbx+0x18], 0x0
0000000100003ada mov qword [ds:rbx+0x10], 0x0
0000000100003ae2 mov rdi, rbx ; argument "ptr" for method imp___stubs__malloc_size
0000000100003ae5 call imp___stubs__malloc_size
0000000100003aea sub rax, 0x20
0000000100003aee jo 0x100003c3b
0000000100003af4 cmp rax, 0xfffffffffffffff9
0000000100003af8 jl 0x100003c3b
0000000100003afe mov rcx, rax
0000000100003b01 sar rcx, 0x3f
0000000100003b05 shr rcx, 0x3d
0000000100003b09 add rcx, rax
0000000100003b0c sar rcx, 0x3
0000000100003b10 add rcx, rcx
0000000100003b13 mov qword [ds:rbx+0x10], 0x2
0000000100003b1b mov qword [ds:rbx+0x18], rcx
0000000100003b1f mov rax, qword [ds:r15+0x18]
0000000100003b23 mov qword [ds:rbx+0x20], rax
0000000100003b27 mov rax, qword [ds:r15+0x20]
0000000100003b2b mov qword [ds:rbx+0x28], rax
0000000100003b2f mov qword [ss:rbp+var_28], r15
0000000100003b33 lea rdi, qword [ss:rbp+var_28]
0000000100003b37 call imp___stubs__swift_fixLifetime
0000000100003b3c mov rdi, qword [ss:rbp+var_28]
0000000100003b40 call imp___stubs__swift_release
0000000100003b45 test rbx, rbx
0000000100003b48 je 0x100003bd7
0000000100003b4e mov r12, qword [ds:rbx+0x10]
0000000100003b52 mov rdi, rbx
0000000100003b55 call imp___stubs__swift_retain
0000000100003b5a mov r15, rax
0000000100003b5d test r12, r12
0000000100003b60 je 0x100003bf2
0000000100003b66 mov rax, 0x7fffffffffffffff
0000000100003b70 lea rcx, qword [ds:rax+0x1]
0000000100003b74 cmp r12, rcx
0000000100003b77 je 0x100003c3b
0000000100003b7d mov rcx, r12
0000000100003b80 dec rcx
0000000100003b83 js 0x100003c3b
0000000100003b89 cmp rcx, rax
0000000100003b8c je 0x100003c3b
0000000100003b92 cmp qword [ds:rbx+r12*8+0x18], 0xabcdefb
0000000100003b9b jne 0x100003bf2
0000000100003b9d mov edi, 0xffffffff ; argument "upper_bound" for method imp___stubs__arc4random_uniform
0000000100003ba2 call imp___stubs__arc4random_uniform
0000000100003ba7 cmp eax, 0xabcdefa
0000000100003bac jne 0x100003bf2
0000000100003bae mov rdi, r15
0000000100003bb1 call imp___stubs__swift_release
0000000100003bb6 lea rax, qword [ds:___unnamed_3_100019560] ; "Found int1 at the end of array"
0000000100003bbd mov qword [ss:rbp+var_40], rax
0000000100003bc1 mov qword [ss:rbp+var_38], 0x1e
0000000100003bc9 mov qword [ss:rbp+var_30], 0x0
0000000100003bd1 lea rdi, qword [ss:rbp+var_40]
0000000100003bd5 jmp 0x100003c19
0000000100003bd7 mov rdi, rbx ; XREF=__TFC12swift_weekly4Test8example5fS0_FT_T_+248
0000000100003bda call imp___stubs__swift_retain
0000000100003bdf mov rdi, rax
0000000100003be2 call imp___stubs__swift_retain
0000000100003be7 mov rdi, rax
0000000100003bea call imp___stubs__swift_retain
0000000100003bef mov r15, rax
0000000100003bf2 mov rdi, r15 ; XREF=__TFC12swift_weekly4Test8example5fS0_FT_T_+272, __TFC12swift_weekly4Test8example5fS0_FT_T_+331,
0000000100003bf5 call imp___stubs__swift_release
0000000100003bfa lea rax, qword [ds:___unnamed_4_100019580] ; "Could not find int2 at the end of array"
0000000100003c01 mov qword [ss:rbp+var_58], rax
0000000100003c05 mov qword [ss:rbp+var_50], 0x27
0000000100003c0d mov qword [ss:rbp+var_48], 0x0
0000000100003c15 lea rdi, qword [ss:rbp+var_58]
0000000100003c19 call __TTSSS___TFSs7printlnU__FQ_T_ ; XREF=__TFC12swift_weekly4Test8example5fS0_FT_T_+389
0000000100003c1e mov rdi, r15
0000000100003c21 call imp___stubs__swift_release
0000000100003c26 mov rdi, r14
0000000100003c29 call imp___stubs__swift_release
0000000100003c2e add rsp, 0x40
0000000100003c32 pop rbx
0000000100003c33 pop r12
0000000100003c35 pop r14
0000000100003c37 pop r15
0000000100003c39 pop rbp
0000000100003c3a ret
0000000100003c3b ud2
0000000100003c3d nop qword [ds:rax]
__TFC12swift_weekly4Testd:
0000000100003c40 push rbp
0000000100003c41 mov rbp, rsp
0000000100003c44 mov rax, rdi
0000000100003c47 pop rbp
0000000100003c48 ret
endp
0000000100003c49 nop qword [ds:rax+0x0]
here is the breakdown of the code, with my focus being on the generic-related side of the generated code. I will also not go through the stuff that I've said before so here is the new stuff:
-
Our array is allocated with the
imp___stubs__swift_bufferAllocate
call. Mysterious! If you know about this function or its implementation, please send a pull request and complete this article. -
This part of our Swift code:
if let last = array.last{ //this 0xabcdefa is placed here so that we can find this code easier in the output assembly if last == item && randomInt() == 0xabcdefa{ return true } }
is translated to this asm code:
0000000100003b92 cmp qword [ds:rbx+r12*8+0x18], 0xabcdefb 0000000100003b9b jne 0x100003bf2 0000000100003b9d mov edi, 0xffffffff ; argument "upper_bound" for method imp___stubs__arc4random_uniform 0000000100003ba2 call imp___stubs__arc4random_uniform 0000000100003ba7 cmp eax, 0xabcdefa 0000000100003bac jne 0x100003bf2
note how smart Swift's compiler is in this case. It has placed the value of
int2
which is0xabcdefb
right into the code segment and then does the comparison right there. If that value matches the found value then it continues to the next section of our if statement which is the random integer value. If it matches the random integer with0xabcdefb
(but this time as an inline value, notint2
), then it finishes the loop and does a conditional jump statement.
- For very simple
add
generic functions for theIntegerType
protocol, the compiler does the addition of the integers at compile-time and puts the results directly into the code segment. Noadd
function will be written in the code as such. - If you place a generic function inside a loop of
N
times, the compiler will generate the code for the generic functionN
times. This can be good and bad. - It seems like Swift generally perfers to keep the integer counter inside a loop in the
ebx
register. If you have a loop that goes from0
to1000
, that number range that moves from0
to1000
is usually kept inside theebx
register. - For more complext generic functions inside loops, the code is brought inline like less complex generic functions however the conditional
jmp
instructions that are placed around the inlined function allow it to be inlined only once. - Generic types, depending on their complexity, can also be brought inline. Swift, more often than not, brings generic code inline.
- For small or large constant arrays constructed inline, array values are inserted into the data segment in the opening lines of your function. This makes the code execute slower (than if the items were in the code segment) and accessed through general purpose registers, depending on how many items your array has.
- Swift's compiler generally prefers to place integer values right into the code segment if possible rather than storing them as
dq
ordd
values in the data segment. That saves it time in execution at the cost of more complex output assembly, and longer lines of code on production applications. - If you want to ensure that your code is as small as possible for production applications, I suggest playing a bit with the optimization levels in Swift's compiler so maybe for your app, the highest optimization level is not the best. At the time of this writing, there are only 2 optimiztion levels are available and none of them match "smallest code". So this is not an option unfortunately at this level. Swift is pretty much a baby right now so maybe in the future. We have to give it some time. Maybe in the next 3-4 years.