1 | //===-- CodeGen.cpp -- bridge to lower to LLVM ----------------------------===// |
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 | // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/ |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "flang/Optimizer/CodeGen/FIROpPatterns.h" |
14 | #include "mlir/Dialect/OpenMP/OpenMPDialect.h" |
15 | #include "llvm/Support/Debug.h" |
16 | |
17 | static inline mlir::Type getLlvmPtrType(mlir::MLIRContext *context, |
18 | unsigned addressSpace = 0) { |
19 | return mlir::LLVM::LLVMPointerType::get(context, addressSpace); |
20 | } |
21 | |
22 | static unsigned getTypeDescFieldId(mlir::Type ty) { |
23 | auto isArray = fir::dyn_cast_ptrOrBoxEleTy(ty).isa<fir::SequenceType>(); |
24 | return isArray ? kOptTypePtrPosInBox : kDimsPosInBox; |
25 | } |
26 | |
27 | namespace fir { |
28 | |
29 | ConvertFIRToLLVMPattern::ConvertFIRToLLVMPattern( |
30 | llvm::StringRef rootOpName, mlir::MLIRContext *context, |
31 | const fir::LLVMTypeConverter &typeConverter, |
32 | const fir::FIRToLLVMPassOptions &options, mlir::PatternBenefit benefit) |
33 | : ConvertToLLVMPattern(rootOpName, context, typeConverter, benefit), |
34 | options(options) {} |
35 | |
36 | // Convert FIR type to LLVM without turning fir.box<T> into memory |
37 | // reference. |
38 | mlir::Type |
39 | ConvertFIRToLLVMPattern::convertObjectType(mlir::Type firType) const { |
40 | if (auto boxTy = firType.dyn_cast<fir::BaseBoxType>()) |
41 | return lowerTy().convertBoxTypeAsStruct(boxTy); |
42 | return lowerTy().convertType(firType); |
43 | } |
44 | |
45 | mlir::LLVM::ConstantOp ConvertFIRToLLVMPattern::genI32Constant( |
46 | mlir::Location loc, mlir::ConversionPatternRewriter &rewriter, |
47 | int value) const { |
48 | mlir::Type i32Ty = rewriter.getI32Type(); |
49 | mlir::IntegerAttr attr = rewriter.getI32IntegerAttr(value); |
50 | return rewriter.create<mlir::LLVM::ConstantOp>(loc, i32Ty, attr); |
51 | } |
52 | |
53 | mlir::LLVM::ConstantOp ConvertFIRToLLVMPattern::genConstantOffset( |
54 | mlir::Location loc, mlir::ConversionPatternRewriter &rewriter, |
55 | int offset) const { |
56 | mlir::Type ity = lowerTy().offsetType(); |
57 | mlir::IntegerAttr cattr = rewriter.getI32IntegerAttr(offset); |
58 | return rewriter.create<mlir::LLVM::ConstantOp>(loc, ity, cattr); |
59 | } |
60 | |
61 | /// Perform an extension or truncation as needed on an integer value. Lowering |
62 | /// to the specific target may involve some sign-extending or truncation of |
63 | /// values, particularly to fit them from abstract box types to the |
64 | /// appropriate reified structures. |
65 | mlir::Value |
66 | ConvertFIRToLLVMPattern::integerCast(mlir::Location loc, |
67 | mlir::ConversionPatternRewriter &rewriter, |
68 | mlir::Type ty, mlir::Value val) const { |
69 | auto valTy = val.getType(); |
70 | // If the value was not yet lowered, lower its type so that it can |
71 | // be used in getPrimitiveTypeSizeInBits. |
72 | if (!valTy.isa<mlir::IntegerType>()) |
73 | valTy = convertType(valTy); |
74 | auto toSize = mlir::LLVM::getPrimitiveTypeSizeInBits(ty); |
75 | auto fromSize = mlir::LLVM::getPrimitiveTypeSizeInBits(valTy); |
76 | if (toSize < fromSize) |
77 | return rewriter.create<mlir::LLVM::TruncOp>(loc, ty, val); |
78 | if (toSize > fromSize) |
79 | return rewriter.create<mlir::LLVM::SExtOp>(loc, ty, val); |
80 | return val; |
81 | } |
82 | |
83 | fir::ConvertFIRToLLVMPattern::TypePair |
84 | ConvertFIRToLLVMPattern::getBoxTypePair(mlir::Type firBoxTy) const { |
85 | mlir::Type llvmBoxTy = |
86 | lowerTy().convertBoxTypeAsStruct(mlir::cast<fir::BaseBoxType>(firBoxTy)); |
87 | return TypePair{firBoxTy, llvmBoxTy}; |
88 | } |
89 | |
90 | /// Construct code sequence to extract the specific value from a `fir.box`. |
91 | mlir::Value ConvertFIRToLLVMPattern::getValueFromBox( |
92 | mlir::Location loc, TypePair boxTy, mlir::Value box, mlir::Type resultTy, |
93 | mlir::ConversionPatternRewriter &rewriter, int boxValue) const { |
94 | if (box.getType().isa<mlir::LLVM::LLVMPointerType>()) { |
95 | auto pty = getLlvmPtrType(resultTy.getContext()); |
96 | auto p = rewriter.create<mlir::LLVM::GEPOp>( |
97 | loc, pty, boxTy.llvm, box, |
98 | llvm::ArrayRef<mlir::LLVM::GEPArg>{0, boxValue}); |
99 | auto loadOp = rewriter.create<mlir::LLVM::LoadOp>(loc, resultTy, p); |
100 | attachTBAATag(loadOp, boxTy.fir, nullptr, p); |
101 | return loadOp; |
102 | } |
103 | return rewriter.create<mlir::LLVM::ExtractValueOp>(loc, box, boxValue); |
104 | } |
105 | |
106 | /// Method to construct code sequence to get the triple for dimension `dim` |
107 | /// from a box. |
108 | llvm::SmallVector<mlir::Value, 3> ConvertFIRToLLVMPattern::getDimsFromBox( |
109 | mlir::Location loc, llvm::ArrayRef<mlir::Type> retTys, TypePair boxTy, |
110 | mlir::Value box, mlir::Value dim, |
111 | mlir::ConversionPatternRewriter &rewriter) const { |
112 | mlir::Value l0 = |
113 | loadDimFieldFromBox(loc, boxTy, box, dim, 0, retTys[0], rewriter); |
114 | mlir::Value l1 = |
115 | loadDimFieldFromBox(loc, boxTy, box, dim, 1, retTys[1], rewriter); |
116 | mlir::Value l2 = |
117 | loadDimFieldFromBox(loc, boxTy, box, dim, 2, retTys[2], rewriter); |
118 | return {l0, l1, l2}; |
119 | } |
120 | |
121 | llvm::SmallVector<mlir::Value, 3> ConvertFIRToLLVMPattern::getDimsFromBox( |
122 | mlir::Location loc, llvm::ArrayRef<mlir::Type> retTys, TypePair boxTy, |
123 | mlir::Value box, int dim, mlir::ConversionPatternRewriter &rewriter) const { |
124 | mlir::Value l0 = |
125 | getDimFieldFromBox(loc, boxTy, box, dim, 0, retTys[0], rewriter); |
126 | mlir::Value l1 = |
127 | getDimFieldFromBox(loc, boxTy, box, dim, 1, retTys[1], rewriter); |
128 | mlir::Value l2 = |
129 | getDimFieldFromBox(loc, boxTy, box, dim, 2, retTys[2], rewriter); |
130 | return {l0, l1, l2}; |
131 | } |
132 | |
133 | mlir::Value ConvertFIRToLLVMPattern::loadDimFieldFromBox( |
134 | mlir::Location loc, TypePair boxTy, mlir::Value box, mlir::Value dim, |
135 | int off, mlir::Type ty, mlir::ConversionPatternRewriter &rewriter) const { |
136 | assert(box.getType().isa<mlir::LLVM::LLVMPointerType>() && |
137 | "descriptor inquiry with runtime dim can only be done on descriptor " |
138 | "in memory" ); |
139 | mlir::LLVM::GEPOp p = genGEP(loc, boxTy.llvm, rewriter, box, 0, |
140 | static_cast<int>(kDimsPosInBox), dim, off); |
141 | auto loadOp = rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p); |
142 | attachTBAATag(loadOp, boxTy.fir, nullptr, p); |
143 | return loadOp; |
144 | } |
145 | |
146 | mlir::Value ConvertFIRToLLVMPattern::getDimFieldFromBox( |
147 | mlir::Location loc, TypePair boxTy, mlir::Value box, int dim, int off, |
148 | mlir::Type ty, mlir::ConversionPatternRewriter &rewriter) const { |
149 | if (box.getType().isa<mlir::LLVM::LLVMPointerType>()) { |
150 | mlir::LLVM::GEPOp p = genGEP(loc, boxTy.llvm, rewriter, box, 0, |
151 | static_cast<int>(kDimsPosInBox), dim, off); |
152 | auto loadOp = rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p); |
153 | attachTBAATag(loadOp, boxTy.fir, nullptr, p); |
154 | return loadOp; |
155 | } |
156 | return rewriter.create<mlir::LLVM::ExtractValueOp>( |
157 | loc, box, llvm::ArrayRef<std::int64_t>{kDimsPosInBox, dim, off}); |
158 | } |
159 | |
160 | mlir::Value ConvertFIRToLLVMPattern::getStrideFromBox( |
161 | mlir::Location loc, TypePair boxTy, mlir::Value box, unsigned dim, |
162 | mlir::ConversionPatternRewriter &rewriter) const { |
163 | auto idxTy = lowerTy().indexType(); |
164 | return getDimFieldFromBox(loc, boxTy, box, dim, kDimStridePos, idxTy, |
165 | rewriter); |
166 | } |
167 | |
168 | /// Read base address from a fir.box. Returned address has type ty. |
169 | mlir::Value ConvertFIRToLLVMPattern::getBaseAddrFromBox( |
170 | mlir::Location loc, TypePair boxTy, mlir::Value box, |
171 | mlir::ConversionPatternRewriter &rewriter) const { |
172 | mlir::Type resultTy = ::getLlvmPtrType(boxTy.llvm.getContext()); |
173 | return getValueFromBox(loc, boxTy, box, resultTy, rewriter, kAddrPosInBox); |
174 | } |
175 | |
176 | mlir::Value ConvertFIRToLLVMPattern::getElementSizeFromBox( |
177 | mlir::Location loc, mlir::Type resultTy, TypePair boxTy, mlir::Value box, |
178 | mlir::ConversionPatternRewriter &rewriter) const { |
179 | return getValueFromBox(loc, boxTy, box, resultTy, rewriter, kElemLenPosInBox); |
180 | } |
181 | |
182 | // Get the element type given an LLVM type that is of the form |
183 | // (array|struct|vector)+ and the provided indexes. |
184 | mlir::Type ConvertFIRToLLVMPattern::getBoxEleTy( |
185 | mlir::Type type, llvm::ArrayRef<std::int64_t> indexes) const { |
186 | for (unsigned i : indexes) { |
187 | if (auto t = type.dyn_cast<mlir::LLVM::LLVMStructType>()) { |
188 | assert(!t.isOpaque() && i < t.getBody().size()); |
189 | type = t.getBody()[i]; |
190 | } else if (auto t = type.dyn_cast<mlir::LLVM::LLVMArrayType>()) { |
191 | type = t.getElementType(); |
192 | } else if (auto t = type.dyn_cast<mlir::VectorType>()) { |
193 | type = t.getElementType(); |
194 | } else { |
195 | fir::emitFatalError(mlir::UnknownLoc::get(type.getContext()), |
196 | "request for invalid box element type" ); |
197 | } |
198 | } |
199 | return type; |
200 | } |
201 | |
202 | // Return LLVM type of the object described by a fir.box of \p boxType. |
203 | mlir::Type ConvertFIRToLLVMPattern::getLlvmObjectTypeFromBoxType( |
204 | mlir::Type boxType) const { |
205 | mlir::Type objectType = fir::dyn_cast_ptrOrBoxEleTy(boxType); |
206 | assert(objectType && "boxType must be a box type" ); |
207 | return this->convertType(objectType); |
208 | } |
209 | |
210 | /// Read the address of the type descriptor from a box. |
211 | mlir::Value ConvertFIRToLLVMPattern::loadTypeDescAddress( |
212 | mlir::Location loc, TypePair boxTy, mlir::Value box, |
213 | mlir::ConversionPatternRewriter &rewriter) const { |
214 | unsigned typeDescFieldId = getTypeDescFieldId(boxTy.fir); |
215 | mlir::Type tdescType = lowerTy().convertTypeDescType(rewriter.getContext()); |
216 | return getValueFromBox(loc, boxTy, box, tdescType, rewriter, typeDescFieldId); |
217 | } |
218 | |
219 | // Load the attribute from the \p box and perform a check against \p maskValue |
220 | // The final comparison is implemented as `(attribute & maskValue) != 0`. |
221 | mlir::Value ConvertFIRToLLVMPattern::genBoxAttributeCheck( |
222 | mlir::Location loc, TypePair boxTy, mlir::Value box, |
223 | mlir::ConversionPatternRewriter &rewriter, unsigned maskValue) const { |
224 | mlir::Type attrTy = rewriter.getI32Type(); |
225 | mlir::Value attribute = |
226 | getValueFromBox(loc, boxTy, box, attrTy, rewriter, kAttributePosInBox); |
227 | mlir::LLVM::ConstantOp attrMask = genConstantOffset(loc, rewriter, maskValue); |
228 | auto maskRes = |
229 | rewriter.create<mlir::LLVM::AndOp>(loc, attrTy, attribute, attrMask); |
230 | mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0); |
231 | return rewriter.create<mlir::LLVM::ICmpOp>(loc, mlir::LLVM::ICmpPredicate::ne, |
232 | maskRes, c0); |
233 | } |
234 | |
235 | // Find the Block in which the alloca should be inserted. |
236 | // The order to recursively find the proper block: |
237 | // 1. An OpenMP Op that will be outlined. |
238 | // 2. A LLVMFuncOp |
239 | // 3. The first ancestor that is an OpenMP Op or a LLVMFuncOp |
240 | mlir::Block * |
241 | ConvertFIRToLLVMPattern::getBlockForAllocaInsert(mlir::Operation *op) const { |
242 | if (auto iface = mlir::dyn_cast<mlir::omp::OutlineableOpenMPOpInterface>(op)) |
243 | return iface.getAllocaBlock(); |
244 | if (auto llvmFuncOp = mlir::dyn_cast<mlir::LLVM::LLVMFuncOp>(op)) |
245 | return &llvmFuncOp.front(); |
246 | return getBlockForAllocaInsert(op->getParentOp()); |
247 | } |
248 | |
249 | // Generate an alloca of size 1 for an object of type \p llvmObjectTy in the |
250 | // allocation address space provided for the architecture in the DataLayout |
251 | // specification. If the address space is different from the devices |
252 | // program address space we perform a cast. In the case of most architectures |
253 | // the program and allocation address space will be the default of 0 and no |
254 | // cast will be emitted. |
255 | mlir::Value ConvertFIRToLLVMPattern::genAllocaAndAddrCastWithType( |
256 | mlir::Location loc, mlir::Type llvmObjectTy, unsigned alignment, |
257 | mlir::ConversionPatternRewriter &rewriter) const { |
258 | auto thisPt = rewriter.saveInsertionPoint(); |
259 | mlir::Operation *parentOp = rewriter.getInsertionBlock()->getParentOp(); |
260 | if (mlir::isa<mlir::omp::DeclareReductionOp>(parentOp)) { |
261 | // DeclareReductionOp has multiple child regions. We want to get the first |
262 | // block of whichever of those regions we are currently in |
263 | mlir::Region *parentRegion = rewriter.getInsertionBlock()->getParent(); |
264 | rewriter.setInsertionPointToStart(&parentRegion->front()); |
265 | } else { |
266 | mlir::Block *insertBlock = getBlockForAllocaInsert(parentOp); |
267 | rewriter.setInsertionPointToStart(insertBlock); |
268 | } |
269 | auto size = genI32Constant(loc, rewriter, 1); |
270 | unsigned allocaAs = getAllocaAddressSpace(rewriter); |
271 | unsigned programAs = getProgramAddressSpace(rewriter); |
272 | |
273 | mlir::Value al = rewriter.create<mlir::LLVM::AllocaOp>( |
274 | loc, ::getLlvmPtrType(llvmObjectTy.getContext(), allocaAs), llvmObjectTy, |
275 | size, alignment); |
276 | |
277 | // if our allocation address space, is not the same as the program address |
278 | // space, then we must emit a cast to the program address space before use. |
279 | // An example case would be on AMDGPU, where the allocation address space is |
280 | // the numeric value 5 (private), and the program address space is 0 |
281 | // (generic). |
282 | if (allocaAs != programAs) { |
283 | al = rewriter.create<mlir::LLVM::AddrSpaceCastOp>( |
284 | loc, ::getLlvmPtrType(llvmObjectTy.getContext(), programAs), al); |
285 | } |
286 | |
287 | rewriter.restoreInsertionPoint(thisPt); |
288 | return al; |
289 | } |
290 | |
291 | unsigned ConvertFIRToLLVMPattern::getAllocaAddressSpace( |
292 | mlir::ConversionPatternRewriter &rewriter) const { |
293 | mlir::Operation *parentOp = rewriter.getInsertionBlock()->getParentOp(); |
294 | assert(parentOp != nullptr && |
295 | "expected insertion block to have parent operation" ); |
296 | if (auto module = parentOp->getParentOfType<mlir::ModuleOp>()) |
297 | if (mlir::Attribute addrSpace = |
298 | mlir::DataLayout(module).getAllocaMemorySpace()) |
299 | return llvm::cast<mlir::IntegerAttr>(addrSpace).getUInt(); |
300 | return defaultAddressSpace; |
301 | } |
302 | |
303 | unsigned ConvertFIRToLLVMPattern::getProgramAddressSpace( |
304 | mlir::ConversionPatternRewriter &rewriter) const { |
305 | mlir::Operation *parentOp = rewriter.getInsertionBlock()->getParentOp(); |
306 | assert(parentOp != nullptr && |
307 | "expected insertion block to have parent operation" ); |
308 | if (auto module = parentOp->getParentOfType<mlir::ModuleOp>()) |
309 | if (mlir::Attribute addrSpace = |
310 | mlir::DataLayout(module).getProgramMemorySpace()) |
311 | return llvm::cast<mlir::IntegerAttr>(addrSpace).getUInt(); |
312 | return defaultAddressSpace; |
313 | } |
314 | |
315 | } // namespace fir |
316 | |