[Clang] Lower fp atomic compound assignments into atomicrmw (#216033)
This patch lowers fp atomic compound assignments into
atomicrmw when not in strictfp environment as
exception handling and status flags updates can be ignored. This should
improve final assembly.
Co-authored-by: Amina Chabane <amina.chabane@arm.com>
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index 096da6c..8a1dd776 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -4106,17 +4106,42 @@
// 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;
+ bool CanEmitAtomicRMW;
+ if (AtomicValueTy->isFloatingType()) {
+ llvm::Type *IRTy = CGF.ConvertType(AtomicValueTy);
+ uint64_t StoreBits = CGF.CGM.getDataLayout().getTypeStoreSizeInBits(IRTy);
+ // Floating atomicrmw operations cannot model constrained FP semantics.
+ CanEmitAtomicRMW =
+ !OpInfo.FPFeatures.isFPConstrained() &&
+ CGF.getContext().hasSameUnqualifiedType(AtomicValueTy, ResultTy) &&
+ llvm::isPowerOf2_64(StoreBits);
+ } else {
+ CanEmitAtomicRMW =
+ !AtomicValueTy->isBooleanType() && AtomicValueTy->isIntegerType() &&
+ ResultTy->isIntegerType() &&
+ !(AtomicValueTy->isUnsignedIntegerType() &&
+ CGF.SanOpts.has(SanitizerKind::UnsignedIntegerOverflow)) &&
+ CGF.getLangOpts().getSignedOverflowBehavior() !=
+ LangOptions::SOB_Trapping;
+ }
if (CanEmitAtomicRMW) {
llvm::AtomicRMWInst::BinOp AtomicOp = llvm::AtomicRMWInst::BAD_BINOP;
llvm::Instruction::BinaryOps Op;
- switch (OpInfo.Opcode) {
+ if (AtomicValueTy->isFloatingType()) {
+ switch (OpInfo.Opcode) {
+ case BO_AddAssign:
+ AtomicOp = llvm::AtomicRMWInst::FAdd;
+ Op = llvm::Instruction::FAdd;
+ break;
+ case BO_SubAssign:
+ AtomicOp = llvm::AtomicRMWInst::FSub;
+ Op = llvm::Instruction::FSub;
+ break;
+ default:
+ break;
+ }
+ } else {
+ switch (OpInfo.Opcode) {
// We don't have atomicrmw operands for *, %, /, <<, >>
case BO_MulAssign: case BO_DivAssign:
case BO_RemAssign:
@@ -4145,6 +4170,7 @@
break;
default:
llvm_unreachable("Invalid compound assignment type");
+ }
}
if (AtomicOp != llvm::AtomicRMWInst::BAD_BINOP) {
llvm::Value *Amt = CGF.EmitToMemory(
diff --git a/clang/test/CodeGen/compound-assign-atomic-fp-fallback.c b/clang/test/CodeGen/compound-assign-atomic-fp-fallback.c
new file mode 100644
index 0000000..ea186f4
--- /dev/null
+++ b/clang/test/CodeGen/compound-assign-atomic-fp-fallback.c
@@ -0,0 +1,234 @@
+// 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-prefixes=CHECK-DEFAULT
+// RUN: %clang_cc1 %s -emit-llvm -o - -triple=aarch64-linux-gnu -ffp-exception-behavior=strict | FileCheck %s --check-prefixes=CHECK-STRICT
+// RUN: %clang_cc1 %s -emit-llvm -o - -triple=x86_64-linux-gnu | FileCheck %s --check-prefixes=CHECK-X86
+
+_Atomic(float) f;
+_Atomic(long double) ld;
+
+
+// CHECK-DEFAULT-LABEL: define dso_local void @test_same_type(
+// CHECK-DEFAULT-SAME: float noundef [[VALUE:%.*]]) #[[ATTR0:[0-9]+]] {
+// CHECK-DEFAULT-NEXT: [[ENTRY:.*:]]
+// CHECK-DEFAULT-NEXT: [[VALUE_ADDR:%.*]] = alloca float, align 4
+// CHECK-DEFAULT-NEXT: store float [[VALUE]], ptr [[VALUE_ADDR]], align 4
+// CHECK-DEFAULT-NEXT: [[TMP0:%.*]] = load float, ptr [[VALUE_ADDR]], align 4
+// CHECK-DEFAULT-NEXT: [[TMP1:%.*]] = atomicrmw fadd ptr @f, float [[TMP0]] seq_cst, align 4
+// CHECK-DEFAULT-NEXT: [[TMP2:%.*]] = fadd float [[TMP1]], [[TMP0]]
+// CHECK-DEFAULT-NEXT: ret void
+//
+// CHECK-STRICT-LABEL: define dso_local void @test_same_type(
+// CHECK-STRICT-SAME: float noundef [[VALUE:%.*]]) #[[ATTR0:[0-9]+]] {
+// CHECK-STRICT-NEXT: [[ENTRY:.*]]:
+// CHECK-STRICT-NEXT: [[VALUE_ADDR:%.*]] = alloca float, align 4
+// CHECK-STRICT-NEXT: store float [[VALUE]], ptr [[VALUE_ADDR]], align 4
+// CHECK-STRICT-NEXT: [[TMP0:%.*]] = load float, ptr [[VALUE_ADDR]], align 4
+// CHECK-STRICT-NEXT: [[ATOMIC_LOAD:%.*]] = load atomic float, ptr @f seq_cst, align 4
+// CHECK-STRICT-NEXT: br label %[[ATOMIC_OP:.*]]
+// CHECK-STRICT: [[ATOMIC_OP]]:
+// CHECK-STRICT-NEXT: [[TMP1:%.*]] = phi float [ [[ATOMIC_LOAD]], %[[ENTRY]] ], [ [[TMP7:%.*]], %[[ATOMIC_OP]] ]
+// CHECK-STRICT-NEXT: [[ADD:%.*]] = call float @llvm.experimental.constrained.fadd.f32(float [[TMP1]], float [[TMP0]], metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR2:[0-9]+]]
+// CHECK-STRICT-NEXT: [[TMP2:%.*]] = bitcast float [[TMP1]] to i32
+// CHECK-STRICT-NEXT: [[TMP3:%.*]] = bitcast float [[ADD]] to i32
+// CHECK-STRICT-NEXT: [[TMP4:%.*]] = cmpxchg ptr @f, i32 [[TMP2]], i32 [[TMP3]] seq_cst seq_cst, align 4
+// CHECK-STRICT-NEXT: [[TMP5:%.*]] = extractvalue { i32, i1 } [[TMP4]], 0
+// CHECK-STRICT-NEXT: [[TMP6:%.*]] = extractvalue { i32, i1 } [[TMP4]], 1
+// CHECK-STRICT-NEXT: [[TMP7]] = bitcast i32 [[TMP5]] to float
+// CHECK-STRICT-NEXT: br i1 [[TMP6]], label %[[ATOMIC_CONT:.*]], label %[[ATOMIC_OP]]
+// CHECK-STRICT: [[ATOMIC_CONT]]:
+// CHECK-STRICT-NEXT: ret void
+//
+// CHECK-X86-LABEL: define dso_local void @test_same_type(
+// CHECK-X86-SAME: float noundef [[VALUE:%.*]]) #[[ATTR0:[0-9]+]] {
+// CHECK-X86-NEXT: [[ENTRY:.*:]]
+// CHECK-X86-NEXT: [[VALUE_ADDR:%.*]] = alloca float, align 4
+// CHECK-X86-NEXT: store float [[VALUE]], ptr [[VALUE_ADDR]], align 4
+// CHECK-X86-NEXT: [[TMP0:%.*]] = load float, ptr [[VALUE_ADDR]], align 4
+// CHECK-X86-NEXT: [[TMP1:%.*]] = atomicrmw fadd ptr @f, float [[TMP0]] seq_cst, align 4
+// CHECK-X86-NEXT: [[TMP2:%.*]] = fadd float [[TMP1]], [[TMP0]]
+// CHECK-X86-NEXT: ret void
+//
+void test_same_type(float value) {
+ f += value;
+}
+
+// CHECK-DEFAULT-LABEL: define dso_local void @test_wider_rhs(
+// CHECK-DEFAULT-SAME: double noundef [[VALUE:%.*]]) #[[ATTR0]] {
+// CHECK-DEFAULT-NEXT: [[ENTRY:.*]]:
+// CHECK-DEFAULT-NEXT: [[VALUE_ADDR:%.*]] = alloca double, align 8
+// CHECK-DEFAULT-NEXT: store double [[VALUE]], ptr [[VALUE_ADDR]], align 8
+// CHECK-DEFAULT-NEXT: [[TMP0:%.*]] = load double, ptr [[VALUE_ADDR]], align 8
+// CHECK-DEFAULT-NEXT: [[ATOMIC_LOAD:%.*]] = load atomic float, ptr @f seq_cst, align 4
+// CHECK-DEFAULT-NEXT: br label %[[ATOMIC_OP:.*]]
+// CHECK-DEFAULT: [[ATOMIC_OP]]:
+// CHECK-DEFAULT-NEXT: [[TMP1:%.*]] = phi float [ [[ATOMIC_LOAD]], %[[ENTRY]] ], [ [[TMP7:%.*]], %[[ATOMIC_OP]] ]
+// CHECK-DEFAULT-NEXT: [[CONV:%.*]] = fpext float [[TMP1]] to double
+// CHECK-DEFAULT-NEXT: [[ADD:%.*]] = fadd double [[CONV]], [[TMP0]]
+// CHECK-DEFAULT-NEXT: [[CONV1:%.*]] = fptrunc double [[ADD]] to float
+// CHECK-DEFAULT-NEXT: [[TMP2:%.*]] = bitcast float [[TMP1]] to i32
+// CHECK-DEFAULT-NEXT: [[TMP3:%.*]] = bitcast float [[CONV1]] to i32
+// CHECK-DEFAULT-NEXT: [[TMP4:%.*]] = cmpxchg ptr @f, i32 [[TMP2]], i32 [[TMP3]] seq_cst seq_cst, align 4
+// CHECK-DEFAULT-NEXT: [[TMP5:%.*]] = extractvalue { i32, i1 } [[TMP4]], 0
+// CHECK-DEFAULT-NEXT: [[TMP6:%.*]] = extractvalue { i32, i1 } [[TMP4]], 1
+// CHECK-DEFAULT-NEXT: [[TMP7]] = bitcast i32 [[TMP5]] to float
+// CHECK-DEFAULT-NEXT: br i1 [[TMP6]], label %[[ATOMIC_CONT:.*]], label %[[ATOMIC_OP]]
+// CHECK-DEFAULT: [[ATOMIC_CONT]]:
+// CHECK-DEFAULT-NEXT: ret void
+//
+// CHECK-STRICT-LABEL: define dso_local void @test_wider_rhs(
+// CHECK-STRICT-SAME: double noundef [[VALUE:%.*]]) #[[ATTR0]] {
+// CHECK-STRICT-NEXT: [[ENTRY:.*]]:
+// CHECK-STRICT-NEXT: [[VALUE_ADDR:%.*]] = alloca double, align 8
+// CHECK-STRICT-NEXT: store double [[VALUE]], ptr [[VALUE_ADDR]], align 8
+// CHECK-STRICT-NEXT: [[TMP0:%.*]] = load double, ptr [[VALUE_ADDR]], align 8
+// CHECK-STRICT-NEXT: [[ATOMIC_LOAD:%.*]] = load atomic float, ptr @f seq_cst, align 4
+// CHECK-STRICT-NEXT: br label %[[ATOMIC_OP:.*]]
+// CHECK-STRICT: [[ATOMIC_OP]]:
+// CHECK-STRICT-NEXT: [[TMP1:%.*]] = phi float [ [[ATOMIC_LOAD]], %[[ENTRY]] ], [ [[TMP7:%.*]], %[[ATOMIC_OP]] ]
+// CHECK-STRICT-NEXT: [[CONV:%.*]] = call double @llvm.experimental.constrained.fpext.f64.f32(float [[TMP1]], metadata !"fpexcept.strict") #[[ATTR2]]
+// CHECK-STRICT-NEXT: [[ADD:%.*]] = call double @llvm.experimental.constrained.fadd.f64(double [[CONV]], double [[TMP0]], metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR2]]
+// CHECK-STRICT-NEXT: [[CONV1:%.*]] = call float @llvm.experimental.constrained.fptrunc.f32.f64(double [[ADD]], metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR2]]
+// CHECK-STRICT-NEXT: [[TMP2:%.*]] = bitcast float [[TMP1]] to i32
+// CHECK-STRICT-NEXT: [[TMP3:%.*]] = bitcast float [[CONV1]] to i32
+// CHECK-STRICT-NEXT: [[TMP4:%.*]] = cmpxchg ptr @f, i32 [[TMP2]], i32 [[TMP3]] seq_cst seq_cst, align 4
+// CHECK-STRICT-NEXT: [[TMP5:%.*]] = extractvalue { i32, i1 } [[TMP4]], 0
+// CHECK-STRICT-NEXT: [[TMP6:%.*]] = extractvalue { i32, i1 } [[TMP4]], 1
+// CHECK-STRICT-NEXT: [[TMP7]] = bitcast i32 [[TMP5]] to float
+// CHECK-STRICT-NEXT: br i1 [[TMP6]], label %[[ATOMIC_CONT:.*]], label %[[ATOMIC_OP]]
+// CHECK-STRICT: [[ATOMIC_CONT]]:
+// CHECK-STRICT-NEXT: ret void
+//
+// CHECK-X86-LABEL: define dso_local void @test_wider_rhs(
+// CHECK-X86-SAME: double noundef [[VALUE:%.*]]) #[[ATTR0]] {
+// CHECK-X86-NEXT: [[ENTRY:.*]]:
+// CHECK-X86-NEXT: [[VALUE_ADDR:%.*]] = alloca double, align 8
+// CHECK-X86-NEXT: store double [[VALUE]], ptr [[VALUE_ADDR]], align 8
+// CHECK-X86-NEXT: [[TMP0:%.*]] = load double, ptr [[VALUE_ADDR]], align 8
+// CHECK-X86-NEXT: [[ATOMIC_LOAD:%.*]] = load atomic float, ptr @f seq_cst, align 4
+// CHECK-X86-NEXT: br label %[[ATOMIC_OP:.*]]
+// CHECK-X86: [[ATOMIC_OP]]:
+// CHECK-X86-NEXT: [[TMP1:%.*]] = phi float [ [[ATOMIC_LOAD]], %[[ENTRY]] ], [ [[TMP7:%.*]], %[[ATOMIC_OP]] ]
+// CHECK-X86-NEXT: [[CONV:%.*]] = fpext float [[TMP1]] to double
+// CHECK-X86-NEXT: [[ADD:%.*]] = fadd double [[CONV]], [[TMP0]]
+// CHECK-X86-NEXT: [[CONV1:%.*]] = fptrunc double [[ADD]] to float
+// CHECK-X86-NEXT: [[TMP2:%.*]] = bitcast float [[TMP1]] to i32
+// CHECK-X86-NEXT: [[TMP3:%.*]] = bitcast float [[CONV1]] to i32
+// CHECK-X86-NEXT: [[TMP4:%.*]] = cmpxchg ptr @f, i32 [[TMP2]], i32 [[TMP3]] seq_cst seq_cst, align 4
+// CHECK-X86-NEXT: [[TMP5:%.*]] = extractvalue { i32, i1 } [[TMP4]], 0
+// CHECK-X86-NEXT: [[TMP6:%.*]] = extractvalue { i32, i1 } [[TMP4]], 1
+// CHECK-X86-NEXT: [[TMP7]] = bitcast i32 [[TMP5]] to float
+// CHECK-X86-NEXT: br i1 [[TMP6]], label %[[ATOMIC_CONT:.*]], label %[[ATOMIC_OP]]
+// CHECK-X86: [[ATOMIC_CONT]]:
+// CHECK-X86-NEXT: ret void
+//
+void test_wider_rhs(double value) {
+ f += value;
+}
+
+// CHECK-DEFAULT-LABEL: define dso_local void @test_integer_rhs(
+// CHECK-DEFAULT-SAME: i32 noundef [[VALUE:%.*]]) #[[ATTR0]] {
+// CHECK-DEFAULT-NEXT: [[ENTRY:.*:]]
+// CHECK-DEFAULT-NEXT: [[VALUE_ADDR:%.*]] = alloca i32, align 4
+// CHECK-DEFAULT-NEXT: store i32 [[VALUE]], ptr [[VALUE_ADDR]], align 4
+// CHECK-DEFAULT-NEXT: [[TMP0:%.*]] = load i32, ptr [[VALUE_ADDR]], align 4
+// CHECK-DEFAULT-NEXT: [[CONV:%.*]] = sitofp i32 [[TMP0]] to float
+// CHECK-DEFAULT-NEXT: [[TMP1:%.*]] = atomicrmw fadd ptr @f, float [[CONV]] seq_cst, align 4
+// CHECK-DEFAULT-NEXT: [[TMP2:%.*]] = fadd float [[TMP1]], [[CONV]]
+// CHECK-DEFAULT-NEXT: ret void
+//
+// CHECK-STRICT-LABEL: define dso_local void @test_integer_rhs(
+// CHECK-STRICT-SAME: i32 noundef [[VALUE:%.*]]) #[[ATTR0]] {
+// CHECK-STRICT-NEXT: [[ENTRY:.*]]:
+// CHECK-STRICT-NEXT: [[VALUE_ADDR:%.*]] = alloca i32, align 4
+// CHECK-STRICT-NEXT: store i32 [[VALUE]], ptr [[VALUE_ADDR]], align 4
+// CHECK-STRICT-NEXT: [[TMP0:%.*]] = load i32, ptr [[VALUE_ADDR]], align 4
+// CHECK-STRICT-NEXT: [[CONV:%.*]] = call float @llvm.experimental.constrained.sitofp.f32.i32(i32 [[TMP0]], metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR2]]
+// CHECK-STRICT-NEXT: [[ATOMIC_LOAD:%.*]] = load atomic float, ptr @f seq_cst, align 4
+// CHECK-STRICT-NEXT: br label %[[ATOMIC_OP:.*]]
+// CHECK-STRICT: [[ATOMIC_OP]]:
+// CHECK-STRICT-NEXT: [[TMP1:%.*]] = phi float [ [[ATOMIC_LOAD]], %[[ENTRY]] ], [ [[TMP7:%.*]], %[[ATOMIC_OP]] ]
+// CHECK-STRICT-NEXT: [[ADD:%.*]] = call float @llvm.experimental.constrained.fadd.f32(float [[TMP1]], float [[CONV]], metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR2]]
+// CHECK-STRICT-NEXT: [[TMP2:%.*]] = bitcast float [[TMP1]] to i32
+// CHECK-STRICT-NEXT: [[TMP3:%.*]] = bitcast float [[ADD]] to i32
+// CHECK-STRICT-NEXT: [[TMP4:%.*]] = cmpxchg ptr @f, i32 [[TMP2]], i32 [[TMP3]] seq_cst seq_cst, align 4
+// CHECK-STRICT-NEXT: [[TMP5:%.*]] = extractvalue { i32, i1 } [[TMP4]], 0
+// CHECK-STRICT-NEXT: [[TMP6:%.*]] = extractvalue { i32, i1 } [[TMP4]], 1
+// CHECK-STRICT-NEXT: [[TMP7]] = bitcast i32 [[TMP5]] to float
+// CHECK-STRICT-NEXT: br i1 [[TMP6]], label %[[ATOMIC_CONT:.*]], label %[[ATOMIC_OP]]
+// CHECK-STRICT: [[ATOMIC_CONT]]:
+// CHECK-STRICT-NEXT: ret void
+//
+// CHECK-X86-LABEL: define dso_local void @test_integer_rhs(
+// CHECK-X86-SAME: i32 noundef [[VALUE:%.*]]) #[[ATTR0]] {
+// CHECK-X86-NEXT: [[ENTRY:.*:]]
+// CHECK-X86-NEXT: [[VALUE_ADDR:%.*]] = alloca i32, align 4
+// CHECK-X86-NEXT: store i32 [[VALUE]], ptr [[VALUE_ADDR]], align 4
+// CHECK-X86-NEXT: [[TMP0:%.*]] = load i32, ptr [[VALUE_ADDR]], align 4
+// CHECK-X86-NEXT: [[CONV:%.*]] = sitofp i32 [[TMP0]] to float
+// CHECK-X86-NEXT: [[TMP1:%.*]] = atomicrmw fadd ptr @f, float [[CONV]] seq_cst, align 4
+// CHECK-X86-NEXT: [[TMP2:%.*]] = fadd float [[TMP1]], [[CONV]]
+// CHECK-X86-NEXT: ret void
+//
+void test_integer_rhs(int value) {
+ f += value;
+}
+
+// CHECK-DEFAULT-LABEL: define dso_local void @test_non_power_of_two_type(
+// CHECK-DEFAULT-SAME: fp128 noundef [[VALUE:%.*]]) #[[ATTR0]] {
+// CHECK-DEFAULT-NEXT: [[ENTRY:.*:]]
+// CHECK-DEFAULT-NEXT: [[VALUE_ADDR:%.*]] = alloca fp128, align 16
+// CHECK-DEFAULT-NEXT: store fp128 [[VALUE]], ptr [[VALUE_ADDR]], align 16
+// CHECK-DEFAULT-NEXT: [[TMP0:%.*]] = load fp128, ptr [[VALUE_ADDR]], align 16
+// CHECK-DEFAULT-NEXT: [[TMP1:%.*]] = atomicrmw fadd ptr @ld, fp128 [[TMP0]] seq_cst, align 16
+// CHECK-DEFAULT-NEXT: [[TMP2:%.*]] = fadd fp128 [[TMP1]], [[TMP0]]
+// CHECK-DEFAULT-NEXT: ret void
+//
+// CHECK-STRICT-LABEL: define dso_local void @test_non_power_of_two_type(
+// CHECK-STRICT-SAME: fp128 noundef [[VALUE:%.*]]) #[[ATTR0]] {
+// CHECK-STRICT-NEXT: [[ENTRY:.*]]:
+// CHECK-STRICT-NEXT: [[VALUE_ADDR:%.*]] = alloca fp128, align 16
+// CHECK-STRICT-NEXT: store fp128 [[VALUE]], ptr [[VALUE_ADDR]], align 16
+// CHECK-STRICT-NEXT: [[TMP0:%.*]] = load fp128, ptr [[VALUE_ADDR]], align 16
+// CHECK-STRICT-NEXT: [[ATOMIC_LOAD:%.*]] = load atomic fp128, ptr @ld seq_cst, align 16
+// CHECK-STRICT-NEXT: br label %[[ATOMIC_OP:.*]]
+// CHECK-STRICT: [[ATOMIC_OP]]:
+// CHECK-STRICT-NEXT: [[TMP1:%.*]] = phi fp128 [ [[ATOMIC_LOAD]], %[[ENTRY]] ], [ [[TMP7:%.*]], %[[ATOMIC_OP]] ]
+// CHECK-STRICT-NEXT: [[ADD:%.*]] = call fp128 @llvm.experimental.constrained.fadd.f128(fp128 [[TMP1]], fp128 [[TMP0]], metadata !"round.tonearest", metadata !"fpexcept.strict") #[[ATTR2]]
+// CHECK-STRICT-NEXT: [[TMP2:%.*]] = bitcast fp128 [[TMP1]] to i128
+// CHECK-STRICT-NEXT: [[TMP3:%.*]] = bitcast fp128 [[ADD]] to i128
+// CHECK-STRICT-NEXT: [[TMP4:%.*]] = cmpxchg ptr @ld, i128 [[TMP2]], i128 [[TMP3]] seq_cst seq_cst, align 16
+// CHECK-STRICT-NEXT: [[TMP5:%.*]] = extractvalue { i128, i1 } [[TMP4]], 0
+// CHECK-STRICT-NEXT: [[TMP6:%.*]] = extractvalue { i128, i1 } [[TMP4]], 1
+// CHECK-STRICT-NEXT: [[TMP7]] = bitcast i128 [[TMP5]] to fp128
+// CHECK-STRICT-NEXT: br i1 [[TMP6]], label %[[ATOMIC_CONT:.*]], label %[[ATOMIC_OP]]
+// CHECK-STRICT: [[ATOMIC_CONT]]:
+// CHECK-STRICT-NEXT: ret void
+//
+// CHECK-X86-LABEL: define dso_local void @test_non_power_of_two_type(
+// CHECK-X86-SAME: x86_fp80 noundef [[VALUE:%.*]]) #[[ATTR0]] {
+// CHECK-X86-NEXT: [[ENTRY:.*]]:
+// CHECK-X86-NEXT: [[VALUE_ADDR:%.*]] = alloca x86_fp80, align 16
+// CHECK-X86-NEXT: [[ATOMIC_TEMP:%.*]] = alloca x86_fp80, align 16
+// CHECK-X86-NEXT: [[ATOMIC_TEMP1:%.*]] = alloca x86_fp80, align 16
+// CHECK-X86-NEXT: [[ATOMIC_TEMP2:%.*]] = alloca x86_fp80, align 16
+// CHECK-X86-NEXT: store x86_fp80 [[VALUE]], ptr [[VALUE_ADDR]], align 16
+// CHECK-X86-NEXT: [[TMP0:%.*]] = load x86_fp80, ptr [[VALUE_ADDR]], align 16
+// CHECK-X86-NEXT: call void @__atomic_load(i64 noundef 16, ptr noundef @ld, ptr noundef [[ATOMIC_TEMP]], i32 noundef 5)
+// CHECK-X86-NEXT: [[TMP1:%.*]] = load x86_fp80, ptr [[ATOMIC_TEMP]], align 16
+// CHECK-X86-NEXT: br label %[[ATOMIC_OP:.*]]
+// CHECK-X86: [[ATOMIC_OP]]:
+// CHECK-X86-NEXT: [[TMP2:%.*]] = phi x86_fp80 [ [[TMP1]], %[[ENTRY]] ], [ [[TMP3:%.*]], %[[ATOMIC_OP]] ]
+// CHECK-X86-NEXT: [[ADD:%.*]] = fadd x86_fp80 [[TMP2]], [[TMP0]]
+// CHECK-X86-NEXT: call void @llvm.memset.p0.i64(ptr align 16 [[ATOMIC_TEMP1]], i8 0, i64 16, i1 false)
+// CHECK-X86-NEXT: store x86_fp80 [[TMP2]], ptr [[ATOMIC_TEMP1]], align 16
+// CHECK-X86-NEXT: call void @llvm.memset.p0.i64(ptr align 16 [[ATOMIC_TEMP2]], i8 0, i64 16, i1 false)
+// CHECK-X86-NEXT: store x86_fp80 [[ADD]], ptr [[ATOMIC_TEMP2]], align 16
+// CHECK-X86-NEXT: [[CALL:%.*]] = call zeroext i1 @__atomic_compare_exchange(i64 noundef 16, ptr noundef @ld, ptr noundef [[ATOMIC_TEMP1]], ptr noundef [[ATOMIC_TEMP2]], i32 noundef 5, i32 noundef 5)
+// CHECK-X86-NEXT: [[TMP3]] = load x86_fp80, ptr [[ATOMIC_TEMP1]], align 16
+// CHECK-X86-NEXT: br i1 [[CALL]], label %[[ATOMIC_CONT:.*]], label %[[ATOMIC_OP]]
+// CHECK-X86: [[ATOMIC_CONT]]:
+// CHECK-X86-NEXT: ret void
+//
+void test_non_power_of_two_type(long double value) {
+ ld += value;
+}
\ No newline at end of file
diff --git a/clang/test/CodeGen/compound-assign-atomic-fp.c b/clang/test/CodeGen/compound-assign-atomic-fp.c
new file mode 100644
index 0000000..df7422f
--- /dev/null
+++ b/clang/test/CodeGen/compound-assign-atomic-fp.c
@@ -0,0 +1,269 @@
+// 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-C
+// RUN: %clang_cc1 -x c++ -std=c++11 %s -emit-llvm -o - -triple=aarch64-linux-gnu | FileCheck %s --check-prefix=CHECK-CPP
+
+_Atomic(float) f;
+_Atomic(double) d;
+_Atomic(__bf16) bf;
+_Atomic(_Float16) h;
+
+// CHECK-C-LABEL: define dso_local void @test_float_add(
+// CHECK-C-SAME: float noundef [[VAL:%.*]]) #[[ATTR0:[0-9]+]] {
+// CHECK-C-NEXT: [[ENTRY:.*:]]
+// CHECK-C-NEXT: [[VAL_ADDR:%.*]] = alloca float, align 4
+// CHECK-C-NEXT: store float [[VAL]], ptr [[VAL_ADDR]], align 4
+// CHECK-C-NEXT: [[TMP0:%.*]] = load float, ptr [[VAL_ADDR]], align 4
+// CHECK-C-NEXT: [[TMP1:%.*]] = atomicrmw fadd ptr @f, float [[TMP0]] seq_cst, align 4
+// CHECK-C-NEXT: [[TMP2:%.*]] = fadd float [[TMP1]], [[TMP0]]
+// CHECK-C-NEXT: ret void
+//
+// CHECK-CPP-LABEL: define dso_local void @_Z14test_float_addf(
+// CHECK-CPP-SAME: float noundef [[VAL:%.*]]) #[[ATTR0:[0-9]+]] {
+// CHECK-CPP-NEXT: [[ENTRY:.*:]]
+// CHECK-CPP-NEXT: [[VAL_ADDR:%.*]] = alloca float, align 4
+// CHECK-CPP-NEXT: store float [[VAL]], ptr [[VAL_ADDR]], align 4
+// CHECK-CPP-NEXT: [[TMP0:%.*]] = load float, ptr [[VAL_ADDR]], align 4
+// CHECK-CPP-NEXT: [[TMP1:%.*]] = atomicrmw fadd ptr @f, float [[TMP0]] seq_cst, align 4
+// CHECK-CPP-NEXT: [[TMP2:%.*]] = fadd float [[TMP1]], [[TMP0]]
+// CHECK-CPP-NEXT: ret void
+//
+void test_float_add(float val) {
+ f += val;
+}
+
+// CHECK-C-LABEL: define dso_local void @test_float_compound_add(
+// CHECK-C-SAME: float [[VAL:%.*]]) #[[ATTR0]] {
+// CHECK-C-NEXT: [[ENTRY:.*:]]
+// CHECK-C-NEXT: [[VAL_ADDR:%.*]] = alloca float, align 4
+// CHECK-C-NEXT: store float [[VAL]], ptr [[VAL_ADDR]], align 4
+// CHECK-C-NEXT: [[ATOMIC_LOAD:%.*]] = load atomic float, ptr [[VAL_ADDR]] seq_cst, align 4
+// CHECK-C-NEXT: [[TMP0:%.*]] = atomicrmw fadd ptr @f, float [[ATOMIC_LOAD]] seq_cst, align 4
+// CHECK-C-NEXT: [[TMP1:%.*]] = fadd float [[TMP0]], [[ATOMIC_LOAD]]
+// CHECK-C-NEXT: ret void
+//
+// CHECK-CPP-LABEL: define dso_local void @_Z23test_float_compound_addU7_Atomicf(
+// CHECK-CPP-SAME: float [[VAL:%.*]]) #[[ATTR0]] {
+// CHECK-CPP-NEXT: [[ENTRY:.*:]]
+// CHECK-CPP-NEXT: [[VAL_ADDR:%.*]] = alloca float, align 4
+// CHECK-CPP-NEXT: store float [[VAL]], ptr [[VAL_ADDR]], align 4
+// CHECK-CPP-NEXT: [[ATOMIC_LOAD:%.*]] = load atomic float, ptr [[VAL_ADDR]] seq_cst, align 4
+// CHECK-CPP-NEXT: [[TMP0:%.*]] = atomicrmw fadd ptr @f, float [[ATOMIC_LOAD]] seq_cst, align 4
+// CHECK-CPP-NEXT: [[TMP1:%.*]] = fadd float [[TMP0]], [[ATOMIC_LOAD]]
+// CHECK-CPP-NEXT: ret void
+//
+void test_float_compound_add(_Atomic(float) val){
+ f += val;
+}
+
+// CHECK-C-LABEL: define dso_local void @test_float_read_add(
+// CHECK-C-SAME: float [[VAL:%.*]]) #[[ATTR0]] {
+// CHECK-C-NEXT: [[ENTRY:.*:]]
+// CHECK-C-NEXT: [[VAL_ADDR:%.*]] = alloca float, align 4
+// CHECK-C-NEXT: store float [[VAL]], ptr [[VAL_ADDR]], align 4
+// CHECK-C-NEXT: [[ATOMIC_LOAD:%.*]] = load atomic float, ptr @f seq_cst, align 4
+// CHECK-C-NEXT: [[ATOMIC_LOAD1:%.*]] = load atomic float, ptr [[VAL_ADDR]] seq_cst, align 4
+// CHECK-C-NEXT: [[ADD:%.*]] = fadd float [[ATOMIC_LOAD]], [[ATOMIC_LOAD1]]
+// CHECK-C-NEXT: store atomic float [[ADD]], ptr @f seq_cst, align 4
+// CHECK-C-NEXT: ret void
+//
+// CHECK-CPP-LABEL: define dso_local void @_Z19test_float_read_addU7_Atomicf(
+// CHECK-CPP-SAME: float [[VAL:%.*]]) #[[ATTR0]] {
+// CHECK-CPP-NEXT: [[ENTRY:.*:]]
+// CHECK-CPP-NEXT: [[VAL_ADDR:%.*]] = alloca float, align 4
+// CHECK-CPP-NEXT: store float [[VAL]], ptr [[VAL_ADDR]], align 4
+// CHECK-CPP-NEXT: [[ATOMIC_LOAD:%.*]] = load atomic float, ptr @f seq_cst, align 4
+// CHECK-CPP-NEXT: [[ATOMIC_LOAD1:%.*]] = load atomic float, ptr [[VAL_ADDR]] seq_cst, align 4
+// CHECK-CPP-NEXT: [[ADD:%.*]] = fadd float [[ATOMIC_LOAD]], [[ATOMIC_LOAD1]]
+// CHECK-CPP-NEXT: store atomic float [[ADD]], ptr @f seq_cst, align 4
+// CHECK-CPP-NEXT: ret void
+//
+void test_float_read_add(_Atomic(float) val){
+ f = f + val;
+}
+
+// CHECK-C-LABEL: define dso_local void @test_double_add(
+// CHECK-C-SAME: double noundef [[VAL:%.*]]) #[[ATTR0]] {
+// CHECK-C-NEXT: [[ENTRY:.*:]]
+// CHECK-C-NEXT: [[VAL_ADDR:%.*]] = alloca double, align 8
+// CHECK-C-NEXT: store double [[VAL]], ptr [[VAL_ADDR]], align 8
+// CHECK-C-NEXT: [[TMP0:%.*]] = load double, ptr [[VAL_ADDR]], align 8
+// CHECK-C-NEXT: [[TMP1:%.*]] = atomicrmw fadd ptr @d, double [[TMP0]] seq_cst, align 8
+// CHECK-C-NEXT: [[TMP2:%.*]] = fadd double [[TMP1]], [[TMP0]]
+// CHECK-C-NEXT: ret void
+//
+// CHECK-CPP-LABEL: define dso_local void @_Z15test_double_addd(
+// CHECK-CPP-SAME: double noundef [[VAL:%.*]]) #[[ATTR0]] {
+// CHECK-CPP-NEXT: [[ENTRY:.*:]]
+// CHECK-CPP-NEXT: [[VAL_ADDR:%.*]] = alloca double, align 8
+// CHECK-CPP-NEXT: store double [[VAL]], ptr [[VAL_ADDR]], align 8
+// CHECK-CPP-NEXT: [[TMP0:%.*]] = load double, ptr [[VAL_ADDR]], align 8
+// CHECK-CPP-NEXT: [[TMP1:%.*]] = atomicrmw fadd ptr @d, double [[TMP0]] seq_cst, align 8
+// CHECK-CPP-NEXT: [[TMP2:%.*]] = fadd double [[TMP1]], [[TMP0]]
+// CHECK-CPP-NEXT: ret void
+//
+void test_double_add(double val) {
+ d += val;
+}
+
+// CHECK-LLVM-LABEL: define dso_local void @test_bf16_add(
+// CHECK-LLVM-SAME: bfloat noundef [[VAL:%.*]]) #[[ATTR0]] {
+// CHECK-LLVM-NEXT: [[ENTRY:.*:]]
+// CHECK-LLVM-NEXT: [[VAL_ADDR:%.*]] = alloca bfloat, align 2
+// CHECK-LLVM-NEXT: store bfloat [[VAL]], ptr [[VAL_ADDR]], align 2
+// CHECK-LLVM-NEXT: [[TMP0:%.*]] = load bfloat, ptr [[VAL_ADDR]], align 2
+// CHECK-LLVM-NEXT: [[EXT:%.*]] = fpext bfloat [[TMP0]] to float
+// CHECK-LLVM-NEXT: [[CONV:%.*]] = fptrunc float [[EXT]] to bfloat
+// CHECK-LLVM-NEXT: [[TMP1:%.*]] = atomicrmw fadd ptr @bf, bfloat [[CONV]] seq_cst, align 2
+// CHECK-LLVM-NEXT: [[TMP2:%.*]] = fadd bfloat [[TMP1]], [[CONV]]
+// CHECK-LLVM-NEXT: ret void
+// CHECK-C-LABEL: define dso_local void @test_bf16_add(
+// CHECK-C-SAME: bfloat noundef [[VAL:%.*]]) #[[ATTR0]] {
+// CHECK-C-NEXT: [[ENTRY:.*:]]
+// CHECK-C-NEXT: [[VAL_ADDR:%.*]] = alloca bfloat, align 2
+// CHECK-C-NEXT: store bfloat [[VAL]], ptr [[VAL_ADDR]], align 2
+// CHECK-C-NEXT: [[TMP0:%.*]] = load bfloat, ptr [[VAL_ADDR]], align 2
+// CHECK-C-NEXT: [[EXT:%.*]] = fpext bfloat [[TMP0]] to float
+// CHECK-C-NEXT: [[CONV:%.*]] = fptrunc float [[EXT]] to bfloat
+// CHECK-C-NEXT: [[TMP1:%.*]] = atomicrmw fadd ptr @bf, bfloat [[CONV]] seq_cst, align 2
+// CHECK-C-NEXT: [[TMP2:%.*]] = fadd bfloat [[TMP1]], [[CONV]]
+// CHECK-C-NEXT: ret void
+//
+// CHECK-CPP-LABEL: define dso_local void @_Z13test_bf16_addu6__bf16(
+// CHECK-CPP-SAME: bfloat noundef [[VAL:%.*]]) #[[ATTR0]] {
+// CHECK-CPP-NEXT: [[ENTRY:.*:]]
+// CHECK-CPP-NEXT: [[VAL_ADDR:%.*]] = alloca bfloat, align 2
+// CHECK-CPP-NEXT: store bfloat [[VAL]], ptr [[VAL_ADDR]], align 2
+// CHECK-CPP-NEXT: [[TMP0:%.*]] = load bfloat, ptr [[VAL_ADDR]], align 2
+// CHECK-CPP-NEXT: [[EXT:%.*]] = fpext bfloat [[TMP0]] to float
+// CHECK-CPP-NEXT: [[CONV:%.*]] = fptrunc float [[EXT]] to bfloat
+// CHECK-CPP-NEXT: [[TMP1:%.*]] = atomicrmw fadd ptr @bf, bfloat [[CONV]] seq_cst, align 2
+// CHECK-CPP-NEXT: [[TMP2:%.*]] = fadd bfloat [[TMP1]], [[CONV]]
+// CHECK-CPP-NEXT: ret void
+//
+void test_bf16_add(__bf16 val){
+ bf += val;
+}
+
+// CHECK-C-LABEL: define dso_local void @test_f16_add(
+// CHECK-C-SAME: half noundef [[VAL:%.*]]) #[[ATTR0]] {
+// CHECK-C-NEXT: [[ENTRY:.*:]]
+// CHECK-C-NEXT: [[VAL_ADDR:%.*]] = alloca half, align 2
+// CHECK-C-NEXT: store half [[VAL]], ptr [[VAL_ADDR]], align 2
+// CHECK-C-NEXT: [[TMP0:%.*]] = load half, ptr [[VAL_ADDR]], align 2
+// CHECK-C-NEXT: [[TMP1:%.*]] = atomicrmw fadd ptr @h, half [[TMP0]] seq_cst, align 2
+// CHECK-C-NEXT: [[TMP2:%.*]] = fadd half [[TMP1]], [[TMP0]]
+// CHECK-C-NEXT: ret void
+//
+// CHECK-CPP-LABEL: define dso_local void @_Z12test_f16_addDF16_(
+// CHECK-CPP-SAME: half noundef [[VAL:%.*]]) #[[ATTR0]] {
+// CHECK-CPP-NEXT: [[ENTRY:.*:]]
+// CHECK-CPP-NEXT: [[VAL_ADDR:%.*]] = alloca half, align 2
+// CHECK-CPP-NEXT: store half [[VAL]], ptr [[VAL_ADDR]], align 2
+// CHECK-CPP-NEXT: [[TMP0:%.*]] = load half, ptr [[VAL_ADDR]], align 2
+// CHECK-CPP-NEXT: [[TMP1:%.*]] = atomicrmw fadd ptr @h, half [[TMP0]] seq_cst, align 2
+// CHECK-CPP-NEXT: [[TMP2:%.*]] = fadd half [[TMP1]], [[TMP0]]
+// CHECK-CPP-NEXT: ret void
+//
+void test_f16_add(_Float16 val){
+ h += val;
+}
+
+// CHECK-C-LABEL: define dso_local void @test_float_sub(
+// CHECK-C-SAME: float noundef [[VAL:%.*]]) #[[ATTR0]] {
+// CHECK-C-NEXT: [[ENTRY:.*:]]
+// CHECK-C-NEXT: [[VAL_ADDR:%.*]] = alloca float, align 4
+// CHECK-C-NEXT: store float [[VAL]], ptr [[VAL_ADDR]], align 4
+// CHECK-C-NEXT: [[TMP0:%.*]] = load float, ptr [[VAL_ADDR]], align 4
+// CHECK-C-NEXT: [[TMP1:%.*]] = atomicrmw fsub ptr @f, float [[TMP0]] seq_cst, align 4
+// CHECK-C-NEXT: [[TMP2:%.*]] = fsub float [[TMP1]], [[TMP0]]
+// CHECK-C-NEXT: ret void
+//
+// CHECK-CPP-LABEL: define dso_local void @_Z14test_float_subf(
+// CHECK-CPP-SAME: float noundef [[VAL:%.*]]) #[[ATTR0]] {
+// CHECK-CPP-NEXT: [[ENTRY:.*:]]
+// CHECK-CPP-NEXT: [[VAL_ADDR:%.*]] = alloca float, align 4
+// CHECK-CPP-NEXT: store float [[VAL]], ptr [[VAL_ADDR]], align 4
+// CHECK-CPP-NEXT: [[TMP0:%.*]] = load float, ptr [[VAL_ADDR]], align 4
+// CHECK-CPP-NEXT: [[TMP1:%.*]] = atomicrmw fsub ptr @f, float [[TMP0]] seq_cst, align 4
+// CHECK-CPP-NEXT: [[TMP2:%.*]] = fsub float [[TMP1]], [[TMP0]]
+// CHECK-CPP-NEXT: ret void
+//
+void test_float_sub(float val) {
+ f -= val;
+}
+
+// CHECK-C-LABEL: define dso_local void @test_double_sub(
+// CHECK-C-SAME: double noundef [[VAL:%.*]]) #[[ATTR0]] {
+// CHECK-C-NEXT: [[ENTRY:.*:]]
+// CHECK-C-NEXT: [[VAL_ADDR:%.*]] = alloca double, align 8
+// CHECK-C-NEXT: store double [[VAL]], ptr [[VAL_ADDR]], align 8
+// CHECK-C-NEXT: [[TMP0:%.*]] = load double, ptr [[VAL_ADDR]], align 8
+// CHECK-C-NEXT: [[TMP1:%.*]] = atomicrmw fsub ptr @d, double [[TMP0]] seq_cst, align 8
+// CHECK-C-NEXT: [[TMP2:%.*]] = fsub double [[TMP1]], [[TMP0]]
+// CHECK-C-NEXT: ret void
+//
+// CHECK-CPP-LABEL: define dso_local void @_Z15test_double_subd(
+// CHECK-CPP-SAME: double noundef [[VAL:%.*]]) #[[ATTR0]] {
+// CHECK-CPP-NEXT: [[ENTRY:.*:]]
+// CHECK-CPP-NEXT: [[VAL_ADDR:%.*]] = alloca double, align 8
+// CHECK-CPP-NEXT: store double [[VAL]], ptr [[VAL_ADDR]], align 8
+// CHECK-CPP-NEXT: [[TMP0:%.*]] = load double, ptr [[VAL_ADDR]], align 8
+// CHECK-CPP-NEXT: [[TMP1:%.*]] = atomicrmw fsub ptr @d, double [[TMP0]] seq_cst, align 8
+// CHECK-CPP-NEXT: [[TMP2:%.*]] = fsub double [[TMP1]], [[TMP0]]
+// CHECK-CPP-NEXT: ret void
+//
+void test_double_sub(double val){
+ d -= val;
+}
+
+// CHECK-C-LABEL: define dso_local void @test_bf16_sub(
+// CHECK-C-SAME: bfloat noundef [[VAL:%.*]]) #[[ATTR0]] {
+// CHECK-C-NEXT: [[ENTRY:.*:]]
+// CHECK-C-NEXT: [[VAL_ADDR:%.*]] = alloca bfloat, align 2
+// CHECK-C-NEXT: store bfloat [[VAL]], ptr [[VAL_ADDR]], align 2
+// CHECK-C-NEXT: [[TMP0:%.*]] = load bfloat, ptr [[VAL_ADDR]], align 2
+// CHECK-C-NEXT: [[EXT:%.*]] = fpext bfloat [[TMP0]] to float
+// CHECK-C-NEXT: [[CONV:%.*]] = fptrunc float [[EXT]] to bfloat
+// CHECK-C-NEXT: [[TMP1:%.*]] = atomicrmw fsub ptr @bf, bfloat [[CONV]] seq_cst, align 2
+// CHECK-C-NEXT: [[TMP2:%.*]] = fsub bfloat [[TMP1]], [[CONV]]
+// CHECK-C-NEXT: ret void
+//
+// CHECK-CPP-LABEL: define dso_local void @_Z13test_bf16_subu6__bf16(
+// CHECK-CPP-SAME: bfloat noundef [[VAL:%.*]]) #[[ATTR0]] {
+// CHECK-CPP-NEXT: [[ENTRY:.*:]]
+// CHECK-CPP-NEXT: [[VAL_ADDR:%.*]] = alloca bfloat, align 2
+// CHECK-CPP-NEXT: store bfloat [[VAL]], ptr [[VAL_ADDR]], align 2
+// CHECK-CPP-NEXT: [[TMP0:%.*]] = load bfloat, ptr [[VAL_ADDR]], align 2
+// CHECK-CPP-NEXT: [[EXT:%.*]] = fpext bfloat [[TMP0]] to float
+// CHECK-CPP-NEXT: [[CONV:%.*]] = fptrunc float [[EXT]] to bfloat
+// CHECK-CPP-NEXT: [[TMP1:%.*]] = atomicrmw fsub ptr @bf, bfloat [[CONV]] seq_cst, align 2
+// CHECK-CPP-NEXT: [[TMP2:%.*]] = fsub bfloat [[TMP1]], [[CONV]]
+// CHECK-CPP-NEXT: ret void
+//
+void test_bf16_sub(__bf16 val){
+ bf -= val;
+}
+
+// CHECK-C-LABEL: define dso_local void @test_f16_sub(
+// CHECK-C-SAME: half noundef [[VAL:%.*]]) #[[ATTR0]] {
+// CHECK-C-NEXT: [[ENTRY:.*:]]
+// CHECK-C-NEXT: [[VAL_ADDR:%.*]] = alloca half, align 2
+// CHECK-C-NEXT: store half [[VAL]], ptr [[VAL_ADDR]], align 2
+// CHECK-C-NEXT: [[TMP0:%.*]] = load half, ptr [[VAL_ADDR]], align 2
+// CHECK-C-NEXT: [[TMP1:%.*]] = atomicrmw fsub ptr @h, half [[TMP0]] seq_cst, align 2
+// CHECK-C-NEXT: [[TMP2:%.*]] = fsub half [[TMP1]], [[TMP0]]
+// CHECK-C-NEXT: ret void
+//
+// CHECK-CPP-LABEL: define dso_local void @_Z12test_f16_subDF16_(
+// CHECK-CPP-SAME: half noundef [[VAL:%.*]]) #[[ATTR0]] {
+// CHECK-CPP-NEXT: [[ENTRY:.*:]]
+// CHECK-CPP-NEXT: [[VAL_ADDR:%.*]] = alloca half, align 2
+// CHECK-CPP-NEXT: store half [[VAL]], ptr [[VAL_ADDR]], align 2
+// CHECK-CPP-NEXT: [[TMP0:%.*]] = load half, ptr [[VAL_ADDR]], align 2
+// CHECK-CPP-NEXT: [[TMP1:%.*]] = atomicrmw fsub ptr @h, half [[TMP0]] seq_cst, align 2
+// CHECK-CPP-NEXT: [[TMP2:%.*]] = fsub half [[TMP1]], [[TMP0]]
+// CHECK-CPP-NEXT: ret void
+//
+void test_f16_sub(_Float16 val){
+ h -= val;
+}