问题
I'm catching an assembler error when using inline assembly and a local label. The compiler is GCC, and the machine is PowerPC running AIX. The code reads the timestamp (it is roughly equivalent to rdtsc
):
static unsigned long long cpucycles( void )
{
unsigned long long int result=0;
unsigned long int upper, lower,tmp;
__asm__ __volatile__ (
"0: \n\t"
"mftbu %0 \n\t"
"mftb %1 \n\t"
"mftbu %2 \n\t"
"cmpw %2,%0 \n\t"
"bne- 0b \n\t"
: "=r"(upper),"=r"(lower),"=r"(tmp)
: :
);
result = upper;
result = result<<32;
result = result|lower;
return(result);
}
When the code is assembled it results in:
gcc -O3 -Wall -Wextra -mcpu=power8 -maltivec test.c -o test.exe
Assembler:
test.s: line 103: 1252-142 Syntax error.
Compiling with --save-temps
and examining test.s
:
$ cat -n test.s
...
101 L..5:
102 # 58 "test.c" 1
103 0:
104 mftbu 10
105 mftb 9
106 mftbu 8
107 cmpw 8,10
108 bne 0b
109
It looks like the assembler is having trouble with the local label. Based on IBM's Use of inline assembly and local labels I believe the label and branch are being used correctly:
Only some local labels are legal in inline assembly. You might see labels, such as 0 and 1 in Code C. They are the branching target of instruction
bne- 0b\n\t
andbne 1f\n\t
. (The f suffix for the label means the label behind the branch instruction, and b is for the one ahead)
IBM's error message for 1252-142 is not very helpful:
Cause
If an error occurred in the assembly processing and the error is not defined in the message catalog, this generic error message is used. This message covers both pseudo-ops and instructions. Therefore, a usage statement would be useless.
Action
Determine intent and source line construction, then consult the specific instruction article to correct the source line.
What is the problem and how do I fix it?
Based on @Eric's suggestions in the comments:
__asm__ __volatile__ (
"\n0: \n\t"
"mftbu %0 \n\t"
"mftb %1 \n\t"
"mftbu %2 \n\t"
"cmpw %2,%0 \n\t"
"bne- 0b \n\t"
: "=r"(upper),"=r"(lower),"=r"(tmp)
);
Results in the problem moving one line down:
gcc -O3 -Wall -Wextra -mcpu=power8 -maltivec test.c -o test.exe
Assembler:
test.s: line 104: 1252-142 Syntax error.
But it looks like the label is in column 0:
103
104 0:
105 mftbu 10
106 mftb 9
107 mftbu 8
108 cmpw 8,10
109 bne- 0b
回答1:
gcc doesn't emit machine-code directly; it feeds its asm output to the system assembler. You could configure gcc to use a different assembler, like GAS, but apparently the default setup on the machine you're using has GCC using AIX's assembler.
Apparently AIX's assembler doesn't support numeric labels, unlike the GNU assembler. Probably that article you linked is assume Linux (accidentally or on purpose) when it mentions using labels like 0
.
The easiest workaround is probably having GCC auto-number the label instead of using local labels, so the same asm block can be inlined / unrolled multiple times in the same compilation unit without symbol-name conflicts. %= expands to a unique number in every instance.
IDK if an L..
makes it a file-local label (which won't clutter up debug info or the symbol table). On Linux/ELF/x86, .L
is the normal prefix, but you have a compiler-generated L..
label.
__asm__ __volatile__ (
"L..again%=: \n\t"
"mftbu %0 \n\t"
"mftb %1 \n\t"
"mftbu %2 \n\t"
"cmpw %2,%0 \n\t"
"bne- L..again%="
: "=r"(upper),"=r"(lower),"=r"(tmp)
: :
);
Or for this specific asm use-case, there might be a built-in function that reads the time-stamp registers which would compile to asm like this.
回答2:
In addition to @Peter's answer, I just found this answer while working on How to have GCC combine “move r10, r3; store r10” into a “store r3”?. The other question hit the problem on AIX, too.
Here's the code from the other question that caused 1252-142 on AIX:
uint32_t val;
__asm__ __volatile__ (
"1: \n" // retry label
#if __BIG_ENDIAN__
".byte 0x7c, 0x60, 0x05, 0xe6 \n" // r3 = darn 3, 0
#else
".byte 0xe6, 0x05, 0x60, 0x7c \n" // r3 = darn 3, 0
#else
"cmpwi 3,-1 \n" // r3 == -1?
"beq 1b \n" // again on failure
"mr %0,3 \n" // val = r3
: "=r" (val) : : "r3", "cc"
);
The solution is, don't use labels. Just use displacements:
uint32_t val;
__asm__ __volatile__ (
// "1: \n" // retry label
#if __BIG_ENDIAN__
".byte 0x7c, 0x60, 0x05, 0xe6 \n" // r3 = darn 3, 0
#else
".byte 0xe6, 0x05, 0x60, 0x7c \n" // r3 = darn 3, 0
#else
"cmpwi 3,-1 \n" // r3 == -1?
// "beq 1b \n" // again on failure
"beq .-8 \n" // again on failure
"mr %0,3 \n" // val = r3
: "=r" (val) : : "r3", "cc"
);
In the code above, I needed to jump back 2 instructions to re-execute darn 3, 0
. Each instruction is 4-bytes, so the jump was -8
. However, the jump target needs to be relocatable so the expression .-8
was used. The dot means "here".
And it works on both AIX and Linux.
来源:https://stackoverflow.com/questions/51869790/1252-142-syntax-error-from-aix-assembler-due-to-local-label