diff options
author | rdivacky <rdivacky@FreeBSD.org> | 2009-10-14 17:57:32 +0000 |
---|---|---|
committer | rdivacky <rdivacky@FreeBSD.org> | 2009-10-14 17:57:32 +0000 |
commit | cd749a9c07f1de2fb8affde90537efa4bc3e7c54 (patch) | |
tree | b21f6de4e08b89bb7931806bab798fc2a5e3a686 /unittests/ExecutionEngine | |
parent | 72621d11de5b873f1695f391eb95f0b336c3d2d4 (diff) | |
download | FreeBSD-src-cd749a9c07f1de2fb8affde90537efa4bc3e7c54.zip FreeBSD-src-cd749a9c07f1de2fb8affde90537efa4bc3e7c54.tar.gz |
Update llvm to r84119.
Diffstat (limited to 'unittests/ExecutionEngine')
-rw-r--r-- | unittests/ExecutionEngine/ExecutionEngineTest.cpp | 129 | ||||
-rw-r--r-- | unittests/ExecutionEngine/JIT/JITEventListenerTest.cpp | 14 | ||||
-rw-r--r-- | unittests/ExecutionEngine/JIT/JITMemoryManagerTest.cpp | 277 | ||||
-rw-r--r-- | unittests/ExecutionEngine/JIT/JITTest.cpp | 277 | ||||
-rw-r--r-- | unittests/ExecutionEngine/Makefile | 7 |
5 files changed, 694 insertions, 10 deletions
diff --git a/unittests/ExecutionEngine/ExecutionEngineTest.cpp b/unittests/ExecutionEngine/ExecutionEngineTest.cpp new file mode 100644 index 0000000..904ee2b --- /dev/null +++ b/unittests/ExecutionEngine/ExecutionEngineTest.cpp @@ -0,0 +1,129 @@ +//===- 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/DerivedTypes.h" +#include "llvm/GlobalVariable.h" +#include "llvm/LLVMContext.h" +#include "llvm/Module.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/ExecutionEngine/Interpreter.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +class ExecutionEngineTest : public testing::Test { +protected: + ExecutionEngineTest() + : M(new Module("<main>", getGlobalContext())), + Engine(EngineBuilder(M).create()) { + } + + virtual void SetUp() { + ASSERT_TRUE(Engine.get() != NULL); + } + + GlobalVariable *NewExtGlobal(const Type *T, const Twine &Name) { + return new GlobalVariable(*M, T, false, // Not constant. + GlobalValue::ExternalLinkage, NULL, Name); + } + + Module *const M; + const OwningPtr<ExecutionEngine> Engine; +}; + +TEST_F(ExecutionEngineTest, ForwardGlobalMapping) { + GlobalVariable *G1 = + NewExtGlobal(Type::getInt32Ty(getGlobalContext()), "Global1"); + int32_t Mem1 = 3; + Engine->addGlobalMapping(G1, &Mem1); + EXPECT_EQ(&Mem1, Engine->getPointerToGlobalIfAvailable(G1)); + int32_t Mem2 = 4; + Engine->updateGlobalMapping(G1, &Mem2); + EXPECT_EQ(&Mem2, Engine->getPointerToGlobalIfAvailable(G1)); + Engine->updateGlobalMapping(G1, NULL); + EXPECT_EQ(NULL, Engine->getPointerToGlobalIfAvailable(G1)); + Engine->updateGlobalMapping(G1, &Mem2); + EXPECT_EQ(&Mem2, Engine->getPointerToGlobalIfAvailable(G1)); + + GlobalVariable *G2 = + NewExtGlobal(Type::getInt32Ty(getGlobalContext()), "Global1"); + EXPECT_EQ(NULL, 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(getGlobalContext()), "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(NULL, Engine->getGlobalValueAtAddress(&Mem1)); + EXPECT_EQ(G1, Engine->getGlobalValueAtAddress(&Mem2)); + + GlobalVariable *G2 = + NewExtGlobal(Type::getInt32Ty(getGlobalContext()), "Global2"); + Engine->updateGlobalMapping(G2, &Mem1); + EXPECT_EQ(G2, Engine->getGlobalValueAtAddress(&Mem1)); + EXPECT_EQ(G1, Engine->getGlobalValueAtAddress(&Mem2)); + Engine->updateGlobalMapping(G1, NULL); + EXPECT_EQ(G2, Engine->getGlobalValueAtAddress(&Mem1)) + << "Removing one mapping doesn't affect a different one."; + EXPECT_EQ(NULL, Engine->getGlobalValueAtAddress(&Mem2)); + Engine->updateGlobalMapping(G2, &Mem2); + EXPECT_EQ(NULL, 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(getGlobalContext()), "Global1"); + + int32_t Mem1 = 3; + Engine->addGlobalMapping(G1, &Mem1); + EXPECT_EQ(G1, Engine->getGlobalValueAtAddress(&Mem1)); + + Engine->clearGlobalMappingsFromModule(M); + + EXPECT_EQ(NULL, Engine->getGlobalValueAtAddress(&Mem1)); + + GlobalVariable *G2 = + NewExtGlobal(Type::getInt32Ty(getGlobalContext()), "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(getGlobalContext()), "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(NULL, Engine->getGlobalValueAtAddress(&Mem1)); +} + +} diff --git a/unittests/ExecutionEngine/JIT/JITEventListenerTest.cpp b/unittests/ExecutionEngine/JIT/JITEventListenerTest.cpp index 1007ae1..87e3280 100644 --- a/unittests/ExecutionEngine/JIT/JITEventListenerTest.cpp +++ b/unittests/ExecutionEngine/JIT/JITEventListenerTest.cpp @@ -65,8 +65,10 @@ struct RecordingJITEventListener : public JITEventListener { class JITEventListenerTest : public testing::Test { protected: JITEventListenerTest() - : M(new Module("module", *new LLVMContext())), - EE(ExecutionEngine::createJIT(new ExistingModuleProvider(M))) { + : M(new Module("module", getGlobalContext())), + EE(EngineBuilder(M) + .setEngineKind(EngineKind::JIT) + .create()) { } Module *M; @@ -75,11 +77,11 @@ class JITEventListenerTest : public testing::Test { Function *buildFunction(Module *M) { Function *Result = Function::Create( - TypeBuilder<int32_t(int32_t), false>::get(), + TypeBuilder<int32_t(int32_t), false>::get(getGlobalContext()), GlobalValue::ExternalLinkage, "id", M); Value *Arg = Result->arg_begin(); - BasicBlock *BB = BasicBlock::Create("entry", Result); - ReturnInst::Create(Arg, BB); + BasicBlock *BB = BasicBlock::Create(M->getContext(), "entry", Result); + ReturnInst::Create(M->getContext(), Arg, BB); return Result; } @@ -232,7 +234,7 @@ TEST_F(JITEventListenerTest, MatchesMachineCodeInfo) { class JITEnvironment : public testing::Environment { virtual void SetUp() { - // Required for ExecutionEngine::createJIT to create a JIT. + // Required to create a JIT. InitializeNativeTarget(); } }; diff --git a/unittests/ExecutionEngine/JIT/JITMemoryManagerTest.cpp b/unittests/ExecutionEngine/JIT/JITMemoryManagerTest.cpp new file mode 100644 index 0000000..89a4be7 --- /dev/null +++ b/unittests/ExecutionEngine/JIT/JITMemoryManagerTest.cpp @@ -0,0 +1,277 @@ +//===- JITMemoryManagerTest.cpp - Unit tests for the JIT memory manager ---===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/ExecutionEngine/JITMemoryManager.h" +#include "llvm/DerivedTypes.h" +#include "llvm/Function.h" +#include "llvm/GlobalValue.h" + +using namespace llvm; + +namespace { + +Function *makeFakeFunction() { + std::vector<const Type*> params; + const FunctionType *FTy = + FunctionType::get(Type::getVoidTy(getGlobalContext()), params, false); + return Function::Create(FTy, GlobalValue::ExternalLinkage); +} + +// Allocate three simple functions that fit in the initial slab. This exercises +// the code in the case that we don't have to allocate more memory to store the +// function bodies. +TEST(JITMemoryManagerTest, NoAllocations) { + OwningPtr<JITMemoryManager> MemMgr( + JITMemoryManager::CreateDefaultMemManager()); + uintptr_t size; + uint8_t *start; + std::string Error; + + // Allocate the functions. + OwningPtr<Function> F1(makeFakeFunction()); + size = 1024; + start = MemMgr->startFunctionBody(F1.get(), size); + memset(start, 0xFF, 1024); + MemMgr->endFunctionBody(F1.get(), start, start + 1024); + EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; + + OwningPtr<Function> F2(makeFakeFunction()); + size = 1024; + start = MemMgr->startFunctionBody(F2.get(), size); + memset(start, 0xFF, 1024); + MemMgr->endFunctionBody(F2.get(), start, start + 1024); + EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; + + OwningPtr<Function> F3(makeFakeFunction()); + size = 1024; + start = MemMgr->startFunctionBody(F3.get(), size); + memset(start, 0xFF, 1024); + MemMgr->endFunctionBody(F3.get(), start, start + 1024); + EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; + + // Deallocate them out of order, in case that matters. + MemMgr->deallocateMemForFunction(F2.get()); + EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; + MemMgr->deallocateMemForFunction(F1.get()); + EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; + MemMgr->deallocateMemForFunction(F3.get()); + EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; +} + +// Make three large functions that take up most of the space in the slab. Then +// try allocating three smaller functions that don't require additional slabs. +TEST(JITMemoryManagerTest, TestCodeAllocation) { + OwningPtr<JITMemoryManager> MemMgr( + JITMemoryManager::CreateDefaultMemManager()); + uintptr_t size; + uint8_t *start; + std::string Error; + + // Big functions are a little less than the largest block size. + const uintptr_t smallFuncSize = 1024; + const uintptr_t bigFuncSize = (MemMgr->GetDefaultCodeSlabSize() - + smallFuncSize * 2); + + // Allocate big functions + OwningPtr<Function> F1(makeFakeFunction()); + size = bigFuncSize; + start = MemMgr->startFunctionBody(F1.get(), size); + ASSERT_LE(bigFuncSize, size); + memset(start, 0xFF, bigFuncSize); + MemMgr->endFunctionBody(F1.get(), start, start + bigFuncSize); + EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; + + OwningPtr<Function> F2(makeFakeFunction()); + size = bigFuncSize; + start = MemMgr->startFunctionBody(F2.get(), size); + ASSERT_LE(bigFuncSize, size); + memset(start, 0xFF, bigFuncSize); + MemMgr->endFunctionBody(F2.get(), start, start + bigFuncSize); + EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; + + OwningPtr<Function> F3(makeFakeFunction()); + size = bigFuncSize; + start = MemMgr->startFunctionBody(F3.get(), size); + ASSERT_LE(bigFuncSize, size); + memset(start, 0xFF, bigFuncSize); + MemMgr->endFunctionBody(F3.get(), start, start + bigFuncSize); + EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; + + // Check that each large function took it's own slab. + EXPECT_EQ(3U, MemMgr->GetNumCodeSlabs()); + + // Allocate small functions + OwningPtr<Function> F4(makeFakeFunction()); + size = smallFuncSize; + start = MemMgr->startFunctionBody(F4.get(), size); + ASSERT_LE(smallFuncSize, size); + memset(start, 0xFF, smallFuncSize); + MemMgr->endFunctionBody(F4.get(), start, start + smallFuncSize); + EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; + + OwningPtr<Function> F5(makeFakeFunction()); + size = smallFuncSize; + start = MemMgr->startFunctionBody(F5.get(), size); + ASSERT_LE(smallFuncSize, size); + memset(start, 0xFF, smallFuncSize); + MemMgr->endFunctionBody(F5.get(), start, start + smallFuncSize); + EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; + + OwningPtr<Function> F6(makeFakeFunction()); + size = smallFuncSize; + start = MemMgr->startFunctionBody(F6.get(), size); + ASSERT_LE(smallFuncSize, size); + memset(start, 0xFF, smallFuncSize); + MemMgr->endFunctionBody(F6.get(), start, start + smallFuncSize); + EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; + + // Check that the small functions didn't allocate any new slabs. + EXPECT_EQ(3U, MemMgr->GetNumCodeSlabs()); + + // Deallocate them out of order, in case that matters. + MemMgr->deallocateMemForFunction(F2.get()); + EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; + MemMgr->deallocateMemForFunction(F1.get()); + EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; + MemMgr->deallocateMemForFunction(F4.get()); + EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; + MemMgr->deallocateMemForFunction(F3.get()); + EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; + MemMgr->deallocateMemForFunction(F5.get()); + EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; + MemMgr->deallocateMemForFunction(F6.get()); + EXPECT_TRUE(MemMgr->CheckInvariants(Error)) << Error; +} + +// Allocate five global ints of varying widths and alignment, and check their +// alignment and overlap. +TEST(JITMemoryManagerTest, TestSmallGlobalInts) { + OwningPtr<JITMemoryManager> MemMgr( + JITMemoryManager::CreateDefaultMemManager()); + uint8_t *a = (uint8_t *)MemMgr->allocateGlobal(8, 0); + uint16_t *b = (uint16_t*)MemMgr->allocateGlobal(16, 2); + uint32_t *c = (uint32_t*)MemMgr->allocateGlobal(32, 4); + uint64_t *d = (uint64_t*)MemMgr->allocateGlobal(64, 8); + + // Check the alignment. + EXPECT_EQ(0U, ((uintptr_t)b) & 0x1); + EXPECT_EQ(0U, ((uintptr_t)c) & 0x3); + EXPECT_EQ(0U, ((uintptr_t)d) & 0x7); + + // Initialize them each one at a time and make sure they don't overlap. + *a = 0xff; + *b = 0U; + *c = 0U; + *d = 0U; + EXPECT_EQ(0xffU, *a); + EXPECT_EQ(0U, *b); + EXPECT_EQ(0U, *c); + EXPECT_EQ(0U, *d); + *a = 0U; + *b = 0xffffU; + EXPECT_EQ(0U, *a); + EXPECT_EQ(0xffffU, *b); + EXPECT_EQ(0U, *c); + EXPECT_EQ(0U, *d); + *b = 0U; + *c = 0xffffffffU; + EXPECT_EQ(0U, *a); + EXPECT_EQ(0U, *b); + EXPECT_EQ(0xffffffffU, *c); + EXPECT_EQ(0U, *d); + *c = 0U; + *d = 0xffffffffffffffffULL; + EXPECT_EQ(0U, *a); + EXPECT_EQ(0U, *b); + EXPECT_EQ(0U, *c); + EXPECT_EQ(0xffffffffffffffffULL, *d); + + // Make sure we didn't allocate any extra slabs for this tiny amount of data. + EXPECT_EQ(1U, MemMgr->GetNumDataSlabs()); +} + +// Allocate a small global, a big global, and a third global, and make sure we +// only use two slabs for that. +TEST(JITMemoryManagerTest, TestLargeGlobalArray) { + OwningPtr<JITMemoryManager> MemMgr( + JITMemoryManager::CreateDefaultMemManager()); + size_t Size = 4 * MemMgr->GetDefaultDataSlabSize(); + uint64_t *a = (uint64_t*)MemMgr->allocateGlobal(64, 8); + uint8_t *g = MemMgr->allocateGlobal(Size, 8); + uint64_t *b = (uint64_t*)MemMgr->allocateGlobal(64, 8); + + // Check the alignment. + EXPECT_EQ(0U, ((uintptr_t)a) & 0x7); + EXPECT_EQ(0U, ((uintptr_t)g) & 0x7); + EXPECT_EQ(0U, ((uintptr_t)b) & 0x7); + + // Initialize them to make sure we don't segfault and make sure they don't + // overlap. + memset(a, 0x1, 8); + memset(g, 0x2, Size); + memset(b, 0x3, 8); + EXPECT_EQ(0x0101010101010101ULL, *a); + // Just check the edges. + EXPECT_EQ(0x02U, g[0]); + EXPECT_EQ(0x02U, g[Size - 1]); + EXPECT_EQ(0x0303030303030303ULL, *b); + + // Check the number of slabs. + EXPECT_EQ(2U, MemMgr->GetNumDataSlabs()); +} + +// Allocate lots of medium globals so that we can test moving the bump allocator +// to a new slab. +TEST(JITMemoryManagerTest, TestManyGlobals) { + OwningPtr<JITMemoryManager> MemMgr( + JITMemoryManager::CreateDefaultMemManager()); + size_t SlabSize = MemMgr->GetDefaultDataSlabSize(); + size_t Size = 128; + int Iters = (SlabSize / Size) + 1; + + // We should start with one slab. + EXPECT_EQ(1U, MemMgr->GetNumDataSlabs()); + + // After allocating a bunch of globals, we should have two. + for (int I = 0; I < Iters; ++I) + MemMgr->allocateGlobal(Size, 8); + EXPECT_EQ(2U, MemMgr->GetNumDataSlabs()); + + // And after much more, we should have three. + for (int I = 0; I < Iters; ++I) + MemMgr->allocateGlobal(Size, 8); + EXPECT_EQ(3U, MemMgr->GetNumDataSlabs()); +} + +// Allocate lots of function stubs so that we can test moving the stub bump +// allocator to a new slab. +TEST(JITMemoryManagerTest, TestManyStubs) { + OwningPtr<JITMemoryManager> MemMgr( + JITMemoryManager::CreateDefaultMemManager()); + size_t SlabSize = MemMgr->GetDefaultStubSlabSize(); + size_t Size = 128; + int Iters = (SlabSize / Size) + 1; + + // We should start with one slab. + EXPECT_EQ(1U, MemMgr->GetNumStubSlabs()); + + // After allocating a bunch of stubs, we should have two. + for (int I = 0; I < Iters; ++I) + MemMgr->allocateStub(NULL, Size, 8); + EXPECT_EQ(2U, MemMgr->GetNumStubSlabs()); + + // And after much more, we should have three. + for (int I = 0; I < Iters; ++I) + MemMgr->allocateStub(NULL, Size, 8); + EXPECT_EQ(3U, MemMgr->GetNumStubSlabs()); +} + +} diff --git a/unittests/ExecutionEngine/JIT/JITTest.cpp b/unittests/ExecutionEngine/JIT/JITTest.cpp new file mode 100644 index 0000000..55d3749 --- /dev/null +++ b/unittests/ExecutionEngine/JIT/JITTest.cpp @@ -0,0 +1,277 @@ +//===- JITTest.cpp - Unit tests for the JIT -------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/BasicBlock.h" +#include "llvm/Constant.h" +#include "llvm/Constants.h" +#include "llvm/DerivedTypes.h" +#include "llvm/ExecutionEngine/JIT.h" +#include "llvm/ExecutionEngine/JITMemoryManager.h" +#include "llvm/Function.h" +#include "llvm/GlobalValue.h" +#include "llvm/GlobalVariable.h" +#include "llvm/LLVMContext.h" +#include "llvm/Module.h" +#include "llvm/ModuleProvider.h" +#include "llvm/Support/IRBuilder.h" +#include "llvm/Support/TypeBuilder.h" +#include "llvm/Target/TargetSelect.h" +#include "llvm/Type.h" + +using namespace llvm; + +namespace { + +Function *makeReturnGlobal(std::string Name, GlobalVariable *G, Module *M) { + std::vector<const Type*> params; + const FunctionType *FTy = FunctionType::get(G->getType()->getElementType(), + params, false); + Function *F = Function::Create(FTy, GlobalValue::ExternalLinkage, Name, M); + BasicBlock *Entry = BasicBlock::Create(M->getContext(), "entry", F); + IRBuilder<> builder(Entry); + Value *Load = builder.CreateLoad(G); + const Type *GTy = G->getType()->getElementType(); + Value *Add = builder.CreateAdd(Load, ConstantInt::get(GTy, 1LL)); + builder.CreateStore(Add, G); + builder.CreateRet(Add); + return F; +} + +class JITTest : public testing::Test { + protected: + virtual void SetUp() { + M = new Module("<main>", Context); + std::string Error; + TheJIT.reset(EngineBuilder(M).setEngineKind(EngineKind::JIT) + .setErrorStr(&Error).create()); + ASSERT_TRUE(TheJIT.get() != NULL) << Error; + } + + LLVMContext Context; + Module *M; // Owned by ExecutionEngine. + OwningPtr<ExecutionEngine> TheJIT; +}; + +// Regression test for a bug. The JIT used to allocate globals inside the same +// memory block used for the function, and when the function code was freed, +// the global was left in the same place. This test allocates a function +// that uses and global, deallocates it, and then makes sure that the global +// stays alive after that. +TEST(JIT, GlobalInFunction) { + LLVMContext context; + Module *M = new Module("<main>", context); + ExistingModuleProvider *MP = new ExistingModuleProvider(M); + + JITMemoryManager *MemMgr = JITMemoryManager::CreateDefaultMemManager(); + // Tell the memory manager to poison freed memory so that accessing freed + // memory is more easily tested. + MemMgr->setPoisonMemory(true); + std::string Error; + OwningPtr<ExecutionEngine> JIT(EngineBuilder(MP) + .setEngineKind(EngineKind::JIT) + .setErrorStr(&Error) + .setJITMemoryManager(MemMgr) + // The next line enables the fix: + .setAllocateGVsWithCode(false) + .create()); + ASSERT_EQ(Error, ""); + + // Create a global variable. + const Type *GTy = Type::getInt32Ty(context); + GlobalVariable *G = new GlobalVariable( + *M, + GTy, + false, // Not constant. + GlobalValue::InternalLinkage, + Constant::getNullValue(GTy), + "myglobal"); + + // Make a function that points to a global. + Function *F1 = makeReturnGlobal("F1", G, M); + + // Get the pointer to the native code to force it to JIT the function and + // allocate space for the global. + void (*F1Ptr)() = + reinterpret_cast<void(*)()>((intptr_t)JIT->getPointerToFunction(F1)); + + // Since F1 was codegen'd, a pointer to G should be available. + int32_t *GPtr = (int32_t*)JIT->getPointerToGlobalIfAvailable(G); + ASSERT_NE((int32_t*)NULL, GPtr); + EXPECT_EQ(0, *GPtr); + + // F1() should increment G. + F1Ptr(); + EXPECT_EQ(1, *GPtr); + + // Make a second function identical to the first, referring to the same + // global. + Function *F2 = makeReturnGlobal("F2", G, M); + void (*F2Ptr)() = + reinterpret_cast<void(*)()>((intptr_t)JIT->getPointerToFunction(F2)); + + // F2() should increment G. + F2Ptr(); + EXPECT_EQ(2, *GPtr); + + // Deallocate F1. + JIT->freeMachineCodeForFunction(F1); + + // F2() should *still* increment G. + F2Ptr(); + EXPECT_EQ(3, *GPtr); +} + +int PlusOne(int arg) { + return arg + 1; +} + +TEST_F(JITTest, FarCallToKnownFunction) { + // x86-64 can only make direct calls to functions within 32 bits of + // the current PC. To call anything farther away, we have to load + // the address into a register and call through the register. The + // current JIT does this by allocating a stub for any far call. + // There was a bug in which the JIT tried to emit a direct call when + // the target was already in the JIT's global mappings and lazy + // compilation was disabled. + + Function *KnownFunction = Function::Create( + TypeBuilder<int(int), false>::get(Context), + GlobalValue::ExternalLinkage, "known", M); + TheJIT->addGlobalMapping(KnownFunction, (void*)(intptr_t)PlusOne); + + // int test() { return known(7); } + Function *TestFunction = Function::Create( + TypeBuilder<int(), false>::get(Context), + GlobalValue::ExternalLinkage, "test", M); + BasicBlock *Entry = BasicBlock::Create(Context, "entry", TestFunction); + IRBuilder<> Builder(Entry); + Value *result = Builder.CreateCall( + KnownFunction, + ConstantInt::get(TypeBuilder<int, false>::get(Context), 7)); + Builder.CreateRet(result); + + TheJIT->EnableDlsymStubs(false); + TheJIT->DisableLazyCompilation(); + int (*TestFunctionPtr)() = reinterpret_cast<int(*)()>( + (intptr_t)TheJIT->getPointerToFunction(TestFunction)); + // This used to crash in trying to call PlusOne(). + EXPECT_EQ(8, TestFunctionPtr()); +} + +// Test a function C which calls A and B which call each other. +TEST_F(JITTest, NonLazyCompilationStillNeedsStubs) { + TheJIT->DisableLazyCompilation(); + + const FunctionType *Func1Ty = + cast<FunctionType>(TypeBuilder<void(void), false>::get(Context)); + std::vector<const Type*> arg_types; + arg_types.push_back(Type::getInt1Ty(Context)); + const FunctionType *FuncTy = FunctionType::get( + Type::getVoidTy(Context), arg_types, false); + Function *Func1 = Function::Create(Func1Ty, Function::ExternalLinkage, + "func1", M); + Function *Func2 = Function::Create(FuncTy, Function::InternalLinkage, + "func2", M); + Function *Func3 = Function::Create(FuncTy, Function::InternalLinkage, + "func3", M); + BasicBlock *Block1 = BasicBlock::Create(Context, "block1", Func1); + BasicBlock *Block2 = BasicBlock::Create(Context, "block2", Func2); + BasicBlock *True2 = BasicBlock::Create(Context, "cond_true", Func2); + BasicBlock *False2 = BasicBlock::Create(Context, "cond_false", Func2); + BasicBlock *Block3 = BasicBlock::Create(Context, "block3", Func3); + BasicBlock *True3 = BasicBlock::Create(Context, "cond_true", Func3); + BasicBlock *False3 = BasicBlock::Create(Context, "cond_false", Func3); + + // Make Func1 call Func2(0) and Func3(0). + IRBuilder<> Builder(Block1); + Builder.CreateCall(Func2, ConstantInt::getTrue(Context)); + Builder.CreateCall(Func3, ConstantInt::getTrue(Context)); + Builder.CreateRetVoid(); + + // void Func2(bool b) { if (b) { Func3(false); return; } return; } + Builder.SetInsertPoint(Block2); + Builder.CreateCondBr(Func2->arg_begin(), True2, False2); + Builder.SetInsertPoint(True2); + Builder.CreateCall(Func3, ConstantInt::getFalse(Context)); + Builder.CreateRetVoid(); + Builder.SetInsertPoint(False2); + Builder.CreateRetVoid(); + + // void Func3(bool b) { if (b) { Func2(false); return; } return; } + Builder.SetInsertPoint(Block3); + Builder.CreateCondBr(Func3->arg_begin(), True3, False3); + Builder.SetInsertPoint(True3); + Builder.CreateCall(Func2, ConstantInt::getFalse(Context)); + Builder.CreateRetVoid(); + Builder.SetInsertPoint(False3); + Builder.CreateRetVoid(); + + // Compile the function to native code + void (*F1Ptr)() = + reinterpret_cast<void(*)()>((intptr_t)TheJIT->getPointerToFunction(Func1)); + + F1Ptr(); +} + +// Regression test for PR5162. This used to trigger an AssertingVH inside the +// JIT's Function to stub mapping. +TEST_F(JITTest, NonLazyLeaksNoStubs) { + TheJIT->DisableLazyCompilation(); + + // Create two functions with a single basic block each. + const FunctionType *FuncTy = + cast<FunctionType>(TypeBuilder<int(), false>::get(Context)); + Function *Func1 = Function::Create(FuncTy, Function::ExternalLinkage, + "func1", M); + Function *Func2 = Function::Create(FuncTy, Function::InternalLinkage, + "func2", M); + BasicBlock *Block1 = BasicBlock::Create(Context, "block1", Func1); + BasicBlock *Block2 = BasicBlock::Create(Context, "block2", Func2); + + // The first function calls the second and returns the result + IRBuilder<> Builder(Block1); + Value *Result = Builder.CreateCall(Func2); + Builder.CreateRet(Result); + + // The second function just returns a constant + Builder.SetInsertPoint(Block2); + Builder.CreateRet(ConstantInt::get(TypeBuilder<int, false>::get(Context),42)); + + // Compile the function to native code + (void)TheJIT->getPointerToFunction(Func1); + + // Free the JIT state for the functions + TheJIT->freeMachineCodeForFunction(Func1); + TheJIT->freeMachineCodeForFunction(Func2); + + // Delete the first function (and show that is has no users) + EXPECT_EQ(Func1->getNumUses(), 0u); + Func1->eraseFromParent(); + + // Delete the second function (and show that it has no users - it had one, + // func1 but that's gone now) + EXPECT_EQ(Func2->getNumUses(), 0u); + Func2->eraseFromParent(); +} + +// This code is copied from JITEventListenerTest, but it only runs once for all +// the tests in this directory. Everything seems fine, but that's strange +// behavior. +class JITEnvironment : public testing::Environment { + virtual void SetUp() { + // Required to create a JIT. + InitializeNativeTarget(); + } +}; +testing::Environment* const jit_env = + testing::AddGlobalTestEnvironment(new JITEnvironment); + +} diff --git a/unittests/ExecutionEngine/Makefile b/unittests/ExecutionEngine/Makefile index e837a7d..d4ef92f 100644 --- a/unittests/ExecutionEngine/Makefile +++ b/unittests/ExecutionEngine/Makefile @@ -8,12 +8,11 @@ ##===----------------------------------------------------------------------===## LEVEL = ../.. +TESTNAME = ExecutionEngine +LINK_COMPONENTS := engine interpreter include $(LEVEL)/Makefile.config PARALLEL_DIRS = JIT -include $(LEVEL)/Makefile.common - -clean:: - $(Verb) $(RM) -f *Tests +include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest |