[英]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.