| ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6 | |
| ; RUN: opt -mtriple=amdgpu-amd-amdhsa -aa-pipeline=basic-aa,scoped-noalias-aa -passes=load-store-vectorizer -S -o - %s | FileCheck %s | |
| ; Test that getModRefInfo(FenceInst, Loc) uses scoped noalias metadata on the | |
| ; fence to prove it cannot affect a given memory location. Without this, the | |
| ; load-store vectorizer conservatively treats fences as potential clobbers, | |
| ; preventing vectorization of adjacent loads across fences. | |
| ; | |
| ; This models what happens after AMDGPULowerKernelArguments: noalias kernel | |
| ; pointer arguments are replaced with loads from the kernarg segment, losing | |
| ; the readonly attribute. The !alias.scope/!noalias metadata partially | |
| ; compensates, but getModRefInfo(FenceInst) did not check it. | |
| ; Positive: fence declares !noalias for the load's scope -> vectorization. | |
| define void @vectorize_loads_across_fence_with_noalias(ptr addrspace(1) %ptr) { | |
| ; CHECK-LABEL: define void @vectorize_loads_across_fence_with_noalias( | |
| ; CHECK-SAME: ptr addrspace(1) [[PTR:%.*]]) { | |
| ; CHECK-NEXT: [[TMP1:%.*]] = load <2 x i32>, ptr addrspace(1) [[PTR]], align 8, !alias.scope [[META0:![0-9]+]], !noalias [[META3:![0-9]+]] | |
| ; CHECK-NEXT: [[LOAD01:%.*]] = extractelement <2 x i32> [[TMP1]], i64 0 | |
| ; CHECK-NEXT: [[LOAD12:%.*]] = extractelement <2 x i32> [[TMP1]], i64 1 | |
| ; CHECK-NEXT: fence syncscope("workgroup") release, !noalias [[META5:![0-9]+]] | |
| ; CHECK-NEXT: fence syncscope("workgroup") acquire, !noalias [[META5]] | |
| ; CHECK-NEXT: [[SUM:%.*]] = add i32 [[LOAD01]], [[LOAD12]] | |
| ; CHECK-NEXT: call void @use(i32 [[SUM]]) | |
| ; CHECK-NEXT: ret void | |
| ; | |
| %gep1 = getelementptr i32, ptr addrspace(1) %ptr, i64 1 | |
| %load0 = load i32, ptr addrspace(1) %ptr, align 8, !alias.scope !0, !noalias !3 | |
| fence syncscope("workgroup") release, !noalias !5 | |
| fence syncscope("workgroup") acquire, !noalias !5 | |
| %load1 = load i32, ptr addrspace(1) %gep1, align 4, !alias.scope !0, !noalias !3 | |
| %sum = add i32 %load0, %load1 | |
| call void @use(i32 %sum) | |
| ret void | |
| } | |
| ; Positive: fence declares !alias.scope, load declares !noalias covering that | |
| ; scope. Exercises the symmetric branch in ScopedNoAliasAA::getModRefInfo. | |
| define void @vectorize_loads_across_fence_with_alias_scope(ptr addrspace(1) %ptr) { | |
| ; CHECK-LABEL: define void @vectorize_loads_across_fence_with_alias_scope( | |
| ; CHECK-SAME: ptr addrspace(1) [[PTR:%.*]]) { | |
| ; CHECK-NEXT: [[TMP1:%.*]] = load <2 x i32>, ptr addrspace(1) [[PTR]], align 8, !alias.scope [[META0]], !noalias [[META6:![0-9]+]] | |
| ; CHECK-NEXT: [[LOAD01:%.*]] = extractelement <2 x i32> [[TMP1]], i64 0 | |
| ; CHECK-NEXT: [[LOAD12:%.*]] = extractelement <2 x i32> [[TMP1]], i64 1 | |
| ; CHECK-NEXT: fence syncscope("workgroup") release, !alias.scope [[META6]] | |
| ; CHECK-NEXT: fence syncscope("workgroup") acquire, !alias.scope [[META6]] | |
| ; CHECK-NEXT: [[SUM:%.*]] = add i32 [[LOAD01]], [[LOAD12]] | |
| ; CHECK-NEXT: call void @use(i32 [[SUM]]) | |
| ; CHECK-NEXT: ret void | |
| ; | |
| %gep1 = getelementptr i32, ptr addrspace(1) %ptr, i64 1 | |
| %load0 = load i32, ptr addrspace(1) %ptr, align 8, !alias.scope !0, !noalias !7 | |
| fence syncscope("workgroup") release, !alias.scope !7 | |
| fence syncscope("workgroup") acquire, !alias.scope !7 | |
| %load1 = load i32, ptr addrspace(1) %gep1, align 4, !alias.scope !0, !noalias !7 | |
| %sum = add i32 %load0, %load1 | |
| call void @use(i32 %sum) | |
| ret void | |
| } | |
| ; Negative: no metadata on fence at all -> no vectorization. | |
| define void @no_vectorize_loads_across_fence_without_noalias(ptr addrspace(1) %ptr) { | |
| ; CHECK-LABEL: define void @no_vectorize_loads_across_fence_without_noalias( | |
| ; CHECK-SAME: ptr addrspace(1) [[PTR:%.*]]) { | |
| ; CHECK-NEXT: [[GEP1:%.*]] = getelementptr i32, ptr addrspace(1) [[PTR]], i64 1 | |
| ; CHECK-NEXT: [[LOAD0:%.*]] = load i32, ptr addrspace(1) [[PTR]], align 8, !alias.scope [[META0]], !noalias [[META3]] | |
| ; CHECK-NEXT: fence syncscope("workgroup") release | |
| ; CHECK-NEXT: fence syncscope("workgroup") acquire | |
| ; CHECK-NEXT: [[LOAD1:%.*]] = load i32, ptr addrspace(1) [[GEP1]], align 4, !alias.scope [[META0]], !noalias [[META3]] | |
| ; CHECK-NEXT: [[SUM:%.*]] = add i32 [[LOAD0]], [[LOAD1]] | |
| ; CHECK-NEXT: call void @use(i32 [[SUM]]) | |
| ; CHECK-NEXT: ret void | |
| ; | |
| %gep1 = getelementptr i32, ptr addrspace(1) %ptr, i64 1 | |
| %load0 = load i32, ptr addrspace(1) %ptr, align 8, !alias.scope !0, !noalias !3 | |
| fence syncscope("workgroup") release | |
| fence syncscope("workgroup") acquire | |
| %load1 = load i32, ptr addrspace(1) %gep1, align 4, !alias.scope !0, !noalias !3 | |
| %sum = add i32 %load0, %load1 | |
| call void @use(i32 %sum) | |
| ret void | |
| } | |
| ; Negative: fence has !noalias but only for a different scope (other_arg_scope), | |
| ; not the load's scope (arg_scope). Partial coverage does not help. | |
| define void @no_vectorize_loads_across_fence_with_partial_noalias(ptr addrspace(1) %ptr) { | |
| ; CHECK-LABEL: define void @no_vectorize_loads_across_fence_with_partial_noalias( | |
| ; CHECK-SAME: ptr addrspace(1) [[PTR:%.*]]) { | |
| ; CHECK-NEXT: [[GEP1:%.*]] = getelementptr i32, ptr addrspace(1) [[PTR]], i64 1 | |
| ; CHECK-NEXT: [[LOAD0:%.*]] = load i32, ptr addrspace(1) [[PTR]], align 8, !alias.scope [[META0]], !noalias [[META3]] | |
| ; CHECK-NEXT: fence syncscope("workgroup") release, !noalias [[META3]] | |
| ; CHECK-NEXT: fence syncscope("workgroup") acquire, !noalias [[META3]] | |
| ; CHECK-NEXT: [[LOAD1:%.*]] = load i32, ptr addrspace(1) [[GEP1]], align 4, !alias.scope [[META0]], !noalias [[META3]] | |
| ; CHECK-NEXT: [[SUM:%.*]] = add i32 [[LOAD0]], [[LOAD1]] | |
| ; CHECK-NEXT: call void @use(i32 [[SUM]]) | |
| ; CHECK-NEXT: ret void | |
| ; | |
| %gep1 = getelementptr i32, ptr addrspace(1) %ptr, i64 1 | |
| %load0 = load i32, ptr addrspace(1) %ptr, align 8, !alias.scope !0, !noalias !3 | |
| fence syncscope("workgroup") release, !noalias !3 | |
| fence syncscope("workgroup") acquire, !noalias !3 | |
| %load1 = load i32, ptr addrspace(1) %gep1, align 4, !alias.scope !0, !noalias !3 | |
| %sum = add i32 %load0, %load1 | |
| call void @use(i32 %sum) | |
| ret void | |
| } | |
| declare void @use(i32) | |
| ; Metadata: three noalias scopes in the same domain. | |
| !0 = !{!1} | |
| !1 = distinct !{!1, !2, !"arg_scope"} | |
| !2 = distinct !{!2, i1 false, !"kernel_domain"} | |
| !3 = !{!4} | |
| !4 = distinct !{!4, !2, !"other_arg_scope"} | |
| !5 = !{!1, !4} | |
| !6 = distinct !{!6, !2, !"fence_sync_scope"} | |
| !7 = !{!6} | |
| ;. | |
| ; CHECK: [[META0]] = !{[[META1:![0-9]+]]} | |
| ; CHECK: [[META1]] = distinct !{[[META1]], [[META2:![0-9]+]], !"arg_scope"} | |
| ; CHECK: [[META2]] = distinct !{[[META2]], i1 false, !"kernel_domain"} | |
| ; CHECK: [[META3]] = !{[[META4:![0-9]+]]} | |
| ; CHECK: [[META4]] = distinct !{[[META4]], [[META2]], !"other_arg_scope"} | |
| ; CHECK: [[META5]] = !{[[META1]], [[META4]]} | |
| ; CHECK: [[META6]] = !{[[META7:![0-9]+]]} | |
| ; CHECK: [[META7]] = distinct !{[[META7]], [[META2]], !"fence_sync_scope"} | |
| ;. |