From e0d8c95de4aac5cad1221a5852c56c4179c331a3 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Thu, 20 Mar 2025 10:45:47 +0000 Subject: [PATCH 1/6] Remove conditional stack effects -- We no longer need them --- Python/bytecodes.c | 7 +- Python/executor_cases.c.h | 8 +- Python/generated_cases.c.h | 88 ++++++++++++-------- Python/optimizer.c | 6 +- Python/optimizer_bytecodes.c | 14 ++-- Python/optimizer_cases.c.h | 14 ++-- Tools/cases_generator/analyzer.py | 54 +----------- Tools/cases_generator/generators_common.py | 2 - Tools/cases_generator/lexer.py | 1 - Tools/cases_generator/optimizer_generator.py | 10 +-- Tools/cases_generator/parsing.py | 14 +--- Tools/cases_generator/stack.py | 34 +------- Tools/cases_generator/tier1_generator.py | 5 +- Tools/cases_generator/tier2_generator.py | 12 +-- 14 files changed, 95 insertions(+), 174 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 66546080b1f5fe..cdd4d5bdd46b43 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -59,7 +59,6 @@ #define guard #define override #define specializing -#define split #define replicate(TIMES) #define tier1 #define no_save_ip @@ -1686,8 +1685,10 @@ dummy_func( ERROR_IF(PyStackRef_IsNull(*res), error); } - op(_PUSH_NULL_CONDITIONAL, ( -- null if (oparg & 1))) { - null = PyStackRef_NULL; + op(_PUSH_NULL_CONDITIONAL, ( -- null[oparg & 1])) { + if (oparg & 1) { + null[0] = PyStackRef_NULL; + } } macro(LOAD_GLOBAL) = diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index ff9f33b6db0187..42a3d6d4be9ba4 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -2285,10 +2285,12 @@ } case _PUSH_NULL_CONDITIONAL: { - _PyStackRef null = PyStackRef_NULL; + _PyStackRef *null; oparg = CURRENT_OPARG(); - null = PyStackRef_NULL; - if (oparg & 1) stack_pointer[0] = null; + null = &stack_pointer[0]; + if (oparg & 1) { + null[0] = PyStackRef_NULL; + } stack_pointer += (oparg & 1); assert(WITHIN_STACK_BOUNDS()); break; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 558b0b48ceaa71..9ce2b633d5e506 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -6982,7 +6982,7 @@ _PyStackRef class_st; _PyStackRef self_st; _PyStackRef attr; - _PyStackRef null = PyStackRef_NULL; + _PyStackRef *null; /* Skip 1 cache entry */ // _LOAD_SUPER_ATTR { @@ -7078,10 +7078,12 @@ } // _PUSH_NULL_CONDITIONAL { - null = PyStackRef_NULL; + null = &stack_pointer[1]; + if (oparg & 1) { + null[0] = PyStackRef_NULL; + } } stack_pointer[0] = attr; - if (oparg & 1) stack_pointer[1] = null; stack_pointer += 1 + (oparg & 1); assert(WITHIN_STACK_BOUNDS()); DISPATCH(); @@ -7840,7 +7842,7 @@ static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); _PyStackRef owner; _PyStackRef attr; - _PyStackRef null = PyStackRef_NULL; + _PyStackRef *null; /* Skip 1 cache entry */ // _CHECK_ATTR_CLASS { @@ -7876,9 +7878,11 @@ } // _PUSH_NULL_CONDITIONAL { - null = PyStackRef_NULL; + null = &stack_pointer[0]; + if (oparg & 1) { + null[0] = PyStackRef_NULL; + } } - if (oparg & 1) stack_pointer[0] = null; stack_pointer += (oparg & 1); assert(WITHIN_STACK_BOUNDS()); DISPATCH(); @@ -7897,7 +7901,7 @@ static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); _PyStackRef owner; _PyStackRef attr; - _PyStackRef null = PyStackRef_NULL; + _PyStackRef *null; /* Skip 1 cache entry */ // _CHECK_ATTR_CLASS { @@ -7943,9 +7947,11 @@ } // _PUSH_NULL_CONDITIONAL { - null = PyStackRef_NULL; + null = &stack_pointer[0]; + if (oparg & 1) { + null[0] = PyStackRef_NULL; + } } - if (oparg & 1) stack_pointer[0] = null; stack_pointer += (oparg & 1); assert(WITHIN_STACK_BOUNDS()); DISPATCH(); @@ -8022,7 +8028,7 @@ static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); _PyStackRef owner; _PyStackRef attr; - _PyStackRef null = PyStackRef_NULL; + _PyStackRef *null; /* Skip 1 cache entry */ // _GUARD_TYPE_VERSION { @@ -8078,9 +8084,11 @@ /* Skip 5 cache entries */ // _PUSH_NULL_CONDITIONAL { - null = PyStackRef_NULL; + null = &stack_pointer[0]; + if (oparg & 1) { + null[0] = PyStackRef_NULL; + } } - if (oparg & 1) stack_pointer[0] = null; stack_pointer += (oparg & 1); assert(WITHIN_STACK_BOUNDS()); DISPATCH(); @@ -8270,7 +8278,7 @@ static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); _PyStackRef owner; _PyStackRef attr; - _PyStackRef null = PyStackRef_NULL; + _PyStackRef *null; /* Skip 1 cache entry */ // _LOAD_ATTR_MODULE { @@ -8321,9 +8329,11 @@ /* Skip 5 cache entries */ // _PUSH_NULL_CONDITIONAL { - null = PyStackRef_NULL; + null = &stack_pointer[0]; + if (oparg & 1) { + null[0] = PyStackRef_NULL; + } } - if (oparg & 1) stack_pointer[0] = null; stack_pointer += (oparg & 1); assert(WITHIN_STACK_BOUNDS()); DISPATCH(); @@ -8552,7 +8562,7 @@ static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); _PyStackRef owner; _PyStackRef attr; - _PyStackRef null = PyStackRef_NULL; + _PyStackRef *null; /* Skip 1 cache entry */ // _GUARD_TYPE_VERSION { @@ -8599,9 +8609,11 @@ /* Skip 5 cache entries */ // _PUSH_NULL_CONDITIONAL { - null = PyStackRef_NULL; + null = &stack_pointer[0]; + if (oparg & 1) { + null[0] = PyStackRef_NULL; + } } - if (oparg & 1) stack_pointer[0] = null; stack_pointer += (oparg & 1); assert(WITHIN_STACK_BOUNDS()); DISPATCH(); @@ -8620,7 +8632,7 @@ static_assert(INLINE_CACHE_ENTRIES_LOAD_ATTR == 9, "incorrect cache size"); _PyStackRef owner; _PyStackRef attr; - _PyStackRef null = PyStackRef_NULL; + _PyStackRef *null; /* Skip 1 cache entry */ // _GUARD_TYPE_VERSION { @@ -8700,9 +8712,11 @@ /* Skip 5 cache entries */ // _PUSH_NULL_CONDITIONAL { - null = PyStackRef_NULL; + null = &stack_pointer[0]; + if (oparg & 1) { + null[0] = PyStackRef_NULL; + } } - if (oparg & 1) stack_pointer[0] = null; stack_pointer += (oparg & 1); assert(WITHIN_STACK_BOUNDS()); DISPATCH(); @@ -9080,7 +9094,7 @@ _Py_CODEUNIT* const this_instr = next_instr - 5; (void)this_instr; _PyStackRef *res; - _PyStackRef null = PyStackRef_NULL; + _PyStackRef *null; // _SPECIALIZE_LOAD_GLOBAL { uint16_t counter = read_u16(&this_instr[1].cache); @@ -9114,9 +9128,11 @@ } // _PUSH_NULL_CONDITIONAL { - null = PyStackRef_NULL; + null = &stack_pointer[1]; + if (oparg & 1) { + null[0] = PyStackRef_NULL; + } } - if (oparg & 1) stack_pointer[1] = null; stack_pointer += 1 + (oparg & 1); assert(WITHIN_STACK_BOUNDS()); DISPATCH(); @@ -9134,7 +9150,7 @@ INSTRUCTION_STATS(LOAD_GLOBAL_BUILTIN); static_assert(INLINE_CACHE_ENTRIES_LOAD_GLOBAL == 4, "incorrect cache size"); _PyStackRef res; - _PyStackRef null = PyStackRef_NULL; + _PyStackRef *null; /* Skip 1 cache entry */ // _GUARD_GLOBALS_VERSION { @@ -9191,10 +9207,12 @@ } // _PUSH_NULL_CONDITIONAL { - null = PyStackRef_NULL; + null = &stack_pointer[1]; + if (oparg & 1) { + null[0] = PyStackRef_NULL; + } } stack_pointer[0] = res; - if (oparg & 1) stack_pointer[1] = null; stack_pointer += 1 + (oparg & 1); assert(WITHIN_STACK_BOUNDS()); DISPATCH(); @@ -9212,7 +9230,7 @@ INSTRUCTION_STATS(LOAD_GLOBAL_MODULE); static_assert(INLINE_CACHE_ENTRIES_LOAD_GLOBAL == 4, "incorrect cache size"); _PyStackRef res; - _PyStackRef null = PyStackRef_NULL; + _PyStackRef *null; /* Skip 1 cache entry */ // _NOP { @@ -9256,10 +9274,12 @@ } // _PUSH_NULL_CONDITIONAL { - null = PyStackRef_NULL; + null = &stack_pointer[1]; + if (oparg & 1) { + null[0] = PyStackRef_NULL; + } } stack_pointer[0] = res; - if (oparg & 1) stack_pointer[1] = null; stack_pointer += 1 + (oparg & 1); assert(WITHIN_STACK_BOUNDS()); DISPATCH(); @@ -9387,7 +9407,7 @@ _PyStackRef class_st; _PyStackRef self_st; _PyStackRef attr; - _PyStackRef null = PyStackRef_NULL; + _PyStackRef *null; // _SPECIALIZE_LOAD_SUPER_ATTR { class_st = stack_pointer[-2]; @@ -9499,10 +9519,12 @@ } // _PUSH_NULL_CONDITIONAL { - null = PyStackRef_NULL; + null = &stack_pointer[1]; + if (oparg & 1) { + null[0] = PyStackRef_NULL; + } } stack_pointer[0] = attr; - if (oparg & 1) stack_pointer[1] = null; stack_pointer += 1 + (oparg & 1); assert(WITHIN_STACK_BOUNDS()); DISPATCH(); diff --git a/Python/optimizer.c b/Python/optimizer.c index 6fc5eabdf8b44e..aa60f538bbe5c0 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -1226,11 +1226,7 @@ uop_optimize( for (int pc = 0; pc < length; pc++) { int opcode = buffer[pc].opcode; int oparg = buffer[pc].oparg; - if (_PyUop_Flags[opcode] & HAS_OPARG_AND_1_FLAG) { - buffer[pc].opcode = opcode + 1 + (oparg & 1); - assert(strncmp(_PyOpcode_uop_name[buffer[pc].opcode], _PyOpcode_uop_name[opcode], strlen(_PyOpcode_uop_name[opcode])) == 0); - } - else if (oparg < _PyUop_Replication[opcode]) { + if (oparg < _PyUop_Replication[opcode]) { buffer[pc].opcode = opcode + oparg + 1; assert(strncmp(_PyOpcode_uop_name[buffer[pc].opcode], _PyOpcode_uop_name[opcode], strlen(_PyOpcode_uop_name[opcode])) == 0); } diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index ea7c39bd01ea07..486751847a43cf 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -546,10 +546,14 @@ dummy_func(void) { } } - op (_PUSH_NULL_CONDITIONAL, ( -- null if (oparg & 1))) { - int opcode = (oparg & 1) ? _PUSH_NULL : _NOP; - REPLACE_OP(this_instr, opcode, 0, 0); - null = sym_new_null(ctx); + op (_PUSH_NULL_CONDITIONAL, ( -- null[oparg & 1])) { + if (oparg & 1) { + REPLACE_OP(this_instr, _PUSH_NULL, 0, 0); + null[0] = sym_new_null(ctx); + } + else { + REPLACE_OP(this_instr, _NOP); + } } op(_LOAD_ATTR, (owner -- attr, self_or_null[oparg&1])) { @@ -765,7 +769,7 @@ dummy_func(void) { Py_UNREACHABLE(); } - op(_PUSH_FRAME, (new_frame: _Py_UOpsAbstractFrame * -- unused if (0))) { + op(_PUSH_FRAME, (new_frame: _Py_UOpsAbstractFrame * -- )) { SYNC_SP(); ctx->frame->stack_pointer = stack_pointer; ctx->frame = new_frame; diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 3f315901a5beb8..f8962b6054f94e 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -921,11 +921,15 @@ } case _PUSH_NULL_CONDITIONAL: { - JitOptSymbol *null = NULL; - int opcode = (oparg & 1) ? _PUSH_NULL : _NOP; - REPLACE_OP(this_instr, opcode, 0, 0); - null = sym_new_null(ctx); - if (oparg & 1) stack_pointer[0] = null; + JitOptSymbol **null; + null = &stack_pointer[0]; + if (oparg & 1) { + REPLACE_OP(this_instr, _PUSH_NULL, 0, 0); + null[0] = sym_new_null(ctx); + } + else { + REPLACE_OP(this_instr, _NOP); + } stack_pointer += (oparg & 1); assert(WITHIN_STACK_BOUNDS()); break; diff --git a/Tools/cases_generator/analyzer.py b/Tools/cases_generator/analyzer.py index e0ef198c1646c2..ac2cfb7b50be40 100644 --- a/Tools/cases_generator/analyzer.py +++ b/Tools/cases_generator/analyzer.py @@ -33,7 +33,6 @@ class Properties: pure: bool uses_opcode: bool tier: int | None = None - oparg_and_1: bool = False const_oparg: int = -1 needs_prev: bool = False no_save_ip: bool = False @@ -136,16 +135,14 @@ def size(self) -> int: class StackItem: name: str type: str | None - condition: str | None size: str peek: bool = False used: bool = False def __str__(self) -> str: - cond = f" if ({self.condition})" if self.condition else "" size = f"[{self.size}]" if self.size else "" type = "" if self.type is None else f"{self.type} " - return f"{type}{self.name}{size}{cond} {self.peek}" + return f"{type}{self.name}{size} {self.peek}" def is_array(self) -> bool: return self.size != "" @@ -348,10 +345,7 @@ def override_error( def convert_stack_item( item: parser.StackEffect, replace_op_arg_1: str | None ) -> StackItem: - cond = item.cond - if replace_op_arg_1 and OPARG_AND_1.match(item.cond): - cond = replace_op_arg_1 - return StackItem(item.name, item.type, cond, item.size) + return StackItem(item.name, item.type, item.size) def check_unused(stack: list[StackItem], input_names: dict[str, lexer.Token]) -> None: "Unused items cannot be on the stack above used, non-peek items" @@ -815,33 +809,12 @@ def stack_effect_only_peeks(instr: parser.InstDef) -> bool: return False if len(stack_inputs) == 0: return False - if any(s.cond for s in stack_inputs) or any(s.cond for s in instr.outputs): - return False return all( (s.name == other.name and s.type == other.type and s.size == other.size) for s, other in zip(stack_inputs, instr.outputs) ) -OPARG_AND_1 = re.compile("\\(*oparg *& *1") - - -def effect_depends_on_oparg_1(op: parser.InstDef) -> bool: - for effect in op.inputs: - if isinstance(effect, parser.CacheEffect): - continue - if not effect.cond: - continue - if OPARG_AND_1.match(effect.cond): - return True - for effect in op.outputs: - if not effect.cond: - continue - if OPARG_AND_1.match(effect.cond): - return True - return False - - def compute_properties(op: parser.CodeDef) -> Properties: escaping_calls = find_escaping_api_calls(op) has_free = ( @@ -908,29 +881,6 @@ def make_uop( body=op.block.tokens, properties=compute_properties(op), ) - if effect_depends_on_oparg_1(op) and "split" in op.annotations: - result.properties.oparg_and_1 = True - for bit in ("0", "1"): - name_x = name + "_" + bit - properties = compute_properties(op) - if properties.oparg: - # May not need oparg anymore - properties.oparg = any( - token.text == "oparg" for token in op.block.tokens - ) - rep = Uop( - name=name_x, - context=op.context, - annotations=op.annotations, - stack=analyze_stack(op, bit), - caches=analyze_caches(inputs), - deferred_refs=analyze_deferred_refs(op), - output_stores=find_stores_outputs(op), - body=op.block.tokens, - properties=properties, - ) - rep.replicates = result - uops[name_x] = rep for anno in op.annotations: if anno.startswith("replicate"): result.replicated = int(anno[10:-1]) diff --git a/Tools/cases_generator/generators_common.py b/Tools/cases_generator/generators_common.py index 8e6bc5a8995dc9..fc0b468266078d 100644 --- a/Tools/cases_generator/generators_common.py +++ b/Tools/cases_generator/generators_common.py @@ -648,8 +648,6 @@ def cflags(p: Properties) -> str: flags.append("HAS_PURE_FLAG") if p.no_save_ip: flags.append("HAS_NO_SAVE_IP_FLAG") - if p.oparg_and_1: - flags.append("HAS_OPARG_AND_1_FLAG") if flags: return " | ".join(flags) else: diff --git a/Tools/cases_generator/lexer.py b/Tools/cases_generator/lexer.py index 6afca750be9b19..b4bcd73fdbfe52 100644 --- a/Tools/cases_generator/lexer.py +++ b/Tools/cases_generator/lexer.py @@ -227,7 +227,6 @@ def choice(*opts: str) -> str: "register", "replaced", "pure", - "split", "replicate", "tier1", "tier2", diff --git a/Tools/cases_generator/optimizer_generator.py b/Tools/cases_generator/optimizer_generator.py index b265847a26c971..15be7608e93937 100644 --- a/Tools/cases_generator/optimizer_generator.py +++ b/Tools/cases_generator/optimizer_generator.py @@ -48,19 +48,13 @@ def declare_variables(uop: Uop, out: CWriter, skip_inputs: bool) -> None: for var in reversed(uop.stack.inputs): if var.used and var.name not in variables: variables.add(var.name) - if var.condition: - out.emit(f"{type_name(var)}{var.name} = NULL;\n") - else: - out.emit(f"{type_name(var)}{var.name};\n") + out.emit(f"{type_name(var)}{var.name};\n") for var in uop.stack.outputs: if var.peek: continue if var.name not in variables: variables.add(var.name) - if var.condition: - out.emit(f"{type_name(var)}{var.name} = NULL;\n") - else: - out.emit(f"{type_name(var)}{var.name};\n") + out.emit(f"{type_name(var)}{var.name};\n") def decref_inputs( diff --git a/Tools/cases_generator/parsing.py b/Tools/cases_generator/parsing.py index 011f34de288871..84aed49d491e01 100644 --- a/Tools/cases_generator/parsing.py +++ b/Tools/cases_generator/parsing.py @@ -77,12 +77,11 @@ class Block(Node): class StackEffect(Node): name: str = field(compare=False) # __eq__ only uses type, cond, size type: str = "" # Optional `:type` - cond: str = "" # Optional `if (cond)` size: str = "" # Optional `[size]` # Note: size cannot be combined with type or cond def __repr__(self) -> str: - items = [self.name, self.type, self.cond, self.size] + items = [self.name, self.type, self.size] while items and items[-1] == "": del items[-1] return f"StackEffect({', '.join(repr(item) for item in items)})" @@ -299,22 +298,15 @@ def stack_effect(self) -> StackEffect | None: type_text = self.require(lx.IDENTIFIER).text.strip() if self.expect(lx.TIMES): type_text += " *" - cond_text = "" - if self.expect(lx.IF): - self.require(lx.LPAREN) - if not (cond := self.expression()): - raise self.make_syntax_error("Expected condition") - self.require(lx.RPAREN) - cond_text = cond.text.strip() size_text = "" if self.expect(lx.LBRACKET): - if type_text or cond_text: + if type_text: raise self.make_syntax_error("Unexpected [") if not (size := self.expression()): raise self.make_syntax_error("Expected expression") self.require(lx.RBRACKET) size_text = size.text.strip() - return StackEffect(tkn.text, type_text, cond_text, size_text) + return StackEffect(tkn.text, type_text, size_text) return None @contextual diff --git a/Tools/cases_generator/stack.py b/Tools/cases_generator/stack.py index eb7d1967eb75c3..2f0b00d657f1bf 100644 --- a/Tools/cases_generator/stack.py +++ b/Tools/cases_generator/stack.py @@ -23,17 +23,7 @@ def maybe_parenthesize(sym: str) -> str: def var_size(var: StackItem) -> str: - if var.condition: - # Special case simplifications - if var.condition == "0": - return "0" - elif var.condition == "1": - return var.get_size() - elif var.condition == "oparg & 1" and not var.size: - return f"({var.condition})" - else: - return f"(({var.condition}) ? {var.get_size()} : 0)" - elif var.size: + if var.size: return var.size else: return "1" @@ -89,10 +79,6 @@ def size(self) -> str: def name(self) -> str: return self.item.name - @property - def condition(self) -> str | None: - return self.item.condition - def is_array(self) -> bool: return self.item.is_array() @@ -275,15 +261,7 @@ def pop(self, var: StackItem) -> tuple[str, Local]: cast = f"({var.type})" if (not indirect and var.type) else "" bits = ".bits" if cast and self.extract_bits else "" assign = f"{var.name} = {cast}{indirect}stack_pointer[{self.base_offset.to_c()}]{bits};" - if var.condition: - if var.condition == "1": - assign = f"{assign}\n" - elif var.condition == "0": - return "", Local.unused(var) - else: - assign = f"if ({var.condition}) {{ {assign} }}\n" - else: - assign = f"{assign}\n" + assign = f"{assign}\n" return assign, Local.from_memory(var) def push(self, var: Local) -> None: @@ -303,10 +281,6 @@ def _do_emit( ) -> None: cast = f"({cast_type})" if var.type else "" bits = ".bits" if cast and extract_bits else "" - if var.condition == "0": - return - if var.condition and var.condition != "1": - out.emit(f"if ({var.condition}) ") out.emit(f"stack_pointer[{base_offset.to_c()}]{bits} = {cast}{var.name};\n") def _adjust_stack_pointer(self, out: CWriter, number: str) -> None: @@ -665,7 +639,7 @@ def close_named(close: str, name: str, overwrite: str) -> None: def close_variable(var: Local, overwrite: str) -> None: nonlocal tmp_defined close = "PyStackRef_CLOSE" - if "null" in var.name or var.condition and var.condition != "1": + if "null" in var.name: close = "PyStackRef_XCLOSE" if var.size: if var.size == "1": @@ -678,8 +652,6 @@ def close_variable(var: Local, overwrite: str) -> None: close_named(close, f"{var.name}[_i]", overwrite) out.emit("}\n") else: - if var.condition and var.condition == "0": - return close_named(close, var.name, overwrite) self.clear_dead_inputs() diff --git a/Tools/cases_generator/tier1_generator.py b/Tools/cases_generator/tier1_generator.py index 0f0addb3d99589..ee375681b50f0b 100644 --- a/Tools/cases_generator/tier1_generator.py +++ b/Tools/cases_generator/tier1_generator.py @@ -40,10 +40,7 @@ def declare_variable(var: StackItem, out: CWriter) -> None: type, null = type_and_null(var) space = " " if type[-1].isalnum() else "" - if var.condition: - out.emit(f"{type}{space}{var.name} = {null};\n") - else: - out.emit(f"{type}{space}{var.name};\n") + out.emit(f"{type}{space}{var.name};\n") def declare_variables(inst: Instruction, out: CWriter) -> None: diff --git a/Tools/cases_generator/tier2_generator.py b/Tools/cases_generator/tier2_generator.py index d378815f6af391..572c636e84c0ca 100644 --- a/Tools/cases_generator/tier2_generator.py +++ b/Tools/cases_generator/tier2_generator.py @@ -41,14 +41,7 @@ def declare_variable( required.remove(var.name) type, null = type_and_null(var) space = " " if type[-1].isalnum() else "" - if var.condition: - out.emit(f"{type}{space}{var.name} = {null};\n") - if uop.replicates: - # Replicas may not use all their conditional variables - # So avoid a compiler warning with a fake use - out.emit(f"(void){var.name};\n") - else: - out.emit(f"{type}{space}{var.name};\n") + out.emit(f"{type}{space}{var.name};\n") def declare_variables(uop: Uop, out: CWriter) -> None: @@ -189,9 +182,6 @@ def generate_tier2( for name, uop in analysis.uops.items(): if uop.properties.tier == 1: continue - if uop.properties.oparg_and_1: - out.emit(f"/* {uop.name} is split on (oparg & 1) */\n\n") - continue if uop.is_super(): continue why_not_viable = uop.why_not_viable() From accf4c9130cc8bd290fc16ce93d22cfee7b16ea9 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Thu, 20 Mar 2025 11:17:06 +0000 Subject: [PATCH 2/6] Update test --- Lib/test/test_generated_cases.py | 137 ++----------------------------- 1 file changed, 6 insertions(+), 131 deletions(-) diff --git a/Lib/test/test_generated_cases.py b/Lib/test/test_generated_cases.py index 32d14548159cce..69967f1d6c19bf 100644 --- a/Lib/test/test_generated_cases.py +++ b/Lib/test/test_generated_cases.py @@ -59,14 +59,14 @@ class TestEffects(unittest.TestCase): def test_effect_sizes(self): stack = Stack() inputs = [ - x := StackItem("x", None, "", "1"), - y := StackItem("y", None, "", "oparg"), - z := StackItem("z", None, "", "oparg*2"), + x := StackItem("x", None, "1"), + y := StackItem("y", None, "oparg"), + z := StackItem("z", None, "oparg*2"), ] outputs = [ - StackItem("x", None, "", "1"), - StackItem("b", None, "", "oparg*4"), - StackItem("c", None, "", "1"), + StackItem("x", None, "1"), + StackItem("b", None, "oparg*4"), + StackItem("c", None, "1"), ] stack.pop(z) stack.pop(y) @@ -104,20 +104,6 @@ def test_push_one(self): """ self.check(input, output) - def test_cond_push(self): - input = """ - inst(OP, (a -- b, c if (oparg))) { - SPAM(); - } - """ - output = """ - case OP: { - *effect = ((oparg) ? 1 : 0); - return 0; - } - """ - self.check(input, output) - def test_ops_pass_two(self): input = """ op(A, (-- val1)) { @@ -138,25 +124,6 @@ def test_ops_pass_two(self): """ self.check(input, output) - def test_ops_pass_two_cond_push(self): - input = """ - op(A, (-- val1, val2)) { - val1 = 0; - val2 = 1; - } - op(B, (val1, val2 -- val1, val2, val3 if (oparg))) { - val3 = SPAM(); - } - macro(OP) = A + B; - """ - output = """ - case OP: { - *effect = Py_MAX(2, 2 + ((oparg) ? 1 : 0)); - return 0; - } - """ - self.check(input, output) - def test_pop_push_array(self): input = """ inst(OP, (values[oparg] -- values[oparg], above)) { @@ -1074,98 +1041,6 @@ def test_array_error_if(self): """ self.run_cases_test(input, output) - def test_cond_effect(self): - input = """ - inst(OP, (aa, input if ((oparg & 1) == 1), cc -- xx, output if (oparg & 2), zz)) { - output = SPAM(oparg, aa, cc, input); - INPUTS_DEAD(); - xx = 0; - zz = 0; - } - """ - output = """ - TARGET(OP) { - #if Py_TAIL_CALL_INTERP - int opcode = OP; - (void)(opcode); - #endif - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(OP); - _PyStackRef aa; - _PyStackRef input = PyStackRef_NULL; - _PyStackRef cc; - _PyStackRef xx; - _PyStackRef output = PyStackRef_NULL; - _PyStackRef zz; - cc = stack_pointer[-1]; - if ((oparg & 1) == 1) { input = stack_pointer[-1 - (((oparg & 1) == 1) ? 1 : 0)]; } - aa = stack_pointer[-2 - (((oparg & 1) == 1) ? 1 : 0)]; - output = SPAM(oparg, aa, cc, input); - xx = 0; - zz = 0; - stack_pointer[-2 - (((oparg & 1) == 1) ? 1 : 0)] = xx; - if (oparg & 2) stack_pointer[-1 - (((oparg & 1) == 1) ? 1 : 0)] = output; - stack_pointer[-1 - (((oparg & 1) == 1) ? 1 : 0) + ((oparg & 2) ? 1 : 0)] = zz; - stack_pointer += -(((oparg & 1) == 1) ? 1 : 0) + ((oparg & 2) ? 1 : 0); - assert(WITHIN_STACK_BOUNDS()); - DISPATCH(); - } - """ - self.run_cases_test(input, output) - - def test_macro_cond_effect(self): - input = """ - op(A, (left, middle, right --)) { - USE(left, middle, right); - INPUTS_DEAD(); - } - op(B, (-- deep, extra if (oparg), res)) { - deep = -1; - res = 0; - extra = 1; - INPUTS_DEAD(); - } - macro(M) = A + B; - """ - output = """ - TARGET(M) { - #if Py_TAIL_CALL_INTERP - int opcode = M; - (void)(opcode); - #endif - frame->instr_ptr = next_instr; - next_instr += 1; - INSTRUCTION_STATS(M); - _PyStackRef left; - _PyStackRef middle; - _PyStackRef right; - _PyStackRef deep; - _PyStackRef extra = PyStackRef_NULL; - _PyStackRef res; - // A - { - right = stack_pointer[-1]; - middle = stack_pointer[-2]; - left = stack_pointer[-3]; - USE(left, middle, right); - } - // B - { - deep = -1; - res = 0; - extra = 1; - } - stack_pointer[-3] = deep; - if (oparg) stack_pointer[-2] = extra; - stack_pointer[-2 + ((oparg) ? 1 : 0)] = res; - stack_pointer += -1 + ((oparg) ? 1 : 0); - assert(WITHIN_STACK_BOUNDS()); - DISPATCH(); - } - """ - self.run_cases_test(input, output) - def test_macro_push_push(self): input = """ op(A, (-- val1)) { From 6b3bbf18edc912cde0a7d1fdef5af910d00f5a3b Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Thu, 20 Mar 2025 12:00:29 +0000 Subject: [PATCH 3/6] Add missing include --- Objects/codeobject.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Objects/codeobject.c b/Objects/codeobject.c index c55ab6b9b28e9e..635da094e37a29 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -9,6 +9,7 @@ #include "pycore_interpframe.h" // FRAME_SPECIALS_SIZE #include "pycore_opcode_metadata.h" // _PyOpcode_Caches #include "pycore_opcode_utils.h" // RESUME_AT_FUNC_START +#include "pycore_optimizer.h" // _Py_ExecutorDetach #include "pycore_pymem.h" // _PyMem_FreeDelayed() #include "pycore_pystate.h" // _PyInterpreterState_GET() #include "pycore_setobject.h" // _PySet_NextEntry() From dfaba30fbf18796172389f480e573b790c17e6df Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Thu, 20 Mar 2025 12:13:38 +0000 Subject: [PATCH 4/6] Add missing #includes --- Modules/_testinternalcapi.c | 1 + Python/optimizer.c | 4 ++++ Python/optimizer_analysis.c | 1 + Python/optimizer_bytecodes.c | 2 +- Python/optimizer_cases.c.h | 2 +- Python/optimizer_symbols.c | 1 + Python/pylifecycle.c | 1 + 7 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index 5f39ed11b57e5c..56e3408652a6a0 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -28,6 +28,7 @@ #include "pycore_instruction_sequence.h" // _PyInstructionSequence_New() #include "pycore_interpframe.h" // _PyFrame_GetFunction() #include "pycore_object.h" // _PyObject_IsFreed() +#include "pycore_optimizer.h" // _Py_Executor_DependsOn #include "pycore_pathconfig.h" // _PyPathConfig_ClearGlobal() #include "pycore_pyerrors.h" // _PyErr_ChainExceptions1() #include "pycore_pylifecycle.h" // _PyInterpreterConfig_InitFromDict() diff --git a/Python/optimizer.c b/Python/optimizer.c index aa60f538bbe5c0..e2fe0f6cff7464 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -6,11 +6,15 @@ #include "pycore_interp.h" #include "pycore_backoff.h" #include "pycore_bitutils.h" // _Py_popcount32() +#include "pycore_code.h" // _Py_GetBaseCodeUnit +#include "pycore_interpframe.h" #include "pycore_object.h" // _PyObject_GC_UNTRACK() #include "pycore_opcode_metadata.h" // _PyOpcode_OpName[] #include "pycore_opcode_utils.h" // MAX_REAL_OPCODE #include "pycore_optimizer.h" // _Py_uop_analyze_and_optimize() #include "pycore_pystate.h" // _PyInterpreterState_GET() +#include "pycore_tuple.h" // _PyTuple_FromArraySteal +#include "pycore_unicodeobject.h" // _PyUnicode_FromASCII #include "pycore_uop_ids.h" #include "pycore_jit.h" #include diff --git a/Python/optimizer_analysis.c b/Python/optimizer_analysis.c index 67bf8d11b3f9ac..017a2eeca0741e 100644 --- a/Python/optimizer_analysis.c +++ b/Python/optimizer_analysis.c @@ -21,6 +21,7 @@ #include "pycore_uop_metadata.h" #include "pycore_dict.h" #include "pycore_long.h" +#include "pycore_interpframe.h" // _PyFrame_GetCode #include "pycore_optimizer.h" #include "pycore_object.h" #include "pycore_dict.h" diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index 486751847a43cf..cfa0a733cda21d 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -552,7 +552,7 @@ dummy_func(void) { null[0] = sym_new_null(ctx); } else { - REPLACE_OP(this_instr, _NOP); + REPLACE_OP(this_instr, _NOP, 0, 0); } } diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index f8962b6054f94e..fc70ee31a80002 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -928,7 +928,7 @@ null[0] = sym_new_null(ctx); } else { - REPLACE_OP(this_instr, _NOP); + REPLACE_OP(this_instr, _NOP, 0, 0); } stack_pointer += (oparg & 1); assert(WITHIN_STACK_BOUNDS()); diff --git a/Python/optimizer_symbols.c b/Python/optimizer_symbols.c index 8445546ffdf716..c50f98cb99b396 100644 --- a/Python/optimizer_symbols.c +++ b/Python/optimizer_symbols.c @@ -6,6 +6,7 @@ #include "pycore_frame.h" #include "pycore_long.h" #include "pycore_optimizer.h" +#include "pycore_stats.h" #include "pycore_tuple.h" // _PyTuple_FromArray() #include diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 8fe58c320a33b2..ed21fce335c99d 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -18,6 +18,7 @@ #include "pycore_long.h" // _PyLong_InitTypes() #include "pycore_object.h" // _PyDebug_PrintTotalRefs() #include "pycore_obmalloc.h" // _PyMem_init_obmalloc() +#include "pycore_optimizer.h" // _Py_Executors_InvalidateAll #include "pycore_pathconfig.h" // _PyPathConfig_UpdateGlobal() #include "pycore_pyerrors.h" // _PyErr_Occurred() #include "pycore_pylifecycle.h" // _PyErr_Print() From 7add005ee61e4764f7f200cc550c921fd0f2ae0e Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Thu, 20 Mar 2025 14:00:13 +0000 Subject: [PATCH 5/6] Add jit includes --- Include/internal/pycore_jit.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Include/internal/pycore_jit.h b/Include/internal/pycore_jit.h index 4d6cc35a7a3de7..8a88cbf607ba4b 100644 --- a/Include/internal/pycore_jit.h +++ b/Include/internal/pycore_jit.h @@ -5,6 +5,10 @@ extern "C" { #endif +#include "pycore_interp.h" +#include "pycore_optimizer.h" +#include "pycore_stackref.h" + #ifndef Py_BUILD_CORE # error "this header requires Py_BUILD_CORE define" #endif From b6ab21ebff428ad3ce76b0af967d6787ea91406e Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Thu, 20 Mar 2025 14:32:50 +0000 Subject: [PATCH 6/6] Add more JIT includes --- Python/jit.c | 7 +++++++ Tools/jit/template.c | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/Python/jit.c b/Python/jit.c index 95b5a1b52b8b65..1f4873ee63a88f 100644 --- a/Python/jit.c +++ b/Python/jit.c @@ -8,7 +8,11 @@ #include "pycore_ceval.h" #include "pycore_critical_section.h" #include "pycore_dict.h" +#include "pycore_floatobject.h" +#include "pycore_frame.h" +#include "pycore_interpframe.h" #include "pycore_intrinsics.h" +#include "pycore_list.h" #include "pycore_long.h" #include "pycore_opcode_metadata.h" #include "pycore_opcode_utils.h" @@ -16,6 +20,9 @@ #include "pycore_pyerrors.h" #include "pycore_setobject.h" #include "pycore_sliceobject.h" +#include "pycore_tuple.h" +#include "pycore_unicodeobject.h" + #include "pycore_jit.h" // Memory management stuff: //////////////////////////////////////////////////// diff --git a/Tools/jit/template.c b/Tools/jit/template.c index 0b7d077d78ce7d..adc08f3cc5f2a5 100644 --- a/Tools/jit/template.c +++ b/Tools/jit/template.c @@ -4,10 +4,16 @@ #include "pycore_call.h" #include "pycore_ceval.h" #include "pycore_cell.h" +#include "pycore_code.h" #include "pycore_dict.h" +#include "pycore_floatobject.h" #include "pycore_emscripten_signal.h" +#include "pycore_frame.h" +#include "pycore_genobject.h" +#include "pycore_interpframe.h" #include "pycore_intrinsics.h" #include "pycore_jit.h" +#include "pycore_list.h" #include "pycore_long.h" #include "pycore_opcode_metadata.h" #include "pycore_opcode_utils.h" @@ -18,6 +24,8 @@ #include "pycore_sliceobject.h" #include "pycore_descrobject.h" #include "pycore_stackref.h" +#include "pycore_tuple.h" +#include "pycore_unicodeobject.h" #include "ceval_macros.h"