xref: /freebsd/contrib/llvm-project/clang/lib/CIR/CodeGen/CIRGenBuilder.h (revision 700637cbb5e582861067a11aaca4d053546871d2)
1 //===----------------------------------------------------------------------===//
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 LLVM_CLANG_LIB_CIR_CODEGEN_CIRGENBUILDER_H
10 #define LLVM_CLANG_LIB_CIR_CODEGEN_CIRGENBUILDER_H
11 
12 #include "Address.h"
13 #include "CIRGenRecordLayout.h"
14 #include "CIRGenTypeCache.h"
15 #include "clang/CIR/Interfaces/CIRTypeInterfaces.h"
16 #include "clang/CIR/MissingFeatures.h"
17 
18 #include "clang/CIR/Dialect/Builder/CIRBaseBuilder.h"
19 #include "clang/CIR/MissingFeatures.h"
20 #include "llvm/ADT/APFloat.h"
21 #include "llvm/ADT/STLExtras.h"
22 
23 namespace clang::CIRGen {
24 
25 class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
26   const CIRGenTypeCache &typeCache;
27   llvm::StringMap<unsigned> recordNames;
28   llvm::StringMap<unsigned> globalsVersioning;
29 
30 public:
CIRGenBuilderTy(mlir::MLIRContext & mlirContext,const CIRGenTypeCache & tc)31   CIRGenBuilderTy(mlir::MLIRContext &mlirContext, const CIRGenTypeCache &tc)
32       : CIRBaseBuilderTy(mlirContext), typeCache(tc) {}
33 
34   /// Get a cir::ConstArrayAttr for a string literal.
35   /// Note: This is different from what is returned by
36   /// mlir::Builder::getStringAttr() which is an mlir::StringAttr.
getString(llvm::StringRef str,mlir::Type eltTy,std::optional<size_t> size)37   mlir::Attribute getString(llvm::StringRef str, mlir::Type eltTy,
38                             std::optional<size_t> size) {
39     size_t finalSize = size.value_or(str.size());
40 
41     size_t lastNonZeroPos = str.find_last_not_of('\0');
42     // If the string is full of null bytes, emit a #cir.zero rather than
43     // a #cir.const_array.
44     if (lastNonZeroPos == llvm::StringRef::npos) {
45       auto arrayTy = cir::ArrayType::get(eltTy, finalSize);
46       return cir::ZeroAttr::get(arrayTy);
47     }
48     // We emit trailing zeros only if there are multiple trailing zeros.
49     size_t trailingZerosNum = 0;
50     if (finalSize > lastNonZeroPos + 2)
51       trailingZerosNum = finalSize - lastNonZeroPos - 1;
52     auto truncatedArrayTy =
53         cir::ArrayType::get(eltTy, finalSize - trailingZerosNum);
54     auto fullArrayTy = cir::ArrayType::get(eltTy, finalSize);
55     return cir::ConstArrayAttr::get(
56         fullArrayTy,
57         mlir::StringAttr::get(str.drop_back(trailingZerosNum),
58                               truncatedArrayTy),
59         trailingZerosNum);
60   }
61 
getUniqueAnonRecordName()62   std::string getUniqueAnonRecordName() { return getUniqueRecordName("anon"); }
63 
getUniqueRecordName(const std::string & baseName)64   std::string getUniqueRecordName(const std::string &baseName) {
65     auto it = recordNames.find(baseName);
66     if (it == recordNames.end()) {
67       recordNames[baseName] = 0;
68       return baseName;
69     }
70 
71     return baseName + "." + std::to_string(recordNames[baseName]++);
72   }
73 
getLongDoubleTy(const llvm::fltSemantics & format)74   cir::LongDoubleType getLongDoubleTy(const llvm::fltSemantics &format) const {
75     if (&format == &llvm::APFloat::IEEEdouble())
76       return cir::LongDoubleType::get(getContext(), typeCache.DoubleTy);
77     if (&format == &llvm::APFloat::x87DoubleExtended())
78       return cir::LongDoubleType::get(getContext(), typeCache.FP80Ty);
79     if (&format == &llvm::APFloat::IEEEquad())
80       return cir::LongDoubleType::get(getContext(), typeCache.FP128Ty);
81     if (&format == &llvm::APFloat::PPCDoubleDouble())
82       llvm_unreachable("NYI: PPC double-double format for long double");
83     llvm_unreachable("Unsupported format for long double");
84   }
85 
86   /// Get a CIR record kind from a AST declaration tag.
getRecordKind(const clang::TagTypeKind kind)87   cir::RecordType::RecordKind getRecordKind(const clang::TagTypeKind kind) {
88     switch (kind) {
89     case clang::TagTypeKind::Class:
90       return cir::RecordType::Class;
91     case clang::TagTypeKind::Struct:
92       return cir::RecordType::Struct;
93     case clang::TagTypeKind::Union:
94       return cir::RecordType::Union;
95     case clang::TagTypeKind::Interface:
96       llvm_unreachable("interface records are NYI");
97     case clang::TagTypeKind::Enum:
98       llvm_unreachable("enums are not records");
99     }
100     llvm_unreachable("Unsupported record kind");
101   }
102 
103   /// Get a CIR named record type.
104   ///
105   /// If a record already exists and is complete, but the client tries to fetch
106   /// it with a different set of attributes, this method will crash.
getCompleteRecordTy(llvm::ArrayRef<mlir::Type> members,llvm::StringRef name,bool packed,bool padded)107   cir::RecordType getCompleteRecordTy(llvm::ArrayRef<mlir::Type> members,
108                                       llvm::StringRef name, bool packed,
109                                       bool padded) {
110     const auto nameAttr = getStringAttr(name);
111     auto kind = cir::RecordType::RecordKind::Struct;
112     assert(!cir::MissingFeatures::astRecordDeclAttr());
113 
114     // Create or get the record.
115     auto type =
116         getType<cir::RecordType>(members, nameAttr, packed, padded, kind);
117 
118     // If we found an existing type, verify that either it is incomplete or
119     // it matches the requested attributes.
120     assert(!type.isIncomplete() ||
121            (type.getMembers() == members && type.getPacked() == packed &&
122             type.getPadded() == padded));
123 
124     // Complete an incomplete record or ensure the existing complete record
125     // matches the requested attributes.
126     type.complete(members, packed, padded);
127 
128     return type;
129   }
130 
131   /// Get an incomplete CIR struct type. If we have a complete record
132   /// declaration, we may create an incomplete type and then add the
133   /// members, so \p rd here may be complete.
getIncompleteRecordTy(llvm::StringRef name,const clang::RecordDecl * rd)134   cir::RecordType getIncompleteRecordTy(llvm::StringRef name,
135                                         const clang::RecordDecl *rd) {
136     const mlir::StringAttr nameAttr = getStringAttr(name);
137     cir::RecordType::RecordKind kind = cir::RecordType::RecordKind::Struct;
138     if (rd)
139       kind = getRecordKind(rd->getTagKind());
140     return getType<cir::RecordType>(nameAttr, kind);
141   }
142 
143   // Return true if the value is a null constant such as null pointer, (+0.0)
144   // for floating-point or zero initializer
isNullValue(mlir::Attribute attr)145   bool isNullValue(mlir::Attribute attr) const {
146     if (mlir::isa<cir::ZeroAttr>(attr))
147       return true;
148 
149     if (const auto ptrVal = mlir::dyn_cast<cir::ConstPtrAttr>(attr))
150       return ptrVal.isNullValue();
151 
152     if (const auto intVal = mlir::dyn_cast<cir::IntAttr>(attr))
153       return intVal.isNullValue();
154 
155     if (const auto boolVal = mlir::dyn_cast<cir::BoolAttr>(attr))
156       return !boolVal.getValue();
157 
158     if (auto fpAttr = mlir::dyn_cast<cir::FPAttr>(attr)) {
159       auto fpVal = fpAttr.getValue();
160       bool ignored;
161       llvm::APFloat fv(+0.0);
162       fv.convert(fpVal.getSemantics(), llvm::APFloat::rmNearestTiesToEven,
163                  &ignored);
164       return fv.bitwiseIsEqual(fpVal);
165     }
166 
167     if (const auto arrayVal = mlir::dyn_cast<cir::ConstArrayAttr>(attr)) {
168       if (mlir::isa<mlir::StringAttr>(arrayVal.getElts()))
169         return false;
170 
171       return llvm::all_of(
172           mlir::cast<mlir::ArrayAttr>(arrayVal.getElts()),
173           [&](const mlir::Attribute &elt) { return isNullValue(elt); });
174     }
175     return false;
176   }
177 
178   //
179   // Type helpers
180   // ------------
181   //
getUIntNTy(int n)182   cir::IntType getUIntNTy(int n) {
183     switch (n) {
184     case 8:
185       return getUInt8Ty();
186     case 16:
187       return getUInt16Ty();
188     case 32:
189       return getUInt32Ty();
190     case 64:
191       return getUInt64Ty();
192     default:
193       return cir::IntType::get(getContext(), n, false);
194     }
195   }
196 
getSIntNTy(int n)197   cir::IntType getSIntNTy(int n) {
198     switch (n) {
199     case 8:
200       return getSInt8Ty();
201     case 16:
202       return getSInt16Ty();
203     case 32:
204       return getSInt32Ty();
205     case 64:
206       return getSInt64Ty();
207     default:
208       return cir::IntType::get(getContext(), n, true);
209     }
210   }
211 
getVoidTy()212   cir::VoidType getVoidTy() { return typeCache.VoidTy; }
213 
getSInt8Ty()214   cir::IntType getSInt8Ty() { return typeCache.SInt8Ty; }
getSInt16Ty()215   cir::IntType getSInt16Ty() { return typeCache.SInt16Ty; }
getSInt32Ty()216   cir::IntType getSInt32Ty() { return typeCache.SInt32Ty; }
getSInt64Ty()217   cir::IntType getSInt64Ty() { return typeCache.SInt64Ty; }
218 
getUInt8Ty()219   cir::IntType getUInt8Ty() { return typeCache.UInt8Ty; }
getUInt16Ty()220   cir::IntType getUInt16Ty() { return typeCache.UInt16Ty; }
getUInt32Ty()221   cir::IntType getUInt32Ty() { return typeCache.UInt32Ty; }
getUInt64Ty()222   cir::IntType getUInt64Ty() { return typeCache.UInt64Ty; }
223 
224   cir::ConstantOp getConstInt(mlir::Location loc, llvm::APSInt intVal);
225 
226   cir::ConstantOp getConstInt(mlir::Location loc, llvm::APInt intVal);
227 
228   cir::ConstantOp getConstInt(mlir::Location loc, mlir::Type t, uint64_t c);
229 
230   cir::ConstantOp getConstFP(mlir::Location loc, mlir::Type t,
231                              llvm::APFloat fpVal);
232 
isInt8Ty(mlir::Type i)233   bool isInt8Ty(mlir::Type i) {
234     return i == typeCache.UInt8Ty || i == typeCache.SInt8Ty;
235   }
isInt16Ty(mlir::Type i)236   bool isInt16Ty(mlir::Type i) {
237     return i == typeCache.UInt16Ty || i == typeCache.SInt16Ty;
238   }
isInt32Ty(mlir::Type i)239   bool isInt32Ty(mlir::Type i) {
240     return i == typeCache.UInt32Ty || i == typeCache.SInt32Ty;
241   }
isInt64Ty(mlir::Type i)242   bool isInt64Ty(mlir::Type i) {
243     return i == typeCache.UInt64Ty || i == typeCache.SInt64Ty;
244   }
isInt(mlir::Type i)245   bool isInt(mlir::Type i) { return mlir::isa<cir::IntType>(i); }
246 
247   //
248   // Constant creation helpers
249   // -------------------------
250   //
getSInt32(int32_t c,mlir::Location loc)251   cir::ConstantOp getSInt32(int32_t c, mlir::Location loc) {
252     return getConstantInt(loc, getSInt32Ty(), c);
253   }
254 
255   // Creates constant nullptr for pointer type ty.
getNullPtr(mlir::Type ty,mlir::Location loc)256   cir::ConstantOp getNullPtr(mlir::Type ty, mlir::Location loc) {
257     assert(!cir::MissingFeatures::targetCodeGenInfoGetNullPointer());
258     return create<cir::ConstantOp>(loc, getConstPtrAttr(ty, 0));
259   }
260 
createNeg(mlir::Value value)261   mlir::Value createNeg(mlir::Value value) {
262 
263     if (auto intTy = mlir::dyn_cast<cir::IntType>(value.getType())) {
264       // Source is a unsigned integer: first cast it to signed.
265       if (intTy.isUnsigned())
266         value = createIntCast(value, getSIntNTy(intTy.getWidth()));
267       return create<cir::UnaryOp>(value.getLoc(), value.getType(),
268                                   cir::UnaryOpKind::Minus, value);
269     }
270 
271     llvm_unreachable("negation for the given type is NYI");
272   }
273 
274   // TODO: split this to createFPExt/createFPTrunc when we have dedicated cast
275   // operations.
createFloatingCast(mlir::Value v,mlir::Type destType)276   mlir::Value createFloatingCast(mlir::Value v, mlir::Type destType) {
277     assert(!cir::MissingFeatures::fpConstraints());
278 
279     return create<cir::CastOp>(v.getLoc(), destType, cir::CastKind::floating,
280                                v);
281   }
282 
createFSub(mlir::Location loc,mlir::Value lhs,mlir::Value rhs)283   mlir::Value createFSub(mlir::Location loc, mlir::Value lhs, mlir::Value rhs) {
284     assert(!cir::MissingFeatures::metaDataNode());
285     assert(!cir::MissingFeatures::fpConstraints());
286     assert(!cir::MissingFeatures::fastMathFlags());
287 
288     return create<cir::BinOp>(loc, cir::BinOpKind::Sub, lhs, rhs);
289   }
290 
createFAdd(mlir::Location loc,mlir::Value lhs,mlir::Value rhs)291   mlir::Value createFAdd(mlir::Location loc, mlir::Value lhs, mlir::Value rhs) {
292     assert(!cir::MissingFeatures::metaDataNode());
293     assert(!cir::MissingFeatures::fpConstraints());
294     assert(!cir::MissingFeatures::fastMathFlags());
295 
296     return create<cir::BinOp>(loc, cir::BinOpKind::Add, lhs, rhs);
297   }
createFMul(mlir::Location loc,mlir::Value lhs,mlir::Value rhs)298   mlir::Value createFMul(mlir::Location loc, mlir::Value lhs, mlir::Value rhs) {
299     assert(!cir::MissingFeatures::metaDataNode());
300     assert(!cir::MissingFeatures::fpConstraints());
301     assert(!cir::MissingFeatures::fastMathFlags());
302 
303     return create<cir::BinOp>(loc, cir::BinOpKind::Mul, lhs, rhs);
304   }
createFDiv(mlir::Location loc,mlir::Value lhs,mlir::Value rhs)305   mlir::Value createFDiv(mlir::Location loc, mlir::Value lhs, mlir::Value rhs) {
306     assert(!cir::MissingFeatures::metaDataNode());
307     assert(!cir::MissingFeatures::fpConstraints());
308     assert(!cir::MissingFeatures::fastMathFlags());
309 
310     return create<cir::BinOp>(loc, cir::BinOpKind::Div, lhs, rhs);
311   }
312 
createBaseClassAddr(mlir::Location loc,Address addr,mlir::Type destType,unsigned offset,bool assumeNotNull)313   Address createBaseClassAddr(mlir::Location loc, Address addr,
314                               mlir::Type destType, unsigned offset,
315                               bool assumeNotNull) {
316     if (destType == addr.getElementType())
317       return addr;
318 
319     auto ptrTy = getPointerTo(destType);
320     auto baseAddr = create<cir::BaseClassAddrOp>(
321         loc, ptrTy, addr.getPointer(), mlir::APInt(64, offset), assumeNotNull);
322     return Address(baseAddr, destType, addr.getAlignment());
323   }
324 
325   /// Cast the element type of the given address to a different type,
326   /// preserving information like the alignment.
createElementBitCast(mlir::Location loc,Address addr,mlir::Type destType)327   Address createElementBitCast(mlir::Location loc, Address addr,
328                                mlir::Type destType) {
329     if (destType == addr.getElementType())
330       return addr;
331 
332     auto ptrTy = getPointerTo(destType);
333     return Address(createBitcast(loc, addr.getPointer(), ptrTy), destType,
334                    addr.getAlignment());
335   }
336 
337   cir::LoadOp createLoad(mlir::Location loc, Address addr,
338                          bool isVolatile = false) {
339     mlir::IntegerAttr align = getAlignmentAttr(addr.getAlignment());
340     return create<cir::LoadOp>(loc, addr.getPointer(), /*isDeref=*/false,
341                                align);
342   }
343 
344   cir::StoreOp createStore(mlir::Location loc, mlir::Value val, Address dst,
345                            mlir::IntegerAttr align = {}) {
346     if (!align)
347       align = getAlignmentAttr(dst.getAlignment());
348     return CIRBaseBuilderTy::createStore(loc, val, dst.getPointer(), align);
349   }
350 
createComplexCreate(mlir::Location loc,mlir::Value real,mlir::Value imag)351   mlir::Value createComplexCreate(mlir::Location loc, mlir::Value real,
352                                   mlir::Value imag) {
353     auto resultComplexTy = cir::ComplexType::get(real.getType());
354     return create<cir::ComplexCreateOp>(loc, resultComplexTy, real, imag);
355   }
356 
createComplexReal(mlir::Location loc,mlir::Value operand)357   mlir::Value createComplexReal(mlir::Location loc, mlir::Value operand) {
358     auto operandTy = mlir::cast<cir::ComplexType>(operand.getType());
359     return create<cir::ComplexRealOp>(loc, operandTy.getElementType(), operand);
360   }
361 
createComplexImag(mlir::Location loc,mlir::Value operand)362   mlir::Value createComplexImag(mlir::Location loc, mlir::Value operand) {
363     auto operandTy = mlir::cast<cir::ComplexType>(operand.getType());
364     return create<cir::ComplexImagOp>(loc, operandTy.getElementType(), operand);
365   }
366 
367   /// Create a cir.complex.real_ptr operation that derives a pointer to the real
368   /// part of the complex value pointed to by the specified pointer value.
createComplexRealPtr(mlir::Location loc,mlir::Value value)369   mlir::Value createComplexRealPtr(mlir::Location loc, mlir::Value value) {
370     auto srcPtrTy = mlir::cast<cir::PointerType>(value.getType());
371     auto srcComplexTy = mlir::cast<cir::ComplexType>(srcPtrTy.getPointee());
372     return create<cir::ComplexRealPtrOp>(
373         loc, getPointerTo(srcComplexTy.getElementType()), value);
374   }
375 
createComplexRealPtr(mlir::Location loc,Address addr)376   Address createComplexRealPtr(mlir::Location loc, Address addr) {
377     return Address{createComplexRealPtr(loc, addr.getPointer()),
378                    addr.getAlignment()};
379   }
380 
381   /// Create a cir.complex.imag_ptr operation that derives a pointer to the
382   /// imaginary part of the complex value pointed to by the specified pointer
383   /// value.
createComplexImagPtr(mlir::Location loc,mlir::Value value)384   mlir::Value createComplexImagPtr(mlir::Location loc, mlir::Value value) {
385     auto srcPtrTy = mlir::cast<cir::PointerType>(value.getType());
386     auto srcComplexTy = mlir::cast<cir::ComplexType>(srcPtrTy.getPointee());
387     return create<cir::ComplexImagPtrOp>(
388         loc, getPointerTo(srcComplexTy.getElementType()), value);
389   }
390 
createComplexImagPtr(mlir::Location loc,Address addr)391   Address createComplexImagPtr(mlir::Location loc, Address addr) {
392     return Address{createComplexImagPtr(loc, addr.getPointer()),
393                    addr.getAlignment()};
394   }
395 
396   /// Create a cir.ptr_stride operation to get access to an array element.
397   /// \p idx is the index of the element to access, \p shouldDecay is true if
398   /// the result should decay to a pointer to the element type.
399   mlir::Value getArrayElement(mlir::Location arrayLocBegin,
400                               mlir::Location arrayLocEnd, mlir::Value arrayPtr,
401                               mlir::Type eltTy, mlir::Value idx,
402                               bool shouldDecay);
403 
404   /// Returns a decayed pointer to the first element of the array
405   /// pointed to by \p arrayPtr.
406   mlir::Value maybeBuildArrayDecay(mlir::Location loc, mlir::Value arrayPtr,
407                                    mlir::Type eltTy);
408 
409   /// Creates a versioned global variable. If the symbol is already taken, an ID
410   /// will be appended to the symbol. The returned global must always be queried
411   /// for its name so it can be referenced correctly.
412   [[nodiscard]] cir::GlobalOp
createVersionedGlobal(mlir::ModuleOp module,mlir::Location loc,mlir::StringRef name,mlir::Type type,cir::GlobalLinkageKind linkage)413   createVersionedGlobal(mlir::ModuleOp module, mlir::Location loc,
414                         mlir::StringRef name, mlir::Type type,
415                         cir::GlobalLinkageKind linkage) {
416     // Create a unique name if the given name is already taken.
417     std::string uniqueName;
418     if (unsigned version = globalsVersioning[name.str()]++)
419       uniqueName = name.str() + "." + std::to_string(version);
420     else
421       uniqueName = name.str();
422 
423     return createGlobal(module, loc, uniqueName, type, linkage);
424   }
425 
createSetBitfield(mlir::Location loc,mlir::Type resultType,mlir::Value dstAddr,mlir::Type storageType,mlir::Value src,const CIRGenBitFieldInfo & info,bool isLvalueVolatile,bool useVolatile)426   mlir::Value createSetBitfield(mlir::Location loc, mlir::Type resultType,
427                                 mlir::Value dstAddr, mlir::Type storageType,
428                                 mlir::Value src, const CIRGenBitFieldInfo &info,
429                                 bool isLvalueVolatile, bool useVolatile) {
430     return create<cir::SetBitfieldOp>(loc, resultType, dstAddr, storageType,
431                                       src, info.name, info.size, info.offset,
432                                       info.isSigned, isLvalueVolatile);
433   }
434 
createGetBitfield(mlir::Location loc,mlir::Type resultType,mlir::Value addr,mlir::Type storageType,const CIRGenBitFieldInfo & info,bool isLvalueVolatile,bool useVolatile)435   mlir::Value createGetBitfield(mlir::Location loc, mlir::Type resultType,
436                                 mlir::Value addr, mlir::Type storageType,
437                                 const CIRGenBitFieldInfo &info,
438                                 bool isLvalueVolatile, bool useVolatile) {
439     return create<cir::GetBitfieldOp>(loc, resultType, addr, storageType,
440                                       info.name, info.size, info.offset,
441                                       info.isSigned, isLvalueVolatile);
442   }
443 };
444 
445 } // namespace clang::CIRGen
446 
447 #endif
448