X-Git-Url: http://demsky.eecs.uci.edu/git/?a=blobdiff_plain;f=unittests%2FExecutionEngine%2FJIT%2FJITTest.cpp;h=5e2af030f20f5b157712b3479f9518aba1f4d28e;hb=97eb05b39b456ab8f830395f027c150303b8f12c;hp=c6c26c7e8106954d336e9595e163f710e0c1f8e8;hpb=92fdf4537bd298673552d47adac2ff7bceab9dc9;p=oota-llvm.git diff --git a/unittests/ExecutionEngine/JIT/JITTest.cpp b/unittests/ExecutionEngine/JIT/JITTest.cpp index c6c26c7e810..5e2af030f20 100644 --- a/unittests/ExecutionEngine/JIT/JITTest.cpp +++ b/unittests/ExecutionEngine/JIT/JITTest.cpp @@ -7,28 +7,29 @@ // //===----------------------------------------------------------------------===// -#include "gtest/gtest.h" -#include "llvm/ADT/OwningPtr.h" -#include "llvm/ADT/SmallPtrSet.h" -#include "llvm/Assembly/Parser.h" #include "llvm/BasicBlock.h" #include "llvm/Constant.h" #include "llvm/Constants.h" #include "llvm/DerivedTypes.h" -#include "llvm/ExecutionEngine/JIT.h" -#include "llvm/ExecutionEngine/JITMemoryManager.h" #include "llvm/Function.h" #include "llvm/GlobalValue.h" #include "llvm/GlobalVariable.h" +#include "llvm/IRBuilder.h" #include "llvm/LLVMContext.h" #include "llvm/Module.h" -#include "llvm/ModuleProvider.h" -#include "llvm/Support/IRBuilder.h" -#include "llvm/Support/SourceMgr.h" -#include "llvm/Support/TypeBuilder.h" -#include "llvm/Target/TargetSelect.h" #include "llvm/Type.h" +#include "llvm/TypeBuilder.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/Assembly/Parser.h" +#include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/ExecutionEngine/JIT.h" +#include "llvm/ExecutionEngine/JITMemoryManager.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/TargetSelect.h" +#include "gtest/gtest.h" #include using namespace llvm; @@ -36,14 +37,14 @@ using namespace llvm; namespace { Function *makeReturnGlobal(std::string Name, GlobalVariable *G, Module *M) { - std::vector params; - const FunctionType *FTy = FunctionType::get(G->getType()->getElementType(), + std::vector params; + FunctionType *FTy = FunctionType::get(G->getType()->getElementType(), params, false); Function *F = Function::Create(FTy, GlobalValue::ExternalLinkage, Name, M); BasicBlock *Entry = BasicBlock::Create(M->getContext(), "entry", F); IRBuilder<> builder(Entry); Value *Load = builder.CreateLoad(G); - const Type *GTy = G->getType()->getElementType(); + Type *GTy = G->getType()->getElementType(); Value *Add = builder.CreateAdd(Load, ConstantInt::get(GTy, 1LL)); builder.CreateStore(Add, G); builder.CreateRet(Add); @@ -63,6 +64,10 @@ public: : Base(JITMemoryManager::CreateDefaultMemManager()) { stubsAllocated = 0; } + virtual void *getPointerToNamedFunction(const std::string &Name, + bool AbortOnFailure = true) { + return Base->getPointerToNamedFunction(Name, AbortOnFailure); + } virtual void setMemoryWritable() { Base->setMemoryWritable(); } virtual void setMemoryExecutable() { Base->setMemoryExecutable(); } @@ -112,6 +117,14 @@ public: EndFunctionBodyCall(F, FunctionStart, FunctionEnd)); Base->endFunctionBody(F, FunctionStart, FunctionEnd); } + virtual uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment, + unsigned SectionID) { + return Base->allocateDataSection(Size, Alignment, SectionID); + } + virtual uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment, + unsigned SectionID) { + return Base->allocateCodeSection(Size, Alignment, SectionID); + } virtual uint8_t *allocateSpace(intptr_t Size, unsigned Alignment) { return Base->allocateSpace(Size, Alignment); } @@ -177,36 +190,43 @@ public: } }; +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() { M = new Module("
", Context); - MP = new ExistingModuleProvider(M); RJMM = new RecordingJITMemoryManager; RJMM->setPoisonMemory(true); std::string Error; - TheJIT.reset(EngineBuilder(MP).setEngineKind(EngineKind::JIT) + TheJIT.reset(EngineBuilder(M).setEngineKind(EngineKind::JIT) .setJITMemoryManager(RJMM) .setErrorStr(&Error).create()); ASSERT_TRUE(TheJIT.get() != NULL) << Error; } 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; - Module *M; // Owned by MP. - ModuleProvider *MP; // Owned by ExecutionEngine. + Module *M; // Owned by ExecutionEngine. RecordingJITMemoryManager *RJMM; OwningPtr TheJIT; }; +// Tests on ARM disabled as we're running the old jit +#if !defined(__arm__) + // Regression test for a bug. The JIT used to allocate globals inside the same // memory block used for the function, and when the function code was freed, // the global was left in the same place. This test allocates a function @@ -215,14 +235,13 @@ class JITTest : public testing::Test { TEST(JIT, GlobalInFunction) { LLVMContext context; Module *M = new Module("
", context); - ExistingModuleProvider *MP = new ExistingModuleProvider(M); JITMemoryManager *MemMgr = JITMemoryManager::CreateDefaultMemManager(); // Tell the memory manager to poison freed memory so that accessing freed // memory is more easily tested. MemMgr->setPoisonMemory(true); std::string Error; - OwningPtr JIT(EngineBuilder(MP) + OwningPtr JIT(EngineBuilder(M) .setEngineKind(EngineKind::JIT) .setErrorStr(&Error) .setJITMemoryManager(MemMgr) @@ -232,7 +251,7 @@ TEST(JIT, GlobalInFunction) { ASSERT_EQ(Error, ""); // Create a global variable. - const Type *GTy = Type::getInt32Ty(context); + Type *GTy = Type::getInt32Ty(context); GlobalVariable *G = new GlobalVariable( *M, GTy, @@ -276,10 +295,14 @@ TEST(JIT, GlobalInFunction) { EXPECT_EQ(3, *GPtr); } +#endif // !defined(__arm__) + int PlusOne(int arg) { return arg + 1; } +// ARM tests disabled pending fix for PR10783. +#if !defined(__arm__) TEST_F(JITTest, FarCallToKnownFunction) { // x86-64 can only make direct calls to functions within 32 bits of // the current PC. To call anything farther away, we have to load @@ -316,11 +339,11 @@ TEST_F(JITTest, FarCallToKnownFunction) { TEST_F(JITTest, NonLazyCompilationStillNeedsStubs) { TheJIT->DisableLazyCompilation(true); - const FunctionType *Func1Ty = + FunctionType *Func1Ty = cast(TypeBuilder::get(Context)); - std::vector arg_types; + std::vector arg_types; arg_types.push_back(Type::getInt1Ty(Context)); - const FunctionType *FuncTy = FunctionType::get( + FunctionType *FuncTy = FunctionType::get( Type::getVoidTy(Context), arg_types, false); Function *Func1 = Function::Create(Func1Ty, Function::ExternalLinkage, "func1", M); @@ -373,7 +396,7 @@ TEST_F(JITTest, NonLazyLeaksNoStubs) { TheJIT->DisableLazyCompilation(true); // Create two functions with a single basic block each. - const FunctionType *FuncTy = + FunctionType *FuncTy = cast(TypeBuilder::get(Context)); Function *Func1 = Function::Create(FuncTy, Function::ExternalLinkage, "func1", M); @@ -420,7 +443,8 @@ TEST_F(JITTest, ModuleDeletion) { "} "); Function *func = M->getFunction("main"); TheJIT->getPointerToFunction(func); - TheJIT->deleteModuleProvider(MP); + TheJIT->removeModule(M); + delete M; SmallPtrSet FunctionsDeallocated; for (unsigned i = 0, e = RJMM->deallocateFunctionBodyCalls.size(); @@ -456,11 +480,13 @@ TEST_F(JITTest, ModuleDeletion) { EXPECT_EQ(RJMM->startExceptionTableCalls.size(), NumTablesDeallocated); } +#endif // !defined(__arm__) -// ARM and PPC still emit stubs for calls since the target may be too far away -// to call directly. This #if can probably be removed when +// ARM, MIPS and PPC still emit stubs for calls since the target may be +// too far away to call directly. This #if can probably be removed when // http://llvm.org/PR5201 is fixed. -#if !defined(__arm__) && !defined(__powerpc__) && !defined(__ppc__) +#if !defined(__arm__) && !defined(__mips__) && \ + !defined(__powerpc__) && !defined(__ppc__) typedef int (*FooPtr) (); TEST_F(JITTest, NoStubs) { @@ -500,6 +526,9 @@ TEST_F(JITTest, NoStubs) { } #endif // !ARM && !PPC +// Tests on ARM disabled as we're running the old jit +#if !defined(__arm__) + TEST_F(JITTest, FunctionPointersOutliveTheirCreator) { TheJIT->DisableLazyCompilation(true); LoadAssembly("define i8()* @get_foo_addr() { " @@ -534,6 +563,12 @@ TEST_F(JITTest, FunctionPointersOutliveTheirCreator) { #endif } +#endif //!defined(__arm__) + +// ARM does not have an implementation +// of replaceMachineCodeForFunction(), so recompileAndRelinkFunction +// doesn't work. +#if !defined(__arm__) TEST_F(JITTest, FunctionIsRecompiledAndRelinked) { Function *F = Function::Create(TypeBuilder::get(Context), GlobalValue::ExternalLinkage, "test", M); @@ -564,6 +599,7 @@ TEST_F(JITTest, FunctionIsRecompiledAndRelinked) { 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 @@ -573,6 +609,9 @@ extern "C" int32_t JITTest_AvailableExternallyGlobal; int32_t JITTest_AvailableExternallyGlobal = 42; namespace { +// Tests on ARM disabled as we're running the old jit +#if !defined(__arm__) + TEST_F(JITTest, AvailableExternallyGlobalIsntEmitted) { TheJIT->DisableLazyCompilation(true); LoadAssembly("@JITTest_AvailableExternallyGlobal = " @@ -589,7 +628,7 @@ TEST_F(JITTest, AvailableExternallyGlobalIsntEmitted) { EXPECT_EQ(42, loader()) << "func should return 42 from the external global," << " not 7 from the IR version."; } - +#endif //!defined(__arm__) } // 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 @@ -599,6 +638,8 @@ extern "C" int32_t JITTest_AvailableExternallyFunction() { } namespace { +// ARM tests disabled pending fix for PR10783. +#if !defined(__arm__) TEST_F(JITTest, AvailableExternallyFunctionIsntCompiled) { TheJIT->DisableLazyCompilation(true); LoadAssembly("define available_externally i32 " @@ -619,6 +660,143 @@ TEST_F(JITTest, AvailableExternallyFunctionIsntCompiled) { << " 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( + (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 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( + (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 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( + (intptr_t)TheJIT->getPointerToFunction(recur1IR)); + EXPECT_EQ(3, recur1(4)); +} +#endif // !defined(__arm__) + // 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.