mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-29 19:26:05 +00:00

This adds the ability to specify a location when creating BlockArguments. Notably Value::getLoc() will return this correctly, which makes diagnostics more precise (e.g. the example in test-legalize-type-conversion.mlir). This is currently optional to avoid breaking any existing code - if absent, the BlockArgument defaults to using the location of its enclosing operation (preserving existing behavior). The bulk of this change is plumbing location tracking through the parser and printer to make sure it can round trip (in -mlir-print-debuginfo mode). This is complete for generic operations, but requires manual adoption for custom ops. I added support for function-like ops to round trip their argument locations - they print correctly, but when parsing the locations are dropped on the floor. I intend to fix this, but it will require more invasive plumbing through "function_like_impl" stuff so I think it best to split it out to its own patch. Differential Revision: https://reviews.llvm.org/D102567
249 lines
9.1 KiB
C++
249 lines
9.1 KiB
C++
//===- 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;
|
|
|
|
/// 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 cast<BlockArgument>().getLoc();
|
|
}
|
|
|
|
void Value::setLoc(Location loc) {
|
|
if (auto *op = getDefiningOp())
|
|
return op->setLoc(loc);
|
|
|
|
return cast<BlockArgument>().setLoc(loc);
|
|
}
|
|
|
|
/// 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
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
/// 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 (OpOperand &use : llvm::make_early_inc_range(getUses())) {
|
|
if (exceptions.count(use.getOwner()) == 0)
|
|
use.set(newValue);
|
|
}
|
|
}
|
|
|
|
/// Replace all uses of 'this' value with 'newValue', updating anything in the
|
|
/// IR that uses 'this' to use the other value instead except if the user is
|
|
/// 'exceptedUser'.
|
|
void Value::replaceAllUsesExcept(Value newValue,
|
|
Operation *exceptedUser) const {
|
|
for (OpOperand &use : llvm::make_early_inc_range(getUses())) {
|
|
if (use.getOwner() != exceptedUser)
|
|
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;
|
|
});
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// OpResult
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
/// Returns the parent operation of this trailing result.
|
|
Operation *OpResultImpl::getOwner() const {
|
|
// We need to do some arithmetic to get the operation pointer. Results are
|
|
// stored in reverse order before the operation, so move the trailing owner up
|
|
// to the start of the array. A rough diagram of the memory layout is:
|
|
//
|
|
// | Out-of-Line results | Inline results | Operation |
|
|
//
|
|
// Given that the results are reverse order we use the result number to know
|
|
// how far to jump to get to the operation. So if we are currently the 0th
|
|
// result, the layout would be:
|
|
//
|
|
// | Inline result 0 | Operation
|
|
//
|
|
// ^-- To get the base address of the operation, we add the result count + 1.
|
|
if (const auto *result = dyn_cast<InlineOpResult>(this)) {
|
|
result += result->getResultNumber() + 1;
|
|
return reinterpret_cast<Operation *>(const_cast<InlineOpResult *>(result));
|
|
}
|
|
|
|
// Out-of-line results are stored in an array just before the inline results.
|
|
const OutOfLineOpResult *outOfLineIt = (const OutOfLineOpResult *)(this);
|
|
outOfLineIt += (outOfLineIt->outOfLineIndex + 1);
|
|
|
|
// Move the owner past the inline results to get to the operation.
|
|
const auto *inlineIt = reinterpret_cast<const InlineOpResult *>(outOfLineIt);
|
|
inlineIt += getMaxInlineResults();
|
|
return reinterpret_cast<Operation *>(const_cast<InlineOpResult *>(inlineIt));
|
|
}
|
|
|
|
OpResultImpl *OpResultImpl::getNextResultAtOffset(intptr_t offset) {
|
|
if (offset == 0)
|
|
return this;
|
|
// We need to do some arithmetic to get the next result given that results are
|
|
// in reverse order, and that we need to account for the different types of
|
|
// results. As a reminder, the rough diagram of the memory layout is:
|
|
//
|
|
// | Out-of-Line results | Inline results | Operation |
|
|
//
|
|
// So an example operation with two results would look something like:
|
|
//
|
|
// | Inline result 1 | Inline result 0 | Operation |
|
|
//
|
|
|
|
// Handle the case where this result is an inline result.
|
|
OpResultImpl *result = this;
|
|
if (auto *inlineResult = dyn_cast<InlineOpResult>(this)) {
|
|
// Check to see how many results there are after this one before the start
|
|
// of the out-of-line results. If the desired offset is less than the number
|
|
// remaining, we can directly use the offset from the current result
|
|
// pointer. The following diagrams highlight the two situations.
|
|
//
|
|
// | Out-of-Line results | Inline results | Operation |
|
|
// ^- Say we are here.
|
|
// ^- If our destination is here, we can use the
|
|
// offset directly.
|
|
//
|
|
intptr_t leftBeforeTrailing =
|
|
getMaxInlineResults() - inlineResult->getResultNumber() - 1;
|
|
if (leftBeforeTrailing >= offset)
|
|
return inlineResult - offset;
|
|
|
|
// Otherwise, adjust the current result pointer to the end (start in memory)
|
|
// of the inline result array.
|
|
//
|
|
// | Out-of-Line results | Inline results | Operation |
|
|
// ^- Say we are here.
|
|
// ^- If our destination is here, we need to first jump to
|
|
// the end (start in memory) of the inline result array.
|
|
//
|
|
result = inlineResult - leftBeforeTrailing;
|
|
offset -= leftBeforeTrailing;
|
|
}
|
|
|
|
// If we land here, the current result is an out-of-line result and we can
|
|
// offset directly.
|
|
return reinterpret_cast<OutOfLineOpResult *>(result) - offset;
|
|
}
|
|
|
|
/// Given a number of operation results, returns the number that need to be
|
|
/// stored inline.
|
|
unsigned OpResult::getNumInline(unsigned numResults) {
|
|
return std::min(numResults, OpResultImpl::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 = OpResultImpl::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);
|
|
}
|