Skip to content

Commit ee9b38c

Browse files
Use an explicit unsigned comparison for span index checks (#83150)
LLVM ought to be able to do this transformation for us, but it currently fails to do so. We can code around it easily enough. #83172 has a better long-term fix.
1 parent cb0a21d commit ee9b38c

File tree

3 files changed

+74
-3
lines changed

3 files changed

+74
-3
lines changed

stdlib/public/core/Span/MutableSpan.swift

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -290,12 +290,18 @@ extension MutableSpan where Element: ~Copyable {
290290
@_alwaysEmitIntoClient
291291
public subscript(_ position: Index) -> Element {
292292
unsafeAddress {
293-
_precondition(indices.contains(position), "index out of bounds")
293+
_precondition(
294+
UInt(bitPattern: position) < UInt(bitPattern: _count),
295+
"Index out of bounds"
296+
)
294297
return unsafe UnsafePointer(_unsafeAddressOfElement(unchecked: position))
295298
}
296299
@lifetime(self: copy self)
297300
unsafeMutableAddress {
298-
_precondition(indices.contains(position), "index out of bounds")
301+
_precondition(
302+
UInt(bitPattern: position) < UInt(bitPattern: _count),
303+
"Index out of bounds"
304+
)
299305
return unsafe _unsafeAddressOfElement(unchecked: position)
300306
}
301307
}

stdlib/public/core/Span/Span.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,10 @@ extension Span where Element: ~Copyable {
419419
@inline(__always)
420420
@_alwaysEmitIntoClient
421421
internal func _checkIndex(_ position: Index) {
422-
_precondition(indices.contains(position), "Index out of bounds")
422+
_precondition(
423+
UInt(bitPattern: position) < UInt(bitPattern: _count),
424+
"Index out of bounds"
425+
)
423426
}
424427

425428
/// Accesses the element at the specified position in the `Span`.
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
//===--- BoundsCheckOptimization.swift ------------------------*- swift -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2025 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
// RUN: %target-swift-frontend -primary-file %s -O -emit-assembly | %FileCheck %s --check-prefix CHECK --check-prefix CHECK-%target-cpu
14+
// REQUIRES: swift_stdlib_no_asserts
15+
16+
import Swift
17+
18+
func read(index: Int, span: Span<UInt8>) -> UInt8 {
19+
span[index]
20+
}
21+
// CHECK: s23BoundsCheckOptimization4read5index4spans5UInt8VSi_s4SpanVyAFGtF:
22+
23+
// CHECK-arm64-NOT: tbnz
24+
// CHECK-arm64: cmp
25+
// all unsigned comparison spellings?
26+
// CHECK-arm64-NEXT: b.{{hs|hi|ls|lo|cc|cs}}
27+
// CHECK-arm64-NEXT: ldrb
28+
// CHECK-arm64-NEXT: ret
29+
30+
// CHECK-x86_64-NOT: test
31+
// CHECK-x86_64: cmp
32+
// all unsigned comparison spellings?
33+
// CHECK-x86_64-NEXT: j{{a|ae|b|be|c|na|nae|nb|nbe|nc}}
34+
// CHECK-x86_64-NEXT: movzb
35+
// x86_64 might have a frame pointer operation before ret
36+
37+
func write(value: UInt8, index: Int, span: inout MutableSpan<UInt8>) {
38+
span[index] = value
39+
}
40+
// CHECK: s23BoundsCheckOptimization5write5value5index4spanys5UInt8V_Sis11MutableSpanVyAGGztF:
41+
42+
// CHECK-arm64-NOT: tbnz
43+
// CHECK-arm64: cmp
44+
// all unsigned comparison spellings?
45+
// CHECK-arm64-NEXT: b.{{hs|hi|ls|lo|cc|cs}}
46+
// no second compare
47+
// CHECK-arm64-NOT: cmp
48+
// no test after compare
49+
// CHECK-arm64-NOT: tbnz
50+
// CHECK-arm64: strb
51+
// CHECK-arm64-NEXT: ret
52+
53+
// CHECK-x86_64-NOT: test
54+
// CHECK-x86_64: cmp
55+
// all unsigned comparison spellings?
56+
// CHECK-x86_64-NEXT: j{{a|ae|b|be|c|na|nae|nb|nbe|nc}}
57+
// no second compare
58+
// CHECK-x86_64-NOT: cmp
59+
// no test after compare
60+
// CHECK-x86_64-NOT: test
61+
// CHECK-x86_64: movb
62+
// x86_64 might have a frame pointer operation before ret

0 commit comments

Comments
 (0)