Author: Amr Hesham Date: 2025-04-03T19:25:25+02:00 New Revision: 262b9b515330daf7c446cc7983bf5f89185b3666
URL: https://github.com/llvm/llvm-project/commit/262b9b515330daf7c446cc7983bf5f89185b3666 DIFF: https://github.com/llvm/llvm-project/commit/262b9b515330daf7c446cc7983bf5f89185b3666.diff LOG: [CIR][Upstream] Local initialization for ArrayType (#132974) This change adds local initialization for ArrayType Issue #130197 Added: clang/include/clang/CIR/LoweringHelpers.h clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp clang/lib/CIR/Lowering/LoweringHelpers.cpp Modified: clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h clang/include/clang/CIR/Dialect/IR/CIRDialect.h clang/include/clang/CIR/Dialect/IR/CIROps.td clang/include/clang/CIR/MissingFeatures.h clang/lib/CIR/CodeGen/Address.h clang/lib/CIR/CodeGen/CIRGenDecl.cpp clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp clang/lib/CIR/CodeGen/CIRGenFunction.cpp clang/lib/CIR/CodeGen/CIRGenFunction.h clang/lib/CIR/CodeGen/CIRGenModule.cpp clang/lib/CIR/CodeGen/CIRGenModule.h clang/lib/CIR/CodeGen/CIRGenTypeCache.h clang/lib/CIR/CodeGen/CIRGenTypes.cpp clang/lib/CIR/CodeGen/CIRGenTypes.h clang/lib/CIR/CodeGen/CIRGenValue.h clang/lib/CIR/CodeGen/CMakeLists.txt clang/lib/CIR/Dialect/IR/CIRDialect.cpp clang/lib/CIR/Lowering/CMakeLists.txt clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h clang/test/CIR/CodeGen/array.cpp clang/test/CIR/Lowering/array.cpp Removed: ################################################################################ diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h index e666be0b25d75..51939e3af833d 100644 --- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h +++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h @@ -67,6 +67,16 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { return create<cir::ConstantOp>(loc, attr.getType(), attr); } + cir::ConstantOp getConstantInt(mlir::Location loc, mlir::Type ty, + int64_t value) { + return getConstant(loc, cir::IntAttr::get(ty, value)); + } + + // Creates constant null value for integral type ty. + cir::ConstantOp getNullValue(mlir::Type ty, mlir::Location loc) { + return getConstant(loc, getZeroInitAttr(ty)); + } + mlir::TypedAttr getConstNullPtrAttr(mlir::Type t) { assert(mlir::isa<cir::PointerType>(t) && "expected cir.ptr"); return getConstPtrAttr(t, 0); @@ -171,6 +181,11 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { return createLoad(loc, addr); } + cir::PtrStrideOp createPtrStride(mlir::Location loc, mlir::Value base, + mlir::Value stride) { + return create<cir::PtrStrideOp>(loc, base.getType(), base, stride); + } + //===--------------------------------------------------------------------===// // Cast/Conversion Operators //===--------------------------------------------------------------------===// diff --git a/clang/include/clang/CIR/Dialect/IR/CIRDialect.h b/clang/include/clang/CIR/Dialect/IR/CIRDialect.h index 4d7f537418a90..986f682a19f4f 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRDialect.h +++ b/clang/include/clang/CIR/Dialect/IR/CIRDialect.h @@ -32,6 +32,31 @@ #include "clang/CIR/Interfaces/CIRLoopOpInterface.h" #include "clang/CIR/Interfaces/CIROpInterfaces.h" +namespace mlir { +namespace OpTrait { + +namespace impl { +// These functions are out-of-line implementations of the methods in the +// corresponding trait classes. This avoids them being template +// instantiated/duplicated. +LogicalResult verifySameFirstOperandAndResultType(Operation *op); +} // namespace impl + +/// This class provides verification for ops that are known to have the same +/// first operand and result type. +/// +template <typename ConcreteType> +class SameFirstOperandAndResultType + : public TraitBase<ConcreteType, SameFirstOperandAndResultType> { +public: + static llvm::LogicalResult verifyTrait(Operation *op) { + return impl::verifySameFirstOperandAndResultType(op); + } +}; + +} // namespace OpTrait +} // namespace mlir + using BuilderCallbackRef = llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)>; diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index c17abfd752a1a..562493888e10c 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -79,6 +79,13 @@ class LLVMLoweringInfo { class CIR_Op<string mnemonic, list<Trait> traits = []> : Op<CIR_Dialect, mnemonic, traits>, LLVMLoweringInfo; +//===----------------------------------------------------------------------===// +// CIR Op Traits +//===----------------------------------------------------------------------===// + +def SameFirstOperandAndResultType : + NativeOpTrait<"SameFirstOperandAndResultType">; + //===----------------------------------------------------------------------===// // CastOp //===----------------------------------------------------------------------===// @@ -229,6 +236,40 @@ def CastOp : CIR_Op<"cast", let hasFolder = 1; } + +//===----------------------------------------------------------------------===// +// PtrStrideOp +//===----------------------------------------------------------------------===// + +def PtrStrideOp : CIR_Op<"ptr_stride", + [Pure, SameFirstOperandAndResultType]> { + let summary = "Pointer access with stride"; + let description = [{ + Given a base pointer as first operand, provides a new pointer after applying + a stride (second operand). + + ```mlir + %3 = cir.const 0 : i32 + %4 = cir.ptr_stride(%2 : !cir.ptr<i32>, %3 : i32), !cir.ptr<i32> + ``` + }]; + + let arguments = (ins CIR_PointerType:$base, PrimitiveInt:$stride); + let results = (outs CIR_PointerType:$result); + + let assemblyFormat = [{ + `(` $base `:` qualified(type($base)) `,` $stride `:` + qualified(type($stride)) `)` `,` qualified(type($result)) attr-dict + }]; + + let extraClassDeclaration = [{ + // Get type pointed by the base pointer. + mlir::Type getElementTy() { + return mlir::cast<cir::PointerType>(getBase().getType()).getPointee(); + } + }]; +} + //===----------------------------------------------------------------------===// // ConstantOp //===----------------------------------------------------------------------===// diff --git a/clang/include/clang/CIR/LoweringHelpers.h b/clang/include/clang/CIR/LoweringHelpers.h new file mode 100644 index 0000000000000..3077010ee5ffe --- /dev/null +++ b/clang/include/clang/CIR/LoweringHelpers.h @@ -0,0 +1,40 @@ +//====- LoweringHelpers.h - Lowering helper functions ---------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file declares helper functions for lowering from CIR to LLVM or MLIR. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_CIR_LOWERINGHELPERS_H +#define LLVM_CLANG_CIR_LOWERINGHELPERS_H + +#include "mlir/IR/BuiltinAttributes.h" +#include "mlir/Transforms/DialectConversion.h" +#include "clang/CIR/Dialect/IR/CIRDialect.h" + +mlir::DenseElementsAttr +convertStringAttrToDenseElementsAttr(cir::ConstArrayAttr attr, mlir::Type type); + +template <typename StorageTy> StorageTy getZeroInitFromType(mlir::Type ty); +template <> mlir::APInt getZeroInitFromType(mlir::Type ty); +template <> mlir::APFloat getZeroInitFromType(mlir::Type ty); + +template <typename AttrTy, typename StorageTy> +void convertToDenseElementsAttrImpl(cir::ConstArrayAttr attr, + llvm::SmallVectorImpl<StorageTy> &values); + +template <typename AttrTy, typename StorageTy> +mlir::DenseElementsAttr +convertToDenseElementsAttr(cir::ConstArrayAttr attr, + const llvm::SmallVectorImpl<int64_t> &dims, + mlir::Type type); + +std::optional<mlir::Attribute> +lowerConstArrayAttr(cir::ConstArrayAttr constArr, + const mlir::TypeConverter *converter); + +#endif diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 23bf826d19a69..21a1d99c7c218 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -118,6 +118,7 @@ struct MissingFeatures { static bool vectorType() { return false; } static bool complexType() { return false; } static bool fixedPointType() { return false; } + static bool stringTypeWithDifferentArraySize() { return false; } // Future CIR operations static bool awaitOp() { return false; } diff --git a/clang/lib/CIR/CodeGen/Address.h b/clang/lib/CIR/CodeGen/Address.h index fba1ffd90877b..2cc8ada783197 100644 --- a/clang/lib/CIR/CodeGen/Address.h +++ b/clang/lib/CIR/CodeGen/Address.h @@ -70,6 +70,14 @@ class Address { return pointerAndKnownNonNull.getPointer(); } + mlir::Type getType() const { + assert(mlir::cast<cir::PointerType>( + pointerAndKnownNonNull.getPointer().getType()) + .getPointee() == elementType); + + return mlir::cast<cir::PointerType>(getPointer().getType()); + } + mlir::Type getElementType() const { assert(isValid()); assert(mlir::cast<cir::PointerType>( @@ -77,6 +85,8 @@ class Address { .getPointee() == elementType); return elementType; } + + clang::CharUnits getAlignment() const { return alignment; } }; } // namespace clang::CIRGen diff --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp index f2153c23ebb43..5b832b463e752 100644 --- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp @@ -251,7 +251,7 @@ void CIRGenFunction::emitExprAsInit(const Expr *init, const ValueDecl *d, return; } case cir::TEK_Aggregate: - cgm.errorNYI(init->getSourceRange(), "emitExprAsInit: aggregate type"); + emitAggExpr(init, AggValueSlot::forLValue(lvalue)); return; } llvm_unreachable("bad evaluation kind"); diff --git a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp new file mode 100644 index 0000000000000..36da63d5f7d76 --- /dev/null +++ b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp @@ -0,0 +1,277 @@ +//===--- CIRGenExprAgg.cpp - Emit CIR Code from Aggregate Expressions -----===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This contains code to emit Aggregate Expr nodes as CIR code. +// +//===----------------------------------------------------------------------===// + +#include "CIRGenBuilder.h" +#include "CIRGenFunction.h" +#include "CIRGenValue.h" +#include "clang/CIR/Dialect/IR/CIRAttrs.h" + +#include "clang/AST/Expr.h" +#include "clang/AST/StmtVisitor.h" +#include <cstdint> + +using namespace clang; +using namespace clang::CIRGen; + +namespace { +class AggExprEmitter : public StmtVisitor<AggExprEmitter> { + + CIRGenFunction &cgf; + AggValueSlot dest; + + AggValueSlot ensureSlot(mlir::Location loc, QualType t) { + if (!dest.isIgnored()) + return dest; + + cgf.cgm.errorNYI(loc, "Slot for ignored address"); + return dest; + } + +public: + AggExprEmitter(CIRGenFunction &cgf, AggValueSlot dest) + : cgf(cgf), dest(dest) {} + + void emitArrayInit(Address destPtr, cir::ArrayType arrayTy, QualType arrayQTy, + Expr *exprToVisit, ArrayRef<Expr *> args, + Expr *arrayFiller); + + void emitInitializationToLValue(Expr *e, LValue lv); + + void emitNullInitializationToLValue(mlir::Location loc, LValue lv); + + void Visit(Expr *e) { StmtVisitor<AggExprEmitter>::Visit(e); } + + void VisitInitListExpr(InitListExpr *e); + + void visitCXXParenListOrInitListExpr(Expr *e, ArrayRef<Expr *> args, + FieldDecl *initializedFieldInUnion, + Expr *arrayFiller); +}; + +} // namespace + +static bool isTrivialFiller(Expr *e) { + if (!e) + return true; + + if (isa<ImplicitValueInitExpr>(e)) + return true; + + if (auto *ile = dyn_cast<InitListExpr>(e)) { + if (ile->getNumInits()) + return false; + return isTrivialFiller(ile->getArrayFiller()); + } + + if (const auto *cons = dyn_cast_or_null<CXXConstructExpr>(e)) + return cons->getConstructor()->isDefaultConstructor() && + cons->getConstructor()->isTrivial(); + + return false; +} + +void AggExprEmitter::emitArrayInit(Address destPtr, cir::ArrayType arrayTy, + QualType arrayQTy, Expr *e, + ArrayRef<Expr *> args, Expr *arrayFiller) { + CIRGenBuilderTy &builder = cgf.getBuilder(); + const mlir::Location loc = cgf.getLoc(e->getSourceRange()); + + const uint64_t numInitElements = args.size(); + + const QualType elementType = + cgf.getContext().getAsArrayType(arrayQTy)->getElementType(); + + if (elementType.isDestructedType()) { + cgf.cgm.errorNYI(loc, "dtorKind NYI"); + return; + } + + const QualType elementPtrType = cgf.getContext().getPointerType(elementType); + + const mlir::Type cirElementType = cgf.convertType(elementType); + const cir::PointerType cirElementPtrType = + builder.getPointerTo(cirElementType); + + auto begin = builder.create<cir::CastOp>(loc, cirElementPtrType, + cir::CastKind::array_to_ptrdecay, + destPtr.getPointer()); + + const CharUnits elementSize = + cgf.getContext().getTypeSizeInChars(elementType); + const CharUnits elementAlign = + destPtr.getAlignment().alignmentOfArrayElement(elementSize); + + // The 'current element to initialize'. The invariants on this + // variable are complicated. Essentially, after each iteration of + // the loop, it points to the last initialized element, except + // that it points to the beginning of the array before any + // elements have been initialized. + mlir::Value element = begin; + + // Don't build the 'one' before the cycle to avoid + // emmiting the redundant `cir.const 1` instrs. + mlir::Value one; + + // Emit the explicit initializers. + for (uint64_t i = 0; i != numInitElements; ++i) { + // Advance to the next element. + if (i > 0) { + one = builder.getConstantInt(loc, cgf.PtrDiffTy, i); + element = builder.createPtrStride(loc, begin, one); + } + + const Address address = Address(element, cirElementType, elementAlign); + const LValue elementLV = LValue::makeAddr(address, elementType); + emitInitializationToLValue(args[i], elementLV); + } + + const uint64_t numArrayElements = arrayTy.getSize(); + + // Check whether there's a non-trivial array-fill expression. + const bool hasTrivialFiller = isTrivialFiller(arrayFiller); + + // Any remaining elements need to be zero-initialized, possibly + // using the filler expression. We can skip this if the we're + // emitting to zeroed memory. + if (numInitElements != numArrayElements && + !(dest.isZeroed() && hasTrivialFiller && + cgf.getTypes().isZeroInitializable(elementType))) { + // Advance to the start of the rest of the array. + if (numInitElements) { + one = builder.getConstantInt(loc, cgf.PtrDiffTy, 1); + element = builder.create<cir::PtrStrideOp>(loc, cirElementPtrType, + element, one); + } + + // Allocate the temporary variable + // to store the pointer to first unitialized element + const Address tmpAddr = cgf.createTempAlloca( + cirElementPtrType, cgf.getPointerAlign(), loc, "arrayinit.temp", + /*insertIntoFnEntryBlock=*/false); + LValue tmpLV = LValue::makeAddr(tmpAddr, elementPtrType); + cgf.emitStoreThroughLValue(RValue::get(element), tmpLV); + + // TODO(CIR): Replace this part later with cir::DoWhileOp + for (unsigned i = numInitElements; i != numArrayElements; ++i) { + cir::LoadOp currentElement = + builder.createLoad(loc, tmpAddr.getPointer()); + + // Emit the actual filler expression. + const LValue elementLV = LValue::makeAddr( + Address(currentElement, cirElementType, elementAlign), elementType); + + if (arrayFiller) + emitInitializationToLValue(arrayFiller, elementLV); + else + emitNullInitializationToLValue(loc, elementLV); + + // Advance pointer and store them to temporary variable + one = builder.getConstantInt(loc, cgf.PtrDiffTy, 1); + cir::PtrStrideOp nextElement = + builder.createPtrStride(loc, currentElement, one); + cgf.emitStoreThroughLValue(RValue::get(nextElement), tmpLV); + } + } +} + +void AggExprEmitter::emitInitializationToLValue(Expr *e, LValue lv) { + const QualType type = lv.getType(); + + if (isa<ImplicitValueInitExpr, CXXScalarValueInitExpr>(e)) { + const mlir::Location loc = e->getSourceRange().isValid() + ? cgf.getLoc(e->getSourceRange()) + : *cgf.currSrcLoc; + return emitNullInitializationToLValue(loc, lv); + } + + if (isa<NoInitExpr>(e)) + return; + + if (type->isReferenceType()) + cgf.cgm.errorNYI("emitInitializationToLValue ReferenceType"); + + switch (cgf.getEvaluationKind(type)) { + case cir::TEK_Complex: + cgf.cgm.errorNYI("emitInitializationToLValue TEK_Complex"); + break; + case cir::TEK_Aggregate: + cgf.emitAggExpr(e, AggValueSlot::forLValue(lv)); + return; + case cir::TEK_Scalar: + if (lv.isSimple()) + cgf.emitScalarInit(e, cgf.getLoc(e->getSourceRange()), lv); + else + cgf.emitStoreThroughLValue(RValue::get(cgf.emitScalarExpr(e)), lv); + return; + } +} + +void AggExprEmitter::emitNullInitializationToLValue(mlir::Location loc, + LValue lv) { + const QualType type = lv.getType(); + + // If the destination slot is already zeroed out before the aggregate is + // copied into it, we don't have to emit any zeros here. + if (dest.isZeroed() && cgf.getTypes().isZeroInitializable(type)) + return; + + if (cgf.hasScalarEvaluationKind(type)) { + // For non-aggregates, we can store the appropriate null constant. + mlir::Value null = cgf.cgm.emitNullConstant(type, loc); + if (lv.isSimple()) { + cgf.emitStoreOfScalar(null, lv, /* isInitialization */ true); + return; + } + + cgf.cgm.errorNYI("emitStoreThroughBitfieldLValue"); + return; + } + + // There's a potential optimization opportunity in combining + // memsets; that would be easy for arrays, but relatively + // diff icult for structures with the current code. + cgf.emitNullInitialization(loc, lv.getAddress(), lv.getType()); +} + +void AggExprEmitter::VisitInitListExpr(InitListExpr *e) { + if (e->hadArrayRangeDesignator()) + llvm_unreachable("GNU array range designator extension"); + + if (e->isTransparent()) + return Visit(e->getInit(0)); + + visitCXXParenListOrInitListExpr( + e, e->inits(), e->getInitializedFieldInUnion(), e->getArrayFiller()); +} + +void AggExprEmitter::visitCXXParenListOrInitListExpr( + Expr *e, ArrayRef<Expr *> args, FieldDecl *initializedFieldInUnion, + Expr *arrayFiller) { + + const AggValueSlot dest = + ensureSlot(cgf.getLoc(e->getSourceRange()), e->getType()); + + if (e->getType()->isConstantArrayType()) { + cir::ArrayType arrayTy = + cast<cir::ArrayType>(dest.getAddress().getElementType()); + emitArrayInit(dest.getAddress(), arrayTy, e->getType(), e, args, + arrayFiller); + return; + } + + cgf.cgm.errorNYI( + "visitCXXParenListOrInitListExpr Record or VariableSizeArray type"); +} + +void CIRGenFunction::emitAggExpr(const Expr *e, AggValueSlot slot) { + AggExprEmitter(*this, slot).Visit(const_cast<Expr *>(e)); +} diff --git a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp index fc49d6da97206..50fa029851f33 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp @@ -412,3 +412,25 @@ mlir::Attribute ConstantEmitter::tryEmitPrivate(const APValue &value, } llvm_unreachable("Unknown APValue kind"); } + +mlir::Value CIRGenModule::emitNullConstant(QualType t, mlir::Location loc) { + if (t->getAs<PointerType>()) { + return builder.getNullPtr(getTypes().convertTypeForMem(t), loc); + } + + if (getTypes().isZeroInitializable(t)) + return builder.getNullValue(getTypes().convertTypeForMem(t), loc); + + if (getASTContext().getAsConstantArrayType(t)) { + errorNYI("CIRGenModule::emitNullConstant ConstantArrayType"); + } + + if (t->getAs<RecordType>()) + errorNYI("CIRGenModule::emitNullConstant RecordType"); + + assert(t->isMemberDataPointerType() && + "Should only see pointers to data members here!"); + + errorNYI("CIRGenModule::emitNullConstant unsupported type"); + return {}; +} diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index 47fc90836fca6..2465ccffd19d6 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -453,4 +453,47 @@ LValue CIRGenFunction::emitLValue(const Expr *e) { } } +void CIRGenFunction::emitNullInitialization(mlir::Location loc, Address destPtr, + QualType ty) { + // Ignore empty classes in C++. + if (getLangOpts().CPlusPlus) { + if (const RecordType *rt = ty->getAs<RecordType>()) { + if (cast<CXXRecordDecl>(rt->getDecl())->isEmpty()) + return; + } + } + + // Cast the dest ptr to the appropriate i8 pointer type. + if (builder.isInt8Ty(destPtr.getElementType())) { + cgm.errorNYI(loc, "Cast the dest ptr to the appropriate i8 pointer type"); + } + + // Get size and alignment info for this aggregate. + const CharUnits size = getContext().getTypeSizeInChars(ty); + if (size.isZero()) { + // But note that getTypeInfo returns 0 for a VLA. + if (isa<VariableArrayType>(getContext().getAsArrayType(ty))) { + cgm.errorNYI(loc, + "emitNullInitialization for zero size VariableArrayType"); + } else { + return; + } + } + + // If the type contains a pointer to data member we can't memset it to zero. + // Instead, create a null constant and copy it to the destination. + // TODO: there are other patterns besides zero that we can usefully memset, + // like -1, which happens to be the pattern used by member-pointers. + if (!cgm.getTypes().isZeroInitializable(ty)) { + cgm.errorNYI(loc, "type is not zero initializable"); + } + + // In LLVM Codegen: otherwise, just memset the whole thing to zero using + // Builder.CreateMemSet. In CIR just emit a store of #cir.zero to the + // respective address. + // Builder.CreateMemSet(DestPtr, Builder.getInt8(0), SizeVal, false); + const mlir::Value zeroValue = builder.getNullValue(convertType(ty), loc); + builder.createStore(loc, zeroValue, destPtr.getPointer()); +} + } // namespace clang::CIRGen diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 5cae4d5da9516..4889c3ce4ca9c 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -110,6 +110,8 @@ class CIRGenFunction : public CIRGenTypeCache { public: mlir::Value createDummyValue(mlir::Location loc, clang::QualType qt); + void emitNullInitialization(mlir::Location loc, Address destPtr, QualType ty); + private: // Track current variable initialization (if there's one) const clang::VarDecl *currVarDecl = nullptr; @@ -377,6 +379,8 @@ class CIRGenFunction : public CIRGenTypeCache { mlir::OpBuilder::InsertPoint ip, mlir::Value arraySize = nullptr); + void emitAggExpr(const clang::Expr *e, AggValueSlot slot); + /// Emit code to compute the specified expression which can have any type. The /// result is returned as an RValue struct. If this is an aggregate /// expression, the aggloc/agglocvolatile arguments indicate where the result diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index 2a37d6c7d1888..d3b3b0632c2f0 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -57,6 +57,18 @@ CIRGenModule::CIRGenModule(mlir::MLIRContext &mlirContext, FP80Ty = cir::FP80Type::get(&getMLIRContext()); FP128Ty = cir::FP128Type::get(&getMLIRContext()); + PointerAlignInBytes = + astContext + .toCharUnitsFromBits( + astContext.getTargetInfo().getPointerAlign(LangAS::Default)) + .getQuantity(); + + // TODO(CIR): Should be updated once TypeSizeInfoAttr is upstreamed + const unsigned sizeTypeSize = + astContext.getTypeSize(astContext.getSignedSizeType()); + PtrDiffTy = + cir::IntType::get(&getMLIRContext(), sizeTypeSize, /*isSigned=*/true); + theModule->setAttr(cir::CIRDialect::getTripleAttrName(), builder.getStringAttr(getTriple().str())); } diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index 734cafa2e07bb..6ba1ccc4ddd9f 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -113,6 +113,10 @@ class CIRGenModule : public CIRGenTypeCache { void emitGlobalVarDefinition(const clang::VarDecl *vd, bool isTentative = false); + /// Return the result of value-initializing the given type, i.e. a null + /// expression of the given type. + mlir::Value emitNullConstant(QualType t, mlir::Location loc); + cir::FuncOp getOrCreateCIRFunction(llvm::StringRef mangledName, mlir::Type funcType, clang::GlobalDecl gd, bool forVTable, diff --git a/clang/lib/CIR/CodeGen/CIRGenTypeCache.h b/clang/lib/CIR/CodeGen/CIRGenTypeCache.h index 99c0123c64b28..a5b7f0c9579b4 100644 --- a/clang/lib/CIR/CodeGen/CIRGenTypeCache.h +++ b/clang/lib/CIR/CodeGen/CIRGenTypeCache.h @@ -13,6 +13,7 @@ #ifndef LLVM_CLANG_LIB_CIR_CIRGENTYPECACHE_H #define LLVM_CLANG_LIB_CIR_CIRGENTYPECACHE_H +#include "clang/AST/CharUnits.h" #include "clang/CIR/Dialect/IR/CIRTypes.h" namespace clang::CIRGen { @@ -47,6 +48,18 @@ struct CIRGenTypeCache { cir::DoubleType DoubleTy; cir::FP80Type FP80Ty; cir::FP128Type FP128Ty; + + mlir::Type PtrDiffTy; + + /// The size and alignment of a pointer into the generic address space. + union { + unsigned char PointerAlignInBytes; + unsigned char PointerSizeInBytes; + }; + + clang::CharUnits getPointerAlign() const { + return clang::CharUnits::fromQuantity(PointerAlignInBytes); + } }; } // namespace clang::CIRGen diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp index aaf3fe240f3c3..1e47ccc451b86 100644 --- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp @@ -254,3 +254,30 @@ mlir::Type CIRGenTypes::convertTypeForMem(clang::QualType qualType, return convertedType; } + +bool CIRGenTypes::isZeroInitializable(clang::QualType t) { + if (t->getAs<PointerType>()) + return astContext.getTargetNullPointerValue(t) == 0; + + if (const auto *at = astContext.getAsArrayType(t)) { + if (isa<IncompleteArrayType>(at)) + return true; + + if (const auto *cat = dyn_cast<ConstantArrayType>(at)) + if (astContext.getConstantArrayElementCount(cat) == 0) + return true; + } + + if (t->getAs<RecordType>()) { + cgm.errorNYI(SourceLocation(), "isZeroInitializable for RecordType", t); + return false; + } + + if (t->getAs<MemberPointerType>()) { + cgm.errorNYI(SourceLocation(), "isZeroInitializable for MemberPointerType", + t); + return false; + } + + return true; +} diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.h b/clang/lib/CIR/CodeGen/CIRGenTypes.h index f280e17ebddc6..73948f5c63e6a 100644 --- a/clang/lib/CIR/CodeGen/CIRGenTypes.h +++ b/clang/lib/CIR/CodeGen/CIRGenTypes.h @@ -71,6 +71,10 @@ class CIRGenTypes { /// representation is usually i8 or i32, depending on the target. // TODO: convert this comment to account for MLIR's equivalence mlir::Type convertTypeForMem(clang::QualType, bool forBitField = false); + + /// Return whether a type can be zero-initialized (in the C++ sense) with an + /// LLVM zeroinitializer. + bool isZeroInitializable(clang::QualType t); }; } // namespace clang::CIRGen diff --git a/clang/lib/CIR/CodeGen/CIRGenValue.h b/clang/lib/CIR/CodeGen/CIRGenValue.h index c559e853aad39..d22d518ef4904 100644 --- a/clang/lib/CIR/CodeGen/CIRGenValue.h +++ b/clang/lib/CIR/CodeGen/CIRGenValue.h @@ -85,11 +85,15 @@ class LValue { MatrixElt // This is a matrix element, use getVector* } lvType; clang::QualType type; + clang::Qualifiers quals; mlir::Value v; mlir::Type elementType; - void initialize(clang::QualType type) { this->type = type; } + void initialize(clang::QualType type, clang::Qualifiers quals) { + this->type = type; + this->quals = quals; + } public: bool isSimple() const { return lvType == Simple; } @@ -111,16 +115,52 @@ class LValue { return Address(getPointer(), elementType, getAlignment()); } + const clang::Qualifiers &getQuals() const { return quals; } + static LValue makeAddr(Address address, clang::QualType t) { LValue r; r.lvType = Simple; r.v = address.getPointer(); r.elementType = address.getElementType(); - r.initialize(t); + r.initialize(t, t.getQualifiers()); return r; } }; +/// An aggregate value slot. +class AggValueSlot { + + Address addr; + clang::Qualifiers quals; + + /// This is set to true if the memory in the slot is known to be zero before + /// the assignment into it. This means that zero fields don't need to be set. + bool zeroedFlag : 1; + +public: + enum IsZeroed_t { IsNotZeroed, IsZeroed }; + + AggValueSlot(Address addr, clang::Qualifiers quals, bool zeroedFlag) + : addr(addr), quals(quals), zeroedFlag(zeroedFlag) {} + + static AggValueSlot forAddr(Address addr, clang::Qualifiers quals, + IsZeroed_t isZeroed = IsNotZeroed) { + return AggValueSlot(addr, quals, isZeroed); + } + + static AggValueSlot forLValue(const LValue &lv) { + return forAddr(lv.getAddress(), lv.getQuals()); + } + + clang::Qualifiers getQualifiers() const { return quals; } + + Address getAddress() const { return addr; } + + bool isIgnored() const { return !addr.isValid(); } + + IsZeroed_t isZeroed() const { return IsZeroed_t(zeroedFlag); } +}; + } // namespace clang::CIRGen #endif // CLANG_LIB_CIR_CIRGENVALUE_H diff --git a/clang/lib/CIR/CodeGen/CMakeLists.txt b/clang/lib/CIR/CodeGen/CMakeLists.txt index 8ee65c2763e70..da8d63ca569af 100644 --- a/clang/lib/CIR/CodeGen/CMakeLists.txt +++ b/clang/lib/CIR/CodeGen/CMakeLists.txt @@ -10,6 +10,7 @@ add_clang_library(clangCIR CIRGenerator.cpp CIRGenDecl.cpp CIRGenExpr.cpp + CIRGenExprAggregate.cpp CIRGenExprConstant.cpp CIRGenExprScalar.cpp CIRGenFunction.cpp diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 4ace083e3c081..143ed5544375f 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -246,8 +246,8 @@ OpFoldResult cir::ConstantOp::fold(FoldAdaptor /*adaptor*/) { //===----------------------------------------------------------------------===// LogicalResult cir::CastOp::verify() { - auto resType = getResult().getType(); - auto srcType = getSrc().getType(); + const mlir::Type resType = getResult().getType(); + const mlir::Type srcType = getSrc().getType(); switch (getKind()) { case cir::CastKind::int_to_bool: { @@ -271,6 +271,15 @@ LogicalResult cir::CastOp::verify() { return emitOpError() << "requires !cir.int type for source"; return success(); } + case cir::CastKind::array_to_ptrdecay: { + const auto arrayPtrTy = mlir::dyn_cast<cir::PointerType>(srcType); + const auto flatPtrTy = mlir::dyn_cast<cir::PointerType>(resType); + if (!arrayPtrTy || !flatPtrTy) + return emitOpError() << "requires !cir.ptr type for source and result"; + + // TODO(CIR): Make sure the AddrSpace of both types are equals + return success(); + } case cir::CastKind::bitcast: { // Handle the pointer types first. auto srcPtrTy = mlir::dyn_cast<cir::PointerType>(srcType); @@ -453,9 +462,9 @@ mlir::LogicalResult cir::ReturnOp::verify() { /// Given the region at `index`, or the parent operation if `index` is None, /// return the successor regions. These are the regions that may be selected -/// during the flow of control. `operands` is a set of optional attributes that -/// correspond to a constant value for each operand, or null if that operand is -/// not a constant. +/// during the flow of control. `operands` is a set of optional attributes +/// that correspond to a constant value for each operand, or null if that +/// operand is not a constant. void cir::ScopeOp::getSuccessorRegions( mlir::RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> ®ions) { // The only region always branch back to the parent operation. @@ -683,8 +692,8 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) { } bool cir::FuncOp::isDeclaration() { - // TODO(CIR): This function will actually do something once external function - // declarations and aliases are upstreamed. + // TODO(CIR): This function will actually do something once external + // function declarations and aliases are upstreamed. return false; } @@ -710,6 +719,25 @@ void cir::FuncOp::print(OpAsmPrinter &p) { } } +//===----------------------------------------------------------------------===// +// CIR defined traits +//===----------------------------------------------------------------------===// + +LogicalResult +mlir::OpTrait::impl::verifySameFirstOperandAndResultType(Operation *op) { + if (failed(verifyAtLeastNOperands(op, 1)) || failed(verifyOneResult(op))) + return failure(); + + const Type type = op->getResult(0).getType(); + const Type opType = op->getOperand(0).getType(); + + if (type != opType) + return op->emitOpError() + << "requires the same type for first operand and result"; + + return success(); +} + // TODO(CIR): The properties of functions that require verification haven't // been implemented yet. mlir::LogicalResult cir::FuncOp::verify() { return success(); } diff --git a/clang/lib/CIR/Lowering/CMakeLists.txt b/clang/lib/CIR/Lowering/CMakeLists.txt index 09e48862df63c..28ec3c551018c 100644 --- a/clang/lib/CIR/Lowering/CMakeLists.txt +++ b/clang/lib/CIR/Lowering/CMakeLists.txt @@ -7,6 +7,7 @@ get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) add_clang_library(clangCIRLoweringCommon CIRPasses.cpp + LoweringHelpers.cpp LINK_LIBS clangCIR diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 48dc09d151dcf..1c455039269b9 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -13,6 +13,7 @@ #include "LowerToLLVM.h" #include <deque> +#include <optional> #include "mlir/Conversion/LLVMCommon/TypeConverter.h" #include "mlir/Dialect/DLTI/DLTI.h" @@ -28,6 +29,7 @@ #include "mlir/Transforms/DialectConversion.h" #include "clang/CIR/Dialect/IR/CIRDialect.h" #include "clang/CIR/Dialect/Passes.h" +#include "clang/CIR/LoweringHelpers.h" #include "clang/CIR/MissingFeatures.h" #include "clang/CIR/Passes.h" #include "llvm/ADT/TypeSwitch.h" @@ -523,6 +525,66 @@ mlir::LogicalResult CIRToLLVMCastOpLowering::matchAndRewrite( return mlir::success(); } +mlir::LogicalResult CIRToLLVMPtrStrideOpLowering::matchAndRewrite( + cir::PtrStrideOp ptrStrideOp, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const { + + const mlir::TypeConverter *tc = getTypeConverter(); + const mlir::Type resultTy = tc->convertType(ptrStrideOp.getType()); + + mlir::Type elementTy = + convertTypeForMemory(*tc, dataLayout, ptrStrideOp.getElementTy()); + mlir::MLIRContext *ctx = elementTy.getContext(); + + // void and function types doesn't really have a layout to use in GEPs, + // make it i8 instead. + if (mlir::isa<mlir::LLVM::LLVMVoidType>(elementTy) || + mlir::isa<mlir::LLVM::LLVMFunctionType>(elementTy)) + elementTy = mlir::IntegerType::get(elementTy.getContext(), 8, + mlir::IntegerType::Signless); + // Zero-extend, sign-extend or trunc the pointer value. + mlir::Value index = adaptor.getStride(); + const unsigned width = + mlir::cast<mlir::IntegerType>(index.getType()).getWidth(); + const std::optional<std::uint64_t> layoutWidth = + dataLayout.getTypeIndexBitwidth(adaptor.getBase().getType()); + + mlir::Operation *indexOp = index.getDefiningOp(); + if (indexOp && layoutWidth && width != *layoutWidth) { + // If the index comes from a subtraction, make sure the extension happens + // before it. To achieve that, look at unary minus, which already got + // lowered to "sub 0, x". + const auto sub = dyn_cast<mlir::LLVM::SubOp>(indexOp); + auto unary = dyn_cast_if_present<cir::UnaryOp>( + ptrStrideOp.getStride().getDefiningOp()); + bool rewriteSub = + unary && unary.getKind() == cir::UnaryOpKind::Minus && sub; + if (rewriteSub) + index = indexOp->getOperand(1); + + // Handle the cast + const auto llvmDstType = mlir::IntegerType::get(ctx, *layoutWidth); + index = getLLVMIntCast(rewriter, index, llvmDstType, + ptrStrideOp.getStride().getType().isUnsigned(), + width, *layoutWidth); + + // Rewrite the sub in front of extensions/trunc + if (rewriteSub) { + index = rewriter.create<mlir::LLVM::SubOp>( + index.getLoc(), index.getType(), + rewriter.create<mlir::LLVM::ConstantOp>( + index.getLoc(), index.getType(), + mlir::IntegerAttr::get(index.getType(), 0)), + index); + rewriter.eraseOp(sub); + } + } + + rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>( + ptrStrideOp, resultTy, elementTy, adaptor.getBase(), index); + return mlir::success(); +} + mlir::LogicalResult CIRToLLVMAllocaOpLowering::matchAndRewrite( cir::AllocaOp op, OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) const { @@ -603,6 +665,15 @@ mlir::LogicalResult CIRToLLVMStoreOpLowering::matchAndRewrite( return mlir::LogicalResult::success(); } +bool hasTrailingZeros(cir::ConstArrayAttr attr) { + auto array = mlir::dyn_cast<mlir::ArrayAttr>(attr.getElts()); + return attr.hasTrailingZeros() || + (array && std::count_if(array.begin(), array.end(), [](auto elt) { + auto ar = dyn_cast<cir::ConstArrayAttr>(elt); + return ar && hasTrailingZeros(ar); + })); +} + mlir::LogicalResult CIRToLLVMConstantOpLowering::matchAndRewrite( cir::ConstantOp op, OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) const { @@ -641,6 +712,27 @@ mlir::LogicalResult CIRToLLVMConstantOpLowering::matchAndRewrite( } assert(!cir::MissingFeatures::opGlobalViewAttr()); attr = op.getValue(); + } else if (const auto arrTy = mlir::dyn_cast<cir::ArrayType>(op.getType())) { + const auto constArr = mlir::dyn_cast<cir::ConstArrayAttr>(op.getValue()); + if (!constArr && !isa<cir::ZeroAttr, cir::UndefAttr>(op.getValue())) + return op.emitError() << "array does not have a constant initializer"; + + std::optional<mlir::Attribute> denseAttr; + if (constArr && hasTrailingZeros(constArr)) { + const mlir::Value newOp = + lowerCirAttrAsValue(op, constArr, rewriter, getTypeConverter()); + rewriter.replaceOp(op, newOp); + return mlir::success(); + } else if (constArr && + (denseAttr = lowerConstArrayAttr(constArr, typeConverter))) { + attr = denseAttr.value(); + } else { + const mlir::Value initVal = + lowerCirAttrAsValue(op, op.getValue(), rewriter, typeConverter); + rewriter.replaceAllUsesWith(op, initVal); + rewriter.eraseOp(op); + return mlir::success(); + } } else { return op.emitError() << "unsupported constant type " << op.getType(); } @@ -1230,6 +1322,8 @@ void ConvertCIRToLLVMPass::runOnOperation() { patterns.add<CIRToLLVMStoreOpLowering>(converter, patterns.getContext(), dl); patterns.add<CIRToLLVMGlobalOpLowering>(converter, patterns.getContext(), dl); patterns.add<CIRToLLVMCastOpLowering>(converter, patterns.getContext(), dl); + patterns.add<CIRToLLVMPtrStrideOpLowering>(converter, patterns.getContext(), + dl); patterns.add< // clang-format off CIRToLLVMBinOpLowering, diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h index b2926e75d1303..6f489fb49f44f 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h @@ -204,6 +204,21 @@ class CIRToLLVMTrapOpLowering : public mlir::OpConversionPattern<cir::TrapOp> { mlir::ConversionPatternRewriter &) const override; }; +class CIRToLLVMPtrStrideOpLowering + : public mlir::OpConversionPattern<cir::PtrStrideOp> { + mlir::DataLayout const &dataLayout; + +public: + CIRToLLVMPtrStrideOpLowering(const mlir::TypeConverter &typeConverter, + mlir::MLIRContext *context, + mlir::DataLayout const &dataLayout) + : OpConversionPattern(typeConverter, context), dataLayout(dataLayout) {} + using mlir::OpConversionPattern<cir::PtrStrideOp>::OpConversionPattern; + + mlir::LogicalResult + matchAndRewrite(cir::PtrStrideOp op, OpAdaptor, + mlir::ConversionPatternRewriter &) const override; +}; } // namespace direct } // namespace cir diff --git a/clang/lib/CIR/Lowering/LoweringHelpers.cpp b/clang/lib/CIR/Lowering/LoweringHelpers.cpp new file mode 100644 index 0000000000000..0320bc40509b0 --- /dev/null +++ b/clang/lib/CIR/Lowering/LoweringHelpers.cpp @@ -0,0 +1,146 @@ +//====- LoweringHelpers.cpp - Lowering helper functions -------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file contains helper functions for lowering from CIR to LLVM or MLIR. +// +//===----------------------------------------------------------------------===// + +#include "clang/CIR/LoweringHelpers.h" +#include "clang/CIR/MissingFeatures.h" + +mlir::DenseElementsAttr +convertStringAttrToDenseElementsAttr(cir::ConstArrayAttr attr, + mlir::Type type) { + auto values = llvm::SmallVector<mlir::APInt, 8>{}; + const auto stringAttr = mlir::cast<mlir::StringAttr>(attr.getElts()); + + for (const char element : stringAttr) + values.push_back({8, (uint64_t)element}); + + const auto arrayTy = mlir::cast<cir::ArrayType>(attr.getType()); + if (arrayTy.getSize() != stringAttr.size()) + assert(!cir::MissingFeatures::stringTypeWithDifferentArraySize()); + + return mlir::DenseElementsAttr::get( + mlir::RankedTensorType::get({(int64_t)values.size()}, type), + llvm::ArrayRef(values)); +} + +template <> mlir::APInt getZeroInitFromType(mlir::Type ty) { + assert(mlir::isa<cir::IntType>(ty) && "expected int type"); + const auto intTy = mlir::cast<cir::IntType>(ty); + return mlir::APInt::getZero(intTy.getWidth()); +} + +template <> mlir::APFloat getZeroInitFromType(mlir::Type ty) { + assert((mlir::isa<cir::SingleType, cir::DoubleType>(ty)) && + "only float and double supported"); + + if (ty.isF32() || mlir::isa<cir::SingleType>(ty)) + return mlir::APFloat(0.f); + + if (ty.isF64() || mlir::isa<cir::DoubleType>(ty)) + return mlir::APFloat(0.0); + + llvm_unreachable("NYI"); +} + +/// \param attr the ConstArrayAttr to convert +/// \param values the output parameter, the values array to fill +/// \param currentDims the shpae of tensor we're going to convert to +/// \param dimIndex the current dimension we're processing +/// \param currentIndex the current index in the values array +template <typename AttrTy, typename StorageTy> +void convertToDenseElementsAttrImpl( + cir::ConstArrayAttr attr, llvm::SmallVectorImpl<StorageTy> &values, + const llvm::SmallVectorImpl<int64_t> ¤tDims, int64_t dimIndex, + int64_t currentIndex) { + if (auto stringAttr = mlir::dyn_cast<mlir::StringAttr>(attr.getElts())) { + if (auto arrayType = mlir::dyn_cast<cir::ArrayType>(attr.getType())) { + for (auto element : stringAttr) { + auto intAttr = cir::IntAttr::get(arrayType.getEltType(), element); + values[currentIndex++] = mlir::dyn_cast<AttrTy>(intAttr).getValue(); + } + return; + } + } + + dimIndex++; + std::size_t elementsSizeInCurrentDim = 1; + for (std::size_t i = dimIndex; i < currentDims.size(); i++) + elementsSizeInCurrentDim *= currentDims[i]; + + auto arrayAttr = mlir::cast<mlir::ArrayAttr>(attr.getElts()); + for (auto eltAttr : arrayAttr) { + if (auto valueAttr = mlir::dyn_cast<AttrTy>(eltAttr)) { + values[currentIndex++] = valueAttr.getValue(); + continue; + } + + if (auto subArrayAttr = mlir::dyn_cast<cir::ConstArrayAttr>(eltAttr)) { + convertToDenseElementsAttrImpl<AttrTy>(subArrayAttr, values, currentDims, + dimIndex, currentIndex); + currentIndex += elementsSizeInCurrentDim; + continue; + } + + if (mlir::isa<cir::ZeroAttr, cir::UndefAttr>(eltAttr)) { + currentIndex += elementsSizeInCurrentDim; + continue; + } + + llvm_unreachable("unknown element in ConstArrayAttr"); + } +} + +template <typename AttrTy, typename StorageTy> +mlir::DenseElementsAttr convertToDenseElementsAttr( + cir::ConstArrayAttr attr, const llvm::SmallVectorImpl<int64_t> &dims, + mlir::Type elementType, mlir::Type convertedElementType) { + unsigned vectorSize = 1; + for (auto dim : dims) + vectorSize *= dim; + auto values = llvm::SmallVector<StorageTy, 8>( + vectorSize, getZeroInitFromType<StorageTy>(elementType)); + convertToDenseElementsAttrImpl<AttrTy>(attr, values, dims, /*currentDim=*/0, + /*initialIndex=*/0); + return mlir::DenseElementsAttr::get( + mlir::RankedTensorType::get(dims, convertedElementType), + llvm::ArrayRef(values)); +} + +std::optional<mlir::Attribute> +lowerConstArrayAttr(cir::ConstArrayAttr constArr, + const mlir::TypeConverter *converter) { + // Ensure ConstArrayAttr has a type. + const auto typedConstArr = mlir::cast<mlir::TypedAttr>(constArr); + + // Ensure ConstArrayAttr type is a ArrayType. + const auto cirArrayType = mlir::cast<cir::ArrayType>(typedConstArr.getType()); + + // Is a ConstArrayAttr with an cir::ArrayType: fetch element type. + mlir::Type type = cirArrayType; + auto dims = llvm::SmallVector<int64_t, 2>{}; + while (auto arrayType = mlir::dyn_cast<cir::ArrayType>(type)) { + dims.push_back(arrayType.getSize()); + type = arrayType.getEltType(); + } + + if (mlir::isa<mlir::StringAttr>(constArr.getElts())) + return convertStringAttrToDenseElementsAttr(constArr, + converter->convertType(type)); + if (mlir::isa<cir::IntType>(type)) + return convertToDenseElementsAttr<cir::IntAttr, mlir::APInt>( + constArr, dims, type, converter->convertType(type)); + + if (mlir::isa<cir::CIRFPTypeInterface>(type)) + return convertToDenseElementsAttr<cir::FPAttr, mlir::APFloat>( + constArr, dims, type, converter->convertType(type)); + + return std::nullopt; +} diff --git a/clang/test/CIR/CodeGen/array.cpp b/clang/test/CIR/CodeGen/array.cpp index 1e74275eab058..0d28ebc66f83c 100644 --- a/clang/test/CIR/CodeGen/array.cpp +++ b/clang/test/CIR/CodeGen/array.cpp @@ -28,14 +28,114 @@ int f[5] = {1, 2}; // CHECK: cir.global external @f = #cir.const_array<[#cir.int<1> : !s32i, #cir.int<2> : !s32i, #cir.int<0> : !s32i, #cir.int<0> : !s32i, #cir.int<0> : !s32i]> : !cir.array<!s32i x 5> void func() { - int l[10]; - // CHECK: %[[ARR:.*]] = cir.alloca !cir.array<!s32i x 10>, !cir.ptr<!cir.array<!s32i x 10>>, ["l"] + int arr[10]; + + // CHECK: %[[ARR:.*]] = cir.alloca !cir.array<!s32i x 10>, !cir.ptr<!cir.array<!s32i x 10>>, ["arr"] +} + +void func2() { + int arr[2] = {5}; + + // CHECK: %[[ARR2:.*]] = cir.alloca !cir.array<!s32i x 2>, !cir.ptr<!cir.array<!s32i x 2>>, ["arr", init] + // CHECK: %[[ELE_ALLOCA:.*]] = cir.alloca !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>>, ["arrayinit.temp", init] + // CHECK: %[[ARR_2_PTR:.*]] = cir.cast(array_to_ptrdecay, %[[ARR2]] : !cir.ptr<!cir.array<!s32i x 2>>), !cir.ptr<!s32i> + // CHECK: %[[V1:.*]] = cir.const #cir.int<5> : !s32i + // CHECK: cir.store %[[V1]], %[[ARR_2_PTR]] : !s32i, !cir.ptr<!s32i> + // CHECK: %[[OFFSET_0:.*]] = cir.const #cir.int<1> : !s64i + // CHECK: %[[ELE_PTR:.*]] = cir.ptr_stride(%[[ARR_2_PTR]] : !cir.ptr<!s32i>, %[[OFFSET_0]] : !s64i), !cir.ptr<!s32i> + // CHECK: cir.store %[[ELE_PTR]], %[[ELE_ALLOCA]] : !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>> + // CHECK: %[[LOAD_1:.*]] = cir.load %[[ELE_ALLOCA]] : !cir.ptr<!cir.ptr<!s32i>>, !cir.ptr<!s32i> + // CHECK: %[[V2:.*]] = cir.const #cir.int<0> : !s32i + // CHECK: cir.store %[[V2]], %[[LOAD_1]] : !s32i, !cir.ptr<!s32i> + // CHECK: %[[OFFSET_1:.*]] = cir.const #cir.int<1> : !s64i + // CHECK: %[[ELE_1_PTR:.*]] = cir.ptr_stride(%[[LOAD_1]] : !cir.ptr<!s32i>, %[[OFFSET_1]] : !s64i), !cir.ptr<!s32i> + // CHECK: cir.store %[[ELE_1_PTR]], %[[ELE_ALLOCA]] : !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>> +} + +void func3() { + int arr[2] = {5, 6}; + + // CHECK: %[[ARR3:.*]] = cir.alloca !cir.array<!s32i x 2>, !cir.ptr<!cir.array<!s32i x 2>>, ["arr", init] + // CHECK: %[[ARR_3_PTR:.*]] = cir.cast(array_to_ptrdecay, %[[ARR3]] : !cir.ptr<!cir.array<!s32i x 2>>), !cir.ptr<!s32i> + // CHECK: %[[V0:.*]] = cir.const #cir.int<5> : !s32i + // CHECK: cir.store %[[V0]], %[[ARR_3_PTR]] : !s32i, !cir.ptr<!s32i> + // CHECK: %[[OFFSET_0:.*]] = cir.const #cir.int<1> : !s64i + // CHECK: %[[ELE_1_PTR:.*]] = cir.ptr_stride(%[[ARR_3_PTR]] : !cir.ptr<!s32i>, %[[OFFSET_0]] : !s64i), !cir.ptr<!s32i> + // CHECK: %[[V1:.*]] = cir.const #cir.int<6> : !s32i + // CHECK: cir.store %[[V1]], %[[ELE_1_PTR]] : !s32i, !cir.ptr<!s32i> +} + +void func4() { + int arr[2][1] = {{5}, {6}}; + + // CHECK: %[[ARR:.*]] = cir.alloca !cir.array<!cir.array<!s32i x 1> x 2>, !cir.ptr<!cir.array<!cir.array<!s32i x 1> x 2>>, ["arr", init] + // CHECK: %[[ARR_PTR:.*]] = cir.cast(array_to_ptrdecay, %[[ARR]] : !cir.ptr<!cir.array<!cir.array<!s32i x 1> x 2>>), !cir.ptr<!cir.array<!s32i x 1>> + // CHECK: %[[ARR_0_PTR:.*]] = cir.cast(array_to_ptrdecay, %[[ARR_PTR]] : !cir.ptr<!cir.array<!s32i x 1>>), !cir.ptr<!s32i> + // CHECK: %[[V_0_0:.*]] = cir.const #cir.int<5> : !s32i + // CHECK: cir.store %[[V_0_0]], %[[ARR_0_PTR]] : !s32i, !cir.ptr<!s32i> + // CHECK: %[[OFFSET:.*]] = cir.const #cir.int<1> : !s64i + // CHECK: %[[ARR_1:.*]] = cir.ptr_stride(%[[ARR_PTR]] : !cir.ptr<!cir.array<!s32i x 1>>, %[[OFFSET]] : !s64i), !cir.ptr<!cir.array<!s32i x 1>> + // CHECK: %[[ARR_1_PTR:.*]] = cir.cast(array_to_ptrdecay, %[[ARR_1]] : !cir.ptr<!cir.array<!s32i x 1>>), !cir.ptr<!s32i> + // CHECK: %[[V_1_0:.*]] = cir.const #cir.int<6> : !s32i + // CHECK: cir.store %[[V_1_0]], %[[ARR_1_PTR]] : !s32i, !cir.ptr<!s32i> +} + +void func5() { + int arr[2][1] = {{5}}; + + // CHECK: %[[ARR:.*]] = cir.alloca !cir.array<!cir.array<!s32i x 1> x 2>, !cir.ptr<!cir.array<!cir.array<!s32i x 1> x 2>>, ["arr", init] + // CHECK: %[[ARR_PTR:.*]] = cir.alloca !cir.ptr<!cir.array<!s32i x 1>>, !cir.ptr<!cir.ptr<!cir.array<!s32i x 1>>>, ["arrayinit.temp", init] + // CHECK: %[[ARR_0:.*]] = cir.cast(array_to_ptrdecay, %0 : !cir.ptr<!cir.array<!cir.array<!s32i x 1> x 2>>), !cir.ptr<!cir.array<!s32i x 1>> + // CHECK: %[[ARR_0_PTR:.*]] = cir.cast(array_to_ptrdecay, %[[ARR_0]] : !cir.ptr<!cir.array<!s32i x 1>>), !cir.ptr<!s32i> + // CHECK: %[[V_0_0:.*]] = cir.const #cir.int<5> : !s32i + // CHECK: cir.store %[[V_0_0]], %[[ARR_0_PTR]] : !s32i, !cir.ptr<!s32i> + // CHECK: %[[OFFSET:.*]] = cir.const #cir.int<1> : !s64i + // CHECK: %6 = cir.ptr_stride(%[[ARR_0]] : !cir.ptr<!cir.array<!s32i x 1>>, %[[OFFSET]] : !s64i), !cir.ptr<!cir.array<!s32i x 1>> + // CHECK: cir.store %6, %[[ARR_PTR]] : !cir.ptr<!cir.array<!s32i x 1>>, !cir.ptr<!cir.ptr<!cir.array<!s32i x 1>>> + // CHECK: %7 = cir.load %[[ARR_PTR]] : !cir.ptr<!cir.ptr<!cir.array<!s32i x 1>>>, !cir.ptr<!cir.array<!s32i x 1>> + // CHECK: %8 = cir.const #cir.zero : !cir.array<!s32i x 1> + // CHECK: cir.store %8, %7 : !cir.array<!s32i x 1>, !cir.ptr<!cir.array<!s32i x 1>> + // CHECK: %[[OFFSET_1:.*]] = cir.const #cir.int<1> : !s64i + // CHECK: %10 = cir.ptr_stride(%7 : !cir.ptr<!cir.array<!s32i x 1>>, %[[OFFSET_1]] : !s64i), !cir.ptr<!cir.array<!s32i x 1>> + // CHECK: cir.store %10, %[[ARR_PTR]] : !cir.ptr<!cir.array<!s32i x 1>>, !cir.ptr<!cir.ptr<!cir.array<!s32i x 1>>> +} + +void func6() { + int x = 4; + int arr[2] = { x, 5 }; + + // CHECK: %[[VAR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["x", init] + // CHECK: %[[ARR:.*]] = cir.alloca !cir.array<!s32i x 2>, !cir.ptr<!cir.array<!s32i x 2>>, ["arr", init] + // CHECK: %[[V:.*]] = cir.const #cir.int<4> : !s32i + // CHECK: cir.store %[[V]], %[[VAR]] : !s32i, !cir.ptr<!s32i> + // CHECK: %[[ARR_PTR:.*]] = cir.cast(array_to_ptrdecay, %[[ARR]] : !cir.ptr<!cir.array<!s32i x 2>>), !cir.ptr<!s32i> + // CHECK: %[[TMP:.*]] = cir.load %[[VAR]] : !cir.ptr<!s32i>, !s32i + // CHECK: cir.store %[[TMP]], %[[ARR_PTR]] : !s32i, !cir.ptr<!s32i> + // CHECK: %[[OFFSET:.*]] = cir.const #cir.int<1> : !s64i + // CHECK: %[[ELE_PTR:.*]] = cir.ptr_stride(%[[ARR_PTR]] : !cir.ptr<!s32i>, %[[OFFSET]] : !s64i), !cir.ptr<!s32i> + // CHECK: %[[V1:.*]] = cir.const #cir.int<5> : !s32i + // CHECK: cir.store %[[V1]], %[[ELE_PTR]] : !s32i, !cir.ptr<!s32i> +} + +void func7() { + int* arr[1] = {}; + + // CHECK: %[[ARR:.*]] = cir.alloca !cir.array<!cir.ptr<!s32i> x 1>, !cir.ptr<!cir.array<!cir.ptr<!s32i> x 1>>, ["arr", init] + // CHECK: %[[ARR_TMP:.*]] = cir.alloca !cir.ptr<!cir.ptr<!s32i>>, !cir.ptr<!cir.ptr<!cir.ptr<!s32i>>>, ["arrayinit.temp", init] + // CHECK: %[[ARR_PTR:.*]] = cir.cast(array_to_ptrdecay, %[[ARR]] : !cir.ptr<!cir.array<!cir.ptr<!s32i> x 1>>), !cir.ptr<!cir.ptr<!s32i>> + // CHECK: cir.store %[[ARR_PTR]], %[[ARR_TMP]] : !cir.ptr<!cir.ptr<!s32i>>, !cir.ptr<!cir.ptr<!cir.ptr<!s32i>>> + // CHECK: %[[TMP:.*]] = cir.load %[[ARR_TMP]] : !cir.ptr<!cir.ptr<!cir.ptr<!s32i>>>, !cir.ptr<!cir.ptr<!s32i>> + // CHECK: %[[NULL_PTR:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!s32i> + // CHECK: cir.store %[[NULL_PTR]], %[[TMP]] : !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>> + // CHECK: %[[OFFSET:.*]] = cir.const #cir.int<1> : !s64i + // CHECK: %[[ELE_PTR:.*]] = cir.ptr_stride(%[[TMP]] : !cir.ptr<!cir.ptr<!s32i>>, %[[OFFSET]] : !s64i), !cir.ptr<!cir.ptr<!s32i>> + // CHECK: cir.store %[[ELE_PTR]], %[[ARR_TMP]] : !cir.ptr<!cir.ptr<!s32i>>, !cir.ptr<!cir.ptr<!cir.ptr<!s32i>>> } -void func2(int p[10]) {} -// CHECK: cir.func @func2(%arg0: !cir.ptr<!s32i> +void func8(int p[10]) {} +// CHECK: cir.func @func8(%arg0: !cir.ptr<!s32i> // CHECK: cir.alloca !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>>, ["p", init] -void func3(int pp[10][5]) {} -// CHECK: cir.func @func3(%arg0: !cir.ptr<!cir.array<!s32i x 5>> +void func9(int pp[10][5]) {} +// CHECK: cir.func @func9(%arg0: !cir.ptr<!cir.array<!s32i x 5>> // CHECK: cir.alloca !cir.ptr<!cir.array<!s32i x 5>>, !cir.ptr<!cir.ptr<!cir.array<!s32i x 5>>> diff --git a/clang/test/CIR/Lowering/array.cpp b/clang/test/CIR/Lowering/array.cpp index 4fb996aefe79e..e1c977eb43141 100644 --- a/clang/test/CIR/Lowering/array.cpp +++ b/clang/test/CIR/Lowering/array.cpp @@ -30,15 +30,95 @@ int f[5] = {1, 2}; // CHECK: @f = dso_local global [5 x i32] [i32 1, i32 2, i32 0, i32 0, i32 0] void func() { - int l[10]; + int arr[10]; } // CHECK: define void @func() // CHECK-NEXT: alloca [10 x i32], i64 1, align 16 -void func2(int p[10]) {} -// CHECK: define void @func2(ptr {{%.*}}) +void func2() { + int arr2[2] = {5}; +} +// CHECK: define void @func2() +// CHECK: %[[ARR_ALLOCA:.*]] = alloca [2 x i32], i64 1, align 4 +// CHECK: %[[TMP:.*]] = alloca ptr, i64 1, align 8 +// CHECK: %[[ARR_PTR:.*]] = getelementptr i32, ptr %[[ARR_ALLOCA]], i32 0 +// CHECK: store i32 5, ptr %[[ARR_PTR]], align 4 +// CHECK: %[[ELE_1_PTR:.*]] = getelementptr i32, ptr %[[ARR_PTR]], i64 1 +// CHECK: store ptr %[[ELE_1_PTR]], ptr %[[TMP]], align 8 +// CHECK: %[[TMP2:.*]] = load ptr, ptr %[[TMP]], align 8 +// CHECK: store i32 0, ptr %[[TMP2]], align 4 +// CHECK: %[[ELE_1:.*]] = getelementptr i32, ptr %[[TMP2]], i64 1 +// CHECK: store ptr %[[ELE_1]], ptr %[[TMP]], align 8 + +void func3() { + int arr3[2] = {5, 6}; +} +// CHECK: define void @func3() +// CHECK: %[[ARR_ALLOCA:.*]] = alloca [2 x i32], i64 1, align 4 +// CHECK: %[[ARR_PTR:.*]] = getelementptr i32, ptr %[[ARR_ALLOCA]], i32 0 +// CHECK: store i32 5, ptr %[[ARR_PTR]], align 4 +// CHECK: %[[ELE_1_PTR:.*]] = getelementptr i32, ptr %[[ARR_PTR]], i64 1 +// CHECK: store i32 6, ptr %[[ELE_1_PTR]], align 4 + +void func4() { + int arr4[2][1] = {{5}, {6}}; +} +// CHECK: define void @func4() +// CHECK: %[[ARR_ALLOCA:.*]] = alloca [2 x [1 x i32]], i64 1, align 4 +// CHECK: %[[ARR_0:.*]] = getelementptr [1 x i32], ptr %[[ARR_ALLOCA]], i32 0 +// CHECK: %[[ARR_0_ELE_0:.*]] = getelementptr i32, ptr %[[ARR_0]], i32 0 +// CHECK: store i32 5, ptr %[[ARR_0_ELE_0]], align 4 +// CHECK: %[[ARR_1:.*]] = getelementptr [1 x i32], ptr %2, i64 1 +// CHECK: %[[ARR_0_ELE_0:.*]] = getelementptr i32, ptr %[[ARR_1]], i32 0 +// CHECK: store i32 6, ptr %[[ARR_0_ELE_0]], align 4 + +void func5() { + int arr5[2][1] = {{5}}; +} +// CHECK: define void @func5() +// CHECK: %[[ARR_ALLOCA:.*]] = alloca [2 x [1 x i32]], i64 1, align 4 +// CHECK: %[[TMP:.*]] = alloca ptr, i64 1, align 8 +// CHECK: %[[ARR_PTR:.*]] = getelementptr [1 x i32], ptr %[[ARR_ALLOCA]], i32 0 +// CHECK: %[[ARR_0:.*]] = getelementptr i32, ptr %[[ARR_PTR]], i32 0 +// CHECK: store i32 5, ptr %[[ARR_0]], align 4 +// CHECK: %[[ARR_1:.*]] = getelementptr [1 x i32], ptr %[[ARR_PTR]], i64 1 +// CHECK: store ptr %[[ARR_1]], ptr %[[TMP]], align 8 +// CHECK: %[[ARR_1_VAL:.*]] = load ptr, ptr %[[TMP]], align 8 +// CHECK: store [1 x i32] zeroinitializer, ptr %[[ARR_1_VAL]], align 4 +// CHECK: %[[ARR_1_PTR:.*]] = getelementptr [1 x i32], ptr %[[ARR_1_VAL]], i64 1 +// CHECK: store ptr %[[ARR_1_PTR]], ptr %[[TMP]], align 8 + +void func6() { + int x = 4; + int arr[2] = { x, 5 }; +} +// CHECK: define void @func6() +// CHECK: %[[VAR:.*]] = alloca i32, i64 1, align 4 +// CHECK: %[[ARR:.*]] = alloca [2 x i32], i64 1, align 4 +// CHECK: store i32 4, ptr %[[VAR]], align 4 +// CHECK: %[[ELE_0:.*]] = getelementptr i32, ptr %[[ARR]], i32 0 +// CHECK: %[[TMP:.*]] = load i32, ptr %[[VAR]], align 4 +// CHECK: store i32 %[[TMP]], ptr %[[ELE_0]], align 4 +// CHECK: %[[ELE_1:.*]] = getelementptr i32, ptr %[[ELE_0]], i64 1 +// CHECK: store i32 5, ptr %[[ELE_1]], align 4 + +void func7() { + int* arr[1] = {}; +} +// CHECK: define void @func7() +// CHECK: %[[ARR:.*]] = alloca [1 x ptr], i64 1, align 8 +// CHECK: %[[ALLOCA:.*]] = alloca ptr, i64 1, align 8 +// CHECK: %[[ELE_PTR:.*]] = getelementptr ptr, ptr %[[ARR]], i32 0 +// CHECK: store ptr %[[ELE_PTR]], ptr %[[ALLOCA]], align 8 +// CHECK: %[[TMP:.*]] = load ptr, ptr %[[ALLOCA]], align 8 +// CHECK: store ptr null, ptr %[[TMP]], align 8 +// CHECK: %[[ELE:.*]] = getelementptr ptr, ptr %[[TMP]], i64 1 +// CHECK: store ptr %[[ELE]], ptr %[[ALLOCA]], align 8 + +void func8(int p[10]) {} +// CHECK: define void @func8(ptr {{%.*}}) // CHECK-NEXT: alloca ptr, i64 1, align 8 -void func3(int pp[10][5]) {} -// CHECK: define void @func3(ptr {{%.*}}) +void func9(int pp[10][5]) {} +// CHECK: define void @func9(ptr {{%.*}}) // CHECK-NEXT: alloca ptr, i64 1, align 8 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits