gpt4 book ai didi

gcc - 使用 gcc 使用 YMM 指令添加数组

转载 作者:行者123 更新时间:2023-12-02 09:09:28 26 4
gpt4 key购买 nike

我想在 gcc(AT&T 语法)中运行以下代码(Intel 语法)。

; float a[128], b[128], c[128];
; for (int i = 0; i < 128; i++) a[i] = b[i] + c[i];
; Assume that a, b and c are aligned by 32
xor ecx, ecx ; Loop counter i = 0
L: vmovaps ymm0, [b+rcx] ; Load 8 elements from b
vaddps ymm0,ymm0,[c+rcx] ; Add 8 elements from c
vmovaps [a+rcx], ymm0 ; Store result in a
add ecx,32 ; 8 elements * 4 bytes = 32
cmp ecx, 512 ; 128 elements * 4 bytes = 512
jb L ;Loop

代码来自Optimizing subroutines in assembly language .

到目前为止我编写的代码是:

static inline void addArray(float *a, float *b, float *c) {
__asm__ __volatile__ (
"nop \n"
"xor %%ecx, %%ecx \n" //;Loop counter set to 0
"loop: \n\t"
"vmovaps %1, %%ymm0 \n" //;Load 8 elements from b <== WRONG
"vaddps %2, %%ymm0, %%ymm0 \n" //;Add 8 elements from c <==WRONG
"vmovaps %%ymm0, %0 \n" //;Store result in a
"add 0x20, %%ecx \n" //;8 elemtns * 4 bytes = 32 (0x20)
"cmp 0x200,%%ecx \n" //;128 elements * 4 bytes = 512 (0x200)
"jb loop \n" //;Loop"
"nop \n"
: "=m"(a) //Outputs
: "m"(b), "m"(c) //Inputs
: "%ecx","%ymm0" //Modifies ECX and YMM0
);
}

标记为“错误”的行生成:(除了 gdb 反汇编)

0x0000000000000b78 <+19>:   vmovaps -0x10(%rbp),%ymm0
0x0000000000000b7d <+24>: vaddps -0x18(%rbp),%ymm0,%ymm0

我想要得到这样的东西(我猜):

vmovaps -0x10(%rbp,%ecx,%0x8),%ymm0

但我不知道如何指定 %ecx 作为我的索引寄存器。

你能帮我一下吗?

编辑

我已经尝试过(%1,%%ecx):

__asm__ __volatile__ (
"nop \n"
"xor %%ecx, %%ecx \n" //;Loop counter set to 0
"loop: \n\t"
"vmovaps (%1, %%rcx), %%ymm0 \n" //;Load 8 elements from b <== MODIFIED HERE
"vaddps %2, %%ymm0, %%ymm0 \n" //;Add 8 elements from c
"vmovaps %%ymm0, %0 \n" //;Store result in a
"add 0x20, %%ecx \n" //;8 elemtns * 4 bytes = 32 (0x20)
"cmp 0x200,%%ecx \n" //;128 elements * 4 bytes = 512 (0x200)
"jb loop \n" //;Loop"
"nop \n"
: "=m"(a) //Outputs
: "m"(b), "m"(c) //Inputs
: "%ecx","%ymm0" //Modifies ECX and YMM0
);

我得到了:

inline1.cpp: Assembler messages:
inline1.cpp:90: Error: found '(', expected: ')'
inline1.cpp:90: Error: junk `(%rbp),%rcx)' after expression

最佳答案

我认为不可能将其字面翻译为 GAS 内联汇编。在AT&T语法中,语法为:

displacement(base register, offset register, scalar multiplier)

这会产生类似于:

movl  -4(%ebp, %ecx, 4), %eax

或者根据您的情况:

vmovaps  -16(%rsp, %ecx, 0), %ymm0

问题是,当您使用内存约束 (m) 时,无论您在何处编写 %n (其中 n 是输入/输出的数量):

-16(%rsp)

没有办法将上面的内容操纵成你真正想要的形式。你可以写:

(%1, %%rcx)

但这会产生:

(-16(%rsp),%rcx)

这显然是错误的。无法获取这些括号内的偏移寄存器(它所属的位置),因为 %n 正在发出整个 -16(%rsp)作为一个 block 。

当然,这并不是真正的问题,因为您编写内联汇编是为了获得速度,并且从内存加载并没有什么速度。您应该将输入放在寄存器中,并且当您对输入/输出 (r) 使用寄存器约束时,就不会出现问题。请注意,这将需要稍微修改您的代码

内联汇编的其他问题包括:

  1. 数字文字以 $ 开头。
  2. 指令应具有大小后缀,例如 32 位的 l 和 64 位的 q
  3. 当您通过 a 写入时,您会破坏内存,因此您应该有一个内存破坏器。
  4. 开头和结尾的 nop 指令完全没有意义。他们甚至没有调整分支目标。
  5. 除了换行符 (\n) 之外,每一行实际上都应该以制表符 (\t) 结尾,以便在您检查拆卸情况。

这是我的代码版本:

void addArray(float *a, float *b, float *c) {
__asm__ __volatile__ (
"xorl %%ecx, %%ecx \n\t" // Loop counter set to 0
"loop: \n\t"
"vmovaps (%1,%%rcx), %%ymm0 \n\t" // Load 8 elements from b
"vaddps (%2,%%rcx), %%ymm0, %%ymm0 \n\t" // Add 8 elements from c
"vmovaps %%ymm0, (%0,%%rcx) \n\t" // Store result in a
"addl $0x20, %%ecx \n\t" // 8 elemtns * 4 bytes = 32 (0x20)
"cmpl $0x200, %%ecx \n\t" // 128 elements * 4 bytes = 512 (0x200)
"jb loop" // Loop"
: // Outputs
: "r" (a), "r" (b), "r" (c) // Inputs
: "%ecx", "%ymm0", "memory" // Modifies ECX, YMM0, and memory
);
}

这会导致编译器发出以下内容:

addArray(float*, float*, float*):
xorl %ecx, %ecx
loop:
vmovaps (%rsi,%rcx), %ymm0 # b
vaddps (%rdx,%rcx), %ymm0, %ymm0 # c
vmovaps %ymm0, (%rdi,%rcx) # a
addl $0x20, %ecx
cmpl $0x200, %ecx
jb loop
vzeroupper
retq

或者,采用更熟悉的英特尔语法:

addArray(float*, float*, float*):
xor ecx, ecx
loop:
vmovaps ymm0, YMMWORD PTR [rsi + rcx]
vaddps ymm0, ymm0, YMMWORD PTR [rdx + rcx]
vmovaps YMMWORD PTR [rdi + rcx], ymm0
add ecx, 32
cmp ecx, 512
jb loop
vzeroupper
ret

在 System V 64 位调用约定中,前三个参数在 rdirsirdx 寄存器中传递,因此代码不需要将参数移动到寄存器中——它们已经在那里了。

但是您没有充分利用输入/输出约束。您不需要将 rcx 用作计数器。您也不需要使用 ymm0 作为暂存寄存器。如果让编译器选择要使用的空闲寄存器,将使代码更加高效。您也不需要提供明确的破坏者列表:

#include <stdint.h>
#include <x86intrin.h>

void addArray(float *a, float *b, float *c) {
uint64_t temp = 0;
__m256 ymm;
__asm__ __volatile__(
"loop: \n\t"
"vmovaps (%3,%0), %1 \n\t" // Load 8 elements from b
"vaddps (%4,%0), %1, %1 \n\t" // Add 8 elements from c
"vmovaps %1, (%2,%0) \n\t" // Store result in a
"addl $0x20, %0 \n\t" // 8 elemtns * 4 bytes = 32 (0x20)
"cmpl $0x200, %0 \n\t" // 128 elements * 4 bytes = 512 (0x200)
"jb loop" // Loop
: "+r" (temp), "=x" (ymm)
: "r" (a), "r" (b), "r" (c)
: "memory"
);
}

当然,正如评论中提到的,整个练习是浪费时间。 GAS 风格的内联汇编虽然功能强大,但正确编写极其非常困难(我什至不能 100% 肯定我的代码是正确的!),因此您不应该使用内联汇编编写任何内容你绝对不必这样做。 当然不是您必须这样做的情况 - 编译器会自动优化加法循环:

void addArray(float *a, float *b, float *c) {
for (int i = 0; i < 128; i++) a[i] = b[i] + c[i];
}

使用 -O2-mavx2,GCC 将其编译为以下内容:

addArray(float*, float*, float*):
xor eax, eax
.L2:
vmovss xmm0, DWORD PTR [rsi+rax]
vaddss xmm0, xmm0, DWORD PTR [rdx+rax]
vmovss DWORD PTR [rdi+rax], xmm0
add rax, 4
cmp rax, 512
jne .L2
rep ret

嗯,这看起来非常熟悉,不是吗?公平地说,它不像您的代码那样进行矢量化。您可以使用 -O3-ftree-vectorize 获得该值,但您还可以获得 a lot more code generated ,所以我需要一个基准来说服我,它实际上更快并且值得代码大小的爆炸式增长。但其中大部分是为了处理输入未对齐的情况 - if you indicate that it is aligned and that the pointer is restricted, that solves these problems and improves the code generation substantially 。请注意,它完全展开循环,并对加法进行矢量化。

关于gcc - 使用 gcc 使用 YMM 指令添加数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41921508/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com