Skip to content

Commit c212fc6

Browse files
feat(backends): add QEMU generator for RISC-V instruction set
- Implement a generator that generates insn32.decode for qemu. - Move load_full_instructions to generator.py so that other generators can use it. Resolves issue: #1255
1 parent fd034a0 commit c212fc6

File tree

4 files changed

+635
-40
lines changed

4 files changed

+635
-40
lines changed

backends/generators/binutils/binutils_generator.py

Lines changed: 5 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,14 @@
1010
import sys
1111
import argparse
1212
import logging
13-
import yaml
14-
import glob
1513

1614
# Add parent directory to path to find generator.py
1715
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
18-
from generator import parse_match, parse_extension_requirements
16+
from generator import (
17+
parse_match,
18+
parse_extension_requirements,
19+
load_full_instructions,
20+
)
1921
from naming_config import USER_DEFINED_INSN_NAMES, USER_DEFINED_OPERAND_PREFERENCES, is_user_defined_class
2022
import re
2123

@@ -220,43 +222,6 @@ def _map_single_operand(self, operand, variables, instr_info):
220222
return f"NON_DEFINED_{operand}"
221223

222224

223-
def load_full_instructions(inst_dir, enabled_extensions, include_all, target_arch):
224-
instructions = {}
225-
if enabled_extensions is None:
226-
enabled_extensions = []
227-
yaml_files = glob.glob(os.path.join(inst_dir, "**/*.yaml"), recursive=True)
228-
logging.info(f"Found {len(yaml_files)} instruction files in {inst_dir}")
229-
for yaml_file in yaml_files:
230-
try:
231-
with open(yaml_file, 'r', encoding='utf-8') as f:
232-
data = yaml.safe_load(f)
233-
if not isinstance(data, dict) or data.get('kind') != 'instruction':
234-
continue
235-
name = data.get('name')
236-
if not name:
237-
continue
238-
defined_by = data.get('definedBy')
239-
if not include_all and defined_by:
240-
try:
241-
meets_req = parse_extension_requirements(defined_by)
242-
if not meets_req(enabled_extensions):
243-
logging.debug(f"Skipping {name} - extension requirements not met")
244-
continue
245-
except Exception as e:
246-
logging.debug(f"Error parsing extension requirements for {name}: {e}")
247-
continue
248-
encoding = data.get('encoding', {})
249-
if target_arch in ['RV32', 'RV64'] and target_arch in encoding:
250-
arch_encoding = encoding[target_arch]
251-
data['encoding'] = arch_encoding
252-
instructions[name] = data
253-
except Exception as e:
254-
logging.error(f"Error loading {yaml_file}: {e}")
255-
continue
256-
logging.info(f"Loaded {len(instructions)} instructions after filtering")
257-
return instructions
258-
259-
260225
def generate_binutils_opcodes(instr_dict, output_file="riscv-opc.c", extension_mapper=None, binutils_path=None):
261226
operand_mapper = OperandMapper(binutils_path)
262227
if extension_mapper is None:

backends/generators/generator.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#!/usr/bin/env python3
22
import os
3+
import glob
34
import yaml
45
import logging
56
import pprint
@@ -281,6 +282,79 @@ def load_instructions(
281282
return instr_dict
282283

283284

285+
def load_full_instructions(inst_dir, enabled_extensions, include_all=False, target_arch="RV64"):
286+
"""Load full instruction metadata, optionally filtering by extensions and architecture.
287+
288+
Parameters
289+
----------
290+
inst_dir : str
291+
Root directory containing instruction YAML files.
292+
enabled_extensions : list[str]
293+
Extensions that should be considered enabled when filtering instructions.
294+
include_all : bool, optional
295+
When True, skip extension filtering entirely.
296+
target_arch : str, optional
297+
Target architecture selector: "RV32", "RV64", or "BOTH". When a YAML file
298+
provides per-architecture encodings, the matching entry is selected.
299+
300+
Returns
301+
-------
302+
dict[str, dict]
303+
Mapping from instruction name to the loaded YAML data (with a resolved
304+
``encoding`` section for the requested architecture).
305+
"""
306+
307+
instructions = {}
308+
ext_filter = [] if enabled_extensions is None else enabled_extensions
309+
310+
yaml_files = glob.glob(os.path.join(inst_dir, "**/*.yaml"), recursive=True)
311+
logging.info("Found %d instruction files in %s", len(yaml_files), inst_dir)
312+
313+
for yaml_file in yaml_files:
314+
try:
315+
with open(yaml_file, "r", encoding="utf-8") as fh:
316+
data = yaml.safe_load(fh)
317+
318+
if not isinstance(data, dict) or data.get("kind") != "instruction":
319+
continue
320+
321+
name = data.get("name")
322+
if not name:
323+
continue
324+
325+
defined_by = data.get("definedBy")
326+
if not include_all and defined_by:
327+
try:
328+
meets_req = parse_extension_requirements(defined_by)
329+
if not meets_req(ext_filter):
330+
logging.debug(
331+
"Skipping %s - extension requirements not met", name
332+
)
333+
continue
334+
except Exception as exc: # pragma: no cover - logging path only
335+
logging.debug(
336+
"Error parsing extension requirements for %s: %s",
337+
name,
338+
exc,
339+
)
340+
continue
341+
342+
encoding = data.get("encoding", {})
343+
if target_arch in ("RV32", "RV64") and isinstance(encoding, dict):
344+
arch_encoding = encoding.get(target_arch)
345+
if arch_encoding:
346+
data["encoding"] = arch_encoding
347+
348+
instructions[name] = data
349+
350+
except Exception as exc: # pragma: no cover - logging path only
351+
logging.error("Error loading %s: %s", yaml_file, exc)
352+
continue
353+
354+
logging.info("Loaded %d instructions after filtering", len(instructions))
355+
return instructions
356+
357+
284358
def load_csrs(csr_root, enabled_extensions, include_all=False, target_arch="RV64"):
285359
"""
286360
Recursively walk through csr_root, load YAML files that define a CSR,

backends/generators/qemu/README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# QEMU generator
2+
3+
## Usage
4+
5+
```sh
6+
python3 generate_insn32_decode.py \
7+
--inst-dir ../../../spec/std/isa/inst/ \
8+
--extensions I,Zicsr \
9+
--arch RV64 \
10+
--output ./insn32.snippet
11+
```
12+
13+
Arguments:
14+
15+
- `--extensions` – comma-separated list of enabled extensions. Use
16+
`--include-all` to bypass filtering.
17+
- `--arch` – target architecture (`RV32`, `RV64`, or `BOTH`).
18+
- `--output` – destination file (`-` for stdout).
19+
20+
Run with `--verbose` to see which instructions cannot yet be mapped to a QEMU
21+
instruction format tag.

0 commit comments

Comments
 (0)