Rename the Instruction class to Operation. This just renames the class, usages of Instruction will still refer to a typedef in the interim.

This is step 1/N to renaming Instruction to Operation.

PiperOrigin-RevId: 240431520
This commit is contained in:
River Riddle
2019-03-26 14:45:38 -07:00
committed by jpienaar
parent e18d8ad7fa
commit 9ffdc930c0
36 changed files with 990 additions and 1043 deletions

View File

@@ -35,7 +35,8 @@ class AffineApplyOp;
class AffineForOp;
class AffineValueMap;
class FlatAffineConstraints;
class Instruction;
class Operation;
using Instruction = Operation;
class Value;
/// Returns in `affineApplyOps`, the sequence of those AffineApplyOp

View File

@@ -31,7 +31,8 @@ namespace mlir {
class AffineExpr;
class AffineForOp;
class AffineMap;
class Instruction;
class Operation;
using Instruction = Operation;
class MemRefType;
class Value;

View File

@@ -24,7 +24,8 @@
namespace mlir {
struct NestedPattern;
class Instruction;
class Operation;
using Instruction = Operation;
/// An NestedPattern captures nested patterns in the IR.
/// It is used in conjunction with a scoped NestedPatternContext which is an

View File

@@ -27,7 +27,8 @@
namespace mlir {
class Instruction;
class Operation;
using Instruction = Operation;
/// Type of the condition to limit the propagation of transitive use-defs.
/// This can be used in particular to limit the propagation to a given Scope or

View File

@@ -38,10 +38,10 @@ namespace mlir {
class AffineForOp;
class Block;
class FlatAffineConstraints;
class Instruction;
class Location;
class MemRefAccess;
class Instruction;
class Operation;
using Instruction = Operation;
class Value;
/// Populates 'loops' with IVs of the loops surrounding 'inst' ordered from

View File

@@ -28,9 +28,10 @@ class AffineApplyOp;
class AffineForOp;
class AffineMap;
class FuncBuilder;
class Instruction;
class Location;
class MemRefType;
class Operation;
using Instruction = Operation;
class Value;
class VectorType;

View File

@@ -19,7 +19,7 @@
#define MLIR_IR_BUILDERS_H
#include "mlir/IR/Function.h"
#include "mlir/IR/Instruction.h"
#include "mlir/IR/Operation.h"
namespace mlir {

View File

@@ -25,8 +25,8 @@
#include "mlir/IR/Attributes.h"
#include "mlir/IR/Block.h"
#include "mlir/IR/Identifier.h"
#include "mlir/IR/Instruction.h"
#include "mlir/IR/Location.h"
#include "mlir/IR/Operation.h"
#include "mlir/IR/Types.h"
#include "mlir/Support/LLVM.h"
#include "llvm/ADT/ilist.h"

View File

@@ -1,171 +0,0 @@
//===- InstructionSupport.h - MLIR Instruction Utilities --------*- 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 defines utilities for the Instruction class.
//
//===----------------------------------------------------------------------===//
#ifndef MLIR_IR_INSTRUCTION_SUPPORT_H
#define MLIR_IR_INSTRUCTION_SUPPORT_H
#include "mlir/IR/OperationSupport.h"
#include "mlir/IR/Value.h"
#include "mlir/Support/LLVM.h"
#include "llvm/Support/TrailingObjects.h"
namespace mlir {
class Instruction;
namespace detail {
/// A utility class holding the information necessary to dynamically resize
/// operands.
struct ResizableStorage {
ResizableStorage(InstOperand *opBegin, unsigned numOperands)
: firstOpAndIsDynamic(opBegin, false), capacity(numOperands) {}
~ResizableStorage() { cleanupStorage(); }
/// Cleanup any allocated storage.
void cleanupStorage() {
// If the storage is dynamic, then we need to free the storage.
if (isStorageDynamic())
free(firstOpAndIsDynamic.getPointer());
}
/// Sets the storage pointer to a new dynamically allocated block.
void setDynamicStorage(InstOperand *opBegin) {
/// Cleanup the old storage if necessary.
cleanupStorage();
firstOpAndIsDynamic.setPointerAndInt(opBegin, true);
}
/// Returns the current storage pointer.
InstOperand *getPointer() { return firstOpAndIsDynamic.getPointer(); }
const InstOperand *getPointer() const {
return firstOpAndIsDynamic.getPointer();
}
/// Returns if the current storage of operands is in the trailing objects is
/// in a dynamically allocated memory block.
bool isStorageDynamic() const { return firstOpAndIsDynamic.getInt(); }
/// A pointer to the first operand element. This is either to the trailing
/// objects storage, or a dynamically allocated block of memory.
llvm::PointerIntPair<InstOperand *, 1, bool> firstOpAndIsDynamic;
// The maximum number of operands that can be currently held by the storage.
unsigned capacity;
};
/// This class handles the management of instruction operands. Operands are
/// stored similarly to the elements of a SmallVector except for two key
/// differences. The first is the inline storage, which is a trailing objects
/// array. The second is that being able to dynamically resize the operand list
/// is optional.
class OperandStorage final
: private llvm::TrailingObjects<OperandStorage, ResizableStorage,
InstOperand> {
public:
OperandStorage(unsigned numOperands, bool resizable)
: numOperands(numOperands), resizable(resizable) {
// Initialize the resizable storage.
if (resizable) {
new (&getResizableStorage())
ResizableStorage(getTrailingObjects<InstOperand>(), numOperands);
}
}
~OperandStorage() {
// Manually destruct the operands.
for (auto &operand : getInstOperands())
operand.~InstOperand();
// If the storage is resizable then destruct the utility.
if (resizable)
getResizableStorage().~ResizableStorage();
}
/// Replace the operands contained in the storage with the ones provided in
/// 'operands'.
void setOperands(Instruction *owner, ArrayRef<Value *> operands);
/// Erase an operand held by the storage.
void eraseOperand(unsigned index);
/// Get the instruction operands held by the storage.
ArrayRef<InstOperand> getInstOperands() const {
return {getRawOperands(), size()};
}
MutableArrayRef<InstOperand> getInstOperands() {
return {getRawOperands(), size()};
}
/// Return the number of operands held in the storage.
unsigned size() const { return numOperands; }
/// Returns the additional size necessary for allocating this object.
static size_t additionalAllocSize(unsigned numOperands, bool resizable) {
return additionalSizeToAlloc<ResizableStorage, InstOperand>(
resizable ? 1 : 0, numOperands);
}
/// Returns if this storage is resizable.
bool isResizable() const { return resizable; }
private:
/// Clear the storage and destroy the current operands held by the storage.
void clear() { numOperands = 0; }
/// Returns the current pointer for the raw operands array.
InstOperand *getRawOperands() {
return resizable ? getResizableStorage().getPointer()
: getTrailingObjects<InstOperand>();
}
const InstOperand *getRawOperands() const {
return resizable ? getResizableStorage().getPointer()
: getTrailingObjects<InstOperand>();
}
/// Returns the resizable operand utility class.
ResizableStorage &getResizableStorage() {
assert(resizable);
return *getTrailingObjects<ResizableStorage>();
}
const ResizableStorage &getResizableStorage() const {
assert(resizable);
return *getTrailingObjects<ResizableStorage>();
}
/// Grow the internal resizable operand storage.
void grow(ResizableStorage &resizeUtil, size_t minSize);
/// The current number of operands, and the current max operand capacity.
unsigned numOperands : 31;
/// Whether this storage is resizable or not.
bool resizable : 1;
// This stuff is used by the TrailingObjects template.
friend llvm::TrailingObjects<OperandStorage, ResizableStorage, InstOperand>;
size_t numTrailingObjects(OverloadToken<ResizableStorage>) const {
return resizable ? 1 : 0;
}
};
} // end namespace detail
} // end namespace mlir
#endif // MLIR_IR_INSTRUCTION_SUPPORT_H

View File

@@ -25,7 +25,7 @@
#define MLIR_MATCHERS_H
#include "mlir/IR/Attributes.h"
#include "mlir/IR/Instruction.h"
#include "mlir/IR/Operation.h"
#include "mlir/IR/StandardTypes.h"
#include "mlir/IR/Value.h"
#include <type_traits>

View File

@@ -28,7 +28,7 @@
#ifndef MLIR_IR_OPDEFINITION_H
#define MLIR_IR_OPDEFINITION_H
#include "mlir/IR/Instruction.h"
#include "mlir/IR/Operation.h"
#include <type_traits>
namespace mlir {
@@ -782,7 +782,7 @@ protected:
/// This is a private constructor only accessible through the
/// Instruction::cast family of methods.
explicit Op(Instruction *state) : OpState(state) {}
friend class Instruction;
friend class Operation;
private:
template <typename... Types> struct BaseVerifier;

View File

@@ -1,4 +1,4 @@
//===- Instruction.h - MLIR Instruction Class -------------------*- C++ -*-===//
//===- Operation.h - MLIR Operation Class -----------------------*- C++ -*-===//
//
// Copyright 2019 The MLIR Authors.
//
@@ -15,16 +15,16 @@
// limitations under the License.
// =============================================================================
//
// This file defines the Instruction class.
// This file defines the Operation class.
//
//===----------------------------------------------------------------------===//
#ifndef MLIR_IR_INSTRUCTION_H
#define MLIR_IR_INSTRUCTION_H
#ifndef MLIR_IR_OPERATION_H
#define MLIR_IR_OPERATION_H
#include "mlir/IR/AffineMap.h"
#include "mlir/IR/Block.h"
#include "mlir/IR/InstructionSupport.h"
#include "mlir/IR/OperationSupport.h"
#include "llvm/ADT/Twine.h"
#include "llvm/ADT/ilist.h"
#include "llvm/ADT/ilist_node.h"
@@ -40,31 +40,31 @@ class ResultTypeIterator;
/// Terminator operations can have Block operands to represent successors.
using BlockOperand = IROperandImpl<Block>;
/// Instruction is a basic unit of execution within a function. Instructions can
/// be nested within other instructions effectively forming a tree. Child
/// instructions are organized into instruction blocks represented by a 'Block'
/// Operation is a basic unit of execution within a function. Operations can
/// be nested within other operations effectively forming a tree. Child
/// operations are organized into operation blocks represented by a 'Block'
/// class.
class Instruction final
: public llvm::ilist_node_with_parent<Instruction, Block>,
private llvm::TrailingObjects<Instruction, InstResult, BlockOperand,
class Operation final
: public llvm::ilist_node_with_parent<Operation, Block>,
private llvm::TrailingObjects<Operation, InstResult, BlockOperand,
unsigned, Region, detail::OperandStorage> {
public:
/// Create a new Instruction with the specific fields.
static Instruction *create(Location location, OperationName name,
ArrayRef<Value *> operands,
ArrayRef<Type> resultTypes,
ArrayRef<NamedAttribute> attributes,
ArrayRef<Block *> successors, unsigned numRegions,
bool resizableOperandList, MLIRContext *context);
/// Create a new Operation with the specific fields.
static Operation *create(Location location, OperationName name,
ArrayRef<Value *> operands,
ArrayRef<Type> resultTypes,
ArrayRef<NamedAttribute> attributes,
ArrayRef<Block *> successors, unsigned numRegions,
bool resizableOperandList, MLIRContext *context);
/// Overload of create that takes an existing NamedAttributeList to avoid
/// unnecessarily uniquing a list of attributes.
static Instruction *create(Location location, OperationName name,
ArrayRef<Value *> operands,
ArrayRef<Type> resultTypes,
const NamedAttributeList &attributes,
ArrayRef<Block *> successors, unsigned numRegions,
bool resizableOperandList, MLIRContext *context);
static Operation *create(Location location, OperationName name,
ArrayRef<Value *> operands,
ArrayRef<Type> resultTypes,
const NamedAttributeList &attributes,
ArrayRef<Block *> successors, unsigned numRegions,
bool resizableOperandList, MLIRContext *context);
/// The name of an operation is the key identifier for it.
OperationName getName() { return name; }
@@ -75,18 +75,18 @@ public:
return getName().getAbstractOperation();
}
/// Remove this instruction from its parent block and delete it.
/// Remove this operation from its parent block and delete it.
void erase();
/// Create a deep copy of this instruction, remapping any operands that use
/// values outside of the instruction using the map that is provided (leaving
/// Create a deep copy of this operation, remapping any operands that use
/// values outside of the operation using the map that is provided (leaving
/// them alone if no entry is present). Replaces references to cloned
/// sub-instructions to the corresponding instruction that is copied, and adds
/// sub-operations to the corresponding operation that is copied, and adds
/// those mappings to the map.
Instruction *clone(BlockAndValueMapping &mapper, MLIRContext *context);
Instruction *clone(MLIRContext *context);
Operation *clone(BlockAndValueMapping &mapper, MLIRContext *context);
Operation *clone(MLIRContext *context);
/// Returns the instruction block that contains this instruction.
/// Returns the operation block that contains this operation.
Block *getBlock() { return block; }
/// Return the context this operation is associated with.
@@ -102,41 +102,41 @@ public:
/// Set the source location the operation was defined or derived from.
void setLoc(Location loc) { location = loc; }
/// Returns the closest surrounding instruction that contains this instruction
/// or nullptr if this is a top-level instruction.
Instruction *getParentInst();
/// Returns the closest surrounding operation that contains this operation
/// or nullptr if this is a top-level operation.
Operation *getParentInst();
/// Returns the function that this instruction is part of.
/// The function is determined by traversing the chain of parent instructions.
/// Returns nullptr if the instruction is unlinked.
/// Returns the function that this operation is part of.
/// The function is determined by traversing the chain of parent operations.
/// Returns nullptr if the operation is unlinked.
Function *getFunction();
/// Destroys this instruction and its subclass data.
/// Destroys this operation and its subclass data.
void destroy();
/// This drops all operand uses from this instruction, which is an essential
/// This drops all operand uses from this operation, which is an essential
/// step in breaking cyclic dependences between references when they are to
/// be deleted.
void dropAllReferences();
/// Drop uses of all values defined by this instruction or its nested regions.
/// Drop uses of all values defined by this operation or its nested regions.
void dropAllDefinedValueUses();
/// Unlink this instruction from its current block and insert it right before
/// Unlink this operation from its current block and insert it right before
/// `existingInst` which may be in the same or another block in the same
/// function.
void moveBefore(Instruction *existingInst);
void moveBefore(Operation *existingInst);
/// Unlink this operation instruction from its current block and insert it
/// Unlink this operation operation from its current block and insert it
/// right before `iterator` in the specified block.
void moveBefore(Block *block, llvm::iplist<Instruction>::iterator iterator);
void moveBefore(Block *block, llvm::iplist<Operation>::iterator iterator);
/// Given an instruction 'other' that is within the same parent block, return
/// whether the current instruction is before 'other' in the instruction list
/// Given an operation 'other' that is within the same parent block, return
/// whether the current operation is before 'other' in the operation list
/// of the parent block.
/// Note: This function has an average complexity of O(1), but worst case may
/// take O(N) where N is the number of instructions within the parent block.
bool isBeforeInBlock(Instruction *other);
/// take O(N) where N is the number of operations within the parent block.
bool isBeforeInBlock(Operation *other);
void print(raw_ostream &os);
void dump();
@@ -212,11 +212,11 @@ public:
// Attributes
//===--------------------------------------------------------------------===//
// Instructions may optionally carry a list of attributes that associate
// Operations may optionally carry a list of attributes that associate
// constants to names. Attributes may be dynamically added and removed over
// the lifetime of an instruction.
// the lifetime of an operation.
/// Return all of the attributes on this instruction.
/// Return all of the attributes on this operation.
ArrayRef<NamedAttribute> getAttrs() { return attrs.getAttrs(); }
/// Return the specified attribute if present, null otherwise.
@@ -369,7 +369,7 @@ public:
// Conversions to declared operations like DimOp
//===--------------------------------------------------------------------===//
/// The dyn_cast methods perform a dynamic cast from an Instruction to a typed
/// The dyn_cast methods perform a dynamic cast from an Operation to a typed
/// Op like DimOp. This returns a null Op on failure.
template <typename OpClass> OpClass dyn_cast() {
if (isa<OpClass>())
@@ -377,7 +377,7 @@ public:
return OpClass();
}
/// The cast methods perform a cast from an Instruction to a typed Op like
/// The cast methods perform a cast from an Operation to a typed Op like
/// DimOp. This aborts if the parameter to the template isn't an instance of
/// the template type argument.
template <typename OpClass> OpClass cast() {
@@ -390,31 +390,31 @@ public:
template <typename OpClass> bool isa() { return OpClass::isClassFor(this); }
//===--------------------------------------------------------------------===//
// Instruction Walkers
// Operation Walkers
//===--------------------------------------------------------------------===//
/// Walk the instructions held by this instruction in preorder, calling the
/// callback for each instruction.
void walk(const std::function<void(Instruction *)> &callback);
/// Walk the operations held by this operation in preorder, calling the
/// callback for each operation.
void walk(const std::function<void(Operation *)> &callback);
/// Specialization of walk to only visit operations of 'OpTy'.
template <typename OpTy> void walk(std::function<void(OpTy)> callback) {
walk([&](Instruction *inst) {
if (auto op = inst->dyn_cast<OpTy>())
callback(op);
walk([&](Operation *op) {
if (auto derivedOp = op->dyn_cast<OpTy>())
callback(derivedOp);
});
}
/// Walk the instructions held by this function in postorder, calling the
/// callback for each instruction.
void walkPostOrder(const std::function<void(Instruction *)> &callback);
/// Walk the operations held by this function in postorder, calling the
/// callback for each operation.
void walkPostOrder(const std::function<void(Operation *)> &callback);
/// Specialization of walkPostOrder to only visit operations of 'OpTy'.
template <typename OpTy>
void walkPostOrder(std::function<void(OpTy)> callback) {
walkPostOrder([&](Instruction *inst) {
if (auto op = inst->dyn_cast<OpTy>())
callback(op);
walkPostOrder([&](Operation *op) {
if (auto derivedOp = op->dyn_cast<OpTy>())
callback(derivedOp);
});
}
@@ -441,13 +441,13 @@ public:
void emitNote(const Twine &message);
private:
Instruction(Location location, OperationName name, unsigned numResults,
unsigned numSuccessors, unsigned numRegions,
const NamedAttributeList &attributes, MLIRContext *context);
Operation(Location location, OperationName name, unsigned numResults,
unsigned numSuccessors, unsigned numRegions,
const NamedAttributeList &attributes, MLIRContext *context);
// Instructions are deleted through the destroy() member because they are
// Operations are deleted through the destroy() member because they are
// allocated with malloc.
~Instruction();
~Operation();
/// Returns the operand storage object.
detail::OperandStorage &getOperandStorage() {
@@ -457,15 +457,15 @@ private:
// Provide a 'getParent' method for ilist_node_with_parent methods.
Block *getParent() { return getBlock(); }
/// The instruction block that containts this instruction.
/// The operation block that containts this operation.
Block *block = nullptr;
/// This holds information about the source location the operation was defined
/// or derived from.
Location location;
/// Relative order of this instruction in its parent block. Used for
/// O(1) local dominance checks between instructions.
/// Relative order of this operation in its parent block. Used for
/// O(1) local dominance checks between operations.
mutable unsigned orderIndex = 0;
const unsigned numResults, numSuccs, numRegions;
@@ -477,16 +477,16 @@ private:
NamedAttributeList attrs;
// allow ilist_traits access to 'block' field.
friend struct llvm::ilist_traits<Instruction>;
friend struct llvm::ilist_traits<Operation>;
// allow block to access the 'orderIndex' field.
friend class Block;
// allow ilist_node_with_parent to access the 'getParent' method.
friend class llvm::ilist_node_with_parent<Instruction, Block>;
friend class llvm::ilist_node_with_parent<Operation, Block>;
// This stuff is used by the TrailingObjects template.
friend llvm::TrailingObjects<Instruction, InstResult, BlockOperand, unsigned,
friend llvm::TrailingObjects<Operation, InstResult, BlockOperand, unsigned,
Region, detail::OperandStorage>;
size_t numTrailingObjects(OverloadToken<InstResult>) const {
return numResults;
@@ -498,60 +498,63 @@ private:
size_t numTrailingObjects(OverloadToken<unsigned>) const { return numSuccs; }
};
inline raw_ostream &operator<<(raw_ostream &os, Instruction &inst) {
inst.print(os);
inline raw_ostream &operator<<(raw_ostream &os, Operation &op) {
op.print(os);
return os;
}
/// Temporary typedef to Instruction to while the codebase transitions to
/// Operation.
using Instruction = Operation;
/// This class implements the const/non-const operand iterators for the
/// Instruction class in terms of getOperand(idx).
/// Operation class in terms of getOperand(idx).
class OperandIterator final
: public IndexedAccessorIterator<OperandIterator, Instruction, Value> {
: public IndexedAccessorIterator<OperandIterator, Operation, Value> {
public:
/// Initializes the operand iterator to the specified operand index.
OperandIterator(Instruction *object, unsigned index)
: IndexedAccessorIterator<OperandIterator, Instruction, Value>(object,
index) {}
OperandIterator(Operation *object, unsigned index)
: IndexedAccessorIterator<OperandIterator, Operation, Value>(object,
index) {}
Value *operator*() const { return this->object->getOperand(this->index); }
};
// Implement the inline operand iterator methods.
inline auto Instruction::operand_begin() -> operand_iterator {
inline auto Operation::operand_begin() -> operand_iterator {
return operand_iterator(this, 0);
}
inline auto Instruction::operand_end() -> operand_iterator {
inline auto Operation::operand_end() -> operand_iterator {
return operand_iterator(this, getNumOperands());
}
inline auto Instruction::getOperands() -> operand_range {
inline auto Operation::getOperands() -> operand_range {
return {operand_begin(), operand_end()};
}
/// This class implements the result iterators for the Instruction class
/// This class implements the result iterators for the Operation class
/// in terms of getResult(idx).
class ResultIterator final
: public IndexedAccessorIterator<ResultIterator, Instruction, Value> {
: public IndexedAccessorIterator<ResultIterator, Operation, Value> {
public:
/// Initializes the result iterator to the specified index.
ResultIterator(Instruction *object, unsigned index)
: IndexedAccessorIterator<ResultIterator, Instruction, Value>(object,
index) {}
ResultIterator(Operation *object, unsigned index)
: IndexedAccessorIterator<ResultIterator, Operation, Value>(object,
index) {}
Value *operator*() const { return this->object->getResult(this->index); }
};
/// This class implements the result type iterators for the Instruction
/// This class implements the result type iterators for the Operation
/// class in terms of getResult(idx)->getType().
class ResultTypeIterator final
: public IndexedAccessorIterator<ResultTypeIterator, Instruction, Value> {
: public IndexedAccessorIterator<ResultTypeIterator, Operation, Value> {
public:
/// Initializes the result type iterator to the specified index.
ResultTypeIterator(Instruction *object, unsigned index)
: IndexedAccessorIterator<ResultTypeIterator, Instruction, Value>(object,
index) {
}
ResultTypeIterator(Operation *object, unsigned index)
: IndexedAccessorIterator<ResultTypeIterator, Operation, Value>(object,
index) {}
Type operator*() const {
return this->object->getResult(this->index)->getType();
@@ -559,31 +562,31 @@ public:
};
// Implement the inline result iterator methods.
inline auto Instruction::result_begin() -> result_iterator {
inline auto Operation::result_begin() -> result_iterator {
return result_iterator(this, 0);
}
inline auto Instruction::result_end() -> result_iterator {
inline auto Operation::result_end() -> result_iterator {
return result_iterator(this, getNumResults());
}
inline auto Instruction::getResults() -> llvm::iterator_range<result_iterator> {
inline auto Operation::getResults() -> llvm::iterator_range<result_iterator> {
return {result_begin(), result_end()};
}
inline auto Instruction::result_type_begin() -> result_type_iterator {
inline auto Operation::result_type_begin() -> result_type_iterator {
return result_type_iterator(this, 0);
}
inline auto Instruction::result_type_end() -> result_type_iterator {
inline auto Operation::result_type_end() -> result_type_iterator {
return result_type_iterator(this, getNumResults());
}
inline auto Instruction::getResultTypes()
inline auto Operation::getResultTypes()
-> llvm::iterator_range<result_type_iterator> {
return {result_type_begin(), result_type_end()};
}
} // end namespace mlir
#endif // MLIR_IR_INSTRUCTION_H
#endif // MLIR_IR_OPERATION_H

View File

@@ -15,7 +15,7 @@
// limitations under the License.
// =============================================================================
//
// This file defines a number of support types that Instruction and related
// This file defines a number of support types that Operation and related
// classes build on top of.
//
//===----------------------------------------------------------------------===//
@@ -27,14 +27,18 @@
#include "mlir/IR/Identifier.h"
#include "mlir/IR/Location.h"
#include "mlir/IR/Types.h"
#include "mlir/IR/Value.h"
#include "mlir/Support/LLVM.h"
#include "mlir/Support/LogicalResult.h"
#include "llvm/ADT/PointerUnion.h"
#include "llvm/Support/TrailingObjects.h"
#include <memory>
namespace mlir {
class Block;
class Dialect;
class Instruction;
class Operation;
using Instruction = Operation;
struct OperationState;
class OpAsmParser;
class OpAsmParserResult;
@@ -79,23 +83,22 @@ public:
Dialect &dialect;
/// Return true if this "op class" can match against the specified operation.
bool (&isClassFor)(Instruction *op);
bool (&isClassFor)(Operation *op);
/// Use the specified object to parse this ops custom assembly format.
bool (&parseAssembly)(OpAsmParser *parser, OperationState *result);
/// This hook implements the AsmPrinter for this operation.
void (&printAssembly)(Instruction *op, OpAsmPrinter *p);
void (&printAssembly)(Operation *op, OpAsmPrinter *p);
/// This hook implements the verifier for this operation. It should emits an
/// error message and returns true if a problem is detected, or returns false
/// if everything is ok.
bool (&verifyInvariants)(Instruction *op);
bool (&verifyInvariants)(Operation *op);
/// This hook implements a constant folder for this operation. It fills in
/// `results` on success.
LogicalResult (&constantFoldHook)(Instruction *op,
ArrayRef<Attribute> operands,
LogicalResult (&constantFoldHook)(Operation *op, ArrayRef<Attribute> operands,
SmallVectorImpl<Attribute> &results);
/// This hook implements a generalized folder for this operation. Operations
@@ -118,7 +121,7 @@ public:
/// "x+0 -> x", "min(x,y,x,z) -> min(x,y,z)", "x+y-x -> y", etc), but does
/// not allow for canonicalizations that need to introduce new operations, not
/// even constants (e.g. "x-x -> 0" cannot be expressed).
LogicalResult (&foldHook)(Instruction *op, SmallVectorImpl<Value *> &results);
LogicalResult (&foldHook)(Operation *op, SmallVectorImpl<Value *> &results);
/// This hook returns any canonicalization pattern rewrites that the operation
/// supports, for use by the canonicalization pass.
@@ -147,14 +150,14 @@ public:
private:
AbstractOperation(
StringRef name, Dialect &dialect, OperationProperties opProperties,
bool (&isClassFor)(Instruction *op),
bool (&isClassFor)(Operation *op),
bool (&parseAssembly)(OpAsmParser *parser, OperationState *result),
void (&printAssembly)(Instruction *op, OpAsmPrinter *p),
bool (&verifyInvariants)(Instruction *op),
LogicalResult (&constantFoldHook)(Instruction *op,
void (&printAssembly)(Operation *op, OpAsmPrinter *p),
bool (&verifyInvariants)(Operation *op),
LogicalResult (&constantFoldHook)(Operation *op,
ArrayRef<Attribute> operands,
SmallVectorImpl<Attribute> &results),
LogicalResult (&foldHook)(Instruction *op,
LogicalResult (&foldHook)(Operation *op,
SmallVectorImpl<Value *> &results),
void (&getCanonicalizationPatterns)(OwningRewritePatternList &results,
MLIRContext *context))
@@ -274,14 +277,14 @@ public:
operands.append(succOperands.begin(), succOperands.end());
}
/// Create a region that should be attached to the instruction. These regions
/// can be filled in immediately without waiting for Instruction to be
/// Create a region that should be attached to the operation. These regions
/// can be filled in immediately without waiting for Operation to be
/// created. When it is, the region bodies will be transferred.
Region *addRegion();
/// Take a region that should be attached to the Instruction. The body of the
/// region will be transferred when the Instruction is constructed. If the
/// region is null, a new empty region will be attached to the Instruction.
/// Take a region that should be attached to the Operation. The body of the
/// region will be transferred when the Operation is constructed. If the
/// region is null, a new empty region will be attached to the Operation.
void addRegion(std::unique_ptr<Region> &&region);
/// Sets the operand list of the operation as resizable.
@@ -290,6 +293,142 @@ public:
}
};
namespace detail {
/// A utility class holding the information necessary to dynamically resize
/// operands.
struct ResizableStorage {
ResizableStorage(InstOperand *opBegin, unsigned numOperands)
: firstOpAndIsDynamic(opBegin, false), capacity(numOperands) {}
~ResizableStorage() { cleanupStorage(); }
/// Cleanup any allocated storage.
void cleanupStorage() {
// If the storage is dynamic, then we need to free the storage.
if (isStorageDynamic())
free(firstOpAndIsDynamic.getPointer());
}
/// Sets the storage pointer to a new dynamically allocated block.
void setDynamicStorage(InstOperand *opBegin) {
/// Cleanup the old storage if necessary.
cleanupStorage();
firstOpAndIsDynamic.setPointerAndInt(opBegin, true);
}
/// Returns the current storage pointer.
InstOperand *getPointer() { return firstOpAndIsDynamic.getPointer(); }
const InstOperand *getPointer() const {
return firstOpAndIsDynamic.getPointer();
}
/// Returns if the current storage of operands is in the trailing objects is
/// in a dynamically allocated memory block.
bool isStorageDynamic() const { return firstOpAndIsDynamic.getInt(); }
/// A pointer to the first operand element. This is either to the trailing
/// objects storage, or a dynamically allocated block of memory.
llvm::PointerIntPair<InstOperand *, 1, bool> firstOpAndIsDynamic;
// The maximum number of operands that can be currently held by the storage.
unsigned capacity;
};
/// This class handles the management of operation operands. Operands are
/// stored similarly to the elements of a SmallVector except for two key
/// differences. The first is the inline storage, which is a trailing objects
/// array. The second is that being able to dynamically resize the operand list
/// is optional.
class OperandStorage final
: private llvm::TrailingObjects<OperandStorage, ResizableStorage,
InstOperand> {
public:
OperandStorage(unsigned numOperands, bool resizable)
: numOperands(numOperands), resizable(resizable) {
// Initialize the resizable storage.
if (resizable) {
new (&getResizableStorage())
ResizableStorage(getTrailingObjects<InstOperand>(), numOperands);
}
}
~OperandStorage() {
// Manually destruct the operands.
for (auto &operand : getInstOperands())
operand.~InstOperand();
// If the storage is resizable then destruct the utility.
if (resizable)
getResizableStorage().~ResizableStorage();
}
/// Replace the operands contained in the storage with the ones provided in
/// 'operands'.
void setOperands(Operation *owner, ArrayRef<Value *> operands);
/// Erase an operand held by the storage.
void eraseOperand(unsigned index);
/// Get the operation operands held by the storage.
ArrayRef<InstOperand> getInstOperands() const {
return {getRawOperands(), size()};
}
MutableArrayRef<InstOperand> getInstOperands() {
return {getRawOperands(), size()};
}
/// Return the number of operands held in the storage.
unsigned size() const { return numOperands; }
/// Returns the additional size necessary for allocating this object.
static size_t additionalAllocSize(unsigned numOperands, bool resizable) {
return additionalSizeToAlloc<ResizableStorage, InstOperand>(
resizable ? 1 : 0, numOperands);
}
/// Returns if this storage is resizable.
bool isResizable() const { return resizable; }
private:
/// Clear the storage and destroy the current operands held by the storage.
void clear() { numOperands = 0; }
/// Returns the current pointer for the raw operands array.
InstOperand *getRawOperands() {
return resizable ? getResizableStorage().getPointer()
: getTrailingObjects<InstOperand>();
}
const InstOperand *getRawOperands() const {
return resizable ? getResizableStorage().getPointer()
: getTrailingObjects<InstOperand>();
}
/// Returns the resizable operand utility class.
ResizableStorage &getResizableStorage() {
assert(resizable);
return *getTrailingObjects<ResizableStorage>();
}
const ResizableStorage &getResizableStorage() const {
assert(resizable);
return *getTrailingObjects<ResizableStorage>();
}
/// Grow the internal resizable operand storage.
void grow(ResizableStorage &resizeUtil, size_t minSize);
/// The current number of operands, and the current max operand capacity.
unsigned numOperands : 31;
/// Whether this storage is resizable or not.
bool resizable : 1;
// This stuff is used by the TrailingObjects template.
friend llvm::TrailingObjects<OperandStorage, ResizableStorage, InstOperand>;
size_t numTrailingObjects(OverloadToken<ResizableStorage>) const {
return resizable ? 1 : 0;
}
};
} // end namespace detail
} // end namespace mlir
namespace llvm {

View File

@@ -28,8 +28,9 @@
namespace mlir {
class Instruction;
class IROperand;
class Operation;
using Instruction = Operation;
template <typename OperandType> class ValueUseIterator;
class IRObjectWithUseList {

View File

@@ -29,7 +29,8 @@
namespace mlir {
class Block;
class Function;
class Instruction;
class Operation;
using Instruction = Operation;
class Value;
/// Operands contain a Value.

View File

@@ -31,8 +31,9 @@ namespace mlir {
// Forward declarations.
class Block;
class FuncBuilder;
class Instruction;
class MLIRContext;
class Operation;
using Instruction = Operation;
class Type;
class Value;

View File

@@ -26,8 +26,8 @@
#include "mlir/Analysis/Utils.h"
#include "mlir/IR/AffineExprVisitor.h"
#include "mlir/IR/Builders.h"
#include "mlir/IR/Instruction.h"
#include "mlir/IR/IntegerSet.h"
#include "mlir/IR/Operation.h"
#include "mlir/StandardOps/Ops.h"
#include "mlir/Support/MathExtras.h"
#include "mlir/Support/STLExtras.h"

View File

@@ -23,8 +23,8 @@
#include "mlir/AffineOps/AffineOps.h"
#include "mlir/IR/AffineExprVisitor.h"
#include "mlir/IR/AffineMap.h"
#include "mlir/IR/Instruction.h"
#include "mlir/IR/IntegerSet.h"
#include "mlir/IR/Operation.h"
#include "mlir/StandardOps/Ops.h"
#include "mlir/Support/MathExtras.h"
#include "llvm/ADT/DenseSet.h"

View File

@@ -21,7 +21,7 @@
//===----------------------------------------------------------------------===//
#include "mlir/Analysis/Dominance.h"
#include "mlir/IR/Instruction.h"
#include "mlir/IR/Operation.h"
#include "llvm/Support/GenericDomTreeConstruction.h"
using namespace mlir;
using namespace mlir::detail;

View File

@@ -28,7 +28,7 @@
#include "mlir/Analysis/VectorAnalysis.h"
#include "mlir/IR/AffineMap.h"
#include "mlir/IR/Builders.h"
#include "mlir/IR/Instruction.h"
#include "mlir/IR/Operation.h"
#include "mlir/StandardOps/Ops.h"
#include "mlir/SuperVectorOps/SuperVectorOps.h"
#include "mlir/Support/Functional.h"

View File

@@ -15,8 +15,8 @@
// limitations under the License.
// =============================================================================
#include "mlir/IR/Instruction.h"
#include "mlir/IR/Module.h"
#include "mlir/IR/Operation.h"
#include "mlir/IR/OperationSupport.h"
#include "mlir/Pass/Pass.h"
#include "llvm/ADT/DenseMap.h"

View File

@@ -22,7 +22,7 @@
#include "mlir/Analysis/SliceAnalysis.h"
#include "mlir/AffineOps/AffineOps.h"
#include "mlir/Analysis/VectorAnalysis.h"
#include "mlir/IR/Instruction.h"
#include "mlir/IR/Operation.h"
#include "mlir/Support/Functional.h"
#include "mlir/Support/STLExtras.h"

View File

@@ -19,8 +19,8 @@
#include "mlir/AffineOps/AffineOps.h"
#include "mlir/Analysis/AffineAnalysis.h"
#include "mlir/Analysis/LoopAnalysis.h"
#include "mlir/IR/Instruction.h"
#include "mlir/IR/IntegerSet.h"
#include "mlir/IR/Operation.h"
#include "mlir/StandardOps/Ops.h"
#include "mlir/SuperVectorOps/SuperVectorOps.h"
#include "mlir/Support/Functional.h"

View File

@@ -37,8 +37,8 @@
#include "mlir/IR/Attributes.h"
#include "mlir/IR/Dialect.h"
#include "mlir/IR/Function.h"
#include "mlir/IR/Instruction.h"
#include "mlir/IR/Module.h"
#include "mlir/IR/Operation.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/Regex.h"

View File

@@ -26,10 +26,10 @@
#include "mlir/EDSC/MLIREmitter.h"
#include "mlir/EDSC/Types.h"
#include "mlir/IR/Builders.h"
#include "mlir/IR/Instruction.h"
#include "mlir/IR/IntegerSet.h"
#include "mlir/IR/Location.h"
#include "mlir/IR/OpDefinition.h"
#include "mlir/IR/Operation.h"
#include "mlir/IR/Value.h"
#include "mlir/StandardOps/Ops.h"
#include "mlir/SuperVectorOps/SuperVectorOps.h"

View File

@@ -25,12 +25,12 @@
#include "mlir/IR/Attributes.h"
#include "mlir/IR/Dialect.h"
#include "mlir/IR/Function.h"
#include "mlir/IR/Instruction.h"
#include "mlir/IR/IntegerSet.h"
#include "mlir/IR/MLIRContext.h"
#include "mlir/IR/Matchers.h"
#include "mlir/IR/Module.h"
#include "mlir/IR/OpImplementation.h"
#include "mlir/IR/Operation.h"
#include "mlir/IR/StandardTypes.h"
#include "mlir/Support/STLExtras.h"
#include "llvm/ADT/APFloat.h"

View File

@@ -18,7 +18,7 @@
#include "mlir/IR/Block.h"
#include "mlir/IR/BlockAndValueMapping.h"
#include "mlir/IR/Builders.h"
#include "mlir/IR/Instruction.h"
#include "mlir/IR/Operation.h"
using namespace mlir;
//===----------------------------------------------------------------------===//

View File

@@ -1,665 +0,0 @@
//===- Instruction.cpp - MLIR 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/Instruction.h"
#include "mlir/IR/AffineExpr.h"
#include "mlir/IR/AffineMap.h"
#include "mlir/IR/BlockAndValueMapping.h"
#include "mlir/IR/Dialect.h"
#include "mlir/IR/Function.h"
#include "mlir/IR/IntegerSet.h"
#include "mlir/IR/MLIRContext.h"
#include "llvm/ADT/DenseMap.h"
#include <numeric>
using namespace mlir;
//===----------------------------------------------------------------------===//
// InstResult
//===----------------------------------------------------------------------===//
/// Return the result number of this result.
unsigned InstResult::getResultNumber() {
// Results are always stored consecutively, so use pointer subtraction to
// figure out what number this is.
return this - &getOwner()->getInstResults()[0];
}
//===----------------------------------------------------------------------===//
// InstOperand
//===----------------------------------------------------------------------===//
/// Return which operand this is in the operand list.
template <> unsigned InstOperand::getOperandNumber() {
return this - &getOwner()->getInstOperands()[0];
}
//===----------------------------------------------------------------------===//
// BlockOperand
//===----------------------------------------------------------------------===//
/// Return which operand this is in the operand list.
template <> unsigned BlockOperand::getOperandNumber() {
return this - &getOwner()->getBlockOperands()[0];
}
//===----------------------------------------------------------------------===//
// OperandStorage
//===----------------------------------------------------------------------===//
/// Replace the operands contained in the storage with the ones provided in
/// 'operands'.
void detail::OperandStorage::setOperands(Instruction *owner,
ArrayRef<Value *> operands) {
// If the number of operands is less than or equal to the current amount, we
// can just update in place.
if (operands.size() <= numOperands) {
auto instOperands = getInstOperands();
// If the number of new operands is less than the current count, then remove
// any extra operands.
for (unsigned i = operands.size(); i != numOperands; ++i)
instOperands[i].~InstOperand();
// Set the operands in place.
numOperands = operands.size();
for (unsigned i = 0; i != numOperands; ++i)
instOperands[i].set(operands[i]);
return;
}
// Otherwise, we need to be resizable.
assert(resizable && "Only resizable operations may add operands");
// Grow the capacity if necessary.
auto &resizeUtil = getResizableStorage();
if (resizeUtil.capacity < operands.size())
grow(resizeUtil, operands.size());
// Set the operands.
InstOperand *opBegin = getRawOperands();
for (unsigned i = 0; i != numOperands; ++i)
opBegin[i].set(operands[i]);
for (unsigned e = operands.size(); numOperands != e; ++numOperands)
new (&opBegin[numOperands]) InstOperand(owner, operands[numOperands]);
}
/// Erase an operand held by the storage.
void detail::OperandStorage::eraseOperand(unsigned index) {
assert(index < size());
auto Operands = getInstOperands();
--numOperands;
// Shift all operands down by 1 if the operand to remove is not at the end.
if (index != numOperands)
std::rotate(&Operands[index], &Operands[index + 1], &Operands[numOperands]);
Operands[numOperands].~InstOperand();
}
/// Grow the internal operand storage.
void detail::OperandStorage::grow(ResizableStorage &resizeUtil,
size_t minSize) {
// Allocate a new storage array.
resizeUtil.capacity =
std::max(size_t(llvm::NextPowerOf2(resizeUtil.capacity + 2)), minSize);
InstOperand *newStorage = static_cast<InstOperand *>(
llvm::safe_malloc(resizeUtil.capacity * sizeof(InstOperand)));
// Move the current operands to the new storage.
auto operands = getInstOperands();
std::uninitialized_copy(std::make_move_iterator(operands.begin()),
std::make_move_iterator(operands.end()), newStorage);
// Destroy the original operands and update the resizable storage pointer.
for (auto &operand : operands)
operand.~InstOperand();
resizeUtil.setDynamicStorage(newStorage);
}
//===----------------------------------------------------------------------===//
// Instruction
//===----------------------------------------------------------------------===//
/// Create a new Instruction with the specific fields.
Instruction *Instruction::create(Location location, OperationName name,
ArrayRef<Value *> operands,
ArrayRef<Type> resultTypes,
ArrayRef<NamedAttribute> attributes,
ArrayRef<Block *> successors,
unsigned numRegions, bool resizableOperandList,
MLIRContext *context) {
return create(location, name, operands, resultTypes,
NamedAttributeList(context, attributes), successors, numRegions,
resizableOperandList, context);
}
/// Overload of create that takes an existing NamedAttributeList to avoid
/// unnecessarily uniquing a list of attributes.
Instruction *Instruction::create(Location location, OperationName name,
ArrayRef<Value *> operands,
ArrayRef<Type> resultTypes,
const NamedAttributeList &attributes,
ArrayRef<Block *> successors,
unsigned numRegions, bool resizableOperandList,
MLIRContext *context) {
unsigned numSuccessors = successors.size();
// Input operands are nullptr-separated for each successor, the null operands
// aren't actually stored.
unsigned numOperands = operands.size() - numSuccessors;
// Compute the byte size for the instruction and the operand storage.
auto byteSize = totalSizeToAlloc<InstResult, BlockOperand, unsigned, Region,
detail::OperandStorage>(
resultTypes.size(), numSuccessors, numSuccessors, numRegions,
/*detail::OperandStorage*/ 1);
byteSize += llvm::alignTo(detail::OperandStorage::additionalAllocSize(
numOperands, resizableOperandList),
alignof(Instruction));
void *rawMem = malloc(byteSize);
// Create the new Instruction.
auto inst = ::new (rawMem)
Instruction(location, name, resultTypes.size(), numSuccessors, numRegions,
attributes, context);
assert((numSuccessors == 0 || !inst->isKnownNonTerminator()) &&
"unexpected successors in a non-terminator operation");
// Initialize the regions.
for (unsigned i = 0; i != numRegions; ++i)
new (&inst->getRegion(i)) Region(inst);
// Initialize the results and operands.
new (&inst->getOperandStorage())
detail::OperandStorage(numOperands, resizableOperandList);
auto instResults = inst->getInstResults();
for (unsigned i = 0, e = resultTypes.size(); i != e; ++i)
new (&instResults[i]) InstResult(resultTypes[i], inst);
auto InstOperands = inst->getInstOperands();
// Initialize normal operands.
unsigned operandIt = 0, operandE = operands.size();
unsigned nextOperand = 0;
for (; operandIt != operandE; ++operandIt) {
// Null operands are used as sentinels between successor operand lists. If
// we encounter one here, break and handle the successor operands lists
// separately below.
if (!operands[operandIt])
break;
new (&InstOperands[nextOperand++]) InstOperand(inst, operands[operandIt]);
}
unsigned currentSuccNum = 0;
if (operandIt == operandE) {
// Verify that the amount of sentinel operands is equivalent to the number
// of successors.
assert(currentSuccNum == numSuccessors);
return inst;
}
assert(!inst->isKnownNonTerminator() &&
"Unexpected nullptr in operand list when creating non-terminator.");
auto instBlockOperands = inst->getBlockOperands();
unsigned *succOperandCountIt = inst->getTrailingObjects<unsigned>();
unsigned *succOperandCountE = succOperandCountIt + numSuccessors;
(void)succOperandCountE;
for (; operandIt != operandE; ++operandIt) {
// If we encounter a sentinel 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 sentinel operands than successors.");
}
new (&instBlockOperands[currentSuccNum])
BlockOperand(inst, successors[currentSuccNum]);
*succOperandCountIt = 0;
++currentSuccNum;
continue;
}
new (&InstOperands[nextOperand++]) InstOperand(inst, operands[operandIt]);
++(*succOperandCountIt);
}
// Verify that the amount of sentinel operands is equivalent to the number of
// successors.
assert(currentSuccNum == numSuccessors);
return inst;
}
Instruction::Instruction(Location location, OperationName name,
unsigned numResults, unsigned numSuccessors,
unsigned numRegions,
const NamedAttributeList &attributes,
MLIRContext *context)
: location(location), numResults(numResults), numSuccs(numSuccessors),
numRegions(numRegions), name(name), attrs(attributes) {}
// Instructions are deleted through the destroy() member because they are
// allocated via malloc.
Instruction::~Instruction() {
assert(block == nullptr && "instruction destroyed but still in a block");
// Explicitly run the destructors for the operands and results.
getOperandStorage().~OperandStorage();
for (auto &result : getInstResults())
result.~InstResult();
// Explicitly run the destructors for the successors.
for (auto &successor : getBlockOperands())
successor.~BlockOperand();
// Explicitly destroy the regions.
for (auto &region : getRegions())
region.~Region();
}
/// Destroy this instruction or one of its subclasses.
void Instruction::destroy() {
this->~Instruction();
free(this);
}
/// Return the context this operation is associated with.
MLIRContext *Instruction::getContext() {
// If we have a result or operand type, that is a constant time way to get
// to the context.
if (getNumResults())
return getResult(0)->getType().getContext();
if (getNumOperands())
return getOperand(0)->getType().getContext();
// In the very odd case where we have no operands or results, fall back to
// doing a find.
return getFunction()->getContext();
}
/// Return the dialact this operation is associated with, or nullptr if the
/// associated dialect is not registered.
Dialect *Instruction::getDialect() {
if (auto *abstractOp = getAbstractOperation())
return &abstractOp->dialect;
// If this operation hasn't been registered or doesn't have abstract
// operation, fall back to a dialect which matches the prefix.
auto opName = getName().getStringRef();
auto dialectPrefix = opName.split('.').first;
return getContext()->getRegisteredDialect(dialectPrefix);
}
Instruction *Instruction::getParentInst() {
return block ? block->getContainingInst() : nullptr;
}
Function *Instruction::getFunction() {
return block ? block->getFunction() : nullptr;
}
//===----------------------------------------------------------------------===//
// Instruction Walkers
//===----------------------------------------------------------------------===//
void Instruction::walk(const std::function<void(Instruction *)> &callback) {
// Visit the current instruction.
callback(this);
// Visit any internal instructions.
for (auto &region : getRegions())
for (auto &block : region)
block.walk(callback);
}
void Instruction::walkPostOrder(
const std::function<void(Instruction *)> &callback) {
// Visit any internal instructions.
for (auto &region : llvm::reverse(getRegions()))
for (auto &block : llvm::reverse(region))
block.walkPostOrder(callback);
// Visit the current instruction.
callback(this);
}
//===----------------------------------------------------------------------===//
// Other
//===----------------------------------------------------------------------===//
/// Emit a note about this instruction, reporting up to any diagnostic
/// handlers that may be listening.
void Instruction::emitNote(const Twine &message) {
getContext()->emitDiagnostic(getLoc(), message,
MLIRContext::DiagnosticKind::Note);
}
/// Emit a warning about this instruction, reporting up to any diagnostic
/// handlers that may be listening.
void Instruction::emitWarning(const Twine &message) {
getContext()->emitDiagnostic(getLoc(), message,
MLIRContext::DiagnosticKind::Warning);
}
/// Emit an error about fatal conditions with this operation, reporting up to
/// any diagnostic handlers that may be listening. This function always
/// returns true. NOTE: This may terminate the containing application, only
/// use when the IR is in an inconsistent state.
bool Instruction::emitError(const Twine &message) {
return getContext()->emitError(getLoc(), message);
}
/// Given an instruction 'other' that is within the same parent block, return
/// whether the current instruction is before 'other' in the instruction list
/// of the parent block.
/// Note: This function has an average complexity of O(1), but worst case may
/// take O(N) where N is the number of instructions within the parent block.
bool Instruction::isBeforeInBlock(Instruction *other) {
assert(block && "Instructions without parent blocks have no order.");
assert(other && other->block == block &&
"Expected other instruction to have the same parent block.");
// Recompute the parent ordering if necessary.
if (!block->isInstOrderValid())
block->recomputeInstOrder();
return orderIndex < other->orderIndex;
}
//===----------------------------------------------------------------------===//
// ilist_traits for Instruction
//===----------------------------------------------------------------------===//
auto llvm::ilist_detail::SpecificNodeAccess<
typename llvm::ilist_detail::compute_node_options<
::mlir::Instruction>::type>::getNodePtr(pointer N) -> node_type * {
return NodeAccess::getNodePtr<OptionsT>(N);
}
auto llvm::ilist_detail::SpecificNodeAccess<
typename llvm::ilist_detail::compute_node_options<
::mlir::Instruction>::type>::getNodePtr(const_pointer N)
-> const node_type * {
return NodeAccess::getNodePtr<OptionsT>(N);
}
auto llvm::ilist_detail::SpecificNodeAccess<
typename llvm::ilist_detail::compute_node_options<
::mlir::Instruction>::type>::getValuePtr(node_type *N) -> pointer {
return NodeAccess::getValuePtr<OptionsT>(N);
}
auto llvm::ilist_detail::SpecificNodeAccess<
typename llvm::ilist_detail::compute_node_options<
::mlir::Instruction>::type>::getValuePtr(const node_type *N)
-> const_pointer {
return NodeAccess::getValuePtr<OptionsT>(N);
}
void llvm::ilist_traits<::mlir::Instruction>::deleteNode(Instruction *inst) {
inst->destroy();
}
Block *llvm::ilist_traits<::mlir::Instruction>::getContainingBlock() {
size_t Offset(size_t(&((Block *)nullptr->*Block::getSublistAccess(nullptr))));
iplist<Instruction> *Anchor(static_cast<iplist<Instruction> *>(this));
return reinterpret_cast<Block *>(reinterpret_cast<char *>(Anchor) - Offset);
}
/// This is a trait method invoked when a instruction is added to a block. We
/// keep the block pointer up to date.
void llvm::ilist_traits<::mlir::Instruction>::addNodeToList(Instruction *inst) {
assert(!inst->getBlock() && "already in a instruction block!");
inst->block = getContainingBlock();
// Invalidate the block ordering.
inst->block->invalidateInstOrder();
}
/// This is a trait method invoked when a instruction is removed from a block.
/// We keep the block pointer up to date.
void llvm::ilist_traits<::mlir::Instruction>::removeNodeFromList(
Instruction *inst) {
assert(inst->block && "not already in a instruction block!");
inst->block = nullptr;
}
/// This is a trait method invoked when a instruction is moved from one block
/// to another. We keep the block pointer up to date.
void llvm::ilist_traits<::mlir::Instruction>::transferNodesFromList(
ilist_traits<Instruction> &otherList, inst_iterator first,
inst_iterator last) {
Block *curParent = getContainingBlock();
// Invalidate the ordering of the parent block.
curParent->invalidateInstOrder();
// If we are transferring instructions within the same block, the block
// pointer doesn't need to be updated.
if (curParent == otherList.getContainingBlock())
return;
// Update the 'block' member of each instruction.
for (; first != last; ++first)
first->block = curParent;
}
/// Remove this instruction (and its descendants) from its Block and delete
/// all of them.
void Instruction::erase() {
assert(getBlock() && "Instruction has no block");
getBlock()->getInstructions().erase(this);
}
/// Unlink this instruction from its current block and insert it right before
/// `existingInst` which may be in the same or another block in the same
/// function.
void Instruction::moveBefore(Instruction *existingInst) {
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 Instruction::moveBefore(Block *block,
llvm::iplist<Instruction>::iterator iterator) {
block->getInstructions().splice(iterator, getBlock()->getInstructions(),
getIterator());
}
/// 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();
for (auto &region : getRegions())
for (Block &block : region)
block.dropAllReferences();
for (auto &dest : getBlockOperands())
dest.drop();
}
/// This drops all uses of any values defined by this operation or its nested
/// regions, wherever they are located.
void Instruction::dropAllDefinedValueUses() {
for (auto &val : getInstResults())
val.dropAllUses();
for (auto &region : getRegions())
for (auto &block : region)
block.dropAllDefinedValueUses();
}
/// Return true if there are no users of any results of this operation.
bool Instruction::use_empty() {
for (auto *result : getResults())
if (!result->use_empty())
return false;
return true;
}
void Instruction::setSuccessor(Block *block, unsigned index) {
assert(index < getNumSuccessors());
getBlockOperands()[index].set(block);
}
auto Instruction::getNonSuccessorOperands() -> operand_range {
return {operand_iterator(this, 0),
operand_iterator(this, getSuccessorOperandIndex(0))};
}
/// Get the index of the first operand of the successor at the provided
/// index.
unsigned Instruction::getSuccessorOperandIndex(unsigned index) {
assert(!isKnownNonTerminator() && "only terminators may have successors");
assert(index < getNumSuccessors());
// Count the number of operands for each of the successors after, and
// including, the one at 'index'. This is based upon the assumption that all
// non successor operands are placed at the beginning of the operand list.
auto *successorOpCountBegin = getTrailingObjects<unsigned>();
unsigned postSuccessorOpCount =
std::accumulate(successorOpCountBegin + index,
successorOpCountBegin + getNumSuccessors(), 0);
return getNumOperands() - postSuccessorOpCount;
}
auto Instruction::getSuccessorOperands(unsigned index) -> operand_range {
unsigned succOperandIndex = getSuccessorOperandIndex(index);
return {operand_iterator(this, succOperandIndex),
operand_iterator(this,
succOperandIndex + getNumSuccessorOperands(index))};
}
/// Attempt to constant fold this operation with the specified constant
/// operand values. If successful, this fills in the results vector. If not,
/// results is unspecified.
LogicalResult Instruction::constantFold(ArrayRef<Attribute> operands,
SmallVectorImpl<Attribute> &results) {
if (auto *abstractOp = getAbstractOperation()) {
// If we have a registered operation definition matching this one, use it to
// try to constant fold the operation.
if (succeeded(abstractOp->constantFoldHook(this, operands, results)))
return success();
// Otherwise, fall back on the dialect hook to handle it.
return abstractOp->dialect.constantFoldHook(this, operands, results);
}
// If this operation hasn't been registered or doesn't have abstract
// operation, fall back to a dialect which matches the prefix.
auto opName = getName().getStringRef();
auto dialectPrefix = opName.split('.').first;
if (auto *dialect = getContext()->getRegisteredDialect(dialectPrefix))
return dialect->constantFoldHook(this, operands, results);
return failure();
}
/// Attempt to fold this operation using the Op's registered foldHook.
LogicalResult Instruction::fold(SmallVectorImpl<Value *> &results) {
if (auto *abstractOp = getAbstractOperation()) {
// If we have a registered operation definition matching this one, use it to
// try to constant fold the operation.
if (succeeded(abstractOp->foldHook(this, results)))
return success();
}
return failure();
}
/// Emit an error with the op name prefixed, like "'dim' op " which is
/// convenient for verifiers.
bool Instruction::emitOpError(const Twine &message) {
return emitError(Twine('\'') + getName().getStringRef() + "' op " + message);
}
//===----------------------------------------------------------------------===//
// Instruction Cloning
//===----------------------------------------------------------------------===//
/// Create a deep copy of this instruction, remapping any operands that use
/// values outside of the instruction using the map that is provided (leaving
/// them alone if no entry is present). Replaces references to cloned
/// sub-instructions to the corresponding instruction that is copied, and adds
/// those mappings to the map.
Instruction *Instruction::clone(BlockAndValueMapping &mapper,
MLIRContext *context) {
SmallVector<Value *, 8> operands;
SmallVector<Block *, 2> successors;
operands.reserve(getNumOperands() + getNumSuccessors());
if (getNumSuccessors() == 0) {
// Non-branching operations can just add all the operands.
for (auto *opValue : getOperands())
operands.push_back(mapper.lookupOrDefault(opValue));
} else {
// We add the operands separated by nullptr's for each successor.
unsigned firstSuccOperand =
getNumSuccessors() ? getSuccessorOperandIndex(0) : getNumOperands();
auto InstOperands = getInstOperands();
unsigned i = 0;
for (; i != firstSuccOperand; ++i)
operands.push_back(mapper.lookupOrDefault(InstOperands[i].get()));
successors.reserve(getNumSuccessors());
for (unsigned succ = 0, e = getNumSuccessors(); succ != e; ++succ) {
successors.push_back(mapper.lookupOrDefault(getSuccessor(succ)));
// Add sentinel to delineate successor operands.
operands.push_back(nullptr);
// Remap the successors operands.
for (auto *operand : getSuccessorOperands(succ))
operands.push_back(mapper.lookupOrDefault(operand));
}
}
SmallVector<Type, 8> resultTypes;
resultTypes.reserve(getNumResults());
for (auto *result : getResults())
resultTypes.push_back(result->getType());
unsigned numRegions = getNumRegions();
auto *newOp = Instruction::create(getLoc(), getName(), operands, resultTypes,
attrs, successors, numRegions,
hasResizableOperandsList(), context);
// Clone the regions.
for (unsigned i = 0; i != numRegions; ++i)
getRegion(i).cloneInto(&newOp->getRegion(i), mapper, context);
// Remember the mapping of any results.
for (unsigned i = 0, e = getNumResults(); i != e; ++i)
mapper.map(getResult(i), newOp->getResult(i));
return newOp;
}
Instruction *Instruction::clone(MLIRContext *context) {
BlockAndValueMapping mapper;
return clone(mapper, context);
}

View File

@@ -15,9 +15,10 @@
// limitations under the License.
// =============================================================================
#include "mlir/IR/Operation.h"
#include "mlir/IR/BlockAndValueMapping.h"
#include "mlir/IR/Dialect.h"
#include "mlir/IR/Function.h"
#include "mlir/IR/Instruction.h"
#include "mlir/IR/MLIRContext.h"
#include "mlir/IR/OpDefinition.h"
#include "mlir/IR/OpImplementation.h"
@@ -51,6 +52,564 @@ OperationName OperationName::getFromOpaquePointer(void *pointer) {
OpAsmParser::~OpAsmParser() {}
//===----------------------------------------------------------------------===//
// InstResult
//===----------------------------------------------------------------------===//
/// Return the result number of this result.
unsigned InstResult::getResultNumber() {
// Results are always stored consecutively, so use pointer subtraction to
// figure out what number this is.
return this - &getOwner()->getInstResults()[0];
}
//===----------------------------------------------------------------------===//
// InstOperand
//===----------------------------------------------------------------------===//
/// Return which operand this is in the operand list.
template <> unsigned InstOperand::getOperandNumber() {
return this - &getOwner()->getInstOperands()[0];
}
//===----------------------------------------------------------------------===//
// BlockOperand
//===----------------------------------------------------------------------===//
/// Return which operand this is in the operand list.
template <> unsigned BlockOperand::getOperandNumber() {
return this - &getOwner()->getBlockOperands()[0];
}
//===----------------------------------------------------------------------===//
// Operation
//===----------------------------------------------------------------------===//
/// Create a new Operation with the specific fields.
Operation *Operation::create(Location location, OperationName name,
ArrayRef<Value *> operands,
ArrayRef<Type> resultTypes,
ArrayRef<NamedAttribute> attributes,
ArrayRef<Block *> successors, unsigned numRegions,
bool resizableOperandList, MLIRContext *context) {
return create(location, name, operands, resultTypes,
NamedAttributeList(context, attributes), successors, numRegions,
resizableOperandList, context);
}
/// Overload of create that takes an existing NamedAttributeList to avoid
/// unnecessarily uniquing a list of attributes.
Operation *Operation::create(Location location, OperationName name,
ArrayRef<Value *> operands,
ArrayRef<Type> resultTypes,
const NamedAttributeList &attributes,
ArrayRef<Block *> successors, unsigned numRegions,
bool resizableOperandList, MLIRContext *context) {
unsigned numSuccessors = successors.size();
// Input operands are nullptr-separated for each successor, the null operands
// aren't actually stored.
unsigned numOperands = operands.size() - numSuccessors;
// Compute the byte size for the operation and the operand storage.
auto byteSize = totalSizeToAlloc<InstResult, BlockOperand, unsigned, Region,
detail::OperandStorage>(
resultTypes.size(), numSuccessors, numSuccessors, numRegions,
/*detail::OperandStorage*/ 1);
byteSize += llvm::alignTo(detail::OperandStorage::additionalAllocSize(
numOperands, resizableOperandList),
alignof(Operation));
void *rawMem = malloc(byteSize);
// Create the new Operation.
auto op =
::new (rawMem) Operation(location, name, resultTypes.size(),
numSuccessors, numRegions, attributes, context);
assert((numSuccessors == 0 || !op->isKnownNonTerminator()) &&
"unexpected successors in a non-terminator operation");
// Initialize the regions.
for (unsigned i = 0; i != numRegions; ++i)
new (&op->getRegion(i)) Region(op);
// Initialize the results and operands.
new (&op->getOperandStorage())
detail::OperandStorage(numOperands, resizableOperandList);
auto instResults = op->getInstResults();
for (unsigned i = 0, e = resultTypes.size(); i != e; ++i)
new (&instResults[i]) InstResult(resultTypes[i], op);
auto InstOperands = op->getInstOperands();
// Initialize normal operands.
unsigned operandIt = 0, operandE = operands.size();
unsigned nextOperand = 0;
for (; operandIt != operandE; ++operandIt) {
// Null operands are used as sentinels between successor operand lists. If
// we encounter one here, break and handle the successor operands lists
// separately below.
if (!operands[operandIt])
break;
new (&InstOperands[nextOperand++]) InstOperand(op, operands[operandIt]);
}
unsigned currentSuccNum = 0;
if (operandIt == operandE) {
// Verify that the amount of sentinel operands is equivalent to the number
// of successors.
assert(currentSuccNum == numSuccessors);
return op;
}
assert(!op->isKnownNonTerminator() &&
"Unexpected nullptr in operand list when creating non-terminator.");
auto instBlockOperands = op->getBlockOperands();
unsigned *succOperandCountIt = op->getTrailingObjects<unsigned>();
unsigned *succOperandCountE = succOperandCountIt + numSuccessors;
(void)succOperandCountE;
for (; operandIt != operandE; ++operandIt) {
// If we encounter a sentinel 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 sentinel operands than successors.");
}
new (&instBlockOperands[currentSuccNum])
BlockOperand(op, successors[currentSuccNum]);
*succOperandCountIt = 0;
++currentSuccNum;
continue;
}
new (&InstOperands[nextOperand++]) InstOperand(op, operands[operandIt]);
++(*succOperandCountIt);
}
// Verify that the amount of sentinel operands is equivalent to the number of
// successors.
assert(currentSuccNum == numSuccessors);
return op;
}
Operation::Operation(Location location, OperationName name, unsigned numResults,
unsigned numSuccessors, unsigned numRegions,
const NamedAttributeList &attributes, MLIRContext *context)
: location(location), numResults(numResults), numSuccs(numSuccessors),
numRegions(numRegions), name(name), attrs(attributes) {}
// Operations are deleted through the destroy() member because they are
// allocated via malloc.
Operation::~Operation() {
assert(block == nullptr && "operation destroyed but still in a block");
// Explicitly run the destructors for the operands and results.
getOperandStorage().~OperandStorage();
for (auto &result : getInstResults())
result.~InstResult();
// Explicitly run the destructors for the successors.
for (auto &successor : getBlockOperands())
successor.~BlockOperand();
// Explicitly destroy the regions.
for (auto &region : getRegions())
region.~Region();
}
/// Destroy this operation or one of its subclasses.
void Operation::destroy() {
this->~Operation();
free(this);
}
/// Return the context this operation is associated with.
MLIRContext *Operation::getContext() {
// If we have a result or operand type, that is a constant time way to get
// to the context.
if (getNumResults())
return getResult(0)->getType().getContext();
if (getNumOperands())
return getOperand(0)->getType().getContext();
// In the very odd case where we have no operands or results, fall back to
// doing a find.
return getFunction()->getContext();
}
/// Return the dialact this operation is associated with, or nullptr if the
/// associated dialect is not registered.
Dialect *Operation::getDialect() {
if (auto *abstractOp = getAbstractOperation())
return &abstractOp->dialect;
// If this operation hasn't been registered or doesn't have abstract
// operation, fall back to a dialect which matches the prefix.
auto opName = getName().getStringRef();
auto dialectPrefix = opName.split('.').first;
return getContext()->getRegisteredDialect(dialectPrefix);
}
Operation *Operation::getParentInst() {
return block ? block->getContainingInst() : nullptr;
}
Function *Operation::getFunction() {
return block ? block->getFunction() : nullptr;
}
//===----------------------------------------------------------------------===//
// Operation Walkers
//===----------------------------------------------------------------------===//
void Operation::walk(const std::function<void(Operation *)> &callback) {
// Visit the current operation.
callback(this);
// Visit any internal operations.
for (auto &region : getRegions())
for (auto &block : region)
block.walk(callback);
}
void Operation::walkPostOrder(
const std::function<void(Operation *)> &callback) {
// Visit any internal operations.
for (auto &region : llvm::reverse(getRegions()))
for (auto &block : llvm::reverse(region))
block.walkPostOrder(callback);
// Visit the current operation.
callback(this);
}
//===----------------------------------------------------------------------===//
// Other
//===----------------------------------------------------------------------===//
/// Emit a note about this operation, reporting up to any diagnostic
/// handlers that may be listening.
void Operation::emitNote(const Twine &message) {
getContext()->emitDiagnostic(getLoc(), message,
MLIRContext::DiagnosticKind::Note);
}
/// Emit a warning about this operation, reporting up to any diagnostic
/// handlers that may be listening.
void Operation::emitWarning(const Twine &message) {
getContext()->emitDiagnostic(getLoc(), message,
MLIRContext::DiagnosticKind::Warning);
}
/// Emit an error about fatal conditions with this operation, reporting up to
/// any diagnostic handlers that may be listening. This function always
/// returns true. NOTE: This may terminate the containing application, only
/// use when the IR is in an inconsistent state.
bool Operation::emitError(const Twine &message) {
return getContext()->emitError(getLoc(), message);
}
/// Given an operation 'other' that is within the same parent block, return
/// whether the current operation is before 'other' in the operation list
/// of the parent block.
/// Note: This function has an average complexity of O(1), but worst case may
/// take O(N) where N is the number of operations within the parent block.
bool Operation::isBeforeInBlock(Operation *other) {
assert(block && "Operations without parent blocks have no order.");
assert(other && other->block == block &&
"Expected other operation to have the same parent block.");
// Recompute the parent ordering if necessary.
if (!block->isInstOrderValid())
block->recomputeInstOrder();
return orderIndex < other->orderIndex;
}
//===----------------------------------------------------------------------===//
// ilist_traits for Operation
//===----------------------------------------------------------------------===//
auto llvm::ilist_detail::SpecificNodeAccess<
typename llvm::ilist_detail::compute_node_options<
::mlir::Operation>::type>::getNodePtr(pointer N) -> node_type * {
return NodeAccess::getNodePtr<OptionsT>(N);
}
auto llvm::ilist_detail::SpecificNodeAccess<
typename llvm::ilist_detail::compute_node_options<
::mlir::Operation>::type>::getNodePtr(const_pointer N)
-> const node_type * {
return NodeAccess::getNodePtr<OptionsT>(N);
}
auto llvm::ilist_detail::SpecificNodeAccess<
typename llvm::ilist_detail::compute_node_options<
::mlir::Operation>::type>::getValuePtr(node_type *N) -> pointer {
return NodeAccess::getValuePtr<OptionsT>(N);
}
auto llvm::ilist_detail::SpecificNodeAccess<
typename llvm::ilist_detail::compute_node_options<
::mlir::Operation>::type>::getValuePtr(const node_type *N)
-> const_pointer {
return NodeAccess::getValuePtr<OptionsT>(N);
}
void llvm::ilist_traits<::mlir::Operation>::deleteNode(Operation *op) {
op->destroy();
}
Block *llvm::ilist_traits<::mlir::Operation>::getContainingBlock() {
size_t Offset(size_t(&((Block *)nullptr->*Block::getSublistAccess(nullptr))));
iplist<Operation> *Anchor(static_cast<iplist<Operation> *>(this));
return reinterpret_cast<Block *>(reinterpret_cast<char *>(Anchor) - Offset);
}
/// This is a trait method invoked when a operation is added to a block. We
/// keep the block pointer up to date.
void llvm::ilist_traits<::mlir::Operation>::addNodeToList(Operation *op) {
assert(!op->getBlock() && "already in a operation block!");
op->block = getContainingBlock();
// Invalidate the block ordering.
op->block->invalidateInstOrder();
}
/// This is a trait method invoked when a operation is removed from a block.
/// We keep the block pointer up to date.
void llvm::ilist_traits<::mlir::Operation>::removeNodeFromList(Operation *op) {
assert(op->block && "not already in a operation block!");
op->block = nullptr;
}
/// This is a trait method invoked when a operation is moved from one block
/// to another. We keep the block pointer up to date.
void llvm::ilist_traits<::mlir::Operation>::transferNodesFromList(
ilist_traits<Operation> &otherList, inst_iterator first,
inst_iterator last) {
Block *curParent = getContainingBlock();
// Invalidate the ordering of the parent block.
curParent->invalidateInstOrder();
// If we are transferring operations within the same block, the block
// pointer doesn't need to be updated.
if (curParent == otherList.getContainingBlock())
return;
// Update the 'block' member of each operation.
for (; first != last; ++first)
first->block = curParent;
}
/// Remove this operation (and its descendants) from its Block and delete
/// all of them.
void Operation::erase() {
assert(getBlock() && "Operation has no block");
getBlock()->getInstructions().erase(this);
}
/// Unlink this operation from its current block and insert it right before
/// `existingInst` which may be in the same or another block in the same
/// function.
void Operation::moveBefore(Operation *existingInst) {
moveBefore(existingInst->getBlock(), existingInst->getIterator());
}
/// Unlink this operation operation from its current basic block and insert
/// it right before `iterator` in the specified basic block.
void Operation::moveBefore(Block *block,
llvm::iplist<Operation>::iterator iterator) {
block->getInstructions().splice(iterator, getBlock()->getInstructions(),
getIterator());
}
/// This drops all operand uses from this operation, which is an essential
/// step in breaking cyclic dependences between references when they are to
/// be deleted.
void Operation::dropAllReferences() {
for (auto &op : getInstOperands())
op.drop();
for (auto &region : getRegions())
for (Block &block : region)
block.dropAllReferences();
for (auto &dest : getBlockOperands())
dest.drop();
}
/// This drops all uses of any values defined by this operation or its nested
/// regions, wherever they are located.
void Operation::dropAllDefinedValueUses() {
for (auto &val : getInstResults())
val.dropAllUses();
for (auto &region : getRegions())
for (auto &block : region)
block.dropAllDefinedValueUses();
}
/// Return true if there are no users of any results of this operation.
bool Operation::use_empty() {
for (auto *result : getResults())
if (!result->use_empty())
return false;
return true;
}
void Operation::setSuccessor(Block *block, unsigned index) {
assert(index < getNumSuccessors());
getBlockOperands()[index].set(block);
}
auto Operation::getNonSuccessorOperands() -> operand_range {
return {operand_iterator(this, 0),
operand_iterator(this, getSuccessorOperandIndex(0))};
}
/// Get the index of the first operand of the successor at the provided
/// index.
unsigned Operation::getSuccessorOperandIndex(unsigned index) {
assert(!isKnownNonTerminator() && "only terminators may have successors");
assert(index < getNumSuccessors());
// Count the number of operands for each of the successors after, and
// including, the one at 'index'. This is based upon the assumption that all
// non successor operands are placed at the beginning of the operand list.
auto *successorOpCountBegin = getTrailingObjects<unsigned>();
unsigned postSuccessorOpCount =
std::accumulate(successorOpCountBegin + index,
successorOpCountBegin + getNumSuccessors(), 0u);
return getNumOperands() - postSuccessorOpCount;
}
auto Operation::getSuccessorOperands(unsigned index) -> operand_range {
unsigned succOperandIndex = getSuccessorOperandIndex(index);
return {operand_iterator(this, succOperandIndex),
operand_iterator(this,
succOperandIndex + getNumSuccessorOperands(index))};
}
/// Attempt to constant fold this operation with the specified constant
/// operand values. If successful, this fills in the results vector. If not,
/// results is unspecified.
LogicalResult Operation::constantFold(ArrayRef<Attribute> operands,
SmallVectorImpl<Attribute> &results) {
if (auto *abstractOp = getAbstractOperation()) {
// If we have a registered operation definition matching this one, use it to
// try to constant fold the operation.
if (succeeded(abstractOp->constantFoldHook(this, operands, results)))
return success();
// Otherwise, fall back on the dialect hook to handle it.
return abstractOp->dialect.constantFoldHook(this, operands, results);
}
// If this operation hasn't been registered or doesn't have abstract
// operation, fall back to a dialect which matches the prefix.
auto opName = getName().getStringRef();
auto dialectPrefix = opName.split('.').first;
if (auto *dialect = getContext()->getRegisteredDialect(dialectPrefix))
return dialect->constantFoldHook(this, operands, results);
return failure();
}
/// Attempt to fold this operation using the Op's registered foldHook.
LogicalResult Operation::fold(SmallVectorImpl<Value *> &results) {
if (auto *abstractOp = getAbstractOperation()) {
// If we have a registered operation definition matching this one, use it to
// try to constant fold the operation.
if (succeeded(abstractOp->foldHook(this, results)))
return success();
}
return failure();
}
/// Emit an error with the op name prefixed, like "'dim' op " which is
/// convenient for verifiers.
bool Operation::emitOpError(const Twine &message) {
return emitError(Twine('\'') + getName().getStringRef() + "' op " + message);
}
//===----------------------------------------------------------------------===//
// Operation Cloning
//===----------------------------------------------------------------------===//
/// Create a deep copy of this operation, remapping any operands that use
/// values outside of the operation using the map that is provided (leaving
/// them alone if no entry is present). Replaces references to cloned
/// sub-operations to the corresponding operation that is copied, and adds
/// those mappings to the map.
Operation *Operation::clone(BlockAndValueMapping &mapper,
MLIRContext *context) {
SmallVector<Value *, 8> operands;
SmallVector<Block *, 2> successors;
operands.reserve(getNumOperands() + getNumSuccessors());
if (getNumSuccessors() == 0) {
// Non-branching operations can just add all the operands.
for (auto *opValue : getOperands())
operands.push_back(mapper.lookupOrDefault(opValue));
} else {
// We add the operands separated by nullptr's for each successor.
unsigned firstSuccOperand =
getNumSuccessors() ? getSuccessorOperandIndex(0) : getNumOperands();
auto InstOperands = getInstOperands();
unsigned i = 0;
for (; i != firstSuccOperand; ++i)
operands.push_back(mapper.lookupOrDefault(InstOperands[i].get()));
successors.reserve(getNumSuccessors());
for (unsigned succ = 0, e = getNumSuccessors(); succ != e; ++succ) {
successors.push_back(mapper.lookupOrDefault(getSuccessor(succ)));
// Add sentinel to delineate successor operands.
operands.push_back(nullptr);
// Remap the successors operands.
for (auto *operand : getSuccessorOperands(succ))
operands.push_back(mapper.lookupOrDefault(operand));
}
}
SmallVector<Type, 8> resultTypes;
resultTypes.reserve(getNumResults());
for (auto *result : getResults())
resultTypes.push_back(result->getType());
unsigned numRegions = getNumRegions();
auto *newOp = Operation::create(getLoc(), getName(), operands, resultTypes,
attrs, successors, numRegions,
hasResizableOperandsList(), context);
// Clone the regions.
for (unsigned i = 0; i != numRegions; ++i)
getRegion(i).cloneInto(&newOp->getRegion(i), mapper, context);
// Remember the mapping of any results.
for (unsigned i = 0, e = getNumResults(); i != e; ++i)
mapper.map(getResult(i), newOp->getResult(i));
return newOp;
}
Operation *Operation::clone(MLIRContext *context) {
BlockAndValueMapping mapper;
return clone(mapper, context);
}
//===----------------------------------------------------------------------===//
// OpState trait class.
//===----------------------------------------------------------------------===//
@@ -93,19 +652,19 @@ void OpState::emitNote(const Twine &message) {
// Op Trait implementations
//===----------------------------------------------------------------------===//
bool OpTrait::impl::verifyZeroOperands(Instruction *op) {
bool OpTrait::impl::verifyZeroOperands(Operation *op) {
if (op->getNumOperands() != 0)
return op->emitOpError("requires zero operands");
return false;
}
bool OpTrait::impl::verifyOneOperand(Instruction *op) {
bool OpTrait::impl::verifyOneOperand(Operation *op) {
if (op->getNumOperands() != 1)
return op->emitOpError("requires a single operand");
return false;
}
bool OpTrait::impl::verifyNOperands(Instruction *op, unsigned numOperands) {
bool OpTrait::impl::verifyNOperands(Operation *op, unsigned numOperands) {
if (op->getNumOperands() != numOperands) {
return op->emitOpError("expected " + Twine(numOperands) +
" operands, but found " +
@@ -114,7 +673,7 @@ bool OpTrait::impl::verifyNOperands(Instruction *op, unsigned numOperands) {
return false;
}
bool OpTrait::impl::verifyAtLeastNOperands(Instruction *op,
bool OpTrait::impl::verifyAtLeastNOperands(Operation *op,
unsigned numOperands) {
if (op->getNumOperands() < numOperands)
return op->emitOpError("expected " + Twine(numOperands) +
@@ -134,7 +693,7 @@ static Type getTensorOrVectorElementType(Type type) {
return type;
}
bool OpTrait::impl::verifyOperandsAreIntegerLike(Instruction *op) {
bool OpTrait::impl::verifyOperandsAreIntegerLike(Operation *op) {
for (auto *operand : op->getOperands()) {
auto type = getTensorOrVectorElementType(operand->getType());
if (!type.isIntOrIndex())
@@ -143,7 +702,7 @@ bool OpTrait::impl::verifyOperandsAreIntegerLike(Instruction *op) {
return false;
}
bool OpTrait::impl::verifySameTypeOperands(Instruction *op) {
bool OpTrait::impl::verifySameTypeOperands(Operation *op) {
// Zero or one operand always have the "same" type.
unsigned nOperands = op->getNumOperands();
if (nOperands < 2)
@@ -157,26 +716,25 @@ bool OpTrait::impl::verifySameTypeOperands(Instruction *op) {
return false;
}
bool OpTrait::impl::verifyZeroResult(Instruction *op) {
bool OpTrait::impl::verifyZeroResult(Operation *op) {
if (op->getNumResults() != 0)
return op->emitOpError("requires zero results");
return false;
}
bool OpTrait::impl::verifyOneResult(Instruction *op) {
bool OpTrait::impl::verifyOneResult(Operation *op) {
if (op->getNumResults() != 1)
return op->emitOpError("requires one result");
return false;
}
bool OpTrait::impl::verifyNResults(Instruction *op, unsigned numOperands) {
bool OpTrait::impl::verifyNResults(Operation *op, unsigned numOperands) {
if (op->getNumResults() != numOperands)
return op->emitOpError("expected " + Twine(numOperands) + " results");
return false;
}
bool OpTrait::impl::verifyAtLeastNResults(Instruction *op,
unsigned numOperands) {
bool OpTrait::impl::verifyAtLeastNResults(Operation *op, unsigned numOperands) {
if (op->getNumResults() < numOperands)
return op->emitOpError("expected " + Twine(numOperands) +
" or more results");
@@ -204,7 +762,7 @@ static bool verifyShapeMatch(Type type1, Type type2) {
return false;
}
bool OpTrait::impl::verifySameOperandsAndResultShape(Instruction *op) {
bool OpTrait::impl::verifySameOperandsAndResultShape(Operation *op) {
if (op->getNumOperands() == 0 || op->getNumResults() == 0)
return true;
@@ -222,7 +780,7 @@ bool OpTrait::impl::verifySameOperandsAndResultShape(Instruction *op) {
return false;
}
bool OpTrait::impl::verifySameOperandsAndResultType(Instruction *op) {
bool OpTrait::impl::verifySameOperandsAndResultType(Operation *op) {
if (op->getNumOperands() == 0 || op->getNumResults() == 0)
return true;
@@ -241,8 +799,8 @@ bool OpTrait::impl::verifySameOperandsAndResultType(Instruction *op) {
}
static bool
verifyBBArguments(llvm::iterator_range<Instruction::operand_iterator> operands,
Block *destBB, Instruction *op) {
verifyBBArguments(llvm::iterator_range<Operation::operand_iterator> operands,
Block *destBB, Operation *op) {
unsigned operandCount = std::distance(operands.begin(), operands.end());
if (operandCount != destBB->getNumArguments())
return op->emitError("branch has " + Twine(operandCount) +
@@ -258,7 +816,7 @@ verifyBBArguments(llvm::iterator_range<Instruction::operand_iterator> operands,
return false;
}
static bool verifyTerminatorSuccessors(Instruction *op) {
static bool verifyTerminatorSuccessors(Operation *op) {
// Verify that the operands lines up with the BB arguments in the successor.
Function *fn = op->getFunction();
for (unsigned i = 0, e = op->getNumSuccessors(); i != e; ++i) {
@@ -271,11 +829,11 @@ static bool verifyTerminatorSuccessors(Instruction *op) {
return false;
}
bool OpTrait::impl::verifyIsTerminator(Instruction *op) {
bool OpTrait::impl::verifyIsTerminator(Operation *op) {
Block *block = op->getBlock();
// Verify that the operation is at the end of the respective parent block.
if (!block || &block->back() != op)
return op->emitOpError("must be the last instruction in the parent block");
return op->emitOpError("must be the last operation in the parent block");
// Verify the state of the successor blocks.
if (op->getNumSuccessors() != 0 && verifyTerminatorSuccessors(op))
@@ -283,7 +841,7 @@ bool OpTrait::impl::verifyIsTerminator(Instruction *op) {
return false;
}
bool OpTrait::impl::verifyResultsAreBoolLike(Instruction *op) {
bool OpTrait::impl::verifyResultsAreBoolLike(Operation *op) {
for (auto *result : op->getResults()) {
auto elementType = getTensorOrVectorElementType(result->getType());
bool isBoolType = elementType.isInteger(1);
@@ -294,7 +852,7 @@ bool OpTrait::impl::verifyResultsAreBoolLike(Instruction *op) {
return false;
}
bool OpTrait::impl::verifyResultsAreFloatLike(Instruction *op) {
bool OpTrait::impl::verifyResultsAreFloatLike(Operation *op) {
for (auto *result : op->getResults()) {
if (!getTensorOrVectorElementType(result->getType()).isa<FloatType>())
return op->emitOpError("requires a floating point type");
@@ -303,7 +861,7 @@ bool OpTrait::impl::verifyResultsAreFloatLike(Instruction *op) {
return false;
}
bool OpTrait::impl::verifyResultsAreIntegerLike(Instruction *op) {
bool OpTrait::impl::verifyResultsAreIntegerLike(Operation *op) {
for (auto *result : op->getResults()) {
auto type = getTensorOrVectorElementType(result->getType());
if (!type.isIntOrIndex())
@@ -336,7 +894,7 @@ bool impl::parseBinaryOp(OpAsmParser *parser, OperationState *result) {
parser->addTypeToList(type, result->types);
}
void impl::printBinaryOp(Instruction *op, OpAsmPrinter *p) {
void impl::printBinaryOp(Operation *op, OpAsmPrinter *p) {
assert(op->getNumOperands() == 2 && "binary op should have two operands");
assert(op->getNumResults() == 1 && "binary op should have one result");
@@ -375,7 +933,7 @@ bool impl::parseCastOp(OpAsmParser *parser, OperationState *result) {
parser->addTypeToList(dstType, result->types);
}
void impl::printCastOp(Instruction *op, OpAsmPrinter *p) {
void impl::printCastOp(Operation *op, OpAsmPrinter *p) {
*p << op->getName() << ' ' << *op->getOperand(0) << " : "
<< op->getOperand(0)->getType() << " to " << op->getResult(0)->getType();
}

View File

@@ -16,14 +16,18 @@
// =============================================================================
//
// This file contains out-of-line implementations of the support types that
// Instruction and related classes build on top of.
// Operation and related classes build on top of.
//
//===----------------------------------------------------------------------===//
#include "mlir/IR/OperationSupport.h"
#include "mlir/IR/Block.h"
#include "mlir/IR/Operation.h"
using namespace mlir;
namespace mlir {
//===----------------------------------------------------------------------===//
// OperationState
//===----------------------------------------------------------------------===//
OperationState::OperationState(MLIRContext *context, Location location,
StringRef name)
@@ -59,4 +63,75 @@ void OperationState::addRegion(std::unique_ptr<Region> &&region) {
regions.push_back(std::move(region));
}
} // end namespace mlir
//===----------------------------------------------------------------------===//
// OperandStorage
//===----------------------------------------------------------------------===//
/// Replace the operands contained in the storage with the ones provided in
/// 'operands'.
void detail::OperandStorage::setOperands(Operation *owner,
ArrayRef<Value *> operands) {
// If the number of operands is less than or equal to the current amount, we
// can just update in place.
if (operands.size() <= numOperands) {
auto instOperands = getInstOperands();
// If the number of new operands is less than the current count, then remove
// any extra operands.
for (unsigned i = operands.size(); i != numOperands; ++i)
instOperands[i].~InstOperand();
// Set the operands in place.
numOperands = operands.size();
for (unsigned i = 0; i != numOperands; ++i)
instOperands[i].set(operands[i]);
return;
}
// Otherwise, we need to be resizable.
assert(resizable && "Only resizable operations may add operands");
// Grow the capacity if necessary.
auto &resizeUtil = getResizableStorage();
if (resizeUtil.capacity < operands.size())
grow(resizeUtil, operands.size());
// Set the operands.
InstOperand *opBegin = getRawOperands();
for (unsigned i = 0; i != numOperands; ++i)
opBegin[i].set(operands[i]);
for (unsigned e = operands.size(); numOperands != e; ++numOperands)
new (&opBegin[numOperands]) InstOperand(owner, operands[numOperands]);
}
/// Erase an operand held by the storage.
void detail::OperandStorage::eraseOperand(unsigned index) {
assert(index < size());
auto Operands = getInstOperands();
--numOperands;
// Shift all operands down by 1 if the operand to remove is not at the end.
if (index != numOperands)
std::rotate(&Operands[index], &Operands[index + 1], &Operands[numOperands]);
Operands[numOperands].~InstOperand();
}
/// Grow the internal operand storage.
void detail::OperandStorage::grow(ResizableStorage &resizeUtil,
size_t minSize) {
// Allocate a new storage array.
resizeUtil.capacity =
std::max(size_t(llvm::NextPowerOf2(resizeUtil.capacity + 2)), minSize);
InstOperand *newStorage = static_cast<InstOperand *>(
llvm::safe_malloc(resizeUtil.capacity * sizeof(InstOperand)));
// Move the current operands to the new storage.
auto operands = getInstOperands();
std::uninitialized_copy(std::make_move_iterator(operands.begin()),
std::make_move_iterator(operands.end()), newStorage);
// Destroy the original operands and update the resizable storage pointer.
for (auto &operand : operands)
operand.~InstOperand();
resizeUtil.setDynamicStorage(newStorage);
}

View File

@@ -16,7 +16,7 @@
// =============================================================================
#include "mlir/IR/PatternMatch.h"
#include "mlir/IR/Instruction.h"
#include "mlir/IR/Operation.h"
#include "mlir/IR/Value.h"
using namespace mlir;

View File

@@ -17,7 +17,7 @@
#include "mlir/IR/Value.h"
#include "mlir/IR/Function.h"
#include "mlir/IR/Instruction.h"
#include "mlir/IR/Operation.h"
using namespace mlir;
/// If this value is the result of an Instruction, return the instruction

View File

@@ -21,8 +21,8 @@
#include "mlir/Analysis/AffineStructures.h"
#include "mlir/IR/Function.h"
#include "mlir/IR/Instruction.h"
#include "mlir/IR/IntegerSet.h"
#include "mlir/IR/Operation.h"
#include "mlir/Pass/Pass.h"
#include "mlir/Transforms/Passes.h"

View File

@@ -16,7 +16,7 @@
// =============================================================================
#include "mlir/IR/Function.h"
#include "mlir/IR/Instruction.h"
#include "mlir/IR/Operation.h"
#include "mlir/Pass/Pass.h"
#include "mlir/Transforms/Passes.h"

View File

@@ -29,7 +29,7 @@
#include "mlir/IR/AffineMap.h"
#include "mlir/IR/BlockAndValueMapping.h"
#include "mlir/IR/Builders.h"
#include "mlir/IR/Instruction.h"
#include "mlir/IR/Operation.h"
#include "mlir/StandardOps/Ops.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/Support/Debug.h"

View File

@@ -1,4 +1,4 @@
//===- InstructionSupportTest.cpp - Instruction support unit tests --------===//
//===- OperationSupportTest.cpp - Operation support unit tests ------------===//
//
// Copyright 2019 The MLIR Authors.
//
@@ -15,7 +15,7 @@
// limitations under the License.
// =============================================================================
#include "mlir/IR/InstructionSupport.h"
#include "mlir/IR/OperationSupport.h"
#include "mlir/IR/Builders.h"
#include "mlir/IR/StandardTypes.h"
#include "gtest/gtest.h"
@@ -24,10 +24,10 @@ using namespace mlir;
using namespace mlir::detail;
namespace {
Instruction *createInst(MLIRContext *context, bool resizableOperands,
ArrayRef<Value *> operands = llvm::None,
ArrayRef<Type> resultTypes = llvm::None) {
return Instruction::create(
Operation *createOp(MLIRContext *context, bool resizableOperands,
ArrayRef<Value *> operands = llvm::None,
ArrayRef<Type> resultTypes = llvm::None) {
return Operation::create(
UnknownLoc::get(context), OperationName("foo.bar", context), operands,
resultTypes, llvm::None, llvm::None, 0, resizableOperands, context);
}
@@ -36,14 +36,14 @@ TEST(OperandStorageTest, NonResizable) {
MLIRContext context;
Builder builder(&context);
Instruction *useInst =
createInst(&context, /*resizableOperands=*/false, /*operands=*/llvm::None,
builder.getIntegerType(16));
Value *operand = useInst->getResult(0);
Operation *useOp =
createOp(&context, /*resizableOperands=*/false, /*operands=*/llvm::None,
builder.getIntegerType(16));
Value *operand = useOp->getResult(0);
// Create a non-resizable instruction with one operand.
Instruction *user = createInst(&context, /*resizableOperands=*/false, operand,
builder.getIntegerType(16));
Operation *user = createOp(&context, /*resizableOperands=*/false, operand,
builder.getIntegerType(16));
// Sanity check the storage.
EXPECT_EQ(user->hasResizableOperandsList(), false);
@@ -58,21 +58,21 @@ TEST(OperandStorageTest, NonResizable) {
// Destroy the instructions.
user->destroy();
useInst->destroy();
useOp->destroy();
}
TEST(OperandStorageDeathTest, AddToNonResizable) {
MLIRContext context;
Builder builder(&context);
Instruction *useInst =
createInst(&context, /*resizableOperands=*/false, /*operands=*/llvm::None,
builder.getIntegerType(16));
Value *operand = useInst->getResult(0);
Operation *useOp =
createOp(&context, /*resizableOperands=*/false, /*operands=*/llvm::None,
builder.getIntegerType(16));
Value *operand = useOp->getResult(0);
// Create a non-resizable instruction with one operand.
Instruction *user = createInst(&context, /*resizableOperands=*/false, operand,
builder.getIntegerType(16));
Operation *user = createOp(&context, /*resizableOperands=*/false, operand,
builder.getIntegerType(16));
// Sanity check the storage.
EXPECT_EQ(user->hasResizableOperandsList(), false);
@@ -85,14 +85,14 @@ TEST(OperandStorageTest, Resizable) {
MLIRContext context;
Builder builder(&context);
Instruction *useInst =
createInst(&context, /*resizableOperands=*/false, /*operands=*/llvm::None,
builder.getIntegerType(16));
Value *operand = useInst->getResult(0);
Operation *useOp =
createOp(&context, /*resizableOperands=*/false, /*operands=*/llvm::None,
builder.getIntegerType(16));
Value *operand = useOp->getResult(0);
// Create a resizable instruction with one operand.
Instruction *user = createInst(&context, /*resizableOperands=*/true, operand,
builder.getIntegerType(16));
Operation *user = createOp(&context, /*resizableOperands=*/true, operand,
builder.getIntegerType(16));
// Sanity check the storage.
EXPECT_EQ(user->hasResizableOperandsList(), true);
@@ -111,7 +111,7 @@ TEST(OperandStorageTest, Resizable) {
// Destroy the instructions.
user->destroy();
useInst->destroy();
useOp->destroy();
}
} // end namespace