簡體   English   中英

如何返回復雜的返回值?

[英]How to return a complex return value?

目前我正在編寫一些匯編語言程序。 正如一些慣例所說,當我想向調用者返回一些值時,比如一個整數,我應該在EAX寄存器中返回它。 現在我想知道如果我想返回一個float,一個double,一個枚舉,甚至一個復雜的結構。 如何返回這些類型的值?

我可以想到在EAX中返回一個指向內存中真實值的地址。 但這是標准方式嗎?

非常感謝~~~

如果調用者是您的代碼,這完全取決於您。 如果呼叫者不在您的控制之下,您必須遵循現有慣例或一起制定自己的慣例。

例如,在x86平台上,當FPU指令處理浮點運算時,函數的結果將作為FPU寄存器堆棧的最高值返回。 (如果你知道,x86 FPU寄存器被組織成各種各樣的“循環堆棧”)。 那時它既不是float也不是double ,它是一個以內部FPU精度存儲的值(可能高於floatdouble ),並且調用者有責任從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) ;
  • 對於返回structunion類型的函數,調用者為返回值提供空間,並將其地址作為隱藏的第一個參數傳遞。 被調用者在%eax返回此地址。

通常你會使用堆棧

如果您計划與C或其他更高級語言進行交互,通常您會接受內存緩沖區的地址作為函數的參數,並通過填充該緩沖區來返回復雜值。 如果這只是匯編,那么你可以使用你想要的任何寄存器來定義自己的約定,盡管通常只有在你有特定原因(例如性能)時才這樣做。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM