From 3f23cef24fc9200def464bd4bce820678b5715de Mon Sep 17 00:00:00 2001 From: Andrew Kaylor Date: Tue, 2 Oct 2012 21:18:39 +0000 Subject: Clean-up of memory buffer and object ownership model in MCJIT git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@165053 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/ExecutionEngine/RuntimeDyld/GDBRegistrar.cpp | 8 +-- lib/ExecutionEngine/RuntimeDyld/JITRegistrar.h | 6 +- lib/ExecutionEngine/RuntimeDyld/ObjectImage.h | 59 ----------------- .../RuntimeDyld/ObjectImageCommon.h | 76 +++++++++++++++++++++ lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp | 16 ++--- lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp | 77 ++++++++++------------ lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h | 9 +-- lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h | 17 ++--- .../RuntimeDyld/RuntimeDyldMachO.cpp | 6 +- lib/ExecutionEngine/RuntimeDyld/RuntimeDyldMachO.h | 2 +- 10 files changed, 137 insertions(+), 139 deletions(-) delete mode 100644 lib/ExecutionEngine/RuntimeDyld/ObjectImage.h create mode 100644 lib/ExecutionEngine/RuntimeDyld/ObjectImageCommon.h (limited to 'lib/ExecutionEngine/RuntimeDyld') diff --git a/lib/ExecutionEngine/RuntimeDyld/GDBRegistrar.cpp b/lib/ExecutionEngine/RuntimeDyld/GDBRegistrar.cpp index 8b50101422..50cd0724ea 100644 --- a/lib/ExecutionEngine/RuntimeDyld/GDBRegistrar.cpp +++ b/lib/ExecutionEngine/RuntimeDyld/GDBRegistrar.cpp @@ -78,12 +78,12 @@ public: /// Creates an entry in the JIT registry for the buffer @p Object, /// which must contain an object file in executable memory with any /// debug information for the debugger. - void registerObject(const MemoryBuffer &Object); + void registerObject(const ObjectBuffer &Object); /// Removes the internal registration of @p Object, and /// frees associated resources. /// Returns true if @p Object was found in ObjectBufferMap. - bool deregisterObject(const MemoryBuffer &Object); + bool deregisterObject(const ObjectBuffer &Object); private: /// Deregister the debug info for the given object file from the debugger @@ -124,7 +124,7 @@ GDBJITRegistrar::~GDBJITRegistrar() { ObjectBufferMap.clear(); } -void GDBJITRegistrar::registerObject(const MemoryBuffer &Object) { +void GDBJITRegistrar::registerObject(const ObjectBuffer &Object) { const char *Buffer = Object.getBufferStart(); size_t Size = Object.getBufferSize(); @@ -147,7 +147,7 @@ void GDBJITRegistrar::registerObject(const MemoryBuffer &Object) { } } -bool GDBJITRegistrar::deregisterObject(const MemoryBuffer& Object) { +bool GDBJITRegistrar::deregisterObject(const ObjectBuffer& Object) { const char *Buffer = Object.getBufferStart(); RegisteredObjectBufferMap::iterator I = ObjectBufferMap.find(Buffer); diff --git a/lib/ExecutionEngine/RuntimeDyld/JITRegistrar.h b/lib/ExecutionEngine/RuntimeDyld/JITRegistrar.h index f964bc6182..69e9dbe490 100644 --- a/lib/ExecutionEngine/RuntimeDyld/JITRegistrar.h +++ b/lib/ExecutionEngine/RuntimeDyld/JITRegistrar.h @@ -10,7 +10,7 @@ #ifndef LLVM_EXECUTION_ENGINE_JIT_REGISTRAR_H #define LLVM_EXECUTION_ENGINE_JIT_REGISTRAR_H -#include "llvm/Support/MemoryBuffer.h" +#include "llvm/ExecutionEngine/ObjectBuffer.h" namespace llvm { @@ -27,12 +27,12 @@ public: /// Creates an entry in the JIT registry for the buffer @p Object, /// which must contain an object file in executable memory with any /// debug information for the debugger. - virtual void registerObject(const MemoryBuffer &Object) = 0; + virtual void registerObject(const ObjectBuffer &Object) = 0; /// Removes the internal registration of @p Object, and /// frees associated resources. /// Returns true if @p Object was previously registered. - virtual bool deregisterObject(const MemoryBuffer &Object) = 0; + virtual bool deregisterObject(const ObjectBuffer &Object) = 0; /// Returns a reference to a GDB JIT registrar singleton static JITRegistrar& getGDBRegistrar(); diff --git a/lib/ExecutionEngine/RuntimeDyld/ObjectImage.h b/lib/ExecutionEngine/RuntimeDyld/ObjectImage.h deleted file mode 100644 index 4fa5c1cfea..0000000000 --- a/lib/ExecutionEngine/RuntimeDyld/ObjectImage.h +++ /dev/null @@ -1,59 +0,0 @@ -//===---- ObjectImage.h - Format independent executuable object image -----===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file declares a file format independent ObjectImage class. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_RUNTIMEDYLD_OBJECT_IMAGE_H -#define LLVM_RUNTIMEDYLD_OBJECT_IMAGE_H - -#include "llvm/Object/ObjectFile.h" - -namespace llvm { - -class ObjectImage { - ObjectImage() LLVM_DELETED_FUNCTION; - ObjectImage(const ObjectImage &other) LLVM_DELETED_FUNCTION; -protected: - object::ObjectFile *ObjFile; - -public: - ObjectImage(object::ObjectFile *Obj) { ObjFile = Obj; } - virtual ~ObjectImage() {} - - virtual object::symbol_iterator begin_symbols() const - { return ObjFile->begin_symbols(); } - virtual object::symbol_iterator end_symbols() const - { return ObjFile->end_symbols(); } - - virtual object::section_iterator begin_sections() const - { return ObjFile->begin_sections(); } - virtual object::section_iterator end_sections() const - { return ObjFile->end_sections(); } - - virtual /* Triple::ArchType */ unsigned getArch() const - { return ObjFile->getArch(); } - - // Subclasses can override these methods to update the image with loaded - // addresses for sections and common symbols - virtual void updateSectionAddress(const object::SectionRef &Sec, - uint64_t Addr) {} - virtual void updateSymbolAddress(const object::SymbolRef &Sym, uint64_t Addr) - {} - - // Subclasses can override these methods to provide JIT debugging support - virtual void registerWithDebugger() {} - virtual void deregisterWithDebugger() {} -}; - -} // end namespace llvm - -#endif // LLVM_RUNTIMEDYLD_OBJECT_IMAGE_H - diff --git a/lib/ExecutionEngine/RuntimeDyld/ObjectImageCommon.h b/lib/ExecutionEngine/RuntimeDyld/ObjectImageCommon.h new file mode 100644 index 0000000000..17f3a21464 --- /dev/null +++ b/lib/ExecutionEngine/RuntimeDyld/ObjectImageCommon.h @@ -0,0 +1,76 @@ +//===-- ObjectImageCommon.h - Format independent executuable object image -===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file declares a file format independent ObjectImage class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_RUNTIMEDYLD_OBJECTIMAGECOMMON_H +#define LLVM_RUNTIMEDYLD_OBJECTIMAGECOMMON_H + +#include "llvm/Object/ObjectFile.h" +#include "llvm/ExecutionEngine/ObjectImage.h" +#include "llvm/ExecutionEngine/ObjectBuffer.h" + +namespace llvm { + +class ObjectImageCommon : public ObjectImage { + ObjectImageCommon(); // = delete + ObjectImageCommon(const ObjectImageCommon &other); // = delete + +protected: + object::ObjectFile *ObjFile; + + // This form of the constructor allows subclasses to use + // format-specific subclasses of ObjectFile directly + ObjectImageCommon(ObjectBuffer *Input, object::ObjectFile *Obj) + : ObjectImage(Input), // saves Input as Buffer and takes ownership + ObjFile(Obj) + { + } + +public: + ObjectImageCommon(ObjectBuffer* Input) + : ObjectImage(Input) // saves Input as Buffer and takes ownership + { + ObjFile = object::ObjectFile::createObjectFile(Buffer->getMemBuffer()); + } + virtual ~ObjectImageCommon() { delete ObjFile; } + + virtual object::symbol_iterator begin_symbols() const + { return ObjFile->begin_symbols(); } + virtual object::symbol_iterator end_symbols() const + { return ObjFile->end_symbols(); } + + virtual object::section_iterator begin_sections() const + { return ObjFile->begin_sections(); } + virtual object::section_iterator end_sections() const + { return ObjFile->end_sections(); } + + virtual /* Triple::ArchType */ unsigned getArch() const + { return ObjFile->getArch(); } + + virtual StringRef getData() const { return ObjFile->getData(); } + + // Subclasses can override these methods to update the image with loaded + // addresses for sections and common symbols + virtual void updateSectionAddress(const object::SectionRef &Sec, + uint64_t Addr) {} + virtual void updateSymbolAddress(const object::SymbolRef &Sym, uint64_t Addr) + {} + + // Subclasses can override these methods to provide JIT debugging support + virtual void registerWithDebugger() {} + virtual void deregisterWithDebugger() {} +}; + +} // end namespace llvm + +#endif // LLVM_RUNTIMEDYLD_OBJECT_IMAGE_H + diff --git a/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp b/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp index d06a1fc984..eb69693359 100644 --- a/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp +++ b/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #define DEBUG_TYPE "dyld" +#include "ObjectImageCommon.h" #include "RuntimeDyldImpl.h" #include "RuntimeDyldELF.h" #include "RuntimeDyldMachO.h" @@ -61,14 +62,11 @@ void RuntimeDyldImpl::mapSectionAddress(const void *LocalAddress, // Subclasses can implement this method to create specialized image instances. // The caller owns the pointer that is returned. -ObjectImage *RuntimeDyldImpl::createObjectImage(const MemoryBuffer *InputBuffer) { - ObjectFile *ObjFile = ObjectFile::createObjectFile(const_cast - (InputBuffer)); - ObjectImage *Obj = new ObjectImage(ObjFile); - return Obj; +ObjectImage *RuntimeDyldImpl::createObjectImage(ObjectBuffer *InputBuffer) { + return new ObjectImageCommon(InputBuffer); } -bool RuntimeDyldImpl::loadObject(const MemoryBuffer *InputBuffer) { +ObjectImage *RuntimeDyldImpl::loadObject(ObjectBuffer *InputBuffer) { OwningPtr obj(createObjectImage(InputBuffer)); if (!obj) report_fatal_error("Unable to create object image from memory buffer!"); @@ -178,9 +176,7 @@ bool RuntimeDyldImpl::loadObject(const MemoryBuffer *InputBuffer) { } } - handleObjectLoaded(obj.take()); - - return false; + return obj.take(); } void RuntimeDyldImpl::emitCommonSymbols(ObjectImage &Obj, @@ -437,7 +433,7 @@ RuntimeDyld::~RuntimeDyld() { delete Dyld; } -bool RuntimeDyld::loadObject(MemoryBuffer *InputBuffer) { +ObjectImage *RuntimeDyld::loadObject(ObjectBuffer *InputBuffer) { if (!Dyld) { sys::LLVMFileType type = sys::IdentifyFileType( InputBuffer->getBufferStart(), diff --git a/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp b/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp index a1c0e4020f..597c5cb131 100644 --- a/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp +++ b/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp @@ -12,16 +12,19 @@ //===----------------------------------------------------------------------===// #define DEBUG_TYPE "dyld" +#include "RuntimeDyldELF.h" +#include "JITRegistrar.h" +#include "ObjectImageCommon.h" #include "llvm/ADT/OwningPtr.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/IntervalMap.h" -#include "RuntimeDyldELF.h" #include "llvm/Object/ObjectFile.h" +#include "llvm/ExecutionEngine/ObjectImage.h" +#include "llvm/ExecutionEngine/ObjectBuffer.h" #include "llvm/Support/ELF.h" #include "llvm/ADT/Triple.h" #include "llvm/Object/ELF.h" -#include "JITRegistrar.h" using namespace llvm; using namespace llvm::object; @@ -41,19 +44,12 @@ class DyldELFObject : public ELFObjectFile { typedef typename ELFDataTypeTypedefHelper< target_endianness, is64Bits>::value_type addr_type; -protected: - // This duplicates the 'Data' member in the 'Binary' base class - // but it is necessary to workaround a bug in gcc 4.2 - MemoryBuffer *InputData; - public: - DyldELFObject(MemoryBuffer *Object, error_code &ec); + DyldELFObject(MemoryBuffer *Wrapper, error_code &ec); void updateSectionAddress(const SectionRef &Sec, uint64_t Addr); void updateSymbolAddress(const SymbolRef &Sym, uint64_t Addr); - const MemoryBuffer& getBuffer() const { return *InputData; } - // Methods for type inquiry through isa, cast and dyn_cast static inline bool classof(const Binary *v) { return (isa >(v) @@ -69,14 +65,15 @@ public: }; template -class ELFObjectImage : public ObjectImage { +class ELFObjectImage : public ObjectImageCommon { protected: DyldELFObject *DyldObj; bool Registered; public: - ELFObjectImage(DyldELFObject *Obj) - : ObjectImage(Obj), + ELFObjectImage(ObjectBuffer *Input, + DyldELFObject *Obj) + : ObjectImageCommon(Input, Obj), DyldObj(Obj), Registered(false) {} @@ -99,20 +96,22 @@ class ELFObjectImage : public ObjectImage { virtual void registerWithDebugger() { - JITRegistrar::getGDBRegistrar().registerObject(DyldObj->getBuffer()); + JITRegistrar::getGDBRegistrar().registerObject(*Buffer); Registered = true; } virtual void deregisterWithDebugger() { - JITRegistrar::getGDBRegistrar().deregisterObject(DyldObj->getBuffer()); + JITRegistrar::getGDBRegistrar().deregisterObject(*Buffer); } }; +// The MemoryBuffer passed into this constructor is just a wrapper around the +// actual memory. Ultimately, the Binary parent class will take ownership of +// this MemoryBuffer object but not the underlying memory. template -DyldELFObject::DyldELFObject(MemoryBuffer *Object, +DyldELFObject::DyldELFObject(MemoryBuffer *Wrapper, error_code &ec) - : ELFObjectFile(Object, ec), - InputData(Object) { + : ELFObjectFile(Wrapper, ec) { this->isDyldELFObject = true; } @@ -148,46 +147,39 @@ void DyldELFObject::updateSymbolAddress( namespace llvm { -ObjectImage *RuntimeDyldELF::createObjectImage( - const MemoryBuffer *ConstInputBuffer) { - MemoryBuffer *InputBuffer = const_cast(ConstInputBuffer); - std::pair Ident = getElfArchType(InputBuffer); +ObjectImage *RuntimeDyldELF::createObjectImage(ObjectBuffer *Buffer) { + if (Buffer->getBufferSize() < ELF::EI_NIDENT) + llvm_unreachable("Unexpected ELF object size"); + std::pair Ident = std::make_pair( + (uint8_t)Buffer->getBufferStart()[ELF::EI_CLASS], + (uint8_t)Buffer->getBufferStart()[ELF::EI_DATA]); error_code ec; if (Ident.first == ELF::ELFCLASS32 && Ident.second == ELF::ELFDATA2LSB) { DyldELFObject *Obj = - new DyldELFObject(InputBuffer, ec); - return new ELFObjectImage(Obj); + new DyldELFObject(Buffer->getMemBuffer(), ec); + return new ELFObjectImage(Buffer, Obj); } else if (Ident.first == ELF::ELFCLASS32 && Ident.second == ELF::ELFDATA2MSB) { DyldELFObject *Obj = - new DyldELFObject(InputBuffer, ec); - return new ELFObjectImage(Obj); + new DyldELFObject(Buffer->getMemBuffer(), ec); + return new ELFObjectImage(Buffer, Obj); } else if (Ident.first == ELF::ELFCLASS64 && Ident.second == ELF::ELFDATA2MSB) { DyldELFObject *Obj = - new DyldELFObject(InputBuffer, ec); - return new ELFObjectImage(Obj); + new DyldELFObject(Buffer->getMemBuffer(), ec); + return new ELFObjectImage(Buffer, Obj); } else if (Ident.first == ELF::ELFCLASS64 && Ident.second == ELF::ELFDATA2LSB) { DyldELFObject *Obj = - new DyldELFObject(InputBuffer, ec); - return new ELFObjectImage(Obj); + new DyldELFObject(Buffer->getMemBuffer(), ec); + return new ELFObjectImage(Buffer, Obj); } else llvm_unreachable("Unexpected ELF format"); } -void RuntimeDyldELF::handleObjectLoaded(ObjectImage *Obj) -{ - Obj->registerWithDebugger(); - // Save the loaded object. It will deregister itself when deleted - LoadedObject = Obj; -} - RuntimeDyldELF::~RuntimeDyldELF() { - if (LoadedObject) - delete LoadedObject; } void RuntimeDyldELF::resolveX86_64Relocation(uint8_t *LocalAddress, @@ -522,8 +514,9 @@ void RuntimeDyldELF::processRelocationRef(const ObjRelocationInfo &Rel, } } -bool RuntimeDyldELF::isCompatibleFormat(const MemoryBuffer *InputBuffer) const { - StringRef Magic = InputBuffer->getBuffer().slice(0, ELF::EI_NIDENT); - return (memcmp(Magic.data(), ELF::ElfMagic, strlen(ELF::ElfMagic))) == 0; +bool RuntimeDyldELF::isCompatibleFormat(const ObjectBuffer *Buffer) const { + if (Buffer->getBufferSize() < strlen(ELF::ElfMagic)) + return false; + return (memcmp(Buffer->getBufferStart(), ELF::ElfMagic, strlen(ELF::ElfMagic))) == 0; } } // namespace llvm diff --git a/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h b/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h index eade49ee25..3011a06537 100644 --- a/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h +++ b/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h @@ -22,8 +22,6 @@ using namespace llvm; namespace llvm { class RuntimeDyldELF : public RuntimeDyldImpl { protected: - ObjectImage *LoadedObject; - void resolveX86_64Relocation(uint8_t *LocalAddress, uint64_t FinalAddress, uint64_t Value, @@ -60,16 +58,15 @@ protected: const SymbolTableMap &Symbols, StubMap &Stubs); - virtual ObjectImage *createObjectImage(const MemoryBuffer *InputBuffer); - virtual void handleObjectLoaded(ObjectImage *Obj); + virtual ObjectImage *createObjectImage(ObjectBuffer *InputBuffer); public: RuntimeDyldELF(RTDyldMemoryManager *mm) - : RuntimeDyldImpl(mm), LoadedObject(0) {} + : RuntimeDyldImpl(mm) {} virtual ~RuntimeDyldELF(); - bool isCompatibleFormat(const MemoryBuffer *InputBuffer) const; + bool isCompatibleFormat(const ObjectBuffer *Buffer) const; }; } // end namespace llvm diff --git a/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h b/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h index d56cab293c..a9733407ec 100644 --- a/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h +++ b/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h @@ -14,8 +14,8 @@ #ifndef LLVM_RUNTIME_DYLD_IMPL_H #define LLVM_RUNTIME_DYLD_IMPL_H -#include "ObjectImage.h" #include "llvm/ExecutionEngine/RuntimeDyld.h" +#include "llvm/ExecutionEngine/ObjectImage.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringMap.h" @@ -33,7 +33,7 @@ using namespace llvm::object; namespace llvm { -class MemoryBuffer; +class ObjectBuffer; class Twine; @@ -251,19 +251,13 @@ protected: /// \brief Resolve relocations to external symbols. void resolveExternalSymbols(); - virtual ObjectImage *createObjectImage(const MemoryBuffer *InputBuffer); - virtual void handleObjectLoaded(ObjectImage *Obj) - { - // Subclasses may choose to retain this image if they have a use for it - delete Obj; - } - + virtual ObjectImage *createObjectImage(ObjectBuffer *InputBuffer); public: RuntimeDyldImpl(RTDyldMemoryManager *mm) : MemMgr(mm), HasError(false) {} virtual ~RuntimeDyldImpl(); - bool loadObject(const MemoryBuffer *InputBuffer); + ObjectImage *loadObject(ObjectBuffer *InputBuffer); void *getSymbolAddress(StringRef Name) { // FIXME: Just look up as a function for now. Overly simple of course. @@ -298,8 +292,7 @@ public: // Get the error message. StringRef getErrorString() { return ErrorStr; } - virtual bool isCompatibleFormat(const MemoryBuffer *InputBuffer) const = 0; - + virtual bool isCompatibleFormat(const ObjectBuffer *Buffer) const = 0; }; } // end namespace llvm diff --git a/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldMachO.cpp b/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldMachO.cpp index 465e85d7d9..56540c23da 100644 --- a/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldMachO.cpp +++ b/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldMachO.cpp @@ -295,8 +295,10 @@ void RuntimeDyldMachO::processRelocationRef(const ObjRelocationInfo &Rel, bool RuntimeDyldMachO::isCompatibleFormat( - const MemoryBuffer *InputBuffer) const { - StringRef Magic = InputBuffer->getBuffer().slice(0, 4); + const ObjectBuffer *InputBuffer) const { + if (InputBuffer->getBufferSize() < 4) + return false; + StringRef Magic(InputBuffer->getBufferStart(), 4); if (Magic == "\xFE\xED\xFA\xCE") return true; if (Magic == "\xCE\xFA\xED\xFE") return true; if (Magic == "\xFE\xED\xFA\xCF") return true; diff --git a/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldMachO.h b/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldMachO.h index 707664c732..ef56f551fc 100644 --- a/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldMachO.h +++ b/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldMachO.h @@ -63,7 +63,7 @@ public: RuntimeDyldMachO(RTDyldMemoryManager *mm) : RuntimeDyldImpl(mm) {} - bool isCompatibleFormat(const MemoryBuffer *InputBuffer) const; + bool isCompatibleFormat(const ObjectBuffer *Buffer) const; }; } // end namespace llvm -- cgit v1.2.3