aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Tools
diff options
context:
space:
mode:
authorMark Shannon <mark@hotpy.org>2025-01-20 17:09:23 +0000
committerGitHub <noreply@github.com>2025-01-20 17:09:23 +0000
commitab61d3f4303d14a413bc9ae6557c730ffdf7579e (patch)
treeec35e41ce467f4cb281208970cf453a680d82aed /Tools
parent0a6412f9cc9e694e76299cfbd73ec969b7d47af6 (diff)
downloadcpython-ab61d3f4303d14a413bc9ae6557c730ffdf7579e.tar.gz
cpython-ab61d3f4303d14a413bc9ae6557c730ffdf7579e.zip
GH-128914: Remove conditional stack effects from `bytecodes.c` and the code generators (GH-128918)
Diffstat (limited to 'Tools')
-rw-r--r--Tools/cases_generator/analyzer.py78
-rw-r--r--Tools/cases_generator/generators_common.py7
-rw-r--r--Tools/cases_generator/lexer.py1
-rw-r--r--Tools/cases_generator/opcode_metadata_generator.py1
-rw-r--r--Tools/cases_generator/optimizer_generator.py10
-rw-r--r--Tools/cases_generator/parsing.py14
-rw-r--r--Tools/cases_generator/stack.py31
-rw-r--r--Tools/cases_generator/tier1_generator.py5
-rw-r--r--Tools/cases_generator/tier2_generator.py12
-rw-r--r--Tools/scripts/summarize_stats.py2
10 files changed, 17 insertions, 144 deletions
diff --git a/Tools/cases_generator/analyzer.py b/Tools/cases_generator/analyzer.py
index 4013b503502..1f15c3bb9c8 100644
--- a/Tools/cases_generator/analyzer.py
+++ b/Tools/cases_generator/analyzer.py
@@ -25,7 +25,6 @@ class Properties:
side_exit: bool
pure: bool
tier: int | None = None
- oparg_and_1: bool = False
const_oparg: int = -1
needs_prev: bool = False
no_save_ip: bool = False
@@ -124,16 +123,14 @@ class Flush:
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 != ""
@@ -315,25 +312,19 @@ 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)
+def convert_stack_item(item: parser.StackEffect) -> StackItem:
+ return StackItem(item.name, item.type, item.size)
def analyze_stack(
- op: parser.InstDef | parser.Pseudo, replace_op_arg_1: str | None = None
-) -> StackEffect:
+ op: parser.InstDef | parser.Pseudo) -> StackEffect:
inputs: list[StackItem] = [
- convert_stack_item(i, replace_op_arg_1)
+ convert_stack_item(i)
for i in op.inputs
if isinstance(i, parser.StackEffect)
]
outputs: list[StackItem] = [
- convert_stack_item(i, replace_op_arg_1) for i in op.outputs
+ convert_stack_item(i) for i in op.outputs
]
# Mark variables with matching names at the base of the stack as "peek"
modified = False
@@ -756,40 +747,6 @@ def always_exits(op: parser.InstDef) -> bool:
return True
return False
-
-def stack_effect_only_peeks(instr: parser.InstDef) -> bool:
- stack_inputs = [s for s in instr.inputs if not isinstance(s, parser.CacheEffect)]
- if len(stack_inputs) != len(instr.outputs):
- 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.InstDef) -> Properties:
escaping_calls = find_escaping_api_calls(op)
has_free = (
@@ -863,29 +820,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 f54afbb880d..c441569b7e7 100644
--- a/Tools/cases_generator/generators_common.py
+++ b/Tools/cases_generator/generators_common.py
@@ -246,7 +246,7 @@ class Emitter:
if var.name == "null":
continue
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":
@@ -255,9 +255,6 @@ class Emitter:
self.out.emit(f"for (int _i = {var.size}; --_i >= 0;) {{\n")
self.out.emit(f"{close}({var.name}[_i]);\n")
self.out.emit("}\n")
- elif var.condition:
- if var.condition != "0":
- self.out.emit(f"{close}({var.name});\n")
else:
self.out.emit(f"{close}({var.name});\n")
for input in storage.inputs:
@@ -668,8 +665,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 bee2a185745..303a1c02705 100644
--- a/Tools/cases_generator/lexer.py
+++ b/Tools/cases_generator/lexer.py
@@ -222,7 +222,6 @@ annotations = {
"register",
"replaced",
"pure",
- "split",
"replicate",
"tier1",
"tier2",
diff --git a/Tools/cases_generator/opcode_metadata_generator.py b/Tools/cases_generator/opcode_metadata_generator.py
index 453db6905d6..9ef49bdc41d 100644
--- a/Tools/cases_generator/opcode_metadata_generator.py
+++ b/Tools/cases_generator/opcode_metadata_generator.py
@@ -51,7 +51,6 @@ FLAGS = [
"EXIT",
"PURE",
"PASSTHROUGH",
- "OPARG_AND_1",
"ERROR_NO_POP",
"NO_SAVE_IP",
]
diff --git a/Tools/cases_generator/optimizer_generator.py b/Tools/cases_generator/optimizer_generator.py
index 5cfec4bfecb..7ebeff716d2 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 41b36b6a546..b50bb627b6c 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)})"
@@ -278,22 +277,15 @@ class Parser(PLexer):
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 5121837ed83..4e9a312ce34 100644
--- a/Tools/cases_generator/stack.py
+++ b/Tools/cases_generator/stack.py
@@ -24,17 +24,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"
@@ -90,10 +80,6 @@ class Local:
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()
@@ -274,16 +260,7 @@ class Stack:
self.defined.add(var.name)
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"{var.name} = {cast}{indirect}stack_pointer[{self.base_offset.to_c()}]{bits};\n"
return assign, Local.from_memory(var)
def push(self, var: Local) -> None:
@@ -303,10 +280,6 @@ class Stack:
) -> 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:
diff --git a/Tools/cases_generator/tier1_generator.py b/Tools/cases_generator/tier1_generator.py
index 40562da99b2..95876c387bd 100644
--- a/Tools/cases_generator/tier1_generator.py
+++ b/Tools/cases_generator/tier1_generator.py
@@ -37,10 +37,7 @@ FOOTER = "#undef TIER_ONE\n"
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 dd16a1a7eb2..abd9b8d7924 100644
--- a/Tools/cases_generator/tier2_generator.py
+++ b/Tools/cases_generator/tier2_generator.py
@@ -39,14 +39,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:
@@ -215,9 +208,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()
diff --git a/Tools/scripts/summarize_stats.py b/Tools/scripts/summarize_stats.py
index bc7ccfe33e7..17478933eb6 100644
--- a/Tools/scripts/summarize_stats.py
+++ b/Tools/scripts/summarize_stats.py
@@ -284,7 +284,7 @@ class OpcodeStats:
def kind_to_text(kind: int, opcode: str):
if kind <= 8:
return pretty(self._defines[kind][0])
- if opcode == "LOAD_SUPER_ATTR":
+ if opcode == "LOAD_SUPER_ATTR" or opcode == "LOAD_SUPER_METHOD":
opcode = "SUPER"
elif opcode.endswith("ATTR"):
opcode = "ATTR"