繁体   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