mirror of
https://github.com/odin-lang/Odin.git
synced 2026-09-02 02:03:35 +00:00
rexcode/arm64: catch the tooling up to per-condition branch mnemonics
Splitting B_COND left three places still describing the old model. The generated builders were already right -- inst_b_le(label), inst_bc_ne(label), one per condition, all 32 verified to encode and round-trip -- but the hand-written scaffolding around them was not. verify_against_llvm normalised our mnemonic by truncating at the first underscore, which turned B_COND into "b". LLVM prints b.eq/b.ne/..., so the tool carried 32 alias rows pairing "b" with each of them to stop the mismatch being reported. That truncation now collapses all sixteen B_* onto "b" and makes every condition compare equal to every other -- the check would pass whatever the table said. Keep the condition instead (B_LE -> "b.le") and the 32 alias rows are unnecessary; they are gone. specgen's canonicalizer kept B_COND and BC_COND off its rename path by name. Those names no longer exist, so replace the entry with a rule that matches the shape (BC?_%u%u), which is what the intent was. And the note in instructions.odin still pointed at inst_b_cond. It now says what is actually true: a conditional branch is one builder per condition because the condition is part of the mnemonic, while the select/compare family -- CSEL, CSINC, CSINV, CSNEG, CCMP, CCMN, FCSEL -- really does take a condition operand and keeps one. specgen still re-derives its 1130 forms from llvm-mc and finds every one already present, all suites pass, and all 13 packages build. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -118,8 +118,10 @@ inst_branch :: #force_inline proc "contextless" (m: Mnemonic, label_id: u32) ->
|
||||
ops = {op_label(label_id, 4), {}, {}, {}}}
|
||||
}
|
||||
|
||||
// NOTE: inst_b_cond / inst_cbz (+cbnz) / inst_tbz (+tbnz) /
|
||||
// inst_csel (+csinc/csinv/csneg) are now generated per-mnemonic in
|
||||
// mnemonic_builders.odin (e.g. inst_cbz(rt, label), inst_cbnz(rt, label),
|
||||
// inst_csinc(rd, rn, rm, cond)). They are no longer hand-written here so the
|
||||
// generator can own those names for full mnemonic coverage.
|
||||
// NOTE: the conditional branches, inst_cbz (+cbnz), inst_tbz (+tbnz) and
|
||||
// inst_csel (+csinc/csinv/csneg) are generated per-mnemonic in
|
||||
// mnemonic_builders.odin, so the generator owns those names. A conditional
|
||||
// branch is one builder per condition -- inst_b_le(label), inst_bc_ne(label)
|
||||
// -- because the condition is part of the mnemonic, not an operand; the
|
||||
// condition-operand builders are the select/compare family, which really do
|
||||
// take one (inst_csinc(rd, rn, rm, cond)).
|
||||
|
||||
@@ -71,10 +71,13 @@ local CANON_SUF = {
|
||||
"_II","_IR","_RI","_RR","_X2","_X4","_SR","_ER","_ZA",
|
||||
"_Z","_P","_V","_H","_S","_D","_B","_3","_4",
|
||||
}
|
||||
local CANON_KEEP = { B_COND=true, BC_COND=true }
|
||||
-- A conditional branch is one mnemonic per condition (B_LE -> `b.le`), so
|
||||
-- nothing about those names should be canonicalized away.
|
||||
local function is_cond_branch(name) return name:match("^BC?_%u%u$") ~= nil end
|
||||
local CANON_KEEP = {}
|
||||
local CANON_RENAME = { LSLV="LSL", LSRV="LSR", ASRV="ASR", RORV="ROR" }
|
||||
local function canon(name)
|
||||
if CANON_KEEP[name] then return name end
|
||||
if CANON_KEEP[name] or is_cond_branch(name) then return name end
|
||||
for _, k in ipairs({"DC_","IC_","AT_","TLBI_","BTI_","PSB_","TSB_"}) do
|
||||
if name:sub(1, #k) == k then return name end
|
||||
end
|
||||
|
||||
@@ -37,6 +37,13 @@ normalize_our :: proc(name: string) -> string {
|
||||
break
|
||||
}
|
||||
}
|
||||
// A conditional branch keeps its condition: B_LE is `b.le`, not `b`.
|
||||
// Truncating at the underscore would collapse all sixteen onto one name
|
||||
// and make every one of them compare equal to every other.
|
||||
if strings.has_prefix(s, "b_") || strings.has_prefix(s, "bc_") {
|
||||
return strings.concatenate({s[:strings.index_byte(s, '_')], ".",
|
||||
s[strings.index_byte(s, '_')+1:]}, context.temp_allocator)
|
||||
}
|
||||
if i := strings.index_byte(s, '_'); i >= 0 {
|
||||
s = s[:i]
|
||||
}
|
||||
@@ -146,15 +153,6 @@ is_known_alias :: proc(ours, llvm: string) -> bool {
|
||||
{"and", "mov"},
|
||||
{"facge", "facge"},
|
||||
{"facgt", "facgt"},
|
||||
// Conditional branches: our table has B_COND but LLVM prints b.eq/b.ne/...
|
||||
{"b", "b.eq"}, {"b", "b.ne"}, {"b", "b.cs"}, {"b", "b.cc"},
|
||||
{"b", "b.mi"}, {"b", "b.pl"}, {"b", "b.vs"}, {"b", "b.vc"},
|
||||
{"b", "b.hi"}, {"b", "b.ls"}, {"b", "b.ge"}, {"b", "b.lt"},
|
||||
{"b", "b.gt"}, {"b", "b.le"}, {"b", "b.al"}, {"b", "b.nv"},
|
||||
{"bc", "bc.eq"}, {"bc", "bc.ne"}, {"bc", "bc.cs"}, {"bc", "bc.cc"},
|
||||
{"bc", "bc.mi"}, {"bc", "bc.pl"}, {"bc", "bc.vs"}, {"bc", "bc.vc"},
|
||||
{"bc", "bc.hi"}, {"bc", "bc.ls"}, {"bc", "bc.ge"}, {"bc", "bc.lt"},
|
||||
{"bc", "bc.gt"}, {"bc", "bc.le"}, {"bc", "bc.al"}, {"bc", "bc.nv"},
|
||||
// FCSEL is canonical for FCSET/FCINC variants if any
|
||||
{"fmov", "fmov"},
|
||||
// Atomics: many of these LLVM might print without the size suffix
|
||||
|
||||
Reference in New Issue
Block a user