Search code examples
caixinline-assemblypowerpc

1252-142 Syntax error from AIX assembler due to local label


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 and bne 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

Solution

  • 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.