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