├── CMakeLists.txt ├── LICENSE.txt ├── README.txt ├── TODO.txt ├── cmake └── modules │ └── FindReadline.cmake ├── diff_clang_source_files.sh ├── docs ├── Makefile ├── _build │ ├── doctrees │ │ ├── environment.pickle │ │ └── index.doctree │ └── html │ │ ├── .buildinfo │ │ ├── _sources │ │ └── index.txt │ │ ├── _static │ │ ├── ajax-loader.gif │ │ ├── basic.css │ │ ├── comment-bright.png │ │ ├── comment-close.png │ │ ├── comment.png │ │ ├── default.css │ │ ├── doctools.js │ │ ├── down-pressed.png │ │ ├── down.png │ │ ├── file.png │ │ ├── jquery.js │ │ ├── minus.png │ │ ├── plus.png │ │ ├── pygments.css │ │ ├── searchtools.js │ │ ├── sidebar.js │ │ ├── underscore.js │ │ ├── up-pressed.png │ │ ├── up.png │ │ └── websupport.js │ │ ├── genindex.html │ │ ├── index.html │ │ ├── objects.inv │ │ ├── search.html │ │ └── searchindex.js ├── conf.py ├── index.rst └── make.bat ├── pull_clang_source_files.sh ├── push_clang_source_files.sh ├── src ├── CMakeLists.txt ├── backend │ ├── CMakeLists.txt │ ├── jagger │ │ ├── codegen.cpp │ │ ├── instructionstream.cpp │ │ ├── interface.h │ │ ├── print.cpp │ │ └── types.h │ ├── llvm │ │ ├── CMakeLists.txt │ │ ├── IRGen.cpp │ │ └── IRGen.h │ └── x64builder │ │ ├── CMakeLists.txt │ │ ├── instr.h │ │ ├── instrbuilder.h │ │ ├── template.cpp │ │ └── x64builder.cpp ├── base │ ├── ArrayTree.h │ ├── CMakeLists.txt │ ├── DiagnosticEmitter.h │ ├── LLVMDependencies.h │ ├── MemRegion.cpp │ ├── MemRegion.h │ ├── MutArrayRef.h │ ├── NestedStack.h │ └── SimpleArray.h ├── clang │ ├── ClangCFGWalker.h │ ├── ClangTranslator.cpp │ ├── ClangTranslator.h │ ├── README.txt │ ├── diff_clang_source_files.sh │ ├── pull_clang_source_files.sh │ └── push_clang_source_files.sh ├── grammar │ ├── CMakeLists.txt │ ├── ohmu.grammar │ ├── parser.grammar │ └── sexpr.grammar ├── lsa │ ├── BuildCallGraph.cpp │ ├── BuildCallGraph.h │ ├── CMakeLists.txt │ ├── GraphComputation.h │ ├── GraphDeserializer.h │ ├── GraphSerializer.h │ ├── StandaloneGraphComputation.h │ ├── StandaloneRunner.h │ └── examples │ │ ├── CMakeLists.txt │ │ ├── EscapeAnalysis.cpp │ │ ├── EscapeAnalysis.h │ │ ├── ExampleOhmuComputation.h │ │ ├── SCCComputation.cpp │ │ └── SCCComputation.h ├── ohmu │ ├── examples │ │ └── argument_dependencies.ohmu │ ├── test_cfg.ohmu │ ├── test_dependent_functions.ohmu │ ├── test_literal.ohmu │ ├── test_loop.ohmu │ ├── test_module.ohmu │ ├── test_scalartypes.ohmu │ └── test_ssa.ohmu ├── parser │ ├── ASTNode.cpp │ ├── ASTNode.h │ ├── BNFParser.cpp │ ├── BNFParser.h │ ├── CMakeLists.txt │ ├── DefaultLexer.cpp │ ├── DefaultLexer.h │ ├── Lexer.cpp │ ├── Lexer.h │ ├── Parser.cpp │ ├── Parser.h │ ├── ParserBuilder.h │ ├── TILParser.cpp │ ├── TILParser.h │ └── Token.h ├── test │ ├── CMakeLists.txt │ ├── Driver.h │ ├── backend │ │ ├── CMakeLists.txt │ │ └── test_llvm.cpp │ ├── base │ │ ├── CMakeLists.txt │ │ └── test_base.cpp │ ├── lsa │ │ ├── CMakeLists.txt │ │ ├── escape_analysis.cpp │ │ ├── generate_callgraph.cpp │ │ ├── globals_analysis.cpp │ │ ├── run_test_lsa.sh │ │ ├── scc_analysis.cpp │ │ ├── test_input_callgraph.cpp │ │ ├── test_input_escape.cpp │ │ ├── test_input_global_vars.cpp │ │ └── test_input_scc.cpp │ ├── parser │ │ ├── CMakeLists.txt │ │ ├── test_BNFParser.cpp │ │ └── test_parser.cpp │ └── til │ │ ├── CMakeLists.txt │ │ ├── test_compare.cpp │ │ ├── test_copier.cpp │ │ ├── test_serialization.cpp │ │ └── test_visitor.cpp ├── til │ ├── Annotation.h │ ├── AnnotationImpl.cpp │ ├── AnnotationImpl.h │ ├── AttributeGrammar.h │ ├── Bytecode.cpp │ ├── Bytecode.h │ ├── CFGBuilder.cpp │ ├── CFGBuilder.h │ ├── CMakeLists.txt │ ├── CopyReducer.h │ ├── Evaluator.h │ ├── Global.cpp │ ├── Global.h │ ├── InplaceReducer.h │ ├── README.txt │ ├── SSAPass.cpp │ ├── SSAPass.h │ ├── TIL.cpp │ ├── TIL.h │ ├── TILAnnKinds.def │ ├── TILBaseType.h │ ├── TILCompare.h │ ├── TILOps.def │ ├── TILPrettyPrint.h │ ├── TILTraverse.h │ ├── TILVisitor.h │ ├── TypedEvaluator.cpp │ ├── TypedEvaluator.h │ └── VisitCFG.h └── unittests │ ├── CMakeLists.txt │ └── lsa │ ├── BuildCallGraphTest.cpp │ ├── CMakeLists.txt │ ├── SCCComputationTest.cpp │ └── StandaloneGraphComputationTest.cpp └── temp.ohmu /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | # set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/modules/") 4 | 5 | set( CMAKE_EXPORT_COMPILE_COMMANDS 1 ) 6 | 7 | add_subdirectory (src) 8 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | 2 | Ohmu is a new programming language being developed at Google. Right now, it 3 | is just a hobby project that a few engineers are working on in their spare 4 | time. Its purpose is to serve as a sandbox for experimenting with various 5 | compiler technologies, such as type systems, partial evaluation, run-time 6 | code generation, and GPU programming. 7 | 8 | *Disclaimer*: There are no plans to use ohmu internally or to release it as 9 | a real product. Anything and everything is subject to change without 10 | notice. May be known to the State of California to cause cancer in lab 11 | animals, including engineers in computer labs. Do not eat. 12 | 13 | 14 | Build Instructions: 15 | 16 | (1) Install the ohmu source code into a directory, e.g. ohmu. 17 | (2) mkdir ohmu_build (make this directory alongside ohmu) 18 | (3) cd ohmu_build 19 | (4) ccmake ../ohmu 20 | (5) Go to advanced mode 21 | (6) [Optional] Change compiler to clang. 22 | (7) Change LLVM_DIR to "$LLVM_INSTALL_DIR"/share/llvm/cmake/ 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /TODO.txt: -------------------------------------------------------------------------------- 1 | 2 | This is a list of bugs and missing functionality that needs to be fixed in 3 | the near term. 4 | 5 | Front-end 6 | ========= 7 | 8 | (p1) Parsing of float and integer literals of different widths. 9 | (p1) Report position of syntax errors. 10 | 11 | (p2) Better recovery after parse errors. 12 | 13 | 14 | Middle-end 15 | ========== 16 | 17 | (p1) Add CFG simplification & cleanup pass (unused phi, dup vars, empty BBs) 18 | (p1) Add records, slots, and inheritance. 19 | (p1) Add arithmetic up-conversions and type-checking. 20 | (p1) Calculate numUses and distance metric for variables. 21 | 22 | (p2) Add initial type-checking pass. 23 | (p2) Implement lazy rewriting. 24 | (p2) Implement partial evaluator. 25 | 26 | 27 | Back-end 28 | ========= 29 | 30 | (p1) Find and solve constraints. 31 | (p1) Register allocation. 32 | (p1) Emit x86 instructions for CFG. 33 | (p1) JIT-compiler. (Create code page, emit instructions to code page, and run.) 34 | 35 | 36 | Misc 37 | ==== 38 | 39 | (p1) Make test infrastructure to automatically run tests. 40 | -------------------------------------------------------------------------------- /cmake/modules/FindReadline.cmake: -------------------------------------------------------------------------------- 1 | # Code copied from sethhall@github 2 | # 3 | # - Try to find readline include dirs and libraries 4 | # 5 | # Usage of this module as follows: 6 | # 7 | # find_package(Readline) 8 | # 9 | # Variables used by this module, they can change the default behaviour and need 10 | # to be set before calling find_package: 11 | # 12 | # Readline_ROOT_DIR Set this variable to the root installation of 13 | # readline if the module has problems finding the 14 | # proper installation path. 15 | # 16 | # Variables defined by this module: 17 | # 18 | # READLINE_FOUND System has readline, include and lib dirs found 19 | # Readline_INCLUDE_DIR The readline include directories. 20 | # Readline_LIBRARY The readline library. 21 | 22 | find_path(Readline_ROOT_DIR 23 | NAMES include/readline/readline.h 24 | ) 25 | 26 | find_path(Readline_INCLUDE_DIR 27 | NAMES readline/readline.h 28 | HINTS ${Readline_ROOT_DIR}/include 29 | ) 30 | 31 | find_library(Readline_LIBRARY 32 | NAMES readline 33 | HINTS ${Readline_ROOT_DIR}/lib 34 | ) 35 | 36 | if(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY) 37 | set(READLINE_FOUND TRUE) 38 | else(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY) 39 | FIND_LIBRARY(Readline_LIBRARY NAMES readline) 40 | include(FindPackageHandleStandardArgs) 41 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(Readline DEFAULT_MSG Readline_INCLUDE_DIR Readline_LIBRARY ) 42 | MARK_AS_ADVANCED(Readline_INCLUDE_DIR Readline_LIBRARY) 43 | endif(Readline_INCLUDE_DIR AND Readline_LIBRARY AND Ncurses_LIBRARY) 44 | 45 | mark_as_advanced( 46 | Readline_ROOT_DIR 47 | Readline_INCLUDE_DIR 48 | Readline_LIBRARY 49 | ) 50 | -------------------------------------------------------------------------------- /diff_clang_source_files.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CLANG_INC="$1"/tools/clang/include/clang/Analysis/Til 4 | CLANG_LIB="$1"/tools/clang/lib/Analysis/Til 5 | 6 | diff -u "$CLANG_INC"/base/SimpleArray.h src/base/SimpleArray.h 7 | diff -u "$CLANG_INC"/base/MutArrayRef.h src/base/MutArrayRef.h 8 | diff -u "$CLANG_INC"/base/ArrayTree.h src/base/ArrayTree.h 9 | 10 | diff -u "$CLANG_INC"/TILOps.def src/til/TILOps.def 11 | diff -u "$CLANG_INC"/TILAnnKinds.def src/til/TILAnnKinds.def 12 | diff -u "$CLANG_INC"/TILBaseType.h src/til/TILBaseType.h 13 | diff -u "$CLANG_INC"/TIL.h src/til/TIL.h 14 | diff -u "$CLANG_INC"/TILTraverse.h src/til/TILTraverse.h 15 | diff -u "$CLANG_INC"/TILCompare.h src/til/TILCompare.h 16 | diff -u "$CLANG_INC"/TILPrettyPrint.h src/til/TILPrettyPrint.h 17 | diff -u "$CLANG_INC"/Annotation.h src/til/Annotation.h 18 | diff -u "$CLANG_INC"/AnnotationImpl.h src/til/AnnotationImpl.h 19 | diff -u "$CLANG_INC"/CFGBuilder.h src/til/CFGBuilder.h 20 | diff -u "$CLANG_INC"/AttributeGrammar.h src/til/AttributeGrammar.h 21 | diff -u "$CLANG_INC"/CopyReducer.h src/til/CopyReducer.h 22 | diff -u "$CLANG_INC"/InplaceReducer.h src/til/InplaceReducer.h 23 | diff -u "$CLANG_INC"/SSAPass.h src/til/SSAPass.h 24 | diff -u "$CLANG_INC"/Bytecode.h src/til/Bytecode.h 25 | 26 | diff -u "$CLANG_LIB"/TIL.cpp src/til/TIL.cpp 27 | diff -u "$CLANG_LIB"/CFGBuilder.cpp src/til/CFGBuilder.cpp 28 | diff -u "$CLANG_LIB"/SSAPass.cpp src/til/SSAPass.cpp 29 | diff -u "$CLANG_LIB"/AnnotationImpl.cpp src/til/AnnotationImpl.cpp 30 | diff -u "$CLANG_LIB"/Bytecode.cpp src/til/Bytecode.cpp 31 | -------------------------------------------------------------------------------- /docs/_build/doctrees/environment.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/ohmu/33991d1bb655ab26ce1f172fdea0fd7eef32e763/docs/_build/doctrees/environment.pickle -------------------------------------------------------------------------------- /docs/_build/doctrees/index.doctree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/ohmu/33991d1bb655ab26ce1f172fdea0fd7eef32e763/docs/_build/doctrees/index.doctree -------------------------------------------------------------------------------- /docs/_build/html/.buildinfo: -------------------------------------------------------------------------------- 1 | # Sphinx build info version 1 2 | # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. 3 | config: eafa5706c0f674d809f34c0c3bbed28b 4 | tags: fbb0d17656682115ca4d033fb2f83ba1 5 | -------------------------------------------------------------------------------- /docs/_build/html/_sources/index.txt: -------------------------------------------------------------------------------- 1 | .. ohmu documentation master file, created by 2 | sphinx-quickstart on Sun May 11 10:04:06 2014. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Contents 7 | ================== 8 | 9 | .. toctree:: 10 | :maxdepth: 2 11 | 12 | 13 | Introduction 14 | ============ 15 | 16 | Ohmu is a new programming language being developed at Google. Right now, it 17 | is just a hobby project that a few engineers are working on in their spare 18 | time. Its purpose is to serve as a sandbox for experimenting with various 19 | compiler technologies, such as type systems, partial evaluation, run-time code 20 | generation, and GPU programming. 21 | 22 | *Disclaimer*: There are no plans to use ohmu internally or to release it as a 23 | real product. Anything and everything is subject to change without notice. 24 | May be known to the State of California to cause cancer in lab animals, 25 | including engineers in computer labs. Do not eat. 26 | 27 | Why a new language? 28 | ------------------- 29 | 30 | We believe that a good programming language is one where the tool chain 31 | provides as much assistance as possible. Optimizing compilers improve 32 | performance, type systems and warnings help find bugs, while IDEs and 33 | refactoring tools help organize and maintain code. These tools are effective 34 | only if the language itself has a well-defined semantics, so that the compiler 35 | or IDE can analyze and understand the code. 36 | 37 | Unfortunately, most practical programming languages have a semantics that is 38 | informal, ad-hoc, overly complex, or unsound in various ways, which tends to 39 | confound any attempt at analysis. A great deal of academic research has been 40 | done in the area of formal programming language semantics, but academic 41 | languages go too far in the other direction; they are either too formal (e.g. 42 | Agda) or impractical for most real-world tasks (e.g. Haskell). 43 | 44 | Our goal is to collect the best ideas from academic research, combine those 45 | ideas together, and apply them to the design of a modern, elegant, and above 46 | all, a *practical* language. 47 | 48 | Overview of Language Features 49 | ----------------------------- 50 | 51 | * High performance: 52 | 53 | * Faster than C. 54 | * Suitable for systems programming, games, or scientific computation. 55 | * Transparent foreign-function interface to C and C++ code. 56 | * Transparent support for GPGPU programming. 57 | * Advanced optimizations driven by static analysis (e.g. alias analysis). 58 | 59 | * Safe: 60 | 61 | * Type and memory safe. No unsafe casts or buffer overflows... 62 | * Thread-safe by design. No race conditions... 63 | * Designed from the ground-up for static analysis. 64 | 65 | * Modular and high-level: 66 | 67 | * Object-oriented programming: classes, inheritance, generics, and mixins. 68 | * Functional programming: type-classes, variant data types, and ADTs. 69 | * Mixin-modules: virtual classes and extensible data types. 70 | 71 | * Extensible: 72 | 73 | * *Extensible syntax*: libraries can extend the language with new syntax. 74 | * *Partial evaluation*: compile language extensions down to the core language. 75 | * Compile-time reflection and meta-programming. 76 | * Support for embedding domain-specific languages (DSLs): 77 | 78 | * E.g. parser generators, matrix libraries, image filters, shaders, etc. 79 | 80 | 81 | Key Technologies 82 | ^^^^^^^^^^^^^^^^ 83 | 84 | Although this may look like a long wish-list, all of these features depend 85 | primarily on just two key technologies: 86 | 87 | #. A sophisticated static type system. 88 | #. Partial evaluation. 89 | 90 | **Type system**. Type systems are routinely used to handle structural types, 91 | such as ``int`` and ``String``. However, they can do much more than that. 92 | Type systems a general purpose tool for declaring and enforcing any program 93 | invariant, including aliasing constraints, ownership, or freedom from race 94 | conditions. The ohmu type system is responsible for the *safety* features 95 | above, and it is a key part of *modularity*, since mixin modules have very 96 | complex types. Moreover, the type system enforces program invariants that are 97 | then used by the optimizer to achieve *high performance*. 98 | 99 | **Partial evaluation**. Partial evaluation optimizes code by shifting 100 | computations from run-time to compile-time. Most compilers can perform 101 | constant propogation. However, the ohmu compiler can perform arbitrary 102 | computations at compile-time. Moreover, the partial evaluator is linked to 103 | the static type system, which enables it to perform type-based 104 | transformations, like method de-virtualization. The *extensibility* features 105 | use partial evaluation to eliminate the run-time overhead that is generally 106 | associated with reflection and DSLs. 107 | 108 | 109 | Tradeoffs in Language Design 110 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 111 | 112 | There has traditionally been a tradeoff in language design between performance 113 | and safety. A safe language must perform additional run-time checks, such as 114 | array bounds checks, to prevent unsafe operations from occuring. Ohmu 115 | leverages the type system to achieve both performance and safety at the same 116 | time. E.g. if the type system can prove that the array bounds check always 117 | succeeds, then run-time check can be omitted. 118 | 119 | Similarly, there has traditionally been a tradeoff between performance and 120 | high-level abstractions. Abstractions introduce an additional level of 121 | indirection, which usually has a run-time cost. For example, object-oriented 122 | inheritance and virtual methods are a useful abstraction, but most compilers 123 | cannot inline virtual methods, so they have a high cost. Ohmu leverages 124 | partial evaluation to eliminate the overhead of most abstractions. For 125 | example, the partial evaluator can specialize a polymorphic function to a 126 | concrete type, thus allowing the virtual calls to be eliminated. 127 | 128 | 129 | Indices and tables 130 | ================== 131 | 132 | * :ref:`genindex` 133 | * :ref:`modindex` 134 | * :ref:`search` 135 | 136 | -------------------------------------------------------------------------------- /docs/_build/html/_static/ajax-loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/ohmu/33991d1bb655ab26ce1f172fdea0fd7eef32e763/docs/_build/html/_static/ajax-loader.gif -------------------------------------------------------------------------------- /docs/_build/html/_static/comment-bright.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/ohmu/33991d1bb655ab26ce1f172fdea0fd7eef32e763/docs/_build/html/_static/comment-bright.png -------------------------------------------------------------------------------- /docs/_build/html/_static/comment-close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/ohmu/33991d1bb655ab26ce1f172fdea0fd7eef32e763/docs/_build/html/_static/comment-close.png -------------------------------------------------------------------------------- /docs/_build/html/_static/comment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/ohmu/33991d1bb655ab26ce1f172fdea0fd7eef32e763/docs/_build/html/_static/comment.png -------------------------------------------------------------------------------- /docs/_build/html/_static/default.css: -------------------------------------------------------------------------------- 1 | /* 2 | * default.css_t 3 | * ~~~~~~~~~~~~~ 4 | * 5 | * Sphinx stylesheet -- default theme. 6 | * 7 | * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. 8 | * :license: BSD, see LICENSE for details. 9 | * 10 | */ 11 | 12 | @import url("basic.css"); 13 | 14 | /* -- page layout ----------------------------------------------------------- */ 15 | 16 | body { 17 | font-family: sans-serif; 18 | font-size: 100%; 19 | background-color: #11303d; 20 | color: #000; 21 | margin: 0; 22 | padding: 0; 23 | } 24 | 25 | div.document { 26 | background-color: #1c4e63; 27 | } 28 | 29 | div.documentwrapper { 30 | float: left; 31 | width: 100%; 32 | } 33 | 34 | div.bodywrapper { 35 | margin: 0 0 0 230px; 36 | } 37 | 38 | div.body { 39 | background-color: #ffffff; 40 | color: #000000; 41 | padding: 0 20px 30px 20px; 42 | } 43 | 44 | div.footer { 45 | color: #ffffff; 46 | width: 100%; 47 | padding: 9px 0 9px 0; 48 | text-align: center; 49 | font-size: 75%; 50 | } 51 | 52 | div.footer a { 53 | color: #ffffff; 54 | text-decoration: underline; 55 | } 56 | 57 | div.related { 58 | background-color: #133f52; 59 | line-height: 30px; 60 | color: #ffffff; 61 | } 62 | 63 | div.related a { 64 | color: #ffffff; 65 | } 66 | 67 | div.sphinxsidebar { 68 | } 69 | 70 | div.sphinxsidebar h3 { 71 | font-family: 'Trebuchet MS', sans-serif; 72 | color: #ffffff; 73 | font-size: 1.4em; 74 | font-weight: normal; 75 | margin: 0; 76 | padding: 0; 77 | } 78 | 79 | div.sphinxsidebar h3 a { 80 | color: #ffffff; 81 | } 82 | 83 | div.sphinxsidebar h4 { 84 | font-family: 'Trebuchet MS', sans-serif; 85 | color: #ffffff; 86 | font-size: 1.3em; 87 | font-weight: normal; 88 | margin: 5px 0 0 0; 89 | padding: 0; 90 | } 91 | 92 | div.sphinxsidebar p { 93 | color: #ffffff; 94 | } 95 | 96 | div.sphinxsidebar p.topless { 97 | margin: 5px 10px 10px 10px; 98 | } 99 | 100 | div.sphinxsidebar ul { 101 | margin: 10px; 102 | padding: 0; 103 | color: #ffffff; 104 | } 105 | 106 | div.sphinxsidebar a { 107 | color: #98dbcc; 108 | } 109 | 110 | div.sphinxsidebar input { 111 | border: 1px solid #98dbcc; 112 | font-family: sans-serif; 113 | font-size: 1em; 114 | } 115 | 116 | 117 | 118 | /* -- hyperlink styles ------------------------------------------------------ */ 119 | 120 | a { 121 | color: #355f7c; 122 | text-decoration: none; 123 | } 124 | 125 | a:visited { 126 | color: #355f7c; 127 | text-decoration: none; 128 | } 129 | 130 | a:hover { 131 | text-decoration: underline; 132 | } 133 | 134 | 135 | 136 | /* -- body styles ----------------------------------------------------------- */ 137 | 138 | div.body h1, 139 | div.body h2, 140 | div.body h3, 141 | div.body h4, 142 | div.body h5, 143 | div.body h6 { 144 | font-family: 'Trebuchet MS', sans-serif; 145 | background-color: #f2f2f2; 146 | font-weight: normal; 147 | color: #20435c; 148 | border-bottom: 1px solid #ccc; 149 | margin: 20px -20px 10px -20px; 150 | padding: 3px 0 3px 10px; 151 | } 152 | 153 | div.body h1 { margin-top: 0; font-size: 200%; } 154 | div.body h2 { font-size: 160%; } 155 | div.body h3 { font-size: 140%; } 156 | div.body h4 { font-size: 120%; } 157 | div.body h5 { font-size: 110%; } 158 | div.body h6 { font-size: 100%; } 159 | 160 | a.headerlink { 161 | color: #c60f0f; 162 | font-size: 0.8em; 163 | padding: 0 4px 0 4px; 164 | text-decoration: none; 165 | } 166 | 167 | a.headerlink:hover { 168 | background-color: #c60f0f; 169 | color: white; 170 | } 171 | 172 | div.body p, div.body dd, div.body li { 173 | text-align: justify; 174 | line-height: 130%; 175 | } 176 | 177 | div.admonition p.admonition-title + p { 178 | display: inline; 179 | } 180 | 181 | div.admonition p { 182 | margin-bottom: 5px; 183 | } 184 | 185 | div.admonition pre { 186 | margin-bottom: 5px; 187 | } 188 | 189 | div.admonition ul, div.admonition ol { 190 | margin-bottom: 5px; 191 | } 192 | 193 | div.note { 194 | background-color: #eee; 195 | border: 1px solid #ccc; 196 | } 197 | 198 | div.seealso { 199 | background-color: #ffc; 200 | border: 1px solid #ff6; 201 | } 202 | 203 | div.topic { 204 | background-color: #eee; 205 | } 206 | 207 | div.warning { 208 | background-color: #ffe4e4; 209 | border: 1px solid #f66; 210 | } 211 | 212 | p.admonition-title { 213 | display: inline; 214 | } 215 | 216 | p.admonition-title:after { 217 | content: ":"; 218 | } 219 | 220 | pre { 221 | padding: 5px; 222 | background-color: #eeffcc; 223 | color: #333333; 224 | line-height: 120%; 225 | border: 1px solid #ac9; 226 | border-left: none; 227 | border-right: none; 228 | } 229 | 230 | tt { 231 | background-color: #ecf0f3; 232 | padding: 0 1px 0 1px; 233 | font-size: 0.95em; 234 | } 235 | 236 | th { 237 | background-color: #ede; 238 | } 239 | 240 | .warning tt { 241 | background: #efc2c2; 242 | } 243 | 244 | .note tt { 245 | background: #d6d6d6; 246 | } 247 | 248 | .viewcode-back { 249 | font-family: sans-serif; 250 | } 251 | 252 | div.viewcode-block:target { 253 | background-color: #f4debf; 254 | border-top: 1px solid #ac9; 255 | border-bottom: 1px solid #ac9; 256 | } -------------------------------------------------------------------------------- /docs/_build/html/_static/down-pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/ohmu/33991d1bb655ab26ce1f172fdea0fd7eef32e763/docs/_build/html/_static/down-pressed.png -------------------------------------------------------------------------------- /docs/_build/html/_static/down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/ohmu/33991d1bb655ab26ce1f172fdea0fd7eef32e763/docs/_build/html/_static/down.png -------------------------------------------------------------------------------- /docs/_build/html/_static/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/ohmu/33991d1bb655ab26ce1f172fdea0fd7eef32e763/docs/_build/html/_static/file.png -------------------------------------------------------------------------------- /docs/_build/html/_static/minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/ohmu/33991d1bb655ab26ce1f172fdea0fd7eef32e763/docs/_build/html/_static/minus.png -------------------------------------------------------------------------------- /docs/_build/html/_static/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/ohmu/33991d1bb655ab26ce1f172fdea0fd7eef32e763/docs/_build/html/_static/plus.png -------------------------------------------------------------------------------- /docs/_build/html/_static/pygments.css: -------------------------------------------------------------------------------- 1 | .highlight .hll { background-color: #ffffcc } 2 | .highlight { background: #eeffcc; } 3 | .highlight .c { color: #408090; font-style: italic } /* Comment */ 4 | .highlight .err { border: 1px solid #FF0000 } /* Error */ 5 | .highlight .k { color: #007020; font-weight: bold } /* Keyword */ 6 | .highlight .o { color: #666666 } /* Operator */ 7 | .highlight .cm { color: #408090; font-style: italic } /* Comment.Multiline */ 8 | .highlight .cp { color: #007020 } /* Comment.Preproc */ 9 | .highlight .c1 { color: #408090; font-style: italic } /* Comment.Single */ 10 | .highlight .cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */ 11 | .highlight .gd { color: #A00000 } /* Generic.Deleted */ 12 | .highlight .ge { font-style: italic } /* Generic.Emph */ 13 | .highlight .gr { color: #FF0000 } /* Generic.Error */ 14 | .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 15 | .highlight .gi { color: #00A000 } /* Generic.Inserted */ 16 | .highlight .go { color: #303030 } /* Generic.Output */ 17 | .highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ 18 | .highlight .gs { font-weight: bold } /* Generic.Strong */ 19 | .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 20 | .highlight .gt { color: #0040D0 } /* Generic.Traceback */ 21 | .highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */ 22 | .highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */ 23 | .highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */ 24 | .highlight .kp { color: #007020 } /* Keyword.Pseudo */ 25 | .highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */ 26 | .highlight .kt { color: #902000 } /* Keyword.Type */ 27 | .highlight .m { color: #208050 } /* Literal.Number */ 28 | .highlight .s { color: #4070a0 } /* Literal.String */ 29 | .highlight .na { color: #4070a0 } /* Name.Attribute */ 30 | .highlight .nb { color: #007020 } /* Name.Builtin */ 31 | .highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */ 32 | .highlight .no { color: #60add5 } /* Name.Constant */ 33 | .highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */ 34 | .highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */ 35 | .highlight .ne { color: #007020 } /* Name.Exception */ 36 | .highlight .nf { color: #06287e } /* Name.Function */ 37 | .highlight .nl { color: #002070; font-weight: bold } /* Name.Label */ 38 | .highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ 39 | .highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */ 40 | .highlight .nv { color: #bb60d5 } /* Name.Variable */ 41 | .highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */ 42 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */ 43 | .highlight .mf { color: #208050 } /* Literal.Number.Float */ 44 | .highlight .mh { color: #208050 } /* Literal.Number.Hex */ 45 | .highlight .mi { color: #208050 } /* Literal.Number.Integer */ 46 | .highlight .mo { color: #208050 } /* Literal.Number.Oct */ 47 | .highlight .sb { color: #4070a0 } /* Literal.String.Backtick */ 48 | .highlight .sc { color: #4070a0 } /* Literal.String.Char */ 49 | .highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */ 50 | .highlight .s2 { color: #4070a0 } /* Literal.String.Double */ 51 | .highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */ 52 | .highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */ 53 | .highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */ 54 | .highlight .sx { color: #c65d09 } /* Literal.String.Other */ 55 | .highlight .sr { color: #235388 } /* Literal.String.Regex */ 56 | .highlight .s1 { color: #4070a0 } /* Literal.String.Single */ 57 | .highlight .ss { color: #517918 } /* Literal.String.Symbol */ 58 | .highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */ 59 | .highlight .vc { color: #bb60d5 } /* Name.Variable.Class */ 60 | .highlight .vg { color: #bb60d5 } /* Name.Variable.Global */ 61 | .highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */ 62 | .highlight .il { color: #208050 } /* Literal.Number.Integer.Long */ -------------------------------------------------------------------------------- /docs/_build/html/_static/sidebar.js: -------------------------------------------------------------------------------- 1 | /* 2 | * sidebar.js 3 | * ~~~~~~~~~~ 4 | * 5 | * This script makes the Sphinx sidebar collapsible. 6 | * 7 | * .sphinxsidebar contains .sphinxsidebarwrapper. This script adds 8 | * in .sphixsidebar, after .sphinxsidebarwrapper, the #sidebarbutton 9 | * used to collapse and expand the sidebar. 10 | * 11 | * When the sidebar is collapsed the .sphinxsidebarwrapper is hidden 12 | * and the width of the sidebar and the margin-left of the document 13 | * are decreased. When the sidebar is expanded the opposite happens. 14 | * This script saves a per-browser/per-session cookie used to 15 | * remember the position of the sidebar among the pages. 16 | * Once the browser is closed the cookie is deleted and the position 17 | * reset to the default (expanded). 18 | * 19 | * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS. 20 | * :license: BSD, see LICENSE for details. 21 | * 22 | */ 23 | 24 | $(function() { 25 | // global elements used by the functions. 26 | // the 'sidebarbutton' element is defined as global after its 27 | // creation, in the add_sidebar_button function 28 | var bodywrapper = $('.bodywrapper'); 29 | var sidebar = $('.sphinxsidebar'); 30 | var sidebarwrapper = $('.sphinxsidebarwrapper'); 31 | 32 | // for some reason, the document has no sidebar; do not run into errors 33 | if (!sidebar.length) return; 34 | 35 | // original margin-left of the bodywrapper and width of the sidebar 36 | // with the sidebar expanded 37 | var bw_margin_expanded = bodywrapper.css('margin-left'); 38 | var ssb_width_expanded = sidebar.width(); 39 | 40 | // margin-left of the bodywrapper and width of the sidebar 41 | // with the sidebar collapsed 42 | var bw_margin_collapsed = '.8em'; 43 | var ssb_width_collapsed = '.8em'; 44 | 45 | // colors used by the current theme 46 | var dark_color = $('.related').css('background-color'); 47 | var light_color = $('.document').css('background-color'); 48 | 49 | function sidebar_is_collapsed() { 50 | return sidebarwrapper.is(':not(:visible)'); 51 | } 52 | 53 | function toggle_sidebar() { 54 | if (sidebar_is_collapsed()) 55 | expand_sidebar(); 56 | else 57 | collapse_sidebar(); 58 | } 59 | 60 | function collapse_sidebar() { 61 | sidebarwrapper.hide(); 62 | sidebar.css('width', ssb_width_collapsed); 63 | bodywrapper.css('margin-left', bw_margin_collapsed); 64 | sidebarbutton.css({ 65 | 'margin-left': '0', 66 | 'height': bodywrapper.height() 67 | }); 68 | sidebarbutton.find('span').text('»'); 69 | sidebarbutton.attr('title', _('Expand sidebar')); 70 | document.cookie = 'sidebar=collapsed'; 71 | } 72 | 73 | function expand_sidebar() { 74 | bodywrapper.css('margin-left', bw_margin_expanded); 75 | sidebar.css('width', ssb_width_expanded); 76 | sidebarwrapper.show(); 77 | sidebarbutton.css({ 78 | 'margin-left': ssb_width_expanded-12, 79 | 'height': bodywrapper.height() 80 | }); 81 | sidebarbutton.find('span').text('«'); 82 | sidebarbutton.attr('title', _('Collapse sidebar')); 83 | document.cookie = 'sidebar=expanded'; 84 | } 85 | 86 | function add_sidebar_button() { 87 | sidebarwrapper.css({ 88 | 'float': 'left', 89 | 'margin-right': '0', 90 | 'width': ssb_width_expanded - 28 91 | }); 92 | // create the button 93 | sidebar.append( 94 | '
«
' 95 | ); 96 | var sidebarbutton = $('#sidebarbutton'); 97 | light_color = sidebarbutton.css('background-color'); 98 | // find the height of the viewport to center the '<<' in the page 99 | var viewport_height; 100 | if (window.innerHeight) 101 | viewport_height = window.innerHeight; 102 | else 103 | viewport_height = $(window).height(); 104 | sidebarbutton.find('span').css({ 105 | 'display': 'block', 106 | 'margin-top': (viewport_height - sidebar.position().top - 20) / 2 107 | }); 108 | 109 | sidebarbutton.click(toggle_sidebar); 110 | sidebarbutton.attr('title', _('Collapse sidebar')); 111 | sidebarbutton.css({ 112 | 'color': '#FFFFFF', 113 | 'border-left': '1px solid ' + dark_color, 114 | 'font-size': '1.2em', 115 | 'cursor': 'pointer', 116 | 'height': bodywrapper.height(), 117 | 'padding-top': '1px', 118 | 'margin-left': ssb_width_expanded - 12 119 | }); 120 | 121 | sidebarbutton.hover( 122 | function () { 123 | $(this).css('background-color', dark_color); 124 | }, 125 | function () { 126 | $(this).css('background-color', light_color); 127 | } 128 | ); 129 | } 130 | 131 | function set_position_from_cookie() { 132 | if (!document.cookie) 133 | return; 134 | var items = document.cookie.split(';'); 135 | for(var k=0; k 7 | 8 | 9 | 10 | 11 | 12 | 13 | Index — ohmu 0.1 documentation 14 | 15 | 16 | 17 | 18 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 43 | 44 |
45 |
46 |
47 |
48 | 49 | 50 |

Index

51 | 52 |
53 | 54 |
55 | 56 | 57 |
58 |
59 |
60 |
61 |
62 | 63 | 64 | 65 | 77 | 78 |
79 |
80 |
81 |
82 | 91 | 95 | 96 | -------------------------------------------------------------------------------- /docs/_build/html/objects.inv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/ohmu/33991d1bb655ab26ce1f172fdea0fd7eef32e763/docs/_build/html/objects.inv -------------------------------------------------------------------------------- /docs/_build/html/search.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Search — ohmu 0.1 documentation 12 | 13 | 14 | 15 | 16 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 34 | 35 | 36 | 37 | 38 | 47 | 48 |
49 |
50 |
51 |
52 | 53 |

Search

54 |
55 | 56 |

57 | Please activate JavaScript to enable the search 58 | functionality. 59 |

60 |
61 |

62 | From here you can search these documents. Enter your search 63 | words into the box below and click "search". Note that the search 64 | function will automatically search for all of the words. Pages 65 | containing fewer words won't appear in the result list. 66 |

67 |
68 | 69 | 70 | 71 |
72 | 73 |
74 | 75 |
76 | 77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 | 95 | 99 | 100 | -------------------------------------------------------------------------------- /docs/_build/html/searchindex.js: -------------------------------------------------------------------------------- 1 | Search.setIndex({objects:{},terms:{programm:[],all:0,code:0,partial:0,help:0,just:0,softwar:[],antlr:[],modular:0,syntax:0,refactor:0,faster:0,follow:[],simultan:[],find:0,languag:0,ground:0,adt:0,depend:0,chain:0,mix:[],hobbi:0,dsl:0,eat:0,program:0,analyz:0,appli:0,formal:0,arrai:0,introduc:0,inform:0,real:0,them:0,anim:0,string:0,variou:0,transpar:0,far:0,familiar:[],safe:0,gpu:0,game:0,cannot:0,"new":0,veri:0,world:0,now:0,"class":0,discuss:[],introduct:0,enabl:0,specif:0,anyth:0,level:0,list:0,overli:0,collect:0,achiev:0,race:0,bison:[],either:0,alia:0,found:[],agda:0,where:0,page:0,concret:0,system:0,compil:0,prevent:0,domain:0,right:0,deal:0,thu:0,direct:0,intern:0,meta:0,design:0,respons:0,our:0,indirect:0,engin:0,orient:0,special:0,subject:0,index:0,what:[],safeti:0,matrix:0,defin:0,academ:0,crucial:[],below:[],tend:0,content:0,optim:0,state:0,version:[],suitabl:0,between:0,without:0,progress:[],neither:[],experi:0,ever:[],method:0,impract:0,core:0,elimin:0,assist:0,run:0,extend:0,advanc:0,gener:0,error:[],onli:0,like:0,disclaim:0,extens:0,base:0,california:0,believ:0,although:0,same:0,releas:0,organ:0,valu:[],addit:0,unsaf:0,both:0,great:0,thread:0,constraint:0,freedom:0,howev:0,area:0,technolog:0,shader:0,unfortun:0,filter:0,stride:[],enforc:0,sandbox:0,spare:0,chang:0,produc:[],oper:0,routin:0,semant:0,tradeoff:0,mixin:0,primarili:0,overview:0,modul:0,encod:[],bound:0,automat:[],two:0,down:0,unsound:0,written:[],done:0,haskel:0,comput:0,complex:0,mere:[],leverag:0,kei:0,cancer:0,from:0,evalu:0,wai:0,memori:0,data:0,support:0,transform:0,"long":0,sinc:0,few:0,much:0,call:0,includ:0,similarli:0,overhead:0,type:0,tell:[],too:0,analysi:0,modern:0,form:[],imag:0,arbitrari:0,thei:0,search:0,notic:0,idea:0,warn:0,part:0,understand:0,togeth:0,particular:[],known:0,overflow:0,inlin:0,than:0,those:0,must:0,serv:0,commun:[],kind:[],made:[],look:0,possibl:0,provid:0,wish:0,work:0,structur:0,project:0,cast:0,"while":0,cost:0,can:0,abov:0,ohmu:0,sophist:0,"static":0,embed:0,purpos:0,succe:0,"function":0,featur:0,constant:0,have:0,creat:[],"int":0,"abstract":0,parser:0,toolkit:[],indic:0,confound:0,gpgpu:0,befor:[],good:0,exist:[],research:0,interfac:0,been:0,foreign:0,simplest:[],declar:0,alwai:0,multipl:[],googl:0,studi:[],etc:0,perform:0,eleg:0,make:[],detail:[],virtual:0,occur:0,how:[],high:0,exampl:0,best:0,which:0,big:[],techniqu:[],verifi:[],more:0,librari:0,simpl:[],tool:0,difficult:[],product:0,express:[],practial:[],allow:0,normal:[],buffer:0,polymorph:0,why:0,object:0,variant:0,effect:0,lab:0,hand:[],reflect:0,most:0,tabl:0,driven:0,plan:0,ownership:0,improv:0,mai:0,develop:0,shift:0,philosophi:[],associ:0,tradition:0,check:0,hoc:0,other:0,prove:0,handl:0,task:0,attempt:0,practic:0,bug:0,scientif:0,ani:0,condit:0,goal:0,propog:0,well:0,inherit:0,doe:[],caus:0,alias:0,itself:0,maintain:0,combin:0,thi:0,time:0,omit:0,link:0,everyth:0,invari:0,moreov:0,usual:0},objtypes:{},titles:["Contents"],objnames:{},filenames:["index"]}) -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. ohmu documentation master file, created by 2 | sphinx-quickstart on Sun May 11 10:04:06 2014. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Contents 7 | ================== 8 | 9 | .. toctree:: 10 | :maxdepth: 2 11 | 12 | 13 | Introduction 14 | ============ 15 | 16 | Ohmu is a new programming language being developed at Google. Right now, it 17 | is just a hobby project that a few engineers are working on in their spare 18 | time. Its purpose is to serve as a sandbox for experimenting with various 19 | compiler technologies, such as type systems, partial evaluation, run-time code 20 | generation, and GPU programming. 21 | 22 | *Disclaimer*: There are no plans to use ohmu internally or to release it as a 23 | real product. Anything and everything is subject to change without notice. 24 | May be known to the State of California to cause cancer in lab animals, 25 | including engineers in computer labs. Do not eat. 26 | 27 | Why a new language? 28 | ------------------- 29 | 30 | We believe that a good programming language is one where the tool chain 31 | provides as much assistance as possible. Optimizing compilers improve 32 | performance, type systems and warnings help find bugs, while IDEs and 33 | refactoring tools help organize and maintain code. These tools are effective 34 | only if the language itself has a well-defined semantics, so that the compiler 35 | or IDE can analyze and understand the code. 36 | 37 | Unfortunately, most practical programming languages have a semantics that is 38 | informal, ad-hoc, overly complex, or unsound in various ways, which tends to 39 | confound any attempt at analysis. A great deal of academic research has been 40 | done in the area of formal programming language semantics, but academic 41 | languages go too far in the other direction; they are either too formal (e.g. 42 | Agda) or impractical for most real-world tasks (e.g. Haskell). 43 | 44 | Our goal is to collect the best ideas from academic research, combine those 45 | ideas together, and apply them to the design of a modern, elegant, and above 46 | all, a *practical* language. 47 | 48 | Overview of Language Features 49 | ----------------------------- 50 | 51 | * High performance: 52 | 53 | * Faster than C. 54 | * Suitable for systems programming, games, or scientific computation. 55 | * Transparent foreign-function interface to C and C++ code. 56 | * Transparent support for GPGPU programming. 57 | * Advanced optimizations driven by static analysis (e.g. alias analysis). 58 | 59 | * Safe: 60 | 61 | * Type and memory safe. No unsafe casts or buffer overflows... 62 | * Thread-safe by design. No race conditions... 63 | * Designed from the ground-up for static analysis. 64 | 65 | * Modular and high-level: 66 | 67 | * Object-oriented programming: classes, inheritance, generics, and mixins. 68 | * Functional programming: type-classes, variant data types, and ADTs. 69 | * Mixin-modules: virtual classes and extensible data types. 70 | 71 | * Extensible: 72 | 73 | * *Extensible syntax*: libraries can extend the language with new syntax. 74 | * *Partial evaluation*: compile language extensions down to the core language. 75 | * Compile-time reflection and meta-programming. 76 | * Support for embedding domain-specific languages (DSLs): 77 | 78 | * E.g. parser generators, matrix libraries, image filters, shaders, etc. 79 | 80 | 81 | Key Technologies 82 | ^^^^^^^^^^^^^^^^ 83 | 84 | Although this may look like a long wish-list, all of these features depend 85 | primarily on just two key technologies: 86 | 87 | #. A sophisticated static type system. 88 | #. Partial evaluation. 89 | 90 | **Type system**. Type systems are routinely used to handle structural types, 91 | such as ``int`` and ``String``. However, they can do much more than that. 92 | Type systems a general purpose tool for declaring and enforcing any program 93 | invariant, including aliasing constraints, ownership, or freedom from race 94 | conditions. The ohmu type system is responsible for the *safety* features 95 | above, and it is a key part of *modularity*, since mixin modules have very 96 | complex types. Moreover, the type system enforces program invariants that are 97 | then used by the optimizer to achieve *high performance*. 98 | 99 | **Partial evaluation**. Partial evaluation optimizes code by shifting 100 | computations from run-time to compile-time. Most compilers can perform 101 | constant propogation. However, the ohmu compiler can perform arbitrary 102 | computations at compile-time. Moreover, the partial evaluator is linked to 103 | the static type system, which enables it to perform type-based 104 | transformations, like method de-virtualization. The *extensibility* features 105 | use partial evaluation to eliminate the run-time overhead that is generally 106 | associated with reflection and DSLs. 107 | 108 | 109 | Tradeoffs in Language Design 110 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 111 | 112 | There has traditionally been a tradeoff in language design between performance 113 | and safety. A safe language must perform additional run-time checks, such as 114 | array bounds checks, to prevent unsafe operations from occuring. Ohmu 115 | leverages the type system to achieve both performance and safety at the same 116 | time. E.g. if the type system can prove that the array bounds check always 117 | succeeds, then run-time check can be omitted. 118 | 119 | Similarly, there has traditionally been a tradeoff between performance and 120 | high-level abstractions. Abstractions introduce an additional level of 121 | indirection, which usually has a run-time cost. For example, object-oriented 122 | inheritance and virtual methods are a useful abstraction, but most compilers 123 | cannot inline virtual methods, so they have a high cost. Ohmu leverages 124 | partial evaluation to eliminate the overhead of most abstractions. For 125 | example, the partial evaluator can specialize a polymorphic function to a 126 | concrete type, thus allowing the virtual calls to be eliminated. 127 | 128 | 129 | Indices and tables 130 | ================== 131 | 132 | * :ref:`genindex` 133 | * :ref:`modindex` 134 | * :ref:`search` 135 | 136 | -------------------------------------------------------------------------------- /pull_clang_source_files.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CLANG_INC="$1"/tools/clang/include/clang/Analysis/Til 4 | CLANG_LIB="$1"/tools/clang/lib/Analysis/Til 5 | 6 | cp -v "$CLANG_INC"/base/SimpleArray.h src/base/ 7 | cp -v "$CLANG_INC"/base/MutArrayRef.h src/base/ 8 | cp -v "$CLANG_INC"/base/ArrayTree.h src/base/ 9 | 10 | cp -v "$CLANG_INC"/TILOps.def src/til/ 11 | cp -v "$CLANG_INC"/TILAnnKinds.def src/til/ 12 | cp -v "$CLANG_INC"/TILBaseType.h src/til/ 13 | cp -v "$CLANG_INC"/TIL.h src/til/ 14 | cp -v "$CLANG_INC"/TILTraverse.h src/til/ 15 | cp -v "$CLANG_INC"/TILCompare.h src/til/ 16 | cp -v "$CLANG_INC"/TILPrettyPrint.h src/til/ 17 | cp -v "$CLANG_INC"/Annotation.h src/til/ 18 | cp -v "$CLANG_INC"/AnnotationImpl.h src/til/ 19 | cp -v "$CLANG_INC"/CFGBuilder.h src/til/ 20 | cp -v "$CLANG_INC"/AttributeGrammar.h src/til/ 21 | cp -v "$CLANG_INC"/CopyReducer.h src/til/ 22 | cp -v "$CLANG_INC"/InplaceReducer.h src/til/ 23 | cp -v "$CLANG_INC"/SSAPass.h src/til/ 24 | cp -v "$CLANG_INC"/Bytecode.h src/til/ 25 | 26 | cp -v "$CLANG_LIB"/TIL.cpp src/til/ 27 | cp -v "$CLANG_LIB"/CFGBuilder.cpp src/til/ 28 | cp -v "$CLANG_LIB"/SSAPass.cpp src/til/ 29 | cp -v "$CLANG_LIB"/AnnotationImpl.cpp src/til/ 30 | cp -v "$CLANG_LIB"/Bytecode.cpp src/til/ 31 | -------------------------------------------------------------------------------- /push_clang_source_files.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CLANG_INC="$1"/tools/clang/include/clang/Analysis/Til 4 | CLANG_LIB="$1"/tools/clang/lib/Analysis/Til 5 | 6 | cp -v src/base/SimpleArray.h "$CLANG_INC"/base/ 7 | cp -v src/base/MutArrayRef.h "$CLANG_INC"/base/ 8 | cp -v src/base/ArrayTree.h "$CLANG_INC"/base/ 9 | 10 | cp -v src/til/TILOps.def "$CLANG_INC" 11 | cp -v src/til/TILAnnKinds.def "$CLANG_INC" 12 | cp -v src/til/TILBaseType.h "$CLANG_INC" 13 | cp -v src/til/TIL.h "$CLANG_INC" 14 | cp -v src/til/TILTraverse.h "$CLANG_INC" 15 | cp -v src/til/TILCompare.h "$CLANG_INC" 16 | cp -v src/til/TILPrettyPrint.h "$CLANG_INC" 17 | cp -v src/til/Annotation.h "$CLANG_INC" 18 | cp -v src/til/AnnotationImpl.h "$CLANG_INC" 19 | cp -v src/til/CFGBuilder.h "$CLANG_INC" 20 | cp -v src/til/AttributeGrammar.h "$CLANG_INC" 21 | cp -v src/til/CopyReducer.h "$CLANG_INC" 22 | cp -v src/til/InplaceReducer.h "$CLANG_INC" 23 | cp -v src/til/SSAPass.h "$CLANG_INC" 24 | cp -v src/til/Bytecode.h "$CLANG_INC" 25 | 26 | cp -v src/til/TIL.cpp "$CLANG_LIB" 27 | cp -v src/til/CFGBuilder.cpp "$CLANG_LIB" 28 | cp -v src/til/SSAPass.cpp "$CLANG_LIB" 29 | cp -v src/til/AnnotationImpl.cpp "$CLANG_LIB" 30 | cp -v src/til/Bytecode.cpp "$CLANG_LIB" 31 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | # set up LLVM dependencies 4 | # pass -DLLVM_DIR=${LLVM_INSTALL_DIR}/share/llvm/cmake/ to cmake 5 | # or set LLVM_DIR within ccmake, or cmake-gui 6 | 7 | find_package(LLVM REQUIRED CONFIG) 8 | message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") 9 | message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") 10 | 11 | foreach (LLVM_INCLUDE_DIR ${LLVM_INCLUDE_DIRS}) 12 | set(CLANG_INCLUDE_DIRS 13 | ${CLANG_INCLUDE_DIRS} 14 | ${LLVM_INCLUDE_DIR}/../tools/clang/include 15 | ) 16 | endforeach(LLVM_INCLUDE_DIR) 17 | message(STATUS "LLVM include dirs ${LLVM_INCLUDE_DIRS}") 18 | message(STATUS "Clang include dirs ${CLANG_INCLUDE_DIRS}") 19 | include_directories(${LLVM_INCLUDE_DIRS}) 20 | include_directories(${CLANG_INCLUDE_DIRS}) 21 | add_definitions(${LLVM_DEFINITIONS}) 22 | 23 | link_directories("${LLVM_DIR}/../../../lib") 24 | link_directories("${LLVM_DIR}/../../../tools/clang/lib") 25 | 26 | # Find the libraries that correspond to the LLVM components that we wish to use 27 | # llvm_map_components_to_libnames(llvm_libs support core irreader mc option) 28 | 29 | 30 | if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") 31 | add_definitions(-std=c++11) 32 | add_definitions(-fno-exceptions) 33 | add_definitions(-fno-rtti) 34 | # add_definitions(-Weverything -Wno-c++98-compat -Wno-padded -Wno-shorten-64-to-32 -Wno-weak-vtables -Wno-unused-parameter -Wno-sign-conversion -Wno-switch-enum -Wno-missing-prototypes -Wno-c++98-compat-pedantic -Wno-missing-noreturn -Wno-zero-length-array -Wno-float-equal) 35 | add_definitions(-Wall) 36 | endif() 37 | 38 | if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") 39 | add_definitions(-std=c++11) 40 | add_definitions(-fno-exceptions) 41 | add_definitions(-fno-rtti) 42 | add_definitions(-Wall) 43 | endif() 44 | 45 | # cl likes to complain about POSIX interfaces like fopen, but we don't care. 46 | if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") 47 | add_definitions(/wd4200) # nonstandard extension used : zero-sized array in struct/union 48 | add_definitions(/wd4201) 49 | add_definitions(/wd4291) 50 | # add_definitions(/W4 /WX) 51 | add_definitions(/GR-) 52 | add_definitions(/D_CRT_SECURE_NO_WARNINGS) 53 | add_definitions(/DWIN32_LEAN_AND_MEAN) 54 | add_definitions(/D_HAS_EXCEPTIONS=0) 55 | endif() 56 | 57 | include_directories("${PROJECT_SOURCE_DIR}/src") 58 | 59 | add_subdirectory(base) 60 | add_subdirectory(grammar) 61 | add_subdirectory(parser) 62 | add_subdirectory(til) 63 | 64 | # add_subdirectory(backend) 65 | add_subdirectory(lsa) 66 | add_subdirectory(test) 67 | 68 | list(GET LLVM_INCLUDE_DIRS 0 LLVM_SRC_INC_DIR) 69 | set(LLVM_SRC_DIR "${LLVM_SRC_INC_DIR}/../") 70 | 71 | # Unit tests depend on the google test infrastructure. 72 | 73 | if(EXISTS ${LLVM_SRC_DIR}/utils/unittest/googletest/include/gtest/gtest.h) 74 | include_directories(${LLVM_SRC_DIR}/utils/unittest/googletest/include) 75 | include_directories(${LLVM_SRC_DIR}/utils/unittest/googletest/) 76 | add_definitions(-DGTEST_HAS_RTTI=0) 77 | add_library(gtest 78 | ${LLVM_SRC_DIR}/utils/unittest/googletest/src/gtest-all.cc 79 | ) 80 | set(GTEST_BIN ${CMAKE_CURRENT_BINARY_DIR}) 81 | add_subdirectory(unittests) 82 | endif() 83 | 84 | -------------------------------------------------------------------------------- /src/backend/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | add_subdirectory(llvm) 4 | -------------------------------------------------------------------------------- /src/backend/jagger/codegen.cpp: -------------------------------------------------------------------------------- 1 | //===- event.cpp -----------------------------------------------*- C++ --*-===// 2 | // Copyright 2014 Google 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | //===----------------------------------------------------------------------===// 17 | 18 | // Peephole Notes: 19 | // load->immediate 20 | // mul->add/lea/shift 21 | // div->shift 22 | // cmp 0 folding 23 | // address mode folding 24 | // folding loads/stores into instrs 25 | // 0-offset jump/branch elimination 26 | // Commute arguments for destructive operations 27 | 28 | #if 0 29 | #include "types.h" 30 | #include "interface.h" 31 | 32 | namespace Jagger { 33 | static const GP32Reg regs[] = { 34 | EAX, ECX, EDX, EBX, 35 | R8D, R9D, R10D, R11D, 36 | R12D, R13D, R14D, R15D, 37 | }; 38 | 39 | static const char* const regNames[] = { 40 | "EAX", "ECX", "EDX", "EBX", 41 | "R8D", "R9D", "R10D", "R11D", 42 | "R12D", "R13D", "R14D", "R15D", 43 | }; 44 | 45 | extern "C" unsigned char _BitScanReverse(unsigned long * Index, unsigned long Mask); 46 | 47 | static int setToIndex(unsigned x) { 48 | unsigned long a; 49 | _BitScanReverse(&a, (unsigned long)x); 50 | return (int)a; 51 | } 52 | 53 | void Jump::emit(X64Builder& builder) { 54 | #if 0 55 | if (kind == JUMP) { 56 | printf("JMP ???\n"); 57 | builder.JMP(0); 58 | } 59 | else { 60 | printf("JMPcc ???\n"); 61 | builder.JA(0); 62 | } 63 | #endif 64 | } 65 | 66 | void IntLiteral::emit(X64Builder& builder) { 67 | #if 0 68 | for (unsigned set = copySet; set; set &= ~(set & -set)) 69 | printf("MOV %s, %d\n", regNames[setToIndex(set)], value); 70 | #endif 71 | } 72 | 73 | void Instruction::emit(X64Builder& builder) { 74 | #if 0 75 | switch (kind) { 76 | case Object::ADD: { 77 | unsigned reg0 = ((Event *)this)[-2].liveRange.reg; 78 | unsigned reg1 = ((Event *)this)[-1].liveRange.reg; 79 | printf("ADD %s, %s\n", regNames[setToIndex(reg0)], 80 | regNames[setToIndex(reg1)]); 81 | for (unsigned set = copySet & ~reg0; set; set &= ~(set & -set)) 82 | printf("MOV %s, %s\n", regNames[setToIndex(set & -set)], 83 | regNames[setToIndex(reg0)]); 84 | } break; 85 | case Object::MUL: 86 | printf("MUL\n"); 87 | break; 88 | case Object::CMP_EQ: 89 | case Object::CMP_LT: 90 | case Object::CMP_LE: 91 | printf("CMP\n"); 92 | break; 93 | //i->instruction.emit(builder); 94 | break; 95 | } 96 | #endif 97 | } 98 | 99 | void emitASM(X64Builder& builder, Event* events, size_t numEvents) { 100 | #if 0 101 | for (auto i = events, e = events + numEvents; i != e; ++i) 102 | switch (i->kind) { 103 | case Object::JUMP: 104 | case Object::BRANCH: 105 | i->jump.emit(builder); 106 | break; 107 | case Object::INT_LITERAL: 108 | i->intLiteral.emit(builder); 109 | break; 110 | case Object::ADD: 111 | case Object::MUL: 112 | case Object::CMP_EQ: 113 | case Object::CMP_LT: 114 | case Object::CMP_LE: 115 | i->instruction.emit(builder); 116 | break; 117 | } 118 | #endif 119 | } 120 | } 121 | #endif 122 | -------------------------------------------------------------------------------- /src/backend/jagger/interface.h: -------------------------------------------------------------------------------- 1 | //===- event.cpp -----------------------------------------------*- C++ --*-===// 2 | // Copyright 2014 Google 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | //===----------------------------------------------------------------------===// 17 | 18 | #pragma once 19 | #include "clang/Analysis/Analyses/ThreadSafetyTIL.h" 20 | 21 | // FIXME: Should take an allocator rather than a buffer. 22 | void encode(clang::threadSafety::til::SCFG* cfg, char* output); 23 | -------------------------------------------------------------------------------- /src/backend/jagger/print.cpp: -------------------------------------------------------------------------------- 1 | //===- event.cpp -----------------------------------------------*- C++ --*-===// 2 | // Copyright 2014 Google 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | //===----------------------------------------------------------------------===// 17 | 18 | #include "types.h" 19 | #include 20 | 21 | namespace Jagger { 22 | void Instruction::print(const Instruction* base) { 23 | const Opcode* opcode = this->opcode; 24 | printf("%3d %-10s", this - base, opcode->name); 25 | if (opcode->isJump) printf(" jump to %d", arg1 - base); 26 | if (opcode->hasResult) 27 | printf(" |%3d| {%04x (%04x) : %04x} [%2d]", key - base, ~invalidRegs, 28 | preferredRegs, reg, pressure); 29 | if (opcode->hasArg0) { 30 | printf(" (%d", arg0 - base); 31 | if (!arg0Live) 32 | printf("*"); 33 | if (opcode->hasArg1) { 34 | printf(", %d", arg1 - base); 35 | if (!arg1Live) 36 | printf("*"); 37 | } 38 | printf(")"); 39 | } 40 | if (opcode->isIntLiteral) 41 | printf(" %-3d ", arg0); 42 | printf("\n"); 43 | } 44 | 45 | void print(Instruction* instrs, size_t numInstrs) { 46 | for (auto i = instrs, e = instrs + numInstrs; i != e; ++i) 47 | i->print(instrs); 48 | } 49 | } -------------------------------------------------------------------------------- /src/backend/llvm/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | add_library(backend_llvm STATIC 4 | IRGen.cpp 5 | ) 6 | -------------------------------------------------------------------------------- /src/backend/llvm/IRGen.h: -------------------------------------------------------------------------------- 1 | //===- IRGen.h -------------------------------------------------*- C++ --*-===// 2 | // Copyright 2014 Google 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | //===----------------------------------------------------------------------===// 17 | // 18 | // Defines the LLVM IR Generation layer. 19 | // 20 | //===----------------------------------------------------------------------===// 21 | 22 | #ifndef OHMU_BACKEND_LLVM_IRGEN_H 23 | #define OHMU_BACKEND_LLVM_IRGEN_H 24 | 25 | #include "clang/Analysis/Analyses/ThreadSafetyTIL.h" 26 | 27 | 28 | #include "llvm/IR/Verifier.h" 29 | #include "llvm/IR/DerivedTypes.h" 30 | #include "llvm/IR/IRBuilder.h" 31 | #include "llvm/IR/LLVMContext.h" 32 | #include "llvm/IR/Module.h" 33 | #include "llvm/Support/Casting.h" 34 | 35 | 36 | namespace ohmu { 37 | namespace backend_llvm { 38 | 39 | using namespace clang::threadSafety::til; 40 | 41 | void generate_LLVM_IR(SExpr* E); 42 | 43 | } // end namespace backend_llvm 44 | } // end namespace ohmu 45 | 46 | #endif // OHMU_BACKEND_LLVM_IRGEN_H 47 | -------------------------------------------------------------------------------- /src/backend/x64builder/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | add_executable(x64builder x64builder.cpp) 4 | 5 | if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") 6 | add_definitions(/Dsnprintf=sprintf_s) 7 | endif() 8 | -------------------------------------------------------------------------------- /src/backend/x64builder/instrbuilder.h: -------------------------------------------------------------------------------- 1 | // Warren Hunt : 2014/02/08 2 | 3 | #include "instr.h" 4 | #include "args.h" 5 | 6 | //enum ConditionCode { 7 | // O, NO, B, NAE = B, C = B, NB, AE = NB, NC = NB, Z, E = Z, NZ, NE = NZ, BE, NA = BE, NBE, A = NBE, 8 | // S, NS, P, PE = P, NP, PO = NP, L, NGE = L, NL, GE = NL, LE, NG = LE, NLE, G = NLE 9 | //}; 10 | 11 | -------------------------------------------------------------------------------- /src/base/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | add_library(base STATIC 4 | MemRegion.cpp 5 | ) 6 | -------------------------------------------------------------------------------- /src/base/DiagnosticEmitter.h: -------------------------------------------------------------------------------- 1 | //===- DiagnosticEmitter.h -------------------------------------*- C++ --*-===// 2 | // Copyright 2014 Google 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | //===----------------------------------------------------------------------===// 17 | // 18 | // Provide a simple class for emitting error and warning messages. 19 | // 20 | //===----------------------------------------------------------------------===// 21 | 22 | #ifndef OHMU_TIL_DIAGNOSTIC_H 23 | #define OHMU_TIL_DIAGNOSTIC_H 24 | 25 | #include "LLVMDependencies.h" 26 | 27 | #include 28 | 29 | namespace ohmu { 30 | 31 | 32 | /// Wraps a std::ostream to provide custom output for various things. 33 | class DiagnosticStream { 34 | public: 35 | DiagnosticStream(std::ostream& s) : ss(s), emitted(false) { } 36 | ~DiagnosticStream() { 37 | if (emitted) 38 | ss << "\n"; 39 | } 40 | 41 | DiagnosticStream& operator<<(bool b) { 42 | emitted = true; 43 | if (b) ss << "true"; 44 | else ss << "false"; 45 | return *this; 46 | } 47 | 48 | DiagnosticStream& operator<<(int i) { 49 | emitted = true; 50 | ss << i; 51 | return *this; 52 | } 53 | 54 | DiagnosticStream& operator<<(unsigned i) { 55 | emitted = true; 56 | ss << i; 57 | return *this; 58 | } 59 | 60 | DiagnosticStream& operator<<(const char* msg) { 61 | emitted = true; 62 | ss << msg; 63 | return *this; 64 | } 65 | 66 | DiagnosticStream& operator<<(StringRef msg) { 67 | emitted = true; 68 | ss << msg.c_str(); 69 | return *this; 70 | } 71 | 72 | std::ostream& outputStream() { 73 | emitted = true; 74 | return ss; 75 | } 76 | 77 | public: 78 | std::ostream& ss; 79 | 80 | private: 81 | bool emitted; 82 | }; 83 | 84 | 85 | 86 | /// This file defines a simple interface for publishing warnings and errors. 87 | /// The current methods are placeholders; they will be improved in the future. 88 | class DiagnosticEmitter { 89 | public: 90 | DiagnosticEmitter() : dstream_(std::cerr) { } 91 | 92 | DiagnosticStream& error(const char* msg) { 93 | dstream_ << "\nerror: " << msg; 94 | return dstream_; 95 | } 96 | 97 | DiagnosticStream& warning(const char* msg) { 98 | dstream_ << "\nwarning: " << msg; 99 | return dstream_; 100 | } 101 | 102 | private: 103 | DiagnosticStream dstream_; 104 | }; 105 | 106 | 107 | } // end namespace ohmu 108 | 109 | #endif // OHMU_TIL_DIAGNOSTIC_H 110 | -------------------------------------------------------------------------------- /src/base/MemRegion.cpp: -------------------------------------------------------------------------------- 1 | //===- MemRegion.cpp -------------------------------------------*- C++ --*-===// 2 | // Copyright 2014 Google 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | //===----------------------------------------------------------------------===// 17 | 18 | #include "MemRegion.h" 19 | 20 | namespace ohmu { 21 | 22 | MemRegion::MemRegion() 23 | : currentBlock_(0), currentBlockEnd_(0), currentPosition_(0), 24 | largeBlocks_(0) { 25 | grabNewBlock(); 26 | } 27 | 28 | 29 | inline void freeList(char* p) { 30 | while (p) { 31 | // std::cerr << "."; 32 | // Each block has a pointer to the previous block at the start 33 | char* np = *reinterpret_cast(p); 34 | free(p); 35 | p = np; // pun intended. 36 | } 37 | // std::cerr << "\n"; 38 | } 39 | 40 | 41 | MemRegion::~MemRegion() { 42 | // std::cerr << "\nfree[" << std::hex << reinterpret_cast(this) << "]"; 43 | freeList(currentBlock_); 44 | // std::cerr << "\nfree[]"; 45 | freeList(largeBlocks_); 46 | } 47 | 48 | 49 | void MemRegion::grabNewBlock() { 50 | // std::cerr << "\nallocBlock[" << std::hex << reinterpret_cast(this) << "]"; 51 | 52 | // FIXME: ideally, we'd like to allocate exact memory pages. 53 | // If defaultBlockSize=4096, and malloc adds headers of its own, then we 54 | // may be over page size. 55 | char* newBlock = reinterpret_cast(malloc(defaultBlockSize)); 56 | linkBack(currentBlock_, newBlock); 57 | 58 | currentPosition_ = newBlock + headerSize; 59 | currentBlockEnd_ = newBlock + defaultBlockSize; 60 | } 61 | 62 | 63 | } // end namespace ohmu 64 | -------------------------------------------------------------------------------- /src/base/MemRegion.h: -------------------------------------------------------------------------------- 1 | //===- MemRegion.h ---------------------------------------------*- C++ --*-===// 2 | // Copyright 2014 Google 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | //===----------------------------------------------------------------------===// 17 | // 18 | // MemRegion is a class which does bump pointer allocation. Everything 19 | // allocated in the region will be destroyed when the region is destroyed. 20 | // 21 | // MemRegionRef stores a reference to a region. 22 | // 23 | //===----------------------------------------------------------------------===// 24 | 25 | 26 | #ifndef OHMU_MEMREGION_H 27 | #define OHMU_MEMREGION_H 28 | 29 | #include 30 | #include 31 | #include 32 | 33 | namespace ohmu { 34 | 35 | 36 | class MemRegion { 37 | public: 38 | // Create a new MemRegion 39 | MemRegion(); 40 | 41 | // Destroy a MemRegion, along with all data that was allocated in it. 42 | ~MemRegion(); 43 | 44 | // Pad sizes out to nearest 8 byte boundary. 45 | inline unsigned getAlignedSize(unsigned size) { 46 | if ((size & 0x7) == 0) 47 | return size; 48 | else 49 | return ((size >> 3) + 1) << 3; 50 | } 51 | 52 | template 53 | inline T* allocateT() { 54 | return reinterpret_cast(allocate(sizeof(T))); 55 | } 56 | 57 | template 58 | inline T* allocateT(size_t nelems) { 59 | return reinterpret_cast(allocate(sizeof(T) * nelems)); 60 | } 61 | 62 | // Allocate memory for a new object from the pool. 63 | // Small objects are bump allocated; large ones are not. 64 | inline void* allocate(size_t size) { 65 | // std::cerr << "allocate " << size << ".\n"; 66 | size = getAlignedSize(size); 67 | if (size <= maxBumpAllocSize) 68 | return allocateSmall(size); 69 | else 70 | return allocateLarge(size); 71 | } 72 | 73 | // No-op. 74 | void deallocate(void* ptr) { } 75 | 76 | inline void* allocateSmall(size_t size) { 77 | if (currentPosition_ + size >= currentBlockEnd_) 78 | grabNewBlock(); 79 | 80 | void* result = currentPosition_; 81 | currentPosition_ += size; 82 | return result; 83 | } 84 | 85 | inline void* allocateLarge(size_t size) { 86 | // std::cerr << "\nallocLarge " << size; 87 | char* p = reinterpret_cast(malloc(size + headerSize)); 88 | linkBack(largeBlocks_, p); 89 | return p + headerSize; 90 | } 91 | 92 | void grabNewBlock(); 93 | 94 | private: 95 | static const unsigned defaultBlockSize = 4096; // 4kb blocks 96 | static const unsigned maxBumpAllocSize = 512; // 8 allocs per block 97 | static const unsigned headerSize = sizeof(void*); 98 | 99 | void linkBack(char*& blockPointer, char* newBlock) { 100 | *reinterpret_cast(newBlock) = blockPointer; 101 | blockPointer = newBlock; 102 | } 103 | 104 | char* currentBlock_; // current bump allocation block 105 | char* currentBlockEnd_; 106 | char* currentPosition_; 107 | 108 | char* largeBlocks_; // linked list of large blocks 109 | }; 110 | 111 | 112 | // Simple wrapper class which holds a pointer to a region. 113 | class MemRegionRef { 114 | public: 115 | MemRegionRef() : allocator_(nullptr) {} 116 | MemRegionRef(MemRegion *region) : allocator_(region) { } 117 | 118 | void setRegion(MemRegion *r) { allocator_ = r; } 119 | 120 | void *allocate(size_t sz) { 121 | return allocator_->allocate(sz); 122 | } 123 | 124 | template T *allocateT() { 125 | return allocator_->allocateT(); 126 | } 127 | 128 | template T *allocateT(size_t nelems) { 129 | return allocator_->allocateT(nelems); 130 | } 131 | 132 | private: 133 | MemRegion* allocator_; 134 | }; 135 | 136 | 137 | } // end namespace ohmu 138 | 139 | 140 | inline void* operator new(size_t size, ohmu::MemRegionRef& region) { 141 | return region.allocate(size); 142 | } 143 | 144 | 145 | #endif // OHMU_MEMREGION_H 146 | -------------------------------------------------------------------------------- /src/base/MutArrayRef.h: -------------------------------------------------------------------------------- 1 | //===- MutArrayRef.h -------------------------------------------*- C++ --*-===// 2 | // 3 | // The LLVM Compiler Infrastructure 4 | // 5 | // This file is distributed under the University of Illinois Open Source 6 | // License. See LICENSE.TXT in the LLVM repository for details. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #ifndef OHMU_BASE_MUTARRAYREF_H 11 | #define OHMU_BASE_MUTARRAYREF_H 12 | 13 | 14 | namespace ohmu { 15 | 16 | // A version of ArrayRef which provides mutable access to the underlying data. 17 | template 18 | class MutArrayRef { 19 | public: 20 | typedef T* iterator; 21 | typedef const T* const_iterator; 22 | 23 | MutArrayRef() : data_(nullptr), len_(0) { } 24 | MutArrayRef(T* dat, size_t sz) : data_(dat), len_(sz) { } 25 | MutArrayRef(T* begin, T* end) : data_(begin), len_(end-begin) { } 26 | 27 | size_t size() const { return len_; } 28 | T& operator[](size_t i) { return data_[i]; } 29 | const T& operator[](size_t i) const { return data_[i]; } 30 | 31 | iterator begin() { return data_; } 32 | const_iterator begin() const { return begin(); } 33 | const_iterator cbegin() const { return begin(); } 34 | 35 | iterator end() { return data_ + len_; } 36 | const_iterator end() const { return end(); } 37 | const_iterator cend() const { return end(); } 38 | 39 | private: 40 | T* data_; 41 | size_t len_; 42 | }; 43 | 44 | } // end namespace ohmu 45 | 46 | #endif // OHMU_BASE_MUTARRAYREF_H 47 | -------------------------------------------------------------------------------- /src/base/NestedStack.h: -------------------------------------------------------------------------------- 1 | //===- NestedStack.h -------------------------------------------*- C++ --*-===// 2 | // Copyright 2014 Google 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | //===----------------------------------------------------------------------===// 17 | 18 | 19 | #ifndef OHMU_BASE_NESTEDSTACK_H_ 20 | #define OHMU_BASE_NESTEDSTACK_H_ 21 | 22 | #include 23 | 24 | namespace ohmu { 25 | 26 | 27 | /// NestedStack implements a logical "stack of stacks". 28 | /// There is a topmost stack of elements, which is visible, and allows 29 | /// elements to be pushed and popped as usual. 30 | /// The topmost stack can be saved, which will create a new, empty topmost 31 | /// stack. The original stack can then be restored later. 32 | /// The implementation uses a single contiguous array for all logical stacks. 33 | template 34 | class NestedStack { 35 | public: 36 | NestedStack() : Start(0) { } 37 | 38 | /// Push a new element onto the topmost stack. 39 | void push_back(const T& Elem) { Elements.push_back(Elem); } 40 | 41 | /// Pop an element off of the topmost stack. 42 | void pop_back() { 43 | assert(size() > 0 && "Cannot pop off of empty stack!."); 44 | Elements.pop_back(); 45 | } 46 | 47 | /// Return top element on topmost stack. 48 | const T& back() const { return Elements.back(); } 49 | T& back() { return Elements.back(); } 50 | 51 | /// Return the i^th element on the topmost stack. 52 | const T& at(unsigned i) const { return Elements.at(Start + i); } 53 | T& at(unsigned i) { return Elements.at(Start + i); } 54 | 55 | /// Return size of topmost stack 56 | unsigned size() const { return Elements.size() - Start; } 57 | 58 | /// Return true if topmost stack is empty. 59 | bool empty() const { return size() == 0; } 60 | 61 | /// Return all elements in topmost stack. 62 | ArrayRef elements() { 63 | T* pbegin = &Elements[Start]; 64 | return ArrayRef(pbegin, size()); 65 | } 66 | 67 | /// Clear all elements from topmost stack. 68 | void clear() { 69 | for (unsigned i=0,n=size(); i < n; ++i) 70 | Elements.pop_back(); 71 | } 72 | 73 | /// Save topmost stack, and return an id that can be used to restore it. 74 | /// Clear topmost stack. 75 | unsigned save() { 76 | unsigned Sv = Start; 77 | Start = Elements.size(); 78 | return Sv; 79 | } 80 | 81 | /// Restore a stack that was previously saved. 82 | void restore(unsigned SaveID) { 83 | assert(size() == 0 && "Must clear stack before restoring!"); 84 | Start = SaveID; 85 | } 86 | 87 | private: 88 | std::vector Elements; //< Array of elements. 89 | unsigned Start; //< Index of first elem in topmost stack. 90 | }; 91 | 92 | 93 | 94 | } // end namespace ohmu 95 | 96 | #endif // SRC_BASE_NESTEDSTACK_H_ 97 | -------------------------------------------------------------------------------- /src/base/SimpleArray.h: -------------------------------------------------------------------------------- 1 | //===- SimpleArray.h -------------------------------------------*- C++ --*-===// 2 | // 3 | // The LLVM Compiler Infrastructure 4 | // 5 | // This file is distributed under the University of Illinois Open Source 6 | // License. See LICENSE.TXT in the LLVM repository for details. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #ifndef OHMU_BASE_SIMPLEARRAY_H 11 | #define OHMU_BASE_SIMPLEARRAY_H 12 | 13 | #include "MemRegion.h" 14 | 15 | 16 | namespace ohmu { 17 | 18 | // A simple fixed size array class that does not manage its own memory, 19 | // suitable for use with bump pointer allocation. 20 | template class SimpleArray { 21 | public: 22 | SimpleArray() : Data(nullptr), Size(0), Capacity(0) {} 23 | SimpleArray(T *Dat, size_t Cp, size_t Sz = 0) 24 | : Data(Dat), Size(Sz), Capacity(Cp) {} 25 | SimpleArray(MemRegionRef A, size_t Cp) 26 | : Data(Cp == 0 ? nullptr : A.allocateT(Cp)), Size(0), Capacity(Cp) {} 27 | SimpleArray(SimpleArray &&A) 28 | : Data(A.Data), Size(A.Size), Capacity(A.Capacity) { 29 | A.Data = nullptr; 30 | A.Size = 0; 31 | A.Capacity = 0; 32 | } 33 | 34 | SimpleArray &operator=(SimpleArray &&RHS) { 35 | if (this != &RHS) { 36 | Data = RHS.Data; 37 | Size = RHS.Size; 38 | Capacity = RHS.Capacity; 39 | 40 | RHS.Data = nullptr; 41 | RHS.Size = RHS.Capacity = 0; 42 | } 43 | return *this; 44 | } 45 | 46 | /// Reserve space for at least Ncp items, reallocating if necessary. 47 | void reserve(size_t Ncp, MemRegionRef A) { 48 | if (Ncp <= Capacity) 49 | return; 50 | T *Odata = Data; 51 | Data = A.allocateT(Ncp); 52 | Capacity = Ncp; 53 | memcpy(Data, Odata, sizeof(T) * Size); 54 | return; 55 | } 56 | 57 | /// Resize to Nsz, initializing newly-added elements to V 58 | void resize(size_t Nsz, MemRegionRef A, const T& V) { 59 | if (Nsz <= Size) 60 | return; 61 | reserve(Nsz, A); 62 | for (size_t i = Size; i < Nsz; ++i) 63 | Data[i] = V; 64 | Size = Nsz; 65 | } 66 | 67 | /// Reserve space for at least N more items. 68 | void reserveCheck(size_t N, MemRegionRef A) { 69 | if (Capacity == 0) 70 | reserve(u_max(InitialCapacity, N), A); 71 | else if (Size + N > Capacity) 72 | reserve(u_max(Size + N, Capacity * 2), A); 73 | } 74 | 75 | typedef T *iterator; 76 | typedef const T *const_iterator; 77 | 78 | size_t size() const { return Size; } 79 | size_t capacity() const { return Capacity; } 80 | 81 | T &operator[](unsigned i) { 82 | assert(i < Size && "Array index out of bounds."); 83 | return Data[i]; 84 | } 85 | const T &operator[](unsigned i) const { 86 | assert(i < Size && "Array index out of bounds."); 87 | return Data[i]; 88 | } 89 | T &back() { 90 | assert(Size && "No elements in the array."); 91 | return Data[Size - 1]; 92 | } 93 | const T &back() const { 94 | assert(Size && "No elements in the array."); 95 | return Data[Size - 1]; 96 | } 97 | 98 | iterator begin() { return Data; } 99 | iterator end() { return Data + Size; } 100 | 101 | const_iterator begin() const { return Data; } 102 | const_iterator end() const { return Data + Size; } 103 | 104 | const_iterator cbegin() const { return Data; } 105 | const_iterator cend() const { return Data + Size; } 106 | 107 | void push_back(const T &Elem) { 108 | assert(Size < Capacity); 109 | Data[Size++] = Elem; 110 | } 111 | 112 | template 113 | void emplace_back(Params... Ps) { 114 | assert(Size < Capacity); 115 | new (&Data[Size++]) T(Ps...); 116 | } 117 | 118 | /// drop last n elements from array 119 | void drop(unsigned n = 0) { 120 | assert(Size > n); 121 | Size -= n; 122 | } 123 | 124 | /// drop all elements from array. 125 | void clear() { 126 | Size = 0; 127 | } 128 | 129 | void setValues(unsigned Sz, const T& C) { 130 | assert(Sz <= Capacity); 131 | Size = Sz; 132 | for (unsigned i = 0; i < Sz; ++i) { 133 | Data[i] = C; 134 | } 135 | } 136 | 137 | template unsigned append(Iter I, Iter E) { 138 | size_t Osz = Size; 139 | size_t J = Osz; 140 | for (; J < Capacity && I != E; ++J, ++I) 141 | Data[J] = *I; 142 | Size = J; 143 | return J - Osz; 144 | } 145 | 146 | // An adaptor to reverse a simple array 147 | class ReverseAdaptor { 148 | public: 149 | ReverseAdaptor(SimpleArray &Arr) : Array(Arr) {} 150 | // A reverse iterator used by the reverse adaptor 151 | class Iterator { 152 | public: 153 | Iterator(T *Dat) : Data(Dat) {} 154 | T &operator*() { return *Data; } 155 | const T &operator*() const { return *Data; } 156 | Iterator &operator++() { 157 | --Data; 158 | return *this; 159 | } 160 | bool operator!=(Iterator Other) { return Data != Other.Data; } 161 | 162 | private: 163 | T *Data; 164 | }; 165 | Iterator begin() { return Array.end() - 1; } 166 | Iterator end() { return Array.begin() - 1; } 167 | const Iterator begin() const { return Array.end() - 1; } 168 | const Iterator end() const { return Array.begin() - 1; } 169 | 170 | private: 171 | SimpleArray &Array; 172 | }; 173 | 174 | const ReverseAdaptor reverse() const { return ReverseAdaptor(*this); } 175 | ReverseAdaptor reverse() { return ReverseAdaptor(*this); } 176 | 177 | private: 178 | // std::max is annoying here, because it requires a reference, 179 | // thus forcing InitialCapacity to be initialized outside the .h file. 180 | size_t u_max(size_t i, size_t j) { return (i < j) ? j : i; } 181 | 182 | static const size_t InitialCapacity = 4; 183 | 184 | SimpleArray(const SimpleArray &A) = delete; 185 | 186 | T *Data; 187 | size_t Size; 188 | size_t Capacity; 189 | }; 190 | 191 | } // end namespace ohmu 192 | 193 | #endif // OHMU_BASE_SIMPLEARRAY_H 194 | -------------------------------------------------------------------------------- /src/clang/README.txt: -------------------------------------------------------------------------------- 1 | 2 | This directory contains files that translate the clang CFG into ohmu. 3 | These cannot currently be compiled separately from clang, but are checked 4 | in here, until such time as they are accepted upstream. 5 | 6 | --- The following will be updated on every sync with clang. 7 | 8 | Valid for clang revision: r242554 9 | -------------------------------------------------------------------------------- /src/clang/diff_clang_source_files.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CLANG_INC="$1"/tools/clang/include/clang/Analysis/Til 4 | CLANG_LIB="$1"/tools/clang/lib/Analysis/Til 5 | 6 | diff -u "$CLANG_INC"/ClangCFGWalker.h ClangCFGWalker.h 7 | diff -u "$CLANG_INC"/ClangTranslator.h ClangTranslator.h 8 | 9 | diff -u "$CLANG_LIB"/ClangTranslator.cpp ClangTranslator.cpp 10 | 11 | -------------------------------------------------------------------------------- /src/clang/pull_clang_source_files.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CLANG_INC="$1"/tools/clang/include/clang/Analysis/Til 4 | CLANG_LIB="$1"/tools/clang/lib/Analysis/Til 5 | 6 | cp -v "$CLANG_INC"/ClangCFGWalker.h . 7 | cp -v "$CLANG_INC"/ClangTranslator.h . 8 | 9 | cp -v "$CLANG_LIB"/ClangTranslator.cpp . 10 | 11 | -------------------------------------------------------------------------------- /src/clang/push_clang_source_files.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CLANG_INC="$1"/tools/clang/include/clang/Analysis/Til 4 | CLANG_LIB="$1"/tools/clang/lib/Analysis/Til 5 | 6 | cp -v ClangCFGWalker.h "$CLANG_INC" 7 | cp -v ClangTranslator.h "$CLANG_INC" 8 | 9 | cp -v ClangTranslator.cpp "$CLANG_LIB" 10 | 11 | -------------------------------------------------------------------------------- /src/grammar/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | add_custom_target(parser_grammar) 4 | add_custom_command( 5 | TARGET parser_grammar 6 | DEPENDS parser.grammar 7 | COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/parser.grammar ${CMAKE_CURRENT_BINARY_DIR}/parser.grammar) 8 | 9 | add_custom_target(ohmu_grammar) 10 | add_custom_command( 11 | TARGET ohmu_grammar 12 | DEPENDS ohmu.grammar 13 | COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/ohmu.grammar ${CMAKE_CURRENT_BINARY_DIR}/ohmu.grammar) 14 | -------------------------------------------------------------------------------- /src/grammar/ohmu.grammar: -------------------------------------------------------------------------------- 1 | 2 | // First rule for files 3 | definitions ::= 4 | e=slot { (append [] e) } 5 | |*[es] e=slot { (append es e) } 6 | ; 7 | 8 | // First rule for read-eval-print loop 9 | command ::= 10 | %TK_Newline { (litNull) } 11 | | e=expr %TK_Newline { e } 12 | ; 13 | 14 | literal ::= 15 | "null" { (litNull) } 16 | | "true" { (litBool "true") } 17 | | "false" { (litBool "false") } 18 | | s= %TK_LitCharacter { (litChar s) } 19 | | s= %TK_LitInteger { (litInteger s) } 20 | | s= %TK_LitFloat { (litFloat s) } 21 | | s= %TK_LitString { (litString s) } 22 | ; 23 | 24 | value ::= 25 | literal 26 | | "\\" "(" function 27 | | "\\@" "(" sfunction 28 | | "struct" ( "{" ss=slotList "}" { (record ss) } 29 | | "extends" e=expr "{" ss=slotList "}" { (record e ss) } 30 | ) 31 | | "[" es=exprList "]" { (array es) } 32 | ; 33 | 34 | field ::= 35 | ":" t=expr "=" ( e=expr { (field t e) } 36 | | "_" { (field t (litNull)) } 37 | ) 38 | ; 39 | 40 | function ::= 41 | ")" funBody 42 | | x= %TK_Identifier ":" t=expr v=funRest { (function x t v) } 43 | ; 44 | 45 | funRest ::= 46 | ")" funBody 47 | | "," x= %TK_Identifier ":" t=expr v=funRest { (function x t v) } 48 | ; 49 | 50 | sfunction ::= 51 | x= %TK_Identifier ")" v=funBody { (sfunction x v) } 52 | ; 53 | 54 | funBody ::= 55 | "(" function 56 | | "@" "(" sfunction 57 | | "->" expr 58 | | ":" t=expr "->" ( e=expr { (code t e) } 59 | | "_" { (code t (litNull)) } 60 | ) 61 | ; 62 | 63 | slot ::= 64 | m= %TK_Identifier 65 | ( "(" f=function ";" { (slot m f) } 66 | | "@" "(" f=sfunction ";" { (slot m f) } 67 | | f=field ";" { (slot m f) } 68 | | "=" e=expr ";" { (slot m e) } 69 | ) 70 | ; 71 | 72 | slotList ::= 73 | s=slot { (append [] s) } 74 | |*[ss] s=slot { (append ss s) } 75 | ; 76 | 77 | exprList ::= 78 | e=expr { (append [] e) } 79 | |*[es] "," e=expr { (append es e) } 80 | ; 81 | 82 | simple ::= 83 | value 84 | | s= %TK_Identifier { (identifier s) } 85 | | "(" e=expr ")" { e } 86 | | "{" e=statement "}" { e } 87 | ; 88 | 89 | mkApply[f,a] ::= 90 | ")" { (apply f a) } 91 | | "," app={ (apply f a) } b=expr mkApply[app, b] 92 | ; 93 | 94 | postfix ::= 95 | simple 96 | |*[f] ( "(" ( ")" { (call f) } 97 | | a=expr mkApply[f,a] 98 | ) 99 | | "@" "(" ( ")" { (sapply f) } 100 | | a=expr ")" { (sapply f a) } 101 | ) 102 | | "." m= %TK_Identifier { (project f m) } 103 | | "[" i=expr "]" { (arrayIndex f i) } 104 | | "^" { (load f) } 105 | ) 106 | ; 107 | 108 | unary ::= 109 | postfix 110 | | "-" e=unary { (unary "-" e) } 111 | | "~" e=unary { (unary "~" e) } 112 | | "!" e=unary { (unary "!" e) } 113 | ; 114 | 115 | expr10 ::= 116 | unary 117 | |*[a] ( "*" b=unary { (binary "*" a b) } 118 | | "/" b=unary { (binary "/" a b) } 119 | | "%" b=unary { (binary "%" a b) } 120 | ) 121 | ; 122 | 123 | expr20 ::= 124 | expr10 125 | |*[a] ( "+" b=expr10 { (binary "+" a b) } 126 | | "-" b=expr10 { (binary "-" a b) } 127 | ) 128 | ; 129 | 130 | expr30 ::= 131 | expr20 132 | |*[a] ( "<<" b=expr20 { (binary "<<" a b) } 133 | | ">>" b=expr20 { (binary ">>" a b) } 134 | ) 135 | ; 136 | 137 | expr40 ::= 138 | expr30 139 | |*[a] "&" b=expr30 { (binary "&" a b) } 140 | ; 141 | 142 | expr41 ::= 143 | expr40 144 | |*[a] "^^" b=expr40 { (binary "^" a b) } 145 | ; 146 | 147 | expr42 ::= 148 | expr41 149 | |*[a] "|" b=expr41 { (binary "|" a b) } 150 | ; 151 | 152 | expr50 ::= 153 | a=expr42 ( "==" b=expr42 { (binary "==" a b) } 154 | | "!=" b=expr42 { (binary "!=" a b) } 155 | | "<" b=expr42 { (binary "<" a b) } 156 | | "<=" b=expr42 { (binary "<=" a b) } 157 | | ">" b=expr42 { (binary ">" a b) } 158 | | ">=" b=expr42 { (binary ">=" a b) } 159 | | { a } 160 | ) 161 | ; 162 | 163 | expr60 ::= 164 | expr50 165 | |*[a] "&&" b=expr50 { (binary "&&" a b) } 166 | ; 167 | 168 | expr61 ::= 169 | expr60 170 | |*[a] "||" b=expr60 { (binary "||" a b) } 171 | ; 172 | 173 | expr70 ::= 174 | expr61 175 | | "new" e1=expr61 { (alloc e1) } 176 | ; 177 | 178 | expr90 ::= 179 | a=expr70 (":=" b=expr70 { (store a b) } 180 | | { a } 181 | ) 182 | ; 183 | 184 | expr ::= 185 | expr90 186 | | "if" "(" c=expr90 ")" "then" e1=expr90 "else" e2=expr90 { (if c e1 e2) } 187 | ; 188 | 189 | statement ::= 190 | e1=expr ";" (e2=statement { (let "" e1 e2) } 191 | | { e1 } 192 | ) 193 | | "let" s= %TK_Identifier e1=letbody ";" e2=statement { (let s e1 e2) } 194 | | "var" s= %TK_Identifier ":" t1=expr "=" e1=expr ";" e2=statement 195 | { (let s (alloc (field t1 e1)) e2) } 196 | ; 197 | 198 | 199 | letbody ::= 200 | "=" expr 201 | | "(" function 202 | | "@" "(" sfunction 203 | ; 204 | -------------------------------------------------------------------------------- /src/grammar/parser.grammar: -------------------------------------------------------------------------------- 1 | 2 | // An AST node, using Lisp-like syntax 3 | astNode ::= 4 | s = %TK_LitString { (tokenStr s) } 5 | | id = %TK_Identifier { (variable id) } 6 | | "[" "]" { (emptyList) } 7 | | "(" ( "append" e1=astNode e2=astNode ")" { (astAppend e1 e2) } 8 | | f = %TK_Identifier args=astNodeList ")" { (construct f args) } 9 | ) 10 | ; 11 | 12 | astNodeList ::= 13 | { [] } 14 | |*[es] e=astNode { (append es e) } 15 | ; 16 | 17 | // Simple expressions, not including references 18 | simple ::= 19 | s = %TK_LitString { (keyword s) } 20 | | "%" s = %TK_Identifier { (token s) } 21 | | "(" e=recurseLeft ")" { e } 22 | | "{" e=astNode "}" { (action e) } 23 | ; 24 | 25 | arguments ::= 26 | id = %TK_Identifier { (append [] id) } 27 | |*[as] "," id = %TK_Identifier { (append as id) } 28 | ; 29 | 30 | // Parse arguments, if any, and construct a reference from id 31 | reference[id] ::= 32 | "[" as=arguments "]" { (reference id as) } 33 | | { (reference id []) } 34 | ; 35 | 36 | // Simple expression or reference 37 | simpleCall ::= 38 | simple 39 | | id = %TK_Identifier reference[id] 40 | ; 41 | 42 | // Continue the sequence if possible, otherwise stop and return e. 43 | maybeSequence[e] ::= 44 | sq=sequence { (sequence e sq) } 45 | | { e }; 46 | 47 | sequence ::= 48 | e=simple maybeSequence[e] 49 | | id = %TK_Identifier ( "=" e=simpleCall sq=sequence { (sequence id e sq) } 50 | | e=reference[id] maybeSequence[e] 51 | ); 52 | 53 | option ::= 54 | e1=sequence ( "|" e2=option { (option e1 e2) } 55 | | { e1 } 56 | ); 57 | 58 | recurseLeft ::= 59 | e1=option ( "|*" "[" id = %TK_Identifier "]" e2=sequence 60 | { (recurseLeft id e1 e2 ) } 61 | | { e1 } 62 | ); 63 | 64 | maybeArguments ::= 65 | "[" as=arguments "]" { as } 66 | | { [] }; 67 | 68 | definition ::= 69 | id = %TK_Identifier as=maybeArguments "::=" e=recurseLeft ";" 70 | { (definition id as e) }; 71 | 72 | definitionList ::= 73 | { [] } 74 | |*[ds] d=definition { (definitionList ds d) } 75 | ; 76 | -------------------------------------------------------------------------------- /src/grammar/sexpr.grammar: -------------------------------------------------------------------------------- 1 | 2 | // An AST node, using Lisp-like syntax 3 | astNode ::= 4 | s = %TK_LitString { (tokenStr s) } 5 | | id = %TK_Identifier { (variable id) } 6 | | "[" "]" { (emptyList) } 7 | | "(" ( "append" e1=astNode e2=astNode ")" { (astAppend e1 e2) } 8 | | f = %TK_Identifier args=astNodeList ")" { (construct f args) } 9 | ) 10 | ; 11 | 12 | astNodeList ::= 13 | { [] } 14 | |*[es] e=astNode { (append es e) } 15 | ; 16 | -------------------------------------------------------------------------------- /src/lsa/BuildCallGraph.h: -------------------------------------------------------------------------------- 1 | //===- BuildCallGraph.h ----------------------------------------*- C++ --*-===// 2 | // 3 | // The LLVM Compiler Infrastructure 4 | // 5 | // This file is distributed under the University of Illinois Open Source 6 | // License. See LICENSE.TXT in the LLVM repository for details. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | // Construction of a call graph of C++ functions, paired with their Ohmu IR 10 | // translated bodies. 11 | //===----------------------------------------------------------------------===// 12 | 13 | #ifndef OHMU_LSA_BUILDCALLGRAPH_H 14 | #define OHMU_LSA_BUILDCALLGRAPH_H 15 | 16 | #include "clang/ASTMatchers/ASTMatchFinder.h" 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | namespace ohmu { 24 | namespace lsa { 25 | 26 | /// Interface for actually constructing the call graph from the discovered calls 27 | /// and produced Ohmu IR. In this graph functions are identified by their C++ 28 | /// mangled name. 29 | class CallGraphBuilder { 30 | public: 31 | virtual ~CallGraphBuilder() {} 32 | 33 | /// Request to store the information that there is a path in function From on 34 | /// which function To is called. 35 | virtual void AddCall(const std::string &From, const std::string &To) = 0; 36 | 37 | /// Request to store the generated ohmu IR representation of the function 38 | /// identified by Func. 39 | virtual void SetOhmuIR(const std::string &Func, const std::string &IR) = 0; 40 | }; 41 | 42 | /// The standard implementation of GraphConstructor stores the call graph as a 43 | /// mapping from function identifier to CGNode. 44 | class DefaultCallGraphBuilder : public CallGraphBuilder { 45 | public: 46 | void AddCall(const std::string &From, const std::string &To) override; 47 | void SetOhmuIR(const std::string &Func, const std::string &IR) override; 48 | 49 | void Print(std::ostream &Out); 50 | 51 | class CallGraphNode { 52 | public: 53 | void AddCall(const std::string &To) { OutgoingCalls.insert(To); } 54 | void SetIR(const std::string &IR) { OhmuIR = IR; } 55 | void Print(std::ostream &Out); 56 | 57 | const std::unordered_set *GetCalls() const { 58 | return &OutgoingCalls; 59 | } 60 | const std::string &GetIR() const { return OhmuIR; } 61 | 62 | private: 63 | std::unordered_set OutgoingCalls; 64 | std::string OhmuIR; 65 | }; 66 | 67 | const std::unordered_map> & 68 | GetGraph() { 69 | return Graph; 70 | } 71 | 72 | private: 73 | /// Returns the CGNode currently constructed for the function identified by 74 | /// Func. Creates a new node if none is associated with this function yet. 75 | CallGraphNode *GetNodeByName(const std::string &Func); 76 | 77 | std::unordered_map> 78 | Graph; // Mapping function names to their nodes. 79 | }; 80 | 81 | /// Tool to be used for creating call graphs with Ohmu IR for each function. 82 | class CallGraphBuilderTool { 83 | public: 84 | /// Create the required AST matchers and register them with 'Finder'. 85 | /// Matches all function declarations. 86 | void RegisterMatchers(CallGraphBuilder &Builder, 87 | clang::ast_matchers::MatchFinder *Finder); 88 | 89 | private: 90 | /// This tool creates and owns its MatchCallbacks. 91 | std::vector> 92 | match_callbacks_; 93 | }; 94 | 95 | } // namespace lsa 96 | } // namespace ohmu 97 | 98 | #endif // OHMU_LSA_BUILDCALLGRAPH_H 99 | -------------------------------------------------------------------------------- /src/lsa/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(lsa 2 | BuildCallGraph.cpp 3 | ) 4 | 5 | # There is quite some repetition here, but it does not as simple to remove 6 | # dependencies as it may appear. 7 | target_link_libraries(lsa 8 | clangASTMatchers 9 | clangTooling 10 | clangDriver 11 | clangAST 12 | clangLex 13 | clangParse 14 | clangBasic 15 | clangFrontend 16 | clangEdit 17 | clangAST 18 | clangSerialization 19 | clangSema 20 | clangAST 21 | clangAnalysis 22 | clangASTMatchers 23 | clangFrontendTool 24 | clangDynamicASTMatchers 25 | clangTooling 26 | clangAST 27 | clangASTMatchers 28 | clangBasic 29 | clangFrontend 30 | clangLex 31 | clangRewrite 32 | clangTooling 33 | clangToolingCore 34 | 35 | ohmuTil 36 | 37 | LLVMMC 38 | LLVMMCParser 39 | LLVMIRReader 40 | LLVMOption 41 | LLVMCore 42 | LLVMSupport 43 | ) 44 | 45 | add_subdirectory (examples) -------------------------------------------------------------------------------- /src/lsa/GraphComputation.h: -------------------------------------------------------------------------------- 1 | //===- GraphComputation.h --------------------------------------*- C++ --*-===// 2 | // 3 | // The LLVM Compiler Infrastructure 4 | // 5 | // This file is distributed under the University of Illinois Open Source 6 | // License. See LICENSE.TXT in the LLVM repository for details. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | // The purpose of this level of indirection is to easily replace the standalone 10 | // framework with a different framework providing the same GraphComputation and 11 | // GraphVertex interface. 12 | //===----------------------------------------------------------------------===// 13 | 14 | #ifndef OHMU_LSA_GRAPHCOMPUTATION_H 15 | #define OHMU_LSA_GRAPHCOMPUTATION_H 16 | 17 | #include "StandaloneGraphComputation.h" 18 | 19 | /// To provide serialization in Google's Pregel framework. 20 | template class StringCoderCustom; 21 | 22 | namespace ohmu { 23 | namespace lsa { 24 | 25 | // Adapted from til/Bytecode.cpp: 26 | static void writeUInt64ToString(uint64_t V, string *result) { 27 | if (V == 0) { 28 | result->push_back('\0'); 29 | return; 30 | } 31 | while (V > 0) { 32 | uint64_t V2 = V >> 7; 33 | uint8_t Hibit = (V2 == 0) ? 0 : 0x80; 34 | // Write lower 7 bits. The 8th bit is high if there's more to write. 35 | result->push_back(static_cast((V & 0x7Fu) | Hibit)); 36 | V = V2; 37 | } 38 | } 39 | 40 | static uint64_t readUInt64FromString(const string &str, int &index) { 41 | uint64_t V = 0; 42 | for (unsigned B = 0; B < 64; B += 7) { 43 | uint64_t Byt = str[index++]; 44 | V = V | ((Byt & 0x7Fu) << B); 45 | if ((Byt & 0x80) == 0) 46 | break; 47 | } 48 | return V; 49 | } 50 | 51 | } // namespace lsa 52 | } // namespace ohmu 53 | 54 | #endif // OHMU_LSA_GRAPHCOMPUTATION_H 55 | -------------------------------------------------------------------------------- /src/lsa/GraphDeserializer.h: -------------------------------------------------------------------------------- 1 | #ifndef OHMU_LSA_GRAPHDESERIALIZER_H 2 | #define OHMU_LSA_GRAPHDESERIALIZER_H 3 | 4 | #include "lsa/StandaloneGraphComputation.h" 5 | #include "til/Bytecode.h" 6 | 7 | namespace ohmu { 8 | namespace lsa { 9 | 10 | template 11 | class GraphDeserializer { 12 | public: 13 | static void read(const std::string& FileName, 14 | StandaloneGraphBuilder *Builder) { 15 | ohmu::MemRegion Arena; 16 | ohmu::til::BytecodeFileReader ReadStream(FileName, 17 | ohmu::MemRegionRef(&Arena)); 18 | 19 | int32_t NFunc = ReadStream.readInt32(); 20 | for (unsigned i = 0; i < NFunc; i++) { 21 | std::string Function = ReadStream.readString(); 22 | std::string OhmuIR = ReadStream.readString(); 23 | typename GraphTraits::VertexValueType Value; 24 | Builder->addVertex(Function, OhmuIR, Value); 25 | 26 | int32_t NNodes = ReadStream.readInt32(); 27 | for (unsigned n = 0; n < NNodes; n++) { 28 | std::string Call = ReadStream.readString(); 29 | Builder->addCall(Function, Call); 30 | } 31 | } 32 | } 33 | }; 34 | 35 | } // namespace lsa 36 | } // namespace ohmu 37 | 38 | #endif // OHMU_LSA_GRAPHDESERIALIZER_H 39 | -------------------------------------------------------------------------------- /src/lsa/GraphSerializer.h: -------------------------------------------------------------------------------- 1 | #ifndef OHMU_LSA_GRAPHSERIALIZER_H 2 | #define OHMU_LSA_GRAPHSERIALIZER_H 3 | 4 | #include "clang/Analysis/Til/Bytecode.h" 5 | #include "lsa/BuildCallGraph.h" 6 | 7 | namespace ohmu { 8 | namespace lsa { 9 | 10 | class GraphSerializer { 11 | public: 12 | static void write(const std::string& FileName, 13 | DefaultCallGraphBuilder *Builder) { 14 | ohmu::til::BytecodeFileWriter WriteStream(FileName); 15 | 16 | WriteStream.writeInt32(Builder->GetGraph().size()); 17 | for (const auto &Pair : Builder->GetGraph()) { 18 | WriteStream.writeString(Pair.first); 19 | WriteStream.writeString(Pair.second->GetIR()); 20 | WriteStream.writeInt32(Pair.second->GetCalls()->size()); 21 | for (const std::string &Call : *Pair.second->GetCalls()) { 22 | WriteStream.writeString(Call); 23 | } 24 | } 25 | 26 | WriteStream.flush(); 27 | } 28 | }; 29 | 30 | } // namespace lsa 31 | } // namespace ohmu 32 | 33 | #endif // OHMU_LSA_GRAPHSERIALIZER_H 34 | -------------------------------------------------------------------------------- /src/lsa/StandaloneRunner.h: -------------------------------------------------------------------------------- 1 | //===- StandaloneRunner.h --------------------------------------*- C++ --*-===// 2 | // 3 | // The LLVM Compiler Infrastructure 4 | // 5 | // This file is distributed under the University of Illinois Open Source 6 | // License. See LICENSE.TXT in the LLVM repository for details. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | // Convenience class providing methods to run graph computations with the LSA 10 | // framework. 11 | //===----------------------------------------------------------------------===/ 12 | 13 | #ifndef OHMU_LSA_STANDALONERUNNER_H 14 | #define OHMU_LSA_STANDALONERUNNER_H 15 | 16 | #include "clang/Tooling/CommonOptionsParser.h" 17 | #include "llvm/Support/CommandLine.h" 18 | #include "lsa/GraphDeserializer.h" 19 | #include "lsa/StandaloneGraphComputation.h" 20 | 21 | #include 22 | 23 | namespace ohmu { 24 | namespace lsa { 25 | 26 | static llvm::cl::opt NThreads("t", 27 | llvm::cl::desc("Specify number of threads"), 28 | llvm::cl::value_desc("number"), 29 | llvm::cl::Optional); 30 | 31 | static llvm::cl::opt 32 | InputFile("i", llvm::cl::desc("Specify input file"), 33 | llvm::cl::value_desc("file"), llvm::cl::Required); 34 | 35 | template class StandaloneRunner { 36 | public: 37 | StandaloneRunner(int argc, const char *argv[]) { 38 | llvm::cl::ParseCommandLineOptions(argc, argv); 39 | } 40 | 41 | void readCallGraph() { 42 | GraphDeserializer::read(InputFile.getValue(), 43 | &ComputationGraphBuilder); 44 | } 45 | 46 | void runComputation() { 47 | if (NThreads.getNumOccurrences() > 0) 48 | ComputationGraphBuilder.setNThreads(NThreads.getValue()); 49 | 50 | ComputationGraphBuilder.run(&Factory); 51 | } 52 | 53 | void printComputationResult(bool Alphabetic = false) { 54 | std::unique_ptr> Computation( 55 | Factory.createComputation()); 56 | if (Alphabetic) 57 | ComputationGraphBuilder.sortVertices(); 58 | for (const auto &Vertex : ComputationGraphBuilder.getVertices()) 59 | std::cout << Vertex.id() << ": " << Computation->output(&Vertex) << "\n"; 60 | } 61 | 62 | private: 63 | ohmu::lsa::StandaloneGraphBuilder ComputationGraphBuilder; 64 | ohmu::lsa::GraphComputationFactory Factory; 65 | }; 66 | 67 | } // namespace lsa 68 | } // namespace ohmu 69 | 70 | #endif // OHMU_LSA_STANDALONERUNNER_H 71 | -------------------------------------------------------------------------------- /src/lsa/examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(lsa_example_scc 2 | SCCComputation.cpp 3 | ) 4 | 5 | add_library(lsa_example_escape 6 | EscapeAnalysis.cpp 7 | ) 8 | 9 | # Would prefer to link these against til instead of ohmuTil. However, there 10 | # seems to be a problem with the Ohmu version of MemRegion? Or at least it 11 | # behaves differently. 12 | target_link_libraries(lsa_example_scc 13 | ohmuTil LLVMOption 14 | ) 15 | target_link_libraries(lsa_example_escape 16 | ohmuTil LLVMOption 17 | ) -------------------------------------------------------------------------------- /src/lsa/examples/ExampleOhmuComputation.h: -------------------------------------------------------------------------------- 1 | //===- SCCComputation.h ----------------------------------------*- C++ --*-===// 2 | // 3 | // The LLVM Compiler Infrastructure 4 | // 5 | // This file is distributed under the University of Illinois Open Source 6 | // License. See LICENSE.TXT in the LLVM repository for details. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | // Simple computation that checks whether a function, or any of the functions it 10 | // calls, modifies a global variable. It serves as an example of how to run an 11 | // Ohmu analysis, and is by no means perfect :) 12 | // 13 | // The computation only finds direct changes to global variables, not changes 14 | // via e.g. aliased values or passed-by-reference. It also has false positive, 15 | // e.g. when assigning to a local reference. 16 | //===----------------------------------------------------------------------===// 17 | 18 | #ifndef OHMU_LSA_EXAMPLES_EXAMPLEOHMUOMPUTATION_H 19 | #define OHMU_LSA_EXAMPLES_EXAMPLEOHMUOMPUTATION_H 20 | 21 | // NOTE: Whatever is included should not include ohmu's version of MemRegion.h, 22 | // since in a standalone computation this code is combined with the call graph 23 | // builder, which uses the version of MemRegion.h residing in Clang, which is 24 | // different. The solution is to let the standalone version also generate 25 | // intermediate results on disks, allowing us to create separate binaries for 26 | // call graph generation and analysis. 27 | 28 | #include "clang/Analysis/Til/TILTraverse.h" 29 | #include "lsa/GraphComputation.h" 30 | 31 | namespace ohmu { 32 | namespace lsa { 33 | 34 | class OhmuComputation; 35 | 36 | template <> struct GraphTraits { 37 | // True if we think this function changes a global variable. 38 | typedef bool VertexValueType; 39 | typedef bool MessageValueType; 40 | }; 41 | 42 | /// Simple traversal that looks for any store operation of the kind "a.b := c" 43 | /// where "a" is not part of a record. If such a store operation exists, we 44 | /// conclude that "b" is a global variable and mark this function as modifying 45 | /// global variables. 46 | class FindGlobalModification 47 | : public ohmu::til::Traversal, 48 | public ohmu::til::DefaultScopeHandler, 49 | public ohmu::til::DefaultReducer { 50 | public: 51 | typedef ohmu::til::Traversal SuperTv; 52 | 53 | FindGlobalModification() : MadeModification(false) {} 54 | 55 | void reduceStore(ohmu::til::Store *E) { 56 | if (auto *Dest = ohmu::dyn_cast(E->destination())) { 57 | if (!Dest->record()) { 58 | MadeModification = true; 59 | } 60 | } 61 | } 62 | 63 | /// Shortcut traversal if we already know that this function makes global 64 | /// modifications. 65 | template void traverse(T *E, ohmu::til::TraversalKind K) { 66 | if (!MadeModification) 67 | SuperTv::traverse(E, K); 68 | } 69 | 70 | /// Returns true if the traversed function modifies a global variable. 71 | bool madeModification() { return MadeModification; } 72 | 73 | protected: 74 | bool MadeModification; 75 | }; 76 | 77 | /// Distributed graph computation that determines whether calling a function 78 | /// changes a global variable. In the first step it computes the changes made 79 | /// in this function body; then forwards this information to its callers. 80 | class OhmuComputation : public GraphComputation { 81 | public: 82 | void computePhase(GraphVertex *Vertex, const string &Phase, 83 | MessageList Messages) override { 84 | 85 | // First step, compute if this function modifies a global variable. If so, 86 | // update the state and inform callers. 87 | if (stepCount() == 0) { 88 | *Vertex->mutableValue() = modifiesGlobal(Vertex); 89 | 90 | if (Vertex->value()) 91 | for (const string &Out : Vertex->outgoingCalls()) 92 | Vertex->sendMessage(Out, true); 93 | 94 | // Second step; only care about incoming messages if so far we think this 95 | // function does not change global variables. If one of the functions we 96 | // call informs us that it changes a global variable, update the state 97 | // and inform callers of this function. 98 | } else if (!Vertex->value()) { 99 | for (const Message &In : Messages) { 100 | if (In.value()) { 101 | *Vertex->mutableValue() = true; 102 | } 103 | } 104 | if (Vertex->value()) 105 | for (const string &Out : Vertex->outgoingCalls()) 106 | Vertex->sendMessage(Out, true); 107 | } 108 | 109 | Vertex->voteToHalt(); 110 | } 111 | 112 | string output(const GraphVertex *Vertex) const override { 113 | return Vertex->value() ? "yes" : "no"; 114 | } 115 | 116 | private: 117 | /// Run traversal to determine if this function changes a global variable. 118 | bool modifiesGlobal(GraphVertex *Vertex) { 119 | if (!Vertex->ohmuIR()) { 120 | std::cerr << "Could not read OhmuIR of " << Vertex->id() << ".\n"; 121 | return false; 122 | } 123 | 124 | FindGlobalModification Finder; 125 | Finder.traverseAll(Vertex->ohmuIR()); 126 | return Finder.madeModification(); 127 | } 128 | }; 129 | 130 | } // namespace ohmu 131 | } // namespace lsa 132 | 133 | #endif // OHMU_LSA_EXAMPLES_EXAMPLEOHMUOMPUTATION_H 134 | -------------------------------------------------------------------------------- /src/lsa/examples/SCCComputation.cpp: -------------------------------------------------------------------------------- 1 | //===- SCCComputation.cpp --------------------------------------*- C++ --*-===// 2 | // 3 | // The LLVM Compiler Infrastructure 4 | // 5 | // This file is distributed under the University of Illinois Open Source 6 | // License. See LICENSE.TXT in the LLVM repository for details. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #include 11 | 12 | #include "SCCComputation.h" 13 | 14 | namespace { 15 | /// Phase identifiers 16 | const char kPhaseForward[] = "phase_forward"; 17 | const char kPhaseBackward[] = "phase_backward"; 18 | const char kPhaseDecompose[] = "phase_decompose"; 19 | 20 | /// Special value representing 'infinity' as vertex identity. Thus assuming that 21 | /// this is not a value that can appear as a real identity. 22 | const char kInfinity[] = "INF"; 23 | } // end namespace 24 | 25 | namespace ohmu { 26 | namespace lsa { 27 | 28 | SCCNode::SCCNode() { 29 | ForwardMin = kInfinity; 30 | BackwardMin = kInfinity; 31 | } 32 | 33 | string SCCComputation::transition(const string &Phase) { 34 | if (!shouldReiterate()) 35 | return "HALT"; 36 | if (Phase == "START") { 37 | return kPhaseForward; 38 | } else if (Phase == kPhaseForward) { 39 | return kPhaseBackward; 40 | } else if (Phase == kPhaseBackward) { 41 | return kPhaseDecompose; 42 | } else if (Phase == kPhaseDecompose) { 43 | return kPhaseForward; 44 | } 45 | return "HALT"; 46 | } 47 | 48 | void SCCComputation::computePhase(GraphVertex *Vertex, const string &Phase, 49 | MessageList Messages) { 50 | // As long as some vertex is not in a known SCC, we should keep cycling 51 | // through the phases. 52 | if (!inSCC(Vertex)) { 53 | Vertex->voteToReiterate(); 54 | if (Phase == kPhaseForward) { 55 | forwardMin(Vertex, Messages); 56 | } else if (Phase == kPhaseBackward) { 57 | backwardMin(Vertex, Messages); 58 | } else if (Phase == kPhaseDecompose) { 59 | decomposeGraph(Vertex, Messages); 60 | } 61 | } 62 | 63 | // Always halt; only wake up on incoming messages. 64 | Vertex->voteToHalt(); 65 | } 66 | 67 | /// First set the current ForwardMin to this vertex' id and forward it on all 68 | /// outgoing calls. While the incoming messages contain an id lower than 69 | /// ForwardMin, update it and forward the new lowest value. 70 | void SCCComputation::forwardMin(GraphVertex *Vertex, MessageList Messages) { 71 | if (stepCount() == 0) { 72 | (*Vertex->mutableValue()).ForwardMin = Vertex->id(); 73 | for (const string &Out : Vertex->outgoingCalls()) { 74 | Vertex->sendMessage(Out, Vertex->value().ForwardMin); 75 | } 76 | } else { 77 | bool updated = false; 78 | for (const Message &Incoming : Messages) { 79 | if (Incoming.value().compare(Vertex->value().ForwardMin) < 0 || 80 | Vertex->value().ForwardMin == kInfinity) { 81 | (*Vertex->mutableValue()).ForwardMin = Incoming.value(); 82 | updated = true; 83 | } 84 | } 85 | // If we updated ForwardMin, inform our forward-neighbours. 86 | if (updated) { 87 | for (const string &Out : Vertex->outgoingCalls()) { 88 | Vertex->sendMessage(Out, Vertex->value().ForwardMin); 89 | } 90 | } 91 | } 92 | } 93 | 94 | /// First set the current BackwardMin to this vertex' id if it received its 95 | /// own id as ForwardMin, otherwise to infinite. While the incoming messages 96 | /// contain an id lower than BackwardMin, update it and backward the new value. 97 | void SCCComputation::backwardMin(GraphVertex *Vertex, MessageList Messages) { 98 | if (stepCount() == 0) { 99 | if (Vertex->id() != Vertex->value().ForwardMin) { 100 | (*Vertex->mutableValue()).BackwardMin = kInfinity; 101 | } else { 102 | (*Vertex->mutableValue()).BackwardMin = Vertex->id(); 103 | for (const string &In : Vertex->incomingCalls()) { 104 | Vertex->sendMessage(In, Vertex->value().BackwardMin); 105 | } 106 | } 107 | } else { 108 | bool updated = false; 109 | for (const Message &Incoming : Messages) { 110 | if (Incoming.value().compare(Vertex->value().BackwardMin) < 0 || 111 | Vertex->value().BackwardMin == kInfinity) { 112 | (*Vertex->mutableValue()).BackwardMin = Incoming.value(); 113 | updated = true; 114 | } 115 | } 116 | // If we updated BackwardMin, inform our backward-neighbours. 117 | if (updated) { 118 | for (const string &In : Vertex->incomingCalls()) { 119 | Vertex->sendMessage(In, Vertex->value().BackwardMin); 120 | } 121 | } 122 | } 123 | } 124 | 125 | /// In step 0, send on all outgoing calls this vertex' partition id. 126 | /// In SCCComputation 1, remove calls to vertices that sent a different 127 | /// partition id. 128 | void SCCComputation::decomposeGraph(GraphVertex *Vertex, MessageList Messages) { 129 | string partition = partitionID(Vertex); 130 | if (stepCount() == 0) { 131 | for (const string &Out : Vertex->outgoingCalls()) { 132 | Vertex->sendMessage(Out, partition); 133 | } 134 | } else { 135 | for (const Message &Incoming : Messages) { 136 | if (Incoming.value() != partition) { 137 | removeCall(Incoming.source(), Vertex->id()); 138 | } 139 | } 140 | } 141 | } 142 | 143 | bool SCCComputation::inSCC(GraphVertex *Vertex) { 144 | return Vertex->value().ForwardMin != kInfinity && 145 | Vertex->value().ForwardMin == Vertex->value().BackwardMin; 146 | } 147 | 148 | string SCCComputation::partitionID(const GraphVertex *Vertex) const { 149 | std::stringstream Stream; 150 | Stream << Vertex->value().ForwardMin; 151 | Stream << ":"; 152 | Stream << Vertex->value().BackwardMin; 153 | return Stream.str(); 154 | } 155 | 156 | } // namespace ohmu 157 | } // namespace lsa 158 | -------------------------------------------------------------------------------- /src/lsa/examples/SCCComputation.h: -------------------------------------------------------------------------------- 1 | //===- SCCComputation.h ----------------------------------------*- C++ --*-===// 2 | // 3 | // The LLVM Compiler Infrastructure 4 | // 5 | // This file is distributed under the University of Illinois Open Source 6 | // License. See LICENSE.TXT in the LLVM repository for details. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | // Example distributed graph computation, computing the strongly connected 10 | // components (SCC) in a graph. This is an implementation of the unoptimized SCC 11 | // algorithm described by Yan et al. in "Pregel Algorithms for Graph 12 | // Connectivity Problems with Performance Guarantees" from VLDB 2014: 13 | // 14 | // http://www.vldb.org/pvldb/vol7/p1821-yan.pdf 15 | // 16 | // The algorithm consists of three phases which are repeated until all SCCs are 17 | // found: 18 | // 1) forward-min: vertices keep communicating the minimal vertex-id they have 19 | // encountered over their forward edges until no lower id is received. 20 | // 2) backward-min: vertices keep communicating the minimal vertex-id they have 21 | // encountered over their backward edges until no lower id is received. 22 | // 3) decompose: edges between vertices whose pairs (forwardMin, backwardMin), 23 | // ie their partitions, are not equal are removed 24 | // Vertices are in an SCC when forwardMin = backwardMin. 25 | // The SCCs are identified by the pair (forwardMin, backwardMin). 26 | //===----------------------------------------------------------------------===// 27 | 28 | #ifndef OHMU_LSA_EXAMPLES_SCCCOMPUTATION_H 29 | #define OHMU_LSA_EXAMPLES_SCCCOMPUTATION_H 30 | 31 | #include "lsa/GraphComputation.h" 32 | 33 | namespace ohmu { 34 | namespace lsa { 35 | 36 | /// The type of the value at a vertex during SCC computation. 37 | class SCCNode { 38 | public: 39 | SCCNode(); 40 | 41 | /// The minimal ID encountered sending over the outgoing calls. 42 | string ForwardMin; 43 | 44 | /// The minimal ID encountered sending over the incoming calls. 45 | string BackwardMin; 46 | }; 47 | 48 | class SCCComputation; 49 | 50 | template <> struct GraphTraits { 51 | typedef SCCNode VertexValueType; 52 | typedef string MessageValueType; 53 | }; 54 | 55 | class SCCComputation : public GraphComputation { 56 | public: 57 | void computePhase(GraphVertex *Vertex, const string &Phase, 58 | MessageList Messages) override; 59 | 60 | string transition(const string &Phase) override; 61 | 62 | string output(const GraphVertex *Vertex) const override { 63 | return partitionID(Vertex); 64 | } 65 | 66 | private: 67 | /// Returns true if the SCC of this vertex is known. 68 | bool inSCC(GraphVertex *Vertex); 69 | 70 | /// The computation phases. 71 | void forwardMin(GraphVertex *Vertex, MessageList Messages); 72 | void backwardMin(GraphVertex *Vertex, MessageList Messages); 73 | void decomposeGraph(GraphVertex *Vertex, MessageList Messages); 74 | 75 | /// Send a message with this vertex' current minimal value. The argument 76 | /// indicates whether we are in the forward or backward phase. 77 | void sendUpdateMessage(GraphVertex *Vertex, bool Forward); 78 | 79 | /// Returns this vertex' partition id. This is the concatenation of the 80 | /// ForwardMin and BackwardMin value. 81 | string partitionID(const GraphVertex *Vertex) const; 82 | }; 83 | 84 | } // namespace ohmu 85 | } // namespace lsa 86 | 87 | /// Serialization for Google's Pregel framework. 88 | template <> class StringCoderCustom { 89 | public: 90 | static void Encode(const ohmu::lsa::SCCNode &value, string *result) { 91 | result->clear(); 92 | ohmu::lsa::writeUInt64ToString(value.ForwardMin.size(), result); 93 | result->append(value.ForwardMin); 94 | ohmu::lsa::writeUInt64ToString(value.BackwardMin.size(), result); 95 | result->append(value.BackwardMin); 96 | } 97 | 98 | static bool Decode(const string &str, ohmu::lsa::SCCNode *result) { 99 | int index = 0; 100 | uint64_t length = ohmu::lsa::readUInt64FromString(str, index); 101 | if (str.length() < index + length) 102 | return false; 103 | result->ForwardMin = str.substr(index, length); 104 | index += length; 105 | length = ohmu::lsa::readUInt64FromString(str, index); 106 | if (str.length() < index + length) 107 | return false; 108 | result->BackwardMin = str.substr(index, length); 109 | return true; 110 | } 111 | }; 112 | 113 | #endif // OHMU_LSA_EXAMPLES_SCCCOMPUTATION_H 114 | -------------------------------------------------------------------------------- /src/ohmu/examples/argument_dependencies.ohmu: -------------------------------------------------------------------------------- 1 | 2 | 3 | Array(nDim: Int) = struct { 4 | dim (i: Int): Int -> _; 5 | data(dims: List(Int)): Int -> _; 6 | 7 | elem = elemImpl(0, []); 8 | 9 | elemImpl(d: Int, dims: List(Int)) = 10 | if (d >= nDim) then 11 | (: Int -> data(dims)()) 12 | else 13 | \(i: Int) = elemImpl(d+1, append(dims, dim(i))); 14 | }; 15 | 16 | 17 | varArgs(n: Int, args: List(Int), f: Function) = 18 | if (n == 0) 19 | (: T -> f(args)) 20 | else 21 | \(i: Int) = varArgs(n-1, append(args, i)) 22 | 23 | 24 | printIntegers(n: Int) = varArgs(n, [], print) 25 | -------------------------------------------------------------------------------- /src/ohmu/test_dependent_functions.ohmu: -------------------------------------------------------------------------------- 1 | 2 | foo(i: Int): Int -> 0; 3 | 4 | bar(i: Int) -> 5 | if (i == 0) then 6 | \(): Int -> 0 7 | else 8 | \(x: Int) -> bar(i-1); 9 | 10 | 11 | testGood(): Int -> { 12 | let a = foo(0)(); 13 | let b = bar(0)(); 14 | let c = bar(1, 0)(); 15 | let d = bar(2, 0, 0)(); 16 | d; 17 | }; 18 | 19 | /* 20 | testBad0(): Int -> { 21 | let a = bar(0, 0)(); 22 | a; 23 | }; 24 | 25 | testBad1(): Int -> { 26 | let a = bar(1)(); 27 | a; 28 | }; 29 | */ 30 | -------------------------------------------------------------------------------- /src/ohmu/test_literal.ohmu: -------------------------------------------------------------------------------- 1 | 2 | 123; 3 | 0xFFFF; 4 | 5 | 123.456; 6 | 123.456e+2; 7 | 123.456e-3; 8 | 123.456E+5; 9 | 123E+6; 10 | 123e-6; 11 | 12 | "Hello!"; 13 | 14 | 'c'; 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/ohmu/test_loop.ohmu: -------------------------------------------------------------------------------- 1 | 2 | sum(n: Int): Int -> { 3 | let loop@(loop)(i: Int, total: Int): Int -> { 4 | if (i == 0) then total 5 | else loop@()(i-1, total+i)(); 6 | }; 7 | loop@()(n, 0)(); 8 | }; 9 | -------------------------------------------------------------------------------- /src/ohmu/test_module.ohmu: -------------------------------------------------------------------------------- 1 | 2 | pi = 3; // The biblical pi 3 | 4 | circle = \@self struct { 5 | radius = 10; 6 | 7 | area(): Int -> 2 * pi * radius; 8 | }; 9 | 10 | circleArea(): Int -> circle.area(); 11 | 12 | getArea(c: circle): Int -> c.area(); 13 | 14 | foo(a: Int, b: Int): Int -> a + b; 15 | 16 | bar(a: Int, b: Int): Int -> foo(a, b) * foo(b, a); -------------------------------------------------------------------------------- /src/ohmu/test_scalartypes.ohmu: -------------------------------------------------------------------------------- 1 | 2 | foo(a: Int, b: Int, c: Float, d: Double): Int -> { 3 | let x = a*a + b*b; 4 | let y = c*c + d*d; 5 | let z = x + y; 6 | let w = -x + y + z; 7 | let ans = if (x > z) then ~a else b; 8 | ans+1; 9 | }; 10 | 11 | 12 | bar(): Int -> { 13 | var a = 0; 14 | var b = 1; 15 | let c = if (a^ < b^) then a^ else b^; 16 | c; 17 | }; 18 | 19 | -------------------------------------------------------------------------------- /src/ohmu/test_ssa.ohmu: -------------------------------------------------------------------------------- 1 | 2 | sum(n: Int): Int -> { 3 | var i: Int = 0; 4 | var total: Int = 0; 5 | let loop@(loop): Int -> { 6 | if (i^ >= n) then total^ 7 | else { 8 | total := total^ + i^; 9 | i := i^ + 1; 10 | loop@()(); 11 | }; 12 | }; 13 | loop@()(); 14 | }; 15 | 16 | -------------------------------------------------------------------------------- /src/parser/ASTNode.cpp: -------------------------------------------------------------------------------- 1 | //===- ASTNode.cpp ---------------------------------------------*- C++ --*-===// 2 | // Copyright 2014 Google 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | //===----------------------------------------------------------------------===// 17 | 18 | #include "parser/ASTNode.h" 19 | 20 | 21 | namespace ohmu { 22 | namespace parsing { 23 | namespace ast { 24 | 25 | 26 | void PrettyPrinter::print(const ASTNode* node, std::ostream &ss) { 27 | switch (node->opcode()) { 28 | case ASTNode::AST_None: 29 | printNone(ss); 30 | return; 31 | case ASTNode::AST_Variable: 32 | printVariable(cast(node), ss); 33 | return; 34 | case ASTNode::AST_TokenStr: 35 | printTokenStr(cast(node), ss); 36 | return; 37 | case ASTNode::AST_Construct: 38 | printConstruct(cast(node), ss); 39 | return; 40 | case ASTNode::AST_EmptyList: 41 | printEmptyList(cast(node), ss); 42 | return; 43 | case ASTNode::AST_Append: 44 | printAppend(cast(node), ss); 45 | return; 46 | } 47 | } 48 | 49 | void PrettyPrinter::printNone(std::ostream& ss) { 50 | ss << "null"; 51 | } 52 | 53 | void PrettyPrinter::printVariable(const Variable* e, std::ostream& ss) { 54 | ss << e->name(); 55 | } 56 | 57 | void PrettyPrinter::printTokenStr(const TokenStr* e, std::ostream& ss) { 58 | ss << "\""; 59 | ss << e->string(); 60 | ss << "\""; 61 | } 62 | 63 | void PrettyPrinter::printConstruct(const Construct* e, std::ostream& ss) { 64 | ss << "(" << e->opcodeName(); 65 | for (unsigned i = 0; i < e->arity(); ++i) { 66 | ss << " "; 67 | print(e->subExpr(i), ss); 68 | } 69 | ss << ")"; 70 | } 71 | 72 | void PrettyPrinter::printEmptyList(const EmptyList* e, std::ostream& ss) { 73 | ss << "[]"; 74 | } 75 | 76 | void PrettyPrinter::printAppend(const Append* e, std::ostream& ss) { 77 | ss << "(append "; 78 | print(e->list(), ss); 79 | ss << " "; 80 | print(e->item(), ss); 81 | ss << ")"; 82 | } 83 | 84 | 85 | 86 | } // namespace ast 87 | } // namespace parsing 88 | } // namespace ohmu 89 | -------------------------------------------------------------------------------- /src/parser/BNFParser.h: -------------------------------------------------------------------------------- 1 | //===- BNFParser.h ---------------------------------------------*- C++ --*-===// 2 | // Copyright 2014 Google 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | //===----------------------------------------------------------------------===// 17 | // 18 | // BNFParser is a concrete parser. It parses grammar files in BNF form, and 19 | // will construct other parsers from them. 20 | // 21 | //===----------------------------------------------------------------------===// 22 | 23 | #include 24 | 25 | #include "parser/ASTNode.h" 26 | #include "parser/DefaultLexer.h" 27 | #include "parser/Parser.h" 28 | #include "parser/ParserBuilder.h" 29 | 30 | 31 | #ifndef OHMU_BNF_PARSER_H 32 | #define OHMU_BNF_PARSER_H 33 | 34 | namespace ohmu { 35 | namespace parsing { 36 | 37 | 38 | class BNFParser : public Parser { 39 | public: 40 | enum BNF_Opcode { 41 | // ParseRules 42 | BNF_None = 0, 43 | BNF_Token, 44 | BNF_Keyword, 45 | BNF_Sequence, 46 | BNF_Option, 47 | BNF_RecurseLeft, 48 | BNF_Reference, 49 | BNF_Action, 50 | BNF_NamedDefinition, 51 | BNF_DefinitionList, 52 | 53 | // ASTNodes 54 | BNF_Variable, 55 | BNF_TokenStr, 56 | BNF_Construct, 57 | BNF_EmptyList, 58 | BNF_Append 59 | }; 60 | 61 | enum BNF_Result { 62 | BPR_ParseRule = ParseResult::PRS_UserDefined, 63 | BPR_ASTNode 64 | }; 65 | 66 | // Main entry point. 67 | // Read grammar definition from file, and use it to initialize parser. 68 | // If trace is true, will print out debugging information. 69 | static bool initParserFromFile(Parser &parser, FILE* file, bool trace=false); 70 | 71 | public: 72 | BNFParser(Lexer *lexer) : Parser(lexer) { 73 | initMap(); 74 | } 75 | ~BNFParser() { } 76 | 77 | // Initialize opcode dictionary. (Dictionary used by lookupOpcode.) 78 | void initMap(); 79 | 80 | // Get the name of op. Used to construct opcode dictionary. 81 | const char* getOpcodeName(BNF_Opcode op); 82 | 83 | // Lookup the opcode for string s. 84 | unsigned lookupOpcode(const std::string &s) override; 85 | 86 | // Make a ParseRule or ASTNode. 87 | ParseResult makeExpr(unsigned op, unsigned arity, ParseResult *prs) override; 88 | 89 | // Create the default BNF grammar. 90 | void defineGrammar(); 91 | 92 | // The BNF parser parses a grammar definition in BNF form, 93 | // and it will add all of the definitions to the target parser. 94 | void setTarget(Parser* p) { targetParser_ = p; } 95 | 96 | public: 97 | std::unordered_map opcodeNameMap_; 98 | Parser* targetParser_ = nullptr; 99 | }; 100 | 101 | 102 | } // end namespace parser 103 | } // end namespace ohmu 104 | 105 | #endif // OHMU_BNF_PARSER_H 106 | 107 | 108 | -------------------------------------------------------------------------------- /src/parser/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | # find_package(Readline REQUIRED) 4 | 5 | add_library(parser STATIC 6 | Lexer.cpp 7 | DefaultLexer.cpp 8 | Parser.cpp 9 | ASTNode.cpp 10 | BNFParser.cpp 11 | TILParser.cpp 12 | ) 13 | 14 | target_link_libraries(parser base) 15 | 16 | if (NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") 17 | target_link_libraries(parser readline) 18 | endif() 19 | -------------------------------------------------------------------------------- /src/parser/DefaultLexer.h: -------------------------------------------------------------------------------- 1 | //===- DefaultLexer.h ------------------------------------------*- C++ --*-===// 2 | // Copyright 2014 Google 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | //===----------------------------------------------------------------------===// 17 | // 18 | // This file defines the default lexer for ohmu. Tokens consist of: 19 | // 20 | // identifers: x, y, foobar 21 | // operators: +, <<, %^&= (any sequence of symbols, excluding punctuation) 22 | // booleans: true, false 23 | // integers: 0, 324, 0x32FF 24 | // floats: 1.52, 1.2e+6 25 | // characters: 'a', 'z' 26 | // strings: "Hello World!\n" 27 | // punctuation: ( ) [ ] { } , ; : . 28 | // 29 | // Keywords overlap with identifiers and symbols, and must be registered. 30 | // 31 | //===----------------------------------------------------------------------===// 32 | 33 | 34 | #ifndef OHMU_DEFAULTLEXER_H 35 | #define OHMU_DEFAULTLEXER_H 36 | 37 | #include "base/MemRegion.h" 38 | #include "base/LLVMDependencies.h" 39 | 40 | #include "parser/Token.h" 41 | #include "parser/Lexer.h" 42 | 43 | 44 | namespace ohmu { 45 | namespace parsing { 46 | 47 | 48 | enum DefaultTokenIDs { 49 | TK_Identifier = TK_BasicTokenEnd, 50 | TK_Operator, 51 | 52 | TK_LitCharacter, 53 | TK_LitInteger, 54 | TK_LitFloat, 55 | TK_LitString, 56 | 57 | TK_LParen, 58 | TK_RParen, 59 | TK_LCurlyBrace, 60 | TK_RCurlyBrace, 61 | TK_LSquareBrace, 62 | TK_RSquareBrace, 63 | TK_Comma, 64 | TK_Semicolon, 65 | TK_Colon, 66 | TK_Period, 67 | 68 | TK_BeginKeywordIDs 69 | }; 70 | 71 | 72 | class DefaultLexer : public Lexer { 73 | public: 74 | DefaultLexer() : interactive_(false) { 75 | setKeywordStartID(TK_BeginKeywordIDs); 76 | stringArena_.setRegion(&stringRegion_); 77 | } 78 | DefaultLexer(const DefaultLexer& l) = delete; 79 | 80 | void readNewline(char c); 81 | 82 | void readIdentifier(char startChar); 83 | void readInteger (char startChar); 84 | void readHexInteger(); 85 | void readOperator (char startChar); 86 | 87 | void readLineComment(); 88 | bool readEscapeCharacter(char c); 89 | bool readString(); 90 | bool readCharacter(); 91 | bool readFloatExp(char startChar); 92 | 93 | StringRef copyStr(StringRef s) { 94 | char* mem = static_cast(stringArena_.allocate(s.size()+1)); 95 | return copyStringRef(mem, s); 96 | } 97 | 98 | virtual const char* getTokenIDString(unsigned tid); 99 | virtual unsigned registerKeyword(const std::string& s); 100 | 101 | virtual Token readToken(); 102 | 103 | inline bool isInteractive() const { return interactive_; } 104 | inline void setInteractive(bool b) { interactive_ = b; } 105 | 106 | private: 107 | MemRegion stringRegion_; // Region to allocate all token strings 108 | MemRegionRef stringArena_; 109 | 110 | bool interactive_; 111 | }; 112 | 113 | } // end namespace parsing 114 | 115 | } // end namespace ohmu 116 | 117 | #endif 118 | -------------------------------------------------------------------------------- /src/parser/Lexer.cpp: -------------------------------------------------------------------------------- 1 | //===- Lexer.cpp -----------------------------------------------*- C++ --*-===// 2 | // Copyright 2014 Google 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | //===----------------------------------------------------------------------===// 17 | 18 | #include 19 | #include 20 | 21 | #ifndef _MSC_VER 22 | #include 23 | #include 24 | #endif 25 | 26 | #include "parser/Lexer.h" 27 | 28 | namespace ohmu { 29 | 30 | namespace parsing { 31 | 32 | unsigned FileStream::fillBuffer(char* buf, unsigned size) { 33 | size_t n = fread(buf, 1, size, file_); 34 | return static_cast(n); 35 | } 36 | 37 | unsigned StringStream::fillBuffer(char* buf, unsigned size) { 38 | if (str_[0] == 0) 39 | return 0; 40 | unsigned i = 0; 41 | while (i < size && str_[0] != 0) { 42 | buf[i] = str_[0]; 43 | ++i; 44 | ++str_; 45 | } 46 | return i; 47 | } 48 | 49 | 50 | #ifndef _MSC_VER 51 | bool InteractiveStream::readlineLibraryInitialized_ = false; 52 | 53 | InteractiveStream::InteractiveStream(const char* p1, const char* p2) 54 | : prompt1_(p1), prompt2_(p2), firstLine_(true) { 55 | if (!readlineLibraryInitialized_) { 56 | // Do nothing for now... 57 | readlineLibraryInitialized_ = true; 58 | } 59 | } 60 | 61 | 62 | unsigned InteractiveStream::fillBuffer(char* buf, unsigned size) { 63 | const char* p = firstLine_ ? prompt1_ : prompt2_; 64 | 65 | char* line = readline(p); 66 | if (line == 0) 67 | return 0; 68 | 69 | unsigned i = 0; 70 | while (i < size && line[i] != 0) { 71 | buf[i] = line[i]; 72 | ++i; 73 | } 74 | if (i+1 < size) { 75 | buf[i] = '\n'; // add on a newline. 76 | buf[i+1] = ' '; // add whitespace to satisfy lexer lookahead. 77 | i += 2; 78 | } 79 | free(line); 80 | firstLine_ = false; 81 | return i; 82 | } 83 | #endif 84 | 85 | 86 | // Look up the token id for the token named s. 87 | unsigned Lexer::lookupTokenID(const std::string& s) { 88 | // initialize token dictionary on first call 89 | if (tokenList_.size() == 0) { 90 | for (unsigned i=0,n=getKeywordStartID(); isecond; 100 | } 101 | 102 | 103 | unsigned Lexer::registerKeyword(const std::string& s) { 104 | KeywordDict::iterator it = keyDict_.find(s); 105 | if (it == keyDict_.end()) { 106 | unsigned sz = keyList_.size(); 107 | keyList_.push_back(s); // map from unsigned to string 108 | keyDict_[s] = sz; // map from string to unsigned 109 | return sz + startKeywordTokenID_; 110 | } 111 | return it->second + startKeywordTokenID_; 112 | } 113 | 114 | 115 | void Lexer::signalLexicalError() { 116 | char c = lookChar(); 117 | std::cerr << "Lexical error: unknown character "; 118 | if (c) std::cerr << "'" << c << "'"; 119 | else std::cerr << 0; 120 | lexical_error = true; 121 | } 122 | 123 | 124 | // Tell the lexer that a close brace has been seen. The id should 125 | // be that of the corresponding open brace. 126 | bool Lexer::signalCloseBrace(unsigned short tokid) { 127 | if (braces_.size() > 0 && braces_.back() == tokid) { 128 | braces_.pop_back(); 129 | return true; 130 | } 131 | // attempt to recover by popping extra braces off the stack 132 | while (braces_.size() > 0 && braces_.back() != tokid) 133 | braces_.pop_back(); 134 | if (braces_.size() > 0) 135 | braces_.pop_back(); // pop off the given brace 136 | return false; 137 | } 138 | 139 | 140 | void Lexer::readTokens(unsigned numTokens) { 141 | unsigned i = 0; 142 | for (; i < numTokens; ++i) { 143 | if (stream_eof_ || lexical_error) 144 | break; 145 | lookAhead_.push_back(readToken()); 146 | } 147 | 148 | // push extra EOF tokens onto the end if necessary to enable 149 | // unlimited lookahead. 150 | for (; i < numTokens; ++i) { 151 | lookAhead_.push_back(eofToken_); 152 | } 153 | } 154 | 155 | 156 | void Lexer::fillBuffer(unsigned numChars) { 157 | unsigned bsize = bufferSize(); 158 | 159 | if (bufferPos_ > 0) { 160 | // Move unread characters to begining of buffer. 161 | // There should only be a few. 162 | if (bufferPos_ > bsize) { 163 | memcpy(buffer_, buffer_ + bufferPos_, bsize); 164 | } 165 | else { 166 | // regions overlap -- move the data byte by byte. 167 | char* p = buffer_ + bufferPos_; 168 | for (unsigned i = 0; i < bsize; ++i, ++p) 169 | buffer_[i] = *p; 170 | } 171 | 172 | bufferPos_ = 0; 173 | bufferLen_ = bsize; 174 | } 175 | 176 | // Sanity check. 177 | if (numChars + bufferLen_ > bufferCapacity_-1) 178 | numChars = bufferCapacity_ - bufferLen_ - 1; 179 | 180 | unsigned read = 0; 181 | while (read < numChars && !stream_eof_) { 182 | unsigned nread = 183 | charStream_->fillBuffer(buffer_ + bufferLen_, 184 | bufferCapacity_ - bufferLen_); 185 | if (nread == 0) { 186 | stream_eof_ = true; 187 | break; 188 | } 189 | 190 | read += nread; 191 | bufferLen_ += nread; 192 | } 193 | 194 | // 0-pad any requested lookahead past end of file 195 | while (read < numChars) { 196 | buffer_[bufferLen_] = 0; 197 | ++bufferLen_; 198 | ++read; 199 | } 200 | } 201 | 202 | } // end namespace parsing 203 | 204 | } // end namespace ohmu 205 | -------------------------------------------------------------------------------- /src/parser/TILParser.h: -------------------------------------------------------------------------------- 1 | //===- TILParser.h ---------------------------------------------*- C++ --*-===// 2 | // Copyright 2014 Google 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | //===----------------------------------------------------------------------===// 17 | // 18 | // TILParser is a concrete parser, which constructs TIL expressions. 19 | // The TIL grammar is read from an external grammar file. 20 | // 21 | //===----------------------------------------------------------------------===// 22 | 23 | #ifndef OHMU_TIL_PARSER_H 24 | #define OHMU_TIL_PARSER_H 25 | 26 | #include "parser/Parser.h" 27 | #include "parser/BNFParser.h" 28 | #include "til/TIL.h" 29 | 30 | 31 | namespace ohmu { 32 | namespace parsing { 33 | 34 | using namespace ohmu::til; 35 | 36 | 37 | class TILParser : public Parser { 38 | public: 39 | 40 | // The set of opcodes that are allowed to appear in astNode constructors. 41 | // This mostly mirrors TIL_Opcode, but there are some differences, e 42 | // especially with regard to literals and variables. 43 | enum TIL_ConstructOp { 44 | TCOP_LitNull, 45 | TCOP_LitBool, 46 | TCOP_LitChar, 47 | TCOP_LitInteger, 48 | TCOP_LitFloat, 49 | TCOP_LitString, 50 | 51 | TCOP_Identifier, 52 | TCOP_Function, 53 | TCOP_SFunction, 54 | TCOP_Code, 55 | TCOP_Field, 56 | TCOP_Record, 57 | TCOP_Slot, 58 | TCOP_Array, 59 | 60 | TCOP_Apply, 61 | TCOP_SApply, 62 | TCOP_Project, 63 | TCOP_Call, 64 | 65 | TCOP_Alloc, 66 | TCOP_Load, 67 | TCOP_Store, 68 | TCOP_ArrayIndex, 69 | TCOP_ArrayAdd, 70 | 71 | TCOP_UnaryOp, 72 | TCOP_BinaryOp, 73 | TCOP_Cast, 74 | 75 | TCOP_Let, 76 | TCOP_If 77 | }; 78 | 79 | static const unsigned short TCOP_MAX = TCOP_If; 80 | 81 | // All parse rules return SExprs. 82 | static const unsigned short TILP_SExpr = ParseResult::PRS_UserDefined; 83 | 84 | 85 | TILParser(Lexer *lexer) : Parser(lexer) { 86 | initMap(); 87 | } 88 | ~TILParser() { } 89 | 90 | MemRegionRef arena() { return arena_; } 91 | 92 | void setArenas(MemRegionRef strArena, MemRegionRef parseArena) { 93 | arena_ = parseArena; 94 | stringArena_ = strArena; 95 | } 96 | 97 | const char* getOpcodeName(TIL_ConstructOp op); 98 | 99 | void initMap(); 100 | 101 | StringRef copyStr (StringRef s); 102 | bool toBool (StringRef s); 103 | char toChar (StringRef s); 104 | int toInteger(StringRef s); 105 | double toDouble (StringRef s); 106 | StringRef toString (StringRef s); 107 | 108 | unsigned lookupOpcode(const std::string &s) override; 109 | 110 | TIL_UnaryOpcode lookupUnaryOpcode(StringRef s); 111 | TIL_BinaryOpcode lookupBinaryOpcode(StringRef s); 112 | TIL_CastOpcode lookupCastOpcode(StringRef s); 113 | 114 | ParseResult makeExpr(unsigned op, unsigned arity, ParseResult *prs) override; 115 | 116 | private: 117 | MemRegionRef arena_; 118 | MemRegionRef stringArena_; 119 | 120 | std::unordered_map opcodeMap_; 121 | std::unordered_map unaryOpcodeMap_; 122 | std::unordered_map binaryOpcodeMap_; 123 | std::unordered_map castOpcodeMap_; 124 | }; 125 | 126 | 127 | } // end namespace parsing 128 | } // end namespace ohmu 129 | 130 | #endif // OHMU_TIL_PARSER_H 131 | -------------------------------------------------------------------------------- /src/parser/Token.h: -------------------------------------------------------------------------------- 1 | //===- Token.h -------------------------------------------------*- C++ --*-===// 2 | // Copyright 2014 Google 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | //===----------------------------------------------------------------------===// 17 | // 18 | // Defines classes for dealing with source files and tokens. 19 | // 20 | // class SourceLocation: a location within a source file. 21 | // class Token: consists of a token id, the parsed string, and a SourceLocation. 22 | // class TokenSet: a set of token ids. Used in parsing. 23 | // 24 | //===----------------------------------------------------------------------===// 25 | 26 | 27 | #ifndef OHMU_TOKEN_H 28 | #define OHMU_TOKEN_H 29 | 30 | #include "base/LLVMDependencies.h" 31 | 32 | namespace ohmu { 33 | 34 | namespace parsing { 35 | 36 | enum BasicTokenID { 37 | TK_None = 0, 38 | TK_EOF, 39 | TK_Error, 40 | TK_Newline, 41 | TK_Whitespace, 42 | TK_Comment, 43 | TK_BasicTokenEnd 44 | }; 45 | 46 | 47 | struct SourceLocation { 48 | unsigned lineNum; 49 | unsigned short linePos; 50 | unsigned short fileIndex; 51 | 52 | SourceLocation() 53 | : lineNum(0), linePos(0), fileIndex(0) 54 | { } 55 | SourceLocation(unsigned ln, unsigned short lp, unsigned short fi=0) 56 | : lineNum(ln), linePos(lp), fileIndex(fi) 57 | { } 58 | }; 59 | 60 | 61 | class Token { 62 | public: 63 | Token() 64 | : tokenID_(TK_EOF), tokenStr_("") 65 | { } 66 | Token(const Token& tok) 67 | : tokenID_(tok.tokenID_), tokenStr_(tok.tokenStr_), 68 | sourceLoc_(tok.sourceLoc_) 69 | { } 70 | Token(unsigned short tid) 71 | : tokenID_(tid), tokenStr_(""), sourceLoc_(SourceLocation()) 72 | { } 73 | Token(unsigned short tid, const char* s, const SourceLocation& loc) 74 | : tokenID_(tid), tokenStr_(StringRef(s)), sourceLoc_(SourceLocation()) 75 | { } 76 | Token(unsigned short tid, StringRef s, const SourceLocation& loc) 77 | : tokenID_(tid), tokenStr_(s), sourceLoc_(loc) 78 | { } 79 | 80 | unsigned id() const { return tokenID_; } 81 | unsigned length() const { return tokenStr_.size(); } 82 | StringRef string() const { return tokenStr_; } 83 | SourceLocation location() const { return sourceLoc_; } 84 | 85 | std::string cppString() const { 86 | return std::string(tokenStr_.c_str(), tokenStr_.size()); 87 | } 88 | 89 | const char* c_str() const { return tokenStr_.c_str(); } 90 | 91 | private: 92 | unsigned short tokenID_; 93 | StringRef tokenStr_; 94 | SourceLocation sourceLoc_; 95 | }; 96 | 97 | 98 | class TokenSet { 99 | public: 100 | bool get(int i) const { 101 | unsigned idx = i/sizeof(unsigned); 102 | unsigned rem = i - idx*sizeof(unsigned); 103 | return (bits_[idx] >> rem) & 0x01; 104 | } 105 | 106 | void set(int i) { 107 | unsigned idx = i/sizeof(unsigned); 108 | unsigned rem = i - idx*sizeof(unsigned); 109 | bits_[idx] |= 0x01 << rem; 110 | } 111 | 112 | static void makeZero(TokenSet& tset) { 113 | for (unsigned i = 0; i < maxSize; ++i) tset.bits_[i] = 0; 114 | } 115 | 116 | static void makeUnion(TokenSet& set1, TokenSet& set2, 117 | TokenSet& result) 118 | { 119 | for (unsigned i = 0; i < maxSize; ++i) 120 | result.bits_[i] = set1.bits_[i] | set2.bits_[i]; 121 | } 122 | 123 | private: 124 | static const unsigned maxSize = 16; 125 | 126 | unsigned bits_[maxSize]; // 16*32 token types should be enough... 127 | }; 128 | 129 | 130 | } // end namespace lexing 131 | } // end namespace ohmu 132 | 133 | #endif 134 | 135 | -------------------------------------------------------------------------------- /src/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | add_subdirectory(backend) 4 | add_subdirectory(base) 5 | add_subdirectory(lsa) 6 | add_subdirectory(parser) 7 | add_subdirectory(til) 8 | -------------------------------------------------------------------------------- /src/test/Driver.h: -------------------------------------------------------------------------------- 1 | //===- Driver.h ------------------------------------------------*- C++ --*-===// 2 | // Copyright 2014 Google 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | //===----------------------------------------------------------------------===// 17 | // 18 | // Driver provides a simple harness for parsing and compiling an ohmu program 19 | // which is shared between test cases. 20 | // 21 | //===----------------------------------------------------------------------===// 22 | 23 | 24 | #ifndef OHMU_TEST_DRIVER_H 25 | #define OHMU_TEST_DRIVER_H 26 | 27 | 28 | #include "parser/DefaultLexer.h" 29 | #include "parser/BNFParser.h" 30 | #include "parser/TILParser.h" 31 | #include "til/Global.h" 32 | #include "til/TIL.h" 33 | #include "til/TILCompare.h" 34 | #include "til/TILPrettyPrint.h" 35 | #include "til/TILTraverse.h" 36 | 37 | 38 | #include 39 | 40 | namespace ohmu { 41 | 42 | using namespace ohmu::parsing; 43 | using namespace ohmu::til; 44 | 45 | 46 | class Driver { 47 | public: 48 | bool initParser(FILE* grammarFile); 49 | bool initParser(const char* grammarFileName); 50 | 51 | bool parseDefinitions(Global *global, CharStream &stream); 52 | bool parseDefinitions(Global *global, FILE *file); 53 | bool parseDefinitions(Global *global, const char* fname); 54 | 55 | Driver() : tilParser(&lexer), startRule(nullptr) { } 56 | 57 | private: 58 | DefaultLexer lexer; 59 | TILParser tilParser; 60 | ParseNamedDefinition* startRule; 61 | }; 62 | 63 | 64 | bool Driver::initParser(FILE* grammarFile) { 65 | // Build the ohmu parser from the grammar file. 66 | bool success = BNFParser::initParserFromFile(tilParser, grammarFile, false); 67 | if (!success) 68 | return false; 69 | 70 | // Find the starting point. 71 | startRule = tilParser.findDefinition("definitions"); 72 | if (!startRule) { 73 | std::cout << "Grammar does not contain rule named 'definitions'.\n"; 74 | return false; 75 | } 76 | return true; 77 | } 78 | 79 | 80 | bool Driver::initParser(const char* grammarFileName) { 81 | // Open the grammar file. 82 | FILE* grammarFile = fopen(grammarFileName, "r"); 83 | if (!grammarFile) { 84 | std::cout << "File " << grammarFileName << " not found.\n"; 85 | return false; 86 | } 87 | bool success = initParser(grammarFile); 88 | fclose(grammarFile); 89 | return success; 90 | } 91 | 92 | 93 | 94 | 95 | bool Driver::parseDefinitions(Global *global, CharStream &stream) { 96 | tilParser.setArenas(global->StringArena, global->ParseArena); 97 | lexer.setStream(&stream); 98 | // tilParser.setTrace(true); 99 | ParseResult result = tilParser.parse(startRule); 100 | if (tilParser.parseError()) 101 | return false; 102 | 103 | // Add parsed definitions to global namespace. 104 | auto* v = result.getList(TILParser::TILP_SExpr); 105 | if (!v) { 106 | std::cout << "No definitions found.\n"; 107 | return false; 108 | } 109 | global->addDefinitions(*v); 110 | delete v; 111 | return true; 112 | } 113 | 114 | bool Driver::parseDefinitions(Global *global, FILE *file) { 115 | // Parse file. 116 | FileStream fs(file); 117 | return parseDefinitions(global, fs); 118 | } 119 | 120 | 121 | bool Driver::parseDefinitions(Global *global, const char* fname) { 122 | FILE* file = fopen(fname, "r"); 123 | if (!file) { 124 | std::cout << "File " << fname << " not found.\n"; 125 | return false; 126 | } 127 | bool success = parseDefinitions(global, file); 128 | fclose(file); 129 | return success; 130 | } 131 | 132 | } // end namespace ohmu 133 | 134 | 135 | #endif // OHMU_TEST_DRIVER_H 136 | -------------------------------------------------------------------------------- /src/test/backend/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #add_executable(test_llvm test_llvm.cpp) 2 | #target_link_libraries(test_llvm base parser til backend_llvm ${llvm_libs}) 3 | #add_dependencies(test_llvm ohmu_grammar) -------------------------------------------------------------------------------- /src/test/backend/test_llvm.cpp: -------------------------------------------------------------------------------- 1 | //===- test_TILParser.cpp --------------------------------------*- C++ --*-===// 2 | // Copyright 2014 Google 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | //===----------------------------------------------------------------------===// 17 | 18 | #include "backend/llvm/IRGen.h" 19 | #include "clang/Analysis/Analyses/ThreadSafetyTIL.h" 20 | #include "clang/Analysis/Analyses/ThreadSafetyTraverse.h" 21 | #include "parser/DefaultLexer.h" 22 | #include "parser/BNFParser.h" 23 | #include "parser/TILParser.h" 24 | #include "til/CFGReducer.h" 25 | 26 | #include 27 | 28 | using namespace ohmu; 29 | using namespace ohmu::parsing; 30 | using namespace clang::threadSafety; 31 | 32 | class TILPrinter : public til::PrettyPrinter { 33 | public: 34 | TILPrinter() : PrettyPrinter(false, false) { } 35 | }; 36 | 37 | void printSExpr(til::SExpr* e) { 38 | TILPrinter::print(e, std::cout); 39 | } 40 | 41 | 42 | int main(int argc, const char** argv) { 43 | DefaultLexer lexer; 44 | TILParser tilParser(&lexer); 45 | 46 | 47 | const char* grammarFileName = "src/grammar/ohmu.grammar"; 48 | FILE* file = fopen(grammarFileName, "r"); 49 | if (!file) { 50 | std::cout << "File " << grammarFileName << " not found.\n"; 51 | return -1; 52 | } 53 | 54 | bool success = BNFParser::initParserFromFile(tilParser, file, false); 55 | std::cout << "\n"; 56 | // if (success) 57 | // tilParser.printSyntax(std::cout); 58 | 59 | fclose(file); 60 | 61 | if (argc == 0) 62 | return 0; 63 | 64 | // Read the ohmu file. 65 | auto *startRule = tilParser.findDefinition("definitions"); 66 | if (!startRule) { 67 | std::cout << "Grammar does not contain rule named 'definitions'.\n"; 68 | return -1; 69 | } 70 | 71 | file = fopen(argv[1], "r"); 72 | if (!file) { 73 | std::cout << "File " << argv[1] << " not found.\n"; 74 | return -1; 75 | } 76 | 77 | std::cout << "\nParsing " << argv[1] << "...\n"; 78 | FileStream fs(file); 79 | lexer.setStream(&fs); 80 | // tilParser.setTrace(true); 81 | ParseResult result = tilParser.parse(startRule); 82 | if (tilParser.parseError()) 83 | return -1; 84 | 85 | // Pretty print the parsed ohmu code. 86 | auto* v = result.getList(TILParser::TILP_SExpr); 87 | if (!v) { 88 | std::cout << "No definitions found.\n"; 89 | return 0; 90 | } 91 | 92 | for (SExpr* e : *v) { 93 | std::cout << "\nDefinition:\n"; 94 | printSExpr(e); 95 | std::cout << "\nCFG:\n"; 96 | SExpr* e2 = CFGReducer::lower(e, tilParser.arena()); 97 | printSExpr(e2); 98 | 99 | //backend_llvm::generate_LLVM_IR(cfg); 100 | } 101 | 102 | delete v; 103 | 104 | std::cout << "\n"; 105 | return 0; 106 | } 107 | 108 | -------------------------------------------------------------------------------- /src/test/base/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(test_base test_base.cpp) 2 | target_link_libraries(test_base base) -------------------------------------------------------------------------------- /src/test/base/test_base.cpp: -------------------------------------------------------------------------------- 1 | //===- test_base.cpp -------------------------------------------*- C++ --*-===// 2 | // Copyright 2014 Google 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | //===----------------------------------------------------------------------===// 17 | // 18 | // Test code for classes in base. 19 | // 20 | //===----------------------------------------------------------------------===// 21 | 22 | #include "base/LLVMDependencies.h" 23 | #include "base/MemRegion.h" 24 | #include "base/ArrayTree.h" 25 | 26 | #include 27 | 28 | using namespace ohmu; 29 | 30 | 31 | void error(const char* msg) { 32 | std::cerr << msg; 33 | assert(false && "Test failed."); 34 | } 35 | 36 | 37 | class UnMoveableItem { 38 | public: 39 | UnMoveableItem() : uniqueHandle(0) { } 40 | UnMoveableItem(unsigned h) : uniqueHandle(h) { } 41 | ~UnMoveableItem() { uniqueHandle = 0; } 42 | 43 | unsigned getHandle() { return uniqueHandle; } 44 | 45 | private: 46 | UnMoveableItem(const UnMoveableItem& i) = delete; 47 | UnMoveableItem(UnMoveableItem&& i) = delete; 48 | 49 | void operator=(const UnMoveableItem& i) = delete; 50 | void operator=(UnMoveableItem&& i) = delete; 51 | 52 | private: 53 | unsigned uniqueHandle; 54 | }; 55 | 56 | 57 | void testTreeArray() { 58 | MemRegion region; 59 | MemRegionRef arena(®ion); 60 | ArrayTree atree; 61 | std::vector items; 62 | 63 | unsigned i = 0; 64 | unsigned n = 1024; 65 | 66 | for (i = 0; i < n; ++i) { 67 | atree.emplace_back(arena, i); 68 | items.push_back(&atree.back()); 69 | } 70 | 71 | for (i = 0; i < n; ++i) { 72 | // std::cerr << i << ","; 73 | if (atree[i].getHandle() != i) 74 | error("Error: ArrayTree construction failed.\n"); 75 | } 76 | 77 | i = 0; 78 | for (auto& H : atree) { 79 | // std::cerr << i << ","; 80 | if (H.getHandle() != i) 81 | error("Error: ArrayTree iterator failed.\n"); 82 | ++i; 83 | } 84 | if (i != n) 85 | error("Error: ArrayTree iteration failed.\n"); 86 | 87 | i = n; 88 | for (auto & H : atree.reverse()) { 89 | if (H.getHandle() != i-1) 90 | error("Error: ArrayTree reverse iterator failed.\n"); 91 | --i; 92 | } 93 | if (i != 0) 94 | error("Error: ArrayTree reverse iteration failed.\n"); 95 | 96 | unsigned n2 = n + 2713; 97 | atree.resize(arena, n2, 42); 98 | for (i = n; i < n2; ++i) { 99 | if (atree[i].getHandle() != 42) 100 | error("Error: ArrayTree construction failed.\n"); 101 | } 102 | 103 | unsigned n3 = n*4; 104 | atree.resize(arena, n3, 43); 105 | for (i = n2; i < n3; ++i) { 106 | if (atree[i].getHandle() != 43) 107 | error("Error: ArrayTree construction failed.\n"); 108 | } 109 | 110 | atree.clear(); 111 | for (auto *H : items) { 112 | if (H->getHandle() != 0) 113 | error("Error: ArrayTree clear failed.\n"); 114 | } 115 | } 116 | 117 | 118 | 119 | int main(int argc, char** argv) { 120 | testTreeArray(); 121 | return 0; 122 | } 123 | 124 | -------------------------------------------------------------------------------- /src/test/lsa/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ## Call graph generation 2 | 3 | add_executable(generate_callgraph 4 | generate_callgraph.cpp 5 | test_input_callgraph.cpp 6 | ) 7 | target_link_libraries(generate_callgraph 8 | lsa 9 | ) 10 | 11 | ## Analyses 12 | 13 | add_executable(scc_analysis 14 | scc_analysis.cpp 15 | test_input_scc.cpp 16 | ) 17 | target_link_libraries(scc_analysis 18 | lsa_example_scc 19 | ) 20 | 21 | add_executable(globals_analysis 22 | globals_analysis.cpp 23 | test_input_global_vars.cpp 24 | ) 25 | target_link_libraries(globals_analysis 26 | ohmuTil LLVMOption 27 | ) 28 | 29 | add_executable(escape_analysis 30 | escape_analysis.cpp 31 | test_input_escape.cpp 32 | ) 33 | target_link_libraries(escape_analysis 34 | lsa_example_escape 35 | ) 36 | 37 | 38 | ## Helper command 39 | 40 | add_custom_target(run_test_lsa) 41 | add_custom_command( 42 | TARGET run_test_lsa 43 | DEPENDS run_test_lsa.sh 44 | COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/run_test_lsa.sh ${CMAKE_CURRENT_BINARY_DIR}/run_test_lsa.sh) 45 | -------------------------------------------------------------------------------- /src/test/lsa/escape_analysis.cpp: -------------------------------------------------------------------------------- 1 | //===- escape_analysis.cpp -------------------------------------*- C++ --*-===// 2 | // 3 | //===----------------------------------------------------------------------===// 4 | 5 | #include "lsa/examples/EscapeAnalysis.h" 6 | #include "lsa/StandaloneRunner.h" 7 | 8 | int main(int argc, const char *argv[]) { 9 | 10 | ohmu::lsa::StandaloneRunner Runner(argc, argv); 11 | 12 | Runner.readCallGraph(); 13 | Runner.runComputation(); 14 | Runner.printComputationResult(true); 15 | 16 | return 0; 17 | } 18 | -------------------------------------------------------------------------------- /src/test/lsa/generate_callgraph.cpp: -------------------------------------------------------------------------------- 1 | //===- generate_callgraph.cpp ----------------------------------*- C++ --*-===// 2 | // Simple program that generates and prints the call graph and OhmuIR of a 3 | // single translation unit. 4 | //===----------------------------------------------------------------------===// 5 | 6 | #include 7 | 8 | #include "clang/Tooling/CommonOptionsParser.h" 9 | #include "clang/Tooling/Tooling.h" 10 | #include "llvm/Support/CommandLine.h" 11 | #include "lsa/BuildCallGraph.h" 12 | #include "lsa/GraphSerializer.h" 13 | 14 | static llvm::cl::opt 15 | OutputFile("o", llvm::cl::desc("Specify output file"), 16 | llvm::cl::value_desc("file"), llvm::cl::Optional); 17 | 18 | int main(int argc, const char *argv[]) { 19 | 20 | clang::tooling::CommonOptionsParser OptParser(argc, argv, 21 | llvm::cl::GeneralCategory); 22 | ohmu::lsa::DefaultCallGraphBuilder CallGraphBuilder; 23 | clang::ast_matchers::MatchFinder Finder; 24 | ohmu::lsa::CallGraphBuilderTool BuilderTool; 25 | BuilderTool.RegisterMatchers(CallGraphBuilder, &Finder); 26 | 27 | clang::tooling::ClangTool Tool(OptParser.getCompilations(), 28 | OptParser.getSourcePathList()); 29 | 30 | int Res = Tool.run(clang::tooling::newFrontendActionFactory(&Finder).get()); 31 | if (Res != 0) 32 | return Res; 33 | 34 | if (OutputFile.getNumOccurrences() > 0) { 35 | ohmu::lsa::GraphSerializer::write(OutputFile.getValue(), &CallGraphBuilder); 36 | } else { 37 | CallGraphBuilder.Print(std::cout); 38 | } 39 | 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /src/test/lsa/globals_analysis.cpp: -------------------------------------------------------------------------------- 1 | //===- test_lsa_global_vars.cpp --------------------------------*- C++ --*-===// 2 | // Runs the LSA call graph generation on a clang compiled file and computes 3 | // which functions modify global variables. 4 | //===----------------------------------------------------------------------===// 5 | 6 | #include "lsa/examples/ExampleOhmuComputation.h" 7 | #include "lsa/StandaloneRunner.h" 8 | 9 | int main(int argc, const char *argv[]) { 10 | 11 | ohmu::lsa::StandaloneRunner Runner(argc, argv); 12 | 13 | Runner.readCallGraph(); 14 | Runner.runComputation(); 15 | Runner.printComputationResult(); 16 | 17 | return 0; 18 | } 19 | -------------------------------------------------------------------------------- /src/test/lsa/run_test_lsa.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Assisting script to call a clang-tool binary with the right arguments to the 4 | # Clang include headers. Requires that the environment variable LLVM_BUILD is 5 | # set to the LLVM build directory that includes Clang. 6 | # 7 | # Call this script as: 8 | # ./run_test_lsa.sh 9 | # 10 | BPATH=${LLVM_BUILD:?"Specify the absolute path to the LLVM build directory as the environment variable LLVM_BUILD."} 11 | VERSION=`ls $BPATH/lib/clang | sort -n | tail -1` 12 | COMMAND=$1 13 | shift 1 14 | "$COMMAND" \ 15 | -extra-arg=-I \ 16 | -extra-arg=$BPATH/lib/clang/$VERSION/include \ 17 | $@ 18 | 19 | -------------------------------------------------------------------------------- /src/test/lsa/scc_analysis.cpp: -------------------------------------------------------------------------------- 1 | //===- test_lsa_scc.cpp ----------------------------------------*- C++ --*-===// 2 | // Runs the LSA call graph generation on a clang compiled file. To call this 3 | // binary, use run_test_lsa.sh which sets the right include path for clang 4 | // libraries. The clang tool requires the json compilation database to be 5 | // present in the directory of the specified source file or a parent directory 6 | // of the source file. Alternatively, you can specify the path to the folder 7 | // containing the database with -p (eg: "-p=."). 8 | // 9 | // To create the database, specify "-DCMAKE_EXPORT_COMPILE_COMMANDS=ON" when 10 | // running CMake, or "-t compdb" when running ninja. 11 | // 12 | // In addition, since clang requires some substitute includes for common system 13 | // headers, use the exported shell script for a more convenient call: 14 | // $ export LLVM_BUILD=/path/to/llvm/with/clang/build/ 15 | // $ ./src/test/run_test_lsa.sh -p=. 16 | //===----------------------------------------------------------------------===// 17 | 18 | #include "lsa/examples/SCCComputation.h" 19 | #include "lsa/StandaloneRunner.h" 20 | 21 | int main(int argc, const char *argv[]) { 22 | 23 | ohmu::lsa::StandaloneRunner Runner(argc, argv); 24 | 25 | Runner.readCallGraph(); 26 | Runner.runComputation(); 27 | Runner.printComputationResult(); 28 | 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /src/test/lsa/test_input_callgraph.cpp: -------------------------------------------------------------------------------- 1 | /// basics 2 | 3 | void globalFunction() { } 4 | 5 | void globalFunctionWithArg(int i) { } 6 | 7 | void callingGlobalFunction() { 8 | globalFunction(); 9 | } 10 | 11 | void callingGlobalFunctionWithArg() { 12 | globalFunctionWithArg(3); 13 | } 14 | 15 | void multipleCalls() { 16 | globalFunction(); 17 | globalFunction(); 18 | globalFunctionWithArg(15); 19 | globalFunctionWithArg(100); 20 | } 21 | 22 | /// basics classes 23 | class B { 24 | public: 25 | void memberFunctionWithOneArg(int x) { } 26 | }; 27 | 28 | void callingMemberFunctionWithOneArg() { 29 | B b; 30 | b.memberFunctionWithOneArg(15); 31 | } 32 | 33 | class A { 34 | public: 35 | void bar() { globalFunction(); } 36 | }; 37 | 38 | void callingViaPointer(A* a) { 39 | a->bar(); 40 | } 41 | 42 | /// templated functions 43 | 44 | template 45 | void templatedFunction(T t) { 46 | globalFunction(); 47 | } 48 | 49 | void callingTemplatedFunction() { 50 | templatedFunction(false); 51 | templatedFunction(3); 52 | } 53 | 54 | template 55 | void templatedFunctionCalling(T t) { 56 | t.memberFunctionWithOneArg(13); 57 | } 58 | 59 | void callingTemplatedFunctionCalling() { 60 | B b; 61 | templatedFunctionCalling(b); 62 | } 63 | 64 | /// Specializations 65 | template 66 | void specializeMe(T t) { 67 | globalFunction(); 68 | } 69 | 70 | template <> 71 | void specializeMe(int x) { 72 | globalFunctionWithArg(x); 73 | } 74 | 75 | void callSpecialInt() { 76 | specializeMe(13); 77 | } 78 | 79 | 80 | /// templated classes 81 | 82 | template 83 | class TemplatedClass { 84 | public: 85 | T* getResultT() { globalFunction(); return 0; } 86 | }; 87 | 88 | void callingTemplatedClass() { 89 | TemplatedClass Tbool; 90 | Tbool.getResultT(); 91 | TemplatedClass Tint; 92 | Tint.getResultT(); 93 | } 94 | 95 | template 96 | class CRTP { 97 | public: 98 | Self *self() { return static_cast(this); } 99 | 100 | void CRTPFunction() { 101 | Self* s = self(); 102 | s->semiVirtualFunction(); 103 | } 104 | }; 105 | 106 | class InstanceCRTP : public CRTP { 107 | public: 108 | void semiVirtualFunction() { 109 | globalFunction(); 110 | } 111 | }; 112 | 113 | void callingInstanceCRTP(InstanceCRTP C) { 114 | C.CRTPFunction(); 115 | } 116 | 117 | class C { 118 | public: 119 | void end() { 120 | globalFunction(); 121 | } 122 | ~C() { 123 | end(); 124 | } 125 | }; 126 | 127 | void constructDestruct() { 128 | C c; 129 | } 130 | 131 | template 132 | class X { 133 | public: 134 | void x(); 135 | private: 136 | int * _m; 137 | }; 138 | 139 | template 140 | void X<_T>::x() { 141 | delete this->_m; 142 | } 143 | 144 | /// overwriting virtual 145 | 146 | /// dynamic dispatch on arguments 147 | 148 | /// different return types? 149 | 150 | -------------------------------------------------------------------------------- /src/test/lsa/test_input_escape.cpp: -------------------------------------------------------------------------------- 1 | int *Global; 2 | int GlobalCopy; 3 | 4 | void EscapeBoth(int *inp1, int *inp2) { 5 | Global = inp1 + 1 -1; 6 | int *tmp = inp2; 7 | Global = tmp; 8 | } 9 | 10 | void EscapeFirstOnly(int *fst, int *snd) { 11 | EscapeBoth(fst, fst); 12 | } 13 | 14 | void EscapeSecondOnly(int *fst, int *snd) { 15 | EscapeFirstOnly(snd, fst); 16 | } 17 | 18 | class EscapeSelf { 19 | private: 20 | EscapeSelf *Leak; 21 | public: 22 | void escape() { 23 | Leak = this; 24 | } 25 | void callEscape() { 26 | escape(); 27 | } 28 | }; 29 | 30 | 31 | void SimpleNoEscapePointer(int *i) { } 32 | void SimpleNoEscapeReference(int &i) { } 33 | void SimpleNoEscapeCopy(int i) { Global = &i; } 34 | 35 | void SimpleEscapePointer(int *i) { Global = i; } 36 | void SimpleEscapeReference(int &i) { Global = &i; } 37 | 38 | void NoEscapeDereference(int *p) { 39 | GlobalCopy = *p; 40 | } 41 | 42 | void EscapeReferenceDereference(int *p) { 43 | Global = &(*p); 44 | } 45 | 46 | void bar(int a) { } 47 | 48 | int* foo(int *a, int *b) { 49 | int c = *a + *b; 50 | bar(*a + *b); 51 | bar(c); // getting rid of compile warning 52 | return a; 53 | } 54 | 55 | int* phiTestBothReturn(int *a, int *b, bool c) { 56 | if (c) { 57 | return a; 58 | } else { 59 | return b; 60 | } 61 | } 62 | 63 | void phiTestBothBranch(int *a, int *b, bool c) { 64 | int *x; 65 | if (c) { 66 | x = a; 67 | } else { 68 | x = b; 69 | } 70 | Global = x; 71 | } 72 | 73 | int* phiTestSingleReturn(int *a, int *b, bool c) { 74 | if (c) { 75 | return b; 76 | } else { 77 | return b; 78 | } 79 | } 80 | 81 | void phiTestSingleBranch(int *a, int *b, bool c) { 82 | int *x; 83 | if (c) { 84 | x = a; 85 | } else { 86 | x = a; 87 | } 88 | Global = x; 89 | } 90 | 91 | // Code below would serve testing with lifetime of objects. 92 | 93 | /* 94 | class Collection { 95 | public: 96 | int TotalAge; 97 | }; 98 | 99 | class User { 100 | private: 101 | int Age; 102 | char *Name; 103 | Collection *Copy; 104 | 105 | public: 106 | void AddAge(Collection *C) { 107 | C->TotalAge += Age; 108 | } 109 | 110 | void SaveCollectionPointer(Collection *C) { 111 | Copy = C; 112 | } 113 | 114 | void SaveCollectionReference(Collection &C) { 115 | Copy = &C; 116 | } 117 | }; 118 | 119 | void NoEscapeLocalCollection(User* U) { 120 | Collection C; 121 | U->AddAge(&C); 122 | } 123 | 124 | void EscapeLocalCollectionReference(User* U) { 125 | Collection C; 126 | U->SaveCollectionReference(C); 127 | } 128 | 129 | void EscapeLocalCollectionPointer(User* U) { 130 | Collection C; 131 | U->SaveCollectionPointer(&C); 132 | } 133 | 134 | void NoEscapeLocalUserLocalCollection() { 135 | User U; 136 | Collection C; 137 | U.SaveCollectionPointer(&C); 138 | } 139 | */ 140 | -------------------------------------------------------------------------------- /src/test/lsa/test_input_global_vars.cpp: -------------------------------------------------------------------------------- 1 | //===- test_input_global_vars.cpp ------------------------------*- C++ --*-===// 2 | // A simple file for testing detecting changes to global variables. 3 | //===----------------------------------------------------------------------===// 4 | 5 | int globalVariable; 6 | 7 | void changeGlobalVariable() { 8 | globalVariable = 10; 9 | } 10 | 11 | void dontChangeAnything() { 12 | 13 | } 14 | 15 | void changeLocalVariable() { 16 | int localVariable; 17 | localVariable = 10; 18 | } 19 | 20 | int changeGlobalVariableMoreCode(bool b) { 21 | if (b) { 22 | return 11; 23 | } else { 24 | int x = 0; 25 | while (x < 21) { 26 | ++x; 27 | } 28 | globalVariable = 10; 29 | return globalVariable; 30 | } 31 | } 32 | 33 | int dontChangeGlobalVariableMoreCode(bool b) { 34 | if (b) { 35 | return 11; 36 | } else { 37 | int x = 0; 38 | while (x < 21) { 39 | ++x; 40 | } 41 | return globalVariable; 42 | } 43 | } 44 | 45 | class MyClass { 46 | public: 47 | int classVariable; 48 | void changeMemberVariable() { 49 | this->classVariable = 10; 50 | } 51 | }; 52 | 53 | void changeMemberVariableOutside() { 54 | MyClass C; 55 | C.classVariable = 10; 56 | } 57 | 58 | void changeMemberVariableOutside(MyClass &C) { 59 | C.classVariable = 10; 60 | } 61 | 62 | void changeAPointer(int *p) { 63 | *p = 10; 64 | } 65 | 66 | void changeGlobalVariableViaPointerCall() { 67 | changeAPointer(&globalVariable); 68 | } 69 | 70 | void changeGlobalVariableViaPointer() { 71 | int *p = &globalVariable; 72 | *p = 10; 73 | } 74 | 75 | void changeLocalVariableViaPointer() { 76 | int localVariable = 3; 77 | int *p = &localVariable; 78 | *p = 10; 79 | } 80 | 81 | void changeGlobalVariableViaReference() { 82 | int &p = globalVariable; 83 | p = 10; 84 | } 85 | 86 | void changeLocalVariableViaReference() { 87 | int localVariable = 3; 88 | int &p = localVariable; 89 | p = 10; 90 | } 91 | -------------------------------------------------------------------------------- /src/test/lsa/test_input_scc.cpp: -------------------------------------------------------------------------------- 1 | //===- test_input_scc.cpp --------------------------------------*- C++ --*-===// 2 | // A simple file for testing call graph generation and SCC computation. 3 | // 4 | // Generated graph: 5 | // 6 | // a ----> b ----> c ----> d ----> e 7 | // ^ ^ | ^ | 8 | // | | | | | 9 | // | | | v | 10 | // \------- f <------/ g <------/ 11 | // 12 | // SCC #1: {a, b, c, f} 13 | // SCC #2: {d, e, g} 14 | //===----------------------------------------------------------------------===// 15 | 16 | void a(); 17 | void b(); 18 | void c(); 19 | void d(); 20 | void e(); 21 | void f(); 22 | void g(); 23 | 24 | void a() { b(); } 25 | void b() { c(); } 26 | void c() { f(); d(); } 27 | void d() { e(); g(); } 28 | void e() { g(); } 29 | void f() { a(); b(); } 30 | void g() { d(); } 31 | -------------------------------------------------------------------------------- /src/test/parser/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(test_BNFParser test_BNFParser.cpp) 2 | target_link_libraries(test_BNFParser base parser til) 3 | add_dependencies(test_BNFParser parser_grammar) 4 | 5 | add_executable(test_parser test_parser.cpp) 6 | target_link_libraries(test_parser parser til) 7 | add_dependencies(test_parser ohmu_grammar) -------------------------------------------------------------------------------- /src/test/parser/test_BNFParser.cpp: -------------------------------------------------------------------------------- 1 | //===- test_BNFParser.cpp --------------------------------------*- C++ --*-===// 2 | // Copyright 2014 Google 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | //===----------------------------------------------------------------------===// 17 | 18 | #include 19 | #include 20 | 21 | #include "parser/DefaultLexer.h" 22 | #include "parser/BNFParser.h" 23 | #include "parser/TILParser.h" 24 | 25 | using namespace ohmu::parsing; 26 | 27 | int bootstrapBNF() { 28 | const char* fname = "src/grammar/parser.grammar"; 29 | 30 | ohmu::parsing::DefaultLexer lexer; 31 | ohmu::parsing::BNFParser bootstrapBNFParser(&lexer); 32 | 33 | FILE* file = fopen(fname, "r"); 34 | if (!file) { 35 | std::cout << "File '" << fname << "' not found.\n"; 36 | return -1; 37 | } 38 | 39 | BNFParser::initParserFromFile(bootstrapBNFParser, file, false); 40 | bootstrapBNFParser.printSyntax(std::cout); 41 | 42 | fclose(file); 43 | return 0; 44 | } 45 | 46 | int makeTILParser(const char* fname) { 47 | ohmu::parsing::DefaultLexer lexer; 48 | ohmu::parsing::TILParser myParser(&lexer); 49 | 50 | FILE* file = fopen(fname, "r"); 51 | if (!file) { 52 | std::cout << "File '" << fname << "' not found.\n"; 53 | return -1; 54 | } 55 | 56 | BNFParser::initParserFromFile(myParser, file, false); 57 | myParser.printSyntax(std::cout); 58 | 59 | fclose(file); 60 | return 0; 61 | } 62 | 63 | 64 | int main(int argc, const char** argv) { 65 | if (argc <= 1) 66 | return bootstrapBNF(); 67 | else 68 | return makeTILParser(argv[1]); 69 | } 70 | 71 | -------------------------------------------------------------------------------- /src/test/parser/test_parser.cpp: -------------------------------------------------------------------------------- 1 | //===- test_parser.cpp -----------------------------------------*- C++ --*-===// 2 | // Copyright 2014 Google 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | //===----------------------------------------------------------------------===// 17 | 18 | #include "test/Driver.h" 19 | #include "til/Bytecode.h" 20 | #include "til/VisitCFG.h" 21 | 22 | 23 | using namespace ohmu; 24 | using namespace ohmu::parsing; 25 | using namespace ohmu::til; 26 | 27 | 28 | void printSExpr(SExpr* e) { 29 | TILDebugPrinter::print(e, std::cout); 30 | } 31 | 32 | 33 | int main(int argc, const char** argv) { 34 | if (argc == 1) { 35 | std::cerr << "No file to parse.\n"; 36 | return 0; 37 | } 38 | 39 | Global global; 40 | Driver driver; 41 | 42 | // Load up the ohmu grammar. 43 | bool success = driver.initParser("src/grammar/ohmu.grammar"); 44 | if (!success) 45 | return -1; 46 | 47 | // Parse the ohmu source file. 48 | if (strcmp("--",argv[1]) == 0) { 49 | InteractiveStream IS("Ohmu > ", ".... > "); 50 | success = driver.parseDefinitions(&global, IS); 51 | } 52 | else { 53 | success = driver.parseDefinitions(&global, argv[1]); 54 | } 55 | if (!success) 56 | return -1; 57 | 58 | // Convert high-level AST to low-level IR. 59 | global.lower(); 60 | std::cout << "\n------ Ohmu IR ------\n"; 61 | global.print(std::cout); 62 | 63 | // Find all of the CFGs. 64 | VisitCFG visitCFG; 65 | visitCFG.traverseAll(global.global()); 66 | 67 | std::cout << "\n\nNumber of CFGs: " << visitCFG.cfgs().size() << "\n\n"; 68 | return 0; 69 | } 70 | 71 | -------------------------------------------------------------------------------- /src/test/til/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(test_serialization test_serialization.cpp) 2 | target_link_libraries(test_serialization til) 3 | 4 | add_executable(test_copier test_copier.cpp) 5 | target_link_libraries(test_copier til) 6 | 7 | add_executable(test_compare test_compare.cpp) 8 | target_link_libraries(test_compare parser til) 9 | add_dependencies(test_compare ohmu_grammar) -------------------------------------------------------------------------------- /src/test/til/test_copier.cpp: -------------------------------------------------------------------------------- 1 | //===- test_copier.cpp -----------------------------------------*- C++ --*-===// 2 | // Copyright 2014 Google 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | //===----------------------------------------------------------------------===// 17 | 18 | #include "til/CFGBuilder.h" 19 | #include "til/CopyReducer.h" 20 | #include "til/TILPrettyPrint.h" 21 | #include "til/TypedEvaluator.h" 22 | 23 | #include 24 | 25 | using namespace ohmu; 26 | using namespace til; 27 | 28 | SExpr* makeSimple(CFGBuilder& bld) { 29 | auto* four = bld.newLiteralT(4); 30 | four->addAnnotation(bld.newAnnotationT(132)); 31 | 32 | auto* vd = bld.newVarDecl(VarDecl::VK_Let, "four", four); 33 | vd->setVarIndex(1); 34 | auto* anncond = bld.newBinaryOp(BOP_Leq, bld.newLiteralT(5), 35 | bld.newLiteralT(3)); 36 | anncond->addAnnotation( 37 | bld.newAnnotationT(bld.newLiteralT(true))); 38 | vd->addAnnotation(bld.newAnnotationT(anncond)); 39 | 40 | auto* cond2 = bld.newLiteralT(13); 41 | auto* precond2 = bld.newAnnotationT(cond2); 42 | auto *cond = bld.newBinaryOp(BOP_Leq, bld.newLiteralT(6), 43 | bld.newLiteralT(7)); 44 | cond->addAnnotation(precond2); 45 | cond->addAnnotation(bld.newAnnotationT("COMPARE")); 46 | 47 | auto* let = bld.newLet(vd, cond); 48 | let->addAnnotation(bld.newAnnotationT("LET")); 49 | 50 | auto* A = bld.newBinaryOp(BOP_Leq, bld.newLiteralT(200), 51 | bld.newLiteralT(201)); 52 | auto* B = bld.newBinaryOp(BOP_Leq, bld.newLiteralT(300), 53 | bld.newLiteralT(301)); 54 | 55 | auto* Acond = bld.newLiteralT(13); 56 | A->addAnnotation(bld.newAnnotationT(Acond)); 57 | B->addAnnotation(bld.newAnnotationT("lequals")); 58 | 59 | auto* C = bld.newBinaryOp(BOP_Leq, bld.newLiteralT(400), 60 | bld.newLiteralT(401)); 61 | auto* tri = bld.newAnnotationT(A, B, C); 62 | let->addAnnotation(tri); 63 | 64 | return let; 65 | } 66 | 67 | void testCopying(CFGBuilder& bld, SExpr *e) { 68 | std::cout << "Original:\n"; 69 | TILDebugPrinter::print(e, std::cout); 70 | std::cout << "\n\n"; 71 | 72 | std::cout << "Copy to same arena:\n"; 73 | SExprCopier copier(bld.arena()); 74 | SExpr* e1 = copier.copy(e, bld.arena()); 75 | TILDebugPrinter::print(e1, std::cout); 76 | std::cout << "\n\n"; 77 | 78 | std::cout << "Copy to different arena:\n"; 79 | MemRegion region; 80 | MemRegionRef arena(®ion); 81 | SExpr* e2 = copier.copy(e, arena); 82 | TILDebugPrinter::print(e2, std::cout); 83 | std::cout << "\n\n"; 84 | 85 | std::cout << "Inplace reduce (TypedEvaluator):\n"; 86 | TypedEvaluator eval(bld.arena()); 87 | SExpr* e3 = eval.traverseAll(e); 88 | TILDebugPrinter::print(e3, std::cout); 89 | std::cout << "\n\n"; 90 | } 91 | 92 | 93 | void testCopying() { 94 | MemRegion region; 95 | MemRegionRef arena(®ion); 96 | CFGBuilder builder(arena); 97 | 98 | testCopying(builder, makeSimple(builder)); 99 | } 100 | 101 | int main(int argc, const char** argv) { 102 | testCopying(); 103 | } 104 | -------------------------------------------------------------------------------- /src/test/til/test_visitor.cpp: -------------------------------------------------------------------------------- 1 | //===- test_visitor.cpp ----------------------------------------*- C++ --*-===// 2 | // Copyright 2014 Google 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | //===----------------------------------------------------------------------===// 17 | // 18 | // Compile-only test. 19 | // Instantiates some visitor and traverser templates for testing purposes. 20 | // 21 | //===----------------------------------------------------------------------===// 22 | 23 | #include "til/TILVisitor.h" 24 | 25 | namespace ohmu { 26 | namespace til { 27 | 28 | 29 | class SimpleVisitor : public Visitor { }; 30 | 31 | 32 | void test(SExpr* E) { 33 | SimpleVisitor::visit(E); 34 | } 35 | 36 | 37 | } // end namespace til 38 | } // end namespace ohmu 39 | -------------------------------------------------------------------------------- /src/til/Annotation.h: -------------------------------------------------------------------------------- 1 | //===- Annotation.h --------------------------------------------*- C++ --*-===// 2 | // 3 | // The LLVM Compiler Infrastructure 4 | // 5 | // This file is distributed under the University of Illinois Open Source 6 | // License. See LICENSE.TXT in the LLVM repository for details. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #ifndef OHMU_TIL_ANNOTATION_H 11 | #define OHMU_TIL_ANNOTATION_H 12 | 13 | #include "base/LLVMDependencies.h" 14 | #include "base/MemRegion.h" 15 | 16 | namespace ohmu { 17 | namespace til { 18 | 19 | /// Enum for the various kinds of attributes 20 | enum TIL_AnnKind { 21 | #define TIL_ANNKIND_DEF(X) ANNKIND_##X, 22 | #include "TILAnnKinds.def" 23 | }; 24 | 25 | /// Annotation stores one annotation and a next-pointer; thus doubling as a 26 | /// linked list. New annotations need to be created in an arena. 27 | class Annotation { 28 | public: 29 | TIL_AnnKind kind() const { return static_cast(Kind); } 30 | 31 | /// Allocate Annotation in the given region. Annotations must be allocated in 32 | /// regions. 33 | void *operator new(size_t S, MemRegionRef &R) { 34 | return ::operator new(S, R); 35 | } 36 | 37 | /// Annotation objects cannot be deleted. 38 | // This declaration is public to workaround a gcc bug that breaks building 39 | // with REQUIRES_EH=1. 40 | void operator delete(void *) = delete; 41 | 42 | Annotation *next() const { return Next; } 43 | 44 | /// Insert annotation in this sorted list of annotations. 45 | void insert(Annotation *A) { 46 | if (A == nullptr) 47 | return; 48 | assert(A->kind() >= kind() && "Keep annotations sorted, change list head."); 49 | Annotation *Ap = this; 50 | while (Ap->next() && A->kind() >= Ap->kind()) { 51 | Ap = Ap->next(); 52 | } 53 | A->Next = Ap->next(); 54 | Ap->Next = A; 55 | } 56 | 57 | /// Get annotation of the specified derived type. Returns nullptr if no such 58 | /// annotation exists in the list. 59 | template 60 | T *getAnnotation() { 61 | Annotation *Ap = this; 62 | do { 63 | if (isa(Ap)) 64 | return cast(Ap); 65 | Ap = Ap->Next; 66 | } while (Ap); 67 | return nullptr; 68 | } 69 | 70 | /// Get all annotations of the specified derived type. 71 | template 72 | std::vector getAllAnnotations() { 73 | std::vector Res; 74 | T *A = getAnnotation(); 75 | // Using the fact that the list is sorted. 76 | while (A) { 77 | Res.push_back(A); 78 | if (A->next() && A->kind() == A->next()->kind()) 79 | A = A->next(); 80 | else 81 | A = nullptr; 82 | } 83 | return Res; 84 | } 85 | 86 | protected: 87 | Annotation(TIL_AnnKind K) : Kind(K), Next(nullptr) { } 88 | 89 | private: 90 | Annotation() = delete; 91 | 92 | /// Annotation objects must be created in an arena. 93 | void *operator new(size_t) = delete; 94 | 95 | const uint16_t Kind; 96 | 97 | Annotation* Next; 98 | }; 99 | 100 | } // end namespace til 101 | } // end namespace ohmu 102 | 103 | #endif // OHMU_TIL_ANNOTATION_H 104 | -------------------------------------------------------------------------------- /src/til/AnnotationImpl.cpp: -------------------------------------------------------------------------------- 1 | //===- AnnotationImpl.cpp --------------------------------------*- C++ --*-===// 2 | // 3 | // The LLVM Compiler Infrastructure 4 | // 5 | // This file is distributed under the University of Illinois Open Source 6 | // License. See LICENSE.TXT in the LLVM repository for details. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | 10 | #include "AnnotationImpl.h" 11 | #include "Bytecode.h" 12 | #include "CFGBuilder.h" 13 | 14 | namespace ohmu { 15 | namespace til { 16 | 17 | 18 | InstrNameAnnot *InstrNameAnnot::copy(CFGBuilder &Builder, 19 | const std::vector &SubExprs) { 20 | return Builder.newAnnotationT(Name); 21 | } 22 | 23 | void InstrNameAnnot::serialize(BytecodeWriter *B) { 24 | B->getWriter()->writeString(Name); 25 | } 26 | 27 | InstrNameAnnot *InstrNameAnnot::deserialize(BytecodeReader *B) { 28 | StringRef Nm = B->getReader()->readString(); 29 | return B->getBuilder().newAnnotationT(Nm); 30 | } 31 | 32 | 33 | SourceLocAnnot *SourceLocAnnot::copy(CFGBuilder &Builder, 34 | const std::vector &SubExprs) { 35 | return Builder.newAnnotationT(Position); 36 | } 37 | 38 | void SourceLocAnnot::serialize(BytecodeWriter *B) { 39 | B->getWriter()->writeInt64(Position); 40 | } 41 | 42 | SourceLocAnnot *SourceLocAnnot::deserialize(BytecodeReader *B) { 43 | SourcePosition P = B->getReader()->readInt64(); 44 | return B->getBuilder().newAnnotationT(P); 45 | } 46 | 47 | 48 | PreconditionAnnot* 49 | PreconditionAnnot::copy(CFGBuilder &Builder, 50 | const std::vector &SubExprs) { 51 | return Builder.newAnnotationT(SubExprs.at(0)); 52 | } 53 | 54 | void PreconditionAnnot::serialize(BytecodeWriter *B) { } 55 | 56 | PreconditionAnnot *PreconditionAnnot::deserialize(BytecodeReader *B) { 57 | PreconditionAnnot *A = 58 | B->getBuilder().newAnnotationT(B->arg(0)); 59 | B->drop(1); 60 | return A; 61 | } 62 | 63 | 64 | TestTripletAnnot *TestTripletAnnot::copy(CFGBuilder &Builder, 65 | const std::vector &SubExprs) { 66 | return Builder.newAnnotationT( 67 | SubExprs.at(0), SubExprs.at(1), SubExprs.at(2)); 68 | } 69 | 70 | void TestTripletAnnot::serialize(BytecodeWriter *B) { } 71 | 72 | TestTripletAnnot *TestTripletAnnot::deserialize(BytecodeReader *B) { 73 | TestTripletAnnot *A = B->getBuilder().newAnnotationT( 74 | B->arg(2), B->arg(1), B->arg(0)); 75 | B->drop(3); 76 | return A; 77 | } 78 | 79 | 80 | } // end namespace til 81 | } // end namespace ohmu 82 | -------------------------------------------------------------------------------- /src/til/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | add_library(til STATIC 4 | Bytecode.cpp 5 | CFGBuilder.cpp 6 | Global.cpp 7 | SSAPass.cpp 8 | AnnotationImpl.cpp 9 | TIL.cpp 10 | TypedEvaluator.cpp 11 | ) 12 | 13 | target_link_libraries(til base) 14 | -------------------------------------------------------------------------------- /src/til/Evaluator.h: -------------------------------------------------------------------------------- 1 | //===- TypedEvaluator.h ----------------------------------------*- C++ --*-===// 2 | // Copyright 2014 Google 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | //===----------------------------------------------------------------------===// 17 | 18 | #ifndef OHMU_TIL_EVALUATOR_H 19 | #define OHMU_TIL_EVALUATOR_H 20 | 21 | #include "TIL.h" 22 | 23 | namespace ohmu { 24 | namespace til { 25 | 26 | // TODO: minimum integer size should not be hardcoded at ST_32. 27 | // See TILBaseType.h 28 | 29 | 30 | #define DEFINE_BINARY_OP_CLASS(CName, OP, RTy) \ 31 | template \ 32 | struct CName { \ 33 | typedef Literal* ReturnType; \ 34 | static Literal* defaultAction(MemRegionRef A, Literal*, Literal*) { \ 35 | return nullptr; \ 36 | } \ 37 | static LiteralT* action(MemRegionRef A, Literal* E0, Literal* E1) { \ 38 | return new (A) LiteralT(E0->as()->value() OP \ 39 | E1->as()->value()); \ 40 | } \ 41 | }; 42 | 43 | 44 | namespace opclass { 45 | 46 | DEFINE_BINARY_OP_CLASS(Add, +, Ty1) 47 | DEFINE_BINARY_OP_CLASS(Sub, -, Ty1) 48 | DEFINE_BINARY_OP_CLASS(Mul, *, Ty1) 49 | DEFINE_BINARY_OP_CLASS(Div, /, Ty1) 50 | DEFINE_BINARY_OP_CLASS(Rem, %, Ty1) 51 | DEFINE_BINARY_OP_CLASS(Shl, <<, Ty1) 52 | DEFINE_BINARY_OP_CLASS(Shr, >>, Ty1) 53 | DEFINE_BINARY_OP_CLASS(BitAnd, &, Ty1) 54 | DEFINE_BINARY_OP_CLASS(BitXor, ^, Ty1) 55 | DEFINE_BINARY_OP_CLASS(BitOr, |, Ty1) 56 | 57 | DEFINE_BINARY_OP_CLASS(Eq, ==, bool) 58 | DEFINE_BINARY_OP_CLASS(Neq, !=, bool) 59 | DEFINE_BINARY_OP_CLASS(Lt, <, bool) 60 | DEFINE_BINARY_OP_CLASS(Leq, <=, bool) 61 | 62 | DEFINE_BINARY_OP_CLASS(LogicAnd, &&, Ty1) 63 | DEFINE_BINARY_OP_CLASS(LogicOr, ||, Ty1) 64 | 65 | } // end namespace opclass 66 | 67 | #undef DEFINE_BINARY_OP_CLASS 68 | 69 | 70 | #define ARGS(OP) opclass::OP, Literal*, MemRegionRef, Literal*, Literal* 71 | 72 | Literal* evaluateBinaryOp(TIL_BinaryOpcode Op, BaseType Bt, MemRegionRef A, 73 | Literal* E0, Literal* E1) { 74 | switch (Op) { 75 | case BOP_Add: 76 | return BtBr::branchOnNumeric(Bt, A, E0, E1); 77 | case BOP_Sub: 78 | return BtBr::branchOnNumeric(Bt, A, E0, E1); 79 | case BOP_Mul: 80 | return BtBr::branchOnNumeric(Bt, A, E0, E1); 81 | case BOP_Div: 82 | return BtBr::branchOnNumeric(Bt, A, E0, E1); 83 | case BOP_Rem: 84 | return BtBr::branchOnIntegral(Bt, A, E0, E1); 85 | case BOP_Shl: 86 | return BtBr::branchOnIntegral(Bt, A, E0, E1); 87 | case BOP_Shr: 88 | return BtBr::branchOnIntegral(Bt, A, E0, E1); 89 | case BOP_BitAnd: 90 | return BtBr::branchOnIntegral(Bt, A, E0, E1); 91 | case BOP_BitXor: 92 | return BtBr::branchOnIntegral(Bt, A, E0, E1); 93 | case BOP_BitOr: 94 | return BtBr::branchOnIntegral(Bt, A, E0, E1); 95 | case BOP_Eq: 96 | return BtBr::branch(Bt, A, E0, E1); 97 | case BOP_Neq: 98 | return BtBr::branch(Bt, A, E0, E1); 99 | case BOP_Lt: 100 | return BtBr::branchOnNumeric(Bt, A, E0, E1); 101 | case BOP_Leq: 102 | return BtBr::branchOnNumeric(Bt, A, E0, E1); 103 | case BOP_Gt: 104 | return BtBr::branchOnNumeric(Bt, A, E1, E0); 105 | case BOP_Geq: 106 | return BtBr::branchOnNumeric(Bt, A, E1, E0); 107 | case BOP_LogicAnd: 108 | return opclass::LogicAnd::action(A, E0, E1); 109 | case BOP_LogicOr: 110 | return opclass::LogicOr::action(A, E0, E1); 111 | } 112 | return nullptr; 113 | } 114 | 115 | #undef ARGS 116 | 117 | 118 | } // endif namespace til 119 | } // endif namespace ohmu 120 | 121 | #endif // OHMU_TIL_EVALUATOR_H 122 | -------------------------------------------------------------------------------- /src/til/Global.cpp: -------------------------------------------------------------------------------- 1 | //===- Global.cpp ----------------------------------------------*- C++ --*-===// 2 | // Copyright 2014 Google 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | //===----------------------------------------------------------------------===// 17 | 18 | #include "Global.h" 19 | #include "TypedEvaluator.h" 20 | 21 | namespace ohmu { 22 | namespace til { 23 | 24 | 25 | template 26 | inline Slot* scalarTypeSlot(Global &G, StringRef Name) { 27 | auto* Ty = new (G.LangArena) ScalarType(BaseType::getBaseType()); 28 | auto* Slt = new (G.ParseArena) Slot(Name, Ty); 29 | Slt->setModifier(Slot::SLT_Final); 30 | return Slt; 31 | } 32 | 33 | void Global::createPrelude() { 34 | PreludeDefs.push_back( scalarTypeSlot (*this, "Void") ); 35 | PreludeDefs.push_back( scalarTypeSlot (*this, "Bool") ); 36 | PreludeDefs.push_back( scalarTypeSlot (*this, "Int8") ); 37 | PreludeDefs.push_back( scalarTypeSlot (*this, "UInt8") ); 38 | PreludeDefs.push_back( scalarTypeSlot (*this, "Int16") ); 39 | PreludeDefs.push_back( scalarTypeSlot (*this, "UInt16") ); 40 | PreludeDefs.push_back( scalarTypeSlot (*this, "Int32") ); 41 | PreludeDefs.push_back( scalarTypeSlot (*this, "UInt32") ); 42 | PreludeDefs.push_back( scalarTypeSlot (*this, "Int64") ); 43 | PreludeDefs.push_back( scalarTypeSlot (*this, "UInt64") ); 44 | PreludeDefs.push_back( scalarTypeSlot (*this, "Float") ); 45 | PreludeDefs.push_back( scalarTypeSlot (*this, "Double") ); 46 | PreludeDefs.push_back( scalarTypeSlot(*this, "String") ); 47 | PreludeDefs.push_back( scalarTypeSlot (*this, "PointerType") ); 48 | 49 | PreludeDefs.push_back( scalarTypeSlot(*this, "Int") ); 50 | PreludeDefs.push_back( scalarTypeSlot(*this, "UInt") ); 51 | } 52 | 53 | 54 | void Global::addDefinitions(std::vector& Defs) { 55 | assert(GlobalRec == nullptr && "FIXME: support multiple calls."); 56 | 57 | if (PreludeDefs.empty()) 58 | createPrelude(); 59 | 60 | unsigned Sz = PreludeDefs.size() + Defs.size(); 61 | GlobalRec = new (ParseArena) Record(ParseArena, Sz); 62 | 63 | for (auto *Slt : PreludeDefs) { 64 | GlobalRec->slots().emplace_back(ParseArena, Slt); 65 | } 66 | for (auto *E : Defs) { 67 | auto *Slt = dyn_cast_or_null(E); 68 | if (Slt) 69 | GlobalRec->slots().emplace_back(ParseArena, Slt); 70 | } 71 | 72 | auto *Vd = new (ParseArena) VarDecl(VarDecl::VK_SFun, "global", nullptr); 73 | GlobalSFun = new (ParseArena) Function(Vd, GlobalRec); 74 | } 75 | 76 | 77 | void Global::lower() { 78 | TypedEvaluator eval(DefArena); 79 | SExpr* E = eval.traverseAll(GlobalSFun); 80 | 81 | // Replace the global definitions with lowered versions. 82 | GlobalSFun = dyn_cast(E); 83 | if (GlobalSFun) 84 | GlobalRec = dyn_cast(GlobalSFun->body()); 85 | else 86 | GlobalRec = nullptr; 87 | } 88 | 89 | 90 | void Global::print(std::ostream &SS) { 91 | TILDebugPrinter::print(GlobalSFun, SS); 92 | } 93 | 94 | 95 | } // end namespace til 96 | } // end namespace ohmu 97 | -------------------------------------------------------------------------------- /src/til/Global.h: -------------------------------------------------------------------------------- 1 | //===- Global.h ------------------------------------------------*- C++ --*-===// 2 | // Copyright 2014 Google 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | //===----------------------------------------------------------------------===// 17 | 18 | #ifndef OHMU_TIL_GLOBAL_H 19 | #define OHMU_TIL_GLOBAL_H 20 | 21 | #include "TIL.h" 22 | 23 | #include 24 | 25 | namespace ohmu { 26 | namespace til { 27 | 28 | 29 | class Global { 30 | public: 31 | Global() 32 | : GlobalRec(nullptr), GlobalSFun(nullptr), 33 | LangArena(&LangRegion), StringArena(&StringRegion), 34 | ParseArena(&ParseRegion), DefArena(&DefRegion) 35 | { } 36 | 37 | inline SExpr* global() { return GlobalSFun; } 38 | 39 | void createPrelude(); 40 | 41 | // Add Defs to the set of global, newly parsed definitions. 42 | void addDefinitions(std::vector &Defs); 43 | 44 | // Lower the parsed definitions. 45 | void lower(); 46 | 47 | // Dump outputs to the given stream 48 | void print(std::ostream &SS); 49 | 50 | private: 51 | MemRegion LangRegion; // Standard language definitions. 52 | MemRegion StringRegion; // Region to hold string constants. 53 | MemRegion ParseRegion; // Region for the initial AST produced by the parser. 54 | MemRegion DefRegion; // Region for rewritten definitions. 55 | 56 | Record *GlobalRec; 57 | Function *GlobalSFun; 58 | std::vector PreludeDefs; 59 | 60 | public: 61 | MemRegionRef LangArena; 62 | MemRegionRef StringArena; 63 | MemRegionRef ParseArena; 64 | MemRegionRef DefArena; 65 | }; 66 | 67 | 68 | } // end namespace til 69 | } // end namespace ohmu 70 | 71 | #endif // OHMU_TIL_GLOBAL_H 72 | -------------------------------------------------------------------------------- /src/til/README.txt: -------------------------------------------------------------------------------- 1 | 2 | The Ohmu Typed Intermediate Language (TIL) has been carefully designed to be 3 | language-agnostic. The hope is that many different source languages can be 4 | translated down to the TIL, so that the ohmu static analysis can be made 5 | language agnostic. 6 | 7 | The clang C++ compiler uses the TIL to do thread safety analysis. Many of the 8 | files in this directory are thus mirrored in the clang open source repository, 9 | and can be found under clang/lib/Analysis/Analyses/ThreadSafety/. Files which 10 | shared with clang are released under the LLVM open source license, rather than 11 | the Apache open source license, and are written in the clang/LLVM style. 12 | 13 | -------------------------------------------------------------------------------- /src/til/SSAPass.h: -------------------------------------------------------------------------------- 1 | //===- SSAPass.h -----------------------------------------------*- C++ --*-===// 2 | // Copyright 2014 Google 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | //===----------------------------------------------------------------------===// 17 | // 18 | // Implements the conversion to SSA. 19 | // 20 | //===----------------------------------------------------------------------===// 21 | 22 | #ifndef OHMU_TIL_SSAPASS_H 23 | #define OHMU_TIL_SSAPASS_H 24 | 25 | #include "InplaceReducer.h" 26 | 27 | namespace ohmu { 28 | namespace til { 29 | 30 | 31 | // Map from local variables (allocID) to their definitions (SExpr*). 32 | typedef std::vector LocalVarMap; 33 | 34 | // Maintain a variable map for each basic block. 35 | struct SSABlockInfo { 36 | LocalVarMap AllocVarMap; 37 | }; 38 | 39 | 40 | class SSAPass : public InplaceReducer, 41 | public AGTraversal { 42 | public: 43 | void enterCFG(SCFG *Cfg); 44 | void exitCFG(SCFG *Cfg); 45 | void enterBlock(BasicBlock *B); 46 | void exitBlock(BasicBlock *B); 47 | 48 | void reduceWeak(Instruction *I); 49 | void reduceAlloc(Alloc *Orig); 50 | void reduceStore(Store *Orig); 51 | void reduceLoad (Load *Orig); 52 | 53 | protected: 54 | // An Alloc instruction that may be removed. 55 | class FutureAlloc : public Future { 56 | public: 57 | FutureAlloc(Alloc* A) : AllocInstr(A) { } 58 | virtual ~FutureAlloc() { } 59 | 60 | /// We don't use evaluate(); FutureAllocs are forced manually. 61 | virtual SExpr* evaluate() override { 62 | assert(false && "Cannot force this future."); 63 | return nullptr; 64 | } 65 | 66 | Alloc *AllocInstr; 67 | }; 68 | 69 | // A Store instruction that may be removed. 70 | class FutureStore : public Future { 71 | public: 72 | FutureStore(Store* S, Alloc* A) : StoreInstr(S), AllocInstr(A) { } 73 | virtual ~FutureStore() { } 74 | 75 | /// We don't use evaluate(); FutureStores are forced manually. 76 | virtual SExpr* evaluate() override { 77 | assert(false && "Cannot force this future."); 78 | return nullptr; 79 | } 80 | 81 | Store *StoreInstr; 82 | Alloc *AllocInstr; 83 | }; 84 | 85 | // A load instruction that needs to be rewritten. 86 | class FutureLoad : public Future { 87 | public: 88 | FutureLoad(Load* L, Alloc* A) : LoadInstr(L), AllocInstr(A) { } 89 | virtual ~FutureLoad() { } 90 | 91 | /// We don't use evaluate(); FutureLoads are forced manually. 92 | virtual SExpr* evaluate() override { 93 | assert(false && "Cannot force this future."); 94 | return nullptr; 95 | } 96 | 97 | Load *LoadInstr; 98 | Alloc *AllocInstr; 99 | }; 100 | 101 | // Look up variable in the cache. 102 | SExpr* lookupInCache(LocalVarMap *LvarMap, unsigned LvarID); 103 | 104 | // Make a new phi node, with the first i values set to E 105 | Phi* makeNewPhiNode(unsigned i, SExpr *E, unsigned numPreds); 106 | 107 | // Lookup value of local variable at the beginning of basic block B 108 | SExpr* lookupInPredecessors(BasicBlock *B, unsigned LvarID, StringRef Nm); 109 | 110 | // Lookup value of local variable at the end of basic block B 111 | SExpr* lookup(BasicBlock *B, unsigned LvarID, StringRef Nm); 112 | 113 | // Second pass of SSA -- lookup variables and replace all loads. 114 | void replacePending(); 115 | 116 | public: 117 | SSAPass(MemRegionRef A) 118 | : InplaceReducer(A), CurrentVarMap(nullptr) { 119 | FutArena.setRegion(&FutRegion); 120 | } 121 | 122 | private: 123 | typedef InplaceReducer Super; 124 | typedef Traversal SuperTv; 125 | 126 | SSAPass() = delete; 127 | 128 | MemRegion FutRegion; ///< Put Futures in region for immediate deletion. 129 | MemRegionRef FutArena; 130 | 131 | LocalVarMap* CurrentVarMap; 132 | 133 | std::vector BInfoMap; ///< Side table for basic blocks. 134 | std::vector PendingAllocs; ///< Possibly removable allocs. 135 | std::vector PendingStores; ///< Possibly removable stores. 136 | std::vector PendingLoads; ///< Loads that need to be forced. 137 | 138 | std::vector NumUses; 139 | }; 140 | 141 | 142 | } // end namespace til 143 | } // end namespace ohmu 144 | 145 | #endif // OHMU_TIL_SSAPASS_H 146 | -------------------------------------------------------------------------------- /src/til/TILAnnKinds.def: -------------------------------------------------------------------------------- 1 | //===- TILAnnKinds.def -----------------------------------------*- C++ --*-===// 2 | // 3 | // The LLVM Compiler Infrastructure 4 | // 5 | // This file is distributed under the University of Illinois Open Source 6 | // License. See LICENSE.TXT for details. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | // 10 | // This file defines the list of annotation kinds for the Typed Intermediate 11 | // language. See Annotation.h for their usage. 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | #ifdef TIL_ANNKIND_FIRST 16 | TIL_ANNKIND_FIRST(InstrName) 17 | #undef TIL_ANNKIND_FIRST 18 | #endif 19 | 20 | #ifdef TIL_ANNKIND_DEF 21 | 22 | // Declarations and definitions 23 | TIL_ANNKIND_DEF(InstrNameAnnot) 24 | TIL_ANNKIND_DEF(SourceLocAnnot) 25 | TIL_ANNKIND_DEF(TestTripletAnnot) 26 | TIL_ANNKIND_DEF(PreconditionAnnot) 27 | 28 | #undef TIL_ANNKIND_DEF 29 | #endif // TIL_ANNKIND_DEF 30 | 31 | #ifdef TIL_ANNKIND_LAST 32 | TIL_ANNKIND_LAST(Precondition) 33 | #undef TIL_ANNKIND_LAST 34 | #endif 35 | -------------------------------------------------------------------------------- /src/til/TILOps.def: -------------------------------------------------------------------------------- 1 | //===- ThreadSafetyTIL.h ---------------------------------------*- C++ --*-===// 2 | // 3 | // The LLVM Compiler Infrastructure 4 | // 5 | // This file is distributed under the University of Illinois Open Source 6 | // License. See LICENSE.TXT for details. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | // 10 | // This file defines the list of core opcodes for the Thread Safety 11 | // Typed Intermediate language. Please see ThreadSafetyTIL.h for more 12 | // information. 13 | // 14 | //===----------------------------------------------------------------------===// 15 | 16 | #ifdef TIL_OPCODE_FIRST 17 | TIL_OPCODE_FIRST(VarDecl) 18 | #undef TIL_OPCODE_FIRST 19 | #endif 20 | 21 | #ifdef TIL_OPCODE_DEF 22 | 23 | // Declarations and definitions 24 | TIL_OPCODE_DEF(VarDecl) 25 | TIL_OPCODE_DEF(Function) 26 | TIL_OPCODE_DEF(Code) 27 | TIL_OPCODE_DEF(Field) 28 | TIL_OPCODE_DEF(Slot) 29 | TIL_OPCODE_DEF(Record) 30 | TIL_OPCODE_DEF(Array) 31 | TIL_OPCODE_DEF(ScalarType) 32 | 33 | // CFG constructs 34 | TIL_OPCODE_DEF(SCFG) 35 | TIL_OPCODE_DEF(BasicBlock) 36 | 37 | // Instructions 38 | TIL_OPCODE_DEF(Literal) 39 | TIL_OPCODE_DEF(Variable) 40 | 41 | // Paths 42 | TIL_OPCODE_DEF(Apply) 43 | TIL_OPCODE_DEF(Project) 44 | 45 | TIL_OPCODE_DEF(Call) 46 | TIL_OPCODE_DEF(Alloc) 47 | TIL_OPCODE_DEF(Load) 48 | TIL_OPCODE_DEF(Store) 49 | TIL_OPCODE_DEF(ArrayIndex) 50 | TIL_OPCODE_DEF(ArrayAdd) 51 | TIL_OPCODE_DEF(UnaryOp) 52 | TIL_OPCODE_DEF(BinaryOp) 53 | TIL_OPCODE_DEF(Cast) 54 | TIL_OPCODE_DEF(Phi) 55 | 56 | // Terminator instructions 57 | TIL_OPCODE_DEF(Goto) 58 | TIL_OPCODE_DEF(Branch) 59 | TIL_OPCODE_DEF(Switch) 60 | TIL_OPCODE_DEF(Return) 61 | 62 | // Future and Undefined are instructions for rewriting purposes. 63 | TIL_OPCODE_DEF(Future) 64 | TIL_OPCODE_DEF(Undefined) 65 | 66 | // Pseudo-terms 67 | TIL_OPCODE_DEF(Wildcard) 68 | 69 | TIL_OPCODE_DEF(Identifier) 70 | TIL_OPCODE_DEF(Let) 71 | TIL_OPCODE_DEF(IfThenElse) 72 | 73 | #undef TIL_OPCODE_DEF 74 | #endif // TIL_OPCODE_DEF 75 | 76 | #ifdef TIL_OPCODE_LAST 77 | TIL_OPCODE_LAST(IfThenElse) 78 | #undef TIL_OPCODE_LAST 79 | #endif 80 | -------------------------------------------------------------------------------- /src/til/TILVisitor.h: -------------------------------------------------------------------------------- 1 | //===- TILVisitor.h --------------------------------------------*- C++ --*-===// 2 | // 3 | // The LLVM Compiler Infrastructure 4 | // 5 | // This file is distributed under the University of Illinois Open Source 6 | // License. See LICENSE.TXT for details. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | // 10 | // Implements the reducer interface so that every reduce method simply 11 | // calls a corresponding visit method. 12 | // 13 | //===----------------------------------------------------------------------===// 14 | 15 | #ifndef LLVM_CLANG_ANALYSIS_ANALYSES_THREADSAFETY_TILVISITOR_H 16 | #define LLVM_CLANG_ANALYSIS_ANALYSES_THREADSAFETY_TILVISITOR_H 17 | 18 | #include "TIL.h" 19 | #include "TILTraverse.h" 20 | 21 | namespace ohmu { 22 | namespace til { 23 | 24 | 25 | /// Implements a post-order visitor. 26 | template 27 | class Visitor : public Traversal, 28 | public DefaultScopeHandler, 29 | public DefaultReducer { 30 | public: 31 | typedef Traversal SuperTv; 32 | 33 | Self* self() { return static_cast(this); } 34 | 35 | Visitor() : Success(true) { } 36 | 37 | static bool visit(SExpr *E) { 38 | Self Visitor; 39 | Visitor.traverseAll(E); 40 | return Visitor.Success; 41 | } 42 | 43 | /// Visit routines may invoke fail() to abort the visitor. 44 | void fail() { Success = false; } 45 | 46 | /// Override traverse to abort traversal on failure. 47 | template 48 | void traverse(T* E, TraversalKind K) { 49 | if (Success) 50 | SuperTv::traverse(E, K); 51 | } 52 | 53 | protected: 54 | bool Success; 55 | }; 56 | 57 | 58 | } // end namespace til 59 | } // end namespace ohmu 60 | 61 | #endif // LLVM_CLANG_ANALYSIS_ANALYSES_THREADSAFETY_TILVISITOR_H 62 | -------------------------------------------------------------------------------- /src/til/VisitCFG.h: -------------------------------------------------------------------------------- 1 | //===- VisitCFG.h --------------------------------------------*- C++ --*-===// 2 | // Copyright 2014 Google 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | //===----------------------------------------------------------------------===// 17 | // 18 | // This is a simple Visitor class that collects all of the CFGs in a module 19 | // into a list. 20 | // 21 | //===----------------------------------------------------------------------===// 22 | 23 | #include "TILVisitor.h" 24 | 25 | #ifndef OHMU_TIL_VISITCFG_H_ 26 | #define OHMU_TIL_VISITCFG_H_ 27 | 28 | namespace ohmu { 29 | namespace til { 30 | 31 | 32 | // Simple visitor which finds all CFGs and adds them to list. 33 | class VisitCFG : public Visitor { 34 | public: 35 | // Don't traverse inside CFGs; just add them to the list. 36 | bool traverseSCFG(SCFG* cfg) { 37 | cfgList_.push_back(cfg); 38 | return true; 39 | } 40 | 41 | std::vector& cfgs() { return cfgList_; } 42 | 43 | private: 44 | std::vector cfgList_; 45 | }; 46 | 47 | 48 | } // end namespace til 49 | } // end namespace ohmu 50 | 51 | #endif // OHMU_TIL_VISITCFG_H_ 52 | -------------------------------------------------------------------------------- /src/unittests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(Threads) 2 | 3 | macro(run_test TEST_NAME) 4 | add_dependencies(${TEST_NAME} gtest) 5 | target_link_libraries(${TEST_NAME} 6 | ${GTEST_BIN}/libgtest.a 7 | ) 8 | target_link_libraries(${TEST_NAME} ${CMAKE_THREAD_LIBS_INIT}) 9 | add_test(run_${TEST_NAME} ${TEST_NAME}) 10 | endmacro(run_test) 11 | 12 | add_subdirectory(lsa) 13 | 14 | -------------------------------------------------------------------------------- /src/unittests/lsa/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(lsa_callgraph_unittests BuildCallGraphTest.cpp) 2 | target_link_libraries(lsa_callgraph_unittests lsa) 3 | run_test(lsa_callgraph_unittests) 4 | 5 | add_executable(lsa_standalone_graph_unittests 6 | StandaloneGraphComputationTest.cpp) 7 | target_link_libraries(lsa_standalone_graph_unittests lsa) 8 | run_test(lsa_standalone_graph_unittests) 9 | 10 | add_executable(lsa_scc_unittests SCCComputationTest.cpp) 11 | target_link_libraries(lsa_scc_unittests lsa_example_scc) 12 | run_test(lsa_scc_unittests) 13 | -------------------------------------------------------------------------------- /src/unittests/lsa/SCCComputationTest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "gtest/gtest.h" 4 | #include "lsa/examples/SCCComputation.h" 5 | 6 | namespace { 7 | /// Return the partition identifier as it is used in the SCC computation. 8 | string partition(string id) { return id + ":" + id; } 9 | 10 | /// Actually runs the test. Creates the graph with specified vertices and calls. 11 | /// Runs the SCC computation and checks whether each vertex ends up in the 12 | /// expected SCC (partition). 13 | void TestSCC(const std::vector &vertices, 14 | const std::vector> &calls, 15 | const std::unordered_map &expected) { 16 | ohmu::lsa::StandaloneGraphBuilder Builder; 17 | 18 | for (const string &vertex : vertices) { 19 | ohmu::lsa::SCCNode node; 20 | Builder.addVertex(vertex, "", node); 21 | } 22 | 23 | for (const auto &call : calls) { 24 | Builder.addCall(call.first, call.second); 25 | } 26 | ohmu::lsa::GraphComputationFactory Factory; 27 | Builder.run(&Factory); 28 | 29 | std::unique_ptr> 30 | Computation(Factory.createComputation()); 31 | for (const auto &Vertex : Builder.getVertices()) { 32 | string expected_partition = expected.find(Vertex.id())->second; 33 | string actual_partition = Computation->output(&Vertex); 34 | EXPECT_EQ(expected_partition, actual_partition) 35 | << "When checking SCC of vertex " << Vertex.id() << ".\n"; 36 | } 37 | } 38 | 39 | } // end namespace 40 | 41 | TEST(SCCComputation, SingletonSCC) { 42 | string aId = "a", bId = "b", cId = "c"; 43 | 44 | // Generated graph: 45 | // 46 | // a b c 47 | // 48 | // SCC #1: {a} 49 | // SCC #2: {b} 50 | // SCC #3: {c} 51 | 52 | std::vector vertices = {aId, bId, cId}; 53 | std::vector> calls = {}; 54 | std::unordered_map expected = { 55 | {aId, partition(aId)}, {bId, partition(bId)}, {cId, partition(cId)}}; 56 | 57 | TestSCC(vertices, calls, expected); 58 | } 59 | 60 | TEST(SCCComputation, OneSCC) { 61 | string aId = "a", bId = "b", cId = "c"; 62 | 63 | // Generated graph: 64 | // 65 | // a ----> b ----> c 66 | // ^ | 67 | // \-------------------/ 68 | // 69 | // SCC #1: {a, b, c} 70 | 71 | std::vector vertices = {aId, bId, cId}; 72 | std::vector> calls = { 73 | {aId, bId}, {bId, cId}, {cId, aId}}; 74 | std::unordered_map expected = { 75 | {aId, partition(aId)}, {bId, partition(aId)}, {cId, partition(aId)}}; 76 | 77 | TestSCC(vertices, calls, expected); 78 | } 79 | 80 | TEST(SCCComputation, TwoSCC) { 81 | string aId = "a", bId = "b", cId = "c", dId = "d", eId = "e", fId = "f", 82 | gId = "g"; 83 | 84 | // Generated graph: 85 | // 86 | // a ----> b ----> c ----> d ----> e 87 | // ^ ^ | ^ | 88 | // | | | | | 89 | // | | | v | 90 | // \------- f <------/ g <------/ 91 | // 92 | // SCC #1: {a, b, c, f} 93 | // SCC #2: {d, e, g} 94 | 95 | std::vector vertices = {aId, bId, cId, dId, eId, fId, gId}; 96 | std::vector> calls = { 97 | {aId, bId}, {bId, cId}, {cId, fId}, {cId, dId}, {dId, eId}, 98 | {dId, gId}, {eId, gId}, {fId, bId}, {fId, aId}, {gId, dId}}; 99 | std::unordered_map expected = { 100 | {aId, partition(aId)}, {bId, partition(aId)}, {cId, partition(aId)}, 101 | {fId, partition(aId)}, {dId, partition(dId)}, {eId, partition(dId)}, 102 | {gId, partition(dId)}}; 103 | 104 | TestSCC(vertices, calls, expected); 105 | } 106 | 107 | int main(int argc, char **argv) { 108 | ::testing::InitGoogleTest(&argc, argv); 109 | return RUN_ALL_TESTS(); 110 | } 111 | -------------------------------------------------------------------------------- /temp.ohmu: -------------------------------------------------------------------------------- 1 | 2 | let x = (let y = 2; y*y); 3 | x+x; 4 | --------------------------------------------------------------------------------