Skip to content

Conversation

Amichaxx
Copy link
Contributor

@Amichaxx Amichaxx commented Oct 9, 2025

Currently, Clang emits CAS loops for atomic fp compound assignments, instead of atomicrmw instructions. The code in CGExprScalar.cpp now checks for both integer and floating-point atomic types and emits atomicrmw fadd/fsub instructions in the LLVM IR.

cc: @efriedma-quic ?

Currently, Clang emits CAS loops for atoic fp compound assignments, instead of atomicrmw instructions. The code in CGExprScalar.cpp now checks for both integer and floating-point
atomic types and emits atomicrmw fadd/fsub instructions in the LLVM IR.
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:codegen IR generation bugs: mangling, exceptions, etc. labels Oct 9, 2025
@Amichaxx Amichaxx changed the title [LLVM] Fix clang to emit llvm-ir for fadd/fsub atomics [LLVM] [Clang] Fix clang to emit llvm-ir for fadd/fsub atomics Oct 9, 2025
@llvmbot
Copy link
Member

llvmbot commented Oct 9, 2025

@llvm/pr-subscribers-clang

@llvm/pr-subscribers-clang-codegen

Author: Amina Chabane (Amichaxx)

Changes

Currently, Clang emits CAS loops for atomic fp compound assignments, instead of atomicrmw instructions. The code in CGExprScalar.cpp now checks for both integer and floating-point atomic types and emits atomicrmw fadd/fsub instructions in the LLVM IR.

cc: @efriedma-quic ?


Full diff: https://github.com/llvm/llvm-project/pull/162679.diff

2 Files Affected:

  • (modified) clang/lib/CodeGen/CGExprScalar.cpp (+16-5)
  • (added) clang/test/CodeGen/aarch64-lsfe-atomics.c (+61)
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index f319b176513f8..4028a91ad7639 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -3847,7 +3847,16 @@ LValue ScalarExprEmitter::EmitCompoundAssignLValue(
   llvm::PHINode *atomicPHI = nullptr;
   if (const AtomicType *atomicTy = LHSTy->getAs<AtomicType>()) {
     QualType type = atomicTy->getValueType();
-    if (!type->isBooleanType() && type->isIntegerType() &&
+    const bool isFloat = type->isFloatingType();
+    const bool isInteger = type->isIntegerType();
+
+    bool isPowerOfTwo = false;
+    if (isFloat || isInteger) {
+      llvm::Type *IRTy = CGF.ConvertType(type);
+      uint64_t StoreBits = CGF.CGM.getDataLayout().getTypeStoreSizeInBits(IRTy);
+      isPowerOfTwo = llvm::isPowerOf2_64(StoreBits);
+    }
+    if (!type->isBooleanType() && (isInteger || isFloat) && isPowerOfTwo &&
         !(type->isUnsignedIntegerType() &&
           CGF.SanOpts.has(SanitizerKind::UnsignedIntegerOverflow)) &&
         CGF.getLangOpts().getSignedOverflowBehavior() !=
@@ -3862,12 +3871,14 @@ LValue ScalarExprEmitter::EmitCompoundAssignLValue(
         case BO_ShrAssign:
           break;
         case BO_AddAssign:
-          AtomicOp = llvm::AtomicRMWInst::Add;
-          Op = llvm::Instruction::Add;
+          AtomicOp =
+              isFloat ? llvm::AtomicRMWInst::FAdd : llvm::AtomicRMWInst::Add;
+          Op = isFloat ? llvm::Instruction::FAdd : llvm::Instruction::Add;
           break;
         case BO_SubAssign:
-          AtomicOp = llvm::AtomicRMWInst::Sub;
-          Op = llvm::Instruction::Sub;
+          AtomicOp =
+              isFloat ? llvm::AtomicRMWInst::FSub : llvm::AtomicRMWInst::Sub;
+          Op = isFloat ? llvm::Instruction::FSub : llvm::Instruction::Sub;
           break;
         case BO_AndAssign:
           AtomicOp = llvm::AtomicRMWInst::And;
diff --git a/clang/test/CodeGen/aarch64-lsfe-atomics.c b/clang/test/CodeGen/aarch64-lsfe-atomics.c
new file mode 100644
index 0000000000000..957e960b3fe3a
--- /dev/null
+++ b/clang/test/CodeGen/aarch64-lsfe-atomics.c
@@ -0,0 +1,61 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 6
+// RUN: %clang_cc1 %s -emit-llvm -o - -triple=aarch64-linux-gnu | FileCheck %s --check-prefix=CHECK-LLVM
+
+_Atomic(float) f;
+_Atomic(double) d;
+
+// CHECK-LLVM-LABEL: define dso_local void @test_float_add(
+// CHECK-LLVM-SAME: float noundef [[VAL:%.*]]) #[[ATTR0:[0-9]+]] {
+// CHECK-LLVM-NEXT:  [[ENTRY:.*:]]
+// CHECK-LLVM-NEXT:    [[VAL_ADDR:%.*]] = alloca float, align 4
+// CHECK-LLVM-NEXT:    store float [[VAL]], ptr [[VAL_ADDR]], align 4
+// CHECK-LLVM-NEXT:    [[TMP0:%.*]] = load float, ptr [[VAL_ADDR]], align 4
+// CHECK-LLVM-NEXT:    [[TMP1:%.*]] = atomicrmw fadd ptr @f, float [[TMP0]] seq_cst, align 4
+// CHECK-LLVM-NEXT:    [[TMP2:%.*]] = fadd float [[TMP1]], [[TMP0]]
+// CHECK-LLVM-NEXT:    ret void
+//
+void test_float_add(float val) {
+  f += val;
+}
+
+// CHECK-LLVM-LABEL: define dso_local void @test_double_add(
+// CHECK-LLVM-SAME: double noundef [[VAL:%.*]]) #[[ATTR0]] {
+// CHECK-LLVM-NEXT:  [[ENTRY:.*:]]
+// CHECK-LLVM-NEXT:    [[VAL_ADDR:%.*]] = alloca double, align 8
+// CHECK-LLVM-NEXT:    store double [[VAL]], ptr [[VAL_ADDR]], align 8
+// CHECK-LLVM-NEXT:    [[TMP0:%.*]] = load double, ptr [[VAL_ADDR]], align 8
+// CHECK-LLVM-NEXT:    [[TMP1:%.*]] = atomicrmw fadd ptr @d, double [[TMP0]] seq_cst, align 8
+// CHECK-LLVM-NEXT:    [[TMP2:%.*]] = fadd double [[TMP1]], [[TMP0]]
+// CHECK-LLVM-NEXT:    ret void
+//
+void test_double_add(double val) {
+  d += val;
+}
+
+// CHECK-LLVM-LABEL: define dso_local void @test_float_sub(
+// CHECK-LLVM-SAME: float noundef [[VAL:%.*]]) #[[ATTR0]] {
+// CHECK-LLVM-NEXT:  [[ENTRY:.*:]]
+// CHECK-LLVM-NEXT:    [[VAL_ADDR:%.*]] = alloca float, align 4
+// CHECK-LLVM-NEXT:    store float [[VAL]], ptr [[VAL_ADDR]], align 4
+// CHECK-LLVM-NEXT:    [[TMP0:%.*]] = load float, ptr [[VAL_ADDR]], align 4
+// CHECK-LLVM-NEXT:    [[TMP1:%.*]] = atomicrmw fsub ptr @f, float [[TMP0]] seq_cst, align 4
+// CHECK-LLVM-NEXT:    [[TMP2:%.*]] = fsub float [[TMP1]], [[TMP0]]
+// CHECK-LLVM-NEXT:    ret void
+//
+void test_float_sub(float val) {
+  f -= val;
+}
+
+// CHECK-LLVM-LABEL: define dso_local void @test_double_sub(
+// CHECK-LLVM-SAME: double noundef [[VAL:%.*]]) #[[ATTR0]] {
+// CHECK-LLVM-NEXT:  [[ENTRY:.*:]]
+// CHECK-LLVM-NEXT:    [[VAL_ADDR:%.*]] = alloca double, align 8
+// CHECK-LLVM-NEXT:    store double [[VAL]], ptr [[VAL_ADDR]], align 8
+// CHECK-LLVM-NEXT:    [[TMP0:%.*]] = load double, ptr [[VAL_ADDR]], align 8
+// CHECK-LLVM-NEXT:    [[TMP1:%.*]] = atomicrmw fsub ptr @d, double [[TMP0]] seq_cst, align 8
+// CHECK-LLVM-NEXT:    [[TMP2:%.*]] = fsub double [[TMP1]], [[TMP0]]
+// CHECK-LLVM-NEXT:    ret void
+//
+void test_double_sub(double val){
+    d -= val;
+}

@Amichaxx Amichaxx changed the title [LLVM] [Clang] Fix clang to emit llvm-ir for fadd/fsub atomics [Clang] Fix clang to emit llvm-ir for fadd/fsub atomics Oct 10, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:codegen IR generation bugs: mangling, exceptions, etc. clang Clang issues not falling into any other category

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants