Add read-before-write for flags, and fix bugs caused by typos

This commit is contained in:
gingerBill
2026-08-25 10:26:27 +01:00
parent c200bfc189
commit 173443cdd1
2 changed files with 21 additions and 7 deletions

View File

@@ -1630,8 +1630,9 @@ gb_internal bool check_mnemonic(AsmCtx *asm_ctx, CheckerContext *ctx, Entity *tm
explicit_writes |= synth;
}
facts->gen_regs = produced | pinned_param_writes;
facts->gen_flags = cast(u16)clobber.flags_wr;
facts->gen_regs = produced | pinned_param_writes;
facts->gen_flags = cast(u16)clobber.flags_wr;
facts->read_flags = cast(u16)clobber.flags_rd;
// 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.

View File

@@ -24,6 +24,7 @@ struct AsmInstructionFacts {
String name;
u16 gen_flags; // flag bits this instruction defines
u16 read_flags;
u16 gen_regs;
u16 read_regs;
@@ -463,7 +464,7 @@ gb_internal void check_asm_cfg_analyse(AsmCtx *asm_ctx, AsmCfg *cfg, CheckerCont
nin_p = universe_pm;
for (i32 p : preds[bi]) {
nin_r &= out_regs[p];
nin_r &= out_flags[p];
nin_f &= out_flags[p];
nin_p &= out_pm[p];
}
}
@@ -546,7 +547,8 @@ gb_internal void check_asm_cfg_analyse(AsmCtx *asm_ctx, AsmCfg *cfg, CheckerCont
PtrSet<Entity *> reported_params = {};
defer (ptr_set_destroy(&reported_params));
u16 reported_regs = 0;
u16 reported_regs = 0;
u16 reported_flags = 0;
for_array(bi, cfg->blocks) {
AsmBlock const &b = cfg->blocks[bi];
@@ -554,8 +556,9 @@ gb_internal void check_asm_cfg_analyse(AsmCtx *asm_ctx, AsmCfg *cfg, CheckerCont
continue;
}
u16 run_regs = in_regs[bi];
u64 run_pm = in_pm[bi];
u16 run_regs = in_regs[bi];
u16 run_flags = in_flags[bi];
u64 run_pm = in_pm[bi];
for (i32 ii = b.first; ii <= b.last; ii++) {
AstAsmInstruction *instr = cfg->insts[ii];
@@ -573,6 +576,15 @@ gb_internal void check_asm_cfg_analyse(AsmCtx *asm_ctx, AsmCfg *cfg, CheckerCont
reported_regs |= bit;
}
u16 undef_flags = f->read_flags & ~run_flags & ~reported_flags;
if (undef_flags != 0) {
error(instr->name,
"'%.*s' reads a status flag that is not set on all paths reaching here; "
"a flag-setting instruction (e.g. 'cmp', 'test') must precede it on every path",
LIT(f->name));
reported_flags |= undef_flags;
}
for (Entity *pe : f->read_params) {
i32 *ix = map_get(&cfg->entity_to_index, pe);
if (ix == nullptr) {
@@ -591,7 +603,8 @@ gb_internal void check_asm_cfg_analyse(AsmCtx *asm_ctx, AsmCfg *cfg, CheckerCont
}
}
run_regs |= f->gen_regs;
run_regs |= f->gen_regs;
run_flags |= f->gen_flags;
for (Entity *pe : f->gen_params) {
run_pm |= bit_of(pe);
}