exception compiler-construction ocaml internals longjmp

exception - Internas OCaml: Excepciones



compiler-construction internals (1)

Tengo curiosidad por saber cómo se manejan las excepciones en el tiempo de ejecución de OCaml para que sean tan ligeras. ¿Utilizan setjmp / longjmp o devuelven un valor especial en cada función y lo propagan?

Me parece que longjmp pondría un poco de tensión en el sistema, pero solo cuando se produce una excepción, mientras que la verificación de cada función, el valor de retorno tendría que verificar cada uno de los valores después de llamar a una función, lo que me parece que pondría Un montón de controles y saltos, y parece que se desempeñaría peor.

Al observar cómo OCaml interactúa con C ( http://caml.inria.fr/pub/docs/manual-ocaml/manual032.html#toc142 ), y al ver callback.h, parece que una excepción se etiqueta usando la alineación de memoria de los objetos (#define Is_exception_result (v) (((v) & 3) == 2)). Esto parece indicar que su implementación no usa longjmp y verifica el resultado de cada función después de cada llamada de función. ¿Es asi? ¿O la función C ya intenta detectar alguna excepción y luego la convierte a este formato?

¡Gracias!


Manejo de excepciones OCaml

No usa setjmp/longjmp . Cuando se evalúa un try <expr> with <handle> , se coloca una "trampa" en la pila, que contiene información sobre el controlador. La dirección de la trampa superior se mantiene en un registro¹, y cuando sube, salta directamente a esta trampa, desenrollando varios marcos de pila de una sola vez (esto es mejor que verificar cada código de retorno). Una trampa también almacena la dirección de la trampa anterior, que se restaura en el registro en el momento de subida.

¹: o una global, en arquitecturas sin registros suficientes

Puedes verlo por ti mismo en el código:

  • compilación de bytecode : líneas 635-641, dos bytecodes Kpushtrap/Kpoptrap rodean la expresión try..with ed
  • compilación nativa : líneas 254-260, nuevamente instrucciones Lpushtrap/Lpoptrap alrededor de la expresión
  • ejecución de bytecode para el bytecode PUSHTRAP (coloca la captura / manejador), POPTRAP (eliminarlo, caso sin error) y RAISE (saltar a la captura)
  • emisión de código nativo en mips y en amd64 (por ejemplo)

Comparación con setjmp

Ocaml utiliza una convención de llamadas no estándar con pocos o ningún registro guardado por el usuario, lo que hace que esto (y la recursión de la cola) sea eficiente. Supongo que (pero no soy un experto) esa es la razón por la que C longjmp/setjmp no es tan eficiente en la mayoría de las arquitecturas. Vea, por ejemplo, esta implementación de setjmp x86_64 que se ve exactamente igual que el mecanismo de captura anterior más el guardado de registros de llamadas.

Esto se tiene en cuenta en la interfaz C / OCaml : la forma habitual de llamar a una función Caml desde el código C, caml_callback , no captura las excepciones de OCaml-land; tiene que usar un caml_callback_exn específico si lo desea, que configura su controlador de trampas y guarda / restaura los registros de la convención de llamadas C guardados por el usuario. Ver por ejemplo El código amd64 , que guarda los registros, luego salta a esta etiqueta para configurar la captura de excepción.