6

我在 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以来,该错误一直存在,但直到现在我都可以忽略它)。

$ 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
4

1 回答 1

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",强制编译器使用非偏移寄存器操作数。

于 2018-11-02T02:35:07.210 回答