https://github.com/antoniofrighetto created https://github.com/llvm/llvm-project/pull/209114
Per C11 6.5.16.2p1, when the left operand of operator `+=` has atomic type, the right shall have arithmetic type. Do not try the `atomicrmw` fast-path when FP conversion, arithmetic and truncation are required, as a single atomic operation may not correctly capture the combined semantics. Fixes: https://github.com/llvm/llvm-project/issues/208997. >From 462b7b4c244a7c146b668b3098f0121ddf249607 Mon Sep 17 00:00:00 2001 From: Antonio Frighetto <[email protected]> Date: Sun, 12 Jul 2026 17:10:51 +0200 Subject: [PATCH 1/2] [clang][CodeGen] Precommit test (NFC) --- clang/test/CodeGen/c11atomics.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/clang/test/CodeGen/c11atomics.c b/clang/test/CodeGen/c11atomics.c index 6a66cd8cbae89..132ffdbea55b8 100644 --- a/clang/test/CodeGen/c11atomics.c +++ b/clang/test/CodeGen/c11atomics.c @@ -152,6 +152,17 @@ void testandeq(void) l &= 42; s &= 42; } +// CHECK: testaddeq_fp +void testaddeq_fp(void) +{ + // CHECK: [[TMP0:%.*]] = atomicrmw add ptr @i, i32 0 seq_cst, align 4 + // CHECK-NEXT: add i32 [[TMP0]], 0 + i += -0.5; + + // CHECK: [[TMP1:%.*]] = atomicrmw add ptr @i, i32 poison seq_cst, align 4 + // CHECK-NEXT: add i32 [[TMP1]], poison + i += -2.5; +} // CHECK-LABEL: define{{.*}} arm_aapcscc void @testFloat(ptr void testFloat(_Atomic(float) *fp) { >From 6eb00ca6f9e0382944d959b9fa7a761e1a7e4711 Mon Sep 17 00:00:00 2001 From: Antonio Frighetto <[email protected]> Date: Mon, 13 Jul 2026 10:50:24 +0200 Subject: [PATCH 2/2] [clang][CodeGen] Do not use atomicrmw in compound assignments with FP conversions Per C11 6.5.16.2p1, when the left operand of operator `+=` has atomic type, the right shall have arithmetic type. Do not try the `atomicrmw` fast-path when FP conversion, arithmetic and truncation are required, as a single atomic operation may not correctly capture the combined semantics. --- clang/lib/CodeGen/CGExprScalar.cpp | 22 +++++++++++++++++----- clang/test/CodeGen/c11atomics.c | 16 ++++++++++++---- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 6f7b7e7d6840e..81016d3d5d308 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -4091,12 +4091,24 @@ LValue ScalarExprEmitter::EmitCompoundAssignLValue( llvm::PHINode *atomicPHI = nullptr; if (const AtomicType *atomicTy = LHSTy->getAs<AtomicType>()) { - QualType type = atomicTy->getValueType(); - if (!type->isBooleanType() && type->isIntegerType() && - !(type->isUnsignedIntegerType() && + // Type wrapped by _Atomic. + QualType AtomicValueTy = atomicTy->getValueType(); + // Type resulting from FP conversion / integer promotion of the compound + // assignment operands. + QualType ResultTy = E->getComputationResultType(); + // Do not try the atomicrmw op fast-path when the compound assignment may + // involve FP conversions, as the correct semantics would require promoting + // the loaded integer to double, performing FP arithmetics, and truncation + // back as a single atomic operation. Integer promotion is still + // semantically safe. + bool CanEmitAtomicRMW = + !AtomicValueTy->isBooleanType() && AtomicValueTy->isIntegerType() && + ResultTy->isIntegerType() && + !(AtomicValueTy->isUnsignedIntegerType() && CGF.SanOpts.has(SanitizerKind::UnsignedIntegerOverflow)) && CGF.getLangOpts().getSignedOverflowBehavior() != - LangOptions::SOB_Trapping) { + LangOptions::SOB_Trapping; + if (CanEmitAtomicRMW) { llvm::AtomicRMWInst::BinOp AtomicOp = llvm::AtomicRMWInst::BAD_BINOP; llvm::Instruction::BinaryOps Op; switch (OpInfo.Opcode) { @@ -4149,7 +4161,7 @@ LValue ScalarExprEmitter::EmitCompoundAssignLValue( llvm::BasicBlock *startBB = Builder.GetInsertBlock(); llvm::BasicBlock *opBB = CGF.createBasicBlock("atomic_op", CGF.CurFn); OpInfo.LHS = EmitLoadOfLValue(LHSLV, E->getExprLoc()); - OpInfo.LHS = CGF.EmitToMemory(OpInfo.LHS, type); + OpInfo.LHS = CGF.EmitToMemory(OpInfo.LHS, AtomicValueTy); Builder.CreateBr(opBB); Builder.SetInsertPoint(opBB); atomicPHI = Builder.CreatePHI(OpInfo.LHS->getType(), 2); diff --git a/clang/test/CodeGen/c11atomics.c b/clang/test/CodeGen/c11atomics.c index 132ffdbea55b8..9f17c3b1c0bf9 100644 --- a/clang/test/CodeGen/c11atomics.c +++ b/clang/test/CodeGen/c11atomics.c @@ -155,12 +155,20 @@ void testandeq(void) // CHECK: testaddeq_fp void testaddeq_fp(void) { - // CHECK: [[TMP0:%.*]] = atomicrmw add ptr @i, i32 0 seq_cst, align 4 - // CHECK-NEXT: add i32 [[TMP0]], 0 + // CHECK: [[CONV:%.*]] = uitofp i32 [[TMP0:%.*]] to double + // CHECK-NEXT: [[FADD:%.*]] = fadd double [[CONV]], -5.000000e-01 + // CHECK-NEXT: [[CONV1:%.*]] = fptosi double [[FADD]] to i32 + // CHECK-NEXT: store i32 [[TMP0]], ptr {{%.*}}, align 4 + // CHECK-NEXT: store i32 [[CONV1]], ptr {{%.*}}, align 4 + // CHECK-NEXT: call arm_aapcscc zeroext i1 @__atomic_compare_exchange(i32 noundef 4, ptr noundef @i, ptr noundef {{%.*}}, ptr noundef {{%.*}}, i32 noundef 5, i32 noundef 5) i += -0.5; - // CHECK: [[TMP1:%.*]] = atomicrmw add ptr @i, i32 poison seq_cst, align 4 - // CHECK-NEXT: add i32 [[TMP1]], poison + // CHECK: [[CONV2:%.*]] = uitofp i32 [[TMP1:%.*]] to double + // CHECK-NEXT: [[FADD1:%.*]] = fadd double [[CONV2]], -2.500000e+00 + // CHECK-NEXT: [[CONV3:%.*]] = fptosi double [[FADD1]] to i32 + // CHECK-NEXT: store i32 [[TMP1]], ptr {{%.*}}, align 4 + // CHECK-NEXT: store i32 [[CONV3]], ptr {{%.*}}, align 4 + // CHECK-NEXT: call arm_aapcscc zeroext i1 @__atomic_compare_exchange(i32 noundef 4, ptr noundef @i, ptr noundef {{%.*}}, ptr noundef {{%.*}}, i32 noundef 5, i32 noundef 5) i += -2.5; } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
