; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5 ; RUN: llc < %s -march=nvptx64 -mcpu=sm_90 -mattr=+ptx88 | FileCheck %s -check-prefixes=CHECK,SM90 ; RUN: %if ptxas-sm_90 && ptxas-isa-8.8 %{ llc < %s -march=nvptx64 -mcpu=sm_90 -mattr=+ptx88 | %ptxas-verify -arch=sm_90 %} ; RUN: llc < %s -march=nvptx64 -mcpu=sm_100 -mattr=+ptx88 | FileCheck %s -check-prefixes=CHECK,SM100 ; RUN: %if ptxas-sm_100 && ptxas-isa-8.8 %{ llc < %s -march=nvptx64 -mcpu=sm_100 -mattr=+ptx88 | %ptxas-verify -arch=sm_100 %} ; This test is based on load-store-vectors.ll, ; and contains testing for lowering 256-bit masked vector stores ; Types we are checking: i32, i64, f32, f64 ; Address spaces we are checking: generic, global ; - Global is the only address space that currently supports masked stores. ; - The generic stores will get legalized before the backend via scalarization, ; this file tests that even though we won't be generating them in the LSV. ; 256-bit vector loads/stores are only legal for blackwell+, so on sm_90, the vectors will be split ; generic address space define void @generic_8xi32(ptr %a, ptr %b) { ; CHECK-LABEL: generic_8xi32( ; CHECK: { ; CHECK-NEXT: .reg .b32 %r<9>; ; CHECK-NEXT: .reg .b64 %rd<3>; ; CHECK-EMPTY: ; CHECK-NEXT: // %bb.0: ; CHECK-NEXT: ld.param.b64 %rd1, [generic_8xi32_param_0]; ; CHECK-NEXT: ld.v4.b32 {%r1, %r2, %r3, %r4}, [%rd1+16]; ; CHECK-NEXT: ld.v4.b32 {%r5, %r6, %r7, %r8}, [%rd1]; ; CHECK-NEXT: ld.param.b64 %rd2, [generic_8xi32_param_1]; ; CHECK-NEXT: st.b32 [%rd2], %r5; ; CHECK-NEXT: st.b32 [%rd2+8], %r7; ; CHECK-NEXT: st.b32 [%rd2+28], %r4; ; CHECK-NEXT: ret; %a.load = load <8 x i32>, ptr %a tail call void @llvm.masked.store.v8i32.p0(<8 x i32> %a.load, ptr align 32 %b, <8 x i1> ) ret void } define void @generic_4xi64(ptr %a, ptr %b) { ; CHECK-LABEL: generic_4xi64( ; CHECK: { ; CHECK-NEXT: .reg .b64 %rd<7>; ; CHECK-EMPTY: ; CHECK-NEXT: // %bb.0: ; CHECK-NEXT: ld.param.b64 %rd1, [generic_4xi64_param_0]; ; CHECK-NEXT: ld.v2.b64 {%rd2, %rd3}, [%rd1+16]; ; CHECK-NEXT: ld.v2.b64 {%rd4, %rd5}, [%rd1]; ; CHECK-NEXT: ld.param.b64 %rd6, [generic_4xi64_param_1]; ; CHECK-NEXT: st.b64 [%rd6], %rd4; ; CHECK-NEXT: st.b64 [%rd6+16], %rd2; ; CHECK-NEXT: ret; %a.load = load <4 x i64>, ptr %a tail call void @llvm.masked.store.v4i64.p0(<4 x i64> %a.load, ptr align 32 %b, <4 x i1> ) ret void } define void @generic_8xfloat(ptr %a, ptr %b) { ; CHECK-LABEL: generic_8xfloat( ; CHECK: { ; CHECK-NEXT: .reg .b32 %r<9>; ; CHECK-NEXT: .reg .b64 %rd<3>; ; CHECK-EMPTY: ; CHECK-NEXT: // %bb.0: ; CHECK-NEXT: ld.param.b64 %rd1, [generic_8xfloat_param_0]; ; CHECK-NEXT: ld.v4.b32 {%r1, %r2, %r3, %r4}, [%rd1+16]; ; CHECK-NEXT: ld.v4.b32 {%r5, %r6, %r7, %r8}, [%rd1]; ; CHECK-NEXT: ld.param.b64 %rd2, [generic_8xfloat_param_1]; ; CHECK-NEXT: st.b32 [%rd2], %r5; ; CHECK-NEXT: st.b32 [%rd2+8], %r7; ; CHECK-NEXT: st.b32 [%rd2+28], %r4; ; CHECK-NEXT: ret; %a.load = load <8 x float>, ptr %a tail call void @llvm.masked.store.v8f32.p0(<8 x float> %a.load, ptr align 32 %b, <8 x i1> ) ret void } define void @generic_4xdouble(ptr %a, ptr %b) { ; CHECK-LABEL: generic_4xdouble( ; CHECK: { ; CHECK-NEXT: .reg .b64 %rd<7>; ; CHECK-EMPTY: ; CHECK-NEXT: // %bb.0: ; CHECK-NEXT: ld.param.b64 %rd1, [generic_4xdouble_param_0]; ; CHECK-NEXT: ld.v2.b64 {%rd2, %rd3}, [%rd1+16]; ; CHECK-NEXT: ld.v2.b64 {%rd4, %rd5}, [%rd1]; ; CHECK-NEXT: ld.param.b64 %rd6, [generic_4xdouble_param_1]; ; CHECK-NEXT: st.b64 [%rd6], %rd4; ; CHECK-NEXT: st.b64 [%rd6+16], %rd2; ; CHECK-NEXT: ret; %a.load = load <4 x double>, ptr %a tail call void @llvm.masked.store.v4f64.p0(<4 x double> %a.load, ptr align 32 %b, <4 x i1> ) ret void } ; global address space define void @global_8xi32(ptr addrspace(1) %a, ptr addrspace(1) %b) { ; SM90-LABEL: global_8xi32( ; SM90: { ; SM90-NEXT: .reg .b32 %r<9>; ; SM90-NEXT: .reg .b64 %rd<3>; ; SM90-EMPTY: ; SM90-NEXT: // %bb.0: ; SM90-NEXT: ld.param.b64 %rd1, [global_8xi32_param_0]; ; SM90-NEXT: ld.global.v4.b32 {%r1, %r2, %r3, %r4}, [%rd1+16]; ; SM90-NEXT: ld.global.v4.b32 {%r5, %r6, %r7, %r8}, [%rd1]; ; SM90-NEXT: ld.param.b64 %rd2, [global_8xi32_param_1]; ; SM90-NEXT: st.global.b32 [%rd2], %r5; ; SM90-NEXT: st.global.b32 [%rd2+8], %r7; ; SM90-NEXT: st.global.b32 [%rd2+28], %r4; ; SM90-NEXT: ret; ; ; SM100-LABEL: global_8xi32( ; SM100: { ; SM100-NEXT: .reg .b32 %r<9>; ; SM100-NEXT: .reg .b64 %rd<3>; ; SM100-EMPTY: ; SM100-NEXT: // %bb.0: ; SM100-NEXT: ld.param.b64 %rd1, [global_8xi32_param_0]; ; SM100-NEXT: ld.global.v8.b32 {%r1, %r2, %r3, %r4, %r5, %r6, %r7, %r8}, [%rd1]; ; SM100-NEXT: ld.param.b64 %rd2, [global_8xi32_param_1]; ; SM100-NEXT: st.global.v8.b32 [%rd2], {%r1, _, %r3, _, _, _, _, %r8}; ; SM100-NEXT: ret; %a.load = load <8 x i32>, ptr addrspace(1) %a tail call void @llvm.masked.store.v8i32.p1(<8 x i32> %a.load, ptr addrspace(1) align 32 %b, <8 x i1> ) ret void } define void @global_4xi64(ptr addrspace(1) %a, ptr addrspace(1) %b) { ; SM90-LABEL: global_4xi64( ; SM90: { ; SM90-NEXT: .reg .b64 %rd<7>; ; SM90-EMPTY: ; SM90-NEXT: // %bb.0: ; SM90-NEXT: ld.param.b64 %rd1, [global_4xi64_param_0]; ; SM90-NEXT: ld.global.v2.b64 {%rd2, %rd3}, [%rd1+16]; ; SM90-NEXT: ld.global.v2.b64 {%rd4, %rd5}, [%rd1]; ; SM90-NEXT: ld.param.b64 %rd6, [global_4xi64_param_1]; ; SM90-NEXT: st.global.b64 [%rd6], %rd4; ; SM90-NEXT: st.global.b64 [%rd6+16], %rd2; ; SM90-NEXT: ret; ; ; SM100-LABEL: global_4xi64( ; SM100: { ; SM100-NEXT: .reg .b64 %rd<7>; ; SM100-EMPTY: ; SM100-NEXT: // %bb.0: ; SM100-NEXT: ld.param.b64 %rd1, [global_4xi64_param_0]; ; SM100-NEXT: ld.global.v4.b64 {%rd2, %rd3, %rd4, %rd5}, [%rd1]; ; SM100-NEXT: ld.param.b64 %rd6, [global_4xi64_param_1]; ; SM100-NEXT: st.global.v4.b64 [%rd6], {%rd2, _, %rd4, _}; ; SM100-NEXT: ret; %a.load = load <4 x i64>, ptr addrspace(1) %a tail call void @llvm.masked.store.v4i64.p1(<4 x i64> %a.load, ptr addrspace(1) align 32 %b, <4 x i1> ) ret void } define void @global_8xfloat(ptr addrspace(1) %a, ptr addrspace(1) %b) { ; SM90-LABEL: global_8xfloat( ; SM90: { ; SM90-NEXT: .reg .b32 %r<9>; ; SM90-NEXT: .reg .b64 %rd<3>; ; SM90-EMPTY: ; SM90-NEXT: // %bb.0: ; SM90-NEXT: ld.param.b64 %rd1, [global_8xfloat_param_0]; ; SM90-NEXT: ld.global.v4.b32 {%r1, %r2, %r3, %r4}, [%rd1+16]; ; SM90-NEXT: ld.global.v4.b32 {%r5, %r6, %r7, %r8}, [%rd1]; ; SM90-NEXT: ld.param.b64 %rd2, [global_8xfloat_param_1]; ; SM90-NEXT: st.global.b32 [%rd2], %r5; ; SM90-NEXT: st.global.b32 [%rd2+8], %r7; ; SM90-NEXT: st.global.b32 [%rd2+28], %r4; ; SM90-NEXT: ret; ; ; SM100-LABEL: global_8xfloat( ; SM100: { ; SM100-NEXT: .reg .b32 %r<9>; ; SM100-NEXT: .reg .b64 %rd<3>; ; SM100-EMPTY: ; SM100-NEXT: // %bb.0: ; SM100-NEXT: ld.param.b64 %rd1, [global_8xfloat_param_0]; ; SM100-NEXT: ld.global.v8.b32 {%r1, %r2, %r3, %r4, %r5, %r6, %r7, %r8}, [%rd1]; ; SM100-NEXT: ld.param.b64 %rd2, [global_8xfloat_param_1]; ; SM100-NEXT: st.global.v8.b32 [%rd2], {%r1, _, %r3, _, _, _, _, %r8}; ; SM100-NEXT: ret; %a.load = load <8 x float>, ptr addrspace(1) %a tail call void @llvm.masked.store.v8f32.p1(<8 x float> %a.load, ptr addrspace(1) align 32 %b, <8 x i1> ) ret void } define void @global_4xdouble(ptr addrspace(1) %a, ptr addrspace(1) %b) { ; SM90-LABEL: global_4xdouble( ; SM90: { ; SM90-NEXT: .reg .b64 %rd<7>; ; SM90-EMPTY: ; SM90-NEXT: // %bb.0: ; SM90-NEXT: ld.param.b64 %rd1, [global_4xdouble_param_0]; ; SM90-NEXT: ld.global.v2.b64 {%rd2, %rd3}, [%rd1+16]; ; SM90-NEXT: ld.global.v2.b64 {%rd4, %rd5}, [%rd1]; ; SM90-NEXT: ld.param.b64 %rd6, [global_4xdouble_param_1]; ; SM90-NEXT: st.global.b64 [%rd6], %rd4; ; SM90-NEXT: st.global.b64 [%rd6+16], %rd2; ; SM90-NEXT: ret; ; ; SM100-LABEL: global_4xdouble( ; SM100: { ; SM100-NEXT: .reg .b64 %rd<7>; ; SM100-EMPTY: ; SM100-NEXT: // %bb.0: ; SM100-NEXT: ld.param.b64 %rd1, [global_4xdouble_param_0]; ; SM100-NEXT: ld.global.v4.b64 {%rd2, %rd3, %rd4, %rd5}, [%rd1]; ; SM100-NEXT: ld.param.b64 %rd6, [global_4xdouble_param_1]; ; SM100-NEXT: st.global.v4.b64 [%rd6], {%rd2, _, %rd4, _}; ; SM100-NEXT: ret; %a.load = load <4 x double>, ptr addrspace(1) %a tail call void @llvm.masked.store.v4f64.p1(<4 x double> %a.load, ptr addrspace(1) align 32 %b, <4 x i1> ) ret void } ; edge cases define void @global_8xi32_all_mask_on(ptr addrspace(1) %a, ptr addrspace(1) %b) { ; SM90-LABEL: global_8xi32_all_mask_on( ; SM90: { ; SM90-NEXT: .reg .b32 %r<9>; ; SM90-NEXT: .reg .b64 %rd<3>; ; SM90-EMPTY: ; SM90-NEXT: // %bb.0: ; SM90-NEXT: ld.param.b64 %rd1, [global_8xi32_all_mask_on_param_0]; ; SM90-NEXT: ld.global.v4.b32 {%r1, %r2, %r3, %r4}, [%rd1]; ; SM90-NEXT: ld.global.v4.b32 {%r5, %r6, %r7, %r8}, [%rd1+16]; ; SM90-NEXT: ld.param.b64 %rd2, [global_8xi32_all_mask_on_param_1]; ; SM90-NEXT: st.global.v4.b32 [%rd2+16], {%r5, %r6, %r7, %r8}; ; SM90-NEXT: st.global.v4.b32 [%rd2], {%r1, %r2, %r3, %r4}; ; SM90-NEXT: ret; ; ; SM100-LABEL: global_8xi32_all_mask_on( ; SM100: { ; SM100-NEXT: .reg .b64 %rd<7>; ; SM100-EMPTY: ; SM100-NEXT: // %bb.0: ; SM100-NEXT: ld.param.b64 %rd1, [global_8xi32_all_mask_on_param_0]; ; SM100-NEXT: ld.global.v4.b64 {%rd2, %rd3, %rd4, %rd5}, [%rd1]; ; SM100-NEXT: ld.param.b64 %rd6, [global_8xi32_all_mask_on_param_1]; ; SM100-NEXT: st.global.v4.b64 [%rd6], {%rd2, %rd3, %rd4, %rd5}; ; SM100-NEXT: ret; %a.load = load <8 x i32>, ptr addrspace(1) %a tail call void @llvm.masked.store.v8i32.p1(<8 x i32> %a.load, ptr addrspace(1) align 32 %b, <8 x i1> ) ret void } define void @global_8xi32_all_mask_off(ptr addrspace(1) %a, ptr addrspace(1) %b) { ; CHECK-LABEL: global_8xi32_all_mask_off( ; CHECK: { ; CHECK-EMPTY: ; CHECK-EMPTY: ; CHECK-NEXT: // %bb.0: ; CHECK-NEXT: ret; %a.load = load <8 x i32>, ptr addrspace(1) %a tail call void @llvm.masked.store.v8i32.p1(<8 x i32> %a.load, ptr addrspace(1) align 32 %b, <8 x i1> ) ret void } ; This is an example pattern for the LSV's output of these masked stores define void @vectorizerOutput(ptr addrspace(1) %in, ptr addrspace(1) %out) { ; SM90-LABEL: vectorizerOutput( ; SM90: { ; SM90-NEXT: .reg .b32 %r<9>; ; SM90-NEXT: .reg .b64 %rd<3>; ; SM90-EMPTY: ; SM90-NEXT: // %bb.0: ; SM90-NEXT: ld.param.b64 %rd1, [vectorizerOutput_param_0]; ; SM90-NEXT: ld.global.v4.b32 {%r1, %r2, %r3, %r4}, [%rd1+16]; ; SM90-NEXT: ld.global.v4.b32 {%r5, %r6, %r7, %r8}, [%rd1]; ; SM90-NEXT: ld.param.b64 %rd2, [vectorizerOutput_param_1]; ; SM90-NEXT: st.global.b32 [%rd2], %r5; ; SM90-NEXT: st.global.b32 [%rd2+4], %r6; ; SM90-NEXT: st.global.b32 [%rd2+12], %r8; ; SM90-NEXT: st.global.b32 [%rd2+16], %r1; ; SM90-NEXT: ret; ; ; SM100-LABEL: vectorizerOutput( ; SM100: { ; SM100-NEXT: .reg .b32 %r<9>; ; SM100-NEXT: .reg .b64 %rd<3>; ; SM100-EMPTY: ; SM100-NEXT: // %bb.0: ; SM100-NEXT: ld.param.b64 %rd1, [vectorizerOutput_param_0]; ; SM100-NEXT: ld.global.v8.b32 {%r1, %r2, %r3, %r4, %r5, %r6, %r7, %r8}, [%rd1]; ; SM100-NEXT: ld.param.b64 %rd2, [vectorizerOutput_param_1]; ; SM100-NEXT: st.global.v8.b32 [%rd2], {%r1, %r2, _, %r4, %r5, _, _, _}; ; SM100-NEXT: ret; %1 = load <8 x i32>, ptr addrspace(1) %in, align 32 %load05 = extractelement <8 x i32> %1, i32 0 %load16 = extractelement <8 x i32> %1, i32 1 %load38 = extractelement <8 x i32> %1, i32 3 %load49 = extractelement <8 x i32> %1, i32 4 %2 = insertelement <8 x i32> poison, i32 %load05, i32 0 %3 = insertelement <8 x i32> %2, i32 %load16, i32 1 %4 = insertelement <8 x i32> %3, i32 poison, i32 2 %5 = insertelement <8 x i32> %4, i32 %load38, i32 3 %6 = insertelement <8 x i32> %5, i32 %load49, i32 4 %7 = insertelement <8 x i32> %6, i32 poison, i32 5 %8 = insertelement <8 x i32> %7, i32 poison, i32 6 %9 = insertelement <8 x i32> %8, i32 poison, i32 7 call void @llvm.masked.store.v8i32.p1(<8 x i32> %9, ptr addrspace(1) align 32 %out, <8 x i1> ) ret void } declare void @llvm.masked.store.v8i32.p0(<8 x i32>, ptr, <8 x i1>) declare void @llvm.masked.store.v4i64.p0(<4 x i64>, ptr, <4 x i1>) declare void @llvm.masked.store.v8f32.p0(<8 x float>, ptr, <8 x i1>) declare void @llvm.masked.store.v4f64.p0(<4 x double>, ptr, <4 x i1>) declare void @llvm.masked.store.v8i32.p1(<8 x i32>, ptr addrspace(1), <8 x i1>) declare void @llvm.masked.store.v4i64.p1(<4 x i64>, ptr addrspace(1), <4 x i1>) declare void @llvm.masked.store.v8f32.p1(<8 x float>, ptr addrspace(1), <8 x i1>) declare void @llvm.masked.store.v4f64.p1(<4 x double>, ptr addrspace(1), <4 x i1>)