Value.cpp
8.74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
//===- 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);
}