Компилятор GCC предоставляет возможность использовать ассемблерные вставки. Это может быть полезно например для умножения двух 64-битных чисел по 64-битному модулю.
Дело в том, что умножая два 64-битных регистра, процессор сохраняет результат в паре регистров rdx (верхнюю часть) и rax (нижнюю часть). Деление же работает похожим образом: делимое берется с регистров rdx и rax, после чего в rax сохраняется частное, а в rdx остаток.
Используя эти знания можно реализовать аналог следующей функции:
inline long long mul(long long a, long long b) {
return (__int128)a * b % 1000000014018503;
}
Вот таким образом:
inline long long mul(long long a, long long b) {
long long res;
asm(
"mov %1, %%rax\n"
"mov %2, %%rbx\n"
"imul %%rbx\n"
"mov $1000000014018503, %%rbx\n"
"idiv %%rbx\n"
"mov %%rdx, %0\n"
:"=res"(res)
:"a"(a), "b"(b)
);
return res;
}
Мы указываем на использование переменных res на запись, a и b на чтение. Они соответственно получают обозначения %0, %1, %2. Операции записываются с использованием стандартного AT&T синтаксиса.
Теперь вы умеете писать хеши по 64-битному модулю, что эквивалентно использованию пары по 32-битному модулю, без использования __int128.