Tuesday, April 17, 2012

GCC: new inter-procedural constant propagation pass


GCC 4.7 has "the inter-procedural constant propagation pass" rewritten, which
brought generic function specialization to C world.

Suppose, we have the following example:

 void foo(bool f)
 {
        char *p;
        if (f) {
                p = (char *) malloc(256);
                if (!p) {
                        printf("Mem alloc error\n");
                        return;
                }
            /* something extremly valuable */
                free(p);
        } else {
                p = (char *) malloc(256);
                if (!p) {
                        printf("Mem alloc error\n");
                        return;
                }
            /* something very important */
                free(p);
        }
}


int main()
{
         foo(TRUE);
         foo(FALSE);
         return 0;
}
 

GCC now able to produce the following main():

(gdb) disassemble main
<+0>:    push   %rax
<+1>:    callq  0x4005cc <foo.part.0>
<+6>:    xor    %edi,%edi
<+8>:    callq  0x400649 <foo>
<+13>:    xor    %eax,%eax
<+15>:    pop    %rdx
<+16>:    retq  



Note, that GCC has actually generated two functions:
-- foo.part.0 at 0x4005cc
-- foo at 0x400649

for each possible arg values: TRUE and FALSE.

Since compiler now changes function call code it has to protect himself from "incorrect"
outside calls, e.g. foo(TRUE) instead of foo.part.0(TRUE)

That's the reason "master" copy of foo() has switch() at the beginning:
(gdb) disassemble foo
<+0>:    test   %dil,%dil
<+3>:    je     0x400653
<foo+10>
<+5>:    jmpq   0x4005cc  <foo.part.0>
<+10>:    push   %rbx
<+11>:    mov    $0x40079e,%edi
[...]