Вызов функции C / Assembly из OCaml более напрямую, чем с помощью caml_c_call - PullRequest
7 голосов
/ 26 сентября 2011

OCaml позволяет вызывать функции C из программ OCaml, если программист следует инструкциям в главе «Взаимодействие C с OCaml».

При выполнении этих инструкций вызовФункция Си переведена собственным компилятором в:

    movq    ml_as_z_sub@GOTPCREL(%rip), %rax
    call    caml_c_call@PLT

(здесь указана инструкция amd64, но, глядя на другие архитектуры, схема выглядит довольно равномерной).

Функция caml_c_call в конце концов делает вычисленный переход call *%rax, но он делает много вещей до и после.От asmrun / amd64.S:

/* Call a C function from Caml */

FUNCTION(G(caml_c_call))
.Lcaml_c_call:
    /* Record lowest stack address and return address */
        popq    %r12
        STORE_VAR(%r12, caml_last_return_address)
        STORE_VAR(%rsp, caml_bottom_of_stack)
    /* Make the exception handler and alloc ptr available to the C code */
        STORE_VAR(%r15, caml_young_ptr)
        STORE_VAR(%r14, caml_exception_pointer)
    /* Call the function (address in %rax) */
        call    *%rax
    /* Reload alloc ptr */
        LOAD_VAR(caml_young_ptr, %r15)
    /* Return to caller */
        pushq   %r12
        ret

Когда кто-то хочет часто выполнять пару инструкций, которые не выделяют и не генерируют исключения, вышеприведенное немного излишне.

Кто-нибудь имеетЕсть ли у вас опыт вызова небольшой процедуры сборки непосредственно из OCaml без прохождения заглушки caml_c_call?Вероятно, для этого нужно обманом заставить собственный компилятор думать, что он вызывает функцию ML, или модифицировать компилятор.

Вопрос заключается в контексте библиотеки Zarith, где небольшие биты кода сборки могут вычислить и вернуть большинстворезультаты напрямую, без необходимости проходить через caml_c_call и переходить к caml_c_code только для сложных аргументов, требующих выделения или исключения.См. этот файл с примерами битов сборки, которые могут быть выполнены напрямую.

Ответы [ 3 ]

8 голосов
/ 26 сентября 2011

Может быть "noalloc" и "float" могут быть полезны?

PS еще ссылки по теме .

5 голосов
/ 28 сентября 2011

Похоже, вы не будете возражать против затрат на вызов функции OCaml, если вызываемая вами функция может быть написана на ассемблере.Я только что провел некоторые эксперименты, и вы можете сделать это способом, описанным выше.

Вот что я сделал.Чтобы получить работающий шаблон на ассемблере, я определил простую функцию в OCaml и скомпилировал с флагом -S.

$ cat sep.ml
let addto x = x + 1
$ /usr/local/ocaml312/bin/ocamlopt -inline 0 -c -S sep.ml

Примечание: вам нужно указать -inline 0, чтобы убедиться, чтоocamlopt берет код из вашего сгенерированного файла .o, а не из встроенного определения в файле .cmx.

Теперь у вас есть файл с именем sep.s.Функция addto выглядит следующим образом (на самом деле удивительно хороший код):

_camlSep__addto_1030:
.L100:
        addq    $2, %rax
        ret

Просто для теста я изменил 2 (который представляет 1 в OCaml) на 4 (который представляет 2 в OCaml),Итак, теперь у вас есть:

_camlSep__addto_1030:
.L100:
        addq    $4, %rax
        ret

Теперь соберите этот файл, создавая отклоняющуюся версию sep.o.

$ as -o sep.o sep.s

По сути, вы обманули ocamlopt для обработки кода в sep.о как будто это было закодировано в OCaml.Но вы можете написать код самостоятельно в сборке (если вы будете осторожны, чтобы не нарушить какие-либо архитектурные предположения).

Вы можете связать его с основной программой и запустить:

$ cat main.ml
let main () =
    Printf.printf "%d\n" (Sep.addto 19)

let () = main ()
$ /usr/local/ocaml312/bin/ocamlopt -o main sep.cmx main.ml
$ main
21

Как видите, он запускает измененный код сборки.

Вы можете выполнить эту процедуру для создания любых функций, вызываемых OCaml, в коде сборки.Пока вы не возражаете против накладных расходов на вызов функции OCaml, этот подход может делать то, что вы хотите.

Я не знаю, как эта хитрость повлияет на обработку отладки и сборку мусора, поэтому яЯ бы не стал делать это с функцией, которая выполняет какое-либо распределение.

Эти тесты были запущены на Mac OS X 10.6.8 с использованием OCaml 3.12.0 (стандартная 64-битная сборка).Когда я запускаю «как», я запускаю стандартный ассемблер OS X из Xcode 4.0.2, который по умолчанию использует архитектуру x86_64.

2 голосов
/ 26 сентября 2011

Мне кажется, это не поможет обмануть компилятор, заставляя его думать, что он вызывает функцию OCaml, если только вы не обманываете его, вставляя вызов. Насколько я могу судить, просматривая источники, встроенные функции выражаются в так называемом коде Ulambda, который, в свою очередь, содержит примитивы. Таким образом, этот образ мышления, в любом случае, приводит к добавлению примитивов для ваших операций Zarith. Если вы это сделаете, у вас есть хорошее (совсем не хитрое) решение, но это может быть больше работы, чем вы хотите.

Для действительно сложного подхода вы можете попробовать пост-обработку сгенерированного кода asm, чтобы удалить вызовы функций и заменить их встроенным кодом. Этот вид трюка использовался много раз. Обычно это не длится долго, но может быть достаточно хорошим в краткосрочной перспективе. Чтобы сделать это, вам нужно просто указать компилятору OCaml имя другого ассемблера, который будет выполнять ваши изменения перед сборкой.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...