X-Git-Url: http://demsky.eecs.uci.edu/git/?a=blobdiff_plain;f=unittests%2FExecutionEngine%2FMCJIT%2FMCJITTest.cpp;h=744bfdb4a01bc175923ec9ada297f19d524db842;hb=4e036ff575c54e01aea7f8028d282974480b071e;hp=92230f3f5e0bf884be77e6db2f351883c00659e5;hpb=cc7773bdcbecf893cb9442a77cdf5e41f2af4256;p=oota-llvm.git diff --git a/unittests/ExecutionEngine/MCJIT/MCJITTest.cpp b/unittests/ExecutionEngine/MCJIT/MCJITTest.cpp index 92230f3f5e0..744bfdb4a01 100644 --- a/unittests/ExecutionEngine/MCJIT/MCJITTest.cpp +++ b/unittests/ExecutionEngine/MCJIT/MCJITTest.cpp @@ -1,4 +1,4 @@ -//===- MCJITTest.cpp - Unit tests for the MCJIT ---------------------------===// +//===- MCJITTest.cpp - Unit tests for the MCJIT -----------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -12,25 +12,31 @@ // //===----------------------------------------------------------------------===// -#include "gtest/gtest.h" #include "llvm/ExecutionEngine/MCJIT.h" +#include "llvm/Support/DynamicLibrary.h" #include "MCJITTestBase.h" +#include "gtest/gtest.h" using namespace llvm; +namespace { + class MCJITTest : public testing::Test, public MCJITTestBase { protected: - - virtual void SetUp() { - M.reset(createEmptyModule("
")); - } + void SetUp() override { M.reset(createEmptyModule("
")); } }; -namespace { +// FIXME: Ensure creating an execution engine does not crash when constructed +// with a null module. +/* +TEST_F(MCJITTest, null_module) { + createJIT(0); +} +*/ // FIXME: In order to JIT an empty module, there needs to be // an interface to ExecutionEngine that forces compilation but -// does require retrieval of a pointer to a function/global. +// does not require retrieval of a pointer to a function/global. /* TEST_F(MCJITTest, empty_module) { createJIT(M.take()); @@ -44,11 +50,9 @@ TEST_F(MCJITTest, global_variable) { int initialValue = 5; GlobalValue *Global = insertGlobalInt32(M.get(), "test_global", initialValue); - createJIT(M.take()); + createJIT(std::move(M)); void *globalPtr = TheJIT->getPointerToGlobal(Global); - MM->applyPermissions(); - static_cast(MM)->invalidateInstructionCache(); - EXPECT_TRUE(0 != globalPtr) + EXPECT_TRUE(nullptr != globalPtr) << "Unable to get pointer to global value from JIT"; EXPECT_EQ(initialValue, *(int32_t*)globalPtr) @@ -59,17 +63,20 @@ TEST_F(MCJITTest, add_function) { SKIP_UNSUPPORTED_PLATFORM; Function *F = insertAddFunction(M.get()); - createJIT(M.take()); - void *addPtr = TheJIT->getPointerToFunction(F); - MM->applyPermissions(); - static_cast(MM)->invalidateInstructionCache(); + createJIT(std::move(M)); + uint64_t addPtr = TheJIT->getFunctionAddress(F->getName().str()); EXPECT_TRUE(0 != addPtr) << "Unable to get pointer to function from JIT"; - int (*AddPtrTy)(int, int) = (int(*)(int, int))(intptr_t)addPtr; - EXPECT_EQ(0, AddPtrTy(0, 0)); - EXPECT_EQ(3, AddPtrTy(1, 2)); - EXPECT_EQ(-5, AddPtrTy(-2, -3)); + ASSERT_TRUE(addPtr != 0) << "Unable to get pointer to function ."; + int (*AddPtr)(int, int) = (int(*)(int, int))addPtr ; + EXPECT_EQ(0, AddPtr(0, 0)); + EXPECT_EQ(1, AddPtr(1, 0)); + EXPECT_EQ(3, AddPtr(1, 2)); + EXPECT_EQ(-5, AddPtr(-2, -3)); + EXPECT_EQ(30, AddPtr(10, 20)); + EXPECT_EQ(-30, AddPtr(-10, -20)); + EXPECT_EQ(-40, AddPtr(-10, -30)); } TEST_F(MCJITTest, run_main) { @@ -77,14 +84,12 @@ TEST_F(MCJITTest, run_main) { int rc = 6; Function *Main = insertMainFunction(M.get(), 6); - createJIT(M.take()); - void *vPtr = TheJIT->getPointerToFunction(Main); - MM->applyPermissions(); - static_cast(MM)->invalidateInstructionCache(); - EXPECT_TRUE(0 != vPtr) + createJIT(std::move(M)); + uint64_t ptr = TheJIT->getFunctionAddress(Main->getName().str()); + EXPECT_TRUE(0 != ptr) << "Unable to get pointer to main() from JIT"; - int (*FuncPtr)(void) = (int(*)(void))(intptr_t)vPtr; + int (*FuncPtr)() = (int(*)())ptr; int returnCode = FuncPtr(); EXPECT_EQ(returnCode, rc); } @@ -100,13 +105,11 @@ TEST_F(MCJITTest, return_global) { Value *ReadGlobal = Builder.CreateLoad(GV); endFunctionWithRet(ReturnGlobal, ReadGlobal); - createJIT(M.take()); - void *rgvPtr = TheJIT->getPointerToFunction(ReturnGlobal); - MM->applyPermissions(); - static_cast(MM)->invalidateInstructionCache(); + createJIT(std::move(M)); + uint64_t rgvPtr = TheJIT->getFunctionAddress(ReturnGlobal->getName().str()); EXPECT_TRUE(0 != rgvPtr); - int32_t(*FuncPtr)(void) = (int32_t(*)(void))(intptr_t)rgvPtr; + int32_t(*FuncPtr)() = (int32_t(*)())rgvPtr; EXPECT_EQ(initialNum, FuncPtr()) << "Invalid value for global returned from JITted function"; } @@ -136,7 +139,7 @@ TEST_F(MCJITTest, increment_global) { void *gvPtr = TheJIT->getPointerToGlobal(GV); EXPECT_EQ(initialNum, *(int32_t*)gvPtr); - void *vPtr = TheJIT->getPointerToFunction(IncrementGlobal); + void *vPtr = TheJIT->getFunctionAddress(IncrementGlobal->getName().str()); EXPECT_TRUE(0 != vPtr) << "Unable to get pointer to main() from JIT"; @@ -150,6 +153,9 @@ TEST_F(MCJITTest, increment_global) { } */ +// PR16013: XFAIL this test on ARM, which currently can't handle multiple relocations. +#if !defined(__arm__) + TEST_F(MCJITTest, multiple_functions) { SKIP_UNSUPPORTED_PLATFORM; @@ -164,72 +170,115 @@ TEST_F(MCJITTest, multiple_functions) { std::stringstream funcName; funcName << "level_" << i; Outer = startFunction(M.get(), funcName.str()); - Value *innerResult = Builder.CreateCall(Inner); + Value *innerResult = Builder.CreateCall(Inner, {}); endFunctionWithRet(Outer, innerResult); Inner = Outer; } - createJIT(M.take()); - void *vPtr = TheJIT->getPointerToFunction(Outer); - MM->applyPermissions(); - static_cast(MM)->invalidateInstructionCache(); - EXPECT_TRUE(0 != vPtr) + createJIT(std::move(M)); + uint64_t ptr = TheJIT->getFunctionAddress(Outer->getName().str()); + EXPECT_TRUE(0 != ptr) << "Unable to get pointer to outer function from JIT"; - int32_t(*FuncPtr)(void) = (int32_t(*)(void))(intptr_t)vPtr; + int32_t(*FuncPtr)() = (int32_t(*)())ptr; EXPECT_EQ(innerRetVal, FuncPtr()) << "Incorrect result returned from function"; } -// FIXME: ExecutionEngine has no support empty modules -/* -TEST_F(MCJITTest, multiple_empty_modules) { - SKIP_UNSUPPORTED_PLATFORM; - - createJIT(M.take()); - // JIT-compile - EXPECT_NE(0, TheJIT->getObjectImage()) - << "Unable to generate executable loaded object image"; - - TheJIT->addModule(createEmptyModule("")); - TheJIT->addModule(createEmptyModule("")); +#endif /*!defined(__arm__)*/ - // JIT again - EXPECT_NE(0, TheJIT->getObjectImage()) - << "Unable to generate executable loaded object image"; -} -*/ - -// FIXME: MCJIT must support multiple modules -/* -TEST_F(MCJITTest, multiple_modules) { +TEST_F(MCJITTest, multiple_decl_lookups) { SKIP_UNSUPPORTED_PLATFORM; - Function *Callee = insertAddFunction(M.get()); - createJIT(M.take()); - - // caller function is defined in a different module - M.reset(createEmptyModule("")); - - Function *CalleeRef = insertExternalReferenceToFunction(M.get(), Callee); - Function *Caller = insertSimpleCallFunction(M.get(), CalleeRef); + Function *Foo = insertExternalReferenceToFunction(M.get(), "_exit"); + createJIT(std::move(M)); + void *A = TheJIT->getPointerToFunction(Foo); + void *B = TheJIT->getPointerToFunction(Foo); - TheJIT->addModule(M.take()); - - // get a function pointer in a module that was not used in EE construction - void *vPtr = TheJIT->getPointerToFunction(Caller); - EXPECT_NE(0, vPtr) - << "Unable to get pointer to caller function from JIT"; + EXPECT_TRUE(A != nullptr) << "Failed lookup - test not correctly configured."; + EXPECT_EQ(A, B) << "Repeat calls to getPointerToFunction fail."; +} - int(*FuncPtr)(int, int) = (int(*)(int, int))(intptr_t)vPtr; - EXPECT_EQ(0, FuncPtr(0, 0)); - EXPECT_EQ(30, FuncPtr(10, 20)); - EXPECT_EQ(-30, FuncPtr(-10, -20)); +typedef void * (*FunctionHandlerPtr)(const std::string &str); - // ensure caller is destroyed before callee (free use before def) - M.reset(); +TEST_F(MCJITTest, lazy_function_creator_pointer) { + SKIP_UNSUPPORTED_PLATFORM; + + Function *Foo = insertExternalReferenceToFunction(M.get(), + "\1Foo"); + startFunction(M.get(), "Parent"); + CallInst *Call = Builder.CreateCall(Foo, {}); + Builder.CreateRet(Call); + + createJIT(std::move(M)); + + // Set up the lazy function creator that records the name of the last + // unresolved external function found in the module. Using a function pointer + // prevents us from capturing local variables, which is why this is static. + static std::string UnresolvedExternal; + FunctionHandlerPtr UnresolvedHandler = [] (const std::string &str) { + // Try to resolve the function in the current process before marking it as + // unresolved. This solves an issue on ARM where '__aeabi_*' function names + // are passed to this handler. + void *symbol = + llvm::sys::DynamicLibrary::SearchForAddressOfSymbol(str.c_str()); + if (symbol) { + return symbol; + } + + UnresolvedExternal = str; + return (void *)(uintptr_t)-1; + }; + TheJIT->InstallLazyFunctionCreator(UnresolvedHandler); + + // JIT the module. + TheJIT->finalizeObject(); + + // Verify that our handler was called. + EXPECT_EQ(UnresolvedExternal, "Foo"); } -*/ +TEST_F(MCJITTest, lazy_function_creator_lambda) { + SKIP_UNSUPPORTED_PLATFORM; + + Function *Foo1 = insertExternalReferenceToFunction(M.get(), + "\1Foo1"); + Function *Foo2 = insertExternalReferenceToFunction(M.get(), + "\1Foo2"); + startFunction(M.get(), "Parent"); + CallInst *Call1 = Builder.CreateCall(Foo1, {}); + CallInst *Call2 = Builder.CreateCall(Foo2, {}); + Value *Result = Builder.CreateAdd(Call1, Call2); + Builder.CreateRet(Result); + + createJIT(std::move(M)); + + // Set up the lazy function creator that records the name of unresolved + // external functions in the module. + std::vector UnresolvedExternals; + auto UnresolvedHandler = [&UnresolvedExternals] (const std::string &str) { + // Try to resolve the function in the current process before marking it as + // unresolved. This solves an issue on ARM where '__aeabi_*' function names + // are passed to this handler. + void *symbol = + llvm::sys::DynamicLibrary::SearchForAddressOfSymbol(str.c_str()); + if (symbol) { + return symbol; + } + UnresolvedExternals.push_back(str); + return (void *)(uintptr_t)-1; + }; + TheJIT->InstallLazyFunctionCreator(UnresolvedHandler); + + // JIT the module. + TheJIT->finalizeObject(); + + // Verify that our handler was called for each unresolved function. + auto I = UnresolvedExternals.begin(), E = UnresolvedExternals.end(); + EXPECT_EQ(UnresolvedExternals.size(), 2u); + EXPECT_FALSE(std::find(I, E, "Foo1") == E); + EXPECT_FALSE(std::find(I, E, "Foo2") == E); } + +} // end anonymous namespace