#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
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
*** 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)"}
(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.
Looks like IRBuilder is creating a ConstantExpr that's not safe to speculatively evaluate. Is that supposed to be possible?
(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.
Constant::canTrap can be used to query whether a constant is one of these possibly-trapping constants.
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?
Looks like the same bug, yes.
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?
(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.
Thanks for the explanation, that's really interesting.
Patch proposal: https://reviews.llvm.org/D108771
Should be fixed with: https://reviews.llvm.org/rG416a119f9e5c