Examples of inline assembly statements
volatile keyword. #include <stdio.h>
inline bool acquireLock(int *lock){
bool returnvalue = false;
int lockval;
asm volatile(
/*--------a fence here-----*/
" 0: lwarx %0,0,%2 \n" // Loads the word and reserves
// a memory location for the subsequent
// stwcx. instruction.
" cmpwi %0,0 \n" // Compares the lock value to 0.
" bne- 1f \n" // If it is 0, you can acquire the
// lock. Otherwise, you did not get the
// lock and must try again later.
" ori %0,%0,1 \n" // Sets the lock to 1.
" stwcx. %0,0,%2 \n" // Tries to conditionally store 1
// into the lock word to acquire
// the lock.
" bne- 0b \n" // Reservation was lost. Try again.
" isync \n" // Lock acquired. The isync instruction
// implements an import barrier to
// ensure that the instructions that
// access the shared region guarded by
// this lock are executed only after
// they acquire the lock.
" ori %1,%1,1 \n" // Sets the return value for the
// function acquireLock to true.
" 1: \n" // Did not get the lock.
// Will return false.
/*------a fence here------*/
: "+r" (lockval),
"+r" (returnvalue)
: "r" (lock) // "lock" is the address of the lock in
// memory.
: "cr0" // cr0 is clobbered by cmpwi and stwcx.
);
return returnvalue;
}
int main()
{
int myLock;
if(acquireLock(&myLock)){
printf("got it!\n");
}else{
printf("someone else got it\n");
}
return 0;
}In this example, %0 refers
to the first operand "+r"(lockval), %1 refers
to the second operand "+r"(returnvalue), and %2 refers
to the third operand "r"(lock).
The assembly statement uses a lock to control access to the shared storage; no instruction can access the shared storage before acquiring the lock.
The volatile keyword implies fences around
the assembly instruction group, so that no assembly instructions can
be moved out of or around the assembly block.
Without the volatile keyword,
the compiler can move the instructions around for optimization. This
might cause some instructions to access the shared storage without
acquiring the lock.
It is unnecessary to use the memory clobber
in this assembly statement, because the instructions do not modify
memory in an unexpected way. If you use the memory clobber,
the program is still functionally correct. However, the memory clobber
results in many unnecessary reloads, imposing a performance penalty.
int a ;
int b = 1, c = 2, d = 3 ;
__asm(" addc %[result], %[first], %[second]"
: [result] "=r" (a)
: [first] "r" (b),
[second] "r" (d)
); In this example, %[result] refers
to the output operand variable a, %[first] refers to
the input operand variable b, and %[second] refers
to the input operand variable d.
asm (" add. %0,%1,%2 \n"
: "=r" (c)
: "r" (a),
"r" (b)
: "cr0"
);In this example, apart from the registers listed
in the input and output of the assembly statement, the add. instruction
also affects the condition register field 0. Therefore, you must inform
the compiler about this by adding cr0 to the clobbers.memory clobber. asm volatile (" dcbz 0, %0 \n"
: "=r"(b)
:
: "memory"
);In this example, the instruction dcbz clears
a cache block, and might have changed the variables in the memory
location. There is no way for the compiler to know which variables
have been changed. Therefore, the compiler assumes that all data might
be aliased with the memory changed by that instruction. As
a result, everything that is needed must be reloaded from memory after
the completion of the assembly statement. The memory clobber
ensures program correctness at the expense of program performance,
because the compiler might reload data that had nothing to do with
the assembly statement.
= modifier and the r constraint.int a ;
int b = 100 ;
int c = 200 ;
asm(" add %0, %1, %2"
: "=r" (a)
: "r" (b),
"r" (c)
);The add instruction
adds the contents of two general purpose registers. The %0, %1,
and %2 operands are substituted by the C expressions
in the output/input operand fields.
= modifier to indicate that
a modifiable operand is required; it uses the r constraint
to indicate that a general purpose register is required. Likewise,
the r constraint in the input operands indicates that general
purpose registers are required. Within these restrictions, the compiler
is free to choose any registers to substitute for %0, %1, and %2.r0 for
the second operand, the add instruction uses the
literal value 0 and yields an unexpected result.
Thus, to prevent the compiler from choosing r0 for
the second operand, you can use the b constraint
to denote the second operand.+ modifier
and the K constraint. asm (" addi %0,%0,%2"
: "+r" (a)
: "r" (a),
"K" (15)
);This assembly statement adds operand %0 and
operand %2,
and writes the result to operand %0. The output operand
uses the + modifier to indicate that operand %0 can
be read and written by the instruction. The K constraint
indicates that the value loaded to operand %2 must be an unsigned
16-bit constant value.
% modifier
and the f constraint.asm(" fadd %0, %1, %2"
: "=f" (c)
: "%f" (a),
"f" (b)
); This assembly statement adds operands a and b,
and writes the result to operand c. The % modifier
indicates that operands a and b can
be switched if the compiler can generate better code in doing so.
Each operand has the f constraint, which indicates
that a floating point register is required.b constraint.char res[8]={'a','b','c','d','e','f','g','h'};
char a='y';
int index=7;
asm (" stbx %0,%1,%2 \n" \
: \
: "r" (a),
"b" (index),
"r" (res)
); In this example, the b constraint
instructs the compiler to choose a general register other than r0 for
the input operand %1. The result string of this program
is abcdefgy. However, if you use the r constraint
and the compiler chooses r0 for %1,
this instruction produces an incorrect result string ybcdefgh.
For instructions that treat the designation of r0 specially,
it is therefore important to denote the input operands with the b constraint.m constraint. asm (" stb %1,%0 \n" \
: "=m" (res) \
: "r" (a)
);In this example, the syntax of the instruction stb is stb RS,D(RA), where D is
a displacement and R is a register. D+RA forms
an effective address, which is calculated from D(RA).
By using constraint m, you do not need to manually construct
effective addresses by specifying the register and displacement separately.
You can use a single constraint m or o to
refer to the two operands in the instruction, regardless of what the
correct offset should be and whether it is an offset off the stack
or off the TOC (Table of Contents). This allows the compiler to choose
the right register (r1 for an automatic variable,
for instance) and apply the right displacement automatically.