Quuxplusone / LLVMBugzillaTest

0 stars 0 forks source link

Segmentation fault in simple function call using O1 ~ Os #44091

Open Quuxplusone opened 4 years ago

Quuxplusone commented 4 years ago
Bugzilla Link PR45121
Status CONFIRMED
Importance P normal
Reported by Haoxin Tu (haoxintu@gmail.com)
Reported on 2020-03-05 07:48:45 -0800
Last modified on 2020-04-06 14:07:25 -0700
Version trunk
Hardware PC Linux
CC blitzrakete@gmail.com, dgregor@apple.com, erik.pilkington@gmail.com, hfinkel@anl.gov, llvm-bugs@lists.llvm.org, richard-llvm@metafoo.co.uk
Fixed by commit(s)
Attachments
Blocks
Blocked by
See also

Hi, the test.c

include

void foo( ) { while(1){} //do {}while(1); //crash //for(;;); //crash while(-1){} // non-executed loop, but still crash } int main () { int size = 100; printf("size = %d\n", size); foo(); return 0; }

is a simple function call code. When I compiler is using clang-6.0 -O1 test.c (or O2, O3, and Os) and then ./a.out, the output is

size=100 size=100 Segmentation fault(code dumped).

The strange thing is that printf function executed twice and whatever loop I use in the function foo(), even a non-executed loop, it gets faults.

Quuxplusone commented 4 years ago

Hi, is this a bug or not?

Quuxplusone commented 4 years ago
[This still reproduces in trunk, though I suspect the mechanism by which we get
to the bad result is probably different these days.]

This is formally a bug in C, but not in C++: in C++, infinite loops with no
side-effects are undefined behavior unconditionally, whereas in C they're valid
if the loop condition is a constant expression.

In any case, I don't think that's relevant to what's happening to this code,
and the transform that LLVM is performing does appear to be incorrect.

What's happening is:

1) The "return 0" in main is replaced by "unreachable" because foo() is deduced
to be noreturn.

2) CSE removes the call to foo (presumably because foo() is annotated as
readnone and its result is unused):

*** IR Dump Before Early CSE w/ MemorySSA ***
; Function Attrs: nofree noreturn nounwind uwtable
define dso_local i32 @main() local_unnamed_addr #1 {
entry:
  %call = call i32 (i8*, ...) @printf(i8* nonnull dereferenceable(1) getelementptr inbounds ([11 x i8], [11 x i8]* @.str, i64 0, i64 0), i32 100)
  call void @foo()
  unreachable
}
*** IR Dump After Early CSE w/ MemorySSA ***
; Function Attrs: nofree noreturn nounwind uwtable
define dso_local i32 @main() local_unnamed_addr #1 {
entry:
  %call = call i32 (i8*, ...) @printf(i8* nonnull dereferenceable(1) getelementptr inbounds ([11 x i8], [11 x i8]* @.str, i64 0, i64 0), i32 100)
  unreachable
}

I believe step (1) is correct, but step (2) is a bug -- it results in a
miscompilation of nonterminating programs, and would only be correct if foo()
were annotated 'willreturn'.

If there's an annotation that Clang needs to emit to prevent this
transformation being applied in C to 'foo', that's certainly something we can
do, but on the face of it this appears to be an optimizer bug.