//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_LIB_CIR_CODEGEN_CIRGENBUILDER_H #define LLVM_CLANG_LIB_CIR_CODEGEN_CIRGENBUILDER_H #include "Address.h" #include "CIRGenRecordLayout.h" #include "CIRGenTypeCache.h" #include "mlir/Dialect/Ptr/IR/MemorySpaceInterfaces.h" #include "mlir/IR/Attributes.h" #include "mlir/IR/Builders.h" #include "mlir/IR/BuiltinAttributes.h" #include "mlir/Support/LLVM.h" #include "clang/CIR/Dialect/IR/CIRDataLayout.h" #include "clang/CIR/MissingFeatures.h" #include "clang/CIR/Dialect/Builder/CIRBaseBuilder.h" #include "clang/CIR/MissingFeatures.h" #include "llvm/ADT/APFloat.h" #include "llvm/ADT/STLExtras.h" #include "llvm/IR/FPEnv.h" namespace clang::CIRGen { class CIRGenBuilderTy : public cir::CIRBaseBuilderTy { const CIRGenTypeCache &typeCache; bool isFPConstrained = false; llvm::fp::ExceptionBehavior defaultConstrainedExcept = llvm::fp::ebStrict; llvm::RoundingMode defaultConstrainedRounding = llvm::RoundingMode::Dynamic; llvm::StringMap recordNames; llvm::StringMap globalsVersioning; public: CIRGenBuilderTy(mlir::MLIRContext &mlirContext, const CIRGenTypeCache &tc) : CIRBaseBuilderTy(mlirContext), typeCache(tc) {} /// Get a cir::ConstArrayAttr for a string literal. /// Note: This is different from what is returned by /// mlir::Builder::getStringAttr() which is an mlir::StringAttr. mlir::Attribute getString(llvm::StringRef str, mlir::Type eltTy, std::optional size, bool ensureNullTerm = true) { size_t finalSize = size.value_or(str.size()); size_t lastNonZeroPos = str.find_last_not_of('\0'); // If the string is full of null bytes, emit a #cir.zero rather than // a #cir.const_array. if (lastNonZeroPos == llvm::StringRef::npos) { auto arrayTy = cir::ArrayType::get(eltTy, finalSize); return cir::ZeroAttr::get(arrayTy); } // We emit trailing zeros for all trailing zeros, so the null-terminator in // a constant is always in trailing zeros, and the null-terminator is // skipped in the CIR representation. size_t trailingZerosNum = finalSize - lastNonZeroPos - 1; auto truncatedArrayTy = cir::ArrayType::get(eltTy, finalSize - trailingZerosNum); auto strAttr = mlir::StringAttr::get(str.drop_back(trailingZerosNum), truncatedArrayTy); // Most C strings are null terminated, so if we are ensuring there is one, // grow the array size by 1 to add a trailing zero if necessary. The 'auto' // calculation of trailing zeros (the difference between the provided string // and the type) will ensure we get the count correct. finalSize += (ensureNullTerm && trailingZerosNum == 0); auto fullArrayTy = cir::ArrayType::get(eltTy, finalSize); return cir::ConstArrayAttr::get(fullArrayTy, strAttr); } cir::ConstArrayAttr getConstArray(mlir::Attribute attrs, cir::ArrayType arrayTy) const { return cir::ConstArrayAttr::get(arrayTy, attrs); } mlir::Attribute getConstRecordOrZeroAttr(mlir::ArrayAttr arrayAttr, bool packed = false, bool padded = false, mlir::Type type = {}); cir::ConstRecordAttr getAnonConstRecord(mlir::ArrayAttr arrayAttr, bool packed = false, bool padded = false, mlir::Type ty = {}) { llvm::SmallVector members; for (auto &f : arrayAttr) { auto ta = mlir::cast(f); members.push_back(ta.getType()); } if (!ty) ty = getAnonRecordTy(members, packed, padded); auto sTy = mlir::cast(ty); return cir::ConstRecordAttr::get(sTy, arrayAttr); } cir::TypeInfoAttr getTypeInfo(mlir::ArrayAttr fieldsAttr) { cir::ConstRecordAttr anonRecord = getAnonConstRecord(fieldsAttr); return cir::TypeInfoAttr::get(anonRecord.getType(), fieldsAttr); } std::string getUniqueAnonRecordName() { return getUniqueRecordName("anon"); } std::string getUniqueRecordName(const std::string &baseName) { auto it = recordNames.find(baseName); if (it == recordNames.end()) { recordNames[baseName] = 0; return baseName; } return baseName + "." + std::to_string(recordNames[baseName]++); } // // Floating point specific helpers // ------------------------------- // /// Enable/Disable use of constrained floating point math. When enabled the /// CreateF() calls instead create constrained floating point intrinsic /// calls. Fast math flags are unaffected by this setting. void setIsFPConstrained(bool isCon) { isFPConstrained = isCon; } /// Query for the use of constrained floating point math bool getIsFPConstrained() const { return isFPConstrained; } /// Set the exception handling to be used with constrained floating point void setDefaultConstrainedExcept(llvm::fp::ExceptionBehavior newExcept) { assert(llvm::convertExceptionBehaviorToStr(newExcept) && "Garbage strict exception behavior!"); defaultConstrainedExcept = newExcept; } /// Get the exception handling used with constrained floating point llvm::fp::ExceptionBehavior getDefaultConstrainedExcept() const { return defaultConstrainedExcept; } /// Set the rounding mode handling to be used with constrained floating point void setDefaultConstrainedRounding(llvm::RoundingMode newRounding) { assert(llvm::convertRoundingModeToStr(newRounding) && "Garbage strict rounding mode!"); defaultConstrainedRounding = newRounding; } /// Get the rounding mode handling used with constrained floating point llvm::RoundingMode getDefaultConstrainedRounding() const { return defaultConstrainedRounding; } cir::LongDoubleType getLongDoubleTy(const llvm::fltSemantics &format) const { if (&format == &llvm::APFloat::IEEEdouble()) return cir::LongDoubleType::get(getContext(), typeCache.doubleTy); if (&format == &llvm::APFloat::x87DoubleExtended()) return cir::LongDoubleType::get(getContext(), typeCache.fP80Ty); if (&format == &llvm::APFloat::IEEEquad()) return cir::LongDoubleType::get(getContext(), typeCache.fP128Ty); if (&format == &llvm::APFloat::PPCDoubleDouble()) llvm_unreachable("NYI: PPC double-double format for long double"); llvm_unreachable("Unsupported format for long double"); } mlir::Type getPtrToVPtrType() { return getPointerTo(cir::VPtrType::get(getContext())); } cir::FuncType getFuncType(llvm::ArrayRef params, mlir::Type retTy, bool isVarArg = false) { return cir::FuncType::get(params, retTy, isVarArg); } /// Get a CIR record kind from a AST declaration tag. /// Returns true if the tag kind represents a C++ class (as opposed to a /// plain struct). static bool tagKindIsClass(const clang::TagTypeKind kind) { return kind == clang::TagTypeKind::Class; } /// Returns true if the tag kind is a union. static bool tagKindIsUnion(const clang::TagTypeKind kind) { return kind == clang::TagTypeKind::Union; } /// Get a CIR named record type. /// /// If a record already exists and is complete, but the client tries to fetch /// it with a different set of attributes, this method will crash. cir::RecordType getCompleteNamedRecordType(llvm::ArrayRef members, bool packed, bool padded, llvm::StringRef name) { const auto nameAttr = getStringAttr(name); assert(!cir::MissingFeatures::astRecordDeclAttr()); // Create or get the struct type (named anonymous struct helper — always // struct, never class or union at this call site). auto type = cir::StructType::get(getContext(), members, nameAttr, packed, padded, /*is_class=*/false); // If we found an existing type, verify that either it is incomplete or // it matches the requested attributes. assert(!type.isIncomplete() || (type.getMembers() == members && type.getPacked() == packed && type.getPadded() == padded)); // Complete an incomplete record or ensure the existing complete record // matches the requested attributes. type.complete(members, packed, padded); return type; } cir::RecordType getCompleteRecordType(mlir::ArrayAttr fields, bool packed = false, bool padded = false, llvm::StringRef name = ""); /// Get an incomplete CIR record type. If we have a complete record /// declaration, we may create an incomplete type and then add the /// members, so \p rd here may be complete. cir::RecordType getIncompleteRecordTy(llvm::StringRef name, const clang::RecordDecl *rd) { const mlir::StringAttr nameAttr = getStringAttr(name); if (rd && tagKindIsUnion(rd->getTagKind())) return cir::UnionType::get(getContext(), nameAttr); bool is_class = rd && tagKindIsClass(rd->getTagKind()); return cir::StructType::get(getContext(), nameAttr, is_class); } // // Operation creation helpers // -------------------------- // cir::MemCpyOp createMemCpy(mlir::Location loc, mlir::Value dst, mlir::Value src, mlir::Value len) { return cir::MemCpyOp::create(*this, loc, dst, src, len); } cir::MemMoveOp createMemMove(mlir::Location loc, mlir::Value dst, mlir::Value src, mlir::Value len) { return cir::MemMoveOp::create(*this, loc, dst, src, len); } cir::MemSetOp createMemSet(mlir::Location loc, mlir::Value dst, mlir::Value val, mlir::Value len) { assert(val.getType() == getUInt8Ty()); return cir::MemSetOp::create(*this, loc, dst, {}, val, len); } cir::MemSetOp createMemSet(mlir::Location loc, Address dst, mlir::Value val, mlir::Value len) { mlir::IntegerAttr align = getAlignmentAttr(dst.getAlignment()); assert(val.getType() == getUInt8Ty()); return cir::MemSetOp::create(*this, loc, dst.getPointer(), align, val, len); } // --------------------------- cir::DataMemberAttr getDataMemberAttr(cir::DataMemberType ty, unsigned memberIndex) { return cir::DataMemberAttr::get(ty, memberIndex); } cir::DataMemberAttr getNullDataMemberAttr(cir::DataMemberType ty) { return cir::DataMemberAttr::get(ty); } // Return true if the value is a null constant such as null pointer, (+0.0) // for floating-point or zero initializer bool isNullValue(mlir::Attribute attr) const { if (mlir::isa(attr)) return true; if (const auto ptrVal = mlir::dyn_cast(attr)) return ptrVal.isNullValue(); if (const auto intVal = mlir::dyn_cast(attr)) return intVal.isNullValue(); if (const auto boolVal = mlir::dyn_cast(attr)) return !boolVal.getValue(); if (auto fpAttr = mlir::dyn_cast(attr)) { auto fpVal = fpAttr.getValue(); bool ignored; llvm::APFloat fv(+0.0); fv.convert(fpVal.getSemantics(), llvm::APFloat::rmNearestTiesToEven, &ignored); return fv.bitwiseIsEqual(fpVal); } if (const auto recordVal = mlir::dyn_cast(attr)) { for (const auto elt : recordVal.getMembers()) { // FIXME(cir): the record's ID should not be considered a member. if (mlir::isa(elt)) continue; if (!isNullValue(elt)) return false; } return true; } if (const auto arrayVal = mlir::dyn_cast(attr)) { if (mlir::isa(arrayVal.getElts())) return false; return llvm::all_of( mlir::cast(arrayVal.getElts()), [&](const mlir::Attribute &elt) { return isNullValue(elt); }); } return false; } // // Type helpers // ------------ // cir::IntType getUIntNTy(int n) { switch (n) { case 8: return getUInt8Ty(); case 16: return getUInt16Ty(); case 32: return getUInt32Ty(); case 64: return getUInt64Ty(); default: return cir::IntType::get(getContext(), n, false); } } cir::IntType getSIntNTy(int n) { switch (n) { case 8: return getSInt8Ty(); case 16: return getSInt16Ty(); case 32: return getSInt32Ty(); case 64: return getSInt64Ty(); default: return cir::IntType::get(getContext(), n, true); } } cir::VoidType getVoidTy() { return typeCache.voidTy; } cir::IntType getSInt8Ty() { return typeCache.sInt8Ty; } cir::IntType getSInt16Ty() { return typeCache.sInt16Ty; } cir::IntType getSInt32Ty() { return typeCache.sInt32Ty; } cir::IntType getSInt64Ty() { return typeCache.sInt64Ty; } cir::IntType getUInt8Ty() { return typeCache.uInt8Ty; } cir::IntType getUInt16Ty() { return typeCache.uInt16Ty; } cir::IntType getUInt32Ty() { return typeCache.uInt32Ty; } cir::IntType getUInt64Ty() { return typeCache.uInt64Ty; } cir::FP16Type getFp16Ty() { return typeCache.fP16Ty; } cir::BF16Type getBfloat6Ty() { return typeCache.bFloat16Ty; } cir::SingleType getSingleTy() { return typeCache.floatTy; } cir::DoubleType getDoubleTy() { return typeCache.doubleTy; } cir::ConstantOp getConstInt(mlir::Location loc, llvm::APSInt intVal); cir::ConstantOp getConstInt(mlir::Location loc, llvm::APInt intVal, bool isUnsigned = true); cir::ConstantOp getConstInt(mlir::Location loc, mlir::Type t, uint64_t c); cir::ConstantOp getConstFP(mlir::Location loc, mlir::Type t, llvm::APFloat fpVal); bool isInt8Ty(mlir::Type i) { return i == typeCache.uInt8Ty || i == typeCache.sInt8Ty; } bool isInt16Ty(mlir::Type i) { return i == typeCache.uInt16Ty || i == typeCache.sInt16Ty; } bool isInt32Ty(mlir::Type i) { return i == typeCache.uInt32Ty || i == typeCache.sInt32Ty; } bool isInt64Ty(mlir::Type i) { return i == typeCache.uInt64Ty || i == typeCache.sInt64Ty; } bool isInt(mlir::Type i) { return mlir::isa(i); } cir::IntType getExtendedIntTy(cir::IntType ty, bool isSigned) { switch (ty.getWidth()) { case 8: return isSigned ? typeCache.sInt16Ty : typeCache.uInt16Ty; case 16: return isSigned ? typeCache.sInt32Ty : typeCache.uInt32Ty; case 32: return isSigned ? typeCache.sInt64Ty : typeCache.uInt64Ty; default: llvm_unreachable("NYI"); } } cir::IntType getTruncatedIntTy(cir::IntType ty, bool isSigned) { switch (ty.getWidth()) { case 16: return isSigned ? typeCache.sInt8Ty : typeCache.uInt8Ty; case 32: return isSigned ? typeCache.sInt16Ty : typeCache.uInt16Ty; case 64: return isSigned ? typeCache.sInt32Ty : typeCache.uInt32Ty; default: llvm_unreachable("NYI"); } } cir::VectorType getExtendedOrTruncatedElementVectorType(cir::VectorType vt, bool isExtended, bool isSigned = false) { auto elementTy = mlir::dyn_cast_or_null(vt.getElementType()); assert(elementTy && "expected int vector"); return cir::VectorType::get(isExtended ? getExtendedIntTy(elementTy, isSigned) : getTruncatedIntTy(elementTy, isSigned), vt.getSize()); } // Fetch the type representing a pointer to unsigned int8 values. cir::PointerType getUInt8PtrTy() { return typeCache.uInt8PtrTy; } /// Get a CIR anonymous struct type. cir::StructType getAnonRecordTy(llvm::ArrayRef members, bool packed = false, bool padded = false) { assert(!cir::MissingFeatures::astRecordDeclAttr()); return cir::StructType::get(getContext(), members, packed, padded, /*is_class=*/false); } //===--------------------------------------------------------------------===// // Constant creation helpers //===--------------------------------------------------------------------===// cir::ConstantOp getSInt32(int32_t c, mlir::Location loc) { return getConstantInt(loc, getSInt32Ty(), c); } cir::ConstantOp getUInt32(uint32_t c, mlir::Location loc) { return getConstantInt(loc, getUInt32Ty(), c); } cir::ConstantOp getSInt64(uint64_t c, mlir::Location loc) { return getConstantInt(loc, getSInt64Ty(), c); } cir::ConstantOp getUInt64(uint64_t c, mlir::Location loc) { return getConstantInt(loc, getUInt64Ty(), c); } cir::ConstantOp getZero(mlir::Location loc, mlir::Type ty) { // TODO: dispatch creation for primitive types. assert((mlir::isa(ty) || mlir::isa(ty) || mlir::isa(ty)) && "NYI for other types"); return cir::ConstantOp::create(*this, loc, cir::ZeroAttr::get(ty)); } //===--------------------------------------------------------------------===// // UnaryOp creation helpers //===--------------------------------------------------------------------===// mlir::Value createNeg(mlir::Value value, bool nsw = false) { if (auto intTy = mlir::dyn_cast(value.getType())) { // Source is a unsigned integer: first cast it to signed. if (intTy.isUnsigned()) value = createIntCast(value, getSIntNTy(intTy.getWidth())); return createMinus(value.getLoc(), value, nsw); } llvm_unreachable("negation for the given type is NYI"); } mlir::Value createFNeg(mlir::Value value) { assert(mlir::isa(value.getType()) && "Non-fp input type!"); assert(!cir::MissingFeatures::metaDataNode()); assert(!cir::MissingFeatures::fpConstraints()); assert(!cir::MissingFeatures::fastMathFlags()); return createMinus(value.getLoc(), value); } //===--------------------------------------------------------------------===// // BinaryOp creation helpers //===--------------------------------------------------------------------===// mlir::Value createFSub(mlir::Location loc, mlir::Value lhs, mlir::Value rhs) { assert(!cir::MissingFeatures::metaDataNode()); assert(!cir::MissingFeatures::fpConstraints()); assert(!cir::MissingFeatures::fastMathFlags()); return cir::SubOp::create(*this, loc, lhs, rhs); } mlir::Value createFAdd(mlir::Location loc, mlir::Value lhs, mlir::Value rhs) { assert(!cir::MissingFeatures::metaDataNode()); assert(!cir::MissingFeatures::fpConstraints()); assert(!cir::MissingFeatures::fastMathFlags()); return cir::AddOp::create(*this, loc, lhs, rhs); } mlir::Value createFMul(mlir::Location loc, mlir::Value lhs, mlir::Value rhs) { assert(!cir::MissingFeatures::metaDataNode()); assert(!cir::MissingFeatures::fpConstraints()); assert(!cir::MissingFeatures::fastMathFlags()); return cir::MulOp::create(*this, loc, lhs, rhs); } mlir::Value createFDiv(mlir::Location loc, mlir::Value lhs, mlir::Value rhs) { assert(!cir::MissingFeatures::metaDataNode()); assert(!cir::MissingFeatures::fpConstraints()); assert(!cir::MissingFeatures::fastMathFlags()); return cir::DivOp::create(*this, loc, lhs, rhs); } //===--------------------------------------------------------------------===// // CastOp creation helpers //===--------------------------------------------------------------------===// // TODO: split this to createFPExt/createFPTrunc when we have dedicated cast // operations. mlir::Value createFloatingCast(mlir::Value v, mlir::Type destType) { assert(!cir::MissingFeatures::fpConstraints()); return cir::CastOp::create(*this, v.getLoc(), destType, cir::CastKind::floating, v); } mlir::Value createDynCast(mlir::Location loc, mlir::Value src, cir::PointerType destType, bool isRefCast, cir::DynamicCastInfoAttr info) { auto castKind = isRefCast ? cir::DynamicCastKind::Ref : cir::DynamicCastKind::Ptr; return cir::DynamicCastOp::create(*this, loc, destType, castKind, src, info, /*relative_layout=*/false); } mlir::Value createDynCastToVoid(mlir::Location loc, mlir::Value src, bool vtableUseRelativeLayout) { // TODO(cir): consider address space here. assert(!cir::MissingFeatures::addressSpace()); cir::PointerType destTy = getVoidPtrTy(); return cir::DynamicCastOp::create( *this, loc, destTy, cir::DynamicCastKind::Ptr, src, cir::DynamicCastInfoAttr{}, vtableUseRelativeLayout); } //===--------------------------------------------------------------------===// // Address creation helpers //===--------------------------------------------------------------------===// Address createBaseClassAddr(mlir::Location loc, Address addr, mlir::Type destType, unsigned offset, bool assumeNotNull) { if (destType == addr.getElementType()) return addr; auto ptrTy = getPointerTo(destType); auto baseAddr = cir::BaseClassAddrOp::create(*this, loc, ptrTy, addr.getPointer(), mlir::APInt(64, offset), assumeNotNull); return Address(baseAddr, destType, addr.getAlignment()); } Address createDerivedClassAddr(mlir::Location loc, Address addr, mlir::Type destType, unsigned offset, bool assumeNotNull) { if (destType == addr.getElementType()) return addr; cir::PointerType ptrTy = getPointerTo(destType); auto derivedAddr = cir::DerivedClassAddrOp::create(*this, loc, ptrTy, addr.getPointer(), mlir::APInt(64, offset), assumeNotNull); return Address(derivedAddr, destType, addr.getAlignment()); } //===--------------------------------------------------------------------===// // Virtual Address creation helpers //===--------------------------------------------------------------------===// mlir::Value createVTTAddrPoint(mlir::Location loc, mlir::Type retTy, mlir::Value addr, uint64_t offset) { return cir::VTTAddrPointOp::create(*this, loc, retTy, mlir::FlatSymbolRefAttr{}, addr, offset); } mlir::Value createVTTAddrPoint(mlir::Location loc, mlir::Type retTy, mlir::FlatSymbolRefAttr sym, uint64_t offset) { return cir::VTTAddrPointOp::create(*this, loc, retTy, sym, mlir::Value{}, offset); } //===--------------------------------------------------------------------===// // Other creation helpers //===--------------------------------------------------------------------===// cir::IsFPClassOp createIsFPClass(mlir::Location loc, mlir::Value src, cir::FPClassTest flags) { return cir::IsFPClassOp::create(*this, loc, src, flags); } /// Cast the element type of the given address to a different type, /// preserving information like the alignment. Address createElementBitCast(mlir::Location loc, Address addr, mlir::Type destType) { if (destType == addr.getElementType()) return addr; auto ptrTy = getPointerTo(destType); return Address(createBitcast(loc, addr.getPointer(), ptrTy), destType, addr.getAlignment()); } cir::LoadOp createLoad(mlir::Location loc, Address addr, bool isVolatile = false) { mlir::IntegerAttr align = getAlignmentAttr(addr.getAlignment()); return cir::LoadOp::create(*this, loc, addr.getPointer(), /*isDeref=*/false, isVolatile, /*alignment=*/align, /*sync_scope=*/cir::SyncScopeKindAttr{}, /*mem_order=*/cir::MemOrderAttr{}); } cir::LoadOp createAlignedLoad(mlir::Location loc, mlir::Type ty, mlir::Value ptr, llvm::MaybeAlign align) { if (ty != mlir::cast(ptr.getType()).getPointee()) ptr = createPtrBitcast(ptr, ty); uint64_t alignment = align ? align->value() : 0; mlir::IntegerAttr alignAttr = getAlignmentAttr(alignment); return cir::LoadOp::create(*this, loc, ptr, /*isDeref=*/false, /*isVolatile=*/false, alignAttr, /*sync_scope=*/cir::SyncScopeKindAttr{}, /*mem_order=*/cir::MemOrderAttr{}); } cir::LoadOp createAlignedLoad(mlir::Location loc, mlir::Type ty, mlir::Value ptr, clang::CharUnits align = clang::CharUnits::One()) { return createAlignedLoad(loc, ty, ptr, align.getAsAlign()); } cir::StoreOp createStore(mlir::Location loc, mlir::Value val, Address dst, bool isVolatile = false, mlir::IntegerAttr align = {}, cir::SyncScopeKindAttr scope = {}, cir::MemOrderAttr order = {}) { if (!align) align = getAlignmentAttr(dst.getAlignment()); return CIRBaseBuilderTy::createStore(loc, val, dst.getPointer(), isVolatile, align, scope, order); } /// Create a cir.complex.real_ptr operation that derives a pointer to the real /// part of the complex value pointed to by the specified pointer value. mlir::Value createComplexRealPtr(mlir::Location loc, mlir::Value value) { auto srcPtrTy = mlir::cast(value.getType()); auto srcComplexTy = mlir::cast(srcPtrTy.getPointee()); return cir::ComplexRealPtrOp::create( *this, loc, getPointerTo(srcComplexTy.getElementType()), value); } Address createComplexRealPtr(mlir::Location loc, Address addr) { return Address{createComplexRealPtr(loc, addr.getPointer()), addr.getAlignment()}; } /// Create a cir.complex.imag_ptr operation that derives a pointer to the /// imaginary part of the complex value pointed to by the specified pointer /// value. mlir::Value createComplexImagPtr(mlir::Location loc, mlir::Value value) { auto srcPtrTy = mlir::cast(value.getType()); auto srcComplexTy = mlir::cast(srcPtrTy.getPointee()); return cir::ComplexImagPtrOp::create( *this, loc, getPointerTo(srcComplexTy.getElementType()), value); } Address createComplexImagPtr(mlir::Location loc, Address addr) { return Address{createComplexImagPtr(loc, addr.getPointer()), addr.getAlignment()}; } cir::GetRuntimeMemberOp createGetIndirectMember(mlir::Location loc, mlir::Value objectPtr, mlir::Value memberPtr) { auto memberPtrTy = mlir::cast(memberPtr.getType()); // TODO(cir): consider address space. assert(!cir::MissingFeatures::addressSpace()); cir::PointerType resultTy = getPointerTo(memberPtrTy.getMemberTy()); return cir::GetRuntimeMemberOp::create(*this, loc, resultTy, objectPtr, memberPtr); } /// Create a cir.ptr_stride operation to get access to an array element. /// \p idx is the index of the element to access, \p shouldDecay is true if /// the result should decay to a pointer to the element type. mlir::Value getArrayElement(mlir::Location arrayLocBegin, mlir::Location arrayLocEnd, mlir::Value arrayPtr, mlir::Type eltTy, mlir::Value idx, bool shouldDecay); /// Returns a decayed pointer to the first element of the array /// pointed to by \p arrayPtr. mlir::Value maybeBuildArrayDecay(mlir::Location loc, mlir::Value arrayPtr, mlir::Type eltTy); // Convert byte offset to sequence of high-level indices suitable for // GlobalViewAttr. Ideally we shouldn't deal with low-level offsets at all // but currently some parts of Clang AST, which we don't want to touch just // yet, return them. void computeGlobalViewIndicesFromFlatOffset( int64_t offset, mlir::Type ty, cir::CIRDataLayout layout, llvm::SmallVectorImpl &indices); // Convert high-level indices (e.g. from GlobalViewAttr) to byte offset. uint64_t computeOffsetFromGlobalViewIndices(const cir::CIRDataLayout &layout, mlir::Type ty, llvm::ArrayRef indices); /// Creates a versioned global variable. If the symbol is already taken, an ID /// will be appended to the symbol. The returned global must always be queried /// for its name so it can be referenced correctly. [[nodiscard]] cir::GlobalOp createVersionedGlobal(mlir::ModuleOp module, mlir::Location loc, mlir::StringRef name, mlir::Type type, bool isConstant, cir::GlobalLinkageKind linkage, mlir::ptr::MemorySpaceAttrInterface addrSpace = {}) { // Create a unique name if the given name is already taken. std::string uniqueName; if (unsigned version = globalsVersioning[name.str()]++) uniqueName = name.str() + "." + std::to_string(version); else uniqueName = name.str(); return createGlobal(module, loc, uniqueName, type, isConstant, linkage, addrSpace); } cir::StackSaveOp createStackSave(mlir::Location loc, mlir::Type ty) { return cir::StackSaveOp::create(*this, loc, ty); } cir::StackRestoreOp createStackRestore(mlir::Location loc, mlir::Value v) { return cir::StackRestoreOp::create(*this, loc, v); } cir::CmpThreeWayOp createThreeWayCmpTotalOrdering( mlir::Location loc, mlir::Value lhs, mlir::Value rhs, const llvm::APSInt <Res, const llvm::APSInt &eqRes, const llvm::APSInt >Res, cir::CmpOrdering ordering) { assert(ltRes.getBitWidth() == eqRes.getBitWidth() && ltRes.getBitWidth() == gtRes.getBitWidth() && "the three comparison results must have the same bit width"); assert((ordering == cir::CmpOrdering::Strong || ordering == cir::CmpOrdering::Weak) && "total ordering must be strong or weak"); cir::IntType cmpResultTy = getSIntNTy(ltRes.getBitWidth()); auto infoAttr = cir::CmpThreeWayInfoAttr::get( getContext(), ordering, ltRes.getSExtValue(), eqRes.getSExtValue(), gtRes.getSExtValue()); return cir::CmpThreeWayOp::create(*this, loc, cmpResultTy, lhs, rhs, infoAttr); } cir::CmpThreeWayOp createThreeWayCmpPartialOrdering( mlir::Location loc, mlir::Value lhs, mlir::Value rhs, const llvm::APSInt <Res, const llvm::APSInt &eqRes, const llvm::APSInt >Res, const llvm::APSInt &unorderedRes) { assert(ltRes.getBitWidth() == eqRes.getBitWidth() && ltRes.getBitWidth() == gtRes.getBitWidth() && ltRes.getBitWidth() == unorderedRes.getBitWidth() && "the four comparison results must have the same bit width"); cir::IntType cmpResultTy = getSIntNTy(ltRes.getBitWidth()); auto infoAttr = cir::CmpThreeWayInfoAttr::get( getContext(), ltRes.getSExtValue(), eqRes.getSExtValue(), gtRes.getSExtValue(), unorderedRes.getSExtValue()); return cir::CmpThreeWayOp::create(*this, loc, cmpResultTy, lhs, rhs, infoAttr); } mlir::Value createSetBitfield(mlir::Location loc, mlir::Type resultType, Address dstAddr, mlir::Type storageType, mlir::Value src, const CIRGenBitFieldInfo &info, bool isLvalueVolatile, bool useVolatile) { unsigned offset = useVolatile ? info.volatileOffset : info.offset; // If using AAPCS and the field is volatile, load with the size of the // declared field storageType = useVolatile ? cir::IntType::get(storageType.getContext(), info.volatileStorageSize, info.isSigned) : storageType; return cir::SetBitfieldOp::create( *this, loc, resultType, dstAddr.getPointer(), storageType, src, info.name, info.size, offset, info.isSigned, isLvalueVolatile, dstAddr.getAlignment().getAsAlign().value()); } mlir::Value createGetBitfield(mlir::Location loc, mlir::Type resultType, Address addr, mlir::Type storageType, const CIRGenBitFieldInfo &info, bool isLvalueVolatile, bool useVolatile) { unsigned offset = useVolatile ? info.volatileOffset : info.offset; // If using AAPCS and the field is volatile, load with the size of the // declared field storageType = useVolatile ? cir::IntType::get(storageType.getContext(), info.volatileStorageSize, info.isSigned) : storageType; return cir::GetBitfieldOp::create(*this, loc, resultType, addr.getPointer(), storageType, info.name, info.size, offset, info.isSigned, isLvalueVolatile, addr.getAlignment().getAsAlign().value()); } mlir::Value createMaskedLoad(mlir::Location loc, mlir::Type ty, mlir::Value ptr, llvm::Align alignment, mlir::Value mask, mlir::Value passThru) { assert(mlir::isa(ty) && "Type should be vector"); assert(mask && "Mask should not be all-ones (null)"); if (!passThru) passThru = this->getConstant(loc, cir::PoisonAttr::get(ty)); auto alignAttr = this->getI64IntegerAttr(static_cast(alignment.value())); return cir::VecMaskedLoadOp::create(*this, loc, ty, ptr, mask, passThru, alignAttr); } cir::VecShuffleOp createVecShuffle(mlir::Location loc, mlir::Value vec1, mlir::Value vec2, llvm::ArrayRef maskAttrs) { auto vecType = mlir::cast(vec1.getType()); auto resultTy = cir::VectorType::get(vecType.getElementType(), maskAttrs.size()); return cir::VecShuffleOp::create(*this, loc, resultTy, vec1, vec2, getArrayAttr(maskAttrs)); } cir::VecShuffleOp createVecShuffle(mlir::Location loc, mlir::Value vec1, mlir::Value vec2, llvm::ArrayRef mask) { auto maskAttrs = llvm::to_vector_of( llvm::map_range(mask, [&](int32_t idx) { return cir::IntAttr::get(getSInt32Ty(), idx); })); return createVecShuffle(loc, vec1, vec2, maskAttrs); } cir::VecShuffleOp createVecShuffle(mlir::Location loc, mlir::Value vec1, llvm::ArrayRef mask) { /// Create a unary shuffle. The second vector operand of the IR instruction /// is poison. cir::ConstantOp poison = getConstant(loc, cir::PoisonAttr::get(vec1.getType())); return createVecShuffle(loc, vec1, poison, mask); } template mlir::Value emitIntrinsicCallOp(mlir::Location loc, const llvm::StringRef str, const mlir::Type &resTy, Operands &&...op) { return cir::LLVMIntrinsicCallOp::create(*this, loc, this->getStringAttr(str), resTy, std::forward(op)...) .getResult(); } }; } // namespace clang::CIRGen #endif