Skip to content

Commit 2b6e9d2

Browse files
authored
[NVPTX] Set default value of nvptx-allow-ftz-atomics to true (#206154)
This is a follow-up to #200732. Setting the default value of nvptx-allow-ftz-atomics to true preserves the status quo. It allows the NVPTX backend to emit native floating-point atomic add instructions by default, rather than falling back to the CAS loop required for strict FTZ compliance. Correct FTZ handling forces a lot of existing code to emit CAS loops. This introduces thread divergence, breaking code that relies on atomic operations being non-divergent (for example, warp-wide atomic additions followed by a load of the reduced value without explicit synchronization). While FTZ-correct atomic behavior is desirable as a long-term default, introducing thread divergence into a common operation that previously "happened to work" without synchronization is highly disruptive. Defaulting to true makes the FTZ-correct behavior opt-in for now, allowing users time to audit and adapt their code. Test cases are updated to verify both enabled and disabled states of the option, alongside the new default behavior.
1 parent 9b096bb commit 2b6e9d2

7 files changed

Lines changed: 7594 additions & 3893 deletions

File tree

llvm/lib/Target/NVPTX/NVPTXISelLowering.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,14 +110,15 @@ static cl::opt<bool> UsePrecSqrtF32(
110110
cl::init(true));
111111

112112
// PTX atom.add.f32 has fixed FTZ behavior that may not match the function's
113-
// (see shouldExpandAtomicRMWInIR), so by default we fall back to a CAS loop
114-
// when they disagree. This flag is an escape hatch to use atom.add anyway,
115-
// trading correct denormal handling for the speed of the native instruction.
113+
// (see shouldExpandAtomicRMWInIR), so we'd normally fall back to a CAS loop
114+
// when they disagree. This option (enabled by default) allows using atom.add
115+
// anyway, trading correct denormal handling for the speed of the native
116+
// instruction.
116117
static cl::opt<bool> AllowFTZAtomics(
117118
"nvptx-allow-ftz-atomics", cl::Hidden,
118119
cl::desc("NVPTX Specific: Lower atomicrmw fadd to atom.add even when its "
119120
"FTZ behavior does not match the function's denormal mode."),
120-
cl::init(false));
121+
cl::init(true));
121122

122123
/// Whereas CUDA's implementation (see libdevice) uses ex2.approx for exp2(), it
123124
/// does NOT use lg2.approx for log2, so this is disabled by default.

llvm/test/CodeGen/NVPTX/atomicrmw-allow-ftz-atomics.ll

Lines changed: 53 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,39 @@
11
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
22
; -nvptx-allow-ftz-atomics forces atomicrmw fadd to lower to atom.add even when
33
; the hardware's FTZ behavior doesn't match the function's denormal mode, where
4-
; we'd otherwise emit a CAS loop. Compare the default lowering (DEFAULT) against
5-
; the flag (ALLOW).
6-
; RUN: llc < %s -march=nvptx64 -mcpu=sm_90 -mattr=+ptx87 | FileCheck %s --check-prefixes=CHECK,DEFAULT
7-
; RUN: llc < %s -march=nvptx64 -mcpu=sm_90 -mattr=+ptx87 -nvptx-allow-ftz-atomics | FileCheck %s --check-prefixes=CHECK,ALLOW
4+
; we'd otherwise emit a CAS loop. Compare the lowering with the option disabled
5+
; (DISALLOW) against the option enabled or default (ALLOW).
6+
; RUN: llc < %s -march=nvptx64 -mcpu=sm_90 -mattr=+ptx87 -nvptx-allow-ftz-atomics=false | FileCheck %s --check-prefixes=CHECK,DISALLOW
7+
; RUN: llc < %s -march=nvptx64 -mcpu=sm_90 -mattr=+ptx87 -nvptx-allow-ftz-atomics=true | FileCheck %s --check-prefixes=CHECK,ALLOW
8+
; RUN: llc < %s -march=nvptx64 -mcpu=sm_90 -mattr=+ptx87 | FileCheck %s --check-prefixes=CHECK,ALLOW
9+
; RUN: %if ptxas-sm_90 && ptxas-isa-8.7 %{ llc < %s -march=nvptx64 -mcpu=sm_90 -mattr=+ptx87 -nvptx-allow-ftz-atomics=false | %ptxas-verify -arch=sm_90 %}
10+
; RUN: %if ptxas-sm_90 && ptxas-isa-8.7 %{ llc < %s -march=nvptx64 -mcpu=sm_90 -mattr=+ptx87 -nvptx-allow-ftz-atomics=true | %ptxas-verify -arch=sm_90 %}
811
; RUN: %if ptxas-sm_90 && ptxas-isa-8.7 %{ llc < %s -march=nvptx64 -mcpu=sm_90 -mattr=+ptx87 | %ptxas-verify -arch=sm_90 %}
9-
; RUN: %if ptxas-sm_90 && ptxas-isa-8.7 %{ llc < %s -march=nvptx64 -mcpu=sm_90 -mattr=+ptx87 -nvptx-allow-ftz-atomics | %ptxas-verify -arch=sm_90 %}
1012

11-
; global f32: atom.add flushes, so default uses a CAS loop (function is not FTZ);
12-
; the flag forces atom.add.
13+
; global f32: atom.add flushes, so we'd normally use a CAS loop (function is not FTZ)
14+
; when the option is disabled; when enabled (default), it lowers to atom.add.
1315
define float @fadd_f32_global(ptr addrspace(1) %addr, float %val) {
14-
; DEFAULT-LABEL: fadd_f32_global(
15-
; DEFAULT: {
16-
; DEFAULT-NEXT: .reg .pred %p<2>;
17-
; DEFAULT-NEXT: .reg .b32 %r<5>;
18-
; DEFAULT-NEXT: .reg .b64 %rd<2>;
19-
; DEFAULT-EMPTY:
20-
; DEFAULT-NEXT: // %bb.0:
21-
; DEFAULT-NEXT: ld.param.b32 %r2, [fadd_f32_global_param_1];
22-
; DEFAULT-NEXT: ld.param.b64 %rd1, [fadd_f32_global_param_0];
23-
; DEFAULT-NEXT: ld.relaxed.sys.global.b32 %r4, [%rd1];
24-
; DEFAULT-NEXT: $L__BB0_1: // %atomicrmw.start
25-
; DEFAULT-NEXT: // =>This Inner Loop Header: Depth=1
26-
; DEFAULT-NEXT: add.rn.f32 %r3, %r4, %r2;
27-
; DEFAULT-NEXT: atom.relaxed.sys.global.cas.b32 %r1, [%rd1], %r4, %r3;
28-
; DEFAULT-NEXT: setp.ne.b32 %p1, %r1, %r4;
29-
; DEFAULT-NEXT: mov.b32 %r4, %r1;
30-
; DEFAULT-NEXT: @%p1 bra $L__BB0_1;
31-
; DEFAULT-NEXT: // %bb.2: // %atomicrmw.end
32-
; DEFAULT-NEXT: st.param.b32 [func_retval0], %r1;
33-
; DEFAULT-NEXT: ret;
16+
;
17+
; DISALLOW-LABEL: fadd_f32_global(
18+
; DISALLOW: {
19+
; DISALLOW-NEXT: .reg .pred %p<2>;
20+
; DISALLOW-NEXT: .reg .b32 %r<5>;
21+
; DISALLOW-NEXT: .reg .b64 %rd<2>;
22+
; DISALLOW-EMPTY:
23+
; DISALLOW-NEXT: // %bb.0:
24+
; DISALLOW-NEXT: ld.param.b32 %r2, [fadd_f32_global_param_1];
25+
; DISALLOW-NEXT: ld.param.b64 %rd1, [fadd_f32_global_param_0];
26+
; DISALLOW-NEXT: ld.relaxed.sys.global.b32 %r4, [%rd1];
27+
; DISALLOW-NEXT: $L__BB0_1: // %atomicrmw.start
28+
; DISALLOW-NEXT: // =>This Inner Loop Header: Depth=1
29+
; DISALLOW-NEXT: add.rn.f32 %r3, %r4, %r2;
30+
; DISALLOW-NEXT: atom.relaxed.sys.global.cas.b32 %r1, [%rd1], %r4, %r3;
31+
; DISALLOW-NEXT: setp.ne.b32 %p1, %r1, %r4;
32+
; DISALLOW-NEXT: mov.b32 %r4, %r1;
33+
; DISALLOW-NEXT: @%p1 bra $L__BB0_1;
34+
; DISALLOW-NEXT: // %bb.2: // %atomicrmw.end
35+
; DISALLOW-NEXT: st.param.b32 [func_retval0], %r1;
36+
; DISALLOW-NEXT: ret;
3437
;
3538
; ALLOW-LABEL: fadd_f32_global(
3639
; ALLOW: {
@@ -47,29 +50,30 @@ define float @fadd_f32_global(ptr addrspace(1) %addr, float %val) {
4750
ret float %r
4851
}
4952

50-
; generic f32: we can't know the memory space, so default always uses a CAS loop;
51-
; the flag forces atom.add.
53+
; generic f32: we can't know the memory space, so we'd normally use a CAS loop
54+
; when the option is disabled; when enabled (default), it lowers to atom.add.
5255
define float @fadd_f32_generic(ptr %addr, float %val) {
53-
; DEFAULT-LABEL: fadd_f32_generic(
54-
; DEFAULT: {
55-
; DEFAULT-NEXT: .reg .pred %p<2>;
56-
; DEFAULT-NEXT: .reg .b32 %r<5>;
57-
; DEFAULT-NEXT: .reg .b64 %rd<2>;
58-
; DEFAULT-EMPTY:
59-
; DEFAULT-NEXT: // %bb.0:
60-
; DEFAULT-NEXT: ld.param.b32 %r2, [fadd_f32_generic_param_1];
61-
; DEFAULT-NEXT: ld.param.b64 %rd1, [fadd_f32_generic_param_0];
62-
; DEFAULT-NEXT: ld.relaxed.sys.b32 %r4, [%rd1];
63-
; DEFAULT-NEXT: $L__BB1_1: // %atomicrmw.start
64-
; DEFAULT-NEXT: // =>This Inner Loop Header: Depth=1
65-
; DEFAULT-NEXT: add.rn.f32 %r3, %r4, %r2;
66-
; DEFAULT-NEXT: atom.relaxed.sys.cas.b32 %r1, [%rd1], %r4, %r3;
67-
; DEFAULT-NEXT: setp.ne.b32 %p1, %r1, %r4;
68-
; DEFAULT-NEXT: mov.b32 %r4, %r1;
69-
; DEFAULT-NEXT: @%p1 bra $L__BB1_1;
70-
; DEFAULT-NEXT: // %bb.2: // %atomicrmw.end
71-
; DEFAULT-NEXT: st.param.b32 [func_retval0], %r1;
72-
; DEFAULT-NEXT: ret;
56+
;
57+
; DISALLOW-LABEL: fadd_f32_generic(
58+
; DISALLOW: {
59+
; DISALLOW-NEXT: .reg .pred %p<2>;
60+
; DISALLOW-NEXT: .reg .b32 %r<5>;
61+
; DISALLOW-NEXT: .reg .b64 %rd<2>;
62+
; DISALLOW-EMPTY:
63+
; DISALLOW-NEXT: // %bb.0:
64+
; DISALLOW-NEXT: ld.param.b32 %r2, [fadd_f32_generic_param_1];
65+
; DISALLOW-NEXT: ld.param.b64 %rd1, [fadd_f32_generic_param_0];
66+
; DISALLOW-NEXT: ld.relaxed.sys.b32 %r4, [%rd1];
67+
; DISALLOW-NEXT: $L__BB1_1: // %atomicrmw.start
68+
; DISALLOW-NEXT: // =>This Inner Loop Header: Depth=1
69+
; DISALLOW-NEXT: add.rn.f32 %r3, %r4, %r2;
70+
; DISALLOW-NEXT: atom.relaxed.sys.cas.b32 %r1, [%rd1], %r4, %r3;
71+
; DISALLOW-NEXT: setp.ne.b32 %p1, %r1, %r4;
72+
; DISALLOW-NEXT: mov.b32 %r4, %r1;
73+
; DISALLOW-NEXT: @%p1 bra $L__BB1_1;
74+
; DISALLOW-NEXT: // %bb.2: // %atomicrmw.end
75+
; DISALLOW-NEXT: st.param.b32 [func_retval0], %r1;
76+
; DISALLOW-NEXT: ret;
7377
;
7478
; ALLOW-LABEL: fadd_f32_generic(
7579
; ALLOW: {

0 commit comments

Comments
 (0)