Я пытался найти соглашение о вызове OCaml, чтобы вручную интерпретировать трассировки стека, которые gdb не может проанализировать. К сожалению, кажется, что ничто никогда не было записано на английском языке, кроме общих наблюдений. Например, люди будут комментировать блоги, что OCaml передает множество аргументов в регистры. (Если где-то есть английская документация, ссылка будет очень оценена.)
Итак, я пытался разобраться с источником ocamlopt. Может ли кто-нибудь подтвердить точность этих догадок?
И, если я прав насчет первых десяти аргументов, передаваемых в регистры, можно ли вообще восстановить аргументы для вызова функции? В C аргументы все равно будут помещены в стек где-нибудь, если только я вернусь к правильному кадру. В OCaml, казалось бы, призывы могут уничтожить аргументы своих вызывающих.
Распределение регистров (от /asmcomp/amd64/proc.ml
)
Для вызова функций OCaml
- Первые 10 целых и указательных аргументов передаются в регистры rax, rbx, rdi, rsi, rdx, rcx, r8, r9, r10 и r11
- Первые 10 аргументов с плавающей запятой передаются в регистры xmm0 - xmm9
- Дополнительные аргументы переносятся в стек (leftmost-first-in?), поплавки и ints и указатели перемежаются
- Указатель ловушки (см. Исключения ниже) передается в r14
- Указатель выделения (предположительно для небольшой кучи, описанный в этом сообщении в блоге) передается в r15
- Возвращаемое значение передается обратно в rax, если оно является целым числом или указателем, а в xmm0, если оно является float
- Все регистры - это сохранение звонящего?
Для вызова функций C используется стандартное соглашение amd64 C:
- Первые шесть аргументов integer и pointer передаются в rdi, rsi, rdx, rcs, r8 и r9
- Первые восемь аргументов float передаются в xmm0 - xmm7
- Дополнительные аргументы помещаются в стек
- Возвращаемое значение возвращается в rax или xmm0
- Регистры rbx, rbp и r12 - r15 - это callee-save
Обратный адрес (от /asmcomp/amd64/emit.mlp
)
Обратный адрес - это первый указатель, введенный в кадр вызова, в соответствии с соглашением amd64 C. (Я предполагаю, что инструкция ret
предполагает этот макет.)
Исключения (от /asmcomp/linearize.ml
)
Код try (...body...) with (...handler...); (...rest...)
линеаризуется следующим образом:
Lsetuptrap .body
(...handler...)
Lbranch .join
Llabel .body
Lpushtrap
(...body...)
Lpoptrap
Llabel .join
(...rest...)
а затем выбрано как сборка (это пункты назначения справа):
call .body
(...handler...)
jmp .join
.body:
pushq %r14
movq %rsp, %r14
(...body...)
popq %r14
addq %rsp, 8
.join:
(...rest...)
Где-то в теле есть линеаризованный код операции Lraise
, который испускается как эта точная сборка:
movq %r14, %rsp
popq %r14
ret
Это действительно аккуратно! Вместо этого бизнеса setjmp/longjmp мы создаем фиктивный фрейм, обратный адрес которого является обработчиком исключений и единственным локальным из которых является предыдущий такой фиктивный фрейм. /asmcomp/amd64/proc.ml
имеет комментарий, вызывающий $r14 "указатель на ловушку", поэтому я буду называть этот фиктивный фрейм ловушкой. Когда мы хотим создать исключение, мы устанавливаем указатель стека на самый последний ловушечный кадр, перед этим устанавливаем указатель ловушки на ловушку, а затем "возвращаемся" в обработчик исключений. И я уверен, если обработчик исключений не может справиться с этим исключением, он просто ререйзит.
Исключение составляет% eax.