From a2d9ca86973c87d01afcb09298ba034d8b3d2d17 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 24 Aug 2026 22:25:53 +0100 Subject: [PATCH] Remove unneeded hash maps when you can store the data directly on the nodes --- src/check_asm.cpp | 28 ++++++++++++++-------------- src/check_asm_cfg.cpp | 43 +++++++++++++++++++++++++------------------ src/check_expr.cpp | 4 ++++ src/parser.cpp | 1 + src/parser.hpp | 1 + 5 files changed, 45 insertions(+), 32 deletions(-) diff --git a/src/check_asm.cpp b/src/check_asm.cpp index 2b0fde8d7..1d5da9b02 100644 --- a/src/check_asm.cpp +++ b/src/check_asm.cpp @@ -1121,11 +1121,11 @@ gb_internal void check_mnemonic(AsmCtx *asm_ctx, CheckerContext *ctx, Entity *tm name = asm_ctx->pseudo_mnemonic_strings[pseudo_mnemonic]; } - AsmInstructionFacts facts = {}; - facts.node = instr; - facts.name = name; - facts.gen_params.allocator = heap_allocator(); - facts.read_params.allocator = heap_allocator(); + AsmInstructionFacts *facts = gb_alloc_item(permanent_allocator(), AsmInstructionFacts); + facts->node = instr; + facts->name = name; + facts->gen_params.allocator = heap_allocator(); + facts->read_params.allocator = heap_allocator(); defer ({ for (auto const &op : operands) { @@ -1136,11 +1136,11 @@ gb_internal void check_mnemonic(AsmCtx *asm_ctx, CheckerContext *ctx, Entity *tm if (e == nullptr || e->kind != Entity_Label) { continue; } - facts.branch_target = e; + facts->branch_target = e; break; } - map_set(&cfg->instruction_facts, instr, facts); + instr->facts = facts; }); @@ -1541,7 +1541,7 @@ gb_internal void check_mnemonic(AsmCtx *asm_ctx, CheckerContext *ctx, Entity *tm // Handle clobbering from mnemonic auto clobber = clobber_forms[valid_form_index]; - facts.read_regs = cast(u16)clobber.implicit_rd & asm_ctx->CLOBBER_REGS_NAMED; + facts->read_regs = cast(u16)clobber.implicit_rd & asm_ctx->CLOBBER_REGS_NAMED; // NOTE(bill): reads_mem/writes_mem are per-FORM capability bits. // A form with an r/m slot (e.g. add r/m32, imm32) carries them even @@ -1630,7 +1630,7 @@ gb_internal void check_mnemonic(AsmCtx *asm_ctx, CheckerContext *ctx, Entity *tm explicit_writes |= synth; } - facts.gen_regs = produced | pinned_param_writes; + facts->gen_regs = produced | pinned_param_writes; // NOTE(bill): mnemonics such as `xor r, r` / `sub r, r` act as zeroing the destination // independent of its prior value: the read is architecturally dead, so it must not count as a use. @@ -1665,10 +1665,10 @@ gb_internal void check_mnemonic(AsmCtx *asm_ctx, CheckerContext *ctx, Entity *tm } if (!self_zeroing && (cast(u16)clobber.read & (1u << slot))) { - array_add(&facts.read_params, pe); + array_add(&facts->read_params, pe); } if (cast(u16)clobber.written & (1u << slot)) { - array_add(&facts.gen_params, pe); + array_add(&facts->gen_params, pe); } } @@ -1700,9 +1700,9 @@ gb_internal void check_mnemonic(AsmCtx *asm_ctx, CheckerContext *ctx, Entity *tm // A conditional branch reads a flag and can fall through -> not terminal. bool conditional = clobber.is_conditional(); - facts.is_control = control; - facts.is_conditional = conditional; - facts.is_terminal = halt || (control && !conditional); + facts->is_control = control; + facts->is_conditional = conditional; + facts->is_terminal = halt || (control && !conditional); } asm_ctx->clobber_implicit_regs(&tmpl_entity->AsmTemplate.clobber_registers_set, produced); diff --git a/src/check_asm_cfg.cpp b/src/check_asm_cfg.cpp index 8d4e4eb39..1f37ccb3d 100644 --- a/src/check_asm_cfg.cpp +++ b/src/check_asm_cfg.cpp @@ -44,11 +44,8 @@ struct AsmCfg { char const *impure_reason; Ast * impure_reason_node; - PtrMap instruction_facts; - Array insts; // program-order (only for fact-carrying instrs) Array blocks; - PtrMap label_block; // key: Entity_Label* PtrMap entity_to_index; Array decl_pin_bit; @@ -56,7 +53,6 @@ struct AsmCfg { }; gb_internal void asm_cfg_init(AsmCfg *cfg) { - map_init(&cfg->instruction_facts); map_init(&cfg->entity_to_index); cfg->decl_pin_bit.allocator = heap_allocator(); cfg->can_be_pure = true; @@ -71,12 +67,24 @@ gb_internal void asm_cfg_destroy(AsmCfg *cfg) { } array_free(&cfg->blocks); array_free(&cfg->insts); - map_destroy(&cfg->label_block); - map_destroy(&cfg->instruction_facts); map_destroy(&cfg->entity_to_index); array_free(&cfg->decl_pin_bit); } +gb_internal i32 asm_cfg_label_block_index(Entity *entity) { + if (entity != nullptr && entity->kind == Entity_Label) { + return entity->Label.asm_block_index; + } + return -1; +} +gb_internal bool asm_cfg_label_block_index_set(Entity *entity, i32 index) { + if (entity != nullptr && entity->kind == Entity_Label) { + GB_ASSERT(entity->Label.asm_block_index < 0); + entity->Label.asm_block_index = index; + } + return false; +} + // The physical-register bit a decl is pinned to. A width-view carries no pin of // its own; it inherits its source decl's pin. Returns 0 for unpinned decls. template @@ -124,7 +132,6 @@ gb_internal void check_asm_cfg_build(AsmCtx *asm_ctx, AsmCfg *cfg, Ast *at_node, cfg->insts.allocator = heap_allocator(); cfg->blocks.allocator = heap_allocator(); - map_init(&cfg->label_block); bool need_leader = true; @@ -136,7 +143,7 @@ gb_internal void check_asm_cfg_build(AsmCtx *asm_ctx, AsmCfg *cfg, Ast *at_node, // opens; consecutive labels share it. A trailing label maps to blocks.count. Entity *le = node->AsmLabelDecl.name->Ident.entity; if (le != nullptr) { - map_set(&cfg->label_block, le, cast(i32)cfg->blocks.count); + asm_cfg_label_block_index_set(le, cast(i32)cfg->blocks.count); } need_leader = true; continue; @@ -146,7 +153,7 @@ gb_internal void check_asm_cfg_build(AsmCtx *asm_ctx, AsmCfg *cfg, Ast *at_node, } AstAsmInstruction *instr = &node->AsmInstruction; - AsmInstructionFacts *facts = map_get(&cfg->instruction_facts, instr); + AsmInstructionFacts *facts = instr->facts; // Prefixes and pseudo-macro ops (li/la) carry no facts and never branch. if (need_leader || cfg->blocks.count == 0) { @@ -175,16 +182,16 @@ gb_internal void check_asm_cfg_build(AsmCtx *asm_ctx, AsmCfg *cfg, Ast *at_node, AsmBlock *b = &cfg->blocks[bi]; AstAsmInstruction *last = cfg->insts[b->last]; - AsmInstructionFacts *lf = map_get(&cfg->instruction_facts, last); + AsmInstructionFacts *lf = last->facts; i32 branch_succ = -1; bool fallthrough = true; if (lf != nullptr && lf->is_control) { if (lf->branch_target != nullptr) { - i32 *t = map_get(&cfg->label_block, lf->branch_target); - if (t != nullptr && *t < cast(i32)cfg->blocks.count) { - branch_succ = *t; // in-range internal target ('jmp .l' / 'jz .l') + i32 t = asm_cfg_label_block_index(lf->branch_target); + if (0 <= t && t < cast(i32)cfg->blocks.count) { + branch_succ = t; // in-range internal target ('jmp .l' / 'jz .l') } // For `t == blocks.count`, this implies a jump to the implicit end, and is handled as "leaves" below } @@ -228,11 +235,11 @@ gb_internal void check_asm_cfg_build(AsmCtx *asm_ctx, AsmCfg *cfg, Ast *at_node, gb_internal bool check_asm_cfg_block_leaves(AsmCfg *cfg, i32 bi) { AsmBlock const *b = &cfg->blocks[bi]; AstAsmInstruction *last = cfg->insts[b->last]; - AsmInstructionFacts *lf = map_get(&cfg->instruction_facts, last); + AsmInstructionFacts *lf = last->facts; if (lf != nullptr && lf->branch_target != nullptr) { - i32 *t = map_get(&cfg->label_block, lf->branch_target); - if (t != nullptr && *t >= cast(i32)cfg->blocks.count) { + i32 t = asm_cfg_label_block_index(lf->branch_target); + if (t >= 0 && t >= cast(i32)cfg->blocks.count) { return true; // 'jmp .end' — falls into the implicit return } } @@ -367,7 +374,7 @@ gb_internal void check_asm_cfg_analyse(AsmCtx *asm_ctx, AsmCfg *cfg, CheckerCont u64 gp = 0; AsmBlock const &b = cfg->blocks[bi]; for (i32 ii = b.first; ii <= b.last; ii++) { - AsmInstructionFacts *f = map_get(&cfg->instruction_facts, cfg->insts[ii]); + AsmInstructionFacts *f = cfg->insts[ii]->facts; if (f == nullptr) { continue; } @@ -484,7 +491,7 @@ gb_internal void check_asm_cfg_analyse(AsmCtx *asm_ctx, AsmCfg *cfg, CheckerCont for (i32 ii = b.first; ii <= b.last; ii++) { AstAsmInstruction *instr = cfg->insts[ii]; - AsmInstructionFacts *f = map_get(&cfg->instruction_facts, instr); + AsmInstructionFacts *f = instr->facts; if (f == nullptr) { continue; } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 091ebdac2..937a0eb07 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -13894,6 +13894,10 @@ gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthan str = gb_string_appendc(str, " = "); str = write_expr_to_string(str, spec->value, shorthand); } + for (Ast *dir : spec->directives) { + str = gb_string_appendc(str, " "); + str = write_expr_to_string(str, dir, shorthand); + } case_end; case_ast_node(clobber, AsmClobber, node); diff --git a/src/parser.cpp b/src/parser.cpp index f8ccb79a6..43fbea7c9 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -548,6 +548,7 @@ gb_internal Ast *clone_ast(Ast *node, AstFile *f) { case Ast_AsmInstruction: n->AsmInstruction.name = clone_ast(n->AsmInstruction.name, f); n->AsmInstruction.operands = clone_ast_array(n->AsmInstruction.operands, f); + n->AsmInstruction.facts = nullptr; break; case Ast_AsmMemoryOperand: n->AsmMemoryOperand.segment_override = clone_ast(n->AsmMemoryOperand.segment_override, f); diff --git a/src/parser.hpp b/src/parser.hpp index ad82834ed..260cb8a85 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -510,6 +510,7 @@ struct AstSplitArgs { u16 mnemonic; \ u8 suffix_flags; \ i32 valid_form_index; \ + struct AsmInstructionFacts *facts; \ }) \ AST_KIND(AsmMemoryOperand, "asm memory operand", struct { \ Token open; \