gentoo-haskell progress for two weeks
Сегодня закончился мой двухнедельный отпуск. Подведем итоги свершенного:
В официальное дерево ебилдов добавил:
По ходу дела было закрыто 30 багов в gentoo bugzilla, относящихся к haskell
.
Команда gentoo-haskell
не может удержаться, чтобы не сообщить, что она добыла бинарники ghc
для следующих архитектур:
alpha
: 6.10.4-r1, 6.12.3 (собирается 8.5 часов)ia64
: 6.10.4-r1, 6.12.3 (собирается 7.5 часов)x86-fbsd
: 6.12.3 (не сообщают, но думаю часа 1.5, как и обычныйx86
)
Как это обычно бывает с экзотическими архитектурами, без патчей не обошлось.
На alpha
для ghc-6.10.4-r1
всё было хорошо, а в 6.12.3
сгнил кусочек кода,
отвечающий за высовывание haskell
функций в C
рантайм (так называемый
foreign import wrapper
aka f.i.w
, бывший foreign export dynamic
.
Это была ошибка времени компиляции, исправилась откатом на переносимую
реализацию замыканий в libffi
.
На ia64
всё было горадзо разнообразнее:
Собрал я бинарник 6.10.4
и пытался собрать 6.12.3
. GHC
доходил до фазы сборки
самого себя и падал с очень пространной ошибкой:
"inplace/bin/ghc-stage2" -H32m -O -H64m -O0 -w ...
ghc-stage2: internal error: evacuate: strange closure type 15
(GHC version 6.12.3 for ia64_unknown_linux)
Please report this as a GHC bug: http://www.haskell.org/ghc/reportabug
make[1]: *** [libraries/dph/dph-base/dist-install/build/Data/Array/Parallel/Base/Hyperstrict.o] Aborted
Ошибка гласит о том, что замыкание “статического” типа (в сегмете кода) якобы одновременно находится и в куче.
Проблема оказалась в виртуальных адресах регионов памяти на системе ia64
(спасибо zygoloid
).
Рассмотрим amd64
: это железо физически не может оперировать виртуальными адресами старше 48 бит
(и физическими старше 36 бит на десктопных процах). Судя по всему это ограничение того, как
представляется MMU
в кристалле: по аналогии с 32-битных (i386
) структура:
virtual_address = PAGE_DIR << (10 + 12) // 10 bits
| PAGE_TABLE << 12 // 10 bits
| PAGE // 12 bits // 4K pages
// all: 32 bits
Превратилась в:
virtual_address = // PML5 not implemented yet
PML4 << (?? + ?? + 13) // up to 8 bits
| PAGE_DIR << (?? + 13) // ?? bits
| PAGE_TABLE << 13 // ?? bits
| PAGE // 13 bits // 8K pages
// all: up to 48 bits
Посмотрим на распределение памяти в типичном процессе на amd64
:
$ pmap $$
23807: bash
0000000000400000 824K r-x-- /bin/bash # code
00000000006cd000 4K r---- /bin/bash # code
00000000006ce000 36K rw--- /bin/bash # PLT/GOT
00000000006d7000 24K rw--- [ anon ] # bss/rw data
0000000000ca9000 1876K rw--- [ anon ] # heap
00007f2370e49000 8K r-x-- /usr/lib64/gconv/KOI8-R.so # shared lib
...
00007f23725d3000 4K rw--- /lib64/ld-2.11.2.so # shlib PLT/GOT
00007f23725d4000 4K rw--- [ anon ] # bss/rw data
00007fff0f79b000 132K rw--- [ stack ] # stack!
00007fff0f7ff000 4K r-x-- [ anon ] # stack bottom, red zone? или вообще in-userspace vdso. хз
ffffffffff600000 4K r-x-- [ anon ] # vdso
total 25296K
Как видим, старших 4 байта адреса всегда равны нулю (кроме vdso
, там все единицы).
GHC
на этом и сэкономил. Минимальная самостоятельная единица, выделяемая storage manager
- 1MB
(20 bits).
Итого значащих битов: 48 - 20 = 28 bits. GHC
создает массив битов (выставленный бит означает,
что адрес выделен в heap
). Индексом в массиве является значащая часть указателя (28-битное значение).
Всё было бы хорошо, если бы остальные 64-битные архитектуры так делали, и они обычно так и делают (хотя и не обязаны).
На ppc64
, alpha
и amd64
всё так и есть (правда, всегда может измениться),
но на ia64
тип региона определяется старшим байтом в адресе.
Посмотрим на распределение памяти в типичном процессе на ia64
:
itanium $ pmap $$
30584: -bash
0000000000000000 16K r---- [ anon ] # zero address mapping (sysctl vm.mmap_min_addr)
2000000000000000 240K r-x-- /lib/ld-2.11.2.so # shlib code
2000000000048000 32K rw--- /lib/ld-2.11.2.so # shlib bss
...
4000000000000000 1744K r-x-- /bin/bash # code
6000000000000000 80K rw--- /bin/bash # bss/rw data
...
6000000000014000 304K rw--- [ anon ] # heap
600007ffffd74000 16K rw--- [ anon ]
60000fffffd1c000 336K rw--- [ stack ] # stack
a000000000000000 128K r-x-- [ anon ] # ? vdso?
total 7440K
Итак, что мы видим: если отбросить старшие 4 байта адреса из секций code
и heap
мы
можем получить (и получаем в нашем случае!) коллизию - то есть на статический код наш Ъ-алгоритм находит
соответствующий участок в памяти. Решение простое - не обрезать старшие биты на 64-битных не-amd64
.
Это снизит производительность сборщика мусора, но восстановит корректность.
Это позволилос собрать ghc-6.12.3
, но на этом дело не кончилось.
Собранный на 6.12.3
darcs-2.4.4
сразу начал падать. По тесту #3516
я быстро вычислил, что виноват опять-таки f.i.w
. Проблема ровно как с
ppc64
, фиксилось также - откатом на libffi
.
С x86-freebsd
проблем не было за исключением того, что в версии платформы ghc
никак не ожидал видеть
i686-gentoo-freebsd8
.
Это фиксится локальными хаками в gentoo
.
Итого, текущая картина по бинарникам:
package alpha amd64 ia64 ppc ppc64 sparc x86 x86-fbsd
----------------------------------- ------- ------- ------ ----- ------- ------- ----- ----------
ghc-6.12.3 ~alpha ~amd64 ~ia64 ~ppc64 ~x86 ~x86-fbsd
ghc-6.12.1 ~amd64 ~ppc ~ppc64 ~sparc ~x86
ghc-6.10.4-r1 ~alpha ~amd64 ~ia64 ~ppc ~ppc64 ~sparc ~x86
ghc-6.2.2 ppc sparc x86
ghc-6.4.2 ~alpha amd64 ~ia64 ppc ppc64 sparc x86 ~x86-fbsd
ghc-6.6 ~alpha ~amd64 ~ppc ~sparc ~x86
ghc-6.6.1 ~alpha amd64 ~ia64 ppc sparc x86
ghc-6.8.2 alpha amd64 ia64 ~ppc sparc x86
ghc-6.8.2-r1 amd64 x86
Остались ppc
, ppc64
и sparc
:)
Отдельное спасибо Сергею Семашко (aka ssvb
) за предоставленный доступ к G5
(ppc64
).
Без опыта, полученного исправлением там ghc
я бы не зафиксил все эти
баги (ну как минимум так быстро).