123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320 |
- //===- ObjectTransformLayerTest.cpp - Unit tests for ObjectTransformLayer -===//
- //
- // 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/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/IR/Module.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:
- MockBaseLayer() : MockSymbol(nullptr) { resetExpectations(); }
- template <typename ObjPtrT> llvm::Error addObject(VModuleKey K, ObjPtrT Obj) {
- EXPECT_EQ(MockKey, K) << "Key should pass through";
- EXPECT_EQ(MockObject + 1, *Obj) << "Transform should be applied";
- LastCalled = "addObject";
- return llvm::Error::success();
- }
- template <typename ObjPtrT> void expectAddObject(VModuleKey K, ObjPtrT Obj) {
- MockKey = K;
- MockObject = *Obj;
- }
- void verifyAddObject() {
- EXPECT_EQ("addObject", LastCalled);
- resetExpectations();
- }
- llvm::Error removeObject(VModuleKey K) {
- EXPECT_EQ(MockKey, K);
- LastCalled = "removeObject";
- return llvm::Error::success();
- }
- void expectRemoveObject(VModuleKey K) { MockKey = K; }
- 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(VModuleKey K, const std::string &Name,
- bool ExportedSymbolsOnly) {
- EXPECT_EQ(MockKey, K) << "VModuleKey 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(VModuleKey K, const std::string &Name,
- bool ExportedSymbolsOnly) {
- MockKey = K;
- 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(VModuleKey K) {
- EXPECT_EQ(MockKey, K) << "VModuleKey should pass through";
- LastCalled = "emitAndFinalize";
- return llvm::Error::success();
- }
- void expectEmitAndFinalize(VModuleKey K) { MockKey = K; }
- void verifyEmitAndFinalize() {
- EXPECT_EQ("emitAndFinalize", LastCalled);
- resetExpectations();
- }
- void mapSectionAddress(VModuleKey K, const void *LocalAddress,
- llvm::JITTargetAddress TargetAddr) {
- EXPECT_EQ(MockKey, K);
- EXPECT_EQ(MockLocalAddress, LocalAddress);
- EXPECT_EQ(MockTargetAddress, TargetAddr);
- LastCalled = "mapSectionAddress";
- }
- void expectMapSectionAddress(VModuleKey K, const void *LocalAddress,
- llvm::JITTargetAddress TargetAddr) {
- MockKey = K;
- MockLocalAddress = LocalAddress;
- MockTargetAddress = TargetAddr;
- }
- void verifyMapSectionAddress() {
- EXPECT_EQ("mapSectionAddress", LastCalled);
- resetExpectations();
- }
- private:
- // Backing fields for remembering parameter/return values
- std::string LastCalled;
- VModuleKey MockKey;
- MockObjectFile MockObject;
- 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";
- MockKey = 0;
- MockObject = 0;
- MockName = "bogus";
- MockSymbol = llvm::JITSymbol(nullptr);
- MockLocalAddress = nullptr;
- MockTargetAddress = 0;
- MockBuffer = 0;
- }
- };
- // Test each operation on LegacyObjectTransformLayer.
- TEST(LegacyObjectTransformLayerTest, Main) {
- MockBaseLayer M;
- ExecutionSession ES(std::make_shared<SymbolStringPool>());
- // Create one object transform layer using a transform (as a functor)
- // that allocates new objects, and deals in unique pointers.
- LegacyObjectTransformLayer<MockBaseLayer, AllocatingTransform> T1(
- llvm::AcknowledgeORCv1Deprecation, M);
- // Create a second object transform layer using a transform (as a lambda)
- // that mutates objects in place, and deals in naked pointers
- LegacyObjectTransformLayer<MockBaseLayer,
- std::function<std::shared_ptr<MockObjectFile>(
- std::shared_ptr<MockObjectFile>)>>
- T2(llvm::AcknowledgeORCv1Deprecation, M,
- [](std::shared_ptr<MockObjectFile> Obj) {
- ++(*Obj);
- return Obj;
- });
- // Test addObject with T1 (allocating)
- auto K1 = ES.allocateVModule();
- auto Obj1 = std::make_shared<MockObjectFile>(211);
- M.expectAddObject(K1, Obj1);
- cantFail(T1.addObject(K1, std::move(Obj1)));
- M.verifyAddObject();
- // Test addObjectSet with T2 (mutating)
- auto K2 = ES.allocateVModule();
- auto Obj2 = std::make_shared<MockObjectFile>(222);
- M.expectAddObject(K2, Obj2);
- cantFail(T2.addObject(K2, Obj2));
- M.verifyAddObject();
- EXPECT_EQ(223, *Obj2) << "Expected mutation";
- // Test removeObjectSet
- M.expectRemoveObject(K2);
- cantFail(T1.removeObject(K2));
- 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(K1, Name, ExportedOnly);
- llvm::JITSymbol Sym2 = T1.findSymbolIn(K1, Name, ExportedOnly);
- M.verifyFindSymbolIn(std::move(Sym2));
- // Test emitAndFinalize
- M.expectEmitAndFinalize(K1);
- cantFail(T2.emitAndFinalize(K1));
- M.verifyEmitAndFinalize();
- // Test mapSectionAddress
- char Buffer[24];
- llvm::JITTargetAddress MockAddress = 255;
- M.expectMapSectionAddress(K1, Buffer, MockAddress);
- T1.mapSectionAddress(K1, 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 LegacyObjectTransformLayer implements the object layer concept
- // correctly by sandwitching one between an ObjectLinkingLayer and an
- // LegacyIRCompileLayer, 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.
- LegacyRTDyldObjectLinkingLayer BaseLayer(
- llvm::AcknowledgeORCv1Deprecation, ES, [](VModuleKey) {
- return LegacyRTDyldObjectLinkingLayer::Resources{
- std::make_shared<llvm::SectionMemoryManager>(),
- std::make_shared<NullResolver>()};
- });
- auto IdentityTransform = [](std::unique_ptr<llvm::MemoryBuffer> Obj) {
- return Obj;
- };
- LegacyObjectTransformLayer<decltype(BaseLayer), decltype(IdentityTransform)>
- TransformLayer(llvm::AcknowledgeORCv1Deprecation, BaseLayer,
- IdentityTransform);
- auto NullCompiler = [](llvm::Module &) {
- return std::unique_ptr<llvm::MemoryBuffer>(nullptr);
- };
- LegacyIRCompileLayer<decltype(TransformLayer), decltype(NullCompiler)>
- CompileLayer(llvm::AcknowledgeORCv1Deprecation, TransformLayer,
- NullCompiler);
- // Make sure that the calls from LegacyIRCompileLayer to LegacyObjectTransformLayer
- // compile.
- cantFail(CompileLayer.addModule(ES.allocateVModule(),
- std::unique_ptr<llvm::Module>()));
- // Make sure that the calls from LegacyObjectTransformLayer to ObjectLinkingLayer
- // compile.
- VModuleKey DummyKey = ES.allocateVModule();
- cantFail(TransformLayer.emitAndFinalize(DummyKey));
- TransformLayer.findSymbolIn(DummyKey, Name, false);
- TransformLayer.findSymbol(Name, true);
- TransformLayer.mapSectionAddress(DummyKey, nullptr, 0);
- cantFail(TransformLayer.removeObject(DummyKey));
- }
- }
|