From fb51095c4b3e1c75328b6b9ac7ad116aa07ca0c6 Mon Sep 17 00:00:00 2001 From: Joseph Tremoulet Date: Thu, 25 Jun 2015 13:35:22 +0000 Subject: [PATCH] [ORC] Add ObjectTransformLayer Summary: This is a utility for clients that want to insert a layer that modifies each ObjectFile and then passes it along to the next layer. Reviewers: lhames Reviewed By: lhames Subscribers: llvm-commits Differential Revision: http://reviews.llvm.org/D10456 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@240640 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../Orc/ObjectTransformLayer.h | 112 +++++++ unittests/ExecutionEngine/Orc/CMakeLists.txt | 1 + .../Orc/ObjectTransformLayerTest.cpp | 301 ++++++++++++++++++ 3 files changed, 414 insertions(+) create mode 100644 include/llvm/ExecutionEngine/Orc/ObjectTransformLayer.h create mode 100644 unittests/ExecutionEngine/Orc/ObjectTransformLayerTest.cpp diff --git a/include/llvm/ExecutionEngine/Orc/ObjectTransformLayer.h b/include/llvm/ExecutionEngine/Orc/ObjectTransformLayer.h new file mode 100644 index 00000000000..7af66208547 --- /dev/null +++ b/include/llvm/ExecutionEngine/Orc/ObjectTransformLayer.h @@ -0,0 +1,112 @@ +//===- ObjectTransformLayer.h - Run all objects through functor -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Run all objects passed in through a user supplied functor. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTIONENGINE_ORC_OBJECTTRANSFORMLAYER_H +#define LLVM_EXECUTIONENGINE_ORC_OBJECTTRANSFORMLAYER_H + +#include "JITSymbol.h" + +namespace llvm { +namespace orc { + +/// @brief Object mutating layer. +/// +/// This layer accepts sets of ObjectFiles (via addObjectSet). It +/// immediately applies the user supplied functor to each object, then adds +/// the set of transformed objects to the layer below. +template +class ObjectTransformLayer { +public: + /// @brief Handle to a set of added objects. + typedef typename BaseLayerT::ObjSetHandleT ObjSetHandleT; + + /// @brief Construct an ObjectTransformLayer with the given BaseLayer + ObjectTransformLayer(BaseLayerT &BaseLayer, + TransformFtor Transform = TransformFtor()) + : BaseLayer(BaseLayer), Transform(std::move(Transform)) {} + + /// @brief Apply the transform functor to each object in the object set, then + /// add the resulting set of objects to the base layer, along with the + /// memory manager and symbol resolver. + /// + /// @return A handle for the added objects. + template + ObjSetHandleT addObjectSet(ObjSetT &Objects, MemoryManagerPtrT MemMgr, + SymbolResolverPtrT Resolver) { + + for (auto I = Objects.begin(), E = Objects.end(); I != E; ++I) + *I = Transform(std::move(*I)); + + return BaseLayer.addObjectSet(Objects, std::move(MemMgr), + std::move(Resolver)); + } + + /// @brief Remove the object set associated with the handle H. + void removeObjectSet(ObjSetHandleT H) { BaseLayer.removeObjectSet(H); } + + /// @brief Search for the given named symbol. + /// @param Name The name of the symbol to search for. + /// @param ExportedSymbolsOnly If true, search only for exported symbols. + /// @return A handle for the given named symbol, if it exists. + JITSymbol findSymbol(const std::string &Name, bool ExportedSymbolsOnly) { + return BaseLayer.findSymbol(Name, ExportedSymbolsOnly); + } + + /// @brief Get the address of the given symbol in the context of the set of + /// objects represented by the handle H. This call is forwarded to the + /// base layer's implementation. + /// @param H The handle for the object set to search in. + /// @param Name The name of the symbol to search for. + /// @param ExportedSymbolsOnly If true, search only for exported symbols. + /// @return A handle for the given named symbol, if it is found in the + /// given object set. + JITSymbol findSymbolIn(ObjSetHandleT H, const std::string &Name, + bool ExportedSymbolsOnly) { + return BaseLayer.findSymbolIn(H, Name, ExportedSymbolsOnly); + } + + /// @brief Immediately emit and finalize the object set represented by the + /// given handle. + /// @param H Handle for object set to emit/finalize. + void emitAndFinalize(ObjSetHandleT H) { BaseLayer.emitAndFinalize(H); } + + /// @brief Map section addresses for the objects associated with the handle H. + void mapSectionAddress(ObjSetHandleT H, const void *LocalAddress, + TargetAddress TargetAddr) { + BaseLayer.mapSectionAddress(H, LocalAddress, TargetAddr); + } + + // Ownership hack. + // FIXME: Remove this as soon as RuntimeDyldELF can apply relocations without + // referencing the original object. + template + void takeOwnershipOfBuffers(ObjSetHandleT H, OwningMBSet MBs) { + BaseLayer.takeOwnershipOfBuffers(H, std::move(MBs)); + } + + /// @brief Access the transform functor directly. + TransformFtor &getTransform() { return Transform; } + + /// @brief Access the mumate functor directly. + const TransformFtor &getTransform() const { return Transform; } + +private: + BaseLayerT &BaseLayer; + TransformFtor Transform; +}; + +} // End namespace orc. +} // End namespace llvm. + +#endif // LLVM_EXECUTIONENGINE_ORC_OBJECTTRANSFORMLAYER_H diff --git a/unittests/ExecutionEngine/Orc/CMakeLists.txt b/unittests/ExecutionEngine/Orc/CMakeLists.txt index 67b215ebc6d..30bd19fa3d2 100644 --- a/unittests/ExecutionEngine/Orc/CMakeLists.txt +++ b/unittests/ExecutionEngine/Orc/CMakeLists.txt @@ -7,5 +7,6 @@ set(LLVM_LINK_COMPONENTS add_llvm_unittest(OrcJITTests IndirectionUtilsTest.cpp LazyEmittingLayerTest.cpp + ObjectTransformLayerTest.cpp OrcTestCommon.cpp ) diff --git a/unittests/ExecutionEngine/Orc/ObjectTransformLayerTest.cpp b/unittests/ExecutionEngine/Orc/ObjectTransformLayerTest.cpp new file mode 100644 index 00000000000..772ed5e706d --- /dev/null +++ b/unittests/ExecutionEngine/Orc/ObjectTransformLayerTest.cpp @@ -0,0 +1,301 @@ +//===- 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/SmallVector.h" +#include "gtest/gtest.h" + +using namespace llvm::orc; + +namespace { + +// Stand-in for RuntimeDyld::MemoryManager +typedef int MockMemoryManager; + +// Stand-in for RuntimeDyld::SymbolResolver +typedef int MockSymbolResolver; + +// stand-in for object::ObjectFile +typedef int MockObjectFile; + +// stand-in for llvm::MemoryBuffer set +typedef int MockMemoryBufferSet; + +// Mock transform that operates on unique pointers to object files, and +// allocates new object files rather than mutating the given ones. +struct AllocatingTransform { + std::unique_ptr + operator()(std::unique_ptr Obj) const { + return std::make_unique(*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 ObjSetHandleT; + + MockBaseLayer() : MockSymbol(nullptr) { resetExpectations(); } + + template + ObjSetHandleT addObjectSet(ObjSetT &Objects, MemoryManagerPtrT MemMgr, + SymbolResolverPtrT Resolver) { + EXPECT_EQ(MockManager, *MemMgr) << "MM should pass through"; + EXPECT_EQ(MockResolver, *Resolver) << "Resolver should pass through"; + int I = 0; + for (auto &ObjPtr : Objects) { + EXPECT_EQ(MockObjects[I++] + 1, *ObjPtr) << "Transform should be applied"; + } + EXPECT_EQ(MockObjects.size(), I) << "Number of objects should match"; + LastCalled = "addObjectSet"; + MockObjSetHandle = 111; + return MockObjSetHandle; + } + template + void expectAddObjectSet(ObjSetT &Objects, MockMemoryManager *MemMgr, + MockSymbolResolver *Resolver) { + MockManager = *MemMgr; + MockResolver = *Resolver; + for (auto &ObjPtr : Objects) { + MockObjects.push_back(*ObjPtr); + } + } + void verifyAddObjectSet(ObjSetHandleT Returned) { + EXPECT_EQ("addObjectSet", LastCalled); + EXPECT_EQ(MockObjSetHandle, Returned) << "Return should pass through"; + resetExpectations(); + } + + void removeObjectSet(ObjSetHandleT H) { + EXPECT_EQ(MockObjSetHandle, H); + LastCalled = "removeObjectSet"; + } + void expectRemoveObjectSet(ObjSetHandleT H) { MockObjSetHandle = H; } + void verifyRemoveObjectSet() { + EXPECT_EQ("removeObjectSet", LastCalled); + resetExpectations(); + } + + 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 = JITSymbol(122, llvm::JITSymbolFlags::None); + return MockSymbol; + } + void expectFindSymbol(const std::string &Name, bool ExportedSymbolsOnly) { + MockName = Name; + MockBool = ExportedSymbolsOnly; + } + void verifyFindSymbol(llvm::orc::JITSymbol Returned) { + EXPECT_EQ("findSymbol", LastCalled); + EXPECT_EQ(MockSymbol.getAddress(), Returned.getAddress()) + << "Return should pass through"; + resetExpectations(); + } + + JITSymbol findSymbolIn(ObjSetHandleT H, const std::string &Name, + bool ExportedSymbolsOnly) { + EXPECT_EQ(MockObjSetHandle, H) << "Handle should pass through"; + EXPECT_EQ(MockName, Name) << "Name should pass through"; + EXPECT_EQ(MockBool, ExportedSymbolsOnly) << "Flag should pass through"; + LastCalled = "findSymbolIn"; + MockSymbol = JITSymbol(122, llvm::JITSymbolFlags::None); + return MockSymbol; + } + void expectFindSymbolIn(ObjSetHandleT H, const std::string &Name, + bool ExportedSymbolsOnly) { + MockObjSetHandle = H; + MockName = Name; + MockBool = ExportedSymbolsOnly; + } + void verifyFindSymbolIn(llvm::orc::JITSymbol Returned) { + EXPECT_EQ("findSymbolIn", LastCalled); + EXPECT_EQ(MockSymbol.getAddress(), Returned.getAddress()) + << "Return should pass through"; + resetExpectations(); + } + + void emitAndFinalize(ObjSetHandleT H) { + EXPECT_EQ(MockObjSetHandle, H) << "Handle should pass through"; + LastCalled = "emitAndFinalize"; + } + void expectEmitAndFinalize(ObjSetHandleT H) { MockObjSetHandle = H; } + void verifyEmitAndFinalize() { + EXPECT_EQ("emitAndFinalize", LastCalled); + resetExpectations(); + } + + void mapSectionAddress(ObjSetHandleT H, const void *LocalAddress, + TargetAddress TargetAddr) { + EXPECT_EQ(MockObjSetHandle, H); + EXPECT_EQ(MockLocalAddress, LocalAddress); + EXPECT_EQ(MockTargetAddress, TargetAddr); + LastCalled = "mapSectionAddress"; + } + void expectMapSectionAddress(ObjSetHandleT H, const void *LocalAddress, + TargetAddress TargetAddr) { + MockObjSetHandle = H; + MockLocalAddress = LocalAddress; + MockTargetAddress = TargetAddr; + } + void verifyMapSectionAddress() { + EXPECT_EQ("mapSectionAddress", LastCalled); + resetExpectations(); + } + + template + void takeOwnershipOfBuffers(ObjSetHandleT H, OwningMBSet MBs) { + EXPECT_EQ(MockObjSetHandle, H); + EXPECT_EQ(MockBufferSet, *MBs); + LastCalled = "takeOwnershipOfBuffers"; + } + void expectTakeOwnershipOfBuffers(ObjSetHandleT H, MockMemoryBufferSet *MBs) { + MockObjSetHandle = H; + MockBufferSet = *MBs; + } + void verifyTakeOwnershipOfBuffers() { + EXPECT_EQ("takeOwnershipOfBuffers", LastCalled); + resetExpectations(); + } + +private: + // Backing fields for remembering parameter/return values + std::string LastCalled; + MockMemoryManager MockManager; + MockSymbolResolver MockResolver; + std::vector MockObjects; + ObjSetHandleT MockObjSetHandle; + std::string MockName; + bool MockBool; + JITSymbol MockSymbol; + const void *MockLocalAddress; + TargetAddress MockTargetAddress; + MockMemoryBufferSet MockBufferSet; + + // Clear remembered parameters between calls + void resetExpectations() { + LastCalled = "nothing"; + MockManager = 0; + MockResolver = 0; + MockObjects.clear(); + MockObjSetHandle = 0; + MockName = "bogus"; + MockSymbol = JITSymbol(nullptr); + MockLocalAddress = nullptr; + MockTargetAddress = 0; + MockBufferSet = 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 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> + T2(M, [](MockObjectFile *Obj) { + ++(*Obj); + return Obj; + }); + + // Instantiate some mock objects to use below + MockObjectFile MockObject1 = 211; + MockObjectFile MockObject2 = 222; + MockMemoryManager MockManager = 233; + MockSymbolResolver MockResolver = 244; + + // Test addObjectSet with T1 (allocating, unique pointers) + std::vector> Objs1; + Objs1.push_back(std::make_unique(MockObject1)); + Objs1.push_back(std::make_unique(MockObject2)); + auto MM = std::make_unique(MockManager); + auto SR = std::make_unique(MockResolver); + M.expectAddObjectSet(Objs1, MM.get(), SR.get()); + auto H = T1.addObjectSet(Objs1, std::move(MM), std::move(SR)); + M.verifyAddObjectSet(H); + + // Test addObjectSet with T2 (mutating, naked pointers) + llvm::SmallVector Objs2; + Objs2.push_back(&MockObject1); + Objs2.push_back(&MockObject2); + M.expectAddObjectSet(Objs2, &MockManager, &MockResolver); + H = T2.addObjectSet(Objs2, &MockManager, &MockResolver); + M.verifyAddObjectSet(H); + EXPECT_EQ(212, MockObject1) << "Expected mutation"; + EXPECT_EQ(223, MockObject2) << "Expected mutation"; + + // Test removeObjectSet + M.expectRemoveObjectSet(H); + T1.removeObjectSet(H); + M.verifyRemoveObjectSet(); + + // Test findSymbol + std::string Name = "foo"; + bool ExportedOnly = true; + M.expectFindSymbol(Name, ExportedOnly); + JITSymbol Symbol = T2.findSymbol(Name, ExportedOnly); + M.verifyFindSymbol(Symbol); + + // Test findSymbolIn + Name = "bar"; + ExportedOnly = false; + M.expectFindSymbolIn(H, Name, ExportedOnly); + Symbol = T1.findSymbolIn(H, Name, ExportedOnly); + M.verifyFindSymbolIn(Symbol); + + // Test emitAndFinalize + M.expectEmitAndFinalize(H); + T2.emitAndFinalize(H); + M.verifyEmitAndFinalize(); + + // Test mapSectionAddress + char Buffer[24]; + TargetAddress MockAddress = 255; + M.expectMapSectionAddress(H, Buffer, MockAddress); + T1.mapSectionAddress(H, Buffer, MockAddress); + M.verifyMapSectionAddress(); + + // Test takeOwnershipOfBuffers, using unique pointer to buffer set + auto MockBufferSetPtr = std::make_unique(366); + M.expectTakeOwnershipOfBuffers(H, MockBufferSetPtr.get()); + T2.takeOwnershipOfBuffers(H, std::move(MockBufferSetPtr)); + M.verifyTakeOwnershipOfBuffers(); + + // Test takeOwnershipOfBuffers, using naked pointer to buffer set + MockMemoryBufferSet MockBufferSet = 266; + M.expectTakeOwnershipOfBuffers(H, &MockBufferSet); + T1.takeOwnershipOfBuffers(H, &MockBufferSet); + M.verifyTakeOwnershipOfBuffers(); + + // Verify transform getter (non-const) + MockObjectFile Mutatee = 277; + MockObjectFile *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_unique(288); + const auto &T1C = T1; + OwnedObj = T1C.getTransform()(std::move(OwnedObj)); + EXPECT_EQ(289, *OwnedObj) << "Expected incrementing transform"; +} +} -- 2.34.1