2010-01-13 239 views
3

我在項目中大量使用內聯彙編,在編譯時我需要調用具有未知數量參數的函數,並且我管理自己使其有時在linux中工作(在Windows中,我不「T記得有這個問題),奇怪的事情是這樣發生的:內部循環中的內聯彙編

如果我有像

for(int i = 1; i >= 0; i--) 
    asm("push %0"::"m"(someArray[i])); 

它的工作原理。

如果我有

for(int i = this->someVar; i >= 0; i--) 
    asm("push %0"::"m"(someArray[i])); 

,我保證我的生活,someVar拿着它拋出段錯誤的值1。

另外,如果我有

int x = 1; 
for(int i = x; i >= 0; i--) 
    asm("push %0"::"m"(someArray[i])); 

它的工作原理,但

int x = this->someVar; 
for(int i = x; i >= 0; i--) 
    asm("push %0"::"m"(someArray[i])); 

沒有。

另外,也奇怪的是,我可以說,雖然在某些功能中,我沒有問題在我擁有的其他人中這樣做,但都在同一個對象中。

如果有人能指點我一些信息,可以清除那裏的問題,我將不勝感激。

請注意,我真的必須在for循環中推入參數,所以避免它不是一個選項。

我也試過使用內聯彙編詞「volatile」,但沒有任何改變。

+0

的問題肯定是,「爲什麼不通過一個指針數組,而不是複製數組元素堆棧的,」還是我失去了一些東西?沿着:func(someArray,this-> someVar),然後你會得到一個性能提升以及(沒有內存移動,沒有循環) – Skizz 2010-01-13 11:17:52

+0

我調用的功能,我沒有控制。他們在其他圖書館。我需要把他們的期望傳給他們。 – user246100 2010-01-13 11:24:44

+0

通過在編譯器的手下執行不成對的'push'權限來更改局部變量的相對位置是非常非常糟糕的主意。 – avakar 2010-01-13 11:26:34

回答

4

我不明白出了什麼問題,但試圖利用清晰的彙編代碼相同

asm{ 
    loop1: 
    mov ax, this->var 
    ... 
    dec ax 
    cmp ax, 0 
    je exit 
    jmp loop1 
} 

寫代碼...

退出:

而且儘量做到「無功」價值作爲靜態可能也有幫助。

+0

由於代碼的流向,它不能是靜態的。我將嘗試使其僅彙編代碼。謝謝。 – user246100 2010-01-13 10:24:19

4

檢查拆卸。最可能的原因是在for循環的每次迭代中,i和/或包含最終值的變量將從堆棧上的固定偏移量中重新獲取,並且push會偏離編譯器所期望的堆棧指針和所以會導致錯誤的值被提取。

你可以嘗試不同的解決方法(例如聲明局部變量register),但遺憾的是在C/C++在這種情況下保證正確的行爲沒有什麼好辦法。爲了避免這個問題,像oivoodoo建議的那樣,自己實現這個循環。

+0

謝謝我打算試一試oivoodoo暗示 – user246100 2010-01-13 10:29:25

3

這裏是我的心理調試的努力:

ithis最有可能存儲在堆棧上,並在386及以上,機器代碼可以直接引用esp -relative內存位置,所以編譯器可能產生像

mov eax,[esp+8] 

指令來獲取的this值到eax寄存器。問題是您的push操作與堆棧指針混淆,因此這些硬編碼訪問將在第一次迭代後訪問(越來越多)錯誤的內存位置。

最有可能的,而不會this->someVar簡單的循環形式更徹底由編譯器優化,導致只使用寄存器和無esp -relative訪問,這意味着他們繼續正常工作的機器代碼。

曾幾何時,所有的存儲器訪問局部變量和參數均通過ebp寄存器,這是不是你的內聯彙編代碼更改完成。如果您可以找到一個編譯器開關來強制使用ebp而不是esp,這可能會解決您的問題。

警告:編譯器不希望你惹惱堆棧 - 它希望它始終知道堆棧的頂部在哪裏。如果你真的想動態地將東西放在堆棧上,我建議用oivoodoo完成的操作,完全用匯編語言編寫循環。

+0

儘管我不瞭解這個主題,但我的觀察和我所知道的讓我認爲發生的事情就像你說的一樣。感謝您的有用評論。 – user246100 2010-01-13 10:28:49

0

如果你知道限制成參數的數量,你可以用與數量的參數一個函數調用調用它,你的心實際參數到最後。

警告:x86_64的ABI使用註冊的一些參數,打破這一點,你的代碼了。

+0

這個數字是無限的,我希望它是這樣的。我想你的解決方案效率會更低。關於您提到的x86_64問題,僅僅因爲應用程序不適用於此問題,這不是問題。 – user246100 2010-01-13 10:46:57

1

首先,什麼是可能發生的是linux下的gcc使用堆棧指針索引你的局部變量,而不是使用堆棧幀指針。這是一個優化,它允許gcc使用幀指針(x86下的BP)作爲另一個通用寄存器並避免大量設置幀的代碼。幀本質上就是SP和BP之間屬於本地函數的區域。我敢打賭,如果你包含了一個調用alloca的大小,那麼你將會得到更好的結果,因爲它會迫使編譯器不做這個優化。

這就是說,這個錯誤確實在你的代碼中。除非你真的知道你在做什麼,否則你不應該退出一個內聯asm,其棧指針與你在進入內聯asm時的內容不同。編譯器幾乎總是認爲它們只擁有堆棧指針。他們依靠它保持不變,以便他們可以使用它來找到他們存儲變量的位置。 你也應該遠離幀指針(BP)。

當它是確定與那些亂七八糟的時間是罕見的,通常對於像上下文切換代碼(從一個線程或進程更改爲另一種)。