bpo-45850: Implement deep-freeze on Windows (#29648)

Implement changes to build with deep-frozen modules on Windows.
Note that we now require Python 3.10 as the "bootstrap" or "host" Python.
This causes a modest startup speed (around 7%) on Windows.
This commit is contained in:
Guido van Rossum 2021-11-22 10:09:48 -08:00 committed by GitHub
parent 4d6c0c0cce
commit 1037ca5a8e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 500 additions and 24 deletions

View file

@ -1,13 +1,16 @@
import argparse
import ast
import builtins
import collections
import contextlib
import os
import sys
import re
import time
import types
import typing
import umarshal
verbose = False
@ -55,7 +58,8 @@ def get_localsplus_counts(code: types.CodeType,
nplaincellvars += 1
elif kind & CO_FAST_FREE:
nfreevars += 1
assert nlocals == len(code.co_varnames) == code.co_nlocals
assert nlocals == len(code.co_varnames) == code.co_nlocals, \
(nlocals, len(code.co_varnames), code.co_nlocals)
assert ncellvars == len(code.co_cellvars)
assert nfreevars == len(code.co_freevars)
assert len(names) == nlocals + nplaincellvars + nfreevars
@ -274,14 +278,7 @@ class Printer:
self.write(item + ",")
return f"& {name}._object.ob_base.ob_base"
def generate_int(self, name: str, i: int) -> str:
maxint = sys.maxsize
if maxint == 2**31 - 1:
digit = 2**15
elif maxint == 2**63 - 1:
digit = 2**30
else:
assert False, f"What int size is this system?!? {maxint=}"
def _generate_int_for_bits(self, name: str, i: int, digit: int) -> None:
sign = -1 if i < 0 else 0 if i == 0 else +1
i = abs(i)
digits: list[int] = []
@ -298,6 +295,20 @@ class Printer:
if digits:
ds = ", ".join(map(str, digits))
self.write(f".ob_digit = {{ {ds} }},")
def generate_int(self, name: str, i: int) -> str:
if abs(i) < 2**15:
self._generate_int_for_bits(name, i, 2**15)
else:
connective = "if"
for bits_in_digit in 15, 30:
self.write(f"#{connective} PYLONG_BITS_IN_DIGIT == {bits_in_digit}")
self._generate_int_for_bits(name, i, 2**bits_in_digit)
connective = "elif"
self.write("#else")
self.write('#error "PYLONG_BITS_IN_DIGIT should be 15 or 30"')
self.write("#endif")
# If neither clause applies, it won't compile
return f"& {name}.ob_base.ob_base"
def generate_float(self, name: str, x: float) -> str:
@ -326,7 +337,7 @@ class Printer:
return self.cache[key]
self.misses += 1
match obj:
case types.CodeType() as code:
case types.CodeType() | umarshal.Code() as code:
val = self.generate_code(name, code)
case tuple(t):
val = self.generate_tuple(name, t)
@ -367,8 +378,31 @@ _Py_get_%%NAME%%_toplevel(void)
}
"""
FROZEN_COMMENT = "/* Auto-generated by Programs/_freeze_module.c */"
FROZEN_DATA_LINE = r"\s*(\d+,\s*)+\s*"
def is_frozen_header(source: str) -> bool:
return source.startswith(FROZEN_COMMENT)
def decode_frozen_data(source: str) -> types.CodeType:
lines = source.splitlines()
while lines and re.match(FROZEN_DATA_LINE, lines[0]) is None:
del lines[0]
while lines and re.match(FROZEN_DATA_LINE, lines[-1]) is None:
del lines[-1]
values: tuple[int, ...] = ast.literal_eval("".join(lines))
data = bytes(values)
return umarshal.loads(data)
def generate(source: str, filename: str, modname: str, file: typing.TextIO) -> None:
code = compile(source, filename, "exec")
if is_frozen_header(source):
code = decode_frozen_data(source)
else:
code = compile(source, filename, "exec")
printer = Printer(file)
printer.generate("toplevel", code)
printer.write("")