// RUN: mlir-opt %s -shard-simplify | FileCheck %s shard.grid @grid_ag(shape = 2x2) shard.grid @grid_ag_alt(shape = 2x2) // CHECK-LABEL: func.func @all_gather_all_slice_identity func.func @all_gather_all_slice_identity( %arg0: tensor<4x4xf32>) -> tensor<4x4xf32> { %0 = shard.all_gather %arg0 on @grid_ag grid_axes = [1] gather_axis = 1 : tensor<4x4xf32> -> tensor<4x8xf32> %1 = shard.all_slice %0 on @grid_ag grid_axes = [1] slice_axis = 1 : tensor<4x8xf32> -> tensor<4x4xf32> // CHECK-NOT: shard.all_gather // CHECK-NOT: shard.all_slice // CHECK: return %arg0 : tensor<4x4xf32> return %1 : tensor<4x4xf32> } // CHECK-LABEL: func.func @all_gather_all_slice_different_axis func.func @all_gather_all_slice_different_axis( %arg0: tensor<4x4xf32>) -> tensor<2x8xf32> { %0 = shard.all_gather %arg0 on @grid_ag grid_axes = [1] gather_axis = 1 : tensor<4x4xf32> -> tensor<4x8xf32> %1 = shard.all_slice %0 on @grid_ag grid_axes = [1] slice_axis = 0 : tensor<4x8xf32> -> tensor<2x8xf32> // CHECK: shard.all_gather // CHECK: shard.all_slice return %1 : tensor<2x8xf32> } // CHECK-LABEL: func.func @all_gather_all_slice_different_grid_axes func.func @all_gather_all_slice_different_grid_axes( %arg0: tensor<4x4xf32>) -> tensor<4x4xf32> { %0 = shard.all_gather %arg0 on @grid_ag grid_axes = [0] gather_axis = 0 : tensor<4x4xf32> -> tensor<8x4xf32> %1 = shard.all_slice %0 on @grid_ag grid_axes = [1] slice_axis = 0 : tensor<8x4xf32> -> tensor<4x4xf32> // CHECK: shard.all_gather // CHECK: shard.all_slice return %1 : tensor<4x4xf32> } // CHECK-LABEL: func.func @all_gather_all_slice_different_grid func.func @all_gather_all_slice_different_grid( %arg0: tensor<4x4xf32>) -> tensor<4x4xf32> { %0 = shard.all_gather %arg0 on @grid_ag grid_axes = [1] gather_axis = 1 : tensor<4x4xf32> -> tensor<4x8xf32> %1 = shard.all_slice %0 on @grid_ag_alt grid_axes = [1] slice_axis = 1 : tensor<4x8xf32> -> tensor<4x4xf32> // CHECK: shard.all_gather // CHECK: shard.all_slice return %1 : tensor<4x4xf32> } // CHECK-LABEL: func.func @all_slice_all_gather_identity func.func @all_slice_all_gather_identity( %arg0: tensor<4x4xf32>) -> tensor<4x4xf32> { %0 = shard.all_slice %arg0 on @grid_ag grid_axes = [1] slice_axis = 1 : tensor<4x4xf32> -> tensor<4x2xf32> %1 = shard.all_gather %0 on @grid_ag grid_axes = [1] gather_axis = 1 : tensor<4x2xf32> -> tensor<4x4xf32> // CHECK-NOT: shard.all_slice // CHECK-NOT: shard.all_gather // CHECK: return %arg0 : tensor<4x4xf32> return %1 : tensor<4x4xf32> } // CHECK-LABEL: func.func @all_slice_all_gather_different_axis func.func @all_slice_all_gather_different_axis( %arg0: tensor<4x4xf32>) -> tensor<8x2xf32> { %0 = shard.all_slice %arg0 on @grid_ag grid_axes = [1] slice_axis = 1 : tensor<4x4xf32> -> tensor<4x2xf32> %1 = shard.all_gather %0 on @grid_ag grid_axes = [1] gather_axis = 0 : tensor<4x2xf32> -> tensor<8x2xf32> // CHECK: shard.all_slice // CHECK: shard.all_gather return %1 : tensor<8x2xf32> } // CHECK-LABEL: func.func @all_slice_all_gather_different_grid func.func @all_slice_all_gather_different_grid( %arg0: tensor<4x4xf32>) -> tensor<4x4xf32> { %0 = shard.all_slice %arg0 on @grid_ag grid_axes = [1] slice_axis = 1 : tensor<4x4xf32> -> tensor<4x2xf32> %1 = shard.all_gather %0 on @grid_ag_alt grid_axes = [1] gather_axis = 1 : tensor<4x2xf32> -> tensor<4x4xf32> // CHECK: shard.all_slice // CHECK: shard.all_gather return %1 : tensor<4x4xf32> } // CHECK-LABEL: func.func @all_slice_all_gather_different_grid_axes func.func @all_slice_all_gather_different_grid_axes( %arg0: tensor<4x4xf32>) -> tensor<4x4xf32> { %0 = shard.all_slice %arg0 on @grid_ag grid_axes = [0] slice_axis = 0 : tensor<4x4xf32> -> tensor<2x4xf32> %1 = shard.all_gather %0 on @grid_ag grid_axes = [1] gather_axis = 0 : tensor<2x4xf32> -> tensor<4x4xf32> // CHECK: shard.all_slice // CHECK: shard.all_gather return %1 : tensor<4x4xf32> } shard.grid @grid0(shape = 4x2) shard.grid @grid1(shape = 4) // Checks that `all_reduce(x) + all_reduce(y)` gets transformed to // `all_reduce(x + y)`. // CHECK-LABEL: func.func @all_reduce_arith_addf_endomorphism func.func @all_reduce_arith_addf_endomorphism( // CHECK-SAME: %[[ARG0:[A-Za-z0-9_]*]]: tensor<5xf32> %arg0: tensor<5xf32>, // CHECK-SAME: %[[ARG1:[A-Za-z0-9_]*]]: tensor<5xf32> %arg1: tensor<5xf32>) -> tensor<5xf32> { %0 = shard.all_reduce %arg0 on @grid0 grid_axes = [0] : tensor<5xf32> -> tensor<5xf32> %1 = shard.all_reduce %arg1 on @grid0 grid_axes = [0] : tensor<5xf32> -> tensor<5xf32> // CHECK: %[[ADD_RES:[A-Za-z0-9_]*]] = arith.addf %[[ARG0]], %[[ARG1]] %2 = arith.addf %0, %1 : tensor<5xf32> // CHECK: %[[ALL_REDUCE_RES:[A-Za-z0-9_]*]] = shard.all_reduce %[[ADD_RES]] // CHECK: return %[[ALL_REDUCE_RES]] return %2 : tensor<5xf32> } // CHECK-LABEL: func.func @all_reduce_arith_addf_endomorphism_multiple_uses_of_result func.func @all_reduce_arith_addf_endomorphism_multiple_uses_of_result( // CHECK-SAME: %[[ARG0:[A-Za-z0-9_]*]]: tensor<5xf32> %arg0: tensor<5xf32>, // CHECK-SAME: %[[ARG1:[A-Za-z0-9_]*]]: tensor<5xf32> %arg1: tensor<5xf32>) -> (tensor<5xf32>, tensor<5xf32>) { %0 = shard.all_reduce %arg0 on @grid0 grid_axes = [0] : tensor<5xf32> -> tensor<5xf32> %1 = shard.all_reduce %arg1 on @grid0 grid_axes = [0] : tensor<5xf32> -> tensor<5xf32> // CHECK: %[[ADD_RES:[A-Za-z0-9_]*]] = arith.addf %[[ARG0]], %[[ARG1]] %2 = arith.addf %0, %1 : tensor<5xf32> // CHECK: %[[ALL_REDUCE_RES:[A-Za-z0-9_]*]] = shard.all_reduce %[[ADD_RES]] // CHECK: return %[[ALL_REDUCE_RES]], %[[ALL_REDUCE_RES]] return %2, %2 : tensor<5xf32>, tensor<5xf32> } // Do not simplify if there is another use of one of the all-reduces. // CHECK-LABEL: func.func @all_reduce_arith_addf_endomorphism_multiple_uses_of_all_reduce_result func.func @all_reduce_arith_addf_endomorphism_multiple_uses_of_all_reduce_result( // CHECK-SAME: %[[ARG0:[A-Za-z0-9_]*]]: tensor<5xf32> %arg0: tensor<5xf32>, // CHECK-SAME: %[[ARG1:[A-Za-z0-9_]*]]: tensor<5xf32> %arg1: tensor<5xf32>) -> (tensor<5xf32>, tensor<5xf32>) { // CHECK: %[[ALL_REDUCE_0_RES:[A-Za-z0-9_]*]] = shard.all_reduce %[[ARG0]] %0 = shard.all_reduce %arg0 on @grid0 grid_axes = [0] : tensor<5xf32> -> tensor<5xf32> // CHECK: %[[ALL_REDUCE_1_RES:[A-Za-z0-9_]*]] = shard.all_reduce %[[ARG1]] %1 = shard.all_reduce %arg1 on @grid0 grid_axes = [0] : tensor<5xf32> -> tensor<5xf32> // CHECK: %[[ADD_RES:[A-Za-z0-9_]*]] = arith.addf %[[ALL_REDUCE_0_RES]], %[[ALL_REDUCE_1_RES]] %2 = arith.addf %0, %1 : tensor<5xf32> // CHECK: return %[[ALL_REDUCE_0_RES]], %[[ADD_RES]] return %0, %2 : tensor<5xf32>, tensor<5xf32> } // CHECK-LABEL: func.func @all_reduce_arith_addf_no_endomorphism_different_grid func.func @all_reduce_arith_addf_no_endomorphism_different_grid( // CHECK-SAME: %[[ARG0:[A-Za-z0-9_]*]]: tensor<5xf32> %arg0: tensor<5xf32>, // CHECK-SAME: %[[ARG1:[A-Za-z0-9_]*]]: tensor<5xf32> %arg1: tensor<5xf32>) -> tensor<5xf32> { // CHECK: %[[ALL_REDUCE0:[A-Za-z0-9_]*]] = shard.all_reduce %[[ARG0]] on @grid0 %0 = shard.all_reduce %arg0 on @grid0 grid_axes = [0] : tensor<5xf32> -> tensor<5xf32> // CHECK: %[[ALL_REDUCE1:[A-Za-z0-9_]*]] = shard.all_reduce %[[ARG1]] on @grid1 %1 = shard.all_reduce %arg1 on @grid1 grid_axes = [0] : tensor<5xf32> -> tensor<5xf32> // CHECK: %[[ADD_RES:[A-Za-z0-9_]*]] = arith.addf %[[ALL_REDUCE0]], %[[ALL_REDUCE1]] %2 = arith.addf %0, %1 : tensor<5xf32> // CHECK: return %[[ADD_RES]] return %2 : tensor<5xf32> } // CHECK-LABEL: func.func @all_reduce_arith_addf_no_endomorphism_different_grid_axes func.func @all_reduce_arith_addf_no_endomorphism_different_grid_axes( // CHECK-SAME: %[[ARG0:[A-Za-z0-9_]*]]: tensor<5xf32> %arg0: tensor<5xf32>, // CHECK-SAME: %[[ARG1:[A-Za-z0-9_]*]]: tensor<5xf32> %arg1: tensor<5xf32>) -> tensor<5xf32> { // CHECK: %[[ALL_REDUCE0:[A-Za-z0-9_]*]] = shard.all_reduce %[[ARG0]] on @grid0 grid_axes = [0] %0 = shard.all_reduce %arg0 on @grid0 grid_axes = [0] : tensor<5xf32> -> tensor<5xf32> // CHECK: %[[ALL_REDUCE1:[A-Za-z0-9_]*]] = shard.all_reduce %[[ARG1]] on @grid0 grid_axes = [1] %1 = shard.all_reduce %arg1 on @grid0 grid_axes = [1] : tensor<5xf32> -> tensor<5xf32> // CHECK: %[[ADD_RES:[A-Za-z0-9_]*]] = arith.addf %[[ALL_REDUCE0]], %[[ALL_REDUCE1]] %2 = arith.addf %0, %1 : tensor<5xf32> // CHECK: return %[[ADD_RES]] return %2 : tensor<5xf32> } // CHECK-LABEL: func.func @all_reduce_arith_addf_no_endomorphism_wrong_reduction_kind func.func @all_reduce_arith_addf_no_endomorphism_wrong_reduction_kind( // CHECK-SAME: %[[ARG0:[A-Za-z0-9_]*]]: tensor<5xf32> %arg0: tensor<5xf32>, // CHECK-SAME: %[[ARG1:[A-Za-z0-9_]*]]: tensor<5xf32> %arg1: tensor<5xf32>) -> tensor<5xf32> { // CHECK: %[[ALL_REDUCE0:[A-Za-z0-9_]*]] = shard.all_reduce %[[ARG0]] on @grid0 grid_axes = [0] reduction = max %0 = shard.all_reduce %arg0 on @grid0 grid_axes = [0] reduction = max : tensor<5xf32> -> tensor<5xf32> // CHECK: %[[ALL_REDUCE1:[A-Za-z0-9_]*]] = shard.all_reduce %[[ARG1]] on @grid0 grid_axes = [0] %1 = shard.all_reduce %arg1 on @grid0 grid_axes = [0] : tensor<5xf32> -> tensor<5xf32> // CHECK: %[[ADD_RES:[A-Za-z0-9_]*]] = arith.addf %[[ALL_REDUCE0]], %[[ALL_REDUCE1]] %2 = arith.addf %0, %1 : tensor<5xf32> // CHECK: return %[[ADD_RES]] return %2 : tensor<5xf32> } // CHECK-LABEL: func.func @all_reduce_arith_addf_no_endomorphism_different_operand_result_element_types func.func @all_reduce_arith_addf_no_endomorphism_different_operand_result_element_types( // CHECK-SAME: %[[ARG0:[A-Za-z0-9_]*]]: tensor<5xf32> %arg0: tensor<5xf32>, // CHECK-SAME: %[[ARG1:[A-Za-z0-9_]*]]: tensor<5xf32> %arg1: tensor<5xf32>) -> tensor<5xf64> { // CHECK: %[[ALL_REDUCE0:[A-Za-z0-9_]*]] = shard.all_reduce %[[ARG0]] on @grid0 grid_axes = [0] %0 = shard.all_reduce %arg0 on @grid0 grid_axes = [0] : tensor<5xf32> -> tensor<5xf64> // CHECK: %[[ALL_REDUCE1:[A-Za-z0-9_]*]] = shard.all_reduce %[[ARG1]] on @grid0 grid_axes = [0] %1 = shard.all_reduce %arg1 on @grid0 grid_axes = [0] : tensor<5xf32> -> tensor<5xf64> // CHECK: %[[ADD_RES:[A-Za-z0-9_]*]] = arith.addf %[[ALL_REDUCE0]], %[[ALL_REDUCE1]] %2 = arith.addf %0, %1 : tensor<5xf64> // CHECK: return %[[ADD_RES]] return %2 : tensor<5xf64> } // Checks that `min(all_reduce(x), all_reduce(y))` gets transformed to // `all_reduce(min(x, y))`. // CHECK-LABEL: func.func @all_reduce_arith_minimumf_endomorphism func.func @all_reduce_arith_minimumf_endomorphism( // CHECK-SAME: %[[ARG0:[A-Za-z0-9_]*]]: tensor<5xf32> %arg0: tensor<5xf32>, // CHECK-SAME: %[[ARG1:[A-Za-z0-9_]*]]: tensor<5xf32> %arg1: tensor<5xf32>) -> tensor<5xf32> { %0 = shard.all_reduce %arg0 on @grid0 grid_axes = [0] reduction = min : tensor<5xf32> -> tensor<5xf32> %1 = shard.all_reduce %arg1 on @grid0 grid_axes = [0] reduction = min : tensor<5xf32> -> tensor<5xf32> // CHECK: %[[ADD_RES:[A-Za-z0-9_]*]] = arith.minimumf %[[ARG0]], %[[ARG1]] %2 = arith.minimumf %0, %1 : tensor<5xf32> // CHECK: %[[ALL_REDUCE_RES:[A-Za-z0-9_]*]] = shard.all_reduce %[[ADD_RES]] on @grid0 grid_axes = [0] reduction = min // CHECK: return %[[ALL_REDUCE_RES]] return %2 : tensor<5xf32> } // CHECK-LABEL: func.func @all_reduce_arith_minsi_endomorphism func.func @all_reduce_arith_minsi_endomorphism( // CHECK-SAME: %[[ARG0:[A-Za-z0-9_]*]]: tensor<5xi32> %arg0: tensor<5xi32>, // CHECK-SAME: %[[ARG1:[A-Za-z0-9_]*]]: tensor<5xi32> %arg1: tensor<5xi32>) -> tensor<5xi32> { %0 = shard.all_reduce %arg0 on @grid0 grid_axes = [0] reduction = min : tensor<5xi32> -> tensor<5xi32> %1 = shard.all_reduce %arg1 on @grid0 grid_axes = [0] reduction = min : tensor<5xi32> -> tensor<5xi32> // CHECK: %[[ADD_RES:[A-Za-z0-9_]*]] = arith.minsi %[[ARG0]], %[[ARG1]] %2 = arith.minsi %0, %1 : tensor<5xi32> // CHECK: %[[ALL_REDUCE_RES:[A-Za-z0-9_]*]] = shard.all_reduce %[[ADD_RES]] on @grid0 grid_axes = [0] reduction = min // CHECK: return %[[ALL_REDUCE_RES]] return %2 : tensor<5xi32> } // Ensure this case without endomorphism op not crash. // CHECK-LABEL: func.func @no_endomorphism_op func.func @no_endomorphism_op(%arg0: tensor<2xi64>) -> i64 { %c0 = arith.constant 0 : index %c1_i64 = arith.constant 1 : i64 // CHECK: tensor.extract %extracted = tensor.extract %arg0[%c0] : tensor<2xi64> // CHECK: arith.maxsi %0 = arith.maxsi %extracted, %c1_i64 : i64 return %0 : i64 } // ----- // AllReduceOp + AllSliceOp -> ReduceScatterOp tests // ----- // Basic case: all_slice(all_reduce(x)) with matching grid and axes folds // into reduce_scatter. // CHECK-LABEL: func.func @all_reduce_all_slice_to_reduce_scatter func.func @all_reduce_all_slice_to_reduce_scatter( // CHECK-SAME: %[[ARG0:[A-Za-z0-9_]*]]: tensor<4x8xf32> %arg0: tensor<4x8xf32>) -> tensor<1x8xf32> { %0 = shard.all_reduce %arg0 on @grid0 grid_axes = [0] : tensor<4x8xf32> -> tensor<4x8xf32> %1 = shard.all_slice %0 on @grid0 grid_axes = [0] slice_axis = 0 : tensor<4x8xf32> -> tensor<1x8xf32> // CHECK: %[[RS:.*]] = shard.reduce_scatter %[[ARG0]] on @grid0 grid_axes = [0] scatter_dim = 0 // CHECK-SAME: : tensor<4x8xf32> -> tensor<1x8xf32> // CHECK: return %[[RS]] return %1 : tensor<1x8xf32> } // Verify non-default reduction kind is preserved. // CHECK-LABEL: func.func @all_reduce_all_slice_max_reduction func.func @all_reduce_all_slice_max_reduction( // CHECK-SAME: %[[ARG0:[A-Za-z0-9_]*]]: tensor<4x8xf32> %arg0: tensor<4x8xf32>) -> tensor<1x8xf32> { %0 = shard.all_reduce %arg0 on @grid0 grid_axes = [0] reduction = max : tensor<4x8xf32> -> tensor<4x8xf32> %1 = shard.all_slice %0 on @grid0 grid_axes = [0] slice_axis = 0 : tensor<4x8xf32> -> tensor<1x8xf32> // CHECK: %[[RS:.*]] = shard.reduce_scatter %[[ARG0]] on @grid0 grid_axes = [0] reduction = max scatter_dim = 0 // CHECK-SAME: : tensor<4x8xf32> -> tensor<1x8xf32> // CHECK: return %[[RS]] return %1 : tensor<1x8xf32> } // Slice on a different tensor axis than the reduce axes. // CHECK-LABEL: func.func @all_reduce_all_slice_different_slice_axis func.func @all_reduce_all_slice_different_slice_axis( // CHECK-SAME: %[[ARG0:[A-Za-z0-9_]*]]: tensor<4x8xf32> %arg0: tensor<4x8xf32>) -> tensor<4x4xf32> { %0 = shard.all_reduce %arg0 on @grid0 grid_axes = [1] : tensor<4x8xf32> -> tensor<4x8xf32> %1 = shard.all_slice %0 on @grid0 grid_axes = [1] slice_axis = 1 : tensor<4x8xf32> -> tensor<4x4xf32> // CHECK: %[[RS:.*]] = shard.reduce_scatter %[[ARG0]] on @grid0 grid_axes = [1] scatter_dim = 1 // CHECK-SAME: : tensor<4x8xf32> -> tensor<4x4xf32> // CHECK: return %[[RS]] return %1 : tensor<4x4xf32> } // Do not fold when grids differ. // CHECK-LABEL: func.func @all_reduce_all_slice_different_grid func.func @all_reduce_all_slice_different_grid( // CHECK-SAME: %[[ARG0:[A-Za-z0-9_]*]]: tensor<4x8xf32> %arg0: tensor<4x8xf32>) -> tensor<1x8xf32> { // CHECK: %[[AR:.*]] = shard.all_reduce %[[ARG0]] on @grid0 %0 = shard.all_reduce %arg0 on @grid0 grid_axes = [0] : tensor<4x8xf32> -> tensor<4x8xf32> // CHECK: %[[AS:.*]] = shard.all_slice %[[AR]] on @grid1 %1 = shard.all_slice %0 on @grid1 grid_axes = [0] slice_axis = 0 : tensor<4x8xf32> -> tensor<1x8xf32> // CHECK: return %[[AS]] return %1 : tensor<1x8xf32> } // Do not fold when grid_axes differ. // CHECK-LABEL: func.func @all_reduce_all_slice_different_grid_axes func.func @all_reduce_all_slice_different_grid_axes( // CHECK-SAME: %[[ARG0:[A-Za-z0-9_]*]]: tensor<4x8xf32> %arg0: tensor<4x8xf32>) -> tensor<4x4xf32> { // CHECK: %[[AR:.*]] = shard.all_reduce %[[ARG0]] on @grid0 grid_axes = [0] %0 = shard.all_reduce %arg0 on @grid0 grid_axes = [0] : tensor<4x8xf32> -> tensor<4x8xf32> // CHECK: %[[AS:.*]] = shard.all_slice %[[AR]] on @grid0 grid_axes = [1] %1 = shard.all_slice %0 on @grid0 grid_axes = [1] slice_axis = 1 : tensor<4x8xf32> -> tensor<4x4xf32> // CHECK: return %[[AS]] return %1 : tensor<4x4xf32> } // Verify element type conversion is preserved (all_reduce input/output types may differ). // CHECK-LABEL: func.func @all_reduce_all_slice_type_promotion func.func @all_reduce_all_slice_type_promotion( // CHECK-SAME: %[[ARG0:[A-Za-z0-9_]*]]: tensor<4x8xf32> %arg0: tensor<4x8xf32>) -> tensor<1x8xf64> { %0 = shard.all_reduce %arg0 on @grid0 grid_axes = [0] : tensor<4x8xf32> -> tensor<4x8xf64> %1 = shard.all_slice %0 on @grid0 grid_axes = [0] slice_axis = 0 : tensor<4x8xf64> -> tensor<1x8xf64> // CHECK: %[[RS:.*]] = shard.reduce_scatter %[[ARG0]] on @grid0 grid_axes = [0] scatter_dim = 0 // CHECK-SAME: : tensor<4x8xf32> -> tensor<1x8xf64> // CHECK: return %[[RS]] return %1 : tensor<1x8xf64> }