├── .gitignore ├── CMakeLists.txt ├── LICENCE ├── README.md ├── helloPass ├── CMakeLists.txt ├── Hello.cpp ├── README.md ├── run.sh.in ├── run_pass_in_clang.sh.in └── simple_prog.c ├── stripDeadFunctionsDemo ├── CMakeLists.txt ├── README.md ├── simple.c └── strip.sh.in ├── toolDemo ├── CMakeLists.txt └── tool.cpp ├── usingAnalyses ├── CMakeLists.txt ├── CycleDetect.cpp ├── README.md ├── has_cycle.c ├── no_cycle.c └── run.sh.in ├── usingIRBuilder ├── CMakeLists.txt ├── README.md ├── ReplaceGetGlobalID.cpp ├── foo.cl └── run.sh.in └── visualisationDemos ├── CMakeLists.txt ├── README.md ├── call_graph.sh.in ├── calls.c ├── control_flow_graph.sh.in ├── dominator_tree.sh.in ├── loop.c └── region_graph.sh.in /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.ll 3 | *.dot 4 | *.bc 5 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(SRG_LLVM_TUTORIAL) 2 | cmake_minimum_required(VERSION 2.8.12) 3 | 4 | # We generate shell scripts that need to use the 5 | # LOCATION property. CMake >=3.0 complains about 6 | # this so use old behaviour for now. 7 | if (CMAKE_MAJOR_VERSION GREATER 2) 8 | cmake_policy(SET CMP0026 OLD) 9 | endif() 10 | 11 | #------------------------------------------------------------------------------ 12 | # FindLLVM 13 | #------------------------------------------------------------------------------ 14 | # If building against a locally built version of LLVM (this must be built with 15 | # CMake not and not the Autoconf/Makefile build system) you need to set the 16 | # LLVM_DIR cache variable that find_package(LLVM ...) introduces. 17 | # E.g. 18 | # cmake -DLLVM_DIR:PATH=/path/to/llvm/build/share/llvm/cmake /path/to/this_projects_source_directory 19 | find_package(LLVM REQUIRED) 20 | 21 | message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") 22 | 23 | if ( "${LLVM_PACKAGE_VERSION}" VERSION_LESS "3.5" ) 24 | message(FATAL_ERROR "Need LLVM >=3.5") 25 | endif() 26 | 27 | include_directories("${LLVM_INCLUDE_DIRS}") 28 | message(STATUS "LLVM_INCLUDE_DIRS is ${LLVM_INCLUDE_DIRS}") 29 | 30 | message(STATUS "LLVM_TOOLS_BINARY_DIR is ${LLVM_TOOLS_BINARY_DIR}") 31 | 32 | #------------------------------------------------------------------------------ 33 | # Set compiler flags 34 | #------------------------------------------------------------------------------ 35 | # This unfortunately doesn't add much 36 | add_definitions(${LLVM_DEFINITIONS}) 37 | 38 | include(CheckCXXCompilerFlag) 39 | macro(add_cxx_flag flag name) 40 | CHECK_CXX_COMPILER_FLAG(${flag} "${name}_SUPPORTED") 41 | if("${name}_SUPPORTED") 42 | add_definitions(${flag}) 43 | else() 44 | message(FATAL_ERROR "${flag} flag is not supported by ${CMAKE_CXX_COMPILER}") 45 | endif() 46 | endmacro() 47 | 48 | # FIXME: Setting flags this way isn't very portable 49 | if (NOT LLVM_ENABLE_RTTI) 50 | message(STATUS "LLVM was built without RTTI, so we must disable it too for linking to work properly") 51 | add_cxx_flag(-fno-rtti RTTI) # Can't use LLVMSupport properly if we have rtti 52 | endif() 53 | add_cxx_flag(-std=c++11 CXX11) 54 | 55 | #------------------------------------------------------------------------------ 56 | # Handy macro to copy files matching a globbing pattern in the current source 57 | # source directory to the current build directory 58 | #------------------------------------------------------------------------------ 59 | macro(copy_files_to_build_dir GLOBS) 60 | file(GLOB ABSOLUTE_PATH_TO_FILES_TO_COPY ${ARGV}) 61 | 62 | foreach(file ${ABSOLUTE_PATH_TO_FILES_TO_COPY}) 63 | get_filename_component(filename ${file} NAME) 64 | configure_file(${filename} ${filename} COPYONLY) 65 | endforeach() 66 | endmacro() 67 | 68 | #------------------------------------------------------------------------------ 69 | # Warn if clang is not available 70 | #------------------------------------------------------------------------------ 71 | if (NOT EXISTS "${LLVM_TOOLS_BINARY_DIR}/clang") 72 | message(WARNING "Clang was not found in LLVM_TOOLS_BINARY_DIR. Many of the demo scripts won't work without this") 73 | endif() 74 | 75 | add_subdirectory(helloPass) 76 | 77 | add_subdirectory(visualisationDemos) 78 | add_subdirectory(stripDeadFunctionsDemo) 79 | add_subdirectory(usingAnalyses) 80 | add_subdirectory(usingIRBuilder) 81 | add_subdirectory(toolDemo) 82 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | Code taken from LLVM (helloPass/) 2 | ================================ 3 | 4 | University of Illinois/NCSA 5 | Open Source License 6 | 7 | Copyright (c) 2003-2014 University of Illinois at Urbana-Champaign. 8 | All rights reserved. 9 | 10 | Developed by: 11 | 12 | LLVM Team 13 | 14 | University of Illinois at Urbana-Champaign 15 | 16 | http://llvm.org 17 | 18 | Permission is hereby granted, free of charge, to any person obtaining a copy of 19 | this software and associated documentation files (the "Software"), to deal with 20 | the Software without restriction, including without limitation the rights to 21 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 22 | of the Software, and to permit persons to whom the Software is furnished to do 23 | so, subject to the following conditions: 24 | 25 | * Redistributions of source code must retain the above copyright notice, 26 | this list of conditions and the following disclaimers. 27 | 28 | * Redistributions in binary form must reproduce the above copyright notice, 29 | this list of conditions and the following disclaimers in the 30 | documentation and/or other materials provided with the distribution. 31 | 32 | * Neither the names of the LLVM Team, University of Illinois at 33 | Urbana-Champaign, nor the names of its contributors may be used to 34 | endorse or promote products derived from this Software without specific 35 | prior written permission. 36 | 37 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 38 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 39 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 40 | CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 41 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 42 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE 43 | SOFTWARE. 44 | 45 | Code taken from Bugle (usingAnalyses) 46 | ==================================== 47 | 48 | is under the following licence. 49 | 50 | Copyright (c) 2012 Peter Collingbourne, Alastair Donaldson 51 | 52 | All rights reserved. 53 | 54 | Permission is hereby granted, free of charge, to any person obtaining a copy of 55 | this software and associated documentation files (the "Software"), to deal with 56 | the Software without restriction, including without limitation the rights to 57 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 58 | of the Software, and to permit persons to whom the Software is furnished to do 59 | so, subject to the following conditions: 60 | 61 | * Redistributions of source code must retain the above copyright notice, 62 | this list of conditions and the following disclaimers. 63 | 64 | * Redistributions in binary form must reproduce the above copyright notice, 65 | this list of conditions and the following disclaimers in the 66 | documentation and/or other materials provided with the distribution. 67 | 68 | * The names of the contributors may not be used to endorse or promote 69 | products derived from this Software without specific prior written 70 | permission. 71 | 72 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 73 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 74 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 75 | CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 76 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 77 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE 78 | SOFTWARE. 79 | 80 | 81 | All other code 82 | ============== 83 | 84 | This is placed under the LLVM licence. 85 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | LLVM passes tutorial 2 | ==================== 3 | 4 | These are the sources used in a [tutorial](https://docs.google.com/presentation/d/1fxsbeGIwcMN5iLDLiIImKIwNwATSy1ilDuTOPDBqw_k/) given to the [Software Reliability 5 | Group at Imperial College London](http://srg.doc.ic.ac.uk). 6 | 7 | To get started run... 8 | 9 | Install or build LLVM 3.5 with the corresponding clang and compiler-rt 10 | and then run the following. 11 | 12 | ``` 13 | $ cd /some/path 14 | $ git clone git://github.com/delcypher/srg-llvm-pass-tutorial.git src/ 15 | $ mkdir build 16 | $ cd build 17 | $ cmake-gui ../src 18 | $ make 19 | ``` 20 | 21 | You may need to specify the ``LLVM_DIR`` variable to CMake so it knows 22 | where to find the LLVM CMake modules. This should be a directory to the 23 | folder containing ``LLVMConfig.cmake``. Typically this is something like 24 | ``/path/to/llvm/build/share/llvm/cmake/``. You can specify this on the command-line 25 | instead of using ``cmake-gui`` or ``ccmake`` like so... 26 | 27 | ``` 28 | $ cmake -DLLVM_DIR:STRING=/path/to/llvm/build/share/llvm/cmake/ ../src 29 | ``` 30 | -------------------------------------------------------------------------------- /helloPass/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | copy_files_to_build_dir(*.c *.md) 2 | add_library(SRGHello MODULE Hello.cpp) 3 | 4 | get_property(MODULE_FILE TARGET SRGHello PROPERTY LOCATION) 5 | configure_file(run.sh.in run.sh @ONLY) 6 | configure_file(run_pass_in_clang.sh.in run_pass_in_clang.sh @ONLY) 7 | -------------------------------------------------------------------------------- /helloPass/Hello.cpp: -------------------------------------------------------------------------------- 1 | //===- Hello.cpp - Example code from "Writing an LLVM Pass" ---------------===// 2 | // 3 | // The LLVM Compiler Infrastructure 4 | // 5 | // This file is distributed under the University of Illinois Open Source 6 | // License. See LICENSE.TXT for details. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | // 10 | // This file implements two versions of the LLVM "Hello World" pass described 11 | // in docs/WritingAnLLVMPass.html 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | #include "llvm/ADT/Statistic.h" 16 | #include "llvm/IR/Function.h" 17 | #include "llvm/Pass.h" 18 | #include "llvm/PassManager.h" 19 | #include "llvm/Transforms/IPO/PassManagerBuilder.h" 20 | #include "llvm/PassRegistry.h" 21 | #include "llvm/Support/raw_ostream.h" 22 | using namespace llvm; 23 | 24 | #define DEBUG_TYPE "hello" 25 | 26 | STATISTIC(HelloCounter, "Counts number of functions greeted"); 27 | 28 | namespace { 29 | // Hello - The first implementation, without getAnalysisUsage. 30 | struct Hello : public FunctionPass { 31 | static char ID; // Pass identification, replacement for typeid 32 | Hello() : FunctionPass(ID) {} 33 | 34 | bool runOnFunction(Function &F) override { 35 | ++HelloCounter; 36 | errs() << "Hello: "; 37 | errs().write_escaped(F.getName()) << '\n'; 38 | return false; 39 | } 40 | }; 41 | } 42 | 43 | char Hello::ID = 0; 44 | static RegisterPass X("hello", "Hello World Pass"); 45 | 46 | namespace { 47 | // Hello2 - The second implementation with getAnalysisUsage implemented. 48 | struct Hello2 : public FunctionPass { 49 | static char ID; // Pass identification, replacement for typeid 50 | Hello2() : FunctionPass(ID) {} 51 | 52 | bool runOnFunction(Function &F) override { 53 | ++HelloCounter; 54 | errs() << "Hello: "; 55 | errs().write_escaped(F.getName()) << '\n'; 56 | return false; 57 | } 58 | 59 | // We don't modify the program, so we preserve all analyses. 60 | void getAnalysisUsage(AnalysisUsage &AU) const override { 61 | AU.setPreservesAll(); 62 | } 63 | }; 64 | } 65 | 66 | char Hello2::ID = 0; 67 | static RegisterPass 68 | Y("hello2", "Hello World Pass (with getAnalysisUsage implemented)"); 69 | 70 | // Register our pass with the Pass registry so it can be used directly 71 | // as a clang plug-in and run during compilation. 72 | // 73 | // This is a very handy tip from https://homes.cs.washington.edu/~asampson/blog/clangpass.html 74 | static void registerHello2Pass(const PassManagerBuilder&, PassManagerBase &PM) 75 | { 76 | PM.add(new Hello2()); 77 | } 78 | static RegisterStandardPasses Z(PassManagerBuilder::EP_EarlyAsPossible, registerHello2Pass); 79 | -------------------------------------------------------------------------------- /helloPass/README.md: -------------------------------------------------------------------------------- 1 | Hello pass 2 | ========== 3 | 4 | This is simply the Hello and Hello2 passes taken with minor modification from the LLVM 5 | source tree. The code here really just serves to demonstrate building 6 | an "out of tree" pass and using it as a plugin to opt. 7 | 8 | To try it out make sure you've built and then run 9 | 10 | ``` 11 | $ run.sh 12 | ``` 13 | 14 | DO NOT RUN ``run.sh.in`` that won't work! 15 | 16 | Running the Hello pass in clang 17 | =============================== 18 | 19 | It is possible to run the Hello2 pass in clang directly by loading it as a 20 | plug-in in clang and setting up the necessary code in Hello.cpp to register the 21 | pass with the pass registry. 22 | 23 | To try this out run 24 | 25 | ``` 26 | $ run_pass_in_clang.sh 27 | ``` 28 | -------------------------------------------------------------------------------- /helloPass/run.sh.in: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function msg() 4 | { 5 | echo -e "\033[32m${1}\033[0m" 6 | } 7 | 8 | @LLVM_TOOLS_BINARY_DIR@/clang -emit-llvm -c simple_prog.c 9 | 10 | msg "Run just hello" 11 | @LLVM_TOOLS_BINARY_DIR@/opt -load @MODULE_FILE@ -hello -stats simple_prog.bc > /dev/null 12 | 13 | msg "Run just hello2" 14 | @LLVM_TOOLS_BINARY_DIR@/opt -load @MODULE_FILE@ -hello2 -stats simple_prog.bc > /dev/null 15 | 16 | msg "Run both hello and hello2" 17 | @LLVM_TOOLS_BINARY_DIR@/opt -load @MODULE_FILE@ -hello -hello2 -stats simple_prog.bc > /dev/null 18 | echo 19 | 20 | msg "Run both hello and hello2 and show pass order before running" 21 | @LLVM_TOOLS_BINARY_DIR@/opt -load @MODULE_FILE@ -debug-pass=Structure -hello -hello2 -stats simple_prog.bc > /dev/null 22 | 23 | msg "Run both hello and hello2 and show timings" 24 | @LLVM_TOOLS_BINARY_DIR@/opt -load @MODULE_FILE@ -time-passes -hello -hello2 -stats simple_prog.bc > /dev/null 25 | -------------------------------------------------------------------------------- /helloPass/run_pass_in_clang.sh.in: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash -x 2 | @LLVM_TOOLS_BINARY_DIR@/clang -Xclang -load -Xclang @MODULE_FILE@ -c simple_prog.c -o simple_prog 3 | -------------------------------------------------------------------------------- /helloPass/simple_prog.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int foo() 4 | { 5 | return 0; 6 | } 7 | 8 | int bar() 9 | { 10 | return 1; 11 | } 12 | 13 | int main() 14 | { 15 | printf("Hello world!\n"); 16 | int x = foo(); 17 | int y = bar(); 18 | return x + y; 19 | } 20 | -------------------------------------------------------------------------------- /stripDeadFunctionsDemo/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | copy_files_to_build_dir(*.c *.md) 2 | configure_file(strip.sh.in strip.sh @ONLY) 3 | -------------------------------------------------------------------------------- /stripDeadFunctionsDemo/README.md: -------------------------------------------------------------------------------- 1 | Stripping dead functions 2 | ======================== 3 | 4 | This gives a small demo of stripping dead functions from a bitcode module. 5 | 6 | First we run the internalize sets the [linkage type](http://llvm.org/docs/LangRef.html#linkage-types) 7 | of to every function except main to ``internal``. This tells LLVM that these functions cannot be 8 | called externally which means if ``main()`` does not call them then they will never be called. 9 | 10 | Second we run the GlobalDCE pass (global dead code elimination) which will remove the dead 11 | functions for us. 12 | 13 | In the ``simple.c`` program the function ``foo()`` is clearly a dead function and by running 14 | the necessary passes it is removed. 15 | -------------------------------------------------------------------------------- /stripDeadFunctionsDemo/simple.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void foo() 4 | { 5 | printf("I'm foo\n"); 6 | } 7 | int main() 8 | { 9 | printf("I'm main\n"); 10 | return 0; 11 | } 12 | -------------------------------------------------------------------------------- /stripDeadFunctionsDemo/strip.sh.in: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | @LLVM_TOOLS_BINARY_DIR@/clang -S -emit-llvm simple.c 3 | @LLVM_TOOLS_BINARY_DIR@/opt -S -print-before-all -print-after-all -internalize \ 4 | -internalize-public-api-list=main \ 5 | -globaldce simple.ll 2>&1 | less 6 | -------------------------------------------------------------------------------- /toolDemo/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(tool tool.cpp) 2 | 3 | llvm_map_components_to_libnames(llvm_libs support core irreader) 4 | target_link_libraries(tool ${llvm_libs}) 5 | -------------------------------------------------------------------------------- /toolDemo/tool.cpp: -------------------------------------------------------------------------------- 1 | // vim: set sw=2 ts=2 expandtab softtabstop=2: 2 | #include "llvm/IR/LLVMContext.h" 3 | #include "llvm/IRReader/IRReader.h" 4 | #include "llvm/IR/Verifier.h" 5 | #include "llvm/AsmParser/Parser.h" 6 | #include "llvm/IR/Module.h" 7 | #include "llvm/PassManager.h" 8 | #include "llvm/Support/CommandLine.h" 9 | #include "llvm/Support/ManagedStatic.h" 10 | #include "llvm/Support/PrettyStackTrace.h" 11 | #include "llvm/Support/raw_ostream.h" 12 | #include "llvm/Support/Signals.h" 13 | #include "llvm/Support/SourceMgr.h" 14 | #include "llvm/Support/SystemUtils.h" 15 | #include 16 | 17 | using namespace llvm; 18 | 19 | static cl::opt 20 | InputFilename(cl::Positional, cl::desc(""), cl::init("-")); 21 | static cl::opt 22 | DisableVerify("disable-verify", cl::Hidden, 23 | cl::desc("Do not run verifier on input LLVM (dangerous!)")); 24 | 25 | 26 | int main(int argc, char **argv) { 27 | // Print a stack trace if we signal out. 28 | sys::PrintStackTraceOnErrorSignal(); 29 | PrettyStackTraceProgram X(argc, argv); 30 | LLVMContext &Context = getGlobalContext(); 31 | llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. 32 | cl::ParseCommandLineOptions(argc, argv, "Demo tool\n"); 33 | 34 | // Parse the file now... 35 | // Guess the file type from extension 36 | std::unique_ptr M; 37 | SMDiagnostic Err; 38 | if (StringRef(InputFilename).endswith(".ll")) { 39 | // Parse as LLVM Assembly 40 | M.reset(ParseAssemblyFile(InputFilename, Err, Context)); 41 | if (M.get() == 0) { 42 | Err.print(argv[0], errs(), /*showColors=*/ true); 43 | return 1; 44 | } 45 | } 46 | else if (StringRef(InputFilename).endswith(".bc")) { 47 | // Parse as LLVM bitcode 48 | M.reset(ParseIRFile(InputFilename, Err, Context)); 49 | if (M.get() == 0) { 50 | Err.print(argv[0], errs(), /*showColors=*/ true); 51 | return 1; 52 | } 53 | } 54 | else { 55 | errs() << "Input file has unsupported extension\n"; 56 | return 1; 57 | } 58 | 59 | if (!DisableVerify) { 60 | std::string Err; 61 | if (verifyModule(*M.get(), &errs() )) { 62 | errs() << "Assembly parsed, but does not verify a correct\n"; 63 | return 1; 64 | } 65 | } 66 | 67 | errs() << "Here's the assembly:\n" << *M.get(); 68 | 69 | return 0; 70 | } 71 | 72 | -------------------------------------------------------------------------------- /usingAnalyses/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | copy_files_to_build_dir(*.c *.md) 2 | add_library(CycleDetect MODULE CycleDetect.cpp) 3 | 4 | get_property(MODULE_FILE TARGET CycleDetect PROPERTY LOCATION) 5 | configure_file(run.sh.in run.sh @ONLY) 6 | -------------------------------------------------------------------------------- /usingAnalyses/CycleDetect.cpp: -------------------------------------------------------------------------------- 1 | /* This code is taken from [Bugle](https://github.com/mc-imperial/bugle) 2 | * with a few modifications. See LICENCE 3 | */ 4 | #include "llvm/Pass.h" 5 | #include "llvm/ADT/SCCIterator.h" 6 | #include "llvm/Analysis/CallGraph.h" 7 | #include "llvm/Support/raw_ostream.h" 8 | 9 | using namespace llvm; 10 | 11 | namespace { 12 | struct CycleDetectPass : public ModulePass { 13 | 14 | static char ID; 15 | CycleDetectPass() : ModulePass(ID) { } 16 | 17 | bool runOnModule(llvm::Module &M) override { 18 | CallGraph &CG = getAnalysis().getCallGraph(); 19 | scc_iterator i = scc_begin(&CG), e = scc_end(&CG); 20 | bool hasCycle = false; 21 | while (i != e) { 22 | if (i.hasLoop()) { 23 | errs().changeColor(raw_ostream::RED, /*bold=*/true, /*bg=*/false); 24 | errs() << "A cycle was detected in the call graph!\n"; 25 | hasCycle = true; 26 | 27 | auto nodes = *i; 28 | errs() << "The following functions were involved in the cycle\n\n"; 29 | errs().changeColor(raw_ostream::RED, /*bold=*/false, /*bg=*/false); 30 | for (CallGraphNode* node : nodes) 31 | node->print(errs()); 32 | 33 | errs().resetColor(); 34 | } 35 | ++i; 36 | } 37 | 38 | if (!hasCycle) { 39 | errs().changeColor(raw_ostream::GREEN, /*bold=*/true, /*bg=*/false); 40 | errs() << "No cycle was detected in the call graph\n"; 41 | errs().resetColor(); 42 | } 43 | 44 | return false; 45 | } 46 | 47 | virtual const char *getPassName() const { 48 | return "CallGraph cycle detection"; 49 | } 50 | 51 | virtual void getAnalysisUsage(AnalysisUsage &AU) const override { 52 | AU.setPreservesAll(); 53 | 54 | // We are explicitly stating that we require this analysis 55 | AU.addRequired(); 56 | } 57 | }; 58 | } 59 | 60 | char CycleDetectPass::ID = 0; 61 | static RegisterPass X("cycle-detect", 62 | "Detect if there is a cycle in the call graph"); 63 | 64 | -------------------------------------------------------------------------------- /usingAnalyses/README.md: -------------------------------------------------------------------------------- 1 | Cycle detect pass 2 | ================= 3 | 4 | This pass is an example of a pass that uses an existing analysis 5 | to compute something. This example is taken from the [Bugle](https://github.com/mc-imperial/bugle) tool 6 | with a few minor modifications. 7 | 8 | To try it run 9 | 10 | ``` 11 | $ run.sh 12 | ``` 13 | -------------------------------------------------------------------------------- /usingAnalyses/has_cycle.c: -------------------------------------------------------------------------------- 1 | 2 | int foo(unsigned int x) 3 | { 4 | if (x > 0) 5 | return x* foo(x -1); 6 | else 7 | return 1; 8 | } 9 | 10 | int main() 11 | { 12 | return foo(5); 13 | } 14 | -------------------------------------------------------------------------------- /usingAnalyses/no_cycle.c: -------------------------------------------------------------------------------- 1 | int foo(unsigned int x) 2 | { 3 | unsigned int temp=1; 4 | for (int i=2; i <=x ; ++i) 5 | temp *= i; 6 | 7 | return temp; 8 | } 9 | 10 | int main() 11 | { 12 | return foo(5); 13 | } 14 | -------------------------------------------------------------------------------- /usingAnalyses/run.sh.in: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function msg() 4 | { 5 | echo -e "\033[32m${1}\033[0m" 6 | } 7 | 8 | msg "Running on has_cycle.c" 9 | @LLVM_TOOLS_BINARY_DIR@/clang -emit-llvm -c has_cycle.c 10 | @LLVM_TOOLS_BINARY_DIR@/opt -load @MODULE_FILE@ -cycle-detect -debug-pass=Structure has_cycle.bc > /dev/null 11 | 12 | msg "Running on no_cycle.c" 13 | @LLVM_TOOLS_BINARY_DIR@/clang -emit-llvm -c no_cycle.c 14 | @LLVM_TOOLS_BINARY_DIR@/opt -load @MODULE_FILE@ -cycle-detect -debug-pass=Structure no_cycle.bc > /dev/null 15 | -------------------------------------------------------------------------------- /usingIRBuilder/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | copy_files_to_build_dir(*.cl *.md) 2 | add_library(ReplaceGetGlobalID MODULE ReplaceGetGlobalID.cpp) 3 | 4 | get_property(MODULE_FILE TARGET ReplaceGetGlobalID PROPERTY LOCATION) 5 | configure_file(run.sh.in run.sh @ONLY) 6 | -------------------------------------------------------------------------------- /usingIRBuilder/README.md: -------------------------------------------------------------------------------- 1 | Using the IRBuilder 2 | =================== 3 | 4 | This is an example pass that uses the [IRBuilder](http://llvm.org/docs/ProgrammersManual.html#creating-and-inserting-new-instructions) to 5 | inject instructions that replace calls to ``get_global_id(x)`` 6 | with 7 | 8 | ``` 9 | get_local_id(x) + get_group_id(x)*get_local_size(x) 10 | ``` 11 | 12 | To try this out run 13 | 14 | ``` 15 | $ run.sh 16 | ``` 17 | -------------------------------------------------------------------------------- /usingIRBuilder/ReplaceGetGlobalID.cpp: -------------------------------------------------------------------------------- 1 | // vim: set sw=2 ts=2 expandtab softtabstop=2: 2 | #include "llvm/IR/Instructions.h" 3 | #include "llvm/IR/IRBuilder.h" 4 | #include "llvm/IR/Module.h" 5 | #include "llvm/Support/raw_ostream.h" 6 | #include 7 | #include 8 | 9 | using namespace llvm; 10 | 11 | 12 | namespace { 13 | struct ReplaceGetGlobalIDPass : public ModulePass { 14 | static char ID; 15 | ReplaceGetGlobalIDPass() : ModulePass(ID) { } 16 | 17 | bool runOnModule(Module& M) override { 18 | Function* get_global_idF = M.getFunction("get_global_id"); 19 | 20 | if (get_global_idF == 0) { 21 | // The function isn't used anywhere 22 | return false; 23 | } 24 | 25 | 26 | std::vector foundCalls; 27 | for (Module::iterator FI = M.begin(), FE =M.end(); FI != FE; ++FI) 28 | for (Function::iterator BB = FI->begin(), BBE=FI->end(); BB != BBE; ++BB) 29 | for (BasicBlock::iterator i = BB->begin(), e = BB->end(); i!=e; ++i) { 30 | if (CallInst* CI = dyn_cast(i)) { 31 | if ( CI->getCalledFunction() == get_global_idF ) { 32 | errs() << "Found :" << get_global_idF->getName() << "\n"; 33 | // We can't do the modification here because we will 34 | // invalidate the instruction iterator when removing the call to get_global_id() 35 | foundCalls.push_back(CI); 36 | } 37 | } 38 | } 39 | 40 | if (foundCalls.size() == 0) { 41 | // get_global_id() is never called. 42 | return false; 43 | } 44 | 45 | // get_global_id(x) === get_local_size(x)*get_group_id(x) + get_local_id(x) 46 | Function* get_local_sizeF = GetWorkItemFunction(M, "get_local_size"); 47 | Function* get_group_idF = GetWorkItemFunction(M, "get_group_id"); 48 | Function* get_local_idF = GetWorkItemFunction(M, "get_local_id"); 49 | 50 | for(std::vector::iterator CI = foundCalls.begin(), CIE = foundCalls.end(); CI != CIE; ++CI) { 51 | // Get dimension argument 52 | assert((*CI)->getNumArgOperands() == 1 && "get_global_id is called with the wrong number of arguments"); 53 | Value* dimension = (*CI)->getArgOperand(0); 54 | 55 | // Inject our instructions 56 | // %rpl.ggi.0. = call i32 @get_group_id(i32 0) 57 | // %rpl.ggi.1. = call i32 @get_local_size(i32 0) 58 | // %rpl.ggi.2. = mul i32 %rpl.ggi.0., %rpl.ggi.1. 59 | // %rpl.ggi.3. = call i32 @get_local_id(i32 0) 60 | // %rpl.ggi.result. = add i32 %rpl.ggi.2., %rpl.ggi.3. 61 | IRBuilder<> Builder(*CI); 62 | CallInst* ggiCall = Builder.CreateCall(get_group_idF, dimension, "rpl.ggi.0."); 63 | CallInst* glsCall = Builder.CreateCall(get_local_sizeF, dimension, "rpl.ggi.1."); 64 | Value* mul = Builder.CreateMul(ggiCall, glsCall, "rpl.ggi.2."); 65 | CallInst* gliCall = Builder.CreateCall(get_local_idF, dimension, "rpl.ggi.3."); 66 | Value* result = Builder.CreateAdd(mul, gliCall,"rpl.ggi.result."); 67 | 68 | // Replace all uses with 69 | (*CI)->replaceAllUsesWith(result); 70 | (*CI)->eraseFromParent(); // Finally we can remove the CallInst to get_global_id() 71 | } 72 | 73 | get_global_idF->eraseFromParent(); 74 | return true; 75 | } 76 | 77 | Function* GetWorkItemFunction(Module& M, StringRef functionName) { 78 | // Easy-path: The function is already in the module 79 | Constant* function = M.getFunction(functionName); 80 | if ( function && isa(function)) 81 | return cast(function); 82 | 83 | // Harder-path: The function isn't already in the 84 | // module so we need to insert it 85 | 86 | // Are these types right for 64-bit? 87 | errs() << "Warning: " << functionName << "() was not in the module, guessing types and inserting\n"; 88 | function = M.getOrInsertFunction(functionName, 89 | Type::getInt32Ty(getGlobalContext()), 90 | Type::getInt32Ty(getGlobalContext()), 91 | 0 92 | ); 93 | assert( (function != 0) && "Could not insert function"); 94 | assert( isa(function) && "FIXME: We don't want a bitcasted function"); 95 | return cast(function); 96 | } 97 | 98 | }; 99 | } 100 | 101 | char ReplaceGetGlobalIDPass::ID = 0; 102 | static RegisterPass X("replace-get-global-id", 103 | "Replace all calls to get-global-id with something equivalent"); 104 | 105 | -------------------------------------------------------------------------------- /usingIRBuilder/foo.cl: -------------------------------------------------------------------------------- 1 | // HACK. You don't normal declare built-ins in OpenCL 2 | // but to make things easier for clang I'll declare them here 3 | int get_global_id(int); 4 | 5 | void __kernel foo(__global int* A, int x) 6 | { 7 | A[ get_global_id(0)] = 1 + get_global_id(x); 8 | } 9 | -------------------------------------------------------------------------------- /usingIRBuilder/run.sh.in: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | @LLVM_TOOLS_BINARY_DIR@/clang -c -emit-llvm -Xclang -cl-std=CL1.2 -fno-builtin -g foo.cl 3 | 4 | @LLVM_TOOLS_BINARY_DIR@/opt -S -load @MODULE_FILE@ -replace-get-global-id -debug-pass=Structure -print-after-all -print-before-all foo.bc 2>&1 | less 5 | -------------------------------------------------------------------------------- /visualisationDemos/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | copy_files_to_build_dir(*.c *.md) 2 | 3 | file(GLOB shell_scripts "*.sh.in") 4 | foreach (file ${shell_scripts}) 5 | get_filename_component(script_name "${file}" NAME_WE) 6 | configure_file("${script_name}.sh.in" "${script_name}.sh" @ONLY) 7 | endforeach() 8 | -------------------------------------------------------------------------------- /visualisationDemos/README.md: -------------------------------------------------------------------------------- 1 | Graphs from LLVM bitcode 2 | ======================== 3 | 4 | The ``opt`` tool provides handy flags for generating graphs (graphviz .dot format). 5 | The scripts in this directory demonstrate some of the graphs that can be generated. 6 | 7 | Make sure you have [xdotpy](https://github.com/jrfonseca/xdot.py) installed first. 8 | You can probably find it in your distribution's repositories. 9 | -------------------------------------------------------------------------------- /visualisationDemos/call_graph.sh.in: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | @LLVM_TOOLS_BINARY_DIR@/clang -S -emit-llvm calls.c 3 | 4 | # You can use this instead if LLVM was compiled with xdot in PATH 5 | # @LLVM_TOOLS_BINARY_DIR@/opt -view-callgraph calls.ll > /dev/null 6 | 7 | # Otherwise this will work 8 | @LLVM_TOOLS_BINARY_DIR@/opt -dot-callgraph calls.ll > /dev/null 9 | xdot callgraph.dot 10 | -------------------------------------------------------------------------------- /visualisationDemos/calls.c: -------------------------------------------------------------------------------- 1 | extern int baz(); 2 | 3 | int bar() 4 | { 5 | baz(); 6 | return 4; 7 | } 8 | 9 | int foo() 10 | { 11 | return bar(); 12 | } 13 | 14 | int fac(unsigned int N) 15 | { 16 | if ( N < 2 ) 17 | return 1; 18 | else 19 | return N * fac(N-1); 20 | } 21 | 22 | int main() 23 | { 24 | foo(); 25 | 26 | fac(5); 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /visualisationDemos/control_flow_graph.sh.in: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | @LLVM_TOOLS_BINARY_DIR@/clang -S -emit-llvm loop.c 3 | 4 | # You can use this instead if LLVM was compiled with xdot in PATH 5 | # @LLVM_TOOLS_BINARY_DIR@/opt -view-cfg loop.ll > /dev/null 6 | 7 | # Otherwise this will work 8 | @LLVM_TOOLS_BINARY_DIR@/opt -dot-cfg loop.ll > /dev/null 9 | xdot cfg.main.dot 10 | -------------------------------------------------------------------------------- /visualisationDemos/dominator_tree.sh.in: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | @LLVM_TOOLS_BINARY_DIR@/clang -S -emit-llvm loop.c 3 | 4 | # You can use this instead if LLVM was compiled with xdot in PATH 5 | # opt -view-dom loop.ll > /dev/null 6 | 7 | # Otherwise this will work 8 | @LLVM_TOOLS_BINARY_DIR@/opt -dot-dom loop.ll > /dev/null 9 | xdot dom.main.dot 10 | -------------------------------------------------------------------------------- /visualisationDemos/loop.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() 4 | { 5 | for (int index=0; index < 5; ++index) 6 | printf("I'm main %d\n", index); 7 | 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /visualisationDemos/region_graph.sh.in: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | @LLVM_TOOLS_BINARY_DIR@/clang -S -emit-llvm loop.c 3 | 4 | # You can use this instead if LLVM was compiled with xdot in PATH 5 | # opt -view-regions loop.ll > /dev/null 6 | 7 | # Otherwise this will work 8 | @LLVM_TOOLS_BINARY_DIR@/opt -dot-regions loop.ll > /dev/null 9 | xdot reg.main.dot 10 | --------------------------------------------------------------------------------