llvm-project/mlir/test/Dialect/Affine/SuperVectorize/vectorize_reduction.mlir
Sergei Grechanik d80b04ab00 [mlir][Affine][Vector] Support vectorizing reduction loops
This patch adds support for vectorizing loops with 'iter_args'
implementing known reductions along the vector dimension. Comparing to
the non-vector-dimension case, two additional things are done during
vectorization of such loops:
- The resulting vector returned from the loop is reduced to a scalar
  using `vector.reduce`.
- In some cases a mask is applied to the vector yielded at the end of
  the loop to prevent garbage values from being written to the
  accumulator.

Vectorization of reduction loops is disabled by default. To enable it, a
map from loops to array of reduction descriptors should be explicitly passed to
`vectorizeAffineLoops`, or `vectorize-reductions=true` should be passed
to the SuperVectorize pass.

Current limitations:
- Loops with a non-unit step size are not supported.
- n-D vectorization with n > 1 is not supported.

Reviewed By: nicolasvasilache

Differential Revision: https://reviews.llvm.org/D100694
2021-05-05 09:03:59 -07:00

469 lines
21 KiB
MLIR

// RUN: mlir-opt %s -affine-super-vectorize="virtual-vector-size=128 test-fastest-varying=0 vectorize-reductions=true" -split-input-file | FileCheck %s
// The inner reduction loop '%j' is vectorized.
func @vecdim_reduction(%in: memref<256x512xf32>, %out: memref<256xf32>) {
%cst = constant 0.000000e+00 : f32
affine.for %i = 0 to 256 {
%final_red = affine.for %j = 0 to 512 iter_args(%red_iter = %cst) -> (f32) {
%ld = affine.load %in[%i, %j] : memref<256x512xf32>
%add = addf %red_iter, %ld : f32
affine.yield %add : f32
}
affine.store %final_red, %out[%i] : memref<256xf32>
}
return
}
// CHECK-LABEL: @vecdim_reduction
// CHECK: affine.for %{{.*}} = 0 to 256 {
// CHECK: %[[vzero:.*]] = constant dense<0.000000e+00> : vector<128xf32>
// CHECK: %[[vred:.*]] = affine.for %{{.*}} = 0 to 512 step 128 iter_args(%[[red_iter:.*]] = %[[vzero]]) -> (vector<128xf32>) {
// CHECK: %[[ld:.*]] = vector.transfer_read %{{.*}} : memref<256x512xf32>, vector<128xf32>
// CHECK: %[[add:.*]] = addf %[[red_iter]], %[[ld]] : vector<128xf32>
// CHECK: affine.yield %[[add]] : vector<128xf32>
// CHECK: }
// CHECK: %[[final_sum:.*]] = vector.reduction "add", %[[vred:.*]] : vector<128xf32> into f32
// CHECK: affine.store %[[final_sum]], %{{.*}} : memref<256xf32>
// CHECK: }
// -----
// The inner reduction loop '%j' is vectorized. (The order of addf's operands is
// different than in the previous test case).
func @vecdim_reduction_comm(%in: memref<256x512xf32>, %out: memref<256xf32>) {
%cst = constant 0.000000e+00 : f32
affine.for %i = 0 to 256 {
%final_red = affine.for %j = 0 to 512 iter_args(%red_iter = %cst) -> (f32) {
%ld = affine.load %in[%i, %j] : memref<256x512xf32>
%add = addf %ld, %red_iter : f32
affine.yield %add : f32
}
affine.store %final_red, %out[%i] : memref<256xf32>
}
return
}
// CHECK-LABEL: @vecdim_reduction_comm
// CHECK: affine.for %{{.*}} = 0 to 256 {
// CHECK: %[[vzero:.*]] = constant dense<0.000000e+00> : vector<128xf32>
// CHECK: %[[vred:.*]] = affine.for %{{.*}} = 0 to 512 step 128 iter_args(%[[red_iter:.*]] = %[[vzero]]) -> (vector<128xf32>) {
// CHECK: %[[ld:.*]] = vector.transfer_read %{{.*}} : memref<256x512xf32>, vector<128xf32>
// CHECK: %[[add:.*]] = addf %[[ld]], %[[red_iter]] : vector<128xf32>
// CHECK: affine.yield %[[add]] : vector<128xf32>
// CHECK: }
// CHECK: %[[final_sum:.*]] = vector.reduction "add", %[[vred:.*]] : vector<128xf32> into f32
// CHECK: affine.store %[[final_sum]], %{{.*}} : memref<256xf32>
// CHECK: }
// -----
// The inner reduction loop '%j' is vectorized. Transforming the input before
// performing the accumulation doesn't cause any problem.
func @vecdim_reduction_expsin(%in: memref<256x512xf32>, %out: memref<256xf32>) {
%cst = constant 0.000000e+00 : f32
affine.for %i = 0 to 256 {
%final_red = affine.for %j = 0 to 512 iter_args(%red_iter = %cst) -> (f32) {
%ld = affine.load %in[%i, %j] : memref<256x512xf32>
%sin = math.sin %ld : f32
%exp = math.exp %sin : f32
%add = addf %red_iter, %exp : f32
affine.yield %add : f32
}
affine.store %final_red, %out[%i] : memref<256xf32>
}
return
}
// CHECK-LABEL: @vecdim_reduction_expsin
// CHECK: affine.for %{{.*}} = 0 to 256 {
// CHECK: %[[vzero:.*]] = constant dense<0.000000e+00> : vector<128xf32>
// CHECK: %[[vred:.*]] = affine.for %{{.*}} = 0 to 512 step 128 iter_args(%[[red_iter:.*]] = %[[vzero]]) -> (vector<128xf32>) {
// CHECK: %[[ld:.*]] = vector.transfer_read %{{.*}} : memref<256x512xf32>, vector<128xf32>
// CHECK: %[[sin:.*]] = math.sin %[[ld]]
// CHECK: %[[exp:.*]] = math.exp %[[sin]]
// CHECK: %[[add:.*]] = addf %[[red_iter]], %[[exp]] : vector<128xf32>
// CHECK: affine.yield %[[add]] : vector<128xf32>
// CHECK: }
// CHECK: %[[final_sum:.*]] = vector.reduction "add", %[[vred:.*]] : vector<128xf32> into f32
// CHECK: affine.store %[[final_sum]], %{{.*}} : memref<256xf32>
// CHECK: }
// -----
// Two reductions at the same time. The inner reduction loop '%j' is vectorized.
func @two_vecdim_reductions(%in: memref<256x512xf32>, %out_sum: memref<256xf32>, %out_prod: memref<256xf32>) {
%cst = constant 1.000000e+00 : f32
affine.for %i = 0 to 256 {
// Note that we pass the same constant '1.0' as initial values for both
// reductions.
%sum, %prod = affine.for %j = 0 to 512 iter_args(%part_sum = %cst, %part_prod = %cst) -> (f32, f32) {
%ld = affine.load %in[%i, %j] : memref<256x512xf32>
%add = addf %part_sum, %ld : f32
%mul = mulf %part_prod, %ld : f32
affine.yield %add, %mul : f32, f32
}
affine.store %sum, %out_sum[%i] : memref<256xf32>
affine.store %prod, %out_prod[%i] : memref<256xf32>
}
return
}
// CHECK-LABEL: @two_vecdim_reductions
// CHECK: %[[cst:.*]] = constant 1.000000e+00 : f32
// CHECK: affine.for %{{.*}} = 0 to 256 {
// CHECK: %[[vzero:.*]] = constant dense<0.000000e+00> : vector<128xf32>
// CHECK: %[[vone:.*]] = constant dense<1.000000e+00> : vector<128xf32>
// CHECK: %[[vred:.*]]:2 = affine.for %{{.*}} = 0 to 512 step 128 iter_args(%[[part_sum:.*]] = %[[vzero]], %[[part_prod:.*]] = %[[vone]]) -> (vector<128xf32>, vector<128xf32>) {
// CHECK: %[[ld:.*]] = vector.transfer_read %{{.*}} : memref<256x512xf32>, vector<128xf32>
// CHECK: %[[add:.*]] = addf %[[part_sum]], %[[ld]] : vector<128xf32>
// CHECK: %[[mul:.*]] = mulf %[[part_prod]], %[[ld]] : vector<128xf32>
// CHECK: affine.yield %[[add]], %[[mul]] : vector<128xf32>, vector<128xf32>
// CHECK: }
// CHECK: %[[nonfinal_sum:.*]] = vector.reduction "add", %[[vred:.*]]#0 : vector<128xf32> into f32
// Note that to compute the final sum we need to add the original initial value
// (%cst) since it is not zero.
// CHECK: %[[final_sum:.*]] = addf %[[nonfinal_sum]], %[[cst]] : f32
// For the final product we don't need to do this additional step because the
// initial value equals to 1 (the neutral element for multiplication).
// CHECK: %[[final_prod:.*]] = vector.reduction "mul", %[[vred:.*]]#1 : vector<128xf32> into f32
// CHECK: affine.store %[[final_sum]], %{{.*}} : memref<256xf32>
// CHECK: affine.store %[[final_prod]], %{{.*}} : memref<256xf32>
// CHECK: }
// -----
// The integer case.
func @two_vecdim_reductions_int(%in: memref<256x512xi64>, %out_sum: memref<256xi64>, %out_prod: memref<256xi64>) {
%cst0 = constant 0 : i64
%cst1 = constant 1 : i64
affine.for %i = 0 to 256 {
%sum, %prod = affine.for %j = 0 to 512 iter_args(%part_sum = %cst0, %part_prod = %cst1) -> (i64, i64) {
%ld = affine.load %in[%i, %j] : memref<256x512xi64>
%add = addi %part_sum, %ld : i64
%mul = muli %part_prod, %ld : i64
affine.yield %add, %mul : i64, i64
}
affine.store %sum, %out_sum[%i] : memref<256xi64>
affine.store %prod, %out_prod[%i] : memref<256xi64>
}
return
}
// CHECK-LABEL: @two_vecdim_reductions
// CHECK: affine.for %{{.*}} = 0 to 256 {
// CHECK: %[[vzero:.*]] = constant dense<0> : vector<128xi64>
// CHECK: %[[vone:.*]] = constant dense<1> : vector<128xi64>
// CHECK: %[[vred:.*]]:2 = affine.for %{{.*}} = 0 to 512 step 128 iter_args(%[[part_sum:.*]] = %[[vzero]], %[[part_prod:.*]] = %[[vone]]) -> (vector<128xi64>, vector<128xi64>) {
// CHECK: %[[ld:.*]] = vector.transfer_read %{{.*}} : memref<256x512xi64>, vector<128xi64>
// CHECK: %[[add:.*]] = addi %[[part_sum]], %[[ld]] : vector<128xi64>
// CHECK: %[[mul:.*]] = muli %[[part_prod]], %[[ld]] : vector<128xi64>
// CHECK: affine.yield %[[add]], %[[mul]] : vector<128xi64>, vector<128xi64>
// CHECK: }
// CHECK: %[[final_sum:.*]] = vector.reduction "add", %[[vred:.*]]#0 : vector<128xi64> into i64
// CHECK: %[[final_prod:.*]] = vector.reduction "mul", %[[vred:.*]]#1 : vector<128xi64> into i64
// CHECK: affine.store %[[final_sum]], %{{.*}} : memref<256xi64>
// CHECK: affine.store %[[final_prod]], %{{.*}} : memref<256xi64>
// CHECK: }
// -----
// The outer reduction loop '%j' is vectorized.
func @vecdim_reduction_nested(%in: memref<256x512xf32>, %out: memref<1xf32>) {
%cst = constant 0.000000e+00 : f32
%outer_red = affine.for %j = 0 to 512 iter_args(%outer_iter = %cst) -> (f32) {
%inner_red = affine.for %i = 0 to 256 iter_args(%inner_iter = %cst) -> (f32) {
%ld = affine.load %in[%i, %j] : memref<256x512xf32>
%add = addf %inner_iter, %ld : f32
affine.yield %add : f32
}
%outer_add = addf %outer_iter, %inner_red : f32
affine.yield %outer_add : f32
}
affine.store %outer_red, %out[0] : memref<1xf32>
return
}
// CHECK-LABEL: @vecdim_reduction_nested
// CHECK: %[[vzero:.*]] = constant dense<0.000000e+00> : vector<128xf32>
// CHECK: %[[outer_red:.*]] = affine.for %{{.*}} = 0 to 512 step 128 iter_args(%[[outer_iter:.*]] = %[[vzero]]) -> (vector<128xf32>) {
// CHECK: %[[vzero:.*]] = constant dense<0.000000e+00> : vector<128xf32>
// CHECK: %[[inner_red:.*]] = affine.for %{{.*}} = 0 to 256 iter_args(%[[inner_iter:.*]] = %[[vzero]]) -> (vector<128xf32>) {
// CHECK: %[[ld:.*]] = vector.transfer_read %{{.*}} : memref<256x512xf32>, vector<128xf32>
// CHECK: %[[add:.*]] = addf %[[inner_iter]], %[[ld]] : vector<128xf32>
// CHECK: affine.yield %[[add]] : vector<128xf32>
// CHECK: }
// CHECK: %[[outer_add:.*]] = addf %[[outer_iter]], %[[inner_red]] : vector<128xf32>
// CHECK: affine.yield %[[outer_add]] : vector<128xf32>
// CHECK: }
// CHECK: %[[final_sum:.*]] = vector.reduction "add", %[[outer_red:.*]] : vector<128xf32> into f32
// CHECK: affine.store %[[final_sum]], %{{.*}} : memref<1xf32>
// -----
// The inner reduction loop '%j' computes partial sums as a side effect and
// is not vectorized.
func @vecdim_partial_sums_1_rejected(%in: memref<256x512xf32>, %out_sum: memref<256xf32>, %out_prod: memref<256xf32>, %out_partsum: memref<256x512xf32>) {
%cst = constant 1.000000e+00 : f32
affine.for %i = 0 to 256 {
%sum, %prod = affine.for %j = 0 to 512 iter_args(%part_sum = %cst, %part_prod = %cst) -> (f32, f32) {
%ld = affine.load %in[%i, %j] : memref<256x512xf32>
%add = addf %part_sum, %ld : f32
%mul = mulf %part_prod, %ld : f32
affine.store %add, %out_partsum[%i, %j] : memref<256x512xf32>
affine.yield %add, %mul : f32, f32
}
affine.store %sum, %out_sum[%i] : memref<256xf32>
affine.store %prod, %out_prod[%i] : memref<256xf32>
}
return
}
// CHECK-LABEL: @vecdim_partial_sums_1_rejected
// CHECK-NOT: vector
// -----
// The inner reduction loop '%j' computes partial sums as a side effect and
// is not vectorized.
func @vecdim_partial_sums_2_rejected(%in: memref<256x512xf32>, %out_sum: memref<256xf32>, %out_prod: memref<256xf32>, %out_partsum: memref<256x512xf32>) {
%cst = constant 1.000000e+00 : f32
affine.for %i = 0 to 256 {
%sum, %prod = affine.for %j = 0 to 512 iter_args(%part_sum = %cst, %part_prod = %cst) -> (f32, f32) {
affine.store %part_sum, %out_partsum[%i, %j] : memref<256x512xf32>
%ld = affine.load %in[%i, %j] : memref<256x512xf32>
%add = addf %part_sum, %ld : f32
%mul = mulf %part_prod, %ld : f32
affine.yield %add, %mul : f32, f32
}
affine.store %sum, %out_sum[%i] : memref<256xf32>
affine.store %prod, %out_prod[%i] : memref<256xf32>
}
return
}
// CHECK-LABEL: @vecdim_partial_sums_2_rejected
// CHECK-NOT: vector
// -----
// The inner reduction loop '%j' performs an unknown reduction operation and is
// not vectorized.
func @vecdim_unknown_reduction_rejected(%in: memref<256x512xf32>, %out: memref<256xf32>) {
%cst = constant 1.000000e+00 : f32
%final_red = affine.for %j = 0 to 512 iter_args(%red_iter = %cst) -> (f32) {
%add = addf %red_iter, %red_iter : f32
affine.yield %add : f32
}
affine.store %final_red, %out[0] : memref<256xf32>
return
}
// CHECK-LABEL: @vecdim_unknown_reduction_rejected
// CHECK-NOT: vector
// -----
// The inner reduction loop '%j' doesn't perform any operation which is not
// recognized as a standard reduction.
func @vecdim_none_reduction_rejected(%in: memref<256x512xf32>, %out: memref<256xf32>) {
%cst = constant 1.000000e+00 : f32
%final_red = affine.for %j = 0 to 512 iter_args(%red_iter = %cst) -> (f32) {
affine.yield %red_iter : f32
}
affine.store %final_red, %out[0] : memref<256xf32>
return
}
// CHECK-LABEL: @vecdim_none_reduction_rejected
// CHECK-NOT: vector
// -----
// The number of iterations is not divisable by the vector size, so a mask has
// to be applied to the last update of the accumulator.
func @vecdim_reduction_masked(%in: memref<256x512xf32>, %out: memref<256xf32>) {
%cst = constant 0.000000e+00 : f32
affine.for %i = 0 to 256 {
%final_red = affine.for %j = 0 to 500 iter_args(%red_iter = %cst) -> (f32) {
%ld = affine.load %in[%i, %j] : memref<256x512xf32>
%add = addf %red_iter, %ld : f32
affine.yield %add : f32
}
affine.store %final_red, %out[%i] : memref<256xf32>
}
return
}
// CHECK: #[[$map0:.*]] = affine_map<([[d0:.*]]) -> (-[[d0]] + 500)>
// CHECK-LABEL: @vecdim_reduction_masked
// CHECK: affine.for %{{.*}} = 0 to 256 {
// CHECK: %[[vzero:.*]] = constant dense<0.000000e+00> : vector<128xf32>
// CHECK: %[[vred:.*]] = affine.for %[[iv:.*]] = 0 to 500 step 128 iter_args(%[[red_iter:.*]] = %[[vzero]]) -> (vector<128xf32>) {
// CHECK: %[[elems_left:.*]] = affine.apply #[[$map0]](%[[iv]])
// CHECK: %[[mask:.*]] = vector.create_mask %[[elems_left]] : vector<128xi1>
// CHECK: %[[ld:.*]] = vector.transfer_read %{{.*}} : memref<256x512xf32>, vector<128xf32>
// CHECK: %[[add:.*]] = addf %[[red_iter]], %[[ld]] : vector<128xf32>
// CHECK: %[[new_acc:.*]] = select %[[mask]], %[[add]], %[[red_iter]] : vector<128xi1>, vector<128xf32>
// CHECK: affine.yield %[[new_acc]] : vector<128xf32>
// CHECK: }
// CHECK: %[[final_sum:.*]] = vector.reduction "add", %[[vred:.*]] : vector<128xf32> into f32
// CHECK: affine.store %[[final_sum]], %{{.*}} : memref<256xf32>
// CHECK: }
// -----
// The number of iteration is not known, so a mask has to be applied.
func @vecdim_reduction_masked_unknown_ub(%in: memref<256x512xf32>, %out: memref<256xf32>, %bnd: index) {
%cst = constant 0.000000e+00 : f32
affine.for %i = 0 to 256 {
%final_red = affine.for %j = 0 to %bnd iter_args(%red_iter = %cst) -> (f32) {
%ld = affine.load %in[%i, %j] : memref<256x512xf32>
%add = addf %red_iter, %ld : f32
affine.yield %add : f32
}
affine.store %final_red, %out[%i] : memref<256xf32>
}
return
}
// CHECK: #[[$map1:.*]] = affine_map<([[d0:.*]]){{\[}}[[s0:.*]]{{\]}} -> (-[[d0]] + [[s0]])>
// CHECK-LABEL: @vecdim_reduction_masked_unknown_ub
// CHECK: affine.for %{{.*}} = 0 to 256 {
// CHECK: %[[vzero:.*]] = constant dense<0.000000e+00> : vector<128xf32>
// CHECK: %[[vred:.*]] = affine.for %[[iv:.*]] = 0 to %[[bnd:.*]] step 128 iter_args(%[[red_iter:.*]] = %[[vzero]]) -> (vector<128xf32>) {
// CHECK: %[[elems_left:.*]] = affine.apply #[[$map1]](%[[iv]])[%[[bnd]]]
// CHECK: %[[mask:.*]] = vector.create_mask %[[elems_left]] : vector<128xi1>
// CHECK: %[[ld:.*]] = vector.transfer_read %{{.*}} : memref<256x512xf32>, vector<128xf32>
// CHECK: %[[add:.*]] = addf %[[red_iter]], %[[ld]] : vector<128xf32>
// CHECK: %[[new_acc:.*]] = select %[[mask]], %[[add]], %[[red_iter]] : vector<128xi1>, vector<128xf32>
// CHECK: affine.yield %[[new_acc]] : vector<128xf32>
// CHECK: }
// CHECK: %[[final_sum:.*]] = vector.reduction "add", %[[vred:.*]] : vector<128xf32> into f32
// CHECK: affine.store %[[final_sum]], %{{.*}} : memref<256xf32>
// CHECK: }
// -----
// The lower bound is nonzero, but the number of iterations is divisible by the
// vector size, so masking is not needed.
func @vecdim_reduction_nonzero_lb(%in: memref<256x512xf32>, %out: memref<256xf32>) {
%cst = constant 0.000000e+00 : f32
affine.for %i = 0 to 256 {
%final_red = affine.for %j = 127 to 511 iter_args(%red_iter = %cst) -> (f32) {
%ld = affine.load %in[%i, %j] : memref<256x512xf32>
%add = addf %red_iter, %ld : f32
affine.yield %add : f32
}
affine.store %final_red, %out[%i] : memref<256xf32>
}
return
}
// CHECK-LABEL: @vecdim_reduction_nonzero_lb
// CHECK: %{{.*}} = affine.for %{{.*}} = 127 to 511 step 128 iter_args({{.*}}) -> (vector<128xf32>) {
// CHECK-NOT: vector.create_mask
// -----
// The lower bound is unknown, so we need to create a mask.
func @vecdim_reduction_masked_unknown_lb(%in: memref<256x512xf32>, %out: memref<256xf32>, %lb: index) {
%cst = constant 0.000000e+00 : f32
affine.for %i = 0 to 256 {
%final_red = affine.for %j = %lb to 512 iter_args(%red_iter = %cst) -> (f32) {
%ld = affine.load %in[%i, %j] : memref<256x512xf32>
%add = addf %red_iter, %ld : f32
affine.yield %add : f32
}
affine.store %final_red, %out[%i] : memref<256xf32>
}
return
}
// CHECK: #[[$map2:.*]] = affine_map<([[d0:.*]]) -> (-[[d0]] + 512)>
// CHECK-LABEL: @vecdim_reduction_masked_unknown_lb
// CHECK: %{{.*}} = affine.for %[[iv:.*]] = %[[lb:.*]] to 512 step 128 iter_args(%[[red_iter:.*]] = {{.*}}) -> (vector<128xf32>) {
// CHECK: %[[elems_left:.*]] = affine.apply #[[$map2]](%[[iv]])
// CHECK: %[[mask:.*]] = vector.create_mask %[[elems_left]] : vector<128xi1>
// CHECK: %[[ld:.*]] = vector.transfer_read %{{.*}} : memref<256x512xf32>, vector<128xf32>
// CHECK: %[[add:.*]] = addf %[[red_iter]], %[[ld]] : vector<128xf32>
// CHECK: %[[new_acc:.*]] = select %[[mask]], %[[add]], %[[red_iter]] : vector<128xi1>, vector<128xf32>
// CHECK: affine.yield %[[new_acc]] : vector<128xf32>
// -----
// The upper bound is a minimum expression.
func @vecdim_reduction_complex_ub(%in: memref<256x512xf32>, %out: memref<256xf32>, %M: index, %N: index) {
%cst = constant 0.000000e+00 : f32
affine.for %i = 0 to 256 {
%final_red = affine.for %j = 0 to min affine_map<(d0, d1) -> (d0, d1*2)>(%M, %N) iter_args(%red_iter = %cst) -> (f32) {
%ld = affine.load %in[%i, %j] : memref<256x512xf32>
%add = addf %red_iter, %ld : f32
affine.yield %add : f32
}
affine.store %final_red, %out[%i] : memref<256xf32>
}
return
}
// CHECK: #[[$map3:.*]] = affine_map<([[d0:.*]], [[d1:.*]]) -> ([[d0]], [[d1]] * 2)>
// CHECK: #[[$map3_sub:.*]] = affine_map<([[d0:.*]], [[d1:.*]]) -> ([[d0]] - [[d1]])>
// CHECK-LABEL: @vecdim_reduction_complex_ub
// CHECK: %{{.*}} = affine.for %[[iv:.*]] = 0 to min #[[$map3]](%[[M:.*]], %[[N:.*]]) step 128 iter_args(%[[red_iter:.*]] = {{.*}}) -> (vector<128xf32>) {
// CHECK: %[[ub:.*]] = affine.min #[[$map3]](%[[M]], %[[N]])
// CHECK: %[[elems_left:.*]] = affine.apply #[[$map3_sub]](%[[ub]], %[[iv]])
// CHECK: %[[mask:.*]] = vector.create_mask %[[elems_left]] : vector<128xi1>
// CHECK: %[[ld:.*]] = vector.transfer_read %{{.*}} : memref<256x512xf32>, vector<128xf32>
// CHECK: %[[add:.*]] = addf %[[red_iter]], %[[ld]] : vector<128xf32>
// CHECK: %[[new_acc:.*]] = select %[[mask]], %[[add]], %[[red_iter]] : vector<128xi1>, vector<128xf32>
// CHECK: affine.yield %[[new_acc]] : vector<128xf32>
// -----
// The same mask is applied to both reductions.
func @vecdim_two_reductions_masked(%in: memref<256x512xf32>, %out: memref<512xf32>) {
%cst = constant 0.000000e+00 : f32
affine.for %i = 0 to 256 {
%final_sum, %final_expsum = affine.for %j = 0 to 500 iter_args(%sum_iter = %cst, %expsum_iter = %cst) -> (f32, f32) {
%ld = affine.load %in[%i, %j] : memref<256x512xf32>
%exp = math.exp %ld : f32
%add = addf %sum_iter, %ld : f32
%eadd = addf %expsum_iter, %exp : f32
affine.yield %add, %eadd : f32, f32
}
affine.store %final_sum, %out[2*%i] : memref<512xf32>
affine.store %final_expsum, %out[2*%i + 1] : memref<512xf32>
}
return
}
// CHECK: #[[$map4:.*]] = affine_map<([[d0:.*]]) -> (-[[d0]] + 500)>
// CHECK-LABEL: @vecdim_two_reductions_masked
// CHECK: affine.for %{{.*}} = 0 to 256 {
// CHECK: %{{.*}} = affine.for %[[iv:.*]] = 0 to 500 step 128 iter_args(%[[sum_iter:.*]] = {{.*}}, %[[esum_iter:.*]] = {{.*}}) -> (vector<128xf32>, vector<128xf32>) {
// CHECK: %[[elems_left:.*]] = affine.apply #[[$map4]](%[[iv]])
// CHECK: %[[mask:.*]] = vector.create_mask %[[elems_left]] : vector<128xi1>
// CHECK: %[[ld:.*]] = vector.transfer_read %{{.*}} : memref<256x512xf32>, vector<128xf32>
// CHECK: %[[exp:.*]] = math.exp %[[ld]] : vector<128xf32>
// CHECK: %[[add:.*]] = addf %[[sum_iter]], %[[ld]] : vector<128xf32>
// CHECK: %[[eadd:.*]] = addf %[[esum_iter]], %[[exp]] : vector<128xf32>
// CHECK: %[[new_acc:.*]] = select %[[mask]], %[[add]], %[[sum_iter]] : vector<128xi1>, vector<128xf32>
// CHECK: %[[new_eacc:.*]] = select %[[mask]], %[[eadd]], %[[esum_iter]] : vector<128xi1>, vector<128xf32>
// CHECK: affine.yield %[[new_acc]], %[[new_eacc]] : vector<128xf32>
// CHECK: }