diff options
author | Eric Christopher <echristo@gmail.com> | 2014-08-07 22:02:54 +0000 |
---|---|---|
committer | Eric Christopher <echristo@gmail.com> | 2014-08-07 22:02:54 +0000 |
commit | aa5b9c0f6f3a99f955fe0ded13d61d7eb4e1a0b5 (patch) | |
tree | 28f2c53797646bca9a2865ca1a5bf23768c8a207 /unittests/ExecutionEngine | |
parent | 9fbb01240448f580f0689fee455c8a19cb44ddde (diff) |
Temporarily Revert "Nuke the old JIT." as it's not quite ready to
be deleted. This will be reapplied as soon as possible and before
the 3.6 branch date at any rate.
Approved by Jim Grosbach, Lang Hames, Rafael Espindola.
This reverts commits r215111, 215115, 215116, 215117, 215136.
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@215154 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'unittests/ExecutionEngine')
17 files changed, 2072 insertions, 3 deletions
diff --git a/unittests/ExecutionEngine/CMakeLists.txt b/unittests/ExecutionEngine/CMakeLists.txt index 783c9b51921..5e462220e5c 100644 --- a/unittests/ExecutionEngine/CMakeLists.txt +++ b/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/unittests/ExecutionEngine/JIT/CMakeLists.txt b/unittests/ExecutionEngine/JIT/CMakeLists.txt new file mode 100644 index 00000000000..5ace1c6ee48 --- /dev/null +++ b/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/unittests/ExecutionEngine/JIT/IntelJITEventListenerTest.cpp b/unittests/ExecutionEngine/JIT/IntelJITEventListenerTest.cpp new file mode 100644 index 00000000000..db9088777a9 --- /dev/null +++ b/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/unittests/ExecutionEngine/JIT/JITEventListenerTest.cpp b/unittests/ExecutionEngine/JIT/JITEventListenerTest.cpp new file mode 100644 index 00000000000..175b9fb107b --- /dev/null +++ b/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/unittests/ExecutionEngine/JIT/JITEventListenerTestCommon.h b/unittests/ExecutionEngine/JIT/JITEventListenerTestCommon.h new file mode 100644 index 00000000000..61220f545f9 --- /dev/null +++ b/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/unittests/ExecutionEngine/JIT/JITMemoryManagerTest.cpp b/unittests/ExecutionEngine/JIT/JITMemoryManagerTest.cpp new file mode 100644 index 00000000000..296838de61b --- /dev/null +++ b/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/unittests/ExecutionEngine/JIT/JITTest.cpp b/unittests/ExecutionEngine/JIT/JITTest.cpp new file mode 100644 index 00000000000..817d207c2dc --- /dev/null +++ b/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/unittests/ExecutionEngine/JIT/JITTests.def b/unittests/ExecutionEngine/JIT/JITTests.def new file mode 100644 index 00000000000..17c91e87bcd --- /dev/null +++ b/unittests/ExecutionEngine/JIT/JITTests.def @@ -0,0 +1,4 @@ +EXPORTS +getPointerToNamedFunction +JITTest_AvailableExternallyFunction +JITTest_AvailableExternallyGlobal diff --git a/unittests/ExecutionEngine/JIT/Makefile b/unittests/ExecutionEngine/JIT/Makefile new file mode 100644 index 00000000000..d86c03bfc80 --- /dev/null +++ b/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/unittests/ExecutionEngine/JIT/MultiJITTest.cpp b/unittests/ExecutionEngine/JIT/MultiJITTest.cpp new file mode 100644 index 00000000000..f530e0de5d5 --- /dev/null +++ b/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/unittests/ExecutionEngine/JIT/OProfileJITEventListenerTest.cpp b/unittests/ExecutionEngine/JIT/OProfileJITEventListenerTest.cpp new file mode 100644 index 00000000000..7057fcaf1d6 --- /dev/null +++ b/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/unittests/ExecutionEngine/MCJIT/CMakeLists.txt b/unittests/ExecutionEngine/MCJIT/CMakeLists.txt index b10cbb4c9ea..cbd08fe9b8e 100644 --- a/unittests/ExecutionEngine/MCJIT/CMakeLists.txt +++ b/unittests/ExecutionEngine/MCJIT/CMakeLists.txt @@ -3,6 +3,7 @@ set(LLVM_LINK_COMPONENTS Core ExecutionEngine IPO + JIT MC MCJIT ScalarOpts diff --git a/unittests/ExecutionEngine/MCJIT/MCJITMemoryManagerTest.cpp b/unittests/ExecutionEngine/MCJIT/MCJITMemoryManagerTest.cpp index 0582c92b5f8..98587f7e85c 100644 --- a/unittests/ExecutionEngine/MCJIT/MCJITMemoryManagerTest.cpp +++ b/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/unittests/ExecutionEngine/MCJIT/MCJITObjectCacheTest.cpp b/unittests/ExecutionEngine/MCJIT/MCJITObjectCacheTest.cpp index a245dbec999..fbbab42dbb0 100644 --- a/unittests/ExecutionEngine/MCJIT/MCJITObjectCacheTest.cpp +++ b/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/unittests/ExecutionEngine/MCJIT/MCJITTestBase.h b/unittests/ExecutionEngine/MCJIT/MCJITTestBase.h index 2c1d518da4c..25de312e0e8 100644 --- a/unittests/ExecutionEngine/MCJIT/MCJITTestBase.h +++ b/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/unittests/ExecutionEngine/MCJIT/Makefile b/unittests/ExecutionEngine/MCJIT/Makefile index 2822b20cdda..c4dd740e057 100644 --- a/unittests/ExecutionEngine/MCJIT/Makefile +++ b/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/unittests/ExecutionEngine/Makefile b/unittests/ExecutionEngine/Makefile index 8ecb883ba9d..38e667ff988 100644 --- a/unittests/ExecutionEngine/Makefile +++ b/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 |