From f4e138241e9383e2fc36b0347bd1444298eda87e Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 28 Feb 2024 14:52:10 -0800 Subject: [PATCH 1/6] Clean up imports --- Tools/cases_generator/optimizer_generator.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Tools/cases_generator/optimizer_generator.py b/Tools/cases_generator/optimizer_generator.py index d3ce4c8a25f5b8..fca42b51fbd689 100644 --- a/Tools/cases_generator/optimizer_generator.py +++ b/Tools/cases_generator/optimizer_generator.py @@ -4,16 +4,12 @@ """ import argparse -import os.path -import sys from analyzer import ( Analysis, Instruction, Uop, - Part, analyze_files, - Skip, StackItem, analysis_error, ) From 23d8a15af6faf39bf818ffa38300db91400db648 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 28 Feb 2024 16:10:54 -0800 Subject: [PATCH 2/6] Add bottom checks, first approximation --- Python/optimizer_analysis.c | 10 ++ Python/optimizer_cases.c.h | 143 ++++++++++++++++++- Tools/cases_generator/optimizer_generator.py | 2 +- Tools/cases_generator/stack.py | 12 +- 4 files changed, 160 insertions(+), 7 deletions(-) diff --git a/Python/optimizer_analysis.c b/Python/optimizer_analysis.c index 2a7ef4ec919eeb..e4302370a636fa 100644 --- a/Python/optimizer_analysis.c +++ b/Python/optimizer_analysis.c @@ -339,6 +339,7 @@ optimize_uops( _PyUOpName(opcode), oparg); switch (opcode) { + #include "optimizer_cases.c.h" default: @@ -363,6 +364,15 @@ optimize_uops( DPRINTF(1, "Encountered error in abstract interpreter\n"); _Py_uop_abstractcontext_fini(ctx); return 0; + +hit_bottom: + // Attempted to push a "bottom" (contradition) symbol onto the stack. + // This means that the abstract interpreter has hit unreachable code. + // We *could* generate an _EXIT_TRACE or _FATAL_ERROR here, but it's + // simpler to just admit failure and not create the executor. + DPRINTF(1, "Hit bottom in abstract interpreter\n"); + _Py_uop_abstractcontext_fini(ctx); + return 0; } diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index b38c03bed4b4d0..bb943048455de5 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -20,6 +20,9 @@ if (sym_is_null(value)) { goto out_of_space; } + if (sym_is_bottom(value)) { + goto hit_bottom; + } stack_pointer[0] = value; stack_pointer += 1; break; @@ -28,6 +31,9 @@ case _LOAD_FAST: { _Py_UopsSymbol *value; value = GETLOCAL(oparg); + if (sym_is_bottom(value)) { + goto hit_bottom; + } stack_pointer[0] = value; stack_pointer += 1; break; @@ -39,6 +45,9 @@ _Py_UopsSymbol *temp; OUT_OF_SPACE_IF_NULL(temp = sym_new_null(ctx)); GETLOCAL(oparg) = temp; + if (sym_is_bottom(value)) { + goto hit_bottom; + } stack_pointer[0] = value; stack_pointer += 1; break; @@ -49,6 +58,9 @@ // There should be no LOAD_CONST. It should be all // replaced by peephole_opt. Py_UNREACHABLE(); + if (sym_is_bottom(value)) { + goto hit_bottom; + } stack_pointer[0] = value; stack_pointer += 1; break; @@ -73,6 +85,9 @@ if (res == NULL) { goto out_of_space; }; + if (sym_is_bottom(res)) { + goto hit_bottom; + } stack_pointer[0] = res; stack_pointer += 1; break; @@ -202,6 +217,9 @@ else { OUT_OF_SPACE_IF_NULL(res = sym_new_type(ctx, &PyLong_Type)); } + if (sym_is_bottom(res)) { + goto hit_bottom; + } stack_pointer[-2] = res; stack_pointer += -1; break; @@ -232,6 +250,9 @@ else { OUT_OF_SPACE_IF_NULL(res = sym_new_type(ctx, &PyLong_Type)); } + if (sym_is_bottom(res)) { + goto hit_bottom; + } stack_pointer[-2] = res; stack_pointer += -1; break; @@ -262,6 +283,9 @@ else { OUT_OF_SPACE_IF_NULL(res = sym_new_type(ctx, &PyLong_Type)); } + if (sym_is_bottom(res)) { + goto hit_bottom; + } stack_pointer[-2] = res; stack_pointer += -1; break; @@ -307,6 +331,9 @@ else { OUT_OF_SPACE_IF_NULL(res = sym_new_type(ctx, &PyFloat_Type)); } + if (sym_is_bottom(res)) { + goto hit_bottom; + } stack_pointer[-2] = res; stack_pointer += -1; break; @@ -338,6 +365,9 @@ else { OUT_OF_SPACE_IF_NULL(res = sym_new_type(ctx, &PyFloat_Type)); } + if (sym_is_bottom(res)) { + goto hit_bottom; + } stack_pointer[-2] = res; stack_pointer += -1; break; @@ -369,6 +399,9 @@ else { OUT_OF_SPACE_IF_NULL(res = sym_new_type(ctx, &PyFloat_Type)); } + if (sym_is_bottom(res)) { + goto hit_bottom; + } stack_pointer[-2] = res; stack_pointer += -1; break; @@ -514,6 +547,9 @@ frame_pop(ctx); stack_pointer = ctx->frame->stack_pointer; res = retval; + if (sym_is_bottom(res)) { + goto hit_bottom; + } stack_pointer[0] = res; stack_pointer += 1; break; @@ -915,8 +951,16 @@ _LOAD_ATTR_NOT_NULL (void)index; (void)owner; + if (sym_is_bottom(attr)) { + goto hit_bottom; + } stack_pointer[-1] = attr; - if (oparg & 1) stack_pointer[0] = null; + if (oparg & 1) { + if (sym_is_bottom(null)) { + goto hit_bottom; + } + stack_pointer[0] = null; + } stack_pointer += (oparg & 1); break; } @@ -967,8 +1011,16 @@ /* No conversion made. We don't know what `attr` is. */ OUT_OF_SPACE_IF_NULL(attr = sym_new_not_null(ctx)); } + if (sym_is_bottom(attr)) { + goto hit_bottom; + } stack_pointer[-1] = attr; - if (oparg & 1) stack_pointer[0] = null; + if (oparg & 1) { + if (sym_is_bottom(null)) { + goto hit_bottom; + } + stack_pointer[0] = null; + } stack_pointer += (oparg & 1); break; } @@ -986,8 +1038,16 @@ _LOAD_ATTR_NOT_NULL (void)hint; (void)owner; + if (sym_is_bottom(attr)) { + goto hit_bottom; + } stack_pointer[-1] = attr; - if (oparg & 1) stack_pointer[0] = null; + if (oparg & 1) { + if (sym_is_bottom(null)) { + goto hit_bottom; + } + stack_pointer[0] = null; + } stack_pointer += (oparg & 1); break; } @@ -1001,8 +1061,16 @@ _LOAD_ATTR_NOT_NULL (void)index; (void)owner; + if (sym_is_bottom(attr)) { + goto hit_bottom; + } stack_pointer[-1] = attr; - if (oparg & 1) stack_pointer[0] = null; + if (oparg & 1) { + if (sym_is_bottom(null)) { + goto hit_bottom; + } + stack_pointer[0] = null; + } stack_pointer += (oparg & 1); break; } @@ -1020,8 +1088,16 @@ _LOAD_ATTR_NOT_NULL (void)descr; (void)owner; + if (sym_is_bottom(attr)) { + goto hit_bottom; + } stack_pointer[-1] = attr; - if (oparg & 1) stack_pointer[0] = null; + if (oparg & 1) { + if (sym_is_bottom(null)) { + goto hit_bottom; + } + stack_pointer[0] = null; + } stack_pointer += (oparg & 1); break; } @@ -1260,6 +1336,9 @@ iter = stack_pointer[-1]; OUT_OF_SPACE_IF_NULL(next = sym_new_type(ctx, &PyLong_Type)); (void)iter; + if (sym_is_bottom(next)) { + goto hit_bottom; + } stack_pointer[0] = next; stack_pointer += 1; break; @@ -1332,7 +1411,13 @@ (void)descr; OUT_OF_SPACE_IF_NULL(attr = sym_new_not_null(ctx)); self = owner; + if (sym_is_bottom(attr)) { + goto hit_bottom; + } stack_pointer[-1] = attr; + if (sym_is_bottom(self)) { + goto hit_bottom; + } stack_pointer[0] = self; stack_pointer += 1; break; @@ -1347,7 +1432,13 @@ (void)descr; OUT_OF_SPACE_IF_NULL(attr = sym_new_not_null(ctx)); self = owner; + if (sym_is_bottom(attr)) { + goto hit_bottom; + } stack_pointer[-1] = attr; + if (sym_is_bottom(self)) { + goto hit_bottom; + } stack_pointer[0] = self; stack_pointer += 1; break; @@ -1382,7 +1473,13 @@ (void)descr; OUT_OF_SPACE_IF_NULL(attr = sym_new_not_null(ctx)); self = owner; + if (sym_is_bottom(attr)) { + goto hit_bottom; + } stack_pointer[-1] = attr; + if (sym_is_bottom(self)) { + goto hit_bottom; + } stack_pointer[0] = self; stack_pointer += 1; break; @@ -1410,7 +1507,13 @@ (void)callable; OUT_OF_SPACE_IF_NULL(func = sym_new_not_null(ctx)); OUT_OF_SPACE_IF_NULL(self = sym_new_not_null(ctx)); + if (sym_is_bottom(func)) { + goto hit_bottom; + } stack_pointer[-2 - oparg] = func; + if (sym_is_bottom(self)) { + goto hit_bottom; + } stack_pointer[-1 - oparg] = self; break; } @@ -1468,6 +1571,9 @@ } OUT_OF_SPACE_IF_NULL(new_frame = frame_new(ctx, co, localsplus_start, n_locals_already_filled, 0)); + if (sym_is_bottom((_Py_UopsSymbol *)new_frame)) { + goto hit_bottom; + } stack_pointer[-2 - oparg] = (_Py_UopsSymbol *)new_frame; stack_pointer += -1 - oparg; break; @@ -1675,6 +1781,9 @@ bottom = stack_pointer[-1 - (oparg-1)]; assert(oparg > 0); top = bottom; + if (sym_is_bottom(top)) { + goto hit_bottom; + } stack_pointer[0] = top; stack_pointer += 1; break; @@ -1694,7 +1803,13 @@ _Py_UopsSymbol *bottom; top = stack_pointer[-1]; bottom = stack_pointer[-2 - (oparg-2)]; + if (sym_is_bottom(top)) { + goto hit_bottom; + } stack_pointer[-2 - (oparg-2)] = top; + if (sym_is_bottom(bottom)) { + goto hit_bottom; + } stack_pointer[-1] = bottom; break; } @@ -1757,6 +1872,9 @@ _Py_UopsSymbol *value; PyObject *ptr = (PyObject *)this_instr->operand; OUT_OF_SPACE_IF_NULL(value = sym_new_const(ctx, ptr)); + if (sym_is_bottom(value)) { + goto hit_bottom; + } stack_pointer[0] = value; stack_pointer += 1; break; @@ -1766,6 +1884,9 @@ _Py_UopsSymbol *value; PyObject *ptr = (PyObject *)this_instr->operand; OUT_OF_SPACE_IF_NULL(value = sym_new_const(ctx, ptr)); + if (sym_is_bottom(value)) { + goto hit_bottom; + } stack_pointer[0] = value; stack_pointer += 1; break; @@ -1777,7 +1898,13 @@ PyObject *ptr = (PyObject *)this_instr->operand; OUT_OF_SPACE_IF_NULL(value = sym_new_const(ctx, ptr)); OUT_OF_SPACE_IF_NULL(null = sym_new_null(ctx)); + if (sym_is_bottom(value)) { + goto hit_bottom; + } stack_pointer[0] = value; + if (sym_is_bottom(null)) { + goto hit_bottom; + } stack_pointer[1] = null; stack_pointer += 2; break; @@ -1789,7 +1916,13 @@ PyObject *ptr = (PyObject *)this_instr->operand; OUT_OF_SPACE_IF_NULL(value = sym_new_const(ctx, ptr)); OUT_OF_SPACE_IF_NULL(null = sym_new_null(ctx)); + if (sym_is_bottom(value)) { + goto hit_bottom; + } stack_pointer[0] = value; + if (sym_is_bottom(null)) { + goto hit_bottom; + } stack_pointer[1] = null; stack_pointer += 2; break; diff --git a/Tools/cases_generator/optimizer_generator.py b/Tools/cases_generator/optimizer_generator.py index fca42b51fbd689..cee901a865c3fb 100644 --- a/Tools/cases_generator/optimizer_generator.py +++ b/Tools/cases_generator/optimizer_generator.py @@ -144,7 +144,7 @@ def write_uop( if not var.peek or is_override: out.emit(stack.push(var)) out.start_line() - stack.flush(out, cast_type="_Py_UopsSymbol *") + stack.flush(out, cast_type="_Py_UopsSymbol *", add_bottom_check=bool(override)) except SizeMismatch as ex: raise analysis_error(ex.args[0], uop.body[0]) diff --git a/Tools/cases_generator/stack.py b/Tools/cases_generator/stack.py index 5aecac39aef5e2..db3cfe2df449f9 100644 --- a/Tools/cases_generator/stack.py +++ b/Tools/cases_generator/stack.py @@ -177,20 +177,30 @@ def push(self, var: StackItem) -> str: self.top_offset.push(var) return "" - def flush(self, out: CWriter, cast_type: str = "PyObject *") -> None: + def flush(self, out: CWriter, cast_type: str = "PyObject *", add_bottom_check: bool = False) -> None: out.start_line() for var in self.variables: if not var.peek: cast = f"({cast_type})" if var.type else "" if var.name not in UNUSED and not var.is_array(): + need_braces = False if var.condition: if var.condition == "0": continue elif var.condition != "1": + need_braces = add_bottom_check out.emit(f"if ({var.condition}) ") + if need_braces: + out.emit("{\n") + if add_bottom_check: + out.emit(f"if (sym_is_bottom({cast}{var.name})) {{\n") + out.emit("goto hit_bottom;\n") + out.emit("}\n") out.emit( f"stack_pointer[{self.base_offset.to_c()}] = {cast}{var.name};\n" ) + if need_braces: + out.emit("}\n") self.base_offset.push(var) if self.base_offset.to_c() != self.top_offset.to_c(): print("base", self.base_offset.to_c(), "top", self.top_offset.to_c()) From c6938a8657ae5190f92573249b5690206c8643f9 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 28 Feb 2024 16:37:17 -0800 Subject: [PATCH 3/6] Revert "Add bottom checks, first approximation" Let's go low-tech instead. This reverts commit 23d8a15af6faf39bf818ffa38300db91400db648. --- Python/optimizer_analysis.c | 10 -- Python/optimizer_cases.c.h | 143 +------------------ Tools/cases_generator/optimizer_generator.py | 2 +- Tools/cases_generator/stack.py | 12 +- 4 files changed, 7 insertions(+), 160 deletions(-) diff --git a/Python/optimizer_analysis.c b/Python/optimizer_analysis.c index e4302370a636fa..2a7ef4ec919eeb 100644 --- a/Python/optimizer_analysis.c +++ b/Python/optimizer_analysis.c @@ -339,7 +339,6 @@ optimize_uops( _PyUOpName(opcode), oparg); switch (opcode) { - #include "optimizer_cases.c.h" default: @@ -364,15 +363,6 @@ optimize_uops( DPRINTF(1, "Encountered error in abstract interpreter\n"); _Py_uop_abstractcontext_fini(ctx); return 0; - -hit_bottom: - // Attempted to push a "bottom" (contradition) symbol onto the stack. - // This means that the abstract interpreter has hit unreachable code. - // We *could* generate an _EXIT_TRACE or _FATAL_ERROR here, but it's - // simpler to just admit failure and not create the executor. - DPRINTF(1, "Hit bottom in abstract interpreter\n"); - _Py_uop_abstractcontext_fini(ctx); - return 0; } diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index bb943048455de5..b38c03bed4b4d0 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -20,9 +20,6 @@ if (sym_is_null(value)) { goto out_of_space; } - if (sym_is_bottom(value)) { - goto hit_bottom; - } stack_pointer[0] = value; stack_pointer += 1; break; @@ -31,9 +28,6 @@ case _LOAD_FAST: { _Py_UopsSymbol *value; value = GETLOCAL(oparg); - if (sym_is_bottom(value)) { - goto hit_bottom; - } stack_pointer[0] = value; stack_pointer += 1; break; @@ -45,9 +39,6 @@ _Py_UopsSymbol *temp; OUT_OF_SPACE_IF_NULL(temp = sym_new_null(ctx)); GETLOCAL(oparg) = temp; - if (sym_is_bottom(value)) { - goto hit_bottom; - } stack_pointer[0] = value; stack_pointer += 1; break; @@ -58,9 +49,6 @@ // There should be no LOAD_CONST. It should be all // replaced by peephole_opt. Py_UNREACHABLE(); - if (sym_is_bottom(value)) { - goto hit_bottom; - } stack_pointer[0] = value; stack_pointer += 1; break; @@ -85,9 +73,6 @@ if (res == NULL) { goto out_of_space; }; - if (sym_is_bottom(res)) { - goto hit_bottom; - } stack_pointer[0] = res; stack_pointer += 1; break; @@ -217,9 +202,6 @@ else { OUT_OF_SPACE_IF_NULL(res = sym_new_type(ctx, &PyLong_Type)); } - if (sym_is_bottom(res)) { - goto hit_bottom; - } stack_pointer[-2] = res; stack_pointer += -1; break; @@ -250,9 +232,6 @@ else { OUT_OF_SPACE_IF_NULL(res = sym_new_type(ctx, &PyLong_Type)); } - if (sym_is_bottom(res)) { - goto hit_bottom; - } stack_pointer[-2] = res; stack_pointer += -1; break; @@ -283,9 +262,6 @@ else { OUT_OF_SPACE_IF_NULL(res = sym_new_type(ctx, &PyLong_Type)); } - if (sym_is_bottom(res)) { - goto hit_bottom; - } stack_pointer[-2] = res; stack_pointer += -1; break; @@ -331,9 +307,6 @@ else { OUT_OF_SPACE_IF_NULL(res = sym_new_type(ctx, &PyFloat_Type)); } - if (sym_is_bottom(res)) { - goto hit_bottom; - } stack_pointer[-2] = res; stack_pointer += -1; break; @@ -365,9 +338,6 @@ else { OUT_OF_SPACE_IF_NULL(res = sym_new_type(ctx, &PyFloat_Type)); } - if (sym_is_bottom(res)) { - goto hit_bottom; - } stack_pointer[-2] = res; stack_pointer += -1; break; @@ -399,9 +369,6 @@ else { OUT_OF_SPACE_IF_NULL(res = sym_new_type(ctx, &PyFloat_Type)); } - if (sym_is_bottom(res)) { - goto hit_bottom; - } stack_pointer[-2] = res; stack_pointer += -1; break; @@ -547,9 +514,6 @@ frame_pop(ctx); stack_pointer = ctx->frame->stack_pointer; res = retval; - if (sym_is_bottom(res)) { - goto hit_bottom; - } stack_pointer[0] = res; stack_pointer += 1; break; @@ -951,16 +915,8 @@ _LOAD_ATTR_NOT_NULL (void)index; (void)owner; - if (sym_is_bottom(attr)) { - goto hit_bottom; - } stack_pointer[-1] = attr; - if (oparg & 1) { - if (sym_is_bottom(null)) { - goto hit_bottom; - } - stack_pointer[0] = null; - } + if (oparg & 1) stack_pointer[0] = null; stack_pointer += (oparg & 1); break; } @@ -1011,16 +967,8 @@ /* No conversion made. We don't know what `attr` is. */ OUT_OF_SPACE_IF_NULL(attr = sym_new_not_null(ctx)); } - if (sym_is_bottom(attr)) { - goto hit_bottom; - } stack_pointer[-1] = attr; - if (oparg & 1) { - if (sym_is_bottom(null)) { - goto hit_bottom; - } - stack_pointer[0] = null; - } + if (oparg & 1) stack_pointer[0] = null; stack_pointer += (oparg & 1); break; } @@ -1038,16 +986,8 @@ _LOAD_ATTR_NOT_NULL (void)hint; (void)owner; - if (sym_is_bottom(attr)) { - goto hit_bottom; - } stack_pointer[-1] = attr; - if (oparg & 1) { - if (sym_is_bottom(null)) { - goto hit_bottom; - } - stack_pointer[0] = null; - } + if (oparg & 1) stack_pointer[0] = null; stack_pointer += (oparg & 1); break; } @@ -1061,16 +1001,8 @@ _LOAD_ATTR_NOT_NULL (void)index; (void)owner; - if (sym_is_bottom(attr)) { - goto hit_bottom; - } stack_pointer[-1] = attr; - if (oparg & 1) { - if (sym_is_bottom(null)) { - goto hit_bottom; - } - stack_pointer[0] = null; - } + if (oparg & 1) stack_pointer[0] = null; stack_pointer += (oparg & 1); break; } @@ -1088,16 +1020,8 @@ _LOAD_ATTR_NOT_NULL (void)descr; (void)owner; - if (sym_is_bottom(attr)) { - goto hit_bottom; - } stack_pointer[-1] = attr; - if (oparg & 1) { - if (sym_is_bottom(null)) { - goto hit_bottom; - } - stack_pointer[0] = null; - } + if (oparg & 1) stack_pointer[0] = null; stack_pointer += (oparg & 1); break; } @@ -1336,9 +1260,6 @@ iter = stack_pointer[-1]; OUT_OF_SPACE_IF_NULL(next = sym_new_type(ctx, &PyLong_Type)); (void)iter; - if (sym_is_bottom(next)) { - goto hit_bottom; - } stack_pointer[0] = next; stack_pointer += 1; break; @@ -1411,13 +1332,7 @@ (void)descr; OUT_OF_SPACE_IF_NULL(attr = sym_new_not_null(ctx)); self = owner; - if (sym_is_bottom(attr)) { - goto hit_bottom; - } stack_pointer[-1] = attr; - if (sym_is_bottom(self)) { - goto hit_bottom; - } stack_pointer[0] = self; stack_pointer += 1; break; @@ -1432,13 +1347,7 @@ (void)descr; OUT_OF_SPACE_IF_NULL(attr = sym_new_not_null(ctx)); self = owner; - if (sym_is_bottom(attr)) { - goto hit_bottom; - } stack_pointer[-1] = attr; - if (sym_is_bottom(self)) { - goto hit_bottom; - } stack_pointer[0] = self; stack_pointer += 1; break; @@ -1473,13 +1382,7 @@ (void)descr; OUT_OF_SPACE_IF_NULL(attr = sym_new_not_null(ctx)); self = owner; - if (sym_is_bottom(attr)) { - goto hit_bottom; - } stack_pointer[-1] = attr; - if (sym_is_bottom(self)) { - goto hit_bottom; - } stack_pointer[0] = self; stack_pointer += 1; break; @@ -1507,13 +1410,7 @@ (void)callable; OUT_OF_SPACE_IF_NULL(func = sym_new_not_null(ctx)); OUT_OF_SPACE_IF_NULL(self = sym_new_not_null(ctx)); - if (sym_is_bottom(func)) { - goto hit_bottom; - } stack_pointer[-2 - oparg] = func; - if (sym_is_bottom(self)) { - goto hit_bottom; - } stack_pointer[-1 - oparg] = self; break; } @@ -1571,9 +1468,6 @@ } OUT_OF_SPACE_IF_NULL(new_frame = frame_new(ctx, co, localsplus_start, n_locals_already_filled, 0)); - if (sym_is_bottom((_Py_UopsSymbol *)new_frame)) { - goto hit_bottom; - } stack_pointer[-2 - oparg] = (_Py_UopsSymbol *)new_frame; stack_pointer += -1 - oparg; break; @@ -1781,9 +1675,6 @@ bottom = stack_pointer[-1 - (oparg-1)]; assert(oparg > 0); top = bottom; - if (sym_is_bottom(top)) { - goto hit_bottom; - } stack_pointer[0] = top; stack_pointer += 1; break; @@ -1803,13 +1694,7 @@ _Py_UopsSymbol *bottom; top = stack_pointer[-1]; bottom = stack_pointer[-2 - (oparg-2)]; - if (sym_is_bottom(top)) { - goto hit_bottom; - } stack_pointer[-2 - (oparg-2)] = top; - if (sym_is_bottom(bottom)) { - goto hit_bottom; - } stack_pointer[-1] = bottom; break; } @@ -1872,9 +1757,6 @@ _Py_UopsSymbol *value; PyObject *ptr = (PyObject *)this_instr->operand; OUT_OF_SPACE_IF_NULL(value = sym_new_const(ctx, ptr)); - if (sym_is_bottom(value)) { - goto hit_bottom; - } stack_pointer[0] = value; stack_pointer += 1; break; @@ -1884,9 +1766,6 @@ _Py_UopsSymbol *value; PyObject *ptr = (PyObject *)this_instr->operand; OUT_OF_SPACE_IF_NULL(value = sym_new_const(ctx, ptr)); - if (sym_is_bottom(value)) { - goto hit_bottom; - } stack_pointer[0] = value; stack_pointer += 1; break; @@ -1898,13 +1777,7 @@ PyObject *ptr = (PyObject *)this_instr->operand; OUT_OF_SPACE_IF_NULL(value = sym_new_const(ctx, ptr)); OUT_OF_SPACE_IF_NULL(null = sym_new_null(ctx)); - if (sym_is_bottom(value)) { - goto hit_bottom; - } stack_pointer[0] = value; - if (sym_is_bottom(null)) { - goto hit_bottom; - } stack_pointer[1] = null; stack_pointer += 2; break; @@ -1916,13 +1789,7 @@ PyObject *ptr = (PyObject *)this_instr->operand; OUT_OF_SPACE_IF_NULL(value = sym_new_const(ctx, ptr)); OUT_OF_SPACE_IF_NULL(null = sym_new_null(ctx)); - if (sym_is_bottom(value)) { - goto hit_bottom; - } stack_pointer[0] = value; - if (sym_is_bottom(null)) { - goto hit_bottom; - } stack_pointer[1] = null; stack_pointer += 2; break; diff --git a/Tools/cases_generator/optimizer_generator.py b/Tools/cases_generator/optimizer_generator.py index cee901a865c3fb..fca42b51fbd689 100644 --- a/Tools/cases_generator/optimizer_generator.py +++ b/Tools/cases_generator/optimizer_generator.py @@ -144,7 +144,7 @@ def write_uop( if not var.peek or is_override: out.emit(stack.push(var)) out.start_line() - stack.flush(out, cast_type="_Py_UopsSymbol *", add_bottom_check=bool(override)) + stack.flush(out, cast_type="_Py_UopsSymbol *") except SizeMismatch as ex: raise analysis_error(ex.args[0], uop.body[0]) diff --git a/Tools/cases_generator/stack.py b/Tools/cases_generator/stack.py index db3cfe2df449f9..5aecac39aef5e2 100644 --- a/Tools/cases_generator/stack.py +++ b/Tools/cases_generator/stack.py @@ -177,30 +177,20 @@ def push(self, var: StackItem) -> str: self.top_offset.push(var) return "" - def flush(self, out: CWriter, cast_type: str = "PyObject *", add_bottom_check: bool = False) -> None: + def flush(self, out: CWriter, cast_type: str = "PyObject *") -> None: out.start_line() for var in self.variables: if not var.peek: cast = f"({cast_type})" if var.type else "" if var.name not in UNUSED and not var.is_array(): - need_braces = False if var.condition: if var.condition == "0": continue elif var.condition != "1": - need_braces = add_bottom_check out.emit(f"if ({var.condition}) ") - if need_braces: - out.emit("{\n") - if add_bottom_check: - out.emit(f"if (sym_is_bottom({cast}{var.name})) {{\n") - out.emit("goto hit_bottom;\n") - out.emit("}\n") out.emit( f"stack_pointer[{self.base_offset.to_c()}] = {cast}{var.name};\n" ) - if need_braces: - out.emit("}\n") self.base_offset.push(var) if self.base_offset.to_c() != self.top_offset.to_c(): print("base", self.base_offset.to_c(), "top", self.top_offset.to_c()) From ae71b8a0b143ab9adcfd5d406a7512d07e3d075b Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 28 Feb 2024 16:46:50 -0800 Subject: [PATCH 4/6] Make sym_set_...() return false if sym is now bottom --- Include/internal/pycore_optimizer.h | 8 +++---- Python/optimizer_symbols.c | 36 ++++++++++++++++++----------- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/Include/internal/pycore_optimizer.h b/Include/internal/pycore_optimizer.h index 614850468ec1d3..d91401609b5ee8 100644 --- a/Include/internal/pycore_optimizer.h +++ b/Include/internal/pycore_optimizer.h @@ -91,10 +91,10 @@ extern _Py_UopsSymbol *_Py_uop_sym_new_type( extern _Py_UopsSymbol *_Py_uop_sym_new_const(_Py_UOpsContext *ctx, PyObject *const_val); extern _Py_UopsSymbol *_Py_uop_sym_new_null(_Py_UOpsContext *ctx); extern bool _Py_uop_sym_matches_type(_Py_UopsSymbol *sym, PyTypeObject *typ); -extern void _Py_uop_sym_set_null(_Py_UopsSymbol *sym); -extern void _Py_uop_sym_set_non_null(_Py_UopsSymbol *sym); -extern void _Py_uop_sym_set_type(_Py_UopsSymbol *sym, PyTypeObject *typ); -extern void _Py_uop_sym_set_const(_Py_UopsSymbol *sym, PyObject *const_val); +extern bool _Py_uop_sym_set_null(_Py_UopsSymbol *sym); +extern bool _Py_uop_sym_set_non_null(_Py_UopsSymbol *sym); +extern bool _Py_uop_sym_set_type(_Py_UopsSymbol *sym, PyTypeObject *typ); +extern bool _Py_uop_sym_set_const(_Py_UopsSymbol *sym, PyObject *const_val); extern bool _Py_uop_sym_is_bottom(_Py_UopsSymbol *sym); diff --git a/Python/optimizer_symbols.c b/Python/optimizer_symbols.c index a529cc2f5cf215..5c3ec2b5ed1a4c 100644 --- a/Python/optimizer_symbols.c +++ b/Python/optimizer_symbols.c @@ -113,60 +113,68 @@ _Py_uop_sym_get_const(_Py_UopsSymbol *sym) return sym->const_val; } -void +bool _Py_uop_sym_set_type(_Py_UopsSymbol *sym, PyTypeObject *typ) { assert(typ != NULL && PyType_Check(typ)); if (sym->flags & IS_NULL) { sym_set_bottom(sym); - return; + return false; } if (sym->typ != NULL) { if (sym->typ != typ) { sym_set_bottom(sym); + return false; } - return; } - sym_set_flag(sym, NOT_NULL); - sym->typ = typ; + else { + sym_set_flag(sym, NOT_NULL); + sym->typ = typ; + } + return true; } -void +bool _Py_uop_sym_set_const(_Py_UopsSymbol *sym, PyObject *const_val) { assert(const_val != NULL); if (sym->flags & IS_NULL) { sym_set_bottom(sym); - return; + return false; } PyTypeObject *typ = Py_TYPE(const_val); if (sym->typ != NULL && sym->typ != typ) { sym_set_bottom(sym); - return; + return false; } if (sym->const_val != NULL) { if (sym->const_val != const_val) { // TODO: What if they're equal? sym_set_bottom(sym); + return false; } - return; } - sym_set_flag(sym, NOT_NULL); - sym->typ = typ; - sym->const_val = Py_NewRef(const_val); + else { + sym_set_flag(sym, NOT_NULL); + sym->typ = typ; + sym->const_val = Py_NewRef(const_val); + } + return true; } -void +bool _Py_uop_sym_set_null(_Py_UopsSymbol *sym) { sym_set_flag(sym, IS_NULL); + return !_Py_uop_sym_is_bottom(sym); } -void +bool _Py_uop_sym_set_non_null(_Py_UopsSymbol *sym) { sym_set_flag(sym, NOT_NULL); + return !_Py_uop_sym_is_bottom(sym); } From 7182c28252857750e5bb10ffdfc30f56b4f1e8ea Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 28 Feb 2024 16:53:39 -0800 Subject: [PATCH 5/6] Manually insert bottom checks into optimizer_bytecodes.c --- Python/optimizer_analysis.c | 9 +++++++++ Python/optimizer_bytecodes.c | 36 +++++++++++++++++++++++++++--------- Python/optimizer_cases.c.h | 36 +++++++++++++++++++++++++++--------- 3 files changed, 63 insertions(+), 18 deletions(-) diff --git a/Python/optimizer_analysis.c b/Python/optimizer_analysis.c index 2a7ef4ec919eeb..a326e2249bb4de 100644 --- a/Python/optimizer_analysis.c +++ b/Python/optimizer_analysis.c @@ -363,6 +363,15 @@ optimize_uops( DPRINTF(1, "Encountered error in abstract interpreter\n"); _Py_uop_abstractcontext_fini(ctx); return 0; + +hit_bottom: + // Attempted to push a "bottom" (contradition) symbol onto the stack. + // This means that the abstract interpreter has hit unreachable code. + // We *could* generate an _EXIT_TRACE or _FATAL_ERROR here, but it's + // simpler to just admit failure and not create the executor. + DPRINTF(1, "Hit bottom in abstract interpreter\n"); + _Py_uop_abstractcontext_fini(ctx); + return 0; } diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index 928c22da16b999..aa19a50f09ff71 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -85,8 +85,12 @@ dummy_func(void) { sym_matches_type(right, &PyLong_Type)) { REPLACE_OP(this_instr, _NOP, 0, 0); } - sym_set_type(left, &PyLong_Type); - sym_set_type(right, &PyLong_Type); + if (!sym_set_type(left, &PyLong_Type)) { + goto hit_bottom; + } + if (!sym_set_type(right, &PyLong_Type)) { + goto hit_bottom; + } } op(_GUARD_BOTH_FLOAT, (left, right -- left, right)) { @@ -94,8 +98,12 @@ dummy_func(void) { sym_matches_type(right, &PyFloat_Type)) { REPLACE_OP(this_instr, _NOP, 0 ,0); } - sym_set_type(left, &PyFloat_Type); - sym_set_type(right, &PyFloat_Type); + if (!sym_set_type(left, &PyFloat_Type)) { + goto hit_bottom; + } + if (!sym_set_type(right, &PyFloat_Type)) { + goto hit_bottom; + } } op(_GUARD_BOTH_UNICODE, (left, right -- left, right)) { @@ -103,8 +111,12 @@ dummy_func(void) { sym_matches_type(right, &PyUnicode_Type)) { REPLACE_OP(this_instr, _NOP, 0 ,0); } - sym_set_type(left, &PyUnicode_Type); - sym_set_type(right, &PyUnicode_Type); + if (!sym_set_type(left, &PyUnicode_Type)) { + goto hit_bottom; + } + if (!sym_set_type(right, &PyUnicode_Type)) { + goto hit_bottom; + } } op(_BINARY_OP_ADD_INT, (left, right -- res)) { @@ -365,14 +377,20 @@ dummy_func(void) { op(_CHECK_FUNCTION_EXACT_ARGS, (func_version/2, callable, self_or_null, unused[oparg] -- callable, self_or_null, unused[oparg])) { - sym_set_type(callable, &PyFunction_Type); + if (!sym_set_type(callable, &PyFunction_Type)) { + goto hit_bottom; + } (void)self_or_null; (void)func_version; } op(_CHECK_CALL_BOUND_METHOD_EXACT_ARGS, (callable, null, unused[oparg] -- callable, null, unused[oparg])) { - sym_set_null(null); - sym_set_type(callable, &PyMethod_Type); + if (!sym_set_null(null)) { + goto hit_bottom; + } + if (!sym_set_type(callable, &PyMethod_Type)) { + goto hit_bottom; + } } op(_INIT_CALL_PY_EXACT_ARGS, (callable, self_or_null, args[oparg] -- new_frame: _Py_UOpsAbstractFrame *)) { diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index b38c03bed4b4d0..b34c57376df88e 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -172,8 +172,12 @@ sym_matches_type(right, &PyLong_Type)) { REPLACE_OP(this_instr, _NOP, 0, 0); } - sym_set_type(left, &PyLong_Type); - sym_set_type(right, &PyLong_Type); + if (!sym_set_type(left, &PyLong_Type)) { + goto hit_bottom; + } + if (!sym_set_type(right, &PyLong_Type)) { + goto hit_bottom; + } break; } @@ -276,8 +280,12 @@ sym_matches_type(right, &PyFloat_Type)) { REPLACE_OP(this_instr, _NOP, 0 ,0); } - sym_set_type(left, &PyFloat_Type); - sym_set_type(right, &PyFloat_Type); + if (!sym_set_type(left, &PyFloat_Type)) { + goto hit_bottom; + } + if (!sym_set_type(right, &PyFloat_Type)) { + goto hit_bottom; + } break; } @@ -383,8 +391,12 @@ sym_matches_type(right, &PyUnicode_Type)) { REPLACE_OP(this_instr, _NOP, 0 ,0); } - sym_set_type(left, &PyUnicode_Type); - sym_set_type(right, &PyUnicode_Type); + if (!sym_set_type(left, &PyUnicode_Type)) { + goto hit_bottom; + } + if (!sym_set_type(right, &PyUnicode_Type)) { + goto hit_bottom; + } break; } @@ -1397,8 +1409,12 @@ _Py_UopsSymbol *callable; null = stack_pointer[-1 - oparg]; callable = stack_pointer[-2 - oparg]; - sym_set_null(null); - sym_set_type(callable, &PyMethod_Type); + if (!sym_set_null(null)) { + goto hit_bottom; + } + if (!sym_set_type(callable, &PyMethod_Type)) { + goto hit_bottom; + } break; } @@ -1425,7 +1441,9 @@ self_or_null = stack_pointer[-1 - oparg]; callable = stack_pointer[-2 - oparg]; uint32_t func_version = (uint32_t)this_instr->operand; - sym_set_type(callable, &PyFunction_Type); + if (!sym_set_type(callable, &PyFunction_Type)) { + goto hit_bottom; + } (void)self_or_null; (void)func_version; break; From 00d3660427144199171381fc5f0a806102fc7915 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 29 Feb 2024 09:45:07 -0800 Subject: [PATCH 6/6] Fix test --- Lib/test/test_capi/test_opt.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index a43726f05a448d..d21765307f0f09 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -894,23 +894,33 @@ def testfunc(n): def test_type_inconsistency(self): ns = {} - exec(textwrap.dedent(""" + src = textwrap.dedent(""" def testfunc(n): for i in range(n): x = _test_global + _test_global - """), globals(), ns) + """) + exec(src, ns, ns) testfunc = ns['testfunc'] - # Must be a real global else it won't be optimized to _LOAD_CONST_INLINE - global _test_global - _test_global = 0 + ns['_test_global'] = 0 _, ex = self._run_with_optimizer(testfunc, 16) self.assertIsNone(ex) - _test_global = 1.2 + ns['_test_global'] = 1 _, ex = self._run_with_optimizer(testfunc, 16) self.assertIsNotNone(ex) uops = get_opnames(ex) - self.assertIn("_GUARD_BOTH_INT", uops) + self.assertNotIn("_GUARD_BOTH_INT", uops) self.assertIn("_BINARY_OP_ADD_INT", uops) + # Try again, but between the runs, set the global to a float. + # This should result in no executor the second time. + ns = {} + exec(src, ns, ns) + testfunc = ns['testfunc'] + ns['_test_global'] = 0 + _, ex = self._run_with_optimizer(testfunc, 16) + self.assertIsNone(ex) + ns['_test_global'] = 3.14 + _, ex = self._run_with_optimizer(testfunc, 16) + self.assertIsNone(ex) if __name__ == "__main__":