ARM Neon:如何从 uint8x16_t 转换为 uint8x8x2_t? [英] ARM Neon: How to convert from uint8x16_t to uint8x8x2_t?

查看:50
本文介绍了ARM Neon:如何从 uint8x16_t 转换为 uint8x8x2_t?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近发现了 vreinterpret{q}_dsttype_srctype 转换运算符.但是,这似乎不支持 此链接(页面底部):

I recently discovered about the vreinterpret{q}_dsttype_srctype casting operator. However this doesn't seem to support conversion in the data type described at this link (bottom of the page):

某些内在函数使用以下形式的向量类型数组:

Some intrinsics use an array of vector types of the form:

x<车道数>x<数组长度>_t

这些类型被视为包含单个的普通 C 结构名为 val 的元素.

These types are treated as ordinary C structures containing a single element named val.

一个示例结构定义是:

struct int16x4x2_t    
{
    int16x4_t val[2];     
};

你知道如何从uint8x16_t转换成uint8x8x2_t吗?

Do you know how to convert from uint8x16_t to uint8x8x2_t?

请注意,问题无法使用联合可靠地解决(从非活动成员读取会导致未定义的行为这只是 C++ 的情况,而事实证明 C 允许类型双关),也不是使用指针进行强制转换(违反了严格的别名规则).

Note that that the problem cannot be reliably addressed using union (reading from inactive members leads to undefined behaviour That's only the case for C++, while it turns out that C allows type punning), nor by using pointers to cast (breaks the strict aliasing rule).

推荐答案

根据您的评论,您似乎想要进行善意 转换——也就是说,产生一个独特的、新的, 不同类型的单独值.这与重新解释是非常不同的事情,例如对您的问题的引导表明您想要.特别是,您假定变量声明如下:

Based on your comments, it seems you want to perform a bona fide conversion -- that is, to produce a distinct, new, separate value of a different type. This is a very different thing than a reinterpretation, such as the lead-in to your question suggests you wanted. In particular, you posit variables declared like this:

uint8x16_t  a;
uint8x8x2_t b;

// code to set the value of a ...

并且您想知道如何设置 b 的值,使其在某种意义上等同于 a 的值.

and you want to know how to set the value of b so that it is in some sense equivalent to the value of a.

严格的别名规则(C2011 6.5/7) 说,

The strict aliasing rule (C2011 6.5/7) says,

一个对象的存储值只能由左值访问具有以下类型之一的表达式:

An object shall have its stored value accessed only by an lvalue expression that has one of the following types:

  • 与对象的有效类型兼容的类型,[...]
  • 在其成员中包含上述类型之一的聚合或联合类型[...],或
  • 一种字符类型.
  • a type compatible with the effective type of the object, [...]
  • an aggregate or union type that includes one of the aforementioned types among its members [...], or
  • a character type.

(强调.其他枚举选项涉及对象的有效类型或兼容类型的不同限定和不同签名的版本;这些在这里不相关.)

(Emphasis added. Other enumerated options involve differently-qualified and differently-signed versions of the of the effective type of the object or compatible types; these are not relevant here.)

请注意,这些规定从不干扰通过变量a访问a的值,包括成员值,对于<代码>b.但是不要忽视有效类型"这个词的用法——这就是在稍微不同的情况下事情会变得混乱的地方.稍后会详细介绍.

Note that these provisions never interfere with accessing a's value, including the member value, via variable a, and similarly for b. But don't overlook overlook the usage of the term "effective type" -- this is where things can get bolluxed up under slightly different circumstances. More on that later.

C 当然允许您通过中间 union 执行转换,或者您可以依赖 b 作为联合成员来删除中间"部分:

C certainly permits you to perform a conversion via an intermediate union, or you could rely on b being a union member in the first place so as to remove the "intermediate" part:

union {
    uint8x16_t  x1;
    uint8x8_2_t x2;
} temp;
temp.x1 = a;
b = temp.x2;

使用类型转换指针(产生 UB)

然而,虽然看到它并不少见,但 C 不允许您通过指针输入双关语:

Using a typecast pointer (to produce UB)

However, although it's not so uncommon to see it, C does not permit you to type-pun via a pointer:

// UNDEFINED BEHAVIOR - strict-aliasing violation
    b = *(uint8x8x2_t *)&a;
// DON'T DO THAT

在那里,您正在通过 uint8x8x2_t 类型的左值访问 a 的值,其有效类型为 uint8x16_t.请注意,这不是禁止的强制转换,我认为甚至不是取消引用——它是 读取 取消引用的值以应用 = 运算符.

There, you are accessing the value of a, whose effective type is uint8x16_t, via an lvalue of type uint8x8x2_t. Note that it is not the cast that is forbidden, nor even, I'd argue, the dereferencing -- it is reading the dereferenced value so as to apply the side effect of the = operator.

现在,memcpy() 怎么样?这就是它变得有趣的地方.C 允许通过字符类型的左值访问 ab 的存储值,尽管它的参数被声明为类型 void *,这是对 memcpy() 如何工作的唯一合理解释.当然其描述将其描述为抄袭字符.因此,执行一个

Now, what about memcpy()? This is where it gets interesting. C permits the stored values of a and b to be accessed via lvalues of character type, and although its arguments are declared to have type void *, this is the only plausible interpretation of how memcpy() works. Certainly its description characterizes it as copying characters. There is therefore nothing wrong with performing a

memcpy(&b, &a, sizeof a);

这样做后,您可以通过变量 b 自由访问 b 的值,如前所述.这样做的某些方面在更一般的情况下可能会出现问题,但这里没有 UB.

Having done so, you may freely access the value of b via variable b, as already mentioned. There are aspects of doing so that could be problematic in a more general context, but there's no UB here.

然而,将此与表面上类似的情况进行对比,在这种情况下,您希望将转换后的值放入动态分配的空间中:

However, contrast this with the superficially similar situation in which you want to put the converted value into dynamically-allocated space:

uint8x8x2_t *c = malloc(sizeof(*c));
memcpy(c, &a, sizeof a);

这有什么问题?就目前而言,它没有任何问题,但是如果您之后尝试访问 *c 的值,您将获得 UB.为什么?因为 c 指向的内存没有声明类型,因此它的有效类型是最后存储在其中的任何内容的有效类型(如果有有效类型)),包括该值是否通过 memcpy() 复制到其中(C2011 6.5/6).结果,c指向的对象复制后的有效类型为uint8x16_t,而表达式*c的类型为uint8x8x2_t;严格的别名规则表示通过该左值访问该对象会产生 UB.

What could be wrong with that? Nothing is wrong with it, as far as it goes, but here you have UB if you afterward you try to access the value of *c. Why? because the memory to which c points does not have a declared type, therefore its effective type is the effective type of whatever was last stored in it (if that has an effective type), including if that value was copied into it via memcpy() (C2011 6.5/6). As a result, the object to which c points has effective type uint8x16_t after the copy, whereas the expression *c has type uint8x8x2_t; the strict aliasing rule says that accessing that object via that lvalue produces UB.

这篇关于ARM Neon:如何从 uint8x16_t 转换为 uint8x8x2_t?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆