簡體   English   中英

了解有關AAPCS的gcc行為(在STM32上)

[英]Understanding gcc behaviour regarding AAPCS (on STM32)

編輯:我完全知道函數asmCopy可能不是functionnal,我的問題更多是關於gcc的有關在寄存器中傳遞的參數的行為。

我正在使用構建器為arm-none-eabi-gcc的STM32CubeIDE開發STM32H7

優化級別為-Os

我看到以下無法解釋的行為。 我進行了屏幕截圖,以獲取並行的asm和C代碼。

我的C代碼正在調用3個函數。 第一個和第三個具有完全相同的參數。

第二個不帶參數。 這是它的代碼:

static void Reset_Cycle_Counter(void)
{
    volatile unsigned long *DWT_CYCCNT = (unsigned long *)0xE0001004;
    volatile unsigned long *DWT_CONTROL = (uint32_t *)0xE0001000;

    // Reset cycle counter
    *DWT_CONTROL = *DWT_CONTROL & ~0x00000001 ;
    *DWT_CYCCNT = 0;
    *DWT_CONTROL = *DWT_CONTROL | 1 ;
}

第三個功能很特別:我正在嘗試編寫一些匯編代碼(現在可能很錯誤)。

static void __attribute__((noinline)) asmCopy(void *dst, void *src, uint32_t bytes)
{
    while (bytes--)
    {
        asm("ldrb r12,[r1], #1"); // src param is stored in r1, r12 can be modified without being restored after
        asm("strb r12,[r0], #1"); // dst paramis stored in r0
    }
}

在第一個函數調用(對memcpy)之前,向r0,r1和r2加載正確的值。

在此處輸入圖片說明

然后,在調用第三個函數之前,您可以在下面看到r1和r2中的參數錯誤(qspi_addr應該為0x90000000)。 在此處輸入圖片說明

我對AAPCS(ARM上的過程調用標准)的理解是,在調用子例程之前,應將函數的參數(如果有的話)裝入寄存器r0至r3。 並且子例程不需要保留或還原這些寄存器。 然后,第二個函數修改r1和r2是正常的。 因此,我希望編譯器在第三個調用之前更新r0,r1和r2。

如果將優化代碼更改為-O0,則確實可以達到預期的效果。

你怎么看 ?

您不能只打開一個內聯匯編塊並假定r0和r1仍然包含函數參數。 對此沒有任何保證。 如果需要使用參數,則需要正確地將其作為輸入和/或輸出操作數傳遞

static void __attribute__((noinline))
myAsmCopy(void* dst, void* src, uint32_t bytes) {
  asm volatile("1: cbz %[bytes], 1f \n"
               "ldrb r12, [%[src]], #1 \n"
               "strb r12, [%[dst]], #1 \n"
               "subs %[bytes], #1 \n"
               "b 1b \n"
               "1: \n"
               : [dst] "+&r"(dst), [src] "+&r"(src), [bytes] "+&r"(bytes)
               :
               : "cc", "memory", "r12");
}

GCC在此處提供了有關內聯匯編的大量文檔: https : //gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html

由於您顯然從未使用過任何一種方法,因此我必須對此提出嚴重建議。 如果“ C包含腳槍”,則內聯匯編會將6發左輪手槍放5枚子彈到您的頭部。

如果您嘗試詢問編譯器如何對其進行存檔,一切將變得更加容易

https://godbolt.org/z/rXxeRe

void __attribute__((noinline)) asmCopy(void *dst, void *src, uint32_t bytes)
{
    while (bytes--)
    {
        asm("ldrb r12,[r1], #1"); // src param is stored in r1, r12 can be modified without being restored after
        asm("strb r12,[r0], #1"); // dst paramis stored in r0
    }
}

void __attribute__((noinline)) asmCopy1(void *dst, void *src, uint32_t bytes)
{
    while (bytes--)
    {
        *(uint8_t *)dst++ = *(uint8_t *)src++;
    }
}

和代碼

asmCopy:
.L2:
        adds    r2, r2, #-1
        bcs     .L3
        bx      lr
.L3:
        ldrb r12,[r1], #1
        strb r12,[r0], #1
        b       .L2
asmCopy1:
        subs    r0, r0, #1
        add     r2, r2, r1
.L5:
        cmp     r1, r2
        bne     .L6
        bx      lr
.L6:
        ldrb    r3, [r1], #1    @ zero_extendqisi2
        strb    r3, [r0, #1]!
        b       .L5

我想我找到了答案。

在我正在測試的函數中(無論是我實現的最糟糕的函數,還是來自@Vinci的更好的函數),傳遞給函數的某些參數是全局變量(用於執行某些測試的虛擬數據數組)。

我的理解是,編譯器“修改”函數的原型以僅使用一個參數來構建函數。 其他參數被視為常量,僅在函數開始時相對加載了PC。

因此,我修改了代碼以調用完全相同的函數,但使用了本地易失性指針,問題消失了:我可以看到寄存器r0,r1和r2加載了預期的參數。

是否有意義 ?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM