Files
clang-p2996/mlir/lib/Quantizer/Support/ConstraintAnalysisGraph.cpp
Stella Laurenzo 79265887ff Upstreaming Quantizer tool (part 2).
This adds some additional core types and utilities, notably the constraint analysis graph (CAG) structures, associated metadata and configuration policy object base class.

    The CAG is not particularly memory efficient as it stands now. I had started some work to turn it into a form that could be better managed by a bump pointer allocator but abandoned that for now in favor of having something that does semantically what I was going for as a starting point.

--

PiperOrigin-RevId: 248413133
2019-05-20 13:43:31 -07:00

182 lines
5.7 KiB
C++

//===- ConstraintAnalysisGraph.cpp - Graphs type for constraints ----------===//
//
// 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.
// =============================================================================
#include "mlir/Quantizer/Support/ConstraintAnalysisGraph.h"
#include "mlir/IR/MLIRContext.h"
#include "mlir/Quantizer/Support/Configuration.h"
#include "llvm/Support/raw_ostream.h"
using namespace mlir;
using namespace mlir::quantizer;
void CAGNode::replaceIncoming(CAGNode *otherNode) {
if (this == otherNode)
return;
for (CAGNode *parentNode : incoming) {
for (CAGNode *&it : parentNode->outgoing) {
if (it == this) {
it = otherNode;
otherNode->incoming.push_back(parentNode);
}
}
}
incoming.clear();
}
void CAGNode::addOutgoing(CAGNode *toNode) {
if (!llvm::is_contained(outgoing, toNode)) {
outgoing.push_back(toNode);
toNode->incoming.push_back(this);
}
}
CAGOperandAnchor::CAGOperandAnchor(Operation *op, unsigned operandIdx)
: CAGAnchorNode(Kind::OperandAnchor, op->getOperand(operandIdx)->getType()),
op(op), operandIdx(operandIdx) {}
CAGResultAnchor::CAGResultAnchor(Operation *op, unsigned resultIdx)
: CAGAnchorNode(Kind::ResultAnchor, op->getResult(resultIdx)->getType()),
resultValue(op->getResult(resultIdx)) {}
CAGSlice::CAGSlice(SolverContext &context) : context(context) {}
CAGSlice::~CAGSlice() { llvm::DeleteContainerPointers(allNodes); }
CAGOperandAnchor *CAGSlice::getOperandAnchor(Operation *op,
unsigned operandIdx) {
assert(operandIdx < op->getNumOperands() && "illegal operand index");
// Dedup.
auto key = std::make_pair(op, operandIdx);
auto foundIt = operandAnchors.find(key);
if (foundIt != operandAnchors.end()) {
return foundIt->second;
}
// Create.
auto anchor = llvm::make_unique<CAGOperandAnchor>(op, operandIdx);
auto *unowned = anchor.release();
unowned->nodeId = allNodes.size();
allNodes.push_back(unowned);
operandAnchors.insert(std::make_pair(key, unowned));
return unowned;
}
CAGResultAnchor *CAGSlice::getResultAnchor(Operation *op, unsigned resultIdx) {
assert(resultIdx < op->getNumResults() && "illegal result index");
// Dedup.
auto key = std::make_pair(op, resultIdx);
auto foundIt = resultAnchors.find(key);
if (foundIt != resultAnchors.end()) {
return foundIt->second;
}
// Create.
auto anchor = llvm::make_unique<CAGResultAnchor>(op, resultIdx);
auto *unowned = anchor.release();
unowned->nodeId = allNodes.size();
allNodes.push_back(unowned);
resultAnchors.insert(std::make_pair(key, unowned));
return unowned;
}
void CAGSlice::enumerateImpliedConnections(
std::function<void(CAGAnchorNode *from, CAGAnchorNode *to)> callback) {
// Discover peer identity pairs (i.e. implied edges from Result->Operand and
// Arg->Call). Use an intermediate vector so that the callback can modify.
std::vector<std::pair<CAGAnchorNode *, CAGAnchorNode *>> impliedPairs;
for (auto &resultAnchorPair : resultAnchors) {
CAGResultAnchor *resultAnchor = resultAnchorPair.second;
Value *resultValue = resultAnchor->getValue();
for (auto &use : resultValue->getUses()) {
Operation *operandOp = use.getOwner();
unsigned operandIdx = use.getOperandNumber();
auto foundIt = operandAnchors.find(std::make_pair(operandOp, operandIdx));
if (foundIt != operandAnchors.end()) {
impliedPairs.push_back(std::make_pair(resultAnchor, foundIt->second));
}
}
}
// Callback for each pair.
for (auto &impliedPair : impliedPairs) {
callback(impliedPair.first, impliedPair.second);
}
}
unsigned CAGSlice::propagate(const TargetConfiguration &config) {
std::vector<CAGNode *> dirtyNodes;
dirtyNodes.reserve(allNodes.size());
// Note that because iteration happens in nodeId order, there is no need
// to sort in order to make deterministic. If the selection method changes,
// a sort should be explicitly done.
for (CAGNode *child : *this) {
if (child->isDirty()) {
dirtyNodes.push_back(child);
}
}
if (dirtyNodes.empty()) {
return 0;
}
for (auto dirtyNode : dirtyNodes) {
dirtyNode->clearDirty();
dirtyNode->propagate(context, config);
}
return dirtyNodes.size();
}
void CAGAnchorNode::propagate(SolverContext &solverContext,
const TargetConfiguration &config) {
for (CAGNode *child : *this) {
child->markDirty();
}
}
Type CAGAnchorNode::getTransformedType() {
if (!getUniformMetadata().selectedType) {
return nullptr;
}
return getUniformMetadata().selectedType.castFromExpressedType(
getOriginalType());
}
void CAGNode::printLabel(llvm::raw_ostream &os) const {
os << "Node<" << static_cast<const void *>(this) << ">";
}
void CAGAnchorNode::printLabel(llvm::raw_ostream &os) const {
getUniformMetadata().printSummary(os);
}
void CAGOperandAnchor::printLabel(llvm::raw_ostream &os) const {
os << "Operand<";
op->getName().print(os);
os << "," << operandIdx;
os << ">";
CAGAnchorNode::printLabel(os);
}
void CAGResultAnchor::printLabel(llvm::raw_ostream &os) const {
os << "Result<";
getOp()->getName().print(os);
os << ">";
CAGAnchorNode::printLabel(os);
}