Author: Amr Hesham Date: 2025-03-19T21:29:37+01:00 New Revision: 6aeae62aef63c7c11ab67a880716afdc92ac8422
URL: https://github.com/llvm/llvm-project/commit/6aeae62aef63c7c11ab67a880716afdc92ac8422 DIFF: https://github.com/llvm/llvm-project/commit/6aeae62aef63c7c11ab67a880716afdc92ac8422.diff LOG: [CIR] Upstream global initialization for ArrayType (#131657) This change adds global initialization for ArrayType Issue #130197 Added: Modified: clang/include/clang/CIR/Dialect/IR/CIRAttrs.td clang/lib/CIR/CodeGen/CIRGenBuilder.h clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h clang/lib/CIR/CodeGen/CIRGenDecl.cpp clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp clang/lib/CIR/CodeGen/CIRGenModule.cpp clang/lib/CIR/Dialect/IR/CIRAttrs.cpp clang/lib/CIR/Dialect/IR/CIRDialect.cpp clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h clang/test/CIR/CodeGen/array.cpp clang/test/CIR/IR/array.cir clang/test/CIR/Lowering/array.cpp Removed: ################################################################################ diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index 7b3741de29075..3680ded4afafe 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -154,6 +154,48 @@ def FPAttr : CIR_Attr<"FP", "fp", [TypedAttrInterface]> { }]; } + +//===----------------------------------------------------------------------===// +// ConstArrayAttr +//===----------------------------------------------------------------------===// + +def ConstArrayAttr : CIR_Attr<"ConstArray", "const_array", [TypedAttrInterface]> { + let summary = "A constant array from ArrayAttr or StringRefAttr"; + let description = [{ + An CIR array attribute is an array of literals of the specified attr types. + }]; + + let parameters = (ins AttributeSelfTypeParameter<"">:$type, + "mlir::Attribute":$elts, + "int":$trailingZerosNum); + + // Define a custom builder for the type; that removes the need to pass + // in an MLIRContext instance, as it can be infered from the `type`. + let builders = [ + AttrBuilderWithInferredContext<(ins "cir::ArrayType":$type, + "mlir::Attribute":$elts), [{ + int zeros = 0; + auto typeSize = mlir::cast<cir::ArrayType>(type).getSize(); + if (auto str = mlir::dyn_cast<mlir::StringAttr>(elts)) + zeros = typeSize - str.size(); + else + zeros = typeSize - mlir::cast<mlir::ArrayAttr>(elts).size(); + + return $_get(type.getContext(), type, elts, zeros); + }]> + ]; + + // Printing and parsing available in CIRDialect.cpp + let hasCustomAssemblyFormat = 1; + + // Enable verifier. + let genVerifyDecl = 1; + + let extraClassDeclaration = [{ + bool hasTrailingZeros() const { return getTrailingZerosNum() != 0; }; + }]; +} + //===----------------------------------------------------------------------===// // ConstPtrAttr //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h index 85f6a00e596a7..fef290612149a 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h +++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h @@ -45,6 +45,42 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy { return false; } + // 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<cir::ZeroAttr>(attr)) + return true; + + if (const auto ptrVal = mlir::dyn_cast<cir::ConstPtrAttr>(attr)) + return ptrVal.isNullValue(); + + if (const auto intVal = mlir::dyn_cast<cir::IntAttr>(attr)) + return intVal.isNullValue(); + + if (const auto boolVal = mlir::dyn_cast<cir::BoolAttr>(attr)) + return !boolVal.getValue(); + + if (auto fpAttr = mlir::dyn_cast<cir::FPAttr>(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 arrayVal = mlir::dyn_cast<cir::ConstArrayAttr>(attr)) { + if (mlir::isa<mlir::StringAttr>(arrayVal.getElts())) + return false; + for (const auto elt : mlir::cast<mlir::ArrayAttr>(arrayVal.getElts())) { + if (!isNullValue(elt)) + return false; + } + return true; + } + return false; + } + bool isInt(mlir::Type i) { return mlir::isa<cir::IntType>(i); } // Creates constant nullptr for pointer type ty. diff --git a/clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h b/clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h index 5b22a8e59908d..ca4e607992bbc 100644 --- a/clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h +++ b/clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h @@ -20,7 +20,6 @@ #include "CIRGenFunction.h" #include "CIRGenModule.h" -#include "llvm/ADT/SmallVector.h" namespace clang::CIRGen { @@ -41,6 +40,9 @@ class ConstantEmitter { /// block addresses or PredefinedExprs. ConstantEmitter(CIRGenFunction &cgf) : cgm(cgf.cgm), cgf(&cgf) {} + ConstantEmitter(CIRGenModule &cgm, CIRGenFunction *cgf = nullptr) + : cgm(cgm), cgf(cgf) {} + ConstantEmitter(const ConstantEmitter &other) = delete; ConstantEmitter &operator=(const ConstantEmitter &other) = delete; @@ -66,7 +68,7 @@ class ConstantEmitter { mlir::Attribute emitAbstract(SourceLocation loc, const APValue &value, QualType t); - mlir::Attribute tryEmitConstantExpr(const ConstantExpr *CE); + mlir::Attribute tryEmitConstantExpr(const ConstantExpr *ce); // These are private helper routines of the constant emitter that // can't actually be private because things are split out into helper diff --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp index 27ed0113a4f55..a93e8dbcb42de 100644 --- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp @@ -225,7 +225,6 @@ void CIRGenFunction::emitScalarInit(const Expr *init, mlir::Location loc, } assert(!cir::MissingFeatures::emitNullabilityCheck()); emitStoreThroughLValue(RValue::get(value), lvalue, true); - return; } void CIRGenFunction::emitExprAsInit(const Expr *init, const ValueDecl *d, diff --git a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp index 1ea7f6212766c..fc49d6da97206 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp @@ -158,13 +158,63 @@ class ConstExprEmitter // TODO(cir): this can be shared with LLVM's codegen static QualType getNonMemoryType(CIRGenModule &cgm, QualType type) { - if (auto at = type->getAs<AtomicType>()) { + if (const auto *at = type->getAs<AtomicType>()) { return cgm.getASTContext().getQualifiedType(at->getValueType(), type.getQualifiers()); } return type; } +static mlir::Attribute +emitArrayConstant(CIRGenModule &cgm, mlir::Type desiredType, + mlir::Type commonElementType, unsigned arrayBound, + SmallVectorImpl<mlir::TypedAttr> &elements, + mlir::TypedAttr filler) { + const CIRGenBuilderTy &builder = cgm.getBuilder(); + + unsigned nonzeroLength = arrayBound; + if (elements.size() < nonzeroLength && builder.isNullValue(filler)) + nonzeroLength = elements.size(); + + if (nonzeroLength == elements.size()) { + while (nonzeroLength > 0 && + builder.isNullValue(elements[nonzeroLength - 1])) + --nonzeroLength; + } + + if (nonzeroLength == 0) + return cir::ZeroAttr::get(builder.getContext(), desiredType); + + const unsigned trailingZeroes = arrayBound - nonzeroLength; + + // Add a zeroinitializer array filler if we have lots of trailing zeroes. + if (trailingZeroes >= 8) { + assert(elements.size() >= nonzeroLength && + "missing initializer for non-zero element"); + } else if (elements.size() != arrayBound) { + elements.resize(arrayBound, filler); + + if (filler.getType() != commonElementType) + commonElementType = {}; + } + + if (commonElementType) { + SmallVector<mlir::Attribute, 4> eles; + eles.reserve(elements.size()); + + for (const auto &element : elements) + eles.push_back(element); + + return cir::ConstArrayAttr::get( + cir::ArrayType::get(builder.getContext(), commonElementType, + arrayBound), + mlir::ArrayAttr::get(builder.getContext(), eles)); + } + + cgm.errorNYI("array with diff erent type elements"); + return {}; +} + //===----------------------------------------------------------------------===// // ConstantEmitter //===----------------------------------------------------------------------===// @@ -271,16 +321,57 @@ mlir::Attribute ConstantEmitter::tryEmitPrivate(const APValue &value, cgm.getASTContext().getTargetInfo().useFP16ConversionIntrinsics()) { cgm.errorNYI("ConstExprEmitter::tryEmitPrivate half"); return {}; - } else { - mlir::Type ty = cgm.convertType(destType); - assert(mlir::isa<cir::CIRFPTypeInterface>(ty) && - "expected floating-point type"); - return cgm.getBuilder().getAttr<cir::FPAttr>(ty, init); } + + mlir::Type ty = cgm.convertType(destType); + assert(mlir::isa<cir::CIRFPTypeInterface>(ty) && + "expected floating-point type"); + return cgm.getBuilder().getAttr<cir::FPAttr>(ty, init); } case APValue::Array: { - cgm.errorNYI("ConstExprEmitter::tryEmitPrivate array"); - return {}; + const ArrayType *arrayTy = cgm.getASTContext().getAsArrayType(destType); + const QualType arrayElementTy = arrayTy->getElementType(); + const unsigned numElements = value.getArraySize(); + const unsigned numInitElts = value.getArrayInitializedElts(); + + mlir::Attribute filler; + if (value.hasArrayFiller()) { + filler = tryEmitPrivate(value.getArrayFiller(), arrayElementTy); + if (!filler) + return {}; + } + + SmallVector<mlir::TypedAttr, 16> elements; + if (filler && builder.isNullValue(filler)) + elements.reserve(numInitElts + 1); + else + elements.reserve(numInitElts); + + mlir::Type commonElementType; + for (unsigned i = 0; i < numInitElts; ++i) { + const APValue &arrayElement = value.getArrayInitializedElt(i); + const mlir::Attribute element = + tryEmitPrivateForMemory(arrayElement, arrayElementTy); + if (!element) + return {}; + + const mlir::TypedAttr elementTyped = mlir::cast<mlir::TypedAttr>(element); + if (i == 0) + commonElementType = elementTyped.getType(); + else if (elementTyped.getType() != commonElementType) { + commonElementType = {}; + } + + elements.push_back(elementTyped); + } + + mlir::TypedAttr typedFiller = llvm::cast_or_null<mlir::TypedAttr>(filler); + if (filler && !typedFiller) + cgm.errorNYI("array filler should always be typed"); + + mlir::Type desiredType = cgm.convertType(destType); + return emitArrayConstant(cgm, desiredType, commonElementType, numElements, + elements, typedFiller); } case APValue::Vector: { cgm.errorNYI("ConstExprEmitter::tryEmitPrivate vector"); @@ -290,9 +381,23 @@ mlir::Attribute ConstantEmitter::tryEmitPrivate(const APValue &value, cgm.errorNYI("ConstExprEmitter::tryEmitPrivate member pointer"); return {}; } - case APValue::LValue: - cgm.errorNYI("ConstExprEmitter::tryEmitPrivate lvalue"); + case APValue::LValue: { + + if (value.getLValueBase()) { + cgm.errorNYI("non-null pointer initialization"); + } else { + + mlir::Type desiredType = cgm.convertType(destType); + if (const cir::PointerType ptrType = + mlir::dyn_cast<cir::PointerType>(desiredType)) { + return builder.getConstPtrAttr(ptrType, + value.getLValueOffset().getQuantity()); + } else { + llvm_unreachable("non-pointer variable initialized with a pointer"); + } + } return {}; + } case APValue::Struct: case APValue::Union: cgm.errorNYI("ConstExprEmitter::tryEmitPrivate struct or union"); diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index 36bfc2cdf6f02..9776a4e09f9e0 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "CIRGenModule.h" +#include "CIRGenConstantEmitter.h" #include "CIRGenFunction.h" #include "clang/AST/ASTContext.h" @@ -128,7 +129,8 @@ void CIRGenModule::emitGlobalFunctionDefinition(clang::GlobalDecl gd, void CIRGenModule::emitGlobalVarDefinition(const clang::VarDecl *vd, bool isTentative) { - mlir::Type type = convertType(vd->getType()); + const QualType astTy = vd->getType(); + const mlir::Type type = convertType(vd->getType()); if (clang::IdentifierInfo *identifier = vd->getIdentifier()) { auto varOp = builder.create<cir::GlobalOp>(getLoc(vd->getSourceRange()), identifier->getName(), type); @@ -141,38 +143,8 @@ void CIRGenModule::emitGlobalVarDefinition(const clang::VarDecl *vd, if (initExpr) { mlir::Attribute initializer; if (APValue *value = initDecl->evaluateValue()) { - switch (value->getKind()) { - case APValue::Int: { - if (mlir::isa<cir::BoolType>(type)) - initializer = - builder.getCIRBoolAttr(value->getInt().getZExtValue()); - else - initializer = builder.getAttr<cir::IntAttr>(type, value->getInt()); - break; - } - case APValue::Float: { - initializer = builder.getAttr<cir::FPAttr>(type, value->getFloat()); - break; - } - case APValue::LValue: { - if (value->getLValueBase()) { - errorNYI(initExpr->getSourceRange(), - "non-null pointer initialization"); - } else { - if (auto ptrType = mlir::dyn_cast<cir::PointerType>(type)) { - initializer = builder.getConstPtrAttr( - ptrType, value->getLValueOffset().getQuantity()); - } else { - llvm_unreachable( - "non-pointer variable initialized with a pointer"); - } - } - break; - } - default: - errorNYI(initExpr->getSourceRange(), "unsupported initializer kind"); - break; - } + ConstantEmitter emitter(*this); + initializer = emitter.tryEmitPrivateForMemory(*value, astTy); } else { errorNYI(initExpr->getSourceRange(), "non-constant initializer"); } diff --git a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp index 8e8f7d5b7d7cb..a8d9f6a0e6e9b 100644 --- a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp @@ -190,6 +190,115 @@ LogicalResult FPAttr::verify(function_ref<InFlightDiagnostic()> emitError, return success(); } +//===----------------------------------------------------------------------===// +// CIR ConstArrayAttr +//===----------------------------------------------------------------------===// + +LogicalResult +ConstArrayAttr::verify(function_ref<::mlir::InFlightDiagnostic()> emitError, + Type type, Attribute elts, int trailingZerosNum) { + + if (!(mlir::isa<ArrayAttr>(elts) || mlir::isa<StringAttr>(elts))) + return emitError() << "constant array expects ArrayAttr or StringAttr"; + + if (auto strAttr = mlir::dyn_cast<StringAttr>(elts)) { + const auto arrayTy = mlir::cast<ArrayType>(type); + const auto intTy = mlir::dyn_cast<IntType>(arrayTy.getEltType()); + + // TODO: add CIR type for char. + if (!intTy || intTy.getWidth() != 8) { + emitError() << "constant array element for string literals expects " + "!cir.int<u, 8> element type"; + return failure(); + } + return success(); + } + + assert(mlir::isa<ArrayAttr>(elts)); + const auto arrayAttr = mlir::cast<mlir::ArrayAttr>(elts); + const auto arrayTy = mlir::cast<ArrayType>(type); + + // Make sure both number of elements and subelement types match type. + if (arrayTy.getSize() != arrayAttr.size() + trailingZerosNum) + return emitError() << "constant array size should match type size"; + return success(); +} + +Attribute ConstArrayAttr::parse(AsmParser &parser, Type type) { + mlir::FailureOr<Type> resultTy; + mlir::FailureOr<Attribute> resultVal; + + // Parse literal '<' + if (parser.parseLess()) + return {}; + + // Parse variable 'value' + resultVal = FieldParser<Attribute>::parse(parser); + if (failed(resultVal)) { + parser.emitError( + parser.getCurrentLocation(), + "failed to parse ConstArrayAttr parameter 'value' which is " + "to be a `Attribute`"); + return {}; + } + + // ArrayAttrrs have per-element type, not the type of the array... + if (mlir::isa<ArrayAttr>(*resultVal)) { + // Array has implicit type: infer from const array type. + if (parser.parseOptionalColon().failed()) { + resultTy = type; + } else { // Array has explicit type: parse it. + resultTy = FieldParser<Type>::parse(parser); + if (failed(resultTy)) { + parser.emitError( + parser.getCurrentLocation(), + "failed to parse ConstArrayAttr parameter 'type' which is " + "to be a `::mlir::Type`"); + return {}; + } + } + } else { + auto ta = mlir::cast<TypedAttr>(*resultVal); + resultTy = ta.getType(); + if (mlir::isa<mlir::NoneType>(*resultTy)) { + parser.emitError(parser.getCurrentLocation(), + "expected type declaration for string literal"); + return {}; + } + } + + unsigned zeros = 0; + if (parser.parseOptionalComma().succeeded()) { + if (parser.parseOptionalKeyword("trailing_zeros").succeeded()) { + unsigned typeSize = + mlir::cast<cir::ArrayType>(resultTy.value()).getSize(); + mlir::Attribute elts = resultVal.value(); + if (auto str = mlir::dyn_cast<mlir::StringAttr>(elts)) + zeros = typeSize - str.size(); + else + zeros = typeSize - mlir::cast<mlir::ArrayAttr>(elts).size(); + } else { + return {}; + } + } + + // Parse literal '>' + if (parser.parseGreater()) + return {}; + + return parser.getChecked<ConstArrayAttr>( + parser.getCurrentLocation(), parser.getContext(), resultTy.value(), + resultVal.value(), zeros); +} + +void ConstArrayAttr::print(AsmPrinter &printer) const { + printer << "<"; + printer.printStrippedAttrOrType(getElts()); + if (getTrailingZerosNum()) + printer << ", trailing_zeros"; + printer << ">"; +} + //===----------------------------------------------------------------------===// // CIR Dialect //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 56247e2466350..e3872bf73f750 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -149,6 +149,12 @@ static LogicalResult checkConstantTypes(mlir::Operation *op, mlir::Type opType, return success(); } + if (isa<cir::ZeroAttr>(attrType)) { + if (::mlir::isa<cir::ArrayType>(opType)) + return success(); + return op->emitOpError("zero expects struct or array type"); + } + if (mlir::isa<cir::BoolAttr>(attrType)) { if (!mlir::isa<cir::BoolType>(opType)) return op->emitOpError("result type (") @@ -166,6 +172,9 @@ static LogicalResult checkConstantTypes(mlir::Operation *op, mlir::Type opType, return success(); } + if (mlir::isa<cir::ConstArrayAttr>(attrType)) + return success(); + assert(isa<TypedAttr>(attrType) && "What else could we be looking at here?"); return op->emitOpError("global with type ") << cast<TypedAttr>(attrType).getType() << " not yet supported"; diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index db94cf511ba63..83226ed8680bb 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -128,6 +128,21 @@ static mlir::Value emitToMemory(mlir::ConversionPatternRewriter &rewriter, return value; } +static mlir::Value +emitCirAttrToMemory(mlir::Operation *parentOp, mlir::Attribute attr, + mlir::ConversionPatternRewriter &rewriter, + const mlir::TypeConverter *converter, + mlir::DataLayout const &dataLayout) { + + mlir::Value loweredValue = + lowerCirAttrAsValue(parentOp, attr, rewriter, converter); + if (auto boolAttr = mlir::dyn_cast<cir::BoolAttr>(attr)) { + return emitToMemory(rewriter, dataLayout, boolAttr.getType(), loweredValue); + } + + return loweredValue; +} + mlir::LLVM::Linkage convertLinkage(cir::GlobalLinkageKind linkage) { using CIR = cir::GlobalLinkageKind; using LLVM = mlir::LLVM::Linkage; @@ -184,14 +199,16 @@ class CIRAttrToValue { mlir::Value visit(mlir::Attribute attr) { return llvm::TypeSwitch<mlir::Attribute, mlir::Value>(attr) - .Case<cir::IntAttr, cir::FPAttr, cir::ConstPtrAttr>( - [&](auto attrT) { return visitCirAttr(attrT); }) + .Case<cir::IntAttr, cir::FPAttr, cir::ConstArrayAttr, cir::ConstPtrAttr, + cir::ZeroAttr>([&](auto attrT) { return visitCirAttr(attrT); }) .Default([&](auto attrT) { return mlir::Value(); }); } mlir::Value visitCirAttr(cir::IntAttr intAttr); mlir::Value visitCirAttr(cir::FPAttr fltAttr); mlir::Value visitCirAttr(cir::ConstPtrAttr ptrAttr); + mlir::Value visitCirAttr(cir::ConstArrayAttr attr); + mlir::Value visitCirAttr(cir::ZeroAttr attr); private: mlir::Operation *parentOp; @@ -199,6 +216,18 @@ class CIRAttrToValue { const mlir::TypeConverter *converter; }; +/// Switches on the type of attribute and calls the appropriate conversion. +mlir::Value lowerCirAttrAsValue(mlir::Operation *parentOp, + const mlir::Attribute attr, + mlir::ConversionPatternRewriter &rewriter, + const mlir::TypeConverter *converter) { + CIRAttrToValue valueConverter(parentOp, rewriter, converter); + mlir::Value value = valueConverter.visit(attr); + if (!value) + llvm_unreachable("unhandled attribute type"); + return value; +} + /// IntAttr visitor. mlir::Value CIRAttrToValue::visitCirAttr(cir::IntAttr intAttr) { mlir::Location loc = parentOp->getLoc(); @@ -228,6 +257,42 @@ mlir::Value CIRAttrToValue::visitCirAttr(cir::FPAttr fltAttr) { loc, converter->convertType(fltAttr.getType()), fltAttr.getValue()); } +// ConstArrayAttr visitor +mlir::Value CIRAttrToValue::visitCirAttr(cir::ConstArrayAttr attr) { + mlir::Type llvmTy = converter->convertType(attr.getType()); + mlir::Location loc = parentOp->getLoc(); + mlir::Value result; + + if (auto zeros = attr.getTrailingZerosNum()) { + mlir::Type arrayTy = attr.getType(); + result = rewriter.create<mlir::LLVM::ZeroOp>( + loc, converter->convertType(arrayTy)); + } else { + result = rewriter.create<mlir::LLVM::UndefOp>(loc, llvmTy); + } + + // Iteratively lower each constant element of the array. + if (auto arrayAttr = mlir::dyn_cast<mlir::ArrayAttr>(attr.getElts())) { + for (auto [idx, elt] : llvm::enumerate(arrayAttr)) { + mlir::DataLayout dataLayout(parentOp->getParentOfType<mlir::ModuleOp>()); + mlir::Value init = visit(elt); + result = + rewriter.create<mlir::LLVM::InsertValueOp>(loc, result, init, idx); + } + } else { + llvm_unreachable("unexpected ConstArrayAttr elements"); + } + + return result; +} + +/// ZeroAttr visitor. +mlir::Value CIRAttrToValue::visitCirAttr(cir::ZeroAttr attr) { + auto loc = parentOp->getLoc(); + return rewriter.create<mlir::LLVM::ZeroOp>( + loc, converter->convertType(attr.getType())); +} + // This class handles rewriting initializer attributes for types that do not // require region initialization. class GlobalInitAttrRewriter { @@ -705,7 +770,7 @@ CIRToLLVMGlobalOpLowering::matchAndRewriteRegionInitializedGlobal( cir::GlobalOp op, mlir::Attribute init, mlir::ConversionPatternRewriter &rewriter) const { // TODO: Generalize this handling when more types are needed here. - assert(isa<cir::ConstPtrAttr>(init)); + assert((isa<cir::ConstArrayAttr, cir::ConstPtrAttr, cir::ZeroAttr>(init))); // TODO(cir): once LLVM's dialect has proper equivalent attributes this // should be updated. For now, we use a custom op to initialize globals @@ -758,7 +823,8 @@ mlir::LogicalResult CIRToLLVMGlobalOpLowering::matchAndRewrite( op.emitError() << "unsupported initializer '" << init.value() << "'"; return mlir::failure(); } - } else if (mlir::isa<cir::ConstPtrAttr>(init.value())) { + } else if (mlir::isa<cir::ConstArrayAttr, cir::ConstPtrAttr, cir::ZeroAttr>( + init.value())) { // TODO(cir): once LLVM's dialect has proper equivalent attributes this // should be updated. For now, we use a custom op to initialize globals // to the appropriate value. diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h index b96e16113abe0..d49ab815941dc 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h @@ -20,6 +20,12 @@ namespace cir { namespace direct { +/// Convert a CIR attribute to an LLVM attribute. May use the datalayout for +/// lowering attributes to-be-stored in memory. +mlir::Value lowerCirAttrAsValue(mlir::Operation *parentOp, mlir::Attribute attr, + mlir::ConversionPatternRewriter &rewriter, + const mlir::TypeConverter *converter); + mlir::LLVM::Linkage convertLinkage(cir::GlobalLinkageKind linkage); class CIRToLLVMCastOpLowering : public mlir::OpConversionPattern<cir::CastOp> { diff --git a/clang/test/CIR/CodeGen/array.cpp b/clang/test/CIR/CodeGen/array.cpp index 02ecdc11e1d94..294c4af822c44 100644 --- a/clang/test/CIR/CodeGen/array.cpp +++ b/clang/test/CIR/CodeGen/array.cpp @@ -12,15 +12,30 @@ extern int b[10]; extern int bb[10][5]; // CHECK: cir.global external @bb : !cir.array<!cir.array<!cir.int<s, 32> x 5> x 10> -void f() { +int c[10] = {}; +// CHECK: cir.global external @c = #cir.zero : !cir.array<!cir.int<s, 32> x 10> + +int d[3] = {1, 2, 3}; +// CHECK: cir.global external @d = #cir.const_array<[#cir.int<1> : !cir.int<s, 32>, #cir.int<2> : !cir.int<s, 32>, #cir.int<3> : !cir.int<s, 32>]> : !cir.array<!cir.int<s, 32> x 3> + +int dd[3][2] = {{1, 2}, {3, 4}, {5, 6}}; +// CHECK: cir.global external @dd = #cir.const_array<[#cir.const_array<[#cir.int<1> : !cir.int<s, 32>, #cir.int<2> : !cir.int<s, 32>]> : !cir.array<!cir.int<s, 32> x 2>, #cir.const_array<[#cir.int<3> : !cir.int<s, 32>, #cir.int<4> : !cir.int<s, 32>]> : !cir.array<!cir.int<s, 32> x 2>, #cir.const_array<[#cir.int<5> : !cir.int<s, 32>, #cir.int<6> : !cir.int<s, 32>]> : !cir.array<!cir.int<s, 32> x 2>]> : !cir.array<!cir.array<!cir.int<s, 32> x 2> x 3> + +int e[10] = {1, 2}; +// CHECK: cir.global external @e = #cir.const_array<[#cir.int<1> : !cir.int<s, 32>, #cir.int<2> : !cir.int<s, 32>], trailing_zeros> : !cir.array<!cir.int<s, 32> x 10> + +int f[5] = {1, 2}; +// CHECK: cir.global external @f = #cir.const_array<[#cir.int<1> : !cir.int<s, 32>, #cir.int<2> : !cir.int<s, 32>, #cir.int<0> : !cir.int<s, 32>, #cir.int<0> : !cir.int<s, 32>, #cir.int<0> : !cir.int<s, 32>]> : !cir.array<!cir.int<s, 32> x 5> + +void func() { int l[10]; // CHECK: %[[ARR:.*]] = cir.alloca !cir.array<!cir.int<s, 32> x 10>, !cir.ptr<!cir.array<!cir.int<s, 32> x 10>>, ["l"] } -void f2(int p[10]) {} -// CHECK: cir.func @f2(%arg0: !cir.ptr<!cir.int<s, 32>> +void func2(int p[10]) {} +// CHECK: cir.func @func2(%arg0: !cir.ptr<!cir.int<s, 32>> // CHECK: cir.alloca !cir.ptr<!cir.int<s, 32>>, !cir.ptr<!cir.ptr<!cir.int<s, 32>>>, ["p", init] -void f3(int pp[10][5]) {} -// CHECK: cir.func @f3(%arg0: !cir.ptr<!cir.array<!cir.int<s, 32> x 5>> +void func3(int pp[10][5]) {} +// CHECK: cir.func @func3(%arg0: !cir.ptr<!cir.array<!cir.int<s, 32> x 5>> // CHECK: cir.alloca !cir.ptr<!cir.array<!cir.int<s, 32> x 5>>, !cir.ptr<!cir.ptr<!cir.array<!cir.int<s, 32> x 5>>> diff --git a/clang/test/CIR/IR/array.cir b/clang/test/CIR/IR/array.cir index 293a202d18a92..790a988d9b9d8 100644 --- a/clang/test/CIR/IR/array.cir +++ b/clang/test/CIR/IR/array.cir @@ -14,35 +14,47 @@ cir.global external @b : !cir.array<!cir.int<s, 32> x 10> cir.global external @bb : !cir.array<!cir.array<!cir.int<s, 32> x 10> x 10> // CHECK: cir.global external @bb : !cir.array<!cir.array<!cir.int<s, 32> x 10> x 10> -cir.func @f() { +cir.global external @c = #cir.zero : !cir.array<!cir.int<s, 32> x 10> +// CHECK: cir.global external @c = #cir.zero : !cir.array<!cir.int<s, 32> x 10> + +cir.global external @d = #cir.const_array<[#cir.int<1> : !cir.int<s, 32>, #cir.int<2> : !cir.int<s, 32>, #cir.int<3> : !cir.int<s, 32>]> : !cir.array<!cir.int<s, 32> x 3> +// CHECK: cir.global external @d = #cir.const_array<[#cir.int<1> : !cir.int<s, 32>, #cir.int<2> : !cir.int<s, 32>, #cir.int<3> : !cir.int<s, 32>]> : !cir.array<!cir.int<s, 32> x 3> + +cir.global external @dd = #cir.const_array<[#cir.const_array<[#cir.int<1> : !cir.int<s, 32>, #cir.int<2> : !cir.int<s, 32>]> : !cir.array<!cir.int<s, 32> x 2>, #cir.const_array<[#cir.int<3> : !cir.int<s, 32>, #cir.int<4> : !cir.int<s, 32>]> : !cir.array<!cir.int<s, 32> x 2>, #cir.const_array<[#cir.int<5> : !cir.int<s, 32>, #cir.int<6> : !cir.int<s, 32>]> : !cir.array<!cir.int<s, 32> x 2>]> : !cir.array<!cir.array<!cir.int<s, 32> x 2> x 3> +// CHECK: cir.global external @dd = #cir.const_array<[#cir.const_array<[#cir.int<1> : !cir.int<s, 32>, #cir.int<2> : !cir.int<s, 32>]> : !cir.array<!cir.int<s, 32> x 2>, #cir.const_array<[#cir.int<3> : !cir.int<s, 32>, #cir.int<4> : !cir.int<s, 32>]> : !cir.array<!cir.int<s, 32> x 2>, #cir.const_array<[#cir.int<5> : !cir.int<s, 32>, #cir.int<6> : !cir.int<s, 32>]> : !cir.array<!cir.int<s, 32> x 2>]> : !cir.array<!cir.array<!cir.int<s, 32> x 2> x 3> + +cir.global external @e = #cir.const_array<[#cir.int<1> : !cir.int<s, 32>, #cir.int<2> : !cir.int<s, 32>], trailing_zeros> : !cir.array<!cir.int<s, 32> x 10> +// CHECK: cir.global external @e = #cir.const_array<[#cir.int<1> : !cir.int<s, 32>, #cir.int<2> : !cir.int<s, 32>], trailing_zeros> : !cir.array<!cir.int<s, 32> x 10> + +cir.func @func() { %0 = cir.alloca !cir.array<!cir.int<s, 32> x 10>, !cir.ptr<!cir.array<!cir.int<s, 32> x 10>>, ["l"] {alignment = 4 : i64} cir.return } -// CHECK: cir.func @f() { +// CHECK: cir.func @func() { // CHECK: %0 = cir.alloca !cir.array<!cir.int<s, 32> x 10>, !cir.ptr<!cir.array<!cir.int<s, 32> x 10>>, ["l"] {alignment = 4 : i64} // CHECK: cir.return // CHECK: } -cir.func @f2(%arg0: !cir.ptr<!cir.int<s, 32>>) { +cir.func @func2(%arg0: !cir.ptr<!cir.int<s, 32>>) { %0 = cir.alloca !cir.ptr<!cir.int<s, 32>>, !cir.ptr<!cir.ptr<!cir.int<s, 32>>>, ["p", init] {alignment = 8 : i64} cir.store %arg0, %0 : !cir.ptr<!cir.int<s, 32>>, !cir.ptr<!cir.ptr<!cir.int<s, 32>>> cir.return } -// CHECK: cir.func @f2(%arg0: !cir.ptr<!cir.int<s, 32>>) { +// CHECK: cir.func @func2(%arg0: !cir.ptr<!cir.int<s, 32>>) { // CHECK: %0 = cir.alloca !cir.ptr<!cir.int<s, 32>>, !cir.ptr<!cir.ptr<!cir.int<s, 32>>>, ["p", init] {alignment = 8 : i64} // CHECK: cir.store %arg0, %0 : !cir.ptr<!cir.int<s, 32>>, !cir.ptr<!cir.ptr<!cir.int<s, 32>>> // CHECK: cir.return // CHECK: } -cir.func @f3(%arg0: !cir.ptr<!cir.array<!cir.int<s, 32> x 10>>) { +cir.func @func3(%arg0: !cir.ptr<!cir.array<!cir.int<s, 32> x 10>>) { %0 = cir.alloca !cir.ptr<!cir.array<!cir.int<s, 32> x 10>>, !cir.ptr<!cir.ptr<!cir.array<!cir.int<s, 32> x 10>>>, ["pp", init] {alignment = 8 : i64} cir.store %arg0, %0 : !cir.ptr<!cir.array<!cir.int<s, 32> x 10>>, !cir.ptr<!cir.ptr<!cir.array<!cir.int<s, 32> x 10>>> cir.return } -// CHECK: cir.func @f3(%arg0: !cir.ptr<!cir.array<!cir.int<s, 32> x 10>>) { +// CHECK: cir.func @func3(%arg0: !cir.ptr<!cir.array<!cir.int<s, 32> x 10>>) { // CHECK: %0 = cir.alloca !cir.ptr<!cir.array<!cir.int<s, 32> x 10>>, !cir.ptr<!cir.ptr<!cir.array<!cir.int<s, 32> x 10>>>, ["pp", init] {alignment = 8 : i64} // CHECK: cir.store %arg0, %0 : !cir.ptr<!cir.array<!cir.int<s, 32> x 10>>, !cir.ptr<!cir.ptr<!cir.array<!cir.int<s, 32> x 10>>> // CHECK: cir.return diff --git a/clang/test/CIR/Lowering/array.cpp b/clang/test/CIR/Lowering/array.cpp index 42208a81caea2..763980b9124a3 100644 --- a/clang/test/CIR/Lowering/array.cpp +++ b/clang/test/CIR/Lowering/array.cpp @@ -12,16 +12,33 @@ extern int b[10]; extern int bb[10][5]; // CHECK: @bb = external dso_local global [10 x [5 x i32]] -void f() { +int c[10] = {}; +// CHECK: @c = dso_local global [10 x i32] zeroinitializer + +int d[3] = {1, 2, 3}; +// CHECK: @d = dso_local global [3 x i32] [i32 1, i32 2, i32 3] + +int dd[3][2] = {{1, 2}, {3, 4}, {5, 6}}; +// CHECK: @dd = dso_local global [3 x [2 x i32]] [ +// CHECK: [2 x i32] [i32 1, i32 2], [2 x i32] +// CHECK: [i32 3, i32 4], [2 x i32] [i32 5, i32 6]] + +int e[10] = {1, 2}; +// CHECK: @e = dso_local global [10 x i32] [i32 1, i32 2, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0] + +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]; } -// CHECK: define void @f() +// CHECK: define void @func() // CHECK-NEXT: alloca [10 x i32], i64 1, align 16 -void f2(int p[10]) {} -// CHECK: define void @f2(ptr {{%.*}}) +void func2(int p[10]) {} +// CHECK: define void @func2(ptr {{%.*}}) // CHECK-NEXT: alloca ptr, i64 1, align 8 -void f3(int pp[10][5]) {} -// CHECK: define void @f3(ptr {{%.*}}) +void func3(int pp[10][5]) {} +// CHECK: define void @func3(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