Roll-forward initial liveness analysis including test cases.
authorAlexander Belyaev <pifon@google.com>
Wed, 11 Dec 2019 16:01:56 +0000 (08:01 -0800)
committerA. Unique TensorFlower <gardener@tensorflow.org>
Wed, 11 Dec 2019 16:13:43 +0000 (08:13 -0800)
Fix the usage of the map size when appending to the map with [].

PiperOrigin-RevId: 284985916

mlir/include/mlir/Analysis/Liveness.h [new file with mode: 0644]
mlir/lib/Analysis/CMakeLists.txt
mlir/lib/Analysis/Liveness.cpp [new file with mode: 0644]
mlir/test/Analysis/test-liveness.mlir [new file with mode: 0644]
mlir/test/lib/Transforms/CMakeLists.txt
mlir/test/lib/Transforms/TestLiveness.cpp [new file with mode: 0644]

diff --git a/mlir/include/mlir/Analysis/Liveness.h b/mlir/include/mlir/Analysis/Liveness.h
new file mode 100644 (file)
index 0000000..0bdb474
--- /dev/null
@@ -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
index c16ad3f..96c2928 100644 (file)
@@ -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 (file)
index 0000000..6aaec4c
--- /dev/null
@@ -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;
+}
diff --git a/mlir/test/Analysis/test-liveness.mlir b/mlir/test/Analysis/test-liveness.mlir
new file mode 100644 (file)
index 0000000..2f46049
--- /dev/null
@@ -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
index 2d5f37a..b6338e1 100644 (file)
@@ -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 (file)
index 0000000..d970602
--- /dev/null
@@ -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.");