1 //===-- BoxValue.cpp ------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Pretty printers for box values, etc.
10 //
11 //===----------------------------------------------------------------------===//
12
13 #include "flang/Optimizer/Builder/BoxValue.h"
14 #include "flang/Optimizer/Builder/FIRBuilder.h"
15 #include "flang/Optimizer/Builder/Todo.h"
16 #include "mlir/IR/BuiltinTypes.h"
17 #include "llvm/Support/Debug.h"
18
19 #define DEBUG_TYPE "flang-box-value"
20
getBase(const fir::ExtendedValue & exv)21 mlir::Value fir::getBase(const fir::ExtendedValue &exv) {
22 return exv.match([](const fir::UnboxedValue &x) { return x; },
23 [](const auto &x) { return x.getAddr(); });
24 }
25
getLen(const fir::ExtendedValue & exv)26 mlir::Value fir::getLen(const fir::ExtendedValue &exv) {
27 return exv.match(
28 [](const fir::CharBoxValue &x) { return x.getLen(); },
29 [](const fir::CharArrayBoxValue &x) { return x.getLen(); },
30 [](const fir::BoxValue &) -> mlir::Value {
31 llvm::report_fatal_error("Need to read len from BoxValue Exv");
32 },
33 [](const fir::MutableBoxValue &) -> mlir::Value {
34 llvm::report_fatal_error("Need to read len from MutableBoxValue Exv");
35 },
36 [](const auto &) { return mlir::Value{}; });
37 }
38
substBase(const fir::ExtendedValue & exv,mlir::Value base)39 fir::ExtendedValue fir::substBase(const fir::ExtendedValue &exv,
40 mlir::Value base) {
41 return exv.match(
42 [=](const fir::UnboxedValue &x) { return fir::ExtendedValue(base); },
43 [=](const auto &x) { return fir::ExtendedValue(x.clone(base)); });
44 }
45
46 llvm::SmallVector<mlir::Value>
getTypeParams(const fir::ExtendedValue & exv)47 fir::getTypeParams(const fir::ExtendedValue &exv) {
48 using RT = llvm::SmallVector<mlir::Value>;
49 auto baseTy = fir::getBase(exv).getType();
50 if (auto t = fir::dyn_cast_ptrEleTy(baseTy))
51 baseTy = t;
52 baseTy = fir::unwrapSequenceType(baseTy);
53 if (!fir::hasDynamicSize(baseTy))
54 return {}; // type has constant size, no type parameters needed
55 [[maybe_unused]] auto loc = fir::getBase(exv).getLoc();
56 return exv.match(
57 [](const fir::CharBoxValue &x) -> RT { return {x.getLen()}; },
58 [](const fir::CharArrayBoxValue &x) -> RT { return {x.getLen()}; },
59 [&](const fir::BoxValue &) -> RT {
60 TODO(loc, "box value is missing type parameters");
61 return {};
62 },
63 [&](const fir::MutableBoxValue &) -> RT {
64 // In this case, the type params may be bound to the variable in an
65 // ALLOCATE statement as part of a type-spec.
66 TODO(loc, "mutable box value is missing type parameters");
67 return {};
68 },
69 [](const auto &) -> RT { return {}; });
70 }
71
isArray(const fir::ExtendedValue & exv)72 bool fir::isArray(const fir::ExtendedValue &exv) {
73 return exv.match(
74 [](const fir::ArrayBoxValue &) { return true; },
75 [](const fir::CharArrayBoxValue &) { return true; },
76 [](const fir::BoxValue &box) { return box.hasRank(); },
77 [](const fir::MutableBoxValue &box) { return box.hasRank(); },
78 [](auto) { return false; });
79 }
80
operator <<(llvm::raw_ostream & os,const fir::CharBoxValue & box)81 llvm::raw_ostream &fir::operator<<(llvm::raw_ostream &os,
82 const fir::CharBoxValue &box) {
83 return os << "boxchar { addr: " << box.getAddr() << ", len: " << box.getLen()
84 << " }";
85 }
86
operator <<(llvm::raw_ostream & os,const fir::ArrayBoxValue & box)87 llvm::raw_ostream &fir::operator<<(llvm::raw_ostream &os,
88 const fir::ArrayBoxValue &box) {
89 os << "boxarray { addr: " << box.getAddr();
90 if (box.getLBounds().size()) {
91 os << ", lbounds: [";
92 llvm::interleaveComma(box.getLBounds(), os);
93 os << "]";
94 } else {
95 os << ", lbounds: all-ones";
96 }
97 os << ", shape: [";
98 llvm::interleaveComma(box.getExtents(), os);
99 return os << "]}";
100 }
101
operator <<(llvm::raw_ostream & os,const fir::CharArrayBoxValue & box)102 llvm::raw_ostream &fir::operator<<(llvm::raw_ostream &os,
103 const fir::CharArrayBoxValue &box) {
104 os << "boxchararray { addr: " << box.getAddr() << ", len : " << box.getLen();
105 if (box.getLBounds().size()) {
106 os << ", lbounds: [";
107 llvm::interleaveComma(box.getLBounds(), os);
108 os << "]";
109 } else {
110 os << " lbounds: all-ones";
111 }
112 os << ", shape: [";
113 llvm::interleaveComma(box.getExtents(), os);
114 return os << "]}";
115 }
116
operator <<(llvm::raw_ostream & os,const fir::ProcBoxValue & box)117 llvm::raw_ostream &fir::operator<<(llvm::raw_ostream &os,
118 const fir::ProcBoxValue &box) {
119 return os << "boxproc: { procedure: " << box.getAddr()
120 << ", context: " << box.hostContext << "}";
121 }
122
operator <<(llvm::raw_ostream & os,const fir::BoxValue & box)123 llvm::raw_ostream &fir::operator<<(llvm::raw_ostream &os,
124 const fir::BoxValue &box) {
125 os << "box: { value: " << box.getAddr();
126 if (box.lbounds.size()) {
127 os << ", lbounds: [";
128 llvm::interleaveComma(box.lbounds, os);
129 os << "]";
130 }
131 if (!box.explicitParams.empty()) {
132 os << ", explicit type params: [";
133 llvm::interleaveComma(box.explicitParams, os);
134 os << "]";
135 }
136 if (!box.extents.empty()) {
137 os << ", explicit extents: [";
138 llvm::interleaveComma(box.extents, os);
139 os << "]";
140 }
141 return os << "}";
142 }
143
operator <<(llvm::raw_ostream & os,const fir::MutableBoxValue & box)144 llvm::raw_ostream &fir::operator<<(llvm::raw_ostream &os,
145 const fir::MutableBoxValue &box) {
146 os << "mutablebox: { addr: " << box.getAddr();
147 if (!box.lenParams.empty()) {
148 os << ", non deferred type params: [";
149 llvm::interleaveComma(box.lenParams, os);
150 os << "]";
151 }
152 const auto &properties = box.mutableProperties;
153 if (!properties.isEmpty()) {
154 os << ", mutableProperties: { addr: " << properties.addr;
155 if (!properties.lbounds.empty()) {
156 os << ", lbounds: [";
157 llvm::interleaveComma(properties.lbounds, os);
158 os << "]";
159 }
160 if (!properties.extents.empty()) {
161 os << ", shape: [";
162 llvm::interleaveComma(properties.extents, os);
163 os << "]";
164 }
165 if (!properties.deferredParams.empty()) {
166 os << ", deferred type params: [";
167 llvm::interleaveComma(properties.deferredParams, os);
168 os << "]";
169 }
170 os << "}";
171 }
172 return os << "}";
173 }
174
operator <<(llvm::raw_ostream & os,const fir::ExtendedValue & exv)175 llvm::raw_ostream &fir::operator<<(llvm::raw_ostream &os,
176 const fir::ExtendedValue &exv) {
177 exv.match([&](const auto &value) { os << value; });
178 return os;
179 }
180
181 /// Debug verifier for MutableBox ctor. There is no guarantee that this will
182 /// always be called, so it should not have any functional side effects,
183 /// the const is here to enforce that.
verify() const184 bool fir::MutableBoxValue::verify() const {
185 mlir::Type type = fir::dyn_cast_ptrEleTy(getAddr().getType());
186 if (!type)
187 return false;
188 auto box = type.dyn_cast<fir::BoxType>();
189 if (!box)
190 return false;
191 // A boxed value always takes a memory reference,
192
193 auto nParams = lenParams.size();
194 if (isCharacter()) {
195 if (nParams > 1)
196 return false;
197 } else if (!isDerived()) {
198 if (nParams != 0)
199 return false;
200 }
201 return true;
202 }
203
204 /// Debug verifier for BoxValue ctor. There is no guarantee this will
205 /// always be called.
verify() const206 bool fir::BoxValue::verify() const {
207 if (!addr.getType().isa<fir::BoxType>())
208 return false;
209 if (!lbounds.empty() && lbounds.size() != rank())
210 return false;
211 // Explicit extents are here to cover cases where an explicit-shape dummy
212 // argument comes as a fir.box. This can only happen with derived types and
213 // unlimited polymorphic.
214 if (!extents.empty() && !(isDerived() || isUnlimitedPolymorphic()))
215 return false;
216 if (!extents.empty() && extents.size() != rank())
217 return false;
218 if (isCharacter() && explicitParams.size() > 1)
219 return false;
220 return true;
221 }
222
223 /// Get exactly one extent for any array-like extended value, \p exv. If \p exv
224 /// is not an array or has rank less then \p dim, the result will be a nullptr.
getExtentAtDimension(mlir::Location loc,fir::FirOpBuilder & builder,const fir::ExtendedValue & exv,unsigned dim)225 mlir::Value fir::factory::getExtentAtDimension(mlir::Location loc,
226 fir::FirOpBuilder &builder,
227 const fir::ExtendedValue &exv,
228 unsigned dim) {
229 auto extents = fir::factory::getExtents(loc, builder, exv);
230 if (dim < extents.size())
231 return extents[dim];
232 return {};
233 }
234
isUndefOp(mlir::Value v)235 static inline bool isUndefOp(mlir::Value v) {
236 return mlir::isa_and_nonnull<fir::UndefOp>(v.getDefiningOp());
237 }
238
isAssumedSize() const239 bool fir::ExtendedValue::isAssumedSize() const {
240 return match(
241 [](const fir::ArrayBoxValue &box) -> bool {
242 return !box.getExtents().empty() && isUndefOp(box.getExtents().back());
243 ;
244 },
245 [](const fir::CharArrayBoxValue &box) -> bool {
246 return !box.getExtents().empty() && isUndefOp(box.getExtents().back());
247 },
248 [](const auto &box) -> bool { return false; });
249 }
250