Son olarak “asm” ve “c” volatile anahtar sözcüklerine bakalım. Özel olarak ikisi arasından_spesifikasyondaki tanımı ile_ “c” volatile günümüzde artık bir şey ifade etmiyor. Lkml’de son zamanlarda da çok tartışıldı. Biz de lkml ve gcc dokümanı ışığında bahsi geçen iki anahtar sözcüğün ne zaman ve nerelerde kullanılması gerektiğine bakalım.

Önce kullanımı daha bariz olan “asm” volatile:

Using the GNU Compiler Collection - Extended Asm bölümü içerisinden bir alıntı:

“If an asm has output operands, GCC assumes for optimization purposes the instruction has no side effects except to change the output operands. This does not mean instructions with a side effect cannot be used, but you must be careful, because the compiler may eliminate them if the output operands aren’t used, or move them out of loops, or replace two with one if they constitute a common subexpression. Also, if your instruction does have a side effect on a variable that otherwise appears not to change, the old value of the variable may be reused later if it happens to be found in a register.”

Özet ve örneklerle gcc asm blok’unuza şu optimizasyonları yapabilir:

(1) gcc, optimizasyon amaçlı bir komutun tek etkisinin _eğer tanımlı ise_ çıkış değerine olduğunu kabul eder. Ve bu değer bir yerde kullanılmıyorsa asm bloğunu kaldırabilir.

int Terminator(void)
{
    int civil;
    asm("addl $99, %0":"=c"(civil)::"%eax");
}

Terminator:
    pushl	%ebp
    movl	%esp, %ebp
    popl	%ebp
    ret

int Terminator2(void)
{
    int civil;
    /* yeniden programlayarak artık yok etmemesi gerektiğini söyleyelim */
    asm volatile("addl $99, %0":"=c"(civil));
}

Terminator2:
    pushl	%ebp
    movl	%esp, %ebp
#APP
    addl $99, %ecx
#NO_APP
    popl	%ebp
    ret

(2) gcc, inline asm blok’unu döngü içinde gereksiz yere çalışmasın diye dışarı çıkarabilir.

extern int globalwarming;
int report(void)
{
    int dummy, co2;
    for (co2 = 0; co2 < 100; co2++) {
        globalwarming++;
        asm("movl $666, %0":"=c"(dummy));
    }
    return dummy;
}

report:
    pushl	%ebp
#APP
    movl $666, %ecx              <—— döngü dışında
#NO_APP
    movl	%esp, %ebp
    pushl	%esi
    movl	globalwarming, %esi
    pushl	%ebx
    movl	$99, %eax
    movl	%ecx, %ebx
    .p2align 4,,15
.L5:
    leal	1(%esi), %edx
    decl	%eax
    movl	%edx, %esi
    jns	.L5
    movl	%edx, globalwarming
    movl	%ebx, %eax
    popl	%ebx
    popl	%esi
    popl	%ebp
    ret

extern int globalwarming;
int report(void)
{
    int dummy, co2;
    for (co2 = 0; co2 < 100; co2++) {
        globalwarming++;
        asm volatile("movl $666, %0":"=c"(dummy));
    }
    return dummy;
}

report:
    pushl	%ebp
    movl	$99, %eax
    movl	%esp, %ebp
    pushl	%esi
    movl	globalwarming, %esi
    pushl	%ebx
    .p2align 4,,15
.L5:
    leal	1(%esi), %edx
    movl	%edx, %esi
#APP
    movl $666, %ecx           <——— döngü içinde
#NO_APP
    decl	%eax
    jns	.L5
    popl	%ebx
    movl	%ecx, %eax
    movl	%edx, globalwarming
    popl	%esi
    popl	%ebp
    ret

(3) gcc, kod’da tekrarlanan kısımlardan birini elimine edip bir önceki’nin çıktısı’nı yeniden kullanabilir. (Common Subexpression Elimination(CSE))

Kodu C ile yazıp optimizasyonsuz ve -O2 ile derledikten sonra çıktılarda gcc cse etkisi görülebilir. Ama volatile’ın inline assembly üzerindeki etkisini görmek istiyoruz. Uğraştım, olmadı. C kodundan üretilen optimizasyonsuz kodu alarak, inline assembly haline çevirdim. Çıktı birebir aynı olacak şekilde, asm üzerinde oynadım ama yine -O2 ile derlediğimde optimize etmedi. Dokümantasyondaki ilgili kısım şu şekilde geçiyor: “replace two with one if they constitute a common subexpression”.

Ben de #gcc kanalına gidip sordum. Dokümanda geçen “replace two”; iki “common expression” değil, iki inline asm bloku anlamına geliyormuş. Dokümanda biraz daha iyi ifade edilebilir miydi acaba? :)

Buna tersten bakalım, önce volatile ile:

extern int a,b,c;
int testelimination()
{
    asm volatile("movl %0, %1":"=r"(a):"r"(c));
    asm volatile("movl %0, %1":"=r"(b):"r"(c));
}

testelimination:
    pushl	%ebp
    movl	%esp, %ebp
    movl	c, %eax
#APP
    movl %eax, %eax
#NO_APP
    movl	%eax, a
    movl	c, %eax
#APP
    movl %eax, %eax
#NO_APP
    movl	%eax, b
    popl	%ebp
    ret

extern int a,b,c;
int testelimination()
{
    asm("movl %0, %1":"=r"(a):"r"(c));
    asm("movl %0, %1":"=r"(b):"r"(c));
}

testelimination:
    pushl	%ebp
    movl	c, %eax
    movl	%esp, %ebp
    popl	%ebp
#APP
    movl %eax, %eax
#NO_APP
    movl	%eax, a
    movl	%eax, b
    ret

“c” volatile son…