Skip to content

Commit 6f82a5b

Browse files
committed
Add fence, cmpxchg and atomicRMW to the builder
1 parent 0c6b336 commit 6f82a5b

File tree

1 file changed

+271
-1
lines changed

1 file changed

+271
-1
lines changed

Sources/LLVM/IRBuilder.swift

Lines changed: 271 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,196 @@ 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: Int {
125+
/// A load or store which is not atomic
126+
case notAtomic = 0
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 = 1
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 = 2
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 = 3
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 = 4
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 = 5
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 = 6
186+
187+
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+
/// Retrieves the corresponding `LLVMAtomicOrdering`.
198+
public var llvm: LLVMAtomicOrdering {
199+
return AtomicOrdering.orderingMapping[self]!
200+
}
201+
}
202+
203+
/// `AtomicRMWOperation` enumerates the kinds of supported atomic
204+
/// read-write-modify operations.
205+
public enum AtomicRMWOperation {
206+
/// Set the new value and return the one old
207+
///
208+
/// ```
209+
/// *ptr = val
210+
/// ```
211+
case xchg
212+
/// Add a value and return the old one
213+
///
214+
/// ```
215+
/// *ptr = *ptr + val
216+
/// ```
217+
case add
218+
/// Subtract a value and return the old one
219+
///
220+
/// ```
221+
/// *ptr = *ptr - val
222+
/// ```
223+
case sub
224+
/// And a value and return the old one
225+
///
226+
/// ```
227+
/// *ptr = *ptr & val
228+
/// ```
229+
case and
230+
/// Not-And a value and return the old one
231+
///
232+
/// ```
233+
/// *ptr = ~(*ptr & val)
234+
/// ```
235+
case nand
236+
/// OR a value and return the old one
237+
///
238+
/// ```
239+
/// *ptr = *ptr | val
240+
/// ```
241+
case or
242+
/// Xor a value and return the old one
243+
///
244+
/// ```
245+
/// *ptr = *ptr ^ val
246+
/// ```
247+
case xor
248+
/// Sets the value if it's greater than the original using a signed comparison
249+
/// and return the old one.
250+
///
251+
/// ```
252+
/// // Using a signed comparison
253+
/// *ptr = *ptr > val ? *ptr : val
254+
/// ```
255+
case max
256+
/// Sets the value if it's Smaller 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 min
264+
/// Sets the value if it's greater than the original using an unsigned
265+
/// comparison and return the old one.
266+
///
267+
/// ```
268+
/// // Using an unsigned comparison
269+
/// *ptr = *ptr > val ? *ptr : val
270+
/// ```
271+
case umax
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 umin
280+
281+
static let atomicRMWMapping: [AtomicRMWOperation: LLVMAtomicRMWBinOp] = [
282+
.xchg: LLVMAtomicRMWBinOpXchg, .add: LLVMAtomicRMWBinOpAdd,
283+
.sub: LLVMAtomicRMWBinOpSub, .and: LLVMAtomicRMWBinOpAnd,
284+
.nand: LLVMAtomicRMWBinOpNand, .or: LLVMAtomicRMWBinOpOr,
285+
.xor: LLVMAtomicRMWBinOpXor, .max: LLVMAtomicRMWBinOpMax,
286+
.min: LLVMAtomicRMWBinOpMin, .umax: LLVMAtomicRMWBinOpUMax,
287+
.umin: LLVMAtomicRMWBinOpUMin,
288+
]
289+
290+
/// Retrieves the corresponding `LLVMAtomicRMWBinOp`.
291+
public var llvm: LLVMAtomicRMWBinOp {
292+
return AtomicRMWOperation.atomicRMWMapping[self]!
293+
}
294+
}
295+
111296
extension Module {
112-
/// Searches for and retrieves a global variable with the given name in this
297+
/// Searches for and retrieves a global variable with the given name in this
113298
/// module if that name references an existing global variable.
114299
///
115300
/// - parameter name: The name of the global to reference.
@@ -942,6 +1127,91 @@ public class IRBuilder {
9421127
return LLVMSizeOf(val.asLLVM())
9431128
}
9441129

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

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

0 commit comments

Comments
 (0)