在 GNU C 内联汇编中,xmm/ymm/zmm 的单个操作数的大小覆盖修饰符是什么?
In GNU C inline asm, what are the size-override modifiers for xmm/ymm/zmm for a single operand?
在尝试回答 时,我试图做这样的事情:
__m512 mul_bcast(__m512 a, float b) {
asm(
"vbroadcastss %k[scalar], %q[scalar]\n\t" // want vbcast.. %xmm0, %zmm0
"vmulps %q[scalar], %[vec], %[vec]\n\t"
: [vec] "+x" (a), [scalar] "+&x" (b)
: :
);
return a;
}
GNU C x86 Operand Modifiers 文档仅指定最大为 q
的修饰符(DI (DoubleInt) 大小,64 位)。在向量寄存器上使用 q
将始终将其降低为 xmm
(从 ymm
或 zmm
)。例如标量寄存器:
long scratch = 0; // not useful instructions, just syntax demo
asm(
"movw symbol(%q[inttmp]), %w[inttmp]\n\t" // movw symbol(%rax), %ax
"movsbl %h[inttmp], %k[inttmp]\n\t" // movsx %ah, %eax
: [inttmp] "+r" (scratch)
:: "memory" // we read some index in symbol[]
);
问题:
在向量寄存器大小之间更改的修饰符是什么?
此外,输入或输出操作数是否有任何特定的大小限制?不同于通用 x
的东西,它最终可能是 xmm、ymm 或 zmm,具体取决于您放在括号中的表达式的类型。
题外话:
clang 似乎有一些 Yi
/ Yt
约束(不是修饰符),但我也找不到相关文档。 clang 甚至不会编译它,即使向量指令被注释掉,因为它不喜欢 +x
作为 __m512
向量的约束。
背景/动机
我可以通过将标量作为输入操作数传递来获得我想要的结果,将其限制在与更宽的输出操作数相同的寄存器中,但它比较笨拙。 (这个用例的最大缺点是 AFAIK 匹配约束只能通过操作数编号引用,而不是 [symbolic_name]
,因此当 adding/removing 输出约束时它很容易被破坏。)
// does what I want, by using a paired output and input constraint
__m512 mul_bcast(__m512 a, float b) {
__m512 tmpvec;
asm(
"vbroadcastss %[scalar], %[tmpvec]\n\t"
"vmulps %[tmpvec], %[vec], %[vec]\n\t"
: [vec] "+x" (a), [tmpvec] "=&x" (tmpvec)
: [scalar] "1" (b)
:
);
return a;
}
此外,我认为我试图解决的问题的整个方法将是死胡同,因为 Multi-Alternative constraints 不允许您为不同的约束模式提供不同的 asm。我希望 x
和 r
约束最终从寄存器发出 vbroadcastss
,而 m
约束最终发出 vmulps (mem_src){1to16}, %zmm_src2, %zmm_dst
(折叠广播-加载)。使用内联 asm 这样做的目的是 gcc 还不知道如何将 set1()
内存操作数折叠成广播负载(但 clang 知道)。
无论如何,这个具体问题是关于向量寄存器的操作数修饰符和约束。请关注这一点,但欢迎对另一个问题发表评论和回答。 (或者更好,只是评论/回答 Z Boson 关于嵌入式广播的问题。)
来自 GCC 源的文件 gcc/config/i386/i386.c:
b -- print the QImode name of the register for the indicated operand.
%b0 would print %al if operands[0] is reg 0.
w -- likewise, print the HImode name of the register.
k -- likewise, print the SImode name of the register.
q -- likewise, print the DImode name of the register.
x -- likewise, print the V4SFmode name of the register.
t -- likewise, print the V8SFmode name of the register.
g -- likewise, print the V16SFmode name of the register.
h -- print the QImode name for a "high" register, either ah, bh, ch or dh.
与gcc/config/i386/contraints.md类似:
;; We use the Y prefix to denote any number of conditional register sets:
;; z First SSE register.
;; i SSE2 inter-unit moves to SSE register enabled
;; j SSE2 inter-unit moves from SSE register enabled
;; m MMX inter-unit moves to MMX register enabled
;; n MMX inter-unit moves from MMX register enabled
;; a Integer register when zero extensions with AND are disabled
;; p Integer register when TARGET_PARTIAL_REG_STALL is disabled
;; f x87 register when 80387 floating point arithmetic is enabled
;; r SSE regs not requiring REX prefix when prefixes avoidance is enabled
;; and all SSE regs otherwise
此文件还定义了一个 "Yk" 约束,但我不知道它在 asm 语句中的效果如何:
(define_register_constraint "Yk" "TARGET_AVX512F ? MASK_EVEX_REGS : NO_REGS"
"@internal Any mask register that can be used as predicate, i.e. k1-k7.")
请注意,这都是从最新的 SVN 修订版复制而来的。我不知道 GCC 的哪个版本(如果有)添加了您感兴趣的特定修饰符和约束。
似乎所有最新版本的 GCC 都接受 'q' 和 'x' 作为修饰符来打印 YMM 寄存器的 XMM 版本。
Intel 的 icc 看起来接受 'q',但不接受 'x'(至少通过版本 13.0.1)。
[编辑:好吧,它在下面的这个小例子中有效,但在一个真实的测试用例中,我遇到了 icc 14.0.3 接受 'q' 但写 'ymm' 的问题.]
[编辑:使用更新版本的 icc 进行测试,我发现 icc 15 和 icc 16 都不能与 'q' 或 'x' 一起使用。]
但 Clang 3.6 及更早版本不接受这两种语法。至少在 Godbolt 上,Clang 3.7 会同时崩溃!
// inline assembly modifiers to convert ymm to xmm
#include <x86intrin.h>
#include <stdint.h>
// gcc also accepts "%q1" as "%x1"
// icc accepts "%q1" but not "%x1"
// clang-3.6 accepts neither
// clang-3.7 crashes with both!
#define ASM_MOVD(vec, reg) \
__asm volatile("vmovd %q1, %0" : \
"=r" (reg) : \
"x" (vec) \
);
uint32_t movd_ymm(__m256i ymm) {
uint32_t low;
ASM_MOVD(ymm, low);
return low;
}
uint32_t movd_xmm(__m128i xmm) {
uint32_t low;
ASM_MOVD(xmm, low);
return low;
}
Link 在 Godbolt 上测试:http://goo.gl/bOkjNu
(很抱歉,这不是您问题的完整答案,但这似乎是值得分享的有用信息,但评论时间太长)
在尝试回答
__m512 mul_bcast(__m512 a, float b) {
asm(
"vbroadcastss %k[scalar], %q[scalar]\n\t" // want vbcast.. %xmm0, %zmm0
"vmulps %q[scalar], %[vec], %[vec]\n\t"
: [vec] "+x" (a), [scalar] "+&x" (b)
: :
);
return a;
}
GNU C x86 Operand Modifiers 文档仅指定最大为 q
的修饰符(DI (DoubleInt) 大小,64 位)。在向量寄存器上使用 q
将始终将其降低为 xmm
(从 ymm
或 zmm
)。例如标量寄存器:
long scratch = 0; // not useful instructions, just syntax demo
asm(
"movw symbol(%q[inttmp]), %w[inttmp]\n\t" // movw symbol(%rax), %ax
"movsbl %h[inttmp], %k[inttmp]\n\t" // movsx %ah, %eax
: [inttmp] "+r" (scratch)
:: "memory" // we read some index in symbol[]
);
问题:
在向量寄存器大小之间更改的修饰符是什么?
此外,输入或输出操作数是否有任何特定的大小限制?不同于通用 x
的东西,它最终可能是 xmm、ymm 或 zmm,具体取决于您放在括号中的表达式的类型。
题外话:
clang 似乎有一些 Yi
/ Yt
约束(不是修饰符),但我也找不到相关文档。 clang 甚至不会编译它,即使向量指令被注释掉,因为它不喜欢 +x
作为 __m512
向量的约束。
背景/动机
我可以通过将标量作为输入操作数传递来获得我想要的结果,将其限制在与更宽的输出操作数相同的寄存器中,但它比较笨拙。 (这个用例的最大缺点是 AFAIK 匹配约束只能通过操作数编号引用,而不是 [symbolic_name]
,因此当 adding/removing 输出约束时它很容易被破坏。)
// does what I want, by using a paired output and input constraint
__m512 mul_bcast(__m512 a, float b) {
__m512 tmpvec;
asm(
"vbroadcastss %[scalar], %[tmpvec]\n\t"
"vmulps %[tmpvec], %[vec], %[vec]\n\t"
: [vec] "+x" (a), [tmpvec] "=&x" (tmpvec)
: [scalar] "1" (b)
:
);
return a;
}
此外,我认为我试图解决的问题的整个方法将是死胡同,因为 Multi-Alternative constraints 不允许您为不同的约束模式提供不同的 asm。我希望 x
和 r
约束最终从寄存器发出 vbroadcastss
,而 m
约束最终发出 vmulps (mem_src){1to16}, %zmm_src2, %zmm_dst
(折叠广播-加载)。使用内联 asm 这样做的目的是 gcc 还不知道如何将 set1()
内存操作数折叠成广播负载(但 clang 知道)。
无论如何,这个具体问题是关于向量寄存器的操作数修饰符和约束。请关注这一点,但欢迎对另一个问题发表评论和回答。 (或者更好,只是评论/回答 Z Boson 关于嵌入式广播的问题。)
来自 GCC 源的文件 gcc/config/i386/i386.c:
b -- print the QImode name of the register for the indicated operand. %b0 would print %al if operands[0] is reg 0. w -- likewise, print the HImode name of the register. k -- likewise, print the SImode name of the register. q -- likewise, print the DImode name of the register. x -- likewise, print the V4SFmode name of the register. t -- likewise, print the V8SFmode name of the register. g -- likewise, print the V16SFmode name of the register. h -- print the QImode name for a "high" register, either ah, bh, ch or dh.
与gcc/config/i386/contraints.md类似:
;; We use the Y prefix to denote any number of conditional register sets: ;; z First SSE register. ;; i SSE2 inter-unit moves to SSE register enabled ;; j SSE2 inter-unit moves from SSE register enabled ;; m MMX inter-unit moves to MMX register enabled ;; n MMX inter-unit moves from MMX register enabled ;; a Integer register when zero extensions with AND are disabled ;; p Integer register when TARGET_PARTIAL_REG_STALL is disabled ;; f x87 register when 80387 floating point arithmetic is enabled ;; r SSE regs not requiring REX prefix when prefixes avoidance is enabled ;; and all SSE regs otherwise
此文件还定义了一个 "Yk" 约束,但我不知道它在 asm 语句中的效果如何:
(define_register_constraint "Yk" "TARGET_AVX512F ? MASK_EVEX_REGS : NO_REGS" "@internal Any mask register that can be used as predicate, i.e. k1-k7.")
请注意,这都是从最新的 SVN 修订版复制而来的。我不知道 GCC 的哪个版本(如果有)添加了您感兴趣的特定修饰符和约束。
似乎所有最新版本的 GCC 都接受 'q' 和 'x' 作为修饰符来打印 YMM 寄存器的 XMM 版本。
Intel 的 icc 看起来接受 'q',但不接受 'x'(至少通过版本 13.0.1)。
[编辑:好吧,它在下面的这个小例子中有效,但在一个真实的测试用例中,我遇到了 icc 14.0.3 接受 'q' 但写 'ymm' 的问题.]
[编辑:使用更新版本的 icc 进行测试,我发现 icc 15 和 icc 16 都不能与 'q' 或 'x' 一起使用。]
但 Clang 3.6 及更早版本不接受这两种语法。至少在 Godbolt 上,Clang 3.7 会同时崩溃!
// inline assembly modifiers to convert ymm to xmm
#include <x86intrin.h>
#include <stdint.h>
// gcc also accepts "%q1" as "%x1"
// icc accepts "%q1" but not "%x1"
// clang-3.6 accepts neither
// clang-3.7 crashes with both!
#define ASM_MOVD(vec, reg) \
__asm volatile("vmovd %q1, %0" : \
"=r" (reg) : \
"x" (vec) \
);
uint32_t movd_ymm(__m256i ymm) {
uint32_t low;
ASM_MOVD(ymm, low);
return low;
}
uint32_t movd_xmm(__m128i xmm) {
uint32_t low;
ASM_MOVD(xmm, low);
return low;
}
Link 在 Godbolt 上测试:http://goo.gl/bOkjNu
(很抱歉,这不是您问题的完整答案,但这似乎是值得分享的有用信息,但评论时间太长)