64 位 PyPy 和 Windows

让 64 位 Windows 与 PyPy 协同工作是一个长期存在的请求,最终在 2020 年这个充满诅咒的一年里得到了解决。问题在于,我们假设 RPython 的整数类型(rffi.Signed)足够大,可以(偶尔)容纳一个指针值转换为整数。在大多数平台上,相应的 C 类型 long 满足此条件。但在 64 位 Windows 上,long 是 32 位的,而 sizeof(void*) 是 64 位的。最简单的解决方法是确保 rffi.Signed 可以容纳一个 64 位整数,这会导致 python2 在 Win64 上出现以下 CPython 和 PyPy 之间的兼容性问题

CPython: sys.maxint == 2**31-1, sys.maxsize == 2**63-1

PyPy: sys.maxint == sys.maxsize == 2**63-1

…相应地,PyPy2 支持的整数大小上限为 sys.maxint 的较大值,在此之后它们将被转换为 long

我们做了什么

首先,我们对 CPython2 进行了一些修改,使其符合这种模式:用一个 long long 字段替换 PyIntObject 中的字段,并更改 sys.maxint 的值。这在 nulano 的 cpython 分支 中可用(再次强调:这是 python2)。

这个修改后的 python 用于接下来的步骤。我们将其称为 CPython64/64。

首先,在 rpython/translator/c/test/ 中的测试,例如 test_standalone.pytest_newgc.py,需要在 CPython64/64 之上通过。

这运行了一些小的翻译,并且一些细节是错误的。最明显的是,在所有其他平台上,整数类型 Signed 等于 long,但在 Win64 上,它应该类似于 long long

然后,我们对 rpython/translator/c/src 中的所有 C 文件进行了更广泛的审查,以查找单词 long,它在 Win64 上也表示一个 32 位整数,并将其替换为 Signed

然后,这两个 C 类型分别对应于 RPython 类型:rffi.LONGlltype.Signed。第一个应该真正对应于 C long,如 test_rffi_sizeof 测试所验证。后者的尺寸在 rpython/rlib/rarithmetic 中得到验证。

一旦这些基本测试通过,我们审查了 rpython/rlib/rffi.LONGlltype.Signed 的使用情况。目标是通过修复测试来解决更多 LONG-versus-Signed 问题——如往常一样,在 CPython64/64 之上运行。请注意,在 rarithmetic 中进行了一些早期工作,其目标是在常规 CPython 上运行 Win64 上的所有测试,但这些早期工作被放弃,因为这是一个糟糕的想法。只关注 CPython64/64。

这足以获得一个使用 -O2 选项编译的 PyPy 翻译版本,它包含最小的模块集,从 --no-allworkingmodules 开始;并使用 CPython64/64 来运行此翻译。仔细检查 C 编译器最后的警告,发现还需要进行更多工作。默认情况下,MSVC 会将许多整数大小不匹配报告为警告,而不是错误。

然后,我们检查了 pypy/module/*/ 目录,以查找 LONG-versus-Signed 问题。这让我们在 Windows 64 上获得了一个可工作的 PyPy 翻译版本,它包含所有 --translationmodules,即运行翻译所需的一切。一旦我们有了它,经过修改的 CPython64/64 就变得不那么重要了,因为我们可以在这个翻译后的 PyPy 之上运行未来的翻译。这使得它进入了默认分支的 nightly builds,任何想要在 Win64 上工作的人都需要使用它。整个过程最终导致了一种奇怪的依赖关系——我们需要一个翻译后的 PyPy 来翻译 PyPy——但这在这里是可以接受的,因为 Windows 可执行文件应该永远不会被更新版本的 Windows 破坏。

祝您编程愉快! :-)