Skip to content

[CIR] Refactor global variable emission and initialization #138222

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 4 commits into from
May 2, 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
7 changes: 7 additions & 0 deletions clang/include/clang/CIR/MissingFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ struct MissingFeatures {
static bool opGlobalConstant() { return false; }
static bool opGlobalAlignment() { return false; }
static bool opGlobalWeakRef() { return false; }
static bool opGlobalLinkage() { return false; }
static bool opGlobalSetVisitibility() { return false; }

static bool supportIFuncAttr() { return false; }
static bool supportVisibility() { return false; }
Expand Down Expand Up @@ -163,6 +165,11 @@ struct MissingFeatures {
static bool setDSOLocal() { return false; }
static bool foldCaseStmt() { return false; }
static bool constantFoldSwitchStatement() { return false; }
static bool cudaSupport() { return false; }
static bool maybeHandleStaticInExternC() { return false; }
static bool constEmitterArrayILE() { return false; }
static bool constEmitterVectorILE() { return false; }
static bool needsGlobalCtorDtor() { return false; }

// Missing types
static bool dataMemberType() { return false; }
Expand Down
45 changes: 41 additions & 4 deletions clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,19 @@ class ConstantEmitter {
private:
bool abstract = false;

#ifndef NDEBUG
// Variables used for asserting state consistency.

/// Whether non-abstract components of the emitter have been initialized.
bool initializedNonAbstract = false;
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this just for sanity checking? Should this be wrapped in a #ifndef NDEBUG?

Same for finalized and failed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, these are just for debugging. Wrapping them in NDEBUG makes sense.


/// Whether the emitter has been finalized.
bool finalized = false;

/// Whether the constant-emission failed.
bool failed = false;
#endif // NDEBUG

/// Whether we're in a constant context.
bool inConstantContext = false;

Expand All @@ -46,6 +59,14 @@ class ConstantEmitter {
ConstantEmitter(const ConstantEmitter &other) = delete;
ConstantEmitter &operator=(const ConstantEmitter &other) = delete;

~ConstantEmitter();

/// Try to emit the initializer of the given declaration as an abstract
/// constant. If this succeeds, the emission must be finalized.
mlir::Attribute tryEmitForInitializer(const VarDecl &d);

void finalize(cir::GlobalOp gv);

// All of the "abstract" emission methods below permit the emission to
// be immediately discarded without finalizing anything. Therefore, they
// must also promise not to do anything that will, in the future, require
Expand All @@ -61,6 +82,10 @@ class ConstantEmitter {
// reference to its current location.
mlir::Attribute emitForMemory(mlir::Attribute c, QualType t);

/// Try to emit the initializer of the given declaration as an abstract
/// constant.
mlir::Attribute tryEmitAbstractForInitializer(const VarDecl &d);

/// Emit the result of the given expression as an abstract constant,
/// asserting that it succeeded. This is only safe to do when the
/// expression is known to be a constant expression with either a fairly
Expand All @@ -79,11 +104,23 @@ class ConstantEmitter {
mlir::Attribute tryEmitPrivate(const APValue &value, QualType destType);
mlir::Attribute tryEmitPrivateForMemory(const APValue &value, QualType t);

/// Try to emit the initializer of the given declaration as an abstract
/// constant.
mlir::Attribute tryEmitAbstractForInitializer(const VarDecl &d);

private:
#ifndef NDEBUG
void initializeNonAbstract() {
assert(!initializedNonAbstract);
initializedNonAbstract = true;
assert(!cir::MissingFeatures::addressSpace());
}
mlir::Attribute markIfFailed(mlir::Attribute init) {
if (!init)
failed = true;
return init;
}
#else
void initializeNonAbstract() {}
mlir::Attribute markIfFailed(mlir::Attribute init) { return init; }
#endif // NDEBUG

class AbstractStateRAII {
ConstantEmitter &emitter;
bool oldValue;
Expand Down
140 changes: 137 additions & 3 deletions clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,100 @@ class ConstExprEmitter
}

mlir::Attribute VisitCastExpr(CastExpr *e, QualType destType) {
cgm.errorNYI(e->getBeginLoc(), "ConstExprEmitter::VisitCastExpr");
return {};
if (const auto *ece = dyn_cast<ExplicitCastExpr>(e))
cgm.errorNYI(e->getBeginLoc(),
"ConstExprEmitter::VisitCastExpr explicit cast");
Expr *subExpr = e->getSubExpr();

switch (e->getCastKind()) {
case CK_ToUnion:
case CK_AddressSpaceConversion:
case CK_ReinterpretMemberPointer:
case CK_DerivedToBaseMemberPointer:
case CK_BaseToDerivedMemberPointer:
cgm.errorNYI(e->getBeginLoc(), "ConstExprEmitter::VisitCastExpr");
return {};

case CK_LValueToRValue:
case CK_AtomicToNonAtomic:
case CK_NonAtomicToAtomic:
case CK_NoOp:
case CK_ConstructorConversion:
return Visit(subExpr, destType);

case CK_IntToOCLSampler:
llvm_unreachable("global sampler variables are not generated");

case CK_Dependent:
llvm_unreachable("saw dependent cast!");

case CK_BuiltinFnToFnPtr:
llvm_unreachable("builtin functions are handled elsewhere");

// These will never be supported.
case CK_ObjCObjectLValueCast:
case CK_ARCProduceObject:
case CK_ARCConsumeObject:
case CK_ARCReclaimReturnedObject:
case CK_ARCExtendBlockObject:
case CK_CopyAndAutoreleaseBlockObject:
return {};

// These don't need to be handled here because Evaluate knows how to
// evaluate them in the cases where they can be folded.
case CK_BitCast:
case CK_ToVoid:
case CK_Dynamic:
case CK_LValueBitCast:
case CK_LValueToRValueBitCast:
case CK_NullToMemberPointer:
case CK_UserDefinedConversion:
case CK_CPointerToObjCPointerCast:
case CK_BlockPointerToObjCPointerCast:
case CK_AnyPointerToBlockPointerCast:
case CK_ArrayToPointerDecay:
case CK_FunctionToPointerDecay:
case CK_BaseToDerived:
case CK_DerivedToBase:
case CK_UncheckedDerivedToBase:
case CK_MemberPointerToBoolean:
case CK_VectorSplat:
case CK_FloatingRealToComplex:
case CK_FloatingComplexToReal:
case CK_FloatingComplexToBoolean:
case CK_FloatingComplexCast:
case CK_FloatingComplexToIntegralComplex:
case CK_IntegralRealToComplex:
case CK_IntegralComplexToReal:
case CK_IntegralComplexToBoolean:
case CK_IntegralComplexCast:
case CK_IntegralComplexToFloatingComplex:
case CK_PointerToIntegral:
case CK_PointerToBoolean:
case CK_NullToPointer:
case CK_IntegralCast:
case CK_BooleanToSignedIntegral:
case CK_IntegralToPointer:
case CK_IntegralToBoolean:
case CK_IntegralToFloating:
case CK_FloatingToIntegral:
case CK_FloatingToBoolean:
case CK_FloatingCast:
case CK_FloatingToFixedPoint:
case CK_FixedPointToFloating:
case CK_FixedPointCast:
case CK_FixedPointToBoolean:
case CK_FixedPointToIntegral:
case CK_IntegralToFixedPoint:
case CK_ZeroToOCLOpaqueType:
case CK_MatrixCast:
case CK_HLSLArrayRValue:
case CK_HLSLVectorTruncation:
case CK_HLSLElementwiseCast:
case CK_HLSLAggregateSplatCast:
return {};
}
llvm_unreachable("Invalid CastKind");
}

mlir::Attribute VisitCXXDefaultInitExpr(CXXDefaultInitExpr *die, QualType t) {
Expand All @@ -118,7 +210,28 @@ class ConstExprEmitter
}

mlir::Attribute VisitInitListExpr(InitListExpr *ile, QualType t) {
cgm.errorNYI(ile->getBeginLoc(), "ConstExprEmitter::VisitInitListExpr");
if (ile->isTransparent())
return Visit(ile->getInit(0), t);

if (ile->getType()->isArrayType()) {
// If we return null here, the non-constant initializer will take care of
// it, but we would prefer to handle it here.
assert(!cir::MissingFeatures::constEmitterArrayILE());
return {};
}

if (ile->getType()->isRecordType()) {
cgm.errorNYI(ile->getBeginLoc(), "ConstExprEmitter: record ILE");
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd add an explicit return {}; here just from a defensive programming standpoint.

Should this also have its own MissingFeature assert?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've been using MissingFeature for places where we need to do something but either can't check for the conditions where we need to do it yet (because the check uses constructs that haven't been upstreamed yet) or we don't want to issue an error (as in the other two cases here). So, I don't think it would add anything here.

I'll add the explicit return.

return {};
}

if (ile->getType()->isVectorType()) {
// If we return null here, the non-constant initializer will take care of
// it, but we would prefer to handle it here.
assert(!cir::MissingFeatures::constEmitterVectorILE());
return {};
}

return {};
}

Expand Down Expand Up @@ -218,12 +331,33 @@ emitArrayConstant(CIRGenModule &cgm, mlir::Type desiredType,
// ConstantEmitter
//===----------------------------------------------------------------------===//

mlir::Attribute ConstantEmitter::tryEmitForInitializer(const VarDecl &d) {
initializeNonAbstract();
return markIfFailed(tryEmitPrivateForVarInit(d));
}

void ConstantEmitter::finalize(cir::GlobalOp gv) {
assert(initializedNonAbstract &&
"finalizing emitter that was used for abstract emission?");
assert(!finalized && "finalizing emitter multiple times");
assert(!gv.isDeclaration());
#ifndef NDEBUG
// Note that we might also be Failed.
finalized = true;
#endif // NDEBUG
}

mlir::Attribute
ConstantEmitter::tryEmitAbstractForInitializer(const VarDecl &d) {
AbstractStateRAII state(*this, true);
return tryEmitPrivateForVarInit(d);
}

ConstantEmitter::~ConstantEmitter() {
assert((!initializedNonAbstract || finalized || failed) &&
"not finalized after being initialized for non-abstract emission");
}

mlir::Attribute ConstantEmitter::tryEmitPrivateForVarInit(const VarDecl &d) {
// Make a quick check if variable can be default NULL initialized
// and avoid going through rest of code which may do, for c++11,
Expand Down
Loading