1//===-- PolymorphicOpConversion.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#include "flang/Lower/BuiltinModules.h"
10#include "flang/Optimizer/Builder/Todo.h"
11#include "flang/Optimizer/Dialect/FIRDialect.h"
12#include "flang/Optimizer/Dialect/FIROps.h"
13#include "flang/Optimizer/Dialect/FIROpsSupport.h"
14#include "flang/Optimizer/Dialect/FIRType.h"
15#include "flang/Optimizer/Dialect/Support/FIRContext.h"
16#include "flang/Optimizer/Dialect/Support/KindMapping.h"
17#include "flang/Optimizer/Support/InternalNames.h"
18#include "flang/Optimizer/Support/TypeCode.h"
19#include "flang/Optimizer/Transforms/Passes.h"
20#include "flang/Runtime/derived-api.h"
21#include "flang/Semantics/runtime-type-info.h"
22#include "mlir/Dialect/Affine/IR/AffineOps.h"
23#include "mlir/Dialect/Arith/IR/Arith.h"
24#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
25#include "mlir/Dialect/Func/IR/FuncOps.h"
26#include "mlir/IR/BuiltinOps.h"
27#include "mlir/Pass/Pass.h"
28#include "mlir/Transforms/DialectConversion.h"
29#include "llvm/ADT/SmallSet.h"
30#include "llvm/Support/CommandLine.h"
31
32namespace fir {
33#define GEN_PASS_DEF_POLYMORPHICOPCONVERSION
34#include "flang/Optimizer/Transforms/Passes.h.inc"
35} // namespace fir
36
37using namespace fir;
38using namespace mlir;
39
40// Reconstruct binding tables for dynamic dispatch.
41using BindingTable = llvm::DenseMap<llvm::StringRef, unsigned>;
42using BindingTables = llvm::DenseMap<llvm::StringRef, BindingTable>;
43
44static std::string getTypeDescriptorTypeName() {
45 llvm::SmallVector<llvm::StringRef, 1> modules = {
46 Fortran::semantics::typeInfoBuiltinModule};
47 return fir::NameUniquer::doType(modules, /*proc=*/{}, /*blockId=*/0,
48 Fortran::semantics::typeDescriptorTypeName,
49 /*kinds=*/{});
50}
51
52static std::optional<mlir::Type>
53buildBindingTables(BindingTables &bindingTables, mlir::ModuleOp mod) {
54
55 std::optional<mlir::Type> typeDescriptorType;
56 std::string typeDescriptorTypeName = getTypeDescriptorTypeName();
57 // The binding tables are defined in FIR after lowering inside fir.type_info
58 // operations. Go through each binding tables and store the procedure name and
59 // binding index for later use by the fir.dispatch conversion pattern.
60 for (auto typeInfo : mod.getOps<fir::TypeInfoOp>()) {
61 if (!typeDescriptorType && typeInfo.getSymName() == typeDescriptorTypeName)
62 typeDescriptorType = typeInfo.getType();
63 unsigned bindingIdx = 0;
64 BindingTable bindings;
65 if (typeInfo.getDispatchTable().empty()) {
66 bindingTables[typeInfo.getSymName()] = bindings;
67 continue;
68 }
69 for (auto dtEntry :
70 typeInfo.getDispatchTable().front().getOps<fir::DTEntryOp>()) {
71 bindings[dtEntry.getMethod()] = bindingIdx;
72 ++bindingIdx;
73 }
74 bindingTables[typeInfo.getSymName()] = bindings;
75 }
76 return typeDescriptorType;
77}
78
79namespace {
80
81/// SelectTypeOp converted to an if-then-else chain
82///
83/// This lowers the test conditions to calls into the runtime.
84class SelectTypeConv : public OpConversionPattern<fir::SelectTypeOp> {
85public:
86 using OpConversionPattern<fir::SelectTypeOp>::OpConversionPattern;
87
88 SelectTypeConv(mlir::MLIRContext *ctx)
89 : mlir::OpConversionPattern<fir::SelectTypeOp>(ctx) {}
90
91 llvm::LogicalResult
92 matchAndRewrite(fir::SelectTypeOp selectType, OpAdaptor adaptor,
93 mlir::ConversionPatternRewriter &rewriter) const override;
94
95private:
96 // Generate comparison of type descriptor addresses.
97 mlir::Value genTypeDescCompare(mlir::Location loc, mlir::Value selector,
98 mlir::Type ty, mlir::ModuleOp mod,
99 mlir::PatternRewriter &rewriter) const;
100
101 llvm::LogicalResult genTypeLadderStep(mlir::Location loc,
102 mlir::Value selector,
103 mlir::Attribute attr, mlir::Block *dest,
104 std::optional<mlir::ValueRange> destOps,
105 mlir::ModuleOp mod,
106 mlir::PatternRewriter &rewriter,
107 fir::KindMapping &kindMap) const;
108
109 llvm::SmallSet<llvm::StringRef, 4> collectAncestors(fir::TypeInfoOp dt,
110 mlir::ModuleOp mod) const;
111};
112
113/// Lower `fir.dispatch` operation. A virtual call to a method in a dispatch
114/// table.
115struct DispatchOpConv : public OpConversionPattern<fir::DispatchOp> {
116 using OpConversionPattern<fir::DispatchOp>::OpConversionPattern;
117
118 DispatchOpConv(mlir::MLIRContext *ctx, const BindingTables &bindingTables,
119 std::optional<mlir::Type> typeDescriptorType)
120 : mlir::OpConversionPattern<fir::DispatchOp>(ctx),
121 bindingTables(bindingTables), typeDescriptorType{typeDescriptorType} {}
122
123 llvm::LogicalResult
124 matchAndRewrite(fir::DispatchOp dispatch, OpAdaptor adaptor,
125 mlir::ConversionPatternRewriter &rewriter) const override {
126 mlir::Location loc = dispatch.getLoc();
127
128 if (bindingTables.empty())
129 return emitError(loc) << "no binding tables found";
130
131 // Get derived type information.
132 mlir::Type declaredType =
133 fir::getDerivedType(dispatch.getObject().getType().getEleTy());
134 assert(mlir::isa<fir::RecordType>(declaredType) && "expecting fir.type");
135 auto recordType = mlir::dyn_cast<fir::RecordType>(declaredType);
136
137 // Lookup for the binding table.
138 auto bindingsIter = bindingTables.find(recordType.getName());
139 if (bindingsIter == bindingTables.end())
140 return emitError(loc)
141 << "cannot find binding table for " << recordType.getName();
142
143 // Lookup for the binding.
144 const BindingTable &bindingTable = bindingsIter->second;
145 auto bindingIter = bindingTable.find(dispatch.getMethod());
146 if (bindingIter == bindingTable.end())
147 return emitError(loc)
148 << "cannot find binding for " << dispatch.getMethod();
149 unsigned bindingIdx = bindingIter->second;
150
151 mlir::Value passedObject = dispatch.getObject();
152
153 if (!typeDescriptorType)
154 return emitError(loc) << "cannot find " << getTypeDescriptorTypeName()
155 << " fir.type_info that is required to get the "
156 "related builtin type and lower fir.dispatch";
157 mlir::Type typeDescTy = *typeDescriptorType;
158
159 // clang-format off
160 // Before:
161 // fir.dispatch "proc1"(%11 :
162 // !fir.class<!fir.heap<!fir.type<_QMpolyTp1{a:i32,b:i32}>>>)
163
164 // After:
165 // %12 = fir.box_tdesc %11 : (!fir.class<!fir.heap<!fir.type<_QMpolyTp1{a:i32,b:i32}>>>) -> !fir.tdesc<none>
166 // %13 = fir.convert %12 : (!fir.tdesc<none>) -> !fir.ref<!fir.type<_QM__fortran_type_infoTderivedtype>>
167 // %14 = fir.field_index binding, !fir.type<_QM__fortran_type_infoTderivedtype>
168 // %15 = fir.coordinate_of %13, %14 : (!fir.ref<!fir.type<_QM__fortran_type_infoTderivedtype>>, !fir.field) -> !fir.ref<!fir.box<!fir.ptr<!fir.array<?x!fir.type<_QM__fortran_type_infoTbinding>>>>>
169 // %bindings = fir.load %15 : !fir.ref<!fir.box<!fir.ptr<!fir.array<?x!fir.type<_QM__fortran_type_infoTbinding>>>>>
170 // %16 = fir.box_addr %bindings : (!fir.box<!fir.ptr<!fir.array<?x!fir.type<_QM__fortran_type_infoTbinding>>>>) -> !fir.ptr<!fir.array<?x!fir.type<_QM__fortran_type_infoTbinding>>>
171 // %17 = fir.coordinate_of %16, %c0 : (!fir.ptr<!fir.array<?x!fir.type<_QM__fortran_type_infoTbinding>>>, index) -> !fir.ref<!fir.type<_QM__fortran_type_infoTbinding>>
172 // %18 = fir.field_index proc, !fir.type<_QM__fortran_type_infoTbinding>
173 // %19 = fir.coordinate_of %17, %18 : (!fir.ref<!fir.type<_QM__fortran_type_infoTbinding>>, !fir.field) -> !fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_funptr>>
174 // %20 = fir.field_index __address, !fir.type<_QM__fortran_builtinsT__builtin_c_funptr>
175 // %21 = fir.coordinate_of %19, %20 : (!fir.ref<!fir.type<_QM__fortran_builtinsT__builtin_c_funptr>>, !fir.field) -> !fir.ref<i64>
176 // %22 = fir.load %21 : !fir.ref<i64>
177 // %23 = fir.convert %22 : (i64) -> (() -> ())
178 // fir.call %23() : () -> ()
179 // clang-format on
180
181 // Load the descriptor.
182 mlir::Type fieldTy = fir::FieldType::get(rewriter.getContext());
183 mlir::Type tdescType =
184 fir::TypeDescType::get(mlir::NoneType::get(rewriter.getContext()));
185 mlir::Value boxDesc =
186 rewriter.create<fir::BoxTypeDescOp>(loc, tdescType, passedObject);
187 boxDesc = rewriter.create<fir::ConvertOp>(
188 loc, fir::ReferenceType::get(typeDescTy), boxDesc);
189
190 // Load the bindings descriptor.
191 auto bindingsCompName = Fortran::semantics::bindingDescCompName;
192 fir::RecordType typeDescRecTy = mlir::cast<fir::RecordType>(typeDescTy);
193 mlir::Value field = rewriter.create<fir::FieldIndexOp>(
194 loc, fieldTy, bindingsCompName, typeDescRecTy, mlir::ValueRange{});
195 mlir::Type coorTy =
196 fir::ReferenceType::get(typeDescRecTy.getType(bindingsCompName));
197 mlir::Value bindingBoxAddr =
198 rewriter.create<fir::CoordinateOp>(loc, coorTy, boxDesc, field);
199 mlir::Value bindingBox = rewriter.create<fir::LoadOp>(loc, bindingBoxAddr);
200
201 // Load the correct binding.
202 mlir::Value bindings = rewriter.create<fir::BoxAddrOp>(loc, bindingBox);
203 fir::RecordType bindingTy = fir::unwrapIfDerived(
204 mlir::cast<fir::BaseBoxType>(bindingBox.getType()));
205 mlir::Type bindingAddrTy = fir::ReferenceType::get(bindingTy);
206 mlir::Value bindingIdxVal = rewriter.create<mlir::arith::ConstantOp>(
207 loc, rewriter.getIndexType(), rewriter.getIndexAttr(bindingIdx));
208 mlir::Value bindingAddr = rewriter.create<fir::CoordinateOp>(
209 loc, bindingAddrTy, bindings, bindingIdxVal);
210
211 // Get the function pointer.
212 auto procCompName = Fortran::semantics::procCompName;
213 mlir::Value procField = rewriter.create<fir::FieldIndexOp>(
214 loc, fieldTy, procCompName, bindingTy, mlir::ValueRange{});
215 fir::RecordType procTy =
216 mlir::cast<fir::RecordType>(bindingTy.getType(procCompName));
217 mlir::Type procRefTy = fir::ReferenceType::get(procTy);
218 mlir::Value procRef = rewriter.create<fir::CoordinateOp>(
219 loc, procRefTy, bindingAddr, procField);
220
221 auto addressFieldName = Fortran::lower::builtin::cptrFieldName;
222 mlir::Value addressField = rewriter.create<fir::FieldIndexOp>(
223 loc, fieldTy, addressFieldName, procTy, mlir::ValueRange{});
224 mlir::Type addressTy = procTy.getType(addressFieldName);
225 mlir::Type addressRefTy = fir::ReferenceType::get(addressTy);
226 mlir::Value addressRef = rewriter.create<fir::CoordinateOp>(
227 loc, addressRefTy, procRef, addressField);
228 mlir::Value address = rewriter.create<fir::LoadOp>(loc, addressRef);
229
230 // Get the function type.
231 llvm::SmallVector<mlir::Type> argTypes;
232 for (mlir::Value operand : dispatch.getArgs())
233 argTypes.push_back(operand.getType());
234 llvm::SmallVector<mlir::Type> resTypes;
235 if (!dispatch.getResults().empty())
236 resTypes.push_back(dispatch.getResults()[0].getType());
237
238 mlir::Type funTy =
239 mlir::FunctionType::get(rewriter.getContext(), argTypes, resTypes);
240 mlir::Value funcPtr = rewriter.create<fir::ConvertOp>(loc, funTy, address);
241
242 // Make the call.
243 llvm::SmallVector<mlir::Value> args{funcPtr};
244 args.append(dispatch.getArgs().begin(), dispatch.getArgs().end());
245 rewriter.replaceOpWithNewOp<fir::CallOp>(
246 dispatch, resTypes, nullptr, args, dispatch.getArgAttrsAttr(),
247 dispatch.getResAttrsAttr(), dispatch.getProcedureAttrsAttr());
248 return mlir::success();
249 }
250
251private:
252 BindingTables bindingTables;
253 std::optional<mlir::Type> typeDescriptorType;
254};
255
256/// Convert FIR structured control flow ops to CFG ops.
257class PolymorphicOpConversion
258 : public fir::impl::PolymorphicOpConversionBase<PolymorphicOpConversion> {
259public:
260 llvm::LogicalResult initialize(mlir::MLIRContext *ctx) override {
261 return mlir::success();
262 }
263
264 void runOnOperation() override {
265 auto *context = &getContext();
266 mlir::ModuleOp mod = getOperation();
267 mlir::RewritePatternSet patterns(context);
268
269 BindingTables bindingTables;
270 std::optional<mlir::Type> typeDescriptorType =
271 buildBindingTables(bindingTables, mod);
272
273 patterns.insert<SelectTypeConv>(context);
274 patterns.insert<DispatchOpConv>(context, bindingTables, typeDescriptorType);
275 mlir::ConversionTarget target(*context);
276 target.addLegalDialect<mlir::affine::AffineDialect,
277 mlir::cf::ControlFlowDialect, FIROpsDialect,
278 mlir::func::FuncDialect>();
279
280 // apply the patterns
281 target.addIllegalOp<SelectTypeOp>();
282 target.addIllegalOp<DispatchOp>();
283 target.markUnknownOpDynamicallyLegal([](Operation *) { return true; });
284 if (mlir::failed(mlir::applyPartialConversion(getOperation(), target,
285 std::move(patterns)))) {
286 mlir::emitError(mlir::UnknownLoc::get(context),
287 "error in converting to CFG\n");
288 signalPassFailure();
289 }
290 }
291};
292} // namespace
293
294llvm::LogicalResult SelectTypeConv::matchAndRewrite(
295 fir::SelectTypeOp selectType, OpAdaptor adaptor,
296 mlir::ConversionPatternRewriter &rewriter) const {
297 auto operands = adaptor.getOperands();
298 auto typeGuards = selectType.getCases();
299 unsigned typeGuardNum = typeGuards.size();
300 auto selector = selectType.getSelector();
301 auto loc = selectType.getLoc();
302 auto mod = selectType.getOperation()->getParentOfType<mlir::ModuleOp>();
303 fir::KindMapping kindMap = fir::getKindMapping(mod);
304
305 // Order type guards so the condition and branches are done to respect the
306 // Execution of SELECT TYPE construct as described in the Fortran 2018
307 // standard 11.1.11.2 point 4.
308 // 1. If a TYPE IS type guard statement matches the selector, the block
309 // following that statement is executed.
310 // 2. Otherwise, if exactly one CLASS IS type guard statement matches the
311 // selector, the block following that statement is executed.
312 // 3. Otherwise, if several CLASS IS type guard statements match the
313 // selector, one of these statements will inevitably specify a type that
314 // is an extension of all the types specified in the others; the block
315 // following that statement is executed.
316 // 4. Otherwise, if there is a CLASS DEFAULT type guard statement, the block
317 // following that statement is executed.
318 // 5. Otherwise, no block is executed.
319
320 llvm::SmallVector<unsigned> orderedTypeGuards;
321 llvm::SmallVector<unsigned> orderedClassIsGuards;
322 unsigned defaultGuard = typeGuardNum - 1;
323
324 // The following loop go through the type guards in the fir.select_type
325 // operation and sort them into two lists.
326 // - All the TYPE IS type guard are added in order to the orderedTypeGuards
327 // list. This list is used at the end to generate the if-then-else ladder.
328 // - CLASS IS type guard are added in a separate list. If a CLASS IS type
329 // guard type extends a type already present, the type guard is inserted
330 // before in the list to respect point 3. above. Otherwise it is just
331 // added in order at the end.
332 for (unsigned t = 0; t < typeGuardNum; ++t) {
333 if (auto a = mlir::dyn_cast<fir::ExactTypeAttr>(typeGuards[t])) {
334 orderedTypeGuards.push_back(Elt: t);
335 continue;
336 }
337
338 if (auto a = mlir::dyn_cast<fir::SubclassAttr>(typeGuards[t])) {
339 if (auto recTy = mlir::dyn_cast<fir::RecordType>(a.getType())) {
340 auto dt = mod.lookupSymbol<fir::TypeInfoOp>(recTy.getName());
341 assert(dt && "dispatch table not found");
342 llvm::SmallSet<llvm::StringRef, 4> ancestors =
343 collectAncestors(dt, mod);
344 if (!ancestors.empty()) {
345 auto it = orderedClassIsGuards.begin();
346 while (it != orderedClassIsGuards.end()) {
347 fir::SubclassAttr sAttr =
348 mlir::dyn_cast<fir::SubclassAttr>(typeGuards[*it]);
349 if (auto ty = mlir::dyn_cast<fir::RecordType>(sAttr.getType())) {
350 if (ancestors.contains(V: ty.getName()))
351 break;
352 }
353 ++it;
354 }
355 if (it != orderedClassIsGuards.end()) {
356 // Parent type is present so place it before.
357 orderedClassIsGuards.insert(I: it, Elt: t);
358 continue;
359 }
360 }
361 }
362 orderedClassIsGuards.push_back(Elt: t);
363 }
364 }
365 orderedTypeGuards.append(RHS: orderedClassIsGuards);
366 orderedTypeGuards.push_back(Elt: defaultGuard);
367 assert(orderedTypeGuards.size() == typeGuardNum &&
368 "ordered type guard size doesn't match number of type guards");
369
370 for (unsigned idx : orderedTypeGuards) {
371 auto *dest = selectType.getSuccessor(idx);
372 std::optional<mlir::ValueRange> destOps =
373 selectType.getSuccessorOperands(operands, idx);
374 if (mlir::dyn_cast<mlir::UnitAttr>(typeGuards[idx]))
375 rewriter.replaceOpWithNewOp<mlir::cf::BranchOp>(
376 selectType, dest, destOps.value_or(mlir::ValueRange{}));
377 else if (mlir::failed(genTypeLadderStep(loc, selector, typeGuards[idx],
378 dest, destOps, mod, rewriter,
379 kindMap)))
380 return mlir::failure();
381 }
382 return mlir::success();
383}
384
385llvm::LogicalResult SelectTypeConv::genTypeLadderStep(
386 mlir::Location loc, mlir::Value selector, mlir::Attribute attr,
387 mlir::Block *dest, std::optional<mlir::ValueRange> destOps,
388 mlir::ModuleOp mod, mlir::PatternRewriter &rewriter,
389 fir::KindMapping &kindMap) const {
390 mlir::Value cmp;
391 // TYPE IS type guard comparison are all done inlined.
392 if (auto a = mlir::dyn_cast<fir::ExactTypeAttr>(attr)) {
393 if (fir::isa_trivial(a.getType()) ||
394 mlir::isa<fir::CharacterType>(a.getType())) {
395 // For type guard statement with Intrinsic type spec the type code of
396 // the descriptor is compared.
397 int code = fir::getTypeCode(a.getType(), kindMap);
398 if (code == 0)
399 return mlir::emitError(loc)
400 << "type code unavailable for " << a.getType();
401 mlir::Value typeCode = rewriter.create<mlir::arith::ConstantOp>(
402 loc, rewriter.getI8IntegerAttr(code));
403 mlir::Value selectorTypeCode = rewriter.create<fir::BoxTypeCodeOp>(
404 loc, rewriter.getI8Type(), selector);
405 cmp = rewriter.create<mlir::arith::CmpIOp>(
406 loc, mlir::arith::CmpIPredicate::eq, selectorTypeCode, typeCode);
407 } else {
408 // Flang inline the kind parameter in the type descriptor so we can
409 // directly check if the type descriptor addresses are identical for
410 // the TYPE IS type guard statement.
411 mlir::Value res =
412 genTypeDescCompare(loc, selector, a.getType(), mod, rewriter);
413 if (!res)
414 return mlir::failure();
415 cmp = res;
416 }
417 // CLASS IS type guard statement is done with a runtime call.
418 } else if (auto a = mlir::dyn_cast<fir::SubclassAttr>(attr)) {
419 // Retrieve the type descriptor from the type guard statement record type.
420 assert(mlir::isa<fir::RecordType>(a.getType()) && "expect fir.record type");
421 mlir::Value typeDescAddr =
422 rewriter.create<fir::TypeDescOp>(loc, mlir::TypeAttr::get(a.getType()));
423 mlir::Type refNoneType = ReferenceType::get(rewriter.getNoneType());
424 mlir::Value typeDesc =
425 rewriter.create<ConvertOp>(loc, refNoneType, typeDescAddr);
426
427 // Prepare the selector descriptor for the runtime call.
428 mlir::Type descNoneTy = fir::BoxType::get(rewriter.getNoneType());
429 mlir::Value descSelector =
430 rewriter.create<ConvertOp>(loc, descNoneTy, selector);
431
432 // Generate runtime call.
433 llvm::StringRef fctName = RTNAME_STRING(ClassIs);
434 mlir::func::FuncOp callee;
435 {
436 // Since conversion is done in parallel for each fir.select_type
437 // operation, the runtime function insertion must be threadsafe.
438 auto runtimeAttr =
439 mlir::NamedAttribute(fir::FIROpsDialect::getFirRuntimeAttrName(),
440 mlir::UnitAttr::get(rewriter.getContext()));
441 callee =
442 fir::createFuncOp(rewriter.getUnknownLoc(), mod, fctName,
443 rewriter.getFunctionType({descNoneTy, refNoneType},
444 rewriter.getI1Type()),
445 {runtimeAttr});
446 }
447 cmp = rewriter
448 .create<fir::CallOp>(loc, callee,
449 mlir::ValueRange{descSelector, typeDesc})
450 .getResult(0);
451 }
452
453 auto *thisBlock = rewriter.getInsertionBlock();
454 auto *newBlock =
455 rewriter.createBlock(dest->getParent(), mlir::Region::iterator(dest));
456 rewriter.setInsertionPointToEnd(thisBlock);
457 if (destOps.has_value())
458 rewriter.create<mlir::cf::CondBranchOp>(loc, cmp, dest, destOps.value(),
459 newBlock, std::nullopt);
460 else
461 rewriter.create<mlir::cf::CondBranchOp>(loc, cmp, dest, newBlock);
462 rewriter.setInsertionPointToEnd(newBlock);
463 return mlir::success();
464}
465
466// Generate comparison of type descriptor addresses.
467mlir::Value
468SelectTypeConv::genTypeDescCompare(mlir::Location loc, mlir::Value selector,
469 mlir::Type ty, mlir::ModuleOp mod,
470 mlir::PatternRewriter &rewriter) const {
471 assert(mlir::isa<fir::RecordType>(ty) && "expect fir.record type");
472 mlir::Value typeDescAddr =
473 rewriter.create<fir::TypeDescOp>(loc, mlir::TypeAttr::get(ty));
474 mlir::Value selectorTdescAddr = rewriter.create<fir::BoxTypeDescOp>(
475 loc, typeDescAddr.getType(), selector);
476 auto intPtrTy = rewriter.getIndexType();
477 auto typeDescInt =
478 rewriter.create<fir::ConvertOp>(loc, intPtrTy, typeDescAddr);
479 auto selectorTdescInt =
480 rewriter.create<fir::ConvertOp>(loc, intPtrTy, selectorTdescAddr);
481 return rewriter.create<mlir::arith::CmpIOp>(
482 loc, mlir::arith::CmpIPredicate::eq, typeDescInt, selectorTdescInt);
483}
484
485llvm::SmallSet<llvm::StringRef, 4>
486SelectTypeConv::collectAncestors(fir::TypeInfoOp dt, mlir::ModuleOp mod) const {
487 llvm::SmallSet<llvm::StringRef, 4> ancestors;
488 while (auto parentName = dt.getIfParentName()) {
489 ancestors.insert(*parentName);
490 dt = mod.lookupSymbol<fir::TypeInfoOp>(*parentName);
491 assert(dt && "parent type info not generated");
492 }
493 return ancestors;
494}
495

source code of flang/lib/Optimizer/Transforms/PolymorphicOpConversion.cpp