blob: 9237998739efda36adc1b3d52f034f4bbae919d7 [file] [log] [blame]
// Copyright 2021 The IREE Authors
//
// Licensed 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
#ifndef IREE_COMPILER_DIALECT_IREE_UTIL_ANALYSIS_CONSTANT_CONST_EXPR_H_
#define IREE_COMPILER_DIALECT_IREE_UTIL_ANALYSIS_CONSTANT_CONST_EXPR_H_
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
#include "mlir/IR/Operation.h"
#include "mlir/Support/LLVM.h"
namespace mlir {
namespace iree_compiler {
namespace IREE {
namespace Util {
// Analyzes an entire module to determine all operations/values that are
// purely derived from constants or immutable data and builds a
// dependency tree.
//
// Modifying any of the analyzed operations invalidates this analysis.
class ConstExprAnalysis {
public:
struct ConstValueInfo;
explicit ConstExprAnalysis(Operation *rootOp);
void print(raw_ostream &os) const;
void dump() const;
// Returns const-expr info for an operation (or nullptr if unknown).
const ConstValueInfo *lookup(Value queryValue) const {
return constInfoMap.lookup(queryValue);
}
// Return const-expr info for an operation (or nullptr if unknown). Presently,
// an operation's results will either all be const-expr or not, so we just
// check the first. 0-result ops cannot be const-expr.
const ConstValueInfo *lookup(Operation *queryOp) const {
if (queryOp->getNumResults() == 0) return nullptr;
return lookup(queryOp->getResult(0));
}
// Returns true if the given value is only derived from immutable inputs.
// Note that this only returns true for derived values. Direct use of
// existing constants returns false.
bool isConstExprValue(Value queryValue) const {
ConstValueInfo *found = constInfoMap.lookup(queryValue);
if (!found) return false;
return found->state == ConstValueInfo::CONSTANT && !found->isRoot;
}
// Returns whether the given operation is considered const-expr. Presently,
// an operation's results will either all be const-expr or not, so we just
// check the first. 0-result ops cannot be const-expr.
bool isConstExprOperation(Operation *queryOp) const {
if (queryOp->getNumResults() == 0) return false;
return isConstExprValue(queryOp->getResult(0));
}
// Populates a set, in arbitrary order, of all const-expr ops in the
// program. This includes root ops.
void populateConstExprOperations(llvm::DenseSet<Operation *> &ops) const {
for (auto it : constInfoMap) {
ConstValueInfo *info = it.second;
if (info->state == ConstValueInfo::CONSTANT) {
Operation *definingOp = info->constValue.getDefiningOp();
assert(definingOp && "const-expr values must have a defining op");
ops.insert(definingOp);
}
}
}
// Map of a root value in the program that should be considered constant
// to the operation that defines the constant. Two cases:
// LoadGlobalOp.result -> GlobalOp
// ConstantOp.result -> ConstantOp
// Entries can come from the whole program.
using ConstRootMap = llvm::DenseMap<Value, Operation *>;
// Information about a Value that is has been analyzed.
struct ConstValueInfo {
ConstValueInfo(Value constValue) : constValue(constValue) {}
// UNKNOWN: Not all producers have been validated.
// CONSTANT: Producers have all been validated as constants.
// NON_CONSTANT: The op is not eligible to be treated as a constant or
// one or more producers is non constant.
enum State { UNKNOWN, CONSTANT, NON_CONSTANT };
State state = UNKNOWN;
// The presumed constant value.
Value constValue;
// Root values (in ConstRootMap) that this value (indirectly) derives from.
SmallPtrSet<Value, 4> roots;
// Direct producers that feed into this constant value.
SmallVector<ConstValueInfo *> producers;
// Whether this is a root.
bool isRoot = false;
// Whether this is a const-expr value.
bool isConstExpr() const { return state == CONSTANT; }
Operation *getOperation() const {
Operation *ret = constValue.getDefiningOp();
assert(ret && "const-expr must have a defining op");
return ret;
}
};
private:
// Expands the frontier to include all results of a given op in an UNKNOWN
// state. This also checks that all of its operands are known, adding
// them recusrively if not.
void expandToOp(Operation *op);
// Add a new info record for a value to analyze for const-ness.
ConstValueInfo *addInfo(Value constValue);
// Map of a root value in the program that should be considered constant
// to the operation that defines the constant. Two cases:
// LoadGlobalOp.result -> GlobalOp
// ConstantOp.result -> ConstantOp
// Entries can come from the whole program.
llvm::DenseMap<Value, Operation *> constantRoots;
// Map of analyzed value to corresponding info struct.
llvm::DenseMap<Value, ConstValueInfo *> constInfoMap;
// Allocated ConstValueInfo structs (to preserve pointer stability).
llvm::SmallVector<std::unique_ptr<ConstValueInfo>> allocedConstInfos;
// Worklist of const value info structs which need more resolution.
using ConstValueWorklist = llvm::SmallVector<ConstValueInfo *>;
ConstValueWorklist worklist;
};
inline raw_ostream &operator<<(raw_ostream &os,
const ConstExprAnalysis &analysis) {
analysis.print(os);
return os;
}
} // namespace Util
} // namespace IREE
} // namespace iree_compiler
} // namespace mlir
#endif // IREE_COMPILER_DIALECT_IREE_UTIL_ANALYSIS_CONSTANT_CONST_EXPR_H_