代码之家  ›  专栏  ›  技术社区  ›  jww avp

如何在使用内置变量后强制转换为无符号向量类型_*

  •  1
  • jww avp  · 技术社区  · 6 年前

    我正在评估 MIPS SIMD Architecture (MSA) 使用编程 Codescape GCC Toolchain . 没有太多关于MSA和内置设备的信息。(据我所知,只有两个MSA CPU,即p5600和warrior i6400,几年前就开始使用了)。

    我的测试程序如下。

    #include <msa.h>
    #include <stdint.h>
    
    #define ALIGN16 __attribute__((aligned(16)))
    
    int main(int argc, char* argv[])
    {
        ALIGN16 uint32_t a[] = {64, 128, 256, 512};
        ALIGN16 uint32_t b[] = {1024, 2048, 4096, 8192};
        ALIGN16 uint32_t c[4];
    
        v4u32 va = __builtin_msa_ld_w (a, 0);
        v4u32 vb = __builtin_msa_ld_w (b, 0);
    
        v4u32 vc = __builtin_msa_adds_u_w (va, vb);
        __builtin_msa_st_w (vc, c, 0);
    
        return 0;
    }
    

    编译程序会导致以下错误。问题是, vector loads return a signed vector 但我的向量是无符号的。我对向量存储也有类似的问题。

    // The 4 vector loads provided through builtins
    v16i8 __builtin_msa_ld_b (void *, imm_n512_511);    // byte
    v8i16 __builtin_msa_ld_h (void *, imm_n1024_1022);  // half word
    v4i32 __builtin_msa_ld_w (void *, imm_n2048_2044);  // word
    v2i64 __builtin_msa_ld_d (void *, imm_n4096_4088);  // double word
    

    (The imm_n512_511 在GCC手册中讨论了朋友。 6.59.16 MIPS SIMD Architecture (MSA) Support )

    我读过MIPS论文(?)在 MIPS SIMD Architecture 但是它没有讨论如何在积分向量类型之间转换。有很多浮点转换指令,但是对于整型没有。

    简单转换是在积分向量类型之间转换的首选方法吗?或者我还有别的事要做?


    MSA$ mips-img-linux-gnu-gcc.exe -mmsa test.c -c
    test.c: In function 'main':
    test.c:12:2: note: use -flax-vector-conversions to permit conversions between ve
    ctors with differing element types or numbers of subparts
      v4u32 va = __builtin_msa_ld_w (a, 0);
      ^~~~~
    test.c:12:13: error: incompatible types when initializing type 'v4u32 {aka __vec
    tor(4) unsigned int}' using type '__vector(4) int'
      v4u32 va = __builtin_msa_ld_w (a, 0);
                 ^~~~~~~~~~~~~~~~~~
    test.c:13:13: error: incompatible types when initializing type 'v4u32 {aka __vec
    tor(4) unsigned int}' using type '__vector(4) int'
      v4u32 vb = __builtin_msa_ld_w (b, 0);
                 ^~~~~~~~~~~~~~~~~~
    test.c:16:22: error: incompatible type for argument 1 of '__builtin_msa_st_w'
      __builtin_msa_st_w (vc, c, 0);
                          ^~
    test.c:16:22: note: expected '__vector(4) int' but argument is of type 'v4u32 {a
    ka __vector(4) unsigned int}'
    
    2 回复  |  直到 6 年前
        1
  •  2
  •   Nominal Animal    6 年前

    要么你用石膏 -flax-vector-conversions 或者使用联合类型来表示向量寄存器并显式处理该联合类型。GCC明确支持这种类型的punning。

    例如,您可以声明 msa128 类型,

    typedef union __attribute__ ((aligned (16))) {
        v2u64   u64;
        v2i64   i64;
        v2f64   f64;
        v4u32   u32;
        v4i32   i32;
        v4f32   f32;
        v8u16   u16;
        v8i16   i16;
        v16u8   u8;
        v16i8   i8;
    } msa128;
    

    然后让您的代码在 MSA128 类型。您的示例程序可以编写为

        uint32_t a[4] = { 64, 128, 256, 512 };
        uint32_t b[4] = { 1024, 2048, 4096, 8192 };
        uint32_t c[4];
        msa128   va, vb, vc;
    
        va.i32 = __builtin_msa_ld_w(a, 0);
        vb.i32 = __builtin_msa_ld_w(b, 0);
        vc.u32 = __builtin_msa_adds_u_w(va.u32, vb.u32);
        __builtin_msa_st_w(vc.i32, c, 0);
    

    显然,记住需要使用的确切类型会变得非常烦人,因此一些静态内联帮助器函数肯定会很方便:

    static inline msa128  msa128_load64(const void *from, const int imm)
    { return (msa128){ .i64 = __builtin_msa_ld_d(from, imm); } }
    
    static inline msa128  msa128_load32(const void *from, const int imm)
    { return (msa128){ .i32 = __builtin_msa_ld_w(from, imm); } }
    
    static inline msa128  msa128_load16(const void *from, const int imm)
    { return (msa128){ .i16 = __builtin_msa_ld_h(from, imm); } }
    
    static inline msa128  msa128_load8(const void *from, const int imm)
    { return (msa128){ .i8  = __builtin_msa_ld_b(from, imm); } }
    
    static inline void  msa128_store64(const msa128 val, void *to, const int imm)
    { __builtin_msa_st_d(val.i64, to, imm); }
    
    static inline void  msa128_store32(const msa128 val, void *to, const int imm)
    { __builtin_msa_st_w(val.i32, to, imm); }
    
    static inline void  msa128_store16(const msa128 val, void *to, const int imm)
    { __builtin_msa_st_h(val.i16, to, imm); }
    
    static inline void  msa128_store8(const msa128 val, void *to, const int imm)
    { __builtin_msa_st_b(val.i8, to, imm); }
    

    例如,binary and、or、nor和xor操作是

    static inline msa128  msa128_and(const msa128 a, const msa128 b)
    { return (msa128){ .u8 = __builtin_msa_and_v(a, b) }; }
    
    static inline msa128  msa128_or(const msa128 a, const msa128 b)
    { return (msa128){ .u8 = __builtin_msa_or_v(a, b) }; }
    
    static inline msa128  msa128_nor(const msa128 a, const msa128 b)
    { return (msa128){ .u8 = __builtin_msa_nor_v(a, b) }; }
    
    static inline msa128  msa128_xor(const msa128 a, const msa128 b)
    { return (msa128){ .u8 = __builtin_msa_xor_v(a, b) }; }
    

    创建一些宏来表示数组形式的向量可能不会有什么影响:

    #define  MSA128_U64(...)  ((msa128){ .u64 = { __VA_ARGS__ }})
    #define  MSA128_I64(...)  ((msa128){ .i64 = { __VA_ARGS__ }})
    #define  MSA128_F64(...)  ((msa128){ .f64 = { __VA_ARGS__ }})
    #define  MSA128_U32(...)  ((msa128){ .u32 = { __VA_ARGS__ }})
    #define  MSA128_I32(...)  ((msa128){ .i32 = { __VA_ARGS__ }})
    #define  MSA128_F32(...)  ((msa128){ .f32 = { __VA_ARGS__ }})
    #define  MSA128_U16(...)  ((msa128){ .u16 = { __VA_ARGS__ }})
    #define  MSA128_I16(...)  ((msa128){ .i16 = { __VA_ARGS__ }})
    #define  MSA128_U8(...)   ((msa128){ .u8  = { __VA_ARGS__ }})
    #define  MSA128_I8(...)   ((msa128){ .i8  = { __VA_ARGS__ }})
    

    我建议使用这种特定于GCC的方法的原因是,不管怎样,内置组件都是特定于GCC的。除了union类型外,它非常接近gcc在 <immintrin.h> .

        2
  •  0
  •   jww avp    6 年前

    这里有一个C和C++都可以使用的替代方案。它执行一个 memcpy 在寄存器变量上。内联函数借用arm neon支持。ARM为氖矢量提供了投射,比如 vreinterpretq_u64_u8 . 这个 inline 在功能上需要C99。

    #include <msa.h>
    #include <stdint.h>
    #include <string.h>
    
    inline v4i32 reinterpretq_i32_u32(const v4u32 val) {
        v4i32 res;
        memcpy(&res, &val, sizeof(res));
        return res;
    }
    
    inline v4u32 reinterpretq_u32_i32(const v4i32 val) {
        v4u32 res;
        memcpy(&res, &val, sizeof(res));
        return res;
    }
    
    #define ALIGN16 __attribute__((aligned(16)))
    
    int main(int argc, char* argv[])
    {
        ALIGN16 uint32_t a[] = {64, 128, 256, 512};
        ALIGN16 uint32_t b[] = {1024, 2048, 4096, 8192};
        ALIGN16 uint32_t c[4];
    
        v4u32 va = reinterpretq_u32_i32(__builtin_msa_ld_w (a, 0));
        v4u32 vb = reinterpretq_u32_i32(__builtin_msa_ld_w (b, 0));
    
        v4u32 vc = __builtin_msa_adds_u_w (va, vb);
        __builtin_msa_st_w (reinterpretq_i32_u32(vc), c, 0);
    
        return 0;
    }
    

    和编译 -O3 (它是干净的) -Wall -Wextra ):

    MSA$ mips-img-linux-gnu-gcc.exe -O3 -mmsa test.c -c
    MSA$
    

    拆卸看起来像是通过了嗅探测试:

    MSA$ mips-img-linux-gnu-objdump.exe --disassemble test.o
    
    test.o:     file format elf32-tradbigmips
    
    Disassembly of section .text:
    
    00000000 <main>:
       0:   27bdffc8        addiu      sp,sp,-56
       4:   3c020000        lui        v0,0x0
       8:   24420000        addiu      v0,v0,0
       c:   78001062        ld.w       $w1,0(v0)
      10:   3c020000        lui        v0,0x0
      14:   24420000        addiu      v0,v0,0
      18:   78001022        ld.w       $w0,0(v0)
      1c:   79c10010        adds_u.w   $w0,$w0,$w1
      20:   7802e826        st.w       $w0,8(sp)
      24:   93a2000b        lbu        v0,11(sp)
      28:   03e00009        jr         ra
      2c:   27bd0038        addiu      sp,sp,56
    

    为了完整性,GCC 6.3.0:

    MSA$ mips-img-linux-gnu-gcc.exe --version
    mips-img-linux-gnu-gcc.exe (Codescape GNU Tools 2017.10-05 for MIPS IMG Linux) 6.3.0
    Copyright (C) 2016 Free Software Foundation, Inc.