diff --git a/include/tsar/Support/DiagnosticKinds.td b/include/tsar/Support/DiagnosticKinds.td index 84768832..4db344a8 100644 --- a/include/tsar/Support/DiagnosticKinds.td +++ b/include/tsar/Support/DiagnosticKinds.td @@ -106,6 +106,10 @@ def note_assert_no_macro : Note<"macro found">; def warn_rename_macro_prevent : Warning<"macro prevent renaming">; +def warn_splitdeclaration_macro_prevent : Warning<"macro prevent splitting">; +def warn_parm_var_decl_split_prevent : Warning<"local parm variable declarations are not splitted"> +def warn_pointers_to_constants_split_prevent : Warning<"declarations of pointers to constants are not splitted"> + def warn_propagate_macro_prevent : Warning<"macro prevent expression propagation">; def warn_disable_propagate_in_include : Warning<"disable expression propagation in header file">; def warn_disable_propagate : Warning<"disable expression propagation">; diff --git a/include/tsar/Support/Directives.td b/include/tsar/Support/Directives.td index 85f3b668..d613d798 100644 --- a/include/tsar/Support/Directives.td +++ b/include/tsar/Support/Directives.td @@ -134,6 +134,8 @@ def NoInline : Clause<"noinline", Transform>; def Propagate : Clause<"propagate", Transform>; +def SplitDeclaration : Clause<"splitdeclaration", Transform>; + def Rename : Clause<"rename", Transform>; def LoopInterchange : Clause<"interchange", Transform, diff --git a/include/tsar/Transform/Clang/Passes.h b/include/tsar/Transform/Clang/Passes.h index dcb9dc5f..65699b55 100644 --- a/include/tsar/Transform/Clang/Passes.h +++ b/include/tsar/Transform/Clang/Passes.h @@ -114,5 +114,12 @@ void initializeClangLoopReversePass(PassRegistry &Registry); /// Create a pass to reverse loop. ModulePass *createClangLoopReverse(); + +/// Creates a pass for splitting variable declarations into single ones. +llvm::ModulePass * createClangSplitDeclsPass(); + +/// Initializes a pass for splitting variable declarations into single ones. +void initializeClangSplitDeclsPassPass(PassRegistry &Registry); + } #endif//TSAR_CLANG_TRANSFORM_PASSES_H diff --git a/include/tsar/Transform/Clang/SplitDecls.h b/include/tsar/Transform/Clang/SplitDecls.h new file mode 100644 index 00000000..a79b5701 --- /dev/null +++ b/include/tsar/Transform/Clang/SplitDecls.h @@ -0,0 +1,61 @@ +//===- MyFirstPass.h - Source-level Renaming of Local Objects -- *- 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 declares a pass to perform renaming of objects into a specified +// scope. The goal of this transformation is to ensure that there is no +// different objects with the same name at a specified scope. The transformation +// also guaranties that names of objects declared in a specified scope do not +// match any name from other scopes. +// +//===----------------------------------------------------------------------===// +#ifndef TSAR_CLANG_SPLIT_DECLS_H +#define TSAR_CLANG_SPLIT_DECLS_H + +#include "tsar/Transform/Clang/Passes.h" +#include +#include + +#include "tsar/Analysis/Clang/GlobalInfoExtractor.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace llvm { +/// This pass separates variable declaration statements that contain multiple +/// variable declarations at once into single declarations. +class ClangSplitDeclsPass : public ModulePass, private bcl::Uncopyable { +public: + static char ID; + + ClangSplitDeclsPass() : ModulePass(ID) { + initializeClangSplitDeclsPassPass(*PassRegistry::getPassRegistry()); + } + + bool runOnModule(Module &M) override; + void getAnalysisUsage(AnalysisUsage &AU) const override; +}; +} +#endif//TSAR_CLANG_SPLIT_DECLS_H diff --git a/lib/Transform/Clang/CMakeLists.txt b/lib/Transform/Clang/CMakeLists.txt index d5eef742..9c672345 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 SplitDecls.cpp) if(MSVC_IDE) file(GLOB_RECURSE TRANSFORM_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/lib/Transform/Clang/Passes.cpp b/lib/Transform/Clang/Passes.cpp index 1414a706..79942c8d 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); + initializeClangSplitDeclsPassPass(Registry); } diff --git a/lib/Transform/Clang/SplitDecls.cpp b/lib/Transform/Clang/SplitDecls.cpp new file mode 100644 index 00000000..aa9a67f0 --- /dev/null +++ b/lib/Transform/Clang/SplitDecls.cpp @@ -0,0 +1,421 @@ +//===- SplitDecls.cpp - Source-level Splitting of Local Objects - *- 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. +// +//===----------------------------------------------------------------------===// +// +// The file declares a pass to perform splitting of objects in a specified scope. +// +//===----------------------------------------------------------------------===// + +#include "tsar/Transform/Clang/SplitDecls.h" +#include "tsar/Analysis/DFRegionInfo.h" +#include "tsar/Analysis/Clang/GlobalInfoExtractor.h" +#include "tsar/Analysis/Clang/NoMacroAssert.h" +#include "tsar/Core/Query.h" +#include "tsar/Frontend/Clang/Pragma.h" +#include "tsar/Frontend/Clang/TransformationContext.h" +#include "tsar/Support/Clang/Diagnostic.h" +#include "tsar/Support/Clang/Utils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace llvm; +using namespace clang; +using namespace tsar; + +#undef DEBUG_TYPE +#define DEBUG_TYPE "clang-split-decls" + +char ClangSplitDeclsPass::ID = 0; + +INITIALIZE_PASS_IN_GROUP_BEGIN(ClangSplitDeclsPass, "clang-split", + "Separation of variable declaration statements (Clang)", false, false, + tsar::TransformationQueryManager::getPassRegistry()) +INITIALIZE_PASS_DEPENDENCY(TransformationEnginePass) +INITIALIZE_PASS_DEPENDENCY(ClangGlobalInfoPass) +INITIALIZE_PASS_IN_GROUP_END(ClangSplitDeclsPass, "clang-split-decls", + "Separation of variable declaration statements (Clang)", false, false, + tsar::TransformationQueryManager::getPassRegistry()) + +void ClangSplitDeclsPass::getAnalysisUsage(AnalysisUsage & AU) const { + AU.addRequired(); + AU.addRequired(); + AU.setPreservesAll(); +} + +ModulePass * llvm::createClangSplitDeclsPass() { + return new ClangSplitDeclsPass(); +} + +namespace { +/// The visitor searches a pragma `split` and performs splitting for a scope +/// after it. It also checks absence a macros in this scope and print some +/// other warnings. +struct notSingleDecl { + bool IsNotSingleFlag = false; + int VarDeclsNum = 0; + bool IsFirstVar = true; + bool PointerFlag = false; + SourceLocation NotSingleDeclStart; + SourceLocation NotSingleDeclEnd; + std::deque Starts; + std::deque Ends; + std::deque Names; + std::string VarDeclType; +}; + +class ClangSplitter : public RecursiveASTVisitor { +public: + ClangSplitter(ClangTransformationContext &TfmCtx, + const ASTImportInfo &ImportInfo, + const GlobalInfoExtractor &GlobalInfo, + ClangGlobalInfo::RawInfo &RawInfo) + : mTfmCtx(&TfmCtx), mImportInfo(ImportInfo), mGlobalInfo(GlobalInfo), + mRawInfo(&RawInfo), mRewriter(TfmCtx.getRewriter()), + mContext(TfmCtx.getContext()), mSrcMgr(mRewriter.getSourceMgr()), + mLangOpts(mRewriter.getLangOpts()) {} + + bool TraverseStmt(Stmt *S) { // to traverse the parse tree and visit each statement + if (!S) { + return RecursiveASTVisitor::TraverseStmt(S); + } + Pragma P(*S); + if (findClause(P, ClauseId::SplitDeclaration, mClauses)) { + llvm::SmallVector ToRemove; + auto IsPossible = pragmaRangeToRemove(P, mClauses, mSrcMgr, mLangOpts, + mImportInfo, ToRemove); + if (!IsPossible.first) + if (IsPossible.second & PragmaFlags::IsInMacro) + toDiag(mSrcMgr.getDiagnostics(), mClauses.front()->getBeginLoc(), + tsar::diag::warn_remove_directive_in_macro); + else if (IsPossible.second & PragmaFlags::IsInHeader) + toDiag(mSrcMgr.getDiagnostics(), mClauses.front()->getBeginLoc(), + tsar::diag::warn_remove_directive_in_include); + else + toDiag(mSrcMgr.getDiagnostics(), mClauses.front()->getBeginLoc(), + tsar::diag::warn_remove_directive); + Rewriter::RewriteOptions RemoveEmptyLine; + /// TODO (kaniandr@gmail.com): it seems that RemoveLineIfEmpty is + /// set to true then removing (in RewriterBuffer) works incorrect. + RemoveEmptyLine.RemoveLineIfEmpty = false; + for (auto SR : ToRemove) + mRewriter.RemoveText(SR, RemoveEmptyLine); // delete each range + std::map::iterator it; + for (it = mGlobalVarDeclsMap.begin(); it != mGlobalVarDeclsMap.end(); it++) { + if (it->second.IsNotSingleFlag) { + SourceRange toInsert(it->second.NotSingleDeclStart, + it->second.NotSingleDeclEnd); + ExternalRewriter Canvas(toInsert, mSrcMgr, mLangOpts); + mRewriter.RemoveText(toInsert, RemoveEmptyLine); + } + } + if (mLocalVarDecls.IsNotSingleFlag) { + SourceRange toInsert(mLocalVarDecls.NotSingleDeclStart, + mLocalVarDecls.NotSingleDeclEnd); + mRewriter.RemoveText(toInsert, RemoveEmptyLine); + } + return true; + } + Rewriter::RewriteOptions RemoveEmptyLine; + RemoveEmptyLine.RemoveLineIfEmpty = false; + if (mLocalVarDecls.IsNotSingleFlag) { + SourceRange toInsert(mLocalVarDecls.NotSingleDeclStart, + mLocalVarDecls.NotSingleDeclEnd); + mRewriter.RemoveText(toInsert, RemoveEmptyLine); + while (mLocalVarDecls.Names.size()) { + if (mLocalVarDecls.IsFirstVar) { + mRewriter.InsertTextAfterToken(mLocalVarDecls.NotSingleDeclEnd, + mLocalVarDecls.Names.back()); + mLocalVarDecls.IsFirstVar = false; + } else { + mRewriter.InsertTextAfterToken(mLocalVarDecls.NotSingleDeclEnd, + mLocalVarDecls.VarDeclType + mLocalVarDecls.Names.back()); + } + mLocalVarDecls.Names.pop_back(); + } + } + + std::map::iterator it; + for (it = mGlobalVarDeclsMap.begin(); it != mGlobalVarDeclsMap.end(); it++) { + if (it->second.IsNotSingleFlag) { + SourceRange toInsert(it->second.NotSingleDeclStart, it->second.Ends.back()); + mRewriter.RemoveText(toInsert, RemoveEmptyLine); + while (it->second.Names.size()) { + if (it->second.IsFirstVar) { + mRewriter.InsertTextAfterToken(it->second.Ends.back(), + it->second.Names.back()); + it->second.IsFirstVar = false; + } else { + if (it->second.Names.size() == 1) { + mRewriter.InsertTextAfterToken(it->second.Ends.back(), + it->second.VarDeclType + it->second.Names.back()); + } else { + mRewriter.InsertTextAfterToken(it->second.Ends.back(), + it->second.VarDeclType + it->second.Names.back() + ";\n"); + } + } + it->second.Names.pop_back(); + } + } + } + return RecursiveASTVisitor::TraverseStmt(S); + } + + std::string getType(SourceLocation beginLoc, SourceLocation endLoc) { + SourceRange varDeclRange(beginLoc, endLoc); + std::string type = mRewriter.getRewrittenText(varDeclRange); + if (type.find("*") != -1) { + mLocalVarDecls.PointerFlag = true; + if (mGlobalVarDeclsMap.count(mCurrentSL)) { + mGlobalVarDeclsMap[mCurrentSL].PointerFlag = true; + } + } + return type; + } + + void findPointerToConst(QualType qualType, SourceLocation beginLoc) { + if (mLocalVarDecls.PointerFlag) { + if (qualType.isConstQualified()) { + mLocalVarDecls.IsNotSingleFlag = false; + if (mTypeLocs.empty() || mTypeLocs.front() != beginLoc) { + toDiag(mSrcMgr.getDiagnostics(), beginLoc, + tsar::diag::warn_pointers_to_constants_split_prevent); + mTypeLocs.push_front(beginLoc); + } + } + } + if (mGlobalVarDeclsMap.count(mCurrentSL) && mGlobalVarDeclsMap[mCurrentSL].PointerFlag) { + if (qualType.isConstQualified()) { + mGlobalVarDeclsMap[mCurrentSL].IsNotSingleFlag = false; + if (mTypeLocs.empty() || mTypeLocs.front() != beginLoc) { + toDiag(mSrcMgr.getDiagnostics(), beginLoc, + tsar::diag::warn_pointers_to_constants_split_prevent); + mTypeLocs.push_front(beginLoc); + } + } + } + } + + bool TraverseTypeLoc(TypeLoc Loc) { + findPointerToConst(Loc.getType(), Loc.getBeginLoc()); + if (mLocalVarDecls.IsNotSingleFlag && mLocalVarDecls.VarDeclsNum == 1) { + mLocalVarDecls.VarDeclType = getType(mLocalVarDecls.NotSingleDeclStart, Loc.getEndLoc()); + return RecursiveASTVisitor::TraverseTypeLoc(Loc); + } + if (mGlobalVarDeclsMap.count(mCurrentSL)) { + if (mGlobalVarDeclsMap[mCurrentSL].VarDeclsNum == 1) { + mGlobalVarDeclsMap[mCurrentSL].VarDeclType = getType(mCurrentSL, + Loc.getEndLoc()); + return RecursiveASTVisitor::TraverseTypeLoc(Loc); + } + } + return true; + } + + void ProcessLocalDeclaration(VarDecl *S, SourceRange toInsert) { + std::string txtStr; + ExternalRewriter Canvas(toInsert, mSrcMgr, mLangOpts); + SourceRange Range(S->getLocation()); + mLocalVarDecls.Starts.push_front(S->getBeginLoc()); + mLocalVarDecls.Ends.push_front(S->getEndLoc()); + SourceRange varDeclRange(S->getBeginLoc(), S->getEndLoc()); + if (mLocalVarDecls.VarDeclsNum == 1) { + mLocalVarDecls.IsFirstVar = true; + txtStr = Canvas.getRewrittenText(varDeclRange).str(); + } + if (mLocalVarDecls.VarDeclsNum > 1) { + SourceRange prevVarDeclRange(mLocalVarDecls.Starts.back(), + mLocalVarDecls.Ends.back()); + mLocalVarDecls.Starts.pop_back(); + mLocalVarDecls.Ends.pop_back(); + Canvas.ReplaceText(prevVarDeclRange, ""); + txtStr = Canvas.getRewrittenText(varDeclRange).str(); + auto it = std::remove(txtStr.begin(), txtStr.end(), ','); + txtStr.erase(it, txtStr.end()); + } + mLocalVarDecls.Names.push_front(txtStr + ";\n"); + } + + void ProcessGlobalDeclaration(VarDecl *S, SourceRange toInsert) { + std::string txtStr; + ExternalRewriter Canvas(toInsert, mSrcMgr, mLangOpts); + SourceRange Range(S->getLocation()); + mGlobalVarDeclsMap[S->getBeginLoc()].Starts.push_front(S->getBeginLoc()); + mGlobalVarDeclsMap[S->getBeginLoc()].Ends.push_front(S->getEndLoc()); + SourceRange varDeclRange(S->getBeginLoc(), S->getEndLoc()); + if (mGlobalVarDeclsMap[S->getBeginLoc()].VarDeclsNum == 1) { + mGlobalVarDeclsMap[S->getBeginLoc()].IsFirstVar = true; + txtStr = Canvas.getRewrittenText(varDeclRange).str(); + mGlobalVarDeclsMap[S->getBeginLoc()].NotSingleDeclStart = S->getBeginLoc(); + mGlobalVarDeclsMap[S->getBeginLoc()].Names.push_front(txtStr + ";\n"); + } + if (mGlobalVarDeclsMap[S->getBeginLoc()].VarDeclsNum > 1) { + SourceRange prevVarDeclRange(mGlobalVarDeclsMap[S->getBeginLoc()].Starts.back(), + mGlobalVarDeclsMap[S->getBeginLoc()].Ends.back()); + mGlobalVarDeclsMap[S->getBeginLoc()].Starts.pop_back(); + mGlobalVarDeclsMap[S->getBeginLoc()].Ends.pop_back(); + Canvas.ReplaceText(prevVarDeclRange, ""); + txtStr = Canvas.getRewrittenText(varDeclRange).str(); + + auto it = std::remove(txtStr.begin(), txtStr.end(), ','); + txtStr.erase(it, txtStr.end()); + + mGlobalVarDeclsMap[S->getBeginLoc()].Names.push_front(txtStr); + } + mGlobalVarDeclsMap[S->getBeginLoc()].NotSingleDeclEnd = S->getEndLoc(); + } + + bool VisitVarDecl(VarDecl *S) { // to traverse the parse tree and visit each statement + mCurrentSL = S->getBeginLoc(); + if (mGlobalInfo.findOutermostDecl(S)) { + if (mGlobalVarDeclsMap[S->getBeginLoc()].VarDeclsNum == 0) { + std::map::iterator it; + for (it = mGlobalVarDeclsMap.begin(); it != mGlobalVarDeclsMap.end(); it++) { + if (it->first != S->getBeginLoc()) { + // it->second.Starts.clear(); + // it->second.Ends.clear(); + // it->second.Names.clear(); + // mGlobalVarDeclsMap.erase(it->first); + } + } + } + mGlobalVarDeclsMap[S->getBeginLoc()].VarDeclsNum++; + if (mGlobalVarDeclsMap[S->getBeginLoc()].VarDeclsNum == 2) { + mGlobalVarDeclsMap[S->getBeginLoc()].IsNotSingleFlag = true; + } + SourceRange toInsert(S->getBeginLoc(), S->getEndLoc()); + ProcessGlobalDeclaration(S, toInsert); + } + if (mLocalVarDecls.IsNotSingleFlag) { + mLocalVarDecls.VarDeclsNum++; + SourceRange toInsert(mLocalVarDecls.NotSingleDeclStart, + mLocalVarDecls.NotSingleDeclEnd); + ProcessLocalDeclaration(S, toInsert); + } + return true; + } + + bool VisitDeclStmt(DeclStmt *S) { + if(!(S->isSingleDecl())) { + mLocalVarDecls.VarDeclsNum = 0; + mLocalVarDecls.Starts.clear(); + mLocalVarDecls.Ends.clear(); + mLocalVarDecls.Names.clear(); + mLocalVarDecls.IsNotSingleFlag = true; + mLocalVarDecls.NotSingleDeclStart = S->getBeginLoc(); + mLocalVarDecls.NotSingleDeclEnd = S->getEndLoc(); + } else { + mLocalVarDecls.IsNotSingleFlag = false; + } + return true; + } + + bool VisitParmVarDecl(ParmVarDecl *S) { + if (mLocalVarDecls.IsNotSingleFlag) { + toDiag(mSrcMgr.getDiagnostics(), S->getBeginLoc(), + tsar::diag::warn_parm_var_decl_split_prevent); + mLocalVarDecls.IsNotSingleFlag = false; + } + return true; + } + + bool VisitStmt(Stmt *S) { + if (!mClauses.empty()) { + mClauses.clear(); + } + return RecursiveASTVisitor::VisitStmt(S); + } + + bool VisitDecl(Decl * D) { + if (!mClauses.empty()) { + toDiag(mContext.getDiagnostics(), mClauses.front()->getBeginLoc(), + tsar::diag::warn_unexpected_directive); + mClauses.clear(); + } + return RecursiveASTVisitor::VisitDecl(D); + } + +private: + ClangTransformationContext *mTfmCtx; + const ASTImportInfo &mImportInfo; + const GlobalInfoExtractor &mGlobalInfo; + ClangGlobalInfo::RawInfo *mRawInfo; + Rewriter &mRewriter; + ASTContext &mContext; + SourceManager &mSrcMgr; + const LangOptions &mLangOpts; + SmallVector mClauses; + std::map mGlobalVarDeclsMap; + notSingleDecl mLocalVarDecls; + SourceLocation mCurrentSL; + std::deque mTypeLocs; +}; +} + +bool ClangSplitDeclsPass::runOnModule(llvm::Module &M) { + auto &TfmInfo{getAnalysis()}; + if (!TfmInfo) { + M.getContext().emitError("cannot transform sources" + ": transformation context is not available"); + return false; + } + ASTImportInfo ImportStub; + const auto *ImportInfo = &ImportStub; + if (auto *ImportPass = getAnalysisIfAvailable()) + ImportInfo = &ImportPass->getImportInfo(); + auto *CUs{M.getNamedMetadata("llvm.dbg.cu")}; + for (auto *MD : CUs->operands()) { + auto *CU{cast(MD)}; + auto *TfmCtx{ + dyn_cast_or_null(TfmInfo->getContext(*CU))}; + if (!TfmCtx || !TfmCtx->hasInstance()) { + M.getContext().emitError("cannot transform sources" + ": transformation context is not available"); + return false; + } + ASTImportInfo ImportStub; + const auto *ImportInfo = &ImportStub; + if (auto *ImportPass = getAnalysisIfAvailable()) + ImportInfo = &ImportPass->getImportInfo(); + auto &GIP = getAnalysis(); + ClangSplitter Vis(*TfmCtx, *ImportInfo, GIP.getGlobalInfo(TfmCtx)->GIE, + GIP.getGlobalInfo(TfmCtx)->RI); + Vis.TraverseDecl(TfmCtx->getContext().getTranslationUnitDecl()); + } + return false; +} \ No newline at end of file