From 092c22c776a7205126b47d758881a1d6fd67b7e2 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Tue, 8 Mar 2022 00:24:05 +0300 Subject: [PATCH 01/93] [TSAR, Tfm, Clang, Replace] Fix, check for simple references to a structure in a member statement. Disable replacement in case of '(S+I)->X'. --- lib/Transform/Clang/StructureReplacement.cpp | 66 +++++++++++++------- 1 file changed, 43 insertions(+), 23 deletions(-) diff --git a/lib/Transform/Clang/StructureReplacement.cpp b/lib/Transform/Clang/StructureReplacement.cpp index b11f94b7..3baf829c 100644 --- a/lib/Transform/Clang/StructureReplacement.cpp +++ b/lib/Transform/Clang/StructureReplacement.cpp @@ -788,8 +788,9 @@ class ReplacementSanitizer : public RecursiveASTVisitor { } bool VisitDeclRefExpr(DeclRefExpr *Expr) { - mLastDeclRef = nullptr; - if (!mIsInnermostMember && !mReplacements.inClause(Expr)) { + if (mReplacements.inClause(Expr)) + return true; + if (!mIsInnermostMember) { auto ND = Expr->getFoundDecl(); if (mReplacements.Candidates.count(ND)) { toDiag(mSrcMgr.getDiagnostics(), ND->getBeginLoc(), @@ -798,39 +799,58 @@ class ReplacementSanitizer : public RecursiveASTVisitor { tsar::diag::note_replace_struct_arrow); mReplacements.Candidates.erase(ND); } - } else { - mLastDeclRef = Expr; + } else { + auto ND = Expr->getFoundDecl(); + auto ReplacementItr = mReplacements.Candidates.find(ND); + if (ReplacementItr != mReplacements.Candidates.end()) + mLastDeclRef = Expr; } return true; } + bool TraverseStmt(Stmt *S) { + auto Res{RecursiveASTVisitor::TraverseStmt(S)}; + if (!mIsInnermostMember || !mLastDeclRef || isa(S)) + return Res; + if (auto *Cast{dyn_cast(S)}; + Cast && Cast->getCastKind() == CK_LValueToRValue) + return Res; + auto ND = mLastDeclRef->getFoundDecl(); + toDiag(mSrcMgr.getDiagnostics(), ND->getBeginLoc(), + tsar::diag::warn_disable_replace_struct); + toDiag(mSrcMgr.getDiagnostics(), cast(S)->getExprLoc(), + tsar::diag::note_replace_struct_arrow); + mReplacements.Candidates.erase(ND); + mLastDeclRef = nullptr; + return Res; + } + bool TraverseMemberExpr(MemberExpr *Expr) { mIsInnermostMember = true; bool CurrInAssignment = mInAssignment; auto Res = RecursiveASTVisitor::TraverseMemberExpr(Expr); if (mIsInnermostMember && mLastDeclRef) { auto ND = mLastDeclRef->getFoundDecl(); - auto ReplacementItr = mReplacements.Candidates.find(ND); - if (ReplacementItr != mReplacements.Candidates.end()) { - if (!Expr->isArrow()) { - toDiag(mSrcMgr.getDiagnostics(), ND->getBeginLoc(), - tsar::diag::warn_disable_replace_struct); - toDiag(mSrcMgr.getDiagnostics(), Expr->getOperatorLoc(), - tsar::diag::note_replace_struct_arrow); - mReplacements.Candidates.erase(ReplacementItr); - } else if (!checkMacroBoundsAndEmitDiag( - Expr, mReplacements.Definition->getLocation(), mSrcMgr, - mLangOpts)) { - mReplacements.Candidates.erase(ReplacementItr); - } else { - auto Itr = addToReplacement(Expr->getMemberDecl(), - ReplacementItr->get()); - Itr->Ranges.emplace_back(Expr->getSourceRange()); - Itr->InAssignment |= CurrInAssignment; - } + if (!Expr->isArrow()) { + toDiag(mSrcMgr.getDiagnostics(), ND->getBeginLoc(), + tsar::diag::warn_disable_replace_struct); + toDiag(mSrcMgr.getDiagnostics(), Expr->getOperatorLoc(), + tsar::diag::note_replace_struct_arrow); + mReplacements.Candidates.erase(ND); + } else if (!checkMacroBoundsAndEmitDiag( + Expr, mReplacements.Definition->getLocation(), mSrcMgr, + mLangOpts)) { + mReplacements.Candidates.erase(ND); + } else { + auto ReplacementItr = mReplacements.Candidates.find(ND); + auto Itr = addToReplacement(Expr->getMemberDecl(), + ReplacementItr->get()); + Itr->Ranges.emplace_back(Expr->getSourceRange()); + Itr->InAssignment |= CurrInAssignment; } } mIsInnermostMember = false; + mLastDeclRef = nullptr; return Res; } @@ -853,7 +873,7 @@ class ReplacementSanitizer : public RecursiveASTVisitor { ReplacementMap &mReplacementInfo; bool mIsInnermostMember = false; - DeclRefExpr *mLastDeclRef; + DeclRefExpr *mLastDeclRef = nullptr; bool mInAssignment = true; }; From 2c859a714b99849971c10145f374a42a6293c56d Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Tue, 8 Mar 2022 12:33:52 +0300 Subject: [PATCH 02/93] [TSAR, Tfm, Clang, Replace] Add support for not arrow dereference expressions. --- lib/Transform/Clang/StructureReplacement.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/Transform/Clang/StructureReplacement.cpp b/lib/Transform/Clang/StructureReplacement.cpp index 3baf829c..fe54b389 100644 --- a/lib/Transform/Clang/StructureReplacement.cpp +++ b/lib/Transform/Clang/StructureReplacement.cpp @@ -810,11 +810,15 @@ class ReplacementSanitizer : public RecursiveASTVisitor { bool TraverseStmt(Stmt *S) { auto Res{RecursiveASTVisitor::TraverseStmt(S)}; - if (!mIsInnermostMember || !mLastDeclRef || isa(S)) + if (!mIsInnermostMember || !mLastDeclRef || isa(S) || + isa(S)) return Res; if (auto *Cast{dyn_cast(S)}; Cast && Cast->getCastKind() == CK_LValueToRValue) return Res; + if (auto *Op{dyn_cast(S)}; + Op && Op->getOpcode() == UnaryOperatorKind::UO_Deref) + return Res; auto ND = mLastDeclRef->getFoundDecl(); toDiag(mSrcMgr.getDiagnostics(), ND->getBeginLoc(), tsar::diag::warn_disable_replace_struct); @@ -831,13 +835,7 @@ class ReplacementSanitizer : public RecursiveASTVisitor { auto Res = RecursiveASTVisitor::TraverseMemberExpr(Expr); if (mIsInnermostMember && mLastDeclRef) { auto ND = mLastDeclRef->getFoundDecl(); - if (!Expr->isArrow()) { - toDiag(mSrcMgr.getDiagnostics(), ND->getBeginLoc(), - tsar::diag::warn_disable_replace_struct); - toDiag(mSrcMgr.getDiagnostics(), Expr->getOperatorLoc(), - tsar::diag::note_replace_struct_arrow); - mReplacements.Candidates.erase(ND); - } else if (!checkMacroBoundsAndEmitDiag( + if (!checkMacroBoundsAndEmitDiag( Expr, mReplacements.Definition->getLocation(), mSrcMgr, mLangOpts)) { mReplacements.Candidates.erase(ND); From 7af2c8f56a9bec45b66cb76148e5ed5c953f947f Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Wed, 16 Mar 2022 12:27:42 +0300 Subject: [PATCH 03/93] [TSAR, Tfm, Struct, Replace] Implement replacement of an array of structures. --- include/tsar/Support/DiagnosticKinds.td | 2 + include/tsar/Support/Directives.td | 24 +- lib/Transform/Clang/StructureReplacement.cpp | 339 ++++++++++++++++--- 3 files changed, 306 insertions(+), 59 deletions(-) diff --git a/include/tsar/Support/DiagnosticKinds.td b/include/tsar/Support/DiagnosticKinds.td index a6cda4cc..aaa3a93d 100644 --- a/include/tsar/Support/DiagnosticKinds.td +++ b/include/tsar/Support/DiagnosticKinds.td @@ -165,6 +165,7 @@ def warn_disable_replace_struct_no_param : Warning<"unable to replace any variab def warn_disable_replace_struct_no_pointer : Warning<"disable replacement of a non-pointer type">; def warn_disable_replace_struct_no_struct : Warning<"disable replacement of a non-structure type">; def warn_disable_replace_struct_system : Warning<"disable replacement of a non-user defined function">; +def warn_disable_replace_struct_array_strict : Warning<"diasable strict replacement of a structure-type with array">; def note_replace_struct_arrow: Note<"not-arrow access prevent replacement">; def note_replace_struct_decl: Note<"unable to build declaration of a record member">; @@ -177,6 +178,7 @@ def remark_replace_struct: Remark<"structure replacement">; def warn_replace_call_unable : Warning<"unable to replace call expression">; def warn_replace_call_indirect_unable : Warning<"unable to replace indirect call expression">; def note_replace_call_no_md : Note<"replacement metadata not found for function %0">; +def note_replace_call_no_array : Note<"unable to pass pointer to a member of a structure as an array">; def error_replace_md_missing : Error<"missing replacement metadata">; def error_replace_md_target_param_expected : Error<"expected replacement for target parameter">; diff --git a/include/tsar/Support/Directives.td b/include/tsar/Support/Directives.td index f0e2024e..85f3b668 100644 --- a/include/tsar/Support/Directives.td +++ b/include/tsar/Support/Directives.td @@ -167,8 +167,10 @@ def ReplaceMetadata : Clause<"replace", Metadata, One<[ LBrace, ZeroOrOne<[ - Period, PPIdentifier, Equal, Identifier, - ZeroOrMore<[Comma, Period, PPIdentifier, Equal, Identifier]> + ZeroOrOne<[LSquare, RSquare]>, + Period, PPIdentifier, Equal, Identifier, + ZeroOrMore<[Comma, ZeroOrOne<[LSquare, RSquare]>, + Period, PPIdentifier, Equal, Identifier]> ]>, RBrace ]> @@ -179,8 +181,10 @@ def ReplaceMetadata : Clause<"replace", Metadata, One<[ LBrace, ZeroOrOne<[ - Period, PPIdentifier, Equal, Identifier, - ZeroOrMore<[Comma, Period, PPIdentifier, Equal, Identifier]> + ZeroOrOne<[LSquare, RSquare]>, + Period, PPIdentifier, Equal, Identifier, + ZeroOrMore<[Comma, ZeroOrOne<[LSquare, RSquare]>, + Period, PPIdentifier, Equal, Identifier]> ]>, RBrace ]> @@ -196,8 +200,10 @@ def ReplaceMetadata : Clause<"replace", Metadata, One<[ LBrace, ZeroOrOne<[ - Period, PPIdentifier, Equal, Identifier, - ZeroOrMore<[Comma, Period, PPIdentifier, Equal, Identifier]> + ZeroOrOne<[LSquare, RSquare]>, + Period, PPIdentifier, Equal, Identifier, + ZeroOrMore<[Comma, ZeroOrOne<[LSquare, RSquare]>, + Period, PPIdentifier, Equal, Identifier]> ]>, RBrace ]> @@ -208,8 +214,10 @@ def ReplaceMetadata : Clause<"replace", Metadata, One<[ LBrace, ZeroOrOne<[ - Period, PPIdentifier, Equal, Identifier, - ZeroOrMore<[Comma, Period, PPIdentifier, Equal, Identifier]> + ZeroOrOne<[LSquare, RSquare]>, + Period, PPIdentifier, Equal, Identifier, + ZeroOrMore<[Comma, ZeroOrOne<[LSquare, RSquare]>, + Period, PPIdentifier, Equal, Identifier]> ]>, RBrace ]> diff --git a/lib/Transform/Clang/StructureReplacement.cpp b/lib/Transform/Clang/StructureReplacement.cpp index fe54b389..f7fc3067 100644 --- a/lib/Transform/Clang/StructureReplacement.cpp +++ b/lib/Transform/Clang/StructureReplacement.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -65,6 +66,8 @@ inline const clang::Type *getCanonicalUnqualifiedType(ValueDecl *VD) { ->getTypePtr(); } +LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); + struct Replacement { Replacement(ValueDecl *M) : Member(M) {} @@ -73,16 +76,29 @@ struct Replacement { ValueDecl *Member; /// Locations in a source code which contains accesses to the member 'Member' - /// of an original parameter. - std::vector Ranges; + /// of an original parameter and must be replaced. + std::vector RangesToReplace; + + /// Locations in a source code which contains accesses to the member 'Member' + /// of an original parameter and must be removed. + /// + /// For example, to transform `S[I].X` we replace `S[I]` with `S_X0[I]` and + /// remove `.X`. + std::vector RangesToRemove; /// Identifier of a new parameter which corresponds to the member 'Member' of /// an original parameter which should be replaced. SmallString<32> Identifier; - /// This is 'true' if a value of the member 'Member' of an original parameter - /// can be changed in the original function call. - bool InAssignment = false; + enum ReplacemntFlags : uint8_t { + NoFlag = 0u, + /// It is set if a value of the member 'Member' of an original + /// parameter can be changed in the original function call. + InAssignment = 1u << 0, + /// It is set if an accessed 'Member' is in array of structures. + InArray = 1u << 2, + LLVM_MARK_AS_BITMASK_ENUM(InArray) + } Flags = NoFlag; }; /// Map from parameter to its replacement which is list of necessary members and @@ -97,10 +113,11 @@ using ReplacementCandidates = SmallDenseMap< /// Description of a possible replacement of a source function. struct ReplacementMetadata { + enum ParamKind : uint8_t { PK_Value, PK_Pointer, PK_Array }; struct ParamReplacement { Optional TargetParam; FieldDecl *TargetMember = nullptr; - bool IsPointer = false; + ParamKind Kind = PK_Value; }; bool valid(unsigned *ParamIdx = nullptr) const { @@ -390,6 +407,16 @@ class ReplacementCollector : public RecursiveASTVisitor { return false; } mCurrMetaMember = *MemberItr; + mIsCurrMetaMemberArray = false; + LocalLexer Lex(mSrcMgr.getExpansionRange(CharSourceRange::getCharRange( + mCurrMetaParamLoc, SL->getBeginLoc())), + mSrcMgr, mLangOpts); + clang::Token Tok; + while (!Lex.LexFromRawLexer(Tok)) + if (Tok.is(tok::r_square)) { + mIsCurrMetaMemberArray = true; + break; + } return true; } @@ -425,9 +452,11 @@ class ReplacementCollector : public RecursiveASTVisitor { clang::diag::note_declared_at); return false; } + mCurrMetaParamLoc = CS->getBeginLoc(); auto Res = RecursiveASTVisitor::TraverseCompoundStmt(CS); ++mCurrMetaTargetParam; mCurrMetaMember = nullptr; + mIsCurrMetaMemberArray = false; return Res; } @@ -470,6 +499,7 @@ class ReplacementCollector : public RecursiveASTVisitor { bool VisitReplaceMetadataClauseExpr(DeclRefExpr *Expr) { assert(mCurrFunc && "Replacement description must not be null!"); + mCurrMetaParamLoc = Expr->getEndLoc(); mCurrFunc->Meta.insert(Expr); auto ND = Expr->getFoundDecl(); if (auto *FD = dyn_cast(ND)) { @@ -565,7 +595,10 @@ class ReplacementCollector : public RecursiveASTVisitor { break; assert(ParamIdx < mCurrFunc->Definition->getNumParams() && "Unknown parameter!"); - CurrMD.Parameters[ParamIdx].IsPointer = IsPointer; + CurrMD.Parameters[ParamIdx].Kind = + mIsCurrMetaMemberArray ? ReplacementMetadata::PK_Array + : IsPointer ? ReplacementMetadata::PK_Pointer + : ReplacementMetadata::PK_Value; CurrMD.Parameters[ParamIdx].TargetMember = mCurrMetaMember; if (CurrMD.Parameters[ParamIdx].TargetParam) { toDiag(mSrcMgr.getDiagnostics(), Expr->getLocation(), @@ -648,7 +681,9 @@ class ReplacementCollector : public RecursiveASTVisitor { unsigned mCurrMetaTargetParam = 0; FieldDecl *mCurrMetaMember = nullptr; + bool mIsCurrMetaMemberArray = false; SourceLocation mCurrMetaBeginLoc; + SourceLocation mCurrMetaParamLoc; }; /// Return metadata which are necessary to process request or nullptr. @@ -781,7 +816,7 @@ class ReplacementSanitizer : public RecursiveASTVisitor { ReplacementItr->get()); } } else { - Res &= !TraverseStmt(ArgExpr); + Res &= TraverseStmt(ArgExpr); } } return Res; @@ -811,7 +846,7 @@ class ReplacementSanitizer : public RecursiveASTVisitor { bool TraverseStmt(Stmt *S) { auto Res{RecursiveASTVisitor::TraverseStmt(S)}; if (!mIsInnermostMember || !mLastDeclRef || isa(S) || - isa(S)) + isa(S) || isa(S)) return Res; if (auto *Cast{dyn_cast(S)}; Cast && Cast->getCastKind() == CK_LValueToRValue) @@ -843,8 +878,18 @@ class ReplacementSanitizer : public RecursiveASTVisitor { auto ReplacementItr = mReplacements.Candidates.find(ND); auto Itr = addToReplacement(Expr->getMemberDecl(), ReplacementItr->get()); - Itr->Ranges.emplace_back(Expr->getSourceRange()); - Itr->InAssignment |= CurrInAssignment; + if (auto *Subscript{ + dyn_cast(*Expr->child_begin())}) { + Itr->RangesToReplace.emplace_back( + Subscript->getBase()->getSourceRange()); + Itr->RangesToRemove.emplace_back( + SourceRange{Expr->getOperatorLoc(), Expr->getEndLoc()}); + Itr->Flags |= Replacement::InArray; + } else { + Itr->RangesToReplace.emplace_back(Expr->getSourceRange()); + } + if (CurrInAssignment) + Itr->Flags |= Replacement::InAssignment; } } mIsInnermostMember = false; @@ -935,6 +980,8 @@ void addPragmaMetadata(FunctionInfo &FuncInfo, auto Itr = ReplacementItr->get().begin(); auto EndItr = ReplacementItr->get().end(); if (Itr != EndItr) { + if (Itr->Flags & Replacement::InArray) + MDPragma += "[]"; MDPragma += "."; MDPragma += Itr->Member->getName(); MDPragma += "="; @@ -943,6 +990,8 @@ void addPragmaMetadata(FunctionInfo &FuncInfo, } for (auto &R : make_range(Itr, EndItr)) { MDPragma += ','; + if (Itr->Flags & Replacement::InArray) + MDPragma += "[]"; MDPragma += "."; MDPragma += R.Member->getName(); MDPragma += "="; @@ -966,7 +1015,7 @@ void addPragmaMetadata(FunctionInfo &FuncInfo, } template -void replaceCall(FunctionInfo &FI, const CallExpr &Expr, +bool replaceCall(FunctionInfo &FI, const CallExpr &Expr, StringRef ReplacementName, const ReplacementMetadata &Meta, RewriterT &Rewriter) { auto &SrcMgr = Rewriter.getSourceMgr(); @@ -980,7 +1029,14 @@ void replaceCall(FunctionInfo &FI, const CallExpr &Expr, auto ArgExpr = Expr.getArg(*ParamInfo.TargetParam); auto ReplacementItr = isExprInCandidates(ArgExpr, FI.Candidates); if (ReplacementItr == FI.Candidates.end()) { - if (ParamInfo.IsPointer) + if (ParamInfo.Kind == ReplacementMetadata::PK_Array) { + toDiag(SrcMgr.getDiagnostics(), Expr.getBeginLoc(), + tsar::diag::warn_replace_call_unable); + toDiag(SrcMgr.getDiagnostics(), ParamInfo.TargetMember->getLocation(), + tsar::diag::note_replace_call_no_array); + return false; + } + if (ParamInfo.Kind == ReplacementMetadata::PK_Pointer) NewCallExpr += '&'; if (ParamInfo.TargetMember) { NewCallExpr += '('; @@ -990,11 +1046,11 @@ void replaceCall(FunctionInfo &FI, const CallExpr &Expr, NewCallExpr += "->"; NewCallExpr += ParamInfo.TargetMember->getName(); } else { - if (ParamInfo.IsPointer) + if (ParamInfo.Kind == ReplacementMetadata::PK_Pointer) NewCallExpr += '('; NewCallExpr += Rewriter.getRewrittenText( SrcMgr.getExpansionRange(ArgExpr->getSourceRange()).getAsRange()); - if (ParamInfo.IsPointer) + if (ParamInfo.Kind == ReplacementMetadata::PK_Pointer) NewCallExpr += ')'; } } else { @@ -1004,17 +1060,20 @@ void replaceCall(FunctionInfo &FI, const CallExpr &Expr, }); assert(Itr != ReplacementItr->get().end() && "Description of the replacement must be found!"); - if (Itr->InAssignment) { - if (ParamInfo.IsPointer) { + if (Itr->Flags & (Replacement::InAssignment | Replacement::InArray)) { + if (ParamInfo.Kind == ReplacementMetadata::PK_Pointer || + ParamInfo.Kind == ReplacementMetadata::PK_Array) { NewCallExpr += Itr->Identifier; } else { NewCallExpr += "*"; NewCallExpr += Itr->Identifier; } - } else if (ParamInfo.IsPointer) { + } else if (ParamInfo.Kind == ReplacementMetadata::PK_Pointer) { NewCallExpr += "&"; NewCallExpr += Itr->Identifier; } else { + assert(ParamInfo.Kind != ReplacementMetadata::PK_Array && + "Unable to pass value as array parameter!"); NewCallExpr += Itr->Identifier; } } @@ -1023,6 +1082,7 @@ void replaceCall(FunctionInfo &FI, const CallExpr &Expr, Rewriter.ReplaceText( getExpansionRange(SrcMgr, Expr.getSourceRange()).getAsRange(), NewCallExpr); + return true; } #ifdef LLVM_DEBUG @@ -1058,7 +1118,17 @@ void printMetadataLog(const FunctionInfo &FuncInfo) { if (CanonicalDeclPtr(FD->getCanonicalDecl()) != SI.TargetDecl) dbgs() << FD->getParamDecl(I)->getName() << ","; - dbgs() << (PI.IsPointer ? "pointer" : "value"); + switch (PI.Kind) { + case ReplacementMetadata::PK_Value: + dbgs() << "value"; + break; + case ReplacementMetadata::PK_Pointer: + dbgs() << "pointer"; + break; + case ReplacementMetadata::PK_Array: + dbgs() << "array"; + break; + } dbgs() << ")\n"; } } @@ -1074,8 +1144,10 @@ void printCandidateLog(const ReplacementCandidates &Candidates, bool IsStrict) { dbgs() << " with members: "; for (auto &R : Candidate.get()) { dbgs() << " " << R.Member->getName(); - if (R.InAssignment) + if (R.Flags & Replacement::InAssignment) dbgs() << "(ref)"; + if (R.Flags & Replacement::InArray) + dbgs() << "(array)"; } } dbgs() << "\n"; @@ -1126,8 +1198,9 @@ template bool replaceCalls(FunctionInfo &FI, FI.Definition->getLocation(), SrcMgr, LangOpts)) continue; - replaceCall(FI, *Request.get(), - Request.get()->getName(), *Meta, Rewriter); + if (!replaceCall(FI, *Request.get(), + Request.get()->getName(), *Meta, Rewriter)) + continue; IsChanged = true; } for (auto *Request : FI.ImplicitRequests) { @@ -1148,8 +1221,9 @@ template bool replaceCalls(FunctionInfo &FI, continue; assert(!Itr->get()->ReplacmenetName.empty() && "Name of the function clone must not be null!"); - replaceCall(FI, *Request, Itr->get()->ReplacmenetName, - *MetaItr, Rewriter); + if (!replaceCall(FI, *Request, Itr->get()->ReplacmenetName, + *MetaItr, Rewriter)) + continue; IsChanged = true; } return IsChanged; @@ -1215,7 +1289,7 @@ class ClangStructureReplacementPass : /// members can be implicitly accessed in callees while these members are not /// accessed in a function explicitly. This function collects these members /// for further replacement. - void fillImplicitReplacementMembers(scc_iterator &SCC); + void fillReplacementMembersFromCallees(scc_iterator &SCC); /// Check replacement candidates which are passed to calls. /// Remove replacement candidates if it cannot be replaced in callee. @@ -1431,13 +1505,14 @@ void ClangStructureReplacementPass::sanitizeCandidates(FunctionInfo &FuncInfo) { for (auto &C : FuncInfo.Candidates) for (auto &R : C.get()) if (isa(R.Member->getType())) - R.InAssignment = false; - else - R.InAssignment |= FuncInfo.Strict; + R.Flags &= ~Replacement::InAssignment; + else if (FuncInfo.Strict) + R.Flags |= Replacement::InAssignment; } -void ClangStructureReplacementPass::fillImplicitReplacementMembers( +void ClangStructureReplacementPass::fillReplacementMembersFromCallees( scc_iterator &SCC) { + auto &SrcMgr{mTfmCtx->getContext().getSourceManager()}; bool IsChanged = false; do { IsChanged = false; @@ -1445,6 +1520,29 @@ void ClangStructureReplacementPass::fillImplicitReplacementMembers( auto FuncInfo = tieCallGraphNode(CGN); if (!FuncInfo || !FuncInfo->hasCandidates()) continue; + for (auto &R : FuncInfo->Requests) { + auto Meta{findRequestMetadata(R, mReplacementInfo, SrcMgr)}; + if (!Meta) + continue; + for (auto &ParamInfo : Meta->Parameters) { + assert(ParamInfo.TargetParam && + "Target parameter must be known for a valid request!"); + if (ParamInfo.Kind == ReplacementMetadata::PK_Array) { + auto ArgExpr{R.get()->getArg(*ParamInfo.TargetParam)}; + auto ReplacementItr{ + isExprInCandidates(ArgExpr, FuncInfo->Candidates)}; + if (ReplacementItr != FuncInfo->Candidates.end()) { + auto I{find_if(ReplacementItr->get(), + [&ParamInfo](Replacement &R) { + return R.Member == ParamInfo.TargetMember; + })}; + assert(I != ReplacementItr->get().end() && + "Member to replace must be known for a valid request!"); + I->Flags |= Replacement::InArray; + } + } + } + } for (auto *Call : FuncInfo->ImplicitRequests) { auto *Callee = Call->getDirectCallee(); assert(Callee && @@ -1469,12 +1567,15 @@ void ClangStructureReplacementPass::fillImplicitReplacementMembers( return R.Member == CalleeR.Member; }); if (CallerRItr != Itr->get().end()) { - if (!CallerRItr->InAssignment && CalleeR.InAssignment) - CallerRItr->InAssignment = IsChanged = true; + if (!(CallerRItr->Flags & Replacement::InAssignment) && + CalleeR.Flags & Replacement::InAssignment) + IsChanged = (CallerRItr->Flags |= Replacement::InAssignment); + if (!(CallerRItr->Flags & Replacement::InArray) && + CalleeR.Flags & Replacement::InArray) + IsChanged = (CallerRItr->Flags |= Replacement::InArray); } else { Itr->get().emplace_back(CalleeR.Member); - Itr->get().back().InAssignment = - CalleeR.InAssignment; + Itr->get().back().Flags = CalleeR.Flags; IsChanged = true; } } @@ -1485,8 +1586,9 @@ void ClangStructureReplacementPass::fillImplicitReplacementMembers( } void ClangStructureReplacementPass::sanitizeCandidatesInCalls( - scc_iterator &SCC) { - auto &SrcMgr = mTfmCtx->getContext().getSourceManager(); + scc_iterator &SCC){ + auto &SrcMgr{mTfmCtx->getContext().getSourceManager()}; + auto &LangOpts{mTfmCtx->getContext().getLangOpts()}; bool IsChanged = false; do { IsChanged = false; @@ -1494,6 +1596,37 @@ void ClangStructureReplacementPass::sanitizeCandidatesInCalls( auto FuncInfo = tieCallGraphNode(CGN); if (!FuncInfo || !FuncInfo->hasCandidates()) continue; + for (auto &R : FuncInfo->Requests) { + auto *Callee{R.get()->getDirectCallee()}; + assert(Callee && "Called must be known for a valid implicit request!"); + auto CalleeItr{mReplacementInfo.find(Callee)}; + if (CalleeItr == mReplacementInfo.end()) + continue; + for (unsigned I = 0, EI = R.get()->getNumArgs(); I < EI; + ++I) { + auto Itr{isExprInCandidates(R.get()->getArg(I), + FuncInfo->Candidates)}; + if (Itr != FuncInfo->Candidates.end()) + continue; + if (auto CalleeCandidateItr{ + CalleeItr->get()->Candidates.find( + CalleeItr->get()->Definition->getParamDecl( + I))}; + CalleeCandidateItr != + CalleeItr->get()->Candidates.end()) { + if (auto ToArrayItr{find_if( + CalleeCandidateItr->get(), + [](auto &R) { return R.Flags & Replacement::InArray; })}; + ToArrayItr != CalleeCandidateItr->get().end()) { + toDiag(SrcMgr.getDiagnostics(), R.get()->getBeginLoc(), + tsar::diag::warn_replace_call_unable); + toDiag(SrcMgr.getDiagnostics(), ToArrayItr->Member->getLocation(), + tsar::diag::note_replace_call_no_array); + IsChanged = true; + } + } + } + } SmallVector ImplicitRequestToRemove; for (auto *Call : FuncInfo->ImplicitRequests) { auto *Callee = Call->getDirectCallee(); @@ -1504,9 +1637,46 @@ void ClangStructureReplacementPass::sanitizeCandidatesInCalls( for (unsigned I = 0, EI = Call->getNumArgs(); I < EI; ++I) { auto Itr = (isExprInCandidates(Call->getArg(I), FuncInfo->Candidates)); - if (Itr == FuncInfo->Candidates.end()) - continue; - if (CalleeItr == mReplacementInfo.end()) { + if (Itr == FuncInfo->Candidates.end()) { + if (CalleeItr == mReplacementInfo.end()) + continue; + // Do not replace a call if at least one formal parameter must be + // replaced with an array and actual parameter cannot be replaced. + // In this case we disable replacement of all actual parameters + // also. + if (auto CalleeCandidateItr{ + CalleeItr->get()->Candidates.find( + CalleeItr->get() + ->Definition->getParamDecl(I))}; + CalleeCandidateItr != + CalleeItr->get()->Candidates.end()) { + if (auto ToArrayItr{find_if( + CalleeCandidateItr->get(), + [](auto &R) { return R.Flags & Replacement::InArray; })}; + ToArrayItr != CalleeCandidateItr->get().end()) { + toDiag(SrcMgr.getDiagnostics(), Call->getBeginLoc(), + tsar::diag::warn_replace_call_unable); + toDiag(SrcMgr.getDiagnostics(), + ToArrayItr->Member->getLocation(), + tsar::diag::note_replace_call_no_array); + for_each( + Call->arguments(), [Call, &SrcMgr, FuncInfo](auto *Arg) { + auto Itr{isExprInCandidates(Arg, FuncInfo->Candidates)}; + if (Itr == FuncInfo->Candidates.end()) + return; + toDiag(SrcMgr.getDiagnostics(), + Itr->get()->getLocation(), + tsar::diag::warn_disable_replace_struct); + toDiag(SrcMgr.getDiagnostics(), Arg->getBeginLoc(), + tsar::diag::note_replace_struct_arrow); + FuncInfo->Candidates.erase(Itr); + }); + HasCandidatesInArgs = false; + IsChanged = true; + break; + } + } + } else if (CalleeItr == mReplacementInfo.end()) { toDiag(SrcMgr.getDiagnostics(), Itr->get()->getLocation(), tsar::diag::warn_disable_replace_struct); @@ -1536,10 +1706,46 @@ void ClangStructureReplacementPass::sanitizeCandidatesInCalls( for (auto *Call : ImplicitRequestToRemove) FuncInfo->ImplicitRequests.erase(Call); } - } while (IsChanged && SCC.hasCycle()); + } while (IsChanged); } +static Optional buildParameterType(const Replacement &R) { + auto ParamType{R.Member->getType().getAsString()}; + if (!(R.Flags & (Replacement::InArray | Replacement::InAssignment))) + return ParamType; + if (auto *ATy{dyn_cast(R.Member->getType())}) { + auto ETy{ATy->getElementType()}; + while (auto *T{dyn_cast(ETy)}) + ETy = T->getElementType(); + auto ElemType{ETy.getAsString()}; + if (ParamType.size() <= ElemType.size() || + ParamType.substr(0, ElemType.size()) != ElemType) + return None; + if (isa(R.Member->getType())) { + ParamType = ElemType + "(*)" + ParamType.substr(ElemType.size()); + } else if (isa(R.Member->getType())) { + StringRef Suffix{ParamType.data() + ElemType.size(), + ParamType.size() - ElemType.size()}; + Suffix = Suffix.trim(); + if (!Suffix.startswith("[]")) + return None; + ParamType = ElemType + "(**)" + Suffix.substr(2).str(); + } else { + return None; + } + } else if (auto PTy{dyn_cast(R.Member->getType())}; + PTy && isa(PTy->getPointeeType())) { + auto ParenPos{ParamType.find_first_of('(')}; + if (ParenPos == std::string::npos) + return None; + ParamType.insert(ParenPos + 1, "*"); + } else { + ParamType += "*"; + } + return ParamType; +} + void ClangStructureReplacementPass::buildParameters(FunctionInfo &FuncInfo) { auto &SrcMgr = mTfmCtx->getContext().getSourceManager(); auto &LangOpts = mTfmCtx->getContext().getLangOpts(); @@ -1595,11 +1801,18 @@ void ClangStructureReplacementPass::buildParameters(FunctionInfo &FuncInfo) { break; } addSuffix(PD->getName() + "_" + R.Member->getName(), R.Identifier); - auto ParamType = R.InAssignment - ? R.Member->getType().getAsString() + "*" - : R.Member->getType().getAsString(); + auto ParamType{buildParameterType(R)}; + if (!ParamType) { + Context.resize(StashContextSize); + NewParams.clear(); + toDiag(SrcMgr.getDiagnostics(), PD->getLocation(), + tsar::diag::warn_disable_replace_struct); + toDiag(SrcMgr.getDiagnostics(), R.Member->getBeginLoc(), + tsar::diag::note_replace_struct_decl); + break; + } auto Tokens = - buildDeclStringRef(ParamType, R.Identifier, Context, Replacements); + buildDeclStringRef(*ParamType, R.Identifier, Context, Replacements); if (Tokens.empty()) { Context.resize(StashContextSize); NewParams.clear(); @@ -1694,16 +1907,19 @@ bool ClangStructureReplacementPass::insertNewFunction(FunctionInfo &FuncInfo, SrcMgr.getExpansionLoc(PD->getBeginLoc()), SrcMgr.getExpansionLoc(EndLoc)).getAsRange(); Canvas.ReplaceText(ReplaceRange, ReplacementItr->get()); - // Replace accesses to parameters. + // Replace accesses to parameters. for (auto &R : ReplacementItr->get()) { - for (auto Range : R.Ranges) { + for (auto Range : R.RangesToReplace) { SmallString<64> Tmp; - auto AccessString = R.InAssignment - ? ("(*" + R.Identifier + ")").toStringRef(Tmp) - : StringRef(R.Identifier); + auto AccessString = (R.Flags & Replacement::InAssignment && + !(R.Flags & Replacement::InArray)) + ? ("(*" + R.Identifier + ")").toStringRef(Tmp) + : StringRef(R.Identifier); Canvas.ReplaceText(getExpansionRange(SrcMgr, Range).getAsRange(), AccessString); } + for (auto Range : R.RangesToRemove) + Canvas.RemoveText(getExpansionRange(SrcMgr, Range).getAsRange()); } } // Build implicit metadata. @@ -1716,13 +1932,18 @@ bool ClangStructureReplacementPass::insertNewFunction(FunctionInfo &FuncInfo, if (ReplacementItr == FuncInfo.Candidates.end()) { FuncMeta.Parameters.emplace_back(); FuncMeta.Parameters.back().TargetParam = I; - FuncMeta.Parameters.back().IsPointer = false; + FuncMeta.Parameters.back().Kind = ReplacementMetadata::PK_Value; } else { for (auto &R : ReplacementItr->get()) { FuncMeta.Parameters.emplace_back(); FuncMeta.Parameters.back().TargetParam = I; FuncMeta.Parameters.back().TargetMember = cast(R.Member); - FuncMeta.Parameters.back().IsPointer = R.InAssignment; + if (R.Flags & Replacement::InArray) + FuncMeta.Parameters.back().Kind = ReplacementMetadata::PK_Array; + else if (R.Flags & Replacement::InAssignment) + FuncMeta.Parameters.back().Kind = ReplacementMetadata::PK_Pointer; + else + FuncMeta.Parameters.back().Kind = ReplacementMetadata::PK_Value; } } } @@ -1819,7 +2040,23 @@ bool ClangStructureReplacementPass::runOnModule(llvm::Module &M) { sanitizeCandidates(*FuncInfo); } } - fillImplicitReplacementMembers(SCC); + fillReplacementMembersFromCallees(SCC); + for (auto *CGN : *SCC) { + auto FuncInfo = tieCallGraphNode(CGN); + if (!FuncInfo || !FuncInfo->Strict) + continue; + SmallVector ToRemove; + for (auto &Candidate : FuncInfo->Candidates) { + if (any_of(Candidate.get(), + [](auto &R) { return R.Flags & Replacement::InArray; })) + ToRemove.push_back(Candidate.get()); + for_each(ToRemove, [this, FuncInfo](auto *ND) { + toDiag(mTfmCtx->getContext().getDiagnostics(), ND->getLocation(), + tsar::diag::warn_disable_replace_struct_array_strict); + FuncInfo->Candidates.erase(ND); + }); + } + } buildParameters(SCC); sanitizeCandidatesInCalls(SCC); if (!insertNewFunctions(SCC)) From 8f7bba264e68dc593d7f7637f44eda161b68c915 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Wed, 16 Mar 2022 13:52:17 +0300 Subject: [PATCH 04/93] [TSAR, Pragma] Update representation of pragmas to support identifiers of an aggregate type. --- lib/Frontend/Clang/PragmaHandlers.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/lib/Frontend/Clang/PragmaHandlers.cpp b/lib/Frontend/Clang/PragmaHandlers.cpp index 191226c5..8fec5b26 100644 --- a/lib/Frontend/Clang/PragmaHandlers.cpp +++ b/lib/Frontend/Clang/PragmaHandlers.cpp @@ -88,7 +88,7 @@ class DefaultClauseVisitor : /// (for identifier `A`). void visitEK_Identifier(Token &Tok) { assert(Tok.is(tok::identifier) && "Token must be an identifier!"); - // Each identifier 'I' will be replace by (void)(sizeof((long long)(I))). + // Each identifier 'I' will be replace by (void)(sizeof(&I)). // This construction is necessary to disable warnings for unused expressions // (cast to void) and to disable generation of LLVM IR for it (sizeof). // Cast to void inside 'sizeof' operator is necessary in case of variable @@ -99,22 +99,19 @@ class DefaultClauseVisitor : // (void)(sizeof((void)(A))) // This does not produce LLVM IR. // However it is forbidden to apply 'sizeof' to the void type in C++, // it is also forbidden to apply 'sizeof' to a function type in C++. - // So, we use 'long long' instead of 'void'. + // It is also forbidden to cast aggregate types to void and arithmetic + // types, so the use of ampersand instead of a cast operation allows + // using identifier of aggregate types. AddToken(tok::l_paren, Tok.getLocation(), 1, getReplacement()); AddToken(tok::kw_void, Tok.getLocation(), 1, getReplacement()); AddToken(tok::r_paren, Tok.getLocation(), 1, getReplacement()); AddToken(tok::l_paren, Tok.getLocation(), 1, getReplacement()); AddToken(tok::kw_sizeof, Tok.getLocation(), 1, getReplacement()); AddToken(tok::l_paren, Tok.getLocation(), 1, getReplacement()); - AddToken(tok::l_paren, Tok.getLocation(), 1, getReplacement()); - AddToken(tok::kw_long, Tok.getLocation(), 1, getReplacement()); - AddToken(tok::kw_long, Tok.getLocation(), 1, getReplacement()); - AddToken(tok::r_paren, Tok.getLocation(), 1, getReplacement()); - AddToken(tok::l_paren, Tok.getLocation(), 1, getReplacement()); + AddToken(tok::amp, Tok.getLocation(), 1, getReplacement()); getReplacement().push_back(Tok); AddToken(tok::r_paren, Tok.getLocation(), 1, getReplacement()); AddToken(tok::r_paren, Tok.getLocation(), 1, getReplacement()); - AddToken(tok::r_paren, Tok.getLocation(), 1, getReplacement()); AddToken(tok::semi, Tok.getLocation(), 1, getReplacement()); } From d8d75acf0ddc21783236455a3160d8745a1da4d0 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Wed, 16 Mar 2022 13:53:02 +0300 Subject: [PATCH 05/93] [TSAR, Tfm, Replace, Struct] Implement replacement of parameters passed by value. --- lib/Transform/Clang/StructureReplacement.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/Transform/Clang/StructureReplacement.cpp b/lib/Transform/Clang/StructureReplacement.cpp index f7fc3067..0d0cc3a6 100644 --- a/lib/Transform/Clang/StructureReplacement.cpp +++ b/lib/Transform/Clang/StructureReplacement.cpp @@ -629,6 +629,8 @@ class ReplacementCollector : public RecursiveASTVisitor { toDiag(mSrcMgr.getDiagnostics(), Expr->getBeginLoc(), tsar::diag::warn_disable_replace_struct_no_struct); } + } else if (auto StructTy = dyn_cast(Ty)) { + mCurrFunc->Candidates.try_emplace(PD); } else { toDiag(mSrcMgr.getDiagnostics(), Expr->getBeginLoc(), tsar::diag::warn_disable_replace_struct_no_pointer); @@ -852,7 +854,8 @@ class ReplacementSanitizer : public RecursiveASTVisitor { Cast && Cast->getCastKind() == CK_LValueToRValue) return Res; if (auto *Op{dyn_cast(S)}; - Op && Op->getOpcode() == UnaryOperatorKind::UO_Deref) + Op && (Op->getOpcode() == UnaryOperatorKind::UO_Deref || + Op->getOpcode() == UnaryOperatorKind::UO_AddrOf)) return Res; auto ND = mLastDeclRef->getFoundDecl(); toDiag(mSrcMgr.getDiagnostics(), ND->getBeginLoc(), @@ -1504,7 +1507,11 @@ void ClangStructureReplacementPass::sanitizeCandidates(FunctionInfo &FuncInfo) { Verifier.TraverseDecl(FuncInfo.Definition); for (auto &C : FuncInfo.Candidates) for (auto &R : C.get()) - if (isa(R.Member->getType())) + if (isa(C.get()) && + isa( + getCanonicalUnqualifiedType(cast(C.get())))) + R.Flags &= ~Replacement::InAssignment; + else if (isa(R.Member->getType())) R.Flags &= ~Replacement::InAssignment; else if (FuncInfo.Strict) R.Flags |= Replacement::InAssignment; From 720813d76198d13c7b8865a42c5bd9e853d85be9 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Fri, 18 Mar 2022 16:22:46 +0300 Subject: [PATCH 06/93] [TSAR, Tfm, Struct, Replace] Implement replacement of local variables of a structure type. --- include/tsar/Support/DiagnosticKinds.td | 5 +- lib/Transform/Clang/StructureReplacement.cpp | 323 +++++++++++++------ 2 files changed, 231 insertions(+), 97 deletions(-) diff --git a/include/tsar/Support/DiagnosticKinds.td b/include/tsar/Support/DiagnosticKinds.td index aaa3a93d..84768832 100644 --- a/include/tsar/Support/DiagnosticKinds.td +++ b/include/tsar/Support/DiagnosticKinds.td @@ -161,17 +161,18 @@ def warn_region_add_call_unable : Warning<"unable to mark function call for opti def warn_region_not_found : Warning<"optimization region with name '%0' not found">; def warn_disable_replace_struct : Warning<"disable structure replacement">; -def warn_disable_replace_struct_no_param : Warning<"unable to replace any variable except argument">; -def warn_disable_replace_struct_no_pointer : Warning<"disable replacement of a non-pointer type">; +def warn_disable_replace_struct_no_local : Warning<"unable to replace any object except an argument or a local variable">; def warn_disable_replace_struct_no_struct : Warning<"disable replacement of a non-structure type">; def warn_disable_replace_struct_system : Warning<"disable replacement of a non-user defined function">; def warn_disable_replace_struct_array_strict : Warning<"diasable strict replacement of a structure-type with array">; +def warn_disable_replace_struct_init : Warning<"inititialization prevent structure replacement">; def note_replace_struct_arrow: Note<"not-arrow access prevent replacement">; def note_replace_struct_decl: Note<"unable to build declaration of a record member">; def note_replace_struct_macro_prevent : Note<"macro prevent replacement">; def note_replace_struct_de_decl : Note<"unable to remove unused declaration">; def note_replace_struct_decl_internal : Note<"unable to replace declaration">; +def note_replace_struct_not_compound_stmt : Note<"unable to replace declaration not in a compound statement">; def remark_replace_struct: Remark<"structure replacement">; diff --git a/lib/Transform/Clang/StructureReplacement.cpp b/lib/Transform/Clang/StructureReplacement.cpp index 0d0cc3a6..466bde72 100644 --- a/lib/Transform/Clang/StructureReplacement.cpp +++ b/lib/Transform/Clang/StructureReplacement.cpp @@ -105,9 +105,10 @@ struct Replacement { /// replacement string. using ReplacementCandidates = SmallDenseMap< NamedDecl *, - std::tuple, std::string>, 8, + std::tuple, std::string>, 8, DenseMapInfo, TaggedDenseMapTuple, + bcl::tagged, bcl::tagged, Replacement>, bcl::tagged>>; @@ -182,6 +183,9 @@ struct FunctionInfo { /// clause, which should be replaced. ReplacementCandidates Candidates; + /// List of declarations statements which contain replacement candidates. + SmallSetVector DeclarationStmts; + /// List of calls from this function, which are marked with a 'with' clause, /// which should be replaced. ReplacementRequests Requests; @@ -619,25 +623,25 @@ class ReplacementCollector : public RecursiveASTVisitor { bool VisitReplaceClauseExpr(DeclRefExpr *Expr) { mCurrFunc->Meta.insert(Expr); auto ND = Expr->getFoundDecl(); - if (auto PD = dyn_cast(ND)) { - auto Ty = getCanonicalUnqualifiedType(PD); + if (auto VD{dyn_cast(ND)}; VD && VD->isLocalVarDeclOrParm()) { + auto Ty = getCanonicalUnqualifiedType(VD); if (auto PtrTy = dyn_cast(Ty)) { auto PointeeTy = PtrTy->getPointeeType().getTypePtr(); if (auto StructTy = dyn_cast(PointeeTy)) { - mCurrFunc->Candidates.try_emplace(PD); + mCurrFunc->Candidates.try_emplace(VD); } else { toDiag(mSrcMgr.getDiagnostics(), Expr->getBeginLoc(), tsar::diag::warn_disable_replace_struct_no_struct); } } else if (auto StructTy = dyn_cast(Ty)) { - mCurrFunc->Candidates.try_emplace(PD); + mCurrFunc->Candidates.try_emplace(VD); } else { toDiag(mSrcMgr.getDiagnostics(), Expr->getBeginLoc(), - tsar::diag::warn_disable_replace_struct_no_pointer); + tsar::diag::warn_disable_replace_struct_no_struct); } } else { toDiag(mSrcMgr.getDiagnostics(), Expr->getBeginLoc(), - tsar::diag::warn_disable_replace_struct_no_param); + tsar::diag::warn_disable_replace_struct_no_local); } return true; } @@ -824,6 +828,29 @@ class ReplacementSanitizer : public RecursiveASTVisitor { return Res; } + bool VisitVarDecl(VarDecl* VD) { + if (isa_and_nonnull(mParent)) + if (auto I{mReplacements.Candidates.find(VD)}; + I != mReplacements.Candidates.end()) { + if (!isa_and_nonnull(mScope)) { + toDiag(mSrcMgr.getDiagnostics(), VD->getLocation(), + tsar::diag::warn_disable_replace_struct); + if (mScope) + toDiag(mSrcMgr.getDiagnostics(), mScope->getBeginLoc(), + tsar::diag::note_replace_struct_not_compound_stmt); + mReplacements.Candidates.erase(I); + } + else if (VD->hasInit()) { + toDiag(mSrcMgr.getDiagnostics(), VD->getLocation(), + tsar::diag::warn_disable_replace_struct_init); + mReplacements.Candidates.erase(I); + } + I->get() = cast(mParent); + mReplacements.DeclarationStmts.insert(cast(mParent)); + } + return true; + } + bool VisitDeclRefExpr(DeclRefExpr *Expr) { if (mReplacements.inClause(Expr)) return true; @@ -846,7 +873,15 @@ class ReplacementSanitizer : public RecursiveASTVisitor { } bool TraverseStmt(Stmt *S) { + auto StashParent{mParent}; + mParent = S; + auto StashScope(mScope); + if (S && (isa(S) || isa(S) || isa(S) || + isa(S) || isa(S))) + mScope = S; auto Res{RecursiveASTVisitor::TraverseStmt(S)}; + mParent = StashParent; + mScope = StashScope; if (!mIsInnermostMember || !mLastDeclRef || isa(S) || isa(S) || isa(S)) return Res; @@ -918,6 +953,8 @@ class ReplacementSanitizer : public RecursiveASTVisitor { FunctionInfo &mReplacements; ReplacementMap &mReplacementInfo; + Stmt *mParent = nullptr; + Stmt *mScope = nullptr; bool mIsInnermostMember = false; DeclRefExpr *mLastDeclRef = nullptr; bool mInAssignment = true; @@ -1298,13 +1335,21 @@ class ClangStructureReplacementPass : /// Remove replacement candidates if it cannot be replaced in callee. void sanitizeCandidatesInCalls(scc_iterator &SCC); - void buildParameters(FunctionInfo &FuncInfo); - void buildParameters(scc_iterator &SCC) { + StringRef buildMemberDeclaration(VarDecl *VD, char Separator, Replacement &R, + std::string &Context, + SmallVectorImpl &DeclString); + + SmallString<128> + buildMemberDeclaration(char Separator, std::string &Context, + ReplacementCandidates::value_type &Candidate); + + void buildDeclarations(FunctionInfo &FuncInfo); + void buildDeclarations(scc_iterator &SCC) { for (auto *CGN : *SCC) { auto FuncInfo = tieCallGraphNode(CGN); if (!FuncInfo || !FuncInfo->hasCandidates()) continue; - buildParameters(*FuncInfo); + buildDeclarations(*FuncInfo); } } @@ -1328,18 +1373,24 @@ class ClangStructureReplacementPass : auto FuncInfo = tieCallGraphNode(CGN); if (!FuncInfo || !FuncInfo->hasCandidates()) continue; - auto OriginDefString = Lexer::getSourceText( - CharSourceRange::getTokenRange( - FuncInfo->Definition->getBeginLoc(), - FuncInfo->Definition - ->getParamDecl(FuncInfo->Definition->getNumParams() - 1) - ->getEndLoc()), - SrcMgr, LangOpts); + auto OriginDefStart{Lexer::getSourceText( + CharSourceRange::getTokenRange(FuncInfo->Definition->getBeginLoc(), + FuncInfo->Definition->getLocation()), + SrcMgr, LangOpts)}; + StringRef OriginDefParams; + if (FuncInfo->Definition->getNumParams() > 0) + OriginDefParams = Lexer::getSourceText( + CharSourceRange::getTokenRange( + FuncInfo->Definition->getParamDecl(0)->getBeginLoc(), + FuncInfo->Definition + ->getParamDecl(FuncInfo->Definition->getNumParams() - 1) + ->getEndLoc()), + SrcMgr, LangOpts); auto LocToInsert = SrcMgr.getExpansionLoc(FuncInfo->Definition->getEndLoc()); - Rewriter.InsertTextAfterToken( - LocToInsert, - ("\n\n/* Replacement for " + OriginDefString + ") */\n").str()); + Rewriter.InsertTextAfterToken(LocToInsert, + ("\n\n/* Replacement for " + + OriginDefStart + "(" + OriginDefParams + ") */\n").str()); Rewriter.InsertTextAfterToken(LocToInsert, CanvasItr->getBuffer()); } return true; @@ -1493,10 +1544,10 @@ void ClangStructureReplacementPass::sanitizeCandidates(FunctionInfo &FuncInfo) { } else { SmallVector ToRemove; for (auto &C : FuncInfo.Candidates) { - auto *PD = C.get(); - if (!checkMacroBoundsAndEmitDiag(PD, FuncInfo.Definition->getLocation(), + auto *VD = C.get(); + if (!checkMacroBoundsAndEmitDiag(VD, FuncInfo.Definition->getLocation(), SrcMgr, LangOpts)) { - ToRemove.push_back(PD); + ToRemove.push_back(VD); continue; } } @@ -1753,7 +1804,62 @@ static Optional buildParameterType(const Replacement &R) { return ParamType; } -void ClangStructureReplacementPass::buildParameters(FunctionInfo &FuncInfo) { +inline StringRef ClangStructureReplacementPass::buildMemberDeclaration( + VarDecl *VD, char Separator, Replacement &R, + std::string &Context, SmallVectorImpl &DeclString) { + auto &SrcMgr{mTfmCtx->getContext().getSourceManager()}; + TypeSearch TS(VD, R.Member, SrcMgr, *mGlobalInfo); + TS.TraverseDecl(R.Member); + if (!TS.isOk()) + return StringRef{}; + addSuffix(VD->getName() + "_" + R.Member->getName(), R.Identifier); + auto ParamType{buildParameterType(R)}; + if (!ParamType) + return StringRef{}; + StringMap Replacements; + auto Tokens{ + buildDeclStringRef(*ParamType, R.Identifier, Context, Replacements)}; + if (Tokens.empty()) + return StringRef{}; + if (!DeclString.empty()) + DeclString.push_back(Separator); + auto Size{DeclString.size()}; + join(Tokens.begin(), Tokens.end(), " ", DeclString); + Context += StringRef(DeclString.data() + Size, DeclString.size() - Size); + Context += ";"; + return StringRef{DeclString.data() + Size, DeclString.size() - Size}; +} + +inline SmallString<128> ClangStructureReplacementPass::buildMemberDeclaration( + char Separator, std::string &Context, + ReplacementCandidates::value_type &Candidate) { + auto &SrcMgr{mTfmCtx->getContext().getSourceManager()}; + SmallString<128> NewDecls; + auto StashContextSize = Context.size(); + for (auto &R : Candidate.get()) { + auto NewDecl{ + buildMemberDeclaration(cast(Candidate.get()), + Separator, R, Context, NewDecls)}; + if (NewDecl.empty()) { + Context.resize(StashContextSize); + NewDecls.clear(); + toDiag(SrcMgr.getDiagnostics(), Candidate.get()->getLocation(), + tsar::diag::warn_disable_replace_struct); + toDiag(SrcMgr.getDiagnostics(), R.Member->getBeginLoc(), + tsar::diag::note_replace_struct_decl); + break; + } + LLVM_DEBUG(dbgs() << "[REPLACE]: replacement for " + << Candidate.get()->getName() + << (isa(Candidate.get()) + ? " parameter: " + : " variable: ") + << NewDecl << "\n"); + } + return NewDecls; +} + +void ClangStructureReplacementPass::buildDeclarations(FunctionInfo &FuncInfo) { auto &SrcMgr = mTfmCtx->getContext().getSourceManager(); auto &LangOpts = mTfmCtx->getContext().getLangOpts(); // Look up for declaration of types of parameters. @@ -1797,59 +1903,30 @@ void ClangStructureReplacementPass::buildParameters(FunctionInfo &FuncInfo) { if (ReplacementItr == FuncInfo.Candidates.end() || ReplacementItr->get().empty()) continue; - SmallString<128> NewParams; - auto StashContextSize = Context.size(); - for (auto &R : ReplacementItr->get()) { - TypeSearch TS(PD, R.Member, SrcMgr, *mGlobalInfo); - TS.TraverseDecl(R.Member); - if (!TS.isOk()) { - Context.resize(StashContextSize); - NewParams.clear(); - break; - } - addSuffix(PD->getName() + "_" + R.Member->getName(), R.Identifier); - auto ParamType{buildParameterType(R)}; - if (!ParamType) { - Context.resize(StashContextSize); - NewParams.clear(); - toDiag(SrcMgr.getDiagnostics(), PD->getLocation(), - tsar::diag::warn_disable_replace_struct); - toDiag(SrcMgr.getDiagnostics(), R.Member->getBeginLoc(), - tsar::diag::note_replace_struct_decl); - break; - } - auto Tokens = - buildDeclStringRef(*ParamType, R.Identifier, Context, Replacements); - if (Tokens.empty()) { - Context.resize(StashContextSize); - NewParams.clear(); - toDiag(SrcMgr.getDiagnostics(), PD->getLocation(), - tsar::diag::warn_disable_replace_struct); - toDiag(SrcMgr.getDiagnostics(), R.Member->getBeginLoc(), - tsar::diag::note_replace_struct_decl); - break; - } - if (!NewParams.empty()) - NewParams.push_back(','); - auto Size = NewParams.size(); - join(Tokens.begin(), Tokens.end(), " ", NewParams); - Context += StringRef(NewParams.data() + Size, NewParams.size() - Size); - Context += ";"; - LLVM_DEBUG(dbgs() << "[REPLACE]: replacement for " << I - << " parameter: " - << StringRef(NewParams.data() + Size, - NewParams.size() - Size) - << "\n"); - } + auto NewParams{buildMemberDeclaration(',', Context, *ReplacementItr)}; if (!NewParams.empty()) ReplacementItr->get() = std::string(NewParams); else FuncInfo.Candidates.erase(ReplacementItr); } + // Replace aggregate variables with separate variables. + SmallVector ToRemove; + for (auto &Candidate : FuncInfo.Candidates) { + if (isa(Candidate.get())) + continue; + if (Candidate.get().empty()) + continue; + auto NewDecls{buildMemberDeclaration(';', Context, Candidate)}; + if (!NewDecls.empty()) + Candidate.get() = (NewDecls + ";").str(); + else + ToRemove.push_back(Candidate.get()); + } + for_each(ToRemove, [&FuncInfo](auto *ND) { FuncInfo.Candidates.erase(ND); }); } -bool ClangStructureReplacementPass::insertNewFunction(FunctionInfo &FuncInfo, - SmallVectorImpl &CanvasList) { +bool ClangStructureReplacementPass::insertNewFunction( + FunctionInfo &FuncInfo, SmallVectorImpl &CanvasList) { auto &Rewriter = mTfmCtx->getRewriter(); auto &SrcMgr = Rewriter.getSourceMgr(); auto &LangOpts = Rewriter.getLangOpts(); @@ -1862,14 +1939,29 @@ bool ClangStructureReplacementPass::insertNewFunction(FunctionInfo &FuncInfo, SrcMgr, LangOpts); auto &Canvas = CanvasList.back(); // Build unique name for a new function. - addSuffix(FuncInfo.Definition->getName() + "_spf", - FuncInfo.ReplacmenetName); + addSuffix(FuncInfo.Definition->getName() + "_spf", FuncInfo.ReplacmenetName); SourceRange NameRange( SrcMgr.getExpansionLoc(FuncInfo.Definition->getLocation())); NameRange.setEnd(NameRange.getBegin().getLocWithOffset( FuncInfo.Definition->getName().size() - 1)); Canvas.ReplaceText(NameRange, FuncInfo.ReplacmenetName); - // Replace aggregate parameters with separate variables. + // Replace accesses to variables. + auto replaceAccesses = [&SrcMgr, &Canvas](auto ReplacementItr) { + for (auto &R : ReplacementItr->template get()) { + for (auto Range : R.RangesToReplace) { + SmallString<64> Tmp; + auto AccessString = (R.Flags & Replacement::InAssignment && + !(R.Flags & Replacement::InArray)) + ? ("(*" + R.Identifier + ")").toStringRef(Tmp) + : StringRef(R.Identifier); + Canvas.ReplaceText(getExpansionRange(SrcMgr, Range).getAsRange(), + AccessString); + } + for (auto Range : R.RangesToRemove) + Canvas.RemoveText(getExpansionRange(SrcMgr, Range).getAsRange()); + } + }; + // Replace aggregate parameters with separate variables. bool TheLastParam = true; for (unsigned I = FuncInfo.Definition->getNumParams(); I > 0; --I) { auto *PD = FuncInfo.Definition->getParamDecl(I - 1); @@ -1878,10 +1970,10 @@ bool ClangStructureReplacementPass::insertNewFunction(FunctionInfo &FuncInfo, if (TheLastParam || (ReplacementItr != FuncInfo.Candidates.end() && ReplacementItr->get().empty())) { Token CommaTok; - if (getRawTokenAfter(SrcMgr.getExpansionLoc(EndLoc), SrcMgr, - LangOpts, CommaTok)) { + if (getRawTokenAfter(SrcMgr.getExpansionLoc(EndLoc), SrcMgr, LangOpts, + CommaTok)) { toDiag(SrcMgr.getDiagnostics(), PD->getEndLoc(), - tsar::diag::warn_transform_internal); + tsar::diag::warn_transform_internal); return false; } if (CommaTok.is(tok::comma)) @@ -1898,12 +1990,13 @@ bool ClangStructureReplacementPass::insertNewFunction(FunctionInfo &FuncInfo, // We also remove an unused parameter if it is mentioned in replace clause. if (ReplacementItr->get().empty()) { Canvas.RemoveText(CharSourceRange::getTokenRange( - SrcMgr.getExpansionLoc(PD->getBeginLoc()), - SrcMgr.getExpansionLoc(EndLoc)).getAsRange()); + SrcMgr.getExpansionLoc(PD->getBeginLoc()), + SrcMgr.getExpansionLoc(EndLoc)) + .getAsRange()); toDiag(SrcMgr.getDiagnostics(), PD->getLocation(), - tsar::diag::remark_replace_struct); + tsar::diag::remark_replace_struct); toDiag(SrcMgr.getDiagnostics(), PD->getBeginLoc(), - tsar::diag::remark_remove_de_decl); + tsar::diag::remark_remove_de_decl); // Do not update TheLastParam variable. If the current parameter is the // last in the list and if it is removed than the previous parameter // in the list become the last one. @@ -1911,22 +2004,62 @@ bool ClangStructureReplacementPass::insertNewFunction(FunctionInfo &FuncInfo, } TheLastParam = false; auto ReplaceRange = CharSourceRange::getTokenRange( - SrcMgr.getExpansionLoc(PD->getBeginLoc()), - SrcMgr.getExpansionLoc(EndLoc)).getAsRange(); + SrcMgr.getExpansionLoc(PD->getBeginLoc()), + SrcMgr.getExpansionLoc(EndLoc)) + .getAsRange(); Canvas.ReplaceText(ReplaceRange, ReplacementItr->get()); - // Replace accesses to parameters. - for (auto &R : ReplacementItr->get()) { - for (auto Range : R.RangesToReplace) { - SmallString<64> Tmp; - auto AccessString = (R.Flags & Replacement::InAssignment && - !(R.Flags & Replacement::InArray)) - ? ("(*" + R.Identifier + ")").toStringRef(Tmp) - : StringRef(R.Identifier); - Canvas.ReplaceText(getExpansionRange(SrcMgr, Range).getAsRange(), - AccessString); + replaceAccesses(ReplacementItr); + } + for (auto &DS : FuncInfo.DeclarationStmts) { + Decl *NotToReplace{nullptr}; + SmallVector Unused, Candidates; + for (auto *D : DS->getDeclGroup()) { + auto ReplacementItr{FuncInfo.Candidates.find(cast(D))}; + if (ReplacementItr == FuncInfo.Candidates.end()) { + NotToReplace = D; + continue; + } + Candidates.push_back(D); + if (!ReplacementItr->get().empty()) { + if (DS->getEndLoc().isMacroID()) + Canvas.InsertTextAfterToken(SrcMgr.getExpansionLoc(DS->getEndLoc()), + " "); + Canvas.InsertTextAfterToken(SrcMgr.getExpansionLoc(DS->getEndLoc()), + ReplacementItr->get()); + replaceAccesses(ReplacementItr); + } else { + Unused.push_back(D); + } + } + if (!NotToReplace) { + if (!checkMacroBoundsAndEmitDiag(DS, FuncInfo.Definition->getLocation(), + SrcMgr, LangOpts)) { + for (auto *D : Candidates) { + toDiag(SrcMgr.getDiagnostics(), D->getLocation(), + tsar::diag::remark_replace_struct); + toDiag(SrcMgr.getDiagnostics(), D->getLocation(), + tsar::diag::note_replace_struct_de_decl); + } + } else { + Canvas.RemoveText(SrcMgr.getExpansionRange(DS->getSourceRange())); + for (auto *D : Unused) { + toDiag(SrcMgr.getDiagnostics(), D->getLocation(), + tsar::diag::remark_replace_struct); + toDiag(SrcMgr.getDiagnostics(), D->getLocation(), + tsar::diag::remark_remove_de_decl); + } + } + } else { + toDiag(SrcMgr.getDiagnostics(), DS->getBeginLoc(), + tsar::diag::remark_replace_struct); + toDiag(SrcMgr.getDiagnostics(), NotToReplace->getLocation(), + tsar::diag::note_de_multiple_prevent); + for (auto *D : Candidates) { + toDiag(SrcMgr.getDiagnostics(), D->getLocation(), + tsar::diag::remark_replace_struct); + toDiag(SrcMgr.getDiagnostics(), D->getLocation(), + tsar::diag::note_replace_struct_de_decl); } - for (auto Range : R.RangesToRemove) - Canvas.RemoveText(getExpansionRange(SrcMgr, Range).getAsRange()); } } // Build implicit metadata. @@ -2064,7 +2197,7 @@ bool ClangStructureReplacementPass::runOnModule(llvm::Module &M) { }); } } - buildParameters(SCC); + buildDeclarations(SCC); sanitizeCandidatesInCalls(SCC); if (!insertNewFunctions(SCC)) return false; From c76f9a0aab35e8c6466da977b4da85e643f41cab Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Mon, 21 Mar 2022 00:24:24 +0300 Subject: [PATCH 07/93] [TSAR, Anls, Include, Clang] Do not process files twice. --- lib/Analysis/Clang/IncludeTree.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Analysis/Clang/IncludeTree.cpp b/lib/Analysis/Clang/IncludeTree.cpp index 3a4f2835..0122f643 100644 --- a/lib/Analysis/Clang/IncludeTree.cpp +++ b/lib/Analysis/Clang/IncludeTree.cpp @@ -78,7 +78,7 @@ bool FileTree::reconstruct(TransformationContextBase &TfmCtx, // Do not use FileInfo.first iterator after insert() inside the loop // because insert() may invalidate iterators. auto *FN = &*FileInfo.first; - while (Loc.first.isValid()) { + while (Loc.first.isValid() && FileInfo.second) { auto ParentInfo = insert(Loc.first, *TfmCtxImpl); assert(ParentInfo.first != file_end() && "FileNode must not be null!"); ParentInfo.first->push_back(FN); From b4222c69f1f6829a5e2b5f26a1d7bbebdc505923 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Mon, 21 Mar 2022 00:24:49 +0300 Subject: [PATCH 08/93] [TSAR, Anls, Include, Clang] Fix, trivial. --- lib/Analysis/Clang/IncludeTree.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/Analysis/Clang/IncludeTree.cpp b/lib/Analysis/Clang/IncludeTree.cpp index 0122f643..a0c5a1d9 100644 --- a/lib/Analysis/Clang/IncludeTree.cpp +++ b/lib/Analysis/Clang/IncludeTree.cpp @@ -79,11 +79,10 @@ bool FileTree::reconstruct(TransformationContextBase &TfmCtx, // because insert() may invalidate iterators. auto *FN = &*FileInfo.first; while (Loc.first.isValid() && FileInfo.second) { - auto ParentInfo = insert(Loc.first, *TfmCtxImpl); - assert(ParentInfo.first != file_end() && "FileNode must not be null!"); - ParentInfo.first->push_back(FN); - FileInfo = std::move(ParentInfo); - auto *FN = &*FileInfo.first; + FileInfo = insert(Loc.first, *TfmCtxImpl); + assert(FileInfo.first != file_end() && "FileNode must not be null!"); + FileInfo.first->push_back(FN); + FN = &*FileInfo.first; Loc = SrcMgr.getDecomposedIncludedLoc(Loc.first); } } From 66995712d357c38634e2e12c958332a49dc912d0 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Mon, 21 Mar 2022 00:28:13 +0300 Subject: [PATCH 09/93] [TSAR, Clang, Tfm, Replace] Allow using of a default constructor to initialize a variable to be replaced. --- lib/Transform/Clang/StructureReplacement.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/Transform/Clang/StructureReplacement.cpp b/lib/Transform/Clang/StructureReplacement.cpp index 466bde72..16a422ab 100644 --- a/lib/Transform/Clang/StructureReplacement.cpp +++ b/lib/Transform/Clang/StructureReplacement.cpp @@ -839,11 +839,14 @@ class ReplacementSanitizer : public RecursiveASTVisitor { toDiag(mSrcMgr.getDiagnostics(), mScope->getBeginLoc(), tsar::diag::note_replace_struct_not_compound_stmt); mReplacements.Candidates.erase(I); - } - else if (VD->hasInit()) { - toDiag(mSrcMgr.getDiagnostics(), VD->getLocation(), - tsar::diag::warn_disable_replace_struct_init); - mReplacements.Candidates.erase(I); + } else if (auto Init{VD->getInit()}) { + if (!(isa(Init) && cast(Init) + ->getConstructor() + ->isDefaultConstructor())) { + toDiag(mSrcMgr.getDiagnostics(), VD->getLocation(), + tsar::diag::warn_disable_replace_struct_init); + mReplacements.Candidates.erase(I); + } } I->get() = cast(mParent); mReplacements.DeclarationStmts.insert(cast(mParent)); From 81558bcec44f59ad4be74b555284bd3b1e87837b Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Mon, 21 Mar 2022 00:29:13 +0300 Subject: [PATCH 10/93] [TSAR, Clang, Tfm, Replace] Fix GCC compilation errors. --- lib/Transform/Clang/StructureReplacement.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Transform/Clang/StructureReplacement.cpp b/lib/Transform/Clang/StructureReplacement.cpp index 16a422ab..edf5dc73 100644 --- a/lib/Transform/Clang/StructureReplacement.cpp +++ b/lib/Transform/Clang/StructureReplacement.cpp @@ -1726,7 +1726,7 @@ void ClangStructureReplacementPass::sanitizeCandidatesInCalls( if (Itr == FuncInfo->Candidates.end()) return; toDiag(SrcMgr.getDiagnostics(), - Itr->get()->getLocation(), + Itr->template get()->getLocation(), tsar::diag::warn_disable_replace_struct); toDiag(SrcMgr.getDiagnostics(), Arg->getBeginLoc(), tsar::diag::note_replace_struct_arrow); From a3396dfb6ecf5e5cb40b6159acb07f8ddffdfdfc Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Mon, 21 Mar 2022 00:30:31 +0300 Subject: [PATCH 11/93] [TSAR, Tfm, Clang, Replace] Allow the pass to replace macro arguments. --- lib/Transform/Clang/StructureReplacement.cpp | 44 +++++++++++++++----- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/lib/Transform/Clang/StructureReplacement.cpp b/lib/Transform/Clang/StructureReplacement.cpp index edf5dc73..564ca08d 100644 --- a/lib/Transform/Clang/StructureReplacement.cpp +++ b/lib/Transform/Clang/StructureReplacement.cpp @@ -259,6 +259,23 @@ ReplacementCandidates::iterator isExprInCandidates(const clang::Expr *ArgExpr, return Candidates.end(); } +bool isImmediateMacroArg(SourceLocation Loc, const SourceManager &SrcMgr) { + assert(Loc.isValid() && "Location must be valid!"); + if (!Loc.isMacroID()) + return false; + auto &EI{SrcMgr.getSLocEntry(SrcMgr.getFileID(Loc)).getExpansion()}; + bool IsArg{EI.isMacroArgExpansion()}; + Loc = EI.getExpansionLocStart(); + bool IsPrevArg{IsArg}; + while (!Loc.isFileID()) { + IsPrevArg = IsArg; + auto &EI{SrcMgr.getSLocEntry(SrcMgr.getFileID(Loc)).getExpansion()}; + IsArg = EI.isMacroArgExpansion(); + Loc = EI.getExpansionLocStart(); + } + return IsPrevArg; +} + template bool checkMacroBoundsAndEmitDiag(T *Obj, SourceLocation FuncLoc, const SourceManager &SrcMgr, @@ -282,8 +299,6 @@ bool checkMacroBoundsAndEmitDiag(T *Obj, SourceLocation FuncLoc, return true; } - - using CallList = std::vector; /// This class collects all 'replace' clauses in the code. @@ -910,10 +925,12 @@ class ReplacementSanitizer : public RecursiveASTVisitor { bool CurrInAssignment = mInAssignment; auto Res = RecursiveASTVisitor::TraverseMemberExpr(Expr); if (mIsInnermostMember && mLastDeclRef) { + bool IsMacroArg{isImmediateMacroArg(Expr->getBeginLoc(), mSrcMgr) && + isImmediateMacroArg(Expr->getEndLoc(), mSrcMgr)}; auto ND = mLastDeclRef->getFoundDecl(); - if (!checkMacroBoundsAndEmitDiag( - Expr, mReplacements.Definition->getLocation(), mSrcMgr, - mLangOpts)) { + if (!IsMacroArg && !checkMacroBoundsAndEmitDiag( + Expr, mReplacements.Definition->getLocation(), + mSrcMgr, mLangOpts)) { mReplacements.Candidates.erase(ND); } else { auto ReplacementItr = mReplacements.Candidates.find(ND); @@ -921,13 +938,20 @@ class ReplacementSanitizer : public RecursiveASTVisitor { ReplacementItr->get()); if (auto *Subscript{ dyn_cast(*Expr->child_begin())}) { - Itr->RangesToReplace.emplace_back( - Subscript->getBase()->getSourceRange()); - Itr->RangesToRemove.emplace_back( - SourceRange{Expr->getOperatorLoc(), Expr->getEndLoc()}); + auto ToReplace{Subscript->getBase()->getSourceRange()}; + if (IsMacroArg) + ToReplace = getSpellingRange(mSrcMgr, ToReplace).getAsRange(); + Itr->RangesToReplace.emplace_back(std::move(ToReplace)); + SourceRange ToRemove{Expr->getOperatorLoc(), Expr->getEndLoc()}; + if (IsMacroArg) + ToRemove = getSpellingRange(mSrcMgr, ToRemove).getAsRange(); + Itr->RangesToRemove.emplace_back(std::move(ToRemove)); Itr->Flags |= Replacement::InArray; } else { - Itr->RangesToReplace.emplace_back(Expr->getSourceRange()); + auto Range(Expr->getSourceRange()); + if (IsMacroArg) + Range = getSpellingRange(mSrcMgr, Range).getAsRange(); + Itr->RangesToReplace.emplace_back(std::move(Range)); } if (CurrInAssignment) Itr->Flags |= Replacement::InAssignment; From f0d38725c8adfcfa6374099915ab153c69f5fbeb Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Mon, 21 Mar 2022 00:32:44 +0300 Subject: [PATCH 12/93] [TSAR, Clang, Tfm, Replace] Fix, disable `InAssignment` flag if an object of a structure type is replaced directly. --- lib/Transform/Clang/StructureReplacement.cpp | 21 ++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/lib/Transform/Clang/StructureReplacement.cpp b/lib/Transform/Clang/StructureReplacement.cpp index 564ca08d..9a44abce 100644 --- a/lib/Transform/Clang/StructureReplacement.cpp +++ b/lib/Transform/Clang/StructureReplacement.cpp @@ -1652,15 +1652,24 @@ void ClangStructureReplacementPass::fillReplacementMembersFromCallees( return R.Member == CalleeR.Member; }); if (CallerRItr != Itr->get().end()) { - if (!(CallerRItr->Flags & Replacement::InAssignment) && - CalleeR.Flags & Replacement::InAssignment) - IsChanged = (CallerRItr->Flags |= Replacement::InAssignment); - if (!(CallerRItr->Flags & Replacement::InArray) && - CalleeR.Flags & Replacement::InArray) - IsChanged = (CallerRItr->Flags |= Replacement::InArray); + if (!(isa(Itr->get()) && + isa(getCanonicalUnqualifiedType( + cast(Itr->get()))))) { + if (!(CallerRItr->Flags & Replacement::InAssignment) && + CalleeR.Flags & Replacement::InAssignment) + IsChanged = (CallerRItr->Flags |= Replacement::InAssignment); + if (!(CallerRItr->Flags & Replacement::InArray) && + CalleeR.Flags & Replacement::InArray) + IsChanged = (CallerRItr->Flags |= Replacement::InArray); + } } else { Itr->get().emplace_back(CalleeR.Member); Itr->get().back().Flags = CalleeR.Flags; + if (isa(Itr->get()) && + isa(getCanonicalUnqualifiedType( + cast(Itr->get())))) + Itr->get().back().Flags &= + ~Replacement::InAssignment; IsChanged = true; } } From 56867d21600c7dc01efc0a298b03edcccb1d9dfa Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Mon, 21 Mar 2022 00:35:13 +0300 Subject: [PATCH 13/93] [TSAR, Tfm, Replace, Clang] Replace an actual argument if it is a part of '&' or '*' expression. --- lib/Transform/Clang/StructureReplacement.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/Transform/Clang/StructureReplacement.cpp b/lib/Transform/Clang/StructureReplacement.cpp index 9a44abce..185ecf63 100644 --- a/lib/Transform/Clang/StructureReplacement.cpp +++ b/lib/Transform/Clang/StructureReplacement.cpp @@ -239,6 +239,12 @@ using ReplacementMap = clang::DeclRefExpr *getCandidate(clang::Expr *ArgExpr) { + while (auto *UO{dyn_cast(ArgExpr)}) + if (UO->getOpcode() == UnaryOperatorKind::UO_Deref || + UO->getOpcode() == UnaryOperatorKind::UO_AddrOf) + ArgExpr = UO->getSubExpr(); + else + break; if (auto *Cast = dyn_cast(ArgExpr)) if (Cast->getCastKind() == CK_LValueToRValue) ArgExpr = Cast->getSubExpr(); @@ -246,6 +252,12 @@ clang::DeclRefExpr *getCandidate(clang::Expr *ArgExpr) { } const clang::DeclRefExpr *getCandidate(const clang::Expr *ArgExpr) { + while (auto *UO{dyn_cast(ArgExpr)}) + if (UO->getOpcode() == UnaryOperatorKind::UO_Deref || + UO->getOpcode() == UnaryOperatorKind::UO_AddrOf) + ArgExpr = UO->getSubExpr(); + else + break; if (auto *Cast = dyn_cast(ArgExpr)) if (Cast->getCastKind() == CK_LValueToRValue) ArgExpr = Cast->getSubExpr(); From 820422697b40f212c98836bfe4d1edbeaee33589 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Wed, 30 Mar 2022 12:11:05 +0300 Subject: [PATCH 14/93] [TSAR, APC, Parallel] Implement parallelization of loops without distributed arrays. --- lib/APC/AstWrapperImpl.h | 23 +++++++++++- lib/APC/ClangDVMHWriter.cpp | 36 +++++++++++++++++- lib/APC/Parallelization.cpp | 74 +++++++++++++++++++++++++++++-------- 3 files changed, 115 insertions(+), 18 deletions(-) diff --git a/lib/APC/AstWrapperImpl.h b/lib/APC/AstWrapperImpl.h index c4dd8c64..80f2e327 100644 --- a/lib/APC/AstWrapperImpl.h +++ b/lib/APC/AstWrapperImpl.h @@ -36,7 +36,7 @@ struct LoopGraph; struct FuncInfo; -namespace Distribution{ +namespace Distribution { class Array; } @@ -44,6 +44,8 @@ namespace tsar { class APCContext; } +class Statement; + class Symbol { public: using Redeclarations = llvm::SmallVector; @@ -56,6 +58,10 @@ class Symbol { : mContext(Ctx), mMemory(DIM) { assert(Ctx && "Context must not be null!"); } + Symbol(tsar::APCContext *Ctx, Statement *S) + : mContext(Ctx), mMemory(S) { + assert(Ctx && "Context must not be null!"); + } virtual ~Symbol() {} @@ -68,6 +74,19 @@ class Symbol { return std::get(mMemory); } + bool isStatement() const { + return std::holds_alternative(mMemory); + } + + auto *getStatement() { return std::get(mMemory); } + const auto *getStatement() const { + return std::get(mMemory); + } + + bool isVariable() const { + return std::holds_alternative(mMemory); + } + auto &getVariable() { return std::get(mMemory); } const auto &getVariable() const { return std::get(mMemory); } @@ -96,7 +115,7 @@ class Symbol { const tsar::APCContext *getContext() const noexcept { return mContext; } private: tsar::APCContext *mContext; - std::variant mMemory; + std::variant mMemory; }; class File {}; diff --git a/lib/APC/ClangDVMHWriter.cpp b/lib/APC/ClangDVMHWriter.cpp index e8de114c..8279b09c 100644 --- a/lib/APC/ClangDVMHWriter.cpp +++ b/lib/APC/ClangDVMHWriter.cpp @@ -439,6 +439,7 @@ bool APCClangDVMHWriter::runOnModule(llvm::Module &M) { }; std::map Globals; DenseMap> Locals; + DenseMap> Loops; }; DenseMap ArraysInContext; auto emitTfmError = [](const Function &F) { @@ -449,6 +450,27 @@ bool APCClangDVMHWriter::runOnModule(llvm::Module &M) { for (auto &AR : DataDirs.alignRules) { auto *APCSymbol{AR.alignArray->GetDeclSymbol()}; assert(APCSymbol && "Symbol must not be null!"); + if (APCSymbol->isStatement()) { + if (auto *LS{dyn_cast(APCSymbol->getStatement())}) { + auto &F{*LS->getFunction()}; + if (auto *DISub{findMetadata(&F)}) + if (auto *CU{DISub->getUnit()}; + isC(CU->getSourceLanguage()) || isCXX(CU->getSourceLanguage())) { + auto *TfmCtx{TfmInfo ? dyn_cast_or_null( + TfmInfo->getContext(*CU)) + : nullptr}; + if (TfmCtx && TfmCtx->hasInstance()) { + auto Itr{ArraysInContext.try_emplace(TfmCtx).first}; + auto FuncItr{Itr->second.Loops.try_emplace(&F).first}; + FuncItr->second.push_back(&AR); + continue; + } + } + emitTfmError(F); + return false; + } + continue; + } for (auto &Var : APCSymbol->getVariable()) { auto *DIAN{ Var.get()->getAliasNode() }; auto *DIAT{ DIAN->getAliasTree() }; @@ -495,7 +517,7 @@ bool APCClangDVMHWriter::runOnModule(llvm::Module &M) { // We should add declaration of template before 'align' directive. // So, we remember file with 'align' directive if this directive // has been successfully inserted. - if (DefLoc.isValid()) { + if (DefLoc.isValid() && AR.alignWith->IsTemplate()) { auto &SrcMgr = TfmCtx->getContext().getSourceManager(); auto FID = SrcMgr.getFileID(DefLoc); auto TplItr = Templates.try_emplace(FID).first; @@ -530,6 +552,18 @@ bool APCClangDVMHWriter::runOnModule(llvm::Module &M) { insertAlignAndCollectTpl(*Var.get(), *AR); NotDistrCanonicalDecls.erase(Var.get()); } + for (auto &&[F, Loops] : Arrays.Loops) { + auto *FD{cast(TfmCtx->getDeclForMangledName(F->getName()))}; + assert(FD && "AST-level function declaration must not be null!"); + auto &SrcMgr{TfmCtx->getContext().getSourceManager()}; + auto FID{SrcMgr.getFileID(FD->getBeginLoc())}; + auto TplItr{Templates.try_emplace(FID).first}; + for (auto *AR : Loops) { + if (!AR->alignWith->IsTemplate()) + continue; + TplItr->second.try_emplace(AR->alignWith); + } + } checkNotDistributedDecls(NotDistrCanonicalDecls, *TfmCtx); auto visitRealign = [&TemplatesMap, &Templates](PragmaRealign *Realign, FileID FID) { diff --git a/lib/APC/Parallelization.cpp b/lib/APC/Parallelization.cpp index f149079e..c0d7a626 100644 --- a/lib/APC/Parallelization.cpp +++ b/lib/APC/Parallelization.cpp @@ -104,6 +104,13 @@ using FileToLoopMap = std::map>; /// Map from a file name to a list of messages emitted for this file. using FileToMessageMap = APCContextImpl::FileDiagnostics; +/// Map from a function to a list of top level loops. +using FuncToLoopMap = DenseMap< + apc::FuncInfo *, std::tuple>, + DenseMapInfo, + TaggedDenseMapTuple, + bcl::tagged, Root>>>; + /// Map from a loop to a set of accessed arrays and descriptions of accesses. using LoopToArrayMap = std::map>; @@ -158,7 +165,7 @@ class APCParallelizationPass : public ModulePass, private bcl::Uncopyable { void collectFunctionsAndLoops( Module &M, APCContext &APCCtx, NameToFunctionMap &Functions, FileToFuncMap &FileToFunc, FileInfoMap &Files, FileToLoopMap &FileToLoop, - APCToIRLoopsMap &APCToIRLoops); + FuncToLoopMap &FuncToLoop, APCToIRLoopsMap &APCToIRLoops); void collectArrayAccessInfo( Module &M, DIArrayAccess &Access, APCContext &APCCtx, @@ -170,14 +177,15 @@ class APCParallelizationPass : public ModulePass, private bcl::Uncopyable { void buildDataDistributionGraph( const RegionList &Regions, const FormalToActualMap &FormalToActual, - const ArrayAccessSummary &ArrayRWs, FileToLoopMap &FileToLoop, + const ArrayAccessSummary &ArrayRWs, const FileToFuncMap &FileToFunc, + const FuncToLoopMap &FuncToLoop, FileToLoopMap &FileToLoop, FileInfoMap &Files, KeyToArrayMap &CreatedArrays, FileToMessageMap &APCMsgs); void buildDataDistribution( const RegionList &Regions, const FormalToActualMap &FormalToActual, - const ArrayToKeyMap &ArrayIds, APCContext &APCCtx, - DVMHParallelizationContext &ParallelCtx, + const ArrayToKeyMap &ArrayIds, const FileInfoMap &Files, + APCContext &APCCtx, DVMHParallelizationContext &ParallelCtx, std::set &DistributedArrays, KeyToArrayMap &CreatedArrays, FileToMessageMap &APCMsgs); @@ -258,7 +266,7 @@ void APCParallelizationPass::getAnalysisUsage(AnalysisUsage &AU) const { void APCParallelizationPass::collectFunctionsAndLoops(Module &M, APCContext &APCCtx, NameToFunctionMap &Functions, FileToFuncMap &FileToFunc, - FileInfoMap &Files, FileToLoopMap &FileToLoop, + FileInfoMap &Files, FileToLoopMap &FileToLoop, FuncToLoopMap &FuncToLoop, APCToIRLoopsMap &APCToIRLoops) { for (auto &F : M) { auto *FI{APCCtx.findFunction(F)}; @@ -275,16 +283,19 @@ void APCParallelizationPass::collectFunctionsAndLoops(Module &M, auto &Provider{getAnalysis(F)}; auto &FileInfo{Files.try_emplace(FI->fileName).first->second}; auto &LoopList{FileToLoop.try_emplace(FI->fileName).first->second}; + auto &FuncLoopList{*FuncToLoop.try_emplace(FI).first}; for_each_loop( Provider.get().getLoopInfo(), - [&APCCtx, &FileInfo, &LoopList, &APCToIRLoops](Loop *L) { + [&APCCtx, &FileInfo, &LoopList, &APCToIRLoops, &FuncLoopList](Loop *L) { if (auto ID{L->getLoopID()}) if (auto APCLoop{APCCtx.findLoop(ID)}) { FileInfo.get().emplace(APCLoop->lineNum, APCLoop); LoopList.push_back(APCLoop); APCToIRLoops.try_emplace(APCLoop->loop, L->getHeader()); - if (!APCLoop->parent) + if (!APCLoop->parent) { FileInfo.get().push_back(APCLoop); + FuncLoopList.get().push_back(APCLoop); + } } }); } @@ -563,8 +574,9 @@ void APCParallelizationPass::bindFormalAndActualPrameters( } void APCParallelizationPass::buildDataDistributionGraph( - const RegionList &Regions, const FormalToActualMap &FormalToActual, - const ArrayAccessSummary &ArrayRWs, FileToLoopMap &FileToLoop, + const RegionList &Regions, const FormalToActualMap &FormalToActual, + const ArrayAccessSummary &ArrayRWs, const FileToFuncMap &FileToFunc, + const FuncToLoopMap &FuncToLoop, FileToLoopMap &FileToLoop, FileInfoMap &Files, KeyToArrayMap &CreatedArrays, FileToMessageMap &APCMsgs) { // Build a data distribution graph. @@ -596,6 +608,13 @@ void APCParallelizationPass::buildDataDistributionGraph( auto &Accesses{FileInfo.second.get()}; processLoopInformationForFunction(Accesses); addToDistributionGraph(Accesses, FormalToActual); + if (auto FuncItr{FileToFunc.find(FileInfo.getKey().str())}; + FuncItr != FileToFunc.end()) + for (auto *FI : FuncItr->second) + if (auto LoopItr{FuncToLoop.find(FI)}; LoopItr != FuncToLoop.end()) + selectFreeLoopsForParallelization( + LoopItr->get(), FI->funcName, true, Regions, + getObjectForFileFromMap(FileInfo.getKeyData(), APCMsgs)); } } @@ -614,7 +633,8 @@ static void dotGraphLog(apc::ParallelRegion &APCRegion) { void APCParallelizationPass::buildDataDistribution(const RegionList &Regions, const FormalToActualMap &FormalToActual, const ArrayToKeyMap &ArrayIds, - APCContext &APCCtx, DVMHParallelizationContext &ParallelCtx, + const FileInfoMap &Files, APCContext &APCCtx, + DVMHParallelizationContext &ParallelCtx, std::set &DistributedArrays, KeyToArrayMap &CreatedArrays, FileToMessageMap &APCMsgs) { std::set ArraysInAllRegions; @@ -654,6 +674,16 @@ void APCParallelizationPass::buildDataDistribution(const RegionList &Regions, auto APCSymbol{new apc::Symbol(&APCCtx, Tpl)}; APCCtx.addSymbol(APCSymbol); A->SetDeclSymbol(APCSymbol); + } else if (A->IsLoopArray()) { + auto FileItr{Files.find(A->GetDeclInfo().begin()->first)}; + assert(FileItr != Files.end() && "File must be known!"); + auto I{FileItr->second.get().find( + A->GetDeclInfo().begin()->second)}; + assert(I != FileItr->second.get().end() && + "A loop the array is attached to must be known!"); + auto APCSymbol{new apc::Symbol(&APCCtx, I->second->loop)}; + APCCtx.addSymbol(APCSymbol); + A->SetDeclSymbol(APCSymbol); } } LLVM_DEBUG(dotGraphLog(*APCRegion)); @@ -876,7 +906,7 @@ void APCParallelizationPass::addRemoteAccessDirectives( DenseMap> ToDistribute; for (auto *A : DistributedArrays) { - if (A->IsTemplate()) + if (A->IsTemplate() || A->IsLoopArray()) continue; auto *S{A->GetDeclSymbol()}; auto Var{S->getVariable(&ClientF)}; @@ -1014,12 +1044,13 @@ bool APCParallelizationPass::runOnModule(Module &M) { NameToFunctionMap Functions; FileToFuncMap FileToFunc; FileToLoopMap FileToLoop; + FuncToLoopMap FuncToLoop; APCToIRLoopsMap APCToIRLoops; FileInfoMap Files; ArrayAccessPool AccessPool; ArrayAccessSummary ArrayRWs; collectFunctionsAndLoops(M, APCCtx, Functions, FileToFunc, Files, FileToLoop, - APCToIRLoops); + FuncToLoop, APCToIRLoops); // Initial initialization of loop properties that depends on arrays accesses // to be collected further. for (auto &&[F, LoopList] : FileToLoop) @@ -1052,15 +1083,24 @@ bool APCParallelizationPass::runOnModule(Module &M) { } // A stub variable which is not used at this moment. std::map CreatedArrays; - buildDataDistributionGraph(Regions, FormalToActual, ArrayRWs, FileToLoop, - Files, CreatedArrays, APCCtx.mImpl->Diags); - buildDataDistribution(Regions, FormalToActual, ArrayIds, APCCtx, + buildDataDistributionGraph(Regions, FormalToActual, ArrayRWs, FileToFunc, + FuncToLoop, FileToLoop, Files, CreatedArrays, + APCCtx.mImpl->Diags); + buildDataDistribution(Regions, FormalToActual, ArrayIds, Files, APCCtx, ParallelCtx, DistributedArrays, CreatedArrays, APCCtx.mImpl->Diags); for (auto &FileInfo : Files) { createParallelDirectives( FileInfo.second.get(), Regions, FormalToActual, getObjectForFileFromMap(FileInfo.getKeyData(), APCCtx.mImpl->Diags)); + if (auto FuncItr{FileToFunc.find(FileInfo.getKey().str())}; + FuncItr != FileToFunc.end()) + for (auto *FI : FuncItr->second) + if (auto LoopItr{FuncToLoop.find(FI)}; LoopItr != FuncToLoop.end()) + selectFreeLoopsForParallelization( + LoopItr->get(), FI->funcName, false, Regions, + getObjectForFileFromMap(FileInfo.getKeyData(), + APCCtx.mImpl->Diags)); UniteNestedDirectives(FileInfo.second.get()); for (auto *APCRegion : Regions) { std::vector ParallelDirs; @@ -1275,6 +1315,8 @@ apc::Directive * ParallelDirective::genDirective(File *F, assert(A && "Array must be known!"); assert(!A->IsTemplate() && "Template cannot be referenced in across clause!"); + assert(!A->IsLoopArray() && + "Loop cannot be referenced in across clause!"); auto AcrossItr{ Clauses.get() .try_emplace(A->GetDeclSymbol()->getVariable(Func).getValue()) @@ -1313,6 +1355,8 @@ apc::Directive * ParallelDirective::genDirective(File *F, assert(A && "Array must be known!"); assert(!A->IsTemplate() && "Template cannot be referenced in shadow_renew clause!"); + assert(!A->IsLoopArray() && + "Loop cannot be referenced in shadow_renew clause!"); LLVM_DEBUG(dbgs() << "[APC]: calculate shadow_renew for " << A->GetShortName() << " (" << A << ")\n"); auto ShadowItr{ From 61514d57c9fdf28fe0fcd0a4a42cecca0920f9b2 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Wed, 30 Mar 2022 13:22:44 +0300 Subject: [PATCH 15/93] [TSAR, APC, Functions] Fix, ignore unknown locations of instructions. --- lib/APC/FunctionInfo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/APC/FunctionInfo.cpp b/lib/APC/FunctionInfo.cpp index ad42933c..23015ada 100644 --- a/lib/APC/FunctionInfo.cpp +++ b/lib/APC/FunctionInfo.cpp @@ -92,7 +92,7 @@ std::pair getFunctionRange(const Function &F) { }; for (auto &I : instructions(F)) { auto Loc = I.getDebugLoc(); - if (!Loc) + if (!Loc || Loc.getLine() == 0 && Loc.getCol() == 0) continue; if (!StartLoc || isLess(Loc, StartLoc)) StartLoc = Loc; From ba217efc741432fc50856a4a7c0c105326389e62 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Fri, 1 Apr 2022 14:46:39 +0300 Subject: [PATCH 16/93] [TSAR, Clang, Match, Expr] Fix, process the `address of` unary operator. --- lib/Analysis/Clang/ExpressionMatcher.cpp | 42 ++++++++++++++++-------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/lib/Analysis/Clang/ExpressionMatcher.cpp b/lib/Analysis/Clang/ExpressionMatcher.cpp index b283bea4..8ae162ef 100644 --- a/lib/Analysis/Clang/ExpressionMatcher.cpp +++ b/lib/Analysis/Clang/ExpressionMatcher.cpp @@ -54,6 +54,13 @@ namespace { class MatchExprVisitor : public MatchASTBase, public RecursiveASTVisitor { + + enum AccessKind : uint8_t { + AK_Store, + AK_AddrOf, + AK_Other + }; + public: MatchExprVisitor(SourceManager &SrcMgr, Matcher &MM, UnmatchedASTSet &Unmatched, LocToIRMap &LocMap, LocToASTMap &MacroMap) : @@ -91,7 +98,7 @@ class MatchExprVisitor : LLVM_DEBUG(dbgs() << "[EXPR MATCHER]: visit " << D->getDeclKindName() << D->getName() << "\n"); VisitItem(DynTypedNode::create(*D), D->getLocation()); - return true; + return true; } bool TraverseStmt(Stmt *S) { @@ -142,11 +149,12 @@ class MatchExprVisitor : if (!RecursiveASTVisitor::TraverseStmt(S)) return false; } - auto [InArraySubscriptBase, InStore] = isInArraySubscriptBaseAndStore(S); + auto [InArraySubscriptBase, AK] = + isInArraySubscriptBaseAndAccessKind(S); // Match current statement if it is an innermost array subscript // expression. Note, that assignment-like expressions have been already // matched. - if (!mHasChildArraySubscript && !InStore) + if (!mHasChildArraySubscript && AK != AK_Store && AK != AK_AddrOf) VisitItem(DynTypedNode::create(*SE), SE->getExprLoc()); mHasChildArraySubscript = InArraySubscriptBase; return true; @@ -168,7 +176,7 @@ class MatchExprVisitor : // only). if (auto VD{dyn_cast(DRE->getDecl())}; VD && isa(VD->getType()) && - isInArraySubscriptBaseAndStore(S).first) + std::get(isInArraySubscriptBaseAndAccessKind(S))) return true; } if (isa(S) || isa(S) || @@ -190,27 +198,30 @@ class MatchExprVisitor : } private: - std::pair isInArraySubscriptBaseAndStore(const Stmt *S) { + std::tuple + isInArraySubscriptBaseAndAccessKind(const Stmt *S) { auto *Prev{S}; bool IsInArraySubscriptBase{false}; for (auto *P : reverse(mParents)) { if (auto *SE{dyn_cast(P)}) { if (SE->getBase() != Prev) - return std::pair{IsInArraySubscriptBase, false}; + return std::tuple{IsInArraySubscriptBase, AK_Other}; IsInArraySubscriptBase = true; } else { - if (auto BO{ dyn_cast(P) }; - BO && BO->isAssignmentOp() && BO->getLHS() == Prev) - return std::pair{ IsInArraySubscriptBase, true }; - if (auto UO{ dyn_cast(P) }; - UO && (UO->isPrefix() || UO->isPostfix())) - return std::pair{ IsInArraySubscriptBase, true }; + if (auto BO{dyn_cast(P)}; + BO && BO->isAssignmentOp() && BO->getLHS() == Prev) + return std::tuple{IsInArraySubscriptBase, AK_Store}; + if (auto UO{dyn_cast(P)}) + if (UO->isPrefix() || UO->isPostfix()) + return std::tuple{IsInArraySubscriptBase, AK_Store}; + else if (UO->getOpcode() == UnaryOperatorKind::UO_AddrOf) + return std::tuple{IsInArraySubscriptBase, AK_AddrOf}; if (!isa(P)) - return std::pair{ IsInArraySubscriptBase, false }; + return std::tuple{IsInArraySubscriptBase, AK_Other}; } Prev = P; } - return std::pair{IsInArraySubscriptBase, false}; + return std::tuple{IsInArraySubscriptBase, AK_Other}; } SmallVector mParents; @@ -277,6 +288,9 @@ bool ClangExprMatcherPass::runOnFunction(Function &F) { ++NumNonMatchIRExpr; auto Loc = I.getDebugLoc(); if (Loc) { + LLVM_DEBUG(dbgs() << "[EXPR MATCHER]: remember instruction "; + I.print(dbgs()); dbgs() << " at "; Loc.print(dbgs()); + dbgs() << "\n"); auto Itr{ LocToExpr.try_emplace(Loc).first }; Itr->second.push_back(&I); } From 8f498f0dbe33d63f6e8d41b1d1f46e949341cbe5 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Sun, 10 Apr 2022 20:31:46 +0300 Subject: [PATCH 17/93] [TSAR, Anls, Read] Ignore artificial variables while loading external analysis results. --- include/tsar/Support/MetadataUtils.h | 2 +- lib/Analysis/Reader/AnalysisReader.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/include/tsar/Support/MetadataUtils.h b/include/tsar/Support/MetadataUtils.h index eceac8a4..6bf7fa9d 100644 --- a/include/tsar/Support/MetadataUtils.h +++ b/include/tsar/Support/MetadataUtils.h @@ -170,7 +170,7 @@ inline bool isStubType(llvm::DIType *DITy) { /// /// TODO (kaniandr@gmail.com): may be we should use other way to distinguish /// such types. How LLVM uses 'artificial' flag on variables? -inline bool isStubVariable(llvm::DIVariable &DIVar) { +inline bool isStubVariable(const llvm::DIVariable &DIVar) { return llvm::isa(DIVar) && llvm::cast(DIVar).isArtificial() || llvm::isa(DIVar) && diff --git a/lib/Analysis/Reader/AnalysisReader.cpp b/lib/Analysis/Reader/AnalysisReader.cpp index 399d3214..6a110753 100644 --- a/lib/Analysis/Reader/AnalysisReader.cpp +++ b/lib/Analysis/Reader/AnalysisReader.cpp @@ -454,6 +454,8 @@ bool AnalysisReader::runOnFunction(Function &F) { if (!DIEM) continue; auto *DIVar = DIEM->getVariable(); + if (isStubVariable(*DIVar)) + continue; auto *DIExpr = DIEM->getExpression(); assert(DIVar && DIExpr && "Invalid memory location!"); VariableT Var; From a4524e84ead07a649f2c97a14fb76c3b7d0aa336 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Sun, 10 Apr 2022 20:37:13 +0300 Subject: [PATCH 18/93] [TSAR, Dep, Metadata] Existance of self-copying instructions does not prevent a variable to be a reduction. --- lib/Analysis/Memory/DIDependenceAnalysis.cpp | 32 +++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/lib/Analysis/Memory/DIDependenceAnalysis.cpp b/lib/Analysis/Memory/DIDependenceAnalysis.cpp index d9223ec8..db128139 100644 --- a/lib/Analysis/Memory/DIDependenceAnalysis.cpp +++ b/lib/Analysis/Memory/DIDependenceAnalysis.cpp @@ -1688,22 +1688,32 @@ void DIDependencyAnalysisPass::propagateReduction(PHINode *Phi, auto *UserBB = UI->getParent(); if (InnerL->contains(UserBB)) continue; - ReductionCopy Copy; + SmallPtrSet Visited; do { - Copy = useReduction(UI); - // Skip intermediate phi-nodes which only forward value. - if (!Copy.To && !Copy.From && UI->hasNUses(1) && - UI->getNumIncomingValues() == 1 && + ReductionCopy Copy; + do { + Visited.insert(UI); + Copy = useReduction(UI); + // Skip intermediate phi-nodes which only forward value. + if (!Copy.To && !Copy.From && UI->hasNUses(1) && + UI->getNumIncomingValues() == 1 && + !InnerL->contains(UserBB = UI->getParent())) + UI = dyn_cast(*UI->user_begin()); + else + break; + } while (UI && !Visited.count(UI)); + if (!UI || !Copy.To) + continue; + if (Copy.From) + return true; + LCSSAPhis.insert(UI); + // We also want to remember self-copying instructions. + if (UI->hasNUses(1) && UI->getNumIncomingValues() == 1 && !InnerL->contains(UserBB = UI->getParent())) UI = dyn_cast(*UI->user_begin()); else break; - } while (UI); - if (!UI || !Copy.To) - continue; - if (Copy.From) - return true; - LCSSAPhis.insert(UI); + } while (UI && !Visited.count(UI)); } } return false; From 6b205e500fbaeed098661f023ed4ebec1ffbfd8d Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Sun, 17 Apr 2022 16:42:50 +0300 Subject: [PATCH 19/93] [TSAR, DVMH, Parallel] Fix, enclose negative constants in brackets. --- lib/Transform/Clang/DVMHWriter.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/Transform/Clang/DVMHWriter.cpp b/lib/Transform/Clang/DVMHWriter.cpp index 0dda7bd7..f4d5a630 100644 --- a/lib/Transform/Clang/DVMHWriter.cpp +++ b/lib/Transform/Clang/DVMHWriter.cpp @@ -476,7 +476,11 @@ static void addVar(const dvmh::Align &A, FunctionT &&getIdxName, Str.append(Name.begin(), Name.end()); if (!V.Offset.isNullValue()) { Str.push_back('+'); + if (V.Offset.isNegative()) + Str.push_back('('); V.Offset.toString(Str); + if (V.Offset.isNegative()) + Str.push_back(')'); } } else { V.toString(Str); From 084f951f58e7564356dd042c2dfb6fe74afdb6e2 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Sun, 17 Apr 2022 16:45:25 +0300 Subject: [PATCH 20/93] [TSAR, APC, Loop] Fix, do not take into account unreachable exits from a loop. --- lib/APC/LoopInfo.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/APC/LoopInfo.cpp b/lib/APC/LoopInfo.cpp index 48554a11..4db000ba 100644 --- a/lib/APC/LoopInfo.cpp +++ b/lib/APC/LoopInfo.cpp @@ -493,15 +493,20 @@ APCLoopInfoBasePass::evaluateInduction(const Loop &L, void APCLoopInfoBasePass::evaluateExits(const Loop &L, const DFLoop &DFL, apc::LoopGraph &APCLoop) { - if (DFL.getExitNode()->numberOfPredecessors() < 2) { APCLoop.hasGoto = false; - } else { + if (DFL.getExitNode()->numberOfPredecessors() > 1) { auto M = L.getHeader()->getModule(); auto F = L.getHeader()->getParent(); - APCLoop.hasGoto = true; SmallVector Exiting; L.getExitingBlocks(Exiting); + unsigned NumberOfExits{0}; for (auto *BB : Exiting) { + if (all_of(successors(BB), [&L](llvm::BasicBlock *SuccBB) { + return L.contains(SuccBB) || + llvm::isa(SuccBB->front()); + })) + continue; + ++NumberOfExits; auto I = BB->getTerminator(); if (!I || !I->getDebugLoc()) continue; @@ -512,6 +517,10 @@ void APCLoopInfoBasePass::evaluateExits(const Loop &L, const DFLoop &DFL, else APCLoop.linesOfExternalGoTo.push_back(ShrinkLoc); } + if (NumberOfExits == 1) + APCLoop.linesOfExternalGoTo.clear(); + else + APCLoop.hasGoto = true; } } From 8447c4717ac846afa1f13d1a6a569366bfea978b Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Sun, 17 Apr 2022 16:47:18 +0300 Subject: [PATCH 21/93] [TSAR, APC] Fix, some loops in a nest may not have known induction variables. --- lib/APC/Parallelization.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/APC/Parallelization.cpp b/lib/APC/Parallelization.cpp index c0d7a626..3926d97e 100644 --- a/lib/APC/Parallelization.cpp +++ b/lib/APC/Parallelization.cpp @@ -1220,7 +1220,8 @@ apc::Directive * ParallelDirective::genDirective(File *F, if (parallel[I] != "*") { auto Itr{find_if(Nest, [&Name = parallel[I]](auto &L) { auto LpStmt{cast(L->loop)}; - return LpStmt->getInduction().template get()->getName() == Name; + return LpStmt->hasInduction() && + LpStmt->getInduction().template get()->getName() == Name; })}; assert(Itr != Nest.end() && "Unknown parallel loop!"); auto LpStmt{cast((**Itr).loop)}; From 5ec8ace5eb4dccb43cc10222342f1a81ce704d3b Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Sun, 17 Apr 2022 16:49:27 +0300 Subject: [PATCH 22/93] [TSAR, DI, Dep] Fix, ignore dependencies which have been already unset on previous steps. --- lib/Analysis/Memory/DIDependenceAnalysis.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/Analysis/Memory/DIDependenceAnalysis.cpp b/lib/Analysis/Memory/DIDependenceAnalysis.cpp index db128139..c192b439 100644 --- a/lib/Analysis/Memory/DIDependenceAnalysis.cpp +++ b/lib/Analysis/Memory/DIDependenceAnalysis.cpp @@ -315,6 +315,12 @@ bool clarifyDescriptor(TraitT &&FromDIMTrait, DIMemoryTrait &DIMTrait) { FromDIMTrait.template set(); if (DIMTrait.is()) FromDIMTrait.template set(); + if (!DIMTrait.is()) + FromDIMTrait.template unset(); + if (!DIMTrait.is()) + FromDIMTrait.template unset(); + if (!DIMTrait.is()) + FromDIMTrait.template unset(); if (DIMTrait.is_any()) return false; if (DIMTrait.is() && From 90853fe51460649e1cc8a5954aab9a0cccfe28b6 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Sun, 17 Apr 2022 17:01:06 +0300 Subject: [PATCH 23/93] [TSAR, Parallel] Allow parallelization of loops that have multiple unreachable exits. Sometimes LLVM IR for a loop contains `select` instuction which determines loop exits. This instruction always has default basic block, so, if loop has a single exit in a source code, this default block will be marked as unreachable. Hence we can ignore this block and parallelize a corresponding loop if it has only single reachable exit. --- include/tsar/Support/IRUtils.h | 24 ++++++++++++++++++++++++ lib/APC/Parallelization.cpp | 2 +- lib/Analysis/Parallel/ParallelLoop.cpp | 2 +- lib/Transform/Clang/DVMHSMAutoPar.cpp | 9 +++++---- lib/Transform/Clang/OpenMPAutoPar.cpp | 9 +++++---- 5 files changed, 36 insertions(+), 10 deletions(-) diff --git a/include/tsar/Support/IRUtils.h b/include/tsar/Support/IRUtils.h index 6cde9a45..a64d6d5c 100644 --- a/include/tsar/Support/IRUtils.h +++ b/include/tsar/Support/IRUtils.h @@ -157,6 +157,30 @@ void for_each_user_insts(llvm::Instruction &I, FunctionT F) { F(UI); } } + +/// Return a block inside the loop that have successors outside of the loop if +/// there is no other blocks inside the loop that have reachable successors +/// outside of the loop. +inline llvm::BasicBlock* getValidExitingBlock(const llvm::Loop& L) { + llvm::SmallVector ExitingBlocks; + L.getExitingBlocks(ExitingBlocks); + if (ExitingBlocks.size() == 1) + return ExitingBlocks.front(); + unsigned ValidCount = 0; + llvm::BasicBlock *ValidBB{nullptr}; + for (auto *BB : ExitingBlocks) { + if (any_of(successors(BB), [&L](llvm::BasicBlock *SuccBB) { + return !L.contains(SuccBB) && + !llvm::isa(SuccBB->front()); + })) { + ValidBB = BB; + ++ValidCount; + } + } + if (ValidCount == 1) + return ValidBB; + return nullptr; +} } #endif//TSAR_SUPPORT_IR_UTILS_H diff --git a/lib/APC/Parallelization.cpp b/lib/APC/Parallelization.cpp index 3926d97e..698b576c 100644 --- a/lib/APC/Parallelization.cpp +++ b/lib/APC/Parallelization.cpp @@ -782,7 +782,7 @@ void APCParallelizationPass::updateParallelization( EntryInfo.first->get().back().Anchor = LpStmt->getId(); auto *LLVMLoop{LpInfo.getLoopFor(HeaderBB)}; assert(LLVMLoop && "LLVM IR representation of a loop must be known!"); - auto ExitingBB{LLVMLoop->getExitingBlock()}; + auto ExitingBB{getValidExitingBlock(*LLVMLoop)}; assert(ExitingBB && "Parallel loop must have a single exit!"); ParallelLocation *ExitLoc{nullptr}; if (ExitingBB == LLVMLoop->getHeader()) { diff --git a/lib/Analysis/Parallel/ParallelLoop.cpp b/lib/Analysis/Parallel/ParallelLoop.cpp index 3fdfec4e..40aaafaa 100644 --- a/lib/Analysis/Parallel/ParallelLoop.cpp +++ b/lib/Analysis/Parallel/ParallelLoop.cpp @@ -102,7 +102,7 @@ bool ParallelLoopPass::runOnFunction(Function &F) { for_each_loop(LI, [this, &F, &GO, &LoopAttr, &getLoopID, &getValue, DIAT, DIDepInfo](Loop *L) { auto SLoc = L->getStartLoc(); - if (!L->getExitingBlock() || + if (!getValidExitingBlock(*L) || !LoopAttr.hasAttr(*L, AttrKind::AlwaysReturn) || !LoopAttr.hasAttr(*L, AttrKind::NoIO) || !LoopAttr.hasAttr(*L, Attribute::NoUnwind) || diff --git a/lib/Transform/Clang/DVMHSMAutoPar.cpp b/lib/Transform/Clang/DVMHSMAutoPar.cpp index fb54b6d8..60a7b665 100644 --- a/lib/Transform/Clang/DVMHSMAutoPar.cpp +++ b/lib/Transform/Clang/DVMHSMAutoPar.cpp @@ -47,6 +47,7 @@ #include "tsar/Core/Query.h" #include "tsar/Frontend/Clang/Pragma.h" #include "tsar/Frontend/Clang/TransformationContext.h" +#include "tsar/Support/IRUtils.h" #include "tsar/Support/Clang/Diagnostic.h" #include "tsar/Support/Clang/Utils.h" #include "tsar/Transform/Clang/DVMHDirecitves.h" @@ -270,7 +271,7 @@ ParallelItem *ClangDVMHSMParallelization::exploitParallelism( EntryInfo.first->get().emplace_back(); EntryInfo.first->get().back().Anchor = IR.getLoop()->getLoopID(); - auto ExitingBB = IR.getLoop()->getExitingBlock(); + auto ExitingBB{getValidExitingBlock(*IR.getLoop())}; assert(ExitingBB && "Parallel loop must have a single exit!"); ParallelLocation *ExitLoc = nullptr; if (ExitingBB == IR.getLoop()->getHeader()) { @@ -326,7 +327,7 @@ static void mergeRegions(const SmallVectorImpl &ToMerge, auto MergedRegion = ParallelizationInfo.find( ToMerge.front()->getHeader(), ToMerge.front()->getLoopID(), true); auto MergedMarker = ParallelizationInfo.find>( - ToMerge.back()->getExitingBlock(), ToMerge.back()->getLoopID(), false); + getValidExitingBlock(*ToMerge.back()), ToMerge.back()->getLoopID(), false); auto &MergedActual = *[&PB = MergedRegion.getPL()->Entry]() { auto I = find_if(PB, [](auto &PI) { return isa(*PI); }); if (I != PB.end()) @@ -386,7 +387,7 @@ static void mergeRegions(const SmallVectorImpl &ToMerge, }; auto removeEndOfRegion = [©Actual, &remove, &MergedGetActual, &ParallelizationInfo](Loop *L) { - auto ExitingBB = L->getExitingBlock(); + auto ExitingBB{getValidExitingBlock(*L)}; auto ID = L->getLoopID(); auto Marker = ParallelizationInfo.find>( ExitingBB, ID, false); @@ -509,7 +510,7 @@ static void sanitizeAcrossLoops(ItrT I, ItrT EI, tsar::diag::note_parallel_across_tie_unable); // Remote parallel loop, enclosing region and actualization directives. auto ID{(**I).getLoopID()}; - auto ExitingBB{(**I).getExitingBlock()}; + auto ExitingBB{getValidExitingBlock(**I)}; auto Marker{ParallelizationInfo.find>( ExitingBB, ID, false)}; auto &ExitPB{Marker.getPL()->Exit}; diff --git a/lib/Transform/Clang/OpenMPAutoPar.cpp b/lib/Transform/Clang/OpenMPAutoPar.cpp index 6893018f..d5ec9523 100644 --- a/lib/Transform/Clang/OpenMPAutoPar.cpp +++ b/lib/Transform/Clang/OpenMPAutoPar.cpp @@ -35,6 +35,7 @@ #include "tsar/Core/Query.h" #include "tsar/Frontend/Clang/TransformationContext.h" #include "tsar/Frontend/Clang/Pragma.h" +#include "tsar/Support/IRUtils.h" #include "tsar/Support/Clang/Diagnostic.h" #include "tsar/Support/Clang/Utils.h" #include "tsar/Support/MetadataUtils.h" @@ -312,7 +313,7 @@ void mergeRegions(const SmallVectorImpl &ToMerge, ToMerge.front()->getHeader(), ToMerge.front()->getLoopID(), true); auto MergedMarker = ParallelizationInfo.find>( - ToMerge.back()->getExitingBlock(), ToMerge.back()->getLoopID(), + getValidExitingBlock(*ToMerge.back()), ToMerge.back()->getLoopID(), false); cast>(MergedMarker) ->parent_insert(MergedRegion.getUnchecked()); @@ -329,7 +330,7 @@ void mergeRegions(const SmallVectorImpl &ToMerge, } }; auto removeEndOfRegion = [&remove, &ParallelizationInfo](Loop *L) { - auto ExitingBB = L->getExitingBlock(); + auto ExitingBB = getValidExitingBlock(*L); auto ID = L->getLoopID(); auto Marker = ParallelizationInfo.find>( @@ -339,7 +340,7 @@ void mergeRegions(const SmallVectorImpl &ToMerge, ExitPB); }; auto removeStartOfRegion = [&remove, &ParallelizationInfo](Loop *L) { - auto HeaderBB = L->getExitingBlock(); + auto HeaderBB = getValidExitingBlock(*L); auto ID = L->getLoopID(); auto Region = ParallelizationInfo.find(L->getHeader(), ID); @@ -532,7 +533,7 @@ ParallelItem * ClangOpenMPParallelization::exploitParallelism( assert(EntryInfo.second && "Unable to create a parallel block!"); EntryInfo.first->get().emplace_back(); EntryInfo.first->get().back().Anchor = LoopID; - auto ExitingBB = DFL.getLoop()->getExitingBlock(); + auto ExitingBB = getValidExitingBlock(*DFL.getLoop()); assert(ExitingBB && "Parallel loop must have a single exit!"); ParallelLocation *ExitLoc = nullptr; if (ExitingBB == DFL.getLoop()->getHeader()) { From 8020f2557f8cdfee9504f6f7244c5b6ce904099e Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Sun, 17 Apr 2022 22:32:40 +0300 Subject: [PATCH 24/93] [TSAR, IR, Ptr2Reg] Initial implementation of pointer promotion. Credit goes to v.makeev (see dvm-system/tsar#5 for details). --- .../Analysis/Memory/DIDependencyAnalysis.h | 2 +- include/tsar/Transform/IR/Passes.h | 7 + lib/Analysis/Memory/DIDependenceAnalysis.cpp | 114 ++++- lib/Core/Query.cpp | 1 + lib/Transform/IR/CMakeLists.txt | 2 +- lib/Transform/IR/Passes.cpp | 1 + lib/Transform/IR/PointerScalarizer.cpp | 465 ++++++++++++++++++ 7 files changed, 577 insertions(+), 15 deletions(-) create mode 100644 lib/Transform/IR/PointerScalarizer.cpp diff --git a/include/tsar/Analysis/Memory/DIDependencyAnalysis.h b/include/tsar/Analysis/Memory/DIDependencyAnalysis.h index 47dbdc10..2add7ef8 100644 --- a/include/tsar/Analysis/Memory/DIDependencyAnalysis.h +++ b/include/tsar/Analysis/Memory/DIDependencyAnalysis.h @@ -158,7 +158,7 @@ class DIDependencyAnalysisPass : ArrayRef LockedTraits, const tsar::GlobalOptions &GlobalOpts, tsar::DependenceSet &DepSet, tsar::DIDependenceSet &DIDepSet, - tsar::DIMemoryTraitRegionPool &Pool); + tsar::DIMemoryTraitRegionPool &Pool, Loop *L); bool mIsInitialization; tsar::DIDependencInfo mDeps; diff --git a/include/tsar/Transform/IR/Passes.h b/include/tsar/Transform/IR/Passes.h index d4b4e8e4..b20a2283 100644 --- a/include/tsar/Transform/IR/Passes.h +++ b/include/tsar/Transform/IR/Passes.h @@ -100,5 +100,12 @@ void initializeNoCaptureAnalysisPass(PassRegistry &Registry); /// Create a pass calculating preserved parameters. Pass * createNoCaptureAnalysisPass(); + +/// Initialize a pass which attempts to promote pointer +/// values to registers. +void initializePointerScalarizerPassPass(PassRegistry &Registry); + +/// Create a pass which attempts to promote pointer +/// values to registers. } #endif//TSAR_IR_TRANSFORM_PASSES_H diff --git a/lib/Analysis/Memory/DIDependenceAnalysis.cpp b/lib/Analysis/Memory/DIDependenceAnalysis.cpp index c192b439..45b3cff5 100644 --- a/lib/Analysis/Memory/DIDependenceAnalysis.cpp +++ b/lib/Analysis/Memory/DIDependenceAnalysis.cpp @@ -596,6 +596,67 @@ trait::Reduction::Kind getReductionKind( return trait::DIReduction::RK_NoReduction; } +bool handleLoopEmptyBindings( + const Loop *L, const DIMemoryTrait &DITraitItr, + DIMemoryTraitRegionPool &Pool, + const SpanningTreeRelation &DIAliasSTR) { + auto *Mem = DITraitItr.getMemory(); + if (Mem->emptyBinding()) + return true; + for (auto &Loc : *Mem) { + if (!Loc.pointsToAliveValue()) + continue; + for (auto *User : Loc->users()) + if (auto *I = dyn_cast(User)) + if (L->contains(I)) { + return false; + } + if (auto *CE = dyn_cast(Loc)) { + SmallVector WorkList{CE}; + do { + auto *Expr = WorkList.pop_back_val(); + for (auto &ExprU : Expr->uses()) { + auto ExprUse = ExprU.getUser(); + if (auto ExprUseInst = dyn_cast(ExprUse)) + if (L->contains(ExprUseInst)) + return false; + else if (auto ExprUseExpr = dyn_cast(ExprUse)) + WorkList.push_back(ExprUseExpr); + } + } while (!WorkList.empty()); + } + } + if (!DITraitItr.is()) + return false; + for (auto &Trait: Pool) { + if (!Trait.is()) + continue; + auto *PoolNode = Trait.getMemory()->getAliasNode(); + auto *MemNode = Mem->getAliasNode(); + + if (Trait.getMemory() != Mem && !DIAliasSTR.isUnreachable(PoolNode, MemNode)) + return false; + } + return true; +} + +MDNode *mdNodeFromAliasTreeMapping(const Function *F, DIMemoryLocation &Loc) { + auto MD = F->getMetadata("alias.tree.mapping"); + if (MD == nullptr) + return nullptr; + for (auto &op : MD->operands()) { + auto *DIN = dyn_cast(op); + if (!DIN) + continue; + assert(DIN->getNumOperands() == 2 && "Alias tree mapping node must contain three elements"); + auto *DINewVar = dyn_cast(DIN->getOperand(1)); + if (Loc.Var == DINewVar) { + return dyn_cast(DIN->getOperand(0)); + } + } + return nullptr; +} + /// Update traits of metadata-level locations related to a specified Phi-node /// in a specified loop. This function uses a specified `TraitInserter` functor /// to update traits for a single memory location. @@ -619,7 +680,10 @@ template void updateTraits(const Loop *L, const PHINode *Phi, if (DILocs.empty()) return; for (auto &DILoc : DILocs) { - auto *MD = getRawDIMemoryIfExists(Phi->getContext(), DILoc); + auto *MD = mdNodeFromAliasTreeMapping(Phi->getFunction(), DILoc); + if (!MD) { + MD = getRawDIMemoryIfExists(Phi->getContext(), DILoc); + } // If a memory location is partially promoted we will try to use // dbg.declare or dbg.addr intrinsics to find the corresponding node in // the alias tree. @@ -648,7 +712,7 @@ template void updateTraits(const Loop *L, const PHINode *Phi, auto DIMTraitItr = Pool.find_as(MD); if (DIMTraitItr == Pool.end() || DIMTraitItr->getMemory()->isOriginal() || - !DIMTraitItr->getMemory()->emptyBinding() || + !handleLoopEmptyBindings(L, *DIMTraitItr, Pool, DIAliasSTR) || !DIMTraitItr->is() || isLockedTrait(*DIMTraitItr, LockedTraits, DIAliasSTR)) continue; @@ -1450,13 +1514,14 @@ void DIDependencyAnalysisPass::analyzePrivatePromoted(Loop *L, for (auto &Candidate : Privates) { if (OutwardUses.count(Candidate)) continue; - auto *MD = - getRawDIMemoryIfExists(L->getHeader()->getContext(), Candidate); + auto MD{mdNodeFromAliasTreeMapping(L->getHeader()->getParent(), Candidate)}; + if (!MD) + MD = getRawDIMemoryIfExists(L->getHeader()->getContext(), Candidate); if (!MD) continue; auto DIMTraitItr = Pool.find_as(MD); if (DIMTraitItr == Pool.end() || DIMTraitItr->getMemory()->isOriginal() || - !DIMTraitItr->getMemory()->emptyBinding() || + !handleLoopEmptyBindings(L, *DIMTraitItr, Pool, DIAliasSTR) || !DIMTraitItr->is_any() || @@ -1494,7 +1559,7 @@ void DIDependencyAnalysisPass::analyzePromoted(Loop *L, L->getStartLoc().print(dbgs()); dbgs() << "\n"); for (auto &DIMTrait : Pool) { if (DIMTrait.getMemory()->isOriginal() || - !DIMTrait.getMemory()->emptyBinding() || + !handleLoopEmptyBindings(L, DIMTrait, Pool, DIAliasSTR) || isLockedTrait(DIMTrait, LockedTraits, DIAliasSTR)) continue; DIMTrait.unset(); @@ -1619,14 +1684,14 @@ void DIDependencyAnalysisPass::propagateReduction(PHINode *Phi, return; SmallVector Traits; auto allowToUpdate = - [&Pool, &LockedTraits, &DIAliasSTR, &Traits](DIMemoryLocation &DILoc) { + [&Pool, &LockedTraits, &DIAliasSTR, &Traits, &L](DIMemoryLocation &DILoc) { auto *MD = getRawDIMemoryIfExists(DILoc.Var->getContext(), DILoc); if (!MD) return false; auto DIMTraitItr = Pool.find_as(MD); if (DIMTraitItr == Pool.end() || DIMTraitItr->getMemory()->isOriginal() || - !DIMTraitItr->getMemory()->emptyBinding() || + !handleLoopEmptyBindings(L, *DIMTraitItr, Pool, DIAliasSTR) || !DIMTraitItr->is() || isLockedTrait(*DIMTraitItr, LockedTraits, DIAliasSTR)) return false; @@ -1766,7 +1831,7 @@ void DIDependencyAnalysisPass::analyzeNode(DIAliasMemoryNode &DIN, const SpanningTreeRelation &DIAliasSTR, ArrayRef LockedTraits, const GlobalOptions &GlobalOpts, DependenceSet &DepSet, DIDependenceSet &DIDepSet, - DIMemoryTraitRegionPool &Pool) { + DIMemoryTraitRegionPool &Pool, Loop *L) { assert(!DIN.empty() && "Alias node must contain memory locations!"); auto *AN = findBoundAliasNode(*mAT, AliasSTR, DIN); auto ATraitItr = AN ? DepSet.find_as(AN) : DepSet.end(); @@ -1779,6 +1844,29 @@ void DIDependencyAnalysisPass::analyzeNode(DIAliasMemoryNode &DIN, dbgs() << "\n"; }); auto DIMTraitItr = Pool.find_as(&M); + bool IsAccessed{false}; + auto *F{L->getHeader()->getParent()}; // L - придется добавить как параметр к `analyzeNode` + if (auto MD{F->getMetadata("alias.tree.mapping")}) { + for (auto &Op : MD->operands()) { + auto *OpMD{dyn_cast(Op)}; + if (!OpMD) + continue; + assert(OpMD->getNumOperands() == 2 && + "Alias tree mapping node must contain two elements"); + auto MappingMD{dyn_cast(OpMD->getOperand(0))}; + if (MappingMD == M.getAsMDNode()) { + IsAccessed = true; + } + } + } + bool UsedInLoop = false; + if (IsAccessed) + for (auto &Loc : M) + if (auto *I = dyn_cast(Loc)) + if (L->contains(I)) { + UsedInLoop = true; + break; + } if (M.isOriginal() || M.emptyBinding() || ATraitItr == DepSet.end()) { if (DIMTraitItr == Pool.end()) continue; @@ -1796,13 +1884,13 @@ void DIDependencyAnalysisPass::analyzeNode(DIAliasMemoryNode &DIN, MustNoAccessValues.insert(Bind); if (IsChanged) { clarify(DIMTrait, *DIMTraitItr); - } else { + } else if (!UsedInLoop) { LLVM_DEBUG(dbgs() << "[DA DI]: use existing traits\n"); LLVM_DEBUG(dbgs() << "[DA DI]: mark as redundant\n"); DIMTraitItr->set(); DIMTraitItr->unset(); } - } else { + } else if (!UsedInLoop) { LLVM_DEBUG(dbgs() << "[DA DI]: use existing traits\n"); if ((!M.emptyBinding() && ATraitItr == DepSet.end()) || (M.emptyBinding() && M.isOriginal())) { @@ -1849,7 +1937,7 @@ void DIDependencyAnalysisPass::analyzeNode(DIAliasMemoryNode &DIN, } else { clarify(DIMTrait, *DIMTraitItr); } - } else { + } else if (!UsedInLoop) { if (DIMTraitItr == Pool.end()) continue; LLVM_DEBUG(dbgs() << "[DA DI]: use existing traits\n"); @@ -2056,7 +2144,7 @@ bool DIDependencyAnalysisPass::runOnFunction(Function &F) { if (isa(DIN)) continue; analyzeNode(cast(*DIN), DWLang, AliasSTR, DIAliasSTR, - LockedTraits, GlobalOpts, DepSet, DIDepSet, *Pool); + LockedTraits, GlobalOpts, DepSet, DIDepSet, *Pool, L); for (auto &DIM : cast(*DIN)) if (auto *DIEM = dyn_cast(&DIM)) if (DIEM->getExpression()->getNumElements() == 0) diff --git a/lib/Core/Query.cpp b/lib/Core/Query.cpp index 819ecd14..6f42cf05 100644 --- a/lib/Core/Query.cpp +++ b/lib/Core/Query.cpp @@ -176,6 +176,7 @@ void addAfterSROAAnalysis(const GlobalOptions &GO, const DataLayout &DL, Passes.add(createCFGSimplificationPass()); Passes.add(createInstructionCombiningPass()); Passes.add(createLoopSimplifyPass()); + Passes.add(createPointerScalarizerPass()); Passes.add(createSCEVAAWrapperPass()); Passes.add(createGlobalsAAWrapperPass()); Passes.add(createRPOFunctionAttrsAnalysis()); diff --git a/lib/Transform/IR/CMakeLists.txt b/lib/Transform/IR/CMakeLists.txt index 10e97a45..64443454 100644 --- a/lib/Transform/IR/CMakeLists.txt +++ b/lib/Transform/IR/CMakeLists.txt @@ -1,6 +1,6 @@ set(TRANSFORM_SOURCES Passes.cpp DeadCodeElimination.cpp InterprocAttr.cpp MetadataUtils.cpp Utils.cpp CallExtractor.cpp DependenceInliner.cpp - NoCaptureAnalysis.cpp) + NoCaptureAnalysis.cpp PointerScalarizer.cpp) if(MSVC_IDE) file(GLOB_RECURSE TRANSFORM_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/lib/Transform/IR/Passes.cpp b/lib/Transform/IR/Passes.cpp index 60080348..76709342 100644 --- a/lib/Transform/IR/Passes.cpp +++ b/lib/Transform/IR/Passes.cpp @@ -37,4 +37,5 @@ void llvm::initializeIRTransform(PassRegistry &Registry) { initializeDependenceInlinerPassPass(Registry); initializeDependenceInlinerAttributerPass(Registry); initializeNoCaptureAnalysisPass(Registry); + initializePointerScalarizerPassPass(Registry); } diff --git a/lib/Transform/IR/PointerScalarizer.cpp b/lib/Transform/IR/PointerScalarizer.cpp new file mode 100644 index 00000000..77217f47 --- /dev/null +++ b/lib/Transform/IR/PointerScalarizer.cpp @@ -0,0 +1,465 @@ +//=== PointerScalarizer.cpp - Promoting certain values after SROA * C++ *-===// +// +// Traits Static Analyzer (SAPFOR) +// +// Copyright 2021 DVM System Group +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +// +// This file implements functional pass that tries to promote both pointers +// and values which address is taken to registers. +// +//===----------------------------------------------------------------------===// + +#include "tsar/Analysis/Attributes.h" +#include "tsar/ADT/SpanningTreeRelation.h" +#include "tsar/Analysis/DFRegionInfo.h" +#include "tsar/Analysis/Memory/DIMemoryTrait.h" +#include "tsar/Analysis/Memory/EstimateMemory.h" +#include "tsar/Analysis/Memory/MemoryAccessUtils.h" + +#include "tsar/Core/Query.h" +#include "tsar/Support/IRUtils.h" +#include "tsar/Transform/IR/InterprocAttr.h" + +#include +#include +#include +#include +#include +#include +#include + +#undef DEBUG_TYPE +#define DEBUG_TYPE "ptr-red" + +using namespace tsar; +using namespace llvm; + +namespace { +class PointerScalarizerPass : public FunctionPass, private bcl::Uncopyable { +public: + static char ID; + + PointerScalarizerPass() : FunctionPass(ID) { + initializePointerScalarizerPassPass(*PassRegistry::getPassRegistry()); + } + + bool runOnFunction(Function &F) override; + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequired(); + AU.addRequired(); + AU.addRequired(); + AU.addRequired(); + AU.addRequired(); + AU.addRequired(); + } +}; + +struct PhiNodeLink { + PHINode *phiNode; + PhiNodeLink *parent; + + PhiNodeLink() = default; + + explicit PhiNodeLink(PhiNodeLink *node) : phiNode(nullptr), parent(node) {} + + explicit PhiNodeLink(PHINode *phi) : phiNode(phi), parent(nullptr) {} + + bool hasValue() const { + return phiNode || (parent && parent->hasValue()); + } + + PHINode *getPhi() const { + if (phiNode) { + return phiNode; + } + return parent->getPhi(); + } +}; + +struct ScalarizerContext { + explicit ScalarizerContext(Value *V, Function &F, Loop *L, DIBuilder *DIB) + : V(V), DbgVar(), DbgLoc(), F(F), L(L), DIB(DIB), PhiLinks{} { + for (auto *BB : L->getBlocks()) + PhiLinks.insert({BB, PhiNodeLink()}); + } + + Value *V; + DIVariable *DbgVar; + DILocation *DbgLoc; + Function &F; + Loop *L; + LoadInst *InsertedLoad; + DenseMap PhiLinks; + DenseSet UniqueNodes; + DenseMap LastValues; + DIBuilder *DIB; +}; +} + +char PointerScalarizerPass::ID = 0; + +INITIALIZE_PASS_BEGIN(PointerScalarizerPass, "ptr-scalar", + "Pointer Scalarizer Pass", false, false) +INITIALIZE_PASS_DEPENDENCY(DIMemoryTraitPoolWrapper); +INITIALIZE_PASS_DEPENDENCY(DFRegionInfoPass); +INITIALIZE_PASS_DEPENDENCY(LoopAttributesDeductionPass); +INITIALIZE_PASS_DEPENDENCY(EstimateMemoryPass); +INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass); +INITIALIZE_PASS_DEPENDENCY(DIEstimateMemoryPass); +INITIALIZE_PASS_END(PointerScalarizerPass, "ptr-scalar", + "Pointer Scalarizer Pass", false, false) + +FunctionPass * llvm::createPointerScalarizerPass() { + return new PointerScalarizerPass(); +} + +static inline bool hasVolatileInstInLoop(ScalarizerContext &Ctx) { + for (auto *User : Ctx.V->users()) { + if (auto *LI = dyn_cast(User)) + if (LI->isVolatile() && Ctx.L->contains(LI)) + return true; + if (auto *SI = dyn_cast(User)) + if (SI->isVolatile() && Ctx.L->contains(SI)) + return true; + } + return false; +} + +static inline void insertDbgValueCall(ScalarizerContext &Ctx, + Instruction *I, Instruction *InsertBefore, bool Add) { + if (Add) + I->setDebugLoc(Ctx.DbgLoc); + Ctx.DIB->insertDbgValueIntrinsic( + I, dyn_cast(Ctx.DbgVar), + DIExpression::get(Ctx.F.getContext(), {}), + Ctx.DbgLoc, InsertBefore + ); +} + +static void insertLoadInstructions(ScalarizerContext &Ctx) { + auto *BeforeInstr = new LoadInst( + Ctx.V->getType()->getPointerElementType(), + Ctx.V, "load." + Ctx.V->getName(), + &Ctx.L->getLoopPreheader()->back()); + Ctx.InsertedLoad = BeforeInstr; + auto *insertBefore = &Ctx.L->getLoopPreheader()->back(); + insertDbgValueCall(Ctx, BeforeInstr, insertBefore, true); +} + +static inline void insertStoreInstructions(ScalarizerContext &Ctx) { + SmallVector ExitBlocks; + Ctx.L->getExitBlocks(ExitBlocks); + for (auto *BB : ExitBlocks) { + new StoreInst(Ctx.LastValues[BB], Ctx.V, BB->getFirstNonPHI()); + } +} + +static void handleMemoryAccess(BasicBlock *BB, ScalarizerContext &Ctx) { + SmallVector Loads; + DenseMap Stores; + SmallVector ToDelete; + bool LastValuesChanged = false; + for (auto &Instr : BB->getInstList()) { + auto *SI = dyn_cast(&Instr); + auto *LI = dyn_cast(&Instr); + if (SI && SI->getPointerOperand() == Ctx.V){ + ToDelete.push_back(SI); + Ctx.LastValues[SI->getParent()] = SI->getValueOperand(); + LastValuesChanged = true; + } else if (LI && LI->getPointerOperand() == Ctx.V && !LI->user_empty()) { + Value *Last = Ctx.LastValues[BB]; + LI->replaceAllUsesWith(Last); + ToDelete.push_back(LI); + } + } + for (auto *I : ToDelete) { + if (auto *LI = dyn_cast(I)) { + if (auto OpInst = dyn_cast(LI->getOperand(0))) + insertDbgValueCall(Ctx, OpInst, I, false); + } + I->dropAllReferences(); + I->eraseFromParent(); + } + if (pred_size(BB) == 1 && !LastValuesChanged) + Ctx.LastValues[BB] = Ctx.LastValues[BB->getSinglePredecessor()]; +} + +static void handleLoads(ScalarizerContext &Ctx, + BasicBlock *BB, + DenseSet &Completed, + bool Init = false) { + if (Completed.find(BB) != Completed.end()) + return; + if (!Init) + handleMemoryAccess(BB, Ctx); + Completed.insert(BB); + for (auto *Succ : successors(BB)) + if (Ctx.L->contains(Succ)) + handleLoads(Ctx, Succ, Completed); + else + Ctx.LastValues[Succ] = Ctx.LastValues[BB]; +} + +static void insertPhiNodes(ScalarizerContext &Ctx, BasicBlock *BB, bool Init = false) { + if (Ctx.L->contains(BB) && Ctx.PhiLinks[BB].hasValue()) + return; + bool NeedsCreate = false; + if (pred_size(BB) == 1 && !Init) { + auto *Pred = BB->getSinglePredecessor(); + if (Ctx.L->contains(Pred) && Ctx.PhiLinks[Pred].hasValue()) + Ctx.PhiLinks[BB] = Ctx.PhiLinks[Pred]; + else + NeedsCreate = true; + } else if (!Init) { + NeedsCreate = true; + } + if (NeedsCreate) { + auto *Phi = PHINode::Create(Ctx.InsertedLoad->getType(), 0, + "Phi." + BB->getName(), &BB->front()); + insertDbgValueCall(Ctx, Phi, BB->getFirstNonPHI(), true); + Ctx.PhiLinks[BB] = PhiNodeLink(Phi); + Ctx.UniqueNodes.insert(Phi); + } + for (auto *Succ : successors(BB)) + if (Ctx.L->contains(Succ)) + insertPhiNodes(Ctx, Succ); + // all nodes and links are created at this point and BB = loop predecessor + if (Init) { + Ctx.LastValues[BB] = Ctx.InsertedLoad; + for (auto &P : Ctx.PhiLinks) + Ctx.LastValues[P.getFirst()] = P.getSecond().getPhi(); + } +} + +static void fillPhiNodes(ScalarizerContext &Ctx) { + for (auto *Phi : Ctx.UniqueNodes) { + auto *BB = Phi->getParent(); + for (auto Pred = pred_begin(BB); Pred != pred_end(BB); Pred++) { + if (Ctx.LastValues.find(*Pred) != Ctx.LastValues.end()) + Phi->addIncoming(Ctx.LastValues[*Pred], *Pred); + } + } +} + +static void deleteRedundantPhiNodes(ScalarizerContext &Ctx) { + bool Changed = false; + do { + SmallVector ToDelete; + for (auto *Phi: Ctx.UniqueNodes) { + bool SameOperands = true; + auto *Op = Phi->getOperand(0); + for (auto *OtherOp: Phi->operand_values()) + if (Op != OtherOp) { + SameOperands = false; + break; + } + if (SameOperands) + ToDelete.emplace_back(Phi); + } + Changed = !ToDelete.empty(); + for (auto *Phi : ToDelete) { + Phi->replaceAllUsesWith(Phi->getOperand(0)); + Ctx.UniqueNodes.erase(Phi); + Ctx.PhiLinks[Phi->getParent()].phiNode = nullptr; + auto *Instr = dyn_cast(Phi->getOperand(0)); + Ctx.PhiLinks[Phi->getParent()] = Ctx.PhiLinks[Instr->getParent()]; + Phi->eraseFromParent(); + } + } while (Changed); +} + +static bool analyzeAliasTree(Value *V, AliasTree &AT, Loop *L, TargetLibraryInfo &TLI) { + auto STR = SpanningTreeRelation(&AT); + auto *EM = AT.find(MemoryLocation(V, LocationSize(0))); + if (!EM) + return false; + EM = EM->getTopLevelParent(); + for (auto *BB : L->getBlocks()) { + for (auto &Inst : BB->getInstList()) { + bool HasWrite = false; + auto *EMNode = EM->getAliasNode(AT); + auto memLambda = [&V, &STR, &HasWrite, &AT, &EM, &EMNode]( + Instruction &I, MemoryLocation &&Loc, unsigned, AccessInfo, AccessInfo W) { + if (HasWrite || W == AccessInfo::No) + return; + auto InstEM = AT.find(Loc); + assert(InstEM && "alias tree node is empty"); + auto *InstNode = InstEM->getAliasNode(AT); + if (InstEM->getTopLevelParent() != EM && !STR.isUnreachable(EMNode, InstNode)) { + HasWrite = true; + } else if (InstEM->getTopLevelParent() == EM) { + if (auto *LI = dyn_cast(&I)) + if (LI->getPointerOperand() != V) + HasWrite = true; + if (auto *SI = dyn_cast(&I)) + if (SI->getPointerOperand() != V) + HasWrite = true; + } + }; + auto unknownMemLambda = [&HasWrite, &AT, &STR, &EMNode]( + Instruction &I, AccessInfo, AccessInfo W) { + if (HasWrite || W == AccessInfo::No) + return; + auto *InstEM = AT.findUnknown(&I); + if (!STR.isEqual(InstEM, EMNode) && !STR.isUnreachable(InstEM, EMNode)) + HasWrite = true; + }; + for_each_memory(Inst, TLI, memLambda, unknownMemLambda); + if (HasWrite) + return false; + } + } + return true; +} + +static bool createDbgInfo(ScalarizerContext &Ctx, + DIType *DIT, AliasTree &AT, + SmallVectorImpl &MDs) { + if (!Ctx.DbgVar) + return false; + auto &DL = Ctx.F.getParent()->getDataLayout(); + auto TypeSize = DL.getTypeStoreSize(Ctx.V->getType()->getPointerElementType()); + auto EM = AT.find(MemoryLocation(Ctx.V, LocationSize::precise(TypeSize))); + if (EM == nullptr) + return false; + auto *RawDIMem = getRawDIMemoryIfExists( + *EM, + Ctx.F.getContext(), DL, + AT.getDomTree()); + if (!RawDIMem) + return false; + auto *Scope = dyn_cast(Ctx.L->getStartLoc()->getScope()); + auto *NewVar = Ctx.DIB->createAutoVariable( + Scope, "deref." + Ctx.DbgVar->getName().str(), + Ctx.DbgVar->getFile(), Ctx.DbgVar->getLine(), + DIT, false, + DINode::FlagZero); + auto *Node = DINode::get(Ctx.F.getContext(), {RawDIMem, NewVar}); + MDs.push_back(Node); + Ctx.DbgVar = NewVar; + return true; +} + +bool PointerScalarizerPass::runOnFunction(Function &F) { + auto &TraitPool = getAnalysis().get(); + auto &LI = getAnalysis().getLoopInfo(); + auto &LoopAttr = getAnalysis(); + auto &AT = getAnalysis().getAliasTree(); + auto &TLI = getAnalysis().getTLI(F); + + auto MDsToAttach = SmallVector(); + for_each_loop(LI, [&TraitPool, &LoopAttr, &AT, &TLI, &F, &MDsToAttach](Loop *L) { + // 1. Find memory that was marked anti/flow/output; + // 2. Check that this memory is accessed inside the loop; + // 3. If possible, copy its value at the preheader and store it back after the exit block(s); + // 4. Replace all load/store instructions and their users in the loop's body + // with corresponding operations with the copied memory; + // 5. Map inserted instructions with the original value using DI nodes. + if (!L->getLoopID() || + !L->getLoopPreheader() || + !LoopAttr.hasAttr(*L, Attribute::NoUnwind) || + LoopAttr.hasAttr(*L, Attribute::ReturnsTwice) || + LoopAttr.hasAttr(*L, AttrKind::AlwaysReturn)) + return; + auto &Pool = TraitPool[L->getLoopID()]; + if (!Pool) + return; + SmallDenseSet Values; + SmallDenseMap ValueTraitMapping; + for (auto &T : *Pool) { + if (!T.is_any()) + continue; + for (auto &V : *T.getMemory()) { + if (!V.pointsToAliveValue() || + isa(V) || + isa(V) || + isa(V)) + continue; + if (auto *I = dyn_cast(V)) + if (L->contains(I)) + continue; + for (auto *User : V->users()) { + if (auto *SI = dyn_cast(User)) + if (L->contains(SI) && SI->getPointerOperand() == V) { + Values.insert(V); + ValueTraitMapping[V] = &T; + break; + } + } + } + } + for (auto *Val : Values) { + auto DIB = new DIBuilder(*F.getParent()); + auto Ctx = ScalarizerContext(Val, F, L, DIB); + if (hasVolatileInstInLoop(Ctx) || + !analyzeAliasTree(Val, AT, L, TLI)) { + ValueTraitMapping.erase(Val); + continue; + } + SmallVector DILocs; + Optional DIM; + if (isa(Ctx.V) || isa(Ctx.V)) { + DIM = findMetadata(Ctx.V, DILocs); + } else { + bool HasDbgInLoop = false; + for (auto *BB : L->getBlocks()) + for (auto &I : BB->getInstList()) + if (auto *DVI = dyn_cast(&I)) + if (DVI->getValue() == Ctx.V) { + HasDbgInLoop = true; + break; + } + if (!HasDbgInLoop) + DIM = findMetadata(Ctx.V, {&L->getHeader()->front()}, AT.getDomTree(), DILocs); + } + if (!DIM || !DIM->isValid()) + continue; + Ctx.DbgVar = DIM->Var; + Ctx.DbgLoc = DIM->Loc; + bool InsertedDI = false; + auto *DerivedType = dyn_cast(Ctx.DbgVar->getType()); + if (DerivedType && Val->getType()->isPointerTy()) + InsertedDI = createDbgInfo(Ctx, DerivedType->getBaseType(), AT, MDsToAttach); + else + InsertedDI = createDbgInfo(Ctx, Ctx.DbgVar->getType(), AT, MDsToAttach); + if (!InsertedDI) + continue; + if (!Ctx.DbgLoc) + Ctx.DbgLoc = DILocation::get( + F.getContext(), + DIM->Var->getLine(), + 0, + Ctx.DbgVar->getScope()); + insertLoadInstructions(Ctx); + insertPhiNodes(Ctx, L->getLoopPreheader(), true); + DenseSet Processed; + handleLoads(Ctx, L->getLoopPreheader(), Processed, true); + fillPhiNodes(Ctx); + deleteRedundantPhiNodes(Ctx); + insertStoreInstructions(Ctx); + } + for (auto &Pair: ValueTraitMapping) { + Pair.getSecond()->unset(); + } + }); + if (!MDsToAttach.empty()) { + auto *MappingNode = DINode::get(F.getContext(), MDsToAttach); + F.setMetadata("alias.tree.mapping", MappingNode); + } + return false; +} From 23307787e2e9c4124761e5a557dc2a4309d9ea82 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Sat, 23 Apr 2022 02:40:14 +0300 Subject: [PATCH 25/93] [TSAR, IR, Utils] Fix, trivial. --- include/tsar/Support/IRUtils.h | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/include/tsar/Support/IRUtils.h b/include/tsar/Support/IRUtils.h index a64d6d5c..e27c7d69 100644 --- a/include/tsar/Support/IRUtils.h +++ b/include/tsar/Support/IRUtils.h @@ -94,14 +94,14 @@ Function for_each_loop(const llvm::LoopInfo &LI, Function F) { } /// Check if predicate is `true` for at least one instruction which uses -/// result of a specified instruction. +/// result of a specified value. /// /// This function tries to find instruction which covers each user of a -/// specified instruction (a user may be a constant expression for example). +/// specified value (a user may be a constant expression for example). /// If corresponding instruction is not found then a user is passed to a /// functor. template -bool any_of_user_insts(llvm::Instruction &I, FunctionT F) { +bool any_of_user_insts(llvm::Value &I, FunctionT F) { for (auto *U : I.users()) { if (auto *UI = llvm::dyn_cast(U)) { if (F(UI)) @@ -112,7 +112,7 @@ bool any_of_user_insts(llvm::Instruction &I, FunctionT F) { auto *Expr = WorkList.pop_back_val(); for (auto *ExprU : Expr->users()) { if (auto ExprUseInst = llvm::dyn_cast(ExprU)) { - if (F(UI)) + if (F(ExprUseInst)) return true; } else if (auto Expr = llvm::dyn_cast(ExprU)) { WorkList.push_back(Expr); @@ -121,23 +121,22 @@ bool any_of_user_insts(llvm::Instruction &I, FunctionT F) { } } } while (!WorkList.empty()); - } else if (F(UI)) { + } else if (F(U)) { return true; } } return false; } -/// Apply a specified function to each instruction which uses result of a -/// specified instruction. +/// Apply a specified function to each instruction which uses a specified value. /// /// This function tries to find instruction which covers each user of a -/// specified instruction (a user may be a constant expression for example). +/// specified value (a user may be a constant expression for example). /// If corresponding instruction is not found then a user is passed to a /// functor. template -void for_each_user_insts(llvm::Instruction &I, FunctionT F) { - for (auto *U : I.users()) { +void for_each_user_insts(llvm::Value &V, FunctionT F) { + for (auto *U : V.users()) { if (auto *UI = llvm::dyn_cast(U)) { F(UI); } else if (auto *CE = llvm::dyn_cast(U)) { @@ -146,7 +145,7 @@ void for_each_user_insts(llvm::Instruction &I, FunctionT F) { auto *Expr = WorkList.pop_back_val(); for (auto *ExprU : Expr->users()) { if (auto ExprUseInst = llvm::dyn_cast(ExprU)) - F(UI); + F(ExprUseInst); else if (auto ExprUseExpr = llvm::dyn_cast(ExprU)) WorkList.push_back(ExprUseExpr); else @@ -154,7 +153,7 @@ void for_each_user_insts(llvm::Instruction &I, FunctionT F) { } } while (!WorkList.empty()); } else - F(UI); + F(U); } } From 493dce944ec8cf00045f66141a7118812920aad1 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Tue, 26 Apr 2022 21:48:29 +0300 Subject: [PATCH 26/93] [TSAR, Metadata, Utils] Move an auxiliary function to the global scope. --- include/tsar/Support/MetadataUtils.h | 8 ++++++++ lib/Support/Utils.cpp | 15 +++++++++++++++ lib/Transform/Mixed/DINodeRetriever.cpp | 14 +------------- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/include/tsar/Support/MetadataUtils.h b/include/tsar/Support/MetadataUtils.h index 6bf7fa9d..cb83b731 100644 --- a/include/tsar/Support/MetadataUtils.h +++ b/include/tsar/Support/MetadataUtils.h @@ -32,6 +32,10 @@ #include #include +namespace llvm { +class DIBuilder; +} + namespace tsar { /// Returns a language for a specified function. inline llvm::Optional getLanguage(const llvm::Function &F) { @@ -164,6 +168,10 @@ inline bool isStubType(llvm::DIType *DITy) { return !DITy || (DITy->isArtificial() && DITy->getName() == "sapfor.type"); } +/// Create auxiliary type of internal needs. +llvm::DIType *createStubType(llvm::Module &M, unsigned int AS, + llvm::DIBuilder &DIB); + /// Additional variables may be necessary for metadata-level analysis. /// This function returns 'true' if a specified variable is one of these /// variables and it has not been accurately generated. diff --git a/lib/Support/Utils.cpp b/lib/Support/Utils.cpp index 10b5561e..74d7ca10 100644 --- a/lib/Support/Utils.cpp +++ b/lib/Support/Utils.cpp @@ -26,6 +26,7 @@ #include "tsar/Support/IRUtils.h" #include "tsar/Support/MetadataUtils.h" #include +#include #include using namespace llvm; @@ -112,4 +113,18 @@ bool pointsToLocalMemory(const Value &V, const Loop &L) { } return false; } + +llvm::DIType *createStubType(llvm::Module &M, unsigned int AS, + llvm::DIBuilder &DIB) { + /// TODO (kaniandr@gmail.com): we create a stub instead of an appropriate + /// type because type must not be set to nullptr. We mark such type as + /// artificial type with name "sapfor.type", however may be this is not + /// a good way to distinguish such types? + auto DIBasicTy = DIB.createBasicType( + "char", llvm::Type::getInt1Ty(M.getContext())->getScalarSizeInBits(), + dwarf::DW_ATE_unsigned_char); + auto PtrSize = M.getDataLayout().getPointerSizeInBits(AS); + return DIB.createArtificialType( + DIB.createPointerType(DIBasicTy, PtrSize, 0, None, "sapfor.type")); +} } diff --git a/lib/Transform/Mixed/DINodeRetriever.cpp b/lib/Transform/Mixed/DINodeRetriever.cpp index 9664246d..e7f8679e 100644 --- a/lib/Transform/Mixed/DINodeRetriever.cpp +++ b/lib/Transform/Mixed/DINodeRetriever.cpp @@ -26,6 +26,7 @@ #include "tsar/Analysis/KnownFunctionTraits.h" #include "tsar/Analysis/Memory/Utils.h" #include "tsar/Frontend/Clang/TransformationContext.h" +#include "tsar/Support/MetadataUtils.h" #include "tsar/Support/Clang/Utils.h" #include "tsar/Transform/Mixed/Passes.h" #include @@ -61,19 +62,6 @@ class DINodeRetrieverPass : public ModulePass, private bcl::Uncopyable { } private: - DIType *createStubType(llvm::Module &M, unsigned int AS, DIBuilder &DIB) { - /// TODO (kaniandr@gmail.com): we create a stub instead of an appropriate - /// type because type must not be set to nullptr. We mark such type as - /// artificial type with name "sapfor.type", however may be this is not - /// a good way to distinguish such types? - auto DIBasicTy = DIB.createBasicType( - "char", llvm::Type::getInt1Ty(M.getContext())->getScalarSizeInBits(), - dwarf::DW_ATE_unsigned_char); - auto PtrSize = M.getDataLayout().getPointerSizeInBits(AS); - return DIB.createArtificialType( - DIB.createPointerType(DIBasicTy, PtrSize, 0, None, "sapfor.type")); - } - /// Insert artificial metadata for allocas. void insertDeclareIfNotExist(Function &F, DIFile *FileCU, DIBuilder &DIB) { for (auto &I : instructions(F)) From 0f1563bba3f7aff3a901e11dbbaa80c83f9b8470 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Tue, 26 Apr 2022 21:51:31 +0300 Subject: [PATCH 27/93] [TSAR, Attr, IPO] Use llvm `willreturn` attribute to set `sapfor.always-return`. The `willreturn` attribute has been added to LLVM IR and it is similar to `sapfor.always-return` which we added earlier. --- lib/Transform/IR/InterprocAttr.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/Transform/IR/InterprocAttr.cpp b/lib/Transform/IR/InterprocAttr.cpp index 8d622b82..7fff8b52 100644 --- a/lib/Transform/IR/InterprocAttr.cpp +++ b/lib/Transform/IR/InterprocAttr.cpp @@ -285,11 +285,12 @@ AttrKind POFunctionAttrsAnalysis::addDirectUserCalleeAttr() { AttrKind POFunctionAttrsAnalysis::addAlwaysReturnAttr() { for (auto *SCCF : mSCCFuncs) { - if (SCCF->hasFnAttribute(Attribute::NoReturn) || - (SCCF->isDeclaration() && - !SCCF->isIntrinsic() && !hasFnAttr(*SCCF, AttrKind::LibFunc))) + if (!SCCF->hasFnAttribute(Attribute::WillReturn) && + (SCCF->hasFnAttribute(Attribute::NoReturn) || + (SCCF->isDeclaration() && !SCCF->isIntrinsic() && + !hasFnAttr(*SCCF, AttrKind::LibFunc)))) return AttrKind::not_attribute; - } + } for (auto *CF : mCalleeFuncs) if (!hasFnAttr(*CF, AttrKind::AlwaysReturn)) return AttrKind::not_attribute; From 5f698ea4ffcf2c04fc6607331161c5825b34869e Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Tue, 26 Apr 2022 21:53:46 +0300 Subject: [PATCH 28/93] [TSAR, Memory, Dep] Add a new loop trait to specify variables which are live after a loop exit. --- include/tsar/Analysis/Memory/MemoryTrait.h | 24 ++++++----- lib/Analysis/Memory/BitMemoryTrait.cpp | 6 +++ lib/Analysis/Memory/BitMemoryTrait.h | 45 ++++++++++---------- lib/Analysis/Memory/DIDependenceAnalysis.cpp | 2 + lib/Analysis/Memory/PrivateAnalysis.cpp | 28 ++++++------ lib/Analysis/Reader/AnalysisReader.cpp | 2 + 6 files changed, 63 insertions(+), 44 deletions(-) diff --git a/include/tsar/Analysis/Memory/MemoryTrait.h b/include/tsar/Analysis/Memory/MemoryTrait.h index 49bb2df0..ea47613f 100644 --- a/include/tsar/Analysis/Memory/MemoryTrait.h +++ b/include/tsar/Analysis/Memory/MemoryTrait.h @@ -267,7 +267,7 @@ class Reduction { using MemoryDescriptor = bcl::TraitDescriptor< trait::AddressAccess, trait::ExplicitAccess, trait::HeaderAccess, trait::Lock, trait::Redundant, trait::NoRedundant, trait::NoPromotedScalar, - trait::DirectAccess, trait::IndirectAccess, + trait::DirectAccess, trait::IndirectAccess, trait::UseAfterLoop, bcl::TraitAlternative< trait::NoAccess, trait::Readonly, trait::Reduction, trait::Induction, bcl::TraitUnion, @@ -319,7 +319,8 @@ using MemoryStatistic = bcl::tagged_tuple< bcl::tagged, bcl::tagged, bcl::tagged, - bcl::tagged>; + bcl::tagged, + bcl::tagged>; /// A macro to make definition of statistics really simple. /// @@ -348,14 +349,17 @@ using MemoryStatistic = bcl::tagged_tuple< STATISTIC(VARNAME##NoPromotedScalar, "Number of not promoted scalars"); \ STATISTIC(VARNAME##DirectAccess, "Number of not directly accessed locations"); \ STATISTIC(VARNAME##IndirectAccess, "Number of not indirectly accessed locations"); \ - static ::tsar::MemoryStatistic VARNAME = {\ - VARNAME##AddressAccess, VARNAME##HeaderAccess, VARNAME##ExplicitAccess, \ - VARNAME##Readonly, VARNAME##Shared, VARNAME##Private, \ - VARNAME##FirstPrivate, VARNAME##SecondToLastPrivate, VARNAME##LastPrivate, \ - VARNAME##DynamicPrivate, VARNAME##Reduction, VARNAME##Induction, \ - VARNAME##Flow, VARNAME##Anti, VARNAME##Output, VARNAME##Lock, \ - VARNAME##Redundant, VARNAME##NoRedundant, VARNAME##NoPromotedScalar, \ - VARNAME##DirectAccess, VARNAME##IndirectAccess }; + STATISTIC(VARNAME##UseAfterLoop, "Number of locations used after exit from a loop"); \ + static ::tsar::MemoryStatistic VARNAME = { \ + VARNAME##AddressAccess, VARNAME##HeaderAccess, VARNAME##ExplicitAccess, \ + VARNAME##Readonly, VARNAME##Shared, VARNAME##Private, \ + VARNAME##FirstPrivate, VARNAME##SecondToLastPrivate, \ + VARNAME##LastPrivate, VARNAME##DynamicPrivate, VARNAME##Reduction, \ + VARNAME##Induction, VARNAME##Flow, VARNAME##Anti, VARNAME##Output, \ + VARNAME##Lock, VARNAME##Redundant, VARNAME##NoRedundant, \ + VARNAME##NoPromotedScalar, VARNAME##DirectAccess, \ + VARNAME##IndirectAccess, VARNAME##UseAfterLoop \ + }; } #endif//TSAR_MEMORY_TRAIT_H diff --git a/lib/Analysis/Memory/BitMemoryTrait.cpp b/lib/Analysis/Memory/BitMemoryTrait.cpp index 7924f71f..cd3f904d 100644 --- a/lib/Analysis/Memory/BitMemoryTrait.cpp +++ b/lib/Analysis/Memory/BitMemoryTrait.cpp @@ -29,6 +29,8 @@ using namespace tsar; BitMemoryTrait::BitMemoryTrait(const MemoryDescriptor &Dptr) : mId(NoAccess) { if (Dptr.is()) mId &= AddressAccess; + if (Dptr.is()) + mId &= UseAfterLoop; if (Dptr.is()) mId &= HeaderAccess; if (Dptr.is()) @@ -81,6 +83,10 @@ BitMemoryTrait::BitMemoryTrait(const MemoryDescriptor &Dptr) : mId(NoAccess) { MemoryDescriptor BitMemoryTrait::toDescriptor(unsigned TraitNumber, MemoryStatistic &Stat) const { MemoryDescriptor Dptr; + if (!(get() & ~UseAfterLoop)) { + Dptr.set(); + Stat.get() += TraitNumber; + } if (!(get() & ~AddressAccess)) { Dptr.set(); Stat.get() += TraitNumber; diff --git a/lib/Analysis/Memory/BitMemoryTrait.h b/lib/Analysis/Memory/BitMemoryTrait.h index 0e2cae02..2330ac60 100644 --- a/lib/Analysis/Memory/BitMemoryTrait.h +++ b/lib/Analysis/Memory/BitMemoryTrait.h @@ -57,28 +57,29 @@ class BitMemoryTrait { /// `SharedJoin` instead of `&`. To drop it use `dropSharedFlag()` method /// (note, this method change NoAccess, Readonly and Shared to invalid flags). enum Id : unsigned long long { - NoAccess = 111111111111111111_b, - Readonly = 100111101111111111_b, - SharedJoin = 000000001111111111_b, - Shared = 100000101111111111_b, - Private = 000011110111111111_b, - FirstPrivate = 000011100111111111_b, - SecondToLastPrivate = 000010110111111111_b, - LastPrivate = 000001110111111111_b, - DynamicPrivate = 000000110111111111_b, - Dependency = 000000000111111111_b, - Reduction = 001000000111111111_b, - Induction = 010000000111111111_b, - AddressAccess = 111111111011111111_b, - HeaderAccess = 111111111101111111_b, - ExplicitAccess = 111111111110111111_b, - Lock = 111111111111011111_b, - Redundant = 111111111111101111_b, - NoRedundant = 111111111111110111_b, - NoPromotedScalar = 111111111111111011_b, - DirectAccess = 111111111111111101_b, - IndirectAccess = 111111111111111110_b, - AllUnitFlags = 111111111000000000_b, + NoAccess = 1111111111111111111_b, + Readonly = 1001111011111111111_b, + SharedJoin = 0000000011111111111_b, + Shared = 1000001011111111111_b, + Private = 0000111101111111111_b, + FirstPrivate = 0000111001111111111_b, + SecondToLastPrivate = 0000101101111111111_b, + LastPrivate = 0000011101111111111_b, + DynamicPrivate = 0000001101111111111_b, + Dependency = 0000000001111111111_b, + Reduction = 0010000001111111111_b, + Induction = 0100000001111111111_b, + AddressAccess = 1111111110111111111_b, + HeaderAccess = 1111111111011111111_b, + ExplicitAccess = 1111111111101111111_b, + Lock = 1111111111110111111_b, + Redundant = 1111111111111011111_b, + NoRedundant = 1111111111111101111_b, + NoPromotedScalar = 1111111111111110111_b, + DirectAccess = 1111111111111111011_b, + IndirectAccess = 1111111111111111101_b, + UseAfterLoop = 1111111111111111110_b, + AllUnitFlags = 1111111110000000000_b, }; BitMemoryTrait() = default; diff --git a/lib/Analysis/Memory/DIDependenceAnalysis.cpp b/lib/Analysis/Memory/DIDependenceAnalysis.cpp index 45b3cff5..3cc65581 100644 --- a/lib/Analysis/Memory/DIDependenceAnalysis.cpp +++ b/lib/Analysis/Memory/DIDependenceAnalysis.cpp @@ -309,6 +309,8 @@ bool clarifyDescriptor(TraitT &&FromDIMTrait, DIMemoryTrait &DIMTrait) { FromDIMTrait.template unset(); if (DIMTrait.is()) FromDIMTrait.template set(); + if (DIMTrait.is()) + FromDIMTrait.template set(); if (DIMTrait.is()) FromDIMTrait.template set(); if (DIMTrait.is()) diff --git a/lib/Analysis/Memory/PrivateAnalysis.cpp b/lib/Analysis/Memory/PrivateAnalysis.cpp index 620a5717..9806320c 100644 --- a/lib/Analysis/Memory/PrivateAnalysis.cpp +++ b/lib/Analysis/Memory/PrivateAnalysis.cpp @@ -779,19 +779,18 @@ void PrivateRecognitionPass::resolveAccesses(Loop *L, const DFNode *LatchNode, SharedTrait = BitMemoryTrait::SharedJoin; DefTrait = BitMemoryTrait::Shared; } + // TODO (kaniandr@gmail.com): live memory analysis does not expand + // analysis results from aggregated array representation to explicit + // accesses, so we conservatively use analysis for the whole array + // instead of analysis results for an explicitly accessed memory location. + auto isLiveAggregate = [this, &LS](auto &Loc) { + auto BasePtr = GetUnderlyingObject(const_cast(Loc.Ptr), *mDL, 0); + if (BasePtr == Loc.Ptr) + return false; + return LS.getOut().overlap( + MemoryLocation(BasePtr, LocationSize::unknown(), Loc.AATags)); + }; if (!DefUse.hasUse(Loc)) { - // TODO (kaniandr@gmail.com): live memory analysis does not expand - // analysis results from aggregated array representation to explicit - // accesses, so we conservatively use analysis for the whole array - // instead of analysis results for an explicitly accessed memory location. - auto isLiveAggregate = [this, &LS](auto &Loc) { - auto BasePtr = - GetUnderlyingObject(const_cast(Loc.Ptr), *mDL, 0); - if (BasePtr == Loc.Ptr) - return false; - return LS.getOut().overlap( - MemoryLocation(BasePtr, LocationSize::unknown(), Loc.AATags)); - }; if (!LS.getOut().overlap(Loc) && !isLiveAggregate(Loc)) CurrTraits &= BitMemoryTrait::Private | SharedTrait; else { @@ -818,11 +817,16 @@ void PrivateRecognitionPass::resolveAccesses(Loop *L, const DFNode *LatchNode, // if it has not been assigned. CurrTraits &= BitMemoryTrait::DynamicPrivate & BitMemoryTrait::FirstPrivate | SharedTrait; + CurrTraits &= BitMemoryTrait::UseAfterLoop; } } else if ((DefUse.hasMayDef(Loc) || DefUse.hasDef(Loc))) { CurrTraits &= DefTrait; + if (LS.getOut().overlap(Loc) || isLiveAggregate(Loc)) + CurrTraits &= BitMemoryTrait::UseAfterLoop; } else { CurrTraits &= BitMemoryTrait::Readonly; + if (LS.getOut().overlap(Loc) || isLiveAggregate(Loc)) + CurrTraits &= BitMemoryTrait::UseAfterLoop; } LLVM_DEBUG(updateTraitsLog(Base, CurrTraits)); } diff --git a/lib/Analysis/Reader/AnalysisReader.cpp b/lib/Analysis/Reader/AnalysisReader.cpp index 6a110753..8e4a4249 100644 --- a/lib/Analysis/Reader/AnalysisReader.cpp +++ b/lib/Analysis/Reader/AnalysisReader.cpp @@ -547,6 +547,8 @@ bool AnalysisReader::runOnFunction(Function &F) { if (!TraitItr->second.get()) DITrait.unset(); } + if (!TraitItr->second.get()) + DITrait.unset(); LLVM_DEBUG(dbgs() << "[ANALYSIS READER]: set traits to "; DITrait.print(dbgs()); dbgs() << "\n"); } From 40376227934570b9aeb0a157af7fc9b266b46250 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Tue, 26 Apr 2022 22:23:23 +0300 Subject: [PATCH 29/93] [TSAR, IR, Ptr2Reg] Major update of ptr2reg pass and dependence analysis pass. - Fix errors. - Add new checks. - Select appropriate order of the pass execution. - Update analysis of partially promoted reduction, induction and privitizable variables. --- .../Analysis/Memory/DIDependencyAnalysis.h | 14 +- include/tsar/Transform/IR/Passes.h | 7 +- lib/Analysis/Memory/DIDependenceAnalysis.cpp | 303 +++++++++----- lib/Core/Query.cpp | 2 +- lib/Transform/IR/PointerScalarizer.cpp | 392 ++++++++---------- 5 files changed, 393 insertions(+), 325 deletions(-) diff --git a/include/tsar/Analysis/Memory/DIDependencyAnalysis.h b/include/tsar/Analysis/Memory/DIDependencyAnalysis.h index 2add7ef8..e7a6c5c5 100644 --- a/include/tsar/Analysis/Memory/DIDependencyAnalysis.h +++ b/include/tsar/Analysis/Memory/DIDependencyAnalysis.h @@ -29,6 +29,7 @@ #include "tsar/ADT/DenseMapTraits.h" #include "tsar/Analysis/Memory/DIMemoryTrait.h" +#include "tsar/Analysis/Memory/LiveMemory.h" #include "tsar/Analysis/Memory/Passes.h" #include #include @@ -50,6 +51,7 @@ class BitMemoryTrait; class DIAliasMemoryNode; class DIMemory; class DependenceSet; +class DFRegionInfo; template class SpanningTreeRelation; struct GlobalOptions; @@ -117,6 +119,9 @@ class DIDependencyAnalysisPass : mAT = nullptr; mDT = nullptr; mSE = nullptr; + mLiveInfo = nullptr; + mRegionInfo = nullptr; + mTLI = nullptr; } /// Prints out the internal state of the pass. This also used to produce @@ -128,6 +133,7 @@ class DIDependencyAnalysisPass : /// updates description of metadata-level traits in a specified pool if /// necessary. void analyzePromoted(Loop *L, Optional DWLang, + const tsar::SpanningTreeRelation &AliasSTR, const tsar::SpanningTreeRelation &DIAliasSTR, ArrayRef LockedTraits, tsar::DIMemoryTraitRegionPool &Pool); @@ -135,6 +141,7 @@ class DIDependencyAnalysisPass : /// Determine promoted memory locations which could be privitized in the /// original program. void analyzePrivatePromoted(Loop *L, Optional DWLang, + const tsar::SpanningTreeRelation &AliasSTR, const tsar::SpanningTreeRelation &DIAliasSTR, ArrayRef LockedTraits, tsar::DIMemoryTraitRegionPool &Pool); @@ -156,14 +163,17 @@ class DIDependencyAnalysisPass : const tsar::SpanningTreeRelation &AliasSTR, const tsar::SpanningTreeRelation &DIAliasSTR, ArrayRef LockedTraits, - const tsar::GlobalOptions &GlobalOpts, + const tsar::GlobalOptions &GlobalOpts, const llvm::Loop &L, tsar::DependenceSet &DepSet, tsar::DIDependenceSet &DIDepSet, - tsar::DIMemoryTraitRegionPool &Pool, Loop *L); + tsar::DIMemoryTraitRegionPool &Pool); bool mIsInitialization; tsar::DIDependencInfo mDeps; tsar::AliasTree *mAT; tsar::DIMemoryTraitPool *mTraitPool; + tsar::LiveMemoryInfo *mLiveInfo; + tsar::DFRegionInfo *mRegionInfo; + TargetLibraryInfo *mTLI; LoopInfo *mLI; DominatorTree *mDT; ScalarEvolution *mSE; diff --git a/include/tsar/Transform/IR/Passes.h b/include/tsar/Transform/IR/Passes.h index b20a2283..e59e39c6 100644 --- a/include/tsar/Transform/IR/Passes.h +++ b/include/tsar/Transform/IR/Passes.h @@ -101,11 +101,10 @@ void initializeNoCaptureAnalysisPass(PassRegistry &Registry); /// Create a pass calculating preserved parameters. Pass * createNoCaptureAnalysisPass(); -/// Initialize a pass which attempts to promote pointer -/// values to registers. +/// Initialize a pass which attempts to promote pointer values to registers. void initializePointerScalarizerPassPass(PassRegistry &Registry); -/// Create a pass which attempts to promote pointer -/// values to registers. +/// Create a pass which attempts to promote pointer values to registers. +FunctionPass *createPointerScalarizerPass(); } #endif//TSAR_IR_TRANSFORM_PASSES_H diff --git a/lib/Analysis/Memory/DIDependenceAnalysis.cpp b/lib/Analysis/Memory/DIDependenceAnalysis.cpp index 3cc65581..38bf7847 100644 --- a/lib/Analysis/Memory/DIDependenceAnalysis.cpp +++ b/lib/Analysis/Memory/DIDependenceAnalysis.cpp @@ -34,6 +34,7 @@ #include "tsar/Analysis/PrintUtils.h" #include "tsar/Analysis/Memory/DIEstimateMemory.h" #include "tsar/Analysis/Memory/EstimateMemory.h" +#include "tsar/Analysis/Memory/MemoryAccessUtils.h" #include "tsar/Analysis/Memory/MemoryTraitUtils.h" #include "tsar/Analysis/Memory/PrivateAnalysis.h" #include "tsar/Analysis/Memory/Utils.h" @@ -70,6 +71,7 @@ char DIDependencyAnalysisPass::ID = 0; INITIALIZE_PASS_IN_GROUP_BEGIN(DIDependencyAnalysisPass, "da-di", "Dependency Analysis (Metadata)", false, true, DefaultQueryManager::PrintPassGroup::getPassRegistry()) + INITIALIZE_PASS_DEPENDENCY(LiveMemoryPass) INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass); INITIALIZE_PASS_DEPENDENCY(DFRegionInfoPass); INITIALIZE_PASS_DEPENDENCY(ScalarEvolutionWrapperPass); @@ -79,6 +81,7 @@ INITIALIZE_PASS_IN_GROUP_BEGIN(DIDependencyAnalysisPass, "da-di", INITIALIZE_PASS_DEPENDENCY(EstimateMemoryPass) INITIALIZE_PASS_DEPENDENCY(PrivateRecognitionPass) INITIALIZE_PASS_DEPENDENCY(DIEstimateMemoryPass) + INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass) INITIALIZE_PASS_IN_GROUP_END(DIDependencyAnalysisPass, "da-di", "Dependency Analysis (Metadata)", false, true, DefaultQueryManager::PrintPassGroup::getPassRegistry()) @@ -600,49 +603,35 @@ trait::Reduction::Kind getReductionKind( bool handleLoopEmptyBindings( const Loop *L, const DIMemoryTrait &DITraitItr, - DIMemoryTraitRegionPool &Pool, + const DIMemoryTraitRegionPool &Pool, const SpanningTreeRelation &DIAliasSTR) { - auto *Mem = DITraitItr.getMemory(); - if (Mem->emptyBinding()) + auto *DIM = DITraitItr.getMemory(); + if (DIM->emptyBinding()) return true; - for (auto &Loc : *Mem) { - if (!Loc.pointsToAliveValue()) - continue; - for (auto *User : Loc->users()) - if (auto *I = dyn_cast(User)) - if (L->contains(I)) { - return false; - } - if (auto *CE = dyn_cast(Loc)) { - SmallVector WorkList{CE}; - do { - auto *Expr = WorkList.pop_back_val(); - for (auto &ExprU : Expr->uses()) { - auto ExprUse = ExprU.getUser(); - if (auto ExprUseInst = dyn_cast(ExprUse)) - if (L->contains(ExprUseInst)) - return false; - else if (auto ExprUseExpr = dyn_cast(ExprUse)) - WorkList.push_back(ExprUseExpr); - } - } while (!WorkList.empty()); - } - } if (!DITraitItr.is()) return false; - for (auto &Trait: Pool) { - if (!Trait.is()) + for (auto &Ptr : *DIM) { + if (!Ptr.pointsToAliveValue()) continue; - auto *PoolNode = Trait.getMemory()->getAliasNode(); - auto *MemNode = Mem->getAliasNode(); - - if (Trait.getMemory() != Mem && !DIAliasSTR.isUnreachable(PoolNode, MemNode)) + if (isa(Ptr) && L->contains(cast(Ptr))) + return false; + if (any_of_user_insts(*Ptr, [L](auto *U) { + return isa(U) && L->contains(cast(U)); + })) + return false; + } + for (auto &T : Pool) { + if (!T.is()) + continue; + auto *PoolNode = T.getMemory()->getAliasNode(); + auto *MemNode = DIM->getAliasNode(); + if (T.getMemory() != DIM && !DIAliasSTR.isUnreachable(PoolNode, MemNode)) return false; } return true; } -MDNode *mdNodeFromAliasTreeMapping(const Function *F, DIMemoryLocation &Loc) { +MDNode *findInAliasTreeMapping(const Function *F, const DIMemoryLocation &Loc) { auto MD = F->getMetadata("alias.tree.mapping"); if (MD == nullptr) return nullptr; @@ -650,11 +639,11 @@ MDNode *mdNodeFromAliasTreeMapping(const Function *F, DIMemoryLocation &Loc) { auto *DIN = dyn_cast(op); if (!DIN) continue; - assert(DIN->getNumOperands() == 2 && "Alias tree mapping node must contain three elements"); + assert(DIN->getNumOperands() == 2 && + "Alias tree mapping node must contain two operands!"); auto *DINewVar = dyn_cast(DIN->getOperand(1)); - if (Loc.Var == DINewVar) { + if (Loc.Var == DINewVar) return dyn_cast(DIN->getOperand(0)); - } } return nullptr; } @@ -682,10 +671,9 @@ template void updateTraits(const Loop *L, const PHINode *Phi, if (DILocs.empty()) return; for (auto &DILoc : DILocs) { - auto *MD = mdNodeFromAliasTreeMapping(Phi->getFunction(), DILoc); - if (!MD) { + auto *MD = findInAliasTreeMapping(Phi->getFunction(), DILoc); + if (!MD) MD = getRawDIMemoryIfExists(Phi->getContext(), DILoc); - } // If a memory location is partially promoted we will try to use // dbg.declare or dbg.addr intrinsics to find the corresponding node in // the alias tree. @@ -751,7 +739,8 @@ void combineTraits(bool IgnoreRedundant, DIAliasTrait &DIATrait) { DIATrait.unset(); if (IgnoreRedundant && DIMTraitItr->is()) { DIATrait.set(); - DIATrait.unset(); + DIATrait.unset(); } if (!(DIMTraitItr->is() && DIATrait.getNode() == DIMTraitItr->getMemory()->getAliasNode())) @@ -1427,35 +1416,33 @@ class IndirectAccessSanitizer : void DIDependencyAnalysisPass::analyzePrivatePromoted(Loop *L, Optional DWLang, + const tsar::SpanningTreeRelation &AliasSTR, const SpanningTreeRelation &DIAliasSTR, ArrayRef LockedTraits, DIMemoryTraitRegionPool &Pool) { auto *Head = L->getHeader(); - SmallVector ExitBlocks; - L->getExitBlocks(ExitBlocks); - // Collect first instructions outside the loop. - SmallVector ExitInsts; - llvm::transform(ExitBlocks, std::back_inserter(ExitInsts), - [](BasicBlock *BB) { return &BB->front(); }); // For each instruction we determine variables which it uses. Then we explore // instructions which computes values of these variables. We check whether // these instructions and currently processes instruction are always executed // on the same iteration. - SmallDenseSet OutwardUses, OutwardDefs, Privates; + SmallDenseSet OutwardUses, Privates; + SmallDenseMap, 8> OutwardDefs; for (auto *BB : L->blocks()) { for (auto &I : *BB) { if (auto II = dyn_cast(&I)) if (isDbgInfoIntrinsic(II->getIntrinsicID()) || isMemoryMarkerIntrinsic(II->getIntrinsicID())) continue; - // If instruction computes a value of a variable, remember it. - // May be it is better to call this function for each instruction in - // ExitInsts separately because processing of all instruction is more - // conservative. In some cases a variable may have a value after one - // exit and it may not have a value after another exit. This variable - // could be a dynamic private (not private), so we simplify the check. - SmallVector DILocs; - findMetadata(&I, ExitInsts, *mDT, DILocs); - Privates.insert(DILocs.begin(), DILocs.end()); + // Ignore Phi-nodes which just move the value throw to loop body. + // This instructions are not associated with a variable and are not used + // outside the loop. + if (auto *Phi{dyn_cast(&I)}) { + SmallVector DbgInsts; + findDbgUsers(DbgInsts, Phi); + if (DbgInsts.empty() && !any_of_user_insts(*Phi, [L](auto *U) { + return isa(U) && !L->contains(cast(U)); + })) + continue; + } // Collect users of a specified instruction which are located outside the // loop. for_each_user_insts(I, [this, &I, L, &OutwardDefs](Value *V) { @@ -1464,9 +1451,29 @@ void DIDependencyAnalysisPass::analyzePrivatePromoted(Loop *L, return; SmallVector DILocs; findMetadata(&I, makeArrayRef(UI), *mDT, DILocs); - OutwardDefs.insert(DILocs.begin(), DILocs.end()); + for (auto &DILoc : DILocs) + OutwardDefs.try_emplace(DILoc).first->second.push_back(UI); } }); + // If instruction computes a value of a variable, remember it. + // May be it is better to call this function for each instruction in + // ExitInsts separately because processing of all instruction is more + // conservative. In some cases a variable may have a value after one + // exit and it may not have a value after another exit. This variable + // could be a dynamic private (not private), so we simplify the check. + SmallVector DbgInsts; + findDbgValues(DbgInsts, &I); + bool HasDbgInLoop{false}; + for (auto *DVI : DbgInsts) + if (HasDbgInLoop |= L->contains(DVI)) + Privates.insert(DIMemoryLocation::get(DVI)); + // Ignore values which come outside of the loop or from a previous loop + // iteration but are not associated with any variable. This phi-nodes + // just transits a value which maybe even isn't used inside the loop. + // However, we should collect uses of this instruction outside the loop, + // so do not move this check above. + if (!HasDbgInLoop && isa(I) && I.getParent() == Head) + continue; SmallPtrSet VisitedInsts{ &I }; // If boolean value is set to true then value of a variable has been // computed on previous iteration and data dependence exists. @@ -1513,10 +1520,22 @@ void DIDependencyAnalysisPass::analyzePrivatePromoted(Loop *L, } while (!Worklist.empty()); } } + SmallVector ExitBlocks; + L->getExitBlocks(ExitBlocks); + SmallVector ExitLives; + transform(ExitBlocks, std::back_inserter(ExitLives), [this](auto *BB) { + auto DFB{mRegionInfo->getRegionFor(BB)}; + assert(DFB && "Data-flow region must not be null!"); + auto LiveItr{mLiveInfo->find(DFB)}; + assert(LiveItr != mLiveInfo->end() && + "Live memory description must not be null!"); + return LiveItr->template get().get(); + }); for (auto &Candidate : Privates) { if (OutwardUses.count(Candidate)) continue; - auto MD{mdNodeFromAliasTreeMapping(L->getHeader()->getParent(), Candidate)}; + auto MD{findInAliasTreeMapping(L->getHeader()->getParent(), Candidate)}; + bool IsFromMapping{MD != nullptr}; if (!MD) MD = getRawDIMemoryIfExists(L->getHeader()->getContext(), Candidate); if (!MD) @@ -1534,39 +1553,124 @@ void DIDependencyAnalysisPass::analyzePrivatePromoted(Loop *L, printDILocationSource(*DWLang, *DIMTraitItr->getMemory(), dbgs()); dbgs() << "\n"; }); + // Check if a partially promoted memory location is live after an exit from + // the loop, + auto isPartiallyPromotedLive = [this, IsFromMapping, &DIMTraitItr, + &Candidate, &OutwardDefs, &AliasSTR]() { + auto DIEM{dyn_cast(DIMTraitItr->getMemory())}; + if (!IsFromMapping || !DIEM || DIEM->emptyBinding() || + DIEM->getExpression()->getNumOperands() != 0) + return true; + auto Size{DIEM->getSize()}; + auto OutwardDefsItr{OutwardDefs.find(Candidate)}; + if (OutwardDefsItr == OutwardDefs.end()) + return false; + SmallVector BlocksToCheck; + SmallVector LivesToCheck; + for (auto *UI : OutwardDefsItr->second) { + if (auto *SI{dyn_cast(UI)}; + SI && any_of(*DIEM, [SI](auto &VH) { + return VH && SI->getPointerOperand() == VH; + })) { + BlocksToCheck.push_back(UI->getParent()); + auto DFB{mRegionInfo->getRegionFor(UI->getParent())}; + assert(DFB && "Data-flow region must not be null!"); + auto LiveItr{mLiveInfo->find(DFB)}; + assert(LiveItr != mLiveInfo->end() && + "Live memory description must not be null!"); + LivesToCheck.push_back(LiveItr->get().get()); + continue; + } + return true; + } + SmallPtrSet Nodes; + auto IsLive{false}; + for (auto &VH : *DIEM) { + if (!VH) + continue; + Nodes.insert(mAT->find(MemoryLocation(VH, Size))->getAliasNode(*mAT)); + IsLive |= any_of(LivesToCheck, [&VH, &Size](auto *LS) { + return LS->getOut().overlap(MemoryLocation{VH, Size}); + }); + } + IsLive |= + any_of(BlocksToCheck, [this, &Nodes, &AliasSTR](BasicBlock *BB) { + bool IsLive{false}; + for_each_memory( + *BB, *mTLI, + [this, &Nodes, &AliasSTR, + &IsLive](Instruction &I, MemoryLocation &&Loc, unsigned, + AccessInfo R, AccessInfo) { + if (IsLive || R == AccessInfo::No) + return; + auto *EM{mAT->find(Loc)}; + IsLive |= any_of(Nodes, [this, EM, &AliasSTR](auto *AN) { + return !AliasSTR.isUnreachable(EM->getAliasNode(*mAT), AN); + }); + }, + [this, &Nodes, &AliasSTR, &IsLive](Instruction &I, AccessInfo R, + AccessInfo) { + if (IsLive || R == AccessInfo::No) + return; + auto *UN{mAT->findUnknown(I)}; + IsLive |= any_of(Nodes, [this, UN, &AliasSTR](auto *AN) { + return !AliasSTR.isUnreachable(UN, AN); + }); + }); + return IsLive; + }); + return IsLive; + }; // Look up for users of an analyzed variable outside the loop. - if (OutwardDefs.count(Candidate)) { - // Do not unset 'first private' because it is not known whether a new - // value will be always set for a variable. - if (!DIMTraitItr->is() && - !DIMTraitItr->is()) { - DIMTraitItr->set(); + if (OutwardDefs.count(Candidate) && + DIMTraitItr->is() && isPartiallyPromotedLive()) { + if (DIMTraitItr->is_any()) { + // TODO (kaniandr@gmail.com): do not mark variables as a first private + // if there is at least one iteration in the loop. + DIMTraitItr->set(); + LLVM_DEBUG(dbgs() << "[DA DI]: first private variable found\n"); LLVM_DEBUG(dbgs() << "[DA DI]: dynamic private variable found\n"); + ++NumTraits.get(); ++NumTraits.get(); } } else { DIMTraitItr->set(); + DIMTraitItr->unset(); LLVM_DEBUG(dbgs() << "[DA DI]: private variable found\n"); - ++NumTraits.get(); + ++NumTraits.get(); + --NumTraits.get(); } } } void DIDependencyAnalysisPass::analyzePromoted(Loop *L, Optional DWLang, + const SpanningTreeRelation &AliasSTR, const SpanningTreeRelation &DIAliasSTR, ArrayRef LockedTraits, DIMemoryTraitRegionPool &Pool) { assert(L && "Loop must not be null!"); LLVM_DEBUG(dbgs() << "[DA DI]: process loop at "; L->getStartLoc().print(dbgs()); dbgs() << "\n"); for (auto &DIMTrait : Pool) { + // Do not use hear `handleLoopEmptyBindings` because variable can be + // promoted for a loop but access to its pointer without dereference is + // steal allowed. + // Otherwise we loose 'address access' traits for the following example: + // int X; + // long long bar() { return (long long)&X; } + // long long foo() { + // long long S = 0; + // for (int I = 0; I < 10; ++I) + // S += bar(); + // return S; + // } if (DIMTrait.getMemory()->isOriginal() || - !handleLoopEmptyBindings(L, DIMTrait, Pool, DIAliasSTR) || + !DIMTrait.getMemory()->emptyBinding() || isLockedTrait(DIMTrait, LockedTraits, DIAliasSTR)) continue; DIMTrait.unset(); } - analyzePrivatePromoted(L, DWLang, DIAliasSTR, LockedTraits, Pool); + analyzePrivatePromoted(L, DWLang, AliasSTR, DIAliasSTR, LockedTraits, Pool); // If there is no preheader induction and reduction analysis will fail. if (!L->getLoopPreheader()) return; @@ -1832,8 +1936,8 @@ void DIDependencyAnalysisPass::analyzeNode(DIAliasMemoryNode &DIN, const SpanningTreeRelation &AliasSTR, const SpanningTreeRelation &DIAliasSTR, ArrayRef LockedTraits, const GlobalOptions &GlobalOpts, - DependenceSet &DepSet, DIDependenceSet &DIDepSet, - DIMemoryTraitRegionPool &Pool, Loop *L) { + const Loop &L, DependenceSet &DepSet, DIDependenceSet &DIDepSet, + DIMemoryTraitRegionPool &Pool) { assert(!DIN.empty() && "Alias node must contain memory locations!"); auto *AN = findBoundAliasNode(*mAT, AliasSTR, DIN); auto ATraitItr = AN ? DepSet.find_as(AN) : DepSet.end(); @@ -1846,29 +1950,30 @@ void DIDependencyAnalysisPass::analyzeNode(DIAliasMemoryNode &DIN, dbgs() << "\n"; }); auto DIMTraitItr = Pool.find_as(&M); - bool IsAccessed{false}; - auto *F{L->getHeader()->getParent()}; // L - придется добавить как параметр к `analyzeNode` + auto *F{L.getHeader()->getParent()}; + bool NoRedundantMapping{false}; if (auto MD{F->getMetadata("alias.tree.mapping")}) { - for (auto &Op : MD->operands()) { - auto *OpMD{dyn_cast(Op)}; - if (!OpMD) - continue; - assert(OpMD->getNumOperands() == 2 && - "Alias tree mapping node must contain two elements"); - auto MappingMD{dyn_cast(OpMD->getOperand(0))}; - if (MappingMD == M.getAsMDNode()) { - IsAccessed = true; - } + auto MappingItr{ + find_if(MD->operands(), [ToFind = M.getAsMDNode()](auto &Op) { + auto *OpMD{dyn_cast(Op)}; + if (!OpMD) + return false; + assert(OpMD->getNumOperands() == 2 && + "Alias tree mapping node must contain two operands!"); + auto MappingMD{dyn_cast(OpMD->getOperand(0))}; + if (MappingMD == ToFind) + return true; + return false; + })}; + if (MappingItr != MD->operands().end()) { + if (auto *MDV{MetadataAsValue::getIfExists( + F->getContext(), cast(*MappingItr)->getOperand(1))}; + MDV && any_of_user_insts(*MDV, [&L](auto *U) { + return isa(U) && L.contains(cast(U)); + })) + NoRedundantMapping = true; } } - bool UsedInLoop = false; - if (IsAccessed) - for (auto &Loc : M) - if (auto *I = dyn_cast(Loc)) - if (L->contains(I)) { - UsedInLoop = true; - break; - } if (M.isOriginal() || M.emptyBinding() || ATraitItr == DepSet.end()) { if (DIMTraitItr == Pool.end()) continue; @@ -1886,13 +1991,13 @@ void DIDependencyAnalysisPass::analyzeNode(DIAliasMemoryNode &DIN, MustNoAccessValues.insert(Bind); if (IsChanged) { clarify(DIMTrait, *DIMTraitItr); - } else if (!UsedInLoop) { + } else if (!NoRedundantMapping) { LLVM_DEBUG(dbgs() << "[DA DI]: use existing traits\n"); LLVM_DEBUG(dbgs() << "[DA DI]: mark as redundant\n"); DIMTraitItr->set(); DIMTraitItr->unset(); } - } else if (!UsedInLoop) { + } else if (!NoRedundantMapping) { LLVM_DEBUG(dbgs() << "[DA DI]: use existing traits\n"); if ((!M.emptyBinding() && ATraitItr == DepSet.end()) || (M.emptyBinding() && M.isOriginal())) { @@ -1939,7 +2044,7 @@ void DIDependencyAnalysisPass::analyzeNode(DIAliasMemoryNode &DIN, } else { clarify(DIMTrait, *DIMTraitItr); } - } else if (!UsedInLoop) { + } else if (!NoRedundantMapping) { if (DIMTraitItr == Pool.end()) continue; LLVM_DEBUG(dbgs() << "[DA DI]: use existing traits\n"); @@ -2096,15 +2201,17 @@ bool DIDependencyAnalysisPass::runOnFunction(Function &F) { mSE = &getAnalysis().getSE(); mAT = &getAnalysis().getAliasTree(); mLI = &getAnalysis().getLoopInfo(); + mLiveInfo = &getAnalysis().getLiveInfo(); + mRegionInfo = &getAnalysis().getRegionInfo(); mTraitPool = &getAnalysis().get(); - auto &DFI = getAnalysis().getRegionInfo(); + mTLI = &getAnalysis().getTLI(F); auto &PI = getAnalysis().getPrivateInfo(); auto &DIAT = getAnalysis().getAliasTree(); auto &DL = F.getParent()->getDataLayout(); auto DWLang = getLanguage(F); SpanningTreeRelation AliasSTR(mAT); SpanningTreeRelation DIAliasSTR(&DIAT); - auto *DFF = cast(DFI.getTopLevelRegion()); + auto *DFF = cast(mRegionInfo->getTopLevelRegion()); std::deque LQ; for (auto *DFN : DFF->getRegions()) addLoopIntoQueue(DFN, LQ); @@ -2140,13 +2247,13 @@ bool DIDependencyAnalysisPass::runOnFunction(Function &F) { assert(PI.count(DFL) && "IR-level traits must be available for a loop!"); auto &DepSet = PI.find(DFL)->get(); auto &DIDepSet = mDeps.try_emplace(DILoop, DepSet.size()).first->second; - analyzePromoted(L, DWLang, DIAliasSTR, LockedTraits, *Pool); + analyzePromoted(L, DWLang, AliasSTR, DIAliasSTR, LockedTraits, *Pool); DenseMap VarToMemory; for (auto *DIN : post_order(&DIAT)) { if (isa(DIN)) continue; analyzeNode(cast(*DIN), DWLang, AliasSTR, DIAliasSTR, - LockedTraits, GlobalOpts, DepSet, DIDepSet, *Pool, L); + LockedTraits, GlobalOpts, *L, DepSet, DIDepSet, *Pool); for (auto &DIM : cast(*DIN)) if (auto *DIEM = dyn_cast(&DIM)) if (DIEM->getExpression()->getNumElements() == 0) @@ -2438,7 +2545,7 @@ void DIDependencyAnalysisPass::print(raw_ostream &OS, const Module *M) const { } } using IgnoreTraitList = bcl::TypeList; + trait::DirectAccess, trait::IndirectAccess, trait::UseAfterLoop>; TM.for_each( TraitPrinter{OS, DIAT, Offset, *DWLang}); SeparateTrateList::for_each_type( @@ -2447,6 +2554,7 @@ void DIDependencyAnalysisPass::print(raw_ostream &OS, const Module *M) const { } void DIDependencyAnalysisPass::getAnalysisUsage(AnalysisUsage &AU) const { + AU.addRequired(); AU.addRequired(); AU.addRequired(); AU.addRequired(); @@ -2456,6 +2564,7 @@ void DIDependencyAnalysisPass::getAnalysisUsage(AnalysisUsage &AU) const { AU.addRequired(); AU.addRequired(); AU.addRequired(); + AU.addRequired(); AU.setPreservesAll(); } diff --git a/lib/Core/Query.cpp b/lib/Core/Query.cpp index 6f42cf05..cb3399f7 100644 --- a/lib/Core/Query.cpp +++ b/lib/Core/Query.cpp @@ -176,11 +176,11 @@ void addAfterSROAAnalysis(const GlobalOptions &GO, const DataLayout &DL, Passes.add(createCFGSimplificationPass()); Passes.add(createInstructionCombiningPass()); Passes.add(createLoopSimplifyPass()); - Passes.add(createPointerScalarizerPass()); Passes.add(createSCEVAAWrapperPass()); Passes.add(createGlobalsAAWrapperPass()); Passes.add(createRPOFunctionAttrsAnalysis()); Passes.add(createPOFunctionAttrsAnalysis()); + Passes.add(createPointerScalarizerPass()); Passes.add(createMemoryMatcherPass()); Passes.add(createGlobalsAccessCollector()); Passes.add(createCallExtractorPass()); diff --git a/lib/Transform/IR/PointerScalarizer.cpp b/lib/Transform/IR/PointerScalarizer.cpp index 77217f47..5e33c5fc 100644 --- a/lib/Transform/IR/PointerScalarizer.cpp +++ b/lib/Transform/IR/PointerScalarizer.cpp @@ -23,27 +23,21 @@ // //===----------------------------------------------------------------------===// -#include "tsar/Analysis/Attributes.h" #include "tsar/ADT/SpanningTreeRelation.h" -#include "tsar/Analysis/DFRegionInfo.h" +#include "tsar/Analysis/Attributes.h" #include "tsar/Analysis/Memory/DIMemoryTrait.h" #include "tsar/Analysis/Memory/EstimateMemory.h" #include "tsar/Analysis/Memory/MemoryAccessUtils.h" - -#include "tsar/Core/Query.h" +#include #include "tsar/Support/IRUtils.h" #include "tsar/Transform/IR/InterprocAttr.h" - #include #include -#include #include -#include -#include -#include +#include #undef DEBUG_TYPE -#define DEBUG_TYPE "ptr-red" +#define DEBUG_TYPE "ptr2reg" using namespace tsar; using namespace llvm; @@ -70,102 +64,83 @@ class PointerScalarizerPass : public FunctionPass, private bcl::Uncopyable { }; struct PhiNodeLink { - PHINode *phiNode; - PhiNodeLink *parent; + PHINode *PhiNode; + PhiNodeLink *Parent; PhiNodeLink() = default; - explicit PhiNodeLink(PhiNodeLink *node) : phiNode(nullptr), parent(node) {} + explicit PhiNodeLink(PhiNodeLink *Node) : PhiNode(nullptr), Parent(Node) {} - explicit PhiNodeLink(PHINode *phi) : phiNode(phi), parent(nullptr) {} + explicit PhiNodeLink(PHINode *Phi) : PhiNode(Phi), Parent(nullptr) {} - bool hasValue() const { - return phiNode || (parent && parent->hasValue()); - } + bool hasValue() const { return PhiNode || (Parent && Parent->hasValue()); } PHINode *getPhi() const { - if (phiNode) { - return phiNode; - } - return parent->getPhi(); + if (PhiNode) + return PhiNode; + return Parent->getPhi(); } }; struct ScalarizerContext { - explicit ScalarizerContext(Value *V, Function &F, Loop *L, DIBuilder *DIB) - : V(V), DbgVar(), DbgLoc(), F(F), L(L), DIB(DIB), PhiLinks{} { + explicit ScalarizerContext(Value *V, Loop *L, DIBuilder *DIB) + : V(V), L(L), DIB(DIB), PhiLinks{} { for (auto *BB : L->getBlocks()) PhiLinks.insert({BB, PhiNodeLink()}); } - Value *V; - DIVariable *DbgVar; - DILocation *DbgLoc; - Function &F; - Loop *L; - LoadInst *InsertedLoad; + Value *V{nullptr}; + DILocalVariable *DbgVar{nullptr}; + DILocation *DbgLoc{nullptr}; + Loop *L{nullptr}; + LoadInst *InsertedLoad{nullptr}; DenseMap PhiLinks; DenseSet UniqueNodes; DenseMap LastValues; - DIBuilder *DIB; + DIBuilder *DIB{nullptr}; }; } char PointerScalarizerPass::ID = 0; -INITIALIZE_PASS_BEGIN(PointerScalarizerPass, "ptr-scalar", - "Pointer Scalarizer Pass", false, false) +INITIALIZE_PASS_BEGIN(PointerScalarizerPass, "ptr2reg", + "Promote Pointer To Register", false, false) INITIALIZE_PASS_DEPENDENCY(DIMemoryTraitPoolWrapper); -INITIALIZE_PASS_DEPENDENCY(DFRegionInfoPass); INITIALIZE_PASS_DEPENDENCY(LoopAttributesDeductionPass); INITIALIZE_PASS_DEPENDENCY(EstimateMemoryPass); INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass); INITIALIZE_PASS_DEPENDENCY(DIEstimateMemoryPass); -INITIALIZE_PASS_END(PointerScalarizerPass, "ptr-scalar", - "Pointer Scalarizer Pass", false, false) +INITIALIZE_PASS_END(PointerScalarizerPass, "ptr2reg", + "Promote Pointer To Register", false, false) FunctionPass * llvm::createPointerScalarizerPass() { return new PointerScalarizerPass(); } -static inline bool hasVolatileInstInLoop(ScalarizerContext &Ctx) { - for (auto *User : Ctx.V->users()) { - if (auto *LI = dyn_cast(User)) - if (LI->isVolatile() && Ctx.L->contains(LI)) - return true; - if (auto *SI = dyn_cast(User)) - if (SI->isVolatile() && Ctx.L->contains(SI)) - return true; - } - return false; -} - -static inline void insertDbgValueCall(ScalarizerContext &Ctx, - Instruction *I, Instruction *InsertBefore, bool Add) { - if (Add) - I->setDebugLoc(Ctx.DbgLoc); - Ctx.DIB->insertDbgValueIntrinsic( - I, dyn_cast(Ctx.DbgVar), - DIExpression::get(Ctx.F.getContext(), {}), - Ctx.DbgLoc, InsertBefore - ); +static inline void insertDbgValueCall(ScalarizerContext &Ctx, Value *V, + Instruction *InsertBefore, bool Add) { + Ctx.DIB->insertDbgValueIntrinsic(V, Ctx.DbgVar, + DIExpression::get(V->getContext(), {}), + Ctx.DbgLoc, InsertBefore); } static void insertLoadInstructions(ScalarizerContext &Ctx) { - auto *BeforeInstr = new LoadInst( - Ctx.V->getType()->getPointerElementType(), - Ctx.V, "load." + Ctx.V->getName(), - &Ctx.L->getLoopPreheader()->back()); + auto PreheaderBB{Ctx.L->getLoopPreheader()}; + auto *BeforeInstr{new LoadInst(Ctx.V->getType()->getPointerElementType(), + Ctx.V, "load." + Ctx.V->getName(), + &PreheaderBB->back())}; Ctx.InsertedLoad = BeforeInstr; - auto *insertBefore = &Ctx.L->getLoopPreheader()->back(); - insertDbgValueCall(Ctx, BeforeInstr, insertBefore, true); + auto *InsertBefore = &Ctx.L->getLoopPreheader()->back(); + insertDbgValueCall(Ctx, BeforeInstr, InsertBefore, true); + Ctx.LastValues[PreheaderBB] = Ctx.InsertedLoad; } static inline void insertStoreInstructions(ScalarizerContext &Ctx) { SmallVector ExitBlocks; Ctx.L->getExitBlocks(ExitBlocks); for (auto *BB : ExitBlocks) { - new StoreInst(Ctx.LastValues[BB], Ctx.V, BB->getFirstNonPHI()); + auto SI{new StoreInst(Ctx.LastValues[BB], Ctx.V, BB->getFirstNonPHI())}; + insertDbgValueCall(Ctx, SI->getValueOperand(), SI, false); } } @@ -173,36 +148,31 @@ static void handleMemoryAccess(BasicBlock *BB, ScalarizerContext &Ctx) { SmallVector Loads; DenseMap Stores; SmallVector ToDelete; - bool LastValuesChanged = false; + Value *LastValue{Ctx.PhiLinks[BB].getPhi()}; + if (cast(LastValue)->getParent() != BB) + LastValue = Ctx.LastValues[BB->getSinglePredecessor()]; for (auto &Instr : BB->getInstList()) { - auto *SI = dyn_cast(&Instr); - auto *LI = dyn_cast(&Instr); - if (SI && SI->getPointerOperand() == Ctx.V){ + if (auto *SI{dyn_cast(&Instr)}; + SI && SI->getPointerOperand() == Ctx.V) { ToDelete.push_back(SI); - Ctx.LastValues[SI->getParent()] = SI->getValueOperand(); - LastValuesChanged = true; - } else if (LI && LI->getPointerOperand() == Ctx.V && !LI->user_empty()) { - Value *Last = Ctx.LastValues[BB]; - LI->replaceAllUsesWith(Last); + LastValue = SI->getValueOperand(); + insertDbgValueCall(Ctx, SI->getValueOperand(), SI, false); + } else if (auto *LI{dyn_cast(&Instr)}; + LI && LI->getPointerOperand() == Ctx.V && !LI->user_empty()) { + LI->replaceAllUsesWith(LastValue); ToDelete.push_back(LI); } } for (auto *I : ToDelete) { - if (auto *LI = dyn_cast(I)) { - if (auto OpInst = dyn_cast(LI->getOperand(0))) - insertDbgValueCall(Ctx, OpInst, I, false); - } I->dropAllReferences(); I->eraseFromParent(); } - if (pred_size(BB) == 1 && !LastValuesChanged) - Ctx.LastValues[BB] = Ctx.LastValues[BB->getSinglePredecessor()]; + Ctx.LastValues[BB] = LastValue; } -static void handleLoads(ScalarizerContext &Ctx, - BasicBlock *BB, - DenseSet &Completed, - bool Init = false) { +static void handleLoads(ScalarizerContext &Ctx, BasicBlock *BB, + SmallPtrSetImpl &Completed, + bool Init = false) { if (Completed.find(BB) != Completed.end()) return; if (!Init) @@ -215,35 +185,29 @@ static void handleLoads(ScalarizerContext &Ctx, Ctx.LastValues[Succ] = Ctx.LastValues[BB]; } -static void insertPhiNodes(ScalarizerContext &Ctx, BasicBlock *BB, bool Init = false) { +static void insertPhiNodes(ScalarizerContext &Ctx, BasicBlock *BB) { if (Ctx.L->contains(BB) && Ctx.PhiLinks[BB].hasValue()) return; - bool NeedsCreate = false; - if (pred_size(BB) == 1 && !Init) { - auto *Pred = BB->getSinglePredecessor(); + bool NeedsCreate{false}; + if (pred_size(BB) == 1) { + auto *Pred{BB->getSinglePredecessor()}; if (Ctx.L->contains(Pred) && Ctx.PhiLinks[Pred].hasValue()) Ctx.PhiLinks[BB] = Ctx.PhiLinks[Pred]; else NeedsCreate = true; - } else if (!Init) { + } else { NeedsCreate = true; } if (NeedsCreate) { - auto *Phi = PHINode::Create(Ctx.InsertedLoad->getType(), 0, - "Phi." + BB->getName(), &BB->front()); - insertDbgValueCall(Ctx, Phi, BB->getFirstNonPHI(), true); + auto *Phi{PHINode::Create(Ctx.InsertedLoad->getType(), 0, + Ctx.V->getName() + "." + BB->getName(), + &BB->front())}; Ctx.PhiLinks[BB] = PhiNodeLink(Phi); Ctx.UniqueNodes.insert(Phi); } for (auto *Succ : successors(BB)) if (Ctx.L->contains(Succ)) insertPhiNodes(Ctx, Succ); - // all nodes and links are created at this point and BB = loop predecessor - if (Init) { - Ctx.LastValues[BB] = Ctx.InsertedLoad; - for (auto &P : Ctx.PhiLinks) - Ctx.LastValues[P.getFirst()] = P.getSecond().getPhi(); - } } static void fillPhiNodes(ScalarizerContext &Ctx) { @@ -260,10 +224,10 @@ static void deleteRedundantPhiNodes(ScalarizerContext &Ctx) { bool Changed = false; do { SmallVector ToDelete; - for (auto *Phi: Ctx.UniqueNodes) { + for (auto *Phi : Ctx.UniqueNodes) { bool SameOperands = true; auto *Op = Phi->getOperand(0); - for (auto *OtherOp: Phi->operand_values()) + for (auto *OtherOp : Phi->operand_values()) if (Op != OtherOp) { SameOperands = false; break; @@ -275,7 +239,7 @@ static void deleteRedundantPhiNodes(ScalarizerContext &Ctx) { for (auto *Phi : ToDelete) { Phi->replaceAllUsesWith(Phi->getOperand(0)); Ctx.UniqueNodes.erase(Phi); - Ctx.PhiLinks[Phi->getParent()].phiNode = nullptr; + Ctx.PhiLinks[Phi->getParent()].PhiNode = nullptr; auto *Instr = dyn_cast(Phi->getOperand(0)); Ctx.PhiLinks[Phi->getParent()] = Ctx.PhiLinks[Instr->getParent()]; Phi->eraseFromParent(); @@ -283,76 +247,59 @@ static void deleteRedundantPhiNodes(ScalarizerContext &Ctx) { } while (Changed); } -static bool analyzeAliasTree(Value *V, AliasTree &AT, Loop *L, TargetLibraryInfo &TLI) { +static bool hasAmbiguousAccessesOrReadonly(Value *V, AliasTree &AT, Loop *L, + TargetLibraryInfo &TLI) { auto STR = SpanningTreeRelation(&AT); auto *EM = AT.find(MemoryLocation(V, LocationSize(0))); if (!EM) return false; EM = EM->getTopLevelParent(); + // TODO(kaniandr@gmail.com): to promote readonly variables it is necessary + // add corresponding analysis for readonly registers in the + // DIDependenceAnalysisPass pass. + bool HasWrite{false}; for (auto *BB : L->getBlocks()) { for (auto &Inst : BB->getInstList()) { - bool HasWrite = false; + bool HasAccess = false; auto *EMNode = EM->getAliasNode(AT); - auto memLambda = [&V, &STR, &HasWrite, &AT, &EM, &EMNode]( - Instruction &I, MemoryLocation &&Loc, unsigned, AccessInfo, AccessInfo W) { - if (HasWrite || W == AccessInfo::No) + auto memLambda = [BB, &V, &STR, &AT, &EM, &EMNode, &HasAccess, + &HasWrite](Instruction &I, MemoryLocation &&Loc, + unsigned, AccessInfo R, AccessInfo W) { + if (HasAccess || (W == AccessInfo::No && R == AccessInfo::No)) return; auto InstEM = AT.find(Loc); - assert(InstEM && "alias tree node is empty"); + assert(InstEM && "Memory location must not be null!"); auto *InstNode = InstEM->getAliasNode(AT); - if (InstEM->getTopLevelParent() != EM && !STR.isUnreachable(EMNode, InstNode)) { - HasWrite = true; + if (InstEM->getTopLevelParent() != EM && + !STR.isUnreachable(EMNode, InstNode)) { + HasAccess = true; } else if (InstEM->getTopLevelParent() == EM) { - if (auto *LI = dyn_cast(&I)) - if (LI->getPointerOperand() != V) - HasWrite = true; - if (auto *SI = dyn_cast(&I)) - if (SI->getPointerOperand() != V) - HasWrite = true; + if (auto *LI = dyn_cast(&I)) { + if (LI->getPointerOperand() != V || LI->isVolatile()) + HasAccess = true; + } else if (auto *SI = dyn_cast(&I)) { + HasWrite = true; + if (SI->getPointerOperand() != V || SI->isVolatile()) + HasAccess = true; + } else { + HasAccess = true; + } } }; - auto unknownMemLambda = [&HasWrite, &AT, &STR, &EMNode]( - Instruction &I, AccessInfo, AccessInfo W) { - if (HasWrite || W == AccessInfo::No) + auto unknownMemLambda = [&HasAccess, &AT, &STR, &EMNode]( + Instruction &I, AccessInfo R, AccessInfo W) { + if (HasAccess || (W == AccessInfo::No && R == AccessInfo::No)) return; auto *InstEM = AT.findUnknown(&I); - if (!STR.isEqual(InstEM, EMNode) && !STR.isUnreachable(InstEM, EMNode)) - HasWrite = true; + if (!STR.isUnreachable(InstEM, EMNode)) + HasAccess = true; }; for_each_memory(Inst, TLI, memLambda, unknownMemLambda); - if (HasWrite) - return false; + if (HasAccess) + return true; } } - return true; -} - -static bool createDbgInfo(ScalarizerContext &Ctx, - DIType *DIT, AliasTree &AT, - SmallVectorImpl &MDs) { - if (!Ctx.DbgVar) - return false; - auto &DL = Ctx.F.getParent()->getDataLayout(); - auto TypeSize = DL.getTypeStoreSize(Ctx.V->getType()->getPointerElementType()); - auto EM = AT.find(MemoryLocation(Ctx.V, LocationSize::precise(TypeSize))); - if (EM == nullptr) - return false; - auto *RawDIMem = getRawDIMemoryIfExists( - *EM, - Ctx.F.getContext(), DL, - AT.getDomTree()); - if (!RawDIMem) - return false; - auto *Scope = dyn_cast(Ctx.L->getStartLoc()->getScope()); - auto *NewVar = Ctx.DIB->createAutoVariable( - Scope, "deref." + Ctx.DbgVar->getName().str(), - Ctx.DbgVar->getFile(), Ctx.DbgVar->getLine(), - DIT, false, - DINode::FlagZero); - auto *Node = DINode::get(Ctx.F.getContext(), {RawDIMem, NewVar}); - MDs.push_back(Node); - Ctx.DbgVar = NewVar; - return true; + return !HasWrite; } bool PointerScalarizerPass::runOnFunction(Function &F) { @@ -361,101 +308,104 @@ bool PointerScalarizerPass::runOnFunction(Function &F) { auto &LoopAttr = getAnalysis(); auto &AT = getAnalysis().getAliasTree(); auto &TLI = getAnalysis().getTLI(F); - - auto MDsToAttach = SmallVector(); - for_each_loop(LI, [&TraitPool, &LoopAttr, &AT, &TLI, &F, &MDsToAttach](Loop *L) { - // 1. Find memory that was marked anti/flow/output; - // 2. Check that this memory is accessed inside the loop; - // 3. If possible, copy its value at the preheader and store it back after the exit block(s); + auto MDsToAttach = SmallVector(); + DIBuilder DIB{*F.getParent()}; + for_each_loop(LI, [&TraitPool, &LoopAttr, &AT, &TLI, &F, &DIB, + &MDsToAttach](Loop *L) { + // 1. Find memory that was marked anti/flow/output. + // 2. Check that this memory is accessed inside the loop. + // 3. If possible, copy its value at the preheader and store it back after + // the exit block(s). // 4. Replace all load/store instructions and their users in the loop's body - // with corresponding operations with the copied memory; + // with corresponding operations with the copied memory. // 5. Map inserted instructions with the original value using DI nodes. - if (!L->getLoopID() || - !L->getLoopPreheader() || + if (!L->getLoopID() || !L->getLoopPreheader() || !LoopAttr.hasAttr(*L, Attribute::NoUnwind) || LoopAttr.hasAttr(*L, Attribute::ReturnsTwice) || - LoopAttr.hasAttr(*L, AttrKind::AlwaysReturn)) + !LoopAttr.hasAttr(*L, AttrKind::AlwaysReturn)) return; - auto &Pool = TraitPool[L->getLoopID()]; - if (!Pool) + auto PoolItr{TraitPool.find(L->getLoopID())}; + if (PoolItr == TraitPool.end()) return; + SmallPtrSet Disabled; SmallDenseSet Values; - SmallDenseMap ValueTraitMapping; - for (auto &T : *Pool) { + SmallDenseMap> ToPromote; + auto disableAll = [L, &Disabled](auto &T) { + for (auto &V : *T.getMemory()) + if (V.pointsToAliveValue() && !isa(V) && + any_of_user_insts(*V, [L](User *U) { + return isa(U) && L->contains(cast(U)); + })) + Disabled.insert(V); + }; + for (auto &T : *PoolItr->get()) { if (!T.is_any()) continue; + Value *SingleV{nullptr}; for (auto &V : *T.getMemory()) { - if (!V.pointsToAliveValue() || - isa(V) || - isa(V) || - isa(V)) + if (!V.pointsToAliveValue() || isa(V) || + !any_of_user_insts(*V, [L](User *U) { + return isa(U) && L->contains(cast(U)); + })) continue; - if (auto *I = dyn_cast(V)) - if (L->contains(I)) - continue; - for (auto *User : V->users()) { - if (auto *SI = dyn_cast(User)) - if (L->contains(SI) && SI->getPointerOperand() == V) { - Values.insert(V); - ValueTraitMapping[V] = &T; - break; - } + if (Disabled.contains(V) || SingleV || + (isa(V) && L->contains(cast(V))) || + [L, V]() { + for (auto *BB : L->getBlocks()) + for (auto &I : BB->getInstList()) + if (auto *DVI{dyn_cast(&I)}; + DVI && DVI->getVariableLocation() == V) + return true; + return false; + }() || + hasAmbiguousAccessesOrReadonly(V, AT, L, TLI)) { + SingleV = nullptr; + disableAll(T); + break; } + SingleV = V; } + if (SingleV) + ToPromote.try_emplace(SingleV).first->second.push_back(&T); } - for (auto *Val : Values) { - auto DIB = new DIBuilder(*F.getParent()); - auto Ctx = ScalarizerContext(Val, F, L, DIB); - if (hasVolatileInstInLoop(Ctx) || - !analyzeAliasTree(Val, AT, L, TLI)) { - ValueTraitMapping.erase(Val); + for (auto &&[V, Traits] : ToPromote) { + if (Disabled.contains(V)) continue; + auto Ctx = ScalarizerContext(V, L, &DIB); + auto *DITy{createStubType( + *F.getParent(), cast(V->getType())->getAddressSpace(), + DIB)}; + auto *Scope{dyn_cast(Ctx.L->getStartLoc()->getScope())}; + StringRef Name{V->getName()}; + DIFile *File{nullptr}; + unsigned Line{0}; + if (auto *DIEM{dyn_cast(Traits.front()->getMemory())}) { + auto *Var{DIEM->getVariable()}; + Name = Var->getName(); + File = Var->getFile(); + Line = Var->getLine(); } - SmallVector DILocs; - Optional DIM; - if (isa(Ctx.V) || isa(Ctx.V)) { - DIM = findMetadata(Ctx.V, DILocs); - } else { - bool HasDbgInLoop = false; - for (auto *BB : L->getBlocks()) - for (auto &I : BB->getInstList()) - if (auto *DVI = dyn_cast(&I)) - if (DVI->getValue() == Ctx.V) { - HasDbgInLoop = true; - break; - } - if (!HasDbgInLoop) - DIM = findMetadata(Ctx.V, {&L->getHeader()->front()}, AT.getDomTree(), DILocs); + auto *NewVar{Ctx.DIB->createAutoVariable(Scope, ("deref." + Name).str(), + File, Line, DITy, false, + DINode::FlagZero)}; + for (auto *T : Traits) { + auto *Node{DINode::get(F.getContext(), + {static_cast(const_cast( + (T->getMemory()->getAsMDNode()))), + NewVar})}; + MDsToAttach.push_back(Node); } - if (!DIM || !DIM->isValid()) - continue; - Ctx.DbgVar = DIM->Var; - Ctx.DbgLoc = DIM->Loc; - bool InsertedDI = false; - auto *DerivedType = dyn_cast(Ctx.DbgVar->getType()); - if (DerivedType && Val->getType()->isPointerTy()) - InsertedDI = createDbgInfo(Ctx, DerivedType->getBaseType(), AT, MDsToAttach); - else - InsertedDI = createDbgInfo(Ctx, Ctx.DbgVar->getType(), AT, MDsToAttach); - if (!InsertedDI) - continue; - if (!Ctx.DbgLoc) - Ctx.DbgLoc = DILocation::get( - F.getContext(), - DIM->Var->getLine(), - 0, - Ctx.DbgVar->getScope()); + Ctx.DbgVar = NewVar; + Ctx.DbgLoc = DILocation::get(F.getContext(), NewVar->getLine(), 0, + Ctx.DbgVar->getScope()); insertLoadInstructions(Ctx); - insertPhiNodes(Ctx, L->getLoopPreheader(), true); - DenseSet Processed; + insertPhiNodes(Ctx, L->getHeader()); + SmallPtrSet Processed; handleLoads(Ctx, L->getLoopPreheader(), Processed, true); fillPhiNodes(Ctx); deleteRedundantPhiNodes(Ctx); insertStoreInstructions(Ctx); } - for (auto &Pair: ValueTraitMapping) { - Pair.getSecond()->unset(); - } }); if (!MDsToAttach.empty()) { auto *MappingNode = DINode::get(F.getContext(), MDsToAttach); From 595c6ce9284c1bd164e7a255e1bf5a5cc68119cb Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Fri, 29 Apr 2022 20:13:02 +0300 Subject: [PATCH 30/93] [TSAR, LLVM, 15.0] Migrate to LLVM 15.0. --- CMakeLists.txt | 11 +++- include/tsar/ADT/PersistentIterator.h | 4 +- include/tsar/Analysis/AnalysisServer.h | 4 +- include/tsar/Analysis/AnalysisSocket.h | 6 +- include/tsar/Analysis/Clang/IncludeTree.h | 6 +- include/tsar/Analysis/Memory/EstimateMemory.h | 3 +- .../tsar/Analysis/Memory/MemoryAccessUtils.h | 8 +-- include/tsar/Frontend/Clang/FrontendActions.h | 2 +- .../Frontend/Flang/TransformationContext.h | 5 +- include/tsar/Support/Clang/Utils.h | 2 +- include/tsar/Support/Flang/Diagnostic.h | 7 +- include/tsar/Support/Flang/Rewriter.h | 3 +- include/tsar/Support/IRUtils.h | 3 +- include/tsar/Support/MetadataUtils.h | 31 ++++----- lib/APC/APCContext.cpp | 1 + lib/APC/LoopInfo.cpp | 2 +- lib/Analysis/Clang/CanonicalLoop.cpp | 38 +++++------ lib/Analysis/Clang/GlobalInfoExtractor.cpp | 7 +- lib/Analysis/Clang/IncludeTree.cpp | 16 ++--- lib/Analysis/Clang/MemoryMatcher.cpp | 2 +- lib/Analysis/Intrinsics.cpp | 14 ++-- lib/Analysis/Memory/AliasTreePrinter.cpp | 16 ++--- lib/Analysis/Memory/AllocasModRef.cpp | 8 +-- lib/Analysis/Memory/BitMemoryTrait.cpp | 1 + lib/Analysis/Memory/DIAliasTreePrinter.cpp | 8 +-- lib/Analysis/Memory/DIArrayAccess.cpp | 2 +- lib/Analysis/Memory/DIDependenceAnalysis.cpp | 43 ++++++------ lib/Analysis/Memory/DIEstimateMemory.cpp | 2 +- lib/Analysis/Memory/DIMemoryLocation.cpp | 6 +- lib/Analysis/Memory/DefinedMemory.cpp | 8 +-- lib/Analysis/Memory/Delinearization.cpp | 6 +- lib/Analysis/Memory/DependenceAnalysis.cpp | 39 +++++------ lib/Analysis/Memory/EstimateMemory.cpp | 55 ++++++++-------- lib/Analysis/Memory/GlobalDefinedMemory.cpp | 1 + lib/Analysis/Memory/GlobalLiveMemory.cpp | 8 +-- lib/Analysis/Memory/GlobalsAccess.cpp | 6 +- lib/Analysis/Memory/LiveMemory.cpp | 4 +- lib/Analysis/Memory/NotInitializedMemory.cpp | 4 +- lib/Analysis/Memory/PrivateAnalysis.cpp | 6 +- lib/Analysis/Memory/ProcessTraitPass.cpp | 1 + lib/Analysis/Memory/TraitFilter.cpp | 4 +- lib/Analysis/Memory/Utils.cpp | 8 +-- lib/Analysis/Parallel/ParallelLoop.cpp | 2 +- lib/Core/TransformationContext.cpp | 2 +- lib/Frontend/Clang/Action.cpp | 20 +++--- lib/Frontend/Flang/TransformationContext.cpp | 7 +- lib/Support/Clang/Utils.cpp | 2 +- lib/Support/Flang/Rewriter.cpp | 21 +++--- lib/Support/SCEVUtils.cpp | 26 ++++---- lib/Transform/Clang/DVMHDataTransferIPO.cpp | 12 ++-- lib/Transform/Clang/ExprPropagation.cpp | 6 +- lib/Transform/Clang/LoopReversal.cpp | 7 +- lib/Transform/IR/PointerScalarizer.cpp | 2 +- lib/Transform/Mixed/DINodeRetriever.cpp | 11 ++-- lib/Transform/Mixed/DummyScopeAAPass.cpp | 4 +- lib/Transform/Mixed/Instrumentation.cpp | 12 ++-- lib/Unparse/DIUnparser.cpp | 4 +- lib/Unparse/SourceUnparserUtils.cpp | 6 +- tools/tsar-server/Messages.h | 2 +- tools/tsar-server/PrivateServerPass.cpp | 65 ++++++++++--------- 60 files changed, 329 insertions(+), 293 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5413d3f0..f8b9ade8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.4.3) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) -project(TSAR VERSION 0.0.0 LANGUAGES CXX) +project(TSAR VERSION 0.0.0 LANGUAGES C CXX) if(SAPFOR_VERSION) set(TSAR_VERSION ${SAPFOR_VERSION}) @@ -205,7 +205,7 @@ message(STATUS ${LLVM_STATUS}) set(LLVM_COMPONENTS analysis asmparser bitreader core instrumentation irreader scalaropts support tablegen target transformutils coverage mcparser option debuginfodwarf - frontendopenacc) + frontendopenacc lto windowsdriver) llvm_map_components_to_libnames(LLVM_LIBS ${LLVM_COMPONENTS}) @@ -227,6 +227,13 @@ set(CLANG_LIBS if(FLANG_FOUND) set(FLANG_LIBS FortranSemantics FortranEvaluate FortranParser FortranDecimal FortranCommon) + include(TestBigEndian) + test_big_endian(IS_BIGENDIAN) + if (IS_BIGENDIAN) + add_compile_definitions(FLANG_BIG_ENDIAN=1) + else() + add_compile_definitions(FLANG_LITTLE_ENDIAN=1) + endif() endif() if(NOT PACKAGE_LLVM) diff --git a/include/tsar/ADT/PersistentIterator.h b/include/tsar/ADT/PersistentIterator.h index e41c89dc..feb79a05 100644 --- a/include/tsar/ADT/PersistentIterator.h +++ b/include/tsar/ADT/PersistentIterator.h @@ -31,7 +31,7 @@ #include namespace llvm { -template struct DenseMapInfo; +template struct DenseMapInfo; } namespace tsar { @@ -119,7 +119,7 @@ class NotPersistentIterator { template class PersistentIteratorC { friend struct PersistentValueWrapper; - friend struct llvm::DenseMapInfo; + friend struct llvm::DenseMapInfo; using NotPersistentIteratorC = NotPersistentIterator; protected: using PersistentBucket = typename MapT::value_type; diff --git a/include/tsar/Analysis/AnalysisServer.h b/include/tsar/Analysis/AnalysisServer.h index f1393eed..c2fd04a1 100644 --- a/include/tsar/Analysis/AnalysisServer.h +++ b/include/tsar/Analysis/AnalysisServer.h @@ -260,7 +260,7 @@ class AnalysisResponsePass : public ModulePass, private bcl::Uncopyable { WaitForRequest = false; return { tsar::AnalysisSocket::Notify }; } - json::Parser Parser(Request); + ::json::Parser Parser(Request); tsar::AnalysisRequest R; if (!Parser.parse(R)) { llvm_unreachable("Unknown request: listen for analysis request!"); @@ -332,7 +332,7 @@ class AnalysisResponsePass : public ModulePass, private bcl::Uncopyable { } } return tsar::AnalysisSocket::Data + - json::Parser::unparseAsObject(Response); + ::json::Parser::unparseAsObject(Response); })) ; return false; diff --git a/include/tsar/Analysis/AnalysisSocket.h b/include/tsar/Analysis/AnalysisSocket.h index 683ae3b5..7863eeac 100644 --- a/include/tsar/Analysis/AnalysisSocket.h +++ b/include/tsar/Analysis/AnalysisSocket.h @@ -84,7 +84,7 @@ class AnalysisSocket final : public SMStringSocketBase { /// analysis pass. A `nullptr` could be encoded with empty string. void processResponse(const std::string &Response) const { llvm::StringRef Json(Response.data() + 1, Response.size() - 2); - json::Parser Parser(Json.str()); + ::json::Parser Parser(Json.str()); AnalysisResponse R; if (!Parser.parse(R)) mAnalysis.clear(); @@ -103,7 +103,7 @@ class AnalysisSocket final : public SMStringSocketBase { R[AnalysisRequest::Function] = nullptr; bcl::TypeList::for_each_type(PushBackAnalysisID{R}); auto Request = - json::Parser::unparseAsObject(R) + Delimiter; + ::json::Parser::unparseAsObject(R) + Delimiter; for (auto &Callback : mReceiveCallbacks) Callback(Request); // Note, that callback run send() in client, so mAnalysisPass is already @@ -130,7 +130,7 @@ class AnalysisSocket final : public SMStringSocketBase { R[AnalysisRequest::Function] = &F; bcl::TypeList::for_each_type(PushBackAnalysisID{R}); auto Request = - json::Parser::unparseAsObject(R) + Delimiter; + ::json::Parser::unparseAsObject(R) + Delimiter; for (auto &Callback : mReceiveCallbacks) Callback(Request); // Note, that callback run send() in client, so mAnalysisPass is already diff --git a/include/tsar/Analysis/Clang/IncludeTree.h b/include/tsar/Analysis/Clang/IncludeTree.h index 67300a84..c7f27fa2 100644 --- a/include/tsar/Analysis/Clang/IncludeTree.h +++ b/include/tsar/Analysis/Clang/IncludeTree.h @@ -71,7 +71,7 @@ class FileNode { iterator() = default; iterator(ChildrenT::iterator I) : iterator_adaptor_base(std::move(I)) {} - ChildT &operator*() { return this->I->first; } + ChildT &operator*() const { return this->I->first; } }; using iterator_range = llvm::iterator_range; @@ -211,10 +211,10 @@ class FileTree { public: iterator_impl() = default; - FileNode::ChildT & operator*() { + FileNode::ChildT & operator*() const { return FileNode::isDecl(*mRootItr) ? *mRootItr : **mChildItr; } - FileNode::ChildT * operator->() { return &operator*(); } + FileNode::ChildT * operator->() const { return &operator*(); } iterator &operator++() { if (FileNode::isDecl(*mRootItr)) { diff --git a/include/tsar/Analysis/Memory/EstimateMemory.h b/include/tsar/Analysis/Memory/EstimateMemory.h index e8421c72..1b75db10 100644 --- a/include/tsar/Analysis/Memory/EstimateMemory.h +++ b/include/tsar/Analysis/Memory/EstimateMemory.h @@ -1108,8 +1108,7 @@ class AliasTree { /// to undefined behavior. const EstimateMemory * find(const MemoryLocationRange &Loc) const { return (Loc.Kind & MemoryLocationRange::LocKind::Collapsed) ? - find(llvm::MemoryLocation(Loc.Ptr, LocationSize::unknown(), - Loc.AATags)) : + find(llvm::MemoryLocation::getBeforeOrAfter(Loc.Ptr, Loc.AATags)) : find(llvm::MemoryLocation(Loc.Ptr, Loc.UpperBound, Loc.AATags)); } diff --git a/include/tsar/Analysis/Memory/MemoryAccessUtils.h b/include/tsar/Analysis/Memory/MemoryAccessUtils.h index cd4d656e..6717038c 100644 --- a/include/tsar/Analysis/Memory/MemoryAccessUtils.h +++ b/include/tsar/Analysis/Memory/MemoryAccessUtils.h @@ -81,7 +81,7 @@ void for_each_memory(llvm::Instruction &I, llvm::TargetLibraryInfo &TLI, if (!isValidPtr(Loc.Ptr)) return; Func(*Call, std::move(Loc), Idx, - (Call->doesNotReadMemory() || IsMarker) + (Call->doesNotAccessMemory() || IsMarker) ? AccessInfo::No : AccessInfo::May, (Call->onlyReadsMemory() || IsMarker) ? AccessInfo::No : AccessInfo::May); @@ -93,7 +93,7 @@ void for_each_memory(llvm::Instruction &I, llvm::TargetLibraryInfo &TLI, if (!isValidPtr(Loc.Ptr)) return; Func(*Call, std::move(Loc), Idx, - Call->doesNotReadMemory() ? AccessInfo::No : AccessInfo::May, + Call->doesNotAccessMemory() ? AccessInfo::No : AccessInfo::May, Call->onlyReadsMemory() ? AccessInfo::No : AccessInfo::May); }); } else { @@ -106,14 +106,14 @@ void for_each_memory(llvm::Instruction &I, llvm::TargetLibraryInfo &TLI, if (!isValidPtr(Loc.Ptr)) continue; Func(*Call, std::move(Loc), Idx, - Call->doesNotReadMemory() ? AccessInfo::No : AccessInfo::May, + Call->doesNotAccessMemory() ? AccessInfo::No : AccessInfo::May, Call->onlyReadsMemory() ? AccessInfo::No : AccessInfo::May); } } if (!IsMarker && !Call->onlyAccessesArgMemory() && Call->mayReadOrWriteMemory()) UnknownFunc(*Call, - Call->doesNotReadMemory() ? AccessInfo::No : AccessInfo::May, + Call->doesNotAccessMemory() ? AccessInfo::No : AccessInfo::May, Call->onlyReadsMemory() ? AccessInfo::No : AccessInfo::May); }; switch (I.getOpcode()) { diff --git a/include/tsar/Frontend/Clang/FrontendActions.h b/include/tsar/Frontend/Clang/FrontendActions.h index 55c93ce1..3d909c83 100644 --- a/include/tsar/Frontend/Clang/FrontendActions.h +++ b/include/tsar/Frontend/Clang/FrontendActions.h @@ -75,7 +75,7 @@ class GenPCHPragmaAction : public PublicWrapperFrontendAction { bool BeginSourceFileAction(clang::CompilerInstance& CI) override; void EndSourceFileAction() override; private: - llvm::SmallVector, 1> mNamespaces; + llvm::SmallVector mNamespaces; clang::Preprocessor* mPP = nullptr; }; } diff --git a/include/tsar/Frontend/Flang/TransformationContext.h b/include/tsar/Frontend/Flang/TransformationContext.h index 6d39e0a0..1ee195f5 100644 --- a/include/tsar/Frontend/Flang/TransformationContext.h +++ b/include/tsar/Frontend/Flang/TransformationContext.h @@ -48,7 +48,7 @@ class FlangTransformationContext : public TransformationContextBase { const Fortran::common::IntrinsicTypeDefaultKinds &DefaultKinds) : TransformationContextBase(TC_Flang) , mOptions(Opts) - , mContext(DefaultKinds, Opts.features, mAllSources) {} + , mContext(DefaultKinds, Opts.features, mAllCookedSources) {} void initialize(const llvm::Module &M, const llvm::DICompileUnit &CU); @@ -97,7 +97,8 @@ class FlangTransformationContext : public TransformationContextBase { private: Fortran::parser::AllSources mAllSources; Fortran::parser::Options mOptions; - Fortran::parser::Parsing mParsing{mAllSources}; + Fortran::parser::AllCookedSources mAllCookedSources{mAllSources}; + Fortran::parser::Parsing mParsing{mAllCookedSources}; Fortran::semantics::SemanticsContext mContext; MangledToSourceMapT mGlobals; std::unique_ptr mRewriter{nullptr}; diff --git a/include/tsar/Support/Clang/Utils.h b/include/tsar/Support/Clang/Utils.h index 3fe62b18..c3150376 100644 --- a/include/tsar/Support/Clang/Utils.h +++ b/include/tsar/Support/Clang/Utils.h @@ -103,7 +103,7 @@ std::vector getRawIdentifiers(clang::SourceRange SR, /// Note, that if there are several macro definitions with the same name /// (or includes of the same file), then only the first one will be remembered. void getRawMacrosAndIncludes( - clang::FileID FID, const llvm::MemoryBuffer *InputBuffer, + clang::FileID FID, const llvm::MemoryBufferRef &InputBuffer, const clang::SourceManager &SM, const clang::LangOptions &LangOpts, llvm::StringMap &Macros, llvm::StringMap &Includes, diff --git a/include/tsar/Support/Flang/Diagnostic.h b/include/tsar/Support/Flang/Diagnostic.h index 28cd947b..b9ffde6b 100644 --- a/include/tsar/Support/Flang/Diagnostic.h +++ b/include/tsar/Support/Flang/Diagnostic.h @@ -47,7 +47,10 @@ Fortran::parser::Message &toDiag(Fortran::semantics::SemanticsContext &Ctx, // temporary object Text. return Ctx.Say(Loc, Fortran::parser::MessageFormattedText{ Fortran::parser::MessageFixedText{ - Text.data(), Text.size(), Diag->isError()}, + Text.data(), Text.size(), + Diag->isError() + ? Fortran::parser::Severity::Error + : Fortran::parser::Severity::Warning}, std::forward(Args)...}); } @@ -55,7 +58,7 @@ Fortran::parser::Message &toDiag(Fortran::semantics::SemanticsContext &Ctx, template Fortran::parser::Message &toDiag(Fortran::semantics::SemanticsContext &Ctx, unsigned int DiagId, ArgT &&... Args) { - auto Loc{Ctx.allSources().GetFirstFileProvenance()}; + auto Loc{Ctx.allCookedSources().allSources().GetFirstFileProvenance()}; assert(Loc && "At least one file must be parsed!"); return toDiag(Ctx, *Loc, DiagId, std::forward(Args)...); } diff --git a/include/tsar/Support/Flang/Rewriter.h b/include/tsar/Support/Flang/Rewriter.h index 53b15b78..31065cb9 100644 --- a/include/tsar/Support/Flang/Rewriter.h +++ b/include/tsar/Support/Flang/Rewriter.h @@ -110,7 +110,8 @@ class FlangRewriter { public: using buffer_iterator = FileMap::const_iterator; - explicit FlangRewriter(Fortran::parser::CookedSource &Cooked); + explicit FlangRewriter(const Fortran::parser::CookedSource &Cooked, + Fortran::parser::AllCookedSources &AllCooked); bool isMacroOrCompiler(Fortran::parser::Provenance P) const { if (mMainFile && mMainFile->getRange().Contains(P)) diff --git a/include/tsar/Support/IRUtils.h b/include/tsar/Support/IRUtils.h index e27c7d69..1b60bf19 100644 --- a/include/tsar/Support/IRUtils.h +++ b/include/tsar/Support/IRUtils.h @@ -26,6 +26,7 @@ #define TSAR_SUPPORT_IR_UTILS_H #include +#include #include #include @@ -62,7 +63,7 @@ inline bool hasUnderlyingPointer(llvm::Type *Ty) { if (Ty->isArrayTy()) return hasUnderlyingPointer(Ty->getArrayElementType()); if (Ty->isVectorTy()) - return hasUnderlyingPointer(Ty->getScalarType()); + return hasUnderlyingPointer(Ty->getScalarType()); if (Ty->isStructTy()) for (unsigned I = 0, EI = Ty->getStructNumElements(); I < EI; ++I) return hasUnderlyingPointer(Ty->getStructElementType(I)); diff --git a/include/tsar/Support/MetadataUtils.h b/include/tsar/Support/MetadataUtils.h index cb83b731..388054ce 100644 --- a/include/tsar/Support/MetadataUtils.h +++ b/include/tsar/Support/MetadataUtils.h @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -99,7 +100,7 @@ inline bool isForwardDim(unsigned DWLang) noexcept { } /// Returns size of type, in address units, type must not be null. -inline uint64_t getSize(const llvm::DIType *Ty) { +inline uint64_t getSize(const llvm::DIType *Ty) { assert(Ty && "Type must not be null!"); return (Ty->getSizeInBits() + 7) / 8; } @@ -112,9 +113,9 @@ llvm::Optional getConstantCount(const llvm::DISubrange &Range); /// /// For example, const int and int & will be stripped to int, typedef will be /// also stripped. -inline llvm::DIType * stripDIType(llvm::DIType *DITy) { +inline llvm::DIType * stripDIType(llvm::DIType *DITy) { using namespace llvm; - if (!DITy || !isa(DITy)) + if (!DITy || !isa(DITy)) return DITy; auto DIDTy = cast(DITy); switch (DIDTy->getTag()) { @@ -130,18 +131,18 @@ inline llvm::DIType * stripDIType(llvm::DIType *DITy) { } /// Returns type of an array element or nullptr if type is unknown. -inline llvm::DIType * arrayElementDIType(llvm::DIType *DITy) { +inline llvm::DIType * arrayElementDIType(llvm::DIType *DITy) { using namespace llvm; auto ElTy = stripDIType(DITy); - if (!ElTy) - return nullptr; - if (ElTy->getTag() != dwarf::DW_TAG_pointer_type && - ElTy->getTag() != dwarf::DW_TAG_array_type) - return nullptr; - if (ElTy->getTag() == dwarf::DW_TAG_pointer_type) - ElTy = cast(ElTy)->getBaseType(); - if (ElTy->getTag() == dwarf::DW_TAG_array_type) - ElTy = cast(ElTy)->getBaseType(); + if (!ElTy) + return nullptr; + if (ElTy->getTag() != dwarf::DW_TAG_pointer_type && + ElTy->getTag() != dwarf::DW_TAG_array_type) + return nullptr; + if (ElTy->getTag() == dwarf::DW_TAG_pointer_type) + ElTy = cast(ElTy)->getBaseType(); + if (ElTy->getTag() == dwarf::DW_TAG_array_type) + ElTy = cast(ElTy)->getBaseType(); return stripDIType(ElTy); } @@ -164,8 +165,8 @@ inline llvm::AAMDNodes sanitizeAAInfo(llvm::AAMDNodes AAInfo) { /// /// TODO (kaniandr@gmail.com): may be we should use other way to distinguish /// such types. How LLVM uses 'artificial' flag on types? -inline bool isStubType(llvm::DIType *DITy) { - return !DITy || (DITy->isArtificial() && DITy->getName() == "sapfor.type"); +inline bool isStubType(llvm::DIType *DITy) { + return !DITy || (DITy->isArtificial() && DITy->getName() == "sapfor.type"); } /// Create auxiliary type of internal needs. diff --git a/lib/APC/APCContext.cpp b/lib/APC/APCContext.cpp index 57c55cb4..aa9fea65 100644 --- a/lib/APC/APCContext.cpp +++ b/lib/APC/APCContext.cpp @@ -26,6 +26,7 @@ #include "APCContextImpl.h" #include "tsar/APC/APCContext.h" #include "tsar/APC/Passes.h" +#include #include #include diff --git a/lib/APC/LoopInfo.cpp b/lib/APC/LoopInfo.cpp index 4db000ba..12dcdee2 100644 --- a/lib/APC/LoopInfo.cpp +++ b/lib/APC/LoopInfo.cpp @@ -269,7 +269,7 @@ void APCLoopInfoBasePass::runOnLoop(Loop &L, apc::LoopGraph &APCLoop) { assert(mRegions->getRegionFor(&L) && "Loop region must not be null!"); auto DFL = cast(mRegions->getRegionFor(&L)); APCLoop.perfectLoop = - !L.empty() && mPerfect->getPerfectLoopInfo().count(DFL) ? 1 : 0; + !L.isInnermost() && mPerfect->getPerfectLoopInfo().count(DFL) ? 1 : 0; bool KnownMaxBackageCount = false; auto MaxBackedgeCount = mSE->getConstantMaxBackedgeTakenCount(&L); if (MaxBackedgeCount && !isa(MaxBackedgeCount)) { diff --git a/lib/Analysis/Clang/CanonicalLoop.cpp b/lib/Analysis/Clang/CanonicalLoop.cpp index c1348c67..3093a2dd 100644 --- a/lib/Analysis/Clang/CanonicalLoop.cpp +++ b/lib/Analysis/Clang/CanonicalLoop.cpp @@ -215,12 +215,12 @@ class CanonicalLoopLabeler : public MatchFinder::MatchCallback { Incr->getOpcode() == UO_PreInc; bool LessCondition = Condition->getOpcode() == BO_LT || Condition->getOpcode() == BO_LE; - APSInt Start, End; - if (Init->isIntegerConstantExpr(Start, *Ctx) && - (ReversedCond ? Condition->getLHS()->isIntegerConstantExpr(End, *Ctx) : - Condition->getRHS()->isIntegerConstantExpr(End, *Ctx))) - if (Increment && Start >= End || !Increment && Start <= End) - return false; + if (auto Start{Init->getIntegerConstantExpr(*Ctx)}) + if (auto End{ReversedCond + ? Condition->getLHS()->getIntegerConstantExpr(*Ctx) + : Condition->getRHS()->getIntegerConstantExpr(*Ctx)}) + if (Increment && *Start >= *End || !Increment && *Start <= *End) + return false; return Increment && LessCondition && !ReversedCond || Increment && !LessCondition && ReversedCond || !Increment && !LessCondition && !ReversedCond || @@ -243,26 +243,26 @@ class CanonicalLoopLabeler : public MatchFinder::MatchCallback { bool coherent(const clang::Expr *Init, const clang::BinaryOperator *Incr, const clang::BinaryOperator *Condition, bool ReversedCond, ASTContext *Ctx) { - APSInt Step; + Optional Step; // If step is not constant we can not prove anything. - if (!Incr->getRHS()->isIntegerConstantExpr(Step, *Ctx) && - !Incr->getLHS()->isIntegerConstantExpr(Step, *Ctx)) - return true; - if (Step.isNonNegative() && !Step.isStrictlyPositive()) + if (!(Step = Incr->getRHS()->getIntegerConstantExpr(*Ctx))) + if (!(Step = Incr->getLHS()->getIntegerConstantExpr(*Ctx))) + return true; + if (Step->isNonNegative() && !Step->isStrictlyPositive()) return false; bool Increment = - Step.isStrictlyPositive() && + Step->isStrictlyPositive() && (Incr->getOpcode() == BO_Add || Incr->getOpcode() == BO_AddAssign) || - Step.isNegative() && + Step->isNegative() && (Incr->getOpcode() == BO_Sub || Incr->getOpcode() == BO_SubAssign); bool LessCondition = Condition->getOpcode() == BO_LT || Condition->getOpcode() == BO_LE; - APSInt Start, End; - if (Init->isIntegerConstantExpr(Start, *Ctx) && - (ReversedCond ? Condition->getLHS()->isIntegerConstantExpr(End, *Ctx) : - Condition->getRHS()->isIntegerConstantExpr(End, *Ctx))) - if (Increment && Start >= End || !Increment && Start <= End) - return false; + if (auto Start{Init->getIntegerConstantExpr(*Ctx)}) + if (auto End{ReversedCond + ? Condition->getLHS()->getIntegerConstantExpr(*Ctx) + : Condition->getRHS()->getIntegerConstantExpr(*Ctx)}) + if (Increment && *Start >= *End || !Increment && *Start <= *End) + return false; return Increment && LessCondition && !ReversedCond || Increment && !LessCondition && ReversedCond || !Increment && !LessCondition && !ReversedCond || diff --git a/lib/Analysis/Clang/GlobalInfoExtractor.cpp b/lib/Analysis/Clang/GlobalInfoExtractor.cpp index 92f9a998..b1450811 100644 --- a/lib/Analysis/Clang/GlobalInfoExtractor.cpp +++ b/lib/Analysis/Clang/GlobalInfoExtractor.cpp @@ -88,11 +88,12 @@ bool ClangGlobalInfoPass::runOnModule(llvm::Module &M) { Itr->second->GIE.TraverseDecl(Context.getTranslationUnitDecl()); for (auto *File : Itr->second->GIE.getFiles()) { StringMap RawIncludes; - const llvm::MemoryBuffer *Buffer = - const_cast(SrcMgr).getMemoryBufferForFile(File); + auto Buffer{ + const_cast(SrcMgr).getMemoryBufferForFileOrNone( + File)}; FileID FID = SrcMgr.translateFile(File); getRawMacrosAndIncludes( - FID, Buffer, SrcMgr, LangOpts, Itr->second->RI.Macros, + FID, *Buffer, SrcMgr, LangOpts, Itr->second->RI.Macros, Itr->second->RI.Includes, Itr->second->RI.Identifiers); } } diff --git a/lib/Analysis/Clang/IncludeTree.cpp b/lib/Analysis/Clang/IncludeTree.cpp index a0c5a1d9..e21dea32 100644 --- a/lib/Analysis/Clang/IncludeTree.cpp +++ b/lib/Analysis/Clang/IncludeTree.cpp @@ -198,11 +198,11 @@ struct ClangIncludeTreePassGraphTraits { } }; -struct ClangIncludeTreePrinter : public DOTGraphTraitsModulePrinter< +struct ClangIncludeTreePrinter : public DOTGraphTraitsModulePrinterWrapperPass< ClangIncludeTreePass, false, tsar::FileTree *, ClangIncludeTreePassGraphTraits> { static char ID; - ClangIncludeTreePrinter() : DOTGraphTraitsModulePrinter< + ClangIncludeTreePrinter() : DOTGraphTraitsModulePrinterWrapperPass< ClangIncludeTreePass, false, tsar::FileTree *, ClangIncludeTreePassGraphTraits>("files", ID) { initializeClangIncludeTreePrinterPass(*PassRegistry::getPassRegistry()); @@ -210,11 +210,11 @@ struct ClangIncludeTreePrinter : public DOTGraphTraitsModulePrinter< }; char ClangIncludeTreePrinter::ID = 0; -struct ClangIncludeTreeOnlyPrinter : public DOTGraphTraitsModulePrinter< +struct ClangIncludeTreeOnlyPrinter : public DOTGraphTraitsModulePrinterWrapperPass< ClangIncludeTreePass, true, tsar::FileTree *, ClangIncludeTreePassGraphTraits> { static char ID; - ClangIncludeTreeOnlyPrinter() : DOTGraphTraitsModulePrinter< + ClangIncludeTreeOnlyPrinter() : DOTGraphTraitsModulePrinterWrapperPass< ClangIncludeTreePass, true, tsar::FileTree *, ClangIncludeTreePassGraphTraits>("files-only", ID) { initializeClangIncludeTreeOnlyPrinterPass(*PassRegistry::getPassRegistry()); @@ -222,11 +222,11 @@ struct ClangIncludeTreeOnlyPrinter : public DOTGraphTraitsModulePrinter< }; char ClangIncludeTreeOnlyPrinter::ID = 0; -struct ClangIncludeTreeViewer : public DOTGraphTraitsModuleViewer< +struct ClangIncludeTreeViewer : public DOTGraphTraitsModuleViewerWrapperPass< ClangIncludeTreePass, false, tsar::FileTree *, ClangIncludeTreePassGraphTraits> { static char ID; - ClangIncludeTreeViewer() : DOTGraphTraitsModuleViewer< + ClangIncludeTreeViewer() : DOTGraphTraitsModuleViewerWrapperPass< ClangIncludeTreePass, false, tsar::FileTree *, ClangIncludeTreePassGraphTraits>("files", ID) { initializeClangIncludeTreeViewerPass(*PassRegistry::getPassRegistry()); @@ -234,11 +234,11 @@ struct ClangIncludeTreeViewer : public DOTGraphTraitsModuleViewer< }; char ClangIncludeTreeViewer::ID = 0; -struct ClangIncludeTreeOnlyViewer : public DOTGraphTraitsModuleViewer< +struct ClangIncludeTreeOnlyViewer : public DOTGraphTraitsModuleViewerWrapperPass< ClangIncludeTreePass, true, tsar::FileTree *, ClangIncludeTreePassGraphTraits> { static char ID; - ClangIncludeTreeOnlyViewer() : DOTGraphTraitsModuleViewer< + ClangIncludeTreeOnlyViewer() : DOTGraphTraitsModuleViewerWrapperPass< ClangIncludeTreePass, true, tsar::FileTree *, ClangIncludeTreePassGraphTraits>("files-only", ID) { initializeClangIncludeTreeOnlyViewerPass(*PassRegistry::getPassRegistry()); diff --git a/lib/Analysis/Clang/MemoryMatcher.cpp b/lib/Analysis/Clang/MemoryMatcher.cpp index d2ce058f..d723e54d 100644 --- a/lib/Analysis/Clang/MemoryMatcher.cpp +++ b/lib/Analysis/Clang/MemoryMatcher.cpp @@ -32,12 +32,12 @@ #include #include #include +#include #include #include #include #include #include -#include using namespace clang; using namespace llvm; diff --git a/lib/Analysis/Intrinsics.cpp b/lib/Analysis/Intrinsics.cpp index 8ec21fbf..88ce30d0 100644 --- a/lib/Analysis/Intrinsics.cpp +++ b/lib/Analysis/Intrinsics.cpp @@ -30,9 +30,15 @@ #include #include -using namespace llvm; using namespace tsar; +using llvm::FunctionType; +using llvm::LLVMContext; +using llvm::Module; +using llvm::PointerType; +using llvm::StringRef; +using llvm::Type; + /// Table of string intrinsic names indexed by enum value. static const char * const IntrinsicNameTable[] = { "not_intrinsic", @@ -102,14 +108,14 @@ StringRef getName(IntrinsicId Id) { FunctionType *getType(LLVMContext &Ctx, IntrinsicId Id) { auto Offset = PrototypeOffsetTable[static_cast(Id)]; Type *ResultTy = DecodeType(Ctx, Offset.Start); - SmallVector ArgsTys; + llvm::SmallVector ArgsTys; while (Offset.Start < Offset.End) ArgsTys.push_back(DecodeType(Ctx, Offset.Start)); return FunctionType::get(ResultTy, ArgsTys, false); } -llvm::FunctionCallee getDeclaration(Module *M, IntrinsicId Id) { - return M->getOrInsertFunction(getName(Id), getType(M->getContext(), Id)); +llvm::FunctionCallee getDeclaration(llvm::Module *M, IntrinsicId Id) { + return M->getOrInsertFunction(getName(Id), getType(M->getContext(), Id)); } bool getTsarLibFunc(StringRef funcName, IntrinsicId &Id) { diff --git a/lib/Analysis/Memory/AliasTreePrinter.cpp b/lib/Analysis/Memory/AliasTreePrinter.cpp index 1f95bbb4..474c784b 100644 --- a/lib/Analysis/Memory/AliasTreePrinter.cpp +++ b/lib/Analysis/Memory/AliasTreePrinter.cpp @@ -152,11 +152,11 @@ struct EstimateMemoryPassGraphTraits { namespace { struct AliasTreePrinter: - public DOTGraphTraitsPrinter { static char ID; AliasTreePrinter() : - DOTGraphTraitsPrinter("em", ID) { initializeAliasTreePrinterPass(*PassRegistry::getPassRegistry()); } @@ -165,11 +165,11 @@ struct AliasTreePrinter: char AliasTreePrinter::ID = 0; struct AliasTreeOnlyPrinter : - public DOTGraphTraitsPrinter { static char ID; AliasTreeOnlyPrinter() : - DOTGraphTraitsPrinter("emonly", ID) { initializeAliasTreeOnlyPrinterPass(*PassRegistry::getPassRegistry()); } @@ -178,11 +178,11 @@ struct AliasTreeOnlyPrinter : char AliasTreeOnlyPrinter::ID = 0; struct AliasTreeViewer : - public DOTGraphTraitsViewer { static char ID; AliasTreeViewer() : - DOTGraphTraitsViewer("em", ID) { initializeAliasTreeViewerPass(*PassRegistry::getPassRegistry()); } @@ -191,11 +191,11 @@ struct AliasTreeViewer : char AliasTreeViewer::ID = 0; struct AliasTreeOnlyViewer : - public DOTGraphTraitsViewer { static char ID; AliasTreeOnlyViewer() : - DOTGraphTraitsViewer("emonly", ID) { initializeAliasTreeOnlyViewerPass(*PassRegistry::getPassRegistry()); } diff --git a/lib/Analysis/Memory/AllocasModRef.cpp b/lib/Analysis/Memory/AllocasModRef.cpp index 1767cebf..7b756dd8 100644 --- a/lib/Analysis/Memory/AllocasModRef.cpp +++ b/lib/Analysis/Memory/AllocasModRef.cpp @@ -64,16 +64,16 @@ void AllocasAAResult::analyzeFunction(const Function &F) { AliasResult AllocasAAResult::alias(const MemoryLocation &LocA, const MemoryLocation &LocB, AAQueryInfo &AAQI) { - auto P1 = GetUnderlyingObject(LocA.Ptr, mDL, 0); - auto P2 = GetUnderlyingObject(LocB.Ptr, mDL, 0); + auto P1 = getUnderlyingObject(LocA.Ptr, 0); + auto P2 = getUnderlyingObject(LocB.Ptr, 0); if (P1 != P2 && isIdentifiedObject(P1) && isIdentifiedObject(P2)) - return NoAlias; + return AliasResult::NoAlias; return AAResultBase::alias(LocA, LocB, AAQI); } ModRefInfo AllocasAAResult::getModRefInfo(const CallBase *Call, const MemoryLocation &Loc, AAQueryInfo &AAQI) { - auto P = GetUnderlyingObject(Loc.Ptr, mDL, 0); + auto P = getUnderlyingObject(Loc.Ptr, 0); if (auto *AI = dyn_cast(P); AI && mNonAddressTakenAllocas.count(AI)) return ModRefInfo::NoModRef; diff --git a/lib/Analysis/Memory/BitMemoryTrait.cpp b/lib/Analysis/Memory/BitMemoryTrait.cpp index cd3f904d..8e68dc84 100644 --- a/lib/Analysis/Memory/BitMemoryTrait.cpp +++ b/lib/Analysis/Memory/BitMemoryTrait.cpp @@ -23,6 +23,7 @@ //===----------------------------------------------------------------------===// #include "BitMemoryTrait.h" +#include using namespace tsar; diff --git a/lib/Analysis/Memory/DIAliasTreePrinter.cpp b/lib/Analysis/Memory/DIAliasTreePrinter.cpp index c79d2cbe..688fcf50 100644 --- a/lib/Analysis/Memory/DIAliasTreePrinter.cpp +++ b/lib/Analysis/Memory/DIAliasTreePrinter.cpp @@ -92,11 +92,11 @@ struct DIEstimateMemoryPassGraphTraits { namespace { struct DIAliasTreePrinter : - public DOTGraphTraitsPrinter { static char ID; DIAliasTreePrinter() : - DOTGraphTraitsPrinter("em-di", ID) { initializeDIAliasTreePrinterPass(*PassRegistry::getPassRegistry()); } @@ -105,11 +105,11 @@ struct DIAliasTreePrinter : char DIAliasTreePrinter::ID = 0; struct DIAliasTreeViewer : - public DOTGraphTraitsViewer { static char ID; DIAliasTreeViewer() : - DOTGraphTraitsViewer("em-di", ID) { initializeDIAliasTreeViewerPass(*PassRegistry::getPassRegistry()); } diff --git a/lib/Analysis/Memory/DIArrayAccess.cpp b/lib/Analysis/Memory/DIArrayAccess.cpp index d8dc2a6f..064f405e 100644 --- a/lib/Analysis/Memory/DIArrayAccess.cpp +++ b/lib/Analysis/Memory/DIArrayAccess.cpp @@ -769,7 +769,7 @@ copySubscriptToClient(const DIAffineSubscript &Subscript, auto Symbol{copySymbol(Subscript.getSymbol())}; auto *ClientAccess = Access.make(Subscript.getDimension(), Symbol); - for (auto &I : seq(0u, Subscript.getNumberOfMonoms())) { + for (auto I : seq(0u, Subscript.getNumberOfMonoms())) { auto LoopItr = ServerToClientLoop.find(Subscript.getMonom(I).Column); if (LoopItr == ServerToClientLoop.end()) { Access.reset(Subscript.getDimension()); diff --git a/lib/Analysis/Memory/DIDependenceAnalysis.cpp b/lib/Analysis/Memory/DIDependenceAnalysis.cpp index 38bf7847..a50d6954 100644 --- a/lib/Analysis/Memory/DIDependenceAnalysis.cpp +++ b/lib/Analysis/Memory/DIDependenceAnalysis.cpp @@ -53,9 +53,10 @@ #include #include #include +#include #include #include -#include +#include #include #include @@ -571,31 +572,31 @@ AliasNode * findBoundAliasNode(AliasTree &AT, trait::Reduction::Kind getReductionKind( const RecurrenceDescriptor &RD) { switch (const_cast(RD).getRecurrenceKind()) { - case RecurrenceDescriptor::RK_IntegerAdd: - case RecurrenceDescriptor::RK_FloatAdd: + case RecurKind::Add: + case RecurKind::FAdd: return trait::DIReduction::RK_Add; - case RecurrenceDescriptor::RK_IntegerMult: - case RecurrenceDescriptor::RK_FloatMult: + case RecurKind::Mul: + case RecurKind::FMul: return trait::DIReduction::RK_Mult; - case RecurrenceDescriptor::RK_IntegerOr: + case RecurKind::Or: return trait::DIReduction::RK_Or; - case RecurrenceDescriptor::RK_IntegerAnd: + case RecurKind::And: return trait::DIReduction::RK_And; - case RecurrenceDescriptor::RK_IntegerXor: + case RecurKind::Xor: return trait::DIReduction::RK_Xor; - case RecurrenceDescriptor::RK_IntegerMinMax: - case RecurrenceDescriptor::RK_FloatMinMax: - switch (const_cast(RD).getMinMaxRecurrenceKind()) { - case RecurrenceDescriptor::MRK_FloatMax: - case RecurrenceDescriptor::MRK_SIntMax: - case RecurrenceDescriptor::MRK_UIntMax: - return trait::DIReduction::RK_Max; - case RecurrenceDescriptor::MRK_FloatMin: - case RecurrenceDescriptor::MRK_SIntMin: - case RecurrenceDescriptor::MRK_UIntMin: - return trait::DIReduction::RK_Min; - } - break; + case RecurKind::FMax: + case RecurKind::SMax: + case RecurKind::UMax: + return trait::DIReduction::RK_Max; + case RecurKind::FMin: + case RecurKind::SMin: + case RecurKind::UMin: + return trait::DIReduction::RK_Min; + case RecurKind::FMulAdd: + case RecurKind::SelectICmp: + case RecurKind::SelectFCmp: + //TODO (kaniandr@gmail.com): add support for these kinds of reductions. + return trait::DIReduction::RK_NoReduction; } llvm_unreachable("Unknown kind of reduction!"); return trait::DIReduction::RK_NoReduction; diff --git a/lib/Analysis/Memory/DIEstimateMemory.cpp b/lib/Analysis/Memory/DIEstimateMemory.cpp index e542142b..d1a512bc 100644 --- a/lib/Analysis/Memory/DIEstimateMemory.cpp +++ b/lib/Analysis/Memory/DIEstimateMemory.cpp @@ -1485,7 +1485,7 @@ std::pair buildDIExpression( Base = GetPointerBaseWithConstantOffset(Curr, CurrOffset, DL); Offset += CurrOffset; if (Curr == Base) { - Base = GetUnderlyingObject(const_cast(Curr), DL, 1); + Base = getUnderlyingObject(const_cast(Curr), 1); if (Curr == Base) break; IsTemplate = true; diff --git a/lib/Analysis/Memory/DIMemoryLocation.cpp b/lib/Analysis/Memory/DIMemoryLocation.cpp index c82d30c1..44cea79f 100644 --- a/lib/Analysis/Memory/DIMemoryLocation.cpp +++ b/lib/Analysis/Memory/DIMemoryLocation.cpp @@ -55,10 +55,10 @@ LocationSize DIMemoryLocation::getSize() const { assert(isValid() && "Debug memory location is invalid!"); auto Fragment = Expr->getFragmentInfo(); if (Fragment.hasValue()) - return Fragment->SizeInBits == 0 ? LocationSize::unknown() : + return Fragment->SizeInBits == 0 ? LocationSize::afterPointer() : LocationSize::precise((Fragment->SizeInBits + 7) / 8); if (hasDeref()) - return LocationSize::unknown(); + return LocationSize::afterPointer(); if (auto Ty = stripDIType(Var->getType())) { // There is no dereference and size of type is known, so try to determine // size. We should check that the last offset does not lead to out of range @@ -72,7 +72,7 @@ LocationSize DIMemoryLocation::getSize() const { return LocationSize::precise(TySize - Offsets.back()); } // Return UnknownSize in case of out of range memory access. - return LocationSize::unknown(); + return LocationSize::afterPointer(); } void DIMemoryLocation::getOffsets( SmallVectorImpl &Offsets, SmallBitVector &SignMask) const { diff --git a/lib/Analysis/Memory/DefinedMemory.cpp b/lib/Analysis/Memory/DefinedMemory.cpp index c60b15db..cfb0fcc1 100644 --- a/lib/Analysis/Memory/DefinedMemory.cpp +++ b/lib/Analysis/Memory/DefinedMemory.cpp @@ -267,7 +267,7 @@ std::pair aggregate( LLVM_DEBUG(dbgs() << "[AGGREGATE] Array info: " << *ArrayPtr->getBase() << ", IsAddress: " << ArrayPtr->isAddressOfVariable() << ".\n"); auto BaseType = ArrayPtr->getBase()->getType(); - auto ArrayType = cast(BaseType)->getElementType(); + auto ArrayType = cast(BaseType)->getPointerElementType(); if (ArrayPtr->isAddressOfVariable()) { ResLoc.Kind = LocKind::NonCollapsable; return std::make_pair(ResLoc, true); @@ -448,7 +448,7 @@ class AddKnownAccessFunctor : : mLoc.Size.hasValue() ? LocationSize::upperBound( OffsetLoc - OffsetALoc + mLoc.Size.getValue()) - : LocationSize::unknown(); + : LocationSize::afterPointer(); MemoryLocationRange Range(ALoc.Ptr, LocationSize::precise(OffsetLoc - OffsetALoc), UpperBound, ALoc.AATags); @@ -644,7 +644,7 @@ void DataFlowTraits::initialize( UnknownAddressAccess = false; for (auto *Ptr : DUS->getAddressAccesses()) { if (auto *GV{ - dyn_cast(GetUnderlyingObject(Ptr, DL, 0))}) + dyn_cast(getUnderlyingObject(Ptr, 0))}) if (AT.find(MemoryLocation(GV, LocationSize::precise(0)))) DU->addAddressTransitives(GV, &I); else UnknownAddressAccess = true; @@ -769,7 +769,7 @@ void DataFlowTraits::initialize( for (auto &Loc : InterDUItr->get()->getExplicitAccesses()) if (auto *GV{dyn_cast( - GetUnderlyingObject(Loc.Ptr, DL, 0))}) { + getUnderlyingObject(Loc.Ptr, 0))}) { auto EM{ AT.find(MemoryLocation(GV, LocationSize::precise(0)))}; if (!EM) { diff --git a/lib/Analysis/Memory/Delinearization.cpp b/lib/Analysis/Memory/Delinearization.cpp index 862ed0dc..669b8c43 100644 --- a/lib/Analysis/Memory/Delinearization.cpp +++ b/lib/Analysis/Memory/Delinearization.cpp @@ -39,12 +39,12 @@ #include #include #include +#include #include #include #include #include #include -#include #include #include #include @@ -149,7 +149,7 @@ std::tuple GetUnderlyingArray(Value *Ptr, // Attached metadata may refer to an inlined function after callee has been // inlined. Try to find metadata related to the current function. if (CurrFuncDbgItr == DbgUsers.end()) { - auto InlineBasePtr = GetUnderlyingObject(BasePtrInfo.first, DL, 1); + auto InlineBasePtr = getUnderlyingObject(BasePtrInfo.first, 1); if (InlineBasePtr != BasePtrInfo.first) return GetUnderlyingArray(InlineBasePtr, DL); } @@ -496,7 +496,7 @@ void DelinearizationPass::findArrayDimensionsFromDbgInfo(Array &ArrayInfo) { DbgInsts.push_back(DII); if (DbgInsts.size() == 1) { ArrayInfo.setDimSize(DimIdx + PassPtrDim, - mSE->getSCEV(DbgInsts.front()->getVariableLocation())); + mSE->getSCEV(DbgInsts.front()->getVariableLocationOp(0))); } } LLVM_DEBUG(dbgs() << DIVar->getName() << "\n"); diff --git a/lib/Analysis/Memory/DependenceAnalysis.cpp b/lib/Analysis/Memory/DependenceAnalysis.cpp index e045eacc..65497b57 100644 --- a/lib/Analysis/Memory/DependenceAnalysis.cpp +++ b/lib/Analysis/Memory/DependenceAnalysis.cpp @@ -62,6 +62,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/Statistic.h" #include "llvm/Analysis/AliasAnalysis.h" +#include "llvm/Analysis/Delinearization.h" #include "llvm/Analysis/LoopInfo.h" #include "llvm/Analysis/ScalarEvolution.h" #include "llvm/Analysis/ScalarEvolutionExpressions.h" @@ -666,25 +667,25 @@ static AliasResult underlyingObjectsAlias(AliasAnalysis *AA, // tbaa, incompatible underlying object locations, etc. MemoryLocation LocAS(LocA.Ptr, MemoryLocation::UnknownSize, LocA.AATags); MemoryLocation LocBS(LocB.Ptr, MemoryLocation::UnknownSize, LocB.AATags); - if (AA->alias(LocAS, LocBS) == NoAlias) - return NoAlias; + if (AA->alias(LocAS, LocBS) == AliasResult::NoAlias) + return AliasResult::NoAlias; // Check the underlying objects are the same - const Value *AObj = GetUnderlyingObject(LocA.Ptr, DL); - const Value *BObj = GetUnderlyingObject(LocB.Ptr, DL); + const Value *AObj = getUnderlyingObject(LocA.Ptr); + const Value *BObj = getUnderlyingObject(LocB.Ptr); // If the underlying objects are the same, they must alias if (AObj == BObj) - return MustAlias; + return AliasResult::MustAlias; // We may have hit the recursion limit for underlying objects, or have // underlying objects where we don't know they will alias. if (!isIdentifiedObject(AObj) || !isIdentifiedObject(BObj)) - return MayAlias; + return AliasResult::MayAlias; // Otherwise we know the objects are different and both identified objects so // must not alias. - return NoAlias; + return AliasResult::NoAlias; } @@ -3484,15 +3485,15 @@ bool DependenceInfo::tryDelinearize(Instruction *Src, Instruction *Dst, // First step: collect parametric terms in both array references. SmallVector Terms; - SE->collectParametricTerms(SrcAR, Terms); - SE->collectParametricTerms(DstAR, Terms); + collectParametricTerms(*SE, SrcAR, Terms); + collectParametricTerms(*SE, DstAR, Terms); // Second step: find subscript sizes. - SE->findArrayDimensions(Terms, Sizes, ElementSize); + findArrayDimensions(*SE, Terms, Sizes, ElementSize); // Third step: compute the access functions for each subscript. - SE->computeAccessFunctions(SrcAR, SrcSubscripts, Sizes); - SE->computeAccessFunctions(DstAR, DstSubscripts, Sizes); + computeAccessFunctions(*SE, SrcAR, SrcSubscripts, Sizes); + computeAccessFunctions(*SE, DstAR, DstSubscripts, Sizes); } // Fail when there is only a subscript: that's a linearized access function. @@ -3603,16 +3604,16 @@ DependenceInfo::depends(Instruction *Src, Instruction *Dst, switch (underlyingObjectsAlias(AA, F->getParent()->getDataLayout(), MemoryLocation::get(Dst), MemoryLocation::get(Src))) { - case MayAlias: - case PartialAlias: + case AliasResult::MayAlias: + case AliasResult::PartialAlias: // cannot analyse objects if we don't understand their aliasing. LLVM_DEBUG(dbgs() << "can't analyze may or partial alias\n"); return std::make_unique(Src, Dst); - case NoAlias: + case AliasResult::NoAlias: // If the objects noalias, they are distinct, accesses are independent. LLVM_DEBUG(dbgs() << "no alias\n"); return nullptr; - case MustAlias: + case AliasResult::MustAlias: break; // The underlying objects alias; test accesses for dependence. } @@ -4038,9 +4039,9 @@ const SCEV *DependenceInfo::getSplitIteration(const Dependence &Dep, assert(isLoadOrStore(Dst)); Value *SrcPtr = getLoadStorePointerOperand(Src); Value *DstPtr = getLoadStorePointerOperand(Dst); - assert(underlyingObjectsAlias(AA, F->getParent()->getDataLayout(), - MemoryLocation::get(Dst), - MemoryLocation::get(Src)) == MustAlias); + assert(underlyingObjectsAlias( + AA, F->getParent()->getDataLayout(), MemoryLocation::getAfter(Dst), + MemoryLocation::getAfter(Src)) == AliasResult::MustAlias); // establish loop nesting levels establishNestingLevels(Src, Dst); diff --git a/lib/Analysis/Memory/EstimateMemory.cpp b/lib/Analysis/Memory/EstimateMemory.cpp index 9f89752b..5aab9004 100644 --- a/lib/Analysis/Memory/EstimateMemory.cpp +++ b/lib/Analysis/Memory/EstimateMemory.cpp @@ -83,12 +83,12 @@ static inline void clarifyUnknownSize(const DataLayout &DL, namespace tsar { Value * stripPointer(const DataLayout &DL, Value *Ptr) { assert(Ptr && "Pointer to memory location must not be null!"); - Ptr = GetUnderlyingObject(Ptr, DL, 0); + Ptr = getUnderlyingObject(Ptr, 0); if (auto LI = dyn_cast(Ptr)) return stripPointer(DL, LI->getPointerOperand()); if (Operator::getOpcode(Ptr) == Instruction::IntToPtr) { return stripPointer(DL, - GetUnderlyingObject(cast(Ptr)->getOperand(0), DL, 0)); + getUnderlyingObject(cast(Ptr)->getOperand(0), 0)); } return Ptr; } @@ -107,7 +107,7 @@ void stripToBase(const DataLayout &DL, MemoryLocation &Loc) { // analysis works well in this case. LLVM IR specification requires that // if the address space conversion is legal then both result and operand // refer to the same memory location. - auto BasePtr = GetUnderlyingObject(const_cast(Loc.Ptr), DL, 1); + auto BasePtr = getUnderlyingObject(const_cast(Loc.Ptr), 1); if (BasePtr == Loc.Ptr) return; Loc.Ptr = BasePtr; @@ -120,9 +120,10 @@ bool stripMemoryLevel(const DataLayout &DL, MemoryLocation &Loc) { assert(Loc.Ptr && "Pointer to memory location must not be null!"); auto Ty = Loc.Ptr->getType(); if (auto PtrTy = dyn_cast(Ty)) { - auto Size = PtrTy->getElementType()->isSized() ? - LocationSize::precise(DL.getTypeStoreSize(PtrTy->getElementType())) : - LocationSize::unknown(); + auto Size = PtrTy->getPointerElementType()->isSized() + ? LocationSize::precise( + DL.getTypeStoreSize(PtrTy->getPointerElementType())) + : LocationSize::afterPointer(); if (MemorySetInfo::sizecmp(Size, Loc.Size) > 0) { Loc.Size = Size; return true; @@ -190,12 +191,12 @@ bool stripMemoryLevel(const DataLayout &DL, MemoryLocation &Loc) { } } for (;;) { - auto BasePtr = GetUnderlyingObject(Loc.Ptr, DL, 0); + auto BasePtr = getUnderlyingObject(Loc.Ptr, 0); if (BasePtr == Loc.Ptr) break; Loc.Ptr = BasePtr; } - Loc.Size = LocationSize::unknown(); + Loc.Size = LocationSize::afterPointer(); clarifyUnknownSize(DL, Loc); return true; } @@ -245,9 +246,9 @@ AliasDescriptor aliasRelation(AAResults &AA, const DataLayout &DL, isAAInfoCorrupted(RHS.AATags) ? RHS.getWithoutAATags() : RHS); switch (AR) { default: llvm_unreachable("Unknown result of alias analysis!"); - case NoAlias: Dptr.set(); break; - case MayAlias: Dptr.set(); break; - case PartialAlias: + case AliasResult::NoAlias: Dptr.set(); break; + case AliasResult::MayAlias: Dptr.set(); break; + case AliasResult::PartialAlias: { Dptr.set(); // Now we try to prove that one location covers other location. @@ -260,11 +261,11 @@ AliasDescriptor aliasRelation(AAResults &AA, const DataLayout &DL, if (OffsetLHS == 0 && OffsetRHS == 0) break; auto BaseAlias = AA.alias( - BaseLHS, LocationSize::unknown(), - BaseRHS, LocationSize::unknown()); + BaseLHS, LocationSize::afterPointer(), + BaseRHS, LocationSize::afterPointer()); // It is possible to precisely compare two partially overlapped // locations in case of the same base pointer only. - if (BaseAlias != MustAlias) + if (BaseAlias != AliasResult::MustAlias) break; if (OffsetLHS < OffsetRHS && OffsetLHS + LHS.Size.getValue() >= OffsetRHS + RHS.Size.getValue()) @@ -274,7 +275,7 @@ AliasDescriptor aliasRelation(AAResults &AA, const DataLayout &DL, Dptr.set(); } break; - case MustAlias: + case AliasResult::MustAlias: Dptr.set(); if (!LHS.Size.isPrecise() || !RHS.Size.isPrecise()) { // It is safe to compare sizes, which are not precise, if pointers are @@ -681,7 +682,7 @@ AliasEstimateNode::slowMayAliasImp(const EstimateMemory &EM, AAResults &AA) { auto AR = AA.alias( MemoryLocation(LHSPtr, ThisEM.getSize(), ThisEM.getAAInfo()), MemoryLocation(RHSPtr, EM.getSize(), EM.getAAInfo())); - if (AR == NoAlias) + if (AR == AliasResult::NoAlias) continue; return std::make_pair(true, &ThisEM); } @@ -818,11 +819,11 @@ AliasResult AliasTree::isSamePointer( switch (mAA->alias( MemoryLocation(Ptr, 1, EM.getAAInfo()), MemoryLocation(Loc.Ptr, 1, LocAATags))) { - case MustAlias: return MustAlias; - case MayAlias: IsAmbiguous = true; break; + case AliasResult::MustAlias: return AliasResult::MustAlias; + case AliasResult::MayAlias: IsAmbiguous = true; break; } } - return IsAmbiguous ? MayAlias : NoAlias; + return IsAmbiguous ? AliasResult::MayAlias : AliasResult::NoAlias; } const AliasUnknownNode * AliasTree::findUnknown( @@ -859,9 +860,9 @@ const EstimateMemory * AliasTree::find(const llvm::MemoryLocation &Loc) const { if (!isSameBase(*mDL, Chain->front(), Base.Ptr)) continue; switch (isSamePointer(*Chain, Base)) { - case NoAlias: continue; - case MustAlias: break; - case MayAlias: + case AliasResult::NoAlias: continue; + case AliasResult::MustAlias: break; + case AliasResult::MayAlias: llvm_unreachable("It seems that something goes wrong or memory location"\ "was not added to alias tree!"); break; default: @@ -940,9 +941,9 @@ AliasTree::insert(const MemoryLocation &Base) { default: llvm_unreachable("Unknown result of alias query!"); break; // TODO(kaniandr@gmail.com): Is it correct to ignore NoAlias? Algorithm // should be accurately explored to understand this case. - case NoAlias: continue; - case MustAlias: break; - case MayAlias: + case AliasResult::NoAlias: continue; + case AliasResult::MustAlias: break; + case AliasResult::MayAlias: AddAmbiguous = true; Chain->getAmbiguousList()->push_back(Base.Ptr); break; @@ -1087,12 +1088,12 @@ bool EstimateMemoryPass::runOnFunction(Function &F) { return; if (AccessedMemory.count(V)) return; - auto PointeeTy = cast(V->getType())->getElementType(); + auto PointeeTy = cast(V->getType())->getPointerElementType(); assert(PointeeTy && "Pointee type must not be null!"); addLocation(MemoryLocation( V, PointeeTy->isSized() ? LocationSize::precise(DL.getTypeStoreSize(PointeeTy)) - : LocationSize::unknown())); + : LocationSize::afterPointer())); }; for (auto &Arg : F.args()) addPointeeIfNeed(&Arg); diff --git a/lib/Analysis/Memory/GlobalDefinedMemory.cpp b/lib/Analysis/Memory/GlobalDefinedMemory.cpp index be1e802c..d8a57679 100644 --- a/lib/Analysis/Memory/GlobalDefinedMemory.cpp +++ b/lib/Analysis/Memory/GlobalDefinedMemory.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include diff --git a/lib/Analysis/Memory/GlobalLiveMemory.cpp b/lib/Analysis/Memory/GlobalLiveMemory.cpp index e7d8e4e3..793c0a54 100644 --- a/lib/Analysis/Memory/GlobalLiveMemory.cpp +++ b/lib/Analysis/Memory/GlobalLiveMemory.cpp @@ -128,14 +128,14 @@ void initMayLivesWithIPO(Function &F, LiveMemoryForCalls &LiveSetForCalls, } auto init = [&DL, &F, &FOut, &MayLives](const MemoryLocationRange &Loc) { assert(Loc.Ptr && "Pointer to location must not be null!"); - auto Ptr = GetUnderlyingObject(Loc.Ptr, DL, 0); + auto Ptr = getUnderlyingObject(Loc.Ptr, 0); if (isa(Ptr)) return; if (find_if(F.args(), [Ptr](Argument &Arg) { return Ptr == &Arg; }) != F.arg_end() || isa(Ptr)) { if (Ptr == Loc.Ptr && !FOut.overlap(Loc) || - !FOut.overlap(MemoryLocation(Ptr))) + !FOut.overlap(MemoryLocation::getAfter(Ptr))) return; } MayLives.insert(Loc); @@ -306,10 +306,10 @@ bool GlobalLiveMemory::runOnModule(Module &M) { LLVM_DEBUG(dbgs() << "[GLOBAL LIVE MEMORY]: " "use conservative boundary conditions\n"); for (auto &Loc : DefUse->getDefs()) - if (!isa(GetUnderlyingObject(Loc.Ptr, DL, 0))) + if (!isa(getUnderlyingObject(Loc.Ptr, 0))) MayLives.insert(Loc); for (auto &Loc : DefUse->getMayDefs()) - if (!isa(GetUnderlyingObject(Loc.Ptr, DL, 0))) + if (!isa(getUnderlyingObject(Loc.Ptr, 0))) MayLives.insert(Loc); } LiveMemoryInfo IntraLiveInfo; diff --git a/lib/Analysis/Memory/GlobalsAccess.cpp b/lib/Analysis/Memory/GlobalsAccess.cpp index 1be36803..c70884a4 100644 --- a/lib/Analysis/Memory/GlobalsAccess.cpp +++ b/lib/Analysis/Memory/GlobalsAccess.cpp @@ -163,10 +163,10 @@ bool GlobalsAccessCollector::runOnModule(Module &M) { Value *Prev{Op}, *Curr{Op}; do { Prev = Curr; - Curr = GetUnderlyingObject(Op, DL, 0); + Curr = getUnderlyingObject(Op, 0); if (Operator::getOpcode(Curr) == Instruction::PtrToInt) - Curr = GetUnderlyingObject(cast(Curr)->getOperand(0), - DL, 0); + Curr = getUnderlyingObject(cast(Curr)->getOperand(0), + 0); } while (Curr != Prev); if (auto *GV{dyn_cast(Curr)}) SCCAccesses.insert(const_cast(GV)); diff --git a/lib/Analysis/Memory/LiveMemory.cpp b/lib/Analysis/Memory/LiveMemory.cpp index 0cf86fb9..a937f032 100644 --- a/lib/Analysis/Memory/LiveMemory.cpp +++ b/lib/Analysis/Memory/LiveMemory.cpp @@ -83,12 +83,12 @@ INITIALIZE_PASS_END(LiveMemoryPass, "live-mem", DataFlowTraits::ValueType MayLives; for (auto &Loc : DefUse->getDefs()) { assert(Loc.Ptr && "Pointer to location must not be null!"); - if (!isa(GetUnderlyingObject(Loc.Ptr, DL, 0))) + if (!isa(getUnderlyingObject(Loc.Ptr, 0))) MayLives.insert(Loc); } for (auto &Loc : DefUse->getMayDefs()) { assert(Loc.Ptr && "Pointer to location must not be null!"); - if (!isa(GetUnderlyingObject(Loc.Ptr, DL, 0))) + if (!isa(getUnderlyingObject(Loc.Ptr, 0))) MayLives.insert(Loc); } LS->setOut(std::move(MayLives)); diff --git a/lib/Analysis/Memory/NotInitializedMemory.cpp b/lib/Analysis/Memory/NotInitializedMemory.cpp index 6a66d537..a8117b8a 100644 --- a/lib/Analysis/Memory/NotInitializedMemory.cpp +++ b/lib/Analysis/Memory/NotInitializedMemory.cpp @@ -171,7 +171,7 @@ bool NotInitializedMemoryAnalysis::runOnFunction(Function &F) { releaseMemory(); mFunc = &F; if (!(mDWLang = getLanguage(F))) - return false; + return false; auto &DL = F.getParent()->getDataLayout(); auto &DFI = getAnalysis().getRegionInfo(); auto &DU = getAnalysis().getDefInfo(); @@ -184,7 +184,7 @@ bool NotInitializedMemoryAnalysis::runOnFunction(Function &F) { auto *EM = AT.find(Loc); assert(EM && "Estimate memory must not be null!"); auto *Root = EM->getTopLevelParent(); - auto Object = GetUnderlyingObject(Root->front(), DL, 0); + auto Object = getUnderlyingObject(Root->front(), 0); if (isa(Object) || isa(Object) || isa(Object) || isa(Object)) continue; diff --git a/lib/Analysis/Memory/PrivateAnalysis.cpp b/lib/Analysis/Memory/PrivateAnalysis.cpp index 9806320c..afd350c1 100644 --- a/lib/Analysis/Memory/PrivateAnalysis.cpp +++ b/lib/Analysis/Memory/PrivateAnalysis.cpp @@ -784,11 +784,11 @@ void PrivateRecognitionPass::resolveAccesses(Loop *L, const DFNode *LatchNode, // accesses, so we conservatively use analysis for the whole array // instead of analysis results for an explicitly accessed memory location. auto isLiveAggregate = [this, &LS](auto &Loc) { - auto BasePtr = GetUnderlyingObject(const_cast(Loc.Ptr), *mDL, 0); + auto BasePtr = getUnderlyingObject(const_cast(Loc.Ptr), 0); if (BasePtr == Loc.Ptr) return false; return LS.getOut().overlap( - MemoryLocation(BasePtr, LocationSize::unknown(), Loc.AATags)); + MemoryLocation::getAfter(BasePtr, Loc.AATags)); }; if (!DefUse.hasUse(Loc)) { if (!LS.getOut().overlap(Loc) && !isLiveAggregate(Loc)) @@ -849,7 +849,7 @@ void PrivateRecognitionPass::resolveAccesses(Loop *L, const DFNode *LatchNode, void PrivateRecognitionPass::resolvePointers( const tsar::DefUseSet &DefUse, TraitMap &ExplicitAccesses) { for (const auto &Loc : DefUse.getExplicitAccesses()) { - auto BasePtr = GetUnderlyingObject(const_cast(Loc.Ptr), *mDL, 0); + auto BasePtr = getUnderlyingObject(const_cast(Loc.Ptr), 0); // *p means that address of location should be loaded from p using 'load'. if (auto *LI = dyn_cast(BasePtr)) { auto *EM = mAliasTree->find(Loc); diff --git a/lib/Analysis/Memory/ProcessTraitPass.cpp b/lib/Analysis/Memory/ProcessTraitPass.cpp index ccefc64e..9e34a02c 100644 --- a/lib/Analysis/Memory/ProcessTraitPass.cpp +++ b/lib/Analysis/Memory/ProcessTraitPass.cpp @@ -26,6 +26,7 @@ #include "tsar/Analysis/Memory/DIMemoryTrait.h" #include "tsar/Analysis/Memory/Passes.h" +#include #include #include #include diff --git a/lib/Analysis/Memory/TraitFilter.cpp b/lib/Analysis/Memory/TraitFilter.cpp index 7a0b69ce..3fc047d8 100644 --- a/lib/Analysis/Memory/TraitFilter.cpp +++ b/lib/Analysis/Memory/TraitFilter.cpp @@ -26,7 +26,7 @@ #include "tsar/Analysis/Memory/DIMemoryTrait.h" #include "tsar/Analysis/Memory/EstimateMemory.h" #include "tsar/Analysis/Memory/TraitFilter.h" -#include +#include using namespace llvm; @@ -41,7 +41,7 @@ void markIfNotPromoted(const DataLayout &DL, DIMemoryTrait &T) { continue; auto *AI = dyn_cast(stripPointer(DL, VH)); if (!AI || AI->isArrayAllocation() || - AI->getType()->getElementType()->isArrayTy()) + AI->getType()->getPointerElementType()->isArrayTy()) continue; bool DbgDeclareFound = false; for (auto *U : FindDbgAddrUses(AI)) { diff --git a/lib/Analysis/Memory/Utils.cpp b/lib/Analysis/Memory/Utils.cpp index c90401a2..cef4fb5a 100644 --- a/lib/Analysis/Memory/Utils.cpp +++ b/lib/Analysis/Memory/Utils.cpp @@ -34,7 +34,7 @@ #include #include #include -#include +#include > #include using namespace llvm; @@ -132,7 +132,7 @@ std::pair GetUnderlyingObjectWithMetadata( if (V->isUsedByMetadata()) return std::make_pair(V, true); } - auto BasePtr = GetUnderlyingObject(V, DL, 1); + auto BasePtr = getUnderlyingObject(V, 1); return V == BasePtr ? std::make_pair(V, false) : GetUnderlyingObjectWithMetadata(BasePtr, DL); } @@ -496,12 +496,12 @@ std::pair isPure(const llvm::Function &F, const DefUseSet &DUS) { return std::pair{false, false}; for (auto &Arg : F.args()) if (auto Ty = dyn_cast(Arg.getType())) - if (hasUnderlyingPointer(Ty->getElementType())) + if (hasUnderlyingPointer(Ty->getPointerElementType())) return std::pair{false, false}; auto &DL = F.getParent()->getDataLayout(); bool HasGlobalAccess{false}; for (auto &Range : DUS.getExplicitAccesses()) { - auto *Ptr{GetUnderlyingObject(const_cast(Range.Ptr), DL, 0)}; + auto *Ptr{getUnderlyingObject(const_cast(Range.Ptr), 0)}; if (isa(Ptr)) HasGlobalAccess = true; if (isa(stripPointer(DL, Ptr))) diff --git a/lib/Analysis/Parallel/ParallelLoop.cpp b/lib/Analysis/Parallel/ParallelLoop.cpp index 40aaafaa..5b6ffe05 100644 --- a/lib/Analysis/Parallel/ParallelLoop.cpp +++ b/lib/Analysis/Parallel/ParallelLoop.cpp @@ -126,7 +126,7 @@ bool ParallelLoopPass::runOnFunction(Function &F) { SLoc.print(dbgs()); dbgs() << "\n"); return; } else { - if (!Callee->doesNotReadMemory() && !Callee->isSpeculatable() && + if (!Callee->doesNotAccessMemory() && !Callee->isSpeculatable() && (!isa(I) || !isDbgInfoIntrinsic(Callee->getIntrinsicID()) && !isMemoryMarkerIntrinsic(Callee->getIntrinsicID()))) { diff --git a/lib/Core/TransformationContext.cpp b/lib/Core/TransformationContext.cpp index 2ac442d2..0410cb8f 100644 --- a/lib/Core/TransformationContext.cpp +++ b/lib/Core/TransformationContext.cpp @@ -100,7 +100,7 @@ AtomicallyMovedFile::AtomicallyMovedFile(StringRef File, ErrorT *Error) : if (!mUseTemporary) { std::error_code EC; mFileStream.reset( - new llvm::raw_fd_ostream(mFilename, EC, llvm::sys::fs::F_Text)); + new llvm::raw_fd_ostream(mFilename, EC, llvm::sys::fs::OF_Text)); if (EC && mError) *mError = std::tuple{tsar::diag::err_fe_unable_to_open_output, std::tuple{std::string{mFilename}, EC.message()}}; diff --git a/lib/Frontend/Clang/Action.cpp b/lib/Frontend/Clang/Action.cpp index 5bad0fdc..197a4c8e 100644 --- a/lib/Frontend/Clang/Action.cpp +++ b/lib/Frontend/Clang/Action.cpp @@ -198,7 +198,7 @@ class AnalysisConsumer : public ASTConsumer { } bool MainAction::BeginSourceFileAction(CompilerInstance &CI) { - TimePassesIsEnabled = CI.getFrontendOpts().ShowTimers; + TimePassesIsEnabled = CI.getCodeGenOpts().TimePasses; return mQueryManager->beginSourceFile(CI, getCurrentFile()); } @@ -221,7 +221,7 @@ class ASTSocket final : public SMStringSocketBase { public: void processResponse(const std::string &Response) const { llvm::StringRef Json{Response.data() + 1, Response.size() - 2}; - json::Parser Parser(Json.str()); + ::json::Parser Parser(Json.str()); SourceResponse R; if (!Parser.parse(R)) mTfmCtx = nullptr; @@ -266,7 +266,7 @@ class SourceQueryManager : public QueryManager { Response[SourceResponse::Context] = TfmInfo->getContext(*cast(*I)); return ASTSocket::Data + - json::Parser::unparseAsObject(Response); + ::json::Parser::unparseAsObject(Response); } else { return {ASTSocket::Invalid}; } @@ -340,6 +340,7 @@ struct ActionHelper { Options.isFixedForm = (Extension == ".f" || Extension == ".F" || Extension == ".ff"); Options.searchDirectories.emplace_back("."s); + Options.needProvenanceRangeToCharBlockMappings = true; IntrusiveRefCntPtr TfmCtx{ new FlangTransformationContext{Options, DefaultKinds}}; auto &Parsing{cast(TfmCtx)->getParsing()}; @@ -347,13 +348,13 @@ struct ActionHelper { cast(TfmCtx)->getOptions()); if (!Parsing.messages().empty() && Parsing.messages().AnyFatalError()) { - Parsing.messages().Emit(errs(), Parsing.cooked()); + Parsing.messages().Emit(errs(), Parsing.allCooked()); errs() << IRSource << " could not scan " << Path << '\n'; return nullptr; } Parsing.Parse(outs()); Parsing.ClearLog(); - Parsing.messages().Emit(errs(), Parsing.cooked()); + Parsing.messages().Emit(errs(), Parsing.allCooked()); if (!Parsing.consumedWholeFile()) { Parsing.EmitMessage(errs(), Parsing.finalRestingPlace(), "parser FAIL (final position)"); @@ -367,7 +368,7 @@ struct ActionHelper { auto &ParseTree{ *Parsing.parseTree() }; Fortran::semantics::Semantics Semantics{ cast(TfmCtx)->getContext(), ParseTree, - Parsing.cooked(), false}; + false}; Semantics.Perform(); Semantics.EmitMessages(llvm::errs()); if (Semantics.AnyFatalError()) { @@ -424,13 +425,12 @@ void MainAction::ExecuteAction() { CompilerInstance &CI = getCompilerInstance(); SourceManager &SM = CI.getSourceManager(); FileID FID = SM.getMainFileID(); - auto *MainFile = SM.getBuffer(FID, &Invalid); - if (Invalid) + auto MainFile = SM.getBufferOrNone(FID); + if (!MainFile) return; llvm::SMDiagnostic Err; LLVMContext Ctx; - std::unique_ptr M = - parseIR(MainFile->getMemBufferRef(), Err, Ctx); + std::unique_ptr M = parseIR(*MainFile, Err, Ctx); if (!M) { // Translate from the diagnostic info to the SourceManager location if // available. diff --git a/lib/Frontend/Flang/TransformationContext.cpp b/lib/Frontend/Flang/TransformationContext.cpp index a9741667..c875aa71 100644 --- a/lib/Frontend/Flang/TransformationContext.cpp +++ b/lib/Frontend/Flang/TransformationContext.cpp @@ -80,8 +80,9 @@ void FlangTransformationContext::initialize( NameHierarchyMapT::key_type Names; match(Child, Names, NameHierarchy, mGlobals); } - mRewriter = std::make_unique(mParsing.cooked()); - mParsing.cooked().CompileProvenanceRangeToOffsetMappings(); + mRewriter = + std::make_unique(mParsing.cooked(), mAllCookedSources); + //mParsing.cooked().CompileProvenanceRangeToOffsetMappings(mAllSources); } std::pair FlangTransformationContext::release( @@ -118,6 +119,6 @@ std::pair FlangTransformationContext::release( MainFile = Name; } } - mContext.messages().Emit(errs(), mParsing.cooked()); + mContext.messages().Emit(errs(), mAllCookedSources); return std::make_pair(std::move(MainFile), AllWritten); } diff --git a/lib/Support/Clang/Utils.cpp b/lib/Support/Clang/Utils.cpp index 26a6e63a..b5d7f234 100644 --- a/lib/Support/Clang/Utils.cpp +++ b/lib/Support/Clang/Utils.cpp @@ -130,7 +130,7 @@ std::vector tsar::getRawIdentifiers(clang::SourceRange SR, } void tsar::getRawMacrosAndIncludes( - clang::FileID FID, const llvm::MemoryBuffer *InputBuffer, + clang::FileID FID, const llvm::MemoryBufferRef &InputBuffer, const clang::SourceManager &SM, const clang::LangOptions &LangOpts, llvm::StringMap &Macros, llvm::StringMap &Includes, diff --git a/lib/Support/Flang/Rewriter.cpp b/lib/Support/Flang/Rewriter.cpp index 79f1332b..0dfcd140 100644 --- a/lib/Support/Flang/Rewriter.cpp +++ b/lib/Support/Flang/Rewriter.cpp @@ -45,14 +45,15 @@ void FlangRewriter::FileRewriter::Map(Fortran::parser::Provenance P, << mBuffer[Offset] << "\n"); } -FlangRewriter::FlangRewriter(Fortran::parser::CookedSource &Cooked) { - for (auto &Ch : Cooked.data()) { - auto Range{Cooked.GetProvenanceRange(&Ch)}; +FlangRewriter::FlangRewriter(const Fortran::parser::CookedSource &Cooked, + Fortran::parser::AllCookedSources &AllCooked) { + for (auto &Ch : Cooked.AsCharBlock()) { + auto Range{AllCooked.GetProvenanceRange(&Ch)}; assert(Range && "Provenance range must be known!"); - auto Position{Cooked.GetSourcePositionRange(&Ch)}; + auto Position{AllCooked.GetSourcePositionRange(&Ch)}; // Check for compiler insertions, beginning spaces and macros. if (Position) { - auto *File{Cooked.allSources().GetSourceFile(Range->start())}; + auto *File{AllCooked.allSources().GetSourceFile(Range->start())}; assert(File && "File must not be null!"); auto [Itr, IsNew] = mFiles.try_emplace(File); if (IsNew) @@ -72,9 +73,9 @@ FlangRewriter::FlangRewriter(Fortran::parser::CookedSource &Cooked) { Info->getRange().start().offset() + Info->getRange().size() - 1, Info.get()); } - for (auto &Ch : Cooked.data()) { - auto Range{Cooked.GetProvenanceRange(&Ch)}; - auto Position{Cooked.GetSourcePositionRange(&Ch)}; + for (auto &Ch : Cooked.AsCharBlock()) { + auto Range{AllCooked.GetProvenanceRange(&Ch)}; + auto Position{AllCooked.GetSourcePositionRange(&Ch)}; // Check for compiler insertions, beginning spaces and macros. if (Position) { auto *FI{mIntervals.find(Range->start().offset()).value()}; @@ -85,8 +86,8 @@ FlangRewriter::FlangRewriter(Fortran::parser::CookedSource &Cooked) { FI->Map(Range->start(), Position->first); } } - auto FirstRange{Cooked.allSources().GetFirstFileProvenance()}; - auto *FirstFile{Cooked.allSources().GetSourceFile(FirstRange->start())}; + auto FirstRange{AllCooked.allSources().GetFirstFileProvenance()}; + auto *FirstFile{AllCooked.allSources().GetSourceFile(FirstRange->start())}; assert(FirstFile && mFiles.count(FirstFile) && "Main file must not be null!"); mMainFile = mFiles[FirstFile].get(); diff --git a/lib/Support/SCEVUtils.cpp b/lib/Support/SCEVUtils.cpp index 7cacf043..04308cf9 100644 --- a/lib/Support/SCEVUtils.cpp +++ b/lib/Support/SCEVUtils.cpp @@ -60,11 +60,13 @@ static inline int sizeOfSCEV(const SCEV *S) { struct SCEVDivision : public SCEVVisitor { // Except in the trivial case described above, we do not know how to divide // Expr by Denominator for the following functions with empty implementation. + void visitPtrToIntExpr(const SCEVPtrToIntExpr *Numerator) {} + void visitSequentialUMinExpr(const SCEVSequentialUMinExpr *NUmberator) {} void visitUDivExpr(const SCEVUDivExpr *Numerator) {} void visitSMaxExpr(const SCEVSMaxExpr *Numerator) {} void visitUMaxExpr(const SCEVUMaxExpr *Numerator) {} - void visitSMinExpr(const SCEVSMinExpr *Numerator) {} - void visitUMinExpr(const SCEVUMinExpr *Numerator) {} + void visitSMinExpr(const SCEVSMinExpr *Numerator) {} + void visitUMinExpr(const SCEVUMinExpr *Numerator) {} void visitUnknown(const SCEVUnknown *Numerator) {} void visitCouldNotCompute(const SCEVCouldNotCompute *Numerator) {} @@ -258,17 +260,15 @@ struct SCEVDivision : public SCEVVisitor { if (!isa(Denominator)) return; // The Remainder is obtained by replacing Denominator by 0 in Numerator. - ValueToValueMap RewriteMap; - RewriteMap[cast(Denominator)->getValue()] = - cast(Zero)->getValue(); + ValueToSCEVMapTy RewriteMap; + RewriteMap[cast(Denominator)->getValue()] = Zero; Res.Remainder = - SCEVParameterRewriter::rewrite(Numerator, SE, RewriteMap, true); + SCEVParameterRewriter::rewrite(Numerator, SE, RewriteMap); if (Res.Remainder->isZero()) { // The Quotient is obtained by replacing Denominator by 1 in Numerator. - RewriteMap[cast(Denominator)->getValue()] = - cast(One)->getValue(); + RewriteMap[cast(Denominator)->getValue()] = One; Res.Quotient = - SCEVParameterRewriter::rewrite(Numerator, SE, RewriteMap, true); + SCEVParameterRewriter::rewrite(Numerator, SE, RewriteMap); return; } if (Numerator->getType() != Res.Remainder->getType()) @@ -405,12 +405,16 @@ struct SCEVBionmialSearch : public SCEVVisitor { } } + void visitPtrToIntExpr(const SCEVPtrToIntExpr *S) { FreeTerm = S; } + void visitSequentialUMinExpr(const SCEVSequentialUMinExpr *S) { + FreeTerm = S; + } void visitConstant(const SCEVConstant *S) { FreeTerm = S; } void visitUDivExpr(const SCEVUDivExpr *S) { FreeTerm = S; } void visitSMaxExpr(const SCEVSMaxExpr *S) { FreeTerm = S; } void visitUMaxExpr(const SCEVUMaxExpr *S) { FreeTerm = S; } - void visitSMinExpr(const SCEVSMinExpr *S) { FreeTerm = S; } - void visitUMinExpr(const SCEVUMinExpr *S) { FreeTerm = S; } + void visitSMinExpr(const SCEVSMinExpr *S) { FreeTerm = S; } + void visitUMinExpr(const SCEVUMinExpr *S) { FreeTerm = S; } void visitUnknown(const SCEVUnknown *S) { FreeTerm = S; } void visitCouldNotCompute(const SCEVCouldNotCompute *S) { FreeTerm = S; } }; diff --git a/lib/Transform/Clang/DVMHDataTransferIPO.cpp b/lib/Transform/Clang/DVMHDataTransferIPO.cpp index 69c184f1..44c83600 100644 --- a/lib/Transform/Clang/DVMHDataTransferIPO.cpp +++ b/lib/Transform/Clang/DVMHDataTransferIPO.cpp @@ -46,6 +46,7 @@ #include #include #include +#include #include using namespace llvm; @@ -235,7 +236,7 @@ bool DVMHDataTransferIPOPass::initializeIPO(Module &M) { if (auto *DIEM{ dyn_cast_or_null(Var.template get())}; DIEM && !DIEM->emptyBinding() && *DIEM->begin() && - isa(GetUnderlyingObject(*DIEM->begin(), DL, 0))) { + isa(getUnderlyingObject(*DIEM->begin(), 0))) { assert(DIEM->begin() + 1 == DIEM->end() && "Alias tree is corrupted: multiple binded globals!"); ToOptimize.try_emplace(*DIEM->begin(), Var.template get()); @@ -313,7 +314,7 @@ bool DVMHDataTransferIPOPass::initializeIPO(Module &M) { ++FuncItr) for (auto &I : instructions(*std::get(*FuncItr))) for (auto &Op : I.operands()) { - auto Ptr{GetUnderlyingObject(Op.get(), M.getDataLayout(), 0)}; + auto Ptr{getUnderlyingObject(Op.get(), 0)}; if (!isa(Ptr) || (!mIPOToActual.count(Ptr) && !mIPOToGetActual.count(Ptr))) continue; @@ -349,7 +350,7 @@ bool DVMHDataTransferIPOPass::initializeIPO(Module &M) { if (isa(I)) continue; for (auto &Op: I.operands()) { - auto Ptr{GetUnderlyingObject(Op.get(), M.getDataLayout(), 0)}; + auto Ptr{getUnderlyingObject(Op.get(), 0)}; if (!isa(Ptr) || (!mIPOToActual.count(Ptr) && !mIPOToGetActual.count(Ptr))) continue; @@ -799,7 +800,7 @@ bool DVMHDataTransferIPOPass::optimizeGlobalOut( auto IsIPOVar{false}; if (auto MH{Var.template get()}; MH && !MH->emptyBinding()) if (auto BindItr{MH->begin()}; *BindItr) - if (auto Ptr{GetUnderlyingObject(*BindItr, DL, 0)}; + if (auto Ptr{getUnderlyingObject(*BindItr, 0)}; IPOVars.count(Ptr) && (IPOCalleeItr == mIPOMap.end() || !IPOCalleeItr->get().count(Ptr))) IsIPOVar = true; @@ -1069,7 +1070,7 @@ bool DVMHDataTransferIPOPass::optimizeGlobalOut( if (auto BindItr{DIEM->begin()}; FinalPragma != IPOPragma && !DIEM->emptyBinding() && *BindItr) - if (auto Ptr{GetUnderlyingObject(*BindItr, DL, 0)}; + if (auto Ptr{getUnderlyingObject(*BindItr, 0)}; IPOMemory.count(Ptr) && !IPOInfoItr->get().count(Ptr)) IPOPragma->getMemory().emplace(Var); @@ -1319,4 +1320,3 @@ INITIALIZE_PASS_END(DVMHDataTransferIPOPass, "dvmh-actual-ipo", ModulePass *llvm::createDVMHDataTransferIPOPass() { return new DVMHDataTransferIPOPass; } - diff --git a/lib/Transform/Clang/ExprPropagation.cpp b/lib/Transform/Clang/ExprPropagation.cpp index 4104cc54..6ef2ea54 100644 --- a/lib/Transform/Clang/ExprPropagation.cpp +++ b/lib/Transform/Clang/ExprPropagation.cpp @@ -727,7 +727,7 @@ void findAvailableDecls(Instruction &DI, Instruction &UI, } for (auto *Op : Ops) { auto *Call = dyn_cast(Op); - if ((Call && !Call->onlyReadsMemory() && !Call->doesNotReadMemory()) || + if ((Call && !Call->onlyReadsMemory() && !Call->doesNotAccessMemory()) || (Call && !Call->doesNotThrow())) { LLVM_DEBUG(dbgs() << "[PROPAGATION]: disable due to "; TSAR_LLVM_DUMP(Op->dump())); @@ -837,8 +837,8 @@ bool ClangExprPropagation::unparseReplacement( else return false; } else if (auto *GEP = dyn_cast(&Def)) { - auto MD = - buildDIMemory(MemoryLocation(GEP), GEP->getContext(), *mDL, *mDT); + auto MD = buildDIMemory(MemoryLocation::getAfter(GEP), GEP->getContext(), + *mDL, *mDT); if (MD && MD->isValid() && !MD->Template) { if (unparseToString(DWLang, *MD, DefStr, false)) { auto NumberOfDims = 1 + dimensionsNum( diff --git a/lib/Transform/Clang/LoopReversal.cpp b/lib/Transform/Clang/LoopReversal.cpp index b3576095..b093c1d0 100644 --- a/lib/Transform/Clang/LoopReversal.cpp +++ b/lib/Transform/Clang/LoopReversal.cpp @@ -596,8 +596,11 @@ class ClangLoopReverseVisitor return false; } auto InitStr{mRewriter.getRewrittenText(InitRange)}; - if (Start) - InitStr = Start->toString(10); + if (Start) { + SmallString<8> Init; + Start->toString(Init, 10); + InitStr = Init.str().str(); + } auto CondStr{mRewriter.getRewrittenText(CE.ExprRange)}; SmallString<128> NewInitStr; if (End && Step) { diff --git a/lib/Transform/IR/PointerScalarizer.cpp b/lib/Transform/IR/PointerScalarizer.cpp index 5e33c5fc..8c06e2d6 100644 --- a/lib/Transform/IR/PointerScalarizer.cpp +++ b/lib/Transform/IR/PointerScalarizer.cpp @@ -354,7 +354,7 @@ bool PointerScalarizerPass::runOnFunction(Function &F) { for (auto *BB : L->getBlocks()) for (auto &I : BB->getInstList()) if (auto *DVI{dyn_cast(&I)}; - DVI && DVI->getVariableLocation() == V) + DVI && DVI->getVariableLocationOp(0) == V) return true; return false; }() || diff --git a/lib/Transform/Mixed/DINodeRetriever.cpp b/lib/Transform/Mixed/DINodeRetriever.cpp index e7f8679e..094645e7 100644 --- a/lib/Transform/Mixed/DINodeRetriever.cpp +++ b/lib/Transform/Mixed/DINodeRetriever.cpp @@ -32,13 +32,13 @@ #include #include #include +#include #include #include #include #include #include #include -#include using namespace clang; using namespace llvm; @@ -80,9 +80,8 @@ class DINodeRetrieverPass : public ModulePass, private bcl::Uncopyable { auto DistinctDIVar = DILocalVariable::getDistinct( Ctx, DIVar->getScope(), "sapfor.var", DIVar->getFile(), DIVar->getLine(), DIVar->getType(), DIVar->getArg(), - DIVar->getFlags(), DIVar->getAlignInBits()); - DbgInst->setArgOperand( - 1, MetadataAsValue::get(Ctx, DistinctDIVar)); + DIVar->getFlags(), DIVar->getAlignInBits(), nullptr); + DbgInst->setVariable(DistinctDIVar); } } continue; @@ -92,7 +91,7 @@ class DINodeRetrieverPass : public ModulePass, private bcl::Uncopyable { auto *DISub = F.getSubprogram(); auto *DIVar = DILocalVariable::getDistinct( Ctx, DISub, "sapfor.var", FileCU, 0, DITy, 0, - DINode::FlagArtificial, AI->getAlignment()); + DINode::FlagArtificial, AI->getAlignment(), nullptr); DIB.insertDeclare(AI, DIVar, DIExpression::get(Ctx, {}), DILocation::get(AI->getContext(), 0, 0, DISub), AI->getParent()); } @@ -144,7 +143,7 @@ bool DINodeRetrieverPass::runOnModule(llvm::Module &M) { auto *GV = DIGlobalVariable::getDistinct( Ctx, File, Name, GlobalVar.getName(), File, Line, DITy, GlobalVar.hasLocalLinkage(), GlobalVar.isDeclaration(), - nullptr, nullptr, 0); + nullptr, nullptr, 0, nullptr); auto *GVE = DIGlobalVariableExpression::get(Ctx, GV, DIExpression::get(Ctx, {})); GlobalVar.setMetadata("sapfor.dbg", GVE); diff --git a/lib/Transform/Mixed/DummyScopeAAPass.cpp b/lib/Transform/Mixed/DummyScopeAAPass.cpp index 81e7355b..dbde9c29 100644 --- a/lib/Transform/Mixed/DummyScopeAAPass.cpp +++ b/lib/Transform/Mixed/DummyScopeAAPass.cpp @@ -162,7 +162,7 @@ bool FlangDummyAliasAnalysis::runOnFunction(Function &F) { bool IsChanged = false; for (auto &I : instructions(F)) { if (auto SI = dyn_cast(&I)) { - auto BasePtr = GetUnderlyingObject(SI->getPointerOperand(), DL, 0); + auto BasePtr = getUnderlyingObject(SI->getPointerOperand(), 0); if (auto ArgItr = Scopes.find(BasePtr); ArgItr != Scopes.end()) { updateNoAliasMD(*SI, BasePtr); MDNode *ScopeMD{MDNode::get(Ctx, {ArgItr->second})}; @@ -172,7 +172,7 @@ bool FlangDummyAliasAnalysis::runOnFunction(Function &F) { IsChanged = true; } } else if (auto LI = dyn_cast(&I)) { - auto BasePtr = GetUnderlyingObject(LI->getPointerOperand(), DL, 0); + auto BasePtr = getUnderlyingObject(LI->getPointerOperand(), 0); if (auto ArgItr = Scopes.find(BasePtr); ArgItr != Scopes.end()) { updateNoAliasMD(*LI, BasePtr); IsChanged = true; diff --git a/lib/Transform/Mixed/Instrumentation.cpp b/lib/Transform/Mixed/Instrumentation.cpp index 1f256096..be0b0c4d 100755 --- a/lib/Transform/Mixed/Instrumentation.cpp +++ b/lib/Transform/Mixed/Instrumentation.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -52,7 +53,6 @@ #include #include #include -#include #include #include @@ -770,7 +770,7 @@ void Instrumentation::visitCallBase(llvm::CallBase &Call) { auto FuncTy = Call.getFunctionType(); assert(FuncTy && "Function type must not be null!"); assert(mDT && "Dominator tree must not be null!"); - auto DIM = buildDIMemory(MemoryLocation(CalledValue), + auto DIM = buildDIMemory(MemoryLocation::getAfter(CalledValue), M->getContext(), M->getDataLayout(), *mDT); regFunction(*CalledValue, FuncTy->getReturnType(), FuncTy->getNumParams(), DIM ? DIM->Var : nullptr, FuncIdx, *M); @@ -806,8 +806,8 @@ Instrumentation::regMemoryAccessArgs(Value *Ptr, const DebugLoc &DbgLoc, if (Info.second) { auto M = InsertBefore.getModule(); assert(mDT && "Dominator tree must not be null!"); - auto DIM = - buildDIMemory(MemoryLocation(BasePtr), Ctx, M->getDataLayout(), *mDT); + auto DIM = buildDIMemory(MemoryLocation::getAfter(BasePtr), Ctx, + M->getDataLayout(), *mDT); auto ArraySize = ConstantInt::get(Type::getInt64Ty(Ctx), 1); regValue(BasePtr, BasePtr->getType(), ArraySize, DIM ? &*DIM : nullptr, OpIdx, InsertBefore, *InsertBefore.getModule()); @@ -822,7 +822,7 @@ Instrumentation::regMemoryAccessArgs(Value *Ptr, const DebugLoc &DbgLoc, auto DIVar = createPointerToDI(OpIdx, *DILoc); auto BasePtrTy = cast_or_null(BasePtr->getType()); llvm::Instruction *ArrayBase = - ((BasePtrTy && isa(BasePtrTy->getElementType())) || + ((BasePtrTy && isa(BasePtrTy->getPointerElementType())) || (isa(BasePtr) && cast(BasePtr)->isArrayAllocation())) ? new BitCastInst(BasePtr, Type::getInt8PtrTy(Ctx), @@ -1018,7 +1018,7 @@ GetElementPtrInst* Instrumentation::createDIStringPtr( Var->setMetadata("sapfor.da", MDNode::get(M.getContext(), {})); auto Int0 = llvm::ConstantInt::get(llvm::Type::getInt32Ty(Ctx), 0); return GetElementPtrInst::CreateInBounds( - Var, { Int0,Int0 }, "distring", &InsertBefore); + Var->getValueType(), Var, { Int0,Int0 }, "distring", &InsertBefore); } LoadInst* Instrumentation::createPointerToDI( diff --git a/lib/Unparse/DIUnparser.cpp b/lib/Unparse/DIUnparser.cpp index 89d514e4..492ab09d 100644 --- a/lib/Unparse/DIUnparser.cpp +++ b/lib/Unparse/DIUnparser.cpp @@ -169,7 +169,7 @@ bool DIUnparser::unparse(const GEPOperator *GEP, SmallVectorImpl &Str) { if (!WasGEPChain) { auto Size = Str.size(); if (!unparseToString(Str, I.getOperand(), mDT)) { - Str.set_size(Size); + Str.resize(Size); Str.push_back('?'); } } else { @@ -217,7 +217,7 @@ bool DIUnparser::unparse(const GEPOperator *GEP, SmallVectorImpl &Str) { Str.push_back('['); auto Size = Str.size(); if (!unparseToString(Str, I.getOperand(), mDT)) { - Str.set_size(Size); + Str.resize(Size); Str.push_back('?'); } Str.push_back(']'); diff --git a/lib/Unparse/SourceUnparserUtils.cpp b/lib/Unparse/SourceUnparserUtils.cpp index 0b09b2a7..0f4bf443 100644 --- a/lib/Unparse/SourceUnparserUtils.cpp +++ b/lib/Unparse/SourceUnparserUtils.cpp @@ -111,14 +111,14 @@ bool unparseDump(unsigned DWLang, const DIMemoryLocation &Loc, bool IsMinimal) { return false; } -bool unparseCallee(const llvm::CallBase &CB, llvm::Module &M, +bool unparseCallee(const llvm::CallBase &CB, llvm::Module &M, llvm::DominatorTree &DT, llvm::SmallVectorImpl &S, bool IsMinimal) { - auto Callee = CB.getCalledOperand()->stripPointerCasts(); + auto Callee = CB.getCalledOperand()->stripPointerCasts(); if (auto F = dyn_cast(Callee)) { S.assign(F->getName().begin(), F->getName().end()); return true; } - auto DIM = buildDIMemory(MemoryLocation(Callee), + auto DIM = buildDIMemory(MemoryLocation(Callee, LocationSize::afterPointer()), M.getContext(), M.getDataLayout(), DT); if (DIM && DIM->isValid()) if (auto DWLang = getLanguage(*DIM->Var)) diff --git a/tools/tsar-server/Messages.h b/tools/tsar-server/Messages.h index e2d7b5b0..e459f017 100644 --- a/tools/tsar-server/Messages.h +++ b/tools/tsar-server/Messages.h @@ -231,7 +231,7 @@ template struct Traits> { "Underlining type must be default constructible!"); static_assert(std::is_copy_assignable::value, "Underlining type must be copy assignable!"); - static bool parse(llvm::Optional &Dest, json::Lexer &Lex) noexcept { + static bool parse(llvm::Optional &Dest, ::json::Lexer &Lex) noexcept { try { auto Value = Lex.discardQuote(); auto S = Lex.json().substr(Value.first, Value.second - Value.first + 1); diff --git a/tools/tsar-server/PrivateServerPass.cpp b/tools/tsar-server/PrivateServerPass.cpp index ca105528..606cd823 100644 --- a/tools/tsar-server/PrivateServerPass.cpp +++ b/tools/tsar-server/PrivateServerPass.cpp @@ -416,7 +416,7 @@ JSON_DEFAULT_TRAITS(tsar::msg::, Dependence) namespace json { /// Specialization of JSON serialization traits for tsar::msg::LoopType type. template<> struct Traits { - static bool parse(tsar::msg::LoopType &Dest, json::Lexer &Lex) noexcept { + static bool parse(tsar::msg::LoopType &Dest, ::json::Lexer &Lex) noexcept { try { auto Value = Lex.discardQuote(); auto S = Lex.json().substr(Value.first, Value.second - Value.first + 1); @@ -447,7 +447,7 @@ template<> struct Traits { /// Specialization of JSON serialization traits for tsar::msg::StmtKind type. template<> struct Traits { - static bool parse(tsar::msg::StmtKind &Dest, json::Lexer &Lex) noexcept { + static bool parse(tsar::msg::StmtKind &Dest, ::json::Lexer &Lex) noexcept { try { auto Value = Lex.discardQuote(); auto S = Lex.json().substr(Value.first, Value.second - Value.first + 1); @@ -478,7 +478,8 @@ template<> struct Traits { /// Specialization of JSON serialization traits for tsar::DIAliasNode::Kind type. template<> struct Traits { - static bool parse(tsar::DIAliasNode::Kind &Dest, json::Lexer &Lex) noexcept { + static bool parse(tsar::DIAliasNode::Kind &Dest, + ::json::Lexer &Lex) noexcept { try { auto Value = Lex.discardQuote(); auto S = Lex.json().substr(Value.first, Value.second - Value.first + 1); @@ -510,7 +511,7 @@ template<> struct Traits { /// tsar::trait::DIInduction::InductionKind type. template<> struct Traits { static bool parse(trait::DIInduction::InductionKind &Dest, - json::Lexer &Lex) noexcept { + ::json::Lexer &Lex) noexcept { try { auto Value = Lex.discardQuote(); auto S = Lex.json().substr(Value.first, Value.second - Value.first + 1); @@ -548,7 +549,7 @@ template<> struct Traits { /// Specialization of JSON serialization traits for tsar::trait::DIInduction. template<> struct Traits { - static bool parse(trait::DIInduction *&Dest, json::Lexer &Lex) { + static bool parse(trait::DIInduction *&Dest, ::json::Lexer &Lex) { msg::Induction TmpDest; auto Res = Traits::parse(TmpDest, Lex); if (!Res) @@ -588,7 +589,7 @@ template<> struct Traits { /// Specialization of JSON serialization traits for tsar::trait::DIIReduction. template<> struct Traits { - static bool parse(trait::DIReduction *&Dest, json::Lexer &Lex) { + static bool parse(trait::DIReduction *&Dest, ::json::Lexer &Lex) { msg::Reduction TmpDest; auto Res = Traits::parse(TmpDest, Lex); if (!Res) @@ -606,7 +607,7 @@ template<> struct Traits { /// Specialization of JSON serialization traits for tsar::trait::DIDependence. template<> struct Traits { - static bool parse(trait::DIDependence *&Dest, json::Lexer &Lex) { + static bool parse(trait::DIDependence *&Dest, ::json::Lexer &Lex) { msg::Dependence TmpDest; auto Res = Traits::parse(TmpDest, Lex); if (!Res) @@ -685,7 +686,7 @@ template<> struct Traits { StringRef TraitStr; DIMemoryTraitSet &TS; - json::Lexer &Lex; + ::json::Lexer &Lex; bool Result; }; struct ToJSONFunctor { @@ -711,11 +712,11 @@ template<> struct Traits { const DIMemoryTraitSet &TS; String &JSON; }; - static bool parse(DIMemoryTraitSet &Dest, json::Lexer &Lex) { + static bool parse(DIMemoryTraitSet &Dest, ::json::Lexer &Lex) { Dest.unset_all(); return Parser<>::traverse>(Dest, Lex); } - static bool parse(DIMemoryTraitSet &Dest, json::Lexer &Lex, + static bool parse(DIMemoryTraitSet &Dest, ::json::Lexer &Lex, std::pair Key) noexcept { Lex.storePosition(); Lex.setPosition(Key.first); @@ -753,7 +754,7 @@ template<> struct Traits { } String &JSON; }; - static bool parse(MemoryDescriptor &Dest, json::Lexer &Lex) { + static bool parse(MemoryDescriptor &Dest, ::json::Lexer &Lex) { Position MaxIdx, Count; bool Ok; std::tie(Count, MaxIdx, Ok) = Parser<>::numberOfKeys(Lex); @@ -762,7 +763,7 @@ template<> struct Traits { Dest.unset_all(); return Parser<>::traverse>(Dest, Lex); } - static bool parse(MemoryDescriptor &Dest, json::Lexer &Lex, + static bool parse(MemoryDescriptor &Dest, ::json::Lexer &Lex, std::pair Key) noexcept { try { auto Value = Lex.discardQuote(); @@ -786,7 +787,7 @@ template<> struct Traits { /// Specialization of JSON serialization traits for tsar::CFFlags. template<> struct Traits { - static bool parse(CFFlags &Dest, json::Lexer &Lex) { + static bool parse(CFFlags &Dest, ::json::Lexer &Lex) { Position MaxIdx, Count; bool Ok; std::tie(Count, MaxIdx, Ok) = Parser<>::numberOfKeys(Lex); @@ -795,7 +796,7 @@ template<> struct Traits { Dest = CFFlags::DefaultFlags; return Parser<>::traverse>(Dest, Lex); } - static bool parse(CFFlags &Dest, json::Lexer &Lex, + static bool parse(CFFlags &Dest, ::json::Lexer &Lex, std::pair Key) noexcept { try { auto Value = Lex.discardQuote(); @@ -1120,7 +1121,7 @@ std::string PrivateServerPass::answerStatistic(llvm::Module &M) { std::make_pair(msg::Analysis::No, Loops.second)); } } - return json::Parser::unparseAsObject(Stat); + return ::json::Parser::unparseAsObject(Stat); } std::string PrivateServerPass::answerLoopTree(llvm::Module &M, @@ -1143,7 +1144,7 @@ std::string PrivateServerPass::answerLoopTree(llvm::Module &M, DefItr->second->Id != Request[msg::LoopTree::FunctionID]) continue; if (F.isDeclaration()) - return json::Parser::unparseAsObject(Request); + return ::json::Parser::unparseAsObject(Request); msg::LoopTree LoopTree; LoopTree[msg::LoopTree::FunctionID] = Request[msg::LoopTree::FunctionID]; auto &SrcMgr = TfmCtx->getContext().getSourceManager(); @@ -1240,9 +1241,9 @@ std::string PrivateServerPass::answerLoopTree(llvm::Module &M, Loop[msg::Loop::Level] = Levels.size() + 1; Levels.push_back(Loop[msg::Loop::EndLocation]); } - return json::Parser::unparseAsObject(LoopTree); + return ::json::Parser::unparseAsObject(LoopTree); } - return json::Parser::unparseAsObject(Request); + return ::json::Parser::unparseAsObject(Request); } void PrivateServerPass::collectBuiltinFunctions( @@ -1309,7 +1310,7 @@ std::string PrivateServerPass::answerFileList() { } } } - return json::Parser::unparseAsObject(FileList); + return ::json::Parser::unparseAsObject(FileList); } std::string PrivateServerPass::answerFunctionList(llvm::Module &M) { @@ -1424,7 +1425,7 @@ std::string PrivateServerPass::answerFunctionList(llvm::Module &M) { for (auto *FD : Funcs.second.get()) mVisibleToUser.try_emplace(FD, Funcs.second.get()); } - return json::Parser::unparseAsObject(FuncList); + return ::json::Parser::unparseAsObject(FuncList); } std::string PrivateServerPass::answerCalleeFuncList(llvm::Module &M, @@ -1447,7 +1448,7 @@ std::string PrivateServerPass::answerCalleeFuncList(llvm::Module &M, DefItr->second->Id != Request[msg::CalleeFuncList::FuncID]) continue; if (F.isDeclaration()) - return json::Parser::unparseAsObject(Request); + return ::json::Parser::unparseAsObject(Request); msg::CalleeFuncList StmtList = Request; auto &SrcMgr = TfmCtx->getContext().getSourceManager(); auto &Provider = getAnalysis(F); @@ -1475,7 +1476,7 @@ std::string PrivateServerPass::answerCalleeFuncList(llvm::Module &M, } } if (!Loop.get()) - return json::Parser::unparseAsObject(Request); + return ::json::Parser::unparseAsObject(Request); auto I = CFLoopInfo.find(Loop.get()); if (I != CFLoopInfo.end()) Info = &I->second; @@ -1483,7 +1484,7 @@ std::string PrivateServerPass::answerCalleeFuncList(llvm::Module &M, Info = &FuncInfo; } if (!Info) - return json::Parser::unparseAsObject(Request); + return ::json::Parser::unparseAsObject(Request); DenseMap FuncMap; std::array(msg::StmtKind::Number)> @@ -1525,9 +1526,9 @@ std::string PrivateServerPass::answerCalleeFuncList(llvm::Module &M, StmtList[msg::CalleeFuncList::Functions].push_back(std::move(CFI)); for (auto &CFI: FuncMap) StmtList[msg::CalleeFuncList::Functions].push_back(std::move(CFI.second)); - return json::Parser::unparseAsObject(StmtList); + return ::json::Parser::unparseAsObject(StmtList); } - return json::Parser::unparseAsObject(Request); + return ::json::Parser::unparseAsObject(Request); } std::string PrivateServerPass::answerAliasTree(llvm::Module &Module, @@ -1550,7 +1551,7 @@ std::string PrivateServerPass::answerAliasTree(llvm::Module &Module, DefItr->second->Id != Request[msg::AliasTree::FuncID]) continue; if (F.isDeclaration()) - return json::Parser::unparseAsObject(Request); + return ::json::Parser::unparseAsObject(Request); auto &SrcMgr = TfmCtx->getContext().getSourceManager(); auto &Provider = getAnalysis(F); auto &LoopMatcher = Provider.get().getMatcher(); @@ -1566,7 +1567,7 @@ std::string PrivateServerPass::answerAliasTree(llvm::Module &Module, break; } if (!Loop.get() || !Loop.get()->getLoopID()) - return json::Parser::unparseAsObject(Request); + return ::json::Parser::unparseAsObject(Request); auto RF = mSocket->getAnalysis< DIEstimateMemoryPass, DIDependencyAnalysisPass>(F); assert(RF && "Dependence analysis must be available!"); @@ -1585,7 +1586,7 @@ std::string PrivateServerPass::answerAliasTree(llvm::Module &Module, auto ServerLoopID = cast(*CToS.getMappedMD(Loop.get()->getLoopID())); if (!ServerLoopID) - return json::Parser::unparseAsObject(Request); + return ::json::Parser::unparseAsObject(Request); auto DIDepSet = DIDepInfo[ServerLoopID]; DenseSet Coverage; accessCoverage(DIDepSet, DIAT, Coverage, @@ -1695,10 +1696,10 @@ std::string PrivateServerPass::answerAliasTree(llvm::Module &Module, reinterpret_cast(&C), N[msg::AliasNode::Kind]); } } - return json::Parser::unparseAsObject(Response); + return ::json::Parser::unparseAsObject(Response); } } - return json::Parser::unparseAsObject(Request); + return ::json::Parser::unparseAsObject(Request); } bool PrivateServerPass::runOnModule(llvm::Module &M) { @@ -1770,9 +1771,9 @@ bool PrivateServerPass::runOnModule(llvm::Module &M) { msg::Diagnostic Diag(msg::Status::Error); if (mStdErr->isDiff()) { Diag[msg::Diagnostic::Terminal] += mStdErr->diff(); - return json::Parser::unparseAsObject(Diag); + return ::json::Parser::unparseAsObject(Diag); } - json::Parser P(Request); auto Obj = P.parse(); assert(Obj && "Invalid request!"); From 43d645f0c0039d4ee15f0347afdc303e5a71be83 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Sun, 1 May 2022 01:48:01 +0300 Subject: [PATCH 31/93] [TSAR, Tool] Invoke LLVM IR processing action directly. Clang Tooling support actions for sources only, so we process .ll files separately now. --- include/tsar/Core/IRAction.h | 44 +++ include/tsar/Core/Tool.h | 1 + include/tsar/Core/TransformationContext.h | 20 +- include/tsar/Frontend/Clang/Action.h | 15 +- lib/Core/CMakeLists.txt | 3 +- lib/Core/IRAction.cpp | 308 +++++++++++++++++++ lib/Core/Tool.cpp | 23 +- lib/Frontend/Clang/Action.cpp | 342 +--------------------- 8 files changed, 401 insertions(+), 355 deletions(-) create mode 100644 include/tsar/Core/IRAction.h create mode 100644 lib/Core/IRAction.cpp diff --git a/include/tsar/Core/IRAction.h b/include/tsar/Core/IRAction.h new file mode 100644 index 00000000..d487c1ed --- /dev/null +++ b/include/tsar/Core/IRAction.h @@ -0,0 +1,44 @@ +//===--- IRAction.h --------- TSAR IR Action --------------------*- C++ -*-===// +// +// Traits Static Analyzer (SAPFOR) +// +// Copyright 2022 DVM System Group +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +// +// This file implements an action to process multi-file project. It loads IR +// from a file, parses corresponding sources and attach them to the +// transformation context. +// +//===----------------------------------------------------------------------===// + +#include +#include +#include + +namespace clang { +namespace tooling { +class CompilationDatabase; +} +} // namespace clang + +namespace tsar { +class QueryManager; + +int executeIRAction( + llvm::StringRef ToolName, llvm::ArrayRef Sources, + QueryManager &QM, + const clang::tooling::CompilationDatabase *Compilations = nullptr); +} diff --git a/include/tsar/Core/Tool.h b/include/tsar/Core/Tool.h index 14862188..c25645fc 100644 --- a/include/tsar/Core/Tool.h +++ b/include/tsar/Core/Tool.h @@ -96,6 +96,7 @@ class Tool : private bcl::Uncopyable { /// void storePrintOptions(OptionList &IncompatibleOpts); + std::string mToolName; GlobalOptions mGlobalOpts; std::vector mCommandLine; std::vector mSources; diff --git a/include/tsar/Core/TransformationContext.h b/include/tsar/Core/TransformationContext.h index ab4a0357..3df0162b 100644 --- a/include/tsar/Core/TransformationContext.h +++ b/include/tsar/Core/TransformationContext.h @@ -40,6 +40,12 @@ #include #include +namespace clang { +namespace tooling { +class CompilationDatabase; +} +} // namespace clang + namespace llvm { class DICompileUnit; class Module; @@ -187,13 +193,15 @@ class TransformationInfo final : private bcl::Uncopyable { llvm::mapped_iterator; /// Create storage for transformation contexts. - /// - /// \param [in] CL Command line which has been used to run analysis. - explicit TransformationInfo(llvm::ArrayRef CL) - : mCommandLine(CL) {} + explicit TransformationInfo( + const clang::tooling::CompilationDatabase &Compilations) + : mCompilations(Compilations) {} /// Returns command line which has been used to run analysis. - llvm::ArrayRef getCommandLine() const { return mCommandLine; } + const clang::tooling::CompilationDatabase & + getCompilationDatabase() const noexcept { + return mCompilations; + } /// Set transformation context for a specified compilation unit. void setContext(llvm::DICompileUnit &CU, @@ -216,7 +224,7 @@ class TransformationInfo final : private bcl::Uncopyable { private: TransformationMap mTransformPool; - std::vector mCommandLine; + const clang::tooling::CompilationDatabase &mCompilations; }; } // namespace tsar diff --git a/include/tsar/Frontend/Clang/Action.h b/include/tsar/Frontend/Clang/Action.h index f8e67934..c8c2ecb1 100644 --- a/include/tsar/Frontend/Clang/Action.h +++ b/include/tsar/Frontend/Clang/Action.h @@ -39,8 +39,9 @@ class QueryManager; /// Base front-end action to analyze and transform sources. class MainAction : public clang::ASTFrontendAction { public: - MainAction(llvm::ArrayRef CL, QueryManager *QM, - bool LoadSources = true); + MainAction(const clang::tooling::CompilationDatabase &Compilations, + QueryManager &QM) + : mTfmInfo(Compilations), mQueryManager(QM) {} /// Callback at the start of processing a single input. /// @@ -54,19 +55,13 @@ class MainAction : public clang::ASTFrontendAction { /// BeginSourceFileAction (and BeginSourceFile). void EndSourceFileAction() override; - /// Return true because this action supports use with IR files. - bool hasIRSupport() const override { return true; } - - /// Execute action, evaluation of IR files is also supported. - void ExecuteAction() override; - /// Create AST Consumer. std::unique_ptr CreateASTConsumer( clang::CompilerInstance &CI, llvm::StringRef InFile) override; private: - tsar::QueryManager *mQueryManager; - std::unique_ptr mTfmInfo; + TransformationInfo mTfmInfo; + QueryManager &mQueryManager; }; /// Creates an analysis/transformations actions factory. diff --git a/lib/Core/CMakeLists.txt b/lib/Core/CMakeLists.txt index 26611993..8d9f031d 100644 --- a/lib/Core/CMakeLists.txt +++ b/lib/Core/CMakeLists.txt @@ -1,7 +1,8 @@ configure_file(${PROJECT_SOURCE_DIR}/include/tsar/Core/tsar-config.h.in tsar-config.h) -set(CORE_SOURCES TransformationContext.cpp Query.cpp Passes.cpp Tool.cpp) +set(CORE_SOURCES TransformationContext.cpp Query.cpp Passes.cpp Tool.cpp + IRAction.cpp) if(MSVC_IDE) file(GLOB_RECURSE CORE_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/lib/Core/IRAction.cpp b/lib/Core/IRAction.cpp new file mode 100644 index 00000000..4f1d7d13 --- /dev/null +++ b/lib/Core/IRAction.cpp @@ -0,0 +1,308 @@ +//===--- IRAction.cpp ------- TSAR IR Action --------------------*- C++ -*-===// +// +// Traits Static Analyzer (SAPFOR) +// +// Copyright 2022 DVM System Group +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +// +// This file implements an action to process multi-file project. It loads IR +// from a file, parses corresponding sources and attach them to the +// transformation context. +// +//===----------------------------------------------------------------------===// + +#include "tsar/Core/IRAction.h" +#include "tsar/Core/Query.h" +#include "tsar/Core/tsar-config.h" +#include "tsar/Core/TransformationContext.h" +#include "tsar/Frontend/Clang/Action.h" +#include "tsar/Frontend/Clang/FrontendActions.h" +#include +#include +#include +#include +#include "tsar/Support/MetadataUtils.h" +#include "tsar/Support/SMStringSocket.h" +#include +#include +#include +#include +#include +#ifdef FLANG_FOUND +# include "tsar/Frontend/Flang/TransformationContext.h" +# include +# include +#endif +#include + +using namespace clang; +using namespace llvm; +using namespace tsar; + +namespace tsar::detail { +JSON_OBJECT_BEGIN(SourceResponse) +JSON_OBJECT_ROOT_PAIR(SourceResponse, Context, + tsar::TransformationContextBase *) +SourceResponse() : JSON_INIT_ROOT {} +JSON_OBJECT_END(SourceResponse) +} // namespace tsar::detail + +using namespace tsar::detail; + +namespace { +template struct ActionHelper { + IntrusiveRefCntPtr CreateTransformationContext( + [[maybe_unused]] const llvm::Module &M, + [[maybe_unused]] const DICompileUnit &CU, + [[maybe_unused]] StringRef IRSource, [[maybe_unused]] StringRef Path, + [[maybe_unused]] const tooling::CompilationDatabase &Compilations) { + return nullptr; + } +}; + +class ASTSocket final : public SMStringSocketBase { +public: + void processResponse(const std::string &Response) const { + llvm::StringRef Json{Response.data() + 1, Response.size() - 2}; + ::json::Parser Parser(Json.str()); + SourceResponse R; + if (!Parser.parse(R)) + mTfmCtx = nullptr; + else + mTfmCtx = R[SourceResponse::Context]; + } + + auto getContext() { + for (auto &Callback : mReceiveCallbacks) + Callback({Data, Delimiter}); + // Note, that callback run send() in client, so result is already set here. + assert(mResponseKind == Data && "Unknown response: wait for data!"); + return IntrusiveRefCntPtr(mTfmCtx); + } + +private: + mutable TransformationContextBase *mTfmCtx{nullptr}; +}; + +class SourceQueryManager : public QueryManager { +public: + explicit SourceQueryManager(bcl::IntrusiveConnection *C) : mConnection(C) { + assert(C && "Connection must not be null!"); + } + void run(llvm::Module *M, TransformationInfo *TfmInfo) override { + bool WaitForRequest{true}; + while (WaitForRequest && + mConnection->answer([&WaitForRequest, M, + TfmInfo](std::string &Request) -> std::string { + if (Request == ASTSocket::Release) { + WaitForRequest = false; + return {ASTSocket::Notify}; + } else if (Request == ASTSocket::Data) { + auto CUs{M->getNamedMetadata("llvm.dbg.cu")}; + assert(CUs && "DICompileUnit metadata must exist!"); + auto I{find_if(CUs->operands(), + [](auto *Op) { return isa(Op); })}; + assert(I != CUs->operands().end() && + "DICompileUnit metadata must exist!"); + SourceResponse Response; + Response[SourceResponse::Context] = + TfmInfo->getContext(*cast(*I)); + return ASTSocket::Data + + ::json::Parser::unparseAsObject(Response); + } else { + return {ASTSocket::Invalid}; + } + })) + ; + } + +private: + bcl::IntrusiveConnection *mConnection; +}; + +template<> +struct ActionHelper { + ~ActionHelper() { + for (auto &S : mSockets) + S->release(); + } + + IntrusiveRefCntPtr CreateTransformationContext( + [[maybe_unused]] const llvm::Module &M, + [[maybe_unused]] const DICompileUnit &CU, + [[maybe_unused]] StringRef IRSource, StringRef Path, + const tooling::CompilationDatabase &Compilations) { + mSockets.push_back(std::make_unique()); + bcl::IntrusiveConnection::connect( + mSockets.back().get(), ASTSocket::Delimiter, + [this, &Compilations, &Path](bcl::IntrusiveConnection C) { + tooling::ClangTool CTool(Compilations, makeArrayRef(Path.str())); + SourceQueryManager SQM{&C}; + CTool.run(newActionFactory( + std::forward_as_tuple(Compilations, + static_cast(SQM))) + .get()); + }); + return mSockets.back()->getContext(); + } + +private: + std::vector> mSockets; +}; + +#ifdef FLANG_FOUND +template<> +struct ActionHelper { + Fortran::common::IntrinsicTypeDefaultKinds DefaultKinds; + + IntrusiveRefCntPtr CreateTransformationContext( + const llvm::Module &M, const DICompileUnit &CU, StringRef IRSource, + StringRef Path, const tooling::CompilationDatabase &Compilations) { + Fortran::parser::Options Options; + Options.predefinitions.emplace_back("__F18", "1"); + Options.predefinitions.emplace_back("__F18_MAJOR__", "1"); + Options.predefinitions.emplace_back("__F18_MINOR__", "1"); + Options.predefinitions.emplace_back("__F18_PATCHLEVEL__", "1"); + Options.features.Enable( + Fortran::common::LanguageFeature::BackslashEscapes, true); + auto Extension = sys::path::extension(Path); + Options.isFixedForm = + (Extension == ".f" || Extension == ".F" || Extension == ".ff"); + Options.searchDirectories.emplace_back("."s); + Options.needProvenanceRangeToCharBlockMappings = true; + IntrusiveRefCntPtr TfmCtx{ + new FlangTransformationContext{Options, DefaultKinds}}; + auto &Parsing{cast(TfmCtx)->getParsing()}; + Parsing.Prescan(std::string{Path}, + cast(TfmCtx)->getOptions()); + if (!Parsing.messages().empty() && + Parsing.messages().AnyFatalError()) { + Parsing.messages().Emit(errs(), Parsing.allCooked()); + errs() << IRSource << " could not scan " << Path << '\n'; + return nullptr; + } + Parsing.Parse(outs()); + Parsing.ClearLog(); + Parsing.messages().Emit(errs(), Parsing.allCooked()); + if (!Parsing.consumedWholeFile()) { + Parsing.EmitMessage(errs(), Parsing.finalRestingPlace(), + "parser FAIL (final position)"); + return nullptr; + } + if (!Parsing.messages().empty() && + Parsing.messages().AnyFatalError() || !Parsing.parseTree()) { + errs() << IRSource << " could not parse " << Path << '\n'; + return nullptr; + } + auto &ParseTree{ *Parsing.parseTree() }; + Fortran::semantics::Semantics Semantics{ + cast(TfmCtx)->getContext(), ParseTree, + false}; + Semantics.Perform(); + Semantics.EmitMessages(llvm::errs()); + if (Semantics.AnyFatalError()) { + errs() << IRSource << " semantic errors in " << Path << '\n'; + return nullptr; + } + cast(TfmCtx)->initialize(M, CU); + return TfmCtx; + } +}; +#endif +} + +namespace json { +template <> +struct CellTraits { + using CellKey = tsar::detail::json_::SourceResponseImpl::Context; + using ValueType = CellKey::ValueType; + inline static bool parse(ValueType &Dest, Lexer &Lex) + noexcept( + noexcept(Traits::parse(Dest, Lex))) { + uintptr_t RawDest; + auto Res = Traits::parse(RawDest, Lex); + if (Res) + Dest = reinterpret_cast(RawDest); + return Res; + } + inline static void unparse(String &JSON, const ValueType &Obj) + noexcept( + noexcept(Traits::unparse(JSON, Obj))) { + Traits::unparse(JSON, reinterpret_cast(Obj)); + } + inline static typename std::result_of< + decltype(&CellKey::name)()>::type name() + noexcept(noexcept(CellKey::name())) { + return CellKey::name(); + } +}; +} + +JSON_DEFAULT_TRAITS(::, SourceResponse) + +int tsar::executeIRAction(StringRef ToolName, ArrayRef Sources, + QueryManager &QM, + const tooling::CompilationDatabase *Compilations) { + std::size_t IsOk{Sources.size()}; + Timer ASTGeneration("ASTGeneration", "AST Generation Time"); + Timer LLVMIRAnalysis("LLVMIRAnalysis", "LLVM IR Analysis Time"); + for (auto &File : Sources) { + SMDiagnostic Err; + LLVMContext Ctx; + auto M{parseIRFile(File, Err, Ctx)}; + if (!M) { + --IsOk; + Err.print(ToolName.data(), errs()); + continue; + } + if (Compilations) { + if (TimePassesIsEnabled) + ASTGeneration.startTimer(); + TransformationInfo TfmInfo(*Compilations); + ActionHelper ClangHelper; + ActionHelper FlangHelper; + auto CUs = M->getNamedMetadata("llvm.dbg.cu"); + for (auto *Op : CUs->operands()) + if (auto *CU = dyn_cast(Op)) { + SmallString<128> Path{CU->getFilename()}; + sys::fs::make_absolute(CU->getDirectory(), Path); + if (isFortran(CU->getSourceLanguage())) { + if (auto TfmCtx{FlangHelper.CreateTransformationContext( + *M, *CU, File, Path, *Compilations)}) + TfmInfo.setContext(*CU, std::move(TfmCtx)); + } else if (isC(CU->getSourceLanguage()) || + isCXX(CU->getSourceLanguage())) + if (auto TfmCtx{ClangHelper.CreateTransformationContext( + *M, *CU, File, Path, *Compilations)}) + TfmInfo.setContext(*CU, std::move(TfmCtx)); + } + if (TimePassesIsEnabled) { + ASTGeneration.stopTimer(); + LLVMIRAnalysis.startTimer(); + } + QM.run(M.get(), &TfmInfo); + if (TimePassesIsEnabled) + LLVMIRAnalysis.stopTimer(); + } else { + if (TimePassesIsEnabled) + LLVMIRAnalysis.startTimer(); + QM.run(M.get(), nullptr); + if (TimePassesIsEnabled) + LLVMIRAnalysis.stopTimer(); + } + } + return IsOk != Sources.size() ? IsOk != 0 ? 2 : 1 : 0; +} diff --git a/lib/Core/Tool.cpp b/lib/Core/Tool.cpp index 2baee7ce..59a4966e 100644 --- a/lib/Core/Tool.cpp +++ b/lib/Core/Tool.cpp @@ -24,6 +24,7 @@ // //===----------------------------------------------------------------------===// +#include "tsar/Core/IRAction.h" #include "tsar/Core/Query.h" #include "tsar/Core/Passes.h" #include "tsar/Core/Tool.h" @@ -374,12 +375,14 @@ static std::vector addInternalArgs(int Argc, const char **Argv) { } Tool::Tool(int Argc, const char **Argv) { - assert(Argv && "List of command line arguments must not be null!"); + assert(Argv && Argc > 1 && + "List of command line arguments must not be null!"); Options::get(); // At first, initialize command line options. std::string Descr = std::string(TSAR_DESCRIPTION) + "(TSAR)"; // Passes should be initialized previously then command line options are // parsed, due to initialize list of available passes. initializeTSAR(*PassRegistry::getPassRegistry()); + mToolName = Argv[0]; auto Args = addInternalArgs(Argc, Argv); cl::ParseCommandLineOptions(Args.size(), Args.data(), Descr); storeCLOptions(); @@ -761,12 +764,12 @@ int Tool::run(QueryManager *QM) { .get()); if (!ImportInfoStorage) return CTool.run(newActionFactory( - std::forward_as_tuple(mCommandLine, QM), + std::forward_as_tuple(*mCompilations, *QM), std::forward_as_tuple(SourcesToMerge)) .get()); return CTool.run( newActionFactory( - std::forward_as_tuple(mCommandLine, QM), + std::forward_as_tuple(*mCompilations, *QM), std::forward_as_tuple(SourcesToMerge, ImportInfoStorage)) .get()); } @@ -779,12 +782,10 @@ int Tool::run(QueryManager *QM) { return CTool.run( newActionFactory() .get()); - // Do not search pragmas in .ll file to avoid internal assertion fails. - ClangTool CLLTool(*mCompilations, LLSources); - return - CTool.run(newActionFactory( - std::forward_as_tuple(mCommandLine, QM)).get()) || - CLLTool.run(newActionFactory( - std::forward_as_tuple(mCommandLine, QM, mLoadSources)).get()) ? - 1 : 0; + auto CRes{CTool.run(newActionFactory( + std::forward_as_tuple(*mCompilations, *QM)) + .get())}; + auto LLRes{executeIRAction(mToolName, LLSources, *QM, + mLoadSources ? mCompilations.get() : nullptr)}; + return (CRes != 0 && LLRes != 0) ? (CRes > 1 || LLRes > 1) ? 2 : 1 : 0; } diff --git a/lib/Frontend/Clang/Action.cpp b/lib/Frontend/Clang/Action.cpp index 197a4c8e..a2c4d28a 100644 --- a/lib/Frontend/Clang/Action.cpp +++ b/lib/Frontend/Clang/Action.cpp @@ -24,41 +24,19 @@ //===----------------------------------------------------------------------===// #include "tsar/Frontend/Clang/Action.h" -#include "tsar/Frontend/Clang/FrontendActions.h" -#include "tsar/Frontend/Clang/TransformationContext.h" #include "tsar/Core/Query.h" -#include "tsar/Core/TransformationContext.h" -#include "tsar/Core/tsar-config.h" -#include "tsar/Support/MetadataUtils.h" -#include "tsar/Support/SMStringSocket.h" -#include -#include -#include +#include "tsar/Frontend/Clang/TransformationContext.h" #include #include #include #include -#include -#include -#include -#include -#ifdef FLANG_FOUND -# include "tsar/Frontend/Flang/TransformationContext.h" -# include -# include -#endif #include #include #include -#include -#include -#include -#include #include #include using namespace clang; -using namespace clang::tooling; using namespace llvm; using namespace tsar; @@ -69,7 +47,7 @@ class AnalysisConsumer : public ASTConsumer { public: /// Constructor. AnalysisConsumer(CompilerInstance &CI, StringRef InFile, - TransformationInfo *TfmInfo, QueryManager &QM) + TransformationInfo &TfmInfo, QueryManager &QM) : mLLVMIRGeneration( "mLLVMIRGeneration", "LLVM IR Generation Time" @@ -78,7 +56,7 @@ class AnalysisConsumer : public ASTConsumer { mGen(CreateLLVMCodeGen(CI.getDiagnostics(), InFile, CI.getHeaderSearchOpts(), CI.getPreprocessorOpts(), CI.getCodeGenOpts(), *mLLVMContext)), - mTransformInfo(TfmInfo), mQueryManager(&QM) { + mTransformInfo(&TfmInfo), mQueryManager(&QM) { } void HandleCXXStaticMemberVarInstantiation(VarDecl *VD) override { @@ -149,14 +127,12 @@ class AnalysisConsumer : public ASTConsumer { "LLVM IR Analysis Time"); if (llvm::TimePassesIsEnabled) LLVMIRAnalysis.startTimer(); - if (mTransformInfo) { - auto CUs = M->getNamedMetadata("llvm.dbg.cu"); - if (CUs->getNumOperands() == 1) { - auto *CU = cast(*CUs->op_begin()); - IntrusiveRefCntPtr TfmCtx{ - new ClangTransformationContext{*mCI, ASTCtx, *mGen}}; - mTransformInfo->setContext(*CU, std::move(TfmCtx)); - } + auto CUs = M->getNamedMetadata("llvm.dbg.cu"); + if (CUs->getNumOperands() == 1) { + auto *CU = cast(*CUs->op_begin()); + IntrusiveRefCntPtr TfmCtx{ + new ClangTransformationContext{*mCI, ASTCtx, *mGen}}; + mTransformInfo->setContext(*CU, std::move(TfmCtx)); } mQueryManager->run(M, mTransformInfo); if (llvm::TimePassesIsEnabled) @@ -199,303 +175,15 @@ class AnalysisConsumer : public ASTConsumer { bool MainAction::BeginSourceFileAction(CompilerInstance &CI) { TimePassesIsEnabled = CI.getCodeGenOpts().TimePasses; - return mQueryManager->beginSourceFile(CI, getCurrentFile()); + return mQueryManager.beginSourceFile(CI, getCurrentFile()); } void MainAction::EndSourceFileAction() { - mQueryManager->endSourceFile(); -} - -namespace tsar::detail { -JSON_OBJECT_BEGIN(SourceResponse) -JSON_OBJECT_ROOT_PAIR(SourceResponse, Context, - tsar::TransformationContextBase *) -SourceResponse() : JSON_INIT_ROOT {} -JSON_OBJECT_END(SourceResponse) -} // namespace tsar::detail - -using namespace tsar::detail; - -namespace { -class ASTSocket final : public SMStringSocketBase { -public: - void processResponse(const std::string &Response) const { - llvm::StringRef Json{Response.data() + 1, Response.size() - 2}; - ::json::Parser Parser(Json.str()); - SourceResponse R; - if (!Parser.parse(R)) - mTfmCtx = nullptr; - else - mTfmCtx = R[SourceResponse::Context]; - } - - auto getContext() { - for (auto &Callback : mReceiveCallbacks) - Callback({Data, Delimiter}); - // Note, that callback run send() in client, so mAnalysisPass is already - // set here. - assert(mResponseKind == Data && "Unknown response: wait for data!"); - return IntrusiveRefCntPtr(mTfmCtx); - } - -private: - mutable TransformationContextBase *mTfmCtx{nullptr}; -}; - -class SourceQueryManager : public QueryManager { -public: - explicit SourceQueryManager(bcl::IntrusiveConnection *C) : mConnection(C) { - assert(C && "Connection must not be null!"); - } - void run(llvm::Module *M, TransformationInfo *TfmInfo) override { - bool WaitForRequest{true}; - while (WaitForRequest && - mConnection->answer([&WaitForRequest, M, - TfmInfo](std::string &Request) -> std::string { - if (Request == ASTSocket::Release) { - WaitForRequest = false; - return {ASTSocket::Notify}; - } else if (Request == ASTSocket::Data) { - auto CUs{M->getNamedMetadata("llvm.dbg.cu")}; - assert(CUs && "DICompileUnit metadata must exist!"); - auto I{find_if(CUs->operands(), - [](auto *Op) { return isa(Op); })}; - assert(I != CUs->operands().end() && - "DICompileUnit metadata must exist!"); - SourceResponse Response; - Response[SourceResponse::Context] = - TfmInfo->getContext(*cast(*I)); - return ASTSocket::Data + - ::json::Parser::unparseAsObject(Response); - } else { - return {ASTSocket::Invalid}; - } - })) - ; - } - -private: - bcl::IntrusiveConnection *mConnection; -}; - -template struct ActionHelper { - IntrusiveRefCntPtr CreateTransformationContext( - [[maybe_unused]] const llvm::Module &M, - [[maybe_unused]] const DICompileUnit &CU, - [[maybe_unused]] StringRef IRSource, [[maybe_unused]] StringRef Path, - [[maybe_unused]] ArrayRef CommandLine) { - return nullptr; - } -}; - -template<> -struct ActionHelper { - ~ActionHelper() { - for (auto &S : mSockets) - S->release(); - } - - IntrusiveRefCntPtr - CreateTransformationContext([[maybe_unused]] const llvm::Module &M, - [[maybe_unused]] const DICompileUnit &CU, - [[maybe_unused]] StringRef IRSource, - StringRef Path, - ArrayRef CommandLine) { - mSockets.push_back(std::make_unique()); - bcl::IntrusiveConnection::connect( - mSockets.back().get(), ASTSocket::Delimiter, - [this, &CommandLine, &Path](bcl::IntrusiveConnection C) { - auto Compilations{std::unique_ptr( - new FixedCompilationDatabase(".", CommandLine))}; - ClangTool CTool(*Compilations, makeArrayRef(Path.str())); - SourceQueryManager SQM{&C}; - QueryManager *QM{&SQM}; - CTool.run(newActionFactory( - std::forward_as_tuple(CommandLine, QM)) - .get()); - }); - return mSockets.back()->getContext(); - } - -private: - std::vector> mSockets; -}; - -#ifdef FLANG_FOUND -template<> -struct ActionHelper { - Fortran::common::IntrinsicTypeDefaultKinds DefaultKinds; - - IntrusiveRefCntPtr - CreateTransformationContext(const llvm::Module &M, const DICompileUnit &CU, - StringRef IRSource, StringRef Path, ArrayRef CommandLine) { - Fortran::parser::Options Options; - Options.predefinitions.emplace_back("__F18", "1"); - Options.predefinitions.emplace_back("__F18_MAJOR__", "1"); - Options.predefinitions.emplace_back("__F18_MINOR__", "1"); - Options.predefinitions.emplace_back("__F18_PATCHLEVEL__", "1"); - Options.features.Enable( - Fortran::common::LanguageFeature::BackslashEscapes, true); - auto Extension = sys::path::extension(Path); - Options.isFixedForm = - (Extension == ".f" || Extension == ".F" || Extension == ".ff"); - Options.searchDirectories.emplace_back("."s); - Options.needProvenanceRangeToCharBlockMappings = true; - IntrusiveRefCntPtr TfmCtx{ - new FlangTransformationContext{Options, DefaultKinds}}; - auto &Parsing{cast(TfmCtx)->getParsing()}; - Parsing.Prescan(std::string{Path}, - cast(TfmCtx)->getOptions()); - if (!Parsing.messages().empty() && - Parsing.messages().AnyFatalError()) { - Parsing.messages().Emit(errs(), Parsing.allCooked()); - errs() << IRSource << " could not scan " << Path << '\n'; - return nullptr; - } - Parsing.Parse(outs()); - Parsing.ClearLog(); - Parsing.messages().Emit(errs(), Parsing.allCooked()); - if (!Parsing.consumedWholeFile()) { - Parsing.EmitMessage(errs(), Parsing.finalRestingPlace(), - "parser FAIL (final position)"); - return nullptr; - } - if (!Parsing.messages().empty() && - Parsing.messages().AnyFatalError() || !Parsing.parseTree()) { - errs() << IRSource << " could not parse " << Path << '\n'; - return nullptr; - } - auto &ParseTree{ *Parsing.parseTree() }; - Fortran::semantics::Semantics Semantics{ - cast(TfmCtx)->getContext(), ParseTree, - false}; - Semantics.Perform(); - Semantics.EmitMessages(llvm::errs()); - if (Semantics.AnyFatalError()) { - errs() << IRSource << " semantic errors in " << Path << '\n'; - return nullptr; - } - cast(TfmCtx)->initialize(M, CU); - return TfmCtx; - } -}; -#endif -} - -namespace json { -template <> -struct CellTraits { - using CellKey = tsar::detail::json_::SourceResponseImpl::Context; - using ValueType = CellKey::ValueType; - inline static bool parse(ValueType &Dest, Lexer &Lex) - noexcept( - noexcept(Traits::parse(Dest, Lex))) { - uintptr_t RawDest; - auto Res = Traits::parse(RawDest, Lex); - if (Res) - Dest = reinterpret_cast(RawDest); - return Res; - } - inline static void unparse(String &JSON, const ValueType &Obj) - noexcept( - noexcept(Traits::unparse(JSON, Obj))) { - Traits::unparse(JSON, reinterpret_cast(Obj)); - } - inline static typename std::result_of< - decltype(&CellKey::name)()>::type name() - noexcept(noexcept(CellKey::name())) { - return CellKey::name(); - } -}; -} - -JSON_DEFAULT_TRAITS(::, SourceResponse) - -void MainAction::ExecuteAction() { - // If this is an IR file, we have to treat it specially. - if (getCurrentFileKind().getLanguage() != Language::LLVM_IR) { - ASTFrontendAction::ExecuteAction(); - return; - } - if (!hasIRSupport()) { - errs() << getCurrentFile() << " error: requested action is not available\n"; - return; - } - bool Invalid; - CompilerInstance &CI = getCompilerInstance(); - SourceManager &SM = CI.getSourceManager(); - FileID FID = SM.getMainFileID(); - auto MainFile = SM.getBufferOrNone(FID); - if (!MainFile) - return; - llvm::SMDiagnostic Err; - LLVMContext Ctx; - std::unique_ptr M = parseIR(*MainFile, Err, Ctx); - if (!M) { - // Translate from the diagnostic info to the SourceManager location if - // available. - SourceLocation Loc; - if (Err.getLineNo() > 0) { - assert(Err.getColumnNo() >= 0); - Loc = SM.translateFileLineCol(SM.getFileEntryForID(FID), - Err.getLineNo(), Err.getColumnNo() + 1); - } - // Strip off a leading diagnostic code if there is one. - StringRef Msg = Err.getMessage(); - if (Msg.startswith("error: ")) - Msg = Msg.substr(7); - unsigned DiagID = - CI.getDiagnostics().getCustomDiagID(DiagnosticsEngine::Error, "%0"); - CI.getDiagnostics().Report(Loc, DiagID) << Msg; - return; - } - const auto &TargetOpts = CI.getTargetOpts(); - if (M->getTargetTriple() != TargetOpts.Triple) { - CI.getDiagnostics().Report(SourceLocation(), - diag::warn_fe_override_module) - << TargetOpts.Triple; - M->setTargetTriple(TargetOpts.Triple); - } - Timer LLVMIRAnalysis( -#if LLVM_VERSION_MAJOR > 3 - "LLVMIRAnalysis", -#endif - "LLVM IR Analysis Time"); - if (llvm::TimePassesIsEnabled) - LLVMIRAnalysis.startTimer(); - ActionHelper ClangHelper; - ActionHelper FlangHelper; - if (mTfmInfo) { - auto CUs = M->getNamedMetadata("llvm.dbg.cu"); - for (auto *Op : CUs->operands()) - if (auto *CU = dyn_cast(Op)) { - SmallString<128> Path{CU->getFilename()}; - sys::fs::make_absolute(CU->getDirectory(), Path); - if (isFortran(CU->getSourceLanguage())) { - if (auto TfmCtx = FlangHelper.CreateTransformationContext( - *M, *CU, getCurrentFile(), Path, mTfmInfo->getCommandLine())) - mTfmInfo->setContext(*CU, std::move(TfmCtx)); - } else if (isC(CU->getSourceLanguage()) || - isCXX(CU->getSourceLanguage())) - if (auto TfmCtx = ClangHelper.CreateTransformationContext( - *M, *CU, getCurrentFile(), Path, mTfmInfo->getCommandLine())) - mTfmInfo->setContext(*CU, std::move(TfmCtx)); - } - } - mQueryManager->run(M.get(), mTfmInfo.get()); - if (llvm::TimePassesIsEnabled) - LLVMIRAnalysis.stopTimer(); -} - -std::unique_ptr -MainAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { - return std::make_unique(CI, InFile, mTfmInfo.get(), - *mQueryManager); + mQueryManager.endSourceFile(); } -MainAction::MainAction(ArrayRef CL, QueryManager *QM, - bool LoadSources) - : mQueryManager(QM), - mTfmInfo(LoadSources ? new TransformationInfo(CL) : nullptr) { - assert(QM && "Query manager must not be null!"); +std::unique_ptr MainAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + return std::make_unique(CI, InFile, mTfmInfo, + mQueryManager); } From 5f5cd7aca69f80859ff4a68ab9a24039d193e944 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Wed, 4 May 2022 23:16:44 +0300 Subject: [PATCH 32/93] [TSAR, Core, Frontend] Integration with Flang, implement in a way similar to Clang integration. --- CMakeLists.txt | 17 +- include/tsar/Core/IRAction.h | 4 + include/tsar/Core/Query.h | 27 +-- include/tsar/Frontend/ActionFactory.h | 80 ++++++++ include/tsar/Frontend/Clang/Action.h | 65 ++----- include/tsar/Frontend/Flang/Action.h | 92 +++++++++ include/tsar/Frontend/Flang/Tooling.h | 75 ++++++++ .../Frontend/Flang/TransformationContext.h | 64 ++++--- include/tsar/Support/Clang/Utils.h | 14 ++ include/tsar/Support/OutputFile.h | 100 ++++++++++ include/tsar/Support/Utils.h | 7 +- lib/Analysis/Memory/Utils.cpp | 2 +- lib/Core/IRAction.cpp | 81 +++----- lib/Core/Query.cpp | 28 ++- lib/Core/Tool.cpp | 123 ++++++++---- lib/Frontend/Clang/Action.cpp | 16 +- lib/Frontend/Flang/Action.cpp | 129 +++++++++++++ lib/Frontend/Flang/CMakeLists.txt | 2 +- lib/Frontend/Flang/Tooling.cpp | 179 ++++++++++++++++++ lib/Frontend/Flang/TransformationContext.cpp | 17 +- lib/Support/Clang/Utils.cpp | 28 +++ lib/Support/Utils.cpp | 136 +++++++++++++ 22 files changed, 1078 insertions(+), 208 deletions(-) create mode 100644 include/tsar/Frontend/ActionFactory.h create mode 100644 include/tsar/Frontend/Flang/Action.h create mode 100644 include/tsar/Frontend/Flang/Tooling.h create mode 100644 include/tsar/Support/OutputFile.h create mode 100644 lib/Frontend/Flang/Action.cpp create mode 100644 lib/Frontend/Flang/Tooling.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index f8b9ade8..9498551c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -151,7 +151,7 @@ else() message(STATUS ${LLVM_STATUS}) set(LLVM_PROJECTS clang) if (BUILD_FLANG) - set(LLVM_PROJECTS ${LLVM_PROJECTS} flang) + set(LLVM_PROJECTS ${LLVM_PROJECTS} flang mlir) endif() if (BUILD_PROFILE) set(LLVM_PROJECTS ${LLVM_PROJECTS} compiler-rt) @@ -226,7 +226,20 @@ set(CLANG_LIBS if(FLANG_FOUND) set(FLANG_LIBS - FortranSemantics FortranEvaluate FortranParser FortranDecimal FortranCommon) + flangFrontend FortranLower FortranSemantics FortranEvaluate FortranParser + FortranDecimal FortranCommon FIRCodeGen FIRTransforms FIRBuilder + FIRDialect FIRSupport MLIROpenMPToLLVM MLIROpenMPToLLVMIRTranslation + MLIRLLVMToLLVMIRTranslation MLIROpenACC MLIROpenMP MLIRSCFToControlFlow + MLIRFuncToLLVM MLIRArithmeticToLLVM MLIRControlFlowToLLVM + MLIRTargetLLVMIRExport MLIRDLTI MLIRLLVMIRTransforms MLIRMemRefToLLVM + MLIRParser MLIRSCF MLIRBufferization MLIRAffine MLIRVector MLIRMemRef + MLIRVectorInterfaces MLIRLLVMCommonConversion MLIRTransforms MLIRLLVMIR + MLIRFunc MLIRControlFlow MLIRTensor MLIRArithmeticUtils MLIRComplex + MLIRArithmetic MLIRDialectUtils MLIRTransformUtils MLIRRewrite + MLIRPDLToPDLInterp MLIRPDLInterp MLIRPDL MLIRPass MLIRAnalysis + MLIRCallInterfaces MLIRControlFlowInterfaces MLIRInferTypeOpInterface + MLIRSideEffectInterfaces MLIRDataLayoutInterfaces MLIRViewLikeInterface + MLIRIR MLIRSupport) include(TestBigEndian) test_big_endian(IS_BIGENDIAN) if (IS_BIGENDIAN) diff --git a/include/tsar/Core/IRAction.h b/include/tsar/Core/IRAction.h index d487c1ed..f895044e 100644 --- a/include/tsar/Core/IRAction.h +++ b/include/tsar/Core/IRAction.h @@ -24,6 +24,9 @@ // //===----------------------------------------------------------------------===// +#ifndef TSAR_IR_ACTION_H +#define TSAR_IR_ACTION_H + #include #include #include @@ -42,3 +45,4 @@ int executeIRAction( QueryManager &QM, const clang::tooling::CompilationDatabase *Compilations = nullptr); } +#endif//TSAR_IR_ACTION_H diff --git a/include/tsar/Core/Query.h b/include/tsar/Core/Query.h index e4a6f74d..bb35f40a 100644 --- a/include/tsar/Core/Query.h +++ b/include/tsar/Core/Query.h @@ -27,6 +27,7 @@ #define TSAR_QUERY_H #include "tsar/Frontend/Clang/ASTImportInfo.h" +#include "tsar/Support/OutputFile.h" #include "tsar/Support/PassGroupRegistry.h" #include #include @@ -45,8 +46,7 @@ class PassManager; } namespace clang { -class CompilerInstance; -class CodeGenOptions; +class DiagnosticsEngine; } namespace tsar { @@ -110,7 +110,10 @@ class QueryManager { /// \brief Callback at the start of processing a single input. /// /// \return True on success, on failure run() passes will not be called. - virtual bool beginSourceFile(clang::CompilerInstance &, llvm::StringRef) { + virtual bool beginSourceFile(clang::DiagnosticsEngine &Diags, + llvm::StringRef InputFile, + llvm::StringRef OutputFile, + llvm::StringRef WorkingDir) { return true; } @@ -118,7 +121,7 @@ class QueryManager { /// /// This is guaranteed to only be called following a successful call to /// beginSourceFile(). - virtual void endSourceFile() {} + virtual void endSourceFile(bool HasErrorOccurred) {} /// Analysis the specified module and transforms source file associated with /// it if rewriter context is specified. @@ -253,19 +256,17 @@ class DefaultQueryManager: public QueryManager { /// This prints LLVM IR to the standard output stream. class EmitLLVMQueryManager : public QueryManager { public: - bool beginSourceFile( - clang::CompilerInstance &CI, llvm::StringRef InFile) override; + bool beginSourceFile(clang::DiagnosticsEngine &Diags, + llvm::StringRef InputFile, llvm::StringRef OutputFIle, + llvm::StringRef WorkingDir) override; void run(llvm::Module *M, tsar::TransformationInfo *) override; - void endSourceFile() override { - // An output stream attached to a temporary output file should be freed. - // Otherwise it prevents renaming a temporary output file to a regular one. - mOS.reset(); - } + void endSourceFile(bool HasErrorOccurred) override; protected: - std::unique_ptr mOS; - const clang::CodeGenOptions *mCodeGenOpts = nullptr; + llvm::Optional mOutputFile; + std::string mWorkingDir; + clang::DiagnosticsEngine *mDiags{nullptr}; }; /// This performs instrumentation of LLVM IR and prints it to the standard diff --git a/include/tsar/Frontend/ActionFactory.h b/include/tsar/Frontend/ActionFactory.h new file mode 100644 index 00000000..fcf9e030 --- /dev/null +++ b/include/tsar/Frontend/ActionFactory.h @@ -0,0 +1,80 @@ +//===- ActionFactory.h --- TSAR Frontend Action (Clang) ---------*- C++ -*-===// +// +// Traits Static Analyzer (SAPFOR) +// +// Copyright 2022 DVM System Group +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +// +// This file proposes general interface to build frontend actions. +// +//===----------------------------------------------------------------------===// + +#ifndef TSAR_ACTION_FACTORY_H +#define TSAR_ACTION_FACTORY_H + +#include +#include + +namespace tsar { +/// Creates an analysis/transformations actions factory. +template +std::unique_ptr newActionFactory(std::tuple Args) { + class ActionFactory : public FactoryBaseT { + public: + explicit ActionFactory(std::tuple Args) : mArgs{std::move(Args)} {} + std::unique_ptr create() override { + return std::unique_ptr( + bcl::make_unique_piecewise(mArgs).release()); + } + + private: + std::tuple mArgs; + }; + return std::unique_ptr(new ActionFactory(std::move(Args))); +} + +/// Creates an analysis/transformations actions factory with adaptor. +template +std::unique_ptr +newActionFactory(std::tuple ActionArgs = {}, + std::tuple AdaptorArgs = {}) { + class ActionFactory : public FactoryBaseT { + public: + ActionFactory(std::tuple ActionArgs, + std::tuple AdaptorArgs) + : mActionArgs{std::move(ActionArgs)}, mAdaptorArgs{ + std::move(AdaptorArgs)} {} + std::unique_ptr create() override { + std::unique_ptr Action{ + bcl::make_unique_piecewise(mActionArgs).release()}; + return std::unique_ptr( + bcl::make_unique_piecewise( + std::tuple_cat(std::forward_as_tuple(std::move(Action)), + mAdaptorArgs)) + .release()); + } + + private: + std::tuple mActionArgs; + std::tuple mAdaptorArgs; + }; + return std::unique_ptr( + new ActionFactory(std::move(ActionArgs), std::move(AdaptorArgs))); +} +} // namespace tsar +#endif//TSAR_ACTION_FACTORY_H diff --git a/include/tsar/Frontend/Clang/Action.h b/include/tsar/Frontend/Clang/Action.h index c8c2ecb1..67d4bc6b 100644 --- a/include/tsar/Frontend/Clang/Action.h +++ b/include/tsar/Frontend/Clang/Action.h @@ -1,4 +1,4 @@ -//===--- Action.h ----------- TSAR Frontend Action --------------*- C++ -*-===// +//===--- Action.h ------- TSAR Frontend Action (Clang) ----------*- C++ -*-===// // // Traits Static Analyzer (SAPFOR) // @@ -23,24 +23,21 @@ // //===----------------------------------------------------------------------===// -#ifndef TSAR_ACTION_H -#define TSAR_ACTION_H +#ifndef TSAR_CLANG_ACTION_H +#define TSAR_CLANG_ACTION_H +#include "tsar/Frontend/ActionFactory.h" #include "tsar/Core/TransformationContext.h" -#include #include -#include -#include namespace tsar { -class TransformationInfo; class QueryManager; /// Base front-end action to analyze and transform sources. -class MainAction : public clang::ASTFrontendAction { +class ClangMainAction : public clang::ASTFrontendAction { public: - MainAction(const clang::tooling::CompilationDatabase &Compilations, - QueryManager &QM) + ClangMainAction(const clang::tooling::CompilationDatabase &Compilations, + QueryManager &QM) : mTfmInfo(Compilations), mQueryManager(QM) {} /// Callback at the start of processing a single input. @@ -53,7 +50,7 @@ class MainAction : public clang::ASTFrontendAction { /// /// This is guaranteed to only be called following a successful call to /// BeginSourceFileAction (and BeginSourceFile). - void EndSourceFileAction() override; + bool shouldEraseOutputFiles() override; /// Create AST Consumer. std::unique_ptr CreateASTConsumer( @@ -68,49 +65,19 @@ class MainAction : public clang::ASTFrontendAction { template std::unique_ptr newActionFactory(std::tuple Args) { - class ActionFactory : public clang::tooling::FrontendActionFactory { - public: - explicit ActionFactory(std::tuple Args) : mArgs{std::move(Args)} {} - std::unique_ptr create() override { - return std::unique_ptr( - bcl::make_unique_piecewise(mArgs).release()); - } - - private: - std::tuple mArgs; - }; - return std::unique_ptr( - new ActionFactory(std::move(Args))); + return newActionFactory(std::move(Args)); } /// Creates an analysis/transformations actions factory with adaptor. template std::unique_ptr -newActionFactory(std::tuple ActionArgs = {}, - std::tuple AdaptorArgs = {}) { - class ActionFactory : public clang::tooling::FrontendActionFactory { - public: - ActionFactory(std::tuple ActionArgs, - std::tuple AdaptorArgs) - : mActionArgs{std::move(ActionArgs)} - , mAdaptorArgs{std::move(AdaptorArgs)} {} - std::unique_ptr create() override { - std::unique_ptr Action{ - bcl::make_unique_piecewise(mActionArgs).release()}; - return std::unique_ptr( - bcl::make_unique_piecewise( - std::tuple_cat(std::forward_as_tuple(std::move(Action)), - mAdaptorArgs)) - .release()); - } - - private: - std::tuple mActionArgs; - std::tuple mAdaptorArgs; - }; - return std::unique_ptr( - new ActionFactory(std::move(ActionArgs), std::move(AdaptorArgs))); +newClangActionFactory(std::tuple ActionArgs = {}, + std::tuple AdaptorArgs = {}) { + return newActionFactory( + std::move(ActionArgs), std::move(AdaptorArgs)); } } -#endif//TSAR_ACTION_H +#endif//TSAR_CLANG_ACTION_H diff --git a/include/tsar/Frontend/Flang/Action.h b/include/tsar/Frontend/Flang/Action.h new file mode 100644 index 00000000..d6ebe6e8 --- /dev/null +++ b/include/tsar/Frontend/Flang/Action.h @@ -0,0 +1,92 @@ +//===- Action.h -------- TSAR Frontend Action (Flang) ------------*- C++ -*===// +// +// Traits Static Analyzer (SAPFOR) +// +// Copyright 2022 DVM System Group +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +// +// This file contains front-end actions which are necessary to analyze and +// transform sources. +// +//===----------------------------------------------------------------------===// + +#ifndef TSAR_FLANG_ACTION_H +#define TSAR_FLANG_ACTION_H + +#include "tsar/Core/TransformationContext.h" +#include "tsar/Frontend/ActionFactory.h" +#include +#include +#include + +namespace tsar { +class QueryManager; + +class FlangFrontendAction : public Fortran::frontend::CodeGenAction { +public: + FlangFrontendAction() + : Fortran::frontend::CodeGenAction( + Fortran::frontend::BackendActionTy::Backend_EmitLL) {} + bool hasWorkingDir() const { return !mWorkingDir.empty(); } + llvm::StringRef getWorkingDir() const { return mWorkingDir; } + void setWorkingDir(llvm::StringRef Path) { mWorkingDir = Path; } + +private: + std::string mWorkingDir; +}; + +class FlangMainAction : public FlangFrontendAction { +public: + FlangMainAction(const clang::tooling::CompilationDatabase &Compilations, + QueryManager &QM) + : mTfmInfo(Compilations), mQueryManager(QM) {} + + void executeAction() override; + bool shouldEraseOutputFiles() override; + bool beginSourceFileAction() override; + +private: + TransformationInfo mTfmInfo; + QueryManager &mQueryManager; +}; + +class FlangFrontendActionFactory { +public: + virtual ~FlangFrontendActionFactory() = default; + virtual std::unique_ptr create() = 0; +}; + +/// Creates an analysis/transformations actions factory. +template +std::unique_ptr +newFlangActionFactory(std::tuple Args) { + return newActionFactory( + std::move(Args)); +} + +/// Creates an analysis/transformations actions factory with adaptor. +template +std::unique_ptr +newFlangActionFactory(std::tuple ActionArgs = {}, + std::tuple AdaptorArgs = {}) { + return newActionFactory( + std::move(ActionArgs), std::move(AdaptorArgs)); +} +} // namespace tsar +#endif // TSAR_FLANG_ACTION_H diff --git a/include/tsar/Frontend/Flang/Tooling.h b/include/tsar/Frontend/Flang/Tooling.h new file mode 100644 index 00000000..056fcaaa --- /dev/null +++ b/include/tsar/Frontend/Flang/Tooling.h @@ -0,0 +1,75 @@ +//===- Tooling.h ----------- Flang Based Tool (Flang) ------------*- C++ -*===// +// +// Traits Static Analyzer (SAPFOR) +// +// Copyright 2022 DVM System Group +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +// +// This file implements functions to run flang tools standalone instead of +// running them as a plugin. +// +//===----------------------------------------------------------------------===// + +#ifndef TSAR_FLANG_TOOLING_H +#define TSAR_FLANG_TOOLING_H + +#include +#include +#include +#include +#include +#include +#include + +namespace clang::tooling { +class CompilationDatabase; +} + +namespace tsar { +class FlangFrontendActionFactory; + +class FlangTool { +public: + FlangTool(const clang::tooling::CompilationDatabase &Compilations, + llvm::ArrayRef SourcePaths, + llvm::IntrusiveRefCntPtr BaseFS = + llvm::vfs::getRealFileSystem()) + : mCompilations(Compilations), mSourcePaths(SourcePaths), + mOverlayFileSystem( + new llvm::vfs::OverlayFileSystem(std::move(BaseFS))) { + appendArgumentsAdjuster(clang::tooling::getClangStripOutputAdjuster()); + } + + ~FlangTool() = default; + + int run(FlangFrontendActionFactory *Factory); + + void setRestoreWorkingDir(bool RestoreCWD) noexcept { + mRestoreCWD = RestoreCWD; + } + + void appendArgumentsAdjuster(clang::tooling::ArgumentsAdjuster Adjuster); + void clearArgumentsAdjusters() { mArgsAdjuster = nullptr; } + +private: + const clang::tooling::CompilationDatabase &mCompilations; + std::vector mSourcePaths; + llvm::IntrusiveRefCntPtr mOverlayFileSystem; + bool mRestoreCWD{true}; + clang::tooling::ArgumentsAdjuster mArgsAdjuster; +}; +} // namespace tsar +#endif//TSAR_FLANG_TOOLING_H diff --git a/include/tsar/Frontend/Flang/TransformationContext.h b/include/tsar/Frontend/Flang/TransformationContext.h index 1ee195f5..0f0d08c9 100644 --- a/include/tsar/Frontend/Flang/TransformationContext.h +++ b/include/tsar/Frontend/Flang/TransformationContext.h @@ -39,41 +39,58 @@ class DICompileUnit; namespace tsar { class FlangTransformationContext : public TransformationContextBase { using MangledToSourceMapT = llvm::StringMap; + public: static bool classof(const TransformationContextBase *Ctx) noexcept { return Ctx->getKind() == TC_Flang; } - FlangTransformationContext(const Fortran::parser::Options &Opts, - const Fortran::common::IntrinsicTypeDefaultKinds &DefaultKinds) - : TransformationContextBase(TC_Flang) - , mOptions(Opts) - , mContext(DefaultKinds, Opts.features, mAllCookedSources) {} - - void initialize(const llvm::Module &M, const llvm::DICompileUnit &CU); + FlangTransformationContext(Fortran::parser::Parsing &Parsing, + Fortran::parser::Options &Options, + Fortran::semantics::SemanticsContext &Context, + const llvm::Module &M, + const llvm::DICompileUnit &CU) + : TransformationContextBase(TC_Flang), mParsing(&Parsing), + mOptions(&Options), mContext(&Context) { + initialize(M, CU); + } bool hasInstance() const override { auto *This{const_cast(this)}; - return This->mParsing.parseTree().has_value() && - !This->mParsing.messages().AnyFatalError() && - !This->mContext.AnyFatalError() && - mRewriter; + return This->mParsing && This->mParsing->parseTree().has_value() && + !This->mParsing->messages().AnyFatalError() && This->mOptions && + This->mContext && !This->mContext->AnyFatalError(); } bool hasModification() const override { return hasInstance() && mRewriter->hasModification(); } - std::pair release( - const FilenameAdjuster &FA = getDumpFilenameAdjuster()) override; + std::pair + release(const FilenameAdjuster &FA = getDumpFilenameAdjuster()) override; - auto &getParsing() noexcept { return mParsing; } - const auto &getParsing() const noexcept { return mParsing; } + auto &getParsing() noexcept { + assert(hasInstance() && "Transformation context must be configured!"); + return *mParsing; + } + const auto &getParsing() const noexcept { + assert(hasInstance() && "Transformation context must be configured!"); + return *mParsing; + } - const auto &getOptions() const noexcept { return mOptions; } + const auto &getOptions() const noexcept { + assert(hasInstance() && "Transformation context must be configured!"); + return *mOptions; + } - auto &getContext() noexcept { return mContext; } - const auto &getContext() const noexcept { return mContext; } + auto &getContext() noexcept { + assert(hasInstance() && "Transformation context must be configured!"); + return *mContext; + } + const auto &getContext() const noexcept { + assert(hasInstance() && "Transformation context must be configured!"); + return *mContext; + } auto &getRewriter() { assert(hasInstance() && "Transformation context is not configured!"); @@ -95,14 +112,13 @@ class FlangTransformationContext : public TransformationContextBase { } private: - Fortran::parser::AllSources mAllSources; - Fortran::parser::Options mOptions; - Fortran::parser::AllCookedSources mAllCookedSources{mAllSources}; - Fortran::parser::Parsing mParsing{mAllCookedSources}; - Fortran::semantics::SemanticsContext mContext; + void initialize(const llvm::Module &M, const llvm::DICompileUnit &CU); + + Fortran::parser::Parsing *mParsing{nullptr}; + Fortran::parser::Options *mOptions{nullptr}; + Fortran::semantics::SemanticsContext *mContext{nullptr}; MangledToSourceMapT mGlobals; std::unique_ptr mRewriter{nullptr}; }; } - #endif//TSAR_FLANG_TRANSFORMATION_CONTEXT_H diff --git a/include/tsar/Support/Clang/Utils.h b/include/tsar/Support/Clang/Utils.h index c3150376..931dbe3b 100644 --- a/include/tsar/Support/Clang/Utils.h +++ b/include/tsar/Support/Clang/Utils.h @@ -41,6 +41,7 @@ template class SmallPtrSetImpl; namespace clang { class CFG; +class DiagnosticEngine; class CFGBlock; class FunctionDecl; class LangOptions; @@ -49,6 +50,8 @@ class SourceManager; } namespace tsar { +class OutputFile; + /// Returns kind of Clang token for a specified clause expression // or tok::unknown. clang::tok::TokenKind getTokenKind(ClauseExpr EK) noexcept; @@ -304,5 +307,16 @@ inline bool isSubRange(const clang::SourceManager &SM, /// SmallVector and a StringRef to the SmallVector's data is returned. llvm::StringRef getFunctionName(clang::FunctionDecl &FD, llvm::SmallVectorImpl &Name); + +/// Create a new output file. +/// +/// Emit diagnostic on error and return nullptr. +/// This function is copied from clang::CompilerInstance. +llvm::Optional createDefaultOutputFile( + clang::DiagnosticsEngine &Diags, llvm::StringRef OutputPath = "", + bool Binary = true, llvm::StringRef BaseInput = "", + llvm::StringRef Extension = "", bool RemoveFileOnSignal = true, + bool UseTemporary = true, + bool CreateMissingDirectories = false); } #endif//TSAR_CLANG_UTILS_H diff --git a/include/tsar/Support/OutputFile.h b/include/tsar/Support/OutputFile.h new file mode 100644 index 00000000..55a6559b --- /dev/null +++ b/include/tsar/Support/OutputFile.h @@ -0,0 +1,100 @@ +//===----- OutputFile.h ---------- Output File ------------------*- C++ -*-===// +// +// Traits Static Analyzer (SAPFOR) +// +// Copyright 2018 DVM System Group +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +// +// This file defines classes to write output files. +// +//===----------------------------------------------------------------------===// + +#ifndef TSAR_SUPPORT_OUTPUT_FILE_H +#define TSAR_SUPPORT_OUTPUT_FILE_H + +#include +#include +#include +#include +#include + +namespace tsar { +class OutputFile { +public: + /// Create a new output file. + /// + /// Implementation is copied from clang::CompilerInstance. + static llvm::Expected + create(llvm::StringRef OutputPath, bool Binary = true, + bool RemoveFileOnSignal = true, bool UseTemporary = true, + bool CreateMissingDirectories = true); + + OutputFile(OutputFile &&) = default; + OutputFile &operator=(OutputFile &&) = default; + + OutputFile(const OutputFile &) = delete; + OutputFile &operator=(const OutputFile &) = delete; + + ~OutputFile() { + if (isValid()) + llvm::consumeError(clear("", true)); + } + + /// Finish processing of an output file, keep a temporary file with a given + /// name. + /// + /// If EraseFile is true, attempt to erase a file from disk. + /// Implementation is copied from clang::CompilerInstance. + llvm::Error clear(llvm::StringRef WorkingDir = "", bool EraseFile = false); + + bool isBinary() const noexcept { return mBinary; } + bool isRemoveFileOnSignal() const noexcept { return mRemoveFileOnSignal; } + bool isCreateMssingDirectories() const noexcept { + return mCreateMissingDirectories; + } + bool useTemporary() const { return mTemp.hasValue(); } + llvm::StringRef getFilename() const { return mFilename; } + llvm::raw_pwrite_stream &getStream() { + assert(isValid() && "The file has been already cleared!"); + return *mOS; + } + const llvm::sys::fs::TempFile & getTemporary() const { + assert(useTemporary() && "Temporary file is not used!"); + return *mTemp; + } + + bool isValid() const noexcept { return mOS != nullptr; } + operator bool() const noexcept { return isValid(); } + +private: + OutputFile(llvm::StringRef Filename, bool Binary, bool RemoveFileOnSignal, + bool CreateMissingDirectories, + std::unique_ptr OS, + llvm::Optional Temp) + : mFilename(Filename), mBinary(Binary), + mRemoveFileOnSignal(RemoveFileOnSignal), + mCreateMissingDirectories(CreateMissingDirectories), + mOS(std::move(OS)), mTemp(std::move(Temp)) {} + + bool mBinary{true}; + bool mRemoveFileOnSignal{true}; + bool mCreateMissingDirectories{true}; + llvm::Optional mTemp; + std::string mFilename; + std::unique_ptr mOS; +}; +} +#endif//TSAR_SUPPORT_OUTPUT_FILE_H \ No newline at end of file diff --git a/include/tsar/Support/Utils.h b/include/tsar/Support/Utils.h index 2a45794e..2a73e12b 100644 --- a/include/tsar/Support/Utils.h +++ b/include/tsar/Support/Utils.h @@ -32,6 +32,10 @@ #include #include +namespace llvm::sys::fs { +class TempFile; +} + #if !defined LLVM_RELEASE_BUILD && defined TSAR_ENABLE_LLVM_DUMP /// Use this macro if dump() is called for LLVM objects. Otherwise, link-time /// errors occur if configuration of LLVM build is not Debug. @@ -130,5 +134,6 @@ bool operator!=(const llvm::SmallPtrSet &LHS, const llvm::SmallPtrSet &RHS) { return !(LHS == RHS); } + } -#endif//TSAR_SUPPORT_UTILS_H +#endif // TSAR_SUPPORT_UTILS_H diff --git a/lib/Analysis/Memory/Utils.cpp b/lib/Analysis/Memory/Utils.cpp index cef4fb5a..86f9cacc 100644 --- a/lib/Analysis/Memory/Utils.cpp +++ b/lib/Analysis/Memory/Utils.cpp @@ -34,7 +34,7 @@ #include #include #include -#include > +#include #include using namespace llvm; diff --git a/lib/Core/IRAction.cpp b/lib/Core/IRAction.cpp index 4f1d7d13..8132c57f 100644 --- a/lib/Core/IRAction.cpp +++ b/lib/Core/IRAction.cpp @@ -42,9 +42,9 @@ #include #include #ifdef FLANG_FOUND +# include "tsar/Frontend/Flang/Action.h" +# include "tsar/Frontend/Flang/Tooling.h" # include "tsar/Frontend/Flang/TransformationContext.h" -# include -# include #endif #include @@ -151,7 +151,7 @@ struct ActionHelper { [this, &Compilations, &Path](bcl::IntrusiveConnection C) { tooling::ClangTool CTool(Compilations, makeArrayRef(Path.str())); SourceQueryManager SQM{&C}; - CTool.run(newActionFactory( + CTool.run(newClangActionFactory( std::forward_as_tuple(Compilations, static_cast(SQM))) .get()); @@ -166,60 +166,33 @@ struct ActionHelper { #ifdef FLANG_FOUND template<> struct ActionHelper { - Fortran::common::IntrinsicTypeDefaultKinds DefaultKinds; + ~ActionHelper() { + for (auto &S : mSockets) + S->release(); + } IntrusiveRefCntPtr CreateTransformationContext( - const llvm::Module &M, const DICompileUnit &CU, StringRef IRSource, - StringRef Path, const tooling::CompilationDatabase &Compilations) { - Fortran::parser::Options Options; - Options.predefinitions.emplace_back("__F18", "1"); - Options.predefinitions.emplace_back("__F18_MAJOR__", "1"); - Options.predefinitions.emplace_back("__F18_MINOR__", "1"); - Options.predefinitions.emplace_back("__F18_PATCHLEVEL__", "1"); - Options.features.Enable( - Fortran::common::LanguageFeature::BackslashEscapes, true); - auto Extension = sys::path::extension(Path); - Options.isFixedForm = - (Extension == ".f" || Extension == ".F" || Extension == ".ff"); - Options.searchDirectories.emplace_back("."s); - Options.needProvenanceRangeToCharBlockMappings = true; - IntrusiveRefCntPtr TfmCtx{ - new FlangTransformationContext{Options, DefaultKinds}}; - auto &Parsing{cast(TfmCtx)->getParsing()}; - Parsing.Prescan(std::string{Path}, - cast(TfmCtx)->getOptions()); - if (!Parsing.messages().empty() && - Parsing.messages().AnyFatalError()) { - Parsing.messages().Emit(errs(), Parsing.allCooked()); - errs() << IRSource << " could not scan " << Path << '\n'; - return nullptr; - } - Parsing.Parse(outs()); - Parsing.ClearLog(); - Parsing.messages().Emit(errs(), Parsing.allCooked()); - if (!Parsing.consumedWholeFile()) { - Parsing.EmitMessage(errs(), Parsing.finalRestingPlace(), - "parser FAIL (final position)"); - return nullptr; - } - if (!Parsing.messages().empty() && - Parsing.messages().AnyFatalError() || !Parsing.parseTree()) { - errs() << IRSource << " could not parse " << Path << '\n'; - return nullptr; - } - auto &ParseTree{ *Parsing.parseTree() }; - Fortran::semantics::Semantics Semantics{ - cast(TfmCtx)->getContext(), ParseTree, - false}; - Semantics.Perform(); - Semantics.EmitMessages(llvm::errs()); - if (Semantics.AnyFatalError()) { - errs() << IRSource << " semantic errors in " << Path << '\n'; - return nullptr; - } - cast(TfmCtx)->initialize(M, CU); - return TfmCtx; + [[maybe_unused]] const llvm::Module &M, + [[maybe_unused]] const DICompileUnit &CU, + [[maybe_unused]] StringRef IRSource, StringRef Path, + const tooling::CompilationDatabase &Compilations) { + mSockets.push_back(std::make_unique()); + bcl::IntrusiveConnection::connect( + mSockets.back().get(), ASTSocket::Delimiter, + [this, &Compilations, &Path](bcl::IntrusiveConnection C) { + FlangTool FortranTool(Compilations, makeArrayRef(Path.str())); + SourceQueryManager SQM{&C}; + FortranTool.run( + newFlangActionFactory( + std::forward_as_tuple(Compilations, + static_cast(SQM))) + .get()); + }); + return mSockets.back()->getContext(); } + +private: + std::vector> mSockets; }; #endif } diff --git a/lib/Core/Query.cpp b/lib/Core/Query.cpp index cb3399f7..70d5a872 100644 --- a/lib/Core/Query.cpp +++ b/lib/Core/Query.cpp @@ -36,10 +36,12 @@ #include "tsar/Core/Query.h" #include "tsar/Core/TransformationContext.h" #include "tsar/Support/GlobalOptions.h" +#include "tsar/Support/OutputFile.h" #include "tsar/Support/PassBarrier.h" #include "tsar/Transform/AST/Passes.h" #include "tsar/Transform/IR/Passes.h" #include "tsar/Transform/Mixed/Passes.h" +#include "tsar/Support/Clang/Utils.h" #include #include #include @@ -358,17 +360,29 @@ void DefaultQueryManager::run(llvm::Module *M, TransformationInfo *TfmInfo) { Passes.run(*M); } -bool EmitLLVMQueryManager::beginSourceFile( - CompilerInstance &CI, StringRef InFile) { - mOS = CI.createDefaultOutputFile(false, InFile, "ll"); - mCodeGenOpts = &CI.getCodeGenOpts(); - return mOS && mCodeGenOpts; +bool EmitLLVMQueryManager::beginSourceFile(clang::DiagnosticsEngine &Diags, + StringRef InputFile, + StringRef OutputFile, + StringRef WorkingDir) { + mDiags = &Diags; + mWorkingDir = WorkingDir.str(); + mOutputFile = std::move(createDefaultOutputFile(Diags, OutputFile, false, + InputFile, "ll", true, true)); + return mOutputFile.hasValue(); +} + +void EmitLLVMQueryManager::endSourceFile(bool HasErrorOccurred) { + auto E{mOutputFile->clear(mWorkingDir, HasErrorOccurred)}; + if (E && !HasErrorOccurred && mOutputFile->useTemporary()) + mDiags->Report(diag::err_unable_to_rename_temp) + << mOutputFile->getTemporary().TmpName << mOutputFile->getFilename() + << std::move(E); } void EmitLLVMQueryManager::run(llvm::Module *M, TransformationInfo *) { assert(M && "Module must not be null!"); legacy::PassManager Passes; - Passes.add(createPrintModulePass(*mOS, "", mCodeGenOpts->EmitLLVMUseLists)); + Passes.add(createPrintModulePass(mOutputFile->getStream(), "")); Passes.run(*M); } @@ -390,7 +404,7 @@ void InstrLLVMQueryManager::run(llvm::Module *M, Passes.add(createGlobalsAccessStorage()); Passes.add(createGlobalsAccessCollector()); Passes.add(createInstrumentationPass(mInstrEntry, mInstrStart)); - Passes.add(createPrintModulePass(*mOS, "", mCodeGenOpts->EmitLLVMUseLists)); + Passes.add(createPrintModulePass(mOutputFile->getStream(), "")); Passes.run(*M); } diff --git a/lib/Core/Tool.cpp b/lib/Core/Tool.cpp index 59a4966e..13d1654d 100644 --- a/lib/Core/Tool.cpp +++ b/lib/Core/Tool.cpp @@ -36,6 +36,11 @@ #include #include #include +#ifdef FLANG_FOUND +# include "tsar/Frontend/Flang/Action.h" +# include "tsar/Frontend/Flang/Tooling.h" +# include +#endif #include #include #include @@ -668,27 +673,52 @@ void Tool::storeCLOptions() { } int Tool::run(QueryManager *QM) { - std::vector NoASTSources; - std::vector SourcesToMerge; + std::vector NoASTCSources; + std::vector CSourcesToMerge; std::vector LLSources; - std::vector NoLLSources; - bool IsLLVMSources = false; + std::vector CSources; + [[maybe_unused]] std::vector FortranSources; for (auto &Src : mSources) { - auto InputKind = FrontendOptions::getInputKindForExtension( - sys::path::extension(Src).substr(1)); // ignore first . in extension - if (mLanguage != "ast" && InputKind.getLanguage() == Language::LLVM_IR) + auto Extension{ + sys::path::extension(Src).substr(1)}; // ignore first . in extension + auto InputKind{FrontendOptions::getInputKindForExtension(Extension)}; + if (!mLanguage.empty()) { + if (mLanguage == "ast") { + CSources.push_back(Src); + CSourcesToMerge.push_back(Src); + } else if (mLanguage == "llvm") { + LLSources.push_back(Src); + } else if (mLanguage == "c" || mLanguage == "cxx" || mLanguage == "c++") { + CSources.push_back(Src); + NoASTCSources.push_back(Src); + } else if (mLanguage == "fortran") { + FortranSources.push_back(Src); + } + } else if (InputKind.getLanguage() == clang::Language::C || + InputKind.getLanguage() == clang::Language::CXX) { + CSources.push_back(Src); + if (InputKind.getFormat() == InputKind::Precompiled) + CSourcesToMerge.push_back(Src); + else + NoASTCSources.push_back(Src); + } else if (InputKind.getLanguage() == clang::Language::LLVM_IR) { LLSources.push_back(Src); - else - NoLLSources.push_back(Src); - if (mLanguage != "ast" && InputKind.getFormat() != InputKind::Precompiled) - NoASTSources.push_back(Src); - else - SourcesToMerge.push_back(Src); + } else { +#ifdef FLANG_FOUND + auto FortranKind{ + Fortran::frontend::FrontendOptions::getInputKindForExtension( + Extension)}; + if (FortranKind.getLanguage() == Fortran::frontend::Language::Fortran) + FortranSources.push_back(Src); + else +#endif + errs() << "Skipping " << Src << ". Language is not recognized.\n"; + } } // Evaluation of Clang AST files by this tool leads an error, // so these sources should be excluded. - ClangTool EmitPCHTool(*mCompilations, NoASTSources); - auto ArgumentsAdjuster = [&SourcesToMerge, this]( + ClangTool EmitPCHTool(*mCompilations, NoASTCSources); + auto ArgumentsAdjuster = [&CSourcesToMerge, this]( const CommandLineArguments &CL, StringRef Filename) { CommandLineArguments Adjusted; for (std::size_t I = 0; I < CL.size(); ++I) { @@ -704,22 +734,22 @@ int Tool::run(QueryManager *QM) { SmallString<128> PCHFile = Filename; sys::path::replace_extension(PCHFile, ".ast"); Adjusted.push_back(std::string(PCHFile)); - SourcesToMerge.push_back(std::string(PCHFile)); + CSourcesToMerge.push_back(std::string(PCHFile)); } else { Adjusted.push_back(mOutputFilename); - SourcesToMerge.push_back(mOutputFilename); + CSourcesToMerge.push_back(mOutputFilename); } return Adjusted; }; EmitPCHTool.appendArgumentsAdjuster(ArgumentsAdjuster); if (mEmitAST) { - if (!mOutputFilename.empty() && NoASTSources.size() > 1) { + if (!mOutputFilename.empty() && NoASTCSources.size() > 1) { errs() << "WARNING: The -o (output filename) option is ignored when " "generating multiple output files.\n"; mOutputFilename.clear(); } return EmitPCHTool.run( - newActionFactory().get()); + newClangActionFactory().get()); } if (!mOutputFilename.empty()) errs() << "WARNING: The -o (output filename) option is ignored when " @@ -728,12 +758,12 @@ int Tool::run(QueryManager *QM) { // for EmitPCHTool will be invoked. mOutputFilename.clear(); // Emit Clang AST files for source inputs if inputs should be merged before - // analysis. AST files will be stored in SourcesToMerge collection. + // analysis. AST files will be stored in CSourcesToMerge collection. // If an input file already contains Clang AST it will be pushed into - // the SourcesToMerge collection only. + // the CSourcesToMerge collection only. if (mMergeAST) { EmitPCHTool.run( - newActionFactory().get()); + newClangActionFactory().get()); } if (!QM) { if (mEmitLLVM) @@ -750,42 +780,53 @@ int Tool::run(QueryManager *QM) { } auto ImportInfoStorage = QM->initializeImportInfo(); if (mMergeAST) { - ClangTool CTool(*mCompilations, SourcesToMerge.back()); - SourcesToMerge.pop_back(); + ClangTool CTool(*mCompilations, CSourcesToMerge.back()); + CSourcesToMerge.pop_back(); if (mDumpAST) return CTool.run( - newActionFactory( - std::forward_as_tuple(), std::forward_as_tuple(SourcesToMerge)) + newClangActionFactory( + std::forward_as_tuple(), std::forward_as_tuple(CSourcesToMerge)) .get()); if (mPrintAST) return CTool.run( - newActionFactory( - std::forward_as_tuple(), std::forward_as_tuple(SourcesToMerge)) + newClangActionFactory( + std::forward_as_tuple(), std::forward_as_tuple(CSourcesToMerge)) .get()); if (!ImportInfoStorage) - return CTool.run(newActionFactory( - std::forward_as_tuple(*mCompilations, *QM), - std::forward_as_tuple(SourcesToMerge)) - .get()); + return CTool.run( + newClangActionFactory( + std::forward_as_tuple(*mCompilations, *QM), + std::forward_as_tuple(CSourcesToMerge)) + .get()); return CTool.run( - newActionFactory( + newClangActionFactory( std::forward_as_tuple(*mCompilations, *QM), - std::forward_as_tuple(SourcesToMerge, ImportInfoStorage)) + std::forward_as_tuple(CSourcesToMerge, ImportInfoStorage)) .get()); } - ClangTool CTool(*mCompilations, NoLLSources); + ClangTool CTool(*mCompilations, CSources); if (mDumpAST) return CTool.run( - newActionFactory() + newClangActionFactory() .get()); if (mPrintAST) return CTool.run( - newActionFactory() + newClangActionFactory() .get()); - auto CRes{CTool.run(newActionFactory( - std::forward_as_tuple(*mCompilations, *QM)) - .get())}; + auto CRes{ + CTool.run(newClangActionFactory( + std::forward_as_tuple(*mCompilations, *QM)) + .get())}; + int FortranRes{0}; +#ifdef FLANG_FOUND + FlangTool FortranTool(*mCompilations, FortranSources); + FortranRes = FortranTool.run(newFlangActionFactory( + std::forward_as_tuple(*mCompilations, *QM)) + .get()); +#endif auto LLRes{executeIRAction(mToolName, LLSources, *QM, mLoadSources ? mCompilations.get() : nullptr)}; - return (CRes != 0 && LLRes != 0) ? (CRes > 1 || LLRes > 1) ? 2 : 1 : 0; + return (CRes != 0 || FortranRes != 0 || LLRes != 0) + ? (CRes > 1 || FortranRes > 1 || LLRes > 1) ? 2 : 1 + : 0; } diff --git a/lib/Frontend/Clang/Action.cpp b/lib/Frontend/Clang/Action.cpp index a2c4d28a..1a6fb108 100644 --- a/lib/Frontend/Clang/Action.cpp +++ b/lib/Frontend/Clang/Action.cpp @@ -173,17 +173,21 @@ class AnalysisConsumer : public ASTConsumer { }; } -bool MainAction::BeginSourceFileAction(CompilerInstance &CI) { +bool ClangMainAction::BeginSourceFileAction(CompilerInstance &CI) { TimePassesIsEnabled = CI.getCodeGenOpts().TimePasses; - return mQueryManager.beginSourceFile(CI, getCurrentFile()); + return mQueryManager.beginSourceFile(CI.getDiagnostics(), getCurrentFile(), + CI.getFrontendOpts().OutputFile, + CI.getFileSystemOpts().WorkingDir); } -void MainAction::EndSourceFileAction() { - mQueryManager.endSourceFile(); +bool ClangMainAction::shouldEraseOutputFiles() { + mQueryManager.endSourceFile( + getCompilerInstance().getDiagnostics().hasErrorOccurred()); + return clang::ASTFrontendAction::shouldEraseOutputFiles(); } -std::unique_ptr MainAction::CreateASTConsumer(CompilerInstance &CI, - StringRef InFile) { +std::unique_ptr +ClangMainAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { return std::make_unique(CI, InFile, mTfmInfo, mQueryManager); } diff --git a/lib/Frontend/Flang/Action.cpp b/lib/Frontend/Flang/Action.cpp new file mode 100644 index 00000000..00b6484d --- /dev/null +++ b/lib/Frontend/Flang/Action.cpp @@ -0,0 +1,129 @@ +//===- Action.cpp ------ TSAR Frontend Action (Flang) ------------*- C++ -*===// +// +// Traits Static Analyzer (SAPFOR) +// +// Copyright 2022 DVM System Group +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +// +// This file contains front-end actions which are necessary to analyze and +// transform sources. +// +//===----------------------------------------------------------------------===// + +#include +#include "tsar/Frontend/Flang/Action.h" +#include "tsar/Core/Query.h" +#include "tsar/Frontend/Flang/TransformationContext.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace llvm; +using namespace tsar; +using namespace Fortran; + +bool tsar::FlangMainAction::beginSourceFileAction() { + using namespace Fortran::frontend; + // CodeGenAction::BeginSourceFileAction() is private, so we cannot call + // it explicitly here. We also want to extend it functionality here, so + // we juast copy its implementation. + + llvmCtx = std::make_unique(); + + // If the input is an LLVM file, just parse it and return. + if (this->getCurrentInput().getKind().getLanguage() == Language::LLVM_IR) + return false; + + // Otherwise, generate an MLIR module from the input Fortran source + assert(getCurrentInput().getKind().getLanguage() == Language::Fortran && + "Invalid input type - expecting a Fortran file"); + bool res = runPrescan() && runParse() && runSemanticChecks(); + if (!res) + return res; + + CompilerInstance &ci = this->getInstance(); + + // Load the MLIR dialects required by Flang + mlir::DialectRegistry registry; + mlirCtx = std::make_unique(registry); + fir::support::registerNonCodegenDialects(registry); + fir::support::loadNonCodegenDialects(*mlirCtx); + + // Create a LoweringBridge + const common::IntrinsicTypeDefaultKinds &defKinds = + ci.getInvocation().getSemanticsContext().defaultKinds(); + fir::KindMapping kindMap(mlirCtx.get(), + llvm::ArrayRef{fir::fromDefaultKinds(defKinds)}); + lower::LoweringBridge lb = Fortran::lower::LoweringBridge::create( + *mlirCtx, defKinds, ci.getInvocation().getSemanticsContext().intrinsics(), + ci.getParsing().allCooked(), ci.getInvocation().getTargetOpts().triple, + kindMap); + + // Create a parse tree and lower it to FIR + Fortran::parser::Program &parseTree{*ci.getParsing().parseTree()}; + lb.lower(parseTree, ci.getInvocation().getSemanticsContext()); + mlirModule = std::make_unique(lb.getModule()); + + // run the default passes. + mlir::PassManager pm(mlirCtx.get(), mlir::OpPassManager::Nesting::Implicit); + pm.enableVerifier(/*verifyPasses=*/true); + pm.addPass(std::make_unique()); + + if (mlir::failed(pm.run(*mlirModule))) { + unsigned diagID = ci.getDiagnostics().getCustomDiagID( + clang::DiagnosticsEngine::Error, + "verification of lowering to FIR failed"); + ci.getDiagnostics().Report(diagID); + return false; + } + // And of CodeGenAction::BeginSourceFileAction() copy. + + return mQueryManager.beginSourceFile( + getInstance().getDiagnostics(), getCurrentFile(), + getInstance().getFrontendOpts().outputFile, getWorkingDir()); +} + +bool tsar::FlangMainAction::shouldEraseOutputFiles() { + mQueryManager.endSourceFile( + getInstance().getDiagnostics().hasErrorOccurred()); + return Fortran::frontend::CodeGenAction::shouldEraseOutputFiles(); +} + +void tsar::FlangMainAction::executeAction() { + auto &CI{getInstance()}; + generateLLVMIR(); + Timer LLVMIRAnalysis{"LLVMIRAnalysis", "LLVM IR Analysis Time"}; + if (llvm::TimePassesIsEnabled) + LLVMIRAnalysis.startTimer(); + auto CUs{llvmModule->getNamedMetadata("llvm.dbg.cu")}; + if (CUs->getNumOperands() == 1) { + auto *CU{cast(*CUs->op_begin())}; + IntrusiveRefCntPtr TfmCtx{ + new FlangTransformationContext{ + CI.getParsing(), CI.getInvocation().getFortranOpts(), + CI.getSemantics().context(), *llvmModule, *CU}}; + mTfmInfo.setContext(*CU, std::move(TfmCtx)); + } + mQueryManager.run(llvmModule.get(), &mTfmInfo); + if (llvm::TimePassesIsEnabled) + LLVMIRAnalysis.stopTimer(); +} diff --git a/lib/Frontend/Flang/CMakeLists.txt b/lib/Frontend/Flang/CMakeLists.txt index 75692324..38d779c7 100644 --- a/lib/Frontend/Flang/CMakeLists.txt +++ b/lib/Frontend/Flang/CMakeLists.txt @@ -1,5 +1,5 @@ -set(FRONTEND_SOURCES TransformationContext.cpp) +set(FRONTEND_SOURCES TransformationContext.cpp Tooling.cpp Action.cpp) if(MSVC_IDE) file(GLOB_RECURSE FRONTEND_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/lib/Frontend/Flang/Tooling.cpp b/lib/Frontend/Flang/Tooling.cpp new file mode 100644 index 00000000..00a92369 --- /dev/null +++ b/lib/Frontend/Flang/Tooling.cpp @@ -0,0 +1,179 @@ +//===- Tooling.cpp --------- Flang Based Tool (Flang) ------------*- C++ -*===// +// +// Traits Static Analyzer (SAPFOR) +// +// Copyright 2022 DVM System Group +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +// +// This file implements functions to run flang tools standalone instead of +// running them as a plugin. +// +//===----------------------------------------------------------------------===// + +#include "tsar/Frontend/Flang/Tooling.h" +#include "tsar/Frontend/Flang/Action.h" +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace clang; +using namespace clang::tooling; +using namespace llvm; +using namespace tsar; + +namespace llvm { +// TODO (kaniandr@gmail.com) : there is no corresponding option in flang, +// so we make some kind of hack and initialize it here expclitily. +// It's better to move initialization in ...Action, if possible. +extern bool TimePassesIsEnabled; +} + +static CommandLineArguments removeUnusedOptions(const CommandLineArguments &CL, + StringRef Filename) { + CommandLineArguments AdjustedArgs; + for (std::size_t I = 0; I < CL.size(); ++I) { + // Silently remove unsupported internal options. + auto Arg{StringSwitch(CL[I]) + .Cases("-O1", "-Xclang", "-disable-llvm-passes", "-g", + "-fstandalone-debug", "-gcolumn-info", + "-Qunused-arguments", "") + .Default(CL[I])}; + if (Arg.empty()) + continue; + Arg = StringSwitch(CL[I]) + .Cases("-ftime-report", "-fcaret-diagnostics", + "-fno-caret-diagnostics", "-fshow-source-location", + "-fno-show-source-location", "-fdiscard-value-names", + "-fno-discard-value-names", "-v", "") + .Default(CL[I]); + // TODO (kaniandr@gmail.com) : there is no cooresponding option in flang, + // so we make some kind of hack and initialize it here expclitily. + // It's better to move initialization in ...Action, if possible. + if (CL[I] == "-ftime-report") + TimePassesIsEnabled = true; + if (!Arg.empty()) + AdjustedArgs.push_back(Arg.str()); + else + errs() << "Skipping unsupported option " << CL[I] << " while processing " + << Filename << "\n"; + } + return AdjustedArgs; +} + +static CommandLineArguments addLLVMOptions(const CommandLineArguments &CL, + StringRef Filename) { + CommandLineArguments AdjustedArgs(CL); + AdjustedArgs.push_back("-mllvm"); + AdjustedArgs.push_back("-disable-external-name-interop"); + return AdjustedArgs; +} + +int FlangTool::run(FlangFrontendActionFactory *Factory) { + appendArgumentsAdjuster(removeUnusedOptions); + appendArgumentsAdjuster(addLLVMOptions); + std::vector AbsolutePaths; + AbsolutePaths.reserve(mSourcePaths.size()); + for (const auto &Path : mSourcePaths) { + auto APath{getAbsolutePath(*mOverlayFileSystem, Path)}; + if (!APath) { + errs() << "Skipping " << Path + << ". Error while getting an absolute path: " + << llvm::toString(APath.takeError()) << "\n"; + continue; + } + AbsolutePaths.push_back(std::move(*APath)); + } + std::string InitialWorkingDir; + if (mRestoreCWD) + if (auto CWD{mOverlayFileSystem->getCurrentWorkingDirectory()}) + InitialWorkingDir = std::move(*CWD); + else + errs() << "Could not get working directory: " << CWD.getError().message() + << "\n"; + bool ProcessingFailed{false}, FileSkipped{false}; + for (StringRef File : AbsolutePaths) { + auto CompileCommandsForFile{mCompilations.getCompileCommands(File)}; + if (CompileCommandsForFile.empty()) { + errs() << "Skipping " << File << ". Compile command not found.\n"; + FileSkipped = true; + continue; + } + for (auto &CompileCommand : CompileCommandsForFile) { + if (mOverlayFileSystem->setCurrentWorkingDirectory( + CompileCommand.Directory)) + report_fatal_error("Cannot chdir int \"" + + Twine(CompileCommand.Directory) + "\"!"); + std::vector CommandLine{CompileCommand.CommandLine}; + if (mArgsAdjuster) + CommandLine = mArgsAdjuster(CommandLine, CompileCommand.Filename); + assert(!CommandLine.empty() && "Command line must not be empty!"); + auto Flang{std::make_unique()}; + Flang->createDiagnostics(); + if (!Flang->hasDiagnostics()) + report_fatal_error( + "Cannot create diagnostic engine for the frontend driver!"); + auto *DiagsBuffer{new Fortran::frontend::TextDiagnosticBuffer}; + IntrusiveRefCntPtr DiagID{new DiagnosticIDs}; + IntrusiveRefCntPtr DiagOpts{new DiagnosticOptions}; + DiagnosticsEngine Diags{DiagID, &*DiagOpts, DiagsBuffer}; + std::vector RawCommandLine; + RawCommandLine.reserve(CommandLine.size()); + transform(CommandLine, std::back_inserter(RawCommandLine), + [](auto &V) { return V.c_str(); }); + auto Success{Fortran::frontend::CompilerInvocation::createFromArgs( + Flang->getInvocation(), makeArrayRef(RawCommandLine).slice(1), Diags)}; + DiagsBuffer->flushDiagnostics(Flang->getDiagnostics()); + if (!Flang->getFrontendOpts().llvmArgs.empty()) { + unsigned NumArgs = Flang->getFrontendOpts().llvmArgs.size(); + auto Args{std::make_unique(NumArgs + 2)}; + Args[0] = "flang (LLVM option parsing)"; + for (unsigned I = 0; I < NumArgs; ++I) + Args[I + 1] = Flang->getFrontendOpts().llvmArgs[I].c_str(); + // Add additional argument because at least on positional argument is + // required for TSAR. + Args[NumArgs + 1] = ""; + llvm::cl::ParseCommandLineOptions(NumArgs + 2, Args.get()); + } + if (Success) { + auto Action{Factory->create()}; + Action->setWorkingDir(CompileCommand.Directory); + Success = Flang->executeAction(*Action); + } + if (!Success) { + errs() << "Error while processing " << File << "\n"; + ProcessingFailed = true; + } + Flang->clearOutputFiles(false); + } + } + if (!InitialWorkingDir.empty()) { + if (auto EC{ + mOverlayFileSystem->setCurrentWorkingDirectory(InitialWorkingDir)}) + llvm::errs() << "Error when trying to restore working dir: " + << EC.message() << "\n"; + } + return ProcessingFailed ? 1 : (FileSkipped ? 2 : 0); +} + +void FlangTool::appendArgumentsAdjuster(ArgumentsAdjuster Adjuster) { + mArgsAdjuster = + combineAdjusters(std::move(mArgsAdjuster), std::move(Adjuster)); +} \ No newline at end of file diff --git a/lib/Frontend/Flang/TransformationContext.cpp b/lib/Frontend/Flang/TransformationContext.cpp index c875aa71..6bd84347 100644 --- a/lib/Frontend/Flang/TransformationContext.cpp +++ b/lib/Frontend/Flang/TransformationContext.cpp @@ -76,13 +76,12 @@ void FlangTransformationContext::initialize( assert(hasInstance() && "Transformation context is not configured!"); NameHierarchyMapT NameHierarchy; collect(M, CU, NameHierarchy); - for (auto &Child : mContext.globalScope().children()) { + for (auto &Child : mContext->globalScope().children()) { NameHierarchyMapT::key_type Names; match(Child, Names, NameHierarchy, mGlobals); } - mRewriter = - std::make_unique(mParsing.cooked(), mAllCookedSources); - //mParsing.cooked().CompileProvenanceRangeToOffsetMappings(mAllSources); + mRewriter = std::make_unique(mParsing->cooked(), + mParsing->allCooked()); } std::pair FlangTransformationContext::release( @@ -104,13 +103,13 @@ std::pair FlangTransformationContext::release( I->second->write(File.getStream()); } if (Error) { - auto Pos{*mParsing.cooked().GetCharBlock(I->second->getRange())}; + auto Pos{*mParsing->cooked().GetCharBlock(I->second->getRange())}; AllWritten = false; std::visit( [this, &Error, Pos](const auto &Args) { bcl::forward_as_args( - Args, [this, &Error, Pos](const auto &... Args) { - toDiag(mContext, Pos, std::get(*Error), Args...); + Args, [this, &Error, Pos](const auto &...Args) { + toDiag(*mContext, Pos, std::get(*Error), Args...); }); }, std::get(*Error)); @@ -119,6 +118,6 @@ std::pair FlangTransformationContext::release( MainFile = Name; } } - mContext.messages().Emit(errs(), mAllCookedSources); + mContext->messages().Emit(errs(), mParsing->allCooked()); return std::make_pair(std::move(MainFile), AllWritten); - } +} diff --git a/lib/Support/Clang/Utils.cpp b/lib/Support/Clang/Utils.cpp index b5d7f234..0ca489b9 100644 --- a/lib/Support/Clang/Utils.cpp +++ b/lib/Support/Clang/Utils.cpp @@ -23,6 +23,8 @@ //===----------------------------------------------------------------------===// #include "tsar/Support/Clang/Utils.h" +#include "tsar/Support/OutputFile.h" +#include "tsar/Support/Clang/Diagnostic.h" #include "tsar/Support/Utils.h" #include #include @@ -454,3 +456,29 @@ StringRef tsar::getFunctionName(FunctionDecl &FD, } return FD.getName(); } + +Optional tsar::createDefaultOutputFile( + clang::DiagnosticsEngine &Diags, StringRef OutputPath, + bool Binary, llvm::StringRef BaseInput, + llvm::StringRef Extension, bool RemoveFileOnSignal, + bool UseTemporary, + bool CreateMissingDirectories) { + Optional> PathStorage; + if (OutputPath.empty()) { + if (BaseInput == "-" || Extension.empty()) { + OutputPath = "-"; + } else { + PathStorage.emplace(BaseInput); + llvm::sys::path::replace_extension(*PathStorage, Extension); + OutputPath = *PathStorage; + } + } + + auto OF{OutputFile::create(OutputPath, Binary, RemoveFileOnSignal, + UseTemporary, CreateMissingDirectories)}; + if (OF) + return std::move(*OF); + toDiag(Diags, tsar::diag::err_fe_unable_to_open_output) + << OutputPath << errorToErrorCode(OF.takeError()).message(); + return None; +} diff --git a/lib/Support/Utils.cpp b/lib/Support/Utils.cpp index 74d7ca10..3beefb77 100644 --- a/lib/Support/Utils.cpp +++ b/lib/Support/Utils.cpp @@ -25,8 +25,12 @@ #include "tsar/Support/Utils.h" #include "tsar/Support/IRUtils.h" #include "tsar/Support/MetadataUtils.h" +#include "tsar/Support/OutputFile.h" #include #include +#include +#include +#include #include using namespace llvm; @@ -127,4 +131,136 @@ llvm::DIType *createStubType(llvm::Module &M, unsigned int AS, return DIB.createArtificialType( DIB.createPointerType(DIBasicTy, PtrSize, 0, None, "sapfor.type")); } + +Expected +OutputFile::create(StringRef OutputPath, bool Binary, + bool RemoveFileOnSignal, bool UseTemporary, + bool CreateMissingDirectories) { + assert((!CreateMissingDirectories || UseTemporary) && + "CreateMissingDirectories is only allowed when using temporary files"); + + std::unique_ptr OS; + Optional OSFile; + + if (UseTemporary) { + if (OutputPath == "-") + UseTemporary = false; + else { + llvm::sys::fs::file_status Status; + llvm::sys::fs::status(OutputPath, Status); + if (llvm::sys::fs::exists(Status)) { + // Fail early if we can't write to the final destination. + if (!llvm::sys::fs::can_write(OutputPath)) + return llvm::errorCodeToError( + make_error_code(llvm::errc::operation_not_permitted)); + + // Don't use a temporary if the output is a special file. This handles + // things like '-o /dev/null' + if (!llvm::sys::fs::is_regular_file(Status)) + UseTemporary = false; + } + } + } + Optional Temp; + if (UseTemporary) { + // Create a temporary file. + // Insert -%%%%%%%% before the extension (if any), and because some tools + // (noticeable, clang's own GlobalModuleIndex.cpp) glob for build + // artifacts, also append .tmp. + StringRef OutputExtension = llvm::sys::path::extension(OutputPath); + SmallString<128> TempPath = + StringRef(OutputPath).drop_back(OutputExtension.size()); + TempPath += "-%%%%%%%%"; + TempPath += OutputExtension; + TempPath += ".tmp"; + Expected ExpectedFile = + llvm::sys::fs::TempFile::create( + TempPath, llvm::sys::fs::all_read | llvm::sys::fs::all_write, + Binary ? llvm::sys::fs::OF_None : llvm::sys::fs::OF_Text); + + llvm::Error E = handleErrors( + ExpectedFile.takeError(), [&](const llvm::ECError &E) -> llvm::Error { + std::error_code EC = E.convertToErrorCode(); + if (CreateMissingDirectories && + EC == llvm::errc::no_such_file_or_directory) { + StringRef Parent = llvm::sys::path::parent_path(OutputPath); + EC = llvm::sys::fs::create_directories(Parent); + if (!EC) { + ExpectedFile = llvm::sys::fs::TempFile::create(TempPath); + if (!ExpectedFile) + return llvm::errorCodeToError( + llvm::errc::no_such_file_or_directory); + } + } + return llvm::errorCodeToError(EC); + }); + + if (E) { + consumeError(std::move(E)); + } else { + Temp = std::move(ExpectedFile.get()); + OS.reset(new llvm::raw_fd_ostream(Temp->FD, /*shouldClose=*/false)); + OSFile = Temp->TmpName; + } + // If we failed to create the temporary, fallback to writing to the file + // directly. This handles the corner case where we cannot write to the + // directory, but can write to the file. + } + + if (!OS) { + OSFile = OutputPath; + std::error_code EC; + OS.reset(new llvm::raw_fd_ostream( + *OSFile, EC, + (Binary ? llvm::sys::fs::OF_None : llvm::sys::fs::OF_TextWithCRLF))); + if (EC) + return llvm::errorCodeToError(EC); + } + + // Don't try to remove "-", since this means we are using stdin. + if (!Binary || OS->supportsSeeking()) + return OutputFile{(OutputPath != "-") ? OutputPath : "", + Binary, + RemoveFileOnSignal, + CreateMissingDirectories, + std::move(OS), + std::move(Temp)}; + + return OutputFile{ + (OutputPath != "-") ? OutputPath : "", + Binary, + RemoveFileOnSignal, + CreateMissingDirectories, + std::make_unique(std::move(OS)), + std::move(Temp)}; +} + +llvm::Error OutputFile::clear(StringRef WorkingDir, bool EraseFile) { + assert(isValid() && "The file has been already cleared!"); + mOS.reset(); + // Ignore errors that occur when trying to discard the temp file. + if (EraseFile) { + if (mTemp) + consumeError(mTemp->discard()); + if (!mFilename.empty()) + llvm::sys::fs::remove(mFilename); + return Error::success(); + } + if (!mTemp) + return Error::success(); + if (mTemp->TmpName.empty()) { + consumeError(mTemp->discard()); + return Error::success(); + } + SmallString<128> NewOutFile{mFilename}; + if (!WorkingDir.empty() && !llvm::sys::path::is_absolute(mFilename)) { + NewOutFile = WorkingDir; + llvm::sys::path::append(NewOutFile, mFilename); + } + llvm::Error E = mTemp->keep(NewOutFile); + if (!E) + return Error::success(); + llvm::sys::fs::remove(mTemp->TmpName); + return std::move(E); +} } From 83b986e49231fb82b810e07d364b6aedb9722a2f Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Thu, 5 May 2022 16:52:28 +0300 Subject: [PATCH 33/93] [TSAR, Match, Expr] Fix, trivial. --- lib/Analysis/Clang/ExpressionMatcher.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/Analysis/Clang/ExpressionMatcher.cpp b/lib/Analysis/Clang/ExpressionMatcher.cpp index 8ae162ef..cd9b6a30 100644 --- a/lib/Analysis/Clang/ExpressionMatcher.cpp +++ b/lib/Analysis/Clang/ExpressionMatcher.cpp @@ -115,14 +115,14 @@ class MatchExprVisitor : << "\n"); if (auto CE = dyn_cast(S)) { if (!CE->getDirectCallee()) { - StashParent [[maybe_unused]] Stash{CE->getCallee(), mParents}; + [[maybe_unused]] StashParent Stash{CE->getCallee(), mParents}; // We match expression which computes callee before this call. if (!TraverseStmt(CE->getCallee())) return false; } VisitItem(DynTypedNode::create(*S), S->getBeginLoc()); for (auto Arg : CE->arguments()) { - StashParent [[maybe_unused]] Stash{Arg, mParents}; + [[maybe_unused]] StashParent Stash{Arg, mParents}; if (!TraverseStmt(Arg)) return false; } @@ -135,7 +135,7 @@ class MatchExprVisitor : if (auto *T{ dyn_cast(U->getArgumentType().getTypePtr())}) { for (auto *C : U->children()) { - StashParent [[maybe_unused]] Stash{C, mParents}; + [[maybe_unused]] StashParent Stash{C, mParents}; if (!TraverseStmt(C)) return false; } @@ -166,7 +166,7 @@ class MatchExprVisitor : // For `++ ` we match `++` with store and `` with load. VisitItem(DynTypedNode::create(*UO->getSubExpr()), UO->getOperatorLoc()); VisitItem(DynTypedNode::create(*S), UO->getOperatorLoc()); - StashParent [[maybe_unused]] Stash{UO->getSubExpr(), mParents}; + [[maybe_unused]] StashParent Stash{UO->getSubExpr(), mParents}; return TraverseStmt(UO->getSubExpr()); } if (auto DRE{dyn_cast(S)}) { @@ -193,7 +193,7 @@ class MatchExprVisitor : } else if (auto *ME = dyn_cast(S)) { VisitItem(DynTypedNode::create(*S), ME->getMemberLoc()); } - StashParent [[maybe_unused]] Stash{S, mParents}; + [[maybe_unused]] StashParent Stash{S, mParents}; return RecursiveASTVisitor::TraverseStmt(S); } From e7aa372e590a2748b3a4782659d1b699702e91a3 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Fri, 6 May 2022 20:42:48 +0300 Subject: [PATCH 34/93] [TSAR, Flang, IR] Update DICompileUnit for a module if it has not been set properly. --- lib/Frontend/Flang/Action.cpp | 64 +++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/lib/Frontend/Flang/Action.cpp b/lib/Frontend/Flang/Action.cpp index 00b6484d..ec6f99ae 100644 --- a/lib/Frontend/Flang/Action.cpp +++ b/lib/Frontend/Flang/Action.cpp @@ -27,6 +27,7 @@ #include "tsar/Frontend/Flang/Action.h" #include "tsar/Core/Query.h" #include "tsar/Frontend/Flang/TransformationContext.h" +#include "tsar/Support/MetadataUtils.h" #include #include #include @@ -34,7 +35,10 @@ #include #include #include +#include #include +#include +#include #include using namespace llvm; @@ -108,6 +112,33 @@ bool tsar::FlangMainAction::shouldEraseOutputFiles() { return Fortran::frontend::CodeGenAction::shouldEraseOutputFiles(); } +namespace { +struct DICompileUnitReplacer { + DICompileUnitReplacer(DICompileUnit *From, DICompileUnit *To) : + From(From), To(To) {} + + void visitMDNode(MDNode &MD) { + if (!MDNodes.insert(&MD).second) + return; + for (unsigned I{0}, EI{MD.getNumOperands()}; I < EI; ++I) { + auto &Op{MD.getOperand(I)}; + if (!Op.get()) + continue; + if (auto *CU{dyn_cast(Op)}; CU && CU == From) + MD.replaceOperandWith(I, To); + if (auto *N = dyn_cast(Op)) { + visitMDNode(*N); + continue; + } + } + } + + SmallPtrSet MDNodes; + DICompileUnit *From; + DICompileUnit *To; +}; +} + void tsar::FlangMainAction::executeAction() { auto &CI{getInstance()}; generateLLVMIR(); @@ -117,6 +148,39 @@ void tsar::FlangMainAction::executeAction() { auto CUs{llvmModule->getNamedMetadata("llvm.dbg.cu")}; if (CUs->getNumOperands() == 1) { auto *CU{cast(*CUs->op_begin())}; + SmallString<128> CUFilePath; + auto *DIF{CU->getFile()}; + if (DIF) + getAbsolutePath(*CU, CUFilePath); + if (!sys::fs::exists(CUFilePath) || !isFortran(CU->getSourceLanguage())) { + auto Filename{getCurrentFile()}; + assert(sys::path::is_absolute(Filename) && + "Path to a processed file must be absolute!"); + SmallString<128> Directory{Filename}; + sys::path::remove_filename(Directory); + auto *NewDIFile{ + DIFile::get(llvmModule->getContext(), Filename, Directory)}; + auto NewDICU{DICompileUnit::getDistinct( + llvmModule->getContext(), + isFortran(CU->getSourceLanguage()) ? CU->getSourceLanguage() + : dwarf::DW_LANG_Fortran08, + NewDIFile, CU->getProducer(), CU->isOptimized(), CU->getFlags(), + CU->getRuntimeVersion(), CU->getSplitDebugFilename(), + CU->getEmissionKind(), CU->getEnumTypes(), CU->getRetainedTypes(), + CU->getGlobalVariables(), CU->getImportedEntities(), CU->getMacros(), + CU->getDWOId(), CU->getSplitDebugInlining(), + CU->getDebugInfoForProfiling(), CU->getNameTableKind(), + CU->getRangesBaseAddress(), CU->getSysRoot(), CU->getSDK())}; + llvmModule->setSourceFileName(Filename); + DICompileUnitReplacer R{CU, NewDICU}; + for (auto &F : *llvmModule) { + SmallVector, 1> MDs; + F.getAllMetadata(MDs); + for (auto &MD : MDs) + R.visitMDNode(*MD.second); + } + CUs->setOperand(0, NewDICU); + } IntrusiveRefCntPtr TfmCtx{ new FlangTransformationContext{ CI.getParsing(), CI.getInvocation().getFortranOpts(), From 60f5b4f169e466e1d758b024de0322de2d736e8b Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Sun, 8 May 2022 15:58:23 +0300 Subject: [PATCH 35/93] [TSAR, Flang, Frontend] Fix, set missing option to force the parser to establish correspondence between sources and AST entities. --- lib/Frontend/Flang/Tooling.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/Frontend/Flang/Tooling.cpp b/lib/Frontend/Flang/Tooling.cpp index 00a92369..cfde981f 100644 --- a/lib/Frontend/Flang/Tooling.cpp +++ b/lib/Frontend/Flang/Tooling.cpp @@ -153,6 +153,9 @@ int FlangTool::run(FlangFrontendActionFactory *Factory) { llvm::cl::ParseCommandLineOptions(NumArgs + 2, Args.get()); } if (Success) { + Flang->getInvocation() + .getFrontendOpts() + .needProvenanceRangeToCharBlockMappings = true; auto Action{Factory->create()}; Action->setWorkingDir(CompileCommand.Directory); Success = Flang->executeAction(*Action); From 6408f56cb2e8fd8db89c9cd05f703a8037f0ab38 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Sun, 8 May 2022 16:09:26 +0300 Subject: [PATCH 36/93] [TSAR, Flang, Frontend] Update metadata for functions while it is not implemented in the Flang lower properly. --- .../Frontend/Flang/TransformationContext.h | 2 + lib/Frontend/Flang/Action.cpp | 262 +++++++++++++++++- lib/Frontend/Flang/TransformationContext.cpp | 10 +- 3 files changed, 265 insertions(+), 9 deletions(-) diff --git a/include/tsar/Frontend/Flang/TransformationContext.h b/include/tsar/Frontend/Flang/TransformationContext.h index 0f0d08c9..95aaf580 100644 --- a/include/tsar/Frontend/Flang/TransformationContext.h +++ b/include/tsar/Frontend/Flang/TransformationContext.h @@ -45,6 +45,8 @@ class FlangTransformationContext : public TransformationContextBase { return Ctx->getKind() == TC_Flang; } + static constexpr llvm::StringRef UnnamedProgramStub{""}; + FlangTransformationContext(Fortran::parser::Parsing &Parsing, Fortran::parser::Options &Options, Fortran::semantics::SemanticsContext &Context, diff --git a/lib/Frontend/Flang/Action.cpp b/lib/Frontend/Flang/Action.cpp index ec6f99ae..8ce9f776 100644 --- a/lib/Frontend/Flang/Action.cpp +++ b/lib/Frontend/Flang/Action.cpp @@ -24,6 +24,7 @@ //===----------------------------------------------------------------------===// #include +#include "tsar/ADT/DenseMapTraits.h" #include "tsar/Frontend/Flang/Action.h" #include "tsar/Core/Query.h" #include "tsar/Frontend/Flang/TransformationContext.h" @@ -31,12 +32,16 @@ #include #include #include +#include +#include #include #include #include #include #include #include +#include +#include #include #include #include @@ -113,9 +118,8 @@ bool tsar::FlangMainAction::shouldEraseOutputFiles() { } namespace { -struct DICompileUnitReplacer { - DICompileUnitReplacer(DICompileUnit *From, DICompileUnit *To) : - From(From), To(To) {} +struct DINodeReplacer { + DINodeReplacer(DINode *From, DINode *To) : From(From), To(To) {} void visitMDNode(MDNode &MD) { if (!MDNodes.insert(&MD).second) @@ -124,7 +128,7 @@ struct DICompileUnitReplacer { auto &Op{MD.getOperand(I)}; if (!Op.get()) continue; - if (auto *CU{dyn_cast(Op)}; CU && CU == From) + if (Op == From) MD.replaceOperandWith(I, To); if (auto *N = dyn_cast(Op)) { visitMDNode(*N); @@ -134,8 +138,232 @@ struct DICompileUnitReplacer { } SmallPtrSet MDNodes; - DICompileUnit *From; - DICompileUnit *To; + DINode *From; + DINode *To; +}; + +class ProgramUnitCollector { +public: + using LineToSubprogramMap = + DenseMap, DenseMapInfo, + TaggedDenseMapTuple, + bcl::tagged, + bcl::tagged>>; + + explicit ProgramUnitCollector(const parser::AllCookedSources &AllCooked, + LineToSubprogramMap &LineToSubprogram, + LLVMContext &Ctx, DIScope &Scope) + : mAllCooked(AllCooked), mLineToSubprogram(LineToSubprogram), mCtx(Ctx) { + mScopeStack.push_back(&Scope); + } + + template bool Pre(T &N) { return true; } + template void Post(T &N) {} + + bool Pre(parser::ProgramUnit &PU) { + return std::visit( + common::visitors{ + [](auto &) { return false; }, + [this](common::Indirection &X) { + if (const auto &S{std::get< + std::optional>>( + X.value().t)}) + return preUnitStatement(*S); + mInUnnamedProgram = true; + return true; + }, + [this](common::Indirection &X) { + const auto &S{std::get>( + X.value().t)}; + return preUnitStatement(S); + }, + [this](common::Indirection &X) { + const auto &S{std::get>( + X.value().t)}; + return preUnitStatement(S); + }, + [this](common::Indirection &X) { + const auto &S{ + std::get>(X.value().t)}; + const auto &Name{S.statement.v.symbol->name()}; + auto Range{mAllCooked.GetSourcePositionRange(S.source)}; + auto *Scope{mScopeStack.back()}; + auto DIM{DIModule::get( + Scope->getContext(), Scope->getFile(), Scope, + MDString::get(Scope->getContext(), Name.ToString()), nullptr, + nullptr, nullptr, Range ? Range->first.line : 0)}; + mScopeStack.push_back(DIM); + return true; + }}, + PU.u); + } + + bool Pre(parser::InternalSubprogram &IS) { + return std::visit( + common::visitors{ + [](auto &) { return false; }, + [this](common::Indirection &X) { + const auto &S{std::get>( + X.value().t)}; + return preUnitStatement(S); + }, + [this](common::Indirection &X) { + const auto &S{std::get>( + X.value().t)}; + return preUnitStatement(S); + }}, + IS.u); + } + + bool Pre(parser::ModuleSubprogram &MS) { + return std::visit( + common::visitors{ + [](auto &) { return false; }, + [this](common::Indirection &X) { + const auto &S{std::get>( + X.value().t)}; + return preUnitStatement(S); + }, + [this](common::Indirection &X) { + const auto &S{std::get>( + X.value().t)}; + return preUnitStatement(S); + }}, + MS.u); + } + + void Post(parser::ProgramUnit &PU) { mScopeStack.pop_back(); } + void Post(parser::InternalSubprogram &) { mScopeStack.pop_back(); } + void Post(parser::ModuleSubprogram &) { mScopeStack.pop_back(); } + + bool Pre(parser::InternalSubprogramPart &ISP) { + if (mInUnnamedProgram) + mDeferredMainSubprograms = &ISP; + return !mInUnnamedProgram; + } + + template bool Pre(parser::Statement &S) { + if (mInUnnamedProgram) { + if (auto Range{mAllCooked.GetSourcePositionRange(S.source)}) + if (auto LToSItr{mLineToSubprogram.find(Range->first.line)}; + LToSItr != mLineToSubprogram.end() && + LToSItr->template get()) { + markAsMain(LToSItr); + LToSItr->template get()->replaceOperandWith( + 1, mScopeStack.back()); + assert(LToSItr->template get()->getScope() == + mScopeStack.back() && + "Corrupted metadata!"); + LToSItr->template get()->replaceOperandWith( + 2, MDString::get(mCtx, + FlangTransformationContext::UnnamedProgramStub)); + assert(LToSItr->template get()->getName() == + FlangTransformationContext::UnnamedProgramStub && + "Corrupted metadata!"); + mScopeStack.push_back(LToSItr->template get()); + mDIMainProgram = LToSItr->template get(); + // If we haven't processed internal subprograms in the main program + // yet, it's not necessary to defer the processing, because we + // already known DIScope for the main program. + // If there is no executable constracts in the unnamed main program, + // it's line in a metadata corresponds to the end statement, so + // we have already deferred processing of internal subprograms. + mInUnnamedProgram = false; + return false; + } + } + return true; + } + + parser::InternalSubprogramPart * getDeferredSubprograms() noexcept { + return mDeferredMainSubprograms; + } + + DISubprogram * getDIMainProgram() noexcept { return mDIMainProgram; } + +private: + template bool preUnitStatement(const T &S) { + if (auto Range{mAllCooked.GetSourcePositionRange(S.source)}) + if (auto LToSItr{mLineToSubprogram.find(Range->first.line)}; + LToSItr != mLineToSubprogram.end() && + LToSItr->template get()) { + LToSItr->template get()->replaceOperandWith( + 1, mScopeStack.back()); + assert(LToSItr->template get()->getScope() == + mScopeStack.back() && + "Corrupted metadata!"); + if constexpr (std::is_same_v>) { + markAsMain(LToSItr); + LToSItr->template get()->replaceOperandWith( + 2, MDString::get(mCtx, S.statement.v.symbol->name().ToString())); + assert(LToSItr->template get()->getName() == + S.statement.v.symbol->name().ToString() && + "Corrupted metadata!"); + mDIMainProgram = LToSItr->template get(); + } else { + const auto &Name{std::get(S.statement.t)}; + LToSItr->template get()->replaceOperandWith( + 2, MDString::get(mCtx, Name.symbol->name().ToString())); + assert(LToSItr->template get()->getName() == + Name.symbol->name().ToString() && + "Corrupted metadata!"); + } + mScopeStack.push_back(LToSItr->template get()); + return true; + } + return false; + } + + /// Set SPFlagMainSubprogram for main program unit. + void markAsMain(const LineToSubprogramMap::iterator <oSItr) { + assert(LToSItr != mLineToSubprogram.end() && "Subprogram must be known!"); + auto NewDISub{DISubprogram::getDistinct( + LToSItr->get()->getContext(), + LToSItr->get()->getScope(), + LToSItr->get()->getName(), + LToSItr->get()->getLinkageName(), + LToSItr->get()->getFile(), + LToSItr->get()->getLine(), + LToSItr->get()->getType(), + LToSItr->get()->getScopeLine(), + LToSItr->get()->getContainingType(), + LToSItr->get()->getVirtualIndex(), + LToSItr->get()->getThisAdjustment(), + LToSItr->get()->getFlags(), + LToSItr->get()->getSPFlags() | + DISubprogram::SPFlagMainSubprogram, + LToSItr->get()->getUnit(), + LToSItr->get()->getTemplateParams(), + LToSItr->get()->getDeclaration(), + LToSItr->get()->getRetainedNodes(), + LToSItr->get()->getThrownTypes(), + LToSItr->get()->getAnnotations(), + LToSItr->get()->getTargetFuncName())}; + DINodeReplacer R{LToSItr->get(), NewDISub}; + SmallVector, 1> MDs; + LToSItr->get()->getAllMetadata(MDs); + for (auto &I : instructions(LToSItr->get())) { + if (auto &Loc {I.getDebugLoc()}) + MDs.emplace_back(0, Loc.get()); + if (auto *MD{I.getMetadata(LLVMContext::MD_loop)}) + for (unsigned I = 1; I < MD->getNumOperands(); ++I) + if (auto *Loc{dyn_cast_or_null(MD->getOperand(I))}) + MDs.emplace_back(0, Loc); + } + for (auto &MD : MDs) + R.visitMDNode(*MD.second); + LToSItr->get()->setSubprogram(NewDISub); + LToSItr->get() = NewDISub; + } + + const Fortran::parser::AllCookedSources &mAllCooked; + LineToSubprogramMap &mLineToSubprogram; + LLVMContext &mCtx; + SmallVector mScopeStack; + parser::InternalSubprogramPart *mDeferredMainSubprograms{nullptr}; + DISubprogram *mDIMainProgram{nullptr}; + bool mInUnnamedProgram{false}; }; } @@ -172,7 +400,7 @@ void tsar::FlangMainAction::executeAction() { CU->getDebugInfoForProfiling(), CU->getNameTableKind(), CU->getRangesBaseAddress(), CU->getSysRoot(), CU->getSDK())}; llvmModule->setSourceFileName(Filename); - DICompileUnitReplacer R{CU, NewDICU}; + DINodeReplacer R{CU, NewDICU}; for (auto &F : *llvmModule) { SmallVector, 1> MDs; F.getAllMetadata(MDs); @@ -180,6 +408,26 @@ void tsar::FlangMainAction::executeAction() { R.visitMDNode(*MD.second); } CUs->setOperand(0, NewDICU); + CU = NewDICU; + } + ProgramUnitCollector::LineToSubprogramMap LineToSubprogram; + for (auto &F:*llvmModule) { + if (auto *DISub{F.getSubprogram()}) { + auto [I, IsNew] = + LineToSubprogram.try_emplace(DISub->getLine(), DISub, &F); + if (!IsNew) { + I->get() = nullptr; + } + } + } + ProgramUnitCollector V{CI.getParsing().allCooked(), LineToSubprogram, + llvmModule->getContext(), *CU}; + parser::Walk(CI.getParsing().parseTree(), V); + if (V.getDeferredSubprograms() && V.getDIMainProgram()) { + ProgramUnitCollector DeferredV{CI.getParsing().allCooked(), + LineToSubprogram, llvmModule->getContext(), + *V.getDIMainProgram()}; + parser::Walk(*V.getDeferredSubprograms(), DeferredV); } IntrusiveRefCntPtr TfmCtx{ new FlangTransformationContext{ diff --git a/lib/Frontend/Flang/TransformationContext.cpp b/lib/Frontend/Flang/TransformationContext.cpp index 6bd84347..6542513e 100644 --- a/lib/Frontend/Flang/TransformationContext.cpp +++ b/lib/Frontend/Flang/TransformationContext.cpp @@ -43,7 +43,10 @@ void collect(const Module &M, const DICompileUnit &CU, NameHierarchyMapT::key_type Key; DIScope *Scope{DISub}; do { - if (Scope->getName().empty()) + if (Scope->getName().empty() && + (!FlangTransformationContext::UnnamedProgramStub.empty() || + !isa(Scope) || + !cast(Scope)->isMainSubprogram())) break; Key.push_back(std::string{Scope->getName()}); Scope = Scope->getScope(); @@ -59,7 +62,10 @@ void collect(const Module &M, const DICompileUnit &CU, void match(semantics::Scope &Parent, NameHierarchyMapT::key_type &Names, const NameHierarchyMapT &NameHierarchy, MangledToSourceMapT &Map) { if (auto *S{Parent.symbol()}) { - Names.push_back(S->name().ToString()); + Names.push_back((Parent.kind() != semantics::Scope::Kind::MainProgram || + !S->name().empty()) + ? S->name().ToString() + : FlangTransformationContext::UnnamedProgramStub.str()); if (Parent.kind() == semantics::Scope::Kind::Subprogram || Parent.kind() == semantics::Scope::Kind::MainProgram) if (auto I = NameHierarchy.find(Names); I != NameHierarchy.end()) From 072d2e90adcf51e3b0189be0797ca400ee0ca492 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Fri, 6 May 2022 20:46:18 +0300 Subject: [PATCH 37/93] [TSAR, Flang, Frontend] Store reference to an AST-based representation of a unit in a transformation context. --- .../Frontend/Flang/TransformationContext.h | 59 +++++++++- lib/Frontend/Flang/TransformationContext.cpp | 109 +++++++++++++++++- lib/Transform/Flang/ConstantReplacement.cpp | 70 +++-------- lib/Transform/Mixed/DummyScopeAAPass.cpp | 5 +- 4 files changed, 178 insertions(+), 65 deletions(-) diff --git a/include/tsar/Frontend/Flang/TransformationContext.h b/include/tsar/Frontend/Flang/TransformationContext.h index 95aaf580..b5217828 100644 --- a/include/tsar/Frontend/Flang/TransformationContext.h +++ b/include/tsar/Frontend/Flang/TransformationContext.h @@ -30,6 +30,8 @@ #include #include #include +#include +#include namespace llvm { class Module; @@ -37,10 +39,59 @@ class DICompileUnit; } namespace tsar { -class FlangTransformationContext : public TransformationContextBase { - using MangledToSourceMapT = llvm::StringMap; +class FlangASTUnitRef { +public: + using ParserUnitT = llvm::PointerUnion; + using SemanticsUnitT = Fortran::semantics::Symbol *; + + FlangASTUnitRef() = default; + + FlangASTUnitRef(ParserUnitT PU, SemanticsUnitT SU) : + mSemanticsUnit(SU), mParserUnit(PU) {} + + bool isDeclaration() const { return mSemanticsUnit && !mParserUnit; } + + bool isNull() const { return !mSemanticsUnit; } + operator bool() const { return !isNull(); } + + operator SemanticsUnitT () const { return mSemanticsUnit; } + operator ParserUnitT() const { return mParserUnit; } + + SemanticsUnitT getSemanticsUnit() const noexcept { return mSemanticsUnit; } + ParserUnitT getParserUnit() const { return mParserUnit; } + + template bool is() const { + return mParserUnit.is>(); + } + template T & get() const { + return *mParserUnit.get>(); + } + + template T * dyn_cast() const { + return mParserUnit.dyn_cast>(); + } + + template auto visit(VisitorT &&V) { + assert(!isNull() && !isDeclaration() && "Reference must not be null!"); + if (auto *PU{dyn_cast()}) + return V(*PU, *mSemanticsUnit); + if (auto *PU{dyn_cast()}) + return V(*PU, *mSemanticsUnit); + if (auto *PU{dyn_cast()}) + return V(*PU, *mSemanticsUnit); + } +private: + ParserUnitT mParserUnit{nullptr}; + SemanticsUnitT mSemanticsUnit{nullptr}; +}; + +class FlangTransformationContext : public TransformationContextBase { + using MangledToSourceMapT = llvm::StringMap; public: + static bool classof(const TransformationContextBase *Ctx) noexcept { return Ctx->getKind() == TC_Flang; } @@ -107,10 +158,10 @@ class FlangTransformationContext : public TransformationContextBase { /// Return a declaration for a mangled name. /// /// \pre Transformation instance must be configured. - Fortran::semantics::Symbol * getDeclForMangledName(llvm::StringRef Name) { + FlangASTUnitRef getDeclForMangledName(llvm::StringRef Name) { assert(hasInstance() && "Transformation context is not configured!"); auto I = mGlobals.find(Name); - return (I != mGlobals.end()) ? I->getValue() : nullptr; + return (I != mGlobals.end()) ? I->getValue() : FlangASTUnitRef{}; } private: diff --git a/lib/Frontend/Flang/TransformationContext.cpp b/lib/Frontend/Flang/TransformationContext.cpp index 6542513e..31b802ef 100644 --- a/lib/Frontend/Flang/TransformationContext.cpp +++ b/lib/Frontend/Flang/TransformationContext.cpp @@ -25,6 +25,8 @@ #include "tsar/Frontend/Flang/TransformationContext.h" #include "tsar/Support/Flang/Diagnostic.h" #include +#include +#include #include #include @@ -33,8 +35,94 @@ using namespace llvm; using namespace Fortran; namespace { +class ProgramUnitCollector { +public: + template bool Pre(T &N) { return true; } + template void Post(T &N) {} + + bool Pre(parser::ProgramUnit &PU) { + return std::visit( + common::visitors{ + [](auto &) { return false; }, + [this, &PU](common::Indirection &X) { + mParserMain = &PU; + return true; + }, + [this, &PU](common::Indirection &X) { + const auto &S{std::get>( + X.value().t)}; + const auto &Name{std::get(S.statement.t)}; + mUnits.try_emplace(Name.symbol, &PU); + return true; + }, + [this, &PU](common::Indirection &X) { + const auto &S{std::get>( + X.value().t)}; + const auto &Name{std::get(S.statement.t)}; + mUnits.try_emplace(Name.symbol, &PU); + return true; + }, + [](common::Indirection &) { return true; }}, + PU.u); + } + + bool Pre(parser::InternalSubprogram &PU) { + return std::visit( + common::visitors{ + [](auto &) { return false; }, + [this, &PU](common::Indirection &X) { + const auto &S{std::get>( + X.value().t)}; + const auto &Name{std::get(S.statement.t)}; + mUnits.try_emplace(Name.symbol, &PU); + return true; + }, + [this, &PU](common::Indirection &X) { + const auto &S{std::get>( + X.value().t)}; + const auto &Name{std::get(S.statement.t)}; + mUnits.try_emplace(Name.symbol, &PU); + return true; + }}, + PU.u); + } + + bool Pre(parser::ModuleSubprogram &PU) { + return std::visit( + common::visitors{ + [](auto &) { return false; }, + [this, &PU](common::Indirection &X) { + const auto &S{std::get>( + X.value().t)}; + const auto &Name{std::get(S.statement.t)}; + mUnits.try_emplace(Name.symbol, &PU); + return true; + }, + [this, &PU](common::Indirection &X) { + const auto &S{std::get>( + X.value().t)}; + const auto &Name{std::get(S.statement.t)}; + mUnits.try_emplace(Name.symbol, &PU); + return true; + }}, + PU.u); + } + + auto getMainParserUnit() const noexcept { return mParserMain; } + + auto findParserUnit(FlangASTUnitRef::SemanticsUnitT U) const { + auto I{mUnits.find(U)}; + return I != mUnits.end() ? I->second : nullptr; + } + +private: + FlangASTUnitRef::ParserUnitT mParserMain; + DenseMap + mUnits; +}; + using NameHierarchyMapT = std::map, std::string>; -using MangledToSourceMapT = llvm::StringMap; +using MangledToSourceMapT = llvm::StringMap; void collect(const Module &M, const DICompileUnit &CU, NameHierarchyMapT &NameHierarchy) { @@ -60,7 +148,8 @@ void collect(const Module &M, const DICompileUnit &CU, } void match(semantics::Scope &Parent, NameHierarchyMapT::key_type &Names, - const NameHierarchyMapT &NameHierarchy, MangledToSourceMapT &Map) { + const NameHierarchyMapT &NameHierarchy, + const ProgramUnitCollector &Collector, MangledToSourceMapT &Map) { if (auto *S{Parent.symbol()}) { Names.push_back((Parent.kind() != semantics::Scope::Kind::MainProgram || !S->name().empty()) @@ -68,10 +157,16 @@ void match(semantics::Scope &Parent, NameHierarchyMapT::key_type &Names, : FlangTransformationContext::UnnamedProgramStub.str()); if (Parent.kind() == semantics::Scope::Kind::Subprogram || Parent.kind() == semantics::Scope::Kind::MainProgram) - if (auto I = NameHierarchy.find(Names); I != NameHierarchy.end()) - Map.try_emplace(I->second, S); + if (auto I = NameHierarchy.find(Names); I != NameHierarchy.end()) { + auto ParserUnit{Parent.kind() == semantics::Scope::Kind::MainProgram + ? Collector.getMainParserUnit() + : Collector.findParserUnit(S)}; + assert(ParserUnit && + "Representation of a program unit in AST must be known!"); + Map.try_emplace(I->second, ParserUnit, S); + } for (auto &Child : Parent.children()) - match(Child, Names, NameHierarchy, Map); + match(Child, Names, NameHierarchy, Collector, Map); Names.pop_back(); } } @@ -82,9 +177,11 @@ void FlangTransformationContext::initialize( assert(hasInstance() && "Transformation context is not configured!"); NameHierarchyMapT NameHierarchy; collect(M, CU, NameHierarchy); + ProgramUnitCollector V; + parser::Walk(mParsing->parseTree(), V); for (auto &Child : mContext->globalScope().children()) { NameHierarchyMapT::key_type Names; - match(Child, Names, NameHierarchy, mGlobals); + match(Child, Names, NameHierarchy, V, mGlobals); } mRewriter = std::make_unique(mParsing->cooked(), mParsing->allCooked()); diff --git a/lib/Transform/Flang/ConstantReplacement.cpp b/lib/Transform/Flang/ConstantReplacement.cpp index ee8d532f..848bba1a 100644 --- a/lib/Transform/Flang/ConstantReplacement.cpp +++ b/lib/Transform/Flang/ConstantReplacement.cpp @@ -69,9 +69,8 @@ class ConstantCollector { StringMap>; using KindToConstantMap = StringMap; public: - explicit ConstantCollector(const parser::CookedSource &Cooked, - const semantics::Symbol *Unit) - : mCooked(Cooked), mUnit(Unit) {} + explicit ConstantCollector(const parser::CookedSource &Cooked) + : mCooked(Cooked) {} template bool Pre(T &N) { PushBackIfArray(N); @@ -100,51 +99,19 @@ class ConstantCollector { bool Pre(parser::ProgramUnit &PU) { if (mIsProcessed) return false; - return std::visit( - common::visitors{ - [](auto &) { return false; }, - [this](common::Indirection &X) { - if (const auto &S{std::get< - std::optional>>( - X.value().t)}) - return (mIsProcessed = (S->statement.v.symbol == mUnit)); - return false; - }, - [this](common::Indirection &X) { - const auto &S{std::get>( - X.value().t)}; - const auto &Name{std::get(S.statement.t)}; - return (mIsProcessed = (Name.symbol == mUnit)); - }, - [this](common::Indirection &X) { - const auto &S{std::get>( - X.value().t)}; - const auto &Name{std::get(S.statement.t)}; - return (mIsProcessed = (Name.symbol == mUnit)); - }, - [](common::Indirection &M) { return true; }}, - PU.u); + return mIsProcessed = true; + } + + bool Pre(parser::InternalSubprogram &IS) { + if (mIsProcessed) + return false; + return mIsProcessed = true; } bool Pre(parser::ModuleSubprogram &MS) { if (mIsProcessed) return false; - return std::visit( - common::visitors{ - [](auto &) { return false; }, - [this](common::Indirection &X) { - const auto &S{std::get>( - X.value().t)}; - const auto &Name{std::get(S.statement.t)}; - return (mIsProcessed = (Name.symbol == mUnit)); - }, - [this](common::Indirection &X) { - const auto &S{std::get>( - X.value().t)}; - const auto &Name{std::get(S.statement.t)}; - return (mIsProcessed = (Name.symbol == mUnit)); - }}, - MS.u); + return mIsProcessed = true; } bool Pre(parser::Name &N) { @@ -216,7 +183,6 @@ class ConstantCollector { } const parser::CookedSource &mCooked; - const semantics::Symbol *mUnit{nullptr}; bool mIsProcessed{false}; SmallVector, 4> mNestedRefs; KindToConstantMap mConstantsToEliminate; @@ -255,22 +221,20 @@ bool FlangConstantReplacementPass::runOnFunction(Function &F) { << ": transformation context is not available\n"); return false; } - auto *ASTSub{TfmCtx->getDeclForMangledName(F.getName())}; - // TODO(kaniandr@gmail.com): the MAIN function without PROGRAM statement - // does not have a symbol - if (!ASTSub) { + auto ASTSub{TfmCtx->getDeclForMangledName(F.getName())}; + if (!ASTSub || ASTSub.isDeclaration()) { LLVM_DEBUG(dbgs() << "[CONST ELIMINATION]: skip function " << F.getName() - << ": source-level symbol is not available\n"); + << ": parse tree is not available\n"); return false; } auto &Cooked{TfmCtx->getParsing().cooked()}; auto &Rewriter{TfmCtx->getRewriter()}; - ConstantCollector DRV{TfmCtx->getParsing().cooked(), ASTSub}; - parser::Walk(*TfmCtx->getParsing().parseTree(), DRV); + ConstantCollector DRV{TfmCtx->getParsing().cooked()}; + ASTSub.visit([&DRV](auto & PU, auto & S) { parser::Walk(PU, DRV); }); if (DRV.getToEliminate().empty()) return false; if (!DRV.getFirstStmtProvenance()) { - toDiag(TfmCtx->getContext(), ASTSub->name(), + toDiag(TfmCtx->getContext(), ASTSub.getSemanticsUnit()->name(), tsar::diag::warn_fortran_no_execution_part); return false; } @@ -286,7 +250,7 @@ bool FlangConstantReplacementPass::runOnFunction(Function &F) { } StringSet Replacement; auto findSymbol = [&GS = TfmCtx->getContext().globalScope(), - &LS = *ASTSub->scope(), + &LS = *ASTSub.getSemanticsUnit()->scope(), &Replacement](StringRef Name) -> bool { if (auto *S{GS.FindSymbol(parser::CharBlock{Name.data(), Name.size()})}) return true; diff --git a/lib/Transform/Mixed/DummyScopeAAPass.cpp b/lib/Transform/Mixed/DummyScopeAAPass.cpp index dbde9c29..b57e9a9a 100644 --- a/lib/Transform/Mixed/DummyScopeAAPass.cpp +++ b/lib/Transform/Mixed/DummyScopeAAPass.cpp @@ -114,10 +114,11 @@ bool FlangDummyAliasAnalysis::runOnFunction(Function &F) { << ": transformation context is not available\n"); return false; } - auto *ASTSub{TfmCtx->getDeclForMangledName(F.getName())}; + auto ASTSub{TfmCtx->getDeclForMangledName(F.getName())}; if (!ASTSub) return false; - auto *Details{ASTSub->detailsIf()}; + auto *Details{ + ASTSub.getSemanticsUnit()->detailsIf()}; if (any_of(Details->dummyArgs(), [](auto *Dummy) { return Dummy->attrs().HasAny( {Fortran::semantics::Attr::TARGET, Fortran::semantics::Attr::POINTER}); From ad1193d96e81fc7c5438408f514552165eb0d24c Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Sun, 8 May 2022 23:41:29 +0300 Subject: [PATCH 38/93] [TSAR, Tfm, Flang, RegVar] Add pass to register variables. At this moment Flang doesn't emit full debug information about a program. So, we add calls to a SAPFOR intrinsic `sapfor_dbg_variable` to register all variables. Corresponding LLVM debug intrinsics can be inserted into LLVM IR later. --- include/tsar/Transform/Flang/Passes.h | 6 + lib/Transform/Flang/CMakeLists.txt | 3 +- lib/Transform/Flang/Passes.cpp | 1 + lib/Transform/Flang/VariableRegistration.cpp | 187 +++++++++++++++++++ 4 files changed, 196 insertions(+), 1 deletion(-) create mode 100644 lib/Transform/Flang/VariableRegistration.cpp diff --git a/include/tsar/Transform/Flang/Passes.h b/include/tsar/Transform/Flang/Passes.h index 8a46358c..85c17b86 100644 --- a/include/tsar/Transform/Flang/Passes.h +++ b/include/tsar/Transform/Flang/Passes.h @@ -43,6 +43,12 @@ void initializeFlangConstantReplacementPassPass(PassRegistry &Registry); /// Create a pass to replace constants with variables with values equal /// to the replaced constants. FunctionPass *createFlangConstantReplacementPass(); + +/// Initialize a pass to register all varaiables in a program unit. +void initializeFlangVariableRegistrationPassPass(PassRegistry &Registry); + +/// Create a pass to register all varaiables in a program unit. +FunctionPass *createFlangVariableRegistrationPass(); } // namespace llvm #endif//TSAR_FLANG_TRANSFORM_PASSES_H diff --git a/lib/Transform/Flang/CMakeLists.txt b/lib/Transform/Flang/CMakeLists.txt index 3d94c042..9761e707 100644 --- a/lib/Transform/Flang/CMakeLists.txt +++ b/lib/Transform/Flang/CMakeLists.txt @@ -1,4 +1,5 @@ -set(TRANSFORM_SOURCES Passes.cpp Format.cpp ConstantReplacement.cpp) +set(TRANSFORM_SOURCES Passes.cpp Format.cpp ConstantReplacement.cpp + VariableRegistration.cpp) if(MSVC_IDE) file(GLOB_RECURSE TRANSFORM_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/lib/Transform/Flang/Passes.cpp b/lib/Transform/Flang/Passes.cpp index 725904c3..ef7d5890 100644 --- a/lib/Transform/Flang/Passes.cpp +++ b/lib/Transform/Flang/Passes.cpp @@ -29,4 +29,5 @@ using namespace llvm; void llvm::initializeFlangTransform(PassRegistry &Registry) { initializeFlangConstantReplacementPassPass(Registry); + initializeFlangVariableRegistrationPassPass(Registry); } diff --git a/lib/Transform/Flang/VariableRegistration.cpp b/lib/Transform/Flang/VariableRegistration.cpp new file mode 100644 index 00000000..da1733ca --- /dev/null +++ b/lib/Transform/Flang/VariableRegistration.cpp @@ -0,0 +1,187 @@ +//===- VariableRegistration.cpp - Variabler Registration (Flang) -*- C++ -*===// +// +// Traits Static Analyzer (SAPFOR) +// +// Copyright 2022 DVM System Group +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +// +// This file implements source-to-source transformation to register variables. +// +//===----------------------------------------------------------------------===// + +#include "tsar/Analysis/Memory/Utils.h" +#include "tsar/Core/Query.h" +#include "tsar/Frontend/Flang/TransformationContext.h" +#include "tsar/Support/Flang/Diagnostic.h" +#include "tsar/Support/Flang/Rewriter.h" +#include "tsar/Support/MetadataUtils.h" +#include "tsar/Transform/Flang/Passes.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace Fortran; +using namespace llvm; +using namespace tsar; + +#undef DEBUG_TYPE +#define DEBUG_TYPE "flang-variable-registration" + +namespace { +class FlangVariableRegistrationPass : public FunctionPass, + private bcl::Uncopyable { +public: + static char ID; + FlangVariableRegistrationPass() : FunctionPass(ID) { + initializeFlangVariableRegistrationPassPass( + *PassRegistry::getPassRegistry()); + } + + bool runOnFunction(Function &F) override; + void getAnalysisUsage(AnalysisUsage &AU) const override; +}; + +class VariableCollector { +public: + explicit VariableCollector(const parser::CookedSource &Cooked) + : mCooked(Cooked) {} + + template bool Pre(T &N) { return true; } + + template void Post(T &N) {} + + bool Pre(parser::ExecutionPart &) { + mInExecutionPart = true; + return true; + } + + void Post(parser::ExecutionPart &) { mInExecutionPart = false; } + + template bool Pre(parser::Statement &S) { + if (mInExecutionPart) + if (auto Range{mCooked.GetProvenanceRange(S.source)}) + mLastStmt = Range->start() + Range->size(); + return true; + } + + std::optional getLastStmtProvenance() const { + return mLastStmt; + } + + bool Pre(parser::ProgramUnit &PU) { + if (mIsProcessed) + return false; + return mIsProcessed = true; + } + + bool Pre(parser::InternalSubprogram &IS) { + if (mIsProcessed) + return false; + return mIsProcessed = true; + } + + bool Pre(parser::ModuleSubprogram &MS) { + if (mIsProcessed) + return false; + return mIsProcessed = true; + } + +private: + const parser::CookedSource &mCooked; + bool mIsProcessed{false}; + bool mInExecutionPart{false}; + std::optional mLastStmt; +}; +} // namespace + +char FlangVariableRegistrationPass::ID = 0; +INITIALIZE_PASS_IN_GROUP_BEGIN(FlangVariableRegistrationPass, + "flang-variable-registration", + "Variable Registration (Flang)", false, false, + TransformationQueryManager::getPassRegistry()) +INITIALIZE_PASS_DEPENDENCY(TransformationEnginePass) +INITIALIZE_PASS_IN_GROUP_END(FlangVariableRegistrationPass, + "flang-variable-registration", + "Variable Registration (Flang)", false, false, + TransformationQueryManager::getPassRegistry()) + +bool FlangVariableRegistrationPass::runOnFunction(Function &F) { + auto *DISub{findMetadata(&F)}; + if (!DISub) + return false; + auto *CU{DISub->getUnit()}; + if (!isFortran(CU->getSourceLanguage())) + return false; + auto &TfmInfo{getAnalysis()}; + auto *TfmCtx{TfmInfo ? dyn_cast_or_null( + TfmInfo->getContext(*CU)) + : nullptr}; + if (!TfmCtx || !TfmCtx->hasInstance()) + return false; + auto ASTSub{TfmCtx->getDeclForMangledName(F.getName())}; + if (!ASTSub) + return false; + if (ASTSub.getSemanticsUnit()->scope()->GetSymbols().empty()) + return false; + auto &Cooked{TfmCtx->getParsing().cooked()}; + auto &Rewriter{TfmCtx->getRewriter()}; + VariableCollector DRV{TfmCtx->getParsing().cooked()}; + ASTSub.visit([&DRV](auto &PU, auto &S) { parser::Walk(PU, DRV); }); + // Check if execution part exists. + if (!DRV.getLastStmtProvenance()) + return false; + auto ToInsert{*DRV.getLastStmtProvenance()}; + std::string Register; + bool IsFirst{true}; + for (auto &S : ASTSub.getSemanticsUnit()->scope()->GetSymbols()) { + if (S->attrs().HasAny({Fortran::semantics::Attr::PARAMETER})) + continue; + if (const auto *D{S->detailsIf()}) { + if (IsFirst) { + IsFirst = false; + Register += "\n"; + } + if (TfmCtx->getOptions().isFixedForm) + Register.append(6, ' '); + Register += "call sapfor_dbg_variable(" + S->name().ToString() + ")"; + if (!TfmCtx->getOptions().isFixedForm) + Register += ";"; + Register += "\n"; + } + } + if (Register.empty()) + return false; + Register.resize(Register.size() - 1); + Rewriter.InsertText(ToInsert, Register, true); + return false; +} + +void FlangVariableRegistrationPass::getAnalysisUsage(AnalysisUsage &AU) const { + AU.addRequired(); + AU.setPreservesCFG(); +} + +FunctionPass *llvm::createFlangVariableRegistrationPass() { + return new FlangVariableRegistrationPass(); +} From c742592e2d47a41a58a51658846236efe88ad2b0 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Fri, 27 May 2022 15:40:46 +0300 Subject: [PATCH 39/93] [TSAR, CMake, Flang, Win] Enable building of Flang as a part of SAPFOR on Windows. --- CMakeLists.txt | 27 +++++++++++++++++++++++++-- cmake/llvm-utility.cmake | 2 +- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9498551c..676df82c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -97,18 +97,21 @@ cmake_dependent_option(BUILD_LLC "Build LLVM IR static compiler" OFF "NOT PACKAGE_LLVM" OFF) cmake_dependent_option(BUILD_LINK "Build LLVM bitcode linker" OFF "NOT PACKAGE_LLVM" OFF) -cmake_dependent_option(BUILD_FLANG "Build LLVM native Fortran compiler Flang" OFF UNIX OFF) +cmake_dependent_option(BUILD_FLANG "Build LLVM native Fortran compiler Flang" OFF + "NOT PACKAGE_LLVM" OFF) option(TSAR_ENABLE_LLVM_DUMP "Enable use of dump() method for LLVM types" ON) set(LLVM_SOURCE_DIR "${LLVM_PROJECT_DIR}/llvm") set(CLANG_SOURCE_DIR "${LLVM_PROJECT_DIR}/clang") set(FLANG_SOURCE_DIR "${LLVM_PROJECT_DIR}/flang") +set(MLIR_SOURCE_DIR "${LLVM_PROJECT_DIR}/mlir") set(COMPILER_RT_SOURCE_DIR "${LLVM_PROJECT_DIR}/compiler-rt") set(LLVM_BINARY_DIR "${CMAKE_BINARY_DIR}/llvm-build") set(CLANG_BINARY_DIR "${LLVM_BINARY_DIR}/tools/clang") set(FLANG_BINARY_DIR "${LLVM_BINARY_DIR}/tools/flang") +set(MLIR_BINARY_DIR "${LLVM_BINARY_DIR}/tools/mlir") set(FLANG_FOUND OFF) @@ -117,6 +120,7 @@ if(PACKAGE_LLVM) set(LLVM_SOURCE_DIR "${LLVM_BINARY_DIR}/include/llvm") set(CLANG_SOURCE_DIR "${LLVM_BINARY_DIR}/include/clang") set(FLANG_SOURCE_DIR "${LLVM_BINARY_DIR}/include/flang") + set(MLIR_SOURCE_DIR "${LLVM_BINARY_DIR}/include/mlir") set(CLANG_INCLUDE_DIR "${LLVM_BINARY_DIR}/lib/clang/${LLVM_VERSION}/include") set(CLANG_EXECUTABLE "${LLVM_BINARY_DIR}/bin/clang${CMAKE_EXECUTABLE_SUFFIX}") if (EXISTS ${FLANG_SOURCE_DIR}) @@ -144,6 +148,9 @@ else() if (BUILD_FLANG AND NOT EXISTS ${FLANG_SOURCE_DIR}) message(FATAL_ERROR "FLANG_SOURCE_DIR '${FLANG_SOURCE_DIR}' does not exist") endif() + if (BUILD_FLANG AND NOT EXISTS ${MLIR_SOURCE_DIR}) + message(FATAL_ERROR "MLIR_SOURCE_DIR '${MLIR_SOURCE_DIR}' does not exist") + endif() if (BUILD_PROFILE AND NOT EXISTS ${COMPILER_RT_SOURCE_DIR}) message(FATAL_ERROR "COMPILER_RT_SOURCE_DIR '${COMPILER_RT_SOURCE_DIR}' does not exist") endif() @@ -255,7 +262,20 @@ if(NOT PACKAGE_LLVM) endif() if(NOT PACKAGE_LLVM AND FLANG_FOUND) list(APPEND LLVM_INCLUDE_DIRS - ${FLANG_SOURCE_DIR}/include ${FLANG_BINARY_DIR}/include) + ${FLANG_SOURCE_DIR}/include ${FLANG_BINARY_DIR}/include + ${MLIR_SOURCE_DIR}/include ${MLIR_BINARY_DIR}/include) +# TODO: LLVM specify include directories relative to build path, however +# in case of SAPFOR build path is not a build path for LLVM. +# Hence we manually add correct include directories for Flang libraries. + target_include_directories(obj.FIRBuilder SYSTEM PRIVATE ${LLVM_INCLUDE_DIRS}) + target_include_directories(obj.FIRCodeGen SYSTEM PRIVATE ${LLVM_INCLUDE_DIRS}) + target_include_directories(obj.FIRDialect SYSTEM PRIVATE ${LLVM_INCLUDE_DIRS}) + target_include_directories(obj.FIRSupport SYSTEM PRIVATE ${LLVM_INCLUDE_DIRS}) + target_include_directories(obj.FIRTransforms SYSTEM PRIVATE ${LLVM_INCLUDE_DIRS}) + target_include_directories(obj.flangFrontend SYSTEM PRIVATE ${LLVM_INCLUDE_DIRS}) + target_include_directories(obj.flangFrontendTool SYSTEM PRIVATE ${LLVM_INCLUDE_DIRS}) + target_include_directories(obj.FortranLower SYSTEM PRIVATE ${LLVM_INCLUDE_DIRS}) + target_include_directories(flang-new SYSTEM PRIVATE ${LLVM_INCLUDE_DIRS}) endif() include_directories(${LLVM_INCLUDE_DIRS}) @@ -304,6 +324,9 @@ if (MSVC) add_compile_options("/MD") endif() endif() + if (MSVC_VERSION GREATER_EQUAL 1920) + add_compile_options("/Zc:__cplusplus") + endif() endif() foreach(C ${CMAKE_CONFIGURATION_TYPES}) diff --git a/cmake/llvm-utility.cmake b/cmake/llvm-utility.cmake index cf88900d..0abdf767 100644 --- a/cmake/llvm-utility.cmake +++ b/cmake/llvm-utility.cmake @@ -42,7 +42,7 @@ macro(sapfor_install_llvm) endif() if(BUILD_FLANG) - add_external_tool(FLANG_BUILD flang) + add_external_tool(FLANG_BUILD flang-new) endif() if(BUILD_PROFILE) From 19095ab0e2ee4a6c09b5582b568bcf1010df9843 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Sat, 28 May 2022 02:04:51 +0300 Subject: [PATCH 40/93] [TSAR, CMake, Flang] Fix, trivial. --- lib/Frontend/Flang/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Frontend/Flang/CMakeLists.txt b/lib/Frontend/Flang/CMakeLists.txt index 38d779c7..fab6a35b 100644 --- a/lib/Frontend/Flang/CMakeLists.txt +++ b/lib/Frontend/Flang/CMakeLists.txt @@ -3,7 +3,7 @@ set(FRONTEND_SOURCES TransformationContext.cpp Tooling.cpp Action.cpp) if(MSVC_IDE) file(GLOB_RECURSE FRONTEND_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} - ${PROJECT_SOURCE_DIR}/include/tsar/Frontend/Clang/*.h) + ${PROJECT_SOURCE_DIR}/include/tsar/Frontend/Flang/*.h) file(GLOB_RECURSE FRONTEND_INTERNAL_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.h) source_group(bcl FILES ${BCL_CORE_HEADERS}) From aa717aabd4fc984cc56cdff8f28fc8a150b804bd Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Mon, 30 May 2022 16:55:20 +0300 Subject: [PATCH 41/93] [TSAR, Clang, Frontend] Compute mangled names directly to store correspondence between IR and AST. Previouse implementations relied on a location of a function declaration to match mangled names with symbols. --- lib/Frontend/Flang/TransformationContext.cpp | 95 ++++++++++---------- 1 file changed, 49 insertions(+), 46 deletions(-) diff --git a/lib/Frontend/Flang/TransformationContext.cpp b/lib/Frontend/Flang/TransformationContext.cpp index 31b802ef..01856e39 100644 --- a/lib/Frontend/Flang/TransformationContext.cpp +++ b/lib/Frontend/Flang/TransformationContext.cpp @@ -25,11 +25,14 @@ #include "tsar/Frontend/Flang/TransformationContext.h" #include "tsar/Support/Flang/Diagnostic.h" #include +#include #include #include #include #include +#define DEBUG_TYPE "flang-transformation" + using namespace tsar; using namespace llvm; using namespace Fortran; @@ -121,68 +124,68 @@ class ProgramUnitCollector { mUnits; }; -using NameHierarchyMapT = std::map, std::string>; +using IntrinsicMapT = llvm::StringMap>; using MangledToSourceMapT = llvm::StringMap; -void collect(const Module &M, const DICompileUnit &CU, - NameHierarchyMapT &NameHierarchy) { - for (auto &F : M) { - if (auto *DISub = F.getSubprogram(); DISub && DISub->getUnit() == &CU) { - NameHierarchyMapT::key_type Key; - DIScope *Scope{DISub}; - do { - if (Scope->getName().empty() && - (!FlangTransformationContext::UnnamedProgramStub.empty() || - !isa(Scope) || - !cast(Scope)->isMainSubprogram())) - break; - Key.push_back(std::string{Scope->getName()}); - Scope = Scope->getScope(); - } while (Scope != &CU && Scope); - if (Scope != &CU) - continue; - std::reverse(Key.begin(), Key.end()); - NameHierarchy.try_emplace(Key, F.getName()); - } - } +static std::string getMangledName(const Fortran::semantics::Symbol &S) { + const std::string *BindName = S.GetBindName(); + return BindName ? *BindName : Fortran::lower::mangle::mangleName(S); } -void match(semantics::Scope &Parent, NameHierarchyMapT::key_type &Names, - const NameHierarchyMapT &NameHierarchy, - const ProgramUnitCollector &Collector, MangledToSourceMapT &Map) { - if (auto *S{Parent.symbol()}) { - Names.push_back((Parent.kind() != semantics::Scope::Kind::MainProgram || - !S->name().empty()) - ? S->name().ToString() - : FlangTransformationContext::UnnamedProgramStub.str()); - if (Parent.kind() == semantics::Scope::Kind::Subprogram || - Parent.kind() == semantics::Scope::Kind::MainProgram) - if (auto I = NameHierarchy.find(Names); I != NameHierarchy.end()) { - auto ParserUnit{Parent.kind() == semantics::Scope::Kind::MainProgram +void match(semantics::Scope &Parent, const ProgramUnitCollector &Collector, + IntrinsicMapT &Intrinsics, MangledToSourceMapT &Map) { +#ifdef LLVM_DEBUG + auto log = [](semantics::Symbol &S, StringRef MangledName, bool Add) { + dbgs() << "[FLANG TRANSFORMATION]: " << (Add ? "add" : "ignore") + << " demangled symbol '" << S.GetUltimate().name().ToString() + << "' for '" << MangledName << "' [" << &S << "]\n"; + }; +#endif + for (auto &&[Name, S] : Parent) + if (S->test(semantics::Symbol::Flag::Function) || + S->test(semantics::Symbol::Flag::Subroutine) || + S->has()) + if (!S->attrs().test(semantics::Attr::INTRINSIC)) { + auto ParserUnit{S->has() ? Collector.getMainParserUnit() - : Collector.findParserUnit(S)}; - assert(ParserUnit && - "Representation of a program unit in AST must be known!"); - Map.try_emplace(I->second, ParserUnit, S); + : Collector.findParserUnit(&*S)}; + [[maybe_unused]] auto Info{ + Map.try_emplace(getMangledName(S->GetUltimate()), ParserUnit, &*S)}; + LLVM_DEBUG(log(*S, getMangledName(S->GetUltimate()), Info.second)); + } else if (auto I{Intrinsics.find(S->GetUltimate().name().ToString())}; + I != Intrinsics.end()) { + for (auto *F : I->second) { + [[maybe_unused]] auto Info{ + Map.try_emplace(F->getName(), nullptr, &*S)}; + LLVM_DEBUG(log(*S, F->getName(), Info.second)); + } } - for (auto &Child : Parent.children()) - match(Child, Names, NameHierarchy, Collector, Map); - Names.pop_back(); + for (auto &&[Name, S] : Parent.commonBlocks()) { + [[maybe_unused]] auto Info{ + Map.try_emplace(getMangledName(S->GetUltimate()), nullptr, &*S)}; + LLVM_DEBUG(log(*S, getMangledName(S->GetUltimate()), Info.second)); } + for (auto &Child : Parent.children()) + match(Child, Collector, Intrinsics, Map); } } void FlangTransformationContext::initialize( const Module &M, const DICompileUnit &CU) { assert(hasInstance() && "Transformation context is not configured!"); - NameHierarchyMapT NameHierarchy; - collect(M, CU, NameHierarchy); ProgramUnitCollector V; parser::Walk(mParsing->parseTree(), V); - for (auto &Child : mContext->globalScope().children()) { - NameHierarchyMapT::key_type Names; - match(Child, Names, NameHierarchy, V, mGlobals); + IntrinsicMapT Intrinsics; + for (auto &F : M) { + StringRef Prefix{"fir."}; + if (F.getName().startswith(Prefix)) { + auto GeneralName{F.getName().drop_front(Prefix.size())}; + auto GeneralNameEnd(F.getName().find(".")); + GeneralName = GeneralName.substr(0, GeneralNameEnd); + Intrinsics.try_emplace(GeneralName).first->second.push_back(&F); + } } + match(mContext->globalScope(), V, Intrinsics, mGlobals); mRewriter = std::make_unique(mParsing->cooked(), mParsing->allCooked()); } From 61130d883ba4f9eb6508dc95dc64b9efe517627e Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Tue, 31 May 2022 12:51:15 +0300 Subject: [PATCH 42/93] [TSAR, AST] Implement base for Flang and Clang matchers in a similar way. --- include/tsar/Analysis/AST/Matcher.h | 155 ++++++++++++++ .../tsar/Analysis/Clang/ExpressionMatcher.h | 6 +- include/tsar/Analysis/Clang/Matcher.h | 197 ++++-------------- include/tsar/Analysis/Flang/Matcher.h | 101 +++++++++ .../tsar/Support/Clang/PresumedLocationInfo.h | 48 +++++ include/tsar/Support/DILocationMapInfo.h | 121 +++++++++++ .../tsar/Support/Flang/PresumedLocationInfo.h | 48 +++++ lib/Analysis/Clang/DIMemoryMatcher.cpp | 18 +- lib/Analysis/Clang/ExpressionMatcher.cpp | 11 +- lib/Analysis/Clang/LoopMatcher.cpp | 20 +- lib/Analysis/Clang/MemoryMatcher.cpp | 10 +- 11 files changed, 543 insertions(+), 192 deletions(-) create mode 100644 include/tsar/Analysis/AST/Matcher.h create mode 100644 include/tsar/Analysis/Flang/Matcher.h create mode 100644 include/tsar/Support/Clang/PresumedLocationInfo.h create mode 100644 include/tsar/Support/DILocationMapInfo.h create mode 100644 include/tsar/Support/Flang/PresumedLocationInfo.h diff --git a/include/tsar/Analysis/AST/Matcher.h b/include/tsar/Analysis/AST/Matcher.h new file mode 100644 index 00000000..79ea0e2d --- /dev/null +++ b/include/tsar/Analysis/AST/Matcher.h @@ -0,0 +1,155 @@ +//===-- Matcher.h --------- High and Low Level Matcher ----------*- C++ -*-===// +// +// Traits Static Analyzer (SAPFOR) +// +// Copyright 2022 DVM System Group +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// +//===----------------------------------------------------------------------===// +// +// This file defines general classes and functions to match some entities +// (loops, variables, etc.) in a source high-level code and appropriate entities +// (loops, allocas, etc.) in low-level LLVM IR. +// +//===----------------------------------------------------------------------===// + +#ifndef TSAR_MATCHER_H +#define TSAR_MATCHER_H + +#include "tsar/ADT/Bimap.h" +#include "tsar/Support/DILocationMapInfo.h" +#include "tsar/Support/Tags.h" +#include "tsar/Support/MetadataUtils.h" +#include +#include +#include +#include +#include + +namespace tsar { +/// This is a base class which is inherited to match different entities (loops, +/// variables, etc.). +/// +/// The ImplTy class must inherit this class and implement following methods: +/// - typename LocToIRMap::iterator findItrForLocation(ASTLocationTy Loc); +/// - PresumedLocationTy getPresumedLocation(RawLocationTy Loc); +/// Note, that tsar::PresumedLocationInfo<...> template must be specialized for +/// the PresumedLocationTy type. +template, + typename MatcherTy = Bimap< + bcl::tagged, bcl::tagged>, + typename UnmatchedASTSetTy = llvm::DenseSet> +class MatchASTBase { +public: + using Matcher = MatcherTy; + + using UnmatchedASTSet = UnmatchedASTSetTy; + + /// This is a map from entity location to a queue of IR entities. + using LocToIRMap = + llvm::DenseMap, + llvm::TinyPtrVector, + llvm::SmallVector>, + IRLocationMapInfo>; + + /// This is a map from location in a source file to an queue of AST + /// entities, which are associated with this location. + /// + /// The key in this map is a raw encoding for location. + using LocToASTMap = + llvm::DenseMap, + llvm::TinyPtrVector, + llvm::SmallVector>, + RawLocationMapInfo>; + + /// \brief Constructor. + /// + /// \param[in, out] M Representation of match. + /// \param[in, out] UM Storage for unmatched ast entities. + /// \param[in, out] LocToIR Map from entity location to a queue + /// of IR entities. + /// \param[in, out] LocToMacro A map form entity expansion location to a queue + /// of AST entities. All entities explicitly (not implicit loops) defined in + /// macros is going to store in this map. The key in this map is a raw + /// encoding for expansion location. + MatchASTBase(Matcher &M, UnmatchedASTSet &UM, + LocToIRMap &LocToIR, LocToASTMap &LocToMacro) : + mMatcher(&M), mUnmatchedAST(&UM), + mLocToIR(&LocToIR), mLocToMacro(&LocToMacro) {} + + /// Finds low-level representation of an entity at the specified location. + /// + /// \return LLVM IR for an entity or `nullptr`. + IRItemTy findIRForLocation(ASTLocationTy Loc) { + auto LocItr = static_cast(this)->findItrForLocation(Loc); + if (LocItr == mLocToIR->end()) + return nullptr; + auto Res = LocItr->second.back(); + LocItr->second.pop_back(); + return Res; + } + + /// Evaluates entities located in macros. + /// + /// This matches entities from mLocToMacro and mLocToIR. It is recommended + /// to evaluate at first all entities outside macros and then consider macros + /// separately. + /// + /// \param [in] Strict If it is true, macros, containing exactly a single + /// item, are processed. + void matchInMacro(llvm::Statistic &NumMatch, llvm::Statistic &NumNonMatchAST, + llvm::Statistic &NumNonMatchIR, bool Strict = false) { + for (auto &InMacro : *mLocToMacro) { + auto PLoc{static_cast(this)->getPresumedLoc(InMacro.first)}; + auto IREntityItr = mLocToIR->find_as(PLoc); + // If sizes of queues of AST and IR entities are not equal this is mean + // that there are implicit entities (for example, implicit loops) in + // a macro. Such entities are not going to be evaluated due to necessity + // of additional analysis of AST. + if (IREntityItr == mLocToIR->end() || + IREntityItr->second.size() != InMacro.second.size() || + Strict && InMacro.second.size() > 1) { + NumNonMatchAST += InMacro.second.size(); + while (!InMacro.second.empty()) { + mUnmatchedAST->insert(InMacro.second.back()); + InMacro.second.pop_back(); + } + } else { + NumMatch += InMacro.second.size(); + NumNonMatchIR -= InMacro.second.size(); + while (!InMacro.second.empty()) { + mMatcher->emplace(InMacro.second.back(), IREntityItr->second.back()); + InMacro.second.pop_back(); + IREntityItr->second.pop_back(); + } + } + } + } + +protected: + Matcher *mMatcher; + UnmatchedASTSet *mUnmatchedAST; + LocToIRMap *mLocToIR; + LocToASTMap *mLocToMacro; +}; +} +#endif//TSAR_MATCHER_H diff --git a/include/tsar/Analysis/Clang/ExpressionMatcher.h b/include/tsar/Analysis/Clang/ExpressionMatcher.h index 6c465b74..d64964d8 100644 --- a/include/tsar/Analysis/Clang/ExpressionMatcher.h +++ b/include/tsar/Analysis/Clang/ExpressionMatcher.h @@ -33,8 +33,8 @@ #include #include -#ifndef TSAR_EXPRESSION_MATCHER_H -#define TSAR_EXPRESSION_MATCHER_H +#ifndef TSAR_CLANG_EXPRESSION_MATCHER_H +#define TSAR_CLANG_EXPRESSION_MATCHER_H namespace tsar { class ClangTransformationContext; @@ -88,4 +88,4 @@ class ClangExprMatcherPass : } -#endif//TSAR_EXPRESSION_MATCHER_H +#endif//TSAR_CLANG_EXPRESSION_MATCHER_H diff --git a/include/tsar/Analysis/Clang/Matcher.h b/include/tsar/Analysis/Clang/Matcher.h index 4a716960..aeb3d8c1 100644 --- a/include/tsar/Analysis/Clang/Matcher.h +++ b/include/tsar/Analysis/Clang/Matcher.h @@ -1,4 +1,4 @@ -//===-- Matcher.h --------- High and Low Level Matcher ----------*- C++ -*-===// +//===-- Matcher.h --------- High and Low Level Matcher (Clang) --*- C++ -*-===// // // Traits Static Analyzer (SAPFOR) // @@ -25,112 +25,38 @@ // //===----------------------------------------------------------------------===// -#ifndef TSAR_MATCHER_H -#define TSAR_MATCHER_H +#ifndef TSAR_CLANG_MATCHER_H +#define TSAR_CLANG_MATCHER_H -#include "tsar/ADT/Bimap.h" -#include "tsar/Support/Tags.h" -#include "tsar/Support/MetadataUtils.h" +#include "tsar/Analysis/AST/Matcher.h" +#include "tsar/Support/Clang/PresumedLocationInfo.h" #include -#include -#include -#include -#include -#include -#include -#include - -namespace llvm { -/// \brief Implementation of a DenseMapInfo for DILocation *. -/// -/// To generate hash value pair of line and column is used. It is possible to -/// use find_as() method with a parameter of type clang::PresumedLoc. -struct DILocationMapInfo { - static inline DILocation * getEmptyKey() { - return DenseMapInfo::getEmptyKey(); - } - static inline DILocation * getTombstoneKey() { - return DenseMapInfo::getTombstoneKey(); - } - static unsigned getHashValue(const DILocation *Loc) { - auto Line = Loc->getLine(); - auto Column = Loc->getColumn(); - auto Pair = std::make_pair(Line, Column); - return DenseMapInfo::getHashValue(Pair); - } - static unsigned getHashValue(const clang::PresumedLoc &PLoc) { - auto Line = PLoc.getLine(); - auto Column = PLoc.getColumn(); - auto Pair = std::make_pair(Line, Column); - return DenseMapInfo::getHashValue(Pair); - } - static bool isEqual(const DILocation *LHS, const DILocation *RHS) { - if (LHS == RHS) - return true; - auto TK = getTombstoneKey(); - auto EK = getEmptyKey(); - if (RHS == TK || LHS == TK || RHS == EK || LHS == EK || - LHS->getLine() != RHS->getLine() || - LHS->getColumn() != RHS->getColumn()) - return false; - sys::fs::UniqueID LHSId, RHSId; - SmallString<128> LHSPath, RHSPath; - return !sys::fs::getUniqueID( - tsar::getAbsolutePath(*LHS->getScope(), LHSPath), LHSId) && - !sys::fs::getUniqueID( - tsar::getAbsolutePath(*RHS->getScope(), RHSPath), RHSId) && - LHSId == RHSId; - } - static bool isEqual(const clang::PresumedLoc &LHS, const DILocation *RHS) { - if (isEqual(RHS, getTombstoneKey()) || isEqual(RHS, getEmptyKey()) || - LHS.getLine() != RHS->getLine() || LHS.getColumn() != RHS->getColumn()) - return false; - sys::fs::UniqueID LHSId, RHSId; - SmallString<128> LHSPath, RHSPath; - return !sys::fs::getUniqueID(LHS.getFilename(), LHSId) && - !sys::fs::getUniqueID( - tsar::getAbsolutePath(*RHS->getScope(), RHSPath), RHSId) && - LHSId == RHSId; - } -}; -} namespace tsar { /// This is a base class which is inherited to match different entities (loops, /// variables, etc.). -template, - class MatcherTy = Bimap< - bcl::tagged, bcl::tagged>, - class UnmatchedASTSetTy = llvm::DenseSet> -class MatchASTBase { -public: - using Matcher = MatcherTy; +template , + typename MatcherTy = + Bimap, bcl::tagged>, + typename UnmatchedASTSetTy = llvm::DenseSet> +class ClangMatchASTBase + : public MatchASTBase { - using UnmatchedASTSet = UnmatchedASTSetTy; + using BaseT = MatchASTBase; - /// This is a map from entity location to a queue of IR entities. - using LocToIRMap = - llvm::DenseMap, - llvm::TinyPtrVector, - llvm::SmallVector>, - IRLocationMapInfo>; - - /// \brief This is a map from location in a source file to an queue of AST - /// entities, which are associated with this location. - /// - /// The key in this map is a raw encoding for location. - /// To decode it use SourceLocation::getFromRawEncoding() method. - using LocToASTMap = - llvm::DenseMap, - llvm::TinyPtrVector, - llvm::SmallVector>, - ASTLocationMapInfo>; +public: + using typename BaseT::LocToASTMap; + using typename BaseT::LocToIRMap; + using typename BaseT::Matcher; + using typename BaseT::UnmatchedASTSet; /// \brief Constructor. /// @@ -143,77 +69,28 @@ class MatchASTBase { /// of AST entities. All entities explicitly (not implicit loops) defined in /// macros is going to store in this map. The key in this map is a raw /// encoding for expansion location. - MatchASTBase(clang::SourceManager &SrcMgr, Matcher &M, UnmatchedASTSet &UM, - LocToIRMap &LocToIR, LocToASTMap &LocToMacro) : - mSrcMgr(&SrcMgr), mMatcher(&M), mUnmatchedAST(&UM), - mLocToIR(&LocToIR), mLocToMacro(&LocToMacro) {} - - /// Finds low-level representation of an entity at the specified location. - /// - /// \return LLVM IR for an entity or `nullptr`. - IRItemTy findIRForLocation(clang::SourceLocation Loc) { - auto LocItr = findItrForLocation(Loc); - if (LocItr == mLocToIR->end()) - return nullptr; - auto Res = LocItr->second.back(); - LocItr->second.pop_back(); - return Res; - } + ClangMatchASTBase(clang::SourceManager &SrcMgr, Matcher &M, + UnmatchedASTSet &UM, LocToIRMap &LocToIR, + LocToASTMap &LocToMacro) + : BaseT(M, UM, LocToIR, LocToMacro), mSrcMgr(&SrcMgr) {} /// Finds low-level representation of an entity at the specified location. /// /// \return Iterator to an element in LocToIRMap. typename LocToIRMap::iterator findItrForLocation(clang::SourceLocation Loc) { if (Loc.isInvalid()) - return mLocToIR->end(); - auto PLoc = mSrcMgr->getPresumedLoc(Loc, false); - return mLocToIR->find_as(PLoc); + return BaseT::mLocToIR->end(); + auto PLoc{mSrcMgr->getPresumedLoc(Loc, false)}; + return BaseT::mLocToIR->find_as(PLoc); } - /// Evaluates entities located in macros. - /// - /// This matches entities from mLocToMacro and mLocToIR. It is recommended - /// to evaluate at first all entities outside macros and then consider macros - /// separately. - /// - /// \param [in] Strict If it is true, macros, containing exactly a single - /// item, are processed. - void matchInMacro(llvm::Statistic &NumMatch, llvm::Statistic &NumNonMatchAST, - llvm::Statistic &NumNonMatchIR, bool Strict = false) { - for (auto &InMacro : *mLocToMacro) { - clang:: PresumedLoc PLoc = mSrcMgr->getPresumedLoc( - clang::SourceLocation::getFromRawEncoding(InMacro.first), false); - auto IREntityItr = mLocToIR->find_as(PLoc); - // If sizes of queues of AST and IR entities are not equal this is mean - // that there are implicit entities (for example, implicit loops) in - // a macro. Such entities are not going to be evaluated due to necessity - // of additional analysis of AST. - if (IREntityItr == mLocToIR->end() || - IREntityItr->second.size() != InMacro.second.size() || - Strict && InMacro.second.size() > 1) { - NumNonMatchAST += InMacro.second.size(); - while (!InMacro.second.empty()) { - mUnmatchedAST->insert(InMacro.second.back()); - InMacro.second.pop_back(); - } - } else { - NumMatch += InMacro.second.size(); - NumNonMatchIR -= InMacro.second.size(); - while (!InMacro.second.empty()) { - mMatcher->emplace(InMacro.second.back(), IREntityItr->second.back()); - InMacro.second.pop_back(); - IREntityItr->second.pop_back(); - } - } - } + clang::PresumedLoc getPresumedLoc(RawLocationTy Loc) { + return mSrcMgr->getPresumedLoc( + clang::SourceLocation::getFromRawEncoding(Loc), false); } protected: clang::SourceManager *mSrcMgr; - Matcher *mMatcher; - UnmatchedASTSet *mUnmatchedAST; - LocToIRMap *mLocToIR; - LocToASTMap *mLocToMacro; }; -} -#endif//TSAR_MATCHER_H +} // namespace tsar +#endif // TSAR_CLANG_MATCHER_H diff --git a/include/tsar/Analysis/Flang/Matcher.h b/include/tsar/Analysis/Flang/Matcher.h new file mode 100644 index 00000000..a1cece1d --- /dev/null +++ b/include/tsar/Analysis/Flang/Matcher.h @@ -0,0 +1,101 @@ +//===-- Matcher.h --------- High and Low Level Matcher (Flang) --*- C++ -*-===// +// +// Traits Static Analyzer (SAPFOR) +// +// Copyright 2022 DVM System Group +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// +//===----------------------------------------------------------------------===// +// +// This file defines general classes and functions to match some entities +// (loops, variables, etc.) in a source high-level code and appropriate entities +// (loops, allocas, etc.) in low-level LLVM IR. +// +//===----------------------------------------------------------------------===// + +#ifndef TSAR_FLANG_MATCHER_H +#define TSAR_FLANG_MATCHER_H + +#include "tsar/Analysis/AST/Matcher.h" +#include "tsar/Support/Flang/PresumedLocationInfo.h" +#include + +namespace tsar { +/// This is a base class which is inherited to match different entities (loops, +/// variables, etc.). +template , + typename MatcherTy = + Bimap, bcl::tagged>, + typename UnmatchedASTSetTy = llvm::DenseSet> +class FlangMatchASTBase + : public MatchASTBase { + + using BaseT = + MatchASTBase; + +public: + using typename BaseT::LocToASTMap; + using typename BaseT::LocToIRMap; + using typename BaseT::Matcher; + using typename BaseT::UnmatchedASTSet; + + /// \brief Constructor. + /// + /// \param[in] AllCooked Flang source manager to deal with locations. + /// \param[in, out] M Representation of match. + /// \param[in, out] UM Storage for unmatched ast entities. + /// \param[in, out] LocToIR Map from entity location to a queue + /// of IR entities. + /// \param[in, out] LocToMacro A map form entity expansion location to a queue + /// of AST entities. All entities explicitly (not implicit loops) defined in + /// macros is going to store in this map. The key in this map is a raw + /// encoding for expansion location. + FlangMatchASTBase(Fortran::parser::AllCookedSources &AllCooked, Matcher &M, + UnmatchedASTSet &UM, LocToIRMap &LocToIR, + LocToASTMap &LocToMacro) + : BaseT(M, UM, LocToIR, LocToMacro), mAllCooked(&AllCooked) {} + + /// Finds low-level representation of an entity at the specified location. + /// + /// \return Iterator to an element in LocToIRMap. + typename LocToIRMap::iterator + findItrForLocation(Fortran::parser::Provenance Loc) { + if (!mAllCooked->allSources().IsValid(Loc)) + return BaseT::mLocToIR->end(); + auto PLoc{mAllCooked->allSources().GetSourcePosition(Loc)}; + if (!PLoc) + return BaseT::mLocToIR->end(); + return BaseT::mLocToIR->find_as(*PLoc); + } + + Fortran::parser::SourcePosition getPresumedLoc(RawLocationTy Loc) { + return *mAllCooked->allSources().GetSourcePosition( + Fortran::parser::Provenance{Loc}); + } + +protected: + Fortran::parser::AllCookedSources *mAllCooked; +}; +} // namespace tsar +#endif // TSAR_FLANG_MATCHER_H diff --git a/include/tsar/Support/Clang/PresumedLocationInfo.h b/include/tsar/Support/Clang/PresumedLocationInfo.h new file mode 100644 index 00000000..bfd6872a --- /dev/null +++ b/include/tsar/Support/Clang/PresumedLocationInfo.h @@ -0,0 +1,48 @@ +//===-- PresumedLocationInfo.h - Type Traits for llvm::DenseMap -*- C++ -*-===// +// +// Traits Static Analyzer (SAPFOR) +// +// Copyright 2022 DVM System Group +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// +//===----------------------------------------------------------------------===// +// +// This file provide implementation of llvm::DenseMapInfo to compare different +// representations of a presumed location with a metadata location +// llvm::DILocation. +// +//===----------------------------------------------------------------------===// + +#ifndef TSAR_CLANG_PRESUMED_LOCATION_INFO_H +#define TSAR_CLANG_PRESUMED_LOCATION_INFO_H + +#include "tsar/Support/DILocationMapInfo.h" +#include + +namespace tsar { +template <> struct PresumedLocationInfo { + static unsigned getLine(const clang::PresumedLoc &Loc) { + return Loc.getLine(); + } + static unsigned getColumn(const clang::PresumedLoc &Loc) { + return Loc.getColumn(); + } + static llvm::StringRef getFilename(const clang::PresumedLoc &Loc) { + return Loc.getFilename(); + } +}; +} // namespace tsar + +#endif//TSAR_CLANG_PRESUMED_LOCATION_INFO_H diff --git a/include/tsar/Support/DILocationMapInfo.h b/include/tsar/Support/DILocationMapInfo.h new file mode 100644 index 00000000..97dd6441 --- /dev/null +++ b/include/tsar/Support/DILocationMapInfo.h @@ -0,0 +1,121 @@ +//===-- DILocationMapInfo.h - Type Traits for llvm::DenseMap ----*- C++ -*-===// +// +// Traits Static Analyzer (SAPFOR) +// +// Copyright 2022 DVM System Group +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// +//===----------------------------------------------------------------------===// +// +// This file provide implementation of llvm::DenseMapInfo to compare different +// representations of a presumed location with a metadata location +// llvm::DILocation. +// +//===----------------------------------------------------------------------===// + +#ifndef TSAR_DI_LOCATION_MAP_INFO_H +#define TSAR_DI_LOCATION_MAP_INFO_H + +#include "tsar/Support/MetadataUtils.h" +#include +#include +#include +#include +#include + +namespace tsar { +/// This class should be specialized for loction-like classes which are intened +/// to use as a key in llvm::DenseMap based on tsar::DILocationMapInfo traits. +/// +/// The following methods must be provided: +/// - static unsigned getLine(const LocationT &Loc); +/// - static unsigned getColumn(const LocationT &Loc); +/// - static llvm::StringRef getFilename(const LocationT &Loc); +template struct PresumedLocationInfo { + /// If anyone tries to use this class without having an appropriate + /// specialization, make an error. + using LocationType = typename LocationT::UnknownLocationError; +}; + +template <> struct PresumedLocationInfo { + static unsigned getLine(const llvm::DILocation *Loc) { + return Loc->getLine(); + } + static unsigned getColumn(const llvm::DILocation *Loc) { + return Loc->getColumn(); + } + static llvm::SmallString<128> getFilename(const llvm::DILocation *Loc) { + llvm::SmallString<128> Path; + tsar::getAbsolutePath(*Loc->getScope(), Path); + return Path; + } +}; + +template <> +struct PresumedLocationInfo + : public PresumedLocationInfo {}; + +/// Implementation of a DenseMapInfo for DILocation *. +/// +/// To generate hash value pair of line and column is used. It is possible to +/// use find_as() method with a parameter of type with specified +/// tsar::PresumedLocationInfo template. +struct DILocationMapInfo { + static inline llvm::DILocation *getEmptyKey() { + return llvm::DenseMapInfo::getEmptyKey(); + } + + static inline llvm::DILocation *getTombstoneKey() { + return llvm::DenseMapInfo::getTombstoneKey(); + } + + template + static unsigned getHashValue(const LocationT &Loc) { + auto Line{PresumedLocationInfo::getLine(Loc)}; + auto Column{PresumedLocationInfo::getColumn(Loc)}; + std::pair Pair{Line, Column}; + return llvm::DenseMapInfo::getHashValue(Pair); + } + + template + static bool isEqual(const LHSLocationT &LHS, const RHSLocationT &RHS) { + if constexpr (std::is_same_v, + llvm::DILocation *> && + std::is_same_v, + llvm::DILocation *>) + if (LHS == RHS) + return true; + if constexpr (std::is_same_v, + llvm::DILocation *>) + if (LHS == getTombstoneKey() || LHS == getEmptyKey()) + return false; + if constexpr (std::is_same_v, + llvm::DILocation *>) + if (RHS == getTombstoneKey() || RHS == getEmptyKey()) + return false; + llvm::sys::fs::UniqueID LHSId, RHSId; + return PresumedLocationInfo::getLine(LHS) == + PresumedLocationInfo::getLine(RHS) && + PresumedLocationInfo::getColumn(LHS) == + PresumedLocationInfo::getColumn(RHS) && + !llvm::sys::fs::getUniqueID( + PresumedLocationInfo::getFilename(LHS), LHSId) && + !llvm::sys::fs::getUniqueID( + PresumedLocationInfo::getFilename(RHS), RHSId) && + LHSId == RHSId; + } +}; +} // namespace tsar +#endif // TSAR_DI_LOCATION_MAP_INFO_H diff --git a/include/tsar/Support/Flang/PresumedLocationInfo.h b/include/tsar/Support/Flang/PresumedLocationInfo.h new file mode 100644 index 00000000..b35d1b6d --- /dev/null +++ b/include/tsar/Support/Flang/PresumedLocationInfo.h @@ -0,0 +1,48 @@ +//===-- PresumedLocationInfo.h - Type Traits for llvm::DenseMap -*- C++ -*-===// +// +// Traits Static Analyzer (SAPFOR) +// +// Copyright 2022 DVM System Group +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// +//===----------------------------------------------------------------------===// +// +// This file provide implementation of llvm::DenseMapInfo to compare different +// representations of a presumed location with a metadata location +// llvm::DILocation. +// +//===----------------------------------------------------------------------===// + +#ifndef TSAR_FLANG_PRESUMED_LOCATION_INFO_H +#define TSAR_FLANG_PRESUMED_LOCATION_INFO_H + +#include "tsar/Support/DILocationMapInfo.h" +#include + +namespace tsar { +template <> struct PresumedLocationInfo { + static unsigned getLine(Fortran::parser::SourcePosition Loc) { + return Loc.line; + } + static unsigned getColumn(Fortran::parser::SourcePosition Loc) { + return Loc.column; + } + static std::string getFilename(Fortran::parser::SourcePosition Loc) { + return Loc.file.path(); + } +}; +} // namespace tsar + +#endif//TSAR_FLANG_PRESUMED_LOCATION_INFO_H \ No newline at end of file diff --git a/lib/Analysis/Clang/DIMemoryMatcher.cpp b/lib/Analysis/Clang/DIMemoryMatcher.cpp index 999f0248..aa98bdf6 100644 --- a/lib/Analysis/Clang/DIMemoryMatcher.cpp +++ b/lib/Analysis/Clang/DIMemoryMatcher.cpp @@ -128,19 +128,17 @@ struct DILocalScopeMapInfo { } }; -using MatchDIVisitorBase = MatchASTBase, - ClangDIMemoryMatcherPass::DIMemoryMatcher, - ClangDIMemoryMatcherPass::MemoryASTSet>; - -class MatchDIVisitor : - public MatchDIVisitorBase, - public RecursiveASTVisitor { +class MatchDIVisitor + : public ClangMatchASTBase, + ClangDIMemoryMatcherPass::DIMemoryMatcher, + ClangDIMemoryMatcherPass::MemoryASTSet>, + public RecursiveASTVisitor { public: MatchDIVisitor(SourceManager &SrcMgr, Matcher &MM, UnmatchedASTSet &Unmatched, LocToIRMap &LocMap, LocToASTMap &MacroMap) : - MatchASTBase(SrcMgr, MM, Unmatched, LocMap, MacroMap) {} + ClangMatchASTBase(SrcMgr, MM, Unmatched, LocMap, MacroMap) {} bool VisitVarDecl(VarDecl *D) { mVisitedVars.push_back(D->getCanonicalDecl()); diff --git a/lib/Analysis/Clang/ExpressionMatcher.cpp b/lib/Analysis/Clang/ExpressionMatcher.cpp index cd9b6a30..53e62d5b 100644 --- a/lib/Analysis/Clang/ExpressionMatcher.cpp +++ b/lib/Analysis/Clang/ExpressionMatcher.cpp @@ -52,7 +52,7 @@ STATISTIC(NumNonMatchASTExpr, "Number of non-matched AST expressions"); namespace { class MatchExprVisitor : - public MatchASTBase, + public ClangMatchASTBase, public RecursiveASTVisitor { enum AccessKind : uint8_t { @@ -64,7 +64,7 @@ class MatchExprVisitor : public: MatchExprVisitor(SourceManager &SrcMgr, Matcher &MM, UnmatchedASTSet &Unmatched, LocToIRMap &LocMap, LocToASTMap &MacroMap) : - MatchASTBase(SrcMgr, MM, Unmatched, LocMap, MacroMap) {} + ClangMatchASTBase(SrcMgr, MM, Unmatched, LocMap, MacroMap) {} /// Evaluates declarations expanded from a macro and stores such /// declaration into location to macro map. @@ -145,7 +145,7 @@ class MatchExprVisitor : if (auto *SE{dyn_cast(S)}) { { // We use scope to reload stash on exit. - StashParent [[maybe_unused]] Stash{S, mParents}; + [[maybe_unused]] StashParent Stash{S, mParents}; if (!RecursiveASTVisitor::TraverseStmt(S)) return false; } @@ -233,7 +233,6 @@ void ClangExprMatcherPass::print(raw_ostream &OS, const llvm::Module *M) const { if (mMatcher.empty() || !mTfmCtx || !mTfmCtx->hasInstance()) return; auto &GO = getAnalysis().getOptions(); - auto &TfmInfo = getAnalysis(); auto &SrcMgr = mTfmCtx->getRewriter().getSourceMgr(); for (auto &Match : mMatcher) { tsar::print(OS, cast(Match.get())->getDebugLoc(), @@ -317,12 +316,12 @@ void ClangExprMatcherPass::getAnalysisUsage(AnalysisUsage &AU) const { char ClangExprMatcherPass::ID = 0; INITIALIZE_PASS_IN_GROUP_BEGIN(ClangExprMatcherPass, "clang-expr-matcher", - "High and Low Expression Matcher", false , true, + "High and Low Expression Matcher (Clang)", false , true, DefaultQueryManager::PrintPassGroup::getPassRegistry()) INITIALIZE_PASS_DEPENDENCY(TransformationEnginePass) INITIALIZE_PASS_DEPENDENCY(GlobalOptionsImmutableWrapper) INITIALIZE_PASS_IN_GROUP_END(ClangExprMatcherPass, "clang-expr-matcher", - "High and Low Level Expression Matcher", false, true, + "High and Low Level Expression Matcher (Clang)", false, true, DefaultQueryManager::PrintPassGroup::getPassRegistry()) FunctionPass * llvm::createClangExprMatcherPass() { diff --git a/lib/Analysis/Clang/LoopMatcher.cpp b/lib/Analysis/Clang/LoopMatcher.cpp index 28df3a33..c80e3e58 100755 --- a/lib/Analysis/Clang/LoopMatcher.cpp +++ b/lib/Analysis/Clang/LoopMatcher.cpp @@ -65,7 +65,7 @@ INITIALIZE_PASS_END(LoopMatcherPass, "loop-matcher", namespace { /// This matches explicit for, while and do-while loops. class MatchExplicitVisitor : - public MatchASTBase, + public ClangMatchASTBase, public RecursiveASTVisitor { public: @@ -83,11 +83,11 @@ class MatchExplicitVisitor : /// in this map. These loops will not inserted in LM map and must be evaluated /// further. The key in this map is a raw encoding for expansion location. /// To decode it use SourceLocation::getFromRawEncoding() method. - MatchExplicitVisitor(SourceManager &SrcMgr, - Matcher &LM, UnmatchedASTSet &Unmatched, - LocToIRMap &LocMap, LocToIRMap &ImplicitMap, LocToASTMap &MacroMap) : - MatchASTBase(SrcMgr, LM, Unmatched, LocMap, MacroMap), - mLocToImplicit(&ImplicitMap) {} + MatchExplicitVisitor(SourceManager &SrcMgr, Matcher &LM, + UnmatchedASTSet &Unmatched, LocToIRMap &LocMap, + LocToIRMap &ImplicitMap, LocToASTMap &MacroMap) + : ClangMatchASTBase(SrcMgr, LM, Unmatched, LocMap, MacroMap), + mLocToImplicit(&ImplicitMap) {} /// \brief Evaluates statements expanded from a macro. /// @@ -186,12 +186,14 @@ class MatchExplicitVisitor : /// This matches implicit loops. class MatchImplicitVisitor : - public MatchASTBase, + public ClangMatchASTBase, public RecursiveASTVisitor { public: MatchImplicitVisitor(SourceManager &SrcMgr, Matcher &LM, - UnmatchedASTSet &Unmatched, LocToIRMap &LocMap, LocToASTMap &MacroMap) : - MatchASTBase(SrcMgr, LM, Unmatched, LocMap, MacroMap), mLastLabel(nullptr) {} + UnmatchedASTSet &Unmatched, LocToIRMap &LocMap, + LocToASTMap &MacroMap) + : ClangMatchASTBase(SrcMgr, LM, Unmatched, LocMap, MacroMap), + mLastLabel(nullptr) {} bool VisitStmt(Stmt *S) { // We try to find a label which is a start of the loop header. diff --git a/lib/Analysis/Clang/MemoryMatcher.cpp b/lib/Analysis/Clang/MemoryMatcher.cpp index d723e54d..9fdafcfd 100644 --- a/lib/Analysis/Clang/MemoryMatcher.cpp +++ b/lib/Analysis/Clang/MemoryMatcher.cpp @@ -126,14 +126,16 @@ STATISTIC(NumNonMatchASTMemory, "Number of non-matched AST variables"); namespace { /// This matches allocas (IR) and variables (AST). class MatchAllocaVisitor - : public MatchASTBase, - MemoryMatchInfo::MemoryMatcher>, + : public ClangMatchASTBase, + MemoryMatchInfo::MemoryMatcher>, public RecursiveASTVisitor { public: MatchAllocaVisitor(SourceManager &SrcMgr, Matcher &MM, UnmatchedASTSet &Unmatched, LocToIRMap &LocMap, LocToASTMap &MacroMap) : - MatchASTBase(SrcMgr, MM, Unmatched, LocMap, MacroMap) {} + ClangMatchASTBase(SrcMgr, MM, Unmatched, LocMap, MacroMap) {} /// Evaluates declarations expanded from a macro and stores such /// declaration into location to macro map. From 7a37ff3157af147059512dbd0f8e6db22e067d01 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Tue, 31 May 2022 12:54:39 +0300 Subject: [PATCH 43/93] [TSAR, Flang] Add initial implementation of expression matcher. At this moment call statements and expressions are only supported. Note that multiple calls at the same level in statement do not processed properly at the moment (for example, `call foo(bar1(), bar2())`). Note that expressions in macro does not support. --- .../tsar/Analysis/Flang/ExpressionMatcher.h | 101 +++++++ include/tsar/Analysis/Flang/Passes.h | 47 ++++ lib/Analysis/CMakeLists.txt | 3 + lib/Analysis/Flang/CMakeLists.txt | 24 ++ lib/Analysis/Flang/ExpressionMatcher.cpp | 249 ++++++++++++++++++ lib/Analysis/Flang/Passes.cpp | 32 +++ lib/Core/CMakeLists.txt | 4 +- lib/Core/Passes.cpp | 2 + tools/tsar-server/CMakeLists.txt | 2 +- 9 files changed, 461 insertions(+), 3 deletions(-) create mode 100644 include/tsar/Analysis/Flang/ExpressionMatcher.h create mode 100644 include/tsar/Analysis/Flang/Passes.h create mode 100644 lib/Analysis/Flang/CMakeLists.txt create mode 100644 lib/Analysis/Flang/ExpressionMatcher.cpp create mode 100644 lib/Analysis/Flang/Passes.cpp diff --git a/include/tsar/Analysis/Flang/ExpressionMatcher.h b/include/tsar/Analysis/Flang/ExpressionMatcher.h new file mode 100644 index 00000000..35a4f99d --- /dev/null +++ b/include/tsar/Analysis/Flang/ExpressionMatcher.h @@ -0,0 +1,101 @@ +//=== ExpressionMatcher.h - High and Low Level Matcher (Flang) --*- C++ -*-===// +// +// Traits Static Analyzer (SAPFOR) +// +// Copyright 2022 DVM System Group +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +// +// Classes and functions from this file match expressions in Clang AST and +// appropriate expressions in low-level LLVM IR. This file implements +// pass to perform this functionality. +// +//===----------------------------------------------------------------------===// + +#include "tsar/ADT/Bimap.h" +#include "tsar/Analysis/Flang/Passes.h" +#include "tsar/Support/Tags.h" +#include +#include +#include +#include +#include +#include + +#ifndef TSAR_FLANG_EXPRESSION_MATCHER_H +#define TSAR_FLANG_EXPRESSION_MATCHER_H + +namespace tsar { +class FlangTransformationContext; +} + +namespace llvm { +class Value; + +/// This per-function pass matches expressions in a source code (Flang AST) and +/// appropriate expressions in low-level LLVM IR. +/// +/// At this moment only call expressions are processed. +class FlangExprMatcherPass : + public FunctionPass, private bcl::Uncopyable { + template struct NodeInfo { + using ListT = bcl::TypeList; + using NodeT = llvm::PointerUnion; + }; + + using NodeInfoT = NodeInfo; + +public: + using NodeT = NodeInfoT::NodeT; + + using ExprMatcher = tsar::Bimap < + bcl::tagged, + bcl::tagged>; + + using ExprASTSet = llvm::DenseSet; + + static char ID; + + FlangExprMatcherPass() : FunctionPass(ID) { + initializeFlangExprMatcherPassPass(*PassRegistry::getPassRegistry()); + } + + bool runOnFunction(Function &F) override; + void getAnalysisUsage(AnalysisUsage &AU) const override; + + void releaseMemory() override { + mTfmCtx = nullptr; + mMatcher.clear(); + mUnmatchedAST.clear(); + } + + void print(raw_ostream &OS, const Module *M) const override; + + /// Return expression matcher for the analyzed function. + const ExprMatcher & getMatcher() const noexcept { return mMatcher; } + + /// Return unmatched expressions in AST. + const ExprASTSet & getUnmatchedAST() const noexcept { return mUnmatchedAST; } + +private: + tsar::FlangTransformationContext *mTfmCtx{nullptr}; + ExprMatcher mMatcher; + ExprASTSet mUnmatchedAST; +}; + +} + +#endif//TSAR_FLANG_EXPRESSION_MATCHER_H diff --git a/include/tsar/Analysis/Flang/Passes.h b/include/tsar/Analysis/Flang/Passes.h new file mode 100644 index 00000000..46a76c20 --- /dev/null +++ b/include/tsar/Analysis/Flang/Passes.h @@ -0,0 +1,47 @@ +//===- Passes.h - Create and Initialize Analysis Passes (Flang) -*- C++ -*-===// +// +// Traits Static Analyzer (SAPFOR) +// +// Copyright 2022 DVM System Group +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +// +// It contains declarations of functions that initialize and create an instances +// of TSAR passes which are necessary for source-based analysis of Fortran +// programs. +// Declarations of appropriate methods for an each new pass should +// be added to this file. +// +//===----------------------------------------------------------------------===// + +#ifndef TSAR_FLANG_ANALYSIS_PASSES_H +#define TSAR_FLANG_ANALYSIS_PASSES_H + +namespace llvm { +class PassRegistry; +class FunctionPass; +class ModulePass; + +/// Initialize all passes to perform source-base analysis of Fortran programs. +void initializeFlangAnalysis(PassRegistry &Registry); + +/// Initialize a pass to match high-level and low-level expressions. +void initializeFlangExprMatcherPassPass(PassRegistry &Registry); + +/// Create a pass to match high-level and low-level expressions. +FunctionPass * createFlangExprMatcherPass(); + +} +#endif//TSAR_FLANG_ANALYSIS_PASSES_H diff --git a/lib/Analysis/CMakeLists.txt b/lib/Analysis/CMakeLists.txt index 4bd54f38..1bc0ff68 100644 --- a/lib/Analysis/CMakeLists.txt +++ b/lib/Analysis/CMakeLists.txt @@ -32,6 +32,9 @@ tsar_tablegen(Attributes.gen -gen-tsar-attributes-defs add_dependencies(TSARAnalysis IntrinsicsGen AttributesGen) add_subdirectory(Clang) +if(FLANG_FOUND) + add_subdirectory(Flang) +endif() add_subdirectory(Memory) add_subdirectory(Reader) add_subdirectory(Parallel) diff --git a/lib/Analysis/Flang/CMakeLists.txt b/lib/Analysis/Flang/CMakeLists.txt new file mode 100644 index 00000000..af1e4536 --- /dev/null +++ b/lib/Analysis/Flang/CMakeLists.txt @@ -0,0 +1,24 @@ +set(ANALYSIS_SOURCES Passes.cpp ExpressionMatcher.cpp) + +if(MSVC_IDE) + file(GLOB_RECURSE ANALYSIS_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + ${PROJECT_SOURCE_DIR}/include/tsar/Analysis/Flang/*.h) + file(GLOB_RECURSE ANALYSIS_INTERNAL_HEADERS + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.h) + source_group(bcl FILES ${BCL_CORE_HEADERS}) +endif() + +add_library(TSARAnalysisFlang STATIC + ${ANALYSIS_SOURCES} ${ANALYSIS_HEADERS} ${ANALYSIS_INTERNAL_HEADERS}) + +if(NOT PACKAGE_LLVM) + add_dependencies(TSARAnalysisFlang ${FLANG_LIBS} ${LLVM_LIBS}) +endif() +add_dependencies(TSARAnalysisFlang DirectivesGen DiagnosticKinds + IntrinsicsGen AttributesGen) +target_link_libraries(TSARAnalysisFlang + TSARAnalysisMemory TSARSupportFlang TSARTool BCL::Core) + +set_target_properties(TSARAnalysisFlang PROPERTIES + FOLDER "${TSAR_LIBRARY_FOLDER}" + COMPILE_DEFINITIONS $<$>:NDEBUG>) diff --git a/lib/Analysis/Flang/ExpressionMatcher.cpp b/lib/Analysis/Flang/ExpressionMatcher.cpp new file mode 100644 index 00000000..2dbe02ef --- /dev/null +++ b/lib/Analysis/Flang/ExpressionMatcher.cpp @@ -0,0 +1,249 @@ +//=== ExpressionMatcher.cpp - High and Low Level Matcher (Flang) *- C++ -*-===// +// +// Traits Static Analyzer (SAPFOR) +// +// Copyright 2022 DVM System Group +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +// +// Classes and functions from this file match expressions in Flang AST and +// appropriate expressions in low-level LLVM IR. This file implements +// pass to perform this functionality. +// +//===----------------------------------------------------------------------===// + +#include "tsar/Analysis/Flang/ExpressionMatcher.h" +#include "tsar/Analysis/KnownFunctionTraits.h" +#include "tsar/Analysis/Flang/Matcher.h" +#include "tsar/Analysis/Memory/Utils.h" +#include "tsar/Analysis/PrintUtils.h" +#include "tsar/Core/Query.h" +#include "tsar/Frontend/Flang/TransformationContext.h" +#include "tsar/Support/GlobalOptions.h" +#include "tsar/Support/MetadataUtils.h" +#include +#include +#include +#include +#include + +#undef DEBUG_TYPE +#define DEBUG_TYPE "flang-expr-matcher" + +using namespace Fortran; +using namespace llvm; +using namespace tsar; + +STATISTIC(NumMatchExpr, "Number of matched expressions"); +STATISTIC(NumNonMatchIRExpr, "Number of non-matched IR expressions"); +STATISTIC(NumNonMatchASTExpr, "Number of non-matched AST expressions"); + +namespace { +class MatchExprVisitor : public FlangMatchASTBase { +public: + MatchExprVisitor(parser::AllCookedSources &AllCooked, Matcher &MM, + UnmatchedASTSet &Unmatched, LocToIRMap &LocMap, LocToASTMap &MacroMap) : + FlangMatchASTBase(AllCooked, MM, Unmatched, LocMap, MacroMap) {} + + template bool Pre(T &N) { return true; } + template void Post(T &N) {} + + bool Pre(parser::ProgramUnit &PU) { + if (mIsProcessed) + return false; + return mIsProcessed = true; + } + + bool Pre(parser::InternalSubprogram &IS) { + if (mIsProcessed) + return false; + return mIsProcessed = true; + } + + bool Pre(parser::ModuleSubprogram &MS) { + if (mIsProcessed) + return false; + return mIsProcessed = true; + } + + template void Post(parser::Statement& S) { + LLVM_DEBUG(if (auto Range{mAllCooked->GetProvenanceRange(S.source)}) { + if (auto PLoc{ + mAllCooked->allSources().GetSourcePosition(Range->start())}) { + using PLI = PresumedLocationInfo; + dbgs() << "[EXPR MATCHER]: try to match at " << PLI::getFilename(*PLoc) + << ":" << PLI::getLine(*PLoc) << ":" << PLI::getColumn(*PLoc) + << "\n"; + dbgs() << "[EXPR MATCHER]: call stack size " << mASTCallStack.size() + << "\n"; + if (mAmbiguousCallStack) + dbgs() << "[EXPR MATCHER]: call stack is ambiguous\n"; + } + }); + // TODO (kaniandr@gmail.com): match multiple calls at the same level. + // Debug locations for calls inside a single statement are equal at the + // moment, so we cannot match nested calls if at a some level there are + // multiple calls. For example, we cannot match calls which are arguments + // of another call. + if (auto Range{mAllCooked->GetProvenanceRange(S.source)}) + if (auto I{findItrForLocation(Range->start())}; I != mLocToIR->end()) + if (!mAmbiguousCallStack && I->second.size() == mASTCallStack.size()) { + NumMatchExpr += I->second.size(); + NumNonMatchASTExpr -= I->second.size(); + for (auto N : reverse(mASTCallStack)) { + mMatcher->emplace(N, I->second.back()); + I->second.pop_back(); + } + mASTCallStack.clear(); + } else { + I->second.clear(); + } + for (auto N : mASTCallStack) + mUnmatchedAST->insert(N); + NumNonMatchASTExpr += mASTCallStack.size(); + mASTCallStack.clear(); + mAmbiguousCallStack = false; + mWasCallOnLevel = false; + } + + bool Pre([[maybe_unused]] parser::Call &) { + mStashWasCallOnLevel = mWasCallOnLevel; + mWasCallOnLevel = false; + return true; + } + + void Post([[maybe_unused]] parser::Call&) { + mWasCallOnLevel = mStashWasCallOnLevel; + } + + bool Pre(parser::CallStmt &CS) { + mASTCallStack.emplace_back(&CS); + if (mWasCallOnLevel) + mAmbiguousCallStack = true; + return true; + } + + bool Pre(parser::FunctionReference &FR) { + mASTCallStack.emplace_back(&FR); + if (mWasCallOnLevel) + mAmbiguousCallStack = true; + return true; + } + +private: + bool mIsProcessed{false}; + bool mWasCallOnLevel{false}, mStashWasCallOnLevel{false}; + bool mAmbiguousCallStack{false}; + SmallVector mASTCallStack; + SmallVector mIRCallStack; +}; + +struct PrintFunctor { + template void operator()() { + if (auto *N{Node.dyn_cast()}) + OS << N->v.source.ToString(); + } + const FlangExprMatcherPass::NodeT &Node; + llvm::raw_ostream &OS; +}; +} // namespace + +void FlangExprMatcherPass::print(raw_ostream &OS, const llvm::Module *M) const { + if (mMatcher.empty() || !mTfmCtx || !mTfmCtx->hasInstance()) + return; + auto &GO{getAnalysis().getOptions()}; + for (auto &Match : mMatcher) { + tsar::print(OS, cast(Match.get())->getDebugLoc(), + GO.PrintFilenameOnly); + OS << " "; + NodeInfoT::ListT::for_each_type(PrintFunctor{Match.get(), OS}); + Match.get()->print(OS); + OS << "\n"; + } +} + +bool FlangExprMatcherPass::runOnFunction(Function &F) { + releaseMemory(); + auto *DISub{findMetadata(&F)}; + if (!DISub) + return false; + auto *CU{DISub->getUnit()}; + if (!isFortran(CU->getSourceLanguage())) + return false; + auto &TfmInfo{getAnalysis()}; + mTfmCtx = TfmInfo ? dyn_cast_or_null( + TfmInfo->getContext(*CU)) + : nullptr; + if (!mTfmCtx || !mTfmCtx->hasInstance()) + return false; + auto &AllCooked{mTfmCtx->getParsing().allCooked()}; + MatchExprVisitor::LocToIRMap LocToExpr; + MatchExprVisitor::LocToASTMap LocToMacro; + MatchExprVisitor MatchExpr(AllCooked, + mMatcher, mUnmatchedAST, LocToExpr, LocToMacro); + for (auto &I: instructions(F)) { + if (auto II = llvm::dyn_cast(&I); + II && (isDbgInfoIntrinsic(II->getIntrinsicID()) || + isMemoryMarkerIntrinsic(II->getIntrinsicID()))) + continue; + if (!isa(I)) + continue; + ++NumNonMatchIRExpr; + auto Loc = I.getDebugLoc(); + if (Loc) { + LLVM_DEBUG(dbgs() << "[EXPR MATCHER]: remember instruction "; + I.print(dbgs()); dbgs() << " at "; Loc.print(dbgs()); + dbgs() << "\n"); + auto Itr{ LocToExpr.try_emplace(Loc).first }; + Itr->second.push_back(&I); + } + } + for (auto &Pair : LocToExpr) + std::reverse(Pair.second.begin(), Pair.second.end()); + // It is necessary to build LocToExpr map even if AST representation is + // unknown, because a number of unmatched expressions should be calculated. + auto ASTSub{mTfmCtx->getDeclForMangledName(F.getName())}; + if (!ASTSub || ASTSub.isDeclaration()) + return false; + ASTSub.visit( + [&MatchExpr](auto &PU, auto &S) { parser::Walk(PU, MatchExpr); }); + // TODO (kaniandr@gmail.com): collect expressions from macros, note that + // there is no debug locations for such expressions at the moment. + MatchExpr.matchInMacro(NumMatchExpr, NumNonMatchASTExpr, NumNonMatchIRExpr, + true); + return false; +} + +void FlangExprMatcherPass::getAnalysisUsage(AnalysisUsage &AU) const { + AU.addRequired(); + AU.addRequired(); + AU.setPreservesAll(); +} + +char FlangExprMatcherPass::ID = 0; + +INITIALIZE_PASS_IN_GROUP_BEGIN(FlangExprMatcherPass, "flang-expr-matcher", + "High and Low Expression Matcher (Flang)", false , true, + DefaultQueryManager::PrintPassGroup::getPassRegistry()) + INITIALIZE_PASS_DEPENDENCY(TransformationEnginePass) + INITIALIZE_PASS_DEPENDENCY(GlobalOptionsImmutableWrapper) +INITIALIZE_PASS_IN_GROUP_END(FlangExprMatcherPass, "flang-expr-matcher", + "High and Low Level Expression Matcher (Flang)", false, true, + DefaultQueryManager::PrintPassGroup::getPassRegistry()) + +FunctionPass * llvm::createFlangExprMatcherPass() { + return new FlangExprMatcherPass; +} \ No newline at end of file diff --git a/lib/Analysis/Flang/Passes.cpp b/lib/Analysis/Flang/Passes.cpp new file mode 100644 index 00000000..b2f833b5 --- /dev/null +++ b/lib/Analysis/Flang/Passes.cpp @@ -0,0 +1,32 @@ +//=== Passes.cpp - Create and Initialize Analysis Passes (Flang) *- C++ -*-===// +// +// Traits Static Analyzer (SAPFOR) +// +// Copyright 2022 DVM System Group +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +// +// This contains functions to initialize passes which are necessary for +// source-base analysis of Fortran programs. +// +//===----------------------------------------------------------------------===// + +#include "tsar/Analysis/Flang/Passes.h" + +using namespace llvm; + +void llvm::initializeFlangAnalysis(PassRegistry &Registry) { + initializeFlangExprMatcherPassPass(Registry); +} diff --git a/lib/Core/CMakeLists.txt b/lib/Core/CMakeLists.txt index 8d9f031d..f164478a 100644 --- a/lib/Core/CMakeLists.txt +++ b/lib/Core/CMakeLists.txt @@ -40,7 +40,7 @@ endif() if(NOT PACKAGE_LLVM) if(Flang_FOUND) add_dependencies(TSARTool TSARTransformFlang TSARFrontendFlang - TSARSupportFlang) + TSARAnalysisFlang TSARSupportFlang) endif() add_dependencies(TSARTool TSARTransformAST @@ -52,7 +52,7 @@ endif() if (FLANG_FOUND) target_link_libraries(TSARTool PRIVATE TSARTransformFlang TSARFrontendFlang - TSARSupportFlang) + TSARAnalysisFlang TSARSupportFlang) endif() target_link_libraries(TSARTool PRIVATE diff --git a/lib/Core/Passes.cpp b/lib/Core/Passes.cpp index 3e388b33..e02092e5 100644 --- a/lib/Core/Passes.cpp +++ b/lib/Core/Passes.cpp @@ -36,6 +36,7 @@ #include "tsar/Transform/IR/Passes.h" #include "tsar/Transform/Mixed/Passes.h" #ifdef FLANG_FOUND +# include "tsar/Analysis/Flang/Passes.h" # include "tsar/Transform/Flang/Passes.h" #endif #ifdef APC_FOUND @@ -57,6 +58,7 @@ void llvm::initializeTSAR(PassRegistry &Registry) { initializeASTTransform(Registry); initializeClangTransform(Registry); #ifdef FLANG_FOUND + initializeFlangAnalysis(Registry); initializeFlangTransform(Registry); #endif #ifdef APC_FOUND diff --git a/tools/tsar-server/CMakeLists.txt b/tools/tsar-server/CMakeLists.txt index 8f6bee8a..94709dcc 100644 --- a/tools/tsar-server/CMakeLists.txt +++ b/tools/tsar-server/CMakeLists.txt @@ -25,7 +25,7 @@ endif() if (FLANG_FOUND) set_property(TARGET TSARFrontendFlang TSARSupportFlang TSARTransformFlang - PROPERTY POSITION_INDEPENDENT_CODE ON) + TSARAnalysisFlang PROPERTY POSITION_INDEPENDENT_CODE ON) endif() add_library(TSARServer SHARED From 6c97436eacbf6ff677593f31f8441b771e5ddac5 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Fri, 3 Jun 2022 01:08:28 +0300 Subject: [PATCH 44/93] [TSAR, Tfm, Mixed] Fix, trivial. --- lib/Transform/Mixed/DINodeRetriever.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Transform/Mixed/DINodeRetriever.cpp b/lib/Transform/Mixed/DINodeRetriever.cpp index 094645e7..a4995bba 100644 --- a/lib/Transform/Mixed/DINodeRetriever.cpp +++ b/lib/Transform/Mixed/DINodeRetriever.cpp @@ -142,7 +142,7 @@ bool DINodeRetrieverPass::runOnModule(llvm::Module &M) { auto *DITy = createStubType(M, GlobalVar.getType()->getAddressSpace(), DIB); auto *GV = DIGlobalVariable::getDistinct( Ctx, File, Name, GlobalVar.getName(), File, Line, DITy, - GlobalVar.hasLocalLinkage(), GlobalVar.isDeclaration(), + GlobalVar.hasLocalLinkage(), !GlobalVar.isDeclaration(), nullptr, nullptr, 0, nullptr); auto *GVE = DIGlobalVariableExpression::get(Ctx, GV, DIExpression::get(Ctx, {})); From 2118236c206f2f661cd5deb0a931af5d4c7fb320 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Mon, 9 May 2022 23:52:03 +0300 Subject: [PATCH 45/93] [TSAR, Tfm, Flang, Mixed] Add metadata for variables marked with `sapforASTRegVar` intrinsic. Initial implementation only simple scalar and array types are supported. --- include/tsar/Analysis/Intrinsics.td | 4 + include/tsar/Support/EmptyPass.h | 13 + include/tsar/Transform/Mixed/Passes.h | 8 + lib/Core/Query.cpp | 2 + lib/Support/EmptyPass.cpp | 7 +- lib/Transform/Flang/VariableRegistration.cpp | 7 +- lib/Transform/Mixed/CMakeLists.txt | 3 +- lib/Transform/Mixed/DIVariableRetriever.cpp | 351 +++++++++++++++++++ lib/Transform/Mixed/FlangStubs.cpp | 5 + lib/Transform/Mixed/Passes.cpp | 1 + 10 files changed, 397 insertions(+), 4 deletions(-) create mode 100644 lib/Transform/Mixed/DIVariableRetriever.cpp diff --git a/include/tsar/Analysis/Intrinsics.td b/include/tsar/Analysis/Intrinsics.td index 9d7af721..4ebb07a6 100755 --- a/include/tsar/Analysis/Intrinsics.td +++ b/include/tsar/Analysis/Intrinsics.td @@ -127,3 +127,7 @@ def allocate_pool : Intrinsic<"sapforAllocatePool", def decl_types : Intrinsic<"sapforDeclTypes", tsar_void_ty, [tsar_size_ty, tsar_size_ptr_ty, tsar_size_ptr_ty]>; + +def ast_reg_var : Intrinsic<"sapforASTRegVar", + tsar_void_ty, + [tsar_addr_ty]>; diff --git a/include/tsar/Support/EmptyPass.h b/include/tsar/Support/EmptyPass.h index 9ab68a02..2b7083c5 100644 --- a/include/tsar/Support/EmptyPass.h +++ b/include/tsar/Support/EmptyPass.h @@ -42,6 +42,19 @@ class EmptyFunctionPass : public FunctionPass, private bcl::Uncopyable { }; inline FunctionPass *createEmptyFunctionPass() { return new EmptyFunctionPass; } + +void initializeEmptyModulePassPass(PassRegistry &); + +class EmptyModulePass : public ModulePass, private bcl::Uncopyable { +public: + static char ID; + EmptyModulePass() : ModulePass(ID) { + initializeEmptyModulePassPass(*PassRegistry::getPassRegistry()); + } + bool runOnModule(Module &) override { return false; } +}; + +inline ModulePass *createEmptyModulePass() { return new EmptyModulePass; } } #endif//TSAR_EMPTY_PASS_H diff --git a/include/tsar/Transform/Mixed/Passes.h b/include/tsar/Transform/Mixed/Passes.h index 9ed035d8..a968e889 100644 --- a/include/tsar/Transform/Mixed/Passes.h +++ b/include/tsar/Transform/Mixed/Passes.h @@ -70,5 +70,13 @@ void initializeFlangDummyAliasAnalysisPass(PassRegistry &Registry); /// Create a pass which retrieves alias information for dummy arguments from /// the source code. FunctionPass *createFlangDummyAliasAnalysis(); + +/// Initialize a pass which retrieves metadata for Fortran +/// variables if it is not presented properly in LLVM IR. +void initializeFlangDIVariableRetrieverPassPass(PassRegistry &Registry); + +/// Create a pass which retrieves metadata for Fortran +/// variables if it is not presented properly in LLVM IR. +ModulePass *createFlangDIVariableRetrieverPass(); } #endif//TSAR_MIXED_TRANSFORM_PASSES_H diff --git a/lib/Core/Query.cpp b/lib/Core/Query.cpp index 70d5a872..10a28876 100644 --- a/lib/Core/Query.cpp +++ b/lib/Core/Query.cpp @@ -139,6 +139,7 @@ void addInitialTransformations(legacy::PassManager &Passes) { Passes.add(createStripDeadPrototypesPass()); Passes.add(createGlobalDCEPass()); Passes.add(createGlobalsAAWrapperPass()); + Passes.add(createFlangDIVariableRetrieverPass()); Passes.add(createNoMetadataDSEPass()); Passes.add(createDILoopRetrieverPass()); Passes.add(createDINodeRetrieverPass()); @@ -398,6 +399,7 @@ void InstrLLVMQueryManager::run(llvm::Module *M, } Passes.add(createUnreachableBlockEliminationPass()); Passes.add(createNoMetadataDSEPass()); + Passes.add(createFlangDIVariableRetrieverPass()); Passes.add(createDINodeRetrieverPass()); Passes.add(createMemoryMatcherPass()); Passes.add(createDILoopRetrieverPass()); diff --git a/lib/Support/EmptyPass.cpp b/lib/Support/EmptyPass.cpp index 39ce89ee..b2dc42f4 100644 --- a/lib/Support/EmptyPass.cpp +++ b/lib/Support/EmptyPass.cpp @@ -28,4 +28,9 @@ using namespace llvm; char EmptyFunctionPass::ID = 0; -INITIALIZE_PASS(EmptyFunctionPass, "empty-pass", "Empty Pass", true, true) +INITIALIZE_PASS(EmptyFunctionPass, "empty-function-pass", "Empty Function Pass", + true, true) + +char EmptyModulePass::ID = 0; +INITIALIZE_PASS(EmptyModulePass, "empty-module-pass", "Empty Module Pass", true, + true) diff --git a/lib/Transform/Flang/VariableRegistration.cpp b/lib/Transform/Flang/VariableRegistration.cpp index da1733ca..4f12c453 100644 --- a/lib/Transform/Flang/VariableRegistration.cpp +++ b/lib/Transform/Flang/VariableRegistration.cpp @@ -22,6 +22,7 @@ // //===----------------------------------------------------------------------===// +#include "tsar/Analysis/Intrinsics.h" #include "tsar/Analysis/Memory/Utils.h" #include "tsar/Core/Query.h" #include "tsar/Frontend/Flang/TransformationContext.h" @@ -140,7 +141,7 @@ bool FlangVariableRegistrationPass::runOnFunction(Function &F) { if (!TfmCtx || !TfmCtx->hasInstance()) return false; auto ASTSub{TfmCtx->getDeclForMangledName(F.getName())}; - if (!ASTSub) + if (!ASTSub || ASTSub.isDeclaration()) return false; if (ASTSub.getSemanticsUnit()->scope()->GetSymbols().empty()) return false; @@ -164,7 +165,9 @@ bool FlangVariableRegistrationPass::runOnFunction(Function &F) { } if (TfmCtx->getOptions().isFixedForm) Register.append(6, ' '); - Register += "call sapfor_dbg_variable(" + S->name().ToString() + ")"; + Register += ("call " + getName(IntrinsicId::ast_reg_var) + "(" + + S->name().ToString() + ")") + .str(); if (!TfmCtx->getOptions().isFixedForm) Register += ";"; Register += "\n"; diff --git a/lib/Transform/Mixed/CMakeLists.txt b/lib/Transform/Mixed/CMakeLists.txt index e1b62cea..c68ec7f9 100644 --- a/lib/Transform/Mixed/CMakeLists.txt +++ b/lib/Transform/Mixed/CMakeLists.txt @@ -2,7 +2,8 @@ set(TRANSFORM_SOURCES Passes.cpp Instrumentation.cpp DILoopRetriever.cpp DINodeRetriever.cpp) if(FLANG_FOUND) - set(TRANSFORM_SOURCES ${TRANSFORM_SOURCES} DummyScopeAAPass.cpp) + set(TRANSFORM_SOURCES ${TRANSFORM_SOURCES} DummyScopeAAPass.cpp + DIVariableRetriever.cpp) else() set(TRANSFORM_SOURCES ${TRANSFORM_SOURCES} FlangStubs.cpp) endif() diff --git a/lib/Transform/Mixed/DIVariableRetriever.cpp b/lib/Transform/Mixed/DIVariableRetriever.cpp new file mode 100644 index 00000000..2d7935be --- /dev/null +++ b/lib/Transform/Mixed/DIVariableRetriever.cpp @@ -0,0 +1,351 @@ +//=== DIVariableRetriever.cpp - Subroutine Retriever (Flang) --*- C++ -*-===// +// +// Traits Static Analyzer (SAPFOR) +// +// Copyright 2022 DVM System Group +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +// +// This file implements a pass which relies on Flang AST to insert metadata +// for variables. +// +//===----------------------------------------------------------------------===// + +#include "tsar/Analysis/Intrinsics.h" +#include "tsar/Analysis/Memory/Utils.h" +#include "tsar/Analysis/Flang/ExpressionMatcher.h" +#include "tsar/Frontend/Flang/TransformationContext.h" +#include "tsar/Transform/Mixed/Passes.h" +#include "tsar/Support/Flang/PresumedLocationInfo.h" +#include "tsar/Support/GlobalOptions.h" +#include "tsar/Support/IRUtils.h" +#include "tsar/Support/MetadataUtils.h" +#include "tsar/Support/PassProvider.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef DEBUG_TYPE +#define DEBUG_TYPE "di-variable-retriever" + +using namespace Fortran; +using namespace llvm; +using namespace tsar; + +namespace tsar { +} // namespace tsar + +namespace { +using MatcherProvider = FunctionPassProvider; + +class FlangDIVariableRetrieverPass : public ModulePass, + private bcl::Uncopyable { +public: + static char ID; + FlangDIVariableRetrieverPass() : ModulePass(ID) { + initializeFlangDIVariableRetrieverPassPass( + *PassRegistry::getPassRegistry()); + } + bool runOnModule(llvm::Module &M) override; + void getAnalysisUsage(AnalysisUsage &AU) const override; +}; +} // namespace + +INITIALIZE_PROVIDER(MatcherProvider, "di-variable-retrieve-provider", + "Variable Debug Info Retriever (Flang, Provider)") + +char FlangDIVariableRetrieverPass::ID = 0; +INITIALIZE_PASS_BEGIN(FlangDIVariableRetrieverPass, "di-variable-retriever", + "Variable Debug Info Retriever (Flang)", true, false) +INITIALIZE_PASS_DEPENDENCY(GlobalOptionsImmutableWrapper) +INITIALIZE_PASS_DEPENDENCY(TransformationEnginePass) +INITIALIZE_PASS_DEPENDENCY(MatcherProvider) +INITIALIZE_PASS_END(FlangDIVariableRetrieverPass, "di-variable-retriever", + "Variable Debug Info Retriever (Flang)", true, false) + +void FlangDIVariableRetrieverPass::getAnalysisUsage(AnalysisUsage &AU) const { + AU.addRequired(); + AU.addRequired(); + AU.addRequired(); + AU.getPreservesAll(); +} + +static llvm::DIType *createIntrinsicType(const semantics::DeclTypeSpec *FlangT, + llvm::Type &T, + const llvm::DataLayout &DL, + llvm::DIBuilder &DIB) { + if (auto FlangIntrinsicT{FlangT->AsIntrinsic()}) + if (auto Size{DL.getTypeAllocSizeInBits(&T)}; !Size.isScalable()) { + unsigned Encoding{dwarf::DW_ATE_unsigned_char}; + switch (FlangIntrinsicT->category()) { + default: + llvm_unreachable("Unsupported Fortran intrinsic type!"); + return nullptr; + case common::TypeCategory::Integer: + Encoding = dwarf::DW_ATE_signed; + break; + case common::TypeCategory::Real: + Encoding = dwarf::DW_ATE_float; + break; + case common::TypeCategory::Logical: + Encoding = dwarf::DW_ATE_boolean; + break; + case common::TypeCategory::Complex: + Encoding = dwarf::DW_ATE_complex_float; + break; + case common::TypeCategory::Character: + Encoding = dwarf::DW_ATE_signed; + break; + } + return DIB.createBasicType(FlangT->AsFortran(), Size.getFixedSize(), + Encoding); + } + return nullptr; +} + +using DimensionSubranges = SmallVector, 8>; +using MaybeSize = std::optional; + + +static std::tuple +getDimensionSubranges(const semantics::ArraySpec& ArrayShape) { + DimensionSubranges Subranges; + MaybeSize Size{1}; + for (auto &ShapeSpec : ArrayShape) { + Subranges.emplace_back(0, 1); + if (!ShapeSpec.lbound().isExplicit() || !ShapeSpec.ubound().isExplicit()) { + Size = std::nullopt; + continue; + } + auto &LB{ShapeSpec.lbound().GetExplicit()}; + auto &UB{ShapeSpec.ubound().GetExplicit()}; + if (!LB || !UB) { + Size = std::nullopt; + continue; + } + auto LBConst{std::visit( + common::visitors{ + [](auto &&) -> std::optional { return std::nullopt; }, + [](evaluate::Constant C) { + return std::optional{C.GetScalarValue()->ToInt64()}; + }}, + LB->u)}; + auto UBConst{std::visit( + common::visitors{ + [](auto &&) -> std::optional { return std::nullopt; }, + [](evaluate::Constant C) { + return std::optional{C.GetScalarValue()->ToInt64()}; + }}, + UB->u)}; + if (LBConst && UBConst) { + Subranges.back().first = *UBConst - *LBConst + 1; + if (Size) + *Size *= Subranges.back().first; + Subranges.back().second = *LBConst; + } + } + return std::tuple{Size, std::move(Subranges)}; +} + +static llvm::DIType * +createArrayType(const semantics::ArraySpec &ArrayShape, + const semantics::DeclTypeSpec *FlangElementT, + llvm::Type &ElementT, const llvm::DataLayout &DL, + llvm::DIBuilder &DIB) { + auto ArraySizes{getDimensionSubranges(ArrayShape)}; + auto DITy{createIntrinsicType(FlangElementT, ElementT, DL, DIB)}; + SmallVector DISubranges; + transform(std::get(ArraySizes), + std::back_inserter(DISubranges), [&DIB](auto &S) { + return DIB.getOrCreateSubrange(S.second, S.first); + }); + return DIB.createArrayType(std::get(ArraySizes) + ? *std::get(ArraySizes) * + DITy->getSizeInBits() + : 0, + DL.getABITypeAlign(&ElementT).value(), + DITy, DIB.getOrCreateArray(DISubranges)); +} + +static llvm::Type *getPointerElementType(Value &V) { + if (!isa(V.getType())) + return nullptr; + for (auto *U : V.users()) + if (auto *LI{dyn_cast(U)}) + return LI->getType(); + else if (auto *SI{dyn_cast(U)}) + return SI->getValueOperand()->getType(); + else if (auto *GEP{dyn_cast(U)}) + return GEP->getSourceElementType(); + else if (auto *Cast{dyn_cast(U)}) + if (auto *T{getPointerElementType(*Cast)}) + return T; +} + +static void +scheduleToEraseWithOperands(Instruction &I, + SmallPtrSetImpl &Visited, + SmallVectorImpl &ToDelete) { + if (!Visited.insert(&I).second) + return; + ToDelete.push_back(&I); + for (auto &U : I.operands()) { + if (!isa(U)) + continue; + if (auto I{U->user_begin()}; ++I == U->user_end()) + scheduleToEraseWithOperands(*cast(U), Visited, ToDelete); + } +} + +bool FlangDIVariableRetrieverPass::runOnModule(llvm::Module &M) { + DIBuilder DIB{M}; + auto &TfmInfo{getAnalysis()}; + if (!TfmInfo) + return false; + auto &GO{getAnalysis().getOptions()}; + MatcherProvider::initialize( + [&GO](auto &Wrapper) { Wrapper.setOptions(&GO); }); + MatcherProvider::initialize( + [&TfmInfo](auto &Wrapper) { Wrapper.set(TfmInfo.get()); }); + auto &DL{M.getDataLayout()}; + for (auto &F: M) { + auto *DISub{findMetadata(&F)}; + if (!DISub) + continue; + auto *CU{DISub->getUnit()}; + if (!CU || !isFortran(CU->getSourceLanguage())) + continue; + auto *TfmCtx{ + dyn_cast_or_null(TfmInfo->getContext(*CU))}; + if (!TfmCtx || !TfmCtx->hasInstance()) + continue; + auto ASTSub{TfmCtx->getDeclForMangledName(F.getName())}; + if (!ASTSub || ASTSub.isDeclaration()) + continue; + auto &Matcher{getAnalysis(F) + .get() + .getMatcher()}; + SmallPtrSet Visited; + SmallVector ToDelete; + for (auto &I: instructions(F)) { + auto *CB{dyn_cast(&I)}; + if (!CB) + continue; + auto MatchItr{Matcher.find(CB)}; + if (MatchItr == Matcher.end() || + !MatchItr->get().is()) + continue; + auto FlangCS{MatchItr->get().get()}; + if (!FlangCS->typedCall) + continue; + if (StringRef{FlangCS->typedCall->proc().GetName()}.compare_insensitive( + getName(IntrinsicId::ast_reg_var)) != 0) + continue; + scheduleToEraseWithOperands(*CB, Visited, ToDelete); + for (unsigned I = 0, EI = FlangCS->typedCall->arguments().size(); I < EI; + ++I) + if (auto *E{FlangCS->typedCall->UnwrapArgExpr(I)}) + if (auto EDR{evaluate::ExtractDataRef(*E)}) + if (const auto *SR{std::get_if(&EDR->u)}) { + const semantics::ArraySpec *ArrayShape{nullptr}; + if (auto ObjectDetails{ + (**SR).detailsIf()}; + ObjectDetails && ObjectDetails->IsArray() || + ObjectDetails->IsCoarray()) + ArrayShape = &ObjectDetails->shape(); + auto Base{getUnderlyingObject(CB->getArgOperand(I), 0)}; + if (auto *GV{dyn_cast(Base)}) { + DIType *DITy{nullptr}; + if (ArrayShape) { + if (GV->getValueType()->isArrayTy()) { + auto ArrayInfo{arraySize(GV->getValueType())}; + DITy = createArrayType(*ArrayShape, (**SR).GetType(), + *std::get(ArrayInfo), + DL, DIB); + } + } else { + DITy = createIntrinsicType((**SR).GetType(), + *GV->getValueType(), DL, DIB); + } + if (!DITy) + DITy = + createStubType(M, GV->getType()->getAddressSpace(), DIB); + auto *GVE{DIB.createGlobalVariableExpression( + DISub->getFile(), (**SR).name().ToString(), GV->getName(), + DISub->getFile(), DISub->getLine(), DITy, true, + !GV->isDeclaration())}; + GV->addDebugInfo(GVE); + } else if (isa(Base->getType())) { + Function::arg_iterator ArgItr{F.arg_end()}; + DIType *DITy{nullptr}; + if (auto *AI{dyn_cast(Base)}) { + if (!AI->isArrayAllocation()) + DITy = createIntrinsicType( + (**SR).GetType(), *AI->getAllocatedType(), DL, DIB); + } else if (ArgItr = find_if( + F.args(), + [Base](auto &Arg) { return &Arg == Base; }), + ArgItr != F.arg_end()) { + llvm::Type *ElementTy{getPointerElementType(*ArgItr)}; + if (ElementTy) { + if (ArrayShape) + DITy = createArrayType(*ArrayShape, (**SR).GetType(), + *ElementTy, DL, DIB); + else + DITy = createIntrinsicType((**SR).GetType(), *ElementTy, + DL, DIB); + } + } + if (!DITy) + DITy = createStubType( + M, cast(Base->getType())->getAddressSpace(), + DIB); + DILocalVariable *DIVar{nullptr}; + Instruction *InsertBefore{ + &*F.getEntryBlock().getFirstInsertionPt()}; + if (ArgItr != F.arg_end()) { + DIVar = DIB.createParameterVariable( + DISub, (**SR).name().ToString(), ArgItr->getArgNo() + 1, + DISub->getFile(), DISub->getLine(), DITy, false, + DINode::FlagZero); + } else { + DIVar = DIB.createAutoVariable( + DISub, (**SR).name().ToString(), DISub->getFile(), + DISub->getLine(), DITy, false, DINode::FlagZero); + if (auto *I{dyn_cast(Base)}) + InsertBefore = I->getNextNode(); + } + DIB.insertDeclare( + Base, DIVar, DIExpression::get(M.getContext(), {}), + DILocation::get(M.getContext(), DISub->getLine(), 0, DISub), + InsertBefore); + } + } + } + for (auto *I : ToDelete) + I->eraseFromParent(); + } + return false; +} + +ModulePass * llvm::createFlangDIVariableRetrieverPass() { + return new FlangDIVariableRetrieverPass; +} \ No newline at end of file diff --git a/lib/Transform/Mixed/FlangStubs.cpp b/lib/Transform/Mixed/FlangStubs.cpp index 4563982f..af82ab53 100644 --- a/lib/Transform/Mixed/FlangStubs.cpp +++ b/lib/Transform/Mixed/FlangStubs.cpp @@ -32,3 +32,8 @@ void llvm::initializeFlangDummyAliasAnalysisPass(PassRegistry &) {} FunctionPass *llvm::createFlangDummyAliasAnalysis() { return createEmptyFunctionPass(); } + +void llvm::initializeFlangDIVariableRetrieverPassPass(PassRegistry &) {} +ModulePass *llvm::createFlangDIVariableRetrieverPass() { + return createEmptyModulePass(); +} diff --git a/lib/Transform/Mixed/Passes.cpp b/lib/Transform/Mixed/Passes.cpp index 29ca3e21..a4a908e6 100644 --- a/lib/Transform/Mixed/Passes.cpp +++ b/lib/Transform/Mixed/Passes.cpp @@ -32,4 +32,5 @@ void llvm::initializeMixedTransform(PassRegistry &Registry) { initializeDILoopRetrieverPassPass(Registry); initializeDINodeRetrieverPassPass(Registry); initializeFlangDummyAliasAnalysisPass(Registry); + initializeFlangDIVariableRetrieverPassPass(Registry); } From f9bf384b72a676fd00e6e87b2d8d340930a23bac Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Fri, 3 Jun 2022 01:12:43 +0300 Subject: [PATCH 46/93] [TSAR, Tfm, IR] Fix, avoid infinite loop. --- lib/Transform/IR/DeadCodeElimination.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/lib/Transform/IR/DeadCodeElimination.cpp b/lib/Transform/IR/DeadCodeElimination.cpp index e3fc57f3..0911f845 100644 --- a/lib/Transform/IR/DeadCodeElimination.cpp +++ b/lib/Transform/IR/DeadCodeElimination.cpp @@ -110,29 +110,36 @@ bool NoMetadataDSEPass::runOnFunction(Function &F) { // become unused after instruction elimination. for (auto &ASToStore : OnlyStores) for (auto *SI : ASToStore.second) { - SmallVector Worklist, HasUses, Visited; + SmallVector Worklist; + SmallPtrSet HasUses, Visited; for (auto &Op : SI->operands()) if (auto *I = dyn_cast(&Op)) if (!I->mayReadOrWriteMemory()) - Worklist.push_back(I); + if (Visited.insert(I).second) + Worklist.push_back(I); SI->eraseFromParent(); for (;;) { + bool IsChanged{false}; while (!Worklist.empty()) { auto *Inst = Worklist.pop_back_val(); + Visited.erase(Inst); if (Inst->getNumUses() > 0) { - HasUses.push_back(Inst); + HasUses.insert(Inst); } else { for (auto &Op : Inst->operands()) if (auto *I = dyn_cast(&Op)) if (!I->mayReadOrWriteMemory()) - Worklist.push_back(I); + if (Visited.insert(I).second) + Worklist.push_back(I); + HasUses.erase(Inst); Inst->eraseFromParent(); + IsChanged = true; } } - if (HasUses == Visited) + if (!IsChanged) break; Visited = HasUses; - std::swap(Worklist, HasUses); + Worklist.insert(Worklist.end(), HasUses.begin(), HasUses.end()); } } return true; From ea165648041c485261d7199865ad0b423df63a27 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Tue, 19 Jul 2022 17:25:26 +0300 Subject: [PATCH 47/93] [TSAR, Utils, Memory] Extend supported instruction chains which lead to lifetime markers. --- lib/Support/Utils.cpp | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/lib/Support/Utils.cpp b/lib/Support/Utils.cpp index 3beefb77..1bc9b864 100644 --- a/lib/Support/Utils.cpp +++ b/lib/Support/Utils.cpp @@ -100,21 +100,29 @@ bool pointsToLocalMemory(const Value &V, const Loop &L) { if (!isa(V)) return false; bool StartInLoop{false}, EndInLoop{false}; + auto approveLocal = [&L, &StartInLoop, &EndInLoop](auto *V) { + if (auto *II{dyn_cast(V)}) { + auto *BB{II->getParent()}; + if (L.contains(BB)) { + auto ID{II->getIntrinsicID()}; + if (!StartInLoop && ID == llvm::Intrinsic::lifetime_start) + StartInLoop = true; + else if (!EndInLoop && ID == llvm::Intrinsic::lifetime_end) + EndInLoop = true; + if (StartInLoop && EndInLoop) + return true; + } + } + return false; + }; for (auto *V1 : V.users()) - if (auto *BC{dyn_cast(V1)}) + if (approveLocal(V1)) { + return true; + } else if (auto *BC{dyn_cast(V1)}) { for (auto *V2 : BC->users()) - if (auto *II{dyn_cast(V2)}) { - auto *BB{II->getParent()}; - if (L.contains(BB)) { - auto ID{II->getIntrinsicID()}; - if (!StartInLoop && ID == llvm::Intrinsic::lifetime_start) - StartInLoop = true; - else if (!EndInLoop && ID == llvm::Intrinsic::lifetime_end) - EndInLoop = true; - if (StartInLoop && EndInLoop) - return true; - } - } + if (approveLocal(V2)) + return true; + } return false; } From 356b16ee1a80c685068c7715793eb204d9cf9a8a Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Tue, 19 Jul 2022 17:27:29 +0300 Subject: [PATCH 48/93] [TSAR, Utils, IR] Add function to reveal type of an opaque pointer. --- include/tsar/Support/IRUtils.h | 3 ++ lib/Support/Utils.cpp | 58 ++++++++++++++++++++++++++++------ 2 files changed, 52 insertions(+), 9 deletions(-) diff --git a/include/tsar/Support/IRUtils.h b/include/tsar/Support/IRUtils.h index 1b60bf19..611690e0 100644 --- a/include/tsar/Support/IRUtils.h +++ b/include/tsar/Support/IRUtils.h @@ -70,6 +70,9 @@ inline bool hasUnderlyingPointer(llvm::Type *Ty) { return false; } +/// Try to reveal type of an opaque pointer. +llvm::Type *getPointerElementType(const llvm::Value &V); + /// Return true if a specified value points to the memory which is only /// available inside a specific loop. bool pointsToLocalMemory(const llvm::Value &V, const llvm::Loop &L); diff --git a/lib/Support/Utils.cpp b/lib/Support/Utils.cpp index 1bc9b864..c5161219 100644 --- a/lib/Support/Utils.cpp +++ b/lib/Support/Utils.cpp @@ -26,8 +26,9 @@ #include "tsar/Support/IRUtils.h" #include "tsar/Support/MetadataUtils.h" #include "tsar/Support/OutputFile.h" -#include #include +#include +#include #include #include #include @@ -126,6 +127,40 @@ bool pointsToLocalMemory(const Value &V, const Loop &L) { return false; } +llvm::Type *getPointerElementType(const llvm::Value &V) { + if (!llvm::isa(V.getType())) + return nullptr; + if (auto *AI{llvm::dyn_cast(&V)}) + return AI->getAllocatedType(); + if (auto *GV{llvm::dyn_cast(&V)}) + return GV->getValueType(); + if (auto *GEP{llvm::dyn_cast(&V)}) + return GEP->getResultElementType(); + for (auto &U : V.uses()) { + if (auto *LI{llvm::dyn_cast(U.getUser())}) + return LI->getType(); + if (auto *SI{llvm::dyn_cast(U.getUser())}) { + if (SI->getPointerOperand() == U) + return SI->getValueOperand()->getType(); + for (auto &U1 : SI->getPointerOperand()->uses()) + if (auto *LI{llvm::dyn_cast(U1.getUser())}) + if (auto *PointeeTy{getPointerElementType(*LI)}) + return PointeeTy; + } + if (auto *GEP{llvm::dyn_cast(U.getUser())}) + if (GEP->getPointerOperand() == U) + return GEP->getSourceElementType(); + if (auto *Cast{llvm::dyn_cast(U.getUser())}) + if (auto *T{getPointerElementType(*Cast)}) + return T; + if (auto II{llvm::dyn_cast(U.getUser())}) + if (II->isArgOperand(&U)) + if (auto *T{II->getParamElementType(II->getArgOperandNo(&U))}) + return T; + } + return nullptr; +} + llvm::DIType *createStubType(llvm::Module &M, unsigned int AS, llvm::DIBuilder &DIB) { /// TODO (kaniandr@gmail.com): we create a stub instead of an appropriate @@ -248,16 +283,18 @@ llvm::Error OutputFile::clear(StringRef WorkingDir, bool EraseFile) { mOS.reset(); // Ignore errors that occur when trying to discard the temp file. if (EraseFile) { - if (mTemp) - consumeError(mTemp->discard()); + if (useTemporary()) + consumeError(mTemp.front().discard()); if (!mFilename.empty()) llvm::sys::fs::remove(mFilename); + mTemp.clear(); return Error::success(); } - if (!mTemp) + if (!useTemporary()) return Error::success(); - if (mTemp->TmpName.empty()) { - consumeError(mTemp->discard()); + if (getTemporary().TmpName.empty()) { + consumeError(mTemp.front().discard()); + mTemp.clear(); return Error::success(); } SmallString<128> NewOutFile{mFilename}; @@ -265,10 +302,13 @@ llvm::Error OutputFile::clear(StringRef WorkingDir, bool EraseFile) { NewOutFile = WorkingDir; llvm::sys::path::append(NewOutFile, mFilename); } - llvm::Error E = mTemp->keep(NewOutFile); - if (!E) + llvm::Error E = mTemp.front().keep(NewOutFile); + if (!E) { + mTemp.clear(); return Error::success(); - llvm::sys::fs::remove(mTemp->TmpName); + } + llvm::sys::fs::remove(getTemporary().TmpName); + mTemp.clear(); return std::move(E); } } From c5a05abef8bcd3e369b82750c34c373b50561148 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Tue, 19 Jul 2022 17:36:20 +0300 Subject: [PATCH 49/93] [TSAR, Delinearize] Try to reestablish omitted zero subscripts for constant array dimensions. --- .../tsar/Analysis/Memory/Delinearization.h | 5 + lib/Analysis/Memory/Delinearization.cpp | 117 +++++++++++++++--- 2 files changed, 102 insertions(+), 20 deletions(-) diff --git a/include/tsar/Analysis/Memory/Delinearization.h b/include/tsar/Analysis/Memory/Delinearization.h index 474d0ff2..88ccf52b 100644 --- a/include/tsar/Analysis/Memory/Delinearization.h +++ b/include/tsar/Analysis/Memory/Delinearization.h @@ -59,6 +59,7 @@ class Array { }; public: using ExprList = llvm::SmallVector; + using TypeList = llvm::SmallVector; /// Map from linearized index of array to its delinearized representation. struct Range { @@ -92,6 +93,10 @@ class Array { /// This is representation of offset (`Ptr-ArrayPtr`) after delinearization. ExprList Subscripts; + /// List of indexed GEP types, each type is a result of an application of + // a corresponding subscript. + TypeList Types; + /// Creates element referenced with a specified pointer. Initial this /// element is not delinearized yet. explicit Range(llvm::Value *Ptr) : Ptr(Ptr) { diff --git a/lib/Analysis/Memory/Delinearization.cpp b/lib/Analysis/Memory/Delinearization.cpp index 669b8c43..98836879 100644 --- a/lib/Analysis/Memory/Delinearization.cpp +++ b/lib/Analysis/Memory/Delinearization.cpp @@ -93,32 +93,34 @@ namespace { template bool extractSubscriptsFromGEPs( const GEPItrT &GEPBeginItr, const GEPItrT &GEPEndItr, - std::size_t NumberOfDims, SmallVectorImpl &Idxs) { + std::size_t NumberOfDims, + SmallVectorImpl> &Idxs) { assert(Idxs.empty() && "List of indexes must be empty!"); - SmallVector GEPs; + SmallVector, 8> GEPs; for (auto *GEP : make_range(GEPBeginItr, GEPEndItr)) { unsigned NumOperands = GEP->getNumOperands(); if (NumOperands == 2) { - GEPs.push_back(GEP->getOperand(1)); + GEPs.emplace_back(GEP->getOperand(1), + gep_type_begin(GEP).getIndexedType()); } else { - unsigned StructIdx = 0; + SmallVector Types; for (auto I = gep_type_begin(GEP), EI = gep_type_end(GEP); I != EI; ++I) { if (I.isStruct()) { LLVM_DEBUG(dbgs() << "[DELINEARIZE]: drop ending structure\n"); GEPs.clear(); break; } - ++StructIdx; + Types.push_back(I.getIndexedType()); } - if (StructIdx == 0) + if (Types.empty()) continue; - for (unsigned I = StructIdx; I > 1; --I) - GEPs.push_back(GEP->getOperand(I)); + for (unsigned I = Types.size(); I > 1; --I) + GEPs.emplace_back(GEP->getOperand(I), Types[I - 1]); if (auto *SecondOp = dyn_cast(GEP->getOperand(1))) { if (!SecondOp->isZeroValue()) - GEPs.push_back(GEP->getOperand(1)); + GEPs.emplace_back(GEP->getOperand(1), Types[0]); } else { - GEPs.push_back(GEP->getOperand(1)); + GEPs.emplace_back(GEP->getOperand(1), Types[0]); } } } @@ -219,18 +221,72 @@ void DelinearizationPass::cleanSubscripts(Array &ArrayInfo) { LLVM_DEBUG(dbgs() << "[DELINEARIZE]: simplify subscripts for " << (ArrayInfo.isAddressOfVariable() ? "address of " : "") << "base " << ArrayInfo.getBase()->getName() << "\n"); + auto addExtraConstZero = + [this, &ArrayInfo](const Array::Range &Range, unsigned ExtraZeroCount, + const llvm::Type *DereferenceTy, unsigned Idx, + SmallVectorImpl &NewSubscripts) -> bool { + auto *ATy{dyn_cast(DereferenceTy)}; + if (!ATy) + return false; + unsigned IdxE = Range.Types.size(); + for (; ATy && Idx < IdxE; + ATy = dyn_cast(ATy->getElementType())) { + if (ATy->getElementType() == Range.Types[Idx]) { + NewSubscripts.push_back(Range.Subscripts[Idx]); + ++Idx; + } else if (ExtraZeroCount > 0) { + --ExtraZeroCount; + NewSubscripts.push_back(mSE->getZero(mIndexTy)); + } else { + return false; + } + } + if (!ATy) + return Idx == IdxE; + for (unsigned I = 0; I < ExtraZeroCount; ++I) + NewSubscripts.push_back(mSE->getZero(mIndexTy)); + return true; + }; auto LastConstDim = ArrayInfo.getNumberOfDims(); for (LastConstDim; LastConstDim > 0; --LastConstDim) if (!isa(ArrayInfo.getDimSize(LastConstDim - 1))) break; if (LastConstDim == 0) { - for (auto &Range : ArrayInfo) + for (auto &Range : ArrayInfo) { + LLVM_DEBUG(dbgs() << "[DELINEARIZE]: process access "; + Range.Ptr->print(dbgs()); dbgs() << "\n"); + if (auto ExtraZeroCount{ + Range.is(Array::Range::NeedExtraZero) + ? ArrayInfo.getNumberOfDims() - Range.Subscripts.size() + : 0}; + ExtraZeroCount > 0) { + SmallVector NewSubscripts; + auto *BaseType{ArrayInfo.getBase()->getType()}; + if (auto *GV{dyn_cast(ArrayInfo.getBase())}) + BaseType = GV->getValueType(); + else if (auto *AI{dyn_cast(ArrayInfo.getBase())}) + BaseType = AI->getAllocatedType(); + if (!addExtraConstZero(Range, ExtraZeroCount, BaseType, 0, + NewSubscripts)) { + LLVM_DEBUG(dbgs() << "[DELINEARIZE]: unable to delinearize access to " + "constant dimensions\n"); + continue; + } + std::swap(Range.Subscripts, NewSubscripts); + LLVM_DEBUG(dbgs() << "[DELINEARIZE]: add " << ExtraZeroCount + << " extra zero subscripts\n"); + } + assert(Range.Subscripts.size() <= ArrayInfo.getNumberOfDims() && + "Unable to delinearize element access!"); Range.setProperty(Array::Range::IsDelinearized); + } return; } for (auto &Range : ArrayInfo) { if (Range.is(Array::Range::NoGEP)) continue; + LLVM_DEBUG(dbgs() << "[DELINEARIZE]: process access "; + Range.Ptr->print(dbgs()); dbgs() << "\n"); assert((!Range.isElement() || Range.Subscripts.size() == 0 && Range.is(Array::Range::NeedExtraZero) || ArrayInfo.getNumberOfDims() - LastConstDim <= Range.Subscripts.size()) @@ -272,13 +328,32 @@ void DelinearizationPass::cleanSubscripts(Array &ArrayInfo) { } if (DimIdx < DimIdxE) continue; - LLVM_DEBUG(dbgs() << "[DELINEARIZE]: add " << ExtraZeroCount - << " extra zero subscripts to " << SubscriptIdx << "\n"); - for (std::size_t I = 0; I < ExtraZeroCount; ++I) - NewSubscripts.push_back(mSE->getZero(mIndexTy)); - // Add subscripts for constant dimensions. - for (auto EI = Range.Subscripts.size(); SubscriptIdx < EI; ++SubscriptIdx) - NewSubscripts.push_back(Range.Subscripts[SubscriptIdx]); + if (ExtraZeroCount > 0) { + LLVM_DEBUG(dbgs() << "[DELINEARIZE]: add " << ExtraZeroCount + << " extra zero subscripts after " << SubscriptIdx + << "\n"); + if (Range.Subscripts.size() > SubscriptIdx) { + NewSubscripts.push_back(Range.Subscripts[SubscriptIdx]); + if (!addExtraConstZero(Range, ExtraZeroCount, Range.Types[SubscriptIdx], + SubscriptIdx + 1, NewSubscripts)) { + if (SubscriptIdx == 0 || + !(NewSubscripts.pop_back(), + addExtraConstZero(Range, ExtraZeroCount, + Range.Types[SubscriptIdx - 1], SubscriptIdx, + NewSubscripts))) { + LLVM_DEBUG(dbgs() << "[DELINEARIZE]: unable to delinearize\n"); + continue; + } + } + } else { + for (std::size_t I = 0; I < ExtraZeroCount; ++I) + NewSubscripts.push_back(mSE->getZero(mIndexTy)); + } + } else { + // Add subscripts for constant dimensions. + for (auto EI = Range.Subscripts.size(); SubscriptIdx < EI; ++SubscriptIdx) + NewSubscripts.push_back(Range.Subscripts[SubscriptIdx]); + } std::swap(Range.Subscripts, NewSubscripts); assert(Range.Subscripts.size() <= ArrayInfo.getNumberOfDims() && "Unable to delinearize element access!"); @@ -558,7 +633,7 @@ void DelinearizationPass::collectArrays(Function &F) { !(RangePtr == BasePtr && !IsAddressOfVariable) && !(RangePtr == DataPtr && IsAddressOfVariable)) Range.setProperty(Array::Range::NoGEP); - SmallVector SubscriptValues; + SmallVector, 4> SubscriptValues; bool UseAllSubscripts = extractSubscriptsFromGEPs( GEPs.begin(), GEPs.end(), NumberOfDims, SubscriptValues); if (!UseAllSubscripts) @@ -578,8 +653,10 @@ void DelinearizationPass::collectArrays(Function &F) { } if (!SubscriptValues.empty()) { ArrayPtr->setRangeRef(); - for (auto *V : SubscriptValues) + for (auto &&[V, T] : SubscriptValues) { Range.Subscripts.push_back(mSE->getSCEV(V)); + Range.Types.push_back(T); + } } LLVM_DEBUG( dbgs() << "[DELINEARIZE]: number of dimensions " From d10bbce3d8e484601e4ec071257a09cabb0eac84 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Tue, 19 Jul 2022 17:44:16 +0300 Subject: [PATCH 50/93] [TSAR, Memory, MD] Add support for 'after pointer' size for metadata-level memory locations. --- .../tsar/Analysis/Memory/DIEstimateMemory.h | 25 ++++++++---- .../tsar/Analysis/Memory/DIMemoryLocation.h | 37 +++++++++++++---- lib/Analysis/Memory/DIEstimateMemory.cpp | 40 ++++++++++++++----- lib/Analysis/Memory/DIMemoryLocation.cpp | 17 +++++--- lib/Analysis/Memory/Utils.cpp | 3 +- lib/Analysis/Reader/AnalysisReader.cpp | 7 ++-- lib/Unparse/Utils.cpp | 5 ++- tools/tsar-server/PrivateServerPass.cpp | 5 ++- 8 files changed, 99 insertions(+), 40 deletions(-) diff --git a/include/tsar/Analysis/Memory/DIEstimateMemory.h b/include/tsar/Analysis/Memory/DIEstimateMemory.h index cbd8fe21..31a6e5ba 100644 --- a/include/tsar/Analysis/Memory/DIEstimateMemory.h +++ b/include/tsar/Analysis/Memory/DIEstimateMemory.h @@ -280,7 +280,8 @@ class DIEstimateMemory : public DIMemory { enum Flags : uint16_t { NoFlags = 0, Template = 1u << 0, - LLVM_MARK_AS_BITMASK_ENUM(Template) + AfterPointer = 1u << 1, + LLVM_MARK_AS_BITMASK_ENUM(AfterPointer) }; /// Methods for support type inquiry through isa, cast, and dyn_cast. @@ -334,23 +335,31 @@ class DIEstimateMemory : public DIMemory { /// (see DIMemoryLocation for details). bool isTemplate() const { return Template & getFlags(); } + /// Return true if this memory location represents a location after a base + /// pointer. + bool isAfterPointer() const { return AfterPointer & getFlags(); } + /// Returns true if size is known. bool isSized() const { - return DIMemoryLocation( - const_cast(getVariable()), - const_cast(getExpression())).isSized(); + return DIMemoryLocation::get( + const_cast(getVariable()), + const_cast(getExpression()), nullptr, + isTemplate(), isAfterPointer()) + .isSized(); } /// Return size of location, in address units, if it is known. llvm::LocationSize getSize() const { - return DIMemoryLocation( - const_cast(getVariable()), - const_cast(getExpression())).getSize(); + return DIMemoryLocation::get( + const_cast(getVariable()), + const_cast(getExpression()), nullptr, + isTemplate(), isAfterPointer()) + .getSize(); } /// If DW_OP_deref exists it returns true. bool hasDeref() const { - return DIMemoryLocation( + return DIMemoryLocation::get( const_cast(getVariable()), const_cast(getExpression())).hasDeref(); } diff --git a/include/tsar/Analysis/Memory/DIMemoryLocation.h b/include/tsar/Analysis/Memory/DIMemoryLocation.h index 603160cb..23963394 100644 --- a/include/tsar/Analysis/Memory/DIMemoryLocation.h +++ b/include/tsar/Analysis/Memory/DIMemoryLocation.h @@ -74,6 +74,7 @@ struct DIMemoryLocation { llvm::DIExpression *Expr = nullptr; llvm::DILocation *Loc = nullptr; bool Template = false; + bool AfterPointer = false; /// Determines which memory location is exhibits by a specified instruction. static DIMemoryLocation get(llvm::DbgVariableIntrinsic *Inst); @@ -84,17 +85,20 @@ struct DIMemoryLocation { if (auto I = llvm::dyn_cast(Inst)) return get(I); llvm_unreachable("Unsupported memory instruction!"); + return DIMemoryLocation{nullptr, nullptr}; } /// Constructs a new memory location. Note, that variable and expression /// must not be null). - DIMemoryLocation(llvm::DIVariable *Var, llvm::DIExpression *Expr, - llvm::DILocation *Loc = nullptr, bool Template = false) : - Var(Var), Expr(Expr), Loc(Loc), Template(Template) { - // Do not check here that location isValid() because this leads to crash - // of construction of empty key in specialization of llvm::DenseMapInfo. - assert(Var && "Variable must not be null!"); - assert(Expr && "Expression must not be null!"); + static inline DIMemoryLocation get(llvm::DIVariable *Var, + llvm::DIExpression *Expr, + llvm::DILocation *Loc = nullptr, + bool Template = false, + bool AfterPointer = false) { + DIMemoryLocation DILoc{Var, Expr, Loc, Template, AfterPointer}; + if (!DILoc.getSize().mayBeBeforePointer()) + DILoc.AfterPointer = true; + return DILoc; } /// If DW_OP_deref exists it returns true. @@ -130,6 +134,22 @@ struct DIMemoryLocation { /// Checks that representation of memory location is valid (the focus is on /// the expression. bool isValid() const; + +private: + friend struct llvm::DenseMapInfo; + + /// Constructs a new memory location. Note, that variable and expression + /// must not be null). + DIMemoryLocation(llvm::DIVariable *Var, llvm::DIExpression *Expr, + llvm::DILocation *Loc = nullptr, bool Template = false, + bool AfterPointer = false) + : Var(Var), Expr(Expr), Loc(Loc), Template(Template), + AfterPointer(AfterPointer) { + // Do not check here that location isValid() because this leads to crash + // of construction of empty key in specialization of llvm::DenseMapInfo. + assert(Var && "Variable must not be null!"); + assert(Expr && "Expression must not be null!"); + } }; inline bool operator==(DIMemoryLocation LHS, DIMemoryLocation RHS) noexcept { @@ -160,7 +180,8 @@ template<> struct DenseMapInfo { const tsar::DIMemoryLocation &LHS, const tsar::DIMemoryLocation &RHS) { return LHS.Var == RHS.Var && LHS.Expr == RHS.Expr && - LHS.Template == RHS.Template; + LHS.Template == RHS.Template && + LHS.AfterPointer == RHS.AfterPointer; } }; } diff --git a/lib/Analysis/Memory/DIEstimateMemory.cpp b/lib/Analysis/Memory/DIEstimateMemory.cpp index d1a512bc..ef27613b 100644 --- a/lib/Analysis/Memory/DIEstimateMemory.cpp +++ b/lib/Analysis/Memory/DIEstimateMemory.cpp @@ -238,6 +238,10 @@ std::unique_ptr DIEstimateMemory::get( ArrayRef DbgLocs) { assert(Var && "Variable must not be null!"); assert(Expr && "Expression must not be null!"); + auto DILoc{DIMemoryLocation::get(Var, Expr, nullptr, F & Template, + F & AfterPointer)}; + if (DILoc.AfterPointer) + F |= AfterPointer; auto *FlagMD = llvm::ConstantAsMetadata::get( llvm::ConstantInt::get(Type::getInt16Ty(Ctx), F)); SmallVector BasicMDs{ Var, Expr, FlagMD}; @@ -270,6 +274,10 @@ llvm::MDNode * DIEstimateMemory::getRawIfExists(llvm::LLVMContext &Ctx, ArrayRef DbgLocs) { assert(Var && "Variable must not be null!"); assert(Expr && "Expression must not be null!"); + auto DILoc{DIMemoryLocation::get(Var, Expr, nullptr, F & Template, + F & AfterPointer)}; + if (DILoc.AfterPointer) + F |= AfterPointer; auto *FlagMD = llvm::ConstantAsMetadata::getIfExists( llvm::ConstantInt::get(Type::getInt16Ty(Ctx), F)); if (!FlagMD) @@ -993,7 +1001,7 @@ class DIAliasTreeBuilder { auto DIEmptyExpr = DIExpression::get(*mContext, {}); if (mSortedFragments.front()->getNumElements() == 0) { auto DIMVar = DIEstimateMemory::get(*mContext, *mEnv, mVar, DIEmptyExpr, - DIEstimateMemory::NoFlags, mDbgLocs); + DIEstimateMemory::AfterPointer, mDbgLocs); auto IsCorruptedRoot = mCMR->isCorrupted(DIMVar->getBaseAsMDNode()); if (IsCorruptedRoot.first) { if (IsCorruptedRoot.second) @@ -1004,7 +1012,8 @@ class DIAliasTreeBuilder { LLVM_DEBUG(addFragmentLog(DIEmptyExpr)); auto DIM = mDIAT->addNewNode(std::move(DIMVar), *Parent); assert(!DIM.second && "Memory location is already attached to a node!"); - if (auto M = mCMR->beforePromotion(DIMemoryLocation(mVar, DIEmptyExpr))) + if (auto M = + mCMR->beforePromotion(DIMemoryLocation::get(mVar, DIEmptyExpr))) M->replaceAllUsesWith(&*DIM.first); DIM.first->setProperties(DIMemory::Explicit); return; @@ -1024,7 +1033,7 @@ class DIAliasTreeBuilder { void addFragmentLog(llvm::DIExpression *Expr) { dbgs() << "[DI ALIAS TREE]: add a new node and a new fragment "; auto DWLang = getLanguage(mDIAT->getFunction()).getValue(); - printDILocationSource(DWLang, { mVar, Expr }, dbgs()); + printDILocationSource(DWLang, DIMemoryLocation::get(mVar, Expr), dbgs()); dbgs() << "\n"; } #endif @@ -1044,7 +1053,7 @@ class DIAliasTreeBuilder { *Parent); assert(!DIM.second && "Memory location is already attached to a node!"); if (auto M = mCMR->beforePromotion( - DIMemoryLocation(mVar, mSortedFragments[I]))) + DIMemoryLocation::get(mVar, mSortedFragments[I]))) M->replaceAllUsesWith(&*DIM.first); DIM.first->setProperties(DIMemory::Explicit); } @@ -1054,7 +1063,7 @@ class DIAliasTreeBuilder { /// fragment (`mVar`, `Expr`) if this is necessary. DIAliasNode * addUnknownParentIfNecessary( DIAliasNode *Parent, DIExpression *Expr) { - auto *Corrupted = mCMR->hasUnknownParent(DIMemoryLocation(mVar, Expr)); + auto *Corrupted = mCMR->hasUnknownParent(DIMemoryLocation::get(mVar, Expr)); if (!Corrupted) return Parent; Corrupted->erase( @@ -1584,7 +1593,7 @@ void CorruptedMemoryResolver::findNoAliasFragments() { if (VarFragments == mVarToFragments.end()) continue; for (auto *EraseExpr : VarFragments->get()) - mSmallestFragments.erase(DIMemoryLocation{ Loc.Var, EraseExpr }); + mSmallestFragments.erase(DIMemoryLocation::get(Loc.Var, EraseExpr)); VarFragments->get().clear(); continue; } @@ -1619,7 +1628,7 @@ void CorruptedMemoryResolver::findNoAliasFragments() { for (auto *Expr : VarFragments.first->get()) { if (mayAliasFragments(*Expr, *Loc.Expr)) { for (auto *EraseExpr : VarFragments.first->get()) - mSmallestFragments.erase(DIMemoryLocation{ Loc.Var, EraseExpr }); + mSmallestFragments.erase(DIMemoryLocation::get(Loc.Var, EraseExpr)); VarFragments.first->get().clear(); return true; } @@ -1867,8 +1876,9 @@ void CorruptedMemoryResolver::updateWorkLists( LLVM_DEBUG(checkLog(M)); auto Binding = M.getBinding(); if (auto *DIEM = dyn_cast(&M)) { - DIMemoryLocation Loc( - DIEM->getVariable(), DIEM->getExpression(), nullptr, DIEM->isTemplate()); + auto Loc{DIMemoryLocation::get(DIEM->getVariable(), DIEM->getExpression(), + nullptr, DIEM->isTemplate(), + DIEM->isAfterPointer())}; auto FragmentItr = mSmallestFragments.find(Loc); if (FragmentItr != mSmallestFragments.end()) { if (Binding == DIMemory::Destroyed || @@ -2145,7 +2155,8 @@ Optional buildDIMemory(const MemoryLocation &Loc, SmallVector Expr(ReverseExpr.rbegin(), ReverseExpr.rend()); if (Expr.empty()) { auto DIE = DIExpression::get(Ctx, Expr); - DIMemoryLocation DIL(DIInfo.first, DIE, DIInfo.second, IsTemplate); + auto DIL{DIMemoryLocation::get(DIInfo.first, DIE, DIInfo.second, IsTemplate, + !Loc.Size.mayBeBeforePointer())}; // If expression is empty and size can be obtained from a variable than // DW_OP_LLVM_fragment should not be added. If variable will be promoted it // will be represented without this size. So there will be different @@ -2182,13 +2193,16 @@ Optional buildDIMemory(const MemoryLocation &Loc, Expr.append({ dwarf::DW_OP_LLVM_fragment, 0, 0 }); } auto DIE = DIExpression::get(Ctx, Expr); - return DIMemoryLocation(DIInfo.first, DIE, DIInfo.second, IsTemplate); + return DIMemoryLocation::get(DIInfo.first, DIE, DIInfo.second, IsTemplate, + !Loc.Size.mayBeBeforePointer()); } llvm::MDNode * getRawDIMemoryIfExists(llvm::LLVMContext &Ctx, DIMemoryLocation DILoc) { auto F = DILoc.Template ? DIEstimateMemory::Template : DIEstimateMemory::NoFlags; + if (DILoc.AfterPointer) + F |= DIEstimateMemory::AfterPointer; SmallVector Dbgs; if (DILoc.Loc) Dbgs.push_back(DILoc.Loc); @@ -2218,6 +2232,8 @@ std::unique_ptr buildDIMemoryWithNewSize(const EstimateMemory &EM, } else { auto Flags = DILoc->Template ? DIEstimateMemory::Template : DIEstimateMemory::NoFlags; + if (DILoc->AfterPointer) + Flags |= DIEstimateMemory::AfterPointer; SmallVector Dbgs; for (auto *V : EM) if (auto *I = dyn_cast_or_null(V)) @@ -2245,6 +2261,8 @@ llvm::MDNode * getRawDIMemoryIfExists(const EstimateMemory &EM, } else { auto Flags = DILoc->Template ? DIEstimateMemory::Template : DIEstimateMemory::NoFlags; + if (DILoc->AfterPointer) + Flags |= DIEstimateMemory::AfterPointer; SmallVector Dbgs; for (auto *V : EM) if (auto *I = dyn_cast_or_null(V)) diff --git a/lib/Analysis/Memory/DIMemoryLocation.cpp b/lib/Analysis/Memory/DIMemoryLocation.cpp index 44cea79f..c29f92ce 100644 --- a/lib/Analysis/Memory/DIMemoryLocation.cpp +++ b/lib/Analysis/Memory/DIMemoryLocation.cpp @@ -55,10 +55,13 @@ LocationSize DIMemoryLocation::getSize() const { assert(isValid() && "Debug memory location is invalid!"); auto Fragment = Expr->getFragmentInfo(); if (Fragment.hasValue()) - return Fragment->SizeInBits == 0 ? LocationSize::afterPointer() : - LocationSize::precise((Fragment->SizeInBits + 7) / 8); + return Fragment->SizeInBits == 0 + ? !AfterPointer ? LocationSize::beforeOrAfterPointer() + : LocationSize::afterPointer() + : LocationSize::precise((Fragment->SizeInBits + 7) / 8); if (hasDeref()) - return LocationSize::afterPointer(); + return AfterPointer ? LocationSize::afterPointer() + : LocationSize::beforeOrAfterPointer(); if (auto Ty = stripDIType(Var->getType())) { // There is no dereference and size of type is known, so try to determine // size. We should check that the last offset does not lead to out of range @@ -72,7 +75,8 @@ LocationSize DIMemoryLocation::getSize() const { return LocationSize::precise(TySize - Offsets.back()); } // Return UnknownSize in case of out of range memory access. - return LocationSize::afterPointer(); + return AfterPointer ? LocationSize::afterPointer() + : LocationSize::beforeOrAfterPointer(); } void DIMemoryLocation::getOffsets( SmallVectorImpl &Offsets, SmallBitVector &SignMask) const { @@ -126,5 +130,8 @@ DIMemoryLocation DIMemoryLocation::get(DbgVariableIntrinsic *Inst) { auto DbgLoc = Inst->getDebugLoc(); auto *Location = !Var || DbgLoc && DbgLoc.getLine() != 0 ? DbgLoc.get() : DILocation::get(Var->getContext(), Var->getLine(), 0, Var->getScope()); - return {Var, Expr, Location}; + DIMemoryLocation DILoc{Var, Expr, Location}; + if (!DILoc.getSize().mayBeBeforePointer()) + DILoc.AfterPointer = true; + return DILoc; } diff --git a/lib/Analysis/Memory/Utils.cpp b/lib/Analysis/Memory/Utils.cpp index 86f9cacc..3ab1ccb5 100644 --- a/lib/Analysis/Memory/Utils.cpp +++ b/lib/Analysis/Memory/Utils.cpp @@ -182,7 +182,8 @@ bool findGlobalMetadata(const GlobalVariable *Var, } else if (Expr->getNumElements() > 0) { continue; } - DILocs.emplace_back(DIExpr->getVariable(), DIExpr->getExpression()); + DILocs.push_back(DIMemoryLocation::get(DIExpr->getVariable(), + DIExpr->getExpression())); IsChanged = true; } } diff --git a/lib/Analysis/Reader/AnalysisReader.cpp b/lib/Analysis/Reader/AnalysisReader.cpp index 8e4a4249..181ca797 100644 --- a/lib/Analysis/Reader/AnalysisReader.cpp +++ b/lib/Analysis/Reader/AnalysisReader.cpp @@ -484,9 +484,10 @@ bool AnalysisReader::runOnFunction(Function &F) { Var.get() = DefinitionLoc->getLine(); Var.get() = DefinitionLoc->getColumn(); SmallString<32> LocToString; - DIMemoryLocation TmpLoc{ const_cast(DIEM->getVariable()), - const_cast(DIEM->getExpression()), - DefinitionLoc, DIEM->isTemplate() }; + auto TmpLoc{DIMemoryLocation::get( + const_cast(DIEM->getVariable()), + const_cast(DIEM->getExpression()), DefinitionLoc, + DIEM->isTemplate(), DIEM->isAfterPointer())}; if (!unparseToString(*DWLang, TmpLoc, LocToString)) continue; std::replace(LocToString.begin(), LocToString.end(), '*', '^'); diff --git a/lib/Unparse/Utils.cpp b/lib/Unparse/Utils.cpp index 8ad13410..3b29e490 100644 --- a/lib/Unparse/Utils.cpp +++ b/lib/Unparse/Utils.cpp @@ -161,8 +161,9 @@ void printDILocationSource(unsigned DWLang, } }; if (auto EM = dyn_cast(M)) { - DIMemoryLocation TmpLoc{ EM->getVariable(), EM->getExpression(), - nullptr, EM->isTemplate() }; + auto TmpLoc{DIMemoryLocation::get(EM->getVariable(), EM->getExpression(), + nullptr, EM->isTemplate(), + EM->isAfterPointer())}; if (!TmpLoc.isValid()) { O << "<"; O << "sapfor.invalid"; diff --git a/tools/tsar-server/PrivateServerPass.cpp b/tools/tsar-server/PrivateServerPass.cpp index 606cd823..219fc2b9 100644 --- a/tools/tsar-server/PrivateServerPass.cpp +++ b/tools/tsar-server/PrivateServerPass.cpp @@ -1630,10 +1630,11 @@ std::string PrivateServerPass::answerAliasTree(llvm::Module &Module, getLocation(VD->getLocation(), SrcMgr); } } - DIMemoryLocation TmpLoc{ + auto TmpLoc{DIMemoryLocation::get( const_cast(ClonedDIEM->getVariable()), const_cast(ClonedDIEM->getExpression()), - nullptr, ClonedDIEM->isTemplate() }; + nullptr, ClonedDIEM->isTemplate(), + ClonedDIEM->isAfterPointer())}; if (!TmpLoc.isValid()) { AddressOS << "sapfor.invalid"; } else { From ea1d0f3dc1b9037509245d6efc229e063b7858ff Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Tue, 19 Jul 2022 17:45:08 +0300 Subject: [PATCH 51/93] [TSAR, AliasTree, Print] Fix, trivial. --- lib/Analysis/Memory/AliasTreePrinter.cpp | 20 ++++++++++---------- lib/Analysis/Memory/DIAliasTreePrinter.cpp | 8 ++++++++ 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/lib/Analysis/Memory/AliasTreePrinter.cpp b/lib/Analysis/Memory/AliasTreePrinter.cpp index 474c784b..84b6a286 100644 --- a/lib/Analysis/Memory/AliasTreePrinter.cpp +++ b/lib/Analysis/Memory/AliasTreePrinter.cpp @@ -35,6 +35,14 @@ using namespace llvm; using namespace tsar; namespace llvm { +/// TODO (kaniandr@gmail.com): it seems there is a bug in a new LLVM version, +/// so mix of GraphT and GraphT * is used as a parameter fro DOTRgraphTraits. +template <> struct DOTGraphTraits { + static std::string getGraphName(AliasTree **) { + return "Alias Tree"; + } +}; + template<> struct DOTGraphTraits : public DefaultDOTGraphTraits { @@ -61,11 +69,7 @@ template<> struct DOTGraphTraits : printLocationSource(OS, Loc, &G->getDomTree()); OS << (!EM.isExplicit() ? "*" : "") << ' '; } else if (EM.isAmbiguous()) { - OS << "Ambiguous, size "; - if (EM.getSize() == MemoryLocation::UnknownSize) - OS << "unknown"; - else - OS << EM.getSize(); + OS << "Ambiguous, size " << EM.getSize(); OS << (EM.isExplicit() ? ", explicit" : ", implicit"); OS << "\\l"; for (auto Ptr : EM) { @@ -81,11 +85,7 @@ template<> struct DOTGraphTraits : EM.front()->printAsOperand(OS); else EM.front()->print(OS, true); - OS << ", size "; - if (EM.getSize() == MemoryLocation::UnknownSize) - OS << "unknown"; - else - OS << EM.getSize(); + OS << ", size " << EM.getSize(); OS << (EM.isExplicit() ? ", explicit" : ", implicit"); OS << "\\l"; } diff --git a/lib/Analysis/Memory/DIAliasTreePrinter.cpp b/lib/Analysis/Memory/DIAliasTreePrinter.cpp index 688fcf50..7d69e39f 100644 --- a/lib/Analysis/Memory/DIAliasTreePrinter.cpp +++ b/lib/Analysis/Memory/DIAliasTreePrinter.cpp @@ -39,6 +39,14 @@ using namespace llvm; using namespace tsar; namespace llvm { +/// TODO (kaniandr@gmail.com): it seems there is a bug in a new LLVM version, +/// so mix of GraphT and GraphT * is used as a parameter fro DOTRgraphTraits. +template <> struct DOTGraphTraits { + static std::string getGraphName(DIAliasTree **) { + return "Alias Tree (Debug)"; + } +}; + template<> struct DOTGraphTraits : public DefaultDOTGraphTraits { From e1bb694ebcc1be68c2d2f4fc45fd7d8cdd3e9560 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Tue, 19 Jul 2022 17:45:45 +0300 Subject: [PATCH 52/93] [TSAR, Unparser] Avoid empty names. --- lib/Unparse/DIUnparser.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/Unparse/DIUnparser.cpp b/lib/Unparse/DIUnparser.cpp index 492ab09d..5ec61dd7 100644 --- a/lib/Unparse/DIUnparser.cpp +++ b/lib/Unparse/DIUnparser.cpp @@ -132,6 +132,8 @@ bool DIUnparser::unparse(const Value *Expr, SmallVectorImpl &Str) { mIsDITypeEnd = !isa(mDIType) && !isa(mDIType); auto Name = DILoc->Var->getName(); + if (Name.empty()) + Name = "sapfor.null"; Str.append(Name.begin(), Name.end()); } return Result; From b071e041965a5af2560ce68ad4b30f7d3475de46 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Tue, 19 Jul 2022 17:47:00 +0300 Subject: [PATCH 53/93] [TSAR, Unparser] Distinguish string representation of locations with different unknown sizes. --- lib/Unparse/Utils.cpp | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/lib/Unparse/Utils.cpp b/lib/Unparse/Utils.cpp index 3b29e490..0861c614 100644 --- a/lib/Unparse/Utils.cpp +++ b/lib/Unparse/Utils.cpp @@ -51,7 +51,10 @@ void printLocationSource(llvm::raw_ostream &O, const llvm::MemoryLocation &Loc, printLocationSource(O, Loc.Ptr, DT); O << ", "; if (!Loc.Size.hasValue()) - O << "?"; + if (Loc.Size.mayBeBeforePointer()) + O << "?"; + else + O << "+?"; else O << Loc.Size.getValue(); O << ">"; @@ -63,12 +66,18 @@ void printLocationSource(llvm::raw_ostream &O, const MemoryLocationRange &Loc, printLocationSource(O, Loc.Ptr, DT); O << ", "; if (!Loc.LowerBound.hasValue()) - O << "?"; + if (Loc.LowerBound.mayBeBeforePointer()) + O << "?"; + else + O << "+?"; else O << Loc.LowerBound.getValue(); O << ", "; if (!Loc.UpperBound.hasValue()) - O << "?"; + if (Loc.UpperBound.mayBeBeforePointer()) + O << "?"; + else + O << "+?"; else O << Loc.UpperBound.getValue(); if (!IsDebug) { @@ -117,7 +126,10 @@ void printDILocationSource(unsigned DWLang, O << ", "; auto Size = Loc.getSize(); if (!Size.hasValue()) - O << "?"; + if (Size.mayBeBeforePointer()) + O << "?"; + else + O << "+?"; else O << Size.getValue(); O << ">"; @@ -180,7 +192,10 @@ void printDILocationSource(unsigned DWLang, O << ", "; auto Size = TmpLoc.getSize(); if (!Size.hasValue()) - O << "?"; + if (Size.mayBeBeforePointer()) + O << "?"; + else + O << "+?"; else O << Size.getValue(); O << ">"; From ffeeb89a681337fbeef1c86981fc2b87329b7018 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Tue, 19 Jul 2022 17:48:03 +0300 Subject: [PATCH 54/93] [TSAR, Memory] Fix, unknown size comparison. --- include/tsar/Analysis/Memory/MemorySetInfo.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/include/tsar/Analysis/Memory/MemorySetInfo.h b/include/tsar/Analysis/Memory/MemorySetInfo.h index dec6d5f3..2b92ba0a 100644 --- a/include/tsar/Analysis/Memory/MemorySetInfo.h +++ b/include/tsar/Analysis/Memory/MemorySetInfo.h @@ -58,7 +58,7 @@ namespace tsar { /// - static inline uint64_t getNumDims(const LocationTy &) /// Return a number of dimensions of a specified location. /// - static inline bool areJoinable(const LocationTy &, const LocationTy &) -/// Return `true` if one location can be joined to another, `false` +/// Return `true` if one location can be joined to another, `false` /// otherwise. /// - static inline bool join(const LocationTy &What, LocationTy &To) /// Join `What` location to `To` if they are joinable. @@ -104,8 +104,13 @@ template<> struct MemorySetInfo { Loc.Size = Size; } static inline int8_t sizecmp(llvm::LocationSize LHS, llvm::LocationSize RHS) { - if (LHS == RHS || !LHS.hasValue() && !RHS.hasValue()) + if (LHS == RHS) return 0; + if (!LHS.hasValue() && !RHS.hasValue()) { + if (LHS.mayBeBeforePointer()) + return 1; + return -1; + } if (!LHS.hasValue()) return 1; if (!RHS.hasValue()) From 7cc8883d482d76e91fc60a032aca3bd1d6112415 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Tue, 19 Jul 2022 17:48:36 +0300 Subject: [PATCH 55/93] [TSAR, APC] Fix, trivial. --- lib/APC/DistributionLimits.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/APC/DistributionLimits.cpp b/lib/APC/DistributionLimits.cpp index 14856d85..8bfd650c 100644 --- a/lib/APC/DistributionLimits.cpp +++ b/lib/APC/DistributionLimits.cpp @@ -171,7 +171,9 @@ bool APCDistrLimitsChecker::runOnFunction(Function& F) { continue; if (auto *SI{dyn_cast(&I)}) { // Check whether we remember pointer to an array element for further use. - if (auto *Op{SI->getValueOperand()}; Op->getType()->isPointerTy()) { + if (auto *Op{SI->getValueOperand()}; Op->getType()->isPointerTy() && + !isa(Op) && + !isa(Op)) { auto *EM{AT.find(MemoryLocation{Op, LocationSize::precise(1)})}; assert(EM && "Estimate memory must be " "presented in alias tree!"); From 259c4cb2728256b46612fe5e211dfac26bdcdad0 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Tue, 19 Jul 2022 17:53:07 +0300 Subject: [PATCH 56/93] [TSAR, Utils] Fix, overcome error in sys::fs::TempFile move operator. LLVM implementation of `TempFile` do not reset `Done` member (in the assignment targetassigned) if move assignment is used. Is it an error in LLVM implementation? --- include/tsar/Support/OutputFile.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/include/tsar/Support/OutputFile.h b/include/tsar/Support/OutputFile.h index 55a6559b..4a58f9c5 100644 --- a/include/tsar/Support/OutputFile.h +++ b/include/tsar/Support/OutputFile.h @@ -65,7 +65,7 @@ class OutputFile { bool isCreateMssingDirectories() const noexcept { return mCreateMissingDirectories; } - bool useTemporary() const { return mTemp.hasValue(); } + bool useTemporary() const { return !mTemp.empty(); } llvm::StringRef getFilename() const { return mFilename; } llvm::raw_pwrite_stream &getStream() { assert(isValid() && "The file has been already cleared!"); @@ -73,7 +73,7 @@ class OutputFile { } const llvm::sys::fs::TempFile & getTemporary() const { assert(useTemporary() && "Temporary file is not used!"); - return *mTemp; + return mTemp.front(); } bool isValid() const noexcept { return mOS != nullptr; } @@ -87,12 +87,15 @@ class OutputFile { : mFilename(Filename), mBinary(Binary), mRemoveFileOnSignal(RemoveFileOnSignal), mCreateMissingDirectories(CreateMissingDirectories), - mOS(std::move(OS)), mTemp(std::move(Temp)) {} + mOS(std::move(OS)) { + if (Temp) + mTemp.emplace_back(std::move(Temp.getValue())); + } bool mBinary{true}; bool mRemoveFileOnSignal{true}; bool mCreateMissingDirectories{true}; - llvm::Optional mTemp; + llvm::SmallVector mTemp; std::string mFilename; std::unique_ptr mOS; }; From 59e1fdf0708137ee7694ead6e6cae4c4fbfee9c8 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Tue, 19 Jul 2022 17:53:28 +0300 Subject: [PATCH 57/93] [TSAR, Merge] Fix, trivial. --- lib/Frontend/Clang/ASTMergeAction.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Frontend/Clang/ASTMergeAction.cpp b/lib/Frontend/Clang/ASTMergeAction.cpp index ba250648..e6289141 100644 --- a/lib/Frontend/Clang/ASTMergeAction.cpp +++ b/lib/Frontend/Clang/ASTMergeAction.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -91,7 +92,7 @@ class GeneralImporter : public ASTImporter { clang::diag::err_redefinition_different_kind) << Name; } - return make_error(ImportError::NameConflict); + return make_error(ASTImportError::NameConflict); } void Imported(Decl *From, Decl *To) override { From 502d074a61aecffe07b2ab60d09efe65a9d6ac50 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Tue, 19 Jul 2022 17:58:05 +0300 Subject: [PATCH 58/93] [TSAR, Memory] Add support for opaque pointers. --- lib/Analysis/Memory/DefinedMemory.cpp | 5 ++--- lib/Analysis/Memory/EstimateMemory.cpp | 12 ++++++------ lib/Analysis/Memory/TraitFilter.cpp | 2 +- lib/Analysis/Memory/Utils.cpp | 6 ++++-- lib/Transform/Clang/ExprPropagation.cpp | 2 +- lib/Transform/IR/PointerScalarizer.cpp | 4 ++-- lib/Transform/Mixed/DIVariableRetriever.cpp | 15 --------------- lib/Transform/Mixed/Instrumentation.cpp | 3 ++- 8 files changed, 18 insertions(+), 31 deletions(-) diff --git a/lib/Analysis/Memory/DefinedMemory.cpp b/lib/Analysis/Memory/DefinedMemory.cpp index cfb0fcc1..d5e19960 100644 --- a/lib/Analysis/Memory/DefinedMemory.cpp +++ b/lib/Analysis/Memory/DefinedMemory.cpp @@ -266,9 +266,8 @@ std::pair aggregate( } LLVM_DEBUG(dbgs() << "[AGGREGATE] Array info: " << *ArrayPtr->getBase() << ", IsAddress: " << ArrayPtr->isAddressOfVariable() << ".\n"); - auto BaseType = ArrayPtr->getBase()->getType(); - auto ArrayType = cast(BaseType)->getPointerElementType(); - if (ArrayPtr->isAddressOfVariable()) { + auto ArrayType{getPointerElementType(*ArrayPtr->getBase())}; + if (!ArrayType || ArrayPtr->isAddressOfVariable()) { ResLoc.Kind = LocKind::NonCollapsable; return std::make_pair(ResLoc, true); } diff --git a/lib/Analysis/Memory/EstimateMemory.cpp b/lib/Analysis/Memory/EstimateMemory.cpp index 5aab9004..c977c251 100644 --- a/lib/Analysis/Memory/EstimateMemory.cpp +++ b/lib/Analysis/Memory/EstimateMemory.cpp @@ -120,10 +120,11 @@ bool stripMemoryLevel(const DataLayout &DL, MemoryLocation &Loc) { assert(Loc.Ptr && "Pointer to memory location must not be null!"); auto Ty = Loc.Ptr->getType(); if (auto PtrTy = dyn_cast(Ty)) { - auto Size = PtrTy->getPointerElementType()->isSized() + auto *PointeeTy{getPointerElementType(*Loc.Ptr)}; + auto Size = PointeeTy && PointeeTy->isSized() ? LocationSize::precise( - DL.getTypeStoreSize(PtrTy->getPointerElementType())) - : LocationSize::afterPointer(); + DL.getTypeStoreSize(PointeeTy)) + : LocationSize::beforeOrAfterPointer(); if (MemorySetInfo::sizecmp(Size, Loc.Size) > 0) { Loc.Size = Size; return true; @@ -1088,10 +1089,9 @@ bool EstimateMemoryPass::runOnFunction(Function &F) { return; if (AccessedMemory.count(V)) return; - auto PointeeTy = cast(V->getType())->getPointerElementType(); - assert(PointeeTy && "Pointee type must not be null!"); + auto PointeeTy{getPointerElementType(*V)}; addLocation(MemoryLocation( - V, PointeeTy->isSized() + V, PointeeTy && PointeeTy->isSized() ? LocationSize::precise(DL.getTypeStoreSize(PointeeTy)) : LocationSize::afterPointer())); }; diff --git a/lib/Analysis/Memory/TraitFilter.cpp b/lib/Analysis/Memory/TraitFilter.cpp index 3fc047d8..ca32f3a9 100644 --- a/lib/Analysis/Memory/TraitFilter.cpp +++ b/lib/Analysis/Memory/TraitFilter.cpp @@ -41,7 +41,7 @@ void markIfNotPromoted(const DataLayout &DL, DIMemoryTrait &T) { continue; auto *AI = dyn_cast(stripPointer(DL, VH)); if (!AI || AI->isArrayAllocation() || - AI->getType()->getPointerElementType()->isArrayTy()) + AI->getAllocatedType()->isArrayTy()) continue; bool DbgDeclareFound = false; for (auto *U : FindDbgAddrUses(AI)) { diff --git a/lib/Analysis/Memory/Utils.cpp b/lib/Analysis/Memory/Utils.cpp index 3ab1ccb5..6601fc2a 100644 --- a/lib/Analysis/Memory/Utils.cpp +++ b/lib/Analysis/Memory/Utils.cpp @@ -496,9 +496,11 @@ std::pair isPure(const llvm::Function &F, const DefUseSet &DUS) { }) != inst_end(F)) return std::pair{false, false}; for (auto &Arg : F.args()) - if (auto Ty = dyn_cast(Arg.getType())) - if (hasUnderlyingPointer(Ty->getPointerElementType())) + if (auto Ty = dyn_cast(Arg.getType())) { + auto PointeeTy{getPointerElementType(Arg)}; + if (!PointeeTy || hasUnderlyingPointer(PointeeTy)) return std::pair{false, false}; + } auto &DL = F.getParent()->getDataLayout(); bool HasGlobalAccess{false}; for (auto &Range : DUS.getExplicitAccesses()) { diff --git a/lib/Transform/Clang/ExprPropagation.cpp b/lib/Transform/Clang/ExprPropagation.cpp index 6ef2ea54..ddf7b95a 100644 --- a/lib/Transform/Clang/ExprPropagation.cpp +++ b/lib/Transform/Clang/ExprPropagation.cpp @@ -842,7 +842,7 @@ bool ClangExprPropagation::unparseReplacement( if (MD && MD->isValid() && !MD->Template) { if (unparseToString(DWLang, *MD, DefStr, false)) { auto NumberOfDims = 1 + dimensionsNum( - cast(GEP->getType())->getPointerElementType()); + cast(GEP->getSourceElementType())); for(; NumberOfDims > 0 && DefStr.size() > 3; --NumberOfDims) { auto Size = DefStr.size(); if (DefStr[Size - 1] != ']' || diff --git a/lib/Transform/IR/PointerScalarizer.cpp b/lib/Transform/IR/PointerScalarizer.cpp index 8c06e2d6..a3bee968 100644 --- a/lib/Transform/IR/PointerScalarizer.cpp +++ b/lib/Transform/IR/PointerScalarizer.cpp @@ -126,7 +126,7 @@ static inline void insertDbgValueCall(ScalarizerContext &Ctx, Value *V, static void insertLoadInstructions(ScalarizerContext &Ctx) { auto PreheaderBB{Ctx.L->getLoopPreheader()}; - auto *BeforeInstr{new LoadInst(Ctx.V->getType()->getPointerElementType(), + auto *BeforeInstr{new LoadInst(getPointerElementType(*Ctx.V), Ctx.V, "load." + Ctx.V->getName(), &PreheaderBB->back())}; Ctx.InsertedLoad = BeforeInstr; @@ -348,7 +348,7 @@ bool PointerScalarizerPass::runOnFunction(Function &F) { return isa(U) && L->contains(cast(U)); })) continue; - if (Disabled.contains(V) || SingleV || + if (Disabled.contains(V) || SingleV || !getPointerElementType(*V) || (isa(V) && L->contains(cast(V))) || [L, V]() { for (auto *BB : L->getBlocks()) diff --git a/lib/Transform/Mixed/DIVariableRetriever.cpp b/lib/Transform/Mixed/DIVariableRetriever.cpp index 2d7935be..58a4256e 100644 --- a/lib/Transform/Mixed/DIVariableRetriever.cpp +++ b/lib/Transform/Mixed/DIVariableRetriever.cpp @@ -185,21 +185,6 @@ createArrayType(const semantics::ArraySpec &ArrayShape, DITy, DIB.getOrCreateArray(DISubranges)); } -static llvm::Type *getPointerElementType(Value &V) { - if (!isa(V.getType())) - return nullptr; - for (auto *U : V.users()) - if (auto *LI{dyn_cast(U)}) - return LI->getType(); - else if (auto *SI{dyn_cast(U)}) - return SI->getValueOperand()->getType(); - else if (auto *GEP{dyn_cast(U)}) - return GEP->getSourceElementType(); - else if (auto *Cast{dyn_cast(U)}) - if (auto *T{getPointerElementType(*Cast)}) - return T; -} - static void scheduleToEraseWithOperands(Instruction &I, SmallPtrSetImpl &Visited, diff --git a/lib/Transform/Mixed/Instrumentation.cpp b/lib/Transform/Mixed/Instrumentation.cpp index be0b0c4d..e09526b0 100755 --- a/lib/Transform/Mixed/Instrumentation.cpp +++ b/lib/Transform/Mixed/Instrumentation.cpp @@ -821,8 +821,9 @@ Instrumentation::regMemoryAccessArgs(Value *Ptr, const DebugLoc &DbgLoc, Addr->setMetadata("sapfor.da", MD); auto DIVar = createPointerToDI(OpIdx, *DILoc); auto BasePtrTy = cast_or_null(BasePtr->getType()); + auto PointeeTy{getPointerElementType(*BasePtr)}; llvm::Instruction *ArrayBase = - ((BasePtrTy && isa(BasePtrTy->getPointerElementType())) || + ((PointeeTy && isa(PointeeTy)) || (isa(BasePtr) && cast(BasePtr)->isArrayAllocation())) ? new BitCastInst(BasePtr, Type::getInt8PtrTy(Ctx), From 97337181d1c5a01c64fc27f33b9a14d9d9f73049 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Tue, 19 Jul 2022 17:58:37 +0300 Subject: [PATCH 59/93] [TSAR] Fix, trivial, migrate to LLVM 15. --- lib/Transform/Mixed/DINodeRetriever.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Transform/Mixed/DINodeRetriever.cpp b/lib/Transform/Mixed/DINodeRetriever.cpp index a4995bba..3cb1bfa7 100644 --- a/lib/Transform/Mixed/DINodeRetriever.cpp +++ b/lib/Transform/Mixed/DINodeRetriever.cpp @@ -91,7 +91,7 @@ class DINodeRetrieverPass : public ModulePass, private bcl::Uncopyable { auto *DISub = F.getSubprogram(); auto *DIVar = DILocalVariable::getDistinct( Ctx, DISub, "sapfor.var", FileCU, 0, DITy, 0, - DINode::FlagArtificial, AI->getAlignment(), nullptr); + DINode::FlagArtificial, AI->getAlign().value(), nullptr); DIB.insertDeclare(AI, DIVar, DIExpression::get(Ctx, {}), DILocation::get(AI->getContext(), 0, 0, DISub), AI->getParent()); } From de0f08bc369ba0cd1b715014eae9668bca1f8c2b Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Tue, 19 Jul 2022 17:59:55 +0300 Subject: [PATCH 60/93] [TSAR, Intrinsics] Add support for new experimental LLVM memory intrinsic. --- include/tsar/Analysis/KnownFunctionTraits.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/tsar/Analysis/KnownFunctionTraits.h b/include/tsar/Analysis/KnownFunctionTraits.h index 4346496a..bef68aca 100644 --- a/include/tsar/Analysis/KnownFunctionTraits.h +++ b/include/tsar/Analysis/KnownFunctionTraits.h @@ -41,6 +41,7 @@ inline bool isMemoryMarkerIntrinsic(llvm::Intrinsic::ID Id) noexcept { case llvm::Intrinsic::lifetime_start: case llvm::Intrinsic::lifetime_end: case llvm::Intrinsic::invariant_start: case llvm::Intrinsic::invariant_end: case llvm::Intrinsic::sideeffect: case llvm::Intrinsic::assume: + case llvm::Intrinsic::experimental_noalias_scope_decl: return true; } return false; From 1b54b5f96c97b7d334ba7c70e86b02bfe0a4b615 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Wed, 20 Jul 2022 20:35:16 +0300 Subject: [PATCH 61/93] [TSAR, Dep] Fix output, trivial. --- lib/Analysis/Memory/PrivateAnalysis.cpp | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/lib/Analysis/Memory/PrivateAnalysis.cpp b/lib/Analysis/Memory/PrivateAnalysis.cpp index afd350c1..652d5759 100644 --- a/lib/Analysis/Memory/PrivateAnalysis.cpp +++ b/lib/Analysis/Memory/PrivateAnalysis.cpp @@ -1106,6 +1106,8 @@ static EstimateMemoryTrait *mayIgnoreDereference(AliasTrait &Trait, const EstimateMemory *Ptr = Tree.find(MemoryLocation::get(LI)); assert(Ptr && "Estimate memory location must not be null!"); auto Itr = DS.find_as(Ptr->getAliasNode(Tree)); + if (Itr == DS.end()) + return nullptr; if (Itr->is()) continue; return nullptr; @@ -1441,14 +1443,7 @@ class TraitToStringFunctor { T.is() || std::is_same::value && !T.is()) continue; - OS << "<"; - printLocationSource(OS, T.getMemory()->front(), mDT); - OS << ", "; - if (!T.getMemory()->getSize().hasValue()) - OS << "?"; - else - OS << T.getMemory()->getSize().getValue(); - OS << ">"; + printLocationSource(OS, *T.getMemory(), mDT); traitToStr(T.get(), OS); OS << " "; } From 2c925913bce44785f36663bfe1ac3f8d9d17faa9 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Wed, 20 Jul 2022 20:36:46 +0300 Subject: [PATCH 62/93] [TSAR, Dep, Reduction] Set necessary flags to analyze floating point reduction. --- lib/Analysis/Memory/DIDependenceAnalysis.cpp | 25 +++++++++++++------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/lib/Analysis/Memory/DIDependenceAnalysis.cpp b/lib/Analysis/Memory/DIDependenceAnalysis.cpp index a50d6954..defbdd0e 100644 --- a/lib/Analysis/Memory/DIDependenceAnalysis.cpp +++ b/lib/Analysis/Memory/DIDependenceAnalysis.cpp @@ -1678,10 +1678,15 @@ void DIDependencyAnalysisPass::analyzePromoted(Loop *L, BasicBlock *Header = L->getHeader(); Function &F = *Header->getParent(); // Enable analysis of reductions in case of real variables. - bool HasFunNoNaNAttr = - F.getFnAttribute("no-nans-fp-math").getValueAsString() == "true"; - if (!HasFunNoNaNAttr) + FastMathFlags FMF; + FMF.setNoNaNs( + F.getFnAttribute("no-nans-fp-math").getValueAsBool()); + FMF.setNoSignedZeros( + F.getFnAttribute("no-signed-zeros-fp-math").getValueAsBool()); + if (!FMF.noNaNs()) F.addFnAttr("no-nans-fp-math", "true"); + if (!FMF.noSignedZeros()) + F.addFnAttr("no-signed-zeros-fp-math", "true"); for (auto I = L->getHeader()->begin(); isa(I); ++I) { auto *Phi = cast(I); RecurrenceDescriptor RD; @@ -1716,10 +1721,12 @@ void DIDependencyAnalysisPass::analyzePromoted(Loop *L, auto DIEM = dyn_cast(T.getMemory()); if (!DIEM) return; - SourceUnparserImp Unparser(DIMemoryLocation( - const_cast(DIEM->getVariable()), - const_cast(DIEM->getExpression())), - true /*order of dimensions is not important here*/); + SourceUnparserImp Unparser( + DIMemoryLocation::get( + const_cast(DIEM->getVariable()), + const_cast(DIEM->getExpression()), nullptr, + DIEM->isTemplate(), DIEM->isAfterPointer()), + true /*order of dimensions is not important here*/); if (!Unparser.unparse() || Unparser.getIdentifiers().empty()) return; LLVM_DEBUG(dbgs() << "[DA DI]: induction found\n"); @@ -1756,8 +1763,10 @@ void DIDependencyAnalysisPass::analyzePromoted(Loop *L, propagateReduction(Phi, L, DWLang, DIAliasSTR, LockedTraits, Pool); } } - if (!HasFunNoNaNAttr) + if (!FMF.noNaNs()) F.addFnAttr("no-nans-fp-math", "false"); + if (!FMF.noSignedZeros()) + F.addFnAttr("no-signed-zeros-fp-math", "false"); } void DIDependencyAnalysisPass::propagateReduction(PHINode *Phi, From fa6ebd15660d8ca3267eef30d9cd38a81c67a090 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Wed, 20 Jul 2022 20:38:23 +0300 Subject: [PATCH 63/93] [TSAR, Parallel] Fix, specialize cast operations for parallelization info in an appropriate way. --- .../Analysis/Parallel/Parallellelization.h | 69 +++++++++++++++++-- 1 file changed, 62 insertions(+), 7 deletions(-) diff --git a/include/tsar/Analysis/Parallel/Parallellelization.h b/include/tsar/Analysis/Parallel/Parallellelization.h index 05a12f0b..4a6017be 100644 --- a/include/tsar/Analysis/Parallel/Parallellelization.h +++ b/include/tsar/Analysis/Parallel/Parallellelization.h @@ -499,20 +499,75 @@ ParallelItemRef Parallelization::find(llvm::BasicBlock *BB, AnchorT Anchor, } namespace llvm { -template struct simplify_type; -template<> struct simplify_type { - using SimpleType = tsar::ParallelItem *; - static SimpleType getSimplifiedValue(tsar::ParallelItemRef &Ref) { +template <> +struct ValueIsPresent { + using UnwrappedType = tsar::ParallelItem *; + static inline bool isPresent(const tsar::ParallelItemRef &Ref) { + return Ref.isValid(); + } + static inline decltype(auto) unwrapValue(tsar::ParallelItemRef &Ref) { return Ref.getUnchecked(); } }; -template<> struct simplify_type { - using SimpleType = const tsar::ParallelItem *; - static SimpleType getSimplifiedValue(const tsar::ParallelItemRef &Ref) { + +template <> +struct ValueIsPresent { + using UnwrappedType = const tsar::ParallelItem *; + static inline bool isPresent(const tsar::ParallelItemRef &Ref) { + return Ref.isValid(); + } + static inline decltype(auto) unwrapValue(const tsar::ParallelItemRef &Ref) { return Ref.getUnchecked(); } }; +template struct CastIsPossible { + static inline bool isPossible(const tsar::ParallelItemRef &Ref) { + return CastIsPossible::isPossible( + Ref.getUnchecked()); + } +}; + +template struct CastIsPossible { + static inline bool isPossible(const tsar::ParallelItemRef &Ref) { + return CastIsPossible::isPossible( + Ref.getUnchecked()); + } +}; + +template +struct CastInfo + : public CastIsPossible { + using CastReturnType = + typename cast_retty::ret_type; + static inline CastReturnType doCast(tsar::ParallelItemRef &Ref) { + return CastInfo::doCast(Ref.getUnchecked()); + } + static inline CastReturnType castFailed() { return CastReturnType{}; } + static inline CastReturnType doCastIfPossible(tsar::ParallelItemRef &Ref) { + if (!CastInfo::isPossible(Ref)) + return castFailed(); + return doCast(Ref); + } +}; + +template +struct CastInfo + : public CastIsPossible { + using CastReturnType = + typename cast_retty::ret_type; + static inline CastReturnType doCast(const tsar::ParallelItemRef &Ref) { + return CastInfo::doCast(Ref.getUnchecked()); + } + static inline CastReturnType castFailed() { return CastReturnType{}; } + static inline CastReturnType + doCastIfPossible(const tsar::ParallelItemRef &Ref) { + if (!CastInfo::isPossible(Ref)) + return castFailed(); + return doCast(Ref); + } +}; + template <> struct DenseMapInfo { static inline tsar::ParallelItemRef getEmptyKey() { return tsar::ParallelItemRef::getEmptyRef(); From af9380d700b648eec399c39bb4e168eb80841e78 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Wed, 20 Jul 2022 20:40:58 +0300 Subject: [PATCH 64/93] [TSAR, Dep, AddressAccess] Do not collect impossible address accesses. --- lib/Analysis/Memory/DefinedMemory.cpp | 8 +++++++- lib/Analysis/Memory/PrivateAnalysis.cpp | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/Analysis/Memory/DefinedMemory.cpp b/lib/Analysis/Memory/DefinedMemory.cpp index d5e19960..41d80687 100644 --- a/lib/Analysis/Memory/DefinedMemory.cpp +++ b/lib/Analysis/Memory/DefinedMemory.cpp @@ -610,6 +610,9 @@ void DataFlowTraits::initialize( // In LLVM it is not possible to take address of intrinsic function. if (F->isIntrinsic()) return false; + } else if (auto *GV{dyn_cast(V)}; + GV && GV->hasGlobalUnnamedAddr()) { + return false; } return true; }; @@ -631,7 +634,10 @@ void DataFlowTraits::initialize( } auto &DL = I.getModule()->getDataLayout(); // Any call may access some addresses even if it does not access memory. - if (auto *Call = dyn_cast(&I)) { + /// Is it safe to ignore intrinsics here? It seems that all intrinsics in + /// LLVM does not use addresses to perform computations instead of + /// memory accesses. We also ignore intrinsics in PrivateAnalysisPass. + if (auto *Call = dyn_cast(&I); Call && !isa(I)) { bool UnknownAddressAccess = true; auto F = llvm::dyn_cast( Call->getCalledOperand()->stripPointerCasts()); diff --git a/lib/Analysis/Memory/PrivateAnalysis.cpp b/lib/Analysis/Memory/PrivateAnalysis.cpp index 652d5759..7a1c061a 100644 --- a/lib/Analysis/Memory/PrivateAnalysis.cpp +++ b/lib/Analysis/Memory/PrivateAnalysis.cpp @@ -1009,7 +1009,7 @@ void PrivateRecognitionPass::resolveAddresses(DFLoop *L, for (auto *Unknown : DefUse.getAddressUnknowns()) { /// Is it safe to ignore intrinsics here? It seems that all intrinsics in /// LLVM does not use addresses to perform computations instead of - /// memory accesses. + /// memory accesses. We also ignore intrinsics in DefinedMemoryPass. if (isa(Unknown)) continue; const auto *N = mAliasTree->findUnknown(Unknown); From 18a8ad6168ce85edd1653e8b883f1e311453afa4 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Wed, 20 Jul 2022 20:42:49 +0300 Subject: [PATCH 65/93] [TSAR, Memory] Fix, check that locations to compare start at the same point. --- lib/Analysis/Memory/DefinedMemory.cpp | 40 ++++++++++++++++----------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/lib/Analysis/Memory/DefinedMemory.cpp b/lib/Analysis/Memory/DefinedMemory.cpp index 41d80687..510c352e 100644 --- a/lib/Analysis/Memory/DefinedMemory.cpp +++ b/lib/Analysis/Memory/DefinedMemory.cpp @@ -435,22 +435,30 @@ class AddKnownAccessFunctor : addMust(ALoc); } else if (AR.template is()) { int64_t OffsetLoc, OffsetALoc; - GetPointerBaseWithConstantOffset(mLoc.Ptr, OffsetLoc, this->mDL); - GetPointerBaseWithConstantOffset(ALoc.Ptr, OffsetALoc, this->mDL); - // Base - OffsetLoc --------------|mLoc.Ptr| --- mLoc.Size --- | - // Base - OffsetALoc -|ALoc.Ptr| ---- ALoc.Size -------------------- | - //--------------------|ALoc.Ptr|--| ------ addMust(...) ------ | - auto UpperBound = - mLoc.Size.isPrecise() - ? LocationSize::precise( - OffsetLoc - OffsetALoc + mLoc.Size.getValue()) - : mLoc.Size.hasValue() - ? LocationSize::upperBound( - OffsetLoc - OffsetALoc + mLoc.Size.getValue()) - : LocationSize::afterPointer(); - MemoryLocationRange Range(ALoc.Ptr, - LocationSize::precise(OffsetLoc - OffsetALoc), - UpperBound, ALoc.AATags); + auto BaseLoc{ + GetPointerBaseWithConstantOffset(mLoc.Ptr, OffsetLoc, this->mDL)}; + auto BaseALoc{GetPointerBaseWithConstantOffset(ALoc.Ptr, OffsetALoc, + this->mDL)}; + MemoryLocationRange Range; + if (BaseLoc == BaseALoc) { + // Base - OffsetLoc --------------|mLoc.Ptr| --- mLoc.Size --- | + // Base - OffsetALoc -|ALoc.Ptr| ---- ALoc.Size -------------------- + // | + //--------------------|ALoc.Ptr|--| ------ addMust(...) ------ | + auto UpperBound = + mLoc.Size.isPrecise() + ? LocationSize::precise(OffsetLoc - OffsetALoc + + mLoc.Size.getValue()) + : mLoc.Size.hasValue() + ? LocationSize::upperBound(OffsetLoc - OffsetALoc + + mLoc.Size.getValue()) + : mLoc.Size; + Range = MemoryLocationRange{ + ALoc.Ptr, LocationSize::precise(OffsetLoc - OffsetALoc), + UpperBound, ALoc.AATags}; + } else { + Range = MemoryLocationRange{ALoc.Ptr, 0, mLoc.Size , ALoc.AATags}; + } if (this->mDU.hasDef(Range)) continue; addMust(Range); From b822ab5218c373eb52d7c970a87c208b9cc093c5 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Wed, 20 Jul 2022 20:49:22 +0300 Subject: [PATCH 66/93] [TSAR, AliasTree] Fix, find the nearest alias node to find insertion hint for a corrupted location. Even if there are no bound alias node, we try to find nodes which contain locations with similar binded value, to determine insertion hint. Otherwise, locations with smaller sizes become ancestors for bigger locations. --- .../tsar/Analysis/Memory/DIEstimateMemory.h | 11 +++-- lib/Analysis/Memory/DIEstimateMemory.cpp | 44 ++++++++++++++++--- 2 files changed, 45 insertions(+), 10 deletions(-) diff --git a/include/tsar/Analysis/Memory/DIEstimateMemory.h b/include/tsar/Analysis/Memory/DIEstimateMemory.h index 31a6e5ba..d6c52908 100644 --- a/include/tsar/Analysis/Memory/DIEstimateMemory.h +++ b/include/tsar/Analysis/Memory/DIEstimateMemory.h @@ -80,17 +80,22 @@ LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); /// Finds alias nodes which contains memory locations which is bound /// to a specified debug memory location. -void findBoundAliasNodes(const DIEstimateMemory &DIEM, AliasTree &AT, +bool findBoundAliasNodes(const DIEstimateMemory &DIEM, AliasTree &AT, llvm::SmallPtrSetImpl &Nodes); /// Finds alias nodes which contains memory locations which is bound /// to a specified debug memory location. -void findBoundAliasNodes(const DIUnknownMemory &DIUM, AliasTree &AT, +bool findBoundAliasNodes(const DIUnknownMemory &DIUM, AliasTree &AT, llvm::SmallPtrSetImpl &Nodes); /// Finds alias nodes which contains memory locations which is bound /// to a specified debug memory location. -void findBoundAliasNodes(const DIMemory &DIM, AliasTree &AT, +bool findBoundAliasNodes(const DIMemory &DIM, AliasTree &AT, + llvm::SmallPtrSetImpl &Nodes); + +/// Finds alias nodes which contains memory locations which is bound +/// to a specified debug memory location but have lower sizes. +bool findLowerBoundAliasNodes(const DIEstimateMemory &DIEM, AliasTree &AT, llvm::SmallPtrSetImpl &Nodes); /// This represents estimate memory location using metadata information. diff --git a/lib/Analysis/Memory/DIEstimateMemory.cpp b/lib/Analysis/Memory/DIEstimateMemory.cpp index ef27613b..2c1ab970 100644 --- a/lib/Analysis/Memory/DIEstimateMemory.cpp +++ b/lib/Analysis/Memory/DIEstimateMemory.cpp @@ -28,6 +28,7 @@ #include "tsar/Analysis/Memory/DIMemoryEnvironment.h" #include "tsar/Analysis/Memory/DIMemoryLocation.h" #include "tsar/Analysis/Memory/EstimateMemory.h" +#include "tsar/Analysis/Memory/MemorySetInfo.h" #include "tsar/Analysis/Memory/Utils.h" #include "tsar/Support/MetadataUtils.h" #include "tsar/Support/Utils.h" @@ -60,8 +61,9 @@ STATISTIC(NumUnknownMemory, "Number of unknown memory created"); STATISTIC(NumCorruptedMemory, "Number of corrupted memory created"); namespace tsar { -void findBoundAliasNodes(const DIEstimateMemory &DIEM, AliasTree &AT, +bool findBoundAliasNodes(const DIEstimateMemory &DIEM, AliasTree &AT, SmallPtrSetImpl &Nodes) { + bool IsOk{false}; for (auto &VH : DIEM) { if (!VH || isa(VH)) continue; @@ -70,12 +72,36 @@ void findBoundAliasNodes(const DIEstimateMemory &DIEM, AliasTree &AT, // the alias tree. if (!EM) continue; + IsOk = true; Nodes.insert(EM->getAliasNode(AT)); } + return IsOk; } -void findBoundAliasNodes(const DIUnknownMemory &DIUM, AliasTree &AT, +bool findLowerBoundAliasNodes(const DIEstimateMemory &DIEM, AliasTree &AT, + llvm::SmallPtrSetImpl &Nodes) { + bool IsOk{false}; + auto Size{DIEM.getSize()}; + for (auto &VH : DIEM) { + if (!VH || isa(VH)) + continue; + auto EM = AT.find(MemoryLocation(VH, 0)); + // Memory becomes unused after transformation and does not presented in + // the alias tree. + using CT = bcl::ChainTraits; + for (; + EM && MemorySetInfo::sizecmp(Size, EM->getSize()) > 0; + EM = CT::getNext(EM)) { + IsOk = true; + Nodes.insert(EM->getAliasNode(AT)); + } + } + return IsOk; +} + +bool findBoundAliasNodes(const DIUnknownMemory &DIUM, AliasTree &AT, SmallPtrSetImpl &Nodes) { + bool IsOk{false}; for (auto &VH : DIUM) { if (!VH || isa(*VH)) continue; @@ -83,26 +109,28 @@ void findBoundAliasNodes(const DIUnknownMemory &DIUM, AliasTree &AT, if (auto Inst = dyn_cast(VH)) if (auto *N = AT.findUnknown(cast(*VH))) { Nodes.insert(N); - return; + return true; } auto EM = AT.find(MemoryLocation(VH, 0)); // Memory becomes unused after transformation and does not presented in // the alias tree. if (!EM) continue; + IsOk = true; using CT = bcl::ChainTraits; do { Nodes.insert(EM->getAliasNode(AT)); } while (EM = CT::getNext(EM)); } + return IsOk; } -void findBoundAliasNodes(const DIMemory &DIM, AliasTree &AT, +bool findBoundAliasNodes(const DIMemory &DIM, AliasTree &AT, SmallPtrSetImpl &Nodes) { if (auto *EM = dyn_cast(&DIM)) - findBoundAliasNodes(*EM, AT, Nodes); + return findBoundAliasNodes(*EM, AT, Nodes); else - findBoundAliasNodes(cast(DIM), AT, Nodes); + return findBoundAliasNodes(cast(DIM), AT, Nodes); } } @@ -1909,7 +1937,9 @@ void CorruptedMemoryResolver::updateWorkLists( } else { isSameAfterRebuild(cast(M), Replacement); } - findBoundAliasNodes(M, *mAT, Info.NodesWL); + if (!findBoundAliasNodes(M, *mAT, Info.NodesWL) && + isa(M) && Binding != DIMemory::Empty) + findLowerBoundAliasNodes(cast(M), *mAT, Info.NodesWL); } replaceAllMemoryUses(Replacement, Info); } From 3d4318ec4bf1ba46b50be794c8d9f7b1f7e6777d Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Wed, 20 Jul 2022 20:50:00 +0300 Subject: [PATCH 67/93] [TSAR, CMake] Fix, names of LLVM libraries. --- CMakeLists.txt | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 676df82c..71dd7feb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -236,17 +236,18 @@ if(FLANG_FOUND) flangFrontend FortranLower FortranSemantics FortranEvaluate FortranParser FortranDecimal FortranCommon FIRCodeGen FIRTransforms FIRBuilder FIRDialect FIRSupport MLIROpenMPToLLVM MLIROpenMPToLLVMIRTranslation - MLIRLLVMToLLVMIRTranslation MLIROpenACC MLIROpenMP MLIRSCFToControlFlow - MLIRFuncToLLVM MLIRArithmeticToLLVM MLIRControlFlowToLLVM - MLIRTargetLLVMIRExport MLIRDLTI MLIRLLVMIRTransforms MLIRMemRefToLLVM - MLIRParser MLIRSCF MLIRBufferization MLIRAffine MLIRVector MLIRMemRef - MLIRVectorInterfaces MLIRLLVMCommonConversion MLIRTransforms MLIRLLVMIR - MLIRFunc MLIRControlFlow MLIRTensor MLIRArithmeticUtils MLIRComplex - MLIRArithmetic MLIRDialectUtils MLIRTransformUtils MLIRRewrite - MLIRPDLToPDLInterp MLIRPDLInterp MLIRPDL MLIRPass MLIRAnalysis + MLIRLLVMToLLVMIRTranslation MLIROpenACCDialect MLIROpenMPDialect + MLIRSCFToControlFlow MLIRFuncToLLVM MLIRArithmeticToLLVM MLIRControlFlowToLLVM + MLIRTargetLLVMIRExport MLIRDLTIDialect MLIRLLVMIRTransforms MLIRMemRefToLLVM + MLIRParser MLIRSCFDialect MLIRBufferizationDialect MLIRAffineDialect + MLIRVectorDialect MLIRMemRefDialect MLIRVectorInterfaces MLIRLLVMCommonConversion + MLIRTransforms MLIRLLVMDialect MLIRFuncDialect MLIRControlFlowDialect + MLIRTensorDialect MLIRArithmeticUtils MLIRComplexDialect + MLIRArithmeticDialect MLIRDialectUtils MLIRTransformUtils MLIRRewrite + MLIRPDLToPDLInterp MLIRPDLInterpDialect MLIRPDLDialect MLIRPass MLIRAnalysis MLIRCallInterfaces MLIRControlFlowInterfaces MLIRInferTypeOpInterface MLIRSideEffectInterfaces MLIRDataLayoutInterfaces MLIRViewLikeInterface - MLIRIR MLIRSupport) + MLIRInferIntRangeInterface MLIRIR MLIRSupport) include(TestBigEndian) test_big_endian(IS_BIGENDIAN) if (IS_BIGENDIAN) @@ -275,6 +276,7 @@ if(NOT PACKAGE_LLVM AND FLANG_FOUND) target_include_directories(obj.flangFrontend SYSTEM PRIVATE ${LLVM_INCLUDE_DIRS}) target_include_directories(obj.flangFrontendTool SYSTEM PRIVATE ${LLVM_INCLUDE_DIRS}) target_include_directories(obj.FortranLower SYSTEM PRIVATE ${LLVM_INCLUDE_DIRS}) + target_include_directories(obj.FortranEvaluate SYSTEM PRIVATE ${LLVM_INCLUDE_DIRS}) target_include_directories(flang-new SYSTEM PRIVATE ${LLVM_INCLUDE_DIRS}) endif() include_directories(${LLVM_INCLUDE_DIRS}) From 5f1a10c9ab9cf9f806d818d83dd8c4fe8707f719 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Wed, 20 Jul 2022 20:52:54 +0300 Subject: [PATCH 68/93] [TSAR, AliasTree] Fix, try to accurately estimate size of locations at all levels. --- lib/Analysis/Memory/EstimateMemory.cpp | 60 ++++++++++++++++++-------- 1 file changed, 43 insertions(+), 17 deletions(-) diff --git a/lib/Analysis/Memory/EstimateMemory.cpp b/lib/Analysis/Memory/EstimateMemory.cpp index c977c251..657b325a 100644 --- a/lib/Analysis/Memory/EstimateMemory.cpp +++ b/lib/Analysis/Memory/EstimateMemory.cpp @@ -26,6 +26,7 @@ #include "tsar/Analysis/Memory/GlobalsAccess.h" #include "tsar/Analysis/Memory/MemoryAccessUtils.h" #include "tsar/Analysis/Memory/MemorySetInfo.h" +#include "tsar/Support/IRUtils.h" #include "tsar/Unparse/Utils.h" #include #include @@ -58,7 +59,7 @@ static inline void clarifyUnknownSize(const DataLayout &DL, return; int64_t Offset = 0; auto Ptr = GetPointerBaseWithConstantOffset(Loc.Ptr, Offset, DL); - if (Offset != 0) + if (Offset > 0) Ptr = Loc.Ptr; if (auto GV = dyn_cast(Ptr)) { auto Ty = GV->getValueType(); @@ -71,6 +72,8 @@ static inline void clarifyUnknownSize(const DataLayout &DL, Loc.Size = LocationSize::precise( cast(Size)->getValue().getZExtValue() * DL.getTypeStoreSize(Ty)); + else if (Loc.Size.mayBeBeforePointer()) + Loc.Size = LocationSize::afterPointer(); } LLVM_DEBUG(if (Loc.Size.hasValue()) { dbgs() @@ -126,10 +129,32 @@ bool stripMemoryLevel(const DataLayout &DL, MemoryLocation &Loc) { DL.getTypeStoreSize(PointeeTy)) : LocationSize::beforeOrAfterPointer(); if (MemorySetInfo::sizecmp(Size, Loc.Size) > 0) { - Loc.Size = Size; - return true; + auto NewLoc{Loc.getWithNewSize(Size)}; + clarifyUnknownSize(DL, NewLoc); + return NewLoc.Size != Loc.Size ? Loc = std::move(NewLoc), true : false; } auto GEP = dyn_cast(Loc.Ptr); + // It seems that it is safe to not assume unknown size for non-GEP + // instructions. If this pointer is used as an actual parameter + // corresponding location with a correct size is built while a call + // instruction is processed. If there is a GEP instruction which uses + // this pointer, corresponding location with a correct size is built while + // GEP is stripped. If there is a cast to integer and arithmetic operations + // with the casted value, a distinct memory location corresponding to a + // produced result is build. +#if 0 + if (!GEP) { + if (Loc.Size.mayBeBeforePointer()) + return false; + auto NewLoc{Loc.getWithNewSize(!Loc.Size.hasValue() + ? LocationSize::beforeOrAfterPointer() + : LocationSize::afterPointer())}; + clarifyUnknownSize(DL, NewLoc); + return NewLoc.Size != Loc.Size ? Loc = std::move(NewLoc), true : false; + + return false; + } +#endif if (!GEP) return false; unsigned ZeroTailIdx = GEP->getNumOperands(); @@ -178,17 +203,15 @@ bool stripMemoryLevel(const DataLayout &DL, MemoryLocation &Loc) { LLVM_DEBUG(dbgs() << "[ALIAS TREE]: strip GEP to base pointer\n"); Loc.Ptr = GEP->getPointerOperand(); Loc.AATags = llvm::DenseMapInfo::getTombstoneKey(); - if (Loc.Size.hasValue()) { - Type *SrcTy = GEP->getSourceElementType(); - if (SrcTy->isArrayTy() || SrcTy->isStructTy()) { - auto SrcSize = DL.getTypeStoreSize(SrcTy); - APInt GEPOffset(DL.getIndexTypeSizeInBits(GEP->getType()), 0); - if (GEP->accumulateConstantOffset(DL, GEPOffset)) - if (Loc.Size.getValue() + GEPOffset.getSExtValue() <= SrcSize) { - Loc.Size = LocationSize::precise(SrcSize); - return true; - } - } + Type *SrcTy = GEP->getSourceElementType(); + if (SrcTy->isArrayTy() || SrcTy->isStructTy()) { + auto SrcSize = DL.getTypeStoreSize(SrcTy); + APInt GEPOffset(DL.getIndexTypeSizeInBits(GEP->getType()), 0); + if (GEP->accumulateConstantOffset(DL, GEPOffset)) + if (Loc.Size.getValue() + GEPOffset.getSExtValue() <= SrcSize) { + Loc.Size = LocationSize::precise(SrcSize); + return true; + } } } for (;;) { @@ -197,7 +220,9 @@ bool stripMemoryLevel(const DataLayout &DL, MemoryLocation &Loc) { break; Loc.Ptr = BasePtr; } - Loc.Size = LocationSize::afterPointer(); + Loc.Size = Loc.Size.mayBeBeforePointer() + ? LocationSize::beforeOrAfterPointer() + : LocationSize::afterPointer(); clarifyUnknownSize(DL, Loc); return true; } @@ -997,7 +1022,8 @@ AliasTree::insert(const MemoryLocation &Base) { CT::spliceNext(EM, Prev); return std::make_tuple(EM, true, AddAmbiguous); } - if (Base.Size == UpdateChain->getSize()) { + if (MemorySetInfo::sizecmp(Base.Size, + UpdateChain->getSize()) == 0) { UpdateChain->updateAAInfo(Base.AATags); return std::make_tuple(UpdateChain, false, AddAmbiguous); } @@ -1093,7 +1119,7 @@ bool EstimateMemoryPass::runOnFunction(Function &F) { addLocation(MemoryLocation( V, PointeeTy && PointeeTy->isSized() ? LocationSize::precise(DL.getTypeStoreSize(PointeeTy)) - : LocationSize::afterPointer())); + : LocationSize::beforeOrAfterPointer())); }; for (auto &Arg : F.args()) addPointeeIfNeed(&Arg); From f99728ecd2c48531e35551c33d06b5c48675dffa Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Wed, 20 Jul 2022 20:54:10 +0300 Subject: [PATCH 69/93] [TSAR, Inline] Do not try to promote corrupted locations after function inlining. --- lib/Core/Query.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/Core/Query.cpp b/lib/Core/Query.cpp index 10a28876..905c8bae 100644 --- a/lib/Core/Query.cpp +++ b/lib/Core/Query.cpp @@ -348,6 +348,8 @@ void DefaultQueryManager::run(llvm::Module *M, TransformationInfo *TfmInfo) { addAfterFunctionInlineAnalysis( *mGlobalOptions, M->getDataLayout(), [](auto &T) { + if (T.getMemory()->emptyBinding() || T.getMemory()->isOriginal()) + return; unmarkIf(T); unmark(T); }, From 2c1d84779265b20f837cd82954597e990c6d9171 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Wed, 20 Jul 2022 20:55:34 +0300 Subject: [PATCH 70/93] [TSAR, Reduction, Question] Is it correct to consider FMulAdd reduction as Add reduction in SAPFOR. --- lib/Analysis/Memory/DIDependenceAnalysis.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Analysis/Memory/DIDependenceAnalysis.cpp b/lib/Analysis/Memory/DIDependenceAnalysis.cpp index defbdd0e..a17ad95a 100644 --- a/lib/Analysis/Memory/DIDependenceAnalysis.cpp +++ b/lib/Analysis/Memory/DIDependenceAnalysis.cpp @@ -574,6 +574,7 @@ trait::Reduction::Kind getReductionKind( switch (const_cast(RD).getRecurrenceKind()) { case RecurKind::Add: case RecurKind::FAdd: + case RecurKind::FMulAdd: return trait::DIReduction::RK_Add; case RecurKind::Mul: case RecurKind::FMul: @@ -592,7 +593,6 @@ trait::Reduction::Kind getReductionKind( case RecurKind::SMin: case RecurKind::UMin: return trait::DIReduction::RK_Min; - case RecurKind::FMulAdd: case RecurKind::SelectICmp: case RecurKind::SelectFCmp: //TODO (kaniandr@gmail.com): add support for these kinds of reductions. From 45354f1c462b6f499a0417199d109d7cbade70f0 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Fri, 22 Jul 2022 14:37:25 +0300 Subject: [PATCH 71/93] [TSAR, Memory, Delinearize] Fix, do not use 'alloca' to represent a size of an array dimension. Note, SCEV for 'alloca' is SCEVUnknown and has a pointer type, instead of a corresponding variable type. --- lib/Analysis/Memory/Delinearization.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/Analysis/Memory/Delinearization.cpp b/lib/Analysis/Memory/Delinearization.cpp index 98836879..93d2715b 100644 --- a/lib/Analysis/Memory/Delinearization.cpp +++ b/lib/Analysis/Memory/Delinearization.cpp @@ -419,7 +419,7 @@ void DelinearizationPass::fillArrayDimensionsSizes(Array &ArrayInfo) { for (; DimIdx > 0; --DimIdx) { LLVM_DEBUG(dbgs() << "[DELINEARIZE]: process dimension " << DimIdx << "\n"); const SCEV *DimSize; - if (ArrayInfo.isKnownDimSize(DimIdx) > 0) { + if (ArrayInfo.isKnownDimSize(DimIdx)) { DimSize = ArrayInfo.getDimSize(DimIdx); if (DimSize->isZero()) { setUnknownDims(DimIdx); @@ -570,8 +570,12 @@ void DelinearizationPass::findArrayDimensionsFromDbgInfo(Array &ArrayInfo) { if (auto *DII = dyn_cast(U)) DbgInsts.push_back(DII); if (DbgInsts.size() == 1) { - ArrayInfo.setDimSize(DimIdx + PassPtrDim, - mSE->getSCEV(DbgInsts.front()->getVariableLocationOp(0))); + auto *DimSize{ + mSE->getSCEV(DbgInsts.front()->getVariableLocationOp(0))}; + if (!DimSize->getType()->isPointerTy()) + ArrayInfo.setDimSize(DimIdx + PassPtrDim, DimSize); + LLVM_DEBUG(dbgs() + << (isa(DimSize) ? " unknown" : "known ")); } } LLVM_DEBUG(dbgs() << DIVar->getName() << "\n"); From af3cb63f2a36c0bace78675a7c356934034a91d5 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Fri, 22 Jul 2022 14:38:22 +0300 Subject: [PATCH 72/93] [TSAR, Flang, Tfm] Fix, do not crash on error. --- lib/Transform/Flang/ConstantReplacement.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Transform/Flang/ConstantReplacement.cpp b/lib/Transform/Flang/ConstantReplacement.cpp index 848bba1a..77f33f11 100644 --- a/lib/Transform/Flang/ConstantReplacement.cpp +++ b/lib/Transform/Flang/ConstantReplacement.cpp @@ -170,7 +170,7 @@ class ConstantCollector { private: template bool PushBackIfArray(T &N) { - if (auto *E{semantics::GetExpr(N)}) + if (auto *E{semantics::GetExpr(nullptr, N)}) if (auto EDR{evaluate::ExtractDataRef(*E)}) if (const auto *AR{std::get_if(&EDR->u)}) { const semantics::Symbol &A{AR->GetLastSymbol()}; From 3061fcdcbf145cbafbed7048d31f02c0f25f778f Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Fri, 22 Jul 2022 14:41:56 +0300 Subject: [PATCH 73/93] [TSAR, Clang, Tfm, Propagate] Fix, take into account stripped ending zeros in a GEP. --- lib/Transform/Clang/ExprPropagation.cpp | 61 ++++++++++++++++--------- 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/lib/Transform/Clang/ExprPropagation.cpp b/lib/Transform/Clang/ExprPropagation.cpp index ddf7b95a..26904431 100644 --- a/lib/Transform/Clang/ExprPropagation.cpp +++ b/lib/Transform/Clang/ExprPropagation.cpp @@ -93,9 +93,9 @@ class ClangExprPropagation : public FunctionPass, private bcl::Uncopyable { /// /// If `DIDef` is not specified then `Def` must be a constant for successful /// unparsing. - bool unparseReplacement(const Value &Def, const tsar::DIMemoryLocation *DIDef, - unsigned DWLang, const tsar::DIMemoryLocation &DIUse, - SmallVectorImpl &DefStr); + bool unparseReplacement(const Value &Def, const Use &Use, + const tsar::DIMemoryLocation *DIDef, unsigned DWLang, + const tsar::DIMemoryLocation &DIUse, SmallVectorImpl &DefStr); const DataLayout *mDL = nullptr; DominatorTree *mDT = nullptr; @@ -812,9 +812,39 @@ void rememberPossibleAssignment(Value &Def, Instruction &UI, } bool ClangExprPropagation::unparseReplacement( - const Value &Def, const DIMemoryLocation *DIDef, + const Value &Def, const Use &Use, const DIMemoryLocation *DIDef, unsigned DWLang, const DIMemoryLocation &DIUse, SmallVectorImpl &DefStr) { + auto unparseArrayAccess = [DWLang, &Use, &DefStr](Optional &MD) { + if (MD && MD->isValid() && !MD->Template) { + if (unparseToString(DWLang, *MD, DefStr, false)) { + unsigned NumberOfDims = 0; + if (auto *UseGEP{dyn_cast(Use.getUser())}; + UseGEP && + GEPOperator::getPointerOperandIndex() == Use.getOperandNo()) + NumberOfDims = 1 + dimensionsNum(UseGEP->getSourceElementType()); + else if (auto *UseLI{dyn_cast(Use.getUser())}; + UseLI && + LoadInst::getPointerOperandIndex() == Use.getOperandNo()) + NumberOfDims = 1 + dimensionsNum(UseLI->getType()); + else if (auto *UseSI{dyn_cast(Use.getUser())}; + UseSI && + StoreInst::getPointerOperandIndex() == Use.getOperandNo()) + NumberOfDims = 1 + dimensionsNum(UseSI->getValueOperand()->getType()); + else + return false; + for (; NumberOfDims > 0 && DefStr.size() > 3; --NumberOfDims) { + auto Size = DefStr.size(); + if (DefStr[Size - 1] != ']' || DefStr[Size - 2] != '0' || + DefStr[Size - 3] != '[') + return false; + DefStr.resize(Size - 3); + } + return NumberOfDims == 0; + } + } + return false; + }; if (auto *C = dyn_cast(&Def)) { if (auto *CF = dyn_cast(&Def)) { auto *D = mTfmCtx->getDeclForMangledName(CF->getName()); @@ -839,22 +869,11 @@ bool ClangExprPropagation::unparseReplacement( } else if (auto *GEP = dyn_cast(&Def)) { auto MD = buildDIMemory(MemoryLocation::getAfter(GEP), GEP->getContext(), *mDL, *mDT); - if (MD && MD->isValid() && !MD->Template) { - if (unparseToString(DWLang, *MD, DefStr, false)) { - auto NumberOfDims = 1 + dimensionsNum( - cast(GEP->getSourceElementType())); - for(; NumberOfDims > 0 && DefStr.size() > 3; --NumberOfDims) { - auto Size = DefStr.size(); - if (DefStr[Size - 1] != ']' || - DefStr[Size - 2] != '0' || - DefStr[Size - 3] != '[') - return false; - DefStr.resize(Size - 3); - } - return NumberOfDims == 0; - } - } - return false; + return unparseArrayAccess(MD); + } else if (auto *GV = dyn_cast(&Def)) { + auto MD = buildDIMemory(MemoryLocation::getAfter(GV), GV->getContext(), + *mDL, *mDT); + return unparseArrayAccess(MD); } else { return false; } @@ -946,7 +965,7 @@ bool ClangExprPropagation::runOnFunction(Function &F) { if (DIToDeclItr == DIMatcher.end()) continue; SmallString<16> DefStr, UseStr; - if (!unparseReplacement(*Def, DIDef ? &*DIDef : nullptr, + if (!unparseReplacement(*Def, U, DIDef ? &*DIDef : nullptr, *DWLang, DILoc, DefStr)) continue; if (DefStr == DILoc.Var->getName()) From 4a8f17ca2591b04523bc8b9b8878984a7c8cff05 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Fri, 22 Jul 2022 14:44:54 +0300 Subject: [PATCH 74/93] [TSAR, Tool] Fix, language for prcompiled sources is unknown in a current LLVM version. TODO: Clang Tooling doesn't support precompiled sources anymore, so .ast files are not valid input for TSAR now and merge option is not working. --- lib/Core/Tool.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/Core/Tool.cpp b/lib/Core/Tool.cpp index 13d1654d..15112adb 100644 --- a/lib/Core/Tool.cpp +++ b/lib/Core/Tool.cpp @@ -697,12 +697,12 @@ int Tool::run(QueryManager *QM) { } else if (InputKind.getLanguage() == clang::Language::C || InputKind.getLanguage() == clang::Language::CXX) { CSources.push_back(Src); - if (InputKind.getFormat() == InputKind::Precompiled) - CSourcesToMerge.push_back(Src); - else - NoASTCSources.push_back(Src); + NoASTCSources.push_back(Src); } else if (InputKind.getLanguage() == clang::Language::LLVM_IR) { LLSources.push_back(Src); + } else if (InputKind.getLanguage() == clang::Language::Unknown && + InputKind.getFormat() == InputKind::Precompiled) { + CSourcesToMerge.push_back(Src); } else { #ifdef FLANG_FOUND auto FortranKind{ From ea75a7471425cc4b665af423d4b33dea9f60b516 Mon Sep 17 00:00:00 2001 From: Nikita Kataev Date: Fri, 22 Jul 2022 16:38:54 +0300 Subject: [PATCH 75/93] [TSAR, Memory, Server] Fix, use data from client if a function is not available on server. Note, optimization passes may remove function or replace it with another one. Hence, in some cases there is no corresponding function on a server. --- .../tsar/Analysis/Memory/DIClientServerInfo.h | 16 ++- lib/APC/Parallelization.cpp | 8 +- lib/Analysis/Memory/DIClientServerInfo.cpp | 113 +++++++++++------- 3 files changed, 92 insertions(+), 45 deletions(-) diff --git a/include/tsar/Analysis/Memory/DIClientServerInfo.h b/include/tsar/Analysis/Memory/DIClientServerInfo.h index 8fca006c..307d89a0 100644 --- a/include/tsar/Analysis/Memory/DIClientServerInfo.h +++ b/include/tsar/Analysis/Memory/DIClientServerInfo.h @@ -42,6 +42,7 @@ class Value; namespace tsar { class EstimateMemory; +class AnalysisSocket; /// This is a helpful class to obtain analysis results from active server. /// @@ -49,13 +50,22 @@ class EstimateMemory; /// Otherwise, this class is a simple wrapper for analysis on client and /// it returns information from client. struct DIClientServerInfo { - DIClientServerInfo(llvm::Pass &P, llvm::Function &F); + DIClientServerInfo(llvm::Pass &P, llvm::Function &F, + DIAliasTree *ClientDIAT = nullptr, + DIDependencInfo *ClientDIDepInfo = nullptr); + + DIClientServerInfo(AnalysisSocket *Socket, llvm::Function &F, + DIAliasTree *ClientDIAT = nullptr, + DIDependencInfo *ClientDIDepInfo = nullptr); /// Return true if data is available. bool isValid() const noexcept { return DIAT && DIDepInfo; } + bool isValidMemory() const noexcept { return DIAT; } + bool isValideDependecies() const noexcept { return DIDepInfo; } + /// Return true if data is available. operator bool () const noexcept { return isValid(); } @@ -92,6 +102,10 @@ struct DIMemoryClientServerInfo : public DIClientServerInfo { DIMemoryClientServerInfo(DIAliasTree &ClientDIAT, llvm::Pass &P, llvm::Function &F); + /// Obtain analysis from client and server. + DIMemoryClientServerInfo(DIAliasTree &ClientDIAT, AnalysisSocket *Socket, + llvm::Function &F, DIDependencInfo *ClientDIDepInfo = nullptr); + /// Return client-to-server memory pair for a specified estimate memory on /// client. /// diff --git a/lib/APC/Parallelization.cpp b/lib/APC/Parallelization.cpp index 698b576c..64218c11 100644 --- a/lib/APC/Parallelization.cpp +++ b/lib/APC/Parallelization.cpp @@ -895,8 +895,12 @@ void APCParallelizationPass::addRemoteAccessDirectives( auto &TLI{TLIP.getTLI(ClientF)}; auto &Provider{getAnalysis(ClientF)}; auto &ClientDIAT{Provider.get().getAliasTree()}; - DIMemoryClientServerInfo ClientServerInfo(ClientDIAT, *this, ClientF); - auto &F{cast(*ClientServerInfo.getValue(&ClientF))}; + AnalysisSocket *Socket{nullptr}; + if (auto *SI{getAnalysisIfAvailable()}) + Socket = (*SI)->getActiveSocket(); + DIMemoryClientServerInfo ClientServerInfo(ClientDIAT, Socket, ClientF); + assert(ClientServerInfo.isValidMemory() && + "Analysis information must be available!"); SmallPtrSet DistinctMemory; for (auto &DIM : make_range(ClientServerInfo.DIAT->memory_begin(), ClientServerInfo.DIAT->memory_end())) diff --git a/lib/Analysis/Memory/DIClientServerInfo.cpp b/lib/Analysis/Memory/DIClientServerInfo.cpp index d2bfa25c..708cbce9 100644 --- a/lib/Analysis/Memory/DIClientServerInfo.cpp +++ b/lib/Analysis/Memory/DIClientServerInfo.cpp @@ -32,77 +32,106 @@ using namespace tsar; using namespace llvm; -DIClientServerInfo::DIClientServerInfo(llvm::Pass &P, llvm::Function &F) { - if (auto *SI = P.getAnalysisIfAvailable()) { - if (auto *Socket = (*SI)->getActiveSocket()) { - if (auto R = Socket->getAnalysis()) { - auto *Matcher = R->value(); - getMetadata = [Matcher](Metadata *MD) { - assert(MD && "Metadata must not be null!"); - auto ServerMD = (*Matcher)->getMappedMD(MD); - return ServerMD ? *ServerMD : nullptr; - }; - getObjectID = [Matcher](ObjectID ID) { - assert(ID && "Object ID must not be null!"); - auto ServerID = (*Matcher)->getMappedMD(ID); - return ServerID ? cast(*ServerID) : nullptr; - }; - getValue = [Matcher](Value *V) { - assert(V && "Value must not be null!"); - return (**Matcher)[V]; - }; - auto ServerF = cast(getValue(&F)); - assert(ServerF && "Mapped function must exist on server!"); +void static initialize(AnalysisSocket &Socket, llvm::Function &F, + DIAliasTree *ClientDIAT, + DIDependencInfo *ClientDIDepInfo, + DIClientServerInfo &This) { + if (auto R = Socket.getAnalysis()) { + auto *Matcher = R->value(); + This.getMetadata = [Matcher](Metadata *MD) { + assert(MD && "Metadata must not be null!"); + auto ServerMD = (*Matcher)->getMappedMD(MD); + return ServerMD ? *ServerMD : nullptr; + }; + This.getObjectID = [Matcher](ObjectID ID) { + assert(ID && "Object ID must not be null!"); + auto ServerID = (*Matcher)->getMappedMD(ID); + return ServerID ? cast(*ServerID) : nullptr; + }; + This.getValue = [Matcher](Value *V) { + assert(V && "Value must not be null!"); + return (**Matcher)[V]; + }; + auto ServerV = This.getValue(&F); + if (ServerV) { + auto ServerF = cast(ServerV); auto *MemoryMatcher = (**R->value())[*ServerF]; assert(MemoryMatcher && "Memory matcher must exist!"); - getMemory = [MemoryMatcher](DIMemory *M) { + This.getMemory = [MemoryMatcher](DIMemory *M) { assert(M && "Memory must not be null!"); auto Itr = MemoryMatcher->find(M); return Itr != MemoryMatcher->end() ? Itr->get() : nullptr; }; - getClientMemory = [MemoryMatcher](DIMemory *M) { + This.getClientMemory = [MemoryMatcher](DIMemory *M) { assert(M && "Memory must not be null!"); auto Itr = MemoryMatcher->find(M); return Itr != MemoryMatcher->end() ? Itr->get() : nullptr; }; - if (auto R = Socket->getAnalysis< - DIEstimateMemoryPass, DIDependencyAnalysisPass>(F)) { - DIAT = &R->value()->getAliasTree(); - DIDepInfo = + if (auto R = Socket.getAnalysis(F)) { + This.DIAT = &R->value()->getAliasTree(); + This.DIDepInfo = &R->value()->getDependencies(); } } } - } +} + +DIClientServerInfo::DIClientServerInfo(llvm::Pass &P, llvm::Function &F, + DIAliasTree *ClientDIAT, + DIDependencInfo *ClientDIDepInfo) { + if (auto *SI{P.getAnalysisIfAvailable()}) + if (auto *Socket{(*SI)->getActiveSocket()}) + initialize(*Socket, F, ClientDIAT, ClientDIDepInfo, *this); if (!DIAT || !DIDepInfo) { LLVM_DEBUG(dbgs() << "[SERVER INFO]: analysis server is not available\n"); - if (auto *DIATP = P.getAnalysisIfAvailable()) - if (DIATP->isConstructed()) - DIAT = &DIATP->getAliasTree(); - else - return; - else - return; - if (auto *DIDepP = P.getAnalysisIfAvailable()) - DIDepInfo = &DIDepP->getDependencies(); - else - return; + DIAT = ClientDIAT; + DIDepInfo = ClientDIDepInfo; + if (!DIAT) + if (auto *DIATP = P.getAnalysisIfAvailable()) + if (DIATP->isConstructed()) + DIAT = &DIATP->getAliasTree(); + if (!DIDepInfo) + if (auto *DIDepP = P.getAnalysisIfAvailable()) + DIDepInfo = &DIDepP->getDependencies(); LLVM_DEBUG( + if (isValid()) dbgs() << "[SERVER INFO]: use dependence analysis from client\n"); } } +DIClientServerInfo::DIClientServerInfo(AnalysisSocket *Socket, + llvm::Function &F, + DIAliasTree *ClientDIAT, + DIDependencInfo *ClientDIDepInfo) { + if (Socket) + initialize(*Socket, F, ClientDIAT, ClientDIDepInfo, *this); + if (!DIAT || !DIDepInfo) { + LLVM_DEBUG(dbgs() << "[SERVER INFO]: analysis server is not available\n"); + DIAT = ClientDIAT; + DIDepInfo = ClientDIDepInfo; + LLVM_DEBUG(if (isValid()) dbgs() + << "[SERVER INFO]: use dependence analysis from client\n"); + } +} + DIMemoryClientServerInfo::DIMemoryClientServerInfo(DIAliasTree &ClientDIAT, llvm::Pass &P, llvm::Function &F) : DIClientServerInfo(P, F), ClientDIAT(&ClientDIAT) {} +DIMemoryClientServerInfo::DIMemoryClientServerInfo( + DIAliasTree &ClientDIAT, AnalysisSocket *Socket, llvm::Function &F, + DIDependencInfo *ClientDIDepInfo) + : DIClientServerInfo(Socket, F, &ClientDIAT, ClientDIDepInfo), + ClientDIAT(&ClientDIAT) {} + bcl::tagged_pair< bcl::tagged, bcl::tagged> DIMemoryClientServerInfo::findFromClient(const EstimateMemory &EM, const DataLayout &DL, DominatorTree &DT) const { - assert(isValid() && "Results is not available!"); + assert(isValidMemory() && "Results is not available!"); auto RawDIM = getRawDIMemoryIfExists(EM, EM.front()->getContext(), DL, DT); if (!RawDIM) return std::make_pair(nullptr, nullptr); @@ -116,7 +145,7 @@ bcl::tagged_pair< bcl::tagged, bcl::tagged> DIMemoryClientServerInfo::findFromClient(Value &V, DominatorTree &DT, DIUnknownMemory::Flags F) const { - assert(isValid() && "Results is not available!"); + assert(isValidMemory() && "Results is not available!"); auto RawDIM = getRawDIMemoryIfExists(V, V.getContext(), DT, F); if (!RawDIM) return std::make_pair(nullptr, nullptr); From b6b471afbd74f9c30c454f4c0c04cbdb692ccc24 Mon Sep 17 00:00:00 2001 From: Viacheslav Kozyrev Date: Sun, 27 Dec 2020 13:27:33 +0300 Subject: [PATCH 76/93] [TSAR, Distribution] Initialize pass that will do loop distribution. --- .../tsar/Transform/Clang/LoopDistribution.h | 52 +++ include/tsar/Transform/Clang/Passes.h | 6 + .../Memory/DIMemoryAnalysisServer.cpp | 4 +- lib/Transform/Clang/CMakeLists.txt | 2 +- lib/Transform/Clang/LoopDistribution.cpp | 310 ++++++++++++++++++ lib/Transform/Clang/Passes.cpp | 1 + 6 files changed, 373 insertions(+), 2 deletions(-) create mode 100644 include/tsar/Transform/Clang/LoopDistribution.h create mode 100644 lib/Transform/Clang/LoopDistribution.cpp diff --git a/include/tsar/Transform/Clang/LoopDistribution.h b/include/tsar/Transform/Clang/LoopDistribution.h new file mode 100644 index 00000000..de783636 --- /dev/null +++ b/include/tsar/Transform/Clang/LoopDistribution.h @@ -0,0 +1,52 @@ +//===-- LoopDistribution.h - Source-level Loop Distribution (Clang) - C++ -===// +// +// Traits Static Analyzer (SAPFOR) +// +// Copyright 2020 DVM System Group +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +// +// This file declares a pass that makes loop distribution transformation. +// +//===----------------------------------------------------------------------===// + +#ifndef TSAR_LOOP_DISTRIBUTION_H +#define TSAR_LOOP_DISTRIBUTION_H + +#include "tsar/Transform/Clang/Passes.h" +#include +#include + +namespace llvm { + /// \brief This function pass makes loop distribution transformation. + class LoopDistributionPass : public FunctionPass, private bcl::Uncopyable { + public: + /// Pass identification, replacement for typeid. + static char ID; + + /// Default constructor. + LoopDistributionPass() : FunctionPass(ID) { + initializeLoopDistributionPassPass( + *PassRegistry::getPassRegistry()); + } + + /// Makes loop distribution transformation. + bool runOnFunction(Function& F) override; + + /// Specifies a list of analyzes that are necessary for this pass. + void getAnalysisUsage(AnalysisUsage& AU) const override; + }; +} +#endif// TSAR_LOOP_DISTRIBUTION_H \ No newline at end of file diff --git a/include/tsar/Transform/Clang/Passes.h b/include/tsar/Transform/Clang/Passes.h index dcb9dc5f..40fcd800 100644 --- a/include/tsar/Transform/Clang/Passes.h +++ b/include/tsar/Transform/Clang/Passes.h @@ -114,5 +114,11 @@ void initializeClangLoopReversePass(PassRegistry &Registry); /// Create a pass to reverse loop. ModulePass *createClangLoopReverse(); + +/// Creates a pass to perform elimination of dead declarations. +FunctionPass* createLoopDistributionPass(); + +/// Initializes a pass to perform elimination of dead declarations. +void initializeLoopDistributionPassPass(PassRegistry& Registry); } #endif//TSAR_CLANG_TRANSFORM_PASSES_H diff --git a/lib/Analysis/Memory/DIMemoryAnalysisServer.cpp b/lib/Analysis/Memory/DIMemoryAnalysisServer.cpp index 1a1e2765..2dc9e2a6 100644 --- a/lib/Analysis/Memory/DIMemoryAnalysisServer.cpp +++ b/lib/Analysis/Memory/DIMemoryAnalysisServer.cpp @@ -26,6 +26,7 @@ #include "tsar/Analysis/AnalysisServer.h" #include "tsar/Analysis/Memory/ClonedDIMemoryMatcher.h" #include "tsar/Analysis/Memory/DefinedMemory.h" +#include "tsar/Analysis/Memory/DependenceAnalysis.h" #include "tsar/Analysis/Memory/DIArrayAccess.h" #include "tsar/Analysis/Memory/DIDependencyAnalysis.h" #include "tsar/Analysis/Memory/DIMemoryEnvironment.h" @@ -53,7 +54,8 @@ static void initializeDIMemoryAnalysisServerResponsePass(PassRegistry &); namespace { /// This provides access to function-level analysis results on server. using DIMemoryAnalysisServerProvider = -FunctionPassAAProvider; +FunctionPassAAProvider; /// List of responses available from server (client may request corresponding /// analysis, in case of provider all analysis related to a provider may diff --git a/lib/Transform/Clang/CMakeLists.txt b/lib/Transform/Clang/CMakeLists.txt index d5eef742..06607207 100644 --- a/lib/Transform/Clang/CMakeLists.txt +++ b/lib/Transform/Clang/CMakeLists.txt @@ -2,7 +2,7 @@ set(TRANSFORM_SOURCES Passes.cpp ExprPropagation.cpp Inline.cpp RenameLocal.cpp DeadDeclsElimination.cpp Format.cpp OpenMPAutoPar.cpp DVMHWriter.cpp SharedMemoryAutoPar.cpp DVMHDirecitves.cpp DVMHSMAutoPar.cpp DVMHDataTransferIPO.cpp StructureReplacement.cpp LoopInterchange.cpp - LoopReversal.cpp) + LoopReversal.cpp LoopDistribution.cpp) if(MSVC_IDE) file(GLOB_RECURSE TRANSFORM_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/lib/Transform/Clang/LoopDistribution.cpp b/lib/Transform/Clang/LoopDistribution.cpp new file mode 100644 index 00000000..d1d0ff6c --- /dev/null +++ b/lib/Transform/Clang/LoopDistribution.cpp @@ -0,0 +1,310 @@ +//=== MergeLoops.cpp --- High Level Loops Merger (Clang)---------*- C++ -*-===// +// +// Traits Static Analyzer (SAPFOR) +// +// Copyright 2020 DVM System Group +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +// +// This file defines a pass that makes loop distribution transformation. +// +//===----------------------------------------------------------------------===// + +#include "tsar/Transform/Clang/LoopDistribution.h" +#include "tsar/ADT/SpanningTreeRelation.h" +#include "tsar/Analysis/AnalysisServer.h" +#include "tsar/Analysis/Clang/CanonicalLoop.h" +#include "tsar/Analysis/Clang/LoopMatcher.h" +#include "tsar/Analysis/DFRegionInfo.h" +#include "tsar/Analysis/Memory/DependenceAnalysis.h" +#include "tsar/Analysis/Memory/DIClientServerInfo.h" +#include "tsar/Analysis/Memory/DIDependencyAnalysis.h" +#include "tsar/Analysis/Memory/DIEstimateMemory.h" +#include "tsar/Analysis/Memory/EstimateMemory.h" +#include "tsar/Analysis/Memory/MemoryAccessUtils.h" +#include "tsar/Analysis/Memory/MemoryTraitUtils.h" +#include "tsar/Core/Query.h" +#include "tsar/Frontend/Clang/TransformationContext.h" +#include "tsar/Support/GlobalOptions.h" +#include "tsar/Unparse/Utils.h" +#include +#include +#include +#include // TODO: use SmallVector + +using namespace llvm; +using namespace clang; +using namespace tsar; + +#undef DEBUG_TYPE +#define DEBUG_TYPE "loop-distribution" + +namespace { + class LoopDistributionPassInfo final : public PassGroupInfo { + void addBeforePass(legacy::PassManager& Passes) const override { + addImmutableAliasAnalysis(Passes); + addInitialTransformations(Passes); + Passes.add(createAnalysisSocketImmutableStorage()); + Passes.add(createDIMemoryTraitPoolStorage()); + Passes.add(createDIMemoryEnvironmentStorage()); + Passes.add(createDIEstimateMemoryPass()); + Passes.add(createDependenceAnalysisWrapperPass()); + Passes.add(createDIMemoryAnalysisServer()); + Passes.add(createAnalysisWaitServerPass()); + Passes.add(createMemoryMatcherPass()); + Passes.add(createAnalysisWaitServerPass()); + } + void addAfterPass(legacy::PassManager& Passes) const override { + Passes.add(createAnalysisReleaseServerPass()); + Passes.add(createAnalysisCloseConnectionPass()); + } + }; +} + +char LoopDistributionPass::ID = 0; +INITIALIZE_PASS_IN_GROUP_BEGIN(LoopDistributionPass, "loop-distribution", + "Loop Distribution", false, false, + TransformationQueryManager::getPassRegistry()) +INITIALIZE_PASS_IN_GROUP_INFO(LoopDistributionPassInfo) +INITIALIZE_PASS_DEPENDENCY(TransformationEnginePass) +INITIALIZE_PASS_DEPENDENCY(EstimateMemoryPass) +INITIALIZE_PASS_DEPENDENCY(DIEstimateMemoryPass) +INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass) +INITIALIZE_PASS_DEPENDENCY(DependenceAnalysisWrapperPass) +INITIALIZE_PASS_DEPENDENCY(DFRegionInfoPass) +INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass) +INITIALIZE_PASS_DEPENDENCY(CanonicalLoopPass) +INITIALIZE_PASS_DEPENDENCY(LoopMatcherPass) +INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass) +INITIALIZE_PASS_DEPENDENCY(GlobalOptionsImmutableWrapper) +INITIALIZE_PASS_IN_GROUP_END(LoopDistributionPass, "loop-distribution", + "Loop Distribution", false, false, + TransformationQueryManager::getPassRegistry()) + +namespace { +class ASTVisitor : public RecursiveASTVisitor { +public: + // TODO: Make this constructor similar to CanonicalLoop constructor. + ASTVisitor(DFRegionInfo& DFRI, TargetLibraryInfo &TLI, + AliasTree& AT, DominatorTree& DT, + DIMemoryClientServerInfo &DIMInfo, + const CanonicalLoopSet& CLI, + const LoopMatcherPass::LoopMatcher& LM, DIAliasTree &DIAT, + DIDependencInfo DIDep, const GlobalOptions& GO, + AnalysisClientServerMatcherWrapper* Matcher, + DependenceInfo& DepInfo) : + mDFRI(DFRI), mTLI(TLI), mAliasTree(AT), mDomTree(DT), + mDIMInfo(DIMInfo), mCLI(CLI), + mLM(LM), mDIAT(DIAT), mDIDep(DIDep), + mGO(GO), mDepInfo(DepInfo), + mSTR(SpanningTreeRelation(mDIMInfo.DIAT)) { + mGetLoopID = [Matcher](ObjectID ID) { + auto ServerID = (*Matcher)->getMappedMD(ID); + return ServerID ? cast(*ServerID) : nullptr; + }; + mGetInstruction = [Matcher](Instruction* I) { + return cast((**Matcher)[I]); + }; + } + + bool TraverseStmt(Stmt *S) { + if (!S) + return RecursiveASTVisitor::TraverseStmt(S); + ForStmt* ForS = dyn_cast(S); + if (!ForS) + return RecursiveASTVisitor::TraverseStmt(S); + dbgs() << "Loop\n"; + auto Match = mLM.find(ForS); + if (Match == mLM.end()) + return false; + Loop* L = Match->get(); + auto LoopDepth = L->getLoopDepth(); + auto DFL = dyn_cast(mDFRI.getRegionFor(L)); + auto CanonItr = mCLI.find_as(DFL); + if (CanonItr == mCLI.end() || !(**CanonItr).isCanonical() || + !(**CanonItr).getLoop()) + return false; + dbgs() << "Canonical loop\n"; + // TODO: see ParallelLoop.cpp, probably I should use for_each_loop instead. + MDNode* LoopID = L->getLoopID(); + if (!LoopID || !(LoopID = mGetLoopID(LoopID))) { + dbgs() << "Ignore loop without ID"; + return false; + } + auto DepItr = mDIDep.find(LoopID); + if (DepItr == mDIDep.end()) + return false; + auto& DIDepSet = DepItr->get(); + DenseSet Coverage; + accessCoverage(DIDepSet, mDIAT, Coverage, + mGO.IgnoreRedundantMemory); + for (auto& TS : DIDepSet) { + if (!Coverage.count(TS.getNode())) + continue; + auto& DIMTraitItr = *TS.begin(); + auto* DIMem = DIMTraitItr->getMemory(); + if (DIMTraitItr->is()) { + printDILocationSource(dwarf::DW_LANG_C, *DIMem, dbgs()); + dbgs() << "Flow dependency\n"; + } + if (DIMTraitItr->is()) { + printDILocationSource(dwarf::DW_LANG_C, *DIMem, dbgs()); + dbgs() << "Antiflow dependency\n"; + } + if (!DIMTraitItr->is_any()) + continue; + auto* DINode = DIMem->getAliasNode(); + for (auto BB = L->block_begin(); BB != L->block_end(); ++BB) { + std::vector Reads, Writes; + for_each_memory(**BB, mTLI, [this, DINode, &Reads, &Writes](Instruction& I, + MemoryLocation&& Loc, unsigned Idx, AccessInfo R, AccessInfo W) { + auto EM = mAliasTree.find(Loc); + assert(EM && "Estimate memory location must not be null!"); + auto& DL = I.getModule()->getDataLayout(); + auto* DIM = mDIMInfo.findFromClient( + *EM->getTopLevelParent(), DL, mDomTree).get(); + if (!DIM || mSTR.isUnreachable(DINode, DIM->getAliasNode())) { + return; + } + dbgs() << "Instruction\n"; + if (W != AccessInfo::No) { + Writes.push_back(&I); + dbgs() << "Write\n"; + } + if (R != AccessInfo::No) { + Reads.push_back(&I); + dbgs() << "Read\n"; + } + }, [](Instruction& I, AccessInfo, AccessInfo W) { + // TODO: fill in + }); + for (auto Write = Writes.begin(); Write != Writes.end(); ++Write) { + for (auto Read = Reads.begin(); Read != Reads.end(); ++Read) { + auto Dep = mDepInfo.depends(mGetInstruction(*Write), mGetInstruction(*Read), false); // TODO: probably true instead of false + if (!Dep) continue; + auto Dir = Dep.get()->getDirection(LoopDepth); + if (Dir == tsar_impl::Dependence::DVEntry::EQ) { + // TODO: Probably don't need to do so + dbgs() << "Ignore loop independent dependence\n"; + continue; + } + if (Dir == tsar_impl::Dependence::DVEntry::ALL) { + dbgs() << "Flow and anti\n"; + } + else if (Dep.get()->isFlow()) + if (Dir == tsar_impl::Dependence::DVEntry::LT || + Dir == tsar_impl::Dependence::DVEntry::LE) { + dbgs() << "Flow\n"; + } else { + dbgs() << "Anti\n"; + } + else if (Dep.get()->isAnti()) + if (Dir == tsar_impl::Dependence::DVEntry::LT || + Dir == tsar_impl::Dependence::DVEntry::LE) { + dbgs() << "Anti\n"; + } + else { + dbgs() << "Flow\n"; + } + } + } + } + //auto *Alias = DIMem.getAliasNode(); + } + // + bool PrevIsInsideLoop = mIsInsideLoop; + mIsInsideLoop = true; + bool result = RecursiveASTVisitor::TraverseStmt(ForS->getBody()); + mIsInsideLoop = PrevIsInsideLoop; + return result; + } + +private: + DFRegionInfo& mDFRI; + TargetLibraryInfo& mTLI; + AliasTree& mAliasTree; + DominatorTree& mDomTree; + DIMemoryClientServerInfo& mDIMInfo; + const CanonicalLoopSet& mCLI; + const LoopMatcherPass::LoopMatcher& mLM; + DIAliasTree& mDIAT; + DIDependencInfo mDIDep; + const GlobalOptions& mGO; + std::function mGetLoopID; + std::functionmGetInstruction; + DependenceInfo& mDepInfo; + SpanningTreeRelation mSTR; + bool mIsInsideLoop = false; +}; +} + +bool LoopDistributionPass::runOnFunction(Function& F) { + auto M = F.getParent(); + auto& TfmInfo = getAnalysis(); + if (!TfmInfo) + return false; + auto TfmCtx = TfmInfo->getContext(*M); + if (!TfmCtx || !TfmCtx->hasInstance()) + return false; + auto FuncDecl = TfmCtx->getDeclForMangledName(F.getName()); + if (!FuncDecl) + return false; + auto& RgnInfo = getAnalysis().getRegionInfo(); + auto& TLI = getAnalysis().getTLI(F); + auto& AliasTree = getAnalysis().getAliasTree(); + auto& DIEMPass = getAnalysis(); + assert(DIEMPass.isConstructed() && "Alias tree must be constructed!"); + auto& DIMInfo = DIMemoryClientServerInfo(DIEMPass.getAliasTree(), *this, F); + auto& DomTree = getAnalysis().getDomTree(); + auto& SocketInfo = getAnalysis().get(); + auto& Socket = SocketInfo.getActive()->second; + auto RF = + Socket.getAnalysis(F); + assert(RF && "Dependence analysis must be available!"); + auto& DIAT = RF->value()->getAliasTree(); + auto& DIDepInfo = RF->value()->getDependencies(); + auto& DepInfo = RF->value()->getDI(); + + auto RM = Socket.getAnalysis(); + assert(RM && "Client to server IR-matcher must be available!"); + auto Matcher = RM->value(); + auto& GlobalOpts = getAnalysis() + .getOptions(); + auto& CLI = getAnalysis().getCanonicalLoopInfo(); + auto& LoopInfo = getAnalysis().getMatcher(); + ASTVisitor LoopVisitor(RgnInfo, TLI, AliasTree, DomTree, DIMInfo, CLI, LoopInfo, DIAT, DIDepInfo, GlobalOpts, Matcher, DepInfo); + LoopVisitor.TraverseDecl(FuncDecl); + return false; +} + +void LoopDistributionPass::getAnalysisUsage(AnalysisUsage& AU) const { + AU.addRequired(); + AU.addRequired(); + AU.addRequired(); + AU.addRequired(); + AU.addRequired(); + AU.addRequired(); + AU.addRequired(); + AU.addRequired(); + AU.addRequired(); + AU.addRequired(); + AU.addRequired(); + AU.addRequired(); + AU.setPreservesAll(); +} + +FunctionPass* llvm::createLoopDistributionPass() { + return new LoopDistributionPass(); +} \ No newline at end of file diff --git a/lib/Transform/Clang/Passes.cpp b/lib/Transform/Clang/Passes.cpp index 1414a706..1a91ad1d 100644 --- a/lib/Transform/Clang/Passes.cpp +++ b/lib/Transform/Clang/Passes.cpp @@ -39,4 +39,5 @@ void llvm::initializeClangTransform(PassRegistry &Registry) { initializeDVMHDataTransferIPOPassPass(Registry); initializeClangLoopInterchangePass(Registry); initializeClangLoopReversePass(Registry); + initializeLoopDistributionPassPass(Registry); } From 9f1b30a479607a32c9773f72d25f0f042ce60038 Mon Sep 17 00:00:00 2001 From: Viacheslav Kozyrev Date: Sun, 27 Dec 2020 14:17:02 +0300 Subject: [PATCH 77/93] [TSAR, Distribution] Refactor constructor of ASTVisitor. --- lib/Transform/Clang/LoopDistribution.cpp | 99 ++++++++++++++---------- 1 file changed, 56 insertions(+), 43 deletions(-) diff --git a/lib/Transform/Clang/LoopDistribution.cpp b/lib/Transform/Clang/LoopDistribution.cpp index d1d0ff6c..48f0f430 100644 --- a/lib/Transform/Clang/LoopDistribution.cpp +++ b/lib/Transform/Clang/LoopDistribution.cpp @@ -96,20 +96,29 @@ INITIALIZE_PASS_IN_GROUP_END(LoopDistributionPass, "loop-distribution", namespace { class ASTVisitor : public RecursiveASTVisitor { public: - // TODO: Make this constructor similar to CanonicalLoop constructor. - ASTVisitor(DFRegionInfo& DFRI, TargetLibraryInfo &TLI, - AliasTree& AT, DominatorTree& DT, - DIMemoryClientServerInfo &DIMInfo, - const CanonicalLoopSet& CLI, - const LoopMatcherPass::LoopMatcher& LM, DIAliasTree &DIAT, - DIDependencInfo DIDep, const GlobalOptions& GO, - AnalysisClientServerMatcherWrapper* Matcher, - DependenceInfo& DepInfo) : - mDFRI(DFRI), mTLI(TLI), mAliasTree(AT), mDomTree(DT), - mDIMInfo(DIMInfo), mCLI(CLI), - mLM(LM), mDIAT(DIAT), mDIDep(DIDep), - mGO(GO), mDepInfo(DepInfo), - mSTR(SpanningTreeRelation(mDIMInfo.DIAT)) { + ASTVisitor(FunctionPass& P, Function& F) { + mDFRI = &P.getAnalysis().getRegionInfo(); + mTLI = &P.getAnalysis().getTLI(F); + mAliasTree = &P.getAnalysis().getAliasTree(); + mDomTree = &P.getAnalysis().getDomTree(); + auto& DIEMPass = P.getAnalysis(); + assert(DIEMPass.isConstructed() && "Alias tree must be constructed!"); + mDIMInfo = new DIMemoryClientServerInfo(DIEMPass.getAliasTree(), P, F); + mSTR = new SpanningTreeRelation(mDIMInfo->DIAT); + mCLI = &P.getAnalysis().getCanonicalLoopInfo(); + mLM = &P.getAnalysis().getMatcher(); + mGO = &P.getAnalysis().getOptions(); + auto& SocketInfo = P.getAnalysis().get(); + auto& Socket = SocketInfo.getActive()->second; + auto RF = Socket.getAnalysis(F); + assert(RF && "Dependence analysis must be available!"); + mDIAT = &RF->value()->getAliasTree(); + mDIDep = &RF->value()->getDependencies(); + mDepInfo = &RF->value()->getDI(); + auto RM = Socket.getAnalysis(); + assert(RM && "Client to server IR-matcher must be available!"); + auto Matcher = RM->value(); mGetLoopID = [Matcher](ObjectID ID) { auto ServerID = (*Matcher)->getMappedMD(ID); return ServerID ? cast(*ServerID) : nullptr; @@ -126,14 +135,14 @@ class ASTVisitor : public RecursiveASTVisitor { if (!ForS) return RecursiveASTVisitor::TraverseStmt(S); dbgs() << "Loop\n"; - auto Match = mLM.find(ForS); - if (Match == mLM.end()) + auto Match = mLM->find(ForS); + if (Match == mLM->end()) return false; Loop* L = Match->get(); auto LoopDepth = L->getLoopDepth(); - auto DFL = dyn_cast(mDFRI.getRegionFor(L)); - auto CanonItr = mCLI.find_as(DFL); - if (CanonItr == mCLI.end() || !(**CanonItr).isCanonical() || + auto DFL = dyn_cast(mDFRI->getRegionFor(L)); + auto CanonItr = mCLI->find_as(DFL); + if (CanonItr == mCLI->end() || !(**CanonItr).isCanonical() || !(**CanonItr).getLoop()) return false; dbgs() << "Canonical loop\n"; @@ -143,13 +152,13 @@ class ASTVisitor : public RecursiveASTVisitor { dbgs() << "Ignore loop without ID"; return false; } - auto DepItr = mDIDep.find(LoopID); - if (DepItr == mDIDep.end()) + auto DepItr = mDIDep->find(LoopID); + if (DepItr == mDIDep->end()) return false; auto& DIDepSet = DepItr->get(); DenseSet Coverage; - accessCoverage(DIDepSet, mDIAT, Coverage, - mGO.IgnoreRedundantMemory); + accessCoverage(DIDepSet, *mDIAT, Coverage, + mGO->IgnoreRedundantMemory); for (auto& TS : DIDepSet) { if (!Coverage.count(TS.getNode())) continue; @@ -168,14 +177,14 @@ class ASTVisitor : public RecursiveASTVisitor { auto* DINode = DIMem->getAliasNode(); for (auto BB = L->block_begin(); BB != L->block_end(); ++BB) { std::vector Reads, Writes; - for_each_memory(**BB, mTLI, [this, DINode, &Reads, &Writes](Instruction& I, + for_each_memory(**BB, *mTLI, [this, DINode, &Reads, &Writes](Instruction& I, MemoryLocation&& Loc, unsigned Idx, AccessInfo R, AccessInfo W) { - auto EM = mAliasTree.find(Loc); + auto EM = mAliasTree->find(Loc); assert(EM && "Estimate memory location must not be null!"); auto& DL = I.getModule()->getDataLayout(); - auto* DIM = mDIMInfo.findFromClient( - *EM->getTopLevelParent(), DL, mDomTree).get(); - if (!DIM || mSTR.isUnreachable(DINode, DIM->getAliasNode())) { + auto* DIM = mDIMInfo->findFromClient( + *EM->getTopLevelParent(), DL, *mDomTree).get(); + if (!DIM || mSTR->isUnreachable(DINode, DIM->getAliasNode())) { return; } dbgs() << "Instruction\n"; @@ -192,7 +201,9 @@ class ASTVisitor : public RecursiveASTVisitor { }); for (auto Write = Writes.begin(); Write != Writes.end(); ++Write) { for (auto Read = Reads.begin(); Read != Reads.end(); ++Read) { - auto Dep = mDepInfo.depends(mGetInstruction(*Write), mGetInstruction(*Read), false); // TODO: probably true instead of false + // TODO: probably true instead of false + auto Dep = mDepInfo->depends(mGetInstruction(*Write), + mGetInstruction(*Read), false); if (!Dep) continue; auto Dir = Dep.get()->getDirection(LoopDepth); if (Dir == tsar_impl::Dependence::DVEntry::EQ) { @@ -232,20 +243,20 @@ class ASTVisitor : public RecursiveASTVisitor { } private: - DFRegionInfo& mDFRI; - TargetLibraryInfo& mTLI; - AliasTree& mAliasTree; - DominatorTree& mDomTree; - DIMemoryClientServerInfo& mDIMInfo; - const CanonicalLoopSet& mCLI; - const LoopMatcherPass::LoopMatcher& mLM; - DIAliasTree& mDIAT; - DIDependencInfo mDIDep; - const GlobalOptions& mGO; + DFRegionInfo* mDFRI; + TargetLibraryInfo* mTLI; + AliasTree* mAliasTree; + DominatorTree* mDomTree; + DIMemoryClientServerInfo* mDIMInfo; + SpanningTreeRelation* mSTR; + const CanonicalLoopSet* mCLI; + const LoopMatcherPass::LoopMatcher* mLM; + const GlobalOptions* mGO; + DIAliasTree* mDIAT; + DIDependencInfo* mDIDep; + DependenceInfo* mDepInfo; std::function mGetLoopID; - std::functionmGetInstruction; - DependenceInfo& mDepInfo; - SpanningTreeRelation mSTR; + std::function mGetInstruction; bool mIsInsideLoop = false; }; } @@ -261,6 +272,7 @@ bool LoopDistributionPass::runOnFunction(Function& F) { auto FuncDecl = TfmCtx->getDeclForMangledName(F.getName()); if (!FuncDecl) return false; + /* auto& RgnInfo = getAnalysis().getRegionInfo(); auto& TLI = getAnalysis().getTLI(F); auto& AliasTree = getAnalysis().getAliasTree(); @@ -284,7 +296,8 @@ bool LoopDistributionPass::runOnFunction(Function& F) { .getOptions(); auto& CLI = getAnalysis().getCanonicalLoopInfo(); auto& LoopInfo = getAnalysis().getMatcher(); - ASTVisitor LoopVisitor(RgnInfo, TLI, AliasTree, DomTree, DIMInfo, CLI, LoopInfo, DIAT, DIDepInfo, GlobalOpts, Matcher, DepInfo); + */ + ASTVisitor LoopVisitor(*this, F); LoopVisitor.TraverseDecl(FuncDecl); return false; } From f910f8593297da63a34085bec60c9abde78b94e7 Mon Sep 17 00:00:00 2001 From: Viacheslav Kozyrev Date: Sat, 16 Jan 2021 12:23:02 +0300 Subject: [PATCH 78/93] [TSAR, Distribution] Get split instructions --- lib/Transform/Clang/LoopDistribution.cpp | 39 ++++++++++++++++++------ 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/lib/Transform/Clang/LoopDistribution.cpp b/lib/Transform/Clang/LoopDistribution.cpp index 48f0f430..543724a3 100644 --- a/lib/Transform/Clang/LoopDistribution.cpp +++ b/lib/Transform/Clang/LoopDistribution.cpp @@ -146,7 +146,7 @@ class ASTVisitor : public RecursiveASTVisitor { !(**CanonItr).getLoop()) return false; dbgs() << "Canonical loop\n"; - // TODO: see ParallelLoop.cpp, probably I should use for_each_loop instead. + // TODO: See ParallelLoop.cpp, probably I should use for_each_loop instead. MDNode* LoopID = L->getLoopID(); if (!LoopID || !(LoopID = mGetLoopID(LoopID))) { dbgs() << "Ignore loop without ID"; @@ -175,7 +175,9 @@ class ASTVisitor : public RecursiveASTVisitor { if (!DIMTraitItr->is_any()) continue; auto* DINode = DIMem->getAliasNode(); + // TODO: For now I consider the only block in the loop for (auto BB = L->block_begin(); BB != L->block_end(); ++BB) { + // Get all reads and writes of memory leading to dependencies std::vector Reads, Writes; for_each_memory(**BB, *mTLI, [this, DINode, &Reads, &Writes](Instruction& I, MemoryLocation&& Loc, unsigned Idx, AccessInfo R, AccessInfo W) { @@ -190,18 +192,18 @@ class ASTVisitor : public RecursiveASTVisitor { dbgs() << "Instruction\n"; if (W != AccessInfo::No) { Writes.push_back(&I); - dbgs() << "Write\n"; } if (R != AccessInfo::No) { Reads.push_back(&I); - dbgs() << "Read\n"; } }, [](Instruction& I, AccessInfo, AccessInfo W) { - // TODO: fill in + // TODO: Fill in }); + // Try to get write instructions after which should be split + std::set Splits(Writes.begin(), Writes.end()); for (auto Write = Writes.begin(); Write != Writes.end(); ++Write) { for (auto Read = Reads.begin(); Read != Reads.end(); ++Read) { - // TODO: probably true instead of false + // TODO: Probably true instead of false auto Dep = mDepInfo->depends(mGetInstruction(*Write), mGetInstruction(*Read), false); if (!Dep) continue; @@ -211,26 +213,43 @@ class ASTVisitor : public RecursiveASTVisitor { dbgs() << "Ignore loop independent dependence\n"; continue; } + // Get direction of the dependency + bool Flow = false, Anti = false; if (Dir == tsar_impl::Dependence::DVEntry::ALL) { - dbgs() << "Flow and anti\n"; + Flow = true; + Anti = true; } else if (Dep.get()->isFlow()) if (Dir == tsar_impl::Dependence::DVEntry::LT || Dir == tsar_impl::Dependence::DVEntry::LE) { - dbgs() << "Flow\n"; + Flow = true; } else { - dbgs() << "Anti\n"; + Anti = true; } else if (Dep.get()->isAnti()) if (Dir == tsar_impl::Dependence::DVEntry::LT || Dir == tsar_impl::Dependence::DVEntry::LE) { - dbgs() << "Anti\n"; + Anti = true; } else { - dbgs() << "Flow\n"; + Flow = true; } + bool WriteBeforeRead = (**Write).comesBefore(*Read); + // If this is bad instructions dependency, can't split between them + if (!(WriteBeforeRead && Anti || !WriteBeforeRead && Flow)) continue; + // TODO: More effective version of this algo + for (auto Split = Writes.begin(); *Split != *Write; ++Split) { + if (!Splits.count(*Split)) continue; + if ((**Split).comesBefore(*Write) && (**Read).comesBefore(*Split) + || (**Split).comesBefore(*Read) && (**Write).comesBefore(*Split)) { + Splits.erase(*Split); + } + } } } + for (auto Split = Splits.begin(); Split != Splits.end(); ++Split) { + (**Split).dump(); + } } //auto *Alias = DIMem.getAliasNode(); } From b864c8a8a4899d76693cbd0283bece47973e8361 Mon Sep 17 00:00:00 2001 From: Viacheslav Kozyrev Date: Mon, 1 Mar 2021 12:12:14 +0300 Subject: [PATCH 79/93] [TSAR, InstructionMatcher] Initialize AST--IR matcher --- .../tsar/Analysis/Clang/InstructionMatcher.h | 86 ++++++++++ include/tsar/Analysis/Clang/Passes.h | 8 + lib/Analysis/Clang/CMakeLists.txt | 2 +- lib/Analysis/Clang/InstructionMatcher.cpp | 159 ++++++++++++++++++ lib/Analysis/Clang/Passes.cpp | 1 + lib/Transform/Clang/LoopDistribution.cpp | 3 + 6 files changed, 258 insertions(+), 1 deletion(-) create mode 100644 include/tsar/Analysis/Clang/InstructionMatcher.h create mode 100644 lib/Analysis/Clang/InstructionMatcher.cpp diff --git a/include/tsar/Analysis/Clang/InstructionMatcher.h b/include/tsar/Analysis/Clang/InstructionMatcher.h new file mode 100644 index 00000000..57404beb --- /dev/null +++ b/include/tsar/Analysis/Clang/InstructionMatcher.h @@ -0,0 +1,86 @@ +//=== InstructionMatcher.h - High and Low Level Instruction Matcher -- C++ ===// +// +// Traits Static Analyzer (SAPFOR) +// +// Copyright 2021 DVM System Group +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +// +// Classes and functions from this file match AST statements in a high-level +// code and appropriate instructions in low-level LLVM IR. This file implements +// pass to perform this functionality. +// +//===----------------------------------------------------------------------===// + +#ifndef TSAR_INSTRUCTION_MATCHER_H +#define TSAR_INSTRUCTION_MATCHER_H + +#include "tsar/ADT/Bimap.h" +#include "tsar/Analysis/Clang/Passes.h" +#include +#include +#include + +#include "tsar/Support/Tags.h" + +namespace clang { + class Stmt; +} + +namespace llvm { + class Instruction; + + /// \brief This function pass makes loop distribution transformation. + class InstructionMatcherPass : public FunctionPass, private bcl::Uncopyable { + public: + typedef tsar::Bimap< + bcl::tagged, + bcl::tagged> InstructionMatcher; + typedef std::set StmtASTSet; + + /// Pass identification, replacement for typeid. + static char ID; + + /// Default constructor. + InstructionMatcherPass() : FunctionPass(ID) { + initializeInstructionMatcherPassPass( + *PassRegistry::getPassRegistry()); + } + + /// Makes loop distribution transformation. + bool runOnFunction(Function& F) override; + + /// Specifies a list of analyzes that are necessary for this pass. + void getAnalysisUsage(AnalysisUsage& AU) const override; + + /// \brief Returns instruction matcher for the last analyzed function. + const InstructionMatcher& getMatcher() const noexcept { return mMatcher; } + + /// \brief Returns unmatched AST statements. + const StmtASTSet& getUnmatchedAST() const noexcept { return mUnmatchedAST; } + + /// Releases allocated memory. + void releaseMemory() override { + mMatcher.clear(); + mUnmatchedAST.clear(); + } + + private: + InstructionMatcher mMatcher; + StmtASTSet mUnmatchedAST; + }; +} + +#endif//TSAR_INSTRUCTION_MATCHER_H \ No newline at end of file diff --git a/include/tsar/Analysis/Clang/Passes.h b/include/tsar/Analysis/Clang/Passes.h index 50d9bac4..274bee07 100644 --- a/include/tsar/Analysis/Clang/Passes.h +++ b/include/tsar/Analysis/Clang/Passes.h @@ -149,5 +149,13 @@ void initializeClangIncludeTreeOnlyViewerPass(PassRegistry &Registry); /// Create a pass to display source file tree (filenames instead of /// full paths). ModulePass *createClangIncludeTreeOnlyViewer(); + +/// Initialize a pass to match AST statements in a high-level +/// code and appropriate instructions in low-level LLVM IR. +void initializeInstructionMatcherPassPass(PassRegistry& Registry); + +/// Creates a pass to match AST statements in a high-level +/// code and appropriate instructions in low-level LLVM IR. +FunctionPass* createInstructionMatcherPass(); } #endif//TSAR_CLANG_ANALYSIS_PASSES_H diff --git a/lib/Analysis/Clang/CMakeLists.txt b/lib/Analysis/Clang/CMakeLists.txt index 9f831e92..7fbe484b 100644 --- a/lib/Analysis/Clang/CMakeLists.txt +++ b/lib/Analysis/Clang/CMakeLists.txt @@ -2,7 +2,7 @@ set(ANALYSIS_SOURCES Passes.cpp DIMemoryMatcher.cpp NoMacroAssert.cpp MemoryMatcher.cpp LoopMatcher.cpp ExpressionMatcher.cpp CanonicalLoop.cpp PerfectLoop.cpp GlobalInfoExtractor.cpp ControlFlowTraits.cpp RegionDirectiveInfo.cpp VariableCollector.cpp ASTDependenceAnalysis.cpp - IncludeTree.cpp Utils.cpp) + IncludeTree.cpp Utils.cpp InstructionMatcher.cpp) if(MSVC_IDE) diff --git a/lib/Analysis/Clang/InstructionMatcher.cpp b/lib/Analysis/Clang/InstructionMatcher.cpp new file mode 100644 index 00000000..f01da7ce --- /dev/null +++ b/lib/Analysis/Clang/InstructionMatcher.cpp @@ -0,0 +1,159 @@ +//=== InstructionMatcher.h - High and Low Level Instruction Matcher -- C++ ===// +// +// Traits Static Analyzer (SAPFOR) +// +// Copyright 2021 DVM System Group +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +// +// Classes and functions from this file match AST statements in a high-level +// code and appropriate instructions in low-level LLVM IR. This file implements +// pass to perform this functionality. +// +//===----------------------------------------------------------------------===// + +#include "tsar/Analysis/Clang/InstructionMatcher.h" +#include "tsar/Frontend/Clang/TransformationContext.h" +#include +#include +#include +#include +#include + +#include "tsar/Analysis/Clang/Matcher.h" + +using namespace llvm; + +#undef DEBUG_TYPE +#define DEBUG_TYPE "instruction-matcher" + +char InstructionMatcherPass::ID = 0; +INITIALIZE_PASS_BEGIN(InstructionMatcherPass, "instruction-matcher", + "High and Low Instruction Matcher", false, true) +INITIALIZE_PASS_DEPENDENCY(TransformationEnginePass) +INITIALIZE_PASS_END(InstructionMatcherPass, "instruction-matcher", + "High and Low Level Instruction Matcher", false, true) + +using namespace clang; + +namespace +{ + class StatementVisitor : + public tsar::MatchASTBase, + public RecursiveASTVisitor { + public: + /// \brief Constructor. + /// + /// \param SrcMgr Clang source manager to deal with locations. + /// \param LM Representation of match. + /// \param Unmatched Storage for unmatched ast entities. + /// \param LocMap Map from entity location to a queue + /// of IR entities. + /// \param MacroMap A map form entity expansion location to a queue + /// of AST entities. All entities explicitly (not implicit loops) defined in + /// macros is going to store in this map. The key in this map is a raw + /// encoding for expansion location. + StatementVisitor(SourceManager& SrcMgr, + Matcher& LM, UnmatchedASTSet& Unmatched, + LocToIRMap& LocMap, LocToASTMap& MacroMap) : + MatchASTBase(SrcMgr, LM, Unmatched, LocMap, MacroMap) { + } + + bool VisitStmt(Stmt *S) { + if (!S) + return true; + S->getBeginLoc().dump(*mSrcMgr); + auto Itr = findItrForLocation(S->getBeginLoc()); + if (Itr == mLocToIR->end()) + return true; + auto* Instruction = Itr->getSecond().front(); + Instruction = Instruction == nullptr + ? findIRForLocation(S->getEndLoc()) + : Instruction; + if (!Instruction) + return true; + dbgs() << "Found store instr for statement.\n"; + S->dump(); + Instruction->dump(); + return true; + } + + bool VisitExpr(Expr* E) { + if (!E) + return true; + auto Itr = findItrForLocation(E->getBeginLoc()); + if (Itr == mLocToIR->end()) + return true; + auto* Instruction = Itr->getSecond().front(); + Instruction = Instruction == nullptr + ? findIRForLocation(E->getEndLoc()) + : Instruction; + if (!Instruction) + return true; + dbgs() << "Found store instr for expression.\n"; + E->dump(); + Instruction->dump(); + return true; + } + }; +} + +bool InstructionMatcherPass::runOnFunction(Function& F) { + releaseMemory(); + auto& TfmInfo = getAnalysis(); + if (!TfmInfo) + return false; + auto *TfmCtx = TfmInfo->getContext(*F.getParent()); + if (!TfmCtx || !TfmCtx->hasInstance()) + return false; + auto *FuncDecl = TfmCtx->getDeclForMangledName(F.getName()); + if (!FuncDecl) + return false; + auto& SrcMgr = TfmCtx->getRewriter().getSourceMgr(); + StatementVisitor::LocToIRMap LocMap; + // TODO: Fill in LocMap. Also rename LocMap. + for (auto &BB : F) { + for (auto &Instr : BB) { + auto* StoreInstr = dyn_cast(&Instr); + if (!StoreInstr) + continue; + StoreInstr->dump(); + auto Loc = StoreInstr->getDebugLoc(); + if (!Loc) + continue; + Loc.dump(); + const auto Inserted = LocMap.insert( + std::make_pair(Loc, TinyPtrVector(&Instr))).second; + assert(Inserted && "Dublicate of store instruction!"); + } + } + /* + for (auto& Pair : LocToLoop) + std::reverse(Pair.second.begin(), Pair.second.end()); + */ + StatementVisitor::LocToASTMap LocToMacro; + StatementVisitor SV(SrcMgr, mMatcher, mUnmatchedAST, LocMap, LocToMacro); + SV.TraverseDecl(FuncDecl); + return false; +} + +void InstructionMatcherPass::getAnalysisUsage(AnalysisUsage& AU) const { + AU.addRequired(); + AU.setPreservesAll(); +} + +FunctionPass* llvm::createInstructionMatcherPass() { + return new InstructionMatcherPass(); +} \ No newline at end of file diff --git a/lib/Analysis/Clang/Passes.cpp b/lib/Analysis/Clang/Passes.cpp index 146f656b..780f4f3f 100644 --- a/lib/Analysis/Clang/Passes.cpp +++ b/lib/Analysis/Clang/Passes.cpp @@ -42,6 +42,7 @@ void llvm::initializeClangAnalysis(PassRegistry &Registry) { initializeClangIncludeTreeOnlyPrinterPass(Registry); initializeClangIncludeTreeViewerPass(Registry); initializeClangIncludeTreeOnlyViewerPass(Registry); + initializeInstructionMatcherPassPass(Registry); // Initialize checkers. initializeClangNoMacroAssertPass(Registry); } diff --git a/lib/Transform/Clang/LoopDistribution.cpp b/lib/Transform/Clang/LoopDistribution.cpp index 543724a3..36299d7a 100644 --- a/lib/Transform/Clang/LoopDistribution.cpp +++ b/lib/Transform/Clang/LoopDistribution.cpp @@ -26,6 +26,7 @@ #include "tsar/ADT/SpanningTreeRelation.h" #include "tsar/Analysis/AnalysisServer.h" #include "tsar/Analysis/Clang/CanonicalLoop.h" +#include "tsar/Analysis/Clang/InstructionMatcher.h" #include "tsar/Analysis/Clang/LoopMatcher.h" #include "tsar/Analysis/DFRegionInfo.h" #include "tsar/Analysis/Memory/DependenceAnalysis.h" @@ -89,6 +90,7 @@ INITIALIZE_PASS_DEPENDENCY(CanonicalLoopPass) INITIALIZE_PASS_DEPENDENCY(LoopMatcherPass) INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass) INITIALIZE_PASS_DEPENDENCY(GlobalOptionsImmutableWrapper) +INITIALIZE_PASS_DEPENDENCY(InstructionMatcherPass) INITIALIZE_PASS_IN_GROUP_END(LoopDistributionPass, "loop-distribution", "Loop Distribution", false, false, TransformationQueryManager::getPassRegistry()) @@ -334,6 +336,7 @@ void LoopDistributionPass::getAnalysisUsage(AnalysisUsage& AU) const { AU.addRequired(); AU.addRequired(); AU.addRequired(); + AU.addRequired(); AU.setPreservesAll(); } From d5a01d83069463d5e3ea7d885341e3e69b8d4a83 Mon Sep 17 00:00:00 2001 From: Viacheslav Kozyrev Date: Wed, 5 May 2021 08:52:42 +0300 Subject: [PATCH 80/93] {Tsar, InstructionMatcher} Remove unnecessary InstructionMatcher ExpressionMatcher was extended instead. --- .../tsar/Analysis/Clang/InstructionMatcher.h | 86 ---------- include/tsar/Analysis/Clang/Passes.h | 8 - lib/Analysis/Clang/CMakeLists.txt | 2 +- lib/Analysis/Clang/InstructionMatcher.cpp | 159 ------------------ lib/Analysis/Clang/Passes.cpp | 1 - lib/Transform/Clang/LoopDistribution.cpp | 3 - 6 files changed, 1 insertion(+), 258 deletions(-) delete mode 100644 include/tsar/Analysis/Clang/InstructionMatcher.h delete mode 100644 lib/Analysis/Clang/InstructionMatcher.cpp diff --git a/include/tsar/Analysis/Clang/InstructionMatcher.h b/include/tsar/Analysis/Clang/InstructionMatcher.h deleted file mode 100644 index 57404beb..00000000 --- a/include/tsar/Analysis/Clang/InstructionMatcher.h +++ /dev/null @@ -1,86 +0,0 @@ -//=== InstructionMatcher.h - High and Low Level Instruction Matcher -- C++ ===// -// -// Traits Static Analyzer (SAPFOR) -// -// Copyright 2021 DVM System Group -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -// -// Classes and functions from this file match AST statements in a high-level -// code and appropriate instructions in low-level LLVM IR. This file implements -// pass to perform this functionality. -// -//===----------------------------------------------------------------------===// - -#ifndef TSAR_INSTRUCTION_MATCHER_H -#define TSAR_INSTRUCTION_MATCHER_H - -#include "tsar/ADT/Bimap.h" -#include "tsar/Analysis/Clang/Passes.h" -#include -#include -#include - -#include "tsar/Support/Tags.h" - -namespace clang { - class Stmt; -} - -namespace llvm { - class Instruction; - - /// \brief This function pass makes loop distribution transformation. - class InstructionMatcherPass : public FunctionPass, private bcl::Uncopyable { - public: - typedef tsar::Bimap< - bcl::tagged, - bcl::tagged> InstructionMatcher; - typedef std::set StmtASTSet; - - /// Pass identification, replacement for typeid. - static char ID; - - /// Default constructor. - InstructionMatcherPass() : FunctionPass(ID) { - initializeInstructionMatcherPassPass( - *PassRegistry::getPassRegistry()); - } - - /// Makes loop distribution transformation. - bool runOnFunction(Function& F) override; - - /// Specifies a list of analyzes that are necessary for this pass. - void getAnalysisUsage(AnalysisUsage& AU) const override; - - /// \brief Returns instruction matcher for the last analyzed function. - const InstructionMatcher& getMatcher() const noexcept { return mMatcher; } - - /// \brief Returns unmatched AST statements. - const StmtASTSet& getUnmatchedAST() const noexcept { return mUnmatchedAST; } - - /// Releases allocated memory. - void releaseMemory() override { - mMatcher.clear(); - mUnmatchedAST.clear(); - } - - private: - InstructionMatcher mMatcher; - StmtASTSet mUnmatchedAST; - }; -} - -#endif//TSAR_INSTRUCTION_MATCHER_H \ No newline at end of file diff --git a/include/tsar/Analysis/Clang/Passes.h b/include/tsar/Analysis/Clang/Passes.h index 274bee07..50d9bac4 100644 --- a/include/tsar/Analysis/Clang/Passes.h +++ b/include/tsar/Analysis/Clang/Passes.h @@ -149,13 +149,5 @@ void initializeClangIncludeTreeOnlyViewerPass(PassRegistry &Registry); /// Create a pass to display source file tree (filenames instead of /// full paths). ModulePass *createClangIncludeTreeOnlyViewer(); - -/// Initialize a pass to match AST statements in a high-level -/// code and appropriate instructions in low-level LLVM IR. -void initializeInstructionMatcherPassPass(PassRegistry& Registry); - -/// Creates a pass to match AST statements in a high-level -/// code and appropriate instructions in low-level LLVM IR. -FunctionPass* createInstructionMatcherPass(); } #endif//TSAR_CLANG_ANALYSIS_PASSES_H diff --git a/lib/Analysis/Clang/CMakeLists.txt b/lib/Analysis/Clang/CMakeLists.txt index 7fbe484b..9f831e92 100644 --- a/lib/Analysis/Clang/CMakeLists.txt +++ b/lib/Analysis/Clang/CMakeLists.txt @@ -2,7 +2,7 @@ set(ANALYSIS_SOURCES Passes.cpp DIMemoryMatcher.cpp NoMacroAssert.cpp MemoryMatcher.cpp LoopMatcher.cpp ExpressionMatcher.cpp CanonicalLoop.cpp PerfectLoop.cpp GlobalInfoExtractor.cpp ControlFlowTraits.cpp RegionDirectiveInfo.cpp VariableCollector.cpp ASTDependenceAnalysis.cpp - IncludeTree.cpp Utils.cpp InstructionMatcher.cpp) + IncludeTree.cpp Utils.cpp) if(MSVC_IDE) diff --git a/lib/Analysis/Clang/InstructionMatcher.cpp b/lib/Analysis/Clang/InstructionMatcher.cpp deleted file mode 100644 index f01da7ce..00000000 --- a/lib/Analysis/Clang/InstructionMatcher.cpp +++ /dev/null @@ -1,159 +0,0 @@ -//=== InstructionMatcher.h - High and Low Level Instruction Matcher -- C++ ===// -// -// Traits Static Analyzer (SAPFOR) -// -// Copyright 2021 DVM System Group -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -// -// Classes and functions from this file match AST statements in a high-level -// code and appropriate instructions in low-level LLVM IR. This file implements -// pass to perform this functionality. -// -//===----------------------------------------------------------------------===// - -#include "tsar/Analysis/Clang/InstructionMatcher.h" -#include "tsar/Frontend/Clang/TransformationContext.h" -#include -#include -#include -#include -#include - -#include "tsar/Analysis/Clang/Matcher.h" - -using namespace llvm; - -#undef DEBUG_TYPE -#define DEBUG_TYPE "instruction-matcher" - -char InstructionMatcherPass::ID = 0; -INITIALIZE_PASS_BEGIN(InstructionMatcherPass, "instruction-matcher", - "High and Low Instruction Matcher", false, true) -INITIALIZE_PASS_DEPENDENCY(TransformationEnginePass) -INITIALIZE_PASS_END(InstructionMatcherPass, "instruction-matcher", - "High and Low Level Instruction Matcher", false, true) - -using namespace clang; - -namespace -{ - class StatementVisitor : - public tsar::MatchASTBase, - public RecursiveASTVisitor { - public: - /// \brief Constructor. - /// - /// \param SrcMgr Clang source manager to deal with locations. - /// \param LM Representation of match. - /// \param Unmatched Storage for unmatched ast entities. - /// \param LocMap Map from entity location to a queue - /// of IR entities. - /// \param MacroMap A map form entity expansion location to a queue - /// of AST entities. All entities explicitly (not implicit loops) defined in - /// macros is going to store in this map. The key in this map is a raw - /// encoding for expansion location. - StatementVisitor(SourceManager& SrcMgr, - Matcher& LM, UnmatchedASTSet& Unmatched, - LocToIRMap& LocMap, LocToASTMap& MacroMap) : - MatchASTBase(SrcMgr, LM, Unmatched, LocMap, MacroMap) { - } - - bool VisitStmt(Stmt *S) { - if (!S) - return true; - S->getBeginLoc().dump(*mSrcMgr); - auto Itr = findItrForLocation(S->getBeginLoc()); - if (Itr == mLocToIR->end()) - return true; - auto* Instruction = Itr->getSecond().front(); - Instruction = Instruction == nullptr - ? findIRForLocation(S->getEndLoc()) - : Instruction; - if (!Instruction) - return true; - dbgs() << "Found store instr for statement.\n"; - S->dump(); - Instruction->dump(); - return true; - } - - bool VisitExpr(Expr* E) { - if (!E) - return true; - auto Itr = findItrForLocation(E->getBeginLoc()); - if (Itr == mLocToIR->end()) - return true; - auto* Instruction = Itr->getSecond().front(); - Instruction = Instruction == nullptr - ? findIRForLocation(E->getEndLoc()) - : Instruction; - if (!Instruction) - return true; - dbgs() << "Found store instr for expression.\n"; - E->dump(); - Instruction->dump(); - return true; - } - }; -} - -bool InstructionMatcherPass::runOnFunction(Function& F) { - releaseMemory(); - auto& TfmInfo = getAnalysis(); - if (!TfmInfo) - return false; - auto *TfmCtx = TfmInfo->getContext(*F.getParent()); - if (!TfmCtx || !TfmCtx->hasInstance()) - return false; - auto *FuncDecl = TfmCtx->getDeclForMangledName(F.getName()); - if (!FuncDecl) - return false; - auto& SrcMgr = TfmCtx->getRewriter().getSourceMgr(); - StatementVisitor::LocToIRMap LocMap; - // TODO: Fill in LocMap. Also rename LocMap. - for (auto &BB : F) { - for (auto &Instr : BB) { - auto* StoreInstr = dyn_cast(&Instr); - if (!StoreInstr) - continue; - StoreInstr->dump(); - auto Loc = StoreInstr->getDebugLoc(); - if (!Loc) - continue; - Loc.dump(); - const auto Inserted = LocMap.insert( - std::make_pair(Loc, TinyPtrVector(&Instr))).second; - assert(Inserted && "Dublicate of store instruction!"); - } - } - /* - for (auto& Pair : LocToLoop) - std::reverse(Pair.second.begin(), Pair.second.end()); - */ - StatementVisitor::LocToASTMap LocToMacro; - StatementVisitor SV(SrcMgr, mMatcher, mUnmatchedAST, LocMap, LocToMacro); - SV.TraverseDecl(FuncDecl); - return false; -} - -void InstructionMatcherPass::getAnalysisUsage(AnalysisUsage& AU) const { - AU.addRequired(); - AU.setPreservesAll(); -} - -FunctionPass* llvm::createInstructionMatcherPass() { - return new InstructionMatcherPass(); -} \ No newline at end of file diff --git a/lib/Analysis/Clang/Passes.cpp b/lib/Analysis/Clang/Passes.cpp index 780f4f3f..146f656b 100644 --- a/lib/Analysis/Clang/Passes.cpp +++ b/lib/Analysis/Clang/Passes.cpp @@ -42,7 +42,6 @@ void llvm::initializeClangAnalysis(PassRegistry &Registry) { initializeClangIncludeTreeOnlyPrinterPass(Registry); initializeClangIncludeTreeViewerPass(Registry); initializeClangIncludeTreeOnlyViewerPass(Registry); - initializeInstructionMatcherPassPass(Registry); // Initialize checkers. initializeClangNoMacroAssertPass(Registry); } diff --git a/lib/Transform/Clang/LoopDistribution.cpp b/lib/Transform/Clang/LoopDistribution.cpp index 36299d7a..543724a3 100644 --- a/lib/Transform/Clang/LoopDistribution.cpp +++ b/lib/Transform/Clang/LoopDistribution.cpp @@ -26,7 +26,6 @@ #include "tsar/ADT/SpanningTreeRelation.h" #include "tsar/Analysis/AnalysisServer.h" #include "tsar/Analysis/Clang/CanonicalLoop.h" -#include "tsar/Analysis/Clang/InstructionMatcher.h" #include "tsar/Analysis/Clang/LoopMatcher.h" #include "tsar/Analysis/DFRegionInfo.h" #include "tsar/Analysis/Memory/DependenceAnalysis.h" @@ -90,7 +89,6 @@ INITIALIZE_PASS_DEPENDENCY(CanonicalLoopPass) INITIALIZE_PASS_DEPENDENCY(LoopMatcherPass) INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass) INITIALIZE_PASS_DEPENDENCY(GlobalOptionsImmutableWrapper) -INITIALIZE_PASS_DEPENDENCY(InstructionMatcherPass) INITIALIZE_PASS_IN_GROUP_END(LoopDistributionPass, "loop-distribution", "Loop Distribution", false, false, TransformationQueryManager::getPassRegistry()) @@ -336,7 +334,6 @@ void LoopDistributionPass::getAnalysisUsage(AnalysisUsage& AU) const { AU.addRequired(); AU.addRequired(); AU.addRequired(); - AU.addRequired(); AU.setPreservesAll(); } From 6ccf56a2b7d1384e8ab0a60a935fe9a6cb8e540d Mon Sep 17 00:00:00 2001 From: Viacheslav Kozyrev Date: Sat, 8 May 2021 18:06:55 +0300 Subject: [PATCH 81/93] [TSAR, InstructionMatcher] Rename variables, add const modifier. --- lib/Transform/Clang/LoopDistribution.cpp | 316 ++++++++++++----------- 1 file changed, 165 insertions(+), 151 deletions(-) diff --git a/lib/Transform/Clang/LoopDistribution.cpp b/lib/Transform/Clang/LoopDistribution.cpp index 543724a3..67a92ac4 100644 --- a/lib/Transform/Clang/LoopDistribution.cpp +++ b/lib/Transform/Clang/LoopDistribution.cpp @@ -53,7 +53,7 @@ using namespace tsar; namespace { class LoopDistributionPassInfo final : public PassGroupInfo { - void addBeforePass(legacy::PassManager& Passes) const override { + void addBeforePass(legacy::PassManager &Passes) const override { addImmutableAliasAnalysis(Passes); addInitialTransformations(Passes); Passes.add(createAnalysisSocketImmutableStorage()); @@ -66,7 +66,7 @@ namespace { Passes.add(createMemoryMatcherPass()); Passes.add(createAnalysisWaitServerPass()); } - void addAfterPass(legacy::PassManager& Passes) const override { + void addAfterPass(legacy::PassManager &Passes) const override { Passes.add(createAnalysisReleaseServerPass()); Passes.add(createAnalysisCloseConnectionPass()); } @@ -96,97 +96,122 @@ INITIALIZE_PASS_IN_GROUP_END(LoopDistributionPass, "loop-distribution", namespace { class ASTVisitor : public RecursiveASTVisitor { public: - ASTVisitor(FunctionPass& P, Function& F) { - mDFRI = &P.getAnalysis().getRegionInfo(); - mTLI = &P.getAnalysis().getTLI(F); - mAliasTree = &P.getAnalysis().getAliasTree(); - mDomTree = &P.getAnalysis().getDomTree(); - auto& DIEMPass = P.getAnalysis(); + ASTVisitor(FunctionPass& Pass, Function& Function) { + mDFRegion = &Pass.getAnalysis().getRegionInfo(); + mTargetLibrary = &Pass.getAnalysis() + .getTLI(Function); + mAliasTree = &Pass.getAnalysis() + .getAliasTree(); + mDominatorTree = &Pass.getAnalysis() + .getDomTree(); + auto& DIEMPass = Pass.getAnalysis(); assert(DIEMPass.isConstructed() && "Alias tree must be constructed!"); - mDIMInfo = new DIMemoryClientServerInfo(DIEMPass.getAliasTree(), P, F); - mSTR = new SpanningTreeRelation(mDIMInfo->DIAT); - mCLI = &P.getAnalysis().getCanonicalLoopInfo(); - mLM = &P.getAnalysis().getMatcher(); - mGO = &P.getAnalysis().getOptions(); - auto& SocketInfo = P.getAnalysis().get(); + mServerDIMemory = new DIMemoryClientServerInfo( + DIEMPass.getAliasTree(), Pass, Function); + mSpanningTreeRelation = new SpanningTreeRelation( + mServerDIMemory->DIAT); + mCanonicalLoop = &Pass.getAnalysis() + .getCanonicalLoopInfo(); + mLoopMatcher = &Pass.getAnalysis() + .getMatcher(); + mGlobalOptions = &Pass.getAnalysis() + .getOptions(); + auto& SocketInfo = Pass.getAnalysis().get(); auto& Socket = SocketInfo.getActive()->second; - auto RF = Socket.getAnalysis(F); + auto RF = Socket.getAnalysis(Function); assert(RF && "Dependence analysis must be available!"); - mDIAT = &RF->value()->getAliasTree(); - mDIDep = &RF->value()->getDependencies(); - mDepInfo = &RF->value()->getDI(); + mDIAliasTree = &RF->value()->getAliasTree(); + mDIDependency = &RF->value()->getDependencies(); + mDependence = &RF->value()->getDI(); auto RM = Socket.getAnalysis(); assert(RM && "Client to server IR-matcher must be available!"); - auto Matcher = RM->value(); - mGetLoopID = [Matcher](ObjectID ID) { + auto *Matcher = RM->value(); + mGetLoopIdFunction = [Matcher](ObjectID ID) { auto ServerID = (*Matcher)->getMappedMD(ID); - return ServerID ? cast(*ServerID) : nullptr; + return ServerID + ? cast(*ServerID) + : nullptr; }; - mGetInstruction = [Matcher](Instruction* I) { + mGetInstructionFunction = [Matcher](Instruction *I) { return cast((**Matcher)[I]); }; } - bool TraverseStmt(Stmt *S) { - if (!S) - return RecursiveASTVisitor::TraverseStmt(S); - ForStmt* ForS = dyn_cast(S); - if (!ForS) - return RecursiveASTVisitor::TraverseStmt(S); + [[maybe_unused]] + bool TraverseStmt(Stmt *Statement) { + if (!Statement) { + return RecursiveASTVisitor::TraverseStmt(Statement); + } + auto *ForStatement = dyn_cast(Statement); + if (!ForStatement) { + return RecursiveASTVisitor::TraverseStmt(Statement); + } + dbgs() << "Loop\n"; - auto Match = mLM->find(ForS); - if (Match == mLM->end()) + const auto LoopMatch = mLoopMatcher->find(ForStatement); + if (LoopMatch == mLoopMatcher->end()) { return false; - Loop* L = Match->get(); - auto LoopDepth = L->getLoopDepth(); - auto DFL = dyn_cast(mDFRI->getRegionFor(L)); - auto CanonItr = mCLI->find_as(DFL); - if (CanonItr == mCLI->end() || !(**CanonItr).isCanonical() || - !(**CanonItr).getLoop()) + } + + auto *const Loop = LoopMatch->get(); + const auto LoopDepth = Loop->getLoopDepth(); + auto *DFLoopRegion = dyn_cast(mDFRegion->getRegionFor(Loop)); + const auto CanonicalLoopItr = mCanonicalLoop->find_as(DFLoopRegion); + if (CanonicalLoopItr == mCanonicalLoop->end() + || !(**CanonicalLoopItr).isCanonical() + || !(**CanonicalLoopItr).getLoop()) { return false; + } + dbgs() << "Canonical loop\n"; // TODO: See ParallelLoop.cpp, probably I should use for_each_loop instead. - MDNode* LoopID = L->getLoopID(); - if (!LoopID || !(LoopID = mGetLoopID(LoopID))) { + auto *LoopID = Loop->getLoopID(); + if (!LoopID || !(LoopID = mGetLoopIdFunction(LoopID))) { dbgs() << "Ignore loop without ID"; return false; } - auto DepItr = mDIDep->find(LoopID); - if (DepItr == mDIDep->end()) + + const auto DependencyItr = mDIDependency->find(LoopID); + if (DependencyItr == mDIDependency->end()) { return false; - auto& DIDepSet = DepItr->get(); - DenseSet Coverage; - accessCoverage(DIDepSet, *mDIAT, Coverage, - mGO->IgnoreRedundantMemory); - for (auto& TS : DIDepSet) { - if (!Coverage.count(TS.getNode())) + } + + auto& DIDependenceSet = DependencyItr->get(); + DenseSet Coverage; + accessCoverage(DIDependenceSet, *mDIAliasTree, + Coverage, mGlobalOptions->IgnoreRedundantMemory); + for (auto& AliasTrait : DIDependenceSet) { + if (!Coverage.count(AliasTrait.getNode())) { continue; - auto& DIMTraitItr = *TS.begin(); - auto* DIMem = DIMTraitItr->getMemory(); - if (DIMTraitItr->is()) { - printDILocationSource(dwarf::DW_LANG_C, *DIMem, dbgs()); + } + + auto& DIMemoryTraitItr = *AliasTrait.begin(); + const auto *DIMemory = DIMemoryTraitItr->getMemory(); + if (DIMemoryTraitItr->is()) { + printDILocationSource(dwarf::DW_LANG_C, *DIMemory, dbgs()); dbgs() << "Flow dependency\n"; + continue; } - if (DIMTraitItr->is()) { - printDILocationSource(dwarf::DW_LANG_C, *DIMem, dbgs()); + if (DIMemoryTraitItr->is()) { + printDILocationSource(dwarf::DW_LANG_C, *DIMemory, dbgs()); dbgs() << "Antiflow dependency\n"; - } - if (!DIMTraitItr->is_any()) continue; - auto* DINode = DIMem->getAliasNode(); - // TODO: For now I consider the only block in the loop - for (auto BB = L->block_begin(); BB != L->block_end(); ++BB) { + } + + const auto *DINode = DIMemory->getAliasNode(); + for (const auto *BasicBlock = Loop->block_begin(); + BasicBlock != Loop->block_end(); ++BasicBlock) { // Get all reads and writes of memory leading to dependencies - std::vector Reads, Writes; - for_each_memory(**BB, *mTLI, [this, DINode, &Reads, &Writes](Instruction& I, - MemoryLocation&& Loc, unsigned Idx, AccessInfo R, AccessInfo W) { - auto EM = mAliasTree->find(Loc); + std::vector Reads, Writes; + for_each_memory(**BasicBlock, *mTargetLibrary, [this, DINode, &Reads, &Writes](Instruction &I, + MemoryLocation &&Loc, unsigned Idx, AccessInfo R, AccessInfo W) { + auto *EM = mAliasTree->find(Loc); assert(EM && "Estimate memory location must not be null!"); - auto& DL = I.getModule()->getDataLayout(); - auto* DIM = mDIMInfo->findFromClient( - *EM->getTopLevelParent(), DL, *mDomTree).get(); - if (!DIM || mSTR->isUnreachable(DINode, DIM->getAliasNode())) { + const auto &DL = I.getModule()->getDataLayout(); + auto *DIM = mServerDIMemory->findFromClient( + *EM->getTopLevelParent(), DL, *mDominatorTree).get(); + if (!DIM || mSpanningTreeRelation->isUnreachable(DINode, DIM->getAliasNode())) { return; } dbgs() << "Instruction\n"; @@ -196,132 +221,121 @@ class ASTVisitor : public RecursiveASTVisitor { if (R != AccessInfo::No) { Reads.push_back(&I); } - }, [](Instruction& I, AccessInfo, AccessInfo W) { + }, [](Instruction &I, AccessInfo, AccessInfo W) { // TODO: Fill in }); // Try to get write instructions after which should be split - std::set Splits(Writes.begin(), Writes.end()); - for (auto Write = Writes.begin(); Write != Writes.end(); ++Write) { - for (auto Read = Reads.begin(); Read != Reads.end(); ++Read) { + std::set Splits(Writes.begin(), Writes.end()); + for (auto &Write : Writes) { + for (auto &Read : Reads) { // TODO: Probably true instead of false - auto Dep = mDepInfo->depends(mGetInstruction(*Write), - mGetInstruction(*Read), false); - if (!Dep) continue; - auto Dir = Dep.get()->getDirection(LoopDepth); - if (Dir == tsar_impl::Dependence::DVEntry::EQ) { + const auto Dependence = mDependence->depends( + mGetInstructionFunction(Write), + mGetInstructionFunction(Read), false); + if (!Dependence) { + continue; + } + + const auto Direction = Dependence->getDirection(LoopDepth); + if (Direction == tsar_impl::Dependence::DVEntry::EQ) { // TODO: Probably don't need to do so dbgs() << "Ignore loop independent dependence\n"; continue; } + // Get direction of the dependency - bool Flow = false, Anti = false; - if (Dir == tsar_impl::Dependence::DVEntry::ALL) { + auto Flow = false, Anti = false; + if (Direction == tsar_impl::Dependence::DVEntry::ALL) { Flow = true; Anti = true; - } - else if (Dep.get()->isFlow()) - if (Dir == tsar_impl::Dependence::DVEntry::LT || - Dir == tsar_impl::Dependence::DVEntry::LE) { + } else if (Dependence->isFlow()) { + if (Direction == tsar_impl::Dependence::DVEntry::LT || + Direction == tsar_impl::Dependence::DVEntry::LE) { Flow = true; } else { Anti = true; } - else if (Dep.get()->isAnti()) - if (Dir == tsar_impl::Dependence::DVEntry::LT || - Dir == tsar_impl::Dependence::DVEntry::LE) { + } else if (Dependence->isAnti()) { + if (Direction == tsar_impl::Dependence::DVEntry::LT || + Direction == tsar_impl::Dependence::DVEntry::LE) { Anti = true; - } - else { + } else { Flow = true; } - bool WriteBeforeRead = (**Write).comesBefore(*Read); + } + + const auto WriteBeforeRead = Write->comesBefore(Read); // If this is bad instructions dependency, can't split between them - if (!(WriteBeforeRead && Anti || !WriteBeforeRead && Flow)) continue; - // TODO: More effective version of this algo - for (auto Split = Writes.begin(); *Split != *Write; ++Split) { - if (!Splits.count(*Split)) continue; - if ((**Split).comesBefore(*Write) && (**Read).comesBefore(*Split) - || (**Split).comesBefore(*Read) && (**Write).comesBefore(*Split)) { - Splits.erase(*Split); + if (!(WriteBeforeRead && Anti || !WriteBeforeRead && Flow)) { + continue; + } + + for (auto &Split : Writes) { + if (!Splits.count(Split)) { + continue; + } + if (Split->comesBefore(Write) && Read->comesBefore(Split) || + Split->comesBefore(Read) && Write->comesBefore(Split)) { + Splits.erase(Split); } } } } - for (auto Split = Splits.begin(); Split != Splits.end(); ++Split) { - (**Split).dump(); + dbgs() << "Splits:\n"; + for (auto *Split : Splits) { + Split->dump(); } } //auto *Alias = DIMem.getAliasNode(); } - // - bool PrevIsInsideLoop = mIsInsideLoop; + const auto PrevIsInsideLoop = mIsInsideLoop; mIsInsideLoop = true; - bool result = RecursiveASTVisitor::TraverseStmt(ForS->getBody()); + const auto Result = RecursiveASTVisitor::TraverseStmt(ForStatement->getBody()); mIsInsideLoop = PrevIsInsideLoop; - return result; + return Result; } private: - DFRegionInfo* mDFRI; - TargetLibraryInfo* mTLI; - AliasTree* mAliasTree; - DominatorTree* mDomTree; - DIMemoryClientServerInfo* mDIMInfo; - SpanningTreeRelation* mSTR; - const CanonicalLoopSet* mCLI; - const LoopMatcherPass::LoopMatcher* mLM; - const GlobalOptions* mGO; - DIAliasTree* mDIAT; - DIDependencInfo* mDIDep; - DependenceInfo* mDepInfo; - std::function mGetLoopID; - std::function mGetInstruction; + DFRegionInfo *mDFRegion; + TargetLibraryInfo *mTargetLibrary; + AliasTree *mAliasTree; + DominatorTree *mDominatorTree; + DIMemoryClientServerInfo *mServerDIMemory; + SpanningTreeRelation *mSpanningTreeRelation; + const CanonicalLoopSet *mCanonicalLoop; + const LoopMatcherPass::LoopMatcher *mLoopMatcher; + const GlobalOptions *mGlobalOptions; + DIAliasTree *mDIAliasTree; + DIDependencInfo *mDIDependency; + DependenceInfo *mDependence; + std::function mGetLoopIdFunction; + std::function mGetInstructionFunction; bool mIsInsideLoop = false; }; } -bool LoopDistributionPass::runOnFunction(Function& F) { - auto M = F.getParent(); - auto& TfmInfo = getAnalysis(); - if (!TfmInfo) +bool LoopDistributionPass::runOnFunction(Function& Function) { + auto *Module = Function.getParent(); + auto& TransformationInfo = + getAnalysis(); + if (!TransformationInfo) { return false; - auto TfmCtx = TfmInfo->getContext(*M); - if (!TfmCtx || !TfmCtx->hasInstance()) + } + auto *TransformationContext = TransformationInfo->getContext(*Module); + if (!TransformationContext || !TransformationContext->hasInstance()) { return false; - auto FuncDecl = TfmCtx->getDeclForMangledName(F.getName()); - if (!FuncDecl) + } + auto *FunctionDecl = + TransformationContext->getDeclForMangledName(Function.getName()); + if (!FunctionDecl) { return false; - /* - auto& RgnInfo = getAnalysis().getRegionInfo(); - auto& TLI = getAnalysis().getTLI(F); - auto& AliasTree = getAnalysis().getAliasTree(); - auto& DIEMPass = getAnalysis(); - assert(DIEMPass.isConstructed() && "Alias tree must be constructed!"); - auto& DIMInfo = DIMemoryClientServerInfo(DIEMPass.getAliasTree(), *this, F); - auto& DomTree = getAnalysis().getDomTree(); - auto& SocketInfo = getAnalysis().get(); - auto& Socket = SocketInfo.getActive()->second; - auto RF = - Socket.getAnalysis(F); - assert(RF && "Dependence analysis must be available!"); - auto& DIAT = RF->value()->getAliasTree(); - auto& DIDepInfo = RF->value()->getDependencies(); - auto& DepInfo = RF->value()->getDI(); - - auto RM = Socket.getAnalysis(); - assert(RM && "Client to server IR-matcher must be available!"); - auto Matcher = RM->value(); - auto& GlobalOpts = getAnalysis() - .getOptions(); - auto& CLI = getAnalysis().getCanonicalLoopInfo(); - auto& LoopInfo = getAnalysis().getMatcher(); - */ - ASTVisitor LoopVisitor(*this, F); - LoopVisitor.TraverseDecl(FuncDecl); + } + ASTVisitor LoopVisitor(*this, Function); + LoopVisitor.TraverseDecl(FunctionDecl); return false; } -void LoopDistributionPass::getAnalysisUsage(AnalysisUsage& AU) const { +void LoopDistributionPass::getAnalysisUsage(AnalysisUsage &AU) const { AU.addRequired(); AU.addRequired(); AU.addRequired(); @@ -337,6 +351,6 @@ void LoopDistributionPass::getAnalysisUsage(AnalysisUsage& AU) const { AU.setPreservesAll(); } -FunctionPass* llvm::createLoopDistributionPass() { +FunctionPass * llvm::createLoopDistributionPass() { return new LoopDistributionPass(); } \ No newline at end of file From 063d3c1cc1e949e7b31e36fa0c87e8b3dd9a0491 Mon Sep 17 00:00:00 2001 From: Viacheslav Kozyrev Date: Sun, 9 May 2021 21:00:46 +0300 Subject: [PATCH 82/93] [TSAR, Distribution] Add debug information, refactor. --- lib/Transform/Clang/LoopDistribution.cpp | 192 +++++++++++++---------- 1 file changed, 106 insertions(+), 86 deletions(-) diff --git a/lib/Transform/Clang/LoopDistribution.cpp b/lib/Transform/Clang/LoopDistribution.cpp index 67a92ac4..87ef8507 100644 --- a/lib/Transform/Clang/LoopDistribution.cpp +++ b/lib/Transform/Clang/LoopDistribution.cpp @@ -94,9 +94,13 @@ INITIALIZE_PASS_IN_GROUP_END(LoopDistributionPass, "loop-distribution", TransformationQueryManager::getPassRegistry()) namespace { + + typedef std::vector DependencyInstructionVector; + class ASTVisitor : public RecursiveASTVisitor { public: - ASTVisitor(FunctionPass& Pass, Function& Function) { + ASTVisitor(FunctionPass& Pass, Function& Function, + ClangTransformationContext &TransformationContext) { mDFRegion = &Pass.getAnalysis().getRegionInfo(); mTargetLibrary = &Pass.getAnalysis() .getTLI(Function); @@ -116,6 +120,7 @@ class ASTVisitor : public RecursiveASTVisitor { .getMatcher(); mGlobalOptions = &Pass.getAnalysis() .getOptions(); + mSourceManager = &TransformationContext.getRewriter().getSourceMgr(); auto& SocketInfo = Pass.getAnalysis().get(); auto& Socket = SocketInfo.getActive()->second; auto RF = Socket.getAnalysis { return RecursiveASTVisitor::TraverseStmt(Statement); } - dbgs() << "Loop\n"; const auto LoopMatch = mLoopMatcher->find(ForStatement); if (LoopMatch == mLoopMatcher->end()) { return false; } auto *const Loop = LoopMatch->get(); - const auto LoopDepth = Loop->getLoopDepth(); auto *DFLoopRegion = dyn_cast(mDFRegion->getRegionFor(Loop)); const auto CanonicalLoopItr = mCanonicalLoop->find_as(DFLoopRegion); if (CanonicalLoopItr == mCanonicalLoop->end() @@ -164,11 +167,15 @@ class ASTVisitor : public RecursiveASTVisitor { return false; } - dbgs() << "Canonical loop\n"; + LLVM_DEBUG( + dbgs() << "Found canonical loop at "; + ForStatement->getBeginLoc().print(dbgs(), *mSourceManager); + dbgs() << "\n"; + ); // TODO: See ParallelLoop.cpp, probably I should use for_each_loop instead. auto *LoopID = Loop->getLoopID(); if (!LoopID || !(LoopID = mGetLoopIdFunction(LoopID))) { - dbgs() << "Ignore loop without ID"; + LLVM_DEBUG(dbgs() << "Ignore loop without ID"); return false; } @@ -177,35 +184,38 @@ class ASTVisitor : public RecursiveASTVisitor { return false; } - auto& DIDependenceSet = DependencyItr->get(); + auto &DIDependenceSet = DependencyItr->get(); DenseSet Coverage; accessCoverage(DIDependenceSet, *mDIAliasTree, Coverage, mGlobalOptions->IgnoreRedundantMemory); - for (auto& AliasTrait : DIDependenceSet) { + DependencyInstructionVector DependencyReads, DependencyWrites; + for (auto &AliasTrait : DIDependenceSet) { if (!Coverage.count(AliasTrait.getNode())) { continue; } - auto& DIMemoryTraitItr = *AliasTrait.begin(); - const auto *DIMemory = DIMemoryTraitItr->getMemory(); - if (DIMemoryTraitItr->is()) { - printDILocationSource(dwarf::DW_LANG_C, *DIMemory, dbgs()); - dbgs() << "Flow dependency\n"; - continue; - } - if (DIMemoryTraitItr->is()) { - printDILocationSource(dwarf::DW_LANG_C, *DIMemory, dbgs()); - dbgs() << "Antiflow dependency\n"; + auto &DIMemoryTraitItr = *AliasTrait.begin(); + if (!DIMemoryTraitItr->is_any()) { continue; } + const auto *DIMemory = DIMemoryTraitItr->getMemory(); + LLVM_DEBUG( + if (DIMemoryTraitItr->is()) { + printDILocationSource(dwarf::DW_LANG_C, *DIMemory, dbgs()); + dbgs() << "Flow dependency\n"; + } + if (DIMemoryTraitItr->is()) { + printDILocationSource(dwarf::DW_LANG_C, *DIMemory, dbgs()); + dbgs() << "Antiflow dependency\n"; + } + ); + const auto *DINode = DIMemory->getAliasNode(); - for (const auto *BasicBlock = Loop->block_begin(); - BasicBlock != Loop->block_end(); ++BasicBlock) { + for (const auto &BasicBlock : Loop->blocks()) { // Get all reads and writes of memory leading to dependencies - std::vector Reads, Writes; - for_each_memory(**BasicBlock, *mTargetLibrary, [this, DINode, &Reads, &Writes](Instruction &I, - MemoryLocation &&Loc, unsigned Idx, AccessInfo R, AccessInfo W) { + for_each_memory(*BasicBlock, *mTargetLibrary, [this, DINode, &DependencyReads, &DependencyWrites](Instruction &I, + MemoryLocation &&Loc, unsigned, AccessInfo R, AccessInfo W) { auto *EM = mAliasTree->find(Loc); assert(EM && "Estimate memory location must not be null!"); const auto &DL = I.getModule()->getDataLayout(); @@ -214,85 +224,94 @@ class ASTVisitor : public RecursiveASTVisitor { if (!DIM || mSpanningTreeRelation->isUnreachable(DINode, DIM->getAliasNode())) { return; } - dbgs() << "Instruction\n"; if (W != AccessInfo::No) { - Writes.push_back(&I); + DependencyWrites.push_back(&I); + LLVM_DEBUG(dbgs() << "Write "; I.dump()); } if (R != AccessInfo::No) { - Reads.push_back(&I); + DependencyReads.push_back(&I); + LLVM_DEBUG(dbgs() << "Read "; I.dump()); } }, [](Instruction &I, AccessInfo, AccessInfo W) { // TODO: Fill in }); - // Try to get write instructions after which should be split - std::set Splits(Writes.begin(), Writes.end()); - for (auto &Write : Writes) { - for (auto &Read : Reads) { - // TODO: Probably true instead of false - const auto Dependence = mDependence->depends( - mGetInstructionFunction(Write), - mGetInstructionFunction(Read), false); - if (!Dependence) { - continue; - } + } + } - const auto Direction = Dependence->getDirection(LoopDepth); - if (Direction == tsar_impl::Dependence::DVEntry::EQ) { - // TODO: Probably don't need to do so - dbgs() << "Ignore loop independent dependence\n"; - continue; - } + // Try to get write instructions after which should be split + getLoopSplits(Loop, DependencyReads, DependencyWrites); + const auto PrevIsInsideLoop = mIsInsideLoop; + mIsInsideLoop = true; + const auto Result = RecursiveASTVisitor::TraverseStmt(ForStatement->getBody()); + mIsInsideLoop = PrevIsInsideLoop; + return Result; + } - // Get direction of the dependency - auto Flow = false, Anti = false; - if (Direction == tsar_impl::Dependence::DVEntry::ALL) { - Flow = true; - Anti = true; - } else if (Dependence->isFlow()) { - if (Direction == tsar_impl::Dependence::DVEntry::LT || - Direction == tsar_impl::Dependence::DVEntry::LE) { - Flow = true; - } else { - Anti = true; - } - } else if (Dependence->isAnti()) { - if (Direction == tsar_impl::Dependence::DVEntry::LT || - Direction == tsar_impl::Dependence::DVEntry::LE) { - Anti = true; - } else { - Flow = true; - } - } +private: + void getLoopSplits(Loop *Loop, const DependencyInstructionVector &Reads, + const DependencyInstructionVector &Writes) const { + const auto LoopDepth = Loop->getLoopDepth(); + std::set Splits(Writes.begin(), Writes.end()); + for (const auto &Write : Writes) { + for (const auto &Read : Reads) { + // TODO: Probably true instead of false + const auto Dependence = + mDependence->depends(mGetInstructionFunction(Write), + mGetInstructionFunction(Read), false); + if (!Dependence) { + continue; + } - const auto WriteBeforeRead = Write->comesBefore(Read); - // If this is bad instructions dependency, can't split between them - if (!(WriteBeforeRead && Anti || !WriteBeforeRead && Flow)) { - continue; - } + const auto Direction = Dependence->getDirection(LoopDepth); + if (Direction == tsar_impl::Dependence::DVEntry::EQ) { + LLVM_DEBUG(dbgs() << "Ignore loop independent dependence\n"); + continue; + } - for (auto &Split : Writes) { - if (!Splits.count(Split)) { - continue; - } - if (Split->comesBefore(Write) && Read->comesBefore(Split) || - Split->comesBefore(Read) && Write->comesBefore(Split)) { - Splits.erase(Split); - } - } + // Get direction of the dependency + auto Flow = false, Anti = false; + if (Direction == tsar_impl::Dependence::DVEntry::ALL) { + Flow = true; + Anti = true; + } else if (Dependence->isFlow()) { + if (Direction == tsar_impl::Dependence::DVEntry::LT || + Direction == tsar_impl::Dependence::DVEntry::LE) { + Flow = true; + } else { + Anti = true; + } + } else if (Dependence->isAnti()) { + if (Direction == tsar_impl::Dependence::DVEntry::LT || + Direction == tsar_impl::Dependence::DVEntry::LE) { + Anti = true; + } else { + Flow = true; } } - dbgs() << "Splits:\n"; - for (auto *Split : Splits) { - Split->dump(); + + const auto WriteBeforeRead = Write->comesBefore(Read); + // If this is bad instructions dependency, can't split between them + if (!(WriteBeforeRead && Anti || !WriteBeforeRead && Flow)) { + continue; + } + + for (const auto &Split : Writes) { + if (!Splits.count(Split)) { + continue; + } + if (Split->comesBefore(Write) && Read->comesBefore(Split) || + Split->comesBefore(Read) && Write->comesBefore(Split)) { + Splits.erase(Split); + } } } - //auto *Alias = DIMem.getAliasNode(); } - const auto PrevIsInsideLoop = mIsInsideLoop; - mIsInsideLoop = true; - const auto Result = RecursiveASTVisitor::TraverseStmt(ForStatement->getBody()); - mIsInsideLoop = PrevIsInsideLoop; - return Result; + LLVM_DEBUG( + dbgs() << "Found splits:\n"; + for (auto *Split : Splits) { + Split->dump(); + } + ); } private: @@ -305,6 +324,7 @@ class ASTVisitor : public RecursiveASTVisitor { const CanonicalLoopSet *mCanonicalLoop; const LoopMatcherPass::LoopMatcher *mLoopMatcher; const GlobalOptions *mGlobalOptions; + const SourceManager *mSourceManager; DIAliasTree *mDIAliasTree; DIDependencInfo *mDIDependency; DependenceInfo *mDependence; @@ -330,7 +350,7 @@ bool LoopDistributionPass::runOnFunction(Function& Function) { if (!FunctionDecl) { return false; } - ASTVisitor LoopVisitor(*this, Function); + ASTVisitor LoopVisitor(*this, Function, *TransformationContext); LoopVisitor.TraverseDecl(FunctionDecl); return false; } From 47009b51436921af8116b1dc7d4e4417ec1520ef Mon Sep 17 00:00:00 2001 From: Viacheslav Kozyrev Date: Mon, 10 May 2021 15:36:06 +0300 Subject: [PATCH 83/93] [TSAR, Distribution] Pick out funcionality of getting splits. --- lib/Transform/Clang/LoopDistribution.cpp | 151 +++++++++++++---------- 1 file changed, 88 insertions(+), 63 deletions(-) diff --git a/lib/Transform/Clang/LoopDistribution.cpp b/lib/Transform/Clang/LoopDistribution.cpp index 87ef8507..016f9021 100644 --- a/lib/Transform/Clang/LoopDistribution.cpp +++ b/lib/Transform/Clang/LoopDistribution.cpp @@ -40,6 +40,7 @@ #include "tsar/Support/GlobalOptions.h" #include "tsar/Unparse/Utils.h" #include +#include #include #include #include // TODO: use SmallVector @@ -96,6 +97,7 @@ INITIALIZE_PASS_IN_GROUP_END(LoopDistributionPass, "loop-distribution", namespace { typedef std::vector DependencyInstructionVector; + typedef std::set SplitInstructionVector; class ASTVisitor : public RecursiveASTVisitor { public: @@ -132,7 +134,7 @@ class ASTVisitor : public RecursiveASTVisitor { auto RM = Socket.getAnalysis(); assert(RM && "Client to server IR-matcher must be available!"); auto *Matcher = RM->value(); - mGetLoopIdFunction = [Matcher](ObjectID ID) { + mGetServerLoopIdFunction = [Matcher](ObjectID ID) { auto ServerID = (*Matcher)->getMappedMD(ID); return ServerID ? cast(*ServerID) @@ -159,35 +161,63 @@ class ASTVisitor : public RecursiveASTVisitor { } auto *const Loop = LoopMatch->get(); - auto *DFLoopRegion = dyn_cast(mDFRegion->getRegionFor(Loop)); - const auto CanonicalLoopItr = mCanonicalLoop->find_as(DFLoopRegion); - if (CanonicalLoopItr == mCanonicalLoop->end() - || !(**CanonicalLoopItr).isCanonical() - || !(**CanonicalLoopItr).getLoop()) { + auto const OptionalDIDependenceSet = getDIDependenceSet(Loop); + if (!OptionalDIDependenceSet.hasValue()) { return false; } + LLVM_DEBUG(dbgs() << "Found canonical loop with dependencies at "; + ForStatement->getBeginLoc().print(dbgs(), *mSourceManager); + dbgs() << "\n";); + auto &DIDependenceSet = OptionalDIDependenceSet.getValue(); + auto Splits = getLoopSplits(Loop, DIDependenceSet); LLVM_DEBUG( - dbgs() << "Found canonical loop at "; - ForStatement->getBeginLoc().print(dbgs(), *mSourceManager); - dbgs() << "\n"; + dbgs() << "Found splits:\n"; + for (auto *Split : Splits) { + Split->dump(); + } ); - // TODO: See ParallelLoop.cpp, probably I should use for_each_loop instead. + + const auto PrevIsInsideLoop = mIsInsideLoop; + mIsInsideLoop = true; + const auto Result = RecursiveASTVisitor::TraverseStmt(ForStatement->getBody()); + mIsInsideLoop = PrevIsInsideLoop; + return Result; + } + +private: + Optional getDIDependenceSet(Loop *Loop) const { + auto *DFLoopRegion = dyn_cast(mDFRegion->getRegionFor(Loop)); + if (const auto &CanonicalLoopItr = mCanonicalLoop->find_as(DFLoopRegion); + CanonicalLoopItr == mCanonicalLoop->end() || + !(**CanonicalLoopItr).isCanonical() || !(**CanonicalLoopItr).getLoop()) { + return None; + } + auto *LoopID = Loop->getLoopID(); - if (!LoopID || !(LoopID = mGetLoopIdFunction(LoopID))) { - LLVM_DEBUG(dbgs() << "Ignore loop without ID"); - return false; + if (!LoopID) { + LLVM_DEBUG(dbgs() << "Ignored loop without ID\n"); + return None; + } + LoopID = mGetServerLoopIdFunction(LoopID); + if (!LoopID) { + LLVM_DEBUG(dbgs() << "Ignored loop collapsed on the pass server\n"); + return None; } const auto DependencyItr = mDIDependency->find(LoopID); if (DependencyItr == mDIDependency->end()) { - return false; + return None; } - auto &DIDependenceSet = DependencyItr->get(); + return DependencyItr->get(); + } + + SplitInstructionVector getLoopSplits( + Loop *Loop, const DIDependenceSet &DIDependenceSet) { DenseSet Coverage; accessCoverage(DIDependenceSet, *mDIAliasTree, - Coverage, mGlobalOptions->IgnoreRedundantMemory); + Coverage, mGlobalOptions->IgnoreRedundantMemory); DependencyInstructionVector DependencyReads, DependencyWrites; for (auto &AliasTrait : DIDependenceSet) { if (!Coverage.count(AliasTrait.getNode())) { @@ -201,57 +231,57 @@ class ASTVisitor : public RecursiveASTVisitor { const auto *DIMemory = DIMemoryTraitItr->getMemory(); LLVM_DEBUG( - if (DIMemoryTraitItr->is()) { - printDILocationSource(dwarf::DW_LANG_C, *DIMemory, dbgs()); - dbgs() << "Flow dependency\n"; - } - if (DIMemoryTraitItr->is()) { - printDILocationSource(dwarf::DW_LANG_C, *DIMemory, dbgs()); - dbgs() << "Antiflow dependency\n"; - } - ); + if (DIMemoryTraitItr->is()) { + printDILocationSource(dwarf::DW_LANG_C, *DIMemory, dbgs()); + dbgs() << "Flow dependency\n"; + } if (DIMemoryTraitItr->is()) { + printDILocationSource(dwarf::DW_LANG_C, *DIMemory, dbgs()); + dbgs() << "Antiflow dependency\n"; + }); const auto *DINode = DIMemory->getAliasNode(); for (const auto &BasicBlock : Loop->blocks()) { // Get all reads and writes of memory leading to dependencies - for_each_memory(*BasicBlock, *mTargetLibrary, [this, DINode, &DependencyReads, &DependencyWrites](Instruction &I, - MemoryLocation &&Loc, unsigned, AccessInfo R, AccessInfo W) { - auto *EM = mAliasTree->find(Loc); - assert(EM && "Estimate memory location must not be null!"); - const auto &DL = I.getModule()->getDataLayout(); - auto *DIM = mServerDIMemory->findFromClient( - *EM->getTopLevelParent(), DL, *mDominatorTree).get(); - if (!DIM || mSpanningTreeRelation->isUnreachable(DINode, DIM->getAliasNode())) { - return; - } - if (W != AccessInfo::No) { - DependencyWrites.push_back(&I); - LLVM_DEBUG(dbgs() << "Write "; I.dump()); - } - if (R != AccessInfo::No) { - DependencyReads.push_back(&I); - LLVM_DEBUG(dbgs() << "Read "; I.dump()); - } - }, [](Instruction &I, AccessInfo, AccessInfo W) { - // TODO: Fill in - }); + for_each_memory( + *BasicBlock, *mTargetLibrary, + [this, DINode, &DependencyReads, &DependencyWrites]( + Instruction &I, MemoryLocation &&Loc, unsigned, AccessInfo R, + AccessInfo W) { + auto *EM = mAliasTree->find(Loc); + assert(EM && "Estimate memory location must not be null!"); + const auto &DL = I.getModule()->getDataLayout(); + auto *DIM = mServerDIMemory + ->findFromClient(*EM->getTopLevelParent(), DL, + *mDominatorTree) + .get(); + if (!DIM || mSpanningTreeRelation->isUnreachable( + DINode, DIM->getAliasNode())) { + return; + } + if (W != AccessInfo::No) { + DependencyWrites.push_back(&I); + LLVM_DEBUG(dbgs() << "Write "; I.dump()); + } + if (R != AccessInfo::No) { + DependencyReads.push_back(&I); + LLVM_DEBUG(dbgs() << "Read "; I.dump()); + } + }, + [](Instruction &I, AccessInfo, AccessInfo W) { + // TODO: Fill in + } + ); } } - // Try to get write instructions after which should be split - getLoopSplits(Loop, DependencyReads, DependencyWrites); - const auto PrevIsInsideLoop = mIsInsideLoop; - mIsInsideLoop = true; - const auto Result = RecursiveASTVisitor::TraverseStmt(ForStatement->getBody()); - mIsInsideLoop = PrevIsInsideLoop; - return Result; + return getLoopSplits(Loop, DependencyReads, DependencyWrites); } -private: - void getLoopSplits(Loop *Loop, const DependencyInstructionVector &Reads, + SplitInstructionVector getLoopSplits( + Loop *Loop, const DependencyInstructionVector &Reads, const DependencyInstructionVector &Writes) const { const auto LoopDepth = Loop->getLoopDepth(); - std::set Splits(Writes.begin(), Writes.end()); + SplitInstructionVector Splits(Writes.begin(), Writes.end()); for (const auto &Write : Writes) { for (const auto &Read : Reads) { // TODO: Probably true instead of false @@ -306,12 +336,7 @@ class ASTVisitor : public RecursiveASTVisitor { } } } - LLVM_DEBUG( - dbgs() << "Found splits:\n"; - for (auto *Split : Splits) { - Split->dump(); - } - ); + return Splits; } private: @@ -328,7 +353,7 @@ class ASTVisitor : public RecursiveASTVisitor { DIAliasTree *mDIAliasTree; DIDependencInfo *mDIDependency; DependenceInfo *mDependence; - std::function mGetLoopIdFunction; + std::function mGetServerLoopIdFunction; std::function mGetInstructionFunction; bool mIsInsideLoop = false; }; From 78f6b658d2bc1b76aec23af9fd38049b38b8057a Mon Sep 17 00:00:00 2001 From: Viacheslav Kozyrev Date: Tue, 11 May 2021 08:44:07 +0300 Subject: [PATCH 84/93] [TSAR, Distribution] Use ClangExprMatcherPass to get AST node of split. --- lib/Transform/Clang/LoopDistribution.cpp | 49 +++++++++++++++++------- 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/lib/Transform/Clang/LoopDistribution.cpp b/lib/Transform/Clang/LoopDistribution.cpp index 016f9021..4327f9fc 100644 --- a/lib/Transform/Clang/LoopDistribution.cpp +++ b/lib/Transform/Clang/LoopDistribution.cpp @@ -26,6 +26,7 @@ #include "tsar/ADT/SpanningTreeRelation.h" #include "tsar/Analysis/AnalysisServer.h" #include "tsar/Analysis/Clang/CanonicalLoop.h" +#include "tsar/Analysis/Clang/ExpressionMatcher.h" #include "tsar/Analysis/Clang/LoopMatcher.h" #include "tsar/Analysis/DFRegionInfo.h" #include "tsar/Analysis/Memory/DependenceAnalysis.h" @@ -87,6 +88,7 @@ INITIALIZE_PASS_DEPENDENCY(DependenceAnalysisWrapperPass) INITIALIZE_PASS_DEPENDENCY(DFRegionInfoPass) INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass) INITIALIZE_PASS_DEPENDENCY(CanonicalLoopPass) +INITIALIZE_PASS_DEPENDENCY(ClangExprMatcherPass) INITIALIZE_PASS_DEPENDENCY(LoopMatcherPass) INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass) INITIALIZE_PASS_DEPENDENCY(GlobalOptionsImmutableWrapper) @@ -118,11 +120,13 @@ class ASTVisitor : public RecursiveASTVisitor { mServerDIMemory->DIAT); mCanonicalLoop = &Pass.getAnalysis() .getCanonicalLoopInfo(); + mExpressionMatcher = &Pass.getAnalysis().getMatcher(); mLoopMatcher = &Pass.getAnalysis() .getMatcher(); mGlobalOptions = &Pass.getAnalysis() .getOptions(); mSourceManager = &TransformationContext.getRewriter().getSourceMgr(); + mASTContext = &TransformationContext.getContext(); auto& SocketInfo = Pass.getAnalysis().get(); auto& Socket = SocketInfo.getActive()->second; auto RF = Socket.getAnalysis { auto Splits = getLoopSplits(Loop, DIDependenceSet); LLVM_DEBUG( dbgs() << "Found splits:\n"; - for (auto *Split : Splits) { + for (const auto *Split : Splits) { Split->dump(); } ); + processSplits(Splits); const auto PrevIsInsideLoop = mIsInsideLoop; mIsInsideLoop = true; @@ -339,6 +344,19 @@ class ASTVisitor : public RecursiveASTVisitor { return Splits; } + void processSplits(const SplitInstructionVector &Splits) const { + for (auto *Split : Splits) { + const auto &ExpressionMatcherItr = mExpressionMatcher->find(Split); + if (ExpressionMatcherItr == mExpressionMatcher->end()) { + LLVM_DEBUG(dbgs() << "Store instruction can't be bound to AST node: "; + Split->dump();); + continue; + } + const auto &SplitStatement = ExpressionMatcherItr->get(); + SplitStatement.dump(dbgs(), *mASTContext); + } + } + private: DFRegionInfo *mDFRegion; TargetLibraryInfo *mTargetLibrary; @@ -347,9 +365,11 @@ class ASTVisitor : public RecursiveASTVisitor { DIMemoryClientServerInfo *mServerDIMemory; SpanningTreeRelation *mSpanningTreeRelation; const CanonicalLoopSet *mCanonicalLoop; + const ClangExprMatcherPass::ExprMatcher *mExpressionMatcher; const LoopMatcherPass::LoopMatcher *mLoopMatcher; const GlobalOptions *mGlobalOptions; const SourceManager *mSourceManager; + const ASTContext *mASTContext; DIAliasTree *mDIAliasTree; DIDependencInfo *mDIDependency; DependenceInfo *mDependence; @@ -381,19 +401,20 @@ bool LoopDistributionPass::runOnFunction(Function& Function) { } void LoopDistributionPass::getAnalysisUsage(AnalysisUsage &AU) const { - AU.addRequired(); - AU.addRequired(); - AU.addRequired(); - AU.addRequired(); - AU.addRequired(); - AU.addRequired(); - AU.addRequired(); - AU.addRequired(); - AU.addRequired(); - AU.addRequired(); - AU.addRequired(); - AU.addRequired(); - AU.setPreservesAll(); + AU.addRequired(); + AU.addRequired(); + AU.addRequired(); + AU.addRequired(); + AU.addRequired(); + AU.addRequired(); + AU.addRequired(); + AU.addRequired(); + AU.addRequired(); + AU.addRequired(); + AU.addRequired(); + AU.addRequired(); + AU.addRequired(); + AU.setPreservesAll(); } FunctionPass * llvm::createLoopDistributionPass() { From 738960843e79a624b87730baf508a5e53c27470e Mon Sep 17 00:00:00 2001 From: Viacheslav Kozyrev Date: Tue, 25 May 2021 09:11:27 +0300 Subject: [PATCH 85/93] tmp --- lib/Transform/Clang/LoopDistribution.cpp | 47 +++++++++++++++++++++++- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/lib/Transform/Clang/LoopDistribution.cpp b/lib/Transform/Clang/LoopDistribution.cpp index 4327f9fc..9b5b6827 100644 --- a/lib/Transform/Clang/LoopDistribution.cpp +++ b/lib/Transform/Clang/LoopDistribution.cpp @@ -183,6 +183,7 @@ class ASTVisitor : public RecursiveASTVisitor { ); processSplits(Splits); + // TODO: Use this information. const auto PrevIsInsideLoop = mIsInsideLoop; mIsInsideLoop = true; const auto Result = RecursiveASTVisitor::TraverseStmt(ForStatement->getBody()); @@ -377,6 +378,46 @@ class ASTVisitor : public RecursiveASTVisitor { std::function mGetInstructionFunction; bool mIsInsideLoop = false; }; + +class CodeRewriter : public RecursiveASTVisitor { + public: + CodeRewriter(FunctionPass &Pass, Function &Function, + ClangTransformationContext &TransformationContext) { + mRewriter = &TransformationContext.getRewriter(); + } + + [[maybe_unused]] + bool TraverseStmt(Stmt *Statement) { + if (!Statement) { + return RecursiveASTVisitor::TraverseStmt(Statement); + } + + auto *ForStatement = dyn_cast(Statement); + if (ForStatement) { + return TraverseForStmt(ForStatement); + } + + return RecursiveASTVisitor::TraverseStmt(Statement); + } + + [[maybe_unused]] + bool TraverseForStmt(ForStmt *ForStatement) { + dbgs() << "First time?"; + ForStatement->dump(); + + // TODO: Use this information. + const auto PrevIsInsideLoop = mIsInsideLoop; + mIsInsideLoop = true; + const auto Result = + RecursiveASTVisitor::TraverseStmt(ForStatement->getBody()); + mIsInsideLoop = PrevIsInsideLoop; + return Result; + } + +private: + Rewriter *mRewriter; + bool mIsInsideLoop = false; +}; } bool LoopDistributionPass::runOnFunction(Function& Function) { @@ -395,8 +436,10 @@ bool LoopDistributionPass::runOnFunction(Function& Function) { if (!FunctionDecl) { return false; } - ASTVisitor LoopVisitor(*this, Function, *TransformationContext); - LoopVisitor.TraverseDecl(FunctionDecl); + //ASTVisitor LoopVisitor(*this, Function, *TransformationContext); + //LoopVisitor.TraverseDecl(FunctionDecl); + CodeRewriter Rewriter(*this, Function, *TransformationContext); + Rewriter.TraverseDecl(FunctionDecl); return false; } From 712df3178407efa71ca8327bd07f287949573afb Mon Sep 17 00:00:00 2001 From: Viacheslav Kozyrev Date: Mon, 31 May 2021 08:39:20 +0300 Subject: [PATCH 86/93] add rewriter work --- lib/Transform/Clang/LoopDistribution.cpp | 29 +++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/lib/Transform/Clang/LoopDistribution.cpp b/lib/Transform/Clang/LoopDistribution.cpp index 9b5b6827..537c9d3a 100644 --- a/lib/Transform/Clang/LoopDistribution.cpp +++ b/lib/Transform/Clang/LoopDistribution.cpp @@ -402,8 +402,10 @@ class CodeRewriter : public RecursiveASTVisitor { [[maybe_unused]] bool TraverseForStmt(ForStmt *ForStatement) { - dbgs() << "First time?"; - ForStatement->dump(); + dbgs() << "First time?\n"; + //ForStatement->dump(); + + getLoopHeader(ForStatement); // TODO: Use this information. const auto PrevIsInsideLoop = mIsInsideLoop; @@ -411,7 +413,28 @@ class CodeRewriter : public RecursiveASTVisitor { const auto Result = RecursiveASTVisitor::TraverseStmt(ForStatement->getBody()); mIsInsideLoop = PrevIsInsideLoop; - return Result; + //return Result; + return true; + } + + [[maybe_unused]] + bool VisitStmt(Stmt *Statement) { + if (!mIsInsideLoop) { + return true; + } + + /*const char *InsertedText = "for (;;){}"; + mRewriter->InsertText(Statement->getEndLoc(), InsertedText, true, true);*/ + return false; + } + +private: + // TODO: + void getLoopHeader(ForStmt *ForStatement) const { + auto HeaderRange = SourceRange(ForStatement->getBeginLoc(), + ForStatement->getBody()->getBeginLoc()); + HeaderRange.dump(mRewriter->getSourceMgr()); + CharSourceRange::getCharRange(HeaderRange); } private: From 73d5d646ea5c11d50d732ca6efe6275759750006 Mon Sep 17 00:00:00 2001 From: Viacheslav Kozyrev Date: Sat, 7 Aug 2021 12:41:00 +0300 Subject: [PATCH 87/93] Get splitter string --- lib/Transform/Clang/LoopDistribution.cpp | 45 ++++++++++++++++++------ 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/lib/Transform/Clang/LoopDistribution.cpp b/lib/Transform/Clang/LoopDistribution.cpp index 537c9d3a..e26fba65 100644 --- a/lib/Transform/Clang/LoopDistribution.cpp +++ b/lib/Transform/Clang/LoopDistribution.cpp @@ -41,6 +41,7 @@ #include "tsar/Support/GlobalOptions.h" #include "tsar/Unparse/Utils.h" #include +#include #include #include #include @@ -382,8 +383,9 @@ class ASTVisitor : public RecursiveASTVisitor { class CodeRewriter : public RecursiveASTVisitor { public: CodeRewriter(FunctionPass &Pass, Function &Function, - ClangTransformationContext &TransformationContext) { + ClangTransformationContext &TransformationContext) { mRewriter = &TransformationContext.getRewriter(); + mSourceMgr = &mRewriter->getSourceMgr(); } [[maybe_unused]] @@ -405,7 +407,8 @@ class CodeRewriter : public RecursiveASTVisitor { dbgs() << "First time?\n"; //ForStatement->dump(); - getLoopHeader(ForStatement); + mLoopHeaderSplitter = getLoopHeaderSplitter(ForStatement); + dbgs() << mLoopHeaderSplitter << "\n"; // TODO: Use this information. const auto PrevIsInsideLoop = mIsInsideLoop; @@ -423,23 +426,45 @@ class CodeRewriter : public RecursiveASTVisitor { return true; } - /*const char *InsertedText = "for (;;){}"; - mRewriter->InsertText(Statement->getEndLoc(), InsertedText, true, true);*/ + /*mRewriter->InsertText(Statement->getEndLoc(), + mLoopHeaderSplitter, true, true);*/ return false; } private: - // TODO: - void getLoopHeader(ForStmt *ForStatement) const { - auto HeaderRange = SourceRange(ForStatement->getBeginLoc(), - ForStatement->getBody()->getBeginLoc()); - HeaderRange.dump(mRewriter->getSourceMgr()); - CharSourceRange::getCharRange(HeaderRange); + [[nodiscard]] + std::string getLoopHeaderSplitter(ForStmt *ForStatement) const { + std::string LoopHeaderSplitter; + raw_string_ostream SplitterStream(LoopHeaderSplitter); + SplitterStream << "}"; + SplitterStream << getCharacterData(ForStatement->getBeginLoc(), + ForStatement->getBody()->getBeginLoc()); + SplitterStream << "{"; + return SplitterStream.str(); + } + + [[nodiscard]] + std::string getCharacterData(const SourceLocation BeginLoc, + const SourceLocation EndLoc) const { + bool Invalid; + const auto BeginData = mSourceMgr->getCharacterData(BeginLoc, &Invalid); + if (Invalid) { + throw std::exception("Couldn't get character data"); + } + + const auto EndData = mSourceMgr->getCharacterData(EndLoc, &Invalid); + if (Invalid) { + throw std::exception("Couldn't get character data"); + } + + return std::string(BeginData, EndData); } private: Rewriter *mRewriter; + SourceManager *mSourceMgr; bool mIsInsideLoop = false; + std::string mLoopHeaderSplitter; }; } From ac3988b973405e000864ef568a227387b990f36d Mon Sep 17 00:00:00 2001 From: Viacheslav Kozyrev Date: Sun, 22 Aug 2021 16:06:32 +0300 Subject: [PATCH 88/93] Use single ASTVisitor --- lib/Transform/Clang/LoopDistribution.cpp | 161 +++++++++-------------- 1 file changed, 59 insertions(+), 102 deletions(-) diff --git a/lib/Transform/Clang/LoopDistribution.cpp b/lib/Transform/Clang/LoopDistribution.cpp index e26fba65..71007538 100644 --- a/lib/Transform/Clang/LoopDistribution.cpp +++ b/lib/Transform/Clang/LoopDistribution.cpp @@ -126,6 +126,7 @@ class ASTVisitor : public RecursiveASTVisitor { .getMatcher(); mGlobalOptions = &Pass.getAnalysis() .getOptions(); + mRewriter = &TransformationContext.getRewriter(); mSourceManager = &TransformationContext.getRewriter().getSourceMgr(); mASTContext = &TransformationContext.getContext(); auto& SocketInfo = Pass.getAnalysis().get(); @@ -151,15 +152,8 @@ class ASTVisitor : public RecursiveASTVisitor { } [[maybe_unused]] - bool TraverseStmt(Stmt *Statement) { - if (!Statement) { - return RecursiveASTVisitor::TraverseStmt(Statement); - } - auto *ForStatement = dyn_cast(Statement); - if (!ForStatement) { - return RecursiveASTVisitor::TraverseStmt(Statement); - } - + bool TraverseForStmt(ForStmt *ForStatement) { + DynTypedNode::create(*ForStatement).dump(dbgs(), *mASTContext); const auto LoopMatch = mLoopMatcher->find(ForStatement); if (LoopMatch == mLoopMatcher->end()) { return false; @@ -182,12 +176,13 @@ class ASTVisitor : public RecursiveASTVisitor { Split->dump(); } ); - processSplits(Splits); + + processSplits(ForStatement, Splits); // TODO: Use this information. const auto PrevIsInsideLoop = mIsInsideLoop; mIsInsideLoop = true; - const auto Result = RecursiveASTVisitor::TraverseStmt(ForStatement->getBody()); + const auto Result = TraverseStmt(ForStatement->getBody()); mIsInsideLoop = PrevIsInsideLoop; return Result; } @@ -241,11 +236,11 @@ class ASTVisitor : public RecursiveASTVisitor { if (DIMemoryTraitItr->is()) { printDILocationSource(dwarf::DW_LANG_C, *DIMemory, dbgs()); dbgs() << "Flow dependency\n"; - } if (DIMemoryTraitItr->is()) { + } + if (DIMemoryTraitItr->is()) { printDILocationSource(dwarf::DW_LANG_C, *DIMemory, dbgs()); dbgs() << "Antiflow dependency\n"; }); - const auto *DINode = DIMemory->getAliasNode(); for (const auto &BasicBlock : Loop->blocks()) { // Get all reads and writes of memory leading to dependencies @@ -346,7 +341,17 @@ class ASTVisitor : public RecursiveASTVisitor { return Splits; } - void processSplits(const SplitInstructionVector &Splits) const { + void processSplits(const ForStmt *ForStatement, + const SplitInstructionVector &Splits) const { + const auto LoopHeaderSplitter = getLoopHeaderSplitter(ForStatement); + if (!LoopHeaderSplitter.hasValue()) { + dbgs() << "Couldn't get character data for "; + ForStatement->dump(); + dbgs() << "\n"; + return; + } + + dbgs() << LoopHeaderSplitter.getValue() << "\n"; for (auto *Split : Splits) { const auto &ExpressionMatcherItr = mExpressionMatcher->find(Split); if (ExpressionMatcherItr == mExpressionMatcher->end()) { @@ -356,115 +361,66 @@ class ASTVisitor : public RecursiveASTVisitor { } const auto &SplitStatement = ExpressionMatcherItr->get(); SplitStatement.dump(dbgs(), *mASTContext); + // TODO: Incorrect location + mRewriter->InsertText(SplitStatement.getSourceRange().getEnd(), + LoopHeaderSplitter.getValue(), true, true); } } -private: - DFRegionInfo *mDFRegion; - TargetLibraryInfo *mTargetLibrary; - AliasTree *mAliasTree; - DominatorTree *mDominatorTree; - DIMemoryClientServerInfo *mServerDIMemory; - SpanningTreeRelation *mSpanningTreeRelation; - const CanonicalLoopSet *mCanonicalLoop; - const ClangExprMatcherPass::ExprMatcher *mExpressionMatcher; - const LoopMatcherPass::LoopMatcher *mLoopMatcher; - const GlobalOptions *mGlobalOptions; - const SourceManager *mSourceManager; - const ASTContext *mASTContext; - DIAliasTree *mDIAliasTree; - DIDependencInfo *mDIDependency; - DependenceInfo *mDependence; - std::function mGetServerLoopIdFunction; - std::function mGetInstructionFunction; - bool mIsInsideLoop = false; -}; - -class CodeRewriter : public RecursiveASTVisitor { - public: - CodeRewriter(FunctionPass &Pass, Function &Function, - ClangTransformationContext &TransformationContext) { - mRewriter = &TransformationContext.getRewriter(); - mSourceMgr = &mRewriter->getSourceMgr(); - } - - [[maybe_unused]] - bool TraverseStmt(Stmt *Statement) { - if (!Statement) { - return RecursiveASTVisitor::TraverseStmt(Statement); - } - - auto *ForStatement = dyn_cast(Statement); - if (ForStatement) { - return TraverseForStmt(ForStatement); - } - - return RecursiveASTVisitor::TraverseStmt(Statement); - } - - [[maybe_unused]] - bool TraverseForStmt(ForStmt *ForStatement) { - dbgs() << "First time?\n"; - //ForStatement->dump(); - - mLoopHeaderSplitter = getLoopHeaderSplitter(ForStatement); - dbgs() << mLoopHeaderSplitter << "\n"; - - // TODO: Use this information. - const auto PrevIsInsideLoop = mIsInsideLoop; - mIsInsideLoop = true; - const auto Result = - RecursiveASTVisitor::TraverseStmt(ForStatement->getBody()); - mIsInsideLoop = PrevIsInsideLoop; - //return Result; - return true; - } - - [[maybe_unused]] - bool VisitStmt(Stmt *Statement) { - if (!mIsInsideLoop) { - return true; + [[nodiscard]] + Optional getLoopHeaderSplitter( + const ForStmt *ForStatement) const { + auto LoopHeader = getCharacterData( + ForStatement->getBeginLoc(), ForStatement->getBody()->getBeginLoc()); + if (!LoopHeader.hasValue()) { + return None; } - /*mRewriter->InsertText(Statement->getEndLoc(), - mLoopHeaderSplitter, true, true);*/ - return false; - } - -private: - [[nodiscard]] - std::string getLoopHeaderSplitter(ForStmt *ForStatement) const { std::string LoopHeaderSplitter; raw_string_ostream SplitterStream(LoopHeaderSplitter); SplitterStream << "}"; - SplitterStream << getCharacterData(ForStatement->getBeginLoc(), - ForStatement->getBody()->getBeginLoc()); + SplitterStream << LoopHeader.getValue(); SplitterStream << "{"; return SplitterStream.str(); } [[nodiscard]] - std::string getCharacterData(const SourceLocation BeginLoc, - const SourceLocation EndLoc) const { + Optional getCharacterData( + const SourceLocation BeginLoc, const SourceLocation EndLoc) const { bool Invalid; - const auto BeginData = mSourceMgr->getCharacterData(BeginLoc, &Invalid); + const auto BeginData = mSourceManager->getCharacterData(BeginLoc, &Invalid); if (Invalid) { - throw std::exception("Couldn't get character data"); + return None; } - const auto EndData = mSourceMgr->getCharacterData(EndLoc, &Invalid); + const auto EndData = mSourceManager->getCharacterData(EndLoc, &Invalid); if (Invalid) { - throw std::exception("Couldn't get character data"); + return None; } return std::string(BeginData, EndData); } - + private: + DFRegionInfo *mDFRegion; + TargetLibraryInfo *mTargetLibrary; + AliasTree *mAliasTree; + DominatorTree *mDominatorTree; + DIMemoryClientServerInfo *mServerDIMemory; + SpanningTreeRelation *mSpanningTreeRelation; + const CanonicalLoopSet *mCanonicalLoop; + const ClangExprMatcherPass::ExprMatcher *mExpressionMatcher; + const LoopMatcherPass::LoopMatcher *mLoopMatcher; + const GlobalOptions *mGlobalOptions; Rewriter *mRewriter; - SourceManager *mSourceMgr; + const SourceManager *mSourceManager; + const ASTContext *mASTContext; + DIAliasTree *mDIAliasTree; + DIDependencInfo *mDIDependency; + DependenceInfo *mDependence; + std::function mGetServerLoopIdFunction; + std::function mGetInstructionFunction; bool mIsInsideLoop = false; - std::string mLoopHeaderSplitter; }; } @@ -475,19 +431,20 @@ bool LoopDistributionPass::runOnFunction(Function& Function) { if (!TransformationInfo) { return false; } + auto *TransformationContext = TransformationInfo->getContext(*Module); if (!TransformationContext || !TransformationContext->hasInstance()) { return false; } + auto *FunctionDecl = TransformationContext->getDeclForMangledName(Function.getName()); if (!FunctionDecl) { return false; } - //ASTVisitor LoopVisitor(*this, Function, *TransformationContext); - //LoopVisitor.TraverseDecl(FunctionDecl); - CodeRewriter Rewriter(*this, Function, *TransformationContext); - Rewriter.TraverseDecl(FunctionDecl); + + ASTVisitor LoopVisitor(*this, Function, *TransformationContext); + LoopVisitor.TraverseDecl(FunctionDecl); return false; } From 719b9bac9f10a763ed5adc2a598fe6b048dc7394 Mon Sep 17 00:00:00 2001 From: Viacheslav Kozyrev Date: Sun, 29 Aug 2021 12:51:41 +0300 Subject: [PATCH 89/93] Move out dependence check method --- lib/Transform/Clang/LoopDistribution.cpp | 79 ++++++++++++------------ 1 file changed, 41 insertions(+), 38 deletions(-) diff --git a/lib/Transform/Clang/LoopDistribution.cpp b/lib/Transform/Clang/LoopDistribution.cpp index 71007538..5175a3d2 100644 --- a/lib/Transform/Clang/LoopDistribution.cpp +++ b/lib/Transform/Clang/LoopDistribution.cpp @@ -286,44 +286,7 @@ class ASTVisitor : public RecursiveASTVisitor { SplitInstructionVector Splits(Writes.begin(), Writes.end()); for (const auto &Write : Writes) { for (const auto &Read : Reads) { - // TODO: Probably true instead of false - const auto Dependence = - mDependence->depends(mGetInstructionFunction(Write), - mGetInstructionFunction(Read), false); - if (!Dependence) { - continue; - } - - const auto Direction = Dependence->getDirection(LoopDepth); - if (Direction == tsar_impl::Dependence::DVEntry::EQ) { - LLVM_DEBUG(dbgs() << "Ignore loop independent dependence\n"); - continue; - } - - // Get direction of the dependency - auto Flow = false, Anti = false; - if (Direction == tsar_impl::Dependence::DVEntry::ALL) { - Flow = true; - Anti = true; - } else if (Dependence->isFlow()) { - if (Direction == tsar_impl::Dependence::DVEntry::LT || - Direction == tsar_impl::Dependence::DVEntry::LE) { - Flow = true; - } else { - Anti = true; - } - } else if (Dependence->isAnti()) { - if (Direction == tsar_impl::Dependence::DVEntry::LT || - Direction == tsar_impl::Dependence::DVEntry::LE) { - Anti = true; - } else { - Flow = true; - } - } - - const auto WriteBeforeRead = Write->comesBefore(Read); - // If this is bad instructions dependency, can't split between them - if (!(WriteBeforeRead && Anti || !WriteBeforeRead && Flow)) { + if (isSplittableDependence(Read, Write, LoopDepth)) { continue; } @@ -341,6 +304,46 @@ class ASTVisitor : public RecursiveASTVisitor { return Splits; } + bool isSplittableDependence(Instruction *Read, Instruction *Write, + const unsigned LoopDepth) const { + // TODO: Probably true instead of false + const auto Dependence = mDependence->depends( + mGetInstructionFunction(Write), mGetInstructionFunction(Read), false); + if (!Dependence) { + return false; + } + + const auto Direction = Dependence->getDirection(LoopDepth); + if (Direction == tsar_impl::Dependence::DVEntry::EQ) { + return false; + } + + // Get direction of the dependency + auto Flow = false, Anti = false; + if (Direction == tsar_impl::Dependence::DVEntry::ALL) { + Flow = true; + Anti = true; + } else if (Dependence->isFlow()) { + if (Direction == tsar_impl::Dependence::DVEntry::LT || + Direction == tsar_impl::Dependence::DVEntry::LE) { + Flow = true; + } else { + Anti = true; + } + } else if (Dependence->isAnti()) { + if (Direction == tsar_impl::Dependence::DVEntry::LT || + Direction == tsar_impl::Dependence::DVEntry::LE) { + Anti = true; + } else { + Flow = true; + } + } + + const auto WriteBeforeRead = Write->comesBefore(Read); + // If this is bad instructions dependency, can't split between them + return !(WriteBeforeRead && Anti || !WriteBeforeRead && Flow); + } + void processSplits(const ForStmt *ForStatement, const SplitInstructionVector &Splits) const { const auto LoopHeaderSplitter = getLoopHeaderSplitter(ForStatement); From ee9783ca9efb081c1fb836b0ad38f1616c500861 Mon Sep 17 00:00:00 2001 From: Viacheslav Kozyrev Date: Sun, 29 Aug 2021 15:10:09 +0300 Subject: [PATCH 90/93] Insert header in correct location --- lib/Transform/Clang/LoopDistribution.cpp | 34 ++++++++++++++++-------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/lib/Transform/Clang/LoopDistribution.cpp b/lib/Transform/Clang/LoopDistribution.cpp index 5175a3d2..9974533a 100644 --- a/lib/Transform/Clang/LoopDistribution.cpp +++ b/lib/Transform/Clang/LoopDistribution.cpp @@ -41,7 +41,9 @@ #include "tsar/Support/GlobalOptions.h" #include "tsar/Unparse/Utils.h" #include +#include #include +#include #include #include #include @@ -127,7 +129,8 @@ class ASTVisitor : public RecursiveASTVisitor { mGlobalOptions = &Pass.getAnalysis() .getOptions(); mRewriter = &TransformationContext.getRewriter(); - mSourceManager = &TransformationContext.getRewriter().getSourceMgr(); + mSourceManager = &mRewriter->getSourceMgr(); + mLangOptions = &mRewriter->getLangOpts(); mASTContext = &TransformationContext.getContext(); auto& SocketInfo = Pass.getAnalysis().get(); auto& Socket = SocketInfo.getActive()->second; @@ -279,6 +282,7 @@ class ASTVisitor : public RecursiveASTVisitor { return getLoopSplits(Loop, DependencyReads, DependencyWrites); } + //TODO: Remove last write if exists SplitInstructionVector getLoopSplits( Loop *Loop, const DependencyInstructionVector &Reads, const DependencyInstructionVector &Writes) const { @@ -356,17 +360,11 @@ class ASTVisitor : public RecursiveASTVisitor { dbgs() << LoopHeaderSplitter.getValue() << "\n"; for (auto *Split : Splits) { - const auto &ExpressionMatcherItr = mExpressionMatcher->find(Split); - if (ExpressionMatcherItr == mExpressionMatcher->end()) { - LLVM_DEBUG(dbgs() << "Store instruction can't be bound to AST node: "; - Split->dump();); - continue; + const auto SplitLocation = getSplitSourceLocation(Split); + if (SplitLocation.hasValue()) { + mRewriter->InsertTextAfterToken(SplitLocation.getValue(), + LoopHeaderSplitter.getValue()); } - const auto &SplitStatement = ExpressionMatcherItr->get(); - SplitStatement.dump(dbgs(), *mASTContext); - // TODO: Incorrect location - mRewriter->InsertText(SplitStatement.getSourceRange().getEnd(), - LoopHeaderSplitter.getValue(), true, true); } } @@ -403,6 +401,19 @@ class ASTVisitor : public RecursiveASTVisitor { return std::string(BeginData, EndData); } + + Optional getSplitSourceLocation(Instruction *Split) const { + const auto &ExpressionMatcherItr = mExpressionMatcher->find(Split); + if (ExpressionMatcherItr == mExpressionMatcher->end()) { + LLVM_DEBUG(dbgs() << "Store instruction can't be bound to AST node: "; + Split->dump();); + return None; + } + + const auto &SplitStatement = ExpressionMatcherItr->get(); + return Lexer::getLocForEndOfToken(SplitStatement.getSourceRange().getEnd(), + 0, *mSourceManager, *mLangOptions); + } private: DFRegionInfo *mDFRegion; @@ -417,6 +428,7 @@ class ASTVisitor : public RecursiveASTVisitor { const GlobalOptions *mGlobalOptions; Rewriter *mRewriter; const SourceManager *mSourceManager; + const LangOptions *mLangOptions; const ASTContext *mASTContext; DIAliasTree *mDIAliasTree; DIDependencInfo *mDIDependency; From ebe3daf4e4823d0317db4a71e3b00c717e6773b3 Mon Sep 17 00:00:00 2001 From: Viacheslav Kozyrev Date: Tue, 23 Aug 2022 18:22:02 +0300 Subject: [PATCH 91/93] Fix null reference exception --- lib/Transform/Clang/LoopDistribution.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/Transform/Clang/LoopDistribution.cpp b/lib/Transform/Clang/LoopDistribution.cpp index 9974533a..8d3c8e43 100644 --- a/lib/Transform/Clang/LoopDistribution.cpp +++ b/lib/Transform/Clang/LoopDistribution.cpp @@ -150,7 +150,10 @@ class ASTVisitor : public RecursiveASTVisitor { : nullptr; }; mGetInstructionFunction = [Matcher](Instruction *I) { - return cast((**Matcher)[I]); + auto Instr = (**Matcher)[I]; + return Instr + ? cast(Instr) + : nullptr; }; } @@ -310,9 +313,14 @@ class ASTVisitor : public RecursiveASTVisitor { bool isSplittableDependence(Instruction *Read, Instruction *Write, const unsigned LoopDepth) const { + const auto ServerWrite = mGetInstructionFunction(Write); + const auto ServerRead = mGetInstructionFunction(Read); + if (!ServerWrite || !ServerRead) { + return false; + } // TODO: Probably true instead of false - const auto Dependence = mDependence->depends( - mGetInstructionFunction(Write), mGetInstructionFunction(Read), false); + const auto Dependence = + mDependence->depends(ServerWrite, ServerRead, false); if (!Dependence) { return false; } From 630203471911fe1c433328d2cdc57b35954f3e01 Mon Sep 17 00:00:00 2001 From: Viacheslav Kozyrev Date: Fri, 26 Aug 2022 10:16:49 +0300 Subject: [PATCH 92/93] Fix trivial --- lib/Transform/Clang/LoopDistribution.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Transform/Clang/LoopDistribution.cpp b/lib/Transform/Clang/LoopDistribution.cpp index 8d3c8e43..9b8b0faa 100644 --- a/lib/Transform/Clang/LoopDistribution.cpp +++ b/lib/Transform/Clang/LoopDistribution.cpp @@ -322,7 +322,7 @@ class ASTVisitor : public RecursiveASTVisitor { const auto Dependence = mDependence->depends(ServerWrite, ServerRead, false); if (!Dependence) { - return false; + return true; } const auto Direction = Dependence->getDirection(LoopDepth); From af8ce9bcb124a21fcacefc951d3f3433177dfcd1 Mon Sep 17 00:00:00 2001 From: Viacheslav Kozyrev Date: Mon, 29 Aug 2022 16:54:17 +0300 Subject: [PATCH 93/93] Fix compilation on llvm 15 --- lib/Transform/Clang/LoopDistribution.cpp | 28 +++++++++++++++++++----- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/lib/Transform/Clang/LoopDistribution.cpp b/lib/Transform/Clang/LoopDistribution.cpp index 9b8b0faa..04517a6b 100644 --- a/lib/Transform/Clang/LoopDistribution.cpp +++ b/lib/Transform/Clang/LoopDistribution.cpp @@ -36,6 +36,7 @@ #include "tsar/Analysis/Memory/EstimateMemory.h" #include "tsar/Analysis/Memory/MemoryAccessUtils.h" #include "tsar/Analysis/Memory/MemoryTraitUtils.h" +#include "tsar/Analysis/Memory/Utils.h" #include "tsar/Core/Query.h" #include "tsar/Frontend/Clang/TransformationContext.h" #include "tsar/Support/GlobalOptions.h" @@ -448,15 +449,30 @@ class ASTVisitor : public RecursiveASTVisitor { } bool LoopDistributionPass::runOnFunction(Function& Function) { - auto *Module = Function.getParent(); - auto& TransformationInfo = - getAnalysis(); - if (!TransformationInfo) { + auto *DISub{findMetadata(&Function)}; + if (!DISub) { return false; } - auto *TransformationContext = TransformationInfo->getContext(*Module); - if (!TransformationContext || !TransformationContext->hasInstance()) { + auto *CompileUnit{DISub->getUnit()}; + if (!isC(CompileUnit->getSourceLanguage()) + && !isCXX(CompileUnit->getSourceLanguage())) { + return false; + } + + auto& TransformationInfo = + getAnalysis(); + auto *TransformationContext{ + TransformationInfo + ? dyn_cast_or_null( + TransformationInfo->getContext(*CompileUnit)) + : nullptr}; + if (!TransformationContext + || !TransformationContext->hasInstance()) { + Function.getContext().emitError( + "cannot transform sources: " + "transformation context is not available for the '" + + Function.getName() + "' function"); return false; }