static JIT *TheJIT = 0;
+// A declaration may stop being a declaration once it's fully read from bitcode.
+// This function returns true if F is fully read and is still a declaration.
+static bool isNonGhostDeclaration(const Function *F) {
+ return F->isDeclaration() && !F->hasNotBeenReadFromBitcode();
+}
+
//===----------------------------------------------------------------------===//
// JIT lazy compilation code.
//
void *Actual = TheJIT->isCompilingLazily()
? (void *)(intptr_t)LazyResolverFn : (void *)0;
- // TODO: Delete this when PR5737 is fixed.
- std::string ErrorMsg;
- if (TheJIT->materializeFunction(F, &ErrorMsg)) {
- llvm_report_error("Error reading function '" + F->getName()+
- "' from bitcode file: " + ErrorMsg);
- }
// If this is an external declaration, attempt to resolve the address now
// to place in the stub.
- if (F->isDeclaration() || F->hasAvailableExternallyLinkage()) {
+ if (isNonGhostDeclaration(F) || F->hasAvailableExternallyLinkage()) {
Actual = TheJIT->getPointerToFunction(F);
// If we resolved the symbol to a null address (eg. a weak external)
// exist yet, add it to the JIT's work list so that we can fill in the stub
// address later.
if (!Actual && !TheJIT->isCompilingLazily())
- if (!F->isDeclaration() && !F->hasAvailableExternallyLinkage())
+ if (!isNonGhostDeclaration(F) && !F->hasAvailableExternallyLinkage())
TheJIT->addPendingFunction(F);
return Stub;
void *ResultPtr = TheJIT->getPointerToGlobalIfAvailable(F);
if (ResultPtr) return ResultPtr;
- // TODO: Delete this when PR5737 is fixed.
- std::string ErrorMsg;
- if (TheJIT->materializeFunction(F, &ErrorMsg)) {
- llvm_report_error("Error reading function '" + F->getName()+
- "' from bitcode file: " + ErrorMsg);
- }
-
// If this is an external function pointer, we can force the JIT to
// 'compile' it, which really just adds it to the map.
- if (F->isDeclaration() || F->hasAvailableExternallyLinkage())
+ if (isNonGhostDeclaration(F) || F->hasAvailableExternallyLinkage())
return TheJIT->getPointerToFunction(F);
}
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/Assembly/Parser.h"
#include "llvm/BasicBlock.h"
+#include "llvm/Bitcode/ReaderWriter.h"
#include "llvm/Constant.h"
#include "llvm/Constants.h"
#include "llvm/DerivedTypes.h"
#include "llvm/Module.h"
#include "llvm/ModuleProvider.h"
#include "llvm/Support/IRBuilder.h"
+#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/TypeBuilder.h"
#include "llvm/Target/TargetSelect.h"
}
};
+bool LoadAssemblyInto(Module *M, const char *assembly) {
+ SMDiagnostic Error;
+ bool success =
+ NULL != ParseAssemblyString(assembly, M, Error, M->getContext());
+ std::string errMsg;
+ raw_string_ostream os(errMsg);
+ Error.Print("", os);
+ EXPECT_TRUE(success) << os.str();
+ return success;
+}
+
class JITTest : public testing::Test {
protected:
virtual void SetUp() {
}
void LoadAssembly(const char *assembly) {
- SMDiagnostic Error;
- bool success = NULL != ParseAssemblyString(assembly, M, Error, Context);
- std::string errMsg;
- raw_string_ostream os(errMsg);
- Error.Print("", os);
- ASSERT_TRUE(success) << os.str();
+ LoadAssemblyInto(M, assembly);
}
LLVMContext Context;
<< " not 7 from the IR version.";
}
+// Converts the LLVM assembly to bitcode and returns it in a std::string. An
+// empty string indicates an error.
+std::string AssembleToBitcode(LLVMContext &Context, const char *Assembly) {
+ Module TempModule("TempModule", Context);
+ if (!LoadAssemblyInto(&TempModule, Assembly)) {
+ return "";
+ }
+
+ std::string Result;
+ raw_string_ostream OS(Result);
+ WriteBitcodeToFile(&TempModule, OS);
+ OS.flush();
+ return Result;
+}
+
+// Returns a newly-created ExecutionEngine that reads the bitcode in 'Bitcode'
+// lazily. The associated ModuleProvider (owned by the ExecutionEngine) is
+// returned in MP. Both will be NULL on an error. Bitcode must live at least
+// as long as the ExecutionEngine.
+ExecutionEngine *getJITFromBitcode(
+ LLVMContext &Context, const std::string &Bitcode, ModuleProvider *&MP) {
+ // c_str() is null-terminated like MemoryBuffer::getMemBuffer requires.
+ MemoryBuffer *BitcodeBuffer =
+ MemoryBuffer::getMemBuffer(Bitcode.c_str(),
+ Bitcode.c_str() + Bitcode.size(),
+ "Bitcode for test");
+ std::string errMsg;
+ MP = getBitcodeModuleProvider(BitcodeBuffer, Context, &errMsg);
+ if (MP == NULL) {
+ ADD_FAILURE() << errMsg;
+ delete BitcodeBuffer;
+ return NULL;
+ }
+ ExecutionEngine *TheJIT = EngineBuilder(MP)
+ .setEngineKind(EngineKind::JIT)
+ .setErrorStr(&errMsg)
+ .create();
+ if (TheJIT == NULL) {
+ ADD_FAILURE() << errMsg;
+ delete MP;
+ MP = NULL;
+ return NULL;
+ }
+ return TheJIT;
+}
+
+TEST(LazyLoadedJITTest, EagerCompiledRecursionThroughGhost) {
+ LLVMContext Context;
+ const std::string Bitcode =
+ AssembleToBitcode(Context,
+ "define i32 @recur1(i32 %a) { "
+ " %zero = icmp eq i32 %a, 0 "
+ " br i1 %zero, label %done, label %notdone "
+ "done: "
+ " ret i32 3 "
+ "notdone: "
+ " %am1 = sub i32 %a, 1 "
+ " %result = call i32 @recur2(i32 %am1) "
+ " ret i32 %result "
+ "} "
+ " "
+ "define i32 @recur2(i32 %b) { "
+ " %result = call i32 @recur1(i32 %b) "
+ " ret i32 %result "
+ "} ");
+ ASSERT_FALSE(Bitcode.empty()) << "Assembling failed";
+ ModuleProvider *MP;
+ OwningPtr<ExecutionEngine> TheJIT(getJITFromBitcode(Context, Bitcode, MP));
+ ASSERT_TRUE(TheJIT.get()) << "Failed to create JIT.";
+ TheJIT->DisableLazyCompilation(true);
+
+ Module *M = MP->getModule();
+ Function *recur1IR = M->getFunction("recur1");
+ Function *recur2IR = M->getFunction("recur2");
+ EXPECT_TRUE(recur1IR->hasNotBeenReadFromBitcode());
+ EXPECT_TRUE(recur2IR->hasNotBeenReadFromBitcode());
+
+ int32_t (*recur1)(int32_t) = reinterpret_cast<int32_t(*)(int32_t)>(
+ (intptr_t)TheJIT->getPointerToFunction(recur1IR));
+ EXPECT_EQ(3, recur1(4));
+}
+
// This code is copied from JITEventListenerTest, but it only runs once for all
// the tests in this directory. Everything seems fine, but that's strange
// behavior.