Skip to content

Commit 66b729f

Browse files
committed
codegen label entrypoint fix
1 parent 00073fa commit 66b729f

5 files changed

Lines changed: 78 additions & 2 deletions

File tree

src/lower/arm64/codegen.c

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ static uint64_t cg_now_ns(void) {
1111
return (t * (uint64_t)tb.numer) / (uint64_t)tb.denom;
1212
}
1313

14+
static const char *g_entry_label_override = NULL;
15+
16+
void cg_set_entry_label_override(const char *label) {
17+
g_entry_label_override = label;
18+
}
19+
1420
typedef struct {
1521
const char *key;
1622
symtab_entry *val;
@@ -433,6 +439,7 @@ static void emit_adrp_add(cg_blob_t *out, cg_profile_t *prof, uint32_t *w, size_
433439

434440
int cg_emit_arm64(const ir_prog_t *ir, cg_blob_t *out) {
435441
if (!ir || !out) return -1;
442+
const char *entry_override = (g_entry_label_override && g_entry_label_override[0]) ? g_entry_label_override : NULL;
436443
const int profile_enabled = out->profile_enabled;
437444
memset(out,0,sizeof(*out));
438445
out->profile_enabled = profile_enabled;
@@ -512,14 +519,20 @@ int cg_emit_arm64(const ir_prog_t *ir, cg_blob_t *out) {
512519

513520
/* Determine function entry labels: first label, labels named "main", labels prefixed with "fn_", and labels that are CALL targets. */
514521
const uint64_t t_func0 = prof ? cg_now_ns() : 0;
522+
int override_found = 0;
515523
for (size_t i = 0; i < n_labels; i++) {
516524
int is_func = 0;
517-
if (!first_label_seen) { is_func = 1; first_label_seen = 1; }
525+
if (entry_override && label_list[i].name && strcmp(label_list[i].name, entry_override) == 0) { is_func = 1; override_found = 1; }
526+
if (!entry_override && !first_label_seen) { is_func = 1; first_label_seen = 1; }
518527
if (label_list[i].name && strcmp(label_list[i].name, "main") == 0) is_func = 1;
519528
if (label_list[i].name && strncmp(label_list[i].name, "fn_", 3) == 0) is_func = 1;
520529
if (label_list[i].name && seen_has(call_targets, n_calls, label_list[i].name)) is_func = 1;
521530
if (is_func) func_count++;
522531
}
532+
if (entry_override && !override_found) {
533+
if (prof) prof->func_detect_ns = cg_now_ns() - t_func0;
534+
CG_FAIL(NULL, "--entry-label symbol not found in input labels");
535+
}
523536
if (func_count == 0 && any_instr) func_count = 1;
524537
if (prof) prof->func_detect_ns = cg_now_ns() - t_func0;
525538

@@ -556,7 +569,8 @@ int cg_emit_arm64(const ir_prog_t *ir, cg_blob_t *out) {
556569
if (e->kind == IR_ENTRY_LABEL) {
557570
if (e->u.label.name) {
558571
int is_func = 0;
559-
if (!func_seen) is_func = 1;
572+
if (entry_override && strcmp(e->u.label.name, entry_override) == 0) is_func = 1;
573+
if (!entry_override && !func_seen) is_func = 1;
560574
if (strcmp(e->u.label.name, "main") == 0) is_func = 1;
561575
if (strncmp(e->u.label.name, "fn_", 3) == 0) is_func = 1;
562576
if (seen_has(call_targets, n_calls, e->u.label.name)) is_func = 1;
@@ -566,6 +580,18 @@ int cg_emit_arm64(const ir_prog_t *ir, cg_blob_t *out) {
566580
func_has_prologue = 0;
567581
}
568582
symtab_update(&ctx, out->syms, e->u.label.name, pcw*4);
583+
584+
/* Emit the function prologue at the function entry label.
585+
* This ensures any immediately-following label is placed after the
586+
* prologue (distinct address), avoiding accidental stack growth when
587+
* branching to a basic-block label that would otherwise share the
588+
* function entry address.
589+
*/
590+
if (is_func && !func_has_prologue) {
591+
w[pcw++] = enc_stp_fp_lr();
592+
w[pcw++] = enc_mov_fp_sp();
593+
func_has_prologue = 1;
594+
}
569595
}
570596
if (prof) prof->pass2_labels_ns += cg_now_ns() - t_e0;
571597
continue;

src/lower/arm64/codegen.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,10 @@ typedef struct {
6464
int cg_emit_arm64(const ir_prog_t *ir, cg_blob_t *out);
6565
void cg_free(cg_blob_t *out);
6666

67+
/* Optional: override the implicit entry-function heuristic.
68+
* When set, cg_emit_arm64 will treat this label as a function entry.
69+
* Pass NULL to clear.
70+
*/
71+
void cg_set_entry_label_override(const char *label);
72+
6773
#endif /* ZINGCC_CODEGEN_H */

src/lower/arm64/main.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ static void usage(const char *prog) {
4444
" --emit-pc-map Write JSON mapping of code offsets to IR ids/lines\n"
4545
" --emit-lldb Write an LLDB trace script to the given path\n"
4646
" --trace-func Function symbol to trace (default: main)\n"
47+
" --entry-label Override implicit entry label (disables first-label heuristic)\n"
4748
" --trace-syms Comma list of symbols to dump at trace breakpoints\n"
4849
" --trace-regs Comma list of registers (e.g. w0,x0) to dump\n"
4950
" --profile Print per-phase timings to stderr\n"
@@ -480,6 +481,7 @@ int main(int argc, char **argv) {
480481
int json_dump = 0;
481482
const char *emit_lldb_path = NULL;
482483
const char *trace_func = "main";
484+
const char *entry_label = NULL;
483485
const char *trace_syms = NULL;
484486
const char *trace_regs = NULL;
485487
int profile = 0;
@@ -510,6 +512,7 @@ int main(int argc, char **argv) {
510512
if (strcmp(a, "--emit-pc-map") == 0 && argi < argc) { emit_pc_map_path = argv[argi++]; continue; }
511513
if (strcmp(a, "--emit-lldb") == 0 && argi < argc) { emit_lldb_path = argv[argi++]; continue; }
512514
if (strcmp(a, "--trace-func") == 0 && argi < argc) { trace_func = argv[argi++]; continue; }
515+
if (strcmp(a, "--entry-label") == 0 && argi < argc) { entry_label = argv[argi++]; continue; }
513516
if (strcmp(a, "--trace-syms") == 0 && argi < argc) { trace_syms = argv[argi++]; continue; }
514517
if (strcmp(a, "--trace-regs") == 0 && argi < argc) { trace_regs = argv[argi++]; continue; }
515518
if (strcmp(a, "--input") == 0 && argi < argc) { inputs[nin++] = argv[argi++]; continue; }
@@ -544,6 +547,9 @@ int main(int argc, char **argv) {
544547
ir_prog_t prog;
545548
ir_init(&prog);
546549

550+
/* Configure optional entry label override for codegen. */
551+
cg_set_entry_label_override(entry_label);
552+
547553
const uint64_t t_parse0 = profile ? now_ns() : 0;
548554
if (json_ir_read(fp, &prog) != 0) {
549555
fprintf(stderr, "[lower] failed to parse IR: %s\n", in_path);

src/lower/arm64/tests/codegen_cases.jsonl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
{"ir":"zasm-v1.0","k":"label","name":"main"}
2+
{"ir":"zasm-v1.0","k":"label","name":"loop"}
3+
{"ir":"zasm-v1.0","k":"instr","m":"LD","ops":[{"t":"sym","v":"HL"},{"t":"num","v":0}]}
4+
{"ir":"zasm-v1.0","k":"instr","m":"JR","ops":[{"t":"sym","v":"loop"}]}
5+
16
{"ir":"zasm-v1.0","k":"dir","d":"DB","name":"msg","args":[{"t":"str","v":"Hi"},{"t":"num","v":0}]}
27
{"ir":"zasm-v1.0","k":"dir","d":"RESB","name":"buf","args":[{"t":"num","v":9000}]}
38
{"ir":"zasm-v1.0","k":"instr","m":"LD","ops":[{"t":"sym","v":"HL"},{"t":"num","v":42}]}

src/lower/arm64/tests/codegen_test.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,20 @@
44
#include <stdlib.h>
55
#include <string.h>
66

7+
static symtab_entry *find_sym(symtab_entry *root, const char *name) {
8+
for (symtab_entry *s = root; s; s = s->next) {
9+
if (s->name && strcmp(s->name, name) == 0) return s;
10+
}
11+
return NULL;
12+
}
13+
14+
static int ir_has_label(const ir_prog_t *ir, const char *name) {
15+
for (ir_entry_t *e = ir->head; e; e = e->next) {
16+
if (e->kind == IR_ENTRY_LABEL && e->u.label.name && strcmp(e->u.label.name, name) == 0) return 1;
17+
}
18+
return 0;
19+
}
20+
721
static int run_file(const char *path){
822
FILE *f = fopen(path,"r");
923
if (!f) { perror("open"); return 1; }
@@ -12,6 +26,25 @@ static int run_file(const char *path){
1226
fclose(f);
1327
cg_blob_t blob={0};
1428
if (cg_emit_arm64(&ir,&blob)!=0){fprintf(stderr,"codegen failed\n"); ir_free(&ir); return 1;}
29+
30+
/* Regression guard: if a stream begins with adjacent labels (e.g. main then loop)
31+
* and lowerer function prologue insertion happens at the first instruction rather
32+
* than at the function entry label, both labels can wind up with the same address.
33+
* Branching to the second label can then re-run the prologue and grow the stack.
34+
*/
35+
if (ir_has_label(&ir, "main") && ir_has_label(&ir, "loop")) {
36+
symtab_entry *s_main = find_sym(blob.syms, "main");
37+
symtab_entry *s_loop = find_sym(blob.syms, "loop");
38+
if (s_main && s_loop && s_main->off != (size_t)-1 && s_loop->off != (size_t)-1) {
39+
if (s_loop->off <= s_main->off) {
40+
fprintf(stderr, "codegen regression: loop label (off=%zu) not after main (off=%zu)\n", s_loop->off, s_main->off);
41+
cg_free(&blob);
42+
ir_free(&ir);
43+
return 1;
44+
}
45+
}
46+
}
47+
1548
printf("code %zu bytes, data %zu bytes, relocs %u\n", blob.code_len, blob.data_len, blob.reloc_count);
1649
cg_free(&blob);
1750
ir_free(&ir);

0 commit comments

Comments
 (0)