Roll-forward initial liveness analysis including test cases.

Fix the usage of the map size when appending to the map with [].

PiperOrigin-RevId: 284985916
This commit is contained in:
Alexander Belyaev 2019-12-11 08:01:56 -08:00 committed by A. Unique TensorFlower
parent 984fdde269
commit 4b0198acb5
6 changed files with 774 additions and 0 deletions

View File

@ -0,0 +1,157 @@
//===- Liveness.h - Liveness analysis for MLIR ------------------*- C++ -*-===//
//
// Copyright 2019 The MLIR Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// =============================================================================
//
// This file contains an analysis for computing liveness information from a
// given top-level operation. The current version of the analysis uses a
// traditional algorithm to resolve detailed live-range information about all
// values within the specified regions. It is also possible to query liveness
// information on block level.
//
//===----------------------------------------------------------------------===//
#ifndef MLIR_ANALYSIS_LIVENESS_H
#define MLIR_ANALYSIS_LIVENESS_H
#include <vector>
#include "mlir/Support/LLVM.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallPtrSet.h"
namespace mlir {
class Block;
class LivenessBlockInfo;
class Operation;
class Region;
class Value;
/// Represents an analysis for computing liveness information from a
/// given top-level operation. The analysis iterates over all associated
/// regions that are attached to the given top-level operation. It
/// computes liveness information for every value and block that are
/// included in the mentioned regions. It relies on a fixpoint iteration
/// to compute all live-in and live-out values of all included blocks.
/// Sample usage:
/// Liveness liveness(topLevelOp);
/// auto &allInValues = liveness.getLiveIn(block);
/// auto &allOutValues = liveness.getLiveOut(block);
/// auto allOperationsInWhichValueIsLive = liveness.resolveLiveness(value);
/// bool lastUse = liveness.isLastUse(value, operation);
class Liveness {
public:
using OperationListT = std::vector<Operation *>;
using BlockMapT = DenseMap<Block *, LivenessBlockInfo>;
using ValueSetT = SmallPtrSet<Value *, 16>;
public:
/// Creates a new Liveness analysis that computes liveness
/// information for all associated regions.
Liveness(Operation *op);
/// Returns the operation this analysis was constructed from.
Operation *getOperation() const { return operation; }
/// Gets liveness info (if any) for the given value.
/// This includes all operations in which the given value is live.
/// Note that the operations in this list are not ordered and the current
/// implementation is computationally expensive (as it iterates over all
/// blocks in which the given value is live).
OperationListT resolveLiveness(Value *value) const;
/// Gets liveness info (if any) for the block.
const LivenessBlockInfo *getLiveness(Block *block) const;
/// Returns a reference to a set containing live-in values (unordered).
const ValueSetT &getLiveIn(Block *block) const;
/// Returns a reference to a set containing live-out values (unordered).
const ValueSetT &getLiveOut(Block *block) const;
/// Returns true if the given operation represent the last use of the
/// given value.
bool isLastUse(Value *value, Operation *operation) const;
/// Dumps the liveness information in a human readable format.
void dump() const;
/// Dumps the liveness information to the given stream.
void print(raw_ostream &os) const;
private:
/// Initializes the internal mappings.
void build(MutableArrayRef<Region> regions);
private:
/// The operation this analysis was constructed from.
Operation *operation;
/// Maps blocks to internal liveness information.
BlockMapT blockMapping;
};
/// This class represents liveness information on block level.
class LivenessBlockInfo {
public:
/// A typedef declaration of a value set.
using ValueSetT = Liveness::ValueSetT;
public:
/// Returns the underlying block.
Block *getBlock() const { return block; }
/// Returns all values that are live at the beginning
/// of the block (unordered).
const ValueSetT &in() const { return inValues; }
/// Returns all values that are live at the end
/// of the block (unordered).
const ValueSetT &out() const { return outValues; }
/// Returns true if the given value is in the live-in set.
bool isLiveIn(Value *value) const;
/// Returns true if the given value is in the live-out set.
bool isLiveOut(Value *value) const;
/// Gets the start operation for the given value. This is the first operation
/// the given value is considered to be live. This could either be the start
/// operation of the current block (in case the value is live-in) or the
/// operation that defines the given value (must be referenced in this block).
Operation *getStartOperation(Value *value) const;
/// Gets the end operation for the given value using the start operation
/// provided (must be referenced in this block).
Operation *getEndOperation(Value *value, Operation *startOperation) const;
private:
/// The underlying block.
Block *block;
/// The set of all live in values.
ValueSetT inValues;
/// The set of all live out values.
ValueSetT outValues;
friend class Liveness;
};
} // end namespace mlir
#endif // MLIR_ANALYSIS_LIVENESS_H

View File

@ -4,6 +4,7 @@ add_llvm_library(MLIRAnalysis STATIC
CallGraph.cpp
Dominance.cpp
InferTypeOpInterface.cpp
Liveness.cpp
LoopAnalysis.cpp
MemRefBoundCheck.cpp
NestedMatcher.cpp

View File

@ -0,0 +1,382 @@
//===- Liveness.cpp - Liveness analysis for MLIR --------------------------===//
//
// Copyright 2019 The MLIR Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// =============================================================================
//
// Implementation of the liveness analysis.
//
//===----------------------------------------------------------------------===//
#include "mlir/Analysis/Liveness.h"
#include "mlir/IR/Block.h"
#include "mlir/IR/Operation.h"
#include "mlir/IR/Region.h"
#include "mlir/IR/Value.h"
#include "llvm/ADT/SetOperations.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/Support/raw_ostream.h"
using namespace mlir;
/// Builds and holds block information during the construction phase.
struct BlockInfoBuilder {
using ValueSetT = Liveness::ValueSetT;
/// Constructs an empty block builder.
BlockInfoBuilder() : block(nullptr) {}
/// Fills the block builder with initial liveness information.
BlockInfoBuilder(Block *block) : block(block) {
// Mark all block arguments (phis) as defined.
for (BlockArgument *argument : block->getArguments())
defValues.insert(argument);
// Check all result values and whether their uses
// are inside this block or not (see outValues).
for (Operation &operation : *block)
for (Value *result : operation.getResults()) {
defValues.insert(result);
// Check whether this value will be in the outValues
// set (its uses escape this block). Due to the SSA
// properties of the program, the uses must occur after
// the definition. Therefore, we do not have to check
// additional conditions to detect an escaping value.
for (OpOperand &use : result->getUses())
if (use.getOwner()->getBlock() != block) {
outValues.insert(result);
break;
}
}
// Check all operations for used operands.
for (Operation &operation : block->getOperations())
for (Value *operand : operation.getOperands()) {
// If the operand is already defined in the scope of this
// block, we can skip the value in the use set.
if (!defValues.count(operand))
useValues.insert(operand);
}
}
/// Updates live-in information of the current block.
/// To do so it uses the default liveness-computation formula:
/// newIn = use union out \ def.
/// The methods returns true, if the set has changed (newIn != in),
/// false otherwise.
bool updateLiveIn() {
ValueSetT newIn = useValues;
llvm::set_union(newIn, outValues);
llvm::set_subtract(newIn, defValues);
// It is sufficient to check the set sizes (instead of their contents)
// since the live-in set can only grow monotonically during all update
// operations.
if (newIn.size() == inValues.size())
return false;
inValues = newIn;
return true;
}
/// Updates live-out information of the current block.
/// It iterates over all successors and unifies their live-in
/// values with the current live-out values.
template <typename SourceT> void updateLiveOut(SourceT &source) {
for (Block *succ : block->getSuccessors()) {
BlockInfoBuilder &builder = source[succ];
llvm::set_union(outValues, builder.inValues);
}
}
/// The current block.
Block *block;
/// The set of all live in values.
ValueSetT inValues;
/// The set of all live out values.
ValueSetT outValues;
/// The set of all defined values.
ValueSetT defValues;
/// The set of all used values.
ValueSetT useValues;
};
/// Builds the internal liveness block mapping.
static void buildBlockMapping(MutableArrayRef<Region> regions,
DenseMap<Block *, BlockInfoBuilder> &builders) {
llvm::SetVector<Block *> toProcess;
// Initialize all block structures
for (Region &region : regions)
for (Block &block : region) {
BlockInfoBuilder &builder =
builders.try_emplace(&block, &block).first->second;
if (builder.updateLiveIn())
toProcess.insert(block.pred_begin(), block.pred_end());
}
// Propagate the in and out-value sets (fixpoint iteration)
while (!toProcess.empty()) {
Block *current = toProcess.pop_back_val();
BlockInfoBuilder &builder = builders[current];
// Update the current out values.
builder.updateLiveOut(builders);
// Compute (potentially) updated live in values.
if (builder.updateLiveIn())
toProcess.insert(current->pred_begin(), current->pred_end());
}
}
//===----------------------------------------------------------------------===//
// Liveness
//===----------------------------------------------------------------------===//
/// Creates a new Liveness analysis that computes liveness
/// information for all associated regions.
Liveness::Liveness(Operation *op) : operation(op) { build(op->getRegions()); }
/// Initializes the internal mappings.
void Liveness::build(MutableArrayRef<Region> regions) {
// Build internal block mapping.
DenseMap<Block *, BlockInfoBuilder> builders;
buildBlockMapping(regions, builders);
// Store internal block data.
for (auto &entry : builders) {
BlockInfoBuilder &builder = entry.second;
LivenessBlockInfo &info = blockMapping[entry.first];
info.block = builder.block;
info.inValues = std::move(builder.inValues);
info.outValues = std::move(builder.outValues);
}
}
/// Gets liveness info (if any) for the given value.
Liveness::OperationListT Liveness::resolveLiveness(Value *value) const {
OperationListT result;
SmallPtrSet<Block *, 32> visited;
SmallVector<Block *, 8> toProcess;
// Start with the defining block
Block *currentBlock;
if (Operation *defOp = value->getDefiningOp())
currentBlock = defOp->getBlock();
else
currentBlock = cast<BlockArgument>(value)->getOwner();
toProcess.push_back(currentBlock);
visited.insert(currentBlock);
// Start with all associated blocks
for (OpOperand &use : value->getUses()) {
Block *useBlock = use.getOwner()->getBlock();
if (visited.insert(useBlock).second)
toProcess.push_back(useBlock);
}
while (!toProcess.empty()) {
// Get block and block liveness information.
Block *block = toProcess.back();
toProcess.pop_back();
const LivenessBlockInfo *blockInfo = getLiveness(block);
// Note that start and end will be in the same block.
Operation *start = blockInfo->getStartOperation(value);
Operation *end = blockInfo->getEndOperation(value, start);
result.push_back(start);
while (start != end) {
start = start->getNextNode();
result.push_back(start);
}
for (Block *successor : block->getSuccessors()) {
if (getLiveness(successor)->isLiveIn(value) &&
visited.insert(successor).second)
toProcess.push_back(successor);
}
}
return result;
}
/// Gets liveness info (if any) for the block.
const LivenessBlockInfo *Liveness::getLiveness(Block *block) const {
auto it = blockMapping.find(block);
return it == blockMapping.end() ? nullptr : &it->second;
}
/// Returns a reference to a set containing live-in values.
const Liveness::ValueSetT &Liveness::getLiveIn(Block *block) const {
return getLiveness(block)->in();
}
/// Returns a reference to a set containing live-out values.
const Liveness::ValueSetT &Liveness::getLiveOut(Block *block) const {
return getLiveness(block)->out();
}
/// Returns true if the given operation represent the last use of the
/// given value.
bool Liveness::isLastUse(Value *value, Operation *operation) const {
Block *block = operation->getBlock();
const LivenessBlockInfo *blockInfo = getLiveness(block);
// The given value escapes the associated block.
if (blockInfo->isLiveOut(value))
return false;
Operation *endOperation = blockInfo->getEndOperation(value, operation);
// If the operation is a real user of `value` the first check is sufficient.
// If not, we will have to test whether the end operation is executed before
// the given operation in the block.
return endOperation == operation || endOperation->isBeforeInBlock(operation);
}
/// Dumps the liveness information in a human readable format.
void Liveness::dump() const { print(llvm::errs()); }
/// Dumps the liveness information to the given stream.
void Liveness::print(raw_ostream &os) const {
os << "// ---- Liveness -----\n";
// Builds unique block/value mappings for testing purposes.
DenseMap<Block *, size_t> blockIds;
DenseMap<Operation *, size_t> operationIds;
DenseMap<Value *, size_t> valueIds;
for (Region &region : operation->getRegions())
for (Block &block : region) {
blockIds.insert({&block, blockIds.size()});
for (BlockArgument *argument : block.getArguments())
valueIds.insert({argument, valueIds.size()});
for (Operation &operation : block) {
operationIds.insert({&operation, operationIds.size()});
for (Value *result : operation.getResults())
valueIds.insert({result, valueIds.size()});
}
}
// Local printing helpers
auto printValueRef = [&](Value *value) {
if (Operation *defOp = value->getDefiningOp())
os << "val_" << defOp->getName();
else {
auto blockArg = cast<BlockArgument>(value);
os << "arg" << blockArg->getArgNumber() << "@"
<< blockIds[blockArg->getOwner()];
}
os << " ";
};
auto printValueRefs = [&](const ValueSetT &values) {
std::vector<Value *> orderedValues(values.begin(), values.end());
std::sort(orderedValues.begin(), orderedValues.end(),
[&](Value *left, Value *right) {
return valueIds[left] < valueIds[right];
});
for (Value *value : orderedValues)
printValueRef(value);
};
// Dump information about in and out values.
for (Region &region : operation->getRegions())
for (Block &block : region) {
os << "// - Block: " << blockIds[&block] << "\n";
auto liveness = getLiveness(&block);
os << "// --- LiveIn: ";
printValueRefs(liveness->inValues);
os << "\n// --- LiveOut: ";
printValueRefs(liveness->outValues);
os << "\n";
// Print liveness intervals.
os << "// --- BeginLiveness";
for (Operation &op : block) {
if (op.getNumResults() < 1)
continue;
os << "\n";
for (Value *result : op.getResults()) {
os << "// ";
printValueRef(result);
os << ":";
auto liveOperations = resolveLiveness(result);
std::sort(liveOperations.begin(), liveOperations.end(),
[&](Operation *left, Operation *right) {
return operationIds[left] < operationIds[right];
});
for (Operation *operation : liveOperations) {
os << "\n// ";
operation->print(os);
}
}
}
os << "\n// --- EndLiveness\n";
}
os << "// -------------------\n";
}
//===----------------------------------------------------------------------===//
// LivenessBlockInfo
//===----------------------------------------------------------------------===//
/// Returns true if the given value is in the live-in set.
bool LivenessBlockInfo::isLiveIn(Value *value) const {
return inValues.count(value);
}
/// Returns true if the given value is in the live-out set.
bool LivenessBlockInfo::isLiveOut(Value *value) const {
return outValues.count(value);
}
/// Gets the start operation for the given value
/// (must be referenced in this block).
Operation *LivenessBlockInfo::getStartOperation(Value *value) const {
Operation *definingOp = value->getDefiningOp();
// The given value is either live-in or is defined
// in the scope of this block.
if (isLiveIn(value) || !definingOp)
return &block->front();
return definingOp;
}
/// Gets the end operation for the given value using the start operation
/// provided (must be referenced in this block).
Operation *LivenessBlockInfo::getEndOperation(Value *value,
Operation *startOperation) const {
// The given value is either dying in this block or live-out.
if (isLiveOut(value))
return &block->back();
// Resolve the last operation (must exist by definition).
Operation *endOperation = startOperation;
for (OpOperand &use : value->getUses()) {
Operation *useOperation = use.getOwner();
// Check whether the use is in our block and after
// the current end operation.
if (useOperation->getBlock() == block &&
endOperation->isBeforeInBlock(useOperation))
endOperation = useOperation;
}
return endOperation;
}

View File

@ -0,0 +1,191 @@
// RUN: mlir-opt %s -test-print-liveness -split-input-file 2>&1 | FileCheck %s --dump-input-on-failure
// CHECK-LABEL: Testing : func_empty
func @func_empty() {
// CHECK: Block: 0
// CHECK-NEXT: LiveIn:{{ *$}}
// CHECK-NEXT: LiveOut:{{ *$}}
// CHECK-NEXT: BeginLiveness
// CHECK-NEXT: EndLiveness
return
}
// -----
// CHECK-LABEL: Testing : func_simpleBranch
func @func_simpleBranch(%arg0: i32, %arg1 : i32) -> i32 {
// CHECK: Block: 0
// CHECK-NEXT: LiveIn:{{ *$}}
// CHECK-NEXT: LiveOut: arg0@0 arg1@0
// CHECK-NEXT: BeginLiveness
// CHECK-NEXT: EndLiveness
br ^exit
^exit:
// CHECK: Block: 1
// CHECK-NEXT: LiveIn: arg0@0 arg1@0
// CHECK-NEXT: LiveOut:{{ *$}}
// CHECK-NEXT: BeginLiveness
// CHECK: val_std.addi
// CHECK-NEXT: %0 = addi
// CHECK-NEXT: return
// CHECK-NEXT: EndLiveness
%result = addi %arg0, %arg1 : i32
return %result : i32
}
// -----
// CHECK-LABEL: Testing : func_condBranch
func @func_condBranch(%cond : i1, %arg1: i32, %arg2 : i32) -> i32 {
// CHECK: Block: 0
// CHECK-NEXT: LiveIn:{{ *$}}
// CHECK-NEXT: LiveOut: arg1@0 arg2@0
// CHECK-NEXT: BeginLiveness
// CHECK-NEXT: EndLiveness
cond_br %cond, ^bb1, ^bb2
^bb1:
// CHECK: Block: 1
// CHECK-NEXT: LiveIn: arg1@0 arg2@0
// CHECK-NEXT: LiveOut: arg1@0 arg2@0
br ^exit
^bb2:
// CHECK: Block: 2
// CHECK-NEXT: LiveIn: arg1@0 arg2@0
// CHECK-NEXT: LiveOut: arg1@0 arg2@0
br ^exit
^exit:
// CHECK: Block: 3
// CHECK-NEXT: LiveIn: arg1@0 arg2@0
// CHECK-NEXT: LiveOut:{{ *$}}
// CHECK-NEXT: BeginLiveness
// CHECK: val_std.addi
// CHECK-NEXT: %0 = addi
// CHECK-NEXT: return
// CHECK-NEXT: EndLiveness
%result = addi %arg1, %arg2 : i32
return %result : i32
}
// -----
// CHECK-LABEL: Testing : func_loop
func @func_loop(%arg0 : i32, %arg1 : i32) -> i32 {
// CHECK: Block: 0
// CHECK-NEXT: LiveIn:{{ *$}}
// CHECK-NEXT: LiveOut: arg1@0
%const0 = constant 0 : i32
br ^loopHeader(%const0, %arg0 : i32, i32)
^loopHeader(%counter : i32, %i : i32):
// CHECK: Block: 1
// CHECK-NEXT: LiveIn: arg1@0
// CHECK-NEXT: LiveOut: arg1@0 arg0@1
// CHECK-NEXT: BeginLiveness
// CHECK-NEXT: val_std.cmpi
// CHECK-NEXT: %2 = cmpi
// CHECK-NEXT: cond_br
// CHECK-NEXT: EndLiveness
%lessThan = cmpi "slt", %counter, %arg1 : i32
cond_br %lessThan, ^loopBody(%i : i32), ^exit(%i : i32)
^loopBody(%val : i32):
// CHECK: Block: 2
// CHECK-NEXT: LiveIn: arg1@0 arg0@1
// CHECK-NEXT: LiveOut: arg1@0
// CHECK-NEXT: BeginLiveness
// CHECK-NEXT: val_std.constant
// CHECK-NEXT: %c
// CHECK-NEXT: %4 = addi
// CHECK-NEXT: %5 = addi
// CHECK-NEXT: val_std.addi
// CHECK-NEXT: %4 = addi
// CHECK-NEXT: %5 = addi
// CHECK-NEXT: br
// CHECK: EndLiveness
%const1 = constant 1 : i32
%inc = addi %val, %const1 : i32
%inc2 = addi %counter, %const1 : i32
br ^loopHeader(%inc, %inc2 : i32, i32)
^exit(%sum : i32):
// CHECK: Block: 3
// CHECK-NEXT: LiveIn: arg1@0
// CHECK-NEXT: LiveOut:{{ *$}}
%result = addi %sum, %arg1 : i32
return %result : i32
}
// -----
// CHECK-LABEL: Testing : func_ranges
func @func_ranges(%cond : i1, %arg1 : i32, %arg2 : i32, %arg3 : i32) -> i32 {
// CHECK: Block: 0
// CHECK-NEXT: LiveIn:{{ *$}}
// CHECK-NEXT: LiveOut: arg2@0 val_std.muli val_std.addi
// CHECK-NEXT: BeginLiveness
// CHECK-NEXT: val_std.addi
// CHECK-NEXT: %0 = addi
// CHECK-NEXT: %c
// CHECK-NEXT: %1 = addi
// CHECK-NEXT: %2 = addi
// CHECK-NEXT: %3 = muli
// CHECK-NEXT: val_std.constant
// CHECK-NEXT: %c
// CHECK-NEXT: %1 = addi
// CHECK-NEXT: %2 = addi
// CHECK-NEXT: %3 = muli
// CHECK-NEXT: %4 = muli
// CHECK-NEXT: %5 = addi
// CHECK-NEXT: val_std.addi
// CHECK-NEXT: %1 = addi
// CHECK-NEXT: %2 = addi
// CHECK-NEXT: %3 = muli
// CHECK-NEXT: val_std.addi
// CHECK-NEXT %2 = addi
// CHECK-NEXT %3 = muli
// CHECK-NEXT %4 = muli
// CHECK: val_std.muli
// CHECK-NEXT: %3 = muli
// CHECK-NEXT: %4 = muli
// CHECK-NEXT: val_std.muli
// CHECK-NEXT: %4 = muli
// CHECK-NEXT: %5 = addi
// CHECK-NEXT: cond_br
// CHECK-NEXT: %c
// CHECK-NEXT: %6 = muli
// CHECK-NEXT: %7 = muli
// CHECK-NEXT: %8 = addi
// CHECK-NEXT: val_std.addi
// CHECK-NEXT: %5 = addi
// CHECK-NEXT: cond_br
// CHECK-NEXT: %7
// CHECK: EndLiveness
%0 = addi %arg1, %arg2 : i32
%const1 = constant 1 : i32
%1 = addi %const1, %arg2 : i32
%2 = addi %const1, %arg3 : i32
%3 = muli %0, %1 : i32
%4 = muli %3, %2 : i32
%5 = addi %4, %const1 : i32
cond_br %cond, ^bb1, ^bb2
^bb1:
// CHECK: Block: 1
// CHECK-NEXT: LiveIn: arg2@0 val_std.muli
// CHECK-NEXT: LiveOut: arg2@0
%const4 = constant 4 : i32
%6 = muli %4, %const4 : i32
br ^exit(%6 : i32)
^bb2:
// CHECK: Block: 2
// CHECK-NEXT: LiveIn: arg2@0 val_std.muli val_std.addi
// CHECK-NEXT: LiveOut: arg2@0
%7 = muli %4, %5 : i32
%8 = addi %4, %arg2 : i32
br ^exit(%8 : i32)
^exit(%sum : i32):
// CHECK: Block: 3
// CHECK-NEXT: LiveIn: arg2@0
// CHECK-NEXT: LiveOut:{{ *$}}
%result = addi %sum, %arg2 : i32
return %result : i32
}

View File

@ -4,6 +4,7 @@ add_llvm_library(MLIRTestTransforms
TestLoopFusion.cpp
TestInlining.cpp
TestLinalgTransforms.cpp
TestLiveness.cpp
TestLoopMapping.cpp
TestLoopParametricTiling.cpp
TestOpaqueLoc.cpp

View File

@ -0,0 +1,42 @@
//===- TestLiveness.cpp - Test liveness construction and information
//-------===//
//
// Copyright 2019 The MLIR Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// =============================================================================
//
// This file contains test passes for constructing and resolving liveness
// information.
//
//===----------------------------------------------------------------------===//
#include "mlir/Analysis/Liveness.h"
#include "mlir/Pass/Pass.h"
using namespace mlir;
namespace {
struct TestLivenessPass : public FunctionPass<TestLivenessPass> {
void runOnFunction() override {
llvm::errs() << "Testing : " << getFunction().getName() << "\n";
getAnalysis<Liveness>().print(llvm::errs());
}
};
} // end anonymous namespace
static PassRegistration<TestLivenessPass>
pass("test-print-liveness",
"Print the contents of a constructed liveness information.");