llvm-project/mlir/lib/IR/Instructions.cpp

357 lines
13 KiB
C++

//===- Instructions.cpp - MLIR CFGFunction Instruction Classes ------------===//
//
// 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/IR/Instructions.h"
#include "mlir/IR/CFGFunction.h"
#include "mlir/IR/MLIRContext.h"
using namespace mlir;
/// Replace all uses of 'this' value with the new value, updating anything in
/// the IR that uses 'this' to use the other value instead. When this returns
/// there are zero uses of 'this'.
void IRObjectWithUseList::replaceAllUsesWith(IRObjectWithUseList *newValue) {
assert(this != newValue && "cannot RAUW a value with itself");
while (!use_empty()) {
use_begin()->set(newValue);
}
}
/// Return the result number of this result.
unsigned InstResult::getResultNumber() const {
// Results are always stored consecutively, so use pointer subtraction to
// figure out what number this is.
return this - &getOwner()->getInstResults()[0];
}
//===----------------------------------------------------------------------===//
// Instruction
//===----------------------------------------------------------------------===//
// Instructions are deleted through the destroy() member because we don't have
// a virtual destructor.
Instruction::~Instruction() {
assert(block == nullptr && "instruction destroyed but still in a block");
}
/// Destroy this instruction or one of its subclasses.
void Instruction::destroy() {
switch (getKind()) {
case Kind::Operation:
cast<OperationInst>(this)->destroy();
break;
}
}
void OperationInst::destroy() {
this->~OperationInst();
free(this);
}
CFGFunction *Instruction::getFunction() {
auto *block = getBlock();
return block ? block->getFunction() : nullptr;
}
unsigned Instruction::getNumOperands() const {
switch (getKind()) {
case Kind::Operation:
return cast<OperationInst>(this)->getNumOperands();
}
}
MutableArrayRef<InstOperand> Instruction::getInstOperands() {
switch (getKind()) {
case Kind::Operation:
return cast<OperationInst>(this)->getInstOperands();
}
}
/// This drops all operand uses from this instruction, which is an essential
/// step in breaking cyclic dependences between references when they are to
/// be deleted.
void Instruction::dropAllReferences() {
for (auto &op : getInstOperands())
op.drop();
if (OperationInst *opInst = dyn_cast<OperationInst>(this)) {
if (opInst->isTerminator())
for (auto &dest : opInst->getBasicBlockOperands())
dest.drop();
}
}
/// Emit a note about this instruction, reporting up to any diagnostic
/// handlers that may be listening.
void Instruction::emitNote(const Twine &message) const {
getContext()->emitDiagnostic(getLoc(), message,
MLIRContext::DiagnosticKind::Note);
}
/// Emit a warning about this operation, reporting up to any diagnostic
/// handlers that may be listening.
void Instruction::emitWarning(const Twine &message) const {
getContext()->emitDiagnostic(getLoc(), message,
MLIRContext::DiagnosticKind::Warning);
}
/// Emit an error about fatal conditions with this instruction, reporting up to
/// any diagnostic handlers that may be listening. NOTE: This may terminate
/// the containing application, only use when the IR is in an inconsistent
/// state.
void Instruction::emitError(const Twine &message) const {
getContext()->emitDiagnostic(getLoc(), message,
MLIRContext::DiagnosticKind::Error);
}
//===----------------------------------------------------------------------===//
// OperationInst
//===----------------------------------------------------------------------===//
/// Create a new OperationInst with the specified fields.
OperationInst *OperationInst::create(Location location, OperationName name,
ArrayRef<CFGValue *> operands,
ArrayRef<Type> resultTypes,
ArrayRef<NamedAttribute> attributes,
ArrayRef<BasicBlock *> successors,
MLIRContext *context) {
unsigned numSuccessors = successors.size();
auto byteSize = totalSizeToAlloc<InstResult, BasicBlockOperand, unsigned>(
resultTypes.size(), numSuccessors, numSuccessors);
void *rawMem = malloc(byteSize);
// Initialize the OperationInst part of the instruction.
auto inst = ::new (rawMem) OperationInst(location, name, resultTypes.size(),
numSuccessors, attributes, context);
// Initialize the results and operands.
auto instResults = inst->getInstResults();
for (unsigned i = 0, e = resultTypes.size(); i != e; ++i)
new (&instResults[i]) InstResult(resultTypes[i], inst);
unsigned operandIt = 0, operandE = operands.size();
for (; operandIt != operandE; ++operandIt) {
// Null operands are used as sentinals between successor operand lists. If
// we encounter one here, break and handle the successor operands lists
// separately below.
if (!operands[operandIt])
break;
inst->operands.push_back(InstOperand(inst, operands[operandIt]));
}
// Check to see if a sentinal operand was encountered.
unsigned currentSuccNum = 0;
if (operandIt != operandE) {
assert(inst->isTerminator() &&
"Sentinal operand found in non terminator operand list.");
auto instBlockOperands = inst->getBasicBlockOperands();
unsigned *succOperandCountIt = inst->getTrailingObjects<unsigned>();
unsigned *succOperandCountE = succOperandCountIt + numSuccessors;
(void)succOperandCountE;
for (; operandIt != operandE; ++operandIt) {
// If we encounter a sentinal branch to the next operand update the count
// variable.
if (!operands[operandIt]) {
assert(currentSuccNum < numSuccessors);
// After the first iteration update the successor operand count
// variable.
if (currentSuccNum != 0) {
++succOperandCountIt;
assert(succOperandCountIt != succOperandCountE &&
"More sentinal operands than successors.");
}
new (&instBlockOperands[currentSuccNum])
BasicBlockOperand(inst, successors[currentSuccNum]);
*succOperandCountIt = 0;
++currentSuccNum;
continue;
}
inst->operands.push_back(InstOperand(inst, operands[operandIt]));
++(*succOperandCountIt);
}
}
// Verify that the amount of sentinal operands is equivalent to the number of
// successors.
assert(currentSuccNum == numSuccessors);
return inst;
}
OperationInst *OperationInst::clone() const {
SmallVector<CFGValue *, 8> operands;
SmallVector<Type, 8> resultTypes;
SmallVector<BasicBlock *, 1> successors;
// Put together the results.
for (auto *result : getResults())
resultTypes.push_back(result->getType());
// If the instruction is a terminator the successor and non-successor operand
// lists are interleaved with sentinal(nullptr) operands.
if (isTerminator()) {
// To interleave the operand lists we iterate in reverse and insert the
// operands in-place.
operands.resize(getNumOperands() + getNumSuccessors());
successors.resize(getNumSuccessors());
int cloneOperandIt = operands.size() - 1, operandIt = getNumOperands() - 1;
for (int succIt = getNumSuccessors() - 1, succE = 0; succIt >= succE;
--succIt) {
successors[succIt] = getSuccessor(succIt);
// Add the successor operands in-place in reverse order.
for (unsigned i = 0, e = getNumSuccessorOperands(succIt); i != e;
++i, --cloneOperandIt, --operandIt) {
operands[cloneOperandIt] =
const_cast<CFGValue *>(getOperand(operandIt));
}
// Add a null operand for the barrier.
operands[cloneOperandIt--] = nullptr;
}
// Add the rest of the non-successor operands.
for (; cloneOperandIt >= 0; --cloneOperandIt, --operandIt)
operands[cloneOperandIt] = const_cast<CFGValue *>(getOperand(operandIt));
// For non terminators we can simply add each of the instructions in place.
} else {
for (auto *operand : getOperands())
operands.push_back(const_cast<CFGValue *>(operand));
}
return create(getLoc(), getName(), operands, resultTypes, getAttrs(),
successors, getContext());
}
OperationInst::OperationInst(Location location, OperationName name,
unsigned numResults, unsigned numSuccessors,
ArrayRef<NamedAttribute> attributes,
MLIRContext *context)
: Operation(/*isInstruction=*/true, name, attributes, context),
Instruction(Kind::Operation, location), numResults(numResults),
numSuccs(numSuccessors) {}
OperationInst::~OperationInst() {
// Explicitly run the destructors for the results and successors.
for (auto &result : getInstResults())
result.~InstResult();
if (isTerminator())
for (auto &successor : getBasicBlockOperands())
successor.~BasicBlockOperand();
}
void OperationInst::addSuccessorOperand(unsigned index, CFGValue *value) {
assert(isTerminator() && "Only terminators have successors.");
assert(index < getNumSuccessors());
assert(std::accumulate(getTrailingObjects<unsigned>() + index + 1,
getTrailingObjects<unsigned>() + numSuccs, 0u) == 0 &&
"All successor operands must be added before moving to the next.");
operands.push_back(InstOperand(this, value));
++getTrailingObjects<unsigned>()[index];
}
void OperationInst::addSuccessorOperands(unsigned index,
ArrayRef<CFGValue *> values) {
operands.reserve(operands.size() + values.size());
for (auto *value : values)
addSuccessorOperand(index, value);
}
void llvm::ilist_traits<::mlir::OperationInst>::deleteNode(
OperationInst *inst) {
inst->destroy();
}
mlir::BasicBlock *
llvm::ilist_traits<::mlir::OperationInst>::getContainingBlock() {
size_t Offset(
size_t(&((BasicBlock *)nullptr->*BasicBlock::getSublistAccess(nullptr))));
iplist<OperationInst> *Anchor(static_cast<iplist<OperationInst> *>(this));
return reinterpret_cast<BasicBlock *>(reinterpret_cast<char *>(Anchor) -
Offset);
}
/// This is a trait method invoked when an instruction is added to a block. We
/// keep the block pointer up to date.
void llvm::ilist_traits<::mlir::OperationInst>::addNodeToList(
OperationInst *inst) {
assert(!inst->getBlock() && "already in a basic block!");
inst->block = getContainingBlock();
}
/// This is a trait method invoked when an instruction is removed from a block.
/// We keep the block pointer up to date.
void llvm::ilist_traits<::mlir::OperationInst>::removeNodeFromList(
OperationInst *inst) {
assert(inst->block && "not already in a basic block!");
inst->block = nullptr;
}
/// This is a trait method invoked when an instruction is moved from one block
/// to another. We keep the block pointer up to date.
void llvm::ilist_traits<::mlir::OperationInst>::transferNodesFromList(
ilist_traits<OperationInst> &otherList, instr_iterator first,
instr_iterator last) {
// If we are transferring instructions within the same basic block, the block
// pointer doesn't need to be updated.
BasicBlock *curParent = getContainingBlock();
if (curParent == otherList.getContainingBlock())
return;
// Update the 'block' member of each instruction.
for (; first != last; ++first)
first->block = curParent;
}
/// Unlink this instruction from its BasicBlock and delete it.
void OperationInst::erase() {
assert(getBlock() && "Instruction has no parent");
// TODO(riverriddle) Remove this when terminators are a part of the operations
// list.
if (isTerminator()) {
getBlock()->setTerminator(nullptr);
destroy();
} else {
getBlock()->getOperations().erase(this);
}
}
/// Unlink this operation instruction from its current basic block and insert
/// it right before `existingInst` which may be in the same or another block
/// in the same function.
void OperationInst::moveBefore(OperationInst *existingInst) {
assert(existingInst && "Cannot move before a null instruction");
// TODO(riverriddle) Remove this when terminators are a part of the operations
// list.
if (existingInst->isTerminator()) {
return moveBefore(existingInst->getBlock(),
existingInst->getBlock()->end());
}
return moveBefore(existingInst->getBlock(), existingInst->getIterator());
}
/// Unlink this operation instruction from its current basic block and insert
/// it right before `iterator` in the specified basic block.
void OperationInst::moveBefore(BasicBlock *block,
llvm::iplist<OperationInst>::iterator iterator) {
block->getOperations().splice(iterator, getBlock()->getOperations(),
getIterator());
}