├── .gitignore ├── COPYING ├── FAQ ├── INSTALL ├── Makefile.am ├── README ├── configure.ac ├── lib ├── Makefile.am ├── boolector-1.5.116-eeaf10b-121004.tar.gz ├── lingeling-al6-080d45d-120922.tar.gz └── lit │ ├── lit.py │ ├── lit │ ├── LitConfig.py │ ├── LitFormats.py │ ├── LitTestCase.py │ ├── ProgressBar.py │ ├── ShCommands.py │ ├── ShUtil.py │ ├── TclUtil.py │ ├── Test.py │ ├── TestFormats.py │ ├── TestRunner.py │ ├── TestingConfig.py │ ├── Util.py │ ├── __init__.py │ └── main.py │ └── setup.py ├── src ├── AntiAlgebra.cc ├── AntiDCE.cc ├── AntiFunctionPass.cc ├── AntiFunctionPass.h ├── AntiSimplify.cc ├── BugOn.cc ├── BugOn.h ├── BugOnAlias.cc ├── BugOnAssert.cc ├── BugOnBounds.cc ├── BugOnFree.cc ├── BugOnGep.cc ├── BugOnInt.cc ├── BugOnLibc.cc ├── BugOnLinux.cc ├── BugOnLoop.cc ├── BugOnNull.cc ├── BugOnUndef.cc ├── Diagnostic.cc ├── Diagnostic.h ├── ElimAssert.cc ├── GlobalTimeout.cc ├── IgnoreLoopInitial.cc ├── InlineOnly.cc ├── IntAction.cc ├── LoadElim.cc ├── LoopPrepare.cc ├── Makefile.am ├── PHIRange.cc ├── PathGen.cc ├── PathGen.h ├── SMTBoolector.cc ├── SMTLIB.cc ├── SMTSolver.cc ├── SMTSolver.h ├── SMTSonolar.cc ├── SMTZ3.cc ├── SimplifyDelete.cc ├── ValueGen.cc ├── ValueGen.h ├── ncpu ├── optck └── poptck └── test ├── Makefile.am ├── arm-linux-gnueabi-g++ ├── arm-linux-gnueabi-gcc ├── cc1 ├── diagdiff ├── gcc ├── lit.cfg.in ├── opt ├── add100.c ├── alias.c ├── dead-loop.c ├── delete.cc ├── ext4shl.c ├── gpgdiv.c ├── memcpy.c ├── pqdiv.c ├── ptr-2008-1685.c ├── realloc-null.c ├── shlnsw.c ├── use-after-free.c ├── winner1.c └── winner2.c └── stack-build /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.pyc 3 | .DS_Store 4 | Makefile.in 5 | /aclocal.m4 6 | /autom4te.cache 7 | /build* 8 | /config.h.in 9 | /config.h 10 | /config.log 11 | /config.status 12 | /configure 13 | /m4 14 | /libtool 15 | /stamp-h1 16 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | STACK is released under the MIT license. 2 | 3 | http://opensource.org/licenses/MIT 4 | 5 | However, by default STACK links to the Boolector solver, which is 6 | released under GPLv3. In that case, STACK is under GPL as well. 7 | 8 | If you want to keep the MIT license for STACK, use another solver. 9 | See the INSTALL file for details. 10 | -------------------------------------------------------------------------------- /FAQ: -------------------------------------------------------------------------------- 1 | Building Problems 2 | ----------------- 3 | 4 | * Cannot find llvm-config. 5 | 6 | You need to build your own llvm, as instructed in the INSTALL file. 7 | 8 | 9 | * "fatal error: llvm/Analysis/CFG.h: No such file or directory" 10 | 11 | As detailed in the INSTALL file, you need the development version of llvm. 12 | 3.3 doesn't work. 13 | 14 | 15 | * "cc1plus: error unrecognized command line option -std=c++11" 16 | 17 | You need a decent C++ compiler that supports C++11. 18 | 19 | 20 | Bug Reports 21 | ----------- 22 | 23 | * A lot of false positives (null pointer dereference) from C++ delete. 24 | 25 | Use the clang++ you build, as instructed in the INSTALL file, and 26 | you shouldn't see such false positives. STACK relies on information 27 | from that custom built clang++ to recognize delete and avoid false 28 | positives. 29 | 30 | 31 | * "LLVM ERROR: Bad DataLayout ctor used." 32 | 33 | This happens if clang fails to compile your source code. Either 34 | ignore such messages, or fix your code for clang. 35 | 36 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | Prerequisites 2 | ------------- 3 | 4 | To build STACK from source code, you need to build Clang and LLVM 5 | version 3.4. Using pre-built versions of Clang/LLVM is not recommended, 6 | because Stack relies on specific Clang/LLVM build options to help reduce 7 | false warnings. 8 | 9 | Roughly, you should do the following: 10 | 11 | wget http://llvm.org/releases/3.4/llvm-3.4.src.tar.gz \ 12 | http://llvm.org/releases/3.4/clang-3.4.src.tar.gz \ 13 | http://llvm.org/releases/3.4/clang-tools-extra-3.4.src.tar.gz \ 14 | http://llvm.org/releases/3.4/compiler-rt-3.4.src.tar.gz 15 | tar zxf llvm-3.4.src.tar.gz 16 | tar zxf clang-3.4.src.tar.gz -C llvm-3.4/tools 17 | mv llvm-3.4/tools/clang{-3.4,} 18 | tar zxf clang-tools-extra-3.4.src.tar.gz -C llvm-3.4/tools/clang/tools 19 | mv llvm-3.4/tools/clang/tools/{clang-tools-extra-3.4,extra} 20 | tar zxf compiler-rt-3.4.src.tar.gz -C llvm-3.4/projects 21 | mv llvm-3.4/projects/compiler-rt{-3.4,} 22 | 23 | mkdir build && cd build 24 | ../llvm-3.4/configure --enable-cxx11 --enable-targets=host \ 25 | --enable-bindings=none --enable-shared \ 26 | --enable-debug-symbols --enable-optimized 27 | make 28 | make install 29 | 30 | You need a compiler that supports C++11, such as gcc 4.7 or later. 31 | 32 | If you prefer to install Clang and LLVM in a directory other than 33 | /usr/local, also pass --prefix=... to its configure, and add that 34 | directory to your PATH. 35 | 36 | This will install Clang and LLVM. To use them, prepend their bin 37 | directory to PATH. 38 | 39 | This roughly follows the build instructions from this page, except 40 | using Clang/LLVM 3.4 rather than checking out the code from svn: 41 | 42 | http://clang.llvm.org/get_started.html 43 | 44 | 45 | STACK 46 | ----- 47 | 48 | You need to choose an SMT solver for constraint solving. The default 49 | one is Boolector, which is released under GPL. If you need a more 50 | liberal license, see "Use other solvers". 51 | 52 | Build STACK in its source root directory. 53 | 54 | If building from git, first do: 55 | 56 | $ autoreconf -fvi 57 | 58 | Then configure and make. 59 | 60 | $ mkdir build 61 | $ cd build 62 | $ ../configure 63 | $ make 64 | 65 | Finally, add `/build/bin` to PATH. 66 | 67 | 68 | Use other solvers 69 | ----------------- 70 | 71 | By default, STACK links to the Boolector solver v1.5.116 [1], which 72 | is released under GPLv3. If you want to use the MIT license, choose 73 | other solvers. 74 | 75 | One option is the STP solver [2], which uses the MIT license. 76 | Download and build STP, and you should get the executable `stp`. 77 | Then, add the following parameter to STACK's configure: 78 | 79 | --with-smtlib="path/to/stp --SMTLIB2" 80 | 81 | [1] Boolector. http://fmv.jku.at/boolector/ 82 | [2] STP. https://sites.google.com/site/stpfastprover/ 83 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = lib src test 2 | 3 | EXTRA_DIST = COPYING INSTALL README 4 | 5 | intck_bin_distdir = intck-$(PACKAGE_VERSION)-bin 6 | 7 | intck-bin: all html 8 | rm -rf $(intck_bin_distdir) 9 | $(MKDIR_P) $(intck_bin_distdir)/bin 10 | cp bin/* $(intck_bin_distdir)/bin/ 11 | $(MKDIR_P) $(intck_bin_distdir)/lib 12 | cp lib/*.so $(intck_bin_distdir)/lib/ 13 | cp `which opt` $(intck_bin_distdir)/bin/ 14 | cp `llvm-config --libdir`/libLLVM-`llvm-config --version`* $(intck_bin_distdir)/lib/ 15 | $(MKDIR_P) $(intck_bin_distdir)/doc 16 | cp doc/*.html $(intck_bin_distdir)/doc/ 17 | tar chvjf $(intck_bin_distdir).tar.bz2 $(intck_bin_distdir) 18 | rm -rf $(intck_bin_distdir) 19 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | See INSTALL for build instructions. Make sure STACK binaries are 2 | in the PATH. 3 | 4 | 5 | Preparation 6 | ----------- 7 | 8 | STACK works on LLVM bitcode. To analyze a software project, the 9 | first step is to generate LLVM bitcode. STACK provides a script 10 | called `stack-build`, which both calls gcc (or g++) and in parallel 11 | uses Clang to obtain LLVM bitcode from your source code, stored in 12 | .ll files. For example: 13 | 14 | $ cd /path/to/your/project 15 | $ stack-build ./configure 16 | $ stack-build make 17 | 18 | or if analyzing a project that is configured using CMake: 19 | 20 | $ cd /path/to/your/project 21 | $ stack-build cmake 22 | $ stack-build make 23 | 24 | 25 | Unstable code checker 26 | --------------------- 27 | 28 | To find unstable code that can be eliminated due to undefined behavior, 29 | simply run the following command in the project directory after building 30 | LLVM bitcode: 31 | 32 | $ poptck 33 | 34 | You can find bug reports in `pstack.txt`, in the YAML format. 35 | 36 | Here's one example: 37 | 38 | bug: anti-simplify 39 | model: | 40 | %tobool = icmp ne i8* %p, null, !dbg !14 41 | --> true 42 | stack: 43 | - p.c:4:0 44 | ncore: 1 45 | core: 46 | - p.c:3:0 47 | - null pointer dereference 48 | 49 | This means the null pointer check at line 4 ("stack:") may be simplified 50 | into true ("model:") due to the pointer dereference at line 3 ("core:"). 51 | 52 | 53 | Contact 54 | ------- 55 | 56 | If you find any bugs in STACK, feel free to contact us: you can send 57 | us email at int@pdos.csail.mit.edu. 58 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # -*- Autoconf -*- 2 | # Process this file with autoconf to produce a configure script. 3 | 4 | AC_INIT([stack],[0.3],[int@pdos.csail.mit.edu]) 5 | AC_CONFIG_AUX_DIR([build-aux]) 6 | AC_CONFIG_HEADERS([config.h]) 7 | 8 | AM_INIT_AUTOMAKE([foreign -Wall -Werror]) 9 | m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) 10 | 11 | # Checks for programs. 12 | AM_PROG_CC_C_O 13 | AC_PROG_CXX 14 | m4_ifdef([AM_PROG_AR],[AM_PROG_AR]) 15 | AC_PROG_LN_S 16 | AC_PROG_MKDIR_P 17 | AC_PROG_SED 18 | 19 | LT_INIT([disable-static pic-only]) 20 | AC_PROG_LIBTOOL 21 | 22 | # Checks for timer 23 | AC_SEARCH_LIBS( 24 | [timer_create],[rt], 25 | AC_DEFINE([HAVE_TIMER],[1],[Define to 1 if you have the per-process timer.]) 26 | ) 27 | 28 | # Checks for SMT solvers. 29 | AC_MSG_CHECKING([for SMT solver]) 30 | AM_CONDITIONAL([HAVE_BOOLECTOR],[true]) 31 | AM_CONDITIONAL([HAVE_SMTLIB],[false]) 32 | 33 | AC_ARG_WITH([smtlib], 34 | [ AS_HELP_STRING([--with-smtlib=PATH],[use an SMT-LIB v2 compatible executable for constraint solving]) ], 35 | [ case "$withval" in 36 | yes|no) 37 | AC_MSG_ERROR([path to SMT solver not specified]) 38 | ;; 39 | *) 40 | AC_MSG_RESULT([$withval]) 41 | ;; 42 | esac 43 | AC_DEFINE_UNQUOTED([SMTLIB],["$withval"],[path to SMT solver]) 44 | AM_CONDITIONAL([HAVE_BOOLECTOR],[false]) 45 | AM_CONDITIONAL([HAVE_SMTLIB],[true]) 46 | ], 47 | [ AC_MSG_RESULT([Boolector (GPLv3)]) ] 48 | ) 49 | 50 | AC_CONFIG_FILES([ 51 | Makefile 52 | lib/Makefile 53 | src/Makefile 54 | test/Makefile 55 | test/lit.cfg 56 | ]) 57 | AC_CONFIG_LINKS([ 58 | bin/stack/gcc:test/gcc 59 | bin/stack/g++:test/gcc 60 | bin/stack/cc:test/gcc 61 | bin/stack/c++:test/gcc 62 | bin/stack/cc1:test/cc1 63 | bin/stack/clang:test/gcc 64 | bin/stack/clang++:test/gcc 65 | bin/stack/arm-linux-gnueabi-gcc:test/arm-linux-gnueabi-gcc 66 | bin/stack/arm-linux-gnueabi-g++:test/arm-linux-gnueabi-g++ 67 | bin/stack-build:test/stack-build 68 | bin/ncpu:src/ncpu 69 | bin/optck:src/optck 70 | bin/poptck:src/poptck 71 | ]) 72 | AC_OUTPUT 73 | -------------------------------------------------------------------------------- /lib/Makefile.am: -------------------------------------------------------------------------------- 1 | BOOLECTOR = boolector-1.5.116-eeaf10b-121004 2 | LINGELING = lingeling-al6-080d45d-120922 3 | 4 | EXTRA_DIST = $(BOOLECTOR).tar.gz $(LINGELING).tar.gz 5 | 6 | if HAVE_BOOLECTOR 7 | all-local: libboolector.a 8 | endif 9 | 10 | libboolector.a: $(BOOLECTOR).tar.gz liblgl.a 11 | tar xzf $< 12 | $(LN_S) -f $(BOOLECTOR) boolector 13 | $(SED) -i -e "s/CFLAGS=/CFLAGS=-fPIC /" boolector/makefile.in 14 | cd boolector && ./configure && $(MAKE) 15 | $(LN_S) -f boolector/libboolector.a 16 | 17 | liblgl.a: $(LINGELING).tar.gz 18 | tar xzf $< 19 | $(LN_S) -f $(LINGELING) lingeling 20 | $(SED) -i -e 's/CFLAGS=/CFLAGS=-fPIC /' lingeling/makefile.in 21 | $(SED) -i -e 's/stdlib\.h/stdint.h/' lingeling/lglib.h 22 | cd lingeling && ./configure && $(MAKE) liblgl.a 23 | $(LN_S) -f lingeling/liblgl.a 24 | -------------------------------------------------------------------------------- /lib/boolector-1.5.116-eeaf10b-121004.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiw/stack/60370c0025022988d497a177348373f838bb8525/lib/boolector-1.5.116-eeaf10b-121004.tar.gz -------------------------------------------------------------------------------- /lib/lingeling-al6-080d45d-120922.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiw/stack/60370c0025022988d497a177348373f838bb8525/lib/lingeling-al6-080d45d-120922.tar.gz -------------------------------------------------------------------------------- /lib/lit/lit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | if __name__=='__main__': 4 | import lit 5 | lit.main() 6 | -------------------------------------------------------------------------------- /lib/lit/lit/LitConfig.py: -------------------------------------------------------------------------------- 1 | class LitConfig: 2 | """LitConfig - Configuration data for a 'lit' test runner instance, shared 3 | across all tests. 4 | 5 | The LitConfig object is also used to communicate with client configuration 6 | files, it is always passed in as the global variable 'lit' so that 7 | configuration files can access common functionality and internal components 8 | easily. 9 | """ 10 | 11 | # Provide access to Test module. 12 | import Test 13 | 14 | # Provide access to built-in formats. 15 | import LitFormats as formats 16 | 17 | # Provide access to built-in utility functions. 18 | import Util as util 19 | 20 | def __init__(self, progname, path, quiet, 21 | useValgrind, valgrindLeakCheck, valgrindArgs, 22 | useTclAsSh, 23 | noExecute, ignoreStdErr, debug, isWindows, 24 | params): 25 | # The name of the test runner. 26 | self.progname = progname 27 | # The items to add to the PATH environment variable. 28 | self.path = list(map(str, path)) 29 | self.quiet = bool(quiet) 30 | self.useValgrind = bool(useValgrind) 31 | self.valgrindLeakCheck = bool(valgrindLeakCheck) 32 | self.valgrindUserArgs = list(valgrindArgs) 33 | self.useTclAsSh = bool(useTclAsSh) 34 | self.noExecute = noExecute 35 | self.ignoreStdErr = ignoreStdErr 36 | self.debug = debug 37 | self.isWindows = bool(isWindows) 38 | self.params = dict(params) 39 | self.bashPath = None 40 | 41 | self.numErrors = 0 42 | self.numWarnings = 0 43 | 44 | self.valgrindArgs = [] 45 | if self.useValgrind: 46 | self.valgrindArgs = ['valgrind', '-q', '--run-libc-freeres=no', 47 | '--tool=memcheck', '--trace-children=yes', 48 | '--error-exitcode=123'] 49 | if self.valgrindLeakCheck: 50 | self.valgrindArgs.append('--leak-check=full') 51 | else: 52 | # The default is 'summary'. 53 | self.valgrindArgs.append('--leak-check=no') 54 | self.valgrindArgs.extend(self.valgrindUserArgs) 55 | 56 | 57 | def load_config(self, config, path): 58 | """load_config(config, path) - Load a config object from an alternate 59 | path.""" 60 | from TestingConfig import TestingConfig 61 | if self.debug: 62 | self.note('load_config from %r' % path) 63 | return TestingConfig.frompath(path, config.parent, self, 64 | mustExist = True, 65 | config = config) 66 | 67 | def getBashPath(self): 68 | """getBashPath - Get the path to 'bash'""" 69 | import os, Util 70 | 71 | if self.bashPath is not None: 72 | return self.bashPath 73 | 74 | self.bashPath = Util.which('bash', os.pathsep.join(self.path)) 75 | if self.bashPath is None: 76 | # Check some known paths. 77 | for path in ('/bin/bash', '/usr/bin/bash', '/usr/local/bin/bash'): 78 | if os.path.exists(path): 79 | self.bashPath = path 80 | break 81 | 82 | if self.bashPath is None: 83 | self.warning("Unable to find 'bash', running Tcl tests internally.") 84 | self.bashPath = '' 85 | 86 | return self.bashPath 87 | 88 | def getToolsPath(self, dir, paths, tools): 89 | import os, Util 90 | if dir is not None and os.path.isabs(dir) and os.path.isdir(dir): 91 | if not Util.checkToolsPath(dir, tools): 92 | return None 93 | else: 94 | dir = Util.whichTools(tools, paths) 95 | 96 | # bash 97 | self.bashPath = Util.which('bash', dir) 98 | if self.bashPath is None: 99 | self.note("Unable to find 'bash.exe'.") 100 | self.bashPath = '' 101 | 102 | return dir 103 | 104 | def _write_message(self, kind, message): 105 | import inspect, os, sys 106 | 107 | # Get the file/line where this message was generated. 108 | f = inspect.currentframe() 109 | # Step out of _write_message, and then out of wrapper. 110 | f = f.f_back.f_back 111 | file,line,_,_,_ = inspect.getframeinfo(f) 112 | location = '%s:%d' % (os.path.basename(file), line) 113 | 114 | print >>sys.stderr, '%s: %s: %s: %s' % (self.progname, location, 115 | kind, message) 116 | 117 | def note(self, message): 118 | self._write_message('note', message) 119 | 120 | def warning(self, message): 121 | self._write_message('warning', message) 122 | self.numWarnings += 1 123 | 124 | def error(self, message): 125 | self._write_message('error', message) 126 | self.numErrors += 1 127 | 128 | def fatal(self, message): 129 | import sys 130 | self._write_message('fatal', message) 131 | sys.exit(2) 132 | -------------------------------------------------------------------------------- /lib/lit/lit/LitFormats.py: -------------------------------------------------------------------------------- 1 | from TestFormats import FileBasedTest 2 | from TestFormats import GoogleTest, ShTest, TclTest 3 | from TestFormats import SyntaxCheckTest, OneCommandPerFileTest 4 | -------------------------------------------------------------------------------- /lib/lit/lit/LitTestCase.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import Test 3 | 4 | """ 5 | TestCase adaptor for providing a 'unittest' compatible interface to 'lit' tests. 6 | """ 7 | 8 | class UnresolvedError(RuntimeError): 9 | pass 10 | 11 | class LitTestCase(unittest.TestCase): 12 | def __init__(self, test, lit_config): 13 | unittest.TestCase.__init__(self) 14 | self._test = test 15 | self._lit_config = lit_config 16 | 17 | def id(self): 18 | return self._test.getFullName() 19 | 20 | def shortDescription(self): 21 | return self._test.getFullName() 22 | 23 | def runTest(self): 24 | tr, output = self._test.config.test_format.execute( 25 | self._test, self._lit_config) 26 | 27 | if tr is Test.UNRESOLVED: 28 | raise UnresolvedError(output) 29 | elif tr.isFailure: 30 | self.fail(output) 31 | -------------------------------------------------------------------------------- /lib/lit/lit/ProgressBar.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Source: http://code.activestate.com/recipes/475116/, with 4 | # modifications by Daniel Dunbar. 5 | 6 | import sys, re, time 7 | 8 | class TerminalController: 9 | """ 10 | A class that can be used to portably generate formatted output to 11 | a terminal. 12 | 13 | `TerminalController` defines a set of instance variables whose 14 | values are initialized to the control sequence necessary to 15 | perform a given action. These can be simply included in normal 16 | output to the terminal: 17 | 18 | >>> term = TerminalController() 19 | >>> print 'This is '+term.GREEN+'green'+term.NORMAL 20 | 21 | Alternatively, the `render()` method can used, which replaces 22 | '${action}' with the string required to perform 'action': 23 | 24 | >>> term = TerminalController() 25 | >>> print term.render('This is ${GREEN}green${NORMAL}') 26 | 27 | If the terminal doesn't support a given action, then the value of 28 | the corresponding instance variable will be set to ''. As a 29 | result, the above code will still work on terminals that do not 30 | support color, except that their output will not be colored. 31 | Also, this means that you can test whether the terminal supports a 32 | given action by simply testing the truth value of the 33 | corresponding instance variable: 34 | 35 | >>> term = TerminalController() 36 | >>> if term.CLEAR_SCREEN: 37 | ... print 'This terminal supports clearning the screen.' 38 | 39 | Finally, if the width and height of the terminal are known, then 40 | they will be stored in the `COLS` and `LINES` attributes. 41 | """ 42 | # Cursor movement: 43 | BOL = '' #: Move the cursor to the beginning of the line 44 | UP = '' #: Move the cursor up one line 45 | DOWN = '' #: Move the cursor down one line 46 | LEFT = '' #: Move the cursor left one char 47 | RIGHT = '' #: Move the cursor right one char 48 | 49 | # Deletion: 50 | CLEAR_SCREEN = '' #: Clear the screen and move to home position 51 | CLEAR_EOL = '' #: Clear to the end of the line. 52 | CLEAR_BOL = '' #: Clear to the beginning of the line. 53 | CLEAR_EOS = '' #: Clear to the end of the screen 54 | 55 | # Output modes: 56 | BOLD = '' #: Turn on bold mode 57 | BLINK = '' #: Turn on blink mode 58 | DIM = '' #: Turn on half-bright mode 59 | REVERSE = '' #: Turn on reverse-video mode 60 | NORMAL = '' #: Turn off all modes 61 | 62 | # Cursor display: 63 | HIDE_CURSOR = '' #: Make the cursor invisible 64 | SHOW_CURSOR = '' #: Make the cursor visible 65 | 66 | # Terminal size: 67 | COLS = None #: Width of the terminal (None for unknown) 68 | LINES = None #: Height of the terminal (None for unknown) 69 | 70 | # Foreground colors: 71 | BLACK = BLUE = GREEN = CYAN = RED = MAGENTA = YELLOW = WHITE = '' 72 | 73 | # Background colors: 74 | BG_BLACK = BG_BLUE = BG_GREEN = BG_CYAN = '' 75 | BG_RED = BG_MAGENTA = BG_YELLOW = BG_WHITE = '' 76 | 77 | _STRING_CAPABILITIES = """ 78 | BOL=cr UP=cuu1 DOWN=cud1 LEFT=cub1 RIGHT=cuf1 79 | CLEAR_SCREEN=clear CLEAR_EOL=el CLEAR_BOL=el1 CLEAR_EOS=ed BOLD=bold 80 | BLINK=blink DIM=dim REVERSE=rev UNDERLINE=smul NORMAL=sgr0 81 | HIDE_CURSOR=cinvis SHOW_CURSOR=cnorm""".split() 82 | _COLORS = """BLACK BLUE GREEN CYAN RED MAGENTA YELLOW WHITE""".split() 83 | _ANSICOLORS = "BLACK RED GREEN YELLOW BLUE MAGENTA CYAN WHITE".split() 84 | 85 | def __init__(self, term_stream=sys.stdout): 86 | """ 87 | Create a `TerminalController` and initialize its attributes 88 | with appropriate values for the current terminal. 89 | `term_stream` is the stream that will be used for terminal 90 | output; if this stream is not a tty, then the terminal is 91 | assumed to be a dumb terminal (i.e., have no capabilities). 92 | """ 93 | # Curses isn't available on all platforms 94 | try: import curses 95 | except: return 96 | 97 | # If the stream isn't a tty, then assume it has no capabilities. 98 | if not term_stream.isatty(): return 99 | 100 | # Check the terminal type. If we fail, then assume that the 101 | # terminal has no capabilities. 102 | try: curses.setupterm() 103 | except: return 104 | 105 | # Look up numeric capabilities. 106 | self.COLS = curses.tigetnum('cols') 107 | self.LINES = curses.tigetnum('lines') 108 | self.XN = curses.tigetflag('xenl') 109 | 110 | # Look up string capabilities. 111 | for capability in self._STRING_CAPABILITIES: 112 | (attrib, cap_name) = capability.split('=') 113 | setattr(self, attrib, self._tigetstr(cap_name) or '') 114 | 115 | # Colors 116 | set_fg = self._tigetstr('setf') 117 | if set_fg: 118 | for i,color in zip(range(len(self._COLORS)), self._COLORS): 119 | setattr(self, color, curses.tparm(set_fg, i) or '') 120 | set_fg_ansi = self._tigetstr('setaf') 121 | if set_fg_ansi: 122 | for i,color in zip(range(len(self._ANSICOLORS)), self._ANSICOLORS): 123 | setattr(self, color, curses.tparm(set_fg_ansi, i) or '') 124 | set_bg = self._tigetstr('setb') 125 | if set_bg: 126 | for i,color in zip(range(len(self._COLORS)), self._COLORS): 127 | setattr(self, 'BG_'+color, curses.tparm(set_bg, i) or '') 128 | set_bg_ansi = self._tigetstr('setab') 129 | if set_bg_ansi: 130 | for i,color in zip(range(len(self._ANSICOLORS)), self._ANSICOLORS): 131 | setattr(self, 'BG_'+color, curses.tparm(set_bg_ansi, i) or '') 132 | 133 | def _tigetstr(self, cap_name): 134 | # String capabilities can include "delays" of the form "$<2>". 135 | # For any modern terminal, we should be able to just ignore 136 | # these, so strip them out. 137 | import curses 138 | cap = curses.tigetstr(cap_name) or '' 139 | return re.sub(r'\$<\d+>[/*]?', '', cap) 140 | 141 | def render(self, template): 142 | """ 143 | Replace each $-substitutions in the given template string with 144 | the corresponding terminal control string (if it's defined) or 145 | '' (if it's not). 146 | """ 147 | return re.sub(r'\$\$|\${\w+}', self._render_sub, template) 148 | 149 | def _render_sub(self, match): 150 | s = match.group() 151 | if s == '$$': return s 152 | else: return getattr(self, s[2:-1]) 153 | 154 | ####################################################################### 155 | # Example use case: progress bar 156 | ####################################################################### 157 | 158 | class SimpleProgressBar: 159 | """ 160 | A simple progress bar which doesn't need any terminal support. 161 | 162 | This prints out a progress bar like: 163 | 'Header: 0 .. 10.. 20.. ...' 164 | """ 165 | 166 | def __init__(self, header): 167 | self.header = header 168 | self.atIndex = None 169 | 170 | def update(self, percent, message): 171 | if self.atIndex is None: 172 | sys.stdout.write(self.header) 173 | self.atIndex = 0 174 | 175 | next = int(percent*50) 176 | if next == self.atIndex: 177 | return 178 | 179 | for i in range(self.atIndex, next): 180 | idx = i % 5 181 | if idx == 0: 182 | sys.stdout.write('%-2d' % (i*2)) 183 | elif idx == 1: 184 | pass # Skip second char 185 | elif idx < 4: 186 | sys.stdout.write('.') 187 | else: 188 | sys.stdout.write(' ') 189 | sys.stdout.flush() 190 | self.atIndex = next 191 | 192 | def clear(self): 193 | if self.atIndex is not None: 194 | sys.stdout.write('\n') 195 | sys.stdout.flush() 196 | self.atIndex = None 197 | 198 | class ProgressBar: 199 | """ 200 | A 3-line progress bar, which looks like:: 201 | 202 | Header 203 | 20% [===========----------------------------------] 204 | progress message 205 | 206 | The progress bar is colored, if the terminal supports color 207 | output; and adjusts to the width of the terminal. 208 | """ 209 | BAR = '%s${GREEN}[${BOLD}%s%s${NORMAL}${GREEN}]${NORMAL}%s' 210 | HEADER = '${BOLD}${CYAN}%s${NORMAL}\n\n' 211 | 212 | def __init__(self, term, header, useETA=True): 213 | self.term = term 214 | if not (self.term.CLEAR_EOL and self.term.UP and self.term.BOL): 215 | raise ValueError("Terminal isn't capable enough -- you " 216 | "should use a simpler progress dispaly.") 217 | self.BOL = self.term.BOL # BoL from col#79 218 | self.XNL = "\n" # Newline from col#79 219 | if self.term.COLS: 220 | self.width = self.term.COLS 221 | if not self.term.XN: 222 | self.BOL = self.term.UP + self.term.BOL 223 | self.XNL = "" # Cursor must be fed to the next line 224 | else: 225 | self.width = 75 226 | self.bar = term.render(self.BAR) 227 | self.header = self.term.render(self.HEADER % header.center(self.width)) 228 | self.cleared = 1 #: true if we haven't drawn the bar yet. 229 | self.useETA = useETA 230 | if self.useETA: 231 | self.startTime = time.time() 232 | self.update(0, '') 233 | 234 | def update(self, percent, message): 235 | if self.cleared: 236 | sys.stdout.write(self.header) 237 | self.cleared = 0 238 | prefix = '%3d%% ' % (percent*100,) 239 | suffix = '' 240 | if self.useETA: 241 | elapsed = time.time() - self.startTime 242 | if percent > .0001 and elapsed > 1: 243 | total = elapsed / percent 244 | eta = int(total - elapsed) 245 | h = eta//3600. 246 | m = (eta//60) % 60 247 | s = eta % 60 248 | suffix = ' ETA: %02d:%02d:%02d'%(h,m,s) 249 | barWidth = self.width - len(prefix) - len(suffix) - 2 250 | n = int(barWidth*percent) 251 | if len(message) < self.width: 252 | message = message + ' '*(self.width - len(message)) 253 | else: 254 | message = '... ' + message[-(self.width-4):] 255 | sys.stdout.write( 256 | self.BOL + self.term.UP + self.term.CLEAR_EOL + 257 | (self.bar % (prefix, '='*n, '-'*(barWidth-n), suffix)) + 258 | self.XNL + 259 | self.term.CLEAR_EOL + message) 260 | if not self.term.XN: 261 | sys.stdout.flush() 262 | 263 | def clear(self): 264 | if not self.cleared: 265 | sys.stdout.write(self.BOL + self.term.CLEAR_EOL + 266 | self.term.UP + self.term.CLEAR_EOL + 267 | self.term.UP + self.term.CLEAR_EOL) 268 | sys.stdout.flush() 269 | self.cleared = 1 270 | 271 | def test(): 272 | import time 273 | tc = TerminalController() 274 | p = ProgressBar(tc, 'Tests') 275 | for i in range(101): 276 | p.update(i/100., str(i)) 277 | time.sleep(.3) 278 | 279 | if __name__=='__main__': 280 | test() 281 | -------------------------------------------------------------------------------- /lib/lit/lit/ShCommands.py: -------------------------------------------------------------------------------- 1 | class Command: 2 | def __init__(self, args, redirects): 3 | self.args = list(args) 4 | self.redirects = list(redirects) 5 | 6 | def __repr__(self): 7 | return 'Command(%r, %r)' % (self.args, self.redirects) 8 | 9 | def __cmp__(self, other): 10 | if not isinstance(other, Command): 11 | return -1 12 | 13 | return cmp((self.args, self.redirects), 14 | (other.args, other.redirects)) 15 | 16 | def toShell(self, file): 17 | for arg in self.args: 18 | if "'" not in arg: 19 | quoted = "'%s'" % arg 20 | elif '"' not in arg and '$' not in arg: 21 | quoted = '"%s"' % arg 22 | else: 23 | raise NotImplementedError,'Unable to quote %r' % arg 24 | print >>file, quoted, 25 | 26 | # For debugging / validation. 27 | import ShUtil 28 | dequoted = list(ShUtil.ShLexer(quoted).lex()) 29 | if dequoted != [arg]: 30 | raise NotImplementedError,'Unable to quote %r' % arg 31 | 32 | for r in self.redirects: 33 | if len(r[0]) == 1: 34 | print >>file, "%s '%s'" % (r[0][0], r[1]), 35 | else: 36 | print >>file, "%s%s '%s'" % (r[0][1], r[0][0], r[1]), 37 | 38 | class Pipeline: 39 | def __init__(self, commands, negate=False, pipe_err=False): 40 | self.commands = commands 41 | self.negate = negate 42 | self.pipe_err = pipe_err 43 | 44 | def __repr__(self): 45 | return 'Pipeline(%r, %r, %r)' % (self.commands, self.negate, 46 | self.pipe_err) 47 | 48 | def __cmp__(self, other): 49 | if not isinstance(other, Pipeline): 50 | return -1 51 | 52 | return cmp((self.commands, self.negate, self.pipe_err), 53 | (other.commands, other.negate, self.pipe_err)) 54 | 55 | def toShell(self, file, pipefail=False): 56 | if pipefail != self.pipe_err: 57 | raise ValueError,'Inconsistent "pipefail" attribute!' 58 | if self.negate: 59 | print >>file, '!', 60 | for cmd in self.commands: 61 | cmd.toShell(file) 62 | if cmd is not self.commands[-1]: 63 | print >>file, '|\n ', 64 | 65 | class Seq: 66 | def __init__(self, lhs, op, rhs): 67 | assert op in (';', '&', '||', '&&') 68 | self.op = op 69 | self.lhs = lhs 70 | self.rhs = rhs 71 | 72 | def __repr__(self): 73 | return 'Seq(%r, %r, %r)' % (self.lhs, self.op, self.rhs) 74 | 75 | def __cmp__(self, other): 76 | if not isinstance(other, Seq): 77 | return -1 78 | 79 | return cmp((self.lhs, self.op, self.rhs), 80 | (other.lhs, other.op, other.rhs)) 81 | 82 | def toShell(self, file, pipefail=False): 83 | self.lhs.toShell(file, pipefail) 84 | print >>file, ' %s\n' % self.op 85 | self.rhs.toShell(file, pipefail) 86 | -------------------------------------------------------------------------------- /lib/lit/lit/Test.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | # Test results. 4 | 5 | class TestResult: 6 | def __init__(self, name, isFailure): 7 | self.name = name 8 | self.isFailure = isFailure 9 | 10 | PASS = TestResult('PASS', False) 11 | XFAIL = TestResult('XFAIL', False) 12 | FAIL = TestResult('FAIL', True) 13 | XPASS = TestResult('XPASS', True) 14 | UNRESOLVED = TestResult('UNRESOLVED', True) 15 | UNSUPPORTED = TestResult('UNSUPPORTED', False) 16 | 17 | # Test classes. 18 | 19 | class TestFormat: 20 | """TestFormat - Test information provider.""" 21 | 22 | def __init__(self, name): 23 | self.name = name 24 | 25 | class TestSuite: 26 | """TestSuite - Information on a group of tests. 27 | 28 | A test suite groups together a set of logically related tests. 29 | """ 30 | 31 | def __init__(self, name, source_root, exec_root, config): 32 | self.name = name 33 | self.source_root = source_root 34 | self.exec_root = exec_root 35 | # The test suite configuration. 36 | self.config = config 37 | 38 | def getSourcePath(self, components): 39 | return os.path.join(self.source_root, *components) 40 | 41 | def getExecPath(self, components): 42 | return os.path.join(self.exec_root, *components) 43 | 44 | class Test: 45 | """Test - Information on a single test instance.""" 46 | 47 | def __init__(self, suite, path_in_suite, config): 48 | self.suite = suite 49 | self.path_in_suite = path_in_suite 50 | self.config = config 51 | # The test result code, once complete. 52 | self.result = None 53 | # Any additional output from the test, once complete. 54 | self.output = None 55 | # The wall time to execute this test, if timing and once complete. 56 | self.elapsed = None 57 | # The repeat index of this test, or None. 58 | self.index = None 59 | 60 | def copyWithIndex(self, index): 61 | import copy 62 | res = copy.copy(self) 63 | res.index = index 64 | return res 65 | 66 | def setResult(self, result, output, elapsed): 67 | assert self.result is None, "Test result already set!" 68 | self.result = result 69 | self.output = output 70 | self.elapsed = elapsed 71 | 72 | def getFullName(self): 73 | return self.suite.config.name + ' :: ' + '/'.join(self.path_in_suite) 74 | 75 | def getSourcePath(self): 76 | return self.suite.getSourcePath(self.path_in_suite) 77 | 78 | def getExecPath(self): 79 | return self.suite.getExecPath(self.path_in_suite) 80 | -------------------------------------------------------------------------------- /lib/lit/lit/TestFormats.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | import Test 5 | import TestRunner 6 | import Util 7 | 8 | kIsWindows = sys.platform in ['win32', 'cygwin'] 9 | 10 | class GoogleTest(object): 11 | def __init__(self, test_sub_dir, test_suffix): 12 | self.test_sub_dir = os.path.normcase(str(test_sub_dir)).split(';') 13 | self.test_suffix = str(test_suffix) 14 | 15 | # On Windows, assume tests will also end in '.exe'. 16 | if kIsWindows: 17 | self.test_suffix += '.exe' 18 | 19 | def getGTestTests(self, path, litConfig, localConfig): 20 | """getGTestTests(path) - [name] 21 | 22 | Return the tests available in gtest executable. 23 | 24 | Args: 25 | path: String path to a gtest executable 26 | litConfig: LitConfig instance 27 | localConfig: TestingConfig instance""" 28 | 29 | try: 30 | lines = Util.capture([path, '--gtest_list_tests'], 31 | env=localConfig.environment) 32 | if kIsWindows: 33 | lines = lines.replace('\r', '') 34 | lines = lines.split('\n') 35 | except: 36 | litConfig.error("unable to discover google-tests in %r" % path) 37 | raise StopIteration 38 | 39 | nested_tests = [] 40 | for ln in lines: 41 | if not ln.strip(): 42 | continue 43 | 44 | prefix = '' 45 | index = 0 46 | while ln[index*2:index*2+2] == ' ': 47 | index += 1 48 | while len(nested_tests) > index: 49 | nested_tests.pop() 50 | 51 | ln = ln[index*2:] 52 | if ln.endswith('.'): 53 | nested_tests.append(ln) 54 | else: 55 | yield ''.join(nested_tests) + ln 56 | 57 | def getTestsInDirectory(self, testSuite, path_in_suite, 58 | litConfig, localConfig): 59 | source_path = testSuite.getSourcePath(path_in_suite) 60 | for filename in os.listdir(source_path): 61 | # Check for the one subdirectory (build directory) tests will be in. 62 | if not '.' in self.test_sub_dir: 63 | if not os.path.normcase(filename) in self.test_sub_dir: 64 | continue 65 | 66 | filepath = os.path.join(source_path, filename) 67 | if not os.path.isdir(filepath): 68 | continue 69 | 70 | for subfilename in os.listdir(filepath): 71 | if subfilename.endswith(self.test_suffix): 72 | execpath = os.path.join(filepath, subfilename) 73 | 74 | # Discover the tests in this executable. 75 | for name in self.getGTestTests(execpath, litConfig, 76 | localConfig): 77 | testPath = path_in_suite + (filename, subfilename, name) 78 | yield Test.Test(testSuite, testPath, localConfig) 79 | 80 | def execute(self, test, litConfig): 81 | testPath,testName = os.path.split(test.getSourcePath()) 82 | while not os.path.exists(testPath): 83 | # Handle GTest parametrized and typed tests, whose name includes 84 | # some '/'s. 85 | testPath, namePrefix = os.path.split(testPath) 86 | testName = os.path.join(namePrefix, testName) 87 | 88 | cmd = [testPath, '--gtest_filter=' + testName] 89 | if litConfig.useValgrind: 90 | cmd = litConfig.valgrindArgs + cmd 91 | 92 | out, err, exitCode = TestRunner.executeCommand( 93 | cmd, env=test.config.environment) 94 | 95 | if not exitCode: 96 | return Test.PASS,'' 97 | 98 | return Test.FAIL, out + err 99 | 100 | ### 101 | 102 | class FileBasedTest(object): 103 | def getTestsInDirectory(self, testSuite, path_in_suite, 104 | litConfig, localConfig): 105 | source_path = testSuite.getSourcePath(path_in_suite) 106 | for filename in os.listdir(source_path): 107 | # Ignore dot files and excluded tests. 108 | if (filename.startswith('.') or 109 | filename in localConfig.excludes): 110 | continue 111 | 112 | filepath = os.path.join(source_path, filename) 113 | if not os.path.isdir(filepath): 114 | base,ext = os.path.splitext(filename) 115 | if ext in localConfig.suffixes: 116 | yield Test.Test(testSuite, path_in_suite + (filename,), 117 | localConfig) 118 | 119 | class ShTest(FileBasedTest): 120 | def __init__(self, execute_external = False): 121 | self.execute_external = execute_external 122 | 123 | def execute(self, test, litConfig): 124 | return TestRunner.executeShTest(test, litConfig, 125 | self.execute_external) 126 | 127 | class TclTest(FileBasedTest): 128 | def __init__(self, ignoreStdErr=False): 129 | self.ignoreStdErr = ignoreStdErr 130 | 131 | def execute(self, test, litConfig): 132 | litConfig.ignoreStdErr = self.ignoreStdErr 133 | return TestRunner.executeTclTest(test, litConfig) 134 | 135 | ### 136 | 137 | import re 138 | import tempfile 139 | 140 | class OneCommandPerFileTest: 141 | # FIXME: Refactor into generic test for running some command on a directory 142 | # of inputs. 143 | 144 | def __init__(self, command, dir, recursive=False, 145 | pattern=".*", useTempInput=False): 146 | if isinstance(command, str): 147 | self.command = [command] 148 | else: 149 | self.command = list(command) 150 | if dir is not None: 151 | dir = str(dir) 152 | self.dir = dir 153 | self.recursive = bool(recursive) 154 | self.pattern = re.compile(pattern) 155 | self.useTempInput = useTempInput 156 | 157 | def getTestsInDirectory(self, testSuite, path_in_suite, 158 | litConfig, localConfig): 159 | dir = self.dir 160 | if dir is None: 161 | dir = testSuite.getSourcePath(path_in_suite) 162 | 163 | for dirname,subdirs,filenames in os.walk(dir): 164 | if not self.recursive: 165 | subdirs[:] = [] 166 | 167 | subdirs[:] = [d for d in subdirs 168 | if (d != '.svn' and 169 | d not in localConfig.excludes)] 170 | 171 | for filename in filenames: 172 | if (filename.startswith('.') or 173 | not self.pattern.match(filename) or 174 | filename in localConfig.excludes): 175 | continue 176 | 177 | path = os.path.join(dirname,filename) 178 | suffix = path[len(dir):] 179 | if suffix.startswith(os.sep): 180 | suffix = suffix[1:] 181 | test = Test.Test(testSuite, 182 | path_in_suite + tuple(suffix.split(os.sep)), 183 | localConfig) 184 | # FIXME: Hack? 185 | test.source_path = path 186 | yield test 187 | 188 | def createTempInput(self, tmp, test): 189 | abstract 190 | 191 | def execute(self, test, litConfig): 192 | if test.config.unsupported: 193 | return (Test.UNSUPPORTED, 'Test is unsupported') 194 | 195 | cmd = list(self.command) 196 | 197 | # If using temp input, create a temporary file and hand it to the 198 | # subclass. 199 | if self.useTempInput: 200 | tmp = tempfile.NamedTemporaryFile(suffix='.cpp') 201 | self.createTempInput(tmp, test) 202 | tmp.flush() 203 | cmd.append(tmp.name) 204 | elif hasattr(test, 'source_path'): 205 | cmd.append(test.source_path) 206 | else: 207 | cmd.append(test.getSourcePath()) 208 | 209 | out, err, exitCode = TestRunner.executeCommand(cmd) 210 | 211 | diags = out + err 212 | if not exitCode and not diags.strip(): 213 | return Test.PASS,'' 214 | 215 | # Try to include some useful information. 216 | report = """Command: %s\n""" % ' '.join(["'%s'" % a 217 | for a in cmd]) 218 | if self.useTempInput: 219 | report += """Temporary File: %s\n""" % tmp.name 220 | report += "--\n%s--\n""" % open(tmp.name).read() 221 | report += """Output:\n--\n%s--""" % diags 222 | 223 | return Test.FAIL, report 224 | 225 | class SyntaxCheckTest(OneCommandPerFileTest): 226 | def __init__(self, compiler, dir, extra_cxx_args=[], *args, **kwargs): 227 | cmd = [compiler, '-x', 'c++', '-fsyntax-only'] + extra_cxx_args 228 | OneCommandPerFileTest.__init__(self, cmd, dir, 229 | useTempInput=1, *args, **kwargs) 230 | 231 | def createTempInput(self, tmp, test): 232 | print >>tmp, '#include "%s"' % test.source_path 233 | -------------------------------------------------------------------------------- /lib/lit/lit/TestingConfig.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | class TestingConfig: 5 | """" 6 | TestingConfig - Information on the tests inside a suite. 7 | """ 8 | 9 | @staticmethod 10 | def frompath(path, parent, litConfig, mustExist, config = None): 11 | if config is None: 12 | # Set the environment based on the command line arguments. 13 | environment = { 14 | 'LIBRARY_PATH' : os.environ.get('LIBRARY_PATH',''), 15 | 'LD_LIBRARY_PATH' : os.environ.get('LD_LIBRARY_PATH',''), 16 | 'PATH' : os.pathsep.join(litConfig.path + 17 | [os.environ.get('PATH','')]), 18 | 'SYSTEMROOT' : os.environ.get('SYSTEMROOT',''), 19 | 'TERM' : os.environ.get('TERM',''), 20 | 'LLVM_DISABLE_CRASH_REPORT' : '1', 21 | } 22 | 23 | if sys.platform == 'win32': 24 | environment.update({ 25 | 'INCLUDE' : os.environ.get('INCLUDE',''), 26 | 'PATHEXT' : os.environ.get('PATHEXT',''), 27 | 'PYTHONUNBUFFERED' : '1', 28 | 'TEMP' : os.environ.get('TEMP',''), 29 | 'TMP' : os.environ.get('TMP',''), 30 | }) 31 | 32 | # Set the default available features based on the LitConfig. 33 | available_features = [] 34 | if litConfig.useValgrind: 35 | available_features.append('valgrind') 36 | if litConfig.valgrindLeakCheck: 37 | available_features.append('vg_leak') 38 | 39 | config = TestingConfig(parent, 40 | name = '', 41 | suffixes = set(), 42 | test_format = None, 43 | environment = environment, 44 | substitutions = [], 45 | unsupported = False, 46 | on_clone = None, 47 | test_exec_root = None, 48 | test_source_root = None, 49 | excludes = [], 50 | available_features = available_features) 51 | 52 | if os.path.exists(path): 53 | # FIXME: Improve detection and error reporting of errors in the 54 | # config file. 55 | f = open(path) 56 | cfg_globals = dict(globals()) 57 | cfg_globals['config'] = config 58 | cfg_globals['lit'] = litConfig 59 | cfg_globals['__file__'] = path 60 | try: 61 | exec f in cfg_globals 62 | if litConfig.debug: 63 | litConfig.note('... loaded config %r' % path) 64 | except SystemExit,status: 65 | # We allow normal system exit inside a config file to just 66 | # return control without error. 67 | if status.args: 68 | raise 69 | f.close() 70 | else: 71 | if mustExist: 72 | litConfig.fatal('unable to load config from %r ' % path) 73 | elif litConfig.debug: 74 | litConfig.note('... config not found - %r' %path) 75 | 76 | config.finish(litConfig) 77 | return config 78 | 79 | def __init__(self, parent, name, suffixes, test_format, 80 | environment, substitutions, unsupported, on_clone, 81 | test_exec_root, test_source_root, excludes, 82 | available_features): 83 | self.parent = parent 84 | self.name = str(name) 85 | self.suffixes = set(suffixes) 86 | self.test_format = test_format 87 | self.environment = dict(environment) 88 | self.substitutions = list(substitutions) 89 | self.unsupported = unsupported 90 | self.on_clone = on_clone 91 | self.test_exec_root = test_exec_root 92 | self.test_source_root = test_source_root 93 | self.excludes = set(excludes) 94 | self.available_features = set(available_features) 95 | 96 | def clone(self, path): 97 | # FIXME: Chain implementations? 98 | # 99 | # FIXME: Allow extra parameters? 100 | cfg = TestingConfig(self, self.name, self.suffixes, self.test_format, 101 | self.environment, self.substitutions, 102 | self.unsupported, self.on_clone, 103 | self.test_exec_root, self.test_source_root, 104 | self.excludes, self.available_features) 105 | if cfg.on_clone: 106 | cfg.on_clone(self, cfg, path) 107 | return cfg 108 | 109 | def finish(self, litConfig): 110 | """finish() - Finish this config object, after loading is complete.""" 111 | 112 | self.name = str(self.name) 113 | self.suffixes = set(self.suffixes) 114 | self.environment = dict(self.environment) 115 | self.substitutions = list(self.substitutions) 116 | if self.test_exec_root is not None: 117 | # FIXME: This should really only be suite in test suite config 118 | # files. Should we distinguish them? 119 | self.test_exec_root = str(self.test_exec_root) 120 | if self.test_source_root is not None: 121 | # FIXME: This should really only be suite in test suite config 122 | # files. Should we distinguish them? 123 | self.test_source_root = str(self.test_source_root) 124 | self.excludes = set(self.excludes) 125 | 126 | @property 127 | def root(self): 128 | """root attribute - The root configuration for the test suite.""" 129 | if self.parent is None: 130 | return self 131 | else: 132 | return self.parent.root 133 | 134 | -------------------------------------------------------------------------------- /lib/lit/lit/Util.py: -------------------------------------------------------------------------------- 1 | import os, sys 2 | 3 | def detectCPUs(): 4 | """ 5 | Detects the number of CPUs on a system. Cribbed from pp. 6 | """ 7 | # Linux, Unix and MacOS: 8 | if hasattr(os, "sysconf"): 9 | if os.sysconf_names.has_key("SC_NPROCESSORS_ONLN"): 10 | # Linux & Unix: 11 | ncpus = os.sysconf("SC_NPROCESSORS_ONLN") 12 | if isinstance(ncpus, int) and ncpus > 0: 13 | return ncpus 14 | else: # OSX: 15 | return int(capture(['sysctl', '-n', 'hw.ncpu'])) 16 | # Windows: 17 | if os.environ.has_key("NUMBER_OF_PROCESSORS"): 18 | ncpus = int(os.environ["NUMBER_OF_PROCESSORS"]) 19 | if ncpus > 0: 20 | return ncpus 21 | return 1 # Default 22 | 23 | def mkdir_p(path): 24 | """mkdir_p(path) - Make the "path" directory, if it does not exist; this 25 | will also make directories for any missing parent directories.""" 26 | import errno 27 | 28 | if not path or os.path.exists(path): 29 | return 30 | 31 | parent = os.path.dirname(path) 32 | if parent != path: 33 | mkdir_p(parent) 34 | 35 | try: 36 | os.mkdir(path) 37 | except OSError,e: 38 | # Ignore EEXIST, which may occur during a race condition. 39 | if e.errno != errno.EEXIST: 40 | raise 41 | 42 | def capture(args, env=None): 43 | import subprocess 44 | """capture(command) - Run the given command (or argv list) in a shell and 45 | return the standard output.""" 46 | p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, 47 | env=env) 48 | out,_ = p.communicate() 49 | return out 50 | 51 | def which(command, paths = None): 52 | """which(command, [paths]) - Look up the given command in the paths string 53 | (or the PATH environment variable, if unspecified).""" 54 | 55 | if paths is None: 56 | paths = os.environ.get('PATH','') 57 | 58 | # Check for absolute match first. 59 | if os.path.isfile(command): 60 | return command 61 | 62 | # Would be nice if Python had a lib function for this. 63 | if not paths: 64 | paths = os.defpath 65 | 66 | # Get suffixes to search. 67 | # On Cygwin, 'PATHEXT' may exist but it should not be used. 68 | if os.pathsep == ';': 69 | pathext = os.environ.get('PATHEXT', '').split(';') 70 | else: 71 | pathext = [''] 72 | 73 | # Search the paths... 74 | for path in paths.split(os.pathsep): 75 | for ext in pathext: 76 | p = os.path.join(path, command + ext) 77 | if os.path.exists(p): 78 | return p 79 | 80 | return None 81 | 82 | def checkToolsPath(dir, tools): 83 | for tool in tools: 84 | if not os.path.exists(os.path.join(dir, tool)): 85 | return False; 86 | return True; 87 | 88 | def whichTools(tools, paths): 89 | for path in paths.split(os.pathsep): 90 | if checkToolsPath(path, tools): 91 | return path 92 | return None 93 | 94 | def printHistogram(items, title = 'Items'): 95 | import itertools, math 96 | 97 | items.sort(key = lambda (_,v): v) 98 | 99 | maxValue = max([v for _,v in items]) 100 | 101 | # Select first "nice" bar height that produces more than 10 bars. 102 | power = int(math.ceil(math.log(maxValue, 10))) 103 | for inc in itertools.cycle((5, 2, 2.5, 1)): 104 | barH = inc * 10**power 105 | N = int(math.ceil(maxValue / barH)) 106 | if N > 10: 107 | break 108 | elif inc == 1: 109 | power -= 1 110 | 111 | histo = [set() for i in range(N)] 112 | for name,v in items: 113 | bin = min(int(N * v/maxValue), N-1) 114 | histo[bin].add(name) 115 | 116 | barW = 40 117 | hr = '-' * (barW + 34) 118 | print '\nSlowest %s:' % title 119 | print hr 120 | for name,value in items[-20:]: 121 | print '%.2fs: %s' % (value, name) 122 | print '\n%s Times:' % title 123 | print hr 124 | pDigits = int(math.ceil(math.log(maxValue, 10))) 125 | pfDigits = max(0, 3-pDigits) 126 | if pfDigits: 127 | pDigits += pfDigits + 1 128 | cDigits = int(math.ceil(math.log(len(items), 10))) 129 | print "[%s] :: [%s] :: [%s]" % ('Range'.center((pDigits+1)*2 + 3), 130 | 'Percentage'.center(barW), 131 | 'Count'.center(cDigits*2 + 1)) 132 | print hr 133 | for i,row in enumerate(histo): 134 | pct = float(len(row)) / len(items) 135 | w = int(barW * pct) 136 | print "[%*.*fs,%*.*fs)" % (pDigits, pfDigits, i*barH, 137 | pDigits, pfDigits, (i+1)*barH), 138 | print ":: [%s%s] :: [%*d/%*d]" % ('*'*w, ' '*(barW-w), 139 | cDigits, len(row), 140 | cDigits, len(items)) 141 | 142 | -------------------------------------------------------------------------------- /lib/lit/lit/__init__.py: -------------------------------------------------------------------------------- 1 | """'lit' Testing Tool""" 2 | 3 | from main import main 4 | 5 | __author__ = 'Daniel Dunbar' 6 | __email__ = 'daniel@zuster.org' 7 | __versioninfo__ = (0, 3, 0) 8 | __version__ = '.'.join(map(str, __versioninfo__)) + 'dev' 9 | 10 | __all__ = [] 11 | -------------------------------------------------------------------------------- /lib/lit/setup.py: -------------------------------------------------------------------------------- 1 | import lit 2 | 3 | # FIXME: Support distutils? 4 | from setuptools import setup, find_packages 5 | setup( 6 | name = "lit", 7 | version = lit.__version__, 8 | 9 | author = lit.__author__, 10 | author_email = lit.__email__, 11 | url = 'http://llvm.org', 12 | license = 'BSD', 13 | 14 | description = "A Software Testing Tool", 15 | keywords = 'test C++ automatic discovery', 16 | long_description = """\ 17 | *lit* 18 | +++++ 19 | 20 | About 21 | ===== 22 | 23 | *lit* is a portable tool for executing LLVM and Clang style test suites, 24 | summarizing their results, and providing indication of failures. *lit* is 25 | designed to be a lightweight testing tool with as simple a user interface as 26 | possible. 27 | 28 | 29 | Features 30 | ======== 31 | 32 | * Portable! 33 | * Flexible test discovery. 34 | * Parallel test execution. 35 | * Support for multiple test formats and test suite designs. 36 | 37 | 38 | Documentation 39 | ============= 40 | 41 | The official *lit* documentation is in the man page, available online at the LLVM 42 | Command Guide: http://llvm.org/cmds/lit.html. 43 | 44 | 45 | Source 46 | ====== 47 | 48 | The *lit* source is available as part of LLVM, in the LLVM SVN repository: 49 | http://llvm.org/svn/llvm-project/llvm/trunk/utils/lit. 50 | """, 51 | 52 | classifiers=[ 53 | 'Development Status :: 3 - Alpha', 54 | 'Environment :: Console', 55 | 'Intended Audience :: Developers', 56 | 'License :: OSI Approved :: University of Illinois/NCSA Open Source License', 57 | 'Natural Language :: English', 58 | 'Operating System :: OS Independent', 59 | 'Programming Language :: Python', 60 | 'Topic :: Software Development :: Testing', 61 | ], 62 | 63 | zip_safe = False, 64 | packages = find_packages(), 65 | entry_points = { 66 | 'console_scripts': [ 67 | 'lit = lit:main', 68 | ], 69 | } 70 | ) 71 | -------------------------------------------------------------------------------- /src/AntiAlgebra.cc: -------------------------------------------------------------------------------- 1 | // This pass simplifies expressions via "transposition" of formulae. 2 | // Consider a comparison lhs < rhs. The basic idea is to represent 3 | // each side as a symbolic expression (e.g., p + x < p), and transform 4 | // the comparison into lhs - rhs < 0 for simplification (e.g., x < 0). 5 | // Emit an warning if the two forms of the same comparison are only 6 | // equivalent under bug-free assertions. 7 | 8 | #define DEBUG_TYPE "anti-algebra" 9 | #include "AntiFunctionPass.h" 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | using namespace llvm; 21 | 22 | namespace { 23 | 24 | struct AntiAlgebra : AntiFunctionPass { 25 | static char ID; 26 | AntiAlgebra() : AntiFunctionPass(ID) {} 27 | 28 | virtual void getAnalysisUsage(AnalysisUsage &AU) const { 29 | AntiFunctionPass::getAnalysisUsage(AU); 30 | AU.addRequired(); 31 | AU.setPreservesCFG(); 32 | } 33 | 34 | virtual bool doInitialization(Module &) { 35 | TLI = getAnalysisIfAvailable(); 36 | return false; 37 | } 38 | 39 | virtual bool runOnAntiFunction(Function &); 40 | 41 | private: 42 | TargetLibraryInfo *TLI; 43 | ScalarEvolution *SE; 44 | 45 | bool visitICmpInst(ICmpInst *I); 46 | int checkEqv(ICmpInst *Old, ICmpInst *New); 47 | }; 48 | 49 | } // anonymous namespace 50 | 51 | bool AntiAlgebra::runOnAntiFunction(Function &F) { 52 | SE = &getAnalysis(); 53 | bool Changed = false; 54 | for (inst_iterator i = inst_begin(F), e = inst_end(F); i != e; ) { 55 | Instruction *I = &*i++; 56 | if (!Diagnostic::hasSingleDebugLocation(I)) 57 | continue; 58 | // For now we are only interested in comparisons. 59 | if (ICmpInst *ICI = dyn_cast(I)) 60 | Changed |= visitICmpInst(ICI); 61 | } 62 | return Changed; 63 | } 64 | 65 | static void getAdditiveTerms(const SCEV *S, SmallVectorImpl &Terms) { 66 | const SCEVAddExpr *A = dyn_cast(S); 67 | if (!A) { 68 | Terms.push_back(S); 69 | return; 70 | } 71 | Terms.append(A->op_begin(), A->op_end()); 72 | std::sort(Terms.begin(), Terms.end()); 73 | } 74 | 75 | static bool contains(const SCEV *L, const SCEV *R) { 76 | SmallVector LTerms, RTerms; 77 | getAdditiveTerms(L, LTerms); 78 | getAdditiveTerms(R, RTerms); 79 | if (LTerms.size() < RTerms.size()) 80 | LTerms.swap(RTerms); 81 | // Ignore LHS cmp C for now. 82 | if (RTerms.size() == 1) { 83 | if (isa(RTerms[0])) 84 | return false; 85 | } 86 | return std::includes(LTerms.begin(), LTerms.end(), RTerms.begin(), RTerms.end()); 87 | } 88 | 89 | static const char *getPredicateStr(CmpInst::Predicate Pred) { 90 | switch (Pred) { 91 | default: assert(0); 92 | case CmpInst::ICMP_EQ: return " == "; 93 | case CmpInst::ICMP_NE: return " != "; 94 | case CmpInst::ICMP_UGT: return " >u "; 95 | case CmpInst::ICMP_UGE: return " ≥u "; 96 | case CmpInst::ICMP_ULT: return " s "; 99 | case CmpInst::ICMP_SGE: return " ≥s "; 100 | case CmpInst::ICMP_SLT: return " getSCEV(I->getOperand(0)); 115 | const SCEV *R = SE->getSCEV(I->getOperand(1)); 116 | // Is L part of R (or vice versa)? 117 | if (!contains(L, R)) 118 | return false; 119 | const SCEV *S = SE->getMinusSCEV(L, R); 120 | LLVMContext &C = I->getContext(); 121 | IntegerType *T = IntegerType::get(C, DL->getTypeSizeInBits(S->getType())); 122 | Value *V = SCEVExpander(*SE, "").expandCodeFor(S, T, I); 123 | Value *Z = Constant::getNullValue(T); 124 | // Transform (lhs op rhs) to ((lhs - rhs) op 0). 125 | ICmpInst *NewCmp = new ICmpInst(I, I->getSignedPredicate(), V, Z); 126 | NewCmp->setDebugLoc(I->getDebugLoc()); 127 | int isEqv = 0; 128 | if (SMTFork() == 0) 129 | isEqv = checkEqv(I, NewCmp); 130 | SMTJoin(&isEqv); 131 | BENCHMARK(Diagnostic() << "query: " << qstr(isEqv) << "\n"); 132 | if (isEqv <= 0) { 133 | RecursivelyDeleteTriviallyDeadInstructions(NewCmp, TLI); 134 | return false; 135 | } 136 | Diag.bug(DEBUG_TYPE); 137 | Diag << "model: |\n" << *I << "\n -->" << *NewCmp << "\n" 138 | << " ************************************************************\n " 139 | << *L << getPredicateStr(I->getPredicate()) << *R << "\n --> " 140 | << *S << getPredicateStr(I->getSignedPredicate()) << "0\n"; 141 | Diag.backtrace(I); 142 | printMinimalAssertions(); 143 | I->replaceAllUsesWith(NewCmp); 144 | RecursivelyDeleteTriviallyDeadInstructions(I, TLI); 145 | return true; 146 | } 147 | 148 | int AntiAlgebra::checkEqv(ICmpInst *I0, ICmpInst *I1) { 149 | SMTSolver SMT(false); 150 | ValueGen VG(*DL, SMT); 151 | PathGen PG(VG, Backedges, *DT); 152 | int isEqv = 0; 153 | SMTExpr E0 = VG.get(I0); 154 | SMTExpr E1 = VG.get(I1); 155 | SMTExpr Q = SMT.ne(E0, E1); 156 | BasicBlock *BB = I0->getParent(); 157 | SMTExpr R = PG.get(BB); 158 | SMT.assume(R); 159 | // E0 != E1 without bug-free assertions; must be reachable as well. 160 | if (SMT.query(Q) == SMT_SAT) { 161 | SMTExpr Delta = getDeltaForBlock(BB, VG); 162 | if (Delta) { 163 | // E0 == E1 with bug-free assertions. 164 | SMTStatus Status = queryWithDelta(Q, Delta, VG); 165 | SMT.decref(Delta); 166 | if (Status == SMT_UNSAT) 167 | isEqv = 1; 168 | } 169 | } 170 | SMT.decref(Q); 171 | return isEqv; 172 | } 173 | 174 | char AntiAlgebra::ID; 175 | 176 | static RegisterPass 177 | X("anti-algebra", "Anti Algebra Optimization"); 178 | -------------------------------------------------------------------------------- /src/AntiDCE.cc: -------------------------------------------------------------------------------- 1 | // This pass kills unreachable (dead) code by exploiting undefined behavior. 2 | // The basic idea is that given a reachable statement s, if it always blows up 3 | // another reachable statement t (i.e., triggering t's undefined behavior), 4 | // then s is actually "dead" in terms of undefined behavior. 5 | 6 | #define DEBUG_TYPE "anti-dce" 7 | #include "AntiFunctionPass.h" 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | using namespace llvm; 18 | 19 | namespace { 20 | 21 | struct AntiDCE: AntiFunctionPass { 22 | static char ID; 23 | AntiDCE() : AntiFunctionPass(ID) {} 24 | 25 | virtual void getAnalysisUsage(AnalysisUsage &AU) const { 26 | AntiFunctionPass::getAnalysisUsage(AU); 27 | AU.addPreserved(); 28 | AU.addPreserved(); 29 | } 30 | 31 | virtual bool runOnAntiFunction(Function &); 32 | 33 | private: 34 | bool shouldCheck(BasicBlock *BB); 35 | int shouldKeepCode(BasicBlock *BB); 36 | void report(BasicBlock *BB); 37 | void markAsDead(BasicBlock *BB); 38 | }; 39 | 40 | } // anonymous namespace 41 | 42 | bool AntiDCE::shouldCheck(BasicBlock *BB) { 43 | // Ignore unreachable blocks, often from BUG_ON() or assert(). 44 | if (isa(BB->getTerminator())) 45 | return false; 46 | if (isa(BB->getFirstInsertionPt())) 47 | return false; 48 | // BB may become unreachable after marking some block as unreachable. 49 | if (!DT->isReachableFromEntry(BB)) { 50 | markAsDead(BB); 51 | return false; 52 | } 53 | for (BasicBlock::iterator i = BB->begin(), e = BB->end(); i != e; ++i) { 54 | if (Diagnostic::hasSingleDebugLocation(i)) 55 | return true; 56 | } 57 | return false; 58 | } 59 | 60 | static inline 61 | const char *qstr(int Keep) { 62 | switch (Keep) { 63 | default: return "timeout"; 64 | case 0: return "succ"; 65 | case 1: return "fail"; 66 | } 67 | } 68 | 69 | bool AntiDCE::runOnAntiFunction(Function &F) { 70 | bool Changed = false; 71 | for (Function::iterator i = F.begin(), e = F.end(); i != e; ++i) { 72 | BasicBlock *BB = i; 73 | if (!shouldCheck(BB)) 74 | continue; 75 | int Keep; 76 | if (SMTFork() == 0) 77 | Keep = shouldKeepCode(BB); 78 | SMTJoin(&Keep); 79 | BENCHMARK(Diagnostic() << "query: " << qstr(Keep) << "\n"); 80 | if (Keep) 81 | continue; 82 | report(BB); 83 | Changed = true; 84 | markAsDead(BB); 85 | // Update if any optimization performed. 86 | recalculate(F); 87 | } 88 | return Changed; 89 | } 90 | 91 | int AntiDCE::shouldKeepCode(BasicBlock *BB) { 92 | SMTSolver SMT(false); 93 | ValueGen VG(*DL, SMT); 94 | // Compute path condition. 95 | PathGen PG(VG, Backedges, *DT); 96 | SMTExpr R = PG.get(BB); 97 | // Ignore dead path. 98 | if (SMT.query(R) == SMT_UNSAT) 99 | return 1; 100 | // Collect bug assertions. 101 | SMTExpr Delta = getDeltaForBlock(BB, VG); 102 | if (!Delta) 103 | return 1; 104 | SMTStatus Status = queryWithDelta(R, Delta, VG); 105 | SMT.decref(Delta); 106 | if (Status == SMT_UNSAT) 107 | return 0; 108 | return 1; 109 | } 110 | 111 | void AntiDCE::report(BasicBlock *BB) { 112 | // Prove BB is dead; output warning message. 113 | Diag.bug(DEBUG_TYPE); 114 | Diag << "model: |\n"; 115 | for (auto i = pred_begin(BB), e = pred_end(BB); i != e; ++i) { 116 | auto BI = dyn_cast((*i)->getTerminator()); 117 | if (!BI || !BI->isConditional()) 118 | continue; 119 | auto CondInst = dyn_cast(BI->getCondition()); 120 | if (!CondInst) 121 | continue; 122 | bool CondVal = (BI->getSuccessor(1) == BB); 123 | Diag << *CondInst << "\n --> " 124 | << (CondVal ? "true" : "false") 125 | << "\n ************************************************************\n"; 126 | BI->setCondition(ConstantInt::get(CondInst->getType(), CondVal)); 127 | RecursivelyDeleteTriviallyDeadInstructions(CondInst); 128 | } 129 | Diag << " " << BB->getName() << ":\n"; 130 | for (Instruction &I: *BB) 131 | Diag << I << '\n'; 132 | for (Instruction &I: *BB) { 133 | if (!I.getDebugLoc().isUnknown()) { 134 | Diag.backtrace(&I); 135 | break; 136 | } 137 | } 138 | printMinimalAssertions(); 139 | } 140 | 141 | void AntiDCE::markAsDead(BasicBlock *BB) { 142 | // Remove BB from successors. 143 | std::vector Succs(succ_begin(BB), succ_end(BB)); 144 | for (unsigned i = 0, e = Succs.size(); i != e; ++i) 145 | Succs[i]->removePredecessor(BB); 146 | // Empty BB; ignore PHI and LandingPad, which are trikcy to remove. 147 | for (auto i = BB->getFirstInsertionPt(), e = BB->end(); i != e; ) { 148 | Instruction *I = i++; 149 | Type *T = I->getType(); 150 | if (!I->use_empty()) 151 | I->replaceAllUsesWith(UndefValue::get(T)); 152 | I->eraseFromParent(); 153 | } 154 | // Mark it as unreachable. 155 | new UnreachableInst(BB->getContext(), BB); 156 | } 157 | 158 | char AntiDCE::ID; 159 | 160 | static RegisterPass 161 | X("anti-dce", "Anti Dead Code Elimination"); 162 | -------------------------------------------------------------------------------- /src/AntiFunctionPass.cc: -------------------------------------------------------------------------------- 1 | #include "AntiFunctionPass.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | using namespace llvm; 17 | 18 | static cl::opt 19 | IgnorePostOpt("ignore-bugon-post", 20 | cl::desc("Ignore bugon conditions on post-dominators")); 21 | 22 | static cl::opt 23 | MinBugOnOpt("min-bugon", 24 | cl::desc("Compute minimal bugon set"), cl::init(true)); 25 | 26 | static const size_t BUFFER_SIZE = 4096; 27 | 28 | bool BenchmarkFlag; 29 | 30 | namespace { 31 | struct BenchmarkInit { 32 | BenchmarkInit() { BenchmarkFlag = !!::getenv("BENCHMARK"); } 33 | }; 34 | } 35 | 36 | static BenchmarkInit X; 37 | 38 | AntiFunctionPass::AntiFunctionPass(char &ID) : FunctionPass(ID), Buffer(NULL) { 39 | PassRegistry &Registry = *PassRegistry::getPassRegistry(); 40 | initializeDataLayoutPass(Registry); 41 | initializeDominatorTreePass(Registry); 42 | initializePostDominatorTreePass(Registry); 43 | if (MinBugOnOpt) 44 | Buffer = mmap(NULL, BUFFER_SIZE, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0); 45 | } 46 | 47 | AntiFunctionPass::~AntiFunctionPass() { 48 | if (Buffer) 49 | munmap(Buffer, BUFFER_SIZE); 50 | } 51 | 52 | void AntiFunctionPass::getAnalysisUsage(AnalysisUsage &AU) const { 53 | AU.addRequired(); 54 | AU.addRequired(); 55 | AU.addRequired(); 56 | } 57 | 58 | #ifndef NDEBUG 59 | static std::string demangle(Function &F) { 60 | std::string Name = F.getName(); 61 | char *s = abi::__cxa_demangle(Name.c_str(), NULL, NULL, NULL); 62 | if (s) { 63 | Name = s; 64 | free(s); 65 | } 66 | return Name; 67 | } 68 | #endif 69 | 70 | static void calculateBackedges(Function &F, SmallVectorImpl &Backedges, SmallVectorImpl &InLoopBlocks) { 71 | FindFunctionBackedges(F, Backedges); 72 | if (Backedges.empty()) 73 | return; 74 | // No need to calculate InLoopBlocks if post-dominators are ignored. 75 | if (IgnorePostOpt) 76 | return; 77 | for (scc_iterator i = scc_begin(&F), e = scc_end(&F); i != e; ++i) { 78 | if (i.hasLoop()) 79 | InLoopBlocks.append((*i).begin(), (*i).end()); 80 | } 81 | } 82 | 83 | bool AntiFunctionPass::runOnFunction(Function &F) { 84 | BugOn = getBugOn(F.getParent()); 85 | if (!BugOn) 86 | return false; 87 | DEBUG(dbgs() << "Analyzing " << demangle(F) << "\n"); 88 | assert(BugOn->arg_size() == 1); 89 | assert(BugOn->arg_begin()->getType()->isIntegerTy(1)); 90 | DT = &getAnalysis(); 91 | PDT = &getAnalysis(); 92 | DL = &getAnalysis(); 93 | calculateBackedges(F, Backedges, InLoopBlocks); 94 | bool Changed = runOnAntiFunction(F); 95 | Backedges.clear(); 96 | InLoopBlocks.clear(); 97 | return Changed; 98 | } 99 | 100 | void AntiFunctionPass::recalculate(Function &F) { 101 | DT->DT->recalculate(F); 102 | PDT->DT->recalculate(F); 103 | Backedges.clear(); 104 | InLoopBlocks.clear(); 105 | calculateBackedges(F, Backedges, InLoopBlocks); 106 | } 107 | 108 | static SMTExpr computeDelta(ValueGen &VG, SmallVectorImpl &Assertions) { 109 | SMTSolver &SMT = VG.SMT; 110 | SMTExpr U = SMT.bvfalse(); 111 | // Compute R and U. 112 | for (BugOnInst *I : Assertions) { 113 | // ->queryWithAssertions() may mask some assertions. 114 | if (!I) 115 | continue; 116 | Value *V = I->getCondition(); 117 | SMTExpr E = VG.get(V); 118 | SMTExpr Tmp = SMT.bvor(U, E); 119 | SMT.decref(U); 120 | U = Tmp; 121 | } 122 | SMTExpr NotU = SMT.bvnot(U); 123 | SMT.decref(U); 124 | return NotU; 125 | } 126 | 127 | SMTExpr AntiFunctionPass::getDeltaForBlock(BasicBlock *BB, ValueGen &VG) { 128 | Assertions.clear(); 129 | // Ignore post-dominators if the option is set or BB is in a loop. 130 | bool IgnorePostdom = IgnorePostOpt 131 | || (std::find(InLoopBlocks.begin(), InLoopBlocks.end(), BB) != InLoopBlocks.end()); 132 | Function *F = BB->getParent(); 133 | for (Function::iterator i = F->begin(), e = F->end(); i != e; ++i) { 134 | BasicBlock *Blk = i; 135 | // Collect blocks that (post)dominate BB: if BB is reachable, 136 | // these blocks must also be reachable, and we need to check 137 | // their bug assertions. 138 | if (!DT->dominates(Blk, BB)) { 139 | // Skip inspecting postdominators if BB is in a loop. 140 | if (IgnorePostdom) 141 | continue; 142 | if (!PDT->dominates(Blk, BB)) 143 | continue; 144 | } 145 | for (BasicBlock::iterator i = Blk->begin(), e = Blk->end(); i != e; ++i) { 146 | if (BugOnInst *BOI = dyn_cast(i)) 147 | Assertions.push_back(BOI); 148 | } 149 | } 150 | if (Assertions.empty()) 151 | return NULL; 152 | return computeDelta(VG, Assertions); 153 | } 154 | 155 | SMTStatus AntiFunctionPass::queryWithDelta(SMTExpr E, SMTExpr Delta, ValueGen &VG) { 156 | SMTSolver &SMT = VG.SMT; 157 | { 158 | SMTExpr Q = SMT.bvand(E, Delta); 159 | SMTStatus Status = SMT.query(Q); 160 | SMT.decref(Q); 161 | if (!Buffer || Status != SMT_UNSAT) 162 | return Status; 163 | } 164 | unsigned n = Assertions.size(); 165 | // Compute the minimal bugon set. 166 | for (BugOnInst *&I : Assertions) { 167 | if (n <= 1) 168 | break; 169 | BugOnInst *Tmp = I; 170 | // Mask out this bugon and see if still unsat. 171 | I = NULL; 172 | SMTExpr MinDelta = computeDelta(VG, Assertions); 173 | SMTExpr Q = SMT.bvand(E, MinDelta); 174 | SMT.decref(MinDelta); 175 | SMTStatus Status = SMT.query(Q); 176 | SMT.decref(Q); 177 | // Keep this assertions. 178 | if (Status != SMT_UNSAT) 179 | I = Tmp; 180 | else 181 | --n; 182 | } 183 | // Output the unsat core. 184 | BugOnInst **p = (BugOnInst **)Buffer; 185 | for (BugOnInst *I : Assertions) { 186 | if (!I) 187 | continue; 188 | *p++ = I; 189 | } 190 | *p = NULL; 191 | return SMT_UNSAT; 192 | } 193 | 194 | void AntiFunctionPass::printMinimalAssertions() { 195 | if (!Buffer) 196 | return; 197 | int Count = 0; 198 | for (BugOnInst **p = (BugOnInst **)Buffer; *p; ++p) 199 | Count++; 200 | Diag << "ncore: " << Count << "\n"; 201 | Diag << "core: \n"; 202 | LLVMContext &C = BugOn->getContext(); 203 | for (BugOnInst **p = (BugOnInst **)Buffer; *p; ++p) { 204 | BugOnInst *I = *p; 205 | MDNode *MD = I->getDebugLoc().getAsMDNode(C); 206 | Diag.location(MD); 207 | Diag << " - " << I->getAnnotation() << "\n"; 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /src/AntiFunctionPass.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "BugOn.h" 4 | #include "Diagnostic.h" 5 | #include "PathGen.h" 6 | #include "ValueGen.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #define BENCHMARK(e) if (BenchmarkFlag) { e; } 13 | 14 | extern bool BenchmarkFlag; 15 | 16 | class SMTSolver; 17 | 18 | class AntiFunctionPass : public llvm::FunctionPass { 19 | protected: 20 | llvm::DataLayout *DL; 21 | llvm::DominatorTree *DT; 22 | llvm::SmallVector Backedges; 23 | Diagnostic Diag; 24 | 25 | explicit AntiFunctionPass(char &ID); 26 | ~AntiFunctionPass(); 27 | virtual bool runOnAntiFunction(llvm::Function &F) = 0; 28 | virtual void getAnalysisUsage(llvm::AnalysisUsage &AU) const; 29 | 30 | // Call if CFG has changed. 31 | void recalculate(llvm::Function &F); 32 | // Return bug-free assertion. 33 | SMTExpr getDeltaForBlock(llvm::BasicBlock *, ValueGen &); 34 | SMTStatus queryWithDelta(SMTExpr E, SMTExpr Delta, ValueGen &); 35 | void printMinimalAssertions(); 36 | 37 | private: 38 | llvm::Function *BugOn; 39 | llvm::PostDominatorTree *PDT; 40 | llvm::SmallVector InLoopBlocks; 41 | llvm::SmallVector Assertions; 42 | void *Buffer; 43 | 44 | virtual bool runOnFunction(llvm::Function &); 45 | }; 46 | -------------------------------------------------------------------------------- /src/AntiSimplify.cc: -------------------------------------------------------------------------------- 1 | // This pass folds expressions into constants. The basic idea is to 2 | // warn against expression e that is 1) reachable and 2) non-constant, 3 | // but 3) must be constant with bug-free assertions. 4 | 5 | #define DEBUG_TYPE "anti-simplify" 6 | #include "AntiFunctionPass.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | using namespace llvm; 13 | 14 | namespace { 15 | 16 | struct AntiSimplify: AntiFunctionPass { 17 | static char ID; 18 | AntiSimplify() : AntiFunctionPass(ID) {} 19 | 20 | virtual void getAnalysisUsage(AnalysisUsage &AU) const { 21 | AntiFunctionPass::getAnalysisUsage(AU); 22 | AU.setPreservesCFG(); 23 | } 24 | 25 | virtual bool runOnAntiFunction(Function &); 26 | 27 | private: 28 | int foldConst(Instruction *); 29 | }; 30 | 31 | } // anonymous namespace 32 | 33 | #define FOLD_FAIL 2 34 | 35 | static inline 36 | const char *qstr(int ConstVal) { 37 | switch (ConstVal) { 38 | default: 39 | return "timeout"; 40 | case 0: case 1: 41 | return "succ"; 42 | case FOLD_FAIL: 43 | return "fail"; 44 | } 45 | } 46 | 47 | bool AntiSimplify::runOnAntiFunction(Function &F) { 48 | bool Changed = false; 49 | for (inst_iterator i = inst_begin(F), e = inst_end(F); i != e; ) { 50 | Instruction *I = &*i++; 51 | if (!Diagnostic::hasSingleDebugLocation(I)) 52 | continue; 53 | // For now we are only interested in bool expressions. 54 | if (!isa(I)) 55 | continue; 56 | int ConstVal; 57 | if (SMTFork() == 0) 58 | ConstVal = foldConst(I); 59 | SMTJoin(&ConstVal); 60 | BENCHMARK(Diagnostic() << "query: " << qstr(ConstVal) << "\n"); 61 | if (ConstVal != 0 && ConstVal != 1) 62 | continue; 63 | Diag.bug(DEBUG_TYPE); 64 | Diag << "model: |\n" << *I << "\n --> " 65 | << (ConstVal ? "true" : "false") << "\n"; 66 | Diag.backtrace(I); 67 | printMinimalAssertions(); 68 | Type *T = I->getType(); 69 | Constant *C = ConstantInt::get(T, ConstVal); 70 | I->replaceAllUsesWith(C); 71 | RecursivelyDeleteTriviallyDeadInstructions(I); 72 | Changed = true; 73 | } 74 | return Changed; 75 | } 76 | 77 | int AntiSimplify::foldConst(Instruction *I) { 78 | int Result = FOLD_FAIL; 79 | SMTSolver SMT(false); 80 | ValueGen VG(*DL, SMT); 81 | BasicBlock *BB = I->getParent(); 82 | SMTExpr Delta = getDeltaForBlock(BB, VG); 83 | if (!Delta) 84 | return Result; 85 | // Compute path condition. 86 | PathGen PG(VG, Backedges, *DT); 87 | { 88 | SMTExpr R = PG.get(BB); 89 | SMT.assume(R); 90 | } 91 | SMTExpr E = VG.get(I); 92 | int Status = queryWithDelta(E, Delta, VG); 93 | if (Status == SMT_UNSAT) { 94 | // I must be false with Delta. 95 | // Can I be true without Delta? 96 | if (SMT.query(E) == SMT_SAT) 97 | Result = 0; 98 | } else { 99 | // I can be false with Delta. 100 | // Let's try if it can be true. 101 | SMTExpr NE = SMT.bvnot(E); 102 | Status = queryWithDelta(NE, Delta, VG); 103 | if (Status == SMT_UNSAT) { 104 | // I must be true with Delta. 105 | // Can I be false with Delta? 106 | if (SMT.query(NE) == SMT_SAT) 107 | Result = 1; 108 | } 109 | SMT.decref(NE); 110 | } 111 | SMT.decref(Delta); 112 | return Result; 113 | } 114 | 115 | char AntiSimplify::ID; 116 | 117 | static RegisterPass 118 | X("anti-simplify", "Anti Code Simplification"); 119 | -------------------------------------------------------------------------------- /src/BugOn.cc: -------------------------------------------------------------------------------- 1 | #include "BugOn.h" 2 | #include "Diagnostic.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | using namespace llvm; 11 | 12 | #define OPT_BUGON "opt.bugon" 13 | 14 | static cl::opt 15 | ShowTrueOpt("show-bugon-true", 16 | cl::desc("Show always true bug conditions")); 17 | 18 | Function *getBugOn(const Module *M) { 19 | return M->getFunction(OPT_BUGON); 20 | } 21 | 22 | Function *getOrInsertBugOn(Module *M) { 23 | LLVMContext &C = M->getContext(); 24 | Type *VoidTy = Type::getVoidTy(C); 25 | Type *BoolTy = Type::getInt1Ty(C); 26 | FunctionType *T = FunctionType::get(VoidTy, BoolTy, false); 27 | Function *F = cast(M->getOrInsertFunction(OPT_BUGON, T)); 28 | F->setDoesNotThrow(); 29 | return F; 30 | } 31 | 32 | StringRef BugOnInst::getAnnotation() const { 33 | MDNode *MD = getMetadata("bug"); 34 | return cast(MD->getOperand(0))->getString(); 35 | } 36 | 37 | void BugOnPass::getAnalysisUsage(AnalysisUsage &AU) const { 38 | AU.setPreservesCFG(); 39 | } 40 | 41 | bool BugOnPass::runOnFunction(Function &F) { 42 | IRBuilder<> TheBuilder(F.getContext()); 43 | Builder = &TheBuilder; 44 | bool Changed = false; 45 | for (inst_iterator i = inst_begin(F), e = inst_end(F); i != e; ) { 46 | Instruction *I = &*i++; 47 | if (I->getDebugLoc().isUnknown()) 48 | continue; 49 | setInsertPoint(I); 50 | Changed |= runOnInstruction(I); 51 | } 52 | return Changed; 53 | } 54 | 55 | bool BugOnPass::clearDebugLoc(Value *V) { 56 | Instruction *I = dyn_cast(V); 57 | if (!I || I->getDebugLoc().isUnknown()) 58 | return false; 59 | I->setDebugLoc(DebugLoc()); 60 | return true; 61 | } 62 | 63 | bool BugOnPass::recursivelyClearDebugLoc(Value *V) { 64 | Instruction *I = dyn_cast(V); 65 | if (!I || I->getDebugLoc().isUnknown()) 66 | return false; 67 | I->setDebugLoc(DebugLoc()); 68 | for (Instruction::op_iterator i = I->op_begin(), e = I->op_end(); i != e; ++i) { 69 | Value *U = *i; 70 | if (U->hasOneUse()) 71 | recursivelyClearDebugLoc(U); 72 | } 73 | return true; 74 | } 75 | 76 | Value *BugOnPass::getUnderlyingObject(Value *V, DataLayout *DL) { 77 | return GetUnderlyingObject(V, DL, 1000); 78 | } 79 | 80 | Value *BugOnPass::getAddressOperand(Value *I, bool skipVolatile) { 81 | #define IS_VOLATILE(x) (skipVolatile && (x)->isVolatile()) 82 | if (LoadInst *LI = dyn_cast(I)) { 83 | if (!IS_VOLATILE(LI)) 84 | return LI->getPointerOperand(); 85 | } else if (StoreInst *SI = dyn_cast(I)) { 86 | if (!IS_VOLATILE(SI)) 87 | return SI->getPointerOperand(); 88 | } else if (AtomicCmpXchgInst *AI = dyn_cast(I)) { 89 | if (!IS_VOLATILE(AI)) 90 | return AI->getPointerOperand(); 91 | } else if (AtomicRMWInst *AI = dyn_cast(I)) { 92 | if (!IS_VOLATILE(AI)) 93 | return AI->getPointerOperand(); 94 | } 95 | #undef IS_VOLATILE 96 | return NULL; 97 | } 98 | 99 | bool BugOnPass::insert(Value *V, StringRef Bug) { 100 | const DebugLoc &DbgLoc = Builder->GetInsertPoint()->getDebugLoc(); 101 | return insert(V, Bug, DbgLoc); 102 | } 103 | 104 | bool BugOnPass::insert(Value *V, StringRef Bug, const DebugLoc &DbgLoc) { 105 | // Ignore bugon(false). 106 | if (ConstantInt *CI = dyn_cast(V)) { 107 | if (CI->isZero()) 108 | return false; 109 | if (ShowTrueOpt) { 110 | Instruction *I = Builder->GetInsertPoint(); 111 | if (Diagnostic::hasSingleDebugLocation(I)) { 112 | Diagnostic Diag; 113 | Diag.bug(Pass::lookupPassInfo(getPassID())->getPassArgument()); 114 | Diag << "model: |\n" << *I << "\n"; 115 | Diag.backtrace(I); 116 | } 117 | } 118 | } 119 | LLVMContext &C = V->getContext(); 120 | if (!BugOn) { 121 | BugOn = getOrInsertBugOn(getModule()); 122 | MD_bug = C.getMDKindID("bug"); 123 | } 124 | Instruction *I = Builder->CreateCall(BugOn, V); 125 | I->setDebugLoc(DbgLoc); 126 | if (!Bug.empty()) 127 | I->setMetadata(MD_bug, MDNode::get(C, MDString::get(C, Bug))); 128 | return true; 129 | } 130 | 131 | Module *BugOnPass::getModule() { 132 | return Builder->GetInsertBlock()->getParent()->getParent(); 133 | } 134 | 135 | Instruction *BugOnPass::setInsertPoint(Instruction *I) { 136 | Instruction *IP = Builder->GetInsertPoint(); 137 | Builder->SetInsertPoint(I); 138 | // Don't set debugging information for inserted instructions. 139 | Builder->SetCurrentDebugLocation(DebugLoc()); 140 | return IP; 141 | } 142 | 143 | Instruction *BugOnPass::setInsertPointAfter(Instruction *I) { 144 | assert(!isa(I) && "Cannot insert after a terminator!"); 145 | BasicBlock::iterator Iter(I); 146 | ++Iter; 147 | return setInsertPoint(Iter); 148 | } 149 | 150 | Value *BugOnPass::createIsNull(Value *V) { 151 | // Ignore trivial non-null pointers (e.g., a stack pointer). 152 | if (V->isDereferenceablePointer()) 153 | return Builder->getFalse(); 154 | return Builder->CreateIsNull(V); 155 | } 156 | 157 | Value *BugOnPass::createIsZero(Value *V) { 158 | return Builder->CreateIsNull(V); 159 | } 160 | 161 | Value *BugOnPass::createIsNotNull(Value *V) { 162 | if (V->isDereferenceablePointer()) 163 | return Builder->getTrue(); 164 | return Builder->CreateIsNotNull(V); 165 | } 166 | 167 | Value *BugOnPass::createIsWrap(Intrinsic::ID ID, Value *L, Value *R) { 168 | Type *T = L->getType(); 169 | assert(T == R->getType() && "Type mismatch!"); 170 | Function *F = Intrinsic::getDeclaration(getModule(), ID, T); 171 | return Builder->CreateExtractValue(Builder->CreateCall2(F, L, R), 1); 172 | } 173 | 174 | Value *BugOnPass::createIsSAddWrap(Value *L, Value *R) { 175 | return createIsWrap(Intrinsic::sadd_with_overflow, L, R); 176 | } 177 | 178 | Value *BugOnPass::createIsUAddWrap(Value *L, Value *R) { 179 | return createIsWrap(Intrinsic::uadd_with_overflow, L, R); 180 | } 181 | 182 | Value *BugOnPass::createIsSSubWrap(Value *L, Value *R) { 183 | return createIsWrap(Intrinsic::ssub_with_overflow, L, R); 184 | } 185 | 186 | Value *BugOnPass::createIsUSubWrap(Value *L, Value *R) { 187 | return createIsWrap(Intrinsic::usub_with_overflow, L, R); 188 | } 189 | 190 | Value *BugOnPass::createIsSMulWrap(Value *L, Value *R) { 191 | return createIsWrap(Intrinsic::smul_with_overflow, L, R); 192 | } 193 | 194 | Value *BugOnPass::createIsUMulWrap(Value *L, Value *R) { 195 | return createIsWrap(Intrinsic::umul_with_overflow, L, R); 196 | } 197 | 198 | Value *BugOnPass::createIsSDivWrap(Value *L, Value *R) { 199 | Type *T = L->getType(); 200 | assert(T == R->getType() && "Type mismatch!"); 201 | unsigned n = T->getIntegerBitWidth(); 202 | Constant *SMin = ConstantInt::get(T, APInt::getSignedMinValue(n)); 203 | Constant *MinusOne = Constant::getAllOnesValue(T); 204 | return createAnd( 205 | Builder->CreateICmpEQ(L, SMin), 206 | Builder->CreateICmpEQ(R, MinusOne) 207 | ); 208 | } 209 | 210 | Value *BugOnPass::createAnd(Value *L, Value *R) { 211 | if (Constant *C = dyn_cast(L)) { 212 | if (C->isAllOnesValue()) 213 | return R; 214 | if (C->isNullValue()) 215 | return L; 216 | } 217 | if (Constant *C = dyn_cast(R)) { 218 | if (C->isAllOnesValue()) 219 | return L; 220 | if (C->isNullValue()) 221 | return R; 222 | } 223 | return Builder->CreateAnd(L, R); 224 | } 225 | 226 | Value *BugOnPass::createSExtOrTrunc(Value *V, IntegerType *T) { 227 | // New in 3.2. 228 | unsigned SrcWidth = cast(V->getType())->getBitWidth(); 229 | unsigned DstWidth = T->getBitWidth(); 230 | if (SrcWidth < DstWidth) 231 | return Builder->CreateSExt(V, T); 232 | if (SrcWidth > DstWidth) 233 | return Builder->CreateTrunc(V, T); 234 | return V; 235 | } 236 | 237 | Value *BugOnPass::createPointerEQ(Value *V0, Value *V1) { 238 | if (V0 == V1) 239 | return Builder->getTrue(); 240 | return Builder->CreateICmpEQ(V0, Builder->CreatePointerCast(V1, V0->getType())); 241 | } 242 | -------------------------------------------------------------------------------- /src/BugOn.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace llvm { 9 | class DataLayout; 10 | } // namespace llvm 11 | 12 | llvm::Function *getBugOn(const llvm::Module *); 13 | llvm::Function *getOrInsertBugOn(llvm::Module *); 14 | 15 | class BugOnInst : public llvm::CallInst { 16 | typedef llvm::CallInst CallInst; 17 | typedef llvm::Function Function; 18 | typedef llvm::StringRef StringRef; 19 | typedef llvm::Value Value; 20 | public: 21 | Value *getCondition() const { return getArgOperand(0); } 22 | StringRef getAnnotation() const; 23 | 24 | // For LLVM casts. 25 | static inline bool classof(const CallInst *I) { 26 | if (const Function *F = I->getCalledFunction()) 27 | return getBugOn(F->getParent()) == F; 28 | return false; 29 | } 30 | static inline bool classof(const Value *V) { 31 | return llvm::isa(V) && classof(llvm::cast(V)); 32 | } 33 | }; 34 | 35 | struct BugOnPass : llvm::FunctionPass { 36 | typedef llvm::Value Value; 37 | typedef llvm::DataLayout DataLayout; 38 | 39 | BugOnPass(char &ID) : llvm::FunctionPass(ID), BugOn(NULL) {} 40 | 41 | virtual void getAnalysisUsage(llvm::AnalysisUsage &) const; 42 | virtual bool runOnFunction(llvm::Function &); 43 | 44 | static bool clearDebugLoc(Value *); 45 | static bool recursivelyClearDebugLoc(Value *); 46 | 47 | static Value *getUnderlyingObject(Value *, DataLayout *); 48 | static Value *getAddressOperand(Value *, bool skipVolatile = false); 49 | static Value *getNonvolatileAddressOperand(Value *V) { 50 | return getAddressOperand(V, true); 51 | } 52 | static Value *getNonvolatileBaseAddress(Value *V, DataLayout *DL) { 53 | if (Value *P = getNonvolatileAddressOperand(V)) 54 | return getUnderlyingObject(P, DL); 55 | return NULL; 56 | } 57 | 58 | protected: 59 | typedef BugOnPass super; 60 | typedef llvm::IRBuilder<> BuilderTy; 61 | typedef llvm::Instruction Instruction; 62 | BuilderTy *Builder; 63 | 64 | virtual bool runOnInstruction(Instruction *) = 0; 65 | 66 | bool insert(Value *, llvm::StringRef Bug); 67 | bool insert(Value *, llvm::StringRef Bug, const llvm::DebugLoc &); 68 | llvm::Module *getModule(); 69 | Instruction *setInsertPoint(Instruction *); 70 | Instruction *setInsertPointAfter(Instruction *); 71 | 72 | Value *createIsNull(Value *); 73 | Value *createIsNotNull(Value *); 74 | Value *createIsZero(Value *); 75 | Value *createIsWrap(llvm::Intrinsic::ID, Value *, Value *); 76 | Value *createIsSAddWrap(Value *, Value *); 77 | Value *createIsUAddWrap(Value *, Value *); 78 | Value *createIsSSubWrap(Value *, Value *); 79 | Value *createIsUSubWrap(Value *, Value *); 80 | Value *createIsSMulWrap(Value *, Value *); 81 | Value *createIsUMulWrap(Value *, Value *); 82 | Value *createIsSDivWrap(Value *, Value *); 83 | Value *createAnd(Value *, Value *); 84 | Value *createSExtOrTrunc(Value *, llvm::IntegerType *); 85 | Value *createPointerEQ(Value *, Value *); 86 | 87 | private: 88 | llvm::Function *BugOn; 89 | unsigned int MD_bug; 90 | }; 91 | -------------------------------------------------------------------------------- /src/BugOnAlias.cc: -------------------------------------------------------------------------------- 1 | // Insert bug assertions on pointer aliasing. 2 | 3 | #define DEBUG_TYPE "bugon-alias" 4 | #include "BugOn.h" 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace llvm; 10 | 11 | namespace { 12 | 13 | struct BugOnAlias : BugOnPass { 14 | static char ID; 15 | BugOnAlias() : BugOnPass(ID) { 16 | PassRegistry &Registry = *PassRegistry::getPassRegistry(); 17 | initializeDominatorTreePass(Registry); 18 | initializeDataLayoutPass(Registry); 19 | } 20 | 21 | virtual void getAnalysisUsage(AnalysisUsage &AU) const { 22 | super::getAnalysisUsage(AU); 23 | AU.addRequired(); 24 | AU.addRequired(); 25 | } 26 | virtual bool runOnFunction(Function &); 27 | 28 | virtual bool runOnInstruction(Instruction *); 29 | 30 | private: 31 | DominatorTree *DT; 32 | DataLayout *DL; 33 | SmallPtrSet Objects; 34 | 35 | void addObject(Value *); 36 | bool insertNoAlias(Value *, Value *); 37 | bool visitCallInst(CallInst *); 38 | }; 39 | 40 | } // anonymous namespace 41 | 42 | void BugOnAlias::addObject(Value *O) { 43 | if (!O->getType()->isPointerTy()) 44 | return; 45 | O = getUnderlyingObject(O, DL); 46 | Objects.insert(O); 47 | } 48 | 49 | bool BugOnAlias::runOnFunction(Function &F) { 50 | DT = &getAnalysis(); 51 | DL = &getAnalysis(); 52 | 53 | // Find all of the objects first. 54 | Objects.clear(); 55 | for (Argument &A : F.getArgumentList()) 56 | addObject(&A); 57 | for (inst_iterator i = inst_begin(F), e = inst_end(F); i != e; ++i) { 58 | Instruction *I = &*i; 59 | if (I->getDebugLoc().isUnknown()) 60 | continue; 61 | addObject(I); 62 | } 63 | 64 | return super::runOnFunction(F); 65 | } 66 | 67 | bool BugOnAlias::runOnInstruction(Instruction *I) { 68 | bool Changed = false; 69 | if (CallInst *CI = dyn_cast(I)) 70 | Changed |= visitCallInst(CI); 71 | return Changed; 72 | } 73 | 74 | // A function with the malloc or noalias attribute (e.g., malloc) 75 | // returns a pointer that does not alias any other pointer if the 76 | // returned pointer is non-null. 77 | // 78 | // Given p = malloc(...) and any pointer q, a bug condition right 79 | // after the malloc call is: 80 | // p != NULL && p == q. 81 | bool BugOnAlias::visitCallInst(CallInst *I) { 82 | if (!I->getType()->isPointerTy()) 83 | return false; 84 | // Has noalias on return. 85 | Function *F = I->getCalledFunction(); 86 | if (!F || !F->doesNotAlias(0)) 87 | return false; 88 | // Move insert point to after the noalias call. 89 | Instruction *OldIP = setInsertPointAfter(I); 90 | Value *notNull = createIsNotNull(I); 91 | for (Value *O : Objects) { 92 | if (Instruction *OI = dyn_cast(O)) { 93 | // OI needs to properly dominate I. 94 | if (OI == I || !DT->dominates(OI, I)) 95 | continue; 96 | } 97 | Value *E = createAnd(notNull, createPointerEQ(I, O)); 98 | insert(E, "noalias"); 99 | } 100 | // Restore the insert point. 101 | setInsertPoint(OldIP); 102 | return true; 103 | } 104 | 105 | char BugOnAlias::ID; 106 | 107 | static RegisterPass 108 | X("bugon-alias", "Insert bugon calls for pointer aliasing"); 109 | -------------------------------------------------------------------------------- /src/BugOnAssert.cc: -------------------------------------------------------------------------------- 1 | // This pass recognizes assertions and converts them into bugon 2 | // conditions. It also removes debugging information on "print" 3 | // blocks (e.g., from WARN) to suppress warnings. 4 | // 5 | // Examples of programmer-written assertions: 6 | // * BUG_ON() 7 | // * assert() 8 | // 9 | // Examples of compiler-inserted assertions: 10 | // * -ftrapv 11 | // * -fsanitize=undefined (clang) 12 | 13 | #define DEBUG_TYPE "bugon-assert" 14 | #include "BugOn.h" 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | using namespace llvm; 22 | 23 | namespace { 24 | 25 | struct BugOnAssert : BugOnPass { 26 | static char ID; 27 | BugOnAssert() : BugOnPass(ID) {} 28 | 29 | virtual void getAnalysisUsage(AnalysisUsage &) const { 30 | // Override. 31 | } 32 | 33 | virtual bool runOnFunction(Function &); 34 | virtual bool runOnInstruction(Instruction *) { return false; } 35 | 36 | private: 37 | DataLayout *DL; 38 | 39 | bool simplify(Function &); 40 | bool markInfiniteLoops(Function &); 41 | bool isAssertBlock(BasicBlock *); 42 | bool visitAssertBlock(BasicBlock *); 43 | bool convert(BranchInst *I, BasicBlock *BB); 44 | bool isPrintBlock(BasicBlock *); 45 | bool visitPrintBlock(BasicBlock *); 46 | }; 47 | 48 | } // anonymous namespace 49 | 50 | bool BugOnAssert::runOnFunction(Function &F) { 51 | IRBuilder<> TheBuilder(F.getContext()); 52 | Builder = &TheBuilder; 53 | DL = getAnalysisIfAvailable(); 54 | bool Changed = markInfiniteLoops(F); 55 | for (Function::iterator i = F.begin(), e = F.end(); i != e; ) { 56 | BasicBlock *BB = i++; 57 | if (isAssertBlock(BB)) { 58 | Changed |= visitAssertBlock(BB); 59 | continue; 60 | } 61 | if (isPrintBlock(BB)) { 62 | Changed |= visitPrintBlock(BB); 63 | continue; 64 | } 65 | } 66 | return Changed; 67 | } 68 | 69 | static void propagateUnreachable(BasicBlock *BB) { 70 | // pred_iterator may become invalid as we remove predecessors. 71 | // Make a copy. 72 | SmallVector Preds; 73 | for (pred_iterator i = pred_begin(BB), e = pred_end(BB); i != e; ++i) { 74 | BasicBlock *Pred = *i; 75 | TerminatorInst *TI = Pred->getTerminator(); 76 | // Unconditional jump to BB. 77 | if (TI->getNumSuccessors() == 1) 78 | Preds.push_back(Pred); 79 | } 80 | LLVMContext &C = BB->getContext(); 81 | for (unsigned i = 0, n = Preds.size(); i != n; ++i) { 82 | BasicBlock *Pred = Preds[i]; 83 | BB->removePredecessor(Pred); 84 | Pred->getTerminator()->eraseFromParent(); 85 | new UnreachableInst(C, Pred); 86 | } 87 | } 88 | 89 | bool BugOnAssert::markInfiniteLoops(Function &F) { 90 | LLVMContext &C = F.getContext(); 91 | bool Changed = false; 92 | for (Function::iterator i = F.begin(), e = F.end(); i != e; ++i) { 93 | BasicBlock *BB = i; 94 | TerminatorInst *TI = BB->getTerminator(); 95 | // Jump to itself. 96 | if (TI->getNumSuccessors() != 1 || TI->getSuccessor(0) != BB) 97 | continue; 98 | BB->removePredecessor(BB); 99 | new UnreachableInst(C, BB); 100 | TI->eraseFromParent(); 101 | propagateUnreachable(BB); 102 | Changed = true; 103 | } 104 | return Changed; 105 | } 106 | 107 | bool BugOnAssert::isAssertBlock(BasicBlock *BB) { 108 | TerminatorInst *TI = BB->getTerminator(); 109 | // Cannot be return or anything other than unreachable/br. 110 | if (!isa(TI) && !isa(TI)) 111 | return false; 112 | unsigned NumSucc = TI->getNumSuccessors(); 113 | // A single unreachable? 114 | if (TI == &BB->front()) 115 | return NumSucc == 0; 116 | // Check the last instruction before the terminator. 117 | // Cannot be invoke (which must be a terminator); 118 | CallInst *CI = dyn_cast(--BasicBlock::iterator(TI)); 119 | // Block ends with non-call. 120 | if (!CI) 121 | return false; 122 | StringRef Name; 123 | if (Function *F = CI->getCalledFunction()) 124 | Name = F->getName(); 125 | if (NumSucc) { 126 | // -fsanitize=undefined. 127 | if (Name.startswith("__ubsan_handle_")) 128 | return true; 129 | return false; 130 | } 131 | // From now on BB must be unreachable. 132 | // Inline asm? Simply return true for now (look for ud2?). 133 | if (CI->isInlineAsm()) 134 | return true; 135 | // Then function pointer? 136 | if (Name.empty()) 137 | return false; 138 | if (Name.find("assert") != StringRef::npos) 139 | return true; 140 | if (Name.find("panic") != StringRef::npos) 141 | return true; 142 | // -ftrapv. 143 | if (Name == "llvm.trap") 144 | return true; 145 | return false; 146 | } 147 | 148 | bool BugOnAssert::visitAssertBlock(BasicBlock *BB) { 149 | SmallVector CondBrs; 150 | for (pred_iterator i = pred_begin(BB), e = pred_end(BB); i != e; ++i) { 151 | BasicBlock *Pred = *i; 152 | BranchInst *BI = dyn_cast(Pred->getTerminator()); 153 | if (!BI) 154 | continue; 155 | if (!BI->isConditional()) 156 | continue; 157 | CondBrs.push_back(BI); 158 | } 159 | if (CondBrs.empty()) 160 | return false; 161 | for (unsigned i = 0, n = CondBrs.size(); i != n; ++i) { 162 | BranchInst *BI = CondBrs[i]; 163 | BasicBlock *Pred = BI->getParent(); 164 | // Remove possible phi nodes. 165 | BB->removePredecessor(Pred); 166 | convert(BI, BB); 167 | } 168 | return true; 169 | } 170 | 171 | bool BugOnAssert::convert(BranchInst *I, BasicBlock *BB) { 172 | // Convert the branch condition into bugon. 173 | BasicBlock *TrueBB = I->getSuccessor(0); 174 | BasicBlock *FalseBB = I->getSuccessor(1); 175 | Value *Cond = I->getCondition(); 176 | setInsertPoint(I); 177 | if (TrueBB == BB) { 178 | insert(Cond, "assert"); 179 | Builder->CreateBr(FalseBB); 180 | } else { 181 | assert(FalseBB == BB); 182 | insert(Builder->CreateNot(Cond), "assert"); 183 | Builder->CreateBr(TrueBB); 184 | } 185 | I->eraseFromParent(); 186 | // Prevent anti-simplify from inspecting the condition. 187 | recursivelyClearDebugLoc(Cond); 188 | return true; 189 | } 190 | 191 | bool BugOnAssert::isPrintBlock(BasicBlock *BB) { 192 | bool hasPrint = false; 193 | for (BasicBlock::iterator i = BB->begin(), e = BB->end(); i != e; ++i) { 194 | CallSite CS(i); 195 | if (!CS) 196 | continue; 197 | Function *F = CS.getCalledFunction(); 198 | // A function pointer. 199 | if (!F) 200 | return false; 201 | StringRef Name = F->getName(); 202 | if (Name.find("print") != StringRef::npos) { 203 | hasPrint = true; 204 | continue; 205 | } 206 | // Ignore constant functions. 207 | if (CS.onlyReadsMemory()) 208 | continue; 209 | // Ignore calls to debugging intrinsics. 210 | if (isSafeToSpeculativelyExecute(i, DL)) 211 | continue; 212 | // Unknown function. 213 | return false; 214 | } 215 | return hasPrint; 216 | } 217 | 218 | bool BugOnAssert::visitPrintBlock(BasicBlock *BB) { 219 | // Clear debugging information in this BB to suppress warnings. 220 | for (BasicBlock::iterator i = BB->begin(), e = BB->end(); i != e; ++i) 221 | clearDebugLoc(i); 222 | for (pred_iterator i = pred_begin(BB), e = pred_end(BB); i != e; ++i) { 223 | BasicBlock *Pred = *i; 224 | BranchInst *BI = dyn_cast(Pred->getTerminator()); 225 | if (!BI || !BI->isConditional()) 226 | continue; 227 | // Prevent anti-simplify from inspecting the condition. 228 | recursivelyClearDebugLoc(BI->getCondition()); 229 | } 230 | return true; 231 | } 232 | 233 | char BugOnAssert::ID; 234 | 235 | static RegisterPass 236 | X("bugon-assert", "Insert bugon calls from user assertions"); 237 | -------------------------------------------------------------------------------- /src/BugOnBounds.cc: -------------------------------------------------------------------------------- 1 | #define DEBUG_TYPE "bugon-bounds" 2 | #include "BugOn.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace llvm; 9 | 10 | namespace { 11 | 12 | struct BugOnBounds : BugOnPass { 13 | static char ID; 14 | BugOnBounds() : BugOnPass(ID) { 15 | PassRegistry &Registry = *PassRegistry::getPassRegistry(); 16 | initializeDataLayoutPass(Registry); 17 | initializeTargetLibraryInfoPass(Registry); 18 | } 19 | 20 | virtual void getAnalysisUsage(AnalysisUsage &AU) const { 21 | super::getAnalysisUsage(AU); 22 | AU.addRequired(); 23 | AU.addRequired(); 24 | } 25 | 26 | virtual bool runOnFunction(Function &); 27 | virtual bool runOnInstruction(Instruction *); 28 | 29 | private: 30 | DataLayout *DL; 31 | TargetLibraryInfo *TLI; 32 | ObjectSizeOffsetEvaluator *ObjSizeEval; 33 | }; 34 | 35 | } // anonymous namespace 36 | 37 | bool BugOnBounds::runOnFunction(llvm::Function &F) { 38 | DL = &getAnalysis(); 39 | TLI = &getAnalysis(); 40 | ObjectSizeOffsetEvaluator TheObjSizeEval(DL, TLI, F.getContext()); 41 | ObjSizeEval = &TheObjSizeEval; 42 | return super::runOnFunction(F); 43 | } 44 | 45 | bool BugOnBounds::runOnInstruction(Instruction *I) { 46 | Value *Ptr = getNonvolatileAddressOperand(I); 47 | if (!Ptr) 48 | return false; 49 | SizeOffsetEvalType SizeOffset = ObjSizeEval->compute(Ptr); 50 | if (!ObjSizeEval->bothKnown(SizeOffset)) 51 | return false; 52 | Value *Size = SizeOffset.first; 53 | Value *Offset = SizeOffset.second; 54 | Type *T = Offset->getType(); 55 | assert(T == Size->getType()); 56 | Type *ElemTy = cast(Ptr->getType())->getElementType(); 57 | Value *StoreSize = ConstantInt::get(T, DL->getTypeStoreSize(ElemTy)); 58 | // Bug condition: Offset < 0. 59 | { 60 | Value *V = Builder->CreateICmpSLT(Offset, Constant::getNullValue(T)); 61 | insert(V, "buffer overflow"); 62 | } 63 | // Bug condition: Offset > Size. 64 | { 65 | Value *V = Builder->CreateICmpUGT(Offset, Size); 66 | insert(V, "buffer overflow"); 67 | } 68 | // Bug condition: Size - Offset < StoreSize. 69 | { 70 | Value *V = Builder->CreateICmpULT(Builder->CreateSub(Size, Offset), StoreSize); 71 | insert(V, "buffer overflow"); 72 | } 73 | return true; 74 | } 75 | 76 | char BugOnBounds::ID; 77 | 78 | static RegisterPass 79 | X("bugon-bounds", "Insert bugon calls for bounds checking"); 80 | -------------------------------------------------------------------------------- /src/BugOnFree.cc: -------------------------------------------------------------------------------- 1 | // Insert bug assertions on use-after-free. 2 | 3 | #define DEBUG_TYPE "bugon-free" 4 | #include "BugOn.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | using namespace llvm; 13 | 14 | namespace { 15 | 16 | struct BugOnFree : BugOnPass { 17 | static char ID; 18 | BugOnFree() : BugOnPass(ID) { 19 | PassRegistry &Registry = *PassRegistry::getPassRegistry(); 20 | initializeDataLayoutPass(Registry); 21 | initializeTargetLibraryInfoPass(Registry); 22 | initializeDominatorTreePass(Registry); 23 | } 24 | 25 | virtual void getAnalysisUsage(AnalysisUsage &AU) const { 26 | super::getAnalysisUsage(AU); 27 | AU.addRequired(); 28 | AU.addRequired(); 29 | AU.addRequired(); 30 | } 31 | 32 | virtual bool runOnFunction(Function &); 33 | virtual bool runOnInstruction(Instruction *); 34 | 35 | private: 36 | DataLayout *DL; 37 | TargetLibraryInfo *TLI; 38 | DominatorTree *DT; 39 | SmallPtrSet FreePtrs; // Use is a pair. 40 | 41 | Use *extractFree(CallSite CS); 42 | }; 43 | 44 | } // anonymous namespace 45 | 46 | bool BugOnFree::runOnFunction(Function &F) { 47 | DT = &getAnalysis(); 48 | TLI = &getAnalysis(); 49 | DL = &getAnalysis(); 50 | // Collect free/realloc calls. 51 | FreePtrs.clear(); 52 | for (inst_iterator i = inst_begin(F), e = inst_end(F); i != e; ++i) { 53 | Instruction &I = *i; 54 | if (I.getDebugLoc().isUnknown()) 55 | continue; 56 | CallSite CS(&I); 57 | if (!CS || !CS.getCalledFunction()) 58 | continue; 59 | if (Use *U = extractFree(CS)) { 60 | FreePtrs.insert(U); 61 | continue; 62 | } 63 | } 64 | return super::runOnFunction(F); 65 | } 66 | 67 | bool BugOnFree::runOnInstruction(Instruction *I) { 68 | if (FreePtrs.empty()) 69 | return false; 70 | 71 | Value *P = getNonvolatileBaseAddress(I, DL); 72 | if (!P) 73 | return false; 74 | 75 | bool Changed = false; 76 | // free(x): x == p (p must be nonnull). 77 | for (Use *U : FreePtrs) { 78 | Instruction *FreeCall = cast(U->getUser()); 79 | if (!DT->dominates(FreeCall, I)) 80 | continue; 81 | Value *X = U->get(); 82 | Value *V = createPointerEQ(X, P); 83 | // x' = realloc(x, n): x == p && x' != null. 84 | if (FreeCall->getType()->isPointerTy()) 85 | V = createAnd(createIsNotNull(FreeCall), V); 86 | StringRef Name = CallSite(FreeCall).getCalledFunction()->getName(); 87 | Changed |= insert(V, Name, FreeCall->getDebugLoc()); 88 | } 89 | return Changed; 90 | } 91 | 92 | Use *BugOnFree::extractFree(CallSite CS) { 93 | #define P std::make_pair 94 | static std::pair Frees[] = { 95 | P("kfree", 0), 96 | P("vfree", 0), 97 | P("__kfree_skb", 0), 98 | }; 99 | #undef P 100 | // Builtin free/delete/realloc. 101 | Instruction *I = CS.getInstruction(); 102 | if (isFreeCall(I, TLI) || isReallocLikeFn(I, TLI)) 103 | return CS.arg_begin(); 104 | // Custom function. 105 | StringRef Name = CS.getCalledFunction()->getName(); 106 | for (unsigned i = 0; i < sizeof(Frees) / sizeof(Frees[0]); i++) { 107 | if (Name == Frees[i].first) 108 | return CS.arg_begin() + Frees[i].second; 109 | } 110 | return NULL; 111 | 112 | } 113 | 114 | char BugOnFree::ID; 115 | 116 | static RegisterPass 117 | X("bugon-free", "Insert bugon calls for freed pointers"); 118 | -------------------------------------------------------------------------------- /src/BugOnGep.cc: -------------------------------------------------------------------------------- 1 | // Insert bug assertions on pointer arithmetic. 2 | 3 | #define DEBUG_TYPE "bugon-gep" 4 | #include "BugOn.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace llvm; 12 | 13 | namespace { 14 | 15 | struct BugOnGep : BugOnPass { 16 | static char ID; 17 | BugOnGep() : BugOnPass(ID) {} 18 | 19 | virtual void getAnalysisUsage(AnalysisUsage &AU) const { 20 | super::getAnalysisUsage(AU); 21 | AU.addRequired(); 22 | AU.addRequired(); 23 | } 24 | virtual bool runOnFunction(Function &); 25 | 26 | virtual bool runOnInstruction(Instruction *); 27 | 28 | private: 29 | DataLayout *DL; 30 | TargetLibraryInfo *TLI; 31 | 32 | bool insertIndexOverflow(GEPOperator *GEP); 33 | bool insertOffsetOverflow(GEPOperator *GEP); 34 | bool isAlwaysInBounds(Value *P, Value *Offset); 35 | }; 36 | 37 | } // anonymous namespace 38 | 39 | bool BugOnGep::runOnFunction(Function &F) { 40 | DL = &getAnalysis(); 41 | TLI = &getAnalysis(); 42 | return super::runOnFunction(F); 43 | } 44 | 45 | bool BugOnGep::runOnInstruction(Instruction *I) { 46 | GEPOperator *GEP = dyn_cast(I); 47 | if (!GEP || !GEP->isInBounds()) 48 | return false; 49 | // Ignore zero index. 50 | if (GEP->hasAllZeroIndices()) 51 | return false; 52 | bool Changed = false; 53 | Changed |= insertIndexOverflow(GEP); 54 | Changed |= insertOffsetOverflow(GEP); 55 | return Changed; 56 | } 57 | 58 | bool BugOnGep::insertIndexOverflow(GEPOperator *GEP) { 59 | bool Changed = false; 60 | #if 0 61 | LLVMContext &VMCtx = GEP->getContext(); 62 | unsigned PtrBits = DL->getPointerSizeInBits(/*GEP->getPointerAddressSpace()*/); 63 | IntegerType *PtrIntTy = Type::getIntNTy(VMCtx, PtrBits); 64 | gep_type_iterator GTI = gep_type_begin(GEP); 65 | for (GEPOperator::op_iterator i = GEP->idx_begin(), 66 | e = GEP->idx_end(); i != e; ++i, ++GTI) { 67 | if (isa(*GTI)) 68 | continue; 69 | Type *IndexedTy = GTI.getIndexedType(); 70 | if (!IndexedTy->isSized()) 71 | continue; 72 | Value *Size = ConstantInt::get(PtrIntTy, DL->getTypeAllocSize(IndexedTy)); 73 | Value *Index = createSExtOrTrunc(*i, PtrIntTy); 74 | // Bug condition: size * index overflows. 75 | Value *V = createIsSMulWrap(Size, Index); 76 | Changed |= insert(V, "pointer overflow"); 77 | } 78 | #endif 79 | return Changed; 80 | } 81 | 82 | bool BugOnGep::insertOffsetOverflow(GEPOperator *GEP) { 83 | // Ignore struct offset, which is likely to be in range. 84 | for (gep_type_iterator GTI = gep_type_begin(GEP), E = gep_type_end(GEP); GTI != E; ++GTI) { 85 | if (isa(*GTI)) 86 | return false; 87 | } 88 | Value *P = GEP->getPointerOperand(); 89 | Value *Offset = EmitGEPOffset(Builder, *DL, GEP); 90 | if (isAlwaysInBounds(P, Offset)) { 91 | RecursivelyDeleteTriviallyDeadInstructions(Offset, TLI); 92 | return false; 93 | } 94 | unsigned PtrBits = DL->getPointerSizeInBits(GEP->getPointerAddressSpace()); 95 | LLVMContext &VMCtx = GEP->getContext(); 96 | // Extend to n + 1 bits to avoid overflowing ptr + offset. 97 | IntegerType *PtrIntExTy = Type::getIntNTy(VMCtx, PtrBits + 1); 98 | Value *End = Builder->CreateAdd( 99 | Builder->CreatePtrToInt(P, PtrIntExTy), 100 | createSExtOrTrunc(Offset, PtrIntExTy) 101 | ); 102 | bool Changed = false; 103 | // Bug condition: ptr + offset > uintptr_max. 104 | { 105 | Value *PtrMax = ConstantInt::get(VMCtx, APInt::getMaxValue(PtrBits).zext(PtrBits + 1)); 106 | Value *V = Builder->CreateICmpSGT(End, PtrMax); 107 | Changed |= insert(V, "pointer overflow"); 108 | } 109 | // Bug condition: ptr + offset < 0. 110 | { 111 | Value *V = Builder->CreateICmpSLT(End, Constant::getNullValue(PtrIntExTy)); 112 | Changed |= insert(V, "pointer overflow"); 113 | } 114 | if (!Changed) 115 | RecursivelyDeleteTriviallyDeadInstructions(End, TLI); 116 | return Changed; 117 | } 118 | 119 | // Ignore p + foo(p, ...) that is always in-bounds. 120 | bool BugOnGep::isAlwaysInBounds(Value *P, Value *Offset) { 121 | // n + 0 => n. 122 | if (auto I = dyn_cast(Offset)) 123 | Offset = SimplifyInstruction(I, DL, TLI); 124 | auto CI = dyn_cast(Offset); 125 | if (!CI) 126 | return false; 127 | auto Callee = CI->getCalledFunction(); 128 | if (!Callee) 129 | return false; 130 | LibFunc::Func F; 131 | if (!TLI->getLibFunc(Callee->getName(), F) || !TLI->has(F)) 132 | return false; 133 | Value *S; 134 | switch (F) { 135 | default: 136 | return false; 137 | case LibFunc::fwrite: 138 | case LibFunc::strcspn: 139 | case LibFunc::strlen: 140 | case LibFunc::strnlen: 141 | case LibFunc::strspn: 142 | S = CI->getArgOperand(0); 143 | break; 144 | } 145 | S = S->stripPointerCasts(); 146 | if (S == P->stripPointerCasts()) 147 | return true; 148 | return false; 149 | } 150 | 151 | char BugOnGep::ID; 152 | 153 | static RegisterPass 154 | X("bugon-gep", "Insert bugon calls for pointer arithmetic"); 155 | -------------------------------------------------------------------------------- /src/BugOnInt.cc: -------------------------------------------------------------------------------- 1 | #define DEBUG_TYPE "bugon-int" 2 | #include "BugOn.h" 3 | #include 4 | #include 5 | 6 | using namespace llvm; 7 | 8 | namespace { 9 | 10 | struct BugOnInt : BugOnPass { 11 | static char ID; 12 | BugOnInt() : BugOnPass(ID) {} 13 | virtual bool runOnInstruction(Instruction *); 14 | private: 15 | bool visitShiftOperator(IntegerType *, Value *R, const char *Bug); 16 | }; 17 | 18 | } // anonymous namespace 19 | 20 | bool BugOnInt::runOnInstruction(Instruction *I) { 21 | BinaryOperator *BO = dyn_cast(I); 22 | if (!BO) 23 | return false; 24 | IntegerType *T = dyn_cast(I->getType()); 25 | if (!T) 26 | return false; 27 | Value *L = I->getOperand(0); 28 | Value *R = I->getOperand(1); 29 | bool Changed = false; 30 | switch (BO->getOpcode()) { 31 | default: break; 32 | case Instruction::Add: 33 | if (BO->hasNoSignedWrap()) 34 | Changed |= insert(createIsSAddWrap(L, R), "signed addition overflow"); 35 | if (BO->hasNoUnsignedWrap()) 36 | Changed |= insert(createIsUAddWrap(L, R), "unsigned addition overflow"); 37 | break; 38 | case Instruction::Sub: 39 | if (BO->hasNoSignedWrap()) 40 | Changed |= insert(createIsSSubWrap(L, R), "signed subtraction overflow"); 41 | if (BO->hasNoUnsignedWrap()) 42 | Changed |= insert(createIsUSubWrap(L, R), "unsigned subtraction overflow"); 43 | break; 44 | case Instruction::Mul: 45 | if (BO->hasNoSignedWrap()) 46 | Changed |= insert(createIsSMulWrap(L, R), "signed multiplication overflow"); 47 | if (BO->hasNoUnsignedWrap()) 48 | Changed |= insert(createIsUMulWrap(L, R), "unsigned multiplication overflow"); 49 | break; 50 | case Instruction::SDiv: 51 | case Instruction::SRem: 52 | Changed |= insert(createIsSDivWrap(L, R), "signed division overflow"); 53 | // Fall through. 54 | case Instruction::UDiv: 55 | case Instruction::URem: 56 | Changed |= insert(createIsZero(R), "division by zero"); 57 | break; 58 | case Instruction::Shl: 59 | Changed |= visitShiftOperator(T, R, "shift left overflow"); 60 | if (BO->hasNoSignedWrap()) { 61 | Value *Power = Builder->CreateShl(ConstantInt::get(T, 1), R); 62 | Changed |= insert(createIsSMulWrap(L, Power), "signed shift left overflow"); 63 | } 64 | if (BO->hasNoUnsignedWrap()) { 65 | Value *Power = Builder->CreateShl(ConstantInt::get(T, 1), R); 66 | Changed |= insert(createIsUMulWrap(L, Power), "unsigned shift left overflow"); 67 | } 68 | break; 69 | case Instruction::LShr: 70 | Changed |= visitShiftOperator(T, R, "logical shift right overflow"); 71 | break; 72 | case Instruction::AShr: 73 | Changed |= visitShiftOperator(T, R, "arithmetic shift right overflow"); 74 | break; 75 | } 76 | return Changed; 77 | } 78 | 79 | bool BugOnInt::visitShiftOperator(IntegerType *T, Value *R, const char *Bug) { 80 | Constant *BitWidth = ConstantInt::get(T, T->getBitWidth()); 81 | Value *V = Builder->CreateICmpUGE(R, BitWidth); 82 | return insert(V, Bug); 83 | } 84 | 85 | char BugOnInt::ID; 86 | 87 | static RegisterPass 88 | X("bugon-int", "Insert bugon calls for integer operations"); 89 | -------------------------------------------------------------------------------- /src/BugOnLibc.cc: -------------------------------------------------------------------------------- 1 | #define DEBUG_TYPE "bugon-libc" 2 | #include "BugOn.h" 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace llvm; 8 | 9 | namespace { 10 | 11 | struct BugOnLibc : BugOnPass { 12 | static char ID; 13 | BugOnLibc() : BugOnPass(ID) {} 14 | 15 | virtual bool doInitialization(Module &); 16 | virtual bool runOnInstruction(Instruction *); 17 | 18 | private: 19 | typedef bool (BugOnLibc::*handler_t)(CallInst *); 20 | DenseMap FunctionMap; 21 | DenseMap IntrinsicMap; 22 | 23 | bool visitAbs(CallInst *); 24 | bool visitCtz(CallInst *); 25 | bool visitDiv(CallInst *); 26 | bool visitMemcpy(CallInst *); 27 | }; 28 | 29 | } // anonymous namespace 30 | 31 | bool BugOnLibc::doInitialization(Module &M) { 32 | #define HANDLER(method, name) \ 33 | if (Function *F = M.getFunction(name)) \ 34 | FunctionMap[F] = &BugOnLibc::method; 35 | 36 | HANDLER(visitAbs, "abs"); 37 | HANDLER(visitAbs, "labs"); 38 | HANDLER(visitAbs, "llabs"); 39 | HANDLER(visitAbs, "imaxabs"); 40 | 41 | HANDLER(visitDiv, "div"); 42 | HANDLER(visitDiv, "ldiv"); 43 | HANDLER(visitDiv, "lldiv"); 44 | HANDLER(visitDiv, "imaxdiv"); 45 | 46 | HANDLER(visitMemcpy, "memcpy"); 47 | HANDLER(visitMemcpy, "__memcpy"); // Linux kernel internal 48 | 49 | #undef HANDLER 50 | 51 | #define HANDLER(method, id) \ 52 | IntrinsicMap[id] = &BugOnLibc::method; 53 | 54 | HANDLER(visitMemcpy, Intrinsic::memcpy); 55 | HANDLER(visitCtz, Intrinsic::ctlz); 56 | HANDLER(visitCtz, Intrinsic::cttz); 57 | 58 | #undef HANDLER 59 | return false; 60 | } 61 | 62 | bool BugOnLibc::runOnInstruction(Instruction *I) { 63 | CallInst *CI = dyn_cast(I); 64 | if (!CI || isa(CI)) 65 | return false; 66 | Function *F = CI->getCalledFunction(); 67 | if (!F) 68 | return false; 69 | handler_t Handler = NULL; 70 | if (F->isIntrinsic()) { 71 | Handler = IntrinsicMap.lookup(F->getIntrinsicID()); 72 | } else { 73 | Handler = FunctionMap.lookup(F); 74 | } 75 | if (!Handler) 76 | return false; 77 | return (this->*Handler)(CI); 78 | } 79 | 80 | // abs(x): x == INT_MIN 81 | bool BugOnLibc::visitAbs(CallInst *I) { 82 | if (I->getNumArgOperands() != 1) 83 | return false; 84 | Value *R = I->getArgOperand(0); 85 | IntegerType *T = dyn_cast(R->getType()); 86 | if (!T || I->getType() != T) 87 | return false; 88 | Constant *SMin = ConstantInt::get(T, APInt::getSignedMinValue(T->getBitWidth())); 89 | Value *V = Builder->CreateICmpEQ(R, SMin); 90 | insert(V, I->getCalledFunction()->getName()); 91 | Value *IsNeg = Builder->CreateICmpSLT(R, ConstantInt::get(T, 0)); 92 | Value *Abs = Builder->CreateSelect(IsNeg, Builder->CreateNeg(R), R); 93 | I->replaceAllUsesWith(Abs); 94 | return true; 95 | } 96 | 97 | bool BugOnLibc::visitCtz(CallInst *I) { 98 | if (I->getNumArgOperands() != 2) 99 | return false; 100 | Value *X = I->getArgOperand(0); 101 | // Skip vectors for now. 102 | if (!X->getType()->isIntegerTy()) 103 | return false; 104 | // Test if zero is undef. 105 | ConstantInt *IsZeroUndef = dyn_cast(I->getArgOperand(1)); 106 | if (!IsZeroUndef || IsZeroUndef->isZero()) 107 | return false; 108 | Value *V = createIsZero(X); 109 | StringRef Name = I->getCalledFunction()->getName(); 110 | if (Name.startswith("llvm.")) 111 | Name = Name.substr(5); 112 | Name = Name.split('.').first; 113 | return insert(V, Name); 114 | } 115 | 116 | // div(numer, denom): denom == 0 || (numer == INT_MIN && denom == -1) 117 | bool BugOnLibc::visitDiv(CallInst *I) { 118 | if (I->getNumArgOperands() != 2) 119 | return false; 120 | Value *L = I->getArgOperand(0); 121 | Value *R = I->getArgOperand(1); 122 | IntegerType *T = dyn_cast(I->getType()); 123 | if (!T) 124 | return false; 125 | if (T != L->getType() || T != R->getType()) 126 | return false; 127 | StringRef Name = I->getCalledFunction()->getName(); 128 | insert(createIsZero(R), Name); 129 | Constant *SMin = ConstantInt::get(T, APInt::getSignedMinValue(T->getBitWidth())); 130 | Constant *MinusOne = Constant::getAllOnesValue(T); 131 | Value *V = createAnd( 132 | Builder->CreateICmpEQ(L, SMin), 133 | Builder->CreateICmpEQ(R, MinusOne) 134 | ); 135 | insert(V, Name); 136 | Value *SDiv = Builder->CreateSDiv(L, R); 137 | I->replaceAllUsesWith(SDiv); 138 | return true; 139 | } 140 | 141 | // memcpy(a, b, n): a-bgetNumArgOperands() < 3) 144 | return false; 145 | Value *A = I->getArgOperand(0); 146 | Value *B = I->getArgOperand(1); 147 | Value *N = I->getArgOperand(2); 148 | Value *AN = Builder->CreatePointerCast(A, N->getType()); 149 | Value *BN = Builder->CreatePointerCast(B, N->getType()); 150 | Value *AminusB = Builder->CreateSub(AN, BN); 151 | Value *BminusA = Builder->CreateSub(BN, AN); 152 | 153 | /* 154 | * Allow memcpy(a, a, n), for two reasons: 155 | * - in practice, memcpy implementations don't get this wrong, and 156 | * - Clang generates memcpy instrinsic calls for self-assignment. 157 | */ 158 | insert(createAnd( 159 | Builder->CreateICmpULT(AminusB, N), 160 | Builder->CreateICmpNE(AN, BN) 161 | ), "memcpy"); 162 | insert(createAnd( 163 | Builder->CreateICmpULT(BminusA, N), 164 | Builder->CreateICmpNE(AN, BN) 165 | ), "memcpy"); 166 | return true; 167 | } 168 | 169 | char BugOnLibc::ID; 170 | 171 | static RegisterPass 172 | X("bugon-libc", "Insert bugon calls for libc functions"); 173 | -------------------------------------------------------------------------------- /src/BugOnLinux.cc: -------------------------------------------------------------------------------- 1 | #define DEBUG_TYPE "bugon-linux" 2 | #include "BugOn.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace llvm; 9 | 10 | namespace { 11 | 12 | struct BugOnLinux : BugOnPass { 13 | static char ID; 14 | BugOnLinux() : BugOnPass(ID) {} 15 | 16 | virtual bool doInitialization(Module &); 17 | virtual bool runOnInstruction(Instruction *); 18 | 19 | private: 20 | typedef bool (BugOnLinux::*handler_t)(CallInst *); 21 | DenseMap Handlers; 22 | 23 | bool visitDmaPoolCreate(CallInst *); 24 | bool visitDupUser(CallInst *); 25 | bool visitFfz(CallInst *); 26 | }; 27 | 28 | } // anonymous namespace 29 | 30 | bool BugOnLinux::doInitialization(Module &M) { 31 | #define HANDLER(method, name) \ 32 | if (Function *F = M.getFunction(name)) \ 33 | Handlers[F] = &BugOnLinux::method; 34 | 35 | #if 0 36 | // *dup_user 37 | HANDLER(visitDupUser, "memdup_user"); 38 | HANDLER(visitDupUser, "strndup_user"); 39 | 40 | HANDLER(visitDmaPoolCreate, "dma_pool_create"); 41 | #endif 42 | 43 | // bitops 44 | HANDLER(visitFfz, "ffz"); 45 | 46 | #undef HANDLER 47 | return false; 48 | } 49 | 50 | bool BugOnLinux::runOnInstruction(Instruction *I) { 51 | CallInst *CI = dyn_cast(I); 52 | if (!CI) 53 | return false; 54 | Function *F = CI->getCalledFunction(); 55 | if (!F) 56 | return false; 57 | handler_t Handler = Handlers.lookup(F); 58 | if (!Handler) 59 | return false; 60 | return (this->*Handler)(CI); 61 | } 62 | 63 | bool BugOnLinux::visitDupUser(CallInst *I) { 64 | if (!isa(I->getType())) 65 | return false; 66 | Instruction *OldIP = setInsertPointAfter(I); 67 | Value *V = createIsNull(I); 68 | insert(V, I->getCalledFunction()->getName()); 69 | setInsertPoint(OldIP); 70 | return true; 71 | } 72 | 73 | // dma_pool_create(name, dev, size, align, allocation): dev == null 74 | bool BugOnLinux::visitDmaPoolCreate(CallInst *I) { 75 | if (I->getNumArgOperands() != 5) 76 | return false; 77 | Value *Dev = I->getArgOperand(1); 78 | if (!isa(Dev->getType())) 79 | return false; 80 | Value *V = createIsNull(Dev); 81 | return insert(V, I->getCalledFunction()->getName()); 82 | } 83 | 84 | // ffz(x): cttz(~x). 85 | // x cannot be ~0. 86 | bool BugOnLinux::visitFfz(CallInst *I) { 87 | if (I->getNumArgOperands() != 1) 88 | return false; 89 | IntegerType *T = dyn_cast(I->getType()); 90 | if (!T) 91 | return false; 92 | Value *R = I->getArgOperand(0); 93 | if (T != R->getType()) 94 | return false; 95 | Value *NotR = Builder->CreateNot(R); 96 | Value *Pre = createIsZero(NotR); 97 | insert(Pre, I->getCalledFunction()->getName()); 98 | Function *F = Intrinsic::getDeclaration(getModule(), Intrinsic::cttz, T); 99 | CallInst *NewInst = Builder->CreateCall2(F, NotR, Builder->getTrue()); 100 | Value *Post = Builder->CreateICmpUGE(NewInst, ConstantInt::get(T, T->getBitWidth())); 101 | insert(Post, I->getCalledFunction()->getName()); 102 | I->replaceAllUsesWith(NewInst); 103 | I->eraseFromParent(); 104 | return true; 105 | } 106 | 107 | char BugOnLinux::ID; 108 | 109 | static RegisterPass 110 | X("bugon-linux", "Insert bugon calls for Linux API"); 111 | -------------------------------------------------------------------------------- /src/BugOnLoop.cc: -------------------------------------------------------------------------------- 1 | // This pass tries to move bugon calls out of loops. It should run 2 | // after other bugon passes. 3 | 4 | #define DEBUG_TYPE "bugon-loop" 5 | #include "BugOn.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | using namespace llvm; 13 | 14 | namespace { 15 | 16 | struct BugOnLoop : BugOnPass, InstVisitor { 17 | static char ID; 18 | BugOnLoop() : BugOnPass(ID) { 19 | PassRegistry &Registry = *PassRegistry::getPassRegistry(); 20 | initializeLoopInfoPass(Registry); 21 | initializeScalarEvolutionPass(Registry); 22 | } 23 | 24 | virtual void getAnalysisUsage(AnalysisUsage &AU) const { 25 | super::getAnalysisUsage(AU); 26 | AU.addRequired(); 27 | AU.addRequired(); 28 | } 29 | 30 | virtual bool runOnFunction(Function &); 31 | 32 | virtual bool runOnInstruction(Instruction *); 33 | 34 | // InstVisitor. 35 | Value *visitInstruction(Instruction &); 36 | Value *visitBinaryOperator(BinaryOperator &); 37 | Value *visitICmpInst(ICmpInst &); 38 | Value *visitSelectInst(SelectInst &); 39 | Value *visitExtractValueInst(ExtractValueInst &); 40 | 41 | private: 42 | LoopInfo *LI; 43 | ScalarEvolution *SE; 44 | const Loop *Scope; 45 | 46 | Value *computeValueAtScope(Value *V, const Loop *L) { 47 | Scope = L; 48 | return compute(V); 49 | } 50 | 51 | Value *compute(Value *V) { 52 | if (Instruction *I = dyn_cast(V)) 53 | return visit(I); 54 | if (isa(V)) 55 | return V; 56 | return NULL; 57 | } 58 | }; 59 | 60 | } // anonymous namespace 61 | 62 | bool BugOnLoop::runOnFunction(Function &F) { 63 | LI = &getAnalysis(); 64 | SE = &getAnalysis(); 65 | return super::runOnFunction(F); 66 | } 67 | 68 | bool BugOnLoop::runOnInstruction(Instruction *I) { 69 | BugOnInst *BOI = dyn_cast(I); 70 | if (!BOI) 71 | return false; 72 | const Loop *L = LI->getLoopFor(I->getParent()); 73 | if (!L) 74 | return false; 75 | BasicBlock *ExitBlock = L->getExitBlock(); 76 | if (!ExitBlock) 77 | return false; 78 | Instruction *IP = setInsertPoint(ExitBlock->getFirstInsertionPt()); 79 | // We could use SCEV's computeSCEVAtScope. The problem is that SCEV 80 | // cannot represent many instructions (e.g., comparisons and overflow 81 | // intrinsics). So just do it ourselves and only use SCEV for values 82 | // we cannot deal with (e.g., phi). 83 | Value *ExitValue = computeValueAtScope(BOI->getCondition(), L->getParentLoop()); 84 | if (ExitValue) 85 | insert(ExitValue, BOI->getAnnotation(), BOI->getDebugLoc()); 86 | setInsertPoint(IP); 87 | return true; 88 | } 89 | 90 | Value *BugOnLoop::visitInstruction(Instruction &I) { 91 | const SCEV *S = SE->getSCEVAtScope(&I, Scope); 92 | if (!SE->isLoopInvariant(S, Scope)) 93 | return NULL; 94 | SCEVExpander Expander(*SE, ""); 95 | return Expander.expandCodeFor(S, I.getType(), Builder->GetInsertPoint()); 96 | } 97 | 98 | Value *BugOnLoop::visitBinaryOperator(BinaryOperator &I) { 99 | Value *L = compute(I.getOperand(0)); 100 | if (!L) 101 | return NULL; 102 | Value *R = compute(I.getOperand(1)); 103 | if (!R) 104 | return NULL; 105 | return Builder->CreateBinOp(I.getOpcode(), L, R); 106 | } 107 | 108 | Value *BugOnLoop::visitICmpInst(ICmpInst &I) { 109 | Value *L = compute(I.getOperand(0)); 110 | if (!L) 111 | return NULL; 112 | Value *R = compute(I.getOperand(1)); 113 | if (!R) 114 | return NULL; 115 | return Builder->CreateICmp(I.getPredicate(), L, R); 116 | } 117 | 118 | Value *BugOnLoop::visitSelectInst(SelectInst &I) { 119 | Value *Cond = compute(I.getCondition()); 120 | if (!Cond) 121 | return NULL; 122 | Value *TrueVal = compute(I.getTrueValue()); 123 | if (!TrueVal) 124 | return NULL; 125 | Value *FalseVal = compute(I.getFalseValue()); 126 | if (!FalseVal) 127 | return NULL; 128 | return Builder->CreateSelect(Cond, TrueVal, FalseVal); 129 | } 130 | 131 | Value *BugOnLoop::visitExtractValueInst(ExtractValueInst &I) { 132 | if (I.getNumIndices() != 1) 133 | return NULL; 134 | IntrinsicInst *II = dyn_cast(I.getAggregateOperand()); 135 | if (!II || II->getCalledFunction()->getName().find(".with.overflow.") == StringRef::npos) 136 | return NULL; 137 | Value *L = compute(II->getArgOperand(0)); 138 | if (!L) 139 | return NULL; 140 | Value *R = compute(II->getArgOperand(1)); 141 | if (!R) 142 | return NULL; 143 | switch (I.getIndices()[0]) { 144 | default: II->dump(); assert(0 && "Unknown overflow!"); 145 | case 0: 146 | switch (II->getIntrinsicID()) { 147 | default: II->dump(); assert(0 && "Unknown overflow!"); 148 | case Intrinsic::sadd_with_overflow: 149 | case Intrinsic::uadd_with_overflow: 150 | return Builder->CreateAdd(L, R); 151 | case Intrinsic::ssub_with_overflow: 152 | case Intrinsic::usub_with_overflow: 153 | return Builder->CreateSub(L, R); 154 | case Intrinsic::smul_with_overflow: 155 | case Intrinsic::umul_with_overflow: 156 | return Builder->CreateMul(L, R); 157 | } 158 | case 1: 159 | switch (II->getIntrinsicID()) { 160 | default: II->dump(); assert(0 && "Unknown overflow!"); 161 | case Intrinsic::sadd_with_overflow: 162 | return createIsSAddWrap(L, R); 163 | case Intrinsic::uadd_with_overflow: 164 | return createIsUAddWrap(L, R); 165 | case Intrinsic::ssub_with_overflow: 166 | return createIsSSubWrap(L, R); 167 | case Intrinsic::usub_with_overflow: 168 | return createIsUSubWrap(L, R); 169 | case Intrinsic::smul_with_overflow: 170 | return createIsSMulWrap(L, R); 171 | case Intrinsic::umul_with_overflow: 172 | return createIsUMulWrap(L, R); 173 | } 174 | } 175 | assert(I.getIndices()[0] == 1 && "FIXME!"); 176 | } 177 | 178 | char BugOnLoop::ID; 179 | 180 | static RegisterPass 181 | X("bugon-loop", "Insert bugon calls for loop exiting values"); 182 | -------------------------------------------------------------------------------- /src/BugOnNull.cc: -------------------------------------------------------------------------------- 1 | #define DEBUG_TYPE "bugon-null" 2 | #include "BugOn.h" 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace llvm; 8 | 9 | namespace { 10 | 11 | struct BugOnNull : BugOnPass { 12 | static char ID; 13 | BugOnNull() : BugOnPass(ID) { 14 | PassRegistry &Registry = *PassRegistry::getPassRegistry(); 15 | initializeDataLayoutPass(Registry); 16 | } 17 | 18 | virtual void getAnalysisUsage(AnalysisUsage &AU) const { 19 | super::getAnalysisUsage(AU); 20 | AU.addRequired(); 21 | } 22 | 23 | virtual bool runOnFunction(Function &); 24 | virtual bool runOnInstruction(Instruction *); 25 | 26 | private: 27 | DataLayout *DL; 28 | SmallPtrSet Visited; 29 | }; 30 | 31 | } // anonymous namespace 32 | 33 | bool BugOnNull::runOnFunction(Function &F) { 34 | DL = &getAnalysis(); 35 | return super::runOnFunction(F); 36 | } 37 | 38 | bool BugOnNull::runOnInstruction(Instruction *I) { 39 | if (isa(I)) { 40 | Visited.clear(); 41 | return false; 42 | } 43 | Value *Base = getNonvolatileBaseAddress(I, DL); 44 | if (!Base) 45 | return false; 46 | if (!Visited.insert(Base)) 47 | return false; 48 | return insert(createIsNull(Base), "null pointer dereference"); 49 | } 50 | 51 | char BugOnNull::ID; 52 | 53 | static RegisterPass 54 | X("bugon-null", "Insert bugon calls for possible null pointer dereference"); 55 | -------------------------------------------------------------------------------- /src/BugOnUndef.cc: -------------------------------------------------------------------------------- 1 | // Insert bug assertions on undef (e.g., uninitialized) value. 2 | 3 | #define DEBUG_TYPE "bugon-undef" 4 | #include "BugOn.h" 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace llvm; 10 | 11 | namespace { 12 | 13 | struct BugOnUndef : BugOnPass { 14 | static char ID; 15 | BugOnUndef() : BugOnPass(ID) {} 16 | 17 | virtual bool runOnInstruction(Instruction *); 18 | }; 19 | 20 | } // anonymous namespace 21 | 22 | static bool isDeadArg(Instruction *I, unsigned Idx) { 23 | CallSite CS(I); 24 | if (!CS) 25 | return false; 26 | Function *F = CS.getCalledFunction(); 27 | if (!F || F->empty() || Idx >= F->arg_size()) 28 | return false; 29 | Function::arg_iterator A = F->arg_begin(); 30 | for (unsigned i = 0; i < Idx; ++i) 31 | ++A; 32 | return A->use_empty(); 33 | } 34 | 35 | static bool isDeadRet(Function *F) { 36 | for (Function::use_iterator i = F->use_begin(), e = F->use_end(); i != e; ++i) { 37 | CallSite CS(*i); 38 | if (!CS) 39 | return false; 40 | if (!CS->use_empty()) 41 | return false; 42 | } 43 | return true; 44 | } 45 | 46 | bool BugOnUndef::runOnInstruction(Instruction *I) { 47 | // It's okay to have undef in phi's operands. 48 | // TODO: catch conditional undefs. 49 | if (isa(I) || isa(I)) 50 | return false; 51 | if (isa(I) || isa(I)) 52 | return false; 53 | // Allow ret undef is the return value is never used. 54 | if (isa(I)) { 55 | if (isDeadRet(I->getParent()->getParent())) 56 | return false; 57 | } 58 | // If any operand is undef, this instruction must not be reachable. 59 | for (unsigned i = 0, n = I->getNumOperands(); i != n; ++i) { 60 | Value *V = I->getOperand(i); 61 | if (isa(V)) { 62 | // Allow undef arguments created by -deadargelim, 63 | // which are basically unused in the function body. 64 | if (isDeadArg(I, i)) 65 | continue; 66 | return insert(Builder->getTrue(), "undef"); 67 | } 68 | } 69 | return false; 70 | } 71 | 72 | char BugOnUndef::ID; 73 | 74 | static RegisterPass 75 | X("bugon-undef", "Insert bugon calls for undef values"); 76 | -------------------------------------------------------------------------------- /src/Diagnostic.cc: -------------------------------------------------------------------------------- 1 | #include "Diagnostic.h" 2 | #include "SMTSolver.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | using namespace llvm; 11 | 12 | bool Diagnostic::hasSingleDebugLocation(Instruction *I) { 13 | const DebugLoc &DbgLoc = I->getDebugLoc(); 14 | // Skip inserted instructions without debugging information. 15 | if (DbgLoc.isUnknown()) 16 | return false; 17 | // Skip inlined instructions. 18 | if (DbgLoc.getInlinedAt(I->getContext())) 19 | return false; 20 | // Macro-expanded code. 21 | if (I->getMetadata("macro")) 22 | return false; 23 | return true; 24 | } 25 | 26 | Diagnostic::Diagnostic() : OS(errs()) {} 27 | 28 | void Diagnostic::backtrace(Instruction *I) { 29 | MDNode *MD = I->getDebugLoc().getAsMDNode(I->getContext()); 30 | if (!MD) 31 | return; 32 | OS << "stack: \n"; 33 | DILocation Loc(MD); 34 | for (;;) { 35 | this->location(Loc); 36 | Loc = Loc.getOrigLocation(); 37 | if (!Loc.Verify()) 38 | break; 39 | } 40 | } 41 | 42 | void Diagnostic::location(MDNode *MD) { 43 | DILocation Loc(MD); 44 | SmallString<64> Path; 45 | StringRef Filename = Loc.getFilename(); 46 | if (sys::path::is_absolute(Filename)) 47 | Path.append(Filename.begin(), Filename.end()); 48 | else 49 | sys::path::append(Path, Loc.getDirectory(), Filename); 50 | OS << " - " << Path 51 | << ':' << Loc.getLineNumber() 52 | << ':' << Loc.getColumnNumber() << "\n"; 53 | } 54 | 55 | void Diagnostic::bug(Instruction *I) { 56 | MDNode *MD = I->getMetadata("bug"); 57 | if (!MD) 58 | return; 59 | this->bug(cast(MD->getOperand(0))->getString()); 60 | } 61 | 62 | void Diagnostic::bug(const Twine &Str) { 63 | OS << "---\n" << "bug: " << Str << "\n"; 64 | } 65 | 66 | void Diagnostic::status(int Status) { 67 | const char *Str; 68 | switch (Status) { 69 | case SMT_UNDEF: Str = "undef"; break; 70 | case SMT_UNSAT: Str = "unsat"; break; 71 | case SMT_SAT: Str = "sat"; break; 72 | default: Str = "timeout"; break; 73 | } 74 | OS << "status: " << Str << "\n"; 75 | } 76 | -------------------------------------------------------------------------------- /src/Diagnostic.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace llvm { 4 | class Instruction; 5 | class MDNode; 6 | class raw_ostream; 7 | class Twine; 8 | } // namespace llvm 9 | 10 | 11 | class Diagnostic { 12 | public: 13 | typedef llvm::Instruction Instruction; 14 | 15 | // Return if I has a non-inlined debug location. 16 | static bool hasSingleDebugLocation(Instruction *I); 17 | 18 | Diagnostic(); 19 | 20 | llvm::raw_ostream &os() { return OS; } 21 | 22 | void bug(Instruction *); 23 | void bug(const llvm::Twine &); 24 | 25 | void backtrace(Instruction *); 26 | void location(llvm::MDNode *); 27 | void status(int); 28 | 29 | template Diagnostic & 30 | operator <<(const T &Val) { 31 | OS << Val; 32 | return *this; 33 | } 34 | 35 | private: 36 | llvm::raw_ostream &OS; 37 | }; 38 | -------------------------------------------------------------------------------- /src/ElimAssert.cc: -------------------------------------------------------------------------------- 1 | #define DEBUG_TYPE "elim-assert" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "Diagnostic.h" 10 | 11 | using namespace llvm; 12 | 13 | namespace { 14 | 15 | struct ElimAssert : FunctionPass { 16 | static char ID; 17 | ElimAssert() : FunctionPass(ID) {} 18 | 19 | virtual bool runOnFunction(Function &); 20 | 21 | private: 22 | bool safeBB(BasicBlock *BB); 23 | bool dropBB(Function &F, BasicBlock *BB); 24 | }; 25 | 26 | } // anonymous namespace 27 | 28 | static std::set AssertFailures = { 29 | "__assert_fail", // Linux assert() 30 | "panic", // Linux kernel BUG_ON() 31 | }; 32 | 33 | static std::set SafeFunctions = { 34 | "printf", 35 | "printk", // Linux kernel 36 | }; 37 | 38 | bool ElimAssert::runOnFunction(Function &F) { 39 | bool Changed = false; 40 | for (inst_iterator i = inst_begin(F), e = inst_end(F); i != e; ) { 41 | Instruction *I = &*i++; 42 | if (I->getDebugLoc().isUnknown()) 43 | continue; 44 | CallInst *CI = dyn_cast(I); 45 | if (!CI || !CI->getCalledFunction()) 46 | continue; 47 | if (AssertFailures.find(CI->getCalledFunction()->getName()) == 48 | AssertFailures.end()) 49 | continue; 50 | BasicBlock *BB = CI->getParent(); 51 | 52 | if (!safeBB(BB)) 53 | continue; 54 | 55 | Changed |= dropBB(F, BB); 56 | } 57 | return Changed; 58 | } 59 | 60 | // Return true if this basic block has no calls to functions that 61 | // might prevent an eventual assertion failure. 62 | bool ElimAssert::safeBB(BasicBlock *BB) { 63 | for (BasicBlock::iterator i = BB->begin(), e = BB->end(); i != e; ) { 64 | Instruction *I = &*i++; 65 | CallInst *CI = dyn_cast(I); 66 | if (!CI) 67 | continue; 68 | if (!CI->getCalledFunction()) 69 | return false; 70 | StringRef N = CI->getCalledFunction()->getName(); 71 | if (AssertFailures.find(N) == AssertFailures.end() && 72 | SafeFunctions.find(N) == SafeFunctions.end()) { 73 | // Diagnostic() << "ElimAssert: unsafe call to " << N << "\n"; 74 | return false; 75 | } 76 | } 77 | return true; 78 | } 79 | 80 | bool ElimAssert::dropBB(Function &F, BasicBlock *BB) { 81 | IRBuilder<> TheBuilder(F.getContext()); 82 | 83 | bool Changed = false; 84 | for (inst_iterator i = inst_begin(F), e = inst_end(F); i != e; ) { 85 | Instruction *I = &*i++; 86 | BranchInst *BI = dyn_cast(I); 87 | if (!BI) 88 | continue; 89 | if (BI->isUnconditional()) 90 | continue; 91 | if (BI->getNumSuccessors() != 2) 92 | continue; 93 | 94 | if (BI->getSuccessor(0) == BB) { 95 | BI->setCondition(TheBuilder.getFalse()); 96 | Changed = true; 97 | } 98 | 99 | if (BI->getSuccessor(1) == BB) { 100 | BI->setCondition(TheBuilder.getTrue()); 101 | Changed = true; 102 | } 103 | } 104 | return Changed; 105 | } 106 | 107 | char ElimAssert::ID; 108 | 109 | static RegisterPass 110 | X("elim-assert", "Eliminate asserts"); 111 | -------------------------------------------------------------------------------- /src/GlobalTimeout.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "config.h" 8 | 9 | using namespace llvm; 10 | 11 | static cl::opt 12 | GlobalTimeoutOpt("global-timeout-sec", 13 | cl::desc("Specify a global timeout for entire analysis"), 14 | cl::value_desc("seconds")); 15 | 16 | namespace { 17 | 18 | #ifdef HAVE_TIMER 19 | static void global_check(union sigval) { 20 | struct rusage ru_self, ru_child; 21 | getrusage(RUSAGE_SELF, &ru_self); 22 | getrusage(RUSAGE_CHILDREN, &ru_child); 23 | if (ru_self.ru_utime.tv_sec + ru_child.ru_utime.tv_sec > (time_t)GlobalTimeoutOpt) { 24 | printf("Global timeout: self %ld.%06ld, child %ld.%06ld\n", 25 | (long)ru_self.ru_utime.tv_sec, (long)ru_self.ru_utime.tv_usec, 26 | (long)ru_child.ru_utime.tv_sec, (long)ru_child.ru_utime.tv_usec); 27 | exit(0); 28 | } 29 | } 30 | #endif 31 | 32 | struct GlobalTimeout : ImmutablePass { 33 | static char ID; 34 | GlobalTimeout() : ImmutablePass(ID) { 35 | if (!GlobalTimeoutOpt) 36 | return; 37 | #ifdef HAVE_TIMER 38 | timer_t tid; 39 | struct sigevent sigev; 40 | sigev.sigev_notify = SIGEV_THREAD; 41 | sigev.sigev_notify_function = &global_check; 42 | sigev.sigev_notify_attributes = nullptr; 43 | int r = timer_create(CLOCK_MONOTONIC, &sigev, &tid); 44 | if (r < 0) { 45 | perror("timer_create"); 46 | return; 47 | } 48 | 49 | struct itimerspec its; 50 | its.it_value.tv_sec = 60; 51 | its.it_value.tv_nsec = 0; 52 | its.it_interval = its.it_value; 53 | r = timer_settime(tid, 0, &its, nullptr); 54 | if (r < 0) { 55 | perror("timer_settime"); 56 | return; 57 | } 58 | #endif 59 | } 60 | }; 61 | 62 | } // anonymous namespace 63 | 64 | char GlobalTimeout::ID; 65 | 66 | static RegisterPass 67 | X("enable-global-timeout", "Terminate process after a fixed timeout (including child time)"); 68 | -------------------------------------------------------------------------------- /src/IgnoreLoopInitial.cc: -------------------------------------------------------------------------------- 1 | // This pass ignores loop initial conditions for anti-simplify. 2 | // It should run after -loop-rotate. 3 | 4 | #define DEBUG_TYPE "ignore-loop-initial" 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | using namespace llvm; 11 | 12 | namespace { 13 | 14 | struct IgnoreLoopInitial : FunctionPass { 15 | static char ID; 16 | IgnoreLoopInitial() : FunctionPass(ID) {} 17 | 18 | virtual void getAnalysisUsage(AnalysisUsage &AU) const { 19 | AU.setPreservesCFG(); 20 | } 21 | 22 | virtual bool runOnFunction(Function &); 23 | }; 24 | 25 | } // anonymous namespace 26 | 27 | bool IgnoreLoopInitial::runOnFunction(Function &F) { 28 | bool Changed = false; 29 | for (Function::iterator i = F.begin(), e = F.end(); i != e; ++i) { 30 | BasicBlock *Preheader = i; 31 | if (Preheader->getName().find(".lr.ph") == StringRef::npos) 32 | continue; 33 | // Conditional jump to preheader? 34 | BasicBlock *Initial = Preheader->getSinglePredecessor(); 35 | if (!Initial) 36 | continue; 37 | BranchInst *BI = dyn_cast(Initial->getTerminator()); 38 | if (!BI || !BI->isConditional()) 39 | continue; 40 | Value *Cond = BI->getCondition(); 41 | Instruction *I = dyn_cast(Cond); 42 | if (!I) 43 | continue; 44 | I->setDebugLoc(DebugLoc()); 45 | Changed = true; 46 | } 47 | return Changed; 48 | } 49 | 50 | char IgnoreLoopInitial::ID; 51 | 52 | static RegisterPass 53 | X(DEBUG_TYPE, "Ignore loop initial conditions"); 54 | -------------------------------------------------------------------------------- /src/InlineOnly.cc: -------------------------------------------------------------------------------- 1 | #define DEBUG_TYPE "inline-only" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace llvm; 8 | 9 | namespace { 10 | 11 | struct InlineOnly : Inliner { 12 | static char ID; 13 | InlineOnly() : Inliner(ID), CA(nullptr) { 14 | PassRegistry &Registry = *PassRegistry::getPassRegistry(); 15 | initializeInlineCostAnalysisPass(Registry); 16 | } 17 | 18 | virtual void getAnalysisUsage(AnalysisUsage &AU) const { 19 | super::getAnalysisUsage(AU); 20 | AU.addRequired(); 21 | } 22 | 23 | virtual InlineCost getInlineCost(CallSite CS) { 24 | if (!CA) 25 | CA = &getAnalysis(); 26 | return CA->getInlineCost(CS, getInlineThreshold(CS)); 27 | } 28 | 29 | virtual bool doInitialization(CallGraph &CG); 30 | virtual bool doFinalization(CallGraph &CG); 31 | 32 | typedef Inliner super; 33 | using super::doInitialization; 34 | using super::doFinalization; 35 | 36 | private: 37 | InlineCostAnalysis *CA; 38 | typedef DenseMap LinkageMapTy; 39 | LinkageMapTy LinkageMap; 40 | }; 41 | 42 | } // anonymous namespace 43 | 44 | bool InlineOnly::doInitialization(CallGraph &CG) { 45 | Module &M = CG.getModule(); 46 | LinkageMap.clear(); 47 | // Temporarily change local functions to linkonce_odr. 48 | // Inliner handles linkonce_odr in the same way as static, 49 | // except that it doesn't remove linkonce_odr functions. 50 | for (Module::iterator i = M.begin(), e = M.end(); i != e; ++i) { 51 | Function *F = i; 52 | if (F->hasLocalLinkage()) { 53 | LinkageMap[F] = F->getLinkage(); 54 | F->setLinkage(GlobalValue::LinkOnceODRLinkage); 55 | } 56 | } 57 | return !LinkageMap.empty(); 58 | } 59 | 60 | bool InlineOnly::doFinalization(CallGraph &) { 61 | // Restore original linkage. 62 | for (LinkageMapTy::iterator i = LinkageMap.begin(), e = LinkageMap.end(); i != e; ++i) 63 | i->first->setLinkage(i->second); 64 | return !LinkageMap.empty(); 65 | } 66 | 67 | char InlineOnly::ID; 68 | 69 | static RegisterPass 70 | X("inline-only", "Inline functions but never remove inlined ones"); 71 | -------------------------------------------------------------------------------- /src/IntAction.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | using namespace clang; 19 | using namespace llvm; 20 | 21 | // Filename, Line => Location. 22 | typedef StringMap< DenseMap > > MacroMap; 23 | 24 | namespace { 25 | 26 | // Add macro-expanded code with if or ?: conditions. 27 | class ExtractMacroVisitor : public RecursiveASTVisitor { 28 | public: 29 | ExtractMacroVisitor(SourceManager &SM, MacroMap &MM) : SM(SM), MM(MM) {} 30 | 31 | bool VisitIfStmt(IfStmt *S) { 32 | addMacroLoc(S->getLocStart()); 33 | return true; 34 | } 35 | 36 | bool VisitConditionalOperator(ConditionalOperator *E) { 37 | addMacroLoc(E->getLocStart()); 38 | return true; 39 | } 40 | 41 | // GNU extension ?:, the middle operand omitted. 42 | bool VisitBinaryConditionalOperator(BinaryConditionalOperator *E) { 43 | addMacroLoc(E->getLocStart()); 44 | return true; 45 | } 46 | 47 | // p && p->x. 48 | bool VisitBinaryOperator(BinaryOperator *E) { 49 | if (E->isLogicalOp() || E->isEqualityOp()) 50 | addMacroLoc(E->getLocStart()); 51 | return true; 52 | } 53 | 54 | private: 55 | SourceManager &SM; 56 | MacroMap &MM; 57 | 58 | void addMacroLoc(SourceLocation Loc) { 59 | if (Loc.isInvalid()) 60 | return; 61 | if (!Loc.isMacroID()) 62 | return; 63 | PresumedLoc PLoc = SM.getPresumedLoc(Loc); 64 | if (PLoc.isInvalid()) 65 | return; 66 | MM[PLoc.getFilename()][PLoc.getLine()].insert(Loc); 67 | } 68 | }; 69 | 70 | class ExtractMacroConsumer : public ASTConsumer { 71 | public: 72 | ExtractMacroConsumer(MacroMap &MM) : MM(MM) {} 73 | 74 | virtual void HandleTranslationUnit(ASTContext &Ctx) { 75 | ExtractMacroVisitor Visitor(Ctx.getSourceManager(), MM); 76 | Visitor.TraverseDecl(Ctx.getTranslationUnitDecl()); 77 | } 78 | 79 | private: 80 | MacroMap &MM; 81 | }; 82 | 83 | // Use multiple inheritance to intercept code generation. 84 | class IntAction : public PluginASTAction, EmitLLVMOnlyAction { 85 | typedef PluginASTAction super; 86 | typedef EmitLLVMOnlyAction Delegate; 87 | 88 | public: 89 | virtual bool ParseArgs(const CompilerInstance &, const std::vector&) { 90 | return true; 91 | } 92 | 93 | virtual bool usesPreprocessorOnly() const { 94 | return Delegate::usesPreprocessorOnly(); 95 | } 96 | 97 | virtual TranslationUnitKind getTranslationUnitKind() { 98 | return Delegate::getTranslationUnitKind(); 99 | } 100 | 101 | virtual bool hasPCHSupport() const { 102 | return Delegate::hasPCHSupport(); 103 | } 104 | 105 | virtual bool hasASTFileSupport() const { 106 | return Delegate::hasASTFileSupport(); 107 | } 108 | 109 | virtual bool hasIRSupport() const { 110 | return Delegate::hasIRSupport(); 111 | } 112 | 113 | virtual bool hasCodeCompletionSupport() const { 114 | return Delegate::hasCodeCompletionSupport(); 115 | } 116 | 117 | protected: 118 | 119 | virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { 120 | OS = CI.createDefaultOutputFile(true, InFile, "bc"); 121 | ASTConsumer *C[] = { 122 | new ExtractMacroConsumer(MM), 123 | Delegate::CreateASTConsumer(CI, InFile) 124 | }; 125 | return new MultiplexConsumer(C); 126 | } 127 | 128 | virtual bool BeginInvocation(CompilerInstance &CI) { 129 | return Delegate::BeginInvocation(CI); 130 | } 131 | 132 | virtual bool BeginSourceFileAction(CompilerInstance &CI, StringRef Filename) { 133 | Delegate::setCurrentInput(super::getCurrentInput()); 134 | Delegate::setCompilerInstance(&CI); 135 | return Delegate::BeginSourceFileAction(CI, Filename); 136 | } 137 | 138 | virtual void ExecuteAction() { 139 | Delegate::ExecuteAction(); 140 | } 141 | 142 | virtual void EndSourceFileAction() { 143 | Delegate::EndSourceFileAction(); 144 | OwningPtr M(Delegate::takeModule()); 145 | if (!M) 146 | return; 147 | markMacroLocations(*M); 148 | WriteBitcodeToFile(M.get(), *OS); 149 | } 150 | 151 | private: 152 | MacroMap MM; 153 | raw_ostream *OS; 154 | 155 | void markMacroLocations(llvm::Module &); 156 | }; 157 | 158 | } // anonymous namespace 159 | 160 | void IntAction::markMacroLocations(llvm::Module &M) { 161 | // Filename => set. 162 | LLVMContext &C = M.getContext(); 163 | unsigned MD_macro = C.getMDKindID("macro"); 164 | CompilerInstance &CI = super::getCompilerInstance(); 165 | Preprocessor &PP = CI.getPreprocessor(); 166 | for (Function &F : M) { 167 | for (inst_iterator i = inst_begin(F), e = inst_end(F); i != e; ++i) { 168 | Instruction *I = &*i; 169 | const DebugLoc &DbgLoc = I->getDebugLoc(); 170 | if (DbgLoc.isUnknown()) 171 | continue; 172 | StringRef Filename = DIScope(DbgLoc.getScope(C)).getFilename(); 173 | unsigned Line = DbgLoc.getLine(); 174 | SmallVector MDElems; 175 | for (SourceLocation Loc : MM.lookup(Filename).lookup(Line)) { 176 | StringRef MacroName = PP.getImmediateMacroName(Loc); 177 | MDElems.push_back(MDString::get(C, MacroName)); 178 | } 179 | if (MDElems.empty()) 180 | continue; 181 | I->setMetadata(MD_macro, MDNode::get(C, MDElems)); 182 | } 183 | } 184 | } 185 | 186 | static FrontendPluginRegistry::Add 187 | X("intfe", "Frontend rewriting"); 188 | -------------------------------------------------------------------------------- /src/LoadElim.cc: -------------------------------------------------------------------------------- 1 | #define DEBUG_TYPE "load-elim" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace llvm; 12 | 13 | namespace { 14 | 15 | struct LoadElim : FunctionPass { 16 | static char ID; 17 | LoadElim() : FunctionPass(ID) { 18 | PassRegistry &Registry = *PassRegistry::getPassRegistry(); 19 | initializeAliasAnalysisAnalysisGroup(Registry); 20 | } 21 | 22 | virtual void getAnalysisUsage(AnalysisUsage &AU) const { 23 | AU.setPreservesCFG(); 24 | AU.addRequired(); 25 | AU.addRequired(); 26 | } 27 | 28 | virtual bool runOnFunction(Function &); 29 | 30 | private: 31 | AliasAnalysis *AA; 32 | MemoryDependenceAnalysis *MDA; 33 | TargetLibraryInfo *TLI; 34 | 35 | bool merge(LoadInst *); 36 | }; 37 | 38 | } // anonymous namespace 39 | 40 | bool LoadElim::runOnFunction(Function &F) { 41 | AA = &getAnalysis(); 42 | MDA = &getAnalysis(); 43 | TLI = getAnalysisIfAvailable(); 44 | bool Changed = false; 45 | for (inst_iterator i = inst_begin(F), e = inst_end(F); i != e; ) { 46 | Instruction *I = &*i++; 47 | if (LoadInst *LI = dyn_cast(I)) 48 | Changed |= merge(LI); 49 | } 50 | return Changed; 51 | } 52 | 53 | // For now just merge loads in the same block. 54 | bool LoadElim::merge(LoadInst *I) { 55 | if (I->isVolatile()) 56 | return false; 57 | Instruction *Dep = MDA->getDependency(I).getInst(); 58 | if (!Dep) 59 | return false; 60 | Value *P = NULL, *V = NULL; 61 | // Find a previous load/store. 62 | if (LoadInst *LI = dyn_cast(Dep)) { 63 | P = LI->getPointerOperand(); 64 | V = LI; 65 | } else if (StoreInst *SI = dyn_cast(Dep)) { 66 | P = SI->getPointerOperand(); 67 | V = SI->getValueOperand(); 68 | } 69 | if (!P || !V) 70 | return false; 71 | // Must be the same type. 72 | if (V->getType() != I->getType()) 73 | return false; 74 | // Must be the same address. 75 | if (!AA->isMustAlias(P, I->getPointerOperand())) 76 | return false; 77 | I->replaceAllUsesWith(V); 78 | RecursivelyDeleteTriviallyDeadInstructions(I, TLI); 79 | return true; 80 | } 81 | 82 | char LoadElim::ID; 83 | 84 | static RegisterPass 85 | X(DEBUG_TYPE, "Eliminate redundant load instructions"); 86 | -------------------------------------------------------------------------------- /src/LoopPrepare.cc: -------------------------------------------------------------------------------- 1 | // This pass replaces some operations with SCEV-friendly alternatives. 2 | // It should run before -loop-rotate. 3 | 4 | #define DEBUG_TYPE "loop-prepare" 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace llvm; 10 | 11 | namespace { 12 | 13 | struct LoopPrepare : FunctionPass { 14 | static char ID; 15 | LoopPrepare() : FunctionPass(ID) {} 16 | 17 | virtual void getAnalysisUsage(AnalysisUsage &AU) const { 18 | AU.setPreservesCFG(); 19 | } 20 | 21 | virtual bool runOnFunction(Function &); 22 | 23 | private: 24 | typedef IRBuilder<> BuilderTy; 25 | BuilderTy *Builder; 26 | 27 | bool visitShl(BinaryOperator *); 28 | }; 29 | 30 | } // anonymous namespace 31 | 32 | bool LoopPrepare::runOnFunction(Function &F) { 33 | BuilderTy TheBuilder(F.getContext()); 34 | Builder = &TheBuilder; 35 | bool Changed = false; 36 | for (inst_iterator i = inst_begin(F), e = inst_end(F); i != e; ) { 37 | BinaryOperator *I = dyn_cast(&*i++); 38 | if (!I || !I->getType()->isIntegerTy()) 39 | continue; 40 | if (I->getOpcode() == Instruction::Shl) { 41 | Builder->SetInsertPoint(I); 42 | Changed |= visitShl(I); 43 | } 44 | } 45 | return Changed; 46 | } 47 | 48 | // (L << R) => (L * 2^R) 49 | bool LoopPrepare::visitShl(BinaryOperator *I) { 50 | IntegerType *T = cast(I->getType()); 51 | Value *L = I->getOperand(0); 52 | Value *R = I->getOperand(1); 53 | Value *Power = Builder->CreateShl(ConstantInt::get(T, 1), R); 54 | bool hasNSW = I->hasNoSignedWrap(); 55 | bool hasNUW = I->hasNoUnsignedWrap(); 56 | Value *Mul = Builder->CreateMul(L, Power, "", hasNUW, hasNSW); 57 | I->replaceAllUsesWith(Mul); 58 | if (I->hasName()) 59 | Mul->takeName(I); 60 | return true; 61 | } 62 | 63 | char LoopPrepare::ID; 64 | 65 | static RegisterPass 66 | X("loop-prepare", "Optimize for loop-friendly operations"); 67 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | AM_CXXFLAGS = `llvm-config --cxxflags` -Werror -Wall -std=c++11 -fno-strict-aliasing 2 | 3 | noinst_LTLIBRARIES = libsat.la 4 | lib_LTLIBRARIES = liboptck.la liboptfe.la 5 | EXTRA_DIST = optck poptck ncpu 6 | 7 | all-local: liboptck.la liboptfe.la 8 | @cd $(top_builddir)/lib && $(LN_S) -f ../src/.libs/liboptck.so 9 | @cd $(top_builddir)/lib && $(LN_S) -f ../src/.libs/liboptfe.so 10 | 11 | libsat_la_CPPFLAGS = -I$(top_builddir)/lib 12 | libsat_la_SOURCES = ValueGen.cc PathGen.cc Diagnostic.cc SMTSolver.cc 13 | libsat_la_SOURCES += PHIRange.cc LoopPrepare.cc ElimAssert.cc 14 | libsat_la_SOURCES += BugOn.cc BugOnInt.cc BugOnNull.cc BugOnGep.cc 15 | libsat_la_SOURCES += BugOnAlias.cc BugOnFree.cc BugOnBounds.cc BugOnUndef.cc 16 | libsat_la_SOURCES += BugOnLoop.cc BugOnAssert.cc 17 | libsat_la_SOURCES += BugOnLibc.cc BugOnLinux.cc 18 | libsat_la_SOURCES += ValueGen.h PathGen.h Diagnostic.h SMTSolver.h BugOn.h 19 | libsat_la_SOURCES += GlobalTimeout.cc 20 | if HAVE_SMTLIB 21 | libsat_la_SOURCES += SMTLIB.cc 22 | libsat_la_LIBADD = -lutil 23 | endif 24 | if HAVE_BOOLECTOR 25 | libsat_la_SOURCES += SMTBoolector.cc 26 | libsat_la_LIBADD = -lboolector -llgl 27 | endif 28 | #libsat_la_SOURCES += SMTSonolar.cc 29 | #libsat_la_LIBADD = -lsonolar 30 | #libsat_la_SOURCES += SMTZ3.cc 31 | #libsat_la_LIBADD = -lz3 -lgomp 32 | libsat_la_LDFLAGS = -L$(top_builddir)/lib 33 | 34 | liboptck_la_SOURCES = AntiFunctionPass.cc AntiDCE.cc AntiAlgebra.cc AntiSimplify.cc 35 | liboptck_la_SOURCES += InlineOnly.cc SimplifyDelete.cc IgnoreLoopInitial.cc LoadElim.cc 36 | liboptck_la_SOURCES += AntiFunctionPass.h 37 | liboptck_la_LIBADD = libsat.la 38 | liboptck_la_LDFLAGS = -module 39 | 40 | liboptfe_la_SOURCES = IntAction.cc 41 | liboptfe_la_LDFLAGS = -module 42 | -------------------------------------------------------------------------------- /src/PHIRange.cc: -------------------------------------------------------------------------------- 1 | // This pass attaches range metadata to phi nodes, especially those 2 | // in loops, so as to add value constraints. 3 | 4 | #define DEBUG_TYPE "phi-range" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | using namespace llvm; 14 | 15 | namespace { 16 | 17 | struct PHIRange : FunctionPass { 18 | static char ID; 19 | PHIRange() : FunctionPass(ID) { 20 | PassRegistry &Registry = *PassRegistry::getPassRegistry(); 21 | initializeLoopInfoPass(Registry); 22 | initializeScalarEvolutionPass(Registry); 23 | } 24 | 25 | virtual void getAnalysisUsage(AnalysisUsage &AU) const { 26 | AU.setPreservesCFG(); 27 | AU.addRequired(); 28 | AU.addRequired(); 29 | } 30 | 31 | virtual bool runOnFunction(Function &); 32 | 33 | private: 34 | LoopInfo *LI; 35 | ScalarEvolution *SE; 36 | 37 | bool visitPHINode(PHINode *); 38 | }; 39 | 40 | } // anonymous namespace 41 | 42 | bool PHIRange::runOnFunction(Function &F) { 43 | LI = &getAnalysis(); 44 | SE = &getAnalysis(); 45 | bool Changed = false; 46 | // PHI nodes must be at the start of each block. 47 | for (Function::iterator i = F.begin(), e = F.end(); i != e; ++i) { 48 | BasicBlock *BB = i; 49 | if (!LI->getLoopFor(BB)) 50 | continue; 51 | BasicBlock::iterator PHIIter = BB->begin(); 52 | for (;;) { 53 | PHINode *PHI = dyn_cast(PHIIter++); 54 | if (!PHI) 55 | break; 56 | Changed |= visitPHINode(PHI); 57 | } 58 | } 59 | return Changed; 60 | } 61 | 62 | bool PHIRange::visitPHINode(PHINode *I) { 63 | IntegerType *T = dyn_cast(I->getType()); 64 | // Ignore non-integer nodes. 65 | if (!T) 66 | return false; 67 | const SCEV *S = SE->getSCEV(I); 68 | ConstantRange Range = SE->getSignedRange(S); 69 | if (Range.isFullSet() || Range.isEmptySet()) 70 | return false; 71 | Value *Vals[2] = { 72 | ConstantInt::get(T, Range.getLower()), 73 | ConstantInt::get(T, Range.getUpper()) 74 | }; 75 | LLVMContext &VMCtx = I->getContext(); 76 | MDNode *MD = MDNode::get(VMCtx, Vals); 77 | I->setMetadata("intrange", MD); 78 | return true; 79 | } 80 | 81 | char PHIRange::ID; 82 | 83 | static RegisterPass 84 | X("phi-range", "Add range metadata to phi nodes"); 85 | -------------------------------------------------------------------------------- /src/PathGen.cc: -------------------------------------------------------------------------------- 1 | #include "PathGen.h" 2 | #include "ValueGen.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace llvm; 9 | 10 | #define SMT VG.SMT 11 | 12 | PathGen::PathGen(ValueGen &VG, const EdgeVec &BE) 13 | : VG(VG), Backedges(BE), DT(NULL) {} 14 | 15 | PathGen::PathGen(ValueGen &VG, const EdgeVec &Backedges, DominatorTree &DT) 16 | : VG(VG), Backedges(Backedges), DT(&DT) {} 17 | 18 | PathGen::~PathGen() { 19 | for (iterator i = Cache.begin(), e = Cache.end(); i != e; ++i) 20 | SMT.decref(i->second); 21 | } 22 | 23 | static BasicBlock *findCommonDominator(BasicBlock *BB, DominatorTree *DT) { 24 | pred_iterator i = pred_begin(BB), e = pred_end(BB); 25 | BasicBlock *Dom = *i; 26 | for (++i; i != e; ++i) 27 | Dom = DT->findNearestCommonDominator(Dom, *i); 28 | return Dom; 29 | } 30 | 31 | SMTExpr PathGen::get(BasicBlock *BB) { 32 | SMTExpr G = Cache.lookup(BB); 33 | if (G) 34 | return G; 35 | // Entry block has true guard. 36 | if (BB == &BB->getParent()->getEntryBlock()) { 37 | G = SMT.bvtrue(); 38 | Cache[BB] = G; 39 | return G; 40 | } 41 | pred_iterator i, e = pred_end(BB); 42 | if (DT) { 43 | // Fall back to common ancestors if any back edges. 44 | for (i = pred_begin(BB); i != e; ++i) { 45 | if (isBackedge(*i, BB)) 46 | return get(findCommonDominator(BB, DT)); 47 | } 48 | } 49 | // The guard is the disjunction of predecessors' guards. 50 | // Initialize to false. 51 | G = SMT.bvfalse(); 52 | for (i = pred_begin(BB); i != e; ++i) { 53 | BasicBlock *Pred = *i; 54 | // Skip back edges. 55 | if (!DT && isBackedge(Pred, BB)) 56 | continue; 57 | SMTExpr Term = getTermGuard(Pred->getTerminator(), BB); 58 | SMTExpr PN = getPHIGuard(BB, Pred); 59 | SMTExpr TermWithPN = SMT.bvand(Term, PN); 60 | SMT.decref(Term); 61 | SMT.decref(PN); 62 | SMTExpr Br = SMT.bvand(TermWithPN, get(Pred)); 63 | SMT.decref(TermWithPN); 64 | SMTExpr Tmp = SMT.bvor(G, Br); 65 | SMT.decref(G); 66 | SMT.decref(Br); 67 | G = Tmp; 68 | } 69 | Cache[BB] = G; 70 | return G; 71 | } 72 | 73 | bool PathGen::isBackedge(llvm::BasicBlock *From, llvm::BasicBlock *To) { 74 | return std::find(Backedges.begin(), Backedges.end(), Edge(From, To)) 75 | != Backedges.end(); 76 | } 77 | 78 | SMTExpr PathGen::getPHIGuard(BasicBlock *BB, BasicBlock *Pred) { 79 | SMTExpr E = SMT.bvtrue(); 80 | BasicBlock::iterator i = BB->begin(), e = BB->end(); 81 | for (; i != e; ++i) { 82 | PHINode *I = dyn_cast(i); 83 | if (!I) 84 | break; 85 | Value *V = I->getIncomingValueForBlock(Pred); 86 | // Skip undef. 87 | if (isa(V)) 88 | continue; 89 | // Skip non-integral types. 90 | if (!ValueGen::isAnalyzable(V)) 91 | continue; 92 | // Generate I == V. 93 | SMTExpr PN = SMT.eq(VG.get(I), VG.get(V)); 94 | SMTExpr Tmp = SMT.bvand(E, PN); 95 | SMT.decref(E); 96 | SMT.decref(PN); 97 | E = Tmp; 98 | } 99 | return E; 100 | } 101 | 102 | SMTExpr PathGen::getTermGuard(TerminatorInst *I, BasicBlock *BB) { 103 | switch (I->getOpcode()) { 104 | default: I->dump(); llvm_unreachable("Unknown terminator!"); 105 | case Instruction::Br: 106 | return getTermGuard(cast(I), BB); 107 | case Instruction::Switch: 108 | return getTermGuard(cast(I), BB); 109 | case Instruction::IndirectBr: 110 | case Instruction::Invoke: 111 | return SMT.bvtrue(); 112 | } 113 | } 114 | 115 | SMTExpr PathGen::getTermGuard(BranchInst *I, BasicBlock *BB) { 116 | if (I->isUnconditional()) 117 | return SMT.bvtrue(); 118 | // Conditional branch. 119 | Value *V = I->getCondition(); 120 | SMTExpr E = VG.get(V); 121 | SMT.incref(E); 122 | // True or false branch. 123 | if (I->getSuccessor(0) != BB) { 124 | assert(I->getSuccessor(1) == BB); 125 | SMTExpr Tmp = SMT.bvnot(E); 126 | SMT.decref(E); 127 | E = Tmp; 128 | } 129 | return E; 130 | } 131 | 132 | SMTExpr PathGen::getTermGuard(SwitchInst *I, BasicBlock *BB) { 133 | Value *V = I->getCondition(); 134 | SMTExpr L = VG.get(V); 135 | SwitchInst::CaseIt i = I->case_begin(), e = I->case_end(); 136 | if (I->getDefaultDest() != BB) { 137 | // Find all x = C_i for BB. 138 | SMTExpr E = SMT.bvfalse(); 139 | for (; i != e; ++i) { 140 | if (i.getCaseSuccessor() == BB) { 141 | ConstantInt *CI = i.getCaseValue(); 142 | SMTExpr Cond = SMT.eq(L, VG.get(CI)); 143 | SMTExpr Tmp = SMT.bvor(E, Cond); 144 | SMT.decref(Cond); 145 | SMT.decref(E); 146 | E = Tmp; 147 | } 148 | } 149 | return E; 150 | } 151 | // Compute guard for the default case. 152 | // i starts from 1; 0 is reserved for the default. 153 | SMTExpr E = SMT.bvfalse(); 154 | for (; i != e; ++i) { 155 | ConstantInt *CI = i.getCaseValue(); 156 | SMTExpr Cond = SMT.eq(L, VG.get(CI)); 157 | SMTExpr Tmp = SMT.bvor(E, Cond); 158 | SMT.decref(Cond); 159 | SMT.decref(E); 160 | E = Tmp; 161 | } 162 | SMTExpr NotE = SMT.bvnot(E); 163 | SMT.decref(E); 164 | return NotE; 165 | } 166 | -------------------------------------------------------------------------------- /src/PathGen.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "SMTSolver.h" 6 | 7 | namespace llvm { 8 | class BasicBlock; 9 | class BranchInst; 10 | class DominatorTree; 11 | class SwitchInst; 12 | class TerminatorInst; 13 | } // namespace llvm 14 | 15 | class ValueGen; 16 | 17 | class PathGen { 18 | public: 19 | typedef llvm::DenseMap BBExprMap; 20 | typedef BBExprMap::iterator iterator; 21 | typedef std::pair Edge; 22 | typedef llvm::SmallVectorImpl EdgeVec; 23 | 24 | PathGen(ValueGen &, const EdgeVec &); 25 | PathGen(ValueGen &, const EdgeVec &, llvm::DominatorTree &DT); 26 | ~PathGen(); 27 | 28 | SMTExpr get(llvm::BasicBlock *); 29 | 30 | private: 31 | ValueGen &VG; 32 | const EdgeVec &Backedges; 33 | llvm::DominatorTree *DT; 34 | BBExprMap Cache; 35 | 36 | bool isBackedge(llvm::BasicBlock *, llvm::BasicBlock *); 37 | SMTExpr getTermGuard(llvm::TerminatorInst *I, llvm::BasicBlock *BB); 38 | SMTExpr getTermGuard(llvm::BranchInst *I, llvm::BasicBlock *BB); 39 | SMTExpr getTermGuard(llvm::SwitchInst *I, llvm::BasicBlock *BB); 40 | SMTExpr getPHIGuard(llvm::BasicBlock *BB, llvm::BasicBlock *Pred); 41 | }; 42 | -------------------------------------------------------------------------------- /src/SMTBoolector.cc: -------------------------------------------------------------------------------- 1 | #include "SMTSolver.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | extern "C" { 12 | #include 13 | } 14 | 15 | using namespace llvm; 16 | 17 | #define ctx ((Btor *)ctx_) 18 | #define m ((Btor *)m_) 19 | #define e ((BtorNode *)e_) 20 | #define lhs ((BtorNode *)lhs_) 21 | #define rhs ((BtorNode *)rhs_) 22 | 23 | // Boolector 1.5 is much slower due to the new SAT backend. 24 | // Use the workaround to disable preprocessing for performance. 25 | namespace { 26 | struct SMTWorkaround { 27 | SMTWorkaround() { ::setenv("LGLPLAIN", "1", 1); } 28 | }; 29 | } 30 | 31 | static SMTWorkaround X; 32 | 33 | SMTSolver::SMTSolver(bool modelgen) { 34 | ctx_ = boolector_new(); 35 | if (modelgen) 36 | boolector_enable_model_gen(ctx); 37 | boolector_enable_inc_usage(ctx); 38 | } 39 | 40 | SMTSolver::~SMTSolver() { 41 | assert(boolector_get_refs(ctx) == 0); 42 | boolector_delete(ctx); 43 | } 44 | 45 | void SMTSolver::assume(SMTExpr e_) { 46 | boolector_assert(ctx, e); 47 | } 48 | 49 | SMTStatus SMTSolver::query(SMTExpr e_, SMTModel *m_) { 50 | boolector_assume(ctx, e); 51 | switch (boolector_sat(ctx)) { 52 | default: return SMT_UNDEF; 53 | case BOOLECTOR_UNSAT: return SMT_UNSAT; 54 | case BOOLECTOR_SAT: break; 55 | } 56 | if (m_) 57 | *m_ = ctx_; 58 | return SMT_SAT; 59 | } 60 | 61 | void SMTSolver::eval(SMTModel m_, SMTExpr e_, APInt &v) { 62 | char *s = boolector_bv_assignment(ctx, e); 63 | std::string str(s); 64 | boolector_free_bv_assignment(ctx, s); 65 | std::replace(str.begin(), str.end(), 'x', '0'); 66 | v = APInt(bvwidth(e), str.c_str(), 2); 67 | } 68 | 69 | void SMTSolver::release(SMTModel m_) {} 70 | 71 | void SMTSolver::dump(SMTExpr e_) { 72 | print(e, dbgs()); 73 | dbgs() << "\n"; 74 | } 75 | 76 | void SMTSolver::print(SMTExpr e_, raw_ostream &OS) { 77 | FILE *fp = tmpfile(); 78 | assert(fp && "tmpfile"); 79 | boolector_dump_btor(ctx, fp, e); 80 | long n = ftell(fp); 81 | fseek(fp, 0, SEEK_SET); 82 | char *s = (char *)malloc(n + 1); 83 | n = fread(s, 1, n, fp); 84 | assert(n > 0); 85 | s[n] = 0; 86 | OS << s; 87 | free(s); 88 | fclose(fp); 89 | } 90 | 91 | void SMTSolver::incref(SMTExpr e_) { 92 | boolector_copy(ctx, e); 93 | } 94 | 95 | void SMTSolver::decref(SMTExpr e_) { 96 | boolector_release(ctx, e); 97 | } 98 | 99 | unsigned SMTSolver::bvwidth(SMTExpr e_) { 100 | return boolector_get_width(ctx, e); 101 | } 102 | 103 | SMTExpr SMTSolver::bvfalse() { 104 | return boolector_false(ctx); 105 | } 106 | 107 | SMTExpr SMTSolver::bvtrue() { 108 | return boolector_true(ctx); 109 | } 110 | 111 | SMTExpr SMTSolver::bvconst(const APInt &Val) { 112 | unsigned intbits = sizeof(unsigned) * CHAR_BIT; 113 | unsigned width = Val.getBitWidth(); 114 | if (width <= intbits) 115 | return boolector_unsigned_int(ctx, Val.getZExtValue(), width); 116 | SmallString<32> Str, FullStr; 117 | Val.toStringUnsigned(Str, 2); 118 | assert(Str.size() <= width); 119 | FullStr.assign(width - Str.size(), '0'); 120 | FullStr += Str; 121 | return boolector_const(ctx, FullStr.c_str()); 122 | } 123 | 124 | SMTExpr SMTSolver::bvvar(unsigned width, const char *name) { 125 | return boolector_var(ctx, width, name); 126 | } 127 | 128 | SMTExpr SMTSolver::ite(SMTExpr e_, SMTExpr lhs_, SMTExpr rhs_) { 129 | return boolector_cond(ctx, e, lhs, rhs); 130 | } 131 | 132 | SMTExpr SMTSolver::eq(SMTExpr lhs_, SMTExpr rhs_) { 133 | return boolector_eq(ctx, lhs, rhs); 134 | } 135 | 136 | SMTExpr SMTSolver::ne(SMTExpr lhs_, SMTExpr rhs_) { 137 | return boolector_ne(ctx, lhs, rhs); 138 | } 139 | 140 | SMTExpr SMTSolver::bvslt(SMTExpr lhs_, SMTExpr rhs_) { 141 | return boolector_slt(ctx, lhs, rhs); 142 | } 143 | 144 | SMTExpr SMTSolver::bvsle(SMTExpr lhs_, SMTExpr rhs_) { 145 | return boolector_slte(ctx, lhs, rhs); 146 | } 147 | 148 | SMTExpr SMTSolver::bvsgt(SMTExpr lhs_, SMTExpr rhs_) { 149 | return boolector_sgt(ctx, lhs, rhs); 150 | } 151 | 152 | SMTExpr SMTSolver::bvsge(SMTExpr lhs_, SMTExpr rhs_) { 153 | return boolector_sgte(ctx, lhs, rhs); 154 | } 155 | 156 | SMTExpr SMTSolver::bvult(SMTExpr lhs_, SMTExpr rhs_) { 157 | return boolector_ult(ctx, lhs, rhs); 158 | } 159 | 160 | SMTExpr SMTSolver::bvule(SMTExpr lhs_, SMTExpr rhs_) { 161 | return boolector_ulte(ctx, lhs, rhs); 162 | } 163 | 164 | SMTExpr SMTSolver::bvugt(SMTExpr lhs_, SMTExpr rhs_) { 165 | return boolector_ugt(ctx, lhs, rhs); 166 | } 167 | 168 | SMTExpr SMTSolver::bvuge(SMTExpr lhs_, SMTExpr rhs_) { 169 | return boolector_ugte(ctx, lhs, rhs); 170 | } 171 | 172 | SMTExpr SMTSolver::extract(unsigned high, unsigned low, SMTExpr e_) { 173 | return boolector_slice(ctx, e, high, low); 174 | } 175 | 176 | SMTExpr SMTSolver::zero_extend(unsigned i, SMTExpr e_) { 177 | return boolector_uext(ctx, e, i); 178 | } 179 | 180 | SMTExpr SMTSolver::sign_extend(unsigned i, SMTExpr e_) { 181 | return boolector_sext(ctx, e, i); 182 | } 183 | 184 | SMTExpr SMTSolver::bvredand(SMTExpr e_) { 185 | return boolector_redand(ctx, e); 186 | } 187 | 188 | SMTExpr SMTSolver::bvredor(SMTExpr e_) { 189 | return boolector_redor(ctx, e); 190 | } 191 | 192 | SMTExpr SMTSolver::bvnot(SMTExpr e_) { 193 | return boolector_not(ctx, e); 194 | } 195 | 196 | SMTExpr SMTSolver::bvneg(SMTExpr e_) { 197 | return boolector_neg(ctx, e); 198 | } 199 | 200 | SMTExpr SMTSolver::bvadd(SMTExpr lhs_, SMTExpr rhs_) { 201 | return boolector_add(ctx, lhs, rhs); 202 | } 203 | 204 | SMTExpr SMTSolver::bvsub(SMTExpr lhs_, SMTExpr rhs_) { 205 | return boolector_sub(ctx, lhs, rhs); 206 | } 207 | 208 | SMTExpr SMTSolver::bvmul(SMTExpr lhs_, SMTExpr rhs_) { 209 | return boolector_mul(ctx, lhs, rhs); 210 | } 211 | 212 | SMTExpr SMTSolver::bvsdiv(SMTExpr lhs_, SMTExpr rhs_) { 213 | return boolector_sdiv(ctx, lhs, rhs); 214 | } 215 | 216 | SMTExpr SMTSolver::bvudiv(SMTExpr lhs_, SMTExpr rhs_) { 217 | return boolector_udiv(ctx, lhs, rhs); 218 | } 219 | 220 | SMTExpr SMTSolver::bvsrem(SMTExpr lhs_, SMTExpr rhs_) { 221 | return boolector_srem(ctx, lhs, rhs); 222 | } 223 | 224 | SMTExpr SMTSolver::bvurem(SMTExpr lhs_, SMTExpr rhs_) { 225 | return boolector_urem(ctx, lhs, rhs); 226 | } 227 | 228 | // Shift operations use log2n bits for shifting amount. 229 | template 230 | static inline BtorNode *shift(Btor *btor, BtorNode *e0, BtorNode *e1) { 231 | unsigned n = boolector_get_width(btor, e1); 232 | // Round up to nearest power of 2. 233 | unsigned amount = (sizeof(n) * CHAR_BIT - __builtin_clz(n - 1)); 234 | unsigned power2 = 1U << amount; 235 | // Extend e0 to power2 bits. 236 | if (power2 != n) 237 | e0 = boolector_uext(btor, e0, power2 - n); 238 | BtorNode *log2w = boolector_slice(btor, e1, amount - 1, 0); 239 | BtorNode *result = F(btor, e0, log2w); 240 | boolector_release(btor, log2w); 241 | if (power2 != n) { 242 | boolector_release(btor, e0); 243 | // Truncate result back to n bits. 244 | BtorNode *tmp = boolector_slice(btor, result, n - 1, 0); 245 | boolector_release(btor, result); 246 | result = tmp; 247 | } 248 | return result; 249 | } 250 | 251 | SMTExpr SMTSolver::bvshl(SMTExpr lhs_, SMTExpr rhs_) { 252 | // Return 0 if rhs >= n. 253 | unsigned n = boolector_get_width(ctx, rhs); 254 | BtorNode *width = boolector_unsigned_int(ctx, n, n); 255 | BtorNode *cond = boolector_ugte(ctx, rhs, width); 256 | BtorNode *zero = boolector_zero(ctx, n); 257 | BtorNode *tmp = shift(ctx, lhs, rhs); 258 | BtorNode *result = boolector_cond(ctx, cond, zero, tmp); 259 | boolector_release(ctx, width); 260 | boolector_release(ctx, cond); 261 | boolector_release(ctx, zero); 262 | boolector_release(ctx, tmp); 263 | return result; 264 | } 265 | 266 | SMTExpr SMTSolver::bvlshr(SMTExpr lhs_, SMTExpr rhs_) { 267 | // Return 0 if rhs >= n. 268 | unsigned n = boolector_get_width(ctx, rhs); 269 | BtorNode *width = boolector_unsigned_int(ctx, n, n); 270 | BtorNode *cond = boolector_ugte(ctx, rhs, width); 271 | BtorNode *zero = boolector_zero(ctx, n); 272 | BtorNode *tmp = shift(ctx, lhs, rhs); 273 | BtorNode *result = boolector_cond(ctx, cond, zero, tmp); 274 | boolector_release(ctx, width); 275 | boolector_release(ctx, cond); 276 | boolector_release(ctx, zero); 277 | boolector_release(ctx, tmp); 278 | return result; 279 | } 280 | 281 | SMTExpr SMTSolver::bvashr(SMTExpr lhs_, SMTExpr rhs_) { 282 | // If rhs is too large, the result is either zero or all-one, 283 | // the same as limiting rhs to n - 1. 284 | unsigned n = boolector_get_width(ctx, rhs); 285 | BtorNode *maxw = boolector_unsigned_int(ctx, n - 1, n); 286 | BtorNode *cond = boolector_ugt(ctx, rhs, maxw); 287 | BtorNode *rhs_max = boolector_cond(ctx, cond, maxw, rhs); 288 | BtorNode *result = shift(ctx, lhs, rhs_max); 289 | boolector_release(ctx, maxw); 290 | boolector_release(ctx, cond); 291 | boolector_release(ctx, rhs_max); 292 | return result; 293 | } 294 | 295 | SMTExpr SMTSolver::bvand(SMTExpr lhs_, SMTExpr rhs_) { 296 | return boolector_and(ctx, lhs, rhs); 297 | } 298 | 299 | SMTExpr SMTSolver::bvor(SMTExpr lhs_, SMTExpr rhs_) { 300 | return boolector_or(ctx, lhs, rhs); 301 | } 302 | 303 | SMTExpr SMTSolver::bvxor(SMTExpr lhs_, SMTExpr rhs_) { 304 | return boolector_xor(ctx, lhs, rhs); 305 | } 306 | 307 | SMTExpr SMTSolver::bvneg_overflow(SMTExpr e_) { 308 | SMTExpr zero = boolector_zero(ctx, bvwidth(e)); 309 | SMTExpr tmp = bvssub_overflow(zero, e); 310 | decref(zero); 311 | return tmp; 312 | } 313 | 314 | SMTExpr SMTSolver::bvsadd_overflow(SMTExpr lhs_, SMTExpr rhs_) { 315 | return boolector_saddo(ctx, lhs, rhs); 316 | } 317 | 318 | SMTExpr SMTSolver::bvuadd_overflow(SMTExpr lhs_, SMTExpr rhs_) { 319 | return boolector_uaddo(ctx, lhs, rhs); 320 | } 321 | 322 | SMTExpr SMTSolver::bvssub_overflow(SMTExpr lhs_, SMTExpr rhs_) { 323 | return boolector_ssubo(ctx, lhs, rhs); 324 | } 325 | 326 | SMTExpr SMTSolver::bvusub_overflow(SMTExpr lhs_, SMTExpr rhs_) { 327 | return boolector_usubo(ctx, lhs, rhs); 328 | } 329 | 330 | SMTExpr SMTSolver::bvsmul_overflow(SMTExpr lhs_, SMTExpr rhs_) { 331 | return boolector_smulo(ctx, lhs, rhs); 332 | } 333 | 334 | SMTExpr SMTSolver::bvumul_overflow(SMTExpr lhs_, SMTExpr rhs_) { 335 | return boolector_umulo(ctx, lhs, rhs); 336 | } 337 | 338 | SMTExpr SMTSolver::bvsdiv_overflow(SMTExpr lhs_, SMTExpr rhs_) { 339 | return boolector_sdivo(ctx, lhs, rhs); 340 | } 341 | -------------------------------------------------------------------------------- /src/SMTSolver.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace llvm; 8 | 9 | static cl::opt 10 | SMTTimeoutOpt("smt-timeout", 11 | cl::desc("Specify a timeout for SMT solver"), 12 | cl::value_desc("milliseconds")); 13 | 14 | static pid_t pid; 15 | 16 | int SMTFork() 17 | { 18 | if (!SMTTimeoutOpt) 19 | return 0; 20 | pid = fork(); 21 | if (pid < 0) 22 | err(1, "fork"); 23 | // Parent process. 24 | if (pid) 25 | return 1; 26 | // Child process. 27 | struct itimerval itv = {{0, 0}, {(time_t)SMTTimeoutOpt / 1000, (suseconds_t)SMTTimeoutOpt % 1000 * 1000}}; 28 | setitimer(ITIMER_VIRTUAL, &itv, NULL); 29 | return 0; 30 | } 31 | 32 | void SMTJoin(int *status) 33 | { 34 | if (!SMTTimeoutOpt) 35 | return; 36 | // Child process. 37 | if (pid == 0) 38 | _exit(*status); 39 | // Parent process. 40 | waitpid(pid, status, 0); 41 | if (WIFEXITED(*status)) 42 | *status = WEXITSTATUS(*status); 43 | else 44 | *status = -1; 45 | } 46 | -------------------------------------------------------------------------------- /src/SMTSolver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace llvm { 4 | class APInt; 5 | class raw_ostream; 6 | } // namespace llvm 7 | 8 | enum SMTStatus { 9 | SMT_TIMEOUT = -1, 10 | SMT_UNDEF, 11 | SMT_UNSAT, 12 | SMT_SAT, 13 | }; 14 | 15 | typedef void *SMTContext; 16 | typedef void *SMTExpr; 17 | typedef void *SMTModel; 18 | 19 | int SMTFork(); 20 | void SMTJoin(int *); 21 | 22 | class SMTSolver { 23 | public: 24 | SMTSolver(bool modelgen); 25 | ~SMTSolver(); 26 | 27 | void assume(SMTExpr); 28 | 29 | SMTStatus query(SMTExpr, SMTModel * = 0); 30 | void eval(SMTModel, SMTExpr, llvm::APInt &); 31 | void release(SMTModel); 32 | 33 | void dump(SMTExpr); 34 | void print(SMTExpr, llvm::raw_ostream &); 35 | 36 | void incref(SMTExpr); 37 | void decref(SMTExpr); 38 | 39 | unsigned bvwidth(SMTExpr); 40 | 41 | SMTExpr bvfalse(); 42 | SMTExpr bvtrue(); 43 | SMTExpr bvconst(const llvm::APInt &); 44 | SMTExpr bvvar(unsigned width, const char *name); 45 | 46 | // If-else-then. 47 | SMTExpr ite(SMTExpr, SMTExpr, SMTExpr); 48 | 49 | // Comparison. 50 | SMTExpr eq(SMTExpr, SMTExpr); 51 | SMTExpr ne(SMTExpr, SMTExpr); 52 | SMTExpr bvslt(SMTExpr, SMTExpr); 53 | SMTExpr bvsle(SMTExpr, SMTExpr); 54 | SMTExpr bvsgt(SMTExpr, SMTExpr); 55 | SMTExpr bvsge(SMTExpr, SMTExpr); 56 | SMTExpr bvult(SMTExpr, SMTExpr); 57 | SMTExpr bvule(SMTExpr, SMTExpr); 58 | SMTExpr bvugt(SMTExpr, SMTExpr); 59 | SMTExpr bvuge(SMTExpr, SMTExpr); 60 | 61 | SMTExpr extract(unsigned high, unsigned low, SMTExpr); 62 | SMTExpr zero_extend(unsigned i, SMTExpr); 63 | SMTExpr sign_extend(unsigned i, SMTExpr); 64 | 65 | SMTExpr bvredand(SMTExpr); 66 | SMTExpr bvredor(SMTExpr); 67 | SMTExpr bvnot(SMTExpr); 68 | SMTExpr bvneg(SMTExpr); 69 | 70 | // Arithmetic operations. 71 | SMTExpr bvadd(SMTExpr, SMTExpr); 72 | SMTExpr bvsub(SMTExpr, SMTExpr); 73 | SMTExpr bvmul(SMTExpr, SMTExpr); 74 | SMTExpr bvsdiv(SMTExpr, SMTExpr); 75 | SMTExpr bvudiv(SMTExpr, SMTExpr); 76 | SMTExpr bvsrem(SMTExpr, SMTExpr); 77 | SMTExpr bvurem(SMTExpr, SMTExpr); 78 | SMTExpr bvshl(SMTExpr, SMTExpr); 79 | SMTExpr bvlshr(SMTExpr, SMTExpr); 80 | SMTExpr bvashr(SMTExpr, SMTExpr); 81 | SMTExpr bvand(SMTExpr, SMTExpr); 82 | SMTExpr bvor(SMTExpr, SMTExpr); 83 | SMTExpr bvxor(SMTExpr, SMTExpr); 84 | 85 | // Overflow detection. 86 | SMTExpr bvneg_overflow(SMTExpr); 87 | SMTExpr bvsadd_overflow(SMTExpr, SMTExpr); 88 | SMTExpr bvuadd_overflow(SMTExpr, SMTExpr); 89 | SMTExpr bvssub_overflow(SMTExpr, SMTExpr); 90 | SMTExpr bvusub_overflow(SMTExpr, SMTExpr); 91 | SMTExpr bvsmul_overflow(SMTExpr, SMTExpr); 92 | SMTExpr bvumul_overflow(SMTExpr, SMTExpr); 93 | SMTExpr bvsdiv_overflow(SMTExpr, SMTExpr); 94 | 95 | private: 96 | SMTContext ctx_; 97 | }; 98 | -------------------------------------------------------------------------------- /src/SMTSonolar.cc: -------------------------------------------------------------------------------- 1 | #include "SMTSolver.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace llvm; 9 | 10 | #define ctx ((sonolar_t)ctx_) 11 | #define m ((sonolar_t *)m_) 12 | #define e ((sonolar_term_t *)e_) 13 | #define lhs ((sonolar_term_t *)lhs_) 14 | #define rhs ((sonolar_term_t *)rhs_) 15 | 16 | SMTSolver::SMTSolver(bool /*modelgen*/) { 17 | ctx_ = sonolar_create(); 18 | if (sonolar_set_sat_solver(ctx, SONOLAR_SAT_SOLVER_MINISAT)) 19 | assert(0 && "sonolar_set_sat_solver"); 20 | } 21 | 22 | SMTSolver::~SMTSolver() { 23 | sonolar_destroy(ctx); 24 | } 25 | 26 | void SMTSolver::assume(SMTExpr e_) { 27 | sonolar_assert_formula(ctx, e); 28 | } 29 | 30 | SMTStatus SMTSolver::query(SMTExpr e_, SMTModel *m_) { 31 | if (sonolar_assume_formula(ctx, e)) 32 | assert(0 && "sonolar_assume_formula"); 33 | switch (sonolar_solve(ctx)) { 34 | default: return SMT_UNDEF; 35 | case SONOLAR_SOLVE_RESULT_UNSAT: return SMT_UNSAT; 36 | case SONOLAR_SOLVE_RESULT_SAT: break; 37 | } 38 | if (m_) 39 | *m_ = ctx; 40 | return SMT_SAT; 41 | } 42 | 43 | void SMTSolver::eval(SMTModel m_, SMTExpr e_, APInt &) { 44 | assert(0 && "NOT SUPPORTED"); 45 | } 46 | 47 | void SMTSolver::release(SMTModel m_) {} 48 | 49 | void SMTSolver::dump(SMTExpr e_) { 50 | print(e, dbgs()); 51 | dbgs() << "\n"; 52 | } 53 | 54 | void SMTSolver::print(SMTExpr e_, raw_ostream &OS) { 55 | OS << "NOT SUPPORTED"; 56 | } 57 | 58 | void SMTSolver::incref(SMTExpr e_) { 59 | sonolar_add_reference(ctx, e); 60 | } 61 | 62 | void SMTSolver::decref(SMTExpr e_) { 63 | sonolar_remove_reference(ctx, e); 64 | } 65 | 66 | unsigned SMTSolver::bvwidth(SMTExpr e_) { 67 | size_t width; 68 | if (sonolar_get_bitwidth(ctx, e, &width)) 69 | assert(0 && "sonolar_get_bitwidth"); 70 | return (unsigned)width; 71 | } 72 | 73 | SMTExpr SMTSolver::bvfalse() { 74 | return sonolar_make_constant_false(ctx); 75 | } 76 | 77 | SMTExpr SMTSolver::bvtrue() { 78 | return sonolar_make_constant_true(ctx); 79 | } 80 | 81 | SMTExpr SMTSolver::bvconst(const APInt &Val) { 82 | const void *data = Val.getRawData(); 83 | unsigned width = Val.getBitWidth(); 84 | return sonolar_make_constant_bytes(ctx, data, width, SONOLAR_BYTE_ORDER_NATIVE); 85 | } 86 | 87 | SMTExpr SMTSolver::bvvar(unsigned width, const char *name) { 88 | return sonolar_make_variable(ctx, width, name); 89 | } 90 | 91 | SMTExpr SMTSolver::ite(SMTExpr e_, SMTExpr lhs_, SMTExpr rhs_) { 92 | return sonolar_make_ite(ctx, e, lhs, rhs); 93 | } 94 | 95 | SMTExpr SMTSolver::eq(SMTExpr lhs_, SMTExpr rhs_) { 96 | return sonolar_make_equal(ctx, lhs, rhs); 97 | } 98 | 99 | SMTExpr SMTSolver::ne(SMTExpr lhs_, SMTExpr rhs_) { 100 | return sonolar_make_distinct(ctx, lhs, rhs); 101 | } 102 | 103 | SMTExpr SMTSolver::bvslt(SMTExpr lhs_, SMTExpr rhs_) { 104 | return sonolar_make_bv_slt(ctx, lhs, rhs); 105 | } 106 | 107 | SMTExpr SMTSolver::bvsle(SMTExpr lhs_, SMTExpr rhs_) { 108 | return sonolar_make_bv_sle(ctx, lhs, rhs); 109 | } 110 | 111 | SMTExpr SMTSolver::bvsgt(SMTExpr lhs_, SMTExpr rhs_) { 112 | return sonolar_make_bv_sgt(ctx, lhs, rhs); 113 | } 114 | 115 | SMTExpr SMTSolver::bvsge(SMTExpr lhs_, SMTExpr rhs_) { 116 | return sonolar_make_bv_sge(ctx, lhs, rhs); 117 | } 118 | 119 | SMTExpr SMTSolver::bvult(SMTExpr lhs_, SMTExpr rhs_) { 120 | return sonolar_make_bv_ult(ctx, lhs, rhs); 121 | } 122 | 123 | SMTExpr SMTSolver::bvule(SMTExpr lhs_, SMTExpr rhs_) { 124 | return sonolar_make_bv_ule(ctx, lhs, rhs); 125 | } 126 | 127 | SMTExpr SMTSolver::bvugt(SMTExpr lhs_, SMTExpr rhs_) { 128 | return sonolar_make_bv_ugt(ctx, lhs, rhs); 129 | } 130 | 131 | SMTExpr SMTSolver::bvuge(SMTExpr lhs_, SMTExpr rhs_) { 132 | return sonolar_make_bv_uge(ctx, lhs, rhs); 133 | } 134 | 135 | SMTExpr SMTSolver::extract(unsigned high, unsigned low, SMTExpr e_) { 136 | return sonolar_make_bv_extract(ctx, e, high, low); 137 | } 138 | 139 | SMTExpr SMTSolver::zero_extend(unsigned i, SMTExpr e_) { 140 | return sonolar_make_bv_zero_extend(ctx, e, i); 141 | } 142 | 143 | SMTExpr SMTSolver::sign_extend(unsigned i, SMTExpr e_) { 144 | return sonolar_make_bv_sign_extend(ctx, e, i); 145 | } 146 | 147 | SMTExpr SMTSolver::bvredand(SMTExpr e_) { 148 | SMTExpr neg = bvnot(e); 149 | SMTExpr tmp = sonolar_make_is_zero(ctx, neg); 150 | decref(neg); 151 | return tmp; 152 | } 153 | 154 | SMTExpr SMTSolver::bvredor(SMTExpr e_) { 155 | SMTExpr z = sonolar_make_is_zero(ctx, e); 156 | SMTExpr nz = sonolar_make_not(ctx, z); 157 | decref(z); 158 | return nz; 159 | } 160 | 161 | SMTExpr SMTSolver::bvnot(SMTExpr e_) { 162 | return sonolar_make_bv_not(ctx, e); 163 | } 164 | 165 | SMTExpr SMTSolver::bvneg(SMTExpr e_) { 166 | return sonolar_make_bv_neg(ctx, e); 167 | } 168 | 169 | SMTExpr SMTSolver::bvadd(SMTExpr lhs_, SMTExpr rhs_) { 170 | return sonolar_make_bv_add(ctx, lhs, rhs); 171 | } 172 | 173 | SMTExpr SMTSolver::bvsub(SMTExpr lhs_, SMTExpr rhs_) { 174 | return sonolar_make_bv_sub(ctx, lhs, rhs); 175 | } 176 | 177 | SMTExpr SMTSolver::bvmul(SMTExpr lhs_, SMTExpr rhs_) { 178 | return sonolar_make_bv_mul(ctx, lhs, rhs); 179 | } 180 | 181 | SMTExpr SMTSolver::bvsdiv(SMTExpr lhs_, SMTExpr rhs_) { 182 | return sonolar_make_bv_sdiv(ctx, lhs, rhs); 183 | } 184 | 185 | SMTExpr SMTSolver::bvudiv(SMTExpr lhs_, SMTExpr rhs_) { 186 | return sonolar_make_bv_udiv(ctx, lhs, rhs); 187 | } 188 | 189 | SMTExpr SMTSolver::bvsrem(SMTExpr lhs_, SMTExpr rhs_) { 190 | return sonolar_make_bv_srem(ctx, lhs, rhs); 191 | } 192 | 193 | SMTExpr SMTSolver::bvurem(SMTExpr lhs_, SMTExpr rhs_) { 194 | return sonolar_make_bv_urem(ctx, lhs, rhs); 195 | } 196 | 197 | SMTExpr SMTSolver::bvshl(SMTExpr lhs_, SMTExpr rhs_) { 198 | return sonolar_make_bv_shl(ctx, lhs, rhs); 199 | } 200 | 201 | SMTExpr SMTSolver::bvlshr(SMTExpr lhs_, SMTExpr rhs_) { 202 | return sonolar_make_bv_lshr(ctx, lhs, rhs); 203 | } 204 | 205 | SMTExpr SMTSolver::bvashr(SMTExpr lhs_, SMTExpr rhs_) { 206 | return sonolar_make_bv_ashr(ctx, lhs, rhs); 207 | } 208 | 209 | SMTExpr SMTSolver::bvand(SMTExpr lhs_, SMTExpr rhs_) { 210 | return sonolar_make_bv_and(ctx, lhs, rhs); 211 | } 212 | 213 | SMTExpr SMTSolver::bvor(SMTExpr lhs_, SMTExpr rhs_) { 214 | return sonolar_make_bv_or(ctx, lhs, rhs); 215 | } 216 | 217 | SMTExpr SMTSolver::bvxor(SMTExpr lhs_, SMTExpr rhs_) { 218 | return sonolar_make_bv_xor(ctx, lhs, rhs); 219 | } 220 | 221 | SMTExpr SMTSolver::bvneg_overflow(SMTExpr e_) { 222 | SMTExpr zero = sonolar_make_constant_0_bits(ctx, bvwidth(e)); 223 | SMTExpr tmp = bvssub_overflow(zero, e); 224 | decref(zero); 225 | return tmp; 226 | } 227 | 228 | SMTExpr SMTSolver::bvsadd_overflow(SMTExpr lhs_, SMTExpr rhs_) { 229 | return sonolar_make_bv_sadd_ovfl(ctx, lhs, rhs); 230 | } 231 | 232 | SMTExpr SMTSolver::bvuadd_overflow(SMTExpr lhs_, SMTExpr rhs_) { 233 | return sonolar_make_bv_uadd_ovfl(ctx, lhs, rhs); 234 | } 235 | 236 | SMTExpr SMTSolver::bvssub_overflow(SMTExpr lhs_, SMTExpr rhs_) { 237 | return sonolar_make_bv_ssub_ovfl(ctx, lhs, rhs); 238 | } 239 | 240 | SMTExpr SMTSolver::bvusub_overflow(SMTExpr lhs_, SMTExpr rhs_) { 241 | return sonolar_make_bv_usub_ovfl(ctx, lhs, rhs); 242 | } 243 | 244 | SMTExpr SMTSolver::bvsmul_overflow(SMTExpr lhs_, SMTExpr rhs_) { 245 | return sonolar_make_bv_smul_ovfl(ctx, lhs, rhs); 246 | } 247 | 248 | SMTExpr SMTSolver::bvumul_overflow(SMTExpr lhs_, SMTExpr rhs_) { 249 | return sonolar_make_bv_umul_ovfl(ctx, lhs, rhs); 250 | } 251 | 252 | SMTExpr SMTSolver::bvsdiv_overflow(SMTExpr lhs_, SMTExpr rhs_) { 253 | return sonolar_make_bv_sdiv_ovfl(ctx, lhs, rhs); 254 | } 255 | -------------------------------------------------------------------------------- /src/SMTZ3.cc: -------------------------------------------------------------------------------- 1 | #include "SMTSolver.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace llvm; 10 | 11 | struct SMTContextImpl { 12 | Z3_context c; 13 | Z3_ast bvfalse; 14 | Z3_ast bvtrue; 15 | }; 16 | 17 | #define imp ((SMTContextImpl *)ctx_) 18 | #define ctx (imp->c) 19 | #define m ((Z3_model)m_) 20 | #define e ((Z3_ast)e_) 21 | #define lhs ((Z3_ast)lhs_) 22 | #define rhs ((Z3_ast)rhs_) 23 | 24 | static inline Z3_ast bv2bool_(SMTContextImpl *ctx_, Z3_ast e0) { 25 | return Z3_mk_eq(ctx, e0, ctx_->bvtrue); 26 | } 27 | 28 | static inline Z3_ast bool2bv_(SMTContextImpl *ctx_, Z3_ast e0) { 29 | return Z3_mk_ite(ctx, e0, ctx_->bvtrue, ctx_->bvfalse); 30 | } 31 | 32 | #define bv2bool(x) bv2bool_(imp, x) 33 | #define bool2bv(x) bool2bv_(imp, x) 34 | 35 | SMTSolver::SMTSolver(bool modelgen) { 36 | ctx_ = new SMTContextImpl; 37 | Z3_config cfg = Z3_mk_config(); 38 | // Enable model construction. 39 | if (modelgen) 40 | Z3_set_param_value(cfg, "MODEL", "true"); 41 | ctx = Z3_mk_context(cfg); 42 | Z3_del_config(cfg); 43 | // Set up constants. 44 | Z3_sort sort = Z3_mk_bv_sort(ctx, 1); 45 | imp->bvfalse = Z3_mk_int(ctx, 0, sort); 46 | imp->bvtrue = Z3_mk_int(ctx, 1, sort); 47 | } 48 | 49 | SMTSolver::~SMTSolver() { 50 | Z3_del_context(ctx); 51 | delete imp; 52 | } 53 | 54 | void SMTSolver::assume(SMTExpr e_) { 55 | Z3_assert_cnstr(ctx, bv2bool(e)); 56 | } 57 | 58 | SMTStatus SMTSolver::query(SMTExpr e_, SMTModel *m_) { 59 | Z3_push(ctx); 60 | Z3_assert_cnstr(ctx, bv2bool(e)); 61 | Z3_lbool res = Z3_check_and_get_model(ctx, (Z3_model *)m_); 62 | Z3_pop(ctx, 1); 63 | switch (res) { 64 | default: return SMT_UNDEF; 65 | case Z3_L_FALSE: return SMT_UNSAT; 66 | case Z3_L_TRUE: return SMT_SAT; 67 | } 68 | } 69 | 70 | void SMTSolver::eval(SMTModel m_, SMTExpr e_, APInt &r) { 71 | Z3_ast v = 0; 72 | Z3_bool ret = Z3_model_eval(ctx, m, e, Z3_TRUE, &v); 73 | assert(ret); 74 | assert(v); 75 | if (Z3_is_numeral_ast(ctx, v)) { 76 | r = APInt(bvwidth(v), Z3_get_numeral_string(ctx, v), 10); 77 | return; 78 | } 79 | if (bvwidth(v) == 1 && Z3_is_app(ctx, v)) { 80 | Z3_push(ctx); 81 | Z3_assert_cnstr(ctx, Z3_mk_eq(ctx, v, imp->bvtrue)); 82 | switch (Z3_check(ctx)) { 83 | default: assert(0); 84 | case Z3_L_FALSE: r = APInt(1, 0); break; 85 | case Z3_L_TRUE: r = APInt(1, 1); break; 86 | } 87 | Z3_pop(ctx, 1); 88 | return; 89 | } 90 | assert(0); 91 | } 92 | 93 | void SMTSolver::release(SMTModel m_) { 94 | Z3_del_model(ctx, m); 95 | } 96 | 97 | void SMTSolver::dump(SMTExpr e_) { 98 | print(e, dbgs()); 99 | dbgs() << "\n"; 100 | } 101 | 102 | void SMTSolver::print(SMTExpr e_, raw_ostream &OS) { 103 | OS << Z3_ast_to_string(ctx, Z3_simplify(ctx, e)); 104 | } 105 | 106 | // Managed by Z3, no reference counting. 107 | void SMTSolver::incref(SMTExpr) { } 108 | void SMTSolver::decref(SMTExpr) { } 109 | 110 | unsigned SMTSolver::bvwidth(SMTExpr e_) { 111 | return Z3_get_bv_sort_size(ctx, Z3_get_sort(ctx, e)); 112 | } 113 | 114 | SMTExpr SMTSolver::bvfalse() { 115 | return imp->bvfalse; 116 | } 117 | 118 | SMTExpr SMTSolver::bvtrue() { 119 | return imp->bvtrue; 120 | } 121 | 122 | SMTExpr SMTSolver::bvconst(const APInt &Val) { 123 | unsigned width = Val.getBitWidth(); 124 | Z3_sort t = Z3_mk_bv_sort(ctx, width); 125 | if (width <= 64) 126 | return Z3_mk_unsigned_int64(ctx, Val.getZExtValue(), t); 127 | SmallString<32> s; 128 | Val.toStringUnsigned(s); 129 | return Z3_mk_numeral(ctx, s.c_str(), t); 130 | } 131 | 132 | SMTExpr SMTSolver::bvvar(unsigned width, const char *name) { 133 | return Z3_mk_const(ctx, Z3_mk_string_symbol(ctx, name), Z3_mk_bv_sort(ctx, width)); 134 | } 135 | 136 | SMTExpr SMTSolver::ite(SMTExpr e_, SMTExpr lhs_, SMTExpr rhs_) { 137 | return Z3_mk_ite(ctx, bv2bool(e), lhs, rhs); 138 | } 139 | 140 | SMTExpr SMTSolver::eq(SMTExpr lhs_, SMTExpr rhs_) { 141 | return bool2bv(Z3_mk_eq(ctx, lhs, rhs)); 142 | } 143 | 144 | SMTExpr SMTSolver::ne(SMTExpr lhs_, SMTExpr rhs_) { 145 | return bvnot(eq(lhs_, rhs_)); 146 | } 147 | 148 | SMTExpr SMTSolver::bvslt(SMTExpr lhs_, SMTExpr rhs_) { 149 | return bool2bv(Z3_mk_bvslt(ctx, lhs, rhs)); 150 | } 151 | 152 | SMTExpr SMTSolver::bvsle(SMTExpr lhs_, SMTExpr rhs_) { 153 | return bool2bv(Z3_mk_bvsle(ctx, lhs, rhs)); 154 | } 155 | 156 | SMTExpr SMTSolver::bvsgt(SMTExpr lhs_, SMTExpr rhs_) { 157 | return bool2bv(Z3_mk_bvsgt(ctx, lhs, rhs)); 158 | } 159 | 160 | SMTExpr SMTSolver::bvsge(SMTExpr lhs_, SMTExpr rhs_) { 161 | return bool2bv(Z3_mk_bvsge(ctx, lhs, rhs)); 162 | } 163 | 164 | SMTExpr SMTSolver::bvult(SMTExpr lhs_, SMTExpr rhs_) { 165 | return bool2bv(Z3_mk_bvult(ctx, lhs, rhs)); 166 | } 167 | 168 | SMTExpr SMTSolver::bvule(SMTExpr lhs_, SMTExpr rhs_) { 169 | return bool2bv(Z3_mk_bvule(ctx, lhs, rhs)); 170 | } 171 | 172 | SMTExpr SMTSolver::bvugt(SMTExpr lhs_, SMTExpr rhs_) { 173 | return bool2bv(Z3_mk_bvugt(ctx, lhs, rhs)); 174 | } 175 | 176 | SMTExpr SMTSolver::bvuge(SMTExpr lhs_, SMTExpr rhs_) { 177 | return bool2bv(Z3_mk_bvuge(ctx, lhs, rhs)); 178 | } 179 | 180 | SMTExpr SMTSolver::extract(unsigned high, unsigned low, SMTExpr e_) { 181 | return Z3_mk_extract(ctx, high, low, e); 182 | } 183 | 184 | SMTExpr SMTSolver::zero_extend(unsigned i, SMTExpr e_) { 185 | return Z3_mk_zero_ext(ctx, i, e); 186 | } 187 | 188 | SMTExpr SMTSolver::sign_extend(unsigned i, SMTExpr e_) { 189 | return Z3_mk_sign_ext(ctx, i, e); 190 | } 191 | 192 | SMTExpr SMTSolver::bvredand(SMTExpr e_) { 193 | return Z3_mk_bvredand(ctx, e); 194 | } 195 | 196 | SMTExpr SMTSolver::bvredor(SMTExpr e_) { 197 | return Z3_mk_bvredor(ctx, e); 198 | } 199 | 200 | SMTExpr SMTSolver::bvnot(SMTExpr e_) { 201 | return Z3_mk_bvnot(ctx, e); 202 | } 203 | 204 | SMTExpr SMTSolver::bvneg(SMTExpr e_) { 205 | return Z3_mk_bvneg(ctx, e); 206 | } 207 | 208 | SMTExpr SMTSolver::bvadd(SMTExpr lhs_, SMTExpr rhs_) { 209 | return Z3_mk_bvadd(ctx, lhs, rhs); 210 | } 211 | 212 | SMTExpr SMTSolver::bvsub(SMTExpr lhs_, SMTExpr rhs_) { 213 | return Z3_mk_bvsub(ctx, lhs, rhs); 214 | } 215 | 216 | SMTExpr SMTSolver::bvmul(SMTExpr lhs_, SMTExpr rhs_) { 217 | return Z3_mk_bvmul(ctx, lhs, rhs); 218 | } 219 | 220 | SMTExpr SMTSolver::bvsdiv(SMTExpr lhs_, SMTExpr rhs_) { 221 | return Z3_mk_bvsdiv(ctx, lhs, rhs); 222 | } 223 | 224 | SMTExpr SMTSolver::bvudiv(SMTExpr lhs_, SMTExpr rhs_) { 225 | return Z3_mk_bvudiv(ctx, lhs, rhs); 226 | } 227 | 228 | SMTExpr SMTSolver::bvsrem(SMTExpr lhs_, SMTExpr rhs_) { 229 | return Z3_mk_bvsrem(ctx, lhs, rhs); 230 | } 231 | 232 | SMTExpr SMTSolver::bvurem(SMTExpr lhs_, SMTExpr rhs_) { 233 | return Z3_mk_bvurem(ctx, lhs, rhs); 234 | } 235 | 236 | SMTExpr SMTSolver::bvshl(SMTExpr lhs_, SMTExpr rhs_) { 237 | return Z3_mk_bvshl(ctx, lhs, rhs); 238 | } 239 | 240 | SMTExpr SMTSolver::bvlshr(SMTExpr lhs_, SMTExpr rhs_) { 241 | return Z3_mk_bvlshr(ctx, lhs, rhs); 242 | } 243 | 244 | SMTExpr SMTSolver::bvashr(SMTExpr lhs_, SMTExpr rhs_) { 245 | return Z3_mk_bvashr(ctx, lhs, rhs); 246 | } 247 | 248 | SMTExpr SMTSolver::bvand(SMTExpr lhs_, SMTExpr rhs_) { 249 | return Z3_mk_bvand(ctx, lhs, rhs); 250 | } 251 | 252 | SMTExpr SMTSolver::bvor(SMTExpr lhs_, SMTExpr rhs_) { 253 | return Z3_mk_bvor(ctx, lhs, rhs); 254 | } 255 | 256 | SMTExpr SMTSolver::bvxor(SMTExpr lhs_, SMTExpr rhs_) { 257 | return Z3_mk_bvxor(ctx, lhs, rhs); 258 | } 259 | 260 | SMTExpr SMTSolver::bvneg_overflow(SMTExpr e_) { 261 | return bvnot(bool2bv(Z3_mk_bvneg_no_overflow(ctx, e))); 262 | } 263 | 264 | SMTExpr SMTSolver::bvsadd_overflow(SMTExpr lhs_, SMTExpr rhs_) { 265 | return bvor( 266 | bvnot(bool2bv(Z3_mk_bvadd_no_overflow(ctx, lhs, rhs, Z3_TRUE))), 267 | bvnot(bool2bv(Z3_mk_bvadd_no_underflow(ctx, lhs, rhs))) 268 | ); 269 | } 270 | 271 | SMTExpr SMTSolver::bvuadd_overflow(SMTExpr lhs_, SMTExpr rhs_) { 272 | return bvnot(bool2bv(Z3_mk_bvadd_no_overflow(ctx, lhs, rhs, Z3_FALSE))); 273 | } 274 | 275 | SMTExpr SMTSolver::bvssub_overflow(SMTExpr lhs_, SMTExpr rhs_) { 276 | return bvor( 277 | bvnot(bool2bv(Z3_mk_bvsub_no_overflow(ctx, lhs, rhs))), 278 | bvnot(bool2bv(Z3_mk_bvsub_no_underflow(ctx, lhs, rhs, Z3_TRUE))) 279 | ); 280 | } 281 | 282 | SMTExpr SMTSolver::bvusub_overflow(SMTExpr lhs_, SMTExpr rhs_) { 283 | return bvnot(bool2bv(Z3_mk_bvsub_no_underflow(ctx, lhs, rhs, Z3_FALSE))); 284 | } 285 | 286 | SMTExpr SMTSolver::bvsmul_overflow(SMTExpr lhs_, SMTExpr rhs_) { 287 | return bvor( 288 | bvnot(bool2bv(Z3_mk_bvmul_no_overflow(ctx, lhs, rhs, Z3_TRUE))), 289 | bvnot(bool2bv(Z3_mk_bvmul_no_underflow(ctx, lhs, rhs))) 290 | ); 291 | } 292 | 293 | SMTExpr SMTSolver::bvumul_overflow(SMTExpr lhs_, SMTExpr rhs_) { 294 | return bvnot(bool2bv(Z3_mk_bvmul_no_overflow(ctx, lhs, rhs, Z3_FALSE))); 295 | } 296 | 297 | SMTExpr SMTSolver::bvsdiv_overflow(SMTExpr lhs_, SMTExpr rhs_) { 298 | return bvnot(bool2bv(Z3_mk_bvsdiv_no_overflow(ctx, lhs, rhs))); 299 | } 300 | -------------------------------------------------------------------------------- /src/SimplifyDelete.cc: -------------------------------------------------------------------------------- 1 | // This pass removes redundant NULL pointer checks Clang emits 2 | // for C++ delete. 3 | 4 | #define DEBUG_TYPE "simplify-delete" 5 | #include "BugOn.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | using namespace llvm; 15 | 16 | namespace { 17 | 18 | struct SimplifyDelete : FunctionPass { 19 | static char ID; 20 | SimplifyDelete() : FunctionPass(ID) { 21 | PassRegistry &Registry = *PassRegistry::getPassRegistry(); 22 | initializeDominatorTreePass(Registry); 23 | } 24 | 25 | void getAnalysisUsage(AnalysisUsage &AU) const { 26 | AU.addRequired(); 27 | } 28 | 29 | virtual bool runOnFunction(Function &); 30 | 31 | private: 32 | bool removeUnreachable(Function &); 33 | bool visitDeleteBB(BasicBlock *); 34 | }; 35 | 36 | } // anonymous namespace 37 | 38 | bool SimplifyDelete::runOnFunction(Function &F) { 39 | bool Changed = false; 40 | Changed |= removeUnreachable(F); 41 | for (Function::iterator i = F.begin(), e = F.end(); i != e; ) { 42 | BasicBlock *BB = i++; 43 | Changed |= visitDeleteBB(BB); 44 | Changed |= ConstantFoldTerminator(BB, true); 45 | Changed |= EliminateDuplicatePHINodes(BB); 46 | // Must be the last one. 47 | Changed |= MergeBlockIntoPredecessor(BB, this); 48 | } 49 | return Changed; 50 | } 51 | 52 | bool SimplifyDelete::removeUnreachable(Function &F) { 53 | DominatorTree &DT = getAnalysis(); 54 | SmallVector UnreachableBB; 55 | for (BasicBlock &BB : F) { 56 | if (DT.isReachableFromEntry(&BB)) 57 | continue; 58 | for (succ_iterator i = succ_begin(&BB), e = succ_end(&BB); i != e; ++i) { 59 | BasicBlock *Succ = *i; 60 | if (DT.isReachableFromEntry(Succ)) 61 | Succ->removePredecessor(&BB); 62 | } 63 | BB.dropAllReferences(); 64 | UnreachableBB.push_back(&BB); 65 | } 66 | if (UnreachableBB.empty()) 67 | return false; 68 | for (BasicBlock *BB : UnreachableBB) 69 | BB->eraseFromParent(); 70 | return true; 71 | } 72 | 73 | bool SimplifyDelete::visitDeleteBB(BasicBlock *BB) { 74 | // Clang emits BB with this special name for BB. 75 | // It works better with overloaded new/delete. 76 | StringRef Name = BB->getName(); 77 | if (!Name.startswith("new.notnull") 78 | && !Name.startswith("delete.notnull") 79 | && !Name.startswith("cast.notnull")) 80 | return false; 81 | BasicBlock *Pred = BB->getSinglePredecessor(); 82 | if (!Pred) 83 | return false; 84 | BranchInst *BI = dyn_cast(Pred->getTerminator()); 85 | if (!BI || !BI->isConditional()) 86 | return false; 87 | // Remove debugging information to ignore the check. 88 | return BugOnPass::clearDebugLoc(BI->getCondition()); 89 | } 90 | 91 | char SimplifyDelete::ID; 92 | 93 | static RegisterPass 94 | X("simplify-delete", "Remove redundant NULL pointer checks"); 95 | -------------------------------------------------------------------------------- /src/ValueGen.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "SMTSolver.h" 6 | 7 | class ValueGen { 8 | public: 9 | llvm::DataLayout &TD; 10 | SMTSolver &SMT; 11 | 12 | typedef llvm::DenseMap ValueExprMap; 13 | typedef ValueExprMap::iterator iterator; 14 | ValueExprMap Cache; 15 | 16 | ValueGen(llvm::DataLayout &, SMTSolver &); 17 | ~ValueGen(); 18 | 19 | static bool isAnalyzable(llvm::Value *); 20 | static bool isAnalyzable(llvm::Type *); 21 | SMTExpr get(llvm::Value *); 22 | 23 | iterator begin() { return Cache.begin(); } 24 | iterator end() { return Cache.end(); } 25 | }; 26 | -------------------------------------------------------------------------------- /src/ncpu: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import multiprocessing 4 | 5 | print(multiprocessing.cpu_count()) 6 | -------------------------------------------------------------------------------- /src/optck: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | DIR=$(dirname "${BASH_SOURCE[0]}") 4 | OPT="`llvm-config --bindir`/opt" 5 | exec ${OPT} --disable-output -load=${DIR}/../lib/liboptck.so \ 6 | -targetlibinfo -tbaa -basicaa -globalopt -sccp -deadargelim \ 7 | -basiccg -prune-eh -simplify-delete -load-elim \ 8 | -inline-only -functionattrs -argpromotion \ 9 | -strip-dead-prototypes \ 10 | -adce \ 11 | -elim-assert \ 12 | -bugon-null \ 13 | -bugon-gep \ 14 | -bugon-bounds \ 15 | -bugon-free \ 16 | -bugon-alias \ 17 | -bugon-int \ 18 | -bugon-libc -bugon-linux \ 19 | -ignore-bugon-post \ 20 | -anti-dce \ 21 | -anti-simplify \ 22 | -anti-algebra \ 23 | -show-bugon-true \ 24 | "$@" 2>&1 25 | -------------------------------------------------------------------------------- /src/poptck: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$1" = "-v" ]; then 4 | XARGSVERBOSE="-t" 5 | XARGSECHO="" 6 | else 7 | XARGSVERBOSE="" 8 | XARGSECHO="echo Analyzing \"\$0\" ;" 9 | fi 10 | 11 | DIR=$(dirname "${BASH_SOURCE[0]}") 12 | NCPU=`${DIR}/ncpu` 13 | OUT='pstack.txt' 14 | TIMEOUT=5000 15 | TOTALSEC=1000 16 | find . -name '*.ll' -type f -print0 | xargs -0 -P ${NCPU} -n 1 ${XARGSVERBOSE} bash -c "${XARGSECHO} ${DIR}/optck -smt-timeout=${TIMEOUT} -global-timeout-sec=${TOTALSEC} -enable-global-timeout \"\$0\" > \"\$0.out\"" 17 | rm -f ${OUT} 18 | find . -name '*.ll.out' -type f -print0 | xargs -0 -n 1 bash -c "cat \"\$0\" >> ${OUT}" 19 | 20 | NBUGS=`grep -c ^bug: ${OUT}` 21 | echo "Generated ${NBUGS} warnings, see ${OUT} for details." 22 | -------------------------------------------------------------------------------- /test/Makefile.am: -------------------------------------------------------------------------------- 1 | EXTRA_DIST = lit.cfg.in cc1 diagdiff gcc g++ 2 | 3 | LITFLAGS ?= -v 4 | LITTESTS ?= $(builddir) 5 | check-local: lit.cfg 6 | @$(top_srcdir)/lib/lit/lit.py $(LITFLAGS) $(LITTESTS) 7 | -------------------------------------------------------------------------------- /test/arm-linux-gnueabi-g++: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | DIR=$(dirname "${BASH_SOURCE[0]}") 3 | CLANG=`which clang` 4 | export LLVMCC="$CLANG -no-integrated-as -target armv7-gnu-linux-eabi" 5 | "$DIR/cc1" "$@" > /dev/null 6 | /usr/bin/arm-linux-gnueabi-g++ "$@" 7 | -------------------------------------------------------------------------------- /test/arm-linux-gnueabi-gcc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | DIR=$(dirname "${BASH_SOURCE[0]}") 3 | CLANG=`which clang` 4 | export LLVMCC="$CLANG -no-integrated-as -target armv7-gnu-linux-eabi" 5 | "$DIR/cc1" "$@" > /dev/null 6 | /usr/bin/arm-linux-gnueabi-gcc "$@" 7 | -------------------------------------------------------------------------------- /test/cc1: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import subprocess 5 | import sys 6 | 7 | def cc(llvmcc, src, argv): 8 | out = [i for i, x in enumerate(argv) if x == '-o'] 9 | if not out: 10 | out = src 11 | else: 12 | out = argv[out[-1] + 1] 13 | # Why hiding .o? 14 | if out.startswith('/tmp/') or out.startswith('/var/tmp/'): 15 | out = src 16 | if out != '-': 17 | out = os.path.splitext(out)[0] + '.%d.ll' % os.getpid() 18 | argv += ['-o', '-'] 19 | # Remove profiling flags. 20 | argv = [x for x in argv if x not in ['-pg', '-fprofile-arcs', '-ftest-coverage']] 21 | # Removing debug flags. 22 | argv = [x for x in argv if x not in ['-ggdb', '-gstabs']] 23 | # Remove warning flags. 24 | argv = [x for x in argv if not x.startswith('-W')] 25 | # Linux kernel hack: disable asm goto. 26 | argv = [x for x in argv if x != '-DCC_HAVE_ASM_GOTO'] 27 | # Remove std flags (error in C++). 28 | argv = [x for x in argv if x not in ['-std=gnu99']] 29 | # Use -fstrict-overflow to distinguish signed/unsigned integers. 30 | argv = [x for x in argv if x not in ['-fwrapv', '-fno-strict-overflow']] 31 | # Remove runtime undefined error checking options 32 | argv = [x for x in argv if x not in ['-ftrapv', '-fcatch-undefined-errors']] 33 | # Remove other runtime error checking options from clang 34 | argv = [x for x in argv if not x.startswith('-fsanitize=')] 35 | # Drop existing _FORTIFY_SOURCE. 36 | argv = [x for x in argv if not x.startswith('-D_FORTIFY_SOURCE')] 37 | # Additional options. 38 | more = ['-Qunused-arguments', '-w', '-S', '-flto', '-O0', '-g', '-D_FORTIFY_SOURCE=0'] 39 | # Frontend plugin. 40 | intfe = os.path.join(os.path.dirname(__file__), '..', '..', 'lib', 'liboptfe.so') 41 | plugin = ['-Xclang', '-load', '-Xclang', intfe, '-Xclang', '-plugin', '-Xclang', 'intfe'] 42 | p1 = subprocess.Popen(llvmcc + argv + more + plugin + [src], stdout=subprocess.PIPE) 43 | # Don't invoke -early-cse, which may hide undefined behavior bugs. 44 | # Don't invoke -simplifycfg, which may combine basic blocks. 45 | opts = ['-strip-debug-declare', '-scalarrepl', '-lower-expect'] 46 | p2 = subprocess.Popen(['opt', '-S', '-o', out] + opts, stdin=p1.stdout) 47 | p1.stdout.close() 48 | p2.communicate() 49 | return p1.returncode 50 | 51 | def path_find(progname, dirs): 52 | for d in dirs: 53 | p = os.path.join(d, progname) 54 | if os.path.exists(p): 55 | return p 56 | return progname 57 | 58 | def main(): 59 | llvmbindir = subprocess.check_output(['llvm-config', '--bindir'], universal_newlines=True).strip() 60 | clangpath = path_find('clang', [llvmbindir, '/usr/bin', '/usr/local/bin']) 61 | clang = clangpath + ' -no-integrated-as' 62 | llvmcc = os.getenv('LLVMCC', clang).split(' ') 63 | argv = sys.argv[1:] 64 | exts = ['.c', '.cc', '.cpp', '.cxx', '.C'] 65 | # Keep silence for preprocesssing and make depend. 66 | if any(a in argv for a in ['-E', '-M', '-MM']): 67 | return 68 | # Extrace input source files. 69 | srcs = [a for a in argv if os.path.splitext(a)[1] in exts] 70 | # Keep silence if only '-' is given; otherwise we need to duplicate 71 | # data from stdin for the next consumer (e.g., gcc). 72 | if not srcs: 73 | return 74 | # Remove source files froma args. 75 | argv = [x for x in argv if x not in srcs] 76 | for s in srcs: 77 | rc = cc(llvmcc, s, list(argv)) 78 | sys.exit(rc) 79 | 80 | if __name__ == '__main__': 81 | main() 82 | -------------------------------------------------------------------------------- /test/diagdiff: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'getoptlong' 4 | require 'set' 5 | require 'yaml' 6 | 7 | opts = GetoptLong.new( 8 | ['--prefix', GetoptLong::REQUIRED_ARGUMENT] 9 | ) 10 | 11 | prefix = 'CHECK' 12 | opts.each do |opt, arg| 13 | case opt 14 | when '--prefix' 15 | prefix = arg 16 | end 17 | end 18 | 19 | # extract expected output 20 | exp = Set.new 21 | lineno = 1 22 | file = ARGV.shift 23 | path = File.expand_path file 24 | File.open(file).each_line do |line| 25 | if /\/\/\s*#{prefix}:(.*)$/ =~ line 26 | $~[1].scan(/\{\{([^\}]+)\}\}/) do |bug| 27 | exp << [file, lineno, bug[0]] 28 | end 29 | end 30 | # bump lineno if not ending with a backslash 31 | lineno = $. + 1 if not /\\\s*$/ =~ line 32 | end 33 | 34 | # parse actual output 35 | act = Set.new 36 | docs = YAML.load_stream STDIN 37 | docs.each do |doc| 38 | next if doc['status'] == 'unsat' 39 | act_file, act_lineno = doc['stack'][0].split(':')[0, 2] 40 | act_file = file if path == act_file 41 | act_lineno = Integer act_lineno 42 | act << [act_file, act_lineno, doc['bug']] 43 | end 44 | 45 | exit 0 if exp == act 46 | 47 | # missing 48 | (exp - act).each { |e| puts "- #{e.join(':')}" } 49 | 50 | # superfluous 51 | (act - exp).each { |e| puts "+ #{e.join(':')}" } 52 | 53 | exit 1 54 | -------------------------------------------------------------------------------- /test/gcc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | DIR=$(dirname "${BASH_SOURCE[0]}") 3 | "$DIR/cc1" "$@" > /dev/null 4 | 5 | EXE=$(basename "${BASH_SOURCE[0]}") 6 | export PATH=$(echo $PATH | sed -e "s/[^:]*://") 7 | "$EXE" "$@" 8 | -------------------------------------------------------------------------------- /test/lit.cfg.in: -------------------------------------------------------------------------------- 1 | config.name = "@PACKAGE@" 2 | config.test_format = lit.formats.ShTest() 3 | config.suffixes = ['.c', '.cc'] 4 | 5 | config.target_triple = "@host@" 6 | config.test_exec_root = "@abs_builddir@" 7 | config.test_source_root = "@abs_srcdir@" 8 | 9 | import os, os.path 10 | 11 | if 'LLVMCC' in os.environ: 12 | config.environment['LLVMCC'] = os.getenv('LLVMCC') 13 | 14 | llvm_obj_root = lit.util.capture(['llvm-config', '--obj-root']).strip() 15 | llvm_build_mode = lit.util.capture(['llvm-config', '--build-mode']).strip() 16 | llvm_bindir = os.path.join(llvm_obj_root, llvm_build_mode, 'bin') 17 | stack_bindir = os.path.join("@abs_top_builddir@", 'bin') 18 | path = os.path.pathsep.join( (stack_bindir, config.test_source_root, llvm_bindir, config.environment['PATH']) ) 19 | config.environment['PATH'] = path 20 | 21 | cc1 = os.path.join(stack_bindir, 'stack', 'cc1') 22 | cc = cc1 + ' -c -o -' 23 | linuxcc = cc1 + ' -nostdinc -fno-builtin -c -o -' 24 | config.substitutions.append( ('%cc', cc) ) 25 | config.substitutions.append( ('%linuxcc', linuxcc) ) 26 | -------------------------------------------------------------------------------- /test/opt/add100.c: -------------------------------------------------------------------------------- 1 | // RUN: %cc %s | optck | diagdiff --prefix=exp %s 2 | // 3 | // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=30475 4 | 5 | void bar(void); 6 | 7 | int foo(int a) 8 | { 9 | if (!(a + 100 > a)) 10 | bar(); // exp: {{anti-dce}} 11 | return a; 12 | } 13 | -------------------------------------------------------------------------------- /test/opt/alias.c: -------------------------------------------------------------------------------- 1 | // RUN: %cc %s | optck | diagdiff --prefix=exp %s 2 | 3 | #include 4 | 5 | int foo(char *p) { 6 | char *q = malloc(8); 7 | char *r = malloc(4); 8 | 9 | r[0] = 0; 10 | 11 | if (p == q) 12 | return 1; 13 | if (p == r) // exp: {{anti-simplify}} 14 | return 2; 15 | if (q == r) // exp: {{anti-simplify}} 16 | return 3; 17 | 18 | char *s = realloc(r, 8); 19 | if (!s) 20 | return 4; 21 | 22 | r[0] = 0; // exp: {{anti-dce}} 23 | return 5; 24 | } 25 | -------------------------------------------------------------------------------- /test/opt/dead-loop.c: -------------------------------------------------------------------------------- 1 | // RUN: %cc %s | optck | diagdiff --prefix=exp %s 2 | // 3 | // From krb5, krb5_ccache_copy() in clients/ksu/ccache.c. 4 | 5 | void bar(void); 6 | 7 | void foo() 8 | { 9 | int *p = 0; 10 | int i = 0; 11 | if (p) { 12 | while (p[i]) { 13 | bar(); 14 | ++i; 15 | } 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /test/opt/delete.cc: -------------------------------------------------------------------------------- 1 | // RUN: %cc %s | optck | diagdiff --prefix=exp %s 2 | 3 | struct C { 4 | void f(); 5 | bool x; 6 | }; 7 | 8 | void C::f() { 9 | if (x) { 10 | delete this; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test/opt/ext4shl.c: -------------------------------------------------------------------------------- 1 | // RUN: %cc %s | optck | diagdiff --prefix=exp %s 2 | // 3 | // https://bugzilla.kernel.org/show_bug.cgi?id=14287 4 | 5 | void bar(void); 6 | 7 | int foo(unsigned char log_groups_per_flex) 8 | { 9 | unsigned int groups_per_flex; 10 | groups_per_flex = 1 << log_groups_per_flex; 11 | if (groups_per_flex == 0) { 12 | bar(); // exp: {{anti-dce}} 13 | return 1; 14 | } 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /test/opt/gpgdiv.c: -------------------------------------------------------------------------------- 1 | // RUN: %cc %s | optck | diagdiff --prefix=exp %s 2 | 3 | int bar(int); 4 | 5 | int foo1(int msize) 6 | { 7 | if (!msize) 8 | msize = 1 / msize; // exp: {{anti-dce}} 9 | return bar(msize); 10 | } 11 | 12 | int foo2(int dsize) 13 | { 14 | switch (dsize) { 15 | case 0: 16 | return 1 / dsize; // exp: {{anti-dce}} 17 | case 1: 18 | return bar(1); 19 | default: 20 | return bar(dsize); 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /test/opt/memcpy.c: -------------------------------------------------------------------------------- 1 | // RUN: %cc %s | optck | diagdiff --prefix=exp %s 2 | 3 | #include 4 | 5 | void f1(char *x) { 6 | memcpy(&x[2], &x[4], 3); // exp: {{anti-dce}} 7 | } 8 | 9 | void f2(char *x) { 10 | memcpy(&x[2], &x[4], 2); 11 | } 12 | 13 | void f3(char *x) { 14 | memcpy(&x[2], &x[4], 1); 15 | } 16 | 17 | void f4(int *x) { 18 | memcpy(&x[2], &x[4], 9); // exp: {{anti-dce}} 19 | } 20 | 21 | void f5(int *x) { 22 | memcpy(&x[2], &x[4], 8); 23 | } 24 | 25 | void f6(int *x) { 26 | memcpy(&x[2], &x[4], 7); 27 | } 28 | -------------------------------------------------------------------------------- /test/opt/pqdiv.c: -------------------------------------------------------------------------------- 1 | // RUN: %cc -DNORETURN= %s | optck | diagdiff --prefix=exp %s 2 | // RUN: %cc %s | optck | diagdiff %s 3 | // 4 | // http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=616180 5 | 6 | #ifndef NORETURN 7 | #define NORETURN __attribute__((noreturn)) 8 | #endif 9 | void my_raise(void) NORETURN; 10 | 11 | int foo(int x, int y) 12 | { 13 | if (y == 0) 14 | my_raise(); // exp: {{anti-dce}} 15 | return x / y; 16 | } 17 | -------------------------------------------------------------------------------- /test/opt/ptr-2008-1685.c: -------------------------------------------------------------------------------- 1 | // RUN: %cc %s | optck | diagdiff --prefix=exp %s 2 | // 3 | // https://groups.google.com/d/msg/comp.os.plan9/NYdK1L7rf8Q/yfAiZoOlwNUJ 4 | 5 | void bar(void); 6 | 7 | void foo(char *buf) 8 | { 9 | unsigned int len = 1<<30; 10 | if (buf + len < buf) 11 | bar(); // exp: {{anti-dce}} 12 | } 13 | -------------------------------------------------------------------------------- /test/opt/realloc-null.c: -------------------------------------------------------------------------------- 1 | // RUN: %cc %s | optck | diagdiff --prefix=exp %s 2 | 3 | #include 4 | #include 5 | 6 | int foo(int n) 7 | { 8 | char *p = malloc(1); 9 | p[0] = 1; 10 | 11 | char *q = realloc(p, 2); 12 | if (q == 0) 13 | return p[0]; 14 | 15 | return 2; 16 | } 17 | -------------------------------------------------------------------------------- /test/opt/shlnsw.c: -------------------------------------------------------------------------------- 1 | // TODO: %cc %s | sed 's/shl i32/shl nsw i32/' | antiopt -S -loop-prepare -loop-rotate -bugon-int -bugon-loop -anti-dce -simplifycfg | FileCheck %s 2 | // 3 | // http://blog.regehr.org/archives/767 4 | // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=54027 5 | 6 | // CHECK: define i32 @foo() 7 | // CHECK-NEXT: entry: 8 | // CHECK-NEXT: unreachable 9 | int foo() 10 | { 11 | int x = 1; 12 | while (x) x <<= 1; 13 | return x; 14 | } 15 | -------------------------------------------------------------------------------- /test/opt/use-after-free.c: -------------------------------------------------------------------------------- 1 | // RUN: %cc %s | optck | diagdiff --prefix=exp %s 2 | 3 | #include 4 | 5 | void foo() { 6 | char *p = malloc(8); 7 | p[2] = 1; 8 | free(p); 9 | } 10 | 11 | void bar() { 12 | char *p = malloc(8); // exp: {{anti-dce}} 13 | free(p); 14 | p[4] = 2; // exp: {{bugon-free}} 15 | } 16 | -------------------------------------------------------------------------------- /test/opt/winner1.c: -------------------------------------------------------------------------------- 1 | // TODO: %cc %s | optck | diagdiff --prefix=exp %s 2 | // 3 | // http://blog.regehr.org/archives/767 4 | 5 | enum {N = 32}; 6 | static int a[N], pfx[N]; 7 | 8 | void prefix_sum (void) 9 | { 10 | int i, accum; 11 | for (i = 0, accum = a[0]; i < N; i++, accum += a[i]) // exp: {{anti-simplify}} 12 | pfx[i] = accum; 13 | } 14 | 15 | void foo(int *); 16 | void init(void) 17 | { 18 | foo(a); 19 | foo(pfx); 20 | } 21 | -------------------------------------------------------------------------------- /test/opt/winner2.c: -------------------------------------------------------------------------------- 1 | // RUN: %cc %s | optck | diagdiff --prefix=exp %s 2 | // 3 | // http://blog.regehr.org/archives/767 4 | 5 | #include 6 | #include 7 | 8 | int main() 9 | { 10 | int *p = (int*)malloc(sizeof(int)); // exp: {{anti-dce}} 11 | int *q = (int*)realloc(p, sizeof(int)); 12 | *p = 1; 13 | *q = 2; 14 | if (p == q) 15 | printf("%d %d\n", *p, *q); 16 | } 17 | -------------------------------------------------------------------------------- /test/stack-build: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | DIR=$(dirname "${BASH_SOURCE[0]}") 4 | ABS_DIR=$(cd "${DIR}/stack"; pwd) 5 | export PATH="${ABS_DIR}:${PATH}" 6 | export CC="${ABS_DIR}/cc" 7 | export CXX="${ABS_DIR}/c++" 8 | "$@" 9 | --------------------------------------------------------------------------------