diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6e612ce232fb29..c77610e209ebbd 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: a27a2e47c7751b639d2b5badf0ef6ff11fee893f # frozen: v0.15.4 + rev: e05c5c0818279e5ac248ac9e954431ba58865e61 # frozen: v0.15.7 hooks: - id: ruff-check name: Run Ruff (lint) on Platforms/Apple/ diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 08a0ba7a68768d..6543da4487e42f 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -822,6 +822,17 @@ http.cookies (Contributed by Nick Burns and Senthil Kumaran in :gh:`92936`.) +http.server +----------- + +* The logging of :mod:`~http.server.BaseHTTPRequestHandler`, + as used by the :ref:`command-line interface `, + is colored by default. + This can be controlled with :ref:`environment variables + `. + (Contributed by Hugo van Kemenade in :gh:`146292`.) + + inspect ------- @@ -1097,6 +1108,10 @@ tarfile timeit ------ +* The output of the :mod:`timeit` command-line interface is colored by default. + This can be controlled with + :ref:`environment variables `. + (Contributed by Hugo van Kemenade in :gh:`146609`.) * The command-line interface now colorizes error tracebacks by default. This can be controlled with :ref:`environment variables `. diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index 916f3b8ee863c4..cafbcb64505adc 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -1141,7 +1141,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[267] = { [CALL_LIST_APPEND] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, [CALL_METHOD_DESCRIPTOR_FAST] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG | HAS_RECORDS_VALUE_FLAG }, - [CALL_METHOD_DESCRIPTOR_NOARGS] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG | HAS_RECORDS_VALUE_FLAG }, + [CALL_METHOD_DESCRIPTOR_NOARGS] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG | HAS_RECORDS_VALUE_FLAG }, [CALL_METHOD_DESCRIPTOR_O] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG | HAS_RECORDS_VALUE_FLAG }, [CALL_NON_PY_GENERAL] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG | HAS_RECORDS_VALUE_FLAG }, [CALL_PY_EXACT_ARGS] = { true, INSTR_FMT_IBC00, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG | HAS_SYNC_SP_FLAG | HAS_NEEDS_GUARD_IP_FLAG | HAS_RECORDS_VALUE_FLAG }, @@ -1390,7 +1390,7 @@ _PyOpcode_macro_expansion[256] = { [CALL_LIST_APPEND] = { .nuops = 6, .uops = { { _GUARD_CALLABLE_LIST_APPEND, OPARG_SIMPLE, 3 }, { _GUARD_NOS_NOT_NULL, OPARG_SIMPLE, 3 }, { _GUARD_NOS_LIST, OPARG_SIMPLE, 3 }, { _CALL_LIST_APPEND, OPARG_SIMPLE, 3 }, { _POP_TOP, OPARG_SIMPLE, 3 }, { _POP_TOP, OPARG_SIMPLE, 3 } } }, [CALL_METHOD_DESCRIPTOR_FAST] = { .nuops = 3, .uops = { { _GUARD_CALLABLE_METHOD_DESCRIPTOR_FAST, OPARG_SIMPLE, 3 }, { _CALL_METHOD_DESCRIPTOR_FAST, OPARG_SIMPLE, 3 }, { _CHECK_PERIODIC_AT_END, OPARG_REPLACED, 3 } } }, [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = { .nuops = 4, .uops = { { _RECORD_CALLABLE, OPARG_SIMPLE, 0 }, { _GUARD_CALLABLE_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS, OPARG_SIMPLE, 3 }, { _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS, OPARG_SIMPLE, 3 }, { _CHECK_PERIODIC_AT_END, OPARG_REPLACED, 3 } } }, - [CALL_METHOD_DESCRIPTOR_NOARGS] = { .nuops = 5, .uops = { { _RECORD_CALLABLE, OPARG_SIMPLE, 0 }, { _GUARD_CALLABLE_METHOD_DESCRIPTOR_NOARGS, OPARG_SIMPLE, 3 }, { _CHECK_RECURSION_LIMIT, OPARG_SIMPLE, 3 }, { _CALL_METHOD_DESCRIPTOR_NOARGS, OPARG_SIMPLE, 3 }, { _CHECK_PERIODIC_AT_END, OPARG_REPLACED, 3 } } }, + [CALL_METHOD_DESCRIPTOR_NOARGS] = { .nuops = 7, .uops = { { _RECORD_CALLABLE, OPARG_SIMPLE, 0 }, { _GUARD_CALLABLE_METHOD_DESCRIPTOR_NOARGS, OPARG_SIMPLE, 3 }, { _CHECK_RECURSION_LIMIT, OPARG_SIMPLE, 3 }, { _CALL_METHOD_DESCRIPTOR_NOARGS, OPARG_SIMPLE, 3 }, { _POP_TOP, OPARG_SIMPLE, 3 }, { _POP_TOP, OPARG_SIMPLE, 3 }, { _CHECK_PERIODIC_AT_END, OPARG_REPLACED, 3 } } }, [CALL_METHOD_DESCRIPTOR_O] = { .nuops = 8, .uops = { { _RECORD_CALLABLE, OPARG_SIMPLE, 0 }, { _GUARD_CALLABLE_METHOD_DESCRIPTOR_O, OPARG_SIMPLE, 3 }, { _CHECK_RECURSION_LIMIT, OPARG_SIMPLE, 3 }, { _CALL_METHOD_DESCRIPTOR_O, OPARG_SIMPLE, 3 }, { _POP_TOP, OPARG_SIMPLE, 3 }, { _POP_TOP, OPARG_SIMPLE, 3 }, { _POP_TOP, OPARG_SIMPLE, 3 }, { _CHECK_PERIODIC_AT_END, OPARG_REPLACED, 3 } } }, [CALL_NON_PY_GENERAL] = { .nuops = 4, .uops = { { _RECORD_CALLABLE, OPARG_SIMPLE, 0 }, { _CHECK_IS_NOT_PY_CALLABLE, OPARG_SIMPLE, 3 }, { _CALL_NON_PY_GENERAL, OPARG_SIMPLE, 3 }, { _CHECK_PERIODIC_AT_END, OPARG_REPLACED, 3 } } }, [CALL_PY_EXACT_ARGS] = { .nuops = 9, .uops = { { _RECORD_CALLABLE, OPARG_SIMPLE, 0 }, { _CHECK_PEP_523, OPARG_SIMPLE, 1 }, { _CHECK_FUNCTION_VERSION, 2, 1 }, { _CHECK_FUNCTION_EXACT_ARGS, OPARG_SIMPLE, 3 }, { _CHECK_STACK_SPACE, OPARG_SIMPLE, 3 }, { _CHECK_RECURSION_REMAINING, OPARG_SIMPLE, 3 }, { _INIT_CALL_PY_EXACT_ARGS, OPARG_SIMPLE, 3 }, { _SAVE_RETURN_OFFSET, OPARG_SAVE_RETURN_OFFSET, 3 }, { _PUSH_FRAME, OPARG_SIMPLE, 3 } } }, diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h index a1664e9791e662..5fcc422749a3d3 100644 --- a/Include/internal/pycore_uop_ids.h +++ b/Include/internal/pycore_uop_ids.h @@ -527,8 +527,8 @@ extern "C" { #define _CALL_METHOD_DESCRIPTOR_FAST_INLINE_r01 735 #define _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS_r01 736 #define _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS_INLINE_r01 737 -#define _CALL_METHOD_DESCRIPTOR_NOARGS_r01 738 -#define _CALL_METHOD_DESCRIPTOR_NOARGS_INLINE_r01 739 +#define _CALL_METHOD_DESCRIPTOR_NOARGS_r03 738 +#define _CALL_METHOD_DESCRIPTOR_NOARGS_INLINE_r03 739 #define _CALL_METHOD_DESCRIPTOR_O_r03 740 #define _CALL_METHOD_DESCRIPTOR_O_INLINE_r03 741 #define _CALL_NON_PY_GENERAL_r01 742 diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index 5e41f8a021d950..f3d334b85a24ba 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -325,8 +325,8 @@ const uint32_t _PyUop_Flags[MAX_UOP_ID+1] = { [_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS_INLINE] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_GUARD_CALLABLE_METHOD_DESCRIPTOR_NOARGS] = HAS_ARG_FLAG | HAS_EXIT_FLAG, - [_CALL_METHOD_DESCRIPTOR_NOARGS] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, - [_CALL_METHOD_DESCRIPTOR_NOARGS_INLINE] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, + [_CALL_METHOD_DESCRIPTOR_NOARGS] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG, + [_CALL_METHOD_DESCRIPTOR_NOARGS_INLINE] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG, [_GUARD_CALLABLE_METHOD_DESCRIPTOR_FAST] = HAS_ARG_FLAG | HAS_EXIT_FLAG, [_CALL_METHOD_DESCRIPTOR_FAST] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_CALL_METHOD_DESCRIPTOR_FAST_INLINE] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, @@ -3049,7 +3049,7 @@ const _PyUopCachingInfo _PyUop_Caching[MAX_UOP_ID+1] = { [_CALL_METHOD_DESCRIPTOR_NOARGS] = { .best = { 0, 0, 0, 0 }, .entries = { - { 1, 0, _CALL_METHOD_DESCRIPTOR_NOARGS_r01 }, + { 3, 0, _CALL_METHOD_DESCRIPTOR_NOARGS_r03 }, { -1, -1, -1 }, { -1, -1, -1 }, { -1, -1, -1 }, @@ -3058,7 +3058,7 @@ const _PyUopCachingInfo _PyUop_Caching[MAX_UOP_ID+1] = { [_CALL_METHOD_DESCRIPTOR_NOARGS_INLINE] = { .best = { 0, 0, 0, 0 }, .entries = { - { 1, 0, _CALL_METHOD_DESCRIPTOR_NOARGS_INLINE_r01 }, + { 3, 0, _CALL_METHOD_DESCRIPTOR_NOARGS_INLINE_r03 }, { -1, -1, -1 }, { -1, -1, -1 }, { -1, -1, -1 }, @@ -4475,8 +4475,8 @@ const uint16_t _PyUop_Uncached[MAX_UOP_REGS_ID+1] = { [_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS_r01] = _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS, [_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS_INLINE_r01] = _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS_INLINE, [_GUARD_CALLABLE_METHOD_DESCRIPTOR_NOARGS_r00] = _GUARD_CALLABLE_METHOD_DESCRIPTOR_NOARGS, - [_CALL_METHOD_DESCRIPTOR_NOARGS_r01] = _CALL_METHOD_DESCRIPTOR_NOARGS, - [_CALL_METHOD_DESCRIPTOR_NOARGS_INLINE_r01] = _CALL_METHOD_DESCRIPTOR_NOARGS_INLINE, + [_CALL_METHOD_DESCRIPTOR_NOARGS_r03] = _CALL_METHOD_DESCRIPTOR_NOARGS, + [_CALL_METHOD_DESCRIPTOR_NOARGS_INLINE_r03] = _CALL_METHOD_DESCRIPTOR_NOARGS_INLINE, [_GUARD_CALLABLE_METHOD_DESCRIPTOR_FAST_r00] = _GUARD_CALLABLE_METHOD_DESCRIPTOR_FAST, [_CALL_METHOD_DESCRIPTOR_FAST_r01] = _CALL_METHOD_DESCRIPTOR_FAST, [_CALL_METHOD_DESCRIPTOR_FAST_INLINE_r01] = _CALL_METHOD_DESCRIPTOR_FAST_INLINE, @@ -4882,9 +4882,9 @@ const char *const _PyOpcode_uop_name[MAX_UOP_REGS_ID+1] = { [_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS_INLINE] = "_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS_INLINE", [_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS_INLINE_r01] = "_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS_INLINE_r01", [_CALL_METHOD_DESCRIPTOR_NOARGS] = "_CALL_METHOD_DESCRIPTOR_NOARGS", - [_CALL_METHOD_DESCRIPTOR_NOARGS_r01] = "_CALL_METHOD_DESCRIPTOR_NOARGS_r01", + [_CALL_METHOD_DESCRIPTOR_NOARGS_r03] = "_CALL_METHOD_DESCRIPTOR_NOARGS_r03", [_CALL_METHOD_DESCRIPTOR_NOARGS_INLINE] = "_CALL_METHOD_DESCRIPTOR_NOARGS_INLINE", - [_CALL_METHOD_DESCRIPTOR_NOARGS_INLINE_r01] = "_CALL_METHOD_DESCRIPTOR_NOARGS_INLINE_r01", + [_CALL_METHOD_DESCRIPTOR_NOARGS_INLINE_r03] = "_CALL_METHOD_DESCRIPTOR_NOARGS_INLINE_r03", [_CALL_METHOD_DESCRIPTOR_O] = "_CALL_METHOD_DESCRIPTOR_O", [_CALL_METHOD_DESCRIPTOR_O_r03] = "_CALL_METHOD_DESCRIPTOR_O_r03", [_CALL_METHOD_DESCRIPTOR_O_INLINE] = "_CALL_METHOD_DESCRIPTOR_O_INLINE", diff --git a/Lib/_colorize.py b/Lib/_colorize.py index 8361ddbea89716..478f81894911e7 100644 --- a/Lib/_colorize.py +++ b/Lib/_colorize.py @@ -223,6 +223,22 @@ class FancyCompleter(ThemeSection): str: str = ANSIColors.BOLD_GREEN +@dataclass(frozen=True, kw_only=True) +class HttpServer(ThemeSection): + error: str = ANSIColors.YELLOW + path: str = ANSIColors.CYAN + serving: str = ANSIColors.GREEN + size: str = ANSIColors.GREY + status_informational: str = ANSIColors.RESET + status_ok: str = ANSIColors.GREEN + status_redirect: str = ANSIColors.INTENSE_CYAN + status_client_error: str = ANSIColors.YELLOW + status_server_error: str = ANSIColors.RED + timestamp: str = ANSIColors.GREY + url: str = ANSIColors.CYAN + reset: str = ANSIColors.RESET + + @dataclass(frozen=True, kw_only=True) class LiveProfiler(ThemeSection): """Theme section for the live profiling TUI (Tachyon profiler). @@ -346,6 +362,18 @@ class Syntax(ThemeSection): reset: str = ANSIColors.RESET +@dataclass(frozen=True, kw_only=True) +class Timeit(ThemeSection): + timing: str = ANSIColors.CYAN + best: str = ANSIColors.BOLD_GREEN + per_loop: str = ANSIColors.GREEN + punctuation: str = ANSIColors.GREY + warning: str = ANSIColors.YELLOW + warning_worst: str = ANSIColors.MAGENTA + warning_best: str = ANSIColors.GREEN + reset: str = ANSIColors.RESET + + @dataclass(frozen=True, kw_only=True) class Traceback(ThemeSection): type: str = ANSIColors.BOLD_MAGENTA @@ -378,8 +406,10 @@ class Theme: argparse: Argparse = field(default_factory=Argparse) difflib: Difflib = field(default_factory=Difflib) fancycompleter: FancyCompleter = field(default_factory=FancyCompleter) + http_server: HttpServer = field(default_factory=HttpServer) live_profiler: LiveProfiler = field(default_factory=LiveProfiler) syntax: Syntax = field(default_factory=Syntax) + timeit: Timeit = field(default_factory=Timeit) traceback: Traceback = field(default_factory=Traceback) unittest: Unittest = field(default_factory=Unittest) @@ -389,8 +419,10 @@ def copy_with( argparse: Argparse | None = None, difflib: Difflib | None = None, fancycompleter: FancyCompleter | None = None, + http_server: HttpServer | None = None, live_profiler: LiveProfiler | None = None, syntax: Syntax | None = None, + timeit: Timeit | None = None, traceback: Traceback | None = None, unittest: Unittest | None = None, ) -> Self: @@ -403,8 +435,10 @@ def copy_with( argparse=argparse or self.argparse, difflib=difflib or self.difflib, fancycompleter=fancycompleter or self.fancycompleter, + http_server=http_server or self.http_server, live_profiler=live_profiler or self.live_profiler, syntax=syntax or self.syntax, + timeit=timeit or self.timeit, traceback=traceback or self.traceback, unittest=unittest or self.unittest, ) @@ -421,8 +455,10 @@ def no_colors(cls) -> Self: argparse=Argparse.no_colors(), difflib=Difflib.no_colors(), fancycompleter=FancyCompleter.no_colors(), + http_server=HttpServer.no_colors(), live_profiler=LiveProfiler.no_colors(), syntax=Syntax.no_colors(), + timeit=Timeit.no_colors(), traceback=Traceback.no_colors(), unittest=Unittest.no_colors(), ) diff --git a/Lib/http/server.py b/Lib/http/server.py index 9c9cfbce421343..568d3bb38deb6c 100644 --- a/Lib/http/server.py +++ b/Lib/http/server.py @@ -85,6 +85,8 @@ from http import HTTPStatus +lazy import _colorize + # Default error message template DEFAULT_ERROR_MESSAGE = """\ @@ -574,6 +576,31 @@ def flush_headers(self): self.wfile.write(b"".join(self._headers_buffer)) self._headers_buffer = [] + def _colorize_request(self, code, size, t): + try: + code_int = int(code) + except (TypeError, ValueError): + code_color = "" + else: + if code_int >= 500: + code_color = t.status_server_error + elif code_int >= 400: + code_color = t.status_client_error + elif code_int >= 300: + code_color = t.status_redirect + elif code_int >= 200: + code_color = t.status_ok + else: + code_color = t.status_informational + + request_line = self.requestline.translate(self._control_char_table) + parts = request_line.split(None, 2) + if len(parts) == 3: + method, path, version = parts + request_line = f"{method} {t.path}{path}{t.reset} {version}" + + return f'"{request_line}" {code_color}{code} {t.size}{size}{t.reset}' + def log_request(self, code='-', size='-'): """Log an accepted request. @@ -582,6 +609,7 @@ def log_request(self, code='-', size='-'): """ if isinstance(code, HTTPStatus): code = code.value + self._log_request_info = (code, size) self.log_message('"%s" %s %s', self.requestline, str(code), str(size)) @@ -596,7 +624,7 @@ def log_error(self, format, *args): XXX This should go to the separate error log. """ - + self._log_is_error = True self.log_message(format, *args) # https://en.wikipedia.org/wiki/List_of_Unicode_characters#Control_codes @@ -623,12 +651,22 @@ def log_message(self, format, *args): before writing the output to stderr. """ - - message = format % args - sys.stderr.write("%s - - [%s] %s\n" % - (self.address_string(), - self.log_date_time_string(), - message.translate(self._control_char_table))) + message = (format % args).translate(self._control_char_table) + t = _colorize.get_theme(tty_file=sys.stderr).http_server + + info = getattr(self, "_log_request_info", None) + if info is not None: + self._log_request_info = None + message = self._colorize_request(*info, t) + elif getattr(self, "_log_is_error", False): + self._log_is_error = False + message = f"{t.error}{message}{t.reset}" + + sys.stderr.write( + f"{t.timestamp}{self.address_string()} - - " + f"[{self.log_date_time_string()}]{t.reset} " + f"{message}\n" + ) def version_string(self): """Return the server software version string.""" @@ -994,9 +1032,11 @@ def test(HandlerClass=BaseHTTPRequestHandler, host, port = httpd.socket.getsockname()[:2] url_host = f'[{host}]' if ':' in host else host protocol = 'HTTPS' if tls_cert else 'HTTP' + t = _colorize.get_theme().http_server + url = f"{protocol.lower()}://{url_host}:{port}/" print( - f"Serving {protocol} on {host} port {port} " - f"({protocol.lower()}://{url_host}:{port}/) ..." + f"{t.serving}Serving {protocol} on {host} port {port}{t.reset} " + f"({t.url}{url}{t.reset}) ..." ) try: httpd.serve_forever() diff --git a/Lib/test/.ruff.toml b/Lib/test/.ruff.toml index f3e6a46663e100..a960543f277935 100644 --- a/Lib/test/.ruff.toml +++ b/Lib/test/.ruff.toml @@ -1,7 +1,7 @@ extend = "../../.ruff.toml" # Inherit the project-wide settings # Unlike Tools/, tests can use newer syntax than PYTHON_FOR_REGEN -target-version = "py314" +target-version = "py315" extend-exclude = [ # Excluded (run with the other AC files in its own separate ruff job in pre-commit) diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index 124a2c0115769c..f89f0d8bc4ee2a 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -1470,8 +1470,8 @@ class Bar: opnames = list(iter_opnames(ex)) self.assertEqual(res, TIER2_THRESHOLD * 2 + 2) call = opnames.index("_CALL_BUILTIN_FAST") - load_attr_top = opnames.index("_POP_TOP_LOAD_CONST_INLINE_BORROW", 0, call) - load_attr_bottom = opnames.index("_POP_TOP_LOAD_CONST_INLINE_BORROW", call) + load_attr_top = opnames.index("_LOAD_CONST_INLINE_BORROW", 0, call) + load_attr_bottom = opnames.index("_LOAD_CONST_INLINE_BORROW", call) self.assertEqual(opnames[:load_attr_top].count("_GUARD_TYPE_VERSION"), 1) self.assertEqual(opnames[call:load_attr_bottom].count("_CHECK_VALIDITY"), 2) @@ -1493,8 +1493,8 @@ class Foo: self.assertIsNotNone(ex) self.assertEqual(res, TIER2_THRESHOLD * 2) call = opnames.index("_CALL_BUILTIN_FAST_WITH_KEYWORDS") - load_attr_top = opnames.index("_POP_TOP_LOAD_CONST_INLINE_BORROW", 0, call) - load_attr_bottom = opnames.index("_POP_TOP_LOAD_CONST_INLINE_BORROW", call) + load_attr_top = opnames.index("_LOAD_CONST_INLINE_BORROW", 0, call) + load_attr_bottom = opnames.index("_LOAD_CONST_INLINE_BORROW", call) self.assertEqual(opnames[:load_attr_top].count("_GUARD_TYPE_VERSION"), 1) self.assertEqual(opnames[call:load_attr_bottom].count("_CHECK_VALIDITY"), 2) @@ -2340,6 +2340,21 @@ def testfunc(n): self.assertIn("_BINARY_OP_SUBSCR_DICT_KNOWN_HASH", uops) self.assertNotIn("_BINARY_OP_SUBSCR_DICT", uops) + + def test_binary_op_subscr_constant_frozendict_known_hash(self): + def testfunc(n): + x = 0 + for _ in range(n): + x += FROZEN_DICT_CONST['x'] + return x + + res, ex = self._run_with_optimizer(testfunc, 2 * TIER2_THRESHOLD) + self.assertEqual(res, 2 * TIER2_THRESHOLD) + self.assertIsNotNone(ex) + uops = get_opnames(ex) + self.assertNotIn("_BINARY_OP_SUBSCR_DICT_KNOWN_HASH", uops) + self.assertNotIn("_BINARY_OP_SUBSCR_DICT", uops) + def test_store_subscr_dict_known_hash(self): # str, int, bytes, float, complex, tuple and any python object which has generic hash def testfunc(n): @@ -2780,9 +2795,12 @@ def testfunc(n): self.assertEqual(res, TIER2_THRESHOLD * 5) self.assertIsNotNone(ex) uops = get_opnames(ex) + self.assertIn("_CALL_METHOD_DESCRIPTOR_NOARGS_INLINE", uops) self.assertNotIn("_CALL_METHOD_DESCRIPTOR_NOARGS", uops) self.assertNotIn("_GUARD_CALLABLE_METHOD_DESCRIPTOR_NOARGS", uops) + self.assertGreaterEqual(count_ops(ex, "_POP_TOP"), 5) + self.assertGreaterEqual(count_ops(ex, "_POP_TOP"), 3) def test_call_method_descriptor_fast(self): def testfunc(n): diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index 0dc5c9dbaed5d8..d78b94e3a373d4 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -28,10 +28,12 @@ import threading from unittest import mock from io import BytesIO, StringIO +from _colorize import get_theme import unittest from test import support from test.support import ( + force_not_colorized, is_apple, import_helper, os_helper, threading_helper ) from test.support.script_helper import kill_python, spawn_python @@ -480,6 +482,7 @@ def do_GET(self): def do_ERROR(self): self.send_error(HTTPStatus.NOT_FOUND, 'File not found') + @force_not_colorized def test_get(self): self.con = http.client.HTTPConnection(self.HOST, self.PORT) self.con.connect() @@ -490,6 +493,7 @@ def test_get(self): self.assertEndsWith(err.getvalue(), '"GET / HTTP/1.1" 200 -\n') + @force_not_colorized def test_err(self): self.con = http.client.HTTPConnection(self.HOST, self.PORT) self.con.connect() @@ -503,6 +507,39 @@ def test_err(self): self.assertEndsWith(lines[1], '"ERROR / HTTP/1.1" 404 -') +@support.force_colorized_test_class +class RequestHandlerColorizedLoggingTestCase(RequestHandlerLoggingTestCase): + + def test_get(self): + t = get_theme(force_color=True).http_server + self.con = http.client.HTTPConnection(self.HOST, self.PORT) + self.con.connect() + + with support.captured_stderr() as err: + self.con.request("GET", "/") + self.con.getresponse() + + output = err.getvalue() + self.assertIn(f"{t.path}/{t.reset}", output) + self.assertIn(f"{t.status_ok}200", output) + self.assertIn(t.reset, output) + + def test_err(self): + t = get_theme(force_color=True).http_server + self.con = http.client.HTTPConnection(self.HOST, self.PORT) + self.con.connect() + + with support.captured_stderr() as err: + self.con.request("ERROR", "/") + self.con.getresponse() + + lines = err.getvalue().split("\n") + self.assertIn( + f"{t.error}code 404, message File not found{t.reset}", lines[0] + ) + self.assertIn(f"{t.status_client_error}404", lines[1]) + + class SimpleHTTPServerTestCase(BaseTestCase): class request_handler(NoLogRequestHandler, SimpleHTTPRequestHandler): pass @@ -935,6 +972,7 @@ def verify_http_server_response(self, response): match = self.HTTPResponseMatch.search(response) self.assertIsNotNone(match) + @force_not_colorized def test_unprintable_not_logged(self): # We call the method from the class directly as our Socketless # Handler subclass overrode it... nice for everything BUT this test. diff --git a/Lib/test/test_resource.py b/Lib/test/test_resource.py index 5fd076bee38e79..ba736b0806eb1b 100644 --- a/Lib/test/test_resource.py +++ b/Lib/test/test_resource.py @@ -4,7 +4,6 @@ from test import support from test.support import import_helper from test.support import os_helper -import time resource = import_helper.import_module('resource') @@ -41,7 +40,7 @@ def test_fsize_ismax(self): # the number to a C long long and that the conversion doesn't raise # an error. self.assertGreater(resource.RLIM_INFINITY, 0) - self.assertEqual(resource.RLIM_INFINITY, max) + self.assertGreaterEqual(max, 0) self.assertLessEqual(cur, max) resource.setrlimit(resource.RLIMIT_FSIZE, (max, max)) resource.setrlimit(resource.RLIMIT_FSIZE, (cur, max)) @@ -50,47 +49,24 @@ def test_fsize_ismax(self): "setting RLIMIT_FSIZE is not supported on VxWorks") @unittest.skipUnless(hasattr(resource, 'RLIMIT_FSIZE'), 'requires resource.RLIMIT_FSIZE') def test_fsize_enforced(self): - (cur, max) = resource.getrlimit(resource.RLIMIT_FSIZE) - # Check to see what happens when the RLIMIT_FSIZE is small. Some - # versions of Python were terminated by an uncaught SIGXFSZ, but - # pythonrun.c has been fixed to ignore that exception. If so, the - # write() should return EFBIG when the limit is exceeded. - - # At least one platform has an unlimited RLIMIT_FSIZE and attempts - # to change it raise ValueError instead. + self.addCleanup(os_helper.unlink, os_helper.TESTFN) try: + (cur, max_lim) = resource.getrlimit(resource.RLIMIT_FSIZE) + except OSError as e: + self.skipTest(f"getrlimit(RLIMIT_FSIZE) failed: {e}") + if max_lim != resource.RLIM_INFINITY and max_lim < 1025: + self.skipTest(f"system RLIMIT_FSIZE hard limit ({max_lim}) is too small for this test") + with open(os_helper.TESTFN, "wb", buffering=0) as f: try: - resource.setrlimit(resource.RLIMIT_FSIZE, (1024, max)) - limit_set = True - except ValueError: - limit_set = False - f = open(os_helper.TESTFN, "wb") - try: + resource.setrlimit(resource.RLIMIT_FSIZE, (1024, max_lim)) f.write(b"X" * 1024) - try: + with self.assertRaises(OSError, msg="f.write() did not raise OSError when exceeding RLIMIT_FSIZE"): f.write(b"Y") f.flush() - # On some systems (e.g., Ubuntu on hppa) the flush() - # doesn't always cause the exception, but the close() - # does eventually. Try flushing several times in - # an attempt to ensure the file is really synced and - # the exception raised. - for i in range(5): - time.sleep(.1) - f.flush() - except OSError: - if not limit_set: - raise - if limit_set: - # Close will attempt to flush the byte we wrote - # Restore limit first to avoid getting a spurious error - resource.setrlimit(resource.RLIMIT_FSIZE, (cur, max)) finally: - f.close() - finally: - if limit_set: - resource.setrlimit(resource.RLIMIT_FSIZE, (cur, max)) - os_helper.unlink(os_helper.TESTFN) + # Close will attempt to flush the byte we wrote + # Restore limit first to avoid getting a spurious error + resource.setrlimit(resource.RLIMIT_FSIZE, (cur, max_lim)) @unittest.skipIf(sys.platform == "vxworks", "setting RLIMIT_FSIZE is not supported on VxWorks") diff --git a/Lib/test/test_timeit.py b/Lib/test/test_timeit.py index 8837e88ba638cf..4971ebe0b78ef8 100644 --- a/Lib/test/test_timeit.py +++ b/Lib/test/test_timeit.py @@ -5,9 +5,14 @@ from textwrap import dedent from test.support import ( - captured_stdout, captured_stderr, force_not_colorized, + captured_stderr, + captured_stdout, + force_colorized, + force_not_colorized_test_class, ) +from _colorize import get_theme + # timeit's default number of iterations. DEFAULT_NUMBER = 1000000 @@ -42,6 +47,7 @@ def wrap_timer(self, timer): self.saved_timer = timer return self +@force_not_colorized_test_class class TestTimeit(unittest.TestCase): def tearDown(self): @@ -352,13 +358,11 @@ def test_main_with_time_unit(self): self.assertEqual(error_stringio.getvalue(), "Unrecognized unit. Please select nsec, usec, msec, or sec.\n") - @force_not_colorized def test_main_exception(self): with captured_stderr() as error_stringio: s = self.run_main(switches=['1/0']) self.assert_exc_string(error_stringio.getvalue(), 'ZeroDivisionError') - @force_not_colorized def test_main_exception_fixed_reps(self): with captured_stderr() as error_stringio: s = self.run_main(switches=['-n1', '1/0']) @@ -403,5 +407,39 @@ def callback(a, b): self.assertEqual(s.getvalue(), expected) -if __name__ == '__main__': +class TestTimeitColor(unittest.TestCase): + + fake_stmt = TestTimeit.fake_stmt + run_main = TestTimeit.run_main + + @force_colorized + def test_main_colorized(self): + t = get_theme(force_color=True).timeit + s = self.run_main(seconds_per_increment=5.5) + self.assertEqual( + s, + "1 loop, best of 5: " + f"{t.best}5.5 sec{t.reset} " + f"{t.per_loop}per loop{t.reset}\n", + ) + + @force_colorized + def test_main_verbose_colorized(self): + t = get_theme(force_color=True).timeit + s = self.run_main(switches=["-v"]) + self.assertEqual( + s, + f"1 loop {t.punctuation}-> {t.timing}1 secs{t.reset}\n\n" + "raw times: " + f"{t.timing}1 sec{t.punctuation}, " + f"{t.timing}1 sec{t.punctuation}, " + f"{t.timing}1 sec{t.punctuation}, " + f"{t.timing}1 sec{t.punctuation}, " + f"{t.timing}1 sec{t.reset}\n\n" + f"1 loop, best of 5: {t.best}1 sec{t.reset} " + f"{t.per_loop}per loop{t.reset}\n", + ) + + +if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_wsgiref.py b/Lib/test/test_wsgiref.py index 3379df37d38ca8..32ef0ccf4e638d 100644 --- a/Lib/test/test_wsgiref.py +++ b/Lib/test/test_wsgiref.py @@ -1,6 +1,6 @@ from unittest import mock from test import support -from test.support import socket_helper, control_characters_c0 +from test.support import force_not_colorized, socket_helper, control_characters_c0 from test.test_httpservers import NoLogRequestHandler from unittest import TestCase from wsgiref.util import setup_testing_defaults @@ -192,6 +192,7 @@ def bad_app(e,s): err.splitlines()[-2], "AssertionError" ) + @force_not_colorized def test_bytes_validation(self): def app(e, s): s("200 OK", [ diff --git a/Lib/timeit.py b/Lib/timeit.py index f900da6ffe7d67..130777768f79fc 100644 --- a/Lib/timeit.py +++ b/Lib/timeit.py @@ -273,6 +273,8 @@ def main(args=None, *, _wrap_timer=None): args = sys.argv[1:] import _colorize colorize = _colorize.can_colorize() + theme = _colorize.get_theme(force_color=colorize).timeit + reset = theme.reset try: opts, args = getopt.getopt(args, "n:u:s:r:pt:vh", @@ -337,10 +339,13 @@ def main(args=None, *, _wrap_timer=None): callback = None if verbose: def callback(number, time_taken): - msg = "{num} loop{s} -> {secs:.{prec}g} secs" - plural = (number != 1) - print(msg.format(num=number, s='s' if plural else '', - secs=time_taken, prec=precision)) + s = "" if number == 1 else "s" + print( + f"{number} loop{s} " + f"{theme.punctuation}-> " + f"{theme.timing}{time_taken:.{precision}g} secs{reset}" + ) + try: number, _ = t.autorange(callback, target_time) except: @@ -371,24 +376,34 @@ def format_time(dt): return "%.*g %s" % (precision, dt / scale, unit) if verbose: - print("raw times: %s" % ", ".join(map(format_time, raw_timings))) + raw = f"{theme.punctuation}, ".join( + f"{theme.timing}{t}" for t in map(format_time, raw_timings) + ) + print(f"raw times: {raw}{reset}") print() timings = [dt / number for dt in raw_timings] - best = min(timings) - print("%d loop%s, best of %d: %s per loop" - % (number, 's' if number != 1 else '', - repeat, format_time(best))) - best = min(timings) worst = max(timings) + s = "" if number == 1 else "s" + print( + f"{number} loop{s}, best of {repeat}: " + f"{theme.best}{format_time(best)}{reset} " + f"{theme.per_loop}per loop{reset}" + ) + if worst >= best * 4: import warnings - warnings.warn_explicit("The test results are likely unreliable. " - "The worst time (%s) was more than four times " - "slower than the best time (%s)." - % (format_time(worst), format_time(best)), - UserWarning, '', 0) + + print(file=sys.stderr) + warnings.warn_explicit( + f"{theme.warning}The test results are likely unreliable. " + f"The {theme.warning_worst}worst time ({format_time(worst)})" + f"{theme.warning} was more than four times slower than the " + f"{theme.warning_best}best time ({format_time(best)})" + f"{theme.warning}.{reset}", + UserWarning, "", 0, + ) return None diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-04-08-06-59-23.gh-issue-115802.jqfZty.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-04-08-06-59-23.gh-issue-115802.jqfZty.rst new file mode 100644 index 00000000000000..13ed51be0e6c5c --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-04-08-06-59-23.gh-issue-115802.jqfZty.rst @@ -0,0 +1 @@ +Improve JIT code generation on Linux AArch64 by reducing the indirect call to external symbols. Patch by Diego Russo. diff --git a/Misc/NEWS.d/next/Library/2026-03-22-16-52-04.gh-issue-146292.rJvvs0.rst b/Misc/NEWS.d/next/Library/2026-03-22-16-52-04.gh-issue-146292.rJvvs0.rst new file mode 100644 index 00000000000000..40f3b386155cfc --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-03-22-16-52-04.gh-issue-146292.rJvvs0.rst @@ -0,0 +1,2 @@ +Add colour to :mod:`~http.server.BaseHTTPRequestHandler` logs, as used by +the :mod:`http.server` CLI. Patch by Hugo van Kemenade. diff --git a/Misc/NEWS.d/next/Library/2026-03-29-21-31-14.gh-issue-146609.BnshCt.rst b/Misc/NEWS.d/next/Library/2026-03-29-21-31-14.gh-issue-146609.BnshCt.rst new file mode 100644 index 00000000000000..854fcc32ab76e1 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-03-29-21-31-14.gh-issue-146609.BnshCt.rst @@ -0,0 +1 @@ +Add colour to :mod:`timeit` CLI output. Patch by Hugo van Kemenade. diff --git a/Modules/_testinternalcapi/test_cases.c.h b/Modules/_testinternalcapi/test_cases.c.h index 45cbc58b085851..4b6a1d29c7b2eb 100644 --- a/Modules/_testinternalcapi/test_cases.c.h +++ b/Modules/_testinternalcapi/test_cases.c.h @@ -3970,6 +3970,9 @@ _PyStackRef self_or_null; _PyStackRef *args; _PyStackRef res; + _PyStackRef c; + _PyStackRef s; + _PyStackRef value; /* Skip 1 cache entry */ /* Skip 2 cache entries */ // _GUARD_CALLABLE_METHOD_DESCRIPTOR_NOARGS @@ -4031,24 +4034,35 @@ stack_pointer = _PyFrame_GetStackPointer(frame); _Py_LeaveRecursiveCallTstate(tstate); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + if (res_o == NULL) { + JUMP_TO_LABEL(error); + } + c = callable; + s = args[0]; + res = PyStackRef_FromPyObjectSteal(res_o); + } + // _POP_TOP + { + value = s; + stack_pointer[-2 - oparg] = res; + stack_pointer[-1 - oparg] = c; + stack_pointer += -oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(self_stackref); + PyStackRef_XCLOSE(value); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; + } + // _POP_TOP + { + value = c; + stack_pointer += -1; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(callable); + PyStackRef_XCLOSE(value); stack_pointer = _PyFrame_GetStackPointer(frame); - if (res_o == NULL) { - JUMP_TO_LABEL(error); - } - res = PyStackRef_FromPyObjectSteal(res_o); } // _CHECK_PERIODIC_AT_END { - stack_pointer[0] = res; - stack_pointer += 1; - ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); int err = check_periodics(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 18bd7594b41a92..9e887ed84cfe0e 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -4948,7 +4948,7 @@ dummy_func( EXIT_IF(!Py_IS_TYPE(self, method->d_common.d_type)); } - op(_CALL_METHOD_DESCRIPTOR_NOARGS, (callable, self_or_null, args[oparg] -- res)) { + op(_CALL_METHOD_DESCRIPTOR_NOARGS, (callable, self_or_null, args[oparg] -- res, c, s)) { PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o; @@ -4963,15 +4963,16 @@ dummy_func( PyObject *res_o = _PyCFunction_TrampolineCall(cfunc, self, NULL); _Py_LeaveRecursiveCallTstate(tstate); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - PyStackRef_CLOSE(self_stackref); - DEAD(args); - DEAD(self_or_null); - PyStackRef_CLOSE(callable); - ERROR_IF(res_o == NULL); + if (res_o == NULL) { + ERROR_NO_POP(); + } + c = callable; + s = args[0]; + INPUTS_DEAD(); res = PyStackRef_FromPyObjectSteal(res_o); } - tier2 op(_CALL_METHOD_DESCRIPTOR_NOARGS_INLINE, (callable, args[oparg], cfunc/4 -- res)) { + tier2 op(_CALL_METHOD_DESCRIPTOR_NOARGS_INLINE, (callable, args[oparg], cfunc/4 -- res, c, s)) { assert(oparg == 1); _PyStackRef self_stackref = args[0]; PyObject *self = PyStackRef_AsPyObjectBorrow(self_stackref); @@ -4980,10 +4981,12 @@ dummy_func( PyObject *res_o = _PyCFunction_TrampolineCall(cfunc_v, self, NULL); _Py_LeaveRecursiveCallTstate(tstate); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - PyStackRef_CLOSE(self_stackref); - DEAD(args); - PyStackRef_CLOSE(callable); - ERROR_IF(res_o == NULL); + if (res_o == NULL) { + ERROR_NO_POP(); + } + c = callable; + s = args[0]; + INPUTS_DEAD(); res = PyStackRef_FromPyObjectSteal(res_o); } @@ -4994,6 +4997,8 @@ dummy_func( _GUARD_CALLABLE_METHOD_DESCRIPTOR_NOARGS + _CHECK_RECURSION_LIMIT + _CALL_METHOD_DESCRIPTOR_NOARGS + + POP_TOP + + POP_TOP + _CHECK_PERIODIC_AT_END; op(_GUARD_CALLABLE_METHOD_DESCRIPTOR_FAST, (callable, self_or_null, args[oparg] -- callable, self_or_null, args[oparg])) { diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 8f4da0c5a4042a..43a70315d81a86 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -17402,13 +17402,15 @@ break; } - case _CALL_METHOD_DESCRIPTOR_NOARGS_r01: { + case _CALL_METHOD_DESCRIPTOR_NOARGS_r03: { CHECK_CURRENT_CACHED_VALUES(0); assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); _PyStackRef *args; _PyStackRef self_or_null; _PyStackRef callable; _PyStackRef res; + _PyStackRef c; + _PyStackRef s; oparg = CURRENT_OPARG(); args = &stack_pointer[-oparg]; self_or_null = stack_pointer[-1 - oparg]; @@ -17428,33 +17430,31 @@ stack_pointer = _PyFrame_GetStackPointer(frame); _Py_LeaveRecursiveCallTstate(tstate); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(self_stackref); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; - ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(callable); - stack_pointer = _PyFrame_GetStackPointer(frame); if (res_o == NULL) { SET_CURRENT_CACHED_VALUES(0); JUMP_TO_ERROR(); } + c = callable; + s = args[0]; res = PyStackRef_FromPyObjectSteal(res_o); + _tos_cache2 = s; + _tos_cache1 = c; _tos_cache0 = res; - _tos_cache1 = PyStackRef_ZERO_BITS; - _tos_cache2 = PyStackRef_ZERO_BITS; - SET_CURRENT_CACHED_VALUES(1); + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -2 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); break; } - case _CALL_METHOD_DESCRIPTOR_NOARGS_INLINE_r01: { + case _CALL_METHOD_DESCRIPTOR_NOARGS_INLINE_r03: { CHECK_CURRENT_CACHED_VALUES(0); assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); _PyStackRef *args; _PyStackRef callable; _PyStackRef res; + _PyStackRef c; + _PyStackRef s; oparg = CURRENT_OPARG(); args = &stack_pointer[-oparg]; callable = stack_pointer[-1 - oparg]; @@ -17469,23 +17469,19 @@ stack_pointer = _PyFrame_GetStackPointer(frame); _Py_LeaveRecursiveCallTstate(tstate); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(self_stackref); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -1 - oparg; - ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(callable); - stack_pointer = _PyFrame_GetStackPointer(frame); if (res_o == NULL) { SET_CURRENT_CACHED_VALUES(0); JUMP_TO_ERROR(); } + c = callable; + s = args[0]; res = PyStackRef_FromPyObjectSteal(res_o); + _tos_cache2 = s; + _tos_cache1 = c; _tos_cache0 = res; - _tos_cache1 = PyStackRef_ZERO_BITS; - _tos_cache2 = PyStackRef_ZERO_BITS; - SET_CURRENT_CACHED_VALUES(1); + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -1 - oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); break; } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 127a45ef591053..c62dc450525860 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -3970,6 +3970,9 @@ _PyStackRef self_or_null; _PyStackRef *args; _PyStackRef res; + _PyStackRef c; + _PyStackRef s; + _PyStackRef value; /* Skip 1 cache entry */ /* Skip 2 cache entries */ // _GUARD_CALLABLE_METHOD_DESCRIPTOR_NOARGS @@ -4031,24 +4034,35 @@ stack_pointer = _PyFrame_GetStackPointer(frame); _Py_LeaveRecursiveCallTstate(tstate); assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); + if (res_o == NULL) { + JUMP_TO_LABEL(error); + } + c = callable; + s = args[0]; + res = PyStackRef_FromPyObjectSteal(res_o); + } + // _POP_TOP + { + value = s; + stack_pointer[-2 - oparg] = res; + stack_pointer[-1 - oparg] = c; + stack_pointer += -oparg; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(self_stackref); + PyStackRef_XCLOSE(value); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2 - oparg; + } + // _POP_TOP + { + value = c; + stack_pointer += -1; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(callable); + PyStackRef_XCLOSE(value); stack_pointer = _PyFrame_GetStackPointer(frame); - if (res_o == NULL) { - JUMP_TO_LABEL(error); - } - res = PyStackRef_FromPyObjectSteal(res_o); } // _CHECK_PERIODIC_AT_END { - stack_pointer[0] = res; - stack_pointer += 1; - ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); int err = check_periodics(tstate); stack_pointer = _PyFrame_GetStackPointer(frame); diff --git a/Python/optimizer_analysis.c b/Python/optimizer_analysis.c index 3f0aabed10e1db..ed1619df2a188b 100644 --- a/Python/optimizer_analysis.c +++ b/Python/optimizer_analysis.c @@ -155,7 +155,7 @@ type_watcher_callback(PyTypeObject* type) } static PyObject * -convert_global_to_const(_PyUOpInstruction *inst, PyObject *obj, bool pop, bool insert) +convert_global_to_const(_PyUOpInstruction *inst, PyObject *obj, bool insert) { assert(inst->opcode == _LOAD_GLOBAL_MODULE || inst->opcode == _LOAD_GLOBAL_BUILTINS || inst->opcode == _LOAD_ATTR_MODULE); assert(PyDict_CheckExact(obj)); @@ -183,9 +183,9 @@ convert_global_to_const(_PyUOpInstruction *inst, PyObject *obj, bool pop, bool i } } else { if (_Py_IsImmortal(res)) { - inst->opcode = pop ? _POP_TOP_LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE_BORROW; + inst->opcode = _LOAD_CONST_INLINE_BORROW; } else { - inst->opcode = pop ? _POP_TOP_LOAD_CONST_INLINE : _LOAD_CONST_INLINE; + inst->opcode = _LOAD_CONST_INLINE; } if (inst->oparg & 1) { assert(inst[1].opcode == _PUSH_NULL_CONDITIONAL); @@ -340,10 +340,12 @@ optimize_to_bool( int truthiness = sym_truthiness(ctx, value); if (truthiness >= 0) { PyObject *load = truthiness ? Py_True : Py_False; - int opcode = insert_mode ? - _INSERT_1_LOAD_CONST_INLINE_BORROW : - _POP_TOP_LOAD_CONST_INLINE_BORROW; - ADD_OP(opcode, 0, (uintptr_t)load); + if (insert_mode) { + ADD_OP(_INSERT_1_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)load); + } else { + ADD_OP(_POP_TOP, 0, 0); + ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)load); + } *result_ptr = sym_new_const(ctx, load); return 1; } @@ -389,19 +391,22 @@ eliminate_pop_guard(_PyUOpInstruction *this_instr, JitOptContext *ctx, bool exit static JitOptRef lookup_attr(JitOptContext *ctx, _PyBloomFilter *dependencies, _PyUOpInstruction *this_instr, - PyTypeObject *type, PyObject *name, uint16_t immortal, - uint16_t mortal) + PyTypeObject *type, PyObject *name, bool pop) { // The cached value may be dead, so we need to do the lookup again... :( if (type && PyType_Check(type)) { PyObject *lookup = _PyType_Lookup(type, name); if (lookup) { - int opcode = mortal; - // if the object is immortal or the type is immutable, borrowing is safe - if (_Py_IsImmortal(lookup) || (type->tp_flags & Py_TPFLAGS_IMMUTABLETYPE)) { - opcode = immortal; + bool immortal = _Py_IsImmortal(lookup) || (type->tp_flags & Py_TPFLAGS_IMMUTABLETYPE); + if (pop) { + ADD_OP(_POP_TOP, 0, 0); + ADD_OP(immortal ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE, + 0, (uintptr_t)lookup); + } + else { + ADD_OP(immortal ? _INSERT_1_LOAD_CONST_INLINE_BORROW : _INSERT_1_LOAD_CONST_INLINE, + 0, (uintptr_t)lookup); } - ADD_OP(opcode, 0, (uintptr_t)lookup); PyType_Watch(TYPE_WATCHER_ID, (PyObject *)type); _Py_BloomFilter_Add(dependencies, type); return sym_new_const(ctx, lookup); diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index 7a0aac11890306..3ba404b81ea043 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -515,18 +515,18 @@ dummy_func(void) { } op(_BINARY_OP_SUBSCR_DICT, (dict_st, sub_st -- res, ds, ss)) { - PyObject *sub = sym_get_const(ctx, sub_st); - if (sub != NULL) { - optimize_dict_known_hash(ctx, dependencies, this_instr, - sub, _BINARY_OP_SUBSCR_DICT_KNOWN_HASH); - } res = sym_new_not_null(ctx); ds = dict_st; ss = sub_st; + PyObject *sub = sym_get_const(ctx, sub_st); if (sym_is_not_container(sub_st) && sym_matches_type(dict_st, &PyFrozenDict_Type)) { REPLACE_OPCODE_IF_EVALUATES_PURE(dict_st, sub_st, res); } + else if (sub != NULL) { + optimize_dict_known_hash(ctx, dependencies, this_instr, + sub, _BINARY_OP_SUBSCR_DICT_KNOWN_HASH); + } } op(_BINARY_OP_SUBSCR_LIST_SLICE, (list_st, sub_st -- res, ls, ss)) { @@ -757,14 +757,6 @@ dummy_func(void) { value = PyJitRef_Borrow(sym_new_const(ctx, ptr)); } - op(_POP_TOP_LOAD_CONST_INLINE, (ptr/4, pop -- value)) { - value = sym_new_const(ctx, ptr); - } - - op(_POP_TOP_LOAD_CONST_INLINE_BORROW, (ptr/4, pop -- value)) { - value = PyJitRef_Borrow(sym_new_const(ctx, ptr)); - } - op(_POP_CALL_LOAD_CONST_INLINE_BORROW, (ptr/4, unused, unused -- value)) { value = PyJitRef_Borrow(sym_new_const(ctx, ptr)); } @@ -845,7 +837,7 @@ dummy_func(void) { if (watched_mutations < _Py_MAX_ALLOWED_GLOBALS_MODIFICATIONS) { PyDict_Watch(GLOBALS_WATCHER_ID, dict); _Py_BloomFilter_Add(dependencies, dict); - PyObject *res = convert_global_to_const(this_instr, dict, false, true); + PyObject *res = convert_global_to_const(this_instr, dict, true); if (res == NULL) { attr = sym_new_not_null(ctx); } @@ -898,8 +890,7 @@ dummy_func(void) { PyTypeObject *type = (PyTypeObject *)sym_get_const(ctx, owner); PyObject *name = get_co_name(ctx, oparg >> 1); attr = lookup_attr(ctx, dependencies, this_instr, type, name, - _POP_TOP_LOAD_CONST_INLINE_BORROW, - _POP_TOP_LOAD_CONST_INLINE); + true); } op(_LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES, (descr/4, owner -- attr)) { @@ -907,8 +898,7 @@ dummy_func(void) { PyTypeObject *type = sym_get_type(owner); PyObject *name = get_co_name(ctx, oparg >> 1); attr = lookup_attr(ctx, dependencies, this_instr, type, name, - _POP_TOP_LOAD_CONST_INLINE_BORROW, - _POP_TOP_LOAD_CONST_INLINE); + true); } op(_LOAD_ATTR_NONDESCRIPTOR_NO_DICT, (descr/4, owner -- attr)) { @@ -916,8 +906,7 @@ dummy_func(void) { PyTypeObject *type = sym_get_type(owner); PyObject *name = get_co_name(ctx, oparg >> 1); attr = lookup_attr(ctx, dependencies, this_instr, type, name, - _POP_TOP_LOAD_CONST_INLINE_BORROW, - _POP_TOP_LOAD_CONST_INLINE); + true); } op(_LOAD_ATTR_METHOD_WITH_VALUES, (descr/4, owner -- attr, self)) { @@ -925,8 +914,7 @@ dummy_func(void) { PyTypeObject *type = sym_get_type(owner); PyObject *name = get_co_name(ctx, oparg >> 1); attr = lookup_attr(ctx, dependencies, this_instr, type, name, - _INSERT_1_LOAD_CONST_INLINE_BORROW, - _INSERT_1_LOAD_CONST_INLINE); + false); self = owner; } @@ -935,8 +923,7 @@ dummy_func(void) { PyTypeObject *type = sym_get_type(owner); PyObject *name = get_co_name(ctx, oparg >> 1); attr = lookup_attr(ctx, dependencies, this_instr, type, name, - _INSERT_1_LOAD_CONST_INLINE_BORROW, - _INSERT_1_LOAD_CONST_INLINE); + false); self = owner; } @@ -945,8 +932,7 @@ dummy_func(void) { PyTypeObject *type = sym_get_type(owner); PyObject *name = get_co_name(ctx, oparg >> 1); attr = lookup_attr(ctx, dependencies, this_instr, type, name, - _INSERT_1_LOAD_CONST_INLINE_BORROW, - _INSERT_1_LOAD_CONST_INLINE); + false); self = owner; } @@ -1421,7 +1407,7 @@ dummy_func(void) { ctx->frame->is_c_recursion_checked = true; } - op(_CALL_METHOD_DESCRIPTOR_NOARGS, (callable, self_or_null, args[oparg] -- res)) { + op(_CALL_METHOD_DESCRIPTOR_NOARGS, (callable, self_or_null, args[oparg] -- res, c, s)) { PyObject *callable_o = sym_get_const(ctx, callable); if (callable_o && Py_IS_TYPE(callable_o, &PyMethodDescr_Type) && sym_is_not_null(self_or_null)) { @@ -1430,6 +1416,17 @@ dummy_func(void) { ADD_OP(_CALL_METHOD_DESCRIPTOR_NOARGS_INLINE, oparg + 1, (uintptr_t)cfunc); } res = sym_new_not_null(ctx); + c = callable; + if (sym_is_not_null(self_or_null)) { + args--; + s = args[0]; + } + else if (sym_is_null(self_or_null)) { + s = args[0]; + } + else { + s = sym_new_unknown(ctx); + } } op(_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS, (callable, self_or_null, args[oparg] -- res)) { @@ -2010,7 +2007,7 @@ dummy_func(void) { ctx->builtins_watched = true; } if (ctx->frame->globals_checked_version != 0 && ctx->frame->globals_watched) { - cnst = convert_global_to_const(this_instr, builtins, false, false); + cnst = convert_global_to_const(this_instr, builtins, false); } } if (cnst == NULL) { @@ -2049,7 +2046,7 @@ dummy_func(void) { ctx->frame->globals_checked_version = version; } if (ctx->frame->globals_checked_version == version) { - cnst = convert_global_to_const(this_instr, globals, false, false); + cnst = convert_global_to_const(this_instr, globals, false); } } } diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 82ac3c84f02880..2c8a0bff13ce35 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -314,8 +314,9 @@ if (sym_is_const(ctx, res)) { PyObject *result = sym_get_const(ctx, res); if (_Py_IsImmortal(result)) { - // Replace with _POP_TOP_LOAD_CONST_INLINE_BORROW since we have one input and an immortal result - ADD_OP(_POP_TOP_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)result); + // Replace with _POP_TOP + _LOAD_CONST_INLINE_BORROW since we have one input and an immortal result + ADD_OP(_POP_TOP, 0, 0); + ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)result); } } stack_pointer[-1] = res; @@ -1462,14 +1463,10 @@ JitOptRef ss; sub_st = stack_pointer[-1]; dict_st = stack_pointer[-2]; - PyObject *sub = sym_get_const(ctx, sub_st); - if (sub != NULL) { - optimize_dict_known_hash(ctx, dependencies, this_instr, - sub, _BINARY_OP_SUBSCR_DICT_KNOWN_HASH); - } res = sym_new_not_null(ctx); ds = dict_st; ss = sub_st; + PyObject *sub = sym_get_const(ctx, sub_st); if (sym_is_not_container(sub_st) && sym_matches_type(dict_st, &PyFrozenDict_Type)) { if ( @@ -1519,6 +1516,10 @@ break; } } + else if (sub != NULL) { + optimize_dict_known_hash(ctx, dependencies, this_instr, + sub, _BINARY_OP_SUBSCR_DICT_KNOWN_HASH); + } CHECK_STACK_BOUNDS(1); stack_pointer[-2] = res; stack_pointer[-1] = ds; @@ -2085,7 +2086,7 @@ ctx->frame->globals_checked_version = version; } if (ctx->frame->globals_checked_version == version) { - cnst = convert_global_to_const(this_instr, globals, false, false); + cnst = convert_global_to_const(this_instr, globals, false); } } } @@ -2128,7 +2129,7 @@ ctx->builtins_watched = true; } if (ctx->frame->globals_checked_version != 0 && ctx->frame->globals_watched) { - cnst = convert_global_to_const(this_instr, builtins, false, false); + cnst = convert_global_to_const(this_instr, builtins, false); } } if (cnst == NULL) { @@ -2438,7 +2439,7 @@ if (watched_mutations < _Py_MAX_ALLOWED_GLOBALS_MODIFICATIONS) { PyDict_Watch(GLOBALS_WATCHER_ID, dict); _Py_BloomFilter_Add(dependencies, dict); - PyObject *res = convert_global_to_const(this_instr, dict, false, true); + PyObject *res = convert_global_to_const(this_instr, dict, true); if (res == NULL) { attr = sym_new_not_null(ctx); } @@ -2519,8 +2520,7 @@ PyTypeObject *type = (PyTypeObject *)sym_get_const(ctx, owner); PyObject *name = get_co_name(ctx, oparg >> 1); attr = lookup_attr(ctx, dependencies, this_instr, type, name, - _POP_TOP_LOAD_CONST_INLINE_BORROW, - _POP_TOP_LOAD_CONST_INLINE); + true); stack_pointer[-1] = attr; break; } @@ -3413,8 +3413,7 @@ PyTypeObject *type = sym_get_type(owner); PyObject *name = get_co_name(ctx, oparg >> 1); attr = lookup_attr(ctx, dependencies, this_instr, type, name, - _INSERT_1_LOAD_CONST_INLINE_BORROW, - _INSERT_1_LOAD_CONST_INLINE); + false); self = owner; CHECK_STACK_BOUNDS(1); stack_pointer[-1] = attr; @@ -3434,8 +3433,7 @@ PyTypeObject *type = sym_get_type(owner); PyObject *name = get_co_name(ctx, oparg >> 1); attr = lookup_attr(ctx, dependencies, this_instr, type, name, - _INSERT_1_LOAD_CONST_INLINE_BORROW, - _INSERT_1_LOAD_CONST_INLINE); + false); self = owner; CHECK_STACK_BOUNDS(1); stack_pointer[-1] = attr; @@ -3454,8 +3452,7 @@ PyTypeObject *type = sym_get_type(owner); PyObject *name = get_co_name(ctx, oparg >> 1); attr = lookup_attr(ctx, dependencies, this_instr, type, name, - _POP_TOP_LOAD_CONST_INLINE_BORROW, - _POP_TOP_LOAD_CONST_INLINE); + true); stack_pointer[-1] = attr; break; } @@ -3469,8 +3466,7 @@ PyTypeObject *type = sym_get_type(owner); PyObject *name = get_co_name(ctx, oparg >> 1); attr = lookup_attr(ctx, dependencies, this_instr, type, name, - _POP_TOP_LOAD_CONST_INLINE_BORROW, - _POP_TOP_LOAD_CONST_INLINE); + true); stack_pointer[-1] = attr; break; } @@ -3489,8 +3485,7 @@ PyTypeObject *type = sym_get_type(owner); PyObject *name = get_co_name(ctx, oparg >> 1); attr = lookup_attr(ctx, dependencies, this_instr, type, name, - _INSERT_1_LOAD_CONST_INLINE_BORROW, - _INSERT_1_LOAD_CONST_INLINE); + false); self = owner; CHECK_STACK_BOUNDS(1); stack_pointer[-1] = attr; @@ -4319,9 +4314,13 @@ } case _CALL_METHOD_DESCRIPTOR_NOARGS: { + JitOptRef *args; JitOptRef self_or_null; JitOptRef callable; JitOptRef res; + JitOptRef c; + JitOptRef s; + args = &stack_pointer[-oparg]; self_or_null = stack_pointer[-1 - oparg]; callable = stack_pointer[-2 - oparg]; PyObject *callable_o = sym_get_const(ctx, callable); @@ -4332,19 +4331,38 @@ ADD_OP(_CALL_METHOD_DESCRIPTOR_NOARGS_INLINE, oparg + 1, (uintptr_t)cfunc); } res = sym_new_not_null(ctx); - CHECK_STACK_BOUNDS(-1 - oparg); + c = callable; + if (sym_is_not_null(self_or_null)) { + args--; + s = args[0]; + } + else if (sym_is_null(self_or_null)) { + s = args[0]; + } + else { + s = sym_new_unknown(ctx); + } + CHECK_STACK_BOUNDS(1 - oparg); stack_pointer[-2 - oparg] = res; - stack_pointer += -1 - oparg; + stack_pointer[-1 - oparg] = c; + stack_pointer[-oparg] = s; + stack_pointer += 1 - oparg; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } case _CALL_METHOD_DESCRIPTOR_NOARGS_INLINE: { JitOptRef res; + JitOptRef c; + JitOptRef s; res = sym_new_not_null(ctx); - CHECK_STACK_BOUNDS(-oparg); + c = sym_new_not_null(ctx); + s = sym_new_not_null(ctx); + CHECK_STACK_BOUNDS(2 - oparg); stack_pointer[-1 - oparg] = res; - stack_pointer += -oparg; + stack_pointer[-oparg] = c; + stack_pointer[1 - oparg] = s; + stack_pointer += 2 - oparg; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); break; } @@ -4857,8 +4875,7 @@ case _POP_TOP_LOAD_CONST_INLINE: { JitOptRef value; - PyObject *ptr = (PyObject *)this_instr->operand0; - value = sym_new_const(ctx, ptr); + value = sym_new_not_null(ctx); stack_pointer[-1] = value; break; } @@ -4897,8 +4914,7 @@ case _POP_TOP_LOAD_CONST_INLINE_BORROW: { JitOptRef value; - PyObject *ptr = (PyObject *)this_instr->operand0; - value = PyJitRef_Borrow(sym_new_const(ctx, ptr)); + value = sym_new_not_null(ctx); stack_pointer[-1] = value; break; } diff --git a/Tools/cases_generator/optimizer_generator.py b/Tools/cases_generator/optimizer_generator.py index 65896221ba7a05..495462b6bcd906 100644 --- a/Tools/cases_generator/optimizer_generator.py +++ b/Tools/cases_generator/optimizer_generator.py @@ -238,18 +238,23 @@ def replace_opcode_if_evaluates_pure( # Map input count to output index (from TOS) and the appropriate constant-loading uop input_count_to_uop = { 1: { - # (a -- a), usually for unary ops - 0: "_POP_TOP_LOAD_CONST_INLINE_BORROW", + # (a -- res), usually for unary ops + 0: [("_POP_TOP", "0, 0"), + ("_LOAD_CONST_INLINE_BORROW", + "0, (uintptr_t)result")], # (left -- res, left) # usually for unary ops with passthrough references - 1: "_INSERT_1_LOAD_CONST_INLINE_BORROW", + 1: [("_INSERT_1_LOAD_CONST_INLINE_BORROW", + "0, (uintptr_t)result")], }, 2: { - # (a. b -- res), usually for binary ops - 0: "_POP_TWO_LOAD_CONST_INLINE_BORROW", + # (a, b -- res), usually for binary ops + 0: [("_POP_TWO_LOAD_CONST_INLINE_BORROW", + "0, (uintptr_t)result")], # (left, right -- res, left, right) # usually for binary ops with passthrough references - 2: "_INSERT_2_LOAD_CONST_INLINE_BORROW", + 2: [("_INSERT_2_LOAD_CONST_INLINE_BORROW", + "0, (uintptr_t)result")], }, } @@ -263,14 +268,16 @@ def replace_opcode_if_evaluates_pure( assert output_index >= 0 input_count = len(used_stack_inputs) if input_count in input_count_to_uop and output_index in input_count_to_uop[input_count]: - replacement_uop = input_count_to_uop[input_count][output_index] + ops = input_count_to_uop[input_count][output_index] input_desc = "one input" if input_count == 1 else "two inputs" + ops_desc = " + ".join(op for op, _ in ops) emitter.emit(f"if (sym_is_const(ctx, {output_identifier.text})) {{\n") emitter.emit(f"PyObject *result = sym_get_const(ctx, {output_identifier.text});\n") emitter.emit(f"if (_Py_IsImmortal(result)) {{\n") - emitter.emit(f"// Replace with {replacement_uop} since we have {input_desc} and an immortal result\n") - emitter.emit(f"ADD_OP({replacement_uop}, 0, (uintptr_t)result);\n") + emitter.emit(f"// Replace with {ops_desc} since we have {input_desc} and an immortal result\n") + for op, args in ops: + emitter.emit(f"ADD_OP({op}, {args});\n") emitter.emit("}\n") emitter.emit("}\n") diff --git a/Tools/jit/_targets.py b/Tools/jit/_targets.py index 787fcf53260f3d..ea0a9722c3cdf8 100644 --- a/Tools/jit/_targets.py +++ b/Tools/jit/_targets.py @@ -594,7 +594,7 @@ def get_target(host: str) -> _COFF32 | _COFF64 | _ELF | _MachO: host = "aarch64-unknown-linux-gnu" condition = "defined(__aarch64__) && defined(__linux__)" # -mno-outline-atomics: Keep intrinsics from being emitted. - args = ["-fpic", "-mno-outline-atomics", "-fno-plt"] + args = ["-fpic", "-mno-outline-atomics"] optimizer = _optimizers.OptimizerAArch64 target = _ELF( host, condition, args=args, optimizer=optimizer, frame_pointers=True