Как обеспечить 16-байтовое выравнивание кода подпрограмм Delphi? - PullRequest
4 голосов
/ 05 декабря 2009

Справочная информация:

У меня есть блок оптимизированных подпрограмм Delphi / BASM, в основном для тяжелых вычислений. Некоторые из этих подпрограмм содержат внутренние циклы, для которых я могу добиться значительного ускорения, если начало цикла выровнено по границе DQWORD (16-байт). Я могу гарантировать, что рассматриваемые петли выровнены по желанию, если я знаю выравнивание в обычной точке входа.

Насколько я вижу, компилятор Delphi выравнивает процедуры / функции по границам DWORD и, например, добавление функций к устройству может изменить выравнивание последующих. Однако, пока я дополняю конец подпрограмм кратными 16, я могу гарантировать, что последующие подпрограммы также выровнены - или выровнены, в зависимости от выравнивания первой подпрограммы. Поэтому я попытался поместить критические подпрограммы в начале раздела реализации модуля и поместить перед ними немного кода дополнения, чтобы первая процедура была выровнена по DQWORD.

Это выглядит примерно так:

interface

procedure FirstProcInUnit;

implementation

procedure __PadFirstProcTo16;
asm
    // variable number of NOP instructions here to get the desired code length
end;

procedure FirstProcInUnit;
asm //should start at DQWORD boundary
    //do something
    //padding to align the following label to DQWORD boundary
    @Some16BAlignedLabel:
        //code, looping back to @Some16BAlignedLabel
    //do something else
    ret #params
    //padding to get code length to multiple of 16
end;

initialization

__PadFirstProcTo16; //call this here so that it isn't optimised out
ASSERT ((NativeUInt(Pointer(@FirstProcInUnit)) AND $0F) = 0, 'FirstProcInUnit not DQWORD aligned');

end.

Это что-то вроде боли в шее, но я могу заставить такого рода вещи работать, когда это необходимо. Проблема в том, что когда я использую такой юнит в других проектах или вносю некоторые изменения в другие юниты в том же проекте, это все равно может нарушить выравнивание самого __PadFirstProcTo16. Аналогично, повторная компиляция одного и того же проекта с разными версиями компилятора (например, D2009 и D2010) обычно также нарушает выравнивание. Таким образом, единственный способ сделать что-то подобное, что я обнаружил, был вручную - это почти что последнее, что нужно сделать, когда весь остальной проект находится в окончательной форме.

Вопрос 1:

Есть ли другой способ добиться желаемого эффекта, гарантирующего, что (хотя бы некоторые конкретные) подпрограммы будут выровнены по DQWORD?

Вопрос 2:

Какие именно факторы влияют на выравнивание кода компилятором и (как) я мог бы использовать такие специфические знания для преодоления проблемы, изложенной здесь?

Предположим, что для ответа на этот вопрос "не беспокойтесь о выравнивании кода / связанных, по-видимому, небольших преимуществах в скорости" это не допустимый ответ.

Ответы [ 2 ]

7 голосов
/ 31 августа 2010

Начиная с Delphi XE, проблема выравнивания кода теперь легко решается с помощью директивы компилятора $CODEALIGN (см. на этой странице документации Delphi ):

{$CODEALIGN 16}
procedure MyAlignedProc;
begin
..
end;
6 голосов
/ 06 декабря 2009

Одна вещь, которую вы могли бы сделать, это добавить «волшебную» подпись в конце каждой процедуры после явной инструкции ret:

asm
  ...
  ret
  db <magic signature bytes>
end;

Теперь вы можете создавать массив, содержащий указатели на каждую подпрограмму, сканировать подпрограммы во время выполнения один раз, чтобы найти магическую подпись, чтобы найти конец каждой подпрограммы и, следовательно, ее длину. Затем вы можете скопировать их в новый блок памяти, который вы выделите с помощью VirtualAlloc с помощью PAGE_EXECUTE_READWRITE, гарантируя, что на этот раз каждая подпрограмма будет начинаться с 16-байтовой границы.

...