├── src ├── rust │ ├── native │ │ ├── mod.rs │ │ └── native.rs │ ├── objects │ │ ├── mod.rs │ │ └── matrix_stack.rs │ ├── parse │ │ └── mod.rs │ └── lib.rs ├── resources │ ├── include │ │ ├── stdlib.mch │ │ ├── string.mch │ │ ├── event.mch │ │ ├── mcfp.mch │ │ ├── int.mch │ │ ├── int64.mch │ │ └── memory.mch │ ├── assets │ │ └── stdlib │ │ │ ├── data │ │ │ ├── minecraft │ │ │ │ └── tags │ │ │ │ │ └── function │ │ │ │ │ └── load.json │ │ │ └── std │ │ │ │ └── function │ │ │ │ ├── _internal │ │ │ │ ├── shrink_heap.mcfunction │ │ │ │ ├── push_zero.mcfunction │ │ │ │ ├── load_heap.mcfunction │ │ │ │ ├── peek.mcfunction │ │ │ │ ├── pop.mcfunction │ │ │ │ ├── store_heap_custom2.mcfunction │ │ │ │ ├── push.mcfunction │ │ │ │ ├── store_heap_custom3.mcfunction │ │ │ │ ├── load_heap_custom.mcfunction │ │ │ │ ├── merge_string2.mcfunction │ │ │ │ ├── store_heap.mcfunction │ │ │ │ └── store_heap_custom.mcfunction │ │ │ │ ├── heap │ │ │ │ ├── load.mcfunction │ │ │ │ ├── store.mcfunction │ │ │ │ ├── expand.mcfunction │ │ │ │ └── shrink.mcfunction │ │ │ │ └── __init__.mcfunction │ │ │ └── pack.mcmeta │ └── lang │ │ ├── ZH_CN.yaml │ │ └── EN_US.yaml ├── python │ ├── internal │ │ ├── __init__.py │ │ ├── Const.py │ │ └── FileUtils.py │ └── build_assets.py ├── cpp │ ├── Main.h │ ├── i18n │ │ ├── I18nTemplate.h │ │ ├── I18n.h │ │ └── I18n.cpp │ ├── extern │ │ ├── ClRustAPI.h │ │ ├── ResourceManager.h │ │ └── PreProcessorAPI.h │ ├── ir │ │ ├── IRCommon.h │ │ ├── values │ │ │ ├── Value.h │ │ │ ├── Immediate.h │ │ │ ├── Symbol.h │ │ │ ├── SymbolPtr.h │ │ │ ├── Register.h │ │ │ └── Ptr.h │ │ ├── iops │ │ │ ├── Special.h │ │ │ ├── JmpLike.h │ │ │ ├── CallLike.h │ │ │ ├── Op.h │ │ │ └── CmpLike.h │ │ ├── ops │ │ │ ├── Nop.h │ │ │ ├── Ret.h │ │ │ ├── Syscall.h │ │ │ ├── Inline.h │ │ │ ├── Jmp.h │ │ │ ├── Call.h │ │ │ ├── Static.h │ │ │ ├── Peek.h │ │ │ ├── Pop.h │ │ │ ├── Push.h │ │ │ ├── Label.h │ │ │ ├── Mov.h │ │ │ ├── Div.h │ │ │ ├── Mul.h │ │ │ ├── Add.h │ │ │ ├── Sub.h │ │ │ ├── Je.h │ │ │ ├── Jg.h │ │ │ ├── Jl.h │ │ │ ├── Jge.h │ │ │ ├── Jle.h │ │ │ └── Jne.h │ │ ├── State.h │ │ ├── controlFlow │ │ │ ├── JmpTable.h │ │ │ └── JmpTable.cpp │ │ ├── verify │ │ │ ├── Verifier.h │ │ │ └── VerifyResult.h │ │ ├── OpCommon.h │ │ ├── IR.h │ │ └── OpCommon.cpp │ ├── utils │ │ ├── Native.h │ │ ├── CLIUtils.cpp │ │ ├── Stream.h │ │ ├── string │ │ │ └── StringBuilder.h │ │ ├── CLIUtils.h │ │ ├── FileUtils.h │ │ ├── Native.c │ │ └── Common.h │ ├── builder │ │ ├── Builder.h │ │ ├── PostOptimizer.h │ │ ├── PostOptimizer.cpp │ │ └── Builder.cpp │ ├── ClangMc.h │ ├── parse │ │ ├── ParseManager.h │ │ └── ParseManager.cpp │ ├── patches │ │ └── fmt │ │ │ └── core.h │ ├── objects │ │ ├── MatrixStack.h │ │ ├── NameGenerator.h │ │ ├── LogFormatter.h │ │ └── Int.h │ ├── config │ │ ├── ArgParser.h │ │ ├── Config.h │ │ └── ArgParser.cpp │ ├── Start.c │ ├── Main.cpp │ └── ClangMc.cpp └── mcasm │ └── stdlib.mcasm ├── logo.png ├── .cargo └── config.toml ├── Cargo.toml ├── THIRD_PARTY_LICENSES.md ├── .run └── clang-mc.run.xml ├── .gitignore ├── licenses └── LICENSE.MIT ├── scripts ├── asciiGenerator.py └── stackGenerator.py ├── README_CN.md ├── README.md └── CODE_OF_CONDUCT.md /src/rust/native/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod native; 2 | -------------------------------------------------------------------------------- /src/rust/objects/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod matrix_stack; -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xia-mc/clang-mc/HEAD/logo.png -------------------------------------------------------------------------------- /src/resources/include/stdlib.mch: -------------------------------------------------------------------------------- 1 | #once 2 | #include "memory" 3 | -------------------------------------------------------------------------------- /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target-dir = "cmake-build-release/rust" 3 | -------------------------------------------------------------------------------- /src/python/internal/__init__.py: -------------------------------------------------------------------------------- 1 | from . import Const 2 | from . import FileUtils 3 | -------------------------------------------------------------------------------- /src/rust/parse/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod pre_processer; 2 | pub mod pre_processer_api; 3 | -------------------------------------------------------------------------------- /src/resources/assets/stdlib/data/minecraft/tags/function/load.json: -------------------------------------------------------------------------------- 1 | { 2 | "values": [ 3 | "std:__init__" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /src/resources/assets/stdlib/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "description": "Clang-MC Standard Library (stdlib).", 4 | "pack_format": 61 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/resources/assets/stdlib/data/std/function/_internal/shrink_heap.mcfunction: -------------------------------------------------------------------------------- 1 | # 函数原型:void shrink_heap() 2 | # shrink的内部实现 3 | # 特别的,使用Minecraft数据包函数调用协议 4 | # 在Minecraft函数的原型:void shrink_heap(int shp) 5 | 6 | $data remove storage std:vm heap[$(shp)] 7 | -------------------------------------------------------------------------------- /src/rust/native/native.rs: -------------------------------------------------------------------------------- 1 | unsafe extern "C" { 2 | fn onTerminate(); 3 | } 4 | 5 | pub fn on_terminate() { 6 | println!("\x1b[31mfatal error:\x1b[97m crashed in rust\x1b[0m"); 7 | unsafe { 8 | onTerminate(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/resources/assets/stdlib/data/std/function/_internal/push_zero.mcfunction: -------------------------------------------------------------------------------- 1 | # 函数原型:void push_zero() 2 | # push_zero的内部实现 3 | # 特别的,使用Minecraft数据包函数调用协议 4 | # 在Minecraft函数的原型:void push_zero(int rsp) 5 | 6 | $data modify storage std:vm heap[$(rsp)] set value 0 7 | -------------------------------------------------------------------------------- /src/cpp/Main.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/18. 3 | // 4 | 5 | #ifndef CLANG_MC_MAIN_H 6 | #define CLANG_MC_MAIN_H 7 | 8 | #ifdef __cplusplus 9 | extern "C" 10 | #endif 11 | [[gnu::noinline]] int init(int argc, const char *argv[]); 12 | 13 | #endif //CLANG_MC_MAIN_H 14 | -------------------------------------------------------------------------------- /src/resources/assets/stdlib/data/std/function/_internal/load_heap.mcfunction: -------------------------------------------------------------------------------- 1 | # 函数原型:int load_heap() 2 | # heap/load的内部实现 3 | # 特别的,使用Minecraft数据包函数调用协议 4 | # 在Minecraft函数的原型:void load_heap(int ptr) 5 | 6 | $execute store result score rax vm_regs run data get storage std:vm heap[$(ptr)] 7 | -------------------------------------------------------------------------------- /src/resources/assets/stdlib/data/std/function/_internal/peek.mcfunction: -------------------------------------------------------------------------------- 1 | # 函数原型:void peek() 2 | # peek的内部辅助实现 3 | # 特别的,使用Minecraft数据包函数调用协议 4 | # 在Minecraft函数的原型:void pop(NBTString name, int rsp) 5 | 6 | $execute store result score $(name) vm_regs run data get storage std:vm heap[$(rsp)] 7 | -------------------------------------------------------------------------------- /src/resources/assets/stdlib/data/std/function/_internal/pop.mcfunction: -------------------------------------------------------------------------------- 1 | # 函数原型:void pop() 2 | # pop的内部辅助实现 3 | # 特别的,使用Minecraft数据包函数调用协议 4 | # 在Minecraft函数的原型:void pop(NBTString name, int rsp) 5 | 6 | $execute store result score $(name) vm_regs run data get storage std:vm heap[$(rsp)] 7 | -------------------------------------------------------------------------------- /src/resources/assets/stdlib/data/std/function/_internal/store_heap_custom2.mcfunction: -------------------------------------------------------------------------------- 1 | # 函数原型:void store_heap_custom2() 2 | # 汇编器的辅助实现 3 | # 特别的,使用Minecraft数据包函数调用协议 4 | # 在Minecraft函数的原型:void store_heap_custom2(int ptr, int value) 5 | 6 | $data modify storage std:vm heap[$(ptr)] set value $(value) 7 | -------------------------------------------------------------------------------- /src/resources/assets/stdlib/data/std/function/_internal/push.mcfunction: -------------------------------------------------------------------------------- 1 | # 函数原型:void push() 2 | # push的内部辅助实现 3 | # 特别的,使用Minecraft数据包函数调用协议 4 | # 在Minecraft函数的原型:void push(NBTString name, int rsp) 5 | 6 | $execute store result storage std:vm heap[$(rsp)] int 1 run scoreboard players get $(name) vm_regs 7 | -------------------------------------------------------------------------------- /src/resources/assets/stdlib/data/std/function/_internal/store_heap_custom3.mcfunction: -------------------------------------------------------------------------------- 1 | # 函数原型:void store_heap_custom3() 2 | # 汇编器的辅助实现 3 | # 特别的,使用Minecraft数据包函数调用协议 4 | # 在Minecraft函数的原型:void store_heap_custom3(int to, int from) 5 | 6 | $data modify storage std:vm heap[$(to)] set from storage std:vm heap[$(from)] 7 | -------------------------------------------------------------------------------- /src/resources/assets/stdlib/data/std/function/heap/load.mcfunction: -------------------------------------------------------------------------------- 1 | # 函数原型:int load(int ptr) 2 | # 获取堆中对应地址的元素 3 | 4 | data modify storage std:vm s2 set value {ptr: -1} 5 | execute store result storage std:vm s2.ptr int 1 run scoreboard players get r0 vm_regs 6 | function std:_internal/load_heap with storage std:vm s2 7 | -------------------------------------------------------------------------------- /src/cpp/i18n/I18nTemplate.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/18. 3 | // 4 | 5 | #ifndef CLANG_MC_I18NTEMPLATE_H 6 | #define CLANG_MC_I18NTEMPLATE_H 7 | 8 | static inline constexpr auto ZH_CN = "@YAML_ZH_CN@"; 9 | static inline constexpr auto EN_US = "@YAML_EN_US@"; 10 | 11 | #endif //CLANG_MC_I18NTEMPLATE_H 12 | -------------------------------------------------------------------------------- /src/resources/assets/stdlib/data/std/function/_internal/load_heap_custom.mcfunction: -------------------------------------------------------------------------------- 1 | # 函数原型:void load_heap_custom() 2 | # 汇编器的辅助实现 3 | # 特别的,使用Minecraft数据包函数调用协议 4 | # 在Minecraft函数的原型:void load_heap_custom(NBTString result, int ptr) 5 | 6 | $execute store result score $(result) vm_regs run data get storage std:vm heap[$(ptr)] 7 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "clang-mc" 3 | version = "0.0.0-dev" 4 | edition = "2024" 5 | 6 | [lib] 7 | crate-type = ["staticlib"] 8 | name = "clang_mc_rust" 9 | path = "src/rust/lib.rs" 10 | 11 | [profile.release] 12 | opt-level = "z" 13 | lto = true 14 | strip = true 15 | 16 | [dependencies] 17 | regex = "1.11.1" 18 | -------------------------------------------------------------------------------- /src/resources/assets/stdlib/data/std/function/_internal/merge_string2.mcfunction: -------------------------------------------------------------------------------- 1 | # 函数原型:void merge_string2() 2 | # merge_string的内部实现 3 | # 特别的,使用Minecraft数据包函数调用协议 4 | # 在Minecraft函数的原型:void merge_string2(NBTString left, NBTString right) 5 | # 会修改s3.string 6 | 7 | $data modify storage std:vm s3.string set value "$(left)$(right)" 8 | -------------------------------------------------------------------------------- /src/cpp/extern/ClRustAPI.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/3/28. 3 | // 4 | 5 | #ifndef CLANG_MC_CLRUSTAPI_H 6 | #define CLANG_MC_CLRUSTAPI_H 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | void ClRust_Init(void); 13 | 14 | #ifdef __cplusplus 15 | } 16 | #endif 17 | 18 | #endif //CLANG_MC_CLRUSTAPI_H 19 | -------------------------------------------------------------------------------- /src/resources/assets/stdlib/data/std/function/heap/store.mcfunction: -------------------------------------------------------------------------------- 1 | # 函数原型:void store(int ptr, int value) 2 | # 把value存入堆中对应的地址 3 | 4 | data modify storage std:vm s2 set value {ptr: -1} 5 | execute store result storage std:vm s2.ptr int 1 run scoreboard players get r0 vm_regs 6 | function std:_internal/store_heap with storage std:vm s2 7 | -------------------------------------------------------------------------------- /src/resources/assets/stdlib/data/std/function/_internal/store_heap.mcfunction: -------------------------------------------------------------------------------- 1 | # 函数原型:void store_heap([[maybe_unused]] int ptr, int value) 2 | # heap/store的内部实现 3 | # 特别的,使用Minecraft数据包函数调用协议 4 | # 在Minecraft函数的原型:void store_heap(int ptr) 5 | 6 | $execute store result storage std:vm heap[$(ptr)] int 1 run scoreboard players get r1 vm_regs 7 | -------------------------------------------------------------------------------- /src/resources/assets/stdlib/data/std/function/_internal/store_heap_custom.mcfunction: -------------------------------------------------------------------------------- 1 | # 函数原型:void store_heap_custom() 2 | # 汇编器的辅助实现 3 | # 特别的,使用Minecraft数据包函数调用协议 4 | # 在Minecraft函数的原型:void store_heap_custom(int ptr, NBTString value) 5 | 6 | $execute store result storage std:vm heap[$(ptr)] int 1 run scoreboard players get $(value) vm_regs 7 | -------------------------------------------------------------------------------- /src/cpp/ir/IRCommon.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/17. 3 | // 4 | 5 | #ifndef CLANG_MC_IRCOMMON_H 6 | #define CLANG_MC_IRCOMMON_H 7 | 8 | #include "utils/Common.h" 9 | #include "OpCommon.h" 10 | 11 | struct LineState; 12 | 13 | using McFunctions = HashMap; 14 | 15 | PURE OpPtr createOp(const LineState &line, const std::string_view &string); 16 | 17 | PURE bool isTerminate(const OpPtr &op); 18 | 19 | #endif //CLANG_MC_IRCOMMON_H 20 | -------------------------------------------------------------------------------- /src/resources/include/string.mch: -------------------------------------------------------------------------------- 1 | #once 2 | #include "int" 3 | 4 | 5 | // int String_Len(char *string) 6 | String_Len: 7 | mov rax, 0 8 | .loop: 9 | re [r0], 0 10 | add r0, 1 11 | add rax, 1 12 | jmp .loop 13 | 14 | 15 | // void String_Copy(char *src, char *dst) 16 | String_Copy: 17 | mov [r1], [r0] 18 | re [r0], 0 19 | add r0, 1 20 | add r1, 1 21 | jmp String_Copy 22 | 23 | 24 | #define strlen String_Len 25 | #define strcpy String_Copy 26 | -------------------------------------------------------------------------------- /THIRD_PARTY_LICENSES.md: -------------------------------------------------------------------------------- 1 | This project uses the following third-party software: 2 | 3 | 1. **Minecraft** (Proprietary, subject to the [Minecraft EULA](https://www.minecraft.net/en-us/eula)) 4 | 2. **LLVM Platform** (Apache License 2.0) 5 | 3. **ankerl::unordered_dense** (MIT License) 6 | 4. **fmt** (MIT License) 7 | 5. **spdlog** (MIT License) 8 | 6. **yaml-cpp** (MIT License) 9 | 10 | Please refer to the respective licenses for more details, which can be found in the `licenses/` directory. 11 | -------------------------------------------------------------------------------- /src/cpp/ir/values/Value.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/16. 3 | // 4 | 5 | #ifndef CLANG_MC_VALUE_H 6 | #define CLANG_MC_VALUE_H 7 | 8 | #include "utils/Common.h" 9 | 10 | class Value { 11 | public: 12 | explicit Value() = default; 13 | virtual ~Value() = default; 14 | 15 | /** 16 | * 生成值的字符串表示 17 | * 如"42", "rax" 18 | * @return 字符串表示 19 | */ 20 | [[nodiscard]] virtual std::string toString() const noexcept = 0; 21 | }; 22 | 23 | #endif //CLANG_MC_VALUE_H 24 | -------------------------------------------------------------------------------- /src/resources/include/event.mch: -------------------------------------------------------------------------------- 1 | #once 2 | 3 | #define Event_Unreg_TICK(labelName) inline schedule clear std:_internal/_wrapper_labelName 4 | 5 | #define Event_Reg_TICK(labelName) \ 6 | #push line \ 7 | #push label \ 8 | inline schedule function std:_internal/_wrapper_labelName 1t \ 9 | jmp after \ 10 | export std:_internal/_wrapper_labelName: \ 11 | inline schedule function std:_internal/_wrapper_labelName 1t \ 12 | jmp labelName \ 13 | after: \ 14 | #pop label \ 15 | #pop line 16 | -------------------------------------------------------------------------------- /src/mcasm/stdlib.mcasm: -------------------------------------------------------------------------------- 1 | #include "memory" 2 | 3 | // 每个内存块前,有一个元数据头 4 | // struct { 5 | // int blockSize; 6 | // } 7 | // 最高位存储是否可用(可用为0 (正数),占用为1 (负数)),剩下的位存储块大小 8 | // void init_malloc() 9 | init_malloc: 10 | // 放置元数据头 11 | mov [0], shp 12 | ret 13 | 14 | init_stack: 15 | // 初始化栈 16 | mov r0, 256 // 1kb内存 17 | call malloc 18 | mov rsp, rax 19 | add rsp, 256 // 栈顶 20 | ret 21 | 22 | export std:_internal/__init__: 23 | call init_malloc 24 | call init_stack 25 | ret 26 | -------------------------------------------------------------------------------- /src/cpp/utils/Native.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/24. 3 | // 4 | 5 | #ifndef CLANG_MC_NATIVE_H 6 | #define CLANG_MC_NATIVE_H 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | void initNative(); 13 | 14 | #ifdef __cplusplus 15 | [[noreturn]] 16 | #endif 17 | void onOOM(); 18 | 19 | void printStacktraceMsg(const char *err); 20 | 21 | void printStacktrace(); 22 | 23 | #ifdef __cplusplus 24 | [[noreturn]] 25 | #endif 26 | void onTerminate(); 27 | 28 | #ifdef __cplusplus 29 | } 30 | #endif 31 | 32 | #endif //CLANG_MC_NATIVE_H 33 | -------------------------------------------------------------------------------- /src/python/internal/Const.py: -------------------------------------------------------------------------------- 1 | import platform 2 | from pathlib import Path 3 | 4 | PROJECT_DIR = Path(__file__).absolute().parent.parent.parent.parent 5 | BUILD_DIR = Path(PROJECT_DIR, "build").absolute() 6 | BUILD_BIN_DIR = Path(BUILD_DIR, "bin").absolute() 7 | BUILD_TMP_DIR = Path(BUILD_DIR, "tmp").absolute() 8 | 9 | EXECUTABLE = Path(BUILD_BIN_DIR, "clang-mc.exe" if platform.system() == "Windows" else "clang-mc").absolute() 10 | 11 | MCASM_DIR = Path(PROJECT_DIR, "src/mcasm").absolute() 12 | RESOURCES_DIR = Path(PROJECT_DIR, "src/resources").absolute() 13 | -------------------------------------------------------------------------------- /src/rust/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #![no_builtins] 3 | 4 | use std::panic::{catch_unwind, set_hook}; 5 | use std::process::abort; 6 | use crate::native::native::on_terminate; 7 | 8 | mod native; 9 | mod parse; 10 | mod objects; 11 | 12 | #[allow(non_snake_case)] 13 | #[unsafe(no_mangle)] 14 | pub extern "C" fn ClRust_Init() { 15 | set_hook(Box::new(|_panic_info| { 16 | println!("\x1b[31mfatal error:\x1b[97m {}\x1b[0m", _panic_info.to_string()); 17 | let _ = catch_unwind(|| {}); 18 | on_terminate(); 19 | abort(); 20 | })); 21 | } 22 | -------------------------------------------------------------------------------- /src/cpp/utils/CLIUtils.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/3/15. 3 | // 4 | 5 | #include "ClangMc.h" 6 | 7 | int ARGC = 0; 8 | const char **ARGV = nullptr; 9 | 10 | PURE std::string getVersionMessage(const std::string& argv0) noexcept { 11 | auto result = i18nFormat("cli.version_message_template", 12 | ClangMc::NAME, ClangMc::VERSION, 13 | getTargetMachine(), __VERSION__, getExecutableDir(argv0)); 14 | #ifndef NDEBUG 15 | result += i18n("cli.debug_mode"); 16 | #endif 17 | return result; 18 | } 19 | -------------------------------------------------------------------------------- /src/cpp/ir/iops/Special.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/7/4. 3 | // 4 | 5 | #ifndef CLANG_MC_SPECIAL_H 6 | #define CLANG_MC_SPECIAL_H 7 | 8 | #include 9 | 10 | #include "Op.h" 11 | #include "utils/string/StringUtils.h" 12 | #include "ir/OpCommon.h" 13 | 14 | class Special : public virtual Op { 15 | public: 16 | explicit Special() noexcept = default; 17 | 18 | [[nodiscard]] std::string compile() const final { 19 | throw UnsupportedOperationException("Op can't be compile normally."); 20 | } 21 | }; 22 | 23 | #endif //CLANG_MC_SPECIAL_H 24 | -------------------------------------------------------------------------------- /src/cpp/ir/ops/Nop.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/3/2. 3 | // 4 | 5 | #ifndef CLANG_MC_NOP_H 6 | #define CLANG_MC_NOP_H 7 | 8 | #include "ir/iops/Op.h" 9 | #include "ir/OpCommon.h" 10 | 11 | class Nop : public Op { 12 | public: 13 | explicit Nop(i32 lineNumber) noexcept : Op("nop", lineNumber) { 14 | } 15 | 16 | [[nodiscard]] std::string toString() const noexcept override { 17 | return "nop"; 18 | } 19 | 20 | [[nodiscard]] std::string compile() const override { 21 | return ""; 22 | } 23 | }; 24 | 25 | #endif //CLANG_MC_NOP_H 26 | -------------------------------------------------------------------------------- /src/cpp/ir/values/Immediate.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/16. 3 | // 4 | 5 | #ifndef CLANG_MC_IMMEDIATE_H 6 | #define CLANG_MC_IMMEDIATE_H 7 | 8 | #include "Value.h" 9 | #include "objects/Int.h" 10 | 11 | class Immediate : public Value { 12 | private: 13 | const Int value; 14 | public: 15 | explicit Immediate(i32 value) : Value(), value(value) { 16 | } 17 | 18 | GETTER_POD(Value, value); 19 | 20 | [[nodiscard]] std::string toString() const noexcept override { 21 | return std::to_string(getValue()); 22 | } 23 | }; 24 | 25 | #endif //CLANG_MC_IMMEDIATE_H 26 | -------------------------------------------------------------------------------- /src/cpp/builder/Builder.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/3/12. 3 | // 4 | 5 | #ifndef CLANG_MC_BUILDER_H 6 | #define CLANG_MC_BUILDER_H 7 | 8 | 9 | #include "ir/IRCommon.h" 10 | #include "config/Config.h" 11 | 12 | class Builder { 13 | private: 14 | const Config &config; 15 | std::vector mcFunctions; 16 | public: 17 | explicit Builder(const Config &config, std::vector &&mcFunctions) : config(config), mcFunctions(std::move(mcFunctions)) { 18 | } 19 | 20 | void build(); 21 | 22 | void link() const; 23 | }; 24 | 25 | 26 | #endif //CLANG_MC_BUILDER_H 27 | -------------------------------------------------------------------------------- /src/cpp/builder/PostOptimizer.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/3/14. 3 | // 4 | 5 | #ifndef CLANG_MC_POSTOPTIMIZER_H 6 | #define CLANG_MC_POSTOPTIMIZER_H 7 | 8 | 9 | #include "ir/IRCommon.h" 10 | #include "config/Config.h" 11 | #include 12 | 13 | class PostOptimizer { 14 | private: 15 | std::vector &mcFunctions; 16 | public: 17 | explicit PostOptimizer(std::vector &mcFunctions) : mcFunctions(mcFunctions) { 18 | } 19 | 20 | static void doSingleOptimize(std::string &code); 21 | 22 | void optimize(); 23 | }; 24 | 25 | 26 | #endif //CLANG_MC_POSTOPTIMIZER_H 27 | -------------------------------------------------------------------------------- /src/cpp/ir/ops/Ret.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/19. 3 | // 4 | 5 | #ifndef CLANG_MC_RET_H 6 | #define CLANG_MC_RET_H 7 | 8 | #include "ir/iops/Op.h" 9 | #include "utils/string/StringUtils.h" 10 | #include "utils/Common.h" 11 | 12 | class Ret : public Op { 13 | public: 14 | explicit Ret(const i32 lineNumber) : Op("ret", lineNumber) { 15 | } 16 | 17 | [[nodiscard]] std::string toString() const noexcept override { 18 | return "ret"; 19 | } 20 | 21 | [[nodiscard]] std::string compile() const override { 22 | return "return 1"; 23 | } 24 | }; 25 | 26 | #endif //CLANG_MC_RET_H 27 | -------------------------------------------------------------------------------- /src/cpp/i18n/I18n.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/18. 3 | // 4 | 5 | #ifndef CLANG_MC_I18N_H 6 | #define CLANG_MC_I18N_H 7 | 8 | #include "utils/Common.h" 9 | 10 | void initI18n(); 11 | 12 | std::string &i18n(const std::string_view &key, Hash keyHash); 13 | 14 | __forceinline constexpr std::string &i18n(const std::string_view &key) { 15 | return i18n(key, hash(key)); 16 | } 17 | 18 | template 19 | __forceinline std::string i18nFormat(const std::string_view key, T &&... args) { 20 | return fmt::format(fmt::runtime(i18n(key)), std::forward(args)...); 21 | } 22 | 23 | #endif //CLANG_MC_I18N_H 24 | -------------------------------------------------------------------------------- /src/cpp/ir/State.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/7/21. 3 | // 4 | 5 | #ifndef CLANG_MC_STATE_H 6 | #define CLANG_MC_STATE_H 7 | 8 | #include "utils/Common.h" 9 | #include "ir/ops/Label.h" 10 | #include "ir/iops/CallLike.h" 11 | 12 | struct LineState { 13 | i32 lineNumber = -1; 14 | bool noWarn = false; 15 | std::string_view filename = "Unknown Source"; 16 | const Label *lastLabel = nullptr; 17 | }; 18 | 19 | struct LabelState { 20 | HashMap renameLabelMap = HashMap(); 21 | std::vector toRenameLabel = std::vector(); 22 | }; 23 | 24 | #endif //CLANG_MC_STATE_H 25 | -------------------------------------------------------------------------------- /src/cpp/ir/ops/Syscall.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/8/11. 3 | // 4 | 5 | #ifndef CLANG_MC_SYSCALL_H 6 | #define CLANG_MC_SYSCALL_H 7 | 8 | #include "ir/iops/Op.h" 9 | #include "utils/string/StringUtils.h" 10 | #include "utils/Common.h" 11 | 12 | class Syscall : public Op { 13 | public: 14 | explicit Syscall(const i32 lineNumber) : Op("syscall", lineNumber) { 15 | } 16 | 17 | [[nodiscard]] std::string toString() const noexcept override { 18 | return "syscall"; 19 | } 20 | 21 | [[nodiscard]] std::string compile() const override { 22 | return "mcfp syscall"; 23 | } 24 | }; 25 | 26 | #endif //CLANG_MC_SYSCALL_H 27 | -------------------------------------------------------------------------------- /src/cpp/utils/Stream.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/7/4. 3 | // 4 | 5 | #ifndef CLANG_MC_STREAM_H 6 | #define CLANG_MC_STREAM_H 7 | 8 | 9 | #include "utils/Common.h" 10 | 11 | namespace stream { 12 | template 13 | auto map(const std::vector& vector, F&& mapper) -> std::vector()))> { 14 | using R = decltype(mapper(std::declval())); 15 | std::vector result(vector.size()); 16 | for (size_t i = 0; i < vector.size(); ++i) { 17 | result[i] = mapper(vector[i]); 18 | } 19 | return result; 20 | } 21 | } 22 | 23 | #endif //CLANG_MC_STREAM_H 24 | -------------------------------------------------------------------------------- /src/cpp/ir/values/Symbol.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/7/5. 3 | // 4 | 5 | #ifndef CLANG_MC_SYMBOL_H 6 | #define CLANG_MC_SYMBOL_H 7 | 8 | #include 9 | 10 | #include "Value.h" 11 | 12 | class Symbol : public Value { 13 | private: 14 | const std::string name; 15 | const Hash nameHash; 16 | public: 17 | explicit Symbol(std::string name) : Value(), name(std::move(name)), nameHash(hash(this->name)) { 18 | } 19 | 20 | GETTER(Name, name); 21 | 22 | GETTER_POD(NameHash, nameHash); 23 | 24 | [[nodiscard]] std::string toString() const noexcept override { 25 | return getName(); 26 | } 27 | }; 28 | 29 | #endif //CLANG_MC_SYMBOL_H 30 | -------------------------------------------------------------------------------- /src/cpp/ir/iops/JmpLike.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/22. 3 | // 4 | 5 | #ifndef CLANG_MC_JMPLIKE_H 6 | #define CLANG_MC_JMPLIKE_H 7 | 8 | #include 9 | 10 | #include "Op.h" 11 | #include "utils/string/StringUtils.h" 12 | #include "ir/OpCommon.h" 13 | #include "CallLike.h" 14 | 15 | class JmpLike : public virtual CallLike { 16 | public: 17 | explicit JmpLike() noexcept = default; 18 | 19 | [[nodiscard]] std::string compile() const override { 20 | throw UnsupportedOperationException("Op can't be compile normally."); 21 | } 22 | 23 | [[nodiscard]] virtual std::string compile(const JmpMap &jmpMap) const = 0; 24 | }; 25 | 26 | #endif //CLANG_MC_JMPLIKE_H 27 | -------------------------------------------------------------------------------- /src/cpp/ir/values/SymbolPtr.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/7/6. 3 | // 4 | 5 | #ifndef CLANG_MC_SYMBOLPTR_H 6 | #define CLANG_MC_SYMBOLPTR_H 7 | 8 | #include 9 | #include "Value.h" 10 | 11 | class SymbolPtr : public Value { 12 | private: 13 | const std::string name; 14 | const Hash nameHash; 15 | public: 16 | explicit SymbolPtr(std::string name) : Value(), name(std::move(name)), nameHash(hash(this->name)) { 17 | } 18 | 19 | GETTER(Name, name); 20 | 21 | GETTER_POD(NameHash, nameHash); 22 | 23 | [[nodiscard]] std::string toString() const noexcept override { 24 | return getName(); 25 | } 26 | }; 27 | 28 | #endif //CLANG_MC_SYMBOLPTR_H 29 | -------------------------------------------------------------------------------- /src/resources/assets/stdlib/data/std/function/heap/expand.mcfunction: -------------------------------------------------------------------------------- 1 | # 函数原型:void expand(int size) 2 | # 扩展堆空间额外size个元素的大小,每个元素通常4字节 3 | # size将被向上取整到8的倍数(32字节) 4 | 5 | scoreboard players add shp vm_regs 8 6 | scoreboard players remove r0 vm_regs 8 7 | 8 | data modify storage std:vm heap append value 0 9 | data modify storage std:vm heap append value 0 10 | data modify storage std:vm heap append value 0 11 | data modify storage std:vm heap append value 0 12 | data modify storage std:vm heap append value 0 13 | data modify storage std:vm heap append value 0 14 | data modify storage std:vm heap append value 0 15 | data modify storage std:vm heap append value 0 16 | 17 | execute if score r0 vm_regs matches 1.. run function std:heap/expand 18 | -------------------------------------------------------------------------------- /src/cpp/ir/ops/Inline.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/27. 3 | // 4 | 5 | #ifndef CLANG_MC_INLINE_H 6 | #define CLANG_MC_INLINE_H 7 | 8 | #include "ir/iops/Op.h" 9 | #include "utils/string/StringUtils.h" 10 | #include "utils/Common.h" 11 | 12 | class Inline : public Op { 13 | private: 14 | const std::string code; 15 | public: 16 | explicit Inline(const i32 lineNumber, std::string code) : Op("inline", lineNumber), code(std::move(code)) { 17 | } 18 | 19 | [[nodiscard]] std::string toString() const noexcept override { 20 | return fmt::format("inline {}", code); 21 | } 22 | 23 | [[nodiscard]] std::string compile() const override { 24 | return code; 25 | } 26 | }; 27 | 28 | #endif //CLANG_MC_INLINE_H 29 | -------------------------------------------------------------------------------- /.run/clang-mc.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 10 | -------------------------------------------------------------------------------- /src/cpp/ClangMc.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/13. 3 | // 4 | 5 | #ifndef CLANG_MC_CLANGMC_H 6 | #define CLANG_MC_CLANGMC_H 7 | 8 | #include "config/Config.h" 9 | #include "utils/Common.h" 10 | #include "ir/IR.h" 11 | 12 | class ClangMc { 13 | public: 14 | static inline constexpr auto NAME = "clang-mc"; 15 | static inline constexpr auto VERSION = "0.0.0-dev"; 16 | 17 | const Config &config; 18 | Logger logger; 19 | 20 | explicit ClangMc(const Config &config); 21 | 22 | ~ClangMc(); 23 | 24 | void start(); 25 | 26 | [[noreturn]] static void exit(); 27 | private: 28 | void ensureEnvironment() const; 29 | 30 | void ensureValidConfig(); 31 | 32 | void ensureBuildDir(); 33 | }; 34 | 35 | #endif //CLANG_MC_CLANGMC_H 36 | -------------------------------------------------------------------------------- /src/python/internal/FileUtils.py: -------------------------------------------------------------------------------- 1 | import shutil 2 | from pathlib import Path 3 | 4 | 5 | def ensureDirectoryExist(directory: Path) -> bool: 6 | """确保目录存在,返回值为是否创建的新目录""" 7 | if directory.exists(): 8 | return False 9 | 10 | parent = directory.parent 11 | if parent is not directory: 12 | ensureDirectoryExist(parent) 13 | directory.mkdir() 14 | return True 15 | 16 | 17 | def ensureDirectoryEmpty(directory: Path) -> None: 18 | """确保目录存在,并为空""" 19 | if ensureDirectoryExist(directory): 20 | return 21 | 22 | shutil.rmtree(directory) 23 | directory.mkdir() 24 | 25 | 26 | def ensureDirectoryNotExist(directory: Path) -> None: 27 | if not directory.exists(): 28 | return 29 | 30 | shutil.rmtree(directory) 31 | -------------------------------------------------------------------------------- /src/rust/objects/matrix_stack.rs: -------------------------------------------------------------------------------- 1 | use std::vec::Vec; 2 | 3 | pub struct MatrixStack { 4 | stack: Vec, 5 | } 6 | 7 | #[allow(dead_code)] 8 | impl MatrixStack { 9 | pub fn new() -> Self { 10 | Self { 11 | stack: Vec::new(), 12 | } 13 | } 14 | 15 | #[inline(always)] 16 | pub fn push_matrix(&mut self, object: T) { 17 | self.stack.push(object); 18 | } 19 | 20 | #[inline(always)] 21 | pub fn pop_matrix(&mut self) -> T { 22 | self.stack.pop().unwrap() 23 | } 24 | 25 | #[inline(always)] 26 | pub fn is_empty(&self) -> bool { 27 | self.stack.is_empty() 28 | } 29 | 30 | #[inline(always)] 31 | pub fn size(&self) -> usize { 32 | self.stack.len() 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/cpp/ir/iops/CallLike.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/22. 3 | // 4 | 5 | #ifndef CLANG_MC_CALLLIKE_H 6 | #define CLANG_MC_CALLLIKE_H 7 | 8 | #include 9 | 10 | #include "Op.h" 11 | #include "utils/string/StringUtils.h" 12 | #include "ir/OpCommon.h" 13 | 14 | class CallLike : public virtual Op { 15 | protected: 16 | std::string label; 17 | Hash labelHash; 18 | public: 19 | explicit CallLike(std::string label) noexcept: label(std::move(label)), labelHash(hash(this->label)) { 20 | } 21 | 22 | GETTER(Label, label); 23 | 24 | GETTER_POD(LabelHash, labelHash); 25 | 26 | void setLabel(std::string newLabel) { 27 | label = std::move(newLabel); 28 | labelHash = hash(this->label); 29 | } 30 | }; 31 | 32 | #endif //CLANG_MC_CALLLIKE_H 33 | -------------------------------------------------------------------------------- /src/cpp/parse/ParseManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/3/22. 3 | // 4 | 5 | #ifndef CLANG_MC_PARSEMANAGER_H 6 | #define CLANG_MC_PARSEMANAGER_H 7 | 8 | 9 | #include "config/Config.h" 10 | #include "ir/IR.h" 11 | 12 | class ParseManager { 13 | private: 14 | const Config &config; 15 | const Logger &logger; 16 | HashMap sources; 17 | HashMap> defines; 18 | std::vector irs; 19 | public: 20 | explicit ParseManager(const Config &config, const Logger &logger) noexcept : config(config), logger(logger) { 21 | } 22 | 23 | void loadSource(); 24 | 25 | void loadIR(); 26 | 27 | void freeSource(); 28 | 29 | void freeIR(); 30 | 31 | GETTER(Sources, sources) 32 | 33 | GETTER(IRs, irs); 34 | }; 35 | 36 | 37 | #endif //CLANG_MC_PARSEMANAGER_H 38 | -------------------------------------------------------------------------------- /src/resources/include/mcfp.mch: -------------------------------------------------------------------------------- 1 | #once 2 | #define MIN_API_VER 1 3 | 4 | #define SYS_getAPIVersion 0 5 | #define SYS_nanoTimes 1 6 | #define SYS_print 2 7 | 8 | // int _Mcfp_GetAPIVer() except 9 | _Mcfp_GetAPIVer: 10 | mov rax, SYS_getAPIVersion 11 | syscall 12 | ret 13 | 14 | // int Mcfp_GetAPIVer() 15 | Mcfp_GetAPIVer: 16 | static .value -1 17 | mov rax, [.value] 18 | rg rax, -1 19 | 20 | call _Mcfp_GetAPIVer 21 | // 若syscall成功且满足,rax应大于等于MIN_API_VER 22 | // 若syscall失败,rax应等于SYS_getAPIVersion,也等于0表示不支持 23 | mov [.value], rax 24 | ret 25 | 26 | // bool Mcfp_Check() 27 | Mcfp_Check: 28 | call Mcfp_GetAPIVer 29 | mov rax, 1 30 | rge rax, MIN_API_VER 31 | mov rax, 0 32 | ret 33 | 34 | // void Mcfp_NanoTimes(Int64 *value) 35 | Mcfp_NanoTimes: 36 | mov rax, SYS_nanoTimes 37 | syscall 38 | ret 39 | -------------------------------------------------------------------------------- /src/cpp/patches/fmt/core.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/5/24. 3 | // 4 | 5 | #ifndef CLANG_MC_FORMAT_H 6 | #define CLANG_MC_FORMAT_H 7 | 8 | #ifdef CMC_DEFINED___cpp_lib_is_constant_evaluated 9 | #error "Name 'CMC_DEFINED___cpp_lib_is_constant_evaluated' shouldn't be defined." 10 | #endif 11 | 12 | // patch for fmt/base.h 13 | #ifdef __cpp_lib_is_constant_evaluated // NOLINT(*-reserved-identifier) 14 | #define CMC_DEFINED___cpp_lib_is_constant_evaluated __cpp_lib_is_constant_evaluated // NOLINT(*-reserved-identifier) 15 | #undef __cpp_lib_is_constant_evaluated 16 | #endif 17 | 18 | #include "fmt/format.h" 19 | 20 | // restore patch 21 | #ifdef CMC_DEFINED___cpp_lib_is_constant_evaluated 22 | #define __cpp_lib_is_constant_evaluated CMC_DEFINED___cpp_lib_is_constant_evaluated 23 | #undef CMC_DEFINED___cpp_lib_is_constant_evaluated 24 | #endif 25 | 26 | #endif //CLANG_MC_FORMAT_H 27 | -------------------------------------------------------------------------------- /src/cpp/ir/ops/Jmp.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/19. 3 | // 4 | 5 | #ifndef CLANG_MC_JMP_H 6 | #define CLANG_MC_JMP_H 7 | 8 | #include "ir/iops/Op.h" 9 | #include "ir/iops/JmpLike.h" 10 | #include "utils/string/StringUtils.h" 11 | #include "ir/OpCommon.h" 12 | #include "utils/string/StringBuilder.h" 13 | 14 | class Jmp : public JmpLike { 15 | public: 16 | explicit Jmp(const i32 lineNumber, std::string label) noexcept: 17 | Op("jmp", lineNumber), CallLike(std::move(label)) { 18 | } 19 | 20 | [[nodiscard]] std::string toString() const noexcept override { 21 | return fmt::format("jmp {}", label); 22 | } 23 | 24 | [[nodiscard]] std::string compile(const JmpMap &jmpMap) const override { 25 | assert(jmpMap.contains(labelHash)); 26 | return string::join(jmpMap.at(labelHash), '\n'); 27 | } 28 | }; 29 | 30 | #endif //CLANG_MC_JMP_H 31 | -------------------------------------------------------------------------------- /src/cpp/objects/MatrixStack.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/5/26. 3 | // 4 | 5 | #ifndef CLANG_MC_MATRIXSTACK_H 6 | #define CLANG_MC_MATRIXSTACK_H 7 | 8 | 9 | #include 10 | 11 | template 12 | class MatrixStack { 13 | private: 14 | std::stack stack = std::stack(); 15 | public: 16 | explicit MatrixStack() = default; 17 | 18 | __forceinline void pushMatrix(T &&object) { 19 | stack.push(std::move(object)); 20 | } 21 | 22 | __forceinline void pushMatrix(const T object) { 23 | stack.push(std::move(object)); 24 | } 25 | 26 | __forceinline T popMatrix() { 27 | auto result = std::move(stack.top()); 28 | stack.pop(); 29 | return result; 30 | } 31 | 32 | __forceinline bool isEmpty() { 33 | return stack.empty(); 34 | } 35 | 36 | __forceinline u32 size() { 37 | return stack.size(); 38 | } 39 | }; 40 | 41 | 42 | #endif //CLANG_MC_MATRIXSTACK_H 43 | -------------------------------------------------------------------------------- /src/cpp/config/ArgParser.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/13. 3 | // 4 | 5 | #ifndef CLANG_MC_ARGPARSER_H 6 | #define CLANG_MC_ARGPARSER_H 7 | 8 | #include "Config.h" 9 | #include "string" 10 | #include "unordered_set" 11 | #include "utils/string/StringUtils.h" 12 | #include "utils/Common.h" 13 | 14 | // 需要额外参数的参数项 15 | static inline const HashSet DATA_ARGS = { 16 | hash("--output"), hash("-o"), 17 | hash("--build-dir"), hash("-B"), 18 | hash("--namespace"), hash("-N"), 19 | hash("-I"), 20 | hash("--data-dir"), hash("-D") 21 | }; 22 | 23 | class ArgParser { 24 | private: 25 | Config &config; 26 | std::string lastString; 27 | bool required = false; 28 | 29 | void setNameSpace(const std::string &arg); 30 | 31 | public: 32 | explicit ArgParser(Config &config); 33 | 34 | void next(const std::string &string); 35 | 36 | void end(); 37 | 38 | GETTER(Config, config); 39 | }; 40 | 41 | #endif //CLANG_MC_ARGPARSER_H 42 | -------------------------------------------------------------------------------- /src/cpp/ir/controlFlow/JmpTable.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/3/13. 3 | // 4 | 5 | #ifndef CLANG_MC_JMPTABLE_H 6 | #define CLANG_MC_JMPTABLE_H 7 | 8 | #include "ir/IRCommon.h" 9 | #include "ir/ops/Label.h" 10 | 11 | namespace { 12 | inline std::string_view LABEL_RET_CMD = "return 1"; 13 | } 14 | 15 | class JmpTable { 16 | private: 17 | const std::vector &values; 18 | const LabelMap &labelMap; 19 | JmpMap jmpMap = JmpMap(); 20 | 21 | // 缓存data,以优化空间复杂度 22 | std::vector data; 23 | std::string_view *dataView = nullptr; 24 | public: 25 | explicit JmpTable(const std::vector &values, const LabelMap &labelMap) noexcept: 26 | values(values), labelMap(labelMap) { 27 | jmpMap.emplace(hash(LABEL_RET), std::span(&LABEL_RET_CMD, 1)); 28 | } 29 | 30 | ~JmpTable() { 31 | free(dataView); 32 | } 33 | 34 | void make(); 35 | 36 | GETTER(JmpMap, jmpMap); 37 | }; 38 | 39 | 40 | #endif //CLANG_MC_JMPTABLE_H 41 | -------------------------------------------------------------------------------- /src/cpp/ir/ops/Call.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/19. 3 | // 4 | 5 | #ifndef CLANG_MC_CALL_H 6 | #define CLANG_MC_CALL_H 7 | 8 | #include "ir/iops/Op.h" 9 | #include "ir/iops/CallLike.h" 10 | #include "utils/string/StringUtils.h" 11 | #include "ir/OpCommon.h" 12 | 13 | class Call : public CallLike { 14 | public: 15 | explicit Call(const i32 lineNumber, std::string label) noexcept: 16 | Op("call", lineNumber), CallLike(std::move(label)) { 17 | } 18 | 19 | [[nodiscard]] std::string toString() const noexcept override { 20 | return fmt::format("call {}", label); 21 | } 22 | 23 | [[nodiscard]] std::string compile() const override { 24 | throw UnsupportedOperationException("Op can't be compile normally."); 25 | } 26 | 27 | [[nodiscard]] std::string compile(const LabelMap &labelMap) const { 28 | assert(labelMap.contains(labelHash)); 29 | return fmt::format("function {}", labelMap.at(labelHash)); 30 | } 31 | }; 32 | 33 | #endif //CLANG_MC_CALL_H 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | !**/src/main/**/build/ 5 | !**/src/test/**/build/ 6 | 7 | ### IntelliJ IDEA ### 8 | .idea/modules.xml 9 | .idea/jarRepositories.xml 10 | .idea/compiler.xml 11 | .idea/libraries/ 12 | *.iws 13 | *.iml 14 | *.ipr 15 | out/ 16 | !**/src/main/**/out/ 17 | !**/src/test/**/out/ 18 | 19 | ### Eclipse ### 20 | .apt_generated 21 | .classpath 22 | .factorypath 23 | .project 24 | .settings 25 | .springBeans 26 | .sts4-cache 27 | bin/ 28 | !**/src/main/**/bin/ 29 | !**/src/test/**/bin/ 30 | 31 | ### NetBeans ### 32 | /nbproject/private/ 33 | /nbbuild/ 34 | /dist/ 35 | /nbdist/ 36 | /.nb-gradle/ 37 | 38 | ### VS Code ### 39 | .vscode/ 40 | 41 | ### Mac OS ### 42 | .DS_Store 43 | 44 | /.idea/ 45 | /config.yaml 46 | /input.jar 47 | /output.jar 48 | /IR/ 49 | /latest.log 50 | /generated/ 51 | /cache/ 52 | /cmake-build-debug/ 53 | /cmake-build-release/ 54 | /build/ 55 | /target/ 56 | /cmake-build-release-/ 57 | /cmake-build-release-obfuscate/ 58 | 59 | Cargo.lock 60 | __pycache__ 61 | -------------------------------------------------------------------------------- /src/cpp/ir/iops/Op.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/16. 3 | // 4 | 5 | #ifndef CLANG_MC_OP_H 6 | #define CLANG_MC_OP_H 7 | 8 | 9 | class IR; 10 | 11 | class Op { 12 | private: 13 | const std::string_view name; // constexpr 14 | 15 | protected: 16 | IR *ir; 17 | 18 | public: 19 | explicit Op(std::string_view &&name, [[maybe_unused]] i32 lineNumber) noexcept : name(name), ir(nullptr) { 20 | } 21 | 22 | virtual ~Op() = default; 23 | 24 | virtual void withIR(IR *context) { 25 | this->ir = context; 26 | }; 27 | 28 | /** 29 | * 生成指令的字符串表示 30 | * @return 字符串表示 31 | */ 32 | [[nodiscard]] virtual std::string toString() const = 0; 33 | 34 | /** 35 | * 编译prefix指令到McFunction,通常由基类执行 36 | * @return McFunction代码 37 | */ 38 | [[nodiscard]] virtual std::string compilePrefix() const { 39 | return ""; 40 | } 41 | 42 | /** 43 | * 编译指令到McFunction 44 | * @return McFunction代码 45 | */ 46 | [[nodiscard]] virtual std::string compile() const = 0; 47 | }; 48 | 49 | 50 | #endif //CLANG_MC_OP_H 51 | -------------------------------------------------------------------------------- /src/cpp/ir/ops/Static.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/7/4. 3 | // 4 | 5 | #ifndef CLANG_MC_STATIC_H 6 | #define CLANG_MC_STATIC_H 7 | 8 | #include "ir/iops/Op.h" 9 | #include "ir/iops/Special.h" 10 | #include "utils/string/StringUtils.h" 11 | #include "ir/OpCommon.h" 12 | #include "objects/Int.h" 13 | #include "utils/Stream.h" 14 | 15 | 16 | class Static : public Special { 17 | private: 18 | std::string name; 19 | Hash nameHash; 20 | std::vector data; 21 | public: 22 | explicit Static(const i32 lineNumber, std::string name, std::vector &&data) noexcept: 23 | Op("static", lineNumber), name(std::move(name)), nameHash(hash(this->name)), data(std::move(data)) { 24 | } 25 | 26 | [[nodiscard]] std::string toString() const noexcept override { 27 | return fmt::format( 28 | "static {} [{}]", 29 | name, string::join(stream::map(data, FUNC_WITH(std::to_string)), ", ")); 30 | } 31 | 32 | GETTER(Name, name) 33 | 34 | GETTER_POD(NameHash, nameHash); 35 | 36 | GETTER(Data, data); 37 | }; 38 | 39 | #endif //CLANG_MC_STATIC_H 40 | -------------------------------------------------------------------------------- /licenses/LICENSE.MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 WittleWolfie 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 | -------------------------------------------------------------------------------- /src/cpp/ir/ops/Peek.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/27. 3 | // 4 | 5 | #ifndef CLANG_MC_PEEK_H 6 | #define CLANG_MC_PEEK_H 7 | 8 | #include "ir/iops/Op.h" 9 | #include "ir/values/Register.h" 10 | #include "utils/string/StringUtils.h" 11 | #include "utils/Common.h" 12 | 13 | class Peek : public Op { 14 | private: 15 | const Register *reg; 16 | public: 17 | explicit Peek(const i32 lineNumber, Register *reg) : Op("peek", lineNumber), reg(reg) { 18 | assert(reg != nullptr); 19 | if (!reg->getPushable()) { 20 | throw ParseException(i18n("ir.invalid_op")); 21 | } 22 | } 23 | 24 | [[nodiscard]] std::string toString() const noexcept override { 25 | return fmt::format("peek {}", reg->toString()); 26 | } 27 | 28 | [[nodiscard]] std::string compile() const override { 29 | return fmt::format("data modify storage std:vm s2 set value {{name: \"{}\", rsp: -1}}\n" 30 | "execute store result storage std:vm s2.rsp int 1 run scoreboard players get rsp vm_regs\n" 31 | "\n" 32 | "function std:_internal/peek with storage std:vm s2", reg->getName()); 33 | } 34 | }; 35 | 36 | #endif //CLANG_MC_PEEK_H 37 | -------------------------------------------------------------------------------- /src/cpp/extern/ResourceManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/3/17. 3 | // 4 | 5 | #ifndef CLANG_MC_RESOURCEMANAGER_H 6 | #define CLANG_MC_RESOURCEMANAGER_H 7 | 8 | #include "utils/FileUtils.h" 9 | #include "utils/CLIUtils.h" 10 | 11 | inline Path INSTALL_PATH; 12 | 13 | inline Path BIN_PATH; 14 | 15 | inline Path ASSETS_PATH; 16 | 17 | inline Path STDLIB_PATH; 18 | 19 | inline Path INCLUDE_PATH; 20 | 21 | static inline bool initResources() { 22 | try { 23 | BIN_PATH = Path(getExecutableDir(getArgv0())); 24 | if (!BIN_PATH.has_parent_path()) { 25 | return false; 26 | } 27 | INSTALL_PATH = BIN_PATH.parent_path(); 28 | if (BIN_PATH != INSTALL_PATH / "bin") { 29 | return false; 30 | } 31 | ASSETS_PATH = INSTALL_PATH / "assets"; 32 | STDLIB_PATH = ASSETS_PATH / "stdlib"; 33 | INCLUDE_PATH = INSTALL_PATH / "include"; 34 | 35 | for (const auto &path : {ASSETS_PATH, STDLIB_PATH, INCLUDE_PATH}) { // NOLINT(*-use-anyofallof) 36 | if (!exists(path) || !is_directory(path)) return false; 37 | } 38 | return true; 39 | } catch (...) { 40 | return false; 41 | } 42 | } 43 | 44 | #endif //CLANG_MC_RESOURCEMANAGER_H 45 | -------------------------------------------------------------------------------- /src/cpp/ir/ops/Pop.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/27. 3 | // 4 | 5 | #ifndef CLANG_MC_POP_H 6 | #define CLANG_MC_POP_H 7 | 8 | #include "ir/iops/Op.h" 9 | #include "ir/values/Register.h" 10 | #include "utils/string/StringUtils.h" 11 | #include "utils/Common.h" 12 | 13 | class Pop : public Op { 14 | private: 15 | const Register *reg; 16 | public: 17 | explicit Pop(const i32 lineNumber, Register *reg) : Op("pop", lineNumber), reg(reg) { 18 | assert(reg != nullptr); 19 | if (!reg->getPushable()) { 20 | throw ParseException(i18n("ir.invalid_op")); 21 | } 22 | } 23 | 24 | [[nodiscard]] std::string toString() const noexcept override { 25 | return fmt::format("pop {}", reg->toString()); 26 | } 27 | 28 | [[nodiscard]] std::string compile() const override { 29 | return fmt::format("data modify storage std:vm s2 set value {{name: \"{}\", rsp: -1}}\n" 30 | "execute store result storage std:vm s2.rsp int 1 run scoreboard players get rsp vm_regs\n" 31 | "function std:_internal/pop with storage std:vm s2\n" 32 | "scoreboard players add rsp vm_regs 1", reg->getName()); 33 | } 34 | }; 35 | 36 | #endif //CLANG_MC_POP_H 37 | -------------------------------------------------------------------------------- /src/cpp/ir/ops/Push.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/27. 3 | // 4 | 5 | #ifndef CLANG_MC_PUSH_H 6 | #define CLANG_MC_PUSH_H 7 | 8 | #include "ir/iops/Op.h" 9 | #include "ir/values/Register.h" 10 | #include "utils/string/StringUtils.h" 11 | #include "utils/Common.h" 12 | 13 | class Push : public Op { 14 | private: 15 | const Register *reg; 16 | public: 17 | explicit Push(const i32 lineNumber, Register *reg) : Op("push", lineNumber), reg(reg) { 18 | assert(reg != nullptr); 19 | if (!reg->getPushable()) { 20 | throw ParseException(i18n("ir.invalid_op")); 21 | } 22 | } 23 | 24 | [[nodiscard]] std::string toString() const noexcept override { 25 | return fmt::format("push {}", reg->toString()); 26 | } 27 | 28 | [[nodiscard]] std::string compile() const override { 29 | return fmt::format("scoreboard players remove rsp vm_regs 1\n" 30 | "data modify storage std:vm s2 set value {{name: \"{}\", rsp: -1}}\n" 31 | "execute store result storage std:vm s2.rsp int 1 run scoreboard players get rsp vm_regs\n" 32 | "function std:_internal/push with storage std:vm s2", reg->getName()); 33 | } 34 | }; 35 | 36 | #endif //CLANG_MC_PUSH_H 37 | -------------------------------------------------------------------------------- /src/cpp/config/Config.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/13. 3 | // 4 | 5 | #ifndef CLANG_MC_CONFIG_H 6 | #define CLANG_MC_CONFIG_H 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | class Config { 13 | private: 14 | std::vector input = std::vector(); 15 | std::vector includes = std::vector(); 16 | Path output = Path("output"); 17 | Path buildDir = Path("build"); 18 | Path dataDir = Path(""); 19 | std::string nameSpace; 20 | bool compileOnly = false; 21 | bool preprocessOnly = false; 22 | bool logFile = false; 23 | bool debugInfo = false; 24 | bool werror = false; 25 | bool noWarn = false; 26 | public: 27 | explicit Config() = default; 28 | 29 | DATA(Input, input); 30 | 31 | DATA(Includes, includes); 32 | 33 | DATA(Output, output); 34 | 35 | DATA(BuildDir, buildDir); 36 | 37 | DATA(DataDir, dataDir); 38 | 39 | DATA(NameSpace, nameSpace); 40 | 41 | DATA_POD(CompileOnly, compileOnly); 42 | 43 | DATA_POD(PreprocessOnly, preprocessOnly); 44 | 45 | DATA_POD(LogFile, logFile); 46 | 47 | DATA_POD(DebugInfo, debugInfo); 48 | 49 | DATA_POD(Werror, werror); 50 | 51 | DATA_POD(NoWarn, noWarn); 52 | }; 53 | 54 | 55 | #endif //CLANG_MC_CONFIG_H 56 | -------------------------------------------------------------------------------- /src/cpp/Start.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/18. 3 | // 4 | 5 | #include "Main.h" 6 | 7 | #ifdef _WIN32 8 | 9 | #include 10 | 11 | #define bool BOOLEAN 12 | #define true TRUE 13 | #define false FALSE 14 | 15 | static __forceinline bool isVCRuntimeAvailable() { 16 | HMODULE runtime = LoadLibraryA("MSVCP140.dll"); 17 | if (runtime == NULL) 18 | return false; 19 | FreeLibrary(runtime); 20 | 21 | runtime = LoadLibraryA("VCRUNTIME140.dll"); 22 | if (runtime == NULL) 23 | return false; 24 | FreeLibrary(runtime); 25 | 26 | return true; 27 | } 28 | 29 | #endif 30 | 31 | int main(const int argc, const char *argv[]) { 32 | #ifdef _WIN32 33 | const volatile bool envReady = isVCRuntimeAvailable(); 34 | 35 | if (envReady) { 36 | return init(argc, argv); 37 | } else { 38 | MessageBoxA(NULL, 39 | "Microsoft Visual C++ Runtime is missing!\n\n" 40 | "Please install the latest VC++ runtime from:\n" 41 | "https://aka.ms/vs/17/release/vc_redist.x64.exe\n\n" 42 | "After installation, restart the program.", 43 | "Error: Missing Runtime", 44 | MB_ICONERROR | MB_OK); 45 | return 1; 46 | } 47 | #else 48 | return init(argc, argv); 49 | #endif 50 | } 51 | -------------------------------------------------------------------------------- /src/cpp/ir/verify/Verifier.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/19. 3 | // 4 | 5 | #ifndef CLANG_MC_VERIFIER_H 6 | #define CLANG_MC_VERIFIER_H 7 | 8 | #include "ir/IRCommon.h" 9 | #include "ir/IR.h" 10 | #include "VerifyResult.h" 11 | #include "ir/ops/Ret.h" 12 | #include "ir/ops/Jmp.h" 13 | 14 | 15 | class Verifier { 16 | private: 17 | const Logger &logger; 18 | const Config &config; 19 | std::vector &irs; 20 | 21 | const IR *currentIR = nullptr; 22 | const Op *currentOp = nullptr; 23 | u32 errors = 0; 24 | 25 | VerifyResult handleSingle(IR &ir); 26 | 27 | void error(const std::string &message, const IR *ir, const Op *op); 28 | 29 | __forceinline void error(const std::string &message) { 30 | return error(message, currentIR, currentOp); 31 | } 32 | 33 | bool warn(const std::string &message, const IR *ir, const Op *op); 34 | 35 | __forceinline bool warn(const std::string &message) { 36 | return warn(message, currentIR, currentOp); 37 | } 38 | 39 | void note(const std::string &message, const IR *ir, const Op *op); 40 | 41 | public: 42 | explicit Verifier(const Logger &logger, const Config &config, std::vector &irs) : 43 | logger(logger), config(config), irs(irs) { 44 | } 45 | 46 | void verify(); 47 | }; 48 | 49 | 50 | #endif //CLANG_MC_VERIFIER_H 51 | -------------------------------------------------------------------------------- /src/cpp/builder/PostOptimizer.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/3/14. 3 | // 4 | 5 | #include "PostOptimizer.h" 6 | 7 | static inline constexpr auto REPLACES = { 8 | "return run return", "return" 9 | }; 10 | 11 | void PostOptimizer::doSingleOptimize(std::string &code) { 12 | if (code.empty()) return; 13 | auto splits = string::split(code, '\n'); 14 | 15 | bool first = true; 16 | auto builder = StringBuilder(); 17 | for (const auto &line : splits) { 18 | if (line.empty() || line.front() == '#') continue; 19 | 20 | if (!first) builder.append('\n'); 21 | auto fixedLine = string::trim(line); 22 | std::string newLine; 23 | for (auto from = REPLACES.begin(); from < REPLACES.end(); from += 2) { 24 | const auto to = from + 1; 25 | if (string::replaceFast(fixedLine, newLine, *from, *to)) { 26 | fixedLine = newLine; 27 | } 28 | } 29 | builder.append(fixedLine); 30 | first = false; 31 | } 32 | 33 | code = builder.toString(); 34 | } 35 | 36 | void PostOptimizer::optimize() { 37 | for (size_t i = 0; i < mcFunctions.size(); i++) { // NOLINT(modernize-loop-convert) 38 | auto &mcFunction = mcFunctions[i].values(); 39 | 40 | for (size_t j = 0; j < mcFunction.size(); j++) { // NOLINT(modernize-loop-convert) 41 | doSingleOptimize(mcFunction[j].second); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/cpp/ir/OpCommon.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/17. 3 | // 4 | 5 | #ifndef CLANG_MC_OPCOMMON_H 6 | #define CLANG_MC_OPCOMMON_H 7 | 8 | #include 9 | #include "utils/Common.h" 10 | #include "ir/iops/Op.h" 11 | #include "ir/values/Value.h" 12 | #include "ir/values/Immediate.h" 13 | #include "ir/values/Register.h" 14 | #include "ir/values/Ptr.h" 15 | #include "i18n/I18n.h" 16 | #include "State.h" 17 | 18 | using ValuePtr = std::shared_ptr; 19 | using OpPtr = std::unique_ptr; 20 | using LabelMap = HashMap; 21 | using JmpMap = HashMap>; 22 | 23 | static inline bool isLocalSymbol(const std::string_view &rawLabel) { 24 | if (string::contains(rawLabel, ' ')) { 25 | throw ParseException(i18n("ir.invalid_symbol")); 26 | } 27 | return rawLabel[0] == '.'; 28 | } 29 | 30 | static std::string fixSymbol(const LineState &line, const std::string_view &rawSymbol) { 31 | if (isLocalSymbol(rawSymbol)) { 32 | if (line.lastLabel == nullptr) { 33 | throw ParseException(i18n("ir.invalid_local_symbol")); 34 | } 35 | return fmt::format("{}{}", line.lastLabel->getLabel(), rawSymbol); // rawSymbol已经包含一个.了 36 | } 37 | return std::string(rawSymbol); 38 | } 39 | 40 | PURE i32 parseToNumber(std::string_view string); 41 | 42 | PURE ValuePtr createValue(const LineState &line, const std::string &string); 43 | 44 | #endif //CLANG_MC_OPCOMMON_H 45 | -------------------------------------------------------------------------------- /src/resources/assets/stdlib/data/std/function/heap/shrink.mcfunction: -------------------------------------------------------------------------------- 1 | # 函数原型:void shrink(int size) 2 | # 缩小堆空间size个元素的大小,每个元素通常4字节 3 | 4 | execute if score r0 vm_regs matches 0 run return 0 5 | 6 | data modify storage std:vm s2 set value {shp: -1} 7 | 8 | # 展开4次,尽量避免stack overflow 9 | scoreboard players remove shp vm_regs 1 10 | scoreboard players remove r0 vm_regs 1 11 | execute store result storage std:vm s2.shp int 1 run scoreboard players get shp vm_regs 12 | function std:_internal/shrink_heap with storage std:vm s2 13 | 14 | execute if score r0 vm_regs matches 0 run return 0 15 | scoreboard players add shp vm_regs 1 16 | scoreboard players remove r0 vm_regs 1 17 | execute store result storage std:vm s2.shp int 1 run scoreboard players get shp vm_regs 18 | function std:_internal/shrink_heap with storage std:vm s2 19 | 20 | execute if score r0 vm_regs matches 0 run return 0 21 | scoreboard players add shp vm_regs 1 22 | scoreboard players remove r0 vm_regs 1 23 | execute store result storage std:vm s2.shp int 1 run scoreboard players get shp vm_regs 24 | function std:_internal/shrink_heap with storage std:vm s2 25 | 26 | execute if score r0 vm_regs matches 0 run return 0 27 | scoreboard players add shp vm_regs 1 28 | scoreboard players remove r0 vm_regs 1 29 | execute store result storage std:vm s2.shp int 1 run scoreboard players get shp vm_regs 30 | function std:_internal/shrink_heap with storage std:vm s2 31 | 32 | execute if score r0 vm_regs matches 1.. run function std:heap/shrink 33 | -------------------------------------------------------------------------------- /scripts/asciiGenerator.py: -------------------------------------------------------------------------------- 1 | import string 2 | 3 | TEMPLATE_S2C = """ 4 | # 字符串常量映射 不要修改此映射,该映射由脚本生成 5 | data modify storage std:vm str2char_map set value %S2C_MAP%""" 6 | 7 | TEMPLATE_C2S = f""" 8 | %IF% 9 | jmp .c_{ord('?')} 10 | %VAL%""" 11 | 12 | TEMPLATE_C2S_IF = f"""je r1, %ORD%, .c_%ORD%""" 13 | TEMPLATE_C2S_VAL = f""".c_%ORD%: 14 | inline data modify storage std:vm s3.next set value "%CHAR%" 15 | ret""" 16 | 17 | s2cMap = {} 18 | c2sMap = {} 19 | for char in sorted(string.printable, key=ord): 20 | escaped_char = repr(char)[1:-1] # 去掉 repr() 生成的引号 21 | # if escaped_char[0] == "\\": # mcfunction语法转义 22 | # escaped_char = "\\" + escaped_char 23 | s2cMap[escaped_char] = ord(char) 24 | c2sMap[str(ord(char))] = escaped_char 25 | print(TEMPLATE_S2C 26 | .replace("%S2C_MAP%", repr(s2cMap)) 27 | .replace("'\"'", '"\\""') 28 | .replace(", '", ', "') 29 | .replace("': ", '": ') 30 | .replace("', ", '", ') 31 | .replace(": '", ': "') 32 | .replace("'}", '"}') 33 | .replace("{'", '{"') 34 | ) 35 | print(TEMPLATE_C2S 36 | .replace("%IF%", "\n".join(map(lambda c: TEMPLATE_C2S_IF 37 | .replace("%ORD%", c) 38 | , c2sMap.keys()))) 39 | .replace("%VAL%", "\n".join(map(lambda data: TEMPLATE_C2S_VAL 40 | .replace("%ORD%", data[0]) 41 | .replace("%CHAR%", data[1].replace("\"", '\\"')) 42 | , c2sMap.items()))) 43 | ) 44 | -------------------------------------------------------------------------------- /src/cpp/ir/verify/VerifyResult.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/23. 3 | // 4 | 5 | #ifndef CLANG_MC_VERIFYRESULT_H 6 | #define CLANG_MC_VERIFYRESULT_H 7 | 8 | #include "utils/Common.h" 9 | #include "ir/iops/CallLike.h" 10 | 11 | struct SymbolOp { 12 | std::string_view name; 13 | Op *op; 14 | }; 15 | 16 | class VerifyResult { 17 | private: 18 | const HashMap definedSymbols; 19 | const HashSet unusedSymbols; 20 | const HashMap definedLabels; 21 | const HashMap> undefinedLabels; 22 | const HashSet unusedLabels; 23 | public: 24 | explicit VerifyResult(HashMap &&definedSymbols, 25 | HashSet &&unusedSymbols, 26 | HashMap &&definedLabels, 27 | HashMap> &&undefinedLabels, 28 | HashSet &&unusedLabels) noexcept 29 | : definedSymbols(std::move(definedSymbols)), 30 | unusedSymbols(std::move(unusedSymbols)), 31 | definedLabels(std::move(definedLabels)), 32 | undefinedLabels(std::move(undefinedLabels)), 33 | unusedLabels(std::move(unusedLabels)) { 34 | } 35 | 36 | GETTER(DefinedSymbols, definedSymbols); 37 | 38 | GETTER(UnusedSymbols, unusedSymbols); 39 | 40 | GETTER(DefinedLabels, definedLabels); 41 | 42 | GETTER(UndefinedLabels, undefinedLabels); 43 | 44 | GETTER(UnusedLabels, unusedLabels); 45 | }; 46 | 47 | #endif //CLANG_MC_VERIFYRESULT_H 48 | -------------------------------------------------------------------------------- /src/cpp/extern/PreProcessorAPI.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/3/22. 3 | // 4 | 5 | #ifndef CLANG_MC_PREPROCESSORAPI_H 6 | #define CLANG_MC_PREPROCESSORAPI_H 7 | 8 | #include "../utils/Common.h" 9 | 10 | typedef void *PreProcessorC; 11 | typedef struct { 12 | char *path; 13 | char *code; 14 | } Target; 15 | typedef struct { 16 | Target **targets; 17 | u32 size; 18 | } Targets; 19 | typedef struct { 20 | char *key; 21 | char *value; 22 | } Define; 23 | typedef struct { 24 | char *path; 25 | Define **values; 26 | u32 size; 27 | } Defines; 28 | typedef struct { 29 | Defines **defines; 30 | u32 size; 31 | } DefineMap; 32 | 33 | #ifdef __cplusplus 34 | extern "C" { 35 | #endif 36 | 37 | PreProcessorC ClPreProcess_New(void); 38 | 39 | void ClPreProcess_Free(PreProcessorC instance); 40 | 41 | PreProcessorC ClPreProcess_Clone(PreProcessorC instance); 42 | 43 | i32 ClPreProcess_AddIncludeDir(PreProcessorC instance, const char *path); 44 | 45 | i32 ClPreProcess_AddTarget(PreProcessorC instance, const char *path); 46 | 47 | i32 ClPreProcess_AddTargetString(PreProcessorC instance, const char *code); 48 | 49 | i32 ClPreProcess_Load(PreProcessorC instance); 50 | 51 | i32 ClPreProcess_Process(PreProcessorC instance); 52 | 53 | i32 ClPreProcess_GetTargets(PreProcessorC instance, Targets **result); 54 | 55 | i32 ClPreProcess_GetDefines(PreProcessorC instance, DefineMap **result); 56 | 57 | void ClPreProcess_FreeTargets(PreProcessorC instance, Targets *targets); 58 | 59 | void ClPreProcess_FreeDefines(PreProcessorC instance, DefineMap *defineMap); 60 | 61 | #ifdef __cplusplus 62 | } 63 | #endif 64 | 65 | #endif //CLANG_MC_PREPROCESSORAPI_H 66 | -------------------------------------------------------------------------------- /scripts/stackGenerator.py: -------------------------------------------------------------------------------- 1 | PUSH_TEMPLATE = """# 不要修改此文件,该文件由脚本生成 2 | # 函数原型:void push_%REG%() 3 | # 把%REG%的值推入栈顶 4 | 5 | scoreboard players remove rsp vm_regs 1 6 | 7 | data modify storage std:vm s2 set value {name: "%REG%", rsp: -1} 8 | execute store result storage std:vm s2.rsp int 1 run scoreboard players get rsp vm_regs 9 | 10 | function std:_internal/push with storage std:vm s2 11 | """ 12 | 13 | PEEK_TEMPLATE = """# 不要修改此文件,该文件由脚本生成 14 | # 函数原型:void peek_%REG%() 15 | # 获取但不移除栈顶的值,存入%REG% 16 | 17 | data modify storage std:vm s2 set value {name: "%REG%", rsp: -1} 18 | execute store result storage std:vm s2.rsp int 1 run scoreboard players get rsp vm_regs 19 | 20 | function std:_internal/peek with storage std:vm s2 21 | """ 22 | 23 | POP_TEMPLATE = """# 不要修改此文件,该文件由脚本生成 24 | # 函数原型:void pop_%REG%() 25 | # 把栈顶的值弹出到%REG% 26 | 27 | scoreboard players add rsp vm_regs 1 28 | 29 | data modify storage std:vm s2 set value {name: "%REG%", rsp: -1} 30 | execute store result storage std:vm s2.rsp int 1 run scoreboard players get rsp vm_regs 31 | 32 | function std:_internal/pop with storage std:vm s2 33 | """ 34 | 35 | REGS = ["rax", "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"] 36 | 37 | for reg in REGS: 38 | with open(f"push_{reg}.mcfunction", "w", encoding="UTF-8") as f: 39 | f.write(PUSH_TEMPLATE.replace("%REG%", reg)) 40 | with open(f"peek_{reg}.mcfunction", "w", encoding="UTF-8") as f: 41 | f.write(PEEK_TEMPLATE.replace("%REG%", reg)) 42 | with open(f"pop_{reg}.mcfunction", "w", encoding="UTF-8") as f: 43 | f.write(POP_TEMPLATE.replace("%REG%", reg)) 44 | -------------------------------------------------------------------------------- /src/cpp/ir/ops/Label.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/17. 3 | // 4 | 5 | #ifndef CLANG_MC_LABEL_H 6 | #define CLANG_MC_LABEL_H 7 | 8 | #include "ir/iops/Op.h" 9 | #include "ir/values/Value.h" 10 | #include "utils/string/StringUtils.h" 11 | 12 | class Label : public Op { 13 | private: 14 | std::string label; 15 | Hash labelHash; 16 | const bool export_; 17 | const bool extern_; 18 | const bool local; 19 | public: 20 | explicit Label(const i32 lineNumber, std::string label, const bool export_, const bool extern_, const bool local) noexcept: 21 | Op("label", lineNumber), label(std::move(label)), labelHash(hash(this->label)), 22 | export_(export_), extern_(extern_), local(local) { 23 | } 24 | 25 | GETTER(Label, label); 26 | 27 | void setLabel(std::string newLabel) { 28 | label = std::move(newLabel); 29 | labelHash = hash(this->label); 30 | } 31 | 32 | GETTER_POD(LabelHash, labelHash); 33 | 34 | GETTER_POD(Export, export_); 35 | 36 | GETTER_POD(Extern, extern_); 37 | 38 | GETTER_POD(Local, local); 39 | 40 | [[nodiscard]] std::string toString() const noexcept override { 41 | if (export_) { 42 | return fmt::format("export {}:", label); 43 | } 44 | if (extern_) { 45 | return fmt::format("extern {}:", label); 46 | } 47 | return fmt::format("{}:", label); 48 | } 49 | 50 | [[nodiscard]] std::string compile() const override { 51 | throw UnsupportedOperationException("Label op can't be compile normally."); 52 | } 53 | }; 54 | 55 | #define LABEL_RET "__internal_ret" 56 | inline Label labelRet = Label(-1, LABEL_RET, false, false, false); 57 | 58 | #endif //CLANG_MC_LABEL_H 59 | -------------------------------------------------------------------------------- /src/cpp/objects/NameGenerator.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/19. 3 | // 4 | 5 | #ifndef CLANG_MC_NAMEGENERATOR_H 6 | #define CLANG_MC_NAMEGENERATOR_H 7 | 8 | #include 9 | #include "utils/Native.h" 10 | 11 | class NameGenerator { 12 | private: 13 | static inline constexpr char DICTIONARY[] = "abcdefghijklmnopqrstuvwxyz"; 14 | static inline constexpr size_t DICT_SIZE = sizeof(DICTIONARY) - 1; 15 | static inline constexpr size_t START_LENGTH = 1; 16 | 17 | size_t *counters = nullptr; 18 | size_t length = START_LENGTH; 19 | 20 | public: 21 | explicit NameGenerator() noexcept { 22 | counters = static_cast(calloc(START_LENGTH, sizeof(size_t))); 23 | if (counters == nullptr) { 24 | onOOM(); 25 | } 26 | } 27 | 28 | ~NameGenerator() { 29 | free(counters); 30 | } 31 | 32 | static __forceinline NameGenerator &getInstance() noexcept { 33 | static auto generator = NameGenerator(); 34 | return generator; 35 | } 36 | 37 | __forceinline std::string generate() { 38 | auto result = std::string(length, '\0'); 39 | for (size_t i = 0; i < length; i++) { 40 | result[i] = DICTIONARY[counters[i]]; 41 | } 42 | incrementCounters(); 43 | return result; 44 | } 45 | 46 | private: 47 | __forceinline void incrementCounters() noexcept { 48 | size_t i = length - 1; 49 | do { 50 | counters[i]++; 51 | if (LIKELY(counters[i] < DICT_SIZE)) { 52 | return; 53 | } 54 | counters[i] = 0; 55 | } while (i-- > 0); 56 | 57 | length++; 58 | 59 | // 更新counters 60 | free(counters); 61 | counters = static_cast(calloc(length, sizeof(size_t))); 62 | if (counters == nullptr) { 63 | // 不需要显式析构 64 | onOOM(); 65 | } 66 | } 67 | }; 68 | 69 | #endif //CLANG_MC_NAMEGENERATOR_H 70 | -------------------------------------------------------------------------------- /src/python/build_assets.py: -------------------------------------------------------------------------------- 1 | import shutil 2 | import subprocess 3 | from pathlib import Path 4 | 5 | from internal import * 6 | 7 | 8 | def initialize(): 9 | assert Const.BUILD_DIR.exists() 10 | assert Const.BUILD_BIN_DIR.exists() 11 | FileUtils.ensureDirectoryNotExist(Path(Const.BUILD_DIR, "assets")) 12 | FileUtils.ensureDirectoryNotExist(Path(Const.BUILD_DIR, "include")) 13 | FileUtils.ensureDirectoryEmpty(Const.BUILD_TMP_DIR) 14 | 15 | 16 | def main(): 17 | initialize() 18 | 19 | # 拷贝基本stdlib 20 | print("Copying runtime assets to bin...") 21 | shutil.copytree(Path(Const.RESOURCES_DIR, "assets"), Path(Const.BUILD_DIR, "assets")) 22 | shutil.copytree(Path(Const.RESOURCES_DIR, "include"), Path(Const.BUILD_DIR, "include")) 23 | 24 | # 编译stdlib 25 | print("Compiling stdlib...") 26 | sources: set[str] = {str((Const.MCASM_DIR / "stdlib.mcasm").absolute())} 27 | for item in Const.MCASM_DIR.iterdir(): 28 | if item.suffix != ".mcasm": 29 | continue 30 | sources.add(str(item.absolute())) 31 | command = [str(Const.EXECUTABLE), "--compile-only", "--namespace", "std:_internal", "--build-dir", str(Const.BUILD_TMP_DIR)] 32 | command.extend(sources) 33 | prettyCmd = ' '.join(map(lambda s: f'"{s}"' if " " in s else s, map(str, command))) 34 | print(f"Compile command: {prettyCmd}") 35 | process = subprocess.Popen( 36 | command, 37 | stdout=subprocess.PIPE, 38 | stderr=subprocess.PIPE, 39 | encoding="UTF-8", 40 | bufsize=1, 41 | universal_newlines=True 42 | ) 43 | for out in process.communicate(): 44 | if out is None or len(out) == 0: 45 | continue 46 | print(out.strip()) 47 | 48 | # 拷贝stdlib 49 | print("Copying stdlib to bin...") 50 | shutil.copytree(Const.BUILD_TMP_DIR, Path(Const.BUILD_DIR, "assets/stdlib"), dirs_exist_ok=True) 51 | 52 | print("Cleaning...") 53 | shutil.rmtree(Path(Const.BUILD_DIR, "tmp")) 54 | 55 | 56 | if __name__ == '__main__': 57 | main() 58 | -------------------------------------------------------------------------------- /src/cpp/Main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/13. 3 | // 4 | 5 | #include "Main.h" 6 | #include 7 | #include 8 | #include "config/Config.h" 9 | #include "config/ArgParser.h" 10 | #include "ClangMc.h" 11 | #include "utils/Native.h" 12 | #include "utils/CLIUtils.h" 13 | #include "i18n/I18n.h" 14 | 15 | static inline Config parseArgs(const int argc, const char *argv[]) { 16 | auto config = Config(); 17 | if (argc <= 1) { 18 | return config; 19 | } 20 | 21 | auto parser = ArgParser(config); 22 | 23 | for (int i = 1; i < argc; ++i) { 24 | parser.next(argv[i]); 25 | } 26 | parser.end(); 27 | 28 | return config; 29 | } 30 | 31 | extern "C" [[gnu::noinline]] int init(const int argc, const char *argv[]) { 32 | ARGC = argc; 33 | ARGV = argv; 34 | 35 | std::set_new_handler(onOOM); 36 | try { 37 | std::set_terminate(onTerminate); 38 | initNative(); 39 | initI18n(); 40 | 41 | assert(argc >= 1); 42 | if (argc == 2) { 43 | switch (hash(argv[1])) { 44 | case hash("--help"): 45 | std::cout << getHelpMessage(getArgv0()) << std::flush; 46 | return 0; 47 | case hash("--version"): 48 | std::cout << getVersionMessage(getArgv0()) << std::flush; 49 | return 0; 50 | } 51 | } 52 | 53 | Config config; 54 | try { 55 | config = parseArgs(argc, argv); 56 | } catch (const ParseException &e) { 57 | std::cerr << "error: " << e.what() << std::endl; 58 | return 1; 59 | } 60 | 61 | auto instance = ClangMc(config); 62 | instance.start(); 63 | return 0; 64 | } catch (const std::bad_alloc &e) { 65 | onOOM(); 66 | // 让异常termiate,以看到stacktrace 67 | // } catch (const std::exception &e) { 68 | // printStacktrace(e); 69 | // } catch (...) { 70 | // printStacktrace(); 71 | } 72 | return 1; 73 | } 74 | -------------------------------------------------------------------------------- /src/cpp/i18n/I18n.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/18. 3 | // 4 | 5 | #include "I18n.h" 6 | #include 7 | #include "utils/string/StringUtils.h" 8 | 9 | #ifdef GENERATED_SETUP 10 | #include "GeneratedI18n.h" 11 | #else 12 | #include "I18nTemplate.h" 13 | #endif 14 | 15 | inline auto TRANSLATIONS = HashMap(); 16 | 17 | void loadLanguage(const char *const data) { 18 | YAML::Node config = YAML::Load(data); 19 | for (const auto &it: config) { 20 | TRANSLATIONS[hash(it.first.as())] = it.second.as(); 21 | } 22 | } 23 | 24 | #if defined(_WIN32) 25 | #include 26 | #elif defined(__APPLE__) || defined(__linux__) 27 | #include 28 | #include 29 | #endif 30 | 31 | std::string getSystemLanguage() { 32 | std::string lang = "en-US"; 33 | 34 | #if defined(_WIN32) || defined(_WIN64) 35 | wchar_t localeName[LOCALE_NAME_MAX_LENGTH] = {0}; 36 | if (GetUserDefaultLocaleName(localeName, LOCALE_NAME_MAX_LENGTH)) { 37 | std::wstring wstr(localeName); 38 | lang = std::string(wstr.begin(), wstr.end()); 39 | } 40 | #elif defined(__APPLE__) || defined(__linux__) 41 | const char* locale = setlocale(LC_ALL, ""); 42 | if (locale) { 43 | lang = std::string(locale); 44 | } 45 | #endif 46 | 47 | return string::replaceFast(lang, '_', '-'); 48 | } 49 | 50 | void initI18n() { 51 | std::setlocale(LC_ALL, ".UTF-8"); 52 | loadLanguage(EN_US); // fallback 53 | 54 | SWITCH_STR (getSystemLanguage()) { 55 | CASE_STR("zh-CN"): 56 | loadLanguage(ZH_CN); 57 | break; 58 | default: 59 | break; 60 | } 61 | } 62 | 63 | std::string &i18n(const std::string_view &key, const Hash keyHash) { 64 | assert(TRANSLATIONS.contains(keyHash)); 65 | 66 | try { 67 | return TRANSLATIONS.at(keyHash); 68 | } catch (const std::out_of_range &) { 69 | return TRANSLATIONS[keyHash] = key; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /README_CN.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | [English](./README.md) | 简体中文 4 | 5 | logo 6 | 7 | # clang-mc 8 | 9 | ## 一个面向 Minecraft 数据包的开发工具链 10 | 11 | [![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE) 12 | [![Issues](https://img.shields.io/github/issues/xia-mc/clang-mc)](https://github.com/xia-mc/clang-mc/issues) 13 | ![Total lines](https://tokei.rs/b1/github/xia-mc/clang-mc?style=flat) 14 | ![Version](https://img.shields.io/badge/Minecraft-1.21_and_later-blue) 15 | 16 |
17 | 18 | > [!NOTE] 19 | > clang-mc 正处于早期开发阶段,大部分功能还尚未完成,欢迎贡献和反馈! 20 | 21 | ## 简介 22 | 23 | `clang-mc` 是一个专为 **Minecraft 数据包开发** 设计的编译工具链,旨在提供更高效、更易维护的开发环境,并提供标准库来减少重复实现常见功能。 24 | 25 | Minecraft 数据包的开发一直面临 **可读性差、维护困难、功能受限** 等问题。例如: 26 | - **缺乏现代编程语言的功能**:字符串处理、浮点数计算、大整数运算等非常困难。 27 | - **代码组织混乱**:数据包通常依赖大量手写命令,相当多需求必须使用多个.mcfunction间接实现,难以复用和扩展。 28 | 29 | 本项目希望能在一定程度上解决这些问题。 30 | 31 | 查看 [Wiki](https://github.com/xia-mc/clang-mc/wiki) 以了解更多。 32 | 33 | ## 开发路线图 34 | 35 | - [x] 基本实现stdlib标准库,在mcfunction上搭出一个低开销虚拟机模型。 36 | - [x] 写一个wiki文档,规定调用约定、寄存器约定、栈约定等。 37 | - [x] 实现一个汇编器(IR),把IR翻译成基于stdlib的mcfunction代码。 38 | - [x] 实现一个parser,把汇编代码翻译成IR对象。 39 | - [x] 做一个showcase,展示我的项目的优势,如何避免数据包令人难绷的可读性和造轮子的问题。 40 | - [ ] 进一步完善stdlib,提供更多高级抽象。 41 | - [ ] (长期目标) 实现一个LLVM后端,生成汇编代码(IR)。 42 | - [ ] (长期目标) 兼容C/C++/Rust生态,利用LLVM扩展mcfunction的能力。 43 | 44 | ## 贡献 45 | 46 | 欢迎贡献!请随时提交问题或拉取请求。请注意,项目处于早期阶段,我们非常感谢任何反馈或建议。 47 | 48 | ## 感谢 49 | 50 | 没有这些项目,就没有 `clang-mc`: 51 | 52 | - [Minecraft](https://www.minecraft.net): Mojang Studios 开发的 Minecraft 游戏`clang-mc` 遵循 [Minecraft EULA](https://www.minecraft.net/en-us/eula) 及相关使用条款。 53 | - [LLVM](https://llvm.org): 先进的编译器基础设施,遵循 Apache License 2.0 开源协议。 54 | - [ankerl::unordered_dense](https://github.com/martinus/unordered_dense): 一个现代 C++ 的高性能、低内存占用的哈希表实现,遵循 [MIT License](https://github.com/martinus/unordered_dense/blob/main/LICENSE)。 55 | - [fmt](https://fmt.dev/): 一个快速、安全的 C++ 格式化库,遵循 [MIT License](https://github.com/fmtlib/fmt/blob/master/LICENSE.rst)。 56 | - [spdlog](https://github.com/gabime/spdlog): 一个高性能的 C++ 日志库,遵循 [MIT License](https://github.com/gabime/spdlog/blob/v1.x/LICENSE)。 57 | - [yaml-cpp](https://github.com/jbeder/yaml-cpp): 一个 C++ 的 YAML 解析和生成库,遵循 [MIT License](https://github.com/jbeder/yaml-cpp/blob/master/LICENSE)。 58 | 59 | 感谢所有开源社区的贡献者们! 60 | -------------------------------------------------------------------------------- /src/cpp/ir/ops/Mov.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/16. 3 | // 4 | 5 | #ifndef CLANG_MC_MOV_H 6 | #define CLANG_MC_MOV_H 7 | 8 | #include "ir/iops/Op.h" 9 | #include "ir/values/Value.h" 10 | #include "utils/string/StringUtils.h" 11 | #include "i18n/I18n.h" 12 | #include "ir/iops/CmpLike.h" 13 | 14 | class Mov : public CmpLike { 15 | public: 16 | explicit Mov(const i32 lineNumber, ValuePtr &&left, ValuePtr &&right) 17 | : Op("mov", lineNumber), CmpLike(std::move(left), std::move(right)) { 18 | if (INSTANCEOF_SHARED(this->left, Immediate)) { 19 | throw ParseException(i18n("ir.op.immediate_left")); 20 | } 21 | } 22 | 23 | [[nodiscard]] std::string toString() const noexcept override { 24 | return fmt::format("mov {}, {}", left->toString(), right->toString()); 25 | } 26 | 27 | [[nodiscard]] std::string compile() const override { 28 | if (const auto &immediate = INSTANCEOF_SHARED(right, Immediate)) { 29 | if (const auto &result = INSTANCEOF_SHARED(left, Register)) { 30 | return fmt::format("scoreboard players set {} vm_regs {}", 31 | result->getName(), static_cast(immediate->getValue())); 32 | } else { 33 | assert(INSTANCEOF_SHARED(left, Ptr)); 34 | 35 | return CAST_FAST(left, Ptr)->storeFrom(*immediate); 36 | } 37 | } else if (const auto ® = INSTANCEOF_SHARED(right, Register)) { 38 | if (const auto &result = INSTANCEOF_SHARED(left, Register)) { 39 | return fmt::format("scoreboard players operation {} vm_regs = {} vm_regs", 40 | result->getName(), reg->getName()); 41 | } else { 42 | assert(INSTANCEOF_SHARED(left, Ptr)); 43 | 44 | return CAST_FAST(left, Ptr)->storeFrom(*reg); 45 | } 46 | } else { 47 | assert(INSTANCEOF_SHARED(right, Ptr)); 48 | 49 | if (const auto &result = INSTANCEOF_SHARED(left, Register)) { 50 | return CAST_FAST(right, Ptr)->loadTo(*result); 51 | } else { 52 | assert(INSTANCEOF_SHARED(left, Ptr)); 53 | 54 | return CAST_FAST(left, Ptr)->storeFrom(*CAST_FAST(right, Ptr)); 55 | } 56 | } 57 | } 58 | }; 59 | 60 | #endif //CLANG_MC_MOV_H 61 | -------------------------------------------------------------------------------- /src/resources/include/int.mch: -------------------------------------------------------------------------------- 1 | #once 2 | 3 | export std:_internal/declen: 4 | inline execute if score r0 vm_regs matches -9.. run return 1 5 | inline execute if score r0 vm_regs matches -99.. run return 2 6 | inline execute if score r0 vm_regs matches -999.. run return 3 7 | inline execute if score r0 vm_regs matches -9999.. run return 4 8 | inline execute if score r0 vm_regs matches -99999.. run return 5 9 | inline execute if score r0 vm_regs matches -999999.. run return 6 10 | inline execute if score r0 vm_regs matches -9999999.. run return 7 11 | inline execute if score r0 vm_regs matches -99999999.. run return 8 12 | inline return 9 13 | 14 | 15 | // int Int_Declen(int value) 16 | Int_Declen: 17 | mov t0, r0 18 | jl r0, 0, .begin 19 | mul r0, -1 20 | .begin: 21 | inline execute store result score rax vm_regs run function std:_internal/declen 22 | rge t0, 0 23 | add rax, 1 24 | ret 25 | 26 | 27 | // void Int_ToString(char *result, int value) 28 | Int_ToString: 29 | // 特殊处理0 30 | je r1, 0, .special_0 31 | // 特殊处理-2147483648 32 | je r1, -2147483648, .special_INT_MIN 33 | 34 | push x0 35 | push x1 36 | 37 | // char *x0; // result 38 | // int x1; // value 39 | mov x0, r0 40 | mov x1, r1 41 | mov r0, x1 42 | call Int_Declen 43 | 44 | // 特殊处理负数 45 | jg x1, 0, .before_loop 46 | mov [x0], '-' 47 | mul x1, -1 48 | 49 | .before_loop: 50 | // char *x0; // result (指向十进制字符串最后一位) 51 | add x0, rax 52 | mov [x0], 0 // C字符串结束符 53 | 54 | // int r0; // tmp 55 | // int r1; // tmp2 56 | .loop: 57 | sub x0, 1 58 | 59 | // r0 = value; 60 | // r1 = r0 % 10; 61 | // value /= 10; 62 | mov r0, x1 63 | div x1, 10 64 | mov r1, x1 65 | mul r1, 10 66 | // 此时r1为最后一位数为0的r0 67 | 68 | sub r0, r1 69 | add r0, 48 // 转换成ascii码值 70 | 71 | mov [x0], r0 72 | 73 | jg x1, 0, .loop 74 | 75 | pop x1 76 | pop x0 77 | ret 78 | 79 | .special_0: 80 | mov [r0], '0' 81 | mov [r0 + 1], 0 // C字符串结束符 82 | ret 83 | 84 | .special_INT_MIN: 85 | mov [r0], '-' 86 | mov [r0 + 1], '2' 87 | mov [r0 + 2], '1' 88 | mov [r0 + 3], '4' 89 | mov [r0 + 4], '7' 90 | mov [r0 + 5], '4' 91 | mov [r0 + 6], '8' 92 | mov [r0 + 7], '3' 93 | mov [r0 + 8], '6' 94 | mov [r0 + 9], '4' 95 | mov [r0 + 10], '8' 96 | mov [r0 + 11], 0 97 | ret 98 | -------------------------------------------------------------------------------- /src/cpp/ir/controlFlow/JmpTable.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/3/13. 3 | // 4 | 5 | #include "JmpTable.h" 6 | #include "objects/NameGenerator.h" 7 | #include "utils/string/StringBuilder.h" 8 | 9 | static inline constexpr std::string_view JMP_LAST_TEMPLATE = "return run function {}"; 10 | static inline constexpr std::string_view JMP_TEMPLATE = "execute if function {} run return 1"; 11 | 12 | void JmpTable::make() { 13 | assert(!labelMap.empty()); 14 | assert(!values.empty()); 15 | 16 | const auto labelCount = labelMap.size(); 17 | 18 | data = std::vector(labelCount); 19 | Hash * __restrict labels = static_cast(malloc(labelCount * sizeof(Hash))); 20 | if (labels == nullptr) onOOM(); 21 | dataView = static_cast(malloc(labelCount * sizeof(std::string_view))); 22 | if (dataView == nullptr) onOOM(); 23 | bool * __restrict terminateData = static_cast(calloc(labelCount, sizeof(bool))); 24 | if (terminateData == nullptr) onOOM(); 25 | 26 | try { 27 | { 28 | auto iter = values.end(); 29 | Label *op; 30 | size_t i = labelCount - 1; 31 | do { 32 | iter--; 33 | if (isTerminate(*iter)) { 34 | terminateData[i] = true; // 这个label不可能跳转到下一个label 35 | } 36 | 37 | op = INSTANCEOF(*iter, Label); 38 | if (op == nullptr) continue; 39 | 40 | const auto &cmdTemplate = i == labelCount - 1 || terminateData[i] ? JMP_LAST_TEMPLATE : JMP_TEMPLATE; 41 | const auto command = fmt::format(fmt::runtime(cmdTemplate), labelMap.at(op->getLabelHash())); 42 | labels[i] = op->getLabelHash(); 43 | dataView[i] = data[i] = command; 44 | i--; 45 | } while (iter != values.begin()); 46 | } // 释放builder 47 | 48 | // dp 49 | size_t size = 1; 50 | size_t i = labelCount; 51 | do { 52 | i--; 53 | 54 | if (terminateData[i]) { 55 | size = 1; 56 | } 57 | 58 | const auto label = labels[i]; 59 | auto *str = &dataView[i]; 60 | jmpMap[label] = std::span(str, size); 61 | 62 | size++; 63 | } while (i > 0); 64 | } catch (...) { 65 | free(labels); 66 | free(terminateData); 67 | throw std::runtime_error("failed to create jmp table."); 68 | } 69 | free(labels); 70 | free(terminateData); 71 | } 72 | -------------------------------------------------------------------------------- /src/cpp/builder/Builder.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/3/12. 3 | // 4 | 5 | #include "Builder.h" 6 | #include "extern/ResourceManager.h" 7 | #include "PostOptimizer.h" 8 | 9 | static inline constexpr auto PACK_MCMETA = \ 10 | "{\n" 11 | " \"pack\": {\n" 12 | " \"description\": \"\",\n" 13 | " \"pack_format\": 61\n" 14 | " }\n" 15 | "}"; 16 | 17 | void Builder::build() { 18 | for (auto &mcFunction: mcFunctions) { 19 | for (const auto &entry: mcFunction) { 20 | const auto &path = config.getBuildDir() / entry.first; 21 | const auto &data = entry.second; 22 | 23 | ensureParentDir(path); 24 | writeFile(path, data); 25 | } 26 | McFunctions().swap(mcFunction); 27 | } 28 | } 29 | 30 | void Builder::link() const { 31 | writeFileIfNotExist(config.getBuildDir() / "pack.mcmeta", PACK_MCMETA); 32 | 33 | // static link stdlib 34 | auto entries = std::vector(); 35 | for (const auto& entry : std::filesystem::recursive_directory_iterator(STDLIB_PATH)) { 36 | entries.push_back(entry.path()); 37 | } 38 | //#pragma omp parallel for default(none) shared(entries, STDLIB_PATH) 39 | for (size_t i = 0; i < entries.size(); ++i) { // NOLINT(modernize-loop-convert) 40 | const Path& path = entries[i]; 41 | if (is_regular_file(path) && path.extension() != ".mcmeta") { 42 | auto target = config.getBuildDir() / relative(path, STDLIB_PATH); 43 | ensureParentDir(target); 44 | 45 | auto content = readFile(path); 46 | 47 | if (target.extension() == ".mcfunction") { 48 | PostOptimizer::doSingleOptimize(content); 49 | } 50 | 51 | writeFileIfNotExist(target, content); 52 | } 53 | } 54 | 55 | if (!config.getDataDir().empty()) { 56 | for (const auto& path : std::filesystem::recursive_directory_iterator(config.getDataDir())) { 57 | if (is_regular_file(path)) { 58 | auto target = config.getBuildDir() / relative(path, config.getDataDir()); 59 | ensureParentDir(target); 60 | 61 | auto content = readFile(path); 62 | 63 | if (target.extension() == ".mcfunction") { 64 | PostOptimizer::doSingleOptimize(content); 65 | } 66 | 67 | writeFile(target, content); 68 | } 69 | } 70 | } 71 | 72 | auto output = config.getOutput(); 73 | output += ".zip"; 74 | compressFolder(config.getBuildDir(), output); 75 | } 76 | -------------------------------------------------------------------------------- /src/resources/lang/ZH_CN.yaml: -------------------------------------------------------------------------------- 1 | cli.help_message_template: | 2 | 用法: {} [选项] 文件... 3 | 4 | 选项: 5 | --help \t显示此帮助信息 6 | --version \t打印版本信息 7 | --log-file (-l) \t将日志写入文件 8 | --output (-o) <名称> \t指定输出文件名 9 | --build-dir (-B) <名称> \t指定构建目录 10 | --namespace (-N) <名称> \t指定非导出函数的命名空间路径用于编译 11 | --compile-only (-c) \t仅编译,不链接为 .zip 12 | -g \t生成额外的调试信息 13 | -Werror \t将所有警告视为错误 14 | -E \t仅运行预处理器 15 | -w \t抑制所有警告 16 | cli.version_message_template: | 17 | {} 版本 {} 18 | 目标: {} 19 | 编译器: {} 20 | 安装目录: {} 21 | cli.debug_mode: 调试模式 22 | cli.arg.empty_input_file: 输入文件路径不能为空 23 | cli.arg.missing_arg: "选项 '{}' 缺少参数" 24 | cli.arg.invalid_namespace: 无效的命名空间 25 | ir.op.immediate_left: "指令的左操作数不能是立即数" 26 | ir.op.memory_operands: "指令不能同时使用两个内存操作数" 27 | ir.op.empty_value: 操作数不能为空 28 | ir.op.out_of_range: 立即数超出范围 29 | ir.op.invalid_number_format: 无效的数字格式 30 | ir.op.scale_out_of_range: 缩放值超出范围 31 | ir.op.invalid_ptr: 无效的内存操作数 32 | ir.value.register.unknown_register: 未知寄存器 '{}' 33 | ir.errors_generated: "生成了 {} 个错误" 34 | ir.invalid_op: "无效的指令: '{}'" 35 | ir.invalid_pre_op: "无效的预处理指令: '{}'" 36 | ir.invalid_pop: "无效的预处理弹出指令: '{}',因为栈已经耗尽" 37 | ir.invalid_label: 标签名不能包含空格 38 | ir.invalid_symbol: 符号名不能包含空格 39 | ir.invalid_local_label: 无效的局部标签 40 | ir.invalid_local_symbol: 无效的局部符号 41 | ir.invalid_label_identifier: 需要有效的标签标识符,但得到了 '{}' 42 | ir.unknown_op: "未知指令: '{}'" 43 | ir.unknown_symbol: "未知符号: '{}'" 44 | ir.verify.unreachable: 此代码不会被执行 45 | ir.verify.unreachable_before: 此位置之前的代码不可达 46 | ir.verify.reserved_label: 使用了保留的标签前缀 '{}' 47 | ir.verify.label_redefinition: 标签 '{}' 重定义 48 | ir.verify.symbol_redefinition: 符号 '{}' 重定义 49 | ir.verify.previous_definition: 先前的定义在此 50 | ir.verify.undeclared_label: 使用了未声明的标签 '{}' 51 | ir.verify.undeclared_symbol: 使用了未声明的符号 '{}' 52 | ir.verify.unused_label: 标签 '{}' 已定义但未使用 53 | ir.verify.unused_symbol: 符号 '{}' 已定义但未使用 54 | ir.verify.jmp_to_extern: 跳转到一个外部定义标签是未定义的 55 | ir.verify.never_return: 某些控制流入口点缺乏有效的退出路径 56 | ir.verify.never_return_inner: 入口点在此 57 | file.no_such_file_or_directory: "没有这样的文件或目录: '{}'" 58 | file.failed_to_open: "无法打开文件: '{}'" 59 | file.failed_to_create: "无法创建文件或目录: '{}'" 60 | file.file_not_found: "无法找到 '{}' 文件" 61 | oom.oom: "\033[31m致命错误:\033[97m 内存已耗尽" 62 | general.no_input_files: 没有输入文件 63 | general.invalid_input: 无效的输入文件,预期为 '.mcasm' 文件 64 | general.failed_init_build: 无法初始化构建文件夹 65 | general.unable_to_build: 无法构建输入文件 66 | general.environment_error: "无法验证运行时环境: 缺少必要的资源。请确保所有必需文件存在,或重新安装程序。" 67 | -------------------------------------------------------------------------------- /src/cpp/objects/LogFormatter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/23. 3 | // 4 | 5 | #ifndef CLANG_MC_LOGFORMATTER_H 6 | #define CLANG_MC_LOGFORMATTER_H 7 | 8 | #include "spdlog/spdlog.h" 9 | #include "utils/string/StringUtils.h" 10 | 11 | static inline const char *getLevelColor(spdlog::level::level_enum level) { 12 | switch (level) { 13 | case spdlog::level::critical: 14 | case spdlog::level::err: 15 | return "\033[1;31m"; // fatal/error: 粗体红 16 | case spdlog::level::warn: 17 | return "\033[1;33m"; // warning: 粗体黄 18 | case spdlog::level::info: 19 | return "\033[0;34m"; // note: 蓝色 20 | case spdlog::level::debug: 21 | case spdlog::level::trace: 22 | return "\033[0;90m"; // 灰色(暗) 23 | default: 24 | return ""; 25 | } 26 | } 27 | 28 | 29 | static inline const char *getLevelName(spdlog::level::level_enum level) { 30 | switch (level) { 31 | case spdlog::level::critical: 32 | return "fatal error"; 33 | case spdlog::level::err: 34 | return "error"; 35 | case spdlog::level::warn: 36 | return "warn"; 37 | case spdlog::level::info: 38 | return "note"; 39 | case spdlog::level::debug: 40 | return "debug"; 41 | case spdlog::level::trace: 42 | return "trace"; 43 | default: 44 | return "?"; 45 | } 46 | } 47 | 48 | class LogFormatter : public spdlog::formatter { 49 | public: 50 | void format(const spdlog::details::log_msg &msg, spdlog::memory_buf_t &dest) override { 51 | if (!string::contains(msg.payload.data(), '\n')) { 52 | auto string = fmt::format( 53 | "{}{}: \033[97m{}\033[0m\n", 54 | getLevelColor(msg.level), 55 | getLevelName(msg.level), 56 | msg.payload 57 | ); 58 | dest.append(string); 59 | } else { 60 | auto splits = string::split(msg.payload.data(), '\n', 2); 61 | assert(splits.size() == 2); 62 | auto string = fmt::format( 63 | "{}{}: \033[97m{}\033[0m\n{}\n", 64 | getLevelColor(msg.level), 65 | getLevelName(msg.level), 66 | splits[0], 67 | splits[1] 68 | ); 69 | dest.append(string); 70 | } 71 | } 72 | 73 | [[nodiscard]] std::unique_ptr clone() const override { 74 | return std::make_unique(); 75 | } 76 | }; 77 | 78 | #endif //CLANG_MC_LOGFORMATTER_H 79 | -------------------------------------------------------------------------------- /src/cpp/ir/ops/Div.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/3/15. 3 | // 4 | 5 | #ifndef CLANG_MC_DIV_H 6 | #define CLANG_MC_DIV_H 7 | 8 | #include "ir/iops/Op.h" 9 | #include "utils/string/StringUtils.h" 10 | #include "ir/OpCommon.h" 11 | #include "Mov.h" 12 | #include "ir/values/Symbol.h" 13 | 14 | class Div : public CmpLike { 15 | private: 16 | static std::shared_ptr movToReg(StringBuilder &builder, const ValuePtr &value, 17 | const std::shared_ptr &target) { 18 | if (const auto &immediate = INSTANCEOF_SHARED(value, Immediate)) { 19 | builder.appendLine(fmt::format("scoreboard players set {} vm_regs {}", 20 | target->getName(), static_cast(immediate->getValue()))); 21 | return target; 22 | } else if (const auto &ptr = INSTANCEOF_SHARED(value, Ptr)) { 23 | builder.appendLine(ptr->loadTo(*target)); 24 | return target; 25 | } else { 26 | assert(INSTANCEOF_SHARED(value, Register)); 27 | return CAST_SHARED(value, Register); 28 | } 29 | } 30 | public: 31 | explicit Div(const i32 lineNumber, ValuePtr &&left, ValuePtr &&right) : 32 | Op("div", lineNumber), CmpLike(std::move(left), std::move(right)) { 33 | if (INSTANCEOF_SHARED(left, Immediate)) { 34 | throw ParseException(i18n("ir.op.immediate_left")); 35 | } 36 | } 37 | 38 | void withIR(IR *context) override { 39 | CmpLike::withIR(context); 40 | if (INSTANCEOF_SHARED(this->left, Ptr) && INSTANCEOF_SHARED(this->right, Ptr)) { 41 | throw ParseException(i18n("ir.op.memory_operands")); 42 | } 43 | } 44 | 45 | [[nodiscard]] std::string toString() const noexcept override { 46 | return fmt::format("div {}, {}", left->toString(), right->toString()); 47 | } 48 | 49 | [[nodiscard]] std::string compile() const override { 50 | auto builder = StringBuilder(); 51 | 52 | std::shared_ptr leftVal = movToReg(builder, left, Registers::S2); 53 | std::shared_ptr rightVal = movToReg(builder, right, Registers::S3); 54 | 55 | 56 | builder.append(fmt::format("scoreboard players operation {} vm_regs /= {} vm_regs", 57 | leftVal->getName(), rightVal->getName())); 58 | 59 | if (leftVal != left) { 60 | builder.append('\n'); 61 | auto leftCopy = left; 62 | builder.append(Mov(0, std::move(leftCopy), std::move(leftVal)).compile()); 63 | } 64 | 65 | return builder.toString(); 66 | } 67 | }; 68 | 69 | #endif //CLANG_MC_DIV_H 70 | -------------------------------------------------------------------------------- /src/cpp/ir/ops/Mul.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/3/15. 3 | // 4 | 5 | #ifndef CLANG_MC_MUL_H 6 | #define CLANG_MC_MUL_H 7 | 8 | #include "ir/iops/Op.h" 9 | #include "utils/string/StringUtils.h" 10 | #include "ir/OpCommon.h" 11 | #include "Mov.h" 12 | #include "ir/values/Symbol.h" 13 | 14 | class Mul : public CmpLike { 15 | private: 16 | static std::shared_ptr movToReg(StringBuilder &builder, const ValuePtr &value, 17 | const std::shared_ptr &target) { 18 | if (const auto &immediate = INSTANCEOF_SHARED(value, Immediate)) { 19 | builder.appendLine(fmt::format("scoreboard players set {} vm_regs {}", 20 | target->getName(), static_cast(immediate->getValue()))); 21 | return target; 22 | } else if (const auto &ptr = INSTANCEOF_SHARED(value, Ptr)) { 23 | builder.appendLine(ptr->loadTo(*target)); 24 | return target; 25 | } else { 26 | assert(INSTANCEOF_SHARED(value, Register)); 27 | return CAST_SHARED(value, Register); 28 | } 29 | } 30 | public: 31 | explicit Mul(const i32 lineNumber, ValuePtr &&left, ValuePtr &&right) : 32 | Op("mul", lineNumber), CmpLike(std::move(left), std::move(right)) { 33 | if (INSTANCEOF_SHARED(left, Immediate)) { 34 | throw ParseException(i18n("ir.op.immediate_left")); 35 | } 36 | } 37 | 38 | void withIR(IR *context) override { 39 | CmpLike::withIR(context); 40 | if (INSTANCEOF_SHARED(this->left, Ptr) && INSTANCEOF_SHARED(this->right, Ptr)) { 41 | throw ParseException(i18n("ir.op.memory_operands")); 42 | } 43 | } 44 | 45 | [[nodiscard]] std::string toString() const noexcept override { 46 | return fmt::format("mul {}, {}", left->toString(), right->toString()); 47 | } 48 | 49 | [[nodiscard]] std::string compile() const override { 50 | auto builder = StringBuilder(); 51 | 52 | std::shared_ptr leftVal = movToReg(builder, left, Registers::S2); 53 | std::shared_ptr rightVal = movToReg(builder, right, Registers::S3); 54 | 55 | 56 | builder.append(fmt::format("scoreboard players operation {} vm_regs *= {} vm_regs", 57 | leftVal->getName(), rightVal->getName())); 58 | 59 | if (leftVal != left) { 60 | builder.append('\n'); 61 | auto leftCopy = left; 62 | builder.append(Mov(0, std::move(leftCopy), std::move(leftVal)).compile()); 63 | } 64 | 65 | return builder.toString(); 66 | } 67 | }; 68 | 69 | #endif //CLANG_MC_MUL_H 70 | -------------------------------------------------------------------------------- /src/cpp/utils/string/StringBuilder.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/3/1. 3 | // 4 | 5 | #ifndef CLANG_MC_STRINGBUILDER_H 6 | #define CLANG_MC_STRINGBUILDER_H 7 | 8 | #include "StringUtils.h" 9 | #include "utils/Common.h" 10 | #include "utils/Native.h" 11 | #include "cmath" 12 | 13 | class StringBuilder { 14 | private: 15 | std::stringbuf buffer = std::stringbuf(std::ios::in | std::ios::out | std::ios::app); 16 | public: 17 | explicit StringBuilder() = default; 18 | 19 | template 20 | explicit StringBuilder(const StrLike &string) { 21 | append(string); 22 | } 23 | 24 | ~StringBuilder() = default; 25 | 26 | [[nodiscard]] __forceinline std::string toString() const { 27 | return buffer.str(); 28 | } 29 | 30 | __forceinline void clear() noexcept { 31 | buffer.str(""); 32 | } 33 | 34 | __forceinline bool isEmpty() { 35 | return buffer.str().empty(); 36 | } 37 | 38 | __forceinline u32 size() { 39 | return buffer.str().size(); 40 | } 41 | 42 | __forceinline u32 length() { 43 | return buffer.str().length(); 44 | } 45 | 46 | template 47 | __forceinline void append(const StrLike &string) noexcept { 48 | try { 49 | buffer.sputn(string.data(), string.length()); 50 | } catch (const std::bad_alloc &) { 51 | onOOM(); 52 | } 53 | } 54 | 55 | __forceinline void append(const char * __restrict const string) noexcept { 56 | append(string, strlen(string)); 57 | } 58 | 59 | __forceinline void append(const char * __restrict const string, size_t length) noexcept { 60 | try { 61 | buffer.sputn(string, static_cast(length)); 62 | } catch (const std::bad_alloc &) { 63 | onOOM(); 64 | } 65 | } 66 | 67 | __forceinline void append(const char ch) noexcept { 68 | try { 69 | buffer.sputn(&ch, 1); 70 | } catch (const std::bad_alloc &) { 71 | onOOM(); 72 | } 73 | } 74 | 75 | template 76 | __forceinline void appendLine(const StrLike &string) noexcept { 77 | append(string); 78 | append('\n'); 79 | } 80 | 81 | __forceinline void appendLine(const char ch) noexcept { 82 | append(ch); 83 | append('\n'); 84 | } 85 | 86 | // std::move支持 87 | StringBuilder(StringBuilder &&other) noexcept 88 | : buffer(std::move(other.buffer)) { 89 | } 90 | 91 | StringBuilder &operator=(StringBuilder &&other) noexcept { 92 | if (this != &other) { 93 | buffer = std::move(other.buffer); 94 | } 95 | return *this; 96 | } 97 | }; 98 | 99 | #endif //CLANG_MC_STRINGBUILDER_H 100 | -------------------------------------------------------------------------------- /src/resources/include/int64.mch: -------------------------------------------------------------------------------- 1 | #once 2 | 3 | // typedef struct { 4 | // int low; 5 | // int high; 6 | // } Int64; 7 | 8 | 9 | // void Int64_Zero(Int64 *result) 10 | Int64_Zero: 11 | mov [r0], 0 12 | add r0, 1 13 | mov [r0], 0 14 | ret 15 | 16 | 17 | // void Int64_FromInt(Int64 *result, int value) 18 | Int64_FromInt: 19 | mov [r0], r1 20 | add r0, 1 21 | jl r1, 0, .special 22 | mov [r0], 0x00000000 23 | ret 24 | .special: 25 | mov [r0], 0x7FFFFFFF 26 | ret 27 | 28 | 29 | // void Int64_Copy(Int64 *result, Int64 *value) 30 | Int64_Copy: 31 | mov [r0], [r1] 32 | add r0, 1 33 | add r1, 1 34 | mov [r0], [r1] 35 | ret 36 | 37 | 38 | // bool Int64_CheckInt(Int64 *value) 39 | Int64_CheckInt: 40 | mov rax, 1 41 | add r0, 1 42 | re [r0], 0x00000000 43 | re [r0], 0x7FFFFFFF 44 | mov rax, 0 45 | ret 46 | 47 | 48 | // int Int64_AsInt(Int64 *value) 49 | Int64_AsInt: 50 | // assert(Int64_CheckInt(value)); 51 | mov rax, [r0] 52 | add r0, 1 53 | re [r0], 0x00000000 54 | mul rax, -1 55 | ret 56 | 57 | 58 | #define Int64_ADD(resLow, resHigh, valueLow, valueHigh) \ 59 | #push label \ 60 | add resHigh, valueHigh \ 61 | add resLow, valueLow \ 62 | jge resLow, valueLow, ._Int64_ADD__fin \ 63 | add resHigh, 1 \ 64 | ._Int64_ADD__fin: \ 65 | #pop label 66 | 67 | #define Int64_SUB(resLow, resHigh, valueLow, valueHigh, tmp) \ 68 | #push label \ 69 | mov tmp, resLow \ 70 | sub resLow, valueLow \ 71 | sub resHigh, valueHigh \ 72 | jge tmp, valueLow, ._Int64_SUB__fin \ 73 | sub resHigh, 1 // 借位,修正高32位 \ 74 | ._Int64_SUB__fin: \ 75 | #pop label 76 | 77 | 78 | // void Int64_Add(Int64 *result, Int64 *value) 79 | Int64_Add: 80 | mov t0, r0 81 | add t0, 1 82 | mov t1, [r0] 83 | mov t2, [t0] 84 | mov t3, [r1] 85 | add r1, 1 86 | mov t4, [r1] 87 | 88 | // int *r0; // resultLow 89 | // int *t0; // resultHigh 90 | // int t1; // resultLowVal 91 | // int t2; // resultHighVal 92 | // int t3; // valueLowVal 93 | // int t4; // valueHighVal 94 | Int64_ADD(t1, t2, t3, t4) 95 | 96 | mov [r0], t1 97 | mov [t0], t2 98 | ret 99 | 100 | 101 | // void Int64_Sub(Int64 *result, Int64 *value) 102 | Int64_Sub: 103 | mov t0, r0 104 | add t0, 1 105 | mov t1, [r0] 106 | mov t2, [t0] 107 | mov t3, [r1] 108 | add r1, 1 109 | mov t4, [r1] 110 | 111 | // int *r0; // resultLow 112 | // int *t0; // resultHigh 113 | // int t1; // resultLowVal 114 | // int t2; // resultHighVal 115 | // int t3; // valueLowVal 116 | // int t4; // valueHighVal 117 | Int64_SUB(t1, t2, t3, t4, t5) 118 | 119 | mov [r0], t1 120 | mov [t0], t2 121 | ret 122 | -------------------------------------------------------------------------------- /src/cpp/utils/CLIUtils.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/18. 3 | // 4 | 5 | #ifndef CLANG_MC_CLIUTILS_H 6 | #define CLANG_MC_CLIUTILS_H 7 | 8 | #include "utils/Common.h" 9 | #include "i18n/I18n.h" 10 | 11 | extern int ARGC; 12 | extern const char **ARGV; 13 | 14 | static inline const char *getArgv0() { 15 | if (ARGC == 0) return ""; 16 | return *ARGV; 17 | } 18 | 19 | PURE static inline std::string getExecutableName(const std::string_view& argv0) { 20 | if (argv0.empty()) { // 基本不可能发生,我想 21 | #ifdef _WIN32 22 | return "clang-mc.exe"; 23 | #else 24 | return "clang-mc" 25 | #endif 26 | } 27 | return std::filesystem::path(argv0).filename().string(); 28 | } 29 | 30 | PURE static inline std::string getExecutableDir(const std::string_view& argv0) { 31 | if (argv0.empty()) { 32 | #ifdef _WIN32 33 | return "Unknown"; 34 | #else 35 | return "/" 36 | #endif 37 | } 38 | return std::filesystem::path(argv0).parent_path().string(); 39 | } 40 | 41 | PURE static inline std::string getHelpMessage(const std::string& argv0) noexcept { 42 | return i18nFormat("cli.help_message_template", getExecutableName(argv0)); 43 | } 44 | 45 | PURE static inline std::string getTargetMachine() noexcept { 46 | std::string result; 47 | 48 | #if defined(__x86_64__) || defined(_M_X64) 49 | result += "x86_64"; 50 | #elif defined(__i386__) || defined(_M_IX86) 51 | result += "i386"; 52 | #elif defined(__aarch64__) || defined(_M_ARM64) 53 | result += "aarch64"; 54 | #elif defined(__arm__) || defined(_M_ARM) 55 | result += "arm"; 56 | #elif defined(__riscv) && (__riscv_xlen == 64) 57 | result += "riscv64"; 58 | #elif defined(__riscv) && (__riscv_xlen == 32) 59 | result += "riscv32"; 60 | #else 61 | result += "unknown"; 62 | #endif 63 | 64 | result += "-"; 65 | 66 | #if defined(__APPLE__) 67 | result += "apple"; 68 | #elif defined(_WIN32) 69 | result += "pc"; 70 | #else 71 | result += "unknown"; 72 | #endif 73 | 74 | result += "-"; 75 | 76 | #if defined(__linux__) 77 | result += "linux"; 78 | #elif defined(__APPLE__) 79 | result += "darwin"; 80 | #elif defined(_WIN32) 81 | result += "windows"; 82 | #elif defined(__FreeBSD__) 83 | result += "freebsd"; 84 | #elif defined(__NetBSD__) 85 | result += "netbsd"; 86 | #elif defined(__OpenBSD__) 87 | result += "openbsd"; 88 | #else 89 | result += "unknown"; 90 | #endif 91 | 92 | #if defined(__GNUC__) 93 | result += "-gnu"; 94 | #elif defined(_MSC_VER) 95 | result += "-msvc"; 96 | #elif defined(__ANDROID__) 97 | result += "-androideabi"; 98 | #elif defined(__MUSL__) 99 | result += "-musl"; 100 | #endif 101 | 102 | return result; 103 | } 104 | 105 | PURE std::string getVersionMessage(const std::string& argv0) noexcept; 106 | 107 | #endif //CLANG_MC_CLIUTILS_H 108 | -------------------------------------------------------------------------------- /src/resources/include/memory.mch: -------------------------------------------------------------------------------- 1 | #once 2 | 3 | // void *malloc(int size) 4 | malloc: 5 | mov rax, 0 6 | rle r0, 0 7 | 8 | .loop: 9 | mov t0, [rax] 10 | jg t0, r0, .found 11 | sub rax, t0 12 | jge rax, shp, .oom 13 | jmp .loop 14 | .oom: 15 | push r0 16 | mov r0, shp 17 | inline function std:heap/expand 18 | 19 | mov t0, r0 20 | mov [rax], t0 21 | 22 | pop r0 23 | jg t0, r0, .found 24 | add rax, t0 25 | jge rax, shp, .oom 26 | jmp .loop 27 | .found: 28 | add r0, 1 29 | je t0, r0, .to_ret 30 | 31 | sub t0, r0 32 | mov [rax + r0], t0 33 | 34 | .to_ret: 35 | mul r0, -1 36 | mov [rax], r0 37 | add rax, 1 38 | ret 39 | 40 | 41 | // void free(void *ptr) 42 | free: 43 | rle r0, 0 44 | // 暂时不考虑合并块 45 | mul [r0 - 1], -1 // 最高位取反 46 | ret 47 | 48 | // void memset(void *ptr, int value, int size) 49 | memset: 50 | rle r2, 0 51 | 52 | add r2, r0 53 | .loop: 54 | mov [r0], r1 55 | add r0, 1 56 | re r0, r2 57 | 58 | mov [r0], r1 59 | add r0, 1 60 | re r0, r2 61 | 62 | mov [r0], r1 63 | add r0, 1 64 | re r0, r2 65 | 66 | mov [r0], r1 67 | add r0, 1 68 | re r0, r2 69 | 70 | jmp .loop 71 | 72 | // void memcpy(void *from, void *to, int size) 73 | memcpy: 74 | rle r2, 0 75 | re r0, r1 76 | 77 | add r2, r0 78 | .loop: 79 | mov [r1], [r0] 80 | add r0, 1 81 | add r1, 1 82 | re r0, r2 83 | 84 | mov [r1], [r0] 85 | add r0, 1 86 | add r1, 1 87 | re r0, r2 88 | 89 | mov [r1], [r0] 90 | add r0, 1 91 | add r1, 1 92 | re r0, r2 93 | 94 | mov [r1], [r0] 95 | add r0, 1 96 | add r1, 1 97 | re r0, r2 98 | 99 | jmp .loop 100 | 101 | // void memmove(void *src, void *dst, int size) 102 | memmove: 103 | rle r2, 0 104 | re r0, r1 105 | 106 | jl r0, r1, .backward 107 | 108 | .forward: 109 | add r2, r0 110 | .forwardLoop: 111 | mov [r1], [r0] 112 | add r0, 1 113 | add r1, 1 114 | re r0, r2 115 | 116 | mov [r1], [r0] 117 | add r0, 1 118 | add r1, 1 119 | re r0, r2 120 | 121 | mov [r1], [r0] 122 | add r0, 1 123 | add r1, 1 124 | re r0, r2 125 | 126 | mov [r1], [r0] 127 | add r0, 1 128 | add r1, 1 129 | re r0, r2 130 | 131 | jmp .forwardLoop 132 | 133 | .backward: 134 | mov t0, r0 135 | add r0, r2 136 | add r1, r2 137 | .backwardLoop: 138 | sub r0, 1 139 | sub r1, 1 140 | mov [r1], [r0] 141 | re r0, t0 142 | 143 | sub r0, 1 144 | sub r1, 1 145 | mov [r1], [r0] 146 | re r0, t0 147 | 148 | sub r0, 1 149 | sub r1, 1 150 | mov [r1], [r0] 151 | re r0, t0 152 | 153 | sub r0, 1 154 | sub r1, 1 155 | mov [r1], [r0] 156 | re r0, t0 157 | 158 | jmp .backwardLoop 159 | -------------------------------------------------------------------------------- /src/cpp/ir/iops/CmpLike.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/27. 3 | // 4 | 5 | #ifndef CLANG_MC_CMPLIKE_H 6 | #define CLANG_MC_CMPLIKE_H 7 | 8 | #include 9 | 10 | #include "Op.h" 11 | #include "utils/string/StringUtils.h" 12 | #include "ir/OpCommon.h" 13 | #include "ir/values/Symbol.h" 14 | #include "ir/IR.h" 15 | #include "ir/values/SymbolPtr.h" 16 | 17 | class CmpLike : public virtual Op { 18 | private: 19 | std::string prefix; 20 | protected: 21 | ValuePtr left; 22 | ValuePtr right; 23 | 24 | template 25 | inline std::string cmp(const Self *self, const std::span &cmds, T *leftVal, U *rightVal) const { 26 | auto result = StringBuilder(self->cmp(cmds[0], leftVal, rightVal)); 27 | for (size_t i = 1; i < cmds.size(); ++i) { 28 | result.append('\n'); 29 | result.append(self->cmp(cmds[i], leftVal, rightVal)); 30 | } 31 | return result.toString(); 32 | } 33 | public: 34 | explicit CmpLike(ValuePtr &&left, ValuePtr &&right) noexcept: left(std::move(left)), right(std::move(right)) { 35 | } 36 | 37 | void withIR(IR *context) override { 38 | Op::withIR(context); 39 | auto prefixBuilder = StringBuilder(); 40 | 41 | if (const auto &symbol = INSTANCEOF_SHARED(left, Symbol)) { 42 | left = Registers::S3; 43 | prefixBuilder.appendLine("scoreboard players operation s3 vm_regs = sbp vm_regs"); 44 | prefixBuilder.appendLine(fmt::format("scoreboard players add s3 vm_regs {}", 45 | context->getStaticDataMap().at(symbol->getNameHash()))); 46 | } else if (const auto &symbolPtr = INSTANCEOF_SHARED(left, SymbolPtr)) { 47 | left = std::make_shared( 48 | Registers::SBP.get(), nullptr, 1, 49 | context->getStaticDataMap().at(symbolPtr->getNameHash())); 50 | } 51 | 52 | if (const auto &symbol = INSTANCEOF_SHARED(right, Symbol)) { 53 | right = Registers::S4; 54 | prefixBuilder.appendLine("scoreboard players operation s4 vm_regs = sbp vm_regs"); 55 | prefixBuilder.appendLine(fmt::format("scoreboard players add s4 vm_regs {}", 56 | context->getStaticDataMap().at(symbol->getNameHash()))); 57 | } else if (const auto &symbolPtr = INSTANCEOF_SHARED(right, SymbolPtr)) { 58 | right = std::make_shared( 59 | Registers::SBP.get(), nullptr, 1, 60 | context->getStaticDataMap().at(symbolPtr->getNameHash())); 61 | 62 | } 63 | 64 | prefix = prefixBuilder.toString(); 65 | }; 66 | 67 | [[nodiscard]] std::string compilePrefix() const override { 68 | return prefix; 69 | } 70 | 71 | GETTER(Left, left); 72 | 73 | GETTER(Right, right); 74 | }; 75 | 76 | #endif //CLANG_MC_CMPLIKE_H 77 | -------------------------------------------------------------------------------- /src/cpp/ir/ops/Add.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/24. 3 | // 4 | 5 | #ifndef CLANG_MC_ADD_H 6 | #define CLANG_MC_ADD_H 7 | 8 | #include "ir/iops/Op.h" 9 | #include "utils/string/StringUtils.h" 10 | #include "ir/OpCommon.h" 11 | #include "Mov.h" 12 | 13 | class Add : public CmpLike { 14 | public: 15 | explicit Add(const i32 lineNumber, ValuePtr &&left, ValuePtr &&right) : 16 | Op("add", lineNumber), CmpLike(std::move(left), std::move(right)) { 17 | if (INSTANCEOF_SHARED(left, Immediate)) { 18 | throw ParseException(i18n("ir.op.immediate_left")); 19 | } 20 | } 21 | 22 | void withIR(IR *context) override { 23 | CmpLike::withIR(context); 24 | if (INSTANCEOF_SHARED(this->left, Ptr) && INSTANCEOF_SHARED(this->right, Ptr)) { 25 | throw ParseException(i18n("ir.op.memory_operands")); 26 | } 27 | } 28 | 29 | [[nodiscard]] std::string toString() const noexcept override { 30 | return fmt::format("add {}, {}", left->toString(), right->toString()); 31 | } 32 | 33 | [[nodiscard]] std::string compile() const override { 34 | if (const auto &value = INSTANCEOF_SHARED(right, Immediate)) { 35 | if (const auto &result = INSTANCEOF_SHARED(left, Register)) { 36 | return fmt::format("scoreboard players add {} vm_regs {}", 37 | result->getName(), static_cast(value->getValue())); 38 | } 39 | 40 | // 与x86不同,mc不支持直接对storage(内存)中的值做计算 41 | return fmt::format("{}\nscoreboard players add s1 vm_regs {}\n{}", 42 | CAST_FAST(left, Ptr)->loadTo(*Registers::S1), 43 | static_cast(value->getValue()), 44 | CAST_FAST(left, Ptr)->storeFrom(*Registers::S1)); 45 | } 46 | if (const auto &value = INSTANCEOF_SHARED(right, Register)) { 47 | if (const auto &result = INSTANCEOF_SHARED(left, Register)) { 48 | return fmt::format("scoreboard players operation {} vm_regs += {} vm_regs", 49 | result->getName(), value->getName()); 50 | } 51 | 52 | assert(INSTANCEOF_SHARED(left, Ptr)); 53 | 54 | // 与x86不同,mc不支持直接对storage(内存)中的值做计算 55 | return fmt::format("{}\nscoreboard players operation s1 vm_regs += {} vm_regs\n{}", 56 | CAST_FAST(left, Ptr)->loadTo(*Registers::S1), 57 | value->getName(), 58 | CAST_FAST(left, Ptr)->storeFrom(*Registers::S1)); 59 | } 60 | 61 | assert(INSTANCEOF_SHARED(right, Ptr)); 62 | assert(INSTANCEOF_SHARED(left, Register)); 63 | 64 | // 与x86不同,mc不支持直接对storage(内存)中的值做计算 65 | return fmt::format("{}\nscoreboard players operation {} vm_regs += s1 vm_regs", 66 | CAST_FAST(right, Ptr)->loadTo(*Registers::S1), 67 | CAST_FAST(left, Register)->getName()); 68 | } 69 | }; 70 | 71 | #endif //CLANG_MC_ADD_H 72 | -------------------------------------------------------------------------------- /src/cpp/ir/ops/Sub.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/24. 3 | // 4 | 5 | #ifndef CLANG_MC_SUB_H 6 | #define CLANG_MC_SUB_H 7 | 8 | #include "ir/iops/Op.h" 9 | #include "utils/string/StringUtils.h" 10 | #include "ir/OpCommon.h" 11 | #include "Mov.h" 12 | 13 | class Sub : public CmpLike { 14 | public: 15 | explicit Sub(const i32 lineNumber, ValuePtr &&left, ValuePtr &&right) : 16 | Op("sub", lineNumber), CmpLike(std::move(left), std::move(right)) { 17 | if (INSTANCEOF_SHARED(left, Immediate)) { 18 | throw ParseException(i18n("ir.op.immediate_left")); 19 | } 20 | } 21 | 22 | void withIR(IR *context) override { 23 | CmpLike::withIR(context); 24 | if (INSTANCEOF_SHARED(this->left, Ptr) && INSTANCEOF_SHARED(this->right, Ptr)) { 25 | throw ParseException(i18n("ir.op.memory_operands")); 26 | } 27 | } 28 | 29 | [[nodiscard]] std::string toString() const noexcept override { 30 | return fmt::format("sub {}, {}", left->toString(), right->toString()); 31 | } 32 | 33 | [[nodiscard]] std::string compile() const override { 34 | if (const auto &immediate = INSTANCEOF_SHARED(right, Immediate)) { 35 | if (const auto &result = INSTANCEOF_SHARED(left, Register)) { 36 | return fmt::format("scoreboard players remove {} vm_regs {}", 37 | result->getName(), static_cast(immediate->getValue())); 38 | } else { 39 | assert(INSTANCEOF_SHARED(left, Ptr)); 40 | 41 | // 与x86不同,mc不支持直接对storage(内存)中的值做计算 42 | return fmt::format("{}\nscoreboard players remove s1 vm_regs {}\n{}", 43 | CAST_FAST(left, Ptr)->loadTo(*Registers::S1), 44 | static_cast(immediate->getValue()), 45 | CAST_FAST(left, Ptr)->storeFrom(*Registers::S1)); 46 | } 47 | } else if (const auto ® = INSTANCEOF_SHARED(right, Register)) { 48 | if (const auto &result = INSTANCEOF_SHARED(left, Register)) { 49 | return fmt::format("scoreboard players operation {} vm_regs -= {} vm_regs", 50 | result->getName(), reg->getName()); 51 | } else { 52 | assert(INSTANCEOF_SHARED(left, Ptr)); 53 | 54 | // 与x86不同,mc不支持直接对storage(内存)中的值做计算 55 | return fmt::format("{}\nscoreboard players operation s1 vm_regs -= {} vm_regs\n{}", 56 | CAST_FAST(left, Ptr)->loadTo(*Registers::S1), 57 | reg->getName(), 58 | CAST_FAST(left, Ptr)->storeFrom(*Registers::S1)); 59 | } 60 | } else { 61 | assert(INSTANCEOF_SHARED(right, Ptr)); 62 | assert(INSTANCEOF_SHARED(left, Register)); 63 | 64 | // 与x86不同,mc不支持直接对storage(内存)中的值做计算 65 | return fmt::format("{}\nscoreboard players operation {} vm_regs -= s1 vm_regs", 66 | CAST_FAST(right, Ptr)->loadTo(*Registers::S1), 67 | CAST_FAST(left, Register)->getName()); 68 | } 69 | } 70 | }; 71 | 72 | #endif //CLANG_MC_SUB_H 73 | -------------------------------------------------------------------------------- /src/cpp/utils/FileUtils.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/17. 3 | // 4 | 5 | #ifndef CLANG_MC_FILEUTILS_H 6 | #define CLANG_MC_FILEUTILS_H 7 | 8 | #include 9 | #include "Common.h" 10 | #include 11 | #include "i18n/I18n.h" 12 | 13 | [[nodiscard]] static inline std::string readFile(const Path &filePath) { 14 | if (!exists(filePath)) { 15 | throw IOException(i18nFormat("file.no_such_file_or_directory", filePath.string())); 16 | } 17 | 18 | auto file = std::ifstream(filePath); 19 | if (!file) { 20 | throw IOException(i18nFormat("file.failed_to_open", filePath.string())); 21 | } 22 | 23 | std::ostringstream buffer; 24 | buffer << file.rdbuf(); 25 | return buffer.str(); 26 | } 27 | 28 | static inline void writeFile(const Path &filePath, const std::string_view &content) { 29 | auto file = std::ofstream(filePath); 30 | if (!file) { 31 | throw IOException(i18nFormat("file.failed_to_open", filePath.string())); 32 | } 33 | 34 | file << content; 35 | if (!file) { // 确保写入成功 36 | throw IOException(i18nFormat("file.failed_to_open", filePath.string())); 37 | } 38 | } 39 | 40 | static inline void writeFileIfNotExist(const Path &filePath, const std::string_view &content) { 41 | if (exists(filePath)) { 42 | return; 43 | } 44 | 45 | writeFile(filePath, content); 46 | } 47 | 48 | static inline void ensureDirectory(const Path &dir) { // NOLINT(*-no-recursion) 49 | try { 50 | if (!exists(dir)) { 51 | if (dir.has_parent_path()) { 52 | ensureDirectory(dir.parent_path()); 53 | } 54 | create_directory(dir); 55 | } 56 | } catch (const std::filesystem::filesystem_error &e) { 57 | throw IOException(i18nFormat("file.failed_to_create", dir.string())); 58 | } 59 | } 60 | 61 | static inline void ensureParentDir(const Path &file) { 62 | ensureDirectory(file.parent_path()); 63 | } 64 | 65 | static inline void compressFolder(const Path &folder, const Path &zip) { 66 | const auto zipFilename = zip.string(); 67 | int error = 0; 68 | zip_t *archive = zip_open(zipFilename.c_str(), ZIP_CREATE | ZIP_TRUNCATE, &error); 69 | if (!archive) { 70 | throw IOException(i18nFormat("file.failed_to_create", zipFilename)); 71 | } 72 | 73 | for (const auto &entry : std::filesystem::recursive_directory_iterator(folder)) { 74 | if (std::filesystem::is_regular_file(entry.path())) { 75 | const auto &filePath = entry.path(); 76 | std::string relativePath = std::filesystem::relative(filePath, folder).generic_string(); 77 | 78 | zip_source_t *source = zip_source_file(archive, filePath.string().c_str(), 0, 0); 79 | if (!source) { 80 | zip_close(archive); 81 | throw IOException(i18nFormat("file.failed_to_create", zipFilename)); 82 | } 83 | 84 | if (zip_file_add(archive, relativePath.c_str(), source, ZIP_FL_OVERWRITE | ZIP_FL_ENC_UTF_8) < 0) { 85 | zip_source_free(source); 86 | zip_close(archive); 87 | throw IOException(i18nFormat("file.failed_to_create", zipFilename)); 88 | } 89 | } 90 | } 91 | 92 | if (zip_close(archive) < 0) { 93 | throw IOException(i18nFormat("file.failed_to_close", zipFilename)); 94 | } 95 | } 96 | 97 | 98 | #endif //CLANG_MC_FILEUTILS_H 99 | -------------------------------------------------------------------------------- /src/resources/assets/stdlib/data/std/function/__init__.mcfunction: -------------------------------------------------------------------------------- 1 | # 初始化虚拟机资源 2 | scoreboard objectives add vm_regs dummy 3 | scoreboard objectives remove vm_regs 4 | scoreboard objectives add vm_regs dummy 5 | 6 | # 寄存器 7 | # 函数返回值 caller-saved 8 | scoreboard players set rax vm_regs 0 9 | # 参数或临时寄存器 caller-saved 10 | scoreboard players set r0 vm_regs 0 11 | scoreboard players set r1 vm_regs 0 12 | scoreboard players set r2 vm_regs 0 13 | scoreboard players set r3 vm_regs 0 14 | scoreboard players set r4 vm_regs 0 15 | scoreboard players set r5 vm_regs 0 16 | scoreboard players set r6 vm_regs 0 17 | scoreboard players set r7 vm_regs 0 18 | # 临时寄存器 caller-saved 19 | scoreboard players set t0 vm_regs 0 20 | scoreboard players set t1 vm_regs 0 21 | scoreboard players set t2 vm_regs 0 22 | scoreboard players set t3 vm_regs 0 23 | scoreboard players set t4 vm_regs 0 24 | scoreboard players set t5 vm_regs 0 25 | scoreboard players set t6 vm_regs 0 26 | scoreboard players set t7 vm_regs 0 27 | # 保存寄存器 callee-saved 28 | scoreboard players set x0 vm_regs 0 29 | scoreboard players set x1 vm_regs 0 30 | scoreboard players set x2 vm_regs 0 31 | scoreboard players set x3 vm_regs 0 32 | scoreboard players set x4 vm_regs 0 33 | scoreboard players set x5 vm_regs 0 34 | scoreboard players set x6 vm_regs 0 35 | scoreboard players set x7 vm_regs 0 36 | scoreboard players set x8 vm_regs 0 37 | scoreboard players set x9 vm_regs 0 38 | scoreboard players set x10 vm_regs 0 39 | scoreboard players set x11 vm_regs 0 40 | scoreboard players set x12 vm_regs 0 41 | scoreboard players set x13 vm_regs 0 42 | scoreboard players set x14 vm_regs 0 43 | scoreboard players set x15 vm_regs 0 44 | # 栈指针 45 | scoreboard players set rsp vm_regs 0 46 | # 堆大小 47 | scoreboard players set shp vm_regs 0 48 | # 静态数据区指针 49 | scoreboard players set sbp vm_regs 0 50 | # 编译器保留 51 | scoreboard players set s0 vm_regs 0 52 | scoreboard players set s1 vm_regs 0 53 | scoreboard players set s2 vm_regs 0 54 | scoreboard players set s3 vm_regs 0 55 | scoreboard players set s4 vm_regs 0 56 | 57 | # 复杂存储 58 | # list[int] 堆空间 59 | data modify storage std:vm heap set value [] 60 | # 字符串常量映射 不要修改此映射,该映射由脚本生成 61 | data modify storage std:vm str2char_map set value {"\\t": 9, "\\n": 10, "\\x0b": 11, "\\x0c": 12, "\\r": 13, " ": 32, "!": 33, "\"": 34, "#": 35, "$": 36, "%": 37, "&": 38, "'": 39, "(": 40, ")": 41, "*": 42, "+": 43, ",": 44, "-": 45, ".": 46, "/": 47, "0": 48, "1": 49, "2": 50, "3": 51, "4": 52, "5": 53, "6": 54, "7": 55, "8": 56, "9": 57, ":": 58, ";": 59, "<": 60, "=": 61, ">": 62, "?": 63, "@": 64, "A": 65, "B": 66, "C": 67, "D": 68, "E": 69, "F": 70, "G": 71, "H": 72, "I": 73, "J": 74, "K": 75, "L": 76, "M": 77, "N": 78, "O": 79, "P": 80, "Q": 81, "R": 82, "S": 83, "T": 84, "U": 85, "V": 86, "W": 87, "X": 88, "Y": 89, "Z": 90, "[": 91, "\\\\": 92, "]": 93, "^": 94, "_": 95, "`": 96, "a": 97, "b": 98, "c": 99, "d": 100, "e": 101, "f": 102, "g": 103, "h": 104, "i": 105, "j": 106, "k": 107, "l": 108, "m": 109, "n": 110, "o": 111, "p": 112, "q": 113, "r": 114, "s": 115, "t": 116, "u": 117, "v": 118, "w": 119, "x": 120, "y": 121, "z": 122, "{": 123, "|": 124, "}": 125, "~": 126} 62 | # 标准库保留 63 | data modify storage std:vm s2 set value {} 64 | data modify storage std:vm s3 set value {} 65 | data modify storage std:vm s4 set value {} 66 | data modify storage std:vm s5 set value {} 67 | data modify storage std:vm s6 set value {} 68 | 69 | 70 | # 初始化堆 71 | scoreboard players set r0 vm_regs 1024 72 | function std:heap/expand 73 | scoreboard players set r0 vm_regs 0 74 | 75 | function std:_internal/__init__ 76 | -------------------------------------------------------------------------------- /src/cpp/ir/IR.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/16. 3 | // 4 | 5 | #ifndef CLANG_MC_IR_H 6 | #define CLANG_MC_IR_H 7 | 8 | #include 9 | #include "config/Config.h" 10 | #include "vector" 11 | #include "ir/ops/Label.h" 12 | #include "IRCommon.h" 13 | #include "objects/NameGenerator.h" 14 | #include "extern/ResourceManager.h" 15 | #include "State.h" 16 | 17 | class ParseManager; 18 | 19 | class IR { 20 | private: 21 | const Logger &logger; 22 | const Config &config; 23 | Path file; 24 | std::string fileDisplay; 25 | std::string sourceCode; // 维持sourceMap中的view 26 | HashMap sourceMap = HashMap(); 27 | HashMap lineStateMap = HashMap(); 28 | HashMap defines; 29 | std::vector values = std::vector(); 30 | 31 | std::vector staticData = std::vector(); 32 | HashMap staticDataMap = HashMap(); 33 | 34 | std::string createForCall(const Label *labelOp); 35 | 36 | void initLabels(LabelMap &labelMap); 37 | 38 | friend class ParseManager; 39 | public: 40 | explicit IR(const Logger &logger, const Config &config, Path &&file, HashMap &&defines) : 41 | logger(logger), config(config), file(std::move(file)), 42 | fileDisplay(string::getPrettyPath(this->file)), defines(defines) { 43 | } 44 | 45 | [[nodiscard]] __forceinline std::string_view getFileDisplay() const { 46 | return fileDisplay; 47 | } 48 | 49 | GETTER(File, file); 50 | 51 | GETTER(Values, values); 52 | 53 | GETTER(StaticData, staticData); 54 | 55 | GETTER(StaticDataMap, staticDataMap); 56 | 57 | void parse(std::string &&code); 58 | 59 | void preCompile(); 60 | 61 | [[nodiscard]] McFunctions compile(); 62 | 63 | [[nodiscard]] std::string_view getSource(const Op *op) const { 64 | assert(!sourceMap.empty()); 65 | 66 | const auto result = sourceMap.find(op); 67 | if (result != sourceMap.end()) { 68 | return result->second; 69 | } 70 | return "Unknown Source"; 71 | } 72 | 73 | [[nodiscard]] LineState getLine(const Op *op) const { 74 | assert(!lineStateMap.empty()); 75 | 76 | const auto result = lineStateMap.find(op); 77 | if (result != lineStateMap.end()) { 78 | return result->second; 79 | } 80 | return {}; 81 | } 82 | 83 | static inline std::string generateName() { 84 | return NameGenerator::getInstance().generate(); 85 | } 86 | }; 87 | 88 | PURE static inline std::string createIRMessage(const LineState &line, const std::string_view &source, const std::string_view &message) { 89 | std::string lineNumber; 90 | if (line.lineNumber == -1) { 91 | lineNumber = "?"; 92 | } else { 93 | lineNumber = std::to_string(line.lineNumber); 94 | } 95 | 96 | return fmt::format("{}:{}: {}\n {} | {}", 97 | line.filename, lineNumber, message, lineNumber, source); 98 | } 99 | 100 | std::string createIRMessage(const IR &ir, const Op *op, const std::string_view &message); 101 | 102 | PURE static inline std::string createIRMessage(const LineState &line, const std::string_view &message) { 103 | return createIRMessage(line, "Unknown Source", message); 104 | } 105 | 106 | #endif //CLANG_MC_IR_H 107 | -------------------------------------------------------------------------------- /src/cpp/objects/Int.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/5/25. 3 | // 4 | 5 | #ifndef CLANG_MC_INT_H 6 | #define CLANG_MC_INT_H 7 | 8 | 9 | #include "utils/Common.h" 10 | 11 | class Int { 12 | private: 13 | i32 value; 14 | public: 15 | Int() = delete; 16 | 17 | __forceinline constexpr Int(i32 value) noexcept: value(value) { // NOLINT(*-explicit-constructor) 18 | } 19 | 20 | [[nodiscard]] inline constexpr bool checkAdd(Int rhs) const noexcept { 21 | return LIKELY((rhs.value >= 0) 22 | ? value <= (INT32_MAX - rhs.value) 23 | : value >= (INT32_MIN - rhs.value)); 24 | } 25 | 26 | [[nodiscard]] inline constexpr bool checkSub(Int rhs) const noexcept { 27 | return LIKELY((rhs.value >= 0) 28 | ? value >= (INT32_MIN + rhs.value) 29 | : value <= (INT32_MAX + (-rhs.value))); 30 | } 31 | 32 | [[nodiscard]] inline constexpr bool checkMul(Int rhs) const noexcept { 33 | if (value == 0 || rhs.value == 0) return true; 34 | if (value == -1) return rhs.value != INT32_MIN; 35 | if (rhs.value == -1) return value != INT32_MIN; 36 | i32 result = value * rhs.value; 37 | return LIKELY(result / rhs.value == value); 38 | } 39 | 40 | [[nodiscard]] inline constexpr bool checkDiv(Int rhs) const noexcept { 41 | return LIKELY(!(value == INT32_MIN && rhs.value == -1) && rhs.value != 0); 42 | } 43 | 44 | __forceinline constexpr bool operator==(Int rhs) const noexcept { 45 | return value == rhs.value; 46 | } 47 | 48 | __forceinline constexpr bool operator==(i32 rhs) const noexcept { 49 | return operator==(Int(rhs)); 50 | } 51 | 52 | __forceinline constexpr Int operator+(Int rhs) const noexcept { 53 | if (checkAdd(rhs)) return value + rhs.value; 54 | return {static_cast( 55 | static_cast(value) + static_cast(rhs.value))}; 56 | } 57 | 58 | __forceinline constexpr Int operator-(Int rhs) const noexcept { 59 | if (checkSub(rhs)) return value - rhs.value; 60 | return static_cast( 61 | static_cast(value) - static_cast(rhs.value)); 62 | } 63 | 64 | __forceinline constexpr Int operator*(Int rhs) const noexcept { 65 | if (checkMul(rhs)) return value * rhs.value; 66 | return static_cast( 67 | static_cast(value) * static_cast(rhs.value)); 68 | 69 | } 70 | 71 | __forceinline constexpr Int operator/(Int rhs) const noexcept { 72 | if (checkDiv(rhs)) return value / rhs.value; 73 | // UB:INT_MIN / -1 或除以 0 74 | return INT32_MAX; 75 | } 76 | 77 | __forceinline constexpr i32 operator+(i32 rhs) const noexcept { 78 | return operator+(Int(rhs)); 79 | } 80 | 81 | __forceinline constexpr i32 operator-(i32 rhs) const noexcept { 82 | return operator-(Int(rhs)); 83 | } 84 | 85 | __forceinline constexpr i32 operator*(i32 rhs) const noexcept { 86 | return operator*(Int(rhs)); 87 | } 88 | 89 | __forceinline constexpr i32 operator/(i32 rhs) const noexcept { 90 | return operator/(Int(rhs)); 91 | } 92 | 93 | __forceinline constexpr operator i32() const noexcept { return value; } // NOLINT(*-explicit-constructor) 94 | 95 | [[nodiscard]] __forceinline constexpr std::string toString() const { 96 | return std::to_string(value); 97 | } 98 | }; 99 | 100 | 101 | #endif //CLANG_MC_INT_H 102 | -------------------------------------------------------------------------------- /src/resources/lang/EN_US.yaml: -------------------------------------------------------------------------------- 1 | cli.help_message_template: | 2 | USAGE: {} [options] file... 3 | 4 | OPTIONS: 5 | --help \tDisplay this help message 6 | --version \tPrint version information 7 | --log-file (-l) \tWrite logs to a file 8 | --output (-o) \tSpecify the output file name 9 | --build-dir (-B) \tSpecify the build directory 10 | --namespace (-N) \tSpecify the namespace path for compiling non-exported functions 11 | --compile-only (-c) \tOnly compile, do not link as .zip 12 | -g \tGenerate additional debug information 13 | -Werror \tMake all warnings into errors 14 | -E \tOnly run the preprocessor 15 | -w \tSuppress all warnings 16 | cli.version_message_template: | 17 | {} version {} 18 | Target: {} 19 | Compiler: {} 20 | InstalledDir: {} 21 | cli.debug_mode: DEBUG MODE 22 | cli.arg.empty_input_file: input file path cannot be empty 23 | cli.arg.missing_arg: "missing argument for option '{}'" 24 | cli.arg.invalid_namespace: invalid namespace 25 | ir.op.immediate_left: the left operand cannot be an immediate value 26 | ir.op.memory_operands: "the operation cannot have both operands as memory locations" 27 | ir.op.empty_value: operand cannot be empty 28 | ir.op.out_of_range: immediate value out of range 29 | ir.op.invalid_number_format: invalid number format 30 | ir.op.scale_out_of_range: scale value out of range 31 | ir.op.invalid_ptr: invalid memory operand 32 | ir.value.register.unknown_register: unknown register '{}' 33 | ir.errors_generated: "{} errors generated" 34 | ir.invalid_op: "invalid instruction: '{}'" 35 | ir.invalid_pre_op: "invalid preprocessed instruction: '{}'" 36 | ir.invalid_pop: "pop could not pop '{}' no matching push" 37 | ir.invalid_label: label name cannot contain spaces 38 | ir.invalid_symbol: symbol name cannot contain spaces 39 | ir.invalid_local_label: invalid local label 40 | ir.invalid_local_symbol: invalid local symbol 41 | ir.invalid_label_identifier: expected a valid label identifier, but found '{}' 42 | ir.unknown_op: "unknown instruction: '{}'" 43 | ir.unknown_symbol: "unknown symbol: '{}'" 44 | ir.verify.unreachable: code will never be executed 45 | ir.verify.unreachable_before: unreachable code before this point 46 | ir.verify.reserved_label: use of reserved label prefix '{}' 47 | ir.verify.label_redefinition: redefinition of label '{}' 48 | ir.verify.symbol_redefinition: redefinition of symbol '{}' 49 | ir.verify.previous_definition: previous definition is here 50 | ir.verify.undeclared_label: use of undeclared label '{}' 51 | ir.verify.undeclared_symbol: use of undeclared symbol '{}' 52 | ir.verify.unused_label: label '{}' defined but not used 53 | ir.verify.unused_symbol: symbol '{}' defined but not used 54 | ir.verify.jmp_to_extern: jump to an extern label is undefined 55 | ir.verify.never_return: some control flow entry points lack a valid exit path 56 | ir.verify.never_return_inner: entry point is here 57 | file.no_such_file_or_directory: "no such file or directory: '{}'" 58 | file.failed_to_open: "failed to open file: '{}'" 59 | file.failed_to_create: "failed to create file or directory: '{}'" 60 | file.file_not_found: "'{}' file not found" 61 | oom.oom: "\033[31mfatal error:\033[97m out of memory" 62 | general.no_input_files: no input files 63 | general.invalid_input: invalid input file, expected '.mcasm' files 64 | general.failed_init_build: failed to init build directory 65 | general.unable_to_build: unable to build input file 66 | general.environment_error: "failed to verify runtime environment: missing required resources. Ensure all necessary files are present or reinstall the program" 67 | -------------------------------------------------------------------------------- /src/cpp/parse/ParseManager.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/3/22. 3 | // 4 | 5 | #include "ParseManager.h" 6 | #include "extern/PreProcessorAPI.h" 7 | #include "extern/ResourceManager.h" 8 | 9 | constexpr auto ERR_FMT = "error 0x{:x} while interacting with rust preprocesser"; 10 | 11 | [[noreturn]] static inline void handleError(PreProcessorC preprocessor, i32 err) { 12 | ClPreProcess_Free(preprocessor); 13 | #ifndef NDEBUG 14 | printStacktrace(ParseException(fmt::format(ERR_FMT, err))); 15 | #endif 16 | throw ParseException(fmt::format(ERR_FMT, err)); 17 | } 18 | 19 | void ParseManager::loadSource() { 20 | // 旧版parse 21 | // sources.reserve(config.getInput().size()); 22 | // for (const auto &path: config.getInput()) { 23 | // sources.emplace(path, readFile(path)); 24 | // } 25 | 26 | auto instance = ClPreProcess_New(); 27 | i32 err; 28 | 29 | if ((err = ClPreProcess_AddIncludeDir(instance, absolute(INCLUDE_PATH).string().c_str()))) { 30 | handleError(instance, err); 31 | } 32 | if ((err = ClPreProcess_AddIncludeDir(instance, 33 | Path(getExecutableDir(getArgv0())).string().c_str()))) { 34 | handleError(instance, err); 35 | } 36 | for (const auto &path: config.getIncludes()) { 37 | if ((err = ClPreProcess_AddIncludeDir(instance, absolute(path).string().c_str()))) { 38 | handleError(instance, err); 39 | } 40 | } 41 | 42 | for (const auto &path: config.getInput()) { 43 | if ((err = ClPreProcess_AddTarget(instance, absolute(path).string().c_str()))) { 44 | handleError(instance, err); 45 | } 46 | } 47 | 48 | if ((err = ClPreProcess_Load(instance))) { 49 | handleError(instance, err); 50 | } 51 | if ((err = ClPreProcess_Process(instance))) { 52 | handleError(instance, err); 53 | } 54 | 55 | Targets *targets; 56 | if ((err = ClPreProcess_GetTargets(instance, &targets))) { 57 | handleError(instance, err); 58 | } 59 | sources.reserve(targets->size); 60 | for (u32 i = 0; i < targets->size; ++i) { 61 | auto target = targets->targets[i]; 62 | sources.emplace(target->path, target->code); 63 | } 64 | ClPreProcess_FreeTargets(instance, targets); 65 | targets = nullptr; 66 | 67 | DefineMap *defineMap; 68 | if ((err = ClPreProcess_GetDefines(instance, &defineMap))) { 69 | handleError(instance, err); 70 | } 71 | defines.reserve(defineMap->size); 72 | for (u32 i = 0; i < defineMap->size; ++i) { 73 | auto cDefines = defineMap->defines[i]; 74 | auto map = HashMap(cDefines->size); 75 | for (u32 j = 0; j < cDefines->size; ++j) { 76 | auto define = cDefines->values[j]; 77 | map.emplace(define->key, define->value); 78 | } 79 | 80 | defines.emplace(cDefines->path, map); 81 | } 82 | ClPreProcess_FreeDefines(instance, defineMap); 83 | defineMap = nullptr; 84 | 85 | ClPreProcess_Free(instance); 86 | } 87 | 88 | void ParseManager::loadIR() { 89 | irs.reserve(sources.size()); 90 | for (auto &[path, code]: sources) { 91 | auto iter = defines.find(path); 92 | auto defines_ = iter == defines.end() ? HashMap() : iter->second; 93 | irs.emplace_back(logger, config, std::move(path), std::move(defines_)).parse(std::move(code)); 94 | } 95 | HashMap().swap(sources); 96 | HashMap>().swap(defines); 97 | } 98 | 99 | void ParseManager::freeSource() { 100 | for (auto &ir: irs) { 101 | HashMap().swap(ir.sourceMap); 102 | HashMap().swap(ir.lineStateMap); 103 | std::string().swap(ir.sourceCode); 104 | } 105 | } 106 | 107 | void ParseManager::freeIR() { 108 | std::vector().swap(irs); 109 | } 110 | -------------------------------------------------------------------------------- /src/cpp/utils/Native.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/24. 3 | // 4 | 5 | #include "Native.h" 6 | #include "stdio.h" 7 | #include "stdlib.h" 8 | #include "inttypes.h" 9 | 10 | static const char *const OOM_MSG = "\033[31mfatal error:\033[97m out of memory\n\033[31m致命错误:\033[97m 内存已耗尽"; 11 | static void *EMERGENCY_MEMORY = NULL; 12 | 13 | void initNative() { 14 | EMERGENCY_MEMORY = malloc(128); 15 | } 16 | 17 | void onOOM() { 18 | free(EMERGENCY_MEMORY); 19 | EMERGENCY_MEMORY = NULL; 20 | 21 | fputs(OOM_MSG, stderr); 22 | fflush(stderr); 23 | _exit(1); 24 | } 25 | 26 | #if defined(_WIN32) 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | void printStacktraceMsg(const char *err) { 33 | HANDLE process = GetCurrentProcess(); 34 | HANDLE thread = GetCurrentThread(); 35 | 36 | CONTEXT context; 37 | RtlCaptureContext(&context); // 获取当前CPU上下文 38 | 39 | // 初始化调试帮助库 40 | SymInitialize(process, NULL, TRUE); 41 | 42 | STACKFRAME64 stackFrame; 43 | memset(&stackFrame, 0, sizeof(STACKFRAME64)); 44 | 45 | DWORD machineType; 46 | #ifdef _M_IX86 47 | machineType = IMAGE_FILE_MACHINE_I386; 48 | stackFrame.AddrPC.Offset = context.Eip; 49 | stackFrame.AddrPC.Mode = AddrModeFlat; 50 | stackFrame.AddrFrame.Offset = context.Ebp; 51 | stackFrame.AddrFrame.Mode = AddrModeFlat; 52 | stackFrame.AddrStack.Offset = context.Esp; 53 | stackFrame.AddrStack.Mode = AddrModeFlat; 54 | #elif _M_X64 55 | machineType = IMAGE_FILE_MACHINE_AMD64; 56 | stackFrame.AddrPC.Offset = context.Rip; 57 | stackFrame.AddrPC.Mode = AddrModeFlat; 58 | stackFrame.AddrFrame.Offset = context.Rbp; 59 | stackFrame.AddrFrame.Mode = AddrModeFlat; 60 | stackFrame.AddrStack.Offset = context.Rsp; 61 | stackFrame.AddrStack.Mode = AddrModeFlat; 62 | #endif 63 | 64 | if (err == NULL) { 65 | fprintf(stderr, "Exception in thread \"0x%" PRIxPTR "\" with an unknown exception.\n", (uintptr_t) thread); 66 | } else { 67 | fprintf(stderr, "Exception in thread \"0x%" PRIxPTR "\": %s\n", (uintptr_t) thread, err); 68 | } 69 | 70 | size_t unknownCount = 0; 71 | while (StackWalk64(machineType, process, thread, &stackFrame, &context, NULL, SymFunctionTableAccess64, 72 | SymGetModuleBase64, NULL)) { 73 | DWORD64 address = stackFrame.AddrPC.Offset; 74 | if (address == 0) break; 75 | 76 | // 获取函数名 77 | char symbolBuffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]; 78 | SYMBOL_INFO *symbol = (SYMBOL_INFO *) symbolBuffer; 79 | symbol->SizeOfStruct = sizeof(SYMBOL_INFO); 80 | symbol->MaxNameLen = MAX_SYM_NAME; 81 | 82 | if (SymFromAddr(process, address, NULL, symbol)) { 83 | if (unknownCount > 0) { 84 | fprintf(stderr, " Suppressed %zu unknown stack traces.\n", unknownCount); 85 | unknownCount = 0; 86 | } 87 | fprintf(stderr, " at %s", symbol->Name); 88 | } else { 89 | unknownCount++; 90 | continue; 91 | } 92 | 93 | // 获取文件名和行号 94 | IMAGEHLP_LINE64 line; 95 | DWORD displacement; 96 | memset(&line, 0, sizeof(IMAGEHLP_LINE64)); 97 | line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); 98 | 99 | if (SymGetLineFromAddr64(process, address, &displacement, &line)) { 100 | fprintf(stderr, "(0x%" PRIxPTR ") (%s:%lu)\n", line.Address, line.FileName, line.LineNumber); 101 | } else { 102 | fprintf(stderr, "(Unknown Source)\n"); 103 | } 104 | } 105 | 106 | SymCleanup(process); 107 | } 108 | 109 | void printStacktrace() { 110 | printStacktraceMsg(NULL); 111 | } 112 | 113 | void onTerminate() { 114 | printStacktrace(); 115 | } 116 | #else 117 | void onTerminate() { 118 | puts("Exception in thread unknown with an unknown exception."); 119 | } 120 | #endif 121 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | English | [简体中文](./README_CN.md) 4 | 5 | logo 6 | 7 | # clang-mc 8 | 9 | ## A Development Toolchain for Minecraft Datapacks 10 | 11 | [![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE) 12 | [![Issues](https://img.shields.io/github/issues/xia-mc/clang-mc)](https://github.com/xia-mc/clang-mc/issues) 13 | ![Total lines](https://tokei.rs/b1/github/xia-mc/clang-mc?style=flat) 14 | ![Version](https://img.shields.io/badge/Minecraft-1.21_and_later-blue) 15 | 16 |
17 | 18 | > [!NOTE] 19 | > `clang-mc` is in the early stages of development, and many features are not yet complete. Contributions and feedback are welcome! 20 | 21 | ## Introduction 22 | 23 | `clang-mc` is a **compiler toolchain** designed specifically for **Minecraft datapack development**. It aims to provide a more efficient and maintainable development environment while offering a standard library to reduce redundant implementations of common functionalities. 24 | 25 | Minecraft datapack development has long suffered from **poor readability, maintenance challenges, and functional limitations**. For example: 26 | - **Lack of modern programming language features**: String manipulation, floating-point arithmetic, and large integer operations are extremely difficult to implement. 27 | - **Disorganized code structure**: Datapacks often rely on a large number of manually written commands, and many requirements must be indirectly implemented using multiple `.mcfunction` files, making reuse and expansion difficult. 28 | 29 | This project seeks to address these issues to some extent. 30 | 31 | Check out [Wiki](https://github.com/xia-mc/clang-mc/wiki) to learn more. 32 | 33 | --- 34 | 35 | ## Roadmap 36 | 37 | - [x] **Basic implementation of the `stdlib` standard library**, establishing a low-overhead virtual machine model on `mcfunction`. 38 | - [x] **Write a wiki** to define calling conventions, register conventions, stack conventions, etc. 39 | - [x] **Implement an assembler (IR)** to translate IR into `mcfunction` code based on `stdlib`. 40 | - [x] **Develop a parser** to convert assembly code into IR objects. 41 | - [x] **Create a showcase** to demonstrate the advantages of this project and how it improves datapack readability while reducing redundant implementations. 42 | - [ ] **Enhance `stdlib`** with more advanced abstractions. 43 | - [ ] **(Long-term goal) Implement an LLVM backend** to generate assembly code (IR). 44 | - [ ] **(Long-term goal) Achieve compatibility with C/C++/Rust ecosystems**, leveraging LLVM to extend the capabilities of `mcfunction`. 45 | 46 | --- 47 | 48 | ## Contributions 49 | 50 | Contributions are welcome! Feel free to submit issues or pull requests. 51 | Please note that this project is still in its early stages, and we greatly appreciate any feedback or suggestions. 52 | 53 | --- 54 | 55 | ## Acknowledgments 56 | 57 | Without these projects, `clang-mc` would not exist: 58 | 59 | - [Minecraft](https://www.minecraft.net) - Minecraft, developed by Mojang Studios, `clang-mc` complies with the [Minecraft EULA](https://www.minecraft.net/en-us/eula) and related terms of use. 60 | - [LLVM](https://llvm.org) - A powerful compiler infrastructure, open-sourced under the Apache License 2.0. 61 | - [ankerl::unordered_dense](https://github.com/martinus/unordered_dense): A modern, high-performance, low-memory hash table implementation in C++, licensed under the [MIT License](https://github.com/martinus/unordered_dense/blob/main/LICENSE). 62 | - [fmt](https://fmt.dev/) - A fast and safe formatting library for C++, licensed under the [MIT License](https://github.com/fmtlib/fmt/blob/master/LICENSE.rst). 63 | - [spdlog](https://github.com/gabime/spdlog) - A fast C++ logging library, licensed under the [MIT License](https://github.com/gabime/spdlog/blob/v1.x/LICENSE). 64 | - [yaml-cpp](https://github.com/jbeder/yaml-cpp) - A YAML parser and emitter for C++, licensed under the [MIT License](https://github.com/jbeder/yaml-cpp/blob/master/LICENSE). 65 | 66 | Special thanks to all contributors in the open-source community! 67 | -------------------------------------------------------------------------------- /src/cpp/config/ArgParser.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/13. 3 | // 4 | 5 | #include "ArgParser.h" 6 | #include "i18n/I18n.h" 7 | 8 | ArgParser::ArgParser(Config &config) : config(config) { 9 | this->config.setInput(std::vector()); 10 | } 11 | 12 | void ArgParser::setNameSpace(const std::string &arg) { 13 | switch (string::count(arg, ':')) { 14 | case 0: 15 | if (!string::isValidMCNamespace(arg)) { 16 | throw ParseException(i18n("cli.arg.invalid_namespace")); 17 | } 18 | config.setNameSpace(arg + ':'); 19 | break; 20 | case 1: 21 | if (!string::isValidMCNamespace(string::removeFromFirst(arg, ":"))) { 22 | throw ParseException(i18n("cli.arg.invalid_namespace")); 23 | } 24 | assert(arg.length() > 1); 25 | if (arg[arg.length() - 1] != '/' && arg[arg.length() - 1] != ':') { 26 | config.setNameSpace(arg + '/'); 27 | } else { 28 | config.setNameSpace(arg); 29 | } 30 | break; 31 | default: 32 | throw ParseException(i18n("cli.arg.invalid_namespace")); 33 | } 34 | } 35 | 36 | void ArgParser::next(const std::string &arg) { 37 | if (required) { 38 | SWITCH_STR (lastString) { 39 | CASE_STR("--output"): 40 | CASE_STR("-o"): 41 | // 设置输出数据包zip的文件名 42 | config.setOutput(Path(arg)); 43 | break; 44 | CASE_STR("--build-dir"): 45 | CASE_STR("-B"): 46 | // 设置构建文件夹 47 | config.setBuildDir(Path(arg)); 48 | break; 49 | CASE_STR("--namespace"): 50 | CASE_STR("-N"): 51 | // 设置编译非导出函数的命名空间路径 52 | setNameSpace(arg); 53 | break; 54 | CASE_STR("-I"): 55 | // 设置include path 56 | config.getIncludes().emplace_back(arg); 57 | break; 58 | CASE_STR("--data-dir"): 59 | CASE_STR("-D"): 60 | // 设置数据包目录 61 | config.setDataDir(Path(arg)); 62 | break; 63 | } 64 | required = false; 65 | lastString = ""; 66 | return; 67 | } 68 | 69 | const Hash argHash = hash(arg); 70 | if (DATA_ARGS.contains(argHash)) { 71 | required = true; 72 | lastString = arg; 73 | return; 74 | } 75 | 76 | switch (argHash) { 77 | CASE_STR("--compile-only"): 78 | CASE_STR("-c"): 79 | // 只编译,不链接 80 | config.setCompileOnly(true); 81 | return; 82 | CASE_STR("--log-file"): 83 | CASE_STR("-l"): 84 | // 输出日志到文件 85 | config.setLogFile(true); 86 | return; 87 | CASE_STR("-g"): 88 | // 额外的调试信息 89 | config.setDebugInfo(true); 90 | return; 91 | CASE_STR("-Werror"): 92 | // 把警告视为错误 93 | config.setWerror(true); 94 | return; 95 | CASE_STR("-E"): 96 | // 只预处理,不编译 97 | config.setPreprocessOnly(true); 98 | return; 99 | CASE_STR("-w"): 100 | // 抑制所有警告 101 | config.setNoWarn(true); 102 | return; 103 | default: 104 | break; 105 | } 106 | 107 | // 认为是输入文件 108 | if (arg.empty()) { 109 | throw ParseException(i18n("cli.arg.empty_input_file")); 110 | } 111 | if (arg.length() >= 2 && (arg.front() == '"' || arg.front() == '\'') 112 | && (arg.back() == '"' || arg.back() == '\'')) { 113 | config.getInput().emplace_back(arg.substr(1, arg.length() - 2)); 114 | return; 115 | } 116 | config.getInput().emplace_back(arg); 117 | } 118 | 119 | void ArgParser::end() { 120 | if (required) { 121 | throw ParseException(i18n("cli.arg.missing_arg") + lastString); 122 | } 123 | if (config.getNameSpace().empty()) { 124 | setNameSpace(config.getOutput().string()); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/cpp/ir/ops/Je.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/27. 3 | // 4 | 5 | #ifndef CLANG_MC_JE_H 6 | #define CLANG_MC_JE_H 7 | 8 | #include "ir/iops/Op.h" 9 | #include "ir/iops/JmpLike.h" 10 | #include "ir/iops/CmpLike.h" 11 | #include "utils/string/StringUtils.h" 12 | #include "ir/OpCommon.h" 13 | #include "utils/string/StringBuilder.h" 14 | 15 | class Je : public JmpLike, public CmpLike { 16 | private: 17 | static inline std::string cmp(const std::string_view &command, Register *left, Register *right) { 18 | return fmt::format("execute if score {} vm_regs = {} vm_regs run return run {}", 19 | left->getName(), right->getName(), command); 20 | } 21 | 22 | static inline std::string cmp(const std::string_view &command, Register *left, Immediate *right) { 23 | return fmt::format("execute if score {} vm_regs matches {} run return run {}", 24 | left->getName(), static_cast(right->getValue()), command); 25 | } 26 | 27 | static inline std::string cmp(const std::string_view &command, Register *left, Ptr *right) { 28 | return fmt::format("{}\n{}", 29 | right->loadTo(*Registers::S1), 30 | cmp(command, left, Registers::S1.get())); 31 | } 32 | 33 | static inline std::string cmp(const std::string_view &command, Immediate *left, Ptr *right) { 34 | return fmt::format("{}\nexecute if score s1 vm_regs matches {} run return run {}", 35 | right->loadTo(*Registers::S1), static_cast(left->getValue()), command); 36 | } 37 | 38 | template 39 | inline std::string cmp(const JmpMap &jmpMap, T *left, U *right) const { 40 | return CmpLike::cmp(this, jmpMap.at(labelHash), left, right); 41 | } 42 | 43 | friend class CmpLike; 44 | public: 45 | explicit Je(const i32 lineNumber, ValuePtr &&left, ValuePtr &&right, std::string label) : 46 | Op("je", lineNumber), CallLike(std::move(label)), CmpLike(std::move(left), std::move(right)) { 47 | } 48 | 49 | void withIR(IR *context) override { 50 | CmpLike::withIR(context); 51 | if (INSTANCEOF_SHARED(this->left, Ptr) && INSTANCEOF_SHARED(this->right, Ptr)) { 52 | throw ParseException(i18n("ir.op.memory_operands")); 53 | } 54 | } 55 | 56 | [[nodiscard]] std::string toString() const noexcept override { 57 | return fmt::format("je {}, {}, {}", left->toString(), right->toString(), label); 58 | } 59 | 60 | [[nodiscard]] std::string compile() const override { 61 | return JmpLike::compile(); 62 | } 63 | 64 | [[nodiscard]] std::string compile(const JmpMap &jmpMap) const override { 65 | assert(jmpMap.contains(labelHash)); 66 | 67 | if (const auto &left = INSTANCEOF_SHARED(this->left, Register)) { 68 | if (const auto &right = INSTANCEOF_SHARED(this->right, Register)) { 69 | return cmp(jmpMap, left.get(), right.get()); 70 | } 71 | if (const auto &right = INSTANCEOF_SHARED(this->right, Immediate)) { 72 | return cmp(jmpMap, left.get(), right.get()); 73 | } 74 | assert(INSTANCEOF_SHARED(this->right, Ptr)); 75 | return cmp(jmpMap, left.get(), CAST_FAST(this->right, Ptr)); 76 | } 77 | if (const auto &left = INSTANCEOF_SHARED(this->left, Immediate)) { 78 | if (const auto &right = INSTANCEOF_SHARED(this->right, Register)) { 79 | return cmp(jmpMap, right.get(), left.get()); 80 | } 81 | if (const auto &right = INSTANCEOF_SHARED(this->right, Immediate)) { 82 | // 两个立即数比较直接编译时计算掉。因为mcfunction原生不支持比较两个立即数。多一条存储到寄存器就因噎废食了。 83 | if (left->getValue() != right->getValue()) return ""; 84 | return string::join(jmpMap.at(labelHash), '\n'); 85 | } 86 | assert(INSTANCEOF_SHARED(this->right, Ptr)); 87 | return cmp(jmpMap, left.get(), CAST_FAST(this->right, Ptr)); 88 | } 89 | assert(INSTANCEOF_SHARED(this->left, Ptr)); 90 | const auto &left = CAST_FAST(this->left, Ptr); 91 | if (const auto &right = INSTANCEOF_SHARED(this->right, Register)) { 92 | return cmp(jmpMap, right.get(), left); 93 | } 94 | assert(INSTANCEOF_SHARED(this->right, Immediate)); 95 | return cmp(jmpMap, CAST_FAST(this->right, Immediate), left); 96 | } 97 | }; 98 | 99 | #endif //CLANG_MC_JE_H 100 | -------------------------------------------------------------------------------- /src/cpp/ir/ops/Jg.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/27. 3 | // 4 | 5 | #ifndef CLANG_MC_JG_H 6 | #define CLANG_MC_JG_H 7 | 8 | #include "ir/iops/Op.h" 9 | #include "ir/iops/JmpLike.h" 10 | #include "ir/iops/CmpLike.h" 11 | #include "utils/string/StringUtils.h" 12 | #include "ir/OpCommon.h" 13 | #include "utils/string/StringBuilder.h" 14 | 15 | class Jg : public JmpLike, public CmpLike { 16 | private: 17 | static inline std::string cmp(const std::string_view &command, Register *left, Register *right) { 18 | return fmt::format("execute if score {} vm_regs > {} vm_regs run return run {}", 19 | left->getName(), right->getName(), command); 20 | } 21 | 22 | static inline std::string cmp(const std::string_view &command, Register *left, Immediate *right) { 23 | if (!right->getValue().checkAdd(1)) { 24 | return ""; 25 | } 26 | return fmt::format("execute if score {} vm_regs matches {}.. run return run {}", 27 | left->getName(), right->getValue() + 1, command); 28 | } 29 | 30 | static inline std::string cmp(const std::string_view &command, Register *left, Ptr *right) { 31 | return fmt::format("{}\n{}", 32 | right->loadTo(*Registers::S1), 33 | cmp(command, left, Registers::S1.get())); 34 | } 35 | 36 | static inline std::string cmp(const std::string_view &command, Immediate *left, Ptr *right) { 37 | return fmt::format("{}\nexecute if score s1 vm_regs matches ..{} run return run {}", 38 | right->loadTo(*Registers::S1), static_cast(left->getValue()), command); 39 | } 40 | 41 | template 42 | static inline std::string cmp(const std::string_view &command, Ptr *left, T *right) { 43 | return fmt::format("{}\n{}", 44 | left->loadTo(*Registers::S1), 45 | cmp(command, Registers::S1.get(), right)); 46 | } 47 | 48 | template 49 | inline std::string cmp(const JmpMap &jmpMap, T *left, U *right) const { 50 | return CmpLike::cmp(this, jmpMap.at(labelHash), left, right); 51 | } 52 | 53 | friend class CmpLike; 54 | public: 55 | explicit Jg(const i32 lineNumber, ValuePtr &&left, ValuePtr &&right, std::string label) : 56 | Op("jg", lineNumber), CallLike(std::move(label)), CmpLike(std::move(left), std::move(right)) { 57 | } 58 | 59 | void withIR(IR *context) override { 60 | CmpLike::withIR(context); 61 | if (INSTANCEOF_SHARED(this->left, Ptr) && INSTANCEOF_SHARED(this->right, Ptr)) { 62 | throw ParseException(i18n("ir.op.memory_operands")); 63 | } 64 | } 65 | 66 | [[nodiscard]] std::string toString() const noexcept override { 67 | return fmt::format("jg {}, {}, {}", left->toString(), right->toString(), label); 68 | } 69 | 70 | [[nodiscard]] std::string compile() const override { 71 | return JmpLike::compile(); 72 | } 73 | 74 | [[nodiscard]] std::string compile(const JmpMap &jmpMap) const override { 75 | assert(jmpMap.contains(labelHash)); 76 | 77 | if (const auto &left = INSTANCEOF_SHARED(this->left, Register)) { 78 | if (const auto &right = INSTANCEOF_SHARED(this->right, Register)) { 79 | return cmp(jmpMap, left.get(), right.get()); 80 | } 81 | if (const auto &right = INSTANCEOF_SHARED(this->right, Immediate)) { 82 | return cmp(jmpMap, left.get(), right.get()); 83 | } 84 | assert(INSTANCEOF_SHARED(this->right, Ptr)); 85 | return cmp(jmpMap, left.get(), CAST_FAST(this->right, Ptr)); 86 | } 87 | if (const auto &left = INSTANCEOF_SHARED(this->left, Immediate)) { 88 | if (const auto &right = INSTANCEOF_SHARED(this->right, Register)) { 89 | return cmp(jmpMap, right.get(), left.get()); 90 | } 91 | if (const auto &right = INSTANCEOF_SHARED(this->right, Immediate)) { 92 | // 两个立即数比较直接编译时计算掉。因为mcfunction原生不支持比较两个立即数。多一条存储到寄存器就因噎废食了。 93 | if (left->getValue() != right->getValue()) return ""; 94 | return string::join(jmpMap.at(labelHash), '\n'); 95 | } 96 | assert(INSTANCEOF_SHARED(this->right, Ptr)); 97 | return cmp(jmpMap, left.get(), CAST_FAST(this->right, Ptr)); 98 | } 99 | assert(INSTANCEOF_SHARED(this->left, Ptr)); 100 | const auto &left = CAST_FAST(this->left, Ptr); 101 | if (const auto &right = INSTANCEOF_SHARED(this->right, Register)) { 102 | return cmp(jmpMap, left, right.get()); 103 | } 104 | assert(INSTANCEOF_SHARED(this->right, Immediate)); 105 | return cmp(jmpMap, left, CAST_FAST(this->right, Immediate)); 106 | } 107 | }; 108 | 109 | #endif //CLANG_MC_JG_H 110 | -------------------------------------------------------------------------------- /src/cpp/ir/ops/Jl.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/27. 3 | // 4 | 5 | #ifndef CLANG_MC_JL_H 6 | #define CLANG_MC_JL_H 7 | 8 | #include "ir/iops/Op.h" 9 | #include "ir/iops/JmpLike.h" 10 | #include "ir/iops/CmpLike.h" 11 | #include "utils/string/StringUtils.h" 12 | #include "ir/OpCommon.h" 13 | #include "utils/string/StringBuilder.h" 14 | 15 | class Jl : public JmpLike, public CmpLike { 16 | private: 17 | static inline std::string cmp(const std::string_view &command, Register *left, Register *right) { 18 | return fmt::format("execute if score {} vm_regs < {} vm_regs run return run {}", 19 | left->getName(), right->getName(), command); 20 | } 21 | 22 | static inline std::string cmp(const std::string_view &command, Register *left, Immediate *right) { 23 | if (!right->getValue().checkSub(1)) { 24 | return ""; 25 | } 26 | return fmt::format("execute if score {} vm_regs matches ..{} run return run {}", 27 | left->getName(), right->getValue() - 1, command); 28 | } 29 | 30 | static inline std::string cmp(const std::string_view &command, Register *left, Ptr *right) { 31 | return fmt::format("{}\n{}", 32 | right->loadTo(*Registers::S1), 33 | cmp(command, left, Registers::S1.get())); 34 | } 35 | 36 | static inline std::string cmp(const std::string_view &command, Immediate *left, Ptr *right) { 37 | return fmt::format("{}\nexecute if score s1 vm_regs matches {}.. run return run {}", 38 | right->loadTo(*Registers::S1), static_cast(left->getValue()), command); 39 | } 40 | 41 | template 42 | static inline std::string cmp(const std::string_view &command, Ptr *left, T *right) { 43 | return fmt::format("{}\n{}", 44 | left->loadTo(*Registers::S1), 45 | cmp(command, Registers::S1.get(), right)); 46 | } 47 | 48 | template 49 | inline std::string cmp(const JmpMap &jmpMap, T *left, U *right) const { 50 | return CmpLike::cmp(this, jmpMap.at(labelHash), left, right); 51 | } 52 | 53 | friend class CmpLike; 54 | public: 55 | explicit Jl(const i32 lineNumber, ValuePtr &&left, ValuePtr &&right, std::string label) : 56 | Op("jl", lineNumber), CallLike(std::move(label)), CmpLike(std::move(left), std::move(right)) { 57 | } 58 | 59 | void withIR(IR *context) override { 60 | CmpLike::withIR(context); 61 | if (INSTANCEOF_SHARED(this->left, Ptr) && INSTANCEOF_SHARED(this->right, Ptr)) { 62 | throw ParseException(i18n("ir.op.memory_operands")); 63 | } 64 | } 65 | 66 | [[nodiscard]] std::string toString() const noexcept override { 67 | return fmt::format("jl {}, {}, {}", left->toString(), right->toString(), label); 68 | } 69 | 70 | [[nodiscard]] std::string compile() const override { 71 | return JmpLike::compile(); 72 | } 73 | 74 | [[nodiscard]] std::string compile(const JmpMap &jmpMap) const override { 75 | assert(jmpMap.contains(labelHash)); 76 | 77 | if (const auto &left = INSTANCEOF_SHARED(this->left, Register)) { 78 | if (const auto &right = INSTANCEOF_SHARED(this->right, Register)) { 79 | return cmp(jmpMap, left.get(), right.get()); 80 | } 81 | if (const auto &right = INSTANCEOF_SHARED(this->right, Immediate)) { 82 | return cmp(jmpMap, left.get(), right.get()); 83 | } 84 | assert(INSTANCEOF_SHARED(this->right, Ptr)); 85 | return cmp(jmpMap, left.get(), CAST_FAST(this->right, Ptr)); 86 | } 87 | if (const auto &left = INSTANCEOF_SHARED(this->left, Immediate)) { 88 | if (const auto &right = INSTANCEOF_SHARED(this->right, Register)) { 89 | return cmp(jmpMap, right.get(), left.get()); 90 | } 91 | if (const auto &right = INSTANCEOF_SHARED(this->right, Immediate)) { 92 | // 两个立即数比较直接编译时计算掉。因为mcfunction原生不支持比较两个立即数。多一条存储到寄存器就因噎废食了。 93 | if (left->getValue() != right->getValue()) return ""; 94 | return string::join(jmpMap.at(labelHash), '\n'); 95 | } 96 | assert(INSTANCEOF_SHARED(this->right, Ptr)); 97 | return cmp(jmpMap, left.get(), CAST_FAST(this->right, Ptr)); 98 | } 99 | assert(INSTANCEOF_SHARED(this->left, Ptr)); 100 | const auto &left = CAST_FAST(this->left, Ptr); 101 | if (const auto &right = INSTANCEOF_SHARED(this->right, Register)) { 102 | return cmp(jmpMap, left, right.get()); 103 | } 104 | assert(INSTANCEOF_SHARED(this->right, Immediate)); 105 | return cmp(jmpMap, left, CAST_FAST(this->right, Immediate)); 106 | } 107 | }; 108 | 109 | #endif //CLANG_MC_JL_H 110 | -------------------------------------------------------------------------------- /src/cpp/ir/ops/Jge.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/27. 3 | // 4 | 5 | #ifndef CLANG_MC_JGE_H 6 | #define CLANG_MC_JGE_H 7 | 8 | #include "ir/iops/Op.h" 9 | #include "ir/iops/JmpLike.h" 10 | #include "ir/iops/CmpLike.h" 11 | #include "utils/string/StringUtils.h" 12 | #include "ir/OpCommon.h" 13 | #include "utils/string/StringBuilder.h" 14 | 15 | class Jge : public JmpLike, public CmpLike { 16 | private: 17 | static inline std::string cmp(const std::string_view &command, Register *left, Register *right) { 18 | return fmt::format("execute if score {} vm_regs >= {} vm_regs run return run {}", 19 | left->getName(), right->getName(), command); 20 | } 21 | 22 | static inline std::string cmp(const std::string_view &command, Register *left, Immediate *right) { 23 | return fmt::format("execute if score {} vm_regs matches {}.. run return run {}", 24 | left->getName(), static_cast(right->getValue()), command); 25 | } 26 | 27 | static inline std::string cmp(const std::string_view &command, Register *left, Ptr *right) { 28 | return fmt::format("{}\n{}", 29 | right->loadTo(*Registers::S1), 30 | cmp(command, left, Registers::S1.get())); 31 | } 32 | 33 | static inline std::string cmp(const std::string_view &command, Immediate *left, Ptr *right) { 34 | if (!left->getValue().checkSub(1)) { 35 | return ""; 36 | } 37 | return fmt::format("{}\nexecute if score s1 vm_regs matches ..{} run return run {}", 38 | right->loadTo(*Registers::S1), left->getValue() - 1, command); 39 | } 40 | 41 | template 42 | static inline std::string cmp(const std::string_view &command, Ptr *left, T *right) { 43 | return fmt::format("{}\n{}", 44 | left->loadTo(*Registers::S1), 45 | cmp(command, Registers::S1.get(), right)); 46 | } 47 | 48 | template 49 | inline std::string cmp(const JmpMap &jmpMap, T *left, U *right) const { 50 | return CmpLike::cmp(this, jmpMap.at(labelHash), left, right); 51 | } 52 | 53 | friend class CmpLike; 54 | public: 55 | explicit Jge(const i32 lineNumber, ValuePtr &&left, ValuePtr &&right, std::string label) : 56 | Op("jge", lineNumber), CallLike(std::move(label)), CmpLike(std::move(left), std::move(right)) { 57 | } 58 | 59 | void withIR(IR *context) override { 60 | CmpLike::withIR(context); 61 | if (INSTANCEOF_SHARED(this->left, Ptr) && INSTANCEOF_SHARED(this->right, Ptr)) { 62 | throw ParseException(i18n("ir.op.memory_operands")); 63 | } 64 | } 65 | 66 | [[nodiscard]] std::string toString() const noexcept override { 67 | return fmt::format("jge {}, {}, {}", left->toString(), right->toString(), label); 68 | } 69 | 70 | [[nodiscard]] std::string compile() const override { 71 | return JmpLike::compile(); 72 | } 73 | 74 | [[nodiscard]] std::string compile(const JmpMap &jmpMap) const override { 75 | assert(jmpMap.contains(labelHash)); 76 | 77 | if (const auto &left = INSTANCEOF_SHARED(this->left, Register)) { 78 | if (const auto &right = INSTANCEOF_SHARED(this->right, Register)) { 79 | return cmp(jmpMap, left.get(), right.get()); 80 | } 81 | if (const auto &right = INSTANCEOF_SHARED(this->right, Immediate)) { 82 | return cmp(jmpMap, left.get(), right.get()); 83 | } 84 | assert(INSTANCEOF_SHARED(this->right, Ptr)); 85 | return cmp(jmpMap, left.get(), CAST_FAST(this->right, Ptr)); 86 | } 87 | if (const auto &left = INSTANCEOF_SHARED(this->left, Immediate)) { 88 | if (const auto &right = INSTANCEOF_SHARED(this->right, Register)) { 89 | return cmp(jmpMap, right.get(), left.get()); 90 | } 91 | if (const auto &right = INSTANCEOF_SHARED(this->right, Immediate)) { 92 | // 两个立即数比较直接编译时计算掉。因为mcfunction原生不支持比较两个立即数。多一条存储到寄存器就因噎废食了。 93 | if (left->getValue() != right->getValue()) return ""; 94 | return string::join(jmpMap.at(labelHash), '\n'); 95 | } 96 | assert(INSTANCEOF_SHARED(this->right, Ptr)); 97 | return cmp(jmpMap, left.get(), CAST_FAST(this->right, Ptr)); 98 | } 99 | assert(INSTANCEOF_SHARED(this->left, Ptr)); 100 | const auto &left = CAST_FAST(this->left, Ptr); 101 | if (const auto &right = INSTANCEOF_SHARED(this->right, Register)) { 102 | return cmp(jmpMap, left, right.get()); 103 | } 104 | assert(INSTANCEOF_SHARED(this->right, Immediate)); 105 | return cmp(jmpMap, left, CAST_FAST(this->right, Immediate)); 106 | } 107 | }; 108 | 109 | #endif //CLANG_MC_JGE_H 110 | -------------------------------------------------------------------------------- /src/cpp/ir/ops/Jle.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/27. 3 | // 4 | 5 | #ifndef CLANG_MC_JLE_H 6 | #define CLANG_MC_JLE_H 7 | 8 | #include "ir/iops/Op.h" 9 | #include "ir/iops/JmpLike.h" 10 | #include "ir/iops/CmpLike.h" 11 | #include "utils/string/StringUtils.h" 12 | #include "ir/OpCommon.h" 13 | #include "utils/string/StringBuilder.h" 14 | 15 | class Jle : public JmpLike, public CmpLike { 16 | private: 17 | static inline std::string cmp(const std::string_view &command, Register *left, Register *right) { 18 | return fmt::format("execute if score {} vm_regs <= {} vm_regs run return run {}", 19 | left->getName(), right->getName(), command); 20 | } 21 | 22 | static inline std::string cmp(const std::string_view &command, Register *left, Immediate *right) { 23 | return fmt::format("execute if score {} vm_regs matches ..{} run return run {}", 24 | left->getName(), static_cast(right->getValue()), command); 25 | } 26 | 27 | static inline std::string cmp(const std::string_view &command, Register *left, Ptr *right) { 28 | return fmt::format("{}\n{}", 29 | right->loadTo(*Registers::S1), 30 | cmp(command, left, Registers::S1.get())); 31 | } 32 | 33 | static inline std::string cmp(const std::string_view &command, Immediate *left, Ptr *right) { 34 | if (!left->getValue().checkAdd(1)) { 35 | return ""; 36 | } 37 | return fmt::format("{}\nexecute if score s1 vm_regs matches {}.. run return run {}", 38 | right->loadTo(*Registers::S1), left->getValue() + 1, command); 39 | } 40 | 41 | template 42 | static inline std::string cmp(const std::string_view &command, Ptr *left, T *right) { 43 | return fmt::format("{}\n{}", 44 | left->loadTo(*Registers::S1), 45 | cmp(command, Registers::S1.get(), right)); 46 | } 47 | 48 | template 49 | inline std::string cmp(const JmpMap &jmpMap, T *left, U *right) const { 50 | return CmpLike::cmp(this, jmpMap.at(labelHash), left, right); 51 | } 52 | 53 | friend class CmpLike; 54 | public: 55 | explicit Jle(const i32 lineNumber, ValuePtr &&left, ValuePtr &&right, std::string label) : 56 | Op("jle", lineNumber), CallLike(std::move(label)), CmpLike(std::move(left), std::move(right)) { 57 | } 58 | 59 | void withIR(IR *context) override { 60 | CmpLike::withIR(context); 61 | if (INSTANCEOF_SHARED(this->left, Ptr) && INSTANCEOF_SHARED(this->right, Ptr)) { 62 | throw ParseException(i18n("ir.op.memory_operands")); 63 | } 64 | } 65 | 66 | [[nodiscard]] std::string toString() const noexcept override { 67 | return fmt::format("jle {}, {}, {}", left->toString(), right->toString(), label); 68 | } 69 | 70 | [[nodiscard]] std::string compile() const override { 71 | return JmpLike::compile(); 72 | } 73 | 74 | [[nodiscard]] std::string compile(const JmpMap &jmpMap) const override { 75 | assert(jmpMap.contains(labelHash)); 76 | 77 | if (const auto &left = INSTANCEOF_SHARED(this->left, Register)) { 78 | if (const auto &right = INSTANCEOF_SHARED(this->right, Register)) { 79 | return cmp(jmpMap, left.get(), right.get()); 80 | } 81 | if (const auto &right = INSTANCEOF_SHARED(this->right, Immediate)) { 82 | return cmp(jmpMap, left.get(), right.get()); 83 | } 84 | assert(INSTANCEOF_SHARED(this->right, Ptr)); 85 | return cmp(jmpMap, left.get(), CAST_FAST(this->right, Ptr)); 86 | } 87 | if (const auto &left = INSTANCEOF_SHARED(this->left, Immediate)) { 88 | if (const auto &right = INSTANCEOF_SHARED(this->right, Register)) { 89 | return cmp(jmpMap, right.get(), left.get()); 90 | } 91 | if (const auto &right = INSTANCEOF_SHARED(this->right, Immediate)) { 92 | // 两个立即数比较直接编译时计算掉。因为mcfunction原生不支持比较两个立即数。多一条存储到寄存器就因噎废食了。 93 | if (left->getValue() != right->getValue()) return ""; 94 | return string::join(jmpMap.at(labelHash), '\n'); 95 | } 96 | assert(INSTANCEOF_SHARED(this->right, Ptr)); 97 | return cmp(jmpMap, left.get(), CAST_FAST(this->right, Ptr)); 98 | } 99 | assert(INSTANCEOF_SHARED(this->left, Ptr)); 100 | const auto &left = CAST_FAST(this->left, Ptr); 101 | if (const auto &right = INSTANCEOF_SHARED(this->right, Register)) { 102 | return cmp(jmpMap, left, right.get()); 103 | } 104 | assert(INSTANCEOF_SHARED(this->right, Immediate)); 105 | return cmp(jmpMap, left, CAST_FAST(this->right, Immediate)); 106 | } 107 | }; 108 | 109 | #endif //CLANG_MC_JLE_H 110 | -------------------------------------------------------------------------------- /src/cpp/ir/ops/Jne.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/27. 3 | // 4 | 5 | #ifndef CLANG_MC_JNE_H 6 | #define CLANG_MC_JNE_H 7 | 8 | #include "ir/iops/Op.h" 9 | #include "ir/iops/JmpLike.h" 10 | #include "ir/iops/CmpLike.h" 11 | #include "utils/string/StringUtils.h" 12 | #include "ir/OpCommon.h" 13 | #include "utils/string/StringBuilder.h" 14 | 15 | class Jne : public JmpLike, public CmpLike { 16 | private: 17 | static inline std::string cmp(const std::string_view &command, Register *left, Register *right) { 18 | return fmt::format("execute if score {} vm_regs > {} vm_regs run return run {}\nexecute if score {} vm_regs < {} vm_regs run return run {}", 19 | left->getName(), right->getName(), command, left->getName(), right->getName(), command); 20 | } 21 | 22 | static inline std::string cmp(const std::string_view &command, Register *left, Immediate *right) { 23 | if (!right->getValue().checkAdd(1)) { 24 | return fmt::format("execute if score {} vm_regs matches ..{} run return run {}", 25 | left->getName(), right->getValue() - 1, command); 26 | } 27 | if (!right->getValue().checkSub(1)) { 28 | return fmt::format("execute if score {} vm_regs matches {}.. run return run {}", 29 | left->getName(), right->getValue() + 1, command); 30 | } 31 | 32 | return fmt::format("execute if score {} vm_regs matches {}.. run return run {}\nexecute if score {} vm_regs matches ..{} run return run {}", 33 | left->getName(), right->getValue() + 1, command, left->getName(), right->getValue() - 1, command); 34 | } 35 | 36 | static inline std::string cmp(const std::string_view &command, Register *left, Ptr *right) { 37 | return fmt::format("{}\n{}", 38 | right->loadTo(*Registers::S1), 39 | cmp(command, left, Registers::S1.get())); 40 | } 41 | 42 | static inline std::string cmp(const std::string_view &command, Immediate *left, Ptr *right) { 43 | return fmt::format("{}\n{}", 44 | right->loadTo(*Registers::S1), cmp(command, Registers::S1.get(), left)); 45 | } 46 | 47 | template 48 | inline std::string cmp(const JmpMap &jmpMap, T *left, U *right) const { 49 | return CmpLike::cmp(this, jmpMap.at(labelHash), left, right); 50 | } 51 | 52 | friend class CmpLike; 53 | public: 54 | explicit Jne(const i32 lineNumber, ValuePtr &&left, ValuePtr &&right, std::string label) : 55 | Op("jne", lineNumber), CallLike(std::move(label)), CmpLike(std::move(left), std::move(right)) { 56 | } 57 | 58 | void withIR(IR *context) override { 59 | CmpLike::withIR(context); 60 | if (INSTANCEOF_SHARED(this->left, Ptr) && INSTANCEOF_SHARED(this->right, Ptr)) { 61 | throw ParseException(i18n("ir.op.memory_operands")); 62 | } 63 | } 64 | 65 | [[nodiscard]] std::string toString() const noexcept override { 66 | return fmt::format("jne {}, {}, {}", left->toString(), right->toString(), label); 67 | } 68 | 69 | [[nodiscard]] std::string compile() const override { 70 | return JmpLike::compile(); 71 | } 72 | 73 | [[nodiscard]] std::string compile(const JmpMap &jmpMap) const override { 74 | assert(jmpMap.contains(labelHash)); 75 | 76 | if (const auto &left = INSTANCEOF_SHARED(this->left, Register)) { 77 | if (const auto &right = INSTANCEOF_SHARED(this->right, Register)) { 78 | return cmp(jmpMap, left.get(), right.get()); 79 | } 80 | if (const auto &right = INSTANCEOF_SHARED(this->right, Immediate)) { 81 | return cmp(jmpMap, left.get(), right.get()); 82 | } 83 | assert(INSTANCEOF_SHARED(this->right, Ptr)); 84 | return cmp(jmpMap, left.get(), CAST_FAST(this->right, Ptr)); 85 | } 86 | if (const auto &left = INSTANCEOF_SHARED(this->left, Immediate)) { 87 | if (const auto &right = INSTANCEOF_SHARED(this->right, Register)) { 88 | return cmp(jmpMap, right.get(), left.get()); 89 | } 90 | if (const auto &right = INSTANCEOF_SHARED(this->right, Immediate)) { 91 | // 两个立即数比较直接编译时计算掉。因为mcfunction原生不支持比较两个立即数。多一条存储到寄存器就因噎废食了。 92 | if (left->getValue() != right->getValue()) return ""; 93 | return string::join(jmpMap.at(labelHash), '\n'); 94 | } 95 | assert(INSTANCEOF_SHARED(this->right, Ptr)); 96 | return cmp(jmpMap, left.get(), CAST_FAST(this->right, Ptr)); 97 | } 98 | assert(INSTANCEOF_SHARED(this->left, Ptr)); 99 | const auto &left = CAST_FAST(this->left, Ptr); 100 | if (const auto &right = INSTANCEOF_SHARED(this->right, Register)) { 101 | return cmp(jmpMap, right.get(), left); 102 | } 103 | assert(INSTANCEOF_SHARED(this->right, Immediate)); 104 | return cmp(jmpMap, CAST_FAST(this->right, Immediate), left); 105 | } 106 | }; 107 | 108 | #endif //CLANG_MC_JNE_H 109 | -------------------------------------------------------------------------------- /src/cpp/utils/Common.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/13. 3 | // 4 | 5 | #ifndef CLANG_MC_COMMON_H 6 | #define CLANG_MC_COMMON_H 7 | 8 | #include "filesystem" 9 | #include "spdlog/spdlog.h" 10 | #include "cstdint" 11 | #include "objects/include/UnorderedDense.h" 12 | #include 13 | #include "utils/Native.h" 14 | 15 | using Path = std::filesystem::path; 16 | using Logger = std::shared_ptr; 17 | template > 18 | using HashMap = ankerl::unordered_dense::map; 19 | template 20 | using HashSet = ankerl::unordered_dense::set; 21 | using i8 = int8_t; 22 | using i16 = int16_t; 23 | using i32 = int32_t; 24 | using i64 = int64_t; 25 | using u8 = uint8_t; 26 | using u16 = uint16_t; 27 | using u32 = uint32_t; 28 | using u64 = uint64_t; 29 | using Hash = u64; 30 | template 31 | using Supplier = std::function; 32 | using Json = nlohmann::json; 33 | 34 | class ParseException : public std::runtime_error { 35 | public: 36 | explicit ParseException(const std::string &string) : std::runtime_error(string) { 37 | } 38 | }; 39 | 40 | class UnsupportedOperationException : public std::runtime_error { 41 | public: 42 | explicit UnsupportedOperationException(const std::string &string) : std::runtime_error(string) { 43 | } 44 | }; 45 | 46 | class NotImplementedException : public UnsupportedOperationException { 47 | public: 48 | explicit NotImplementedException() : UnsupportedOperationException("Not implemented.") { 49 | } 50 | }; 51 | 52 | class IOException : public std::runtime_error { 53 | public: 54 | explicit IOException(const std::string &string) : std::runtime_error(string) { 55 | } 56 | }; 57 | 58 | class NullPointerException : public std::runtime_error { 59 | public: 60 | explicit NullPointerException(const std::string &string) : std::runtime_error(string) { 61 | } 62 | }; 63 | 64 | #define GETTER(name, field) __forceinline auto &get##name() noexcept { return field; } __forceinline const auto &get##name() const noexcept { return field; } // NOLINT(*-macro-parentheses) 65 | #define GETTER_POD(name, field) __forceinline auto get##name() const noexcept { return field; } 66 | #define SETTER(name, field) __forceinline void set##name(const auto &value) noexcept { field = value; } 67 | #define SETTER_POD(name, field) __forceinline void set##name(auto value) noexcept { field = value; } 68 | 69 | #define DATA(name, field) GETTER(name, field) SETTER(name, field) 70 | #define DATA_POD(name, field) GETTER_POD(name, field) SETTER_POD(name, field) 71 | 72 | #define CAST_FAST(ptr, type) ((type *) ptr.get()) 73 | #define CAST_SHARED(sharedPtr, type) (dynamic_pointer_cast(sharedPtr)) 74 | #define INSTANCEOF(ptr, type) (dynamic_cast((ptr).get())) 75 | #define INSTANCEOF_SHARED(sharedPtr, type) CAST_SHARED(sharedPtr, type) 76 | #define FUNC_WITH(method) ([](auto&&... args) { return method(std::forward(args)...); }) 77 | #define FUNC_THIS(method) ([this](auto&&... args) { return this->method(std::forward(args)...); }) 78 | #define FUNC_ARG0(method) ([](auto &object, auto&&... args) { return object.method(std::forward(args)...); }) 79 | #define UNUSED(expr) ((void) (expr)) 80 | 81 | #define NOT_IMPLEMENTED() throw NotImplementedException() 82 | #define LIKELY(x) __builtin_expect(!!(x), 1) 83 | #define UNLIKELY(x) __builtin_expect(!!(x), 0) 84 | #define UNREACHABLE() __builtin_unreachable() 85 | #define PURE [[nodiscard]] 86 | 87 | PURE static inline constexpr Hash hash(const std::string_view &str) noexcept { 88 | Hash hash = 14695981039346656037U; 89 | for (const char c: str) { 90 | hash ^= static_cast(c); 91 | hash *= 1099511628211U; 92 | } 93 | return hash; 94 | } 95 | 96 | #define SWITCH_STR(string) switch (hash(string)) 97 | #define CASE_STR(string) case hash(string) 98 | 99 | 100 | #ifndef NDEBUG 101 | #undef assert 102 | #define assert(expression) do { \ 103 | if (!(expression)) { \ 104 | printStacktrace(); \ 105 | _wassert(_CRT_WIDE(#expression), _CRT_WIDE(__FILE__), (unsigned)(__LINE__)); \ 106 | } \ 107 | } while (0) 108 | #define WARN(condition, message) \ 109 | do { \ 110 | if (!(condition)) { \ 111 | std::cerr << "Warning: " << message << " (" << __FILE__ << ":" << __LINE__ << ")\n"; \ 112 | } \ 113 | } while (0) 114 | #define DEBUG_PRINT(message) \ 115 | do { \ 116 | asm volatile("" ::: "memory"); \ 117 | __sync_synchronize(); \ 118 | std::cout << message << std::endl; \ 119 | asm volatile("" ::: "memory"); \ 120 | __sync_synchronize(); \ 121 | } while (0) 122 | #else 123 | #undef assert 124 | #define assert(expression) \ 125 | if (false) { \ 126 | UNREACHABLE(); \ 127 | ((void) (expression)); \ 128 | } 129 | #define WARN(condition, message) UNUSED(condition); UNUSED(message) 130 | #define DEBUG_PRINT(message) UNUSED(message) 131 | #endif 132 | 133 | template 134 | static __forceinline constexpr T *requireNonNull(T *object) { 135 | if (UNLIKELY(object == nullptr)) { 136 | throw NullPointerException("null"); 137 | } 138 | return object; 139 | } 140 | 141 | static __forceinline u64 getRsp() { 142 | u64 rsp; 143 | asm inline("mov %%rsp, %0" : "=r"(rsp)); 144 | return rsp; 145 | } 146 | 147 | #endif //CLANG_MC_COMMON_H 148 | -------------------------------------------------------------------------------- /src/cpp/ClangMc.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/13. 3 | // 4 | 5 | #include "ClangMc.h" 6 | #include "utils/FileUtils.h" 7 | #include "ir/verify/Verifier.h" 8 | #include "objects/LogFormatter.h" 9 | #include "builder/Builder.h" 10 | #include "builder/PostOptimizer.h" 11 | #include "extern/ResourceManager.h" 12 | #include "parse/ParseManager.h" 13 | #include "extern/ClRustAPI.h" 14 | #include 15 | #include 16 | #include 17 | #include "vector" 18 | #include "array" 19 | 20 | ClangMc::ClangMc(const Config &config) : config(config) { 21 | auto consoleSink = std::make_shared(); 22 | auto sinks = std::vector{consoleSink}; 23 | if (this->config.getLogFile()) { 24 | auto fileSink = std::make_shared("latest.log", true); 25 | sinks.push_back(fileSink); 26 | } 27 | 28 | logger = std::make_shared("multi_sink", sinks.begin(), sinks.end()); 29 | #ifndef NDEBUG 30 | for (const auto &sink: sinks) { 31 | sink->set_level(spdlog::level::debug); 32 | } 33 | logger->set_level(spdlog::level::debug); 34 | #endif 35 | logger->set_formatter(std::make_unique()); 36 | } 37 | 38 | ClangMc::~ClangMc() { 39 | spdlog::drop_all(); 40 | } 41 | 42 | void ClangMc::start() { 43 | try { 44 | // initializing 45 | ClRust_Init(); 46 | ensureEnvironment(); 47 | ensureValidConfig(); 48 | ensureBuildDir(); 49 | 50 | #ifndef NDEBUG 51 | logger->debug("pre parse"); 52 | #endif 53 | // parse 54 | auto parseManager = ParseManager(config, logger); 55 | parseManager.loadSource(); 56 | parseManager.loadIR(); 57 | auto &irs = parseManager.getIRs(); 58 | 59 | #ifndef NDEBUG 60 | logger->debug("pre verify"); 61 | #endif 62 | // verify 63 | Verifier(logger, config, irs).verify(); 64 | if (!config.getDebugInfo()) { 65 | parseManager.freeSource(); 66 | } 67 | 68 | if (config.getPreprocessOnly()) { 69 | for (auto &ir: irs) { 70 | ir.preCompile(); 71 | auto path = Path(ir.getFile()); 72 | path.replace_extension(".mci"); 73 | 74 | StringBuilder builder = StringBuilder(); 75 | for (const auto &op: ir.getValues()) { 76 | builder.appendLine(op->toString()); 77 | } 78 | ensureParentDir(path); 79 | writeFile(path, builder.toString()); 80 | } 81 | return; 82 | } 83 | 84 | #ifndef NDEBUG 85 | logger->debug("pre compile"); 86 | #endif 87 | // compiling 88 | auto mcFunctions = std::vector(irs.size()); 89 | //#pragma omp parallel for default(none) shared(irs, mcFunctions) 90 | for (size_t i = 0; i < irs.size(); ++i) { 91 | mcFunctions[i] = irs[i].compile(); 92 | } 93 | if (config.getDebugInfo()) { 94 | parseManager.freeSource(); 95 | } 96 | parseManager.freeIR(); 97 | 98 | // post optimize 99 | if (!config.getDebugInfo()) { 100 | auto postOptimizer = PostOptimizer(mcFunctions); 101 | postOptimizer.optimize(); 102 | } 103 | 104 | #ifndef NDEBUG 105 | logger->debug("pre build"); 106 | #endif 107 | auto builder = Builder(config, std::move(mcFunctions)); 108 | // building 109 | #ifndef NDEBUG 110 | logger->debug("pre call build"); 111 | #endif 112 | builder.build(); 113 | 114 | if (config.getCompileOnly()) { 115 | #ifndef NDEBUG 116 | logger->debug("exited normally."); 117 | #endif 118 | return; 119 | } 120 | 121 | 122 | #ifndef NDEBUG 123 | logger->debug("pre link"); 124 | #endif 125 | // linking 126 | builder.link(); 127 | 128 | #ifndef NDEBUG 129 | logger->debug("exited normally."); 130 | #endif 131 | return; 132 | } catch (const IOException &e) { 133 | logger->error(e.what()); 134 | } catch (const ParseException &e) { 135 | logger->error(e.what()); 136 | } 137 | 138 | logger->error(i18n("general.unable_to_build")); 139 | exit(); 140 | } 141 | 142 | [[noreturn]] void ClangMc::exit() { 143 | spdlog::drop_all(); 144 | std::exit(0); 145 | UNREACHABLE(); 146 | } 147 | 148 | void ClangMc::ensureEnvironment() const { 149 | if (initResources()) return; 150 | logger->error(i18n("general.environment_error")); 151 | exit(); 152 | } 153 | 154 | void ClangMc::ensureValidConfig() { 155 | if (config.getInput().empty()) { 156 | std::cout << string::removeFromLast(getExecutableName(getArgv0()), ".") << ": "; 157 | logger->error(i18n("general.no_input_files")); 158 | exit(); 159 | } 160 | if (std::any_of(config.getInput().begin(), config.getInput().end(), [&](const Path &item) { 161 | return item.extension() != ".mcasm"; 162 | })) { 163 | logger->error(i18n("general.invalid_input")); 164 | exit(); 165 | } 166 | } 167 | 168 | void ClangMc::ensureBuildDir() { 169 | const Path &dir = config.getBuildDir(); 170 | try { 171 | if (exists(dir)) { 172 | remove_all(dir); 173 | } 174 | create_directory(dir); 175 | } catch (const std::filesystem::filesystem_error &e) { 176 | logger->error(i18n("general.failed_init_build")); 177 | logger->error(e.what()); 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | 2052472631@qq.com. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /src/cpp/ir/values/Register.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/16. 3 | // 4 | 5 | #ifndef CLANG_MC_REGISTER_H 6 | #define CLANG_MC_REGISTER_H 7 | 8 | #include "Value.h" 9 | #include "i18n/I18n.h" 10 | #include "utils/string/StringUtils.h" 11 | 12 | class Registers; 13 | 14 | class Register : public Value { 15 | private: 16 | const std::string name; 17 | const bool pushable; 18 | 19 | explicit Register(std::string name, bool pushable) noexcept: name(std::move(name)), pushable(pushable) { 20 | } 21 | 22 | friend class Registers; 23 | 24 | public: 25 | explicit Register() = delete; 26 | 27 | GETTER(Name, name); 28 | 29 | GETTER_POD(Pushable, pushable) 30 | 31 | [[nodiscard]] std::string toString() const noexcept override { 32 | return getName(); 33 | } 34 | 35 | [[nodiscard]] __forceinline bool operator==(const Register &target) const noexcept { 36 | return this == ⌖ 37 | } 38 | }; 39 | 40 | class Registers { 41 | private: 42 | static std::shared_ptr create(const std::string &name, const bool pushable) { 43 | return std::shared_ptr(new Register(name, pushable)); 44 | } 45 | 46 | static std::shared_ptr create(const std::string &name) { 47 | return create(name, true); 48 | } 49 | 50 | public: 51 | Registers() = delete; 52 | 53 | // 通用寄存器 54 | static inline const auto RAX = create("rax"); // 函数返回值 caller-saved 55 | static inline const auto R0 = create("r0"); // 参数1或临时寄存器 caller-saved 56 | static inline const auto R1 = create("r1"); // 参数2或临时寄存器 caller-saved 57 | static inline const auto R2 = create("r2"); // 参数3或临时寄存器 caller-saved 58 | static inline const auto R3 = create("r3"); // 参数4或临时寄存器 caller-saved 59 | static inline const auto R4 = create("r4"); // 参数5或临时寄存器 caller-saved 60 | static inline const auto R5 = create("r5"); // 参数6或临时寄存器 caller-saved 61 | static inline const auto R6 = create("r6"); // 参数7或临时寄存器 caller-saved 62 | static inline const auto R7 = create("r7"); // 参数8或临时寄存器 caller-saved 63 | static inline const auto T0 = create("t0"); // 临时寄存器1 caller-saved 64 | static inline const auto T1 = create("t1"); // 临时寄存器2 caller-saved 65 | static inline const auto T2 = create("t2"); // 临时寄存器3 caller-saved 66 | static inline const auto T3 = create("t3"); // 临时寄存器4 caller-saved 67 | static inline const auto T4 = create("t4"); // 临时寄存器5 caller-saved 68 | static inline const auto T5 = create("t5"); // 临时寄存器6 caller-saved 69 | static inline const auto T6 = create("t6"); // 临时寄存器7 caller-saved 70 | static inline const auto T7 = create("t7"); // 临时寄存器8 caller-saved 71 | static inline const auto X0 = create("x0"); // 保存寄存器1 callee-saved 72 | static inline const auto X1 = create("x1"); // 保存寄存器2 callee-saved 73 | static inline const auto X2 = create("x2"); // 保存寄存器3 callee-saved 74 | static inline const auto X3 = create("x3"); // 保存寄存器4 callee-saved 75 | static inline const auto X4 = create("x4"); // 保存寄存器5 callee-saved 76 | static inline const auto X5 = create("x5"); // 保存寄存器6 callee-saved 77 | static inline const auto X6 = create("x6"); // 保存寄存器7 callee-saved 78 | static inline const auto X7 = create("x7"); // 保存寄存器8 callee-saved 79 | static inline const auto X8 = create("x8"); // 保存寄存器9 callee-saved 80 | static inline const auto X9 = create("x9"); // 保存寄存器10 callee-saved 81 | static inline const auto X10 = create("x10"); // 保存寄存器11 callee-saved 82 | static inline const auto X11 = create("x11"); // 保存寄存器12 callee-saved 83 | static inline const auto X12 = create("x12"); // 保存寄存器13 callee-saved 84 | static inline const auto X13 = create("x13"); // 保存寄存器14 callee-saved 85 | static inline const auto X14 = create("x14"); // 保存寄存器15 callee-saved 86 | static inline const auto X15 = create("x15"); // 保存寄存器16 callee-saved 87 | 88 | // VM内部保留寄存器 89 | static inline const auto RSP = create("rsp", false); // 栈指针 90 | static inline const auto SHP = create("shp", false); // 堆大小 91 | static inline const auto SBP = create("sbp", false); // 静态数据基址指针 92 | static inline const auto S0 = create("s0", false); // 编译器保留 93 | static inline const auto S1 = create("s1", false); // 编译器保留 94 | static inline const auto S2 = create("s2", false); // 编译器保留 95 | static inline const auto S3 = create("s3", false); // 编译器保留 96 | static inline const auto S4 = create("s4", false); // 编译器保留 97 | 98 | static std::shared_ptr fromName(const std::string_view &name) { 99 | SWITCH_STR (string::toLowerCase(name)) { 100 | CASE_STR("rax"): return RAX; 101 | CASE_STR("r0"): return R0; 102 | CASE_STR("r1"): return R1; 103 | CASE_STR("r2"): return R2; 104 | CASE_STR("r3"): return R3; 105 | CASE_STR("r4"): return R4; 106 | CASE_STR("r5"): return R5; 107 | CASE_STR("r6"): return R6; 108 | CASE_STR("r7"): return R7; 109 | CASE_STR("t0"): return T0; 110 | CASE_STR("t1"): return T1; 111 | CASE_STR("t2"): return T2; 112 | CASE_STR("t3"): return T3; 113 | CASE_STR("t4"): return T4; 114 | CASE_STR("t5"): return T5; 115 | CASE_STR("t6"): return T6; 116 | CASE_STR("t7"): return T7; 117 | CASE_STR("x0"): return X0; 118 | CASE_STR("x1"): return X1; 119 | CASE_STR("x2"): return X2; 120 | CASE_STR("x3"): return X3; 121 | CASE_STR("x4"): return X4; 122 | CASE_STR("x5"): return X5; 123 | CASE_STR("x6"): return X6; 124 | CASE_STR("x7"): return X7; 125 | CASE_STR("x8"): return X8; 126 | CASE_STR("x9"): return X9; 127 | CASE_STR("x10"): return X10; 128 | CASE_STR("x11"): return X11; 129 | CASE_STR("x12"): return X12; 130 | CASE_STR("x13"): return X13; 131 | CASE_STR("x14"): return X14; 132 | CASE_STR("x15"): return X15; 133 | CASE_STR("rsp"): return RSP; 134 | CASE_STR("shp"): return SHP; 135 | CASE_STR("sbp"): return SBP; 136 | default: [[unlikely]] 137 | throw ParseException(i18nFormat("ir.value.register.unknown_register", name)); 138 | } 139 | } 140 | }; 141 | 142 | #endif //CLANG_MC_REGISTER_H 143 | -------------------------------------------------------------------------------- /src/cpp/ir/values/Ptr.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/24. 3 | // 4 | 5 | #ifndef CLANG_MC_PTR_H 6 | #define CLANG_MC_PTR_H 7 | 8 | #include "Value.h" 9 | #include "Register.h" 10 | 11 | class Ptr : public Value { 12 | private: 13 | const Register *base; 14 | const Register *index; 15 | const i32 scale; 16 | const i32 displacement; 17 | 18 | void setPtr(std::ostringstream &result, const std::string &fieldName) const { 19 | const auto addDisplacementToS0 = [&]() { 20 | if (displacement != 0) { 21 | if (displacement > 0) { 22 | result << fmt::format("scoreboard players add s0 vm_regs {}\n", displacement); 23 | } else { 24 | result << fmt::format("scoreboard players remove s0 vm_regs {}\n", -displacement); 25 | } 26 | } 27 | }; 28 | 29 | if (base == nullptr) { 30 | assert(scale != 1); 31 | 32 | result << fmt::format("scoreboard players operation s0 vm_regs = {} vm_regs\n", index->getName()); 33 | addDisplacementToS0(); 34 | result << fmt::format( 35 | "execute store result storage std:vm s2.{} int {} run scoreboard players get s0 vm_regs\n", 36 | fieldName, scale); 37 | } else if (index == nullptr) { 38 | assert(base != nullptr); 39 | assert(scale == 1); 40 | if (displacement == 0) { 41 | result << fmt::format( 42 | "execute store result storage std:vm s2.{} int 1 run scoreboard players get {} vm_regs\n", 43 | fieldName, base->getName()); 44 | } else { 45 | result << fmt::format("scoreboard players operation s0 vm_regs = {} vm_regs\n", base->getName()); 46 | addDisplacementToS0(); 47 | result << fmt::format( 48 | "execute store result storage std:vm s2.{} int 1 run scoreboard players get s0 vm_regs\n", 49 | fieldName); 50 | } 51 | } else { 52 | // 标准实现 53 | result << fmt::format("scoreboard players set s0 vm_regs {}\n", scale); 54 | result << fmt::format( 55 | "scoreboard players operation s0 vm_regs *= {} vm_regs\n", index->getName()); 56 | result << fmt::format("scoreboard players operation s0 vm_regs += {} vm_regs\n", base->getName()); 57 | addDisplacementToS0(); 58 | result << fmt::format( 59 | "execute store result storage std:vm s2.{} int 1 run scoreboard players get s0 vm_regs\n", 60 | fieldName); 61 | } 62 | } 63 | 64 | public: 65 | explicit Ptr(const Register *base, const Register *index, const i32 scale, const i32 displacement) noexcept: 66 | base(base), index(index), scale(scale), displacement(displacement) { 67 | assert(!(index == nullptr && scale != 1)); 68 | assert(!(base == nullptr && index != nullptr && scale == 1)); 69 | } 70 | 71 | [[nodiscard]] std::string toString() const noexcept override { 72 | auto builder = StringBuilder(); 73 | if (base != nullptr) { 74 | builder.append(base->toString()); 75 | } 76 | if (index != nullptr) { 77 | if (!builder.isEmpty()) { 78 | builder.append(" + "); 79 | } 80 | builder.append(index->toString()); 81 | 82 | if (scale != 1) { 83 | builder.append(" * "); 84 | builder.append(std::to_string(scale)); 85 | } 86 | } 87 | if (displacement != 0 || builder.isEmpty()) { 88 | if (displacement >= 0) { 89 | if (!builder.isEmpty()) { 90 | builder.append(" + "); 91 | } 92 | builder.append(std::to_string(displacement)); 93 | } else { 94 | if (!builder.isEmpty()) { 95 | builder.append(" - "); 96 | } 97 | builder.append(std::to_string(-displacement)); 98 | } 99 | } 100 | return fmt::format("[{}]", builder.toString()); 101 | } 102 | 103 | /** 104 | * 把指针位置的内存加载到寄存器reg 105 | */ 106 | [[nodiscard]] std::string loadTo(const Register ®) const { 107 | if (base == nullptr && index == nullptr) { 108 | assert(scale == 1); 109 | // 内联以改善性能 110 | return fmt::format("execute store result score {} vm_regs run data get storage std:vm heap[{}]", 111 | reg.getName(), displacement); 112 | } 113 | 114 | auto result = std::ostringstream(); 115 | 116 | if (reg == *Registers::RAX) { 117 | result << "data modify storage std:vm s2 set value {ptr: -1}\n"; 118 | setPtr(result, "ptr"); 119 | result << "function std:_internal/load_heap with storage std:vm s2"; 120 | } else { 121 | result << fmt::format("data modify storage std:vm s2 set value {{result: \"{}\", ptr: -1}}\n", 122 | reg.getName()); 123 | setPtr(result, "ptr"); 124 | result << "function std:_internal/load_heap_custom with storage std:vm s2"; 125 | } 126 | return result.str(); 127 | } 128 | 129 | /** 130 | * 把寄存器reg的值存储到指针位置的内存 131 | */ 132 | [[nodiscard]] std::string storeFrom(const Register ®) const { 133 | if (base == nullptr && index == nullptr) { 134 | assert(scale == 1); 135 | // 内联以改善性能 136 | return fmt::format( 137 | "execute store result storage std:vm heap[{}] int 1 run scoreboard players get {} vm_regs", 138 | displacement, reg.getName()); 139 | } 140 | 141 | auto result = std::ostringstream(); 142 | 143 | if (reg == *Registers::R1) { 144 | result << "data modify storage std:vm s2 set value {ptr: -1}\n"; 145 | setPtr(result, "ptr"); 146 | result << "function std:_internal/store_heap with storage std:vm s2"; 147 | } else { 148 | result << fmt::format("data modify storage std:vm s2 set value {{ptr: -1, value: \"{}\"}}\n", 149 | reg.getName()); 150 | setPtr(result, "ptr"); 151 | result << "function std:_internal/store_heap_custom with storage std:vm s2"; 152 | } 153 | 154 | return result.str(); 155 | } 156 | 157 | /** 158 | * 把立即数存储到指针位置的内存 159 | */ 160 | [[nodiscard]] std::string storeFrom(const Immediate &immediate) const { 161 | if (base == nullptr && index == nullptr) { 162 | assert(scale == 1); 163 | // 内联以改善性能 164 | return fmt::format("data modify storage std:vm heap[{}] set value {}", 165 | displacement, static_cast(immediate.getValue())); 166 | } 167 | 168 | auto result = std::ostringstream(); 169 | 170 | result << fmt::format("data modify storage std:vm s2 set value {{ptr: -1, value: {}}}\n", 171 | static_cast(immediate.getValue())); 172 | setPtr(result, "ptr"); 173 | result << "function std:_internal/store_heap_custom2 with storage std:vm s2"; 174 | return result.str(); 175 | } 176 | 177 | /** 178 | * 把入参指针位置的内存存储到指针位置的内存 179 | */ 180 | [[nodiscard]] std::string storeFrom(const Ptr &ptr) const { 181 | if (base == nullptr && index == nullptr && ptr.base == nullptr && ptr.index == nullptr) { 182 | assert(scale == 1); 183 | // 内联以改善性能 184 | return fmt::format("data modify storage std:vm heap[{}] set from storage std:vm heap[{}]", 185 | displacement, ptr.displacement); 186 | } 187 | 188 | auto result = std::ostringstream(); 189 | 190 | result << "data modify storage std:vm s2 set value {to: -1, from: -1}\n"; 191 | setPtr(result, "to"); 192 | ptr.setPtr(result, "from"); 193 | result << "function std:_internal/store_heap_custom3 with storage std:vm s2"; 194 | return result.str(); 195 | } 196 | }; 197 | 198 | #endif //CLANG_MC_PTR_H 199 | -------------------------------------------------------------------------------- /src/cpp/ir/OpCommon.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by xia__mc on 2025/2/24. 3 | // 4 | 5 | #include "OpCommon.h" 6 | #include "i18n/I18n.h" 7 | #include "ir/values/Symbol.h" 8 | #include "ir/values/SymbolPtr.h" 9 | 10 | PURE i32 parseToNumber(std::string_view string) { 11 | assert(!string.empty()); 12 | string = string::trim(string); 13 | 14 | // 字符串("AB")或字符('A') 15 | // if (string.front() == '"' && string.back() == '"' && string.size() > 2) { 16 | // std::string_view content = string.substr(1, string.size() - 2); // 去掉引号 17 | // if (content.size() > 4) { 18 | // throw ParseException(i18n("ir.op.out_of_range")); 19 | // } 20 | // int32_t result = 0; 21 | // for (size_t i = 0; i < content.size(); ++i) { 22 | // result |= (static_cast(content[i]) << (i * 8)); // 小端存储字面量 23 | // } 24 | // return result; 25 | // } else 26 | if (string.front() == '\'' && string.back() == '\'' && string.size() == 3) { 27 | return static_cast(string[1]); // 单字符 28 | } 29 | 30 | // 解析进制 31 | i32 base = 10; 32 | size_t offset = 0; 33 | bool isNegtive; 34 | if (string.front() == '-') { 35 | isNegtive = true; 36 | string = string::trim(string.substr(1)); 37 | } else { 38 | isNegtive = false; 39 | if (string.front() == '+') { 40 | string = string::trim(string.substr(1)); 41 | } 42 | } 43 | 44 | if (string.size() > 2) { 45 | if (string.substr(0, 2) == "0x" || string.substr(0, 2) == "0X") { 46 | base = 16; 47 | offset = 2; 48 | } else if (string.substr(0, 2) == "0b" || string.substr(0, 2) == "0B") { 49 | base = 2; 50 | offset = 2; 51 | } else if (string.substr(0, 2) == "0o" || string.substr(0, 2) == "0O") { 52 | base = 8; 53 | offset = 2; 54 | } 55 | } 56 | 57 | // 处理后缀(如"101b" "173o") 58 | if (string.back() == 'b' && string.size() > 1) { 59 | base = 2; 60 | string = string.substr(0, string.size() - 1); 61 | } else if (string.back() == 'o' && string.size() > 1) { 62 | base = 8; 63 | string = string.substr(0, string.size() - 1); 64 | } 65 | 66 | // 解析数字 67 | i64 result; 68 | auto [ptr, ec] = std::from_chars( 69 | string.data() + offset, string.data() + string.size(), result, base); 70 | 71 | if (ec == std::errc::invalid_argument || ptr != string.data() + string.size()) { 72 | throw ParseException(i18n("ir.op.invalid_number_format")); 73 | } 74 | if (ec == std::errc::result_out_of_range) { 75 | throw ParseException(i18n("ir.op.out_of_range")); 76 | } 77 | 78 | if (isNegtive) { 79 | if (result > static_cast(INT_MAX) + 1) { 80 | throw ParseException(i18n("ir.op.out_of_range")); 81 | } 82 | result = -result; 83 | } else if (result > INT_MAX) { 84 | throw ParseException(i18n("ir.op.out_of_range")); 85 | } 86 | return static_cast(result); 87 | } 88 | 89 | struct PtrData { 90 | const Register *base; 91 | const Register *index; 92 | const i32 scale; 93 | const i32 displacement; 94 | }; 95 | 96 | static __forceinline std::string fixStringForPtrData(const std::string_view &string) { 97 | auto splits = string::split(string, '-'); 98 | if (splits.size() == 1) { 99 | return std::string(string); 100 | } 101 | 102 | std::transform(splits.begin(), splits.end(), splits.begin(), string::trim); 103 | return string::join(splits, "+-"); 104 | } 105 | 106 | static PtrData parsePtrData(const std::string_view &string) { 107 | // base 108 | try { 109 | return PtrData(Registers::fromName(string).get(), nullptr, 1, 0); 110 | } catch (const ParseException &) { 111 | } 112 | 113 | // displacement 114 | try { 115 | return PtrData(nullptr, nullptr, 1, parseToNumber(string)); 116 | } catch (const ParseException &) { 117 | } 118 | 119 | // index * scale 120 | auto splits = string::split(string, '*'); 121 | std::transform(splits.begin(), splits.end(), splits.begin(), string::trim); 122 | if (splits.size() == 2) { 123 | try { 124 | auto scale = parseToNumber(splits[1]); 125 | if (scale < 0) { 126 | throw ParseException(i18n("ir.op.scale_out_of_range")); 127 | } 128 | 129 | return PtrData(nullptr, Registers::fromName(splits[0]).get(), scale, 0); 130 | } catch (const ParseException &) { 131 | } 132 | } 133 | 134 | auto fixed = fixStringForPtrData(string); 135 | splits = string::split(fixed, '+'); 136 | std::transform(splits.begin(), splits.end(), splits.begin(), string::trim); 137 | if (splits.size() == 2) { 138 | // base + index 139 | try { 140 | return PtrData(Registers::fromName(splits[0]).get(), 141 | Registers::fromName(splits[1]).get(), 1, 0); 142 | } catch (const ParseException &) { 143 | } 144 | 145 | // base + displacement 146 | try { 147 | return PtrData(Registers::fromName(splits[0]).get(), nullptr, 148 | 1, parseToNumber(splits[1])); 149 | } catch (const ParseException &) { 150 | } 151 | 152 | // base + index * scale 153 | auto splits2 = string::split(splits[1], '*'); 154 | std::transform(splits2.begin(), splits2.end(), splits2.begin(), string::trim); 155 | if (splits2.size() == 2) { 156 | try { 157 | auto scale = parseToNumber(splits2[1]); 158 | if (scale < 0) { 159 | throw ParseException(i18n("ir.op.scale_out_of_range")); 160 | } 161 | 162 | return PtrData(Registers::fromName(splits[0]).get(), 163 | Registers::fromName(splits2[0]).get(), scale, 0); 164 | } catch (const ParseException &) { 165 | } 166 | } 167 | } else if (splits.size() == 3) { 168 | // base + index + displacement 169 | try { 170 | return PtrData(Registers::fromName(splits[0]).get(), 171 | Registers::fromName(splits[1]).get(), 172 | 1, parseToNumber(splits[2])); 173 | } catch (const ParseException &) { 174 | } 175 | 176 | // base + index * scale + displacement 177 | auto splits2 = string::split(splits[1], '*'); 178 | std::transform(splits2.begin(), splits2.end(), splits2.begin(), string::trim); 179 | if (splits2.size() == 2) { 180 | try { 181 | auto scale = parseToNumber(splits2[1]); 182 | if (scale < 0) { 183 | throw ParseException(i18n("ir.op.scale_out_of_range")); 184 | } 185 | 186 | return PtrData(Registers::fromName(splits[0]).get(), 187 | Registers::fromName(splits2[0]).get(), 188 | scale, parseToNumber(splits[2])); 189 | } catch (const ParseException &) { 190 | } 191 | } 192 | } 193 | 194 | throw ParseException(i18n("ir.op.invalid_ptr")); 195 | } 196 | 197 | 198 | static __forceinline ValuePtr createImmediate(const std::string &string) { 199 | return std::make_shared(parseToNumber(string)); 200 | } 201 | 202 | static __forceinline ValuePtr createPtr(const std::string_view &string) { 203 | const auto data = parsePtrData(string); 204 | return std::make_shared(data.base, data.index, data.scale, data.displacement); 205 | } 206 | 207 | PURE ValuePtr createValue(const LineState &line, const std::string &string) { 208 | if (string.empty()) { 209 | throw ParseException(i18n("ir.op.empty_value")); 210 | } 211 | 212 | if (string.front() == '[' && string.back() == ']') { 213 | auto fixedStr = string.substr(1, string.length() - 2); 214 | try { 215 | return createPtr(fixedStr); 216 | } catch (const ParseException &e) { 217 | if (!string::contains(fixedStr, ' ')) { // TODO 改为白名单isValidSymbol 218 | return std::make_shared(fixSymbol(line, fixedStr)); 219 | } 220 | throw e; 221 | } 222 | 223 | } 224 | 225 | try { 226 | return Registers::fromName(string); 227 | } catch (const ParseException &e) { 228 | } 229 | try { 230 | return createImmediate(string); 231 | } catch (const ParseException &e) { 232 | } 233 | 234 | return std::make_shared(fixSymbol(line, string)); 235 | } 236 | --------------------------------------------------------------------------------