| //==--- CodeGenUtils.cpp - Shared Classic CodeGen/CIR CodeGen Utils--C++ -*-==// | |
| // | |
| // 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 | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "clang/CodeGenUtils/CodeGenUtils.h" | |
| #include "clang/AST/Attr.h" | |
| #include "clang/AST/Expr.h" | |
| #include "clang/Basic/Builtins.h" | |
| #include "clang/Basic/DiagnosticFrontend.h" | |
| #include "clang/Basic/TargetBuiltins.h" | |
| #include "clang/Basic/TargetInfo.h" | |
| #include "llvm/ADT/StringMap.h" | |
| namespace clang::CodeGenUtils { | |
| static bool | |
| hasTrivialDestructorBody(ASTContext &Context, | |
| const CXXRecordDecl *BaseClassDecl, | |
| const CXXRecordDecl *MostDerivedClassDecl) { | |
| // If the destructor is trivial we don't have to check anything else. | |
| if (BaseClassDecl->hasTrivialDestructor()) | |
| return true; | |
| if (!BaseClassDecl->getDestructor()->hasTrivialBody()) | |
| return false; | |
| // Check fields. | |
| for (const auto *Field : BaseClassDecl->fields()) | |
| if (!fieldHasTrivialDestructorBody(Context, Field)) | |
| return false; | |
| // Check non-virtual bases. | |
| for (const auto &I : BaseClassDecl->bases()) { | |
| if (I.isVirtual()) | |
| continue; | |
| const auto *NonVirtualBase = I.getType()->castAsCXXRecordDecl(); | |
| if (!hasTrivialDestructorBody(Context, NonVirtualBase, | |
| MostDerivedClassDecl)) | |
| return false; | |
| } | |
| if (BaseClassDecl == MostDerivedClassDecl) { | |
| // Check virtual bases. | |
| for (const auto &I : BaseClassDecl->vbases()) { | |
| const auto *VirtualBase = I.getType()->castAsCXXRecordDecl(); | |
| if (!hasTrivialDestructorBody(Context, VirtualBase, MostDerivedClassDecl)) | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| bool fieldHasTrivialDestructorBody(ASTContext &Context, | |
| const FieldDecl *Field) { | |
| QualType FieldBaseElementType = Context.getBaseElementType(Field->getType()); | |
| auto *FieldClassDecl = FieldBaseElementType->getAsCXXRecordDecl(); | |
| if (!FieldClassDecl) | |
| return true; | |
| // The destructor for an implicit anonymous union member is never invoked. | |
| if (FieldClassDecl->isUnion() && FieldClassDecl->isAnonymousStructOrUnion()) | |
| return true; | |
| return hasTrivialDestructorBody(Context, FieldClassDecl, FieldClassDecl); | |
| } | |
| /// Check whether we need to initialize any vtable pointers before calling this | |
| /// destructor. | |
| bool canSkipVTablePointerInitialization(ASTContext &Ctx, | |
| const CXXDestructorDecl *Dtor) { | |
| const CXXRecordDecl *ClassDecl = Dtor->getParent(); | |
| if (!ClassDecl->isDynamicClass()) | |
| return true; | |
| // For a final class, the vtable pointer is known to already point to the | |
| // class's vtable. | |
| if (ClassDecl->isEffectivelyFinal()) | |
| return true; | |
| if (!Dtor->hasTrivialBody()) | |
| return false; | |
| // Check the fields. | |
| for (const auto *Field : ClassDecl->fields()) | |
| if (!fieldHasTrivialDestructorBody(Ctx, Field)) | |
| return false; | |
| return true; | |
| } | |
| bool hasUnwindExceptions(const LangOptions &LangOpts) { | |
| // If exceptions are completely disabled, obviously this is false. | |
| if (!LangOpts.Exceptions) | |
| return false; | |
| // If C++ exceptions are enabled, this is true. | |
| if (LangOpts.CXXExceptions) | |
| return true; | |
| // If ObjC exceptions are enabled, this depends on the ABI. | |
| if (LangOpts.ObjCExceptions) { | |
| return LangOpts.ObjCRuntime.hasUnwindExceptions(); | |
| } | |
| return true; | |
| } | |
| bool isAAPCS(const TargetInfo &TargetInfo) { | |
| return TargetInfo.getABI().starts_with("aapcs"); | |
| } | |
| bool isInitializerOfDynamicClass(const CXXCtorInitializer *BaseInit) { | |
| const Type *BaseType = BaseInit->getBaseClass(); | |
| return BaseType->castAsCXXRecordDecl()->isDynamicClass(); | |
| } | |
| // Emits an error if we don't have a valid set of target features for the | |
| // called function. | |
| void checkTargetFeatures(ASTContext &Ctx, DiagnosticsEngine &Diags, | |
| const LangOptions &LangOpts, const CallExpr *E, | |
| const FunctionDecl *Caller, | |
| const FunctionDecl *TargetDecl) { | |
| // SemaChecking cannot handle these x86 builtins because they have different | |
| // parameter ranges depending on the caller's TargetAttribute. | |
| if (Ctx.getTargetInfo().getTriple().isX86()) { | |
| unsigned BuiltinID = TargetDecl->getBuiltinID(); | |
| if (BuiltinID == X86::BI__builtin_ia32_cmpps || | |
| BuiltinID == X86::BI__builtin_ia32_cmpss || | |
| BuiltinID == X86::BI__builtin_ia32_cmppd || | |
| BuiltinID == X86::BI__builtin_ia32_cmpsd) { | |
| llvm::StringMap<bool> TargetFeatureMap; | |
| Ctx.getFunctionFeatureMap(TargetFeatureMap, Caller); | |
| llvm::APSInt Result = *(E->getArg(2)->getIntegerConstantExpr(Ctx)); | |
| if (Result.getSExtValue() > 7 && !TargetFeatureMap.lookup("avx")) | |
| Diags.Report(E->getBeginLoc(), diag::err_builtin_needs_feature) | |
| << TargetDecl->getDeclName() << "avx"; | |
| } | |
| } | |
| checkTargetFeatures(Ctx, Diags, LangOpts, E->getBeginLoc(), Caller, | |
| TargetDecl); | |
| } | |
| // Emits an error if we don't have a valid set of target features for the | |
| // called function. | |
| void checkTargetFeatures(ASTContext &Ctx, DiagnosticsEngine &Diags, | |
| const LangOptions &LangOpts, SourceLocation Loc, | |
| const FunctionDecl *Caller, | |
| const FunctionDecl *TargetDecl) { | |
| if (!TargetDecl || !Caller) | |
| return; | |
| bool IsAlwaysInline = TargetDecl->hasAttr<AlwaysInlineAttr>(); | |
| bool IsFlatten = Caller->hasAttr<FlattenAttr>(); | |
| unsigned BuiltinID = TargetDecl->getBuiltinID(); | |
| std::string MissingFeature; | |
| llvm::StringMap<bool> CallerFeatureMap; | |
| Ctx.getFunctionFeatureMap(CallerFeatureMap, Caller); | |
| // When compiling in HipStdPar mode we have to be conservative in rejecting | |
| // target specific features in the FE, and defer the possible error to the | |
| // AcceleratorCodeSelection pass, wherein iff an unsupported target builtin is | |
| // referenced by an accelerator executable function, we emit an error. | |
| bool IsHipStdPar = LangOpts.HIPStdPar && LangOpts.CUDAIsDevice; | |
| if (BuiltinID) { | |
| StringRef FeatureList(Ctx.BuiltinInfo.getRequiredFeatures(BuiltinID)); | |
| if (!Builtin::evaluateRequiredTargetFeatures(FeatureList, | |
| CallerFeatureMap) && | |
| !IsHipStdPar) | |
| Diags.Report(Loc, diag::err_builtin_needs_feature) | |
| << TargetDecl->getDeclName() << FeatureList; | |
| } else if (!TargetDecl->isMultiVersion() && | |
| TargetDecl->hasAttr<TargetAttr>()) { | |
| // Get the required features for the callee. | |
| const TargetAttr *TD = TargetDecl->getAttr<TargetAttr>(); | |
| ParsedTargetAttr ParsedAttr = Ctx.filterFunctionTargetAttrs(TD); | |
| SmallVector<StringRef, 1> ReqFeatures; | |
| llvm::StringMap<bool> CalleeFeatureMap; | |
| Ctx.getFunctionFeatureMap(CalleeFeatureMap, TargetDecl); | |
| for (const auto &F : ParsedAttr.Features) { | |
| if (F[0] == '+' && CalleeFeatureMap.lookup(F.substr(1))) | |
| ReqFeatures.push_back(StringRef(F).substr(1)); | |
| } | |
| for (const auto &F : CalleeFeatureMap) { | |
| if (F.getValue()) | |
| ReqFeatures.push_back(F.getKey()); | |
| } | |
| if (!llvm::all_of(ReqFeatures, | |
| [&](StringRef Feature) { | |
| if (!CallerFeatureMap.lookup(Feature)) { | |
| MissingFeature = Feature.str(); | |
| return false; | |
| } | |
| return true; | |
| }) && | |
| !IsHipStdPar) { | |
| if (IsAlwaysInline) | |
| Diags.Report(Loc, diag::err_function_needs_feature) | |
| << Caller->getDeclName() << TargetDecl->getDeclName() | |
| << MissingFeature; | |
| else if (IsFlatten) | |
| Diags.Report(Loc, diag::err_flatten_function_needs_feature) | |
| << Caller->getDeclName() << TargetDecl->getDeclName() | |
| << MissingFeature; | |
| } | |
| } else if (!Caller->isMultiVersion() && Caller->hasAttr<TargetAttr>()) { | |
| llvm::StringMap<bool> CalleeFeatureMap; | |
| Ctx.getFunctionFeatureMap(CalleeFeatureMap, TargetDecl); | |
| for (const auto &F : CalleeFeatureMap) { | |
| if (F.getValue() && | |
| (!CallerFeatureMap.lookup(F.getKey()) || | |
| !CallerFeatureMap.find(F.getKey())->getValue()) && | |
| !IsHipStdPar) { | |
| if (IsAlwaysInline) | |
| Diags.Report(Loc, diag::err_function_needs_feature) | |
| << Caller->getDeclName() << TargetDecl->getDeclName() | |
| << F.getKey(); | |
| else if (IsFlatten) | |
| Diags.Report(Loc, diag::err_flatten_function_needs_feature) | |
| << Caller->getDeclName() << TargetDecl->getDeclName() | |
| << F.getKey(); | |
| } | |
| } | |
| } | |
| } | |
| } // namespace clang::CodeGenUtils |