使用 __builtin_msa_ld_* 后如何转换为无符号向量类型
Posted
技术标签:
【中文标题】使用 __builtin_msa_ld_* 后如何转换为无符号向量类型【英文标题】:How to cast to unsigned vector type after using __builtin_msa_ld_* 【发布时间】:2018-10-21 05:58:48 【问题描述】:我正在使用Codescape GCC Toolchain 评估MIPS SIMD Architecture (MSA) 编程。关于 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
(imm_n512_511
和朋友们在 GCC 手册中讨论,6.59.16 MIPS SIMD Architecture (MSA) Support)。
我在MIPS SIMD Architecture 阅读了 MIPS 论文(?),但它没有讨论如何在整数向量类型之间进行转换。有很多浮点转换指令,但没有整数类型。
简单的强制转换是在整数向量类型之间进行转换的首选方式吗?或者还有什么我应该做的吗?
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'
【问题讨论】:
为什么不使用va
和vb
而不是a
和b
?文档还说,“加载/存储指令不需要 128 位(16 字节)内存地址对齐。”,所以我认为您不需要 ALIGN16
。我认为您不必担心“MSA 通过一组超过 150 条新指令来补充完善的 MIPS 架构,这些指令在 8、16、32 和 64 位整数的 32 个向量寄存器上运行,16 - 和 32 位定点,或 32 位和 64 位浮点数据元素....”,看起来像一个简单的转换将正确地完成这项工作(当然,如果真正的整数是无符号的)。跨度>
感谢 @Stargateur - ALIGN16
来自 MIPS SIMD Architecture,第 5.1 节矢量数据类型和内在函数,第 10 页:“建议将矢量数据与矢量的大小对齐注册”。我认为“向量寄存器的大小”的重要性在于,DSP有64位向量寄存器,而MSA有128位向量寄存器。
好吧,recommanded 与 required 不同,也许最好对齐它们,我没有阅读所有文档。另外我认为他们没有添加加载无符号的特定指令来保存一些指令,因为我认为他们的加载对两个符号都有效。也许,您应该添加自己的包装函数,该函数会在您加载和存储无符号整数时为您转换向量。
【参考方案1】:
要么使用强制类型转换和-flax-vector-conversions
,要么使用联合类型来表示向量寄存器并显式处理该联合类型。 GCC 明确支持这种类型的双关语。
例如,您可以声明一个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);
例如,二元 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 的。除了联合类型之外,它非常接近 GCC 在<immintrin.h>
中实现 Intel/AMD 向量内在函数的方式。
【讨论】:
谢谢@Nominal。最终我需要在 C++ 中使用它,所以我不能使用联合技巧。 (抱歉,我没有添加 C++ 标签,因为评论 C 和 C++ 的人是不同的语言......)。 @jww 您的问题是标记 C 和 C++ 错误的典型情况,如果您需要两种语言的答案,您必须创建两个问题,然后由 C++ 专家或 C 决定答案对于 C 和 C++ 都是正确的,并且会以重复的形式关闭。例如,我可以回答(并且我做了但在评论中)C 部分,但我不能确定 C++ 部分,因为我从未阅读过 C++ 标准。除非您的问题涉及兼容性问题(特定问题),否则不要标记两种语言。 谢谢@nominal。是的,联合技巧可以是 C++ 中的未定义行为如果您访问非活动成员(此处依赖于此)。另请参阅Accessing inactive union member and undefined behavior? 我发现我可以通过获取向量变量的地址并使用memcpy
以使 C 和 C++ 满意的方式压缩警告,但这是一个代码缺陷。太糟糕了,MIPS 没有像 ARM 内在函数那样的强制转换(比如 vreinterpretq_i32_u32
)。
再次感谢@nominal。如果 MIPS 专家出现并提供更好的东西,我可能需要接受。
@jww:别担心!对我来说,只有答案的适用性/有用性很重要。 (我承认,我是对 C 和 C++ 差异很重要的人之一——但在我的辩护中,这是因为答案是(或应该是)完全不同的,以提高效率。这 is其中一种情况。)由于我将 C 用于我的低级代码,并且没有在 C++ 中使用向量内在函数,我不确定 C++ 中的最佳解决方案是什么,但我今天稍后会看看,如果我发现比 memcpy() 更好的方法,请编辑我的答案。【参考方案2】:
这是一个同时适用于 C 和 C++ 的替代方案。它对寄存器变量执行memcpy
。内联函数借鉴了 ARM NEON 支持。 ARM 为 NEON 向量提供强制转换,例如 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.
【讨论】:
我想知道使用typedef char msa128 __attribute__((vector_size (16), aligned (16)));
作为基本向量类型是否足够。由于它是字符类型,GCC 允许它为任何其他类型起别名,因此您可以在例如v2u64 a;
和 v4f32 b;
使用 a = (v2u64)((msa128)(b));
和 b = (v4f32)((msa128)(a));
(在 g++ (-std=gnu++14
) 和 gcc (-std=gnu11
) 中使用 GCC 5.4.0)。以上是关于使用 __builtin_msa_ld_* 后如何转换为无符号向量类型的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 mongoDb java 异步驱动程序插入 mongoDb 集合后获取 _id
paypal支付平台如何使用二次验证码_虚拟MFA_两步验证_谷歌身份验证器?
__future__ 模块在 Python 2.7 中如何工作? [复制]
[Java]_[初级]_[Observer和Observable失效后如何使用java.beans包下的类来实现观察者模式]