![](/img/trans.png)
[英]Assembly code/AVX instructions for multiplication of complex numbers. (GCC inline assembly)
[英]GCC Inline Assembly Multiplication
我正在嘗試在Linux(x86)上學習GCC內聯匯編,我的第一個實驗是嘗試實現乘法的整數溢出檢測。 這似乎很容易,但它有副作用,我不明白。
所以,在這里我想要將兩個無符號8位整數相乘,並查看結果是否溢出。 基本上我只是將第一個操作數加載到AL寄存器中,將另一個操作數加載到BL寄存器中,然后使用mul
指令。 結果存儲為AX寄存器中的16位值。 然后我將AX寄存器中的值復制到我的C變量b
,除非它溢出。 如果它溢出我將c
設置為1。
uint8_t a = 10;
uint8_t b = 25;
uint8_t c = 0; // carry flag
__asm__
(
"clc;" // Clear carry flag
"movb %3, %%al;" // Load b into %al
"movb %2, %%bl;" // Load a into %bl
"mul %%bl;" // Multiply a * b (result is stored in %ax)
"movw %%ax, %0;" // Load result into b
"jnc out;" // Jump to 'out' if the carry flag is not set
"movb $1, %1;" // Set 'c' to 1 to indicate an overflow
"out:"
:"=m"(b), "=m"(c) // Output list
:"ir"(a), "m"(b) // Input list
:"%al", "%bl" // Clobbered registers (not sure about this)
);
這似乎工作正常。 如果我printf
'b'的值,我得到250,這是正確的。 另外,如果我將'b'的起始值更改為26,那么在乘法c
設置為1之后,當然會指示溢出(10 * 26> ~uint8_t(0))。 我看到的問題是,C變量a
是乘法后設置為0(或1對溢出)。我不明白為什么a
會被任何東西我在這里做的一切都改變了。 它甚至不是輸出變量的名單上,所以為什么我的匯編程序影響的值a
?
此外,我不確定被破壞的寄存器列表。 該列表應該告知GCC有關在匯編例程中使用的任何寄存器,以便GCC不會嘗試錯誤地使用它們。 我想我需要告知GCC我使用了AL和BL寄存器,但AX寄存器怎么樣? 它隱式用於存儲兩個8位整數的乘積,所以我是否需要將它包含在被破壞的寄存器列表中?
我看到的問題是,C變量
a
是乘法后設置為0(或1對溢出)。我不明白為什么a
會被任何東西我在這里做的一切都改變了。 它甚至不是輸出變量的名單上,所以為什么我的匯編程序影響的值a
?
mul %%bl
將AL(8位)乘以BL(8位),將結果置於AX(16位)。
注意,AL和AX不是單獨的寄存器:AL只是AX的底部8位。
movw %%ax, %0
將AX(16位)存儲到b
...的地址,即uint8_t
。 因此,該指令也會用結果的前8位覆蓋存儲器中的下一個字節。 在這種情況下,該字節恰好是存儲a
的值的位置(這解釋了為什么a
在沒有溢出時被0覆蓋,而在它溢出時為1)。
您需要將其替換為movb %%al, %0
,才能只生成結果底部8位的字節存儲。
我想我需要告知GCC我使用了AL和BL寄存器,但AX寄存器怎么樣? 它隱式用於存儲兩個8位整數的乘積,所以我是否需要將它包含在被破壞的寄存器列表中?
是的 - 您應該告訴GCC有關您更改其值的任何寄存器(並且,正如nategoose在另一個答案中指出的那樣,您應該告訴它您也在更改標志)。 所以這里的clobber列表應該是"%ax", "%bl", "cc"
(AX包括AL,因此您不需要明確提及AL)。
您應該使用-S
選項編譯代碼並查看* .s文件。 所有的程序集都在用分號分隔的同一行上,我相信分號在gnu匯編程序中開始注釋。 您需要在所有匯編指令的末尾添加“\\ n”(或更好的“\\ n \\ t”)。
您可能還想將“cc”添加到clobber列表中。
此外,GCC還有一種方法可以指定輸入既可以是您可能感興趣的匯編的輸入也可以是輸出。
此外,最好讓GCC決定輸入的位置而不是強制它們在內存中。 我似乎記得GCC的內聯匯編對x86的約束意味着“無論是在寄存器中還是在內存中”,它可以用於無關緊要的情況,但你可能不應該有很多“mov”指令因為GCC最大的工作之一是在實際計算指令之間找出最好的移動指令集,所以你的內聯匯編的開始和結束。 例如,在你的代碼中,GCC要做的最好的事情就是將常量10和25存儲在你用來開始的寄存器中。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.