clang 23.0.0git
CIRGenAtomic.cpp
Go to the documentation of this file.
1//===--- CIRGenAtomic.cpp - Emit CIR for atomic operations ----------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file contains the code for emitting atomic operations.
10//
11//===----------------------------------------------------------------------===//
12
13#include "CIRGenFunction.h"
15
16using namespace clang;
17using namespace clang::CIRGen;
18using namespace cir;
19
20namespace {
21class AtomicInfo {
22 CIRGenFunction &cgf;
23 QualType atomicTy;
24 QualType valueTy;
25 uint64_t atomicSizeInBits = 0;
26 uint64_t valueSizeInBits = 0;
27 CharUnits atomicAlign;
28 CharUnits valueAlign;
29 TypeEvaluationKind evaluationKind = cir::TEK_Scalar;
30 bool useLibCall = true;
31 LValue lvalue;
32 mlir::Location loc;
33
34public:
35 AtomicInfo(CIRGenFunction &cgf, LValue &lvalue, mlir::Location loc)
36 : cgf(cgf), loc(loc) {
37 assert(!lvalue.isGlobalReg());
38 ASTContext &ctx = cgf.getContext();
39 if (lvalue.isSimple()) {
40 atomicTy = lvalue.getType();
41 if (auto *ty = atomicTy->getAs<AtomicType>())
42 valueTy = ty->getValueType();
43 else
44 valueTy = atomicTy;
45 evaluationKind = cgf.getEvaluationKind(valueTy);
46
47 TypeInfo valueTypeInfo = ctx.getTypeInfo(valueTy);
48 TypeInfo atomicTypeInfo = ctx.getTypeInfo(atomicTy);
49 uint64_t valueAlignInBits = valueTypeInfo.Align;
50 uint64_t atomicAlignInBits = atomicTypeInfo.Align;
51 valueSizeInBits = valueTypeInfo.Width;
52 atomicSizeInBits = atomicTypeInfo.Width;
53 assert(valueSizeInBits <= atomicSizeInBits);
54 assert(valueAlignInBits <= atomicAlignInBits);
55
56 atomicAlign = ctx.toCharUnitsFromBits(atomicAlignInBits);
57 valueAlign = ctx.toCharUnitsFromBits(valueAlignInBits);
58 if (lvalue.getAlignment().isZero())
59 lvalue.setAlignment(atomicAlign);
60
61 this->lvalue = lvalue;
62 } else {
64 cgf.cgm.errorNYI(loc, "AtomicInfo: non-simple lvalue");
65 }
66 useLibCall = !ctx.getTargetInfo().hasBuiltinAtomic(
67 atomicSizeInBits, ctx.toBits(lvalue.getAlignment()));
68 }
69
70 QualType getValueType() const { return valueTy; }
71 QualType getAtomicType() const { return atomicTy; }
72 CharUnits getAtomicAlignment() const { return atomicAlign; }
73 TypeEvaluationKind getEvaluationKind() const { return evaluationKind; }
74 mlir::Value getAtomicPointer() const {
75 if (lvalue.isSimple())
76 return lvalue.getPointer();
78 return nullptr;
79 }
80 bool shouldUseLibCall() const { return useLibCall; }
81 const LValue &getAtomicLValue() const { return lvalue; }
82 Address getAtomicAddress() const {
83 mlir::Type elemTy;
84 if (lvalue.isSimple()) {
85 elemTy = lvalue.getAddress().getElementType();
86 } else {
88 cgf.cgm.errorNYI(loc, "AtomicInfo::getAtomicAddress: non-simple lvalue");
89 }
90 return Address(getAtomicPointer(), elemTy, getAtomicAlignment());
91 }
92
93 /// Is the atomic size larger than the underlying value type?
94 ///
95 /// Note that the absence of padding does not mean that atomic
96 /// objects are completely interchangeable with non-atomic
97 /// objects: we might have promoted the alignment of a type
98 /// without making it bigger.
99 bool hasPadding() const { return (valueSizeInBits != atomicSizeInBits); }
100
101 bool emitMemSetZeroIfNecessary() const;
102
103 mlir::Value getScalarRValValueOrNull(RValue rvalue) const;
104
105 /// Cast the given pointer to an integer pointer suitable for atomic
106 /// operations on the source.
107 Address castToAtomicIntPointer(Address addr) const;
108
109 /// If addr is compatible with the iN that will be used for an atomic
110 /// operation, bitcast it. Otherwise, create a temporary that is suitable and
111 /// copy the value across.
112 Address convertToAtomicIntPointer(Address addr) const;
113
114 /// Turn an atomic-layout object into an r-value.
115 RValue convertAtomicTempToRValue(Address addr, SourceLocation loc,
116 bool asValue) const;
117
118 /// Converts a rvalue to integer value.
119 mlir::Value convertRValueToInt(RValue rvalue, mlir::Location loc,
120 bool cmpxchg = false) const;
121
122 RValue convertToValueOrAtomic(mlir::Value intVal, AggValueSlot resultSlot,
123 SourceLocation loc, bool asValue,
124 bool cmpxchg = false) const;
125
126 /// Copy an atomic r-value into atomic-layout memory.
127 void emitCopyIntoMemory(RValue rvalue) const;
128
129 /// Project an l-value down to the value field.
130 LValue projectValue() const {
131 assert(lvalue.isSimple());
132 Address addr = getAtomicAddress();
133 if (hasPadding())
134 addr = cgf.getBuilder().createGetMember(loc, addr, /*name=*/"value",
135 /*index=*/0);
136
138 return LValue::makeAddr(addr, getValueType(), lvalue.getBaseInfo());
139 }
140
141 /// Emits atomic load.
142 /// \returns Loaded value.
143 RValue emitAtomicLoad(AggValueSlot resultSlot, SourceLocation loc,
144 bool asValue, cir::MemOrder order, bool isVolatile);
145
146 /// Materialize an atomic r-value in atomic-layout memory.
147 Address materializeRValue(RValue rvalue, mlir::Location loc) const;
148
149 /// Creates temp alloca for intermediate operations on atomic value.
150 Address createTempAlloca() const;
151
152private:
153 bool requiresMemSetZero(mlir::Type ty) const;
154
155 /// Emits atomic load as a CIR operation.
156 mlir::Value emitAtomicLoadOp(cir::MemOrder order, bool isVolatile,
157 bool cmpxchg = false);
158};
159} // namespace
160
161// This function emits any expression (scalar, complex, or aggregate)
162// into a temporary alloca.
164 Address declPtr = cgf.createMemTemp(
165 e->getType(), cgf.getLoc(e->getSourceRange()), ".atomictmp");
166 cgf.emitAnyExprToMem(e, declPtr, e->getType().getQualifiers(),
167 /*Init*/ true);
168 return declPtr;
169}
170
171/// Does a store of the given IR type modify the full expected width?
172static bool isFullSizeType(CIRGenModule &cgm, mlir::Type ty,
173 uint64_t expectedSize) {
174 return cgm.getDataLayout().getTypeStoreSize(ty) * 8 == expectedSize;
175}
176
177/// Does the atomic type require memsetting to zero before initialization?
178///
179/// The IR type is provided as a way of making certain queries faster.
180bool AtomicInfo::requiresMemSetZero(mlir::Type ty) const {
181 // If the atomic type has size padding, we definitely need a memset.
182 if (hasPadding())
183 return true;
184
185 // Otherwise, do some simple heuristics to try to avoid it:
186 switch (getEvaluationKind()) {
187 // For scalars and complexes, check whether the store size of the
188 // type uses the full size.
189 case cir::TEK_Scalar:
190 return !isFullSizeType(cgf.cgm, ty, atomicSizeInBits);
191 case cir::TEK_Complex:
192 return !isFullSizeType(cgf.cgm,
193 mlir::cast<cir::ComplexType>(ty).getElementType(),
194 atomicSizeInBits / 2);
195 // Padding in structs has an undefined bit pattern. User beware.
197 return false;
198 }
199 llvm_unreachable("bad evaluation kind");
200}
201
202Address AtomicInfo::convertToAtomicIntPointer(Address addr) const {
203 mlir::Type ty = addr.getElementType();
204 uint64_t sourceSizeInBits = cgf.cgm.getDataLayout().getTypeSizeInBits(ty);
205 if (sourceSizeInBits != atomicSizeInBits) {
206 cgf.cgm.errorNYI(
207 loc,
208 "AtomicInfo::convertToAtomicIntPointer: convert through temp alloca");
209 }
210
211 return castToAtomicIntPointer(addr);
212}
213
214RValue AtomicInfo::convertAtomicTempToRValue(Address addr, SourceLocation loc,
215 bool asValue) const {
216 if (lvalue.isSimple()) {
217 if (evaluationKind == TEK_Aggregate) {
218 cgf.cgm.errorNYI(
219 loc,
220 "AtomicInfo::convertAtomicTempToRValue: evaluationKind is aggregate");
221 return RValue::get(nullptr);
222 }
223
224 // Drill into the padding structure if we have one.
225 if (hasPadding()) {
226 cgf.cgm.errorNYI(loc,
227 "AtomicInfo::convertAtomicTempToRValue: hasPadding");
228 return RValue::get(nullptr);
229 }
230
231 // Otherwise, just convert the temporary to an r-value using the
232 // normal conversion routine.
233 return cgf.convertTempToRValue(addr, getValueType(), loc);
234 }
235
236 cgf.cgm.errorNYI(
237 loc, "AtomicInfo::convertAtomicTempToRValue: lvalue is not simple");
238 return RValue::get(nullptr);
239}
240
241RValue AtomicInfo::emitAtomicLoad(AggValueSlot resultSlot, SourceLocation loc,
242 bool asValue, cir::MemOrder order,
243 bool isVolatile) {
244 // Check whether we should use a library call.
245 if (shouldUseLibCall()) {
247 cgf.cgm.errorNYI(loc, "emitAtomicLoad: emit atomic lib call");
248 return RValue::get(nullptr);
249 }
250
251 // Okay, we're doing this natively.
252 mlir::Value loadOp = emitAtomicLoadOp(order, isVolatile);
253
254 // If we're ignoring an aggregate return, don't do anything.
255 if (getEvaluationKind() == TEK_Aggregate && resultSlot.isIgnored())
256 return RValue::getAggregate(Address::invalid(), false);
257
258 // Okay, turn that back into the original value or atomic (for non-simple
259 // lvalues) type.
260 return convertToValueOrAtomic(loadOp, resultSlot, loc, asValue);
261}
262
263Address AtomicInfo::createTempAlloca() const {
264 // Remove addrspace info from the atomic pointer element when making the
265 // alloca pointer element.
266 QualType tmpTy = (lvalue.isBitField() && valueSizeInBits > atomicSizeInBits)
267 ? valueTy
268 : atomicTy.getUnqualifiedType();
269 Address tempAlloca =
270 cgf.createMemTemp(tmpTy, getAtomicAlignment(), loc, "atomic-temp");
271
272 // Cast to pointer to value type for bitfields.
273 if (lvalue.isBitField()) {
274 cgf.cgm.errorNYI(loc, "AtomicInfo::createTempAlloca: bitfield lvalue");
275 }
276
277 return tempAlloca;
278}
279
280mlir::Value AtomicInfo::getScalarRValValueOrNull(RValue rvalue) const {
281 if (rvalue.isScalar() && (!hasPadding() || !lvalue.isSimple()))
282 return rvalue.getValue();
283 return nullptr;
284}
285
286Address AtomicInfo::castToAtomicIntPointer(Address addr) const {
287 auto intTy = mlir::dyn_cast<cir::IntType>(addr.getElementType());
288 // Don't bother with int casts if the integer size is the same.
289 if (intTy && intTy.getWidth() == atomicSizeInBits)
290 return addr;
291 auto ty = cgf.getBuilder().getUIntNTy(atomicSizeInBits);
292 return addr.withElementType(cgf.getBuilder(), ty);
293}
294
295bool AtomicInfo::emitMemSetZeroIfNecessary() const {
296 assert(lvalue.isSimple());
297 Address addr = lvalue.getAddress();
298 if (!requiresMemSetZero(addr.getElementType()))
299 return false;
300
301 addr = addr.withElementType(cgf.getBuilder(), cgf.cgm.voidTy);
302 mlir::Value zero = cgf.getBuilder().getConstInt(loc, cgf.cgm.uInt8Ty, 0);
303 mlir::Value memSetSize = cgf.getBuilder().getConstInt(
304 loc, cgf.cgm.uInt64Ty,
305 cgf.getContext().toCharUnitsFromBits(atomicSizeInBits).getQuantity());
306
307 cgf.getBuilder().createMemSet(loc, addr, zero, memSetSize);
308 return true;
309}
310
311/// Return true if \param valueTy is a type that should be casted to integer
312/// around the atomic memory operation. If \param cmpxchg is true, then the
313/// cast of a floating point type is made as that instruction can not have
314/// floating point operands. TODO: Allow compare-and-exchange and FP - see
315/// comment in CIRGenAtomicExpandPass.cpp.
316static bool shouldCastToInt(mlir::Type valueTy, bool cmpxchg) {
317 if (cir::isAnyFloatingPointType(valueTy))
318 return isa<cir::FP80Type>(valueTy) || cmpxchg;
319 return !isa<cir::IntType>(valueTy) && !isa<cir::PointerType>(valueTy);
320}
321
322mlir::Value AtomicInfo::emitAtomicLoadOp(cir::MemOrder order, bool isVolatile,
323 bool cmpxchg) {
324 Address addr = getAtomicAddress();
325 if (shouldCastToInt(addr.getElementType(), cmpxchg))
326 addr = castToAtomicIntPointer(addr);
327
328 cir::LoadOp op =
329 cgf.getBuilder().createLoad(loc, addr, /*isVolatile=*/isVolatile);
330 op.setMemOrder(order);
331
333 return op;
334}
335
336mlir::Value AtomicInfo::convertRValueToInt(RValue rvalue, mlir::Location loc,
337 bool cmpxchg) const {
338 // If we've got a scalar value of the right size, try to avoid going
339 // through memory. Floats get casted if needed by AtomicExpandPass.
340 if (mlir::Value value = getScalarRValValueOrNull(rvalue)) {
341 if (!shouldCastToInt(value.getType(), cmpxchg))
342 return cgf.emitToMemory(value, valueTy);
343
344 cgf.cgm.errorNYI(
345 loc, "AtomicInfo::convertRValueToInt: cast scalar rvalue to int");
346 return nullptr;
347 }
348
349 // Otherwise, we need to go through memory.
350 // Put the r-value in memory.
351 Address addr = materializeRValue(rvalue, loc);
352
353 // Cast the temporary to the atomic int type and pull a value out.
354 addr = castToAtomicIntPointer(addr);
355
356 return cgf.getBuilder().createLoad(loc, addr);
357}
358
359RValue AtomicInfo::convertToValueOrAtomic(mlir::Value intVal,
360 AggValueSlot resultSlot,
361 SourceLocation loc, bool asValue,
362 bool cmpxchg) const {
363 // Try not to in some easy cases.
364 assert((mlir::isa<cir::IntType, cir::PointerType, cir::FPTypeInterface>(
365 intVal.getType())) &&
366 "Expected integer, pointer or floating point value when converting "
367 "result.");
368 bool isWholeValue =
369 !lvalue.isBitField() || lvalue.getBitFieldInfo().size == valueSizeInBits;
370 if (getEvaluationKind() == TEK_Scalar &&
371 ((isWholeValue && !hasPadding()) || !asValue)) {
372 mlir::Type valTy = asValue ? cgf.convertTypeForMem(valueTy)
373 : getAtomicAddress().getElementType();
374 if (!shouldCastToInt(valTy, cmpxchg)) {
375 assert((!mlir::isa<cir::IntType>(valTy) || intVal.getType() == valTy) &&
376 "Different integer types.");
377 return RValue::get(cgf.emitFromMemory(intVal, valueTy));
378 }
379
380 cgf.cgm.errorNYI("convertToValueOrAtomic: convert through bitcast");
381 return RValue::get(nullptr);
382 }
383
384 // Create a temporary. This needs to be big enough to hold the
385 // atomic integer.
386 Address temp = Address::invalid();
387 if (asValue && getEvaluationKind() == TEK_Aggregate) {
388 cgf.cgm.errorNYI("convertToValueOrAtomic: temporary aggregate");
389 return RValue::get(nullptr);
390 } else {
391 temp = createTempAlloca();
392 }
393
394 // Slam the integer into the temporary.
395 Address castTemp = castToAtomicIntPointer(temp);
396 cgf.getBuilder().createStore(cgf.getLoc(loc), intVal, castTemp);
397 return convertAtomicTempToRValue(temp, loc, asValue);
398}
399
400/// Copy an r-value into memory as part of storing to an atomic type.
401/// This needs to create a bit-pattern suitable for atomic operations.
402void AtomicInfo::emitCopyIntoMemory(RValue rvalue) const {
403 assert(lvalue.isSimple());
404
405 // If we have an r-value, the rvalue should be of the atomic type,
406 // which means that the caller is responsible for having zeroed
407 // any padding. Just do an aggregate copy of that type.
408 if (rvalue.isAggregate()) {
409 cgf.cgm.errorNYI("copying aggregate into atomic lvalue");
410 return;
411 }
412
413 // Okay, otherwise we're copying stuff.
414
415 // Zero out the buffer if necessary.
416 emitMemSetZeroIfNecessary();
417
418 // Drill past the padding if present.
419 LValue tempLValue = projectValue();
420
421 // Okay, store the rvalue in.
422 if (rvalue.isScalar()) {
423 cgf.emitStoreOfScalar(rvalue.getValue(), tempLValue, /*isInit=*/true);
424 } else {
425 cgf.emitStoreOfComplex(loc, rvalue.getComplexValue(), tempLValue,
426 /*isInit=*/true);
427 }
428}
429
430/// Materialize an r-value into memory for the purposes of storing it
431/// to an atomic type.
432Address AtomicInfo::materializeRValue(RValue rvalue, mlir::Location loc) const {
433 // Aggregate r-values are already in memory, and EmitAtomicStore
434 // requires them to be values of the atomic type.
435 if (rvalue.isAggregate())
436 return rvalue.getAggregateAddress();
437
438 // Otherwise, make a temporary and materialize into it.
439 LValue tempLV = cgf.makeAddrLValue(createTempAlloca(), getAtomicType());
440 AtomicInfo atomics(cgf, tempLV, loc);
441
442 atomics.emitCopyIntoMemory(rvalue);
443 return tempLV.getAddress();
444}
445
446static void emitDefaultCaseLabel(CIRGenBuilderTy &builder, mlir::Location loc) {
447 mlir::ArrayAttr valuesAttr = builder.getArrayAttr({});
448 mlir::OpBuilder::InsertPoint insertPoint;
449 cir::CaseOp::create(builder, loc, valuesAttr, cir::CaseOpKind::Default,
450 insertPoint);
451 builder.restoreInsertionPoint(insertPoint);
452}
453
454// Create a "case" operation with the given list of orders as its values. Also
455// create the region that will hold the body of the switch-case label.
456static void emitMemOrderCaseLabel(CIRGenBuilderTy &builder, mlir::Location loc,
457 mlir::Type orderType,
460 for (cir::MemOrder order : orders)
461 orderAttrs.push_back(cir::IntAttr::get(orderType, static_cast<int>(order)));
462 mlir::ArrayAttr ordersAttr = builder.getArrayAttr(orderAttrs);
463
464 mlir::OpBuilder::InsertPoint insertPoint;
465 cir::CaseOp::create(builder, loc, ordersAttr, cir::CaseOpKind::Anyof,
466 insertPoint);
467 builder.restoreInsertionPoint(insertPoint);
468}
469
470static void emitAtomicCmpXchg(CIRGenFunction &cgf, AtomicExpr *e, bool isWeak,
471 Address dest, Address ptr, Address val1,
472 Address val2, uint64_t size,
473 cir::MemOrder successOrder,
474 cir::MemOrder failureOrder,
475 cir::SyncScopeKind scope) {
476 mlir::Location loc = cgf.getLoc(e->getSourceRange());
477
478 CIRGenBuilderTy &builder = cgf.getBuilder();
479 mlir::Value expected = builder.createLoad(loc, val1);
480 mlir::Value desired = builder.createLoad(loc, val2);
481
482 auto cmpxchg = cir::AtomicCmpXchgOp::create(
483 builder, loc, expected.getType(), builder.getBoolTy(), ptr.getPointer(),
484 expected, desired,
485 cir::MemOrderAttr::get(&cgf.getMLIRContext(), successOrder),
486 cir::MemOrderAttr::get(&cgf.getMLIRContext(), failureOrder),
487 cir::SyncScopeKindAttr::get(&cgf.getMLIRContext(), scope),
488 builder.getI64IntegerAttr(ptr.getAlignment().getAsAlign().value()));
489
490 cmpxchg.setIsVolatile(e->isVolatile());
491 cmpxchg.setWeak(isWeak);
492
493 mlir::Value failed = builder.createNot(cmpxchg.getSuccess());
494 cir::IfOp::create(builder, loc, failed, /*withElseRegion=*/false,
495 [&](mlir::OpBuilder &, mlir::Location) {
496 auto ptrTy = mlir::cast<cir::PointerType>(
497 val1.getPointer().getType());
498 if (val1.getElementType() != ptrTy.getPointee()) {
499 val1 = val1.withPointer(builder.createPtrBitcast(
500 val1.getPointer(), val1.getElementType()));
501 }
502 builder.createStore(loc, cmpxchg.getOld(), val1);
503 builder.createYield(loc);
504 });
505
506 // Update the memory at Dest with Success's value.
507 cgf.emitStoreOfScalar(cmpxchg.getSuccess(),
508 cgf.makeAddrLValue(dest, e->getType()),
509 /*isInit=*/false);
510}
511
513 bool isWeak, Address dest, Address ptr,
514 Address val1, Address val2,
515 Expr *failureOrderExpr, uint64_t size,
516 cir::MemOrder successOrder,
517 cir::SyncScopeKind scope) {
518 Expr::EvalResult failureOrderEval;
519 if (failureOrderExpr->EvaluateAsInt(failureOrderEval, cgf.getContext())) {
520 uint64_t failureOrderInt = failureOrderEval.Val.getInt().getZExtValue();
521
522 cir::MemOrder failureOrder;
523 if (!cir::isValidCIRAtomicOrderingCABI(failureOrderInt)) {
524 failureOrder = cir::MemOrder::Relaxed;
525 } else {
526 switch ((cir::MemOrder)failureOrderInt) {
527 case cir::MemOrder::Relaxed:
528 // 31.7.2.18: "The failure argument shall not be memory_order_release
529 // nor memory_order_acq_rel". Fallback to monotonic.
530 case cir::MemOrder::Release:
531 case cir::MemOrder::AcquireRelease:
532 failureOrder = cir::MemOrder::Relaxed;
533 break;
534 case cir::MemOrder::Consume:
535 case cir::MemOrder::Acquire:
536 failureOrder = cir::MemOrder::Acquire;
537 break;
538 case cir::MemOrder::SequentiallyConsistent:
539 failureOrder = cir::MemOrder::SequentiallyConsistent;
540 break;
541 }
542 }
543
544 // Prior to c++17, "the failure argument shall be no stronger than the
545 // success argument". This condition has been lifted and the only
546 // precondition is 31.7.2.18. Effectively treat this as a DR and skip
547 // language version checks.
548 emitAtomicCmpXchg(cgf, e, isWeak, dest, ptr, val1, val2, size, successOrder,
549 failureOrder, scope);
550 return;
551 }
552
553 // The failure memory order is not a compile time constant. The CIR atomic ops
554 // require a constant value, so that memory order is known at compile time. In
555 // this case, we can switch based on the memory order and call each variant
556 // individually.
557 mlir::Value failureOrderVal = cgf.emitScalarExpr(failureOrderExpr);
558 mlir::Location atomicLoc = cgf.getLoc(e->getSourceRange());
559 cir::SwitchOp::create(
560 cgf.getBuilder(), atomicLoc, failureOrderVal,
561 [&](mlir::OpBuilder &b, mlir::Location loc, mlir::OperationState &os) {
562 mlir::Block *switchBlock = cgf.getBuilder().getBlock();
563
564 // case cir::MemOrder::Relaxed:
565 // // 31.7.2.18: "The failure argument shall not be
566 // memory_order_release
567 // // nor memory_order_acq_rel". Fallback to monotonic.
568 // case cir::MemOrder::Release:
569 // case cir::MemOrder::AcquireRelease:
570 // Note: Since there are 3 options, this makes sense to just emit as a
571 // 'default', which prevents user code from 'falling off' of this,
572 // which seems reasonable. Also, 'relaxed' being the default behavior
573 // is also probably the least harmful.
574 emitDefaultCaseLabel(cgf.getBuilder(), atomicLoc);
575 emitAtomicCmpXchg(cgf, e, isWeak, dest, ptr, val1, val2, size,
576 successOrder, cir::MemOrder::Relaxed, scope);
577 cgf.getBuilder().createBreak(atomicLoc);
578 cgf.getBuilder().setInsertionPointToEnd(switchBlock);
579
580 // case cir::MemOrder::Consume:
581 // case cir::MemOrder::Acquire:
582 emitMemOrderCaseLabel(cgf.getBuilder(), loc, failureOrderVal.getType(),
583 {cir::MemOrder::Consume, cir::MemOrder::Acquire});
584 emitAtomicCmpXchg(cgf, e, isWeak, dest, ptr, val1, val2, size,
585 successOrder, cir::MemOrder::Acquire, scope);
586 cgf.getBuilder().createBreak(atomicLoc);
587 cgf.getBuilder().setInsertionPointToEnd(switchBlock);
588
589 // case cir::MemOrder::SequentiallyConsistent:
590 emitMemOrderCaseLabel(cgf.getBuilder(), loc, failureOrderVal.getType(),
591 {cir::MemOrder::SequentiallyConsistent});
592 emitAtomicCmpXchg(cgf, e, isWeak, dest, ptr, val1, val2, size,
593 successOrder, cir::MemOrder::SequentiallyConsistent,
594 scope);
595 cgf.getBuilder().createBreak(atomicLoc);
596 cgf.getBuilder().setInsertionPointToEnd(switchBlock);
597
598 cgf.getBuilder().createYield(atomicLoc);
599 });
600}
601
602// A version of the emitAtomicCmpXchgFailureSet function that ALSO checks
603// whether it is 'weak' or not (by adding an 'if' around it, and calling
604// emitAtomicCmpXchgFailureSet 2x).
606 CIRGenFunction &cgf, AtomicExpr *e, Expr *isWeakExpr, Address dest,
607 Address ptr, Address val1, Address val2, Expr *failureOrderExpr,
608 uint64_t size, cir::MemOrder successOrder, cir::SyncScopeKind scope) {
609 mlir::Value isWeakVal = cgf.emitScalarExpr(isWeakExpr);
610 // The AST seems to be inserting a 'bool' cast (even in C mode) here, so we'll
611 // just emit it like a scalar.
612 assert(isWeakVal.getType() == cgf.getBuilder().getBoolTy());
613 mlir::Location atomicLoc = cgf.getLoc(e->getSourceRange());
614
615 // Unlike classic compiler, we use an 'if' here instead of a switch, simply to
616 // make this more readable/logical, plus we don't allow switch over a bool in
617 // CIR.
618 cir::IfOp::create(
619 cgf.getBuilder(), atomicLoc, isWeakVal, /*elseRegion=*/true,
620 [&](mlir::OpBuilder &b, mlir::Location loc) {
621 emitAtomicCmpXchgFailureSet(cgf, e, /*isWeak=*/true, dest, ptr, val1,
622 val2, failureOrderExpr, size, successOrder,
623 scope);
624 cgf.getBuilder().createYield(atomicLoc);
625 },
626 [&](mlir::OpBuilder &b, mlir::Location loc) {
627 emitAtomicCmpXchgFailureSet(cgf, e, /*isWeak=*/false, dest, ptr, val1,
628 val2, failureOrderExpr, size, successOrder,
629 scope);
630 cgf.getBuilder().createYield(atomicLoc);
631 });
632}
633
635 Address ptr, Address val1, Address val2,
636 Expr *isWeakExpr, Expr *failureOrderExpr, int64_t size,
637 cir::MemOrder order, cir::SyncScopeKind scope) {
639 llvm::StringRef opName;
640
641 CIRGenBuilderTy &builder = cgf.getBuilder();
642 mlir::Location loc = cgf.getLoc(expr->getSourceRange());
643 auto orderAttr = cir::MemOrderAttr::get(builder.getContext(), order);
644 auto scopeAttr = cir::SyncScopeKindAttr::get(builder.getContext(), scope);
645 cir::AtomicFetchKindAttr fetchAttr;
646 bool fetchFirst = true;
647
648 auto handleFetchOp = [&](cir::AtomicFetchKind kind) {
649 opName = cir::AtomicFetchOp::getOperationName();
650 fetchAttr = cir::AtomicFetchKindAttr::get(builder.getContext(), kind);
651 };
652
653 switch (expr->getOp()) {
654 case AtomicExpr::AO__c11_atomic_init:
655 llvm_unreachable("already handled!");
656
657 case AtomicExpr::AO__c11_atomic_compare_exchange_strong:
658 emitAtomicCmpXchgFailureSet(cgf, expr, /*isWeak=*/false, dest, ptr, val1,
659 val2, failureOrderExpr, size, order, scope);
660 return;
661
662 case AtomicExpr::AO__c11_atomic_compare_exchange_weak:
663 emitAtomicCmpXchgFailureSet(cgf, expr, /*isWeak=*/true, dest, ptr, val1,
664 val2, failureOrderExpr, size, order, scope);
665 return;
666
667 case AtomicExpr::AO__atomic_compare_exchange:
668 case AtomicExpr::AO__atomic_compare_exchange_n:
669 case AtomicExpr::AO__scoped_atomic_compare_exchange:
670 case AtomicExpr::AO__scoped_atomic_compare_exchange_n: {
671 bool isWeak = false;
672 if (isWeakExpr->EvaluateAsBooleanCondition(isWeak, cgf.getContext())) {
673 emitAtomicCmpXchgFailureSet(cgf, expr, isWeak, dest, ptr, val1, val2,
674 failureOrderExpr, size, order, scope);
675 } else {
676 emitAtomicCmpXchgFailureSetCheckWeak(cgf, expr, isWeakExpr, dest, ptr,
677 val1, val2, failureOrderExpr, size,
678 order, scope);
679 }
680 return;
681 }
682
683 case AtomicExpr::AO__c11_atomic_load:
684 case AtomicExpr::AO__atomic_load_n:
685 case AtomicExpr::AO__atomic_load:
686 case AtomicExpr::AO__scoped_atomic_load_n:
687 case AtomicExpr::AO__scoped_atomic_load: {
688 cir::LoadOp load =
689 builder.createLoad(loc, ptr, /*isVolatile=*/expr->isVolatile());
690
691 load->setAttr("mem_order", orderAttr);
692 load->setAttr("sync_scope", scopeAttr);
693
694 builder.createStore(loc, load->getResult(0), dest);
695 return;
696 }
697
698 case AtomicExpr::AO__c11_atomic_store:
699 case AtomicExpr::AO__atomic_store_n:
700 case AtomicExpr::AO__atomic_store:
701 case AtomicExpr::AO__scoped_atomic_store:
702 case AtomicExpr::AO__scoped_atomic_store_n: {
703 cir::LoadOp loadVal1 = builder.createLoad(loc, val1);
704
706
707 builder.createStore(loc, loadVal1, ptr, expr->isVolatile(),
708 /*align=*/mlir::IntegerAttr{}, scopeAttr, orderAttr);
709 return;
710 }
711
712 case AtomicExpr::AO__c11_atomic_exchange:
713 case AtomicExpr::AO__atomic_exchange_n:
714 case AtomicExpr::AO__atomic_exchange:
715 case AtomicExpr::AO__scoped_atomic_exchange_n:
716 case AtomicExpr::AO__scoped_atomic_exchange:
717 opName = cir::AtomicXchgOp::getOperationName();
718 break;
719
720 case AtomicExpr::AO__atomic_add_fetch:
721 case AtomicExpr::AO__scoped_atomic_add_fetch:
722 fetchFirst = false;
723 [[fallthrough]];
724 case AtomicExpr::AO__c11_atomic_fetch_add:
725 case AtomicExpr::AO__atomic_fetch_add:
726 case AtomicExpr::AO__scoped_atomic_fetch_add:
727 handleFetchOp(cir::AtomicFetchKind::Add);
728 break;
729
730 case AtomicExpr::AO__atomic_sub_fetch:
731 case AtomicExpr::AO__scoped_atomic_sub_fetch:
732 fetchFirst = false;
733 [[fallthrough]];
734 case AtomicExpr::AO__c11_atomic_fetch_sub:
735 case AtomicExpr::AO__atomic_fetch_sub:
736 case AtomicExpr::AO__scoped_atomic_fetch_sub:
737 handleFetchOp(cir::AtomicFetchKind::Sub);
738 break;
739
740 case AtomicExpr::AO__atomic_min_fetch:
741 case AtomicExpr::AO__scoped_atomic_min_fetch:
742 fetchFirst = false;
743 [[fallthrough]];
744 case AtomicExpr::AO__c11_atomic_fetch_min:
745 case AtomicExpr::AO__atomic_fetch_min:
746 case AtomicExpr::AO__scoped_atomic_fetch_min:
747 handleFetchOp(cir::AtomicFetchKind::Min);
748 break;
749
750 case AtomicExpr::AO__atomic_max_fetch:
751 case AtomicExpr::AO__scoped_atomic_max_fetch:
752 fetchFirst = false;
753 [[fallthrough]];
754 case AtomicExpr::AO__c11_atomic_fetch_max:
755 case AtomicExpr::AO__atomic_fetch_max:
756 case AtomicExpr::AO__scoped_atomic_fetch_max:
757 handleFetchOp(cir::AtomicFetchKind::Max);
758 break;
759
760 case AtomicExpr::AO__atomic_and_fetch:
761 case AtomicExpr::AO__scoped_atomic_and_fetch:
762 fetchFirst = false;
763 [[fallthrough]];
764 case AtomicExpr::AO__c11_atomic_fetch_and:
765 case AtomicExpr::AO__atomic_fetch_and:
766 case AtomicExpr::AO__scoped_atomic_fetch_and:
767 handleFetchOp(cir::AtomicFetchKind::And);
768 break;
769
770 case AtomicExpr::AO__atomic_or_fetch:
771 case AtomicExpr::AO__scoped_atomic_or_fetch:
772 fetchFirst = false;
773 [[fallthrough]];
774 case AtomicExpr::AO__c11_atomic_fetch_or:
775 case AtomicExpr::AO__atomic_fetch_or:
776 case AtomicExpr::AO__scoped_atomic_fetch_or:
777 handleFetchOp(cir::AtomicFetchKind::Or);
778 break;
779
780 case AtomicExpr::AO__atomic_xor_fetch:
781 case AtomicExpr::AO__scoped_atomic_xor_fetch:
782 fetchFirst = false;
783 [[fallthrough]];
784 case AtomicExpr::AO__c11_atomic_fetch_xor:
785 case AtomicExpr::AO__atomic_fetch_xor:
786 case AtomicExpr::AO__scoped_atomic_fetch_xor:
787 handleFetchOp(cir::AtomicFetchKind::Xor);
788 break;
789
790 case AtomicExpr::AO__atomic_nand_fetch:
791 case AtomicExpr::AO__scoped_atomic_nand_fetch:
792 fetchFirst = false;
793 [[fallthrough]];
794 case AtomicExpr::AO__c11_atomic_fetch_nand:
795 case AtomicExpr::AO__atomic_fetch_nand:
796 case AtomicExpr::AO__scoped_atomic_fetch_nand:
797 handleFetchOp(cir::AtomicFetchKind::Nand);
798 break;
799
800 case AtomicExpr::AO__atomic_test_and_set: {
801 auto op = cir::AtomicTestAndSetOp::create(
802 builder, loc, ptr.getPointer(), order,
803 builder.getI64IntegerAttr(ptr.getAlignment().getQuantity()),
804 expr->isVolatile());
805 builder.createStore(loc, op, dest);
806 return;
807 }
808
809 case AtomicExpr::AO__atomic_clear: {
810 cir::AtomicClearOp::create(
811 builder, loc, ptr.getPointer(), order,
812 builder.getI64IntegerAttr(ptr.getAlignment().getQuantity()),
813 expr->isVolatile());
814 return;
815 }
816
817 case AtomicExpr::AO__atomic_fetch_uinc:
818 case AtomicExpr::AO__scoped_atomic_fetch_uinc:
819 handleFetchOp(cir::AtomicFetchKind::UIncWrap);
820 break;
821
822 case AtomicExpr::AO__atomic_fetch_udec:
823 case AtomicExpr::AO__scoped_atomic_fetch_udec:
824 handleFetchOp(cir::AtomicFetchKind::UDecWrap);
825 break;
826
827 case AtomicExpr::AO__opencl_atomic_init:
828
829 case AtomicExpr::AO__hip_atomic_compare_exchange_strong:
830 case AtomicExpr::AO__opencl_atomic_compare_exchange_strong:
831
832 case AtomicExpr::AO__opencl_atomic_compare_exchange_weak:
833 case AtomicExpr::AO__hip_atomic_compare_exchange_weak:
834
835 case AtomicExpr::AO__opencl_atomic_load:
836 case AtomicExpr::AO__hip_atomic_load:
837
838 case AtomicExpr::AO__opencl_atomic_store:
839 case AtomicExpr::AO__hip_atomic_store:
840
841 case AtomicExpr::AO__hip_atomic_exchange:
842 case AtomicExpr::AO__opencl_atomic_exchange:
843
844 case AtomicExpr::AO__hip_atomic_fetch_add:
845 case AtomicExpr::AO__opencl_atomic_fetch_add:
846
847 case AtomicExpr::AO__hip_atomic_fetch_sub:
848 case AtomicExpr::AO__opencl_atomic_fetch_sub:
849
850 case AtomicExpr::AO__hip_atomic_fetch_min:
851 case AtomicExpr::AO__opencl_atomic_fetch_min:
852
853 case AtomicExpr::AO__hip_atomic_fetch_max:
854 case AtomicExpr::AO__opencl_atomic_fetch_max:
855
856 case AtomicExpr::AO__hip_atomic_fetch_and:
857 case AtomicExpr::AO__opencl_atomic_fetch_and:
858
859 case AtomicExpr::AO__hip_atomic_fetch_or:
860 case AtomicExpr::AO__opencl_atomic_fetch_or:
861
862 case AtomicExpr::AO__hip_atomic_fetch_xor:
863 case AtomicExpr::AO__opencl_atomic_fetch_xor:
864 cgf.cgm.errorNYI(expr->getSourceRange(), "emitAtomicOp: expr op NYI");
865 return;
866 }
867
868 assert(!opName.empty() && "expected operation name to build");
869 mlir::Value loadVal1 = builder.createLoad(loc, val1);
870
871 SmallVector<mlir::Value> atomicOperands = {ptr.getPointer(), loadVal1};
872 SmallVector<mlir::Type> atomicResTys = {loadVal1.getType()};
873 mlir::Operation *rmwOp = builder.create(loc, builder.getStringAttr(opName),
874 atomicOperands, atomicResTys);
875
876 if (fetchAttr)
877 rmwOp->setAttr("binop", fetchAttr);
878 rmwOp->setAttr("mem_order", orderAttr);
879 rmwOp->setAttr("sync_scope", scopeAttr);
880 if (expr->isVolatile())
881 rmwOp->setAttr("is_volatile", builder.getUnitAttr());
882 if (fetchFirst && opName == cir::AtomicFetchOp::getOperationName())
883 rmwOp->setAttr("fetch_first", builder.getUnitAttr());
884
885 mlir::Value result = rmwOp->getResult(0);
886
887 builder.createStore(loc, result, dest);
888}
889
890// Map clang sync scope to CIR sync scope.
891static cir::SyncScopeKind convertSyncScopeToCIR(CIRGenFunction &cgf,
892 SourceRange range,
893 clang::SyncScope scope) {
894 switch (scope) {
896 return cir::SyncScopeKind::SingleThread;
898 return cir::SyncScopeKind::System;
900 return cir::SyncScopeKind::Device;
902 return cir::SyncScopeKind::Workgroup;
904 return cir::SyncScopeKind::Wavefront;
906 return cir::SyncScopeKind::Cluster;
907
909 return cir::SyncScopeKind::HIPSingleThread;
911 return cir::SyncScopeKind::HIPSystem;
913 return cir::SyncScopeKind::HIPAgent;
915 return cir::SyncScopeKind::HIPWorkgroup;
917 return cir::SyncScopeKind::HIPWavefront;
919 return cir::SyncScopeKind::HIPCluster;
920
922 return cir::SyncScopeKind::OpenCLWorkGroup;
924 return cir::SyncScopeKind::OpenCLDevice;
926 return cir::SyncScopeKind::OpenCLAllSVMDevices;
928 return cir::SyncScopeKind::OpenCLSubGroup;
929 }
930
931 llvm_unreachable("unhandled sync scope");
932}
933
935 Address ptr, Address val1, Address val2,
936 Expr *isWeakExpr, Expr *failureOrderExpr, int64_t size,
937 cir::MemOrder order,
938 const std::optional<Expr::EvalResult> &scopeConst,
939 mlir::Value scopeValue) {
940 std::unique_ptr<AtomicScopeModel> scopeModel = expr->getScopeModel();
941
942 if (!scopeModel) {
943 emitAtomicOp(cgf, expr, dest, ptr, val1, val2, isWeakExpr, failureOrderExpr,
944 size, order, cir::SyncScopeKind::System);
945 return;
946 }
947
948 if (scopeConst.has_value()) {
949 cir::SyncScopeKind mappedScope = convertSyncScopeToCIR(
950 cgf, expr->getScope()->getSourceRange(),
951 scopeModel->map(scopeConst->Val.getInt().getZExtValue()));
952 emitAtomicOp(cgf, expr, dest, ptr, val1, val2, isWeakExpr, failureOrderExpr,
953 size, order, mappedScope);
954 return;
955 }
956
957 // The sync scope is not a compile-time constant. Emit a switch statement to
958 // handle each possible value of the sync scope.
959 CIRGenBuilderTy &builder = cgf.getBuilder();
960 mlir::Location loc = cgf.getLoc(expr->getSourceRange());
961 llvm::ArrayRef<unsigned> allScopes = scopeModel->getRuntimeValues();
962 unsigned fallback = scopeModel->getFallBackValue();
963
964 cir::SwitchOp::create(
965 builder, loc, scopeValue,
966 [&](mlir::OpBuilder &, mlir::Location loc, mlir::OperationState &) {
967 mlir::Block *switchBlock = builder.getBlock();
968
969 // Default case -- use fallback scope
970 cir::SyncScopeKind fallbackScope = convertSyncScopeToCIR(
971 cgf, expr->getScope()->getSourceRange(), scopeModel->map(fallback));
972 emitDefaultCaseLabel(builder, loc);
973 emitAtomicOp(cgf, expr, dest, ptr, val1, val2, isWeakExpr,
974 failureOrderExpr, size, order, fallbackScope);
975 builder.createBreak(loc);
976 builder.setInsertionPointToEnd(switchBlock);
977
978 // Emit a switch case for each non-fallback runtime scope value
979 for (unsigned scope : allScopes) {
980 if (scope == fallback)
981 continue;
982
983 cir::SyncScopeKind cirScope = convertSyncScopeToCIR(
984 cgf, expr->getScope()->getSourceRange(), scopeModel->map(scope));
985
986 mlir::ArrayAttr casesAttr = builder.getArrayAttr(
987 {cir::IntAttr::get(scopeValue.getType(), scope)});
988 mlir::OpBuilder::InsertPoint insertPoint;
989 cir::CaseOp::create(builder, loc, casesAttr, cir::CaseOpKind::Equal,
990 insertPoint);
991
992 builder.restoreInsertionPoint(insertPoint);
993 emitAtomicOp(cgf, expr, dest, ptr, val1, val2, isWeakExpr,
994 failureOrderExpr, size, order, cirScope);
995 builder.createBreak(loc);
996 builder.setInsertionPointToEnd(switchBlock);
997 }
998
999 builder.createYield(loc);
1000 });
1001}
1002
1003static std::optional<cir::MemOrder>
1004getEffectiveAtomicMemOrder(cir::MemOrder oriOrder, bool isStore, bool isLoad,
1005 bool isFence) {
1006 // Some memory orders are not supported by partial atomic operation:
1007 // {memory_order_releaxed} is not valid for fence operations.
1008 // {memory_order_consume, memory_order_acquire} are not valid for write-only
1009 // operations.
1010 // {memory_order_release} is not valid for read-only operations.
1011 // {memory_order_acq_rel} is only valid for read-write operations.
1012 if (isStore) {
1013 if (oriOrder == cir::MemOrder::Consume ||
1014 oriOrder == cir::MemOrder::Acquire ||
1015 oriOrder == cir::MemOrder::AcquireRelease)
1016 return std::nullopt;
1017 } else if (isLoad) {
1018 if (oriOrder == cir::MemOrder::Release ||
1019 oriOrder == cir::MemOrder::AcquireRelease)
1020 return std::nullopt;
1021 } else if (isFence) {
1022 if (oriOrder == cir::MemOrder::Relaxed)
1023 return std::nullopt;
1024 }
1025 // memory_order_consume is not implemented, it is always treated like
1026 // memory_order_acquire
1027 if (oriOrder == cir::MemOrder::Consume)
1028 return cir::MemOrder::Acquire;
1029 return oriOrder;
1030}
1031
1033 CIRGenFunction &cgf, mlir::Value order, bool isStore, bool isLoad,
1034 bool isFence, llvm::function_ref<void(cir::MemOrder)> emitAtomicOpFn) {
1035 if (!order)
1036 return;
1037 // The memory order is not known at compile-time. The atomic operations
1038 // can't handle runtime memory orders; the memory order must be hard coded.
1039 // Generate a "switch" statement that converts a runtime value into a
1040 // compile-time value.
1041 CIRGenBuilderTy &builder = cgf.getBuilder();
1042 cir::SwitchOp::create(
1043 builder, order.getLoc(), order,
1044 [&](mlir::OpBuilder &, mlir::Location loc, mlir::OperationState &) {
1045 mlir::Block *switchBlock = builder.getBlock();
1046
1047 auto emitMemOrderCase = [&](llvm::ArrayRef<cir::MemOrder> caseOrders) {
1048 // Checking there are same effective memory order for each case.
1049 for (int i = 1, e = caseOrders.size(); i < e; i++)
1050 assert((getEffectiveAtomicMemOrder(caseOrders[i - 1], isStore,
1051 isLoad, isFence) ==
1052 getEffectiveAtomicMemOrder(caseOrders[i], isStore, isLoad,
1053 isFence)) &&
1054 "Effective memory order must be same!");
1055 // Emit case label and atomic opeartion if neccessary.
1056 if (caseOrders.empty()) {
1057 emitDefaultCaseLabel(builder, loc);
1058 // There is no good way to report an unsupported memory order at
1059 // runtime, hence the fallback to memory_order_relaxed.
1060 if (!isFence)
1061 emitAtomicOpFn(cir::MemOrder::Relaxed);
1062 } else if (std::optional<cir::MemOrder> actualOrder =
1063 getEffectiveAtomicMemOrder(caseOrders[0], isStore,
1064 isLoad, isFence)) {
1065 // Included in default case.
1066 if (!isFence && actualOrder == cir::MemOrder::Relaxed)
1067 return;
1068 // Creating case operation for effective memory order. If there are
1069 // multiple cases in `caseOrders`, the actual order of each case
1070 // must be same, this needs to be guaranteed by the caller.
1071 emitMemOrderCaseLabel(builder, loc, order.getType(), caseOrders);
1072 emitAtomicOpFn(actualOrder.value());
1073 } else {
1074 // Do nothing if (!caseOrders.empty() && !actualOrder)
1075 return;
1076 }
1077 builder.createBreak(loc);
1078 builder.setInsertionPointToEnd(switchBlock);
1079 };
1080
1081 emitMemOrderCase(/*default:*/ {});
1082 emitMemOrderCase({cir::MemOrder::Relaxed});
1083 emitMemOrderCase({cir::MemOrder::Consume, cir::MemOrder::Acquire});
1084 emitMemOrderCase({cir::MemOrder::Release});
1085 emitMemOrderCase({cir::MemOrder::AcquireRelease});
1086 emitMemOrderCase({cir::MemOrder::SequentiallyConsistent});
1087
1088 builder.createYield(loc);
1089 });
1090}
1091
1093 const Expr *memOrder, bool isStore, bool isLoad, bool isFence,
1094 llvm::function_ref<void(cir::MemOrder)> emitAtomicOpFn) {
1095 // Emit the memory order operand, and try to evaluate it as a constant.
1096 Expr::EvalResult eval;
1097 if (memOrder->EvaluateAsInt(eval, getContext())) {
1098 uint64_t constOrder = eval.Val.getInt().getZExtValue();
1099 // We should not ever get to a case where the ordering isn't a valid CABI
1100 // value, but it's hard to enforce that in general.
1101 if (!cir::isValidCIRAtomicOrderingCABI(constOrder))
1102 return;
1103 cir::MemOrder oriOrder = static_cast<cir::MemOrder>(constOrder);
1104 if (std::optional<cir::MemOrder> actualOrder =
1105 getEffectiveAtomicMemOrder(oriOrder, isStore, isLoad, isFence))
1106 emitAtomicOpFn(actualOrder.value());
1107 return;
1108 }
1109
1110 // Otherwise, handle variable memory ordering. Emit `SwitchOp` to convert
1111 // dynamic value to static value.
1112 mlir::Value dynOrder = emitScalarExpr(memOrder);
1113 emitAtomicExprWithDynamicMemOrder(*this, dynOrder, isStore, isLoad, isFence,
1114 emitAtomicOpFn);
1115}
1116
1118 QualType atomicTy = e->getPtr()->getType()->getPointeeType();
1119 QualType memTy = atomicTy;
1120 if (const auto *ty = atomicTy->getAs<AtomicType>())
1121 memTy = ty->getValueType();
1122
1123 Expr *isWeakExpr = nullptr;
1124 Expr *orderFailExpr = nullptr;
1125
1126 Address val1 = Address::invalid();
1127 Address val2 = Address::invalid();
1128 Address dest = Address::invalid();
1130
1132 if (e->getOp() == AtomicExpr::AO__c11_atomic_init) {
1133 LValue lvalue = makeAddrLValue(ptr, atomicTy);
1134 emitAtomicInit(e->getVal1(), lvalue);
1135 return RValue::get(nullptr);
1136 }
1137
1138 TypeInfoChars typeInfo = getContext().getTypeInfoInChars(atomicTy);
1139 uint64_t size = typeInfo.Width.getQuantity();
1140
1141 // Emit the sync scope operand, and try to evaluate it as a constant.
1142 mlir::Value scope =
1143 e->getScopeModel() ? emitScalarExpr(e->getScope()) : nullptr;
1144 std::optional<Expr::EvalResult> scopeConst;
1145 if (Expr::EvalResult eval;
1146 e->getScopeModel() && e->getScope()->EvaluateAsInt(eval, getContext()))
1147 scopeConst.emplace(std::move(eval));
1148
1149 switch (e->getOp()) {
1150 default:
1151 cgm.errorNYI(e->getSourceRange(), "atomic op NYI");
1152 return RValue::get(nullptr);
1153
1154 case AtomicExpr::AO__c11_atomic_init:
1155 llvm_unreachable("already handled above with emitAtomicInit");
1156
1157 case AtomicExpr::AO__atomic_load_n:
1158 case AtomicExpr::AO__scoped_atomic_load_n:
1159 case AtomicExpr::AO__c11_atomic_load:
1160 case AtomicExpr::AO__atomic_test_and_set:
1161 case AtomicExpr::AO__atomic_clear:
1162 break;
1163
1164 case AtomicExpr::AO__atomic_load:
1165 case AtomicExpr::AO__scoped_atomic_load:
1166 dest = emitPointerWithAlignment(e->getVal1());
1167 break;
1168
1169 case AtomicExpr::AO__atomic_store:
1170 case AtomicExpr::AO__scoped_atomic_store:
1171 val1 = emitPointerWithAlignment(e->getVal1());
1172 break;
1173
1174 case AtomicExpr::AO__atomic_exchange:
1175 case AtomicExpr::AO__scoped_atomic_exchange:
1176 val1 = emitPointerWithAlignment(e->getVal1());
1177 dest = emitPointerWithAlignment(e->getVal2());
1178 break;
1179
1180 case AtomicExpr::AO__atomic_compare_exchange:
1181 case AtomicExpr::AO__atomic_compare_exchange_n:
1182 case AtomicExpr::AO__c11_atomic_compare_exchange_weak:
1183 case AtomicExpr::AO__c11_atomic_compare_exchange_strong:
1184 case AtomicExpr::AO__scoped_atomic_compare_exchange:
1185 case AtomicExpr::AO__scoped_atomic_compare_exchange_n:
1186 val1 = emitPointerWithAlignment(e->getVal1());
1187 if (e->getOp() == AtomicExpr::AO__atomic_compare_exchange ||
1188 e->getOp() == AtomicExpr::AO__scoped_atomic_compare_exchange)
1189 val2 = emitPointerWithAlignment(e->getVal2());
1190 else
1191 val2 = emitValToTemp(*this, e->getVal2());
1192 orderFailExpr = e->getOrderFail();
1193 if (e->getOp() == AtomicExpr::AO__atomic_compare_exchange_n ||
1194 e->getOp() == AtomicExpr::AO__atomic_compare_exchange ||
1195 e->getOp() == AtomicExpr::AO__scoped_atomic_compare_exchange_n ||
1196 e->getOp() == AtomicExpr::AO__scoped_atomic_compare_exchange)
1197 isWeakExpr = e->getWeak();
1198 break;
1199
1200 case AtomicExpr::AO__c11_atomic_fetch_add:
1201 case AtomicExpr::AO__c11_atomic_fetch_sub:
1202 if (memTy->isPointerType()) {
1203 // For pointer arithmetic, we're required to do a bit of math:
1204 // adding 1 to an int* is not the same as adding 1 to a uintptr_t.
1205 // ... but only for the C11 builtins. The GNU builtins expect the
1206 // user to multiply by sizeof(T).
1207 QualType val1Ty = e->getVal1()->getType();
1208 mlir::Location loc = getLoc(e->getSourceRange());
1209 mlir::Value val1Scalar = emitScalarExpr(e->getVal1());
1210 CharUnits pointeeIncAmt =
1211 getContext().getTypeSizeInChars(memTy->getPointeeType());
1212 mlir::Value scale = builder.getConstInt(loc, val1Scalar.getType(),
1213 pointeeIncAmt.getQuantity());
1214 val1Scalar = builder.createMul(loc, val1Scalar, scale);
1215 val1 = createMemTemp(val1Ty, loc, ".atomictmp");
1216 emitStoreOfScalar(val1Scalar, makeAddrLValue(val1, val1Ty),
1217 /*isInit=*/true);
1218 }
1219 [[fallthrough]];
1220 case AtomicExpr::AO__atomic_fetch_add:
1221 case AtomicExpr::AO__atomic_fetch_sub:
1222 case AtomicExpr::AO__atomic_add_fetch:
1223 case AtomicExpr::AO__atomic_sub_fetch:
1224 if (memTy->isPointerType()) {
1225 // Fetch-and-update atomic operation on pointers should treat the pointer
1226 // value as uintptr_t values
1227 if (!val1.isValid())
1228 val1 = emitValToTemp(*this, e->getVal1());
1229 ptr = ptr.withElementType(builder, val1.getElementType());
1230 break;
1231 }
1232 [[fallthrough]];
1233 case AtomicExpr::AO__atomic_fetch_max:
1234 case AtomicExpr::AO__atomic_fetch_min:
1235 case AtomicExpr::AO__atomic_max_fetch:
1236 case AtomicExpr::AO__atomic_min_fetch:
1237 case AtomicExpr::AO__c11_atomic_fetch_max:
1238 case AtomicExpr::AO__c11_atomic_fetch_min:
1239 case AtomicExpr::AO__scoped_atomic_fetch_add:
1240 case AtomicExpr::AO__scoped_atomic_fetch_max:
1241 case AtomicExpr::AO__scoped_atomic_fetch_min:
1242 case AtomicExpr::AO__scoped_atomic_fetch_sub:
1243 case AtomicExpr::AO__scoped_atomic_add_fetch:
1244 case AtomicExpr::AO__scoped_atomic_max_fetch:
1245 case AtomicExpr::AO__scoped_atomic_min_fetch:
1246 case AtomicExpr::AO__scoped_atomic_sub_fetch:
1247 [[fallthrough]];
1248
1249 case AtomicExpr::AO__atomic_fetch_and:
1250 case AtomicExpr::AO__atomic_fetch_nand:
1251 case AtomicExpr::AO__atomic_fetch_or:
1252 case AtomicExpr::AO__atomic_fetch_xor:
1253 case AtomicExpr::AO__atomic_and_fetch:
1254 case AtomicExpr::AO__atomic_nand_fetch:
1255 case AtomicExpr::AO__atomic_or_fetch:
1256 case AtomicExpr::AO__atomic_xor_fetch:
1257 case AtomicExpr::AO__atomic_exchange_n:
1258 case AtomicExpr::AO__atomic_store_n:
1259 case AtomicExpr::AO__c11_atomic_fetch_and:
1260 case AtomicExpr::AO__c11_atomic_fetch_nand:
1261 case AtomicExpr::AO__c11_atomic_fetch_or:
1262 case AtomicExpr::AO__c11_atomic_fetch_xor:
1263 case AtomicExpr::AO__c11_atomic_exchange:
1264 case AtomicExpr::AO__c11_atomic_store:
1265 case AtomicExpr::AO__scoped_atomic_fetch_and:
1266 case AtomicExpr::AO__scoped_atomic_fetch_nand:
1267 case AtomicExpr::AO__scoped_atomic_fetch_or:
1268 case AtomicExpr::AO__scoped_atomic_fetch_xor:
1269 case AtomicExpr::AO__scoped_atomic_and_fetch:
1270 case AtomicExpr::AO__scoped_atomic_nand_fetch:
1271 case AtomicExpr::AO__scoped_atomic_or_fetch:
1272 case AtomicExpr::AO__scoped_atomic_xor_fetch:
1273 case AtomicExpr::AO__scoped_atomic_store_n:
1274 case AtomicExpr::AO__scoped_atomic_exchange_n:
1275 case AtomicExpr::AO__atomic_fetch_uinc:
1276 case AtomicExpr::AO__atomic_fetch_udec:
1277 case AtomicExpr::AO__scoped_atomic_fetch_uinc:
1278 case AtomicExpr::AO__scoped_atomic_fetch_udec:
1279 val1 = emitValToTemp(*this, e->getVal1());
1280 break;
1281 }
1282
1283 QualType resultTy = e->getType().getUnqualifiedType();
1284
1285 bool shouldCastToIntPtrTy =
1287
1288 // The inlined atomics only function on iN types, where N is a power of 2. We
1289 // need to make sure (via temporaries if necessary) that all incoming values
1290 // are compatible.
1291 LValue atomicValue = makeAddrLValue(ptr, atomicTy);
1292 AtomicInfo atomics(*this, atomicValue, getLoc(e->getSourceRange()));
1293
1294 if (shouldCastToIntPtrTy) {
1295 ptr = atomics.castToAtomicIntPointer(ptr);
1296 if (val1.isValid())
1297 val1 = atomics.convertToAtomicIntPointer(val1);
1298 if (val2.isValid())
1299 val2 = atomics.convertToAtomicIntPointer(val2);
1300 }
1301 if (dest.isValid()) {
1302 if (shouldCastToIntPtrTy)
1303 dest = atomics.castToAtomicIntPointer(dest);
1304 } else if (e->isCmpXChg()) {
1305 dest = createMemTemp(resultTy, getLoc(e->getSourceRange()), "cmpxchg.bool");
1306 } else if (e->getOp() == AtomicExpr::AO__atomic_test_and_set) {
1307 dest = createMemTemp(resultTy, getLoc(e->getSourceRange()),
1308 "test_and_set.bool");
1309 } else if (!resultTy->isVoidType()) {
1310 dest = atomics.createTempAlloca();
1311 if (shouldCastToIntPtrTy)
1312 dest = atomics.castToAtomicIntPointer(dest);
1313 }
1314
1315 bool powerOf2Size = (size & (size - 1)) == 0;
1316 bool useLibCall = !powerOf2Size || (size > 16);
1317
1318 // For atomics larger than 16 bytes, emit a libcall from the frontend. This
1319 // avoids the overhead of dealing with excessively-large value types in IR.
1320 // Non-power-of-2 values also lower to libcall here, as they are not currently
1321 // permitted in IR instructions (although that constraint could be relaxed in
1322 // the future). For other cases where a libcall is required on a given
1323 // platform, we let the backend handle it (this includes handling for all of
1324 // the size-optimized libcall variants, which are only valid up to 16 bytes.)
1325 //
1326 // See: https://llvm.org/docs/Atomics.html#libcalls-atomic
1327 if (useLibCall) {
1329 cgm.errorNYI(e->getSourceRange(), "emitAtomicExpr: emit atomic lib call");
1330 return RValue::get(nullptr);
1331 }
1332
1333 bool isStore = e->getOp() == AtomicExpr::AO__c11_atomic_store ||
1334 e->getOp() == AtomicExpr::AO__opencl_atomic_store ||
1335 e->getOp() == AtomicExpr::AO__hip_atomic_store ||
1336 e->getOp() == AtomicExpr::AO__atomic_store ||
1337 e->getOp() == AtomicExpr::AO__atomic_store_n ||
1338 e->getOp() == AtomicExpr::AO__scoped_atomic_store ||
1339 e->getOp() == AtomicExpr::AO__scoped_atomic_store_n ||
1340 e->getOp() == AtomicExpr::AO__atomic_clear;
1341 bool isLoad = e->getOp() == AtomicExpr::AO__c11_atomic_load ||
1342 e->getOp() == AtomicExpr::AO__opencl_atomic_load ||
1343 e->getOp() == AtomicExpr::AO__hip_atomic_load ||
1344 e->getOp() == AtomicExpr::AO__atomic_load ||
1345 e->getOp() == AtomicExpr::AO__atomic_load_n ||
1346 e->getOp() == AtomicExpr::AO__scoped_atomic_load ||
1347 e->getOp() == AtomicExpr::AO__scoped_atomic_load_n;
1348
1349 auto emitAtomicOpCallBackFn = [&](cir::MemOrder memOrder) {
1350 emitAtomicOp(*this, e, dest, ptr, val1, val2, isWeakExpr, orderFailExpr,
1351 size, memOrder, scopeConst, scope);
1352 };
1353 emitAtomicExprWithMemOrder(e->getOrder(), isStore, isLoad, /*isFence*/ false,
1354 emitAtomicOpCallBackFn);
1355
1356 if (resultTy->isVoidType())
1357 return RValue::get(nullptr);
1358
1359 return convertTempToRValue(
1360 dest.withElementType(builder, convertTypeForMem(resultTy)), resultTy,
1361 e->getExprLoc());
1362}
1363
1365 AggValueSlot slot) {
1366 if (lvalue.getType()->isAtomicType())
1367 return emitAtomicLoad(lvalue, loc, cir::MemOrder::SequentiallyConsistent,
1368 /*isVolatile=*/lvalue.isVolatileQualified(), slot);
1369 return emitAtomicLoad(lvalue, loc, cir::MemOrder::Acquire,
1370 /*isVolatile=*/true, slot);
1371}
1372
1374 cir::MemOrder order, bool isVolatile,
1375 AggValueSlot slot) {
1376 AtomicInfo info(*this, lvalue, getLoc(loc));
1377 return info.emitAtomicLoad(slot, loc, /*asValue=*/true, order, isVolatile);
1378}
1379
1380void CIRGenFunction::emitAtomicStore(RValue rvalue, LValue dest, bool isInit) {
1381 bool isVolatile = dest.isVolatileQualified();
1382 auto order = cir::MemOrder::SequentiallyConsistent;
1383 if (!dest.getType()->isAtomicType()) {
1385 }
1386 return emitAtomicStore(rvalue, dest, order, isVolatile, isInit);
1387}
1388
1389/// Emit a store to an l-value of atomic type.
1390///
1391/// Note that the r-value is expected to be an r-value of the atomic type; this
1392/// means that for aggregate r-values, it should include storage for any padding
1393/// that was necessary.
1395 cir::MemOrder order, bool isVolatile,
1396 bool isInit) {
1397 // If this is an aggregate r-value, it should agree in type except
1398 // maybe for address-space qualification.
1399 mlir::Location loc = dest.getPointer().getLoc();
1400 assert(!rvalue.isAggregate() ||
1402 dest.getAddress().getElementType());
1403
1404 AtomicInfo atomics(*this, dest, loc);
1405 LValue lvalue = atomics.getAtomicLValue();
1406
1407 if (lvalue.isSimple()) {
1408 // If this is an initialization, just put the value there normally.
1409 if (isInit) {
1410 atomics.emitCopyIntoMemory(rvalue);
1411 return;
1412 }
1413
1414 // Check whether we should use a library call.
1415 if (atomics.shouldUseLibCall()) {
1417 cgm.errorNYI(loc, "emitAtomicStore: atomic store with library call");
1418 return;
1419 }
1420
1421 // Okay, we're doing this natively.
1422 mlir::Value valueToStore = atomics.convertRValueToInt(rvalue, loc);
1423
1424 // Do the atomic store.
1425 Address addr = atomics.getAtomicAddress();
1426 if (mlir::Value value = atomics.getScalarRValValueOrNull(rvalue)) {
1427 if (shouldCastToInt(value.getType(), /*CmpXchg=*/false)) {
1428 addr = atomics.castToAtomicIntPointer(addr);
1429 valueToStore =
1430 builder.createIntCast(valueToStore, addr.getElementType());
1431 }
1432 }
1433 cir::StoreOp store = builder.createStore(loc, valueToStore, addr);
1434
1435 // Initializations don't need to be atomic.
1436 if (!isInit) {
1438 store.setMemOrder(order);
1439 }
1440
1441 // Other decoration.
1442 if (isVolatile)
1443 store.setIsVolatile(true);
1444
1446 return;
1447 }
1448
1449 cgm.errorNYI(loc, "emitAtomicStore: non-simple atomic lvalue");
1451}
1452
1454 AtomicInfo atomics(*this, dest, getLoc(init->getSourceRange()));
1455
1456 switch (atomics.getEvaluationKind()) {
1457 case cir::TEK_Scalar: {
1458 mlir::Value value = emitScalarExpr(init);
1459 atomics.emitCopyIntoMemory(RValue::get(value));
1460 return;
1461 }
1462
1463 case cir::TEK_Complex: {
1464 mlir::Value value = emitComplexExpr(init);
1465 atomics.emitCopyIntoMemory(RValue::get(value));
1466 return;
1467 }
1468
1469 case cir::TEK_Aggregate: {
1470 // Fix up the destination if the initializer isn't an expression
1471 // of atomic type.
1472 bool zeroed = false;
1473 if (!init->getType()->isAtomicType()) {
1474 zeroed = atomics.emitMemSetZeroIfNecessary();
1475 dest = atomics.projectValue();
1476 }
1477
1478 // Evaluate the expression directly into the destination.
1484
1485 emitAggExpr(init, slot);
1486 return;
1487 }
1488 }
1489
1490 llvm_unreachable("bad evaluation kind");
1491}
static bool shouldCastToInt(mlir::Type valueTy, bool cmpxchg)
Return true if.
static Address emitValToTemp(CIRGenFunction &cgf, Expr *e)
static void emitAtomicCmpXchgFailureSetCheckWeak(CIRGenFunction &cgf, AtomicExpr *e, Expr *isWeakExpr, Address dest, Address ptr, Address val1, Address val2, Expr *failureOrderExpr, uint64_t size, cir::MemOrder successOrder, cir::SyncScopeKind scope)
static void emitAtomicCmpXchg(CIRGenFunction &cgf, AtomicExpr *e, bool isWeak, Address dest, Address ptr, Address val1, Address val2, uint64_t size, cir::MemOrder successOrder, cir::MemOrder failureOrder, cir::SyncScopeKind scope)
static void emitMemOrderCaseLabel(CIRGenBuilderTy &builder, mlir::Location loc, mlir::Type orderType, llvm::ArrayRef< cir::MemOrder > orders)
static cir::SyncScopeKind convertSyncScopeToCIR(CIRGenFunction &cgf, SourceRange range, clang::SyncScope scope)
static void emitAtomicExprWithDynamicMemOrder(CIRGenFunction &cgf, mlir::Value order, bool isStore, bool isLoad, bool isFence, llvm::function_ref< void(cir::MemOrder)> emitAtomicOpFn)
static void emitAtomicOp(CIRGenFunction &cgf, AtomicExpr *expr, Address dest, Address ptr, Address val1, Address val2, Expr *isWeakExpr, Expr *failureOrderExpr, int64_t size, cir::MemOrder order, cir::SyncScopeKind scope)
static void emitDefaultCaseLabel(CIRGenBuilderTy &builder, mlir::Location loc)
static bool isFullSizeType(CIRGenModule &cgm, mlir::Type ty, uint64_t expectedSize)
Does a store of the given IR type modify the full expected width?
static std::optional< cir::MemOrder > getEffectiveAtomicMemOrder(cir::MemOrder oriOrder, bool isStore, bool isLoad, bool isFence)
static void emitAtomicCmpXchgFailureSet(CIRGenFunction &cgf, AtomicExpr *e, bool isWeak, Address dest, Address ptr, Address val1, Address val2, Expr *failureOrderExpr, uint64_t size, cir::MemOrder successOrder, cir::SyncScopeKind scope)
*collection of selector each with an associated kind and an ordered *collection of selectors A selector has a kind
cir::BreakOp createBreak(mlir::Location loc)
Create a break operation.
mlir::Value createPtrBitcast(mlir::Value src, mlir::Type newPointeeTy)
mlir::Value createNot(mlir::Location loc, mlir::Value value)
cir::YieldOp createYield(mlir::Location loc, mlir::ValueRange value={})
Create a yield operation.
cir::BoolType getBoolTy()
llvm::TypeSize getTypeSizeInBits(mlir::Type ty) const
llvm::TypeSize getTypeStoreSize(mlir::Type ty) const
Returns the maximum number of bytes that may be overwritten by storing the specified type.
APSInt & getInt()
Definition APValue.h:508
TypeInfo getTypeInfo(const Type *T) const
Get the size and alignment of the specified complete type in bits.
int64_t toBits(CharUnits CharSize) const
Convert a size in characters to a size in bits.
const TargetInfo & getTargetInfo() const
Definition ASTContext.h:921
CharUnits toCharUnitsFromBits(int64_t BitSize) const
Convert a size in bits to a size in characters.
AtomicExpr - Variadic atomic builtins: __atomic_exchange, __atomic_fetch_*, __atomic_load,...
Definition Expr.h:6931
static std::unique_ptr< AtomicScopeModel > getScopeModel(AtomicOp Op)
Get atomic scope model for the atomic op code.
Definition Expr.h:7080
Expr * getVal2() const
Definition Expr.h:6982
Expr * getOrder() const
Definition Expr.h:6965
Expr * getScope() const
Definition Expr.h:6968
bool isCmpXChg() const
Definition Expr.h:7015
AtomicOp getOp() const
Definition Expr.h:6994
Expr * getVal1() const
Definition Expr.h:6972
Expr * getPtr() const
Definition Expr.h:6962
Expr * getWeak() const
Definition Expr.h:6988
Expr * getOrderFail() const
Definition Expr.h:6978
bool isVolatile() const
Definition Expr.h:7011
Address withPointer(mlir::Value newPtr) const
Return address with different pointer, but same element type and alignment.
Definition Address.h:83
mlir::Value getPointer() const
Definition Address.h:98
mlir::Type getElementType() const
Definition Address.h:125
static Address invalid()
Definition Address.h:76
Address withElementType(CIRGenBuilderTy &builder, mlir::Type ElemTy) const
Return address with different element type, a bitcast pointer, and the same alignment.
clang::CharUnits getAlignment() const
Definition Address.h:138
bool isValid() const
Definition Address.h:77
An aggregate value slot.
static AggValueSlot forLValue(const LValue &LV, IsDestructed_t isDestructed, IsAliased_t isAliased, Overlap_t mayOverlap, IsZeroed_t isZeroed=IsNotZeroed)
cir::StoreOp createStore(mlir::Location loc, mlir::Value val, Address dst, bool isVolatile=false, mlir::IntegerAttr align={}, cir::SyncScopeKindAttr scope={}, cir::MemOrderAttr order={})
cir::ConstantOp getConstInt(mlir::Location loc, llvm::APSInt intVal)
cir::MemSetOp createMemSet(mlir::Location loc, mlir::Value dst, mlir::Value val, mlir::Value len)
cir::LoadOp createLoad(mlir::Location loc, Address addr, bool isVolatile=false)
cir::IntType getUIntNTy(int n)
RValue convertTempToRValue(Address addr, clang::QualType type, clang::SourceLocation loc)
Given the address of a temporary variable, produce an r-value of its type.
Address emitPointerWithAlignment(const clang::Expr *expr, LValueBaseInfo *baseInfo=nullptr)
Given an expression with a pointer type, emit the value and compute our best estimate of the alignmen...
mlir::Value emitComplexExpr(const Expr *e)
Emit the computation of the specified expression of complex type, returning the result.
mlir::Location getLoc(clang::SourceLocation srcLoc)
Helpers to convert Clang's SourceLocation to a MLIR Location.
void emitAnyExprToMem(const Expr *e, Address location, Qualifiers quals, bool isInitializer)
Emits the code necessary to evaluate an arbitrary expression into the given memory location.
RValue emitAtomicExpr(AtomicExpr *e)
RValue emitAtomicLoad(LValue lvalue, SourceLocation loc, AggValueSlot slot=AggValueSlot::ignored())
mlir::Type convertTypeForMem(QualType t)
void emitStoreOfScalar(mlir::Value value, Address addr, bool isVolatile, clang::QualType ty, LValueBaseInfo baseInfo, bool isInit=false, bool isNontemporal=false)
void emitStoreOfComplex(mlir::Location loc, mlir::Value v, LValue dest, bool isInit)
EmitStoreOfComplex - Store a complex number into the specified l-value.
void emitAtomicExprWithMemOrder(const Expr *memOrder, bool isStore, bool isLoad, bool isFence, llvm::function_ref< void(cir::MemOrder)> emitAtomicOp)
mlir::Value emitToMemory(mlir::Value value, clang::QualType ty)
Given a value and its clang type, returns the value casted to its memory representation.
mlir::Value emitScalarExpr(const clang::Expr *e, bool ignoreResultAssign=false)
Emit the computation of the specified expression of scalar type.
CIRGenBuilderTy & getBuilder()
mlir::MLIRContext & getMLIRContext()
void emitAtomicInit(Expr *init, LValue dest)
LValue makeAddrLValue(Address addr, QualType ty, AlignmentSource source=AlignmentSource::Type)
void emitAtomicStore(RValue rvalue, LValue dest, bool isInit)
clang::ASTContext & getContext() const
mlir::Value emitFromMemory(mlir::Value value, clang::QualType ty)
EmitFromMemory - Change a scalar value from its memory representation to its value representation.
Address createMemTemp(QualType t, mlir::Location loc, const Twine &name="tmp", Address *alloca=nullptr, mlir::OpBuilder::InsertPoint ip={})
Create a temporary memory object of the given type, with appropriate alignmen and cast it to the defa...
void emitAggExpr(const clang::Expr *e, AggValueSlot slot)
This class organizes the cross-function state that is used while generating CIR code.
DiagnosticBuilder errorNYI(SourceLocation, llvm::StringRef)
Helpers to emit "not yet implemented" error diagnostics.
const cir::CIRDataLayout getDataLayout() const
Address getAddress() const
clang::QualType getType() const
mlir::Value getPointer() const
bool isVolatileQualified() const
bool isSimple() const
This trivial value class is used to represent the result of an expression that is evaluated.
Definition CIRGenValue.h:33
Address getAggregateAddress() const
Return the value of the address of the aggregate.
Definition CIRGenValue.h:69
bool isAggregate() const
Definition CIRGenValue.h:51
static RValue get(mlir::Value v)
Definition CIRGenValue.h:83
static RValue getAggregate(Address addr, bool isVolatile=false)
Convert an Address to an RValue.
mlir::Value getValue() const
Return the value of this scalar value.
Definition CIRGenValue.h:57
bool isScalar() const
Definition CIRGenValue.h:49
mlir::Value getComplexValue() const
Return the value of this complex value.
Definition CIRGenValue.h:63
CharUnits - This is an opaque type for sizes expressed in character units.
Definition CharUnits.h:38
llvm::Align getAsAlign() const
getAsAlign - Returns Quantity as a valid llvm::Align, Beware llvm::Align assumes power of two 8-bit b...
Definition CharUnits.h:189
QuantityType getQuantity() const
getQuantity - Get the raw integer representation of this quantity.
Definition CharUnits.h:185
This represents one expression.
Definition Expr.h:112
bool EvaluateAsInt(EvalResult &Result, const ASTContext &Ctx, SideEffectsKind AllowSideEffects=SE_NoSideEffects, bool InConstantContext=false) const
EvaluateAsInt - Return true if this is a constant which we can fold and convert to an integer,...
bool EvaluateAsBooleanCondition(bool &Result, const ASTContext &Ctx, bool InConstantContext=false) const
EvaluateAsBooleanCondition - Return true if this is a constant which we can fold and convert to a boo...
SourceLocation getExprLoc() const LLVM_READONLY
getExprLoc - Return the preferred location for the arrow when diagnosing a problem with a generic exp...
Definition Expr.cpp:283
QualType getType() const
Definition Expr.h:144
A (possibly-)qualified type.
Definition TypeBase.h:937
Qualifiers getQualifiers() const
Retrieve the set of qualifiers applied to this type.
Definition TypeBase.h:8487
QualType getUnqualifiedType() const
Retrieve the unqualified variant of the given type, removing as little sugar as possible.
Definition TypeBase.h:8541
Encodes a location in the source.
A trivial tuple used to represent a source range.
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition Stmt.cpp:343
virtual bool hasBuiltinAtomic(uint64_t AtomicSizeInBits, uint64_t AlignmentInBits) const
Returns true if the given target supports lock-free atomic operations at the specified width and alig...
Definition TargetInfo.h:865
bool isVoidType() const
Definition TypeBase.h:9050
bool isPointerType() const
Definition TypeBase.h:8684
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.
Definition Type.cpp:789
bool isAtomicType() const
Definition TypeBase.h:8876
const T * getAs() const
Member-template getAs<specific type>'.
Definition TypeBase.h:9277
bool isValidCIRAtomicOrderingCABI(Int value)
const internal::VariadicDynCastAllOfMatcher< Stmt, Expr > expr
Matches expressions.
@ Address
A pointer to a ValueDecl.
Definition Primitives.h:28
The JSON file list parser is used to communicate input to InstallAPI.
bool isa(CodeGen::Address addr)
Definition Address.h:330
SyncScope
Defines sync scope values used internally by clang.
Definition SyncScope.h:42
unsigned long uint64_t
static bool atomicInfoGetAtomicPointer()
static bool aggValueSlotGC()
static bool opLoadStoreAtomic()
static bool opLoadStoreTbaa()
static bool atomicUseLibCall()
static bool atomicOpenMP()
static bool atomicMicrosoftVolatile()
static bool atomicSyncScopeID()
static bool atomicInfoGetAtomicAddress()
EvalResult is a struct with detailed info about an evaluated expression.
Definition Expr.h:652
APValue Val
Val - This is the value the expression can be folded to.
Definition Expr.h:654