在尝试回答Embedded broadcasts with intrinsics and assembly时,我试图做这样的事情:
__m512 mul_broad(__m512 a, float b) {
int scratch = 0;
asm(
"vbroadcastss %k[scalar], %q[scalar]\n\t" // want vbr.. %xmm0, %zmm0
"vmulps %q[scalar], %[vec], %[vec]\n\t"
// how it's done for integer registers
"movw symbol(%q[inttmp]), %w[inttmp]\n\t" // movw symbol(%rax), %ax
"movsbl %h[inttmp], %k[inttmp]\n\t" // movsx %ah, %eax
: [vec] "+x" (a), [scalar] "+x" (b), [inttmp] "=r" (scratch)
:
:
);
return a;
}
GNU C x86 Operand Modifiers doc仅指定最多q
的修饰符(DI(DoubleInt)大小,64位)。在向量寄存器上使用q
将始终将其降至xmm
(来自ymm
或zmm
)。
在矢量寄存器的大小之间有哪些修饰符可以改变?
此外,是否有任何特定大小的约束用于输入或输出操作数?除通用x
以外的其他东西,最终可能是xmm,ymm或zmm,具体取决于放在括号中的表达式的类型。
题外话:
clang似乎有一些Yi
/ Yt
约束(不是修饰符),但我也找不到文档。即使注释了向量指令,clang也不会编译它,因为它不喜欢+x
作为__m512
向量的约束。
我可以通过将标量作为输入操作数传递给我想要的结果,被限制为与更宽的输出操作数在同一个寄存器中,但它更笨拙。 (这个用例的最大缺点是AFAIK必须使用操作数 - 而不是[symbolic_name]
,所以在添加/删除输出约束时它很容易破损。)
// does what I want, by using a paired output and input constraint
__m512 mul_broad(__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关于嵌入式广播的问题发表评论/回答。)
答案 0 :(得分:8)
来自海湾合作委员会来源的档案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的版本,如果有的话,你感兴趣的特定修饰符和约束被添加。
答案 1 :(得分:2)
似乎所有最近版本的海湾合作委员会都会同时接受' q'和' x'作为修改器来打印YMM寄存器的XMM版本。
英特尔的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;
}
关于Godbolt测试的链接:http://goo.gl/bOkjNu
(很抱歉,这不是对你的问题的完整答案,但它似乎是有用的信息分享,而且评论太长了)