https://github.com/AmrDeveloper created https://github.com/llvm/llvm-project/pull/137233
This change adds support for the variable-length arrays Issue https://github.com/llvm/llvm-project/issues/130197 >From b86553a6f9a087c1c064359726e649942f1499d0 Mon Sep 17 00:00:00 2001 From: AmrDeveloper <am...@programmer.net> Date: Wed, 23 Apr 2025 18:57:35 +0200 Subject: [PATCH] [CIR] Upstream support for Variable size Array --- .../CIR/Dialect/Builder/CIRBaseBuilder.h | 17 ++ clang/include/clang/CIR/Dialect/IR/CIROps.td | 17 +- clang/include/clang/CIR/MissingFeatures.h | 1 + clang/lib/CIR/CodeGen/CIRGenDecl.cpp | 32 ++-- clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 98 +++++++++- clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 175 ++++++++++++++++++ clang/lib/CIR/CodeGen/CIRGenFunction.h | 38 ++++ clang/lib/CIR/CodeGen/CIRGenModule.cpp | 6 + clang/lib/CIR/CodeGen/CIRGenTypeCache.h | 5 + clang/lib/CIR/CodeGen/CIRGenTypes.cpp | 10 + .../lib/CIR/Dialect/Transforms/FlattenCFG.cpp | 53 +++++- .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 9 +- clang/test/CIR/CodeGen/struct.c | 39 ++-- clang/test/CIR/CodeGen/struct.cpp | 19 +- clang/test/CIR/CodeGen/typedef.c | 10 +- clang/test/CIR/CodeGen/union.c | 20 +- clang/test/CIR/CodeGen/vla.cpp | 71 +++++++ clang/test/CIR/Lowering/local-vars.cpp | 18 +- 18 files changed, 556 insertions(+), 82 deletions(-) create mode 100644 clang/test/CIR/CodeGen/vla.cpp diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h index ef29791ed2783..f0089525136ce 100644 --- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h +++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h @@ -175,6 +175,23 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { return create<cir::AllocaOp>(loc, addrType, type, name, alignment); } + mlir::Value createAlloca(mlir::Location loc, cir::PointerType addrType, + mlir::Type type, llvm::StringRef name, + mlir::IntegerAttr alignment, + mlir::Value dynAllocSize) { + return create<cir::AllocaOp>(loc, addrType, type, name, alignment, + dynAllocSize); + } + + mlir::Value createAlloca(mlir::Location loc, cir::PointerType addrType, + mlir::Type type, llvm::StringRef name, + clang::CharUnits alignment, + mlir::Value dynAllocSize) { + auto alignmentIntAttr = getSizeFromCharUnits(getContext(), alignment); + return createAlloca(loc, addrType, type, name, alignmentIntAttr, + dynAllocSize); + } + cir::LoadOp createLoad(mlir::Location loc, mlir::Value ptr, bool isVolatile = false, uint64_t alignment = 0) { mlir::IntegerAttr intAttr; diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index bb19de31b4fa5..10a2e10b4140c 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -348,6 +348,7 @@ def AllocaOp : CIR_Op<"alloca", [ }]; let arguments = (ins + Optional<PrimitiveInt>:$dynAllocSize, TypeAttr:$allocaType, StrAttr:$name, UnitAttr:$init, @@ -364,16 +365,30 @@ def AllocaOp : CIR_Op<"alloca", [ OpBuilder<(ins "mlir::Type":$addr, "mlir::Type":$allocaType, "llvm::StringRef":$name, - "mlir::IntegerAttr":$alignment)> + "mlir::IntegerAttr":$alignment)>, + + OpBuilder<(ins "mlir::Type":$addr, + "mlir::Type":$allocaType, + "llvm::StringRef":$name, + "mlir::IntegerAttr":$alignment, + "mlir::Value":$dynAllocSize), + [{ + if (dynAllocSize) + $_state.addOperands(dynAllocSize); + build($_builder, $_state, addr, allocaType, name, alignment); + }]> ]; let extraClassDeclaration = [{ // Whether the alloca input type is a pointer. bool isPointerType() { return ::mlir::isa<::cir::PointerType>(getAllocaType()); } + + bool isDynamic() { return (bool)getDynAllocSize(); } }]; let assemblyFormat = [{ $allocaType `,` qualified(type($addr)) `,` + ($dynAllocSize^ `:` type($dynAllocSize) `,`)? `[` $name (`,` `init` $init^)? (`,` `const` $constant^)? diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 6bfc1199aea55..fbee81362dd53 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -155,6 +155,7 @@ struct MissingFeatures { static bool setDLLStorageClass() { return false; } static bool openMP() { return false; } static bool emitCheckedInBoundsGEP() { return false; } + static bool sanitizeVLABound() { return false; } static bool preservedAccessIndexRegion() { return false; } static bool bitfields() { return false; } static bool typeChecks() { return false; } diff --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp index d7cbb4f64b2ea..ff75bcd8119c6 100644 --- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp @@ -25,11 +25,11 @@ using namespace clang::CIRGen; CIRGenFunction::AutoVarEmission CIRGenFunction::emitAutoVarAlloca(const VarDecl &d) { - QualType ty = d.getType(); + const QualType ty = d.getType(); if (ty.getAddressSpace() != LangAS::Default) cgm.errorNYI(d.getSourceRange(), "emitAutoVarAlloca: address space"); - mlir::Location loc = getLoc(d.getSourceRange()); + const mlir::Location loc = getLoc(d.getSourceRange()); CIRGenFunction::AutoVarEmission emission(d); emission.IsEscapingByRef = d.isEscapingByref(); @@ -37,22 +37,28 @@ CIRGenFunction::emitAutoVarAlloca(const VarDecl &d) { cgm.errorNYI(d.getSourceRange(), "emitAutoVarDecl: decl escaping by reference"); - CharUnits alignment = getContext().getDeclAlign(&d); + const CharUnits alignment = getContext().getDeclAlign(&d); // If the type is variably-modified, emit all the VLA sizes for it. if (ty->isVariablyModifiedType()) - cgm.errorNYI(d.getSourceRange(), "emitAutoVarDecl: variably modified type"); + emitVariablyModifiedType(ty); Address address = Address::invalid(); - if (!ty->isConstantSizeType()) - cgm.errorNYI(d.getSourceRange(), "emitAutoVarDecl: non-constant size type"); - - // A normal fixed sized variable becomes an alloca in the entry block, - mlir::Type allocaTy = convertTypeForMem(ty); - // Create the temp alloca and declare variable using it. - address = createTempAlloca(allocaTy, alignment, loc, d.getName(), - /*insertIntoFnEntryBlock=*/false); - declare(address.getPointer(), &d, ty, getLoc(d.getSourceRange()), alignment); + if (ty->isConstantSizeType()) { + // A normal fixed sized variable becomes an alloca in the entry block, + const mlir::Type allocaTy = convertTypeForMem(ty); + // Create the temp alloca and declare variable using it. + address = createTempAlloca(allocaTy, alignment, loc, d.getName(), + /*insertIntoFnEntryBlock=*/false); + declare(address.getPointer(), &d, ty, getLoc(d.getSourceRange()), + alignment); + } else { // not openmp nor constant sized type + const VlaSizePair vlaSize = getVLASize(ty); + const mlir::Type mlirType = convertTypeForMem(vlaSize.type); + Address allocaAddr = Address::invalid(); + address = createTempAlloca(mlirType, alignment, loc, "vla", vlaSize.numElts, + &allocaAddr, builder.saveInsertionPoint()); + } emission.Addr = address; setAddrOfLocalVar(&d, address); diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index 0a518c0fd935d..e72002e0ff005 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -88,9 +88,7 @@ Address CIRGenFunction::emitPointerWithAlignment(const Expr *expr, // Array-to-pointer decay. TODO(cir): BaseInfo and TBAAInfo. case CK_ArrayToPointerDecay: { - cgm.errorNYI(expr->getSourceRange(), - "emitPointerWithAlignment: array-to-pointer decay"); - return Address::invalid(); + return emitArrayToPointerDecay(ce->getSubExpr()); } case CK_UncheckedDerivedToBase: @@ -742,6 +740,41 @@ LValue CIRGenFunction::emitMemberExpr(const MemberExpr *e) { llvm_unreachable("Unhandled member declaration!"); } +Address CIRGenFunction::emitArrayToPointerDecay(const Expr *e) { + assert(e->getType()->isArrayType() && + "Array to pointer decay must have array source type!"); + + // Expressions of array type can't be bitfields or vector elements. + const LValue lv = emitLValue(e); + const Address addr = lv.getAddress(); + + // If the array type was an incomplete type, we need to make sure + // the decay ends up being the right type. + const cir::PointerType lvalueAddrTy = + mlir::dyn_cast<cir::PointerType>(addr.getPointer().getType()); + assert(lvalueAddrTy && "expected pointer"); + + if (e->getType()->isVariableArrayType()) + return addr; + + const cir::ArrayType pointeeTy = + mlir::dyn_cast<cir::ArrayType>(lvalueAddrTy.getPointee()); + assert(pointeeTy && "expected array"); + + const mlir::Type arrayTy = convertType(e->getType()); + assert(mlir::isa<cir::ArrayType>(arrayTy) && "expected array"); + assert(pointeeTy == arrayTy); + + const QualType elementType = + e->getType()->castAsArrayTypeUnsafe()->getElementType(); + + const mlir::Value ptr = cgm.getBuilder().maybeBuildArrayDecay( + cgm.getLoc(e->getSourceRange()), addr.getPointer(), + convertTypeForMem(elementType)); + + return Address(ptr, addr.getAlignment()); +} + LValue CIRGenFunction::emitBinaryOperatorLValue(const BinaryOperator *e) { // Comma expressions just emit their LHS then their RHS as an l-value. if (e->getOpcode() == BO_Comma) { @@ -1059,7 +1092,7 @@ mlir::Value CIRGenFunction::emitAlloca(StringRef name, mlir::Type ty, mlir::OpBuilder::InsertionGuard guard(builder); builder.restoreInsertionPoint(ip); addr = builder.createAlloca(loc, /*addr type*/ localVarPtrTy, - /*var type*/ ty, name, alignIntAttr); + /*var type*/ ty, name, alignIntAttr, arraySize); assert(!cir::MissingFeatures::astVarDeclInterface()); } return addr; @@ -1082,3 +1115,60 @@ Address CIRGenFunction::createTempAlloca(mlir::Type ty, CharUnits align, emitAlloca(name.str(), ty, loc, align, insertIntoFnEntryBlock); return Address(alloca, ty, align); } + +Address CIRGenFunction::createTempAlloca(mlir::Type ty, CharUnits align, + mlir::Location loc, const Twine &name, + mlir::Value arraySize, + Address *allocaAddr, + mlir::OpBuilder::InsertPoint ip) { + const Address alloca = + createTempAllocaWithoutCast(ty, align, loc, name, arraySize, ip); + + if (allocaAddr) + *allocaAddr = alloca; + + const mlir::Value v = alloca.getPointer(); + // Alloca always returns a pointer in alloca address space, which may + // be different from the type defined by the language. For example, + // in C++ the auto variables are in the default address space. Therefore + // cast alloca to the default address space when necessary. + // FIXME: Missing features addrspace + return Address(v, ty, align); +} + +/// This creates an alloca and inserts it into the entry block if \p ArraySize +/// is nullptr, otherwise inserts it at the current insertion point of the +/// builder. +cir::AllocaOp CIRGenFunction::createTempAlloca(mlir::Type ty, + mlir::Location loc, + const Twine &name, + mlir::Value arraySize, + bool insertIntoFnEntryBlock) { + return cast<cir::AllocaOp>(emitAlloca(name.str(), ty, loc, CharUnits(), + insertIntoFnEntryBlock, arraySize) + .getDefiningOp()); +} + +/// This creates an alloca and inserts it into the provided insertion point +cir::AllocaOp CIRGenFunction::createTempAlloca(mlir::Type ty, + mlir::Location loc, + const Twine &name, + mlir::OpBuilder::InsertPoint ip, + mlir::Value arraySize) { + assert(ip.isSet() && "Insertion point is not set"); + return cast<cir::AllocaOp>( + emitAlloca(name.str(), ty, loc, CharUnits(), ip, arraySize) + .getDefiningOp()); +} + +/// This creates a alloca and inserts it into the entry block of the +/// current region. +Address CIRGenFunction::createTempAllocaWithoutCast( + mlir::Type ty, CharUnits align, mlir::Location loc, const Twine &name, + mlir::Value arraySize, mlir::OpBuilder::InsertPoint ip) { + cir::AllocaOp alloca = ip.isSet() + ? createTempAlloca(ty, loc, name, ip, arraySize) + : createTempAlloca(ty, loc, name, arraySize); + alloca.setAlignmentAttr(cgm.getSize(align)); + return Address(alloca, ty, align); +} diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index 5412f9f602711..38d6cbdd2fb1e 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -567,4 +567,179 @@ void CIRGenFunction::emitNullInitialization(mlir::Location loc, Address destPtr, builder.createStore(loc, zeroValue, destPtr.getPointer()); } +CIRGenFunction::VlaSizePair CIRGenFunction::getVLASize(QualType type) { + const VariableArrayType *vla = + cgm.getASTContext().getAsVariableArrayType(type); + assert(vla && "type was not a variable array type!"); + return getVLASize(vla); +} + +CIRGenFunction::VlaSizePair +CIRGenFunction::getVLASize(const VariableArrayType *type) { + // The number of elements so far; always size_t. + mlir::Value numElements; + + QualType elementType; + do { + elementType = type->getElementType(); + mlir::Value vlaSize = vlaSizeMap[type->getSizeExpr()]; + assert(vlaSize && "no size for VLA!"); + assert(vlaSize.getType() == SizeTy); + + if (!numElements) { + numElements = vlaSize; + } else { + // It's undefined behavior if this wraps around, so mark it that way. + // FIXME: Teach -fsanitize=undefined to trap this. + numElements = + builder.createMul(numElements.getLoc(), numElements, vlaSize); + } + } while ((type = getContext().getAsVariableArrayType(elementType))); + + assert(numElements && "Undefined elements number"); + return {numElements, elementType}; +} + +// TODO(cir): most part of this function can be shared between CIRGen +// and traditional LLVM codegen +void CIRGenFunction::emitVariablyModifiedType(QualType type) { + assert(type->isVariablyModifiedType() && + "Must pass variably modified type to EmitVLASizes!"); + + // We're going to walk down into the type and look for VLA + // expressions. + do { + assert(type->isVariablyModifiedType()); + + const Type *ty = type.getTypePtr(); + switch (ty->getTypeClass()) { + case clang::Type::CountAttributed: + case clang::Type::PackIndexing: + case clang::Type::ArrayParameter: + case clang::Type::HLSLAttributedResource: + llvm_unreachable("NYI"); + +#define TYPE(Class, Base) +#define ABSTRACT_TYPE(Class, Base) +#define NON_CANONICAL_TYPE(Class, Base) +#define DEPENDENT_TYPE(Class, Base) case Type::Class: +#define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(Class, Base) +#include "clang/AST/TypeNodes.inc" + llvm_unreachable("unexpected dependent type!"); + + // These types are never variably-modified. + case Type::Builtin: + case Type::Complex: + case Type::Vector: + case Type::ExtVector: + case Type::ConstantMatrix: + case Type::Record: + case Type::Enum: + case Type::Using: + case Type::TemplateSpecialization: + case Type::ObjCTypeParam: + case Type::ObjCObject: + case Type::ObjCInterface: + case Type::ObjCObjectPointer: + case Type::BitInt: + llvm_unreachable("type class is never variably-modified!"); + + case Type::Elaborated: + type = cast<clang::ElaboratedType>(ty)->getNamedType(); + break; + + case Type::Adjusted: + type = cast<clang::AdjustedType>(ty)->getAdjustedType(); + break; + + case Type::Decayed: + type = cast<clang::DecayedType>(ty)->getPointeeType(); + break; + + case Type::Pointer: + type = cast<clang::PointerType>(ty)->getPointeeType(); + break; + + case Type::BlockPointer: + type = cast<clang::BlockPointerType>(ty)->getPointeeType(); + break; + + case Type::LValueReference: + case Type::RValueReference: + type = cast<clang::ReferenceType>(ty)->getPointeeType(); + break; + + case Type::MemberPointer: + type = cast<clang::MemberPointerType>(ty)->getPointeeType(); + break; + + case Type::ConstantArray: + case Type::IncompleteArray: + // Losing element qualification here is fine. + type = cast<clang::ArrayType>(ty)->getElementType(); + break; + + case Type::VariableArray: { + // Losing element qualification here is fine. + const VariableArrayType *vat = cast<clang::VariableArrayType>(ty); + + // Unknown size indication requires no size computation. + // Otherwise, evaluate and record it. + if (const Expr *sizeExpr = vat->getSizeExpr()) { + // It's possible that we might have emitted this already, + // e.g. with a typedef and a pointer to it. + mlir::Value &entry = vlaSizeMap[sizeExpr]; + if (!entry) { + mlir::Value size = emitScalarExpr(sizeExpr); + assert(!cir::MissingFeatures::sanitizeVLABound()); + + // Always zexting here would be wrong if it weren't + // undefined behavior to have a negative bound. + // FIXME: What about when size's type is larger than size_t? + entry = builder.createIntCast(size, SizeTy); + } + } + type = vat->getElementType(); + break; + } + + case Type::FunctionProto: + case Type::FunctionNoProto: + type = cast<clang::FunctionType>(ty)->getReturnType(); + break; + + case Type::Paren: + case Type::TypeOf: + case Type::UnaryTransform: + case Type::Attributed: + case Type::BTFTagAttributed: + case Type::SubstTemplateTypeParm: + case Type::MacroQualified: + // Keep walking after single level desugaring. + type = type.getSingleStepDesugaredType(getContext()); + break; + + case Type::Typedef: + case Type::Decltype: + case Type::Auto: + case Type::DeducedTemplateSpecialization: + // Stop walking: nothing to do. + return; + + case Type::TypeOfExpr: + // Stop walking: emit typeof expression. + emitIgnoredExpr(cast<clang::TypeOfExprType>(ty)->getUnderlyingExpr()); + return; + + case Type::Atomic: + type = cast<clang::AtomicType>(ty)->getValueType(); + break; + + case Type::Pipe: + type = cast<clang::PipeType>(ty)->getElementType(); + break; + } + } while (type->isVariablyModifiedType()); +} + } // namespace clang::CIRGen diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index f533d0ab53cd2..97bb01298cb49 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -101,6 +101,20 @@ class CIRGenFunction : public CIRGenTypeCache { mlir::MLIRContext &getMLIRContext() { return cgm.getMLIRContext(); } +public: + struct VlaSizePair { + mlir::Value numElts; + QualType type; + + VlaSizePair(mlir::Value numElts, QualType type) + : numElts(numElts), type(type) {} + }; + + llvm::DenseMap<const Expr *, mlir::Value> vlaSizeMap; + + VlaSizePair getVLASize(const VariableArrayType *type); + VlaSizePair getVLASize(QualType type); + private: /// Declare a variable in the current scope, return success if the variable /// wasn't declared yet. @@ -446,6 +460,8 @@ class CIRGenFunction : public CIRGenTypeCache { LValue emitArraySubscriptExpr(const clang::ArraySubscriptExpr *e); + Address emitArrayToPointerDecay(const Expr *e); + AutoVarEmission emitAutoVarAlloca(const clang::VarDecl &d); /// Emit code and set up symbol table for a variable declaration with auto, @@ -607,6 +623,8 @@ class CIRGenFunction : public CIRGenTypeCache { /// inside a function, including static vars etc. void emitVarDecl(const clang::VarDecl &d); + void emitVariablyModifiedType(QualType ty); + mlir::LogicalResult emitWhileStmt(const clang::WhileStmt &s); /// Given an assignment `*lhs = rhs`, emit a test that checks if \p rhs is @@ -621,6 +639,26 @@ class CIRGenFunction : public CIRGenTypeCache { Address createTempAlloca(mlir::Type ty, CharUnits align, mlir::Location loc, const Twine &name, bool insertIntoFnEntryBlock); + Address createTempAlloca(mlir::Type ty, CharUnits align, mlir::Location loc, + const Twine &name, mlir::Value arraySize = nullptr, + Address *alloca = nullptr, + mlir::OpBuilder::InsertPoint ip = {}); + + cir::AllocaOp createTempAlloca(mlir::Type ty, mlir::Location loc, + const Twine &name, + mlir::OpBuilder::InsertPoint ip, + mlir::Value arraySize); + + cir::AllocaOp createTempAlloca(mlir::Type ty, mlir::Location loc, + const Twine &name, mlir::Value arraySize, + bool insertIntoFnEntryBlock = false); + + Address createTempAllocaWithoutCast(mlir::Type ty, CharUnits align, + mlir::Location loc, + const Twine &name = "tmp", + mlir::Value arraySize = nullptr, + mlir::OpBuilder::InsertPoint ip = {}); + //===--------------------------------------------------------------------===// // OpenACC Emission //===--------------------------------------------------------------------===// diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index 3b13d495be5e3..8854136a0dc70 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -67,9 +67,15 @@ CIRGenModule::CIRGenModule(mlir::MLIRContext &mlirContext, // TODO(CIR): Should be updated once TypeSizeInfoAttr is upstreamed const unsigned sizeTypeSize = astContext.getTypeSize(astContext.getSignedSizeType()); + + SizeTy = + cir::IntType::get(&getMLIRContext(), sizeTypeSize, /*isSigned=*/false); + PtrDiffTy = cir::IntType::get(&getMLIRContext(), sizeTypeSize, /*isSigned=*/true); + UInt8PtrTy = builder.getPointerTo(UInt8Ty); + theModule->setAttr(cir::CIRDialect::getTripleAttrName(), builder.getStringAttr(getTriple().str())); } diff --git a/clang/lib/CIR/CodeGen/CIRGenTypeCache.h b/clang/lib/CIR/CodeGen/CIRGenTypeCache.h index a5b7f0c9579b4..fd6897bc9bfb7 100644 --- a/clang/lib/CIR/CodeGen/CIRGenTypeCache.h +++ b/clang/lib/CIR/CodeGen/CIRGenTypeCache.h @@ -49,8 +49,13 @@ struct CIRGenTypeCache { cir::FP80Type FP80Ty; cir::FP128Type FP128Ty; + /// intptr_t, size_t, and ptrdiff_t, which we assume are the same size. + mlir::Type SizeTy; + mlir::Type PtrDiffTy; + cir::PointerType UInt8PtrTy; + /// The size and alignment of a pointer into the generic address space. union { unsigned char PointerAlignInBytes; diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp index b11f8466607f8..2034cd54a4261 100644 --- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp @@ -392,6 +392,16 @@ mlir::Type CIRGenTypes::convertType(QualType type) { break; } + case Type::VariableArray: { + const VariableArrayType *vla = cast<VariableArrayType>(ty); + assert(vla->getIndexTypeCVRQualifiers() == 0 && + "FIXME: We only handle trivial array types so far!"); + // VLAs resolve to the innermost element type; this matches + // the return of alloca, and there isn't any obviously better choice. + resultType = convertTypeForMem(vla->getElementType()); + break; + } + case Type::ConstantArray: { const ConstantArrayType *arrTy = cast<ConstantArrayType>(ty); mlir::Type elemTy = convertTypeForMem(arrTy->getElementType()); diff --git a/clang/lib/CIR/Dialect/Transforms/FlattenCFG.cpp b/clang/lib/CIR/Dialect/Transforms/FlattenCFG.cpp index 72ccfa8d4e14e..5724ff2374d7a 100644 --- a/clang/lib/CIR/Dialect/Transforms/FlattenCFG.cpp +++ b/clang/lib/CIR/Dialect/Transforms/FlattenCFG.cpp @@ -254,10 +254,55 @@ class CIRLoopOpInterfaceFlattening } }; +class CIRVLAAllocaOpFlattening : public mlir::OpRewritePattern<cir::AllocaOp> { +public: + using OpRewritePattern<cir::AllocaOp>::OpRewritePattern; + + mlir::LogicalResult + matchAndRewrite(cir::AllocaOp op, + mlir::PatternRewriter &rewriter) const override { + if (!op.isDynamic()) { + return mlir::failure(); + } + + const mlir::Location loc = op->getLoc(); + + const Type u8Type = IntegerType::get( + getContext(), 8, IntegerType::SignednessSemantics::Unsigned); + + const PointerType allocaInt8PtrTy = + cir::PointerType::get(getContext(), u8Type); + + const PointerType allocaInt8PtrPtrTy = + cir::PointerType::get(getContext(), allocaInt8PtrTy); + + rewriter.setInsertionPointAfter(op); + + const IntegerAttr alignment = rewriter.getI64IntegerAttr(8); + const Value stackSavedAlloca = rewriter.create<cir::AllocaOp>( + loc, allocaInt8PtrPtrTy, allocaInt8PtrTy, "saved_stack", alignment); + + const Value stackSaveValue = + rewriter.create<cir::StackSaveOp>(loc, allocaInt8PtrTy); + + rewriter.create<cir::StoreOp>(loc, stackSaveValue, stackSavedAlloca); + + const Block::iterator blockEndIt = rewriter.getBlock()->end(); + const Block::iterator it = std::prev(std::prev(blockEndIt)); + rewriter.setInsertionPointAfter(&*it); + + const Value loadStackSaved = + rewriter.create<cir::LoadOp>(loc, allocaInt8PtrTy, stackSavedAlloca); + rewriter.create<cir::StackRestoreOp>(loc, loadStackSaved); + + return mlir::success(); + } +}; + void populateFlattenCFGPatterns(RewritePatternSet &patterns) { - patterns - .add<CIRIfFlattening, CIRLoopOpInterfaceFlattening, CIRScopeOpFlattening>( - patterns.getContext()); + patterns.add<CIRIfFlattening, CIRLoopOpInterfaceFlattening, + CIRScopeOpFlattening, CIRVLAAllocaOpFlattening>( + patterns.getContext()); } void CIRFlattenCFGPass::runOnOperation() { @@ -271,7 +316,7 @@ void CIRFlattenCFGPass::runOnOperation() { assert(!cir::MissingFeatures::switchOp()); assert(!cir::MissingFeatures::ternaryOp()); assert(!cir::MissingFeatures::tryOp()); - if (isa<IfOp, ScopeOp, LoopOpInterface>(op)) + if (isa<IfOp, ScopeOp, LoopOpInterface, AllocaOp>(op)) ops.push_back(op); }); diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 8bb27942d9646..bc6e8ce98755c 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -597,9 +597,12 @@ mlir::LogicalResult CIRToLLVMAllocaOpLowering::matchAndRewrite( cir::AllocaOp op, OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) const { assert(!cir::MissingFeatures::opAllocaDynAllocSize()); - mlir::Value size = rewriter.create<mlir::LLVM::ConstantOp>( - op.getLoc(), typeConverter->convertType(rewriter.getIndexType()), - rewriter.getIntegerAttr(rewriter.getIndexType(), 1)); + const mlir::Value size = + op.isDynamic() ? adaptor.getDynAllocSize() + : rewriter.create<mlir::LLVM::ConstantOp>( + op.getLoc(), + typeConverter->convertType(rewriter.getIndexType()), + rewriter.getIntegerAttr(rewriter.getIndexType(), 1)); mlir::Type elementTy = convertTypeForMemory(*getTypeConverter(), dataLayout, op.getAllocaType()); mlir::Type resultTy = convertTypeForMemory(*getTypeConverter(), dataLayout, diff --git a/clang/test/CIR/CodeGen/struct.c b/clang/test/CIR/CodeGen/struct.c index b78a2367bda3a..805f65f74ddb1 100644 --- a/clang/test/CIR/CodeGen/struct.c +++ b/clang/test/CIR/CodeGen/struct.c @@ -77,43 +77,36 @@ struct PackedAndPaddedS { #pragma pack(pop) -void f(void) { +struct IncompleteS* f(void) { struct IncompleteS *p; + return p; } -// CIR: cir.func @f() -// CIR-NEXT: cir.alloca !cir.ptr<!cir.record<struct "IncompleteS" incomplete>>, -// CIR-SAME: !cir.ptr<!cir.ptr<!cir.record<struct -// CIR-SAME: "IncompleteS" incomplete>>>, ["p"] -// CIR-NEXT: cir.return +// CIR: cir.func @f() +// CIR: cir.alloca !cir.ptr<!cir.record<struct "IncompleteS" incomplete>>, !cir.ptr<!cir.ptr<!cir.record<struct "IncompleteS" incomplete>>>, ["p"] -// LLVM: define void @f() -// LLVM-NEXT: %[[P:.*]] = alloca ptr, i64 1, align 8 -// LLVM-NEXT: ret void +// LLVM: define ptr @f() +// LLVM: %[[P:.*]] = alloca ptr, i64 1, align 8 -// OGCG: define{{.*}} void @f() +// OGCG: define{{.*}} ptr @f() // OGCG-NEXT: entry: -// OGCG-NEXT: %[[P:.*]] = alloca ptr, align 8 -// OGCG-NEXT: ret void +// OGCG: %[[P:.*]] = alloca ptr, align 8 -void f2(void) { +int f2(void) { struct CompleteS s; + return s.a; } // CIR: cir.func @f2() -// CIR-NEXT: cir.alloca !cir.record<struct "CompleteS" {!s32i, !s8i}>, +// CIR: cir.alloca !cir.record<struct "CompleteS" {!s32i, !s8i}>, // CIR-SAME: !cir.ptr<!cir.record<struct "CompleteS" {!s32i, !s8i}>>, -// CIR-SAME: ["s"] {alignment = 4 : i64} -// CIR-NEXT: cir.return +// CIR: ["s"] {alignment = 4 : i64} -// LLVM: define void @f2() -// LLVM-NEXT: %[[S:.*]] = alloca %struct.CompleteS, i64 1, align 4 -// LLVM-NEXT: ret void +// LLVM: define {{.*}} @f2() +// LLVM: %[[S:.*]] = alloca %struct.CompleteS, i64 1, align 4 -// OGCG: define{{.*}} void @f2() -// OGCG-NEXT: entry: -// OGCG-NEXT: %[[S:.*]] = alloca %struct.CompleteS, align 4 -// OGCG-NEXT: ret void +// OGCG: define{{.*}} {{.*}} @f2() +// OGCG: %[[S:.*]] = alloca %struct.CompleteS, align 4 char f3(int a) { cs.a = a; diff --git a/clang/test/CIR/CodeGen/struct.cpp b/clang/test/CIR/CodeGen/struct.cpp index 6197340a7d36b..28fe7643e3d2b 100644 --- a/clang/test/CIR/CodeGen/struct.cpp +++ b/clang/test/CIR/CodeGen/struct.cpp @@ -12,20 +12,17 @@ IncompleteS *p; // LLVM: @p = dso_local global ptr null // OGCG: @p = global ptr null, align 8 -void f(void) { +struct IncompleteS* f(void) { IncompleteS *p; + return p; } -// CIR: cir.func @f() -// CIR-NEXT: cir.alloca !cir.ptr<!cir.record<struct "IncompleteS" incomplete>>, -// CIR-SAME: !cir.ptr<!cir.ptr<!cir.record<struct "IncompleteS" incomplete>>>, ["p"] -// CIR-NEXT: cir.return +// CIR: cir.func @f() +// CIR: cir.alloca !cir.ptr<!cir.record<struct "IncompleteS" incomplete>>, !cir.ptr<!cir.ptr<!cir.record<struct "IncompleteS" incomplete>>>, ["p"] -// LLVM: define void @f() -// LLVM-NEXT: %[[P:.*]] = alloca ptr, i64 1, align 8 -// LLVM-NEXT: ret void +// LLVM: define ptr @f() +// LLVM: %[[P:.*]] = alloca ptr, i64 1, align 8 -// OGCG: define{{.*}} void @_Z1fv() +// OGCG: define{{.*}} ptr @_Z1fv() // OGCG-NEXT: entry: -// OGCG-NEXT: %[[P:.*]] = alloca ptr, align 8 -// OGCG-NEXT: ret void +// OGCG: %[[P:.*]] = alloca ptr, align 8 diff --git a/clang/test/CIR/CodeGen/typedef.c b/clang/test/CIR/CodeGen/typedef.c index 17fce13abf38a..2bee74dc86ba6 100644 --- a/clang/test/CIR/CodeGen/typedef.c +++ b/clang/test/CIR/CodeGen/typedef.c @@ -5,23 +5,21 @@ // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -emit-llvm %s -o %t.ll // RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG -void local_typedef(void) { +int local_typedef(void) { typedef struct {int a;} Struct; Struct s; + return s.a; } // CIR: cir.func @local_typedef() // CIR: cir.alloca !cir.record<struct "Struct" {!s32i}>, // CIR-SAME: !cir.ptr<!cir.record<struct "Struct" {!s32i}>>, ["s"] // CIR-SAME: {alignment = 4 : i64} -// CIR: cir.return // LLVM: %struct.Struct = type { i32 } -// LLVM: define void @local_typedef() +// LLVM: define {{.*}} @local_typedef() // LLVM: alloca %struct.Struct, i64 1, align 4 -// LLVM: ret void // OGCG: %struct.Struct = type { i32 } -// OGCG: define{{.*}} void @local_typedef() +// OGCG: define{{.*}} {{.*}} @local_typedef() // OGCG: alloca %struct.Struct, align 4 -// OGCG: ret void diff --git a/clang/test/CIR/CodeGen/union.c b/clang/test/CIR/CodeGen/union.c index 075d0d2315508..ce89ac733adbc 100644 --- a/clang/test/CIR/CodeGen/union.c +++ b/clang/test/CIR/CodeGen/union.c @@ -11,20 +11,16 @@ union IncompleteU *p; // LLVM: @p = dso_local global ptr null // OGCG: @p = global ptr null, align 8 -void f(void) { +union IncompleteU* f(void) { union IncompleteU *p; + return p; } -// CIR: cir.func @f() -// CIR-NEXT: cir.alloca !cir.ptr<!cir.record<union "IncompleteU" incomplete>>, -// CIR-SAME: !cir.ptr<!cir.ptr<!cir.record<union "IncompleteU" incomplete>>>, ["p"] -// CIR-NEXT: cir.return +// CIR: cir.func @f() -> !cir.ptr<!cir.record<union "IncompleteU" incomplete>> +// CIR: cir.alloca !cir.ptr<!cir.record<union "IncompleteU" incomplete>>, !cir.ptr<!cir.ptr<!cir.record<union "IncompleteU" incomplete>>>, ["p"] -// LLVM: define void @f() -// LLVM-NEXT: %[[P:.*]] = alloca ptr, i64 1, align 8 -// LLVM-NEXT: ret void +// LLVM: define ptr @f() +// LLVM: %[[P:.*]] = alloca ptr, i64 1, align 8 -// OGCG: define{{.*}} void @f() -// OGCG-NEXT: entry: -// OGCG-NEXT: %[[P:.*]] = alloca ptr, align 8 -// OGCG-NEXT: ret void +// OGCG: define{{.*}} ptr @f() +// OGCG: %[[P:.*]] = alloca ptr, align 8 diff --git a/clang/test/CIR/CodeGen/vla.cpp b/clang/test/CIR/CodeGen/vla.cpp new file mode 100644 index 0000000000000..80b3dcbac9dfc --- /dev/null +++ b/clang/test/CIR/CodeGen/vla.cpp @@ -0,0 +1,71 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG + +void func(int len) { + int arr[len]; + int e = arr[0]; +} + +// CIR: %0 = cir.alloca !s32i, !cir.ptr<!s32i>, ["len", init] +// CIR: cir.store %arg0, %0 : !s32i, !cir.ptr<!s32i> +// CIR: %1 = cir.load %0 : !cir.ptr<!s32i>, !s32i +// CIR: %2 = cir.cast(integral, %1 : !s32i), !u64i +// CIR: %3 = cir.alloca !s32i, !cir.ptr<!s32i>, %2 : !u64i, ["vla"] +// CIR: %4 = cir.alloca !s32i, !cir.ptr<!s32i>, ["e", init] +// CIR: %5 = cir.const #cir.int<0> : !s32i +// CIR: %6 = cir.ptr_stride(%3 : !cir.ptr<!s32i>, %5 : !s32i), !cir.ptr<!s32i> +// CIR: %7 = cir.load %6 : !cir.ptr<!s32i>, !s32i +// CIR: cir.store %7, %4 : !s32i, !cir.ptr<!s32i> + +// LLVM: %2 = alloca i32, i64 1 +// LLVM: store i32 %0, ptr %2 +// LLVM: %3 = load i32, ptr %2 +// LLVM: %4 = sext i32 %3 to i64 +// LLVM: %5 = alloca i32, i64 %4 +// LLVM: %6 = alloca ptr, i64 1 +// LLVM: %7 = call ptr @llvm.stacksave.p0() +// LLVM: store ptr %7, ptr %6 +// LLVM: %8 = alloca i32, i64 1 +// LLVM: %9 = getelementptr i32, ptr %5, i64 0 +// LLVM: %10 = load i32, ptr %9 +// LLVM: store i32 %10, ptr %8 +// LLVM: %11 = load ptr, ptr %6 +// LLVM: call void @llvm.stackrestore.p0(ptr %11) + +// OGCG: %len.addr = alloca i32 +// OGCG: %saved_stack = alloca ptr +// OGCG: %__vla_expr0 = alloca i64 +// OGCG: %e = alloca i32 +// OGCG: store i32 %len, ptr %len.addr +// OGCG: %0 = load i32, ptr %len.addr +// OGCG: %1 = zext i32 %0 to i64 +// OGCG: %2 = call ptr @llvm.stacksave.p0() +// OGCG: store ptr %2, ptr %saved_stack +// OGCG: %vla = alloca i32, i64 %1 +// OGCG: store i64 %1, ptr %__vla_expr0 +// OGCG: %arrayidx = getelementptr inbounds i32, ptr %vla, i64 0 +// OGCG: %3 = load i32, ptr %arrayidx +// OGCG: store i32 %3, ptr %e +// OGCG: %4 = load ptr, ptr %saved_stack +// OGCG: call void @llvm.stackrestore.p0(ptr %4) + +void func2(short width, int data[][width]) {} + +// CIR: %0 = cir.alloca !s16i, !cir.ptr<!s16i>, ["width", init] +// CIR: %1 = cir.alloca !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>>, ["data", init] +// CIR: cir.store %arg0, %0 : !s16i, !cir.ptr<!s16i> +// CIR: cir.store %arg1, %1 : !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>> + +// LLVM: %3 = alloca i16, i64 1 +// LLVM: %4 = alloca ptr, i64 1 +// LLVM: store i16 %0, ptr %3 +// LLVM: store ptr %1, ptr %4 + +// OGCG: %width.addr = alloca i16 +// OGCG: %data.addr = alloca ptr +// OGCG: store i16 %width, ptr %width.addr +// OGCG: store ptr %data, ptr %data.addr diff --git a/clang/test/CIR/Lowering/local-vars.cpp b/clang/test/CIR/Lowering/local-vars.cpp index bd47ed14065c6..ea3b702e50968 100644 --- a/clang/test/CIR/Lowering/local-vars.cpp +++ b/clang/test/CIR/Lowering/local-vars.cpp @@ -13,11 +13,16 @@ void test() { const double cd = 4.0; const bool cb1 = true; const bool cb2 = false; + + + // TODO: The following variables need to be used for example as function return value + /* int uii; long uil; float uif; double uid; bool uib; + */ } // Note: The alignment of i64 stores below is wrong. That should be fixed @@ -36,11 +41,14 @@ void test() { // CHECK: %[[CD_PTR:.*]] = alloca double, i64 1, align 8 // CHECK: %[[CB1_PTR:.*]] = alloca i8, i64 1, align 1 // CHECK: %[[CB2_PTR:.*]] = alloca i8, i64 1, align 1 -// CHECK: %[[UII_PTR:.*]] = alloca i32, i64 1, align 4 -// CHECK: %[[UIL_PTR:.*]] = alloca i64, i64 1, align 8 -// CHECK: %[[UIF_PTR:.*]] = alloca float, i64 1, align 4 -// CHECK: %[[UID_PTR:.*]] = alloca double, i64 1, align 8 -// CHECK: %[[UIB_PTR:.*]] = alloca i8, i64 1, align 1 + +// TODO: those alloca are removed in DCE +// CHECK %[[UII_PTR:.*]] = alloca i32, i64 1, align 4 +// CHECK %[[UIL_PTR:.*]] = alloca i64, i64 1, align 8 +// CHECK %[[UIF_PTR:.*]] = alloca float, i64 1, align 4 +// CHECK %[[UID_PTR:.*]] = alloca double, i64 1, align 8 +// CHECK %[[UIB_PTR:.*]] = alloca i8, i64 1, align 1 + // CHECK: store i32 1, ptr %[[I_PTR]], align 4 // CHECK: store i64 2, ptr %[[L_PTR]], align 4 // CHECK: store float 3.000000e+00, ptr %[[F_PTR]], align 4 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits