1 | //===- RuntimeCallTestBase.cpp -- Base for runtime call generation tests --===// |
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 | #ifndef FORTRAN_OPTIMIZER_BUILDER_RUNTIME_RUNTIMECALLTESTBASE_H |
10 | #define FORTRAN_OPTIMIZER_BUILDER_RUNTIME_RUNTIMECALLTESTBASE_H |
11 | |
12 | #include "gtest/gtest.h" |
13 | #include "flang/Optimizer/Builder/FIRBuilder.h" |
14 | #include "flang/Optimizer/Dialect/Support/KindMapping.h" |
15 | #include "flang/Optimizer/Support/InitFIR.h" |
16 | |
17 | struct RuntimeCallTest : public testing::Test { |
18 | public: |
19 | void SetUp() override { |
20 | fir::support::loadDialects(context); |
21 | |
22 | mlir::OpBuilder builder(&context); |
23 | auto loc = builder.getUnknownLoc(); |
24 | |
25 | // Set up a Module with a dummy function operation inside. |
26 | // Set the insertion point in the function entry block. |
27 | mlir::ModuleOp mod = builder.create<mlir::ModuleOp>(loc); |
28 | mlir::func::FuncOp func = |
29 | mlir::func::FuncOp::create(loc, "runtime_unit_tests_func" , |
30 | builder.getFunctionType(std::nullopt, std::nullopt)); |
31 | auto *entryBlock = func.addEntryBlock(); |
32 | mod.push_back(mod); |
33 | builder.setInsertionPointToStart(entryBlock); |
34 | |
35 | kindMap = std::make_unique<fir::KindMapping>(&context); |
36 | firBuilder = std::make_unique<fir::FirOpBuilder>(mod, *kindMap); |
37 | |
38 | i1Ty = firBuilder->getI1Type(); |
39 | i8Ty = firBuilder->getI8Type(); |
40 | i16Ty = firBuilder->getIntegerType(16); |
41 | i32Ty = firBuilder->getI32Type(); |
42 | i64Ty = firBuilder->getI64Type(); |
43 | i128Ty = firBuilder->getIntegerType(128); |
44 | |
45 | f32Ty = firBuilder->getF32Type(); |
46 | f64Ty = firBuilder->getF64Type(); |
47 | f80Ty = firBuilder->getF80Type(); |
48 | f128Ty = firBuilder->getF128Type(); |
49 | |
50 | c4Ty = fir::ComplexType::get(firBuilder->getContext(), 4); |
51 | c8Ty = fir::ComplexType::get(firBuilder->getContext(), 8); |
52 | c10Ty = fir::ComplexType::get(firBuilder->getContext(), 10); |
53 | c16Ty = fir::ComplexType::get(firBuilder->getContext(), 16); |
54 | |
55 | seqTy10 = fir::SequenceType::get(fir::SequenceType::Shape(1, 10), i32Ty); |
56 | boxTy = fir::BoxType::get(mlir::NoneType::get(firBuilder->getContext())); |
57 | |
58 | char1Ty = fir::CharacterType::getSingleton(builder.getContext(), 1); |
59 | char2Ty = fir::CharacterType::getSingleton(builder.getContext(), 2); |
60 | char4Ty = fir::CharacterType::getSingleton(builder.getContext(), 4); |
61 | } |
62 | |
63 | mlir::MLIRContext context; |
64 | std::unique_ptr<fir::KindMapping> kindMap; |
65 | std::unique_ptr<fir::FirOpBuilder> firBuilder; |
66 | |
67 | // Commonly used types |
68 | mlir::Type i1Ty; |
69 | mlir::Type i8Ty; |
70 | mlir::Type i16Ty; |
71 | mlir::Type i32Ty; |
72 | mlir::Type i64Ty; |
73 | mlir::Type i128Ty; |
74 | mlir::Type f32Ty; |
75 | mlir::Type f64Ty; |
76 | mlir::Type f80Ty; |
77 | mlir::Type f128Ty; |
78 | mlir::Type c4Ty; |
79 | mlir::Type c8Ty; |
80 | mlir::Type c10Ty; |
81 | mlir::Type c16Ty; |
82 | mlir::Type seqTy10; |
83 | mlir::Type boxTy; |
84 | mlir::Type char1Ty; |
85 | mlir::Type char2Ty; |
86 | mlir::Type char4Ty; |
87 | }; |
88 | |
89 | /// Check that the \p op is a `fir::CallOp` operation and its name matches |
90 | /// \p fctName and the number of arguments is equal to \p nbArgs. |
91 | /// Most runtime calls have two additional location arguments added. These are |
92 | /// added in this check when \p addLocArgs is true. |
93 | static inline void checkCallOp(mlir::Operation *op, llvm::StringRef fctName, |
94 | unsigned nbArgs, bool addLocArgs = true) { |
95 | EXPECT_TRUE(mlir::isa<fir::CallOp>(*op)); |
96 | auto callOp = mlir::dyn_cast<fir::CallOp>(*op); |
97 | EXPECT_TRUE(callOp.getCallee().has_value()); |
98 | mlir::SymbolRefAttr callee = *callOp.getCallee(); |
99 | EXPECT_EQ(fctName, callee.getRootReference().getValue()); |
100 | // sourceFile and sourceLine are added arguments. |
101 | if (addLocArgs) |
102 | nbArgs += 2; |
103 | EXPECT_EQ(nbArgs, callOp.getArgs().size()); |
104 | } |
105 | |
106 | /// Check the call operation from the \p result value. In some cases the |
107 | /// value is directly used in the call and sometimes there is an indirection |
108 | /// through a `fir.convert` operation. Once the `fir.call` operation is |
109 | /// retrieved the check is made by `checkCallOp`. |
110 | /// |
111 | /// Directly used in `fir.call`. |
112 | /// ``` |
113 | /// %result = arith.constant 1 : i32 |
114 | /// %0 = fir.call @foo(%result) : (i32) -> i1 |
115 | /// ``` |
116 | /// |
117 | /// Value used in `fir.call` through `fir.convert` indirection. |
118 | /// ``` |
119 | /// %result = arith.constant 1 : i32 |
120 | /// %arg = fir.convert %result : (i32) -> i16 |
121 | /// %0 = fir.call @foo(%arg) : (i16) -> i1 |
122 | /// ``` |
123 | static inline void checkCallOpFromResultBox(mlir::Value result, |
124 | llvm::StringRef fctName, unsigned nbArgs, bool addLocArgs = true) { |
125 | EXPECT_TRUE(result.hasOneUse()); |
126 | const auto &u = result.user_begin(); |
127 | if (mlir::isa<fir::CallOp>(*u)) |
128 | return checkCallOp(*u, fctName, nbArgs, addLocArgs); |
129 | auto convOp = mlir::dyn_cast<fir::ConvertOp>(*u); |
130 | EXPECT_NE(nullptr, convOp); |
131 | checkCallOpFromResultBox(convOp.getResult(), fctName, nbArgs, addLocArgs); |
132 | } |
133 | |
134 | /// Check the operations in \p block for a `fir::CallOp` operation where the |
135 | /// function being called shares its function name with \p fctName and the |
136 | /// number of arguments is equal to \p nbArgs. Note that this check only cares |
137 | /// if the operation exists, and not the order in when the operation is called. |
138 | /// This results in exiting the test as soon as the first correct instance of |
139 | /// `fir::CallOp` is found). |
140 | static inline void checkBlockForCallOp( |
141 | mlir::Block *block, llvm::StringRef fctName, unsigned nbArgs) { |
142 | assert(block && "mlir::Block given is a nullptr" ); |
143 | for (auto &op : block->getOperations()) { |
144 | if (auto callOp = mlir::dyn_cast<fir::CallOp>(op)) { |
145 | if (fctName == callOp.getCallee()->getRootReference().getValue()) { |
146 | EXPECT_EQ(nbArgs, callOp.getArgs().size()); |
147 | return; |
148 | } |
149 | } |
150 | } |
151 | FAIL() << "No calls to " << fctName << " were found!" ; |
152 | } |
153 | |
154 | #endif // FORTRAN_OPTIMIZER_BUILDER_RUNTIME_RUNTIMECALLTESTBASE_H |
155 | |