【问题标题】:Returning structs in registers - ARM ABI in GCC在寄存器中返回结构 - GCC 中的 ARM ABI
【发布时间】:2011-02-23 11:53:17
【问题描述】:

在 ARM ABI 文档中,我遇到了如下定义的函数:

__value_in_regs struct bar foo(int a, int b) {
    ...
}

但 GCC(4.3.3) 不允许这样做,我能找到的只是对某些 RealView 编译器的引用。 GCC 有没有办法做到这一点?

我已经尝试过 -freg-struct-return 但它没有任何区别。由于它是 ABI,我无法更改原始程序,并且返回常规结构会破坏堆栈。

如果可以避免的话,我宁愿不为此使用汇编。

谢谢!

【问题讨论】:

  • 您正在阅读哪些 ABI 文档?我正在阅读的 ARM AAPCS 没有那个关键字。 infocenter.arm.com/help/topic/com.arm.doc.ihi0042d/…
  • 我做了一些谷歌搜索,我知道发生了什么。你能写一点汇编代码来重击这些调用吗?或者这种情况经常发生?
  • @jbcreix,不,您只需要编写一个小程序集填充程序来进行函数调用,然后将结果压入堆栈并返回。你可以让其他一切保持不变。
  • @jbcreix,同样的问题。然后你需要让你的汇编 shim/thunk 代码成为公共 API,并从它调用到你的 C 代码中,将堆栈的值 off 拉入寄存器,然后返回。同样的问题,只是往另一个方向发展。
  • 为什么不呢?应该是 10 条指令....

标签: c gcc struct arm


【解决方案1】:

我不确定这是否可行,但您可以尝试使用pcs function attribute

struct bar foo(int a, int b) __attribute__((pcs("aapcs")));
struct bar foo(int a, int b) {
    ...
}

【讨论】:

  • 此行为未在 AAPCS 中指定;这是一个 RVCT 功能。
【解决方案2】:

应要求作为答案发布:

如果您必须生成一个可以与您的编译器不支持的 ABI 一起使用的二进制文件,那么您就会遇到一些麻烦。在 C 语言中您无能为力。在这种情况下,您将需要依靠汇编语言编程并重复必要的调用。有两种可能:

  1. 从您的二进制文件调用到另一个二进制文件的 ABI。
  2. 从其他二进制文件调用到您的二进制文件的 ABI。

这两个问题都以类似方式解决。要从您的代码中调出,您需要在程序集中创建 shim 函数,围绕调用约定来匹配外部 ABI,然后从那里调用外部函数。与您的 C 代码的不同之处在于,现在要进行外部调用,您调用内部汇编例程,它会执行所需的任何外部调用,然后将返回值以您的 C 代码可以理解的格式放回,并返回.

要支持从外部二进制文件调用您的代码,您可以做同样的事情,但相反。二进制文件的入口点将是小型汇编例程,它们将外部 ABI 转换为 C 代码可以理解的格式,调用内部函数,然后将返回值放回外部代码可以理解的格式并返回。

恐怕有时没有好的解决方案。

【讨论】:

    【解决方案3】:

    "Procedure Call Standard for the ARM Architecture" 具体说(第 5.4 节:结果返回):

    “在R0中返回一个不大于4字节的复合类型。”

    “大于 4 字节的复合类型 ... 存储在内存中的地址处,该地址在调用函数时作为额外参数传递...。”

    我知道有些 CPU 有几种不同的“标准”ABI。 但我的印象是,几乎所有 ARM 编译器都使用相同的 ABI。

    您是否有任何证据表明 GCC使用此标准 ABI?

    您是否介意发布一个链接,指向与此标准 ABI 不同的 ARM ABI 的任何信息 - 调用者或被调用者使用的 ABI,或两者兼而有之?

    【讨论】:

    • 坦率地说,我不记得了,我现在还没有准备好检查它的机器,但是重新阅读我自己的话,不,gcc当时没有遵循,否则,如果没有__value_in_regs,代码会生成正确的程序集,我不会问这个问题。
    【解决方案4】:

    您可以使用“long long”为两个寄存器执行此操作,如本页提供的“ARM 体系结构的过程调用标准”链接中所述。

    long long test(uint32_t a, uint32_t b, uint32_t c, uint32_t d)
    {
        long long ret;
        ret = a+b;
        ret <<= 32;
        ret |= c + d;
        return ret;
    }
    

    将被简单地编译为:

    0002dbb8 <test>:
    2dbb8:       1841            adds    r1, r0, r1
    2dbba:       18d0            adds    r0, r2, r3
    2dbbc:       4770            bx      lr
    

    你调用函数中的ret &amp; 0xFFFFFFFFret &gt;&gt; 32将被r0和r1无缝替换。

    甚至可以通过使用“Containerized 向量”:

    typedef uint32_t uint32x4_t __attribute__ ((vector_size (16)));
    
    uint32x4_t test2(uint32_t a, uint32_t b, uint32_t c, uint32_t d)
    {
        uint32x4_t ret = { a + 1, b + 2, c + 3, d + 4};
        // to access elements: ret[0], ret[1], ...
        return ret;
    }
    

    编译为:

    0002dbb8 <test2>:
    2dbb8:       3001            adds    r0, #1
    2dbba:       3102            adds    r1, #2
    2dbbc:       3203            adds    r2, #3
    2dbbe:       3304            adds    r3, #4
    2dbc0:       4770            bx      lr
    

    请注意,在上面的文档中它被称为 SIMD/NEON 功能,但我只是在 Thumb 模式下的 Cortex M0 上实现了它,没有 NEON 支持。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-23
      • 1970-01-01
      • 2019-01-28
      • 1970-01-01
      相关资源
      最近更新 更多