Files
clang-p2996/mlir/lib/Interfaces/SideEffectInterfaces.cpp
Markus Böck 61394636f0 [mlir] Ignore effects on allocated results when checking whether the op is trivially dead.
In the current state, this is only special cased for Allocation effects, but any effects on results allocated by the operation may be ignored when checking whether the op may be removed, as none of them are possible to be observed if the result is unused.

A use case for this is for IRs for languages which always initialize on allocation. To correctly model such operations, a Write as well as an Allocation effect should be placed on the result. This would prevent the Op from being deleted if unused however. This patch fixes that issue.

Differential Revision: https://reviews.llvm.org/D129854
2022-07-19 10:58:25 +02:00

136 lines
5.1 KiB
C++

//===- SideEffectInterfaces.cpp - SideEffects in MLIR ---------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
#include "mlir/Interfaces/SideEffectInterfaces.h"
#include "llvm/ADT/SmallPtrSet.h"
using namespace mlir;
//===----------------------------------------------------------------------===//
// SideEffect Interfaces
//===----------------------------------------------------------------------===//
/// Include the definitions of the side effect interfaces.
#include "mlir/Interfaces/SideEffectInterfaces.cpp.inc"
//===----------------------------------------------------------------------===//
// MemoryEffects
//===----------------------------------------------------------------------===//
bool MemoryEffects::Effect::classof(const SideEffects::Effect *effect) {
return isa<Allocate, Free, Read, Write>(effect);
}
//===----------------------------------------------------------------------===//
// SideEffect Utilities
//===----------------------------------------------------------------------===//
bool mlir::isOpTriviallyDead(Operation *op) {
return op->use_empty() && wouldOpBeTriviallyDead(op);
}
/// Internal implementation of `mlir::wouldOpBeTriviallyDead` that also
/// considers terminator operations as dead if they have no side effects. This
/// allows for marking region operations as trivially dead without always being
/// conservative of terminators.
static bool wouldOpBeTriviallyDeadImpl(Operation *rootOp) {
// The set of operations to consider when checking for side effects.
SmallVector<Operation *, 1> effectingOps(1, rootOp);
while (!effectingOps.empty()) {
Operation *op = effectingOps.pop_back_val();
// If the operation has recursive effects, push all of the nested operations
// on to the stack to consider.
bool hasRecursiveEffects = op->hasTrait<OpTrait::HasRecursiveSideEffects>();
if (hasRecursiveEffects) {
for (Region &region : op->getRegions()) {
for (auto &block : region) {
for (auto &nestedOp : block)
effectingOps.push_back(&nestedOp);
}
}
}
// If the op has memory effects, try to characterize them to see if the op
// is trivially dead here.
if (auto effectInterface = dyn_cast<MemoryEffectOpInterface>(op)) {
// Check to see if this op either has no effects, or only allocates/reads
// memory.
SmallVector<MemoryEffects::EffectInstance, 1> effects;
effectInterface.getEffects(effects);
// Gather all results of this op that are allocated.
SmallPtrSet<Value, 4> allocResults;
for (const MemoryEffects::EffectInstance &it : effects)
if (isa<MemoryEffects::Allocate>(it.getEffect()) && it.getValue() &&
it.getValue().getDefiningOp() == op)
allocResults.insert(it.getValue());
if (!llvm::all_of(effects, [&allocResults](
const MemoryEffects::EffectInstance &it) {
// We can drop effects if the value is an allocation and is a result
// of the operation.
if (allocResults.contains(it.getValue()))
return true;
// Otherwise, the effect must be a read.
return isa<MemoryEffects::Read>(it.getEffect());
})) {
return false;
}
continue;
// Otherwise, if the op has recursive side effects we can treat the
// operation itself as having no effects.
}
if (hasRecursiveEffects)
continue;
// If there were no effect interfaces, we treat this op as conservatively
// having effects.
return false;
}
// If we get here, none of the operations had effects that prevented marking
// 'op' as dead.
return true;
}
template <typename EffectTy>
bool mlir::hasSingleEffect(Operation *op, Value value) {
auto memOp = dyn_cast<MemoryEffectOpInterface>(op);
if (!memOp)
return false;
SmallVector<SideEffects::EffectInstance<MemoryEffects::Effect>, 4> effects;
memOp.getEffects(effects);
bool hasSingleEffectOnVal = false;
// Iterate through `effects` and check if an effect of type `EffectTy` and
// only of that type is present. A `value` to check the effect on may or may
// not have been provided.
for (auto &effect : effects) {
if (value && effect.getValue() != value)
continue;
hasSingleEffectOnVal = isa<EffectTy>(effect.getEffect());
if (!hasSingleEffectOnVal)
return false;
}
return hasSingleEffectOnVal;
}
template bool mlir::hasSingleEffect<MemoryEffects::Allocate>(Operation *,
Value);
template bool mlir::hasSingleEffect<MemoryEffects::Free>(Operation *, Value);
template bool mlir::hasSingleEffect<MemoryEffects::Read>(Operation *, Value);
template bool mlir::hasSingleEffect<MemoryEffects::Write>(Operation *, Value);
bool mlir::wouldOpBeTriviallyDead(Operation *op) {
if (op->mightHaveTrait<OpTrait::IsTerminator>())
return false;
return wouldOpBeTriviallyDeadImpl(op);
}