//===- 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/BuiltinTypes.h" #include "mlir/IR/Operation.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(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()) return arg.getType(); // If this is an operation result, query the parent operation. OpResult result = cast(); Operation *owner = result.getOwner(); if (owner->hasSingleResult) return owner->resultType; return owner->resultType.cast().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()) return arg.setType(newType); OpResult result = cast(); // 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().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()) return result.getOwner(); return nullptr; } Location Value::getLoc() const { if (auto *op = getDefiningOp()) return op->getLoc(); // Use the location of the parent operation if this is a block argument. // TODO: Should we just add locations to block arguments? Operation *parentOp = cast().getOwner()->getParentOp(); return parentOp ? parentOp->getLoc() : UnknownLoc::get(getContext()); } /// Return the Region in which this Value is defined. Region *Value::getParentRegion() { if (auto *op = getDefiningOp()) return op->getParentRegion(); return cast().getOwner()->getParent(); } /// Return the Block in which this Value is defined. Block *Value::getParentBlock() { if (Operation *op = getDefiningOp()) return op->getBlock(); return cast().getOwner(); } //===----------------------------------------------------------------------===// // Value::UseLists //===----------------------------------------------------------------------===// /// Provide the use list that is attached to this value. IRObjectWithUseList *Value::getUseList() const { if (BlockArgument arg = dyn_cast()) return arg.getImpl(); if (getKind() != Kind::TrailingOpResult) { OpResult result = cast(); return result.getOwner()->getInlineResult(result.getResultNumber()); } // Otherwise this is a trailing operation result, which contains a use list. return reinterpret_cast(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 &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 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(owner); // Otherwise, query the trailing result for the owner. return static_cast(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(ownerAndKind.getInt()); // Otherwise, query the trailing result. auto *result = static_cast(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::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::getUseList(Value value) { return value.getUseList(); } /// Return the current value being used by this operand. Value OpOperand::get() const { return IROperand::get(); } /// Set the operand to the given value. void OpOperand::set(Value value) { IROperand::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); }