Search code examples
cgccoptimization

How to optimize "don't care" argument with gcc?


Sometimes a function doesn't use an argument (perhaps because another "flags" argument doesn't enable a specific feature).

However, you have to specify something, so usually you just put 0. But if you do that, and the function is external, gcc will emit code to "really make sure" that parameter gets set to 0.

Is there a way to tell gcc that a particular argument to a function doesn't matter and it can leave alone whatever value it is that happens to be in the argument register right then?

Update: Someone asked about the XY problem. The context behind this question is I want to implement a varargs function in x86_64 without using the compiler varargs support. This is simplest when the parameters are on the stack, so I declare my functions to take 5 or 6 dummy parameters first, so that the last non-vararg parameter and all of the vararg parameters end up on the stack. This works fine, except it's clearly not optimal - when looking at the assembly code it's clear that gcc is initializing all those argument registers to zero in the caller.


Solution

  • Please don't take below answer seriously. The question asks for a hack so there you go.

    GCC will effectively treat value of uninitialized variable as "don't care" so we can try exploiting this:

    int foo(int x, int y);
    
    int bar_1(int y) {
      int tmp = tmp;  // Suppress uninitialized warnings
      return foo(tmp, y);
    }
    

    Unfortunately my version of GCC still cowardly initializes tmp to zero but yours may be more aggressive:

    bar_1:
    .LFB0:
      .cfi_startproc
      movl  %edi, %esi
      xorl  %edi, %edi
      jmp   foo
      .cfi_endproc
    

    Another option is (ab)using inline assembly to fake GCC into thinking that tmp is defined (when in fact it isn't):

    int bar_2(int y) {
      int tmp;
      asm("" : "=r"(tmp));
      return foo(tmp, y);
    }
    

    With this GCC managed to get rid of parameter initializations:

    bar_2:
    .LFB1:
      .cfi_startproc
      movl  %edi, %esi
      jmp   foo
      .cfi_endproc
    

    Note that inline asm must be immediately before the function call, otherwise GCC will think it has to preserve output values which would harm register allocation.