reference, declarationdefinition
definition → references, declarations, derived classes, virtual overrides
reference to multiple definitions → definitions
unreferenced
    1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
//===- ExecutionEngineTest.cpp - Unit tests for ExecutionEngine -----------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#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 = std::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
}

}