From e093329012f0cf39afc16a0f72b5c81f1b126f5f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 24 Aug 2026 23:35:41 +0100 Subject: [PATCH] asm: add a backward liveness check for the CFG --- src/check_asm.cpp | 40 ++++++++---- src/check_asm_cfg.cpp | 140 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 166 insertions(+), 14 deletions(-) diff --git a/src/check_asm.cpp b/src/check_asm.cpp index b81864bc1..c03b081fd 100644 --- a/src/check_asm.cpp +++ b/src/check_asm.cpp @@ -1107,7 +1107,7 @@ gb_internal void check_operand_constraints(AsmCtx *asm_ctx, Slice const } template -gb_internal void check_mnemonic(AsmCtx *asm_ctx, CheckerContext *ctx, Entity *tmpl_entity, AstAsmInstruction *instr, +gb_internal bool check_mnemonic(AsmCtx *asm_ctx, CheckerContext *ctx, Entity *tmpl_entity, AstAsmInstruction *instr, u16 mnemonic, u16 pseudo_mnemonic, Slice const &operands, u8 previous_prefix, Ast *previous_prefix_instr, AsmCfg *cfg) { @@ -1514,7 +1514,7 @@ gb_internal void check_mnemonic(AsmCtx *asm_ctx, CheckerContext *ctx, Entity *tm error(instr->name, "The asm instruction '%.*s' expects %d..=%d operands, got %td", LIT(name), min_count, max_count, operands.count); } print_possible_forms(); - return; + return false; } if (matched) { @@ -1687,8 +1687,20 @@ gb_internal void check_mnemonic(AsmCtx *asm_ctx, CheckerContext *ctx, Entity *tm // A read of the view is a read of the source if (cast(u16)clobber.read & (1u << slot)) { - array_add(&facts->read_params, src_e); + i32 di = -1; + for_array(k, decls) { + if (decls[k].entity == pe) { + di = cast(i32)k; + break; + } + } + if (di >= 0 && decls[di].view_of >= 0 && decls[decls[di].view_of].entity != nullptr) { + array_add(&facts->read_params, decls[decls[di].view_of].entity); + } else { + array_add(&facts->read_params, pe); + } } + // A write of the view defines the source only if it covers the parent if (cast(u16)clobber.written & (1u << slot)) { i32 parent_w = check_asm_operand_bit_width(src_e->type); @@ -1766,8 +1778,10 @@ gb_internal void check_mnemonic(AsmCtx *asm_ctx, CheckerContext *ctx, Entity *tm cfg->impure_reason = why; cfg->impure_reason_node = instr->name; } + + // NOTE(bill): return true even on a purity test because the instruction is still good } - return; + return true; } // NOTE(bill): Failure path @@ -1878,6 +1892,8 @@ gb_internal void check_mnemonic(AsmCtx *asm_ctx, CheckerContext *ctx, Entity *tm print_possible_forms(); } end_error_block(); + + return false; } @@ -2461,6 +2477,7 @@ gb_internal void check_asm_template(AsmCtx *asm_ctx, CheckerContext *ctx, Entity u8 previous_prefix = 0; Ast *previous_prefix_instr = nullptr; // for a good error location + bool all_instructions_good = true; for (Ast *instruction_ : at->instructions) { switch (instruction_->kind) { @@ -2489,9 +2506,9 @@ gb_internal void check_asm_template(AsmCtx *asm_ctx, CheckerContext *ctx, Entity previous_prefix_instr = instruction_; } else if (res == CheckMnemomic_Mnemonic) { instr->suffix_flags = suffix_flags; - check_mnemonic(asm_ctx, ctx, entity, instr, mnemonic, 0, slice_from_array(operands), - previous_prefix, previous_prefix_instr, - &cfg); + all_instructions_good &= check_mnemonic(asm_ctx, ctx, entity, instr, mnemonic, 0, slice_from_array(operands), + previous_prefix, previous_prefix_instr, + &cfg); cfg.saw_any_instructions = true; @@ -2503,9 +2520,9 @@ gb_internal void check_asm_template(AsmCtx *asm_ctx, CheckerContext *ctx, Entity u16 pseudo_mnemonic = cast(u16)mnemonic; auto alias = asm_ctx->pseudo_alias(cast(u16)pseudo_mnemonic); u16 target_mnemonic = cast(u16)alias.target; - check_mnemonic(asm_ctx, ctx, entity, instr, target_mnemonic, pseudo_mnemonic, slice_from_array(operands), - previous_prefix, previous_prefix_instr, - &cfg); + all_instructions_good &=check_mnemonic(asm_ctx, ctx, entity, instr, target_mnemonic, pseudo_mnemonic, slice_from_array(operands), + previous_prefix, previous_prefix_instr, + &cfg); cfg.saw_any_instructions = true; @@ -2513,7 +2530,7 @@ gb_internal void check_asm_template(AsmCtx *asm_ctx, CheckerContext *ctx, Entity previous_prefix_instr = nullptr; } else if (res == CheckMnemomic_PseudoMacroMnemonic) { instr->suffix_flags = suffix_flags; - check_pseudo_macro_mnemonic(asm_ctx, entity, instr, slice_from_array(operands)); + all_instructions_good &= check_pseudo_macro_mnemonic(asm_ctx, entity, instr, slice_from_array(operands)); cfg.saw_any_instructions = true; @@ -2627,6 +2644,7 @@ gb_internal void check_asm_template(AsmCtx *asm_ctx, CheckerContext *ctx, Entity check_asm_cfg_build(asm_ctx, &cfg, d->init_expr, entity); check_asm_cfg_analyse(asm_ctx, &cfg, ctx, entity); + check_asm_cfg_liveness(asm_ctx, &cfg, entity, /*emit_dead_writes*/all_instructions_good); bool vet_unused = false; { diff --git a/src/check_asm_cfg.cpp b/src/check_asm_cfg.cpp index 3b55de62a..3cee17560 100644 --- a/src/check_asm_cfg.cpp +++ b/src/check_asm_cfg.cpp @@ -8,6 +8,9 @@ struct AsmBlock { u16 in_flags; u16 out_flags; + u16 live_in_regs; + u16 live_out_regs; + PtrSet in_params; PtrSet out_params; @@ -134,8 +137,18 @@ gb_internal void asm_cfg_populate_decls(AsmCtx *asm_ctx, AsmCfg *cfg, Entity *en Entity *e = decls[i].entity; cfg->decl_pin_bit[i] = asm_decl_resolve_pin_bit(asm_ctx, decls, cast(i32)i); if (e != nullptr) { - map_set(&cfg->entity_to_index, e, cast(i32)i); - cfg->universe_pm |= (cast(u64)1 << i); + if (decls[i].view_of >= 0) { + // NOTE(bill): A view shares its source's lattice bit, as it is not an independent value. + i32 src_i = decls[i].view_of; + Entity *src_e = decls[src_i].entity; + if (src_e != nullptr) { + map_set(&cfg->entity_to_index, e, src_i); // view entity -> source index + } + // NOTE(bill): No need to set a universe bit for the view as the source already has one + } else { + map_set(&cfg->entity_to_index, e, cast(i32)i); + cfg->universe_pm |= (cast(u64)1 << i); + } } } } @@ -549,8 +562,14 @@ gb_internal void check_asm_cfg_analyse(AsmCtx *asm_ctx, AsmCfg *cfg, CheckerCont if (((run_pm >> *ix) & 1) == 0 && !ptr_set_exists(&reported_params, pe)) { Ast *loc = instr->name; for (Ast *op : instr->operands) { - if (entity_of_node(op) == pe) { loc = op; break; } + if (entity_of_node(op) == pe) { + loc = op; + break; + } } + gb_printf_err("RBW-ERROR site=