如何返回复杂的返回值?

发布于 2024-09-19 04:29:17 字数 183 浏览 11 评论 0原文

目前我正在编写一些汇编语言程序。正如某些约定所说,当我想向调用者返回一些值(例如整数)时,我应该将其返回到 EAX 寄存器中。现在我想知道如果我想返回浮点型、双精度型、枚举型甚至复杂结构型该怎么办。如何返回这些类型的值?

我可以考虑返回 EAX 中的一个地址,该地址指向内存中的实际值。但这是标准方式吗?

非常感谢~~~

Currently I am writing some assembly language procedures. As some convention says, when I want to return some value to the caller, say an integer, I should return it in the EAX register. Now I am wondering what if I want to return a float, a double, an enum, or even a complex struct. How to return these type of values?

I can think of returning an address in the EAX which points to the real value in memory. But is it the standard way?

Many thanks~~~

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

情归归情 2024-09-26 04:29:31

如果您计划与 C 或其他高级语言进行交互,通常您会接受内存缓冲区的地址作为函数的参数,并通过填充该缓冲区来返回复数值。如果这是仅限汇编的,那么您可以使用您想要的任何寄存器集定义自己的约定,尽管通常只有在有特定原因(例如性能)时您才会这样做。

If you're planning to interface with C or another higher-level language, typically you would accept the address of a memory buffer as an argument to your function and return your complex value by populating that buffer. If this is assembly-only, then you can define your own convention using any set of registers you want, although usually you'd only do so if you have a specific reason (e.g., performance).

绝情姑娘 2024-09-26 04:29:29

通常你会使用堆栈

Typically you would use the stack

冰雪梦之恋 2024-09-26 04:29:27

这取决于 ABI。例如,x86 上的 Linux 使用 Sys V ABI,在 Intel386 架构处理器补充中指定,第四版

函数调用序列部分包含有关如何返回值的信息。简而言之,在此 API 中:

  • 返回标量或无值的函数使用 %eax
  • 返回浮点值的函数使用%st(0)
  • 对于返回 struct 或 union 类型的函数,调用者为返回值提供空间,并将其地址作为隐藏的第一个参数传递。被调用者在 %eax 中返回该地址。

It depends on the ABI. For example, Linux on x86 uses the Sys V ABI, specified in the Intel386 Architecture Processor Supplment, Fourth Edition.

The section Function Calling Sequence section has the information on how values are to be returned. Briefly, in this API:

  • Functions returning scalars or no value use %eax;
  • Functions returning floating point values use %st(0);
  • For functions returning struct or union types, the caller provides space for the return value and passes its address as a hidden, first argument. The callee returns this address in %eax.
提笔书几行 2024-09-26 04:29:26

C99 有一个复杂的内置数据类型(_Complex)。因此,如果您有一个兼容 C99 的编译器,您可以只编译一些返回复数的函数并将其编译为汇编程序(通常使用 -S 选项)。在那里您可以看到所采取的约定。

C99 has a complex builtin data type (_Complex). So if you have a C99 compliant compiler, you could just compile some function that returns a complex and compile this to assembler (usually with a -S option). There you can see the convention that is taken.

小鸟爱天空丶 2024-09-26 04:29:25

当您对调用约定或汇编语言有疑问时,请用高级语言(在单独的文件中)编写一个简单的函数。接下来,让编译器生成汇编语言列表,或者让调试器显示“交错汇编”。

该清单不仅会告诉您编译器如何实现代码,还会向您展示调用约定。比发布到 SO 容易得多,而且通常更快。 ;-)

When you have questions about calling conventions or assembly language, write a simple function in high level language (in a separate file). Next, have your compiler generate an assembly language listing or have your debugger display "interleaved assembly".

Not only will the listing tell you how the compiler implements code, but also show you the calling conventions. A lot easier than posting to S.O. and usually faster. ;-)

行至春深 2024-09-26 04:29:24

应返回双精度值作为堆栈中的第一项。

下面是一个 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
}

对于复杂类型,您应该传递一个指针,或返回一个指针。

A double should be returned as the 1st item in the stack.

Here is a C++ code example (x86):

double sqrt(double n)
{
    _asm fld n
    _asm fsqrt
}  

If you prefer to manage the stack manually (saving some CPU cycles):

double inline __declspec (naked) __fastcall sqrt(double n)
{
    _asm fld qword ptr [esp+4]
    _asm fsqrt
    _asm ret 8
}

For complex types, you should pass a pointer, or return a pointer.

往事随风而去 2024-09-26 04:29:22

如果调用者是您的代码,这一切都取决于您。如果调用者不受您的控制,您必须遵循他们现有的约定或共同制定自己的约定。

例如,在 x86 平台上,当 FPU 指令处理浮点运算时,函数的结果作为 FPU 寄存器堆栈的顶部值返回。 (如果您知道的话,x86 FPU 寄存器被组织成某种“循环堆栈”)。此时它既不是 float 也不是 double,它是一个以内部 FPU 精度存储的值(可能高于 float>double),调用者有责任从 FPU 堆栈顶部检索该值并将其转换为所需的任何类型。事实上,这就是典型 FPU 指令的工作方式:它从 FPU 堆栈顶部获取参数,并将结果推回 FPU 堆栈。通过以相同的方式实现您的函数,您基本上可以用您的函数模拟“复杂”FPU 指令 - 这是一种相当自然的方法。

当浮点运算由SSE指令处理时,您可以选择一些SSE寄存器来达到相同的目的(使用xmm0,就像使用EAX处理整数一样)。

对于复杂的结构(即大于一个寄存器或一对寄存器的结构),调用者通常会将指向保留缓冲区的指针传递给函数。该函数会将结果放入缓冲区。换句话说,在幕后,函数永远不会真正“返回”大对象,而是在调用者提供的内存缓冲区中构造它们。

当然,您可以使用这种“内存缓冲区”方法来返回任何类型的值,但对于较小的值,即标量类型的值,使用寄存器比使用内存位置更有效。顺便说一句,这也适用于小型结构。

枚举通常只是某些整数类型的概念包装。因此,返回枚举或整数之间没有区别。

It is all up to you, if the caller is your code. If the caller is not under your control, you have to either follow their existing convention or develop your own convention together.

For example, on x86 platform when floating-point arithmetic is processed by FPU instructions, the result of a function is returned as the top value on the FPU register stack. (If you know, x86 FPU registers are organized into a "circular stack" of sorts). At that moment it is neither float nor double, it is a value stored with internal FPU precision (which could be higher than float or double) and it is the caller's responsibility to retrieve that value from the top of FPU stack and convert it to whatever type it desires. In fact, that is how a typical FPU instruction works: it takes its arguments from the top of FPU stack and pushes the result back onto FPU stack. By implementing your function in the same way you essentially emulate a "complex" FPU instruction with your function - a rather natural way to do it.

When floating-point arithmetic is processed by SSE instructions, you can choose some SSE register for the same purpose (use xmm0 just like you use EAX for integers).

For complex structures (i.e. ones that are larger than a register or a pair of registers), the caller would normally pass a pointer to a reserved buffer to the function. And the function would put the result into the buffer. In other words, under the hood, functions never really "return" large objects, but rather construct them in a caller-provided memory buffer.

Of course, you can use this "memory buffer" method for returning values of any type, but with smaller values, i.e. values of scalar type, it is much more efficient to use registers than a memory location. This applies, BTW, to small structures as well.

Enums are usually just a conceptual wrapper over some integer type. So, there's no difference between returning a enum or an integer.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文