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/Support/InitFIR.h" 15 #include "flang/Optimizer/Support/KindMapping.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(llvm::None, llvm::None)); 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 59 mlir::MLIRContext context; 60 std::unique_ptr<fir::KindMapping> kindMap; 61 std::unique_ptr<fir::FirOpBuilder> firBuilder; 62 63 // Commonly used types 64 mlir::Type i1Ty; 65 mlir::Type i8Ty; 66 mlir::Type i16Ty; 67 mlir::Type i32Ty; 68 mlir::Type i64Ty; 69 mlir::Type i128Ty; 70 mlir::Type f32Ty; 71 mlir::Type f64Ty; 72 mlir::Type f80Ty; 73 mlir::Type f128Ty; 74 mlir::Type c4Ty; 75 mlir::Type c8Ty; 76 mlir::Type c10Ty; 77 mlir::Type c16Ty; 78 mlir::Type seqTy10; 79 mlir::Type boxTy; 80 }; 81 82 /// Check that the \p op is a `fir::CallOp` operation and its name matches 83 /// \p fctName and the number of arguments is equal to \p nbArgs. 84 /// Most runtime calls have two additional location arguments added. These are 85 /// added in this check when \p addLocArgs is true. 86 static inline void checkCallOp(mlir::Operation *op, llvm::StringRef fctName, 87 unsigned nbArgs, bool addLocArgs = true) { 88 EXPECT_TRUE(mlir::isa<fir::CallOp>(*op)); 89 auto callOp = mlir::dyn_cast<fir::CallOp>(*op); 90 EXPECT_TRUE(callOp.getCallee().has_value()); 91 mlir::SymbolRefAttr callee = *callOp.getCallee(); 92 EXPECT_EQ(fctName, callee.getRootReference().getValue()); 93 // sourceFile and sourceLine are added arguments. 94 if (addLocArgs) 95 nbArgs += 2; 96 EXPECT_EQ(nbArgs, callOp.getArgs().size()); 97 } 98 99 /// Check the call operation from the \p result value. In some cases the 100 /// value is directly used in the call and sometimes there is an indirection 101 /// through a `fir.convert` operation. Once the `fir.call` operation is 102 /// retrieved the check is made by `checkCallOp`. 103 /// 104 /// Directly used in `fir.call`. 105 /// ``` 106 /// %result = arith.constant 1 : i32 107 /// %0 = fir.call @foo(%result) : (i32) -> i1 108 /// ``` 109 /// 110 /// Value used in `fir.call` through `fir.convert` indirection. 111 /// ``` 112 /// %result = arith.constant 1 : i32 113 /// %arg = fir.convert %result : (i32) -> i16 114 /// %0 = fir.call @foo(%arg) : (i16) -> i1 115 /// ``` 116 static inline void checkCallOpFromResultBox(mlir::Value result, 117 llvm::StringRef fctName, unsigned nbArgs, bool addLocArgs = true) { 118 EXPECT_TRUE(result.hasOneUse()); 119 const auto &u = result.user_begin(); 120 if (mlir::isa<fir::CallOp>(*u)) 121 return checkCallOp(*u, fctName, nbArgs, addLocArgs); 122 auto convOp = mlir::dyn_cast<fir::ConvertOp>(*u); 123 EXPECT_NE(nullptr, convOp); 124 checkCallOpFromResultBox(convOp.getResult(), fctName, nbArgs, addLocArgs); 125 } 126 127 /// Check the operations in \p block for a `fir::CallOp` operation where the 128 /// function being called shares its function name with \p fctName and the 129 /// number of arguments is equal to \p nbArgs. Note that this check only cares 130 /// if the operation exists, and not the order in when the operation is called. 131 /// This results in exiting the test as soon as the first correct instance of 132 /// `fir::CallOp` is found). 133 static inline void checkBlockForCallOp( 134 mlir::Block *block, llvm::StringRef fctName, unsigned nbArgs) { 135 assert(block && "mlir::Block given is a nullptr"); 136 for (auto &op : block->getOperations()) { 137 if (auto callOp = mlir::dyn_cast<fir::CallOp>(op)) { 138 if (fctName == callOp.getCallee()->getRootReference().getValue()) { 139 EXPECT_EQ(nbArgs, callOp.getArgs().size()); 140 return; 141 } 142 } 143 } 144 FAIL() << "No calls to " << fctName << " were found!"; 145 } 146 147 #endif // FORTRAN_OPTIMIZER_BUILDER_RUNTIME_RUNTIMECALLTESTBASE_H 148