From 5b806c15af712fdc3a7081ba3084b5b33aebeb96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20H=C3=BCck?= Date: Sat, 31 Jan 2026 19:27:03 +0100 Subject: [PATCH 1/7] Make compatible with LLVM 19 and 21 --- include/Util.h | 22 +++++++++++++++++++++- src/printer/LLVMTool.cpp | 8 ++++++-- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/include/Util.h b/include/Util.h index f7a1312..d104624 100644 --- a/include/Util.h +++ b/include/Util.h @@ -9,10 +9,30 @@ #include "llvm/Support/Regex.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Config/llvm-config.h" #include "llvm/IR/Module.h" namespace irprinter::util { +template +inline bool starts_with_any_of(llvm::StringRef lhs, StringTy... rhs) { +#if LLVM_VERSION_MAJOR > 15 + return !lhs.empty() && ((lhs.starts_with(rhs)) || ...); +#else + return !lhs.empty() && ((lhs.startswith(rhs)) || ...); +#endif +} + +template +inline bool ends_with_any_of(llvm::StringRef lhs, StringTy... rhs) { +#if LLVM_VERSION_MAJOR > 15 + return !lhs.empty() && ((lhs.ends_with(rhs)) || ...); +#else + return !lhs.empty() && ((lhs.endswith(rhs)) || ...); +#endif +} + namespace detail { // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4502.pdf : template @@ -75,7 +95,7 @@ inline std::string dump(const Val& s) { template inline std::string try_demangle(String s) { std::string name{s}; -#if LLVM_VERSION_MAJOR == 18 +#if LLVM_VERSION_MAJOR >= 18 auto demangle = llvm::itaniumDemangle(s.data()); #else auto demangle = llvm::itaniumDemangle(s.data(), nullptr, nullptr, nullptr); diff --git a/src/printer/LLVMTool.cpp b/src/printer/LLVMTool.cpp index 23ebf8f..4684c59 100644 --- a/src/printer/LLVMTool.cpp +++ b/src/printer/LLVMTool.cpp @@ -6,7 +6,9 @@ */ #include "printer/LLVMTool.h" +#include "Util.h" +#include #include #include @@ -29,7 +31,7 @@ ArgumentsAdjuster getStripOptFlagAdjuster() { CommandLineArguments AdjustedArgs; for (size_t i = 0, e = Args.size(); i < e; ++i) { StringRef Arg = Args[i]; - if (!Arg.startswith("-O") && !Arg.startswith("-g")) { + if (!util::starts_with_any_of(Arg, "-O", "-g")) { AdjustedArgs.push_back(Args[i]); } } @@ -49,7 +51,9 @@ class ExtractorAction : public CodeGenAction { bool BeginSourceFileAction(CompilerInstance& CI) { // FIXME workaround for error: "clang: Not enough positional command line arguments specified!" // when using clangtool, the commandline parser is executed twice, this removes a leftover causing the above error +#if LLVM_VERSION_MAJOR < 19 llvm::cl::TopLevelSubCommand->PositionalOpts.clear(); +#endif return CodeGenAction::BeginSourceFileAction(CI); } @@ -118,7 +122,7 @@ void LLVMTool::clearUserFlags() { void LLVMTool::commitUserArgs() { for (auto& arg : user_args) { StringRef flag = arg; - if (flag.startswith("-O") || flag.startswith("-g")) { + if (util::starts_with_any_of(flag, "-O", "-g")) { tool.appendArgumentsAdjuster(combineAdjusters( adjuster::getStripOptFlagAdjuster(), getInsertArgumentAdjuster(flag.data(), ArgumentInsertPosition::END))); } else { From 4492ad8670d063895d1e263c2cd8f014bdfdc903 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20H=C3=BCck?= Date: Sun, 1 Feb 2026 12:54:21 +0100 Subject: [PATCH 2/7] Debug location based printing (#6) --- .github/workflows/basic-ci.yml | 37 +++++++--- README.md | 122 ++++++++++++++++++--------------- include/printer/IRNodeFinder.h | 2 + src/main.cpp | 18 ++++- src/printer/IRNodeFinder.cpp | 33 +++++++++ src/printer/LLVMTool.cpp | 2 +- 6 files changed, 144 insertions(+), 70 deletions(-) diff --git a/.github/workflows/basic-ci.yml b/.github/workflows/basic-ci.yml index 4f94ea5..96c9c36 100644 --- a/.github/workflows/basic-ci.yml +++ b/.github/workflows/basic-ci.yml @@ -11,18 +11,31 @@ env: jobs: format-check: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 + + - run: | + wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - + echo "deb http://apt.llvm.org/noble/ llvm-toolchain-noble-18 main" | sudo tee /etc/apt/sources.list.d/llvm-18.list + + - name: Update apt + run: sudo apt-get update + + - name: Install clang-format + run: | + sudo apt-get remove clang-format-* + sudo apt-get install -t llvm-toolchain-noble-18 clang-format-18 - name: Format source code run: | find demo lib test \ -type f \ -a \( -name "*.c" -o -name "*.cpp" -o -name "*.h" \) \ + -not -path "*/lulesh/*" -not -path "*/CallSite.h" \ -print0 \ - | xargs -0 clang-format-14 -i + | xargs -0 clang-format-18 -i - name: Format check run: | @@ -33,28 +46,34 @@ jobs: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v4 - - uses: codespell-project/actions-codespell@v2 + - uses: actions/checkout@v5 + - uses: codespell-project/actions-codespell@v2.2 build-project: strategy: fail-fast: false matrix: include: - - llvm-version: 12 - os: ubuntu-20.04 - preset: develop - llvm-version: 14 os: ubuntu-22.04 preset: develop - llvm-version: 18 os: ubuntu-24.04 preset: develop + - llvm-version: 21 + os: ubuntu-24.04 + preset: develop runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 + + - name: LLVM apt + if: ${{ matrix.llvm-version >= 19 }} + run: | + wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - + echo "deb http://apt.llvm.org/noble/ llvm-toolchain-noble-${{ matrix.llvm-version }} main" | sudo tee /etc/apt/sources.list.d/llvm-${{ matrix.llvm-version }}.list - name: Update apt run: sudo apt-get update diff --git a/README.md b/README.md index 9c6a7cb..b4672be 100644 --- a/README.md +++ b/README.md @@ -1,86 +1,94 @@ # irprinter · ![License](https://img.shields.io/github/license/ahueck/irprinter) -*irprinter* is a command-line tool for exploring LLVM Intermediate Representation (IR) code. -It allows users to print IR code for specific functions, which is particularly useful when dumping the entire translation unit would result in excessive output. - +*irprinter* is a command-line tool for exploring LLVM Intermediate Representation (IR). +It allows you to print IR for specific functions, which is especially useful when dumping an entire translation unit would produce excessive output. ## Features -* Print LLVM IR code for a translation unit (C/C++) to the console. -* Modify and add compiler flags (e.g., replace -g with -O2) and regenerate the (modified) IR. -* Regex matching of (demangled) function names, with options to print: +* Print LLVM IR for a translation unit (C/C++) to the console. +* Modify or add compiler flags (e.g., replacing `-g` with `-O2`) and regenerate the IR. +* Match (demangled) function names using regular expressions, with options to print: 1. Function signatures only. - 2. Functions including their bodies. -* Dump the entire IR code of the translation unit. + 2. Full function bodies. +* Dump the entire IR of a translation unit. +* Print statements within a specific line number range (based on debug information). ## Usage -See [main.cpp](src/main.cpp) for all possible command-line arguments. +Refer to [main.cpp](src/main.cpp) for a full list of command-line arguments. + +### Example +Assume `test.c` contains the following code: -### Example of using *irprinter* -Assume *test.c* contains the code: +```c +int foo () { + return 2; +} +int main() { + int val; + val = foo(); + return 0; +} +``` - ```c - int foo () { - return 2; - } - int main() { - int val; - val = foo(); - return 0; - } - ``` #### Using irprinter on test.c -In this example we - 1) load `test.c` with standard Clang flags, - 2) list all functions in `test.c`, - 3) print the body of main, - 4) optimize the code with `-O3`, and finally, - 5) print the body of main again. +In this example, we: +1. Load `test.c` with standard Clang and debug flags. +2. List all functions in `test.c`. +3. Print the body of `main`. +4. Print statements within the line range [6, 7]. +5. Optimize the code with `-O3`. +6. Print the body of `main` again to see the result of the optimization. ```console -ahueck@sys:~/irprint/install$ ./bin/irprinter ../test.c -- +$~/irprint/install$ ./bin/irprinter ../test.c -- -g ir-printer> l -Match 1 [foo]: -; Function Attrs: noinline nounwind optnone uwtable -define i32 @foo() #0 +Match 1 [foo()]: +; Function Attrs: mustprogress noinline nounwind optnone uwtable +define dso_local noundef i32 @_Z3foov() #0 !dbg !10 Match 2 [main]: -; Function Attrs: noinline nounwind optnone uwtable -define i32 @main() #0 +; Function Attrs: mustprogress noinline norecurse nounwind optnone uwtable +define dso_local noundef i32 @main() #1 !dbg !10 ir-printer> p main -Match 1 [main]: -; Function Attrs: noinline nounwind optnone uwtable -define i32 @main() #0 { - %1 = alloca i32, align 4 - %2 = alloca i32, align 4 - store i32 0, i32* %1, align 4 - %3 = call i32 @foo() - store i32 %3, i32* %2, align 4 - ret i32 0 +Match 1 [main]:; Function Attrs: mustprogress noinline norecurse nounwind optnone uwtable +define dso_local noundef i32 @main() #1 !dbg !10 { +entry: + %retval = alloca i32, align 4 + %val = alloca i32, align 4 + store i32 0, ptr %retval, align 4 + #dbg_declare(ptr %val, !16, !DIExpression(), !17) + %call = call noundef i32 @_Z3foov(), !dbg !18 + store i32 %call, ptr %val, align 4, !dbg !19 + ret i32 0, !dbg !20 } +ir-printer> 6 7 +main: + %call = call noundef i32 @_Z3foov(), !dbg !18 + store i32 %call, ptr %val, align 4, !dbg !19 + ret i32 0, !dbg !20 + ir-printer> f -O3 Set flag to -O3. Re-generating module... ir-printer> p main -Match 1 [main]: -; Function Attrs: norecurse nounwind readnone uwtable -define i32 @main() local_unnamed_addr #1 { +Match 1 [main]:; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) uwtable +define dso_local noundef i32 @main() local_unnamed_addr #0 { +entry: ret i32 0 } - ``` -## How to build -###### Requirements +## How to Build + +### Requirements - CMake >= 3.20 -- Clang/LLVM 12, 14, 18 (CMake needs to find the installation, see - the [LLVM CMake documentation](https://llvm.org/docs/CMake.html) or the [CI workflow](.github/workflows/basic-ci.yml)) -- C++17 compiler +- Clang/LLVM 12, 14, or 18-21 (CMake must be able to find the installation; see the [LLVM CMake documentation](https://llvm.org/docs/CMake.html) or the [CI workflow](.github/workflows/basic-ci.yml)) +- A C++17 compatible compiler -###### Build steps -In the root project folder, execute the following commands (see also [CI workflow](.github/workflows/basic-ci.yml)) +### Build Steps +From the root project folder, execute the following commands: - ``` - cmake -B build -DCMAKE_INSTALL_PREFIX=*your path* - cmake --build build --target install --parallel - ``` +```bash +cmake -B build -DCMAKE_INSTALL_PREFIX=/path/to/install +cmake --build build --target install --parallel +``` \ No newline at end of file diff --git a/include/printer/IRNodeFinder.h b/include/printer/IRNodeFinder.h index 461b591..cedf6bd 100644 --- a/include/printer/IRNodeFinder.h +++ b/include/printer/IRNodeFinder.h @@ -37,6 +37,8 @@ class IRNodeFinder { void printFunction(const std::string& regex = ".*") const; + void printByLocation(unsigned line_start_, unsigned line_end_ = 0) const; + void listFunction(const std::string& regex = ".*") const; static std::string demangle(const std::string& name); diff --git a/src/main.cpp b/src/main.cpp index 1f205f6..fa4e5d4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,11 +1,11 @@ #include #include -#include #include #include #include #include +#include #include using namespace llvm; @@ -53,7 +53,6 @@ int main(int argc, const char** argv) { irprinter::IRNodeFinder ir(op.get()); #endif - auto ret = ir.parse(); if (ret != 0) { llvm::outs() << "Error parsing. Quitting...\n"; @@ -67,6 +66,20 @@ int main(int argc, const char** argv) { if (cmd == "q" || cmd == "quit") { break; + } else if (unsigned start; !cmd.getAsInteger(10, start)) { + auto end_ref = lexWord(StringRef(cmd.end(), ref.end() - cmd.end())); + unsigned end{start}; + if (!end_ref.empty()) { + if (end_ref.getAsInteger(10, end)) { + llvm::outs() << "Invalid end location: " << end_ref << "\n"; + continue; + } + } + if (end < start) { + llvm::outs() << "Error: end location (" << end << ") is less than start location (" << start << ")\n"; + } else { + ir.printByLocation(start, end); + } } else if (cmd == "g" || cmd == "generate") { ir.parse(); } else if (cmd == "f" || cmd == "flag") { @@ -98,7 +111,6 @@ int main(int argc, const char** argv) { auto demangled_name = irprinter::IRNodeFinder::demangle(std::string{str}); llvm::outs() << "Demangled name: " << demangled_name << "\n"; } - llvm::outs().flush(); } diff --git a/src/printer/IRNodeFinder.cpp b/src/printer/IRNodeFinder.cpp index 04a356c..5646aae 100644 --- a/src/printer/IRNodeFinder.cpp +++ b/src/printer/IRNodeFinder.cpp @@ -9,8 +9,11 @@ #include "Util.h" #include +#include #include +#include #include +#include using namespace llvm; @@ -52,6 +55,36 @@ void IRNodeFinder::printFunction(const std::string& regex) const { applyToMatchingFunction(os, m, regex, [&](const Function* f) { f->print(os); }); } +void IRNodeFinder::printByLocation(unsigned line_start_, unsigned line_end_) const { + line_end_ = std::max(line_start_, line_end_); + std::string matches; + llvm::raw_string_ostream local_oss{matches}; + bool first_match{false}; + const auto* m = tool.getModule(); + for (const auto& f : *m) { + first_match = false; + for (const auto& bb : f) { + for (const auto& inst : bb) { + const auto& loc = inst.getDebugLoc(); + if (loc) { + const auto line = loc.getLine(); + if (line >= line_start_ && line <= line_end_) { + if (!first_match) { + local_oss << f.getName() << ":\n"; + first_match = true; + } + inst.print(local_oss); + local_oss << "\n"; + } + } + } + } + } + if (!local_oss.str().empty()) { + os << local_oss.str() << "\n"; + } +} + void IRNodeFinder::listFunction(const std::string& regex) const { const auto* m = tool.getModule(); applyToMatchingFunction(os, m, regex, [&](const Function* f) { diff --git a/src/printer/LLVMTool.cpp b/src/printer/LLVMTool.cpp index 4684c59..083286c 100644 --- a/src/printer/LLVMTool.cpp +++ b/src/printer/LLVMTool.cpp @@ -8,9 +8,9 @@ #include "printer/LLVMTool.h" #include "Util.h" -#include #include #include +#include #include #include From 8ba04f824a0097be403eafb34b1495ed568d2b10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20H=C3=BCck?= Date: Sun, 1 Feb 2026 16:06:19 +0100 Subject: [PATCH 3/7] Update CMake install rules --- src/CMakeLists.txt | 8 -------- src/printer/CMakeLists.txt | 8 +++----- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b0a122f..37e1efb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -11,11 +11,6 @@ target_project_compile_definitions(llvm-ir-printer${EXE_SUFFIX} LOG_LEVEL=${LOG_LEVEL} ) -target_include_directories(llvm-ir-printer${EXE_SUFFIX} - PRIVATE - ${PROJECT_SOURCE_DIR}/include - ) - target_link_libraries(llvm-ir-printer${EXE_SUFFIX} LLVMLineEditor llvm-ir-print @@ -24,7 +19,4 @@ target_link_libraries(llvm-ir-printer${EXE_SUFFIX} install( TARGETS llvm-ir-printer${EXE_SUFFIX} RUNTIME DESTINATION bin - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib - PUBLIC_HEADER DESTINATION include ) diff --git a/src/printer/CMakeLists.txt b/src/printer/CMakeLists.txt index 09dcd7e..8acb181 100644 --- a/src/printer/CMakeLists.txt +++ b/src/printer/CMakeLists.txt @@ -12,7 +12,8 @@ target_project_compile_definitions(llvm-ir-print target_include_directories(llvm-ir-print PUBLIC - ${PROJECT_SOURCE_DIR}/include + $ + $ ) target_include_directories(llvm-ir-print @@ -30,13 +31,10 @@ install( TARGETS llvm-ir-print LIBRARY DESTINATION lib ARCHIVE DESTINATION lib - PUBLIC_HEADER DESTINATION include ) install( - FILES - ${PROJECT_SOURCE_DIR}/include/printer/IRNodeFinder.h - ${PROJECT_SOURCE_DIR}/include/printer/LLVMTool.h + DIRECTORY ${PROJECT_SOURCE_DIR}/include/ DESTINATION include ) From 1c93c3db3baa84725c7b3250ba18448fb6b325ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20H=C3=BCck?= Date: Sun, 1 Feb 2026 17:26:32 +0100 Subject: [PATCH 4/7] Constructor for compilation database --- include/printer/IRNodeFinder.h | 2 ++ include/printer/LLVMTool.h | 3 +++ src/printer/IRNodeFinder.cpp | 9 ++++++++- src/printer/LLVMTool.cpp | 6 +++++- 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/include/printer/IRNodeFinder.h b/include/printer/IRNodeFinder.h index cedf6bd..86f917f 100644 --- a/include/printer/IRNodeFinder.h +++ b/include/printer/IRNodeFinder.h @@ -28,6 +28,8 @@ class IRNodeFinder { public: explicit IRNodeFinder(clang::tooling::CommonOptionsParser& op, llvm::raw_ostream& os = llvm::outs()); + IRNodeFinder(const clang::tooling::CompilationDatabase& compilation_database, llvm::ArrayRef SourcePaths, + llvm::raw_ostream& os = llvm::outs()); int parse(); diff --git a/include/printer/LLVMTool.h b/include/printer/LLVMTool.h index e008d7f..669fbbc 100644 --- a/include/printer/LLVMTool.h +++ b/include/printer/LLVMTool.h @@ -9,8 +9,10 @@ #define SRC_PRINTER_LLVMTOOL_H_ #include +#include #include +#include #include #include #include @@ -35,6 +37,7 @@ class LLVMTool { public: explicit LLVMTool(clang::tooling::CommonOptionsParser&); + LLVMTool(const clang::tooling::CompilationDatabase&, llvm::ArrayRef SourcePaths); int execute(); diff --git a/src/printer/IRNodeFinder.cpp b/src/printer/IRNodeFinder.cpp index 5646aae..f586743 100644 --- a/src/printer/IRNodeFinder.cpp +++ b/src/printer/IRNodeFinder.cpp @@ -8,6 +8,7 @@ #include "printer/IRNodeFinder.h" #include "Util.h" +#include #include #include #include @@ -35,7 +36,13 @@ void applyToMatchingFunction(llvm::raw_ostream& os, const llvm::Module* m, const // using namespace util; -IRNodeFinder::IRNodeFinder(clang::tooling::CommonOptionsParser& op, llvm::raw_ostream& os) : tool(op), os(os) { +IRNodeFinder::IRNodeFinder(clang::tooling::CommonOptionsParser& op, llvm::raw_ostream& os) + : IRNodeFinder(op.getCompilations(), op.getSourcePathList(), os) { +} + +IRNodeFinder::IRNodeFinder(const clang::tooling::CompilationDatabase& compilation_database, + llvm::ArrayRef SourcePaths, llvm::raw_ostream& os) + : tool(compilation_database, SourcePaths), os(os) { } int IRNodeFinder::parse() { diff --git a/src/printer/LLVMTool.cpp b/src/printer/LLVMTool.cpp index 083286c..1a259b7 100644 --- a/src/printer/LLVMTool.cpp +++ b/src/printer/LLVMTool.cpp @@ -86,7 +86,11 @@ std::unique_ptr CreateExtractorActionFactory(LLV } // namespace action -LLVMTool::LLVMTool(CommonOptionsParser& op) : tool(op.getCompilations(), op.getSourcePathList()) { +LLVMTool::LLVMTool(CommonOptionsParser& op) : LLVMTool(op.getCompilations(), op.getSourcePathList()) { +} + +LLVMTool::LLVMTool(const CompilationDatabase& compilation_database, ArrayRef source_path) + : tool(compilation_database, source_path) { } int LLVMTool::execute() { From 9ef27977dd6a5051872b766cd63567fe85e3931f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20H=C3=BCck?= Date: Mon, 2 Feb 2026 19:38:06 +0100 Subject: [PATCH 5/7] Refactoring; use clang resource dir --- CMakeLists.txt | 2 +- cmake/ToolchainOptions.cmake | 14 ++++++++++++++ cmake/modules/clang-format.cmake | 4 ++-- cmake/modules/clang-tidy.cmake | 14 +++++++------- cmake/modules/log-util.cmake | 2 +- cmake/modules/target-util.cmake | 4 ++-- src/CMakeLists.txt | 8 ++++---- src/main.cpp | 16 +++++++++++----- src/printer/CMakeLists.txt | 12 ++++++++---- src/printer/IRNodeFinder.cpp | 4 ++-- src/printer/LLVMTool.cpp | 8 ++++++++ 11 files changed, 60 insertions(+), 28 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 06007d4..b11f139 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,7 @@ list(APPEND CMAKE_MODULE_PATH include(ToolchainOptions) include(CMakePackageConfigHelpers) -add_format_target(format-sources +irprinter_add_format_target(format-sources "Formats project source files" TARGETS src/*.cpp include/*.h diff --git a/cmake/ToolchainOptions.cmake b/cmake/ToolchainOptions.cmake index 165da8b..719e793 100644 --- a/cmake/ToolchainOptions.cmake +++ b/cmake/ToolchainOptions.cmake @@ -29,6 +29,20 @@ include(target-util) set(LOG_LEVEL 0 CACHE STRING "Granularity of the logger. 3 is most verbose, 0 is least.") +option(IRPRINTER_AUTO_RESOURCE_DIR "Try to automatically set the Clang resource directory" OFF) + +if(IRPRINTER_AUTO_RESOURCE_DIR AND NOT IRPRINTER_CLANG_RESOURCE_DIR) + find_program(CLANG_EXECUTABLE NAMES clang-${LLVM_VERSION_MAJOR} clang) + if(CLANG_EXECUTABLE) + execute_process( + COMMAND ${CLANG_EXECUTABLE} -print-resource-dir + OUTPUT_VARIABLE CLANG_RESOURCE_DIR_VAR + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + set(IRPRINTER_CLANG_RESOURCE_DIR ${CLANG_RESOURCE_DIR_VAR} CACHE STRING "Clang resource directory") + endif() +endif() + if (NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Debug CACHE STRING "" FORCE) message(STATUS "Building as debug (default)") diff --git a/cmake/modules/clang-format.cmake b/cmake/modules/clang-format.cmake index 900e3e2..16c5064 100644 --- a/cmake/modules/clang-format.cmake +++ b/cmake/modules/clang-format.cmake @@ -1,4 +1,4 @@ -function(add_format_target target comment) +function(irprinter_add_format_target target comment) macro(filter_dir _dir_name_) foreach (SOURCE_FILE ${ALL_CXX_FILES}) string(FIND ${SOURCE_FILE} ${_dir_name_} EXCLUDE_FOUND) @@ -20,7 +20,7 @@ function(add_format_target target comment) endforeach() find_program(FORMAT_COMMAND - NAMES clang-format clang-format-12 clang-format-14 clang-format-18) + NAMES clang-format-${LLVM_VERSION_MAJOR} clang-format) if(FORMAT_COMMAND) add_custom_target(${target} COMMAND ${FORMAT_COMMAND} -i -style=file ${ARG_OTHER} ${ARG_UNPARSED_ARGUMENTS} diff --git a/cmake/modules/clang-tidy.cmake b/cmake/modules/clang-tidy.cmake index 33ededd..443a00b 100644 --- a/cmake/modules/clang-tidy.cmake +++ b/cmake/modules/clang-tidy.cmake @@ -1,4 +1,4 @@ -function(add_tidy_target target comment) +function(irprinter_add_tidy_target target comment) macro(filter_dir _name_) foreach (SOURCE_FILE ${ARG_SOURCES}) string(FIND ${SOURCE_FILE} ${_name_} EXCLUDE_FOUND) @@ -15,7 +15,7 @@ function(add_tidy_target target comment) endforeach() find_program(TIDY_COMMAND - NAMES clang-tidy clang-tidy-12 clang-tidy-14 clang-tidy-18) + NAMES clang-tidy-${LLVM_VERSION_MAJOR} clang-tidy) if(TIDY_COMMAND) add_custom_target(${target} COMMAND ${TIDY_COMMAND} -p ${CMAKE_BINARY_DIR} @@ -33,23 +33,23 @@ function(add_tidy_target target comment) endif() endfunction() -function(add_tidy_fix_target target comment) +function(irprinter_add_tidy_fix_target target comment) cmake_parse_arguments(ARG "" "" "SOURCES;EXCLUDES;OTHER" ${ARGN}) - add_tidy_target(${target} "${comment}" + irprinter_add_tidy_target(${target} "${comment}" SOURCES ${ARG_SOURCES} EXCLUDES ${ARG_EXCLUDES} OTHER ${ARG_OTHER} -fix ) endfunction() -function(make_tidy_check name sources) - add_tidy_target(tidy-run-on-${name} +function(irprinter_make_tidy_check name sources) + irprinter_add_tidy_target(tidy-run-on-${name} "Clang-tidy run on ${name} translation units" SOURCES ${sources} OTHER --header-filter=${CMAKE_CURRENT_SOURCE_DIR} ) - add_tidy_fix_target(tidy-fix-on-${name} + irprinter_add_tidy_fix_target(tidy-fix-on-${name} "Clang-tidy run with fixes on ${name} translation units" SOURCES ${sources} OTHER --header-filter=${CMAKE_CURRENT_SOURCE_DIR} -checks=-*,modernize-*,llvm-namespace-comment,google-explicit-constructor diff --git a/cmake/modules/log-util.cmake b/cmake/modules/log-util.cmake index 40ebd7f..9422cfa 100644 --- a/cmake/modules/log-util.cmake +++ b/cmake/modules/log-util.cmake @@ -1,4 +1,4 @@ -function(target_define_file_basename targetname) +function(irprinter_target_define_file_basename targetname) get_target_property(source_files "${targetname}" SOURCES) foreach(sourcefile ${source_files}) diff --git a/cmake/modules/target-util.cmake b/cmake/modules/target-util.cmake index a7a7112..128b5c4 100644 --- a/cmake/modules/target-util.cmake +++ b/cmake/modules/target-util.cmake @@ -1,4 +1,4 @@ -function(target_project_compile_options target) +function(irprinter_target_project_compile_options target) cmake_parse_arguments(ARG "" "" "PRIVATE_FLAGS;PUBLIC_FLAGS" ${ARGN}) target_compile_options(${target} PRIVATE @@ -22,7 +22,7 @@ function(target_project_compile_options target) endif () endfunction() -function(target_project_compile_definitions target) +function(irprinter_target_project_compile_definitions target) cmake_parse_arguments(ARG "" "" "PRIVATE_DEFS;PUBLIC_DEFS" ${ARGN}) if (ARG_PRIVATE_DEFS) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 37e1efb..a0ad622 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,11 +4,11 @@ add_executable(llvm-ir-printer${EXE_SUFFIX} main.cpp ) -target_define_file_basename(llvm-ir-printer${EXE_SUFFIX}) -target_project_compile_options(llvm-ir-printer${EXE_SUFFIX}) -target_project_compile_definitions(llvm-ir-printer${EXE_SUFFIX} +irprinter_target_define_file_basename(llvm-ir-printer${EXE_SUFFIX}) +irprinter_target_project_compile_options(llvm-ir-printer${EXE_SUFFIX}) +irprinter_target_project_compile_definitions(llvm-ir-printer${EXE_SUFFIX} PRIVATE_DEFS - LOG_LEVEL=${LOG_LEVEL} + LOG_LEVEL=${IRPRINTER_LOG_LEVEL} ) target_link_libraries(llvm-ir-printer${EXE_SUFFIX} diff --git a/src/main.cpp b/src/main.cpp index fa4e5d4..4cdec3c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,7 @@ #include #include +#include #include #include #include @@ -43,19 +44,24 @@ StringRef lexWord(StringRef word) { int main(int argc, const char** argv) { #if LLVM_VERSION_MAJOR < 14 CommonOptionsParser op(argc, argv, IRPrinter); - irprinter::IRNodeFinder ir(op); + const auto& sources = op.getSourcePathList(); + const auto& compilations = op.getCompilations(); #else - auto op = CommonOptionsParser::create(argc, argv, IRPrinter); - if (!op) { + auto op_res = CommonOptionsParser::create(argc, argv, IRPrinter); + if (!op_res) { llvm::outs() << "Erroneous input"; return 1; } - irprinter::IRNodeFinder ir(op.get()); + auto& op = op_res.get(); + const auto& sources = op.getSourcePathList(); + const auto& compilations = op.getCompilations(); #endif + irprinter::IRNodeFinder ir(compilations, sources); + auto ret = ir.parse(); if (ret != 0) { - llvm::outs() << "Error parsing. Quitting...\n"; + llvm::errs() << "Error parsing. Quitting...\n"; return ret; } diff --git a/src/printer/CMakeLists.txt b/src/printer/CMakeLists.txt index 8acb181..67c235d 100644 --- a/src/printer/CMakeLists.txt +++ b/src/printer/CMakeLists.txt @@ -3,13 +3,17 @@ add_library(llvm-ir-print STATIC LLVMTool.cpp ) -target_define_file_basename(llvm-ir-print) -target_project_compile_options(llvm-ir-print) -target_project_compile_definitions(llvm-ir-print +irprinter_target_define_file_basename(llvm-ir-print) +irprinter_target_project_compile_options(llvm-ir-print) +irprinter_target_project_compile_definitions(llvm-ir-print PRIVATE_DEFS - LOG_LEVEL=${LOG_LEVEL} + LOG_LEVEL=${IRPRINTER_LOG_LEVEL} ) +if(IRPRINTER_CLANG_RESOURCE_DIR) + target_compile_definitions(llvm-ir-print PRIVATE IRPRINTER_CLANG_RESOURCE_DIR="${IRPRINTER_CLANG_RESOURCE_DIR}") +endif() + target_include_directories(llvm-ir-print PUBLIC $ diff --git a/src/printer/IRNodeFinder.cpp b/src/printer/IRNodeFinder.cpp index f586743..c9a369a 100644 --- a/src/printer/IRNodeFinder.cpp +++ b/src/printer/IRNodeFinder.cpp @@ -41,8 +41,8 @@ IRNodeFinder::IRNodeFinder(clang::tooling::CommonOptionsParser& op, llvm::raw_os } IRNodeFinder::IRNodeFinder(const clang::tooling::CompilationDatabase& compilation_database, - llvm::ArrayRef SourcePaths, llvm::raw_ostream& os) - : tool(compilation_database, SourcePaths), os(os) { + llvm::ArrayRef source_path, llvm::raw_ostream& os) + : tool(compilation_database, source_path), os(os) { } int IRNodeFinder::parse() { diff --git a/src/printer/LLVMTool.cpp b/src/printer/LLVMTool.cpp index 1a259b7..f5b1a9f 100644 --- a/src/printer/LLVMTool.cpp +++ b/src/printer/LLVMTool.cpp @@ -91,6 +91,14 @@ LLVMTool::LLVMTool(CommonOptionsParser& op) : LLVMTool(op.getCompilations(), op. LLVMTool::LLVMTool(const CompilationDatabase& compilation_database, ArrayRef source_path) : tool(compilation_database, source_path) { +#ifdef IRPRINTER_CLANG_RESOURCE_DIR + const std::string resource_dir = IRPRINTER_CLANG_RESOURCE_DIR; + if (!resource_dir.empty()) { + const std::string resource_arg = "-resource-dir=" + resource_dir; + tool.appendArgumentsAdjuster( + clang::tooling::getInsertArgumentAdjuster(resource_arg.c_str(), clang::tooling::ArgumentInsertPosition::BEGIN)); + } +#endif } int LLVMTool::execute() { From 3846c7602d967f47a7716411dca3b4e0e5f17f97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20H=C3=BCck?= Date: Mon, 2 Feb 2026 22:39:57 +0100 Subject: [PATCH 6/7] Presets with coverage --- .github/workflows/basic-ci.yml | 4 +- CMakePresets.json | 73 ++++++++++++++++++++++++++++++++++ cmake/ToolchainOptions.cmake | 3 +- 3 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 CMakePresets.json diff --git a/.github/workflows/basic-ci.yml b/.github/workflows/basic-ci.yml index 96c9c36..86306ae 100644 --- a/.github/workflows/basic-ci.yml +++ b/.github/workflows/basic-ci.yml @@ -94,10 +94,10 @@ jobs: - name: Build IRPrinter run: | - cmake -B build -DCMAKE_BUILD_TYPE=Debug -DLLVM_DIR=${LLVM_CMAKE_DIR} -DClang_DIR=${CLANG_CMAKE_DIR} + cmake --preset develop -DLLVM_DIR=${LLVM_CMAKE_DIR} -DClang_DIR=${CLANG_CMAKE_DIR} cmake --build build --parallel - name: Build IRPrinter release run: | - cmake -B build_rel -DCMAKE_BUILD_TYPE=Release -DLLVM_DIR=${LLVM_CMAKE_DIR} -DClang_DIR=${CLANG_CMAKE_DIR} + cmake --preset release -DLLVM_DIR=${LLVM_CMAKE_DIR} -DClang_DIR=${CLANG_CMAKE_DIR} cmake --build build_rel --parallel --target install diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..cb73115 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,73 @@ +{ + "version": 2, + "cmakeMinimumRequired": { + "major": 3, + "minor": 20, + "patch": 0 + }, + "configurePresets": [ + { + "name": "clang-toolchain", + "hidden": true, + "generator": "Unix Makefiles", + "cacheVariables": { + "CMAKE_C_COMPILER": "clang", + "CMAKE_CXX_COMPILER": "clang++" + } + }, + { + "name": "develop", + "displayName": "Develop (Debug)", + "description": "Default develop build options for Clang", + "binaryDir": "${sourceDir}/build", + "inherits": [ + "clang-toolchain" + ], + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "release", + "displayName": "Release", + "description": "Default release build options for Clang", + "binaryDir": "${sourceDir}/build_rel", + "inherits": [ + "clang-toolchain" + ], + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "coverage", + "displayName": "Coverage (LLVM)", + "description": "Default coverage build options for Clang (LLVM-based)", + "binaryDir": "${sourceDir}/build_cov", + "inherits": [ + "clang-toolchain" + ], + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "IRPRINTER_ENABLE_COVERAGE": "ON", + "CMAKE_C_FLAGS": "-fprofile-instr-generate -fcoverage-mapping", + "CMAKE_CXX_FLAGS": "-fprofile-instr-generate -fcoverage-mapping", + "CMAKE_EXE_LINKER_FLAGS": "-fprofile-instr-generate -fcoverage-mapping" + } + } + ], + "buildPresets": [ + { + "name": "develop", + "configurePreset": "develop" + }, + { + "name": "release", + "configurePreset": "release" + }, + { + "name": "coverage", + "configurePreset": "coverage" + } + ] +} diff --git a/cmake/ToolchainOptions.cmake b/cmake/ToolchainOptions.cmake index 719e793..82e6212 100644 --- a/cmake/ToolchainOptions.cmake +++ b/cmake/ToolchainOptions.cmake @@ -27,9 +27,10 @@ include(clang-format) include(log-util) include(target-util) -set(LOG_LEVEL 0 CACHE STRING "Granularity of the logger. 3 is most verbose, 0 is least.") +set(IRPRINTER_LOG_LEVEL 0 CACHE STRING "Granularity of the logger. 3 is most verbose, 0 is least.") option(IRPRINTER_AUTO_RESOURCE_DIR "Try to automatically set the Clang resource directory" OFF) +option(IRPRINTER_ENABLE_COVERAGE "Enable LLVM-based coverage" OFF) if(IRPRINTER_AUTO_RESOURCE_DIR AND NOT IRPRINTER_CLANG_RESOURCE_DIR) find_program(CLANG_EXECUTABLE NAMES clang-${LLVM_VERSION_MAJOR} clang) From a161480fc4d3d589f82c56b7c87beebec89efad1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20H=C3=BCck?= Date: Tue, 3 Feb 2026 18:25:20 +0100 Subject: [PATCH 7/7] Lit testing --- .github/workflows/basic-ci.yml | 18 +++++++---- CMakeLists.txt | 7 +++-- cmake/modules/target-util.cmake | 51 +++++++++++++++++++++++++++++++ include/Util.h | 5 ++- src/main.cpp | 2 +- src/printer/IRNodeFinder.cpp | 13 ++++++-- test/CMakeLists.txt | 54 +++++++++++++++++++++++++++++++++ test/codes/decl.cpp | 9 ++++++ test/codes/demangle.cpp | 7 +++++ test/codes/dump.cpp | 7 +++++ test/codes/errors.cpp | 11 +++++++ test/codes/flags.cpp | 8 +++++ test/codes/generate.cpp | 6 ++++ test/codes/list.cpp | 9 ++++++ test/codes/list_all.cpp | 9 ++++++ test/codes/location.cpp | 10 ++++++ test/codes/opt_flag.cpp | 8 +++++ test/codes/quit.cpp | 6 ++++ test/lit.cfg.py | 18 +++++++++++ test/lit.site.cfg.py.in | 12 ++++++++ 20 files changed, 257 insertions(+), 13 deletions(-) create mode 100644 test/CMakeLists.txt create mode 100644 test/codes/decl.cpp create mode 100644 test/codes/demangle.cpp create mode 100644 test/codes/dump.cpp create mode 100644 test/codes/errors.cpp create mode 100644 test/codes/flags.cpp create mode 100644 test/codes/generate.cpp create mode 100644 test/codes/list.cpp create mode 100644 test/codes/list_all.cpp create mode 100644 test/codes/location.cpp create mode 100644 test/codes/opt_flag.cpp create mode 100644 test/codes/quit.cpp create mode 100644 test/lit.cfg.py create mode 100644 test/lit.site.cfg.py.in diff --git a/.github/workflows/basic-ci.yml b/.github/workflows/basic-ci.yml index 86306ae..63a5043 100644 --- a/.github/workflows/basic-ci.yml +++ b/.github/workflows/basic-ci.yml @@ -92,12 +92,18 @@ jobs: echo "CLANG_CMAKE_DIR=/usr/lib/llvm-${{ matrix.llvm-version }}/lib/cmake/clang" >> $GITHUB_ENV echo "EXTERNAL_LIT=/usr/lib/llvm-${{ matrix.llvm-version }}/build/utils/lit/lit.py" >> $GITHUB_ENV - - name: Build IRPrinter - run: | - cmake --preset develop -DLLVM_DIR=${LLVM_CMAKE_DIR} -DClang_DIR=${CLANG_CMAKE_DIR} - cmake --build build --parallel - - name: Build IRPrinter release run: | - cmake --preset release -DLLVM_DIR=${LLVM_CMAKE_DIR} -DClang_DIR=${CLANG_CMAKE_DIR} + cmake --preset release -DLLVM_DIR=${LLVM_CMAKE_DIR} -DClang_DIR=${CLANG_CMAKE_DIR} -DLLVM_EXTERNAL_LIT=${EXTERNAL_LIT} cmake --build build_rel --parallel --target install + + - name: Test and Coverage + run: | + cmake --preset coverage -DLLVM_DIR=${LLVM_CMAKE_DIR} -DClang_DIR=${CLANG_CMAKE_DIR} -DLLVM_EXTERNAL_LIT=${EXTERNAL_LIT} + cmake --build build_cov --target coverage-irprinter + + - name: Upload coverage + uses: actions/upload-artifact@v6 + with: + name: coverage-report-llvm${{ matrix.llvm-version }} + path: build_cov/coverage_report/ diff --git a/CMakeLists.txt b/CMakeLists.txt index b11f139..aa7de40 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ -CMAKE_MINIMUM_REQUIRED(VERSION 3.20) +CMAKE_MINIMUM_REQUIRED(VERSION 3.21) PROJECT(irprinter - VERSION 0.3 + VERSION 0.4 ) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) @@ -24,3 +24,6 @@ irprinter_add_format_target(format-sources ) add_subdirectory(src) +if(PROJECT_IS_TOP_LEVEL) + add_subdirectory(test) +endif() \ No newline at end of file diff --git a/cmake/modules/target-util.cmake b/cmake/modules/target-util.cmake index 128b5c4..ecc8ced 100644 --- a/cmake/modules/target-util.cmake +++ b/cmake/modules/target-util.cmake @@ -37,3 +37,54 @@ function(irprinter_target_project_compile_definitions target) ) endif () endfunction() + +function(irprinter_find_llvm_progs target names) + cmake_parse_arguments(ARG "ABORT_IF_MISSING;SHOW_VAR" "DEFAULT_EXE" "HINTS" ${ARGN}) + set(TARGET_TMP ${target}) + + find_program( + ${target} + NAMES ${names} + PATHS ${LLVM_TOOLS_BINARY_DIR} + NO_DEFAULT_PATH + ) + if(NOT ${target}) + find_program( + ${target} + NAMES ${names} + HINTS ${ARG_HINTS} + ) + endif() + + if(NOT ${target}) + set(target_missing_message "") + if(ARG_DEFAULT_EXE) + unset(${target} CACHE) + set(${target} + ${ARG_DEFAULT_EXE} + CACHE + STRING + "Default value for ${TARGET_TMP}." + ) + set(target_missing_message "Using default: ${ARG_DEFAULT_EXE}") + endif() + + set(message_status STATUS) + if(ARG_ABORT_IF_MISSING AND NOT ARG_DEFAULT_EXE) + set(message_status SEND_ERROR) + endif() + message(${message_status} + "Did find LLVM program " "${names}" + " in ${LLVM_TOOLS_BINARY_DIR}, in system path or hints " "\"${ARG_HINTS}\"" ". " + ${target_missing_message} + ) + endif() + + set(${TARGET_TMP} "${${TARGET_TMP}}" PARENT_SCOPE) + + if(ARG_SHOW_VAR) + mark_as_advanced(CLEAR ${target}) + else() + mark_as_advanced(${target}) + endif() +endfunction() diff --git a/include/Util.h b/include/Util.h index d104624..d4ea526 100644 --- a/include/Util.h +++ b/include/Util.h @@ -133,7 +133,10 @@ inline llvm::SmallVector regex_find(const llvm::Module if (use_mangle) { return detail::find(m, [&](const llvm::Function& f) { return r.match(f.getName()); }); } else { - return detail::find(m, [&](const llvm::Function& f) { return r.match(try_demangle(f.getName())); }); + return detail::find(m, [&](const llvm::Function& f) { + std::string name = try_demangle(f.getName()); + return r.match(name); + }); } } diff --git a/src/main.cpp b/src/main.cpp index 4cdec3c..2536bf2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -121,4 +121,4 @@ int main(int argc, const char** argv) { } return 0; -} +} \ No newline at end of file diff --git a/src/printer/IRNodeFinder.cpp b/src/printer/IRNodeFinder.cpp index c9a369a..d526559 100644 --- a/src/printer/IRNodeFinder.cpp +++ b/src/printer/IRNodeFinder.cpp @@ -21,13 +21,20 @@ using namespace llvm; namespace irprinter { namespace { +std::string strip_parens(std::string name) { + if (name.size() > 2 && name.substr(name.size() - 2) == "()") { + name.erase(name.size() - 2); + } + return name; +} + template void applyToMatchingFunction(llvm::raw_ostream& os, const llvm::Module* m, const std::string& regex, F&& func) { const auto fvec = util::regex_find(*m, regex); unsigned count{0}; for (auto f : fvec) { auto fname = f->getName(); - os << "Match " << ++count << " [" << util::try_demangle(fname) << "]:"; + os << "Match " << ++count << " [" << strip_parens(util::try_demangle(fname)) << "]:"; func(f); os << "\n"; } @@ -101,7 +108,7 @@ void IRNodeFinder::listFunction(const std::string& regex) const { oss.flush(); if (f->isDeclaration()) { - os << "\n" << s.substr(1); + os << "\n" << s; } else { llvm::Regex r("((;|define)[^{]+){"); SmallVector match; @@ -114,7 +121,7 @@ void IRNodeFinder::listFunction(const std::string& regex) const { } std::string IRNodeFinder::demangle(const std::string& name) { - return util::try_demangle(name); + return strip_parens(util::try_demangle(name)); } } /* namespace irprinter */ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..2049198 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,54 @@ +set(LLVM_IR_PRINTER_BINARY $) + +irprinter_find_llvm_progs(IRPRINTER_FILECHECK_EXEC "FileCheck-${LLVM_VERSION_MAJOR};FileCheck") + +if(LLVM_EXTERNAL_LIT) + cmake_path(GET LLVM_EXTERNAL_LIT PARENT_PATH LLVM_EXTERNAL_LIT_DIR) +endif() +irprinter_find_llvm_progs(IRPRINTER_LIT_EXEC + "llvm-lit;lit;lit.py" + HINTS ${LLVM_EXTERNAL_LIT_DIR} /usr/lib/llvm-${LLVM_VERSION_MAJOR} /usr/lib/llvm /usr/bin /usr/local/bin /opt/local/bin + ABORT_IF_MISSING +) + +add_custom_target(check-irprinter + COMMAND ${IRPRINTER_LIT_EXEC} -vv ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS llvm-ir-printer${EXE_SUFFIX} + COMMENT "Running irprinter regression tests" + USES_TERMINAL +) + +if(IRPRINTER_ENABLE_COVERAGE) + set(COVERAGE_DIR ${CMAKE_BINARY_DIR}/coverage_report) + irprinter_find_llvm_progs(IRPRINTER_PROFDATA_EXEC "llvm-profdata-${LLVM_VERSION_MAJOR};llvm-profdata") + irprinter_find_llvm_progs(IRPRINTER_COV_EXEC "llvm-cov-${LLVM_VERSION_MAJOR};llvm-cov") + + set(IGNORE_REGEX ".*(test|build|build_cov|external).*") + + add_custom_target(coverage-irprinter + COMMAND ${CMAKE_COMMAND} -E make_directory ${COVERAGE_DIR} + COMMAND ${CMAKE_COMMAND} -E rm -f "${CMAKE_CURRENT_BINARY_DIR}/*.profraw" "${CMAKE_CURRENT_BINARY_DIR}/merged.profdata" + # Execute tests + COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target check-irprinter + # Merge all profraw files + COMMAND ${CMAKE_COMMAND} -E env bash -c "${IRPRINTER_PROFDATA_EXEC} merge -sparse ${CMAKE_CURRENT_BINARY_DIR}/*.profraw -o ${CMAKE_CURRENT_BINARY_DIR}/merged.profdata" + # Generate HTML + COMMAND ${IRPRINTER_COV_EXEC} show "${LLVM_IR_PRINTER_BINARY}" + "-instr-profile=${CMAKE_CURRENT_BINARY_DIR}/merged.profdata" + -format=html + "-output-dir=${COVERAGE_DIR}" + -show-line-counts-or-regions + "-ignore-filename-regex=${IGNORE_REGEX}" + # Show summary + COMMAND ${IRPRINTER_COV_EXEC} report "${LLVM_IR_PRINTER_BINARY}" + "-instr-profile=${CMAKE_CURRENT_BINARY_DIR}/merged.profdata" + "-ignore-filename-regex=${IGNORE_REGEX}" + COMMENT "Generating coverage report" + DEPENDS check-irprinter + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + VERBATIM + ) +endif() + +configure_file(lit.site.cfg.py.in lit.site.cfg.py.tmp @ONLY) +file(GENERATE OUTPUT lit.site.cfg.py INPUT ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg.py.tmp) \ No newline at end of file diff --git a/test/codes/decl.cpp b/test/codes/decl.cpp new file mode 100644 index 0000000..c371f71 --- /dev/null +++ b/test/codes/decl.cpp @@ -0,0 +1,9 @@ +// RUN: echo "l printf" | %llvm-ir-printer %s -- | %filecheck %s + +// CHECK: Match 1 [printf]: +// CHECK-NEXT: declare i32 @printf +extern "C" int printf(const char*, ...); +int main() { + printf("test"); + return 0; +} \ No newline at end of file diff --git a/test/codes/demangle.cpp b/test/codes/demangle.cpp new file mode 100644 index 0000000..25d5d14 --- /dev/null +++ b/test/codes/demangle.cpp @@ -0,0 +1,7 @@ +// RUN: echo "d _Z3foov" | %llvm-ir-printer %s -- | %filecheck %s + +// CHECK: Demangled name: foo +void foo() {} +int main() { + return 0; +} \ No newline at end of file diff --git a/test/codes/dump.cpp b/test/codes/dump.cpp new file mode 100644 index 0000000..fd37149 --- /dev/null +++ b/test/codes/dump.cpp @@ -0,0 +1,7 @@ +// RUN: echo "dump" | %llvm-ir-printer %s -- | %filecheck %s + +// CHECK: source_filename = "{{.*}}dump.cpp" +// CHECK: define {{.*}} @main +int main() { + return 0; +} diff --git a/test/codes/errors.cpp b/test/codes/errors.cpp new file mode 100644 index 0000000..7043e9c --- /dev/null +++ b/test/codes/errors.cpp @@ -0,0 +1,11 @@ +// RUN: echo "10 5" | %llvm-ir-printer %s -- | %filecheck %s --check-prefix=LOC-ORDER +// RUN: echo "5 invalid" | %llvm-ir-printer %s -- | %filecheck %s --check-prefix=LOC-PARSE +// RUN: echo "l [" | %llvm-ir-printer %s -- | %filecheck %s --check-prefix=REGEX-ERR + +// LOC-ORDER: Error: end location (5) is less than start location (10) +// LOC-PARSE: Invalid end location: invalid +// REGEX-ERR: Invalid regex ({{.*}}): "[" + +int main() { + return 0; +} diff --git a/test/codes/flags.cpp b/test/codes/flags.cpp new file mode 100644 index 0000000..b48c30b --- /dev/null +++ b/test/codes/flags.cpp @@ -0,0 +1,8 @@ +// RUN: echo -e "f -Wall\nf -O3\np main" | %llvm-ir-printer %s -- | %filecheck %s + +// CHECK: Set flag to -Wall +// CHECK: Set flag to -O3 +// CHECK: define {{.*}} @main +int main() { + return 0; +} diff --git a/test/codes/generate.cpp b/test/codes/generate.cpp new file mode 100644 index 0000000..df8518d --- /dev/null +++ b/test/codes/generate.cpp @@ -0,0 +1,6 @@ +// RUN: echo "g" | %llvm-ir-printer %s -- | %filecheck %s +// Without "-f -g" no effect, but no explicit error either. +// CHECK-NOT: Error +int main() { + return 0; +} diff --git a/test/codes/list.cpp b/test/codes/list.cpp new file mode 100644 index 0000000..4c85d5c --- /dev/null +++ b/test/codes/list.cpp @@ -0,0 +1,9 @@ +// RUN: echo "l foo" | %llvm-ir-printer %s -- | %filecheck %s + +// CHECK: _Z3foov +// CHECK-NOT: main +void foo() { +} +int main() { + return 0; +} \ No newline at end of file diff --git a/test/codes/list_all.cpp b/test/codes/list_all.cpp new file mode 100644 index 0000000..622ac2f --- /dev/null +++ b/test/codes/list_all.cpp @@ -0,0 +1,9 @@ +// RUN: echo "l" | %llvm-ir-printer %s -- | %filecheck %s + +// CHECK: Match 1 [foo] +// CHECK: Match 2 [main] +void foo() { +} +int main() { + return 0; +} \ No newline at end of file diff --git a/test/codes/location.cpp b/test/codes/location.cpp new file mode 100644 index 0000000..62afb37 --- /dev/null +++ b/test/codes/location.cpp @@ -0,0 +1,10 @@ +// RUN: echo "6 10" | %llvm-ir-printer %s -- -g | %filecheck %s + +// CHECK: main: +// CHECK: store i32 1, ptr {{.*}} +// CHECK: store i32 2, ptr {{.*}} +int main() { + int a = 1; + int b = 2; + return a + b; +} \ No newline at end of file diff --git a/test/codes/opt_flag.cpp b/test/codes/opt_flag.cpp new file mode 100644 index 0000000..2bdd39e --- /dev/null +++ b/test/codes/opt_flag.cpp @@ -0,0 +1,8 @@ +// RUN: echo -e "f -O3\np main" | %llvm-ir-printer %s -- | %filecheck %s + +// CHECK: Set flag to -O3. Re-generating module... +// CHECK: define {{.*}} @main{{.*}} +// CHECK-NOT: optnone +int main() { + return 0; +} diff --git a/test/codes/quit.cpp b/test/codes/quit.cpp new file mode 100644 index 0000000..77b9333 --- /dev/null +++ b/test/codes/quit.cpp @@ -0,0 +1,6 @@ +// RUN: echo "q" | %llvm-ir-printer %s -- | %filecheck %s + +// CHECK-NOT: ir-printer> +int main() { + return 0; +} diff --git a/test/lit.cfg.py b/test/lit.cfg.py new file mode 100644 index 0000000..27a4483 --- /dev/null +++ b/test/lit.cfg.py @@ -0,0 +1,18 @@ +import lit.formats +import os + +config.name = 'LLVM-IR-PRINTER' +config.test_format = lit.formats.ShTest(execute_external=True) + +config.suffixes = ['.cpp', '.c', '.ll'] + +config.test_source_root = os.path.dirname(__file__) + +# Substitutions +config.substitutions.append(('%llvm-ir-printer', config.llvm_ir_printer_binary)) +config.substitutions.append(('%filecheck', config.filecheck_binary)) +config.substitutions.append(('%llvm-profdata', config.llvm_profdata_exe)) +config.substitutions.append(('%llvm-cov', config.llvm_cov_exe)) + +if config.enable_coverage.upper() in ['ON', 'YES', 'TRUE', '1']: + config.environment['LLVM_PROFILE_FILE'] = os.path.join(config.test_exec_root, "test-%p.profraw") diff --git a/test/lit.site.cfg.py.in b/test/lit.site.cfg.py.in new file mode 100644 index 0000000..fefd331 --- /dev/null +++ b/test/lit.site.cfg.py.in @@ -0,0 +1,12 @@ +import os + +config.llvm_ir_printer_binary = "@LLVM_IR_PRINTER_BINARY@" +config.filecheck_binary = "@IRPRINTER_FILECHECK_EXEC@" +config.llvm_profdata_exe = "@IRPRINTER_PROFDATA_EXEC@" +config.llvm_cov_exe = "@IRPRINTER_COV_EXEC@" +config.enable_coverage = "@IRPRINTER_ENABLE_COVERAGE@" +config.test_source_root = "@CMAKE_CURRENT_SOURCE_DIR@" +config.test_exec_root = "@CMAKE_CURRENT_BINARY_DIR@" + +# Let the main config do the real work. +lit_config.load_config(config, os.path.join(config.test_source_root, "lit.cfg.py")) \ No newline at end of file