diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index f921b81..fca425f 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,14 +1,11 @@ { "name": "SysY Compiler Dev", "image": "maxxing/compiler-dev", - "workspaceMount": "source=${localWorkspaceFolder},target=${localWorkspaceFolder},type=bind", "workspaceFolder": "${localWorkspaceFolder}", - "mounts": [ "source=${localEnv:HOME}/.ssh,target=/home/ubuntu/.ssh,type=bind,consistency=cached" ], - "customizations": { "vscode": { "extensions": [ @@ -16,7 +13,9 @@ "ms-vscode.cmake-tools", "twxs.cmake", "daohong-emilio.yash", - "mikawi.flex-language" + "mikawi.flex-language", + "aaron-bond.better-comments", + "eamodio.gitlens" ], "settings": { "clangd.path": "/usr/bin/clangd-21", @@ -35,9 +34,7 @@ } } }, - "remoteUser": "ubuntu", - "features": { "ghcr.io/devcontainers/features/common-utils:2": { "username": "ubuntu", @@ -46,13 +43,12 @@ "configureZshAsDefaultShell": true } }, - - "postCreateCommand": "sudo apt-get update && sudo apt-get install -y clangd-21", - + // "postCreateCommand": "sudo apt-get update && sudo apt-get install -y clangd-21", + "postCreateCommand": "sudo apt-get update && sudo apt-get install -y clangd-21 python3-pip && sudo pip3 install ninja --break-system-packages", "runArgs": [ "--network=host", "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined" ] -} +} \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6e1a431..34de4cf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,35 +1,60 @@ name: compiler auto test run-name: ${{ github.actor }} is testing out compiler function 🚀 + on: push: + paths: + - 'project/**' pull_request: workflow_dispatch: - + jobs: - build: + build_compiler: runs-on: ubuntu-latest container: image: maxxing/compiler-dev defaults: run: working-directory: ./project + outputs: + artifact_name: compiler-binary steps: - - name: Check out repository code - uses: actions/checkout@v5 - - name: test koopa + - uses: actions/checkout@v5 + + - name: Install Ninja run: | - autotest -w debug -koopa -s lv1 . - autotest -w debug -koopa -s lv3 . - autotest -w debug -koopa -s lv4 . - autotest -w debug -koopa -s lv5 . - autotest -w debug -koopa -s lv6 . - autotest -w debug -koopa -s lv7 . - autotest -w debug -koopa -s lv8 . - - name: test riscv + apt-get update && apt-get install -y ninja-build + ninja --version + + - name: Build Binary + run: make + + test_compiler: + needs: build_compiler + runs-on: ubuntu-latest + container: + image: maxxing/compiler-dev + strategy: + fail-fast: false + matrix: + test_type: [koopa, riscv] + defaults: + run: + working-directory: ./project + steps: + - uses: actions/checkout@v5 + + - name: Install Ninja run: | - autotest -w debug -riscv -s lv1 . - autotest -w debug -riscv -s lv3 . - autotest -w debug -riscv -s lv4 . - autotest -w debug -riscv -s lv5 . - autotest -w debug -riscv -s lv6 . - autotest -w debug -riscv -s lv7 . + apt-get update && apt-get install -y ninja-build + ninja --version + + - name: Run Test + run: autotest -w debug -${{ matrix.test_type }} . + + - name: Upload Test Logs + if: failure() + uses: actions/upload-artifact@v5 + with: + name: test-logs-${{ matrix.test_type }} + path: project/debug/ \ No newline at end of file diff --git a/project/.clang-format b/project/.clang-format index f8fa673..75066e4 100644 --- a/project/.clang-format +++ b/project/.clang-format @@ -1 +1,2 @@ -AllowShortCaseLabelsOnASingleLine: true \ No newline at end of file +AllowShortCaseLabelsOnASingleLine: true +AllowShortIfStatementsOnASingleLine: AllIfsAndElse \ No newline at end of file diff --git a/project/Makefile b/project/Makefile index efcc69c..6982fb6 100644 --- a/project/Makefile +++ b/project/Makefile @@ -8,7 +8,7 @@ PWD := $(shell pwd) all: build config: - cmake -S . -B $(BUILD_DIR) -G Ninja -DCMAKE_MAKE_PROGRAM=$(PWD)/ninja + cmake -S . -B $(BUILD_DIR) -G Ninja -DCMAKE_CXX_COMPILER=clang++ build: config cmake --build $(BUILD_DIR) -j12 @@ -28,7 +28,8 @@ run: ./cmake-build/compiler -riscv ./tests/hello.c -o ./debug/hello.asm test: - cd cmake-build && ctest && cd .. + python3 scripts/test_runner.py koopa + python3 scripts/test_runner.py riscv docker-build: docker run --rm \ diff --git a/project/include/backend/backend.cppm b/project/include/backend/backend.cppm index cc72a90..c6c5149 100644 --- a/project/include/backend/backend.cppm +++ b/project/include/backend/backend.cppm @@ -81,14 +81,14 @@ private: */ auto visit(const koopa_raw_return_t &ret) -> void; auto visit(const koopa_raw_binary_t &binary) -> void; - auto visit(const koopa_raw_integer_t &integer) -> void; auto visit(const koopa_raw_jump_t &jump) -> void; auto visit(const koopa_raw_branch_t &branch) -> void; auto visit(const koopa_raw_load_t &load) -> void; auto visit(const koopa_raw_store_t &store) -> void; auto visit(const koopa_raw_call_t &call) -> void; - auto visit(const koopa_raw_func_arg_ref_t &func_arg_ref) -> void; auto visit(const koopa_raw_global_alloc_t &global_alloc) -> void; + auto visit(const koopa_raw_get_elem_ptr_t &get_elem_ptr) -> void; + auto visit(const koopa_raw_get_ptr_t &get_ptr) -> void; /** @} */ }; } // namespace backend \ No newline at end of file diff --git a/project/include/ir/ast.cppm b/project/include/ir/ast.cppm index 89cc1d6..9c42650 100644 --- a/project/include/ir/ast.cppm +++ b/project/include/ir/ast.cppm @@ -15,6 +15,7 @@ export module ir.ast; import symbol_table; import ir_builder; +import ir.type; /** * @brief Namespace containing all AST related classes and functions. @@ -59,6 +60,7 @@ public: class LValAST; class BlockAST; +class InitValStmtAST; //* enum class // clang-format off @@ -98,23 +100,32 @@ public: std::string btype; std::string ident; bool is_const; + bool is_ptr; + std::vector> indices; /** * @brief Constructs a function parameter. * @param _btype The type of the parameter (e.g., "int"). * @param _ident The name of the parameter. * @param _is_const Whether the parameter is constant. */ - FuncParamAST(std::string _btype, std::string _ident, bool _is_const) - : btype(std::move(_btype)), ident(std::move(_ident)), - is_const(_is_const) {} + FuncParamAST(std::string _btype, std::string _ident, bool _is_const, + bool _is_ptr, std::vector> _indices) + : btype(std::move(_btype)), ident(std::move(_ident)), is_const(_is_const), + is_ptr(_is_ptr), indices(std::move(_indices)) {} auto dump(int depth) const -> void override; auto codeGen(ir::KoopaBuilder &builder) const -> std::string override; + auto toKoopa(ir::KoopaBuilder &builder) const -> std::string; }; +/** + * @brief Base class for definition AST nodes (variables, functions, arrays). + */ +class DefAST : public BaseAST {}; + /** * @brief AST node for function definitions. */ -class FuncDefAST : public BaseAST { +class FuncDefAST : public DefAST { public: ~FuncDefAST() override; std::string btype; @@ -135,10 +146,29 @@ public: auto codeGen(ir::KoopaBuilder &builder) const -> std::string override; }; +/** + * @brief AST node for array definitions. + * + * Handles both constant and non-constant array definitions, including their sizes and initializers. + */ +class ArrayDefAST : public DefAST { +public: + ~ArrayDefAST() override; + bool is_const; + std::string ident; + std::vector> array_suffix; + std::unique_ptr init_val; + ArrayDefAST(bool _is_const, std::string _ident, + std::vector> _array_suffix, + InitValStmtAST *_init_val); + auto dump(int depth) const -> void override; + auto codeGen(ir::KoopaBuilder &builder) const -> std::string override; +}; + /** * @brief AST node for a single variable definition. */ -class DefAST : public BaseAST { +class ScalarDefAST : public DefAST { public: bool is_const; std::string ident; @@ -149,7 +179,7 @@ public: * @param _ident The name of the variable. * @param _initVal The initial value expression (optional). */ - DefAST(bool _is_const, std::string _ident, BaseAST *_initVal) + ScalarDefAST(bool _is_const, std::string _ident, BaseAST *_initVal) : is_const(_is_const), ident(std::move(_ident)) { if (_initVal) { initVal.reset(static_cast(_initVal)); @@ -206,6 +236,38 @@ public: auto dump(int depth) const -> void override; auto codeGen(ir::KoopaBuilder &builder) const -> std::string override; }; + +/** + * @brief Represents an initialization value statement in the abstract syntax + * tree. + * + * This class is used to model initialization values, which can be either a + * single expression or a list of nested initializers (e.g., for arrays or + * aggregate types). + */ +class InitValStmtAST : public StmtAST { +public: + std::unique_ptr expr; + std::vector> initialize_list; + /** + * @brief Constructs an InitValStmtAST object. + * + * @param _expr Pointer to a BaseAST that will be statically cast to ExprAST. + * It represents the primary initialization expression. + * @param _initialize_list Vector of unique pointers to InitValStmtAST for + * nested initialization. This list is moved into the member to avoid copying. + */ + InitValStmtAST(BaseAST *_expr, + std::vector> _initialize_list) + : initialize_list(std::move(_initialize_list)) { + expr.reset(static_cast(_expr)); + } + + auto dump(int depth) const -> void override; + auto codeGen(ir::KoopaBuilder &builder) const -> std::string override; + auto flatten(std::shared_ptr, ir::KoopaBuilder &) const + -> std::vector; +}; /** @} */ /** @name Data Action @@ -242,9 +304,9 @@ public: * @param _btype The type of the variables. * @param _defs The list of variable definitions. */ - DeclAST(bool _isConst, std::string _btype, + DeclAST(bool _is_const, std::string _btype, std::vector> _defs) - : is_const(_isConst), btype(std::move(_btype)), defs(std::move(_defs)) {} + : is_const(_is_const), btype(std::move(_btype)), defs(std::move(_defs)) {} auto dump(int depth) const -> void override; auto codeGen(ir::KoopaBuilder &builder) const -> std::string override; }; @@ -345,11 +407,13 @@ public: class LValAST : public ExprAST { public: std::string ident; + std::vector> indices; /** * @brief Constructs an LVal node. * @param _ident The variable name. */ - LValAST(std::string _ident) : ident(std::move(_ident)) {}; + LValAST(std::string _ident, std::vector> _indices) + : ident(std::move(_ident)), indices(std::move(_indices)) {}; auto dump(int depth) const -> void override; auto codeGen(ir::KoopaBuilder &builder) const -> std::string override; auto CalcValue(ir::KoopaBuilder &builder) const -> int override; diff --git a/project/include/ir/ir_builder.cppm b/project/include/ir/ir_builder.cppm index 249fa5b..d986463 100644 --- a/project/include/ir/ir_builder.cppm +++ b/project/include/ir/ir_builder.cppm @@ -151,8 +151,6 @@ public: */ auto resetCount() -> void { count_reg = 0; - count_name = 0; - count_label = 0; _is_block_closed = false; } diff --git a/project/include/ir/symbol_table.cppm b/project/include/ir/symbol_table.cppm index ac1e9b9..fc1d79d 100644 --- a/project/include/ir/symbol_table.cppm +++ b/project/include/ir/symbol_table.cppm @@ -33,7 +33,7 @@ struct Symbol { std::string irName; ///< IR-level name (e.g., "@x_1") std::shared_ptr type; ///< Data type of the symbol SymbolKind kind; ///< Variable or Function - bool is_const; ///< True if it's a compile-time constant + bool is_const; ///< True if it's a compile-time constant int constValue; ///< The value of the constant, if applicable }; @@ -94,8 +94,8 @@ public: * @param val Initial value if constant. */ auto define(const std::string &name, const std::string &irName, - std::shared_ptr type, SymbolKind kind, - bool is_const, int val = 0) -> void { + std::shared_ptr type, SymbolKind kind, bool is_const, + int val = 0) -> void { if (scopes.back().contains(name)) { Log::panic("Semantic Error: Redefinition of " + name); @@ -127,7 +127,7 @@ public: * @return Symbol* Pointer to the symbol if found, else nullptr. */ auto lookup(const std::string &name) -> Symbol * { - for (auto &scope : std::views::reverse(scopes)) { + for (auto &scope : scopes | std::views::reverse) { auto it = scope.find(name); if (it != scope.end()) { return &(it->second); diff --git a/project/include/ir/type.cppm b/project/include/ir/type.cppm index 94bcfa7..e981692 100644 --- a/project/include/ir/type.cppm +++ b/project/include/ir/type.cppm @@ -6,9 +6,9 @@ module; #include +#include #include #include -#include export module ir.type; @@ -24,13 +24,14 @@ public: /** * @brief Returns a string representation of the type. */ - virtual auto debug() const -> std::string = 0; + virtual auto toKoopa() const -> std::string = 0; virtual auto is_int() const -> bool { return false; } virtual auto is_void() const -> bool { return false; } virtual auto is_ptr() const -> bool { return false; } virtual auto is_bool() const -> bool { return false; } virtual auto is_float() const -> bool { return false; } + virtual auto is_array() const -> bool { return false; } }; /** @@ -38,7 +39,7 @@ public: */ class IntType : public Type { public: - auto debug() const -> std::string override { return "i32"; } + auto toKoopa() const -> std::string override { return "i32"; } auto is_int() const -> bool override { return true; } /** @@ -55,7 +56,7 @@ public: */ class VoidType : public Type { public: - auto debug() const -> std::string override { return "void"; } + auto toKoopa() const -> std::string override { return "void"; } auto is_void() const -> bool override { return true; } /** @@ -72,7 +73,7 @@ public: */ class BoolType : public Type { public: - auto debug() const -> std::string override { return "bool"; } + auto toKoopa() const -> std::string override { return "bool"; } auto is_bool() const -> bool override { return true; } /** @@ -89,7 +90,7 @@ public: */ class FloatType : public Type { public: - auto debug() const -> std::string override { return "float"; } + auto toKoopa() const -> std::string override { return "float"; } auto is_float() const -> bool override { return true; } /** @@ -108,16 +109,42 @@ class PtrType : public Type { public: std::shared_ptr target; ///< The type being pointed to. - /** - * @brief Constructs a pointer type. - * @param t The base type. - */ PtrType(std::shared_ptr t) : target(t) {}; - auto debug() const -> std::string override { - return fmt::format("*{}", target->debug()); + auto toKoopa() const -> std::string override { + return fmt::format("*{}", target->toKoopa()); } auto is_ptr() const -> bool override { return true; } + + static auto get(std::shared_ptr t) -> std::shared_ptr { + return std::make_shared(t); + } +}; + +class ArrayType : public Type { +public: + std::shared_ptr base; + int len; + + ArrayType(std::shared_ptr t, int _len) : base(t), len(_len) {} + + auto toKoopa() const -> std::string override { + return fmt::format("[{}, {}]", base->toKoopa(), len); + } + auto is_array() const -> bool override { return true; } + + static auto get(std::shared_ptr _base, int _len) + -> std::shared_ptr { + static std::map, int>, + std::shared_ptr> + cache; + auto key = std::make_pair(_base, _len); + if (cache.contains(key)) { + return cache[key]; + } + auto instance = std::make_shared(_base, _len); + return cache[key] = instance; + } }; } // namespace type \ No newline at end of file diff --git a/project/ninja b/project/ninja deleted file mode 100755 index 7b0bdf3..0000000 Binary files a/project/ninja and /dev/null differ diff --git a/project/scripts/test_runner.py b/project/scripts/test_runner.py index 31a3a35..78c5b08 100755 --- a/project/scripts/test_runner.py +++ b/project/scripts/test_runner.py @@ -37,6 +37,10 @@ def read_file(path): with open(path, 'r') as f: return f.read() +def normalize(text): + lines = [line.rstrip() for line in text.strip().splitlines()] + return "\n".join(lines) + def run_cmd(cmd, input_str=None): """运行 shell 命令并获取 stdout, stderr, returncode""" try: @@ -68,7 +72,7 @@ def run_test_case(mode, src_file): # 读取输入和预期输出 stdin_content = read_file(input_file) - expected_output = read_file(expect_file).strip() + expected_output = normalize(read_file(expect_file)) print(f"Testing {name_no_ext} ... ", end='', flush=True) @@ -126,7 +130,7 @@ def run_test_case(mode, src_file): # 先检查输出值是否匹配 out, err, ret = run_cmd(cmd_run, stdin_content) - actual_output = out.strip() + actual_output = normalize(out) if actual_output != expected_output: # 检查返回值和 .out 是否匹配 actual_with_ret = (actual_output + "\n" + str(ret)).strip() diff --git a/project/src/backend/backend.cpp b/project/src/backend/backend.cpp index 91855c8..be17bbe 100644 --- a/project/src/backend/backend.cpp +++ b/project/src/backend/backend.cpp @@ -104,6 +104,7 @@ module; #include "koopa.h" #include #include +#include #include #include @@ -134,10 +135,114 @@ template auto make_span(const koopa_raw_slice_t &slice) { return std::span( reinterpret_cast(slice.buffer), slice.len); } + + +/** + * @brief Calculates the size (in bytes) of a given Koopa type. + * + * @param ty The Koopa type to measure. + * @return u_int32_t The size in bytes. Returns 0 for unknown types. + */ +auto get_type_size(koopa_raw_type_t ty) -> u_int32_t { + switch (ty->tag) { + + case KOOPA_RTT_INT32: return 4; + case KOOPA_RTT_POINTER: return 4; + + case KOOPA_RTT_ARRAY: + return ty->data.array.len * get_type_size(ty->data.array.base); + + default: return 0; + } +} + +/** + * @brief Checks if an integer value fits within a 12-bit signed range. + * + * RISC-V immediate values for many instructions (like ADDI, LW, SW) are + * 12-bit signed integers [-2048, 2047]. + * + * @param val The value to check. + * @return true if -2048 <= val <= 2047, false otherwise. + */ +auto isIn12BitRange(int val) -> bool { return val >= -2048 && val <= 2047; } + +/** + * @brief Emits a RISC-V `addi` instruction (or equivalent sequence). + * + * If `imm` fits in 12 bits, emits a single `addi`. + * Otherwise, loads `imm` into a temporary register and adds it. + * + * @param buffer The output assembly buffer. + * @param rd Destination register. + * @param rs Source register. + * @param imm Immediate value to add. + */ +auto emitAddi(std::string &buffer, std::string_view rd, std::string_view rs, + int imm) -> void { + if (isIn12BitRange(imm)) { + buffer += fmt::format(" addi {}, {}, {}\n", rd, rs, imm); + } else { + buffer += fmt::format(" li t2, {}\n", imm); + buffer += fmt::format(" add {}, {}, t2\n", rd, rs); + } +} + +/** + * @brief Emits a RISC-V `lw` instruction (or equivalent sequence). + * + * Handles large offsets by calculating the address in a temporary register first. + * + * @param buffer The output assembly buffer. + * @param rd Destination register. + * @param rs Base address register. + * @param offset Byte offset from base. + */ +auto emitLw(std::string &buffer, std::string_view rd, std::string_view rs, + int offset) -> void { + if (isIn12BitRange(offset)) { + buffer += fmt::format(" lw {}, {}({})\n", rd, offset, rs); + } else { + buffer += fmt::format(" li t2, {}\n", offset); + buffer += fmt::format(" add t2, t2, {}\n", rs); + buffer += fmt::format(" lw {}, 0(t2)\n", rd); + } +} + +/** + * @brief Emits a RISC-V `sw` instruction (or equivalent sequence). + * + * Handles large offsets by calculating the address in a temporary register first. + * + * @param buffer The output assembly buffer. + * @param src Source register (value to store). + * @param base Base address register. + * @param offset Byte offset from base. + */ +auto emitSw(std::string &buffer, std::string_view src, std::string_view base, + int offset) -> void { + if (isIn12BitRange(offset)) { + buffer += fmt::format(" sw {}, {}({})\n", src, offset, base); + } else { + buffer += fmt::format(" li t2, {}\n", offset); + buffer += fmt::format(" add t2, t2, {}\n", base); + buffer += fmt::format(" sw {}, 0(t2)\n", src); + } +} + } // namespace backend using namespace backend; +using namespace std::views; +/** + * @brief Entry point for code generation. + * + * Traverses the `program`'s global values and function definitions, + * generating code for each. All output is appended to the internal `buffer`. + * + * @param program The root node of the Koopa IR. + */ auto TargetCodeGen::visit(const koopa_raw_program_t &program) -> void { for (const auto value : make_span(program.values)) { visit(value); @@ -158,8 +263,7 @@ auto TargetCodeGen::visit(const koopa_raw_program_t &program) -> void { * @param func The Koopa function to process. */ auto TargetCodeGen::visit(koopa_raw_function_t func) -> void { - if (func->bbs.len == 0) - return; + if (func->bbs.len == 0) return; reset(); @@ -167,20 +271,26 @@ auto TargetCodeGen::visit(koopa_raw_function_t func) -> void { bool has_callee = false; for (const auto bb : make_span(func->bbs)) { for (const auto inst : make_span(bb->insts)) { - // If the instruction produces a value (not void), allocate space on - // stack. In this simple backend, every value-producing instruction gets - // its own slot. - if (inst->ty->tag != KOOPA_RTT_UNIT) { - stkMap[inst] = local_frame_size; - local_frame_size += 4; - } // If this function calls another, we need to save RA and potentially // allocate space for outgoing arguments. if (inst->kind.tag == KOOPA_RVT_CALL) { has_callee = true; ra_size = 4; // Keep track of the maximum number of arguments in any call. - args_size = std::max(args_size, inst->kind.data.call.args.len); + args_size = std::max(args_size, inst->kind.data.call.args.len); + } + + if (inst->ty->tag == KOOPA_RTT_UNIT) continue; + // If the instruction produces a value (not void), allocate space on + // stack. In this simple backend, every value-producing instruction gets + // its own slot. + stkMap[inst] = local_frame_size; + + if (inst->kind.tag == KOOPA_RVT_ALLOC) { + int size = get_type_size(inst->ty->data.pointer.base); + local_frame_size += size; + } else { + local_frame_size += 4; } } } @@ -201,12 +311,12 @@ auto TargetCodeGen::visit(koopa_raw_function_t func) -> void { buffer += fmt::format("{}:\n", func->name + 1); if (stk_frame_size > 0) { - buffer += fmt::format(" addi sp, sp, -{}\n", stk_frame_size); + emitAddi(buffer, "sp", "sp", -stk_frame_size); } if (has_callee) { // Save RA at the top of the frame (below the caller's frame). - buffer += fmt::format(" sw ra, {}(sp)\n", stk_frame_size - 4); + emitSw(buffer, "ra", "sp", stk_frame_size - 4); } // Offset local variable storage by the size allocated for outgoing arguments. @@ -215,18 +325,19 @@ auto TargetCodeGen::visit(koopa_raw_function_t func) -> void { } // --- Parameter Handling --- - for (size_t i = 0; - const auto param : make_span(func->params)) { + + for (const auto [i, param] : + make_span(func->params) | enumerate) { if (i < 8) { // Store input parameters (a0-a7) into their allocated stack slots. int offset = stkMap[param] = i * 4 + args_size; - buffer += fmt::format(" sw a{}, {}(sp)\n", i, offset); + auto src = fmt::format("a{}", i); + emitSw(buffer, src, "sp", offset); } else { // Parameters passed on stack by caller are located ABOVE the current SP. int offset = stk_frame_size + (i - 8) * 4; stkMap[param] = offset; } - ++i; } // --- Function Body --- @@ -235,10 +346,19 @@ auto TargetCodeGen::visit(koopa_raw_function_t func) -> void { } } + +/** + * @brief Generates assembly for a basic block. + * + * Emits the block label (if any) and visits all instructions in the block sequentially. + * + * @param bb The Koopa basic block to process. + */ auto TargetCodeGen::visit(koopa_raw_basic_block_t bb) -> void { if (bb->name) { buffer += fmt::format("{}:\n", bb->name + 1); } + for (const auto inst : make_span(bb->insts)) { visit(inst); } @@ -260,11 +380,6 @@ auto TargetCodeGen::visit(koopa_raw_value_t value) -> void { break; } - case KOOPA_RVT_INTEGER: { - visit(kind.data.integer); - break; - } - case KOOPA_RVT_STORE: { visit(kind.data.store); break; @@ -275,6 +390,7 @@ auto TargetCodeGen::visit(koopa_raw_value_t value) -> void { break; } + // br case KOOPA_RVT_BRANCH: { visit(kind.data.branch); break; @@ -285,9 +401,21 @@ auto TargetCodeGen::visit(koopa_raw_value_t value) -> void { break; } - case KOOPA_RVT_FUNC_ARG_REF: { - // Reference to a function argument. - visit(kind.data.func_arg_ref); + case KOOPA_RVT_GET_ELEM_PTR: { + visit(kind.data.get_elem_ptr); + if (value->ty->tag != KOOPA_RTT_UNIT) { + int offset = stkMap[value]; + emitSw(buffer, "t0", "sp", offset); + } + break; + } + + case KOOPA_RVT_GET_PTR: { + visit(kind.data.get_ptr); + if (value->ty->tag != KOOPA_RTT_UNIT) { + int offset = stkMap[value]; + emitSw(buffer, "t0", "sp", offset); + } break; } @@ -295,9 +423,9 @@ auto TargetCodeGen::visit(koopa_raw_value_t value) -> void { visit(kind.data.call); // If the function returns an int, it's in a0. Save it to the stack slot // assigned to this 'call' value. - if (value->ty->tag == KOOPA_RTT_INT32) { + if (value->ty->tag != KOOPA_RTT_UNIT) { int offset = stkMap[value]; - buffer += fmt::format(" sw a0, {}(sp)\n", offset); + emitSw(buffer, "a0", "sp", offset); } break; } @@ -316,7 +444,7 @@ auto TargetCodeGen::visit(koopa_raw_value_t value) -> void { // Binary operations result in a value in t0. Save it to stack. if (value->ty->tag != KOOPA_RTT_UNIT) { int offset = stkMap[value]; - buffer += fmt::format(" sw t0, {}(sp)\n", offset); + emitSw(buffer, "t0", "sp", offset); } break; } @@ -326,15 +454,24 @@ auto TargetCodeGen::visit(koopa_raw_value_t value) -> void { // Load result is in t0. Save it to stack. if (value->ty->tag != KOOPA_RTT_UNIT) { int offset = stkMap[value]; - buffer += fmt::format(" sw t0, {}(sp)\n", offset); + emitSw(buffer, "t0", "sp", offset); } break; } - default: assert(false); + default: { + Log::panic("Let's explore the world ahead another time~\n" + "(Unhandled value tag in visit value)"); + } } } + +/** + * @brief Generates assembly for a conditional branch. + * + * @param branch The Koopa branch instruction data. + */ auto TargetCodeGen::visit(const koopa_raw_branch_t &branch) -> void { load_to(branch.cond, "t0"); // bnez: branch if not equal to zero. @@ -342,34 +479,42 @@ auto TargetCodeGen::visit(const koopa_raw_branch_t &branch) -> void { buffer += fmt::format(" j {}\n", branch.false_bb->name + 1); } +/** + * @brief Generates assembly for an unconditional jump. + * + * @param jump The Koopa jump instruction data. + */ auto TargetCodeGen::visit(const koopa_raw_jump_t &jump) -> void { std::string target_name = jump.target->name + 1; buffer += fmt::format(" j {}\n", target_name); } +/** + * @brief Generates assembly for a load instruction. + * + * Loads a value from the memory address specified by `load.src`. + * The result is stored into `t0`, which will be saved to the stack by the caller. + * + * @param load The Koopa load instruction data. + */ auto TargetCodeGen::visit(const koopa_raw_load_t &load) -> void { // src is a pointer value (either a local alloc or a global). load_to(load.src, "t0"); - // If it's a global, t0 now contains the address. We need to load from it. - if (load.src->kind.tag == KOOPA_RVT_GLOBAL_ALLOC) { - buffer += " lw t0, 0(t0)\n"; - } - // Note: if it was a local alloc, load_to already performed the lw t0, - // offset(sp) because local allocs are just pointers to stack slots. + buffer += " lw t0, 0(t0)\n"; } +/** + * @brief Generates assembly for a store instruction. + * + * Stores the value in `store.value` to the memory address `store.dest`. + * + * @param store The Koopa store instruction data. + */ auto TargetCodeGen::visit(const koopa_raw_store_t &store) -> void { - // Store 'value' into 'dest'. load_to(store.value, "t0"); - if (store.dest->kind.tag == KOOPA_RVT_GLOBAL_ALLOC) { - std::string name = store.dest->name + 1; - buffer += fmt::format(" la t1, {}\n", name); - buffer += " sw t0, 0(t1)\n"; - } else if (stkMap.contains(store.dest)) { - int offset = stkMap[store.dest]; - // Store the value into the destination's stack slot. - buffer += fmt::format(" sw t0, {}(sp)\n", offset); - } + load_to(store.dest, "t1"); + + buffer += " sw t0, 0(t1)\n"; } /** @@ -382,11 +527,13 @@ auto TargetCodeGen::visit(const koopa_raw_return_t &ret) -> void { } // Function Epilogue: Restore RA and SP. if (ra_size > 0) { - buffer += fmt::format(" lw ra, {}(sp)\n", stk_frame_size - ra_size); + emitLw(buffer, "ra", "sp", stk_frame_size - ra_size); } + if (stk_frame_size > 0) { - buffer += fmt::format(" addi sp, sp, {}\n", stk_frame_size); + emitAddi(buffer, "sp", "sp", stk_frame_size); } + buffer += " ret\n"; } @@ -399,6 +546,7 @@ auto TargetCodeGen::visit(const koopa_raw_return_t &ret) -> void { * @param value The Koopa value to load. * @param reg The target register name (e.g., "t0"). */ + auto TargetCodeGen::load_to(const koopa_raw_value_t &value, const std::string ®) -> void { switch (value->kind.tag) { @@ -417,20 +565,27 @@ auto TargetCodeGen::load_to(const koopa_raw_value_t &value, break; } + case KOOPA_RVT_ALLOC: { + int offset = stkMap[value]; + emitAddi(buffer, reg, "sp", offset); + break; + } + // Local allocations and instruction results are all stored in the stack // frame. + case KOOPA_RVT_GET_ELEM_PTR: + case KOOPA_RVT_GET_PTR: case KOOPA_RVT_CALL: case KOOPA_RVT_FUNC_ARG_REF: case KOOPA_RVT_BINARY: - case KOOPA_RVT_LOAD: - case KOOPA_RVT_ALLOC: { + case KOOPA_RVT_LOAD: { int offset = stkMap[value]; - buffer += fmt::format(" lw {}, {}(sp)\n", reg, offset); + emitLw(buffer, reg, "sp", offset); break; } default: { - buffer += "Wait! Do you realy think you arguments should go here ?\n"; + Log::panic("Unhandled value tag in load_to"); break; } } @@ -455,7 +610,8 @@ auto TargetCodeGen::visit(const koopa_raw_call_t &call) -> void { // Args 9+ go onto the stack at the very bottom of the current frame. load_to(param, "t0"); int offset = (i - 8) * 4; - buffer += fmt::format(" sw t0, {}(sp)\n", offset); + emitSw(buffer, "t0", "sp", offset); + // buffer += fmt::format(" sw t0, {}(sp)\n", offset); } ++i; } @@ -463,25 +619,85 @@ auto TargetCodeGen::visit(const koopa_raw_call_t &call) -> void { buffer += fmt::format(" call {}\n", call.callee->name + 1); } -auto TargetCodeGen::visit(const koopa_raw_func_arg_ref_t &) -> void {} - /** * @brief Handles global variable allocation. + * + * Emits `.data` section directives for global variables. + * Recursively handles aggregate types (arrays) using a lambda. + * + * @param global_alloc The global allocation instruction data. */ auto TargetCodeGen::visit(const koopa_raw_global_alloc_t &global_alloc) -> void { - if (global_alloc.init->kind.tag == KOOPA_RVT_ZERO_INIT) { - // Uninitialized/Zero-initialized global. - buffer += " .zero 4\n"; - } else if (global_alloc.init->kind.tag == KOOPA_RVT_INTEGER) { - // Constant-initialized global. - buffer += - fmt::format(" .word {}\n", global_alloc.init->kind.data.integer.value); - } else if (global_alloc.init->kind.tag == KOOPA_RVT_AGGREGATE) { - /* TODO: Implement aggregate (array) initialization */ - } + [&](this auto &&self, koopa_raw_value_t value) -> void { + const auto &kind = value->kind; + switch (kind.tag) { + case KOOPA_RVT_INTEGER: { + buffer += fmt::format(" .word {}\n", kind.data.integer.value); + break; + } + + case KOOPA_RVT_ZERO_INIT: { + buffer += fmt::format(" .zero {}\n", get_type_size(value->ty)); + break; + } + + case KOOPA_RVT_AGGREGATE: { + for (const auto &sub_val : + make_span(kind.data.aggregate.elems)) { + self(sub_val); + } + break; + } + + default: assert(false); + } + }(global_alloc.init); } +/** + * @brief Generates assembly for `getelementptr` (array element access). + * + * Compute address of `src[index]`. + * `src` is expected to be a pointer to an array (e.g., `[[i32, 10], 5]*`). + * The stride is the size of the array's element type. + * + * @param get_elem_ptr The Koopa GEP instruction data. + */ +auto TargetCodeGen::visit(const koopa_raw_get_elem_ptr_t &get_elem_ptr) + -> void { + load_to(get_elem_ptr.src, "t0"); + load_to(get_elem_ptr.index, "t1"); + + auto stride = + get_type_size(get_elem_ptr.src->ty->data.pointer.base->data.array.base); + + buffer += fmt::format(" li t2, {}\n", stride); + buffer += " mul t1, t1, t2\n"; + buffer += " add t0, t0, t1\n"; +}; + +/** + * @brief Generates assembly for `getptr` (pointer arithmetic). + * + * Compute address of `src + index`. + * `src` is a pointer (e.g., `i32*`). + * The stride is the size of the type pointed to. + * + * @param get_ptr The Koopa getptr instruction data. + */ +auto TargetCodeGen::visit(const koopa_raw_get_ptr_t &get_ptr) -> void { + + load_to(get_ptr.src, "t0"); + load_to(get_ptr.index, "t1"); + + auto stride = get_type_size(get_ptr.src->ty->data.pointer.base); + + buffer += fmt::format(" li t2, {}\n", stride); + buffer += " mul t1, t1, t2\n"; + buffer += " add t0, t0, t1\n"; +}; + /** * @brief Generates assembly for binary operations. * @@ -531,6 +747,4 @@ auto TargetCodeGen::visit(const koopa_raw_binary_t &binary) -> void { default: assert(false); } // clang-format on -} - -auto TargetCodeGen::visit(const koopa_raw_integer_t &) -> void {} \ No newline at end of file +} \ No newline at end of file diff --git a/project/src/frontend/sysy.y b/project/src/frontend/sysy.y index 9b020a3..e0eb61e 100644 --- a/project/src/frontend/sysy.y +++ b/project/src/frontend/sysy.y @@ -46,11 +46,13 @@ void yyerror(std::unique_ptr &ast, const char *str); std::string *str_val; int int_val; ast::BaseAST *ast_val; + ast::InitValStmtAST *init_val; std::vector> *items_val; std::vector> *defs_val; std::vector> *children_val; std::vector> *funcParams_val; std::vector> *args_val; + std::vector> *initialize_list_val; } // terminal letters are written in uppercase. @@ -64,7 +66,9 @@ void yyerror(std::unique_ptr &ast, const char *str); %type ConstDef VarDef BlockItem Decl ConstDecl VarDecl %type CompUnitItem CompUnit FuncFParam %type FuncFParams -%type FuncRParams +%type FuncRParams ArraySuffix ParamArraySuffix +%type InitVal +%type InitializeList %type CompUnitItemList %type BlockItemList %type VarDefList ConstDefList @@ -80,6 +84,14 @@ void yyerror(std::unique_ptr &ast, const char *str); %left '*' '/' '%' // * / % %right '!' PRIORITY // ! - +/* +VarDef ::= IDENT {"[" ConstExp "]"} + | IDENT {"[" ConstExp "]"} "=" InitVal; +InitVal ::= Exp | "{" [InitVal {"," InitVal}] "}"; + +LVal ::= IDENT {"[" Exp "]"}; +*/ + %% /** * @brief Top-level unit of a program. @@ -145,16 +157,37 @@ FuncFParams FuncFParam : Btype IDENT { - $$ = new FuncParamAST(std::move(*$1), std::move(*$2), false); + $$ = new FuncParamAST(std::move(*$1), std::move(*$2), false, false, {}); + delete $1; + delete $2; + } + | Btype IDENT ParamArraySuffix { + $$ = new FuncParamAST(std::move(*$1), std::move(*$2), false, true, std::move(*$3)); delete $1; delete $2; + delete $3; } | CONST Btype IDENT { - $$ = new FuncParamAST(std::move(*$2), std::move(*$3), true); + $$ = new FuncParamAST(std::move(*$2), std::move(*$3), true, false, {}); delete $2; delete $3; } + | CONST Btype IDENT ParamArraySuffix { + $$ = new FuncParamAST(std::move(*$2), std::move(*$3), true, true, std::move(*$4)); + delete $2; + delete $3; + delete $4; + }; +ParamArraySuffix + : '[' ']' { + $$ = new std::vector>(); + $$->push_back(nullptr); + } + | '[' ']' ArraySuffix { + $$ = $3; + $$->insert($$->begin(), nullptr); + }; /** * @brief A block of code enclosed in curly braces. @@ -175,15 +208,22 @@ BlockItemList $$->push_back(std::unique_ptr($2)); }; +/** + * @brief Block item (declaration or statement). + */ BlockItem : Decl { $$ = $1; } | Stmt { $$ = $1; }; +/** + * @brief Declaration (constant or variable). + */ Decl : ConstDecl { $$ = $1; } | VarDecl { $$ = $1; }; - -ConstDecl +/** + * @brief Constant declaration. + */ConstDecl : CONST Btype ConstDefList ';' { $$ = new DeclAST(true, std::move(*$2), std::move(*$3)); delete $2; @@ -201,12 +241,64 @@ ConstDefList $$->push_back(std::unique_ptr(static_cast($3))); }; +/** + * @brief Constant definition. + */ ConstDef : IDENT '=' Expr { - $$ = new DefAST(true, std::move(*$1), $3); + // @param is_const, ident, exprAST + $$ = new ScalarDefAST(true, std::move(*$1), $3); + delete $1; + } + | IDENT ArraySuffix '=' InitVal { + $$ = new ArrayDefAST(true, std::move(*$1), std::move(*$2), $4); delete $1; + delete $2; }; +/** + * @brief Array dimension suffix (e.g. `[2][3]`). + */ +ArraySuffix + : '[' Expr ']' { + $$ = new std::vector>(); + $$->push_back(std::unique_ptr(static_cast($2))); + } + | ArraySuffix '[' Expr ']' { + $$ = $1; + $$->push_back(std::unique_ptr(static_cast($3))); + }; + +InitializeList + : InitVal { + $$ = new std::vector>(); + $$->push_back(std::unique_ptr($1)); + } + | InitializeList ',' InitVal { + $$ = $1; + $$->push_back(std::unique_ptr($3)); + }; + +// {1, 2, {2, 0}} +/** + * @brief Initializer value (expression or initializer list). + */ +InitVal + : Expr { + $$ = new InitValStmtAST($1, {}); + // delete $1; + } + | '{' '}' { + $$ = new InitValStmtAST(nullptr, {}); + } + | '{' InitializeList '}' { + $$ = new InitValStmtAST(nullptr, std::move(*$2)); + delete $2; + }; + +/** + * @brief Variable declaration. + */ VarDecl : Btype VarDefList ';' { $$ = new DeclAST(false, std::move(*$1), std::move(*$2)); @@ -224,16 +316,35 @@ VarDefList $$->push_back(std::unique_ptr(static_cast($3))); }; +/** + * @brief Variable definition (with optional initialization). + */ VarDef : IDENT '=' Expr { - $$ = new DefAST(false, std::move(*$1), $3); + $$ = new ScalarDefAST(false, std::move(*$1), $3); delete $1; }; | IDENT { - $$ = new DefAST(false, std::move(*$1), nullptr); + $$ = new ScalarDefAST(false, std::move(*$1), nullptr); + delete $1; + } + | IDENT ArraySuffix { + $$ = new ArrayDefAST(false, std::move(*$1), std::move(*$2), nullptr); + delete $1; + delete $2; + } + | IDENT ArraySuffix '=' InitVal { + $$ = new ArrayDefAST(false, std::move(*$1), std::move(*$2), $4); delete $1; + delete $2; + // delete $4; }; +/* +VarDef ::= IDENT ["[" ConstExp "]"] + | IDENT ["[" ConstExp "]"] "=" InitVal; +*/ + Btype : INT { $$ = new std::string("int"); } | VOID { $$ = new std::string("void"); }; @@ -283,7 +394,7 @@ Expr : Number { $$ = $1; } | '(' Expr ')' { $$ = $2; } | LVal { $$ = $1; } - /* unary experssion */ + /* unary expression */ /* function call */ | IDENT '(' ')' { auto args = std::vector>(); @@ -304,7 +415,7 @@ Expr | '-' Expr %prec PRIORITY { $$ = new UnaryExprAST(UnaryOp::Neg, $2); } - /* binary experssion */ + /* binary expression */ | Expr '+' Expr { $$ = new BinaryExprAST(BinaryOp::Add, $1, $3); } @@ -345,15 +456,26 @@ Expr $$ = new BinaryExprAST(BinaryOp::Or, $1, $3); }; +/** + * @brief Integer constant wrapper. + */ Number : INT_CONST { $$ = new NumberAST($1); }; +/** + * @brief Left-value expression (variable / array access). + */ LVal : IDENT { - $$ = new LValAST(std::move(*$1)); + $$ = new LValAST(std::move(*$1), {}); delete $1; + } + | IDENT ArraySuffix { + $$ = new LValAST(std::move(*$1), std::move(*$2)); + delete $1; + delete $2; }; /** diff --git a/project/src/ir/ast.cpp b/project/src/ir/ast.cpp index 657b3fe..c709c55 100644 --- a/project/src/ir/ast.cpp +++ b/project/src/ir/ast.cpp @@ -22,6 +22,7 @@ using namespace detail; // Destructors to solve the problem of forward declaration with smart pointers AssignStmtAST::~AssignStmtAST() = default; FuncDefAST::~FuncDefAST() = default; +ArrayDefAST::~ArrayDefAST() = default; /** * @brief Constructs a FuncDefAST node. @@ -50,6 +51,18 @@ AssignStmtAST::AssignStmtAST(BaseAST *_lval, BaseAST *_expr) { } } +/** + * @brief Constructs an ArrayDefAST node. + * @param _is_const Whether the array is constant. + * @param _ident Array identifier name. + * @param _array_suffix List of expressions defining array dimensions. + * @param _init_val Optional initialization value(s). + */ +ArrayDefAST::ArrayDefAST(bool _is_const, std::string _ident, + std::vector> _array_suffix, + InitValStmtAST *_init_val) + : is_const(_is_const), ident(std::move(_ident)), + array_suffix(std::move(_array_suffix)), init_val(_init_val) {} /** * @brief Dumps CompUnitAST node details. @@ -86,17 +99,24 @@ auto FuncDefAST::dump(int depth) const -> void { } /** - * @brief Dumps DefAST node details. + * @brief Dumps ArrayDefAST node details. + * @param depth Indentation depth. + */ +auto ArrayDefAST::dump(int depth) const -> void { + fmt::println("{}ArrayDefAST: {}", indent(depth), ident); +} + +/** + * @brief Dumps ScalarDefAST node details. * @param depth Indentation depth. */ -auto DefAST::dump(int depth) const -> void { - fmt::println("{}DefAST: {}", indent(depth), ident); +auto ScalarDefAST::dump(int depth) const -> void { + fmt::println("{}ScalarDefAST: {}", indent(depth), ident); if (initVal) { initVal->dump(depth + 1); } } - /** * @brief Dumps BlockAST node details. * @param depth Indentation depth. @@ -119,6 +139,16 @@ auto ExprStmtAST::dump(int depth) const -> void { } } +/** + * @brief Dumps InitValStmtAST node details. + * @param depth Indentation depth. + */ +auto InitValStmtAST::dump(int depth) const -> void { + fmt::println("{}InitValStmtAST:", indent(depth)); + for (const auto &init_val : initialize_list) { + init_val->dump(depth + 1); + } +} /** * @brief Dumps ReturnStmtAST node details. @@ -141,7 +171,6 @@ auto AssignStmtAST::dump(int depth) const -> void { expr->dump(depth + 1); } - /** * @brief Dumps DeclAST node details. * @param depth Indentation depth. @@ -154,7 +183,6 @@ auto DeclAST::dump(int depth) const -> void { } } - /** * @brief Dumps IfStmtAST node details. * @param depth Indentation depth. @@ -202,7 +230,6 @@ auto ContinueStmtAST::dump(int depth) const -> void { fmt::println("{}ContinueAST", indent(depth)); } - /** * @brief Dumps NumberAST node details. * @param depth Indentation depth. diff --git a/project/src/ir/codegen.cpp b/project/src/ir/codegen.cpp index 1ff7498..c120296 100644 --- a/project/src/ir/codegen.cpp +++ b/project/src/ir/codegen.cpp @@ -26,6 +26,9 @@ module; #include #include +#include +#include +#include #include #include @@ -37,6 +40,7 @@ import log; using namespace ast; using namespace detail; +using namespace std::views; /** * @brief Generates IR for a compilation unit (the top-level node). @@ -71,19 +75,48 @@ auto FuncParamAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { if (btype == "void") { Log::panic("Semantic Error: Variable cannot be of type 'void'"); } - if (is_const) { - builder.symtab().define(ident, "", type::IntType::get(), SymbolKind::Var, - true, 0); + + std::shared_ptr param_type; + if (is_ptr) { + std::shared_ptr base_type = type::IntType::get(); + for (const auto &dim : + indices | reverse | filter([](auto &p) { return p != nullptr; })) { + base_type = type::ArrayType::get(base_type, dim->CalcValue(builder)); + } + param_type = type::PtrType::get(base_type); } else { - std::string addr = builder.newReg(); - builder.append(fmt::format(" {} = alloc i32\n", addr)); - builder.symtab().define(ident, addr, type::IntType::get(), SymbolKind::Var, - false); - builder.append(fmt::format(" store @{}, {}\n", ident, addr)); + param_type = type::IntType::get(); } + + std::string addr = builder.newReg(); + builder.append(fmt::format(" {} = alloc {}\n", addr, param_type->toKoopa())); + builder.append(fmt::format(" store @{}, {}\n", ident, addr)); + + builder.symtab().define(ident, addr, param_type, SymbolKind::Var, is_const); return ""; } +/** + * @brief Converts function parameter type to Koopa IR string. + * @return The Koopa type string (e.g., `i32` or `*i32`). + */ +auto FuncParamAST::toKoopa(ir::KoopaBuilder &builder) const -> std::string { + std::shared_ptr param_type; + + if (is_ptr) { + std::shared_ptr base_type = type::IntType::get(); + for (const auto &dim : + indices | reverse | filter([](auto &p) { return p != nullptr; })) { + base_type = type::ArrayType::get(base_type, dim->CalcValue(builder)); + } + param_type = type::PtrType::get(base_type); + } else { + param_type = type::IntType::get(); + } + + return param_type->toKoopa(); +} + /** * @brief Generates IR for a function definition. * @@ -100,15 +133,17 @@ auto FuncParamAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { */ auto FuncDefAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { builder.resetCount(); - auto btype2irType = [](std::string_view s) -> std::string { - return (s == "int" ? "i32" : ""); - }; + // auto btype2irType = [](std::string_view s) -> std::string { + // return (s == "int" ? "i32" : ""); + // }; builder.append(fmt::format("{} @", (block ? "fun" : "decl"))); builder.append(ident); builder.append("("); for (const auto ¶m : params) { + // builder.append( + // fmt::format("@{}: {}", param->ident, btype2irType(param->btype))); builder.append( - fmt::format("@{}: {}", param->ident, btype2irType(param->btype))); + fmt::format("@{}: {}", param->ident, param->toKoopa(builder))); if (¶m != ¶ms.back()) { builder.append(", "); } @@ -119,7 +154,7 @@ auto FuncDefAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { builder.symtab().defineGlobal(ident, "", type::VoidType::get(), SymbolKind::Func, false); } else { - builder.append(fmt::format("): {} ", btype2irType(btype))); + builder.append("): i32 "); builder.symtab().defineGlobal(ident, "", type::IntType::get(), SymbolKind::Func, false); } @@ -159,6 +194,105 @@ auto FuncDefAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { return ""; } +/** + * @brief Generates IR for an array definition (global or local). + * + * Handles: + * 1. Type Construction: Recursively builds the multidimensional array type. + * 2. IR String Generation: Format string for the array type (e.g., `[[i32, 2], 3]`). + * 3. Allocation: + * - Global: Allocates in `.data` section, handles initialization. + * - Local: Allocates on stack, handles initialization via `getelemptr` and `store`. + * 4. Initialization: Flattens the initializer list and fills the array. + * + * @param builder The IR builder context. + * @return An empty string. + */ +auto ArrayDefAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { + std::shared_ptr arr_type = type::IntType::get(); + for (const auto &dim : array_suffix | reverse) { + arr_type = type::ArrayType::get(arr_type, dim->CalcValue(builder)); + } + + auto ir_array_suffix = [&](this auto &&self, int idx) -> std::string { + if (idx == ssize(array_suffix) - 1) { + return fmt::format("[i32, {}]", array_suffix[idx]->CalcValue(builder)); + } + return fmt::format("[{}, {}]", self(idx + 1), + array_suffix[idx]->CalcValue(builder)); + }(0); + + Log::trace(ir_array_suffix); + + if (builder.symtab().isGlobalScope()) { + //! [Caution] : Since global variables do not have naming conflicts, we + //! will consistently use ident without the prefix. + //! So this avoids naming conflicts between global and local arrays. + std::string ir_name = builder.newVar(ident); + if (init_val == nullptr) { + builder.append(fmt::format("global {} = alloc {}, zeroinit\n", ir_name, + ir_array_suffix)); + } else { + builder.append( + fmt::format("global {} = alloc {}, ", ir_name, ir_array_suffix)); + auto flatten_initialize_list = init_val->flatten(arr_type, builder); + + int idx = 0; + auto result = [&](this auto &&self, + std::shared_ptr type) -> std::string { + auto arr_type = std::dynamic_pointer_cast(type); + if (arr_type) { + std::string res = "{"; + + for (int i : iota(0, arr_type->len)) { + if (i > 0) { + res += ", "; + } + res += self(arr_type->base); + } + + return res + "}"; + } + return flatten_initialize_list[idx++]; + }(arr_type); + + builder.append(result + "\n"); + } + + builder.symtab().defineGlobal(ident, ir_name, arr_type, SymbolKind::Var, + is_const); + } else { + auto addr = builder.newVar(ident); + // builder.append(fmt::format("[debug]: {}\n", ir_array_suffix)); + builder.append(fmt::format(" {} = alloc {}\n", addr, ir_array_suffix)); + + //! If a local variable has no initial value, its content is undefined + if (init_val != nullptr) { + auto flatten_initialize_list = init_val->flatten(arr_type, builder); + + int idx = 0; + [&](this auto &&self, std::shared_ptr type, + std::string ptr) -> void { + if (auto arr_type = std::dynamic_pointer_cast(type)) { + for (int i : iota(0, arr_type->len)) { + auto nxt_ptr = builder.newReg(); + builder.append( + fmt::format(" {} = getelemptr {}, {}\n", nxt_ptr, ptr, i)); + self(arr_type->base, nxt_ptr); + } + return; + } + + auto value = flatten_initialize_list[idx++]; + builder.append(fmt::format(" store {}, {}\n", value, ptr)); + }(arr_type, addr); + } + builder.symtab().define(ident, addr, arr_type, SymbolKind::Var, is_const); + } + + return ""; +} + /** * @brief Generates IR for a single variable definition (Def). * @@ -166,16 +300,19 @@ auto FuncDefAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { * - Globals: Allocated in the global space, possibly with an initializer. * - Locals: Allocated on the stack using 'alloc'. Constants are tracked in the * symbol table but don't result in 'alloc' instructions unless they are - * non-const. + * non-const. * * @param builder The IR builder context. * @return An empty string. */ -auto DefAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { +auto ScalarDefAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { if (builder.symtab().isGlobalScope()) { int val = 0; bool has_init = false; if (initVal) { + // NOTE: No need to check `initval` here, because `const int a;` and + // similar statements are not implemented and will result in an error at + // the syntax parsing stage. val = initVal->CalcValue(builder); has_init = true; } @@ -216,7 +353,6 @@ auto DefAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { return ""; } - /** * @brief Generates IR for a block of statements (enclosed in { }). * @@ -261,6 +397,152 @@ auto ExprStmtAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { return ""; } +/** + * @brief Dummy code generation method for initializer lists. + * + * Helper node `InitValStmtAST` is only used during initialization flattening. + * Calling this method is a logical error. + */ +auto InitValStmtAST::codeGen([[maybe_unused]] ir::KoopaBuilder &builder) const + -> std::string { + //! No node should call `InitValStmtAST`'s `codeGen` function, as it is only + //! responsible for expanding the initialize list, not for generating code. + Log::panic("InitValStmtAST::codeGen was called unexpectedly. This node is " + "only used to expand initializer lists and must not generate " + "IR directly."); + return ""; +} + +/** + * @brief Flattens a nested SysY initializer list into a linear vector of IR + * values. + * + * Origin: This mechanism handles the complex C-style array initialization + * rules, where braces {} force alignment and scalars (numbers) flow into + * available slots. + * + * Mechanism: + * 1. Flow Mode: Scalars fill the next available i32 slots across dimension + * boundaries. + * 2. Align Mode: Braces force the "cursor" to align with the start of the next + * sub-type. + * + * @param targetType The expected Type (Int or Array) for the current level. + * @param builder The IR builder used to generate code for expressions. + * @return std::vector A flat list of IR constants or register + * names. + */ +auto InitValStmtAST::flatten(std::shared_ptr targetType, + ir::KoopaBuilder &builder) const + -> std::vector { + + // Helper to cast Type to ArrayType + static auto make_arrType = [](std::shared_ptr t) { + return std::dynamic_pointer_cast(t); + }; + + // 'idx' tracks the current position in the initializer list at the top level. + int idx = 0; + + /** + * @brief Recursive lambda to process the hierarchy of types and initializers. + * @param flatten_impl Self-reference for recursion. + * @param type The type we are currently trying to fill. + * @param list The current list of InitVal nodes we are consuming. + * @param idx Reference to the cursor in the current list. + */ + auto result = [&](this auto &&flatten_impl, std::shared_ptr type, + const std::vector> &list, + int &idx) -> std::vector { + // Base Case: Target is a simple Integer + if (type->is_int()) { + // If no more data is provided, SysY requires implicit + // zero-initialization. + if (idx >= ssize(list)) { + return {"0"}; + } + + const auto &node = list[idx]; + // Semantic Check: A scalar target cannot be initialized by a brace list. + // e.g., int a[2] = {1, {2}}; is invalid. + if (!node->expr) { + Log::panic(fmt::format( + "Semantic Error: Expected scalar, but found brace list")); + } + + // Move the cursor after consuming one scalar value. + idx++; + if (builder.symtab().isGlobalScope()) { + int val = node->expr->CalcValue(builder); + return {std::to_string(val)}; + } else { + std::string reg_or_num = node->expr->codeGen(builder); + return {reg_or_num}; + } + } + + std::vector result; + auto arr_type = make_arrType(type); + + // Iterate through each element of the current array dimension. + for (int i = 0; i < arr_type->len; ++i) { + std::vector tmp_res; + + // Scenario 1: The input list is exhausted before the array is full. + if (ssize(list) <= idx) { + int dummy = 0; + static const std::vector> empty; + // Recursively fill the remaining slots with "0". + tmp_res = flatten_impl(arr_type->base, empty, dummy); + result.insert(result.end(), tmp_res.begin(), tmp_res.end()); + continue; + } + + const auto &node = list[idx]; + + if (node->expr) { + // Scenario 2 [Flow Mode]: The current item is a scalar. + // It might fill a part of the sub-type (if the sub-type is an array). + // We pass the current list and the global idx down. + tmp_res = flatten_impl(arr_type->base, list, idx); + } else { + // Scenario 3 [Align Mode]: The current item is a brace list { ... }. + // This forces alignment to the sub-type boundary. + int sub_idx = 0; + // We pass the nested list and a local sub_idx (starting at 0). + tmp_res = flatten_impl(arr_type->base, node->initialize_list, sub_idx); + + // Semantic Check: Verify that the brace list does not contain more + // elements than the sub-type can hold. + if (sub_idx < ssize(node->initialize_list)) { + Log::panic(fmt::format("Semantic Error: Excess elements in array " + "initializer (expected {}, got {}).", + arr_type->base->toKoopa(), + ssize(node->initialize_list))); + } + + // After processing the entire nested list, move the outer cursor by 1. + idx++; + } + + // Append the results of the sub-type filling to the current result. + result.insert(result.end(), std::make_move_iterator(tmp_res.begin()), + std::make_move_iterator(tmp_res.end())); + } + + return result; + }(targetType, this->initialize_list, idx); + + // Final Semantic Check: The top-level initializer list must not have + // more elements than the total capacity of the array. + if (idx < ssize(this->initialize_list)) { + Log::panic( + fmt::format("Semantic Error: Excess elements in initializer list")); + } + + return result; +} + /** * @brief Generates IR for an assignment statement. * @@ -271,18 +553,51 @@ auto ExprStmtAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { * @return An empty string. */ auto AssignStmtAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { - std::string val_reg = expr->codeGen(builder); auto sym = builder.symtab().lookup(lval->ident); if (!sym) { Log::panic( fmt::format("Assignment to undefined variable '{}'", lval->ident)); } + //* lval(sym) = val_reg if (sym->is_const) { Log::panic( fmt::format("Error: Cannot assign to const variable '{}'", sym->name)); } - builder.append(fmt::format(" store {}, {}\n", val_reg, sym->irName)); + + auto cur_type = sym->type; + std::string cur_ptr = sym->irName; + + if (cur_type->is_ptr()) { + std::string loaded_ptr = builder.newReg(); + builder.append(fmt::format(" {} = load {}\n", loaded_ptr, cur_ptr)); + cur_ptr = loaded_ptr; + cur_type = std::static_pointer_cast(cur_type)->target; + } + + for (const auto &[i, elem] : lval->indices | enumerate) { + std::string idx_val = elem->codeGen(builder); + std::string nxt_ptr = builder.newReg(); + + if (i == 0 && sym->type->is_ptr()) { + builder.append( + fmt::format(" {} = getptr {}, {}\n", nxt_ptr, cur_ptr, idx_val)); + } else { + builder.append( + fmt::format(" {} = getelemptr {}, {}\n", nxt_ptr, cur_ptr, idx_val)); + } + cur_ptr = nxt_ptr; + + if (cur_type->is_array()) { + cur_type = std::static_pointer_cast(cur_type)->base; + } + } + + if (cur_type->is_int()) { + std::string expr_res = expr->codeGen(builder); + builder.append(fmt::format(" store {}, {}\n", expr_res, cur_ptr)); + } + return ""; } @@ -299,6 +614,7 @@ auto DeclAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { if (btype == "void") { Log::panic("Semantic Error: Variable cannot be of type 'void'"); } + for (const auto &def : defs) { def->codeGen(builder); } @@ -319,6 +635,7 @@ auto ReturnStmtAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { if (expr) { ret_val = expr->codeGen(builder); } + builder.setBlockClose(); builder.append(fmt::format(" ret {}\n", ret_val)); return ""; @@ -342,6 +659,7 @@ auto IfStmtAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { std::string else_label = builder.newLabel("else", id); std::string end_label = builder.newLabel("end", id); // clang-format on + if (elseS) { builder.append( fmt::format(" br {}, {}, {}\n", cond_reg, then_label, else_label)); @@ -367,6 +685,7 @@ auto IfStmtAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { builder.append(fmt::format(" jump {}\n", end_label)); } } + builder.append(fmt::format("{}:\n", end_label)); // every basic block (entry) need to pair a block close. builder.clearBlockClose(); @@ -441,7 +760,6 @@ auto ContinueStmtAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { return ""; } - /** * @brief Generates IR for a literal number. * @@ -464,18 +782,62 @@ auto NumberAST::codeGen([[maybe_unused]] ir::KoopaBuilder &builder) const * @param builder The IR builder context. * @return The register name or constant value. */ + auto LValAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { auto sym = builder.symtab().lookup(ident); if (!sym) { Log::panic(fmt::format("Undefined variable: '{}'", ident)); } + //* we can calculate const value in compile time - if (sym->is_const) { + if (sym->is_const && indices.empty() && sym->type->is_int()) { return std::to_string(sym->constValue); } - std::string reg = builder.newReg(); - builder.append(fmt::format(" {} = load {}\n", reg, sym->irName)); - return reg; + + std::string cur_ptr = sym->irName; + auto cur_type = sym->type; + + if (cur_type->is_ptr()) { + std::string loaded_ptr = builder.newReg(); + builder.append(fmt::format(" {} = load {}\n", loaded_ptr, cur_ptr)); + cur_ptr = loaded_ptr; + cur_type = std::static_pointer_cast(cur_type)->target; + } + + for (const auto &[i, elem] : indices | enumerate) { + std::string idx_val = elem->codeGen(builder); + std::string nxt_ptr = builder.newReg(); + + if (i == 0 && sym->type->is_ptr()) { + builder.append( + fmt::format(" {} = getptr {}, {}\n", nxt_ptr, cur_ptr, idx_val)); + } else { + builder.append( + fmt::format(" {} = getelemptr {}, {}\n", nxt_ptr, cur_ptr, idx_val)); + + if (cur_type->is_array()) { + cur_type = std::static_pointer_cast(cur_type)->base; + } + } + + cur_ptr = nxt_ptr; + } + + bool is_bare_ptr_param = sym->type->is_ptr() && indices.empty(); + + if (cur_type->is_int() && !is_bare_ptr_param) { + std::string res_val = builder.newReg(); + builder.append(fmt::format(" {} = load {}\n", res_val, cur_ptr)); + return res_val; + } + + if (is_bare_ptr_param) { + return cur_ptr; + } + + std::string decay_ptr = builder.newReg(); + builder.append(fmt::format(" {} = getelemptr {}, 0\n", decay_ptr, cur_ptr)); + return decay_ptr; } /** @@ -497,6 +859,7 @@ auto FuncCallAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { for (const auto &arg : args) { arg_val.emplace_back(arg->codeGen(builder)); } + std::string ret_reg; if (sym->type->is_void()) { builder.append(fmt::format(" call @{}(", ident)); @@ -504,6 +867,7 @@ auto FuncCallAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { ret_reg = builder.newReg(); builder.append(fmt::format(" {} = call @{}(", ret_reg, ident)); } + for (const auto &val : arg_val) { builder.append(val); if (&val != &arg_val.back()) { @@ -511,6 +875,7 @@ auto FuncCallAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { } } builder.append(")\n"); + return ret_reg; } @@ -525,6 +890,7 @@ auto FuncCallAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { auto UnaryExprAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { std::string rhs_reg = rhs->codeGen(builder); std::string ret_reg = builder.newReg(); + switch (op) { case UnaryOp::Neg: builder.append(fmt::format(" {} = sub 0, {}\n", ret_reg, rhs_reg)); @@ -534,6 +900,7 @@ auto UnaryExprAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { break; default: Log::panic("Code Gen Error: Unknown unary op"); } + return ret_reg; } @@ -547,6 +914,7 @@ auto UnaryExprAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { * @return The register name holding the result. */ auto BinaryExprAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { + if (op == BinaryOp::And) { std::string tmp_addr = builder.newVar("and_res"); builder.append(fmt::format(" {} = alloc i32\n", tmp_addr)); @@ -567,12 +935,12 @@ auto BinaryExprAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { std::string rhs_reg = rhs->codeGen(builder); std::string rhs_bool = builder.newReg(); builder.append(fmt::format(" {} = ne {}, 0\n", rhs_bool, rhs_reg)); - builder.append(fmt::format(" store {}, {}\n", rhs_bool, tmp_addr)); + builder.append(fmt::format(" store {}, {}\n", rhs_bool, tmp_addr)); builder.append(fmt::format(" jump {}\n", end_label)); // false branch builder.append(fmt::format("{}:\n", false_label)); - builder.append(fmt::format(" store 0, {}\n", tmp_addr)); + builder.append(fmt::format(" store 0, {}\n", tmp_addr)); builder.append(fmt::format(" jump {}\n", end_label)); // end @@ -582,6 +950,7 @@ auto BinaryExprAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { return ret_reg; } + if (op == BinaryOp::Or) { std::string tmp_addr = builder.newVar("or_res"); builder.append(fmt::format(" {} = alloc i32\n", tmp_addr)); @@ -608,7 +977,7 @@ auto BinaryExprAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { std::string rhs_reg = rhs->codeGen(builder); std::string rhs_bool = builder.newReg(); builder.append(fmt::format(" {} = ne {}, 0\n", rhs_bool, rhs_reg)); - builder.append(fmt::format(" store {}, {}\n", rhs_bool, tmp_addr)); + builder.append(fmt::format(" store {}, {}\n", rhs_bool, tmp_addr)); builder.append(fmt::format(" jump {}\n", end_label)); // end @@ -618,6 +987,7 @@ auto BinaryExprAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { return ret_reg; } + // remain binary operator std::string lhs_reg = lhs->codeGen(builder); std::string rhs_reg = rhs->codeGen(builder); @@ -628,7 +998,6 @@ auto BinaryExprAST::codeGen(ir::KoopaBuilder &builder) const -> std::string { return ret_reg; } - /** * @brief Evaluates a literal number at compile time. * @param builder The IR builder context. @@ -653,6 +1022,7 @@ auto LValAST::CalcValue(ir::KoopaBuilder &builder) const -> int { Log::panic( fmt::format("Undefined variable '{}' in constant expression", ident)); } + if (!sym->is_const) { Log::panic(fmt::format("Variable '{}' is not a constant, cannot be used in " "constant expression", @@ -701,6 +1071,7 @@ auto UnaryExprAST::CalcValue(ir::KoopaBuilder &builder) const -> int { auto BinaryExprAST::CalcValue(ir::KoopaBuilder &builder) const -> int { int rhs_val = rhs->CalcValue(builder); int lhs_val = lhs->CalcValue(builder); + switch (op) { case BinaryOp::Add: return lhs_val + rhs_val; case BinaryOp::Sub: return lhs_val - rhs_val; diff --git a/project/src/main.cpp b/project/src/main.cpp index d92d3d2..7929e28 100644 --- a/project/src/main.cpp +++ b/project/src/main.cpp @@ -111,12 +111,12 @@ auto main(int argc, const char *argv[]) -> int { } // 3. Generate RISC-V assembly from Koopa IR - backend::KoopaWrapper wrapper(ir); - backend::TargetCodeGen generator; - generator.visit(wrapper.getRaw()); - - const std::string asmCode = generator.getAssembly(); if (config.mode == "-riscv") { + backend::KoopaWrapper wrapper(ir); + backend::TargetCodeGen generator; + generator.visit(wrapper.getRaw()); + + const std::string asmCode = generator.getAssembly(); auto out = fmt::output_file(config.output_file); out.print("{}", asmCode); fmt::print(fmt::fg(fmt::color::cyan), "[Success] Parse riscv succeed!\n"); diff --git a/project/tests/hello.c b/project/tests/hello.c index a3bf9fa..ceac27e 100644 --- a/project/tests/hello.c +++ b/project/tests/hello.c @@ -1,6 +1,13 @@ -int a = 10; +int x; -int inc() { - a = a + 1; - return a; +int t() { + x = x + 1; + return 1; +} + +int main() { + int sum = 0; + t(); + putint(x); + return sum; } \ No newline at end of file diff --git a/project/tests/resources/hidden_functional/35_math.in b/project/tests/resources/float_support/35_math.in similarity index 100% rename from project/tests/resources/hidden_functional/35_math.in rename to project/tests/resources/float_support/35_math.in diff --git a/project/tests/resources/hidden_functional/35_math.out b/project/tests/resources/float_support/35_math.out similarity index 100% rename from project/tests/resources/hidden_functional/35_math.out rename to project/tests/resources/float_support/35_math.out diff --git a/project/tests/resources/hidden_functional/35_math.sy b/project/tests/resources/float_support/35_math.sy similarity index 100% rename from project/tests/resources/hidden_functional/35_math.sy rename to project/tests/resources/float_support/35_math.sy diff --git a/project/tests/resources/hidden_functional/36_rotate.in b/project/tests/resources/float_support/36_rotate.in similarity index 100% rename from project/tests/resources/hidden_functional/36_rotate.in rename to project/tests/resources/float_support/36_rotate.in diff --git a/project/tests/resources/hidden_functional/36_rotate.out b/project/tests/resources/float_support/36_rotate.out similarity index 100% rename from project/tests/resources/hidden_functional/36_rotate.out rename to project/tests/resources/float_support/36_rotate.out diff --git a/project/tests/resources/hidden_functional/36_rotate.sy b/project/tests/resources/float_support/36_rotate.sy similarity index 100% rename from project/tests/resources/hidden_functional/36_rotate.sy rename to project/tests/resources/float_support/36_rotate.sy diff --git a/project/tests/resources/hidden_functional/37_dct.in b/project/tests/resources/float_support/37_dct.in similarity index 100% rename from project/tests/resources/hidden_functional/37_dct.in rename to project/tests/resources/float_support/37_dct.in diff --git a/project/tests/resources/hidden_functional/37_dct.out b/project/tests/resources/float_support/37_dct.out similarity index 100% rename from project/tests/resources/hidden_functional/37_dct.out rename to project/tests/resources/float_support/37_dct.out diff --git a/project/tests/resources/hidden_functional/37_dct.sy b/project/tests/resources/float_support/37_dct.sy similarity index 100% rename from project/tests/resources/hidden_functional/37_dct.sy rename to project/tests/resources/float_support/37_dct.sy diff --git a/project/tests/resources/hidden_functional/38_light2d.out b/project/tests/resources/float_support/38_light2d.out similarity index 100% rename from project/tests/resources/hidden_functional/38_light2d.out rename to project/tests/resources/float_support/38_light2d.out diff --git a/project/tests/resources/hidden_functional/38_light2d.sy b/project/tests/resources/float_support/38_light2d.sy similarity index 100% rename from project/tests/resources/hidden_functional/38_light2d.sy rename to project/tests/resources/float_support/38_light2d.sy diff --git a/project/tests/resources/hidden_functional/39_fp_params.in b/project/tests/resources/float_support/39_fp_params.in similarity index 100% rename from project/tests/resources/hidden_functional/39_fp_params.in rename to project/tests/resources/float_support/39_fp_params.in diff --git a/project/tests/resources/hidden_functional/39_fp_params.out b/project/tests/resources/float_support/39_fp_params.out similarity index 100% rename from project/tests/resources/hidden_functional/39_fp_params.out rename to project/tests/resources/float_support/39_fp_params.out diff --git a/project/tests/resources/hidden_functional/39_fp_params.sy b/project/tests/resources/float_support/39_fp_params.sy similarity index 100% rename from project/tests/resources/hidden_functional/39_fp_params.sy rename to project/tests/resources/float_support/39_fp_params.sy diff --git a/project/tests/resources/functional/95_float.in b/project/tests/resources/float_support/95_float.in similarity index 100% rename from project/tests/resources/functional/95_float.in rename to project/tests/resources/float_support/95_float.in diff --git a/project/tests/resources/functional/95_float.out b/project/tests/resources/float_support/95_float.out similarity index 100% rename from project/tests/resources/functional/95_float.out rename to project/tests/resources/float_support/95_float.out diff --git a/project/tests/resources/functional/95_float.sy b/project/tests/resources/float_support/95_float.sy similarity index 100% rename from project/tests/resources/functional/95_float.sy rename to project/tests/resources/float_support/95_float.sy diff --git a/project/tests/resources/functional/96_matrix_add.out b/project/tests/resources/float_support/96_matrix_add.out similarity index 100% rename from project/tests/resources/functional/96_matrix_add.out rename to project/tests/resources/float_support/96_matrix_add.out diff --git a/project/tests/resources/functional/96_matrix_add.sy b/project/tests/resources/float_support/96_matrix_add.sy similarity index 100% rename from project/tests/resources/functional/96_matrix_add.sy rename to project/tests/resources/float_support/96_matrix_add.sy diff --git a/project/tests/resources/functional/97_matrix_sub.out b/project/tests/resources/float_support/97_matrix_sub.out similarity index 100% rename from project/tests/resources/functional/97_matrix_sub.out rename to project/tests/resources/float_support/97_matrix_sub.out diff --git a/project/tests/resources/functional/97_matrix_sub.sy b/project/tests/resources/float_support/97_matrix_sub.sy similarity index 100% rename from project/tests/resources/functional/97_matrix_sub.sy rename to project/tests/resources/float_support/97_matrix_sub.sy diff --git a/project/tests/resources/functional/98_matrix_mul.out b/project/tests/resources/float_support/98_matrix_mul.out similarity index 100% rename from project/tests/resources/functional/98_matrix_mul.out rename to project/tests/resources/float_support/98_matrix_mul.out diff --git a/project/tests/resources/functional/98_matrix_mul.sy b/project/tests/resources/float_support/98_matrix_mul.sy similarity index 100% rename from project/tests/resources/functional/98_matrix_mul.sy rename to project/tests/resources/float_support/98_matrix_mul.sy diff --git a/project/tests/resources/functional/99_matrix_tran.out b/project/tests/resources/float_support/99_matrix_tran.out similarity index 100% rename from project/tests/resources/functional/99_matrix_tran.out rename to project/tests/resources/float_support/99_matrix_tran.out diff --git a/project/tests/resources/functional/99_matrix_tran.sy b/project/tests/resources/float_support/99_matrix_tran.sy similarity index 100% rename from project/tests/resources/functional/99_matrix_tran.sy rename to project/tests/resources/float_support/99_matrix_tran.sy