Skip to content

Commit b4a23ad

Browse files
[stdlib] Update Array.subscript to use _modify (#19154)
* Switch Array to use subscript _modify * Add _modify to ContiguousArray * XFAIL linux failing test
1 parent 1e7dae3 commit b4a23ad

File tree

6 files changed

+27
-127
lines changed

6 files changed

+27
-127
lines changed

Diff for: stdlib/public/core/Array.swift

+3-27
Original file line numberDiff line numberDiff line change
@@ -570,10 +570,11 @@ extension Array: RandomAccessCollection, MutableCollection {
570570
index, wasNativeTypeChecked: wasNativeTypeChecked,
571571
matchingSubscriptCheck: token)
572572
}
573-
mutableAddressWithNativeOwner {
573+
_modify {
574574
_makeMutableAndUnique() // makes the array native, too
575575
_checkSubscript_native(index)
576-
return (_getElementAddress(index), _getOwner_native())
576+
let address = _buffer.subscriptBaseAddress + index
577+
yield &address.pointee
577578
}
578579
}
579580

@@ -646,31 +647,6 @@ extension Array {
646647
return _buffer.capacity
647648
}
648649

649-
/// - Precondition: The array has a native buffer.
650-
@inlinable
651-
@_semantics("array.owner")
652-
internal func _getOwnerWithSemanticLabel_native() -> Builtin.NativeObject {
653-
return Builtin.unsafeCastToNativeObject(_buffer.nativeOwner)
654-
}
655-
656-
/// - Precondition: The array has a native buffer.
657-
@inlinable
658-
@inline(__always)
659-
internal func _getOwner_native() -> Builtin.NativeObject {
660-
#if _runtime(_ObjC)
661-
if _isClassOrObjCExistential(Element.self) {
662-
// We are hiding the access to '_buffer.owner' behind
663-
// _getOwner() to help the compiler hoist uniqueness checks in
664-
// the case of class or Objective-C existential typed array
665-
// elements.
666-
return _getOwnerWithSemanticLabel_native()
667-
}
668-
#endif
669-
// In the value typed case the extra call to
670-
// _getOwnerWithSemanticLabel_native hinders optimization.
671-
return Builtin.unsafeCastToNativeObject(_buffer.owner)
672-
}
673-
674650
@inlinable
675651
@_semantics("array.make_mutable")
676652
internal mutating func _makeMutableAndUnique() {

Diff for: stdlib/public/core/ContiguousArray.swift

+6-83
Original file line numberDiff line numberDiff line change
@@ -286,24 +286,14 @@ extension ContiguousArray: RandomAccessCollection, MutableCollection {
286286
@inlinable
287287
public subscript(index: Int) -> Element {
288288
get {
289-
// This call may be hoisted or eliminated by the optimizer. If
290-
// there is an inout violation, this value may be stale so needs to be
291-
// checked again below.
292-
let wasNativeTypeChecked = _hoistableIsNativeTypeChecked()
293-
294-
// Make sure the index is in range and wasNativeTypeChecked is
295-
// still valid.
296-
let token = _checkSubscript(
297-
index, wasNativeTypeChecked: wasNativeTypeChecked)
298-
299-
return _getElement(
300-
index, wasNativeTypeChecked: wasNativeTypeChecked,
301-
matchingSubscriptCheck: token)
289+
_checkSubscript_native(index)
290+
return _buffer.getElement(index)
302291
}
303-
mutableAddressWithNativeOwner {
304-
_makeMutableAndUnique() // makes the array native, too
292+
_modify {
293+
_makeMutableAndUnique()
305294
_checkSubscript_native(index)
306-
return (_getElementAddress(index), _getOwner_native())
295+
let address = _buffer.subscriptBaseAddress + index
296+
yield &address.pointee
307297
}
308298
}
309299

@@ -353,17 +343,6 @@ extension ContiguousArray: RandomAccessCollection, MutableCollection {
353343

354344
//===--- private helpers---------------------------------------------------===//
355345
extension ContiguousArray {
356-
/// Returns `true` if the array is native and does not need a deferred
357-
/// type check. May be hoisted by the optimizer, which means its
358-
/// results may be stale by the time they are used if there is an
359-
/// inout violation in user code.
360-
@inlinable
361-
@_semantics("array.props.isNativeTypeChecked")
362-
public // @testable
363-
func _hoistableIsNativeTypeChecked() -> Bool {
364-
return _buffer.arrayPropertyIsNativeTypeChecked
365-
}
366-
367346
@inlinable
368347
@_semantics("array.get_count")
369348
internal func _getCount() -> Int {
@@ -376,31 +355,6 @@ extension ContiguousArray {
376355
return _buffer.capacity
377356
}
378357

379-
/// - Precondition: The array has a native buffer.
380-
@inlinable
381-
@_semantics("array.owner")
382-
internal func _getOwnerWithSemanticLabel_native() -> Builtin.NativeObject {
383-
return Builtin.unsafeCastToNativeObject(_buffer.nativeOwner)
384-
}
385-
386-
/// - Precondition: The array has a native buffer.
387-
@inlinable
388-
@inline(__always)
389-
internal func _getOwner_native() -> Builtin.NativeObject {
390-
#if _runtime(_ObjC)
391-
if _isClassOrObjCExistential(Element.self) {
392-
// We are hiding the access to '_buffer.owner' behind
393-
// _getOwner() to help the compiler hoist uniqueness checks in
394-
// the case of class or Objective-C existential typed array
395-
// elements.
396-
return _getOwnerWithSemanticLabel_native()
397-
}
398-
#endif
399-
// In the value typed case the extra call to
400-
// _getOwnerWithSemanticLabel_native hinders optimization.
401-
return Builtin.unsafeCastToNativeObject(_buffer.owner)
402-
}
403-
404358
@inlinable
405359
@_semantics("array.make_mutable")
406360
internal mutating func _makeMutableAndUnique() {
@@ -417,22 +371,6 @@ extension ContiguousArray {
417371
_buffer._checkValidSubscript(index)
418372
}
419373

420-
/// Check that the given `index` is valid for subscripting, i.e.
421-
/// `0 ≤ index < count`.
422-
@inlinable
423-
@_semantics("array.check_subscript")
424-
public // @testable
425-
func _checkSubscript(
426-
_ index: Int, wasNativeTypeChecked: Bool
427-
) -> _DependenceToken {
428-
#if _runtime(_ObjC)
429-
_buffer._checkValidSubscript(index)
430-
#else
431-
_buffer._checkValidSubscript(index)
432-
#endif
433-
return _DependenceToken()
434-
}
435-
436374
/// Check that the specified `index` is valid, i.e. `0 ≤ index ≤ count`.
437375
@inlinable
438376
@_semantics("array.check_index")
@@ -441,21 +379,6 @@ extension ContiguousArray {
441379
_precondition(index >= startIndex, "Negative ContiguousArray index is out of range")
442380
}
443381

444-
@_semantics("array.get_element")
445-
@inline(__always)
446-
public // @testable
447-
func _getElement(
448-
_ index: Int,
449-
wasNativeTypeChecked: Bool,
450-
matchingSubscriptCheck: _DependenceToken
451-
) -> Element {
452-
#if false
453-
return _buffer.getElement(index, wasNativeTypeChecked: wasNativeTypeChecked)
454-
#else
455-
return _buffer.getElement(index)
456-
#endif
457-
}
458-
459382
@inlinable
460383
@_semantics("array.get_element_address")
461384
internal func _getElementAddress(_ index: Int) -> UnsafeMutablePointer<Element> {

Diff for: test/Interpreter/formal_access.swift

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// REQUIRES: executable_test
33
// REQUIRES: swift_test_mode_optimize_none
44

5+
// REQUIRES: rdar44160503
6+
57
class C: CustomStringConvertible {
68
var value: Int
79
init(_ v: Int) { value = v }

Diff for: test/SILGen/tuples.swift

+8-11
Original file line numberDiff line numberDiff line change
@@ -159,22 +159,19 @@ extension P {
159159

160160
// CHECK-LABEL: sil @$S6tuples15testTupleAssign1xySaySiGz_tF : $@convention(thin) (@inout Array<Int>) -> () {
161161
// CHECK: [[ACCESS:%.*]] = begin_access [modify] [unknown] %0 : $*Array<Int>
162-
// function_ref Array.subscript.nativeOwningMutableAddressor
163-
// CHECK: [[ADDRESSOR:%.*]] = function_ref @$SSayxSiciao : $@convention(method) <τ_0_0> (Int, @inout Array<τ_0_0>) -> (UnsafeMutablePointer<τ_0_0>, @owned Builtin.NativeObject)
164-
// CHECK: [[TUPLE:%.*]] = apply [[ADDRESSOR]]<Int>(%{{.*}}, [[ACCESS]]) : $@convention(method) <τ_0_0> (Int, @inout Array<τ_0_0>) -> (UnsafeMutablePointer<τ_0_0>, @owned Builtin.NativeObject)
165-
// CHECK: ([[PTR:%.*]], [[OBJ:%.*]]) = destructure_tuple [[TUPLE]] : $(UnsafeMutablePointer<Int>, Builtin.NativeObject)
162+
// function_ref Array.subscript.modify
163+
// CHECK: [[ACCESSOR:%.*]] = function_ref @$SSayxSiciM : $@yield_once @convention(method) <τ_0_0> (Int, @inout Array<τ_0_0>) -> @yields @inout τ_0_0
164+
// CHECK: ([[VALUE:%.*]], [[TOKEN:%.*]]) = begin_apply [[ACCESSOR]]<Int>(%{{.*}}, [[ACCESS]]) : $@yield_once @convention(method) <τ_0_0> (Int, @inout Array<τ_0_0>) -> @yields @inout τ_0_0
166165
// CHECK: assign %{{.*}} to %{{.*}} : $*Int
166+
// CHECK: end_apply [[TOKEN]]
167167
// CHECK: end_access [[ACCESS]] : $*Array<Int>
168-
// CHECK: destroy_value [[OBJ]] : $Builtin.NativeObject
169-
//
170168
// CHECK: [[ACCESS:%.*]] = begin_access [modify] [unknown] %0 : $*Array<Int>
171-
// function_ref Array.subscript.nativeOwningMutableAddressor
172-
// CHECK: [[ADDRESSOR:%.*]] = function_ref @$SSayxSiciao : $@convention(method) <τ_0_0> (Int, @inout Array<τ_0_0>) -> (UnsafeMutablePointer<τ_0_0>, @owned Builtin.NativeObject)
173-
// CHECK: [[TUPLE:%.*]] = apply [[ADDRESSOR]]<Int>(%{{.*}}, [[ACCESS]]) : $@convention(method) <τ_0_0> (Int, @inout Array<τ_0_0>) -> (UnsafeMutablePointer<τ_0_0>, @owned Builtin.NativeObject)
174-
// CHECK: ([[PTR:%.*]], [[OBJ:%.*]]) = destructure_tuple [[TUPLE]] : $(UnsafeMutablePointer<Int>, Builtin.NativeObject)
169+
// function_ref Array.subscript.modify
170+
// CHECK: [[ACCESSOR:%.*]] = function_ref @$SSayxSiciM : $@yield_once @convention(method) <τ_0_0> (Int, @inout Array<τ_0_0>) -> @yields @inout τ_0_0
171+
// CHECK: ([[VALUE:%.*]], [[TOKEN:%.*]]) = begin_apply [[ACCESSOR]]<Int>(%{{.*}}, [[ACCESS]]) : $@yield_once @convention(method) <τ_0_0> (Int, @inout Array<τ_0_0>) -> @yields @inout τ_0_0
175172
// CHECK: assign %{{.*}} to %{{.*}} : $*Int
173+
// CHECK: end_apply [[TOKEN]]
176174
// CHECK: end_access [[ACCESS]] : $*Array<Int>
177-
// CHECK: destroy_value [[OBJ]] : $Builtin.NativeObject
178175
// CHECK-LABEL: } // end sil function '$S6tuples15testTupleAssign1xySaySiGz_tF'
179176
public func testTupleAssign(x: inout [Int]) {
180177
(x[0], x[1]) = (0, 1)

Diff for: test/SILGen/writeback_conflict_diagnostics.swift

+6-6
Original file line numberDiff line numberDiff line change
@@ -71,12 +71,12 @@ var global_array : [[Int]]
7171

7272
func testMultiArray(_ i : Int, j : Int, array : [[Int]]) {
7373
var array = array
74-
swap(&array[i][j],
75-
&array[i][i])
76-
swap(&array[0][j],
77-
&array[0][i])
78-
swap(&global_array[0][j],
79-
&global_array[0][i])
74+
swap(&array[i][j], // expected-note {{concurrent writeback occurred here}}
75+
&array[i][i]) // expected-error {{inout writeback through subscript occurs in multiple arguments to call, introducing invalid aliasing}}
76+
swap(&array[0][j], // expected-note {{concurrent writeback occurred here}}
77+
&array[0][i]) // expected-error {{inout writeback through subscript occurs in multiple arguments to call, introducing invalid aliasing}}
78+
swap(&global_array[0][j], // expected-note {{concurrent writeback occurred here}}
79+
&global_array[0][i]) // expected-error {{inout writeback through subscript occurs in multiple arguments to call, introducing invalid aliasing}}
8080

8181
// TODO: This is obviously the same writeback problem, but isn't detectable
8282
// with the current level of sophistication in SILGen.

Diff for: test/SILOptimizer/array_mutable_assertonly.swift

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
// RUN: %target-swift-frontend -O -emit-sil -Xllvm -debug-only=cowarray-opts -primary-file %s 2>&1 | %FileCheck %s --check-prefix=TEST13
1414
// REQUIRES: asserts,swift_stdlib_no_asserts,optimized_stdlib
1515

16+
// XFAIL: linux
17+
1618
// TEST1-LABEL: COW Array Opts in Func {{.*}}inoutarr{{.*}}
1719
// TEST1: Hoisting make_mutable
1820
// TEST1: COW Array Opts

0 commit comments

Comments
 (0)