【问题标题】:inline assembly code to read/write XMM & YMM registers?内联汇编代码来读/写 XMM 和 YMM 寄存器?
【发布时间】:2019-12-10 07:08:22
【问题描述】:

我有 2 个变量来模拟 X86 XMM 和 YMM,如下所示:

uint64_t xmm_value[2];
uint64_t ymm_value[4];

现在我想使用内联汇编来读写 XMM/YMM 寄存器。

  • 如何编写GCC内联汇编复制xmm_value到注册XMM0
  • 如何编写 GCC 内联汇编将寄存器YMM0 复制到ymm_value

我已经尝试搜索执行此操作的示例内联汇编,但找不到任何好的答案。谢谢!


在一些帮助下,我编写了这段代码,并且编译成功。我对 XMM 使用 movups,对 YMM 使用 vmovups,如下所示。这是正确的,我还能优化我的代码吗?

__m128 xmm0;
__m256 ymm0;

// write to XMM0, and read from YMM0
__asm__("movups %1, %%xmm0\n\t"
        "vmovups %%ymm0, %0"
        : "=m"(ymm0)
        : "m"(xmm0)
        : "xmm0", "ymm0");

更新 2:这是我的完整代码(添加了 vpbroadcastb)

__m128 xmm0;
__m256 ymm0;

// write to XMM0, and read from YMM0
__asm__("movups %1, %%xmm0\n\t"
        "vpbroadcastb %%xmm0, %%ymm0\n\t"
        "vmovups %%ymm0, %0"
        : "=m"(ymm0)
        : "m"(xmm0)
        : "xmm0", "ymm0");

想法是我想将xmm0(变量)复制到XMM0,然后运行vpbroadcastb,然后将YMM0中的结果复制到ymm0(变量)。现在才知道XMM0是YMM0的下半部分,所以这段代码还可以改进吗?

【问题讨论】:

  • 为什么需要为此使用内联汇编?
  • 有一些可用的方法,如12 等......
  • 感谢您的指点,但这些链接并没有直接回答我的问题,所以我认为其他人仍然可以从这个问题中受益。
  • 为什么要使用内存操作数而不是 XMM 寄存器?此外,您可能希望将 vmovups %1, %%xmm0 零扩展到 YMM0(就像将 EAX 隐式零扩展到 RAX 一样)。使用旧版 SSE 指令写入 XMM0 会使上层通道保持不变。另请参阅 Why is this SSE code 6 times slower without VZEROUPPER on Skylake? 以了解 XMM 错误依赖项或 SSE/AVX 转换停止。 (不会导致 Haswell 上的转换停顿,除非有任何 YMM 寄存器带有脏上限,但混合 SSE 和 AVX 需要小心)

标签: gcc assembly sse inline-assembly


【解决方案1】:

第一步是#include <immintrin.h>,其中包括所需类型的所有定义以及用于访问所有 MMX/SSE/AVX 指令的所有Intel Intrinsics。在大多数情况下,您希望使用这些内在函数而不是内联汇编,因为它们更清晰、更便携,但如果您真的想使用内联 asm,您可以使用内在类型(__m64__m128、@987654325 @、__m256 等)以及 x 约束以绑定到正确类型的 xmm/ymm 寄存器。

【讨论】:

  • 克里斯,这是一个不错的指针,但我找不到任何内在的让我读/写特定寄存器,例如 xmm0 或 ymm0。还是我错过了什么?谢谢!
  • 为什么需要访问“特定寄存器”?如果您将变量声明为__m128 myvar;,那么您只需使用 myvar。您不知道(或关心)它最终在哪个数字寄存器中,您只需将其传递给适当的内在函数以执行所需的功能。或者,如果出于某种原因您确实关心,您需要向我们提供更多信息,告诉我们原因。
  • 听起来您正在调用一个将 128 位值作为输入并返回 256 位值的函数,而您正在调用的函数使用 xmm0 作为输入,使用 ymm0 作为输出。但是,根据您的环境,这些实际上可以是编译器通常用来为声明为extern "C" __m256 example(__m128 a); 的函数传递值的寄存器。我当然会先尝试一下。使用内联 asm 的解决方案很棘手、容易出错、难以支持,并且应该始终是您的最后选择。从内联 asm 调用函数(如此处)特别糟糕。
  • @aq2019:您可以使用register __m128 foo asm("xmm0") 强制"x" 约束选择XMM0。与遵循标准调用约定的 JITing 函数相比,这听起来是一个非常糟糕的想法,因此您可以编写 C 原型,但是如果您想编写难以维护且容易出错的 asm,那就是这样。不要忘记,您不能安全地破坏内联 asm 中的红色区域,因此您需要在 call 之前使用 add $-128, %rsp
  • 这是一个带有 mov, call, mov 的单一汇编吗?这是一个问题,因为您调用的函数可以破坏寄存器(如 eax),并且您没有将它们列为破坏。还有堆栈对齐问题和红区问题。如果您使用多个 asm 语句执行此操作,则不能保证执行顺序,并且不能保证第一个赋值的内容在 asm 指令之后保留。无论如何它可能会起作用,但是根据破坏规则设计代码是一个坏主意。这就是为什么我一直试图让你远离这种方法。
猜你喜欢
  • 2021-03-21
  • 2019-03-24
  • 2011-12-20
  • 1970-01-01
  • 2017-07-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多