簡體   English   中英

將O2優化的for循環從組件轉換為C

[英]Translating O2 optimized for-loop from assembly to C

這是一個作業問題。 我正在嘗試從以下匯編代碼(x86 linux計算機,使用gcc -O2優化編譯)中獲取信息。 我評論了每個部分以顯示我所知道的。 我的大部分假設可能是錯誤的,但是我已經做了足夠的搜索,直到我知道我應該在這里提出這些問題。

.section        .rodata.str1.1,"aMS",@progbits,1

.LC0:
    .string "result %lx\n"  //Printed string at end of program
    .text

main:
.LFB13: 
    xorl    %esi, %esi         // value of esi = 0; x
    movl    $1, %ecx           // value of ecx = 1; result
    xorl    %edx, %edx         // value of edx = 0; Loop increment variable (possibly mask?)
.L2:
    movq    %rcx, %rax         // value of rax = 1; ?
    addl    $1, %edx           // value of edx = 1; Increment loop by one;
    salq    $3, %rcx           // value of rcx = 8; Shift left rcx;
    andl    $3735928559, %eax  // value of eax = 1; Value AND 1 = 1;
    orq     %rax, %rsi         // value of rsi = 1; 1 OR 0 = 1;
    cmpl    $22, %edx          // edx != 22 
    jne     .L2                // if true, go back to .L2 (loop again)
    movl    $.LC0, %edi        // Point to string
    xorl    %eax, %eax         // value of eax = 0;
    jmp     printf             // print
.LFE13: ret                    // return

我應該將其轉換為以下C代碼,並填入空格

#include <stdio.h>
int main()
{
 long x = 0x________;
 long result = ______;
 long mask;
 for (mask = _________; mask _______; mask = ________) {
   result |= ________;
}
 printf("result %lx\n",result);
}

我有兩個問題和健全性檢查,我想確保自己做對了,因為我發現的所有類似示例都不是針對優化代碼的。 自己編寫一些試驗后,我得到的結果很接近,但是L2的中間部分始終關閉。

我的理解

首先,將esi與自身進行異或,得到0,由x表示。 然后將1添加到ecx中,這將由變量result表示。

x = 0;     result = 1;

然后,我相信循環增量變量存儲在edx中並設置為0。這將在for循環的第三部分(更新表達式)中使用。 我也認為此變量必須是mask,因為稍后在edx上添加1,表示循環增量(mask = mask ++),並且在for循環的中間部分對edx進行了比較(測試表達式又稱為mask!= 22)。 )。

mask = 0; (in a way)

然后進入循環,將rax設置為1。由於我沒有聲明第四個變量,所以我根本不知道在哪里使用了它,盡管稍后會顯示它要被ANDED和清零。

movq %rcx, %rax;

然后將循環變量加一

addl $1, %edx;

下一部分讓我感覺到最少

我覺得接下來的三個操作組成了循環的主體表達,但是我不知道該如何處理它們。 它會導致類似於| = x ...的結果,但我不知道還有什么

salq    $3, %rcx      
andl    $3735928559, %eax  
orq     %rax, %rsi

其余的我覺得我掌握的很好。 進行比較(如果mask!= 22,再次循環),並打印結果。

我遇到的問題我不了解幾件事。

1)我不明白如何弄清楚我的變量。 程序集中似乎有3個硬編碼的變量以及一個增量或臨時存儲變量(rax,rcx,rdx,rsi)。 我認為rsi將是x ,而rcx將是result ,但是我不確定mask是rdx還是rax,無論哪種方式,最后一個變量是什么?

2)我不確定的3種表達方式有什么作用? 我覺得我以某種方式將它們與增量混合在一起,但是在不知道變量的情況下,我不知道如何解決這個問題。

任何和所有的幫助將是巨大的,謝謝!

答案是 :

#include <stdio.h>
int main()
{
    long x = 0xDEADBEEF;
    long result = 0;
    long mask;
    for (mask = 1; mask != 0; mask = mask << 3) {
        result |= mask & x;
    }
    printf("result %lx\n",result);
}

在組裝中:

rsiresult 我們推論這是因為它是唯一獲得OR ed的值,並且它是printf的第二個參數(在x64 linux中,參數按順序存儲在rdirsirdx和其他一些參數中)。

x是設置為0xDEADBEEF的常量。 這是不確定的,但確實有道理,因為它似乎在C代碼中設置為常量,並且此后似乎沒有設置。

現在,剩下的內容將被GCC的反優化功能所迷惑。 您會發現,GCC檢測到該循環將被准確執行21次,並且認為巧妙地處理該條件並將其替換為無用的計數器很明智。 知道這一點,我們看到edx是無用的計數器,而rcxmask 然后,我們可以推斷出實際條件和實際的“增量”操作。 我們可以在程序集中看到<<= 3 ,並注意,如果將64位int左移22次,則變為0(將3移22意味着將66位移位,因此全部移出了)。

不幸的是,這種反優化對於GCC來說確實很常見。 該組件可以替換為:

.LFB13: 
    xorl    %esi, %esi
    movl    $1, %ecx
.L2:
    movq    %rcx, %rax
    andl    $3735928559, %eax
    orq     %rax, %rsi
    salq    $3, %rcx // implicit test for 0
    jne     .L2
    movl    $.LC0, %edi
    xorl    %eax, %eax
    jmp     printf

它的作用完全相同,但是我們刪除了無用的計數器並保存了3條匯編指令。 它還與C代碼更好地匹配。

讓我們往后一點。 我們知道result必須是printf()的第二個參數。 在x86_64調用約定中,這是%rsi 循環是.L2標簽和jne .L2指令之間的所有內容。 我們在模板中看到循環的末尾有一個result |=行,並且確實有一個以%rsi為目標的orl指令,因此可以檢出。 現在,我們可以在.main的頂部看到它的初始化.main

ElderBug是正確的,因為編譯器通過添加計數器來偽優化。 但是我們仍然可以弄清楚:當循環重復時,哪條指令在|=之后立即運行? 那一定是循環的第三部分。 循環主體之前會運行什么? 那一定是循環初始化。 不幸的是,您必須弄清楚在原始循環的第22次迭代中會發生什么,才能對循環條件進行逆向工程。 (但是sal是左移,而該行是原始循環條件的痕跡,在插入%rdx測試之前,該條件條件分支之后將是一個條件分支。)

請注意,在%rax %rcx進行修改之前,代碼在%rcx保留了mask值的副本,並且x被折疊為一個常數(仔細看一下andl行)。

另請注意,您可以將.S文件輸入gas以獲取.o文件,然后查看其作用。

暫無
暫無

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

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