Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Global Variable Optimizer] Incorrectly lift a modulo computation outside an if statement #46922

Closed
karineek opened this issue Sep 18, 2020 · 13 comments
Labels
bugzilla Issues migrated from bugzilla

Comments

@karineek
Copy link
Member

Bugzilla Link 47578
Resolution FIXED
Resolved on Aug 27, 2021 05:27
Version 10.0
OS Linux
CC @afd,@ccadar,@efriedma-quic,@jdoerfert,@karineek,@LebedevRI,@zygoloid,@rotateright
Fixed by commit(s) 416a119

Extended Description

#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
@jdoerfert
Copy link
Member

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

@LebedevRI
Copy link
Member

*** 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 762fbbe)"}
!​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 762fbbe)"}

@jdoerfert
Copy link
Member

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.

@zygoloid
Copy link
Mannequin

zygoloid mannequin commented Sep 18, 2020

Looks like IRBuilder is creating a ConstantExpr that's not safe to speculatively evaluate. Is that supposed to be possible?

@efriedma-quic
Copy link
Collaborator

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.

@efriedma-quic
Copy link
Collaborator

Constant::canTrap can be used to query whether a constant is one of these possibly-trapping constants.

@afd
Copy link
Mannequin

afd mannequin commented Aug 24, 2021

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?

@efriedma-quic
Copy link
Collaborator

Looks like the same bug, yes.

@afd
Copy link
Mannequin

afd mannequin commented Aug 26, 2021

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?

@jdoerfert
Copy link
Member

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.

@afd
Copy link
Mannequin

afd mannequin commented Aug 26, 2021

Thanks for the explanation, that's really interesting.

@rotateright
Copy link
Contributor

Patch proposal:
https://reviews.llvm.org/D108771

@rotateright
Copy link
Contributor

Should be fixed with:
https://reviews.llvm.org/rG416a119f9e5c

@llvmbot llvmbot transferred this issue from llvm/llvm-bugzilla-archive Dec 10, 2021
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bugzilla Issues migrated from bugzilla
Projects
None yet
Development

No branches or pull requests

5 participants