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
17static inline mlir::Type getLlvmPtrType(mlir::MLIRContext *context,
18 unsigned addressSpace = 0) {
19 return mlir::LLVM::LLVMPointerType::get(context, addressSpace);
20}
21
22static unsigned getTypeDescFieldId(mlir::Type ty) {
23 auto isArray = fir::dyn_cast_ptrOrBoxEleTy(ty).isa<fir::SequenceType>();
24 return isArray ? kOptTypePtrPosInBox : kDimsPosInBox;
25}
26
27namespace fir {
28
29ConvertFIRToLLVMPattern::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.
38mlir::Type
39ConvertFIRToLLVMPattern::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
45mlir::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
53mlir::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.
65mlir::Value
66ConvertFIRToLLVMPattern::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
83fir::ConvertFIRToLLVMPattern::TypePair
84ConvertFIRToLLVMPattern::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`.
91mlir::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.
108llvm::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
121llvm::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
133mlir::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
146mlir::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
160mlir::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.
169mlir::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
176mlir::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.
184mlir::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.
203mlir::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.
211mlir::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`.
221mlir::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
240mlir::Block *
241ConvertFIRToLLVMPattern::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.
255mlir::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
291unsigned 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
303unsigned 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

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