Skip to content

Commit cb1346f

Browse files
committed
Add fence, cmpxchg and atomicRMW to the builder
1 parent 18438ce commit cb1346f

File tree

1 file changed

+279
-1
lines changed

1 file changed

+279
-1
lines changed

Sources/LLVM/IRBuilder.swift

Lines changed: 279 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ public enum IntPredicate {
5454
.ult: LLVMIntULT, .ule: LLVMIntULE, .sgt: LLVMIntSGT, .sge: LLVMIntSGE,
5555
.slt: LLVMIntSLT, .sle: LLVMIntSLE
5656
]
57+
58+
/// Retrieves the corresponding `LLVMIntPredicate`.
5759
public var llvm: LLVMIntPredicate {
5860
return IntPredicate.predicateMapping[self]!
5961
}
@@ -103,13 +105,204 @@ public enum RealPredicate {
103105
.true: LLVMRealPredicateTrue,
104106
]
105107

108+
/// Retrieves the corresponding `LLVMRealPredicate`.
106109
public var llvm: LLVMRealPredicate {
107110
return RealPredicate.predicateMapping[self]!
108111
}
109112
}
110113

114+
/// `AtomicOrdering` enumerates available memory ordering semantics.
115+
///
116+
/// Atomic instructions (`cmpxchg`, `atomicrmw`, `fence`, `atomic load`, and
117+
/// `atomic store`) take ordering parameters that determine which other atomic
118+
/// instructions on the same address they synchronize with. These semantics are
119+
/// borrowed from Java and C++0x, but are somewhat more colloquial. If these
120+
/// descriptions aren’t precise enough, check those specs (see spec references
121+
/// in the atomics guide). fence instructions treat these orderings somewhat
122+
/// differently since they don’t take an address. See that instruction’s
123+
/// documentation for details.
124+
public enum AtomicOrdering: Comparable {
125+
/// A load or store which is not atomic
126+
case notAtomic
127+
/// Lowest level of atomicity, guarantees somewhat sane results, lock free.
128+
///
129+
/// The set of values that can be read is governed by the happens-before
130+
/// partial order. A value cannot be read unless some operation wrote it. This
131+
/// is intended to provide a guarantee strong enough to model Java’s
132+
/// non-volatile shared variables. This ordering cannot be specified for
133+
/// read-modify-write operations; it is not strong enough to make them atomic
134+
/// in any interesting way.
135+
case unordered
136+
/// Guarantees that if you take all the operations affecting a specific
137+
/// address, a consistent ordering exists.
138+
///
139+
/// In addition to the guarantees of unordered, there is a single total order
140+
/// for modifications by monotonic operations on each address. All
141+
/// modification orders must be compatible with the happens-before order.
142+
/// There is no guarantee that the modification orders can be combined to a
143+
/// global total order for the whole program (and this often will not be
144+
/// possible). The read in an atomic read-modify-write operation (cmpxchg and
145+
/// atomicrmw) reads the value in the modification order immediately before
146+
/// the value it writes. If one atomic read happens before another atomic read
147+
/// of the same address, the later read must see the same value or a later
148+
/// value in the address’s modification order. This disallows reordering of
149+
/// monotonic (or stronger) operations on the same address. If an address is
150+
/// written monotonic-ally by one thread, and other threads monotonic-ally
151+
/// read that address repeatedly, the other threads must eventually see the
152+
/// write. This corresponds to the C++0x/C1x memory_order_relaxed.
153+
case monotonic
154+
/// Acquire provides a barrier of the sort necessary to acquire a lock to
155+
/// access other memory with normal loads and stores.
156+
///
157+
/// In addition to the guarantees of monotonic, a synchronizes-with edge may
158+
/// be formed with a release operation. This is intended to model C++’s
159+
/// `memory_order_acquire`.
160+
case acquire
161+
/// Release is similar to Acquire, but with a barrier of the sort necessary to
162+
/// release a lock.
163+
///
164+
/// In addition to the guarantees of monotonic, if this operation writes a
165+
/// value which is subsequently read by an acquire operation, it
166+
/// synchronizes-with that operation. (This isn’t a complete description; see
167+
/// the C++0x definition of a release sequence.) This corresponds to the
168+
/// C++0x/C1x memory_order_release.
169+
case release
170+
/// provides both an Acquire and a Release barrier (for fences and operations
171+
/// which both read and write memory).
172+
///
173+
/// This corresponds to the C++0x/C1x memory_order_acq_rel.
174+
case acquireRelease
175+
/// Provides Acquire semantics for loads and Release semantics for stores.
176+
///
177+
/// In addition to the guarantees of acq_rel (acquire for an operation that
178+
/// only reads, release for an operation that only writes), there is a global
179+
/// total order on all sequentially-consistent operations on all addresses,
180+
/// which is consistent with the happens-before partial order and with the
181+
/// modification orders of all the affected addresses. Each
182+
/// sequentially-consistent read sees the last preceding write to the same
183+
/// address in this global order. This corresponds to the C++0x/C1x
184+
/// `memory_order_seq_cst` and Java volatile.
185+
case sequentiallyConsistent
186+
187+
private static let orderingMapping: [AtomicOrdering: LLVMAtomicOrdering] = [
188+
.notAtomic: LLVMAtomicOrderingNotAtomic,
189+
.unordered: LLVMAtomicOrderingUnordered,
190+
.monotonic: LLVMAtomicOrderingMonotonic,
191+
.acquire: LLVMAtomicOrderingAcquire,
192+
.release: LLVMAtomicOrderingRelease,
193+
.acquireRelease: LLVMAtomicOrderingAcquireRelease,
194+
.sequentiallyConsistent: LLVMAtomicOrderingSequentiallyConsistent,
195+
]
196+
197+
public static func ==(lhs: AtomicOrdering, rhs: AtomicOrdering) -> Bool {
198+
return lhs.llvm == rhs.llvm
199+
}
200+
201+
public static func <(lhs: AtomicOrdering, rhs: AtomicOrdering) -> Bool {
202+
return lhs.llvm.rawValue < rhs.llvm.rawValue
203+
}
204+
205+
/// Retrieves the corresponding `LLVMAtomicOrdering`.
206+
public var llvm: LLVMAtomicOrdering {
207+
return AtomicOrdering.orderingMapping[self]!
208+
}
209+
}
210+
211+
/// `AtomicReadModifyWriteOperation` enumerates the kinds of supported atomic
212+
/// read-write-modify operations.
213+
public enum AtomicReadModifyWriteOperation {
214+
/// Set the new value and return the one old
215+
///
216+
/// ```
217+
/// *ptr = val
218+
/// ```
219+
case xchg
220+
/// Add a value and return the old one
221+
///
222+
/// ```
223+
/// *ptr = *ptr + val
224+
/// ```
225+
case add
226+
/// Subtract a value and return the old one
227+
///
228+
/// ```
229+
/// *ptr = *ptr - val
230+
/// ```
231+
case sub
232+
/// And a value and return the old one
233+
///
234+
/// ```
235+
/// *ptr = *ptr & val
236+
/// ```
237+
case and
238+
/// Not-And a value and return the old one
239+
///
240+
/// ```
241+
/// *ptr = ~(*ptr & val)
242+
/// ```
243+
case nand
244+
/// OR a value and return the old one
245+
///
246+
/// ```
247+
/// *ptr = *ptr | val
248+
/// ```
249+
case or
250+
/// Xor a value and return the old one
251+
///
252+
/// ```
253+
/// *ptr = *ptr ^ val
254+
/// ```
255+
case xor
256+
/// Sets the value if it's greater than the original using a signed comparison
257+
/// and return the old one.
258+
///
259+
/// ```
260+
/// // Using a signed comparison
261+
/// *ptr = *ptr > val ? *ptr : val
262+
/// ```
263+
case max
264+
/// Sets the value if it's Smaller than the original using a signed comparison
265+
/// and return the old one.
266+
///
267+
/// ```
268+
/// // Using a signed comparison
269+
/// *ptr = *ptr < val ? *ptr : val
270+
/// ```
271+
case min
272+
/// Sets the value if it's greater than the original using an unsigned
273+
/// comparison and return the old one.
274+
///
275+
/// ```
276+
/// // Using an unsigned comparison
277+
/// *ptr = *ptr > val ? *ptr : val
278+
/// ```
279+
case umax
280+
/// Sets the value if it's greater than the original using an unsigned
281+
/// comparison and return the old one.
282+
///
283+
/// ```
284+
/// // Using an unsigned comparison
285+
/// *ptr = *ptr < val ? *ptr : val
286+
/// ```
287+
case umin
288+
289+
static let atomicRMWMapping: [AtomicReadModifyWriteOperation: LLVMAtomicRMWBinOp] = [
290+
.xchg: LLVMAtomicRMWBinOpXchg, .add: LLVMAtomicRMWBinOpAdd,
291+
.sub: LLVMAtomicRMWBinOpSub, .and: LLVMAtomicRMWBinOpAnd,
292+
.nand: LLVMAtomicRMWBinOpNand, .or: LLVMAtomicRMWBinOpOr,
293+
.xor: LLVMAtomicRMWBinOpXor, .max: LLVMAtomicRMWBinOpMax,
294+
.min: LLVMAtomicRMWBinOpMin, .umax: LLVMAtomicRMWBinOpUMax,
295+
.umin: LLVMAtomicRMWBinOpUMin,
296+
]
297+
298+
/// Retrieves the corresponding `LLVMAtomicRMWBinOp`.
299+
public var llvm: LLVMAtomicRMWBinOp {
300+
return AtomicReadModifyWriteOperation.atomicRMWMapping[self]!
301+
}
302+
}
303+
111304
extension Module {
112-
/// Searches for and retrieves a global variable with the given name in this
305+
/// Searches for and retrieves a global variable with the given name in this
113306
/// module if that name references an existing global variable.
114307
///
115308
/// - parameter name: The name of the global to reference.
@@ -942,6 +1135,91 @@ public class IRBuilder {
9421135
return LLVMSizeOf(val.asLLVM())
9431136
}
9441137

1138+
// MARK: Atomic Instructions
1139+
1140+
/// Builds a fence instruction that introduces "happens-before" edges between
1141+
/// operations.
1142+
///
1143+
/// - parameter ordering: Defines the kind of "synchronizes-with" edge this
1144+
/// fence adds.
1145+
/// - parameter singleThreaded: Specifies that the fence only synchronizes
1146+
/// with other atomics in the same thread. (This is useful for interacting
1147+
/// with signal handlers.) Otherwise this fence is atomic with respect to
1148+
/// all other code in the system.
1149+
///
1150+
/// - returns: A value representing `void`.
1151+
public func buildFence(ordering: AtomicOrdering, singleThreaded: Bool = false, name: String = "") -> IRValue {
1152+
return LLVMBuildFence(llvm, ordering.llvm, singleThreaded.llvm, name)
1153+
}
1154+
1155+
/// Builds an atomic compare-and-exchange instruction to atomically modify
1156+
/// memory. It loads a value in memory and compares it to a given value. If
1157+
/// they are equal, it tries to store a new value into the memory.
1158+
///
1159+
/// - parameter ptr: The address of data to update atomically.
1160+
/// - parameter old: The value to base the comparison on.
1161+
/// - parameter new: The new value to write if comparison with the old value
1162+
/// returns true.
1163+
/// - parameter successOrdering: Specifies how this cmpxchg synchronizes with
1164+
/// other atomic operations when it succeeds.
1165+
/// - parameter failureOrdering: Specifies how this cmpxchg synchronizes with
1166+
/// other atomic operations when it fails.
1167+
/// - parameter singleThreaded: Specifies that this cmpxchg only synchronizes
1168+
/// with other atomics in the same thread. (This is useful for interacting
1169+
/// with signal handlers.) Otherwise this cmpxchg is atomic with respect to
1170+
/// all other code in the system.
1171+
///
1172+
/// - returns: A value representing the original value at the given location
1173+
/// is together with a flag indicating success (true) or failure (false).
1174+
public func buildAtomicCmpXchg(
1175+
ptr: IRValue, of old: IRValue, to new: IRValue,
1176+
successOrdering: AtomicOrdering, failureOrdering: AtomicOrdering,
1177+
singleThreaded: Bool = false
1178+
) -> IRValue {
1179+
1180+
if failureOrdering.rawValue < AtomicOrdering.monotonic.rawValue {
1181+
fatalError("Failure ordering must be at least 'Monotonic'")
1182+
}
1183+
1184+
if successOrdering.rawValue < AtomicOrdering.monotonic.rawValue {
1185+
fatalError("Success ordering must be at least 'Monotonic'")
1186+
}
1187+
1188+
if failureOrdering == .release || failureOrdering == .acquireRelease {
1189+
fatalError("Failure ordering may not be 'Release' or 'Acquire Release'")
1190+
}
1191+
1192+
if failureOrdering.rawValue > successOrdering.rawValue {
1193+
fatalError("Failure ordering must be no stronger than success ordering")
1194+
}
1195+
1196+
return LLVMBuildAtomicCmpXchg(
1197+
llvm, ptr.asLLVM(), old.asLLVM(), new.asLLVM(),
1198+
successOrdering.llvm, failureOrdering.llvm, singleThreaded.llvm
1199+
)
1200+
}
1201+
1202+
/// Builds an atomic read-modify-write instruction to atomically modify memory.
1203+
///
1204+
/// - parameter atomicOp: The atomic operation to perform.
1205+
/// - parameter ptr: The address of a value to modify.
1206+
/// - parameter value: The second argument to the given operation.
1207+
/// - parameter ordering: Defines the kind of "synchronizes-with" edge this
1208+
/// atomic operation adds.
1209+
/// - parameter singleThreaded: Specifies that this atomicRMW instruction only
1210+
/// synchronizes with other atomics in the same thread. (This is useful for
1211+
/// interacting with signal handlers.) Otherwise this atomicRMW is atomic
1212+
/// with respect to all other code in the system.
1213+
///
1214+
/// - returns: A value representing the old value of the given pointer before
1215+
/// the atomic operation was executed.
1216+
public func buildAtomicRMW(
1217+
atomicOp: AtomicReadModifyWriteOperation, ptr: IRValue, value: IRValue,
1218+
ordering: AtomicOrdering, singleThreaded: Bool = false
1219+
) -> IRValue {
1220+
return LLVMBuildAtomicRMW(llvm, atomicOp.llvm, ptr.asLLVM(), value.asLLVM(), ordering.llvm, singleThreaded.llvm)
1221+
}
1222+
9451223
// MARK: Aggregate Instructions
9461224

9471225
/// Builds an instruction to insert a value into a member field in an

0 commit comments

Comments
 (0)