Как заставить GCC объединить «перемещение r10, r3; хранение r10» в «хранилище r3»? - PullRequest
0 голосов
/ 27 ноября 2018

Я работаю в Power9 и использую инструкцию аппаратного генератора случайных чисел под названием DARN.У меня есть следующая встроенная сборка:

uint64_t val;
__asm__ __volatile__ (
    "xor 3,3,3                     \n"  // r3 = 0
    "addi 4,3,-1                   \n"  // r4 = -1, failure
    "1:                            \n"
    ".byte 0xe6, 0x05, 0x61, 0x7c  \n"  // r3 = darn 3, 1
    "cmpd 3,4                      \n"  // r3 == -1?
    "beq 1b                        \n"  // retry on failure
    "mr %0,3                       \n"  // val = r3
    : "=g" (val) : : "r3", "r4", "cc"
);

Мне пришлось добавить mr %0,3 с "=g" (val), потому что я не мог заставить GCC генерировать ожидаемый код с "=r3" (val).См. Также Ошибка: ограничение соответствия недопустимо в выходном операнде .

Разборка показывает:

(gdb) b darn.cpp : 36
(gdb) r v
...

Breakpoint 1, DARN::GenerateBlock (this=<optimized out>,
    output=0x7fffffffd990 "\b", size=0x100) at darn.cpp:77
77              DARN64(output+i*8);
Missing separate debuginfos, use: debuginfo-install glibc-2.17-222.el7.ppc64le libgcc-4.8.5-28.el7_5.1.ppc64le libstdc++-4.8.5-28.el7_5.1.ppc64le
(gdb) disass
Dump of assembler code for function DARN::GenerateBlock(unsigned char*, unsigned long):
   ...
   0x00000000102442b0 <+48>:    addi    r10,r8,-8
   0x00000000102442b4 <+52>:    rldicl  r10,r10,61,3
   0x00000000102442b8 <+56>:    addi    r10,r10,1
   0x00000000102442bc <+60>:    mtctr   r10
=> 0x00000000102442c0 <+64>:    xor     r3,r3,r3
   0x00000000102442c4 <+68>:    addi    r4,r3,-1
   0x00000000102442c8 <+72>:    darn    r3,1
   0x00000000102442cc <+76>:    cmpd    r3,r4
   0x00000000102442d0 <+80>:    beq     0x102442c8 <DARN::GenerateBlock(unsigned char*, unsigned long)+72>
   0x00000000102442d4 <+84>:    mr      r10,r3
   0x00000000102442d8 <+88>:    stdu    r10,8(r9)

Обратите внимание, что GCC точно воспроизводит:

0x00000000102442d4 <+84>:    mr      r10,r3
0x00000000102442d8 <+88>:    stdu    r10,8(r9)

Как мне заставить GCC сложить две инструкции в:

0x00000000102442d8 <+84>:    stdu    r3,8(r9)

1 Ответ

0 голосов
/ 27 ноября 2018

GCC никогда не удалит текст, который является частью шаблона asm;он даже не анализирует его, кроме замены на %operand.Это буквально просто текстовая подстановка, прежде чем asm будет отправлен ассемблеру.

Вы должны опустить mr из вашего встроенного шаблона asm и сообщить gcc, что ваш вывод находится в r3 (или использоватьоперанд вывода памяти, но не делайте этого).Если ваш шаблон inline-asm когда-либо начинается или заканчивается инструкциями mov, вы обычно делаете это неправильно.

Используйте register uint64_t foo asm("r3");, чтобы заставить "=r"(foo) выбрать r3 на платформах, которыене имеют ограничений конкретного регистра.

(Несмотря на то, что в ISO C ++ 17 удаляется ключевое слово register, это расширение GNU по-прежнему работает с -std=c++17. Вы также можете использовать register uint64_t foo __asm__("r3");, есливам нужно избегать ключевого слова asm. Вероятно, вам все еще нужно трактовать register как зарезервированное слово в источнике, использующем это расширение, и это нормально. Удаление ISO из C ++ из базового языка не приводит к реализации не используйте его как часть добавочного номера.)


Или, лучше, не кодируйте номер регистра жестко.Используйте ассемблер, который поддерживает инструкцию DARN.(Но, очевидно, это настолько ново, что даже в современном clang его нет, и вам бы хотелось, чтобы этот встроенный ассемблер был запасным вариантом для gcc, слишком старого, чтобы поддерживать __builtin_darn() intrinsic )


Использование этих ограничений также позволит вам удалить настройки регистра и использовать foo=0 / bar=-1 перед оператором inline asm и использовать "+r"(foo).

Нообратите внимание, что выходной регистр darn предназначен только для записи .Там нет необходимости обнулять r3 в первую очередь.Я нашел копию руководства по набору инструкций IBM POWER ISA, которое достаточно новое, чтобы включить в него darn здесь: https://wiki.raptorcs.com/w/images/c/cb/PowerISA_public.v3.0B.pdf#page=96

На самом деле, вам вообще не нужно зацикливаться внутри asm,вы можете оставить это на C и только обернуть одну инструкцию asm, как для inline-asm.

uint64_t random_asm() {
  register uint64_t val asm("r3");
  do {
    //__asm__ __volatile__ ("darn 3, 1");
      __asm__ __volatile__ (".byte 0x7c, 0x61, 0x05, 0xe6  # gcc asm operand = %0\n" : "=r" (val));
  } while(val == -1ULL);
  return val;
}

компилируется чисто ( на GodboltПроводник компилятора ) до

random_asm():
.L6:                 # compiler-generated label, no risk of name clashes
    .byte 0x7c, 0x61, 0x05, 0xe6  # gcc asm operand = 3

    cmpdi 7,3,-1     # compare-immediate
    beq 7,.L6
    blr

Так же плотно, как ваш цикл, с меньшими настройками.(Вы уверены, что вам даже нужно обнулить r3 перед инструкцией asm?)

Эта функция может быть встроенной в любом месте, где вы хотите, позволяя gcc выдавать инструкцию сохранения, которая читает r3 напрямую.


На практике вы захотите использовать счетчик повторов, как рекомендовано в руководстве: если аппаратный RNG сломан, он может навсегда вызвать сбой, поэтому у вас должен быть запасной вариант к PRNG.(То же самое для x86 rdrand)

Доставить случайное число (darn) - Замечание по программированию

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


xor - обнуление неэффективно на большинстве ISA с фиксированной шириной инструкции , посколькуMOV-немедленный так же короток, поэтому нет необходимости обнаруживать и особый случай XOR.(И, таким образом, конструкции ЦП не тратят на это транзисторы).Более того, правила зависимостей для PPC-ассемблера, эквивалентного C ++ 11 std::memory_order_consume , требуют, чтобы содержал зависимость от входного регистра, так что это не могло быть нарушением зависимостей, даже если дизайнеры хотели этого,Обнуление по xor это только вещь на x86 и, возможно, несколько других ISA с переменной шириной.

Используйте li r3, 0, как gcc делает для int foo(){return 0;} https://godbolt.org/z/-gHI4C.

...