Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the Slice compare logic in Swift

Tags:

slice

swift

Here's some code:

var arr1 = [1, 2, 3, 4]
var arr2 = [1, 2, 3, 4]

if arr1 == arr2 {
    println("Equal")
} else {
    println("Not Equal")
}
// console output: Equal

let slice1 = arr1[0..4]
let slice2 = arr2[0..4]

if slice1 == slice2 {
    println("Equal")
} else {
    println("Not Equal")
}
// console output: Equal

It's straightforward, but code following:

if arr1[0..4] == arr2[0..4] {
    println("Equal")
} else {
    println("Not Equal")
}
// console output: Not Equal

Why the result is "Not Equal" rather than "Equal"? Can I use == operator to compare the value type contents of two arrays or two slices? If not, why I could get the excepted results of the first two if statement?

like image 486
Thons Avatar asked Jun 16 '14 07:06

Thons


1 Answers

I believe this is a compiler bug that did not call the correct == overload function to compare Slice under some conditions.


this is the minimum example I came out to produce the problem

let s1 : Slice<Int> = Slice(count: 1, repeatedValue:1)
let s2 : Slice<Int> = Slice(count: 1, repeatedValue:1)

var b1 = s1[0..1] == s2  // false
var b2 = s1 == s2        // true

In order to see what happened, I put it as a unit test in a Xcode project and check the disassembly code

Start with the correct code:

func testSlice2() {
    b2 = s1 == s2
}

Xcode give me this

test2Tests`test2Tests.VariableTests.testSlice2 (test2Tests.VariableTests)() -> () at test2Tests.swift:38:
0x1000ec120:  pushq  %rbp
0x1000ec121:  movq   %rsp, %rbp
0x1000ec124:  subq   $0x80, %rsp
0x1000ec12b:  movq   %rdi, -0x8(%rbp)
0x1000ec12f:  movq   %rdi, -0x10(%rbp)
0x1000ec133:  callq  0x1000efd86               ; symbol stub for: objc_retain
0x1000ec138:  movq   -0x10(%rbp), %rdi
0x1000ec13c:  movq   %rax, -0x18(%rbp)
0x1000ec140:  callq  0x1000efd8c               ; symbol stub for: object_getClass
0x1000ec145:  movq   -0x10(%rbp), %rdi
0x1000ec149:  callq  *0x50(%rax)
0x1000ec14c:  movq   -0x10(%rbp), %rdi
0x1000ec150:  movq   %rax, -0x20(%rbp)
0x1000ec154:  movq   %rdx, -0x28(%rbp)
0x1000ec158:  movq   %rcx, -0x30(%rbp)
0x1000ec15c:  callq  0x1000efd86               ; symbol stub for: objc_retain
0x1000ec161:  movq   -0x10(%rbp), %rcx
0x1000ec165:  movq   %rcx, %rdi
0x1000ec168:  movq   %rax, -0x38(%rbp)
0x1000ec16c:  callq  0x1000efd8c               ; symbol stub for: object_getClass
0x1000ec171:  movq   0x4ed8(%rip), %rcx        ; (void *)0x00000001047bd8b0: direct type metadata for Swift.Int
0x1000ec178:  addq   $0x8, %rcx
0x1000ec17f:  movq   0x4ef2(%rip), %rdx        ; (void *)0x00000001047b6fc8: protocol witness table for Swift.Int : Swift.Equatable
0x1000ec186:  movq   -0x10(%rbp), %rdi
0x1000ec18a:  movq   %rdx, -0x40(%rbp)
0x1000ec18e:  movq   %rcx, -0x48(%rbp)
0x1000ec192:  callq  *0x58(%rax)
0x1000ec195:  movq   -0x20(%rbp), %rdi
0x1000ec199:  movq   -0x28(%rbp), %rsi
0x1000ec19d:  movq   -0x30(%rbp), %r8
0x1000ec1a1:  movq   %rdx, -0x50(%rbp)
0x1000ec1a5:  movq   %r8, %rdx
0x1000ec1a8:  movq   %rcx, -0x58(%rbp)
0x1000ec1ac:  movq   %rax, %rcx
0x1000ec1af:  movq   -0x50(%rbp), %r8
0x1000ec1b3:  movq   -0x58(%rbp), %r9
0x1000ec1b7:  movq   -0x48(%rbp), %rax
0x1000ec1bb:  movq   %rax, (%rsp)
0x1000ec1bf:  movq   -0x40(%rbp), %r10
0x1000ec1c3:  movq   %r10, 0x8(%rsp)
0x1000ec1c8:  callq  0x1000efc3c               ; symbol stub for: Swift.== @infix <A : Swift.Equatable>(Swift.Slice<A>, Swift.Slice<A>) -> Swift.Bool
0x1000ec1cd:  movq   -0x10(%rbp), %rdi
0x1000ec1d1:  movb   %al, -0x59(%rbp)
0x1000ec1d4:  callq  0x1000efd86               ; symbol stub for: objc_retain
0x1000ec1d9:  movb   -0x59(%rbp), %r11b
0x1000ec1dd:  movzbl %r11b, %edi
0x1000ec1e1:  movq   %rax, -0x68(%rbp)
0x1000ec1e5:  callq  0x1000efdb0               ; symbol stub for: ObjectiveC._convertBoolToObjCBool (Swift.Bool) -> ObjectiveC.ObjCBool
0x1000ec1ea:  movq   0x552f(%rip), %rsi        ; "setB2:"
0x1000ec1f1:  movq   -0x10(%rbp), %rcx
0x1000ec1f5:  movq   %rcx, %rdi
0x1000ec1f8:  movzbl %al, %edx
0x1000ec1fb:  callq  0x1000efd6e               ; symbol stub for: objc_msgSend
0x1000ec200:  movq   -0x10(%rbp), %rdi
0x1000ec204:  callq  0x1000efd80               ; symbol stub for: objc_release
0x1000ec209:  movq   -0x10(%rbp), %rdi
0x1000ec20d:  callq  0x1000efd80               ; symbol stub for: objc_release
0x1000ec212:  addq   $0x80, %rsp
0x1000ec219:  popq   %rbp
0x1000ec21a:  retq   

It is not hard to spot this line

0x1000ec1c8:  callq  0x1000efc3c               ; symbol stub for: Swift.== @infix <A : Swift.Equatable>(Swift.Slice<A>, Swift.Slice<A>) -> Swift.Bool

overloaded operator == is called to compare the value


Now for buggy code

func testSlice1() {
    b1 = s1[0..1] == s2
}

Xcode give me this

test2Tests`test2Tests.VariableTests.testSlice1 (test2Tests.VariableTests)() -> () at test2Tests.swift:34:
0x1000ebec0:  pushq  %rbp
0x1000ebec1:  movq   %rsp, %rbp
0x1000ebec4:  pushq  %r14
0x1000ebec6:  pushq  %rbx
0x1000ebec7:  subq   $0xf0, %rsp
0x1000ebece:  movq   %rdi, -0x18(%rbp)
0x1000ebed2:  movq   %rdi, -0x40(%rbp)
0x1000ebed6:  callq  0x1000efd86               ; symbol stub for: objc_retain
0x1000ebedb:  movq   -0x40(%rbp), %rdi
0x1000ebedf:  movq   %rax, -0x48(%rbp)
0x1000ebee3:  callq  0x1000efd8c               ; symbol stub for: object_getClass
0x1000ebee8:  movq   0x5161(%rip), %rdi        ; (void *)0x00000001006cb8b0: direct type metadata for Swift.Int
0x1000ebeef:  addq   $0x8, %rdi
0x1000ebef6:  movq   0x5163(%rip), %r8         ; (void *)0x00000001006c5158: protocol witness table for Swift.Int : Swift.ForwardIndex
0x1000ebefd:  movq   0x5164(%rip), %rcx        ; (void *)0x00000001006c5120: protocol witness table for Swift.Int : Swift._SignedInteger
0x1000ebf04:  movq   0x5165(%rip), %rdx        ; (void *)0x00000001006c4f90: protocol witness table for Swift.Int : Swift._BuiltinIntegerLiteralConvertible
0x1000ebf0b:  leaq   -0x38(%rbp), %rsi
0x1000ebf0f:  leaq   -0x28(%rbp), %r9
0x1000ebf13:  leaq   -0x20(%rbp), %r10
0x1000ebf17:  movq   -0x40(%rbp), %r11
0x1000ebf1b:  movq   %rdi, -0x50(%rbp)
0x1000ebf1f:  movq   %r11, %rdi
0x1000ebf22:  movq   %r10, -0x58(%rbp)
0x1000ebf26:  movq   %r8, -0x60(%rbp)
0x1000ebf2a:  movq   %rcx, -0x68(%rbp)
0x1000ebf2e:  movq   %rdx, -0x70(%rbp)
0x1000ebf32:  movq   %rsi, -0x78(%rbp)
0x1000ebf36:  movq   %r9, -0x80(%rbp)
0x1000ebf3a:  callq  *0x50(%rax)
0x1000ebf3d:  movq   $0x0, -0x20(%rbp)
0x1000ebf45:  movq   $0x1, -0x28(%rbp)
0x1000ebf4d:  movq   -0x58(%rbp), %rsi
0x1000ebf51:  movq   -0x80(%rbp), %rdi
0x1000ebf55:  movq   -0x78(%rbp), %r8
0x1000ebf59:  movq   %rdi, -0x88(%rbp)
0x1000ebf60:  movq   %r8, %rdi
0x1000ebf63:  movq   -0x88(%rbp), %r8
0x1000ebf6a:  movq   %rdx, -0x90(%rbp)
0x1000ebf71:  movq   %r8, %rdx
0x1000ebf74:  movq   -0x50(%rbp), %r9
0x1000ebf78:  movq   %rcx, -0x98(%rbp)
0x1000ebf7f:  movq   %r9, %rcx
0x1000ebf82:  movq   -0x60(%rbp), %r8
0x1000ebf86:  movq   -0x68(%rbp), %r10
0x1000ebf8a:  movq   %r10, (%rsp)
0x1000ebf8e:  movq   -0x50(%rbp), %r11
0x1000ebf92:  movq   %r11, 0x8(%rsp)
0x1000ebf97:  movq   %r11, 0x10(%rsp)
0x1000ebf9c:  movq   -0x70(%rbp), %rbx
0x1000ebfa0:  movq   %rbx, 0x18(%rsp)
0x1000ebfa5:  movq   %rax, -0xa0(%rbp)
0x1000ebfac:  callq  0x1000ecac0               ; Swift... @infix <A : Swift.ForwardIndex>(A, A) -> Swift.Range<A>
0x1000ebfb1:  movq   0x5098(%rip), %rax        ; (void *)0x00000001006cb8b0: direct type metadata for Swift.Int
0x1000ebfb8:  addq   $0x8, %rax
0x1000ebfbe:  movq   -0x38(%rbp), %rdi
0x1000ebfc2:  movq   -0x30(%rbp), %rsi
0x1000ebfc6:  movq   -0xa0(%rbp), %rdx
0x1000ebfcd:  movq   -0x90(%rbp), %rcx
0x1000ebfd4:  movq   -0x98(%rbp), %r8
0x1000ebfdb:  movq   %rax, %r9
0x1000ebfde:  callq  0x1000efc72               ; symbol stub for: Swift.Slice.subscript.getter (Swift.Range<Swift.Int>) -> Swift.Slice<A>
0x1000ebfe3:  movq   0x5066(%rip), %rsi        ; (void *)0x00000001006cb8b0: direct type metadata for Swift.Int
0x1000ebfea:  addq   $0x8, %rsi
0x1000ebff1:  movq   %rax, %rdi
0x1000ebff4:  movq   %rsi, -0xa8(%rbp)
0x1000ebffb:  movq   %rdx, %rsi
0x1000ebffe:  movq   %rcx, %rdx
0x1000ec001:  movq   -0xa8(%rbp), %rcx
0x1000ec008:  callq  0x1000ec910               ; Swift.Slice.__conversion <A>(Swift.Slice<A>)() -> Swift.CConstVoidPointer
0x1000ec00d:  movq   -0x40(%rbp), %rdi
0x1000ec011:  movq   %rax, -0xb0(%rbp)
0x1000ec018:  movq   %rdx, -0xb8(%rbp)
0x1000ec01f:  callq  0x1000efd86               ; symbol stub for: objc_retain
0x1000ec024:  movq   -0x40(%rbp), %rcx
0x1000ec028:  movq   %rcx, %rdi
0x1000ec02b:  movq   %rax, -0xc0(%rbp)
0x1000ec032:  callq  0x1000efd8c               ; symbol stub for: object_getClass
0x1000ec037:  movq   0x5012(%rip), %rcx        ; (void *)0x00000001006cb8b0: direct type metadata for Swift.Int
0x1000ec03e:  addq   $0x8, %rcx
0x1000ec045:  movq   -0x40(%rbp), %rdi
0x1000ec049:  movq   %rcx, -0xc8(%rbp)
0x1000ec050:  callq  *0x58(%rax)
0x1000ec053:  movq   %rax, %rdi
0x1000ec056:  movq   %rdx, %rsi
0x1000ec059:  movq   %rcx, %rdx
0x1000ec05c:  movq   -0xc8(%rbp), %rcx
0x1000ec063:  callq  0x1000ec910               ; Swift.Slice.__conversion <A>(Swift.Slice<A>)() -> Swift.CConstVoidPointer
0x1000ec068:  movq   -0xb8(%rbp), %rcx
0x1000ec06f:  cmpq   %rdx, %rcx
0x1000ec072:  sete   %r14b
0x1000ec076:  movq   %rax, %rdi
0x1000ec079:  movb   %r14b, -0xc9(%rbp)
0x1000ec080:  callq  0x1000efd80               ; symbol stub for: objc_release
0x1000ec085:  movq   -0xb0(%rbp), %rdi
0x1000ec08c:  callq  0x1000efd80               ; symbol stub for: objc_release
0x1000ec091:  movq   -0x40(%rbp), %rdi
0x1000ec095:  callq  0x1000efd86               ; symbol stub for: objc_retain
0x1000ec09a:  movb   -0xc9(%rbp), %r14b
0x1000ec0a1:  movzbl %r14b, %edi
0x1000ec0a5:  movq   %rax, -0xd8(%rbp)
0x1000ec0ac:  callq  0x1000efdb0               ; symbol stub for: ObjectiveC._convertBoolToObjCBool (Swift.Bool) -> ObjectiveC.ObjCBool
0x1000ec0b1:  movq   0x5660(%rip), %rsi        ; "setB1:"
0x1000ec0b8:  movq   -0x40(%rbp), %rcx
0x1000ec0bc:  movq   %rcx, %rdi
0x1000ec0bf:  movzbl %al, %edx
0x1000ec0c2:  callq  0x1000efd6e               ; symbol stub for: objc_msgSend
0x1000ec0c7:  movq   -0x40(%rbp), %rdi
0x1000ec0cb:  callq  0x1000efd80               ; symbol stub for: objc_release
0x1000ec0d0:  movq   -0x40(%rbp), %rdi
0x1000ec0d4:  callq  0x1000efd80               ; symbol stub for: objc_release
0x1000ec0d9:  addq   $0xf0, %rsp
0x1000ec0e0:  popq   %rbx
0x1000ec0e1:  popq   %r14
0x1000ec0e3:  popq   %rbp
0x1000ec0e4:  retq   

I am not expert of assembly code, but a quick scan through the generated comment, I can't see == anywhere.

Also you can these in the assembly

0x1000ec008:  callq  0x1000ec910               ; Swift.Slice.__conversion <A>(Swift.Slice<A>)() -> Swift.CConstVoidPointer

0x1000ec063:  callq  0x1000ec910               ; Swift.Slice.__conversion <A>(Swift.Slice<A>)() -> Swift.CConstVoidPointer

so I think it is comparing the underlying pointer value instead of the actual content of the slices

like image 165
Bryan Chen Avatar answered Oct 15 '22 11:10

Bryan Chen