Error: operand out of range (64 is not between 0 and 31)

Error: operand out of range (64 is not between 0 and 31)

我在 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

问题是生成的 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 的偏移量 and/or RB:

 lxvd2x  0, 40(31), 9    

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

'g':

Any register, memory or immediate integer operand is allowed, except for registers that are not general registers.

但是,我们不能在这里提供内存操作数;只允许一个寄存器(没有偏移量)。如果我们将约束更改为 "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",强制编译器使用非偏移寄存器操作数。