https://github.com/erichkeane updated https://github.com/llvm/llvm-project/pull/227054
>From 3818998712fa877add45171ab103e6c725795ac6 Mon Sep 17 00:00:00 2001 From: erichkeane <[email protected]> Date: Mon, 28 Sep 2026 10:21:17 -0700 Subject: [PATCH 1/3] [CIR] Correct 2 lowering bugs of atomic cmp-xchng builtins This patch fixese two bugs that showed up in a benchmark. First; convertToAtomicIntPointer was zero-filling the source object directly, rather than the temporary. The result was that anything that would not be overwritten thanks to the power-of-2 write, would be incorrect, and corrupted. Second; emitAtomicCmpXchg didn't set the 'old' value back into the real object. This ends up doing an additional argument on this function that better matches classic-codegen. --- clang/lib/CIR/CodeGen/CIRGenAtomic.cpp | 129 ++++++++++++++----------- clang/test/CIR/CodeGen/atomic.c | 72 ++++++++++++-- 2 files changed, 141 insertions(+), 60 deletions(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenAtomic.cpp b/clang/lib/CIR/CodeGen/CIRGenAtomic.cpp index e89be4fc60975..fc47e732bc8a0 100644 --- a/clang/lib/CIR/CodeGen/CIRGenAtomic.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenAtomic.cpp @@ -212,9 +212,9 @@ Address AtomicInfo::convertToAtomicIntPointer(Address addr, cgf.getContext().toCharUnitsFromBits(atomicSizeInBits).getQuantity(); mlir::Value memSetSize = builder.getConstInt(loc, cgf.cgm.uInt64Ty, size); addr = addr.withElementType(builder, cgf.cgm.voidTy); - builder.createMemSet(loc, addr, zero, memSetSize); - tmp = tmp.withElementType(builder, cgf.cgm.voidTy); + builder.createMemSet(loc, tmp, zero, memSetSize); + builder.createMemCpy( loc, tmp, addr, builder.getConstInt(loc, cgf.cgm.uInt64Ty, @@ -484,8 +484,8 @@ static void emitMemOrderCaseLabel(CIRGenBuilderTy &builder, mlir::Location loc, static void emitAtomicCmpXchg(CIRGenFunction &cgf, AtomicExpr *e, bool isWeak, Address dest, Address ptr, Address val1, - Address val2, uint64_t size, - cir::MemOrder successOrder, + Address val2, Address expectedResult, + uint64_t size, cir::MemOrder successOrder, cir::MemOrder failureOrder, cir::SyncScopeKind scope) { mlir::Location loc = cgf.getLoc(e->getSourceRange()); @@ -506,17 +506,30 @@ static void emitAtomicCmpXchg(CIRGenFunction &cgf, AtomicExpr *e, bool isWeak, cmpxchg.setWeak(isWeak); mlir::Value failed = builder.createNot(cmpxchg.getSuccess()); - cir::IfOp::create(builder, loc, failed, /*withElseRegion=*/false, - [&](mlir::OpBuilder &, mlir::Location) { - auto ptrTy = mlir::cast<cir::PointerType>( - val1.getPointer().getType()); - if (val1.getElementType() != ptrTy.getPointee()) { - val1 = val1.withPointer(builder.createPtrBitcast( - val1.getPointer(), val1.getElementType())); - } - builder.createStore(loc, cmpxchg.getOld(), val1); - builder.createYield(loc); - }); + cir::IfOp::create( + builder, loc, failed, /*withElseRegion=*/false, + [&](mlir::OpBuilder &, mlir::Location) { + uint64_t expectedSizeInBytes = cgf.cgm.getDataLayout().getTypeStoreSize( + expectedResult.getElementType()); + + if (expectedSizeInBytes == size) { + Address storeAddr = expectedResult.withElementType( + builder, cmpxchg.getOld().getType()); + builder.createStore(loc, cmpxchg.getOld(), storeAddr); + } else { + Address oldTmp = cgf.createTempAlloca( + cmpxchg.getOld().getType(), ptr.getAlignment(), loc, "old.tmp"); + builder.createStore(loc, cmpxchg.getOld(), oldTmp); + + Address oldTmpVoid = oldTmp.withElementType(builder, cgf.cgm.voidTy); + Address expectedVoid = + expectedResult.withElementType(builder, cgf.cgm.voidTy); + builder.createMemCpy( + loc, expectedVoid, oldTmpVoid, + builder.getConstInt(loc, cgf.cgm.uInt64Ty, expectedSizeInBytes)); + } + builder.createYield(loc); + }); // Update the memory at Dest with Success's value. cgf.emitStoreOfScalar(cmpxchg.getSuccess(), @@ -524,12 +537,10 @@ static void emitAtomicCmpXchg(CIRGenFunction &cgf, AtomicExpr *e, bool isWeak, /*isInit=*/false); } -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) { +static void emitAtomicCmpXchgFailureSet( + CIRGenFunction &cgf, AtomicExpr *e, bool isWeak, Address dest, Address ptr, + Address val1, Address val2, Address expectedResult, Expr *failureOrderExpr, + uint64_t size, cir::MemOrder successOrder, cir::SyncScopeKind scope) { Expr::EvalResult failureOrderEval; if (failureOrderExpr->EvaluateAsInt(failureOrderEval, cgf.getContext())) { uint64_t failureOrderInt = failureOrderEval.Val.getInt().getZExtValue(); @@ -560,8 +571,8 @@ static void emitAtomicCmpXchgFailureSet(CIRGenFunction &cgf, AtomicExpr *e, // success argument". This condition has been lifted and the only // precondition is 31.7.2.18. Effectively treat this as a DR and skip // language version checks. - emitAtomicCmpXchg(cgf, e, isWeak, dest, ptr, val1, val2, size, successOrder, - failureOrder, scope); + emitAtomicCmpXchg(cgf, e, isWeak, dest, ptr, val1, val2, expectedResult, + size, successOrder, failureOrder, scope); return; } @@ -587,8 +598,8 @@ static void emitAtomicCmpXchgFailureSet(CIRGenFunction &cgf, AtomicExpr *e, // which seems reasonable. Also, 'relaxed' being the default behavior // is also probably the least harmful. emitDefaultCaseLabel(cgf.getBuilder(), atomicLoc); - emitAtomicCmpXchg(cgf, e, isWeak, dest, ptr, val1, val2, size, - successOrder, cir::MemOrder::Relaxed, scope); + emitAtomicCmpXchg(cgf, e, isWeak, dest, ptr, val1, val2, expectedResult, + size, successOrder, cir::MemOrder::Relaxed, scope); cgf.getBuilder().createBreak(atomicLoc); cgf.getBuilder().setInsertionPointToEnd(switchBlock); @@ -596,17 +607,17 @@ static void emitAtomicCmpXchgFailureSet(CIRGenFunction &cgf, AtomicExpr *e, // case cir::MemOrder::Acquire: emitMemOrderCaseLabel(cgf.getBuilder(), loc, failureOrderVal.getType(), {cir::MemOrder::Consume, cir::MemOrder::Acquire}); - emitAtomicCmpXchg(cgf, e, isWeak, dest, ptr, val1, val2, size, - successOrder, cir::MemOrder::Acquire, scope); + emitAtomicCmpXchg(cgf, e, isWeak, dest, ptr, val1, val2, expectedResult, + size, successOrder, cir::MemOrder::Acquire, scope); cgf.getBuilder().createBreak(atomicLoc); cgf.getBuilder().setInsertionPointToEnd(switchBlock); // case cir::MemOrder::SequentiallyConsistent: emitMemOrderCaseLabel(cgf.getBuilder(), loc, failureOrderVal.getType(), {cir::MemOrder::SequentiallyConsistent}); - emitAtomicCmpXchg(cgf, e, isWeak, dest, ptr, val1, val2, size, - successOrder, cir::MemOrder::SequentiallyConsistent, - scope); + emitAtomicCmpXchg(cgf, e, isWeak, dest, ptr, val1, val2, expectedResult, + size, successOrder, + cir::MemOrder::SequentiallyConsistent, scope); cgf.getBuilder().createBreak(atomicLoc); cgf.getBuilder().setInsertionPointToEnd(switchBlock); @@ -619,8 +630,9 @@ static void emitAtomicCmpXchgFailureSet(CIRGenFunction &cgf, AtomicExpr *e, // emitAtomicCmpXchgFailureSet 2x). 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) { + Address ptr, Address val1, Address val2, Address expectedResult, + Expr *failureOrderExpr, uint64_t size, cir::MemOrder successOrder, + cir::SyncScopeKind scope) { mlir::Value isWeakVal = cgf.emitScalarExpr(isWeakExpr); // The AST seems to be inserting a 'bool' cast (even in C mode) here, so we'll // just emit it like a scalar. @@ -634,21 +646,22 @@ static void emitAtomicCmpXchgFailureSetCheckWeak( cgf.getBuilder(), atomicLoc, isWeakVal, /*elseRegion=*/true, [&](mlir::OpBuilder &b, mlir::Location loc) { emitAtomicCmpXchgFailureSet(cgf, e, /*isWeak=*/true, dest, ptr, val1, - val2, failureOrderExpr, size, successOrder, - scope); + val2, expectedResult, failureOrderExpr, + size, successOrder, scope); cgf.getBuilder().createYield(atomicLoc); }, [&](mlir::OpBuilder &b, mlir::Location loc) { emitAtomicCmpXchgFailureSet(cgf, e, /*isWeak=*/false, dest, ptr, val1, - val2, failureOrderExpr, size, successOrder, - scope); + val2, expectedResult, failureOrderExpr, + size, successOrder, scope); cgf.getBuilder().createYield(atomicLoc); }); } static void emitAtomicOp(CIRGenFunction &cgf, AtomicExpr *expr, Address dest, Address ptr, Address val1, Address val2, - Expr *isWeakExpr, Expr *failureOrderExpr, int64_t size, + Address expectedResult, Expr *isWeakExpr, + Expr *failureOrderExpr, int64_t size, cir::MemOrder order, cir::SyncScopeKind scope) { assert(!cir::MissingFeatures::atomicSyncScopeID()); llvm::StringRef opName; @@ -673,14 +686,16 @@ static void emitAtomicOp(CIRGenFunction &cgf, AtomicExpr *expr, Address dest, case AtomicExpr::AO__hip_atomic_compare_exchange_strong: case AtomicExpr::AO__opencl_atomic_compare_exchange_strong: emitAtomicCmpXchgFailureSet(cgf, expr, /*isWeak=*/false, dest, ptr, val1, - val2, failureOrderExpr, size, order, scope); + val2, expectedResult, failureOrderExpr, size, + order, scope); return; case AtomicExpr::AO__c11_atomic_compare_exchange_weak: case AtomicExpr::AO__hip_atomic_compare_exchange_weak: case AtomicExpr::AO__opencl_atomic_compare_exchange_weak: emitAtomicCmpXchgFailureSet(cgf, expr, /*isWeak=*/true, dest, ptr, val1, - val2, failureOrderExpr, size, order, scope); + val2, expectedResult, failureOrderExpr, size, + order, scope); return; case AtomicExpr::AO__atomic_compare_exchange: @@ -690,11 +705,12 @@ static void emitAtomicOp(CIRGenFunction &cgf, AtomicExpr *expr, Address dest, bool isWeak = false; if (isWeakExpr->EvaluateAsBooleanCondition(isWeak, cgf.getContext())) { emitAtomicCmpXchgFailureSet(cgf, expr, isWeak, dest, ptr, val1, val2, - failureOrderExpr, size, order, scope); + expectedResult, failureOrderExpr, size, order, + scope); } else { - emitAtomicCmpXchgFailureSetCheckWeak(cgf, expr, isWeakExpr, dest, ptr, - val1, val2, failureOrderExpr, size, - order, scope); + emitAtomicCmpXchgFailureSetCheckWeak( + cgf, expr, isWeakExpr, dest, ptr, val1, val2, expectedResult, + failureOrderExpr, size, order, scope); } return; } @@ -965,15 +981,16 @@ static cir::SyncScopeKind convertSyncScopeToCIR(CIRGenFunction &cgf, static void emitAtomicOp(CIRGenFunction &cgf, AtomicExpr *expr, Address dest, Address ptr, Address val1, Address val2, - Expr *isWeakExpr, Expr *failureOrderExpr, int64_t size, + Address expectedResult, Expr *isWeakExpr, + Expr *failureOrderExpr, int64_t size, cir::MemOrder order, const std::optional<Expr::EvalResult> &scopeConst, mlir::Value scopeValue) { std::unique_ptr<AtomicScopeModel> scopeModel = expr->getScopeModel(); if (!scopeModel) { - emitAtomicOp(cgf, expr, dest, ptr, val1, val2, isWeakExpr, failureOrderExpr, - size, order, cir::SyncScopeKind::System); + emitAtomicOp(cgf, expr, dest, ptr, val1, val2, expectedResult, isWeakExpr, + failureOrderExpr, size, order, cir::SyncScopeKind::System); return; } @@ -981,8 +998,8 @@ static void emitAtomicOp(CIRGenFunction &cgf, AtomicExpr *expr, Address dest, cir::SyncScopeKind mappedScope = convertSyncScopeToCIR( cgf, expr->getScope()->getSourceRange(), scopeModel->map(scopeConst->Val.getInt().getZExtValue())); - emitAtomicOp(cgf, expr, dest, ptr, val1, val2, isWeakExpr, failureOrderExpr, - size, order, mappedScope); + emitAtomicOp(cgf, expr, dest, ptr, val1, val2, expectedResult, isWeakExpr, + failureOrderExpr, size, order, mappedScope); return; } @@ -1002,8 +1019,8 @@ static void emitAtomicOp(CIRGenFunction &cgf, AtomicExpr *expr, Address dest, cir::SyncScopeKind fallbackScope = convertSyncScopeToCIR( cgf, expr->getScope()->getSourceRange(), scopeModel->map(fallback)); emitDefaultCaseLabel(builder, loc); - emitAtomicOp(cgf, expr, dest, ptr, val1, val2, isWeakExpr, - failureOrderExpr, size, order, fallbackScope); + emitAtomicOp(cgf, expr, dest, ptr, val1, val2, expectedResult, + isWeakExpr, failureOrderExpr, size, order, fallbackScope); builder.createBreak(loc); builder.setInsertionPointToEnd(switchBlock); @@ -1022,8 +1039,8 @@ static void emitAtomicOp(CIRGenFunction &cgf, AtomicExpr *expr, Address dest, insertPoint); builder.restoreInsertionPoint(insertPoint); - emitAtomicOp(cgf, expr, dest, ptr, val1, val2, isWeakExpr, - failureOrderExpr, size, order, cirScope); + emitAtomicOp(cgf, expr, dest, ptr, val1, val2, expectedResult, + isWeakExpr, failureOrderExpr, size, order, cirScope); builder.createBreak(loc); builder.setInsertionPointToEnd(switchBlock); } @@ -1589,6 +1606,10 @@ RValue CIRGenFunction::emitAtomicExpr(AtomicExpr *e) { LValue atomicValue = makeAddrLValue(ptr, atomicTy); AtomicInfo atomics(*this, atomicValue, loc); + // Save val1's address before it is (possibly) converted to a temporary + // sized to the full atomic width below. + Address originalVal1 = val1; + if (shouldCastToIntPtrTy) { ptr = atomics.castToAtomicIntPointer(ptr); if (val1.isValid()) @@ -1642,8 +1663,8 @@ RValue CIRGenFunction::emitAtomicExpr(AtomicExpr *e) { e->getOp() == AtomicExpr::AO__scoped_atomic_load_n; auto emitAtomicOpCallBackFn = [&](cir::MemOrder memOrder) { - emitAtomicOp(*this, e, dest, ptr, val1, val2, isWeakExpr, orderFailExpr, - size, memOrder, scopeConst, scope); + emitAtomicOp(*this, e, dest, ptr, val1, val2, originalVal1, isWeakExpr, + orderFailExpr, size, memOrder, scopeConst, scope); }; emitAtomicExprWithMemOrder(e->getOrder(), isStore, isLoad, /*isFence*/ false, emitAtomicOpCallBackFn); diff --git a/clang/test/CIR/CodeGen/atomic.c b/clang/test/CIR/CodeGen/atomic.c index d2fb28116e7bd..bd6e49749f90f 100644 --- a/clang/test/CIR/CodeGen/atomic.c +++ b/clang/test/CIR/CodeGen/atomic.c @@ -3979,17 +3979,17 @@ void store_atomic_different_size(S a) { __c11_atomic_store(&b, a, __ATOMIC_SEQ_CST); // CIR: %[[A_ADDR:.*]] = cir.alloca "a" {{.*}} init : !cir.ptr<!rec_S> - // CIR: %[[B_ADDR:.*]] = cir.alloca "b" {{.*}} : !cir.ptr<!rec_anon_struct1> + // CIR: %[[B_ADDR:.*]] = cir.alloca "b" {{.*}} : !cir.ptr<!rec_anon_struct2> // CIR: %[[A_ATOMIC_TMP_ADDR:.*]] = cir.alloca ".atomictmp" {{.*}} : !cir.ptr<!rec_S> - // CIR: %[[ATOMIC_TMP_ADDR:.*]] = cir.alloca "atomic-temp" {{.*}} : !cir.ptr<!rec_anon_struct1> + // CIR: %[[ATOMIC_TMP_ADDR:.*]] = cir.alloca "atomic-temp" {{.*}} : !cir.ptr<!rec_anon_struct2> // CIR: cir.store %[[A:.*]], %[[A_ADDR]] : !rec_S, !cir.ptr<!rec_S> // CIR: cir.copy %[[A_ADDR]] {{.*}} to %[[A_ATOMIC_TMP_ADDR]] {{.*}} : !cir.ptr<!rec_S> - // CIR: %[[B_VOID_PTR:.*]] = cir.cast bitcast %[[B_ADDR]] : !cir.ptr<!rec_anon_struct1> -> !cir.ptr<!u32i> + // CIR: %[[B_VOID_PTR:.*]] = cir.cast bitcast %[[B_ADDR]] : !cir.ptr<!rec_anon_struct2> -> !cir.ptr<!u32i> // CIR: %[[CONST_0:.*]] = cir.const #cir.int<0> : !u8i // CIR: %[[MEMSET_SIZE:.*]] = cir.const #cir.int<4> : !u64i // CIR: %[[A_VOID_PTR:.*]] = cir.cast bitcast %[[A_ATOMIC_TMP_ADDR]] : !cir.ptr<!rec_S> -> !cir.ptr<!void> - // CIR: cir.libc.memset %[[MEMSET_SIZE]] bytes at %[[A_VOID_PTR]] {{.*}} to %[[CONST_0]] : !cir.ptr<!void>, !u8i, !u64i - // CIR: %[[ATOMIC_TMP:.*]] = cir.cast bitcast %[[ATOMIC_TMP_ADDR]] : !cir.ptr<!rec_anon_struct1> -> !cir.ptr<!void> + // CIR: %[[ATOMIC_TMP:.*]] = cir.cast bitcast %[[ATOMIC_TMP_ADDR]] : !cir.ptr<!rec_anon_struct2> -> !cir.ptr<!void> + // CIR: cir.libc.memset %[[MEMSET_SIZE]] bytes at %[[ATOMIC_TMP]] {{.*}} to %[[CONST_0]] : !cir.ptr<!void>, !u8i, !u64i // CIR: %[[MEMCPY_SIZE:.*]] = cir.const #cir.int<3> : !u64i // CIR: cir.libc.memcpy %[[MEMCPY_SIZE]] bytes from %[[A_VOID_PTR]] align(1) to %[[ATOMIC_TMP]] align(4) : !u64i, !cir.ptr<!void> -> !cir.ptr<!void> // CIR: %[[ATOMIC_TMP_U32:.*]] = cir.cast bitcast %[[ATOMIC_TMP]] : !cir.ptr<!void> -> !cir.ptr<!u32i> @@ -4005,7 +4005,7 @@ void store_atomic_different_size(S a) { // LLVM: %[[ATOMIC_TMP_ADDR:.*]] = alloca { %struct.S, [1 x i8] }, align 4 // LLVM: store %struct.S %[[A]], ptr %[[A_ADDR]], align 1 // LLVM: call void @llvm.memcpy.p0.p0.i64(ptr align 1 %[[A_ATOMIC_TMP_ADDR]], ptr align 1 %[[A_ADDR]], i64 3, i1 false) - // LLVM: call void @llvm.memset.p0.i64(ptr align 1 %[[A_ATOMIC_TMP_ADDR]], i8 0, i64 4, i1 false) + // LLVM: call void @llvm.memset.p0.i64(ptr align 4 %[[ATOMIC_TMP_ADDR]], i8 0, i64 4, i1 false) // LLVM: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %[[ATOMIC_TMP_ADDR]], ptr align 1 %[[A_ATOMIC_TMP_ADDR]], i64 3, i1 false) // LLVM: %[[ATOMIC_TMP:.*]] = load i32, ptr %[[ATOMIC_TMP_ADDR]], align 4 // LLVM: store atomic i32 %[[ATOMIC_TMP]], ptr %[[B_ADDR]] seq_cst, align 4 @@ -4237,3 +4237,63 @@ float atomic_float_post_dec(_Atomic(float) *p) { return (*p)--; } // OGCG-LABEL: @atomic_float_post_dec // OGCG: %[[OLD:.+]] = atomicrmw fsub ptr %{{.+}}, float 1.000000e+00 seq_cst, align 4 // OGCG: ret float %[[OLD]] + +struct S2 { char c; int i; char c2; }; + +_Bool atomic_cmpxchg_struct_padding(_Atomic(struct S2) *ptr, struct S2 *expected, struct S2 *desired) { + return __c11_atomic_compare_exchange_strong(ptr, expected, *desired, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); +} + +// CIR-LABEL: @atomic_cmpxchg_struct_padding +// CIR: %[[DESIRED_TMP:.+]] = cir.alloca ".atomictmp" {{.*}} : !cir.ptr<!rec_S2> +// CIR: %[[EXPECTED_ATOMIC_TMP_ADDR:.+]] = cir.alloca "atomic-temp" {{.*}} : !cir.ptr<!rec_anon_struct{{[0-9]*}}> +// CIR: %[[OLD_TMP:.+]] = cir.alloca "old.tmp" {{.*}} : !cir.ptr<!u128i> +// CIR: %[[PTR:.+]] = cir.load align(8) %{{.+}} : !cir.ptr<!cir.ptr<!rec_anon_struct{{[0-9]*}}>>, !cir.ptr<!rec_anon_struct{{[0-9]*}}> +// CIR: %[[EXPECTED:.+]] = cir.load align(8) %{{.+}} : !cir.ptr<!cir.ptr<!rec_S2>>, !cir.ptr<!rec_S2> +// CIR: %[[DESIRED:.+]] = cir.load deref align(8) %{{.+}} : !cir.ptr<!cir.ptr<!rec_S2>>, !cir.ptr<!rec_S2> +// CIR: cir.copy %[[DESIRED]] {{.*}} to %[[DESIRED_TMP]] {{.*}} : !cir.ptr<!rec_S2> +// CIR: %[[EXPECTED_VOID:.+]] = cir.cast bitcast %[[EXPECTED]] : !cir.ptr<!rec_S2> -> !cir.ptr<!void> +// CIR: %[[EXPECTED_ATOMIC_TMP:.+]] = cir.cast bitcast %[[EXPECTED_ATOMIC_TMP_ADDR]] : !cir.ptr<!rec_anon_struct{{[0-9]*}}> -> !cir.ptr<!void> + +// CIR-NOT: cir.libc.memset {{.*}} bytes at %[[EXPECTED_VOID]] +// CIR: cir.libc.memset %{{.+}} bytes at %[[EXPECTED_ATOMIC_TMP]] {{.*}} to %{{.+}} : !cir.ptr<!void>, !u8i, !u64i + +// CIR: cir.libc.memcpy %{{.+}} bytes from %[[EXPECTED_VOID]] {{.*}} to %[[EXPECTED_ATOMIC_TMP]] {{.*}} : !u64i, !cir.ptr<!void> -> !cir.ptr<!void> +// CIR: %old, %success = cir.atomic.cmpxchg success(seq_cst) failure(seq_cst) syncscope(system) %{{.+}}, %{{.+}}, %{{.+}} align(16) : (!cir.ptr<!u128i>, !u128i, !u128i) -> (!u128i, !cir.bool) +// CIR: cir.if %{{.+}} { +// CIR: cir.store align(16) %old, %[[OLD_TMP]] : !u128i, !cir.ptr<!u128i> +// CIR: %[[OLD_TMP_VOID:.+]] = cir.cast bitcast %[[OLD_TMP]] : !cir.ptr<!u128i> -> !cir.ptr<!void> +// back, and it is written into "expected" itself, not into a temporary. +// CIR: %[[EXPECTED_VOID2:.+]] = cir.cast bitcast %[[EXPECTED]] : !cir.ptr<!rec_S2> -> !cir.ptr<!void> +// CIR: cir.libc.memcpy %{{.+}} bytes from %[[OLD_TMP_VOID]] {{.*}} to %[[EXPECTED_VOID2]] {{.*}} : !u64i, !cir.ptr<!void> -> !cir.ptr<!void> +// CIR: } + +// LLVM-LABEL: @atomic_cmpxchg_struct_padding +// LLVM: %[[DESIRED_TMP:.+]] = alloca %struct.S2, align 4 +// LLVM: %[[EXPECTED_TMP:.+]] = alloca { %struct.S2, [4 x i8] }, align 16 +// LLVM: %[[OLD_TMP:.+]] = alloca i128, align 16 +// LLVM: %[[PTR:.+]] = load ptr, ptr %{{.+}}, align 8 +// LLVM: %[[EXPECTED:.+]] = load ptr, ptr %{{.+}}, align 8 +// LLVM: %[[DESIRED:.+]] = load ptr, ptr %{{.+}}, align 8 +// LLVM: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %[[DESIRED_TMP]], ptr align 4 %[[DESIRED]], i64 12, i1 false) +// LLVM: call void @llvm.memset.p0.i64(ptr align 16 %[[EXPECTED_TMP]], i8 0, i64 16, i1 false) +// LLVM: call void @llvm.memcpy.p0.p0.i64(ptr align 16 %[[EXPECTED_TMP]], ptr align 4 %[[EXPECTED]], i64 12, i1 false) +// LLVM: %[[CMPXCHG:.+]] = cmpxchg ptr %[[PTR]], i128 %{{.+}}, i128 %{{.+}} seq_cst seq_cst, align 16 +// LLVM: %[[OLD:.+]] = extractvalue { i128, i1 } %[[CMPXCHG]], 0 +// LLVM: store i128 %[[OLD]], ptr %[[OLD_TMP]], align 16 +// LLVM: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %[[EXPECTED]], ptr align 16 %[[OLD_TMP]], i64 12, i1 false) + +// OGCG-LABEL: @atomic_cmpxchg_struct_padding +// OGCG: %[[DESIRED_TMP:.+]] = alloca %struct.S2, align 4 +// OGCG: %[[EXPECTED_TMP:.+]] = alloca { %struct.S2, [4 x i8] }, align 16 +// OGCG: %[[OLD_TMP:.+]] = alloca i128, align 16 +// OGCG: %[[PTR:.+]] = load ptr, ptr %{{.+}}, align 8 +// OGCG: %[[EXPECTED:.+]] = load ptr, ptr %{{.+}}, align 8 +// OGCG: %[[DESIRED:.+]] = load ptr, ptr %{{.+}}, align 8 +// OGCG: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %[[DESIRED_TMP]], ptr align 4 %[[DESIRED]], i64 12, i1 false) +// OGCG: call void @llvm.memset.p0.i64(ptr align 16 %[[EXPECTED_TMP]], i8 0, i64 16, i1 false) +// OGCG: call void @llvm.memcpy.p0.p0.i64(ptr align 16 %[[EXPECTED_TMP]], ptr align 4 %[[EXPECTED]], i64 12, i1 false) +// OGCG: %[[CMPXCHG:.+]] = cmpxchg ptr %[[PTR]], i128 %{{.+}}, i128 %{{.+}} seq_cst seq_cst, align 16 +// OGCG: %[[OLD:.+]] = extractvalue { i128, i1 } %[[CMPXCHG]], 0 +// OGCG: store i128 %[[OLD]], ptr %[[OLD_TMP]], align 16 +// OGCG: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %[[EXPECTED]], ptr align 16 %[[OLD_TMP]], i64 12, i1 false) >From 2ea88b6638fdad750afa67c6da64e05324b9840a Mon Sep 17 00:00:00 2001 From: erichkeane <[email protected]> Date: Mon, 28 Sep 2026 10:44:27 -0700 Subject: [PATCH 2/3] Fixup test that used SSA value --- clang/test/CIR/CodeGen/atomic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/test/CIR/CodeGen/atomic.c b/clang/test/CIR/CodeGen/atomic.c index bd6e49749f90f..3481e6a56a842 100644 --- a/clang/test/CIR/CodeGen/atomic.c +++ b/clang/test/CIR/CodeGen/atomic.c @@ -4259,9 +4259,9 @@ _Bool atomic_cmpxchg_struct_padding(_Atomic(struct S2) *ptr, struct S2 *expected // CIR: cir.libc.memset %{{.+}} bytes at %[[EXPECTED_ATOMIC_TMP]] {{.*}} to %{{.+}} : !cir.ptr<!void>, !u8i, !u64i // CIR: cir.libc.memcpy %{{.+}} bytes from %[[EXPECTED_VOID]] {{.*}} to %[[EXPECTED_ATOMIC_TMP]] {{.*}} : !u64i, !cir.ptr<!void> -> !cir.ptr<!void> -// CIR: %old, %success = cir.atomic.cmpxchg success(seq_cst) failure(seq_cst) syncscope(system) %{{.+}}, %{{.+}}, %{{.+}} align(16) : (!cir.ptr<!u128i>, !u128i, !u128i) -> (!u128i, !cir.bool) +// CIR: %[[OLD:.*]], %[[SUCCESS:.*]] = cir.atomic.cmpxchg success(seq_cst) failure(seq_cst) syncscope(system) %{{.+}}, %{{.+}}, %{{.+}} align(16) : (!cir.ptr<!u128i>, !u128i, !u128i) -> (!u128i, !cir.bool) // CIR: cir.if %{{.+}} { -// CIR: cir.store align(16) %old, %[[OLD_TMP]] : !u128i, !cir.ptr<!u128i> +// CIR: cir.store align(16) %[[OLD]], %[[OLD_TMP]] : !u128i, !cir.ptr<!u128i> // CIR: %[[OLD_TMP_VOID:.+]] = cir.cast bitcast %[[OLD_TMP]] : !cir.ptr<!u128i> -> !cir.ptr<!void> // back, and it is written into "expected" itself, not into a temporary. // CIR: %[[EXPECTED_VOID2:.+]] = cir.cast bitcast %[[EXPECTED]] : !cir.ptr<!rec_S2> -> !cir.ptr<!void> >From 0f15c8bb2f5b23df7b00cdad27544d32d371f9dd Mon Sep 17 00:00:00 2001 From: erichkeane <[email protected]> Date: Mon, 28 Sep 2026 10:46:05 -0700 Subject: [PATCH 3/3] Added missing-features-asserts for debug info --- clang/lib/CIR/CodeGen/CIRGenAtomic.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clang/lib/CIR/CodeGen/CIRGenAtomic.cpp b/clang/lib/CIR/CodeGen/CIRGenAtomic.cpp index fc47e732bc8a0..d974f0792d481 100644 --- a/clang/lib/CIR/CodeGen/CIRGenAtomic.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenAtomic.cpp @@ -516,10 +516,12 @@ static void emitAtomicCmpXchg(CIRGenFunction &cgf, AtomicExpr *e, bool isWeak, Address storeAddr = expectedResult.withElementType( builder, cmpxchg.getOld().getType()); builder.createStore(loc, cmpxchg.getOld(), storeAddr); + assert(!MissingFeatures::generateDebugInfo()); } else { Address oldTmp = cgf.createTempAlloca( cmpxchg.getOld().getType(), ptr.getAlignment(), loc, "old.tmp"); builder.createStore(loc, cmpxchg.getOld(), oldTmp); + assert(!MissingFeatures::generateDebugInfo()); Address oldTmpVoid = oldTmp.withElementType(builder, cgf.cgm.voidTy); Address expectedVoid = _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
