[LLVMdev] C infinite recursion mis-optimized?
Isaac Dupree
ml at isaac.cedarswampstudios.org
Sat Feb 27 21:54:10 PST 2010
I tried the LLVM demo with unmodified settings
http://llvm.org/demo/index.cgi
(same results from llvm 2.6 with clang, `clang-cc -emit-llvm -O2
test.c`, on my Linux x86_64)
For fun, I made a recursive function, but LLVM optimized it wrong (if
I'm understanding C standards correctly).
"void f() { f(); }"[see llvm-code in footnote 1]
was optimized to be equivalent to "void f() {}"[also 1]. I believe it
should either be equivalent in effect to "void f() { while(1){} }"[2],
which produces an infinite loop, or perhaps lead to a stack overflow (as
GCC might produce)... it's alright if the infinite loop is written using
tail-calls. All I expect is that it never actually return when you run it.
Adding a store to a global volatile variable fixes it, as does a puts("hi");
"volatile int g; void f() { g = 1; f(); }"[3], or even the version that
never actually gets to the volatile store at all,
"volatile int g; void f() { f(); g = 1; }"[4]
However, a local volatile variable gets quite strange treatment
"void f() { volatile int h; h = 1; f(); }"[5]
(it looks to me like one iteration of the function is inlined -- as I
guess happens in [3] and [4] -- but the recursive call deleted, as
happens in [1]). This is worse than just a non-terminating function
returning, as it also does not write to volatile stack variables the
requisite infinite number of times.
(I could not discover any obviously-*useful* code that gets
mis-optimized, luckily, although problems persist when I add function
arguments and return-values to the infinite recursive function.)
And here are the LLVM code emitted from each C code:
[1] "void f() { f(); }", or "void f() {}"
define void @f() nounwind readnone {
entry:
ret void
}
[2] "void f() { while(1){} }"
define void @f() nounwind readnone {
entry:
br label %bb
bb: ; preds = %bb, %entry
br label %bb
}
[3] "volatile int g; void f() { g = 1; f(); }"
@g = common global i32 0 ; <i32*> [#uses=2]
define void @f() nounwind {
entry:
volatile store i32 1, i32* @g, align 4
volatile store i32 1, i32* @g, align 4
tail call void @f() nounwind
ret void
}
[4] "volatile int g; void f() { f(); g = 1; }"
@g = common global i32 0 ; <i32*> [#uses=2]
define void @f() nounwind {
entry:
tail call void @f() nounwind
volatile store i32 1, i32* @g, align 4
volatile store i32 1, i32* @g, align 4
ret void
}
[5] "void f() { volatile int h; h = 1; f(); }"
define void @f() nounwind readnone {
entry:
%h.i = alloca i32, align 4 ; <i32*> [#uses=1]
%h = alloca i32, align 4 ; <i32*> [#uses=1]
volatile store i32 1, i32* %h, align 4
volatile store i32 1, i32* %h.i, align 4
ret void
}
More information about the llvm-dev
mailing list