summaryrefslogtreecommitdiffstats
path: root/llvm/unittests/ExecutionEngine
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/unittests/ExecutionEngine')
-rw-r--r--llvm/unittests/ExecutionEngine/CMakeLists.txt3
-rw-r--r--llvm/unittests/ExecutionEngine/JIT/CMakeLists.txt65
-rw-r--r--llvm/unittests/ExecutionEngine/JIT/IntelJITEventListenerTest.cpp113
-rw-r--r--llvm/unittests/ExecutionEngine/JIT/JITEventListenerTest.cpp237
-rw-r--r--llvm/unittests/ExecutionEngine/JIT/JITEventListenerTestCommon.h207
-rw-r--r--llvm/unittests/ExecutionEngine/JIT/JITMemoryManagerTest.cpp302
-rw-r--r--llvm/unittests/ExecutionEngine/JIT/JITTest.cpp728
-rw-r--r--llvm/unittests/ExecutionEngine/JIT/JITTests.def4
-rw-r--r--llvm/unittests/ExecutionEngine/JIT/Makefile52
-rw-r--r--llvm/unittests/ExecutionEngine/JIT/MultiJITTest.cpp190
-rw-r--r--llvm/unittests/ExecutionEngine/JIT/OProfileJITEventListenerTest.cpp165
-rw-r--r--llvm/unittests/ExecutionEngine/MCJIT/CMakeLists.txt1
-rw-r--r--llvm/unittests/ExecutionEngine/MCJIT/MCJITMemoryManagerTest.cpp1
-rw-r--r--llvm/unittests/ExecutionEngine/MCJIT/MCJITObjectCacheTest.cpp1
-rw-r--r--llvm/unittests/ExecutionEngine/MCJIT/MCJITTestBase.h2
-rw-r--r--llvm/unittests/ExecutionEngine/MCJIT/Makefile2
-rw-r--r--llvm/unittests/ExecutionEngine/Makefile2
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
OpenPOWER on IntegriCloud