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

source code of flang/lib/Optimizer/CodeGen/FIROpPatterns.cpp