Remove unneeded hash maps when you can store the data directly on the nodes

This commit is contained in:
gingerBill
2026-08-24 22:25:53 +01:00
parent 7fcf65d2c2
commit a2d9ca8697
5 changed files with 45 additions and 32 deletions

View File

@@ -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);

View File

@@ -44,11 +44,8 @@ struct AsmCfg {
char const *impure_reason;
Ast * impure_reason_node;
PtrMap<AstAsmInstruction *, AsmInstructionFacts> instruction_facts;
Array<AstAsmInstruction *> insts; // program-order (only for fact-carrying instrs)
Array<AsmBlock> blocks;
PtrMap<Entity *, i32> label_block; // key: Entity_Label*
PtrMap<Entity *, i32> entity_to_index;
Array<u16> 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 <typename AsmCtx>
@@ -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;
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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; \