Skip to content

Commit d36cde4

Browse files
committed
Fixed silent failure points in code generation
1 parent 9ff6de5 commit d36cde4

7 files changed

Lines changed: 151 additions & 92 deletions

File tree

cc02/src/code-gen/generator.c

Lines changed: 78 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,19 @@ static int is_signed_type(type_t type) {
3939
}
4040

4141

42-
// Find the ZP slot assigned to a var/temp operand. Returns 0 if not found.
42+
// Sentinel for "operand not in ZP map". Must be a ZP address that is never
43+
// assigned by the map — $FF lives in the ABI param zone ($EF–$FF).
44+
#define ZP_NOT_FOUND 0xFF
45+
46+
// Find the ZP slot assigned to a var/temp operand. Returns ZP_NOT_FOUND if not found.
4347
static uint8_t zp_map_lookup(zp_map_t *map, tac_operand_t *op) {
4448
for (unsigned i = 0; i < map->count; i++) {
4549
zp_entry_t *e = &map->entries[i];
4650
if (e->kind != op->kind) continue;
4751
if (e->kind == OPERAND_VAR && strcmp(e->name, op->name) == 0) return e->zp_addr;
4852
if (e->kind == OPERAND_TEMP && e->temp_id == op->temp_id) return e->zp_addr;
4953
}
50-
return 0;
54+
return ZP_NOT_FOUND;
5155
}
5256

5357

@@ -57,9 +61,12 @@ static void zp_map_add(zp_map_t *map, tac_operand_kind_t kind,
5761
tac_operand_t probe = { .kind = kind };
5862
if (kind == OPERAND_VAR) probe.name = name;
5963
else probe.temp_id = temp_id;
60-
if (zp_map_lookup(map, &probe) != 0)
64+
if (zp_map_lookup(map, &probe) != ZP_NOT_FOUND)
65+
return;
66+
if (map->count >= ZP_MAP_MAX) {
67+
fprintf(stderr, "codegen: ZP map overflow (>%d operands)\n", ZP_MAP_MAX);
6168
return;
62-
if (map->count >= ZP_MAP_MAX) return;
69+
}
6370

6471
unsigned size = codegen_type_size(type);
6572
zp_entry_t *e = &map->entries[map->count++];
@@ -111,7 +118,10 @@ static void zp_map_build(zp_map_t *map, cfg_t *cfg) {
111118
static void register_func_label(emitter_t *e, char *name, uint16_t addr) {
112119
if (e->func_label_count >= e->func_label_capacity) {
113120
unsigned cap = e->func_label_capacity ? e->func_label_capacity * 2 : 8;
114-
e->func_labels = realloc(e->func_labels, cap * sizeof(func_label_t));
121+
func_label_t *grown = arena_alloc(&e->arena, cap * sizeof(func_label_t));
122+
if (e->func_labels)
123+
memcpy(grown, e->func_labels, e->func_label_count * sizeof(func_label_t));
124+
e->func_labels = grown;
115125
e->func_label_capacity = cap;
116126
}
117127
func_label_t *l = &e->func_labels[e->func_label_count++];
@@ -148,7 +158,10 @@ static int resolve_func_fixups(emitter_t *e) {
148158
static void add_fixup(emitter_t *e, char *func_name) {
149159
if (e->fixup_count >= e->fixup_capacity) {
150160
unsigned cap = e->fixup_capacity ? e->fixup_capacity * 2 : 8;
151-
e->fixups = realloc(e->fixups, cap * sizeof(fixup_t));
161+
fixup_t *grown = arena_alloc(&e->arena, cap * sizeof(fixup_t));
162+
if (e->fixups)
163+
memcpy(grown, e->fixups, e->fixup_count * sizeof(fixup_t));
164+
e->fixups = grown;
152165
e->fixup_capacity = cap;
153166
}
154167
fixup_t *f = &e->fixups[e->fixup_count++];
@@ -179,7 +192,10 @@ static int resolve_local_fixups(emitter_t *e) {
179192
static void add_local_fixup(emitter_t *e, unsigned label_id) {
180193
if (e->local_fixup_count >= e->local_fixup_capacity) {
181194
unsigned cap = e->local_fixup_capacity ? e->local_fixup_capacity * 2 : 8;
182-
e->local_fixups = realloc(e->local_fixups, cap * sizeof(fixup_t));
195+
fixup_t *grown = arena_alloc(&e->arena, cap * sizeof(fixup_t));
196+
if (e->local_fixups)
197+
memcpy(grown, e->local_fixups, e->local_fixup_count * sizeof(fixup_t));
198+
e->local_fixups = grown;
183199
e->local_fixup_capacity = cap;
184200
}
185201

@@ -208,7 +224,7 @@ static void allocate_globals(emitter_t *e, ir_gen_t *gen) {
208224
unsigned count = gen->module.global_count;
209225
if (count == 0) return;
210226

211-
e->global_entries = malloc(count * sizeof(global_entry_t));
227+
e->global_entries = arena_alloc(&e->arena, count * sizeof(global_entry_t));
212228
e->global_entry_count = count;
213229

214230
for (unsigned i = 0; i < count; i++) {
@@ -255,6 +271,9 @@ OP_EMITTER_SINGLE_ARG(ldx_imm, 0xA2)
255271
OP_EMITTER_SINGLE_ARG(ldy_imm, 0xA0)
256272
OP_EMITTER_SINGLE_ARG(sta_zpg, 0x85)
257273

274+
OP_EMITTER_SINGLE_ARG(ora_imm, 0x09)
275+
OP_EMITTER_SINGLE_ARG(ora_zpg, 0x05)
276+
258277
OP_EMITTER_SINGLE_ARG(cmp_imm, 0xC9)
259278
OP_EMITTER_SINGLE_ARG(cmp_zpg, 0xC5)
260279
OP_EMITTER_SINGLE_ARG(beq_rel, 0xF0)
@@ -280,6 +299,7 @@ OP_EMITTER_NO_ARG(sec, 0x38)
280299
OP_EMITTER_ABS(jmp_abs, 0x4C)
281300
OP_EMITTER_ABS(sta_abs, 0x8D)
282301
OP_EMITTER_ABS(lda_abs, 0xAD)
302+
OP_EMITTER_ABS(ora_abs, 0x0D)
283303
OP_EMITTER_ABS(cmp_abs, 0xCD)
284304
OP_EMITTER_ABS(inc_abs, 0xEE)
285305
OP_EMITTER_ABS(dec_abs, 0xCE)
@@ -308,7 +328,10 @@ static void jsr(emitter_t *e, char *func_name) {
308328
static void add_data_fixup(emitter_t *e, unsigned global_idx, uint8_t byte) {
309329
if (e->data_fixup_count >= e->data_fixup_capacity) {
310330
unsigned cap = e->data_fixup_capacity ? e->data_fixup_capacity * 2 : 8;
311-
e->data_fixups = realloc(e->data_fixups, cap * sizeof(data_fixup_t));
331+
data_fixup_t *grown = arena_alloc(&e->arena, cap * sizeof(data_fixup_t));
332+
if (e->data_fixups)
333+
memcpy(grown, e->data_fixups, e->data_fixup_count * sizeof(data_fixup_t));
334+
e->data_fixups = grown;
312335
e->data_fixup_capacity = cap;
313336
}
314337
data_fixup_t *f = &e->data_fixups[e->data_fixup_count++];
@@ -455,79 +478,40 @@ static void emit_store_byte(emitter_t *e, zp_map_t *map,
455478
}
456479

457480

458-
// Emit CMP against byte N of an operand. Global-aware: uses abs for globals.
459-
static void emit_cmp_byte(emitter_t *e, zp_map_t *map,
460-
tac_operand_t *op, unsigned byte) {
461-
switch (op->kind) {
462-
case OPERAND_CONST_INT:
463-
cmp_imm(e, (uint8_t)((op->int_val >> (8 * byte)) & 0xFF));
464-
break;
465-
case OPERAND_VAR: {
466-
global_entry_t *g = lookup_global(e, op->name);
467-
if (g)
468-
cmp_abs(e, (uint16_t)(g->ram_addr + byte));
469-
else
470-
cmp_zpg(e, (uint8_t)(zp_map_lookup(map, op) + byte));
471-
break;
472-
}
473-
case OPERAND_TEMP:
474-
cmp_zpg(e, (uint8_t)(zp_map_lookup(map, op) + byte));
475-
break;
476-
default: break;
481+
#define GLOBAL_AWARE_ALU_HELPER(NAME, IMM_FN, ZPG_FN, ABS_FN) \
482+
static void NAME(emitter_t *e, zp_map_t *map, \
483+
tac_operand_t *op, unsigned byte) { \
484+
switch (op->kind) { \
485+
case OPERAND_CONST_INT: \
486+
IMM_FN(e, (uint8_t)((op->int_val >> (8 * byte)) & 0xFF)); \
487+
break; \
488+
case OPERAND_VAR: { \
489+
global_entry_t *g = lookup_global(e, op->name); \
490+
if (g) \
491+
ABS_FN(e, (uint16_t)(g->ram_addr + byte)); \
492+
else \
493+
ZPG_FN(e, (uint8_t)(zp_map_lookup(map, op) + byte)); \
494+
break; \
495+
} \
496+
case OPERAND_TEMP: \
497+
ZPG_FN(e, (uint8_t)(zp_map_lookup(map, op) + byte)); \
498+
break; \
499+
default: break; \
500+
} \
477501
}
478-
}
479502

503+
GLOBAL_AWARE_ALU_HELPER(emit_ora_byte, ora_imm, ora_zpg, ora_abs)
504+
GLOBAL_AWARE_ALU_HELPER(emit_cmp_byte, cmp_imm, cmp_zpg, cmp_abs)
505+
GLOBAL_AWARE_ALU_HELPER(emit_adc_byte, adc_imm, adc_zpg, adc_abs)
506+
GLOBAL_AWARE_ALU_HELPER(emit_sbc_byte, sbc_imm, sbc_zpg, sbc_abs)
480507

481-
// Emit ADC against byte N of an operand. Global-aware: uses abs for globals.
482-
static void emit_adc_byte(emitter_t *e, zp_map_t *map,
483-
tac_operand_t *op, unsigned byte) {
484-
switch (op->kind) {
485-
case OPERAND_CONST_INT:
486-
adc_imm(e, (uint8_t)((op->int_val >> (8 * byte)) & 0xFF));
487-
break;
488-
case OPERAND_VAR: {
489-
global_entry_t *g = lookup_global(e, op->name);
490-
if (g)
491-
adc_abs(e, (uint16_t)(g->ram_addr + byte));
492-
else
493-
adc_zpg(e, (uint8_t)(zp_map_lookup(map, op) + byte));
494-
break;
495-
}
496-
case OPERAND_TEMP:
497-
adc_zpg(e, (uint8_t)(zp_map_lookup(map, op) + byte));
498-
break;
499-
default: break;
500-
}
501-
}
502-
503-
504-
// Emit SBC against byte N of an operand. Global-aware: uses abs for globals.
505-
static void emit_sbc_byte(emitter_t *e, zp_map_t *map,
506-
tac_operand_t *op, unsigned byte) {
507-
switch (op->kind) {
508-
case OPERAND_CONST_INT:
509-
sbc_imm(e, (uint8_t)((op->int_val >> (8 * byte)) & 0xFF));
510-
break;
511-
case OPERAND_VAR: {
512-
global_entry_t *g = lookup_global(e, op->name);
513-
if (g)
514-
sbc_abs(e, (uint16_t)(g->ram_addr + byte));
515-
else
516-
sbc_zpg(e, (uint8_t)(zp_map_lookup(map, op) + byte));
517-
break;
518-
}
519-
case OPERAND_TEMP:
520-
sbc_zpg(e, (uint8_t)(zp_map_lookup(map, op) + byte));
521-
break;
522-
default: break;
523-
}
524-
}
525-
526-
527-
// Emit conditional jump: LDA src; BEQ skip; JMP target. Jumps when nonzero.
508+
// Emit conditional jump: LDA src; [ORA src+1]; BEQ skip; JMP target. Jumps when nonzero.
528509
static void emit_cond_jump(emitter_t *e, zp_map_t *map,
529510
tac_operand_t *src, unsigned label_id) {
511+
unsigned width = codegen_type_size(src->type);
530512
emit_load_byte(e, map, src, 0);
513+
if (width > 1)
514+
emit_ora_byte(e, map, src, 1);
531515
beq_rel(e, 3);
532516
if (e->local_labels[label_id]) {
533517
jmp_abs(e, e->local_labels[label_id]);
@@ -549,8 +533,7 @@ static int emit_function_from_cfg(emitter_t *e, cfg_t *cfg) {
549533

550534
e->local_label_count = cfg->next_label;
551535
e->local_fixup_count = 0;
552-
e->local_labels = realloc(e->local_labels, cfg->next_label * sizeof(uint16_t));
553-
memset(e->local_labels, 0, cfg->next_label * sizeof(uint16_t));
536+
e->local_labels = arena_alloc(&e->arena, cfg->next_label * sizeof(uint16_t));
554537

555538
for (unsigned i = 0; i < cfg->block_count; ++i) {
556539
basic_block_t *block = cfg->blocks[i];
@@ -610,6 +593,10 @@ static int emit_function_from_cfg(emitter_t *e, cfg_t *cfg) {
610593
emit_load_byte(e, &map, &instruction->src1, b);
611594
sta_abs(e, (uint16_t)(base_addr + b));
612595
}
596+
} else {
597+
fprintf(stderr, "codegen: unhandled TAC_STORE with non-const destination (op kind %d)\n",
598+
instruction->dst.kind);
599+
return 0;
613600
}
614601
break;
615602
}
@@ -648,8 +635,16 @@ static int emit_function_from_cfg(emitter_t *e, cfg_t *cfg) {
648635

649636
case TAC_NOT: {
650637
uint8_t dst_addr = zp_map_lookup(&map, &instruction->dst);
638+
unsigned width = codegen_type_size(instruction->src1.type);
651639
emit_load_byte(e, &map, &instruction->src1, 0);
652-
eor_imm(e, 0x01);
640+
if (width > 1)
641+
emit_ora_byte(e, &map, &instruction->src1, 1);
642+
// A is now nonzero iff the original value was truthy.
643+
// Convert to boolean: 0 → 1, nonzero → 0.
644+
beq_rel(e, 4); // +4: skip LDA #0 + BEQ +2
645+
lda_imm(e, 0);
646+
beq_rel(e, 2); // +2: skip LDA #1 (always taken, Z=1)
647+
lda_imm(e, 1);
653648
sta_zpg(e, dst_addr);
654649
break;
655650
}
@@ -898,22 +893,18 @@ static int emit_function_from_cfg(emitter_t *e, cfg_t *cfg) {
898893
// Main code gen
899894
// ----------------------------------------------------------------
900895

901-
// Free all heap-allocated emitter resources (labels, fixups, globals).
902896
static void emitter_free(emitter_t *e) {
903-
free(e->func_labels);
904-
free(e->fixups);
905-
free(e->local_labels);
906-
free(e->local_fixups);
907-
free(e->global_entries);
908-
free(e->data_fixups);
897+
arena_free(&e->arena);
909898
}
910899

911900

912901
uint8_t *generate_rom(ir_gen_t *gen, size_t *final_rom_size) {
913902
emitter_t e = { 0 };
914903

904+
if (!arena_init(&e.arena, 4096)) return NULL;
905+
915906
e.rom = malloc(ROM_SIZE);
916-
if (!e.rom) return NULL;
907+
if (!e.rom) { arena_free(&e.arena); return NULL; }
917908

918909
memset(e.rom, 0xEA, ROM_SIZE);
919910
*final_rom_size = ROM_SIZE;

cc02/src/code-gen/generator.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ typedef struct {
4949
} data_fixup_t;
5050

5151
typedef struct {
52+
arena_t arena;
5253
uint8_t *rom;
5354
size_t rom_size;
5455
size_t code_pos;

cc02/src/ir-gen/ir.c

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ static tac_operand_t lower_expr(ir_gen_t *gen, cfg_t *cfg, node_t *node) {
358358
unsigned end_label = new_label(cfg);
359359

360360
tac_operand_t left = lower_expr(gen, cfg, node->binop.left);
361-
tac_operand_t neg = new_temp(cfg, left.type);
361+
tac_operand_t neg = new_temp(cfg, (type_t){ .kind = TYPE_U8 });
362362
emit(gen, cfg, (tac_instr_t){ .op = TAC_NOT, .dst = neg, .src1 = left });
363363
emit(gen, cfg, (tac_instr_t){ .op = TAC_COND_JUMP, .src1 = neg, .label = false_label });
364364

@@ -423,7 +423,9 @@ static tac_operand_t lower_expr(ir_gen_t *gen, cfg_t *cfg, node_t *node) {
423423
tac_operand_t operand = lower_expr(gen, cfg, node->unary.operand);
424424

425425
type_t result_type = operand.type;
426-
if (op == TAC_ADDR_OF) {
426+
if (op == TAC_NOT) {
427+
result_type = (type_t){ .kind = TYPE_U8 };
428+
} else if (op == TAC_ADDR_OF) {
427429
result_type.is_ptr = 1;
428430
result_type.ptr_depth++;
429431
}
@@ -614,7 +616,7 @@ static void lower_stmt(ir_gen_t *gen, cfg_t *cfg, node_t *node) {
614616
int has_else = block_count > 1;
615617

616618
tac_operand_t cond = lower_expr(gen, cfg, node->if_stmt.cond);
617-
tac_operand_t neg = new_temp(cfg, cond.type);
619+
tac_operand_t neg = new_temp(cfg, (type_t){ .kind = TYPE_U8 });
618620
emit(gen, cfg, (tac_instr_t){ .op = TAC_NOT, .dst = neg, .src1 = cond });
619621

620622
unsigned else_label = new_label(cfg);
@@ -653,7 +655,7 @@ static void lower_stmt(ir_gen_t *gen, cfg_t *cfg, node_t *node) {
653655

654656
// evaluate condition
655657
tac_operand_t cond = lower_expr(gen, cfg, node->while_stmt.cond);
656-
tac_operand_t neg = new_temp(cfg, cond.type);
658+
tac_operand_t neg = new_temp(cfg, (type_t){ .kind = TYPE_U8 });
657659

658660
// if !condition -> jump to end
659661
emit(gen, cfg, (tac_instr_t){ .op = TAC_NOT, .dst = neg, .src1 = cond });
@@ -685,7 +687,7 @@ static void lower_stmt(ir_gen_t *gen, cfg_t *cfg, node_t *node) {
685687
// evaluate condition
686688
if (node->for_stmt.cond) {
687689
tac_operand_t cond = lower_expr(gen, cfg, node->for_stmt.cond);
688-
tac_operand_t neg = new_temp(cfg, cond.type);
690+
tac_operand_t neg = new_temp(cfg, (type_t){ .kind = TYPE_U8 });
689691

690692
// if !condition -> jump to end
691693
emit(gen, cfg, (tac_instr_t){ .op = TAC_NOT, .dst = neg, .src1 = cond });

cc02/tests/emu_bare_cond_u16.c02

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
reg u8 PORTB @ 0x6000;
2+
3+
fn main() -> void {
4+
u16 x = 256;
5+
if (x) {
6+
PORTB = 42;
7+
}
8+
}

cc02/tests/emu_bare_cond_u8.c02

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
reg u8 PORTB @ 0x6000;
2+
3+
fn main() -> void {
4+
u8 x = 2;
5+
if (x) {
6+
PORTB = 42;
7+
}
8+
}

cc02/tests/emu_logical_not.c02

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
reg u8 PORTB @ 0x6000;
2+
3+
fn main() -> void {
4+
u8 x = 2;
5+
u8 y = !x;
6+
PORTB = y;
7+
if (!y) {
8+
PORTB = 42;
9+
}
10+
}

0 commit comments

Comments
 (0)