错误:操作数超出范围(64不在0到31之间)

时间:2018-11-01 14:56:43

标签: c++ gcc inline-assembly powerpc altivec

我正在PowerPC上进行GCC内联汇编。该程序可以用-g2 -O3正常编译,但是不能用-g3 -O0编译。问题是,我需要在调试器下观察它,因此我需要没有优化的符号。

这是程序:

$ cat test.cxx
#include <altivec.h>
#undef vector

typedef __vector unsigned char uint8x16_p;

uint8x16_p VectorFastLoad8(const void* p)
{
  long offset = 0;
  uint8x16_p res;
  __asm(" lxvd2x  %x0, %1, %2    \n\t"
        : "=wa" (res)
        : "g" (p), "g" (offset/4), "Z" (*(const char (*)[16]) p));
  return res;
}

这是错误。 (该错误自PowerPC vec_xl_be replacement using inline assembly起就存在,但直到现在我一直可以忽略它。)

$ g++ -g3 -O0 -mcpu=power8 test.cxx -c
/home/test/tmp/ccWvBTN4.s: Assembler messages:
/home/test/tmp/ccWvBTN4.s:31: Error: operand out of range (64 is not between 0 and 31)
/home/test/tmp/ccWvBTN4.s:31: Error: syntax error; found `(', expected `,'
/home/test/tmp/ccWvBTN4.s:31: Error: junk at end of line: `(31),32(31)'

我相信这是* .s清单中的痛处:

#APP
 # 12 "test.cxx" 1
         lxvd2x  0, 64(31), 32(31)

使用lwz时也有类似的问题,但是我还没有发现讨论lxvd2x时遇到的问题。

问题出在哪里,我该如何解决?


这是*.s文件的头部:

$ head -n 40 test.s
        .file   "test.cxx"
        .abiversion 2
        .section        ".toc","aw"
        .align 3
        .section        ".text"
        .machine power8
.Ltext0:
        .align 2
        .globl _Z15VectorFastLoad8PKv
        .type   _Z15VectorFastLoad8PKv, @function
_Z15VectorFastLoad8PKv:
.LFB0:
        .file 1 "test.cxx"
        .loc 1 7 0
        .cfi_startproc
        std 31,-8(1)
        stdu 1,-96(1)
        .cfi_def_cfa_offset 96
        .cfi_offset 31, -8
        mr 31,1
        .cfi_def_cfa_register 31
        std 3,64(31)
.LBB2:
        .loc 1 8 0
        li 9,0
        std 9,32(31)
        .loc 1 12 0
        ld 9,64(31)
#APP
 # 12 "test.cxx" 1
         lxvd2x  0, 64(31), 32(31)

 # 0 "" 2
#NO_APP
        xxpermdi 0,0,0,2
        li 9,48
        stxvd2x 0,31,9
        .loc 1 13 0
        li 9,48
        lxvd2x 0,31,9

这是在-O3生成的代码:

$ g++ -g3 -O3 -mcpu=power8 test.cxx -save-temps -c
$ objdump --disassemble test.o | c++filt

test.o:     file format elf64-powerpcle

Disassembly of section .text:

0000000000000000 <VectorFastLoad8(void const*)>:
   0:   99 06 43 7c     lxvd2x  vs34,r3,r0
   4:   20 00 80 4e     blr
   8:   00 00 00 00     .long 0x0
   c:   00 09 00 00     .long 0x900
  10:   00 00 00 00     .long 0x0

1 个答案:

答案 0 :(得分:5)

问题在于生成的asm具有用于RA和RB的寄存器+偏移操作数,但是lxvd2x指令仅采用直接寄存器地址(即,没有偏移)。

您似乎在这里遇到了约束错误。查看内联汇编程序:

__asm(" lxvd2x  %x0, %1, %2    \n\t"
    : "=wa" (res)
    : "g" (p), "g" (offset/4), "Z" (*(const char (*)[16]) p));

首先,您有一个输出操作数和三个输入操作数(因此总共有四个),但是模板中仅使用了三个操作数。

我假设您的函数直接从*p读取,并且不会破坏任何内容,因此看起来这是一个未使用的操作数,用于指示潜在的内存访问(在下文中进行了更多介绍)。我们现在将其简化。删除它会给我们:

__asm(" lxvd2x  %x0, %1, %2    \n\t"
    : "=wa" (res)
    : "g" (p), "g" (offset/4));

编译后,我仍然得到用于RA和/或RB的偏移量:

 lxvd2x  0, 40(31), 9    

看一下"g"约束的文档,我们看到:

  

'g':

     

允许使用任何寄存器,内存或立即数整数操作数,     除了不是通用寄存器的寄存器。

但是,我们不能在这里提供一个内存操作数;只允许一个寄存器(无偏移)。如果将约束更改为"r"

 __asm(" lxvd2x  %x0, %1, %2    \n\t"
       : "=wa" (res)
       : "r" (p), "r" (offset/4));

对我来说,这编译为有效的lxvd2x调用:

 lxvd2x  0, 9, 10

-汇编程序愉快地接受。

现在,正如@PeterCordes所评论的那样,该示例不再表明它可以访问内存,因此我们应该恢复该内存输入依赖性,给出:

 __asm(" lxvd2x  %x0, %1, %2    \n\t"
    : "=wa" (res)
    : "r" (p), "r" (offset/4), "m" (*(const char (*)[16]) p));

实际上,我们所做的就是将约束从"g"更改为"r",迫使编译器使用非偏移量寄存器操作数。