Compiler README AUTHORS: Sean James <seanjames777@gmail.com> Kurt Mueller <kurtmueller42@gmail.com> 411 BACKGROUND: We rewrote our compiler from scratch in C++, for several reasons. First, we were no longer happy with the organization of our OCaml compiler. We wanted to use C++'s object orientation, template system, etc. to improve the organization of the compiler, as OCaml's module and functor system was getting in the way. We also wanted to gain the performance advantage of native C++ code over OCaml code. Most importantly, we wanted to use the LLVM IR libraries directly. This made working with LLVM much easier, because its built- in assertions could catch errors, and we didn't need to worry about formatting the IR output. Furthermore, we could use LLVM's optimization infrastructure, etc. We did not rewrite our x86_64 backend in C++, because we were focused on the LLVM portion. Frank Pfenning approved this on Piazza, so our compiler simply invokes llc to compile our LLVM output for x86_64. Note that this compiler has also been used as a starting point for a project for another class (mostly retargeting the compiler to CUDA). The changes have been merged back into the main compiler to ease the maintenence effort, so there are some portions of the compiler and infrastructure which are not related to 411. Note that the code makes use of a few patterns. The first is the use of shared pointers in most cases, to avoid memory management problems. Second, std::dynamic_pointer_cast<>() is used as a substitute for basic pattern matching. This looks somewhat strange, but is fairly idiomatic in other projects like LLVM itself, although it also uses its own RTTI system. Third, we have chosen not to implement the bulk of the compiler the way "traditional" OOP would suggest. Specifically, we do not, for example, place type checking, code generation, printing, etc. code for every type of AST node in the AST classes themselves. This is because we wanted to keep concerns such as type checking, code generation, etc. organized by the logical progression of the compiler, rather than mixing those concerns in the AST classes. This means that we need to do some heavy pattern matching, but also means that, for example, all of the type checking code is in the same file. We also make fairly heavy use of assertions and exceptions, rather than propgating error codes. Most of these are caught at the top level and printed as errors to stdout. This simplifies the code, but adds the overhead of exception handling code to the executable. Also note that some of the design of this compiler, which is essentially just a frontend for LLVM at this point, is subsequently somewhat inspired by Clang. Our AST is fairly similar, although much simpler. We also use a macro generated "AST visitor" class to implement some of our static analyses. ORGANIZATION: The source tree, as far as 411 is concerned, is organized as follows: include/ Header files, corresponding to the files in src/ src/ Source C++ files ast/ Abstract syntax tree class heirarchy decl/ Top-level function, type, and struct declarations expr/ Expressions stmt/ Statements type/ Types codegen/ Code generator, translates AST to LLVM IR parser/ Flex/Bison parser and lexer files statics/ Type checker and other statics rules driver/ driver.py Our own test driver, not up to date runtime/ host_rt.c Our runtime, which is copied to l4lib.c tests/ Our own tests which test extended compiler capabilities 411_wrapper.py Python wrapper around the compiler, which translates 411 command line arguments to our own arguments. Makefile 411 makefile which does not require CMake 411 BUILD: The 411 compiler can be built directly with the provided makefile, through: make l4c The general build process is described below. The CUDA backend is not currently up to date. BUILD: Build using CMake. You will need to install some dependencies first: - Install CMake - Insatll Flex and Bison. These can be installed via, for example, MacPorts. - Install LLVM, and specify the LLVM_PATH variable - The driver requires Python - The CUDA backend requires that CUDA is installed. To support a CUDA backend, define "PTX_BACKEND" and set the CUDA_PATH variable. Otherwise, omit these options. Then, create a build directory, configure CMake, and build: mkdir build cd build cmake -DCMAKE_INSTALL_PREFIX="../out/" \ -DLLVM_PATH="/opt/local/libexec/llvm-3.5/bin/" \ -DPTX_BACKEND="TRUE" -DCUDA_PATH="/Developer/NVIDIA/CUDA-6.5/" ../ make && make install 411 TESTING: The ./driver.pl infrastructure works as expected. A secondary test system is described below, although it is not currently up to date. TESTING: Use the test driver to run tests. The build system will create a copy of the test driver script configured with the correct paths in your build directory, so you can run tests as follows. Note that you will need to use 'make install' above. 'make' alone is insufficient. ./driver.py To run tests in device mode: ./driver.py --device To run specific tests, provide the name of the files in tests/: ./driver.py testCall1.cc testCall2.cc To add more tests, edit the CONFIGURATION section of driver/driver.py, and rerun 'make && make install'. DOCUMENTATION: To build documentation with doxygen, simply: make doc