Запись функций x86 asm переносимо (win/linux/osx), без зависимости от зависимости от yasm/nasm?

par2 имеет небольшую и довольно чистую С++-кодовую базу, которая, как я думаю, прекрасно подходит для GNU/Linux, OS X и Windows ( с MSVС++).

Я хотел бы включить версию x86-64 asm одной функции, которая занимает почти все время процессора. (рассылки с более подробными сообщениями. реализация /benchmark здесь.)

Intrinsics будет очевидным решением, но gcc не создает достаточно хороший код для получения одного байта за раз из 64-битного регистра для использования в качестве индекса в LUT. Я также мог бы потратить время, чтобы запланировать инструкции, поэтому каждая строка кэша uop содержит несколько 4-х тонов, поскольку пропускная способность uop является узким местом, даже если буфер ввода/вывода является приличным размером.

Я бы предпочел не вводить зависимость от ясности, поскольку многие люди установили gcc, но не yasm.

Есть ли способ написать функцию в asm в отдельном файле, который может собирать gcc/clang и MSVC? Цели:

  • нет дополнительного программного обеспечения в качестве сборки. (без YASM).
  • только одна версия каждой функции asm. (без поддержки версий MASM и AT & T того же кода.)

Системы сборки Par2cmdline - это autoconf/automake для Unix, MSVC .sln для Windows.

Я знаю, что сборник GNU имеет директиву .intel_syntax noprefix, но это только изменяет форматы команд, а не другие директивы ассемблера. например .align 16 против align 16. Мой код довольно прост и мал, поэтому было бы нормально работать с различными директивами с C-препроцессором #define s, если это может работать.

Я предполагаю, что выполнение обнаружения ЦП и установка указателя функции на основе результата не должны быть проблемой на С++, даже если я должен использовать для этого условную компиляцию #ifdef.

Если нет решения для того, на что я надеюсь, я, вероятно, добавлю зависящую от построения зависимость от yasm и имею параметр ./configure --no-asm, чтобы отключить ускорение asm для людей, строящих на x86 без присутствия язвы.

Мой предпочтительный план обработки различных соглашений о вызовах в Windows и Linux ABI состоял в использовании __attribute__((sysv_abi)) для моих прототипов C для моих функций asm. Тогда мне нужно написать пролог функции для SysV ABI. Есть ли у MSVC что-то вроде этого, что поставит args в regs в соответствии с SysV ABI для определенных функций? (BTW, это щекотало ошибка компилятора, поэтому будьте осторожны с этой идеей, если вы хотите, чтобы ваш код работал с текущим gcc.)

Ответ 1

В то время как у меня нет хорошего решения по удалению зависимости от конкретного ассемблера, у меня есть предложение о том, как разрешить две разностные соглашения с 64-битными вызовами: Microsoft x64 и SysV ABI.

Самый младший значащий знаменатель - это соглашения о вызовах Microsoft x64, поскольку он может передавать только первые четыре значения по регистру. Поэтому, если вы ограничиваете себя этим и используете макросы для определения регистров, вы можете легко сделать свой код для Unix (Linux/BSD/OSX) и Windows.

Например, посмотрите в файле strcat64.asm в Agner Fog asmlib

%IFDEF  WINDOWS
%define Rpar1   rcx                    ; function parameter 1
%define Rpar2   rdx                    ; function parameter 2
%define Rpar3   r8                     ; function parameter 3
%ENDIF
%IFDEF  UNIX
%define Rpar1   rdi                    ; function parameter 1
%define Rpar2   rsi                    ; function parameter 2
%define Rpar3   rdx                    ; function parameter 3
%ENDIF

        push    Rpar1                  ; dest
        push    Rpar2                  ; src
        call    A_strlen               ; length of dest
        push    rax                    ; strlen(dest)
        mov     Rpar1, [rsp+8]         ; src
        call    A_strlen               ; length of src
        pop     Rpar1                  ; strlen(dest)
        pop     Rpar2                  ; src
        add     Rpar1, [rsp]           ; dest + strlen(dest)
        lea     Rpar3, [rax+1]         ; strlen(src)+1
        call    A_memcpy               ; copy
        pop     rax                    ; return dest
        ret

;A_strcat ENDP

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