В встроенной сборке 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-битным значениям, и блок инструкций будет расти вместе с ними. , Заполняя поток команд, можно использовать технику прыжковой таблицы.