diff options
Diffstat (limited to 'llvm/unittests/ExecutionEngine')
17 files changed, 2072 insertions, 3 deletions
diff --git a/llvm/unittests/ExecutionEngine/CMakeLists.txt b/llvm/unittests/ExecutionEngine/CMakeLists.txt index 783c9b51921..5e462220e5c 100644 --- a/llvm/unittests/ExecutionEngine/CMakeLists.txt +++ b/llvm/unittests/ExecutionEngine/CMakeLists.txt @@ -10,9 +10,10 @@ add_llvm_unittest(ExecutionEngineTests ExecutionEngineTest.cpp ) -# Include MCJIT tests only if native arch is a built JIT target. +# Include JIT/MCJIT tests only if native arch is a built JIT target. list(FIND LLVM_TARGETS_TO_BUILD "${LLVM_NATIVE_ARCH}" build_idx) list(FIND LLVM_TARGETS_WITH_JIT "${LLVM_NATIVE_ARCH}" jit_idx) if (NOT build_idx LESS 0 AND NOT jit_idx LESS 0) + add_subdirectory(JIT) add_subdirectory(MCJIT) endif() diff --git a/llvm/unittests/ExecutionEngine/JIT/CMakeLists.txt b/llvm/unittests/ExecutionEngine/JIT/CMakeLists.txt new file mode 100644 index 00000000000..5ace1c6ee48 --- /dev/null +++ b/llvm/unittests/ExecutionEngine/JIT/CMakeLists.txt @@ -0,0 +1,65 @@ +set(LLVM_LINK_COMPONENTS + AsmParser + BitReader + BitWriter + Core + ExecutionEngine + JIT + MC + Support + nativecodegen + ) + +# HACK: Declare a couple of source files as optionally compiled to satisfy the +# missing-file-checker in LLVM's weird CMake build. +set(LLVM_OPTIONAL_SOURCES + IntelJITEventListenerTest.cpp + OProfileJITEventListenerTest.cpp + ) + +if( LLVM_USE_INTEL_JITEVENTS ) + set(ProfileTestSources + IntelJITEventListenerTest.cpp + ) + set(LLVM_LINK_COMPONENTS + ${LLVM_LINK_COMPONENTS} + DebugInfo + IntelJITEvents + Object + ) +endif( LLVM_USE_INTEL_JITEVENTS ) + +if( LLVM_USE_OPROFILE ) + set(ProfileTestSources + ${ProfileTestSources} + OProfileJITEventListenerTest.cpp + ) + set(LLVM_LINK_COMPONENTS + ${LLVM_LINK_COMPONENTS} + OProfileJIT + ) +endif( LLVM_USE_OPROFILE ) + +set(JITTestsSources + JITEventListenerTest.cpp + JITMemoryManagerTest.cpp + JITTest.cpp + MultiJITTest.cpp + ${ProfileTestSources} + ) + +if(MSVC) + list(APPEND JITTestsSources JITTests.def) +endif() + +# The JIT tests need to dlopen things. +set(LLVM_NO_DEAD_STRIP 1) + +add_llvm_unittest(JITTests + ${JITTestsSources} + ) + +if(MINGW OR CYGWIN) + set_property(TARGET JITTests PROPERTY LINK_FLAGS -Wl,--export-all-symbols) +endif() +set_target_properties(JITTests PROPERTIES ENABLE_EXPORTS 1) diff --git a/llvm/unittests/ExecutionEngine/JIT/IntelJITEventListenerTest.cpp b/llvm/unittests/ExecutionEngine/JIT/IntelJITEventListenerTest.cpp new file mode 100644 index 00000000000..db9088777a9 --- /dev/null +++ b/llvm/unittests/ExecutionEngine/JIT/IntelJITEventListenerTest.cpp @@ -0,0 +1,113 @@ +//===- JITEventListenerTest.cpp - Tests for Intel JITEventListener --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "JITEventListenerTestCommon.h" + +using namespace llvm; + +// Because we want to keep the implementation details of the Intel API used to +// communicate with Amplifier out of the public header files, the header below +// is included from the source tree instead. +#include "../../../lib/ExecutionEngine/IntelJITEvents/IntelJITEventsWrapper.h" + +#include <map> +#include <list> + +namespace { + +// map of function ("method") IDs to source locations +NativeCodeMap ReportedDebugFuncs; + +} // namespace + +/// Mock implementaion of Intel JIT API jitprofiling library +namespace test_jitprofiling { + +int NotifyEvent(iJIT_JVM_EVENT EventType, void *EventSpecificData) { + switch (EventType) { + case iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED: { + EXPECT_TRUE(0 != EventSpecificData); + iJIT_Method_Load* msg = static_cast<iJIT_Method_Load*>(EventSpecificData); + + ReportedDebugFuncs[msg->method_id]; + + for(unsigned int i = 0; i < msg->line_number_size; ++i) { + EXPECT_TRUE(0 != msg->line_number_table); + std::pair<std::string, unsigned int> loc( + std::string(msg->source_file_name), + msg->line_number_table[i].LineNumber); + ReportedDebugFuncs[msg->method_id].push_back(loc); + } + } + break; + case iJVM_EVENT_TYPE_METHOD_UNLOAD_START: { + EXPECT_TRUE(0 != EventSpecificData); + unsigned int UnloadId + = *reinterpret_cast<unsigned int*>(EventSpecificData); + EXPECT_TRUE(1 == ReportedDebugFuncs.erase(UnloadId)); + } + default: + break; + } + return 0; +} + +iJIT_IsProfilingActiveFlags IsProfilingActive(void) { + // for testing, pretend we have an Intel Parallel Amplifier XE 2011 + // instance attached + return iJIT_SAMPLING_ON; +} + +unsigned int GetNewMethodID(void) { + static unsigned int id = 0; + return ++id; +} + +} //namespace test_jitprofiling + +class IntelJITEventListenerTest + : public JITEventListenerTestBase<IntelJITEventsWrapper> { +public: + IntelJITEventListenerTest() + : JITEventListenerTestBase<IntelJITEventsWrapper>( + new IntelJITEventsWrapper(test_jitprofiling::NotifyEvent, 0, + test_jitprofiling::IsProfilingActive, 0, 0, + test_jitprofiling::GetNewMethodID)) + { + EXPECT_TRUE(0 != MockWrapper); + + Listener.reset(JITEventListener::createIntelJITEventListener( + MockWrapper.release())); + EXPECT_TRUE(0 != Listener); + EE->RegisterJITEventListener(Listener.get()); + } +}; + +TEST_F(IntelJITEventListenerTest, NoDebugInfo) { + TestNoDebugInfo(ReportedDebugFuncs); +} + +TEST_F(IntelJITEventListenerTest, SingleLine) { + TestSingleLine(ReportedDebugFuncs); +} + +TEST_F(IntelJITEventListenerTest, MultipleLines) { + TestMultipleLines(ReportedDebugFuncs); +} + +// This testcase is disabled because the Intel JIT API does not support a single +// JITted function with source lines associated with multiple files +/* +TEST_F(IntelJITEventListenerTest, MultipleFiles) { + TestMultipleFiles(ReportedDebugFuncs); +} +*/ + +testing::Environment* const jit_env = + testing::AddGlobalTestEnvironment(new JITEnvironment); diff --git a/llvm/unittests/ExecutionEngine/JIT/JITEventListenerTest.cpp b/llvm/unittests/ExecutionEngine/JIT/JITEventListenerTest.cpp new file mode 100644 index 00000000000..175b9fb107b --- /dev/null +++ b/llvm/unittests/ExecutionEngine/JIT/JITEventListenerTest.cpp @@ -0,0 +1,237 @@ +//===- JITEventListenerTest.cpp - Unit tests for JITEventListeners --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ExecutionEngine/JITEventListener.h" +#include "llvm/CodeGen/MachineCodeInfo.h" +#include "llvm/ExecutionEngine/JIT.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/TypeBuilder.h" +#include "llvm/Support/TargetSelect.h" +#include "gtest/gtest.h" +#include <vector> + +using namespace llvm; + +namespace { + +struct FunctionEmittedEvent { + // Indices are local to the RecordingJITEventListener, since the + // JITEventListener interface makes no guarantees about the order of + // calls between Listeners. + unsigned Index; + const Function *F; + void *Code; + size_t Size; + JITEvent_EmittedFunctionDetails Details; +}; +struct FunctionFreedEvent { + unsigned Index; + void *Code; +}; + +struct RecordingJITEventListener : public JITEventListener { + std::vector<FunctionEmittedEvent> EmittedEvents; + std::vector<FunctionFreedEvent> FreedEvents; + + unsigned NextIndex; + + RecordingJITEventListener() : NextIndex(0) {} + + virtual void NotifyFunctionEmitted(const Function &F, + void *Code, size_t Size, + const EmittedFunctionDetails &Details) { + FunctionEmittedEvent Event = {NextIndex++, &F, Code, Size, Details}; + EmittedEvents.push_back(Event); + } + + virtual void NotifyFreeingMachineCode(void *OldPtr) { + FunctionFreedEvent Event = {NextIndex++, OldPtr}; + FreedEvents.push_back(Event); + } +}; + +class JITEventListenerTest : public testing::Test { + protected: + JITEventListenerTest() + : M(new Module("module", getGlobalContext())), + EE(EngineBuilder(M) + .setEngineKind(EngineKind::JIT) + .create()) { + } + + Module *M; + const std::unique_ptr<ExecutionEngine> EE; +}; + +// Tests on SystemZ disabled as we're running the old JIT +#if !defined(__s390__) && !defined(__aarch64__) +Function *buildFunction(Module *M) { + Function *Result = Function::Create( + TypeBuilder<int32_t(int32_t), false>::get(getGlobalContext()), + GlobalValue::ExternalLinkage, "id", M); + Value *Arg = Result->arg_begin(); + BasicBlock *BB = BasicBlock::Create(M->getContext(), "entry", Result); + ReturnInst::Create(M->getContext(), Arg, BB); + return Result; +} + +// Tests that a single JITEventListener follows JIT events accurately. +TEST_F(JITEventListenerTest, Simple) { + RecordingJITEventListener Listener; + EE->RegisterJITEventListener(&Listener); + Function *F1 = buildFunction(M); + Function *F2 = buildFunction(M); + + void *F1_addr = EE->getPointerToFunction(F1); + void *F2_addr = EE->getPointerToFunction(F2); + EE->getPointerToFunction(F1); // Should do nothing. + EE->freeMachineCodeForFunction(F1); + EE->freeMachineCodeForFunction(F2); + + ASSERT_EQ(2U, Listener.EmittedEvents.size()); + ASSERT_EQ(2U, Listener.FreedEvents.size()); + + EXPECT_EQ(0U, Listener.EmittedEvents[0].Index); + EXPECT_EQ(F1, Listener.EmittedEvents[0].F); + EXPECT_EQ(F1_addr, Listener.EmittedEvents[0].Code); + EXPECT_LT(0U, Listener.EmittedEvents[0].Size) + << "We don't know how big the function will be, but it had better" + << " contain some bytes."; + + EXPECT_EQ(1U, Listener.EmittedEvents[1].Index); + EXPECT_EQ(F2, Listener.EmittedEvents[1].F); + EXPECT_EQ(F2_addr, Listener.EmittedEvents[1].Code); + EXPECT_LT(0U, Listener.EmittedEvents[1].Size) + << "We don't know how big the function will be, but it had better" + << " contain some bytes."; + + EXPECT_EQ(2U, Listener.FreedEvents[0].Index); + EXPECT_EQ(F1_addr, Listener.FreedEvents[0].Code); + + EXPECT_EQ(3U, Listener.FreedEvents[1].Index); + EXPECT_EQ(F2_addr, Listener.FreedEvents[1].Code); + + F1->eraseFromParent(); + F2->eraseFromParent(); +} + +// Tests that a single JITEventListener follows JIT events accurately. +TEST_F(JITEventListenerTest, MultipleListenersDontInterfere) { + RecordingJITEventListener Listener1; + RecordingJITEventListener Listener2; + RecordingJITEventListener Listener3; + Function *F1 = buildFunction(M); + Function *F2 = buildFunction(M); + + EE->RegisterJITEventListener(&Listener1); + EE->RegisterJITEventListener(&Listener2); + void *F1_addr = EE->getPointerToFunction(F1); + EE->RegisterJITEventListener(&Listener3); + EE->UnregisterJITEventListener(&Listener1); + void *F2_addr = EE->getPointerToFunction(F2); + EE->UnregisterJITEventListener(&Listener2); + EE->UnregisterJITEventListener(&Listener3); + EE->freeMachineCodeForFunction(F1); + EE->RegisterJITEventListener(&Listener2); + EE->RegisterJITEventListener(&Listener3); + EE->RegisterJITEventListener(&Listener1); + EE->freeMachineCodeForFunction(F2); + EE->UnregisterJITEventListener(&Listener1); + EE->UnregisterJITEventListener(&Listener2); + EE->UnregisterJITEventListener(&Listener3); + + // Listener 1. + ASSERT_EQ(1U, Listener1.EmittedEvents.size()); + ASSERT_EQ(1U, Listener1.FreedEvents.size()); + + EXPECT_EQ(0U, Listener1.EmittedEvents[0].Index); + EXPECT_EQ(F1, Listener1.EmittedEvents[0].F); + EXPECT_EQ(F1_addr, Listener1.EmittedEvents[0].Code); + EXPECT_LT(0U, Listener1.EmittedEvents[0].Size) + << "We don't know how big the function will be, but it had better" + << " contain some bytes."; + + EXPECT_EQ(1U, Listener1.FreedEvents[0].Index); + EXPECT_EQ(F2_addr, Listener1.FreedEvents[0].Code); + + // Listener 2. + ASSERT_EQ(2U, Listener2.EmittedEvents.size()); + ASSERT_EQ(1U, Listener2.FreedEvents.size()); + + EXPECT_EQ(0U, Listener2.EmittedEvents[0].Index); + EXPECT_EQ(F1, Listener2.EmittedEvents[0].F); + EXPECT_EQ(F1_addr, Listener2.EmittedEvents[0].Code); + EXPECT_LT(0U, Listener2.EmittedEvents[0].Size) + << "We don't know how big the function will be, but it had better" + << " contain some bytes."; + + EXPECT_EQ(1U, Listener2.EmittedEvents[1].Index); + EXPECT_EQ(F2, Listener2.EmittedEvents[1].F); + EXPECT_EQ(F2_addr, Listener2.EmittedEvents[1].Code); + EXPECT_LT(0U, Listener2.EmittedEvents[1].Size) + << "We don't know how big the function will be, but it had better" + << " contain some bytes."; + + EXPECT_EQ(2U, Listener2.FreedEvents[0].Index); + EXPECT_EQ(F2_addr, Listener2.FreedEvents[0].Code); + + // Listener 3. + ASSERT_EQ(1U, Listener3.EmittedEvents.size()); + ASSERT_EQ(1U, Listener3.FreedEvents.size()); + + EXPECT_EQ(0U, Listener3.EmittedEvents[0].Index); + EXPECT_EQ(F2, Listener3.EmittedEvents[0].F); + EXPECT_EQ(F2_addr, Listener3.EmittedEvents[0].Code); + EXPECT_LT(0U, Listener3.EmittedEvents[0].Size) + << "We don't know how big the function will be, but it had better" + << " contain some bytes."; + + EXPECT_EQ(1U, Listener3.FreedEvents[0].Index); + EXPECT_EQ(F2_addr, Listener3.FreedEvents[0].Code); + + F1->eraseFromParent(); + F2->eraseFromParent(); +} + +TEST_F(JITEventListenerTest, MatchesMachineCodeInfo) { + RecordingJITEventListener Listener; + MachineCodeInfo MCI; + Function *F = buildFunction(M); + + EE->RegisterJITEventListener(&Listener); + EE->runJITOnFunction(F, &MCI); + void *F_addr = EE->getPointerToFunction(F); + EE->freeMachineCodeForFunction(F); + + ASSERT_EQ(1U, Listener.EmittedEvents.size()); + ASSERT_EQ(1U, Listener.FreedEvents.size()); + + EXPECT_EQ(0U, Listener.EmittedEvents[0].Index); + EXPECT_EQ(F, Listener.EmittedEvents[0].F); + EXPECT_EQ(F_addr, Listener.EmittedEvents[0].Code); + EXPECT_EQ(MCI.address(), Listener.EmittedEvents[0].Code); + EXPECT_EQ(MCI.size(), Listener.EmittedEvents[0].Size); + + EXPECT_EQ(1U, Listener.FreedEvents[0].Index); + EXPECT_EQ(F_addr, Listener.FreedEvents[0].Code); +} +#endif + +class JITEnvironment : public testing::Environment { + virtual void SetUp() { + // Required to create a JIT. + InitializeNativeTarget(); + } +}; +testing::Environment* const jit_env = + testing::AddGlobalTestEnvironment(new JITEnvironment); + +} // anonymous namespace diff --git a/llvm/unittests/ExecutionEngine/JIT/JITEventListenerTestCommon.h b/llvm/unittests/ExecutionEngine/JIT/JITEventListenerTestCommon.h new file mode 100644 index 00000000000..61220f545f9 --- /dev/null +++ b/llvm/unittests/ExecutionEngine/JIT/JITEventListenerTestCommon.h @@ -0,0 +1,207 @@ +//===- JITEventListenerTestCommon.h - Helper for JITEventListener tests ------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===-------------------------------------------------------------------------------===// + +#ifndef JIT_EVENT_LISTENER_TEST_COMMON_H +#define JIT_EVENT_LISTENER_TEST_COMMON_H + +#include "llvm/CodeGen/MachineCodeInfo.h" +#include "llvm/Config/config.h" +#include "llvm/ExecutionEngine/JIT.h" +#include "llvm/ExecutionEngine/JITEventListener.h" +#include "llvm/IR/DIBuilder.h" +#include "llvm/IR/DebugInfo.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/TypeBuilder.h" +#include "llvm/Support/Dwarf.h" +#include "llvm/Support/TargetSelect.h" +#include "gtest/gtest.h" +#include <string> +#include <utility> +#include <vector> + +typedef std::vector<std::pair<std::string, unsigned int> > SourceLocations; +typedef std::map<uint64_t, SourceLocations> NativeCodeMap; + +class JITEnvironment : public testing::Environment { + virtual void SetUp() { + // Required to create a JIT. + llvm::InitializeNativeTarget(); + } +}; + +inline unsigned int getLine() { + return 12; +} + +inline unsigned int getCol() { + return 0; +} + +inline const char* getFilename() { + return "mock_source_file.cpp"; +} + +// Test fixture shared by tests for listener implementations +template<typename WrapperT> +class JITEventListenerTestBase : public testing::Test { +protected: + std::unique_ptr<WrapperT> MockWrapper; + std::unique_ptr<llvm::JITEventListener> Listener; + +public: + llvm::Module* M; + llvm::MDNode* Scope; + llvm::ExecutionEngine* EE; + llvm::DIBuilder* DebugBuilder; + llvm::IRBuilder<> Builder; + + JITEventListenerTestBase(WrapperT* w) + : MockWrapper(w) + , M(new llvm::Module("module", llvm::getGlobalContext())) + , EE(llvm::EngineBuilder(M) + .setEngineKind(llvm::EngineKind::JIT) + .setOptLevel(llvm::CodeGenOpt::None) + .create()) + , DebugBuilder(new llvm::DIBuilder(*M)) + , Builder(llvm::getGlobalContext()) + { + DebugBuilder->createCompileUnit(llvm::dwarf::DW_LANG_C_plus_plus, + "JIT", + "JIT", + "JIT", + true, + "", + 1); + + Scope = DebugBuilder->createFile(getFilename(), "."); + } + + llvm::Function *buildFunction(const SourceLocations& DebugLocations) { + using namespace llvm; + + LLVMContext& GlobalContext = getGlobalContext(); + + SourceLocations::const_iterator CurrentDebugLocation + = DebugLocations.begin(); + + if (CurrentDebugLocation != DebugLocations.end()) { + DebugLoc DebugLocation = DebugLoc::get(getLine(), getCol(), + DebugBuilder->createFile(CurrentDebugLocation->first, ".")); + Builder.SetCurrentDebugLocation(DebugLocation); + CurrentDebugLocation++; + } + + Function *Result = Function::Create( + TypeBuilder<int32_t(int32_t), false>::get(GlobalContext), + GlobalValue::ExternalLinkage, "id", M); + Value *Arg = Result->arg_begin(); + BasicBlock *BB = BasicBlock::Create(M->getContext(), "entry", Result); + Builder.SetInsertPoint(BB); + Value* one = ConstantInt::get(GlobalContext, APInt(32, 1)); + for(; CurrentDebugLocation != DebugLocations.end(); + ++CurrentDebugLocation) { + Arg = Builder.CreateMul(Arg, Builder.CreateAdd(Arg, one)); + Builder.SetCurrentDebugLocation( + DebugLoc::get(CurrentDebugLocation->second, 0, + DebugBuilder->createFile(CurrentDebugLocation->first, "."))); + } + Builder.CreateRet(Arg); + return Result; + } + + void TestNoDebugInfo(NativeCodeMap& ReportedDebugFuncs) { + SourceLocations DebugLocations; + llvm::Function* f = buildFunction(DebugLocations); + EXPECT_TRUE(0 != f); + + //Cause JITting and callbacks to our listener + EXPECT_TRUE(0 != EE->getPointerToFunction(f)); + EXPECT_TRUE(1 == ReportedDebugFuncs.size()); + + EE->freeMachineCodeForFunction(f); + EXPECT_TRUE(ReportedDebugFuncs.size() == 0); + } + + void TestSingleLine(NativeCodeMap& ReportedDebugFuncs) { + SourceLocations DebugLocations; + DebugLocations.push_back(std::make_pair(std::string(getFilename()), + getLine())); + llvm::Function* f = buildFunction(DebugLocations); + EXPECT_TRUE(0 != f); + + EXPECT_TRUE(0 != EE->getPointerToFunction(f)); + EXPECT_TRUE(1 == ReportedDebugFuncs.size()); + EXPECT_STREQ(ReportedDebugFuncs.begin()->second.begin()->first.c_str(), + getFilename()); + EXPECT_EQ(ReportedDebugFuncs.begin()->second.begin()->second, getLine()); + + EE->freeMachineCodeForFunction(f); + EXPECT_TRUE(ReportedDebugFuncs.size() == 0); + } + + void TestMultipleLines(NativeCodeMap& ReportedDebugFuncs) { + using namespace std; + + SourceLocations DebugLocations; + unsigned int c = 5; + for(unsigned int i = 0; i < c; ++i) { + DebugLocations.push_back(make_pair(string(getFilename()), getLine() + i)); + } + + llvm::Function* f = buildFunction(DebugLocations); + EXPECT_TRUE(0 != f); + + EXPECT_TRUE(0 != EE->getPointerToFunction(f)); + EXPECT_TRUE(1 == ReportedDebugFuncs.size()); + SourceLocations& FunctionInfo = ReportedDebugFuncs.begin()->second; + EXPECT_EQ(c, FunctionInfo.size()); + + int VerifyCount = 0; + for(SourceLocations::iterator i = FunctionInfo.begin(); + i != FunctionInfo.end(); + ++i) { + EXPECT_STREQ(i->first.c_str(), getFilename()); + EXPECT_EQ(i->second, getLine() + VerifyCount); + VerifyCount++; + } + + EE->freeMachineCodeForFunction(f); + EXPECT_TRUE(ReportedDebugFuncs.size() == 0); + } + + void TestMultipleFiles(NativeCodeMap& ReportedDebugFuncs) { + + std::string secondFilename("another_file.cpp"); + + SourceLocations DebugLocations; + DebugLocations.push_back(std::make_pair(std::string(getFilename()), + getLine())); + DebugLocations.push_back(std::make_pair(secondFilename, getLine())); + llvm::Function* f = buildFunction(DebugLocations); + EXPECT_TRUE(0 != f); + + EXPECT_TRUE(0 != EE->getPointerToFunction(f)); + EXPECT_TRUE(1 == ReportedDebugFuncs.size()); + SourceLocations& FunctionInfo = ReportedDebugFuncs.begin()->second; + EXPECT_TRUE(2 == FunctionInfo.size()); + + EXPECT_STREQ(FunctionInfo.at(0).first.c_str(), getFilename()); + EXPECT_STREQ(FunctionInfo.at(1).first.c_str(), secondFilename.c_str()); + + EXPECT_EQ(FunctionInfo.at(0).second, getLine()); + EXPECT_EQ(FunctionInfo.at(1).second, getLine()); + + EE->freeMachineCodeForFunction(f); + EXPECT_TRUE(ReportedDebugFuncs.size() == 0); + } +}; + +#endif //JIT_EVENT_LISTENER_TEST_COMMON_H diff --git a/llvm/unittests/ExecutionEngine/JIT/JITMemoryManagerTest.cpp b/llvm/unittests/ExecutionEngine/JIT/JITMemoryManagerTest.cpp new file mode 100644 index 00000000000..296838de61b --- /dev/null +++ b/llvm/unittests/ExecutionEngine/JIT/JITMemoryManagerTest.cpp @@ -0,0 +1,302 @@ +//===- JITMemoryManagerTest.cpp - Unit tests for the JIT memory manager ---===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ExecutionEngine/JITMemoryManager.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/GlobalValue.h" +#include "llvm/IR/LLVMContext.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +Function *makeFakeFunction() { + std::vector<Type*> params; + FunctionType *FTy = + FunctionType::get(Type::getVoidTy(getGlobalContext()), params, false); + return Function::Create(FTy, GlobalValue::ExternalLinkage); +} + +// Allocate three simple functions that fit in the initial slab. This exercises +// the code in the case that we don't have to allocate more memory to store the +// function bodies. +TEST(JITMemoryManagerTest, NoAllocations) { + std::unique_ptr<JITMemoryManager> MemMgr( + JITMemoryManager::CreateDefaultMemManager()); + uintptr_t size; + std::string Error; + + // Allocate the functions. + std::unique_ptr<Function> F1(makeFakeFunction()); + size = 1024; + uint8_t *FunctionBody1 = MemMgr->startFunctionBody(F1.get(), size); + memset(FunctionBody1, 0xFF, 1024); + MemMgr->endFunctionBody(F1.get(), FunctionBody1, FunctionBody1 + 1024); + EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; + + std::unique_ptr<Function> F2(makeFakeFunction()); + size = 1024; + uint8_t *FunctionBody2 = MemMgr->startFunctionBody(F2.get(), size); + memset(FunctionBody2, 0xFF, 1024); + MemMgr->endFunctionBody(F2.get(), FunctionBody2, FunctionBody2 + 1024); + EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; + + std::unique_ptr<Function> F3(makeFakeFunction()); + size = 1024; + uint8_t *FunctionBody3 = MemMgr->startFunctionBody(F3.get(), size); + memset(FunctionBody3, 0xFF, 1024); + MemMgr->endFunctionBody(F3.get(), FunctionBody3, FunctionBody3 + 1024); + EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; + + // Deallocate them out of order, in case that matters. + MemMgr->deallocateFunctionBody(FunctionBody2); + EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; + MemMgr->deallocateFunctionBody(FunctionBody1); + EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; + MemMgr->deallocateFunctionBody(FunctionBody3); + EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; +} + +// Make three large functions that take up most of the space in the slab. Then +// try allocating three smaller functions that don't require additional slabs. +TEST(JITMemoryManagerTest, TestCodeAllocation) { + std::unique_ptr<JITMemoryManager> MemMgr( + JITMemoryManager::CreateDefaultMemManager()); + uintptr_t size; + std::string Error; + + // Big functions are a little less than the largest block size. + const uintptr_t smallFuncSize = 1024; + const uintptr_t bigFuncSize = (MemMgr->GetDefaultCodeSlabSize() - + smallFuncSize * 2); + + // Allocate big functions + std::unique_ptr<Function> F1(makeFakeFunction()); + size = bigFuncSize; + uint8_t *FunctionBody1 = MemMgr->startFunctionBody(F1.get(), size); + ASSERT_LE(bigFuncSize, size); + memset(FunctionBody1, 0xFF, bigFuncSize); + MemMgr->endFunctionBody(F1.get(), FunctionBody1, FunctionBody1 + bigFuncSize); + EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; + + std::unique_ptr<Function> F2(makeFakeFunction()); + size = bigFuncSize; + uint8_t *FunctionBody2 = MemMgr->startFunctionBody(F2.get(), size); + ASSERT_LE(bigFuncSize, size); + memset(FunctionBody2, 0xFF, bigFuncSize); + MemMgr->endFunctionBody(F2.get(), FunctionBody2, FunctionBody2 + bigFuncSize); + EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; + + std::unique_ptr<Function> F3(makeFakeFunction()); + size = bigFuncSize; + uint8_t *FunctionBody3 = MemMgr->startFunctionBody(F3.get(), size); + ASSERT_LE(bigFuncSize, size); + memset(FunctionBody3, 0xFF, bigFuncSize); + MemMgr->endFunctionBody(F3.get(), FunctionBody3, FunctionBody3 + bigFuncSize); + EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; + + // Check that each large function took it's own slab. + EXPECT_EQ(3U, MemMgr->GetNumCodeSlabs()); + + // Allocate small functions + std::unique_ptr<Function> F4(makeFakeFunction()); + size = smallFuncSize; + uint8_t *FunctionBody4 = MemMgr->startFunctionBody(F4.get(), size); + ASSERT_LE(smallFuncSize, size); + memset(FunctionBody4, 0xFF, smallFuncSize); + MemMgr->endFunctionBody(F4.get(), FunctionBody4, + FunctionBody4 + smallFuncSize); + EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; + + std::unique_ptr<Function> F5(makeFakeFunction()); + size = smallFuncSize; + uint8_t *FunctionBody5 = MemMgr->startFunctionBody(F5.get(), size); + ASSERT_LE(smallFuncSize, size); + memset(FunctionBody5, 0xFF, smallFuncSize); + MemMgr->endFunctionBody(F5.get(), FunctionBody5, + FunctionBody5 + smallFuncSize); + EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; + + std::unique_ptr<Function> F6(makeFakeFunction()); + size = smallFuncSize; + uint8_t *FunctionBody6 = MemMgr->startFunctionBody(F6.get(), size); + ASSERT_LE(smallFuncSize, size); + memset(FunctionBody6, 0xFF, smallFuncSize); + MemMgr->endFunctionBody(F6.get(), FunctionBody6, + FunctionBody6 + smallFuncSize); + EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; + + // Check that the small functions didn't allocate any new slabs. + EXPECT_EQ(3U, MemMgr->GetNumCodeSlabs()); + + // Deallocate them out of order, in case that matters. + MemMgr->deallocateFunctionBody(FunctionBody2); + EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; + MemMgr->deallocateFunctionBody(FunctionBody1); + EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; + MemMgr->deallocateFunctionBody(FunctionBody4); + EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; + MemMgr->deallocateFunctionBody(FunctionBody3); + EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; + MemMgr->deallocateFunctionBody(FunctionBody5); + EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; + MemMgr->deallocateFunctionBody(FunctionBody6); + EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; +} + +// Allocate five global ints of varying widths and alignment, and check their +// alignment and overlap. +TEST(JITMemoryManagerTest, TestSmallGlobalInts) { + std::unique_ptr<JITMemoryManager> MemMgr( + JITMemoryManager::CreateDefaultMemManager()); + uint8_t *a = (uint8_t *)MemMgr->allocateGlobal(8, 0); + uint16_t *b = (uint16_t*)MemMgr->allocateGlobal(16, 2); + uint32_t *c = (uint32_t*)MemMgr->allocateGlobal(32, 4); + uint64_t *d = (uint64_t*)MemMgr->allocateGlobal(64, 8); + + // Check the alignment. + EXPECT_EQ(0U, ((uintptr_t)b) & 0x1); + EXPECT_EQ(0U, ((uintptr_t)c) & 0x3); + EXPECT_EQ(0U, ((uintptr_t)d) & 0x7); + + // Initialize them each one at a time and make sure they don't overlap. + *a = 0xff; + *b = 0U; + *c = 0U; + *d = 0U; + EXPECT_EQ(0xffU, *a); + EXPECT_EQ(0U, *b); + EXPECT_EQ(0U, *c); + EXPECT_EQ(0U, *d); + *a = 0U; + *b = 0xffffU; + EXPECT_EQ(0U, *a); + EXPECT_EQ(0xffffU, *b); + EXPECT_EQ(0U, *c); + EXPECT_EQ(0U, *d); + *b = 0U; + *c = 0xffffffffU; + EXPECT_EQ(0U, *a); + EXPECT_EQ(0U, *b); + EXPECT_EQ(0xffffffffU, *c); + EXPECT_EQ(0U, *d); + *c = 0U; + *d = 0xffffffffffffffffULL; + EXPECT_EQ(0U, *a); + EXPECT_EQ(0U, *b); + EXPECT_EQ(0U, *c); + EXPECT_EQ(0xffffffffffffffffULL, *d); + + // Make sure we didn't allocate any extra slabs for this tiny amount of data. + EXPECT_EQ(1U, MemMgr->GetNumDataSlabs()); +} + +// Allocate a small global, a big global, and a third global, and make sure we +// only use two slabs for that. +TEST(JITMemoryManagerTest, TestLargeGlobalArray) { + std::unique_ptr<JITMemoryManager> MemMgr( + JITMemoryManager::CreateDefaultMemManager()); + size_t Size = 4 * MemMgr->GetDefaultDataSlabSize(); + uint64_t *a = (uint64_t*)MemMgr->allocateGlobal(64, 8); + uint8_t *g = MemMgr->allocateGlobal(Size, 8); + uint64_t *b = (uint64_t*)MemMgr->allocateGlobal(64, 8); + + // Check the alignment. + EXPECT_EQ(0U, ((uintptr_t)a) & 0x7); + EXPECT_EQ(0U, ((uintptr_t)g) & 0x7); + EXPECT_EQ(0U, ((uintptr_t)b) & 0x7); + + // Initialize them to make sure we don't segfault and make sure they don't + // overlap. + memset(a, 0x1, 8); + memset(g, 0x2, Size); + memset(b, 0x3, 8); + EXPECT_EQ(0x0101010101010101ULL, *a); + // Just check the edges. + EXPECT_EQ(0x02U, g[0]); + EXPECT_EQ(0x02U, g[Size - 1]); + EXPECT_EQ(0x0303030303030303ULL, *b); + + // Check the number of slabs. + EXPECT_EQ(2U, MemMgr->GetNumDataSlabs()); +} + +// Allocate lots of medium globals so that we can test moving the bump allocator +// to a new slab. +TEST(JITMemoryManagerTest, TestManyGlobals) { + std::unique_ptr<JITMemoryManager> MemMgr( + JITMemoryManager::CreateDefaultMemManager()); + size_t SlabSize = MemMgr->GetDefaultDataSlabSize(); + size_t Size = 128; + int Iters = (SlabSize / Size) + 1; + + // We should start with no slabs. + EXPECT_EQ(0U, MemMgr->GetNumDataSlabs()); + + // After allocating a bunch of globals, we should have two. + for (int I = 0; I < Iters; ++I) + MemMgr->allocateGlobal(Size, 8); + EXPECT_EQ(2U, MemMgr->GetNumDataSlabs()); + + // And after much more, we should have three. + for (int I = 0; I < Iters; ++I) + MemMgr->allocateGlobal(Size, 8); + EXPECT_EQ(3U, MemMgr->GetNumDataSlabs()); +} + +// Allocate lots of function stubs so that we can test moving the stub bump +// allocator to a new slab. +TEST(JITMemoryManagerTest, TestManyStubs) { + std::unique_ptr<JITMemoryManager> MemMgr( + JITMemoryManager::CreateDefaultMemManager()); + size_t SlabSize = MemMgr->GetDefaultStubSlabSize(); + size_t Size = 128; + int Iters = (SlabSize / Size) + 1; + + // We should start with no slabs. + EXPECT_EQ(0U, MemMgr->GetNumDataSlabs()); + + // After allocating a bunch of stubs, we should have two. + for (int I = 0; I < Iters; ++I) + MemMgr->allocateStub(nullptr, Size, 8); + EXPECT_EQ(2U, MemMgr->GetNumStubSlabs()); + + // And after much more, we should have three. + for (int I = 0; I < Iters; ++I) + MemMgr->allocateStub(nullptr, Size, 8); + EXPECT_EQ(3U, MemMgr->GetNumStubSlabs()); +} + +// Check section allocation and alignment +TEST(JITMemoryManagerTest, AllocateSection) { + std::unique_ptr<JITMemoryManager> MemMgr( + JITMemoryManager::CreateDefaultMemManager()); + uint8_t *code1 = MemMgr->allocateCodeSection(256, 0, 1, StringRef()); + uint8_t *data1 = MemMgr->allocateDataSection(256, 16, 2, StringRef(), true); + uint8_t *code2 = MemMgr->allocateCodeSection(257, 32, 3, StringRef()); + uint8_t *data2 = MemMgr->allocateDataSection(256, 64, 4, StringRef(), false); + uint8_t *code3 = MemMgr->allocateCodeSection(258, 64, 5, StringRef()); + + EXPECT_NE((uint8_t*)nullptr, code1); + EXPECT_NE((uint8_t*)nullptr, code2); + EXPECT_NE((uint8_t*)nullptr, data1); + EXPECT_NE((uint8_t*)nullptr, data2); + + // Check alignment + EXPECT_EQ((uint64_t)code1 & 0xf, 0u); + EXPECT_EQ((uint64_t)code2 & 0x1f, 0u); + EXPECT_EQ((uint64_t)code3 & 0x3f, 0u); + EXPECT_EQ((uint64_t)data1 & 0xf, 0u); + EXPECT_EQ((uint64_t)data2 & 0x3f, 0u); +} + +} diff --git a/llvm/unittests/ExecutionEngine/JIT/JITTest.cpp b/llvm/unittests/ExecutionEngine/JIT/JITTest.cpp new file mode 100644 index 00000000000..817d207c2dc --- /dev/null +++ b/llvm/unittests/ExecutionEngine/JIT/JITTest.cpp @@ -0,0 +1,728 @@ +//===- JITTest.cpp - Unit tests for the JIT -------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ExecutionEngine/JIT.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/AsmParser/Parser.h" +#include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/ExecutionEngine/JITMemoryManager.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/Constant.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/GlobalValue.h" +#include "llvm/IR/GlobalVariable.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Type.h" +#include "llvm/IR/TypeBuilder.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/TargetSelect.h" +#include "gtest/gtest.h" +#include <vector> + +using namespace llvm; + +// 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. Note that this variable must be defined even on platforms where +// JIT tests are disabled as it is referenced from the .def file. +extern "C" int32_t JITTest_AvailableExternallyGlobal; +int32_t JITTest_AvailableExternallyGlobal LLVM_ATTRIBUTE_USED = 42; + +// 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. Note that this function must be defined even on platforms where +// JIT tests are disabled as it is referenced from the .def file. +extern "C" int32_t JITTest_AvailableExternallyFunction() LLVM_ATTRIBUTE_USED; +extern "C" int32_t JITTest_AvailableExternallyFunction() { + return 42; +} + +namespace { + +// Tests on ARM, PowerPC and SystemZ disabled as we're running the old jit +#if !defined(__arm__) && !defined(__powerpc__) && !defined(__s390__) \ + && !defined(__aarch64__) + +Function *makeReturnGlobal(std::string Name, GlobalVariable *G, Module *M) { + std::vector<Type*> 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); + Type *GTy = G->getType()->getElementType(); + Value *Add = builder.CreateAdd(Load, ConstantInt::get(GTy, 1LL)); + builder.CreateStore(Add, G); + builder.CreateRet(Add); + return F; +} + +std::string DumpFunction(const Function *F) { + std::string Result; + raw_string_ostream(Result) << "" << *F; + return Result; +} + +class RecordingJITMemoryManager : public JITMemoryManager { + const std::unique_ptr<JITMemoryManager> Base; + +public: + RecordingJITMemoryManager() + : 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(); } + virtual void setPoisonMemory(bool poison) { Base->setPoisonMemory(poison); } + virtual void AllocateGOT() { Base->AllocateGOT(); } + virtual uint8_t *getGOTBase() const { return Base->getGOTBase(); } + struct StartFunctionBodyCall { + StartFunctionBodyCall(uint8_t *Result, const Function *F, + uintptr_t ActualSize, uintptr_t ActualSizeResult) + : Result(Result), F(F), F_dump(DumpFunction(F)), + ActualSize(ActualSize), ActualSizeResult(ActualSizeResult) {} + uint8_t *Result; + const Function *F; + std::string F_dump; + uintptr_t ActualSize; + uintptr_t ActualSizeResult; + }; + std::vector<StartFunctionBodyCall> startFunctionBodyCalls; + virtual uint8_t *startFunctionBody(const Function *F, + uintptr_t &ActualSize) { + uintptr_t InitialActualSize = ActualSize; + uint8_t *Result = Base->startFunctionBody(F, ActualSize); + startFunctionBodyCalls.push_back( + StartFunctionBodyCall(Result, F, InitialActualSize, ActualSize)); + return Result; + } + int stubsAllocated; + uint8_t *allocateStub(const GlobalValue *F, unsigned StubSize, + unsigned Alignment) override { + stubsAllocated++; + return Base->allocateStub(F, StubSize, Alignment); + } + struct EndFunctionBodyCall { + EndFunctionBodyCall(const Function *F, uint8_t *FunctionStart, + uint8_t *FunctionEnd) + : F(F), F_dump(DumpFunction(F)), + FunctionStart(FunctionStart), FunctionEnd(FunctionEnd) {} + const Function *F; + std::string F_dump; + uint8_t *FunctionStart; + uint8_t *FunctionEnd; + }; + std::vector<EndFunctionBodyCall> endFunctionBodyCalls; + virtual void endFunctionBody(const Function *F, uint8_t *FunctionStart, + uint8_t *FunctionEnd) { + endFunctionBodyCalls.push_back( + EndFunctionBodyCall(F, FunctionStart, FunctionEnd)); + Base->endFunctionBody(F, FunctionStart, FunctionEnd); + } + virtual uint8_t *allocateDataSection( + uintptr_t Size, unsigned Alignment, unsigned SectionID, + StringRef SectionName, bool IsReadOnly) { + return Base->allocateDataSection( + Size, Alignment, SectionID, SectionName, IsReadOnly); + } + virtual uint8_t *allocateCodeSection( + uintptr_t Size, unsigned Alignment, unsigned SectionID, + StringRef SectionName) { + return Base->allocateCodeSection( + Size, Alignment, SectionID, SectionName); + } + virtual bool finalizeMemory(std::string *ErrMsg) { return false; } + virtual uint8_t *allocateSpace(intptr_t Size, unsigned Alignment) { + return Base->allocateSpace(Size, Alignment); + } + virtual uint8_t *allocateGlobal(uintptr_t Size, unsigned Alignment) { + return Base->allocateGlobal(Size, Alignment); + } + struct DeallocateFunctionBodyCall { + DeallocateFunctionBodyCall(const void *Body) : Body(Body) {} + const void *Body; + }; + std::vector<DeallocateFunctionBodyCall> deallocateFunctionBodyCalls; + virtual void deallocateFunctionBody(void *Body) { + deallocateFunctionBodyCalls.push_back(DeallocateFunctionBodyCall(Body)); + Base->deallocateFunctionBody(Body); + } +}; + +bool LoadAssemblyInto(Module *M, const char *assembly) { + SMDiagnostic Error; + bool success = + nullptr != 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 RecordingJITMemoryManager *createMemoryManager() { + return new RecordingJITMemoryManager; + } + + virtual void SetUp() { + M = new Module("<main>", Context); + RJMM = createMemoryManager(); + RJMM->setPoisonMemory(true); + std::string Error; + TargetOptions Options; + TheJIT.reset(EngineBuilder(M).setEngineKind(EngineKind::JIT) + .setJITMemoryManager(RJMM) + .setErrorStr(&Error) + .setTargetOptions(Options).create()); + ASSERT_TRUE(TheJIT.get() != nullptr) << Error; + } + + void LoadAssembly(const char *assembly) { + LoadAssemblyInto(M, assembly); + } + + LLVMContext Context; + Module *M; // Owned by ExecutionEngine. + RecordingJITMemoryManager *RJMM; + std::unique_ptr<ExecutionEngine> TheJIT; +}; + +// 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 +// that uses and global, deallocates it, and then makes sure that the global +// stays alive after that. +TEST(JIT, GlobalInFunction) { + LLVMContext context; + Module *M = new Module("<main>", context); + + 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; + std::unique_ptr<ExecutionEngine> JIT(EngineBuilder(M) + .setEngineKind(EngineKind::JIT) + .setErrorStr(&Error) + .setJITMemoryManager(MemMgr) + // The next line enables the fix: + .setAllocateGVsWithCode(false) + .create()); + ASSERT_EQ(Error, ""); + + // Create a global variable. + Type *GTy = Type::getInt32Ty(context); + GlobalVariable *G = new GlobalVariable( + *M, + GTy, + false, // Not constant. + GlobalValue::InternalLinkage, + Constant::getNullValue(GTy), + "myglobal"); + + // Make a function that points to a global. + Function *F1 = makeReturnGlobal("F1", G, M); + + // Get the pointer to the native code to force it to JIT the function and + // allocate space for the global. + void (*F1Ptr)() = + reinterpret_cast<void(*)()>((intptr_t)JIT->getPointerToFunction(F1)); + + // Since F1 was codegen'd, a pointer to G should be available. + int32_t *GPtr = (int32_t*)JIT->getPointerToGlobalIfAvailable(G); + ASSERT_NE((int32_t*)nullptr, GPtr); + EXPECT_EQ(0, *GPtr); + + // F1() should increment G. + F1Ptr(); + EXPECT_EQ(1, *GPtr); + + // Make a second function identical to the first, referring to the same + // global. + Function *F2 = makeReturnGlobal("F2", G, M); + void (*F2Ptr)() = + reinterpret_cast<void(*)()>((intptr_t)JIT->getPointerToFunction(F2)); + + // F2() should increment G. + F2Ptr(); + EXPECT_EQ(2, *GPtr); + + // Deallocate F1. + JIT->freeMachineCodeForFunction(F1); + + // F2() should *still* increment G. + F2Ptr(); + EXPECT_EQ(3, *GPtr); +} + +int PlusOne(int arg) { + return arg + 1; +} + +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 + // the address into a register and call through the register. The + // current JIT does this by allocating a stub for any far call. + // There was a bug in which the JIT tried to emit a direct call when + // the target was already in the JIT's global mappings and lazy + // compilation was disabled. + + Function *KnownFunction = Function::Create( + TypeBuilder<int(int), false>::get(Context), + GlobalValue::ExternalLinkage, "known", M); + TheJIT->addGlobalMapping(KnownFunction, (void*)(intptr_t)PlusOne); + + // int test() { return known(7); } + Function *TestFunction = Function::Create( + TypeBuilder<int(), false>::get(Context), + GlobalValue::ExternalLinkage, "test", M); + BasicBlock *Entry = BasicBlock::Create(Context, "entry", TestFunction); + IRBuilder<> Builder(Entry); + Value *result = Builder.CreateCall( + KnownFunction, + ConstantInt::get(TypeBuilder<int, false>::get(Context), 7)); + Builder.CreateRet(result); + + TheJIT->DisableLazyCompilation(true); + int (*TestFunctionPtr)() = reinterpret_cast<int(*)()>( + (intptr_t)TheJIT->getPointerToFunction(TestFunction)); + // This used to crash in trying to call PlusOne(). + EXPECT_EQ(8, TestFunctionPtr()); +} + +// Test a function C which calls A and B which call each other. +TEST_F(JITTest, NonLazyCompilationStillNeedsStubs) { + TheJIT->DisableLazyCompilation(true); + + FunctionType *Func1Ty = + cast<FunctionType>(TypeBuilder<void(void), false>::get(Context)); + std::vector<Type*> arg_types; + arg_types.push_back(Type::getInt1Ty(Context)); + FunctionType *FuncTy = FunctionType::get( + Type::getVoidTy(Context), arg_types, false); + Function *Func1 = Function::Create(Func1Ty, Function::ExternalLinkage, + "func1", M); + Function *Func2 = Function::Create(FuncTy, Function::InternalLinkage, + "func2", M); + Function *Func3 = Function::Create(FuncTy, Function::InternalLinkage, + "func3", M); + BasicBlock *Block1 = BasicBlock::Create(Context, "block1", Func1); + BasicBlock *Block2 = BasicBlock::Create(Context, "block2", Func2); + BasicBlock *True2 = BasicBlock::Create(Context, "cond_true", Func2); + BasicBlock *False2 = BasicBlock::Create(Context, "cond_false", Func2); + BasicBlock *Block3 = BasicBlock::Create(Context, "block3", Func3); + BasicBlock *True3 = BasicBlock::Create(Context, "cond_true", Func3); + BasicBlock *False3 = BasicBlock::Create(Context, "cond_false", Func3); + + // Make Func1 call Func2(0) and Func3(0). + IRBuilder<> Builder(Block1); + Builder.CreateCall(Func2, ConstantInt::getTrue(Context)); + Builder.CreateCall(Func3, ConstantInt::getTrue(Context)); + Builder.CreateRetVoid(); + + // void Func2(bool b) { if (b) { Func3(false); return; } return; } + Builder.SetInsertPoint(Block2); + Builder.CreateCondBr(Func2->arg_begin(), True2, False2); + Builder.SetInsertPoint(True2); + Builder.CreateCall(Func3, ConstantInt::getFalse(Context)); + Builder.CreateRetVoid(); + Builder.SetInsertPoint(False2); + Builder.CreateRetVoid(); + + // void Func3(bool b) { if (b) { Func2(false); return; } return; } + Builder.SetInsertPoint(Block3); + Builder.CreateCondBr(Func3->arg_begin(), True3, False3); + Builder.SetInsertPoint(True3); + Builder.CreateCall(Func2, ConstantInt::getFalse(Context)); + Builder.CreateRetVoid(); + Builder.SetInsertPoint(False3); + Builder.CreateRetVoid(); + + // Compile the function to native code + void (*F1Ptr)() = + reinterpret_cast<void(*)()>((intptr_t)TheJIT->getPointerToFunction(Func1)); + + F1Ptr(); +} + +// Regression test for PR5162. This used to trigger an AssertingVH inside the +// JIT's Function to stub mapping. +TEST_F(JITTest, NonLazyLeaksNoStubs) { + TheJIT->DisableLazyCompilation(true); + + // Create two functions with a single basic block each. + FunctionType *FuncTy = + cast<FunctionType>(TypeBuilder<int(), false>::get(Context)); + Function *Func1 = Function::Create(FuncTy, Function::ExternalLinkage, + "func1", M); + Function *Func2 = Function::Create(FuncTy, Function::InternalLinkage, + "func2", M); + BasicBlock *Block1 = BasicBlock::Create(Context, "block1", Func1); + BasicBlock *Block2 = BasicBlock::Create(Context, "block2", Func2); + + // The first function calls the second and returns the result + IRBuilder<> Builder(Block1); + Value *Result = Builder.CreateCall(Func2); + Builder.CreateRet(Result); + + // The second function just returns a constant + Builder.SetInsertPoint(Block2); + Builder.CreateRet(ConstantInt::get(TypeBuilder<int, false>::get(Context),42)); + + // Compile the function to native code + (void)TheJIT->getPointerToFunction(Func1); + + // Free the JIT state for the functions + TheJIT->freeMachineCodeForFunction(Func1); + TheJIT->freeMachineCodeForFunction(Func2); + + // Delete the first function (and show that is has no users) + EXPECT_EQ(Func1->getNumUses(), 0u); + Func1->eraseFromParent(); + + // Delete the second function (and show that it has no users - it had one, + // func1 but that's gone now) + EXPECT_EQ(Func2->getNumUses(), 0u); + Func2->eraseFromParent(); +} + +TEST_F(JITTest, ModuleDeletion) { + TheJIT->DisableLazyCompilation(false); + LoadAssembly("define void @main() { " + " call i32 @computeVal() " + " ret void " + "} " + " " + "define internal i32 @computeVal() { " + " ret i32 0 " + "} "); + Function *func = M->getFunction("main"); + TheJIT->getPointerToFunction(func); + TheJIT->removeModule(M); + delete M; + + SmallPtrSet<const void*, 2> FunctionsDeallocated; + for (unsigned i = 0, e = RJMM->deallocateFunctionBodyCalls.size(); + i != e; ++i) { + FunctionsDeallocated.insert(RJMM->deallocateFunctionBodyCalls[i].Body); + } + for (unsigned i = 0, e = RJMM->startFunctionBodyCalls.size(); i != e; ++i) { + EXPECT_TRUE(FunctionsDeallocated.count( + RJMM->startFunctionBodyCalls[i].Result)) + << "Function leaked: \n" << RJMM->startFunctionBodyCalls[i].F_dump; + } + EXPECT_EQ(RJMM->startFunctionBodyCalls.size(), + RJMM->deallocateFunctionBodyCalls.size()); +} + +// 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(__mips__) && \ + !defined(__powerpc__) && !defined(__ppc__) && !defined(__aarch64__) +typedef int (*FooPtr) (); + +TEST_F(JITTest, NoStubs) { + LoadAssembly("define void @bar() {" + "entry: " + "ret void" + "}" + " " + "define i32 @foo() {" + "entry:" + "call void @bar()" + "ret i32 undef" + "}" + " " + "define i32 @main() {" + "entry:" + "%0 = call i32 @foo()" + "call void @bar()" + "ret i32 undef" + "}"); + Function *foo = M->getFunction("foo"); + uintptr_t tmp = (uintptr_t)(TheJIT->getPointerToFunction(foo)); + FooPtr ptr = (FooPtr)(tmp); + + (ptr)(); + + // We should now allocate no more stubs, we have the code to foo + // and the existing stub for bar. + int stubsBefore = RJMM->stubsAllocated; + Function *func = M->getFunction("main"); + TheJIT->getPointerToFunction(func); + + Function *bar = M->getFunction("bar"); + TheJIT->getPointerToFunction(bar); + + ASSERT_EQ(stubsBefore, RJMM->stubsAllocated); +} +#endif // !ARM && !PPC + +TEST_F(JITTest, FunctionPointersOutliveTheirCreator) { + TheJIT->DisableLazyCompilation(true); + LoadAssembly("define i8()* @get_foo_addr() { " + " ret i8()* @foo " + "} " + " " + "define i8 @foo() { " + " ret i8 42 " + "} "); + Function *F_get_foo_addr = M->getFunction("get_foo_addr"); + + typedef char(*fooT)(); + fooT (*get_foo_addr)() = reinterpret_cast<fooT(*)()>( + (intptr_t)TheJIT->getPointerToFunction(F_get_foo_addr)); + fooT foo_addr = get_foo_addr(); + + // Now free get_foo_addr. This should not free the machine code for foo or + // any call stub returned as foo's canonical address. + TheJIT->freeMachineCodeForFunction(F_get_foo_addr); + + // Check by calling the reported address of foo. + EXPECT_EQ(42, foo_addr()); + + // The reported address should also be the same as the result of a subsequent + // getPointerToFunction(foo). +#if 0 + // Fails until PR5126 is fixed: + Function *F_foo = M->getFunction("foo"); + fooT foo = reinterpret_cast<fooT>( + (intptr_t)TheJIT->getPointerToFunction(F_foo)); + EXPECT_EQ((intptr_t)foo, (intptr_t)foo_addr); +#endif +} + +// ARM does not have an implementation of replaceMachineCodeForFunction(), +// so recompileAndRelinkFunction doesn't work. +#if !defined(__arm__) && !defined(__aarch64__) +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__) + +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."; +} + +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"); + ErrorOr<Module*> ModuleOrErr = getLazyBitcodeModule(BitcodeBuffer, Context); + if (std::error_code EC = ModuleOrErr.getError()) { + ADD_FAILURE() << EC.message(); + delete BitcodeBuffer; + return nullptr; + } + M = ModuleOrErr.get(); + std::string errMsg; + ExecutionEngine *TheJIT = EngineBuilder(M) + .setEngineKind(EngineKind::JIT) + .setErrorStr(&errMsg) + .create(); + if (TheJIT == nullptr) { + ADD_FAILURE() << errMsg; + delete M; + M = nullptr; + return nullptr; + } + 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; + std::unique_ptr<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; + std::unique_ptr<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__) && !defined(__powerpc__) && !defined(__s390__) + +} diff --git a/llvm/unittests/ExecutionEngine/JIT/JITTests.def b/llvm/unittests/ExecutionEngine/JIT/JITTests.def new file mode 100644 index 00000000000..17c91e87bcd --- /dev/null +++ b/llvm/unittests/ExecutionEngine/JIT/JITTests.def @@ -0,0 +1,4 @@ +EXPORTS +getPointerToNamedFunction +JITTest_AvailableExternallyFunction +JITTest_AvailableExternallyGlobal diff --git a/llvm/unittests/ExecutionEngine/JIT/Makefile b/llvm/unittests/ExecutionEngine/JIT/Makefile new file mode 100644 index 00000000000..d86c03bfc80 --- /dev/null +++ b/llvm/unittests/ExecutionEngine/JIT/Makefile @@ -0,0 +1,52 @@ +##===- unittests/ExecutionEngine/JIT/Makefile --------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LEVEL = ../../.. +TESTNAME = JIT +LINK_COMPONENTS := asmparser bitreader bitwriter jit native + +# The JIT tests need to dlopen things. +NO_DEAD_STRIP := 1 + +include $(LEVEL)/Makefile.config + +SOURCES := JITEventListenerTest.cpp JITMemoryManagerTest.cpp JITTest.cpp MultiJITTest.cpp + + +ifeq ($(USE_INTEL_JITEVENTS), 1) + # Build the Intel JIT Events interface tests + SOURCES += IntelJITEventListenerTest.cpp + + # Add the Intel JIT Events include directory + CPPFLAGS += -I$(INTEL_JITEVENTS_INCDIR) + + # Link against the LLVM Intel JIT Evens interface library + LINK_COMPONENTS += debuginfo inteljitevents object +endif + +ifeq ($(USE_OPROFILE), 1) + # Build the OProfile JIT interface tests + SOURCES += OProfileJITEventListenerTest.cpp + + # Link against the LLVM oprofile interface library + LINK_COMPONENTS += oprofilejit +endif + +EXPORTED_SYMBOL_FILE = $(PROJ_OBJ_DIR)/JITTests.exports + +include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest + +# Permit these tests to use the JIT's symbolic lookup. +LD.Flags += $(RDYNAMIC) + +# Symbol exports are necessary (at least for now) when building with LTO. +$(LLVMUnitTestExe): $(NativeExportsFile) +$(PROJ_OBJ_DIR)/JITTests.exports: $(PROJ_SRC_DIR)/JITTests.def $(PROJ_OBJ_DIR)/.dir + tail -n +2 $< > $@ + diff --git a/llvm/unittests/ExecutionEngine/JIT/MultiJITTest.cpp b/llvm/unittests/ExecutionEngine/JIT/MultiJITTest.cpp new file mode 100644 index 00000000000..f530e0de5d5 --- /dev/null +++ b/llvm/unittests/ExecutionEngine/JIT/MultiJITTest.cpp @@ -0,0 +1,190 @@ +//===- MultiJITTest.cpp - Unit tests for instantiating multiple JITs ------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ExecutionEngine/JIT.h" +#include "llvm/AsmParser/Parser.h" +#include "llvm/ExecutionEngine/GenericValue.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/SourceMgr.h" +#include "gtest/gtest.h" +#include <vector> + +using namespace llvm; + +namespace { + +// ARM, PowerPC and SystemZ tests disabled pending fix for PR10783. +#if !defined(__arm__) && !defined(__powerpc__) && !defined(__s390__) \ + && !defined(__aarch64__) + +bool LoadAssemblyInto(Module *M, const char *assembly) { + SMDiagnostic Error; + bool success = + nullptr != ParseAssemblyString(assembly, M, Error, M->getContext()); + std::string errMsg; + raw_string_ostream os(errMsg); + Error.print("", os); + EXPECT_TRUE(success) << os.str(); + return success; +} + +void createModule1(LLVMContext &Context1, Module *&M1, Function *&FooF1) { + M1 = new Module("test1", Context1); + LoadAssemblyInto(M1, + "define i32 @add1(i32 %ArgX1) { " + "entry: " + " %addresult = add i32 1, %ArgX1 " + " ret i32 %addresult " + "} " + " " + "define i32 @foo1() { " + "entry: " + " %add1 = call i32 @add1(i32 10) " + " ret i32 %add1 " + "} "); + FooF1 = M1->getFunction("foo1"); +} + +void createModule2(LLVMContext &Context2, Module *&M2, Function *&FooF2) { + M2 = new Module("test2", Context2); + LoadAssemblyInto(M2, + "define i32 @add2(i32 %ArgX2) { " + "entry: " + " %addresult = add i32 2, %ArgX2 " + " ret i32 %addresult " + "} " + " " + "define i32 @foo2() { " + "entry: " + " %add2 = call i32 @add2(i32 10) " + " ret i32 %add2 " + "} "); + FooF2 = M2->getFunction("foo2"); +} + +TEST(MultiJitTest, EagerMode) { + LLVMContext Context1; + Module *M1 = nullptr; + Function *FooF1 = nullptr; + createModule1(Context1, M1, FooF1); + + LLVMContext Context2; + Module *M2 = nullptr; + Function *FooF2 = nullptr; + createModule2(Context2, M2, FooF2); + + // Now we create the JIT in eager mode + std::unique_ptr<ExecutionEngine> EE1(EngineBuilder(M1).create()); + EE1->DisableLazyCompilation(true); + std::unique_ptr<ExecutionEngine> EE2(EngineBuilder(M2).create()); + EE2->DisableLazyCompilation(true); + + // Call the `foo' function with no arguments: + std::vector<GenericValue> noargs; + GenericValue gv1 = EE1->runFunction(FooF1, noargs); + GenericValue gv2 = EE2->runFunction(FooF2, noargs); + + // Import result of execution: + EXPECT_EQ(gv1.IntVal, 11); + EXPECT_EQ(gv2.IntVal, 12); + + EE1->freeMachineCodeForFunction(FooF1); + EE2->freeMachineCodeForFunction(FooF2); +} + +TEST(MultiJitTest, LazyMode) { + LLVMContext Context1; + Module *M1 = nullptr; + Function *FooF1 = nullptr; + createModule1(Context1, M1, FooF1); + + LLVMContext Context2; + Module *M2 = nullptr; + Function *FooF2 = nullptr; + createModule2(Context2, M2, FooF2); + + // Now we create the JIT in lazy mode + std::unique_ptr<ExecutionEngine> EE1(EngineBuilder(M1).create()); + EE1->DisableLazyCompilation(false); + std::unique_ptr<ExecutionEngine> EE2(EngineBuilder(M2).create()); + EE2->DisableLazyCompilation(false); + + // Call the `foo' function with no arguments: + std::vector<GenericValue> noargs; + GenericValue gv1 = EE1->runFunction(FooF1, noargs); + GenericValue gv2 = EE2->runFunction(FooF2, noargs); + + // Import result of execution: + EXPECT_EQ(gv1.IntVal, 11); + EXPECT_EQ(gv2.IntVal, 12); + + EE1->freeMachineCodeForFunction(FooF1); + EE2->freeMachineCodeForFunction(FooF2); +} + +extern "C" { + extern void *getPointerToNamedFunction(const char *Name); +} + +TEST(MultiJitTest, JitPool) { + LLVMContext Context1; + Module *M1 = nullptr; + Function *FooF1 = nullptr; + createModule1(Context1, M1, FooF1); + + LLVMContext Context2; + Module *M2 = nullptr; + Function *FooF2 = nullptr; + createModule2(Context2, M2, FooF2); + + // Now we create two JITs + std::unique_ptr<ExecutionEngine> EE1(EngineBuilder(M1).create()); + std::unique_ptr<ExecutionEngine> EE2(EngineBuilder(M2).create()); + + Function *F1 = EE1->FindFunctionNamed("foo1"); + void *foo1 = EE1->getPointerToFunction(F1); + + Function *F2 = EE2->FindFunctionNamed("foo2"); + void *foo2 = EE2->getPointerToFunction(F2); + + // Function in M1 + EXPECT_EQ(getPointerToNamedFunction("foo1"), foo1); + + // Function in M2 + EXPECT_EQ(getPointerToNamedFunction("foo2"), foo2); + + // Symbol search + intptr_t + sa = (intptr_t)getPointerToNamedFunction("getPointerToNamedFunction"); + EXPECT_TRUE(sa != 0); + intptr_t fa = (intptr_t)&getPointerToNamedFunction; + EXPECT_TRUE(fa != 0); +#ifdef __i386__ + // getPointerToNamedFunction might be indirect jump on Win32 --enable-shared. + // FF 25 <disp32>: jmp *(pointer to IAT) + if (sa != fa && memcmp((char *)fa, "\xFF\x25", 2) == 0) { + fa = *(intptr_t *)(fa + 2); // Address to IAT + EXPECT_TRUE(fa != 0); + fa = *(intptr_t *)fa; // Bound value of IAT + } +#elif defined(__x86_64__) + // getPointerToNamedFunction might be indirect jump + // on Win32 x64 --enable-shared. + // FF 25 <pcrel32>: jmp *(RIP + pointer to IAT) + if (sa != fa && memcmp((char *)fa, "\xFF\x25", 2) == 0) { + fa += *(int32_t *)(fa + 2) + 6; // Address to IAT(RIP) + fa = *(intptr_t *)fa; // Bound value of IAT + } +#endif + EXPECT_TRUE(sa == fa); +} +#endif // !defined(__arm__) && !defined(__powerpc__) && !defined(__s390__) + +} // anonymous namespace diff --git a/llvm/unittests/ExecutionEngine/JIT/OProfileJITEventListenerTest.cpp b/llvm/unittests/ExecutionEngine/JIT/OProfileJITEventListenerTest.cpp new file mode 100644 index 00000000000..7057fcaf1d6 --- /dev/null +++ b/llvm/unittests/ExecutionEngine/JIT/OProfileJITEventListenerTest.cpp @@ -0,0 +1,165 @@ +//===- OProfileJITEventListenerTest.cpp - Unit tests for OProfileJITEventsListener --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===--------------------------------------------------------------------------------------===// + +#include "llvm/ExecutionEngine/OProfileWrapper.h" +#include "JITEventListenerTestCommon.h" +#include "llvm/ExecutionEngine/JITEventListener.h" +#include <list> +#include <map> + +using namespace llvm; + +namespace { + +struct OprofileNativeFunction { + const char* Name; + uint64_t Addr; + const void* CodePtr; + unsigned int CodeSize; + + OprofileNativeFunction(const char* name, + uint64_t addr, + const void* code, + unsigned int size) + : Name(name) + , Addr(addr) + , CodePtr(code) + , CodeSize(size) { + } +}; + +typedef std::list<OprofileNativeFunction> NativeFunctionList; +typedef std::list<debug_line_info> NativeDebugList; +NativeFunctionList NativeFunctions; + +NativeCodeMap ReportedDebugFuncs; + +} // namespace + +/// Mock implementaion of opagent library +namespace test_opagent { + +op_agent_t globalAgent = reinterpret_cast<op_agent_t>(42); + +op_agent_t open_agent() +{ + // return non-null op_agent_t + return globalAgent; +} + +int close_agent(op_agent_t agent) +{ + EXPECT_EQ(globalAgent, agent); + return 0; +} + +int write_native_code(op_agent_t agent, + const char* name, + uint64_t addr, + void const* code, + unsigned int size) +{ + EXPECT_EQ(globalAgent, agent); + OprofileNativeFunction func(name, addr, code, size); + NativeFunctions.push_back(func); + + // Verify no other registration has take place for the same address + EXPECT_TRUE(ReportedDebugFuncs.find(addr) == ReportedDebugFuncs.end()); + + ReportedDebugFuncs[addr]; + return 0; +} + +int write_debug_line_info(op_agent_t agent, + void const* code, + size_t num_entries, + struct debug_line_info const* info) +{ + EXPECT_EQ(globalAgent, agent); + + //verify code has been loaded first + uint64_t addr = reinterpret_cast<uint64_t>(code); + NativeCodeMap::iterator i = ReportedDebugFuncs.find(addr); + EXPECT_TRUE(i != ReportedDebugFuncs.end()); + + NativeDebugList NativeInfo(info, info + num_entries); + + SourceLocations locs; + for(NativeDebugList::iterator i = NativeInfo.begin(); + i != NativeInfo.end(); + ++i) { + locs.push_back(std::make_pair(std::string(i->filename), i->lineno)); + } + ReportedDebugFuncs[addr] = locs; + + return 0; +} + +int unload_native_code(op_agent_t agent, uint64_t addr) { + EXPECT_EQ(globalAgent, agent); + + //verify that something for the given JIT addr has been loaded first + NativeCodeMap::iterator i = ReportedDebugFuncs.find(addr); + EXPECT_TRUE(i != ReportedDebugFuncs.end()); + ReportedDebugFuncs.erase(i); + return 0; +} + +int version() { + return 1; +} + +bool is_oprofile_running() { + return true; +} + +} //namespace test_opagent + +class OProfileJITEventListenerTest +: public JITEventListenerTestBase<OProfileWrapper> +{ +public: + OProfileJITEventListenerTest() + : JITEventListenerTestBase<OProfileWrapper>( + new OProfileWrapper(test_opagent::open_agent, + test_opagent::close_agent, + test_opagent::write_native_code, + test_opagent::write_debug_line_info, + test_opagent::unload_native_code, + test_opagent::version, + test_opagent::version, + test_opagent::is_oprofile_running)) + { + EXPECT_TRUE(0 != MockWrapper); + + Listener.reset(JITEventListener::createOProfileJITEventListener( + MockWrapper.get())); + EXPECT_TRUE(0 != Listener); + EE->RegisterJITEventListener(Listener.get()); + } +}; + +TEST_F(OProfileJITEventListenerTest, NoDebugInfo) { + TestNoDebugInfo(ReportedDebugFuncs); +} + +TEST_F(OProfileJITEventListenerTest, SingleLine) { + TestSingleLine(ReportedDebugFuncs); +} + +TEST_F(OProfileJITEventListenerTest, MultipleLines) { + TestMultipleLines(ReportedDebugFuncs); +} + +TEST_F(OProfileJITEventListenerTest, MultipleFiles) { + TestMultipleFiles(ReportedDebugFuncs); +} + +testing::Environment* const jit_env = + testing::AddGlobalTestEnvironment(new JITEnvironment); diff --git a/llvm/unittests/ExecutionEngine/MCJIT/CMakeLists.txt b/llvm/unittests/ExecutionEngine/MCJIT/CMakeLists.txt index b10cbb4c9ea..cbd08fe9b8e 100644 --- a/llvm/unittests/ExecutionEngine/MCJIT/CMakeLists.txt +++ b/llvm/unittests/ExecutionEngine/MCJIT/CMakeLists.txt @@ -3,6 +3,7 @@ set(LLVM_LINK_COMPONENTS Core ExecutionEngine IPO + JIT MC MCJIT ScalarOpts diff --git a/llvm/unittests/ExecutionEngine/MCJIT/MCJITMemoryManagerTest.cpp b/llvm/unittests/ExecutionEngine/MCJIT/MCJITMemoryManagerTest.cpp index 0582c92b5f8..98587f7e85c 100644 --- a/llvm/unittests/ExecutionEngine/MCJIT/MCJITMemoryManagerTest.cpp +++ b/llvm/unittests/ExecutionEngine/MCJIT/MCJITMemoryManagerTest.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "llvm/ExecutionEngine/SectionMemoryManager.h" +#include "llvm/ExecutionEngine/JIT.h" #include "gtest/gtest.h" using namespace llvm; diff --git a/llvm/unittests/ExecutionEngine/MCJIT/MCJITObjectCacheTest.cpp b/llvm/unittests/ExecutionEngine/MCJIT/MCJITObjectCacheTest.cpp index a245dbec999..fbbab42dbb0 100644 --- a/llvm/unittests/ExecutionEngine/MCJIT/MCJITObjectCacheTest.cpp +++ b/llvm/unittests/ExecutionEngine/MCJIT/MCJITObjectCacheTest.cpp @@ -11,6 +11,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringSet.h" +#include "llvm/ExecutionEngine/JIT.h" #include "llvm/ExecutionEngine/MCJIT.h" #include "llvm/ExecutionEngine/ObjectCache.h" #include "llvm/ExecutionEngine/SectionMemoryManager.h" diff --git a/llvm/unittests/ExecutionEngine/MCJIT/MCJITTestBase.h b/llvm/unittests/ExecutionEngine/MCJIT/MCJITTestBase.h index 2c1d518da4c..25de312e0e8 100644 --- a/llvm/unittests/ExecutionEngine/MCJIT/MCJITTestBase.h +++ b/llvm/unittests/ExecutionEngine/MCJIT/MCJITTestBase.h @@ -317,9 +317,11 @@ protected: EngineBuilder EB(M); std::string Error; TheJIT.reset(EB.setEngineKind(EngineKind::JIT) + .setUseMCJIT(true) /* can this be folded into the EngineKind enum? */ .setMCJITMemoryManager(MM) .setErrorStr(&Error) .setOptLevel(CodeGenOpt::None) + .setAllocateGVsWithCode(false) /*does this do anything?*/ .setCodeModel(CodeModel::JITDefault) .setRelocationModel(Reloc::Default) .setMArch(MArch) diff --git a/llvm/unittests/ExecutionEngine/MCJIT/Makefile b/llvm/unittests/ExecutionEngine/MCJIT/Makefile index 2822b20cdda..c4dd740e057 100644 --- a/llvm/unittests/ExecutionEngine/MCJIT/Makefile +++ b/llvm/unittests/ExecutionEngine/MCJIT/Makefile @@ -9,7 +9,7 @@ LEVEL = ../../.. TESTNAME = MCJIT -LINK_COMPONENTS := core ipo mcjit native support +LINK_COMPONENTS := core ipo jit mcjit native support include $(LEVEL)/Makefile.config include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest diff --git a/llvm/unittests/ExecutionEngine/Makefile b/llvm/unittests/ExecutionEngine/Makefile index 8ecb883ba9d..38e667ff988 100644 --- a/llvm/unittests/ExecutionEngine/Makefile +++ b/llvm/unittests/ExecutionEngine/Makefile @@ -14,7 +14,7 @@ LINK_COMPONENTS :=interpreter include $(LEVEL)/Makefile.config ifeq ($(TARGET_HAS_JIT),1) - PARALLEL_DIRS = MCJIT + PARALLEL_DIRS = JIT MCJIT endif include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest |