[LoopSink] Wrong code at -Os with profile data: LoopSink moves a writeonly call into a cold block
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
LoopSink pass miscompiles the following code.
small.ll
```llvm
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
@l = global [7 x i8] c"\01\00\01\01\06\01\01"
@.str = constant [4 x i8] c"%d\0A\00"
define i32 @main() {
entry:
call fastcc void @fn2()
%0 = load i8, ptr @l, align 1
%conv = sext i8 %0 to i32
%call1 = tail call i32 (ptr, ...) @printf(ptr @.str, i32 %conv)
ret i32 0
}
declare i32 @printf(ptr, ...)
define fastcc void @fn2() !prof !0 {
entry:
%call = call fastcc i16 @fn4()
br label %for.body.i.outer
for.body.i.outer: ; preds = %sw.epilog.preheader.i.preheader, %entry
br label %for.body.i
for.body.i: ; preds = %for.inc18.i.for.body.i_crit_edge, %for.body.i.outer
br i1 false, label %for.inc18.i, label %if.end.i
if.end.i: ; preds = %for.body.i
ret void
for.inc18.i: ; preds = %for.body.i
br i1 false, label %for.inc18.i.for.body.i_crit_edge, label %sw.epilog.preheader.i.preheader
for.inc18.i.for.body.i_crit_edge: ; preds = %for.inc18.i
br label %for.body.i
sw.epilog.preheader.i.preheader: ; preds = %for.inc18.i
%cond1.i = icmp eq i16 %call, 0
br label %for.body.i.outer
}
; Function Attrs: nounwind
define fastcc i16 @fn4() #0 {
entry:
store i8 0, ptr @l, align 1
ret i16 0
}
attributes #0 = { nounwind }
!0 = !{!"function_entry_count", i64 1}
```
Reproduce:
```console
$ lli small.ll
0
$ opt -passes='require,loop-sink' small.ll -S -o after.ll && lli after.ll
1
```
### C reproducer
small.c
```c
#include
char a = 1;
static int b[1];
short f, e;
int x, c;
char l[] = {1, 0, 1, 1, 6, 1, 1};
unsigned int w;
int(ac)(int ad, int ae) { return ad << ae; }
short(d)(short y) { return y; }
static int(fn5)(int ad) { return x || ad > x ? 0 : ad; }
int j = 780895;
char m = 1;
static short fn4(int n) {
short r;
int z;
short ah[2];
for (int i = 0; i < 2; i++)
ah[i] = 6;
unsigned ap = 1;
for (; ap; ap++) {
int an;
if (n) {
a = 0;
for (; a <= 8; a++)
l[6] = 0;
return b[0] = 0;
}
c = 0;
for (; c <= 8; c++) {
for (; an <= 8; an++) {
{ r = l[6] == 0 ? 0 : f % l[6]; }
ah[1] = r;
}
}
z = fn5(ah[1]);
ac(z, b[2]);
}
return b[0];
}
static char fn3(short k) {
short aa = -5L, t;
unsigned u = 186;
ab:
switch ((aa ^ u) + 186)
case -5: {
t = 9;
for (; t; t++)
if (a) {
if (0)
o:
goto p;
int af = (u | 3257915170) + 1037056898 - 4917;
for (int ag = 0; ag < af; ag = 7)
aa = 0;
} else {
goto ai;
for (; u; ++u)
switch (k)
case 1: {
for (int q = 0; q < 0; q++) {
goto o;
ai:;
}
}
continue;
p:
goto ab;
}
}
}
static int fn2(unsigned s) {
unsigned v = fn4(s);
fn3(v);
}
static int fn1() {
fn2(j);
switch (0) {
for (;; w = d(w))
for (int g; g; g++)
h:;
}
if (m)
return e;
goto h;
}
int main() {
int i;
fn1();
printf("%d\n", l[6]);
}
```
profile.txt
```
small.c:fn1
30359600853040728
1
0
small.c:fn3
865316332721329312
1
1
small.c:fn4
739338639883017289
1
0
small.c:fn2
0
5
1
0
0
0
1
small.c:fn5
# Func Hash:
6345692
# Num Counters:
1
# Counter Values:
1
```
Reproduce:
```console
$ clang-trunk -Os small.c && ./a.out
0
$ llvm-profdata merge -o test.profdata profile.txt
$ clang-trunk -Os -fprofile-instr-use=test.profdata small.c && ./a.out
1
```
Contributor guide
Research direction
Start with small.ll and reproduce the mismatch using opt with require,loop-sink, then run the transformed file with lli; the untransformed and transformed programs currently print different values. Compare this with the clang-trunk, profile-guided small.c reproduction and profile.txt. Done means LoopSink no longer causes the profiled -Os program to miscompile and the reproducer preserves the expected output.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- c
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 50/100