Tôi đang cố gắng viết assembly x86-64 nội tuyến cho GCC để sử dụng hiệu quả lệnh MULQ. MULQ nhân RAX đăng ký 64 bit với một giá trị 64 bit khác. Giá trị khác có thể là bất kỳ thanh ghi 64 bit nào (thậm chí là RAX) hoặc một giá trị trong bộ nhớ. MULQ đặt 64 bit cao của sản phẩm vào RDX và 64 bit thấp vào RAX.GCC có thể phát ra các phép ghi nhớ hướng dẫn khác nhau khi lựa chọn giữa nhiều ràng buộc toán hạng thay thế của assembly nội tuyến?
Bây giờ, thật dễ dàng, đủ để thể hiện một mulq đúng như lắp ráp nội tuyến:
#include <stdint.h>
static inline void mulq(uint64_t *high, uint64_t *low, uint64_t x, uint64_t y)
{
asm ("mulq %[y]"
: "=d" (*high), "=a" (*low)
: "a" (x), [y] "rm" (y)
);
}
Mã này là đúng, nhưng không phải là tối ưu. MULQ là giao hoán, vì vậy nếu y
xảy ra ở trong RAX, thì sẽ là chính xác để rời khỏi y
vị trí của nó và nhân lên. Nhưng GCC không biết điều đó, vì vậy nó sẽ phát ra các hướng dẫn bổ sung để di chuyển các toán hạng vào các vị trí được xác định trước của chúng. Tôi muốn nói với GCC rằng nó có thể đặt đầu vào ở một trong hai vị trí, miễn là một đầu vào trong RAX và MULQ tham chiếu đến vị trí khác. GCC có một cú pháp cho điều này, được gọi là "nhiều ràng buộc thay thế". Lưu ý dấu phẩy (nhưng tổng số asm() bị hỏng; xem bên dưới):
asm ("mulq %[y]"
: "=d,d" (*high), "=a,a" (*low)
: "a,rm" (x), [y] "rm,a" (y)
);
Thật không may, điều này là sai. Nếu GCC chọn ràng buộc thay thế thứ hai, nó sẽ phát ra "mulq% rax". Để được rõ ràng, hãy xem xét chức năng này:
uint64_t f()
{
uint64_t high, low;
uint64_t rax;
asm("or %0,%0": "=a" (rax));
mulq(&high, &low, 7, rax);
return high;
}
Biên soạn với gcc -O3 -c -fkeep-inline-functions mulq.c
, GCC phát ra lắp ráp này:
0000000000000010 <f>:
10: or %rax,%rax
13: mov $0x7,%edx
18: mul %rax
1b: mov %rdx,%rax
1e: retq
Các "mul% rax" cần được "mul% RDX".
Làm cách nào có thể viết lại nội tuyến này để tạo ra kết quả chính xác trong mọi trường hợp?
Cách giải quyết là đặt macro lắp ráp trong assembly nội tuyến, một cái gì đó như 'MULQ_FIX_OPERANDS (% 2,% 3)', chọn toán hạng không phải RAX, nhưng trông thực sự xấu với tôi. Tôi hy vọng ai đó có một giải pháp tốt hơn. – staufk
GCC hỗ trợ ràng buộc '%' để thể hiện các toán hạng giao hoán giữa các ràng buộc thay thế, nhưng điều đó không giải quyết được vấn đề vì MULQ chỉ nhận toán hạng. Nếu chỉ có 'gas' nghĩ 'MULQ RAX, r/m64' là một định dạng hợp lệ cho lệnh này! – staufk