Handle status snapshot style instructions

This commit is contained in:
gingerBill
2026-08-30 18:03:26 +01:00
parent 80e9ec0e53
commit 7773596beb
7 changed files with 66 additions and 0 deletions

View File

@@ -354,6 +354,11 @@ main :: proc() {
bool is_atomic = (cast(u16)side_effects & SideEffectFlag_ATOMIC) != 0;
return (implicit & ClobberReg_SP) != 0 || is_atomic;
}
bool is_status_snapshot() const {
u16 flags = cast(u16)this->flags_rd_call();
return gb_count_set_bits(flags & CLOBBER_FLAGS_COND) >= 4;
}
};
void clobber_implicit_regs(StringSet *clobber_registers_set, u16 implicit_regs) {

View File

@@ -289,6 +289,19 @@ main :: proc() {
bool is_atomic = false; // TODO(bill): Add ATOMIC flag to SideEffectFlags in the original INSTRUCTION_TABLE
return (implicit & (ClobberReg_SP)) != 0 || is_atomic;
}
bool is_status_snapshot() const {
// RiscV64 has no architectural condition flags: branches compare two GPRs
// directly (beq/bltu/…) and slt/sltu materialise a 0/1 into a GPR, so nothing
// ever consumes status as an implicit condition — flags_rd_call() is always
// empty and the flag read-before-write check is already vacuous here.
//
// The only status that exists — the accrued fcsr exception flags (fflags[4:0])
// and the frm rounding field — is read solely through an explicit CSR access
// (csrr* / frflags / frrm), which pulls the register out as an opaque value
// into a GPR. That is a snapshot by construction and must never require a
// producer, so every status read that RV64 can express qualifies.
return true;
}
};
void clobber_implicit_regs(StringSet *clobber_registers_set, u16 implicit_regs) {

View File

@@ -153,6 +153,7 @@ main :: proc() {
case ClobberFlag_CF: return \"c\"; case ClobberFlag_PF: return \"p\";
case ClobberFlag_AF: return \"a\"; case ClobberFlag_ZF: return \"z\";
case ClobberFlag_SF: return \"s\"; case ClobberFlag_OF: return \"o\";
case ClobberFlag_DF: return \"d\"; case ClobberFlag_IF: return \"i\";
}
return \"?\";
}
@@ -363,6 +364,14 @@ main :: proc() {
u16 implicit = cast(u16)implicit_rd | cast(u16)implicit_wr;
return (implicit & (ClobberReg_RSP|ClobberReg_RSI|ClobberReg_RDI|ClobberReg_RBX)) != 0;
}
bool is_status_snapshot() const {
u16 flags = cast(u16)this->flags_rd_call();
u16 const STATUS_FLAGS = ClobberFlag_CF | ClobberFlag_PF | ClobberFlag_AF |
ClobberFlag_ZF | ClobberFlag_SF | ClobberFlag_OF;
return ((flags & ClobberFlag_IF) != 0) ||
(gb_count_set_bits(flags & STATUS_FLAGS) >= 4);
}
};
void clobber_implicit_regs(StringSet *clobber_registers_set, u16 implicit_regs) {

View File

@@ -156,6 +156,7 @@ struct Asm_amd64 {
case ClobberFlag_CF: return "c"; case ClobberFlag_PF: return "p";
case ClobberFlag_AF: return "a"; case ClobberFlag_ZF: return "z";
case ClobberFlag_SF: return "s"; case ClobberFlag_OF: return "o";
case ClobberFlag_DF: return "d"; case ClobberFlag_IF: return "i";
}
return "?";
}
@@ -366,6 +367,14 @@ struct Asm_amd64 {
u16 implicit = cast(u16)implicit_rd | cast(u16)implicit_wr;
return (implicit & (ClobberReg_RSP|ClobberReg_RSI|ClobberReg_RDI|ClobberReg_RBX)) != 0;
}
bool is_status_snapshot() const {
u16 flags = cast(u16)this->flags_rd_call();
u16 const STATUS_FLAGS = ClobberFlag_CF | ClobberFlag_PF | ClobberFlag_AF |
ClobberFlag_ZF | ClobberFlag_SF | ClobberFlag_OF;
return ((flags & ClobberFlag_IF) != 0) ||
(gb_count_set_bits(flags & STATUS_FLAGS) >= 4);
}
};
void clobber_implicit_regs(StringSet *clobber_registers_set, u16 implicit_regs) {

View File

@@ -341,6 +341,11 @@ struct Asm_arm64 {
bool is_atomic = (cast(u16)side_effects & SideEffectFlag_ATOMIC) != 0;
return (implicit & ClobberReg_SP) != 0 || is_atomic;
}
bool is_status_snapshot() const {
u16 flags = cast(u16)this->flags_rd_call();
return gb_count_set_bits(flags & CLOBBER_FLAGS_COND) >= 4;
}
};
void clobber_implicit_regs(StringSet *clobber_registers_set, u16 implicit_regs) {

View File

@@ -237,6 +237,19 @@ struct Asm_riscv {
bool is_atomic = false; // TODO(bill): Add ATOMIC flag to SideEffectFlags in the original INSTRUCTION_TABLE
return (implicit & (ClobberReg_SP)) != 0 || is_atomic;
}
bool is_status_snapshot() const {
// RiscV64 has no architectural condition flags: branches compare two GPRs
// directly (beq/bltu/…) and slt/sltu materialise a 0/1 into a GPR, so nothing
// ever consumes status as an implicit condition — flags_rd_call() is always
// empty and the flag read-before-write check is already vacuous here.
//
// The only status that exists — the accrued fcsr exception flags (fflags[4:0])
// and the frm rounding field — is read solely through an explicit CSR access
// (csrr* / frflags / frrm), which pulls the register out as an opaque value
// into a GPR. That is a snapshot by construction and must never require a
// producer, so every status read that RV64 can express qualifies.
return true;
}
};
void clobber_implicit_regs(StringSet *clobber_registers_set, u16 implicit_regs) {

View File

@@ -692,7 +692,19 @@ gb_internal void check_asm_cfg_analyse(AsmCtx *asm_ctx, AsmCfg *cfg, CheckerCont
check_asm_cfg_report_undef_reg(asm_ctx, cfg, entity, instr, f->name, bit);
}
bool is_status_snapshot = false;
if (instr->mnemonic && instr->valid_form_index >= 0) {
auto clobber_forms = asm_ctx->clobber_forms(instr->mnemonic);
if (clobber_forms.count > 0 && instr->valid_form_index < clobber_forms.count) {
auto &clobber = clobber_forms[instr->valid_form_index];
is_status_snapshot = clobber.is_status_snapshot();
}
}
u16 undef_flags = f->read_flags & ~run_flags & ~reported_flags;
if (is_status_snapshot) {
undef_flags = 0;
}
if (undef_flags != 0) {
reported_flags |= undef_flags;