Wszystkie odpowiedzi są doskonałe. Ale przede wszystkim chciałbym podzielić się przykładem.
Poniżej znajduje się mały program cpp:
#include <iostream>
int x;
int main(){
char buf[50];
x = 8;
if(x == 8)
printf("x is 8\n");
else
sprintf(buf, "x is not 8\n");
x=1000;
while(x > 5)
x--;
return 0;
}
Teraz wygenerujmy zestaw powyższego kodu (i wkleję tylko te części zestawu, które są tutaj istotne):
Polecenie generowania zestawu:
g++ -S -O3 -c -fverbose-asm -Wa,-adhln assembly.cpp
I montaż:
main:
.LFB1594:
subq $40, %rsp #,
.seh_stackalloc 40
.seh_endprologue
# assembly.cpp:5: int main(){
call __main #
# assembly.cpp:10: printf("x is 8\n");
leaq .LC0(%rip), %rcx #,
# assembly.cpp:7: x = 8;
movl $8, x(%rip) #, x
# assembly.cpp:10: printf("x is 8\n");
call _ZL6printfPKcz.constprop.0 #
# assembly.cpp:18: }
xorl %eax, %eax #
movl $5, x(%rip) #, x
addq $40, %rsp #,
ret
.seh_endproc
.p2align 4,,15
.def _GLOBAL__sub_I_x; .scl 3; .type 32; .endef
.seh_proc _GLOBAL__sub_I_x
W zestawie można zobaczyć, że kod zestawu nie został wygenerowany, sprintfponieważ kompilator założył, że xnie zmieni się on poza programem. To samo dotyczy whilepętli. whilePętla została całkowicie usunięta z powodu optymalizacji, ponieważ kompilator widział to jako bezużyteczny kod, a zatem bezpośrednio przypisany 5do x(patrz movl $5, x(%rip)).
Problem występuje, gdy co jeśli zewnętrzny proces / sprzęt zmieniłby wartość xgdzieś pomiędzy x = 8;a if(x == 8). Spodziewalibyśmy się, że elseblok zadziała, ale niestety kompilator usunął tę część.
Teraz, aby rozwiązać ten problem assembly.cpp, przejdźmy int x;do volatile int x;i wygenerujmy kod asemblera wygenerowany:
main:
.LFB1594:
subq $104, %rsp #,
.seh_stackalloc 104
.seh_endprologue
# assembly.cpp:5: int main(){
call __main #
# assembly.cpp:7: x = 8;
movl $8, x(%rip) #, x
# assembly.cpp:9: if(x == 8)
movl x(%rip), %eax # x, x.1_1
# assembly.cpp:9: if(x == 8)
cmpl $8, %eax #, x.1_1
je .L11 #,
# assembly.cpp:12: sprintf(buf, "x is not 8\n");
leaq 32(%rsp), %rcx #, tmp93
leaq .LC0(%rip), %rdx #,
call _ZL7sprintfPcPKcz.constprop.0 #
.L7:
# assembly.cpp:14: x=1000;
movl $1000, x(%rip) #, x
# assembly.cpp:15: while(x > 5)
movl x(%rip), %eax # x, x.3_15
cmpl $5, %eax #, x.3_15
jle .L8 #,
.p2align 4,,10
.L9:
# assembly.cpp:16: x--;
movl x(%rip), %eax # x, x.4_3
subl $1, %eax #, _4
movl %eax, x(%rip) # _4, x
# assembly.cpp:15: while(x > 5)
movl x(%rip), %eax # x, x.3_2
cmpl $5, %eax #, x.3_2
jg .L9 #,
.L8:
# assembly.cpp:18: }
xorl %eax, %eax #
addq $104, %rsp #,
ret
.L11:
# assembly.cpp:10: printf("x is 8\n");
leaq .LC1(%rip), %rcx #,
call _ZL6printfPKcz.constprop.1 #
jmp .L7 #
.seh_endproc
.p2align 4,,15
.def _GLOBAL__sub_I_x; .scl 3; .type 32; .endef
.seh_proc _GLOBAL__sub_I_x
Tutaj widać, że kody do montażu sprintf, printforaz whilepętla zostały wygenerowane. Zaletą jest to, że jeśli xzmienna zostanie zmieniona przez jakiś zewnętrzny program lub sprzęt, sprintfczęść kodu zostanie wykonana. Podobnie, whilepętla może być teraz używana do zajętego oczekiwania.