Skip to content

Commit e473977

Browse files
authored
Merge pull request #81458 from lorentey/range-contains-perf
[stdlib] Mark `contains` methods on `Range/ClosedRange` transparent
2 parents d787888 + ff3cf98 commit e473977

File tree

3 files changed

+52
-4
lines changed

3 files changed

+52
-4
lines changed

stdlib/public/core/ClosedRange.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ extension ClosedRange: RangeExpression {
129129
/// - Returns: `true` if `element` is contained in the range; otherwise,
130130
/// `false`.
131131
@inlinable
132+
@inline(__always)
132133
public func contains(_ element: Bound) -> Bool {
133134
return element >= self.lowerBound && element <= self.upperBound
134135
}
@@ -383,6 +384,7 @@ extension ClosedRange {
383384
///
384385
/// - Complexity: O(1)
385386
@_alwaysEmitIntoClient
387+
@_transparent
386388
public func contains(_ other: ClosedRange<Bound>) -> Bool {
387389
lowerBound <= other.lowerBound && upperBound >= other.upperBound
388390
}

stdlib/public/core/Range.swift

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,10 @@ extension RangeExpression {
9494
/// - pattern: A range.
9595
/// - bound: A value to match against `pattern`.
9696
@inlinable
97+
@inline(__always)
9798
public static func ~= (pattern: Self, value: Bound) -> Bool {
9899
return pattern.contains(value)
99-
}
100+
}
100101
}
101102

102103
/// A half-open interval from a lower bound up to, but not including, an upper
@@ -194,7 +195,7 @@ public struct Range<Bound: Comparable> {
194195
/// - Parameter element: The element to check for containment.
195196
/// - Returns: `true` if `element` is contained in the range; otherwise,
196197
/// `false`.
197-
@inlinable
198+
@_transparent
198199
public func contains(_ element: Bound) -> Bool {
199200
return lowerBound <= element && element < upperBound
200201
}
@@ -682,7 +683,7 @@ extension PartialRangeFrom: RangeExpression {
682683
) -> Range<Bound> where C.Index == Bound {
683684
return self.lowerBound..<collection.endIndex
684685
}
685-
@inlinable // trivial-implementation
686+
@_transparent
686687
public func contains(_ element: Bound) -> Bool {
687688
return lowerBound <= element
688689
}
@@ -1074,11 +1075,12 @@ extension Range {
10741075
///
10751076
/// - Complexity: O(1)
10761077
@_alwaysEmitIntoClient
1078+
@_transparent
10771079
public func contains(_ other: Range<Bound>) -> Bool {
10781080
other.isEmpty ||
10791081
(lowerBound <= other.lowerBound && upperBound >= other.upperBound)
10801082
}
1081-
1083+
10821084
/// Returns a Boolean value indicating whether the given closed range is
10831085
/// contained within this range.
10841086
///
@@ -1101,6 +1103,7 @@ extension Range {
11011103
///
11021104
/// - Complexity: O(1)
11031105
@_alwaysEmitIntoClient
1106+
@_transparent
11041107
public func contains(_ other: ClosedRange<Bound>) -> Bool {
11051108
lowerBound <= other.lowerBound && upperBound > other.upperBound
11061109
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// RUN: %target-swift-emit-ir -primary-file %s 2>&1 | %FileCheck %s
2+
// RUN: %target-swift-emit-ir -primary-file %s -O 2>&1 | %FileCheck --check-prefixes CHECK,CHECK-OPTIMIZED %s
3+
// RUN: %target-swift-emit-ir -primary-file %s -Osize 2>&1 | %FileCheck --check-prefixes CHECK,CHECK-OPTIMIZED %s
4+
5+
// We expect calls to `Range.contains` to result in direct bound comparisons in
6+
// all compilation modes, including unoptimized builds. (These are often used to
7+
// implement bounds checking.)
8+
//
9+
// The sample functions below use bounds of different integer types to avoid
10+
// them tail-calling each other.
11+
12+
// CHECK-LABEL: define {{.*}} i1 @"$s21RangeContainsInlining08halfOpenB0ySbSnys4Int8VG_ADtF"
13+
// CHECK-NOT: call swiftcc
14+
// CHECK: icmp
15+
// CHECK-NOT: call swiftcc
16+
// CHECK-LABEL: {{^}}}
17+
public func halfOpenContains(_ r: Range<Int8>, _ i: Int8) -> Bool {
18+
r.contains(i)
19+
}
20+
21+
// `ClosedRange.contains is only marked `@inline(__always)`; unfortunately it
22+
// doesn't get inlined in deug builds.
23+
24+
// CHECK-OPTIMIZED-LABEL: define {{.*}} i1 @"$s21RangeContainsInlining06closedB0ySbSNys5Int16VG_ADtF"
25+
// CHECK-OPTIMIZED-NOT: call swiftcc
26+
// CHECK-OPTIMIZED: icmp
27+
// CHECK-OPTIMIZED-NOT: call swiftcc
28+
// CHECK-OPTIMIZED-LABEL: {{^}}}
29+
public func closedContains(_ r: ClosedRange<Int16>, _ i: Int16) -> Bool {
30+
r.contains(i)
31+
}
32+
33+
// `Range.~=` is only marked `@inline(__always)`; unfortunately it doesn't get
34+
// inlined in deug builds.
35+
36+
// CHECK-OPTIMIZED-LABEL: define {{.*}} i1 @"$s21RangeContainsInlining20halfOpenPatternMatchySbSnys5Int32VG_ADtF"
37+
// CHECK-OPTIMIZED-NOT: call swiftcc
38+
// CHECK-OPTIMIZED: icmp
39+
// CHECK-OPTIMIZED-NOT: call swiftcc
40+
// CHECK-OPTIMIZED-LABEL: {{^}}}
41+
public func halfOpenPatternMatch(_ r: Range<Int32>, _ i: Int32) -> Bool {
42+
r ~= i
43+
}

0 commit comments

Comments
 (0)