+// ARM and MIPS do not have an implementation
+// of replaceMachineCodeForFunction(), so recompileAndRelinkFunction
+// doesn't work.
+#if !defined(__arm__) && !defined(__mips__)
+TEST_F(JITTest, FunctionIsRecompiledAndRelinked) {
+ Function *F = Function::Create(TypeBuilder<int(void), false>::get(Context),
+ GlobalValue::ExternalLinkage, "test", M);
+ BasicBlock *Entry = BasicBlock::Create(Context, "entry", F);
+ IRBuilder<> Builder(Entry);
+ Value *Val = ConstantInt::get(TypeBuilder<int, false>::get(Context), 1);
+ Builder.CreateRet(Val);
+
+ TheJIT->DisableLazyCompilation(true);
+ // Compile the function once, and make sure it works.
+ int (*OrigFPtr)() = reinterpret_cast<int(*)()>(
+ (intptr_t)TheJIT->recompileAndRelinkFunction(F));
+ EXPECT_EQ(1, OrigFPtr());
+
+ // Now change the function to return a different value.
+ Entry->eraseFromParent();
+ BasicBlock *NewEntry = BasicBlock::Create(Context, "new_entry", F);
+ Builder.SetInsertPoint(NewEntry);
+ Val = ConstantInt::get(TypeBuilder<int, false>::get(Context), 2);
+ Builder.CreateRet(Val);
+ // Recompile it, which should produce a new function pointer _and_ update the
+ // old one.
+ int (*NewFPtr)() = reinterpret_cast<int(*)()>(
+ (intptr_t)TheJIT->recompileAndRelinkFunction(F));
+
+ EXPECT_EQ(2, NewFPtr())
+ << "The new pointer should call the new version of the function";
+ EXPECT_EQ(2, OrigFPtr())
+ << "The old pointer's target should now jump to the new version";
+}
+#endif // !defined(__arm__)
+
+} // anonymous namespace
+// This variable is intentionally defined differently in the statically-compiled
+// program from the IR input to the JIT to assert that the JIT doesn't use its
+// definition.
+extern "C" int32_t JITTest_AvailableExternallyGlobal;
+int32_t JITTest_AvailableExternallyGlobal = 42;
+namespace {
+
+TEST_F(JITTest, AvailableExternallyGlobalIsntEmitted) {
+ TheJIT->DisableLazyCompilation(true);
+ LoadAssembly("@JITTest_AvailableExternallyGlobal = "
+ " available_externally global i32 7 "
+ " "
+ "define i32 @loader() { "
+ " %result = load i32* @JITTest_AvailableExternallyGlobal "
+ " ret i32 %result "
+ "} ");
+ Function *loaderIR = M->getFunction("loader");
+
+ int32_t (*loader)() = reinterpret_cast<int32_t(*)()>(
+ (intptr_t)TheJIT->getPointerToFunction(loaderIR));
+ EXPECT_EQ(42, loader()) << "func should return 42 from the external global,"
+ << " not 7 from the IR version.";
+}
+
+} // anonymous namespace
+// This function is intentionally defined differently in the statically-compiled
+// program from the IR input to the JIT to assert that the JIT doesn't use its
+// definition.
+extern "C" int32_t JITTest_AvailableExternallyFunction() {
+ return 42;
+}
+namespace {
+
+// ARM tests disabled pending fix for PR10783.
+#if !defined(__arm__)
+TEST_F(JITTest, AvailableExternallyFunctionIsntCompiled) {
+ TheJIT->DisableLazyCompilation(true);
+ LoadAssembly("define available_externally i32 "
+ " @JITTest_AvailableExternallyFunction() { "
+ " ret i32 7 "
+ "} "
+ " "
+ "define i32 @func() { "
+ " %result = tail call i32 "
+ " @JITTest_AvailableExternallyFunction() "
+ " ret i32 %result "
+ "} ");
+ Function *funcIR = M->getFunction("func");
+
+ int32_t (*func)() = reinterpret_cast<int32_t(*)()>(
+ (intptr_t)TheJIT->getPointerToFunction(funcIR));
+ EXPECT_EQ(42, func()) << "func should return 42 from the static version,"
+ << " not 7 from the IR version.";
+}
+
+TEST_F(JITTest, EscapedLazyStubStillCallable) {
+ TheJIT->DisableLazyCompilation(false);
+ LoadAssembly("define internal i32 @stubbed() { "
+ " ret i32 42 "
+ "} "
+ " "
+ "define i32()* @get_stub() { "
+ " ret i32()* @stubbed "
+ "} ");
+ typedef int32_t(*StubTy)();
+
+ // Call get_stub() to get the address of @stubbed without actually JITting it.
+ Function *get_stubIR = M->getFunction("get_stub");
+ StubTy (*get_stub)() = reinterpret_cast<StubTy(*)()>(
+ (intptr_t)TheJIT->getPointerToFunction(get_stubIR));
+ StubTy stubbed = get_stub();
+ // Now get_stubIR is the only reference to stubbed's stub.
+ get_stubIR->eraseFromParent();
+ // Now there are no references inside the JIT, but we've got a pointer outside
+ // it. The stub should be callable and return the right value.
+ EXPECT_EQ(42, stubbed());
+}
+
+// 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 Module (owned by the ExecutionEngine) is returned in
+// M. 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, Module *&M) {
+ // c_str() is null-terminated like MemoryBuffer::getMemBuffer requires.
+ MemoryBuffer *BitcodeBuffer =
+ MemoryBuffer::getMemBuffer(Bitcode, "Bitcode for test");
+ std::string errMsg;
+ M = getLazyBitcodeModule(BitcodeBuffer, Context, &errMsg);
+ if (M == NULL) {
+ ADD_FAILURE() << errMsg;
+ delete BitcodeBuffer;
+ return NULL;
+ }
+ ExecutionEngine *TheJIT = EngineBuilder(M)
+ .setEngineKind(EngineKind::JIT)
+ .setErrorStr(&errMsg)
+ .create();
+ if (TheJIT == NULL) {
+ ADD_FAILURE() << errMsg;
+ delete M;
+ M = NULL;
+ return NULL;
+ }
+ return TheJIT;
+}
+
+TEST(LazyLoadedJITTest, MaterializableAvailableExternallyFunctionIsntCompiled) {
+ LLVMContext Context;
+ const std::string Bitcode =
+ AssembleToBitcode(Context,
+ "define available_externally i32 "
+ " @JITTest_AvailableExternallyFunction() { "
+ " ret i32 7 "
+ "} "
+ " "
+ "define i32 @func() { "
+ " %result = tail call i32 "
+ " @JITTest_AvailableExternallyFunction() "
+ " ret i32 %result "
+ "} ");
+ ASSERT_FALSE(Bitcode.empty()) << "Assembling failed";
+ Module *M;
+ OwningPtr<ExecutionEngine> TheJIT(getJITFromBitcode(Context, Bitcode, M));
+ ASSERT_TRUE(TheJIT.get()) << "Failed to create JIT.";
+ TheJIT->DisableLazyCompilation(true);
+
+ Function *funcIR = M->getFunction("func");
+ Function *availableFunctionIR =
+ M->getFunction("JITTest_AvailableExternallyFunction");
+
+ // Double-check that the available_externally function is still unmaterialized
+ // when getPointerToFunction needs to find out if it's available_externally.
+ EXPECT_TRUE(availableFunctionIR->isMaterializable());
+
+ int32_t (*func)() = reinterpret_cast<int32_t(*)()>(
+ (intptr_t)TheJIT->getPointerToFunction(funcIR));
+ EXPECT_EQ(42, func()) << "func should return 42 from the static version,"
+ << " not 7 from the IR version.";
+}
+
+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";
+ Module *M;
+ OwningPtr<ExecutionEngine> TheJIT(getJITFromBitcode(Context, Bitcode, M));
+ ASSERT_TRUE(TheJIT.get()) << "Failed to create JIT.";
+ TheJIT->DisableLazyCompilation(true);
+
+ Function *recur1IR = M->getFunction("recur1");
+ Function *recur2IR = M->getFunction("recur2");
+ EXPECT_TRUE(recur1IR->isMaterializable());
+ EXPECT_TRUE(recur2IR->isMaterializable());
+
+ int32_t (*recur1)(int32_t) = reinterpret_cast<int32_t(*)(int32_t)>(
+ (intptr_t)TheJIT->getPointerToFunction(recur1IR));
+ EXPECT_EQ(3, recur1(4));
+}
+#endif // !defined(__arm__)
+