[clang] [CIR] Upstream support for Variable length Array (PR #137233)
Amr Hesham via cfe-commits
cfe-commits at lists.llvm.org
Thu Apr 24 11:48:04 PDT 2025
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 <amr96 at 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
More information about the cfe-commits
mailing list