| ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6 | |
| ; RUN: opt -S -mtriple=amdgcn-amd-amdhsa -mcpu=gfx900 -passes=slp-vectorizer < %s | FileCheck -check-prefixes=NOPACKED %s | |
| ; RUN: opt -S -mtriple=amdgcn-amd-amdhsa -mcpu=gfx90a -passes=slp-vectorizer < %s | FileCheck -check-prefixes=GFX90A %s | |
| ; RUN: opt -S -mtriple=amdgcn-amd-amdhsa -mcpu=gfx1250 -passes=slp-vectorizer < %s | FileCheck -check-prefixes=GFX1250 %s | |
| ; Both functions build the same tree: gather two scalars into <2 x float>, one | |
| ; packed fadd, then a pair of stores. The only difference is what feeds the | |
| ; gathered lanes. | |
| ; A lane fed by a load is packed for free by the memory pipeline, so the tree | |
| ; stays profitable on targets with v_pk_*_f32. | |
| define amdgpu_kernel void @load_fed_gather(ptr addrspace(1) %out, ptr addrspace(1) %in) { | |
| ; NOPACKED-LABEL: define amdgpu_kernel void @load_fed_gather( | |
| ; NOPACKED-SAME: ptr addrspace(1) [[OUT:%.*]], ptr addrspace(1) [[IN:%.*]]) #[[ATTR0:[0-9]+]] { | |
| ; NOPACKED-NEXT: [[Q0:%.*]] = getelementptr inbounds float, ptr addrspace(1) [[IN]], i64 0 | |
| ; NOPACKED-NEXT: [[Q1:%.*]] = getelementptr inbounds float, ptr addrspace(1) [[IN]], i64 17 | |
| ; NOPACKED-NEXT: [[L0:%.*]] = load float, ptr addrspace(1) [[Q0]], align 4 | |
| ; NOPACKED-NEXT: [[L1:%.*]] = load float, ptr addrspace(1) [[Q1]], align 4 | |
| ; NOPACKED-NEXT: [[R0:%.*]] = fadd float [[L0]], 1.000000e+00 | |
| ; NOPACKED-NEXT: [[R1:%.*]] = fadd float [[L1]], 2.000000e+00 | |
| ; NOPACKED-NEXT: [[P0:%.*]] = getelementptr inbounds float, ptr addrspace(1) [[OUT]], i64 0 | |
| ; NOPACKED-NEXT: [[P1:%.*]] = getelementptr inbounds float, ptr addrspace(1) [[OUT]], i64 1 | |
| ; NOPACKED-NEXT: store float [[R0]], ptr addrspace(1) [[P0]], align 8 | |
| ; NOPACKED-NEXT: store float [[R1]], ptr addrspace(1) [[P1]], align 4 | |
| ; NOPACKED-NEXT: ret void | |
| ; | |
| ; GFX90A-LABEL: define amdgpu_kernel void @load_fed_gather( | |
| ; GFX90A-SAME: ptr addrspace(1) [[OUT:%.*]], ptr addrspace(1) [[IN:%.*]]) #[[ATTR0:[0-9]+]] { | |
| ; GFX90A-NEXT: [[Q0:%.*]] = getelementptr inbounds float, ptr addrspace(1) [[IN]], i64 0 | |
| ; GFX90A-NEXT: [[Q1:%.*]] = getelementptr inbounds float, ptr addrspace(1) [[IN]], i64 17 | |
| ; GFX90A-NEXT: [[L0:%.*]] = load float, ptr addrspace(1) [[Q0]], align 4 | |
| ; GFX90A-NEXT: [[L1:%.*]] = load float, ptr addrspace(1) [[Q1]], align 4 | |
| ; GFX90A-NEXT: [[P0:%.*]] = getelementptr inbounds float, ptr addrspace(1) [[OUT]], i64 0 | |
| ; GFX90A-NEXT: [[TMP1:%.*]] = insertelement <2 x float> poison, float [[L0]], i64 0 | |
| ; GFX90A-NEXT: [[TMP2:%.*]] = insertelement <2 x float> [[TMP1]], float [[L1]], i64 1 | |
| ; GFX90A-NEXT: [[TMP3:%.*]] = fadd <2 x float> [[TMP2]], <float 1.000000e+00, float 2.000000e+00> | |
| ; GFX90A-NEXT: store <2 x float> [[TMP3]], ptr addrspace(1) [[P0]], align 8 | |
| ; GFX90A-NEXT: ret void | |
| ; | |
| ; GFX1250-LABEL: define amdgpu_kernel void @load_fed_gather( | |
| ; GFX1250-SAME: ptr addrspace(1) [[OUT:%.*]], ptr addrspace(1) [[IN:%.*]]) #[[ATTR0:[0-9]+]] { | |
| ; GFX1250-NEXT: [[Q0:%.*]] = getelementptr inbounds float, ptr addrspace(1) [[IN]], i64 0 | |
| ; GFX1250-NEXT: [[Q1:%.*]] = getelementptr inbounds float, ptr addrspace(1) [[IN]], i64 17 | |
| ; GFX1250-NEXT: [[L0:%.*]] = load float, ptr addrspace(1) [[Q0]], align 4 | |
| ; GFX1250-NEXT: [[L1:%.*]] = load float, ptr addrspace(1) [[Q1]], align 4 | |
| ; GFX1250-NEXT: [[P0:%.*]] = getelementptr inbounds float, ptr addrspace(1) [[OUT]], i64 0 | |
| ; GFX1250-NEXT: [[TMP1:%.*]] = insertelement <2 x float> poison, float [[L0]], i64 0 | |
| ; GFX1250-NEXT: [[TMP2:%.*]] = insertelement <2 x float> [[TMP1]], float [[L1]], i64 1 | |
| ; GFX1250-NEXT: [[TMP3:%.*]] = fadd <2 x float> [[TMP2]], <float 1.000000e+00, float 2.000000e+00> | |
| ; GFX1250-NEXT: store <2 x float> [[TMP3]], ptr addrspace(1) [[P0]], align 8 | |
| ; GFX1250-NEXT: ret void | |
| ; | |
| ; PACKED-LABEL: define amdgpu_kernel void @load_fed_gather( | |
| ; PACKED-SAME: ptr addrspace(1) [[OUT:%.*]], ptr addrspace(1) [[IN:%.*]]) #[[ATTR0:[0-9]+]] { | |
| ; PACKED-NEXT: [[Q0:%.*]] = getelementptr inbounds float, ptr addrspace(1) [[IN]], i64 0 | |
| ; PACKED-NEXT: [[Q1:%.*]] = getelementptr inbounds float, ptr addrspace(1) [[IN]], i64 17 | |
| ; PACKED-NEXT: [[L0:%.*]] = load float, ptr addrspace(1) [[Q0]], align 4 | |
| ; PACKED-NEXT: [[L1:%.*]] = load float, ptr addrspace(1) [[Q1]], align 4 | |
| ; PACKED-NEXT: [[P0:%.*]] = getelementptr inbounds float, ptr addrspace(1) [[OUT]], i64 0 | |
| ; PACKED-NEXT: [[TMP1:%.*]] = insertelement <2 x float> poison, float [[L0]], i64 0 | |
| ; PACKED-NEXT: [[TMP2:%.*]] = insertelement <2 x float> [[TMP1]], float [[L1]], i64 1 | |
| ; PACKED-NEXT: [[TMP3:%.*]] = fadd <2 x float> [[TMP2]], <float 1.000000e+00, float 2.000000e+00> | |
| ; PACKED-NEXT: store <2 x float> [[TMP3]], ptr addrspace(1) [[P0]], align 8 | |
| ; PACKED-NEXT: ret void | |
| %q0 = getelementptr inbounds float, ptr addrspace(1) %in, i64 0 | |
| %q1 = getelementptr inbounds float, ptr addrspace(1) %in, i64 17 | |
| %l0 = load float, ptr addrspace(1) %q0, align 4 | |
| %l1 = load float, ptr addrspace(1) %q1, align 4 | |
| %r0 = fadd float %l0, 1.0 | |
| %r1 = fadd float %l1, 2.0 | |
| %p0 = getelementptr inbounds float, ptr addrspace(1) %out, i64 0 | |
| %p1 = getelementptr inbounds float, ptr addrspace(1) %out, i64 1 | |
| store float %r0, ptr addrspace(1) %p0, align 8 | |
| store float %r1, ptr addrspace(1) %p1, align 4 | |
| ret void | |
| } | |
| ; A lane fed by a computed value has to be moved into place, and that packing | |
| ; work eats the saving from the single packed fadd, so the tree is rejected. | |
| define amdgpu_kernel void @compute_fed_gather(ptr addrspace(1) %out, float %a, float %b) { | |
| ; NOPACKED-LABEL: define amdgpu_kernel void @compute_fed_gather( | |
| ; NOPACKED-SAME: ptr addrspace(1) [[OUT:%.*]], float [[A:%.*]], float [[B:%.*]]) #[[ATTR0]] { | |
| ; NOPACKED-NEXT: [[L0:%.*]] = call float @llvm.amdgcn.rcp.f32(float [[A]]) | |
| ; NOPACKED-NEXT: [[L1:%.*]] = call float @llvm.amdgcn.rcp.f32(float [[B]]) | |
| ; NOPACKED-NEXT: [[R0:%.*]] = fadd float [[L0]], 1.000000e+00 | |
| ; NOPACKED-NEXT: [[R1:%.*]] = fadd float [[L1]], 2.000000e+00 | |
| ; NOPACKED-NEXT: [[P0:%.*]] = getelementptr inbounds float, ptr addrspace(1) [[OUT]], i64 0 | |
| ; NOPACKED-NEXT: [[P1:%.*]] = getelementptr inbounds float, ptr addrspace(1) [[OUT]], i64 1 | |
| ; NOPACKED-NEXT: store float [[R0]], ptr addrspace(1) [[P0]], align 8 | |
| ; NOPACKED-NEXT: store float [[R1]], ptr addrspace(1) [[P1]], align 4 | |
| ; NOPACKED-NEXT: ret void | |
| ; | |
| ; GFX90A-LABEL: define amdgpu_kernel void @compute_fed_gather( | |
| ; GFX90A-SAME: ptr addrspace(1) [[OUT:%.*]], float [[A:%.*]], float [[B:%.*]]) #[[ATTR0]] { | |
| ; GFX90A-NEXT: [[L0:%.*]] = call float @llvm.amdgcn.rcp.f32(float [[A]]) | |
| ; GFX90A-NEXT: [[L1:%.*]] = call float @llvm.amdgcn.rcp.f32(float [[B]]) | |
| ; GFX90A-NEXT: [[R0:%.*]] = fadd float [[L0]], 1.000000e+00 | |
| ; GFX90A-NEXT: [[R1:%.*]] = fadd float [[L1]], 2.000000e+00 | |
| ; GFX90A-NEXT: [[P0:%.*]] = getelementptr inbounds float, ptr addrspace(1) [[OUT]], i64 0 | |
| ; GFX90A-NEXT: [[P1:%.*]] = getelementptr inbounds float, ptr addrspace(1) [[OUT]], i64 1 | |
| ; GFX90A-NEXT: store float [[R0]], ptr addrspace(1) [[P0]], align 8 | |
| ; GFX90A-NEXT: store float [[R1]], ptr addrspace(1) [[P1]], align 4 | |
| ; GFX90A-NEXT: ret void | |
| ; | |
| ; GFX1250-LABEL: define amdgpu_kernel void @compute_fed_gather( | |
| ; GFX1250-SAME: ptr addrspace(1) [[OUT:%.*]], float [[A:%.*]], float [[B:%.*]]) #[[ATTR0]] { | |
| ; GFX1250-NEXT: [[L0:%.*]] = call float @llvm.amdgcn.rcp.f32(float [[A]]) | |
| ; GFX1250-NEXT: [[L1:%.*]] = call float @llvm.amdgcn.rcp.f32(float [[B]]) | |
| ; GFX1250-NEXT: [[R0:%.*]] = fadd float [[L0]], 1.000000e+00 | |
| ; GFX1250-NEXT: [[R1:%.*]] = fadd float [[L1]], 2.000000e+00 | |
| ; GFX1250-NEXT: [[P0:%.*]] = getelementptr inbounds float, ptr addrspace(1) [[OUT]], i64 0 | |
| ; GFX1250-NEXT: [[P1:%.*]] = getelementptr inbounds float, ptr addrspace(1) [[OUT]], i64 1 | |
| ; GFX1250-NEXT: store float [[R0]], ptr addrspace(1) [[P0]], align 8 | |
| ; GFX1250-NEXT: store float [[R1]], ptr addrspace(1) [[P1]], align 4 | |
| ; GFX1250-NEXT: ret void | |
| ; | |
| ; PACKED-LABEL: define amdgpu_kernel void @compute_fed_gather( | |
| ; PACKED-SAME: ptr addrspace(1) [[OUT:%.*]], float [[A:%.*]], float [[B:%.*]]) #[[ATTR0]] { | |
| ; PACKED-NEXT: [[L0:%.*]] = call float @llvm.amdgcn.rcp.f32(float [[A]]) | |
| ; PACKED-NEXT: [[L1:%.*]] = call float @llvm.amdgcn.rcp.f32(float [[B]]) | |
| ; PACKED-NEXT: [[R0:%.*]] = fadd float [[L0]], 1.000000e+00 | |
| ; PACKED-NEXT: [[R1:%.*]] = fadd float [[L1]], 2.000000e+00 | |
| ; PACKED-NEXT: [[P0:%.*]] = getelementptr inbounds float, ptr addrspace(1) [[OUT]], i64 0 | |
| ; PACKED-NEXT: [[P1:%.*]] = getelementptr inbounds float, ptr addrspace(1) [[OUT]], i64 1 | |
| ; PACKED-NEXT: store float [[R0]], ptr addrspace(1) [[P0]], align 8 | |
| ; PACKED-NEXT: store float [[R1]], ptr addrspace(1) [[P1]], align 4 | |
| ; PACKED-NEXT: ret void | |
| %l0 = call float @llvm.amdgcn.rcp.f32(float %a) | |
| %l1 = call float @llvm.amdgcn.rcp.f32(float %b) | |
| %r0 = fadd float %l0, 1.0 | |
| %r1 = fadd float %l1, 2.0 | |
| %p0 = getelementptr inbounds float, ptr addrspace(1) %out, i64 0 | |
| %p1 = getelementptr inbounds float, ptr addrspace(1) %out, i64 1 | |
| store float %r0, ptr addrspace(1) %p0, align 8 | |
| store float %r1, ptr addrspace(1) %p1, align 4 | |
| ret void | |
| } | |
| declare float @llvm.amdgcn.rcp.f32(float) |