From: Alexander Belyaev Date: Wed, 11 Dec 2019 16:01:56 +0000 (-0800) Subject: Roll-forward initial liveness analysis including test cases. X-Git-Tag: llvmorg-11-init~1466^2~99 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=4b0198acb523ba26b8d4384f0f61ca0b755cb0c6;p=platform%2Fupstream%2Fllvm.git Roll-forward initial liveness analysis including test cases. Fix the usage of the map size when appending to the map with []. PiperOrigin-RevId: 284985916 --- diff --git a/mlir/include/mlir/Analysis/Liveness.h b/mlir/include/mlir/Analysis/Liveness.h new file mode 100644 index 0000000..0bdb474 --- /dev/null +++ b/mlir/include/mlir/Analysis/Liveness.h @@ -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 + +#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; + using BlockMapT = DenseMap; + using ValueSetT = SmallPtrSet; + +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 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 diff --git a/mlir/lib/Analysis/CMakeLists.txt b/mlir/lib/Analysis/CMakeLists.txt index c16ad3f..96c2928 100644 --- a/mlir/lib/Analysis/CMakeLists.txt +++ b/mlir/lib/Analysis/CMakeLists.txt @@ -4,6 +4,7 @@ add_llvm_library(MLIRAnalysis STATIC CallGraph.cpp Dominance.cpp InferTypeOpInterface.cpp + Liveness.cpp LoopAnalysis.cpp MemRefBoundCheck.cpp NestedMatcher.cpp diff --git a/mlir/lib/Analysis/Liveness.cpp b/mlir/lib/Analysis/Liveness.cpp new file mode 100644 index 0000000..6aaec4c --- /dev/null +++ b/mlir/lib/Analysis/Liveness.cpp @@ -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 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 regions, + DenseMap &builders) { + llvm::SetVector toProcess; + + // Initialize all block structures + for (Region ®ion : 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 regions) { + + // Build internal block mapping. + DenseMap 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 visited; + SmallVector toProcess; + + // Start with the defining block + Block *currentBlock; + if (Operation *defOp = value->getDefiningOp()) + currentBlock = defOp->getBlock(); + else + currentBlock = cast(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 blockIds; + DenseMap operationIds; + DenseMap valueIds; + for (Region ®ion : 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(value); + os << "arg" << blockArg->getArgNumber() << "@" + << blockIds[blockArg->getOwner()]; + } + os << " "; + }; + + auto printValueRefs = [&](const ValueSetT &values) { + std::vector 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 ®ion : 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; +} diff --git a/mlir/test/Analysis/test-liveness.mlir b/mlir/test/Analysis/test-liveness.mlir new file mode 100644 index 0000000..2f460497 --- /dev/null +++ b/mlir/test/Analysis/test-liveness.mlir @@ -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 +} \ No newline at end of file diff --git a/mlir/test/lib/Transforms/CMakeLists.txt b/mlir/test/lib/Transforms/CMakeLists.txt index 2d5f37a..b6338e1 100644 --- a/mlir/test/lib/Transforms/CMakeLists.txt +++ b/mlir/test/lib/Transforms/CMakeLists.txt @@ -4,6 +4,7 @@ add_llvm_library(MLIRTestTransforms TestLoopFusion.cpp TestInlining.cpp TestLinalgTransforms.cpp + TestLiveness.cpp TestLoopMapping.cpp TestLoopParametricTiling.cpp TestOpaqueLoc.cpp diff --git a/mlir/test/lib/Transforms/TestLiveness.cpp b/mlir/test/lib/Transforms/TestLiveness.cpp new file mode 100644 index 0000000..d970602 --- /dev/null +++ b/mlir/test/lib/Transforms/TestLiveness.cpp @@ -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 { + void runOnFunction() override { + llvm::errs() << "Testing : " << getFunction().getName() << "\n"; + getAnalysis().print(llvm::errs()); + } +}; + +} // end anonymous namespace + +static PassRegistration + pass("test-print-liveness", + "Print the contents of a constructed liveness information.");