pound-emu_pound/scripts/generate_jit_decoder_a32_table.py
Ronald Caesar 2b5131e56c
refactor: Humongous Commit
Major architectural refactorbto focus exclusively on JIT development.

JIT & Decoder Architecture
-    Implemented scripts/generate_jit_decoder_a32_table.py to parse
     instruction definitions at build-time rather than runtime.
-    Moves decoder lookup tables from RAM to ROM.

Scope Reduction:
-    Removed frontend, GUI, and rendering dependencies.
-    delete src/frontend, src/target, and associated design docs.

Most importantly, this commit starts the transition of this codebase
from C++ to C. I cant stand creating C++ code, and since no one else
is contributing to this project this change shouldnt matter.

Signed-off-by: Ronald Caesar <github43132@proton.me>
2025-11-29 14:53:02 -04:00

128 lines
4.5 KiB
Python

#!/usr/bin/env python3
import re
import sys
import datetime
import argparse
# ---------------------------------------------------------
# Configuration & Logic
# ---------------------------------------------------------
MAX_BUCKET_SIZE = 18
TABLE_SIZE = 4096
class Instruction:
def __init__(self, name, mnemonic, bitstring, array_index):
self.name = name
self.mnemonic = mnemonic
self.bitstring = bitstring
self.array_index = array_index
self.mask = 0
self.expected = 0
self.parse_bits()
def parse_bits(self):
if len(self.bitstring) != 32:
print(f"Error: Bitstring length {len(self.bitstring)} invalid for {self.name}")
sys.exit(1)
for i, char in enumerate(self.bitstring):
bit_pos = 31 - i
if char == '0':
self.mask |= (1 << bit_pos)
elif char == '1':
self.mask |= (1 << bit_pos)
self.expected |= (1 << bit_pos)
def get_hash(self):
major = (self.expected >> 20) & 0xFF
minor = (self.expected >> 4) & 0x0F
return (major << 4) | minor
def parse_inc_file(input_path):
instructions = []
regex = re.compile(r'INST\(\s*([A-Za-z0-9_]+),\s*"(.*?)",\s*"(.*?)"\s*\)')
try:
with open(input_path, 'r') as f:
lines = f.readlines()
except FileNotFoundError:
print(f"Error: Could not find input file: {input_path}")
sys.exit(1)
index_counter = 0
for line in lines:
line = line.strip()
if not line or line.startswith("//"):
continue
match = regex.search(line)
if match:
inst = Instruction(match.group(1), match.group(2), match.group(3), index_counter)
instructions.append(inst)
index_counter += 1
return instructions
def generate_lookup_table(instructions):
buckets = {i: [] for i in range(TABLE_SIZE)}
for inst in instructions:
idx = inst.get_hash()
buckets[idx].append(inst)
if len(buckets[idx]) > MAX_BUCKET_SIZE:
print(f"FATAL ERROR: Bucket {idx:#05x} overflowed! Size: {len(buckets[idx])}")
sys.exit(1)
return buckets
def write_c_file(path, instructions, buckets):
with open(path, 'w') as f:
f.write("/* GENERATED FILE - DO NOT EDIT */\n")
f.write('#include "arm32.h"\n')
f.write('#include "arm32_table_generated.h"\n')
f.write(f"static const pvm_jit_decoder_arm32_instruction_info_t g_instructions[{len(instructions)}] = {{\n")
for inst in instructions:
f.write(f' {{ "{inst.mnemonic}", "{inst.bitstring}", {inst.mask:#010x}U, {inst.expected:#010x}U }},\n')
f.write("};\n\n")
f.write(f"const decode_bucket_t g_decoder_lookup_table[{TABLE_SIZE}] = {{\n")
for i in range(TABLE_SIZE):
if len(buckets[i]) > 0:
f.write(f" [{i:#05x}] = {{ .instructions = {{ ")
for inst in buckets[i]:
f.write(f"&g_instructions[{inst.array_index}], ")
f.write(f"}}, .count = {len(buckets[i])}U }},\n")
f.write("};\n")
def write_h_file(path):
with open(path, 'w') as f:
f.write("#ifndef POUND_JIT_DECODER_ARM32_GENERATED_H\n")
f.write("#define POUND_JIT_DECODER_ARM32_GENERATED_H\n\n")
f.write('#include "arm32.h"\n')
f.write('#include <stddef.h>\n\n')
f.write(f"#define LOOKUP_TABLE_MAX_BUCKET_SIZE {MAX_BUCKET_SIZE}U\n\n")
f.write("typedef struct {\n")
f.write(" const pvm_jit_decoder_arm32_instruction_info_t *instructions[LOOKUP_TABLE_MAX_BUCKET_SIZE];\n")
f.write(" size_t count;\n")
f.write("} decode_bucket_t;\n\n")
f.write(f"extern const decode_bucket_t g_decoder_lookup_table[{TABLE_SIZE}];\n\n")
f.write("#endif\n")
# ---------------------------------------------------------
# Main Execution
# ---------------------------------------------------------
def main():
parser = argparse.ArgumentParser(description="Generate ARM32 Decoder Tables")
parser.add_argument("input", help="Path to arm32.inc")
parser.add_argument("out_c", help="Path to output .c file")
parser.add_argument("out_h", help="Path to output .h file")
args = parser.parse_args()
print(f"--- Generating Decoder: {args.input} -> {args.out_c} ---")
instructions = parse_inc_file(args.input)
buckets = generate_lookup_table(instructions)
write_c_file(args.out_c, instructions, buckets)
write_h_file(args.out_h)
if __name__ == "__main__":
main()