stage1 修复vm 提供简单参数检查 修复聚合类型bug
This commit is contained in:
@@ -476,7 +476,7 @@ void test_call_add(void) {
|
|||||||
* ret i64 ip=10
|
* ret i64 ip=10
|
||||||
*/
|
*/
|
||||||
spl_ins_t add_insns[] = {ins(SPL_LADDR, SPL_VOID, 0), ins(SPL_LOAD, SPL_I64, 0),
|
spl_ins_t add_insns[] = {ins(SPL_LADDR, SPL_VOID, 0), ins(SPL_LOAD, SPL_I64, 0),
|
||||||
ins(SPL_LADDR, SPL_VOID, 1), ins(SPL_LOAD, SPL_I64, 0),
|
ins(SPL_LADDR, SPL_VOID, 8), ins(SPL_LOAD, SPL_I64, 0),
|
||||||
ins(SPL_ADD, SPL_I64, 0), ins(SPL_RET, SPL_I64, 0)};
|
ins(SPL_ADD, SPL_I64, 0), ins(SPL_RET, SPL_I64, 0)};
|
||||||
spl_ins_t main_insns[] = {ins(SPL_PUSH, SPL_I32, 10), ins(SPL_PUSH, SPL_I32, 20),
|
spl_ins_t main_insns[] = {ins(SPL_PUSH, SPL_I32, 10), ins(SPL_PUSH, SPL_I32, 20),
|
||||||
ins(SPL_PUSH, SPL_VOID, 0), ins(SPL_CALL, SPL_VOID, 2),
|
ins(SPL_PUSH, SPL_VOID, 0), ins(SPL_CALL, SPL_VOID, 2),
|
||||||
@@ -583,7 +583,7 @@ void test_calli(void) {
|
|||||||
* ret i64 ip=11
|
* ret i64 ip=11
|
||||||
*/
|
*/
|
||||||
spl_ins_t add_insns[] = {ins(SPL_LADDR, SPL_VOID, 0), ins(SPL_LOAD, SPL_I64, 0),
|
spl_ins_t add_insns[] = {ins(SPL_LADDR, SPL_VOID, 0), ins(SPL_LOAD, SPL_I64, 0),
|
||||||
ins(SPL_LADDR, SPL_VOID, 1), ins(SPL_LOAD, SPL_I64, 0),
|
ins(SPL_LADDR, SPL_VOID, 8), ins(SPL_LOAD, SPL_I64, 0),
|
||||||
ins(SPL_ADD, SPL_I64, 0), ins(SPL_RET, SPL_I64, 0)};
|
ins(SPL_ADD, SPL_I64, 0), ins(SPL_RET, SPL_I64, 0)};
|
||||||
spl_ins_t main_insns[] = {ins(SPL_PUSH, SPL_I32, 10), ins(SPL_PUSH, SPL_I32, 20),
|
spl_ins_t main_insns[] = {ins(SPL_PUSH, SPL_I32, 10), ins(SPL_PUSH, SPL_I32, 20),
|
||||||
ins(SPL_PUSH, SPL_VOID, 2), ins(SPL_PUSH, SPL_VOID, 0),
|
ins(SPL_PUSH, SPL_VOID, 2), ins(SPL_PUSH, SPL_VOID, 0),
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ void spl_patch_to_here(spl_comp_t *ctx, spl_val_t addr) {
|
|||||||
spl_patch(ctx, addr, offset);
|
spl_patch(ctx, addr, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- Multi-slot copy helper ---- */
|
/* ---- Multi-slot copy helpers ---- */
|
||||||
|
|
||||||
void spl_emit_copy_slots(spl_comp_t *ctx, int dest_offset, usize nslots) {
|
void spl_emit_copy_slots(spl_comp_t *ctx, int dest_offset, usize nslots) {
|
||||||
for (usize i = 0; i < nslots; i++) {
|
for (usize i = 0; i < nslots; i++) {
|
||||||
@@ -124,6 +124,31 @@ void spl_emit_copy_slots(spl_comp_t *ctx, int dest_offset, usize nslots) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Copy nslots from source address to destination address.
|
||||||
|
* Stack before: [..., depth_items..., dest_addr, src_addr] (src TOS)
|
||||||
|
* Stack after: [..., depth_items...]
|
||||||
|
* depth: number of items below dest_addr that must be preserved (e.g. 1 if
|
||||||
|
* there's a base pointer between the rest of the stack and dest_addr). */
|
||||||
|
void spl_emit_copy_addr_to_addr(spl_comp_t *ctx, usize nslots, int depth) {
|
||||||
|
for (usize i = 0; i < nslots; i++) {
|
||||||
|
spl_emit(ctx, SPL_DUP, SPL_VOID, 0);
|
||||||
|
if (i > 0) {
|
||||||
|
spl_emit(ctx, SPL_PUSH, SPL_USIZE, i * sizeof(spl_val_t));
|
||||||
|
spl_emit(ctx, SPL_ADD, SPL_USIZE, 0);
|
||||||
|
}
|
||||||
|
spl_emit(ctx, SPL_LOAD, SPL_PTR, 0);
|
||||||
|
spl_emit(ctx, SPL_PICK, SPL_VOID, 2 + depth);
|
||||||
|
if (i > 0) {
|
||||||
|
spl_emit(ctx, SPL_PUSH, SPL_USIZE, i * sizeof(spl_val_t));
|
||||||
|
spl_emit(ctx, SPL_ADD, SPL_USIZE, 0);
|
||||||
|
}
|
||||||
|
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
|
||||||
|
spl_emit(ctx, SPL_STORE, SPL_PTR, 0);
|
||||||
|
}
|
||||||
|
spl_emit(ctx, SPL_DROP, SPL_VOID, 0);
|
||||||
|
spl_emit(ctx, SPL_DROP, SPL_VOID, 0);
|
||||||
|
}
|
||||||
|
|
||||||
/* ---- Uniform LOAD/STORE type helper ---- */
|
/* ---- Uniform LOAD/STORE type helper ---- */
|
||||||
|
|
||||||
spl_type_t spl_type_emit_type(spl_type_info_t *type) {
|
spl_type_t spl_type_emit_type(spl_type_info_t *type) {
|
||||||
@@ -165,17 +190,22 @@ int spl_type_is_scalar(spl_type_info_t *type) {
|
|||||||
|
|
||||||
void spl_emit_load_to_var(spl_comp_t *ctx, int ptr_slot_offset, usize byte_offset,
|
void spl_emit_load_to_var(spl_comp_t *ctx, int ptr_slot_offset, usize byte_offset,
|
||||||
spl_type_info_t *data_type, int var_offset) {
|
spl_type_info_t *data_type, int var_offset) {
|
||||||
spl_type_t bt = spl_type_emit_type(data_type);
|
|
||||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, ptr_slot_offset);
|
spl_emit(ctx, SPL_LADDR, SPL_PTR, ptr_slot_offset);
|
||||||
spl_emit(ctx, SPL_LOAD, SPL_PTR, 0);
|
spl_emit(ctx, SPL_LOAD, SPL_PTR, 0);
|
||||||
if (byte_offset > 0) {
|
if (byte_offset > 0) {
|
||||||
spl_emit(ctx, SPL_PUSH, SPL_USIZE, byte_offset);
|
spl_emit(ctx, SPL_PUSH, SPL_USIZE, byte_offset);
|
||||||
spl_emit(ctx, SPL_ADD, SPL_USIZE, 0);
|
spl_emit(ctx, SPL_ADD, SPL_USIZE, 0);
|
||||||
}
|
}
|
||||||
spl_emit(ctx, SPL_LOAD, bt, 0);
|
if (data_type && !spl_type_is_scalar(data_type) &&
|
||||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, var_offset);
|
spl_type_size(data_type) > sizeof(spl_val_t)) {
|
||||||
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
|
spl_emit_copy_slots(ctx, var_offset, spl_type_slot_count(data_type));
|
||||||
spl_emit(ctx, SPL_STORE, bt, 0);
|
} else {
|
||||||
|
spl_type_t bt = spl_type_emit_type(data_type);
|
||||||
|
spl_emit(ctx, SPL_LOAD, bt, 0);
|
||||||
|
spl_emit(ctx, SPL_LADDR, SPL_PTR, var_offset);
|
||||||
|
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
|
||||||
|
spl_emit(ctx, SPL_STORE, bt, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- Scope management ---- */
|
/* ---- Scope management ---- */
|
||||||
|
|||||||
@@ -287,6 +287,7 @@ void spl_patch_to_here(spl_comp_t *ctx, spl_val_t addr);
|
|||||||
* Stack: [..., temp_addr] → [...]
|
* Stack: [..., temp_addr] → [...]
|
||||||
* Copies nslots slots (each sizeof(spl_val_t) bytes) from temp offset to dest_offset. */
|
* Copies nslots slots (each sizeof(spl_val_t) bytes) from temp offset to dest_offset. */
|
||||||
void spl_emit_copy_slots(spl_comp_t *ctx, int dest_offset, usize nslots);
|
void spl_emit_copy_slots(spl_comp_t *ctx, int dest_offset, usize nslots);
|
||||||
|
void spl_emit_copy_addr_to_addr(spl_comp_t *ctx, usize nslots, int depth);
|
||||||
void spl_emit_ret(spl_comp_t *ctx, spl_type_info_t *ret_type);
|
void spl_emit_ret(spl_comp_t *ctx, spl_type_info_t *ret_type);
|
||||||
void spl_emit_store_init(spl_comp_t *ctx, int var_offset, spl_type_info_t *var_type);
|
void spl_emit_store_init(spl_comp_t *ctx, int var_offset, spl_type_info_t *var_type);
|
||||||
|
|
||||||
|
|||||||
@@ -457,7 +457,11 @@ static void parse_one_field_init(spl_comp_t *ctx, spl_field_vec_t *fields, int b
|
|||||||
spl_emit(ctx, SPL_ADD, SPL_USIZE, 0);
|
spl_emit(ctx, SPL_ADD, SPL_USIZE, 0);
|
||||||
}
|
}
|
||||||
spl_parse_expr(ctx, PREC_MIN);
|
spl_parse_expr(ctx, PREC_MIN);
|
||||||
spl_emit(ctx, SPL_STORE, st, 0);
|
if (elem_type && !spl_type_is_scalar(elem_type)) {
|
||||||
|
spl_emit_copy_addr_to_addr(ctx, spl_type_slot_count(elem_type), 1);
|
||||||
|
} else {
|
||||||
|
spl_emit(ctx, SPL_STORE, st, 0);
|
||||||
|
}
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
}
|
}
|
||||||
if (peek(ctx)->type == TOK_COMMA)
|
if (peek(ctx)->type == TOK_COMMA)
|
||||||
@@ -657,6 +661,67 @@ static int parse_call_args(spl_comp_t *ctx) {
|
|||||||
return nargs;
|
return nargs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Type check: warn when a value is passed where a pointer of the same type is expected.
|
||||||
|
* This catches bugs like passing `tok` (Token) where `*Token` is expected. */
|
||||||
|
static void spl_check_arg_type(spl_comp_t *ctx, const char *fname, spl_type_info_t *arg_type,
|
||||||
|
spl_type_info_t *param_type, int arg_idx) {
|
||||||
|
if (!arg_type || !param_type)
|
||||||
|
return;
|
||||||
|
if (param_type->kind == TYPE_PTR && param_type->elem && arg_type->kind != TYPE_PTR &&
|
||||||
|
arg_type->name && param_type->elem->name &&
|
||||||
|
strcmp(arg_type->name, param_type->elem->name) == 0) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"%s: warning: argument %d of '%s' expects '%s*', "
|
||||||
|
"got '%s' (missing '&'?)\n",
|
||||||
|
ctx->fname, arg_idx + 1, fname, param_type->elem->name, arg_type->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wrapper: parse_call_args with type checking against parameter types */
|
||||||
|
static int parse_call_args_checked(spl_comp_t *ctx, const char *fname,
|
||||||
|
spl_type_info_t **param_types, int nparams) {
|
||||||
|
int nargs = 0; /* physical slot count (for CALL) */
|
||||||
|
int nlogical = 0; /* logical arg index (for param_types lookup) */
|
||||||
|
if (peek(ctx)->type != TOK_R_PAREN) {
|
||||||
|
for (;;) {
|
||||||
|
spl_expr_result_t arg = spl_parse_expr(ctx, PREC_MIN);
|
||||||
|
if (param_types && nlogical < nparams && param_types[nlogical])
|
||||||
|
spl_check_arg_type(ctx, fname, arg.type, param_types[nlogical], nlogical);
|
||||||
|
|
||||||
|
/* For multi-slot struct/enum by-value parameters, expand
|
||||||
|
* the address left on the stack into multiple data slots. */
|
||||||
|
int arg_slots = 1;
|
||||||
|
if (param_types && nlogical < nparams && param_types[nlogical] && arg.type &&
|
||||||
|
param_types[nlogical]->kind != TYPE_PTR &&
|
||||||
|
!spl_type_is_scalar(param_types[nlogical]) &&
|
||||||
|
spl_type_size(param_types[nlogical]) > sizeof(spl_val_t)) {
|
||||||
|
usize nslots = (spl_type_size(param_types[nlogical]) + sizeof(spl_val_t) - 1) /
|
||||||
|
sizeof(spl_val_t);
|
||||||
|
for (usize i = 0; i < nslots; i++) {
|
||||||
|
spl_emit(ctx, SPL_DUP, SPL_VOID, 0);
|
||||||
|
if (i > 0) {
|
||||||
|
spl_emit(ctx, SPL_PUSH, SPL_USIZE, i * sizeof(spl_val_t));
|
||||||
|
spl_emit(ctx, SPL_ADD, SPL_USIZE, 0);
|
||||||
|
}
|
||||||
|
spl_emit(ctx, SPL_LOAD, SPL_PTR, 0);
|
||||||
|
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
|
||||||
|
}
|
||||||
|
spl_emit(ctx, SPL_DROP, SPL_VOID, 0);
|
||||||
|
arg_slots = (int)nslots;
|
||||||
|
}
|
||||||
|
|
||||||
|
nargs += arg_slots;
|
||||||
|
nlogical++;
|
||||||
|
if (peek(ctx)->type == TOK_COMMA) {
|
||||||
|
advance(ctx);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nargs;
|
||||||
|
}
|
||||||
|
|
||||||
static spl_expr_result_t parse_ident(spl_comp_t *ctx) {
|
static spl_expr_result_t parse_ident(spl_comp_t *ctx) {
|
||||||
spl_tok_t *t = advance(ctx);
|
spl_tok_t *t = advance(ctx);
|
||||||
char name[256];
|
char name[256];
|
||||||
@@ -673,7 +738,7 @@ static spl_expr_result_t parse_ident(spl_comp_t *ctx) {
|
|||||||
spl_func_info_t *f = &vec_at(ctx->funcs, fi);
|
spl_func_info_t *f = &vec_at(ctx->funcs, fi);
|
||||||
advance(ctx); /* skip ( */
|
advance(ctx); /* skip ( */
|
||||||
|
|
||||||
int nargs = parse_call_args(ctx);
|
int nargs = parse_call_args_checked(ctx, f->name, f->param_types, f->nparams);
|
||||||
expect(ctx, TOK_R_PAREN);
|
expect(ctx, TOK_R_PAREN);
|
||||||
|
|
||||||
if (f->is_extern) {
|
if (f->is_extern) {
|
||||||
@@ -690,6 +755,23 @@ static spl_expr_result_t parse_ident(spl_comp_t *ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
spl_expr_result_t r = {f->ret_type, 0};
|
spl_expr_result_t r = {f->ret_type, 0};
|
||||||
|
|
||||||
|
/* Multi-slot struct return: copy from returned address to caller's
|
||||||
|
* temp space. The returned address points to fp[1] in callee's frame
|
||||||
|
* (fp[0] is a gap). After RET, sp = callee_fp, so the first PUSH in
|
||||||
|
* emit_copy_slots (DUP) writes to the gap slot, not data. */
|
||||||
|
if (f->ret_type && !spl_type_is_scalar(f->ret_type) &&
|
||||||
|
spl_type_size(f->ret_type) > sizeof(spl_val_t)) {
|
||||||
|
usize nslots = spl_type_slot_count(f->ret_type);
|
||||||
|
int temp_offset = ctx->current_local_bytes;
|
||||||
|
ctx->current_local_bytes += (int)(nslots * sizeof(spl_val_t));
|
||||||
|
if (ctx->current_local_bytes > ctx->peak_local_bytes)
|
||||||
|
ctx->peak_local_bytes = ctx->current_local_bytes;
|
||||||
|
spl_emit_copy_slots(ctx, temp_offset, nslots);
|
||||||
|
spl_emit(ctx, SPL_LADDR, SPL_PTR, temp_offset);
|
||||||
|
r.is_lvalue = 1;
|
||||||
|
}
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -946,7 +1028,15 @@ static spl_expr_result_t parse_postfix_expr(spl_comp_t *ctx, spl_expr_result_t l
|
|||||||
nargs = 0;
|
nargs = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
nargs += parse_call_args(ctx);
|
{
|
||||||
|
spl_type_info_t **cp = func->param_types;
|
||||||
|
int cn = func->nparams;
|
||||||
|
if (is_instance && cn > 0) {
|
||||||
|
cp++;
|
||||||
|
cn--;
|
||||||
|
}
|
||||||
|
nargs += parse_call_args_checked(ctx, func->name, cp, cn);
|
||||||
|
}
|
||||||
expect(ctx, TOK_R_PAREN);
|
expect(ctx, TOK_R_PAREN);
|
||||||
|
|
||||||
/* Record fixup for forward-reference resolution */
|
/* Record fixup for forward-reference resolution */
|
||||||
@@ -957,6 +1047,20 @@ static spl_expr_result_t parse_postfix_expr(spl_comp_t *ctx, spl_expr_result_t l
|
|||||||
vec_push(ctx->call_fixup_funcs, func->func_idx);
|
vec_push(ctx->call_fixup_funcs, func->func_idx);
|
||||||
|
|
||||||
left = (spl_expr_result_t){func->ret_type, 0};
|
left = (spl_expr_result_t){func->ret_type, 0};
|
||||||
|
|
||||||
|
/* Multi-slot return: copy to temp, keep address */
|
||||||
|
if (func->ret_type && !spl_type_is_scalar(func->ret_type) &&
|
||||||
|
spl_type_size(func->ret_type) > sizeof(spl_val_t)) {
|
||||||
|
usize nslots = spl_type_slot_count(func->ret_type);
|
||||||
|
int temp_offset = ctx->current_local_bytes;
|
||||||
|
ctx->current_local_bytes += (int)(nslots * sizeof(spl_val_t));
|
||||||
|
if (ctx->current_local_bytes > ctx->peak_local_bytes)
|
||||||
|
ctx->peak_local_bytes = ctx->current_local_bytes;
|
||||||
|
spl_emit_copy_slots(ctx, temp_offset, nslots);
|
||||||
|
spl_emit(ctx, SPL_LADDR, SPL_PTR, temp_offset);
|
||||||
|
left.is_lvalue = 1;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1193,7 +1297,10 @@ static spl_expr_result_t parse_infix(spl_comp_t *ctx, spl_expr_result_t left, sp
|
|||||||
|
|
||||||
if (left.is_lvalue) {
|
if (left.is_lvalue) {
|
||||||
spl_type_t bt = spl_type_emit_type(left.type);
|
spl_type_t bt = spl_type_emit_type(left.type);
|
||||||
if (op == TOK_ASSIGN) {
|
if (op == TOK_ASSIGN && left.type && !spl_type_is_scalar(left.type) &&
|
||||||
|
right.is_lvalue) {
|
||||||
|
spl_emit_copy_addr_to_addr(ctx, spl_type_slot_count(left.type), 0);
|
||||||
|
} else if (op == TOK_ASSIGN) {
|
||||||
spl_emit(ctx, SPL_STORE, bt, 0);
|
spl_emit(ctx, SPL_STORE, bt, 0);
|
||||||
} else {
|
} else {
|
||||||
spl_emit(ctx, SPL_PICK, SPL_VOID, 1);
|
spl_emit(ctx, SPL_PICK, SPL_VOID, 1);
|
||||||
@@ -1310,8 +1417,20 @@ void spl_emit_match_value_cmp(spl_comp_t *ctx, int val_offset) {
|
|||||||
* ============================================================ */
|
* ============================================================ */
|
||||||
|
|
||||||
void spl_emit_ret(spl_comp_t *ctx, spl_type_info_t *ret_type) {
|
void spl_emit_ret(spl_comp_t *ctx, spl_type_info_t *ret_type) {
|
||||||
spl_type_t rt = ret_type ? spl_type_emit_type(ret_type) : SPL_VOID;
|
if (ret_type && !spl_type_is_scalar(ret_type) && spl_type_size(ret_type) > sizeof(spl_val_t)) {
|
||||||
spl_emit(ctx, SPL_RET, rt, 0);
|
/* Multi-slot struct return: copy to fp[0..nslots-1] (above saved_sp),
|
||||||
|
* then return address of fp[0]. Caller copies from that address to its
|
||||||
|
* own temp space (see parse_ident / method call handling). */
|
||||||
|
usize nslots = spl_type_slot_count(ret_type);
|
||||||
|
/* Store at fp[1..nslots] (skip fp[0] as gap) so that RET's PUSH
|
||||||
|
* and caller's LADDR both write to the gap slot, not the data. */
|
||||||
|
spl_emit_copy_slots(ctx, (int)sizeof(spl_val_t), nslots);
|
||||||
|
spl_emit(ctx, SPL_LADDR, SPL_PTR, (int)sizeof(spl_val_t));
|
||||||
|
spl_emit(ctx, SPL_RET, SPL_PTR, 0);
|
||||||
|
} else {
|
||||||
|
spl_type_t rt = ret_type ? spl_type_emit_type(ret_type) : SPL_VOID;
|
||||||
|
spl_emit(ctx, SPL_RET, rt, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
@@ -1336,7 +1455,11 @@ void spl_emit_store_init(spl_comp_t *ctx, int var_offset, spl_type_info_t *var_t
|
|||||||
for (int i = (int)var_type->array_len - 1; i >= 0; i--) {
|
for (int i = (int)var_type->array_len - 1; i >= 0; i--) {
|
||||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, var_offset + (int)(i * stride));
|
spl_emit(ctx, SPL_LADDR, SPL_PTR, var_offset + (int)(i * stride));
|
||||||
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
|
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
|
||||||
spl_emit(ctx, SPL_STORE, bt, 0);
|
if (elem && !spl_type_is_scalar(elem)) {
|
||||||
|
spl_emit_copy_addr_to_addr(ctx, spl_type_slot_count(elem), 0);
|
||||||
|
} else {
|
||||||
|
spl_emit(ctx, SPL_STORE, bt, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (var_type && var_type->kind == TYPE_SLICE) {
|
} else if (var_type && var_type->kind == TYPE_SLICE) {
|
||||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, var_offset + (int)sizeof(spl_val_t));
|
spl_emit(ctx, SPL_LADDR, SPL_PTR, var_offset + (int)sizeof(spl_val_t));
|
||||||
|
|||||||
@@ -86,14 +86,49 @@ static int parse_fn_body(spl_comp_t *ctx, const char *fn_name, spl_type_info_t *
|
|||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
spl_patch(ctx, alloc_addr, ctx->peak_local_bytes / (int)sizeof(spl_val_t) - nparams);
|
/* Compute physical param slot count (1 slot = 8 bytes).
|
||||||
|
* Multi-slot struct params occupy ceil(size/8) slots; scalar params occupy 1 each. */
|
||||||
|
int total_phys_slots = 0;
|
||||||
|
for (int i = 0; i < nparams; i++) {
|
||||||
|
usize psz = spl_type_size(ptypes[i]);
|
||||||
|
total_phys_slots += (int)((psz + sizeof(spl_val_t) - 1) / sizeof(spl_val_t));
|
||||||
|
}
|
||||||
|
/* Ensure ALLOC provides enough space for multi-slot return value
|
||||||
|
* pre-placed at fp[0..nslots-1] (above saved_sp). */
|
||||||
|
if (ret_type && !spl_type_is_scalar(ret_type) &&
|
||||||
|
spl_type_size(ret_type) > sizeof(spl_val_t)) {
|
||||||
|
usize min_bytes = spl_type_slot_count(ret_type) * sizeof(spl_val_t);
|
||||||
|
if ((usize)ctx->peak_local_bytes < min_bytes)
|
||||||
|
ctx->peak_local_bytes = (int)min_bytes;
|
||||||
|
}
|
||||||
|
spl_patch(ctx, alloc_addr,
|
||||||
|
ctx->peak_local_bytes / (int)sizeof(spl_val_t) - total_phys_slots);
|
||||||
expect(ctx, TOK_R_BRACE);
|
expect(ctx, TOK_R_BRACE);
|
||||||
}
|
}
|
||||||
|
|
||||||
spl_emit_defer_epilogue(ctx, ctx->scope_depth);
|
spl_emit_defer_epilogue(ctx, ctx->scope_depth);
|
||||||
spl_pop_scope(ctx);
|
spl_pop_scope(ctx);
|
||||||
|
|
||||||
spl_emit(ctx, SPL_RET, SPL_VOID, 0);
|
/* Store param_types for call-site type checking and multi-slot expansion */
|
||||||
|
{
|
||||||
|
spl_func_info_t *f = &vec_at(ctx->funcs, fi);
|
||||||
|
f->param_types = calloc(nparams, sizeof(spl_type_info_t *));
|
||||||
|
f->param_names = calloc(nparams, sizeof(char *));
|
||||||
|
for (int i = 0; i < nparams; i++) {
|
||||||
|
f->param_types[i] = ptypes[i];
|
||||||
|
f->param_names[i] = strdup(pnames[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Epilogue return: for multi-slot returns, return address of fp[1]
|
||||||
|
* (fp[0] is a gap slot that absorbs the RET PUSH, data at fp[1..nslots]). */
|
||||||
|
if (ctx->current_ret_type && !spl_type_is_scalar(ctx->current_ret_type) &&
|
||||||
|
spl_type_size(ctx->current_ret_type) > sizeof(spl_val_t)) {
|
||||||
|
spl_emit(ctx, SPL_LADDR, SPL_PTR, (int)sizeof(spl_val_t));
|
||||||
|
spl_emit(ctx, SPL_RET, SPL_PTR, 0);
|
||||||
|
} else {
|
||||||
|
spl_emit(ctx, SPL_RET, SPL_VOID, 0);
|
||||||
|
}
|
||||||
spl_prog_end_func(&ctx->prog, fi);
|
spl_prog_end_func(&ctx->prog, fi);
|
||||||
ctx->current_func_idx = -1;
|
ctx->current_func_idx = -1;
|
||||||
ctx->current_ret_type = NULL;
|
ctx->current_ret_type = NULL;
|
||||||
@@ -183,16 +218,6 @@ static void parse_method_decl(spl_comp_t *ctx, spl_type_info_t *container) {
|
|||||||
|
|
||||||
int fi = parse_fn_body(ctx, qualified, ret_type, nparams, pnames, ptypes, 0);
|
int fi = parse_fn_body(ctx, qualified, ret_type, nparams, pnames, ptypes, 0);
|
||||||
|
|
||||||
{
|
|
||||||
spl_func_info_t *f = &vec_at(ctx->funcs, fi);
|
|
||||||
f->param_types = calloc(nparams, sizeof(spl_type_info_t *));
|
|
||||||
f->param_names = calloc(nparams, sizeof(char *));
|
|
||||||
for (int i = 0; i < nparams; i++) {
|
|
||||||
f->param_types[i] = ptypes[i];
|
|
||||||
f->param_names[i] = strdup(pnames[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
spl_type_add_method(container, mname, fi);
|
spl_type_add_method(container, mname, fi);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -451,11 +451,16 @@ static void parse_for_stmt(spl_comp_t *ctx) {
|
|||||||
spl_emit(ctx, SPL_PUSH, SPL_U64, elem_byte_size);
|
spl_emit(ctx, SPL_PUSH, SPL_U64, elem_byte_size);
|
||||||
spl_emit(ctx, SPL_MUL, SPL_U64, 0);
|
spl_emit(ctx, SPL_MUL, SPL_U64, 0);
|
||||||
spl_emit(ctx, SPL_ADD, SPL_U64, 0);
|
spl_emit(ctx, SPL_ADD, SPL_U64, 0);
|
||||||
spl_type_t elem_bt = elem_type ? spl_type_emit_type(elem_type) : SPL_I32;
|
if (elem_type && !spl_type_is_scalar(elem_type) &&
|
||||||
spl_emit(ctx, SPL_LOAD, elem_bt, 0);
|
spl_type_size(elem_type) > sizeof(spl_val_t)) {
|
||||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, val_offset);
|
spl_emit_copy_slots(ctx, val_offset, spl_type_slot_count(elem_type));
|
||||||
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
|
} else {
|
||||||
spl_emit(ctx, SPL_STORE, elem_bt, 0);
|
spl_type_t elem_bt = elem_type ? spl_type_emit_type(elem_type) : SPL_I32;
|
||||||
|
spl_emit(ctx, SPL_LOAD, elem_bt, 0);
|
||||||
|
spl_emit(ctx, SPL_LADDR, SPL_PTR, val_offset);
|
||||||
|
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
|
||||||
|
spl_emit(ctx, SPL_STORE, elem_bt, 0);
|
||||||
|
}
|
||||||
|
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
spl_parse_block(ctx); /* body */
|
spl_parse_block(ctx); /* body */
|
||||||
|
|||||||
@@ -8,6 +8,133 @@ type Pair = struct {
|
|||||||
b: i32,
|
b: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Three = struct {
|
||||||
|
a: i64,
|
||||||
|
b: i64,
|
||||||
|
c: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Tag = enum {
|
||||||
|
Unknown,
|
||||||
|
BlockComment,
|
||||||
|
LineComment,
|
||||||
|
Ident,
|
||||||
|
Number,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Token = struct {
|
||||||
|
loc_start: i32,
|
||||||
|
loc_end: i32,
|
||||||
|
tag: Tag,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_three(a: i64, b: i64, c: i64) Three {
|
||||||
|
ret Three { .a = a, .b = b, .c = c };
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sum_three(t: Three) i64 {
|
||||||
|
ret t.a + t.b + t.c;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_token(s: i32, e: i32, t: Tag) Token {
|
||||||
|
ret Token { .loc_start = s, .loc_end = e, .tag = t };
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_ret_enum_struct() i32 {
|
||||||
|
var tok := make_token(10, 20, Tag.BlockComment);
|
||||||
|
if tok.loc_start != 10 { ret 1; }
|
||||||
|
if tok.loc_end != 20 { ret 2; }
|
||||||
|
if tok.tag != Tag.BlockComment { ret 3; }
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_nested_ret_enum() i32 {
|
||||||
|
var tok := make_token(1, 5, Tag.Ident);
|
||||||
|
if tok.tag != Tag.Ident { ret 1; }
|
||||||
|
var tok2 := make_token(6, 9, Tag.Number);
|
||||||
|
if tok2.tag != Tag.Number { ret 2; }
|
||||||
|
if tok.tag != Tag.Ident { ret 3; }
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_copy_token() i32 {
|
||||||
|
var tok := make_token(0, 100, Tag.LineComment);
|
||||||
|
var c := tok;
|
||||||
|
if c.loc_start != 0 { ret 1; }
|
||||||
|
if c.tag != Tag.LineComment { ret 2; }
|
||||||
|
c.tag = Tag.Ident;
|
||||||
|
if tok.tag != Tag.LineComment { ret 3; }
|
||||||
|
if c.tag != Tag.Ident { ret 4; }
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_pass_token(t: Token) i32 {
|
||||||
|
if t.tag != Tag.Number { ret 1; }
|
||||||
|
if t.loc_start != 42 { ret 2; }
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_param_pass_token() i32 {
|
||||||
|
var tok := make_token(42, 99, Tag.Number);
|
||||||
|
var r := test_pass_token(tok);
|
||||||
|
if r != 0 { ret r; }
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_get_tag(tok: Token) i32 {
|
||||||
|
ret tok.tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_chain_func_call() i32 {
|
||||||
|
var tok := make_token(1, 2, Tag.BlockComment);
|
||||||
|
var tg := test_get_tag(tok);
|
||||||
|
if tg != Tag.BlockComment { ret 1; }
|
||||||
|
var tok2 := make_token(3, 4, Tag.LineComment);
|
||||||
|
var tg2 := test_get_tag(tok2);
|
||||||
|
if tg2 != Tag.LineComment { ret 2; }
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_ret_multi_slot_struct() i32 {
|
||||||
|
var t := make_three(10, 20, 30);
|
||||||
|
if t.a != 10 { ret 1; }
|
||||||
|
if t.b != 20 { ret 2; }
|
||||||
|
if t.c != 30 { ret 3; }
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_pass_struct_by_value() i32 {
|
||||||
|
var t := make_three(10, 20, 30);
|
||||||
|
var sum := sum_three(t);
|
||||||
|
if sum != 60 { ret 1; }
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_copy_struct() i32 {
|
||||||
|
var t := make_three(10, 20, 30);
|
||||||
|
var t2: Three;
|
||||||
|
t2 = t;
|
||||||
|
if t2.a != 10 { ret 1; }
|
||||||
|
t2.a = 99;
|
||||||
|
if t.a != 10 { ret 2; }
|
||||||
|
if t2.a != 99 { ret 3; }
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_multi_calls() i32 {
|
||||||
|
var ta := make_three(5, 10, 15);
|
||||||
|
var tb := make_three(20, 25, 30);
|
||||||
|
if ta.a + ta.b + ta.c != 30 { ret 1; }
|
||||||
|
if tb.a + tb.b + tb.c != 75 { ret 2; }
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_literal_arg() i32 {
|
||||||
|
var sum := sum_three(Three { .a = 7, .b = 8, .c = 9 });
|
||||||
|
if sum != 24 { ret 1; }
|
||||||
|
ret 0;
|
||||||
|
}
|
||||||
|
|
||||||
fn main() i32 {
|
fn main() i32 {
|
||||||
/* struct 字段访问 */
|
/* struct 字段访问 */
|
||||||
var p: Pair;
|
var p: Pair;
|
||||||
@@ -60,5 +187,29 @@ fn main() i32 {
|
|||||||
if o.inner.val != 42 { ret 7; }
|
if o.inner.val != 42 { ret 7; }
|
||||||
if o.extra != 58 { ret 8; }
|
if o.extra != 58 { ret 8; }
|
||||||
|
|
||||||
|
/* 多 slot 结构体返回值测试 */
|
||||||
|
var r9 := test_ret_multi_slot_struct();
|
||||||
|
if r9 != 0 { ret 100; }
|
||||||
|
var r10 := test_pass_struct_by_value();
|
||||||
|
if r10 != 0 { ret 101; }
|
||||||
|
var r11 := test_copy_struct();
|
||||||
|
if r11 != 0 { ret 102; }
|
||||||
|
var r12 := test_multi_calls();
|
||||||
|
if r12 != 0 { ret 103; }
|
||||||
|
var r13 := test_literal_arg();
|
||||||
|
if r13 != 0 { ret 104; }
|
||||||
|
|
||||||
|
/* 含 enum 字段的结构体返回值测试 */
|
||||||
|
var r14 := test_ret_enum_struct();
|
||||||
|
if r14 != 0 { ret 110; }
|
||||||
|
var r15 := test_nested_ret_enum();
|
||||||
|
if r15 != 0 { ret 111; }
|
||||||
|
var r16 := test_copy_token();
|
||||||
|
if r16 != 0 { ret 112; }
|
||||||
|
var r17 := test_param_pass_token();
|
||||||
|
if r17 != 0 { ret 113; }
|
||||||
|
var r18 := test_chain_func_call();
|
||||||
|
if r18 != 0 { ret 114; }
|
||||||
|
|
||||||
ret 0;
|
ret 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user