2002-04-25 08:24:00

by Szekeres Istvan

[permalink] [raw]
Subject: Assembly question

Hi,

Long time ago (older gcc) this code snippet used to compile. Now it doesn't.
Do you asm gurus have any idea what is wrong?

void p_memset_dword( void *d, int b, int l )
{
__asm__ ("rep\n\t"
"stosl\n\t"
:
: "D" (d), "a" (b), "c" (l)
: "memory","edi", "eax", "ecx"
);
}

The compiler says:
a.c: In function `p_memset_dword':
a.c:9: Invalid `asm' statement:
a.c:9: fixed or forbidden register 0 (ax) was spilled for class AREG.

Thanks,
Pista


2002-04-25 23:53:14

by Mark Zealey

[permalink] [raw]
Subject: Re: Assembly question

On Thu, Apr 25, 2002 at 10:32:25AM +0200, Szekeres Istvan wrote:

> void p_memset_dword( void *d, int b, int l )
> {
> __asm__ ("rep\n\t"
> "stosl\n\t"
> :
> : "D" (d), "a" (b), "c" (l)
> : "memory","edi", "eax", "ecx"

An input or output operand is implicitly clobbered, so it should be written as:

: "D" (d), "a" (b), "c" (l)
: "memory"

Or so.

--

Mark Zealey (aka JALH on irc.openprojects.net: #zealos and many more)
[email protected]; [email protected]

UL++++>$ G!>(GCM/GCS/GS/GM) dpu? s:-@ a17! C++++>$ P++++>+++++$ L+++>+++++$
!E---? W+++>$ !w--- r++ !t---?@ !X---? !R- !tv b+ G+++ e>+++++ !h++* r!-- y--

2002-04-26 11:37:50

by Gabriel Paubert

[permalink] [raw]
Subject: Re: Assembly question

Mark Zealey wrote:

> On Thu, Apr 25, 2002 at 10:32:25AM +0200, Szekeres Istvan wrote:
>
>
>> void p_memset_dword( void *d, int b, int l ) { __asm__ ("rep\n\t"
"stosl\n\t"
>>

>> : : "D" (d), "a" (b), "c" (l) : "memory","edi", "eax", "ecx"
>>
>
> An input or output operand is implicitly clobbered, so it should be
^^^^^
I had expected gcc specialists to jump on that one: if you don't
explicitly tell gcc that an input is clobbered, it may reuse it later if
it needs the same value. So the clobbers are necessary...

Your statement about output operands is also incorrect, since clobbered
means that the register has an unknown value, which can not be used for
_anything_, while outputs are results from the statement and they'll
likely be used later (if none of the outputs is ever used and the asm
statement is not marked volatile, gcc will not even bother to emit it).

Regards,
Gabriel.

2002-04-26 12:32:27

by Richard B. Johnson

[permalink] [raw]
Subject: Re: Assembly question

On Fri, 26 Apr 2002, Gabriel Paubert wrote:

> Mark Zealey wrote:
>
> > On Thu, Apr 25, 2002 at 10:32:25AM +0200, Szekeres Istvan wrote:
> >
> >
> >> void p_memset_dword( void *d, int b, int l ) { __asm__ ("rep\n\t"
> "stosl\n\t"
> >>
>
> >> : : "D" (d), "a" (b), "c" (l) : "memory","edi", "eax", "ecx"
> >>
> >
> > An input or output operand is implicitly clobbered, so it should be
> ^^^^^
> I had expected gcc specialists to jump on that one: if you don't
> explicitly tell gcc that an input is clobbered, it may reuse it later if
> it needs the same value. So the clobbers are necessary...

: : "D" (d), "a" (b), "c" (l) : "memory","edi", "eax", "ecx"
... is correct. Registers EDI, EAX, and ECX are altered and memory
is changed. Nothing having to do with the 'input' is altered in
any meaningful sense. You alter an 'input' by doing something like:

pushl %ebx
..... code
popl %eax ; Input value of ebx is destroyed

Passed parameters are (usually) passed on the stack. They are
local and disappear when the call returns, i.e.,

pushl string ; 4 - byte address
call puts ; call function
addl $4, esp ; Level stack

GCC may optimize in-line and not bother to push a value that is
readily accessible to the in-line function. That value is not
clobbered in any way. The in-line function doesn't have to
'return' since it's in-line. Therefore the return address isn't
on the stack either. When setting up 'clobbers', you have
observe the registers that are used and declare whether or
not memory is altered. That's all. For instance, we see EAX as
a clobbered register. GCC may decide that it is a scratch-register
so it doesn't bother to save/restore its value. On the other hand,
if you didn't tell GCC that you destroyed EAX, it's a bug and a
hard-to-find bug because most of the time, the code will work.


Cheers,
Dick Johnson

Penguin : Linux version 2.4.18 on an i686 machine (797.90 BogoMips).

Windows-2000/Professional isn't.

2002-04-28 18:07:25

by Zack Weinberg

[permalink] [raw]
Subject: Re: Assembly question

Richard Johnson wrote:
> Gabriel Paubert wrote:
> > Mark Zealey wrote:
> > > Szekeres Istvan wrote:
> > >> void p_memset_dword( void *d, int b, int l ) {
> > >> __asm__ ("rep\n\t"
> > >> "stosl\n\t"
> > >> : : "D" (d), "a" (b), "c" (l)
> > >> : "memory","edi", "eax", "ecx");
> > >> }
> > >
> > > An input or output operand is implicitly clobbered, so it should be
> > ^^^^^
> > I had expected gcc specialists to jump on that one: if you don't
> > explicitly tell gcc that an input is clobbered, it may reuse it later if
> > it needs the same value. So the clobbers are necessary...
>
> : : "D" (d), "a" (b), "c" (l) : "memory","edi", "eax", "ecx"
> ... is correct. Registers EDI, EAX, and ECX are altered and memory
> is changed. Nothing having to do with the 'input' is altered in
> any meaningful sense.

As usual you are completely wrong. Clobbers can be used ONLY for
registers that do not already appear in either the input or output
list. In this case, it is the input registers that are modified. You
have to use notation that makes that explicit.

The correct way to write this asm statement with GCC 2.95 or later is

asm volatile
("rep; stosl"
: "+D" (d), "+c" (l) /* read and write edi, ecx */
: "a" (b)
: "memory");

With earlier versions you have to use a more verbose notation.

Some further notes:

rep stosl doesn't change %eax, so a plain input parameter can be used
for that argument.

GCC assumes that any asm with output parameters has no side effects.
The memory clobber doesn't count. Therefore, it will notice that d
and l are not used again and delete the whole asm statement, unless
you qualify it with 'volatile'.

There is no point in using __asm__ instead of asm, unless you are
writing code which has to compile with -pedantic on.

zw