Skip to content

[CIR] Upstream global initialization for ArrayType #131657

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Mar 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,48 @@ def FPAttr : CIR_Attr<"FP", "fp", [TypedAttrInterface]> {
}];
}


//===----------------------------------------------------------------------===//
// ConstArrayAttr
//===----------------------------------------------------------------------===//

def ConstArrayAttr : CIR_Attr<"ConstArray", "const_array", [TypedAttrInterface]> {
let summary = "A constant array from ArrayAttr or StringRefAttr";
let description = [{
An CIR array attribute is an array of literals of the specified attr types.
}];

let parameters = (ins AttributeSelfTypeParameter<"">:$type,
"mlir::Attribute":$elts,
"int":$trailingZerosNum);

// Define a custom builder for the type; that removes the need to pass
// in an MLIRContext instance, as it can be infered from the `type`.
let builders = [
AttrBuilderWithInferredContext<(ins "cir::ArrayType":$type,
"mlir::Attribute":$elts), [{
int zeros = 0;
auto typeSize = mlir::cast<cir::ArrayType>(type).getSize();
if (auto str = mlir::dyn_cast<mlir::StringAttr>(elts))
zeros = typeSize - str.size();
else
zeros = typeSize - mlir::cast<mlir::ArrayAttr>(elts).size();

return $_get(type.getContext(), type, elts, zeros);
}]>
];

// Printing and parsing available in CIRDialect.cpp
let hasCustomAssemblyFormat = 1;

// Enable verifier.
let genVerifyDecl = 1;

let extraClassDeclaration = [{
bool hasTrailingZeros() const { return getTrailingZerosNum() != 0; };
}];
}

//===----------------------------------------------------------------------===//
// ConstPtrAttr
//===----------------------------------------------------------------------===//
Expand Down
36 changes: 36 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,42 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
return false;
}

// Return true if the value is a null constant such as null pointer, (+0.0)
// for floating-point or zero initializer
bool isNullValue(mlir::Attribute attr) const {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be useful to have a comment here explaining the intent of this function. In particular, some clarification is necessary as to what it means for an FPAtt. The function returns false for -0.0, which means it is not simply testing for equality with zero.

if (mlir::isa<cir::ZeroAttr>(attr))
return true;

if (const auto ptrVal = mlir::dyn_cast<cir::ConstPtrAttr>(attr))
return ptrVal.isNullValue();

if (const auto intVal = mlir::dyn_cast<cir::IntAttr>(attr))
return intVal.isNullValue();

if (const auto boolVal = mlir::dyn_cast<cir::BoolAttr>(attr))
return !boolVal.getValue();

if (auto fpAttr = mlir::dyn_cast<cir::FPAttr>(attr)) {
auto fpVal = fpAttr.getValue();
bool ignored;
llvm::APFloat fv(+0.0);
fv.convert(fpVal.getSemantics(), llvm::APFloat::rmNearestTiesToEven,
&ignored);
return fv.bitwiseIsEqual(fpVal);
}

if (const auto arrayVal = mlir::dyn_cast<cir::ConstArrayAttr>(attr)) {
if (mlir::isa<mlir::StringAttr>(arrayVal.getElts()))
return false;
for (const auto elt : mlir::cast<mlir::ArrayAttr>(arrayVal.getElts())) {
if (!isNullValue(elt))
return false;
}
return true;
}
return false;
}

bool isInt(mlir::Type i) { return mlir::isa<cir::IntType>(i); }

// Creates constant nullptr for pointer type ty.
Expand Down
6 changes: 4 additions & 2 deletions clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@

#include "CIRGenFunction.h"
#include "CIRGenModule.h"
#include "llvm/ADT/SmallVector.h"

namespace clang::CIRGen {

Expand All @@ -41,6 +40,9 @@ class ConstantEmitter {
/// block addresses or PredefinedExprs.
ConstantEmitter(CIRGenFunction &cgf) : cgm(cgf.cgm), cgf(&cgf) {}

ConstantEmitter(CIRGenModule &cgm, CIRGenFunction *cgf = nullptr)
: cgm(cgm), cgf(cgf) {}

ConstantEmitter(const ConstantEmitter &other) = delete;
ConstantEmitter &operator=(const ConstantEmitter &other) = delete;

Expand All @@ -66,7 +68,7 @@ class ConstantEmitter {
mlir::Attribute emitAbstract(SourceLocation loc, const APValue &value,
QualType t);

mlir::Attribute tryEmitConstantExpr(const ConstantExpr *CE);
mlir::Attribute tryEmitConstantExpr(const ConstantExpr *ce);

// These are private helper routines of the constant emitter that
// can't actually be private because things are split out into helper
Expand Down
1 change: 0 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,6 @@ void CIRGenFunction::emitScalarInit(const Expr *init, mlir::Location loc,
}
assert(!cir::MissingFeatures::emitNullabilityCheck());
emitStoreThroughLValue(RValue::get(value), lvalue, true);
return;
}

void CIRGenFunction::emitExprAsInit(const Expr *init, const ValueDecl *d,
Expand Down
125 changes: 115 additions & 10 deletions clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,13 +158,63 @@ class ConstExprEmitter

// TODO(cir): this can be shared with LLVM's codegen
static QualType getNonMemoryType(CIRGenModule &cgm, QualType type) {
if (auto at = type->getAs<AtomicType>()) {
if (const auto *at = type->getAs<AtomicType>()) {
return cgm.getASTContext().getQualifiedType(at->getValueType(),
type.getQualifiers());
}
return type;
}

static mlir::Attribute
emitArrayConstant(CIRGenModule &cgm, mlir::Type desiredType,
mlir::Type commonElementType, unsigned arrayBound,
SmallVectorImpl<mlir::TypedAttr> &elements,
mlir::TypedAttr filler) {
const CIRGenBuilderTy &builder = cgm.getBuilder();

unsigned nonzeroLength = arrayBound;
if (elements.size() < nonzeroLength && builder.isNullValue(filler))
nonzeroLength = elements.size();

if (nonzeroLength == elements.size()) {
while (nonzeroLength > 0 &&
builder.isNullValue(elements[nonzeroLength - 1]))
--nonzeroLength;
}

if (nonzeroLength == 0)
return cir::ZeroAttr::get(builder.getContext(), desiredType);

const unsigned trailingZeroes = arrayBound - nonzeroLength;

// Add a zeroinitializer array filler if we have lots of trailing zeroes.
if (trailingZeroes >= 8) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You've dropped a comment from the incubator code here that explains the purpose of the comparison with 8 -- "if we have lots of trailing zeroes". Without the comment, 8 is purely a magic number with no context.

assert(elements.size() >= nonzeroLength &&
"missing initializer for non-zero element");
} else if (elements.size() != arrayBound) {
elements.resize(arrayBound, filler);

if (filler.getType() != commonElementType)
commonElementType = {};
}

if (commonElementType) {
SmallVector<mlir::Attribute, 4> eles;
eles.reserve(elements.size());

for (const auto &element : elements)
eles.push_back(element);

return cir::ConstArrayAttr::get(
cir::ArrayType::get(builder.getContext(), commonElementType,
arrayBound),
mlir::ArrayAttr::get(builder.getContext(), eles));
}

cgm.errorNYI("array with different type elements");
return {};
}

//===----------------------------------------------------------------------===//
// ConstantEmitter
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -271,16 +321,57 @@ mlir::Attribute ConstantEmitter::tryEmitPrivate(const APValue &value,
cgm.getASTContext().getTargetInfo().useFP16ConversionIntrinsics()) {
cgm.errorNYI("ConstExprEmitter::tryEmitPrivate half");
return {};
} else {
mlir::Type ty = cgm.convertType(destType);
assert(mlir::isa<cir::CIRFPTypeInterface>(ty) &&
"expected floating-point type");
return cgm.getBuilder().getAttr<cir::FPAttr>(ty, init);
}

mlir::Type ty = cgm.convertType(destType);
assert(mlir::isa<cir::CIRFPTypeInterface>(ty) &&
"expected floating-point type");
return cgm.getBuilder().getAttr<cir::FPAttr>(ty, init);
}
case APValue::Array: {
cgm.errorNYI("ConstExprEmitter::tryEmitPrivate array");
return {};
const ArrayType *arrayTy = cgm.getASTContext().getAsArrayType(destType);
const QualType arrayElementTy = arrayTy->getElementType();
const unsigned numElements = value.getArraySize();
const unsigned numInitElts = value.getArrayInitializedElts();

mlir::Attribute filler;
if (value.hasArrayFiller()) {
filler = tryEmitPrivate(value.getArrayFiller(), arrayElementTy);
if (!filler)
return {};
}

SmallVector<mlir::TypedAttr, 16> elements;
if (filler && builder.isNullValue(filler))
elements.reserve(numInitElts + 1);
else
elements.reserve(numInitElts);

mlir::Type commonElementType;
for (unsigned i = 0; i < numInitElts; ++i) {
const APValue &arrayElement = value.getArrayInitializedElt(i);
const mlir::Attribute element =
tryEmitPrivateForMemory(arrayElement, arrayElementTy);
if (!element)
return {};

const mlir::TypedAttr elementTyped = mlir::cast<mlir::TypedAttr>(element);
if (i == 0)
commonElementType = elementTyped.getType();
else if (elementTyped.getType() != commonElementType) {
commonElementType = {};
}

elements.push_back(elementTyped);
}

mlir::TypedAttr typedFiller = llvm::cast_or_null<mlir::TypedAttr>(filler);
if (filler && !typedFiller)
cgm.errorNYI("array filler should always be typed");

mlir::Type desiredType = cgm.convertType(destType);
return emitArrayConstant(cgm, desiredType, commonElementType, numElements,
elements, typedFiller);
}
case APValue::Vector: {
cgm.errorNYI("ConstExprEmitter::tryEmitPrivate vector");
Expand All @@ -290,9 +381,23 @@ mlir::Attribute ConstantEmitter::tryEmitPrivate(const APValue &value,
cgm.errorNYI("ConstExprEmitter::tryEmitPrivate member pointer");
return {};
}
case APValue::LValue:
cgm.errorNYI("ConstExprEmitter::tryEmitPrivate lvalue");
case APValue::LValue: {

if (value.getLValueBase()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're probably going to want to leave ConstantLValueEmitter::tryEmit() as a separate function.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, i think later we need to upstreaming ConstantLValueEmitter and also implement the visitors

cgm.errorNYI("non-null pointer initialization");
} else {

mlir::Type desiredType = cgm.convertType(destType);
if (const cir::PointerType ptrType =
mlir::dyn_cast<cir::PointerType>(desiredType)) {
return builder.getConstPtrAttr(ptrType,
value.getLValueOffset().getQuantity());
} else {
llvm_unreachable("non-pointer variable initialized with a pointer");
}
}
return {};
}
case APValue::Struct:
case APValue::Union:
cgm.errorNYI("ConstExprEmitter::tryEmitPrivate struct or union");
Expand Down
38 changes: 5 additions & 33 deletions clang/lib/CIR/CodeGen/CIRGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
//===----------------------------------------------------------------------===//

#include "CIRGenModule.h"
#include "CIRGenConstantEmitter.h"
#include "CIRGenFunction.h"

#include "clang/AST/ASTContext.h"
Expand Down Expand Up @@ -127,7 +128,8 @@ void CIRGenModule::emitGlobalFunctionDefinition(clang::GlobalDecl gd,

void CIRGenModule::emitGlobalVarDefinition(const clang::VarDecl *vd,
bool isTentative) {
mlir::Type type = convertType(vd->getType());
const QualType astTy = vd->getType();
const mlir::Type type = convertType(vd->getType());
if (clang::IdentifierInfo *identifier = vd->getIdentifier()) {
auto varOp = builder.create<cir::GlobalOp>(getLoc(vd->getSourceRange()),
identifier->getName(), type);
Expand All @@ -140,38 +142,8 @@ void CIRGenModule::emitGlobalVarDefinition(const clang::VarDecl *vd,
if (initExpr) {
mlir::Attribute initializer;
if (APValue *value = initDecl->evaluateValue()) {
switch (value->getKind()) {
case APValue::Int: {
if (mlir::isa<cir::BoolType>(type))
initializer =
builder.getCIRBoolAttr(value->getInt().getZExtValue());
else
initializer = builder.getAttr<cir::IntAttr>(type, value->getInt());
break;
}
case APValue::Float: {
initializer = builder.getAttr<cir::FPAttr>(type, value->getFloat());
break;
}
case APValue::LValue: {
if (value->getLValueBase()) {
errorNYI(initExpr->getSourceRange(),
"non-null pointer initialization");
} else {
if (auto ptrType = mlir::dyn_cast<cir::PointerType>(type)) {
initializer = builder.getConstPtrAttr(
ptrType, value->getLValueOffset().getQuantity());
} else {
llvm_unreachable(
"non-pointer variable initialized with a pointer");
}
}
break;
}
default:
errorNYI(initExpr->getSourceRange(), "unsupported initializer kind");
break;
}
ConstantEmitter emitter(*this);
initializer = emitter.tryEmitPrivateForMemory(*value, astTy);
} else {
errorNYI(initExpr->getSourceRange(), "non-constant initializer");
}
Expand Down
Loading
Loading