为什么 Clang 无法通过 constexpr 函数中的索引获取 __m128 的数据

2024-02-11

#include <cstddef>
#include <immintrin.h>

constexpr float get_data(__m128 a, std::size_t pos) {
  return a[pos];
}

它适用于海湾合作委员会。我想知道是否有任何解决方法可以使其成为可能


不管constexpr, a[pos]仅作为 GNU C 扩展有效,不能移植到 MSVC。存储到数组,或 C++20std::bit_cast到一个结构可能会起作用。bit_cast与 constexpr 兼容,与其他类型双关方法不同。尽管我担心跨编译器编译运行时变量的效率如何pos

bit_cast确实可以用 clang 编译,并且可以在constexpr功能。但对于 GCC 编译效率低下。

更正: clang 编译此代码,但如果在以下上下文中调用则拒绝它requires对其进行不断评估。 note: constexpr bit_cast involving type '__attribute__((__vector_size__(4 * sizeof(float)))) float const' (vector of 4 'float' values) is not yet supported.

在 constexpr 上下文中使用当前 clang 的其他失败尝试:

  • _mm_store_ps- 不支持。也不是*(__m128*)f = a;因为它是一个reinterpret_cast。
  • f[0] = vec[0]初始化器:不,constexpr 中的 clang 甚至不支持 GNU C 本机向量的文字常量索引。
  • 联合类型双关:在 constexpr 上下文中读取不允许的非活动成员
  • _mm_cvtss_f32(vec)- 非 constexpr 函数无法使用,因此没有机会使用if constexpr用于单独的洗牌和返回。

不起作用的答案,可能在将来的某个时候起作用,但不适用于 clang trunk pre 15.0

#include <cstddef>
#include <immintrin.h>
#include <bit>

// portable, but inefficient with GCC
constexpr float get_data(__m128 a, std::size_t pos) {
    struct foo { float f[4]; } s;
    s = std::bit_cast<foo>(a);
    return s.f[pos];
}
float test_idx2(__m128 a){
    return get_data(a, 2);
}

float test_idxvar(__m128 a, size_t pos){
    return get_data(a, pos);
}

这些编译为体面的汇编在戈德螺栓上 https://godbolt.org/#g:!((g:!((g:!((h:codeEditor,i:(filename:%271%27,fontScale:14,fontUsePx:%270%27,j:1,lang:c%2B%2B,selection:(endColumn:1,endLineNumber:19,positionColumn:1,positionLineNumber:19,selectionStartColumn:1,selectionStartLineNumber:1,startColumn:1,startLineNumber:1),source:%27%23include+%3Ccstddef%3E%0A%23include+%3Cimmintrin.h%3E%0A%23include+%3Cbit%3E%0A%0Aconstexpr+float+get_data(__m128+a,+std::size_t+pos)+%7B%0A++++struct+foo+%7B+float+f%5B4%5D%3B+%7D+s%3B%0A++++s+%3D+std::bit_cast%3Cfoo%3E(a)%3B%0A++++return+s.f%5Bpos%5D%3B%0A++//return+a%5Bpos%5D%3B%0A%7D%0A%0Afloat+test_idx2(__m128+a)%7B%0A++++return+get_data(a,+2)%3B%0A%7D%0A%0Afloat+test_idxvar(__m128+a,+size_t+pos)%7B%0A++++return+get_data(a,+pos)%3B%0A%7D%0A%27),l:%275%27,n:%270%27,o:%27C%2B%2B+source+%231%27,t:%270%27)),k:46.715328467153284,l:%274%27,n:%270%27,o:%27%27,s:0,t:%270%27),(g:!((g:!((h:compiler,i:(compiler:clang1400,filters:(b:%270%27,binary:%271%27,commentOnly:%270%27,demangle:%270%27,directives:%270%27,execute:%271%27,intel:%270%27,libraryCode:%270%27,trim:%271%27),flagsViewOpen:%271%27,fontScale:14,fontUsePx:%270%27,j:1,lang:c%2B%2B,libs:!(),options:%27-O3+-march%3Dhaswell+-std%3Dgnu%2B%2B20%27,selection:(endColumn:1,endLineNumber:1,positionColumn:1,positionLineNumber:1,selectionStartColumn:1,selectionStartLineNumber:1,startColumn:1,startLineNumber:1),source:1,tree:%271%27),l:%275%27,n:%270%27,o:%27x86-64+clang+14.0.0+(C%2B%2B,+Editor+%231,+Compiler+%231)%27,t:%270%27)),k:45.88257813684033,l:%274%27,m:50,n:%270%27,o:%27%27,s:0,t:%270%27),(g:!((h:output,i:(compilerName:%27x86-64+clang+14.0.0%27,editorid:1,fontScale:14,fontUsePx:%270%27,j:1,wrap:%271%27),l:%275%27,n:%270%27,o:%27Output+of+x86-64+clang+14.0.0+(Compiler+%231)%27,t:%270%27)),header:(),l:%274%27,m:50,n:%270%27,o:%27%27,s:0,t:%270%27)),k:53.284671532846716,l:%273%27,n:%270%27,o:%27%27,t:%270%27)),l:%272%27,n:%270%27,o:%27%27,t:%270%27)),version:4,与您从 clang 中得到的结果相同a[pos]。我用了-O3 -march=haswell -std=gnu++20

# clang 14 -O3 -march=haswell -std=gnu++20
# get_data has no asm output; constexpr is like inline in that respect

test_idx2(float __vector(4)):
        vpermilpd       xmm0, xmm0, 1           # xmm0 = xmm0[1,0]
        ret
test_idxvar(float __vector(4), unsigned long):
        vmovups xmmword ptr [rsp - 16], xmm0
        vmovss  xmm0, dword ptr [rsp + 4*rdi - 16] # xmm0 = mem[0],zero,zero,zero
        ret

对于运行时变量索引来说,存储/重新加载是一个明智的策略,尽管vmovd / vpermilps由于 AVX 引入了使用双字索引的可变控制洗牌,因此这将是一个选项。超出范围的索引是 UB,因此编译器在这种情况下不需要返回任何特定数据。

Using vpermilpd对于常数索引2与代码大小相比是一种浪费vmovhlps xmm0, xmm0, xmm0 or vunpckhpd。它需要更长的 VEX 前缀和立即数(即 2 个字节的机器代码大小),但在大多数 CPU 上的其他性能相同。


不幸的是 GCC 做得不太好

即使对于固定索引,我们也会得到存储/重新加载2,甚至更糟糕的是,通过 GP 整数寄存器弹跳来重新加载。这是一个错过的优化,但我不知道如果报告它会多快得到修复。所以如果你打算这样做,也许#ifdef __clang__ or #ifdef __llvm__对于位广播,以及#ifdef __GNUC__ for a[pos]。 (Clang 定义__GNUC__所以在特殊外壳叮当声之后检查一下。)

# gcc12 -O3 -march=haswell -std=gnu++20
test_idx2(float __vector(4)):
        vmovaps XMMWORD PTR [rsp-24], xmm0
        mov     rax, QWORD PTR [rsp-16]
        vmovd   xmm0, eax              # slow: should have loaded directly from mem
        ret

test_idxvar(float __vector(4), unsigned long):
        vmovdqa XMMWORD PTR [rsp-24], xmm0
        vmovss  xmm0, DWORD PTR [rsp-24+rdi*4]   # this is fine, same as clang
        ret

有趣的是,运行时变量版本没有针对 GCC 的相同反优化。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么 Clang 无法通过 constexpr 函数中的索引获取 __m128 的数据 的相关文章

  • 捕获 foreach 条件中抛出的异常

    我有一个foreach在 foreach 本身的条件下循环期间中断的循环 有没有办法try catch抛出异常然后继续循环的项 这将运行几次 直到异常发生然后结束 try foreach b in bees exception is in
  • ASP .NET MVC,创建类似路由配置的永久链接

    我需要帮助在 MVC 网站中创建类似 URL 路由的永久链接 Slug 已设置为 www xyz com profile slug 代码为 routes MapRoute name Profile url profile slug defa
  • try-catch 中未处理的异常

    try list from XElement e in d Descendants wix File where e Attribute Name Value Contains temp Name e Parent Parent Attri
  • 在 Xcode4 中使用 Boost

    有人设置 C Xcode4 项目来使用 Boost 吗 对于一个简单的 C 控制台应用程序 我需要在 Xcode 中设置哪些设置 Thanks 用这个来管理它 和这个
  • 为什么 BOOST_FOREACH 不完全等同于手工编码的?

    From 增强文档 http www boost org doc libs 1 48 0 doc html foreach html foreach introduction what is literal boost foreach li
  • 如何在 VS 中键入时显示方法的完整文档?

    标题非常具有描述性 是否有任何扩展可以让我看到我正在输入的方法的完整文档 我想查看文档 因为我可以在对象浏览器中看到它 其中包含参数的描述和所有内容 而不仅仅是一些 摘要 当然可以选择查看所有覆盖 它可能是智能感知的一部分 或者我不知道它并
  • 为什么 std::allocator 在 C++17 中丢失成员类型/函数?

    一边看着std 分配器 http en cppreference com w cpp memory allocator 我看到成员 value type pointer const pointer reference const refer
  • 组合框项目为空但数据源已满

    将列表绑定到组合框后 其 dataSource Count 为 5 但组合框项目计数为 0 怎么会这样 我习惯了 Web 编程 而且这是在 Windows 窗体中进行的 所以不行combo DataBind 方法存在 这里的问题是 我试图以
  • C# 创建数组的数组

    我正在尝试创建一个将使用重复数据的数组数组 如下所示 int list1 new int 4 1 2 3 4 int list2 new int 4 5 6 7 8 int list3 new int 4 1 3 2 1 int list4
  • C# using 语句、SQL 和 SqlConnection

    使用 using 语句 C SQL 可以吗 private static void CreateCommand string queryString string connectionString using SqlConnection c
  • UWP 无法在两个应用程序之间创建本地主机连接

    我正在尝试在两个 UWP 应用程序之间设置 TCP 连接 当服务器和客户端在同一个应用程序中运行时 它可以正常工作 但是 当我将服务器部分移动到一个应用程序并将客户端部分移动到另一个应用程序时 ConnectAsync 会引发异常 服务器未
  • 32位PPC rlwinm指令

    我在理解上有点困难rlwinmPPC 汇编指令 旋转左字立即然后与掩码 我正在尝试反转函数的这一部分 rlwinm r3 r3 0 28 28 我已经知道什么了r3 is r3在本例中是一个 4 字节整数 但我不确定这条指令到底是什么rlw
  • 运行代码首先迁移更新数据库时出错

    我在迁移到数据库时遇到问题 并且似乎找不到我遇到的错误的答案 System MissingMethodException Method not found System Data Entity Migrations Builders Tab
  • 如何在 GCC 5 中处理双 ABI?

    我尝试了解如何克服 GCC 5 中引入的双重 ABI 的问题 但是 我没能做到 这是一个重现错误的非常简单的示例 我使用的GCC版本是5 2 如您所见 我的主要函数 在 main cpp 文件中 非常简单 main cpp include
  • 以编程方式使用自定义元素创建网格

    我正在尝试以编程方式创建一个网格 并将自定义控件作为子项附加到网格中 作为 2x2 矩阵中的第 0 行第 0 列 为了让事情变得更棘手 我使用了 MVVM 设计模式 下面是一些代码可以帮助大家理解这个想法 应用程序 xaml cs base
  • 热重载时调用方法

    我正在使用 Visual Studio 2022 和 C 制作游戏 我想知道当您热重新加载应用程序 当它正在运行时 时是否可以触发一些代码 我基本上有 2 个名为 UnloadLevel 和 LoadLevel 的方法 我想在热重载时执行它
  • 在基类集合上调用派生方法

    我有一个名为 A 的抽象类 以及实现 A 的其他类 B C D E 我的派生类持有不同类型的值 我还有一个 A 对象的列表 abstract class A class B class A public int val get privat
  • boost::program_options:带有固定和可变标记的参数?

    是否可以在 boost program options 中使用此类参数 program p1 123 p2 234 p3 345 p12 678 即 是否可以使用第一个标记指定参数名称 例如 p 后跟一个数字 是动态的吗 我想避免这种情况
  • 如何确定母版页中正在显示哪个子页?

    我正在母版页上编写代码 我需要知道正在显示哪个子 内容 页面 我怎样才能以编程方式做到这一点 我用这个 string pageName this ContentPlaceHolder1 Page GetType FullName 它以 AS
  • WPF/数据集:如何通过 XAML 将相关表中的数据绑定到数据网格列中?

    我正在使用 WPF DataSet 连接到 SQL Server Express XAML 和 C Visual Studio 2013 Express 我从名为 BankNoteBook 的现有 SQL Server Express 数据

随机推荐