From d2573b679009ee8126fef3d0e3699112a4cfc7a3 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 8 Dec 2022 15:27:21 -0800 Subject: [PATCH 1/2] Change all super() to macro() using op(JOIN) --- Python/bytecodes.c | 26 ++++++++---- Python/generated_cases.c.h | 83 +++++++++++++++++++++++++++++--------- 2 files changed, 82 insertions(+), 27 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 7a1bfdcc9e26e8..265430134c63ec 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -86,12 +86,14 @@ static PyObject *exit_func, *lasti, *val, *retval, *obj, *iter; static size_t jump; // Dummy variables for cache effects static _Py_CODEUNIT when_to_jump_mask, invert, counter, index, hint; +static _Py_CODEUNIT word; static uint32_t type_version; // Dummy opcode names for 'op' opcodes #define _COMPARE_OP_FLOAT 1003 #define _COMPARE_OP_INT 1004 #define _COMPARE_OP_STR 1005 #define _JUMP_IF 1006 +#define JOIN 0 static PyObject * dummy_func( @@ -115,6 +117,14 @@ dummy_func( switch (opcode) { // BEGIN BYTECODES // + + op(JOIN, (word/1 --)) { + #ifndef NDEBUG + opcode = _Py_OPCODE(word); + #endif + oparg = _Py_OPARG(word); + } + inst(NOP, (--)) { } @@ -154,11 +164,11 @@ dummy_func( SETLOCAL(oparg, value); } - super(LOAD_FAST__LOAD_FAST) = LOAD_FAST + LOAD_FAST; - super(LOAD_FAST__LOAD_CONST) = LOAD_FAST + LOAD_CONST; - super(STORE_FAST__LOAD_FAST) = STORE_FAST + LOAD_FAST; - super(STORE_FAST__STORE_FAST) = STORE_FAST + STORE_FAST; - super(LOAD_CONST__LOAD_FAST) = LOAD_CONST + LOAD_FAST; + macro(LOAD_FAST__LOAD_FAST) = LOAD_FAST + JOIN + LOAD_FAST; + macro(LOAD_FAST__LOAD_CONST) = LOAD_FAST + JOIN + LOAD_CONST; + macro(STORE_FAST__LOAD_FAST) = STORE_FAST + JOIN + LOAD_FAST; + macro(STORE_FAST__STORE_FAST) = STORE_FAST + JOIN + STORE_FAST; + macro(LOAD_CONST__LOAD_FAST) = LOAD_CONST + JOIN + LOAD_FAST; inst(POP_TOP, (value --)) { Py_DECREF(value); @@ -2043,7 +2053,7 @@ dummy_func( } } // We're praying that the compiler optimizes the flags manipuations. - super(COMPARE_OP_FLOAT_JUMP) = _COMPARE_OP_FLOAT + _JUMP_IF; + macro(COMPARE_OP_FLOAT_JUMP) = _COMPARE_OP_FLOAT + JOIN + _JUMP_IF; // Similar to COMPARE_OP_FLOAT op(_COMPARE_OP_INT, (unused/1, when_to_jump_mask/1, left, right -- jump: size_t)) { @@ -2063,7 +2073,7 @@ dummy_func( _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); jump = sign_ish & when_to_jump_mask; } - super(COMPARE_OP_INT_JUMP) = _COMPARE_OP_INT + _JUMP_IF; + macro(COMPARE_OP_INT_JUMP) = _COMPARE_OP_INT + JOIN + _JUMP_IF; // Similar to COMPARE_OP_FLOAT, but for ==, != only op(_COMPARE_OP_STR, (unused/1, invert/1, left, right -- jump: size_t)) { @@ -2080,7 +2090,7 @@ dummy_func( assert(invert == 0 || invert == 1); jump = res ^ invert; } - super(COMPARE_OP_STR_JUMP) = _COMPARE_OP_STR + _JUMP_IF; + macro(COMPARE_OP_STR_JUMP) = _COMPARE_OP_STR + JOIN + _JUMP_IF; // stack effect: (__0 -- ) inst(IS_OP) { diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 76eb6085ec57c1..cdbfbbda141fc1 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -73,8 +73,13 @@ Py_INCREF(value); _tmp_2 = value; } - NEXTOPARG(); - JUMPBY(1); + { + uint16_t word = read_u16(next_instr + 0); + #ifndef NDEBUG + opcode = _Py_OPCODE(word); + #endif + oparg = _Py_OPARG(word); + } { PyObject *value; value = GETLOCAL(oparg); @@ -82,6 +87,7 @@ Py_INCREF(value); _tmp_1 = value; } + JUMPBY(1); STACK_GROW(2); POKE(1, _tmp_1); POKE(2, _tmp_2); @@ -98,14 +104,20 @@ Py_INCREF(value); _tmp_2 = value; } - NEXTOPARG(); - JUMPBY(1); + { + uint16_t word = read_u16(next_instr + 0); + #ifndef NDEBUG + opcode = _Py_OPCODE(word); + #endif + oparg = _Py_OPARG(word); + } { PyObject *value; value = GETITEM(consts, oparg); Py_INCREF(value); _tmp_1 = value; } + JUMPBY(1); STACK_GROW(2); POKE(1, _tmp_1); POKE(2, _tmp_2); @@ -118,8 +130,13 @@ PyObject *value = _tmp_1; SETLOCAL(oparg, value); } - NEXTOPARG(); - JUMPBY(1); + { + uint16_t word = read_u16(next_instr + 0); + #ifndef NDEBUG + opcode = _Py_OPCODE(word); + #endif + oparg = _Py_OPARG(word); + } { PyObject *value; value = GETLOCAL(oparg); @@ -127,6 +144,7 @@ Py_INCREF(value); _tmp_1 = value; } + JUMPBY(1); POKE(1, _tmp_1); DISPATCH(); } @@ -138,12 +156,18 @@ PyObject *value = _tmp_1; SETLOCAL(oparg, value); } - NEXTOPARG(); - JUMPBY(1); + { + uint16_t word = read_u16(next_instr + 0); + #ifndef NDEBUG + opcode = _Py_OPCODE(word); + #endif + oparg = _Py_OPARG(word); + } { PyObject *value = _tmp_2; SETLOCAL(oparg, value); } + JUMPBY(1); STACK_SHRINK(2); DISPATCH(); } @@ -157,8 +181,13 @@ Py_INCREF(value); _tmp_2 = value; } - NEXTOPARG(); - JUMPBY(1); + { + uint16_t word = read_u16(next_instr + 0); + #ifndef NDEBUG + opcode = _Py_OPCODE(word); + #endif + oparg = _Py_OPARG(word); + } { PyObject *value; value = GETLOCAL(oparg); @@ -166,6 +195,7 @@ Py_INCREF(value); _tmp_1 = value; } + JUMPBY(1); STACK_GROW(2); POKE(1, _tmp_1); POKE(2, _tmp_2); @@ -2226,9 +2256,13 @@ jump = sign_ish & when_to_jump_mask; _tmp_2 = (PyObject *)jump; } - JUMPBY(2); - NEXTOPARG(); - JUMPBY(1); + { + uint16_t word = read_u16(next_instr + 2); + #ifndef NDEBUG + opcode = _Py_OPCODE(word); + #endif + oparg = _Py_OPARG(word); + } { size_t jump = (size_t)_tmp_2; assert(opcode == POP_JUMP_IF_FALSE || opcode == POP_JUMP_IF_TRUE); @@ -2236,6 +2270,7 @@ JUMPBY(oparg); } } + JUMPBY(3); STACK_SHRINK(2); DISPATCH(); } @@ -2265,9 +2300,13 @@ jump = sign_ish & when_to_jump_mask; _tmp_2 = (PyObject *)jump; } - JUMPBY(2); - NEXTOPARG(); - JUMPBY(1); + { + uint16_t word = read_u16(next_instr + 2); + #ifndef NDEBUG + opcode = _Py_OPCODE(word); + #endif + oparg = _Py_OPARG(word); + } { size_t jump = (size_t)_tmp_2; assert(opcode == POP_JUMP_IF_FALSE || opcode == POP_JUMP_IF_TRUE); @@ -2275,6 +2314,7 @@ JUMPBY(oparg); } } + JUMPBY(3); STACK_SHRINK(2); DISPATCH(); } @@ -2301,9 +2341,13 @@ jump = res ^ invert; _tmp_2 = (PyObject *)jump; } - JUMPBY(2); - NEXTOPARG(); - JUMPBY(1); + { + uint16_t word = read_u16(next_instr + 2); + #ifndef NDEBUG + opcode = _Py_OPCODE(word); + #endif + oparg = _Py_OPARG(word); + } { size_t jump = (size_t)_tmp_2; assert(opcode == POP_JUMP_IF_FALSE || opcode == POP_JUMP_IF_TRUE); @@ -2311,6 +2355,7 @@ JUMPBY(oparg); } } + JUMPBY(3); STACK_SHRINK(2); DISPATCH(); } From eadca5182a47d30324e6a8da6471a061165c665d Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 8 Dec 2022 15:34:19 -0800 Subject: [PATCH 2/2] Rip out parser and generator support for super() --- Tools/cases_generator/generate_cases.py | 84 ++++--------------------- Tools/cases_generator/parser.py | 22 +------ 2 files changed, 13 insertions(+), 93 deletions(-) diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index 5930c797b8a4d3..fffeab3dba6f60 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -260,27 +260,13 @@ def write_body(self, out: Formatter, cache_adjust: int) -> None: @dataclasses.dataclass -class SuperOrMacroInstruction: - """Common fields for super- and macro instructions.""" +class MacroInstruction: + """A macro instruction.""" name: str stack: list[StackEffect] initial_sp: int final_sp: int - - -@dataclasses.dataclass -class SuperInstruction(SuperOrMacroInstruction): - """A super-instruction.""" - - super: parser.Super - parts: list[Component] - - -@dataclasses.dataclass -class MacroInstruction(SuperOrMacroInstruction): - """A macro instruction.""" - macro: parser.Macro parts: list[Component | parser.CacheEffect] @@ -311,10 +297,8 @@ def error(self, msg: str, node: parser.Node) -> None: print(f"{self.filename}:{lineno}: {msg}", file=sys.stderr) self.errors += 1 - everything: list[parser.InstDef | parser.Super | parser.Macro] + everything: list[parser.InstDef | parser.Macro] instrs: dict[str, Instruction] # Includes ops - supers: dict[str, parser.Super] - super_instrs: dict[str, SuperInstruction] macros: dict[str, parser.Macro] macro_instrs: dict[str, MacroInstruction] families: dict[str, parser.Family] @@ -347,7 +331,6 @@ def parse(self) -> None: psr.setpos(start) self.everything = [] self.instrs = {} - self.supers = {} self.macros = {} self.families = {} while thing := psr.definition(): @@ -355,9 +338,6 @@ def parse(self) -> None: case parser.InstDef(name=name): self.instrs[name] = Instruction(thing) self.everything.append(thing) - case parser.Super(name): - self.supers[name] = thing - self.everything.append(thing) case parser.Macro(name): self.macros[name] = thing self.everything.append(thing) @@ -370,7 +350,7 @@ def parse(self) -> None: print( f"Read {len(self.instrs)} instructions/ops, " - f"{len(self.supers)} supers, {len(self.macros)} macros, " + f"{len(self.macros)} macros, " f"and {len(self.families)} families from {self.filename}", file=sys.stderr, ) @@ -383,7 +363,7 @@ def analyze(self) -> None: self.find_predictions() self.map_families() self.check_families() - self.analyze_supers_and_macros() + self.analyze_macros() def find_predictions(self) -> None: """Find the instructions that need PREDICTED() labels.""" @@ -449,26 +429,12 @@ def check_families(self) -> None: family, ) - def analyze_supers_and_macros(self) -> None: - """Analyze each super- and macro instruction.""" - self.super_instrs = {} + def analyze_macros(self) -> None: + """Analyze each macro instruction.""" self.macro_instrs = {} - for name, super in self.supers.items(): - self.super_instrs[name] = self.analyze_super(super) for name, macro in self.macros.items(): self.macro_instrs[name] = self.analyze_macro(macro) - def analyze_super(self, super: parser.Super) -> SuperInstruction: - components = self.check_super_components(super) - stack, initial_sp = self.stack_analysis(components) - sp = initial_sp - parts: list[Component] = [] - for instr in components: - part, sp = self.analyze_instruction(instr, stack, sp) - parts.append(part) - final_sp = sp - return SuperInstruction(super.name, stack, initial_sp, final_sp, super, parts) - def analyze_macro(self, macro: parser.Macro) -> MacroInstruction: components = self.check_macro_components(macro) stack, initial_sp = self.stack_analysis(components) @@ -499,15 +465,6 @@ def analyze_instruction( sp += 1 return Component(instr, input_mapping, output_mapping), sp - def check_super_components(self, super: parser.Super) -> list[Instruction]: - components: list[Instruction] = [] - for op in super.ops: - if op.name not in self.instrs: - self.error(f"Unknown instruction {op.name!r}", super) - else: - components.append(self.instrs[op.name]) - return components - def check_macro_components( self, macro: parser.Macro ) -> list[InstructionOrCacheEffect]: @@ -527,7 +484,7 @@ def check_macro_components( def stack_analysis( self, components: typing.Iterable[InstructionOrCacheEffect] ) -> tuple[list[StackEffect], int]: - """Analyze a super-instruction or macro. + """Analyze a macro instruction. Print an error if there's a cache effect (which we don't support yet). @@ -567,7 +524,6 @@ def write_instructions(self) -> None: # Write and count instructions of all kinds n_instrs = 0 - n_supers = 0 n_macros = 0 for thing in self.everything: match thing: @@ -575,9 +531,6 @@ def write_instructions(self) -> None: if thing.kind == "inst": n_instrs += 1 self.write_instr(self.instrs[thing.name]) - case parser.Super(): - n_supers += 1 - self.write_super(self.super_instrs[thing.name]) case parser.Macro(): n_macros += 1 self.write_macro(self.macro_instrs[thing.name]) @@ -585,7 +538,7 @@ def write_instructions(self) -> None: typing.assert_never(thing) print( - f"Wrote {n_instrs} instructions, {n_supers} supers, " + f"Wrote {n_instrs} instructions, " f"and {n_macros} macros to {self.output_filename}", file=sys.stderr, ) @@ -602,22 +555,9 @@ def write_instr(self, instr: Instruction) -> None: self.out.emit(f"PREDICT({prediction});") self.out.emit(f"DISPATCH();") - def write_super(self, sup: SuperInstruction) -> None: - """Write code for a super-instruction.""" - with self.wrap_super_or_macro(sup): - first = True - for comp in sup.parts: - if not first: - self.out.emit("NEXTOPARG();") - self.out.emit("JUMPBY(1);") - first = False - comp.write_body(self.out, 0) - if comp.instr.cache_offset: - self.out.emit(f"JUMPBY({comp.instr.cache_offset});") - def write_macro(self, mac: MacroInstruction) -> None: """Write code for a macro instruction.""" - with self.wrap_super_or_macro(mac): + with self.wrap_macro(mac): cache_adjust = 0 for part in mac.parts: match part: @@ -631,8 +571,8 @@ def write_macro(self, mac: MacroInstruction) -> None: self.out.emit(f"JUMPBY({cache_adjust});") @contextlib.contextmanager - def wrap_super_or_macro(self, up: SuperOrMacroInstruction): - """Shared boilerplate for super- and macro instructions.""" + def wrap_macro(self, up: MacroInstruction): + """Boilerplate for macro instructions.""" # TODO: Somewhere (where?) make it so that if one instruction # has an output that is input to another, and the variable names # and types match and don't conflict with other instructions, diff --git a/Tools/cases_generator/parser.py b/Tools/cases_generator/parser.py index d802c733dfd10c..cf5f29f28c592d 100644 --- a/Tools/cases_generator/parser.py +++ b/Tools/cases_generator/parser.py @@ -99,12 +99,6 @@ class InstDef(Node): block: Block -@dataclass -class Super(Node): - name: str - ops: list[OpName] - - @dataclass class Macro(Node): name: str @@ -120,11 +114,9 @@ class Family(Node): class Parser(PLexer): @contextual - def definition(self) -> InstDef | Super | Macro | Family | None: + def definition(self) -> InstDef | Macro | Family | None: if inst := self.inst_def(): return inst - if super := self.super_def(): - return super if macro := self.macro_def(): return macro if family := self.family_def(): @@ -224,18 +216,6 @@ def stack_effect(self) -> StackEffect | None: type = self.require(lx.IDENTIFIER).text return StackEffect(tkn.text, type) - @contextual - def super_def(self) -> Super | None: - if (tkn := self.expect(lx.IDENTIFIER)) and tkn.text == "super": - if self.expect(lx.LPAREN): - if tkn := self.expect(lx.IDENTIFIER): - if self.expect(lx.RPAREN): - if self.expect(lx.EQUALS): - if ops := self.ops(): - self.require(lx.SEMI) - res = Super(tkn.text, ops) - return res - def ops(self) -> list[OpName] | None: if op := self.op(): ops = [op]