[英]How to return a complex return value?
目前我正在編寫一些匯編語言程序。 正如一些慣例所說,當我想向調用者返回一些值時,比如一個整數,我應該在EAX寄存器中返回它。 現在我想知道如果我想返回一個float,一個double,一個枚舉,甚至一個復雜的結構。 如何返回這些類型的值?
我可以想到在EAX中返回一個指向內存中真實值的地址。 但這是標准方式嗎?
非常感謝~~~
如果調用者是您的代碼,這完全取決於您。 如果呼叫者不在您的控制之下,您必須遵循現有慣例或一起制定自己的慣例。
例如,在x86平台上,當FPU指令處理浮點運算時,函數的結果將作為FPU寄存器堆棧的最高值返回。 (如果你知道,x86 FPU寄存器被組織成各種各樣的“循環堆棧”)。 那時它既不是float
也不是double
,它是一個以內部FPU精度存儲的值(可能高於float
或double
),並且調用者有責任從FPU堆棧的頂部檢索該值並將其轉換為任何值打字它的欲望。 實際上,這就是典型的FPU指令的工作原理:它從FPU堆棧的頂部獲取其參數,並將結果推回到FPU堆棧。 通過以相同的方式實現您的功能,您基本上可以使用您的函數模擬“復雜”FPU指令 - 這是一種相當自然的方式。
當SSE指令處理浮點運算時,您可以選擇一些SSE寄存器用於相同的目的(使用xmm0
就像使用EAX
作為整數一樣)。
對於復雜結構(即大於寄存器或寄存器對的結構),調用者通常會將指針傳遞給函數的保留緩沖區。 函數會將結果放入緩沖區。 換句話說,在引擎蓋下,函數永遠不會真正“返回”大對象,而是在調用者提供的內存緩沖區中構造它們。
當然,您可以使用此“內存緩沖區”方法返回任何類型的值,但是使用較小的值(即標量類型的值),使用寄存器比使用內存位置要高效得多。 這也適用於小型結構。
枚舉通常只是某種整數類型的概念包裝。 因此,返回枚舉或整數之間沒有區別。
應該返回double作為堆棧中的第1項。
這是一個C ++代碼示例(x86):
double sqrt(double n)
{
_asm fld n
_asm fsqrt
}
如果您更喜歡手動管理堆棧(節省一些CPU周期):
double inline __declspec (naked) __fastcall sqrt(double n)
{
_asm fld qword ptr [esp+4]
_asm fsqrt
_asm ret 8
}
對於復雜類型,您應該傳遞指針,或返回指針。
當您對調用約定或匯編語言有疑問時,請使用高級語言(在單獨的文件中)編寫一個簡單的函數。 接下來,讓編譯器生成匯編語言列表或讓調試器顯示“interleaved assembly”。
列表不僅會告訴您編譯器如何實現代碼,還會向您顯示調用約定。 比發布到SO更容易,通常更快。 ;-)
C99具有復雜的內置數據類型( _Complex
)。 因此,如果您有一個符合C99的編譯器,您可以編譯一些返回復合體的函數並將其編譯為匯編程序(通常使用-S
選項)。 在那里你可以看到所采取的約定。
這取決於ABI。 例如,x86上的Linux使用在Intel386架構處理器補充版第四版中指定的Sys V ABI。
“ 函數調用序列”部分包含有關如何返回值的信息。 簡而言之,在此API中:
%eax
; %st(0)
; struct
或union
類型的函數,調用者為返回值提供空間,並將其地址作為隱藏的第一個參數傳遞。 被調用者在%eax
返回此地址。 通常你會使用堆棧
如果您計划與C或其他更高級語言進行交互,通常您會接受內存緩沖區的地址作為函數的參數,並通過填充該緩沖區來返回復雜值。 如果這只是匯編,那么你可以使用你想要的任何寄存器來定義自己的約定,盡管通常只有在你有特定原因(例如性能)時才這樣做。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.