From a53ca1de4df5399dec8dc1c54d0d5f266f2b599c Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Fri, 24 Oct 2025 19:07:37 +0100 Subject: [PATCH] Emit RECORD_DYNAMIC_JUMP_TAKEN automatically --- ...-10-18-21-50-44.gh-issue-139109.9QQOzN.rst | 2 +- Python/bytecodes.c | 9 ---- Python/generated_cases.c.h | 2 + Tools/cases_generator/analyzer.py | 44 ++++++++++++++++++- Tools/cases_generator/generators_common.py | 25 +++++++++++ 5 files changed, 71 insertions(+), 11 deletions(-) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-18-21-50-44.gh-issue-139109.9QQOzN.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-18-21-50-44.gh-issue-139109.9QQOzN.rst index 3d6f45b2b447..40b9d19ee429 100644 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-18-21-50-44.gh-issue-139109.9QQOzN.rst +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-18-21-50-44.gh-issue-139109.9QQOzN.rst @@ -1 +1 @@ -A new tracing frontend for the JIT compiler has been implemented. Patch by Ken Jin. Design for CPython by Mark Shannon, Ken Jin and Brandt Bucher. +A new tracing frontend for the JIT compiler has been implemented. Patch by Ken Jin. Design for CPython by Ken Jin, Mark Shannon and Brandt Bucher. diff --git a/Python/bytecodes.c b/Python/bytecodes.c index fc12dc787bce..ee9d41bd5194 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -45,10 +45,6 @@ #define USE_COMPUTED_GOTOS 0 #include "ceval_macros.h" -#include "ceval_macros.h" -#include "ceval_macros.h" -#include "../Include/internal/pycore_code.h" -#include "../Include/internal/pycore_stackref.h" /* Flow control macros */ @@ -1380,7 +1376,6 @@ dummy_func( if (err == 0) { assert(retval_o != NULL); JUMPBY(oparg); - RECORD_DYNAMIC_JUMP_TAKEN(); } else { PyStackRef_CLOSE(v); @@ -3235,7 +3230,6 @@ dummy_func( } // Jump forward by oparg and skip the following END_FOR JUMPBY(oparg + 1); - RECORD_DYNAMIC_JUMP_TAKEN(); DISPATCH(); } next = item; @@ -3297,7 +3291,6 @@ dummy_func( null_or_index = PyStackRef_TagInt(-1); /* Jump forward oparg, then skip following END_FOR instruction */ JUMPBY(oparg + 1); - RECORD_DYNAMIC_JUMP_TAKEN(); DISPATCH(); } #endif @@ -3375,7 +3368,6 @@ dummy_func( null_or_index = PyStackRef_TagInt(-1); /* Jump forward oparg, then skip following END_FOR instruction */ JUMPBY(oparg + 1); - RECORD_DYNAMIC_JUMP_TAKEN(); DISPATCH(); } } @@ -3420,7 +3412,6 @@ dummy_func( if (r->len <= 0) { // Jump over END_FOR instruction. JUMPBY(oparg + 1); - RECORD_DYNAMIC_JUMP_TAKEN(); DISPATCH(); } } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 8169a36a9b95..6ed8bce67cc1 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -6236,6 +6236,7 @@ if (result == 0) { null_or_index = PyStackRef_TagInt(-1); JUMPBY(oparg + 1); + RECORD_DYNAMIC_JUMP_TAKEN(); stack_pointer[-1] = null_or_index; DISPATCH(); } @@ -7363,6 +7364,7 @@ JUMP_TO_LABEL(error); } JUMPBY(oparg + 1); + RECORD_DYNAMIC_JUMP_TAKEN(); stack_pointer[-1] = null_or_index; DISPATCH(); } diff --git a/Tools/cases_generator/analyzer.py b/Tools/cases_generator/analyzer.py index 7a5e8786f2a8..a525a2bf7055 100644 --- a/Tools/cases_generator/analyzer.py +++ b/Tools/cases_generator/analyzer.py @@ -35,6 +35,7 @@ class Properties: pure: bool uses_opcode: bool needs_guard_ip: bool + unpredictable_jump: bool tier: int | None = None const_oparg: int = -1 needs_prev: bool = False @@ -76,7 +77,8 @@ class Properties: pure=all(p.pure for p in properties), needs_prev=any(p.needs_prev for p in properties), no_save_ip=all(p.no_save_ip for p in properties), - needs_guard_ip=any(p.needs_guard_ip for p in properties) + needs_guard_ip=any(p.needs_guard_ip for p in properties), + unpredictable_jump=any(p.unpredictable_jump for p in properties), ) @property @@ -105,6 +107,7 @@ SKIP_PROPERTIES = Properties( pure=True, no_save_ip=False, needs_guard_ip=False, + unpredictable_jump=False, ) @@ -887,6 +890,42 @@ def stmt_escapes(stmt: Stmt) -> bool: else: assert False, "Unexpected statement type" +def stmt_has_jump_on_unpredictable_path_body(stmts: list[Stmt] | None, branches_seen: int) -> bool: + if not stmts: + return False, branches_seen + predict = False + seen = 0 + for st in stmts: + predict_body, seen_body = stmt_has_jump_on_unpredictable_path(st, branches_seen) + predict = predict or predict_body + seen += seen_body + return predict, seen + +def stmt_has_jump_on_unpredictable_path(stmt: Stmt, branches_seen: int) -> bool: + if isinstance(stmt, BlockStmt): + return stmt_has_jump_on_unpredictable_path_body(stmt.body, branches_seen) + elif isinstance(stmt, SimpleStmt): + for tkn in stmt.contents: + if tkn.text == "JUMPBY": + return True, branches_seen + return False, branches_seen + elif isinstance(stmt, IfStmt): + return True, branches_seen + 1 + elif isinstance(stmt, MacroIfStmt): + predict, seen = stmt_has_jump_on_unpredictable_path_body(stmt.body, branches_seen) + if stmt.else_body: + predict_else, seen_else = stmt_has_jump_on_unpredictable_path_body(stmt.else_body, branches_seen) + return predict != predict_else, seen + seen_else + return predict, seen + elif isinstance(stmt, ForStmt): + unpredictable, branches_seen = stmt_has_jump_on_unpredictable_path(stmt.body, branches_seen) + return unpredictable, branches_seen + 1 + elif isinstance(stmt, WhileStmt): + unpredictable, branches_seen = stmt_has_jump_on_unpredictable_path(stmt.body, branches_seen) + return unpredictable, branches_seen + 1 + else: + assert False, f"Unexpected statement type {stmt}" + def compute_properties(op: parser.CodeDef) -> Properties: escaping_calls = find_escaping_api_calls(op) @@ -914,6 +953,8 @@ def compute_properties(op: parser.CodeDef) -> Properties: escapes = stmt_escapes(op.block) pure = False if isinstance(op, parser.LabelDef) else "pure" in op.annotations no_save_ip = False if isinstance(op, parser.LabelDef) else "no_save_ip" in op.annotations + unpredictable, branches_seen = stmt_has_jump_on_unpredictable_path(op.block, 0) + unpredictable_jump = False if isinstance(op, parser.LabelDef) else (unpredictable and branches_seen > 0) return Properties( escaping_calls=escaping_calls, escapes=escapes, @@ -938,6 +979,7 @@ def compute_properties(op: parser.CodeDef) -> Properties: tier=tier_variable(op), needs_prev=variable_used(op, "prev_instr"), needs_guard_ip=variable_used(op, "TIER2_STORE_IP") or variable_used(op, "LLTRACE_RESUME_FRAME") or variable_used(op, "DISPATCH_INLINED"), + unpredictable_jump=unpredictable_jump, ) def expand(items: list[StackItem], oparg: int) -> list[StackItem]: diff --git a/Tools/cases_generator/generators_common.py b/Tools/cases_generator/generators_common.py index 804bd9773b16..fed0b4c1643a 100644 --- a/Tools/cases_generator/generators_common.py +++ b/Tools/cases_generator/generators_common.py @@ -128,6 +128,7 @@ class Emitter: "DISPATCH": self.dispatch, "INSTRUCTION_SIZE": self.instruction_size, "stack_pointer": self.stack_pointer, + "JUMPBY": self.jumpby, } self.out = out self.labels = labels @@ -402,6 +403,30 @@ class Emitter: storage.stack.clear(self.out) return True + def jumpby( + self, + tkn: Token, + tkn_iter: TokenIterator, + uop: CodeSection, + storage: Storage, + inst: Instruction | None, + ) -> bool: + self.out.start_line() + self.emit(tkn) + lparen = next(tkn_iter) + self.emit(lparen) + jump = next(tkn_iter) + self.emit(jump) + emit_to(self.out, tkn_iter, "RPAREN") + next(tkn_iter) + self.emit(");\n") + + + if uop.properties.unpredictable_jump and jump.text != "0": + self.out.start_line() + self.emit("RECORD_DYNAMIC_JUMP_TAKEN();\n") + return True + def stack_pointer( self, tkn: Token, -- 2.47.3