| // RUN: mlir-opt %s -split-input-file -linalg-specialize-generic-ops | FileCheck %s --check-prefix=ALL |
| // RUN: mlir-opt %s -split-input-file -linalg-morph-ops=generic-to-named | FileCheck %s --check-prefix=NAMED,ALL |
| // RUN: mlir-opt %s -split-input-file -linalg-morph-ops=generic-to-category | FileCheck %s --check-prefix=CATEGORY,ALL |
| |
| #umap = affine_map<(d0, d1, d2) -> (d0, d1, d2)> |
| func.func @unary_ops(%A: tensor<?x?x?xf32>, %Out: tensor<?x?x?xf32>) -> tensor<?x?x?xf32> { |
| %4 = linalg.generic |
| {indexing_maps = [#umap, #umap], iterator_types = ["parallel", "parallel","parallel"]} |
| ins(%A : tensor<?x?x?xf32>) outs(%Out : tensor<?x?x?xf32>) { |
| ^bb0(%in: f32, %out: f32): |
| %v = math.floor %in : f32 |
| linalg.yield %v : f32 |
| } -> tensor<?x?x?xf32> |
| %5 = linalg.generic |
| {indexing_maps = [#umap, #umap], iterator_types = ["parallel", "parallel","parallel"]} |
| ins(%4 : tensor<?x?x?xf32>) outs(%Out : tensor<?x?x?xf32>) { |
| ^bb0(%in: f32, %out: f32): |
| %v = arith.negf %in : f32 |
| linalg.yield %v : f32 |
| } -> tensor<?x?x?xf32> |
| %cst_1 = arith.constant 1.0 : f32 |
| %6 = linalg.generic |
| {indexing_maps = [#umap, #umap], iterator_types = ["parallel", "parallel","parallel"]} |
| ins(%5 : tensor<?x?x?xf32>) outs(%Out : tensor<?x?x?xf32>) { |
| ^bb0(%in: f32, %out: f32): |
| %v = arith.divf %cst_1, %in : f32 |
| linalg.yield %v : f32 |
| } -> tensor<?x?x?xf32> |
| %7 = linalg.generic |
| {indexing_maps = [#umap, #umap], iterator_types = ["parallel", "parallel","parallel"]} |
| ins(%6 : tensor<?x?x?xf32>) outs(%Out : tensor<?x?x?xf32>) { |
| ^bb0(%in: f32, %out: f32): |
| %v = math.round %in : f32 |
| linalg.yield %v : f32 |
| } -> tensor<?x?x?xf32> |
| %8 = linalg.generic |
| {indexing_maps = [#umap, #umap], iterator_types = ["parallel", "parallel","parallel"]} |
| ins(%7 : tensor<?x?x?xf32>) outs(%Out : tensor<?x?x?xf32>) { |
| ^bb0(%in: f32, %out: f32): |
| %v = math.sqrt %in : f32 |
| linalg.yield %v : f32 |
| } -> tensor<?x?x?xf32> |
| %9 = linalg.generic |
| {indexing_maps = [#umap, #umap], iterator_types = ["parallel", "parallel","parallel"]} |
| ins(%8 : tensor<?x?x?xf32>) outs(%Out : tensor<?x?x?xf32>) { |
| ^bb0(%in: f32, %out: f32): |
| %v = math.rsqrt %in : f32 |
| linalg.yield %v : f32 |
| } -> tensor<?x?x?xf32> |
| %10 = linalg.generic |
| {indexing_maps = [#umap, #umap], iterator_types = ["parallel", "parallel","parallel"]} |
| ins(%9 : tensor<?x?x?xf32>) outs(%Out : tensor<?x?x?xf32>) { |
| ^bb0(%in: f32, %out: f32): |
| %v = arith.mulf %in, %in : f32 |
| linalg.yield %v : f32 |
| } -> tensor<?x?x?xf32> |
| %11 = linalg.generic |
| {indexing_maps = [#umap, #umap], iterator_types = ["parallel", "parallel","parallel"]} |
| ins(%10 : tensor<?x?x?xf32>) outs(%Out : tensor<?x?x?xf32>) { |
| ^bb0(%in: f32, %out: f32): |
| %v = math.tanh %in : f32 |
| linalg.yield %v : f32 |
| } -> tensor<?x?x?xf32> |
| %12 = linalg.generic |
| {indexing_maps = [#umap, #umap], iterator_types = ["parallel", "parallel","parallel"]} |
| ins(%11 : tensor<?x?x?xf32>) outs(%Out : tensor<?x?x?xf32>) { |
| ^bb0(%in: f32, %out: f32): |
| %v = math.erf %in : f32 |
| linalg.yield %v : f32 |
| } -> tensor<?x?x?xf32> |
| %13 = linalg.generic |
| {indexing_maps = [#umap, #umap], iterator_types = ["parallel", "parallel","parallel"]} |
| ins(%12 : tensor<?x?x?xf32>) outs(%Out : tensor<?x?x?xf32>) { |
| ^bb0(%in: f32, %out: f32): |
| %v = math.sin %in : f32 |
| linalg.yield %v : f32 |
| } -> tensor<?x?x?xf32> |
| %14 = linalg.generic |
| {indexing_maps = [#umap, #umap], iterator_types = ["parallel", "parallel","parallel"]} |
| ins(%13 : tensor<?x?x?xf32>) outs(%Out : tensor<?x?x?xf32>) { |
| ^bb0(%in: f32, %out: f32): |
| %v = math.cos %in : f32 |
| linalg.yield %v : f32 |
| } -> tensor<?x?x?xf32> |
| %15 = linalg.generic |
| {indexing_maps = [#umap, #umap], iterator_types = ["parallel", "parallel","parallel"]} |
| ins(%14 : tensor<?x?x?xf32>) outs(%Out : tensor<?x?x?xf32>) { |
| ^bb0(%in: f32, %out: f32): |
| %v = math.tan %in : f32 |
| linalg.yield %v : f32 |
| } -> tensor<?x?x?xf32> |
| %16 = linalg.generic |
| {indexing_maps = [#umap, #umap], iterator_types = ["parallel", "parallel","parallel"]} |
| ins(%15 : tensor<?x?x?xf32>) outs(%Out : tensor<?x?x?xf32>) { |
| ^bb0(%in: f32, %out: f32): |
| %v = math.acos %in : f32 |
| linalg.yield %v : f32 |
| } -> tensor<?x?x?xf32> |
| %17 = linalg.generic |
| {indexing_maps = [#umap, #umap], iterator_types = ["parallel", "parallel","parallel"]} |
| ins(%16 : tensor<?x?x?xf32>) outs(%Out : tensor<?x?x?xf32>) { |
| ^bb0(%in: f32, %out: f32): |
| %v = math.acosh %in : f32 |
| linalg.yield %v : f32 |
| } -> tensor<?x?x?xf32> |
| %18 = linalg.generic |
| {indexing_maps = [#umap, #umap], iterator_types = ["parallel", "parallel","parallel"]} |
| ins(%17 : tensor<?x?x?xf32>) outs(%Out : tensor<?x?x?xf32>) { |
| ^bb0(%in: f32, %out: f32): |
| %v = math.asin %in : f32 |
| linalg.yield %v : f32 |
| } -> tensor<?x?x?xf32> |
| %19 = linalg.generic |
| {indexing_maps = [#umap, #umap], iterator_types = ["parallel", "parallel","parallel"]} |
| ins(%18 : tensor<?x?x?xf32>) outs(%Out : tensor<?x?x?xf32>) { |
| ^bb0(%in: f32, %out: f32): |
| %v = math.asinh %in : f32 |
| linalg.yield %v : f32 |
| } -> tensor<?x?x?xf32> |
| %20 = linalg.generic |
| {indexing_maps = [#umap, #umap], iterator_types = ["parallel", "parallel","parallel"]} |
| ins(%19 : tensor<?x?x?xf32>) outs(%Out : tensor<?x?x?xf32>) { |
| ^bb0(%in: f32, %out: f32): |
| %v = math.atan %in : f32 |
| linalg.yield %v : f32 |
| } -> tensor<?x?x?xf32> |
| %21 = linalg.generic |
| {indexing_maps = [#umap, #umap], iterator_types = ["parallel", "parallel","parallel"]} |
| ins(%20 : tensor<?x?x?xf32>) outs(%Out : tensor<?x?x?xf32>) { |
| ^bb0(%in: f32, %out: f32): |
| %v = math.atanh %in : f32 |
| linalg.yield %v : f32 |
| } -> tensor<?x?x?xf32> |
| %22 = linalg.generic |
| {indexing_maps = [#umap, #umap], iterator_types = ["parallel", "parallel","parallel"]} |
| ins(%21 : tensor<?x?x?xf32>) outs(%Out : tensor<?x?x?xf32>) { |
| ^bb0(%in: f32, %out: f32): |
| %v = math.log10 %in : f32 |
| linalg.yield %v : f32 |
| } -> tensor<?x?x?xf32> |
| %23 = linalg.generic |
| {indexing_maps = [#umap, #umap], iterator_types = ["parallel", "parallel","parallel"]} |
| ins(%22 : tensor<?x?x?xf32>) outs(%Out : tensor<?x?x?xf32>) { |
| ^bb0(%in: f32, %out: f32): |
| %v = math.log1p %in : f32 |
| linalg.yield %v : f32 |
| } -> tensor<?x?x?xf32> |
| %24 = linalg.generic |
| {indexing_maps = [#umap, #umap], iterator_types = ["parallel", "parallel","parallel"]} |
| ins(%23 : tensor<?x?x?xf32>) outs(%Out : tensor<?x?x?xf32>) { |
| ^bb0(%in: f32, %out: f32): |
| %v = math.log2 %in : f32 |
| linalg.yield %v : f32 |
| } -> tensor<?x?x?xf32> |
| return %24 : tensor<?x?x?xf32> |
| } |
| |
| // ALL-LABEL: unary_ops |
| // ALL-SAME: %[[A:.+]]: tensor<?x?x?xf32>, %[[OUT:.+]]: tensor<?x?x?xf32>) -> tensor<?x?x?xf32> |
| |
| // ALL: %[[RES4:.+]] = linalg.elementwise <floor> |
| // ALL-SAME: ins(%[[A]] : tensor<?x?x?xf32>) |
| // ALL-SAME: outs(%[[OUT]] : tensor<?x?x?xf32>) -> tensor<?x?x?xf32> |
| // ALL: %[[RES5:.+]] = linalg.elementwise <negf> |
| // ALL-SAME: ins(%[[RES4]] : tensor<?x?x?xf32>) |
| // ALL-SAME: outs(%[[OUT]] : tensor<?x?x?xf32>) -> tensor<?x?x?xf32> |
| // ALL: %[[RES6:.+]] = linalg.elementwise <reciprocal> |
| // ALL-SAME: ins(%[[RES5]] : tensor<?x?x?xf32>) |
| // ALL-SAME: outs(%[[OUT]] : tensor<?x?x?xf32>) -> tensor<?x?x?xf32> |
| // ALL: %[[RES7:.+]] = linalg.elementwise <round> |
| // ALL-SAME: ins(%[[RES6]] : tensor<?x?x?xf32>) |
| // ALL-SAME: outs(%[[OUT]] : tensor<?x?x?xf32>) -> tensor<?x?x?xf32> |
| // ALL: %[[RES8:.+]] = linalg.elementwise <sqrt> |
| // ALL-SAME: ins(%[[RES7]] : tensor<?x?x?xf32>) |
| // ALL-SAME: outs(%[[OUT]] : tensor<?x?x?xf32>) -> tensor<?x?x?xf32> |
| // ALL: %[[RES9:.+]] = linalg.elementwise <rsqrt> |
| // ALL-SAME: ins(%[[RES8]] : tensor<?x?x?xf32>) |
| // ALL-SAME: outs(%[[OUT]] : tensor<?x?x?xf32>) -> tensor<?x?x?xf32> |
| // ALL: %[[RES10:.+]] = linalg.elementwise <square> |
| // ALL-SAME: ins(%[[RES9]] : tensor<?x?x?xf32>) |
| // ALL-SAME: outs(%[[OUT]] : tensor<?x?x?xf32>) -> tensor<?x?x?xf32> |
| // ALL: %[[RES11:.+]] = linalg.elementwise <tanh> |
| // ALL-SAME: ins(%[[RES10]] : tensor<?x?x?xf32>) |
| // ALL-SAME: outs(%[[OUT]] : tensor<?x?x?xf32>) -> tensor<?x?x?xf32> |
| // ALL: %[[RES12:.+]] = linalg.elementwise <erf> |
| // ALL-SAME: ins(%[[RES11]] : tensor<?x?x?xf32>) |
| // ALL-SAME: outs(%[[OUT]] : tensor<?x?x?xf32>) -> tensor<?x?x?xf32> |
| // ALL: %[[RES13:.+]] = linalg.elementwise <sin> |
| // ALL-SAME: ins(%[[RES12]] : tensor<?x?x?xf32>) |
| // ALL-SAME: outs(%[[OUT]] : tensor<?x?x?xf32>) -> tensor<?x?x?xf32> |
| // ALL: %[[RES14:.+]] = linalg.elementwise <cos> |
| // ALL-SAME: ins(%[[RES13]] : tensor<?x?x?xf32>) |
| // ALL-SAME: outs(%[[OUT]] : tensor<?x?x?xf32>) -> tensor<?x?x?xf32> |
| // ALL: %[[RES15:.+]] = linalg.elementwise <tan> |
| // ALL-SAME: ins(%[[RES14]] : tensor<?x?x?xf32>) |
| // ALL-SAME: outs(%[[OUT]] : tensor<?x?x?xf32>) -> tensor<?x?x?xf32> |
| // ALL: %[[RES16:.+]] = linalg.elementwise <acos> |
| // ALL-SAME: ins(%[[RES15]] : tensor<?x?x?xf32>) |
| // ALL-SAME: outs(%[[OUT]] : tensor<?x?x?xf32>) -> tensor<?x?x?xf32> |
| // ALL: %[[RES17:.+]] = linalg.elementwise <acosh> |
| // ALL-SAME: ins(%[[RES16]] : tensor<?x?x?xf32>) |
| // ALL-SAME: outs(%[[OUT]] : tensor<?x?x?xf32>) -> tensor<?x?x?xf32> |
| // ALL: %[[RES18:.+]] = linalg.elementwise <asin> |
| // ALL-SAME: ins(%[[RES17]] : tensor<?x?x?xf32>) |
| // ALL-SAME: outs(%[[OUT]] : tensor<?x?x?xf32>) -> tensor<?x?x?xf32> |
| // ALL: %[[RES19:.+]] = linalg.elementwise <asinh> |
| // ALL-SAME: ins(%[[RES18]] : tensor<?x?x?xf32>) |
| // ALL-SAME: outs(%[[OUT]] : tensor<?x?x?xf32>) -> tensor<?x?x?xf32> |
| // ALL: %[[RES20:.+]] = linalg.elementwise <atan> |
| // ALL-SAME: ins(%[[RES19]] : tensor<?x?x?xf32>) |
| // ALL-SAME: outs(%[[OUT]] : tensor<?x?x?xf32>) -> tensor<?x?x?xf32> |
| // ALL: %[[RES21:.+]] = linalg.elementwise <atanh> |
| // ALL-SAME: ins(%[[RES20]] : tensor<?x?x?xf32>) |
| // ALL-SAME: outs(%[[OUT]] : tensor<?x?x?xf32>) -> tensor<?x?x?xf32> |
| // ALL: %[[RES22:.+]] = linalg.elementwise <log10> |
| // ALL-SAME: ins(%[[RES21]] : tensor<?x?x?xf32>) |
| // ALL-SAME: outs(%[[OUT]] : tensor<?x?x?xf32>) -> tensor<?x?x?xf32> |
| // ALL: %[[RES23:.+]] = linalg.elementwise <log1p> |
| // ALL-SAME: ins(%[[RES22]] : tensor<?x?x?xf32>) |
| // ALL-SAME: outs(%[[OUT]] : tensor<?x?x?xf32>) -> tensor<?x?x?xf32> |
| // ALL: %[[RES24:.+]] = linalg.elementwise <log2> |
| // ALL-SAME: ins(%[[RES23]] : tensor<?x?x?xf32>) |
| // ALL-SAME: outs(%[[OUT]] : tensor<?x?x?xf32>) -> tensor<?x?x?xf32> |
| |
| // ----- |
| |
| func.func @unary_ops_non_identity(%A: tensor<?xf32>, %Out: tensor<?x?xf32>) -> tensor<?x?xf32> { |
| %0 = linalg.generic |
| {indexing_maps = [affine_map<(d0, d1) -> (d1)>, affine_map<(d0, d1) -> (d1, d0)>], |
| iterator_types = ["parallel", "parallel"]} |
| ins(%A : tensor<?xf32>) |
| outs(%Out : tensor<?x?xf32>) { |
| ^bb0(%in: f32, %out: f32): |
| %v = math.exp %in : f32 |
| linalg.yield %v : f32 |
| } -> tensor<?x?xf32> |
| return %0 : tensor<?x?xf32> |
| } |
| |
| |
| // ALL-DAG: #[[MAP_BC:.+]] = affine_map<(d0, d1) -> (d1)> |
| // ALL-DAG: #[[MAP_TP:.+]] = affine_map<(d0, d1) -> (d1, d0)> |
| // ALL: unary_ops_non_identity |
| // ALL-SAME: %[[A:.+]]: tensor<?xf32>, %[[OUT:.+]]: tensor<?x?xf32>) -> tensor<?x?xf32> |
| |
| // ALL-NOT: linalg.generic |
| // ALL: linalg.elementwise <exp> |
| // ALL-SAME: indexing_maps = [#[[MAP_BC]], #[[MAP_TP]]] |
| // ALL-SAME: ins(%[[A]] : tensor<?xf32>) |
| // ALL-SAME: outs(%[[OUT]] : tensor<?x?xf32>) -> tensor<?x?xf32> |
| |
| // ----- |
| |
| #map = affine_map<(d0, d1) -> (d0, d1)> |
| func.func @binary_ops_int(%A: tensor<?x?xi32>, %B: tensor<?x?xi32>, |
| %Out: tensor<?x?xi32>) -> tensor<?x?xi32> { |
| %1 = linalg.generic |
| {indexing_maps = [#map, #map, #map], |
| iterator_types = ["parallel", "parallel"]} |
| ins(%A, %B : tensor<?x?xi32>, tensor<?x?xi32>) |
| outs(%Out : tensor<?x?xi32>) { |
| ^bb0(%in: i32, %in_0: i32, %out: i32): |
| %v = arith.subi %in, %in_0 : i32 |
| linalg.yield %v : i32 |
| } -> tensor<?x?xi32> |
| %2 = linalg.generic |
| {indexing_maps = [#map, #map, #map], |
| iterator_types = ["parallel", "parallel"]} |
| ins(%1, %B : tensor<?x?xi32>, tensor<?x?xi32>) |
| outs(%Out : tensor<?x?xi32>) { |
| ^bb0(%in: i32, %in_0: i32, %out: i32): |
| %v = arith.muli %in, %in_0 : i32 |
| linalg.yield %v : i32 |
| } -> tensor<?x?xi32> |
| %3 = linalg.generic |
| {indexing_maps = [#map, #map, #map], |
| iterator_types = ["parallel", "parallel"]} |
| ins(%2, %B : tensor<?x?xi32>, tensor<?x?xi32>) |
| outs(%Out : tensor<?x?xi32>) { |
| ^bb0(%in: i32, %in_0: i32, %out: i32): |
| %v = arith.divsi %in, %in_0 : i32 |
| linalg.yield %v : i32 |
| } -> tensor<?x?xi32> |
| %4 = linalg.generic |
| {indexing_maps = [#map, #map, #map], |
| iterator_types = ["parallel", "parallel"]} |
| ins(%3, %B : tensor<?x?xi32>, tensor<?x?xi32>) |
| outs(%Out : tensor<?x?xi32>) { |
| ^bb0(%in: i32, %in_0: i32, %out: i32): |
| %v = arith.divui %in, %in_0 : i32 |
| linalg.yield %v : i32 |
| } -> tensor<?x?xi32> |
| %5 = linalg.generic |
| {indexing_maps = [#map, #map, #map], |
| iterator_types = ["parallel", "parallel"]} |
| ins(%4, %B : tensor<?x?xi32>, tensor<?x?xi32>) |
| outs(%Out : tensor<?x?xi32>) { |
| ^bb0(%in: i32, %in_0: i32, %out: i32): |
| %v = arith.maxsi %in, %in_0 : i32 |
| linalg.yield %v : i32 |
| } -> tensor<?x?xi32> |
| %6 = linalg.generic |
| {indexing_maps = [#map, #map, #map], |
| iterator_types = ["parallel", "parallel"]} |
| ins(%5, %B : tensor<?x?xi32>, tensor<?x?xi32>) |
| outs(%Out : tensor<?x?xi32>) { |
| ^bb0(%in: i32, %in_0: i32, %out: i32): |
| %v = arith.minsi %in, %in_0 : i32 |
| linalg.yield %v : i32 |
| } -> tensor<?x?xi32> |
| return %6 : tensor<?x?xi32> |
| } |
| |
| // ALL-LABEL: binary_ops_int |
| // ALL-SAME: %[[A:.+]]: [[TTY:tensor<\?x\?xi32>]], %[[B:.+]]: [[TTY]], |
| // ALL-SAME: %[[OUT:.+]]: [[TTY]]) -> [[TTY]] |
| |
| // ALL-NOT: linalg.generic |
| // ALL: %[[RES1:.+]] = linalg.elementwise <sub> |
| // ALL-SAME: ins(%[[A]], %[[B]] : [[TTY]], [[TTY]]) |
| // ALL-SAME: outs(%[[OUT]] : [[TTY]]) -> [[TTY]] |
| // ALL: %[[RES2:.+]] = linalg.elementwise <mul> |
| // ALL-SAME: ins(%[[RES1]], %[[B]] : [[TTY]], [[TTY]]) |
| // ALL-SAME: outs(%[[OUT]] : [[TTY]]) -> [[TTY]] |
| // ALL: %[[RES3:.+]] = linalg.elementwise <div> |
| // ALL-SAME: ins(%[[RES2]], %[[B]] : [[TTY]], [[TTY]]) |
| // ALL-SAME: outs(%[[OUT]] : [[TTY]]) -> [[TTY]] |
| // ALL: %[[RES4:.+]] = linalg.elementwise <div_unsigned> |
| // ALL-SAME: ins(%[[RES3]], %[[B]] : [[TTY]], [[TTY]]) |
| // ALL-SAME: outs(%[[OUT]] : [[TTY]]) -> [[TTY]] |
| // ALL: %[[RES5:.+]] = linalg.elementwise <max_signed> |
| // ALL-SAME: ins(%[[RES4]], %[[B]] : [[TTY]], [[TTY]]) |
| // ALL-SAME: outs(%[[OUT]] : [[TTY]]) -> [[TTY]] |
| // ALL: %[[RES6:.+]] = linalg.elementwise <min_signed> |
| // ALL-SAME: ins(%[[RES5]], %[[B]] : [[TTY]], [[TTY]]) |
| // ALL-SAME: outs(%[[OUT]] : [[TTY]]) -> [[TTY]] |
| |
| // ----- |
| |
| #map = affine_map<(d0, d1) -> (d0, d1)> |
| func.func @binary_ops_float(%A: tensor<?x?xf32>, %B: tensor<?x?xf32>, |
| %Out: tensor<?x?xf32>) -> tensor<?x?xf32> { |
| %1 = linalg.generic |
| {indexing_maps = [#map, #map, #map], |
| iterator_types = ["parallel", "parallel"]} |
| ins(%A, %B : tensor<?x?xf32>, tensor<?x?xf32>) |
| outs(%Out : tensor<?x?xf32>) { |
| ^bb0(%in: f32, %in_0: f32, %out: f32): |
| %v = arith.subf %in, %in_0 : f32 |
| linalg.yield %v : f32 |
| } -> tensor<?x?xf32> |
| %2 = linalg.generic |
| {indexing_maps = [#map, #map, #map], |
| iterator_types = ["parallel", "parallel"]} |
| ins(%1, %B : tensor<?x?xf32>, tensor<?x?xf32>) |
| outs(%Out : tensor<?x?xf32>) { |
| ^bb0(%in: f32, %in_0: f32, %out: f32): |
| %v = arith.mulf %in, %in_0 : f32 |
| linalg.yield %v : f32 |
| } -> tensor<?x?xf32> |
| %3 = linalg.generic |
| {indexing_maps = [#map, #map, #map], |
| iterator_types = ["parallel", "parallel"]} |
| ins(%2, %B : tensor<?x?xf32>, tensor<?x?xf32>) |
| outs(%Out : tensor<?x?xf32>) { |
| ^bb0(%in: f32, %in_0: f32, %out: f32): |
| %v = arith.divf %in, %in_0 : f32 |
| linalg.yield %v : f32 |
| } -> tensor<?x?xf32> |
| %4 = linalg.generic |
| {indexing_maps = [#map, #map, #map], |
| iterator_types = ["parallel", "parallel"]} |
| ins(%3, %B : tensor<?x?xf32>, tensor<?x?xf32>) |
| outs(%Out : tensor<?x?xf32>) { |
| ^bb0(%in: f32, %in_0: f32, %out: f32): |
| %v = arith.maximumf %in, %in_0 : f32 |
| linalg.yield %v : f32 |
| } -> tensor<?x?xf32> |
| %5 = linalg.generic |
| {indexing_maps = [#map, #map, #map], |
| iterator_types = ["parallel", "parallel"]} |
| ins(%4, %B : tensor<?x?xf32>, tensor<?x?xf32>) |
| outs(%Out : tensor<?x?xf32>) { |
| ^bb0(%in: f32, %in_0: f32, %out: f32): |
| %v = arith.minimumf %in, %in_0 : f32 |
| linalg.yield %v : f32 |
| } -> tensor<?x?xf32> |
| %6 = linalg.generic |
| {indexing_maps = [#map, #map, #map], |
| iterator_types = ["parallel", "parallel"]} |
| ins(%5, %B : tensor<?x?xf32>, tensor<?x?xf32>) |
| outs(%Out : tensor<?x?xf32>) { |
| ^bb0(%in: f32, %in_0: f32, %out: f32): |
| %v = math.powf %in, %in_0 : f32 |
| linalg.yield %v : f32 |
| } -> tensor<?x?xf32> |
| return %6 : tensor<?x?xf32> |
| } |
| |
| // ALL-LABEL: binary_ops_float |
| // ALL-SAME: %[[A:.+]]: [[TTY:tensor<\?x\?xf32>]], %[[B:.+]]: [[TTY]], |
| // ALL-SAME: %[[OUT:.+]]: [[TTY]]) -> [[TTY]] |
| |
| // ALL-NOT: linalg.generic |
| // ALL: %[[RES1:.+]] = linalg.elementwise <sub> |
| // ALL-SAME: ins(%[[A]], %[[B]] : [[TTY]], [[TTY]]) |
| // ALL-SAME: outs(%[[OUT]] : [[TTY]]) -> [[TTY]] |
| // ALL: %[[RES2:.+]] = linalg.elementwise <mul> |
| // ALL-SAME: ins(%[[RES1]], %[[B]] : [[TTY]], [[TTY]]) |
| // ALL-SAME: outs(%[[OUT]] : [[TTY]]) -> [[TTY]] |
| // ALL: %[[RES3:.+]] = linalg.elementwise <div> |
| // ALL-SAME: ins(%[[RES2]], %[[B]] : [[TTY]], [[TTY]]) |
| // ALL-SAME: outs(%[[OUT]] : [[TTY]]) -> [[TTY]] |
| // ALL: %[[RES4:.+]] = linalg.elementwise <max_signed> |
| // ALL-SAME: ins(%[[RES3]], %[[B]] : [[TTY]], [[TTY]]) |
| // ALL-SAME: outs(%[[OUT]] : [[TTY]]) -> [[TTY]] |
| // ALL: %[[RES5:.+]] = linalg.elementwise <min_signed> |
| // ALL-SAME: ins(%[[RES4]], %[[B]] : [[TTY]], [[TTY]]) |
| // ALL-SAME: outs(%[[OUT]] : [[TTY]]) -> [[TTY]] |
| // ALL: %[[RES6:.+]] = linalg.elementwise <powf> |
| // ALL-SAME: ins(%[[RES5]], %[[B]] : [[TTY]], [[TTY]]) |
| // ALL-SAME: outs(%[[OUT]] : [[TTY]]) -> [[TTY]] |
| |
| // ----- |
| |
| #map = affine_map<(d0, d1) -> (d0, d1)> |
| func.func @binary_ops_complex(%A: tensor<?x?xcomplex<f32>>, |
| %B: tensor<?x?xcomplex<f32>>, |
| %Out: tensor<?x?xcomplex<f32>>) |
| -> tensor<?x?xcomplex<f32>> { |
| %1 = linalg.generic |
| {indexing_maps = [#map, #map, #map], |
| iterator_types = ["parallel", "parallel"]} |
| ins(%A, %B : tensor<?x?xcomplex<f32>>, tensor<?x?xcomplex<f32>>) |
| outs(%Out : tensor<?x?xcomplex<f32>>) { |
| ^bb0(%in: complex<f32>, %in_0: complex<f32>, %out: complex<f32>): |
| %v = complex.sub %in, %in_0 : complex<f32> |
| linalg.yield %v : complex<f32> |
| } -> tensor<?x?xcomplex<f32>> |
| %2 = linalg.generic |
| {indexing_maps = [#map, #map, #map], |
| iterator_types = ["parallel", "parallel"]} |
| ins(%1, %B : tensor<?x?xcomplex<f32>>, tensor<?x?xcomplex<f32>>) |
| outs(%Out : tensor<?x?xcomplex<f32>>) { |
| ^bb0(%in: complex<f32>, %in_0: complex<f32>, %out: complex<f32>): |
| %v = complex.mul %in, %in_0 : complex<f32> |
| linalg.yield %v : complex<f32> |
| } -> tensor<?x?xcomplex<f32>> |
| %3 = linalg.generic |
| {indexing_maps = [#map, #map, #map], |
| iterator_types = ["parallel", "parallel"]} |
| ins(%2, %B : tensor<?x?xcomplex<f32>>, tensor<?x?xcomplex<f32>>) |
| outs(%Out : tensor<?x?xcomplex<f32>>) { |
| ^bb0(%in: complex<f32>, %in_0: complex<f32>, %out: complex<f32>): |
| %v = complex.div %in, %in_0 : complex<f32> |
| linalg.yield %v : complex<f32> |
| } -> tensor<?x?xcomplex<f32>> |
| return %3 : tensor<?x?xcomplex<f32>> |
| } |
| |
| // ALL-LABEL: binary_ops_complex |
| // ALL-SAME: %[[A:.+]]: [[TTY:tensor<\?x\?xcomplex<f32>>]], %[[B:.+]]: [[TTY]], |
| // ALL-SAME: %[[OUT:.+]]: [[TTY]]) -> [[TTY]] |
| |
| // ALL-NOT: linalg.generic |
| // ALL: %[[RES1:.+]] = linalg.elementwise <sub> |
| // ALL-SAME: ins(%[[A]], %[[B]] : [[TTY]], [[TTY]]) |
| // ALL-SAME: outs(%[[OUT]] : [[TTY]]) -> [[TTY]] |
| // ALL: %[[RES2:.+]] = linalg.elementwise <mul> |
| // ALL-SAME: ins(%[[RES1]], %[[B]] : [[TTY]], [[TTY]]) |
| // ALL-SAME: outs(%[[OUT]] : [[TTY]]) -> [[TTY]] |
| // ALL: %[[RES3:.+]] = linalg.elementwise <div> |
| // ALL-SAME: ins(%[[RES2]], %[[B]] : [[TTY]], [[TTY]]) |
| // ALL-SAME: outs(%[[OUT]] : [[TTY]]) -> [[TTY]] |
| |
| // ----- |
| |
| #map = affine_map<(d0, d1) -> (d0, d1)> |
| func.func @binary_ops_bool(%A: tensor<?x?xi1>, %B: tensor<?x?xi1>, |
| %Out: tensor<?x?xi1>) -> tensor<?x?xi1> { |
| %1 = linalg.generic |
| {indexing_maps = [#map, #map, #map], |
| iterator_types = ["parallel", "parallel"]} |
| ins(%A, %B : tensor<?x?xi1>, tensor<?x?xi1>) |
| outs(%Out : tensor<?x?xi1>) { |
| ^bb0(%in: i1, %in_0: i1, %out: i1): |
| %v = arith.andi %in, %in_0 : i1 |
| linalg.yield %v : i1 |
| } -> tensor<?x?xi1> |
| return %1 : tensor<?x?xi1> |
| } |
| |
| // ALL-LABEL: binary_ops_bool |
| // ALL-SAME: %[[A:.+]]: [[TTY:tensor<\?x\?xi1>]], %[[B:.+]]: [[TTY]], |
| // ALL-SAME: %[[OUT:.+]]: [[TTY]]) -> [[TTY]] |
| |
| // ALL-NOT: linalg.generic |
| // ALL: %[[RES1:.+]] = linalg.elementwise <mul> |
| // ALL-SAME: ins(%[[A]], %[[B]] : [[TTY]], [[TTY]]) |
| // ALL-SAME: outs(%[[OUT]] : [[TTY]]) -> [[TTY]] |
| |
| // ----- |
| |
| #map = affine_map<(d0, d1) -> (d0, d1)> |
| func.func @binary_ops_uint(%A: tensor<?x?xi32>, %B: tensor<?x?xi32>, |
| %Out: tensor<?x?xi32>) -> tensor<?x?xi32> { |
| %0 = linalg.generic |
| {indexing_maps = [#map, #map, #map], |
| iterator_types = ["parallel", "parallel"]} |
| ins(%A, %B : tensor<?x?xi32>, tensor<?x?xi32>) |
| outs(%Out : tensor<?x?xi32>) { |
| ^bb0(%in: i32, %in_0: i32, %out: i32): |
| %v = arith.maxui %in, %in_0 : i32 |
| linalg.yield %v : i32 |
| } -> tensor<?x?xi32> |
| %1 = linalg.generic |
| {indexing_maps = [#map, #map, #map], |
| iterator_types = ["parallel", "parallel"]} |
| ins(%0, %B : tensor<?x?xi32>, tensor<?x?xi32>) |
| outs(%Out : tensor<?x?xi32>) { |
| ^bb0(%in: i32, %in_0: i32, %out: i32): |
| %v = arith.minui %in, %in_0 : i32 |
| linalg.yield %v : i32 |
| } -> tensor<?x?xi32> |
| return %1 : tensor<?x?xi32> |
| } |
| |
| // ALL-LABEL: binary_ops_uint |
| // ALL-SAME: %[[A:.+]]: [[TTY:tensor<\?x\?xi32>]], %[[B:.+]]: [[TTY]], |
| // ALL-SAME: %[[OUT:.+]]: [[TTY]]) -> [[TTY]] |
| |
| // ----- |
| |
| func.func @binary_ops_non_identity(%A: tensor<?xf32>, %B: tensor<?x?xf32>, |
| %Out: tensor<?x?xf32>) -> tensor<?x?xf32> { |
| %0 = linalg.generic |
| {indexing_maps = [affine_map<(d0, d1) -> (d1)>, affine_map<(d0, d1) -> (d1, d0)>, |
| affine_map<(d0, d1) -> (d0, d1)>], |
| iterator_types = ["parallel", "parallel"]} |
| ins(%A, %B : tensor<?xf32>, tensor<?x?xf32>) |
| outs(%Out : tensor<?x?xf32>) { |
| ^bb0(%in: f32, %in_0: f32, %out: f32): |
| %v = arith.subf %in, %in_0 : f32 |
| linalg.yield %v : f32 |
| } -> tensor<?x?xf32> |
| return %0 : tensor<?x?xf32> |
| } |
| |
| // ALL-DAG: #[[MAP_BC:.+]] = affine_map<(d0, d1) -> (d1)> |
| // ALL-DAG: #[[MAP_TP:.+]] = affine_map<(d0, d1) -> (d1, d0)> |
| // ALL-DAG: #[[MAP_ID:.+]] = affine_map<(d0, d1) -> (d0, d1)> |
| // ALL: binary_ops_non_identity |
| // ALL-SAME: %[[A:.+]]: [[TTY1D:tensor<\?xf32>]], %[[B:.+]]: [[TTY:tensor<\?x\?xf32>]], |
| // ALL-SAME: %[[OUT:.+]]: [[TTY]]) -> [[TTY]] |
| |
| // ALL-NOT: linalg.generic |
| // ALL: linalg.elementwise <sub> |
| // ALL-SAME: indexing_maps = [#[[MAP_BC]], #[[MAP_TP]], #[[MAP_ID]]] |
| // ALL-SAME: ins(%[[A]], %[[B]] : [[TTY1D]], [[TTY]]) |
| // ALL-SAME: outs(%[[OUT]] : [[TTY]]) -> [[TTY]] |
| |
| // ----- |
| |
| #map = affine_map<(d0, d1) -> (d0, d1)> |
| #bcast = affine_map<(d0, d1) -> (d0)> |
| func.func @binary_ops_swapped(%A: tensor<?x?xf32>, %B: tensor<?x?xf32>, |
| %C: tensor<?xf32>, %Out: tensor<?x?xf32>) |
| -> tensor<?x?xf32> { |
| %0 = linalg.generic |
| {indexing_maps = [#map, #map, #map], |
| iterator_types = ["parallel", "parallel"]} |
| ins(%A, %B : tensor<?x?xf32>, tensor<?x?xf32>) |
| outs(%Out : tensor<?x?xf32>) { |
| ^bb0(%in: f32, %in_0: f32, %out: f32): |
| %v = arith.mulf %in_0, %in : f32 |
| linalg.yield %v : f32 |
| } -> tensor<?x?xf32> |
| %1 = linalg.generic |
| {indexing_maps = [#map, #bcast, #map], |
| iterator_types = ["parallel", "parallel"]} |
| ins(%0, %C : tensor<?x?xf32>, tensor<?xf32>) |
| outs(%Out : tensor<?x?xf32>) { |
| ^bb0(%in: f32, %in_0: f32, %out: f32): |
| %v = arith.subf %in_0, %in : f32 |
| linalg.yield %v : f32 |
| } -> tensor<?x?xf32> |
| return %1 : tensor<?x?xf32> |
| } |
| |
| // ALL-DAG: #[[MAP_BC:.+]] = affine_map<(d0, d1) -> (d0)> |
| // ALL-DAG: #[[MAP_ID:.+]] = affine_map<(d0, d1) -> (d0, d1)> |
| // ALL: binary_ops_swapped |
| // ALL-SAME: %[[A:.+]]: [[TTY:tensor<\?x\?xf32>]], %[[B:.+]]: [[TTY]], |
| // ALL-SAME: %[[C:.+]]: [[TTY1D:tensor<\?xf32>]], |
| // ALL-SAME: %[[OUT:.+]]: [[TTY]]) -> [[TTY]] |
| |
| // ALL-NOT: linalg.generic |
| // ALL: %[[RES0:.+]] = linalg.elementwise <mul> |
| // ALL-SAME: ins(%[[B]], %[[A]] : [[TTY]], [[TTY]]) |
| // ALL-SAME: outs(%[[OUT]] : [[TTY]]) -> [[TTY]] |
| // ALL: %[[RES1:.+]] = linalg.elementwise <sub> |
| // ALL-SAME: indexing_maps = [#[[MAP_BC]], #[[MAP_ID]], #[[MAP_ID]]] |
| // ALL-SAME: ins(%[[C]], %[[RES0]] : [[TTY1D]], [[TTY]]) |
| // ALL-SAME: outs(%[[OUT]] : [[TTY]]) -> [[TTY]] |
| |
| // ----- |
| |
| #map = affine_map<(d0) -> (d0)> |
| func.func @unary_op_with_scalar(%A: tensor<?xi32>, %Out: tensor<?xi32>) |
| -> tensor<?xi32> { |
| %cst = arith.constant 123 : i32 |
| %0 = linalg.generic |
| {indexing_maps = [#map, #map], |
| iterator_types = ["parallel"]} |
| ins(%A : tensor<?xi32>) |
| outs(%Out : tensor<?xi32>) { |
| ^bb0(%in: i32, %out: i32): |
| %v = arith.subi %cst, %in : i32 |
| linalg.yield %v : i32 |
| } -> tensor<?xi32> |
| return %0 : tensor<?xi32> |
| } |
| |
| // ALL-DAG: #[[MAP_ID:.+]] = affine_map<(d0) -> (d0)> |
| // ALL-DAG: #[[MAP_BC:.+]] = affine_map<(d0) -> ()> |
| // ALL: unary_op_with_scalar |
| // ALL-SAME: %[[A:.+]]: [[TTY:tensor<\?xi32>]], |
| // ALL-SAME: %[[OUT:.+]]: [[TTY]]) -> [[TTY]] |
| |
| // ALL-NOT: linalg.generic |
| // ALL: %[[CST:.+]] = arith.constant 123 : i32 |
| // ALL: linalg.elementwise <sub> |
| // ALL-SAME: indexing_maps = [#[[MAP_BC]], #[[MAP_ID]], #[[MAP_ID]]] |
| // ALL-SAME: ins(%[[CST]], %[[A]] : i32, [[TTY]]) |
| // ALL-SAME: outs(%[[OUT]] : [[TTY]]) -> [[TTY]] |
| |
| // ----- |
| |
| #map = affine_map<(d0) -> (d0)> |
| func.func @negative_unary_op_using_block_arg_twice(%A: tensor<?xi32>, |
| %Out: tensor<?xi32>) |
| -> tensor<?xi32> { |
| %0 = linalg.generic |
| {indexing_maps = [#map, #map], |
| iterator_types = ["parallel"]} |
| ins(%A : tensor<?xi32>) |
| outs(%Out : tensor<?xi32>) { |
| ^bb0(%in: i32, %out: i32): |
| %v = arith.addi %in, %in : i32 |
| linalg.yield %v : i32 |
| } -> tensor<?xi32> |
| return %0 : tensor<?xi32> |
| } |
| |
| // ALL-LABEL: negative_unary_op_using_block_arg_twice |
| |
| // There is no scalar operand to hoist -> expect no change. |
| // ALL-NOT: linalg.elementwise <add> |
| |
| // ALL: linalg.generic |
| |
| // ----- |
| |
| #map = affine_map<(d0, d1) -> (d0, d1)> |
| func.func @ternary_op_select_f32(%C: tensor<?x?xi1>, %T: tensor<?x?xf32>, |
| %F: tensor<?x?xf32>, %Out: tensor<?x?xf32>) |
| -> tensor<?x?xf32> { |
| %0 = linalg.generic |
| {indexing_maps = [#map, #map, #map, #map], |
| iterator_types = ["parallel", "parallel"]} |
| ins(%C, %T, %F : tensor<?x?xi1>, tensor<?x?xf32>, tensor<?x?xf32>) |
| outs(%Out : tensor<?x?xf32>) { |
| ^bb0(%in: i1, %in_0: f32, %in_1: f32, %out: f32): |
| %v = arith.select %in, %in_0, %in_1 : f32 |
| linalg.yield %v : f32 |
| } -> tensor<?x?xf32> |
| return %0 : tensor<?x?xf32> |
| } |
| |
| // ALL-LABEL: ternary_op_select_f32 |
| // ALL-SAME: %[[C:.+]]: [[ITY:tensor<\?x\?xi1>]], %[[T:.+]]: [[TTY:tensor<\?x\?xf32>]], %[[F:.+]]: [[TTY]], |
| // ALL-SAME: %[[OUT:.+]]: [[TTY]]) -> [[TTY]] |
| |
| // ALL-NOT: linalg.generic |
| // ALL: linalg.elementwise <select> |
| // ALL-SAME: ins(%[[C]], %[[T]], %[[F]] : [[ITY]], [[TTY]], [[TTY]]) |
| // ALL-SAME: outs(%[[OUT]] : [[TTY]]) -> [[TTY]] |
| |
| // ----- |
| |
| #map = affine_map<(d0, d1) -> (d0, d1)> |
| func.func @ternary_op_select_f32_swapped(%C: tensor<?x?xi1>, %T: tensor<?x?xf32>, |
| %F: tensor<?x?xf32>, %Out: tensor<?x?xf32>) |
| -> tensor<?x?xf32> { |
| %0 = linalg.generic |
| {indexing_maps = [#map, #map, #map, #map], |
| iterator_types = ["parallel", "parallel"]} |
| ins(%C, %T, %F : tensor<?x?xi1>, tensor<?x?xf32>, tensor<?x?xf32>) |
| outs(%Out : tensor<?x?xf32>) { |
| ^bb0(%in: i1, %in_0: f32, %in_1: f32, %out: f32): |
| %v = arith.select %in, %in_1, %in_0 : f32 |
| linalg.yield %v : f32 |
| } -> tensor<?x?xf32> |
| return %0 : tensor<?x?xf32> |
| } |
| |
| // ALL-LABEL: ternary_op_select_f32_swapped |
| // ALL-SAME: %[[C:.+]]: [[ITY:tensor<\?x\?xi1>]], %[[T:.+]]: [[TTY:tensor<\?x\?xf32>]], %[[F:.+]]: [[TTY]], |
| // ALL-SAME: %[[OUT:.+]]: [[TTY]]) -> [[TTY]] |
| |
| // ALL-NOT: linalg.generic |
| // ALL: linalg.elementwise <select> |
| // ALL-SAME: ins(%[[C]], %[[F]], %[[T]] : [[ITY]], [[TTY]], [[TTY]]) |
| // ALL-SAME: outs(%[[OUT]] : [[TTY]]) -> [[TTY]] |
| |
| // ----- |
| |
| #map = affine_map<(d0, d1) -> (d0, d1)> |
| func.func @ternary_op_select_i32(%C: tensor<?x?xi1>, %T: tensor<?x?xi32>, |
| %F: tensor<?x?xi32>, %Out: tensor<?x?xi32>) |
| -> tensor<?x?xi32> { |
| %0 = linalg.generic |
| {indexing_maps = [#map, #map, #map, #map], |
| iterator_types = ["parallel", "parallel"]} |
| ins(%C, %T, %F : tensor<?x?xi1>, tensor<?x?xi32>, tensor<?x?xi32>) |
| outs(%Out : tensor<?x?xi32>) { |
| ^bb0(%in: i1, %in_0: i32, %in_1: i32, %out: i32): |
| %v = arith.select %in, %in_0, %in_1 : i32 |
| linalg.yield %v : i32 |
| } -> tensor<?x?xi32> |
| return %0 : tensor<?x?xi32> |
| } |
| |
| // ALL-LABEL: ternary_op_select_i32 |
| // ALL-SAME: %[[C:.+]]: [[ITY:tensor<\?x\?xi1>]], %[[T:.+]]: [[TTY:tensor<\?x\?xi32>]], %[[F:.+]]: [[TTY]], |
| // ALL-SAME: %[[OUT:.+]]: [[TTY]]) -> [[TTY]] |
| |
| // ALL-NOT: linalg.generic |
| // ALL: linalg.elementwise <select> |
| // ALL-SAME: ins(%[[C]], %[[T]], %[[F]] : [[ITY]], [[TTY]], [[TTY]]) |
| // ALL-SAME: outs(%[[OUT]] : [[TTY]]) -> [[TTY]] |
| |
| // ----- |
| |
| #map = affine_map<(d0, d1) -> (d0, d1)> |
| func.func @ternary_op_select_i1(%C: tensor<?x?xi1>, %T: tensor<?x?xi1>, |
| %F: tensor<?x?xi1>, %Out: tensor<?x?xi1>) |
| -> tensor<?x?xi1> { |
| %0 = linalg.generic |
| {indexing_maps = [#map, #map, #map, #map], |
| iterator_types = ["parallel", "parallel"]} |
| ins(%C, %T, %F : tensor<?x?xi1>, tensor<?x?xi1>, tensor<?x?xi1>) |
| outs(%Out : tensor<?x?xi1>) { |
| ^bb0(%in: i1, %in_0: i1, %in_1: i1, %out: i1): |
| %v = arith.select %in, %in_0, %in_1 : i1 |
| linalg.yield %v : i1 |
| } -> tensor<?x?xi1> |
| return %0 : tensor<?x?xi1> |
| } |
| |
| // ALL-LABEL: ternary_op_select_i1 |
| // ALL-SAME: %[[C:.+]]: [[TTY:tensor<\?x\?xi1>]], %[[T:.+]]: [[TTY]], %[[F:.+]]: [[TTY]], |
| // ALL-SAME: %[[OUT:.+]]: [[TTY]]) -> [[TTY]] |
| |
| // ALL-NOT: linalg.generic |
| // ALL: linalg.elementwise <select> |
| // ALL-SAME: ins(%[[C]], %[[T]], %[[F]] : [[TTY]], [[TTY]], [[TTY]]) |
| // ALL-SAME: outs(%[[OUT]] : [[TTY]]) -> [[TTY]] |
| |
| // ----- |
| |
| // Mask comes from outside and is constant |
| // this can be elided completely by canonicalization |
| #map = affine_map<(d0, d1) -> (d0, d1)> |
| func.func @negative_ternary_op_select(%C: tensor<?x?xi1>, %T: tensor<?x?xf32>, |
| %F: tensor<?x?xf32>, %Out: tensor<?x?xf32>) |
| -> tensor<?x?xf32> { |
| %true = arith.constant 1 : i1 |
| %0 = linalg.generic |
| {indexing_maps = [#map, #map, #map, #map], |
| iterator_types = ["parallel", "parallel"]} |
| ins(%C, %T, %F : tensor<?x?xi1>, tensor<?x?xf32>, tensor<?x?xf32>) |
| outs(%Out : tensor<?x?xf32>) { |
| ^bb0(%in: i1, %in_0: f32, %in_1: f32, %out: f32): |
| %v = arith.select %true, %in_0, %in_1 : f32 |
| linalg.yield %v : f32 |
| } -> tensor<?x?xf32> |
| return %0 : tensor<?x?xf32> |
| } |
| |
| // ALL-LABEL: negative_ternary_op_select |
| // ALL-SAME: %[[C:.+]]: [[ITY:tensor<\?x\?xi1>]], %[[T:.+]]: [[TTY:tensor<\?x\?xf32>]], %[[F:.+]]: [[TTY]], |
| // ALL-SAME: %[[OUT:.+]]: [[TTY]]) -> [[TTY]] |
| |
| // ALL: linalg.generic |
| // ALL-NOT: linalg.elementwise <select> |
| |
| // ----- |
| |
| ///---------------------------------------------------------------------------------------- |
| /// Tests for linalg.matmul |
| ///---------------------------------------------------------------------------------------- |
| |
| #map = affine_map<(d0, d1, d2) -> (d0, d2)> |
| #map1 = affine_map<(d0, d1, d2) -> (d2, d1)> |
| #map2 = affine_map<(d0, d1, d2) -> (d0, d1)> |
| func.func @op_matmul(%A: tensor<?x?xf32>, %B: tensor<?x?xf32>, |
| %Out: tensor<?x?xf32>) -> tensor<?x?xf32> { |
| %0 = linalg.generic |
| {indexing_maps = [#map, #map1, #map2], |
| iterator_types = ["parallel", "parallel", "reduction"]} |
| ins(%A, %B : tensor<?x?xf32>, tensor<?x?xf32>) |
| outs(%Out : tensor<?x?xf32>) { |
| ^bb0(%in: f32, %in_0: f32, %out: f32): |
| %1 = arith.mulf %in, %in_0 : f32 |
| %2 = arith.addf %out, %1 : f32 |
| linalg.yield %2 : f32 |
| } -> tensor<?x?xf32> |
| return %0 : tensor<?x?xf32> |
| } |
| |
| // CATEGORY-DAG: #[[$MAP_A:.+]] = affine_map<(d0, d1, d2) -> (d0, d2)> |
| // CATEGORY-DAG: #[[$MAP_B:.+]] = affine_map<(d0, d1, d2) -> (d2, d1)> |
| // CATEGORY-DAG: #[[$MAP_C:.+]] = affine_map<(d0, d1, d2) -> (d0, d1)> |
| |
| // ALL-LABEL: op_matmul |
| // ALL-SAME: %[[A:.+]]: tensor<?x?xf32>, %[[B:.+]]: tensor<?x?xf32>, |
| // ALL-SAME: %[[OUT:.+]]: tensor<?x?xf32>) -> tensor<?x?xf32> |
| |
| // NAMED-NOT: linalg.generic |
| // NAMED: linalg.matmul |
| // NAMED-SAME: ins(%[[A]], %[[B]] : tensor<?x?xf32>, tensor<?x?xf32>) |
| // NAMED-SAME: outs(%[[OUT]] : tensor<?x?xf32>) -> tensor<?x?xf32> |
| |
| // CATEGORY-NOT: linalg.generic |
| // CATEGORY: linalg.contract |
| // CATEGORY-SAME: indexing_maps = {{\[}}#[[$MAP_A]], #[[$MAP_B]], #[[$MAP_C]]{{\]}} |
| // CATEGORY-SAME: ins(%[[A]], %[[B]] : tensor<?x?xf32>, tensor<?x?xf32>) |
| // CATEGORY-SAME: outs(%[[OUT]] : tensor<?x?xf32>) -> tensor<?x?xf32> |
| |
| // Cast-auditing tests: ensure we only specialize when the cast semantics can |
| // be expressed by linalg.matmul, and use the cast attribute when needed. |
| |
| // Check matmul with unsigned cast is correctly raised back to named op. |
| func.func @op_matmul_unsigned_cast(%A: tensor<16x8xi16>, %B: tensor<8x32xi32>, |
| %Out: tensor<16x32xi32>) -> tensor<16x32xi32> { |
| %0 = linalg.generic |
| {indexing_maps = [#map, #map1, #map2], |
| iterator_types = ["parallel", "parallel", "reduction"]} |
| ins(%A, %B : tensor<16x8xi16>, tensor<8x32xi32>) |
| outs(%Out : tensor<16x32xi32>) { |
| ^bb0(%in: i16, %in_0: i32, %out: i32): |
| %1 = arith.extui %in : i16 to i32 |
| %3 = arith.muli %1, %in_0 : i32 |
| %4 = arith.addi %out, %3 : i32 |
| linalg.yield %4 : i32 |
| } -> tensor<16x32xi32> |
| return %0 : tensor<16x32xi32> |
| } |
| |
| // ALL-LABEL: op_matmul_unsigned_cast |
| |
| // NAMED-NOT: linalg.generic |
| // NAMED: linalg.matmul {cast = #linalg.type_fn<cast_unsigned>} |
| |
| // CATEGORY-NOT: linalg.generic |
| // CATEGORY: linalg.contract |
| // CATEGORY-SAME: indexing_maps = {{\[}}#[[$MAP_A]], #[[$MAP_B]], #[[$MAP_C]]{{\]}} |
| // CATEGORY-SAME: {cast = #linalg.type_fn<cast_unsigned>} |
| |
| // Ensures truncation rounding is tolerated with unsigned cases. |
| // Note: We only consider casts as conflicting if they have different |
| // signedness behaviours, and then we do not specialize if they do |
| // conflict. Since this is not such a case, we do not block specialization. |
| // Also the roundtrip lowering back to linalg.generic for such an op is |
| // expected to produce the same thing again, so we are not loosing |
| // information here. |
| func.func @op_matmul_unsigned_cast_and_truncate(%A: tensor<16x8xi16>, %B: tensor<8x32xi64>, |
| %Out: tensor<16x32xi32>) -> tensor<16x32xi32> { |
| %0 = linalg.generic |
| {indexing_maps = [#map, #map1, #map2], |
| iterator_types = ["parallel", "parallel", "reduction"]} |
| ins(%A, %B : tensor<16x8xi16>, tensor<8x32xi64>) |
| outs(%Out : tensor<16x32xi32>) { |
| ^bb0(%in: i16, %in_0: i64, %out: i32): |
| %1 = arith.extui %in : i16 to i32 |
| %2 = arith.trunci %in_0 : i64 to i32 |
| %3 = arith.muli %1, %2 : i32 |
| %4 = arith.addi %out, %3 : i32 |
| linalg.yield %4 : i32 |
| } -> tensor<16x32xi32> |
| return %0 : tensor<16x32xi32> |
| } |
| |
| // ALL-LABEL: op_matmul_unsigned_cast_and_truncate |
| |
| // NAMED-NOT: linalg.generic |
| // NAMED: linalg.matmul {cast = #linalg.type_fn<cast_unsigned>} |
| |
| // CATEGORY-NOT: linalg.generic |
| // CATEGORY: linalg.contract |
| // CATEGORY-SAME: indexing_maps = {{\[}}#[[$MAP_A]], #[[$MAP_B]], #[[$MAP_C]]{{\]}} |
| // CATEGORY-SAME: {cast = #linalg.type_fn<cast_unsigned>} |
| |
| // Signed casts are the default, no cast attribute is required. |
| func.func @op_matmul_signed_cast(%A: tensor<16x8xi16>, %B: tensor<8x32xi16>, |
| %Out: tensor<16x32xi32>) -> tensor<16x32xi32> { |
| %0 = linalg.generic |
| {indexing_maps = [#map, #map1, #map2], |
| iterator_types = ["parallel", "parallel", "reduction"]} |
| ins(%A, %B : tensor<16x8xi16>, tensor<8x32xi16>) |
| outs(%Out : tensor<16x32xi32>) { |
| ^bb0(%in: i16, %in_0: i16, %out: i32): |
| %1 = arith.extsi %in : i16 to i32 |
| %2 = arith.extsi %in_0 : i16 to i32 |
| %3 = arith.muli %1, %2 : i32 |
| %4 = arith.addi %out, %3 : i32 |
| linalg.yield %4 : i32 |
| } -> tensor<16x32xi32> |
| return %0 : tensor<16x32xi32> |
| } |
| |
| // ALL-LABEL: op_matmul_signed_cast |
| |
| // NAMED-NOT: linalg.generic |
| // NAMED-NOT: linalg.matmul {cast = #linalg.type_fn<cast_unsigned>} |
| // NAMED: linalg.matmul |
| |
| // CATEGORY-NOT: linalg.generic |
| // CATEGORY: %[[RES:.+]] = linalg.contract |
| // CATEGORY-SAME: indexing_maps = {{\[}}#[[$MAP_A]], #[[$MAP_B]], #[[$MAP_C]]{{\]}} |
| // CATEGORY-NOT: {cast = |
| // CATEGORY-SAME: ins |
| // CATEGORY: return %[[RES]] |
| |
| // Mixed signed/unsigned inputs cannot be encoded with a single cast attribute. |
| func.func @negative_op_matmul_mixed_cast(%A: tensor<16x8xi16>, %B: tensor<8x32xi16>, |
| %Out: tensor<16x32xi32>) -> tensor<16x32xi32> { |
| %0 = linalg.generic |
| {indexing_maps = [#map, #map1, #map2], |
| iterator_types = ["parallel", "parallel", "reduction"]} |
| ins(%A, %B : tensor<16x8xi16>, tensor<8x32xi16>) |
| outs(%Out : tensor<16x32xi32>) { |
| ^bb0(%in: i16, %in_0: i16, %out: i32): |
| %1 = arith.extui %in : i16 to i32 |
| %2 = arith.extsi %in_0 : i16 to i32 |
| %3 = arith.muli %1, %2 : i32 |
| %4 = arith.addi %out, %3 : i32 |
| linalg.yield %4 : i32 |
| } -> tensor<16x32xi32> |
| return %0 : tensor<16x32xi32> |
| } |
| |
| // ALL-LABEL: negative_op_matmul_mixed_cast |
| |
| // NAMED: linalg.generic |
| // NAMED-NOT: linalg.matmul |
| |
| // CATEGORY: linalg.generic |
| // CATEGORY-NOT: linalg.contract |
| |
| // Output-side casts are not representable by the named matmul ops. |
| func.func @negative_op_matmul_output_cast(%A: tensor<16x8xi32>, %B: tensor<8x32xi32>, |
| %Out: tensor<16x32xi64>) -> tensor<16x32xi64> { |
| %0 = linalg.generic |
| {indexing_maps = [#map, #map1, #map2], |
| iterator_types = ["parallel", "parallel", "reduction"]} |
| ins(%A, %B : tensor<16x8xi32>, tensor<8x32xi32>) |
| outs(%Out : tensor<16x32xi64>) { |
| ^bb0(%in: i32, %in_0: i32, %out: i64): |
| %3 = arith.trunci %out : i64 to i32 |
| %4 = arith.muli %in, %in_0 : i32 |
| %5 = arith.addi %3, %4 : i32 |
| %6 = arith.extsi %5 : i32 to i64 |
| linalg.yield %6 : i64 |
| } -> tensor<16x32xi64> |
| return %0 : tensor<16x32xi64> |
| } |
| |
| // ALL-LABEL: negative_op_matmul_output_cast |
| |
| // NAMED: linalg.generic |
| // NAMED-NOT: linalg.matmul |
| |
| // CATEGORY: linalg.generic |
| // CATEGORY-NOT: linalg.contract |
| |
| // Bitcasts are not modeled by the cast attribute, but should not block |
| // specialization. |
| // NOTE: Bitcasts are not preserved by the matmul named op during |
| // roundtrip, so this is potentially loosing information here. |
| // See #177593 for more details. |
| func.func @op_matmul_bitcast_int_to_float(%A: tensor<16x8xi32>, |
| %B: tensor<8x32xi32>, |
| %Out: tensor<16x32xf32>) -> tensor<16x32xf32> { |
| %0 = linalg.generic |
| {indexing_maps = [#map, #map1, #map2], |
| iterator_types = ["parallel", "parallel", "reduction"]} |
| ins(%A, %B : tensor<16x8xi32>, tensor<8x32xi32>) |
| outs(%Out : tensor<16x32xf32>) { |
| ^bb0(%in: i32, %in_0: i32, %out: f32): |
| %1 = arith.bitcast %in : i32 to f32 |
| %2 = arith.bitcast %in_0 : i32 to f32 |
| %3 = arith.mulf %1, %2 : f32 |
| %4 = arith.addf %out, %3 : f32 |
| linalg.yield %4 : f32 |
| } -> tensor<16x32xf32> |
| return %0 : tensor<16x32xf32> |
| } |
| |
| // ALL-LABEL: op_matmul_bitcast_int_to_float |
| |
| // NAMED-NOT: linalg.generic |
| // NAMED: linalg.matmul |
| |
| // CATEGORY-NOT: linalg.generic |
| // CATEGORY: linalg.contract |
| |
| // Signed float casts only use sitofp, which defaults to signed semantics. |
| func.func @op_matmul_signed_cast_float(%A: tensor<16x8xi16>, %B: tensor<8x32xi16>, |
| %Out: tensor<16x32xf32>) -> tensor<16x32xf32> { |
| %0 = linalg.generic |
| {indexing_maps = [#map, #map1, #map2], |
| iterator_types = ["parallel", "parallel", "reduction"]} |
| ins(%A, %B : tensor<16x8xi16>, tensor<8x32xi16>) |
| outs(%Out : tensor<16x32xf32>) { |
| ^bb0(%in: i16, %in_0: i16, %out: f32): |
| %1 = arith.sitofp %in : i16 to f32 |
| %2 = arith.sitofp %in_0 : i16 to f32 |
| %3 = arith.mulf %1, %2 : f32 |
| %4 = arith.addf %out, %3 : f32 |
| linalg.yield %4 : f32 |
| } -> tensor<16x32xf32> |
| return %0 : tensor<16x32xf32> |
| } |
| |
| // ALL-LABEL: op_matmul_signed_cast_float |
| |
| // NAMED-NOT: linalg.generic |
| // NAMED-NOT: linalg.matmul {cast = #linalg.type_fn<cast_unsigned>} |
| // NAMED: linalg.matmul |
| |
| // CATEGORY-NOT: linalg.generic |
| // CATEGORY-NOT: linalg.contract{{.*}}{cast = |
| // CATEGORY: %[[RES:.+]] = linalg.contract |
| // CATEGORY: return %[[RES]] |
| |
| // Unsigned float casts are expressed via uitofp and use the unsigned cast attr. |
| func.func @op_matmul_unsigned_cast_float(%A: tensor<16x8xi16>, %B: tensor<8x32xi16>, |
| %Out: tensor<16x32xf32>) -> tensor<16x32xf32> { |
| %0 = linalg.generic |
| {indexing_maps = [#map, #map1, #map2], |
| iterator_types = ["parallel", "parallel", "reduction"]} |
| ins(%A, %B : tensor<16x8xi16>, tensor<8x32xi16>) |
| outs(%Out : tensor<16x32xf32>) { |
| ^bb0(%in: i16, %in_0: i16, %out: f32): |
| %1 = arith.uitofp %in : i16 to f32 |
| %2 = arith.uitofp %in_0 : i16 to f32 |
| %3 = arith.mulf %1, %2 : f32 |
| %4 = arith.addf %out, %3 : f32 |
| linalg.yield %4 : f32 |
| } -> tensor<16x32xf32> |
| return %0 : tensor<16x32xf32> |
| } |
| |
| // ALL-LABEL: op_matmul_unsigned_cast_float |
| |
| // NAMED-NOT: linalg.generic |
| // NAMED: linalg.matmul {cast = #linalg.type_fn<cast_unsigned>} |
| |
| // CATEGORY-NOT: linalg.generic |
| // CATEGORY: linalg.contract{{.*}}{cast = #linalg.type_fn<cast_unsigned>} |
| |
| // ----- |
| |
| ///---------------------------------------------------------------------------------------- |
| /// Tests for linalg.batch_matmul |
| ///---------------------------------------------------------------------------------------- |
| |
| #map = affine_map<(d0, d1, d2, d3) -> (d0, d1, d3)> |
| #map1 = affine_map<(d0, d1, d2, d3) -> (d0, d3, d2)> |
| #map2 = affine_map<(d0, d1, d2, d3) -> (d0, d1, d2)> |
| func.func @op_batch_matmul(%A: tensor<2x16x8xf32>, %B: tensor<2x8x16xf32>, |
| %Out: tensor<2x16x16xf32>) -> tensor<2x16x16xf32> { |
| %0 = linalg.generic |
| {indexing_maps = [#map, #map1, #map2], |
| iterator_types = ["parallel", "parallel", "parallel", "reduction"]} |
| ins(%A, %B : tensor<2x16x8xf32>, tensor<2x8x16xf32>) |
| outs(%Out : tensor<2x16x16xf32>) { |
| ^bb0(%in: f32, %in_0: f32, %out: f32): |
| %1 = arith.mulf %in, %in_0 : f32 |
| %2 = arith.addf %out, %1 : f32 |
| linalg.yield %2 : f32 |
| } -> tensor<2x16x16xf32> |
| return %0 : tensor<2x16x16xf32> |
| } |
| |
| // CATEGORY-DAG: #[[$MAP_A:.+]] = affine_map<(d0, d1, d2, d3) -> (d0, d1, d3)> |
| // CATEGORY-DAG: #[[$MAP_B:.+]] = affine_map<(d0, d1, d2, d3) -> (d0, d3, d2)> |
| // CATEGORY-DAG: #[[$MAP_C:.+]] = affine_map<(d0, d1, d2, d3) -> (d0, d1, d2)> |
| |
| // ALL-LABEL: op_batch_matmul |
| // ALL-SAME: %[[A:.+]]: tensor<2x16x8xf32>, %[[B:.+]]: tensor<2x8x16xf32>, %[[OUT:.+]]: tensor<2x16x16xf32>) -> tensor<2x16x16xf32> |
| |
| // NAMED-NOT: linalg.generic |
| // NAMED: linalg.batch_matmul ins(%[[A]], %[[B]] : tensor<2x16x8xf32>, tensor<2x8x16xf32>) outs(%[[OUT]] : tensor<2x16x16xf32>) -> tensor<2x16x16xf32> |
| |
| // CATEGORY-NOT: linalg.generic |
| // CATEGORY: linalg.contract indexing_maps = {{\[}}#[[$MAP_A]], #[[$MAP_B]], #[[$MAP_C]]{{\]}} |
| // CATEGORY-SAME: ins(%[[A]], %[[B]] : tensor<2x16x8xf32>, tensor<2x8x16xf32>) |
| // CATEGORY-SAME: outs(%[[OUT]] : tensor<2x16x16xf32>) -> tensor<2x16x16xf32> |
| |
| // Ensure that the unsigned cast path for cast detection is exercised for |
| // batch_matmul as well. |
| func.func @op_batch_matmul_unsigned_cast(%A: tensor<2x16x8xi16>, |
| %B: tensor<2x8x16xi64>, |
| %Out: tensor<2x16x16xi32>) -> tensor<2x16x16xi32> { |
| %0 = linalg.generic |
| {indexing_maps = [#map, #map1, #map2], |
| iterator_types = ["parallel", "parallel", "parallel", "reduction"]} |
| ins(%A, %B : tensor<2x16x8xi16>, tensor<2x8x16xi64>) |
| outs(%Out : tensor<2x16x16xi32>) { |
| ^bb0(%in: i16, %in_0: i64, %out: i32): |
| %1 = arith.extui %in : i16 to i32 |
| %2 = arith.trunci %in_0 : i64 to i32 |
| %3 = arith.muli %1, %2 : i32 |
| %4 = arith.addi %out, %3 : i32 |
| linalg.yield %4 : i32 |
| } -> tensor<2x16x16xi32> |
| return %0 : tensor<2x16x16xi32> |
| } |
| |
| // ALL-LABEL: op_batch_matmul_unsigned_cast |
| |
| // NAMED-NOT: linalg.generic |
| // NAMED: linalg.batch_matmul {cast = #linalg.type_fn<cast_unsigned>} |
| |
| // CATEGORY-NOT: linalg.generic |
| // CATEGORY: linalg.contract indexing_maps = {{\[}}#[[$MAP_A]], #[[$MAP_B]], #[[$MAP_C]]{{\]}} |
| // CATEGORY-SAME: {cast = #linalg.type_fn<cast_unsigned>} |
| |
| // ----- |
| |
| // A multi-reduction contraction. |
| #mapA = affine_map<(m, n, k1, k2) -> (m, k1, k2)> |
| #mapB = affine_map<(m, n, k1, k2) -> (k2, k1, n)> |
| #mapC = affine_map<(m, n, k1, k2) -> (m, n)> |
| func.func @op_multi_reduction(%A: tensor<10x20x30xf32>, |
| %B: tensor<30x20x40xf32>, |
| %C: tensor<10x40xf32>) -> tensor<10x40xf32> { |
| %0 = linalg.generic |
| {indexing_maps = [#mapA, #mapB, #mapC], |
| iterator_types = ["parallel", "parallel", "reduction", "reduction"]} |
| ins(%A, %B : tensor<10x20x30xf32>, tensor<30x20x40xf32>) |
| outs(%C : tensor<10x40xf32>) { |
| ^bb0(%a: f32, %b: f32, %c: f32): |
| %1 = arith.mulf %a, %b : f32 |
| %2 = arith.addf %c, %1 : f32 |
| linalg.yield %2 : f32 |
| } -> tensor<10x40xf32> |
| return %0 : tensor<10x40xf32> |
| } |
| |
| // ALL-LABEL: op_multi_reduction |
| |
| // ALL-NOT: linalg.generic |
| // ALL: linalg.contract |
| |
| // ----- |
| |
| // Batch dim not in identity position: batch dim d0 appears at result |
| // position 1 in A's map instead of position 0. |
| #mapBni0 = affine_map<(d0, d1, d2, d3) -> (d1, d0, d3)> |
| #mapBni1 = affine_map<(d0, d1, d2, d3) -> (d0, d3, d2)> |
| #mapBni2 = affine_map<(d0, d1, d2, d3) -> (d0, d1, d2)> |
| func.func @batch_matmul_non_identity_batch(%A: tensor<4x2x8xf32>, %B: tensor<2x8x16xf32>, |
| %Out: tensor<2x4x16xf32>) -> tensor<2x4x16xf32> { |
| %0 = linalg.generic |
| {indexing_maps = [#mapBni0, #mapBni1, #mapBni2], |
| iterator_types = ["parallel", "parallel", "parallel", "reduction"]} |
| ins(%A, %B : tensor<4x2x8xf32>, tensor<2x8x16xf32>) |
| outs(%Out : tensor<2x4x16xf32>) { |
| ^bb0(%in: f32, %in_0: f32, %out: f32): |
| %1 = arith.mulf %in, %in_0 : f32 |
| %2 = arith.addf %out, %1 : f32 |
| linalg.yield %2 : f32 |
| } -> tensor<2x4x16xf32> |
| return %0 : tensor<2x4x16xf32> |
| } |
| |
| // ALL-LABEL: batch_matmul_non_identity_batch |
| |
| // ALL-NOT: linalg.generic |
| // ALL: linalg.contract |
| |
| // ----- |
| |
| // TODO: named matvec |
| #map = affine_map<(d0, d1) -> (d0, d1)> |
| #map1 = affine_map<(d0, d1) -> (d1)> |
| #map2 = affine_map<(d0, d1) -> (d0)> |
| func.func @op_matvec(%A: tensor<?x?xf32>, %B: tensor<?xf32>, %Out: tensor<?xf32>) -> tensor<?xf32> { |
| %0 = linalg.generic |
| {indexing_maps = [#map, #map1, #map2], |
| iterator_types = ["parallel", "reduction"]} |
| ins(%A, %B : tensor<?x?xf32>, tensor<?xf32>) |
| outs(%Out : tensor<?xf32>) { |
| ^bb0(%in: f32, %in_0: f32, %out: f32): |
| %1 = arith.mulf %in, %in_0 : f32 |
| %2 = arith.addf %out, %1 : f32 |
| linalg.yield %2 : f32 |
| } -> tensor<?xf32> |
| return %0 : tensor<?xf32> |
| } |
| |
| // ALL-LABEL: op_matvec |
| |
| // ALL-NOT: linalg.generic |
| // ALL: linalg.contract |
| |
| // ----- |
| |
| // Matmul transpose A: A is accessed as (k, m) instead of (m, k) |
| #map_ta = affine_map<(d0, d1, d2) -> (d2, d0)> |
| #map_b = affine_map<(d0, d1, d2) -> (d2, d1)> |
| #map_c = affine_map<(d0, d1, d2) -> (d0, d1)> |
| func.func @op_matmul_transpose_a(%A: tensor<?x?xf32>, %B: tensor<?x?xf32>, |
| %Out: tensor<?x?xf32>) -> tensor<?x?xf32> { |
| %0 = linalg.generic |
| {indexing_maps = [#map_ta, #map_b, #map_c], |
| iterator_types = ["parallel", "parallel", "reduction"]} |
| ins(%A, %B : tensor<?x?xf32>, tensor<?x?xf32>) outs(%Out : tensor<?x?xf32>) { |
| ^bb0(%in: f32, %in_0: f32, %out: f32): |
| %1 = arith.mulf %in, %in_0 : f32 |
| %2 = arith.addf %out, %1 : f32 |
| linalg.yield %2 : f32 |
| } -> tensor<?x?xf32> |
| return %0 : tensor<?x?xf32> |
| } |
| |
| // ALL-DAG: #[[$MAP_TA:.+]] = affine_map<(d0, d1, d2) -> (d2, d0)> |
| // ALL-DAG: #[[$MAP_B:.+]] = affine_map<(d0, d1, d2) -> (d2, d1)> |
| // ALL-DAG: #[[$MAP_C:.+]] = affine_map<(d0, d1, d2) -> (d0, d1)> |
| |
| // ALL-LABEL: op_matmul_transpose_a |
| // ALL-SAME: %[[A:.+]]: tensor<?x?xf32>, %[[B:.+]]: tensor<?x?xf32>, %[[OUT:.+]]: tensor<?x?xf32> |
| |
| // NAMED-NOT: linalg.generic |
| // NAMED: linalg.matmul |
| // NAMED-SAME: indexing_maps = [#[[$MAP_TA]], #[[$MAP_B]], #[[$MAP_C]]] |
| // NAMED-SAME: ins(%[[A]], %[[B]] : tensor<?x?xf32>, tensor<?x?xf32>) |
| // NAMED-SAME: outs(%[[OUT]] : tensor<?x?xf32>) -> tensor<?x?xf32> |
| |
| // CATEGORY-NOT: linalg.generic |
| // CATEGORY: linalg.contract |
| // CATEGORY-SAME: indexing_maps = [#[[$MAP_TA]], #[[$MAP_B]], #[[$MAP_C]]] |
| |
| // ----- |
| |
| // Matmul transpose B: B is accessed as (n, k) instead of (k, n) |
| #map_a = affine_map<(d0, d1, d2) -> (d0, d2)> |
| #map_tb = affine_map<(d0, d1, d2) -> (d1, d2)> |
| #map_c = affine_map<(d0, d1, d2) -> (d0, d1)> |
| func.func @op_matmul_transpose_b(%A: tensor<?x?xf32>, %B: tensor<?x?xf32>, |
| %Out: tensor<?x?xf32>) -> tensor<?x?xf32> { |
| %0 = linalg.generic |
| {indexing_maps = [#map_a, #map_tb, #map_c], |
| iterator_types = ["parallel", "parallel", "reduction"]} |
| ins(%A, %B : tensor<?x?xf32>, tensor<?x?xf32>) |
| outs(%Out : tensor<?x?xf32>) { |
| ^bb0(%in: f32, %in_0: f32, %out: f32): |
| %1 = arith.mulf %in, %in_0 : f32 |
| %2 = arith.addf %out, %1 : f32 |
| linalg.yield %2 : f32 |
| } -> tensor<?x?xf32> |
| return %0 : tensor<?x?xf32> |
| } |
| |
| // ALL-DAG: #[[$MAP_A:.+]] = affine_map<(d0, d1, d2) -> (d0, d2)> |
| // ALL-DAG: #[[$MAP_TB:.+]] = affine_map<(d0, d1, d2) -> (d1, d2)> |
| // ALL-DAG: #[[$MAP_C:.+]] = affine_map<(d0, d1, d2) -> (d0, d1)> |
| |
| // ALL-LABEL: op_matmul_transpose_b |
| // ALL-SAME: %[[A:.+]]: tensor<?x?xf32>, %[[B:.+]]: tensor<?x?xf32>, %[[OUT:.+]]: tensor<?x?xf32> |
| |
| // NAMED-NOT: linalg.generic |
| // NAMED: linalg.matmul |
| // NAMED-SAME: indexing_maps = [#[[$MAP_A]], #[[$MAP_TB]], #[[$MAP_C]]] |
| // NAMED-SAME: ins(%[[A]], %[[B]] : tensor<?x?xf32>, tensor<?x?xf32>) |
| // NAMED-SAME: outs(%[[OUT]] : tensor<?x?xf32>) -> tensor<?x?xf32> |
| |
| // CATEGORY-NOT: linalg.generic |
| // CATEGORY: linalg.contract |
| // CATEGORY-SAME: indexing_maps = [#[[$MAP_A]], #[[$MAP_TB]], #[[$MAP_C]]] |
| // CATEGORY-SAME: ins(%[[A]], %[[B]] : tensor<?x?xf32>, tensor<?x?xf32>) |
| // CATEGORY-SAME: outs(%[[OUT]] : tensor<?x?xf32>) -> tensor<?x?xf32> |
| |
| // ----- |
| |
| // Batch matmul transpose A: A is accessed as (b, k, m) instead of (b, m, k) |
| #map_ta = affine_map<(d0, d1, d2, d3) -> (d0, d3, d1)> |
| #map_b = affine_map<(d0, d1, d2, d3) -> (d0, d3, d2)> |
| #map_c = affine_map<(d0, d1, d2, d3) -> (d0, d1, d2)> |
| func.func @op_batch_matmul_transpose_a(%A: tensor<2x8x4xf32>, %B: tensor<2x8x16xf32>, |
| %Out: tensor<2x4x16xf32>) -> tensor<2x4x16xf32> { |
| %0 = linalg.generic |
| {indexing_maps = [#map_ta, #map_b, #map_c], |
| iterator_types = ["parallel", "parallel", "parallel", "reduction"]} |
| ins(%A, %B : tensor<2x8x4xf32>, tensor<2x8x16xf32>) |
| outs(%Out : tensor<2x4x16xf32>) { |
| ^bb0(%in: f32, %in_0: f32, %out: f32): |
| %1 = arith.mulf %in, %in_0 : f32 |
| %2 = arith.addf %out, %1 : f32 |
| linalg.yield %2 : f32 |
| } -> tensor<2x4x16xf32> |
| return %0 : tensor<2x4x16xf32> |
| } |
| |
| // ALL-DAG: #[[$MAP_TA:.+]] = affine_map<(d0, d1, d2, d3) -> (d0, d3, d1)> |
| // ALL-DAG: #[[$MAP_B:.+]] = affine_map<(d0, d1, d2, d3) -> (d0, d3, d2)> |
| // ALL-DAG: #[[$MAP_C:.+]] = affine_map<(d0, d1, d2, d3) -> (d0, d1, d2)> |
| |
| // ALL-LABEL: op_batch_matmul_transpose_a |
| // ALL-SAME: %[[A:.+]]: tensor<2x8x4xf32>, %[[B:.+]]: tensor<2x8x16xf32>, %[[OUT:.+]]: tensor<2x4x16xf32> |
| |
| // NAMED-NOT: linalg.generic |
| // NAMED: linalg.batch_matmul |
| // NAMED-SAME: indexing_maps = [#[[$MAP_TA]], #[[$MAP_B]], #[[$MAP_C]]] |
| // NAMED-SAME: ins(%[[A]], %[[B]] : tensor<2x8x4xf32>, tensor<2x8x16xf32>) |
| // NAMED-SAME: outs(%[[OUT]] : tensor<2x4x16xf32>) -> tensor<2x4x16xf32> |
| |
| // CATEGORY-NOT: linalg.generic |
| // CATEGORY: linalg.contract |
| // CATEGORY-SAME: indexing_maps = [#[[$MAP_TA]], #[[$MAP_B]], #[[$MAP_C]]] |
| // CATEGORY-SAME: ins(%[[A]], %[[B]] : tensor<2x8x4xf32>, tensor<2x8x16xf32>) |
| // CATEGORY-SAME: outs(%[[OUT]] : tensor<2x4x16xf32>) -> tensor<2x4x16xf32> |
| |
| // ----- |
| |
| // Batch matmul transpose B: B is accessed as (b, n, k) instead of (b, k, n) |
| #map_a = affine_map<(d0, d1, d2, d3) -> (d0, d1, d3)> |
| #map_tb = affine_map<(d0, d1, d2, d3) -> (d0, d2, d3)> |
| #map_c = affine_map<(d0, d1, d2, d3) -> (d0, d1, d2)> |
| func.func @op_batch_matmul_transpose_b(%A: tensor<2x4x8xf32>, %B: tensor<2x16x8xf32>, |
| %Out: tensor<2x4x16xf32>) -> tensor<2x4x16xf32> { |
| %0 = linalg.generic |
| {indexing_maps = [#map_a, #map_tb, #map_c], |
| iterator_types = ["parallel", "parallel", "parallel", "reduction"]} |
| ins(%A, %B : tensor<2x4x8xf32>, tensor<2x16x8xf32>) |
| outs(%Out : tensor<2x4x16xf32>) { |
| ^bb0(%in: f32, %in_0: f32, %out: f32): |
| %1 = arith.mulf %in, %in_0 : f32 |
| %2 = arith.addf %out, %1 : f32 |
| linalg.yield %2 : f32 |
| } -> tensor<2x4x16xf32> |
| return %0 : tensor<2x4x16xf32> |
| } |
| |
| // ALL-DAG: #[[$MAP_A:.+]] = affine_map<(d0, d1, d2, d3) -> (d0, d1, d3)> |
| // ALL-DAG: #[[$MAP_TB:.+]] = affine_map<(d0, d1, d2, d3) -> (d0, d2, d3)> |
| // ALL-DAG: #[[$MAP_C:.+]] = affine_map<(d0, d1, d2, d3) -> (d0, d1, d2)> |
| |
| // ALL-LABEL: op_batch_matmul_transpose_b |
| // ALL-SAME: %[[A:.+]]: tensor<2x4x8xf32>, %[[B:.+]]: tensor<2x16x8xf32>, %[[OUT:.+]]: tensor<2x4x16xf32> |
| |
| // NAMED-NOT: linalg.generic |
| // NAMED: linalg.batch_matmul |
| // NAMED-SAME: indexing_maps = [#[[$MAP_A]], #[[$MAP_TB]], #[[$MAP_C]]] |
| // NAMED-SAME: ins(%[[A]], %[[B]] : tensor<2x4x8xf32>, tensor<2x16x8xf32>) |
| // NAMED-SAME: outs(%[[OUT]] : tensor<2x4x16xf32>) -> tensor<2x4x16xf32> |
| |
| // CATEGORY-NOT: linalg.generic |
| // CATEGORY: linalg.contract |
| // CATEGORY-SAME: indexing_maps = [#[[$MAP_A]], #[[$MAP_TB]], #[[$MAP_C]]] |
| // CATEGORY-SAME: ins(%[[A]], %[[B]] : tensor<2x4x8xf32>, tensor<2x16x8xf32>) |
| // CATEGORY-SAME: outs(%[[OUT]] : tensor<2x4x16xf32>) -> tensor<2x4x16xf32> |
| |
| // ----- |
| |
| // Both A and B transposed. |
| #map_ta = affine_map<(d0, d1, d2) -> (d2, d0)> |
| #map_tb = affine_map<(d0, d1, d2) -> (d1, d2)> |
| #map_c = affine_map<(d0, d1, d2) -> (d0, d1)> |
| func.func @op_matmul_transpose_a_and_b(%A: tensor<?x?xf32>, %B: tensor<?x?xf32>, |
| %Out: tensor<?x?xf32>) -> tensor<?x?xf32> { |
| %0 = linalg.generic |
| {indexing_maps = [#map_ta, #map_tb, #map_c], |
| iterator_types = ["parallel", "parallel", "reduction"]} |
| ins(%A, %B : tensor<?x?xf32>, tensor<?x?xf32>) |
| outs(%Out : tensor<?x?xf32>) { |
| ^bb0(%in: f32, %in_0: f32, %out: f32): |
| %1 = arith.mulf %in, %in_0 : f32 |
| %2 = arith.addf %out, %1 : f32 |
| linalg.yield %2 : f32 |
| } -> tensor<?x?xf32> |
| return %0 : tensor<?x?xf32> |
| } |
| |
| // ALL-DAG: #[[$MAP_TA:.+]] = affine_map<(d0, d1, d2) -> (d2, d0)> |
| // ALL-DAG: #[[$MAP_TB:.+]] = affine_map<(d0, d1, d2) -> (d1, d2)> |
| // ALL-DAG: #[[$MAP_C:.+]] = affine_map<(d0, d1, d2) -> (d0, d1)> |
| |
| // ALL-LABEL: op_matmul_transpose_a_and_b |
| // ALL-SAME: %[[A:.+]]: tensor<?x?xf32>, %[[B:.+]]: tensor<?x?xf32>, %[[OUT:.+]]: tensor<?x?xf32> |
| |
| // NAMED-NOT: linalg.generic |
| // NAMED: linalg.matmul |
| // NAMED-SAME: indexing_maps = [#[[$MAP_TA]], #[[$MAP_TB]], #[[$MAP_C]]] |
| // NAMED-SAME: ins(%[[A]], %[[B]] : tensor<?x?xf32>, tensor<?x?xf32>) |
| // NAMED-SAME: outs(%[[OUT]] : tensor<?x?xf32>) -> tensor<?x?xf32> |
| |
| // CATEGORY-NOT: linalg.generic |
| // CATEGORY: linalg.contract |
| // CATEGORY-SAME: indexing_maps = [#[[$MAP_TA]], #[[$MAP_TB]], #[[$MAP_C]]] |
| // CATEGORY-SAME: ins(%[[A]], %[[B]] : tensor<?x?xf32>, tensor<?x?xf32>) |
| // CATEGORY-SAME: outs(%[[OUT]] : tensor<?x?xf32>) -> tensor<?x?xf32> |
| |
| // ----- |
| |
| // Output transposed: C is accessed as (n, m) instead of (m, n). |
| #map_a = affine_map<(d0, d1, d2) -> (d0, d2)> |
| #map_b = affine_map<(d0, d1, d2) -> (d2, d1)> |
| #map_tc = affine_map<(d0, d1, d2) -> (d1, d0)> |
| func.func @op_matmul_transposed_output(%A: tensor<?x?xf32>, %B: tensor<?x?xf32>, |
| %Out: tensor<?x?xf32>) -> tensor<?x?xf32> { |
| %0 = linalg.generic |
| {indexing_maps = [#map_a, #map_b, #map_tc], |
| iterator_types = ["parallel", "parallel", "reduction"]} |
| ins(%A, %B : tensor<?x?xf32>, tensor<?x?xf32>) |
| outs(%Out : tensor<?x?xf32>) { |
| ^bb0(%in: f32, %in_0: f32, %out: f32): |
| %1 = arith.mulf %in, %in_0 : f32 |
| %2 = arith.addf %out, %1 : f32 |
| linalg.yield %2 : f32 |
| } -> tensor<?x?xf32> |
| return %0 : tensor<?x?xf32> |
| } |
| |
| // ALL-DAG: #[[$MAP_A:.+]] = affine_map<(d0, d1, d2) -> (d0, d2)> |
| // ALL-DAG: #[[$MAP_B:.+]] = affine_map<(d0, d1, d2) -> (d2, d1)> |
| // ALL-DAG: #[[$MAP_TC:.+]] = affine_map<(d0, d1, d2) -> (d1, d0)> |
| |
| // ALL-LABEL: op_matmul_transposed_output |
| // ALL-SAME: %[[A:.+]]: tensor<?x?xf32>, %[[B:.+]]: tensor<?x?xf32>, %[[OUT:.+]]: tensor<?x?xf32> |
| |
| // NAMED-NOT: linalg.generic |
| // NAMED: linalg.matmul |
| // NAMED-SAME: indexing_maps = [#[[$MAP_A]], #[[$MAP_B]], #[[$MAP_TC]]] |
| // NAMED-SAME: ins(%[[A]], %[[B]] : tensor<?x?xf32>, tensor<?x?xf32>) |
| // NAMED-SAME: outs(%[[OUT]] : tensor<?x?xf32>) -> tensor<?x?xf32> |
| |
| // CATEGORY-NOT: linalg.generic |
| // CATEGORY: linalg.contract |
| // CATEGORY-SAME: indexing_maps = [#[[$MAP_A]], #[[$MAP_B]], #[[$MAP_TC]]] |
| // CATEGORY-SAME: ins(%[[A]], %[[B]] : tensor<?x?xf32>, tensor<?x?xf32>) |
| // CATEGORY-SAME: outs(%[[OUT]] : tensor<?x?xf32>) -> tensor<?x?xf32> |
| |
| // ----- |
| |
| // Matmul with non-canonical loop ordering. |
| #map_nc_a = affine_map<(m, k, n) -> (m, k)> |
| #map_nc_b = affine_map<(m, k, n) -> (k, n)> |
| #map_nc_c = affine_map<(m, k, n) -> (m, n)> |
| func.func @op_matmul_non_canonical_loops(%A: tensor<?x?xf32>, %B: tensor<?x?xf32>, |
| %Out: tensor<?x?xf32>) -> tensor<?x?xf32> { |
| %0 = linalg.generic |
| {indexing_maps = [#map_nc_a, #map_nc_b, #map_nc_c], |
| iterator_types = ["parallel", "reduction", "parallel"]} |
| ins(%A, %B : tensor<?x?xf32>, tensor<?x?xf32>) |
| outs(%Out : tensor<?x?xf32>) { |
| ^bb0(%in: f32, %in_0: f32, %out: f32): |
| %1 = arith.mulf %in, %in_0 : f32 |
| %2 = arith.addf %out, %1 : f32 |
| linalg.yield %2 : f32 |
| } -> tensor<?x?xf32> |
| return %0 : tensor<?x?xf32> |
| } |
| |
| // CATEGORY-DAG: #[[$MAP_A:.+]] = affine_map<(d0, d1, d2) -> (d0, d1)> |
| // CATEGORY-DAG: #[[$MAP_B:.+]] = affine_map<(d0, d1, d2) -> (d1, d2)> |
| // CATEGORY-DAG: #[[$MAP_C:.+]] = affine_map<(d0, d1, d2) -> (d0, d2)> |
| |
| // ALL-LABEL: op_matmul_non_canonical_loops |
| // ALL-SAME: %[[A:.+]]: tensor<?x?xf32>, %[[B:.+]]: tensor<?x?xf32>, %[[OUT:.+]]: tensor<?x?xf32> |
| |
| // NAMED-NOT: linalg.generic |
| // NAMED: linalg.matmul |
| // NAMED-SAME: ins(%[[A]], %[[B]] : tensor<?x?xf32>, tensor<?x?xf32>) |
| // NAMED-SAME: outs(%[[OUT]] : tensor<?x?xf32>) -> tensor<?x?xf32> |
| |
| // CATEGORY-NOT: linalg.generic |
| // CATEGORY: linalg.contract |
| // CATEGORY-SAME: indexing_maps = [#[[$MAP_A]], #[[$MAP_B]], #[[$MAP_C]]] |
| // CATEGORY-SAME: ins(%[[A]], %[[B]] : tensor<?x?xf32>, tensor<?x?xf32>) |
| // CATEGORY-SAME: outs(%[[OUT]] : tensor<?x?xf32>) -> tensor<?x?xf32> |
| // ----- |
| |
| // Batch matmul with non-canonical loop ordering. |
| #map_bnc_a = affine_map<(batch, m, k, n) -> (batch, m, k)> |
| #map_bnc_b = affine_map<(batch, m, k, n) -> (batch, k, n)> |
| #map_bnc_c = affine_map<(batch, m, k, n) -> (batch, m, n)> |
| func.func @op_batch_matmul_non_canonical_loops(%A: tensor<2x16x8xf32>, %B: tensor<2x8x16xf32>, |
| %Out: tensor<2x16x16xf32>) -> tensor<2x16x16xf32> { |
| %0 = linalg.generic |
| {indexing_maps = [#map_bnc_a, #map_bnc_b, #map_bnc_c], |
| iterator_types = ["parallel", "parallel", "reduction", "parallel"]} |
| ins(%A, %B : tensor<2x16x8xf32>, tensor<2x8x16xf32>) |
| outs(%Out : tensor<2x16x16xf32>) { |
| ^bb0(%in: f32, %in_0: f32, %out: f32): |
| %1 = arith.mulf %in, %in_0 : f32 |
| %2 = arith.addf %out, %1 : f32 |
| linalg.yield %2 : f32 |
| } -> tensor<2x16x16xf32> |
| return %0 : tensor<2x16x16xf32> |
| } |
| |
| // CATEGORY-DAG: #[[$MAP_A:.+]] = affine_map<(d0, d1, d2, d3) -> (d0, d1, d2)> |
| // CATEGORY-DAG: #[[$MAP_B:.+]] = affine_map<(d0, d1, d2, d3) -> (d0, d2, d3)> |
| // CATEGORY-DAG: #[[$MAP_C:.+]] = affine_map<(d0, d1, d2, d3) -> (d0, d1, d3)> |
| |
| // ALL-LABEL: op_batch_matmul_non_canonical_loops |
| // ALL-SAME: %[[A:.+]]: tensor<2x16x8xf32>, %[[B:.+]]: tensor<2x8x16xf32>, %[[OUT:.+]]: tensor<2x16x16xf32> |
| |
| // NAMED-NOT: linalg.generic |
| // NAMED: linalg.batch_matmul |
| // NAMED-SAME: ins(%[[A]], %[[B]] : tensor<2x16x8xf32>, tensor<2x8x16xf32>) |
| // NAMED-SAME: outs(%[[OUT]] : tensor<2x16x16xf32>) -> tensor<2x16x16xf32> |
| |
| // CATEGORY-NOT: linalg.generic |
| // CATEGORY: linalg.contract |
| // CATEGORY-SAME: indexing_maps = [#[[$MAP_A]], #[[$MAP_B]], #[[$MAP_C]]] |
| // CATEGORY-SAME: ins(%[[A]], %[[B]] : tensor<2x16x8xf32>, tensor<2x8x16xf32>) |
| // CATEGORY-SAME: outs(%[[OUT]] : tensor<2x16x16xf32>) -> tensor<2x16x16xf32> |
| |
| // ----- |
| |
| // Matmul with non-canonical loop ordering (d0=m, d1=k, d2=n) and B transposed. |
| #map_nc_tb_a = affine_map<(d0, d1, d2) -> (d0, d1)> |
| #map_nc_tb_b = affine_map<(d0, d1, d2) -> (d2, d1)> |
| #map_nc_tb_c = affine_map<(d0, d1, d2) -> (d0, d2)> |
| func.func @op_matmul_non_canonical_transpose_b(%A: tensor<?x?xf32>, %B: tensor<?x?xf32>, |
| %Out: tensor<?x?xf32>) -> tensor<?x?xf32> { |
| %0 = linalg.generic |
| {indexing_maps = [#map_nc_tb_a, #map_nc_tb_b, #map_nc_tb_c], |
| iterator_types = ["parallel", "reduction", "parallel"]} |
| ins(%A, %B : tensor<?x?xf32>, tensor<?x?xf32>) outs(%Out : tensor<?x?xf32>) { |
| ^bb0(%in: f32, %in_0: f32, %out: f32): |
| %1 = arith.mulf %in, %in_0 : f32 |
| %2 = arith.addf %out, %1 : f32 |
| linalg.yield %2 : f32 |
| } -> tensor<?x?xf32> |
| return %0 : tensor<?x?xf32> |
| } |
| |
| // NAMED-DAG: #[[$MAP_A:.+]] = affine_map<(d0, d1, d2) -> (d0, d2)> |
| // NAMED-DAG: #[[$MAP_TB:.+]] = affine_map<(d0, d1, d2) -> (d1, d2)> |
| // NAMED-DAG: #[[$MAP_C:.+]] = affine_map<(d0, d1, d2) -> (d0, d1)> |
| |
| // CATEGORY-DAG: #[[$MAP_A:.+]] = affine_map<(d0, d1, d2) -> (d0, d1)> |
| // CATEGORY-DAG: #[[$MAP_TB:.+]] = affine_map<(d0, d1, d2) -> (d2, d1)> |
| // CATEGORY-DAG: #[[$MAP_C:.+]] = affine_map<(d0, d1, d2) -> (d0, d2)> |
| |
| // ALL-LABEL: op_matmul_non_canonical_transpose_b |
| // ALL-SAME: %[[A:.+]]: tensor<?x?xf32>, %[[B:.+]]: tensor<?x?xf32>, %[[OUT:.+]]: tensor<?x?xf32> |
| |
| // NAMED-NOT: linalg.generic |
| // NAMED: linalg.matmul |
| // NAMED-SAME: indexing_maps = [#[[$MAP_A]], #[[$MAP_TB]], #[[$MAP_C]]] |
| // NAMED-SAME: ins(%[[A]], %[[B]] : tensor<?x?xf32>, tensor<?x?xf32>) |
| // NAMED-SAME: outs(%[[OUT]] : tensor<?x?xf32>) -> tensor<?x?xf32> |
| |
| // CATEGORY-NOT: linalg.generic |
| // CATEGORY: linalg.contract |
| // CATEGORY-SAME: indexing_maps = [#[[$MAP_A]], #[[$MAP_TB]], #[[$MAP_C]]] |
| // CATEGORY-SAME: ins(%[[A]], %[[B]] : tensor<?x?xf32>, tensor<?x?xf32>) |
| // CATEGORY-SAME: outs(%[[OUT]] : tensor<?x?xf32>) -> tensor<?x?xf32> |
| |
| // ----- |
| |
| // Batch matmul with non-canonical loop ordering (d0=batch, d1=m, d2=k, d3=n) |
| // and B Transposed. |
| #map_bnc_tb_a = affine_map<(d0, d1, d2, d3) -> (d0, d1, d2)> |
| #map_bnc_tb_b = affine_map<(d0, d1, d2, d3) -> (d0, d3, d2)> |
| #map_bnc_tb_c = affine_map<(d0, d1, d2, d3) -> (d0, d1, d3)> |
| func.func @op_batch_matmul_non_canonical_transpose_b(%A: tensor<2x16x8xf32>, %B: tensor<2x16x8xf32>, |
| %Out: tensor<2x16x16xf32>) -> tensor<2x16x16xf32> { |
| %0 = linalg.generic |
| {indexing_maps = [#map_bnc_tb_a, #map_bnc_tb_b, #map_bnc_tb_c], |
| iterator_types = ["parallel", "parallel", "reduction", "parallel"]} |
| ins(%A, %B : tensor<2x16x8xf32>, tensor<2x16x8xf32>) |
| outs(%Out : tensor<2x16x16xf32>) { |
| ^bb0(%in: f32, %in_0: f32, %out: f32): |
| %1 = arith.mulf %in, %in_0 : f32 |
| %2 = arith.addf %out, %1 : f32 |
| linalg.yield %2 : f32 |
| } -> tensor<2x16x16xf32> |
| return %0 : tensor<2x16x16xf32> |
| } |
| |
| // NAMED-DAG: #[[$MAP_A:.+]] = affine_map<(d0, d1, d2, d3) -> (d0, d1, d3)> |
| // NAMED-DAG: #[[$MAP_TB:.+]] = affine_map<(d0, d1, d2, d3) -> (d0, d2, d3)> |
| // NAMED-DAG: #[[$MAP_C:.+]] = affine_map<(d0, d1, d2, d3) -> (d0, d1, d2)> |
| |
| // CATEGORY-DAG: #[[$MAP_A:.+]] = affine_map<(d0, d1, d2, d3) -> (d0, d1, d2)> |
| // CATEGORY-DAG: #[[$MAP_TB:.+]] = affine_map<(d0, d1, d2, d3) -> (d0, d3, d2)> |
| // CATEGORY-DAG: #[[$MAP_C:.+]] = affine_map<(d0, d1, d2, d3) -> (d0, d1, d3)> |
| |
| // ALL-LABEL: op_batch_matmul_non_canonical_transpose_b |
| // ALL-SAME: %[[A:.+]]: tensor<2x16x8xf32>, %[[B:.+]]: tensor<2x16x8xf32>, %[[OUT:.+]]: tensor<2x16x16xf32> |
| |
| // NAMED-NOT: linalg.generic |
| // NAMED: linalg.batch_matmul |
| // NAMED-SAME: indexing_maps = [#[[$MAP_A]], #[[$MAP_TB]], #[[$MAP_C]]] |
| // NAMED-SAME: ins(%[[A]], %[[B]] : tensor<2x16x8xf32>, tensor<2x16x8xf32>) |
| // NAMED-SAME: outs(%[[OUT]] : tensor<2x16x16xf32>) -> tensor<2x16x16xf32> |
| |
| // CATEGORY-NOT: linalg.generic |
| // CATEGORY: linalg.contract |
| // CATEGORY-SAME: indexing_maps = [#[[$MAP_A]], #[[$MAP_TB]], #[[$MAP_C]]] |
| // CATEGORY-SAME: ins(%[[A]], %[[B]] : tensor<2x16x8xf32>, tensor<2x16x8xf32>) |
| // CATEGORY-SAME: outs(%[[OUT]] : tensor<2x16x16xf32>) -> tensor<2x16x16xf32> |
| |
| // ----- |
| |
| // Matmul with fully permuted loop ordering. |
| #map_fs_a = affine_map<(d0, d1, d2) -> (d1, d0)> |
| #map_fs_b = affine_map<(d0, d1, d2) -> (d0, d2)> |
| #map_fs_c = affine_map<(d0, d1, d2) -> (d1, d2)> |
| func.func @op_matmul_fully_shuffled_loops(%A: tensor<?x?xf32>, %B: tensor<?x?xf32>, |
| %Out: tensor<?x?xf32>) -> tensor<?x?xf32> { |
| %0 = linalg.generic |
| {indexing_maps = [#map_fs_a, #map_fs_b, #map_fs_c], |
| iterator_types = ["reduction", "parallel", "parallel"]} |
| ins(%A, %B : tensor<?x?xf32>, tensor<?x?xf32>) |
| outs(%Out : tensor<?x?xf32>) { |
| ^bb0(%in: f32, %in_0: f32, %out: f32): |
| %1 = arith.mulf %in, %in_0 : f32 |
| %2 = arith.addf %out, %1 : f32 |
| linalg.yield %2 : f32 |
| } -> tensor<?x?xf32> |
| return %0 : tensor<?x?xf32> |
| } |
| |
| // CATEGORY-DAG: #[[$MAP_A:.+]] = affine_map<(d0, d1, d2) -> (d1, d0)> |
| // CATEGORY-DAG: #[[$MAP_B:.+]] = affine_map<(d0, d1, d2) -> (d0, d2)> |
| // CATEGORY-DAG: #[[$MAP_C:.+]] = affine_map<(d0, d1, d2) -> (d1, d2)> |
| |
| // ALL-LABEL: op_matmul_fully_shuffled_loops |
| // ALL-SAME: %[[A:.+]]: tensor<?x?xf32>, %[[B:.+]]: tensor<?x?xf32>, %[[OUT:.+]]: tensor<?x?xf32> |
| |
| // NAMED-NOT: linalg.generic |
| // NAMED: linalg.matmul |
| // NAMED-SAME: ins(%[[A]], %[[B]] : tensor<?x?xf32>, tensor<?x?xf32>) |
| // NAMED-SAME: outs(%[[OUT]] : tensor<?x?xf32>) -> tensor<?x?xf32> |
| |
| // CATEGORY-NOT: linalg.generic |
| // CATEGORY: linalg.contract |
| // CATEGORY-SAME: indexing_maps = [#[[$MAP_A]], #[[$MAP_TB]], #[[$MAP_C]]] |
| // CATEGORY-SAME: ins(%[[A]], %[[B]] : tensor<?x?xf32>, tensor<?x?xf32>) |
| // CATEGORY-SAME: outs(%[[OUT]] : tensor<?x?xf32>) -> tensor<?x?xf32> |
| |
| // ----- |
| |
| // TODO: this could also be specialized to a named matmul. |
| #map_bcast_a = affine_map<(d0, d1, d2) -> (d2)> |
| #map_bcast_b = affine_map<(d0, d1, d2) -> (d2, d1)> |
| #map_bcast_c = affine_map<(d0, d1, d2) -> (d0, d1)> |
| func.func @op_matmul_broadcast_a(%A: tensor<?xf32>, %B: tensor<?x?xf32>, |
| %Out: tensor<?x?xf32>) -> tensor<?x?xf32> { |
| %0 = linalg.generic |
| {indexing_maps = [#map_bcast_a, #map_bcast_b, #map_bcast_c], |
| iterator_types = ["parallel", "parallel", "reduction"]} |
| ins(%A, %B : tensor<?xf32>, tensor<?x?xf32>) |
| outs(%Out : tensor<?x?xf32>) { |
| ^bb0(%in: f32, %in_0: f32, %out: f32): |
| %1 = arith.mulf %in, %in_0 : f32 |
| %2 = arith.addf %out, %1 : f32 |
| linalg.yield %2 : f32 |
| } -> tensor<?x?xf32> |
| return %0 : tensor<?x?xf32> |
| } |
| |
| // ALL-LABEL: op_matmul_broadcast_a |
| |
| // ALL-NOT: linalg.generic |
| // ALL: linalg.contract |
| |
| // ----- |
| |
| // TODO: this could also be specialized to a named batch_matmul. |
| #map_bbcast_a = affine_map<(d0, d1, d2, d3) -> (d1, d3)> |
| #map_bbcast_b = affine_map<(d0, d1, d2, d3) -> (d0, d3, d2)> |
| #map_bbcast_c = affine_map<(d0, d1, d2, d3) -> (d0, d1, d2)> |
| func.func @op_batch_matmul_broadcast_a(%A: tensor<16x8xf32>, %B: tensor<2x8x16xf32>, |
| %Out: tensor<2x16x16xf32>) -> tensor<2x16x16xf32> { |
| %0 = linalg.generic |
| {indexing_maps = [#map_bbcast_a, #map_bbcast_b, #map_bbcast_c], |
| iterator_types = ["parallel", "parallel", "parallel", "reduction"]} |
| ins(%A, %B : tensor<16x8xf32>, tensor<2x8x16xf32>) |
| outs(%Out : tensor<2x16x16xf32>) { |
| ^bb0(%in: f32, %in_0: f32, %out: f32): |
| %1 = arith.mulf %in, %in_0 : f32 |
| %2 = arith.addf %out, %1 : f32 |
| linalg.yield %2 : f32 |
| } -> tensor<2x16x16xf32> |
| return %0 : tensor<2x16x16xf32> |
| } |
| |
| // ALL-LABEL: op_batch_matmul_broadcast_a |
| |
| // ALL-NOT: linalg.generic |
| // ALL: linalg.contract |
| |
| // ----- |
| |
| // TODO: this could also be specialized to a named batch_matmul. |
| #map_bbcast2_a = affine_map<(d0, d1, d2, d3) -> (d0, d1, d3)> |
| #map_bbcast2_b = affine_map<(d0, d1, d2, d3) -> (d3)> |
| #map_bbcast2_c = affine_map<(d0, d1, d2, d3) -> (d0, d1, d2)> |
| func.func @op_batch_matmul_broadcast_b(%A: tensor<2x16x8xf32>, %B: tensor<8xf32>, |
| %Out: tensor<2x16x16xf32>) -> tensor<2x16x16xf32> { |
| %0 = linalg.generic |
| {indexing_maps = [#map_bbcast2_a, #map_bbcast2_b, #map_bbcast2_c], |
| iterator_types = ["parallel", "parallel", "parallel", "reduction"]} |
| ins(%A, %B : tensor<2x16x8xf32>, tensor<8xf32>) |
| outs(%Out : tensor<2x16x16xf32>) { |
| ^bb0(%in: f32, %in_0: f32, %out: f32): |
| %1 = arith.mulf %in, %in_0 : f32 |
| %2 = arith.addf %out, %1 : f32 |
| linalg.yield %2 : f32 |
| } -> tensor<2x16x16xf32> |
| return %0 : tensor<2x16x16xf32> |
| } |
| |
| // ALL-LABEL: op_batch_matmul_broadcast_b |
| |
| // ALL-NOT: linalg.generic |
| // ALL: linalg.contract |
| |
| // ----- |
| |
| ///---------------------------------------------------------------------------------------- |
| /// Tests for linalg.mmt4d |
| ///---------------------------------------------------------------------------------------- |
| |
| #mapA = affine_map<(m, n, k, m0, n0, k0) -> (m, k, m0, k0)> |
| #mapB = affine_map<(m, n, k, m0, n0, k0) -> (n, k, n0, k0)> |
| #mapC = affine_map<(m, n, k, m0, n0, k0) -> (m, n, m0, n0)> |
| func.func @op_mmt4d(%A: tensor<?x?x?x?xf32>, %B: tensor<?x?x?x?xf32>, |
| %C: tensor<?x?x?x?xf32>) -> tensor<?x?x?x?xf32> { |
| %0 = linalg.generic |
| {indexing_maps = [#mapA, #mapB, #mapC], |
| iterator_types = ["parallel", "parallel", "reduction", |
| "parallel", "parallel", "reduction"]} |
| ins(%A, %B : tensor<?x?x?x?xf32>, tensor<?x?x?x?xf32>) |
| outs(%C : tensor<?x?x?x?xf32>) { |
| ^bb0(%in: f32, %in_0: f32, %out: f32): |
| %1 = arith.mulf %in, %in_0 : f32 |
| %2 = arith.addf %out, %1 : f32 |
| linalg.yield %2 : f32 |
| } -> tensor<?x?x?x?xf32> |
| return %0 : tensor<?x?x?x?xf32> |
| } |
| |
| // ALL-LABEL: op_mmt4d |
| |
| // NAMED-NOT: linalg.generic |
| // NAMED: linalg.mmt4d |
| |
| // CATEGORY-NOT: linalg.generic |
| // CATEGORY: linalg.contract |
| |
| // MMT4D transpose A inner and outer: |
| // A is accessed as (k, m, k0, m0) instead of (m, k, m0, k0) |
| #map_tA = affine_map<(m, n, k, m0, n0, k0) -> (k, m, k0, m0)> |
| func.func @op_mmt4d_transpose_a(%A: tensor<?x?x?x?xf32>, %B: tensor<?x?x?x?xf32>, |
| %C: tensor<?x?x?x?xf32>) -> tensor<?x?x?x?xf32> { |
| %0 = linalg.generic |
| {indexing_maps = [#map_tA, #mapB, #mapC], |
| iterator_types = ["parallel", "parallel", "reduction", |
| "parallel", "parallel", "reduction"]} |
| ins(%A, %B : tensor<?x?x?x?xf32>, tensor<?x?x?x?xf32>) |
| outs(%C : tensor<?x?x?x?xf32>) { |
| ^bb0(%in: f32, %in_0: f32, %out: f32): |
| %1 = arith.mulf %in, %in_0 : f32 |
| %2 = arith.addf %out, %1 : f32 |
| linalg.yield %2 : f32 |
| } -> tensor<?x?x?x?xf32> |
| return %0 : tensor<?x?x?x?xf32> |
| } |
| |
| // ALL-LABEL: op_mmt4d_transpose_a |
| |
| // NAMED-NOT: linalg.generic |
| // NAMED: linalg.mmt4d |
| |
| // CATEGORY-NOT: linalg.generic |
| // CATEGORY: linalg.contract |
| |
| // MMT4D transpose B inner and outer: |
| // B is accessed as (k, n, k0, n0) instead of (n, k, n0, k0) |
| #map_tB = affine_map<(m, n, k, m0, n0, k0) -> (k, n, k0, n0)> |
| func.func @op_mmt4d_transpose_b(%A: tensor<?x?x?x?xf32>, %B: tensor<?x?x?x?xf32>, |
| %C: tensor<?x?x?x?xf32>) -> tensor<?x?x?x?xf32> { |
| %0 = linalg.generic |
| {indexing_maps = [#mapA, #map_tB, #mapC], |
| iterator_types = ["parallel", "parallel", "reduction", |
| "parallel", "parallel", "reduction"]} |
| ins(%A, %B : tensor<?x?x?x?xf32>, tensor<?x?x?x?xf32>) |
| outs(%C : tensor<?x?x?x?xf32>) { |
| ^bb0(%in: f32, %in_0: f32, %out: f32): |
| %1 = arith.mulf %in, %in_0 : f32 |
| %2 = arith.addf %out, %1 : f32 |
| linalg.yield %2 : f32 |
| } -> tensor<?x?x?x?xf32> |
| return %0 : tensor<?x?x?x?xf32> |
| } |
| |
| // ALL-LABEL: op_mmt4d_transpose_b |
| |
| // NAMED-NOT: linalg.generic |
| // NAMED: linalg.mmt4d |
| |
| // CATEGORY-NOT: linalg.generic |
| // CATEGORY: linalg.contract |
| |
| // MMT4D transpose both A and B inner and outer: |
| func.func @op_mmt4d_transpose_a_and_b( |
| %A: tensor<?x?x?x?xf32>, %B: tensor<?x?x?x?xf32>, |
| %C: tensor<?x?x?x?xf32>) -> tensor<?x?x?x?xf32> { |
| %0 = linalg.generic |
| {indexing_maps = [#map_tA, #map_tB, #mapC], |
| iterator_types = ["parallel", "parallel", "reduction", |
| "parallel", "parallel", "reduction"]} |
| ins(%A, %B : tensor<?x?x?x?xf32>, tensor<?x?x?x?xf32>) |
| outs(%C : tensor<?x?x?x?xf32>) { |
| ^bb0(%in: f32, %in_0: f32, %out: f32): |
| %1 = arith.mulf %in, %in_0 : f32 |
| %2 = arith.addf %out, %1 : f32 |
| linalg.yield %2 : f32 |
| } -> tensor<?x?x?x?xf32> |
| return %0 : tensor<?x?x?x?xf32> |
| } |
| |
| // ALL-LABEL: op_mmt4d_transpose_a_and_b |
| |
| // NAMED-NOT: linalg.generic |
| // NAMED: linalg.mmt4d |
| |
| // CATEGORY-NOT: linalg.generic |
| // CATEGORY: linalg.contract |
| |
| // MMT4D transpose C inner and outer: |
| // C is accessed as (n, m, n0, m0) instead of (m, n, m0, n0) |
| #map_tC = affine_map<(m, n, k, m0, n0, k0) -> (n, m, n0, m0)> |
| func.func @op_mmt4d_transpose_c(%A: tensor<?x?x?x?xf32>, %B: tensor<?x?x?x?xf32>, |
| %C: tensor<?x?x?x?xf32>) -> tensor<?x?x?x?xf32> { |
| %0 = linalg.generic |
| {indexing_maps = [#mapA, #mapB, #map_tC], |
| iterator_types = ["parallel", "parallel", "reduction", |
| "parallel", "parallel", "reduction"]} |
| ins(%A, %B : tensor<?x?x?x?xf32>, tensor<?x?x?x?xf32>) |
| outs(%C : tensor<?x?x?x?xf32>) { |
| ^bb0(%in: f32, %in_0: f32, %out: f32): |
| %1 = arith.mulf %in, %in_0 : f32 |
| %2 = arith.addf %out, %1 : f32 |
| linalg.yield %2 : f32 |
| } -> tensor<?x?x?x?xf32> |
| return %0 : tensor<?x?x?x?xf32> |
| } |
| |
| // ALL-LABEL: op_mmt4d_transpose_c |
| |
| // NAMED-NOT: linalg.generic |
| // NAMED: linalg.mmt4d |
| |
| // CATEGORY-NOT: linalg.generic |
| // CATEGORY: linalg.contract |
| |
| // MMT4D transpose C inner only: |
| // C is accessed as (m, n, n0, m0) instead of (m, n, m0, n0) |
| #map_tC_inner = affine_map<(m, n, k, m0, n0, k0) -> (m, n, n0, m0)> |
| func.func @op_mmt4d_transpose_c_inner(%A: tensor<?x?x?x?xf32>, %B: tensor<?x?x?x?xf32>, |
| %C: tensor<?x?x?x?xf32>) -> tensor<?x?x?x?xf32> { |
| %0 = linalg.generic |
| {indexing_maps = [#mapA, #mapB, #map_tC_inner], |
| iterator_types = ["parallel", "parallel", "reduction", |
| "parallel", "parallel", "reduction"]} |
| ins(%A, %B : tensor<?x?x?x?xf32>, tensor<?x?x?x?xf32>) |
| outs(%C : tensor<?x?x?x?xf32>) { |
| ^bb0(%in: f32, %in_0: f32, %out: f32): |
| %1 = arith.mulf %in, %in_0 : f32 |
| %2 = arith.addf %out, %1 : f32 |
| linalg.yield %2 : f32 |
| } -> tensor<?x?x?x?xf32> |
| return %0 : tensor<?x?x?x?xf32> |
| } |
| |
| // ALL-LABEL: op_mmt4d_transpose_c_inner |
| |
| // NAMED-NOT: linalg.generic |
| // NAMED: linalg.mmt4d |
| |
| // CATEGORY-NOT: linalg.generic |
| // CATEGORY: linalg.contract |
| |
| // Negative MMT4D: |
| // A can only be accessed as inner transpose or outer transpose of (m, k, m0, k0) |
| #mapA_negative = affine_map<(m, n, k, m0, n0, k0) -> (n, k, n0, k0)> |
| func.func @negative_op_mmt4d(%A: tensor<?x?x?x?xf32>, %B: tensor<?x?x?x?xf32>, |
| %C: tensor<?x?x?x?xf32>) -> tensor<?x?x?x?xf32> { |
| %0 = linalg.generic |
| {indexing_maps = [#mapA_negative, #mapB, #mapC], |
| iterator_types = ["parallel", "parallel", "reduction", |
| "parallel", "parallel", "reduction"]} |
| ins(%A, %B : tensor<?x?x?x?xf32>, tensor<?x?x?x?xf32>) |
| outs(%C : tensor<?x?x?x?xf32>) { |
| ^bb0(%in: f32, %in_0: f32, %out: f32): |
| %1 = arith.mulf %in, %in_0 : f32 |
| %2 = arith.addf %out, %1 : f32 |
| linalg.yield %2 : f32 |
| } -> tensor<?x?x?x?xf32> |
| return %0 : tensor<?x?x?x?xf32> |
| } |
| |
| // ALL-LABEL: negative_op_mmt4d |
| |
| // ALL-NOT: linalg.generic |
| // ALL: linalg.contract |