代码之家  ›  专栏  ›  技术社区  ›  Niklas Peter

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

  •  1
  • Niklas Peter  · 技术社区  · 6 年前

    功能 readWord 应返回:

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

    以下示例是否遵循aapcs? 考虑到AAPC,有没有更好的方法可以做到这一点?

    // 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字节的复合类型,或者其大小不能由调用方和 被调用方存储在内存中的一个地址,该地址在调用函数时作为额外参数传递(_§5.5, 规则A.4)。要用于结果的内存可以在函数调用期间的任何点进行修改。

    但是,我不知道它是一个集装箱化的64位向量还是一个聚合复合类型,甚至是其他类型:

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

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

    1 回复  |  直到 6 年前
        1
  •  2
  •   Peter Cordes    6 年前

    您引用的文档指出,任何不适合于单个寄存器的复合类型都将通过隐藏指针返回。这将包括一个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