| ; 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, !"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]], !"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"} |
| ;. |