"""Utilities for writing JIT build artifacts out to C header files.""" import typing import math import _dwarf import _stencils def _dump_footer( groups: dict[str, _stencils.StencilGroup], symbols: dict[str, int] ) -> typing.Iterator[str]: symbol_mask_size = max(math.ceil(len(symbols) / 32), 1) yield f'static_assert(SYMBOL_MASK_WORDS >= {symbol_mask_size}, "SYMBOL_MASK_WORDS too small");' yield "" yield "typedef struct {" yield " void (*emit)(" yield " unsigned char *code, unsigned char *data, _PyExecutorObject *executor," yield " const _PyUOpInstruction *instruction, jit_state *state);" yield " size_t code_size;" yield " size_t data_size;" yield " symbol_mask trampoline_mask;" yield " symbol_mask got_mask;" yield "} StencilGroup;" yield "" yield "static const StencilGroup stencil_groups[MAX_UOP_REGS_ID + 1] = {" for opname, group in sorted(groups.items()): yield f" [{opname}] = {group.as_c(opname)}," yield "};" yield "" yield f"static const void * const symbols_map[{max(len(symbols), 1)}] = {{" if symbols: for symbol, ordinal in symbols.items(): yield f" [{ordinal}] = &{symbol}," else: yield " 0" yield "};" def _dump_stencil(opname: str, group: _stencils.StencilGroup) -> typing.Iterator[str]: yield "void" yield f"emit_{opname}(" yield " unsigned char *code, unsigned char *data, _PyExecutorObject *executor," yield " const _PyUOpInstruction *instruction, jit_state *state)" yield "{" for part, stencil in [("code", group.code), ("data", group.data)]: for line in stencil.disassembly: yield f" // {line}" stripped = stencil.body.rstrip(b"\x00") if stripped: yield f" const unsigned char {part}_body[{len(stencil.body)}] = {{" for i in range(0, len(stripped), 8): row = " ".join(f"{byte:#04x}," for byte in stripped[i : i + 8]) yield f" {row}" yield " };" # Data is written first (so relaxations in the code work properly): for part, stencil in [("data", group.data), ("code", group.code)]: if stencil.body.rstrip(b"\x00"): yield f" memcpy({part}, {part}_body, sizeof({part}_body));" stencil.holes.sort(key=lambda hole: hole.offset) for hole in stencil.holes: yield f" {hole.as_c(part)}" yield "}" yield "" def dump( groups: dict[str, _stencils.StencilGroup], symbols: dict[str, int] ) -> typing.Iterator[str]: """Yield a JIT compiler line-by-line as a C header file.""" for opname, group in groups.items(): yield from _dump_stencil(opname, group) yield from _dump_footer(groups, symbols) def dump_unwind_info( unwind_info: _dwarf.UnwindInfo | None, ) -> typing.Iterator[str]: """Yield JIT unwind information line-by-line as a C header file.""" if unwind_info is None: yield "#define JIT_UNWIND_INFO_SUPPORTED 0" return yield "#define JIT_UNWIND_INFO_SUPPORTED 1" fields = [ ("JIT_UNWIND_CODE_ALIGNMENT_FACTOR", unwind_info.code_alignment_factor), ("JIT_UNWIND_DATA_ALIGNMENT_FACTOR", unwind_info.data_alignment_factor), ("JIT_UNWIND_RA_REG", unwind_info.return_address_register), ("JIT_UNWIND_CFA_REG", unwind_info.cfa_register), ("JIT_UNWIND_CFA_OFFSET", unwind_info.cfa_offset), ("JIT_UNWIND_FP_REG", unwind_info.frame_pointer_register), ("JIT_UNWIND_FP_OFFSET", unwind_info.frame_pointer_offset), ("JIT_UNWIND_RA_OFFSET", unwind_info.return_address_offset), ] for name, value in fields: yield f"#define {name} {value}"