Выровнять функцию C по «нечетному» адресу - PullRequest
2 голосов
/ 09 января 2020

Я знаю из C выравнивание функций в G CC, что я могу выравнивать функции, используя

    __attribute__((optimize("align-functions=32")))

Теперь, что если я хочу, чтобы функция начиналась с " «нечетный» адрес, например, я хочу, чтобы он начинался с адреса вида 32(2k+1), где k - это любое целое число?

Я бы хотел, чтобы функция начиналась с адреса (десятичного) 32 или 96 или 160, но не 0 или 64 или 128.

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

Ответы [ 3 ]

4 голосов
/ 09 января 2020

G CC не имеет возможности сделать это.

Вместо этого скомпилируйте в asm и выполните некоторые манипуляции с текстом на этом выходе . Например, gcc -O3 -S foo.c, затем запустите некоторый скрипт на foo.s для нечетного выравнивания перед некоторыми метками функций, перед компиляцией в конечный исполняемый файл с gcc -o benchmark foo.s.

Один простой способ (который стоит от 32 до 95 байт Это простой способ c:

 .balign 64        # byte-align by 64
 .space 32         # emit 32 bytes (of zeros)
starts_half_way_into_a_cache_line:
testfunc1:

Настройка вывода GCC / clang после компиляции - это, в общем, хороший способ узнать, что g cc должен иметь сделанный. Все ссылки на другой код / ​​данные внутри и снаружи функции используют имена символов, ничто не зависит от относительных расстояний между функциями или абсолютными адресами до тех пор, пока вы не соберете (и ссылку) , поэтому редактирование источника asm на этом этапе совершенно безопасно. (В другом ответе предлагается скопировать окончательный машинный код; это очень просто agile, см. Комментарии под ним.)

Сценарий автоматической обработки текста позволит вам провести эксперимент с большим количеством кода. Это может быть так просто, как
awk '/^testfunc.*:/ { print ".p2align 6; .skip 32"; print $0 }' foo.s
сделать это перед каждым ярлыком, который соответствует шаблону ^testfunc.*. (Предполагая, что имя подчеркивания не будет начальным.)

Или даже используйте sed, который имеет удобную опцию -i, чтобы сделать это "на месте", переименовав выходной файл поверх оригинала, или perl есть нечто подобное. К счастью, вывод компилятора довольно формульный c, для данного компилятора это должно быть довольно простой задачей сопоставления с образцом.


Имейте в виду, что эффекты выравнивания кода не всегда являются чисто локальными. Ветви в одной функции могут быть псевдонимами (в предсказателе ветвления) с ветвями из другой функции в зависимости от деталей выравнивания.

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

Другие эффекты выравнивания включают упаковку uop-cache на современных x86, а также блок выборки. (Помимо очевидного эффекта оставления неиспользуемого пространства в строке I-кэша).


В идеале вы должны вставить только 0,63 байта, чтобы достичь желаемой позиции относительно 64- граница байта. Этот раздел является неудачной попыткой заставить это работать.

.p2align и .balign 1 поддерживают дополнительный 3-й аргумент, который определяет максимум количество заполнения, так что мы близки к тому, чтобы сделать это с директивами GAS. Возможно, мы можем использовать это, чтобы определить, близки ли мы к нечетной или четной границе, проверив, вставил ли он какие-либо отступы или нет. (Предположим, мы говорим только о 2 случаях, а не о 4 случаях 16-байтовых данных, например, относительно 64-байтовых.)

# DOESN'T WORK, and maybe not fixable
1:  # local label
 .balign 64,,31     # pad with up to 31 bytes to reach 64-byte alignment
2:
 .balign  32        # byte-align by 32, maybe to the position we want, maybe not
.ifne 2b - 1b
  # there is space between labels 2 and 1 so that balign reached a 64-byte boundary
  .space  32
.endif       # else it was already an odd boundary

Но, к сожалению, это не работает: Error: non-constant expression in ".if" statement. Если код между метками 1: и 2: имеет фиксированный размер, например .long 0xdeadbeef, он будет собираться просто отлично. Таким образом, очевидно, что GAS не позволит вам запросить с .if, сколько заполнено вставленной директивы выравнивания.

Сноска 1: .align это либо .p2align (степень 2), либо .balign (байт). ) в зависимости от того, для какой цели вы собираете. Вместо того, чтобы помнить, что и на какой цели, я бы рекомендовал всегда использовать .p2align или .balign, а не .align.

2 голосов
/ 10 января 2020

Поскольку этот вопрос помечен как сборка, в моих источниках (NASM 8086) есть две точки, которые "против выравнивания" следуют инструкциям и данным. (Здесь только с выравниванием по четным адресам, ie 2-байтовое выравнивание.) Оба были основаны на вычислениях, выполненных макросом выравнивания NASM.

https://hg.ulukai.org/ecm/ldebug/file/683a1d8ccef9/source/debug.asm#l1161

        times 1 - (($ - $$) & 1) nop    ; align in-code parameter
        call entry_to_code_sel, exc_code

https://hg.ulukai.org/ecm/ldebug/file/683a1d8ccef9/source/debug.asm#l7062

                ; $ - $$        = offset into section
                ; % 2           = 1 if odd offset, 0 if even
                ; 2 -           = 1 if odd, 2 if even
                ; % 2           = 1 if odd, 0 if even
        ; resb (2 - (($-$$) % 2)) % 2
                ; $ - $$        = offset into section
                ; % 2           = 1 if odd offset, 0 if even
                ; 1 -           = 0 if odd, 1 if even
        resb 1 - (($-$$) % 2)           ; make line_out aligned
trim_overflow:  resb 1                  ; actually part of line_out to avoid overflow of trimputs loop
line_out:       resb 263
                resb 1                  ; reserved for terminating zero
line_out_end:

Вот более простой способ добиться анти-выравнивания:

                align 2
                nop

Это более расточительно, но может использовать до 2 байтов, если целевое анти-выравнивание уже будет выполнено до этой последовательности. Мои предыдущие примеры не зарезервируют больше места, чем необходимо.

0 голосов
/ 09 января 2020

Я полагаю, что G CC позволяет вам выравнивать только по степеням 2

. Если вы хотите обойти это для тестирования, вы можете скомпилировать свои функции, используя независимый от позиции код (-FPI C или - FP IE), а затем написать отдельный загрузчик, который вручную копирует функцию в область, которая была MMAP как чтение / запись. И тогда вы можете изменить разрешения, чтобы сделать его исполняемым. Конечно, для правильного сравнения производительности вы должны убедиться, что выровненный код, с которым вы сравниваете его, также был скомпилирован с помощью FPIC / FP IE.

. Я могу дать вам пример кода, если вы нужно, просто дай мне знать.

...