Я сомневаюсь, что вы все еще заинтересованы в обратной связи по этому вопросу, но так как вы никогда не принимали никаких других ответов ...
Есть несколько проблем с кодом OP, но с небольшой очисткой вы получите:
#include <stdint.h>
#define SHIFT(h, l, c) __asm__ volatile ( \
"shld %b2, %1, %0\n\t" \
"sal %b2, %1\n\t" \
: "+r"(h), "+r"(l) : "Jc"(c))
int main(void) {
uint64_t a, b;
a = b = 0;
SHIFT(a, b, 1); /* 1 */
SHIFT(a, b, 2*4); /* 2 */
size_t i;
for(i=0; i<16; i++) {
SHIFT(a, b, (i*4)); /* 3 */
}
}
Наиболее значимые изменения:
- Использование «Jc» для ограничения для (c). Это позволяет gcc использовать немедленное, если это возможно, но при необходимости возвращается к rcx (т.е. значение не помещается в «J» или значение не известно во время компиляции).
- Использование% b2 вместо просто% 2. Это дает нам cl вместо rcx, чего требуют эти инструкции.
- Изменение размера цикла до 16. sal и shld допускают сдвиги только 0-63 на x64 (0-31 на x86).
Скомпилировано с -O2 -m64 -funroll-all-loops -S
, мы видим:
/APP
# 12 "shl.cpp" 1
shld $1, %rdx, %rax
sal $1, %rdx
# 0 "" 2
# 13 "shl.cpp" 1
shld $8, %rdx, %rax
sal $8, %rdx
# 0 "" 2
# 16 "shl.cpp" 1
shld $0, %rdx, %rax
sal $0, %rdx
# 0 "" 2
# 16 "shl.cpp" 1
shld $4, %rdx, %rax
sal $4, %rdx
...
# 0 "" 2
# 16 "shl.cpp" 1
shld $60, %rdx, %rax
sal $60, %rdx
# 0 "" 2
/NO_APP
Что интересно, если вы используете i * 6 вместо i * 4, вы видите, что gcc использует немедленные значения до 60, а затем начинает использовать cl.
Тад!