循环展开优化,这是如何工作的

Ram*_*uri 2 c assembly

考虑这个C代码:

int sum=0;
for(int i=0;i<5;i++)
    sum+=i;
Run Code Online (Sandbox Code Playgroud)

这可以通过这种方式转换为(伪)程序集(无需循环展开):

% pseudo-code assembly
ADDI $R10, #0   % sum
ADDI $R11, #0   % i
LOOP:
ADD $R10, $R11
ADDI $R11, #1
BNE $R11, #5 LOOP
Run Code Online (Sandbox Code Playgroud)

所以我的第一个问题是如何在这两种方式之间使用循环展开来翻译此代码:

1)

ADDI $R10, #0
ADDI $R10, #0
ADDI $R10, #1
ADDI $R10, #2
ADDI $R10, #3
ADDI $R10, #4
Run Code Online (Sandbox Code Playgroud)

2)

   ADD $R10, #10
Run Code Online (Sandbox Code Playgroud)

编译器是否能够优化代码并直接知道它必须在不执行所有总和的情况下添加10?

此外,是否有可能使用分支指令阻止管道?我必须这样写吗:

% pseudo-code assembly
ADDI $R10, #0   % sum
ADDI $R11, #0   % i
LOOP:
ADD $R10, $R11
ADDI $R11, #1
NOP   % is this necessary to avoid the pipeline blocking?
NOP
NOP
NOP
BNE $R11, #5 LOOP
Run Code Online (Sandbox Code Playgroud)

为了避免fetch-decode-exe-mem-write返回周期被分支中断?

Mik*_*wan 10

这更多是为了演示编译器能够做什么,而不是每个编译器都会做什么.来源:

#include <stdio.h>

int main(void)
{
    int i, sum = 0;

    for(i=0; i<5; i++) {
        sum+=i;
    }

    printf("%d\n", sum);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

请注意printf我已添加.如果未使用该变量,编译器将优化整个循环.

使用-O0进行编译(无优化)

gcc -Wall -O0 -S -c lala.c:

.L3:
    movl    -8(%rbp), %eax
    addl    %eax, -4(%rbp)
    addl    $1, -8(%rbp)
.L2:
    cmpl    $4, -8(%rbp)
    jle .L3
Run Code Online (Sandbox Code Playgroud)

循环以'哑'的方式发生,-8(%rbp)作为变量i.

使用-O1进行编译(优化级别1)

gcc -Wall -O1 -S -c lala.c:

movl    $10, %edx
Run Code Online (Sandbox Code Playgroud)

循环已完全删除并替换为等效值.


在展开时,编译器会查看将发生多少次迭代,并尝试通过执行更少的迭代来展开.例如,循环体可能重复两次,这将导致分支数量减半.C中的这种情况:

int i = 0, sum = 0;

sum += i;
i++;

for(; i<5;i++) {
    sum+=i;
    i++;
    sum+=i;
}
Run Code Online (Sandbox Code Playgroud)

请注意,必须从循环中提取一次迭代.这是因为5是奇数,因此通过复制内容不能简单地减半.在这种情况下,循环只会输入两次.汇编代码由-O0:

    movl    -8(%rbp), %eax
    addl    %eax, -4(%rbp)
    addl    $1, -8(%rbp)
    jmp .L2
.L3:
    movl    -8(%rbp), %eax
    addl    %eax, -4(%rbp)
    addl    $1, -8(%rbp)
    movl    -8(%rbp), %eax
    addl    %eax, -4(%rbp)
    addl    $1, -8(%rbp)
.L2:
    cmpl    $4, -8(%rbp)
Run Code Online (Sandbox Code Playgroud)

完全展开C:

for(i=0; i<5;i++) {
    sum+=i;
    i++;
    sum+=i;
    i++;
    sum+=i;
    i++;
    sum+=i;
    i++;
    sum+=i;
}
Run Code Online (Sandbox Code Playgroud)

这次循环实际上只输入一次.组装生产-O0:

.L3:
    movl    -8(%rbp), %eax
    addl    %eax, -4(%rbp)
    addl    $1, -8(%rbp)
    movl    -8(%rbp), %eax
    addl    %eax, -4(%rbp)
    addl    $1, -8(%rbp)
    movl    -8(%rbp), %eax
    addl    %eax, -4(%rbp)
    addl    $1, -8(%rbp)
    movl    -8(%rbp), %eax
    addl    %eax, -4(%rbp)
    addl    $1, -8(%rbp)
    movl    -8(%rbp), %eax
    addl    %eax, -4(%rbp)
    addl    $1, -8(%rbp)
.L2:
    cmpl    $4, -8(%rbp)
    jle .L3
Run Code Online (Sandbox Code Playgroud)