diff --git a/src/check_asm.cpp b/src/check_asm.cpp index c03b081fd..38e9604d5 100644 --- a/src/check_asm.cpp +++ b/src/check_asm.cpp @@ -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. diff --git a/src/check_asm_cfg.cpp b/src/check_asm_cfg.cpp index 2f1ce03c7..24a2621c4 100644 --- a/src/check_asm_cfg.cpp +++ b/src/check_asm_cfg.cpp @@ -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 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); }