Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[stdlib] Implement Atomics module #30553

Closed
wants to merge 42 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
0723a38
[stdlib] Add an Atomics module for low-level atomic operations
lorentey Mar 21, 2020
694bbb7
[Atomics][nfc] Fix file headers
lorentey Mar 23, 2020
3f4b6fb
[Atomics][nfc] Prefer Self to spelling out the type name
lorentey Mar 23, 2020
84eb34f
[Atomics] Add default value of 1 for increment/decrement
lorentey Mar 23, 2020
9b05757
[Atomics] Add @_nonEphemeral attribute to pointer-based initializers
lorentey Mar 23, 2020
f4006a6
[Atomics] Make .sequentiallyConsistent the default ordering
lorentey Mar 24, 2020
63f041e
Merge branch 'master' into atomics2
lorentey Mar 25, 2020
a32ef12
[Atomics] Introduce AtomicProtocol
lorentey Mar 26, 2020
6c4a639
[Atomics] Add AtomicRepresentable
lorentey Mar 28, 2020
9576528
[Atomics] Switch back to no default ordering
lorentey Mar 28, 2020
f123869
[Atomics] UnsafeAtomicLazyReference.initialize(to:) ⟹ .storeIfNil(_:)
lorentey Mar 28, 2020
40d349b
[Atomics] Put address argument after operands in primitive atomics
lorentey Mar 30, 2020
0a263f6
[Atomics] Add support for immutable pointer types
lorentey Mar 30, 2020
8eda0c8
[Atomics] Simplify gyb
lorentey Mar 30, 2020
eb061c7
[Atomics][test][NFC] Move atomics tests to separate subdirectory
lorentey Mar 31, 2020
6f685a4
[Atomics][test] Verify that atomic ops get emitted as direct builtin …
lorentey Mar 31, 2020
4f1e412
[Atomics] Minor edits, code reorganization
lorentey Mar 31, 2020
870a05d
[Atomics] UnsafeAtomicPointer<Value> ⟹ UnsafeAtomic<Value>
lorentey Mar 31, 2020
db68b96
Merge remote-tracking branch 'origin/master' into atomics2
lorentey Apr 4, 2020
add99f0
[Atomics] Eliminate AtomicRepresentable
lorentey Apr 4, 2020
25cbe2c
[Atomics] address ⟹ pointer
lorentey Apr 4, 2020
c04d036
[Atomics] Doc updates
lorentey Apr 4, 2020
7839acd
[Atomics] Mark some arguments __owned in generic contexts
lorentey Apr 4, 2020
be93fbe
[Atomics] Implement the weak compare-exchange operation
lorentey Apr 4, 2020
bf5f4eb
[Atomics] Update spelling of semantics attribute for constant orderings
lorentey Apr 4, 2020
02b7191
[Atomics] Require constant ordering on fences
lorentey Apr 6, 2020
b7bbee8
[Atomics] Mark default ordering branches as unreachable
lorentey Apr 6, 2020
81f7a33
[Atomic] Add some ceremonial naming prefixes: UnsafeAtomic ⟹ UnsafePo…
lorentey Apr 7, 2020
77e79ff
[Atomics] prepareAtomicStorage(for:)/disposeAtomicStorage(_:)
lorentey Apr 9, 2020
171865c
[test][Atomics] Simplify availability handling in AtomicsFolding test
lorentey Apr 9, 2020
3fd590a
[Atomics] UnsafePointerToAtomic ⟹ UnsafeAtomic
lorentey Apr 10, 2020
de5c98d
[Atomics] Split atomics into two protocol layers; overhaul optionals …
lorentey Apr 10, 2020
4b31896
[Atomics] Small API adjustments
lorentey Apr 10, 2020
01a0184
[Atomics][test] Add a test implementing a lock-free single-consumer s…
lorentey Apr 10, 2020
a032ff4
[Atomics] ordering: ⟹ successOrdering: in two-ordering compare-exchan…
lorentey Apr 13, 2020
5f43d00
[Atomics] storeIfNil ⟹ storeIfNilThenLoad
lorentey Apr 13, 2020
691f37e
[test] LockFreeSingleConsumerStack requires Dispatch
lorentey Apr 13, 2020
640e196
[Atomics] Allow integer types to have a custom atomic representation
lorentey Apr 13, 2020
ce1242b
[test][Atomics] Fix Dispatch import in LockFreeSingleConsumerStack test
lorentey Apr 13, 2020
0e2274c
Merge branch 'master' into atomics2
lorentey Apr 15, 2020
e13f1fc
[Atomics] AtomicProtocol ⟹ AtomicValue
lorentey Apr 20, 2020
ad30578
[Atomics] Be more assertive about inlining storage/value conversions
lorentey Apr 20, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions stdlib/public/Atomics/AtomicInteger.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import Swift

@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)
public protocol AtomicInteger: AtomicValue, FixedWidthInteger
where _AtomicStorage: _PrimitiveAtomicInteger {}

@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)
public protocol _PrimitiveAtomicInteger: _PrimitiveAtomic {
/// Perform an atomic wrapping increment operation on the value referenced by
/// `pointer` and return the original value, applying the specified memory
/// ordering.
///
/// Note: This operation silently wraps around on overflow, like the
/// `&+=` operator does on integer values.
///
/// - Parameter operand: The value to add to the current value.
/// - Parameter pointer: A memory location previously initialized with a value
/// returned by `prepareAtomicStorage(for:)`.
/// - Parameter ordering: The memory ordering to apply on this operation.
/// - Returns: The original value before the operation.
@_semantics("atomics.requires_constant_orderings")
static func _atomicLoadThenWrappingIncrement(
by operand: Self,
at pointer: UnsafeMutablePointer<Self>,
ordering: AtomicUpdateOrdering
) -> Self

/// Perform an atomic wrapping decrement operation on the value referenced by
/// `pointer` and return the original value, applying the specified memory
/// ordering.
///
/// Note: This operation silently wraps around on overflow, like the
/// `&-=` operator does on integer values.
///
/// - Parameter operand: The value to subtract from the current value.
/// - Parameter pointer: A memory location previously initialized with a value
/// returned by `prepareAtomicStorage(for:)`.
/// - Parameter ordering: The memory ordering to apply on this operation.
/// - Returns: The original value before the operation.
@_semantics("atomics.requires_constant_orderings")
static func _atomicLoadThenWrappingDecrement(
by operand: Self,
at pointer: UnsafeMutablePointer<Self>,
ordering: AtomicUpdateOrdering
) -> Self

/// Perform an atomic bitwise AND operation on the value referenced by
/// `pointer` and return the original value, applying the specified memory
/// ordering.
///
/// - Parameter operand: An integer value.
/// - Parameter pointer: A memory location previously initialized with a value
/// returned by `prepareAtomicStorage(for:)`.
/// - Parameter ordering: The memory ordering to apply on this operation.
/// - Returns: The original value before the operation.
@_semantics("atomics.requires_constant_orderings")
static func _atomicLoadThenBitwiseAnd(
with operand: Self,
at pointer: UnsafeMutablePointer<Self>,
ordering: AtomicUpdateOrdering
) -> Self

/// Perform an atomic bitwise OR operation on the value referenced by
/// `pointer` and return the original value, applying the specified memory
/// ordering.
///
/// - Parameter operand: An integer value.
/// - Parameter pointer: A memory location previously initialized with a value
/// returned by `prepareAtomicStorage(for:)`.
/// - Parameter ordering: The memory ordering to apply on this operation.
/// - Returns: The original value before the operation.
@_semantics("atomics.requires_constant_orderings")
static func _atomicLoadThenBitwiseOr(
with operand: Self,
at pointer: UnsafeMutablePointer<Self>,
ordering: AtomicUpdateOrdering
) -> Self

/// Perform an atomic bitwise XOR operation on the value referenced by
/// `pointer` and return the original value, applying the specified memory
/// ordering.
///
/// - Parameter operand: An integer value.
/// - Parameter pointer: A memory location previously initialized with a value
/// returned by `prepareAtomicStorage(for:)`.
/// - Parameter ordering: The memory ordering to apply on this operation.
/// - Returns: The original value before the operation.
@_semantics("atomics.requires_constant_orderings")
static func _atomicLoadThenBitwiseXor(
with operand: Self,
at pointer: UnsafeMutablePointer<Self>,
ordering: AtomicUpdateOrdering
) -> Self
}
241 changes: 241 additions & 0 deletions stdlib/public/Atomics/AtomicIntegerConformances.swift.gyb
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

%{
def atomicTypes():
return [
# Swift Builtin Getter
("Int", "Word", "_builtinWordValue"),
("UInt", "Word", "_builtinWordValue"),
("Int64", "Int64", "_value"),
("UInt64", "Int64", "_value"),
("Int32", "Int32", "_value"),
("UInt32", "Int32", "_value"),
("Int16", "Int16", "_value"),
("UInt16", "Int16", "_value"),
("Int8", "Int8", "_value"),
("UInt8", "Int8", "_value"),
]

def rmwOrderings():
return [
# Swift enum case, llvm name, failure name
('relaxed', 'monotonic', 'monotonic'),
('acquiring', 'acquire', 'acquire'),
('releasing', 'release', 'monotonic'),
('acquiringAndReleasing', 'acqrel', 'acquire'),
('sequentiallyConsistent', 'seqcst', 'seqcst'),
]

def loadOrderings():
return [
# Swift enum case, llvm name
('relaxed', 'monotonic'),
('acquiring', 'acquire'),
('sequentiallyConsistent', 'seqcst'),
]

def storeOrderings():
return [
# Swift enum case, llvm name
('relaxed', 'monotonic'),
('releasing', 'release'),
('sequentiallyConsistent', 'seqcst'),
]

def caseStatementForOrdering(ordering):
return "case .{}".format(ordering)

# FIXME: Swift should provide intrinsics for arbitrary ordering pairs
def llvmOrders(rmw, load): # See llvm/Support/AtomicOrdering.h
def max(rmw, load):
if load == "acquire":
if rmw == "monotonic":
return "acquire"
if rmw == "release":
return "acqrel"
if load == "seqcst":
return "seqcst"
return rmw
return max(rmw, load) + "_" + load

def lowerFirst(str):
return str[:1].lower() + str[1:] if str else ""

def argLabel(label):
return label + ": " if label <> "_" else ""

integerOperations = [
# Swift name, llvm name, operator, label, doc
('WrappingIncrement', 'add', '&+', "by", "wrapping add"),
('WrappingDecrement', 'sub', '&-', "by", "wrapping subtract"),
('BitwiseAnd', 'and', '&', "with", "bitwise and"),
('BitwiseOr', 'or', '|', "with", "bitwise or"),
('BitwiseXor', 'xor', '^', "with", "bitwise xor")
]
}%

import Swift

% for (swiftType, builtinType, getter) in atomicTypes():
@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)
extension ${swiftType}: _PrimitiveAtomic {
@_semantics("atomics.requires_constant_orderings")
@_transparent @_alwaysEmitIntoClient
public static func _atomicLoad(
at pointer: UnsafeMutablePointer<${swiftType}>,
ordering: AtomicLoadOrdering
) -> ${swiftType} {
switch ordering {
% for (swiftOrder, llvmOrder) in loadOrderings():
${caseStatementForOrdering(swiftOrder)}:
return ${swiftType}(Builtin.atomicload_${llvmOrder}_${builtinType}(
pointer._rawValue))
% end
default:
Builtin.unreachable()
}
}

@_semantics("atomics.requires_constant_orderings")
@_transparent @_alwaysEmitIntoClient
public static func _atomicStore(
_ desired: ${swiftType},
at pointer: UnsafeMutablePointer<${swiftType}>,
ordering: AtomicStoreOrdering
) {
switch ordering {
% for (swiftOrder, llvmOrder) in storeOrderings():
${caseStatementForOrdering(swiftOrder)}:
Builtin.atomicstore_${llvmOrder}_${builtinType}(
pointer._rawValue,
desired.${getter})
% end
default:
Builtin.unreachable()
}
}

@_semantics("atomics.requires_constant_orderings")
@_transparent @_alwaysEmitIntoClient
public static func _atomicExchange(
_ desired: ${swiftType},
at pointer: UnsafeMutablePointer<${swiftType}>,
ordering: AtomicUpdateOrdering
) -> ${swiftType} {
switch ordering {
% for (swiftOrder, llvmOrder, _) in rmwOrderings():
${caseStatementForOrdering(swiftOrder)}:
return ${swiftType}(Builtin.atomicrmw_xchg_${llvmOrder}_${builtinType}(
pointer._rawValue, desired.${getter}))
% end
default:
Builtin.unreachable()
}
}

@_semantics("atomics.requires_constant_orderings")
@_transparent @_alwaysEmitIntoClient
public static func _atomicCompareExchange(
expected: ${swiftType},
desired: ${swiftType},
at pointer: UnsafeMutablePointer<${swiftType}>,
ordering: AtomicUpdateOrdering
) -> (exchanged: Bool, original: ${swiftType}) {
switch ordering {
% for (swiftOrder, llvmOrder, failureOrder) in rmwOrderings():
${caseStatementForOrdering(swiftOrder)}:
let (oldValue, won) = Builtin.cmpxchg_${llvmOrder}_${failureOrder}_${builtinType}(
pointer._rawValue, expected.${getter}, desired.${getter})
return (Bool(_builtinBooleanLiteral: won), ${swiftType}(oldValue))
% end
default:
Builtin.unreachable()
}
}

@_semantics("atomics.requires_constant_orderings")
@_transparent @_alwaysEmitIntoClient
public static func _atomicCompareExchange(
expected: ${swiftType},
desired: ${swiftType},
at pointer: UnsafeMutablePointer<${swiftType}>,
successOrdering: AtomicUpdateOrdering,
failureOrdering: AtomicLoadOrdering
) -> (exchanged: Bool, original: ${swiftType}) {
switch (successOrdering, failureOrdering) {
% for (swiftSuccess, llvmSuccess, _) in rmwOrderings():
% for (swiftFailure, llvmFailure) in loadOrderings():
case (.${swiftSuccess}, .${swiftFailure}):
let (oldValue, won) = Builtin.cmpxchg_${llvmOrders(llvmSuccess, llvmFailure)}_${builtinType}(
pointer._rawValue, expected.${getter}, desired.${getter})
return (Bool(_builtinBooleanLiteral: won), ${swiftType}(oldValue))
% end
% end
default:
Builtin.unreachable()
}
}

@_semantics("atomics.requires_constant_orderings")
@_transparent @_alwaysEmitIntoClient
public static func _atomicWeakCompareExchange(
expected: ${swiftType},
desired: ${swiftType},
at pointer: UnsafeMutablePointer<${swiftType}>,
successOrdering: AtomicUpdateOrdering,
failureOrdering: AtomicLoadOrdering
) -> (exchanged: Bool, original: ${swiftType}) {
switch (successOrdering, failureOrdering) {
% for (swiftSuccess, llvmSuccess, _) in rmwOrderings():
% for (swiftFailure, llvmFailure) in loadOrderings():
case (.${swiftSuccess}, .${swiftFailure}):
let (oldValue, won) = Builtin.cmpxchg_${llvmOrders(llvmSuccess, llvmFailure)}_weak_${builtinType}(
pointer._rawValue, expected.${getter}, desired.${getter})
return (Bool(_builtinBooleanLiteral: won), ${swiftType}(oldValue))
% end
% end
default:
Builtin.unreachable()
}
}
}

@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)
extension ${swiftType}: AtomicInteger {
public typealias _AtomicStorage = ${swiftType}
}

@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)
extension ${swiftType}: _PrimitiveAtomicInteger {
% for (name, llvmop, op, label, doc) in integerOperations:
% defaultValue = " = 1" if label <> "" else ""
@_semantics("atomics.requires_constant_orderings")
@_transparent @_alwaysEmitIntoClient
@discardableResult
public static func _atomicLoadThen${name}(
${label} operand: Self${defaultValue},
at pointer: UnsafeMutablePointer<Self>,
ordering: AtomicUpdateOrdering
) -> Self {
switch ordering {
% for (swiftOrder, llvmOrder, _) in rmwOrderings():
${caseStatementForOrdering(swiftOrder)}:
return Self(Builtin.atomicrmw_${llvmop}_${llvmOrder}_${builtinType}(
pointer._rawValue, operand.${getter}))
% end
default:
Builtin.unreachable()
}
}
% end
}
Loading