Search code examples
gdbpowerpcaltivec

How to print a vector variable as its 128-bit vsx value?


I'm trying to track down an endianess issue when running on a PowerPC with Power8. Big endian is OK, little endian is having some troubles.

Below uint8x16_p8 is a typedef for __vector unsigned char. On a big endian machine I see:

1110     uint8x16_p8 r5 = (uint8x16_p8)VectorLoadKey(s_mask);
(gdb)
1112     for (unsigned int i=0; i<rounds-2; ++i)
(gdb) p r5
$1 = {0xd, 0xe, 0xf, 0xc, 0xd, 0xe, 0xf, 0xc, 0xd, 0xe, 0xf, 0xc, 0xd, 0xe,
  0xf, 0xc}

On a little endian machine I see:

1110     uint8x16_p8 r5 = (uint8x16_p8)VectorLoadKey(s_mask);
(gdb)
1112     for (unsigned int i=0; i<rounds-2; ++i)
(gdb) p r5
$1 = {0xc, 0xf, 0xe, 0xd, 0xc, 0xf, 0xe, 0xd, 0xc, 0xf, 0xe, 0xd, 0xc, 0xf,
  0xe, 0xd}

When gdb prints the value it is using the memory layout:

(gdb) ptype r5
type = unsigned char __attribute__ ((vector_size(16)))
(gdb)

I want to see the 128-bit integer value as it is loaded in a vsx register. The vsx register value is the important one, and it is always big endian. If there's a difference in the vsx value, then I know I need to permute a vector during the load from memory.

Also, GDB does not appear to support uint128_t:

(gdb) p *(uint128_t)r5
No symbol "uint128_t" in current context.

How do I have GDB print the vsx register value (and not the memory layout value)?


Another problem with GDB is, it cannot disassemble form "here", so I can't find where I am to print registers. For example, disass . does not "disassemble from here" (it results in a syntax error), and using $pc does not disassemble where I am (it looks like the start of the function):

(gdb) disass $pc
Dump of assembler code for function Rijndael_UncheckedSetKey_POWER8(...):
   0x00000000104b82c8 <+0>:     lis     r2,4213
   0x00000000104b82cc <+4>:     addi    r2,r2,-29952
   0x00000000104b82d0 <+8>:     mflr    r0
   0x00000000104b82d4 <+12>:    std     r0,16(r1)
   0x00000000104b82d8 <+16>:    std     r31,-8(r1)
   0x00000000104b82dc <+20>:    stdu    r1,-272(r1)
   0x00000000104b82e0 <+24>:    mr      r31,r1
   0x00000000104b82e4 <+28>:    std     r3,208(r31)
   0x00000000104b82e8 <+32>:    std     r4,216(r31)
   0x00000000104b82ec <+36>:    std     r5,224(r31)
   0x00000000104b82f0 <+40>:    std     r6,232(r31)
   0x00000000104b82f4 <+44>:    mr      r9,r7
   0x00000000104b82f8 <+48>:    stw     r9,240(r31)
   0x00000000104b82fc <+52>:    ld      r9,216(r31)
   0x00000000104b8300 <+56>:    cmpdi   cr7,r9,16
   0x00000000104b8304 <+60>:    bne     cr7,0x104b8548 <Rijndael_UncheckedSetKey_POWER8(...)+640>
   0x00000000104b8308 <+64>:    ld      r9,208(r31)
   0x00000000104b830c <+68>:    std     r9,32(r31)
---Type <return> to continue, or q <return> to quit---
...

Solution

  • Try __int128_t, which is supported by gcc & gdb:

    #include <stdint.h>
    
    int main(void)
    {
        __uint128_t s;
        vector int v = { 0, 1, 2, 3 };
    
        s = (__uint128_t)v;
    
        (void)s;
    
        return 0;
    }
    

    Then, you should be able to print your __uint128_t scalar as a normal value:

    [jk@p8 ~]$ gdb -quiet ./test 
    Reading symbols from ./test...done.
    (gdb) break test.c:12
    Breakpoint 1 at 0x100005ec: file test.c, line 12.
    (gdb) run
    Starting program: /home/jk/test 
    
    Breakpoint 1, main () at test.c:12
    12      return 0;
    (gdb) print s
    $1 = 0x00000003000000020000000100000000
    (gdb) 
    

    Or, if you know which register is used, just print the register value directly, using $<reg> notation:

    (gdb) p $vs0
    $5 = {uint128 = 0x00000003000000020000000100000000, v2_double = {
        2.1219957909652723e-314, 6.3659873738839482e-314}, v4_float = {0, 
        1.40129846e-45, 2.80259693e-45, 4.20389539e-45}, v4_int32 = {0, 1, 2, 3}, 
      v8_int16 = {0, 0, 1, 0, 2, 0, 3, 0}, v16_int8 = {0, 0, 0, 0, 1, 0, 0, 0, 2, 
        0, 0, 0, 3, 0, 0, 0}}
    

    However, keep in mind that the cast to a scalar may affect the register value; check the disassembly if you're unsure.