blob: 66954f17acf5669b809bb7d1ecf60b994db88d89 [file]
//===-- PreCGRewrite.cpp --------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
//
//===----------------------------------------------------------------------===//
#include "flang/Optimizer/CodeGen/CodeGen.h"
#include "flang/Optimizer/Dialect/FIRCG/CGOps.h"
#include "flang/Optimizer/Dialect/FIRDialect.h"
#include "flang/Optimizer/Dialect/FIROps.h"
#include "flang/Optimizer/Dialect/FIRType.h"
#include "flang/Optimizer/Dialect/Support/FIRContext.h"
#include "mlir/Dialect/ControlFlow/IR/ControlFlow.h"
#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
#include "mlir/IR/Iterators.h"
#include "mlir/Interfaces/ControlFlowInterfaces.h"
#include "mlir/Transforms/DialectConversion.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Debug.h"
namespace fir {
#define GEN_PASS_DEF_CODEGENREWRITE
#include "flang/Optimizer/CodeGen/CGPasses.h.inc"
} // namespace fir
//===----------------------------------------------------------------------===//
// Codegen rewrite: rewriting of subgraphs of ops
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "flang-codegen-rewrite"
static void populateShape(llvm::SmallVectorImpl<mlir::Value> &vec,
fir::ShapeOp shape) {
vec.append(shape.getExtents().begin(), shape.getExtents().end());
}
// Operands of fir.shape_shift split into two vectors.
static void populateShapeAndShift(llvm::SmallVectorImpl<mlir::Value> &shapeVec,
llvm::SmallVectorImpl<mlir::Value> &shiftVec,
fir::ShapeShiftOp shift) {
for (auto i = shift.getPairs().begin(), endIter = shift.getPairs().end();
i != endIter;) {
shiftVec.push_back(*i++);
shapeVec.push_back(*i++);
}
}
static void populateShift(llvm::SmallVectorImpl<mlir::Value> &vec,
fir::ShiftOp shift) {
vec.append(shift.getOrigins().begin(), shift.getOrigins().end());
}
/// Report a shape or slice the rewrites cannot read.
///
/// They are folded into the code-gen form through their defining op, so a
/// value that has none -- a block argument, or the result of a select --
/// cannot be folded. Saying so is better than what the rewrites would
/// otherwise do: describe the whole array instead of the section it names.
static llvm::LogicalResult checkFoldableShapeAndSlice(mlir::Operation *op,
mlir::Value shape,
mlir::Value slice) {
if (shape &&
!mlir::isa_and_nonnull<fir::ShapeOp, fir::ShapeShiftOp, fir::ShiftOp>(
shape.getDefiningOp()))
return op->emitOpError("shape operand is not defined by a fir.shape, "
"fir.shape_shift or fir.shift");
if (slice && !mlir::isa_and_nonnull<fir::SliceOp>(slice.getDefiningOp()))
return op->emitOpError("slice operand is not defined by a fir.slice");
return llvm::success();
}
// Helper to emit embox/rebox for OPTIONAL input inside a block
// guarded by a runtime presence check and to return an absent
// box when the input is not present.
template <typename OP>
static mlir::Value
emitOptionalBoxGuard(mlir::PatternRewriter &rewriter, OP op,
llvm::function_ref<mlir::Value()> buildPresent) {
mlir::Location loc = op.getLoc();
mlir::Type boxType = op.getResult().getType();
mlir::Value isPresent = fir::IsPresentOp::create(
rewriter, loc, rewriter.getI1Type(), op->getOperand(0));
mlir::Block *condBlock = op->getBlock();
mlir::Block *mergeBlock = rewriter.splitBlock(condBlock, op->getIterator());
mergeBlock->addArgument(boxType, loc);
mlir::Block *thenBlock = rewriter.createBlock(mergeBlock);
rewriter.setInsertionPointToStart(thenBlock);
mlir::Value present = buildPresent();
mlir::cf::BranchOp::create(rewriter, loc, mergeBlock,
mlir::ValueRange{present});
mlir::Block *elseBlock = rewriter.createBlock(mergeBlock);
rewriter.setInsertionPointToStart(elseBlock);
mlir::Value absent = fir::AbsentOp::create(rewriter, loc, boxType);
mlir::cf::BranchOp::create(rewriter, loc, mergeBlock,
mlir::ValueRange{absent});
rewriter.setInsertionPointToEnd(condBlock);
mlir::cf::CondBranchOp::create(rewriter, loc, isPresent, thenBlock,
elseBlock);
rewriter.setInsertionPointToStart(mergeBlock);
return mergeBlock->getArgument(0);
}
namespace {
/// Convert fir.embox to the extended form where necessary.
///
/// The embox operation can take arguments that specify multidimensional array
/// properties at runtime. These properties may be shared between distinct
/// objects that have the same properties. Before we lower these small DAGs to
/// LLVM-IR, we gather all the information into a single extended operation. For
/// example,
/// ```
/// %1 = fir.shape_shift %4, %5 : (index, index) -> !fir.shapeshift<1>
/// %2 = fir.slice %6, %7, %8 : (index, index, index) -> !fir.slice<1>
/// %3 = fir.embox %0 (%1) [%2] : (!fir.ref<!fir.array<?xi32>>,
/// !fir.shapeshift<1>, !fir.slice<1>) -> !fir.box<!fir.array<?xi32>>
/// ```
/// can be rewritten as
/// ```
/// %1 = fircg.ext_embox %0(%5) origin %4[%6, %7, %8] :
/// (!fir.ref<!fir.array<?xi32>>, index, index, index, index, index) ->
/// !fir.box<!fir.array<?xi32>>
/// ```
class EmboxConversion : public mlir::OpRewritePattern<fir::EmboxOp> {
public:
using OpRewritePattern::OpRewritePattern;
enum class RewriteKind { Dynamic, Static, DropOptional };
static llvm::FailureOr<RewriteKind> getRewriteKind(fir::EmboxOp embox) {
if (auto shapeVal = embox.getShape())
return RewriteKind::Dynamic;
if (auto boxTy = mlir::dyn_cast<fir::BoxType>(embox.getType()))
if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(boxTy.getEleTy()))
if (!seqTy.hasDynamicExtents())
return RewriteKind::Static;
if (embox.getOptional())
return RewriteKind::DropOptional;
return llvm::failure();
}
llvm::LogicalResult
matchAndRewrite(fir::EmboxOp embox,
mlir::PatternRewriter &rewriter) const override {
llvm::FailureOr<RewriteKind> rewriteKind = getRewriteKind(embox);
if (llvm::failed(rewriteKind))
return llvm::failure();
if (*rewriteKind == RewriteKind::Dynamic &&
llvm::failed(checkFoldableShapeAndSlice(embox, embox.getShape(),
embox.getSlice())))
return llvm::failure();
if (embox.getOptional()) {
mlir::Value newBox = emitOptionalBoxGuard(rewriter, embox, [&] {
return matchAndRewriteImpl(embox, rewriter, *rewriteKind)->getResult(0);
});
rewriter.replaceOp(embox, newBox);
return mlir::success();
}
mlir::Operation *newOp = matchAndRewriteImpl(embox, rewriter, *rewriteKind);
rewriter.replaceOp(embox, newOp);
return mlir::success();
}
mlir::Operation *matchAndRewriteImpl(fir::EmboxOp embox,
mlir::PatternRewriter &rewriter,
RewriteKind rewriteKind) const {
switch (rewriteKind) {
case RewriteKind::Dynamic:
return rewriteDynamicShape(embox, rewriter, embox.getShape());
case RewriteKind::Static:
return rewriteStaticShape(embox, rewriter,
fir::unwrapUntilSeqType(embox.getType()));
case RewriteKind::DropOptional: {
auto newEmbox =
llvm::cast<fir::EmboxOp>(rewriter.clone(*embox.getOperation()));
newEmbox.setOptional(false);
return newEmbox.getOperation();
}
}
llvm_unreachable("all cases covered");
return nullptr;
}
mlir::Operation *rewriteStaticShape(fir::EmboxOp embox,
mlir::PatternRewriter &rewriter,
fir::SequenceType seqTy) const {
auto loc = embox.getLoc();
llvm::SmallVector<mlir::Value> shapeOpers;
auto idxTy = rewriter.getIndexType();
for (auto ext : seqTy.getShape()) {
auto iAttr = rewriter.getIndexAttr(ext);
auto extVal =
mlir::arith::ConstantOp::create(rewriter, loc, idxTy, iAttr);
shapeOpers.push_back(extVal);
}
auto xbox = fir::cg::XEmboxOp::create(
rewriter, loc, embox.getType(), embox.getMemref(), shapeOpers,
mlir::ValueRange{}, mlir::ValueRange{}, mlir::ValueRange{},
mlir::ValueRange{}, embox.getTypeparams(), embox.getSourceBox(),
embox.getAllocatorIdxAttr());
LLVM_DEBUG(llvm::dbgs() << "rewriting " << embox << " to " << xbox << '\n');
return xbox.getOperation();
}
mlir::Operation *rewriteDynamicShape(fir::EmboxOp embox,
mlir::PatternRewriter &rewriter,
mlir::Value shapeVal) const {
auto loc = embox.getLoc();
llvm::SmallVector<mlir::Value> shapeOpers;
llvm::SmallVector<mlir::Value> shiftOpers;
// matchAndRewrite has already reported a shape this cannot read.
mlir::Operation *shapeDef = shapeVal.getDefiningOp();
if (auto shapeOp = mlir::dyn_cast_or_null<fir::ShapeOp>(shapeDef)) {
populateShape(shapeOpers, shapeOp);
} else {
auto shiftOp = mlir::dyn_cast_or_null<fir::ShapeShiftOp>(shapeDef);
assert(shiftOp && "shape is neither fir.shape nor fir.shape_shift");
populateShapeAndShift(shapeOpers, shiftOpers, shiftOp);
}
llvm::SmallVector<mlir::Value> sliceOpers;
llvm::SmallVector<mlir::Value> subcompOpers;
llvm::SmallVector<mlir::Value> substrOpers;
if (auto s = embox.getSlice()) {
auto sliceOp = mlir::dyn_cast_or_null<fir::SliceOp>(s.getDefiningOp());
// matchAndRewrite has already reported a slice this cannot read.
// Dropping it would describe the whole array instead of the section.
assert(sliceOp && "slice is not defined by a fir.slice");
sliceOpers.assign(sliceOp.getTriples().begin(),
sliceOp.getTriples().end());
subcompOpers.assign(sliceOp.getFields().begin(),
sliceOp.getFields().end());
substrOpers.assign(sliceOp.getSubstr().begin(),
sliceOp.getSubstr().end());
}
auto xbox = fir::cg::XEmboxOp::create(
rewriter, loc, embox.getType(), embox.getMemref(), shapeOpers,
shiftOpers, sliceOpers, subcompOpers, substrOpers,
embox.getTypeparams(), embox.getSourceBox(),
embox.getAllocatorIdxAttr());
LLVM_DEBUG(llvm::dbgs() << "rewriting " << embox << " to " << xbox << '\n');
return xbox.getOperation();
}
};
/// Convert fir.rebox to the extended form where necessary.
///
/// For example,
/// ```
/// %5 = fir.rebox %3(%1) : (!fir.box<!fir.array<?xi32>>, !fir.shapeshift<1>) ->
/// !fir.box<!fir.array<?xi32>>
/// ```
/// converted to
/// ```
/// %5 = fircg.ext_rebox %3(%13) origin %12 : (!fir.box<!fir.array<?xi32>>,
/// index, index) -> !fir.box<!fir.array<?xi32>>
/// ```
class ReboxConversion : public mlir::OpRewritePattern<fir::ReboxOp> {
public:
using OpRewritePattern::OpRewritePattern;
llvm::LogicalResult
matchAndRewrite(fir::ReboxOp rebox,
mlir::PatternRewriter &rewriter) const override {
if (llvm::failed(checkFoldableShapeAndSlice(rebox, rebox.getShape(),
rebox.getSlice())))
return llvm::failure();
if (rebox.getOptional()) {
mlir::Value newBox = emitOptionalBoxGuard(rewriter, rebox, [&] {
return matchAndRewriteImpl(rebox, rewriter)->getResult(0);
});
rewriter.replaceOp(rebox, newBox);
return mlir::success();
}
mlir::Operation *newOp = matchAndRewriteImpl(rebox, rewriter);
rewriter.replaceOp(rebox, newOp);
return mlir::success();
}
mlir::Operation *matchAndRewriteImpl(fir::ReboxOp rebox,
mlir::PatternRewriter &rewriter) const {
auto loc = rebox.getLoc();
llvm::SmallVector<mlir::Value> shapeOpers;
llvm::SmallVector<mlir::Value> shiftOpers;
if (auto shapeVal = rebox.getShape()) {
if (auto shapeOp = mlir::dyn_cast<fir::ShapeOp>(shapeVal.getDefiningOp()))
populateShape(shapeOpers, shapeOp);
else if (auto shapeShiftOp =
mlir::dyn_cast<fir::ShapeShiftOp>(shapeVal.getDefiningOp()))
populateShapeAndShift(shapeOpers, shiftOpers, shapeShiftOp);
else {
auto shiftOp = mlir::dyn_cast<fir::ShiftOp>(shapeVal.getDefiningOp());
assert(shiftOp && "unexpected shape operand type");
populateShift(shiftOpers, shiftOp);
}
}
llvm::SmallVector<mlir::Value> sliceOpers;
llvm::SmallVector<mlir::Value> subcompOpers;
llvm::SmallVector<mlir::Value> substrOpers;
if (auto s = rebox.getSlice())
if (auto sliceOp =
mlir::dyn_cast_or_null<fir::SliceOp>(s.getDefiningOp())) {
sliceOpers.append(sliceOp.getTriples().begin(),
sliceOp.getTriples().end());
subcompOpers.append(sliceOp.getFields().begin(),
sliceOp.getFields().end());
substrOpers.append(sliceOp.getSubstr().begin(),
sliceOp.getSubstr().end());
}
auto xRebox = fir::cg::XReboxOp::create(
rewriter, loc, rebox.getType(), rebox.getBox(), shapeOpers, shiftOpers,
sliceOpers, subcompOpers, substrOpers);
LLVM_DEBUG(llvm::dbgs()
<< "rewriting " << rebox << " to " << xRebox << '\n');
return xRebox.getOperation();
}
};
/// Convert all fir.array_coor to the extended form.
///
/// For example,
/// ```
/// %4 = fir.array_coor %addr (%1) [%2] %0 : (!fir.ref<!fir.array<?xi32>>,
/// !fir.shapeshift<1>, !fir.slice<1>, index) -> !fir.ref<i32>
/// ```
/// converted to
/// ```
/// %40 = fircg.ext_array_coor %addr(%9) origin %8[%4, %5, %6<%39> :
/// (!fir.ref<!fir.array<?xi32>>, index, index, index, index, index, index) ->
/// !fir.ref<i32>
/// ```
class ArrayCoorConversion : public mlir::OpRewritePattern<fir::ArrayCoorOp> {
public:
using OpRewritePattern::OpRewritePattern;
llvm::LogicalResult
matchAndRewrite(fir::ArrayCoorOp arrCoor,
mlir::PatternRewriter &rewriter) const override {
if (llvm::failed(checkFoldableShapeAndSlice(arrCoor, arrCoor.getShape(),
arrCoor.getSlice())))
return llvm::failure();
auto loc = arrCoor.getLoc();
llvm::SmallVector<mlir::Value> shapeOpers;
llvm::SmallVector<mlir::Value> shiftOpers;
if (auto shapeVal = arrCoor.getShape()) {
if (auto shapeOp = mlir::dyn_cast<fir::ShapeOp>(shapeVal.getDefiningOp()))
populateShape(shapeOpers, shapeOp);
else if (auto shiftOp =
mlir::dyn_cast<fir::ShapeShiftOp>(shapeVal.getDefiningOp()))
populateShapeAndShift(shapeOpers, shiftOpers, shiftOp);
else if (auto shiftOp =
mlir::dyn_cast<fir::ShiftOp>(shapeVal.getDefiningOp()))
populateShift(shiftOpers, shiftOp);
else
return mlir::failure();
}
llvm::SmallVector<mlir::Value> sliceOpers;
llvm::SmallVector<mlir::Value> subcompOpers;
if (auto s = arrCoor.getSlice())
if (auto sliceOp =
mlir::dyn_cast_or_null<fir::SliceOp>(s.getDefiningOp())) {
sliceOpers.append(sliceOp.getTriples().begin(),
sliceOp.getTriples().end());
subcompOpers.append(sliceOp.getFields().begin(),
sliceOp.getFields().end());
assert(sliceOp.getSubstr().empty() &&
"Don't allow substring operations on array_coor. This "
"restriction may be lifted in the future.");
}
auto xArrCoor = fir::cg::XArrayCoorOp::create(
rewriter, loc, arrCoor.getType(), arrCoor.getMemref(), shapeOpers,
shiftOpers, sliceOpers, subcompOpers, arrCoor.getIndices(),
arrCoor.getTypeparams());
LLVM_DEBUG(llvm::dbgs()
<< "rewriting " << arrCoor << " to " << xArrCoor << '\n');
rewriter.replaceOp(arrCoor, xArrCoor.getOperation()->getResults());
return mlir::success();
}
};
class DeclareOpConversion : public mlir::OpRewritePattern<fir::DeclareOp> {
bool preserveDeclare;
public:
using OpRewritePattern::OpRewritePattern;
DeclareOpConversion(mlir::MLIRContext *ctx, bool preserveDecl)
: OpRewritePattern(ctx), preserveDeclare(preserveDecl) {}
llvm::LogicalResult
matchAndRewrite(fir::DeclareOp declareOp,
mlir::PatternRewriter &rewriter) const override {
if (!preserveDeclare) {
rewriter.replaceOp(declareOp, declareOp.getMemref());
return mlir::success();
}
auto loc = declareOp.getLoc();
llvm::SmallVector<mlir::Value> shapeOpers;
llvm::SmallVector<mlir::Value> shiftOpers;
if (auto shapeVal = declareOp.getShape()) {
if (auto shapeOp = mlir::dyn_cast<fir::ShapeOp>(shapeVal.getDefiningOp()))
populateShape(shapeOpers, shapeOp);
else if (auto shiftOp =
mlir::dyn_cast<fir::ShapeShiftOp>(shapeVal.getDefiningOp()))
populateShapeAndShift(shapeOpers, shiftOpers, shiftOp);
else if (auto shiftOp =
mlir::dyn_cast<fir::ShiftOp>(shapeVal.getDefiningOp()))
populateShift(shiftOpers, shiftOp);
else
return mlir::failure();
}
// Extract dummy_arg_no attribute if present
mlir::IntegerAttr dummyArgNoAttr;
if (auto attr = declareOp->getAttrOfType<mlir::IntegerAttr>("dummy_arg_no"))
dummyArgNoAttr = attr;
// FIXME: Add FortranAttrs
auto xDeclOp = fir::cg::XDeclareOp::create(
rewriter, loc, declareOp.getType(), declareOp.getMemref(), shapeOpers,
shiftOpers, declareOp.getTypeparams(), declareOp.getDummyScope(),
declareOp.getStorage(), declareOp.getStorageOffset(),
declareOp.getUniqName(), declareOp.getDataAttrAttr(), dummyArgNoAttr);
LLVM_DEBUG(llvm::dbgs()
<< "rewriting " << declareOp << " to " << xDeclOp << '\n');
rewriter.replaceOp(declareOp, xDeclOp.getOperation()->getResults());
return mlir::success();
}
};
class DummyScopeOpConversion
: public mlir::OpRewritePattern<fir::DummyScopeOp> {
public:
using OpRewritePattern::OpRewritePattern;
llvm::LogicalResult
matchAndRewrite(fir::DummyScopeOp dummyScopeOp,
mlir::PatternRewriter &rewriter) const override {
rewriter.replaceOpWithNewOp<fir::UndefOp>(dummyScopeOp,
dummyScopeOp.getType());
return mlir::success();
}
};
/// Simple DCE to erase fir.shape/shift/slice/unused shape operands after this
/// pass (fir.shape and like have no codegen).
/// mlir::RegionDCE is expensive and requires running
/// mlir::eraseUnreachableBlocks. It does things that are not needed here, like
/// removing unused block arguments. fir.shape/shift/slice cannot be block
/// arguments.
/// This helper does a naive backward walk of the IR. It is not even guaranteed
/// to walk blocks according to backward dominance, but that is good enough for
/// what is done here, fir.shape/shift/slice have no usages anymore. The
/// backward walk allows getting rid of most of the unused operands, it is not a
/// problem to leave some in the weird cases.
static void simpleDCE(mlir::RewriterBase &rewriter, mlir::Operation *op) {
op->walk<mlir::WalkOrder::PostOrder, mlir::ReverseIterator>(
[&](mlir::Operation *subOp) {
if (mlir::isOpTriviallyDead(subOp))
rewriter.eraseOp(subOp);
});
}
/// A fir.shape, fir.shape_shift, fir.shift or fir.slice only describes an
/// array at compile time. The rewrites above read it through its defining op
/// and fold it into the code-gen form, so such a value never reaches codegen
/// and none of these types has an LLVM lowering.
static bool isCompileTimeOnly(mlir::Type type) {
return mlir::isa<fir::ShapeType, fir::ShapeShiftType, fir::ShiftType,
fir::SliceType>(type);
}
/// Return the op \p value is built by, if this pass can rebuild it elsewhere
/// from its operands. A fir.slice naming components is excluded: its fields
/// are fir.field values, which are themselves compile-time only and so cannot
/// be passed along a branch.
static mlir::Operation *getRebuildableDefiningOp(mlir::Value value) {
mlir::Operation *def = value.getDefiningOp();
if (!def)
return nullptr;
if (auto slice = mlir::dyn_cast<fir::SliceOp>(def))
return slice.getFields().empty() ? def : nullptr;
if (mlir::isa<fir::ShapeOp, fir::ShapeShiftOp, fir::ShiftOp>(def))
return def;
return nullptr;
}
/// True if \p lhs and \p rhs can be rebuilt by one op taking one operand list.
static bool haveSameShape(mlir::Operation *lhs, mlir::Operation *rhs) {
if (lhs->getName() != rhs->getName() ||
lhs->getNumOperands() != rhs->getNumOperands())
return false;
return llvm::equal(lhs->getOperandTypes(), rhs->getOperandTypes());
}
/// Rebuild the compile-time-only block argument \p argIndex of \p block from
/// its operands, so the rewrites above find a defining op again.
///
/// Every branch into the block passes a value of one of those types. The
/// operands behind it are integers, which can be block arguments, so they are
/// appended to the block and forwarded along each branch instead, and the
/// value is rebuilt from them at the top of the block.
static void rebuildBlockArgument(mlir::Block *block, unsigned argIndex) {
mlir::BlockArgument arg = block->getArgument(argIndex);
// Collect one incoming edge per branch, keeping the operands each carries.
// A branch can name the block twice, so each edge is held separately.
struct Edge {
mlir::BranchOpInterface branch;
unsigned successorIndex;
};
llvm::SmallVector<Edge> edges;
mlir::Operation *model = nullptr;
for (auto it = block->pred_begin(), e = block->pred_end(); it != e; ++it) {
auto branch =
mlir::dyn_cast<mlir::BranchOpInterface>((*it)->getTerminator());
if (!branch)
return;
unsigned successorIndex = it.getSuccessorIndex();
mlir::SuccessorOperands operands =
branch.getSuccessorOperands(successorIndex);
// An operand the branch itself produces has no value to read here.
if (operands.isOperandProduced(argIndex))
return;
mlir::Operation *def = getRebuildableDefiningOp(operands[argIndex]);
if (!def)
return;
if (!model)
model = def;
else if (!haveSameShape(model, def))
return;
edges.push_back({branch, successorIndex});
}
if (edges.empty())
return;
// Take the operands as block arguments and forward them along each branch.
unsigned firstNewArg = block->getNumArguments();
for (auto [type, value] :
llvm::zip_equal(model->getOperandTypes(), model->getOperands()))
block->addArgument(type, value.getLoc());
for (Edge &edge : edges) {
mlir::SuccessorOperands operands =
edge.branch.getSuccessorOperands(edge.successorIndex);
operands.append(
getRebuildableDefiningOp(operands[argIndex])->getOperands());
}
// Rebuild the value at the top of the block and drop the old argument.
mlir::OpBuilder builder(block, block->begin());
mlir::Operation *rebuilt =
builder.create(model->getLoc(), model->getName().getIdentifier(),
block->getArguments().drop_front(firstNewArg),
model->getResultTypes(), model->getAttrs());
arg.replaceAllUsesWith(rebuilt->getResult(0));
for (Edge &edge : edges)
edge.branch.getSuccessorOperands(edge.successorIndex).erase(argIndex);
block->eraseArgument(argIndex);
}
/// Rebuild every compile-time-only block argument in \p op.
static void rebuildCompileTimeOnlyBlockArguments(mlir::Operation *op) {
llvm::SmallVector<mlir::Block *> blocks;
op->walk([&](mlir::Block *block) {
if (!block->isEntryBlock() &&
llvm::any_of(block->getArgumentTypes(), isCompileTimeOnly))
blocks.push_back(block);
});
for (mlir::Block *block : blocks)
// Rebuilding drops an argument, so work back to front to keep the
// indices of the arguments still to be looked at.
for (unsigned i = block->getNumArguments(); i > 0; --i)
if (isCompileTimeOnly(block->getArgument(i - 1).getType()))
rebuildBlockArgument(block, i - 1);
}
class CodeGenRewrite : public fir::impl::CodeGenRewriteBase<CodeGenRewrite> {
public:
using CodeGenRewriteBase<CodeGenRewrite>::CodeGenRewriteBase;
void runOnOperation() override final {
mlir::ModuleOp mod = getOperation();
rebuildCompileTimeOnlyBlockArguments(mod.getOperation());
auto &context = getContext();
mlir::ConversionTarget target(context);
target.addLegalDialect<mlir::arith::ArithDialect, fir::FIROpsDialect,
fir::FIRCodeGenDialect, mlir::func::FuncDialect,
mlir::cf::ControlFlowDialect>();
target.addIllegalOp<fir::ArrayCoorOp>();
target.addIllegalOp<fir::ReboxOp>();
target.addIllegalOp<fir::DeclareOp>();
target.addIllegalOp<fir::DummyScopeOp>();
target.addDynamicallyLegalOp<fir::EmboxOp>([](fir::EmboxOp embox) {
return llvm::failed(EmboxConversion::getRewriteKind(embox));
});
mlir::RewritePatternSet patterns(&context);
fir::populatePreCGRewritePatterns(patterns, preserveDeclare);
if (mlir::failed(
mlir::applyPartialConversion(mod, target, std::move(patterns)))) {
mlir::emitError(mlir::UnknownLoc::get(&context),
"error in running the pre-codegen conversions");
signalPassFailure();
return;
}
// Erase any residual (fir.shape, fir.slice...).
mlir::IRRewriter rewriter(&context);
simpleDCE(rewriter, mod.getOperation());
}
};
} // namespace
void fir::populatePreCGRewritePatterns(mlir::RewritePatternSet &patterns,
bool preserveDeclare) {
patterns.insert<EmboxConversion, ArrayCoorConversion, ReboxConversion,
DummyScopeOpConversion>(patterns.getContext());
patterns.add<DeclareOpConversion>(patterns.getContext(), preserveDeclare);
}