https://github.com/ahatanak created https://github.com/llvm/llvm-project/pull/110047
The new cast kinds are needed to distinguish between no-op conversions and conversions from pointers to noexcept functions to pointers to functions without noexcept as the latter can cause function pointers to be re-signed on arm64e. See https://github.com/llvm/llvm-project/pull/109056 for background. >From 1113c14d7ea25baf6c328e69661f4b94e437a4b4 Mon Sep 17 00:00:00 2001 From: Akira Hatanaka <ahata...@gmail.com> Date: Wed, 25 Sep 2024 14:30:22 -0700 Subject: [PATCH] [NFC] Add implicit cast kinds for function pointer conversions The new cast kinds are needed to distinguish between no-op conversions and conversions from pointers to noexcept functions to pointers to functions without noexcept as the latter can cause function pointers to be re-signed on arm64e. See https://github.com/llvm/llvm-project/pull/109056 for background. --- .../bugprone/SwappedArgumentsCheck.cpp | 2 + .../ProTypeCstyleCastCheck.cpp | 4 +- .../google/AvoidCStyleCastsCheck.cpp | 7 +- .../clang-tidy/modernize/LoopConvertCheck.cpp | 4 +- .../ImplicitConversionInLoopCheck.cpp | 4 +- .../MakeMemberFunctionConstCheck.cpp | 9 +- .../clang-tidy/utils/DeclRefExprUtils.cpp | 2 + clang-tools-extra/clangd/Hover.cpp | 2 + clang/include/clang/AST/IgnoreExpr.h | 4 +- clang/include/clang/AST/OperationKinds.def | 8 + .../lib/ARCMigrate/TransBlockObjCVariable.cpp | 6 +- clang/lib/AST/ByteCode/Compiler.cpp | 4 + clang/lib/AST/Expr.cpp | 28 ++- clang/lib/AST/ExprCXX.cpp | 6 +- clang/lib/AST/ExprConstant.cpp | 17 +- clang/lib/Analysis/CFG.cpp | 2 + clang/lib/Analysis/ExprMutationAnalyzer.cpp | 12 +- clang/lib/Analysis/FlowSensitive/Transfer.cpp | 8 +- clang/lib/Analysis/ThreadSafety.cpp | 6 +- clang/lib/Analysis/ThreadSafetyCommon.cpp | 2 + clang/lib/CodeGen/CGDecl.cpp | 2 + clang/lib/CodeGen/CGExpr.cpp | 6 +- clang/lib/CodeGen/CGExprAgg.cpp | 4 + clang/lib/CodeGen/CGExprComplex.cpp | 2 + clang/lib/CodeGen/CGExprConstant.cpp | 2 + clang/lib/CodeGen/CGExprScalar.cpp | 4 +- clang/lib/CodeGen/CGHLSLRuntime.cpp | 1 - clang/lib/CodeGen/CodeGenModule.cpp | 20 +- clang/lib/Edit/RewriteObjCFoundationAPI.cpp | 2 + clang/lib/Sema/CheckExprLifetime.cpp | 2 + clang/lib/Sema/SemaChecking.cpp | 4 + clang/lib/Sema/SemaDecl.cpp | 4 +- clang/lib/Sema/SemaDeclCXX.cpp | 4 +- clang/lib/Sema/SemaExpr.cpp | 2 + clang/lib/Sema/SemaExprCXX.cpp | 6 +- clang/lib/Sema/SemaInit.cpp | 4 +- clang/lib/Sema/SemaOpenMP.cpp | 211 +++++++++--------- clang/lib/Sema/SemaOverload.cpp | 6 +- clang/lib/Sema/SemaStmt.cpp | 2 + clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp | 2 + clang/lib/StaticAnalyzer/Core/SValBuilder.cpp | 2 + .../ast-dump-function-pointer-conversion.cpp | 30 +++ .../dcl.decl/dcl.init/dcl.init.ref/p4-ast.cpp | 4 +- .../GlobalConstructorFunction.hlsl | 31 +-- .../CodeGenHLSL/GlobalConstructorLib.hlsl | 23 +- clang/test/CodeGenHLSL/GlobalDestructors.hlsl | 51 ++--- .../builtins/RWBuffer-constructor.hlsl | 1 - .../builtins/RWBuffer-subscript.hlsl | 5 +- .../test/CodeGenHLSL/inline-constructors.hlsl | 76 ------- clang/test/CodeGenHLSL/inline-functions.hlsl | 116 ---------- llvm/include/llvm/CodeGen/VirtRegMap.h | 8 +- llvm/include/llvm/SandboxIR/SandboxIR.h | 34 +++ llvm/lib/CodeGen/MachineVerifier.cpp | 2 +- llvm/lib/SandboxIR/SandboxIR.cpp | 24 ++ .../test_g_insert_subvector.mir | 3 + llvm/unittests/SandboxIR/SandboxIRTest.cpp | 66 ++++++ llvm/unittests/SandboxIR/TrackerTest.cpp | 31 +++ 57 files changed, 506 insertions(+), 428 deletions(-) create mode 100644 clang/test/AST/ast-dump-function-pointer-conversion.cpp delete mode 100644 clang/test/CodeGenHLSL/inline-constructors.hlsl delete mode 100644 clang/test/CodeGenHLSL/inline-functions.hlsl diff --git a/clang-tools-extra/clang-tidy/bugprone/SwappedArgumentsCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/SwappedArgumentsCheck.cpp index 8989444dde1300..acb7112fa2abed 100644 --- a/clang-tools-extra/clang-tidy/bugprone/SwappedArgumentsCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/SwappedArgumentsCheck.cpp @@ -27,6 +27,8 @@ void SwappedArgumentsCheck::registerMatchers(MatchFinder *Finder) { static const Expr *ignoreNoOpCasts(const Expr *E) { if (auto *Cast = dyn_cast<CastExpr>(E)) if (Cast->getCastKind() == CK_LValueToRValue || + Cast->getCastKind() == CK_FunctionPointerConversion || + Cast->getCastKind() == CK_MemberFunctionPointerConversion || Cast->getCastKind() == CK_NoOp) return ignoreNoOpCasts(Cast->getSubExpr()); return E; diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeCstyleCastCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeCstyleCastCheck.cpp index 5e255dcaacd262..bab08496e90e86 100644 --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeCstyleCastCheck.cpp +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeCstyleCastCheck.cpp @@ -90,7 +90,9 @@ void ProTypeCstyleCastCheck::check(const MatchFinder::MatchResult &Result) { return; } - if (MatchedCast->getCastKind() == CK_NoOp && + if ((MatchedCast->getCastKind() == CK_NoOp || + MatchedCast->getCastKind() == CK_FunctionPointerConversion || + MatchedCast->getCastKind() == CK_MemberFunctionPointerConversion) && needsConstCast(SourceType, MatchedCast->getType())) { diag(MatchedCast->getBeginLoc(), "do not use C-style cast to cast away constness"); diff --git a/clang-tools-extra/clang-tidy/google/AvoidCStyleCastsCheck.cpp b/clang-tools-extra/clang-tidy/google/AvoidCStyleCastsCheck.cpp index 3109bbb3724c79..888f7b122b82c3 100644 --- a/clang-tools-extra/clang-tidy/google/AvoidCStyleCastsCheck.cpp +++ b/clang-tools-extra/clang-tidy/google/AvoidCStyleCastsCheck.cpp @@ -123,7 +123,10 @@ void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) { DestTypeAsWritten->isRecordType() && !DestTypeAsWritten->isElaboratedTypeSpecifier(); - if (CastExpr->getCastKind() == CK_NoOp && !FnToFnCast) { + if ((CastExpr->getCastKind() == CK_NoOp || + CastExpr->getCastKind() == CK_FunctionPointerConversion || + CastExpr->getCastKind() == CK_MemberFunctionPointerConversion) && + !FnToFnCast) { // Function pointer/reference casts may be needed to resolve ambiguities in // case of overloaded functions, so detection of redundant casts is trickier // in this case. Don't emit "redundant cast" warnings for function @@ -201,6 +204,8 @@ void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) { } return; case CK_NoOp: + case CK_FunctionPointerConversion: + case CK_MemberFunctionPointerConversion: if (FnToFnCast) { ReplaceWithNamedCast("static_cast"); return; diff --git a/clang-tools-extra/clang-tidy/modernize/LoopConvertCheck.cpp b/clang-tools-extra/clang-tidy/modernize/LoopConvertCheck.cpp index a1786ba5acfdf5..658f134faa0b9f 100644 --- a/clang-tools-extra/clang-tidy/modernize/LoopConvertCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/LoopConvertCheck.cpp @@ -500,7 +500,9 @@ static bool canBeModified(ASTContext *Context, const Expr *E) { if (Parents.size() != 1) return true; if (const auto *Cast = Parents[0].get<ImplicitCastExpr>()) { - if ((Cast->getCastKind() == CK_NoOp && + if (((Cast->getCastKind() == CK_NoOp || + Cast->getCastKind() == CK_FunctionPointerConversion || + Cast->getCastKind() == CK_MemberFunctionPointerConversion) && Context->hasSameType(Cast->getType(), E->getType().withConst())) || (Cast->getCastKind() == CK_LValueToRValue && !Cast->getType().isNull() && Cast->getType()->isFundamentalType())) diff --git a/clang-tools-extra/clang-tidy/performance/ImplicitConversionInLoopCheck.cpp b/clang-tools-extra/clang-tidy/performance/ImplicitConversionInLoopCheck.cpp index 86fca0722dcd82..4fc32e1d1572eb 100644 --- a/clang-tools-extra/clang-tidy/performance/ImplicitConversionInLoopCheck.cpp +++ b/clang-tools-extra/clang-tidy/performance/ImplicitConversionInLoopCheck.cpp @@ -24,7 +24,9 @@ namespace clang::tidy::performance { // case we skip the first cast expr. static bool isNonTrivialImplicitCast(const Stmt *ST) { if (const auto *ICE = dyn_cast<ImplicitCastExpr>(ST)) { - return (ICE->getCastKind() != CK_NoOp) || + return (ICE->getCastKind() != CK_NoOp && + ICE->getCastKind() != CK_FunctionPointerConversion && + ICE->getCastKind() != CK_MemberFunctionPointerConversion) || isNonTrivialImplicitCast(ICE->getSubExpr()); } return false; diff --git a/clang-tools-extra/clang-tidy/readability/MakeMemberFunctionConstCheck.cpp b/clang-tools-extra/clang-tidy/readability/MakeMemberFunctionConstCheck.cpp index d42fcba70e81b4..751cd637a02334 100644 --- a/clang-tools-extra/clang-tidy/readability/MakeMemberFunctionConstCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/MakeMemberFunctionConstCheck.cpp @@ -97,7 +97,9 @@ class FindUsageOfThis : public RecursiveASTVisitor<FindUsageOfThis> { // (possibly `-UnaryOperator Deref) // `-CXXThisExpr 'S *' this bool visitUser(const ImplicitCastExpr *Cast) { - if (Cast->getCastKind() != CK_NoOp) + if (Cast->getCastKind() != CK_NoOp && + Cast->getCastKind() != CK_FunctionPointerConversion && + Cast->getCastKind() != CK_MemberFunctionPointerConversion) return false; // Stop traversal. // Only allow NoOp cast to 'const S' or 'const S *'. @@ -159,7 +161,10 @@ class FindUsageOfThis : public RecursiveASTVisitor<FindUsageOfThis> { if (Cast->getCastKind() == CK_LValueToRValue) return true; - if (Cast->getCastKind() == CK_NoOp && Cast->getType().isConstQualified()) + if ((Cast->getCastKind() == CK_NoOp || + Cast->getCastKind() == CK_FunctionPointerConversion || + Cast->getCastKind() == CK_MemberFunctionPointerConversion) && + Cast->getType().isConstQualified()) return true; } diff --git a/clang-tools-extra/clang-tidy/utils/DeclRefExprUtils.cpp b/clang-tools-extra/clang-tidy/utils/DeclRefExprUtils.cpp index 106feb7fb41720..87e523eaaa7718 100644 --- a/clang-tools-extra/clang-tidy/utils/DeclRefExprUtils.cpp +++ b/clang-tools-extra/clang-tidy/utils/DeclRefExprUtils.cpp @@ -240,6 +240,8 @@ AST_MATCHER_P(DeclRefExpr, doesNotMutateObject, int, Indirections) { case CK_BaseToDerived: case CK_DerivedToBase: case CK_UncheckedDerivedToBase: + case CK_FunctionPointerConversion: + case CK_MemberFunctionPointerConversion: case CK_Dynamic: case CK_BaseToDerivedMemberPointer: case CK_DerivedToBaseMemberPointer: diff --git a/clang-tools-extra/clangd/Hover.cpp b/clang-tools-extra/clangd/Hover.cpp index de103e011c7085..d5c609d100d4e4 100644 --- a/clang-tools-extra/clangd/Hover.cpp +++ b/clang-tools-extra/clangd/Hover.cpp @@ -1122,6 +1122,8 @@ void maybeAddCalleeArgInfo(const SelectionTree::Node *N, HoverInfo &HI, if (const auto *ImplicitCast = CastNode->ASTNode.get<ImplicitCastExpr>()) { switch (ImplicitCast->getCastKind()) { case CK_NoOp: + case CK_FunctionPointerConversion: + case CK_MemberFunctionPointerConversion: case CK_DerivedToBase: case CK_UncheckedDerivedToBase: // If it was a reference before, it's still a reference. diff --git a/clang/include/clang/AST/IgnoreExpr.h b/clang/include/clang/AST/IgnoreExpr.h index 917bada61fa6fd..967c381dfa9dc5 100644 --- a/clang/include/clang/AST/IgnoreExpr.h +++ b/clang/include/clang/AST/IgnoreExpr.h @@ -102,7 +102,9 @@ inline Expr *IgnoreBaseCastsSingleStep(Expr *E) { if (auto *CE = dyn_cast<CastExpr>(E)) if (CE->getCastKind() == CK_DerivedToBase || CE->getCastKind() == CK_UncheckedDerivedToBase || - CE->getCastKind() == CK_NoOp) + CE->getCastKind() == CK_NoOp || + CE->getCastKind() == CK_FunctionPointerConversion || + CE->getCastKind() == CK_MemberFunctionPointerConversion) return CE->getSubExpr(); return E; diff --git a/clang/include/clang/AST/OperationKinds.def b/clang/include/clang/AST/OperationKinds.def index 8788b8ff0ef0a4..25cd0dbb5e3a3b 100644 --- a/clang/include/clang/AST/OperationKinds.def +++ b/clang/include/clang/AST/OperationKinds.def @@ -84,6 +84,14 @@ CAST_OPERATION(LValueToRValue) /// void () noexcept -> void () CAST_OPERATION(NoOp) +/// CK_FunctionPointerConversion - A conversion from pointer/reference to +/// noexcept function to pointer/reference to function. +CAST_OPERATION(FunctionPointerConversion) + +/// CK_MemberFunctionPointerConversion - A conversion from pointer to noexcept +/// member function to pointer to member function. +CAST_OPERATION(MemberFunctionPointerConversion) + /// CK_BaseToDerived - A conversion from a C++ class pointer/reference /// to a derived class pointer/reference. /// B *b = static_cast<B*>(a); diff --git a/clang/lib/ARCMigrate/TransBlockObjCVariable.cpp b/clang/lib/ARCMigrate/TransBlockObjCVariable.cpp index 1e4db33135b6a1..7295f064849ea6 100644 --- a/clang/lib/ARCMigrate/TransBlockObjCVariable.cpp +++ b/clang/lib/ARCMigrate/TransBlockObjCVariable.cpp @@ -53,8 +53,10 @@ class RootBlockObjCVarRewriter : if (ref->getDecl() == Var) { if (castE->getCastKind() == CK_LValueToRValue) return true; // Using the value of the variable. - if (castE->getCastKind() == CK_NoOp && castE->isLValue() && - Var->getASTContext().getLangOpts().CPlusPlus) + if ((castE->getCastKind() == CK_NoOp || + castE->getCastKind() == CK_FunctionPointerConversion || + castE->getCastKind() == CK_MemberFunctionPointerConversion) && + castE->isLValue() && Var->getASTContext().getLangOpts().CPlusPlus) return true; // Binding to const C++ reference. } } diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp index 7e0775a51aee61..394aa9a7d363b2 100644 --- a/clang/lib/AST/ByteCode/Compiler.cpp +++ b/clang/lib/AST/ByteCode/Compiler.cpp @@ -429,6 +429,8 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) { case CK_FunctionToPointerDecay: case CK_NonAtomicToAtomic: case CK_NoOp: + case CK_FunctionPointerConversion: + case CK_MemberFunctionPointerConversion: case CK_UserDefinedConversion: case CK_AddressSpaceConversion: return this->delegate(SubExpr); @@ -3067,6 +3069,8 @@ bool Compiler<Emitter>::VisitCXXNewExpr(const CXXNewExpr *E) { for (; auto *ICE = dyn_cast<ImplicitCastExpr>(Stripped); Stripped = ICE->getSubExpr()) if (ICE->getCastKind() != CK_NoOp && + ICE->getCastKind() != CK_FunctionPointerConversion && + ICE->getCastKind() != CK_MemberFunctionPointerConversion && ICE->getCastKind() != CK_IntegralCast) break; diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 2e463fc00c6b68..c62ffa9bf7df59 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -98,7 +98,9 @@ const Expr *Expr::skipRValueSubobjectAdjustments( continue; } - if (CE->getCastKind() == CK_NoOp) { + if (CE->getCastKind() == CK_NoOp || + CE->getCastKind() == CK_FunctionPointerConversion || + CE->getCastKind() == CK_MemberFunctionPointerConversion) { E = CE->getSubExpr(); continue; } @@ -1926,6 +1928,8 @@ bool CastExpr::CastConsistency() const { case CK_Dependent: case CK_LValueToRValue: case CK_NoOp: + case CK_FunctionPointerConversion: + case CK_MemberFunctionPointerConversion: case CK_AtomicToNonAtomic: case CK_NonAtomicToAtomic: case CK_PointerToBoolean: @@ -3188,7 +3192,9 @@ static const Expr *skipTemporaryBindingsNoOpCastsAndParens(const Expr *E) { E = M->getSubExpr(); while (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) { - if (ICE->getCastKind() == CK_NoOp) + if (ICE->getCastKind() == CK_NoOp || + ICE->getCastKind() == CK_FunctionPointerConversion || + ICE->getCastKind() == CK_MemberFunctionPointerConversion) E = ICE->getSubExpr(); else break; @@ -3198,7 +3204,9 @@ static const Expr *skipTemporaryBindingsNoOpCastsAndParens(const Expr *E) { E = BE->getSubExpr(); while (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) { - if (ICE->getCastKind() == CK_NoOp) + if (ICE->getCastKind() == CK_NoOp || + ICE->getCastKind() == CK_FunctionPointerConversion || + ICE->getCastKind() == CK_MemberFunctionPointerConversion) E = ICE->getSubExpr(); else break; @@ -3263,6 +3271,8 @@ bool Expr::isImplicitCXXThis() const { if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) { if (ICE->getCastKind() == CK_NoOp || + ICE->getCastKind() == CK_FunctionPointerConversion || + ICE->getCastKind() == CK_MemberFunctionPointerConversion || ICE->getCastKind() == CK_LValueToRValue || ICE->getCastKind() == CK_DerivedToBase || ICE->getCastKind() == CK_UncheckedDerivedToBase) { @@ -3478,6 +3488,8 @@ bool Expr::isConstantInitializer(ASTContext &Ctx, bool IsForRef, // Handle misc casts we want to ignore. if (CE->getCastKind() == CK_NoOp || + CE->getCastKind() == CK_FunctionPointerConversion || + CE->getCastKind() == CK_MemberFunctionPointerConversion || CE->getCastKind() == CK_LValueToRValue || CE->getCastKind() == CK_ToUnion || CE->getCastKind() == CK_ConstructorConversion || @@ -4113,7 +4125,10 @@ FieldDecl *Expr::getSourceBitField() { while (ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) { if (ICE->getCastKind() == CK_LValueToRValue || - (ICE->isGLValue() && ICE->getCastKind() == CK_NoOp)) + (ICE->isGLValue() && + (ICE->getCastKind() == CK_NoOp || + ICE->getCastKind() == CK_FunctionPointerConversion || + ICE->getCastKind() == CK_MemberFunctionPointerConversion))) E = ICE->getSubExpr()->IgnoreParens(); else break; @@ -4167,7 +4182,10 @@ bool Expr::refersToVectorElement() const { const Expr *E = this->IgnoreParens(); while (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) { - if (ICE->isGLValue() && ICE->getCastKind() == CK_NoOp) + if (ICE->isGLValue() && + (ICE->getCastKind() == CK_NoOp || + ICE->getCastKind() == CK_FunctionPointerConversion || + ICE->getCastKind() == CK_MemberFunctionPointerConversion)) E = ICE->getSubExpr()->IgnoreParens(); else break; diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp index 83ce404add5f50..a3f09786960b1f 100644 --- a/clang/lib/AST/ExprCXX.cpp +++ b/clang/lib/AST/ExprCXX.cpp @@ -344,8 +344,12 @@ QualType CXXDeleteExpr::getDestroyedType() const { while (const auto *ICE = dyn_cast<ImplicitCastExpr>(Arg)) { if (ICE->getCastKind() == CK_DerivedToBase || ICE->getCastKind() == CK_UncheckedDerivedToBase || - ICE->getCastKind() == CK_NoOp) { + ICE->getCastKind() == CK_NoOp || + ICE->getCastKind() == CK_FunctionPointerConversion || + ICE->getCastKind() == CK_MemberFunctionPointerConversion) { assert((ICE->getCastKind() == CK_NoOp || + ICE->getCastKind() == CK_FunctionPointerConversion || + ICE->getCastKind() == CK_MemberFunctionPointerConversion || getOperatorDelete()->isDestroyingOperatorDelete()) && "only a destroying operator delete can have a converted arg"); Arg = ICE->getSubExpr(); diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 6387e375dda79c..28f829e612be47 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -6247,7 +6247,9 @@ static bool MaybeHandleUnionActiveMemberChange(EvalInfo &Info, } else if (auto *ICE = dyn_cast<ImplicitCastExpr>(E)) { // Step over a derived-to-base conversion. E = ICE->getSubExpr(); - if (ICE->getCastKind() == CK_NoOp) + if (ICE->getCastKind() == CK_NoOp || + ICE->getCastKind() == CK_FunctionPointerConversion || + ICE->getCastKind() == CK_MemberFunctionPointerConversion) continue; if (ICE->getCastKind() != CK_DerivedToBase && ICE->getCastKind() != CK_UncheckedDerivedToBase) @@ -8301,6 +8303,8 @@ class ExprEvaluatorBase } case CK_NoOp: + case CK_FunctionPointerConversion: + case CK_MemberFunctionPointerConversion: case CK_UserDefinedConversion: return StmtVisitorTy::Visit(E->getSubExpr()); @@ -10074,6 +10078,8 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) { for (; auto *ICE = dyn_cast<ImplicitCastExpr>(Stripped); Stripped = ICE->getSubExpr()) if (ICE->getCastKind() != CK_NoOp && + ICE->getCastKind() != CK_FunctionPointerConversion && + ICE->getCastKind() != CK_MemberFunctionPointerConversion && ICE->getCastKind() != CK_IntegralCast) break; @@ -12210,8 +12216,9 @@ static const Expr *ignorePointerCastsAndParens(const Expr *E) { // We only conservatively allow a few kinds of casts, because this code is // inherently a simple solution that seeks to support the common case. auto CastKind = Cast->getCastKind(); - if (CastKind != CK_NoOp && CastKind != CK_BitCast && - CastKind != CK_AddressSpaceConversion) + if (CastKind != CK_NoOp && CastKind != CK_FunctionPointerConversion && + CastKind != CK_MemberFunctionPointerConversion && + CastKind != CK_BitCast && CastKind != CK_AddressSpaceConversion) return NoParens; const auto *SubExpr = Cast->getSubExpr(); @@ -14448,6 +14455,8 @@ bool IntExprEvaluator::VisitCastExpr(const CastExpr *E) { QualType SrcType = SubExpr->getType(); switch (E->getCastKind()) { + case CK_FunctionPointerConversion: + case CK_MemberFunctionPointerConversion: case CK_BaseToDerived: case CK_DerivedToBase: case CK_UncheckedDerivedToBase: @@ -15288,6 +15297,8 @@ bool ComplexExprEvaluator::VisitCastExpr(const CastExpr *E) { case CK_BaseToDerived: case CK_DerivedToBase: case CK_UncheckedDerivedToBase: + case CK_FunctionPointerConversion: + case CK_MemberFunctionPointerConversion: case CK_Dynamic: case CK_ToUnion: case CK_ArrayToPointerDecay: diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp index f678ac6f2ff36a..baa5f96f8cbdbe 100644 --- a/clang/lib/Analysis/CFG.cpp +++ b/clang/lib/Analysis/CFG.cpp @@ -1491,6 +1491,8 @@ void CFGBuilder::findConstructionContexts( // Should we support other implicit cast kinds? switch (Cast->getCastKind()) { case CK_NoOp: + case CK_FunctionPointerConversion: + case CK_MemberFunctionPointerConversion: case CK_ConstructorConversion: findConstructionContexts(Layer, Cast->getSubExpr()); break; diff --git a/clang/lib/Analysis/ExprMutationAnalyzer.cpp b/clang/lib/Analysis/ExprMutationAnalyzer.cpp index 6d726ae44104ed..8f19bd7e918706 100644 --- a/clang/lib/Analysis/ExprMutationAnalyzer.cpp +++ b/clang/lib/Analysis/ExprMutationAnalyzer.cpp @@ -377,11 +377,13 @@ ExprMutationAnalyzer::Analyzer::findDirectMutation(const Expr *Exp) { // We're assuming 'Exp' is mutated as soon as its address is taken, though in // theory we can follow the pointer and see whether it escaped `Stm` or is // dereferenced and then mutated. This is left for future improvements. - const auto AsAmpersandOperand = - unaryOperator(hasOperatorName("&"), - // A NoOp implicit cast is adding const. - unless(hasParent(implicitCastExpr(hasCastKind(CK_NoOp)))), - hasUnaryOperand(canResolveToExpr(Exp))); + const auto AsAmpersandOperand = unaryOperator( + hasOperatorName("&"), + // A NoOp implicit cast is adding const. + unless(hasParent(implicitCastExpr( + anyOf(hasCastKind(CK_NoOp), hasCastKind(CK_FunctionPointerConversion), + hasCastKind(CK_MemberFunctionPointerConversion))))), + hasUnaryOperand(canResolveToExpr(Exp))); const auto AsPointerFromArrayDecay = castExpr( hasCastKind(CK_ArrayToPointerDecay), unless(hasParent(arraySubscriptExpr())), has(canResolveToExpr(Exp))); diff --git a/clang/lib/Analysis/FlowSensitive/Transfer.cpp b/clang/lib/Analysis/FlowSensitive/Transfer.cpp index 9c54eb16d22246..b58d40df7f07fc 100644 --- a/clang/lib/Analysis/FlowSensitive/Transfer.cpp +++ b/clang/lib/Analysis/FlowSensitive/Transfer.cpp @@ -322,7 +322,9 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> { case CK_UserDefinedConversion: // FIXME: Add tests that excercise CK_UncheckedDerivedToBase, // CK_ConstructorConversion, and CK_UserDefinedConversion. - case CK_NoOp: { + case CK_NoOp: + case CK_FunctionPointerConversion: + case CK_MemberFunctionPointerConversion: { // FIXME: Consider making `Environment::getStorageLocation` skip noop // expressions (this and other similar expressions in the file) instead // of assigning them storage locations. @@ -679,7 +681,9 @@ class TransferVisitor : public ConstStmtVisitor<TransferVisitor> { } void VisitCXXStaticCastExpr(const CXXStaticCastExpr *S) { - if (S->getCastKind() == CK_NoOp) { + if (S->getCastKind() == CK_NoOp || + S->getCastKind() == CK_FunctionPointerConversion || + S->getCastKind() == CK_MemberFunctionPointerConversion) { const Expr *SubExpr = S->getSubExpr(); assert(SubExpr != nullptr); diff --git a/clang/lib/Analysis/ThreadSafety.cpp b/clang/lib/Analysis/ThreadSafety.cpp index 5577f45aa5217f..49883e01f77d60 100644 --- a/clang/lib/Analysis/ThreadSafety.cpp +++ b/clang/lib/Analysis/ThreadSafety.cpp @@ -2103,8 +2103,10 @@ void BuildLockset::VisitCXXConstructExpr(const CXXConstructExpr *Exp) { static const Expr *UnpackConstruction(const Expr *E) { if (auto *CE = dyn_cast<CastExpr>(E)) - if (CE->getCastKind() == CK_NoOp) - E = CE->getSubExpr()->IgnoreParens(); + if (CE->getCastKind() == CK_NoOp || + CE->getCastKind() == CK_FunctionPointerConversion || + CE->getCastKind() == CK_MemberFunctionPointerConversion) + E = CE->getSubExpr()->IgnoreParens(); if (auto *CE = dyn_cast<CastExpr>(E)) if (CE->getCastKind() == CK_ConstructorConversion || CE->getCastKind() == CK_UserDefinedConversion) diff --git a/clang/lib/Analysis/ThreadSafetyCommon.cpp b/clang/lib/Analysis/ThreadSafetyCommon.cpp index cbcfefdc525490..aa54fe4ea66d5b 100644 --- a/clang/lib/Analysis/ThreadSafetyCommon.cpp +++ b/clang/lib/Analysis/ThreadSafetyCommon.cpp @@ -620,6 +620,8 @@ til::SExpr *SExprBuilder::translateCastExpr(const CastExpr *CE, // return new (Arena) til::Load(E0); } case CK_NoOp: + case CK_FunctionPointerConversion: + case CK_MemberFunctionPointerConversion: case CK_DerivedToBase: case CK_UncheckedDerivedToBase: case CK_ArrayToPointerDecay: diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index 563f728e29d781..e035ae819eecc7 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -697,6 +697,8 @@ static bool tryEmitARCCopyWeakInit(CodeGenFunction &CGF, switch (castExpr->getCastKind()) { // Look through casts that don't require representation changes. case CK_NoOp: + case CK_FunctionPointerConversion: + case CK_MemberFunctionPointerConversion: case CK_BitCast: case CK_BlockPointerToObjCPointerCast: needsCast = true; diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 35b5daaf6d4b55..95d0a764fdfb84 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -1297,6 +1297,8 @@ static Address EmitPointerWithAlignment(const Expr *E, LValueBaseInfo *BaseInfo, // Non-converting casts (but not C's implicit conversion from void*). case CK_BitCast: case CK_NoOp: + case CK_FunctionPointerConversion: + case CK_MemberFunctionPointerConversion: case CK_AddressSpaceConversion: if (auto PtrTy = CE->getSubExpr()->getType()->getAs<PointerType>()) { if (PtrTy->getPointeeType()->isVoidType()) @@ -5292,6 +5294,7 @@ LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) { case CK_IntegralComplexToBoolean: case CK_IntegralComplexCast: case CK_IntegralComplexToFloatingComplex: + case CK_MemberFunctionPointerConversion: case CK_DerivedToBaseMemberPointer: case CK_BaseToDerivedMemberPointer: case CK_MemberPointerToBoolean: @@ -5339,7 +5342,8 @@ LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) { case CK_LValueToRValue: return EmitLValue(E->getSubExpr()); - case CK_NoOp: { + case CK_NoOp: + case CK_FunctionPointerConversion: { // CK_NoOp can model a qualification conversion, which can remove an array // bound and change the IR type. // FIXME: Once pointee types are removed from IR, remove this. diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp index bbfc6672ecc25a..b30410da255e12 100644 --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -905,6 +905,8 @@ void AggExprEmitter::VisitCastExpr(CastExpr *E) { case CK_FunctionToPointerDecay: case CK_NullToPointer: case CK_NullToMemberPointer: + case CK_FunctionPointerConversion: + case CK_MemberFunctionPointerConversion: case CK_BaseToDerivedMemberPointer: case CK_DerivedToBaseMemberPointer: case CK_MemberPointerToBoolean: @@ -1424,6 +1426,8 @@ static bool castPreservesZero(const CastExpr *CE) { switch (CE->getCastKind()) { // No-ops. case CK_NoOp: + case CK_FunctionPointerConversion: + case CK_MemberFunctionPointerConversion: case CK_UserDefinedConversion: case CK_ConstructorConversion: case CK_BitCast: diff --git a/clang/lib/CodeGen/CGExprComplex.cpp b/clang/lib/CodeGen/CGExprComplex.cpp index fef26e7b4ccdbd..794b06ebf29935 100644 --- a/clang/lib/CodeGen/CGExprComplex.cpp +++ b/clang/lib/CodeGen/CGExprComplex.cpp @@ -560,6 +560,8 @@ ComplexPairTy ComplexExprEmitter::EmitCast(CastKind CK, Expr *Op, } case CK_BitCast: + case CK_FunctionPointerConversion: + case CK_MemberFunctionPointerConversion: case CK_BaseToDerived: case CK_DerivedToBase: case CK_UncheckedDerivedToBase: diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp index f22321f0e738a1..386ca154618473 100644 --- a/clang/lib/CodeGen/CGExprConstant.cpp +++ b/clang/lib/CodeGen/CGExprConstant.cpp @@ -1163,6 +1163,8 @@ class ConstExprEmitter case CK_AtomicToNonAtomic: case CK_NonAtomicToAtomic: case CK_NoOp: + case CK_FunctionPointerConversion: + case CK_MemberFunctionPointerConversion: case CK_ConstructorConversion: return Visit(subExpr, destType); diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 82caf65ac68d6b..5dd97c5a4b7b46 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -2418,7 +2418,9 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) { case CK_UserDefinedConversion: return Visit(const_cast<Expr*>(E)); - case CK_NoOp: { + case CK_NoOp: + case CK_FunctionPointerConversion: + case CK_MemberFunctionPointerConversion: { return CE->changesVolatileQualification() ? EmitLoadOfLValue(CE) : Visit(const_cast<Expr *>(E)); } diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp index bec0a29e34fcb5..b6e6555e63fca1 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.cpp +++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp @@ -338,7 +338,6 @@ void clang::CodeGen::CGHLSLRuntime::setHLSLEntryAttributes( NumThreadsAttr->getZ()); Fn->addFnAttr(NumThreadsKindStr, NumThreadsStr); } - Fn->addFnAttr(llvm::Attribute::NoInline); } static Value *buildVectorInput(IRBuilder<> &B, Function *F, llvm::Type *Ty) { diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 17b82b205063d4..ba2d6588900a11 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -2473,14 +2473,11 @@ void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D, B.addAttribute(llvm::Attribute::StackProtectReq); if (!D) { - // Non-entry HLSL functions must always be inlined. - if (getLangOpts().HLSL && !F->hasFnAttribute(llvm::Attribute::NoInline)) - B.addAttribute(llvm::Attribute::AlwaysInline); // If we don't have a declaration to control inlining, the function isn't // explicitly marked as alwaysinline for semantic reasons, and inlining is // disabled, mark the function as noinline. - else if (!F->hasFnAttribute(llvm::Attribute::AlwaysInline) && - CodeGenOpts.getInlining() == CodeGenOptions::OnlyAlwaysInlining) + if (!F->hasFnAttribute(llvm::Attribute::AlwaysInline) && + CodeGenOpts.getInlining() == CodeGenOptions::OnlyAlwaysInlining) B.addAttribute(llvm::Attribute::NoInline); F->addFnAttrs(B); @@ -2507,13 +2504,9 @@ void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D, ShouldAddOptNone &= !D->hasAttr<MinSizeAttr>(); ShouldAddOptNone &= !D->hasAttr<AlwaysInlineAttr>(); - // Non-entry HLSL functions must always be inlined. - if (getLangOpts().HLSL && !F->hasFnAttribute(llvm::Attribute::NoInline) && - !D->hasAttr<NoInlineAttr>()) { - B.addAttribute(llvm::Attribute::AlwaysInline); - } else if ((ShouldAddOptNone || D->hasAttr<OptimizeNoneAttr>()) && - !F->hasFnAttribute(llvm::Attribute::AlwaysInline)) { - // Add optnone, but do so only if the function isn't always_inline. + // Add optnone, but do so only if the function isn't always_inline. + if ((ShouldAddOptNone || D->hasAttr<OptimizeNoneAttr>()) && + !F->hasFnAttribute(llvm::Attribute::AlwaysInline)) { B.addAttribute(llvm::Attribute::OptimizeNone); // OptimizeNone implies noinline; we should not be inlining such functions. @@ -2533,8 +2526,7 @@ void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D, B.addAttribute(llvm::Attribute::NoInline); } else if (D->hasAttr<NoDuplicateAttr>()) { B.addAttribute(llvm::Attribute::NoDuplicate); - } else if (D->hasAttr<NoInlineAttr>() && - !F->hasFnAttribute(llvm::Attribute::AlwaysInline)) { + } else if (D->hasAttr<NoInlineAttr>() && !F->hasFnAttribute(llvm::Attribute::AlwaysInline)) { // Add noinline if the function isn't always_inline. B.addAttribute(llvm::Attribute::NoInline); } else if (D->hasAttr<AlwaysInlineAttr>() && diff --git a/clang/lib/Edit/RewriteObjCFoundationAPI.cpp b/clang/lib/Edit/RewriteObjCFoundationAPI.cpp index 81797c8c4dc75a..8443f47c2199b6 100644 --- a/clang/lib/Edit/RewriteObjCFoundationAPI.cpp +++ b/clang/lib/Edit/RewriteObjCFoundationAPI.cpp @@ -999,6 +999,8 @@ static bool rewriteToNumericBoxedExpression(const ObjCMessageExpr *Msg, switch (ICE->getCastKind()) { case CK_LValueToRValue: case CK_NoOp: + case CK_FunctionPointerConversion: + case CK_MemberFunctionPointerConversion: case CK_UserDefinedConversion: case CK_HLSLArrayRValue: break; diff --git a/clang/lib/Sema/CheckExprLifetime.cpp b/clang/lib/Sema/CheckExprLifetime.cpp index c98fbca849faba..8089dd7fd50095 100644 --- a/clang/lib/Sema/CheckExprLifetime.cpp +++ b/clang/lib/Sema/CheckExprLifetime.cpp @@ -673,6 +673,8 @@ static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path, // We assume that casts to 'bool' do not preserve enough information to // retain a local object. case CK_NoOp: + case CK_FunctionPointerConversion: + case CK_MemberFunctionPointerConversion: case CK_BitCast: case CK_BaseToDerived: case CK_DerivedToBase: diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 99500daca295c9..eb46743782cd7e 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -12976,6 +12976,8 @@ std::optional<std::pair< default: break; case CK_NoOp: + case CK_FunctionPointerConversion: + case CK_MemberFunctionPointerConversion: return getBaseAlignmentAndOffsetFromLValue(From, Ctx); case CK_UncheckedDerivedToBase: case CK_DerivedToBase: { @@ -13071,6 +13073,8 @@ std::optional<std::pair< default: break; case CK_NoOp: + case CK_FunctionPointerConversion: + case CK_MemberFunctionPointerConversion: return getBaseAlignmentAndOffsetFromPtr(From, Ctx); case CK_ArrayToPointerDecay: return getBaseAlignmentAndOffsetFromLValue(From, Ctx); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 8557c25b93a8da..7970b80a70d39a 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -12733,7 +12733,9 @@ namespace { if (ILE->getNumInits() == 1) ArgExpr = ILE->getInit(0); if (ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(ArgExpr)) - if (ICE->getCastKind() == CK_NoOp) + if (ICE->getCastKind() == CK_NoOp || + ICE->getCastKind() == CK_FunctionPointerConversion || + ICE->getCastKind() == CK_MemberFunctionPointerConversion) ArgExpr = ICE->getSubExpr(); HandleValue(ArgExpr); return; diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index d8cdfcf8c6ec05..59f4d5511b19e3 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -3878,7 +3878,9 @@ namespace { if (ILE->getNumInits() == 1) ArgExpr = ILE->getInit(0); if (ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(ArgExpr)) - if (ICE->getCastKind() == CK_NoOp) + if (ICE->getCastKind() == CK_NoOp || + ICE->getCastKind() == CK_FunctionPointerConversion || + ICE->getCastKind() == CK_MemberFunctionPointerConversion) ArgExpr = ICE->getSubExpr(); HandleValue(ArgExpr, false /*AddressOf*/); return; diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 80c252c79e4d7a..8e490a7e1a3565 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -19444,6 +19444,8 @@ static ExprResult rebuildPotentialResultsAsNonOdrUsed(Sema &S, Expr *E, // can be found. switch (ICE->getCastKind()) { case CK_NoOp: + case CK_FunctionPointerConversion: + case CK_MemberFunctionPointerConversion: case CK_DerivedToBase: case CK_UncheckedDerivedToBase: { ExprResult Sub = Rebuild(ICE->getSubExpr()); diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index ac3fe6ab8f9bd0..db0cfce7f3f177 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -4871,7 +4871,11 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, if (CheckExceptionSpecCompatibility(From, ToType)) return ExprError(); - From = ImpCastExprToType(From, ToType, CK_NoOp, VK_PRValue, + From = ImpCastExprToType(From, ToType, + ToType->isMemberFunctionPointerType() + ? CK_MemberFunctionPointerConversion + : CK_FunctionPointerConversion, + VK_PRValue, /*BasePath=*/nullptr, CCK) .get(); break; diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 4d11f2a43fcc6b..dc28b66f35f900 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -8016,8 +8016,8 @@ ExprResult InitializationSequence::Perform(Sema &S, case SK_FunctionReferenceConversion: assert(CurInit.get()->isLValue() && "function reference should be lvalue"); - CurInit = - S.ImpCastExprToType(CurInit.get(), Step->Type, CK_NoOp, VK_LValue); + CurInit = S.ImpCastExprToType(CurInit.get(), Step->Type, + CK_FunctionPointerConversion, VK_LValue); break; case SK_AtomicConversion: { diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp index b952ffbd69f5d5..9afb8cea26fe78 100644 --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -2861,113 +2861,120 @@ void SemaOpenMP::EndOpenMPDSABlock(Stmt *CurDirective) { // clause requires an accessible, unambiguous default constructor for the // class type, unless the list item is also specified in a firstprivate // clause. - if (const auto *D = dyn_cast_or_null<OMPExecutableDirective>(CurDirective)) { - for (OMPClause *C : D->clauses()) { - if (auto *Clause = dyn_cast<OMPLastprivateClause>(C)) { - SmallVector<Expr *, 8> PrivateCopies; - for (Expr *DE : Clause->varlist()) { - if (DE->isValueDependent() || DE->isTypeDependent()) { - PrivateCopies.push_back(nullptr); - continue; - } - auto *DRE = cast<DeclRefExpr>(DE->IgnoreParens()); - auto *VD = cast<VarDecl>(DRE->getDecl()); - QualType Type = VD->getType().getNonReferenceType(); - const DSAStackTy::DSAVarData DVar = - DSAStack->getTopDSA(VD, /*FromParent=*/false); - if (DVar.CKind == OMPC_lastprivate) { - // Generate helper private variable and initialize it with the - // default value. The address of the original variable is replaced - // by the address of the new private variable in CodeGen. This new - // variable is not added to IdResolver, so the code in the OpenMP - // region uses original variable for proper diagnostics. - VarDecl *VDPrivate = buildVarDecl( - SemaRef, DE->getExprLoc(), Type.getUnqualifiedType(), - VD->getName(), VD->hasAttrs() ? &VD->getAttrs() : nullptr, DRE); - SemaRef.ActOnUninitializedDecl(VDPrivate); - if (VDPrivate->isInvalidDecl()) { - PrivateCopies.push_back(nullptr); - continue; - } - PrivateCopies.push_back(buildDeclRefExpr( - SemaRef, VDPrivate, DE->getType(), DE->getExprLoc())); - } else { - // The variable is also a firstprivate, so initialization sequence - // for private copy is generated already. - PrivateCopies.push_back(nullptr); - } - } - Clause->setPrivateCopies(PrivateCopies); + + auto FinalizeLastprivate = [&](OMPLastprivateClause *Clause) { + SmallVector<Expr *, 8> PrivateCopies; + for (Expr *DE : Clause->varlist()) { + if (DE->isValueDependent() || DE->isTypeDependent()) { + PrivateCopies.push_back(nullptr); continue; } - // Finalize nontemporal clause by handling private copies, if any. - if (auto *Clause = dyn_cast<OMPNontemporalClause>(C)) { - SmallVector<Expr *, 8> PrivateRefs; - for (Expr *RefExpr : Clause->varlist()) { - assert(RefExpr && "NULL expr in OpenMP nontemporal clause."); - SourceLocation ELoc; - SourceRange ERange; - Expr *SimpleRefExpr = RefExpr; - auto Res = getPrivateItem(SemaRef, SimpleRefExpr, ELoc, ERange); - if (Res.second) - // It will be analyzed later. - PrivateRefs.push_back(RefExpr); - ValueDecl *D = Res.first; - if (!D) - continue; - - const DSAStackTy::DSAVarData DVar = - DSAStack->getTopDSA(D, /*FromParent=*/false); - PrivateRefs.push_back(DVar.PrivateCopy ? DVar.PrivateCopy - : SimpleRefExpr); - } - Clause->setPrivateRefs(PrivateRefs); + auto *DRE = cast<DeclRefExpr>(DE->IgnoreParens()); + auto *VD = cast<VarDecl>(DRE->getDecl()); + QualType Type = VD->getType().getNonReferenceType(); + const DSAStackTy::DSAVarData DVar = + DSAStack->getTopDSA(VD, /*FromParent=*/false); + if (DVar.CKind != OMPC_lastprivate) { + // The variable is also a firstprivate, so initialization sequence + // for private copy is generated already. + PrivateCopies.push_back(nullptr); continue; } - if (auto *Clause = dyn_cast<OMPUsesAllocatorsClause>(C)) { - for (unsigned I = 0, E = Clause->getNumberOfAllocators(); I < E; ++I) { - OMPUsesAllocatorsClause::Data D = Clause->getAllocatorData(I); - auto *DRE = dyn_cast<DeclRefExpr>(D.Allocator->IgnoreParenImpCasts()); - if (!DRE) - continue; - ValueDecl *VD = DRE->getDecl(); - if (!VD || !isa<VarDecl>(VD)) - continue; - DSAStackTy::DSAVarData DVar = - DSAStack->getTopDSA(VD, /*FromParent=*/false); - // OpenMP [2.12.5, target Construct] - // Memory allocators that appear in a uses_allocators clause cannot - // appear in other data-sharing attribute clauses or data-mapping - // attribute clauses in the same construct. - Expr *MapExpr = nullptr; - if (DVar.RefExpr || - DSAStack->checkMappableExprComponentListsForDecl( - VD, /*CurrentRegionOnly=*/true, - [VD, &MapExpr]( - OMPClauseMappableExprCommon::MappableExprComponentListRef - MapExprComponents, - OpenMPClauseKind C) { - auto MI = MapExprComponents.rbegin(); - auto ME = MapExprComponents.rend(); - if (MI != ME && - MI->getAssociatedDeclaration()->getCanonicalDecl() == - VD->getCanonicalDecl()) { - MapExpr = MI->getAssociatedExpression(); - return true; - } - return false; - })) { - Diag(D.Allocator->getExprLoc(), - diag::err_omp_allocator_used_in_clauses) - << D.Allocator->getSourceRange(); - if (DVar.RefExpr) - reportOriginalDsa(SemaRef, DSAStack, VD, DVar); - else - Diag(MapExpr->getExprLoc(), diag::note_used_here) - << MapExpr->getSourceRange(); - } - } + // Generate helper private variable and initialize it with the + // default value. The address of the original variable is replaced + // by the address of the new private variable in CodeGen. This new + // variable is not added to IdResolver, so the code in the OpenMP + // region uses original variable for proper diagnostics. + VarDecl *VDPrivate = buildVarDecl( + SemaRef, DE->getExprLoc(), Type.getUnqualifiedType(), VD->getName(), + VD->hasAttrs() ? &VD->getAttrs() : nullptr, DRE); + SemaRef.ActOnUninitializedDecl(VDPrivate); + if (VDPrivate->isInvalidDecl()) { + PrivateCopies.push_back(nullptr); + continue; + } + PrivateCopies.push_back(buildDeclRefExpr( + SemaRef, VDPrivate, DE->getType(), DE->getExprLoc())); + } + Clause->setPrivateCopies(PrivateCopies); + }; + + auto FinalizeNontemporal = [&](OMPNontemporalClause *Clause) { + // Finalize nontemporal clause by handling private copies, if any. + SmallVector<Expr *, 8> PrivateRefs; + for (Expr *RefExpr : Clause->varlist()) { + assert(RefExpr && "NULL expr in OpenMP nontemporal clause."); + SourceLocation ELoc; + SourceRange ERange; + Expr *SimpleRefExpr = RefExpr; + auto Res = getPrivateItem(SemaRef, SimpleRefExpr, ELoc, ERange); + if (Res.second) + // It will be analyzed later. + PrivateRefs.push_back(RefExpr); + ValueDecl *D = Res.first; + if (!D) continue; + + const DSAStackTy::DSAVarData DVar = + DSAStack->getTopDSA(D, /*FromParent=*/false); + PrivateRefs.push_back(DVar.PrivateCopy ? DVar.PrivateCopy + : SimpleRefExpr); + } + Clause->setPrivateRefs(PrivateRefs); + }; + + auto FinalizeAllocators = [&](OMPUsesAllocatorsClause *Clause) { + for (unsigned I = 0, E = Clause->getNumberOfAllocators(); I < E; ++I) { + OMPUsesAllocatorsClause::Data D = Clause->getAllocatorData(I); + auto *DRE = dyn_cast<DeclRefExpr>(D.Allocator->IgnoreParenImpCasts()); + if (!DRE) + continue; + ValueDecl *VD = DRE->getDecl(); + if (!VD || !isa<VarDecl>(VD)) + continue; + DSAStackTy::DSAVarData DVar = + DSAStack->getTopDSA(VD, /*FromParent=*/false); + // OpenMP [2.12.5, target Construct] + // Memory allocators that appear in a uses_allocators clause cannot + // appear in other data-sharing attribute clauses or data-mapping + // attribute clauses in the same construct. + Expr *MapExpr = nullptr; + if (DVar.RefExpr || + DSAStack->checkMappableExprComponentListsForDecl( + VD, /*CurrentRegionOnly=*/true, + [VD, &MapExpr]( + OMPClauseMappableExprCommon::MappableExprComponentListRef + MapExprComponents, + OpenMPClauseKind C) { + auto MI = MapExprComponents.rbegin(); + auto ME = MapExprComponents.rend(); + if (MI != ME && + MI->getAssociatedDeclaration()->getCanonicalDecl() == + VD->getCanonicalDecl()) { + MapExpr = MI->getAssociatedExpression(); + return true; + } + return false; + })) { + Diag(D.Allocator->getExprLoc(), diag::err_omp_allocator_used_in_clauses) + << D.Allocator->getSourceRange(); + if (DVar.RefExpr) + reportOriginalDsa(SemaRef, DSAStack, VD, DVar); + else + Diag(MapExpr->getExprLoc(), diag::note_used_here) + << MapExpr->getSourceRange(); + } + } + }; + + if (const auto *D = dyn_cast_or_null<OMPExecutableDirective>(CurDirective)) { + for (OMPClause *C : D->clauses()) { + if (auto *Clause = dyn_cast<OMPLastprivateClause>(C)) { + FinalizeLastprivate(Clause); + } else if (auto *Clause = dyn_cast<OMPNontemporalClause>(C)) { + FinalizeNontemporal(Clause); + } else if (auto *Clause = dyn_cast<OMPUsesAllocatorsClause>(C)) { + FinalizeAllocators(Clause); } } // Check allocate clauses. diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index d304f322aced64..2e36f652baa5df 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -330,6 +330,8 @@ static const Expr *IgnoreNarrowingConversion(ASTContext &Ctx, while (auto *ICE = dyn_cast<ImplicitCastExpr>(Converted)) { switch (ICE->getCastKind()) { case CK_NoOp: + case CK_FunctionPointerConversion: + case CK_MemberFunctionPointerConversion: case CK_IntegralCast: case CK_IntegralToBoolean: case CK_IntegralToFloating: @@ -14206,7 +14208,9 @@ ExprResult Sema::BuildCXXMemberCallExpr(Expr *E, NamedDecl *FoundDecl, // was a LambdaExpr. Expr *SubE = E; auto *CE = dyn_cast<CastExpr>(SubE); - if (CE && CE->getCastKind() == CK_NoOp) + if (CE && (CE->getCastKind() == CK_NoOp || + CE->getCastKind() == CK_FunctionPointerConversion || + CE->getCastKind() == CK_MemberFunctionPointerConversion)) SubE = CE->getSubExpr(); SubE = SubE->IgnoreParens(); if (auto *BE = dyn_cast<CXXBindTemporaryExpr>(SubE)) diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 9664287b9a3fe9..b8c36eee7ce066 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -281,6 +281,8 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S, unsigned DiagID) { E = WarnExpr; if (const auto *Cast = dyn_cast<CastExpr>(E)) if (Cast->getCastKind() == CK_NoOp || + Cast->getCastKind() == CK_FunctionPointerConversion || + Cast->getCastKind() == CK_MemberFunctionPointerConversion || Cast->getCastKind() == CK_ConstructorConversion || Cast->getCastKind() == CK_IntegralCast) E = Cast->getSubExpr()->IgnoreImpCasts(); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp index 7a900780384a91..ad021d12a7fd43 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -327,6 +327,8 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, case CK_NonAtomicToAtomic: // True no-ops. case CK_NoOp: + case CK_FunctionPointerConversion: + case CK_MemberFunctionPointerConversion: case CK_ConstructorConversion: case CK_UserDefinedConversion: case CK_FunctionToPointerDecay: diff --git a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp index 7eca0579143f44..341d794aabdcc2 100644 --- a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp +++ b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp @@ -397,6 +397,8 @@ std::optional<SVal> SValBuilder::getConstantVal(const Expr *E) { case CK_ArrayToPointerDecay: case CK_IntegralToPointer: case CK_NoOp: + case CK_FunctionPointerConversion: + case CK_MemberFunctionPointerConversion: case CK_BitCast: { const Expr *SE = CE->getSubExpr(); std::optional<SVal> Val = getConstantVal(SE); diff --git a/clang/test/AST/ast-dump-function-pointer-conversion.cpp b/clang/test/AST/ast-dump-function-pointer-conversion.cpp new file mode 100644 index 00000000000000..4c545e5bc40524 --- /dev/null +++ b/clang/test/AST/ast-dump-function-pointer-conversion.cpp @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -std=c++17 -ast-dump %s | FileCheck --check-prefixes=CHECK,CXX17 %s +// RUN: %clang_cc1 -std=c++11 -ast-dump %s | FileCheck %s + +void f() noexcept; + +struct S { + void m() noexcept; +}; + +// CHECK: FunctionDecl {{.*}} testFunctionPointerConversion 'void ()' +// CHECK-NEXT: CompoundStmt +// CHECK-NEXT: DeclStmt +// CHECK-NEXT: VarDecl {{.*}} fp 'void (*)()' cinit +// CXX17-NEXT: ImplicitCastExpr {{.*}} 'void (*)()' <FunctionPointerConversion> +// CHECK-NEXT: UnaryOperator {{.*}} 'void (*)() noexcept' prefix '&' +// CHECK-NEXT: DeclRefExpr {{.*}} 'void () noexcept' lvalue Function {{.*}} 'f' 'void () noexcept' +void testFunctionPointerConversion() { + void (*fp)() = &f; +} + +// CHECK: FunctionDecl {{.*}} testMemberFunctionPointerConversion 'void ()' +// CHECK-NEXT: CompoundStmt +// CHECK-NEXT: DeclStmt +// CHECK-NEXT: VarDecl {{.*}} mfp 'void (S::*)()' cinit +// CXX17-NEXT: ImplicitCastExpr {{.*}} 'void (S::*)()' <MemberFunctionPointerConversion> +// CHECK-NEXT: UnaryOperator {{.*}} 'void (S::*)() noexcept' prefix '&' +// CHECK-NEXT: DeclRefExpr {{.*}} 'void () noexcept' CXXMethod {{.*}} 'm' 'void () noexcept' +void testMemberFunctionPointerConversion() { + void (S::*mfp)() = &S::m; +} diff --git a/clang/test/CXX/dcl.decl/dcl.init/dcl.init.ref/p4-ast.cpp b/clang/test/CXX/dcl.decl/dcl.init/dcl.init.ref/p4-ast.cpp index 32c4ddd921bba1..a6209337a08864 100644 --- a/clang/test/CXX/dcl.decl/dcl.init/dcl.init.ref/p4-ast.cpp +++ b/clang/test/CXX/dcl.decl/dcl.init/dcl.init.ref/p4-ast.cpp @@ -3,7 +3,7 @@ void f() noexcept; // CHECK: VarDecl {{.*}} ref 'void (&)()' cinit -// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void ()' lvalue <NoOp> +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void ()' lvalue <FunctionPointerConversion> // CHECK-NEXT: DeclRefExpr {{.*}} 'void () noexcept' lvalue Function {{.*}} 'f' 'void () noexcept' void (&ref)() = f; @@ -13,6 +13,6 @@ struct X { } x; // CHECK: VarDecl {{.*}} xp 'void (&)()' cinit -// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void ()' lvalue <NoOp> +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void ()' lvalue <FunctionPointerConversion> // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void () noexcept' lvalue <UserDefinedConversion> void (&xp)() = x; diff --git a/clang/test/CodeGenHLSL/GlobalConstructorFunction.hlsl b/clang/test/CodeGenHLSL/GlobalConstructorFunction.hlsl index b39311ad67cd62..f954c9d2f029f2 100644 --- a/clang/test/CodeGenHLSL/GlobalConstructorFunction.hlsl +++ b/clang/test/CodeGenHLSL/GlobalConstructorFunction.hlsl @@ -1,5 +1,4 @@ -// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -emit-llvm -disable-llvm-passes %s -o - | FileCheck %s --check-prefixes=CHECK,NOINLINE -// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -emit-llvm -O0 %s -o - | FileCheck %s --check-prefixes=CHECK,INLINE +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -emit-llvm -disable-llvm-passes %s -o - | FileCheck %s int i; @@ -8,7 +7,7 @@ __attribute__((constructor)) void call_me_first(void) { } __attribute__((constructor)) void then_call_me(void) { - i = 13; + i = 12; } __attribute__((destructor)) void call_me_last(void) { @@ -22,21 +21,11 @@ void main(unsigned GI : SV_GroupIndex) {} // CHECK-NOT:@llvm.global_ctors // CHECK-NOT:@llvm.global_dtors -// CHECK: define void @main() -// CHECK-NEXT: entry: -// Verify function constructors are emitted -// NOINLINE-NEXT: call void @"?call_me_first@@YAXXZ"() -// NOINLINE-NEXT: call void @"?then_call_me@@YAXXZ"() -// NOINLINE-NEXT: %0 = call i32 @llvm.dx.flattened.thread.id.in.group() -// NOINLINE-NEXT: call void @"?main@@YAXI@Z"(i32 %0) -// NOINLINE-NEXT: call void @"?call_me_last@@YAXXZ"( -// NOINLINE-NEXT: ret void - -// Verify constructor calls are inlined when AlwaysInline is run -// INLINE-NEXT: alloca -// INLINE-NEXT: store i32 12 -// INLINE-NEXT: store i32 13 -// INLINE-NEXT: %0 = call i32 @llvm.dx.flattened.thread.id.in.group() -// INLINE-NEXT: store i32 % -// INLINE-NEXT: store i32 0 -// INLINE: ret void +//CHECK: define void @main() +//CHECK-NEXT: entry: +//CHECK-NEXT: call void @"?call_me_first@@YAXXZ"() +//CHECK-NEXT: call void @"?then_call_me@@YAXXZ"() +//CHECK-NEXT: %0 = call i32 @llvm.dx.flattened.thread.id.in.group() +//CHECK-NEXT: call void @"?main@@YAXI@Z"(i32 %0) +//CHECK-NEXT: call void @"?call_me_last@@YAXXZ"( +//CHECK-NEXT: ret void diff --git a/clang/test/CodeGenHLSL/GlobalConstructorLib.hlsl b/clang/test/CodeGenHLSL/GlobalConstructorLib.hlsl index 78f6475462bc47..2c5c4e19c3296d 100644 --- a/clang/test/CodeGenHLSL/GlobalConstructorLib.hlsl +++ b/clang/test/CodeGenHLSL/GlobalConstructorLib.hlsl @@ -1,5 +1,4 @@ -// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -emit-llvm -disable-llvm-passes %s -o - | FileCheck %s --check-prefixes=CHECK,NOINLINE -// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -emit-llvm -O0 %s -o - | FileCheck %s --check-prefixes=CHECK,INLINE +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -emit-llvm -disable-llvm-passes %s -o - | FileCheck %s // Make sure global variable for ctors exist for lib profile. // CHECK:@llvm.global_ctors @@ -12,11 +11,7 @@ void FirstEntry() {} // CHECK: define void @FirstEntry() // CHECK-NEXT: entry: -// NOINLINE-NEXT: call void @_GLOBAL__sub_I_GlobalConstructorLib.hlsl() -// NOINLINE-NEXT: call void @"?FirstEntry@@YAXXZ"() -// Verify inlining leaves only calls to "llvm." intrinsics -// INLINE-NOT: call {{[^@]*}} @{{[^l][^l][^v][^m][^\.]}} -// CHECK: ret void +// CHECK-NEXT: call void @_GLOBAL__sub_I_GlobalConstructorLib.hlsl() [shader("compute")] [numthreads(1,1,1)] @@ -24,15 +19,5 @@ void SecondEntry() {} // CHECK: define void @SecondEntry() // CHECK-NEXT: entry: -// NOINLINE-NEXT: call void @_GLOBAL__sub_I_GlobalConstructorLib.hlsl() -// NOINLINE-NEXT: call void @"?SecondEntry@@YAXXZ"() -// Verify inlining leaves only calls to "llvm." intrinsics -// INLINE-NOT: call {{[^@]*}} @{{[^l][^l][^v][^m][^\.]}} -// CHECK: ret void - - -// Verify the constructor is alwaysinline -// NOINLINE: ; Function Attrs: {{.*}}alwaysinline -// NOINLINE-NEXT: define internal void @_GLOBAL__sub_I_GlobalConstructorLib.hlsl() [[IntAttr:\#[0-9]+]] - -// NOINLINE: attributes [[IntAttr]] = {{.*}} alwaysinline +// CHECK-NEXT: call void @_GLOBAL__sub_I_GlobalConstructorLib.hlsl() +// CHECK-NEXT: call void @"?SecondEntry@@YAXXZ"() diff --git a/clang/test/CodeGenHLSL/GlobalDestructors.hlsl b/clang/test/CodeGenHLSL/GlobalDestructors.hlsl index ea28354222f885..24c3c039fc6192 100644 --- a/clang/test/CodeGenHLSL/GlobalDestructors.hlsl +++ b/clang/test/CodeGenHLSL/GlobalDestructors.hlsl @@ -1,18 +1,10 @@ -// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -std=hlsl202x -emit-llvm -disable-llvm-passes %s -o - | FileCheck %s --check-prefixes=CS,NOINLINE,CHECK -// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -std=hlsl202x -emit-llvm -disable-llvm-passes %s -o - | FileCheck %s --check-prefixes=LIB,NOINLINE,CHECK -// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -std=hlsl202x -emit-llvm -O0 %s -o - | FileCheck %s --check-prefixes=INLINE,CHECK -// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -std=hlsl202x -emit-llvm -O0 %s -o - | FileCheck %s --check-prefixes=INLINE,CHECK +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -std=hlsl202x -emit-llvm -disable-llvm-passes %s -o - | FileCheck %s --check-prefixes=CS,CHECK +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -std=hlsl202x -emit-llvm -disable-llvm-passes %s -o - | FileCheck %s --check-prefixes=LIB,CHECK -// Tests that constructors and destructors are appropriately generated for globals -// and that their calls are inlined when AlwaysInline is run -// but global variables are retained for the library profiles - -// Make sure global variable for ctors/dtors exist for lib profile. -// LIB:@llvm.global_ctors +// Make sure global variable for dtors exist for lib profile. // LIB:@llvm.global_dtors -// Make sure global variable for ctors/dtors removed for compute profile. -// CS-NOT:@llvm.global_ctors -// CS-NOT:@llvm.global_dtors +// Make sure global variable for dtors removed for compute profile. +// CS-NOT:llvm.global_dtors struct Tail { Tail() { @@ -54,25 +46,22 @@ void main(unsigned GI : SV_GroupIndex) { Wag(); } -// CHECK: define void @main() -// CHECK-NEXT: entry: -// Verify destructor is emitted -// NOINLINE-NEXT: call void @_GLOBAL__sub_I_GlobalDestructors.hlsl() -// NOINLINE-NEXT: %0 = call i32 @llvm.dx.flattened.thread.id.in.group() -// NOINLINE-NEXT: call void @"?main@@YAXI@Z"(i32 %0) -// NOINLINE-NEXT: call void @_GLOBAL__D_a() -// NOINLINE-NEXT: ret void -// Verify inlining leaves only calls to "llvm." intrinsics -// INLINE-NOT: call {{[^@]*}} @{{[^l][^l][^v][^m][^\.]}} -// INLINE: ret void +// Make sure global variable for ctors/dtors removed. +// CHECK-NOT:@llvm.global_ctors +// CHECK-NOT:@llvm.global_dtors +//CHECK: define void @main() +//CHECK-NEXT: entry: +//CHECK-NEXT: call void @_GLOBAL__sub_I_GlobalDestructors.hlsl() +//CHECK-NEXT: %0 = call i32 @llvm.dx.flattened.thread.id.in.group() +//CHECK-NEXT: call void @"?main@@YAXI@Z"(i32 %0) +//CHECK-NEXT: call void @_GLOBAL__D_a() +//CHECK-NEXT: ret void // This is really just a sanity check I needed for myself to verify that // function scope static variables also get destroyed properly. -// NOINLINE: define internal void @_GLOBAL__D_a() [[IntAttr:\#[0-9]+]] -// NOINLINE-NEXT: entry: -// NOINLINE-NEXT: call void @"??1Tail@@QAA@XZ"(ptr @"?T@?1??Wag@@YAXXZ@4UTail@@A") -// NOINLINE-NEXT: call void @"??1Pupper@@QAA@XZ"(ptr @"?GlobalPup@@3UPupper@@A") -// NOINLINE-NEXT: ret void - -// NOINLINE: attributes [[IntAttr]] = {{.*}} alwaysinline +//CHECK: define internal void @_GLOBAL__D_a() +//CHECK-NEXT: entry: +//CHECK-NEXT: call void @"??1Tail@@QAA@XZ"(ptr @"?T@?1??Wag@@YAXXZ@4UTail@@A") +//CHECK-NEXT: call void @"??1Pupper@@QAA@XZ"(ptr @"?GlobalPup@@3UPupper@@A") +//CHECK-NEXT: ret void diff --git a/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl b/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl index 174f4c3eaaad26..baddfcf2cf1d52 100644 --- a/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl +++ b/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl @@ -1,4 +1,3 @@ -// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s // RUN: %clang_cc1 -triple spirv-vulkan-library -x hlsl -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s --check-prefix=CHECK-SPIRV RWBuffer<float> Buf; diff --git a/clang/test/CodeGenHLSL/builtins/RWBuffer-subscript.hlsl b/clang/test/CodeGenHLSL/builtins/RWBuffer-subscript.hlsl index 2a350c1619bd6e..da8a1e538ec5e7 100644 --- a/clang/test/CodeGenHLSL/builtins/RWBuffer-subscript.hlsl +++ b/clang/test/CodeGenHLSL/builtins/RWBuffer-subscript.hlsl @@ -11,7 +11,6 @@ void main(unsigned GI : SV_GroupIndex) { // Even at -O0 the subscript operators get inlined. The -O0 IR is a bit messy // and confusing to follow so the match here is pretty weak. -// CHECK: define void @main() -// Verify inlining leaves only calls to "llvm." intrinsics -// CHECK-NOT: call {{[^@]*}} @{{[^l][^l][^v][^m][^\.]}} +// CHECK: define internal void @"?main@@YAXI@Z" +// CHECK-NOT: call // CHECK: ret void diff --git a/clang/test/CodeGenHLSL/inline-constructors.hlsl b/clang/test/CodeGenHLSL/inline-constructors.hlsl deleted file mode 100644 index 995878a9c0f798..00000000000000 --- a/clang/test/CodeGenHLSL/inline-constructors.hlsl +++ /dev/null @@ -1,76 +0,0 @@ -// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -std=hlsl202x -emit-llvm -o - -disable-llvm-passes %s | FileCheck %s --check-prefixes=CHECK,NOINLINE -// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -std=hlsl202x -emit-llvm -o - -disable-llvm-passes %s | FileCheck %s --check-prefixes=CHECK,NOINLINE -// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -std=hlsl202x -emit-llvm -o - -O0 %s | FileCheck %s --check-prefixes=CHECK,INLINE -// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -std=hlsl202x -emit-llvm -o - -O0 %s | FileCheck %s --check-prefixes=CHECK,INLINE -// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -std=hlsl202x -emit-llvm -o - -O1 %s | FileCheck %s --check-prefixes=CHECK,INLINE -// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -std=hlsl202x -emit-llvm -o - -O1 %s | FileCheck %s --check-prefixes=CHECK,INLINE - -// Tests that implicit constructor calls for user classes will always be inlined. - -struct Weed { - Weed() {Count += 1;} - [[maybe_unused]] void pull() {Count--;} - static int weedCount() { return Count; } -private: - static int Count; - -} YardWeeds; - -int Weed::Count = 1; // It begins. . . - -struct Kitty { - unsigned burrsInFur; - - Kitty() { - burrsInFur = 0; - } - - void wanderInYard(int hours) { - burrsInFur = hours*Weed::weedCount()/8; - } - - void lick() { - if(burrsInFur) { - burrsInFur--; - Weed w; - } - } - -} Nion; - -void NionsDay(int hours) { - static Kitty Nion; - Nion.wanderInYard(hours); - while(Nion.burrsInFur) Nion.lick(); -} - -// CHECK: define void @main() -// CHECK-NEXT: entry: -// Verify constructor is emitted -// NOINLINE-NEXT: call void @_GLOBAL__sub_I_inline_constructors.hlsl() -// NOINLINE-NEXT: %0 = call i32 @llvm.dx.flattened.thread.id.in.group() -// NOINLINE-NEXT: call void @"?main@@YAXI@Z"(i32 %0) -// Verify inlining leaves only calls to "llvm." intrinsics -// INLINE-NOT: call {{[^@]*}} @{{[^l][^l][^v][^m][^\.]}} -// CHECK: ret void -[shader("compute")] -[numthreads(1,1,1)] -void main(unsigned GI : SV_GroupIndex) { - NionsDay(10); -} - - -// CHECK: define void @rainyMain() -// CHECK-NEXT: entry: -// Verify constructor is emitted -// NOINLINE-NEXT: call void @_GLOBAL__sub_I_inline_constructors.hlsl() -// NOINLINE-NEXT: call void @"?rainyMain@@YAXXZ"() -// Verify inlining leaves only calls to "llvm." intrinsics -// INLINE-NOT: call {{[^@]*}} @{{[^l][^l][^v][^m][^\.]}} -// CHECK: ret void -[shader("compute")] -[numthreads(1,1,1)] -void rainyMain() { - NionsDay(1); -} - diff --git a/clang/test/CodeGenHLSL/inline-functions.hlsl b/clang/test/CodeGenHLSL/inline-functions.hlsl deleted file mode 100644 index 7dd905e966e069..00000000000000 --- a/clang/test/CodeGenHLSL/inline-functions.hlsl +++ /dev/null @@ -1,116 +0,0 @@ -// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library %s -emit-llvm -disable-llvm-passes -o - | FileCheck %s --check-prefixes=CHECK,NOINLINE -// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library %s -emit-llvm -O0 -o - | FileCheck %s --check-prefixes=CHECK,INLINE -// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library %s -emit-llvm -O1 -o - | FileCheck %s --check-prefixes=CHECK,INLINE -// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute %s -emit-llvm -disable-llvm-passes -o - | FileCheck %s --check-prefixes=CHECK,NOINLINE -// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute %s -emit-llvm -O0 -o - | FileCheck %s --check-prefixes=CHECK,INLINE -// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute %s -emit-llvm -O1 -o - | FileCheck %s --check-prefixes=CHECK,INLINE - -// Tests that user functions will always be inlined. -// This includes exported functions and mangled entry point implementation functions. -// The unmangled entry functions must not be alwaysinlined. - -#define MAX 100 - -float nums[MAX]; - -// Verify that all functions have the alwaysinline attribute -// NOINLINE: Function Attrs: alwaysinline -// NOINLINE: define void @"?swap@@YAXY0GE@III@Z"(ptr noundef byval([100 x i32]) align 4 %Buf, i32 noundef %ix1, i32 noundef %ix2) [[IntAttr:\#[0-9]+]] -// NOINLINE: ret void -// Swap the values of Buf at indices ix1 and ix2 -void swap(unsigned Buf[MAX], unsigned ix1, unsigned ix2) { - float tmp = Buf[ix1]; - Buf[ix1] = Buf[ix2]; - Buf[ix2] = tmp; -} - -// NOINLINE: Function Attrs: alwaysinline -// NOINLINE: define void @"?BubbleSort@@YAXY0GE@II@Z"(ptr noundef byval([100 x i32]) align 4 %Buf, i32 noundef %size) [[IntAttr]] -// NOINLINE: ret void -// Inefficiently sort Buf in place -void BubbleSort(unsigned Buf[MAX], unsigned size) { - bool swapped = true; - while (swapped) { - swapped = false; - for (unsigned i = 1; i < size; i++) { - if (Buf[i] < Buf[i-1]) { - swap(Buf, i, i-1); - swapped = true; - } - } - } -} - -// Note ExtAttr is the inlined export set of attribs -// CHECK: Function Attrs: alwaysinline -// CHECK: define noundef i32 @"?RemoveDupes@@YAIY0GE@II@Z"(ptr {{[a-z_ ]*}}noundef byval([100 x i32]) align 4 %Buf, i32 noundef %size) {{[a-z_ ]*}}[[ExtAttr:\#[0-9]+]] -// CHECK: ret i32 -// Sort Buf and remove any duplicate values -// returns the number of values left -export -unsigned RemoveDupes(unsigned Buf[MAX], unsigned size) { - BubbleSort(Buf, size); - unsigned insertPt = 0; - for (unsigned i = 1; i < size; i++) { - if (Buf[i] == Buf[i-1]) - insertPt++; - else - Buf[insertPt] = Buf[i]; - } - return insertPt; -} - - -RWBuffer<unsigned> Indices; - -// The mangled version of main only remains without inlining -// because it has internal linkage from the start -// Note main functions get the norecurse attrib, which IntAttr reflects -// NOINLINE: Function Attrs: alwaysinline -// NOINLINE: define internal void @"?main@@YAXI@Z"(i32 noundef %GI) [[IntAttr]] -// NOINLINE: ret void - -// The unmangled version is not inlined, EntryAttr reflects that -// CHECK: Function Attrs: {{.*}}noinline -// CHECK: define void @main() {{[a-z_ ]*}}[[EntryAttr:\#[0-9]+]] -// Make sure function calls are inlined when AlwaysInline is run -// This only leaves calls to llvm. intrinsics -// INLINE-NOT: call {{[^@]*}} @{{[^l][^l][^v][^m][^\.]}} -// CHECK: ret void - -[numthreads(1,1,1)] -[shader("compute")] -void main(unsigned int GI : SV_GroupIndex) { - unsigned tmpIndices[MAX]; - if (GI > MAX) return; - for (unsigned i = 1; i < GI; i++) - tmpIndices[i] = Indices[i]; - RemoveDupes(tmpIndices, GI); - for (unsigned i = 1; i < GI; i++) - tmpIndices[i] = Indices[i]; -} - -// The mangled version of main only remains without inlining -// because it has internal linkage from the start -// Note main functions get the norecurse attrib, which IntAttr reflects -// NOINLINE: Function Attrs: alwaysinline -// NOINLINE: define internal void @"?main10@@YAXXZ"() [[IntAttr]] -// NOINLINE: ret void - -// The unmangled version is not inlined, EntryAttr reflects that -// CHECK: Function Attrs: {{.*}}noinline -// CHECK: define void @main10() {{[a-z_ ]*}}[[EntryAttr]] -// Make sure function calls are inlined when AlwaysInline is run -// This only leaves calls to llvm. intrinsics -// INLINE-NOT: call {{[^@]*}} @{{[^l][^l][^v][^m][^\.]}} -// CHECK: ret void - -[numthreads(1,1,1)] -[shader("compute")] -void main10() { - main(10); -} - -// NOINLINE: attributes [[IntAttr]] = {{.*}} alwaysinline -// CHECK: attributes [[ExtAttr]] = {{.*}} alwaysinline -// CHECK: attributes [[EntryAttr]] = {{.*}} noinline diff --git a/llvm/include/llvm/CodeGen/VirtRegMap.h b/llvm/include/llvm/CodeGen/VirtRegMap.h index dee462255b0b28..52221762fed5d0 100644 --- a/llvm/include/llvm/CodeGen/VirtRegMap.h +++ b/llvm/include/llvm/CodeGen/VirtRegMap.h @@ -31,12 +31,6 @@ class raw_ostream; class TargetInstrInfo; class VirtRegMap : public MachineFunctionPass { - public: - enum { - NO_STACK_SLOT = (1L << 30)-1, - }; - - private: MachineRegisterInfo *MRI = nullptr; const TargetInstrInfo *TII = nullptr; const TargetRegisterInfo *TRI = nullptr; @@ -69,6 +63,8 @@ class TargetInstrInfo; public: static char ID; + static constexpr int NO_STACK_SLOT = INT_MAX; + VirtRegMap() : MachineFunctionPass(ID), Virt2StackSlotMap(NO_STACK_SLOT) {} VirtRegMap(const VirtRegMap &) = delete; VirtRegMap &operator=(const VirtRegMap &) = delete; diff --git a/llvm/include/llvm/SandboxIR/SandboxIR.h b/llvm/include/llvm/SandboxIR/SandboxIR.h index 22b46bd8d7da14..b276c06033596d 100644 --- a/llvm/include/llvm/SandboxIR/SandboxIR.h +++ b/llvm/include/llvm/SandboxIR/SandboxIR.h @@ -130,6 +130,7 @@ class GlobalValue; class GlobalObject; class GlobalIFunc; class GlobalVariable; +class GlobalAlias; class Context; class Function; class Instruction; @@ -336,6 +337,7 @@ class Value { friend class GlobalObject; // For `Val`. friend class GlobalIFunc; // For `Val`. friend class GlobalVariable; // For `Val`. + friend class GlobalAlias; // For `Val`. /// All values point to the context. Context &Ctx; @@ -1528,6 +1530,38 @@ class GlobalVariable final #endif }; +class GlobalAlias final + : public GlobalWithNodeAPI<GlobalAlias, llvm::GlobalAlias, GlobalValue, + llvm::GlobalValue> { + GlobalAlias(llvm::GlobalAlias *C, Context &Ctx) + : GlobalWithNodeAPI(ClassID::GlobalAlias, C, Ctx) {} + friend class Context; // For constructor. + +public: + /// For isa/dyn_cast. + static bool classof(const sandboxir::Value *From) { + return From->getSubclassID() == ClassID::GlobalAlias; + } + + // TODO: Missing create() due to unimplemented sandboxir::Module. + + // TODO: Missing copyAttributresFrom(). + // TODO: Missing removeFromParent(), eraseFromParent(). + + void setAliasee(Constant *Aliasee); + Constant *getAliasee() const; + + const GlobalObject *getAliaseeObject() const; + GlobalObject *getAliaseeObject() { + return const_cast<GlobalObject *>( + static_cast<const GlobalAlias *>(this)->getAliaseeObject()); + } + + static bool isValidLinkage(LinkageTypes L) { + return llvm::GlobalAlias::isValidLinkage(L); + } +}; + class BlockAddress final : public Constant { BlockAddress(llvm::BlockAddress *C, Context &Ctx) : Constant(ClassID::BlockAddress, C, Ctx) {} diff --git a/llvm/lib/CodeGen/MachineVerifier.cpp b/llvm/lib/CodeGen/MachineVerifier.cpp index 6eed73c15f091d..1fcbeeec6f64cc 100644 --- a/llvm/lib/CodeGen/MachineVerifier.cpp +++ b/llvm/lib/CodeGen/MachineVerifier.cpp @@ -1739,7 +1739,7 @@ void MachineVerifier::verifyPreISelGenericInstruction(const MachineInstr *MI) { } if (IndexOp.getImm() != 0 && - Src1Ty.getElementCount().getKnownMinValue() % IndexOp.getImm() != 0) { + IndexOp.getImm() % Src1Ty.getElementCount().getKnownMinValue() != 0) { report("Index must be a multiple of the second source vector's " "minimum vector length", MI); diff --git a/llvm/lib/SandboxIR/SandboxIR.cpp b/llvm/lib/SandboxIR/SandboxIR.cpp index c26ba1983db91e..d047a53b4752ee 100644 --- a/llvm/lib/SandboxIR/SandboxIR.cpp +++ b/llvm/lib/SandboxIR/SandboxIR.cpp @@ -2534,6 +2534,8 @@ template class GlobalWithNodeAPI<Function, llvm::Function, GlobalObject, llvm::GlobalObject>; template class GlobalWithNodeAPI<GlobalVariable, llvm::GlobalVariable, GlobalObject, llvm::GlobalObject>; +template class GlobalWithNodeAPI<GlobalAlias, llvm::GlobalAlias, GlobalValue, + llvm::GlobalValue>; } // namespace llvm::sandboxir void GlobalIFunc::setResolver(Constant *Resolver) { @@ -2587,6 +2589,24 @@ void GlobalVariable::setExternallyInitialized(bool V) { cast<llvm::GlobalVariable>(Val)->setExternallyInitialized(V); } +void GlobalAlias::setAliasee(Constant *Aliasee) { + Ctx.getTracker() + .emplaceIfTracking< + GenericSetter<&GlobalAlias::getAliasee, &GlobalAlias::setAliasee>>( + this); + cast<llvm::GlobalAlias>(Val)->setAliasee(cast<llvm::Constant>(Aliasee->Val)); +} + +Constant *GlobalAlias::getAliasee() const { + return cast<Constant>( + Ctx.getOrCreateConstant(cast<llvm::GlobalAlias>(Val)->getAliasee())); +} + +const GlobalObject *GlobalAlias::getAliaseeObject() const { + return cast<GlobalObject>(Ctx.getOrCreateConstant( + cast<llvm::GlobalAlias>(Val)->getAliaseeObject())); +} + void GlobalValue::setUnnamedAddr(UnnamedAddr V) { Ctx.getTracker() .emplaceIfTracking<GenericSetter<&GlobalValue::getUnnamedAddr, @@ -2803,6 +2823,10 @@ Value *Context::getOrCreateValueInternal(llvm::Value *LLVMV, llvm::User *U) { It->second = std::unique_ptr<GlobalVariable>( new GlobalVariable(cast<llvm::GlobalVariable>(C), *this)); break; + case llvm::Value::GlobalAliasVal: + It->second = std::unique_ptr<GlobalAlias>( + new GlobalAlias(cast<llvm::GlobalAlias>(C), *this)); + break; default: It->second = std::unique_ptr<Constant>(new Constant(C, *this)); break; diff --git a/llvm/test/MachineVerifier/test_g_insert_subvector.mir b/llvm/test/MachineVerifier/test_g_insert_subvector.mir index 9fce3c3e842d40..62ddd28919b205 100644 --- a/llvm/test/MachineVerifier/test_g_insert_subvector.mir +++ b/llvm/test/MachineVerifier/test_g_insert_subvector.mir @@ -41,4 +41,7 @@ body: | ; CHECK: Index must be a multiple of the second source vector's minimum vector length %13:_(<vscale x 4 x s32>) = G_INSERT_SUBVECTOR %12, %1, 3 + + ; CHECK: Index must be a multiple of the second source vector's minimum vector length + %13:_(<vscale x 4 x s32>) = G_INSERT_SUBVECTOR %12, %1, 1 ... diff --git a/llvm/unittests/SandboxIR/SandboxIRTest.cpp b/llvm/unittests/SandboxIR/SandboxIRTest.cpp index 5ded063ef6f735..b998091f6d7a97 100644 --- a/llvm/unittests/SandboxIR/SandboxIRTest.cpp +++ b/llvm/unittests/SandboxIR/SandboxIRTest.cpp @@ -1051,6 +1051,72 @@ define void @foo() { EXPECT_EQ(GV0->getCodeModel(), LLVMGV0->getCodeModel()); } +TEST_F(SandboxIRTest, GlobalAlias) { + parseIR(C, R"IR( +@alias0 = dso_local alias void(), ptr @foo +@alias1 = dso_local alias void(), ptr @foo +declare void @bar(); +define void @foo() { + call void @alias0() + call void @alias1() + call void @bar() + ret void +} +)IR"); + Function &LLVMF = *M->getFunction("foo"); + auto *LLVMBB = &*LLVMF.begin(); + auto LLVMIt = LLVMBB->begin(); + auto *LLVMCall0 = cast<llvm::CallInst>(&*LLVMIt++); + auto *LLVMAlias0 = cast<llvm::GlobalAlias>(LLVMCall0->getCalledOperand()); + sandboxir::Context Ctx(C); + + auto &F = *Ctx.createFunction(&LLVMF); + auto *BB = &*F.begin(); + auto It = BB->begin(); + auto *Call0 = cast<sandboxir::CallInst>(&*It++); + auto *Call1 = cast<sandboxir::CallInst>(&*It++); + auto *CallBar = cast<sandboxir::CallInst>(&*It++); + auto *CalleeBar = cast<sandboxir::Constant>(CallBar->getCalledOperand()); + // Check classof(), creation. + auto *Alias0 = cast<sandboxir::GlobalAlias>(Call0->getCalledOperand()); + auto *Alias1 = cast<sandboxir::GlobalAlias>(Call1->getCalledOperand()); + // Check getIterator(). + { + auto It0 = Alias0->getIterator(); + auto It1 = Alias1->getIterator(); + EXPECT_EQ(&*It0, Alias0); + EXPECT_EQ(&*It1, Alias1); + EXPECT_EQ(std::next(It0), It1); + EXPECT_EQ(std::prev(It1), It0); + EXPECT_EQ(&*std::next(It0), Alias1); + EXPECT_EQ(&*std::prev(It1), Alias0); + } + // Check getReverseIterator(). + { + auto RevIt0 = Alias0->getReverseIterator(); + auto RevIt1 = Alias1->getReverseIterator(); + EXPECT_EQ(&*RevIt0, Alias0); + EXPECT_EQ(&*RevIt1, Alias1); + EXPECT_EQ(std::prev(RevIt0), RevIt1); + EXPECT_EQ(std::next(RevIt1), RevIt0); + EXPECT_EQ(&*std::prev(RevIt0), Alias1); + EXPECT_EQ(&*std::next(RevIt1), Alias0); + } + // Check getAliasee(). + EXPECT_EQ(Alias0->getAliasee(), Ctx.getValue(LLVMAlias0->getAliasee())); + // Check setAliasee(). + auto *OrigAliasee = Alias0->getAliasee(); + auto *NewAliasee = CalleeBar; + EXPECT_NE(NewAliasee, OrigAliasee); + Alias0->setAliasee(NewAliasee); + EXPECT_EQ(Alias0->getAliasee(), NewAliasee); + Alias0->setAliasee(OrigAliasee); + EXPECT_EQ(Alias0->getAliasee(), OrigAliasee); + // Check getAliaseeObject(). + EXPECT_EQ(Alias0->getAliaseeObject(), + Ctx.getValue(LLVMAlias0->getAliaseeObject())); +} + TEST_F(SandboxIRTest, BlockAddress) { parseIR(C, R"IR( define void @foo(ptr %ptr) { diff --git a/llvm/unittests/SandboxIR/TrackerTest.cpp b/llvm/unittests/SandboxIR/TrackerTest.cpp index f46e16e626baf0..da5416395ec42f 100644 --- a/llvm/unittests/SandboxIR/TrackerTest.cpp +++ b/llvm/unittests/SandboxIR/TrackerTest.cpp @@ -1638,6 +1638,37 @@ define void @foo() { EXPECT_EQ(GV0->isExternallyInitialized(), OrigIsExtInit); } +TEST_F(TrackerTest, GlobalAliasSetters) { + parseIR(C, R"IR( +@alias = dso_local alias void(), ptr @foo +declare void @bar(); +define void @foo() { + call void @alias() + call void @bar() + ret void +} +)IR"); + Function &LLVMF = *M->getFunction("foo"); + sandboxir::Context Ctx(C); + + auto &F = *Ctx.createFunction(&LLVMF); + auto *BB = &*F.begin(); + auto It = BB->begin(); + auto *Call0 = cast<sandboxir::CallInst>(&*It++); + auto *Call1 = cast<sandboxir::CallInst>(&*It++); + auto *Callee1 = cast<sandboxir::Constant>(Call1->getCalledOperand()); + auto *Alias = cast<sandboxir::GlobalAlias>(Call0->getCalledOperand()); + // Check setAliasee(). + auto *OrigAliasee = Alias->getAliasee(); + auto *NewAliasee = Callee1; + EXPECT_NE(NewAliasee, OrigAliasee); + Ctx.save(); + Alias->setAliasee(NewAliasee); + EXPECT_EQ(Alias->getAliasee(), NewAliasee); + Ctx.revert(); + EXPECT_EQ(Alias->getAliasee(), OrigAliasee); +} + TEST_F(TrackerTest, SetVolatile) { parseIR(C, R"IR( define void @foo(ptr %arg0, i8 %val) { _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits