[SPIRV] Unroll wide elementwise vector operations (#25053) I hit this while compiling a JAX training step to WebGPU through IREE in the browser. A `vector<32xf32>` addition survived until SPIR-V conversion and caused compilation to fail. This uses MLIR's existing unrolling patterns in the vector-breakdown pass to split wide elementwise operations into four-element operations. Signed-off-by: anutosh491 <andersonbhat491@gmail.com>
diff --git a/compiler/src/iree/compiler/Codegen/SPIRV/SPIRVBreakDownLargeVector.cpp b/compiler/src/iree/compiler/Codegen/SPIRV/SPIRVBreakDownLargeVector.cpp index c64f7c3..137b735 100644 --- a/compiler/src/iree/compiler/Codegen/SPIRV/SPIRVBreakDownLargeVector.cpp +++ b/compiler/src/iree/compiler/Codegen/SPIRV/SPIRVBreakDownLargeVector.cpp
@@ -144,10 +144,29 @@ struct SPIRVBreakDownLargeVectorPass final : impl::SPIRVBreakDownLargeVectorPassBase<SPIRVBreakDownLargeVectorPass> { + void getDependentDialects(DialectRegistry ®istry) const override { + registry.insert<arith::ArithDialect, vector::VectorDialect>(); + } + void runOnOperation() override { MLIRContext *context = &getContext(); RewritePatternSet patterns(context); patterns.add<BreakDownCastExtractExtend>(context, /*benefits=*/10); + vector::populateVectorUnrollPatterns( + patterns, vector::UnrollVectorOptions().setNativeShapeFn( + [](Operation *op) -> std::optional<SmallVector<int64_t>> { + if (!OpTrait::hasElementwiseMappableTraits(op) || + op->getNumResults() != 1) { + return std::nullopt; + } + auto type = + dyn_cast<VectorType>(op->getResult(0).getType()); + if (!type || type.getRank() != 1 || + type.getDimSize(0) <= 4 || type.getDimSize(0) % 4) { + return std::nullopt; + } + return SmallVector<int64_t>{4}; + })); // Convert vector.extract_strided_slice into a chain of vector.extract and // then a chain of vector.insert ops. This helps to cancel with previous // vector.insert/extract ops, especially for fP16 cases where we have
diff --git a/compiler/src/iree/compiler/Codegen/SPIRV/test/break_down_large_vector.mlir b/compiler/src/iree/compiler/Codegen/SPIRV/test/break_down_large_vector.mlir index 958a2d0..67c5264 100644 --- a/compiler/src/iree/compiler/Codegen/SPIRV/test/break_down_large_vector.mlir +++ b/compiler/src/iree/compiler/Codegen/SPIRV/test/break_down_large_vector.mlir
@@ -84,3 +84,62 @@ // CHECK: %[[AND3:.+]] = arith.andi %[[SHR3]], %[[MASK]] : i32 // CHECK: %[[RES:.+]] = vector.from_elements %[[AND0]], %[[AND1]], %[[AND2]], %[[AND3]] : vector<4xi32> // CHECK: return %[[RES]] : vector<4xi32> + +// ----- + +func.func @bitcast_extract_extend_8_elements(%input: vector<4xi32>) -> vector<8xi32> { + %bitcast = vector.bitcast %input : vector<4xi32> to vector<32xi4> + %extract = vector.extract_strided_slice %bitcast offsets = [8], sizes = [8], strides = [1] : vector<32xi4> to vector<8xi4> + %extend = arith.extui %extract : vector<8xi4> to vector<8xi32> + return %extend : vector<8xi32> +} + +// CHECK-LABEL: func @bitcast_extract_extend_8_elements +// CHECK-SAME: (%[[INPUT:.+]]: vector<4xi32>) +// CHECK: %[[BASE:.+]] = vector.extract %[[INPUT]][1] : i32 from vector<4xi32> +// CHECK-COUNT-8: arith.andi {{.*}} : i32 +// CHECK: %[[RES:.+]] = vector.from_elements {{.*}} : vector<8xi32> +// CHECK: return %[[RES]] : vector<8xi32> + +// ----- + +// CHECK-LABEL: func @large_elementwise_add +// CHECK-NOT: arith.addf {{.*}}vector<32xf32> +// CHECK: arith.addf {{.*}}vector<4xf32> +// CHECK-NOT: arith.addf {{.*}}vector<32xf32> +func.func @large_elementwise_add(%lhs: vector<32xf32>, %rhs: vector<32xf32>) -> vector<4xf32> { + %sum = arith.addf %lhs, %rhs : vector<32xf32> + %part = vector.extract_strided_slice %sum offsets = [8], sizes = [4], strides = [1] : vector<32xf32> to vector<4xf32> + return %part : vector<4xf32> +} + +// ----- + +// CHECK-LABEL: func @native_elementwise_add +// CHECK: arith.addf {{.*}}vector<4xf32> +func.func @native_elementwise_add(%lhs: vector<4xf32>, %rhs: vector<4xf32>) -> vector<4xf32> { + %sum = arith.addf %lhs, %rhs : vector<4xf32> + return %sum : vector<4xf32> +} + +// ----- + +// CHECK-LABEL: func @elementwise_add_16xi8 +// CHECK-COUNT-4: arith.addi {{.*}} : vector<4xi8> +// CHECK-NOT: arith.addi +// CHECK: return {{.*}} : vector<16xi8> +func.func @elementwise_add_16xi8(%lhs: vector<16xi8>, %rhs: vector<16xi8>) -> vector<16xi8> { + %sum = arith.addi %lhs, %rhs : vector<16xi8> + return %sum : vector<16xi8> +} + +// ----- + +// CHECK-LABEL: func @elementwise_add_8xf16 +// CHECK-COUNT-2: arith.addf {{.*}} : vector<4xf16> +// CHECK-NOT: arith.addf +// CHECK: return {{.*}} : vector<8xf16> +func.func @elementwise_add_8xf16(%lhs: vector<8xf16>, %rhs: vector<8xf16>) -> vector<8xf16> { + %sum = arith.addf %lhs, %rhs : vector<8xf16> + return %sum : vector<8xf16> +}