[mlir] Remove getNumberOfExecutions from RegionBranchOpInterface

`getNumRegionInvocations` was originally added for the async reference counting, but turned out to be not useful, and currently is not used anywhere (couldn't find any uses in public github repos). Removing dead code.

Reviewed By: Mogball, mehdi_amini

Differential Revision: https://reviews.llvm.org/D117347
This commit is contained in:
Eugene Zhulenev 2022-01-14 11:08:23 -08:00
parent 7c269db779
commit 69bc334be5
18 changed files with 1 additions and 841 deletions

View File

@ -1,107 +0,0 @@
//===- NumberOfExecutions.h - Number of executions analysis -----*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This file contains an analysis for computing how many times a block within a
// region is executed *each time* that region is entered. The analysis
// iterates over all associated regions that are attached to the given top-level
// operation.
//
// It is possible to query number of executions information on block level.
//
//===----------------------------------------------------------------------===//
#ifndef MLIR_ANALYSIS_NUMBEROFEXECUTIONS_H
#define MLIR_ANALYSIS_NUMBEROFEXECUTIONS_H
#include "mlir/Support/LLVM.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/Optional.h"
namespace mlir {
class Block;
class BlockNumberOfExecutionsInfo;
class Operation;
class Region;
/// Represents an analysis for computing how many times a block or an operation
/// within a region is executed *each time* that region is entered. The analysis
/// iterates over all associated regions that are attached to the given
/// top-level operation.
///
/// This analysis assumes that all operations complete in a finite amount of
/// time (do not abort and do not go into the infinite loop).
class NumberOfExecutions {
public:
/// Creates a new NumberOfExecutions analysis that computes how many times a
/// block within a region is executed for all associated regions.
explicit NumberOfExecutions(Operation *op);
/// Returns the number of times operations `op` is executed *each time* the
/// control flow enters the region `perEntryOfThisRegion`. Returns empty
/// optional if this is not known statically.
Optional<int64_t> getNumberOfExecutions(Operation *op,
Region *perEntryOfThisRegion) const;
/// Returns the number of times block `block` is executed *each time* the
/// control flow enters the region `perEntryOfThisRegion`. Returns empty
/// optional if this is not known statically.
Optional<int64_t> getNumberOfExecutions(Block *block,
Region *perEntryOfThisRegion) const;
/// Dumps the number of block executions *each time* the control flow enters
/// the region `perEntryOfThisRegion` to the given stream.
void printBlockExecutions(raw_ostream &os,
Region *perEntryOfThisRegion) const;
/// Dumps the number of operation executions *each time* the control flow
/// enters the region `perEntryOfThisRegion` to the given stream.
void printOperationExecutions(raw_ostream &os,
Region *perEntryOfThisRegion) const;
private:
/// The operation this analysis was constructed from.
Operation *operation;
/// A mapping from blocks to number of executions information.
DenseMap<Block *, BlockNumberOfExecutionsInfo> blockNumbersOfExecution;
};
/// Represents number of block executions information.
class BlockNumberOfExecutionsInfo {
public:
BlockNumberOfExecutionsInfo(Block *block,
Optional<int64_t> numberOfRegionInvocations,
Optional<int64_t> numberOfBlockExecutions);
/// Returns the number of times this block will be executed *each time* the
/// parent operation is executed.
Optional<int64_t> getNumberOfExecutions() const;
/// Returns the number of times this block will be executed if the parent
/// region is invoked `numberOfRegionInvocations` times. This can be different
/// from the number of region invocations by the parent operation.
Optional<int64_t>
getNumberOfExecutions(int64_t numberOfRegionInvocations) const;
Block *getBlock() const { return block; }
private:
Block *block;
/// Number of `block` parent region invocations *each time* parent operation
/// is executed.
Optional<int64_t> numberOfRegionInvocations;
/// Number of `block` executions *each time* parent region is invoked.
Optional<int64_t> numberOfBlockExecutions;
};
} // namespace mlir
#endif // MLIR_ANALYSIS_NUMBEROFEXECUTIONS_H

View File

@ -29,8 +29,7 @@ class Async_Op<string mnemonic, list<OpTrait> traits = []> :
def Async_ExecuteOp :
Async_Op<"execute", [SingleBlockImplicitTerminator<"YieldOp">,
DeclareOpInterfaceMethods<RegionBranchOpInterface,
["getSuccessorEntryOperands",
"getNumRegionInvocations"]>,
["getSuccessorEntryOperands"]>,
AttrSizedOperandSegments]> {
let summary = "Asynchronous execute operation";
let description = [{

View File

@ -309,11 +309,6 @@ def ForOp : SCF_Op<"for",
/// induction variable. LoopOp only has one region, so 0 is the only valid
/// value for `index`.
OperandRange getSuccessorEntryOperands(unsigned index);
/// Returns the number of invocations of the body block if the loop bounds
/// are constants. Returns `kUnknownNumRegionInvocations` otherwise.
void getNumRegionInvocations(ArrayRef<Attribute> operands,
SmallVectorImpl<int64_t> &countPerRegion);
}];
let hasCanonicalizer = 1;
@ -404,12 +399,6 @@ def IfOp : SCF_Op<"if",
YieldOp thenYield();
Block* elseBlock();
YieldOp elseYield();
/// If the condition is a constant, returns 1 for the executed block and 0
/// for the other. Otherwise, returns `kUnknownNumRegionInvocations` for
/// both successors.
void getNumRegionInvocations(ArrayRef<Attribute> operands,
SmallVectorImpl<int64_t> &countPerRegion);
}];
let hasFolder = 1;
let hasCanonicalizer = 1;

View File

@ -41,9 +41,6 @@ LogicalResult verifyBranchSuccessorOperands(Operation *op, unsigned succNo,
// RegionBranchOpInterface
//===----------------------------------------------------------------------===//
// A constant value to represent unknown number of region invocations.
extern const int64_t kUnknownNumRegionInvocations;
namespace detail {
/// Verify that types match along control flow edges described the given op.
LogicalResult verifyTypesAlongControlFlowEdges(Operation *op);

View File

@ -129,26 +129,6 @@ def RegionBranchOpInterface : OpInterface<"RegionBranchOpInterface"> {
"void", "getSuccessorRegions",
(ins "::mlir::Optional<unsigned>":$index, "::mlir::ArrayRef<::mlir::Attribute>":$operands,
"::mlir::SmallVectorImpl<::mlir::RegionSuccessor> &":$regions)
>,
InterfaceMethod<[{
Populates countPerRegion with the number of times this operation will
invoke the attached regions (assuming the regions yield normally, i.e.
do not abort or invoke an infinite loop). If the number of region
invocations is not known statically it will set the number of
invocations to `kUnknownNumRegionInvocations`.
`operands` is a set of optional attributes that either correspond to a
constant values for each operand of this operation, or null if that
operand is not a constant.
}],
"void", "getNumRegionInvocations",
(ins "::mlir::ArrayRef<::mlir::Attribute>":$operands,
"::mlir::SmallVectorImpl<int64_t> &":$countPerRegion), [{}],
/*defaultImplementation=*/[{
unsigned numRegions = this->getOperation()->getNumRegions();
assert(countPerRegion.empty());
countPerRegion.resize(numRegions, kUnknownNumRegionInvocations);
}]
>
];

View File

@ -9,7 +9,6 @@ set(LLVM_OPTIONAL_SOURCES
Liveness.cpp
LoopAnalysis.cpp
NestedMatcher.cpp
NumberOfExecutions.cpp
SliceAnalysis.cpp
Utils.cpp
@ -23,7 +22,6 @@ add_mlir_library(MLIRAnalysis
DataFlowAnalysis.cpp
DataLayoutAnalysis.cpp
Liveness.cpp
NumberOfExecutions.cpp
SliceAnalysis.cpp
AliasAnalysis/LocalAliasAnalysis.cpp

View File

@ -1,242 +0,0 @@
//===- NumberOfExecutions.cpp - Number of executions analysis -------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// Implementation of the number of executions analysis.
//
//===----------------------------------------------------------------------===//
#include "mlir/Analysis/NumberOfExecutions.h"
#include "mlir/IR/Matchers.h"
#include "mlir/IR/RegionKindInterface.h"
#include "mlir/Interfaces/ControlFlowInterfaces.h"
#include "llvm/ADT/FunctionExtras.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/Support/raw_ostream.h"
#define DEBUG_TYPE "number-of-executions-analysis"
using namespace mlir;
//===----------------------------------------------------------------------===//
// NumberOfExecutions
//===----------------------------------------------------------------------===//
/// Computes blocks number of executions information for the given region.
static void computeRegionBlockNumberOfExecutions(
Region &region, DenseMap<Block *, BlockNumberOfExecutionsInfo> &blockInfo) {
Operation *parentOp = region.getParentOp();
int regionId = region.getRegionNumber();
auto regionKindInterface = dyn_cast<RegionKindInterface>(parentOp);
bool isGraphRegion =
regionKindInterface &&
regionKindInterface.getRegionKind(regionId) == RegionKind::Graph;
// CFG analysis does not make sense for Graph regions, set the number of
// executions for all blocks as unknown.
if (isGraphRegion) {
for (Block &block : region)
blockInfo.insert({&block, {&block, None, None}});
return;
}
// Number of region invocations for all attached regions.
SmallVector<int64_t, 4> numRegionsInvocations;
// Query RegionBranchOpInterface interface if it is available.
if (auto regionInterface = dyn_cast<RegionBranchOpInterface>(parentOp)) {
SmallVector<Attribute, 4> operands(parentOp->getNumOperands());
for (const auto &operandIt : llvm::enumerate(parentOp->getOperands()))
matchPattern(operandIt.value(), m_Constant(&operands[operandIt.index()]));
regionInterface.getNumRegionInvocations(operands, numRegionsInvocations);
}
// Number of region invocations *each time* parent operation is invoked.
Optional<int64_t> numRegionInvocations;
if (!numRegionsInvocations.empty() &&
numRegionsInvocations[regionId] != kUnknownNumRegionInvocations) {
numRegionInvocations = numRegionsInvocations[regionId];
}
// DFS traversal looking for loops in the CFG.
llvm::SmallSet<Block *, 4> loopStart;
llvm::unique_function<void(Block *, llvm::SmallSet<Block *, 4> &)> dfs =
[&](Block *block, llvm::SmallSet<Block *, 4> &visited) {
// Found a loop in the CFG that starts at the `block`.
if (visited.contains(block)) {
loopStart.insert(block);
return;
}
// Continue DFS traversal.
visited.insert(block);
for (Block *successor : block->getSuccessors())
dfs(successor, visited);
visited.erase(block);
};
llvm::SmallSet<Block *, 4> visited;
dfs(&region.front(), visited);
// Start from the entry block and follow only blocks with single succesor.
Block *block = &region.front();
while (block && !loopStart.contains(block)) {
// Block will be executed exactly once.
blockInfo.insert(
{block, BlockNumberOfExecutionsInfo(block, numRegionInvocations,
/*numberOfBlockExecutions=*/1)});
// We reached the exit block or block with multiple successors.
if (block->getNumSuccessors() != 1)
break;
// Continue traversal.
block = block->getSuccessor(0);
}
// For all blocks that we did not visit set the executions number to unknown.
for (Block &block : region)
if (blockInfo.count(&block) == 0)
blockInfo.insert({&block, BlockNumberOfExecutionsInfo(
&block, numRegionInvocations,
/*numberOfBlockExecutions=*/None)});
}
/// Creates a new NumberOfExecutions analysis that computes how many times a
/// block within a region is executed for all associated regions.
NumberOfExecutions::NumberOfExecutions(Operation *op) : operation(op) {
operation->walk<WalkOrder::PreOrder>([&](Region *region) {
computeRegionBlockNumberOfExecutions(*region, blockNumbersOfExecution);
});
}
Optional<int64_t>
NumberOfExecutions::getNumberOfExecutions(Operation *op,
Region *perEntryOfThisRegion) const {
// Assuming that all operations complete in a finite amount of time (do not
// abort and do not go into the infinite loop), the number of operation
// executions is equal to the number of block executions that contains the
// operation.
return getNumberOfExecutions(op->getBlock(), perEntryOfThisRegion);
}
Optional<int64_t>
NumberOfExecutions::getNumberOfExecutions(Block *block,
Region *perEntryOfThisRegion) const {
// Return None if the given `block` does not lie inside the
// `perEntryOfThisRegion` region.
if (!perEntryOfThisRegion->findAncestorBlockInRegion(*block))
return None;
// Find the block information for the given `block.
auto blockIt = blockNumbersOfExecution.find(block);
if (blockIt == blockNumbersOfExecution.end())
return None;
const auto &blockInfo = blockIt->getSecond();
// Override the number of region invocations with `1` if the
// `perEntryOfThisRegion` region owns the block.
auto getNumberOfExecutions = [&](const BlockNumberOfExecutionsInfo &info) {
if (info.getBlock()->getParent() == perEntryOfThisRegion)
return info.getNumberOfExecutions(/*numberOfRegionInvocations=*/1);
return info.getNumberOfExecutions();
};
// Immediately return None if we do not know the block number of executions.
auto blockExecutions = getNumberOfExecutions(blockInfo);
if (!blockExecutions.hasValue())
return None;
// Follow parent operations until we reach the operations that owns the
// `perEntryOfThisRegion`.
int64_t numberOfExecutions = *blockExecutions;
Operation *parentOp = block->getParentOp();
while (parentOp != perEntryOfThisRegion->getParentOp()) {
// Find how many times will be executed the block that owns the parent
// operation.
Block *parentBlock = parentOp->getBlock();
auto parentBlockIt = blockNumbersOfExecution.find(parentBlock);
if (parentBlockIt == blockNumbersOfExecution.end())
return None;
const auto &parentBlockInfo = parentBlockIt->getSecond();
auto parentBlockExecutions = getNumberOfExecutions(parentBlockInfo);
// We stumbled upon an operation with unknown number of executions.
if (!parentBlockExecutions.hasValue())
return None;
// Number of block executions is a product of all parent blocks executions.
numberOfExecutions *= *parentBlockExecutions;
parentOp = parentOp->getParentOp();
assert(parentOp != nullptr);
}
return numberOfExecutions;
}
void NumberOfExecutions::printBlockExecutions(
raw_ostream &os, Region *perEntryOfThisRegion) const {
unsigned blockId = 0;
operation->walk<WalkOrder::PreOrder>([&](Block *block) {
llvm::errs() << "Block: " << blockId++ << "\n";
llvm::errs() << "Number of executions: ";
if (auto n = getNumberOfExecutions(block, perEntryOfThisRegion))
llvm::errs() << *n << "\n";
else
llvm::errs() << "<unknown>\n";
});
}
void NumberOfExecutions::printOperationExecutions(
raw_ostream &os, Region *perEntryOfThisRegion) const {
operation->walk<WalkOrder::PreOrder>([&](Block *block) {
block->walk([&](Operation *operation) {
// Skip the operation that was used to build the analysis.
if (operation == this->operation)
return;
llvm::errs() << "Operation: " << operation->getName() << "\n";
llvm::errs() << "Number of executions: ";
if (auto n = getNumberOfExecutions(operation, perEntryOfThisRegion))
llvm::errs() << *n << "\n";
else
llvm::errs() << "<unknown>\n";
});
});
}
//===----------------------------------------------------------------------===//
// BlockNumberOfExecutionsInfo
//===----------------------------------------------------------------------===//
BlockNumberOfExecutionsInfo::BlockNumberOfExecutionsInfo(
Block *block, Optional<int64_t> numberOfRegionInvocations,
Optional<int64_t> numberOfBlockExecutions)
: block(block), numberOfRegionInvocations(numberOfRegionInvocations),
numberOfBlockExecutions(numberOfBlockExecutions) {}
Optional<int64_t> BlockNumberOfExecutionsInfo::getNumberOfExecutions() const {
if (numberOfRegionInvocations && numberOfBlockExecutions)
return *numberOfRegionInvocations * *numberOfBlockExecutions;
return None;
}
Optional<int64_t> BlockNumberOfExecutionsInfo::getNumberOfExecutions(
int64_t numberOfRegionInvocations) const {
if (numberOfBlockExecutions)
return numberOfRegionInvocations * *numberOfBlockExecutions;
return None;
}

View File

@ -60,12 +60,6 @@ YieldOp::getMutableSuccessorOperands(Optional<unsigned> index) {
constexpr char kOperandSegmentSizesAttr[] = "operand_segment_sizes";
void ExecuteOp::getNumRegionInvocations(
ArrayRef<Attribute>, SmallVectorImpl<int64_t> &countPerRegion) {
assert(countPerRegion.empty());
countPerRegion.push_back(1);
}
OperandRange ExecuteOp::getSuccessorEntryOperands(unsigned index) {
assert(index == 0 && "invalid region index");
return operands();

View File

@ -463,26 +463,6 @@ void ForOp::getSuccessorRegions(Optional<unsigned> index,
regions.push_back(RegionSuccessor(getResults()));
}
void ForOp::getNumRegionInvocations(ArrayRef<Attribute> operands,
SmallVectorImpl<int64_t> &countPerRegion) {
assert(countPerRegion.empty());
countPerRegion.resize(1);
auto lb = operands[0].dyn_cast_or_null<IntegerAttr>();
auto ub = operands[1].dyn_cast_or_null<IntegerAttr>();
auto step = operands[2].dyn_cast_or_null<IntegerAttr>();
// Loop bounds are not known statically.
if (!lb || !ub || !step || step.getValue().getSExtValue() == 0) {
countPerRegion[0] = kUnknownNumRegionInvocations;
return;
}
countPerRegion[0] =
ceilDiv(ub.getValue().getSExtValue() - lb.getValue().getSExtValue(),
step.getValue().getSExtValue());
}
LoopNest mlir::scf::buildLoopNest(
OpBuilder &builder, Location loc, ValueRange lbs, ValueRange ubs,
ValueRange steps, ValueRange iterArgs,
@ -1182,23 +1162,6 @@ void IfOp::getSuccessorRegions(Optional<unsigned> index,
regions.push_back(RegionSuccessor(condition ? &getThenRegion() : elseRegion));
}
/// If the condition is a constant, returns 1 for the executed block and 0 for
/// the other. Otherwise, returns `kUnknownNumRegionInvocations` for both
/// successors.
void IfOp::getNumRegionInvocations(ArrayRef<Attribute> operands,
SmallVectorImpl<int64_t> &countPerRegion) {
if (auto condAttr = operands.front().dyn_cast_or_null<IntegerAttr>()) {
// If the condition is true, `then` is executed once and `else` zero times,
// and vice-versa.
bool cond = condAttr.getValue().isOneValue();
countPerRegion.assign(1, cond ? 1 : 0);
countPerRegion.push_back(cond ? 0 : 1);
} else {
// Non-constant condition: unknown invocations for both successors.
countPerRegion.assign(2, kUnknownNumRegionInvocations);
}
}
LogicalResult IfOp::fold(ArrayRef<Attribute> operands,
SmallVectorImpl<OpFoldResult> &results) {
// if (!c) then A() else B() -> if c then B() else A()

View File

@ -73,9 +73,6 @@ detail::verifyBranchSuccessorOperands(Operation *op, unsigned succNo,
// RegionBranchOpInterface
//===----------------------------------------------------------------------===//
// A constant value to represent unknown number of region invocations.
const int64_t mlir::kUnknownNumRegionInvocations = -1;
/// Verify that types match along all region control flow edges originating from
/// `sourceNo` (region # if source is a region, llvm::None if source is parent
/// op). `getInputsTypesForRegion` is a function that returns the types of the

View File

@ -1,192 +0,0 @@
// RUN: mlir-opt %s \
// RUN: -test-print-number-of-block-executions \
// RUN: -split-input-file 2>&1 \
// RUN: | FileCheck %s --dump-input=always
// CHECK-LABEL: Number of executions: empty
func @empty() {
// CHECK: Block: 0
// CHECK-NEXT: Number of executions: 1
return
}
// -----
// CHECK-LABEL: Number of executions: sequential
func @sequential() {
// CHECK: Block: 0
// CHECK-NEXT: Number of executions: 1
br ^bb1
^bb1:
// CHECK: Block: 1
// CHECK-NEXT: Number of executions: 1
br ^bb2
^bb2:
// CHECK: Block: 2
// CHECK-NEXT: Number of executions: 1
return
}
// -----
// CHECK-LABEL: Number of executions: conditional
func @conditional(%cond : i1) {
// CHECK: Block: 0
// CHECK-NEXT: Number of executions: 1
br ^bb1
^bb1:
// CHECK: Block: 1
// CHECK-NEXT: Number of executions: 1
cond_br %cond, ^bb2, ^bb3
^bb2:
// CHECK: Block: 2
// CHECK-NEXT: Number of executions: <unknown>
br ^bb4
^bb3:
// CHECK: Block: 3
// CHECK-NEXT: Number of executions: <unknown>
br ^bb4
^bb4:
// CHECK: Block: 4
// CHECK-NEXT: Number of executions: <unknown>
return
}
// -----
// CHECK-LABEL: Number of executions: loop
func @loop(%cond : i1) {
// CHECK: Block: 0
// CHECK-NEXT: Number of executions: 1
br ^bb1
^bb1:
// CHECK: Block: 1
// CHECK-NEXT: Number of executions: <unknown>
br ^bb2
^bb2:
// CHECK: Block: 2
// CHECK-NEXT: Number of executions: <unknown>
br ^bb3
^bb3:
// CHECK: Block: 3
// CHECK-NEXT: Number of executions: <unknown>
cond_br %cond, ^bb1, ^bb4
^bb4:
// CHECK: Block: 4
// CHECK-NEXT: Number of executions: <unknown>
return
}
// -----
// CHECK-LABEL: Number of executions: scf_if_dynamic_branch
func @scf_if_dynamic_branch(%cond : i1) {
// CHECK: Block: 0
// CHECK-NEXT: Number of executions: 1
scf.if %cond {
// CHECK: Block: 1
// CHECK-NEXT: Number of executions: <unknown>
} else {
// CHECK: Block: 2
// CHECK-NEXT: Number of executions: <unknown>
}
return
}
// -----
// CHECK-LABEL: Number of executions: async_execute
func @async_execute() {
// CHECK: Block: 0
// CHECK-NEXT: Number of executions: 1
async.execute {
// CHECK: Block: 1
// CHECK-NEXT: Number of executions: 1
async.yield
}
return
}
// -----
// CHECK-LABEL: Number of executions: async_execute_with_scf_if
func @async_execute_with_scf_if(%cond : i1) {
// CHECK: Block: 0
// CHECK-NEXT: Number of executions: 1
async.execute {
// CHECK: Block: 1
// CHECK-NEXT: Number of executions: 1
scf.if %cond {
// CHECK: Block: 2
// CHECK-NEXT: Number of executions: <unknown>
} else {
// CHECK: Block: 3
// CHECK-NEXT: Number of executions: <unknown>
}
async.yield
}
return
}
// -----
// CHECK-LABEL: Number of executions: scf_for_constant_bounds
func @scf_for_constant_bounds() {
// CHECK: Block: 0
// CHECK-NEXT: Number of executions: 1
%c0 = arith.constant 0 : index
%c1 = arith.constant 1 : index
%c2 = arith.constant 2 : index
scf.for %i = %c0 to %c2 step %c1 {
// CHECK: Block: 1
// CHECK-NEXT: Number of executions: 2
}
return
}
// -----
// CHECK-LABEL: Number of executions: propagate_parent_num_executions
func @propagate_parent_num_executions() {
// CHECK: Block: 0
// CHECK-NEXT: Number of executions: 1
%c0 = arith.constant 0 : index
%c1 = arith.constant 1 : index
%c2 = arith.constant 2 : index
scf.for %i = %c0 to %c2 step %c1 {
// CHECK: Block: 1
// CHECK-NEXT: Number of executions: 2
async.execute {
// CHECK: Block: 2
// CHECK-NEXT: Number of executions: 2
async.yield
}
}
return
}
// -----
// CHECK-LABEL: Number of executions: clear_num_executions
func @clear_num_executions(%step : index) {
// CHECK: Block: 0
// CHECK-NEXT: Number of executions: 1
%c0 = arith.constant 0 : index
%c2 = arith.constant 2 : index
scf.for %i = %c0 to %c2 step %step {
// CHECK: Block: 1
// CHECK-NEXT: Number of executions: <unknown>
async.execute {
// CHECK: Block: 2
// CHECK-NEXT: Number of executions: <unknown>
async.yield
}
}
return
}

View File

@ -1,66 +0,0 @@
// RUN: mlir-opt %s \
// RUN: -test-print-number-of-operation-executions \
// RUN: -split-input-file 2>&1 \
// RUN: | FileCheck %s
// CHECK-LABEL: Number of executions: empty
func @empty() {
// CHECK: Operation: std.return
// CHECK-NEXT: Number of executions: 1
return
}
// -----
// CHECK-LABEL: Number of executions: propagate_parent_num_executions
func @propagate_parent_num_executions() {
// CHECK: Operation: arith.constant
// CHECK-NEXT: Number of executions: 1
%c0 = arith.constant 0 : index
// CHECK: Operation: arith.constant
// CHECK-NEXT: Number of executions: 1
%c1 = arith.constant 1 : index
// CHECK: Operation: arith.constant
// CHECK-NEXT: Number of executions: 1
%c2 = arith.constant 2 : index
// CHECK-DAG: Operation: scf.for
// CHECK-NEXT: Number of executions: 1
scf.for %i = %c0 to %c2 step %c1 {
// CHECK-DAG: Operation: async.execute
// CHECK-NEXT: Number of executions: 2
async.execute {
// CHECK-DAG: Operation: async.yield
// CHECK-NEXT: Number of executions: 2
async.yield
}
}
return
}
// -----
// CHECK-LABEL: Number of executions: clear_num_executions
func @clear_num_executions(%step : index) {
// CHECK: Operation: arith.constant
// CHECK-NEXT: Number of executions: 1
%c0 = arith.constant 0 : index
// CHECK: Operation: arith.constant
// CHECK-NEXT: Number of executions: 1
%c2 = arith.constant 2 : index
// CHECK: Operation: scf.for
// CHECK-NEXT: Number of executions: 1
scf.for %i = %c0 to %c2 step %step {
// CHECK: Operation: async.execute
// CHECK-NEXT: Number of executions: <unknown>
async.execute {
// CHECK: Operation: async.yield
// CHECK-NEXT: Number of executions: <unknown>
async.yield
}
}
return
}

View File

@ -7,7 +7,6 @@ add_mlir_library(MLIRTestAnalysis
TestMemRefBoundCheck.cpp
TestMemRefDependenceCheck.cpp
TestMemRefStrideCalculation.cpp
TestNumberOfExecutions.cpp
TestSlice.cpp

View File

@ -1,67 +0,0 @@
//===- TestNumberOfExecutions.cpp - Test number of executions analysis ----===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This file contains test passes for constructing and resolving number of
// executions information.
//
//===----------------------------------------------------------------------===//
#include "mlir/Analysis/NumberOfExecutions.h"
#include "mlir/Pass/Pass.h"
using namespace mlir;
namespace {
struct TestNumberOfBlockExecutionsPass
: public PassWrapper<TestNumberOfBlockExecutionsPass, FunctionPass> {
StringRef getArgument() const final {
return "test-print-number-of-block-executions";
}
StringRef getDescription() const final {
return "Print the contents of a constructed number of executions analysis "
"for "
"all blocks.";
}
void runOnFunction() override {
llvm::errs() << "Number of executions: " << getFunction().getName() << "\n";
getAnalysis<NumberOfExecutions>().printBlockExecutions(
llvm::errs(), &getFunction().getBody());
}
};
struct TestNumberOfOperationExecutionsPass
: public PassWrapper<TestNumberOfOperationExecutionsPass, FunctionPass> {
StringRef getArgument() const final {
return "test-print-number-of-operation-executions";
}
StringRef getDescription() const final {
return "Print the contents of a constructed number of executions analysis "
"for "
"all operations.";
}
void runOnFunction() override {
llvm::errs() << "Number of executions: " << getFunction().getName() << "\n";
getAnalysis<NumberOfExecutions>().printOperationExecutions(
llvm::errs(), &getFunction().getBody());
}
};
} // namespace
namespace mlir {
namespace test {
void registerTestNumberOfBlockExecutionsPass() {
PassRegistration<TestNumberOfBlockExecutionsPass>();
}
void registerTestNumberOfOperationExecutionsPass() {
PassRegistration<TestNumberOfOperationExecutionsPass>();
}
} // namespace test
} // namespace mlir

View File

@ -102,8 +102,6 @@ void registerTestMathAlgebraicSimplificationPass();
void registerTestMathPolynomialApproximationPass();
void registerTestMemRefDependenceCheck();
void registerTestMemRefStrideCalculation();
void registerTestNumberOfBlockExecutionsPass();
void registerTestNumberOfOperationExecutionsPass();
void registerTestOpaqueLoc();
void registerTestPadFusion();
void registerTestPDLByteCodePass();
@ -196,8 +194,6 @@ void registerTestPasses() {
mlir::test::registerTestMathPolynomialApproximationPass();
mlir::test::registerTestMemRefDependenceCheck();
mlir::test::registerTestMemRefStrideCalculation();
mlir::test::registerTestNumberOfBlockExecutionsPass();
mlir::test::registerTestNumberOfOperationExecutionsPass();
mlir::test::registerTestOpaqueLoc();
mlir::test::registerTestPadFusion();
mlir::test::registerTestPDLByteCodePass();

View File

@ -7,7 +7,6 @@ target_link_libraries(MLIRDialectTests
MLIRDialect)
add_subdirectory(Quant)
add_subdirectory(SCF)
add_subdirectory(SparseTensor)
add_subdirectory(SPIRV)
add_subdirectory(Utils)

View File

@ -1,10 +0,0 @@
add_mlir_unittest(MLIRSCFTests
SCFOps.cpp
)
target_link_libraries(MLIRSCFTests
PRIVATE
MLIRIR
MLIRParser
MLIRSCF
MLIRStandard
)

View File

@ -1,67 +0,0 @@
//===- SCFOps.cpp - SCF Op Unit Tests -------------------------------------===//
//
// 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/Dialect/SCF/SCF.h"
#include "mlir/Dialect/StandardOps/IR/Ops.h"
#include "mlir/Parser.h"
#include "gtest/gtest.h"
using namespace mlir;
namespace {
class SCFOpsTest : public testing::Test {
public:
SCFOpsTest() {
context.getOrLoadDialect<scf::SCFDialect>();
context.getOrLoadDialect<StandardOpsDialect>();
}
protected:
MLIRContext context;
};
TEST_F(SCFOpsTest, IfOpNumRegionInvocations) {
const char *const code = R"mlir(
func @test(%cond : i1) -> () {
scf.if %cond {
scf.yield
} else {
scf.yield
}
return
}
)mlir";
Builder builder(&context);
auto module = parseSourceString(code, &context);
ASSERT_TRUE(module);
scf::IfOp op;
module->walk([&](scf::IfOp ifOp) { op = ifOp; });
ASSERT_TRUE(op);
SmallVector<int64_t> countPerRegion;
op.getNumRegionInvocations({Attribute()}, countPerRegion);
EXPECT_EQ(countPerRegion.size(), 2u);
EXPECT_EQ(countPerRegion[0], kUnknownNumRegionInvocations);
EXPECT_EQ(countPerRegion[1], kUnknownNumRegionInvocations);
countPerRegion.clear();
op.getNumRegionInvocations(
{builder.getIntegerAttr(builder.getI1Type(), true)}, countPerRegion);
EXPECT_EQ(countPerRegion.size(), 2u);
EXPECT_EQ(countPerRegion[0], 1);
EXPECT_EQ(countPerRegion[1], 0);
countPerRegion.clear();
op.getNumRegionInvocations(
{builder.getIntegerAttr(builder.getI1Type(), false)}, countPerRegion);
EXPECT_EQ(countPerRegion.size(), 2u);
EXPECT_EQ(countPerRegion[0], 0);
EXPECT_EQ(countPerRegion[1], 1);
}
} // end anonymous namespace