mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2025-08-05 13:26:55 +00:00
Nuke the old JIT.
I am sure we will be finding bits and pieces of dead code for years to come, but this is a good start. Thanks to Lang Hames for making MCJIT a good replacement! git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@215111 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
@@ -10,10 +10,9 @@ add_llvm_unittest(ExecutionEngineTests
|
||||
ExecutionEngineTest.cpp
|
||||
)
|
||||
|
||||
# Include JIT/MCJIT tests only if native arch is a built JIT target.
|
||||
# Include 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()
|
||||
|
@@ -1,65 +0,0 @@
|
||||
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)
|
@@ -1,113 +0,0 @@
|
||||
//===- 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);
|
@@ -1,237 +0,0 @@
|
||||
//===- 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
|
@@ -1,207 +0,0 @@
|
||||
//===- 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
|
@@ -1,302 +0,0 @@
|
||||
//===- 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);
|
||||
}
|
||||
|
||||
}
|
@@ -1,728 +0,0 @@
|
||||
//===- 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__)
|
||||
|
||||
}
|
@@ -1,4 +0,0 @@
|
||||
EXPORTS
|
||||
getPointerToNamedFunction
|
||||
JITTest_AvailableExternallyFunction
|
||||
JITTest_AvailableExternallyGlobal
|
@@ -1,52 +0,0 @@
|
||||
##===- 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 $< > $@
|
||||
|
@@ -1,190 +0,0 @@
|
||||
//===- 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
|
@@ -1,165 +0,0 @@
|
||||
//===- 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);
|
@@ -3,7 +3,6 @@ set(LLVM_LINK_COMPONENTS
|
||||
Core
|
||||
ExecutionEngine
|
||||
IPO
|
||||
JIT
|
||||
MC
|
||||
MCJIT
|
||||
ScalarOpts
|
||||
|
@@ -8,7 +8,6 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/ExecutionEngine/SectionMemoryManager.h"
|
||||
#include "llvm/ExecutionEngine/JIT.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
@@ -11,7 +11,6 @@
|
||||
#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"
|
||||
|
@@ -317,11 +317,9 @@ 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)
|
||||
|
Reference in New Issue
Block a user