diff --git a/libs/argparse/include/argparse.h b/libs/argparse/include/argparse.h index e11f293..5cc22df 100644 --- a/libs/argparse/include/argparse.h +++ b/libs/argparse/include/argparse.h @@ -218,10 +218,11 @@ static inline void scc_argparse_spec_setup_float(scc_argparse_spec_t *spec, static inline void scc_argparse_spec_setup_choices(scc_argparse_spec_t *spec, const char **values, - int count) { + int count, int *store) { spec->value_type = SCC_ARGPARSE_VAL_TYPE_ENUM; spec->choices.values = values; spec->choices.count = count; + spec->store.int_store = store; } // 添加设置列表的辅助函数(内联) diff --git a/libs/argparse/src/argparse.c b/libs/argparse/src/argparse.c index f4b2258..41fcac2 100644 --- a/libs/argparse/src/argparse.c +++ b/libs/argparse/src/argparse.c @@ -69,6 +69,10 @@ static inline void prepare_cmd(scc_optparse_t *optparse, min_args = 1; max_args = 65535; // FIXME maybe INT_MAX ? break; + case SCC_ARGPARSE_VAL_TYPE_ENUM: + min_args = 1; + max_args = 1; + break; default: min_args = 0; max_args = 0; @@ -136,13 +140,22 @@ static int transite_error(scc_argparse_context_t *ctx) { return error; } -static int check_choices(const scc_argparse_spec_t *spec, const char *value) { +static int check_choices(const scc_argparse_spec_t *spec, const char *value, + int *out_index) { if (!spec->choices.values || spec->choices.count == 0) { return SCC_ARGPARSE_ERR_NONE; } + if (value == nullptr) { + return SCC_ARGPARSE_ERR_MISSING_VALUE; + } + if (out_index) + *out_index = -1; + for (int i = 0; i < spec->choices.count; i += 1) { if (scc_strcmp(value, spec->choices.values[i]) == 0) { + if (out_index) + *out_index = i; return SCC_ARGPARSE_ERR_NONE; } } @@ -219,6 +232,9 @@ static int handle_option(scc_argparse_context_t *ctx, scc_argparse_t *parser) { if (opt->spec.value_type == SCC_ARGPARSE_VAL_TYPE_BOOL) { *opt->spec.store.bool_store = true; + } else if (opt->spec.value_type == SCC_ARGPARSE_VAL_TYPE_ENUM) { + return check_choices(&opt->spec, ctx->result.value, + opt->spec.store.int_store); } if (!ctx->result.value) { diff --git a/libs/ir/mir/include/core_pass/scc_frame_layout.h b/libs/ir/mir/include/core_pass/scc_frame_layout.h new file mode 100644 index 0000000..f00d700 --- /dev/null +++ b/libs/ir/mir/include/core_pass/scc_frame_layout.h @@ -0,0 +1,4 @@ +#ifndef __SCC_FRAME_LAYOUT_H__ +#define __SCC_FRAME_LAYOUT_H__ + +#endif /* __SCC_FRAME_LAYOUT_H__ */ diff --git a/libs/ir/mir/include/core_pass/scc_prolog_epilog.h b/libs/ir/mir/include/core_pass/scc_prolog_epilog.h new file mode 100644 index 0000000..9d32b7e --- /dev/null +++ b/libs/ir/mir/include/core_pass/scc_prolog_epilog.h @@ -0,0 +1,14 @@ +#ifndef __SCC_PROLOG_EPILOG_H__ +#define __SCC_PROLOG_EPILOG_H__ + +#include +#include +typedef void (*scc_prolog_epilog_fn)(void *userdata, + const scc_mir_func_t *func); + +typedef struct scc_prolog_epilog { + scc_prolog_epilog_fn prolog; + scc_prolog_epilog_fn epilog; +} scc_prolog_epilog_t; + +#endif /* __SCC_PROLOG_EPILOG_H__ */ diff --git a/libs/ir/mir/include/scc_lir2mir.h b/libs/ir/mir/include/scc_lir2mir.h index 9dd0450..dd6d522 100644 --- a/libs/ir/mir/include/scc_lir2mir.h +++ b/libs/ir/mir/include/scc_lir2mir.h @@ -7,7 +7,4 @@ void scc_lir2mir(scc_mir_module_t *mir_module, const scc_lir_module_t *lir_module); -// TODO -void scc_mir_pass(scc_mir_module_t *mir_module); - #endif /* __SCC_LIR2MIR_H__ */ diff --git a/libs/ir/mir/include/scc_mir_pass.h b/libs/ir/mir/include/scc_mir_pass.h index 1c5b9ca..811338e 100644 --- a/libs/ir/mir/include/scc_mir_pass.h +++ b/libs/ir/mir/include/scc_mir_pass.h @@ -1,7 +1,7 @@ #ifndef __SCC_MIR_PASS_H__ #define __SCC_MIR_PASS_H__ -#include "scc_mir.h" +#include "scc_mir_module.h" /** Pass 的唯一标识符 (枚举) */ typedef enum { @@ -25,107 +25,6 @@ typedef enum { SCC_MIR_STAGE_POST_REGALLOC // 寄存器分配后 } scc_mir_pass_stage_t; -/** - * @brief Pass 执行函数原型 - * @param func 输入的 MIR 函数 (Pass 可修改或替换) - * @param ctx 可选的上下文指针 (如目标机器信息、调试标志) - * @return 执行后的 MIR 函数指针 (若未修改可返回原指针,若需替换可返回新指针) - * @warning Pass 应负责释放不再使用的旧函数资源 - */ -typedef scc_mir_func_t *(*scc_mir_pass_fn_t)(scc_mir_func_t *func, void *ctx); - -typedef struct scc_lir_pass_desc { - scc_mir_pass_id_t id; // 唯一标识 - const char *name; // 可读名称 - scc_mir_pass_stage_t stage; // 所属阶段 - scc_mir_pass_id_t *deps; // 依赖的 Pass ID 数组 (可为 NULL) - usize num_deps; // 依赖数量 - scc_mir_pass_fn_t run; // 执行函数 - cbool required; // 是否为核心必需 Pass -} scc_mir_pass_desc_t; - -/* -------------------------------------------------------------------------- - * Pass 管理器 - * -------------------------------------------------------------------------- */ - -typedef struct scc_lir_pass_manager { - SCC_VEC(scc_mir_pass_desc_t) passes; // 所有已注册 Pass - SCC_VEC(scc_mir_pass_id_t) pipeline; // 当前流水线顺序 - void *ctx; // 传递给每个 Pass 的上下文 -} scc_mir_pass_manager_t; - -/** - * @brief 初始化 Pass 管理器 - * @param pm 管理器指针 - * @param ctx 全局上下文 (如 scc_target_t*) - */ -void scc_mir_pass_manager_init(scc_mir_pass_manager_t *pm, void *ctx); - -/** - * @brief 销毁 Pass 管理器,释放资源 - */ -void scc_mir_pass_manager_drop(scc_mir_pass_manager_t *pm); - -/** - * @brief 注册一个 Pass (通常在编译器初始化时调用) - * @param pm 管理器 - * @param desc Pass 描述符 - */ -void scc_mir_pass_register(scc_mir_pass_manager_t *pm, - const scc_mir_pass_desc_t *desc); - -/** - * @brief 构建默认的核心流水线 (仅包含 required=true 的 Pass) - * @param pm 管理器 - * - * 自动按阶段排序并解析依赖。 - */ -void scc_mir_pass_build_core_pipeline(scc_mir_pass_manager_t *pm); - -/** - * @brief 向流水线追加一个可选 Pass - * @param pm 管理器 - * @param id 要添加的 Pass ID - * @return 成功返回 true,若依赖不满足或 ID 无效返回 false - */ -cbool scc_mir_pass_add_to_pipeline(scc_mir_pass_manager_t *pm, - scc_mir_pass_id_t id); - -/** - * @brief 对指定函数运行当前流水线中的所有 Pass - * @param pm 管理器 - * @param func 要处理的 LIR 函数 - * @return 最终处理后的函数指针 - */ -scc_lir_func_t *scc_mir_pass_run_pipeline(scc_mir_pass_manager_t *pm, - scc_lir_func_t *func); - -/* -------------------------------------------------------------------------- - * 便捷宏:声明并注册一个 Pass - * -------------------------------------------------------------------------- */ - -/** - * @brief 在模块内部声明并注册一个 Pass - * @param pm 管理器指针 - * @param id Pass ID 枚举值 - * @param name_str Pass 名称字符串 - * @param stage 运行阶段 - * @param deps_arr 依赖数组 (可为 NULL) - * @param num_deps 依赖数量 - * @param fn 执行函数 - * @param req 是否必需 - */ -#define SCC_MIR_REGISTER_PASS(pm, id, name_str, stage, deps_arr, num_deps, fn, \ - req) \ - do { \ - scc_mir_pass_desc_t desc = {.id = (id), \ - .name = (name_str), \ - .stage = (stage), \ - .deps = (deps_arr), \ - .num_deps = (num_deps), \ - .run = (fn), \ - .required = (req)}; \ - scc_mir_pass_register((pm), &desc); \ - } while (0) +void scc_mir_pass(scc_mir_module_t *mir_module, scc_mir_pass_stage_t stage); #endif /* __SCC_LIR_PASS_H__ */ diff --git a/libs/ir/mir/src/arch/x86_64_isel.c b/libs/ir/mir/src/arch/x86_64_isel.c index a911a3b..cd0c8c2 100644 --- a/libs/ir/mir/src/arch/x86_64_isel.c +++ b/libs/ir/mir/src/arch/x86_64_isel.c @@ -36,6 +36,9 @@ void scc_x86_instr_dump(scc_tree_dump_t *td, const scc_mir_instr_t *instr) { case SCC_MIR_OP_SYMBOL: scc_tree_dump_append_fmt(td, "@%s", op->symbol); break; + case SCC_MIR_OP_MEM: + scc_tree_dump_append_fmt(td, "[%d]", op->stack_slot); + break; default: break; } diff --git a/libs/ir/mir/src/scc_mir_dump.c b/libs/ir/mir/src/scc_mir_dump.c index 0af4434..f2be6ef 100644 --- a/libs/ir/mir/src/scc_mir_dump.c +++ b/libs/ir/mir/src/scc_mir_dump.c @@ -14,8 +14,11 @@ void scc_mir_dump_instr(scc_mir_dump_ctx_t *ctx, const scc_mir_instr_t *ins) { case SCC_MIR_PSUEDO_ALLOCA: scc_tree_dump_node(td, " @alloca"); scc_tree_dump_append_fmt(td, "(%d)", ins->operands[1].imm); - Assert(ins->operands[0].kind == SCC_MIR_OP_VREG); - scc_tree_dump_append_fmt(td, " %%%d", ins->operands[0].vreg); + if (ins->operands[0].kind == SCC_MIR_OP_VREG) { + scc_tree_dump_append_fmt(td, " %%%d", ins->operands[0].vreg); + } else { + scc_tree_dump_append(td, ""); + } break; default: break; diff --git a/libs/ir/mir/src/scc_mir_pass.c b/libs/ir/mir/src/scc_mir_pass.c index eac1d61..99eacff 100644 --- a/libs/ir/mir/src/scc_mir_pass.c +++ b/libs/ir/mir/src/scc_mir_pass.c @@ -1,2 +1,8 @@ +#include #include -void scc_mir_pass(scc_mir_module_t *mir_module) {} \ No newline at end of file + +extern scc_reg_alloc_t x86_reg_alloc_hooks; + +void scc_mir_pass(scc_mir_module_t *mir_module) { + scc_reg_alloc(&x86_reg_alloc_hooks, mir_module); +} diff --git a/libs/ir/mir/src/target/win64_abi.c b/libs/ir/mir/src/target/win64_abi.c new file mode 100644 index 0000000..c76d51d --- /dev/null +++ b/libs/ir/mir/src/target/win64_abi.c @@ -0,0 +1,205 @@ +#include +#include + +#include +#include +#include + +static const int WIN64_DEFAULT_ALIGN = 8; +static const int WIN64_STACK_ALIGN = 16; + +typedef struct { + scc_hashtable_t *vreg2slot; + int offset; +} frame_layout_ctx_t; + +static void transit_vreg(scc_mir_func_meta_t *func, scc_mir_operand_t *op) { + Assert(op->kind == SCC_MIR_OP_VREG); +} + +static void frame_alloc_impl(frame_layout_ctx_t *ctx, + scc_mir_module_t *mir_module, + scc_mir_func_t *mir_func) { + /* + WIN ABI + */ + ctx->offset = 8; // called ret address + + scc_mir_func_meta_t *func_meta = SCC_MIR_FUNC_META(mir_func); + scc_vec_foreach(mir_func->bblocks, i) { + scc_cfg_bblock_id_t id = scc_vec_at(mir_func->bblocks, i); + scc_cfg_bblock_t *bb = + scc_cfg_module_unsafe_get_bblock(&mir_module->cfg_module, id); + scc_mir_instr_vec_t *instrs = SCC_MIR_BBLOCK_VALUES(bb); + scc_vec_foreach(*instrs, j) { + scc_mir_instr_t *ins = &scc_vec_at(*instrs, j); + for (int k = 0; k < ins->num_operands; k += 1) { + if (ins->operands[k].kind == SCC_MIR_OP_VREG) { + transit_vreg(func_meta, &ins->operands[k]); + } + } + } + } + + // Windows shadow space + int total_size = ctx->offset + 32; // 加上影子空间 + // 16 字节栈对齐 + ctx->offset = + (total_size + WIN64_STACK_ALIGN - 1) & ~(WIN64_STACK_ALIGN - 1); +} + +/* +windows x86 prolog epilog +https://learn.microsoft.com/zh-cn/cpp/build/prolog-and-epilog?view=msvc-180 +*/ +static void prologue(void *userdata, const scc_mir_func_t *func) { + scc_x86_64_isel_t *isel = userdata; + /* + mov [RSP + 8], RCX + push R15 + push R14 + push R13 + sub RSP, fixed-allocation-size + lea R13, 128[RSP] + ... + */ + scc_mir_func_meta_t *meta = SCC_MIR_FUNC_META(func); + int frame_size = meta->frame_size; // 之前计算好的 + + // 1. push rbp + add_instr_1(isel, SCC_X86_IFORM_PUSH_GPRV_50, reg_operand(SCC_X86_REG_RBP)); + + // 2. mov rbp, rsp + add_instr_2(isel, SCC_X86_IFORM_MOV_GPRV_GPRV_89, + reg_operand(SCC_X86_REG_RBP), reg_operand(SCC_X86_REG_RSP)); + + // 3. sub rsp, frame_size + if (frame_size > 0) { + scc_mir_operand_t imm = {.kind = SCC_MIR_OP_IMM, .imm = frame_size}; + add_instr_2(isel, SCC_X86_IFORM_SUB_GPRV_IMMZ, + reg_operand(SCC_X86_REG_RSP), imm); + } +} + +static void epilogue(void *userdata, const scc_mir_func_t *func) { + scc_x86_64_isel_t *isel = userdata; + /* + add RSP, fixed-allocation-size + pop R13 + pop R14 + pop R15 + ret + */ + // 1. mov rsp, rbp + add_instr_2(isel, SCC_X86_IFORM_MOV_GPRV_GPRV_89, + reg_operand(SCC_X86_REG_RSP), reg_operand(SCC_X86_REG_RBP)); + + // 2. pop rbp + add_instr_1(isel, SCC_X86_IFORM_POP_GPRV_58, reg_operand(SCC_X86_REG_RBP)); +} + +static void lower_call(void *userdata, const scc_lir_instr_t *instr) { + scc_x86_64_isel_t *isel = userdata; + /* + Windows x64 parameter passing + https://learn.microsoft.com/zh-cn/cpp/build/x64-calling-convention?view=msvc-180#parameter-passing + + eg. + func1(int a, int b, int c, int d, int e, int f); + // a in RCX, b in RDX, c in R8, d in R9, f then e passed on stack + + func2(float a, double b, float c, double d, float e, float f); + // a in XMM0, b in XMM1, c in XMM2, d in XMM3, f then e passed on stack + + func3(int a, double b, int c, float d, int e, float f); + // a in RCX, b in XMM1, c in R8, d in XMM3, f then e passed on stack + + func4(__m64 a, __m128 b, struct c, float d, __m128 e, __m128 f); + // a in RCX, ptr to b in RDX, ptr to c in R8, d in XMM3, + // ptr to f passed on stack, then ptr to e passed on stack + */ + + for (int i = instr->metadata.call.arg_count - 1; i >= 0; i -= 1) { + scc_lir_val_t *args = &instr->metadata.call.args[i]; + switch (i) { + case 0: + scc_x86_emit_move(isel, reg_operand(SCC_X86_REG_RCX), + scc_x86_lir_val_to_mir_op(isel, args), 8); + break; + case 1: + scc_x86_emit_move(isel, reg_operand(SCC_X86_REG_RDX), + scc_x86_lir_val_to_mir_op(isel, args), 8); + break; + case 2: + scc_x86_emit_move(isel, reg_operand(SCC_X86_REG_R8), + scc_x86_lir_val_to_mir_op(isel, args), 8); + break; + case 3: + scc_x86_emit_move(isel, reg_operand(SCC_X86_REG_R9), + scc_x86_lir_val_to_mir_op(isel, args), 8); + break; + default: + // Using stack + scc_mir_operand_t op = scc_x86_lir_val_to_mir_op(isel, args); + add_instr_1(isel, + op.kind == SCC_MIR_OP_PREG || op.kind == SCC_MIR_OP_VREG + ? SCC_X86_IFORM_PUSH_GPRV_50 + : SCC_X86_IFORM_PUSH_IMMZ, + op); + break; + } + } + + emit_call(isel, instr->metadata.call.callee); + + scc_mir_operand_t ret_reg = scc_x86_lir_val_to_mir_op(isel, &instr->to); + if (ret_reg.kind == SCC_MIR_OP_VREG) { + scc_x86_emit_move(isel, ret_reg, reg_operand(SCC_X86_REG_RAX), 8); + } else { + TODO(); + } +} + +static scc_mir_operand_t lower_param(void *userdata, const scc_lir_val_t *val) { + scc_x86_64_isel_t *isel = userdata; + Assert(val->kind == SCC_LIR_INSTR_KIND_ARG); + switch (val->data.arg) { + case 0: + return reg_operand(SCC_X86_REG_RCX); + case 1: + return reg_operand(SCC_X86_REG_RDX); + case 2: + return reg_operand(SCC_X86_REG_R8); + case 3: + return reg_operand(SCC_X86_REG_R9); + default: + return (scc_mir_operand_t){.kind = SCC_MIR_OP_MEM, + .stack_slot = -val->data.arg}; + } +} + +static void lower_ret(void *userdata, const scc_lir_instr_t *instr) { + scc_x86_64_isel_t *isel = userdata; + scc_lir_val_t ret_val = instr->metadata.ret_val; + if (ret_val.kind != SCC_LIR_INSTR_KIND_NONE) { + scc_x86_emit_move(isel, reg_operand(SCC_X86_REG_RAX), + scc_x86_lir_val_to_mir_op(isel, &ret_val), 8); + } + emit_ret(isel); +} + +void scc_win_pc_x64_abi_lowering(scc_abi_lowering_t *abi_lowering) { + abi_lowering->lower_call = lower_call; + abi_lowering->lower_ret = lower_ret; + abi_lowering->lower_param = lower_param; + + abi_lowering->lower_va_start = nullptr; + abi_lowering->lower_va_arg = nullptr; + abi_lowering->lower_va_copy = nullptr; + abi_lowering->lower_va_end = nullptr; +} + +void scc_win_pc_x64_prolog_epilog(scc_prolog_epilog_t *prolog_epilog) { + prolog_epilog->prolog = prologue; + prolog_epilog->epilog = epilogue; +} diff --git a/src/config.c b/src/config.c new file mode 100644 index 0000000..9373cc7 --- /dev/null +++ b/src/config.c @@ -0,0 +1,175 @@ +#include "config.h" + +void setup_argparse(scc_argparse_t *argparse, scc_config_t *config, + scc_argparse_lang_t lang) { + enum { + SCC_HINT_PROG_NAME, + SCC_HINT_DESCRIPTION, + SCC_HINT_OUTPUT_FILE, + SCC_HINT_INPUT_FILE, + SCC_HINT_INCLUDE_PATH, + SCC_HINT_DEFINED_MACRO, + SCC_HINT_TARGET_DESC, + SCC_HINT_VERBOSE, + + SCC_HINT_ENTRY_POINT_SYMBOL, + + SCC_HINT_EMIT_STAGE, + + SCC_HINT_EMIT_LEX, + SCC_HINT_EMIT_PP, + SCC_HINT_EMIT_AST, + SCC_HINT_EMIT_HIR, + SCC_HINT_EMIT_LIR, + SCC_HINT_EMIT_MIR, + + SCC_HINT_EMIT_FLATBIN, + SCC_HINT_EMIT_SCCF, + SCC_HINT_EMIT_TARGET, + }; + static const char *scc_hints_en[] = { + [SCC_HINT_PROG_NAME] = "scc", + [SCC_HINT_DESCRIPTION] = "A simple C compiler", + [SCC_HINT_OUTPUT_FILE] = + "Output file (`-` means standard output stream file)", + [SCC_HINT_INPUT_FILE] = "Input source file", + [SCC_HINT_INCLUDE_PATH] = "Add directory to the include search paths", + [SCC_HINT_DEFINED_MACRO] = "Define a macro", + [SCC_HINT_TARGET_DESC] = + "Target description(eg. x86_64-pc-windows-msvc)", + [SCC_HINT_VERBOSE] = "Increase verbosity (can be used multiple times)", + + [SCC_HINT_ENTRY_POINT_SYMBOL] = "Entry point symbol name", + + [SCC_HINT_EMIT_STAGE] = "Generate intermediate code and exit", + + [SCC_HINT_EMIT_LEX] = "Generate lexer sources tokens and exit", + [SCC_HINT_EMIT_PP] = "Generate preprocessed tokens and exit", + [SCC_HINT_EMIT_AST] = "Generate AST and exit", + [SCC_HINT_EMIT_HIR] = "Generate High-level IR and exit", + [SCC_HINT_EMIT_LIR] = "Generate Low-level IR and exit", + [SCC_HINT_EMIT_MIR] = "Generate Machine IR and exit", + + [SCC_HINT_EMIT_FLATBIN] = "Generate flat binary and exit", + [SCC_HINT_EMIT_SCCF] = "Generate SCCF and exit", + [SCC_HINT_EMIT_TARGET] = "Generate target description and exit", + + }; + static const char *scc_hints_zh[] = { + [SCC_HINT_PROG_NAME] = "scc", + [SCC_HINT_DESCRIPTION] = "一个简单的C编译器", + [SCC_HINT_OUTPUT_FILE] = "输出文件(`-`表示标准输出流文件)", + [SCC_HINT_INPUT_FILE] = "输入源文件", + [SCC_HINT_INCLUDE_PATH] = "添加系统头文件到搜索路径", + [SCC_HINT_DEFINED_MACRO] = "定义宏", + [SCC_HINT_TARGET_DESC] = "目标机器描述(eg. x86_64-pc-windows-msvc)", + [SCC_HINT_VERBOSE] = "增加详细输出(可多次使用)", + + [SCC_HINT_ENTRY_POINT_SYMBOL] = "入口点符号名称", + + [SCC_HINT_EMIT_STAGE] = "指定生成代码的阶段并退出", + + [SCC_HINT_EMIT_LEX] = "生成`源代码的词法单元`并退出", + [SCC_HINT_EMIT_PP] = "生成`预处理后的词法单元`并退出", + [SCC_HINT_EMIT_AST] = "生成`抽象语法树`并退出", + [SCC_HINT_EMIT_HIR] = "生成`高级中间代码`并退出", + [SCC_HINT_EMIT_LIR] = "生成`低级中间代码`并退出", + [SCC_HINT_EMIT_MIR] = "生成`机器中间代码`并退出", + + [SCC_HINT_EMIT_FLATBIN] = "生成`flat binary`并退出", + [SCC_HINT_EMIT_SCCF] = "生成`SCCF`并退出", + [SCC_HINT_EMIT_TARGET] = "生成`目标代码`并退出", + }; + + const char **scc_hints; + switch (lang) { + case SCC_ARGPARSE_LANG_EN: + scc_hints = scc_hints_en; + break; + case SCC_ARGPARSE_LANG_ZH: + scc_hints = scc_hints_zh; + break; + default: + scc_hints = scc_hints_en; + break; + } + + scc_argparse_init(argparse, scc_hints[SCC_HINT_PROG_NAME], + scc_hints[SCC_HINT_DESCRIPTION]); + argparse->lang = lang; + scc_argparse_cmd_t *root = scc_argparse_get_root(argparse); + + // -o, --output + scc_argparse_opt_t opt_output; + scc_argparse_opt_init(&opt_output, 'o', "output", + scc_hints[SCC_HINT_OUTPUT_FILE]); + scc_argparse_spec_setup_string(&opt_output.spec, &(config->output_file)); + scc_argparse_cmd_add_opt(root, &opt_output); + + // input file (必需) + scc_argparse_arg_t arg_input; + scc_argparse_arg_init(&arg_input, "input", scc_hints[SCC_HINT_INPUT_FILE]); + scc_argparse_spec_setup_string(&arg_input.spec, &(config->input_file)); + scc_argparse_spec_set_required(&arg_input.spec, true); + scc_argparse_cmd_add_arg(root, &arg_input); + + // -I, --include (添加额外的系统头文件搜索路径) + scc_argparse_opt_t opt_include; + scc_argparse_opt_init(&opt_include, 'I', "include", + scc_hints[SCC_HINT_INCLUDE_PATH]); + scc_argparse_spec_setup_list(&opt_include.spec, &(config->include_paths)); + scc_argparse_cmd_add_opt(root, &opt_include); + + // -D, --define (定义宏) + scc_argparse_opt_t opt_define; + scc_argparse_opt_init(&opt_define, 'D', "define", + scc_hints[SCC_HINT_DEFINED_MACRO]); + scc_argparse_spec_setup_list(&opt_define.spec, &(config->define_macros)); + scc_argparse_cmd_add_opt(root, &opt_define); + + // --entry-point-symbol (设置入口点符号名称) + scc_argparse_opt_t opt_entry_point_symbol; + scc_argparse_opt_init(&opt_entry_point_symbol, 0, "entry-point-symbol", + scc_hints[SCC_HINT_ENTRY_POINT_SYMBOL]); + scc_argparse_spec_setup_string(&opt_entry_point_symbol.spec, + &(config->entry_point_symbol)); + scc_argparse_cmd_add_opt(root, &opt_entry_point_symbol); + + // --target + scc_argparse_opt_t opt_target; + scc_argparse_opt_init(&opt_target, 0, "target", + scc_hints[SCC_HINT_TARGET_DESC]); + scc_argparse_spec_setup_string(&opt_target.spec, + &(config->target_description)); + scc_argparse_cmd_add_opt(root, &opt_target); + + // -v, --verbose (计数) + scc_argparse_opt_t opt_verbose; + scc_argparse_opt_init(&opt_verbose, 'V', "verbose", + scc_hints[SCC_HINT_VERBOSE]); + scc_argparse_spec_setup_count(&opt_verbose.spec, &(config->verbose)); + scc_argparse_cmd_add_opt(root, &opt_verbose); + + scc_argparse_opt_t opt_emit; + scc_argparse_opt_init(&opt_emit, 0, "emit", scc_hints[SCC_HINT_EMIT_STAGE]); + static const char *emit_stages[] = { + [SCC_EMIT_STAGE_LEX] = "lex", + [SCC_EMIT_STAGE_PP] = "pp", + + [SCC_EMIT_STAGE_AST] = "ast", // -T + [SCC_EMIT_STAGE_HIR] = "hir", // -H + [SCC_EMIT_STAGE_LIR] = "lir", // -L + [SCC_EMIT_STAGE_MIR] = "mir", // -M + + [SCC_EMIT_STAGE_MIR_PASS_REG_ALLOC] = "", + [SCC_EMIT_STAGE_MIR_PASS_FLAME_LAYOUT] = "", + [SCC_EMIT_STAGE_MIR_PASS_PROLOG_EPILOG] = "", + [SCC_EMIT_STAGE_FLATBIN] = "flatbin", + [SCC_EMIT_STAGE_SCCF] = "sccf", + [SCC_EMIT_STAGE_TARGET] = "target", + }; + scc_argparse_spec_setup_choices(&opt_emit.spec, emit_stages, + SCC_ARRLEN(emit_stages), + (int *)&(config->emit_stage)); + scc_argparse_cmd_add_opt(root, &opt_emit); +} diff --git a/src/config.h b/src/config.h index 9e70c01..58c1e91 100644 --- a/src/config.h +++ b/src/config.h @@ -3,6 +3,25 @@ #include +typedef enum scc_emit_stage { + SCC_EMIT_STAGE_LEX, + SCC_EMIT_STAGE_PP, + SCC_EMIT_STAGE_AST, + SCC_EMIT_STAGE_HIR, + SCC_EMIT_STAGE_LIR, + SCC_EMIT_STAGE_MIR, + + SCC_EMIT_STAGE_MIR_PASS_REG_ALLOC, + SCC_EMIT_STAGE_MIR_PASS_FLAME_LAYOUT, + SCC_EMIT_STAGE_MIR_PASS_PROLOG_EPILOG, + + SCC_EMIT_STAGE_FLATBIN, + SCC_EMIT_STAGE_SCCF, + SCC_EMIT_STAGE_TARGET, + + SCC_EMIT_STAGE_DEFAULT, +} scc_emit_stage_t; + typedef struct { const char *input_file; const char *output_file; @@ -11,224 +30,11 @@ typedef struct { scc_argparse_list_t include_paths; scc_argparse_list_t define_macros; const char *entry_point_symbol; - cbool emit_lex; - cbool emit_pp; - cbool emit_ast; - cbool emit_hir; - cbool emit_lir; - cbool emit_mir; - cbool emit_flatbin; - cbool emit_sccf; - cbool emit_target; - cbool emit_mir_pass_reg_alloc; - cbool emit_mir_pass_stack_layout; - cbool emit_mir_pass_prolog_epilog; + scc_emit_stage_t emit_stage; } scc_config_t; -static void setup_argparse(scc_argparse_t *argparse, scc_config_t *config, - scc_argparse_lang_t lang) { - enum { - SCC_HINT_PROG_NAME, - SCC_HINT_DESCRIPTION, - SCC_HINT_OUTPUT_FILE, - SCC_HINT_INPUT_FILE, - SCC_HINT_INCLUDE_PATH, - SCC_HINT_DEFINED_MACRO, - SCC_HINT_TARGET_DESC, - SCC_HINT_VERBOSE, - - SCC_HINT_ENTRY_POINT_SYMBOL, - - SCC_HINT_EMIT_LEX, - SCC_HINT_EMIT_PP, - SCC_HINT_EMIT_AST, - SCC_HINT_EMIT_HIR, - SCC_HINT_EMIT_LIR, - SCC_HINT_EMIT_MIR, - - SCC_HINT_EMIT_FLATBIN, - SCC_HINT_EMIT_SCCF, - SCC_HINT_EMIT_TARGET, - }; - static const char *scc_hints_en[] = { - [SCC_HINT_PROG_NAME] = "scc", - [SCC_HINT_DESCRIPTION] = "A simple C compiler", - [SCC_HINT_OUTPUT_FILE] = - "Output file (`-` means standard output stream file)", - [SCC_HINT_INPUT_FILE] = "Input source file", - [SCC_HINT_INCLUDE_PATH] = "Add directory to the include search paths", - [SCC_HINT_DEFINED_MACRO] = "Define a macro", - [SCC_HINT_TARGET_DESC] = - "Target description(eg. x86_64-pc-windows-msvc)", - [SCC_HINT_VERBOSE] = "Increase verbosity (can be used multiple times)", - - [SCC_HINT_ENTRY_POINT_SYMBOL] = "Entry point symbol name", - - [SCC_HINT_EMIT_LEX] = "Generate lexer sources tokens and exit", - [SCC_HINT_EMIT_PP] = "Generate preprocessed tokens and exit", - [SCC_HINT_EMIT_AST] = "Generate AST and exit", - [SCC_HINT_EMIT_HIR] = "Generate High-level IR and exit", - [SCC_HINT_EMIT_LIR] = "Generate Low-level IR and exit", - [SCC_HINT_EMIT_MIR] = "Generate Machine IR and exit", - - [SCC_HINT_EMIT_FLATBIN] = "Generate flat binary and exit", - [SCC_HINT_EMIT_SCCF] = "Generate SCCF and exit", - [SCC_HINT_EMIT_TARGET] = "Generate target description and exit", - - }; - static const char *scc_hints_zh[] = { - [SCC_HINT_PROG_NAME] = "scc", - [SCC_HINT_DESCRIPTION] = "一个简单的C编译器", - [SCC_HINT_OUTPUT_FILE] = "输出文件(`-`表示标准输出流文件)", - [SCC_HINT_INPUT_FILE] = "输入源文件", - [SCC_HINT_INCLUDE_PATH] = "添加系统头文件到搜索路径", - [SCC_HINT_DEFINED_MACRO] = "定义宏", - [SCC_HINT_TARGET_DESC] = "目标机器描述(eg. x86_64-pc-windows-msvc)", - [SCC_HINT_VERBOSE] = "增加详细输出(可多次使用)", - - [SCC_HINT_ENTRY_POINT_SYMBOL] = "入口点符号名称", - - [SCC_HINT_EMIT_LEX] = "生成`源代码的词法单元`并退出", - [SCC_HINT_EMIT_PP] = "生成`预处理后的词法单元`并退出", - [SCC_HINT_EMIT_AST] = "生成`抽象语法树`并退出", - [SCC_HINT_EMIT_HIR] = "生成`高级中间代码`并退出", - [SCC_HINT_EMIT_LIR] = "生成`低级中间代码`并退出", - [SCC_HINT_EMIT_MIR] = "生成`机器中间代码`并退出", - - [SCC_HINT_EMIT_FLATBIN] = "生成`flat binary`并退出", - [SCC_HINT_EMIT_SCCF] = "生成`SCCF`并退出", - [SCC_HINT_EMIT_TARGET] = "生成`目标代码`并退出", - }; - - const char **scc_hints; - switch (lang) { - case SCC_ARGPARSE_LANG_EN: - scc_hints = scc_hints_en; - break; - case SCC_ARGPARSE_LANG_ZH: - scc_hints = scc_hints_zh; - break; - default: - scc_hints = scc_hints_en; - break; - } - - scc_argparse_init(argparse, scc_hints[SCC_HINT_PROG_NAME], - scc_hints[SCC_HINT_DESCRIPTION]); - argparse->lang = lang; - scc_argparse_cmd_t *root = scc_argparse_get_root(argparse); - - // -o, --output - scc_argparse_opt_t opt_output; - scc_argparse_opt_init(&opt_output, 'o', "output", - scc_hints[SCC_HINT_OUTPUT_FILE]); - scc_argparse_spec_setup_string(&opt_output.spec, &(config->output_file)); - scc_argparse_cmd_add_opt(root, &opt_output); - - // input file (必需) - scc_argparse_arg_t arg_input; - scc_argparse_arg_init(&arg_input, "input", scc_hints[SCC_HINT_INPUT_FILE]); - scc_argparse_spec_setup_string(&arg_input.spec, &(config->input_file)); - scc_argparse_spec_set_required(&arg_input.spec, true); - scc_argparse_cmd_add_arg(root, &arg_input); - - // -I, --include (添加额外的系统头文件搜索路径) - scc_argparse_opt_t opt_include; - scc_argparse_opt_init(&opt_include, 'I', "include", - scc_hints[SCC_HINT_INCLUDE_PATH]); - scc_argparse_spec_setup_list(&opt_include.spec, &(config->include_paths)); - scc_argparse_cmd_add_opt(root, &opt_include); - - // -D, --define (定义宏) - scc_argparse_opt_t opt_define; - scc_argparse_opt_init(&opt_define, 'D', "define", - scc_hints[SCC_HINT_DEFINED_MACRO]); - scc_argparse_spec_setup_list(&opt_define.spec, &(config->define_macros)); - scc_argparse_cmd_add_opt(root, &opt_define); - - // --entry-point-symbol (设置入口点符号名称) - scc_argparse_opt_t opt_entry_point_symbol; - scc_argparse_opt_init(&opt_entry_point_symbol, 0, "entry-point-symbol", - scc_hints[SCC_HINT_ENTRY_POINT_SYMBOL]); - scc_argparse_spec_setup_string(&opt_entry_point_symbol.spec, - &(config->entry_point_symbol)); - scc_argparse_cmd_add_opt(root, &opt_entry_point_symbol); - - // --target - scc_argparse_opt_t opt_target; - scc_argparse_opt_init(&opt_target, 0, "target", - scc_hints[SCC_HINT_TARGET_DESC]); - scc_argparse_spec_setup_string(&opt_target.spec, - &(config->target_description)); - scc_argparse_cmd_add_opt(root, &opt_target); - - // -v, --verbose (计数) - scc_argparse_opt_t opt_verbose; - scc_argparse_opt_init(&opt_verbose, 'V', "verbose", - scc_hints[SCC_HINT_VERBOSE]); - scc_argparse_spec_setup_count(&opt_verbose.spec, &(config->verbose)); - scc_argparse_cmd_add_opt(root, &opt_verbose); - - // --emit-lex - scc_argparse_opt_t opt_lex; - scc_argparse_opt_init(&opt_lex, 0, "emit-lex", - scc_hints[SCC_HINT_EMIT_LEX]); - scc_argparse_spec_setup_bool(&opt_lex.spec, &(config->emit_lex)); - scc_argparse_cmd_add_opt(root, &opt_lex); - - // --emit-pp - scc_argparse_opt_t opt_pp; - scc_argparse_opt_init(&opt_pp, 0, "emit-pp", scc_hints[SCC_HINT_EMIT_PP]); - scc_argparse_spec_setup_bool(&opt_pp.spec, &(config->emit_pp)); - scc_argparse_cmd_add_opt(root, &opt_pp); - - // -T, --emit-ast - scc_argparse_opt_t opt_ast; - scc_argparse_opt_init(&opt_ast, 'T', "emit-ast", - scc_hints[SCC_HINT_EMIT_AST]); - scc_argparse_spec_setup_bool(&opt_ast.spec, &(config->emit_ast)); - scc_argparse_cmd_add_opt(root, &opt_ast); - - // -H, --emit-hir - scc_argparse_opt_t opt_hir; - scc_argparse_opt_init(&opt_hir, 'H', "emit-hir", - scc_hints[SCC_HINT_EMIT_HIR]); - scc_argparse_spec_setup_bool(&opt_hir.spec, &(config->emit_hir)); - scc_argparse_cmd_add_opt(root, &opt_hir); - - // -L, --emit-lir - scc_argparse_opt_t opt_lir; - scc_argparse_opt_init(&opt_lir, 'L', "emit-lir", - scc_hints[SCC_HINT_EMIT_LIR]); - scc_argparse_spec_setup_bool(&opt_lir.spec, &(config->emit_lir)); - scc_argparse_cmd_add_opt(root, &opt_lir); - - // -M, --emit-mir - scc_argparse_opt_t opt_mir; - scc_argparse_opt_init(&opt_mir, 'M', "emit-mir", - scc_hints[SCC_HINT_EMIT_MIR]); - scc_argparse_spec_setup_bool(&opt_mir.spec, &(config->emit_mir)); - scc_argparse_cmd_add_opt(root, &opt_mir); - - // --emit-flatbin - scc_argparse_opt_t opt_flatbin; - scc_argparse_opt_init(&opt_flatbin, 0, "emit-flatbin", - scc_hints[SCC_HINT_EMIT_FLATBIN]); - scc_argparse_spec_setup_bool(&opt_flatbin.spec, &(config->emit_flatbin)); - scc_argparse_cmd_add_opt(root, &opt_flatbin); - // --emit-sccf - scc_argparse_opt_t opt_sccf; - scc_argparse_opt_init(&opt_sccf, 0, "emit-sccf", - scc_hints[SCC_HINT_EMIT_SCCF]); - scc_argparse_spec_setup_bool(&opt_sccf.spec, &(config->emit_sccf)); - scc_argparse_cmd_add_opt(root, &opt_sccf); - // --emit-target - scc_argparse_opt_t opt_emit_target; - scc_argparse_opt_init(&opt_emit_target, 0, "emit-target", - scc_hints[SCC_HINT_EMIT_TARGET]); - scc_argparse_spec_setup_bool(&opt_emit_target.spec, &(config->emit_target)); - scc_argparse_cmd_add_opt(root, &opt_emit_target); -} +void setup_argparse(scc_argparse_t *argparse, scc_config_t *config, + scc_argparse_lang_t lang); #endif /* __SCC_CONFIG_H___ */ diff --git a/src/main.c b/src/main.c index 8256fd4..aed0a98 100644 --- a/src/main.c +++ b/src/main.c @@ -89,8 +89,7 @@ int main(int argc, const char **argv, const char **envp) { .verbose = 0, .output_file = nullptr, .entry_point_symbol = nullptr, - .emit_ast = false, - .emit_hir = false, + .emit_stage = SCC_EMIT_STAGE_DEFAULT, .target_description = "x86_64-pc-windows-msvc", }; scc_vec_init(config.include_paths); @@ -129,7 +128,7 @@ int main(int argc, const char **argv, const char **envp) { scc_lexer_t lexer; scc_lexer_init(&lexer, scc_sstream_to_ring(&sstream)); - if (config.emit_lex) { + if (config.emit_stage == SCC_EMIT_STAGE_LEX) { scc_lexer_tok_ring_t *tok_ring = scc_lexer_to_ring(&lexer, 8, fp == nullptr ? false : true); if (fp == nullptr) { @@ -173,7 +172,7 @@ int main(int argc, const char **argv, const char **envp) { scc_pproc_add_object_macro(&pproc.macro_table, &pproc_predefined_macros[i], &pproc_tok_vec); } - if (config.emit_pp) { + if (config.emit_stage == SCC_EMIT_STAGE_PP) { scc_lexer_tok_ring_t *tok_ring = scc_pproc_to_ring(&pproc, 8, true, true); if (fp == nullptr) { @@ -211,7 +210,7 @@ sstream_drop: return error_code; } - if (config.emit_ast) { + if (config.emit_stage == SCC_EMIT_STAGE_AST) { scc_tree_dump_t tree_dump; if (fp == nullptr) { scc_tree_dump_init(&tree_dump, true); @@ -232,7 +231,7 @@ sstream_drop: scc_ast2ir_run(&ast2ir_ctx, translation_unit); scc_ast2ir_ctx_drop(&ast2ir_ctx); - if (config.emit_hir) { + if (config.emit_stage == SCC_EMIT_STAGE_HIR) { scc_hir_dump_t ir_dump_ctx; scc_tree_dump_t tree_dump; if (fp == nullptr) { @@ -252,7 +251,7 @@ sstream_drop: scc_lir_module_t lir_module; scc_lir_module_init(&lir_module); scc_hir2lir(&lir_module, &cprog); - if (config.emit_lir) { + if (config.emit_stage == SCC_EMIT_STAGE_LIR) { scc_lir_dump_ctx_t lir_dump_ctx; scc_tree_dump_t tree_dump; if (fp == nullptr) { @@ -272,7 +271,7 @@ sstream_drop: scc_mir_module_t mir_module; scc_mir_module_init(&mir_module); scc_lir2mir(&mir_module, &lir_module); - if (config.emit_mir) { + if (config.emit_stage == SCC_EMIT_STAGE_MIR) { scc_mir_dump_ctx_t mir_dump_ctx; scc_tree_dump_t tree_dump; if (fp == nullptr) { @@ -288,7 +287,7 @@ sstream_drop: return 0; } - if (config.emit_flatbin) { + if (config.emit_stage == SCC_EMIT_STAGE_FLATBIN) { scc_mcode_t mcode = {0}; scc_mcode_init(&mcode, SCC_MCODE_ARCH_X86_64); scc_ir2mcode(&mcode, &mir_module); @@ -311,18 +310,31 @@ sstream_drop: scc_ir2sccf(&sccf_builder, &mir_module); sccf_builder_set_entry_symbol_name(&sccf_builder, config.entry_point_symbol); - if (config.emit_sccf) { + const sccf_t *sccf = sccf_builder_to_sccf(&sccf_builder); + if (config.emit_stage == SCC_EMIT_STAGE_SCCF) { + sccf_buffer_t buffer; + scc_vec_init(buffer); + sccf_builder_to_buffer(&sccf_builder, &buffer); + if (fp == nullptr) { + scc_printf("output exe at %s\n", config.output_file); + } else { + sccf_builder_to_file(&sccf_builder, config.output_file); + } return 0; } - const sccf_t *sccf = sccf_builder_to_sccf(&sccf_builder); - scc_pe_builder_t pe_builder; - sccf2pe(&pe_builder, sccf); - if (fp == nullptr) { - scc_printf("output exe at %s\n", config.output_file); - } else { - scc_pe_dump_to_file(&pe_builder, config.output_file); + if (config.emit_stage == SCC_EMIT_STAGE_DEFAULT || + config.emit_stage == SCC_EMIT_STAGE_TARGET) { + scc_pe_builder_t pe_builder; + sccf2pe(&pe_builder, sccf); + if (fp == nullptr) { + scc_printf("output exe at %s\n", config.output_file); + } else { + scc_pe_dump_to_file(&pe_builder, config.output_file); + } + return 0; } - return 0; + Panic("unknown emit stage"); + return 1; }