You've already forked linux-packaging-mono
Imported Upstream version 5.18.0.167
Former-commit-id: 289509151e0fee68a1b591a20c9f109c3c789d3a
This commit is contained in:
parent
e19d552987
commit
b084638f15
@ -1,22 +0,0 @@
|
||||
set(LLVM_LINK_COMPONENTS
|
||||
Core
|
||||
ExecutionEngine
|
||||
Interpreter
|
||||
MC
|
||||
OrcJIT
|
||||
RuntimeDyld
|
||||
Support
|
||||
)
|
||||
|
||||
add_llvm_unittest(ExecutionEngineTests
|
||||
ExecutionEngineTest.cpp
|
||||
)
|
||||
|
||||
add_subdirectory(Orc)
|
||||
|
||||
# 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(MCJIT)
|
||||
endif()
|
@ -1,152 +0,0 @@
|
||||
//===- ExecutionEngineTest.cpp - Unit tests for ExecutionEngine -----------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ExecutionEngine/Interpreter.h"
|
||||
#include "llvm/ExecutionEngine/RTDyldMemoryManager.h"
|
||||
#include "llvm/IR/DerivedTypes.h"
|
||||
#include "llvm/IR/GlobalVariable.h"
|
||||
#include "llvm/IR/LLVMContext.h"
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/Support/DynamicLibrary.h"
|
||||
#include "llvm/Support/ManagedStatic.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
namespace {
|
||||
|
||||
class ExecutionEngineTest : public testing::Test {
|
||||
private:
|
||||
llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
|
||||
|
||||
protected:
|
||||
ExecutionEngineTest() {
|
||||
auto Owner = make_unique<Module>("<main>", Context);
|
||||
M = Owner.get();
|
||||
Engine.reset(EngineBuilder(std::move(Owner)).setErrorStr(&Error).create());
|
||||
}
|
||||
|
||||
void SetUp() override {
|
||||
ASSERT_TRUE(Engine.get() != nullptr) << "EngineBuilder returned error: '"
|
||||
<< Error << "'";
|
||||
}
|
||||
|
||||
GlobalVariable *NewExtGlobal(Type *T, const Twine &Name) {
|
||||
return new GlobalVariable(*M, T, false, // Not constant.
|
||||
GlobalValue::ExternalLinkage, nullptr, Name);
|
||||
}
|
||||
|
||||
std::string Error;
|
||||
LLVMContext Context;
|
||||
Module *M; // Owned by ExecutionEngine.
|
||||
std::unique_ptr<ExecutionEngine> Engine;
|
||||
};
|
||||
|
||||
TEST_F(ExecutionEngineTest, ForwardGlobalMapping) {
|
||||
GlobalVariable *G1 = NewExtGlobal(Type::getInt32Ty(Context), "Global1");
|
||||
int32_t Mem1 = 3;
|
||||
Engine->addGlobalMapping(G1, &Mem1);
|
||||
EXPECT_EQ(&Mem1, Engine->getPointerToGlobalIfAvailable(G1));
|
||||
EXPECT_EQ(&Mem1, Engine->getPointerToGlobalIfAvailable("Global1"));
|
||||
int32_t Mem2 = 4;
|
||||
Engine->updateGlobalMapping(G1, &Mem2);
|
||||
EXPECT_EQ(&Mem2, Engine->getPointerToGlobalIfAvailable(G1));
|
||||
Engine->updateGlobalMapping(G1, nullptr);
|
||||
EXPECT_EQ(nullptr, Engine->getPointerToGlobalIfAvailable(G1));
|
||||
Engine->updateGlobalMapping(G1, &Mem2);
|
||||
EXPECT_EQ(&Mem2, Engine->getPointerToGlobalIfAvailable(G1));
|
||||
|
||||
GlobalVariable *G2 = NewExtGlobal(Type::getInt32Ty(Context), "Global1");
|
||||
EXPECT_EQ(nullptr, Engine->getPointerToGlobalIfAvailable(G2))
|
||||
<< "The NULL return shouldn't depend on having called"
|
||||
<< " updateGlobalMapping(..., NULL)";
|
||||
// Check that update...() can be called before add...().
|
||||
Engine->updateGlobalMapping(G2, &Mem1);
|
||||
EXPECT_EQ(&Mem1, Engine->getPointerToGlobalIfAvailable(G2));
|
||||
EXPECT_EQ(&Mem2, Engine->getPointerToGlobalIfAvailable(G1))
|
||||
<< "A second mapping shouldn't affect the first.";
|
||||
}
|
||||
|
||||
TEST_F(ExecutionEngineTest, ReverseGlobalMapping) {
|
||||
GlobalVariable *G1 = NewExtGlobal(Type::getInt32Ty(Context), "Global1");
|
||||
|
||||
int32_t Mem1 = 3;
|
||||
Engine->addGlobalMapping(G1, &Mem1);
|
||||
EXPECT_EQ(G1, Engine->getGlobalValueAtAddress(&Mem1));
|
||||
int32_t Mem2 = 4;
|
||||
Engine->updateGlobalMapping(G1, &Mem2);
|
||||
EXPECT_EQ(nullptr, Engine->getGlobalValueAtAddress(&Mem1));
|
||||
EXPECT_EQ(G1, Engine->getGlobalValueAtAddress(&Mem2));
|
||||
|
||||
GlobalVariable *G2 = NewExtGlobal(Type::getInt32Ty(Context), "Global2");
|
||||
Engine->updateGlobalMapping(G2, &Mem1);
|
||||
EXPECT_EQ(G2, Engine->getGlobalValueAtAddress(&Mem1));
|
||||
EXPECT_EQ(G1, Engine->getGlobalValueAtAddress(&Mem2));
|
||||
Engine->updateGlobalMapping(G1, nullptr);
|
||||
EXPECT_EQ(G2, Engine->getGlobalValueAtAddress(&Mem1))
|
||||
<< "Removing one mapping doesn't affect a different one.";
|
||||
EXPECT_EQ(nullptr, Engine->getGlobalValueAtAddress(&Mem2));
|
||||
Engine->updateGlobalMapping(G2, &Mem2);
|
||||
EXPECT_EQ(nullptr, Engine->getGlobalValueAtAddress(&Mem1));
|
||||
EXPECT_EQ(G2, Engine->getGlobalValueAtAddress(&Mem2))
|
||||
<< "Once a mapping is removed, we can point another GV at the"
|
||||
<< " now-free address.";
|
||||
}
|
||||
|
||||
TEST_F(ExecutionEngineTest, ClearModuleMappings) {
|
||||
GlobalVariable *G1 = NewExtGlobal(Type::getInt32Ty(Context), "Global1");
|
||||
|
||||
int32_t Mem1 = 3;
|
||||
Engine->addGlobalMapping(G1, &Mem1);
|
||||
EXPECT_EQ(G1, Engine->getGlobalValueAtAddress(&Mem1));
|
||||
|
||||
Engine->clearGlobalMappingsFromModule(M);
|
||||
|
||||
EXPECT_EQ(nullptr, Engine->getGlobalValueAtAddress(&Mem1));
|
||||
|
||||
GlobalVariable *G2 = NewExtGlobal(Type::getInt32Ty(Context), "Global2");
|
||||
// After clearing the module mappings, we can assign a new GV to the
|
||||
// same address.
|
||||
Engine->addGlobalMapping(G2, &Mem1);
|
||||
EXPECT_EQ(G2, Engine->getGlobalValueAtAddress(&Mem1));
|
||||
}
|
||||
|
||||
TEST_F(ExecutionEngineTest, DestructionRemovesGlobalMapping) {
|
||||
GlobalVariable *G1 = NewExtGlobal(Type::getInt32Ty(Context), "Global1");
|
||||
int32_t Mem1 = 3;
|
||||
Engine->addGlobalMapping(G1, &Mem1);
|
||||
// Make sure the reverse mapping is enabled.
|
||||
EXPECT_EQ(G1, Engine->getGlobalValueAtAddress(&Mem1));
|
||||
// When the GV goes away, the ExecutionEngine should remove any
|
||||
// mappings that refer to it.
|
||||
G1->eraseFromParent();
|
||||
EXPECT_EQ(nullptr, Engine->getGlobalValueAtAddress(&Mem1));
|
||||
}
|
||||
|
||||
TEST_F(ExecutionEngineTest, LookupWithMangledAndDemangledSymbol) {
|
||||
int x;
|
||||
int _x;
|
||||
llvm::sys::DynamicLibrary::AddSymbol("x", &x);
|
||||
llvm::sys::DynamicLibrary::AddSymbol("_x", &_x);
|
||||
|
||||
// RTDyldMemoryManager::getSymbolAddressInProcess expects a mangled symbol,
|
||||
// but DynamicLibrary is a wrapper for dlsym, which expects the unmangled C
|
||||
// symbol name. This test verifies that getSymbolAddressInProcess strips the
|
||||
// leading '_' on Darwin, but not on other platforms.
|
||||
#ifdef __APPLE__
|
||||
EXPECT_EQ(reinterpret_cast<uint64_t>(&x),
|
||||
RTDyldMemoryManager::getSymbolAddressInProcess("_x"));
|
||||
#else
|
||||
EXPECT_EQ(reinterpret_cast<uint64_t>(&_x),
|
||||
RTDyldMemoryManager::getSymbolAddressInProcess("_x"));
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
set(LLVM_LINK_COMPONENTS
|
||||
Analysis
|
||||
Core
|
||||
ExecutionEngine
|
||||
IPO
|
||||
MC
|
||||
MCJIT
|
||||
RuntimeDyld
|
||||
ScalarOpts
|
||||
Support
|
||||
Target
|
||||
nativecodegen
|
||||
)
|
||||
|
||||
set(MCJITTestsSources
|
||||
MCJITTest.cpp
|
||||
MCJITCAPITest.cpp
|
||||
MCJITMemoryManagerTest.cpp
|
||||
MCJITMultipleModuleTest.cpp
|
||||
MCJITObjectCacheTest.cpp
|
||||
)
|
||||
|
||||
if(MSVC)
|
||||
list(APPEND MCJITTestsSources MCJITTests.def)
|
||||
endif()
|
||||
|
||||
add_llvm_unittest(MCJITTests
|
||||
${MCJITTestsSources}
|
||||
)
|
||||
|
||||
if(MINGW OR CYGWIN)
|
||||
set_property(TARGET MCJITTests PROPERTY LINK_FLAGS -Wl,--export-all-symbols)
|
||||
endif()
|
File diff suppressed because it is too large
Load Diff
@ -1,170 +0,0 @@
|
||||
//===- MCJITMemoryManagerTest.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/SectionMemoryManager.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
namespace {
|
||||
|
||||
TEST(MCJITMemoryManagerTest, BasicAllocations) {
|
||||
std::unique_ptr<SectionMemoryManager> MemMgr(new SectionMemoryManager());
|
||||
|
||||
uint8_t *code1 = MemMgr->allocateCodeSection(256, 0, 1, "");
|
||||
uint8_t *data1 = MemMgr->allocateDataSection(256, 0, 2, "", true);
|
||||
uint8_t *code2 = MemMgr->allocateCodeSection(256, 0, 3, "");
|
||||
uint8_t *data2 = MemMgr->allocateDataSection(256, 0, 4, "", false);
|
||||
|
||||
EXPECT_NE((uint8_t*)nullptr, code1);
|
||||
EXPECT_NE((uint8_t*)nullptr, code2);
|
||||
EXPECT_NE((uint8_t*)nullptr, data1);
|
||||
EXPECT_NE((uint8_t*)nullptr, data2);
|
||||
|
||||
// Initialize the data
|
||||
for (unsigned i = 0; i < 256; ++i) {
|
||||
code1[i] = 1;
|
||||
code2[i] = 2;
|
||||
data1[i] = 3;
|
||||
data2[i] = 4;
|
||||
}
|
||||
|
||||
// Verify the data (this is checking for overlaps in the addresses)
|
||||
for (unsigned i = 0; i < 256; ++i) {
|
||||
EXPECT_EQ(1, code1[i]);
|
||||
EXPECT_EQ(2, code2[i]);
|
||||
EXPECT_EQ(3, data1[i]);
|
||||
EXPECT_EQ(4, data2[i]);
|
||||
}
|
||||
|
||||
std::string Error;
|
||||
EXPECT_FALSE(MemMgr->finalizeMemory(&Error));
|
||||
}
|
||||
|
||||
TEST(MCJITMemoryManagerTest, LargeAllocations) {
|
||||
std::unique_ptr<SectionMemoryManager> MemMgr(new SectionMemoryManager());
|
||||
|
||||
uint8_t *code1 = MemMgr->allocateCodeSection(0x100000, 0, 1, "");
|
||||
uint8_t *data1 = MemMgr->allocateDataSection(0x100000, 0, 2, "", true);
|
||||
uint8_t *code2 = MemMgr->allocateCodeSection(0x100000, 0, 3, "");
|
||||
uint8_t *data2 = MemMgr->allocateDataSection(0x100000, 0, 4, "", false);
|
||||
|
||||
EXPECT_NE((uint8_t*)nullptr, code1);
|
||||
EXPECT_NE((uint8_t*)nullptr, code2);
|
||||
EXPECT_NE((uint8_t*)nullptr, data1);
|
||||
EXPECT_NE((uint8_t*)nullptr, data2);
|
||||
|
||||
// Initialize the data
|
||||
for (unsigned i = 0; i < 0x100000; ++i) {
|
||||
code1[i] = 1;
|
||||
code2[i] = 2;
|
||||
data1[i] = 3;
|
||||
data2[i] = 4;
|
||||
}
|
||||
|
||||
// Verify the data (this is checking for overlaps in the addresses)
|
||||
for (unsigned i = 0; i < 0x100000; ++i) {
|
||||
EXPECT_EQ(1, code1[i]);
|
||||
EXPECT_EQ(2, code2[i]);
|
||||
EXPECT_EQ(3, data1[i]);
|
||||
EXPECT_EQ(4, data2[i]);
|
||||
}
|
||||
|
||||
std::string Error;
|
||||
EXPECT_FALSE(MemMgr->finalizeMemory(&Error));
|
||||
}
|
||||
|
||||
TEST(MCJITMemoryManagerTest, ManyAllocations) {
|
||||
std::unique_ptr<SectionMemoryManager> MemMgr(new SectionMemoryManager());
|
||||
|
||||
uint8_t* code[10000];
|
||||
uint8_t* data[10000];
|
||||
|
||||
for (unsigned i = 0; i < 10000; ++i) {
|
||||
const bool isReadOnly = i % 2 == 0;
|
||||
|
||||
code[i] = MemMgr->allocateCodeSection(32, 0, 1, "");
|
||||
data[i] = MemMgr->allocateDataSection(32, 0, 2, "", isReadOnly);
|
||||
|
||||
for (unsigned j = 0; j < 32; j++) {
|
||||
code[i][j] = 1 + (i % 254);
|
||||
data[i][j] = 2 + (i % 254);
|
||||
}
|
||||
|
||||
EXPECT_NE((uint8_t *)nullptr, code[i]);
|
||||
EXPECT_NE((uint8_t *)nullptr, data[i]);
|
||||
}
|
||||
|
||||
// Verify the data (this is checking for overlaps in the addresses)
|
||||
for (unsigned i = 0; i < 10000; ++i) {
|
||||
for (unsigned j = 0; j < 32;j++ ) {
|
||||
uint8_t ExpectedCode = 1 + (i % 254);
|
||||
uint8_t ExpectedData = 2 + (i % 254);
|
||||
EXPECT_EQ(ExpectedCode, code[i][j]);
|
||||
EXPECT_EQ(ExpectedData, data[i][j]);
|
||||
}
|
||||
}
|
||||
|
||||
std::string Error;
|
||||
EXPECT_FALSE(MemMgr->finalizeMemory(&Error));
|
||||
}
|
||||
|
||||
TEST(MCJITMemoryManagerTest, ManyVariedAllocations) {
|
||||
std::unique_ptr<SectionMemoryManager> MemMgr(new SectionMemoryManager());
|
||||
|
||||
uint8_t* code[10000];
|
||||
uint8_t* data[10000];
|
||||
|
||||
for (unsigned i = 0; i < 10000; ++i) {
|
||||
uintptr_t CodeSize = i % 16 + 1;
|
||||
uintptr_t DataSize = i % 8 + 1;
|
||||
|
||||
bool isReadOnly = i % 3 == 0;
|
||||
unsigned Align = 8 << (i % 4);
|
||||
|
||||
code[i] = MemMgr->allocateCodeSection(CodeSize, Align, i, "");
|
||||
data[i] = MemMgr->allocateDataSection(DataSize, Align, i + 10000, "",
|
||||
isReadOnly);
|
||||
|
||||
for (unsigned j = 0; j < CodeSize; j++) {
|
||||
code[i][j] = 1 + (i % 254);
|
||||
}
|
||||
|
||||
for (unsigned j = 0; j < DataSize; j++) {
|
||||
data[i][j] = 2 + (i % 254);
|
||||
}
|
||||
|
||||
EXPECT_NE((uint8_t *)nullptr, code[i]);
|
||||
EXPECT_NE((uint8_t *)nullptr, data[i]);
|
||||
|
||||
uintptr_t CodeAlign = Align ? (uintptr_t)code[i] % Align : 0;
|
||||
uintptr_t DataAlign = Align ? (uintptr_t)data[i] % Align : 0;
|
||||
|
||||
EXPECT_EQ((uintptr_t)0, CodeAlign);
|
||||
EXPECT_EQ((uintptr_t)0, DataAlign);
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < 10000; ++i) {
|
||||
uintptr_t CodeSize = i % 16 + 1;
|
||||
uintptr_t DataSize = i % 8 + 1;
|
||||
|
||||
for (unsigned j = 0; j < CodeSize; j++) {
|
||||
uint8_t ExpectedCode = 1 + (i % 254);
|
||||
EXPECT_EQ(ExpectedCode, code[i][j]);
|
||||
}
|
||||
|
||||
for (unsigned j = 0; j < DataSize; j++) {
|
||||
uint8_t ExpectedData = 2 + (i % 254);
|
||||
EXPECT_EQ(ExpectedData, data[i][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // Namespace
|
||||
|
@ -1,423 +0,0 @@
|
||||
//===- MCJITMultipeModuleTest.cpp - Unit tests for the MCJIT ----*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This test suite verifies MCJIT for handling multiple modules in a single
|
||||
// ExecutionEngine by building multiple modules, making function calls across
|
||||
// modules, accessing global variables, etc.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "MCJITTestBase.h"
|
||||
#include "llvm/ExecutionEngine/MCJIT.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
namespace {
|
||||
|
||||
class MCJITMultipleModuleTest : public testing::Test, public MCJITTestBase {};
|
||||
|
||||
// FIXME: ExecutionEngine has no support empty modules
|
||||
/*
|
||||
TEST_F(MCJITMultipleModuleTest, multiple_empty_modules) {
|
||||
SKIP_UNSUPPORTED_PLATFORM;
|
||||
|
||||
createJIT(M.take());
|
||||
// JIT-compile
|
||||
EXPECT_NE(0, TheJIT->getObjectImage())
|
||||
<< "Unable to generate executable loaded object image";
|
||||
|
||||
TheJIT->addModule(createEmptyModule("<other module>"));
|
||||
TheJIT->addModule(createEmptyModule("<other other module>"));
|
||||
|
||||
// JIT again
|
||||
EXPECT_NE(0, TheJIT->getObjectImage())
|
||||
<< "Unable to generate executable loaded object image";
|
||||
}
|
||||
*/
|
||||
|
||||
// Helper Function to test add operation
|
||||
void checkAdd(uint64_t ptr) {
|
||||
ASSERT_TRUE(ptr != 0) << "Unable to get pointer to function.";
|
||||
int (*AddPtr)(int, int) = (int (*)(int, int))ptr;
|
||||
EXPECT_EQ(0, AddPtr(0, 0));
|
||||
EXPECT_EQ(1, AddPtr(1, 0));
|
||||
EXPECT_EQ(3, AddPtr(1, 2));
|
||||
EXPECT_EQ(-5, AddPtr(-2, -3));
|
||||
EXPECT_EQ(30, AddPtr(10, 20));
|
||||
EXPECT_EQ(-30, AddPtr(-10, -20));
|
||||
EXPECT_EQ(-40, AddPtr(-10, -30));
|
||||
}
|
||||
|
||||
void checkAccumulate(uint64_t ptr) {
|
||||
ASSERT_TRUE(ptr != 0) << "Unable to get pointer to function.";
|
||||
int32_t (*FPtr)(int32_t) = (int32_t (*)(int32_t))(intptr_t)ptr;
|
||||
EXPECT_EQ(0, FPtr(0));
|
||||
EXPECT_EQ(1, FPtr(1));
|
||||
EXPECT_EQ(3, FPtr(2));
|
||||
EXPECT_EQ(6, FPtr(3));
|
||||
EXPECT_EQ(10, FPtr(4));
|
||||
EXPECT_EQ(15, FPtr(5));
|
||||
}
|
||||
|
||||
// FIXME: ExecutionEngine has no support empty modules
|
||||
/*
|
||||
TEST_F(MCJITMultipleModuleTest, multiple_empty_modules) {
|
||||
SKIP_UNSUPPORTED_PLATFORM;
|
||||
|
||||
createJIT(M.take());
|
||||
// JIT-compile
|
||||
EXPECT_NE(0, TheJIT->getObjectImage())
|
||||
<< "Unable to generate executable loaded object image";
|
||||
|
||||
TheJIT->addModule(createEmptyModule("<other module>"));
|
||||
TheJIT->addModule(createEmptyModule("<other other module>"));
|
||||
|
||||
// JIT again
|
||||
EXPECT_NE(0, TheJIT->getObjectImage())
|
||||
<< "Unable to generate executable loaded object image";
|
||||
}
|
||||
*/
|
||||
|
||||
// Module A { Function FA },
|
||||
// Module B { Function FB },
|
||||
// execute FA then FB
|
||||
TEST_F(MCJITMultipleModuleTest, two_module_case) {
|
||||
SKIP_UNSUPPORTED_PLATFORM;
|
||||
|
||||
std::unique_ptr<Module> A, B;
|
||||
Function *FA, *FB;
|
||||
createTwoModuleCase(A, FA, B, FB);
|
||||
|
||||
createJIT(std::move(A));
|
||||
TheJIT->addModule(std::move(B));
|
||||
|
||||
uint64_t ptr = TheJIT->getFunctionAddress(FA->getName().str());
|
||||
checkAdd(ptr);
|
||||
|
||||
ptr = TheJIT->getFunctionAddress(FB->getName().str());
|
||||
checkAdd(ptr);
|
||||
}
|
||||
|
||||
// Module A { Function FA },
|
||||
// Module B { Function FB },
|
||||
// execute FB then FA
|
||||
TEST_F(MCJITMultipleModuleTest, two_module_reverse_case) {
|
||||
SKIP_UNSUPPORTED_PLATFORM;
|
||||
|
||||
std::unique_ptr<Module> A, B;
|
||||
Function *FA, *FB;
|
||||
createTwoModuleCase(A, FA, B, FB);
|
||||
|
||||
createJIT(std::move(A));
|
||||
TheJIT->addModule(std::move(B));
|
||||
|
||||
uint64_t ptr = TheJIT->getFunctionAddress(FB->getName().str());
|
||||
TheJIT->finalizeObject();
|
||||
checkAdd(ptr);
|
||||
|
||||
ptr = TheJIT->getFunctionAddress(FA->getName().str());
|
||||
checkAdd(ptr);
|
||||
}
|
||||
|
||||
// Module A { Function FA },
|
||||
// Module B { Extern FA, Function FB which calls FA },
|
||||
// execute FB then FA
|
||||
TEST_F(MCJITMultipleModuleTest, two_module_extern_reverse_case) {
|
||||
SKIP_UNSUPPORTED_PLATFORM;
|
||||
|
||||
std::unique_ptr<Module> A, B;
|
||||
Function *FA, *FB;
|
||||
createTwoModuleExternCase(A, FA, B, FB);
|
||||
|
||||
createJIT(std::move(A));
|
||||
TheJIT->addModule(std::move(B));
|
||||
|
||||
uint64_t ptr = TheJIT->getFunctionAddress(FB->getName().str());
|
||||
TheJIT->finalizeObject();
|
||||
checkAdd(ptr);
|
||||
|
||||
ptr = TheJIT->getFunctionAddress(FA->getName().str());
|
||||
checkAdd(ptr);
|
||||
}
|
||||
|
||||
// Module A { Function FA },
|
||||
// Module B { Extern FA, Function FB which calls FA },
|
||||
// execute FA then FB
|
||||
TEST_F(MCJITMultipleModuleTest, two_module_extern_case) {
|
||||
SKIP_UNSUPPORTED_PLATFORM;
|
||||
|
||||
std::unique_ptr<Module> A, B;
|
||||
Function *FA, *FB;
|
||||
createTwoModuleExternCase(A, FA, B, FB);
|
||||
|
||||
createJIT(std::move(A));
|
||||
TheJIT->addModule(std::move(B));
|
||||
|
||||
uint64_t ptr = TheJIT->getFunctionAddress(FA->getName().str());
|
||||
checkAdd(ptr);
|
||||
|
||||
ptr = TheJIT->getFunctionAddress(FB->getName().str());
|
||||
checkAdd(ptr);
|
||||
}
|
||||
|
||||
// Module A { Function FA1, Function FA2 which calls FA1 },
|
||||
// Module B { Extern FA1, Function FB which calls FA1 },
|
||||
// execute FB then FA2
|
||||
TEST_F(MCJITMultipleModuleTest, two_module_consecutive_call_case) {
|
||||
SKIP_UNSUPPORTED_PLATFORM;
|
||||
|
||||
std::unique_ptr<Module> A, B;
|
||||
Function *FA1, *FA2, *FB;
|
||||
createTwoModuleExternCase(A, FA1, B, FB);
|
||||
FA2 = insertSimpleCallFunction<int32_t(int32_t, int32_t)>(A.get(), FA1);
|
||||
|
||||
createJIT(std::move(A));
|
||||
TheJIT->addModule(std::move(B));
|
||||
|
||||
uint64_t ptr = TheJIT->getFunctionAddress(FB->getName().str());
|
||||
TheJIT->finalizeObject();
|
||||
checkAdd(ptr);
|
||||
|
||||
ptr = TheJIT->getFunctionAddress(FA2->getName().str());
|
||||
checkAdd(ptr);
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// Module A { Extern Global GVB, Global Variable GVA, Function FA loads GVB },
|
||||
// Module B { Extern Global GVA, Global Variable GVB, Function FB loads GVA },
|
||||
|
||||
|
||||
// Module A { Global Variable GVA, Function FA loads GVA },
|
||||
// Module B { Global Variable GVB, Internal Global GVC, Function FB loads GVB },
|
||||
// execute FB then FA, also check that the global variables are properly accesible
|
||||
// through the ExecutionEngine APIs
|
||||
TEST_F(MCJITMultipleModuleTest, two_module_global_variables_case) {
|
||||
SKIP_UNSUPPORTED_PLATFORM;
|
||||
|
||||
std::unique_ptr<Module> A, B;
|
||||
Function *FA, *FB;
|
||||
GlobalVariable *GVA, *GVB, *GVC;
|
||||
A.reset(createEmptyModule("A"));
|
||||
B.reset(createEmptyModule("B"));
|
||||
|
||||
int32_t initialNum = 7;
|
||||
GVA = insertGlobalInt32(A.get(), "GVA", initialNum);
|
||||
GVB = insertGlobalInt32(B.get(), "GVB", initialNum);
|
||||
FA = startFunction<int32_t(void)>(A.get(), "FA");
|
||||
endFunctionWithRet(FA, Builder.CreateLoad(GVA));
|
||||
FB = startFunction<int32_t(void)>(B.get(), "FB");
|
||||
endFunctionWithRet(FB, Builder.CreateLoad(GVB));
|
||||
|
||||
GVC = insertGlobalInt32(B.get(), "GVC", initialNum);
|
||||
GVC->setLinkage(GlobalValue::InternalLinkage);
|
||||
|
||||
createJIT(std::move(A));
|
||||
TheJIT->addModule(std::move(B));
|
||||
|
||||
EXPECT_EQ(GVA, TheJIT->FindGlobalVariableNamed("GVA"));
|
||||
EXPECT_EQ(GVB, TheJIT->FindGlobalVariableNamed("GVB"));
|
||||
EXPECT_EQ(GVC, TheJIT->FindGlobalVariableNamed("GVC",true));
|
||||
EXPECT_EQ(nullptr, TheJIT->FindGlobalVariableNamed("GVC"));
|
||||
|
||||
uint64_t FBPtr = TheJIT->getFunctionAddress(FB->getName().str());
|
||||
TheJIT->finalizeObject();
|
||||
EXPECT_TRUE(0 != FBPtr);
|
||||
int32_t(*FuncPtr)() = (int32_t(*)())FBPtr;
|
||||
EXPECT_EQ(initialNum, FuncPtr())
|
||||
<< "Invalid value for global returned from JITted function in module B";
|
||||
|
||||
uint64_t FAPtr = TheJIT->getFunctionAddress(FA->getName().str());
|
||||
EXPECT_TRUE(0 != FAPtr);
|
||||
FuncPtr = (int32_t(*)())FAPtr;
|
||||
EXPECT_EQ(initialNum, FuncPtr())
|
||||
<< "Invalid value for global returned from JITted function in module A";
|
||||
}
|
||||
|
||||
// Module A { Function FA },
|
||||
// Module B { Extern FA, Function FB which calls FA },
|
||||
// Module C { Extern FA, Function FC which calls FA },
|
||||
// execute FC, FB, FA
|
||||
TEST_F(MCJITMultipleModuleTest, three_module_case) {
|
||||
SKIP_UNSUPPORTED_PLATFORM;
|
||||
|
||||
std::unique_ptr<Module> A, B, C;
|
||||
Function *FA, *FB, *FC;
|
||||
createThreeModuleCase(A, FA, B, FB, C, FC);
|
||||
|
||||
createJIT(std::move(A));
|
||||
TheJIT->addModule(std::move(B));
|
||||
TheJIT->addModule(std::move(C));
|
||||
|
||||
uint64_t ptr = TheJIT->getFunctionAddress(FC->getName().str());
|
||||
checkAdd(ptr);
|
||||
|
||||
ptr = TheJIT->getFunctionAddress(FB->getName().str());
|
||||
checkAdd(ptr);
|
||||
|
||||
ptr = TheJIT->getFunctionAddress(FA->getName().str());
|
||||
checkAdd(ptr);
|
||||
}
|
||||
|
||||
// Module A { Function FA },
|
||||
// Module B { Extern FA, Function FB which calls FA },
|
||||
// Module C { Extern FA, Function FC which calls FA },
|
||||
// execute FA, FB, FC
|
||||
TEST_F(MCJITMultipleModuleTest, three_module_case_reverse_order) {
|
||||
SKIP_UNSUPPORTED_PLATFORM;
|
||||
|
||||
std::unique_ptr<Module> A, B, C;
|
||||
Function *FA, *FB, *FC;
|
||||
createThreeModuleCase(A, FA, B, FB, C, FC);
|
||||
|
||||
createJIT(std::move(A));
|
||||
TheJIT->addModule(std::move(B));
|
||||
TheJIT->addModule(std::move(C));
|
||||
|
||||
uint64_t ptr = TheJIT->getFunctionAddress(FA->getName().str());
|
||||
checkAdd(ptr);
|
||||
|
||||
ptr = TheJIT->getFunctionAddress(FB->getName().str());
|
||||
checkAdd(ptr);
|
||||
|
||||
ptr = TheJIT->getFunctionAddress(FC->getName().str());
|
||||
checkAdd(ptr);
|
||||
}
|
||||
|
||||
// Module A { Function FA },
|
||||
// Module B { Extern FA, Function FB which calls FA },
|
||||
// Module C { Extern FB, Function FC which calls FB },
|
||||
// execute FC, FB, FA
|
||||
TEST_F(MCJITMultipleModuleTest, three_module_chain_case) {
|
||||
SKIP_UNSUPPORTED_PLATFORM;
|
||||
|
||||
std::unique_ptr<Module> A, B, C;
|
||||
Function *FA, *FB, *FC;
|
||||
createThreeModuleChainedCallsCase(A, FA, B, FB, C, FC);
|
||||
|
||||
createJIT(std::move(A));
|
||||
TheJIT->addModule(std::move(B));
|
||||
TheJIT->addModule(std::move(C));
|
||||
|
||||
uint64_t ptr = TheJIT->getFunctionAddress(FC->getName().str());
|
||||
checkAdd(ptr);
|
||||
|
||||
ptr = TheJIT->getFunctionAddress(FB->getName().str());
|
||||
checkAdd(ptr);
|
||||
|
||||
ptr = TheJIT->getFunctionAddress(FA->getName().str());
|
||||
checkAdd(ptr);
|
||||
}
|
||||
|
||||
// Module A { Function FA },
|
||||
// Module B { Extern FA, Function FB which calls FA },
|
||||
// Module C { Extern FB, Function FC which calls FB },
|
||||
// execute FA, FB, FC
|
||||
TEST_F(MCJITMultipleModuleTest, three_modules_chain_case_reverse_order) {
|
||||
SKIP_UNSUPPORTED_PLATFORM;
|
||||
|
||||
std::unique_ptr<Module> A, B, C;
|
||||
Function *FA, *FB, *FC;
|
||||
createThreeModuleChainedCallsCase(A, FA, B, FB, C, FC);
|
||||
|
||||
createJIT(std::move(A));
|
||||
TheJIT->addModule(std::move(B));
|
||||
TheJIT->addModule(std::move(C));
|
||||
|
||||
uint64_t ptr = TheJIT->getFunctionAddress(FA->getName().str());
|
||||
checkAdd(ptr);
|
||||
|
||||
ptr = TheJIT->getFunctionAddress(FB->getName().str());
|
||||
checkAdd(ptr);
|
||||
|
||||
ptr = TheJIT->getFunctionAddress(FC->getName().str());
|
||||
checkAdd(ptr);
|
||||
}
|
||||
|
||||
// Module A { Extern FB, Function FA which calls FB1 },
|
||||
// Module B { Extern FA, Function FB1, Function FB2 which calls FA },
|
||||
// execute FA, then FB1
|
||||
// FIXME: this test case is not supported by MCJIT
|
||||
TEST_F(MCJITMultipleModuleTest, cross_module_dependency_case) {
|
||||
SKIP_UNSUPPORTED_PLATFORM;
|
||||
|
||||
std::unique_ptr<Module> A, B;
|
||||
Function *FA, *FB1, *FB2;
|
||||
createCrossModuleRecursiveCase(A, FA, B, FB1, FB2);
|
||||
|
||||
createJIT(std::move(A));
|
||||
TheJIT->addModule(std::move(B));
|
||||
|
||||
uint64_t ptr = TheJIT->getFunctionAddress(FA->getName().str());
|
||||
checkAccumulate(ptr);
|
||||
|
||||
ptr = TheJIT->getFunctionAddress(FB1->getName().str());
|
||||
checkAccumulate(ptr);
|
||||
}
|
||||
|
||||
// Module A { Extern FB, Function FA which calls FB1 },
|
||||
// Module B { Extern FA, Function FB1, Function FB2 which calls FA },
|
||||
// execute FB1 then FA
|
||||
// FIXME: this test case is not supported by MCJIT
|
||||
TEST_F(MCJITMultipleModuleTest, cross_module_dependency_case_reverse_order) {
|
||||
SKIP_UNSUPPORTED_PLATFORM;
|
||||
|
||||
std::unique_ptr<Module> A, B;
|
||||
Function *FA, *FB1, *FB2;
|
||||
createCrossModuleRecursiveCase(A, FA, B, FB1, FB2);
|
||||
|
||||
createJIT(std::move(A));
|
||||
TheJIT->addModule(std::move(B));
|
||||
|
||||
uint64_t ptr = TheJIT->getFunctionAddress(FB1->getName().str());
|
||||
checkAccumulate(ptr);
|
||||
|
||||
ptr = TheJIT->getFunctionAddress(FA->getName().str());
|
||||
checkAccumulate(ptr);
|
||||
}
|
||||
|
||||
// Module A { Extern FB1, Function FA which calls FB1 },
|
||||
// Module B { Extern FA, Function FB1, Function FB2 which calls FA },
|
||||
// execute FB1 then FB2
|
||||
// FIXME: this test case is not supported by MCJIT
|
||||
TEST_F(MCJITMultipleModuleTest, cross_module_dependency_case3) {
|
||||
SKIP_UNSUPPORTED_PLATFORM;
|
||||
|
||||
std::unique_ptr<Module> A, B;
|
||||
Function *FA, *FB1, *FB2;
|
||||
createCrossModuleRecursiveCase(A, FA, B, FB1, FB2);
|
||||
|
||||
createJIT(std::move(A));
|
||||
TheJIT->addModule(std::move(B));
|
||||
|
||||
uint64_t ptr = TheJIT->getFunctionAddress(FB1->getName().str());
|
||||
checkAccumulate(ptr);
|
||||
|
||||
ptr = TheJIT->getFunctionAddress(FB2->getName().str());
|
||||
checkAccumulate(ptr);
|
||||
}
|
||||
|
||||
// Test that FindFunctionNamed finds the definition of
|
||||
// a function in the correct module. We check two functions
|
||||
// in two different modules, to make sure that for at least
|
||||
// one of them MCJIT had to ignore the extern declaration.
|
||||
TEST_F(MCJITMultipleModuleTest, FindFunctionNamed_test) {
|
||||
SKIP_UNSUPPORTED_PLATFORM;
|
||||
|
||||
std::unique_ptr<Module> A, B;
|
||||
Function *FA, *FB1, *FB2;
|
||||
createCrossModuleRecursiveCase(A, FA, B, FB1, FB2);
|
||||
|
||||
createJIT(std::move(A));
|
||||
TheJIT->addModule(std::move(B));
|
||||
|
||||
EXPECT_EQ(FA, TheJIT->FindFunctionNamed(FA->getName().data()));
|
||||
EXPECT_EQ(FB1, TheJIT->FindFunctionNamed(FB1->getName().data()));
|
||||
}
|
||||
|
||||
} // end anonymous namespace
|
@ -1,229 +0,0 @@
|
||||
//===- MCJITObjectCacheTest.cpp - Unit tests for MCJIT object caching -----===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "MCJITTestBase.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
#include "llvm/ADT/StringSet.h"
|
||||
#include "llvm/ExecutionEngine/MCJIT.h"
|
||||
#include "llvm/ExecutionEngine/ObjectCache.h"
|
||||
#include "llvm/ExecutionEngine/SectionMemoryManager.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
namespace {
|
||||
|
||||
class TestObjectCache : public ObjectCache {
|
||||
public:
|
||||
TestObjectCache() : DuplicateInserted(false) { }
|
||||
|
||||
void notifyObjectCompiled(const Module *M, MemoryBufferRef Obj) override {
|
||||
// If we've seen this module before, note that.
|
||||
const std::string ModuleID = M->getModuleIdentifier();
|
||||
if (ObjMap.find(ModuleID) != ObjMap.end())
|
||||
DuplicateInserted = true;
|
||||
// Store a copy of the buffer in our map.
|
||||
ObjMap[ModuleID] = copyBuffer(Obj);
|
||||
}
|
||||
|
||||
std::unique_ptr<MemoryBuffer> getObject(const Module *M) override {
|
||||
const MemoryBuffer* BufferFound = getObjectInternal(M);
|
||||
ModulesLookedUp.insert(M->getModuleIdentifier());
|
||||
if (!BufferFound)
|
||||
return nullptr;
|
||||
// Our test cache wants to maintain ownership of its object buffers
|
||||
// so we make a copy here for the execution engine.
|
||||
return MemoryBuffer::getMemBufferCopy(BufferFound->getBuffer());
|
||||
}
|
||||
|
||||
// Test-harness-specific functions
|
||||
bool wereDuplicatesInserted() { return DuplicateInserted; }
|
||||
|
||||
bool wasModuleLookedUp(const Module *M) {
|
||||
return ModulesLookedUp.find(M->getModuleIdentifier())
|
||||
!= ModulesLookedUp.end();
|
||||
}
|
||||
|
||||
const MemoryBuffer* getObjectInternal(const Module* M) {
|
||||
// Look for the module in our map.
|
||||
const std::string ModuleID = M->getModuleIdentifier();
|
||||
StringMap<const MemoryBuffer *>::iterator it = ObjMap.find(ModuleID);
|
||||
if (it == ObjMap.end())
|
||||
return nullptr;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
private:
|
||||
MemoryBuffer *copyBuffer(MemoryBufferRef Buf) {
|
||||
// Create a local copy of the buffer.
|
||||
std::unique_ptr<MemoryBuffer> NewBuffer =
|
||||
MemoryBuffer::getMemBufferCopy(Buf.getBuffer());
|
||||
MemoryBuffer *Ret = NewBuffer.get();
|
||||
AllocatedBuffers.push_back(std::move(NewBuffer));
|
||||
return Ret;
|
||||
}
|
||||
|
||||
StringMap<const MemoryBuffer *> ObjMap;
|
||||
StringSet<> ModulesLookedUp;
|
||||
SmallVector<std::unique_ptr<MemoryBuffer>, 2> AllocatedBuffers;
|
||||
bool DuplicateInserted;
|
||||
};
|
||||
|
||||
class MCJITObjectCacheTest : public testing::Test, public MCJITTestBase {
|
||||
protected:
|
||||
enum {
|
||||
OriginalRC = 6,
|
||||
ReplacementRC = 7
|
||||
};
|
||||
|
||||
void SetUp() override {
|
||||
M.reset(createEmptyModule("<main>"));
|
||||
Main = insertMainFunction(M.get(), OriginalRC);
|
||||
}
|
||||
|
||||
void compileAndRun(int ExpectedRC = OriginalRC) {
|
||||
// This function shouldn't be called until after SetUp.
|
||||
ASSERT_TRUE(bool(TheJIT));
|
||||
ASSERT_TRUE(nullptr != Main);
|
||||
|
||||
// We may be using a null cache, so ensure compilation is valid.
|
||||
TheJIT->finalizeObject();
|
||||
void *vPtr = TheJIT->getPointerToFunction(Main);
|
||||
|
||||
EXPECT_TRUE(nullptr != vPtr)
|
||||
<< "Unable to get pointer to main() from JIT";
|
||||
|
||||
int (*FuncPtr)() = (int(*)())(intptr_t)vPtr;
|
||||
int returnCode = FuncPtr();
|
||||
EXPECT_EQ(returnCode, ExpectedRC);
|
||||
}
|
||||
|
||||
Function *Main;
|
||||
};
|
||||
|
||||
TEST_F(MCJITObjectCacheTest, SetNullObjectCache) {
|
||||
SKIP_UNSUPPORTED_PLATFORM;
|
||||
|
||||
createJIT(std::move(M));
|
||||
|
||||
TheJIT->setObjectCache(nullptr);
|
||||
|
||||
compileAndRun();
|
||||
}
|
||||
|
||||
TEST_F(MCJITObjectCacheTest, VerifyBasicObjectCaching) {
|
||||
SKIP_UNSUPPORTED_PLATFORM;
|
||||
|
||||
std::unique_ptr<TestObjectCache> Cache(new TestObjectCache);
|
||||
|
||||
// Save a copy of the module pointer before handing it off to MCJIT.
|
||||
const Module * SavedModulePointer = M.get();
|
||||
|
||||
createJIT(std::move(M));
|
||||
|
||||
TheJIT->setObjectCache(Cache.get());
|
||||
|
||||
// Verify that our object cache does not contain the module yet.
|
||||
const MemoryBuffer *ObjBuffer = Cache->getObjectInternal(SavedModulePointer);
|
||||
EXPECT_EQ(nullptr, ObjBuffer);
|
||||
|
||||
compileAndRun();
|
||||
|
||||
// Verify that MCJIT tried to look-up this module in the cache.
|
||||
EXPECT_TRUE(Cache->wasModuleLookedUp(SavedModulePointer));
|
||||
|
||||
// Verify that our object cache now contains the module.
|
||||
ObjBuffer = Cache->getObjectInternal(SavedModulePointer);
|
||||
EXPECT_TRUE(nullptr != ObjBuffer);
|
||||
|
||||
// Verify that the cache was only notified once.
|
||||
EXPECT_FALSE(Cache->wereDuplicatesInserted());
|
||||
}
|
||||
|
||||
TEST_F(MCJITObjectCacheTest, VerifyLoadFromCache) {
|
||||
SKIP_UNSUPPORTED_PLATFORM;
|
||||
|
||||
std::unique_ptr<TestObjectCache> Cache(new TestObjectCache);
|
||||
|
||||
// Compile this module with an MCJIT engine
|
||||
createJIT(std::move(M));
|
||||
TheJIT->setObjectCache(Cache.get());
|
||||
TheJIT->finalizeObject();
|
||||
|
||||
// Destroy the MCJIT engine we just used
|
||||
TheJIT.reset();
|
||||
|
||||
// Create a new memory manager.
|
||||
MM.reset(new SectionMemoryManager());
|
||||
|
||||
// Create a new module and save it. Use a different return code so we can
|
||||
// tell if MCJIT compiled this module or used the cache.
|
||||
M.reset(createEmptyModule("<main>"));
|
||||
Main = insertMainFunction(M.get(), ReplacementRC);
|
||||
const Module * SecondModulePointer = M.get();
|
||||
|
||||
// Create a new MCJIT instance to load this module then execute it.
|
||||
createJIT(std::move(M));
|
||||
TheJIT->setObjectCache(Cache.get());
|
||||
compileAndRun();
|
||||
|
||||
// Verify that MCJIT tried to look-up this module in the cache.
|
||||
EXPECT_TRUE(Cache->wasModuleLookedUp(SecondModulePointer));
|
||||
|
||||
// Verify that MCJIT didn't try to cache this again.
|
||||
EXPECT_FALSE(Cache->wereDuplicatesInserted());
|
||||
}
|
||||
|
||||
TEST_F(MCJITObjectCacheTest, VerifyNonLoadFromCache) {
|
||||
SKIP_UNSUPPORTED_PLATFORM;
|
||||
|
||||
std::unique_ptr<TestObjectCache> Cache(new TestObjectCache);
|
||||
|
||||
// Compile this module with an MCJIT engine
|
||||
createJIT(std::move(M));
|
||||
TheJIT->setObjectCache(Cache.get());
|
||||
TheJIT->finalizeObject();
|
||||
|
||||
// Destroy the MCJIT engine we just used
|
||||
TheJIT.reset();
|
||||
|
||||
// Create a new memory manager.
|
||||
MM.reset(new SectionMemoryManager());
|
||||
|
||||
// Create a new module and save it. Use a different return code so we can
|
||||
// tell if MCJIT compiled this module or used the cache. Note that we use
|
||||
// a new module name here so the module shouldn't be found in the cache.
|
||||
M.reset(createEmptyModule("<not-main>"));
|
||||
Main = insertMainFunction(M.get(), ReplacementRC);
|
||||
const Module * SecondModulePointer = M.get();
|
||||
|
||||
// Create a new MCJIT instance to load this module then execute it.
|
||||
createJIT(std::move(M));
|
||||
TheJIT->setObjectCache(Cache.get());
|
||||
|
||||
// Verify that our object cache does not contain the module yet.
|
||||
const MemoryBuffer *ObjBuffer = Cache->getObjectInternal(SecondModulePointer);
|
||||
EXPECT_EQ(nullptr, ObjBuffer);
|
||||
|
||||
// Run the function and look for the replacement return code.
|
||||
compileAndRun(ReplacementRC);
|
||||
|
||||
// Verify that MCJIT tried to look-up this module in the cache.
|
||||
EXPECT_TRUE(Cache->wasModuleLookedUp(SecondModulePointer));
|
||||
|
||||
// Verify that our object cache now contains the module.
|
||||
ObjBuffer = Cache->getObjectInternal(SecondModulePointer);
|
||||
EXPECT_TRUE(nullptr != ObjBuffer);
|
||||
|
||||
// Verify that MCJIT didn't try to cache this again.
|
||||
EXPECT_FALSE(Cache->wereDuplicatesInserted());
|
||||
}
|
||||
|
||||
} // end anonymous namespace
|
@ -1,283 +0,0 @@
|
||||
//===- MCJITTest.cpp - Unit tests for the MCJIT -----------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This test suite verifies basic MCJIT functionality such as making function
|
||||
// calls, using global variables, and compiling multpile modules.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "MCJITTestBase.h"
|
||||
#include "llvm/Support/DynamicLibrary.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
namespace {
|
||||
|
||||
class MCJITTest : public testing::Test, public MCJITTestBase {
|
||||
protected:
|
||||
void SetUp() override { M.reset(createEmptyModule("<main>")); }
|
||||
};
|
||||
|
||||
// FIXME: Ensure creating an execution engine does not crash when constructed
|
||||
// with a null module.
|
||||
/*
|
||||
TEST_F(MCJITTest, null_module) {
|
||||
createJIT(0);
|
||||
}
|
||||
*/
|
||||
|
||||
// FIXME: In order to JIT an empty module, there needs to be
|
||||
// an interface to ExecutionEngine that forces compilation but
|
||||
// does not require retrieval of a pointer to a function/global.
|
||||
/*
|
||||
TEST_F(MCJITTest, empty_module) {
|
||||
createJIT(M.take());
|
||||
//EXPECT_NE(0, TheJIT->getObjectImage())
|
||||
// << "Unable to generate executable loaded object image";
|
||||
}
|
||||
*/
|
||||
|
||||
TEST_F(MCJITTest, global_variable) {
|
||||
SKIP_UNSUPPORTED_PLATFORM;
|
||||
|
||||
int initialValue = 5;
|
||||
GlobalValue *Global = insertGlobalInt32(M.get(), "test_global", initialValue);
|
||||
createJIT(std::move(M));
|
||||
void *globalPtr = TheJIT->getPointerToGlobal(Global);
|
||||
EXPECT_TRUE(nullptr != globalPtr)
|
||||
<< "Unable to get pointer to global value from JIT";
|
||||
|
||||
EXPECT_EQ(initialValue, *(int32_t*)globalPtr)
|
||||
<< "Unexpected initial value of global";
|
||||
}
|
||||
|
||||
TEST_F(MCJITTest, add_function) {
|
||||
SKIP_UNSUPPORTED_PLATFORM;
|
||||
|
||||
Function *F = insertAddFunction(M.get());
|
||||
createJIT(std::move(M));
|
||||
uint64_t addPtr = TheJIT->getFunctionAddress(F->getName().str());
|
||||
EXPECT_TRUE(0 != addPtr)
|
||||
<< "Unable to get pointer to function from JIT";
|
||||
|
||||
ASSERT_TRUE(addPtr != 0) << "Unable to get pointer to function .";
|
||||
int (*AddPtr)(int, int) = (int(*)(int, int))addPtr ;
|
||||
EXPECT_EQ(0, AddPtr(0, 0));
|
||||
EXPECT_EQ(1, AddPtr(1, 0));
|
||||
EXPECT_EQ(3, AddPtr(1, 2));
|
||||
EXPECT_EQ(-5, AddPtr(-2, -3));
|
||||
EXPECT_EQ(30, AddPtr(10, 20));
|
||||
EXPECT_EQ(-30, AddPtr(-10, -20));
|
||||
EXPECT_EQ(-40, AddPtr(-10, -30));
|
||||
}
|
||||
|
||||
TEST_F(MCJITTest, run_main) {
|
||||
SKIP_UNSUPPORTED_PLATFORM;
|
||||
|
||||
int rc = 6;
|
||||
Function *Main = insertMainFunction(M.get(), 6);
|
||||
createJIT(std::move(M));
|
||||
uint64_t ptr = TheJIT->getFunctionAddress(Main->getName().str());
|
||||
EXPECT_TRUE(0 != ptr)
|
||||
<< "Unable to get pointer to main() from JIT";
|
||||
|
||||
int (*FuncPtr)() = (int(*)())ptr;
|
||||
int returnCode = FuncPtr();
|
||||
EXPECT_EQ(returnCode, rc);
|
||||
}
|
||||
|
||||
TEST_F(MCJITTest, return_global) {
|
||||
SKIP_UNSUPPORTED_PLATFORM;
|
||||
|
||||
int32_t initialNum = 7;
|
||||
GlobalVariable *GV = insertGlobalInt32(M.get(), "myglob", initialNum);
|
||||
|
||||
Function *ReturnGlobal = startFunction<int32_t(void)>(M.get(),
|
||||
"ReturnGlobal");
|
||||
Value *ReadGlobal = Builder.CreateLoad(GV);
|
||||
endFunctionWithRet(ReturnGlobal, ReadGlobal);
|
||||
|
||||
createJIT(std::move(M));
|
||||
uint64_t rgvPtr = TheJIT->getFunctionAddress(ReturnGlobal->getName().str());
|
||||
EXPECT_TRUE(0 != rgvPtr);
|
||||
|
||||
int32_t(*FuncPtr)() = (int32_t(*)())rgvPtr;
|
||||
EXPECT_EQ(initialNum, FuncPtr())
|
||||
<< "Invalid value for global returned from JITted function";
|
||||
}
|
||||
|
||||
// FIXME: This case fails due to a bug with getPointerToGlobal().
|
||||
// The bug is due to MCJIT not having an implementation of getPointerToGlobal()
|
||||
// which results in falling back on the ExecutionEngine implementation that
|
||||
// allocates a new memory block for the global instead of using the same
|
||||
// global variable that is emitted by MCJIT. Hence, the pointer (gvPtr below)
|
||||
// has the correct initial value, but updates to the real global (accessed by
|
||||
// JITted code) are not propagated. Instead, getPointerToGlobal() should return
|
||||
// a pointer into the loaded ObjectImage to reference the emitted global.
|
||||
/*
|
||||
TEST_F(MCJITTest, increment_global) {
|
||||
SKIP_UNSUPPORTED_PLATFORM;
|
||||
|
||||
int32_t initialNum = 5;
|
||||
Function *IncrementGlobal = startFunction<int32_t(void)>(M.get(), "IncrementGlobal");
|
||||
GlobalVariable *GV = insertGlobalInt32(M.get(), "my_global", initialNum);
|
||||
Value *DerefGV = Builder.CreateLoad(GV);
|
||||
Value *AddResult = Builder.CreateAdd(DerefGV,
|
||||
ConstantInt::get(Context, APInt(32, 1)));
|
||||
Builder.CreateStore(AddResult, GV);
|
||||
endFunctionWithRet(IncrementGlobal, AddResult);
|
||||
|
||||
createJIT(M.take());
|
||||
void *gvPtr = TheJIT->getPointerToGlobal(GV);
|
||||
EXPECT_EQ(initialNum, *(int32_t*)gvPtr);
|
||||
|
||||
void *vPtr = TheJIT->getFunctionAddress(IncrementGlobal->getName().str());
|
||||
EXPECT_TRUE(0 != vPtr)
|
||||
<< "Unable to get pointer to main() from JIT";
|
||||
|
||||
int32_t(*FuncPtr)(void) = (int32_t(*)(void))(intptr_t)vPtr;
|
||||
|
||||
for(int i = 1; i < 3; ++i) {
|
||||
int32_t result = FuncPtr();
|
||||
EXPECT_EQ(initialNum + i, result); // OK
|
||||
EXPECT_EQ(initialNum + i, *(int32_t*)gvPtr); // FAILS
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// PR16013: XFAIL this test on ARM, which currently can't handle multiple relocations.
|
||||
#if !defined(__arm__)
|
||||
|
||||
TEST_F(MCJITTest, multiple_functions) {
|
||||
SKIP_UNSUPPORTED_PLATFORM;
|
||||
|
||||
unsigned int numLevels = 23;
|
||||
int32_t innerRetVal= 5;
|
||||
|
||||
Function *Inner = startFunction<int32_t(void)>(M.get(), "Inner");
|
||||
endFunctionWithRet(Inner, ConstantInt::get(Context, APInt(32, innerRetVal)));
|
||||
|
||||
Function *Outer;
|
||||
for (unsigned int i = 0; i < numLevels; ++i) {
|
||||
std::stringstream funcName;
|
||||
funcName << "level_" << i;
|
||||
Outer = startFunction<int32_t(void)>(M.get(), funcName.str());
|
||||
Value *innerResult = Builder.CreateCall(Inner, {});
|
||||
endFunctionWithRet(Outer, innerResult);
|
||||
|
||||
Inner = Outer;
|
||||
}
|
||||
|
||||
createJIT(std::move(M));
|
||||
uint64_t ptr = TheJIT->getFunctionAddress(Outer->getName().str());
|
||||
EXPECT_TRUE(0 != ptr)
|
||||
<< "Unable to get pointer to outer function from JIT";
|
||||
|
||||
int32_t(*FuncPtr)() = (int32_t(*)())ptr;
|
||||
EXPECT_EQ(innerRetVal, FuncPtr())
|
||||
<< "Incorrect result returned from function";
|
||||
}
|
||||
|
||||
#endif /*!defined(__arm__)*/
|
||||
|
||||
TEST_F(MCJITTest, multiple_decl_lookups) {
|
||||
SKIP_UNSUPPORTED_PLATFORM;
|
||||
|
||||
Function *Foo = insertExternalReferenceToFunction<void(void)>(M.get(), "_exit");
|
||||
createJIT(std::move(M));
|
||||
void *A = TheJIT->getPointerToFunction(Foo);
|
||||
void *B = TheJIT->getPointerToFunction(Foo);
|
||||
|
||||
EXPECT_TRUE(A != nullptr) << "Failed lookup - test not correctly configured.";
|
||||
EXPECT_EQ(A, B) << "Repeat calls to getPointerToFunction fail.";
|
||||
}
|
||||
|
||||
typedef void * (*FunctionHandlerPtr)(const std::string &str);
|
||||
|
||||
TEST_F(MCJITTest, lazy_function_creator_pointer) {
|
||||
SKIP_UNSUPPORTED_PLATFORM;
|
||||
|
||||
Function *Foo = insertExternalReferenceToFunction<int32_t(void)>(M.get(),
|
||||
"\1Foo");
|
||||
startFunction<int32_t(void)>(M.get(), "Parent");
|
||||
CallInst *Call = Builder.CreateCall(Foo, {});
|
||||
Builder.CreateRet(Call);
|
||||
|
||||
createJIT(std::move(M));
|
||||
|
||||
// Set up the lazy function creator that records the name of the last
|
||||
// unresolved external function found in the module. Using a function pointer
|
||||
// prevents us from capturing local variables, which is why this is static.
|
||||
static std::string UnresolvedExternal;
|
||||
FunctionHandlerPtr UnresolvedHandler = [] (const std::string &str) {
|
||||
// Try to resolve the function in the current process before marking it as
|
||||
// unresolved. This solves an issue on ARM where '__aeabi_*' function names
|
||||
// are passed to this handler.
|
||||
void *symbol =
|
||||
llvm::sys::DynamicLibrary::SearchForAddressOfSymbol(str.c_str());
|
||||
if (symbol) {
|
||||
return symbol;
|
||||
}
|
||||
|
||||
UnresolvedExternal = str;
|
||||
return (void *)(uintptr_t)-1;
|
||||
};
|
||||
TheJIT->InstallLazyFunctionCreator(UnresolvedHandler);
|
||||
|
||||
// JIT the module.
|
||||
TheJIT->finalizeObject();
|
||||
|
||||
// Verify that our handler was called.
|
||||
EXPECT_EQ(UnresolvedExternal, "Foo");
|
||||
}
|
||||
|
||||
TEST_F(MCJITTest, lazy_function_creator_lambda) {
|
||||
SKIP_UNSUPPORTED_PLATFORM;
|
||||
|
||||
Function *Foo1 = insertExternalReferenceToFunction<int32_t(void)>(M.get(),
|
||||
"\1Foo1");
|
||||
Function *Foo2 = insertExternalReferenceToFunction<int32_t(void)>(M.get(),
|
||||
"\1Foo2");
|
||||
startFunction<int32_t(void)>(M.get(), "Parent");
|
||||
CallInst *Call1 = Builder.CreateCall(Foo1, {});
|
||||
CallInst *Call2 = Builder.CreateCall(Foo2, {});
|
||||
Value *Result = Builder.CreateAdd(Call1, Call2);
|
||||
Builder.CreateRet(Result);
|
||||
|
||||
createJIT(std::move(M));
|
||||
|
||||
// Set up the lazy function creator that records the name of unresolved
|
||||
// external functions in the module.
|
||||
std::vector<std::string> UnresolvedExternals;
|
||||
auto UnresolvedHandler = [&UnresolvedExternals] (const std::string &str) {
|
||||
// Try to resolve the function in the current process before marking it as
|
||||
// unresolved. This solves an issue on ARM where '__aeabi_*' function names
|
||||
// are passed to this handler.
|
||||
void *symbol =
|
||||
llvm::sys::DynamicLibrary::SearchForAddressOfSymbol(str.c_str());
|
||||
if (symbol) {
|
||||
return symbol;
|
||||
}
|
||||
UnresolvedExternals.push_back(str);
|
||||
return (void *)(uintptr_t)-1;
|
||||
};
|
||||
TheJIT->InstallLazyFunctionCreator(UnresolvedHandler);
|
||||
|
||||
// JIT the module.
|
||||
TheJIT->finalizeObject();
|
||||
|
||||
// Verify that our handler was called for each unresolved function.
|
||||
auto I = UnresolvedExternals.begin(), E = UnresolvedExternals.end();
|
||||
EXPECT_EQ(UnresolvedExternals.size(), 2u);
|
||||
EXPECT_FALSE(std::find(I, E, "Foo1") == E);
|
||||
EXPECT_FALSE(std::find(I, E, "Foo2") == E);
|
||||
}
|
||||
|
||||
} // end anonymous namespace
|
@ -1,101 +0,0 @@
|
||||
//===- MCJITTestBase.h - Common base class for MCJIT Unit tests ----------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This class implements functionality shared by both MCJIT C API tests, and
|
||||
// the C++ API tests.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_UNITTESTS_EXECUTIONENGINE_MCJIT_MCJITTESTAPICOMMON_H
|
||||
#define LLVM_UNITTESTS_EXECUTIONENGINE_MCJIT_MCJITTESTAPICOMMON_H
|
||||
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/Triple.h"
|
||||
#include "llvm/IR/LegacyPassManager.h"
|
||||
#include "llvm/InitializePasses.h"
|
||||
#include "llvm/Support/Host.h"
|
||||
#include "llvm/Support/TargetSelect.h"
|
||||
|
||||
// Used to skip tests on unsupported architectures and operating systems.
|
||||
// To skip a test, add this macro at the top of a test-case in a suite that
|
||||
// inherits from MCJITTestBase. See MCJITTest.cpp for examples.
|
||||
#define SKIP_UNSUPPORTED_PLATFORM \
|
||||
do \
|
||||
if (!ArchSupportsMCJIT() || !OSSupportsMCJIT()) \
|
||||
return; \
|
||||
while(0)
|
||||
|
||||
namespace llvm {
|
||||
|
||||
class MCJITTestAPICommon {
|
||||
protected:
|
||||
MCJITTestAPICommon()
|
||||
: HostTriple(sys::getProcessTriple())
|
||||
{
|
||||
InitializeNativeTarget();
|
||||
InitializeNativeTargetAsmPrinter();
|
||||
|
||||
// FIXME: It isn't at all clear why this is necesasry, but without it we
|
||||
// fail to initialize the AssumptionCacheTracker.
|
||||
initializeAssumptionCacheTrackerPass(*PassRegistry::getPassRegistry());
|
||||
|
||||
#ifdef LLVM_ON_WIN32
|
||||
// On Windows, generate ELF objects by specifying "-elf" in triple
|
||||
HostTriple += "-elf";
|
||||
#endif // LLVM_ON_WIN32
|
||||
HostTriple = Triple::normalize(HostTriple);
|
||||
}
|
||||
|
||||
/// Returns true if the host architecture is known to support MCJIT
|
||||
bool ArchSupportsMCJIT() {
|
||||
Triple Host(HostTriple);
|
||||
// If ARCH is not supported, bail
|
||||
if (!is_contained(SupportedArchs, Host.getArch()))
|
||||
return false;
|
||||
|
||||
// If ARCH is supported and has no specific sub-arch support
|
||||
if (!is_contained(HasSubArchs, Host.getArch()))
|
||||
return true;
|
||||
|
||||
// If ARCH has sub-arch support, find it
|
||||
SmallVectorImpl<std::string>::const_iterator I = SupportedSubArchs.begin();
|
||||
for(; I != SupportedSubArchs.end(); ++I)
|
||||
if (Host.getArchName().startswith(*I))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Returns true if the host OS is known to support MCJIT
|
||||
bool OSSupportsMCJIT() {
|
||||
Triple Host(HostTriple);
|
||||
|
||||
if (find(UnsupportedEnvironments, Host.getEnvironment()) !=
|
||||
UnsupportedEnvironments.end())
|
||||
return false;
|
||||
|
||||
if (!is_contained(UnsupportedOSs, Host.getOS()))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string HostTriple;
|
||||
SmallVector<Triple::ArchType, 4> SupportedArchs;
|
||||
SmallVector<Triple::ArchType, 1> HasSubArchs;
|
||||
SmallVector<std::string, 2> SupportedSubArchs; // We need to own the memory
|
||||
SmallVector<Triple::OSType, 4> UnsupportedOSs;
|
||||
SmallVector<Triple::EnvironmentType, 1> UnsupportedEnvironments;
|
||||
};
|
||||
|
||||
} // namespace llvm
|
||||
|
||||
#endif
|
||||
|
@ -1,338 +0,0 @@
|
||||
//===- MCJITTestBase.h - Common base class for MCJIT Unit tests -*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This class implements common functionality required by the MCJIT unit tests,
|
||||
// as well as logic to skip tests on unsupported architectures and operating
|
||||
// systems.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_UNITTESTS_EXECUTIONENGINE_MCJIT_MCJITTESTBASE_H
|
||||
#define LLVM_UNITTESTS_EXECUTIONENGINE_MCJIT_MCJITTESTBASE_H
|
||||
|
||||
#include "MCJITTestAPICommon.h"
|
||||
#include "llvm/Config/config.h"
|
||||
#include "llvm/ExecutionEngine/ExecutionEngine.h"
|
||||
#include "llvm/ExecutionEngine/SectionMemoryManager.h"
|
||||
#include "llvm/IR/Function.h"
|
||||
#include "llvm/IR/IRBuilder.h"
|
||||
#include "llvm/IR/LLVMContext.h"
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/IR/TypeBuilder.h"
|
||||
#include "llvm/Support/CodeGen.h"
|
||||
|
||||
namespace llvm {
|
||||
|
||||
/// Helper class that can build very simple Modules
|
||||
class TrivialModuleBuilder {
|
||||
protected:
|
||||
LLVMContext Context;
|
||||
IRBuilder<> Builder;
|
||||
std::string BuilderTriple;
|
||||
|
||||
TrivialModuleBuilder(const std::string &Triple)
|
||||
: Builder(Context), BuilderTriple(Triple) {}
|
||||
|
||||
Module *createEmptyModule(StringRef Name = StringRef()) {
|
||||
Module * M = new Module(Name, Context);
|
||||
M->setTargetTriple(Triple::normalize(BuilderTriple));
|
||||
return M;
|
||||
}
|
||||
|
||||
template<typename FuncType>
|
||||
Function *startFunction(Module *M, StringRef Name) {
|
||||
Function *Result = Function::Create(
|
||||
TypeBuilder<FuncType, false>::get(Context),
|
||||
GlobalValue::ExternalLinkage, Name, M);
|
||||
|
||||
BasicBlock *BB = BasicBlock::Create(Context, Name, Result);
|
||||
Builder.SetInsertPoint(BB);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
void endFunctionWithRet(Function *Func, Value *RetValue) {
|
||||
Builder.CreateRet(RetValue);
|
||||
}
|
||||
|
||||
// Inserts a simple function that invokes Callee and takes the same arguments:
|
||||
// int Caller(...) { return Callee(...); }
|
||||
template<typename Signature>
|
||||
Function *insertSimpleCallFunction(Module *M, Function *Callee) {
|
||||
Function *Result = startFunction<Signature>(M, "caller");
|
||||
|
||||
SmallVector<Value*, 1> CallArgs;
|
||||
|
||||
for (Argument &A : Result->args())
|
||||
CallArgs.push_back(&A);
|
||||
|
||||
Value *ReturnCode = Builder.CreateCall(Callee, CallArgs);
|
||||
Builder.CreateRet(ReturnCode);
|
||||
return Result;
|
||||
}
|
||||
|
||||
// Inserts a function named 'main' that returns a uint32_t:
|
||||
// int32_t main() { return X; }
|
||||
// where X is given by returnCode
|
||||
Function *insertMainFunction(Module *M, uint32_t returnCode) {
|
||||
Function *Result = startFunction<int32_t(void)>(M, "main");
|
||||
|
||||
Value *ReturnVal = ConstantInt::get(Context, APInt(32, returnCode));
|
||||
endFunctionWithRet(Result, ReturnVal);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
// Inserts a function
|
||||
// int32_t add(int32_t a, int32_t b) { return a + b; }
|
||||
// in the current module and returns a pointer to it.
|
||||
Function *insertAddFunction(Module *M, StringRef Name = "add") {
|
||||
Function *Result = startFunction<int32_t(int32_t, int32_t)>(M, Name);
|
||||
|
||||
Function::arg_iterator args = Result->arg_begin();
|
||||
Value *Arg1 = &*args;
|
||||
Value *Arg2 = &*++args;
|
||||
Value *AddResult = Builder.CreateAdd(Arg1, Arg2);
|
||||
|
||||
endFunctionWithRet(Result, AddResult);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
// Inserts a declaration to a function defined elsewhere
|
||||
template <typename FuncType>
|
||||
Function *insertExternalReferenceToFunction(Module *M, StringRef Name) {
|
||||
Function *Result = Function::Create(
|
||||
TypeBuilder<FuncType, false>::get(Context),
|
||||
GlobalValue::ExternalLinkage, Name, M);
|
||||
return Result;
|
||||
}
|
||||
|
||||
// Inserts an declaration to a function defined elsewhere
|
||||
Function *insertExternalReferenceToFunction(Module *M, StringRef Name,
|
||||
FunctionType *FuncTy) {
|
||||
Function *Result = Function::Create(FuncTy,
|
||||
GlobalValue::ExternalLinkage,
|
||||
Name, M);
|
||||
return Result;
|
||||
}
|
||||
|
||||
// Inserts an declaration to a function defined elsewhere
|
||||
Function *insertExternalReferenceToFunction(Module *M, Function *Func) {
|
||||
Function *Result = Function::Create(Func->getFunctionType(),
|
||||
GlobalValue::ExternalLinkage,
|
||||
Func->getName(), M);
|
||||
return Result;
|
||||
}
|
||||
|
||||
// Inserts a global variable of type int32
|
||||
// FIXME: make this a template function to support any type
|
||||
GlobalVariable *insertGlobalInt32(Module *M,
|
||||
StringRef name,
|
||||
int32_t InitialValue) {
|
||||
Type *GlobalTy = TypeBuilder<types::i<32>, true>::get(Context);
|
||||
Constant *IV = ConstantInt::get(Context, APInt(32, InitialValue));
|
||||
GlobalVariable *Global = new GlobalVariable(*M,
|
||||
GlobalTy,
|
||||
false,
|
||||
GlobalValue::ExternalLinkage,
|
||||
IV,
|
||||
name);
|
||||
return Global;
|
||||
}
|
||||
|
||||
// Inserts a function
|
||||
// int32_t recursive_add(int32_t num) {
|
||||
// if (num == 0) {
|
||||
// return num;
|
||||
// } else {
|
||||
// int32_t recursive_param = num - 1;
|
||||
// return num + Helper(recursive_param);
|
||||
// }
|
||||
// }
|
||||
// NOTE: if Helper is left as the default parameter, Helper == recursive_add.
|
||||
Function *insertAccumulateFunction(Module *M,
|
||||
Function *Helper = nullptr,
|
||||
StringRef Name = "accumulate") {
|
||||
Function *Result = startFunction<int32_t(int32_t)>(M, Name);
|
||||
if (!Helper)
|
||||
Helper = Result;
|
||||
|
||||
BasicBlock *BaseCase = BasicBlock::Create(Context, "", Result);
|
||||
BasicBlock *RecursiveCase = BasicBlock::Create(Context, "", Result);
|
||||
|
||||
// if (num == 0)
|
||||
Value *Param = &*Result->arg_begin();
|
||||
Value *Zero = ConstantInt::get(Context, APInt(32, 0));
|
||||
Builder.CreateCondBr(Builder.CreateICmpEQ(Param, Zero),
|
||||
BaseCase, RecursiveCase);
|
||||
|
||||
// return num;
|
||||
Builder.SetInsertPoint(BaseCase);
|
||||
Builder.CreateRet(Param);
|
||||
|
||||
// int32_t recursive_param = num - 1;
|
||||
// return Helper(recursive_param);
|
||||
Builder.SetInsertPoint(RecursiveCase);
|
||||
Value *One = ConstantInt::get(Context, APInt(32, 1));
|
||||
Value *RecursiveParam = Builder.CreateSub(Param, One);
|
||||
Value *RecursiveReturn = Builder.CreateCall(Helper, RecursiveParam);
|
||||
Value *Accumulator = Builder.CreateAdd(Param, RecursiveReturn);
|
||||
Builder.CreateRet(Accumulator);
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
// Populates Modules A and B:
|
||||
// Module A { Extern FB1, Function FA which calls FB1 },
|
||||
// Module B { Extern FA, Function FB1, Function FB2 which calls FA },
|
||||
void createCrossModuleRecursiveCase(std::unique_ptr<Module> &A, Function *&FA,
|
||||
std::unique_ptr<Module> &B,
|
||||
Function *&FB1, Function *&FB2) {
|
||||
// Define FB1 in B.
|
||||
B.reset(createEmptyModule("B"));
|
||||
FB1 = insertAccumulateFunction(B.get(), nullptr, "FB1");
|
||||
|
||||
// Declare FB1 in A (as an external).
|
||||
A.reset(createEmptyModule("A"));
|
||||
Function *FB1Extern = insertExternalReferenceToFunction(A.get(), FB1);
|
||||
|
||||
// Define FA in A (with a call to FB1).
|
||||
FA = insertAccumulateFunction(A.get(), FB1Extern, "FA");
|
||||
|
||||
// Declare FA in B (as an external)
|
||||
Function *FAExtern = insertExternalReferenceToFunction(B.get(), FA);
|
||||
|
||||
// Define FB2 in B (with a call to FA)
|
||||
FB2 = insertAccumulateFunction(B.get(), FAExtern, "FB2");
|
||||
}
|
||||
|
||||
// Module A { Function FA },
|
||||
// Module B { Extern FA, Function FB which calls FA },
|
||||
// Module C { Extern FB, Function FC which calls FB },
|
||||
void
|
||||
createThreeModuleChainedCallsCase(std::unique_ptr<Module> &A, Function *&FA,
|
||||
std::unique_ptr<Module> &B, Function *&FB,
|
||||
std::unique_ptr<Module> &C, Function *&FC) {
|
||||
A.reset(createEmptyModule("A"));
|
||||
FA = insertAddFunction(A.get());
|
||||
|
||||
B.reset(createEmptyModule("B"));
|
||||
Function *FAExtern_in_B = insertExternalReferenceToFunction(B.get(), FA);
|
||||
FB = insertSimpleCallFunction<int32_t(int32_t, int32_t)>(B.get(), FAExtern_in_B);
|
||||
|
||||
C.reset(createEmptyModule("C"));
|
||||
Function *FBExtern_in_C = insertExternalReferenceToFunction(C.get(), FB);
|
||||
FC = insertSimpleCallFunction<int32_t(int32_t, int32_t)>(C.get(), FBExtern_in_C);
|
||||
}
|
||||
|
||||
// Module A { Function FA },
|
||||
// Populates Modules A and B:
|
||||
// Module B { Function FB }
|
||||
void createTwoModuleCase(std::unique_ptr<Module> &A, Function *&FA,
|
||||
std::unique_ptr<Module> &B, Function *&FB) {
|
||||
A.reset(createEmptyModule("A"));
|
||||
FA = insertAddFunction(A.get());
|
||||
|
||||
B.reset(createEmptyModule("B"));
|
||||
FB = insertAddFunction(B.get());
|
||||
}
|
||||
|
||||
// Module A { Function FA },
|
||||
// Module B { Extern FA, Function FB which calls FA }
|
||||
void createTwoModuleExternCase(std::unique_ptr<Module> &A, Function *&FA,
|
||||
std::unique_ptr<Module> &B, Function *&FB) {
|
||||
A.reset(createEmptyModule("A"));
|
||||
FA = insertAddFunction(A.get());
|
||||
|
||||
B.reset(createEmptyModule("B"));
|
||||
Function *FAExtern_in_B = insertExternalReferenceToFunction(B.get(), FA);
|
||||
FB = insertSimpleCallFunction<int32_t(int32_t, int32_t)>(B.get(),
|
||||
FAExtern_in_B);
|
||||
}
|
||||
|
||||
// Module A { Function FA },
|
||||
// Module B { Extern FA, Function FB which calls FA },
|
||||
// Module C { Extern FB, Function FC which calls FA },
|
||||
void createThreeModuleCase(std::unique_ptr<Module> &A, Function *&FA,
|
||||
std::unique_ptr<Module> &B, Function *&FB,
|
||||
std::unique_ptr<Module> &C, Function *&FC) {
|
||||
A.reset(createEmptyModule("A"));
|
||||
FA = insertAddFunction(A.get());
|
||||
|
||||
B.reset(createEmptyModule("B"));
|
||||
Function *FAExtern_in_B = insertExternalReferenceToFunction(B.get(), FA);
|
||||
FB = insertSimpleCallFunction<int32_t(int32_t, int32_t)>(B.get(), FAExtern_in_B);
|
||||
|
||||
C.reset(createEmptyModule("C"));
|
||||
Function *FAExtern_in_C = insertExternalReferenceToFunction(C.get(), FA);
|
||||
FC = insertSimpleCallFunction<int32_t(int32_t, int32_t)>(C.get(), FAExtern_in_C);
|
||||
}
|
||||
};
|
||||
|
||||
class MCJITTestBase : public MCJITTestAPICommon, public TrivialModuleBuilder {
|
||||
protected:
|
||||
MCJITTestBase()
|
||||
: TrivialModuleBuilder(HostTriple), OptLevel(CodeGenOpt::None),
|
||||
CodeModel(CodeModel::Small), MArch(""), MM(new SectionMemoryManager) {
|
||||
// The architectures below are known to be compatible with MCJIT as they
|
||||
// are copied from test/ExecutionEngine/MCJIT/lit.local.cfg and should be
|
||||
// kept in sync.
|
||||
SupportedArchs.push_back(Triple::aarch64);
|
||||
SupportedArchs.push_back(Triple::arm);
|
||||
SupportedArchs.push_back(Triple::mips);
|
||||
SupportedArchs.push_back(Triple::mipsel);
|
||||
SupportedArchs.push_back(Triple::mips64);
|
||||
SupportedArchs.push_back(Triple::mips64el);
|
||||
SupportedArchs.push_back(Triple::x86);
|
||||
SupportedArchs.push_back(Triple::x86_64);
|
||||
|
||||
// Some architectures have sub-architectures in which tests will fail, like
|
||||
// ARM. These two vectors will define if they do have sub-archs (to avoid
|
||||
// extra work for those who don't), and if so, if they are listed to work
|
||||
HasSubArchs.push_back(Triple::arm);
|
||||
SupportedSubArchs.push_back("armv6");
|
||||
SupportedSubArchs.push_back("armv7");
|
||||
|
||||
UnsupportedEnvironments.push_back(Triple::Cygnus);
|
||||
}
|
||||
|
||||
void createJIT(std::unique_ptr<Module> M) {
|
||||
|
||||
// Due to the EngineBuilder constructor, it is required to have a Module
|
||||
// in order to construct an ExecutionEngine (i.e. MCJIT)
|
||||
assert(M != 0 && "a non-null Module must be provided to create MCJIT");
|
||||
|
||||
EngineBuilder EB(std::move(M));
|
||||
std::string Error;
|
||||
TheJIT.reset(EB.setEngineKind(EngineKind::JIT)
|
||||
.setMCJITMemoryManager(std::move(MM))
|
||||
.setErrorStr(&Error)
|
||||
.setOptLevel(CodeGenOpt::None)
|
||||
.setMArch(MArch)
|
||||
.setMCPU(sys::getHostCPUName())
|
||||
//.setMAttrs(MAttrs)
|
||||
.create());
|
||||
// At this point, we cannot modify the module any more.
|
||||
assert(TheJIT.get() != NULL && "error creating MCJIT with EngineBuilder");
|
||||
}
|
||||
|
||||
CodeGenOpt::Level OptLevel;
|
||||
CodeModel::Model CodeModel;
|
||||
StringRef MArch;
|
||||
SmallVector<std::string, 1> MAttrs;
|
||||
std::unique_ptr<ExecutionEngine> TheJIT;
|
||||
std::unique_ptr<RTDyldMemoryManager> MM;
|
||||
|
||||
std::unique_ptr<Module> M;
|
||||
};
|
||||
|
||||
} // namespace llvm
|
||||
|
||||
#endif // LLVM_UNITTESTS_EXECUTIONENGINE_MCJIT_MCJITTESTBASE_H
|
@ -1 +0,0 @@
|
||||
EXPORTS
|
@ -1,32 +0,0 @@
|
||||
|
||||
set(LLVM_LINK_COMPONENTS
|
||||
Core
|
||||
ExecutionEngine
|
||||
Object
|
||||
OrcJIT
|
||||
RuntimeDyld
|
||||
Support
|
||||
native
|
||||
)
|
||||
|
||||
add_llvm_unittest(OrcJITTests
|
||||
CompileOnDemandLayerTest.cpp
|
||||
IndirectionUtilsTest.cpp
|
||||
GlobalMappingLayerTest.cpp
|
||||
LazyEmittingLayerTest.cpp
|
||||
ObjectTransformLayerTest.cpp
|
||||
OrcCAPITest.cpp
|
||||
OrcTestCommon.cpp
|
||||
QueueChannel.cpp
|
||||
RemoteObjectLayerTest.cpp
|
||||
RPCUtilsTest.cpp
|
||||
RTDyldObjectLinkingLayerTest.cpp
|
||||
SymbolStringPoolTest.cpp
|
||||
)
|
||||
|
||||
set(ORC_JIT_TEST_LIBS ${LLVM_PTHREAD_LIB})
|
||||
if(NOT HAVE_CXX_ATOMICS64_WITHOUT_LIB)
|
||||
list(APPEND ORC_JIT_TEST_LIBS atomic)
|
||||
endif()
|
||||
|
||||
target_link_libraries(OrcJITTests PRIVATE ${ORC_JIT_TEST_LIBS})
|
@ -1,71 +0,0 @@
|
||||
//===----- CompileOnDemandLayerTest.cpp - Unit tests for the COD layer ----===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/ExecutionEngine/Orc/CompileOnDemandLayer.h"
|
||||
#include "OrcTestCommon.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::orc;
|
||||
|
||||
namespace {
|
||||
|
||||
class DummyCallbackManager : public orc::JITCompileCallbackManager {
|
||||
public:
|
||||
DummyCallbackManager() : JITCompileCallbackManager(0) {}
|
||||
|
||||
public:
|
||||
Error grow() override { llvm_unreachable("not implemented"); }
|
||||
};
|
||||
|
||||
class DummyStubsManager : public orc::IndirectStubsManager {
|
||||
public:
|
||||
Error createStub(StringRef StubName, JITTargetAddress InitAddr,
|
||||
JITSymbolFlags Flags) override {
|
||||
llvm_unreachable("Not implemented");
|
||||
}
|
||||
|
||||
Error createStubs(const StubInitsMap &StubInits) override {
|
||||
llvm_unreachable("Not implemented");
|
||||
}
|
||||
|
||||
JITSymbol findStub(StringRef Name, bool ExportedStubsOnly) override {
|
||||
llvm_unreachable("Not implemented");
|
||||
}
|
||||
|
||||
JITSymbol findPointer(StringRef Name) override {
|
||||
llvm_unreachable("Not implemented");
|
||||
}
|
||||
|
||||
Error updatePointer(StringRef Name, JITTargetAddress NewAddr) override {
|
||||
llvm_unreachable("Not implemented");
|
||||
}
|
||||
};
|
||||
|
||||
TEST(CompileOnDemandLayerTest, FindSymbol) {
|
||||
MockBaseLayer<int, std::shared_ptr<Module>> TestBaseLayer;
|
||||
TestBaseLayer.findSymbolImpl =
|
||||
[](const std::string &Name, bool) {
|
||||
if (Name == "foo")
|
||||
return JITSymbol(1, JITSymbolFlags::Exported);
|
||||
return JITSymbol(nullptr);
|
||||
};
|
||||
|
||||
DummyCallbackManager CallbackMgr;
|
||||
|
||||
llvm::orc::CompileOnDemandLayer<decltype(TestBaseLayer)> COD(
|
||||
TestBaseLayer, [](Function &F) { return std::set<Function *>{&F}; },
|
||||
CallbackMgr, [] { return llvm::make_unique<DummyStubsManager>(); }, true);
|
||||
|
||||
auto Sym = COD.findSymbol("foo", true);
|
||||
|
||||
EXPECT_TRUE(!!Sym) << "CompileOnDemand::findSymbol should call findSymbol in "
|
||||
"the base layer.";
|
||||
}
|
||||
}
|
@ -1,63 +0,0 @@
|
||||
//===--- GlobalMappingLayerTest.cpp - Unit test the global mapping layer --===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/ExecutionEngine/Orc/GlobalMappingLayer.h"
|
||||
#include "OrcTestCommon.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::orc;
|
||||
|
||||
namespace {
|
||||
|
||||
TEST(GlobalMappingLayerTest, Empty) {
|
||||
MockBaseLayer<int, std::shared_ptr<Module>> TestBaseLayer;
|
||||
|
||||
TestBaseLayer.addModuleImpl =
|
||||
[](std::shared_ptr<Module> M, std::shared_ptr<JITSymbolResolver> R) {
|
||||
return 42;
|
||||
};
|
||||
|
||||
TestBaseLayer.findSymbolImpl =
|
||||
[](const std::string &Name, bool ExportedSymbolsOnly) -> JITSymbol {
|
||||
if (Name == "bar")
|
||||
return llvm::JITSymbol(0x4567, JITSymbolFlags::Exported);
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
GlobalMappingLayer<decltype(TestBaseLayer)> L(TestBaseLayer);
|
||||
|
||||
// Test addModule interface.
|
||||
int H = cantFail(L.addModule(nullptr, nullptr));
|
||||
EXPECT_EQ(H, 42) << "Incorrect result from addModule";
|
||||
|
||||
// Test fall-through for missing symbol.
|
||||
auto FooSym = L.findSymbol("foo", true);
|
||||
EXPECT_FALSE(FooSym) << "Found unexpected symbol.";
|
||||
|
||||
// Test fall-through for symbol in base layer.
|
||||
auto BarSym = L.findSymbol("bar", true);
|
||||
EXPECT_EQ(cantFail(BarSym.getAddress()),
|
||||
static_cast<JITTargetAddress>(0x4567))
|
||||
<< "Symbol lookup fall-through failed.";
|
||||
|
||||
// Test setup of a global mapping.
|
||||
L.setGlobalMapping("foo", 0x0123);
|
||||
auto FooSym2 = L.findSymbol("foo", true);
|
||||
EXPECT_EQ(cantFail(FooSym2.getAddress()),
|
||||
static_cast<JITTargetAddress>(0x0123))
|
||||
<< "Symbol mapping setup failed.";
|
||||
|
||||
// Test removal of a global mapping.
|
||||
L.eraseGlobalMapping("foo");
|
||||
auto FooSym3 = L.findSymbol("foo", true);
|
||||
EXPECT_FALSE(FooSym3) << "Symbol mapping removal failed.";
|
||||
}
|
||||
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
//===- LazyEmittingLayerTest.cpp - Unit tests for the lazy emitting layer -===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/ExecutionEngine/Orc/IndirectionUtils.h"
|
||||
#include "OrcTestCommon.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
namespace {
|
||||
|
||||
TEST(IndirectionUtilsTest, MakeStub) {
|
||||
LLVMContext Context;
|
||||
ModuleBuilder MB(Context, "x86_64-apple-macosx10.10", "");
|
||||
Function *F = MB.createFunctionDecl<void(DummyStruct, DummyStruct)>("");
|
||||
AttributeSet FnAttrs = AttributeSet::get(
|
||||
Context, AttrBuilder().addAttribute(Attribute::NoUnwind));
|
||||
AttributeSet RetAttrs; // None
|
||||
AttributeSet ArgAttrs[2] = {
|
||||
AttributeSet::get(Context,
|
||||
AttrBuilder().addAttribute(Attribute::StructRet)),
|
||||
AttributeSet::get(Context, AttrBuilder().addAttribute(Attribute::ByVal)),
|
||||
};
|
||||
F->setAttributes(AttributeList::get(Context, FnAttrs, RetAttrs, ArgAttrs));
|
||||
|
||||
auto ImplPtr = orc::createImplPointer(*F->getType(), *MB.getModule(), "", nullptr);
|
||||
orc::makeStub(*F, *ImplPtr);
|
||||
|
||||
auto II = F->getEntryBlock().begin();
|
||||
EXPECT_TRUE(isa<LoadInst>(*II)) << "First instruction of stub should be a load.";
|
||||
auto *Call = dyn_cast<CallInst>(std::next(II));
|
||||
EXPECT_TRUE(Call != nullptr) << "Second instruction of stub should be a call.";
|
||||
EXPECT_TRUE(Call->isTailCall()) << "Indirect call from stub should be tail call.";
|
||||
EXPECT_TRUE(Call->hasStructRetAttr())
|
||||
<< "makeStub should propagate sret attr on 1st argument.";
|
||||
EXPECT_TRUE(Call->paramHasAttr(1U, Attribute::ByVal))
|
||||
<< "makeStub should propagate byval attr on 2nd argument.";
|
||||
}
|
||||
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
//===- LazyEmittingLayerTest.cpp - Unit tests for the lazy emitting layer -===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/ExecutionEngine/Orc/LazyEmittingLayer.h"
|
||||
#include "llvm/ExecutionEngine/RuntimeDyld.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace {
|
||||
|
||||
struct MockBaseLayer {
|
||||
typedef int ModuleHandleT;
|
||||
ModuleHandleT addModule(
|
||||
std::shared_ptr<llvm::Module>,
|
||||
std::unique_ptr<llvm::RuntimeDyld::MemoryManager> MemMgr,
|
||||
std::unique_ptr<llvm::JITSymbolResolver> Resolver) {
|
||||
EXPECT_FALSE(MemMgr);
|
||||
return 42;
|
||||
}
|
||||
};
|
||||
|
||||
TEST(LazyEmittingLayerTest, Empty) {
|
||||
MockBaseLayer M;
|
||||
llvm::orc::LazyEmittingLayer<MockBaseLayer> L(M);
|
||||
cantFail(L.addModule(std::unique_ptr<llvm::Module>(), nullptr));
|
||||
}
|
||||
|
||||
}
|
@ -1,326 +0,0 @@
|
||||
//===- ObjectTransformLayerTest.cpp - Unit tests for ObjectTransformLayer -===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/ExecutionEngine/Orc/ObjectTransformLayer.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ExecutionEngine/Orc/IRCompileLayer.h"
|
||||
#include "llvm/ExecutionEngine/Orc/NullResolver.h"
|
||||
#include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h"
|
||||
#include "llvm/ExecutionEngine/SectionMemoryManager.h"
|
||||
#include "llvm/Object/ObjectFile.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace llvm::orc;
|
||||
|
||||
namespace {
|
||||
|
||||
// stand-in for object::ObjectFile
|
||||
typedef int MockObjectFile;
|
||||
|
||||
// stand-in for llvm::MemoryBuffer set
|
||||
typedef int MockMemoryBuffer;
|
||||
|
||||
// Mock transform that operates on unique pointers to object files, and
|
||||
// allocates new object files rather than mutating the given ones.
|
||||
struct AllocatingTransform {
|
||||
std::shared_ptr<MockObjectFile>
|
||||
operator()(std::shared_ptr<MockObjectFile> Obj) const {
|
||||
return std::make_shared<MockObjectFile>(*Obj + 1);
|
||||
}
|
||||
};
|
||||
|
||||
// Mock base layer for verifying behavior of transform layer.
|
||||
// Each method "T foo(args)" is accompanied by two auxiliary methods:
|
||||
// - "void expectFoo(args)", to be called before calling foo on the transform
|
||||
// layer; saves values of args, which mock layer foo then verifies against.
|
||||
// - "void verifyFoo(T)", to be called after foo, which verifies that the
|
||||
// transform layer called the base layer and forwarded any return value.
|
||||
class MockBaseLayer {
|
||||
public:
|
||||
typedef int ObjHandleT;
|
||||
|
||||
MockBaseLayer() : MockSymbol(nullptr) { resetExpectations(); }
|
||||
|
||||
template <typename ObjPtrT>
|
||||
llvm::Expected<ObjHandleT>
|
||||
addObject(ObjPtrT Obj,
|
||||
std::shared_ptr<llvm::JITSymbolResolver> Resolver) {
|
||||
EXPECT_EQ(MockResolver, Resolver) << "Resolver should pass through";
|
||||
EXPECT_EQ(MockObject + 1, *Obj) << "Transform should be applied";
|
||||
LastCalled = "addObject";
|
||||
MockObjHandle = 111;
|
||||
return MockObjHandle;
|
||||
}
|
||||
|
||||
template <typename ObjPtrT>
|
||||
void expectAddObject(ObjPtrT Obj,
|
||||
std::shared_ptr<llvm::JITSymbolResolver> Resolver) {
|
||||
MockResolver = Resolver;
|
||||
MockObject = *Obj;
|
||||
}
|
||||
|
||||
|
||||
void verifyAddObject(ObjHandleT Returned) {
|
||||
EXPECT_EQ("addObject", LastCalled);
|
||||
EXPECT_EQ(MockObjHandle, Returned) << "Return should pass through";
|
||||
resetExpectations();
|
||||
}
|
||||
|
||||
llvm::Error removeObject(ObjHandleT H) {
|
||||
EXPECT_EQ(MockObjHandle, H);
|
||||
LastCalled = "removeObject";
|
||||
return llvm::Error::success();
|
||||
}
|
||||
|
||||
void expectRemoveObject(ObjHandleT H) { MockObjHandle = H; }
|
||||
void verifyRemoveObject() {
|
||||
EXPECT_EQ("removeObject", LastCalled);
|
||||
resetExpectations();
|
||||
}
|
||||
|
||||
llvm::JITSymbol findSymbol(const std::string &Name,
|
||||
bool ExportedSymbolsOnly) {
|
||||
EXPECT_EQ(MockName, Name) << "Name should pass through";
|
||||
EXPECT_EQ(MockBool, ExportedSymbolsOnly) << "Flag should pass through";
|
||||
LastCalled = "findSymbol";
|
||||
MockSymbol = llvm::JITSymbol(122, llvm::JITSymbolFlags::None);
|
||||
return llvm::JITSymbol(122, llvm::JITSymbolFlags::None);
|
||||
}
|
||||
void expectFindSymbol(const std::string &Name, bool ExportedSymbolsOnly) {
|
||||
MockName = Name;
|
||||
MockBool = ExportedSymbolsOnly;
|
||||
}
|
||||
void verifyFindSymbol(llvm::JITSymbol Returned) {
|
||||
EXPECT_EQ("findSymbol", LastCalled);
|
||||
EXPECT_EQ(cantFail(MockSymbol.getAddress()),
|
||||
cantFail(Returned.getAddress()))
|
||||
<< "Return should pass through";
|
||||
resetExpectations();
|
||||
}
|
||||
|
||||
llvm::JITSymbol findSymbolIn(ObjHandleT H, const std::string &Name,
|
||||
bool ExportedSymbolsOnly) {
|
||||
EXPECT_EQ(MockObjHandle, H) << "Handle should pass through";
|
||||
EXPECT_EQ(MockName, Name) << "Name should pass through";
|
||||
EXPECT_EQ(MockBool, ExportedSymbolsOnly) << "Flag should pass through";
|
||||
LastCalled = "findSymbolIn";
|
||||
MockSymbol = llvm::JITSymbol(122, llvm::JITSymbolFlags::None);
|
||||
return llvm::JITSymbol(122, llvm::JITSymbolFlags::None);
|
||||
}
|
||||
void expectFindSymbolIn(ObjHandleT H, const std::string &Name,
|
||||
bool ExportedSymbolsOnly) {
|
||||
MockObjHandle = H;
|
||||
MockName = Name;
|
||||
MockBool = ExportedSymbolsOnly;
|
||||
}
|
||||
void verifyFindSymbolIn(llvm::JITSymbol Returned) {
|
||||
EXPECT_EQ("findSymbolIn", LastCalled);
|
||||
EXPECT_EQ(cantFail(MockSymbol.getAddress()),
|
||||
cantFail(Returned.getAddress()))
|
||||
<< "Return should pass through";
|
||||
resetExpectations();
|
||||
}
|
||||
|
||||
llvm::Error emitAndFinalize(ObjHandleT H) {
|
||||
EXPECT_EQ(MockObjHandle, H) << "Handle should pass through";
|
||||
LastCalled = "emitAndFinalize";
|
||||
return llvm::Error::success();
|
||||
}
|
||||
|
||||
void expectEmitAndFinalize(ObjHandleT H) { MockObjHandle = H; }
|
||||
|
||||
void verifyEmitAndFinalize() {
|
||||
EXPECT_EQ("emitAndFinalize", LastCalled);
|
||||
resetExpectations();
|
||||
}
|
||||
|
||||
void mapSectionAddress(ObjHandleT H, const void *LocalAddress,
|
||||
llvm::JITTargetAddress TargetAddr) {
|
||||
EXPECT_EQ(MockObjHandle, H);
|
||||
EXPECT_EQ(MockLocalAddress, LocalAddress);
|
||||
EXPECT_EQ(MockTargetAddress, TargetAddr);
|
||||
LastCalled = "mapSectionAddress";
|
||||
}
|
||||
void expectMapSectionAddress(ObjHandleT H, const void *LocalAddress,
|
||||
llvm::JITTargetAddress TargetAddr) {
|
||||
MockObjHandle = H;
|
||||
MockLocalAddress = LocalAddress;
|
||||
MockTargetAddress = TargetAddr;
|
||||
}
|
||||
void verifyMapSectionAddress() {
|
||||
EXPECT_EQ("mapSectionAddress", LastCalled);
|
||||
resetExpectations();
|
||||
}
|
||||
|
||||
private:
|
||||
// Backing fields for remembering parameter/return values
|
||||
std::string LastCalled;
|
||||
std::shared_ptr<llvm::JITSymbolResolver> MockResolver;
|
||||
MockObjectFile MockObject;
|
||||
ObjHandleT MockObjHandle;
|
||||
std::string MockName;
|
||||
bool MockBool;
|
||||
llvm::JITSymbol MockSymbol;
|
||||
const void *MockLocalAddress;
|
||||
llvm::JITTargetAddress MockTargetAddress;
|
||||
MockMemoryBuffer MockBuffer;
|
||||
|
||||
// Clear remembered parameters between calls
|
||||
void resetExpectations() {
|
||||
LastCalled = "nothing";
|
||||
MockResolver = nullptr;
|
||||
MockObject = 0;
|
||||
MockObjHandle = 0;
|
||||
MockName = "bogus";
|
||||
MockSymbol = llvm::JITSymbol(nullptr);
|
||||
MockLocalAddress = nullptr;
|
||||
MockTargetAddress = 0;
|
||||
MockBuffer = 0;
|
||||
}
|
||||
};
|
||||
|
||||
// Test each operation on ObjectTransformLayer.
|
||||
TEST(ObjectTransformLayerTest, Main) {
|
||||
MockBaseLayer M;
|
||||
|
||||
// Create one object transform layer using a transform (as a functor)
|
||||
// that allocates new objects, and deals in unique pointers.
|
||||
ObjectTransformLayer<MockBaseLayer, AllocatingTransform> T1(M);
|
||||
|
||||
// Create a second object transform layer using a transform (as a lambda)
|
||||
// that mutates objects in place, and deals in naked pointers
|
||||
ObjectTransformLayer<MockBaseLayer,
|
||||
std::function<std::shared_ptr<MockObjectFile>(
|
||||
std::shared_ptr<MockObjectFile>)>>
|
||||
T2(M, [](std::shared_ptr<MockObjectFile> Obj) {
|
||||
++(*Obj);
|
||||
return Obj;
|
||||
});
|
||||
|
||||
// Test addObject with T1 (allocating)
|
||||
auto Obj1 = std::make_shared<MockObjectFile>(211);
|
||||
auto SR = std::make_shared<NullResolver>();
|
||||
M.expectAddObject(Obj1, SR);
|
||||
auto H = cantFail(T1.addObject(std::move(Obj1), SR));
|
||||
M.verifyAddObject(H);
|
||||
|
||||
// Test addObjectSet with T2 (mutating)
|
||||
auto Obj2 = std::make_shared<MockObjectFile>(222);
|
||||
M.expectAddObject(Obj2, SR);
|
||||
H = cantFail(T2.addObject(Obj2, SR));
|
||||
M.verifyAddObject(H);
|
||||
EXPECT_EQ(223, *Obj2) << "Expected mutation";
|
||||
|
||||
// Test removeObjectSet
|
||||
M.expectRemoveObject(H);
|
||||
cantFail(T1.removeObject(H));
|
||||
M.verifyRemoveObject();
|
||||
|
||||
// Test findSymbol
|
||||
std::string Name = "foo";
|
||||
bool ExportedOnly = true;
|
||||
M.expectFindSymbol(Name, ExportedOnly);
|
||||
llvm::JITSymbol Sym1 = T2.findSymbol(Name, ExportedOnly);
|
||||
M.verifyFindSymbol(std::move(Sym1));
|
||||
|
||||
// Test findSymbolIn
|
||||
Name = "bar";
|
||||
ExportedOnly = false;
|
||||
M.expectFindSymbolIn(H, Name, ExportedOnly);
|
||||
llvm::JITSymbol Sym2 = T1.findSymbolIn(H, Name, ExportedOnly);
|
||||
M.verifyFindSymbolIn(std::move(Sym2));
|
||||
|
||||
// Test emitAndFinalize
|
||||
M.expectEmitAndFinalize(H);
|
||||
cantFail(T2.emitAndFinalize(H));
|
||||
M.verifyEmitAndFinalize();
|
||||
|
||||
// Test mapSectionAddress
|
||||
char Buffer[24];
|
||||
llvm::JITTargetAddress MockAddress = 255;
|
||||
M.expectMapSectionAddress(H, Buffer, MockAddress);
|
||||
T1.mapSectionAddress(H, Buffer, MockAddress);
|
||||
M.verifyMapSectionAddress();
|
||||
|
||||
// Verify transform getter (non-const)
|
||||
auto Mutatee = std::make_shared<MockObjectFile>(277);
|
||||
auto Out = T2.getTransform()(Mutatee);
|
||||
EXPECT_EQ(*Mutatee, *Out) << "Expected in-place transform";
|
||||
EXPECT_EQ(278, *Mutatee) << "Expected incrementing transform";
|
||||
|
||||
// Verify transform getter (const)
|
||||
auto OwnedObj = std::make_shared<MockObjectFile>(288);
|
||||
const auto &T1C = T1;
|
||||
OwnedObj = T1C.getTransform()(std::move(OwnedObj));
|
||||
EXPECT_EQ(289, *OwnedObj) << "Expected incrementing transform";
|
||||
|
||||
volatile bool RunStaticChecks = false;
|
||||
if (!RunStaticChecks)
|
||||
return;
|
||||
|
||||
// Make sure that ObjectTransformLayer implements the object layer concept
|
||||
// correctly by sandwitching one between an ObjectLinkingLayer and an
|
||||
// IRCompileLayer, verifying that it compiles if we have a call to the
|
||||
// IRComileLayer's addModule that should call the transform layer's
|
||||
// addObject, and also calling the other public transform layer methods
|
||||
// directly to make sure the methods they intend to forward to exist on
|
||||
// the ObjectLinkingLayer.
|
||||
|
||||
// We'll need a concrete MemoryManager class.
|
||||
class NullManager : public llvm::RuntimeDyld::MemoryManager {
|
||||
public:
|
||||
uint8_t *allocateCodeSection(uintptr_t, unsigned, unsigned,
|
||||
llvm::StringRef) override {
|
||||
return nullptr;
|
||||
}
|
||||
uint8_t *allocateDataSection(uintptr_t, unsigned, unsigned, llvm::StringRef,
|
||||
bool) override {
|
||||
return nullptr;
|
||||
}
|
||||
void registerEHFrames(uint8_t *, uint64_t, size_t) override {}
|
||||
void deregisterEHFrames() override {}
|
||||
bool finalizeMemory(std::string *) override { return false; }
|
||||
};
|
||||
|
||||
// Construct the jit layers.
|
||||
RTDyldObjectLinkingLayer BaseLayer(
|
||||
[]() {
|
||||
return std::make_shared<llvm::SectionMemoryManager>();
|
||||
});
|
||||
|
||||
auto IdentityTransform =
|
||||
[](std::shared_ptr<llvm::object::OwningBinary<llvm::object::ObjectFile>>
|
||||
Obj) {
|
||||
return Obj;
|
||||
};
|
||||
ObjectTransformLayer<decltype(BaseLayer), decltype(IdentityTransform)>
|
||||
TransformLayer(BaseLayer, IdentityTransform);
|
||||
auto NullCompiler = [](llvm::Module &) {
|
||||
return llvm::object::OwningBinary<llvm::object::ObjectFile>(nullptr,
|
||||
nullptr);
|
||||
};
|
||||
IRCompileLayer<decltype(TransformLayer), decltype(NullCompiler)>
|
||||
CompileLayer(TransformLayer, NullCompiler);
|
||||
|
||||
// Make sure that the calls from IRCompileLayer to ObjectTransformLayer
|
||||
// compile.
|
||||
auto Resolver = std::make_shared<NullResolver>();
|
||||
cantFail(CompileLayer.addModule(std::shared_ptr<llvm::Module>(), Resolver));
|
||||
|
||||
// Make sure that the calls from ObjectTransformLayer to ObjectLinkingLayer
|
||||
// compile.
|
||||
decltype(TransformLayer)::ObjHandleT H2;
|
||||
cantFail(TransformLayer.emitAndFinalize(H2));
|
||||
TransformLayer.findSymbolIn(H2, Name, false);
|
||||
TransformLayer.findSymbol(Name, true);
|
||||
TransformLayer.mapSectionAddress(H2, nullptr, 0);
|
||||
cantFail(TransformLayer.removeObject(H2));
|
||||
}
|
||||
}
|
@ -1,211 +0,0 @@
|
||||
//===--------------- OrcCAPITest.cpp - Unit tests Orc C API ---------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "OrcTestCommon.h"
|
||||
#include "llvm/ExecutionEngine/Orc/CompileUtils.h"
|
||||
#include "llvm-c/Core.h"
|
||||
#include "llvm-c/OrcBindings.h"
|
||||
#include "llvm-c/Target.h"
|
||||
#include "llvm-c/TargetMachine.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace llvm {
|
||||
|
||||
DEFINE_SIMPLE_CONVERSION_FUNCTIONS(TargetMachine, LLVMTargetMachineRef)
|
||||
|
||||
class OrcCAPIExecutionTest : public testing::Test, public OrcExecutionTest {
|
||||
protected:
|
||||
std::unique_ptr<Module> createTestModule(const Triple &TT) {
|
||||
ModuleBuilder MB(Context, TT.str(), "");
|
||||
Function *TestFunc = MB.createFunctionDecl<int()>("testFunc");
|
||||
Function *Main = MB.createFunctionDecl<int(int, char*[])>("main");
|
||||
|
||||
Main->getBasicBlockList().push_back(BasicBlock::Create(Context));
|
||||
IRBuilder<> B(&Main->back());
|
||||
Value* Result = B.CreateCall(TestFunc);
|
||||
B.CreateRet(Result);
|
||||
|
||||
return MB.takeModule();
|
||||
}
|
||||
|
||||
std::shared_ptr<object::OwningBinary<object::ObjectFile>>
|
||||
createTestObject() {
|
||||
orc::SimpleCompiler IRCompiler(*TM);
|
||||
auto M = createTestModule(TM->getTargetTriple());
|
||||
M->setDataLayout(TM->createDataLayout());
|
||||
return std::make_shared<object::OwningBinary<object::ObjectFile>>(
|
||||
IRCompiler(*M));
|
||||
}
|
||||
|
||||
typedef int (*MainFnTy)();
|
||||
|
||||
static int myTestFuncImpl() {
|
||||
return 42;
|
||||
}
|
||||
|
||||
static char *testFuncName;
|
||||
|
||||
static uint64_t myResolver(const char *Name, void *Ctx) {
|
||||
if (!strncmp(Name, testFuncName, 8))
|
||||
return (uint64_t)&myTestFuncImpl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct CompileContext {
|
||||
CompileContext() : Compiled(false) { }
|
||||
|
||||
OrcCAPIExecutionTest* APIExecTest;
|
||||
std::unique_ptr<Module> M;
|
||||
LLVMOrcModuleHandle H;
|
||||
bool Compiled;
|
||||
};
|
||||
|
||||
static LLVMOrcTargetAddress myCompileCallback(LLVMOrcJITStackRef JITStack,
|
||||
void *Ctx) {
|
||||
CompileContext *CCtx = static_cast<CompileContext*>(Ctx);
|
||||
auto *ET = CCtx->APIExecTest;
|
||||
CCtx->M = ET->createTestModule(ET->TM->getTargetTriple());
|
||||
LLVMSharedModuleRef SM = LLVMOrcMakeSharedModule(wrap(CCtx->M.release()));
|
||||
LLVMOrcAddEagerlyCompiledIR(JITStack, &CCtx->H, SM, myResolver, nullptr);
|
||||
LLVMOrcDisposeSharedModuleRef(SM);
|
||||
CCtx->Compiled = true;
|
||||
LLVMOrcTargetAddress MainAddr;
|
||||
LLVMOrcGetSymbolAddress(JITStack, &MainAddr, "main");
|
||||
LLVMOrcSetIndirectStubPointer(JITStack, "foo", MainAddr);
|
||||
return MainAddr;
|
||||
}
|
||||
};
|
||||
|
||||
char *OrcCAPIExecutionTest::testFuncName = nullptr;
|
||||
|
||||
TEST_F(OrcCAPIExecutionTest, TestEagerIRCompilation) {
|
||||
if (!TM)
|
||||
return;
|
||||
|
||||
LLVMOrcJITStackRef JIT =
|
||||
LLVMOrcCreateInstance(wrap(TM.get()));
|
||||
|
||||
std::unique_ptr<Module> M = createTestModule(TM->getTargetTriple());
|
||||
|
||||
LLVMOrcGetMangledSymbol(JIT, &testFuncName, "testFunc");
|
||||
|
||||
LLVMSharedModuleRef SM = LLVMOrcMakeSharedModule(wrap(M.release()));
|
||||
LLVMOrcModuleHandle H;
|
||||
LLVMOrcAddEagerlyCompiledIR(JIT, &H, SM, myResolver, nullptr);
|
||||
LLVMOrcDisposeSharedModuleRef(SM);
|
||||
LLVMOrcTargetAddress MainAddr;
|
||||
LLVMOrcGetSymbolAddress(JIT, &MainAddr, "main");
|
||||
MainFnTy MainFn = (MainFnTy)MainAddr;
|
||||
int Result = MainFn();
|
||||
EXPECT_EQ(Result, 42)
|
||||
<< "Eagerly JIT'd code did not return expected result";
|
||||
|
||||
LLVMOrcRemoveModule(JIT, H);
|
||||
|
||||
LLVMOrcDisposeMangledSymbol(testFuncName);
|
||||
LLVMOrcDisposeInstance(JIT);
|
||||
}
|
||||
|
||||
TEST_F(OrcCAPIExecutionTest, TestLazyIRCompilation) {
|
||||
if (!TM)
|
||||
return;
|
||||
|
||||
LLVMOrcJITStackRef JIT =
|
||||
LLVMOrcCreateInstance(wrap(TM.get()));
|
||||
|
||||
std::unique_ptr<Module> M = createTestModule(TM->getTargetTriple());
|
||||
|
||||
LLVMOrcGetMangledSymbol(JIT, &testFuncName, "testFunc");
|
||||
|
||||
LLVMSharedModuleRef SM = LLVMOrcMakeSharedModule(wrap(M.release()));
|
||||
LLVMOrcModuleHandle H;
|
||||
LLVMOrcAddLazilyCompiledIR(JIT, &H, SM, myResolver, nullptr);
|
||||
LLVMOrcDisposeSharedModuleRef(SM);
|
||||
LLVMOrcTargetAddress MainAddr;
|
||||
LLVMOrcGetSymbolAddress(JIT, &MainAddr, "main");
|
||||
MainFnTy MainFn = (MainFnTy)MainAddr;
|
||||
int Result = MainFn();
|
||||
EXPECT_EQ(Result, 42)
|
||||
<< "Lazily JIT'd code did not return expected result";
|
||||
|
||||
LLVMOrcRemoveModule(JIT, H);
|
||||
|
||||
LLVMOrcDisposeMangledSymbol(testFuncName);
|
||||
LLVMOrcDisposeInstance(JIT);
|
||||
}
|
||||
|
||||
TEST_F(OrcCAPIExecutionTest, TestAddObjectFile) {
|
||||
if (!TM)
|
||||
return;
|
||||
|
||||
std::unique_ptr<MemoryBuffer> ObjBuffer;
|
||||
{
|
||||
auto OwningObj = createTestObject();
|
||||
auto ObjAndBuffer = OwningObj->takeBinary();
|
||||
ObjBuffer = std::move(ObjAndBuffer.second);
|
||||
}
|
||||
|
||||
LLVMOrcJITStackRef JIT =
|
||||
LLVMOrcCreateInstance(wrap(TM.get()));
|
||||
LLVMOrcGetMangledSymbol(JIT, &testFuncName, "testFunc");
|
||||
|
||||
LLVMOrcModuleHandle H;
|
||||
LLVMOrcAddObjectFile(JIT, &H, wrap(ObjBuffer.release()), myResolver, nullptr);
|
||||
LLVMOrcTargetAddress MainAddr;
|
||||
LLVMOrcGetSymbolAddress(JIT, &MainAddr, "main");
|
||||
MainFnTy MainFn = (MainFnTy)MainAddr;
|
||||
int Result = MainFn();
|
||||
EXPECT_EQ(Result, 42)
|
||||
<< "Lazily JIT'd code did not return expected result";
|
||||
|
||||
LLVMOrcRemoveModule(JIT, H);
|
||||
|
||||
LLVMOrcDisposeMangledSymbol(testFuncName);
|
||||
LLVMOrcDisposeInstance(JIT);
|
||||
}
|
||||
|
||||
TEST_F(OrcCAPIExecutionTest, TestDirectCallbacksAPI) {
|
||||
if (!TM)
|
||||
return;
|
||||
|
||||
LLVMOrcJITStackRef JIT =
|
||||
LLVMOrcCreateInstance(wrap(TM.get()));
|
||||
|
||||
LLVMOrcGetMangledSymbol(JIT, &testFuncName, "testFunc");
|
||||
|
||||
CompileContext C;
|
||||
C.APIExecTest = this;
|
||||
LLVMOrcTargetAddress CCAddr;
|
||||
LLVMOrcCreateLazyCompileCallback(JIT, &CCAddr, myCompileCallback, &C);
|
||||
LLVMOrcCreateIndirectStub(JIT, "foo", CCAddr);
|
||||
LLVMOrcTargetAddress MainAddr;
|
||||
LLVMOrcGetSymbolAddress(JIT, &MainAddr, "foo");
|
||||
MainFnTy FooFn = (MainFnTy)MainAddr;
|
||||
int Result = FooFn();
|
||||
EXPECT_TRUE(C.Compiled)
|
||||
<< "Function wasn't lazily compiled";
|
||||
EXPECT_EQ(Result, 42)
|
||||
<< "Direct-callback JIT'd code did not return expected result";
|
||||
|
||||
C.Compiled = false;
|
||||
FooFn();
|
||||
EXPECT_FALSE(C.Compiled)
|
||||
<< "Direct-callback JIT'd code was JIT'd twice";
|
||||
|
||||
LLVMOrcRemoveModule(JIT, C.H);
|
||||
|
||||
LLVMOrcDisposeMangledSymbol(testFuncName);
|
||||
LLVMOrcDisposeInstance(JIT);
|
||||
}
|
||||
|
||||
} // namespace llvm
|
@ -1,25 +0,0 @@
|
||||
//===--------- OrcTestCommon.cpp - Utilities for Orc Unit Tests -----------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Common utilities for Orc unit tests.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "OrcTestCommon.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
bool OrcNativeTarget::NativeTargetInitialized = false;
|
||||
|
||||
ModuleBuilder::ModuleBuilder(LLVMContext &Context, StringRef Triple,
|
||||
StringRef Name)
|
||||
: M(new Module(Name, Context)) {
|
||||
if (Triple != "")
|
||||
M->setTargetTriple(Triple);
|
||||
}
|
@ -1,193 +0,0 @@
|
||||
//===------ OrcTestCommon.h - Utilities for Orc Unit Tests ------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Common utilities for the Orc unit tests.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
|
||||
#ifndef LLVM_UNITTESTS_EXECUTIONENGINE_ORC_ORCTESTCOMMON_H
|
||||
#define LLVM_UNITTESTS_EXECUTIONENGINE_ORC_ORCTESTCOMMON_H
|
||||
|
||||
#include "llvm/ExecutionEngine/ExecutionEngine.h"
|
||||
#include "llvm/ExecutionEngine/JITSymbol.h"
|
||||
#include "llvm/IR/Function.h"
|
||||
#include "llvm/IR/IRBuilder.h"
|
||||
#include "llvm/IR/LLVMContext.h"
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/IR/TypeBuilder.h"
|
||||
#include "llvm/Object/ObjectFile.h"
|
||||
#include "llvm/Support/TargetSelect.h"
|
||||
#include <memory>
|
||||
|
||||
namespace llvm {
|
||||
|
||||
class OrcNativeTarget {
|
||||
public:
|
||||
static void initialize() {
|
||||
if (!NativeTargetInitialized) {
|
||||
InitializeNativeTarget();
|
||||
InitializeNativeTargetAsmParser();
|
||||
InitializeNativeTargetAsmPrinter();
|
||||
NativeTargetInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static bool NativeTargetInitialized;
|
||||
};
|
||||
|
||||
// Base class for Orc tests that will execute code.
|
||||
class OrcExecutionTest {
|
||||
public:
|
||||
|
||||
OrcExecutionTest() {
|
||||
|
||||
// Initialize the native target if it hasn't been done already.
|
||||
OrcNativeTarget::initialize();
|
||||
|
||||
// Try to select a TargetMachine for the host.
|
||||
TM.reset(EngineBuilder().selectTarget());
|
||||
|
||||
if (TM) {
|
||||
// If we found a TargetMachine, check that it's one that Orc supports.
|
||||
const Triple& TT = TM->getTargetTriple();
|
||||
|
||||
if ((TT.getArch() != Triple::x86_64 && TT.getArch() != Triple::x86) ||
|
||||
TT.isOSWindows())
|
||||
TM = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
protected:
|
||||
LLVMContext Context;
|
||||
std::unique_ptr<TargetMachine> TM;
|
||||
};
|
||||
|
||||
class ModuleBuilder {
|
||||
public:
|
||||
ModuleBuilder(LLVMContext &Context, StringRef Triple,
|
||||
StringRef Name);
|
||||
|
||||
template <typename FuncType>
|
||||
Function* createFunctionDecl(StringRef Name) {
|
||||
return Function::Create(
|
||||
TypeBuilder<FuncType, false>::get(M->getContext()),
|
||||
GlobalValue::ExternalLinkage, Name, M.get());
|
||||
}
|
||||
|
||||
Module* getModule() { return M.get(); }
|
||||
const Module* getModule() const { return M.get(); }
|
||||
std::unique_ptr<Module> takeModule() { return std::move(M); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<Module> M;
|
||||
};
|
||||
|
||||
// Dummy struct type.
|
||||
struct DummyStruct {
|
||||
int X[256];
|
||||
};
|
||||
|
||||
// TypeBuilder specialization for DummyStruct.
|
||||
template <bool XCompile>
|
||||
class TypeBuilder<DummyStruct, XCompile> {
|
||||
public:
|
||||
static StructType *get(LLVMContext &Context) {
|
||||
return StructType::get(
|
||||
TypeBuilder<types::i<32>[256], XCompile>::get(Context));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename HandleT, typename ModuleT>
|
||||
class MockBaseLayer {
|
||||
public:
|
||||
|
||||
using ModuleHandleT = HandleT;
|
||||
|
||||
using AddModuleSignature =
|
||||
Expected<ModuleHandleT>(ModuleT M,
|
||||
std::shared_ptr<JITSymbolResolver> R);
|
||||
|
||||
using RemoveModuleSignature = Error(ModuleHandleT H);
|
||||
using FindSymbolSignature = JITSymbol(const std::string &Name,
|
||||
bool ExportedSymbolsOnly);
|
||||
using FindSymbolInSignature = JITSymbol(ModuleHandleT H,
|
||||
const std::string &Name,
|
||||
bool ExportedSymbolsONly);
|
||||
using EmitAndFinalizeSignature = Error(ModuleHandleT H);
|
||||
|
||||
std::function<AddModuleSignature> addModuleImpl;
|
||||
std::function<RemoveModuleSignature> removeModuleImpl;
|
||||
std::function<FindSymbolSignature> findSymbolImpl;
|
||||
std::function<FindSymbolInSignature> findSymbolInImpl;
|
||||
std::function<EmitAndFinalizeSignature> emitAndFinalizeImpl;
|
||||
|
||||
Expected<ModuleHandleT> addModule(ModuleT M,
|
||||
std::shared_ptr<JITSymbolResolver> R) {
|
||||
assert(addModuleImpl &&
|
||||
"addModule called, but no mock implementation was provided");
|
||||
return addModuleImpl(std::move(M), std::move(R));
|
||||
}
|
||||
|
||||
Error removeModule(ModuleHandleT H) {
|
||||
assert(removeModuleImpl &&
|
||||
"removeModule called, but no mock implementation was provided");
|
||||
return removeModuleImpl(H);
|
||||
}
|
||||
|
||||
JITSymbol findSymbol(const std::string &Name, bool ExportedSymbolsOnly) {
|
||||
assert(findSymbolImpl &&
|
||||
"findSymbol called, but no mock implementation was provided");
|
||||
return findSymbolImpl(Name, ExportedSymbolsOnly);
|
||||
}
|
||||
|
||||
JITSymbol findSymbolIn(ModuleHandleT H, const std::string &Name,
|
||||
bool ExportedSymbolsOnly) {
|
||||
assert(findSymbolInImpl &&
|
||||
"findSymbolIn called, but no mock implementation was provided");
|
||||
return findSymbolInImpl(H, Name, ExportedSymbolsOnly);
|
||||
}
|
||||
|
||||
Error emitAndFinaliez(ModuleHandleT H) {
|
||||
assert(emitAndFinalizeImpl &&
|
||||
"emitAndFinalize called, but no mock implementation was provided");
|
||||
return emitAndFinalizeImpl(H);
|
||||
}
|
||||
};
|
||||
|
||||
class ReturnNullJITSymbol {
|
||||
public:
|
||||
template <typename... Args>
|
||||
JITSymbol operator()(Args...) const {
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ReturnT>
|
||||
class DoNothingAndReturn {
|
||||
public:
|
||||
DoNothingAndReturn(ReturnT Ret) : Ret(std::move(Ret)) {}
|
||||
|
||||
template <typename... Args>
|
||||
void operator()(Args...) const { return Ret; }
|
||||
private:
|
||||
ReturnT Ret;
|
||||
};
|
||||
|
||||
template <>
|
||||
class DoNothingAndReturn<void> {
|
||||
public:
|
||||
template <typename... Args>
|
||||
void operator()(Args...) const { }
|
||||
};
|
||||
|
||||
} // namespace llvm
|
||||
|
||||
#endif
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user