过程调用ARM®体系结构的标准:2个单独但相关的返回值

时间:2018-06-17 14:17:42

标签: assembly arm calling-convention

函数readWord应返回:

  • 状态代码(错误或成功,例如,0 =成功,> 0错误代码)
  • 从数据源读取的数据(仅在状态代码表示成功时才有效)

以下示例是否遵循AAPCS? 考虑到AAPCS,还有更好的方法吗?

// OUT:
//  R0 (status code): 0 if valid, else > 0
//  R1 (data):  
//  if the R0 represents a successful read: data, which was read,
//  else: undetermined
read32:
    PUSH    {LR}

    LDR     R0, =memoryMappedAddressOfDataSource
    LDR     R4, [R0]
    BL      checkValidRead
    // If the read was invalid, directly return the error 
    // code set by checkValidRead in R0 and do not change R1.
    CBNZ    R0, read32_return

    // R0 is 0, so the read was valid and the the data is returned in R1.
    MOV     R1, R4

read32_return:  
    POP     {PC}

// IN: none
// Checks a special status register to determine, 
// whether the last read was successful.    
// OUT:
//  R0: 0 if valid, else > 0
checkValidRead:
    ...

来自AAPCS(第18页):

  

在r0和r1中返回双字大小的基本数据类型(例如,长长,双和64位容器化向量)。

     

大于4个字节的复合类型,或者调用者和调用者无法静态确定其大小   callee,在调用函数时作为额外参数传递的地址存储在内存中(§5.5,   规则A.4)。在函数调用期间的任何时候都可以修改用于结果的内存。

然而,我不知道它是否是一个容器化的64位向量或聚合复合类型甚至是......否则:

  

容器化载体的内容对大多数程序调用标准是不透明的:唯一定义的方面   它的布局是内存格式(基本类型存储在内存中的方式)和   过程调用接口上的不同类别的寄存器。

     

复合类型是一个或多个基本数据类型的集合,作为单个实体处理   程序调用级别。复合类型可以是以下任何一种:   聚合,其中成员按顺序排列在内存中[...]

1 个答案:

答案 0 :(得分:2)

您引用的文档说,任何不适合单个寄存器的复合类型都是通过隐藏指针返回的。这将包括一个C结构。

只有可以在寄存器对中返回单个宽整数或FP类型。

寄存器对比通过隐藏指针存储/重新加载更有效,所以不幸的是你必须破解调用约定而不是只返回struct { uint32_t flag, value; }

要描述您想要C编译器的调用约定,请告诉它您返回uint64_t,并将其拆分为两个32位整数变量。这将免费发生,因为编译器已将它们放在不同的寄存器中。

例如(source + asm on the Godbolt compiler explorer)。我使用了一个联盟,但你可以同样使用一个班次。

#include <stdint.h>

uint64_t read32(void);

union unpack32 {
    uint64_t u64;
    uint32_t u32[2];
};

void ext(uint32_t);        // something to do with a return value

unsigned read32_wrapper() {
    union unpack32 ret = { read32() };
    if (ret.u32[0]) {
        ext(ret.u32[1]);
    }
    return ret.u32[0];
}

编译如下:

    push    {r4, lr}
    bl      read32
    subs    r4, r0, #0          @ set flags and copy the flag to r4

    movne   r0, r1
    blne    ext                 @ the if() body.

    mov     r0, r4              @ always return the status flag
    pop     {r4, lr}
    bx      lr