4 ms·
Consider the simple loop void f(int *a, const int *b, unsigned size) { for (unsigned i = 0; i < size; ++i) { a[i] = b[i] * 2; } } This is th
by cwzwarich 12y ago
Consider the simple loop
void f(int *a, const int *b, unsigned size) {
for (unsigned i = 0; i < size; ++i) {
a[i] = b[i] * 2;
}
}
This is the LLVM IR generated by `clang -Ofast -c -o - -emit-llvm -S`:
https://gist.github.com/zwarich/21cf3a0b2387302ea48b https://gist.github.com/zwarich/21cf3a0b2387302ea48b
Notice the `vector.memcheck` block, which is doing a dynamic aliasing check to decide whether to use the vectorized code. If I add `restrict`, e.g.
void f(int *restrict a, const int *b, unsigned size) {
for (unsigned i = 0; i < size; ++i) {
a[i] = b[i] * 2;
}
}
then that dynamic check goes away:
https://gist.github.com/zwarich/48fd374becf5f891cb5a https://gist.github.com/zwarich/48fd374becf5f891cb5a
However, if I put the vectorized loop in a function that gets inlined, e.g.
static void f(int *restrict a, const int *b, unsigned size) {
for (unsigned i = 0; i < size; ++i) {
a[i] = b[i] * 2;
}
}
void g(int *a, int *b, unsigned size) {
f(a, b, size);
}
the dynamic aliasing check reappears:
https://gist.github.com/zwarich/3e75f17de835dd9064ee https://gist.github.com/zwarich/3e75f17de835dd9064ee
This is because LLVM's loop vectorizer runs after inlining and the standard optimization passes (which are run in a bottom-up traversal of the SCCs of the callgraph, interleaved with inlining). The `noalias` attribute on function parameters, which is what represents `restrict` in LLVM IR, disappears upon inlining. Many other compilers do something similar, although some like IBM's XLC can preserve `restrict` upon inlining.