├── .gitignore ├── .gitmodules ├── .travis.yml ├── CMakeLists.txt ├── LICENSE ├── README.md ├── build.bash ├── builtin ├── builtin.asm ├── builtin.c └── c2nasm.bash ├── codegen.bash ├── compiler ├── CMakeLists.txt ├── ast │ ├── ast_node.h │ ├── fwd.h │ ├── helper.h │ └── visitor.h ├── codegen │ ├── helper.h │ ├── instruction_selection.cpp │ ├── instruction_selection.h │ ├── liveness.cpp │ ├── liveness.h │ ├── naive_register_allocation.cpp │ ├── naive_register_allocation.h │ ├── nasm_cfg.cpp │ ├── nasm_cfg.h │ ├── peephole.cpp │ ├── peephole.h │ ├── register_allocation.cpp │ ├── register_allocation.h │ ├── vreg_assignment.cpp │ └── vreg_assignment.h ├── common │ ├── defs.h │ ├── error.h │ └── position.h ├── ir_builder │ ├── build.cpp │ ├── build.h │ ├── builder.cpp │ ├── builder.h │ ├── builder_context.cpp │ ├── builder_context.h │ ├── preprocessor.cpp │ └── preprocessor.h ├── main.cpp ├── optim │ ├── analysis │ │ ├── defuse.h │ │ ├── dominance.cpp │ │ ├── dominance.h │ │ ├── func_attr.cpp │ │ ├── func_attr.h │ │ ├── loop_info.cpp │ │ └── loop_info.h │ ├── codegen_prepare.cpp │ ├── codegen_prepare.h │ ├── constant_propagation.cpp │ ├── constant_propagation.h │ ├── copy_propagation.cpp │ ├── copy_propagation.h │ ├── dead_code_elimination.cpp │ ├── dead_code_elimination.h │ ├── function_inline.cpp │ ├── function_inline.h │ ├── global_const_inline.h │ ├── global_constant_inline.cpp │ ├── global_value_numbering.cpp │ ├── global_value_numbering.h │ ├── helper.cpp │ ├── helper.h │ ├── induction_variable.cpp │ ├── induction_variable.h │ ├── local_value_numbering.cpp │ ├── local_value_numbering.h │ ├── loopinv.cpp │ ├── loopinv.h │ ├── module_simplification.cpp │ ├── module_simplification.h │ ├── opt_pass.h │ ├── optimizer.h │ ├── promote_global_variables.cpp │ ├── promote_global_variables.h │ ├── reassociation.cpp │ ├── reassociation.h │ ├── simplify_cfg.cpp │ ├── simplify_cfg.h │ ├── ssa.cpp │ └── ssa.h ├── parse │ ├── lexer.cpp │ ├── lexer.h │ ├── parser.cpp │ ├── parser.h │ ├── token.cpp │ └── token.h └── semantic │ ├── error.h │ ├── semantic_checker.cpp │ ├── semantic_checker.h │ ├── semantic_context.h │ ├── sym_tbl.cpp │ └── sym_tbl.h ├── doc └── convention.md ├── ir-interpreter ├── CMakeLists.txt ├── interpreter.cpp ├── interpreter.h └── main.cpp ├── ir ├── CMakeLists.txt ├── include │ └── ir │ │ ├── helper.h │ │ ├── ir_inst.h │ │ ├── module.h │ │ ├── printer.h │ │ └── stats.h └── src │ ├── helper.cpp │ ├── module.cpp │ └── printer.cpp ├── mocker-c ├── nasm ├── CMakeLists.txt ├── include │ └── nasm │ │ ├── addr.h │ │ ├── helper.h │ │ ├── inst.h │ │ ├── module.h │ │ ├── printer.h │ │ └── stats.h └── src │ ├── helper.cpp │ ├── module.cpp │ └── printer.cpp ├── optim.bash ├── semantic.bash ├── support ├── CMakeLists.txt ├── defer.h ├── optional.h ├── set_operation.h └── small_map.h └── test ├── build.bash ├── codegen.bash ├── config.json ├── optim.bash ├── run-judge.bash └── semantic.bash /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | cmake-build-*/ 3 | .DS_Store 4 | build*/ 5 | .vscode/ 6 | .vs/ 7 | program.in 8 | a.asm 9 | *.a 10 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "compiler-offline-judge"] 2 | path = compiler-offline-judge 3 | url = https://github.com/Engineev/compiler-offline-judge.git 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: true 2 | language: cpp 3 | dist: trusty 4 | 5 | addons: 6 | apt: 7 | sources: 8 | - ubuntu-toolchain-r-test 9 | packages: 10 | - g++-5 11 | - build-essential 12 | # - libboost-all-dev 13 | 14 | install: 15 | - export CC=gcc-5 16 | - export CXX=g++-5 17 | 18 | matrix: 19 | include: 20 | - compiler: gcc 21 | env: COMPILER=g++-5 22 | - compiler: clang 23 | env: COMPILER=clang++ 24 | 25 | script: 26 | - $COMPILER --version 27 | - mkdir build 28 | - cd build 29 | - cmake -DCMAKE_CXX_COMPILER=$COMPILER .. && make 30 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8) 2 | project(mocker) 3 | 4 | option(ONLINE_JUDGE_SUPPORT "" OFF) 5 | 6 | add_subdirectory(support) 7 | add_subdirectory(ir) 8 | add_subdirectory(nasm) 9 | add_subdirectory(compiler) 10 | add_subdirectory(ir-interpreter) 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Yunwei Ren 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mocker 2 | 3 | [![Build Status](https://travis-ci.com/Engineev/mocker.svg?token=t7LhMb4BZCM8Q58kCnsH&branch=master)](https://travis-ci.com/Engineev/mocker) 4 | 5 | Hello. If you are reading this, then probably you are taking a compiler course 6 | and going to implement your own compiler, and going to read my code. However, 7 | the quality of code varies. Hence, I wrote this down as a guide and I wish it, 8 | as well as my code, would be helpful. 9 | 10 | ## Overview 11 | 12 | This project consists of three main directories: `compiler/`, `ir/` and 13 | `ir-interpreter`. Most of the code is in `compiler/`. The implemtation of 14 | my IR is in `ir/` and I also wrote a naive interpreter for my IR. It is only 15 | a legacy issue that there are three directories. 16 | 17 | ## Frontend 18 | 19 | By frontend, I mean the parsing, semantic analysis and IRgen. The code of this 20 | part is in the corresponding subdirectories of `compiler/`. 21 | 22 | ### Parser 23 | I handcrafted a LL recursive descent parser. Some ad hoc hacks are used to 24 | simplify the implementation. In most cases, you can use parser generators such 25 | as ANTLR to generate your LR parser. The only reason I handcrafted a parser is 26 | that I thought it would take less time. Anyway, if you are going to implement 27 | your own LL parser, maybe you could read the comments in `parse/parser.h`. I 28 | wrote down the grammar there, which might be helpful. 29 | 30 | ### AST 31 | My implementation of the AST is NOT good. When I wrote that part, I was not 32 | familiar with that Java-like design pattern. Therefore, I used public fields 33 | instead of make them read-only using getter. You could see my implementation of 34 | my IR to understand what I mean here. Except this issue, there are no big 35 | problems. 36 | 37 | Meanwhile, instead of recording everything in the AST node structure, I assign 38 | each node a unique identifier and record the need information such as types in 39 | separate containers. I think this design is better since it is much more 40 | flexible and elegant (personal opinion). 41 | 42 | ### Semantic analysis 43 | This part is quite straightforward. 44 | 45 | ### IRGen 46 | I believe my code is quite clear. On some subtle things such as translating 47 | array constructions, I wrote detailed comments (Cf. 48 | `Builder::translateNewArray`). 49 | 50 | 51 | ## Optimization 52 | 53 | TODO 54 | 55 | ## Codegen 56 | 57 | TODO 58 | 59 | -------------------------------------------------------------------------------- /build.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | cd "$(dirname "$0")" 5 | 6 | # cd builtin 7 | # ./c2nasm.bash ./builtin.c 8 | # cd .. 9 | 10 | # ir 11 | # g++ -c -O2 -std=c++14 -I./support \ 12 | # -I./ir/include/ir ./ir/src/*.cpp 13 | # ar rvs libir.a *.o 14 | # rm *.o 15 | 16 | # nasm 17 | # g++ -c -O2 -std=c++14 -I./support -DONLINE_JUDGE_SUPPORT \ 18 | # -I./nasm/include/nasm ./nasm/src/*.cpp 19 | # ar rvs libnasm.a *.o 20 | # rm *.o 21 | 22 | # compiler 23 | # g++ -DDISABLE_FORWARD_REFERENCE_FOR_GLOBAL_VAR \ 24 | # -O2 -std=c++14 -I./support -I./ir/include -I./nasm/include -I./compiler \ 25 | # ./compiler/ir_builder/*.cpp \ 26 | # ./compiler/optim/*.cpp ./compiler/optim/analysis/*.cpp \ 27 | # ./compiler/codegen/*.cpp \ 28 | # ./compiler/parse/*.cpp ./compiler/semantic/*.cpp ./compiler/main.cpp \ 29 | # -o mocker-c \ 30 | # libir.a libnasm.a 31 | 32 | 33 | -------------------------------------------------------------------------------- /builtin/builtin.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | void *__alloc(size_t sz) { return malloc(sz); } 7 | 8 | void *__string__add(void *lhs, void *rhs) { 9 | uint64_t lhs_len = *((int64_t *)lhs - 1); 10 | uint64_t rhs_len = *((int64_t *)rhs - 1); 11 | uint64_t length = lhs_len + rhs_len; 12 | 13 | char *res_inst_ptr = (char *)malloc(length + 8); 14 | *(int64_t *)res_inst_ptr = length; 15 | res_inst_ptr += 8; 16 | 17 | memcpy(res_inst_ptr, lhs, (size_t)lhs_len); 18 | memcpy((char *)res_inst_ptr + lhs_len, rhs, (size_t)rhs_len); 19 | return res_inst_ptr; 20 | } 21 | 22 | void *__string__substring(void *ptr, int64_t left, int64_t right) { 23 | int64_t len = right - left + 1; 24 | char *res_inst_ptr = (char *)malloc(len + 8); 25 | *(int64_t *)(res_inst_ptr) = len; 26 | res_inst_ptr += 8; 27 | 28 | char *src_content_ptr = (char *)ptr + left; 29 | memcpy(res_inst_ptr, src_content_ptr, (size_t)len); 30 | return res_inst_ptr; 31 | } 32 | 33 | int64_t __string__ord(void *ptr, int64_t pos) { 34 | return (int64_t) * ((char *)ptr + pos); 35 | } 36 | 37 | int64_t __string__parseInt(void *ptr) { 38 | uint64_t len = *((int64_t *)ptr - 1); 39 | char buffer[256]; 40 | buffer[len] = 0; 41 | memcpy(buffer, ptr, len); 42 | return strtol(buffer, NULL, 10); 43 | } 44 | 45 | int64_t __string__equal(void *lhs, void *rhs) { 46 | uint64_t lhs_len = *((int64_t *)lhs - 1); 47 | uint64_t rhs_len = *((int64_t *)rhs - 1); 48 | if (lhs_len != rhs_len) 49 | return 0; 50 | int64_t res = memcmp(lhs, rhs, lhs_len); 51 | return !res; 52 | } 53 | 54 | int64_t __string__inequal(void *lhs, void *rhs) { 55 | return !__string__equal(lhs, rhs); 56 | } 57 | 58 | int64_t __string__less(void *lhs, void *rhs) { 59 | uint64_t lhs_len = *((int64_t *)lhs - 1); 60 | uint64_t rhs_len = *((int64_t *)rhs - 1); 61 | uint64_t len = lhs_len < rhs_len ? lhs_len : rhs_len; 62 | int64_t res = memcmp(lhs, rhs, len); 63 | if (res == 0) 64 | return lhs_len < rhs_len ? 1 : 0; 65 | return res < 0; 66 | } 67 | 68 | int64_t __string__less_equal(void *lhs, void *rhs) { 69 | return __string__equal(lhs, rhs) || __string__less(lhs, rhs); 70 | } 71 | 72 | void __string___ctor_(void *ptr) { *(uint64_t *)ptr = 0; } 73 | 74 | int64_t getInt() { 75 | char c = getchar(); 76 | int64_t neg = 0; 77 | while (c < '0' || c > '9') { 78 | if (c == '-') 79 | neg = 1; 80 | c = getchar(); 81 | } 82 | int64_t num = c - '0'; 83 | c = getchar(); 84 | while (c >= '0' && c <= '9') { 85 | num = num * 10 + c - '0'; 86 | c = getchar(); 87 | } 88 | if (neg) 89 | return -num; 90 | return num; 91 | } 92 | 93 | void print(void *ptr) { 94 | uint64_t len = *((int64_t *)ptr - 1); 95 | printf("%.*s", (int64_t)len, (char *)ptr); 96 | } 97 | 98 | void _printInt(int64_t num) { 99 | if (num == 0) 100 | putchar('0'); 101 | if (num < 0) { 102 | num = -num; 103 | putchar('-'); 104 | } 105 | int64_t digits[10], len = 0; 106 | while (num > 0) { 107 | digits[len++] = num % 10; 108 | num /= 10; 109 | } 110 | for (int64_t i = len - 1; i >= 0; --i) 111 | putchar('0' + digits[i]); 112 | } 113 | 114 | void _printlnInt(int64_t x) { 115 | _printInt(x); 116 | putchar('\n'); 117 | } 118 | 119 | void *getString() { 120 | char buffer[256]; 121 | scanf("%256s", buffer); 122 | 123 | uint64_t len = strlen(buffer); 124 | char *res_inst_ptr = (char *)malloc(8 + len); 125 | *(int64_t *)res_inst_ptr = len; 126 | res_inst_ptr += 8; 127 | memcpy(res_inst_ptr, buffer, len); 128 | return res_inst_ptr; 129 | } 130 | 131 | void *toString(int64_t val) { 132 | char buffer[64]; 133 | sprintf(buffer, "%ld", val); 134 | int64_t len = strlen(buffer); 135 | 136 | char *res_inst_ptr = (char *)malloc(8 + len); 137 | *(int64_t *)(res_inst_ptr) = len; 138 | res_inst_ptr += 8; 139 | memcpy(res_inst_ptr, buffer, len); 140 | return res_inst_ptr; 141 | } 142 | 143 | void println(void *ptr) { 144 | print(ptr); 145 | printf("\n"); 146 | } 147 | -------------------------------------------------------------------------------- /builtin/c2nasm.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # thanks to http://stackoverflow.com/a/20743090 3 | # thanks to https://github.com/diogovk/c2nasm 4 | # install objconv: https://github.com/vertis/objconv 5 | # 6 | # $1: source code 7 | 8 | set -e 9 | C_FILE="$1" 10 | BASE_NAME="${C_FILE%.*}" 11 | O_FILE="$BASE_NAME.o" 12 | NASM_FILE="$BASE_NAME.asm" 13 | gcc -Werror=implicit-function-declaration -fno-asynchronous-unwind-tables -O3 -c -o "$O_FILE" "$C_FILE" 14 | objconv -fnasm "$O_FILE" "$NASM_FILE" 15 | sed -i 's|st(0)|st0 |g' "$NASM_FILE" 16 | sed -i 's|noexecute| |g' "$NASM_FILE" 17 | sed -i 's|execute| |g' "$NASM_FILE" 18 | sed -i 's|: function||g' "$NASM_FILE" 19 | sed -i 's|?_|L_|g' "$NASM_FILE" 20 | sed -i -n '/SECTION .eh_frame/q;p' "$NASM_FILE" 21 | sed -i 's|;.*||g' "$NASM_FILE" 22 | sed -i 's/^M//g' "$NASM_FILE" 23 | sed -i 's|\s\+$||g' "$NASM_FILE" 24 | sed -i 's|align=1||g' "$NASM_FILE" 25 | rm "$O_FILE" -------------------------------------------------------------------------------- /codegen.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | cd "$(dirname "$0")" 5 | 6 | cat > program.in 7 | ./mocker-c ./program.in > a.asm 8 | cat a.asm ./builtin/builtin.asm 9 | 10 | -------------------------------------------------------------------------------- /compiler/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(HEADERS 2 | ast/ast_node.h 3 | ast/visitor.h 4 | ast/fwd.h 5 | ast/helper.h 6 | 7 | codegen/helper.h 8 | codegen/instruction_selection.h 9 | codegen/liveness.h 10 | codegen/naive_register_allocation.h 11 | codegen/nasm_cfg.h 12 | codegen/peephole.h 13 | codegen/register_allocation.h 14 | codegen/vreg_assignment.h 15 | 16 | common/defs.h 17 | common/error.h 18 | common/position.h 19 | 20 | ir_builder/build.h 21 | ir_builder/builder.h 22 | ir_builder/builder_context.h 23 | ir_builder/preprocessor.h 24 | 25 | optim/analysis/defuse.h 26 | optim/analysis/dominance.h 27 | optim/analysis/func_attr.h 28 | optim/analysis/loop_info.h 29 | optim/codegen_prepare.h 30 | optim/constant_propagation.h 31 | optim/copy_propagation.h 32 | optim/dead_code_elimination.h 33 | optim/function_inline.h 34 | optim/global_const_inline.h 35 | optim/global_value_numbering.h 36 | optim/helper.h 37 | optim/induction_variable.h 38 | optim/local_value_numbering.h 39 | optim/loopinv.cpp 40 | optim/module_simplification.h 41 | optim/opt_pass.h 42 | optim/optimizer.h 43 | optim/promote_global_variables.h 44 | optim/reassociation.h 45 | optim/simplify_cfg.h 46 | optim/ssa.h 47 | 48 | parse/lexer.h 49 | parse/parser.h 50 | parse/token.h 51 | 52 | semantic/error.h 53 | semantic/semantic_checker.h 54 | semantic/semantic_context.h 55 | semantic/sym_tbl.h 56 | ) 57 | set(SOURCES 58 | codegen/instruction_selection.cpp 59 | codegen/liveness.cpp 60 | codegen/naive_register_allocation.cpp 61 | codegen/nasm_cfg.cpp 62 | codegen/peephole.cpp 63 | codegen/register_allocation.cpp 64 | codegen/vreg_assignment.cpp 65 | 66 | ir_builder/build.cpp 67 | ir_builder/builder.cpp 68 | ir_builder/builder_context.cpp 69 | ir_builder/preprocessor.cpp 70 | 71 | optim/analysis/dominance.cpp 72 | optim/analysis/func_attr.cpp 73 | optim/analysis/loop_info.cpp 74 | optim/codegen_prepare.cpp 75 | optim/constant_propagation.cpp 76 | optim/copy_propagation.cpp 77 | optim/dead_code_elimination.cpp 78 | optim/function_inline.cpp 79 | optim/global_constant_inline.cpp 80 | optim/global_value_numbering.cpp 81 | optim/helper.cpp 82 | optim/induction_variable.cpp 83 | optim/local_value_numbering.cpp 84 | optim/loopinv.cpp 85 | optim/module_simplification.cpp 86 | optim/promote_global_variables.cpp 87 | optim/reassociation.cpp 88 | optim/simplify_cfg.cpp 89 | optim/ssa.cpp 90 | 91 | parse/lexer.cpp 92 | parse/parser.cpp 93 | parse/token.cpp 94 | 95 | semantic/semantic_checker.cpp 96 | semantic/sym_tbl.cpp 97 | 98 | main.cpp 99 | ) 100 | 101 | add_executable(${PROJECT_NAME}-c ${HEADERS} ${SOURCES}) 102 | target_include_directories(${PROJECT_NAME}-c 103 | PRIVATE ${CMAKE_CURRENT_LIST_DIR} 104 | ) 105 | target_link_libraries(${PROJECT_NAME}-c 106 | PRIVATE ${PROJECT_NAME}-ir ${PROJECT_NAME}-support ${PROJECT_NAME}-nasm 107 | ) 108 | target_compile_features(${PROJECT_NAME}-c 109 | PRIVATE cxx_std_14 110 | ) 111 | target_compile_definitions(${PROJECT_NAME}-c 112 | PRIVATE 113 | CMAKE_SOURCE_DIR="${CMAKE_SOURCE_DIR}" 114 | DISABLE_FORWARD_REFERENCE_FOR_GLOBAL_VAR 115 | ) 116 | if (UNIX) 117 | target_compile_options(${PROJECT_NAME}-c PRIVATE -o2 -Wall) 118 | endif () 119 | -------------------------------------------------------------------------------- /compiler/ast/fwd.h: -------------------------------------------------------------------------------- 1 | #ifndef MOCKER_FWD_H 2 | #define MOCKER_FWD_H 3 | 4 | namespace mocker { 5 | namespace ast { 6 | 7 | class Visitor; 8 | class ConstVisitor; 9 | 10 | struct ASTNode; 11 | 12 | struct Identifier; 13 | 14 | struct Type; 15 | struct NonarrayType; 16 | struct BuiltinType; 17 | struct UserDefinedType; 18 | struct ArrayType; 19 | 20 | struct Expression; 21 | struct LiteralExpr; 22 | struct IntLitExpr; 23 | struct StringLitExpr; 24 | struct NullLitExpr; 25 | struct BoolLitExpr; 26 | struct IdentifierExpr; 27 | struct UnaryExpr; 28 | struct BinaryExpr; 29 | struct FuncCallExpr; 30 | struct NewExpr; 31 | 32 | struct Statement; 33 | struct VarDeclStmt; 34 | struct ExprStmt; 35 | struct ReturnStmt; 36 | struct ContinueStmt; 37 | struct BreakStmt; 38 | struct CompoundStmt; 39 | struct IfStmt; 40 | struct WhileStmt; 41 | struct ForStmt; 42 | struct EmptyStmt; 43 | 44 | struct Declaration; 45 | struct VarDecl; 46 | struct FuncDecl; 47 | struct ClassDecl; 48 | 49 | struct ASTRoot; 50 | 51 | } // namespace ast 52 | } // namespace mocker 53 | 54 | #endif // MOCKER_FWD_H 55 | -------------------------------------------------------------------------------- /compiler/ast/helper.h: -------------------------------------------------------------------------------- 1 | #ifndef MOCKER_HELPER_H 2 | #define MOCKER_HELPER_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "ast_node.h" 9 | 10 | namespace mocker { 11 | namespace ast { 12 | 13 | // Get the inner most type from a arrayType. 14 | // If [type] is not an arrayType, return directly. 15 | inline std::shared_ptr 16 | stripType(const std::shared_ptr &type) { 17 | if (auto res = std::dynamic_pointer_cast(type)) 18 | return res; 19 | auto baseType = std::static_pointer_cast(type)->baseType; 20 | return stripType(baseType); 21 | } 22 | 23 | // Get the identifier of a type 24 | inline std::string getTypeIdentifier(const std::shared_ptr &type) { 25 | if (auto ptr = std::dynamic_pointer_cast(type)) 26 | return getTypeIdentifier(ptr->baseType) + "[]"; 27 | 28 | if (auto ptr = std::dynamic_pointer_cast(type)) 29 | return ptr->name->val; 30 | auto ptr = std::static_pointer_cast(type); 31 | assert(ptr); 32 | switch (ptr->type) { 33 | case BuiltinType::Bool: 34 | return "bool"; 35 | case BuiltinType::Int: 36 | return "int"; 37 | case BuiltinType::String: 38 | return "string"; 39 | case BuiltinType::Null: 40 | return "_null"; 41 | } 42 | assert(false); 43 | } 44 | 45 | } // namespace ast 46 | } // namespace mocker 47 | 48 | namespace mocker { 49 | namespace mk_ast { 50 | 51 | inline std::shared_ptr ident(std::string name) { 52 | return std::make_shared(std::move(name)); 53 | } 54 | 55 | inline std::shared_ptr tyInt() { 56 | static auto res = std::make_shared(ast::BuiltinType::Int); 57 | return res; 58 | } 59 | 60 | inline std::shared_ptr tyString() { 61 | static auto res = 62 | std::make_shared(ast::BuiltinType::String); 63 | return res; 64 | } 65 | 66 | inline std::shared_ptr tyBool() { 67 | static auto res = std::make_shared(ast::BuiltinType::Bool); 68 | return res; 69 | } 70 | 71 | inline std::shared_ptr tyNull() { 72 | static auto res = std::make_shared(ast::BuiltinType::Null); 73 | return res; 74 | } 75 | 76 | inline std::shared_ptr 77 | param(const std::shared_ptr &ty) { 78 | return std::make_shared(ty, ident("")); 79 | } 80 | 81 | inline std::vector> emptyParams() { 82 | return {}; 83 | } 84 | 85 | inline std::shared_ptr body() { return nullptr; } 86 | 87 | } // namespace mk_ast 88 | } // namespace mocker 89 | 90 | #endif // MOCKER_HELPER_H 91 | -------------------------------------------------------------------------------- /compiler/ast/visitor.h: -------------------------------------------------------------------------------- 1 | #ifndef MOCKER_AST_VISITOR_H 2 | #define MOCKER_AST_VISITOR_H 3 | 4 | #include "fwd.h" 5 | 6 | #include 7 | #include 8 | 9 | namespace mocker { 10 | namespace ast { 11 | 12 | namespace detail { 13 | 14 | template class VisitorBase { 15 | private: 16 | template struct Dispatch; 17 | 18 | template struct Dispatch { 19 | using type = typename std::add_const::type; 20 | }; 21 | 22 | template struct Dispatch { 23 | using type = typename std::remove_const::type; 24 | }; 25 | 26 | template using disp = typename Dispatch::type &; 27 | 28 | public: 29 | virtual void operator()(disp) const { assert(DefaultPass); } 30 | 31 | virtual void operator()(disp) const { assert(DefaultPass); } 32 | virtual void operator()(disp) const { assert(DefaultPass); } 33 | virtual void operator()(disp) const { assert(DefaultPass); } 34 | 35 | virtual void operator()(disp) const { assert(DefaultPass); } 36 | virtual void operator()(disp) const { assert(DefaultPass); } 37 | virtual void operator()(disp) const { assert(DefaultPass); } 38 | virtual void operator()(disp) const { assert(DefaultPass); } 39 | virtual void operator()(disp) const { assert(DefaultPass); } 40 | virtual void operator()(disp) const { assert(DefaultPass); } 41 | virtual void operator()(disp) const { assert(DefaultPass); } 42 | virtual void operator()(disp) const { assert(DefaultPass); } 43 | virtual void operator()(disp) const { assert(DefaultPass); } 44 | 45 | virtual void operator()(disp) const { assert(DefaultPass); } 46 | virtual void operator()(disp) const { assert(DefaultPass); } 47 | virtual void operator()(disp) const { assert(DefaultPass); } 48 | virtual void operator()(disp) const { assert(DefaultPass); } 49 | virtual void operator()(disp) const { assert(DefaultPass); } 50 | virtual void operator()(disp) const { assert(DefaultPass); } 51 | virtual void operator()(disp) const { assert(DefaultPass); } 52 | virtual void operator()(disp) const { assert(DefaultPass); } 53 | virtual void operator()(disp) const { assert(DefaultPass); } 54 | virtual void operator()(disp) const { assert(DefaultPass); } 55 | 56 | virtual void operator()(disp) const { assert(DefaultPass); } 57 | virtual void operator()(disp) const { assert(DefaultPass); } 58 | virtual void operator()(disp) const { assert(DefaultPass); } 59 | 60 | virtual void operator()(disp) const { assert(DefaultPass); } 61 | }; 62 | 63 | } // namespace detail 64 | 65 | class Visitor : public detail::VisitorBase {}; 66 | 67 | class ConstVisitor : public detail::VisitorBase {}; 68 | 69 | } // namespace ast 70 | } // namespace mocker 71 | 72 | #endif // MOCKER_AST_VISITOR_H 73 | -------------------------------------------------------------------------------- /compiler/codegen/helper.h: -------------------------------------------------------------------------------- 1 | #ifndef MOCKER_CODEGEN_HELPER_H 2 | #define MOCKER_CODEGEN_HELPER_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "nasm/module.h" 9 | #include "set_operation.h" 10 | 11 | namespace mocker { 12 | 13 | using LineIter = std::list::const_iterator; 14 | 15 | template void removeElement(Set &s, const K &val) { 16 | auto iter = s.find(val); 17 | assert(iter != s.end()); 18 | s.erase(iter); 19 | } 20 | 21 | } // namespace mocker 22 | 23 | #endif // MOCKER_CODEGEN_HELPER_H 24 | -------------------------------------------------------------------------------- /compiler/codegen/instruction_selection.h: -------------------------------------------------------------------------------- 1 | #ifndef MOCKER_INSTRUCTION_SELECTION_H 2 | #define MOCKER_INSTRUCTION_SELECTION_H 3 | 4 | #include "ir/module.h" 5 | #include "nasm/module.h" 6 | 7 | namespace mocker { 8 | 9 | nasm::Module runInstructionSelection(const ir::Module &irModule); 10 | } 11 | 12 | #endif // MOCKER_INSTRUCTION_SELECTION_H 13 | -------------------------------------------------------------------------------- /compiler/codegen/liveness.cpp: -------------------------------------------------------------------------------- 1 | #include "liveness.h" 2 | 3 | #include "nasm/helper.h" 4 | 5 | namespace mocker { 6 | namespace { 7 | 8 | using nasm::RegMap; 9 | using nasm::RegSet; 10 | 11 | // UEVar contains the upward-exposed variables, namely, the ones that are used 12 | // in this block before any redefinition. 13 | // VarKill contains all variables defined in this block. 14 | std::pair buildUEVarAndVarKill(const NasmCfg::Node &node) { 15 | RegSet ueVar, varKill; 16 | LineIter bbBeg, bbEnd; 17 | std::tie(bbBeg, bbEnd) = node.getLines(); 18 | for (auto iter = bbBeg; iter != bbEnd; ++iter) { 19 | if (!iter->inst) 20 | continue; 21 | auto used = nasm::getUsedRegs(iter->inst); 22 | for (auto ® : used) { 23 | if (varKill.find(reg) == varKill.end()) 24 | ueVar.emplace(reg); 25 | } 26 | auto defined = nasm::getDefinedRegs(iter->inst); 27 | for (auto ® : defined) 28 | varKill.emplace(reg); 29 | } 30 | return {ueVar, varKill}; 31 | } 32 | 33 | } // namespace 34 | 35 | std::unordered_map buildLiveOut(const NasmCfg &cfg) { 36 | // build the UEVar and VarKill 37 | std::unordered_map ueVar, varKill; 38 | for (auto &kv : cfg.getNodes()) 39 | std::tie(ueVar[kv.first], varKill[kv.first]) = 40 | buildUEVarAndVarKill(kv.second); 41 | 42 | std::unordered_map liveOut; 43 | 44 | bool changed = true; 45 | while (changed) { 46 | changed = false; 47 | for (auto &kv : cfg.getNodes()) { 48 | auto &bbLiveOut = liveOut[kv.first]; 49 | auto oldSz = bbLiveOut.size(); 50 | 51 | // recompute bbLiveOut 52 | // LiveOut(n) = 53 | // Union_{succ m} (UEVar(m) union (LiveOut(m) intersect not VarKill(m))) 54 | for (auto &succ : kv.second.getSuccs()) { 55 | auto s = ueVar.at(succ); 56 | auto varKillOfSucc = varKill.at(succ); 57 | for (auto &v : liveOut[succ]) { 58 | if (varKillOfSucc.find(v) == varKillOfSucc.end()) { 59 | s.emplace(v); 60 | } 61 | } 62 | 63 | for (auto &v : s) 64 | bbLiveOut.emplace(v); 65 | } 66 | 67 | auto newSz = bbLiveOut.size(); 68 | if (oldSz != newSz) 69 | changed = true; 70 | } 71 | } 72 | 73 | return liveOut; 74 | } 75 | } // namespace mocker -------------------------------------------------------------------------------- /compiler/codegen/liveness.h: -------------------------------------------------------------------------------- 1 | #ifndef MOCKER_LIVENESS_H 2 | #define MOCKER_LIVENESS_H 3 | 4 | #include 5 | #include 6 | 7 | #include "helper.h" 8 | #include "nasm/inst.h" 9 | #include "nasm_cfg.h" 10 | 11 | namespace mocker { 12 | 13 | // For each block b, LiveOut(b) is the collections of variables that are live 14 | // at the end of b. 15 | std::unordered_map buildLiveOut(const NasmCfg &cfg); 16 | 17 | } // namespace mocker 18 | 19 | #endif // MOCKER_LIVENESS_H 20 | -------------------------------------------------------------------------------- /compiler/codegen/naive_register_allocation.h: -------------------------------------------------------------------------------- 1 | #ifndef MOCKER_NAIVE_REGISTER_ALLOCATION_H 2 | #define MOCKER_NAIVE_REGISTER_ALLOCATION_H 3 | 4 | #include "nasm/module.h" 5 | 6 | namespace mocker { 7 | 8 | nasm::Module allocateRegistersNaively(const nasm::Module &module); 9 | } 10 | 11 | #endif // MOCKER_NAIVE_REGISTER_ALLOCATION_H 12 | -------------------------------------------------------------------------------- /compiler/codegen/nasm_cfg.cpp: -------------------------------------------------------------------------------- 1 | #include "nasm_cfg.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace mocker { 9 | void NasmCfg::addNode(LineIter beg, LineIter end) { 10 | auto label = beg->label; 11 | assert(!label.empty()); 12 | Node node{beg, end}; 13 | auto success = nodes.emplace(std::make_pair(label, std::move(node))).second; 14 | assert(success); 15 | } 16 | 17 | const std::unordered_map & 18 | NasmCfg::getNodes() const { 19 | return nodes; 20 | } 21 | 22 | void NasmCfg::buildGraph() { 23 | for (auto &kv : nodes) { 24 | auto &node = kv.second; 25 | auto u = kv.first; 26 | 27 | std::vector> terminators; 28 | for (auto riter = std::make_reverse_iterator(node.getLines().second);; 29 | ++riter) { 30 | auto inst = riter->inst; 31 | if (!inst) { 32 | break; 33 | } 34 | if (!nasm::dyc(inst) && !nasm::dyc(inst) && 35 | !nasm::dyc(inst)) 36 | break; 37 | terminators.emplace_back(inst); 38 | } 39 | std::reverse(terminators.begin(), terminators.end()); 40 | 41 | if (terminators.empty()) { 42 | auto fallThrough = node.getLines().second->label; 43 | assert(!fallThrough.empty()); 44 | node.succs.emplace_back(fallThrough); 45 | continue; 46 | } 47 | 48 | for (auto &terminator : terminators) { 49 | if (auto j = nasm::dyc(terminator)) { 50 | auto v = j->getLabel()->getName(); 51 | node.succs.emplace_back(v); 52 | break; 53 | } 54 | if (auto r = nasm::dyc(terminator)) { 55 | break; 56 | } 57 | if (auto cj = nasm::dyc(terminator)) { 58 | auto v = cj->getLabel()->getName(); 59 | node.succs.emplace_back(v); 60 | continue; 61 | } 62 | } 63 | if (auto p = nasm::dyc(terminators.back())) { 64 | auto fallThrough = node.getLines().second->label; 65 | assert(!fallThrough.empty()); 66 | node.succs.emplace_back(fallThrough); 67 | } 68 | } 69 | } 70 | 71 | } // namespace mocker 72 | 73 | namespace mocker { 74 | namespace { 75 | 76 | std::vector splitFunctionIntoBlocks(LineIter funcBeg, 77 | LineIter funcEnd) { 78 | assert(!funcBeg->inst); 79 | std::vector res; 80 | for (auto iter = funcBeg; iter != funcEnd; ++iter) { 81 | if (iter->label.empty()) 82 | continue; 83 | res.emplace_back(iter); 84 | } 85 | return res; 86 | } 87 | 88 | } // namespace 89 | 90 | NasmCfg buildNasmCfg(LineIter funcBeg, LineIter funcEnd) { 91 | NasmCfg cfg; 92 | 93 | auto blockIters = splitFunctionIntoBlocks(funcBeg, funcEnd); 94 | blockIters.emplace_back(funcEnd); 95 | // add the nodes 96 | for (std::size_t i = 0; i < blockIters.size() - 1; ++i) 97 | cfg.addNode(blockIters[i], blockIters[i + 1]); 98 | 99 | cfg.buildGraph(); 100 | 101 | return cfg; 102 | } 103 | 104 | } // namespace mocker 105 | -------------------------------------------------------------------------------- /compiler/codegen/nasm_cfg.h: -------------------------------------------------------------------------------- 1 | #ifndef MOCKER_NASM_CFG_H 2 | #define MOCKER_NASM_CFG_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "helper.h" 10 | #include "nasm/module.h" 11 | 12 | namespace mocker { 13 | 14 | class NasmCfg { 15 | public: 16 | class Node; 17 | 18 | NasmCfg() = default; 19 | NasmCfg(const NasmCfg &) = default; 20 | NasmCfg(NasmCfg &&) = default; 21 | NasmCfg &operator=(const NasmCfg &) = default; 22 | NasmCfg &operator=(NasmCfg &&) = default; 23 | ~NasmCfg() = default; 24 | 25 | public: 26 | class Node { 27 | public: 28 | std::pair getLines() const { 29 | return {blockBeg, blockEnd}; 30 | } 31 | 32 | const std::vector &getSuccs() const { return succs; } 33 | 34 | const std::string &getLabel() const { 35 | assert(!blockBeg->label.empty()); 36 | return blockBeg->label; 37 | } 38 | 39 | private: 40 | friend class NasmCfg; 41 | Node(LineIter beg, LineIter end) : blockBeg(beg), blockEnd(end) {} 42 | 43 | LineIter blockBeg, blockEnd; 44 | std::vector succs; 45 | }; 46 | 47 | void addNode(LineIter beg, LineIter end); 48 | 49 | void buildGraph(); 50 | 51 | const std::unordered_map &getNodes() const; 52 | 53 | private: 54 | std::unordered_map nodes; 55 | }; 56 | 57 | NasmCfg buildNasmCfg(LineIter funcBeg, LineIter funcEnd); 58 | 59 | } // namespace mocker 60 | 61 | #endif // MOCKER_NASM_CFG_H 62 | -------------------------------------------------------------------------------- /compiler/codegen/peephole.cpp: -------------------------------------------------------------------------------- 1 | #include "peephole.h" 2 | 3 | #include "nasm/addr.h" 4 | #include "nasm/helper.h" 5 | 6 | namespace mocker { 7 | namespace { 8 | 9 | void useIncDec(nasm::Module &module) { 10 | for (auto &line : module.getSection(".text").getLines()) { 11 | auto &inst = line.inst; 12 | if (!inst) 13 | continue; 14 | auto incDec = nasm::dyc(inst); 15 | if (!incDec) 16 | continue; 17 | auto op = incDec->getType(); 18 | if (op != nasm::BinaryInst::Add && op != nasm::BinaryInst::Sub) 19 | continue; 20 | auto lit = nasm::dyc(incDec->getRhs()); 21 | if (!lit || lit->getVal() != 1) 22 | continue; 23 | inst = std::make_shared( 24 | op == nasm::BinaryInst::Add ? nasm::UnaryInst::Inc 25 | : nasm::UnaryInst::Dec, 26 | nasm::dyc(incDec->getLhs())); 27 | } 28 | } 29 | 30 | // I1: a += b ; <- This can be removed 31 | // I2: a = c 32 | void removeUnusedValue(nasm::Module &module) { 33 | auto &lines = module.getSection(".text").getLines(); 34 | 35 | for (auto iter = lines.begin(), end = lines.end(); iter != end; ++iter) { 36 | auto &inst = iter->inst; 37 | if (!inst) 38 | continue; 39 | const auto mov = nasm::dyc(inst); 40 | if (!mov) 41 | continue; 42 | auto dest = nasm::dyc(mov->getDest()); 43 | if (!dest) 44 | continue; 45 | auto used = nasm::getUsedRegs(mov); 46 | bool flag = true; 47 | for (auto ® : used) { 48 | if (reg->getIdentifier() == dest->getIdentifier()) { 49 | flag = false; 50 | break; 51 | } 52 | } 53 | if (!flag) 54 | continue; 55 | 56 | for (auto riter = std::make_reverse_iterator(iter), rend = lines.rend(); 57 | riter != rend; ++riter) { 58 | auto &rInst = riter->inst; 59 | if (!rInst) 60 | break; 61 | std::shared_ptr rDest = nullptr; 62 | if (auto p = nasm::dyc(rInst)) { 63 | rDest = nasm::dyc(p->getDest()); 64 | } 65 | if (auto p = nasm::dyc(rInst)) { 66 | rDest = p->getReg(); 67 | } 68 | if (auto p = nasm::dyc(rInst)) { 69 | rDest = nasm::dyc(p->getLhs()); 70 | } 71 | if (auto p = nasm::dyc(rInst)) { 72 | rDest = nasm::dyc(p->getDest()); 73 | } 74 | if (!rDest || rDest->getIdentifier() != dest->getIdentifier()) 75 | break; 76 | rInst = std::make_shared(); 77 | } 78 | } 79 | 80 | lines.remove_if([](const nasm::Line &line) { 81 | return line.label.empty() && nasm::dyc(line.inst); 82 | }); 83 | } 84 | 85 | } // namespace 86 | 87 | nasm::Module runPeepholeOptimization(const nasm::Module &module) { 88 | auto res = module; 89 | useIncDec(res); 90 | removeUnusedValue(res); 91 | return res; 92 | } 93 | 94 | } // namespace mocker 95 | -------------------------------------------------------------------------------- /compiler/codegen/peephole.h: -------------------------------------------------------------------------------- 1 | #ifndef MOCKER_PEEPHOLE_H 2 | #define MOCKER_PEEPHOLE_H 3 | 4 | #include "nasm/module.h" 5 | 6 | namespace mocker { 7 | 8 | nasm::Module runPeepholeOptimization(const nasm::Module &module); 9 | } 10 | 11 | #endif // MOCKER_PEEPHOLE_H 12 | -------------------------------------------------------------------------------- /compiler/codegen/vreg_assignment.cpp: -------------------------------------------------------------------------------- 1 | #include "vreg_assignment.h" 2 | 3 | #include "ir/helper.h" 4 | 5 | #include 6 | 7 | namespace mocker { 8 | 9 | void VRegAssignment::init(const ir::FunctionModule &func) { 10 | for (auto &bb : func.getBBs()) { 11 | for (auto &inst : bb.getInsts()) { 12 | auto dest = ir::getDest(inst); 13 | if (!dest) 14 | continue; 15 | mp[dest] = std::make_shared("vir_" + 16 | dest->getIdentifier() + "_"); 17 | } 18 | } 19 | 20 | for (auto &bb : func.getBBs()) { 21 | for (auto &inst : bb.getInsts()) { 22 | auto phi = ir::dyc(inst); 23 | if (!phi) 24 | continue; 25 | 26 | for (auto &option : phi->getOptions()) { 27 | auto reg = ir::dycLocalReg(option.first); 28 | assert(reg); 29 | if (mp[reg] != mp[phi->getDest()]) { 30 | mp[reg] = mp[phi->getDest()]; 31 | } 32 | } 33 | } 34 | } 35 | } 36 | 37 | } // namespace mocker -------------------------------------------------------------------------------- /compiler/codegen/vreg_assignment.h: -------------------------------------------------------------------------------- 1 | // Assign each IR Register an NASM virtual register. 2 | // Phi-functions are handled in this phase. 3 | 4 | #ifndef MOCKER_VREG_ASSIGNMENT_H 5 | #define MOCKER_VREG_ASSIGNMENT_H 6 | 7 | #include 8 | 9 | #include "ir/module.h" 10 | #include "nasm/module.h" 11 | 12 | namespace mocker { 13 | 14 | class VRegAssignment { 15 | public: 16 | void init(const ir::FunctionModule &module); 17 | 18 | void set(const std::shared_ptr &irReg, 19 | const std::shared_ptr &vReg) { 20 | mp[irReg] = vReg; 21 | } 22 | 23 | const std::shared_ptr & 24 | getVReg(const std::shared_ptr ®) const { 25 | return mp.at(reg); 26 | } 27 | 28 | private: 29 | ir::RegMap> mp; 30 | }; 31 | 32 | } // namespace mocker 33 | 34 | #endif // MOCKER_VREG_ASSIGNMENT_H 35 | -------------------------------------------------------------------------------- /compiler/common/defs.h: -------------------------------------------------------------------------------- 1 | #ifndef MOCKER_DEFS_H 2 | #define MOCKER_DEFS_H 3 | 4 | #include 5 | #include 6 | 7 | namespace mocker { 8 | 9 | using StrIter = std::string::const_iterator; 10 | 11 | using Integer = std::int64_t; 12 | 13 | } // namespace mocker 14 | 15 | #endif // MOCKER_DEFS_H 16 | -------------------------------------------------------------------------------- /compiler/common/error.h: -------------------------------------------------------------------------------- 1 | #ifndef MOCKER_ERROR_H 2 | #define MOCKER_ERROR_H 3 | 4 | #include 5 | 6 | #include "defs.h" 7 | #include "position.h" 8 | 9 | namespace mocker { 10 | 11 | class CompileError : public std::exception { 12 | public: 13 | CompileError(Position beg, Position end) : beg(beg), end(end) { 14 | using std::to_string; 15 | msg = "An error occurs between line " + to_string(beg.line) + ", column " + 16 | to_string(beg.col) + " and line " + to_string(end.line) + 17 | ", column " + to_string(end.col); 18 | } 19 | explicit CompileError(PosPair pos) : CompileError(pos.first, pos.second) {} 20 | 21 | const char *what() const noexcept override { return msg.c_str(); } 22 | 23 | const std::pair position() const { return {beg, end}; } 24 | 25 | private: 26 | std::string msg; 27 | Position beg, end; 28 | }; 29 | 30 | } // namespace mocker 31 | 32 | #endif // MOCKER_ERROR_H 33 | -------------------------------------------------------------------------------- /compiler/common/position.h: -------------------------------------------------------------------------------- 1 | #ifndef MOCKER_POSITION_H 2 | #define MOCKER_POSITION_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "common/defs.h" 9 | 10 | namespace mocker { 11 | 12 | struct Position { 13 | Position() = default; 14 | Position(std::size_t line, std::size_t col) : line(line), col(col) {} 15 | Position(StrIter bufferBeg, StrIter bufferEnd, StrIter tok) { 16 | assert(bufferBeg <= tok && tok < bufferEnd); 17 | line = 1; 18 | StrIter bol = bufferBeg; // begin of a line 19 | for (auto iter = bufferBeg; iter != tok; ++iter) { 20 | if (*iter == '\n') { 21 | ++line; 22 | bol = iter + 1; 23 | } 24 | } 25 | col = tok - bol; 26 | } 27 | Position(const Position &) = default; 28 | Position &operator=(const Position &) = default; 29 | 30 | bool operator==(const Position &rhs) const { 31 | return line == rhs.line && col == rhs.col; 32 | } 33 | bool operator!=(const Position &rhs) const { return !(*this == rhs); } 34 | 35 | std::size_t line = 0, col = 0; 36 | }; 37 | 38 | using PosPair = std::pair; 39 | 40 | } // namespace mocker 41 | 42 | #endif // MOCKER_POSITION_H 43 | -------------------------------------------------------------------------------- /compiler/ir_builder/build.cpp: -------------------------------------------------------------------------------- 1 | #include "build.h" 2 | 3 | #include "builder.h" 4 | #include "builder_context.h" 5 | #include "preprocessor.h" 6 | 7 | #include 8 | 9 | namespace mocker { 10 | 11 | ir::Module buildIR(const std::shared_ptr &ast, 12 | const SemanticContext &semanticCtx) { 13 | renameASTIdentifiers(ast, semanticCtx.scopeResiding, 14 | semanticCtx.associatedDecl, semanticCtx.exprType); 15 | auto pureFuncs = findPureFunctions(ast); 16 | auto redundantNode = findRedundantNodes(ast, semanticCtx.exprType, pureFuncs); 17 | 18 | ir::BuilderContext IRCtx(semanticCtx.exprType, redundantNode); 19 | ast->accept(ir::Builder(IRCtx)); 20 | auto res = IRCtx.getResult(); 21 | 22 | // TODO: In current version, function inline must be enabled. 23 | // implement some simple builtin functions 24 | { // #_array_#size 25 | ir::FunctionModule func("#_array_#size", {"ptr"}); 26 | auto bb = func.pushBackBB(); 27 | auto ptr = std::make_shared("0"); 28 | auto tmp = func.makeTempLocalReg(); 29 | bb->appendInst(std::make_shared( 30 | tmp, ir::ArithBinaryInst::Sub, ptr, 31 | std::make_shared(8))); 32 | auto funcRes = func.makeTempLocalReg(); 33 | bb->appendInst(std::make_shared(funcRes, tmp)); 34 | bb->appendInst(std::make_shared(funcRes)); 35 | res.overwriteFunc(func.getIdentifier(), func); 36 | } 37 | { // #string#length 38 | ir::FunctionModule func("#string#length", {"ptr"}); 39 | auto bb = func.pushBackBB(); 40 | auto ptr = std::make_shared("0"); 41 | auto tmp = func.makeTempLocalReg(); 42 | bb->appendInst(std::make_shared( 43 | tmp, ir::ArithBinaryInst::Sub, ptr, 44 | std::make_shared(8))); 45 | auto funcRes = func.makeTempLocalReg(); 46 | bb->appendInst(std::make_shared(funcRes, tmp)); 47 | bb->appendInst(std::make_shared(funcRes)); 48 | res.overwriteFunc(func.getIdentifier(), func); 49 | } 50 | /* 51 | { // #string#ord ( this pos ) 52 | ir::FunctionModule func("#string#ord", {"ptr", "pos"}); 53 | auto bb = func.pushBackBB(); 54 | auto ptr = std::make_shared("0"); 55 | auto pos = std::make_shared("1"); 56 | auto tmp = func.makeTempLocalReg(); 57 | bb->appendInst(std::make_shared( 58 | tmp, ir::ArithBinaryInst::Add, ptr, pos)); 59 | auto loadRes = func.makeTempLocalReg(); 60 | bb->appendInst(std::make_shared(loadRes, tmp)); 61 | auto funcRes = func.makeTempLocalReg(); 62 | bb->appendInst(std::make_shared( 63 | funcRes, ir::ArithBinaryInst::BitAnd, loadRes, 64 | std::make_shared(255))); 65 | bb->appendInst(std::make_shared(funcRes)); 66 | res.overwriteFunc(func.getIdentifier(), func); 67 | } 68 | */ 69 | return res; 70 | } 71 | 72 | } // namespace mocker 73 | -------------------------------------------------------------------------------- /compiler/ir_builder/build.h: -------------------------------------------------------------------------------- 1 | #ifndef MOCKER_BUILD_H 2 | #define MOCKER_BUILD_H 3 | 4 | #include "ast/ast_node.h" 5 | #include "ir/module.h" 6 | #include "semantic/semantic_context.h" 7 | 8 | namespace mocker { 9 | 10 | ir::Module buildIR(const std::shared_ptr &ast, 11 | const SemanticContext &semanticCtx); 12 | 13 | } // namespace mocker 14 | 15 | #endif // MOCKER_BUILD_H 16 | -------------------------------------------------------------------------------- /compiler/ir_builder/builder.h: -------------------------------------------------------------------------------- 1 | #ifndef MOCKER_BUILDER_H 2 | #define MOCKER_BUILDER_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "ast/ast_node.h" 9 | #include "ast/visitor.h" 10 | #include "builder_context.h" 11 | 12 | namespace mocker { 13 | namespace ir { 14 | 15 | class Builder : virtual public ast::ConstVisitor { 16 | public: 17 | explicit Builder(BuilderContext &ctx) : ctx(ctx) {} 18 | 19 | void operator()(const ast::Identifier &node) const override { assert(false); } 20 | void operator()(const ast::BuiltinType &node) const override { 21 | assert(false); 22 | } 23 | void operator()(const ast::UserDefinedType &node) const override { 24 | assert(false); 25 | } 26 | void operator()(const ast::ArrayType &node) const override { assert(false); } 27 | 28 | void operator()(const ast::IntLitExpr &node) const override; 29 | 30 | void operator()(const ast::BoolLitExpr &node) const override; 31 | 32 | void operator()(const ast::StringLitExpr &node) const override; 33 | 34 | void operator()(const ast::NullLitExpr &node) const override; 35 | 36 | void operator()(const ast::IdentifierExpr &node) const override; 37 | 38 | void operator()(const ast::UnaryExpr &node) const override; 39 | 40 | void operator()(const ast::BinaryExpr &node) const override; 41 | 42 | void operator()(const ast::FuncCallExpr &node) const override; 43 | 44 | void operator()(const ast::NewExpr &node) const override; 45 | 46 | void operator()(const ast::VarDeclStmt &node) const override; 47 | 48 | void operator()(const ast::ExprStmt &node) const override; 49 | 50 | void operator()(const ast::ReturnStmt &node) const override; 51 | 52 | void operator()(const ast::ContinueStmt &node) const override; 53 | 54 | void operator()(const ast::BreakStmt &node) const override; 55 | 56 | void operator()(const ast::CompoundStmt &node) const override; 57 | 58 | void operator()(const ast::IfStmt &node) const override; 59 | 60 | void operator()(const ast::WhileStmt &node) const override; 61 | 62 | void operator()(const ast::ForStmt &node) const override; 63 | 64 | void operator()(const ast::EmptyStmt &node) const override {} 65 | 66 | void operator()(const ast::VarDecl &node) const override { assert(false); } 67 | 68 | void operator()(const ast::FuncDecl &node) const override; 69 | 70 | void operator()(const ast::ClassDecl &node) const override; 71 | 72 | void operator()(const ast::ASTRoot &node) const override; 73 | 74 | private: 75 | void addClassLayout(const ast::ClassDecl &node) const; 76 | 77 | std::shared_ptr