#!/usr/bin/env python3 """ Update QEMU_SUPPORTED_EXTS in scripts/march-to-cpu-opt from QEMU source. This script parses ISA_EXT_DATA_ENTRY macros from QEMU's target/riscv/cpu.c and generates the list of supported extensions for march-to-cpu-opt. Filtering rules: - Excludes extensions with has_priv_* (no explicit on/off switch) - Excludes s* extensions (supervisor extensions) """ import argparse import os import re import sys def parse_qemu_cpu_c(qemu_cpu_c_path): """Parse ISA_EXT_DATA_ENTRY and MISA_EXT_INFO from QEMU cpu.c file.""" with open(qemu_cpu_c_path, "r") as f: content = f.read() extensions = [] # Match ISA_EXT_DATA_ENTRY(name, version, property) isa_ext_pattern = r"ISA_EXT_DATA_ENTRY\((\w+),\s*\w+,\s*(\w+)\)" isa_ext_matches = re.findall(isa_ext_pattern, content) for ext_name, prop in isa_ext_matches: # Skip the macro definition itself (starts with _) if ext_name.startswith("_"): continue # Skip extensions without explicit on/off switch (has_priv_*) if prop.startswith("has_priv_"): continue # Skip supervisor extensions (s*) if ext_name.startswith("s"): continue extensions.append(ext_name) # Match MISA_EXT_INFO(_bit, "propname", description) # e.g., MISA_EXT_INFO(RVA, "a", "Atomic instructions") misa_ext_pattern = r'MISA_EXT_INFO\(\w+,\s*"(\w+)"' misa_ext_matches = re.findall(misa_ext_pattern, content) # Extensions to skip from MISA_EXT_INFO misa_skip = {"i", "e", "g", "s", "u", "h"} for ext_name in misa_ext_matches: if ext_name not in misa_skip: extensions.append(ext_name) return sorted(set(extensions)) def generate_ext_set(extensions): """Generate the QEMU_SUPPORTED_EXTS set string.""" lines = [] lines.append("# QEMU supported extensions list") lines.append("# This list is auto-generated by scripts/update-ext-cpu-opt from QEMU source") lines.append("# Only includes extensions with explicit on/off switches") lines.append("# Excludes: has_priv_* (no switch), s* (supervisor extensions)") lines.append("# fmt: off") lines.append("QEMU_SUPPORTED_EXTS = {") for ext in extensions: lines.append(' "{}",'.format(ext)) lines.append("}") lines.append("# fmt: on") return "\n".join(lines) def update_march_to_cpu_opt(march_script_path, new_ext_set): """Update the QEMU_SUPPORTED_EXTS in march-to-cpu-opt script.""" with open(march_script_path, "r") as f: content = f.read() # Pattern to match the entire QEMU_SUPPORTED_EXTS block pattern = ( r"# QEMU supported extensions list\n" r"# This list is auto-generated by scripts/update-ext-cpu-opt from QEMU source\n" r"# Only includes extensions with explicit on/off switches\n" r"# Excludes: has_priv_\* \(no switch\), s\* \(supervisor extensions\)\n" r"# fmt: off\n" r"QEMU_SUPPORTED_EXTS = \{[^}]*\}\n" r"# fmt: on" ) if not re.search(pattern, content): print("Error: Could not find QEMU_SUPPORTED_EXTS block in {}".format(march_script_path)) print("Make sure the file contains the expected format.") return False new_content = re.sub(pattern, new_ext_set, content) with open(march_script_path, "w") as f: f.write(new_content) return True def main(): parser = argparse.ArgumentParser( description="Update QEMU_SUPPORTED_EXTS from QEMU source" ) parser.add_argument( "--qemu-src", type=str, default="qemu", help="Path to QEMU source directory (default: qemu)", ) parser.add_argument( "--march-script", type=str, default="scripts/march-to-cpu-opt", help="Path to march-to-cpu-opt script (default: scripts/march-to-cpu-opt)", ) parser.add_argument( "--dry-run", action="store_true", help="Print the generated extension list without updating the file", ) args = parser.parse_args() qemu_cpu_c = os.path.join(args.qemu_src, "target", "riscv", "cpu.c") if not os.path.exists(qemu_cpu_c): print("Error: QEMU cpu.c not found at {}".format(qemu_cpu_c)) return 1 if not os.path.exists(args.march_script): print("Error: march-to-cpu-opt not found at {}".format(args.march_script)) return 1 print("Parsing QEMU cpu.c from: {}".format(qemu_cpu_c)) extensions = parse_qemu_cpu_c(qemu_cpu_c) print("Found {} supported extensions".format(len(extensions))) ext_set = generate_ext_set(extensions) if args.dry_run: print("\nGenerated QEMU_SUPPORTED_EXTS:") print(ext_set) return 0 print("Updating: {}".format(args.march_script)) if update_march_to_cpu_opt(args.march_script, ext_set): print("Successfully updated QEMU_SUPPORTED_EXTS") return 0 else: return 1 if __name__ == "__main__": sys.exit(main())