为什么clang不用memmove替换这个循环
考虑这个 memcpy 类似的功能:
void copy(unsigned *restrict const dst, unsigned const *restrict const src, unsigned long n)
{
for (unsigned long x = 0; x < n; ++x)
{
dst[x] = src[x];
}
}
demo
这段代码很好地优化到了 memcpy:
copy:
cbz x2, .L1
lsl x2, x2, 2
b memcpy
.L1:
ret
但是,当我删除时restrict,会clang应用循环矢量化并且不会将其替换为memmove. 这是为什么?
我尝试在启用优化报告的情况下编译它:
clang-10 main.c -c -O3 -fsave-optimization-record -S && cat ./main.opt.yaml
这就是我得到的restrict:
--- !Passed
Pass: loop-idiom
Name: ProcessLoopStoreOfLoopLoad
DebugLoc: { File: main.c, Line: 4, Column: 12 }
Function: copy
Args:
- String: 'Formed a call to '
- NewFunction: llvm.memcpy.p0i8.p0i8.i64
- String: '() function'
...
并且没有restrict:
--- !Passed
Pass: loop-vectorize
Name: Vectorized
DebugLoc: { File: main.c, Line: 3, Column: 3 }
Function: copy
Args:
- String: 'vectorized loop (vectorization width: '
- VectorizationFactor: '4'
- String: ', interleaved count: '
- InterleaveCount: '2'
- String: ')'
...
优化器直接跳过循环矢量化ProcessLoopStoreOfLoopLoad,不打印任何消息。这是为什么?为什么不能用 替换此代码memmove?
回答
这是关于在数组之间发生碰撞时操作的可观察效果。
例如:
1 2 3 4
如果 src 指向 1 并且 dst 指向 2 结果应该是
1 1 1 1
另一方面,Memmove 会在重叠的情况下执行以下操作:
内存区域可能会重叠:复制发生时,就好像 src 中的字节首先被复制到不与 src 或 dest 重叠的临时数组中,然后将字节从临时数组复制到 dest。
即这种复制的结果将是:
1 1 2 3
与原始代码有何显着不同。
另外,如果你写这个 memmove 类似的代码:
#include <stdlib.h>
void copy(unsigned *const dst, unsigned const *const src, unsigned long n)
{
unsigned *tmp = malloc(n * sizeof(*tmp));
for (unsigned long x = 0; x < n; ++x)
{
tmp[x] = src[x];
}
for (unsigned long x = 0; x < n; ++x)
{
dst[x] = tmp[x];
}
free(tmp);
}
clang很好地将其替换为memmove:
copy: # @copy
testq %rdx, %rdx
je .LBB0_2
pushq %rax
shlq $2, %rdx
callq memmove@PLT
addq $8, %rsp
.LBB0_2:
retq
demo