Есть ли способ генерировать встроенную сборку программно? - PullRequest
0 голосов
/ 21 мая 2018

В моей программе мне нужно вставить NOP как встроенную сборку в цикл, а количество NOP можно контролировать с помощью аргумента.Примерно так:

char nop[] = "nop\nnop";

for(offset = 0; offset < CACHE_SIZE; offset += BLOCK_SIZE) {

    asm volatile (nop
            :
            : "c" (buffer + offset)
            : "rax");

}

Есть ли способ сказать компилятору преобразовать вышеуказанную встроенную сборку в следующую?

   asm volatile ("nop\n"
                 "nop"
            :
            : "c" (buffer + offset)
            : "rax");

Ответы [ 2 ]

0 голосов
/ 21 мая 2018

Ну, есть такой трюк, который вы можете сделать:

#define NOPS(n) asm volatile (".fill %c0, 1, 0x90" :: "i"(n))

Этот макрос вставляет нужное количество nop инструкций в поток инструкций.Обратите внимание, что n должна быть постоянной времени компиляции.Вы можете использовать оператор switch для выбора различной длины:

switch (len) {
case 1: NOPS(1); break;
case 2: NOPS(2); break;
...
}

Вы также можете сделать это для большей экономии размера кода:

if (len & 040) NOPS(040);
if (len & 020) NOPS(020);
if (len & 010) NOPS(010);
if (len & 004) NOPS(004);
if (len & 002) NOPS(002);
if (len & 001) NOPS(001);

Обратите внимание, что вы действительно должны рассмотреть возможность использования pauseинструкции вместо nop инструкции для такого рода вещей, так как pause - это семантическая подсказка, что вы просто пытаетесь скоротать время.Это изменяет определение макроса на:

#define NOPS(n) asm volatile (".fill %c0, 2, 0x90f3" :: "i"(n))
0 голосов
/ 21 мая 2018

Нет, встроенный шаблон asm должен быть постоянным времени компиляции , поэтому ассемблер может собрать его в машинный код.

Если вам нужен гибкий шаблон, который вы изменяете при запускевремя, это называется JIT-компиляцией или генерацией кода.Обычно вы генерируете машинный код напрямую, а не исходный текст на ассемблере, который вы передаете ассемблеру.


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

Кстати, dec eax работает на 1 за такт на всех современных процессорах x86, в отличие от NOP, который работает на 4 за такт или, возможно, 5 на Райзене.См. http://agner.org/optimize/.

Лучшим выбором для небольшой задержки может быть инструкция pause или цепочка зависимостей некоторого переменного числа imul инструкций, или, возможно, sqrtps, заканчивающаяся lfence блокировать выполнение не по порядку (по крайней мере, на процессорах Intel).Я не проверял руководства AMD, чтобы убедиться, что lfence задокументирован как препятствие для выполнения, но Agner Fog сообщает, что он может работать со скоростью 4 в час на Райзене.


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

for (int i=0 ; i<delay_count ; i++) {
    asm volatile("" : "r" (i));  // defeat optimization
}

Это заставит компилятор иметь счетчик цикла в регистре на каждой итерации, поэтому он не может оптимизировать цикл или превратить его в умножение.Вы должны получить сгенерированный компилятором asm как delayloop: dec eax; jnz delayloop.Возможно, вы захотите поставить _mm_lfence() после цикла.

...