dsp-asm/gen/generate_notes.py
Zack Buhman ddf46ce8fd stmt_ins: refactor
The overall intent is to make writing a decompiler require less code
duplication.

"bits.hpp" and "stmt_enum.hpp" are replaced with "stmt_ins.hpp" which
is generated directly from dsp-notes.csv.
2023-09-03 04:46:34 +00:00

242 lines
7.2 KiB
Python

import sys
from generate import renderer
from parse_notes import parse, read, X, Bits, Enum, Struct, Namespace
def bits_mask_literal(bits, start, end) -> str:
b_str = lambda b: '1' if b is not X else '0'
s = ''.join(map(b_str, reversed(bits[start:end])))
return s
def bits_literal(bits, start, end) -> str:
b_str = lambda b: str(b) if b is not X else '0'
s = ''.join(map(b_str, reversed(bits[start:end])))
return s
def bits_start_end(bits):
start = 0
while bits[start] is X:
start += 1
ix = start + 1
end = ix
while ix < 32:
if bits[ix] is not X:
end = ix + 1
ix += 1
return start, end
def bits_to_shift(bits) -> str:
start, end = bits_start_end(bits)
literal = bits_literal(bits, start, end)
return f"0b{literal} << {start}"
def bits_to_mask(bits) -> str:
start, end = bits_start_end(bits)
mask = bits_mask_literal(bits, start, end)
return f"0b{mask} << {start}"
def enum_consts_to_value(namespace_name, enum_name, consts):
yield "static constexpr value_t to_value(uint32_t code)"
yield "{"
yield "using enum value_t;"
yield ""
yield "switch (code & mask()) {"
for const in consts:
shift = bits_to_shift(const.bits)
yield f"case {shift}: return {const.name};"
yield "default:"
yield f' std::cerr << "invalid {namespace_name} {enum_name} code: " << (code & mask()) << std::endl;'
yield ' throw std::runtime_error("invalid code");'
yield "}"
yield "}"
def enum_consts_bits(consts):
yield "uint32_t bits() const"
yield "{"
yield "using enum value_t;"
yield ""
yield "switch (value) {"
for const in consts:
shift = bits_to_shift(const.bits)
yield f"case {const.name}: return {shift};"
yield "default: __builtin_unreachable();"
yield "}"
yield "}"
def enum_value_t(consts):
yield "enum value_t {"
for const in consts:
yield f"{const.name},"
yield "};"
def enum_struct(namespace, enum):
yield f"struct {enum.name}_t {{"
yield from enum_value_t(enum.consts)
yield ""
yield "const value_t value;"
yield ""
yield f"constexpr {enum.name}_t(value_t value)"
yield " : value(value) {}"
yield ""
yield f"constexpr {enum.name}_t(uint32_t code)"
yield " : value(to_value(code)) {}"
yield ""
mask = bits_to_mask(enum.consts[0].bits)
yield f"static constexpr uint32_t mask() {{ return {mask}; }}"
yield ""
yield from enum_consts_to_value(namespace.name, enum.name, enum.consts)
yield ""
yield from enum_consts_bits(enum.consts)
yield "};"
def has_imm(struct):
return 'imm' in {name for _i, name in struct.fields}
def imm_length(struct):
imm_length, = [i for i, name in struct.fields if name == 'imm']
return imm_length + 1
def f_name(name):
return name.split('_', maxsplit=1)[-1]
def struct_struct(namespace, struct):
if has_imm(struct):
length = imm_length(struct)
yield f"struct {struct.name}_t : stmt_accept_t<{struct.name}_t>, ins_imm_t<{struct.name}_t, {length}>"
yield "{"
yield f"using imm_type = uimm_t<{length}>;"
else:
yield f"struct {struct.name}_t : stmt_accept_t<{struct.name}_t>, ins_t<{struct.name}_t>"
yield "{"
yield ""
field_decls = [f"{name}_t {f_name(name)}" for _, name in struct.fields if name != 'imm']
field_inits = [f"{f_name(name)}({f_name(name)})" for _, name in struct.fields if name != 'imm']
if has_imm(struct):
field_decls.insert(0, "imm_type imm")
field_inits.insert(0, "ins_imm_t(imm)")
field_inits = ", ".join(field_inits)
field_decls = ", ".join(field_decls)
yield f"{struct.name}_t({field_decls})"
if field_inits.strip():
yield f" : {field_inits} {{}}"
else:
yield "{}"
yield ""
yield f"{struct.name}_t(uint32_t code)"
code_inits = [f"{f_name(name)}(code)" for _, name in struct.fields if name != 'imm']
if has_imm(struct):
code_inits.insert(0, "ins_imm_t(code)")
code_inits = ", ".join(code_inits)
if code_inits.strip():
yield f" : {code_inits} {{ (void)code; }}"
else:
yield "{ (void)code; }"
yield ""
for _, name in struct.fields:
if name == 'imm':
pass
else:
yield f"const {name}_t {f_name(name)};"
yield ""
start, end = bits_start_end(struct.bits)
mask_literal = bits_mask_literal(struct.bits, start, end)
literal = bits_literal(struct.bits, start, end)
yield f"static uint32_t mask() {{ return 0b{mask_literal} << {start}; }}"
yield f"static uint32_t code() {{ return 0b{literal} << {start}; }}"
bits_return = " | ".join(f"{f_name(name)}.bits()" for _, name in struct.fields if name != 'imm')
if not bits_return.strip():
bits_return = "0"
yield f"uint32_t bits() const {{ return {bits_return}; }}"
yield "};"
def sort_events(events):
enums = []
structs = []
namespace = None
def do_yield():
nonlocal enums, structs, namespace
yield namespace
#yield from sorted(enums, key=lambda e: e.name)
yield from enums
assert structs, structs
yield from structs
#yield from sorted(structs, key=lambda e: e.name)
for event in events:
if type(event) is Namespace:
if namespace is not None:
yield from do_yield()
else:
assert not enums
assert not structs
namespace = event
enums = []
structs = []
elif type(event) is Enum:
assert namespace is not None
enums.append(event)
elif type(event) is Struct:
assert namespace is not None
structs.append(event)
else:
assert False
assert namespace is not None
yield from do_yield()
def generate_header_namespaces(events):
namespace = None
yield "namespace dsp {"
for event in events:
if type(event) is Namespace:
if namespace is not None:
yield ""
yield f"}} // {namespace.name}"
yield ""
yield f"namespace {event.name} {{"
yield ""
namespace = event
elif type(event) is Enum:
yield from enum_struct(namespace, event)
yield ""
elif type(event) is Struct:
yield from struct_struct(namespace, event)
yield ""
else:
assert False
assert namespace is not None
yield f"}} // namespace {namespace.name}"
yield ""
yield "} // namespace dsp"
def generate_includes():
yield "#pragma once"
yield ""
yield "#include <cstdint>"
yield "#include <iostream>"
yield ""
yield '#include "imm.hpp"'
yield '#include "ins.hpp"'
yield '#include "stmt_base.hpp"'
yield ""
def generate_header():
events = list(sort_events(parse(read(sys.argv[1]))))
render, out = renderer()
render(generate_includes())
render(generate_header_namespaces(events))
return out
print(generate_header().getvalue())
#consts=[Const(name='z', bits=xxxxxxx100001xxxxxxxxxxxxxxxxxxx),
b = Bits(bits=[X,X,1,1,0,0,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X])