Skip to content

Commit b58a697

Browse files
committed
[LICM] Don't promote store to global even in single-thread mode
Even if there are no thread-safety concerns, we should not promote (not guaranteed-to-execute) stores to globals without further analysis: While the global may be writable, we may not have provenance to perform the write. The @promote_global_noalias test case illustrates a miscompile in the presence of a noalias pointer to the global. Worth noting that the load-only promotion may also not be well-defined depending on precise semantics (we don't specify whether load violating noalias is poison or UB -- though I believe the general inclination is to make it poison, and only stores UB), but that's a more general issue. This is inspired by #60860, which is a related issue with TBAA metadata. Differential Revision: https://reviews.llvm.org/D146233
1 parent 82df745 commit b58a697

File tree

2 files changed

+40
-81
lines changed

2 files changed

+40
-81
lines changed

llvm/lib/Transforms/Scalar/LICM.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1943,6 +1943,8 @@ bool isNotVisibleOnUnwindInLoop(const Value *Object, const Loop *L,
19431943
isNotCapturedBeforeOrInLoop(Object, L, DT);
19441944
}
19451945

1946+
// We don't consider globals as writable: While the physical memory is writable,
1947+
// we may not have provenance to perform the write.
19461948
bool isWritableObject(const Value *Object) {
19471949
// TODO: Alloca might not be writable after its lifetime ends.
19481950
// See https://github.com/llvm/llvm-project/issues/51838.
@@ -1953,9 +1955,6 @@ bool isWritableObject(const Value *Object) {
19531955
if (auto *A = dyn_cast<Argument>(Object))
19541956
return A->hasByValAttr();
19551957

1956-
if (auto *G = dyn_cast<GlobalVariable>(Object))
1957-
return !G->isConstant();
1958-
19591958
// TODO: Noalias has nothing to do with writability, this should check for
19601959
// an allocator function.
19611960
return isNoAliasCall(Object);

llvm/test/Transforms/LICM/promote-single-thread.ll

Lines changed: 38 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -7,44 +7,26 @@
77

88
declare void @capture(ptr)
99

10-
; In single-thread mode both loads and stores can be promoted. In multi-thread
11-
; mode only loads can be promoted, as a different thread might write to the
12-
; global.
10+
; Even in single-thread mode, can only perform load-only promotion for globals,
11+
; because we might not have provenance to write to the global. See
12+
; promote_global_noalias for an example of the issue.
1313
define void @promote_global(i1 %c, i1 %c2) {
14-
; MT-LABEL: @promote_global(
15-
; MT-NEXT: entry:
16-
; MT-NEXT: [[G_PROMOTED:%.*]] = load i32, ptr @g, align 4
17-
; MT-NEXT: br label [[LOOP:%.*]]
18-
; MT: loop:
19-
; MT-NEXT: [[V_INC2:%.*]] = phi i32 [ [[V_INC1:%.*]], [[LATCH:%.*]] ], [ [[G_PROMOTED]], [[ENTRY:%.*]] ]
20-
; MT-NEXT: br i1 [[C:%.*]], label [[IF:%.*]], label [[LATCH]]
21-
; MT: if:
22-
; MT-NEXT: [[V_INC:%.*]] = add i32 [[V_INC2]], 1
23-
; MT-NEXT: store i32 [[V_INC]], ptr @g, align 4
24-
; MT-NEXT: br label [[LATCH]]
25-
; MT: latch:
26-
; MT-NEXT: [[V_INC1]] = phi i32 [ [[V_INC]], [[IF]] ], [ [[V_INC2]], [[LOOP]] ]
27-
; MT-NEXT: br i1 [[C2:%.*]], label [[EXIT:%.*]], label [[LOOP]]
28-
; MT: exit:
29-
; MT-NEXT: ret void
30-
;
31-
; ST-LABEL: @promote_global(
32-
; ST-NEXT: entry:
33-
; ST-NEXT: [[G_PROMOTED:%.*]] = load i32, ptr @g, align 4
34-
; ST-NEXT: br label [[LOOP:%.*]]
35-
; ST: loop:
36-
; ST-NEXT: [[V_INC2:%.*]] = phi i32 [ [[V_INC1:%.*]], [[LATCH:%.*]] ], [ [[G_PROMOTED]], [[ENTRY:%.*]] ]
37-
; ST-NEXT: br i1 [[C:%.*]], label [[IF:%.*]], label [[LATCH]]
38-
; ST: if:
39-
; ST-NEXT: [[V_INC:%.*]] = add i32 [[V_INC2]], 1
40-
; ST-NEXT: br label [[LATCH]]
41-
; ST: latch:
42-
; ST-NEXT: [[V_INC1]] = phi i32 [ [[V_INC]], [[IF]] ], [ [[V_INC2]], [[LOOP]] ]
43-
; ST-NEXT: br i1 [[C2:%.*]], label [[EXIT:%.*]], label [[LOOP]]
44-
; ST: exit:
45-
; ST-NEXT: [[V_INC1_LCSSA:%.*]] = phi i32 [ [[V_INC1]], [[LATCH]] ]
46-
; ST-NEXT: store i32 [[V_INC1_LCSSA]], ptr @g, align 4
47-
; ST-NEXT: ret void
14+
; CHECK-LABEL: @promote_global(
15+
; CHECK-NEXT: entry:
16+
; CHECK-NEXT: [[G_PROMOTED:%.*]] = load i32, ptr @g, align 4
17+
; CHECK-NEXT: br label [[LOOP:%.*]]
18+
; CHECK: loop:
19+
; CHECK-NEXT: [[V_INC2:%.*]] = phi i32 [ [[V_INC1:%.*]], [[LATCH:%.*]] ], [ [[G_PROMOTED]], [[ENTRY:%.*]] ]
20+
; CHECK-NEXT: br i1 [[C:%.*]], label [[IF:%.*]], label [[LATCH]]
21+
; CHECK: if:
22+
; CHECK-NEXT: [[V_INC:%.*]] = add i32 [[V_INC2]], 1
23+
; CHECK-NEXT: store i32 [[V_INC]], ptr @g, align 4
24+
; CHECK-NEXT: br label [[LATCH]]
25+
; CHECK: latch:
26+
; CHECK-NEXT: [[V_INC1]] = phi i32 [ [[V_INC]], [[IF]] ], [ [[V_INC2]], [[LOOP]] ]
27+
; CHECK-NEXT: br i1 [[C2:%.*]], label [[EXIT:%.*]], label [[LOOP]]
28+
; CHECK: exit:
29+
; CHECK-NEXT: ret void
4830
;
4931
entry:
5032
br label %loop
@@ -105,48 +87,26 @@ exit:
10587
; if %c is false and %ptr == @g, then this should store 42 to the pointer.
10688
; However, if we perform load+store promotion, then we would instead store the
10789
; original value of the global.
108-
; FIXME: This is a miscompile.
10990
define void @promote_global_noalias(i1 %c, i1 %c2, ptr noalias %ptr) {
110-
; MT-LABEL: @promote_global_noalias(
111-
; MT-NEXT: entry:
112-
; MT-NEXT: [[G_PROMOTED:%.*]] = load i32, ptr @g, align 4
113-
; MT-NEXT: br label [[LOOP:%.*]]
114-
; MT: loop:
115-
; MT-NEXT: [[V_INC2:%.*]] = phi i32 [ [[V_INC1:%.*]], [[LATCH:%.*]] ], [ [[G_PROMOTED]], [[ENTRY:%.*]] ]
116-
; MT-NEXT: br i1 [[C:%.*]], label [[IF:%.*]], label [[ELSE:%.*]]
117-
; MT: if:
118-
; MT-NEXT: [[V_INC:%.*]] = add i32 [[V_INC2]], 1
119-
; MT-NEXT: store i32 [[V_INC]], ptr @g, align 4
120-
; MT-NEXT: br label [[LATCH]]
121-
; MT: else:
122-
; MT-NEXT: store i32 42, ptr [[PTR:%.*]], align 4
123-
; MT-NEXT: br label [[LATCH]]
124-
; MT: latch:
125-
; MT-NEXT: [[V_INC1]] = phi i32 [ [[V_INC2]], [[ELSE]] ], [ [[V_INC]], [[IF]] ]
126-
; MT-NEXT: br i1 [[C2:%.*]], label [[EXIT:%.*]], label [[LOOP]]
127-
; MT: exit:
128-
; MT-NEXT: ret void
129-
;
130-
; ST-LABEL: @promote_global_noalias(
131-
; ST-NEXT: entry:
132-
; ST-NEXT: [[G_PROMOTED:%.*]] = load i32, ptr @g, align 4
133-
; ST-NEXT: br label [[LOOP:%.*]]
134-
; ST: loop:
135-
; ST-NEXT: [[V_INC2:%.*]] = phi i32 [ [[V_INC1:%.*]], [[LATCH:%.*]] ], [ [[G_PROMOTED]], [[ENTRY:%.*]] ]
136-
; ST-NEXT: br i1 [[C:%.*]], label [[IF:%.*]], label [[ELSE:%.*]]
137-
; ST: if:
138-
; ST-NEXT: [[V_INC:%.*]] = add i32 [[V_INC2]], 1
139-
; ST-NEXT: br label [[LATCH]]
140-
; ST: else:
141-
; ST-NEXT: store i32 42, ptr [[PTR:%.*]], align 4
142-
; ST-NEXT: br label [[LATCH]]
143-
; ST: latch:
144-
; ST-NEXT: [[V_INC1]] = phi i32 [ [[V_INC2]], [[ELSE]] ], [ [[V_INC]], [[IF]] ]
145-
; ST-NEXT: br i1 [[C2:%.*]], label [[EXIT:%.*]], label [[LOOP]]
146-
; ST: exit:
147-
; ST-NEXT: [[V_INC1_LCSSA:%.*]] = phi i32 [ [[V_INC1]], [[LATCH]] ]
148-
; ST-NEXT: store i32 [[V_INC1_LCSSA]], ptr @g, align 4
149-
; ST-NEXT: ret void
91+
; CHECK-LABEL: @promote_global_noalias(
92+
; CHECK-NEXT: entry:
93+
; CHECK-NEXT: [[G_PROMOTED:%.*]] = load i32, ptr @g, align 4
94+
; CHECK-NEXT: br label [[LOOP:%.*]]
95+
; CHECK: loop:
96+
; CHECK-NEXT: [[V_INC2:%.*]] = phi i32 [ [[V_INC1:%.*]], [[LATCH:%.*]] ], [ [[G_PROMOTED]], [[ENTRY:%.*]] ]
97+
; CHECK-NEXT: br i1 [[C:%.*]], label [[IF:%.*]], label [[ELSE:%.*]]
98+
; CHECK: if:
99+
; CHECK-NEXT: [[V_INC:%.*]] = add i32 [[V_INC2]], 1
100+
; CHECK-NEXT: store i32 [[V_INC]], ptr @g, align 4
101+
; CHECK-NEXT: br label [[LATCH]]
102+
; CHECK: else:
103+
; CHECK-NEXT: store i32 42, ptr [[PTR:%.*]], align 4
104+
; CHECK-NEXT: br label [[LATCH]]
105+
; CHECK: latch:
106+
; CHECK-NEXT: [[V_INC1]] = phi i32 [ [[V_INC2]], [[ELSE]] ], [ [[V_INC]], [[IF]] ]
107+
; CHECK-NEXT: br i1 [[C2:%.*]], label [[EXIT:%.*]], label [[LOOP]]
108+
; CHECK: exit:
109+
; CHECK-NEXT: ret void
150110
;
151111
entry:
152112
br label %loop

0 commit comments

Comments
 (0)