Ответ 1
Обработка исключений OCaml
Он не использует setjmp/longjmp
. Когда a try <expr> with <handle>
оценивается, в стек помещается "ловушка", которая содержит информацию об обработчике. Адрес верхней ловушки хранится в регистре¹, а когда вы поднимаете, он переходит непосредственно к этой ловушке, разматывая несколько кадров стека за один раз (это лучше, чем проверка каждого кода возврата). Ловушка также сохраняет адрес предыдущей ловушки, которая восстанавливается в регистре во время рейса.
¹: или глобальный, на архитектурах с недостаточным количеством регистров
Вы можете убедиться сами в коде:
- компиляция байткода: строки 635-641, два
Kpushtrap/Kpoptrap
байт-кода окружают выражениеtry..with
ed - родная компиляция: строки 254-260, снова инструкции
Lpushtrap/Lpoptrap
вокруг выражения - выполнение байт-кода для байт-кода
PUSHTRAP
(помещает ловушку/обработчик),POPTRAP
(удалить его, случай без ошибок) иRAISE
( прыжок в ловушку) - собственный код на mips и на amd64 (например)
Сравнение с setjmp
Ocaml использует нестандартное соглашение о вызове с небольшим количеством регистров или без них, что делает эту (и хвостовую рекурсию) эффективной. Я полагаю (но я не эксперт), что причина, по которой C longjmp/setjmp
не так эффективна на большинстве архитектур. См., Например, эту реализацию x86_64 setjmp, которая выглядит точно так же, как и предыдущий механизм ловушки плюс резервные регистры.
Это учитывается в интерфейсе C/OCaml: обычный способ вызова функции Caml из C-кода, caml_callback
, не улавливает OCaml исключения за пределами страны; вы должны использовать конкретный caml_callback_exn
, если хотите, который настраивает свой обработчик ловушки и сохраняет/восстанавливает регистры, записанные с помощью вызываемого абонента, из соглашения о вызове C. См. Например. код amd64, который сохраняет регистры и затем переходит к этой метке, чтобы настроить ловушку исключения.