diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 4d4951aa0e126..bbc1e05a94981 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -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; } @@ -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; } diff --git a/clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h b/clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h index ca4e607992bbc..d6dac50bb1263 100644 --- a/clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h +++ b/clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h @@ -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; + + /// 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; @@ -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 @@ -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 @@ -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; diff --git a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp index 6e5c7b8fb51f8..ab1ea07bbf5ef 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp @@ -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(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) { @@ -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"); + 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 {}; } @@ -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, diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index a6a4330ff2ce1..82bd139295b10 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -269,6 +269,40 @@ mlir::Operation *CIRGenModule::getGlobalValue(StringRef name) { return mlir::SymbolTable::lookupSymbolIn(theModule, name); } +cir::GlobalOp CIRGenModule::createGlobalOp(CIRGenModule &cgm, + mlir::Location loc, StringRef name, + mlir::Type t, + mlir::Operation *insertPoint) { + cir::GlobalOp g; + CIRGenBuilderTy &builder = cgm.getBuilder(); + + { + mlir::OpBuilder::InsertionGuard guard(builder); + + // Some global emissions are triggered while emitting a function, e.g. + // void s() { const char *s = "yolo"; ... } + // + // Be sure to insert global before the current function + CIRGenFunction *curCGF = cgm.curCGF; + if (curCGF) + builder.setInsertionPoint(curCGF->curFn); + + g = builder.create(loc, name, t); + if (!curCGF) { + if (insertPoint) + cgm.getModule().insert(insertPoint, g); + else + cgm.getModule().push_back(g); + } + + // Default to private until we can judge based on the initializer, + // since MLIR doesn't allow public declarations. + mlir::SymbolTable::setSymbolVisibility( + g, mlir::SymbolTable::Visibility::Private); + } + return g; +} + /// If the specified mangled name is not in the module, /// create and return an mlir GlobalOp with the specified type (TODO(cir): /// address space). @@ -324,8 +358,15 @@ CIRGenModule::getOrCreateCIRGlobal(StringRef mangledName, mlir::Type ty, return entry; } - errorNYI(d->getSourceRange(), "reference of undeclared global"); - return {}; + mlir::Location loc = getLoc(d->getSourceRange()); + + // mlir::SymbolTable::Visibility::Public is the default, no need to explicitly + // mark it as such. + cir::GlobalOp gv = + CIRGenModule::createGlobalOp(*this, loc, mangledName, ty, + /*insertPoint=*/entry.getOperation()); + + return gv; } cir::GlobalOp @@ -365,46 +406,125 @@ mlir::Value CIRGenModule::getAddrOfGlobalVar(const VarDecl *d, mlir::Type ty, void CIRGenModule::emitGlobalVarDefinition(const clang::VarDecl *vd, bool isTentative) { const QualType astTy = vd->getType(); - const mlir::Type type = convertType(vd->getType()); - if (vd->getIdentifier()) { - StringRef name = getMangledName(GlobalDecl(vd)); - auto varOp = - builder.create(getLoc(vd->getSourceRange()), name, type); - // TODO(CIR): This code for processing initial values is a placeholder - // until class ConstantEmitter is upstreamed and the code for processing - // constant expressions is filled out. Only the most basic handling of - // certain constant expressions is implemented for now. - const VarDecl *initDecl; - const Expr *initExpr = vd->getAnyInitializer(initDecl); - mlir::Attribute initializer; - if (initExpr) { - if (APValue *value = initDecl->evaluateValue()) { - ConstantEmitter emitter(*this); - initializer = emitter.tryEmitPrivateForMemory(*value, astTy); + + if (getLangOpts().OpenCL || getLangOpts().OpenMPIsTargetDevice) { + errorNYI(vd->getSourceRange(), "emit OpenCL/OpenMP global variable"); + return; + } + + // Whether the definition of the variable is available externally. + // If yes, we shouldn't emit the GloablCtor and GlobalDtor for the variable + // since this is the job for its original source. + bool isDefinitionAvailableExternally = + astContext.GetGVALinkageForVariable(vd) == GVA_AvailableExternally; + assert(!cir::MissingFeatures::needsGlobalCtorDtor()); + + // It is useless to emit the definition for an available_externally variable + // which can't be marked as const. + if (isDefinitionAvailableExternally && + (!vd->hasConstantInitialization() || + // TODO: Update this when we have interface to check constexpr + // destructor. + vd->needsDestruction(astContext) || + !vd->getType().isConstantStorage(astContext, true, true))) + return; + + mlir::Attribute init; + const VarDecl *initDecl; + const Expr *initExpr = vd->getAnyInitializer(initDecl); + + std::optional emitter; + + assert(!cir::MissingFeatures::cudaSupport()); + + if (vd->hasAttr()) { + errorNYI(vd->getSourceRange(), "loader uninitialized attribute"); + return; + } else if (!initExpr) { + // This is a tentative definition; tentative definitions are + // implicitly initialized with { 0 }. + // + // Note that tentative definitions are only emitted at the end of + // a translation unit, so they should never have incomplete + // type. In addition, EmitTentativeDefinition makes sure that we + // never attempt to emit a tentative definition if a real one + // exists. A use may still exists, however, so we still may need + // to do a RAUW. + assert(!astTy->isIncompleteType() && "Unexpected incomplete type"); + init = builder.getZeroInitAttr(convertType(vd->getType())); + } else { + emitter.emplace(*this); + mlir::Attribute initializer = emitter->tryEmitForInitializer(*initDecl); + if (!initializer) { + QualType qt = initExpr->getType(); + if (vd->getType()->isReferenceType()) + qt = vd->getType(); + + if (getLangOpts().CPlusPlus) { + if (initDecl->hasFlexibleArrayInit(astContext)) + errorNYI(vd->getSourceRange(), "flexible array initializer"); + init = builder.getZeroInitAttr(convertType(qt)); + if (astContext.GetGVALinkageForVariable(vd) != GVA_AvailableExternally) + errorNYI(vd->getSourceRange(), "global constructor"); } else { - errorNYI(initExpr->getSourceRange(), "non-constant initializer"); + errorNYI(vd->getSourceRange(), "static initializer"); } } else { - initializer = builder.getZeroInitAttr(convertType(astTy)); + init = initializer; + // We don't need an initializer, so remove the entry for the delayed + // initializer position (just in case this entry was delayed) if we + // also don't need to register a destructor. + if (vd->needsDestruction(astContext) == QualType::DK_cxx_destructor) + errorNYI(vd->getSourceRange(), "delayed destructor"); } + } + + mlir::Type initType; + if (mlir::isa(init)) { + errorNYI(vd->getSourceRange(), "global initializer is a symbol reference"); + return; + } else { + assert(mlir::isa(init) && "This should have a type"); + auto typedInitAttr = mlir::cast(init); + initType = typedInitAttr.getType(); + } + assert(!mlir::isa(initType) && "Should have a type by now"); + + cir::GlobalOp gv = + getOrCreateCIRGlobal(vd, initType, ForDefinition_t(!isTentative)); + // TODO(cir): Strip off pointer casts from Entry if we get them? - varOp.setInitialValueAttr(initializer); + if (!gv || gv.getSymType() != initType) { + errorNYI(vd->getSourceRange(), "global initializer with type mismatch"); + return; + } - // Set CIR's linkage type as appropriate. - cir::GlobalLinkageKind linkage = - getCIRLinkageVarDefinition(vd, /*IsConstant=*/false); + assert(!cir::MissingFeatures::maybeHandleStaticInExternC()); - // Set CIR linkage and DLL storage class. - varOp.setLinkage(linkage); + if (vd->hasAttr()) { + errorNYI(vd->getSourceRange(), "annotate global variable"); + } - if (linkage == cir::GlobalLinkageKind::CommonLinkage) - errorNYI(initExpr->getSourceRange(), "common linkage"); + assert(!cir::MissingFeatures::opGlobalLinkage()); - theModule.push_back(varOp); - } else { - errorNYI(vd->getSourceRange().getBegin(), - "variable definition with a non-identifier for a name"); + if (langOpts.CUDA) { + errorNYI(vd->getSourceRange(), "CUDA global variable"); } + + // Set initializer and finalize emission + CIRGenModule::setInitializer(gv, init); + if (emitter) + emitter->finalize(gv); + + // Set CIR's linkage type as appropriate. + cir::GlobalLinkageKind linkage = + getCIRLinkageVarDefinition(vd, /*IsConstant=*/false); + + // Set CIR linkage and DLL storage class. + gv.setLinkage(linkage); + + if (linkage == cir::GlobalLinkageKind::CommonLinkage) + errorNYI(initExpr->getSourceRange(), "common linkage"); } void CIRGenModule::emitGlobalDefinition(clang::GlobalDecl gd, @@ -684,6 +804,12 @@ void CIRGenModule::emitTopLevelDecl(Decl *decl) { } } +void CIRGenModule::setInitializer(cir::GlobalOp &op, mlir::Attribute value) { + // Recompute visibility when updating initializer. + op.setInitialValueAttr(value); + assert(!cir::MissingFeatures::opGlobalSetVisitibility()); +} + cir::FuncOp CIRGenModule::getAddrOfFunction(clang::GlobalDecl gd, mlir::Type funcType, bool forVTable, bool dontDefer, diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index ea30903a97167..b67239fcff44b 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -122,6 +122,10 @@ class CIRGenModule : public CIRGenTypeCache { cir::GlobalOp getOrCreateCIRGlobal(const VarDecl *d, mlir::Type ty, ForDefinition_t isForDefinition); + static cir::GlobalOp createGlobalOp(CIRGenModule &cgm, mlir::Location loc, + llvm::StringRef name, mlir::Type t, + mlir::Operation *insertPoint = nullptr); + /// Return the mlir::Value for the address of the given global variable. /// If Ty is non-null and if the global doesn't exist, then it will be created /// with the specified type instead of whatever the normal requested type @@ -179,6 +183,8 @@ class CIRGenModule : public CIRGenTypeCache { llvm::StringRef getMangledName(clang::GlobalDecl gd); + static void setInitializer(cir::GlobalOp &op, mlir::Attribute value); + cir::FuncOp getOrCreateCIRFunction(llvm::StringRef mangledName, mlir::Type funcType, clang::GlobalDecl gd, bool forVTable,