diff options
Diffstat (limited to 'Tools')
-rw-r--r-- | Tools/cases_generator/analyzer.py | 78 | ||||
-rw-r--r-- | Tools/cases_generator/generators_common.py | 7 | ||||
-rw-r--r-- | Tools/cases_generator/lexer.py | 1 | ||||
-rw-r--r-- | Tools/cases_generator/opcode_metadata_generator.py | 1 | ||||
-rw-r--r-- | Tools/cases_generator/optimizer_generator.py | 10 | ||||
-rw-r--r-- | Tools/cases_generator/parsing.py | 14 | ||||
-rw-r--r-- | Tools/cases_generator/stack.py | 31 | ||||
-rw-r--r-- | Tools/cases_generator/tier1_generator.py | 5 | ||||
-rw-r--r-- | Tools/cases_generator/tier2_generator.py | 12 | ||||
-rw-r--r-- | Tools/scripts/summarize_stats.py | 2 |
10 files changed, 144 insertions, 17 deletions
diff --git a/Tools/cases_generator/analyzer.py b/Tools/cases_generator/analyzer.py index e8ee314..3daae46 100644 --- a/Tools/cases_generator/analyzer.py +++ b/Tools/cases_generator/analyzer.py @@ -25,6 +25,7 @@ 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 @@ -123,14 +124,16 @@ 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} {self.peek}" + return f"{type}{self.name}{size}{cond} {self.peek}" def is_array(self) -> bool: return self.size != "" @@ -312,19 +315,25 @@ def override_error( ) -def convert_stack_item(item: parser.StackEffect) -> StackItem: - return StackItem(item.name, item.type, item.size) +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 analyze_stack( - op: parser.InstDef | parser.Pseudo) -> StackEffect: + op: parser.InstDef | parser.Pseudo, replace_op_arg_1: str | None = None +) -> StackEffect: inputs: list[StackItem] = [ - convert_stack_item(i) + convert_stack_item(i, replace_op_arg_1) for i in op.inputs if isinstance(i, parser.StackEffect) ] outputs: list[StackItem] = [ - convert_stack_item(i) for i in op.outputs + convert_stack_item(i, replace_op_arg_1) for i in op.outputs ] # Mark variables with matching names at the base of the stack as "peek" modified = False @@ -746,6 +755,40 @@ 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 = ( @@ -819,6 +862,29 @@ 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 9edf3d4..f1f166a 100644 --- a/Tools/cases_generator/generators_common.py +++ b/Tools/cases_generator/generators_common.py @@ -247,7 +247,7 @@ class Emitter: if var.name == "null": continue close = "PyStackRef_CLOSE" - if "null" in var.name: + if "null" in var.name or var.condition and var.condition != "1": close = "PyStackRef_XCLOSE" if var.size: if var.size == "1": @@ -256,6 +256,9 @@ 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: @@ -683,6 +686,8 @@ 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 303a1c0..bee2a18 100644 --- a/Tools/cases_generator/lexer.py +++ b/Tools/cases_generator/lexer.py @@ -222,6 +222,7 @@ 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 9ef49bd..453db69 100644 --- a/Tools/cases_generator/opcode_metadata_generator.py +++ b/Tools/cases_generator/opcode_metadata_generator.py @@ -51,6 +51,7 @@ 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 7ebeff7..5cfec4b 100644 --- a/Tools/cases_generator/optimizer_generator.py +++ b/Tools/cases_generator/optimizer_generator.py @@ -48,13 +48,19 @@ 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) - out.emit(f"{type_name(var)}{var.name};\n") + if var.condition: + out.emit(f"{type_name(var)}{var.name} = NULL;\n") + else: + 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) - out.emit(f"{type_name(var)}{var.name};\n") + if var.condition: + out.emit(f"{type_name(var)}{var.name} = NULL;\n") + else: + 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 b50bb62..41b36b6 100644 --- a/Tools/cases_generator/parsing.py +++ b/Tools/cases_generator/parsing.py @@ -77,11 +77,12 @@ 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.size] + items = [self.name, self.type, self.cond, self.size] while items and items[-1] == "": del items[-1] return f"StackEffect({', '.join(repr(item) for item in items)})" @@ -277,15 +278,22 @@ 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: + if type_text or cond_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, size_text) + return StackEffect(tkn.text, type_text, cond_text, size_text) return None @contextual diff --git a/Tools/cases_generator/stack.py b/Tools/cases_generator/stack.py index 4e9a312..5121837 100644 --- a/Tools/cases_generator/stack.py +++ b/Tools/cases_generator/stack.py @@ -24,7 +24,17 @@ def maybe_parenthesize(sym: str) -> str: def var_size(var: StackItem) -> str: - if var.size: + 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: return var.size else: return "1" @@ -80,6 +90,10 @@ 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() @@ -260,7 +274,16 @@ 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};\n" + 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" return assign, Local.from_memory(var) def push(self, var: Local) -> None: @@ -280,6 +303,10 @@ 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 f4409da..441bb3f 100644 --- a/Tools/cases_generator/tier1_generator.py +++ b/Tools/cases_generator/tier1_generator.py @@ -37,7 +37,10 @@ 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 "" - out.emit(f"{type}{space}{var.name};\n") + if var.condition: + out.emit(f"{type}{space}{var.name} = {null};\n") + else: + 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 abd9b8d..dd16a1a 100644 --- a/Tools/cases_generator/tier2_generator.py +++ b/Tools/cases_generator/tier2_generator.py @@ -39,7 +39,14 @@ def declare_variable( required.remove(var.name) type, null = type_and_null(var) space = " " if type[-1].isalnum() else "" - out.emit(f"{type}{space}{var.name};\n") + 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") def declare_variables(uop: Uop, out: CWriter) -> None: @@ -208,6 +215,9 @@ 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 1747893..bc7ccfe 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" or opcode == "LOAD_SUPER_METHOD": + if opcode == "LOAD_SUPER_ATTR": opcode = "SUPER" elif opcode.endswith("ATTR"): opcode = "ATTR" |