1 //===-- Optimizer/Dialect/FIROpsSupport.h -- FIR op support -----*- C++ -*-===// 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_DIALECT_FIROPSSUPPORT_H 10 #define FORTRAN_OPTIMIZER_DIALECT_FIROPSSUPPORT_H 11 12 #include "flang/Optimizer/Dialect/FIROps.h" 13 #include "mlir/Dialect/Func/IR/FuncOps.h" 14 #include "mlir/IR/BuiltinOps.h" 15 16 namespace fir { 17 18 /// Return true iff the Operation is a non-volatile LoadOp or ArrayLoadOp. 19 inline bool nonVolatileLoad(mlir::Operation *op) { 20 if (auto load = mlir::dyn_cast<fir::LoadOp>(op)) 21 return !load->getAttr("volatile"); 22 if (auto arrLoad = mlir::dyn_cast<fir::ArrayLoadOp>(op)) 23 return !arrLoad->getAttr("volatile"); 24 return false; 25 } 26 27 /// Return true iff the Operation is a call. 28 inline bool isaCall(mlir::Operation *op) { 29 return mlir::isa<fir::CallOp>(op) || mlir::isa<fir::DispatchOp>(op) || 30 mlir::isa<mlir::func::CallOp>(op) || 31 mlir::isa<mlir::func::CallIndirectOp>(op); 32 } 33 34 /// Return true iff the Operation is a fir::CallOp, fir::DispatchOp, 35 /// mlir::CallOp, or mlir::CallIndirectOp and not pure 36 /// NB: This is not the same as `!pureCall(op)`. 37 inline bool impureCall(mlir::Operation *op) { 38 // Should we also auto-detect that the called function is pure if its 39 // arguments are not references? For now, rely on a "pure" attribute. 40 return op && isaCall(op) && !op->getAttr("pure"); 41 } 42 43 /// Return true iff the Operation is a fir::CallOp, fir::DispatchOp, 44 /// mlir::CallOp, or mlir::CallIndirectOp and is also pure. 45 /// NB: This is not the same as `!impureCall(op)`. 46 inline bool pureCall(mlir::Operation *op) { 47 // Should we also auto-detect that the called function is pure if its 48 // arguments are not references? For now, rely on a "pure" attribute. 49 return op && isaCall(op) && op->getAttr("pure"); 50 } 51 52 /// Get or create a FuncOp in a module. 53 /// 54 /// If `module` already contains FuncOp `name`, it is returned. Otherwise, a new 55 /// FuncOp is created, and that new FuncOp is returned. A symbol table can 56 /// be provided to speed-up the lookups. 57 mlir::func::FuncOp createFuncOp(mlir::Location loc, mlir::ModuleOp module, 58 llvm::StringRef name, mlir::FunctionType type, 59 llvm::ArrayRef<mlir::NamedAttribute> attrs = {}, 60 const mlir::SymbolTable *symbolTable = nullptr); 61 62 /// Get or create a GlobalOp in a module. A symbol table can be provided to 63 /// speed-up the lookups. 64 fir::GlobalOp createGlobalOp(mlir::Location loc, mlir::ModuleOp module, 65 llvm::StringRef name, mlir::Type type, 66 llvm::ArrayRef<mlir::NamedAttribute> attrs = {}, 67 const mlir::SymbolTable *symbolTable = nullptr); 68 69 /// Attribute to mark Fortran entities with the CONTIGUOUS attribute. 70 constexpr llvm::StringRef getContiguousAttrName() { return "fir.contiguous"; } 71 72 /// Attribute to mark Fortran entities with the OPTIONAL attribute. 73 constexpr llvm::StringRef getOptionalAttrName() { return "fir.optional"; } 74 75 /// Attribute to mark Fortran entities with the TARGET attribute. 76 static constexpr llvm::StringRef getTargetAttrName() { return "fir.target"; } 77 78 /// Attribute to mark Fortran entities with the ASYNCHRONOUS attribute. 79 static constexpr llvm::StringRef getAsynchronousAttrName() { 80 return "fir.asynchronous"; 81 } 82 83 /// Attribute to mark Fortran entities with the VOLATILE attribute. 84 static constexpr llvm::StringRef getVolatileAttrName() { 85 return "fir.volatile"; 86 } 87 88 /// Attribute to mark that a function argument is a character dummy procedure. 89 /// Character dummy procedure have special ABI constraints. 90 static constexpr llvm::StringRef getCharacterProcedureDummyAttrName() { 91 return "fir.char_proc"; 92 } 93 94 /// Attribute to keep track of Fortran scoping information for a symbol. 95 static constexpr llvm::StringRef getSymbolAttrName() { 96 return "fir.bindc_name"; 97 } 98 99 /// Attribute to mark a function that takes a host associations argument. 100 static constexpr llvm::StringRef getHostAssocAttrName() { 101 return "fir.host_assoc"; 102 } 103 104 /// Attribute to link an internal procedure to its host procedure symbol. 105 static constexpr llvm::StringRef getHostSymbolAttrName() { 106 return "fir.host_symbol"; 107 } 108 109 /// Attribute containing the original name of a function from before the 110 /// ExternalNameConverision pass runs 111 static constexpr llvm::StringRef getInternalFuncNameAttrName() { 112 return "fir.internal_name"; 113 } 114 115 /// Does the function, \p func, have a host-associations tuple argument? 116 /// Some internal procedures may have access to host procedure variables. 117 bool hasHostAssociationArgument(mlir::func::FuncOp func); 118 119 /// Is the function, \p func an internal procedure ? 120 /// Some internal procedures may have access to saved host procedure 121 /// variables even when they do not have a tuple argument. 122 inline bool isInternalProcedure(mlir::func::FuncOp func) { 123 return func->hasAttr(fir::getHostSymbolAttrName()); 124 } 125 126 /// Tell if \p value is: 127 /// - a function argument that has attribute \p attributeName 128 /// - or, the result of fir.alloca/fir.allocmem op that has attribute \p 129 /// attributeName 130 /// - or, the result of a fir.address_of of a fir.global that has attribute \p 131 /// attributeName 132 /// - or, a fir.box loaded from a fir.ref<fir.box> that matches one of the 133 /// previous cases. 134 bool valueHasFirAttribute(mlir::Value value, llvm::StringRef attributeName); 135 136 /// A more conservative version of valueHasFirAttribute(). 137 /// If `value` is one of the operation/function-argument cases listed 138 /// for valueHasFirAttribute(): 139 /// * if any of the `attributeNames` attributes is set, then the function 140 /// will return true. 141 /// * otherwise, it will return false. 142 /// 143 /// Otherwise, the function will return true indicating that the attributes 144 /// may actually be set but we were not able to reach the point of definition 145 /// to confirm that. 146 bool valueMayHaveFirAttributes(mlir::Value value, 147 llvm::ArrayRef<llvm::StringRef> attributeNames); 148 149 /// Scan the arguments of a FuncOp to determine if any arguments have the 150 /// attribute `attr` placed on them. This can be used to determine if the 151 /// function has any host associations, for example. 152 bool anyFuncArgsHaveAttr(mlir::func::FuncOp func, llvm::StringRef attr); 153 154 /// Unwrap integer constant from an mlir::Value. 155 std::optional<std::int64_t> getIntIfConstant(mlir::Value value); 156 157 static constexpr llvm::StringRef getAdaptToByRefAttrName() { 158 return "adapt.valuebyref"; 159 } 160 161 static constexpr llvm::StringRef getFuncPureAttrName() { 162 return "fir.func_pure"; 163 } 164 165 static constexpr llvm::StringRef getFuncElementalAttrName() { 166 return "fir.func_elemental"; 167 } 168 169 static constexpr llvm::StringRef getFuncRecursiveAttrName() { 170 return "fir.func_recursive"; 171 } 172 173 static constexpr llvm::StringRef getFortranProcedureFlagsAttrName() { 174 return "fir.proc_attrs"; 175 } 176 177 // Attribute for an alloca that is a trivial adaptor for converting a value to 178 // pass-by-ref semantics for a VALUE parameter. The optimizer may be able to 179 // eliminate these. 180 // Template is used to avoid compiler errors in places that don't include 181 // FIRBuilder.h 182 template <typename Builder> 183 inline mlir::NamedAttribute getAdaptToByRefAttr(Builder &builder) { 184 return {mlir::StringAttr::get(builder.getContext(), 185 fir::getAdaptToByRefAttrName()), 186 builder.getUnitAttr()}; 187 } 188 189 bool isDummyArgument(mlir::Value v); 190 191 template <fir::FortranProcedureFlagsEnum Flag> 192 inline bool hasProcedureAttr(fir::FortranProcedureFlagsEnumAttr flags) { 193 return flags && bitEnumContainsAny(flags.getValue(), Flag); 194 } 195 196 template <fir::FortranProcedureFlagsEnum Flag> 197 inline bool hasProcedureAttr(mlir::Operation *op) { 198 if (auto firCallOp = mlir::dyn_cast<fir::CallOp>(op)) 199 return hasProcedureAttr<Flag>(firCallOp.getProcedureAttrsAttr()); 200 if (auto firCallOp = mlir::dyn_cast<fir::DispatchOp>(op)) 201 return hasProcedureAttr<Flag>(firCallOp.getProcedureAttrsAttr()); 202 return hasProcedureAttr<Flag>( 203 op->getAttrOfType<fir::FortranProcedureFlagsEnumAttr>( 204 getFortranProcedureFlagsAttrName())); 205 } 206 207 inline bool hasBindcAttr(mlir::Operation *op) { 208 return hasProcedureAttr<fir::FortranProcedureFlagsEnum::bind_c>(op); 209 } 210 211 } // namespace fir 212 213 #endif // FORTRAN_OPTIMIZER_DIALECT_FIROPSSUPPORT_H 214