Open Fros1er opened 3 weeks ago
@llvm/issue-subscribers-backend-risc-v
Author: Froster (Fros1er)
Code above with clang -Ofast -march=rv64gcv -S -emit-llvm
:
Changing global_data.a[i] += global_data.b[0];
to global_data.a[i] += global_data.b[i];
:
This doesn't look specific to RISC-V.
I think its an issue with alias analysis.
yes as Craig mentioned clang doesn't even vectorize for x86 targets but gcc does. https://godbolt.org/z/q74eoxGfj
If alias analysis was working, I'd expect LICM to pull the load out of the loop.
Changing global_data.a[i] += global_data.b[0]; to global_data.a[i] += global_data.b[i];
Yeah, this is strange when clang vectorizes with global_data.b[i]
but not with global_data.b[0]
Changing global_data.a[i] += global_data.b[0]; to global_data.a[i] += global_data.b[i];
Yeah, this is strange when clang vectorizes with
global_data.b[i]
but not withglobal_data.b[0]
The vectorizer emitted a runtime check to verify the aliasing. I guess for some reason it doesn't do that for uniform loads?
May I self assign this? Although I'm not familiar with clang, it might take a while for me to understand and solve the issue.
Is there any info that allows us to prove n1 >=s 0
? If that is the case, this gets handled by applying loop guards to the SCEV expressions when trying to determine all source accesses start/end before the sink accesses.
With https://github.com/fhahn/llvm-project/commit/62235dca45533e6e5155470e79a5c18be1a21762 a variant of the reproducer here with an assumption that n1 >=s 0
(https://llvm.godbolt.org/z/bT57M8nnY) can vectorized without runtime checks
Even when the struct is passed as a parameter, the vectorization doesn't happen. https://godbolt.org/z/q5r6dboK3
Seems b[0]
is also not the point.
#define LEN 32000
struct GlobalData {
float a[LEN];
float b;
};
int s122(int n1, struct GlobalData &global_data) {
for (int i = n1; i < LEN; i++) {
global_data.a[i] += global_data.b;
}
return n1;
}
Seems b[0] is also not the point.
Yes that doesn't matter, in both cases we should prove that the loads from global_data.b | global_data.b[0]
don't overlap with the stores to a[I]
.
Even when the struct is passed as a parameter, the vectorization doesn't happen. https://godbolt.org/z/q5r6dboK3 Don't think passing it as parameter changes things, if anything it would make reasoning a bit harder, if there are other accesses.
Hmm, looking at https://github.com/llvm/llvm-test-suite/blob/main/MultiSource/Benchmarks/TSVC/tsc.inc#L1260 which I think should be s122, it seems like there's no info the determine that n1 >=s 0
This doesn't look specific to RISC-V.
I think its an issue with alias analysis.
Index.Val.V here is phi n1, i
. As we don't know about n1 here, CR is (INT_MIN, INT_MAX).
Maybe we can check GEP1->getSourceElementType()->isArrayTy()
here and use array size to narrow CR?
cc: @nikic and @efriedma-quic for help with this.
Trying to use the array bounds from the C source code isn't possible given the way the LLVM IR is currently emitted. Some people have looked at encoding more information about arrays into IR, but it's extremely complicated.
It should be possible for LoopAccessAnalysis to prove the lack of aliasing, just based on the indexing. If i is nonnegative, i < LEN
implies &global_data.a[i] < &global_data.a[LEN]
. If i is negative, &global_data.a[i]
doesn't point to global_data
.
Trying to use the array bounds from the C source code isn't possible given the way the LLVM IR is currently emitted. Some people have looked at encoding more information about arrays into IR, but it's extremely complicated.
It should be possible for LoopAccessAnalysis to prove the lack of aliasing, just based on the indexing. If i is nonnegative,
i < LEN
implies&global_data.a[i] < &global_data.a[LEN]
. If i is negative,&global_data.a[i]
doesn't point toglobal_data
.
What about -scev-aa? I don't know how to enable it though. LoopAccessAnalysis doesn't have interface for this problem and it's hard to write one as it is coupled heavily with vectorization.
scev-aa is a thin wrapper around SCEV; you should be able to just use SCEV directly.
Found this in tsvc benchmark. It's quite diifferent from original code after I removed all unrelated parts.
clang -Ofast -march=rv64gcv
:RVV code will generate if we change:
global_data.a[i] += global_data.b[0];
toglobal_data.a[i] += global_data.b[i];
int i = n1
toint i = 0