Understanding a function

P

pembed2012

Hi

Can any-1 explain me what it do this function?

Thankyou very much


static inline int __try__(struct sl * sl)
{
unsigned int rv;

__asm__ __volatile__(
"1: lwarx %0,0,%1\n\
cmpwi 0,%0,0\n\
li %0,0\n\
bne- 2f\n\
li %0,1\n\
stwcx. %0,0,%1\n\
bne- 1b\n\
isync\n\
2:" : "=&r"(rv)
: "r"(sl)
: "cr0", "memory");

return rv==1 ?0:EBUSY;
}
 
J

jacob navia

Le 05/04/12 22:46, pembed2012 a écrit :
Hi

Can any-1 explain me what it do this function?

Thankyou very much


static inline int __try__(struct sl * sl)
{
unsigned int rv;

__asm__ __volatile__(
"1: lwarx %0,0,%1\n\
cmpwi 0,%0,0\n\
li %0,0\n\
bne- 2f\n\
li %0,1\n\
stwcx. %0,0,%1\n\
bne- 1b\n\
isync\n\
2:" : "=&r"(rv)
: "r"(sl)
: "cr0", "memory");

return rv==1 ?0:EBUSY;
}

This horror is called "gnu inline assembler".
I have always refused to learn it.

Thanks god this is off topic here since just looking at it
makes me sick.
 
B

BGB

Le 05/04/12 22:46, pembed2012 a écrit :

This horror is called "gnu inline assembler".
I have always refused to learn it.

Thanks god this is off topic here since just looking at it
makes me sick.

nevermind that it is also not for x86, looks like it is for PowerPC or
similar...


but, yes, I much preferred the MS/Borland/... style of inline-ASM, as at
least it was generally far less horrid looking...
 
A

Alan Curry

[...]


It's PPC, and it's some kind of atomic read/update of a shared
variable.

It's a troll designed to start an argument over where the question should
have been posted.

There is a comp.lang.asm.x86, but no comp.lang.asm.ppc and instead of
comp.lang.asm there's alt.lang.asm which is full of... x86 stuff. Or maybe
the asm part is completely understood and the C-to-asm binding part is under
question so it should be gnu.gcc.help or something. We've been left to guess.

In any case, it's been presented without any context. It probably comes from
someone's pthread library or kernel, and if that original context was given,
it would be easy to point to the corresponding documentation, so omitting it
really creates an information gap to help the budding flamewar grow.

And it's a trylock operation on a spinlock (with the names stripped down to
make it harder to read, possibly). It returns 0 if it was able to atomically
read a 0 and then write a 1 to the integer at the beginning of the struct sl.
 
P

pembed2012

It's PPC, and it's some kind of atomic read/update of a shared variable.

OK thanks, what I really want is exact C code for that function (not
using asm) ----- or second best will be other asm that works. It will not
compile on my current platform (64bit Linux/gcc)

Thankyou
 
K

Kaz Kylheku

Le 05/04/12 22:46, pembed2012 a écrit :

This horror is called "gnu inline assembler".
I have always refused to learn it.

You're being pretty silly because it is powerful and useful, allowing inline
assembly to dove-tail nicely with the code generated by the surrounding C.

GCC can do things like pick registers for you to use and prepare the operands
in the registers, and take care to reload cached operands.

You can advise the compiler that your operation clobbers certain registers or
memory, or other CPU state. That's very good, and we can see it going on in
the above example.

%0 and %1 are virtual registers. 0 is the output operand corresponding to rv,
%and %1 is an input operand corresponding to sl. GCC will choose those
registers and generate the code to prepare their values before your assembly
sequence. Terrific! Without this ability you would have to hard-code the
registers. And since you don't know if they are being used by the compiler or
not, you would have to save and restore them which is a waste of cycles
and cache traffic.

The code also advises GCC that the instruction sequence has an effect on
cr0 and on memory (the latter being needed in situations when the compiler
might have that memory operand cached in a register). The memory
advice is needed here even though the instruction sequence does *not* in fact
have any effect on some unmentioned memory operand: this is because it
is a locking primitive which semantically needs a memory barrier, and this
memory-clobber has the effect of "spooking" the compiler into providing the
necessary software memory barrier (register spill, reload) to go with the
hardware one (isync).

A few years ago I was working on MIPS and needed some
atomic instructions, making profitable use of this GCC inline syntax.
Actually, code very similar to what we are dealing with, but on another
architecture.

These instructions sequences were to be used in proprietary code so I didn't
want to just take GNU-licensed anything. But I had a look.

Other people were defining rigid code templates for the operations, like
acquiring a spinlock, or compare-and-swap, etc. (Very much like the above:
the whole thing is "canned": the load, comparison, store, loop and barrier).

Instead, I wrapped at a lower level: I created the inline primitives to just do
the "load_linked" and "store_conditional" MIPS operations, with a C interface.

Then I wrote the algorithms for things like acquiring a lock or
compare-swap in C syntax instead of assembly, as easy-to-read little
inline functions:

uint32_t x;
/* loop while lock is busy, or we are not successfully able
to flip it to busy with a store conditional */
do {
x = load_linked(&addr);
} while (x || store_conditional(&addr, 1))
read_barrier(); /* make sure we don't read stale cached data inside lock */

Guess what: GCC was able to optimize and shave some instructions out of stuff
like that, compared to the canned machine language sequences used in other
projects.

This is what I mean by dovetailing: you can do inline assembly on a fine
granularity and let GCC take it into the mix and optimize with it.
 
J

Jens Gustedt

Am 04/07/2012 08:13 PM, schrieb Robert Wessel:
Anyway, there may not be any equivalent C at all on your target
platform (in fact, that's somewhat likely). There may be extensions,
usually in the form of library functions that do the atomic
operations, but those are OS specific. Also, I agree with Alan that
this is probably conditional entry code to a spinlock, and the
structure of spinlocks tends to change drastically from
implementation-to-implementation.

But, on x86 you ought to be able to do the equivalent with roughly:

mov eax,0 ;assuming a 32 bit lock word
mov ecx,1
lock cmpxchg [sl],ecx
;eax now contain zero if if cmpxchg worked,
;or the old value at [sl] if it didn't

The above is Intel syntax, and assumes a 32 bit lock word (use rax for
a 64 bit lock word). Translating it to GAS and hacking it into a GCC
__asm__ block is left as an exercise for the reader. And as I
mentioned, there's no guarantee that spinlocks are implemented in a
sufficiently compatible way on *this* platform for the above code to
be correct.

Alternatively, GCC on Linux should be supporting the
__sync_val_compare_and_swap() atomic builtin, which could be used to
do the same thing.

atomic operations come with the new C11 standard and gcc is moving
quickly towards implementations of these. The most appropriate
gcc builtins to implement a spinlock are probably

__sync_lock_test_and_set and __sync_lock_release

that implement just the minimal operations and minimal memory
consistency that is needed for such locks.

Since gcc 4.7 there is a new family of builtins with prefix __atomic_
that implement the new atomic functionalities more closely.

The __sync builtins also come with most other compilers/platforms that
try to be gcc compatible. For other compilers, implementing just the
atomic primitives is usually not a big deal and you should find code
for the processor of your liking easily on the net.

Jens
 
B

BartC

Kaz Kylheku said:
You're being pretty silly because it is powerful and useful, allowing
inline
assembly to dove-tail nicely with the code generated by the surrounding C.

GCC can do things like pick registers for you to use and prepare the
operands
in the registers, and take care to reload cached operands.

This is all well and good (although you are then starting to work with a
somewhat different language than pure assembly). But why didn't they make
the extra effort to make their inline asm easy on the eye, and comfortable
to work with? Why do those last ":" lines need to be so cryptic; would it
kill someone to have to type a keyword?
 
J

jacob navia

Le 07/04/12 21:08, Kaz Kylheku a écrit :
You're being pretty silly because it is powerful and useful, allowing inline
assembly to dove-tail nicely with the code generated by the surrounding C.

GCC can do things like pick registers for you to use and prepare the operands
in the registers, and take care to reload cached operands.

You can advise the compiler that your operation clobbers certain registers or
memory, or other CPU state. That's very good, and we can see it going on in
the above example.

Then, why not like this?

__asm__ __volatile__ (register arg1, register arg2)
{
loop:
lwarx arg1,0,arg2
cmpwi 0,arg1,0
li arg1,0
bne- endLoop
li arg1,1
stwcx. arg1,0,arg2
bne loop
isync
endLoop:
declarations
output r:ANY write_only early_clobber subset:r(rv)
subset:r(sl)
clobbers:cr0
clobbers:memory

}

This would have needed minimal modifications to the existing parser:
instead of parsing "=" it would parse ANY... big deal.

But no, they decided to make it as obscure as possible and they succeeded.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top