diff --git a/flang/include/flang/Optimizer/Dialect/FIROps.td b/flang/include/flang/Optimizer/Dialect/FIROps.td --- a/flang/include/flang/Optimizer/Dialect/FIROps.td +++ b/flang/include/flang/Optimizer/Dialect/FIROps.td @@ -345,6 +345,8 @@ let hasCustomAssemblyFormat = 1; let hasVerifier = 1; + let hasCanonicalizeMethod = 1; + let extraClassDeclaration = [{ static mlir::Type elementType(mlir::Type refType); }]; diff --git a/flang/lib/Optimizer/Dialect/FIROps.cpp b/flang/lib/Optimizer/Dialect/FIROps.cpp --- a/flang/lib/Optimizer/Dialect/FIROps.cpp +++ b/flang/lib/Optimizer/Dialect/FIROps.cpp @@ -3023,6 +3023,22 @@ return fir::dyn_cast_ptrEleTy(refType); } +mlir::LogicalResult +fir::StoreOp::canonicalize(fir::StoreOp op, mlir::PatternRewriter &rewriter) { + mlir::Operation *definingOp = op.getValue().getDefiningOp(); + if (!definingOp) + return mlir::failure(); + fir::LoadOp loadOp = mlir::dyn_cast(definingOp); + + // Load-store elimination + if (loadOp && op.getMemref() == loadOp.getMemref() && + loadOp->getNextNode() == op) { + rewriter.eraseOp(op); + return mlir::success(); + } + return mlir::failure(); +} + mlir::ParseResult fir::StoreOp::parse(mlir::OpAsmParser &parser, mlir::OperationState &result) { mlir::Type type; diff --git a/flang/test/Fir/fir-canonicalize.fir b/flang/test/Fir/fir-canonicalize.fir new file mode 100644 --- /dev/null +++ b/flang/test/Fir/fir-canonicalize.fir @@ -0,0 +1,60 @@ +// RUN: fir-opt --split-input-file --canonicalize %s | FileCheck %s + +// This file will be used for canonicalizations that are defined as part of +// the operation (not external patterns) + +// CHECK-LABEL: @canonicalize_store_simple +func.func @canonicalize_store_simple(%arg0: !fir.ref) { + %0 = fir.load %arg0 : !fir.ref + fir.store %0 to %arg0 : !fir.ref // should be eliminated + return +} + +// CHECK-NOT: fir.store + +// ----- + +// CHECK-LABEL: @canonicalize_store_argument +func.func @canonicalize_store_argument(%arg0: !fir.ref, %arg1: i32) { + %0 = fir.load %arg0 : !fir.ref + fir.store %arg1 to %arg0 : !fir.ref // should not be eliminated + return +} + +// CHECK-NOT: fir.load +// CHECK: fir.store + +// ----- + +func.func private @foo(%arg0: i32) + +// CHECK-LABEL: @canonicalize_store_load_multiple_uses +func.func @canonicalize_store_load_multiple_uses(%arg0: !fir.ref, %arg1: i32) { + %0 = fir.load %arg0 : !fir.ref + fir.store %0 to %arg0 : !fir.ref // should be eliminated + call @foo(%0) : (i32) -> () + return +} + +// CHECK: fir.load +// CHECK-NOT: fir.store +// CHECK: call @foo + +// ----- + +func.func private @foo(!fir.ref) + +// CHECK-LABEL: @canonicalize_store_use_before_store +func.func @canonicalize_store_use_before_store(%arg0: !fir.ref, %1: i32) { + %0 = fir.load %arg0 : !fir.ref + fir.store %1 to %arg0 : !fir.ref // should not be eliminated + call @foo(%arg0) : (!fir.ref) -> () // called with new value + fir.store %0 to %arg0 : !fir.ref // should not be eliminated + call @foo(%arg0) : (!fir.ref) -> () // called with old value + return +} + +// CHECK: fir.store +// CHECK: call @foo +// CHECK: fir.store +// CHECK: call @foo