| //===- Value.cpp - MLIR Value Classes -------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "mlir/IR/Value.h" |
| #include "mlir/IR/Block.h" |
| #include "mlir/IR/Operation.h" |
| #include "mlir/IR/StandardTypes.h" |
| #include "llvm/ADT/SmallPtrSet.h" |
| |
| using namespace mlir; |
| using namespace mlir::detail; |
| |
| /// Construct a value. |
| Value::Value(BlockArgumentImpl *impl) |
| : ownerAndKind(impl, Kind::BlockArgument) {} |
| Value::Value(Operation *op, unsigned resultNo) { |
| assert(op->getNumResults() > resultNo && "invalid result number"); |
| if (LLVM_LIKELY(canPackResultInline(resultNo))) { |
| ownerAndKind = {op, static_cast<Kind>(resultNo)}; |
| return; |
| } |
| |
| // If we can't pack the result directly, grab the use list from the parent op. |
| unsigned trailingNo = resultNo - OpResult::getMaxInlineResults(); |
| ownerAndKind = {op->getTrailingResult(trailingNo), Kind::TrailingOpResult}; |
| } |
| |
| /// Return the type of this value. |
| Type Value::getType() const { |
| if (BlockArgument arg = dyn_cast<BlockArgument>()) |
| return arg.getType(); |
| |
| // If this is an operation result, query the parent operation. |
| OpResult result = cast<OpResult>(); |
| Operation *owner = result.getOwner(); |
| if (owner->hasSingleResult) |
| return owner->resultType; |
| return owner->resultType.cast<TupleType>().getType(result.getResultNumber()); |
| } |
| |
| /// Mutate the type of this Value to be of the specified type. |
| void Value::setType(Type newType) { |
| if (BlockArgument arg = dyn_cast<BlockArgument>()) |
| return arg.setType(newType); |
| OpResult result = cast<OpResult>(); |
| |
| // If the owner has a single result, simply update it directly. |
| Operation *owner = result.getOwner(); |
| if (owner->hasSingleResult) { |
| owner->resultType = newType; |
| return; |
| } |
| unsigned resultNo = result.getResultNumber(); |
| |
| // Otherwise, rebuild the tuple if the new type is different from the current. |
| auto curTypes = owner->resultType.cast<TupleType>().getTypes(); |
| if (curTypes[resultNo] == newType) |
| return; |
| auto newTypes = llvm::to_vector<4>(curTypes); |
| newTypes[resultNo] = newType; |
| owner->resultType = TupleType::get(newTypes, newType.getContext()); |
| } |
| |
| /// If this value is the result of an Operation, return the operation that |
| /// defines it. |
| Operation *Value::getDefiningOp() const { |
| if (auto result = dyn_cast<OpResult>()) |
| return result.getOwner(); |
| return nullptr; |
| } |
| |
| Location Value::getLoc() const { |
| if (auto *op = getDefiningOp()) |
| return op->getLoc(); |
| return UnknownLoc::get(getContext()); |
| } |
| |
| /// Return the Region in which this Value is defined. |
| Region *Value::getParentRegion() { |
| if (auto *op = getDefiningOp()) |
| return op->getParentRegion(); |
| return cast<BlockArgument>().getOwner()->getParent(); |
| } |
| |
| /// Return the Block in which this Value is defined. |
| Block *Value::getParentBlock() { |
| if (Operation *op = getDefiningOp()) |
| return op->getBlock(); |
| return cast<BlockArgument>().getOwner(); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Value::UseLists |
| //===----------------------------------------------------------------------===// |
| |
| /// Provide the use list that is attached to this value. |
| IRObjectWithUseList<OpOperand> *Value::getUseList() const { |
| if (BlockArgument arg = dyn_cast<BlockArgument>()) |
| return arg.getImpl(); |
| if (getKind() != Kind::TrailingOpResult) { |
| OpResult result = cast<OpResult>(); |
| return result.getOwner()->getInlineResult(result.getResultNumber()); |
| } |
| |
| // Otherwise this is a trailing operation result, which contains a use list. |
| return reinterpret_cast<TrailingOpResult *>(ownerAndKind.getPointer()); |
| } |
| |
| /// Drop all uses of this object from their respective owners. |
| void Value::dropAllUses() const { return getUseList()->dropAllUses(); } |
| |
| /// Replace all uses of 'this' value with the new value, updating anything in |
| /// the IR that uses 'this' to use the other value instead. When this returns |
| /// there are zero uses of 'this'. |
| void Value::replaceAllUsesWith(Value newValue) const { |
| return getUseList()->replaceAllUsesWith(newValue); |
| } |
| |
| /// Replace all uses of 'this' value with the new value, updating anything in |
| /// the IR that uses 'this' to use the other value instead except if the user is |
| /// listed in 'exceptions' . |
| void Value::replaceAllUsesExcept( |
| Value newValue, const SmallPtrSetImpl<Operation *> &exceptions) const { |
| for (auto &use : llvm::make_early_inc_range(getUses())) { |
| if (exceptions.count(use.getOwner()) == 0) |
| use.set(newValue); |
| } |
| } |
| |
| /// Replace all uses of 'this' value with 'newValue' if the given callback |
| /// returns true. |
| void Value::replaceUsesWithIf(Value newValue, |
| function_ref<bool(OpOperand &)> shouldReplace) { |
| for (OpOperand &use : llvm::make_early_inc_range(getUses())) |
| if (shouldReplace(use)) |
| use.set(newValue); |
| } |
| |
| /// Returns true if the value is used outside of the given block. |
| bool Value::isUsedOutsideOfBlock(Block *block) { |
| return llvm::any_of(getUsers(), [block](Operation *user) { |
| return user->getBlock() != block; |
| }); |
| } |
| |
| //===--------------------------------------------------------------------===// |
| // Uses |
| |
| auto Value::use_begin() const -> use_iterator { |
| return getUseList()->use_begin(); |
| } |
| |
| /// Returns true if this value has exactly one use. |
| bool Value::hasOneUse() const { return getUseList()->hasOneUse(); } |
| |
| /// Returns true if this value has no uses. |
| bool Value::use_empty() const { return getUseList()->use_empty(); } |
| |
| //===----------------------------------------------------------------------===// |
| // OpResult |
| //===----------------------------------------------------------------------===// |
| |
| /// Returns the operation that owns this result. |
| Operation *OpResult::getOwner() const { |
| // If the result is in-place, the `owner` is the operation. |
| void *owner = ownerAndKind.getPointer(); |
| if (LLVM_LIKELY(getKind() != Kind::TrailingOpResult)) |
| return static_cast<Operation *>(owner); |
| |
| // Otherwise, query the trailing result for the owner. |
| return static_cast<TrailingOpResult *>(owner)->getOwner(); |
| } |
| |
| /// Return the result number of this result. |
| unsigned OpResult::getResultNumber() const { |
| // If the result is in-place, we can use the kind directly. |
| if (LLVM_LIKELY(getKind() != Kind::TrailingOpResult)) |
| return static_cast<unsigned>(ownerAndKind.getInt()); |
| // Otherwise, query the trailing result. |
| auto *result = static_cast<TrailingOpResult *>(ownerAndKind.getPointer()); |
| return result->getResultNumber(); |
| } |
| |
| /// Given a number of operation results, returns the number that need to be |
| /// stored inline. |
| unsigned OpResult::getNumInline(unsigned numResults) { |
| return std::min(numResults, getMaxInlineResults()); |
| } |
| |
| /// Given a number of operation results, returns the number that need to be |
| /// stored as trailing. |
| unsigned OpResult::getNumTrailing(unsigned numResults) { |
| // If we can pack all of the results, there is no need for additional storage. |
| unsigned maxInline = getMaxInlineResults(); |
| return numResults <= maxInline ? 0 : numResults - maxInline; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // BlockOperand |
| //===----------------------------------------------------------------------===// |
| |
| /// Provide the use list that is attached to the given block. |
| IRObjectWithUseList<BlockOperand> *BlockOperand::getUseList(Block *value) { |
| return value; |
| } |
| |
| /// Return which operand this is in the operand list. |
| unsigned BlockOperand::getOperandNumber() { |
| return this - &getOwner()->getBlockOperands()[0]; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // OpOperand |
| //===----------------------------------------------------------------------===// |
| |
| /// Provide the use list that is attached to the given value. |
| IRObjectWithUseList<OpOperand> *OpOperand::getUseList(Value value) { |
| return value.getUseList(); |
| } |
| |
| /// Return the current value being used by this operand. |
| Value OpOperand::get() const { |
| return IROperand<OpOperand, OpaqueValue>::get(); |
| } |
| |
| /// Set the operand to the given value. |
| void OpOperand::set(Value value) { |
| IROperand<OpOperand, OpaqueValue>::set(value); |
| } |
| |
| /// Return which operand this is in the operand list. |
| unsigned OpOperand::getOperandNumber() { |
| return this - &getOwner()->getOpOperands()[0]; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // OpaqueValue |
| //===----------------------------------------------------------------------===// |
| |
| /// Implicit conversion from 'Value'. |
| OpaqueValue::OpaqueValue(Value value) : impl(value.getAsOpaquePointer()) {} |
| |
| /// Implicit conversion back to 'Value'. |
| OpaqueValue::operator Value() const { |
| return Value::getFromOpaquePointer(impl); |
| } |