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.py
和 test_newgc.py
,需要在 CPython64/64 之上通过。
这运行了一些小的翻译,并且一些细节是错误的。最明显的是,在所有其他平台上,整数类型 Signed
等于 long
,但在 Win64 上,它应该类似于 long long
。
然后,我们对 rpython/translator/c/src
中的所有 C 文件进行了更广泛的审查,以查找单词 long
,它在 Win64 上也表示一个 32 位整数,并将其替换为 Signed
。
然后,这两个 C 类型分别对应于 RPython 类型:rffi.LONG
和 lltype.Signed
。第一个应该真正对应于 C long
,如 test_rffi_sizeof
测试所验证。后者的尺寸在 rpython/rlib/rarithmetic
中得到验证。
一旦这些基本测试通过,我们审查了 rpython/rlib/
中 rffi.LONG
与 lltype.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 破坏。
祝您编程愉快! :-)