PC-относительный переход в сборке gcc - PullRequest
1 голос
/ 05 февраля 2011

У меня есть цикл asm, который гарантированно не будет проходить более 128 итераций, которые я хочу развернуть с помощью ПК-относительного перехода.Идея состоит в том, чтобы развернуть каждую итерацию в обратном порядке, а затем прыгнуть так далеко, насколько это необходимо.Код будет выглядеть так:

#define __mul(i) \
    "movq -"#i"(%3,%5,8),%%rax;" \
    "mulq "#i"(%4,%6,8);" \
    "addq %%rax,%0;" \
    "adcq %%rdx,%1;" \
    "adcq $0,%2;"

asm("jmp (128-count)*size_of_one_iteration" // I need to figure this jump out
    __mul(127)
    __mul(126)
    __mul(125)
    ...
    __mul(1)
    __mul(0)
    : "+r"(lo),"+r"(hi),"+r"(overflow)
    : "r"(a.data),"r"(b.data),"r"(i-k),"r"(k)
    : "%rax","%rdx");

Возможно ли что-то подобное с встроенной сборкой gcc?

Ответы [ 3 ]

1 голос
/ 06 февраля 2011

Это не прямой ответ, но рассматривали ли вы вариант Устройство Даффа вместо встроенной сборки?Это могло бы принять форму оператора switch:

switch(iterations) {
  case 128: /* code for i=128 here */
  case 127: /* code for i=127 here */
  case 126: /* code for i=126 here */
  /* ... */
  case 1:   /* code for i=1 here*/
  break;
  default: die("too many cases");
}
1 голос
/ 18 февраля 2011

В встроенной сборке gcc вы можете использовать метки, и ассемблер сам выберет цель перехода. Нечто подобное (надуманный пример):

int max(int a, int b)
{
    int result;
    __asm__ __volatile__(
        "movl %1, %0\n"
        "cmpl %2, %0\n"
        "jeq  a_is_larger\n"
        "movl %2, %0\n"
        "a_is_larger:\n" : "=r"(result), "r"(a), "r"(b));
    return (result);
}

Это одна вещь. Другая вещь, которую вы могли бы сделать, чтобы избежать умножения, это сделать ассемблер выровнять блоки для вас, скажем, с кратностью 32 байта (я не думаю, что последовательность команд помещается в 16 байтов), например :

#define mul(i)                     \
    ".align 32\n"                  \
    ".Lmul" #i ":\n"               \
    "movq -" #i "(%3,%5,8),%%rax\n"\
    "mulq " #i "(%4,%6,8)\n"       \
    "addq %%rax,%0\n"              \
    "adcq %%rdx,%1\n"              \
    "adcq $0,%2\n"

Это просто добавит поток команд к nop. Если вы решите не выравнивать эти блоки, вы все равно в своем основном выражении можете использовать сгенерированные локальные метки, чтобы найти размер сборочных блоков:

#ifdef UNALIGNED
__asm__ ("imul $(.Lmul0-.Lmul1), %[label]\n"
#else
__asm__ ("shlq $5, %[label]\n"
#endif
    "leaq .Lmulblkstart, %[dummy]\n"        /* this is PC-relative in 64bit */
    "jmp (%[dummy], %[label])\n"
    ".align 32\n"
    ".Lmulblkstart:\n"
    __mul(127)
    ...
    __mul(0)
    : ... [dummy]"=r"(dummy) : [label]"r"((128-count)))

А для случая, когда count - это константа времени компиляции, вы даже можете сделать:

__asm__("jmp .Lmul" #count "\n" ...);

Небольшая заметка на конце:

Выравнивание блоков - хорошая идея, если автоматически сгенерированная вещь _mul() может создавать последовательности различной длины. Для констант 0..127, которые вы используете, это не будет иметь место, поскольку все они вписываются в байт, но если вы увеличите их размер, то это приведет к 16- или 32-битным значениям, и блок инструкций будет расти вместе с ними. , Заполняя поток команд, можно использовать технику прыжковой таблицы.

1 голос
/ 06 февраля 2011

Извините, я не могу дать ответ в синтаксисе ATT, надеюсь, вы легко сможете выполнить переводы.

Если у вас есть счетчик в RCX и вы можете иметь метку сразу после __mul (0), тогдаВы могли бы сделать это:

; rcx must be in [0..128] range.
    imul ecx, ecx, -size_of_one_iteration ; Notice the multiplier is negative (using ecx is faster, the upper half of RCX will be automatically cleared by CPU)
    lea  rcx, [rcx + the_label] ; There is no memory read here
    jmp  rcx

Надеюсь, это поможет.

РЕДАКТИРОВАТЬ: я сделал ошибку вчера.Я предположил, что ссылка на метку в [rcx + the_label] разрешается как [rcx + rip + disp], но это не так, поскольку такого режима адресации нет (существует только [rip + disp32])

Этот код должен работать, и, кроме того, он оставит rcx нетронутым и вместо этого уничтожит rax и rdx (но ваш код, кажется, не читает их перед первой записью):

; rcx must be in [0..128] range.
    imul edx, ecx, -size_of_one_iteration ; Notice the multiplier is negative (using ecx is faster, the upper half of RCX will be automatically cleared by CPU)
    lea  rax, [the_label] ; PC-relative addressing (There is no memory read here)
    add  rax, rdx
    jmp  rax 
...