diff --git a/libs/abi/cbuild.toml b/libs/abi/cbuild.toml deleted file mode 100644 index 44eaa40..0000000 --- a/libs/abi/cbuild.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "scc_abi" -version = "0.1.0" -authors = [] -description = "" - -# dependencies = [] -# features = {} -# default_features = [] diff --git a/libs/ast2ir/cbuild.toml b/libs/ast2ir/cbuild.toml index 78b8d71..3809437 100644 --- a/libs/ast2ir/cbuild.toml +++ b/libs/ast2ir/cbuild.toml @@ -7,7 +7,6 @@ description = "" dependencies = [ { name = "scc_ast", path = "../ast" }, { name = "scc_hir", path = "../ir/hir" }, - { name = "scc_abi", path = "../abi" }, ] # features = {} # default_features = [] diff --git a/libs/ast2ir/include/scc_ast2ir.h b/libs/ast2ir/include/scc_ast2ir.h index f2e7559..d049f38 100644 --- a/libs/ast2ir/include/scc_ast2ir.h +++ b/libs/ast2ir/include/scc_ast2ir.h @@ -38,6 +38,10 @@ void scc_ast2ir_stmt(scc_ast2ir_ctx_t *ctx, const scc_ast_stmt_t *stmt); scc_hir_type_ref_t scc_ast2ir_type(scc_ast2ir_ctx_t *ctx, const scc_ast_qual_type_t *ast_type); +scc_hir_type_ref_t +scc_ast2ir_parse_base_type(scc_ast2ir_ctx_t *ctx, + const scc_ast_qual_type_t *ast_type); + // ====== 类型提升(Type Promotion)接口 ====== /** @@ -47,16 +51,17 @@ scc_hir_type_ref_t scc_ast2ir_type(scc_ast2ir_ctx_t *ctx, * @return 提升后的类型的引用,若无需提升则返回原始类型 */ scc_hir_type_ref_t scc_ast2ir_integer_promotion(scc_ast2ir_ctx_t *ctx, - scc_hir_type_ref_t type_ref); + scc_hir_type_ref_t type_ref); /** * @brief 寻常算术转换(C11 6.3.1.8) * 对二元算术操作的两个操作数类型找到公共类型 * @return 公共类型的引用 */ -scc_hir_type_ref_t scc_ast2ir_usual_arithmetic_conversion( - scc_ast2ir_ctx_t *ctx, scc_hir_type_ref_t t1_ref, - scc_hir_type_ref_t t2_ref); +scc_hir_type_ref_t +scc_ast2ir_usual_arithmetic_conversion(scc_ast2ir_ctx_t *ctx, + scc_hir_type_ref_t t1_ref, + scc_hir_type_ref_t t2_ref); /** * @brief 插入类型转换指令 @@ -64,7 +69,25 @@ scc_hir_type_ref_t scc_ast2ir_usual_arithmetic_conversion( * @return 转换后的值引用 */ scc_hir_value_ref_t scc_ast2ir_emit_conversion(scc_ast2ir_ctx_t *ctx, - scc_hir_value_ref_t value, - scc_hir_type_ref_t target_type); + scc_hir_value_ref_t value, + scc_hir_type_ref_t target_type); + +// ====== 常量表达式求值 ====== + +/** + * @brief 对整数常量表达式求值(C11 6.6) + * + * 递归计算 AST 表达式,得到一个编译期可确定的整数值。 + * 支持:整数字面量、字符字面量、枚举常量、sizeof / _Alignof、 + * 一元运算(+ - ~ !)、二元运算(算术/位/移位/关系/逻辑)、 + * 条件表达式(?:)、强制类型转换。 + * + * @param ctx ast2ir 上下文 + * @param result 输出参数,存放求值结果 + * @param expr 待求值的 AST 表达式 + * @return 成功返回 true;若表达式不是合法的常量表达式则返回 false + */ +cbool scc_ast2ir_eval_constant_int(scc_ast2ir_ctx_t *ctx, scc_ap_t *result, + const scc_ast_expr_t *expr); #endif /* __SCC_AST2IR_H__ */ diff --git a/libs/abi/include/scc_type_abi.h b/libs/ast2ir/include/scc_type_abi.h similarity index 100% rename from libs/abi/include/scc_type_abi.h rename to libs/ast2ir/include/scc_type_abi.h diff --git a/libs/abi/include/target/scc_abi_dummy.h b/libs/ast2ir/include/target/scc_abi_dummy.h similarity index 100% rename from libs/abi/include/target/scc_abi_dummy.h rename to libs/ast2ir/include/target/scc_abi_dummy.h diff --git a/libs/abi/include/target/scc_abi_win_x64_pc.h b/libs/ast2ir/include/target/scc_abi_win_x64_pc.h similarity index 100% rename from libs/abi/include/target/scc_abi_win_x64_pc.h rename to libs/ast2ir/include/target/scc_abi_win_x64_pc.h diff --git a/libs/ast2ir/src/scc_ast2ir.c b/libs/ast2ir/src/scc_ast2ir.c index fa78e9f..3cbd5c6 100644 --- a/libs/ast2ir/src/scc_ast2ir.c +++ b/libs/ast2ir/src/scc_ast2ir.c @@ -24,29 +24,6 @@ static int fixed_param_count(const scc_ast_canon_type_t *canon) { return n; } -static scc_hir_type_ref_t parse_base_type(scc_ast2ir_ctx_t *ctx, - const scc_ast_qual_type_t *ast_type) { - scc_abi_type_layout_t layout; - // 映射内置类型 - ctx->abi->compute_type_layout(ctx->abi, (void *)ast_type, &layout); - switch (layout.size) { - case 0: - return scc_hir_builder_type_void(&ctx->builder); - case 1: - return scc_hir_builder_type_i8(&ctx->builder); - case 2: - return scc_hir_builder_type_i16(&ctx->builder); - case 4: - return scc_hir_builder_type_i32(&ctx->builder); - case 8: - return scc_hir_builder_type_i64(&ctx->builder); - break; - default: - break; - } - return SCC_HIR_REF_nullptr; -} - static inline bool scc_hir_type_is_signed(scc_hir_type_tag_t tag) { switch (tag) { case SCC_HIR_TYPE_i8: @@ -69,6 +46,7 @@ static inline bool scc_hir_type_is_signed(scc_hir_type_tag_t tag) { // 判断是否为算术类型(整数或浮点) static cbool is_arithmetic_type(const scc_hir_type_t *type) { + Assert(type != nullptr); switch (type->tag) { case SCC_HIR_TYPE_i8: case SCC_HIR_TYPE_i16: @@ -235,7 +213,7 @@ scc_hir_type_ref_t scc_ast2ir_type(scc_ast2ir_ctx_t *ctx, switch (ast_type->base.type) { case SCC_AST_TYPE_BUILTIN: { - return parse_base_type(ctx, ast_type); + return scc_ast2ir_parse_base_type(ctx, ast_type); } case SCC_AST_TYPE_POINTER: { scc_hir_type_init(&ir_type, SCC_HIR_TYPE_PTR); @@ -253,16 +231,11 @@ scc_hir_type_ref_t scc_ast2ir_type(scc_ast2ir_ctx_t *ctx, ir_type.data.array.base = element_type; ir_type.data.array.len = 0; if (scc_ast_canon_type(ast_type)->array.size) { - // TODO constant expression - if (scc_ast_canon_type(ast_type)->array.size->base.type != - SCC_AST_EXPR_INT_LITERAL) { - Panic("TODO: array size expression"); - } scc_ap_t value; - scc_ap_from_cstr( - &value, scc_ast_canon_type(ast_type)->array.size->literal.lexme, - 10); - // FIXME hack + if (!scc_ast2ir_eval_constant_int( + ctx, &value, scc_ast_canon_type(ast_type)->array.size)) { + Panic("array size is not a constant expression"); + } ir_type.data.array.len = value.data.digit; } } break; @@ -359,7 +332,7 @@ scc_hir_type_ref_t scc_ast2ir_type(scc_ast2ir_ctx_t *ctx, .base.type = SCC_AST_TYPE_BUILTIN, .type = int_canon_type, }; - return parse_base_type(ctx, &int_type); + return scc_ast2ir_parse_base_type(ctx, &int_type); case SCC_AST_TYPE_TYPEDEF: // TODO maybe using cache return scc_ast2ir_type( @@ -569,7 +542,8 @@ scc_hir_value_ref_t scc_ast2ir_expr(scc_ast2ir_ctx_t *ctx, rhs = scc_ast2ir_emit_conversion( ctx, rhs, lhs_ptr_type->data.pointer.base); } - return scc_hir_builder_store(&ctx->builder, lhs, rhs); + scc_hir_builder_store(&ctx->builder, lhs, rhs); + return rhs; } if (expr->binary.op == SCC_AST_OP_LOGICAL_AND || @@ -577,30 +551,36 @@ scc_hir_value_ref_t scc_ast2ir_expr(scc_ast2ir_ctx_t *ctx, // TODO 类型提升 return scc_ast2ir_logical_expr(ctx, expr); } - rhs = scc_ast2ir_expr(ctx, expr->binary.rhs, false); - lhs = scc_ast2ir_expr(ctx, expr->binary.lhs, false); + if (expr->binary.op == SCC_AST_OP_COMMA) { + lhs = scc_ast2ir_expr(ctx, expr->binary.lhs, false); + rhs = scc_ast2ir_expr(ctx, expr->binary.rhs, false); + } else { + rhs = scc_ast2ir_expr(ctx, expr->binary.rhs, false); + lhs = scc_ast2ir_expr(ctx, expr->binary.lhs, false); + } // 类型提升:对算术类型二元操作应用寻常算术转换(C11 6.3.1.8) // 移位操作符不适用(仅对每个操作数单独整数提升) cbool is_shift = (expr->binary.op == SCC_AST_OP_LEFT_SHIFT || expr->binary.op == SCC_AST_OP_RIGHT_SHIFT); + scc_hir_type_ref_t common_type = 0; if (!is_shift && expr->binary.op != SCC_AST_OP_COMMA) { scc_hir_module_t *module = scc_ast2ir_mir_module(ctx); scc_hir_type_ref_t lhs_type_ref = scc_hir_module_get_value(module, lhs)->type; scc_hir_type_ref_t rhs_type_ref = scc_hir_module_get_value(module, rhs)->type; + Assert(lhs_type_ref != 0 && rhs_type_ref != 0); scc_hir_type_t *lhs_type = scc_hir_module_get_type(module, lhs_type_ref); scc_hir_type_t *rhs_type = scc_hir_module_get_type(module, rhs_type_ref); if (is_arithmetic_type(lhs_type) && is_arithmetic_type(rhs_type)) { - scc_hir_type_ref_t common = - scc_ast2ir_usual_arithmetic_conversion(ctx, lhs_type_ref, - rhs_type_ref); - if (common != 0) { - lhs = scc_ast2ir_emit_conversion(ctx, lhs, common); - rhs = scc_ast2ir_emit_conversion(ctx, rhs, common); + common_type = scc_ast2ir_usual_arithmetic_conversion( + ctx, lhs_type_ref, rhs_type_ref); + if (common_type != 0) { + lhs = scc_ast2ir_emit_conversion(ctx, lhs, common_type); + rhs = scc_ast2ir_emit_conversion(ctx, rhs, common_type); } } } else if (is_shift) { @@ -628,16 +608,42 @@ scc_hir_value_ref_t scc_ast2ir_expr(scc_ast2ir_ctx_t *ctx, case SCC_AST_OP_BITWISE_XOR: op = SCC_HIR_OP_XOR; break; case SCC_AST_OP_LEFT_SHIFT: op = SCC_HIR_OP_SHL; break; case SCC_AST_OP_RIGHT_SHIFT: { - op = SCC_HIR_OP_SHR; - // FIXME op = SCC_HIR_OP_SAR; + scc_hir_module_t *module = scc_ast2ir_mir_module(ctx); + scc_hir_type_ref_t type_ref = scc_hir_module_get_value(module, lhs)->type; + scc_hir_type_t *type = scc_hir_module_get_type(module, type_ref); + op = scc_hir_type_is_signed(type->tag) ? SCC_HIR_OP_SAR : SCC_HIR_OP_SHR; break; } case SCC_AST_OP_EQUAL: op = SCC_HIR_OP_EQ; break; case SCC_AST_OP_NOT_EQUAL: op = SCC_HIR_OP_NEQ; break; - case SCC_AST_OP_LESS: op = SCC_HIR_OP_LT; break; - case SCC_AST_OP_LESS_EQUAL: op = SCC_HIR_OP_LE; break; - case SCC_AST_OP_GREATER: op = SCC_HIR_OP_GT; break; - case SCC_AST_OP_GREATER_EQUAL: op = SCC_HIR_OP_GE; break; + case SCC_AST_OP_LESS: { + scc_hir_module_t *module = scc_ast2ir_mir_module(ctx); + scc_hir_type_ref_t tr = common_type ? common_type : scc_hir_module_get_value(module, lhs)->type; + scc_hir_type_t *t = scc_hir_module_get_type(module, tr); + op = scc_hir_type_is_signed(t->tag) ? SCC_HIR_OP_LT : SCC_HIR_OP_ULT; + break; + } + case SCC_AST_OP_LESS_EQUAL: { + scc_hir_module_t *module = scc_ast2ir_mir_module(ctx); + scc_hir_type_ref_t tr = common_type ? common_type : scc_hir_module_get_value(module, lhs)->type; + scc_hir_type_t *t = scc_hir_module_get_type(module, tr); + op = scc_hir_type_is_signed(t->tag) ? SCC_HIR_OP_LE : SCC_HIR_OP_ULE; + break; + } + case SCC_AST_OP_GREATER: { + scc_hir_module_t *module = scc_ast2ir_mir_module(ctx); + scc_hir_type_ref_t tr = common_type ? common_type : scc_hir_module_get_value(module, lhs)->type; + scc_hir_type_t *t = scc_hir_module_get_type(module, tr); + op = scc_hir_type_is_signed(t->tag) ? SCC_HIR_OP_GT : SCC_HIR_OP_UGT; + break; + } + case SCC_AST_OP_GREATER_EQUAL: { + scc_hir_module_t *module = scc_ast2ir_mir_module(ctx); + scc_hir_type_ref_t tr = common_type ? common_type : scc_hir_module_get_value(module, lhs)->type; + scc_hir_type_t *t = scc_hir_module_get_type(module, tr); + op = scc_hir_type_is_signed(t->tag) ? SCC_HIR_OP_GE : SCC_HIR_OP_UGE; + break; + } case SCC_AST_OP_COMMA: { // 逗号运算符:计算左表达式,丢弃结果,返回右表达式 return rhs; @@ -854,28 +860,24 @@ scc_hir_value_ref_t scc_ast2ir_expr(scc_ast2ir_ctx_t *ctx, scc_hir_value_ref_vec_t args; scc_vec_init(args); - // 获取函数类型信息用于参数转换。注意:不能长期持有指向 types 向量 - // 内部的指针(如 ft->data.function.params),因为递归的 - // scc_ast2ir_expr 可能触发 scc_hir_module_add_type 导致 realloc。 - // 改为通过 func_type_ref(稳定的索引)每次需要时重新获取指针。 + // 获取函数类型信息用于参数转换。 scc_hir_func_ref_t callee_func = (scc_hir_func_ref_t)(usize)scc_hashtable_get( &ctx->symtab, expr->call.callee->identifier._target->name); scc_hir_type_ref_t func_type_ref = SCC_HIR_REF_nullptr; cbool is_variadic = false; int fixed_count = 0; - if (callee_func != SCC_HIR_REF_nullptr) { - scc_hir_func_t *hir_func = scc_hir_module_get_func( - scc_ast2ir_mir_module(ctx), callee_func); - scc_hir_func_meta_t *meta = SCC_HIR_FUNC_META(hir_func); - is_variadic = meta->is_variadic; - func_type_ref = meta->type; - if (func_type_ref != SCC_HIR_REF_nullptr) { - const scc_hir_type_t *ft = scc_hir_module_get_type( - scc_ast2ir_mir_module(ctx), func_type_ref); - if (ft != nullptr && ft->tag == SCC_HIR_TYPE_FUNC) - fixed_count = (int)ft->data.function.params.size; - } + Assert(callee_func != SCC_HIR_REF_nullptr); + scc_hir_func_t *hir_func = + scc_hir_module_get_func(scc_ast2ir_mir_module(ctx), callee_func); + scc_hir_func_meta_t *meta = SCC_HIR_FUNC_META(hir_func); + is_variadic = meta->is_variadic; + func_type_ref = meta->type; + if (func_type_ref != SCC_HIR_REF_nullptr) { + const scc_hir_type_t *ft = scc_hir_module_get_type( + scc_ast2ir_mir_module(ctx), func_type_ref); + if (ft != nullptr && ft->tag == SCC_HIR_TYPE_FUNC) + fixed_count = (int)ft->data.function.params.size; } scc_vec_foreach(expr->call.args, i) { @@ -897,8 +899,11 @@ scc_hir_value_ref_t scc_ast2ir_expr(scc_ast2ir_ctx_t *ctx, // 可变参数:只应用默认参数提升(C11 6.5.2.2) // 整数提升:char/short → int scc_hir_module_t *module = scc_ast2ir_mir_module(ctx); - scc_hir_type_ref_t arg_type_ref = - scc_hir_module_get_value(module, arg_node)->type; + + scc_hir_value_t *arg_value = + scc_hir_module_get_value(module, arg_node); + Assert(arg_value != nullptr); + scc_hir_type_ref_t arg_type_ref = arg_value->type; Assert(arg_type_ref != 0); scc_hir_type_tag_t arg_tag = scc_hir_module_get_type(module, arg_type_ref)->tag; @@ -1136,7 +1141,7 @@ scc_hir_value_ref_t scc_ast2ir_expr(scc_ast2ir_ctx_t *ctx, // FIXME maybe using some array to int; scc_hir_type_ref_t type_ref = scc_hir_builder_type_i32(&ctx->builder); scc_ap_t value; - scc_ap_from_cstr(&value, expr->literal.lexme, 10); + scc_ap_from_cstr(&value, expr->literal.lexme, 0); return scc_hir_builder_integer(&ctx->builder, type_ref, &value); } // SCC_AST_EXPR_FLOAT_LITERAL, // 浮点字面量 @@ -1655,24 +1660,23 @@ void scc_ast2ir_decl(scc_ast2ir_ctx_t *ctx, const scc_ast_decl_t *decl, } break; case SCC_AST_DECL_ENUM: { scc_ap_t val; - int idx = 0; - scc_ap_set_int(&val, idx); + scc_ap_set_int(&val, 0); scc_vec_foreach(decl->record.fields, i) { scc_ast_decl_t *item = scc_vec_at(decl->record.fields, i); Assert(item->base.type == SCC_AST_DECL_VAR); if (item->var.init) { - Assert(item->var.init->base.type == SCC_AST_EXPR_INT_LITERAL); - scc_ap_from_cstr(&val, item->var.init->literal.lexme, 10); + if (!scc_ast2ir_eval_constant_int(ctx, &val, item->var.init)) { + Panic("enum value is not a constant expression"); + } } scc_hir_value_ref_t item_val_ref = scc_hir_builder_integer( &ctx->builder, scc_hir_builder_type_i32(&ctx->builder), &val); scc_hashtable_set(&ctx->ast2ir_cache, item, (void *)(usize)item_val_ref); - // FIXME hack ap - idx = val.data.digit; - idx += 1; - // scc_ap_add 1 (); - scc_ap_set_int(&val, idx); + /* next = val + 1 */ + scc_ap_t one; + scc_ap_set_int(&one, 1); + scc_ap_add(&val, &val, &one); } } break; case SCC_AST_DECL_TYPEDEF: diff --git a/libs/ast2ir/src/scc_ast2ir_const.c b/libs/ast2ir/src/scc_ast2ir_const.c new file mode 100644 index 0000000..08ee055 --- /dev/null +++ b/libs/ast2ir/src/scc_ast2ir_const.c @@ -0,0 +1,381 @@ +#include +#include +#include + +/* ---------- internal helpers ---------- */ + +static cbool is_builtin_signed(scc_ast_builtin_type_t t) { + switch (t) { + case SCC_AST_BUILTIN_TYPE_BOOL: + case SCC_AST_BUILTIN_TYPE_CHAR: /* implementation-defined, treat as signed */ + case SCC_AST_BUILTIN_TYPE_SHORT: + case SCC_AST_BUILTIN_TYPE_INT: + case SCC_AST_BUILTIN_TYPE_LONG: + case SCC_AST_BUILTIN_TYPE_LONG_LONG: + case SCC_AST_BUILTIN_TYPE_SIGNED_CHAR: + case SCC_AST_BUILTIN_TYPE_SIGNED_SHORT: + case SCC_AST_BUILTIN_TYPE_SIGNED_INT: + case SCC_AST_BUILTIN_TYPE_SIGNED_LONG: + case SCC_AST_BUILTIN_TYPE_SIGNED_LONG_LONG: + return true; + default: + return false; + } +} + +static cbool is_builtin_unsigned(scc_ast_builtin_type_t t) { + switch (t) { + case SCC_AST_BUILTIN_TYPE_UNSIGNED_CHAR: + case SCC_AST_BUILTIN_TYPE_UNSIGNED_SHORT: + case SCC_AST_BUILTIN_TYPE_UNSIGNED_INT: + case SCC_AST_BUILTIN_TYPE_UNSIGNED_LONG: + case SCC_AST_BUILTIN_TYPE_UNSIGNED_LONG_LONG: + return true; + default: + return false; + } +} + +/* Check whether an AST qual-type is an integer type (not pointer/float/struct) */ +static cbool is_ast_integer_type(const scc_ast_qual_type_t *type) { + if (type->base.type != SCC_AST_TYPE_BUILTIN) + return false; + scc_ast_builtin_type_t bt = scc_ast_canon_type(type)->builtin.type; + return bt >= SCC_AST_BUILTIN_TYPE_BOOL && + bt <= SCC_AST_BUILTIN_TYPE_UNSIGNED_LONG_LONG; +} + +/* + * Truncate / sign-extend an AP value to match a target integer type width. + * Uses the ABI calculator to determine the type's size in bytes. + */ +static cbool apply_integer_truncation(scc_ast2ir_ctx_t *ctx, scc_ap_t *val, + const scc_ast_qual_type_t *target_type) { + if (!is_ast_integer_type(target_type)) + return false; + + scc_abi_type_layout_t layout; + ctx->abi->compute_type_layout(ctx->abi, (void *)target_type, &layout); + int bits = layout.size * 8; + if (bits >= (int)sizeof(scc_ap_digit) * 8) + return true; /* no truncation needed */ + + /* Extract magnitude */ + uint64_t mag = val->data.digit; + uint64_t mask = ((uint64_t)1 << bits) - 1; + uint64_t truncated = mag & mask; + + /* For signed types, sign-extend if the MSB is set */ + scc_ast_builtin_type_t bt = scc_ast_canon_type(target_type)->builtin.type; + if (is_builtin_signed(bt) && (truncated >> (bits - 1))) + truncated |= ~mask; /* set all bits above bits-1 */ + + val->data.digit = truncated; + val->len = (val->len < 0 && truncated == 0) ? 1 : val->len; + return true; +} + +/* ---------- unary expression ---------- */ + +static cbool eval_unary(scc_ast2ir_ctx_t *ctx, scc_ap_t *result, + const scc_ast_expr_t *expr) { + scc_ap_t val; + scc_ap_init(&val); + + if (!scc_ast2ir_eval_constant_int(ctx, &val, expr->unary.operand)) + return false; + + switch (expr->unary.op) { + case SCC_AST_OP_UNARY_PLUS: + result->capacity = -1; + result->len = val.len; + result->data.digit = val.data.digit; + return true; + case SCC_AST_OP_UNARY_MINUS: + scc_ap_neg(result, &val); + return true; + case SCC_AST_OP_BITWISE_NOT: + scc_ap_not(result, &val); + return true; + case SCC_AST_OP_LOGICAL_NOT: + scc_ap_set_int(result, scc_ap_is_zero(&val)); + return true; + default: + return false; + } +} + +/* ---------- binary expression ---------- */ + +static cbool eval_binary(scc_ast2ir_ctx_t *ctx, scc_ap_t *result, + const scc_ast_expr_t *expr) { + scc_ap_t lhs_val, rhs_val; + scc_ap_init(&lhs_val); + scc_ap_init(&rhs_val); + + if (!scc_ast2ir_eval_constant_int(ctx, &lhs_val, expr->binary.lhs)) + return false; + if (!scc_ast2ir_eval_constant_int(ctx, &rhs_val, expr->binary.rhs)) + return false; + + switch (expr->binary.op) { + /* arithmetic */ + case SCC_AST_OP_ADD: + scc_ap_add(result, &lhs_val, &rhs_val); + return true; + case SCC_AST_OP_SUB: + scc_ap_sub(result, &lhs_val, &rhs_val); + return true; + case SCC_AST_OP_MUL: + scc_ap_mul(result, &lhs_val, &rhs_val); + return true; + case SCC_AST_OP_DIV: + scc_ap_div(result, &lhs_val, &rhs_val); + return true; + case SCC_AST_OP_MOD: + scc_ap_mod(result, &lhs_val, &rhs_val); + return true; + /* bitwise */ + case SCC_AST_OP_BITWISE_AND: + scc_ap_and(result, &lhs_val, &rhs_val); + return true; + case SCC_AST_OP_BITWISE_OR: + scc_ap_or(result, &lhs_val, &rhs_val); + return true; + case SCC_AST_OP_BITWISE_XOR: + scc_ap_xor(result, &lhs_val, &rhs_val); + return true; + /* shift */ + case SCC_AST_OP_LEFT_SHIFT: + scc_ap_shl(result, &lhs_val, (unsigned)rhs_val.data.digit); + return true; + case SCC_AST_OP_RIGHT_SHIFT: + scc_ap_shr(result, &lhs_val, (unsigned)rhs_val.data.digit); + return true; + /* relational */ + case SCC_AST_OP_EQUAL: + scc_ap_set_int(result, scc_ap_cmp(&lhs_val, &rhs_val) == 0); + return true; + case SCC_AST_OP_NOT_EQUAL: + scc_ap_set_int(result, scc_ap_cmp(&lhs_val, &rhs_val) != 0); + return true; + case SCC_AST_OP_LESS: + scc_ap_set_int(result, scc_ap_cmp(&lhs_val, &rhs_val) < 0); + return true; + case SCC_AST_OP_GREATER: + scc_ap_set_int(result, scc_ap_cmp(&lhs_val, &rhs_val) > 0); + return true; + case SCC_AST_OP_LESS_EQUAL: + scc_ap_set_int(result, scc_ap_cmp(&lhs_val, &rhs_val) <= 0); + return true; + case SCC_AST_OP_GREATER_EQUAL: + scc_ap_set_int(result, scc_ap_cmp(&lhs_val, &rhs_val) >= 0); + return true; + /* logical */ + case SCC_AST_OP_LOGICAL_AND: + if (scc_ap_is_zero(&lhs_val)) { + scc_ap_set_int(result, 0); + } else { + scc_ap_set_int(result, !scc_ap_is_zero(&rhs_val)); + } + return true; + case SCC_AST_OP_LOGICAL_OR: + if (!scc_ap_is_zero(&lhs_val)) { + scc_ap_set_int(result, 1); + } else { + scc_ap_set_int(result, !scc_ap_is_zero(&rhs_val)); + } + return true; + default: + return false; + } +} + +/* ---------- identifier (enum constant lookup) ---------- */ + +static cbool eval_identifier(scc_ast2ir_ctx_t *ctx, scc_ap_t *result, + const scc_ast_expr_t *expr) { + if (expr->identifier._target == nullptr) + return false; + + /* Try the ast2ir_cache — enum values are stored there as HIR integer + * constants during scc_ast2ir_decl(SCC_AST_DECL_ENUM) */ + scc_hir_value_ref_t cached = (scc_hir_value_ref_t)(usize)scc_hashtable_get( + &ctx->ast2ir_cache, expr->identifier._target); + if (cached != 0) { + scc_hir_value_t *hir_val = + scc_hir_module_get_value(scc_ast2ir_mir_module(ctx), cached); + if (hir_val != nullptr && hir_val->tag == SCC_HIR_VALUE_TAG_INTEGER) { + /* shallow copy — digit mode has no heap allocation */ + result->capacity = -1; + result->len = hir_val->data.integer.len; + result->data.digit = hir_val->data.integer.data.digit; + return true; + } + } + + /* If not cached yet, evaluate the var init recursively. + * This handles enum-internal forward references within a single enum decl: + * enum { A = 1, B = A + 1 }; — when evaluating B, A may not be cached + * yet, but we can look at A's init expression. */ + if (expr->identifier._target->base.type == SCC_AST_DECL_VAR && + expr->identifier._target->var.init != nullptr) { + return scc_ast2ir_eval_constant_int(ctx, result, + expr->identifier._target->var.init); + } + + return false; +} + +/* ---------- character literal evaluation ---------- */ + +static int eval_char_literal(const scc_ast_expr_t *expr) { + const char *lexme = expr->literal.lexme; + Assert(lexme[0] == '\''); + + if (lexme[1] != '\\') + return (unsigned char)lexme[1]; + + switch (lexme[2]) { + case 'a': + return '\a'; + case 'b': + return '\b'; + case 'f': + return '\f'; + case 'n': + return '\n'; + case 'r': + return '\r'; + case 't': + return '\t'; + case 'v': + return '\v'; + case '\\': + return '\\'; + case '\'': + return '\''; + case '"': + return '"'; + case '0': + return '\0'; + case 'x': + /* hex escape: \xNN — parse up to 2 hex digits */ + { + int val = 0; + int j = 3; + while (1) { + char c = lexme[j]; + unsigned d; + if (c >= '0' && c <= '9') + d = c - '0'; + else if (c >= 'a' && c <= 'f') + d = c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + d = c - 'A' + 10; + else + break; + val = val * 16 + d; + j++; + } + return val; + } + default: + /* octal: \nnn */ + if (lexme[2] >= '0' && lexme[2] <= '7') { + int val = 0; + int j = 2; + while (j < 5 && lexme[j] >= '0' && lexme[j] <= '7') { + val = val * 8 + (lexme[j] - '0'); + j++; + } + return val; + } + return (unsigned char)lexme[2]; + } +} + +/* =================================================================== + * Public API + * =================================================================== */ + +cbool scc_ast2ir_eval_constant_int(scc_ast2ir_ctx_t *ctx, scc_ap_t *result, + const scc_ast_expr_t *expr) { + if (ctx == nullptr || result == nullptr || expr == nullptr) + return false; + + scc_ap_init(result); + + switch (expr->base.type) { + case SCC_AST_EXPR_INT_LITERAL: + scc_ap_from_cstr(result, expr->literal.lexme, 0); + return true; + + case SCC_AST_EXPR_CHAR_LITERAL: + scc_ap_set_int(result, eval_char_literal(expr)); + return true; + + case SCC_AST_EXPR_BINARY: + return eval_binary(ctx, result, expr); + + case SCC_AST_EXPR_UNARY: + return eval_unary(ctx, result, expr); + + case SCC_AST_EXPR_COND: { + scc_ap_t cond_val; + scc_ap_init(&cond_val); + if (!scc_ast2ir_eval_constant_int(ctx, &cond_val, expr->cond.cond)) + return false; + return scc_ast2ir_eval_constant_int(ctx, result, + scc_ap_is_zero(&cond_val) + ? expr->cond.else_expr + : expr->cond.then_expr); + } + + case SCC_AST_EXPR_CAST: { + if (!scc_ast2ir_eval_constant_int(ctx, result, expr->cast.expr)) + return false; + /* Truncate / sign-extend to the target type width */ + if (!apply_integer_truncation(ctx, result, expr->cast.type)) + return false; + return true; + } + + case SCC_AST_EXPR_SIZE_OF: { + scc_abi_type_layout_t layout; + if (expr->attr_of.type) { + ctx->abi->compute_type_layout(ctx->abi, (void *)expr->attr_of.type, + &layout); + } else if (expr->attr_of.expr) { + /* sizeof expression: try to get the type through the ABI */ + /* For constant expressions, sema should have annotated the type */ + /* TODO: handle sizeof(expr) where expr is not a type */ + return false; + } else { + return false; + } + scc_ap_set_int(result, layout.size); + return true; + } + + case SCC_AST_EXPR_ALIGN_OF: { + scc_abi_type_layout_t layout; + if (expr->attr_of.type) { + ctx->abi->compute_type_layout(ctx->abi, (void *)expr->attr_of.type, + &layout); + } else if (expr->attr_of.expr) { + return false; + } else { + return false; + } + scc_ap_set_int(result, layout.alignment); + return true; + } + + case SCC_AST_EXPR_IDENTIFIER: + return eval_identifier(ctx, result, expr); + + default: + return false; + } +} diff --git a/libs/ast2ir/src/scc_ast2ir_promote.c b/libs/ast2ir/src/scc_ast2ir_promote.c index 4f24179..f0fd806 100644 --- a/libs/ast2ir/src/scc_ast2ir_promote.c +++ b/libs/ast2ir/src/scc_ast2ir_promote.c @@ -179,9 +179,10 @@ scc_hir_type_ref_t scc_ast2ir_integer_promotion(scc_ast2ir_ctx_t *ctx, * d. 如果有符号类型可以表示无符号类型的所有值,无符号转换为有符号 * e. 否则,两个都转换为有符号类型的无符号版本 */ -scc_hir_type_ref_t scc_ast2ir_usual_arithmetic_conversion( - scc_ast2ir_ctx_t *ctx, scc_hir_type_ref_t t1_ref, - scc_hir_type_ref_t t2_ref) { +scc_hir_type_ref_t +scc_ast2ir_usual_arithmetic_conversion(scc_ast2ir_ctx_t *ctx, + scc_hir_type_ref_t t1_ref, + scc_hir_type_ref_t t2_ref) { scc_hir_module_t *module = scc_ast2ir_mir_module(ctx); scc_hir_type_t *t1 = scc_hir_module_get_type(module, t1_ref); scc_hir_type_t *t2 = scc_hir_module_get_type(module, t2_ref); @@ -200,13 +201,9 @@ scc_hir_type_ref_t scc_ast2ir_usual_arithmetic_conversion( scc_hir_type_ref_t p2_ref = scc_ast2ir_integer_promotion(ctx, t2_ref); scc_hir_type_tag_t tag1 = - (p1_ref != 0) - ? scc_hir_module_get_type(module, p1_ref)->tag - : t1->tag; + (p1_ref != 0) ? scc_hir_module_get_type(module, p1_ref)->tag : t1->tag; scc_hir_type_tag_t tag2 = - (p2_ref != 0) - ? scc_hir_module_get_type(module, p2_ref)->tag - : t2->tag; + (p2_ref != 0) ? scc_hir_module_get_type(module, p2_ref)->tag : t2->tag; // 如果提升后相同,直接返回 if (tag1 == tag2) @@ -252,6 +249,7 @@ scc_hir_type_ref_t scc_ast2ir_usual_arithmetic_conversion( * - 返回 -1 表示无需转换(同类型同大小) */ static int determine_conv_kind(scc_hir_type_tag_t src, scc_hir_type_tag_t dst) { + Assert(SCC_HIR_TYPE_VALID(src) && SCC_HIR_TYPE_VALID(dst)); if (src == dst) return -1; @@ -302,10 +300,8 @@ scc_hir_value_ref_t scc_ast2ir_emit_conversion(scc_ast2ir_ctx_t *ctx, scc_hir_value_ref_t value, scc_hir_type_ref_t target_type) { scc_hir_module_t *module = scc_ast2ir_mir_module(ctx); - scc_hir_type_t *src_type = - scc_hir_module_get_type_by_value(module, value); - scc_hir_type_t *dst_type = - scc_hir_module_get_type(module, target_type); + scc_hir_type_t *src_type = scc_hir_module_get_type_by_value(module, value); + scc_hir_type_t *dst_type = scc_hir_module_get_type(module, target_type); if (src_type == nullptr || dst_type == nullptr) return value; diff --git a/libs/ast2ir/src/scc_ast2ir_type.c b/libs/ast2ir/src/scc_ast2ir_type.c new file mode 100644 index 0000000..3ab7a51 --- /dev/null +++ b/libs/ast2ir/src/scc_ast2ir_type.c @@ -0,0 +1,76 @@ +#include + +scc_hir_type_ref_t +scc_ast2ir_parse_base_type(scc_ast2ir_ctx_t *ctx, + const scc_ast_qual_type_t *ast_type) { + scc_abi_type_layout_t layout; + // 映射内置类型 + ctx->abi->compute_type_layout(ctx->abi, (void *)ast_type, &layout); + switch (scc_ast_canon_type(ast_type)->builtin.type) { + // FIXME it not a void + case SCC_AST_BUILTIN_TYPE_VA_LIST: + case SCC_AST_BUILTIN_TYPE_VOID: + return scc_hir_builder_type_void(&ctx->builder); + case SCC_AST_BUILTIN_TYPE_BOOL: + case SCC_AST_BUILTIN_TYPE_SIGNED_CHAR: + case SCC_AST_BUILTIN_TYPE_SHORT: + case SCC_AST_BUILTIN_TYPE_SIGNED_SHORT: + case SCC_AST_BUILTIN_TYPE_INT: + case SCC_AST_BUILTIN_TYPE_SIGNED_INT: + case SCC_AST_BUILTIN_TYPE_LONG: + case SCC_AST_BUILTIN_TYPE_SIGNED_LONG: + case SCC_AST_BUILTIN_TYPE_LONG_LONG: + case SCC_AST_BUILTIN_TYPE_SIGNED_LONG_LONG: + switch (layout.size) { + case 1: + return scc_hir_builder_type_i8(&ctx->builder); + case 2: + return scc_hir_builder_type_i16(&ctx->builder); + case 4: + return scc_hir_builder_type_i32(&ctx->builder); + case 8: + return scc_hir_builder_type_i64(&ctx->builder); + break; + default: + break; + } + break; + case SCC_AST_BUILTIN_TYPE_CHAR: + // TODO CHAR TO BE UNSIGNED CHAR + case SCC_AST_BUILTIN_TYPE_UNSIGNED_CHAR: + case SCC_AST_BUILTIN_TYPE_UNSIGNED_SHORT: + case SCC_AST_BUILTIN_TYPE_UNSIGNED_INT: + case SCC_AST_BUILTIN_TYPE_UNSIGNED_LONG: + case SCC_AST_BUILTIN_TYPE_UNSIGNED_LONG_LONG: + switch (layout.size) { + case 1: + return scc_hir_builder_type_u8(&ctx->builder); + case 2: + return scc_hir_builder_type_u16(&ctx->builder); + case 4: + return scc_hir_builder_type_u32(&ctx->builder); + case 8: + return scc_hir_builder_type_u64(&ctx->builder); + break; + default: + break; + } + break; + case SCC_AST_BUILTIN_TYPE_FLOAT: + case SCC_AST_BUILTIN_TYPE_DOUBLE: + switch (layout.size) { + case 4: + return scc_hir_builder_type_f32(&ctx->builder); + case 8: + return scc_hir_builder_type_f64(&ctx->builder); + break; + default: + break; + } + default: + Panic("Unsupported AST type: %d", + scc_ast_canon_type(ast_type)->builtin.type); + break; + } + return SCC_HIR_REF_nullptr; +} diff --git a/libs/abi/src/scc_type_abi.c b/libs/ast2ir/src/scc_type_abi.c similarity index 100% rename from libs/abi/src/scc_type_abi.c rename to libs/ast2ir/src/scc_type_abi.c diff --git a/libs/ir/hir/include/scc_hir_def.h b/libs/ir/hir/include/scc_hir_def.h index 4b2d81f..92b9362 100644 --- a/libs/ir/hir/include/scc_hir_def.h +++ b/libs/ir/hir/include/scc_hir_def.h @@ -48,7 +48,9 @@ typedef enum scc_hir_type_tag { SCC_HIR_TYPE_STRUCT, SCC_HIR_TYPE_UNION, SCC_HIR_TYPE_VECTOR, // TODO SIMD + SCC_HIR_TYPE_COUNT, } scc_hir_type_tag_t; +#define SCC_HIR_TYPE_VALID(tag) ((tag) < SCC_HIR_TYPE_COUNT && (tag) >= 0) struct scc_hir_type { scc_hir_type_tag_t tag; @@ -134,6 +136,14 @@ typedef enum { SCC_HIR_OP_SHR, /// Shift right arithmetic. SCC_HIR_OP_SAR, + /// Unsigned greater than. + SCC_HIR_OP_UGT, + /// Unsigned less than. + SCC_HIR_OP_ULT, + /// Unsigned greater than or equal to. + SCC_HIR_OP_UGE, + /// Unsigned less than or equal to. + SCC_HIR_OP_ULE, } scc_hir_op_type_t; typedef enum { diff --git a/libs/ir/hir/src/scc_hir_dump.c b/libs/ir/hir/src/scc_hir_dump.c index f9a5dc3..01c1ac2 100644 --- a/libs/ir/hir/src/scc_hir_dump.c +++ b/libs/ir/hir/src/scc_hir_dump.c @@ -49,7 +49,9 @@ static const char *get_op_str(scc_hir_op_type_t op) { [SCC_HIR_OP_AND] = "&", [SCC_HIR_OP_OR] = "|", [SCC_HIR_OP_XOR] = "^", [SCC_HIR_OP_NOT] = "~", [SCC_HIR_OP_SHL] = "<<", [SCC_HIR_OP_SHR] = ">>", - [SCC_HIR_OP_SAR] = ">>a", + [SCC_HIR_OP_SAR] = ">>a", [SCC_HIR_OP_ULT] = "u<", + [SCC_HIR_OP_ULE] = "u<=", [SCC_HIR_OP_UGT] = "u>", + [SCC_HIR_OP_UGE] = "u>=", }; if (op >= 0 && (usize)op < sizeof(ops) / sizeof(ops[0]) && ops[op]) return ops[op]; diff --git a/libs/ir/lir/src/scc_hir2lir.c b/libs/ir/lir/src/scc_hir2lir.c index 3fdefdc..2e04f06 100644 --- a/libs/ir/lir/src/scc_hir2lir.c +++ b/libs/ir/lir/src/scc_hir2lir.c @@ -174,7 +174,6 @@ static scc_lir_cond_t map_cmp_cond(scc_hir_op_type_t op, cbool is_float) { Panic("invalid float cmp"); } } else { - // 默认为有符号比较 (无符号需额外处理类型) switch (op) { case SCC_HIR_OP_EQ: return SCC_LIR_COND_EQ; @@ -188,6 +187,14 @@ static scc_lir_cond_t map_cmp_cond(scc_hir_op_type_t op, cbool is_float) { return SCC_LIR_COND_SGT; case SCC_HIR_OP_GE: return SCC_LIR_COND_SGE; + case SCC_HIR_OP_ULT: + return SCC_LIR_COND_ULT; + case SCC_HIR_OP_ULE: + return SCC_LIR_COND_ULE; + case SCC_HIR_OP_UGT: + return SCC_LIR_COND_UGT; + case SCC_HIR_OP_UGE: + return SCC_LIR_COND_UGE; default: Panic("invalid int cmp"); } @@ -196,6 +203,54 @@ static scc_lir_cond_t map_cmp_cond(scc_hir_op_type_t op, cbool is_float) { } static scc_lir_op_t map_binop(scc_hir_op_type_t op, cbool is_float) { + switch (op) { + case SCC_HIR_OP_EMPTY: + return SCC_LIR_NOP; + /// Not equal to. + case SCC_HIR_OP_NEQ: + /// Equal to. + case SCC_HIR_OP_EQ: + /// Greater than. + case SCC_HIR_OP_GT: + /// Less than. + case SCC_HIR_OP_LT: + /// Greater than or equal to. + case SCC_HIR_OP_GE: + /// Less than or equal to. + case SCC_HIR_OP_LE: + /// Unsigned greater than. + case SCC_HIR_OP_UGT: + /// Unsigned less than. + case SCC_HIR_OP_ULT: + /// Unsigned greater than or equal to. + case SCC_HIR_OP_UGE: + /// Unsigned less than or equal to. + case SCC_HIR_OP_ULE: + return SCC_LIR_CMP; + /// Bitwise AND. + case SCC_HIR_OP_AND: + return SCC_LIR_AND; + /// Bitwise OR. + case SCC_HIR_OP_OR: + return SCC_LIR_OR; + /// Bitwise XOR. + case SCC_HIR_OP_XOR: + return SCC_LIR_XOR; + /// Bitwise NOT. + case SCC_HIR_OP_NOT: + return SCC_LIR_NOT; + /// Shift left logical. + case SCC_HIR_OP_SHL: + return SCC_LIR_SHL; + /// Shift right logical. + case SCC_HIR_OP_SHR: + return SCC_LIR_SHR; + /// Shift right arithmetic. + case SCC_HIR_OP_SAR: + return SCC_LIR_SAR; + default: + break; + } if (is_float) { switch (op) { case SCC_HIR_OP_ADD: @@ -206,6 +261,9 @@ static scc_lir_op_t map_binop(scc_hir_op_type_t op, cbool is_float) { return SCC_LIR_FMUL; case SCC_HIR_OP_DIV: return SCC_LIR_FDIV; + case SCC_HIR_OP_MOD: + TODO(); + return SCC_LIR_FNEG; default: Panic("unsupported float binop"); } @@ -221,20 +279,8 @@ static scc_lir_op_t map_binop(scc_hir_op_type_t op, cbool is_float) { return SCC_LIR_DIV_S; case SCC_HIR_OP_MOD: return SCC_LIR_REM_S; - case SCC_HIR_OP_AND: - return SCC_LIR_AND; - case SCC_HIR_OP_OR: - return SCC_LIR_OR; - case SCC_HIR_OP_XOR: - return SCC_LIR_XOR; - case SCC_HIR_OP_SHL: - return SCC_LIR_SHL; - case SCC_HIR_OP_SHR: - return SCC_LIR_SHR; - case SCC_HIR_OP_SAR: - return SCC_LIR_SAR; default: - return SCC_LIR_CMP; + Panic("unsupported binop"); } } return SCC_LIR_NOP; @@ -277,7 +323,10 @@ static void translate_hir_value(ir2lir_ctx_t *ctx, scc_hir_value_t *value, switch (value->tag) { case SCC_HIR_VALUE_TAG_OP: { scc_lir_val_t lhs = ir_value_to_lir_operand(ctx, value->data.op.lhs); - scc_lir_val_t rhs = ir_value_to_lir_operand(ctx, value->data.op.rhs); + scc_lir_val_t rhs = SCC_LIR_NONE(); + if (value->data.op.rhs != SCC_HIR_REF_nullptr) { + rhs = ir_value_to_lir_operand(ctx, value->data.op.rhs); + } scc_lir_op_t op = map_binop(value->data.op.op, is_float); scc_lir_instr_t instr = { @@ -308,9 +357,18 @@ static void translate_hir_value(ir2lir_ctx_t *ctx, scc_hir_value_t *value, ir_value_to_lir_operand(ctx, value->data.store.value); scc_lir_val_t addr = ir_value_to_lir_operand(ctx, value->data.store.target); - ty = scc_hir_module_get_type_by_value(ctx->hir_module, - value->data.store.value); - ir_type_to_lir_size_ext(ty, &size, &ext); + /* 修复: 使用目标指针的元素类型确定 store 宽度而非源值类型 */ + scc_hir_type_t *ptr_type = scc_hir_module_get_type_by_value( + ctx->hir_module, value->data.store.target); + if (ptr_type && ptr_type->tag == SCC_HIR_TYPE_PTR) { + scc_hir_type_t *elem_type = scc_hir_module_get_type( + ctx->hir_module, ptr_type->data.pointer.base); + ir_type_to_lir_size_ext(elem_type, &size, &ext); + } else { + ty = scc_hir_module_get_type_by_value(ctx->hir_module, + value->data.store.value); + ir_type_to_lir_size_ext(ty, &size, &ext); + } scc_lir_instr_t instr = {.op = SCC_LIR_STORE, .ext = ext, .size = size, @@ -446,10 +504,9 @@ static void translate_hir_value(ir2lir_ctx_t *ctx, scc_hir_value_t *value, } else { // 计算源类型宽度 scc_hir_type_t *src_type = scc_hir_module_get_type( - ctx->hir_module, - scc_hir_module_get_value(ctx->hir_module, - value->data.conv.operand) - ->type); + ctx->hir_module, scc_hir_module_get_value( + ctx->hir_module, value->data.conv.operand) + ->type); int from_size = scc_hir_module_type_size(ctx->hir_module, src_type); scc_lir_instr_t instr = {.op = SCC_LIR_EXTEND, .size = size, diff --git a/libs/ir/mir/include/arch/scc_x86_reg_alloc.h b/libs/ir/mir/include/arch/scc_x86_reg_alloc.h index 5ceecda..d9c5f28 100644 --- a/libs/ir/mir/include/arch/scc_x86_reg_alloc.h +++ b/libs/ir/mir/include/arch/scc_x86_reg_alloc.h @@ -3,6 +3,15 @@ #include "../core_pass/scc_reg_alloc.h" +/* 默认位掩码策略:R8/R9 被排除(ABI 参数寄存器),剩余 6 个通用寄存器 */ void scc_reg_alloc_fill_arch_x86(scc_reg_alloc_op_t *ops); +/* + * 顺序分配策略:光标递增,永不回收。 + * 池中包含 R8-R15(共 8 个寄存器),每分配一个光标前进一格。 + * release_reg 为空操作;寄存器耗尽后 acquire_reg 返回 -1, + * 由分配器回退为纯栈操作。 + */ +void scc_reg_alloc_fill_seq_x86(scc_reg_alloc_op_t *ops); + #endif /* __SCC_X86_REG_ALLOC_H__ */ diff --git a/libs/ir/mir/src/arch/scc_x86_isel.c b/libs/ir/mir/src/arch/scc_x86_isel.c index da141bc..1e02098 100644 --- a/libs/ir/mir/src/arch/scc_x86_isel.c +++ b/libs/ir/mir/src/arch/scc_x86_isel.c @@ -148,6 +148,17 @@ static scc_x86_operand_value_t new_vreg_temp(scc_x86_64_isel_t *isel, return scc_x86_op_vreg(scc_mir_alloc_vreg(isel->func), size); } +/* 确保操作数为寄存器: IMM → 加载到临时虚拟寄存器 */ +static scc_x86_operand_value_t ensure_reg(scc_x86_64_isel_t *isel, + scc_x86_operand_value_t op) { + if (op.kind == SCC_X86_OPR_IMM) { + scc_x86_operand_value_t tmp = new_vreg_temp(isel, op.size); + scc_x86_emit_move(isel, tmp, op); + return tmp; + } + return op; +} + static scc_x86_iform_t cond_to_setcc(scc_lir_cond_t cond) { switch (cond) { case SCC_LIR_COND_EQ: @@ -343,9 +354,11 @@ static void sel_mir(scc_x86_64_isel_t *isel, const scc_lir_instr_t *instr) { break; /* ---- 一元运算 ---- */ case SCC_LIR_NEG: + emit_copy_if_needed(isel, dst, src0); add_instr_1(isel, SCC_X86_IFORM_NEG_GPRV, dst); break; case SCC_LIR_NOT: + emit_copy_if_needed(isel, dst, src0); add_instr_1(isel, SCC_X86_IFORM_NOT_GPRV, dst); break; /* ---- 算术/逻辑二元运算 ---- */ @@ -416,11 +429,18 @@ static void sel_mir(scc_x86_64_isel_t *isel, const scc_lir_instr_t *instr) { scc_x86_operand_value_t rax = scc_x86_op_preg(SCC_X86_REG_RAX, size); scc_x86_operand_value_t rdx = scc_x86_op_preg(SCC_X86_REG_RDX, size); - scc_x86_emit_move(isel, rax, src0); - if (instr->op == SCC_LIR_DIV_S || instr->op == SCC_LIR_REM_S) { + if (size < 8) { + // TODO 可能需要在lir进行size对齐 + scc_x86_operand_value_t rax64 = + scc_x86_op_preg(SCC_X86_REG_RAX, 8); + add_instr_2(isel, SCC_X86_IFORM_MOVSXD_GPRV_GPRZ, rax64, src0); + } else { + scc_x86_emit_move(isel, rax, src0); + } add_instr_0(isel, SCC_X86_IFORM_CQO); } else { + scc_x86_emit_move(isel, rax, src0); scc_x86_operand_value_t zero = scc_x86_op_imm(0, size); scc_x86_emit_move(isel, rdx, zero); } @@ -429,7 +449,12 @@ static void sel_mir(scc_x86_64_isel_t *isel, const scc_lir_instr_t *instr) { (instr->op == SCC_LIR_DIV_S || instr->op == SCC_LIR_REM_S) ? SCC_X86_IFORM_IDIV_GPRV : SCC_X86_IFORM_DIV_GPRV; - add_instr_1(isel, div_if, src1); + scc_x86_operand_value_t divisor = src1; + if (src1.kind == SCC_X86_OPR_IMM) { + divisor = new_vreg_temp(isel, size); + scc_x86_emit_move(isel, divisor, src1); + } + add_instr_1(isel, div_if, divisor); if (instr->op == SCC_LIR_REM_S || instr->op == SCC_LIR_REM_U) scc_x86_emit_move(isel, dst, rdx); @@ -440,6 +465,11 @@ static void sel_mir(scc_x86_64_isel_t *isel, const scc_lir_instr_t *instr) { /* ---- 比较指令 ---- */ case SCC_LIR_CMP: { Assert(src0.size == src1.size); + if (src0.kind == SCC_X86_OPR_IMM) { + scc_x86_operand_value_t tmp_reg = new_vreg_temp(isel, src0.size); + scc_x86_emit_move(isel, tmp_reg, src0); + src0 = tmp_reg; + } if (scc_x86_op_is_vreg(&src0) && src1.kind == SCC_X86_OPR_IMM) // SCC_X86_IFORM_CMP_GPR8_IMMB_82R7 历史遗留指令在amd64中已不再使用 add_instr_2(isel, @@ -511,6 +541,7 @@ static void sel_mir(scc_x86_64_isel_t *isel, const scc_lir_instr_t *instr) { int from_size = instr->metadata.extend.from_size; scc_x86_operand_value_t ext_src = scc_x86_lir_val_to_mir_op(isel, &instr->arg0, from_size); + ext_src = ensure_reg(isel, ext_src); if (instr->ext == SCC_LIR_EXT_ZEXT) { if (from_size == 4) { diff --git a/libs/ir/mir/src/arch/scc_x86_mir.c b/libs/ir/mir/src/arch/scc_x86_mir.c index 42b43ac..8667d85 100644 --- a/libs/ir/mir/src/arch/scc_x86_mir.c +++ b/libs/ir/mir/src/arch/scc_x86_mir.c @@ -42,12 +42,27 @@ scc_mir_x86_mov_mem_imm(scc_x86_operand_value_t op0, : SCC_X86_IFORM_MOV_MEMV_IMMZ; } +static inline u8 get_op_size(scc_x86_operand_value_t op, cbool get_value) { + if (get_value) { + return op.size; + } + if (op.kind == SCC_X86_OPR_MEM) { + return 8; + } + if (op.kind == SCC_X86_OPR_RELOC && op.reloc.kind == SCC_X86_OPR_MEM) { + return 8; + } + return op.size; +} + void scc_x86_emit_move_to_vec(scc_mir_x86_instr_vec_t *vec, scc_x86_operand_value_t dst, scc_x86_operand_value_t src) { - if (dst.size != src.size) { - LOG_WARN("Mismatched register sizes for move %d != %d", dst.size, - src.size); + u8 dst_size = get_op_size(dst, false); + u8 src_size = get_op_size(src, false); + if (dst_size != src_size) { + LOG_FATAL("Mismatched register sizes for move %d != %d", dst_size, + src_size); } scc_mir_x86_instr_t ins; if (dst.kind == SCC_X86_OPR_REG) { @@ -60,7 +75,7 @@ void scc_x86_emit_move_to_vec(scc_mir_x86_instr_vec_t *vec, } else if (src.kind == SCC_X86_OPR_MEM || (src.kind == SCC_X86_OPR_RELOC && src.reloc.target == SCC_X86_RELOC_TARGET_SYMBOL)) { - Assert(dst.size == 8); + Assert(dst_size == 8); scc_mir_x86_instr_2(&ins, SCC_X86_IFORM_LEA_GPRV_AGEN, dst, src, scc_pos_create()); } else { @@ -85,8 +100,11 @@ void scc_x86_emit_move_to_vec(scc_mir_x86_instr_vec_t *vec, void scc_x86_emit_load_to_vec(scc_mir_x86_instr_vec_t *vec, scc_x86_operand_value_t dst, scc_x86_operand_value_t src_addr) { - if (dst.size != src_addr.size) { - LOG_WARN("Mismatched sizes for load %d != %d", dst.size, src_addr.size); + u8 dst_size = get_op_size(dst, false); + u8 src_addr_size = get_op_size(src_addr, true); + if (dst_size != src_addr_size) { + LOG_FATAL("Mismatched sizes for load %d != %d", dst_size, + src_addr_size); } Assert(dst.kind == SCC_X86_OPR_REG); scc_x86_operand_value_t mem_op; @@ -99,9 +117,9 @@ void scc_x86_emit_load_to_vec(scc_mir_x86_instr_vec_t *vec, .index = SCC_X86_REG_INVALID, .scale = 1, .disp.displacement = 0, - .disp.displacement_bits = src_addr.size * 8, + .disp.displacement_bits = src_addr_size * 8, }, - .size = src_addr.size, + .size = src_addr_size, }; } else if (src_addr.kind == SCC_X86_OPR_MEM) { mem_op = src_addr; @@ -117,9 +135,11 @@ void scc_x86_emit_load_to_vec(scc_mir_x86_instr_vec_t *vec, void scc_x86_emit_store_to_vec(scc_mir_x86_instr_vec_t *vec, scc_x86_operand_value_t dst_addr, scc_x86_operand_value_t src) { - if (dst_addr.size != src.size) { - LOG_WARN("Mismatched sizes for store %d != %d", dst_addr.size, - src.size); + u8 dst_addr_size = get_op_size(dst_addr, true); + u8 src_size = get_op_size(src, false); + if (dst_addr_size != src_size) { + LOG_FATAL("Mismatched sizes for store %d != %d", dst_addr_size, + src_size); } scc_x86_operand_value_t mem_op; if (dst_addr.kind == SCC_X86_OPR_REG) { @@ -131,9 +151,9 @@ void scc_x86_emit_store_to_vec(scc_mir_x86_instr_vec_t *vec, .index = SCC_X86_REG_INVALID, .scale = 1, .disp.displacement = 0, - .disp.displacement_bits = dst_addr.size * 8, + .disp.displacement_bits = dst_addr_size * 8, }, - .size = dst_addr.size, + .size = dst_addr_size, }; } else if (dst_addr.kind == SCC_X86_OPR_MEM) { mem_op = dst_addr; diff --git a/libs/ir/mir/src/arch/scc_x86_reg_alloc.c b/libs/ir/mir/src/arch/scc_x86_reg_alloc.c index fbc303b..11e25c5 100644 --- a/libs/ir/mir/src/arch/scc_x86_reg_alloc.c +++ b/libs/ir/mir/src/arch/scc_x86_reg_alloc.c @@ -73,16 +73,25 @@ static void get_implicit_regs(void *ctx, int opcode, const int **uses, } } -/* ========== 寄存器池 ========== */ +/* ========== 默认寄存器池(位掩码策略) ========== */ -static const int reg_pool[] = { - SCC_X86_REG_R8, SCC_X86_REG_R9, SCC_X86_REG_R10, SCC_X86_REG_R11, - SCC_X86_REG_R12, SCC_X86_REG_R13, SCC_X86_REG_R14, SCC_X86_REG_R15}; +/* + * R8/R9 被排除在通用寄存器池之外,因为它们被 Win64 ABI 用作参数传递寄存器 + * (RCX=arg0, RDX=arg1, R8=arg2, R9=arg3)。 + * 如果把它们放在池中,当 lower_call 发出 MOV R8, vreg 时, + * 分配器可能把 R8 分配给源 vreg,导致冗余的 MOV R8, R8。 + * + * 可用寄存器: R10, R11, R12, R13, R14, R15 + */ +static const int reg_pool[] = {SCC_X86_REG_R10, SCC_X86_REG_R11, + SCC_X86_REG_R12, SCC_X86_REG_R13, + SCC_X86_REG_R14, SCC_X86_REG_R15}; +#define REG_POOL_SIZE ((int)SCC_ARRLEN(reg_pool)) static uint32_t reg_mask = 0; static int acquire_reg(void *ctx) { (void)ctx; - for (int i = 0; i < SCC_ARRLEN(reg_pool); i++) + for (int i = 0; i < REG_POOL_SIZE; i++) if (!(reg_mask & (1u << i))) { reg_mask |= (1u << i); return reg_pool[i]; @@ -92,7 +101,7 @@ static int acquire_reg(void *ctx) { static void release_reg(void *ctx, int preg) { (void)ctx; - for (int i = 0; i < SCC_ARRLEN(reg_pool); i++) + for (int i = 0; i < REG_POOL_SIZE; i++) if (reg_pool[i] == preg) { reg_mask &= ~(1u << i); return; @@ -101,7 +110,7 @@ static void release_reg(void *ctx, int preg) { static void mark_reg_used(void *ctx, int preg) { (void)ctx; - for (int i = 0; i < SCC_ARRLEN(reg_pool); i++) + for (int i = 0; i < REG_POOL_SIZE; i++) if (reg_pool[i] == preg) { reg_mask |= (1u << i); return; @@ -113,6 +122,57 @@ static void clean_mark_regs(void *ctx) { reg_mask = 0; } +/* ========== 顺序分配策略(光标,永不回收) ========== */ + +/* + * 顺序分配策略:按固定顺序分配寄存器(R8 → R9 → ... → R15), + * 一旦分配就永不回收(release_reg 为空操作)。 + * 当所有 8 个寄存器耗尽后,acquire_reg 返回 -1, + * 分配器将 vreg 直接替换为栈槽(spill/reload 由内存操作数隐式完成)。 + * + * 包含 R8/R9 是安全的,因为顺序分配避免了冲突: + * - 如果 R8 已被分配,参数 MOV R8, vreg 中的 vreg 会分配下一个可用寄存器(如 + * R10), 然后通过 MOV R8, R10 将值移入 R8。 + * - 如果 R8 尚未分配,参数 MOV R8, vreg 的 vreg 会分配到 R8, + * 产生 MOV R8, R8(空操作),这在功能上是正确的。 + */ +static const int seq_pool[] = { + SCC_X86_REG_R8, SCC_X86_REG_R9, SCC_X86_REG_R10, SCC_X86_REG_R11, + SCC_X86_REG_R12, SCC_X86_REG_R13, SCC_X86_REG_R14, SCC_X86_REG_R15}; +#define SEQ_POOL_SIZE ((int)SCC_ARRLEN(seq_pool)) +static int seq_cursor = 0; + +static int seq_acquire_reg(void *ctx) { + (void)ctx; + if (seq_cursor < SEQ_POOL_SIZE) + return seq_pool[seq_cursor++]; + return -1; /* 寄存器耗尽 */ +} + +static void seq_release_reg(void *ctx, int preg) { + (void)ctx; + (void)preg; + /* 永不回收:释放为空操作 */ +} + +static void seq_mark_reg_used(void *ctx, int preg) { + (void)ctx; + for (int i = seq_cursor; i < SEQ_POOL_SIZE; i++) + if (seq_pool[i] == preg) { + /* 将光标移到该寄存器之后(燃烧) */ + if (i + 1 > seq_cursor) + seq_cursor = i + 1; + return; + } +} + +static void seq_clean_mark_regs(void *ctx) { + (void)ctx; + seq_cursor = 0; +} + +/* ========== 指令迭代器(两种策略共享) ========== */ + static void x86_alloc_iter_begin(scc_reg_alloc_iter_t *iter) { iter->op_idx = 0; iter->op_sub_idx = 0; @@ -128,8 +188,8 @@ static cbool x86_alloc_iter_next(scc_reg_alloc_iter_t *iter, int *out_vreg, while (iter->op_idx < num_ops) { const scc_x86_operand_value_t *op = &ins->x86_instr.operands[iter->op_idx]; - scc_reg_op_access_t base_access = SCC_REG_ALLOC_OP_ACCESS_READWRITE; - // base_access = get_operand_access(opcode, iter->op_idx); + scc_reg_op_access_t base_access = + get_operand_access(opcode, iter->op_idx); *out_size = op->size; if (op->kind == SCC_X86_OPR_REG) { @@ -223,6 +283,8 @@ static void x86_alloc_iter_end(scc_reg_alloc_iter_t *iter) { (void)iter; // 无需清理 } +/* ========== 回调表填充 ========== */ + void scc_reg_alloc_fill_arch_x86(scc_reg_alloc_op_t *ops) { ops->acquire_reg = acquire_reg; ops->release_reg = release_reg; @@ -240,3 +302,21 @@ void scc_reg_alloc_fill_arch_x86(scc_reg_alloc_op_t *ops) { ops->emit_reload = emit_reload; ops->emit_copy = emit_copy; } + +void scc_reg_alloc_fill_seq_x86(scc_reg_alloc_op_t *ops) { + ops->acquire_reg = seq_acquire_reg; + ops->release_reg = seq_release_reg; + ops->mark_reg_used = seq_mark_reg_used; + ops->clean_mark_regs = seq_clean_mark_regs; + + ops->alloc_iter_begin = x86_alloc_iter_begin; + ops->alloc_iter_next = x86_alloc_iter_next; + ops->alloc_iter_replace_preg = x86_alloc_iter_replace_preg; + ops->alloc_iter_replace_slot = x86_alloc_iter_replace_slot; + ops->alloc_iter_end = x86_alloc_iter_end; + ops->get_implicit_regs = get_implicit_regs; + + ops->emit_spill = emit_spill; + ops->emit_reload = emit_reload; + ops->emit_copy = emit_copy; +} diff --git a/libs/ir/mir/src/reg_alloc/reg_alloc.c b/libs/ir/mir/src/reg_alloc/reg_alloc.c index d1073ef..86655eb 100644 --- a/libs/ir/mir/src/reg_alloc/reg_alloc.c +++ b/libs/ir/mir/src/reg_alloc/reg_alloc.c @@ -49,16 +49,21 @@ static void alloc_instr(scc_reg_alloc_ctx_t *ctx, if (mapping == 0) slot = scc_mir_vreg_map2slot(ctx->func, vreg, size, 8); preg = ops->acquire_reg(ctx); - scc_vec_push(allocated, preg); + if (preg >= 0) { + scc_vec_push(allocated, preg); - if (access == SCC_REG_ALLOC_OP_ACCESS_READ || - access == SCC_REG_ALLOC_OP_ACCESS_READWRITE) - ops->emit_reload(&before, preg, slot, size); - if (access == SCC_REG_ALLOC_OP_ACCESS_WRITE || - access == SCC_REG_ALLOC_OP_ACCESS_READWRITE) - ops->emit_spill(&after, preg, slot, size); + if (access == SCC_REG_ALLOC_OP_ACCESS_READ || + access == SCC_REG_ALLOC_OP_ACCESS_READWRITE) + ops->emit_reload(&before, preg, slot, size); + if (access == SCC_REG_ALLOC_OP_ACCESS_WRITE || + access == SCC_REG_ALLOC_OP_ACCESS_READWRITE) + ops->emit_spill(&after, preg, slot, size); - ops->alloc_iter_replace_preg(&iter, preg, size); + ops->alloc_iter_replace_preg(&iter, preg, size); + } else { + /* 寄存器耗尽(顺序策略):vreg 直接使用栈槽 */ + ops->alloc_iter_replace_slot(&iter, slot, size); + } } ops->alloc_iter_end(&iter); diff --git a/libs/ir/mir/src/target/win64_abi.c b/libs/ir/mir/src/target/win64_abi.c index a13da5b..6d54cca 100644 --- a/libs/ir/mir/src/target/win64_abi.c +++ b/libs/ir/mir/src/target/win64_abi.c @@ -171,6 +171,10 @@ void scc_win_pc_x64_prolog_epilog_init(scc_prolog_epilog_t *ctx) { ctx->need_epilog = need_epilog; } +/* +windows x64 calling convention +https://learn.microsoft.com/zh-cn/cpp/build/x64-calling-convention?view=msvc-180#varargs +*/ static void lower_call(void *userdata, const scc_lir_instr_t *instr) { scc_x86_64_isel_t *isel = userdata; diff --git a/runtime/ap/cbuild.toml b/runtime/ap/cbuild.toml index 45e6b27..a37bc79 100644 --- a/runtime/ap/cbuild.toml +++ b/runtime/ap/cbuild.toml @@ -1,9 +1,9 @@ [package] -name = "ap" -version = "0.1.0" -authors = [] +name = "ap" +version = "0.1.0" +authors = [] description = "" -# dependencies = [] +dependencies = [{ name = "core", path = "../scc_core" }] # features = {} # default_features = [] diff --git a/runtime/ap/include/ap.h b/runtime/ap/include/ap.h index 9b9e3e3..7b03c4b 100644 --- a/runtime/ap/include/ap.h +++ b/runtime/ap/include/ap.h @@ -4,7 +4,7 @@ * @brief Arbitrary Precision Library * */ -#ifdef __SCC__ +#ifndef __NO_SCC_RUNTIME__ #include #define SCC_AP_DIGIT u64 #define SCC_AP_PANIC Panic @@ -49,21 +49,21 @@ typedef struct { } scc_ap_t; static inline void scc_ap_init(scc_ap_t *ap) { - ap->len = 0; ap->capacity = -1; ap->len = 1; ap->data.digit = 0; } static inline void scc_ap_set_int(scc_ap_t *ap, int val) { + ap->capacity = -1; + SCC_AP_ASSERT(sizeof(scc_ap_digit) >= sizeof(int)); if (val < 0) { ap->len = -1; + ap->data.digit = (scc_ap_digit)(-(int64_t)val); } else { ap->len = 1; + ap->data.digit = (scc_ap_digit)val; } - SCC_AP_ASSERT(sizeof(scc_ap_digit) >= sizeof(int)); - ap->capacity = -1; - ap->data.digit = val; } void scc_ap_drop(scc_ap_t *ap); @@ -77,13 +77,20 @@ void scc_ap_mul(scc_ap_t *to, const scc_ap_t *from_a, const scc_ap_t *from_b); void scc_ap_div(scc_ap_t *to, const scc_ap_t *from_a, const scc_ap_t *from_b); void scc_ap_mod(scc_ap_t *to, const scc_ap_t *from_a, const scc_ap_t *from_b); -/** - * @brief equal - * - * @param a - * @param b - * @return int - */ +void scc_ap_shl(scc_ap_t *to, const scc_ap_t *from, unsigned shift); +void scc_ap_shr(scc_ap_t *to, const scc_ap_t *from, unsigned shift); +void scc_ap_lshr(scc_ap_t *to, const scc_ap_t *from, unsigned shift); + +void scc_ap_and(scc_ap_t *to, const scc_ap_t *a, const scc_ap_t *b); +void scc_ap_or(scc_ap_t *to, const scc_ap_t *a, const scc_ap_t *b); +void scc_ap_xor(scc_ap_t *to, const scc_ap_t *a, const scc_ap_t *b); +void scc_ap_not(scc_ap_t *to, const scc_ap_t *from); + +void scc_ap_neg(scc_ap_t *to, const scc_ap_t *from); + +int scc_ap_cmp(const scc_ap_t *a, const scc_ap_t *b); +int scc_ap_is_zero(const scc_ap_t *ap); + int scc_ap_eql(const scc_ap_t *a, const scc_ap_t *b); typedef void (*ap_dump_fn)(const char ch, void *user_data); diff --git a/runtime/ap/src/ap.c b/runtime/ap/src/ap.c index 358db28..fc8b803 100644 --- a/runtime/ap/src/ap.c +++ b/runtime/ap/src/ap.c @@ -1,48 +1,394 @@ #include -static void *scc_ap_alloc(size_t size) { - void *p = SCC_AP_MALLOC(size); - SCC_AP_ASSERT(p != NULL); - return p; +/* + * Fast-path (hack) implementation: all values stored in single-digit mode. + * data.digit holds the absolute magnitude as u64, len sign indicates sign. + * Arithmetic is done via isize with explicit overflow checks — anything + * out of isize range triggers SCC_AP_PANIC. + */ + +/* ---------- internal helpers ---------- */ + +/* Convert from sign-magnitude (digit mode) to isize */ +static isize ap_to_isize(const scc_ap_t *ap) { + SCC_AP_ASSERT(ap != nullptr); + SCC_AP_ASSERT(ap->capacity == -1); + uint64_t mag = ap->data.digit; + if (ap->len < 0) { + if (mag == 0) + return 0; + if (mag == (uint64_t)INTPTR_MAX + 1) + return INTPTR_MIN; + SCC_AP_ASSERT(mag <= (uint64_t)INTPTR_MAX); + return -(isize)(int64_t)mag; + } + SCC_AP_ASSERT(mag <= (uint64_t)INTPTR_MAX); + return (isize)(int64_t)mag; } -/* 重新分配内存 */ -static void *scc_ap_realloc_data(void *old, size_t new_size) { - void *p = SCC_AP_REALLOC(old, new_size); - SCC_AP_ASSERT(p != NULL); - return p; +/* Store isize value into sign-magnitude (digit mode) */ +static void ap_from_isize(scc_ap_t *ap, isize val) { + ap->capacity = -1; + if (val < 0) { + ap->len = -1; + ap->data.digit = (scc_ap_digit)(-(uint64_t)(int64_t)val); + } else { + ap->len = 1; + ap->data.digit = (scc_ap_digit)val; + } } +/* Checked addition */ +static isize ap_add_isize(isize a, isize b) { + if (b > 0 && a > INTPTR_MAX - b) + SCC_AP_PANIC("scc_ap_add: positive overflow"); + if (b < 0 && a < INTPTR_MIN - b) + SCC_AP_PANIC("scc_ap_add: negative overflow"); + return a + b; +} + +/* Checked subtraction: a - b */ +static isize ap_sub_isize(isize a, isize b) { + /* a - b = a + (-b); -INTPTR_MIN overflows, handle specially */ + if (b == INTPTR_MIN) { + if (a < 0) + SCC_AP_PANIC("scc_ap_sub: overflow (a - INTPTR_MIN, a < 0)"); + SCC_AP_PANIC("scc_ap_sub: overflow (a - INTPTR_MIN, a >= 0)"); + } + return ap_add_isize(a, -b); +} + +/* Checked multiplication */ +static isize ap_mul_isize(isize a, isize b) { + if (a == 0 || b == 0) + return 0; + if (a == 1) + return b; + if (b == 1) + return a; + + /* Determine sign of result */ + int sign = ((a < 0) == (b < 0)) ? 1 : -1; + + /* Absolute values as uint64 */ + uint64_t ua = (a == INTPTR_MIN) + ? (uint64_t)INTPTR_MAX + 1 + : (uint64_t)(a < 0 ? -(int64_t)a : (int64_t)a); + uint64_t ub = (b == INTPTR_MIN) + ? (uint64_t)INTPTR_MAX + 1 + : (uint64_t)(b < 0 ? -(int64_t)b : (int64_t)b); + + /* Overflow threshold */ + uint64_t limit = + (sign > 0) ? (uint64_t)INTPTR_MAX : (uint64_t)INTPTR_MAX + 1; + + if (ua != 0 && ub > limit / ua) + SCC_AP_PANIC("scc_ap_mul: overflow"); + + isize result = (isize)(ua * ub); + return (sign > 0) ? result : -result; +} + +/* Checked division */ +static isize ap_div_isize(isize a, isize b) { + if (b == 0) + SCC_AP_PANIC("scc_ap_div: division by zero"); + if (a == INTPTR_MIN && b == -1) + SCC_AP_PANIC("scc_ap_div: overflow (INTPTR_MIN / -1)"); + return a / b; +} + +/* Checked modulo */ +static isize ap_mod_isize(isize a, isize b) { + if (b == 0) + SCC_AP_PANIC("scc_ap_mod: modulo by zero"); + if (a == INTPTR_MIN && b == -1) + SCC_AP_PANIC("scc_ap_mod: overflow (INTPTR_MIN %% -1)"); + return a % b; +} + +/* ---------- public API ---------- */ + void scc_ap_drop(scc_ap_t *ap) { - SCC_AP_ASSERT(ap); - // SCC_AP_FREE(ap->data); + SCC_AP_ASSERT(ap != nullptr); + /* In digit mode (capacity == -1), there's no heap allocation to free */ scc_ap_init(ap); } -void scc_ap_with_bits(scc_ap_t *ap, int bits) {} - -void scc_ap_from_string(scc_ap_t *ap, const char *str, int len, int base) { - SCC_AP_ASSERT(base == 10); - SCC_AP_ASSERT(ap && str); - if (len == 0) { - scc_ap_set_int(ap, 0); - } - // FIXME - int int_lit = 0; - for (int i = 0; i < len; i += 1) { - int_lit = int_lit * 10 + (str[i] - '0'); - } - scc_ap_set_int(ap, int_lit); +void scc_ap_with_bits(scc_ap_t *ap, int bits) { + SCC_AP_ASSERT(ap != nullptr); + if (bits > (int)(sizeof(scc_ap_digit) * 8)) + SCC_AP_PANIC( + "scc_ap_with_bits: request %d bits but digit only has %zu bits", + bits, sizeof(scc_ap_digit) * 8); + (void)ap; + /* Fast path: always stays in digit mode */ } -void scc_ap_set_digit(scc_ap_t *ap, scc_ap_digit digit) {} +void scc_ap_from_string(scc_ap_t *ap, const char *str, int len, int base) { + SCC_AP_ASSERT(ap != nullptr && str != nullptr); -void scc_ap_add_impl(scc_ap_t *to, const scc_ap_t *from_a, - const scc_ap_t *from_b) {} + /* len < 0 means null-terminated */ + if (len < 0) { + len = 0; + while (str[len] != '\0') + len++; + } + if (len == 0) { + ap_from_isize(ap, 0); + return; + } -void scc_ap_add(scc_ap_t *to, const scc_ap_t *from_a, const scc_ap_t *from_b) {} + int i = 0; + int sign = 1; -void scc_ap_sub(scc_ap_t *to, const scc_ap_t *from_a, const scc_ap_t *from_b) {} + /* Optional leading sign */ + if (str[i] == '-') { + sign = -1; + i++; + } else if (str[i] == '+') { + i++; + } -typedef void (*ap_dump_fn)(const char ch, void *user_data); -int scc_ap_dump(scc_ap_t *ap, ap_dump_fn dump_fn, void *user_data) { return 0; } + if (i >= len) { + ap_from_isize(ap, 0); + return; + } + + /* ---------- base auto-detection ---------- */ + /* hex: 0x / 0X */ + if ((base == 0 || base == 16) && + (i + 2 < len && str[i] == '0' && + (str[i + 1] == 'x' || str[i + 1] == 'X'))) { + base = 16; + i += 2; + } + /* binary: 0b / 0B */ + if ((base == 0 || base == 2) && + (i + 2 < len && str[i] == '0' && + (str[i + 1] == 'b' || str[i + 1] == 'B'))) { + base = 2; + i += 2; + } + if (base == 0) { + if (i < len && str[i] == '0') { + base = 8; + i++; + } else { + base = 10; + } + } + + /* ---------- parse digits ---------- */ + uint64_t val = 0; + for (; i < len; i++) { + char ch = str[i]; + unsigned d; + if (ch >= '0' && ch <= '9') + d = (unsigned)(ch - '0'); + else if (ch >= 'a' && ch <= 'f') + d = (unsigned)(ch - 'a' + 10); + else if (ch >= 'A' && ch <= 'F') + d = (unsigned)(ch - 'A' + 10); + else + break; + if ((int)d >= base) + break; + + /* overflow check: val * base + d <= UINT64_MAX */ + if (val > (UINT64_MAX - d) / (unsigned)base) + SCC_AP_PANIC("scc_ap_from_string: overflow"); + val = val * (unsigned)base + d; + } + + ap->capacity = -1; + ap->data.digit = val; + ap->len = (sign < 0 && val > 0) ? -1 : 1; +} + +void scc_ap_set_digit(scc_ap_t *ap, scc_ap_digit digit) { + SCC_AP_ASSERT(ap != nullptr); + if (digit > (scc_ap_digit)INTPTR_MAX) + SCC_AP_PANIC("scc_ap_set_digit: value %llu exceeds isize range", + (unsigned long long)digit); + ap->capacity = -1; + ap->data.digit = digit; + ap->len = 1; +} + +void scc_ap_add(scc_ap_t *to, const scc_ap_t *from_a, const scc_ap_t *from_b) { + SCC_AP_ASSERT(to != nullptr && from_a != nullptr && from_b != nullptr); + isize a = ap_to_isize(from_a); + isize b = ap_to_isize(from_b); + ap_from_isize(to, ap_add_isize(a, b)); +} + +void scc_ap_sub(scc_ap_t *to, const scc_ap_t *from_a, const scc_ap_t *from_b) { + SCC_AP_ASSERT(to != nullptr && from_a != nullptr && from_b != nullptr); + isize a = ap_to_isize(from_a); + isize b = ap_to_isize(from_b); + ap_from_isize(to, ap_sub_isize(a, b)); +} + +void scc_ap_mul(scc_ap_t *to, const scc_ap_t *from_a, const scc_ap_t *from_b) { + SCC_AP_ASSERT(to != nullptr && from_a != nullptr && from_b != nullptr); + isize a = ap_to_isize(from_a); + isize b = ap_to_isize(from_b); + ap_from_isize(to, ap_mul_isize(a, b)); +} + +void scc_ap_div(scc_ap_t *to, const scc_ap_t *from_a, const scc_ap_t *from_b) { + SCC_AP_ASSERT(to != nullptr && from_a != nullptr && from_b != nullptr); + isize a = ap_to_isize(from_a); + isize b = ap_to_isize(from_b); + ap_from_isize(to, ap_div_isize(a, b)); +} + +void scc_ap_mod(scc_ap_t *to, const scc_ap_t *from_a, const scc_ap_t *from_b) { + SCC_AP_ASSERT(to != nullptr && from_a != nullptr && from_b != nullptr); + isize a = ap_to_isize(from_a); + isize b = ap_to_isize(from_b); + ap_from_isize(to, ap_mod_isize(a, b)); +} + +void scc_ap_shl(scc_ap_t *to, const scc_ap_t *from, unsigned shift) { + SCC_AP_ASSERT(to != nullptr && from != nullptr); + SCC_AP_ASSERT(from->capacity == -1); + if (shift >= sizeof(scc_ap_digit) * 8) + SCC_AP_PANIC("scc_ap_shl: shift %u exceeds digit width (%zu bits)", + shift, sizeof(scc_ap_digit) * 8); + /* left shift = multiply by 2^shift */ + isize val = ap_to_isize(from); + isize result = ap_mul_isize(val, (isize)1 << shift); + ap_from_isize(to, result); +} + +void scc_ap_shr(scc_ap_t *to, const scc_ap_t *from, unsigned shift) { + SCC_AP_ASSERT(to != nullptr && from != nullptr); + SCC_AP_ASSERT(from->capacity == -1); + if (shift >= sizeof(scc_ap_digit) * 8) + SCC_AP_PANIC("scc_ap_shr: shift %u exceeds digit width (%zu bits)", + shift, sizeof(scc_ap_digit) * 8); + /* arithmetic right shift — uses >> on isize (arithmetic on GCC/MSVC/Clang) */ + isize val = ap_to_isize(from); + ap_from_isize(to, val >> shift); +} + +void scc_ap_lshr(scc_ap_t *to, const scc_ap_t *from, unsigned shift) { + SCC_AP_ASSERT(to != nullptr && from != nullptr); + SCC_AP_ASSERT(from->capacity == -1); + if (shift >= sizeof(scc_ap_digit) * 8) + SCC_AP_PANIC("scc_ap_lshr: shift %u exceeds digit width (%zu bits)", + shift, sizeof(scc_ap_digit) * 8); + /* + * logical right shift: reinterpret the signed value as uint64 bit pattern, + * shift right, store as non-negative magnitude. + */ + uint64_t bits; + if (from->len < 0) { + /* negative → two's complement bit pattern */ + bits = -(uint64_t)(int64_t)from->data.digit; + } else { + bits = from->data.digit; + } + to->capacity = -1; + to->data.digit = bits >> shift; + to->len = 1; +} + +/* ---------- bitwise ---------- */ + +void scc_ap_and(scc_ap_t *to, const scc_ap_t *a, const scc_ap_t *b) { + SCC_AP_ASSERT(to != nullptr && a != nullptr && b != nullptr); + ap_from_isize(to, ap_to_isize(a) & ap_to_isize(b)); +} + +void scc_ap_or(scc_ap_t *to, const scc_ap_t *a, const scc_ap_t *b) { + SCC_AP_ASSERT(to != nullptr && a != nullptr && b != nullptr); + ap_from_isize(to, ap_to_isize(a) | ap_to_isize(b)); +} + +void scc_ap_xor(scc_ap_t *to, const scc_ap_t *a, const scc_ap_t *b) { + SCC_AP_ASSERT(to != nullptr && a != nullptr && b != nullptr); + ap_from_isize(to, ap_to_isize(a) ^ ap_to_isize(b)); +} + +void scc_ap_not(scc_ap_t *to, const scc_ap_t *from) { + SCC_AP_ASSERT(to != nullptr && from != nullptr); + ap_from_isize(to, ~ap_to_isize(from)); +} + +/* ---------- unary arithmetic ---------- */ + +void scc_ap_neg(scc_ap_t *to, const scc_ap_t *from) { + SCC_AP_ASSERT(to != nullptr && from != nullptr); + isize val = ap_to_isize(from); + if (val == INTPTR_MIN) + SCC_AP_PANIC("scc_ap_neg: overflow (INTPTR_MIN)"); + ap_from_isize(to, -val); +} + +/* ---------- comparison ---------- */ + +int scc_ap_cmp(const scc_ap_t *a, const scc_ap_t *b) { + SCC_AP_ASSERT(a != nullptr && b != nullptr); + isize va = ap_to_isize(a); + isize vb = ap_to_isize(b); + if (va < vb) return -1; + if (va > vb) return 1; + return 0; +} + +int scc_ap_is_zero(const scc_ap_t *ap) { + SCC_AP_ASSERT(ap != nullptr); + return ap->data.digit == 0; +} + +int scc_ap_eql(const scc_ap_t *a, const scc_ap_t *b) { + SCC_AP_ASSERT(a != nullptr && b != nullptr); + SCC_AP_ASSERT(a->capacity == -1 && b->capacity == -1); + if (a->len != b->len) { + /* -0 == +0 */ + if (a->data.digit == 0 && b->data.digit == 0) + return 1; + return 0; + } + return a->data.digit == b->data.digit; +} + +int scc_ap_dump(scc_ap_t *ap, ap_dump_fn dump_fn, void *user_data) { + SCC_AP_ASSERT(ap != nullptr && dump_fn != nullptr); + SCC_AP_ASSERT(ap->capacity == -1); + + isize val = ap_to_isize(ap); + + if (val == 0) { + dump_fn('0', user_data); + return 0; + } + + /* Buffer large enough for INT64_MIN "-9223372036854775808" */ + char buf[24]; + int pos = sizeof(buf); + + if (val < 0) { + dump_fn('-', user_data); + /* Work with positive magnitude to avoid overflow when printing */ + uint64_t mag = ap->data.digit; + while (mag > 0) { + buf[--pos] = (char)('0' + (mag % 10)); + mag /= 10; + } + } else { + uint64_t mag = (uint64_t)val; + while (mag > 0) { + buf[--pos] = (char)('0' + (mag % 10)); + mag /= 10; + } + } + + for (int i = pos; i < (int)sizeof(buf); i++) + dump_fn(buf[i], user_data); + + return 0; +} diff --git a/runtime/ap/tests/test_ap.c b/runtime/ap/tests/test_ap.c new file mode 100644 index 0000000..00c44b1 --- /dev/null +++ b/runtime/ap/tests/test_ap.c @@ -0,0 +1,1028 @@ +#include +#include +#include +#include + +/* ---------- helpers ---------- */ + +static int eql_int(const scc_ap_t *ap, isize expected) { + return scc_ap_eql( + ap, &(scc_ap_t){.capacity = -1, + .len = expected < 0 ? -1 : 1, + .data.digit = + (scc_ap_digit)(expected < 0 + ? -(uint64_t)(int64_t)expected + : (uint64_t)expected)}); +} + +/* ---------- scc_ap_init / scc_ap_drop ---------- */ + +void test_init_zero(void) { + scc_ap_t ap; + scc_ap_init(&ap); + TEST_CHECK(ap.capacity == -1); + TEST_CHECK(ap.len == 1); + TEST_CHECK(ap.data.digit == 0); + TEST_CHECK(eql_int(&ap, 0)); +} + +void test_drop_resets(void) { + scc_ap_t ap; + scc_ap_set_int(&ap, 42); + scc_ap_drop(&ap); + TEST_CHECK(ap.capacity == -1); + TEST_CHECK(ap.len == 1); + TEST_CHECK(ap.data.digit == 0); + TEST_CHECK(eql_int(&ap, 0)); +} + +/* ---------- scc_ap_set_int ---------- */ + +void test_set_int_positive(void) { + scc_ap_t ap; + scc_ap_init(&ap); + scc_ap_set_int(&ap, 42); + TEST_CHECK(eql_int(&ap, 42)); +} + +void test_set_int_negative(void) { + scc_ap_t ap; + scc_ap_init(&ap); + scc_ap_set_int(&ap, -42); + TEST_CHECK(eql_int(&ap, -42)); +} + +void test_set_int_zero(void) { + scc_ap_t ap; + scc_ap_init(&ap); + scc_ap_set_int(&ap, 0); + TEST_CHECK(eql_int(&ap, 0)); +} + +void test_set_int_edge(void) { + scc_ap_t ap; + scc_ap_init(&ap); + scc_ap_set_int(&ap, 2147483647); /* INT_MAX */ + TEST_CHECK(eql_int(&ap, 2147483647)); + scc_ap_set_int(&ap, -2147483647 - 1); /* INT_MIN */ + TEST_CHECK(eql_int(&ap, -2147483647 - 1)); +} + +/* ---------- scc_ap_set_digit ---------- */ + +void test_set_digit_basic(void) { + scc_ap_t ap; + scc_ap_init(&ap); + scc_ap_set_digit(&ap, 123); + TEST_CHECK(eql_int(&ap, 123)); +} + +void test_set_digit_zero(void) { + scc_ap_t ap; + scc_ap_init(&ap); + scc_ap_set_digit(&ap, 0); + TEST_CHECK(eql_int(&ap, 0)); +} + +void test_set_digit_max(void) { + scc_ap_t ap; + scc_ap_init(&ap); + scc_ap_set_digit(&ap, (scc_ap_digit)INTPTR_MAX); + TEST_CHECK(eql_int(&ap, INTPTR_MAX)); +} + +/* ---------- scc_ap_from_string ---------- */ + +void test_from_string_decimal(void) { + scc_ap_t ap; + scc_ap_init(&ap); + scc_ap_from_string(&ap, "12345", -1, 10); + TEST_CHECK(eql_int(&ap, 12345)); +} + +void test_from_string_negative(void) { + scc_ap_t ap; + scc_ap_init(&ap); + scc_ap_from_string(&ap, "-12345", -1, 10); + TEST_CHECK(eql_int(&ap, -12345)); +} + +void test_from_string_hex(void) { + scc_ap_t ap; + scc_ap_init(&ap); + scc_ap_from_string(&ap, "0xFF", -1, 0); + TEST_CHECK(eql_int(&ap, 255)); +} + +void test_from_string_hex_no_prefix(void) { + scc_ap_t ap; + scc_ap_init(&ap); + scc_ap_from_string(&ap, "FF", -1, 16); + TEST_CHECK(eql_int(&ap, 255)); +} + +void test_from_string_octal(void) { + scc_ap_t ap; + scc_ap_init(&ap); + scc_ap_from_string(&ap, "077", -1, 0); + TEST_CHECK(eql_int(&ap, 63)); +} + +void test_from_string_binary(void) { + scc_ap_t ap; + scc_ap_init(&ap); + scc_ap_from_string(&ap, "0b1010", -1, 0); + TEST_CHECK(eql_int(&ap, 10)); +} + +void test_from_string_zero(void) { + scc_ap_t ap; + scc_ap_init(&ap); + scc_ap_from_string(&ap, "0", -1, 10); + TEST_CHECK(eql_int(&ap, 0)); +} + +void test_from_string_empty(void) { + scc_ap_t ap; + scc_ap_init(&ap); + scc_ap_from_string(&ap, "", -1, 10); + TEST_CHECK(eql_int(&ap, 0)); +} + +void test_from_string_negative_zero(void) { + scc_ap_t ap; + scc_ap_init(&ap); + scc_ap_from_string(&ap, "-0", -1, 10); + TEST_CHECK(eql_int(&ap, 0)); + /* Should be positive zero */ + TEST_CHECK(ap.len > 0); +} + +void test_from_string_len_exact(void) { + scc_ap_t ap; + scc_ap_init(&ap); + scc_ap_from_string(&ap, "42extra", 2, 10); + TEST_CHECK(eql_int(&ap, 42)); +} + +void test_from_string_max(void) { + scc_ap_t ap; + scc_ap_init(&ap); + scc_ap_from_string(&ap, "9223372036854775807", -1, 10); /* INT64_MAX */ + TEST_CHECK(eql_int(&ap, 9223372036854775807LL)); +} + +void test_from_string_min(void) { + scc_ap_t ap; + scc_ap_init(&ap); + scc_ap_from_string(&ap, "-9223372036854775808", -1, 10); + TEST_CHECK(eql_int(&ap, (-9223372036854775807LL - 1))); +} + +/* ---------- scc_ap_add ---------- */ + +void test_add_basic(void) { + scc_ap_t a, b, r; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_init(&r); + scc_ap_set_int(&a, 3); + scc_ap_set_int(&b, 5); + scc_ap_add(&r, &a, &b); + TEST_CHECK(eql_int(&r, 8)); +} + +void test_add_negative(void) { + scc_ap_t a, b, r; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_init(&r); + scc_ap_set_int(&a, -3); + scc_ap_set_int(&b, 5); + scc_ap_add(&r, &a, &b); + TEST_CHECK(eql_int(&r, 2)); +} + +void test_add_both_negative(void) { + scc_ap_t a, b, r; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_init(&r); + scc_ap_set_int(&a, -3); + scc_ap_set_int(&b, -5); + scc_ap_add(&r, &a, &b); + TEST_CHECK(eql_int(&r, -8)); +} + +void test_add_zero(void) { + scc_ap_t a, b, r; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_init(&r); + scc_ap_set_int(&a, 42); + scc_ap_set_int(&b, 0); + scc_ap_add(&r, &a, &b); + TEST_CHECK(eql_int(&r, 42)); +} + +void test_add_to_max(void) { + scc_ap_t a, b, r; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_init(&r); + scc_ap_from_string(&a, "9223372036854775806", -1, 10); + scc_ap_set_int(&b, 1); + scc_ap_add(&r, &a, &b); + TEST_CHECK(eql_int(&r, INTPTR_MAX)); +} + +void test_add_to_min(void) { + scc_ap_t a, b, r; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_init(&r); + /* INTPTR_MIN + 1 + (-1) = INTPTR_MIN */ + scc_ap_from_string(&a, "-9223372036854775807", -1, 10); + scc_ap_set_int(&b, -1); + scc_ap_add(&r, &a, &b); + TEST_CHECK(eql_int(&r, INTPTR_MIN)); +} + +/* ---------- scc_ap_sub ---------- */ + +void test_sub_basic(void) { + scc_ap_t a, b, r; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_init(&r); + scc_ap_set_int(&a, 10); + scc_ap_set_int(&b, 3); + scc_ap_sub(&r, &a, &b); + TEST_CHECK(eql_int(&r, 7)); +} + +void test_sub_negative_result(void) { + scc_ap_t a, b, r; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_init(&r); + scc_ap_set_int(&a, 3); + scc_ap_set_int(&b, 10); + scc_ap_sub(&r, &a, &b); + TEST_CHECK(eql_int(&r, -7)); +} + +void test_sub_negatives(void) { + scc_ap_t a, b, r; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_init(&r); + scc_ap_set_int(&a, -5); + scc_ap_set_int(&b, -3); + scc_ap_sub(&r, &a, &b); + TEST_CHECK(eql_int(&r, -2)); +} + +void test_sub_zero(void) { + scc_ap_t a, b, r; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_init(&r); + scc_ap_set_int(&a, 42); + scc_ap_set_int(&b, 0); + scc_ap_sub(&r, &a, &b); + TEST_CHECK(eql_int(&r, 42)); +} + +void test_sub_from_min(void) { + scc_ap_t a, b, r; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_init(&r); + /* (INTPTR_MIN + 1) - 1 = INTPTR_MIN */ + scc_ap_from_string(&a, "-9223372036854775807", -1, 10); + scc_ap_set_int(&b, 1); + scc_ap_sub(&r, &a, &b); + TEST_CHECK(eql_int(&r, INTPTR_MIN)); +} + +/* ---------- scc_ap_mul ---------- */ + +void test_mul_basic(void) { + scc_ap_t a, b, r; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_init(&r); + scc_ap_set_int(&a, 6); + scc_ap_set_int(&b, 7); + scc_ap_mul(&r, &a, &b); + TEST_CHECK(eql_int(&r, 42)); +} + +void test_mul_negative(void) { + scc_ap_t a, b, r; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_init(&r); + scc_ap_set_int(&a, -4); + scc_ap_set_int(&b, 5); + scc_ap_mul(&r, &a, &b); + TEST_CHECK(eql_int(&r, -20)); +} + +void test_mul_both_negative(void) { + scc_ap_t a, b, r; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_init(&r); + scc_ap_set_int(&a, -4); + scc_ap_set_int(&b, -5); + scc_ap_mul(&r, &a, &b); + TEST_CHECK(eql_int(&r, 20)); +} + +void test_mul_zero(void) { + scc_ap_t a, b, r; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_init(&r); + scc_ap_set_int(&a, 42); + scc_ap_set_int(&b, 0); + scc_ap_mul(&r, &a, &b); + TEST_CHECK(eql_int(&r, 0)); +} + +void test_mul_one(void) { + scc_ap_t a, b, r; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_init(&r); + scc_ap_set_int(&a, 99); + scc_ap_set_int(&b, 1); + scc_ap_mul(&r, &a, &b); + TEST_CHECK(eql_int(&r, 99)); +} + +void test_mul_neg_one(void) { + scc_ap_t a, b, r; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_init(&r); + scc_ap_set_int(&a, 99); + scc_ap_set_int(&b, -1); + scc_ap_mul(&r, &a, &b); + TEST_CHECK(eql_int(&r, -99)); +} + +void test_mul_max(void) { + scc_ap_t a, b, r; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_init(&r); + scc_ap_set_int(&a, 1); + scc_ap_from_string(&b, "9223372036854775807", -1, 10); + scc_ap_mul(&r, &a, &b); + TEST_CHECK(eql_int(&r, INTPTR_MAX)); +} + +/* ---------- scc_ap_div ---------- */ + +void test_div_basic(void) { + scc_ap_t a, b, r; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_init(&r); + scc_ap_set_int(&a, 42); + scc_ap_set_int(&b, 6); + scc_ap_div(&r, &a, &b); + TEST_CHECK(eql_int(&r, 7)); +} + +void test_div_negative(void) { + scc_ap_t a, b, r; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_init(&r); + scc_ap_set_int(&a, -42); + scc_ap_set_int(&b, 6); + scc_ap_div(&r, &a, &b); + TEST_CHECK(eql_int(&r, -7)); +} + +void test_div_trunc_toward_zero(void) { + scc_ap_t a, b, r; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_init(&r); + scc_ap_set_int(&a, 7); + scc_ap_set_int(&b, 3); + scc_ap_div(&r, &a, &b); + TEST_CHECK(eql_int(&r, 2)); +} + +void test_div_one(void) { + scc_ap_t a, b, r; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_init(&r); + scc_ap_set_int(&a, 123); + scc_ap_set_int(&b, 1); + scc_ap_div(&r, &a, &b); + TEST_CHECK(eql_int(&r, 123)); +} + +void test_div_self(void) { + scc_ap_t a, b, r; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_init(&r); + scc_ap_set_int(&a, 42); + scc_ap_set_int(&b, 42); + scc_ap_div(&r, &a, &b); + TEST_CHECK(eql_int(&r, 1)); +} + +void test_div_zero_dividend(void) { + scc_ap_t a, b, r; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_init(&r); + scc_ap_set_int(&a, 0); + scc_ap_set_int(&b, 7); + scc_ap_div(&r, &a, &b); + TEST_CHECK(eql_int(&r, 0)); +} + +/* ---------- scc_ap_mod ---------- */ + +void test_mod_basic(void) { + scc_ap_t a, b, r; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_init(&r); + scc_ap_set_int(&a, 10); + scc_ap_set_int(&b, 3); + scc_ap_mod(&r, &a, &b); + TEST_CHECK(eql_int(&r, 1)); +} + +void test_mod_negative_dividend(void) { + scc_ap_t a, b, r; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_init(&r); + scc_ap_set_int(&a, -10); + scc_ap_set_int(&b, 3); + scc_ap_mod(&r, &a, &b); + /* C truncates toward zero: -10 % 3 = -1 */ + TEST_CHECK(eql_int(&r, -1)); +} + +void test_mod_self(void) { + scc_ap_t a, b, r; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_init(&r); + scc_ap_set_int(&a, 42); + scc_ap_set_int(&b, 42); + scc_ap_mod(&r, &a, &b); + TEST_CHECK(eql_int(&r, 0)); +} + +/* ---------- scc_ap_shl / scc_ap_shr / scc_ap_lshr ---------- */ + +void test_shl_basic(void) { + scc_ap_t a, r; + scc_ap_init(&a); + scc_ap_init(&r); + scc_ap_set_int(&a, 3); + scc_ap_shl(&r, &a, 2); + TEST_CHECK(eql_int(&r, 12)); /* 3 << 2 = 12 */ +} + +void test_shl_negative(void) { + scc_ap_t a, r; + scc_ap_init(&a); + scc_ap_init(&r); + scc_ap_set_int(&a, -3); + scc_ap_shl(&r, &a, 2); + TEST_CHECK(eql_int(&r, -12)); +} + +void test_shl_zero(void) { + scc_ap_t a, r; + scc_ap_init(&a); + scc_ap_init(&r); + scc_ap_set_int(&a, 42); + scc_ap_shl(&r, &a, 0); + TEST_CHECK(eql_int(&r, 42)); +} + +void test_shl_by_62(void) { + scc_ap_t a, r; + scc_ap_init(&a); + scc_ap_init(&r); + scc_ap_set_int(&a, 1); + scc_ap_shl(&r, &a, 62); + TEST_CHECK(eql_int(&r, 4611686018427387904LL)); +} + +void test_shr_basic(void) { + scc_ap_t a, r; + scc_ap_init(&a); + scc_ap_init(&r); + scc_ap_set_int(&a, 12); + scc_ap_shr(&r, &a, 2); + TEST_CHECK(eql_int(&r, 3)); /* 12 >> 2 = 3 */ +} + +void test_shr_arithmetic_negative(void) { + scc_ap_t a, r; + scc_ap_init(&a); + scc_ap_init(&r); + scc_ap_set_int(&a, -12); + scc_ap_shr(&r, &a, 2); + TEST_CHECK(eql_int(&r, -3)); /* arithmetic: -12 >> 2 = -3 */ +} + +void test_shr_arithmetic_negative_rounding(void) { + scc_ap_t a, r; + scc_ap_init(&a); + scc_ap_init(&r); + scc_ap_set_int(&a, -7); + scc_ap_shr(&r, &a, 2); + TEST_CHECK(eql_int(&r, -2)); /* arithmetic: -7 >> 2 = -2 (rounds down) */ +} + +void test_shr_zero_shift(void) { + scc_ap_t a, r; + scc_ap_init(&a); + scc_ap_init(&r); + scc_ap_set_int(&a, -42); + scc_ap_shr(&r, &a, 0); + TEST_CHECK(eql_int(&r, -42)); +} + +void test_lshr_basic(void) { + scc_ap_t a, r; + scc_ap_init(&a); + scc_ap_init(&r); + scc_ap_set_int(&a, 12); + scc_ap_lshr(&r, &a, 2); + TEST_CHECK(eql_int(&r, 3)); /* 12 >> 2 = 3 */ +} + +void test_lshr_negative(void) { + scc_ap_t a, r; + scc_ap_init(&a); + scc_ap_init(&r); + scc_ap_set_int(&a, -1); /* 0xFFFFFFFFFFFFFFFF */ + scc_ap_lshr(&r, &a, 63); + TEST_CHECK(eql_int(&r, 1)); /* 0xFFFFFFFFFFFFFFFF >> 63 = 1 */ +} + +void test_lshr_negative_bits(void) { + scc_ap_t a, r; + scc_ap_init(&a); + scc_ap_init(&r); + scc_ap_set_int(&a, -8); /* 0xFFFFFFFFFFFFFFF8 */ + scc_ap_lshr(&r, &a, 2); + /* 0xFFFFFFFFFFFFFFF8 >> 2 = 0x3FFFFFFFFFFFFFFE = 4611686018427387902 */ + TEST_CHECK(eql_int(&r, 4611686018427387902LL)); +} + +/* ---------- bitwise ---------- */ + +void test_and_basic(void) { + scc_ap_t a, b, r; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_init(&r); + scc_ap_set_int(&a, 5); + scc_ap_set_int(&b, 3); + scc_ap_and(&r, &a, &b); + TEST_CHECK(eql_int(&r, 1)); /* 5 & 3 = 1 */ +} + +void test_and_negative(void) { + scc_ap_t a, b, r; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_init(&r); + scc_ap_set_int(&a, -5); + scc_ap_set_int(&b, 3); + scc_ap_and(&r, &a, &b); + /* -5 = 0xFFFFFFFB, & 3 = 0x00000003 = 3 */ + TEST_CHECK(eql_int(&r, 3)); +} + +void test_and_zero(void) { + scc_ap_t a, b, r; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_init(&r); + scc_ap_set_int(&a, 42); + scc_ap_set_int(&b, 0); + scc_ap_and(&r, &a, &b); + TEST_CHECK(eql_int(&r, 0)); +} + +void test_or_basic(void) { + scc_ap_t a, b, r; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_init(&r); + scc_ap_set_int(&a, 5); + scc_ap_set_int(&b, 3); + scc_ap_or(&r, &a, &b); + TEST_CHECK(eql_int(&r, 7)); /* 5 | 3 = 7 */ +} + +void test_or_negative(void) { + scc_ap_t a, b, r; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_init(&r); + scc_ap_set_int(&a, -5); + scc_ap_set_int(&b, 3); + scc_ap_or(&r, &a, &b); + TEST_CHECK(eql_int(&r, -5)); /* -5 | 3 = -5 */ +} + +void test_xor_basic(void) { + scc_ap_t a, b, r; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_init(&r); + scc_ap_set_int(&a, 5); + scc_ap_set_int(&b, 3); + scc_ap_xor(&r, &a, &b); + TEST_CHECK(eql_int(&r, 6)); /* 5 ^ 3 = 6 */ +} + +void test_xor_negative(void) { + scc_ap_t a, b, r; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_init(&r); + scc_ap_set_int(&a, -5); + scc_ap_set_int(&b, 3); + scc_ap_xor(&r, &a, &b); + TEST_CHECK(eql_int(&r, -8)); /* -5 ^ 3 = -8 */ +} + +void test_not_basic(void) { + scc_ap_t a, r; + scc_ap_init(&a); + scc_ap_init(&r); + scc_ap_set_int(&a, 5); + scc_ap_not(&r, &a); + TEST_CHECK(eql_int(&r, -6)); /* ~5 = -6 */ +} + +void test_not_neg_one(void) { + scc_ap_t a, r; + scc_ap_init(&a); + scc_ap_init(&r); + scc_ap_set_int(&a, -1); + scc_ap_not(&r, &a); + TEST_CHECK(eql_int(&r, 0)); /* ~(-1) = 0 */ +} + +void test_not_zero(void) { + scc_ap_t a, r; + scc_ap_init(&a); + scc_ap_init(&r); + scc_ap_set_int(&a, 0); + scc_ap_not(&r, &a); + TEST_CHECK(eql_int(&r, -1)); /* ~0 = -1 */ +} + +/* ---------- scc_ap_neg ---------- */ + +void test_neg_positive(void) { + scc_ap_t a, r; + scc_ap_init(&a); + scc_ap_init(&r); + scc_ap_set_int(&a, 42); + scc_ap_neg(&r, &a); + TEST_CHECK(eql_int(&r, -42)); +} + +void test_neg_negative(void) { + scc_ap_t a, r; + scc_ap_init(&a); + scc_ap_init(&r); + scc_ap_set_int(&a, -42); + scc_ap_neg(&r, &a); + TEST_CHECK(eql_int(&r, 42)); +} + +void test_neg_zero(void) { + scc_ap_t a, r; + scc_ap_init(&a); + scc_ap_init(&r); + scc_ap_set_int(&a, 0); + scc_ap_neg(&r, &a); + TEST_CHECK(eql_int(&r, 0)); +} + +/* ---------- scc_ap_cmp ---------- */ + +void test_cmp_equal(void) { + scc_ap_t a, b; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_set_int(&a, 42); + scc_ap_set_int(&b, 42); + TEST_CHECK(scc_ap_cmp(&a, &b) == 0); +} + +void test_cmp_less(void) { + scc_ap_t a, b; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_set_int(&a, 3); + scc_ap_set_int(&b, 5); + TEST_CHECK(scc_ap_cmp(&a, &b) < 0); +} + +void test_cmp_greater(void) { + scc_ap_t a, b; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_set_int(&a, 5); + scc_ap_set_int(&b, 3); + TEST_CHECK(scc_ap_cmp(&a, &b) > 0); +} + +void test_cmp_neg_vs_pos(void) { + scc_ap_t a, b; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_set_int(&a, -5); + scc_ap_set_int(&b, 3); + TEST_CHECK(scc_ap_cmp(&a, &b) < 0); +} + +/* ---------- scc_ap_is_zero ---------- */ + +void test_is_zero_true(void) { + scc_ap_t ap; + scc_ap_init(&ap); + TEST_CHECK(scc_ap_is_zero(&ap) != 0); +} + +void test_is_zero_false_pos(void) { + scc_ap_t ap; + scc_ap_init(&ap); + scc_ap_set_int(&ap, 42); + TEST_CHECK(scc_ap_is_zero(&ap) == 0); +} + +void test_is_zero_false_neg(void) { + scc_ap_t ap; + scc_ap_init(&ap); + scc_ap_set_int(&ap, -42); + TEST_CHECK(scc_ap_is_zero(&ap) == 0); +} + +/* ---------- scc_ap_eql ---------- */ + +void test_eql_equal(void) { + scc_ap_t a, b; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_set_int(&a, 42); + scc_ap_set_int(&b, 42); + TEST_CHECK(scc_ap_eql(&a, &b) != 0); +} + +void test_eql_not_equal(void) { + scc_ap_t a, b; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_set_int(&a, 42); + scc_ap_set_int(&b, 43); + TEST_CHECK(scc_ap_eql(&a, &b) == 0); +} + +void test_eql_neg_vs_pos(void) { + scc_ap_t a, b; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_set_int(&a, -42); + scc_ap_set_int(&b, 42); + TEST_CHECK(scc_ap_eql(&a, &b) == 0); +} + +void test_eql_both_zero(void) { + scc_ap_t a, b; + scc_ap_init(&a); + scc_ap_init(&b); + /* +0 == 0 */ + scc_ap_from_string(&a, "0", -1, 10); + scc_ap_from_string(&b, "-0", -1, 10); + TEST_CHECK(scc_ap_eql(&a, &b) != 0); +} + +/* ---------- scc_ap_with_bits ---------- */ + +void test_with_bits_small(void) { + scc_ap_t ap; + scc_ap_init(&ap); + scc_ap_with_bits(&ap, 1); + TEST_CHECK(ap.capacity == -1); +} + +void test_with_bits_64(void) { + scc_ap_t ap; + scc_ap_init(&ap); + scc_ap_with_bits(&ap, 64); + TEST_CHECK(ap.capacity == -1); +} + +/* ---------- scc_ap_dump ---------- */ + +typedef struct { + char buf[128]; + int pos; +} dump_ctx_t; + +static void dump_char(char ch, void *user_data) { + dump_ctx_t *ctx = (dump_ctx_t *)user_data; + if (ctx->pos < (int)sizeof(ctx->buf) - 1) + ctx->buf[ctx->pos++] = ch; +} + +void test_dump_positive(void) { + scc_ap_t ap; + dump_ctx_t ctx; + scc_ap_init(&ap); + scc_ap_set_int(&ap, 12345); + ctx.pos = 0; + ctx.buf[0] = '\0'; + scc_ap_dump(&ap, dump_char, &ctx); + ctx.buf[ctx.pos] = '\0'; + TEST_CHECK(strcmp(ctx.buf, "12345") == 0); +} + +void test_dump_negative(void) { + scc_ap_t ap; + dump_ctx_t ctx; + scc_ap_init(&ap); + scc_ap_set_int(&ap, -12345); + ctx.pos = 0; + ctx.buf[0] = '\0'; + scc_ap_dump(&ap, dump_char, &ctx); + ctx.buf[ctx.pos] = '\0'; + TEST_CHECK(strcmp(ctx.buf, "-12345") == 0); +} + +void test_dump_zero(void) { + scc_ap_t ap; + dump_ctx_t ctx; + scc_ap_init(&ap); + ctx.pos = 0; + ctx.buf[0] = '\0'; + scc_ap_dump(&ap, dump_char, &ctx); + ctx.buf[ctx.pos] = '\0'; + TEST_CHECK(strcmp(ctx.buf, "0") == 0); +} + +void test_dump_max(void) { + scc_ap_t ap; + dump_ctx_t ctx; + scc_ap_init(&ap); + scc_ap_from_string(&ap, "9223372036854775807", -1, 10); + ctx.pos = 0; + ctx.buf[0] = '\0'; + scc_ap_dump(&ap, dump_char, &ctx); + ctx.buf[ctx.pos] = '\0'; + TEST_CHECK(strcmp(ctx.buf, "9223372036854775807") == 0); +} + +void test_dump_min(void) { + scc_ap_t ap; + dump_ctx_t ctx; + scc_ap_init(&ap); + scc_ap_from_string(&ap, "-9223372036854775808", -1, 10); + ctx.pos = 0; + ctx.buf[0] = '\0'; + scc_ap_dump(&ap, dump_char, &ctx); + ctx.buf[ctx.pos] = '\0'; + TEST_CHECK(strcmp(ctx.buf, "-9223372036854775808") == 0); +} + +/* ---------- test list ---------- */ + +TEST_LIST = { + /* init / drop */ + {"test_init_zero", test_init_zero}, + {"test_drop_resets", test_drop_resets}, + /* set_int */ + {"test_set_int_positive", test_set_int_positive}, + {"test_set_int_negative", test_set_int_negative}, + {"test_set_int_zero", test_set_int_zero}, + {"test_set_int_edge", test_set_int_edge}, + /* set_digit */ + {"test_set_digit_basic", test_set_digit_basic}, + {"test_set_digit_zero", test_set_digit_zero}, + {"test_set_digit_max", test_set_digit_max}, + /* from_string */ + {"test_from_string_decimal", test_from_string_decimal}, + {"test_from_string_negative", test_from_string_negative}, + {"test_from_string_hex", test_from_string_hex}, + {"test_from_string_hex_no_prefix", test_from_string_hex_no_prefix}, + {"test_from_string_octal", test_from_string_octal}, + {"test_from_string_binary", test_from_string_binary}, + {"test_from_string_zero", test_from_string_zero}, + {"test_from_string_empty", test_from_string_empty}, + {"test_from_string_negative_zero", test_from_string_negative_zero}, + {"test_from_string_len_exact", test_from_string_len_exact}, + {"test_from_string_max", test_from_string_max}, + {"test_from_string_min", test_from_string_min}, + /* add */ + {"test_add_basic", test_add_basic}, + {"test_add_negative", test_add_negative}, + {"test_add_both_negative", test_add_both_negative}, + {"test_add_zero", test_add_zero}, + {"test_add_to_max", test_add_to_max}, + {"test_add_to_min", test_add_to_min}, + /* sub */ + {"test_sub_basic", test_sub_basic}, + {"test_sub_negative_result", test_sub_negative_result}, + {"test_sub_negatives", test_sub_negatives}, + {"test_sub_zero", test_sub_zero}, + {"test_sub_from_min", test_sub_from_min}, + /* mul */ + {"test_mul_basic", test_mul_basic}, + {"test_mul_negative", test_mul_negative}, + {"test_mul_both_negative", test_mul_both_negative}, + {"test_mul_zero", test_mul_zero}, + {"test_mul_one", test_mul_one}, + {"test_mul_neg_one", test_mul_neg_one}, + {"test_mul_max", test_mul_max}, + /* div */ + {"test_div_basic", test_div_basic}, + {"test_div_negative", test_div_negative}, + {"test_div_trunc_toward_zero", test_div_trunc_toward_zero}, + {"test_div_one", test_div_one}, + {"test_div_self", test_div_self}, + {"test_div_zero_dividend", test_div_zero_dividend}, + /* mod */ + {"test_mod_basic", test_mod_basic}, + {"test_mod_negative_dividend", test_mod_negative_dividend}, + {"test_mod_self", test_mod_self}, + /* shl / shr / lshr */ + {"test_shl_basic", test_shl_basic}, + {"test_shl_negative", test_shl_negative}, + {"test_shl_zero", test_shl_zero}, + {"test_shl_by_62", test_shl_by_62}, + {"test_shr_basic", test_shr_basic}, + {"test_shr_arithmetic_negative", test_shr_arithmetic_negative}, + {"test_shr_arithmetic_negative_rounding", test_shr_arithmetic_negative_rounding}, + {"test_shr_zero_shift", test_shr_zero_shift}, + {"test_lshr_basic", test_lshr_basic}, + {"test_lshr_negative", test_lshr_negative}, + {"test_lshr_negative_bits", test_lshr_negative_bits}, + /* bitwise */ + {"test_and_basic", test_and_basic}, + {"test_and_negative", test_and_negative}, + {"test_and_zero", test_and_zero}, + {"test_or_basic", test_or_basic}, + {"test_or_negative", test_or_negative}, + {"test_xor_basic", test_xor_basic}, + {"test_xor_negative", test_xor_negative}, + {"test_not_basic", test_not_basic}, + {"test_not_neg_one", test_not_neg_one}, + {"test_not_zero", test_not_zero}, + /* neg */ + {"test_neg_positive", test_neg_positive}, + {"test_neg_negative", test_neg_negative}, + {"test_neg_zero", test_neg_zero}, + /* cmp */ + {"test_cmp_equal", test_cmp_equal}, + {"test_cmp_less", test_cmp_less}, + {"test_cmp_greater", test_cmp_greater}, + {"test_cmp_neg_vs_pos", test_cmp_neg_vs_pos}, + /* is_zero */ + {"test_is_zero_true", test_is_zero_true}, + {"test_is_zero_false_pos", test_is_zero_false_pos}, + {"test_is_zero_false_neg", test_is_zero_false_neg}, + /* eql */ + {"test_eql_equal", test_eql_equal}, + {"test_eql_not_equal", test_eql_not_equal}, + {"test_eql_neg_vs_pos", test_eql_neg_vs_pos}, + {"test_eql_both_zero", test_eql_both_zero}, + /* with_bits */ + {"test_with_bits_small", test_with_bits_small}, + {"test_with_bits_64", test_with_bits_64}, + /* dump */ + {"test_dump_positive", test_dump_positive}, + {"test_dump_negative", test_dump_negative}, + {"test_dump_zero", test_dump_zero}, + {"test_dump_max", test_dump_max}, + {"test_dump_min", test_dump_min}, + {nullptr, nullptr}, +}; diff --git a/runtime/ap/tests/test_ap_panic.c b/runtime/ap/tests/test_ap_panic.c new file mode 100644 index 0000000..5e57fe9 --- /dev/null +++ b/runtime/ap/tests/test_ap_panic.c @@ -0,0 +1,184 @@ +#include +#include +#include +#include + +/* + * Panic / overflow tests — each test calls SCC_AP_PANIC which aborts the + * process. Run this file separately from the normal tests. + */ + +/* ---------- helper: attempt an operation and report result ---------- */ + +#define TRY_PANIC(call, label) \ + do { \ + fprintf(stderr, " " label " ... "); \ + call; \ + fprintf(stderr, "FAIL (no panic)\n"); \ + TEST_CHECK(0 && "expected panic"); \ + } while (0) + +void test_add_overflow_positive(void) { + scc_ap_t a, b, r; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_init(&r); + scc_ap_from_string(&a, "9223372036854775807", -1, 10); + scc_ap_set_int(&b, 1); + TRY_PANIC(scc_ap_add(&r, &a, &b), "INTPTR_MAX + 1"); +} + +void test_add_overflow_negative(void) { + scc_ap_t a, b, r; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_init(&r); + scc_ap_from_string(&a, "-9223372036854775808", -1, 10); + scc_ap_set_int(&b, -1); + TRY_PANIC(scc_ap_add(&r, &a, &b), "INTPTR_MIN + (-1)"); +} + +void test_mul_overflow(void) { + scc_ap_t a, b, r; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_init(&r); + scc_ap_from_string(&a, "4611686018427387904", -1, 10); /* INT64_MAX/2 + 1 */ + scc_ap_set_int(&b, 2); + TRY_PANIC(scc_ap_mul(&r, &a, &b), "(INT64_MAX/2+1) * 2"); +} + +void test_div_by_zero(void) { + scc_ap_t a, b, r; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_init(&r); + scc_ap_set_int(&a, 1); + scc_ap_set_int(&b, 0); + TRY_PANIC(scc_ap_div(&r, &a, &b), "1 / 0"); +} + +void test_mod_by_zero(void) { + scc_ap_t a, b, r; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_init(&r); + scc_ap_set_int(&a, 1); + scc_ap_set_int(&b, 0); + TRY_PANIC(scc_ap_mod(&r, &a, &b), "1 % 0"); +} + +void test_from_string_overflow(void) { + scc_ap_t ap; + scc_ap_init(&ap); + TRY_PANIC(scc_ap_from_string(&ap, "18446744073709551615", -1, 10), + "UINT64_MAX as string"); +} + +void test_set_digit_overflow(void) { + scc_ap_t ap; + scc_ap_init(&ap); + TRY_PANIC(scc_ap_set_digit(&ap, (scc_ap_digit)18446744073709551615ULL), + "set_digit(UINT64_MAX)"); +} + +void test_sub_underflow(void) { + scc_ap_t a, b, r; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_init(&r); + scc_ap_from_string(&a, "-9223372036854775808", -1, 10); + scc_ap_set_int(&b, 1); + TRY_PANIC(scc_ap_sub(&r, &a, &b), "INTPTR_MIN - 1"); +} + +void test_div_overflow(void) { + scc_ap_t a, b, r; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_init(&r); + scc_ap_from_string(&a, "-9223372036854775808", -1, 10); + scc_ap_set_int(&b, -1); + TRY_PANIC(scc_ap_div(&r, &a, &b), "INTPTR_MIN / -1"); +} + +void test_mod_overflow(void) { + scc_ap_t a, b, r; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_init(&r); + scc_ap_from_string(&a, "-9223372036854775808", -1, 10); + scc_ap_set_int(&b, -1); + TRY_PANIC(scc_ap_mod(&r, &a, &b), "INTPTR_MIN % -1"); +} + +void test_mul_overflow_neg_limit(void) { + scc_ap_t a, b, r; + scc_ap_init(&a); + scc_ap_init(&b); + scc_ap_init(&r); + scc_ap_from_string(&a, "-9223372036854775808", -1, 10); + scc_ap_set_int(&b, 2); + TRY_PANIC(scc_ap_mul(&r, &a, &b), "INTPTR_MIN * 2"); +} + +void test_with_bits_overflow(void) { + scc_ap_t ap; + scc_ap_init(&ap); + TRY_PANIC(scc_ap_with_bits(&ap, 65), "with_bits(65)"); +} + +/* ---------- shift overflow tests ---------- */ + +void test_shl_overflow(void) { + scc_ap_t a, r; + scc_ap_init(&a); + scc_ap_init(&r); + scc_ap_from_string(&a, "4611686018427387904", -1, 10); /* INT64_MAX/2+1 */ + TRY_PANIC(scc_ap_shl(&r, &a, 1), "(INT64_MAX/2+1) << 1"); +} + +void test_shl_shift_ge_64(void) { + scc_ap_t a, r; + scc_ap_init(&a); + scc_ap_init(&r); + scc_ap_set_int(&a, 1); + TRY_PANIC(scc_ap_shl(&r, &a, 64), "1 << 64"); +} + +void test_shr_shift_ge_64(void) { + scc_ap_t a, r; + scc_ap_init(&a); + scc_ap_init(&r); + scc_ap_set_int(&a, 1); + TRY_PANIC(scc_ap_shr(&r, &a, 64), "1 >> 64"); +} + +void test_lshr_shift_ge_64(void) { + scc_ap_t a, r; + scc_ap_init(&a); + scc_ap_init(&r); + scc_ap_set_int(&a, 1); + TRY_PANIC(scc_ap_lshr(&r, &a, 64), "1 lshr 64"); +} + +TEST_LIST = { + {"test_add_overflow_positive", test_add_overflow_positive}, + {"test_add_overflow_negative", test_add_overflow_negative}, + {"test_mul_overflow", test_mul_overflow}, + {"test_mul_overflow_neg_limit", test_mul_overflow_neg_limit}, + {"test_div_by_zero", test_div_by_zero}, + {"test_div_overflow", test_div_overflow}, + {"test_mod_by_zero", test_mod_by_zero}, + {"test_mod_overflow", test_mod_overflow}, + {"test_sub_underflow", test_sub_underflow}, + {"test_from_string_overflow", test_from_string_overflow}, + {"test_set_digit_overflow", test_set_digit_overflow}, + {"test_with_bits_overflow", test_with_bits_overflow}, + /* shift overflow */ + {"test_shl_overflow", test_shl_overflow}, + {"test_shl_shift_ge_64", test_shl_shift_ge_64}, + {"test_shr_shift_ge_64", test_shr_shift_ge_64}, + {"test_lshr_shift_ge_64", test_lshr_shift_ge_64}, + {nullptr, nullptr}, +}; diff --git a/src/bin/sccfdump.c b/src/bin/sccfdump.c index 07a77ed..727beff 100644 --- a/src/bin/sccfdump.c +++ b/src/bin/sccfdump.c @@ -12,6 +12,9 @@ typedef struct { cbool dump_relocs; cbool dump_hex; cbool dump_all; + const char *get_section_name; + int get_section_idx; + cbool has_get_section_idx; int verbose; } sccfdump_config_t; @@ -188,6 +191,8 @@ int main(int argc, const char **argv, const char **envp) { HINT_HEX, HINT_VERBOSE, HINT_ALL, + HINT_GET_SECTION, + HINT_GET_SECTION_IDX, }; static const char *hints_en[] = { [HINT_PROG_NAME] = "sccfdump", @@ -201,6 +206,8 @@ int main(int argc, const char **argv, const char **envp) { [HINT_HEX] = "Dump section hex content", [HINT_VERBOSE] = "Verbose output (use with -x)", [HINT_ALL] = "Dump header, sections, symbols and relocs", + [HINT_GET_SECTION] = "Extract raw section data by name (e.g. .text)", + [HINT_GET_SECTION_IDX] = "Extract raw section data by index", }; static const char *hints_zh[] = { [HINT_PROG_NAME] = "sccfdump", @@ -214,6 +221,8 @@ int main(int argc, const char **argv, const char **envp) { [HINT_HEX] = "转存段的十六进制内容", [HINT_VERBOSE] = "详细输出(与-x配合使用)", [HINT_ALL] = "转存文件头、段、符号和重定位", + [HINT_GET_SECTION] = "按名称提取段原始数据(如 .text)", + [HINT_GET_SECTION_IDX] = "按索引提取段原始数据", }; scc_argparse_lang_t lang = SCC_ARGPARSE_LANG_EN; @@ -228,6 +237,7 @@ int main(int argc, const char **argv, const char **envp) { sccfdump_config_t config; scc_memset(&config, 0, sizeof(config)); config.dump_all = true; + config.get_section_idx = -1; scc_argparse_t argparse; scc_argparse_init(&argparse, hints[HINT_PROG_NAME], hints[HINT_DESCRIPTION]); @@ -275,6 +285,16 @@ int main(int argc, const char **argv, const char **envp) { scc_argparse_spec_setup_count(&opt_verbose.spec, &config.verbose); scc_argparse_cmd_add_opt(root, &opt_verbose); + scc_argparse_opt_t opt_get_section; + scc_argparse_opt_init(&opt_get_section, 0, "get-section", hints[HINT_GET_SECTION]); + scc_argparse_spec_setup_string(&opt_get_section.spec, &config.get_section_name); + scc_argparse_cmd_add_opt(root, &opt_get_section); + + scc_argparse_opt_t opt_get_section_idx; + scc_argparse_opt_init(&opt_get_section_idx, 0, "get-section-idx", hints[HINT_GET_SECTION_IDX]); + scc_argparse_spec_setup_int(&opt_get_section_idx.spec, &config.get_section_idx); + scc_argparse_cmd_add_opt(root, &opt_get_section_idx); + scc_argparse_arg_t arg_file; scc_argparse_arg_init(&arg_file, "file", hints[HINT_FILE]); scc_argparse_spec_setup_string(&arg_file.spec, &config.filepath); @@ -318,6 +338,60 @@ int main(int argc, const char **argv, const char **envp) { sccf_parse(&sccf, &buf, 0); u8 *base = scc_vec_unsafe_get_data(buf); + /* --get-section / --get-section-idx: extract raw data to a file/stdout */ + if (config.get_section_name || config.get_section_idx >= 0) { + usize idx = (usize)-1; + if (config.get_section_idx >= 0) { + idx = (usize)config.get_section_idx; + } else { + const sccf_header_t *hdr = (const sccf_header_t *)base; + for (usize i = 0; i < (usize)hdr->sect_header_num; i++) { + const sccf_sect_header_t *sh = sccf_sect_header(base, i); + if (sh && scc_memcmp(sh->name, + config.get_section_name, 8) == 0) { + idx = i; + break; + } + } + if (idx == (usize)-1) { + LOG_ERROR("section not found: %s", config.get_section_name); + scc_vec_free(buf); + return 1; + } + } + + const sccf_sect_header_t *sh = sccf_sect_header(base, idx); + if (!sh) { + LOG_ERROR("invalid section index: %d", config.get_section_idx); + scc_vec_free(buf); + return 1; + } + u8 *sdata = sccf_sect_data(base, idx); + if (!sdata || sh->size == 0) { + scc_vec_free(buf); + return 0; + } + + if (config.output && scc_strcmp(config.output, "-") == 0) { + scc_fwrite(scc_stdout, sdata, sh->size); + } else if (config.output) { + scc_file_t ofp = scc_fopen(config.output, SCC_FILE_WRITE); + if (!ofp) { + LOG_ERROR("cannot open output: %s", config.output); + scc_vec_free(buf); + return 1; + } + scc_fwrite(ofp, sdata, sh->size); + scc_fclose(ofp); + } else { + scc_fwrite(scc_stdout, sdata, sh->size); + } + + scc_vec_free(buf); + return 0; + } + + /* --hex without -o: also go to stdout */ if (config.dump_all) { dump_header(&sccf.header); dump_sections(base); diff --git a/tests/simple/test.py b/tests/simple/test.py index 361deb3..8df8ffe 100644 --- a/tests/simple/test.py +++ b/tests/simple/test.py @@ -7,6 +7,7 @@ executes the resulting binary, and validates the process return code or stdout. from __future__ import annotations import argparse +import difflib import logging import os import subprocess @@ -23,7 +24,8 @@ from typing import Sequence # Configuration # --------------------------------------------------------------------------- WORKSPACE = Path(__file__).resolve().parent -CC_PATH = WORKSPACE / "../../build/dev/scc" +CC_ROOT = WORKSPACE / "../.." +CC_PATH = CC_ROOT / "build/dev/scc" CONFIG_PATH = WORKSPACE / "expect.toml" DEFAULT_TIMEOUT = 1 # seconds @@ -99,8 +101,15 @@ class Runner: text=True, timeout=self.timeout, ) - except subprocess.TimeoutExpired: - return False, "Compilation timed out" + except subprocess.TimeoutExpired as e: + # e.stdout, e.stderr 为已捕获的部分输出(字符串) + captured_stderr = e.stderr.strip() if e.stderr else "" + captured_stdout = e.stdout.strip() if e.stdout else "" + err_msg = f"""Compilation timed out. + stderr: {captured_stderr or '(no output)'} + stdout: {captured_stdout or '(no output)'} + cmd: {" ".join(cmd)}""" + return False, err_msg except OSError as exc: return False, f"Failed to invoke compiler: {exc}" @@ -127,6 +136,26 @@ class Runner: return proc.returncode, proc.stdout, proc.stderr.strip() + def _print_diff(self, expected: str, actual: str) -> None: + """Print unified diff between expected and actual stdout.""" + expected_lines = expected.splitlines() + actual_lines = actual.splitlines() + diff = difflib.unified_diff( + expected_lines, actual_lines, + fromfile='expected', tofile='actual', + lineterm='' + ) + # diff = difflib.ndiff( + # expected_lines, actual_lines, + # ) + diff_lines = list(diff) + if not diff_lines: + logger.error(" Strings differ but no diff generated?") + return + logger.error(" Diff (expected vs actual):") + for line in diff_lines: + logger.error(" %s", line) + def run_one(self, test: TestCase) -> bool: """Run a single test case. Returns True if passed.""" logger.info("Testing %s", test.source) @@ -157,9 +186,14 @@ class Runner: if passed: logger.info(" PASSED") else: - logger.error( - " FAILED: expected %r, got %r", test.expected, actual - ) + if test.test_type == "stdout": + logger.error(" FAILED: stdout mismatch") + self._print_diff(test.expected, actual) + else: + logger.error( + " FAILED: expected return code %r, got %r", + test.expected, actual + ) # 4. Cleanup self._remove(exe_path) @@ -326,12 +360,12 @@ def main() -> None: runner = Runner( cc=args.cc, - workspace=WORKSPACE, + workspace=CC_ROOT, timeout=args.timeout, keep_temps=args.keep_temps, ) - global_timeout = DEFAULT_TIMEOUT * 3 + global_timeout = args.timeout * 3 passed, total = runner.run_all(selected, global_timeout) logger.info("=" * 40) if global_timeout and total < len(selected):