Skip to content

Commit 34c7cf0

Browse files
authored
[Clang] Add support for fp when using min_fetch/max_fetch atomics (#160330)
Previously when using min_fetch/max_fetch atomics with floating point types, LLVM would emit a crash. This patch updates the EmitPostAtomicMinMax function in CGAtomic.cpp to take floating point types. Included is a clang CodeGen test atomic-ops-float-check-minmax.c and Sema test atomic-ops-fp-minmax.c.
1 parent 735ee5c commit 34c7cf0

File tree

3 files changed

+145
-0
lines changed

3 files changed

+145
-0
lines changed

clang/lib/CodeGen/CGAtomic.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,18 @@ static llvm::Value *EmitPostAtomicMinMax(CGBuilderTy &Builder,
507507
bool IsSigned,
508508
llvm::Value *OldVal,
509509
llvm::Value *RHS) {
510+
const bool IsFP = OldVal->getType()->isFloatingPointTy();
511+
512+
if (IsFP) {
513+
llvm::Intrinsic::ID IID = (Op == AtomicExpr::AO__atomic_max_fetch ||
514+
Op == AtomicExpr::AO__scoped_atomic_max_fetch)
515+
? llvm::Intrinsic::maxnum
516+
: llvm::Intrinsic::minnum;
517+
518+
return Builder.CreateBinaryIntrinsic(IID, OldVal, RHS, llvm::FMFSource(),
519+
"newval");
520+
}
521+
510522
llvm::CmpInst::Predicate Pred;
511523
switch (Op) {
512524
default:
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 6
2+
// RUN: %clang_cc1 %s -emit-llvm -o - -ffreestanding -triple=aarch64-linux-gnu -pthread | FileCheck %s
3+
4+
#include <stdint.h>
5+
#include <stdatomic.h>
6+
7+
// CHECK-LABEL: define dso_local void @test_minmax_postop(
8+
// CHECK-SAME: ptr noundef [[F32:%.*]], ptr noundef [[F16:%.*]], ptr noundef [[BF16:%.*]], ptr noundef [[F64:%.*]]) #[[ATTR0:[0-9]+]] {
9+
// CHECK-NEXT: [[ENTRY:.*:]]
10+
// CHECK-NEXT: [[F32_ADDR:%.*]] = alloca ptr, align 8
11+
// CHECK-NEXT: [[F16_ADDR:%.*]] = alloca ptr, align 8
12+
// CHECK-NEXT: [[BF16_ADDR:%.*]] = alloca ptr, align 8
13+
// CHECK-NEXT: [[F64_ADDR:%.*]] = alloca ptr, align 8
14+
// CHECK-NEXT: [[DOTATOMICTMP:%.*]] = alloca double, align 8
15+
// CHECK-NEXT: [[ATOMIC_TEMP:%.*]] = alloca double, align 8
16+
// CHECK-NEXT: [[DOTATOMICTMP1:%.*]] = alloca float, align 4
17+
// CHECK-NEXT: [[ATOMIC_TEMP2:%.*]] = alloca float, align 4
18+
// CHECK-NEXT: [[DOTATOMICTMP4:%.*]] = alloca half, align 2
19+
// CHECK-NEXT: [[ATOMIC_TEMP5:%.*]] = alloca half, align 2
20+
// CHECK-NEXT: [[DOTATOMICTMP7:%.*]] = alloca bfloat, align 2
21+
// CHECK-NEXT: [[ATOMIC_TEMP8:%.*]] = alloca bfloat, align 2
22+
// CHECK-NEXT: [[DOTATOMICTMP10:%.*]] = alloca double, align 8
23+
// CHECK-NEXT: [[ATOMIC_TEMP11:%.*]] = alloca double, align 8
24+
// CHECK-NEXT: [[DOTATOMICTMP13:%.*]] = alloca float, align 4
25+
// CHECK-NEXT: [[ATOMIC_TEMP14:%.*]] = alloca float, align 4
26+
// CHECK-NEXT: [[DOTATOMICTMP16:%.*]] = alloca half, align 2
27+
// CHECK-NEXT: [[ATOMIC_TEMP17:%.*]] = alloca half, align 2
28+
// CHECK-NEXT: [[DOTATOMICTMP19:%.*]] = alloca bfloat, align 2
29+
// CHECK-NEXT: [[ATOMIC_TEMP20:%.*]] = alloca bfloat, align 2
30+
// CHECK-NEXT: store ptr [[F32]], ptr [[F32_ADDR]], align 8
31+
// CHECK-NEXT: store ptr [[F16]], ptr [[F16_ADDR]], align 8
32+
// CHECK-NEXT: store ptr [[BF16]], ptr [[BF16_ADDR]], align 8
33+
// CHECK-NEXT: store ptr [[F64]], ptr [[F64_ADDR]], align 8
34+
// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[F64_ADDR]], align 8
35+
// CHECK-NEXT: store double 4.210000e+01, ptr [[DOTATOMICTMP]], align 8
36+
// CHECK-NEXT: [[TMP1:%.*]] = load double, ptr [[DOTATOMICTMP]], align 8
37+
// CHECK-NEXT: [[TMP2:%.*]] = atomicrmw fmax ptr [[TMP0]], double [[TMP1]] release, align 8
38+
// CHECK-NEXT: [[NEWVAL:%.*]] = call double @llvm.maxnum.f64(double [[TMP2]], double [[TMP1]])
39+
// CHECK-NEXT: store double [[NEWVAL]], ptr [[ATOMIC_TEMP]], align 8
40+
// CHECK-NEXT: [[TMP3:%.*]] = load double, ptr [[ATOMIC_TEMP]], align 8
41+
// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[F64_ADDR]], align 8
42+
// CHECK-NEXT: store double [[TMP3]], ptr [[TMP4]], align 8
43+
// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[F32_ADDR]], align 8
44+
// CHECK-NEXT: store float 0x40450CCCC0000000, ptr [[DOTATOMICTMP1]], align 4
45+
// CHECK-NEXT: [[TMP6:%.*]] = load float, ptr [[DOTATOMICTMP1]], align 4
46+
// CHECK-NEXT: [[TMP7:%.*]] = atomicrmw fmax ptr [[TMP5]], float [[TMP6]] release, align 4
47+
// CHECK-NEXT: [[NEWVAL3:%.*]] = call float @llvm.maxnum.f32(float [[TMP7]], float [[TMP6]])
48+
// CHECK-NEXT: store float [[NEWVAL3]], ptr [[ATOMIC_TEMP2]], align 4
49+
// CHECK-NEXT: [[TMP8:%.*]] = load float, ptr [[ATOMIC_TEMP2]], align 4
50+
// CHECK-NEXT: [[TMP9:%.*]] = load ptr, ptr [[F32_ADDR]], align 8
51+
// CHECK-NEXT: store float [[TMP8]], ptr [[TMP9]], align 4
52+
// CHECK-NEXT: [[TMP10:%.*]] = load ptr, ptr [[F16_ADDR]], align 8
53+
// CHECK-NEXT: store half 0xH5143, ptr [[DOTATOMICTMP4]], align 2
54+
// CHECK-NEXT: [[TMP11:%.*]] = load half, ptr [[DOTATOMICTMP4]], align 2
55+
// CHECK-NEXT: [[TMP12:%.*]] = atomicrmw fmax ptr [[TMP10]], half [[TMP11]] release, align 2
56+
// CHECK-NEXT: [[NEWVAL6:%.*]] = call half @llvm.maxnum.f16(half [[TMP12]], half [[TMP11]])
57+
// CHECK-NEXT: store half [[NEWVAL6]], ptr [[ATOMIC_TEMP5]], align 2
58+
// CHECK-NEXT: [[TMP13:%.*]] = load half, ptr [[ATOMIC_TEMP5]], align 2
59+
// CHECK-NEXT: [[TMP14:%.*]] = load ptr, ptr [[F16_ADDR]], align 8
60+
// CHECK-NEXT: store half [[TMP13]], ptr [[TMP14]], align 2
61+
// CHECK-NEXT: [[TMP15:%.*]] = load ptr, ptr [[BF16_ADDR]], align 8
62+
// CHECK-NEXT: store bfloat 0xR4228, ptr [[DOTATOMICTMP7]], align 2
63+
// CHECK-NEXT: [[TMP16:%.*]] = load bfloat, ptr [[DOTATOMICTMP7]], align 2
64+
// CHECK-NEXT: [[TMP17:%.*]] = atomicrmw fmax ptr [[TMP15]], bfloat [[TMP16]] release, align 2
65+
// CHECK-NEXT: [[NEWVAL9:%.*]] = call bfloat @llvm.maxnum.bf16(bfloat [[TMP17]], bfloat [[TMP16]])
66+
// CHECK-NEXT: store bfloat [[NEWVAL9]], ptr [[ATOMIC_TEMP8]], align 2
67+
// CHECK-NEXT: [[TMP18:%.*]] = load bfloat, ptr [[ATOMIC_TEMP8]], align 2
68+
// CHECK-NEXT: [[TMP19:%.*]] = load ptr, ptr [[BF16_ADDR]], align 8
69+
// CHECK-NEXT: store bfloat [[TMP18]], ptr [[TMP19]], align 2
70+
// CHECK-NEXT: [[TMP20:%.*]] = load ptr, ptr [[F64_ADDR]], align 8
71+
// CHECK-NEXT: store double 4.210000e+01, ptr [[DOTATOMICTMP10]], align 8
72+
// CHECK-NEXT: [[TMP21:%.*]] = load double, ptr [[DOTATOMICTMP10]], align 8
73+
// CHECK-NEXT: [[TMP22:%.*]] = atomicrmw fmin ptr [[TMP20]], double [[TMP21]] release, align 8
74+
// CHECK-NEXT: [[NEWVAL12:%.*]] = call double @llvm.minnum.f64(double [[TMP22]], double [[TMP21]])
75+
// CHECK-NEXT: store double [[NEWVAL12]], ptr [[ATOMIC_TEMP11]], align 8
76+
// CHECK-NEXT: [[TMP23:%.*]] = load double, ptr [[ATOMIC_TEMP11]], align 8
77+
// CHECK-NEXT: [[TMP24:%.*]] = load ptr, ptr [[F64_ADDR]], align 8
78+
// CHECK-NEXT: store double [[TMP23]], ptr [[TMP24]], align 8
79+
// CHECK-NEXT: [[TMP25:%.*]] = load ptr, ptr [[F32_ADDR]], align 8
80+
// CHECK-NEXT: store float 0x40450CCCC0000000, ptr [[DOTATOMICTMP13]], align 4
81+
// CHECK-NEXT: [[TMP26:%.*]] = load float, ptr [[DOTATOMICTMP13]], align 4
82+
// CHECK-NEXT: [[TMP27:%.*]] = atomicrmw fmin ptr [[TMP25]], float [[TMP26]] release, align 4
83+
// CHECK-NEXT: [[NEWVAL15:%.*]] = call float @llvm.minnum.f32(float [[TMP27]], float [[TMP26]])
84+
// CHECK-NEXT: store float [[NEWVAL15]], ptr [[ATOMIC_TEMP14]], align 4
85+
// CHECK-NEXT: [[TMP28:%.*]] = load float, ptr [[ATOMIC_TEMP14]], align 4
86+
// CHECK-NEXT: [[TMP29:%.*]] = load ptr, ptr [[F32_ADDR]], align 8
87+
// CHECK-NEXT: store float [[TMP28]], ptr [[TMP29]], align 4
88+
// CHECK-NEXT: [[TMP30:%.*]] = load ptr, ptr [[F16_ADDR]], align 8
89+
// CHECK-NEXT: store half 0xH5143, ptr [[DOTATOMICTMP16]], align 2
90+
// CHECK-NEXT: [[TMP31:%.*]] = load half, ptr [[DOTATOMICTMP16]], align 2
91+
// CHECK-NEXT: [[TMP32:%.*]] = atomicrmw fmin ptr [[TMP30]], half [[TMP31]] release, align 2
92+
// CHECK-NEXT: [[NEWVAL18:%.*]] = call half @llvm.minnum.f16(half [[TMP32]], half [[TMP31]])
93+
// CHECK-NEXT: store half [[NEWVAL18]], ptr [[ATOMIC_TEMP17]], align 2
94+
// CHECK-NEXT: [[TMP33:%.*]] = load half, ptr [[ATOMIC_TEMP17]], align 2
95+
// CHECK-NEXT: [[TMP34:%.*]] = load ptr, ptr [[F16_ADDR]], align 8
96+
// CHECK-NEXT: store half [[TMP33]], ptr [[TMP34]], align 2
97+
// CHECK-NEXT: [[TMP35:%.*]] = load ptr, ptr [[BF16_ADDR]], align 8
98+
// CHECK-NEXT: store bfloat 0xR4228, ptr [[DOTATOMICTMP19]], align 2
99+
// CHECK-NEXT: [[TMP36:%.*]] = load bfloat, ptr [[DOTATOMICTMP19]], align 2
100+
// CHECK-NEXT: [[TMP37:%.*]] = atomicrmw fmin ptr [[TMP35]], bfloat [[TMP36]] release, align 2
101+
// CHECK-NEXT: [[NEWVAL21:%.*]] = call bfloat @llvm.minnum.bf16(bfloat [[TMP37]], bfloat [[TMP36]])
102+
// CHECK-NEXT: store bfloat [[NEWVAL21]], ptr [[ATOMIC_TEMP20]], align 2
103+
// CHECK-NEXT: [[TMP38:%.*]] = load bfloat, ptr [[ATOMIC_TEMP20]], align 2
104+
// CHECK-NEXT: [[TMP39:%.*]] = load ptr, ptr [[BF16_ADDR]], align 8
105+
// CHECK-NEXT: store bfloat [[TMP38]], ptr [[TMP39]], align 2
106+
// CHECK-NEXT: ret void
107+
//
108+
void test_minmax_postop(float *f32, _Float16 *f16, __bf16 *bf16, double *f64) {
109+
*f64 = __atomic_max_fetch(f64, 42.1, memory_order_release);
110+
*f32 = __atomic_max_fetch(f32, 42.1, memory_order_release);
111+
*f16 = __atomic_max_fetch(f16, 42.1, memory_order_release);
112+
*bf16 = __atomic_max_fetch(bf16, 42.1, memory_order_release);
113+
*f64 = __atomic_min_fetch(f64, 42.1, memory_order_release);
114+
*f32 = __atomic_min_fetch(f32, 42.1, memory_order_release);
115+
*f16 = __atomic_min_fetch(f16, 42.1, memory_order_release);
116+
*bf16 = __atomic_min_fetch(bf16, 42.1, memory_order_release);
117+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// RUN: %clang_cc1 -verify -ffreestanding -triple=aarch64-linux-gnu %s
2+
// REQUIRES: aarch64-registered-target
3+
4+
#include <stdatomic.h>
5+
6+
void memory_checks(_Float16 *p16, __bf16 *pbf, float *pf, double *pd) {
7+
(void)__atomic_fetch_min(p16, (_Float16)1.0f, memory_order_relaxed);
8+
(void)__atomic_fetch_max(pbf, (__bf16)2.0f, memory_order_acquire);
9+
(void)__atomic_fetch_min(pf, 3.0f, memory_order_release);
10+
(void)__atomic_fetch_max(pd, 4.0, memory_order_seq_cst);
11+
}
12+
13+
void nullPointerWarning(void) {
14+
(void)__atomic_fetch_min((volatile float*)0, 42.0, memory_order_relaxed); // expected-warning {{null passed to a callee that requires a non-null argument}}
15+
(void)__atomic_fetch_max((float*)0, 42.0, memory_order_relaxed); // expected-warning {{null passed to a callee that requires a non-null argument}}
16+
}

0 commit comments

Comments
 (0)