mirror of
https://github.com/odin-lang/Odin.git
synced 2026-06-19 16:42:33 +00:00
Adds 9 register-only three-same mnemonics (59 forms) via specgen: ADDP/SMAXP/SMINP/UMAXP/UMINP (pairwise) and SSHL/USHL/SRSHL/URSHL (per-lane variable shift). Verified: decode round-trips (ADDP/SSHL/SMAXP/URSHL), arm64 check + 461 tests pass. Skipped the already-implemented logical/compare/mul forms (AND_V/ORR_V/EOR_V/BIC_V/ORN_V/BSL/BIT/BIF/CMEQ/CMGT/CMHI/MUL_V) to avoid duplicate keys.
126 lines
5.3 KiB
Lua
126 lines
5.3 KiB
Lua
#!/usr/bin/env luajit
|
|
-- rexcode · Brendan Punsky (dotbmp@github), original author
|
|
--
|
|
-- Encode-form spec generator (arm64). Expands compact per-instruction specs
|
|
-- into ENCODING_TABLE entries, deriving `bits` from llvm-mc (the oracle) and
|
|
-- `mask` empirically: assemble each form with operand registers at 0 and at 31;
|
|
-- the differing bits are operand-driven, so mask = ~(bits0 ^ bits31). Per-form
|
|
-- assembly makes it robust: an arrangement llvm-mc rejects (e.g. SQADD .1D) is
|
|
-- reported and skipped, never misaligned.
|
|
--
|
|
-- The generated entries replace the SPECGEN:BEGIN..SPECGEN:END region of
|
|
-- encoding_table.odin in place (the hand-written core is left untouched). Every
|
|
-- bit pattern is therefore reproducible and llvm-mc-backed.
|
|
--
|
|
-- Adding a family: pick a SHAPE (operand layout) and list {enum, llvm} pairs.
|
|
--
|
|
-- Run: luajit tablegen/specgen.lua (from arm64/, or with a full path)
|
|
|
|
local bit = require("bit")
|
|
local LLVM = "llvm-mc --assemble --arch=aarch64 --show-encoding"
|
|
local DIR = (arg[0]:match("^(.*)/[^/]*$")) or "."
|
|
local TABLE = DIR .. "/encoding_table.odin"
|
|
|
|
-- arrangement token -> { vt = Operand_Type, asm = llvm suffix }
|
|
local ARR = {
|
|
["8B"]={vt="V_8B",asm="8b"}, ["16B"]={vt="V_16B",asm="16b"},
|
|
["4H"]={vt="V_4H",asm="4h"}, ["8H"] ={vt="V_8H", asm="8h"},
|
|
["2S"]={vt="V_2S",asm="2s"}, ["4S"] ={vt="V_4S", asm="4s"},
|
|
["1D"]={vt="V_1D",asm="1d"}, ["2D"] ={vt="V_2D", asm="2d"},
|
|
}
|
|
-- over-specify; llvm-mc decides which arrangements are legal per instruction.
|
|
local ALL_ARR = {"8B","16B","4H","8H","2S","4S","2D"}
|
|
|
|
-- Vector shapes (all operands share one arrangement T). enc lists the register
|
|
-- slots, in operand order. The encoder packs each into its 5-bit field.
|
|
local SHAPE = {
|
|
THREE_SAME = { nreg=3, enc={"VD","VN","VM"} }, -- Vd.T, Vn.T, Vm.T
|
|
TWO_SAME = { nreg=2, enc={"VD","VN"} }, -- Vd.T, Vn.T
|
|
}
|
|
|
|
-- Families: { shape, feature, items = {{enum, llvm}, ...} }
|
|
local FAMILIES = {
|
|
{ shape="THREE_SAME", feature="NEON", title="Advanced SIMD three-same (integer)", items = {
|
|
{"SHADD","shadd"}, {"UHADD","uhadd"}, {"SHSUB","shsub"}, {"UHSUB","uhsub"},
|
|
{"SRHADD","srhadd"},{"URHADD","urhadd"},
|
|
{"SQADD","sqadd"}, {"UQADD","uqadd"}, {"SQSUB","sqsub"}, {"UQSUB","uqsub"},
|
|
{"SMAX","smax"}, {"UMAX","umax"}, {"SMIN","smin"}, {"UMIN","umin"},
|
|
{"SABD","sabd"}, {"UABD","uabd"}, {"SABA","saba"}, {"UABA","uaba"},
|
|
{"MLA_V","mla"}, {"MLS_V","mls"},
|
|
{"CMGE","cmge"}, {"CMHS","cmhs"}, {"CMTST","cmtst"},
|
|
{"SQDMULH","sqdmulh"}, {"SQRDMULH","sqrdmulh"},
|
|
{"ADDP_V","addp"}, {"SMAXP","smaxp"}, {"SMINP","sminp"}, {"UMAXP","umaxp"}, {"UMINP","uminp"},
|
|
{"SSHL","sshl"}, {"USHL","ushl"}, {"SRSHL","srshl"}, {"URSHL","urshl"},
|
|
}},
|
|
{ shape="TWO_SAME", feature="NEON", title="Advanced SIMD two-register misc", items = {
|
|
{"NOT_V","not"}, {"RBIT_V","rbit"},
|
|
{"REV16_V","rev16"},{"REV32_V","rev32"},{"REV64","rev64"},
|
|
{"CLS_V","cls"}, {"CLZ_V","clz"}, {"CNT","cnt"},
|
|
{"URECPE_V","urecpe"}, {"URSQRTE_V","ursqrte"},
|
|
}},
|
|
}
|
|
|
|
local function word(line)
|
|
local p = io.popen(string.format("printf '%%s\\n' '%s' | %s 2>/dev/null", line, LLVM))
|
|
local out = p:read("*a"); p:close()
|
|
local b1,b2,b3,b4 = out:match("0x(%x%x),0x(%x%x),0x(%x%x),0x(%x%x)")
|
|
if not b1 then return nil end
|
|
return tonumber(b4..b3..b2..b1, 16)
|
|
end
|
|
|
|
local function padded(prefix_tokens, n)
|
|
local t = {}
|
|
for i = 1, 4 do t[i] = prefix_tokens[i] or ".NONE" end
|
|
return "{" .. table.concat(t, ", ") .. "}"
|
|
end
|
|
|
|
local sections, skips, n_forms, n_mnem = {}, {}, 0, 0
|
|
for _, fam in ipairs(FAMILIES) do
|
|
local sh = SHAPE[fam.shape]
|
|
local enc_tokens = {}; for i, e in ipairs(sh.enc) do enc_tokens[i] = "."..e end
|
|
local enc_str = padded(enc_tokens, sh.nreg)
|
|
local blocks = {}
|
|
for _, it in ipairs(fam.items) do
|
|
local mnem, llvm = it[1], it[2]
|
|
local rows = {}
|
|
for _, a in ipairs(ALL_ARR) do
|
|
local s = ARR[a].asm
|
|
local function mk(reg)
|
|
local parts = {}; for i = 1, sh.nreg do parts[i] = "v"..reg.."."..s end
|
|
return llvm.." "..table.concat(parts, ", ")
|
|
end
|
|
local w0, w31 = word(mk(0)), word(mk(31))
|
|
if w0 and w31 then
|
|
local mask = bit.band(bit.bnot(bit.bxor(w0, w31)), 0xFFFFFFFF)
|
|
local op_tokens = {}; for i = 1, sh.nreg do op_tokens[i] = "."..ARR[a].vt end
|
|
rows[#rows+1] = string.format("\t\t{.%s, %s, %s, 0x%s, 0x%s, .%s, {}},",
|
|
mnem, padded(op_tokens, sh.nreg), enc_str,
|
|
bit.tohex(w0):upper(), bit.tohex(mask):upper(), fam.feature)
|
|
n_forms = n_forms + 1
|
|
else
|
|
skips[#skips+1] = mnem.." ."..a
|
|
end
|
|
end
|
|
if #rows > 0 then
|
|
blocks[#blocks+1] = string.format("\t.%s = {\n%s\n\t},", mnem, table.concat(rows, "\n"))
|
|
n_mnem = n_mnem + 1
|
|
end
|
|
end
|
|
sections[#sections+1] = string.format("\t// %s.\n%s", fam.title, table.concat(blocks, "\n"))
|
|
end
|
|
|
|
local region = "\t// SPECGEN:BEGIN\n" .. table.concat(sections, "\n\n") .. "\n\t// SPECGEN:END"
|
|
|
|
local fh = assert(io.open(TABLE, "r")); local src = fh:read("*a"); fh:close()
|
|
local new, n = src:gsub("\t// SPECGEN:BEGIN.-\t// SPECGEN:END", (region:gsub("%%", "%%%%")))
|
|
if n ~= 1 then
|
|
io.stderr:write("FATAL: expected exactly one SPECGEN:BEGIN..END region, found "..n.."\n")
|
|
os.exit(1)
|
|
end
|
|
local wh = assert(io.open(TABLE, "w")); wh:write(new); wh:close()
|
|
|
|
io.write(string.format("specgen: wrote %d mnemonics / %d forms into %s\n", n_mnem, n_forms, TABLE))
|
|
if #skips > 0 then
|
|
io.write(" skipped "..#skips.." invalid arrangement(s): "..table.concat(skips, ", ").."\n")
|
|
end
|