Difference between revisions of "User:Georgp24/testpage"
Line 1: | Line 1: | ||
− | Inlining assembler allows to insert assembler instructions into C or C++ code for GCC. GCC provides "asm" statement for that. For a start this example will add a NOP (no operation) to the C code: | + | Inlining assembler allows to insert assembler instructions into C or C++ code for GCC. GCC provides the "asm" statement for that. This tutorial describes how to use PowerPC instructions as inline assembler code with GCC. |
+ | |||
+ | For a start this example will add a NOP (no operation) to the C code: | ||
asm volatile ("ori 0,0,0"); /* NOP */ | asm volatile ("ori 0,0,0"); /* NOP */ | ||
Line 6: | Line 8: | ||
If "asm" or "volatile" are already used by your program as names you can use __asm__ und __volatile__ instead. | If "asm" or "volatile" are already used by your program as names you can use __asm__ und __volatile__ instead. | ||
− | Some compilers do save all registers before executing inline assembler statements and restore them afterwords. GCC does not do that so. Modifying a register and not | + | Some compilers do save all registers before executing inline assembler statements and restore them afterwords. GCC does not do that so. Modifying a register and not putting that into the "clobber list" described below may crash your program. |
To add several assembler statements at once you can use: | To add several assembler statements at once you can use: | ||
Line 16: | Line 18: | ||
); | ); | ||
− | all statements in this example should do nothing. The \n\t parameters are added to separate the statements and improve the readability of the assembler source | + | all statements in this example should do nothing. The \n\t parameters are added to separate the statements and improve the readability of the assembler source GCC will generate from this should you use the -save-temps switch when compiling the code. You can use also use the -mregnames command line switch with GCC to compile the code. GCC will then output register names in the assembly language output. |
Line 23: | Line 25: | ||
'''asm(code : output operand list : input operand list : clobber list);''' | '''asm(code : output operand list : input operand list : clobber list);''' | ||
− | If no | + | If e.g. no clobber list needs to be specified, the last colons may be omitted. The first example above was short for: |
asm volatile ("ori 0,0,0" :::); /* NOP */ | asm volatile ("ori 0,0,0" :::); /* NOP */ | ||
Line 37: | Line 39: | ||
); | ); | ||
− | Following the code the output operand is specified. In square brackets the symbolic name is specified that will be used within the assembler code to access this variable. Here the same name as in the C code is used to improve the readablility. Following that in quotation marks is the so-called "constraint" which | + | Following the code, which just consists of the MR instruction, the output operand is specified. In square brackets the symbolic name is specified that will be used within the assembler code to access this variable. Here the same name as in the C code is used to improve the readablility. Following that in quotation marks is the so-called "constraint" which specifies the type of the operand and will be discussed below. Then enclosed in parentheses there is the C expression determining the variable passed to the assembler code. Output operand must be long values. |
− | In the next line the input operand is specified equivalent to the output operand. If there are more than one input operand, these are separated by commas within this part. | + | In the next line the input operand is specified in equivalent syntax to the output operand. If there are more than one input operand, these are separated by commas within this part. These operands can be any C expression, a variable or a member of a structure. |
− | Finally, in the clobber list the registers are specified which will be modified or overwritten by the assembler statements. This way GCC can make sure that these modifications do not corrupt the rest of the C code. Otherwise the program will operate wrong or crash! | + | Finally, in the clobber list the registers are specified which will be modified or overwritten by the assembler statements. This way GCC can make sure that these modifications do not corrupt the rest of the C code. Otherwise the program will operate wrong or crash! If you use compare instructions you have to include "cc" here for the condition register. |
In the code section the C expressions are referenced by a percent sign followed by the symbolic name in square brackets. Multiple statements are separated by a semicolon or \n. Output operands must be long values. | In the code section the C expressions are referenced by a percent sign followed by the symbolic name in square brackets. Multiple statements are separated by a semicolon or \n. Output operands must be long values. | ||
Line 58: | Line 60: | ||
); | ); | ||
− | In older versions of GCC there were no symbolic names in square brackets available. The output and input | + | In older versions of GCC there were no symbolic names in square brackets available. The output and input operands were referenced by numbers. The sample above would have looked like that then: |
long in_value; | long in_value; | ||
Line 65: | Line 67: | ||
"lwz 20,%1\n\t" /* move in_value to GPR20 */ | "lwz 20,%1\n\t" /* move in_value to GPR20 */ | ||
"stw 20,%0" /* move GPR20 to out_value */<br> | "stw 20,%0" /* move GPR20 to out_value */<br> | ||
− | :"=m" (out_value) /* output - %0 */ | + | :"=m" (out_value) /* output - %0 -> operand zero */ |
− | :"m" (in_value) /* input - %1 */ | + | :"m" (in_value) /* input - %1 -> operand one */ |
:"20" /* GPR20 will be clobbered */ | :"20" /* GPR20 will be clobbered */ | ||
); | ); | ||
Line 83: | Line 85: | ||
"&" marks an operand as "earlyclobber". | "&" marks an operand as "earlyclobber". | ||
− | + | A "=" or "+" modifier usually has to be added to the constraint of the output operand. | |
+ | The "earlyclobber" modifier can be added to output operands to make sure GCC uses different registers for input and output operands (e.g. "=&r"). GCC assumes that the output operands are not used before the code is done with all input operands and reuses the input registers for output operands. If there are a lot of statements an output operand may be used before the code is finished with the input operands. | ||
− | In | + | |
+ | In the next example out_value is set to four. The constant "const_value" will be passed as an immediate operand: | ||
#define const_value 4 | #define const_value 4 | ||
Line 96: | Line 100: | ||
); | ); | ||
− | If you want to use the same variable for input and output you have to add the "+" modifer to the output operand. This is done in this example: | + | If you want to use the same variable for input and output you have to add the "+" modifer to the output operand. This is done in this example which takes out_value for input and doubles that: |
long out_value;<br> | long out_value;<br> | ||
Line 112: | Line 116: | ||
); | ); | ||
− | The constraint "0" (zero) for operand 1 (input) specifies that this operand must occupy the same location as operand 0 (output). A number in a constraint may only be used for an input operand and this has to refer to an output operand. | + | The constraint "0" (zero) for operand 1 (input) specifies that this operand must occupy the same location as operand 0 (output). A number in a constraint may only be used for an input operand and this has to refer to an output operand. Observe that this operand is used as %1 in the assembler instruction. |
Finally here is a very simple example for a subroutine including an "asm" statement. It just tests if the input value (which is equal to the output value) is four. If this is the case the value will be changed to eight. Since the "cmpwi" instruction will modify the condition register we have to add that to the clobber list: | Finally here is a very simple example for a subroutine including an "asm" statement. It just tests if the input value (which is equal to the output value) is four. If this is the case the value will be changed to eight. Since the "cmpwi" instruction will modify the condition register we have to add that to the clobber list: | ||
Line 135: | Line 139: | ||
out_value = test_for_4(out_value); | out_value = test_for_4(out_value); | ||
+ | |||
+ | You may branch within one "asm" statement only, you cannot jump to a different "asm" statement within the C code. | ||
<br><br> | <br><br> | ||
---- | ---- |
Revision as of 18:19, 11 August 2009
Inlining assembler allows to insert assembler instructions into C or C++ code for GCC. GCC provides the "asm" statement for that. This tutorial describes how to use PowerPC instructions as inline assembler code with GCC.
For a start this example will add a NOP (no operation) to the C code:
asm volatile ("ori 0,0,0"); /* NOP */
The assembler code is in quotation marks. The modifier "volatile" will stop GCC from optimising the assembler code. This could involve rearranging the order of the statements and this may not what is intended. If "asm" or "volatile" are already used by your program as names you can use __asm__ und __volatile__ instead.
Some compilers do save all registers before executing inline assembler statements and restore them afterwords. GCC does not do that so. Modifying a register and not putting that into the "clobber list" described below may crash your program.
To add several assembler statements at once you can use:
asm volatile ( "ori 0,0,0\n\t" "mr 0,0\n\t" "rlwinm 0,0,0,0,31" );
all statements in this example should do nothing. The \n\t parameters are added to separate the statements and improve the readability of the assembler source GCC will generate from this should you use the -save-temps switch when compiling the code. You can use also use the -mregnames command line switch with GCC to compile the code. GCC will then output register names in the assembly language output.
To access variables within the C code the asm statement includes additional parts which are separated by colons. This is the general form of this statement:
asm(code : output operand list : input operand list : clobber list);
If e.g. no clobber list needs to be specified, the last colons may be omitted. The first example above was short for:
asm volatile ("ori 0,0,0" :::); /* NOP */
In the following example the long integer value in_value is moved into the long integer value out_value. Using the "r" constraint these variables are passed to the assembler code as registers:
long in_value; long out_value;
asm ( "mr %[in_value],%[out_value]" :[out_value]"=r" (out_value) /* output */ :[in_value]"r" (in_value) /* input */ :"20" /* GPR20 will be clobbered */ );
Following the code, which just consists of the MR instruction, the output operand is specified. In square brackets the symbolic name is specified that will be used within the assembler code to access this variable. Here the same name as in the C code is used to improve the readablility. Following that in quotation marks is the so-called "constraint" which specifies the type of the operand and will be discussed below. Then enclosed in parentheses there is the C expression determining the variable passed to the assembler code. Output operand must be long values.
In the next line the input operand is specified in equivalent syntax to the output operand. If there are more than one input operand, these are separated by commas within this part. These operands can be any C expression, a variable or a member of a structure.
Finally, in the clobber list the registers are specified which will be modified or overwritten by the assembler statements. This way GCC can make sure that these modifications do not corrupt the rest of the C code. Otherwise the program will operate wrong or crash! If you use compare instructions you have to include "cc" here for the condition register.
In the code section the C expressions are referenced by a percent sign followed by the symbolic name in square brackets. Multiple statements are separated by a semicolon or \n. Output operands must be long values.
In the following example the long integer values are passed to the assembler code as memory addresses by using the "m" constraint .
long in_value; long out_value;
asm ( "lwz 20,%[in_value];" /* move in_value to GPR20 */ "stw 20,%[out_value]" /* move GPR20 to out_value */
:[out_value]"=m" (out_value) /* output */ :[in_value]"m" (in_value) /* input */ :"20" /* GPR20 will be clobbered */ );
In older versions of GCC there were no symbolic names in square brackets available. The output and input operands were referenced by numbers. The sample above would have looked like that then:
long in_value; long out_value;
asm ( "lwz 20,%1\n\t" /* move in_value to GPR20 */ "stw 20,%0" /* move GPR20 to out_value */
:"=m" (out_value) /* output - %0 -> operand zero */ :"m" (in_value) /* input - %1 -> operand one */ :"20" /* GPR20 will be clobbered */ );
You will find this type of coding in source files and it is still supported by GCC. Here the assembler code lines are separated by \n\t. \t adds a tab on the new line in the g
The most common constraints are:
r = general register m = memory address i = symbolic constant to be used as an immediate value
There are more constraints documented in the GCC manual.
You can add modifiers to these constraints. If no modifier is added to the constraint, it means a read-only operand. Otherwise add:
"=" for a write-only operand "+" for a read-write operand. "&" marks an operand as "earlyclobber".
A "=" or "+" modifier usually has to be added to the constraint of the output operand.
The "earlyclobber" modifier can be added to output operands to make sure GCC uses different registers for input and output operands (e.g. "=&r"). GCC assumes that the output operands are not used before the code is done with all input operands and reuses the input registers for output operands. If there are a lot of statements an output operand may be used before the code is finished with the input operands.
In the next example out_value is set to four. The constant "const_value" will be passed as an immediate operand:
#define const_value 4 long out_value;
asm ( "li %[out_value],%[const_value]" :[out_value]"=r" (out_value) /* output */ :[const_value]"i" (const_value) /* input */ );
If you want to use the same variable for input and output you have to add the "+" modifer to the output operand. This is done in this example which takes out_value for input and doubles that:
long out_value;
asm ( "add %[out_value],%[out_value],%[out_value]\n\t" :[out_value]"+r" (out_value) /* output and input */ );
With older versions of GCC this example would be written as:
long out_value;
asm ( "add %[out_value],%[out_value],%1\n\t" :[out_value]"=r" (out_value) /* output */ :"0" (out_value) /* input */ );
The constraint "0" (zero) for operand 1 (input) specifies that this operand must occupy the same location as operand 0 (output). A number in a constraint may only be used for an input operand and this has to refer to an output operand. Observe that this operand is used as %1 in the assembler instruction.
Finally here is a very simple example for a subroutine including an "asm" statement. It just tests if the input value (which is equal to the output value) is four. If this is the case the value will be changed to eight. Since the "cmpwi" instruction will modify the condition register we have to add that to the clobber list:
int test_for_4(out_value) {
asm ( "cmpwi %[out_value],4\n\t" /* Compare value in out_value with 4 */ "bne else_label\n\t" /*if not 4 goto else_label */ "li %[out_value],8\n\t" /* if 4 then make it 8 */ "b endif_label\n\t" /* jmp over else part */
"else_label:\n\t" "ori 0,0,0\n\t" /* nop */
"endif_label:\n\t"
:[out_value]"+r" (out_value) /* output and input */ : /* no separate input operand */ :"cc" /* condition register will be clobbered */ );
return out_value; }
This function is then called e.g. with:
out_value = test_for_4(out_value);
You may branch within one "asm" statement only, you cannot jump to a different "asm" statement within the C code.
- Links