HowToUseJIT: failed assertion on PPC/Mac OS X

I just got the CVS version of LLVM running tonight. On my PowerBook, one of the examples (HowToUseJIT) has an assertion error when I try and run it:

Running foo: JIT.cpp:217: failed assertion `!isAlreadyCodeGenerating && "Error: Recursive compilation detected!"'

However, when I compile and run the same program on x86 Linux, it runs fine (Running foo: Result: 11). I can run the Fibonacci example, and I can execute simple C programs compiled with llvm-gcc. I couldn't see anything that looked like a bug report for this issue in Bugzilla. I can't quite figure out this bug. The backtrace looks like:

#0 0x900429ac in kill ()
#1 0x9009eb1c in abort ()
#2 0x00120ec8 in __eprintf () at /Users/ejones/llvm/llvm/include/llvm/ADT/ilist:440
#3 0x00011ae8 in llvm::JIT::runJITOnFunction(llvm::Function*) (this=0x51015c0, F=0x5101010) at JIT.cpp:217
#4 0x00011da4 in llvm::JIT::getPointerToFunction(llvm::Function*) (this=0x51015c0, F=0x5101010) at JIT.cpp:261
#5 0x000123e4 in (anonymous namespace)::JITResolver::getFunctionStub(llvm::Function*) (this=0x70be78, F=0x5101010) at JITEmitter.cpp:184
#6 0x00012a48 in (anonymous namespace)::JITEmitter::getPointerToGlobal(llvm::GlobalValue*, void*, bool) (this=0x5101670, V=0x5101010, Reference=0x5685010, DoesntNeedStub=false) at JITEmitter.cpp:334
#7 0x00012ccc in (anonymous namespace)::JITEmitter::finishFunction(llvm::MachineFunction&) (this=0x5101670, F=@0x51038c0) at JITEmitter.cpp:355
#8 0x00033fec in (anonymous namespace)::PPC32CodeEmitter::runOnMachineFunction(llvm::MachineFunction&) (this=0x5101b50, MF=@0x51038c0) at PPC32CodeEmitter.cpp:95
#9 0x00254a7c in llvm::MachineFunctionPass::runOnFunction(llvm::Function&) (this=0x5101b50, F=@0x51012d0) at /Users/ejones/llvm/llvm/include/llvm/CodeGen/MachineFunctionPass.h:38
#10 0x00415ee8 in llvm::PassManagerTraits<llvm::Function>::runPass(llvm::FunctionPass*, llvm::Function*) (P=0x5101b50, F=0x51012d0) at PassManagerT.h:706
#11 0x0040834c in llvm::PassManagerT<llvm::Function>::runOnUnit(llvm::Function*) (this=0x5101610, M=0x51012d0) at PassManagerT.h:256
#12 0x00408ad0 in llvm::PassManagerTraits<llvm::Function>::runOnFunction(llvm::Function&) (this=0x5101610, F=@0x51012d0) at PassManagerT.h:810
#13 0x000ef23c in llvm::FunctionPass::run(llvm::Function&) (this=0x5101610, F=@0x51012d0) at Pass.cpp:261
#14 0x000ee370 in llvm::FunctionPassManager::run(llvm::Function&) (this=0x51015f0, F=@0x51012d0) at Pass.cpp:108
#15 0x00011b0c in llvm::JIT::runJITOnFunction(llvm::Function*) (this=0x51015c0, F=0x51012d0) at JIT.cpp:221
#16 0x00011da4 in llvm::JIT::getPointerToFunction(llvm::Function*) (this=0x51015c0, F=0x51012d0) at JIT.cpp:261
#17 0x00010b18 in llvm::JIT::runFunction(llvm::Function*, std::vector<llvm::GenericValue, std::allocator<llvm::GenericValue> > const&) (this=0x51015c0, F=0x51012d0, ArgValues=@0xbffffb90) at JIT.cpp:65
#18 0x00002e8c in main () at HowToUseJIT.cpp:104

I figured the problem must be some difference between PPC32CodeEmitter::runOnMachineFunction and X86CodeEmitter::runOnMachineFunction, but the code at the beginning of these two functions is identical.

I'm assuming that what happens is that on X86, "DoesntNeedStub" is set, so JITEmitter::getPointerToGlobal calls AddCallbackAtLocation instead of getFunctionStub. In JITResolver::getFunctionStub, the function that is being resolved is "add1", and it is marked as "Linkage = ExternalLinkage". The code uses F->hasExternalLinkage() to decide if the function is external or not. This returns true, so instead of emitting a stub, it calls TheJIT->getPointerToFunction.

In TheJIT->getPointerToFunction, it uses F->isExternal() to decide if it should run the JIT or not. This returns false, so it decides to run the JIT, which causes the assertion.

I can "fix" it by changing JITResolver::getFunctionStub to use F->isExternal() instead of F->hasExternalLinkage(). However, this then breaks when calling *real* external functions (native code). I've attached a patch for this anyway, in case that makes it easier for someone to understand. At this point, I am completely lost and out of ideas. Any comments?

Thanks,

Evan Jones

JITEmitter.patch (866 Bytes)

I obviously should not post to mailing lists before I've eaten. With my "fix" I get an assertion when taking an address of a function, not with native code:

#include <stdio.h>

int foo() {
         return 1;
}

int main() {
         printf( "%p\n", foo );
         return 0;
}

The backtrace is the same as before: a recursive JIT loop. In this case, F->isExternal() returns "true" because the function has not yet been loaded (?). A hack to fix both issues is to change the conditional to test both F->hasExternalLinkage() && F->isExternal().

I don't quite get what is going on here. From what I understand, external linkage means that the function is visible outside the current module? So a function declared in a C file would have external linkage, while a "static" function would have internal linkage. The GlobalValue::isExternal() method should return true if the global value is defined outside the current translation unit.

since it is defined in the current translation unit. However, it doesn't because none of the basic blocks for the body have not yet been loaded (?).

At any rate, I've attached a new patch which "works for me" on PPC and x86. At this point, I really don't know what is going on, so I really don't know if it is correct.

Evan Jones

JITEmitter.patch (920 Bytes)

I can "fix" it by changing JITResolver::getFunctionStub to use F->isExternal() instead of F->hasExternalLinkage(). However, this then breaks when calling *real* external functions (native code).

I obviously should not post to mailing lists before I've eaten. With my "fix" I get an assertion when taking an address of a function, not with native code:

FWIW I'm looking into this too, my darwin build is just out of date so it's taking a while. :slight_smile:

The backtrace is the same as before: a recursive JIT loop. In this case, F->isExternal() returns "true" because the function has not yet been loaded (?). A hack to fix both issues is to change the conditional to test both F->hasExternalLinkage() && F->isExternal().

I'm suspicious of two things. First the above conditional does make sense, but second, why does it only happen with the HowToUseTheJIT example, and not every program run by lli (for example). My guess is that the ExistingModuleProvider used by the HowToUseTheJIT example is doing something funny. :frowning:

I don't quite get what is going on here. From what I understand, external linkage means that the function is visible outside the current module? So a function declared in a C file would have external linkage, while a "static" function would have internal linkage. The GlobalValue::isExternal() method should return true if the global value is defined outside the current translation unit.

Yes, linkage determines whether something is accessible inside or outside the module, and isExternal() determines whether it is *defined* inside of outside the module. Note that only some combinations of these make sense.
:slight_smile:

From my understanding, "foo()"->isExternal() should return *false*, since it is defined in the current translation unit. However, it doesn't because none of the basic blocks for the body have not yet been loaded (?).

Yes, that is what doesn't make sense to me. I bet it's the ExistingModuleProvider or something else strange.

At any rate, I've attached a new patch which "works for me" on PPC and x86. At this point, I really don't know what is going on, so I really don't know if it is correct.

Thanks for taking a look at this, I'll get back to you in a few minutes. :slight_smile:

-Chris

I can "fix" it by changing JITResolver::getFunctionStub to use F->isExternal() instead of F->hasExternalLinkage(). However, this then breaks when calling *real* external functions (native code).

I obviously should not post to mailing lists before I've eaten. With my "fix" I get an assertion when taking an address of a function, not with native code:
The backtrace is the same as before: a recursive JIT loop. In this case, F->isExternal() returns "true" because the function has not yet been loaded (?). A hack to fix both issues is to change the conditional to test both F->hasExternalLinkage() && F->isExternal().

Yup, this is basically right. I've committed this patch to fix it:
http://mail.cs.uiuc.edu/pipermail/llvm-commits/Week-of-Mon-20050214/024196.html

At any rate, I've attached a new patch which "works for me" on PPC and x86. At this point, I really don't know what is going on, so I really don't know if it is correct.

You're right, and this is a general problem. After puzzling over it for a while, I realized that the llvm-test programs are all run after the -internalize pass has been run on them. This means that the only functions with external linkage are those that ARE external. The
HowToUseTheJIT example builds functions that have external linkage but aren't external.

Thanks for noticing this bug!! Please let us know if you run into anything else,

-Chris