우리는 당신의 예를 보면 godbolt.org
에서 컴파일러 탐색기를 사용할 수 있습니다. 우리는 다음과 같은 테스트 벤치 코드를 사용합니다 :
int test() {
int a[15] = {0};
int b[15] = {0};
for (int i = 0; i < 15; i++){
a[b[i]]++;
}
return 0;
}
Godbolt는 86 어셈블리가 아닌 LLVM 바이트 코드를 보여줍니다,하지만 난 무슨 일이 일어나고 있는지 보여 그것을 조금 정리했습니다. 여기가 -O0 -m32
에 있습니다 :
test():
# set up stack
.LBB0_1:
cmp dword ptr [ebp - 128], 15 # i < 15?
jge .LBB0_4 # no? then jump out of loop
mov eax, dword ptr [ebp - 128] # load i
mov eax, dword ptr [ebp + 4*eax - 124] # load b[i]
mov ecx, dword ptr [ebp + 4*eax - 64] # load a[b[i]]
add ecx, 1 # increment it
mov dword ptr [ebp + 4*eax - 64], ecx # store it back
mov eax, dword ptr [ebp - 128]
add eax, 1 # increment i
mov dword ptr [ebp - 128], eax
jmp .LBB0_1 # repeat
.LBB0_4:
# tear down stack
ret
우리가 기대하는 것처럼이 보인다 : 루프가 명확하게 볼 수 있습니다 그리고 우리가 나열된 모든 단계를 수행합니다. 우리가 에서 컴파일 할 경우, 우리는 루프가 여전히 볼 수 있지만 훨씬 간단 :
이
test(): # @test()
# set up stack
.LBB0_1:
mov ecx, dword ptr [esp + 4*eax] # load b[i]
inc dword ptr [esp + 4*ecx + 60] # increment a[b[i]]
inc eax # increment i
cmp eax, 15 # compare == 15
jne .LBB0_1 # no? then loop
# tear down stack
ret
연타 지금 inc
명령 (유용) 사용주의는 루프 카운터 i
의 eax
레지스터를 사용할 수있다 (스트레이트) , 조건 검사를 루프의 맨 아래로 옮겼습니다 (아마도 더 좋을 것입니다). 그래도 우리는 원래 코드를 인식 할 수 있습니다. 이제 -O2 -m32 -march=i386
으로 시도해 보겠습니다.
test():
xor eax, eax # does nothing
ret
그래? 예.
clang
은 a
배열을 함수 밖에서 절대 사용할 수 없다는 것을 감지했습니다. 즉, 증분 작업을 수행하면 프로그램의 다른 부분에는 아무런 영향을 미치지 않으며, 사라지면 아무도 그 프로그램을 놓치지 않습니다.
증가를 제거하면 빈 몸체와 부작용이없는 루프가 나타납니다. 제거 할 수도 있습니다. 차례로 루프를 제거하면 (모든 의도와 목적으로) 빈 함수가 남습니다.
이 빈 함수는 LLVM 바이트 코드 (ret i32 0
)에서 보았을 가능성이 큽니다.
이것은 매우 과학적인 설명과 다를 수 있습니다 소요 clang
단계는 아니지만 나는 예는 비트를 지 웁니다 바랍니다. 원하는 경우 as-if rule에서 읽을 수 있습니다. https://godbolt.org/을 조금만 돌아 보는 것이 좋습니다. 예를 들어, a
및 b
을이 기능 외부로 이동하면 어떻게되는지보십시오.
최적화를 단계별로보고 싶다면 다음을 시도해보십시오.'clang -mllvm -print-after-all' – Joky