diff --git a/mlir/include/mlir/Dialect/StandardOps/IR/Ops.td b/mlir/include/mlir/Dialect/StandardOps/IR/Ops.td --- a/mlir/include/mlir/Dialect/StandardOps/IR/Ops.td +++ b/mlir/include/mlir/Dialect/StandardOps/IR/Ops.td @@ -2139,6 +2139,35 @@ }]; } +//===----------------------------------------------------------------------===// +// SinkOp +//===----------------------------------------------------------------------===// + +def SinkOp : Std_Op<"sink"> { + let summary = "sinks an arbitrary value"; + let description = [{ + Consumes an SSA value of any type. + + This op has no runtime semantics and is mainly useful as a structural + placeholder to maintain IR validity outside a typical compilation flow, + such as during test case reduction. + + ```mlir + sink %0 : i1 + sink %1 : !my_dialect.my_type + sink %0, %1 : i1, !my_dialect.my_type + ``` + }]; + let arguments = (ins Variadic:$operands); + let results = (outs); + + let assemblyFormat = "$operands attr-dict `:` type($operands)"; + + // This op is fully verified by its declarative specification. + let verifier = ?; +} + + //===----------------------------------------------------------------------===// // SignedDivIOp //===----------------------------------------------------------------------===// @@ -2298,6 +2327,37 @@ let hasFolder = 0; } +//===----------------------------------------------------------------------===// +// SourceOp +//===----------------------------------------------------------------------===// + +def SourceOp : Std_Op<"source", [NoSideEffect]> { + let summary = "sources a value of a given type"; + let description = [{ + Creates an SSA value of the prescribed type. + + This op has no runtime semantics and is mainly useful as a structural + placeholder to maintain IR validity outside a typical compilation flow, + such as during test case reduction. + + ```mlir + %0 = source : i1 + %1 = source : !my_dialect.my_type + %a, %b, %c = source : i1, i2, i3 + ``` + }]; + + let arguments = (ins); + let results = (outs Variadic:$results); + + let assemblyFormat = "attr-dict `:` type($results)"; + + // This op is fully verified by its declarative specification. + let verifier = ?; + + let hasCanonicalizer = 1; +} + //===----------------------------------------------------------------------===// // SplatOp //===----------------------------------------------------------------------===// diff --git a/mlir/lib/Dialect/StandardOps/IR/Ops.cpp b/mlir/lib/Dialect/StandardOps/IR/Ops.cpp --- a/mlir/lib/Dialect/StandardOps/IR/Ops.cpp +++ b/mlir/lib/Dialect/StandardOps/IR/Ops.cpp @@ -2102,6 +2102,36 @@ return a.isSignlessInteger() && b.isa(); } +//===----------------------------------------------------------------------===// +// SourceOp +//===----------------------------------------------------------------------===// + +namespace { +class RemoveUnusedSourceOpOperands : public OpRewritePattern { +public: + using OpRewritePattern::OpRewritePattern; + LogicalResult matchAndRewrite(SourceOp op, + PatternRewriter &rewriter) const override { + auto results = llvm::to_vector<6>(op.getResults()); + llvm::erase_if(results, [](Value v) { return v.use_empty(); }); + if (results.size() == op.getNumResults()) + return success(); + auto newResultTypes = llvm::to_vector<6>( + llvm::map_range(results, [](Value v) { return v.getType(); })); + auto newOp = rewriter.create(op.getLoc(), newResultTypes); + for (auto t : llvm::zip(results, newOp.getResults())) + std::get<0>(t).replaceAllUsesWith(std::get<1>(t)); + rewriter.eraseOp(op); + return success(); + } +}; +} // namespace + +void SourceOp::getCanonicalizationPatterns(OwningRewritePatternList &results, + MLIRContext *context) { + results.insert(context); +} + //===----------------------------------------------------------------------===// // SplatOp //===----------------------------------------------------------------------===// diff --git a/mlir/test/Dialect/Standard/source-sink.mlir b/mlir/test/Dialect/Standard/source-sink.mlir new file mode 100644 --- /dev/null +++ b/mlir/test/Dialect/Standard/source-sink.mlir @@ -0,0 +1,30 @@ +// RUN: mlir-opt %s -canonicalize | FileCheck %s --dump-input=fail + +// CHECK-LABEL: func @canonicalize_does_not_remove_used_source_values +func @canonicalize_does_not_remove_used_source_values() { + // CHECK: source : i1, i2, i3 + %1, %2, %3 = source : i1, i2, i3 + sink %1, %2, %3 : i1, i2, i3 + return +} + +// CHECK-LABEL: func @canonicalize_removes_unused_source_value +func @canonicalize_removes_unused_source_value() { + // CHECK: source : i1, i3 + %1, %2, %3 = source : i1, i2, i3 + sink %1, %3 : i1, i3 + return +} + +// CHECK-LABEL: func @source_can_be_trivially_deleted +func @source_can_be_trivially_deleted() { + // CHECK-NOT: source : i1 + %0 = source : i1 + return +} + +// CHECK-LABEL: func @sink_cannot_be_trivially_deleted +func @sink_cannot_be_trivially_deleted(%arg0: i1) { + sink %arg0 : i1 + return +}