add_custom_target(Linalg)
set_target_properties(Linalg PROPERTIES FOLDER Examples)
add_dependencies(Linalg
- linalg-conversion-1
linalg-conversion-3
- linalg-example-1
linalg-example-2
linalg-example-3
linalg-example-4
add_subdirectory(lib)
-
-set(LLVM_LINK_COMPONENTS
- Core
- Support
- )
-
-set(LLVM_OPTIONAL_SOURCES Conversion.cpp Example.cpp)
-
-add_llvm_example(linalg-conversion-1
- Conversion.cpp
- )
-
-add_llvm_example(linalg-example-1
- Example.cpp
- )
-
-target_link_libraries(linalg-example-1
- PRIVATE
- Linalg1DialectConstruction
- Linalg1
- )
-
-whole_archive_link(linalg-example-1
- MLIRStandardOps
- )
-
-target_link_libraries(linalg-conversion-1
- PRIVATE
- Linalg1DialectConstruction
- Linalg1
- )
-
-whole_archive_link(linalg-conversion-1
- MLIRStandardOps
- )
+++ /dev/null
-//===- Conversion.cpp - Linalg to LLVM conversion driver ------------------===//
-//
-// Copyright 2019 The MLIR Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// =============================================================================
-
-// RUN: %p/conversion | FileCheck %s
-
-#include "TestHarness.h"
-
-#include "linalg1/Common.h"
-#include "linalg1/ConvertToLLVMDialect.h"
-#include "linalg1/Dialect.h"
-#include "linalg1/Intrinsics.h"
-#include "linalg1/Ops.h"
-#include "linalg1/Types.h"
-#include "mlir/EDSC/Builders.h"
-#include "mlir/EDSC/Intrinsics.h"
-#include "mlir/IR/Function.h"
-
-using namespace linalg;
-using namespace linalg::common;
-using namespace linalg::intrinsics;
-using namespace mlir;
-using namespace mlir::edsc;
-using namespace mlir::edsc::intrinsics;
-
-TEST_FUNC(rangeConversion) {
- // Define the MLIR context, create a Module in this context, and a Builder to
- // facilitate type construction.
- MLIRContext context;
- Module module(&context);
- Builder builder(&module);
-
- // Declare a function called "rangeConversion" with type:
- // (index, index, index) -> ()
- // define it, and add it to the module.
- FunctionType funcType = builder.getFunctionType(
- {builder.getIndexType(), builder.getIndexType(), builder.getIndexType()},
- {});
- Function *f =
- new Function(builder.getUnknownLoc(), "rangeConversion", funcType);
- f->addEntryBlock();
- module.getFunctions().push_back(f);
-
- // Construct a linalg::RangeOp taking function arguments as operands.
- ScopedContext scope(f);
- ValueHandle arg0(f->getArgument(0)), arg1(f->getArgument(1)),
- arg2(f->getArgument(2));
- {
- range(arg0, arg1, arg2);
- ret();
- }
-
- // clang-format off
- // CHECK-LABEL: @rangeConversion
- // CHECK-NEXT: %0 = llvm.undef : !llvm<"{ i64, i64, i64 }">
- // CHECK-NEXT: %1 = llvm.insertvalue %arg0, %0[0] : !llvm<"{ i64, i64, i64 }">
- // CHECK-NEXT: %2 = llvm.insertvalue %arg1, %1[1] : !llvm<"{ i64, i64, i64 }">
- // CHECK-NEXT: %3 = llvm.insertvalue %arg2, %2[2] : !llvm<"{ i64, i64, i64 }">
- // clang-format on
- convertToLLVM(module);
- module.print(llvm::outs());
-}
-
-TEST_FUNC(viewRangeConversion) {
- // Define the MLIR context, create a Module in this context, and a Builder to
- // facilitate type construction.
- MLIRContext context;
- Module module(&context);
- Builder builder(&module);
-
- // Declare a function called "viewRangeConversion" with type:
- // (memref<?x?xf32>, !linalg.range, !linalg.range) -> ()
- // define it, and add it to the module.
- FunctionType funcType = builder.getFunctionType(
- {builder.getMemRefType({-1, -1}, builder.getF32Type(), {}, 0),
- builder.getType<RangeType>(), builder.getType<RangeType>()},
- {});
- Function *f =
- new Function(builder.getUnknownLoc(), "viewRangeConversion", funcType);
- f->addEntryBlock();
- module.getFunctions().push_back(f);
-
- // Construct a linalg::ViewOp taking function arguments as operands.
- ScopedContext scope(f);
- ValueHandle memref(f->getArgument(0)), range1(f->getArgument(1)),
- range2(f->getArgument(2));
- {
- view(memref, {range1, range2});
- ret();
- }
-
- // clang-format off
- // CHECK-LABEL: @viewRangeConversion
- // CHECK-NEXT: %0 = llvm.undef : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
- // CHECK-NEXT: %1 = llvm.extractvalue %arg0[0] : !llvm<"{ float*, i64, i64 }">
- // CHECK-NEXT: %2 = llvm.insertvalue %1, %0[0] : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
- // CHECK-NEXT: %3 = llvm.extractvalue %arg0[2] : !llvm<"{ float*, i64, i64 }">
- // CHECK-NEXT: %4 = llvm.constant(1 : index) : !llvm.i64
- // CHECK-NEXT: %5 = llvm.mul %4, %3 : !llvm.i64
- // CHECK-NEXT: %6 = llvm.constant(0 : index) : !llvm.i64
- // CHECK-NEXT: %7 = llvm.extractvalue %arg1[0] : !llvm<"{ i64, i64, i64 }">
- // CHECK-NEXT: %8 = llvm.mul %7, %5 : !llvm.i64
- // CHECK-NEXT: %9 = llvm.add %6, %8 : !llvm.i64
- // CHECK-NEXT: %10 = llvm.extractvalue %arg2[0] : !llvm<"{ i64, i64, i64 }">
- // CHECK-NEXT: %11 = llvm.mul %10, %4 : !llvm.i64
- // CHECK-NEXT: %12 = llvm.add %9, %11 : !llvm.i64
- // CHECK-NEXT: %13 = llvm.insertvalue %12, %2[1] : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
- // CHECK-NEXT: %14 = llvm.extractvalue %arg1[0] : !llvm<"{ i64, i64, i64 }">
- // CHECK-NEXT: %15 = llvm.extractvalue %arg1[1] : !llvm<"{ i64, i64, i64 }">
- // CHECK-NEXT: %16 = llvm.sub %15, %14 : !llvm.i64
- // CHECK-NEXT: %17 = llvm.insertvalue %16, %13[2, 0] : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
- // CHECK-NEXT: %18 = llvm.extractvalue %arg2[0] : !llvm<"{ i64, i64, i64 }">
- // CHECK-NEXT: %19 = llvm.extractvalue %arg2[1] : !llvm<"{ i64, i64, i64 }">
- // CHECK-NEXT: %20 = llvm.sub %19, %18 : !llvm.i64
- // CHECK-NEXT: %21 = llvm.insertvalue %20, %17[2, 1] : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
- // CHECK-NEXT: %22 = llvm.extractvalue %arg1[2] : !llvm<"{ i64, i64, i64 }">
- // CHECK-NEXT: %23 = llvm.mul %5, %22 : !llvm.i64
- // CHECK-NEXT: %24 = llvm.insertvalue %23, %21[3, 0] : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
- // CHECK-NEXT: %25 = llvm.extractvalue %arg2[2] : !llvm<"{ i64, i64, i64 }">
- // CHECK-NEXT: %26 = llvm.mul %4, %25 : !llvm.i64
- // CHECK-NEXT: %27 = llvm.insertvalue %26, %24[3, 1] : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
- // clang-format on
- convertToLLVM(module);
- module.print(llvm::outs());
-}
-
-TEST_FUNC(viewNonRangeConversion) {
- // Define the MLIR context, create a Module in this context, and a Builder to
- // facilitate type construction.
- MLIRContext context;
- Module module(&context);
- Builder builder(&module);
-
- // Declare a function called "viewNonRangeConversion" with type:
- // (memref<?x?xf32>, !linalg.range, index) -> ()
- // define it, and add it to the module.
- FunctionType funcType = builder.getFunctionType(
- {builder.getMemRefType({-1, -1}, builder.getF32Type(), {}, 0),
- builder.getType<RangeType>(), builder.getIndexType()},
- {});
- Function *f =
- new Function(builder.getUnknownLoc(), "viewNonRangeConversion", funcType);
- f->addEntryBlock();
- module.getFunctions().push_back(f);
-
- // Construct a linalg::ViewOp taking function arguments as operands.
- ScopedContext scope(f);
- ValueHandle memref(f->getArgument(0)), range(f->getArgument(1)),
- index(f->getArgument(2));
- {
- view(memref, {range, index});
- ret();
- }
-
- // clang-format off
- // CHECK-LABEL: @viewNonRangeConversion
- // CHECK-NEXT: %0 = llvm.undef : !llvm<"{ float*, i64, [1 x i64], [1 x i64] }">
- // CHECK-NEXT: %1 = llvm.extractvalue %arg0[0] : !llvm<"{ float*, i64, i64 }">
- // CHECK-NEXT: %2 = llvm.insertvalue %1, %0[0] : !llvm<"{ float*, i64, [1 x i64], [1 x i64] }">
- // CHECK-NEXT: %3 = llvm.extractvalue %arg0[2] : !llvm<"{ float*, i64, i64 }">
- // CHECK-NEXT: %4 = llvm.constant(1 : index) : !llvm.i64
- // CHECK-NEXT: %5 = llvm.mul %4, %3 : !llvm.i64
- // CHECK-NEXT: %6 = llvm.constant(0 : index) : !llvm.i64
- // CHECK-NEXT: %7 = llvm.extractvalue %arg1[0] : !llvm<"{ i64, i64, i64 }">
- // CHECK-NEXT: %8 = llvm.mul %7, %5 : !llvm.i64
- // CHECK-NEXT: %9 = llvm.add %6, %8 : !llvm.i64
- // CHECK-NEXT: %10 = llvm.mul %arg2, %4 : !llvm.i64
- // CHECK-NEXT: %11 = llvm.add %9, %10 : !llvm.i64
- // CHECK-NEXT: %12 = llvm.insertvalue %11, %2[1] : !llvm<"{ float*, i64, [1 x i64], [1 x i64] }">
- // CHECK-NEXT: %13 = llvm.extractvalue %arg1[0] : !llvm<"{ i64, i64, i64 }">
- // CHECK-NEXT: %14 = llvm.extractvalue %arg1[1] : !llvm<"{ i64, i64, i64 }">
- // CHECK-NEXT: %15 = llvm.sub %14, %13 : !llvm.i64
- // CHECK-NEXT: %16 = llvm.insertvalue %15, %12[2, 0] : !llvm<"{ float*, i64, [1 x i64], [1 x i64] }">
- // CHECK-NEXT: %17 = llvm.extractvalue %arg1[2] : !llvm<"{ i64, i64, i64 }">
- // CHECK-NEXT: %18 = llvm.mul %5, %17 : !llvm.i64
- // CHECK-NEXT: %19 = llvm.insertvalue %18, %16[3, 0] : !llvm<"{ float*, i64, [1 x i64], [1 x i64] }">
- // clang-format on
- convertToLLVM(module);
- module.print(llvm::outs());
-}
-
-TEST_FUNC(sliceRangeConversion) {
- // Define the MLIR context, create a Module in this context, and a Builder to
- // facilitate type construction.
- MLIRContext context;
- Module module(&context);
- Builder builder(&module);
-
- // Declare a function called "sliceRangeConversion" with type:
- // (memref<?x?xf32>, !linalg.range, !linalg.range, !linalg.range) -> ()
- // define it, and add it to the module.
- FunctionType funcType = builder.getFunctionType(
- {builder.getMemRefType({-1, -1}, builder.getF32Type(), {}, 0),
- builder.getType<RangeType>(), builder.getType<RangeType>(),
- builder.getType<RangeType>()},
- {});
- Function *f =
- new Function(builder.getUnknownLoc(), "sliceRangeConversion", funcType);
- f->addEntryBlock();
- module.getFunctions().push_back(f);
-
- // Construct a linalg::SliceOp based on the result of a linalg::ViewOp.
- // Note: SliceOp builder does not support ViewOps that are not defined by
- // a dominating ViewOp.
- ScopedContext scope(f);
- ValueHandle memref(f->getArgument(0)), range1(f->getArgument(1)),
- range2(f->getArgument(2)), range3(f->getArgument(3));
- {
- slice(view(memref, {range1, range2}), range3, 0);
- ret();
- }
-
- // clang-format off
- // CHECK-LABEL: @sliceRangeConversion
- // CHECK: %28 = llvm.undef : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
- // CHECK-NEXT: %29 = llvm.extractvalue %27[0] : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
- // CHECK-NEXT: %30 = llvm.insertvalue %29, %28[0] : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
- // CHECK-NEXT: %31 = llvm.extractvalue %27[1] : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
- // CHECK-NEXT: %32 = llvm.extractvalue %arg3[0] : !llvm<"{ i64, i64, i64 }">
- // CHECK-NEXT: %33 = llvm.extractvalue %27[3, 0] : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
- // CHECK-NEXT: %34 = llvm.mul %32, %33 : !llvm.i64
- // CHECK-NEXT: %35 = llvm.add %31, %34 : !llvm.i64
- // CHECK-NEXT: %36 = llvm.insertvalue %35, %30[1] : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
- // CHECK-NEXT: %37 = llvm.extractvalue %arg3[1] : !llvm<"{ i64, i64, i64 }">
- // CHECK-NEXT: %38 = llvm.extractvalue %arg3[0] : !llvm<"{ i64, i64, i64 }">
- // CHECK-NEXT: %39 = llvm.sub %37, %38 : !llvm.i64
- // CHECK-NEXT: %40 = llvm.extractvalue %27[3, 0] : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
- // CHECK-NEXT: %41 = llvm.extractvalue %arg3[2] : !llvm<"{ i64, i64, i64 }">
- // CHECK-NEXT: %42 = llvm.mul %40, %41 : !llvm.i64
- // CHECK-NEXT: %43 = llvm.insertvalue %39, %36[2, 0] : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
- // CHECK-NEXT: %44 = llvm.insertvalue %42, %43[3, 0] : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
- // CHECK-NEXT: %45 = llvm.extractvalue %27[2, 1] : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
- // CHECK-NEXT: %46 = llvm.extractvalue %27[3, 1] : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
- // CHECK-NEXT: %47 = llvm.insertvalue %45, %44[2, 1] : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
- // CHECK-NEXT: %48 = llvm.insertvalue %46, %47[3, 1] : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
- // clang-format on
- convertToLLVM(module);
- module.print(llvm::outs());
-}
-
-TEST_FUNC(sliceNonRangeConversion) {
- // Define the MLIR context, create a Module in this context, and a Builder to
- // facilitate type construction.
- MLIRContext context;
- Module module(&context);
- Builder builder(&module);
-
- // Declare a function called "sliceNonRangeConversion" with type:
- // (memref<?x?xf32>, !linalg.range, !linalg.range, !linalg.range) -> ()
- // define it, and add it to the module.
- FunctionType funcType = builder.getFunctionType(
- {builder.getMemRefType({-1, -1}, builder.getF32Type(), {}, 0),
- builder.getType<RangeType>(), builder.getType<RangeType>(),
- builder.getIndexType()},
- {});
- Function *f = new Function(builder.getUnknownLoc(), "sliceNonRangeConversion",
- funcType);
- f->addEntryBlock();
- module.getFunctions().push_back(f);
-
- // Construct a linalg::SliceOp based on the result of a linalg::ViewOp.
- // Note: SliceOp builder does not support ViewOps that are not defined by
- // a dominating ViewOp.
- ScopedContext scope(f);
- ValueHandle memref(f->getArgument(0)), range1(f->getArgument(1)),
- range2(f->getArgument(2)), index(f->getArgument(3));
- {
- slice(view(memref, {range1, range2}), index, 0);
- ret();
- }
-
- // CHECK-LABEL: @sliceNonRangeConversion
- // CHECK: %28 = llvm.undef : !llvm<"{ float*, i64, [1 x i64], [1 x i64]
- // }"> CHECK-NEXT: %29 = llvm.extractvalue %27[0] : !llvm<"{ float*, i64, [2 x
- // i64], [2 x i64] }"> CHECK-NEXT: %30 = llvm.insertvalue %29, %28[0] :
- // !llvm<"{ float*, i64, [1 x i64], [1 x i64] }"> CHECK-NEXT: %31 =
- // llvm.extractvalue %27[1] : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
- // CHECK-NEXT: %32 = llvm.extractvalue %27[3, 0] : !llvm<"{ float*, i64, [2 x
- // i64], [2 x i64] }"> CHECK-NEXT: %33 = llvm.mul %arg3, %32 : !llvm.i64
- // CHECK-NEXT: %34 = llvm.add %31, %33 : !llvm.i64
- // CHECK-NEXT: %35 = llvm.insertvalue %34, %30[1] : !llvm<"{ float*, i64, [1 x
- // i64], [1 x i64] }"> CHECK-NEXT: %36 = llvm.extractvalue %27[2, 1] :
- // !llvm<"{ float*, i64, [2 x i64], [2 x i64] }"> CHECK-NEXT: %37 =
- // llvm.extractvalue %27[3, 1] : !llvm<"{ float*, i64, [2 x i64], [2 x i64]
- // }"> CHECK-NEXT: %38 = llvm.insertvalue %36, %35[2, 0] : !llvm<"{ float*,
- // i64, [1 x i64], [1 x i64] }"> CHECK-NEXT: %39 = llvm.insertvalue %37,
- // %38[3, 0] : !llvm<"{ float*, i64, [1 x i64], [1 x i64] }">
- convertToLLVM(module);
- module.print(llvm::outs());
-}
-
-int main() {
- mlir::registerDialect<linalg::LinalgDialect>();
- RUN_TESTS();
- return 0;
-}
+++ /dev/null
-//===- Example.cpp - Our running example ----------------------------------===//
-//
-// Copyright 2019 The MLIR Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// =============================================================================
-
-// RUN: %p/test | FileCheck %s
-
-#include "TestHarness.h"
-
-#include "linalg1/Common.h"
-#include "linalg1/Dialect.h"
-#include "linalg1/Intrinsics.h"
-#include "linalg1/Ops.h"
-#include "linalg1/Types.h"
-#include "linalg1/Utils.h"
-#include "mlir/IR/Function.h"
-
-using namespace linalg;
-using namespace linalg::common;
-using namespace linalg::intrinsics;
-using namespace mlir;
-using namespace mlir::edsc;
-using namespace mlir::edsc::intrinsics;
-
-TEST_FUNC(view_op) {
- MLIRContext context;
- Module module(&context);
- auto indexType = mlir::IndexType::get(&context);
- Function *f =
- makeFunction(module, "view_op", {indexType, indexType, indexType}, {});
-
- ScopedContext scope(f);
-
- // Let's be lazy and define some custom ops that prevent DCE.
- CustomOperation<OperationHandle> some_consumer("some_consumer");
-
- // clang-format off
- ValueHandle M(f->getArgument(0)), N(f->getArgument(1)),
- A0 = alloc(floatMemRefType<0>(&context)),
- A1 = alloc(floatMemRefType<1>(&context), {M}),
- A2 = alloc(floatMemRefType<2>(&context), {M, N}),
- r0 = range(constant_index(3), constant_index(17), constant_index(1)),
- v0 = view(A0, {}),
- v1 = view(A1, {r0}),
- v2 = view(A2, {r0, r0});
- some_consumer({v0, v1, v2});
- ret();
- // CHECK-LABEL: func @view_op
- // CHECK: %[[R:.*]] = linalg.range %{{.*}}:%{{.*}}:%{{.*}} : !linalg.range
- // CHECK-NEXT: {{.*}} = linalg.view {{.*}}[] : !linalg.view<f32>
- // CHECK-NEXT: {{.*}} = linalg.view {{.*}}[%[[R]]] : !linalg.view<?xf32>
- // CHECK-NEXT: {{.*}} = linalg.view {{.*}}[%[[R]], %[[R]]] : !linalg.view<?x?xf32>
- // clang-format on
-
- cleanupAndPrintFunction(f);
-}
-
-TEST_FUNC(slice_op) {
- MLIRContext context;
- Module module(&context);
- auto indexType = mlir::IndexType::get(&context);
- Function *f =
- makeFunction(module, "slice_op", {indexType, indexType, indexType}, {});
-
- ScopedContext scope(f);
-
- // Let's be lazy and define some custom op that prevents DCE.
- CustomOperation<OperationHandle> some_consumer("some_consumer");
-
- // clang-format off
- ValueHandle M(f->getArgument(0)), N(f->getArgument(1)),
- A = alloc(floatMemRefType<2>(&context), {M, N});
- ViewOp vA = emitAndReturnViewOpFromMemRef(A);
- IndexHandle i, j;
- LoopNestRangeBuilder({&i, &j}, vA.getRanges())({
- some_consumer(slice(vA, i, 1)),
- some_consumer(slice(slice(vA, j, 0), i, 0)),
- });
- ret();
- // CHECK-LABEL: func @slice_op(%arg0: index, %arg1: index, %arg2: index) {
- // CHECK: %[[ALLOC:.*]] = alloc(%arg0, %arg1) : memref<?x?xf32>
- // CHECK-NEXT: %[[M:.*]] = dim %0, 0 : memref<?x?xf32>
- // CHECK-NEXT: %[[N:.*]] = dim %0, 1 : memref<?x?xf32>
- // CHECK-NEXT: %[[R1:.*]] = linalg.range {{.*}}:%[[M]]:{{.*}} : !linalg.range
- // CHECK-NEXT: %[[R2:.*]] = linalg.range {{.*}}:%[[N]]:{{.*}} : !linalg.range
- // CHECK-NEXT: %[[V:.*]] = linalg.view %0[%[[R1]], %[[R2]]] : !linalg.view<?x?xf32>
- // CHECK-NEXT: for %i0 = 0 to (d0) -> (d0)(%[[M]]) {
- // CHECK-NEXT: for %i1 = 0 to (d0) -> (d0)(%[[N]]) {
- // CHECK-NEXT: %[[S1:.*]] = linalg.slice %[[V]][*, %i0] : !linalg.view<?xf32>
- // CHECK-NEXT: "some_consumer"(%[[S1]]) : (!linalg.view<?xf32>) -> ()
- // CHECK-NEXT: %[[S2:.*]] = linalg.slice %[[V]][%i1, *] : !linalg.view<?xf32>
- // CHECK-NEXT: %[[S3:.*]] = linalg.slice %[[S2]][%i0] : !linalg.view<f32>
- // CHECK-NEXT: "some_consumer"(%[[S3]]) : (!linalg.view<f32>) -> ()
- // clang-format on
-
- cleanupAndPrintFunction(f);
-}
-
-int main() {
- mlir::registerDialect<linalg::LinalgDialect>();
- RUN_TESTS();
- return 0;
-}
--- /dev/null
+//===- Passes.h - Pass Entrypoints ------------------------------*- C++ -*-===//
+//
+// Copyright 2019 The MLIR Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// =============================================================================
+//
+// This header file defines prototypes that expose pass constructors.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LINALG1_PASSES_H
+#define LINALG1_PASSES_H
+
+#include "mlir/Support/LLVM.h"
+#include <functional>
+#include <limits>
+
+namespace mlir {
+class ModulePassBase;
+} // namespace mlir
+
+namespace linalg {
+
+mlir::ModulePassBase *createLowerLinalgToLLVMPass();
+
+} // namespace linalg
+
+#endif // LINALG1_PASSES_H
Utils.cpp
ViewType.cpp
DialectConstruction.cpp
+ DialectRegistration.cpp
)
+set(LIBS
+ MLIRAffineOps
+ MLIRAnalysis
+ MLIREDSC
+ MLIRLLVMIR
+ MLIRParser
+ MLIRPass
+ MLIRStandardOps
+ MLIRSupport
+ MLIRTransforms
+)
+
add_llvm_library(Linalg1
Analysis.cpp
ConvertToLLVMDialect.cpp
Utils.cpp
ViewType.cpp
)
-
target_link_libraries(Linalg1
PUBLIC
- MLIRAnalysis
- MLIRDialect
- MLIREDSC
- MLIRIR
- MLIRLLVMIR
- MLIRParser
- MLIRPass
- MLIRTransforms
- )
+ ${LIBS}
+ )
+
+add_llvm_library(Linalg1LLVMConversion
+ ConvertToLLVMDialect.cpp
+ )
add_llvm_library(Linalg1DialectConstruction
DialectConstruction.cpp
)
+target_link_libraries(Linalg1DialectConstruction PUBLIC Linalg1)
-target_link_libraries(Linalg1DialectConstruction
- PUBLIC Linalg1)
+add_executable(linalg1-opt
+ DialectRegistration.cpp
+ )
+llvm_update_compile_flags(linalg1-opt)
+whole_archive_link(linalg1-opt
+ Linalg1LLVMConversion
+ Linalg1DialectConstruction
+ ${LIBS}
+ )
+target_link_libraries(linalg1-opt
+ Linalg1
+ Linalg1LLVMConversion
+ Linalg1DialectConstruction
+ MLIRMlirOptLib
+ ${LIBS}
+ LLVMSupport)
#include "linalg1/ConvertToLLVMDialect.h"
#include "linalg1/LLVMIntrinsics.h"
#include "linalg1/Ops.h"
+#include "linalg1/Passes.h"
using namespace mlir;
(void)r;
assert(succeeded(r) && "second conversion failed");
}
+
+namespace {
+struct LowerLinalgToLLVMPass : public ModulePass<LowerLinalgToLLVMPass> {
+ void runOnModule() { linalg::convertToLLVM(getModule()); }
+};
+} // namespace
+
+ModulePassBase *linalg::createLowerLinalgToLLVMPass() {
+ return new LowerLinalgToLLVMPass();
+}
+
+static PassRegistration<LowerLinalgToLLVMPass>
+ pass("lower-linalg-to-llvm",
+ "Lower the operations from the linalg dialect into the LLVM dialect");
#include "linalg1/Ops.h"
#include "linalg1/Types.h"
#include "mlir/IR/Dialect.h"
+#include "mlir/IR/OpImplementation.h"
+#include "mlir/IR/StandardTypes.h"
#include "llvm/Support/raw_ostream.h"
using llvm::raw_ostream;
using llvm::StringRef;
-using mlir::Location;
-using mlir::Type;
+using namespace mlir;
using namespace linalg;
Type LinalgDialect::parseType(StringRef spec, Location loc) const {
- llvm_unreachable("Unhandled linalg dialect parsing");
- return Type();
+ MLIRContext *context = getContext();
+ if (spec == "range")
+ return RangeType::get(getContext());
+
+ StringRef str = spec;
+ if (str.consume_front("view<")) {
+ // Just count the number of ? to get the rank, the type must be f32 for now.
+ unsigned rank = 0;
+ while (str.consume_front("?x"))
+ ++rank;
+ if (str.consume_front("bf16>"))
+ return ViewType::get(context, FloatType::getBF16(context), rank);
+ if (str.consume_front("f16>"))
+ return ViewType::get(context, FloatType::getF16(context), rank);
+ if (str.consume_front("f32>"))
+ return ViewType::get(context, FloatType::getF32(context), rank);
+ if (str.consume_front("f64>"))
+ return ViewType::get(context, FloatType::getF64(context), rank);
+ }
+ return (context->emitError(loc, "unknown Linalg type: " + spec), nullptr);
}
/// RangeType prints as just "range".
--- /dev/null
+//===- DialectRegistration.cpp - Registration of the Linalg dialect -------===//
+//
+// Copyright 2019 The MLIR Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// =============================================================================
+//
+// This file implements a registration for the Linalg Dialect globally.
+// This can just be linked in as a dependence into any binary to enable the
+// Linalg dialect. Note that the binary it is linked into must not already
+// register Linalg or double registration will occur.
+//
+//===----------------------------------------------------------------------===//
+
+#include "linalg1/Dialect.h"
+
+using namespace linalg;
+
+// Dialect registration triggers the creation of a `LinalgDialect` object which
+// adds the proper types and operations to the dialect.
+static mlir::DialectRegistration<LinalgDialect> LinalgOps;
#include "mlir/IR/OpImplementation.h"
#include "mlir/IR/StandardTypes.h"
-using mlir::Builder;
-using mlir::IndexType;
-using mlir::OpAsmParser;
-using mlir::OpAsmPrinter;
-using mlir::OperationState;
-using mlir::Value;
+using llvm::SmallVector;
+using namespace mlir;
+using namespace linalg;
// Minimal example for a new RangeOp operating on RangeType.
void linalg::RangeOp::build(Builder *b, OperationState *result, Value *min,
Value *max, Value *step) {
result->addOperands({min, max, step});
- result->addTypes({linalg::RangeType::get(b->getContext())});
+ result->addTypes({RangeType::get(b->getContext())});
}
// Verification is simply that a RangeOp takes 3 index ssa-value.
return mlir::success();
}
-// Parsing of the linalg dialect is not supported in this tutorial.
bool linalg::RangeOp::parse(OpAsmParser *parser, OperationState *result) {
- llvm_unreachable("Parsing linalg dialect is not supported in this tutorial");
+ SmallVector<OpAsmParser::OperandType, 3> rangeInfo(3);
+ RangeType type;
+ auto indexTy = parser->getBuilder().getIndexType();
+ return parser->parseOperand(rangeInfo[0]) || parser->parseColon() ||
+ parser->parseOperand(rangeInfo[1]) || parser->parseColon() ||
+ parser->parseOperand(rangeInfo[2]) || parser->parseColonType(type) ||
+ parser->resolveOperands(rangeInfo, indexTy, result->operands) ||
+ parser->addTypeToList(type, result->types);
}
// A RangeOp prints as:
// ```
void linalg::RangeOp::print(OpAsmPrinter *p) {
*p << getOperationName() << " " << *getMin() << ":" << *getMax() << ":"
- << *getStep() << " : " << getType();
+ << *getStep();
+ p->printOptionalAttrDict(getAttrs());
+ *p << " : " << getType();
}
}
mlir::LogicalResult linalg::SliceOp::verify() {
+ if (!getAttr(getSlicingDimAttrName()))
+ return emitOpError("slice op expects a dim attribute");
unsigned dim = getSlicingDim();
if (dim >= getParentRank())
return emitOpError("slicing dim must be in the [0 .. parent_rank) range");
return mlir::success();
}
-// Parsing of the linalg dialect is not supported in this tutorial.
bool linalg::SliceOp::parse(OpAsmParser *parser, OperationState *result) {
- llvm_unreachable("Parsing linalg dialect is not supported in this tutorial");
+ OpAsmParser::OperandType viewInfo;
+ SmallVector<OpAsmParser::OperandType, 1> indexingInfo;
+ SmallVector<Type, 8> types;
+ if (parser->parseOperand(viewInfo) ||
+ parser->parseOperandList(indexingInfo, 1,
+ OpAsmParser::Delimiter::Square) ||
+ parser->parseOptionalAttributeDict(result->attributes) ||
+ parser->parseColonTypeList(types))
+ return true;
+
+ if (indexingInfo.size() != 1)
+ return parser->emitError(parser->getNameLoc(), "expected 1 indexing type");
+
+ ViewType viewType = types.front().dyn_cast<ViewType>();
+ if (!viewType)
+ return parser->emitError(parser->getNameLoc(),
+ "view type expected as first type");
+
+ IndexType indexType = types.back().dyn_cast<IndexType>();
+ RangeType rangeType = types.back().dyn_cast<RangeType>();
+ if (!indexType && !rangeType) {
+ llvm::errs() << types.back();
+ return parser->emitError(parser->getNameLoc(),
+ "indexing must be of range or index type");
+ }
+
+ unsigned rank = viewType.getRank();
+ if (indexType)
+ --rank;
+ ViewType resultViewType =
+ ViewType::get(viewType.getContext(), viewType.getElementType(), rank);
+
+ return parser->resolveOperand(viewInfo, viewType, result->operands) ||
+ parser->resolveOperands(indexingInfo[0], types.back(),
+ result->operands) ||
+ parser->addTypeToList(resultViewType, result->types);
}
// A SliceOp prints as:
//
// ```{.mlir}
-// linalg.slice %0[*, %i0] : !linalg.view<?xf32>
+// linalg.slice %0[%i0] {dim: 0} : !linalg.view<?xf32>, index
// ```
//
// Where %0 is an ssa-value holding a `view<?x?xf32>`, %i0 is an ssa-value
// holding an index.
void linalg::SliceOp::print(OpAsmPrinter *p) {
- unsigned dim = getSlicingDim();
- *p << getOperationName() << " " << *getParentView() << "[";
- for (unsigned idx = 0, rank = getParentRank(); idx < rank; ++idx) {
- if (idx != dim) {
- *p << "*";
- } else {
- auto *v = getIndexing();
- if (isa_and_nonnull<RangeOp>(v->getDefiningOp())) {
- *p << *v << "..";
- } else {
- *p << *v;
- }
- }
- *p << ((idx == rank - 1) ? "" : ", ");
- }
- *p << "] : " << getViewType();
+ *p << getOperationName() << " " << *getParentView() << "[" << *getIndexing()
+ << "]";
+ *p << " {dim: ";
+ p->printAttribute(getAttr("dim"));
+ *p << "}";
+ p->printOptionalAttrDict(getAttrs(), {"dim"});
+ *p << " : " << getParentViewType() << ", " << getIndexing()->getType();
}
ViewType linalg::SliceOp::getViewType() { return getType().cast<ViewType>(); }
if (llvm::empty(getOperands()))
return emitOpError(
"requires at least a memref operand followed by 'rank' indices");
- auto memrefType = getOperand(0)->getType().dyn_cast<MemRefType>();
- unsigned memrefRank = memrefType.getRank();
- if (!memrefType)
+ auto memRefType = getOperand(0)->getType().dyn_cast<MemRefType>();
+ unsigned memrefRank = memRefType.getRank();
+ if (!memRefType)
return emitOpError("first operand must be of MemRefType");
unsigned index = 0;
for (auto indexing : getIndexings()) {
return success();
}
-// Parsing of the linalg dialect is not supported in this tutorial.
bool linalg::ViewOp::parse(OpAsmParser *parser, OperationState *result) {
- llvm_unreachable("Parsing linalg dialect is not supported in this tutorial");
+ OpAsmParser::OperandType memRefInfo;
+ SmallVector<OpAsmParser::OperandType, 8> indexingsInfo;
+ SmallVector<Type, 8> types;
+ if (parser->parseOperand(memRefInfo) ||
+ parser->parseOperandList(indexingsInfo, -1,
+ OpAsmParser::Delimiter::Square) ||
+ parser->parseOptionalAttributeDict(result->attributes) ||
+ parser->parseColonTypeList(types))
+ return true;
+
+ if (types.size() != 2 + indexingsInfo.size())
+ return parser->emitError(parser->getNameLoc(),
+ "unexpected number of types ");
+ MemRefType memRefType = types[0].dyn_cast<MemRefType>();
+ if (!memRefType)
+ return parser->emitError(parser->getNameLoc(),
+ "memRef type expected for first type");
+ if (indexingsInfo.size() != memRefType.getRank())
+ return parser->emitError(parser->getNameLoc(),
+ "expected " + Twine(memRefType.getRank()) +
+ " indexings");
+ ViewType viewType = types.back().dyn_cast<ViewType>();
+ if (!viewType)
+ return parser->emitError(parser->getNameLoc(), "view type expected");
+
+ ArrayRef<Type> indexingTypes = ArrayRef<Type>(types).drop_front().drop_back();
+ if (indexingTypes.size() != memRefType.getRank())
+ return parser->emitError(parser->getNameLoc(),
+ "expected " + Twine(memRefType.getRank()) +
+ " indexing types");
+ return parser->resolveOperand(memRefInfo, memRefType, result->operands) ||
+ (!indexingsInfo.empty() &&
+ parser->resolveOperands(indexingsInfo, indexingTypes,
+ indexingsInfo.front().location,
+ result->operands)) ||
+ parser->addTypeToList(viewType, result->types);
}
// A ViewOp prints as:
//
// ```{.mlir}
-// linalg.view %0[%1, %2] : !linalg.view<?x?xf32>
+// linalg.view %0[%1, %2] :
+// memref-type, [indexing-types], !linalg.view<?x?xf32>
// ```
//
// Where %0 is an ssa-value holding a MemRef, %1 and %2 are ssa-value each
for (auto indexing : getIndexings()) {
*p << *indexing << ((index++ == numRanges - 1) ? "" : ", ");
}
- *p << "] : " << getType();
+ p->printOptionalAttrDict(getAttrs());
+ *p << "] : " << getSupportingMemRef()->getType().cast<MemRefType>();
+ for (auto indexing : getIndexings()) {
+ *p << ", " << indexing->getType();
+ }
+ *p << ", " << getType();
}
Type linalg::ViewOp::getElementType() { return getViewType().getElementType(); }
target_link_libraries(linalg-example-2
PRIVATE
- Linalg1
Linalg2
Linalg2DialectConstruction
)
whole_archive_link(linalg-example-2
+ MLIRAffineOps
MLIRStandardOps
)
dot(sA, sB, ssC);
ret();
// CHECK-LABEL: func @linalg_ops(%arg0: index, %arg1: index, %arg2: index) {
- // CHECK: {{.*}} = linalg.slice {{.*}}[*, {{.*}}] : !linalg.view<?xf32>
- // CHECK-NEXT: {{.*}} = linalg.slice {{.*}}[*, {{.*}}] : !linalg.view<?xf32>
- // CHECK-NEXT: {{.*}} = linalg.slice {{.*}}[{{.*}}, *] : !linalg.view<?xf32>
- // CHECK-NEXT: {{.*}} = linalg.slice {{.*}}[{{.*}}] : !linalg.view<f32>
+ // CHECK: {{.*}} = linalg.slice {{.*}}[{{.*}}] {dim: 1} : !linalg.view<?x?xf32>, index
+ // CHECK-NEXT: {{.*}} = linalg.slice {{.*}}[{{.*}}] {dim: 1} : !linalg.view<?x?xf32>, index
+ // CHECK-NEXT: {{.*}} = linalg.slice {{.*}}[{{.*}}] {dim: 0} : !linalg.view<?x?xf32>, index
+ // CHECK-NEXT: {{.*}} = linalg.slice {{.*}}[{{.*}}] {dim: 0} : !linalg.view<?xf32>, index
// CHECK: linalg.matmul({{.*}}, {{.*}}, {{.*}}) : !linalg.view<?x?xf32>
// CHECK-NEXT: linalg.matvec({{.*}}, {{.*}}, {{.*}}) : !linalg.view<?xf32>
// CHECK-NEXT: linalg.dot({{.*}}, {{.*}}, {{.*}}) : !linalg.view<f32>
target_link_libraries(Linalg2
PUBLIC
+ MLIRAffineOps
MLIRAnalysis
MLIRDialect
MLIREDSC
MLIRParser
MLIRPass
MLIRTransforms
+ Linalg1
)
add_llvm_library(Linalg2DialectConstruction
target_link_libraries(linalg-example-3
PRIVATE
- Linalg1
- Linalg2
Linalg3
Linalg3DialectConstruction
)
whole_archive_link(linalg-example-3
+ MLIRAffineOps
MLIRStandardOps
)
target_link_libraries(linalg-conversion-3
PRIVATE
- Linalg1
- Linalg2
Linalg3
Linalg3DialectConstruction
)
target_link_libraries(linalg-execution-3
PRIVATE
MLIRExecutionEngine
- Linalg1
- Linalg2
Linalg3
Linalg3DialectConstruction
)
// clang-format off
// CHECK-LABEL: func @matmul_as_matvec(%arg0: memref<?x?xf32>, %arg1: memref<?x?xf32>, %arg2: memref<?x?xf32>) {
// CHECK: %[[N:.*]] = dim %arg2, 1 : memref<?x?xf32>
- // CHECK: %[[vA:.*]] = linalg.view %arg0[%{{.*}}, %{{.*}}] : !linalg.view<?x?xf32>
+ // CHECK: %[[vA:.*]] = linalg.view %arg0[%{{.*}}, %{{.*}}] : memref<?x?xf32>, !linalg.range, !linalg.range, !linalg.view<?x?xf32>
// CHECK: affine.for %i0 = 0 to (d0) -> (d0)(%[[N]]) {
- // CHECK: %[[vB:.*]] = linalg.view %arg1[%{{.*}}, %{{.*}}] : !linalg.view<?xf32>
- // CHECK: %[[vC:.*]] = linalg.view %arg2[%{{.*}}, %{{.*}}] : !linalg.view<?xf32>
+ // CHECK: %[[vB:.*]] = linalg.view %arg1[%{{.*}}, %{{.*}}] : memref<?x?xf32>, !linalg.range, index, !linalg.view<?xf32>
+ // CHECK: %[[vC:.*]] = linalg.view %arg2[%{{.*}}, %{{.*}}] : memref<?x?xf32>, !linalg.range, index, !linalg.view<?xf32>
// CHECK: linalg.matvec(%[[vA]], %[[vB]], %[[vC]]) : !linalg.view<?xf32>
// clang-format on
cleanupAndPrintFunction(f);
// CHECK: %[[M:.*]] = dim %arg0, 0 : memref<?x?xf32>
// CHECK: %[[N:.*]] = dim %arg2, 1 : memref<?x?xf32>
// CHECK: affine.for %i0 = 0 to (d0) -> (d0)(%[[N]]) {
- // CHECK: %[[vB:.*]] = linalg.view %arg1[%{{.*}}, %{{.*}}] : !linalg.view<?xf32>
+ // CHECK: %[[vB:.*]] = linalg.view %arg1[%{{.*}}, %{{.*}}] : memref<?x?xf32>, !linalg.range, index, !linalg.view<?xf32>
// CHECK-NEXT: affine.for %i1 = 0 to (d0) -> (d0)(%[[M]]) {
- // CHECK: %[[vA:.*]] = linalg.view %arg0[%{{.*}}, %{{.*}}] : !linalg.view<?xf32>
- // CHECK-NEXT: %[[vC:.*]] = linalg.view %arg2[%{{.*}}, %{{.*}}] : !linalg.view<f32>
+ // CHECK: %[[vA:.*]] = linalg.view %arg0[%{{.*}}, %{{.*}}] : memref<?x?xf32>, index, !linalg.range, !linalg.view<?xf32>
+ // CHECK-NEXT: %[[vC:.*]] = linalg.view %arg2[%{{.*}}, %{{.*}}] : memref<?x?xf32>, index, index, !linalg.view<f32>
// CHECK-NEXT: linalg.dot(%[[vA]], %[[vB]], %[[vC]]) : !linalg.view<f32>
// clang-format on
cleanupAndPrintFunction(f);
// CHECK: %[[rM:.*]] = linalg.range %c0:%[[M]]:%c1 : !linalg.range
// CHECK: %[[rN:.*]] = linalg.range %c0:%[[N]]:%c1 : !linalg.range
// CHECK: %[[rK:.*]] = linalg.range %c0:%[[K]]:%c1 : !linalg.range
- // CHECK: %[[vA:.*]] = linalg.view %arg0[%[[rM]], %[[rK]]] : !linalg.view<?x?xf32>
- // CHECK: %[[vB:.*]] = linalg.view %arg1[%[[rK]], %[[rN]]] : !linalg.view<?x?xf32>
- // CHECK: %[[vC:.*]] = linalg.view %arg2[%[[rM]], %[[rN]]] : !linalg.view<?x?xf32>
+ // CHECK: %[[vA:.*]] = linalg.view %arg0[%[[rM]], %[[rK]]] : memref<?x?xf32>, !linalg.range, !linalg.range, !linalg.view<?x?xf32>
+ // CHECK: %[[vB:.*]] = linalg.view %arg1[%[[rK]], %[[rN]]] : memref<?x?xf32>, !linalg.range, !linalg.range, !linalg.view<?x?xf32>
+ // CHECK: %[[vC:.*]] = linalg.view %arg2[%[[rM]], %[[rN]]] : memref<?x?xf32>, !linalg.range, !linalg.range, !linalg.view<?x?xf32>
// CHECK: affine.for %i0 = 0 to (d0) -> (d0)(%[[M]]) {
// CHECK: affine.for %i1 = 0 to (d0) -> (d0)(%[[N]]) {
// CHECK: affine.for %i2 = 0 to (d0) -> (d0)(%[[K]]) {
// CHECK: %[[M:.*]] = dim %arg0, 0 : memref<?x?xf32>
// CHECK: %[[N:.*]] = dim %arg2, 1 : memref<?x?xf32>
// CHECK: %[[K:.*]] = dim %arg0, 1 : memref<?x?xf32>
- // CHECK: %[[vA:.*]] = linalg.view %arg0[{{.*}}, {{.*}}] : !linalg.view<?x?xf32>
+ // CHECK: %[[vA:.*]] = linalg.view %arg0[{{.*}}, {{.*}}] : memref<?x?xf32>, !linalg.range, !linalg.range, !linalg.view<?x?xf32>
// CHECK: affine.for %i0 = 0 to (d0) -> (d0)(%[[N]]) {
- // CHECK: %[[vB:.*]] = linalg.view %arg1[{{.*}}, {{.*}}] : !linalg.view<?xf32>
- // CHECK: %[[vC:.*]] = linalg.view %arg2[{{.*}}, {{.*}}] : !linalg.view<?xf32>
+ // CHECK: %[[vB:.*]] = linalg.view %arg1[{{.*}}, {{.*}}] : memref<?x?xf32>, !linalg.range, index, !linalg.view<?xf32>
+ // CHECK: %[[vC:.*]] = linalg.view %arg2[{{.*}}, {{.*}}] : memref<?x?xf32>, !linalg.range, index, !linalg.view<?xf32>
// CHECK: affine.for %i1 = 0 to (d0) -> (d0)(%[[M]]) {
// CHECK: affine.for %i2 = 0 to (d0) -> (d0)(%[[K]]) {
// CHECK: %{{.*}} = cmpi "eq", %i2, %{{.*}} : index
target_link_libraries(Linalg3
PUBLIC
+ MLIRAffineOps
MLIRAnalysis
MLIRDialect
MLIREDSC
MLIRParser
MLIRPass
MLIRTransforms
+ Linalg2
)
add_llvm_library(Linalg3DialectConstruction
target_link_libraries(linalg-example-4
PRIVATE
+ MLIRAffineOps
MLIRAnalysis
MLIRDialect
MLIREDSC
MLIRParser
MLIRPass
MLIRTransforms
- Linalg1
- Linalg2
- Linalg3
Linalg4
Linalg3DialectConstruction
)
whole_archive_link(linalg-example-4
+ MLIRAffineOps
MLIRStandardOps
)
// CHECK-NEXT: %[[i0max:.*]] = affine.apply (d0) -> (d0 + 8)(%i0)
// CHECK-NEXT: %[[ri0:.*]] = linalg.range %[[i0min]]:%[[i0max]]:{{.*}} : !linalg.range
// CHECK: %[[rK:.*]] = linalg.range %{{.*}}:%{{.*}}:%{{.*}} : !linalg.range
- // CHECK: %[[vA:.*]] = linalg.view %arg0[%[[ri0]], %[[rK]]] : !linalg.view<?x?xf32>
+ // CHECK: %[[vA:.*]] = linalg.view %arg0[%[[ri0]], %[[rK]]] : memref<?x?xf32>, !linalg.range, !linalg.range, !linalg.view<?x?xf32>
// CHECK: %[[i1min:.*]] = affine.apply (d0) -> (d0)(%i1)
// CHECK-NEXT: %[[i1max:.*]] = affine.apply (d0) -> (d0 + 9)(%i1)
// CHECK-NEXT: %[[ri1:.*]] = linalg.range %[[i1min]]:%[[i1max]]:%{{.*}} : !linalg.range
- // CHECK-NEXT: %[[vB:.*]] = linalg.view %arg1[%10, %13] : !linalg.view<?x?xf32>
- // CHECK-NEXT: %[[vC:.*]] = linalg.view %arg2[%5, %13] : !linalg.view<?x?xf32>
+ // CHECK-NEXT: %[[vB:.*]] = linalg.view %arg1[%10, %13] : memref<?x?xf32>, !linalg.range, !linalg.range, !linalg.view<?x?xf32>
+ // CHECK-NEXT: %[[vC:.*]] = linalg.view %arg2[%5, %13] : memref<?x?xf32>, !linalg.range, !linalg.range, !linalg.view<?x?xf32>
// CHECK-NEXT: linalg.matmul(%[[vA]], %[[vB]], %[[vC]]) : !linalg.view<?x?xf32>
// clang-format on
cleanupAndPrintFunction(f);
// CHECK-NEXT: %[[i0max:.*]] = affine.apply (d0) -> (d0 + 8)(%i0)
// CHECK-NEXT: %[[ri0:.*]] = linalg.range %[[i0min]]:%[[i0max]]:{{.*}} : !linalg.range
// CHECK: %[[rK:.*]] = linalg.range %{{.*}}:%{{.*}}:%{{.*}} : !linalg.range
- // CHECK: %[[vA:.*]] = linalg.view %arg0[%[[ri0]], %[[rK]]] : !linalg.view<?x?xf32>
+ // CHECK: %[[vA:.*]] = linalg.view %arg0[%[[ri0]], %[[rK]]] : memref<?x?xf32>, !linalg.range, !linalg.range, !linalg.view<?x?xf32>
// CHECK: %[[i1min:.*]] = affine.apply (d0) -> (d0)(%i1)
// CHECK-NEXT: %[[i1max:.*]] = affine.apply (d0) -> (d0 + 9)(%i1)
// CHECK-NEXT: %[[ri1:.*]] = linalg.range %[[i1min]]:%[[i1max]]:%{{.*}} : !linalg.range
- // CHECK-NEXT: %[[vB:.*]] = linalg.view %arg1[%10, %13] : !linalg.view<?x?xf32>
- // CHECK-NEXT: %[[vC:.*]] = linalg.view %arg2[%5, %13] : !linalg.view<?x?xf32>
+ // CHECK-NEXT: %[[vB:.*]] = linalg.view %arg1[%10, %13] : memref<?x?xf32>, !linalg.range, !linalg.range, !linalg.view<?x?xf32>
+ // CHECK-NEXT: %[[vC:.*]] = linalg.view %arg2[%5, %13] : memref<?x?xf32>, !linalg.range, !linalg.range, !linalg.view<?x?xf32>
// CHECK-NEXT: affine.for %i2 = (d0) -> (d0)(%i0) to (d0) -> (d0)(%[[i0max]]) {
// CHECK-NEXT: affine.for %i3 = (d0) -> (d0)(%i1) to (d0) -> (d0)(%[[i1max]]) {
// CHECK-NEXT: affine.for %i4 = 0 to (d0) -> (d0)(%[[K]]) {
MLIRParser
MLIRPass
MLIRTransforms
+ Linalg3
)
Similarly to Toy, the dialect must be registered so that the pretty-printer and
verifier can be enabled. Without registration, only the custom op form can be
-printed. Beware of ops printed in custom op form, when a short-hand form exists,
+printed. Beware of ops printed in custom op form, when a shorthand form exists,
because there is a high chance the IR verification is not enabled.
To register the Linalg dialect, call
## Putting it all together
-The
-[example](https://github.com/tensorflow/mlir/blob/master/examples/Linalg/Linalg1/Example.cpp)
-demonstrates how to construct some simple IR snippets that pass through the
-verifier checks. We introduce a custom op called `some_consumer` to ensure that
-dead-code elimination does not optimize these simple examples out of existence.
+We create a `linalg1-opt` executable which links together `Linalg1` and the core
+`MlirOptLib` library to add traditional compiler support for file handling,
+parsing, command-line interface etc. The FileCheck'd test
+[example](https://github.com/tensorflow/mlir/blob/master/test/Examples/Linalg/Linalg1.mlir)
+demonstrates parsing, verification, pretty printing of the IR we have
+constructed so far. We introduce a custom op called `some_consumer` to ensure
+that dead-code elimination does not optimize these simple examples out of
+existence, in the case an extra -canonicalize option is passed to `linalg1-opt`.
+When called with `lower-linalg-to-llvm`, the test uses the
+[LLVM conversion](LLVMConversion.md) mechanisms.
${MLIR_MAIN_INCLUDE_DIR}/mlir/Transforms
)
add_dependencies(MLIRTransforms MLIRStandardOpsIncGen)
-target_link_libraries(MLIRTransforms MLIRVectorOps)
+target_link_libraries(MLIRTransforms
+ MLIRAffineOps
+ MLIRAnalysis
+ MLIRPass
+ MLIRVectorOps
+ )
if(LLVM_BUILD_EXAMPLES)
list(APPEND MLIR_TEST_DEPENDS
+ linalg1-opt
toyc-ch1
toyc-ch2
toyc-ch3
--- /dev/null
+// RUN: linalg1-opt %s -verify | FileCheck %s
+// RUN: linalg1-opt %s -lower-linalg-to-llvm -verify | FileCheck %s -check-prefix=LLVM
+
+func @view_op(%arg0: memref<f32>, %arg1: memref<?xf32>, %arg2: memref<?x?xf32>) {
+ %c3 = constant 3 : index
+ %c17 = constant 17 : index
+ %c1 = constant 1 : index
+ %3 = linalg.range %c3:%c17:%c1 : !linalg.range
+ %4 = linalg.view %arg0[] : memref<f32>, !linalg.view<f32>
+ %5 = linalg.view %arg1[%3] : memref<?xf32>, !linalg.range, !linalg.view<?xf32>
+ %6 = linalg.view %arg2[%3, %3] : memref<?x?xf32>, !linalg.range, !linalg.range, !linalg.view<?x?xf32>
+ "some_consumer"(%4, %5, %6) : (!linalg.view<f32>, !linalg.view<?xf32>, !linalg.view<?x?xf32>) -> ()
+ return
+}
+// CHECK-LABEL: func @view_op(%arg0: memref<f32>, %arg1: memref<?xf32>, %arg2: memref<?x?xf32>) {
+// CHECK: %0 = linalg.range {{.*}} : !linalg.range
+// CHECK: {{.*}} = linalg.view %arg0[] : memref<f32>, !linalg.view<f32>
+// CHECK: {{.*}} = linalg.view %arg1[%0] : memref<?xf32>, !linalg.range, !linalg.view<?xf32>
+// CHECK: {{.*}} = linalg.view %arg2[%0, %0] : memref<?x?xf32>, !linalg.range, !linalg.range, !linalg.view<?x?xf32>
+
+func @slice_op(%arg0: memref<?x?xf32>) {
+ %c0 = constant 0 : index
+ %c1 = constant 1 : index
+ %1 = dim %arg0, 0 : memref<?x?xf32>
+ %2 = dim %arg0, 1 : memref<?x?xf32>
+ %3 = linalg.range %c0:%1:%c1 : !linalg.range
+ %4 = linalg.range %c0:%2:%c1 : !linalg.range
+ %5 = linalg.view %arg0[%3, %4] : memref<?x?xf32>, !linalg.range, !linalg.range, !linalg.view<?x?xf32>
+ affine.for %i0 = 0 to (d0) -> (d0)(%1) {
+ affine.for %i1 = 0 to (d0) -> (d0)(%2) {
+ %6 = linalg.slice %5[%i0] {dim: 1} : !linalg.view<?x?xf32>, index
+ "some_consumer"(%6) : (!linalg.view<?xf32>) -> ()
+ %7 = linalg.slice %5[%i1] {dim: 0} : !linalg.view<?x?xf32>, index
+ %8 = linalg.slice %7[%i0] {dim: 0} : !linalg.view<?xf32>, index
+ }
+ }
+ return
+}
+// CHECK-LABEL: func @slice_op(%arg0: memref<?x?xf32>) {
+// CHECK: %[[M:.*]] = dim %arg0, 0 : memref<?x?xf32>
+// CHECK: %[[N:.*]] = dim %arg0, 1 : memref<?x?xf32>
+// CHECK: %[[r1:.*]] = linalg.range %c0:%[[M]]:%c1 : !linalg.range
+// CHECK: %[[r2:.*]] = linalg.range %c0:%[[N]]:%c1 : !linalg.range
+// CHECK: %[[V:.*]] = linalg.view %arg0[%[[r1]], %[[r2]]] : memref<?x?xf32>, !linalg.range, !linalg.range, !linalg.view<?x?xf32>
+// CHECK: affine.for %i0 = 0 to #map1(%0) {
+// CHECK: affine.for %i1 = 0 to #map1(%1) {
+// CHECK: {{.*}} = linalg.slice %[[V]][%i0] {dim: 1} : !linalg.view<?x?xf32>, index
+// CHECK: %[[V2:.*]] = linalg.slice %[[V]][%i1] {dim: 0} : !linalg.view<?x?xf32>, index
+// CHECK: {{.*}} = linalg.slice %[[V2]][%i0] {dim: 0} : !linalg.view<?xf32>, index
+
+func @rangeConversion(%arg0: index, %arg1: index, %arg2: index) {
+ %0 = linalg.range %arg0:%arg1:%arg2 : !linalg.range
+ return
+}
+// LLVM-LABEL: @rangeConversion
+// LLVM-NEXT: %0 = llvm.undef : !llvm<"{ i64, i64, i64 }">
+// LLVM-NEXT: %1 = llvm.insertvalue %arg0, %0[0] : !llvm<"{ i64, i64, i64 }">
+// LLVM-NEXT: %2 = llvm.insertvalue %arg1, %1[1] : !llvm<"{ i64, i64, i64 }">
+// LLVM-NEXT: %3 = llvm.insertvalue %arg2, %2[2] : !llvm<"{ i64, i64, i64 }">
+
+func @viewRangeConversion(%arg0: memref<?x?xf32>, %arg1: !linalg.range, %arg2: !linalg.range) {
+ %0 = linalg.view %arg0[%arg1, %arg2] : memref<?x?xf32>, !linalg.range, !linalg.range, !linalg.view<?x?xf32>
+ return
+}
+// LLVM-LABEL: @viewRangeConversion
+// LLVM-NEXT: %0 = llvm.undef : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
+// LLVM-NEXT: %1 = llvm.extractvalue %arg0[0] : !llvm<"{ float*, i64, i64 }">
+// LLVM-NEXT: %2 = llvm.insertvalue %1, %0[0] : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
+// LLVM-NEXT: %3 = llvm.extractvalue %arg0[2] : !llvm<"{ float*, i64, i64 }">
+// LLVM-NEXT: %4 = llvm.constant(1 : index) : !llvm.i64
+// LLVM-NEXT: %5 = llvm.mul %4, %3 : !llvm.i64
+// LLVM-NEXT: %6 = llvm.constant(0 : index) : !llvm.i64
+// LLVM-NEXT: %7 = llvm.extractvalue %arg1[0] : !llvm<"{ i64, i64, i64 }">
+// LLVM-NEXT: %8 = llvm.mul %7, %5 : !llvm.i64
+// LLVM-NEXT: %9 = llvm.add %6, %8 : !llvm.i64
+// LLVM-NEXT: %10 = llvm.extractvalue %arg2[0] : !llvm<"{ i64, i64, i64 }">
+// LLVM-NEXT: %11 = llvm.mul %10, %4 : !llvm.i64
+// LLVM-NEXT: %12 = llvm.add %9, %11 : !llvm.i64
+// LLVM-NEXT: %13 = llvm.insertvalue %12, %2[1] : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
+// LLVM-NEXT: %14 = llvm.extractvalue %arg1[0] : !llvm<"{ i64, i64, i64 }">
+// LLVM-NEXT: %15 = llvm.extractvalue %arg1[1] : !llvm<"{ i64, i64, i64 }">
+// LLVM-NEXT: %16 = llvm.sub %15, %14 : !llvm.i64
+// LLVM-NEXT: %17 = llvm.insertvalue %16, %13[2, 0] : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
+// LLVM-NEXT: %18 = llvm.extractvalue %arg2[0] : !llvm<"{ i64, i64, i64 }">
+// LLVM-NEXT: %19 = llvm.extractvalue %arg2[1] : !llvm<"{ i64, i64, i64 }">
+// LLVM-NEXT: %20 = llvm.sub %19, %18 : !llvm.i64
+// LLVM-NEXT: %21 = llvm.insertvalue %20, %17[2, 1] : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
+// LLVM-NEXT: %22 = llvm.extractvalue %arg1[2] : !llvm<"{ i64, i64, i64 }">
+// LLVM-NEXT: %23 = llvm.mul %5, %22 : !llvm.i64
+// LLVM-NEXT: %24 = llvm.insertvalue %23, %21[3, 0] : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
+// LLVM-NEXT: %25 = llvm.extractvalue %arg2[2] : !llvm<"{ i64, i64, i64 }">
+// LLVM-NEXT: %26 = llvm.mul %4, %25 : !llvm.i64
+// LLVM-NEXT: %27 = llvm.insertvalue %26, %24[3, 1] : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
+
+func @viewNonRangeConversion(%arg0: memref<?x?xf32>, %arg1: !linalg.range, %arg2: index) {
+ %0 = linalg.view %arg0[%arg1, %arg2] : memref<?x?xf32>, !linalg.range, index, !linalg.view<?xf32>
+ return
+}
+// LLVM-LABEL: @viewNonRangeConversion
+// LLVM-NEXT: %0 = llvm.undef : !llvm<"{ float*, i64, [1 x i64], [1 x i64] }">
+// LLVM-NEXT: %1 = llvm.extractvalue %arg0[0] : !llvm<"{ float*, i64, i64 }">
+// LLVM-NEXT: %2 = llvm.insertvalue %1, %0[0] : !llvm<"{ float*, i64, [1 x i64], [1 x i64] }">
+// LLVM-NEXT: %3 = llvm.extractvalue %arg0[2] : !llvm<"{ float*, i64, i64 }">
+// LLVM-NEXT: %4 = llvm.constant(1 : index) : !llvm.i64
+// LLVM-NEXT: %5 = llvm.mul %4, %3 : !llvm.i64
+// LLVM-NEXT: %6 = llvm.constant(0 : index) : !llvm.i64
+// LLVM-NEXT: %7 = llvm.extractvalue %arg1[0] : !llvm<"{ i64, i64, i64 }">
+// LLVM-NEXT: %8 = llvm.mul %7, %5 : !llvm.i64
+// LLVM-NEXT: %9 = llvm.add %6, %8 : !llvm.i64
+// LLVM-NEXT: %10 = llvm.mul %arg2, %4 : !llvm.i64
+// LLVM-NEXT: %11 = llvm.add %9, %10 : !llvm.i64
+// LLVM-NEXT: %12 = llvm.insertvalue %11, %2[1] : !llvm<"{ float*, i64, [1 x i64], [1 x i64] }">
+// LLVM-NEXT: %13 = llvm.extractvalue %arg1[0] : !llvm<"{ i64, i64, i64 }">
+// LLVM-NEXT: %14 = llvm.extractvalue %arg1[1] : !llvm<"{ i64, i64, i64 }">
+// LLVM-NEXT: %15 = llvm.sub %14, %13 : !llvm.i64
+// LLVM-NEXT: %16 = llvm.insertvalue %15, %12[2, 0] : !llvm<"{ float*, i64, [1 x i64], [1 x i64] }">
+// LLVM-NEXT: %17 = llvm.extractvalue %arg1[2] : !llvm<"{ i64, i64, i64 }">
+// LLVM-NEXT: %18 = llvm.mul %5, %17 : !llvm.i64
+// LLVM-NEXT: %19 = llvm.insertvalue %18, %16[3, 0] : !llvm<"{ float*, i64, [1 x i64], [1 x i64] }">
+
+func @sliceRangeConversion(%arg0: memref<?x?xf32>, %arg1: !linalg.range, %arg2: !linalg.range, %arg3: !linalg.range) {
+ %0 = linalg.view %arg0[%arg1, %arg2] : memref<?x?xf32>, !linalg.range, !linalg.range, !linalg.view<?x?xf32>
+ %1 = linalg.slice %0[%arg3] {dim: 0} : !linalg.view<?x?xf32>, !linalg.range
+ return
+}
+// LLVM-LABEL: @sliceRangeConversion
+// LLVM: %28 = llvm.undef : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
+// LLVM-NEXT: %29 = llvm.extractvalue %27[0] : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
+// LLVM-NEXT: %30 = llvm.insertvalue %29, %28[0] : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
+// LLVM-NEXT: %31 = llvm.extractvalue %27[1] : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
+// LLVM-NEXT: %32 = llvm.extractvalue %arg3[0] : !llvm<"{ i64, i64, i64 }">
+// LLVM-NEXT: %33 = llvm.extractvalue %27[3, 0] : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
+// LLVM-NEXT: %34 = llvm.mul %32, %33 : !llvm.i64
+// LLVM-NEXT: %35 = llvm.add %31, %34 : !llvm.i64
+// LLVM-NEXT: %36 = llvm.insertvalue %35, %30[1] : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
+// LLVM-NEXT: %37 = llvm.extractvalue %arg3[1] : !llvm<"{ i64, i64, i64 }">
+// LLVM-NEXT: %38 = llvm.extractvalue %arg3[0] : !llvm<"{ i64, i64, i64 }">
+// LLVM-NEXT: %39 = llvm.sub %37, %38 : !llvm.i64
+// LLVM-NEXT: %40 = llvm.extractvalue %27[3, 0] : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
+// LLVM-NEXT: %41 = llvm.extractvalue %arg3[2] : !llvm<"{ i64, i64, i64 }">
+// LLVM-NEXT: %42 = llvm.mul %40, %41 : !llvm.i64
+// LLVM-NEXT: %43 = llvm.insertvalue %39, %36[2, 0] : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
+// LLVM-NEXT: %44 = llvm.insertvalue %42, %43[3, 0] : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
+// LLVM-NEXT: %45 = llvm.extractvalue %27[2, 1] : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
+// LLVM-NEXT: %46 = llvm.extractvalue %27[3, 1] : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
+// LLVM-NEXT: %47 = llvm.insertvalue %45, %44[2, 1] : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
+// LLVM-NEXT: %48 = llvm.insertvalue %46, %47[3, 1] : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
+
+func @sliceNonRangeConversion2(%arg0: memref<?x?xf32>, %arg1: !linalg.range, %arg2: !linalg.range, %arg3: index) {
+ %0 = linalg.view %arg0[%arg1, %arg2] : memref<?x?xf32>, !linalg.range, !linalg.range, !linalg.view<?x?xf32>
+ %1 = linalg.slice %0[%arg3] {dim: 0} : !linalg.view<?x?xf32>, index
+ return
+}
+// LLVM-LABEL: @sliceNonRangeConversion2
+// LLVM: %28 = llvm.undef : !llvm<"{ float*, i64, [1 x i64], [1 x i64] }">
+// LLVM-NEXT: %29 = llvm.extractvalue %27[0] : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
+// LLVM-NEXT: %30 = llvm.insertvalue %29, %28[0] : !llvm<"{ float*, i64, [1 x i64], [1 x i64] }">
+// LLVM-NEXT: %31 = llvm.extractvalue %27[1] : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
+// LLVM-NEXT: %32 = llvm.extractvalue %27[3, 0] : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
+// LLVM-NEXT: %33 = llvm.mul %arg3, %32 : !llvm.i64
+// LLVM-NEXT: %34 = llvm.add %31, %33 : !llvm.i64
+// LLVM-NEXT: %35 = llvm.insertvalue %34, %30[1] : !llvm<"{ float*, i64, [1 x i64], [1 x i64] }">
+// LLVM-NEXT: %36 = llvm.extractvalue %27[2, 1] : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
+// LLVM-NEXT: %37 = llvm.extractvalue %27[3, 1] : !llvm<"{ float*, i64, [2 x i64], [2 x i64] }">
+// LLVM-NEXT: %38 = llvm.insertvalue %36, %35[2, 0] : !llvm<"{ float*, i64, [1 x i64], [1 x i64] }">
+// LLVM-NEXT: %39 = llvm.insertvalue %37, %38[3, 0] : !llvm<"{ float*, i64, [1 x i64], [1 x i64] }">
+set(LLVM_OPTIONAL_SOURCES
+ null.cpp
+)
+
+set(LIB_LIBS
+ MLIRAnalysis
+ MLIRLLVMIR
+ MLIRParser
+ MLIRPass
+ MLIRTransforms
+ MLIRSupport
+)
+add_llvm_library(MLIRMlirOptLib
+ mlir-opt.cpp
+)
+target_link_libraries(MLIRMlirOptLib ${LIB_LIBS})
+
set(LIBS
MLIRAffineOps
MLIRAnalysis
MLIRVectorOps
)
add_executable(mlir-opt
- mlir-opt.cpp
+ mlir-opt.cpp
)
llvm_update_compile_flags(mlir-opt)
whole_archive_link(mlir-opt ${LIBS})
-target_link_libraries(mlir-opt MLIRIR ${LIBS} LLVMSupport)
+target_link_libraries(mlir-opt MLIRIR MLIRMlirOptLib ${LIBS} LLVMSupport)