Skip to content

Commit

Permalink
Harvest basic DWARF info
Browse files Browse the repository at this point in the history
  • Loading branch information
brandtbucher committed Nov 13, 2024
1 parent 09d6f5d commit f920730
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 20 deletions.
30 changes: 24 additions & 6 deletions Python/jit.c
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,9 @@ combine_symbol_mask(const symbol_mask src, symbol_mask dest)
}
}

extern void __register_frame(const void *);
extern void __deregister_frame(const void *);

// Compiles executor in-place. Don't forget to call _PyJIT_Free later!
int
_PyJIT_Compile(_PyExecutorObject *executor, const _PyUOpInstruction trace[], size_t length)
Expand All @@ -469,22 +472,26 @@ _PyJIT_Compile(_PyExecutorObject *executor, const _PyUOpInstruction trace[], siz
// Loop once to find the total compiled size:
size_t code_size = 0;
size_t data_size = 0;
size_t debug_size = 0;
jit_state state = {0};
group = &shim;
code_size += group->code_size;
data_size += group->data_size;
debug_size += group->debug_size;
combine_symbol_mask(group->trampoline_mask, state.trampolines.mask);
for (size_t i = 0; i < length; i++) {
const _PyUOpInstruction *instruction = &trace[i];
group = &stencil_groups[instruction->opcode];
state.instruction_starts[i] = code_size;
code_size += group->code_size;
data_size += group->data_size;
debug_size += group->debug_size;
combine_symbol_mask(group->trampoline_mask, state.trampolines.mask);
}
group = &stencil_groups[_FATAL_ERROR];
code_size += group->code_size;
data_size += group->data_size;
debug_size += group->debug_size;
combine_symbol_mask(group->trampoline_mask, state.trampolines.mask);
// Calculate the size of the trampolines required by the whole trace
for (size_t i = 0; i < Py_ARRAY_LENGTH(state.trampolines.mask); i++) {
Expand All @@ -493,8 +500,8 @@ _PyJIT_Compile(_PyExecutorObject *executor, const _PyUOpInstruction trace[], siz
// Round up to the nearest page:
size_t page_size = get_page_size();
assert((page_size & (page_size - 1)) == 0);
size_t padding = page_size - ((code_size + data_size + state.trampolines.size) & (page_size - 1));
size_t total_size = code_size + data_size + state.trampolines.size + padding;
size_t padding = page_size - ((code_size + data_size + debug_size + state.trampolines.size) & (page_size - 1));
size_t total_size = code_size + data_size + debug_size + state.trampolines.size + padding;
unsigned char *memory = jit_alloc(total_size);
if (memory == NULL) {
return -1;
Expand All @@ -506,29 +513,40 @@ _PyJIT_Compile(_PyExecutorObject *executor, const _PyUOpInstruction trace[], siz
// Loop again to emit the code:
unsigned char *code = memory;
unsigned char *data = memory + code_size;
state.trampolines.mem = memory + code_size + data_size;
unsigned char *debug = memory + code_size + data_size;
state.trampolines.mem = memory + code_size + data_size + debug_size;
// Compile the shim, which handles converting between the native
// calling convention and the calling convention used by jitted code
// (which may be different for efficiency reasons).
group = &shim;
group->emit(code, data, executor, NULL, &state);
group->emit(code, data, debug, executor, NULL, &state);
// XXX: This is probably wrong:
__register_frame(debug);
code += group->code_size;
data += group->data_size;
debug += group->debug_size;
assert(trace[0].opcode == _START_EXECUTOR);
for (size_t i = 0; i < length; i++) {
const _PyUOpInstruction *instruction = &trace[i];
group = &stencil_groups[instruction->opcode];
group->emit(code, data, executor, instruction, &state);
group->emit(code, data, debug, executor, instruction, &state);
// XXX: This is probably wrong:
__register_frame(debug);
code += group->code_size;
data += group->data_size;
debug += group->debug_size;
}
// Protect against accidental buffer overrun into data:
group = &stencil_groups[_FATAL_ERROR];
group->emit(code, data, executor, NULL, &state);
group->emit(code, data, debug, executor, NULL, &state);
// XXX: This is probably wrong:
__register_frame(debug);
code += group->code_size;
data += group->data_size;
debug += group->debug_size;
assert(code == memory + code_size);
assert(data == memory + code_size + data_size);
assert(debug == memory + code_size + data_size + debug_size);
if (mark_executable(memory, total_size)) {
jit_free(memory, total_size);
return -1;
Expand Down
9 changes: 7 additions & 2 deletions Tools/jit/_stencils.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ class HoleValue(enum.Enum):
CONTINUE = enum.auto()
# The base address of the read-only data for this uop:
DATA = enum.auto()
# XXX
DEBUG = enum.auto()
# The address of the current executor (exposed as _JIT_EXECUTOR):
EXECUTOR = enum.auto()
# The base address of the "global" offset table located in the read-only data.
Expand Down Expand Up @@ -95,6 +97,7 @@ class HoleValue(enum.Enum):
HoleValue.CODE: "(uintptr_t)code",
HoleValue.CONTINUE: "(uintptr_t)code + sizeof(code_body)",
HoleValue.DATA: "(uintptr_t)data",
# HoleValue.DEBUG: "",
HoleValue.EXECUTOR: "(uintptr_t)executor",
# These should all have been turned into DATA values by process_relocations:
# HoleValue.GOT: "",
Expand Down Expand Up @@ -258,6 +261,7 @@ class StencilGroup:

code: Stencil = dataclasses.field(default_factory=Stencil, init=False)
data: Stencil = dataclasses.field(default_factory=Stencil, init=False)
debug: Stencil = dataclasses.field(default_factory=Stencil, init=False)
symbols: dict[int | str, tuple[HoleValue, int]] = dataclasses.field(
default_factory=dict, init=False
)
Expand Down Expand Up @@ -291,7 +295,7 @@ def process_relocations(
self.code.remove_jump(alignment=alignment)
self.code.pad(alignment)
self.data.pad(8)
for stencil in [self.code, self.data]:
for stencil in [self.code, self.data, self.debug]:
for hole in stencil.holes:
if hole.value is HoleValue.GOT:
assert hole.symbol is not None
Expand All @@ -312,6 +316,7 @@ def process_relocations(
self._emit_global_offset_table()
self.code.holes.sort(key=lambda hole: hole.offset)
self.data.holes.sort(key=lambda hole: hole.offset)
self.debug.holes.sort(key=lambda hole: hole.offset)

def _global_offset_table_lookup(self, symbol: str) -> int:
return len(self.data.body) + self._got.setdefault(symbol, 8 * len(self._got))
Expand Down Expand Up @@ -355,7 +360,7 @@ def _get_trampoline_mask(self) -> str:

def as_c(self, opname: str) -> str:
"""Dump this hole as a StencilGroup initializer."""
return f"{{emit_{opname}, {len(self.code.body)}, {len(self.data.body)}, {self._get_trampoline_mask()}}}"
return f"{{emit_{opname}, {len(self.code.body)}, {len(self.data.body)}, {len(self.debug.body)}, {self._get_trampoline_mask()}}}"


def symbol_to_value(symbol: str) -> tuple[HoleValue, str | None]:
Expand Down
25 changes: 19 additions & 6 deletions Tools/jit/_targets.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,15 @@ async def _parse(self, path: pathlib.Path) -> _stencils.StencilGroup:
for line in output.splitlines()
if not line.isspace()
)
# XXX: "--unwind-info" on other platforms?
args = ["--dwarf=frames", f"{path}"]
output = await _llvm.maybe_run("llvm-objdump", args, echo=self.verbose)
if output is not None:
group.debug.disassembly.extend(
line.expandtabs().strip()
for line in output.splitlines()
if not line.isspace()
)
args = [
"--elf-output-style=JSON",
"--expand-relocs",
Expand Down Expand Up @@ -122,10 +131,6 @@ async def _compile(
f"-I{CPYTHON / 'Tools' / 'jit'}",
"-O3",
"-c",
# This debug info isn't necessary, and bloats out the JIT'ed code.
# We *may* be able to re-enable this, process it, and JIT it for a
# nicer debugging experience... but that needs a lot more research:
"-fno-asynchronous-unwind-tables",
# Don't call built-in functions that we can't find or patch:
"-fno-builtin",
# Emit relaxable 64-bit calls/jumps, so we don't have to worry about
Expand Down Expand Up @@ -305,9 +310,11 @@ def _handle_section(
value, base = group.symbols[section["Info"]]
if value is _stencils.HoleValue.CODE:
stencil = group.code
else:
assert value is _stencils.HoleValue.DATA
elif value is _stencils.HoleValue.DATA:
stencil = group.data
else:
assert value is _stencils.HoleValue.DEBUG
stencil = group.debug
for wrapped_relocation in section["Relocations"]:
relocation = wrapped_relocation["Relocation"]
hole = self._handle_relocation(base, relocation, stencil.body)
Expand All @@ -330,6 +337,12 @@ def _handle_section(
group.symbols[name] = value, offset
stencil.body.extend(section["SectionData"]["Bytes"])
assert not section["Relocations"]
elif section_type == "SHT_X86_64_UNWIND":
assert "SHF_ALLOC" in flags
assert not section["Symbols"]
group.symbols[section["Index"]] = _stencils.HoleValue.DEBUG, 0
group.debug.body.extend(section["SectionData"]["Bytes"])
assert not section["Relocations"]
else:
assert section_type in {
"SHT_GROUP",
Expand Down
23 changes: 17 additions & 6 deletions Tools/jit/_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ def _dump_footer(
yield ""
yield "typedef struct {"
yield " void (*emit)("
yield " unsigned char *code, unsigned char *data, _PyExecutorObject *executor,"
yield " const _PyUOpInstruction *instruction, jit_state *state);"
yield " unsigned char *code, unsigned char *data, unsigned char *debug,"
yield " _PyExecutorObject *executor, const _PyUOpInstruction *instruction,"
yield " jit_state *state);"
yield " size_t code_size;"
yield " size_t data_size;"
yield " size_t debug_size;"
yield " symbol_mask trampoline_mask;"
yield "} StencilGroup;"
yield ""
Expand All @@ -43,10 +45,15 @@ def _dump_footer(
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 " unsigned char *code, unsigned char *data, unsigned char *debug,"
yield " _PyExecutorObject *executor, const _PyUOpInstruction *instruction,"
yield " jit_state *state)"
yield "{"
for part, stencil in [("code", group.code), ("data", group.data)]:
for part, stencil in [
("code", group.code),
("data", group.data),
("debug", group.debug),
]:
for line in stencil.disassembly:
yield f" // {line}"
if stencil.body:
Expand All @@ -56,7 +63,11 @@ def _dump_stencil(opname: str, group: _stencils.StencilGroup) -> typing.Iterator
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)]:
for part, stencil in [
("data", group.data),
("code", group.code),
("debug", group.debug),
]:
if stencil.body:
yield f" memcpy({part}, {part}_body, sizeof({part}_body));"
skip = False
Expand Down

0 comments on commit f920730

Please sign in to comment.