LLVM Bugzilla is read-only and represents the historical archive of all LLVM issues filled before November 26, 2021. Use github to submit LLVM bugs

Bug 47578 - [Global Variable Optimizer] Incorrectly lift a modulo computation outside an if statement
Summary: [Global Variable Optimizer] Incorrectly lift a modulo computation outside an ...
Status: RESOLVED FIXED
Alias: None
Product: libraries
Classification: Unclassified
Component: Scalar Optimizations (show other bugs)
Version: 10.0
Hardware: PC Linux
: P enhancement
Assignee: Unassigned LLVM Bugs
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2020-09-18 08:34 PDT by Karine Even-Mendoza
Modified: 2021-08-27 05:27 PDT (History)
11 users (show)

See Also:
Fixed By Commit(s): 416a119f9e5c


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Karine Even-Mendoza 2020-09-18 08:34:01 PDT
#include <stdio.h>

static long r = 0;
static long *m[1] = {&r};

static int i = 1;
static int *pi = &i;

int main() {
   unsigned x = 0;
   if (i) { // Evaluates to true
     ++x; // Guaranteed to be reached, and afterwards x == 1
     int test = pi == (int*) 1;
   }

   printf("x = %d\n", x);
   // At this point, x == 1
   if (!x) { // Evaluates to false
     i = 7UL % (m[0] == (long*) 1); // Unreachable.  (It would have UB if it *were* reachable, becuase of a mod by zero.)
   }
}

Get "floating point exception" with -02, Ubuntun, and clang-6/Clang-10.
The bug is no appearing with -O0 flag.

Restor the bug:
clang-10 -O2 -w test1.c 
./a.out 
Floating point exception (core dumped)
======
clang-10 -O0 -w test1.c 
./a.out 
x = 1

I tried with two versions, both got the bugs:
1) from llvm trunk: clang version 10.0.0 (trunk), Target: x86_64-unknown-linux-gnu, Thread model: posix, InstalledDir: /home/user42/llvm-csmith-0/llvm-install/bin (from 03/09/2020)
2) Ubuntu's: clang version 10.0.0-4ubuntu1~18.04.2, Target: x86_64-pc-linux-gnu, Thread model: posix, InstalledDir: /usr/bin
Comment 1 Johannes Doerfert 2020-09-18 09:06:11 PDT
I think the problem is in instcombine, the reuse of a condition is coupled with the swap of the branch targets, that seems wrong: https://godbolt.org/z/YMvxr6
Comment 2 Roman Lebedev 2020-09-18 09:33:02 PDT
*** IR Dump Before Global Variable Optimizer ***
; ModuleID = '/tmp/test.c'
source_filename = "/tmp/test.c"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

@i = internal unnamed_addr global i32 1, align 4
@r = internal global i64 0, align 8

; Function Attrs: nounwind uwtable
define dso_local i32 @main() local_unnamed_addr #0 {
entry:
  %0 = load i32, i32* @i, align 4, !tbaa !2
  %tobool.not = icmp eq i32 %0, 0
  %not.tobool.not = xor i1 %tobool.not, true
  %spec.select = zext i1 %not.tobool.not to i32
  tail call void @use(i32 %spec.select) #2
  br i1 %tobool.not, label %if.then2, label %if.end7

if.then2:                                         ; preds = %entry
  store i32 trunc (i64 urem (i64 7, i64 zext (i1 icmp eq (i64* inttoptr (i64 1 to i64*), i64* @r) to i64)) to i32), i32* @i, align 4, !tbaa !2
  br label %if.end7

if.end7:                                          ; preds = %if.then2, %entry
  ret i32 0
}

declare dso_local void @use(i32) local_unnamed_addr #1

attributes #0 = { nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #2 = { nounwind }

!llvm.module.flags = !{!0}
!llvm.ident = !{!1}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{!"clang version 12.0.0 (git@github.com:LebedevRI/llvm-project.git 762fbbe536996acf7175b551ce0e2c310165b135)"}
!2 = !{!3, !3, i64 0}
!3 = !{!"int", !4, i64 0}
!4 = !{!"omnipotent char", !5, i64 0}
!5 = !{!"Simple C/C++ TBAA"}
*** IR Dump Before Dead Global Elimination ***
; ModuleID = '/tmp/test.c'
source_filename = "/tmp/test.c"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

@i = internal unnamed_addr global i1 false, align 4
@r = internal global i64 0, align 8

; Function Attrs: nounwind uwtable
define dso_local i32 @main() local_unnamed_addr #0 {
entry:
  %.b = load i1, i1* @i, align 1
  %0 = select i1 %.b, i32 trunc (i64 urem (i64 7, i64 zext (i1 icmp eq (i64* inttoptr (i64 1 to i64*), i64* @r) to i64)) to i32), i32 1
  %tobool.not = icmp eq i32 %0, 0
  %not.tobool.not = xor i1 %tobool.not, true
  %spec.select = zext i1 %not.tobool.not to i32
  tail call void @use(i32 %spec.select) #2
  br i1 %tobool.not, label %if.then2, label %if.end7

if.then2:                                         ; preds = %entry
  store i1 true, i1* @i, align 1
  br label %if.end7

if.end7:                                          ; preds = %if.then2, %entry
  ret i32 0
}

declare dso_local void @use(i32) local_unnamed_addr #1

attributes #0 = { nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #2 = { nounwind }

!llvm.module.flags = !{!0}
!llvm.ident = !{!1}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{!"clang version 12.0.0 (git@github.com:LebedevRI/llvm-project.git 762fbbe536996acf7175b551ce0e2c310165b135)"}
Comment 3 Johannes Doerfert 2020-09-18 10:05:32 PDT
(In reply to Johannes Doerfert from comment #1)
> I think the problem is in instcombine, the reuse of a condition is coupled
> with the swap of the branch targets, that seems wrong:
> https://godbolt.org/z/YMvxr6

Nevermind, ^ is not true.
Comment 4 Richard Smith 2020-09-18 10:41:30 PDT
Looks like IRBuilder is creating a ConstantExpr that's not safe to speculatively evaluate. Is that supposed to be possible?
Comment 5 Eli Friedman 2020-09-18 14:23:30 PDT
(In reply to Richard Smith from comment #4)
> Looks like IRBuilder is creating a ConstantExpr that's not safe to
> speculatively evaluate. Is that supposed to be possible?

Yes, unfortunately.

This generally isn't a problem unless the instruction that uses the constantexpr is moved, like GlobalOpt is doing here.
Comment 6 Eli Friedman 2020-09-18 14:25:43 PDT
Constant::canTrap can be used to query whether a constant is one of these possibly-trapping constants.
Comment 7 Alastair Donaldson 2021-08-24 03:14:51 PDT
I used C-Reduce to shrink the original test case that found this bug, using clang 12.0.1.

I ended up with this test case:

#include <stdio.h>

static long *a, *b;
static short d[1];
short e(f, g) { return f % g; }
void h() {
  long *c = &b;
  long *i = &a[2];
  *d = e(7, i == c);
}
int main() { printf("%d\n", d[0]); }


$ clang-12 -O2 miscompile.c
miscompile.c:7:9: warning: incompatible pointer types initializing 'long *' with an expression of type 'long **'; remove & [-Wincompatible-pointer-types]
  long *c = &b;
        ^   ~~
1 warning generated.


$ ./a.out 
Floating point exception (core dumped)


Despite the warning, I believe the program is UB-free, in particular because "main" doesn't call the function "h".

Does this look like an instance of the same bug?
Comment 8 Eli Friedman 2021-08-24 11:24:30 PDT
Looks like the same bug, yes.
Comment 9 Alastair Donaldson 2021-08-26 02:36:13 PDT
Thanks. So does it seem like the problem (in my reduced example) is that function "h", and as a result "e", is somehow being speculatively evaluated even though it's not called from main?
Comment 10 Johannes Doerfert 2021-08-26 07:38:28 PDT
(In reply to Alastair Donaldson from comment #9)
> Thanks. So does it seem like the problem (in my reduced example) is that
> function "h", and as a result "e", is somehow being speculatively evaluated
> even though it's not called from main?

Not called, inter-procedural constant propagation replaces d[0] with a select between 0 and the only value that was ever written in d[0].
So basically d[0] becomes, for reasons I don't grasp right now:
``` 
tmp = d[0];
val = tmp ? 7 % ((void*)16 == &b): 0;
printf(..., val);
```

See https://godbolt.org/z/1zaTsvvrE.
Comment 11 Alastair Donaldson 2021-08-26 07:58:11 PDT
Thanks for the explanation, that's really interesting.
Comment 12 Sanjay Patel 2021-08-26 10:08:00 PDT
Patch proposal:
https://reviews.llvm.org/D108771
Comment 13 Sanjay Patel 2021-08-27 05:27:14 PDT
Should be fixed with:
https://reviews.llvm.org/rG416a119f9e5c