Re: Inline assembler + register values

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Andrew Haley wrote:
> On 01/25/2012 12:28 PM, Georg-Johann Lay wrote:
>> Andrew Haley wrote:
>>> On 01/24/2012 11:35 AM, Konstantin Vladimirov wrote:
>>>> This problem is backend independent, so I build reproduction in x86
>>>> backend. Consider code:
>>>>
>>>> int func(int x);
>>>>
>>>> int test(int x, int *data)
>>>> {
>>>>   int retval;
>>>>   register int *buffer asm ("eax");
>>>>
>>>>   buffer = data;
>>>>   retval = func(x);
>>>>   __asm__ __volatile__ (".internal_label _t." "4096"
>>>>                         ".%0.%1.%2:;nop"::"ri" (buffer), "ri" (4137), "ri" (4));
>>>>   return retval;
>>>> }
>>>>
>>>> here nop will be replaced for something, that essentially uses %eax,
>>>> but it is not actually required for minimal reproduction.
>>>>
>>>> being compiled with gcc (SUSE Linux) 4.5.0 20100604 [gcc-4_5-branch
>>>> revision 160292]
>>>>
>>>> with following options:
>>>>
>>>> gcc -m32 -O2 -S -fomit-frame-pointer reprox86.c
>>>>
>>>> yields assembler:
>>>>
>>>>   .type test, @function
>>>> test:
>>>>   subl  $28, %esp
>>>>   movl  32(%esp), %eax
>>>>   movl  %eax, (%esp)
>>>>   call  func                           // <--- here eax is clobbered
>>>>   .internal_label _t.4096.%eax.$4137.$4:;nop 0
>>>>   addl  $28, %esp
>>>>   ret
>>>>
>>>> That looks quite odd. I have a question -- why compiler doesn't save &
>>>> restore buffer (i.e. %eax) value around function call, when we
>>>> specifically pointed to %eax usage in the inline assembler
>>>> instruction?
>>> Well, hold on: you've said that you want buffer to live in register
>>> EAX for the entire duration of test().  GCC assumes that you know what
>>> you are doing, know that EAX is call-clobbered, and are using EAX
>>> because you want to pass arguments to func() in EAX.  In other words,
>>> it's not a bug, it's a feature.
>> No, it's a bug.
>>
>> The OP says that %eax is not used to pass an argument to func().
>>
>> It is fine if %eax is clobbered while calling func() if tha ABI says so.
>>
>> The "asm" in the local definition of buffer should have no effect except at the
>> time it is used as operand to an inline assembler statement.
> 
> Why do you say that?  People writing interpreters have for many
> years used inline asm declarations to force variables to live in
> certain hard registers.

Because the code breaks if, for example, the compiler introduces an implicit
libgcc call. That code is not mentioned in the source and could otherwise
clobber some if the values. Just as with other values, the values in the local
asm registers have to be saved/restored around such calls.

If a variable is to be tied always to a specific hard register, then it can be
accomplished by global register variable.

With hard-binding local register variables to hard registers you really run
into problems with code like the following. Suppose the ABI says that the first
parameter will be passed in r1 and the second in r2:

void foo (int r1, int r2)
{
    register int q1 asm ("r1") = r2;
    register int q2 asm ("r2") = r1;
    ...
}


>> A local asm variable does *not* say "this variable will be held in the register
>> throughout the whole function or lifetime of the variable". Is just ensures
>> that the variable is reloaded to respective asm operand.
> 
> It always used to.  If not, this is a regression.

The docs say:

> Defining such a register variable does not reserve the register;
> it remains available for other uses in places where flow control
> determines the variable's value is not live.

> This option does not guarantee that GCC will generate code that
> has this variable in the register you specify at all times.
> You may not code an explicit reference to this register in the assembler
> instruction template part of an asm statement and assume it will always
> refer to this variable. However, using the variable as an asm operand
> guarantees that the specified register is used for the operand.

Maybe different users/code use local registers to accomplish different things
and make different assumptions on what local registers must do or must not do.

There are many use cases for local regs and many existing code, for example
writing an interface to a non-ABI function like the following. Again, the first
parameter is passed and returned in r1:

void foo (int r1)
{
    register int r3 asm ("r3") = r1*5;

    // bar gets and returns values in r3
    asm ("call bar" : "+r" (r3));

    return r3;
}

In some of my applications I like to tie a local variable to a specific
register or register class. Binding it globally is not option because that
renders the register to a fixed one. The reason to bind the variable to a
register serves to generate better code; it's rather a hack around optimization
flaws in the compiler. But the code still works if the variable is not in the
specified register class because the code using the register variable is all
vanilla C code and no inline assembler.

The situation is when accessing many struct components like a.b where indirect
addressing (&a)->b yield better code. Writing

 struct A * pa = &a;
 pa->b = 0;

does not help because the compiler knows that pa->b can be replaced with a.b.

I guess you have similar requirements in the interpreter, i.e. the local
variable shall serve to yield small and/or fast code, but is it not essential
to the code that the variable is kept in the register at each point in time?


> 
> Andrew.
> 



[Index of Archives]     [Linux C Programming]     [Linux Kernel]     [eCos]     [Fedora Development]     [Fedora Announce]     [Autoconf]     [The DWARVES Debugging Tools]     [Yosemite Campsites]     [Yosemite News]     [Linux GCC]

  Powered by Linux