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