From 4f6539e95961f89aaa3384e0b1d9ea7f2cbd2a8d Mon Sep 17 00:00:00 2001 From: m3rcer Date: Wed, 26 Nov 2025 20:11:26 +0530 Subject: [PATCH] Windows VS 2022 Build Support --- .github/workflows/tests.yml | 94 +-- .gitignore | 24 +- CMakeLists.txt | 157 +++-- DebugIR.cpp | 1092 +++++++++++++++++------------------ LICENSE | 402 ++++++------- Main.cpp | 238 ++++---- README.md | 186 +++--- include/debugir/DebugIR.h | 64 +- test-files/hello.c | 54 +- 9 files changed, 1170 insertions(+), 1141 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index fdda27bf5..b1df9c12c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,47 +1,47 @@ -on: - push: - branches: [ "master" ] - pull_request: - branches: [ "master" ] - -name: Test debug ll generation - -jobs: - test-debug-ll-generation: - runs-on: [ubuntu-22.04] - timeout-minutes: 90 - env: - LLVM_VERSION: 18 - steps: - - uses: actions/checkout@v3 - - - name: Check if CMakeLists.txt is in sync with our LLVM_VERSION - run: | - grep -q "set(DEBUGIR_LLVM_VERSION $LLVM_VERSION" CMakeLists.txt - - - name: Check if README is in sync with our LLVM_VERSION - run: | - grep -q "LLVM-$LLVM_VERSION" README.md - - - name: install LLVM - run: | - wget https://apt.llvm.org/llvm.sh - chmod +x llvm.sh - sudo ./llvm.sh $LLVM_VERSION - - - name: build debugir - run: | - mkdir build - cd build - cmake -DCMAKE_BUILD_TYPE=Release ../ - cmake --build . - - - name: generate hello.ll - run: | - cd test-files - clang-$LLVM_VERSION -emit-llvm -o hello.ll -S hello.c - - - name: test generating hello dbg ll - run: | - ./build/debugir ./test-files/hello.ll - test -e ./test-files/hello.dbg.ll +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +name: Test debug ll generation + +jobs: + test-debug-ll-generation: + runs-on: [ubuntu-22.04] + timeout-minutes: 90 + env: + LLVM_VERSION: 18 + steps: + - uses: actions/checkout@v3 + + - name: Check if CMakeLists.txt is in sync with our LLVM_VERSION + run: | + grep -q "set(DEBUGIR_LLVM_VERSION $LLVM_VERSION" CMakeLists.txt + + - name: Check if README is in sync with our LLVM_VERSION + run: | + grep -q "LLVM-$LLVM_VERSION" README.md + + - name: install LLVM + run: | + wget https://apt.llvm.org/llvm.sh + chmod +x llvm.sh + sudo ./llvm.sh $LLVM_VERSION + + - name: build debugir + run: | + mkdir build + cd build + cmake -DCMAKE_BUILD_TYPE=Release ../ + cmake --build . + + - name: generate hello.ll + run: | + cd test-files + clang-$LLVM_VERSION -emit-llvm -o hello.ll -S hello.c + + - name: test generating hello dbg ll + run: | + ./build/debugir ./test-files/hello.ll + test -e ./test-files/hello.dbg.ll diff --git a/.gitignore b/.gitignore index 469a84e23..a98493e4d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,12 @@ -build/ -cscope.out -TAGS -GTAGS -GRTAGS -GPATH -.vscode -compile_commands.json -.gdbinit -hello.ll -hello.dbg.ll -.cache/ +build/ +cscope.out +TAGS +GTAGS +GRTAGS +GPATH +.vscode +compile_commands.json +.gdbinit +hello.ll +hello.dbg.ll +.cache/ diff --git a/CMakeLists.txt b/CMakeLists.txt index d5b08d686..c20f9b04e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,64 +1,93 @@ -cmake_minimum_required(VERSION 3.5.1) - -project(DebugIR LANGUAGES C CXX) - -option(LINK_LLVM_SHARED "Control whether debugir links to a shared LLVM library, or individual static components" ON) - -# export compile commands -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -# C++ standard -set(CMAKE_CXX_STANDARD 20) - -# detect operating system -message(STATUS "We are on a ${CMAKE_SYSTEM_NAME} system") -list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") - -# Print build type: Debug/Release etc. -message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") -# -# check dependencies -# -# LLVM stuff -if(NOT DEFINED DEBUGIR_LLVM_VERSION) - set(DEBUGIR_LLVM_VERSION 18.1) -endif() -find_package(LLVM ${DEBUGIR_LLVM_VERSION} REQUIRED CONFIG) -message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") -message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") -if(LINK_LLVM_SHARED) - set(llvm_libs LLVM) -else() - # Find the libraries that correspond to the LLVM components we use - llvm_map_components_to_libnames(llvm_libs support core irreader transformutils Passes) -endif() - -add_library(dbgir SHARED DebugIR.cpp) - -# Match LLVM's RTTI build mode -if (NOT LLVM_ENABLE_RTTI) - message(STATUS "LLVM does not have RTTI enabled. Building with -fno-rtti") - target_compile_options(dbgir PUBLIC -fno-rtti) -endif() - -set(DEBUGIR_WARN_FLAGS - -Wall - -Werror - -pedantic - -Wextra - -Wno-unknown-pragmas - -Wno-unused-parameter -) - -# compiler and linker options -# PUBLIC because we apply the same to the debugir executable -target_compile_options(dbgir PRIVATE ${DEBUGIR_WARN_FLAGS}) - -separate_arguments(LLVM_DEFINITIONS_LIST NATIVE_COMMAND ${LLVM_DEFINITIONS}) -target_compile_definitions(dbgir PUBLIC ${LLVM_DEFINITIONS_LIST}) -target_include_directories(dbgir PUBLIC ${PROJECT_SOURCE_DIR}/include/) -target_include_directories(dbgir PUBLIC ${LLVM_INCLUDE_DIRS}) -target_link_libraries (dbgir PUBLIC ${llvm_libs}) - -add_executable(debugir Main.cpp) -target_compile_options(debugir PRIVATE ${DEBUGIR_WARN_FLAGS}) -target_link_libraries(debugir dbgir) +cmake_minimum_required(VERSION 3.5.1) + +project(DebugIR LANGUAGES C CXX) + +option(LINK_LLVM_SHARED "Control whether debugir links to a shared LLVM library, or individual static components" ON) + +# export compile commands +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +# C++ standard +set(CMAKE_CXX_STANDARD 20) + +# detect operating system +message(STATUS "We are on a ${CMAKE_SYSTEM_NAME} system") +list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") + +# Print build type: Debug/Release etc. +message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") +# +# check dependencies +# +# LLVM stuff +if(NOT DEFINED DEBUGIR_LLVM_VERSION) + set(DEBUGIR_LLVM_VERSION 18.1) +endif() +find_package(LLVM ${DEBUGIR_LLVM_VERSION} REQUIRED CONFIG) +message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") +message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") +if(LINK_LLVM_SHARED) + set(llvm_libs LLVM) +else() + # Find the libraries that correspond to the LLVM components we use + llvm_map_components_to_libnames(llvm_libs support core irreader transformutils Passes) +endif() + +if (WIN32) + find_library( + DIA_GUIDS_LIB + NAMES diaguids + PATHS + "$ENV{VSINSTALLDIR}/DIA SDK/lib/amd64" + "$ENV{ProgramFiles}/Microsoft Visual Studio/2022/Community/DIA SDK/lib/amd64" + "$ENV{ProgramFiles}/Microsoft Visual Studio/2022/BuildTools/DIA SDK/lib/amd64" + "$ENV{ProgramFiles\(x86\)}/Microsoft Visual Studio/2022/Community/DIA SDK/lib/amd64" + "$ENV{ProgramFiles\(x86\)}/Microsoft Visual Studio/2022/BuildTools/DIA SDK/lib/amd64" + NO_DEFAULT_PATH + ) + if (DIA_GUIDS_LIB) + list(APPEND llvm_libs ${DIA_GUIDS_LIB}) + endif() +endif() + +add_library(dbgir STATIC DebugIR.cpp) + +# Match LLVM's RTTI build mode +if (NOT LLVM_ENABLE_RTTI) + message(STATUS "LLVM does not have RTTI enabled. Building with -fno-rtti") + target_compile_options( + dbgir + PUBLIC + $<$:/GR-> + $<$>:-fno-rtti> + ) +endif() + +if (MSVC) + set(DEBUGIR_WARN_FLAGS + /W4 + /permissive- + ) +else() + set(DEBUGIR_WARN_FLAGS + -Wall + -Werror + -pedantic + -Wextra + -Wno-unknown-pragmas + -Wno-unused-parameter + ) +endif() + +# compiler and linker options +# PUBLIC because we apply the same to the debugir executable +target_compile_options(dbgir PRIVATE ${DEBUGIR_WARN_FLAGS}) + +separate_arguments(LLVM_DEFINITIONS_LIST NATIVE_COMMAND ${LLVM_DEFINITIONS}) +target_compile_definitions(dbgir PUBLIC ${LLVM_DEFINITIONS_LIST}) +target_include_directories(dbgir PUBLIC ${PROJECT_SOURCE_DIR}/include/) +target_include_directories(dbgir PUBLIC ${LLVM_INCLUDE_DIRS}) +target_link_libraries (dbgir PUBLIC ${llvm_libs}) + +add_executable(debugir Main.cpp) +target_compile_options(debugir PRIVATE ${DEBUGIR_WARN_FLAGS}) +target_link_libraries(debugir dbgir) \ No newline at end of file diff --git a/DebugIR.cpp b/DebugIR.cpp index 72faeac3c..500427264 100644 --- a/DebugIR.cpp +++ b/DebugIR.cpp @@ -1,546 +1,546 @@ -//===--- DebugIR.cpp - Transform debug metadata to allow debugging IR -----===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// A Module transform pass that emits a succinct version of the IR and replaces -// the source file metadata to allow debuggers to step through the IR. -// -// FIXME: instead of replacing debug metadata, this pass should allow for -// additional metadata to be used to point capable debuggers to the IR file -// without destroying the mapping to the original source file. -// -//===----------------------------------------------------------------------===// - -#include "llvm/IR/AssemblyAnnotationWriter.h" -#include "llvm/IR/DIBuilder.h" -#include "llvm/IR/DataLayout.h" -#include "llvm/IR/DebugInfo.h" -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/InstVisitor.h" -#include "llvm/IR/Instruction.h" -#include "llvm/IR/LLVMContext.h" -#include "llvm/IR/Module.h" -#include "llvm/IR/ValueMap.h" -#include "llvm/IR/Verifier.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/FormattedStream.h" -#include "llvm/Transforms/Utils/Cloning.h" -#include - -#include - -#define STR_HELPER(x) #x -#define STR(x) STR_HELPER(x) - -using namespace llvm; - -#define DEBUG_TYPE "debug-ir" - -namespace { - -/// Builds a map of Value* to line numbers on which the Value appears in a -/// textual representation of the IR by plugging into the AssemblyWriter by -/// masquerading as an AssemblyAnnotationWriter. -class ValueToLineMap : public AssemblyAnnotationWriter { - ValueMap Lines; - typedef ValueMap::const_iterator LineIter; - - void addEntry(const Value *V, formatted_raw_ostream &Out) { - Out.flush(); - Lines.insert(std::make_pair(V, Out.getLine() + 1)); - } - -public: - /// Prints Module to a null buffer in order to build the map of Value pointers - /// to line numbers. - ValueToLineMap(const Module *M) { - raw_null_ostream ThrowAway; - M->print(ThrowAway, this); - } - - // This function is called after an Instruction, GlobalValue, or GlobalAlias - // is printed. - void printInfoComment(const Value &V, formatted_raw_ostream &Out) override { - addEntry(&V, Out); - } - - void emitBasicBlockStartAnnot(const BasicBlock *B, - formatted_raw_ostream &Out) override { - addEntry(B, Out); - } - - void emitFunctionAnnot(const Function *F, - formatted_raw_ostream &Out) override { - addEntry(F, Out); - } - - /// If V appears on a line in the textual IR representation, sets Line to the - /// line number and returns true, otherwise returns false. - bool getLine(const Value *V, unsigned int &Line) const { - LineIter i = Lines.find(V); - if (i != Lines.end()) { - Line = i->second; - return true; - } - return false; - } -}; - -/// Updates debug metadata in a Module: -/// - changes Filename/Directory to values provided on construction -/// - adds/updates line number (DebugLoc) entries associated with each -/// instruction to reflect the instruction's location in an LLVM IR file -class DIUpdater : public InstVisitor { - /// Builder of debug information - DIBuilder Builder; - - /// Helper for type attributes/sizes/etc - DataLayout Layout; - - /// Map of Value* to line numbers - const ValueToLineMap LineTable; - - /// Map of Value* (in original Module) to Value* (in optional cloned Module) - const ValueToValueMapTy *VMap; - - /// Directory of debug metadata - DebugInfoFinder Finder; - - /// Source filename and directory - StringRef Filename; - StringRef Directory; - - // CU nodes needed when creating DI subprograms - DIFile *FileNode; - DILexicalBlockFile *LexicalBlockFileNode; - - Module &M; - int tempNameCounter; - - ValueMap SubprogramDescriptors; - ValueMap BlockDescriptors; - DenseMap TypeDescriptors; - -public: - DIUpdater(Module &M, StringRef Filename = StringRef(), - StringRef Directory = StringRef(), const Module *DisplayM = nullptr, - const ValueToValueMapTy *VMap = nullptr) - : Builder(M), Layout(&M), LineTable(DisplayM ? DisplayM : &M), VMap(VMap), - Finder(), Filename(Filename), Directory(Directory), FileNode(nullptr), - LexicalBlockFileNode(nullptr), M(M), tempNameCounter(0) { - - // Even without finder, this screws up. - Finder.processModule(M); - visit(&M); - } - - ~DIUpdater() { Builder.finalize(); } - - void visitModule(Module &M) { - (void)M; - if (Finder.compile_unit_count() > 1) - report_fatal_error("DebugIR pass supports only a signle compile unit per " - "Module."); - createCompileUnit(Finder.compile_unit_count() == 1 - ? (DICompileUnit *)*Finder.compile_units().begin() - : nullptr); - } - - void visitFunction(Function &F) { - if (F.isDeclaration() || findDISubprogram(&F)) - return; - - StringRef MangledName = F.getName(); - DISubroutineType *Sig = createFunctionSignature(&F); - - // find line of function declaration - unsigned Line = 0; - if (!findLine(&F, Line)) { - LLVM_DEBUG(dbgs() << "WARNING: No line for Function " << F.getName().str() - << "\n"); - return; - } - - Instruction *FirstInst = &*F.begin()->begin(); - unsigned ScopeLine = 0; - if (!findLine(FirstInst, ScopeLine)) { - LLVM_DEBUG(dbgs() << "WARNING: No line for 1st Instruction in Function " - << F.getName().str() << "\n"); - return; - } - - bool IsOptimized = false; - - DISubprogram::DISPFlags SPFlags = DISubprogram::SPFlagDefinition; - if (IsOptimized) - SPFlags |= DISubprogram::SPFlagOptimized; - if (F.hasPrivateLinkage() || F.hasInternalLinkage()) - SPFlags |= DISubprogram::SPFlagLocalToUnit; - if (F.isDeclaration()) - SPFlags |= DISubprogram::SPFlagDefinition; - DISubprogram *Sub = - Builder.createFunction(FileNode, F.getName(), MangledName, FileNode, - Line, Sig, ScopeLine, DINode::FlagZero, SPFlags); - F.setSubprogram(Sub); - LLVM_DEBUG(dbgs() << "create subprogram mdnode " << *Sub << ": " - << "\n"); - - SubprogramDescriptors.insert(std::make_pair(&F, Sub)); - - // Clang and the Kaleidoscope tutorial both copy function arguments to - // allocas and then insert debug locations on these allocas. - IRBuilder<> ArgIrBuilder(&F.getEntryBlock(), - F.getEntryBlock().getFirstInsertionPt()); - for (size_t I = 0; I < F.arg_size(); I++) { - auto *Arg = F.getArg(I); - if (Arg->getName().empty()) - continue; - auto *Alloca = - ArgIrBuilder.CreateAlloca(Arg->getType(), nullptr, Arg->getName()); - ArgIrBuilder.CreateStore(Arg, Alloca); - - // Scope must be the function for gdb to recognize this as a function - // argument - auto DILV = Builder.createParameterVariable( - Sub, Arg->getName(), I + 1, FileNode, Line, - getOrCreateType(Arg->getType()), true); - auto Loc = DebugLoc(DILocation::get(M.getContext(), Line, 0, Sub)); - Builder.insertDeclare(Alloca, DILV, Builder.createExpression(), Loc.get(), - &F.getEntryBlock()); - } - } - - void visitInstruction(Instruction &I) { - DebugLoc Loc(I.getDebugLoc()); - - /// If a ValueToValueMap is provided, use it to get the real instruction as - /// the line table was generated on a clone of the module on which we are - /// operating. - Value *RealInst = nullptr; - if (VMap) - RealInst = VMap->lookup(&I); - - if (!RealInst) - RealInst = &I; - - unsigned Col = 0; // FIXME: support columns - unsigned Line; - if (!LineTable.getLine(RealInst, Line)) { - // Instruction has no line, it may have been removed (in the module that - // will be passed to the debugger) so there is nothing to do here. - LLVM_DEBUG(dbgs() << "WARNING: no LineTable entry for instruction " - << RealInst << "\n"); - return; - } - - DILocalScope *Scope; - DILocation *InlinedAt; - if (Loc) { - Scope = llvm::cast(Loc.getScope()); - InlinedAt = Loc.getInlinedAt(); - } else if ((Scope = dyn_cast(findScope(&I)))) { - InlinedAt = nullptr; - } else { - LLVM_DEBUG(dbgs() << "WARNING: no valid scope for instruction " << &I - << ". no DebugLoc will be present." - << "\n"); - return; - } - - if (isa(I)) - Scope = Scope->getSubprogram(); // See https://github.com/llvm/llvm-project/issues/118883 - - DebugLoc NewLoc = - DebugLoc(DILocation::get(M.getContext(), Line, Col, Scope, InlinedAt)); - addDebugLocation(I, NewLoc); - - if (!I.getType()->isVoidTy() && !I.getName().empty()) { - auto DILV = Builder.createAutoVariable(Scope, I.getName(), FileNode, Line, - getOrCreateType(I.getType())); - if (isa(I)) - Builder.insertDbgValueIntrinsic(&I, DILV, Builder.createExpression(), - NewLoc.get(), I.getParent()->getFirstNonPHI()); - else if (Instruction *NI = I.getNextNonDebugInstruction(/* SkipPseudoOp */ true)) - Builder.insertDbgValueIntrinsic(&I, DILV, Builder.createExpression(), - NewLoc.get(), NI); - else - Builder.insertDbgValueIntrinsic(&I, DILV, Builder.createExpression(), - NewLoc.get(), I.getParent()); - } - } - -private: - void createCompileUnit(DICompileUnit *CUToReplace) { - std::string Flags; - bool IsOptimized = false; - StringRef Producer; - unsigned RuntimeVersion(0); - StringRef SplitName; - - if (CUToReplace) { - // save fields from existing CU to re-use in the new CU - Producer = CUToReplace->getProducer(); - IsOptimized = CUToReplace->isOptimized(); - Flags = CUToReplace->getFlags().str(); - RuntimeVersion = CUToReplace->getRuntimeVersion(); - SplitName = CUToReplace->getSplitDebugFilename(); - } else { - Producer = - "LLVM Version " STR(LLVM_VERSION_MAJOR) "." STR(LLVM_VERSION_MINOR); - } - - FileNode = Builder.createFile(Filename, Directory); - DICompileUnit *CU = - Builder.createCompileUnit(dwarf::DW_LANG_C99, FileNode, Producer, - IsOptimized, Flags, RuntimeVersion); - - NamedMDNode *NMD = M.getOrInsertNamedMetadata("llvm.dbg.cu"); - NMD->clearOperands(); - NMD->addOperand(CU); - - for (DISubprogram *S : Finder.subprograms()) { - S->replaceUnit(CU); - S->replaceOperandWith(0, FileNode); // replace 'File' - } - } - - DIScope *getBlockScope(DIScope *ParentScope, const BasicBlock *B) { - auto BScope = BlockDescriptors.find(B); - if (BScope != BlockDescriptors.end()) { - return BScope->second; - } else { - // Let's build a scope for this block. - unsigned Line = 0; - if (!findLine(B, Line)) { - LLVM_DEBUG(dbgs() << "WARNING: No line for basic block " - << B->getName().str() << " in Function " - << B->getParent()->getName().str() << "\n"); - } - auto Scope = Builder.createLexicalBlock(ParentScope, FileNode, Line, 0); - BlockDescriptors[B] = Scope; - return Scope; - } - } - - /// Returns the MDNode* that represents the DI scope to associate with I - DIScope *findScope(const Instruction *I) { - - const BasicBlock *B = I->getParent(); - const Function *F = B->getParent(); - - auto returnFallback = [this, I]() { - (void)I; - LLVM_DEBUG(dbgs() << "WARNING: Using fallback lexical block file scope " - << LexicalBlockFileNode << " as scope for instruction " - << I << "\n"); - return LexicalBlockFileNode; - }; - - DISubprogram *SubprogramScope = findDISubprogram(F); - if (!SubprogramScope) - return returnFallback(); - - auto *EntryBlockScope = getBlockScope(SubprogramScope, B); - if (&F->getEntryBlock() == B) { - return EntryBlockScope; - } - return getBlockScope(EntryBlockScope, B); - } - - /// Returns the MDNode* that is the descriptor for F - DISubprogram *findDISubprogram(const Function *F) { - typedef ValueMap::const_iterator - FuncNodeIter; - FuncNodeIter i = SubprogramDescriptors.find(F); - if (i != SubprogramDescriptors.end()) - return i->second; - - LLVM_DEBUG(dbgs() << "searching for DI scope node for Function " << F - << " in a list of " << Finder.subprogram_count() - << " subprogram nodes" - << "\n"); - - // TODO: When would this even be reached? - for (DISubprogram *S : Finder.subprograms()) { - // TODO: Is describes correct? - if (S->describes(F)) { - LLVM_DEBUG(dbgs() << "Found DISubprogram " << S << " for function " - << F->getName() << "\n"); - return S; - } - } - LLVM_DEBUG(dbgs() << "unable to find DISubprogram node for function " - << F->getName().str() << "\n"); - return nullptr; - } - - /// Sets Line to the line number on which V appears and returns true. If a - /// line location for V is not found, returns false. - bool findLine(const Value *V, unsigned &Line) { - if (LineTable.getLine(V, Line)) - return true; - - if (VMap) { - Value *mapped = VMap->lookup(V); - if (mapped && LineTable.getLine(mapped, Line)) - return true; - } - return false; - } - - std::string getTypeName(Type *T) { - std::string TypeName; - raw_string_ostream TypeStream(TypeName); - if (T) - T->print(TypeStream); - else - TypeStream << "Printing Type"; - TypeStream.flush(); - return TypeName; - } - - /// Returns the MDNode that represents type T if it is already created, or 0 - /// if it is not. - DIType *getType(const Type *T) { - typedef DenseMap::const_iterator TypeNodeIter; - TypeNodeIter i = TypeDescriptors.find(T); - if (i != TypeDescriptors.end()) - return i->second; - return nullptr; - } - - /// Returns a DebugInfo type from an LLVM type T. - DIType *getOrCreateType(Type *T) { - DIType *N = getType(T); - if (N) - return N; - else if (T->isVoidTy()) - return Builder.createUnspecifiedType("void"); - else if (T->isStructTy()) { - // NOTE: where does DINodeArray come from? - StructType *ST = cast(T); - if (ST->isOpaque()) - N = Builder.createUnspecifiedType(ST->getName()); - else { - DICompositeType *S = Builder.createStructType( - LexicalBlockFileNode, - ST->hasName() ? T->getStructName() : "literal", FileNode, - /*LineNumber=*/0, Layout.getTypeSizeInBits(T), - Layout.getPrefTypeAlign(T).value() * CHAR_BIT, /*DIFlags=*/llvm::DINode::FlagZero, - /*DerivedFrom=*/nullptr, llvm::DINodeArray()); // filled in later - N = S; // the Node _is_ the struct type. - - // N is added to the map (early) so that element search below can find - // it, so as to avoid infinite recursion for structs that contain - // pointers to their own type. - TypeDescriptors[T] = N; - - SmallVector - Elements; // unfortunately, SmallVector does not decay to - // SmallVector - - auto *TLayout = Layout.getStructLayout(llvm::cast(T)); - for (unsigned I = 0; I < T->getStructNumElements(); ++I) { - Type *ElType = T->getStructElementType(I); - DIType *ElDIType = getOrCreateType(ElType); - DIType *MemType = Builder.createMemberType( - LexicalBlockFileNode, - (ST->hasName() ? T->getStructName().str() + "." + - std::to_string(tempNameCounter++) - : "literal"), - FileNode, 0, 0, 0, TLayout->getElementOffsetInBits(I), - DINode::DIFlags::FlagZero, ElDIType); - Elements.push_back(MemType); - } - - Builder.replaceArrays(S, Builder.getOrCreateArray(Elements)); - } - } else if (T->isPointerTy()) { - N = Builder.createPointerType( - nullptr, Layout.getPointerTypeSizeInBits(T), - Layout.getPrefTypeAlign(T).value() * CHAR_BIT, -#if LLVM_VERSION_MAJOR > 15 - /*DWARFAddressSpace=*/std::nullopt, -#else - /*DWARFAddressSpace=*/None, -#endif - getTypeName(T)); - } else if (T->isArrayTy()) { - SmallVector - Subscripts; // unfortunately, SmallVector does not decay to - // SmallVector - - Subscripts.push_back( - Builder.getOrCreateSubrange(0, T->getArrayNumElements() - 1)); - - N = Builder.createArrayType(Layout.getTypeSizeInBits(T), - Layout.getPrefTypeAlign(T).value() * CHAR_BIT, - getOrCreateType(T->getArrayElementType()), - Builder.getOrCreateArray(Subscripts)); - } else { - int encoding = llvm::dwarf::DW_ATE_signed; - if (T->isIntegerTy()) - encoding = llvm::dwarf::DW_ATE_unsigned; - else if (T->isFloatingPointTy()) - encoding = llvm::dwarf::DW_ATE_float; - - N = Builder.createBasicType(getTypeName(T), T->getPrimitiveSizeInBits(), - encoding); - } - TypeDescriptors[T] = N; - return N; - } - - /// Returns a DebugInfo type that represents a function signature for Func. - DISubroutineType *createFunctionSignature(const Function *Func) { - SmallVector Params; // SmallVector does not - // auto-case to SmallVector - DIType *ReturnType = getOrCreateType(Func->getReturnType()); - Params.push_back(ReturnType); - - for (const Argument &Arg : Func->args()) { - Type *T = Arg.getType(); - Params.push_back(getOrCreateType(T)); - } - - DITypeRefArray ParamArray = Builder.getOrCreateTypeArray(Params); - return Builder.createSubroutineType(ParamArray); - } - - /// Associates Instruction I with debug location Loc. - void addDebugLocation(Instruction &I, DebugLoc Loc) { I.setDebugLoc(Loc); } -}; - -} // anonymous namespace - -namespace debugir { - -std::unique_ptr createDebugInfo(Module &M, std::string Directory, - std::string Filename) { - - auto VMap = std::make_unique(); - auto DisplayM = CloneModule(M, *VMap); - StripDebugInfo(*(DisplayM.get())); - - { - // DIUpdater is in its own scope so that it's destructor, and hence - // DIBuilder::finalize() gets called. Without that there's dangling stuff. - DIUpdater R(M, Filename, Directory, DisplayM.get(), VMap.get()); - } - - auto DIVersionKey = "Debug Info Version"; - if (!M.getModuleFlag(DIVersionKey)) - // Add the current debug info version into the module. - M.addModuleFlag(Module::Warning, DIVersionKey, DEBUG_METADATA_VERSION); - - assert(!verifyModule(M, &errs()) && "verifyModule found issues"); - - return DisplayM; -} - -} // namespace llvm +//===--- DebugIR.cpp - Transform debug metadata to allow debugging IR -----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// A Module transform pass that emits a succinct version of the IR and replaces +// the source file metadata to allow debuggers to step through the IR. +// +// FIXME: instead of replacing debug metadata, this pass should allow for +// additional metadata to be used to point capable debuggers to the IR file +// without destroying the mapping to the original source file. +// +//===----------------------------------------------------------------------===// + +#include "llvm/IR/AssemblyAnnotationWriter.h" +#include "llvm/IR/DIBuilder.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/DebugInfo.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/InstVisitor.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/ValueMap.h" +#include "llvm/IR/Verifier.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/FormattedStream.h" +#include "llvm/Transforms/Utils/Cloning.h" +#include + +#include + +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) + +using namespace llvm; + +#define DEBUG_TYPE "debug-ir" + +namespace { + +/// Builds a map of Value* to line numbers on which the Value appears in a +/// textual representation of the IR by plugging into the AssemblyWriter by +/// masquerading as an AssemblyAnnotationWriter. +class ValueToLineMap : public AssemblyAnnotationWriter { + ValueMap Lines; + typedef ValueMap::const_iterator LineIter; + + void addEntry(const Value *V, formatted_raw_ostream &Out) { + Out.flush(); + Lines.insert(std::make_pair(V, Out.getLine() + 1)); + } + +public: + /// Prints Module to a null buffer in order to build the map of Value pointers + /// to line numbers. + ValueToLineMap(const Module *M) { + raw_null_ostream ThrowAway; + M->print(ThrowAway, this); + } + + // This function is called after an Instruction, GlobalValue, or GlobalAlias + // is printed. + void printInfoComment(const Value &V, formatted_raw_ostream &Out) override { + addEntry(&V, Out); + } + + void emitBasicBlockStartAnnot(const BasicBlock *B, + formatted_raw_ostream &Out) override { + addEntry(B, Out); + } + + void emitFunctionAnnot(const Function *F, + formatted_raw_ostream &Out) override { + addEntry(F, Out); + } + + /// If V appears on a line in the textual IR representation, sets Line to the + /// line number and returns true, otherwise returns false. + bool getLine(const Value *V, unsigned int &Line) const { + LineIter i = Lines.find(V); + if (i != Lines.end()) { + Line = i->second; + return true; + } + return false; + } +}; + +/// Updates debug metadata in a Module: +/// - changes Filename/Directory to values provided on construction +/// - adds/updates line number (DebugLoc) entries associated with each +/// instruction to reflect the instruction's location in an LLVM IR file +class DIUpdater : public InstVisitor { + /// Builder of debug information + DIBuilder Builder; + + /// Helper for type attributes/sizes/etc + DataLayout Layout; + + /// Map of Value* to line numbers + const ValueToLineMap LineTable; + + /// Map of Value* (in original Module) to Value* (in optional cloned Module) + const ValueToValueMapTy *VMap; + + /// Directory of debug metadata + DebugInfoFinder Finder; + + /// Source filename and directory + StringRef Filename; + StringRef Directory; + + // CU nodes needed when creating DI subprograms + DIFile *FileNode; + DILexicalBlockFile *LexicalBlockFileNode; + + Module &M; + int tempNameCounter; + + ValueMap SubprogramDescriptors; + ValueMap BlockDescriptors; + DenseMap TypeDescriptors; + +public: + DIUpdater(Module &M, StringRef Filename = StringRef(), + StringRef Directory = StringRef(), const Module *DisplayM = nullptr, + const ValueToValueMapTy *VMap = nullptr) + : Builder(M), Layout(&M), LineTable(DisplayM ? DisplayM : &M), VMap(VMap), + Finder(), Filename(Filename), Directory(Directory), FileNode(nullptr), + LexicalBlockFileNode(nullptr), M(M), tempNameCounter(0) { + + // Even without finder, this screws up. + Finder.processModule(M); + visit(&M); + } + + ~DIUpdater() { Builder.finalize(); } + + void visitModule(Module &M) { + (void)M; + if (Finder.compile_unit_count() > 1) + report_fatal_error("DebugIR pass supports only a signle compile unit per " + "Module."); + createCompileUnit(Finder.compile_unit_count() == 1 + ? (DICompileUnit *)*Finder.compile_units().begin() + : nullptr); + } + + void visitFunction(Function &F) { + if (F.isDeclaration() || findDISubprogram(&F)) + return; + + StringRef MangledName = F.getName(); + DISubroutineType *Sig = createFunctionSignature(&F); + + // find line of function declaration + unsigned Line = 0; + if (!findLine(&F, Line)) { + LLVM_DEBUG(dbgs() << "WARNING: No line for Function " << F.getName().str() + << "\n"); + return; + } + + Instruction *FirstInst = &*F.begin()->begin(); + unsigned ScopeLine = 0; + if (!findLine(FirstInst, ScopeLine)) { + LLVM_DEBUG(dbgs() << "WARNING: No line for 1st Instruction in Function " + << F.getName().str() << "\n"); + return; + } + + bool IsOptimized = false; + + DISubprogram::DISPFlags SPFlags = DISubprogram::SPFlagDefinition; + if (IsOptimized) + SPFlags |= DISubprogram::SPFlagOptimized; + if (F.hasPrivateLinkage() || F.hasInternalLinkage()) + SPFlags |= DISubprogram::SPFlagLocalToUnit; + if (F.isDeclaration()) + SPFlags |= DISubprogram::SPFlagDefinition; + DISubprogram *Sub = + Builder.createFunction(FileNode, F.getName(), MangledName, FileNode, + Line, Sig, ScopeLine, DINode::FlagZero, SPFlags); + F.setSubprogram(Sub); + LLVM_DEBUG(dbgs() << "create subprogram mdnode " << *Sub << ": " + << "\n"); + + SubprogramDescriptors.insert(std::make_pair(&F, Sub)); + + // Clang and the Kaleidoscope tutorial both copy function arguments to + // allocas and then insert debug locations on these allocas. + IRBuilder<> ArgIrBuilder(&F.getEntryBlock(), + F.getEntryBlock().getFirstInsertionPt()); + for (size_t I = 0; I < F.arg_size(); I++) { + auto *Arg = F.getArg(I); + if (Arg->getName().empty()) + continue; + auto *Alloca = + ArgIrBuilder.CreateAlloca(Arg->getType(), nullptr, Arg->getName()); + ArgIrBuilder.CreateStore(Arg, Alloca); + + // Scope must be the function for gdb to recognize this as a function + // argument + auto DILV = Builder.createParameterVariable( + Sub, Arg->getName(), I + 1, FileNode, Line, + getOrCreateType(Arg->getType()), true); + auto Loc = DebugLoc(DILocation::get(M.getContext(), Line, 0, Sub)); + Builder.insertDeclare(Alloca, DILV, Builder.createExpression(), Loc.get(), + &F.getEntryBlock()); + } + } + + void visitInstruction(Instruction &I) { + DebugLoc Loc(I.getDebugLoc()); + + /// If a ValueToValueMap is provided, use it to get the real instruction as + /// the line table was generated on a clone of the module on which we are + /// operating. + Value *RealInst = nullptr; + if (VMap) + RealInst = VMap->lookup(&I); + + if (!RealInst) + RealInst = &I; + + unsigned Col = 0; // FIXME: support columns + unsigned Line; + if (!LineTable.getLine(RealInst, Line)) { + // Instruction has no line, it may have been removed (in the module that + // will be passed to the debugger) so there is nothing to do here. + LLVM_DEBUG(dbgs() << "WARNING: no LineTable entry for instruction " + << RealInst << "\n"); + return; + } + + DILocalScope *Scope; + DILocation *InlinedAt; + if (Loc) { + Scope = llvm::cast(Loc.getScope()); + InlinedAt = Loc.getInlinedAt(); + } else if ((Scope = dyn_cast(findScope(&I)))) { + InlinedAt = nullptr; + } else { + LLVM_DEBUG(dbgs() << "WARNING: no valid scope for instruction " << &I + << ". no DebugLoc will be present." + << "\n"); + return; + } + + if (isa(I)) + Scope = Scope->getSubprogram(); // See https://github.com/llvm/llvm-project/issues/118883 + + DebugLoc NewLoc = + DebugLoc(DILocation::get(M.getContext(), Line, Col, Scope, InlinedAt)); + addDebugLocation(I, NewLoc); + + if (!I.getType()->isVoidTy() && !I.getName().empty()) { + auto DILV = Builder.createAutoVariable(Scope, I.getName(), FileNode, Line, + getOrCreateType(I.getType())); + if (isa(I)) + Builder.insertDbgValueIntrinsic(&I, DILV, Builder.createExpression(), + NewLoc.get(), I.getParent()->getFirstNonPHI()); + else if (Instruction *NI = I.getNextNonDebugInstruction(/* SkipPseudoOp */ true)) + Builder.insertDbgValueIntrinsic(&I, DILV, Builder.createExpression(), + NewLoc.get(), NI); + else + Builder.insertDbgValueIntrinsic(&I, DILV, Builder.createExpression(), + NewLoc.get(), I.getParent()); + } + } + +private: + void createCompileUnit(DICompileUnit *CUToReplace) { + std::string Flags; + bool IsOptimized = false; + StringRef Producer; + unsigned RuntimeVersion(0); + StringRef SplitName; + + if (CUToReplace) { + // save fields from existing CU to re-use in the new CU + Producer = CUToReplace->getProducer(); + IsOptimized = CUToReplace->isOptimized(); + Flags = CUToReplace->getFlags().str(); + RuntimeVersion = CUToReplace->getRuntimeVersion(); + SplitName = CUToReplace->getSplitDebugFilename(); + } else { + Producer = + "LLVM Version " STR(LLVM_VERSION_MAJOR) "." STR(LLVM_VERSION_MINOR); + } + + FileNode = Builder.createFile(Filename, Directory); + DICompileUnit *CU = + Builder.createCompileUnit(dwarf::DW_LANG_C99, FileNode, Producer, + IsOptimized, Flags, RuntimeVersion); + + NamedMDNode *NMD = M.getOrInsertNamedMetadata("llvm.dbg.cu"); + NMD->clearOperands(); + NMD->addOperand(CU); + + for (DISubprogram *S : Finder.subprograms()) { + S->replaceUnit(CU); + S->replaceOperandWith(0, FileNode); // replace 'File' + } + } + + DIScope *getBlockScope(DIScope *ParentScope, const BasicBlock *B) { + auto BScope = BlockDescriptors.find(B); + if (BScope != BlockDescriptors.end()) { + return BScope->second; + } else { + // Let's build a scope for this block. + unsigned Line = 0; + if (!findLine(B, Line)) { + LLVM_DEBUG(dbgs() << "WARNING: No line for basic block " + << B->getName().str() << " in Function " + << B->getParent()->getName().str() << "\n"); + } + auto Scope = Builder.createLexicalBlock(ParentScope, FileNode, Line, 0); + BlockDescriptors[B] = Scope; + return Scope; + } + } + + /// Returns the MDNode* that represents the DI scope to associate with I + DIScope *findScope(const Instruction *I) { + + const BasicBlock *B = I->getParent(); + const Function *F = B->getParent(); + + auto returnFallback = [this, I]() { + (void)I; + LLVM_DEBUG(dbgs() << "WARNING: Using fallback lexical block file scope " + << LexicalBlockFileNode << " as scope for instruction " + << I << "\n"); + return LexicalBlockFileNode; + }; + + DISubprogram *SubprogramScope = findDISubprogram(F); + if (!SubprogramScope) + return returnFallback(); + + auto *EntryBlockScope = getBlockScope(SubprogramScope, B); + if (&F->getEntryBlock() == B) { + return EntryBlockScope; + } + return getBlockScope(EntryBlockScope, B); + } + + /// Returns the MDNode* that is the descriptor for F + DISubprogram *findDISubprogram(const Function *F) { + typedef ValueMap::const_iterator + FuncNodeIter; + FuncNodeIter i = SubprogramDescriptors.find(F); + if (i != SubprogramDescriptors.end()) + return i->second; + + LLVM_DEBUG(dbgs() << "searching for DI scope node for Function " << F + << " in a list of " << Finder.subprogram_count() + << " subprogram nodes" + << "\n"); + + // TODO: When would this even be reached? + for (DISubprogram *S : Finder.subprograms()) { + // TODO: Is describes correct? + if (S->describes(F)) { + LLVM_DEBUG(dbgs() << "Found DISubprogram " << S << " for function " + << F->getName() << "\n"); + return S; + } + } + LLVM_DEBUG(dbgs() << "unable to find DISubprogram node for function " + << F->getName().str() << "\n"); + return nullptr; + } + + /// Sets Line to the line number on which V appears and returns true. If a + /// line location for V is not found, returns false. + bool findLine(const Value *V, unsigned &Line) { + if (LineTable.getLine(V, Line)) + return true; + + if (VMap) { + Value *mapped = VMap->lookup(V); + if (mapped && LineTable.getLine(mapped, Line)) + return true; + } + return false; + } + + std::string getTypeName(Type *T) { + std::string TypeName; + raw_string_ostream TypeStream(TypeName); + if (T) + T->print(TypeStream); + else + TypeStream << "Printing Type"; + TypeStream.flush(); + return TypeName; + } + + /// Returns the MDNode that represents type T if it is already created, or 0 + /// if it is not. + DIType *getType(const Type *T) { + typedef DenseMap::const_iterator TypeNodeIter; + TypeNodeIter i = TypeDescriptors.find(T); + if (i != TypeDescriptors.end()) + return i->second; + return nullptr; + } + + /// Returns a DebugInfo type from an LLVM type T. + DIType *getOrCreateType(Type *T) { + DIType *N = getType(T); + if (N) + return N; + else if (T->isVoidTy()) + return Builder.createUnspecifiedType("void"); + else if (T->isStructTy()) { + // NOTE: where does DINodeArray come from? + StructType *ST = cast(T); + if (ST->isOpaque()) + N = Builder.createUnspecifiedType(ST->getName()); + else { + DICompositeType *S = Builder.createStructType( + LexicalBlockFileNode, + ST->hasName() ? T->getStructName() : "literal", FileNode, + /*LineNumber=*/0, Layout.getTypeSizeInBits(T), + Layout.getPrefTypeAlign(T).value() * CHAR_BIT, /*DIFlags=*/llvm::DINode::FlagZero, + /*DerivedFrom=*/nullptr, llvm::DINodeArray()); // filled in later + N = S; // the Node _is_ the struct type. + + // N is added to the map (early) so that element search below can find + // it, so as to avoid infinite recursion for structs that contain + // pointers to their own type. + TypeDescriptors[T] = N; + + SmallVector + Elements; // unfortunately, SmallVector does not decay to + // SmallVector + + auto *TLayout = Layout.getStructLayout(llvm::cast(T)); + for (unsigned I = 0; I < T->getStructNumElements(); ++I) { + Type *ElType = T->getStructElementType(I); + DIType *ElDIType = getOrCreateType(ElType); + DIType *MemType = Builder.createMemberType( + LexicalBlockFileNode, + (ST->hasName() ? T->getStructName().str() + "." + + std::to_string(tempNameCounter++) + : "literal"), + FileNode, 0, 0, 0, TLayout->getElementOffsetInBits(I), + DINode::DIFlags::FlagZero, ElDIType); + Elements.push_back(MemType); + } + + Builder.replaceArrays(S, Builder.getOrCreateArray(Elements)); + } + } else if (T->isPointerTy()) { + N = Builder.createPointerType( + nullptr, Layout.getPointerTypeSizeInBits(T), + Layout.getPrefTypeAlign(T).value() * CHAR_BIT, +#if LLVM_VERSION_MAJOR > 15 + /*DWARFAddressSpace=*/std::nullopt, +#else + /*DWARFAddressSpace=*/None, +#endif + getTypeName(T)); + } else if (T->isArrayTy()) { + SmallVector + Subscripts; // unfortunately, SmallVector does not decay to + // SmallVector + + Subscripts.push_back( + Builder.getOrCreateSubrange(0, T->getArrayNumElements() - 1)); + + N = Builder.createArrayType(Layout.getTypeSizeInBits(T), + Layout.getPrefTypeAlign(T).value() * CHAR_BIT, + getOrCreateType(T->getArrayElementType()), + Builder.getOrCreateArray(Subscripts)); + } else { + int encoding = llvm::dwarf::DW_ATE_signed; + if (T->isIntegerTy()) + encoding = llvm::dwarf::DW_ATE_unsigned; + else if (T->isFloatingPointTy()) + encoding = llvm::dwarf::DW_ATE_float; + + N = Builder.createBasicType(getTypeName(T), T->getPrimitiveSizeInBits(), + encoding); + } + TypeDescriptors[T] = N; + return N; + } + + /// Returns a DebugInfo type that represents a function signature for Func. + DISubroutineType *createFunctionSignature(const Function *Func) { + SmallVector Params; // SmallVector does not + // auto-case to SmallVector + DIType *ReturnType = getOrCreateType(Func->getReturnType()); + Params.push_back(ReturnType); + + for (const Argument &Arg : Func->args()) { + Type *T = Arg.getType(); + Params.push_back(getOrCreateType(T)); + } + + DITypeRefArray ParamArray = Builder.getOrCreateTypeArray(Params); + return Builder.createSubroutineType(ParamArray); + } + + /// Associates Instruction I with debug location Loc. + void addDebugLocation(Instruction &I, DebugLoc Loc) { I.setDebugLoc(Loc); } +}; + +} // anonymous namespace + +namespace debugir { + +std::unique_ptr createDebugInfo(Module &M, std::string Directory, + std::string Filename) { + + auto VMap = std::make_unique(); + auto DisplayM = CloneModule(M, *VMap); + StripDebugInfo(*(DisplayM.get())); + + { + // DIUpdater is in its own scope so that it's destructor, and hence + // DIBuilder::finalize() gets called. Without that there's dangling stuff. + DIUpdater R(M, Filename, Directory, DisplayM.get(), VMap.get()); + } + + auto DIVersionKey = "Debug Info Version"; + if (!M.getModuleFlag(DIVersionKey)) + // Add the current debug info version into the module. + M.addModuleFlag(Module::Warning, DIVersionKey, DEBUG_METADATA_VERSION); + + assert(!verifyModule(M, &errs()) && "verifyModule found issues"); + + return DisplayM; +} + +} // namespace llvm diff --git a/LICENSE b/LICENSE index 261eeb9e9..29f81d812 100644 --- a/LICENSE +++ b/LICENSE @@ -1,201 +1,201 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/Main.cpp b/Main.cpp index 0b617d0f6..27f5a689c 100644 --- a/Main.cpp +++ b/Main.cpp @@ -1,119 +1,119 @@ -/* - * Copyright (C) 2020 Vaivaswatha N - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include - -#include "llvm/Analysis/CGSCCPassManager.h" -#include "llvm/Analysis/LoopAnalysisManager.h" -#include "llvm/IR/LegacyPassManager.h" -#include "llvm/IR/Module.h" -#include "llvm/IRReader/IRReader.h" -#include "llvm/Pass.h" -#include "llvm/Passes/PassBuilder.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/Path.h" -#include "llvm/Support/SourceMgr.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Transforms/Utils/InstructionNamer.h" - -#include - -using namespace llvm; - -namespace { - -// Command line arguments parsed using LLVM's command line parser. -cl::opt InputFile(cl::Positional, cl::desc(""), - cl::Required); -cl::opt - RunInstNamer("instnamer", - cl::desc("Run instnamer on input, prior to processing it")); - -void versionPrinter(llvm::raw_ostream &OS) { OS << "debugir: v0.1.0\n"; } - -} // end of anonymous namespace - -int main(int argc, char *argv[]) { - cl::SetVersionPrinter(versionPrinter); - cl::ParseCommandLineOptions(argc, argv); - - SmallVector DBF(InputFile.begin(), InputFile.end()); - sys::path::replace_extension(DBF, ".dbg.ll"); - std::string DebugFile(DBF.begin(), DBF.end()); - - // The directory/filename that debug info should refer to. - auto Directory = sys::path::parent_path(InputFile); - auto Filename = sys::path::filename(InputFile); - - auto Ctx = std::make_unique(); - SMDiagnostic Smd; - auto M = parseIRFile(InputFile, Smd, *Ctx); - if (!M) { - std::string ErrMsg; - raw_string_ostream OS(ErrMsg); - Smd.print(argv[0], OS); - std::cerr << OS.str(); - return EXIT_FAILURE; - } - - if (RunInstNamer) { - // Create the analysis managers. - // These must be declared in this order so that they are destroyed in the - // correct order due to inter-analysis-manager references. - LoopAnalysisManager LAM; - FunctionAnalysisManager FAM; - CGSCCAnalysisManager CGAM; - ModuleAnalysisManager MAM; - - // Create the new pass manager builder. - // Take a look at the PassBuilder constructor parameters for more - // customization, e.g. specifying a TargetMachine or various debugging - // options. - PassBuilder PB; - - // Register all the basic analyses with the managers. - PB.registerModuleAnalyses(MAM); - PB.registerCGSCCAnalyses(CGAM); - PB.registerFunctionAnalyses(FAM); - PB.registerLoopAnalyses(LAM); - PB.crossRegisterProxies(LAM, FAM, CGAM, MAM); - - PB.registerPipelineStartEPCallback([&](ModulePassManager &MPM, - OptimizationLevel Level) { - MPM.addPass(createModuleToFunctionPassAdaptor(InstructionNamerPass())); - }); - - ModulePassManager MPM = - PB.buildPerModuleDefaultPipeline(OptimizationLevel::O1); - MPM.run(*M, MAM); - } - - auto DisplayM = debugir::createDebugInfo(*M.get(), Directory.str(), Filename.str()); - std::error_code EC; - - // Update InputFile in-place so that line numbers match with DebugFile. - raw_fd_ostream OS_disp(InputFile, EC, sys::fs::OF_Text); - DisplayM->print(OS_disp, nullptr); - - // Output the display file - raw_fd_ostream OS_dbg(DebugFile, EC, sys::fs::OF_Text); - M->print(OS_dbg, nullptr); - - return EXIT_SUCCESS; -} +/* + * Copyright (C) 2020 Vaivaswatha N + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "llvm/Analysis/CGSCCPassManager.h" +#include "llvm/Analysis/LoopAnalysisManager.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/Module.h" +#include "llvm/IRReader/IRReader.h" +#include "llvm/Pass.h" +#include "llvm/Passes/PassBuilder.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Transforms/Utils/InstructionNamer.h" + +#include + +using namespace llvm; + +namespace { + +// Command line arguments parsed using LLVM's command line parser. +cl::opt InputFile(cl::Positional, cl::desc(""), + cl::Required); +cl::opt + RunInstNamer("instnamer", + cl::desc("Run instnamer on input, prior to processing it")); + +void versionPrinter(llvm::raw_ostream &OS) { OS << "debugir: v0.1.0\n"; } + +} // end of anonymous namespace + +int main(int argc, char *argv[]) { + cl::SetVersionPrinter(versionPrinter); + cl::ParseCommandLineOptions(argc, argv); + + SmallVector DBF(InputFile.begin(), InputFile.end()); + sys::path::replace_extension(DBF, ".dbg.ll"); + std::string DebugFile(DBF.begin(), DBF.end()); + + // The directory/filename that debug info should refer to. + auto Directory = sys::path::parent_path(InputFile); + auto Filename = sys::path::filename(InputFile); + + auto Ctx = std::make_unique(); + SMDiagnostic Smd; + auto M = parseIRFile(InputFile, Smd, *Ctx); + if (!M) { + std::string ErrMsg; + raw_string_ostream OS(ErrMsg); + Smd.print(argv[0], OS); + std::cerr << OS.str(); + return EXIT_FAILURE; + } + + if (RunInstNamer) { + // Create the analysis managers. + // These must be declared in this order so that they are destroyed in the + // correct order due to inter-analysis-manager references. + LoopAnalysisManager LAM; + FunctionAnalysisManager FAM; + CGSCCAnalysisManager CGAM; + ModuleAnalysisManager MAM; + + // Create the new pass manager builder. + // Take a look at the PassBuilder constructor parameters for more + // customization, e.g. specifying a TargetMachine or various debugging + // options. + PassBuilder PB; + + // Register all the basic analyses with the managers. + PB.registerModuleAnalyses(MAM); + PB.registerCGSCCAnalyses(CGAM); + PB.registerFunctionAnalyses(FAM); + PB.registerLoopAnalyses(LAM); + PB.crossRegisterProxies(LAM, FAM, CGAM, MAM); + + PB.registerPipelineStartEPCallback([&](ModulePassManager &MPM, + OptimizationLevel Level) { + MPM.addPass(createModuleToFunctionPassAdaptor(InstructionNamerPass())); + }); + + ModulePassManager MPM = + PB.buildPerModuleDefaultPipeline(OptimizationLevel::O1); + MPM.run(*M, MAM); + } + + auto DisplayM = debugir::createDebugInfo(*M.get(), Directory.str(), Filename.str()); + std::error_code EC; + + // Update InputFile in-place so that line numbers match with DebugFile. + raw_fd_ostream OS_disp(InputFile, EC, sys::fs::OF_Text); + DisplayM->print(OS_disp, nullptr); + + // Output the display file + raw_fd_ostream OS_dbg(DebugFile, EC, sys::fs::OF_Text); + M->print(OS_dbg, nullptr); + + return EXIT_SUCCESS; +} diff --git a/README.md b/README.md index 2e146e176..bf90b2583 100644 --- a/README.md +++ b/README.md @@ -1,93 +1,93 @@ -# DebugIR: Debugging LLVM-IR Files - -This utility [revives](https://lists.llvm.org/pipermail/llvm-dev/2018-March/122013.html) -the debug-ir pass in LLVM, but as a standalone tool. I found the idea of -having a separate utility simpler after knowing that -[this earlier patch](https://reviews.llvm.org/D40778) to revive it as a -pass inside LLVM didn't go through. - -## Instructions -Let us assume that the LLVM-IR to be debugged is named `hello.ll`. -Such a file can be dynamically generated, or using clang as - -```sh - clang -emit-llvm -o hello.ll -S hello.c -``` - -
hello.c - -```C - #include - #include - - int main(int argc, char *argv[]) - { - if (!strcmp(argv[0], "hello")) { - printf("Hello World\n"); - } else { - printf("No hello\n"); - } - return 0; - } -``` -
- -### Clone and build -This tool requires LLVM-18 to be installed. - -```sh - git clone https://github.com/vaivaswatha/debugir.git debugir - cd debugir; mkdir build; cd build - cmake -DCMAKE_BUILD_TYPE=Release ../ - cmake --build . -``` - -If you have LLVM installed in a non-standard path, you may provide the -additional `CMake` argument `-DLLVM_DIR=/path/to/llvm`. - -If you would like to link debugir to the relevant static LLVM components, use -``` --DLINK_LLVM_SHARED=OFF -``` -during the cmake command. - -### Run -You should now have an executable file `debugir` in the build directory. - -```sh - ./debugir hello.ll -``` - -This produces a file `hello.dbg.ll`. **NOTE** The utility also overwrites -the input LLVM-IR file (if you have comments in it, they will be lost). -The new file `hello.dbg.ll` is semantically the same as the input file, -but with debug information referring to the input file. - -If you now debug `hello.dbg.ll` (instead of debugging `hello.ll`), the -debugger can pickup and display `hello.ll` as the execution proceeds. - -Note: LLVM values that don't have an explicit name cannot have their -names (and hence values) seen in GDB. To workaround this, provide -explicit names yourself or run the instruction namer pass by providing -the `-instnamer` flag to `debugir`. - -Following on the example [here](https://llvm.org/docs/DebuggingJITedCode.html) -let us try and debug `hello.dbg.ll`. - -```sh - gdb --args lli -jit-kind=mcjit hello.dbg.ll - (gdb) break hello.ll:15 # set breakpoint at line 15 in hello.ll - (gdb) run -``` - -```sh - lldb lli -- -jit-kind=mcjit hello.dbg.ll - (lldb) break set -y hello.ll:15 # set breakpoint at line 15 in hello.ll - (lldb) run -``` - -You should now hit the program at line 15 in `hello.ll`, assuming that -line 15 is a valid line number in the LLVM source. Change this line number -to an appropriate value or to a function name. Note: Since `lli`, at the -time of invocation from gdb will not have, yet, loaded the object file for -`hello`, you will need to set `set breakpoint pending on` in `gdb`. +# DebugIR: Debugging LLVM-IR Files + +This utility [revives](https://lists.llvm.org/pipermail/llvm-dev/2018-March/122013.html) +the debug-ir pass in LLVM, but as a standalone tool. I found the idea of +having a separate utility simpler after knowing that +[this earlier patch](https://reviews.llvm.org/D40778) to revive it as a +pass inside LLVM didn't go through. + +## Instructions +Let us assume that the LLVM-IR to be debugged is named `hello.ll`. +Such a file can be dynamically generated, or using clang as + +```sh + clang -emit-llvm -o hello.ll -S hello.c +``` + +
hello.c + +```C + #include + #include + + int main(int argc, char *argv[]) + { + if (!strcmp(argv[0], "hello")) { + printf("Hello World\n"); + } else { + printf("No hello\n"); + } + return 0; + } +``` +
+ +### Clone and build +This tool requires LLVM-18 to be installed. + +```sh + git clone https://github.com/vaivaswatha/debugir.git debugir + cd debugir; mkdir build; cd build + cmake -DCMAKE_BUILD_TYPE=Release ../ + cmake --build . +``` + +If you have LLVM installed in a non-standard path, you may provide the +additional `CMake` argument `-DLLVM_DIR=/path/to/llvm`. + +If you would like to link debugir to the relevant static LLVM components, use +``` +-DLINK_LLVM_SHARED=OFF +``` +during the cmake command. + +### Run +You should now have an executable file `debugir` in the build directory. + +```sh + ./debugir hello.ll +``` + +This produces a file `hello.dbg.ll`. **NOTE** The utility also overwrites +the input LLVM-IR file (if you have comments in it, they will be lost). +The new file `hello.dbg.ll` is semantically the same as the input file, +but with debug information referring to the input file. + +If you now debug `hello.dbg.ll` (instead of debugging `hello.ll`), the +debugger can pickup and display `hello.ll` as the execution proceeds. + +Note: LLVM values that don't have an explicit name cannot have their +names (and hence values) seen in GDB. To workaround this, provide +explicit names yourself or run the instruction namer pass by providing +the `-instnamer` flag to `debugir`. + +Following on the example [here](https://llvm.org/docs/DebuggingJITedCode.html) +let us try and debug `hello.dbg.ll`. + +```sh + gdb --args lli -jit-kind=mcjit hello.dbg.ll + (gdb) break hello.ll:15 # set breakpoint at line 15 in hello.ll + (gdb) run +``` + +```sh + lldb lli -- -jit-kind=mcjit hello.dbg.ll + (lldb) break set -y hello.ll:15 # set breakpoint at line 15 in hello.ll + (lldb) run +``` + +You should now hit the program at line 15 in `hello.ll`, assuming that +line 15 is a valid line number in the LLVM source. Change this line number +to an appropriate value or to a function name. Note: Since `lli`, at the +time of invocation from gdb will not have, yet, loaded the object file for +`hello`, you will need to set `set breakpoint pending on` in `gdb`. diff --git a/include/debugir/DebugIR.h b/include/debugir/DebugIR.h index fceae6e60..89249ee63 100644 --- a/include/debugir/DebugIR.h +++ b/include/debugir/DebugIR.h @@ -1,32 +1,32 @@ -//===- llvm/Transforms/Instrumentation/DebugIR.h - Interface ----*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file defines the interface of the DebugIR pass. For most users, -// including Instrumentation.h and calling createDebugIRPass() is sufficient and -// there is no need to include this file. -// -//===----------------------------------------------------------------------===// - -#ifndef DEBUG_IR_H -#define DEBUG_IR_H - -#include "llvm/IR/Module.h" -#include -#include - -namespace debugir { - -// Attaches debug info to M, assuming it is parsed from Directory/Filename. -// Returns a module for display in debugger devoid of any debug info. -std::unique_ptr -createDebugInfo(llvm::Module &M, std::string Directory, std::string Filename); - -} // namespace llvm - -#endif // DEBUG_IR_H +//===- llvm/Transforms/Instrumentation/DebugIR.h - Interface ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the interface of the DebugIR pass. For most users, +// including Instrumentation.h and calling createDebugIRPass() is sufficient and +// there is no need to include this file. +// +//===----------------------------------------------------------------------===// + +#ifndef DEBUG_IR_H +#define DEBUG_IR_H + +#include "llvm/IR/Module.h" +#include +#include + +namespace debugir { + +// Attaches debug info to M, assuming it is parsed from Directory/Filename. +// Returns a module for display in debugger devoid of any debug info. +std::unique_ptr +createDebugInfo(llvm::Module &M, std::string Directory, std::string Filename); + +} // namespace llvm + +#endif // DEBUG_IR_H diff --git a/test-files/hello.c b/test-files/hello.c index 3cdfeaac9..9e1d59c3e 100644 --- a/test-files/hello.c +++ b/test-files/hello.c @@ -1,27 +1,27 @@ -#include -#include - -typedef struct { - float fl; - char k; -} S; - -void foo (int a, S s) { - int b = a + 1; - printf ("%d\n", b); - printf ("%f, %c\n", s.fl, s.k); -} - -int main(int argc, char *argv[]) -{ - if (!strcmp(argv[0], "hello")) { - S s = {1.0, 'a'}; - foo(0, s); - printf("Hello World\n"); - } else { - S s = {1.2, 'b'}; - foo(100, s); - printf("No hello\n"); - } - return 0; -} +#include +#include + +typedef struct { + float fl; + char k; +} S; + +void foo (int a, S s) { + int b = a + 1; + printf ("%d\n", b); + printf ("%f, %c\n", s.fl, s.k); +} + +int main(int argc, char *argv[]) +{ + if (!strcmp(argv[0], "hello")) { + S s = {1.0, 'a'}; + foo(0, s); + printf("Hello World\n"); + } else { + S s = {1.2, 'b'}; + foo(100, s); + printf("No hello\n"); + } + return 0; +}