diff --git a/CMakeLists.txt b/CMakeLists.txt index 5413d3f0..71dd7feb 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}) @@ -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() @@ -151,7 +158,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) @@ -205,7 +212,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}) @@ -226,7 +233,28 @@ 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 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 + MLIRInferIntRangeInterface MLIRIR MLIRSupport) + 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) @@ -235,7 +263,21 @@ 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(obj.FortranEvaluate SYSTEM PRIVATE ${LLVM_INCLUDE_DIRS}) + target_include_directories(flang-new SYSTEM PRIVATE ${LLVM_INCLUDE_DIRS}) endif() include_directories(${LLVM_INCLUDE_DIRS}) @@ -284,6 +326,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) 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/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/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/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/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/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/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/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/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/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/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; 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/include/tsar/Analysis/Memory/DIDependencyAnalysis.h b/include/tsar/Analysis/Memory/DIDependencyAnalysis.h index 47dbdc10..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,7 +163,7 @@ 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); @@ -164,6 +171,9 @@ class DIDependencyAnalysisPass : 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/Analysis/Memory/DIEstimateMemory.h b/include/tsar/Analysis/Memory/DIEstimateMemory.h index cbd8fe21..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. @@ -280,7 +285,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 +340,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/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/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/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()) 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/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(); diff --git a/include/tsar/Core/IRAction.h b/include/tsar/Core/IRAction.h new file mode 100644 index 00000000..f895044e --- /dev/null +++ b/include/tsar/Core/IRAction.h @@ -0,0 +1,48 @@ +//===--- 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. +// +//===----------------------------------------------------------------------===// + +#ifndef TSAR_IR_ACTION_H +#define TSAR_IR_ACTION_H + +#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); +} +#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/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/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 f8e67934..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,22 @@ // //===----------------------------------------------------------------------===// -#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(llvm::ArrayRef CL, QueryManager *QM, - bool LoadSources = true); + ClangMainAction(const clang::tooling::CompilationDatabase &Compilations, + QueryManager &QM) + : mTfmInfo(Compilations), mQueryManager(QM) {} /// Callback at the start of processing a single input. /// @@ -52,70 +50,34 @@ class MainAction : public clang::ASTFrontendAction { /// /// This is guaranteed to only be called following a successful call to /// 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; + bool shouldEraseOutputFiles() 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. 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/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/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 6d39e0a0..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,43 +39,111 @@ class DICompileUnit; } namespace tsar { +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; + 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, mAllSources) {} + static constexpr llvm::StringRef UnnamedProgramStub{""}; - 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!"); @@ -88,20 +158,20 @@ 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: - Fortran::parser::AllSources mAllSources; - Fortran::parser::Options mOptions; - Fortran::parser::Parsing mParsing{mAllSources}; - 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/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/Clang/Utils.h b/include/tsar/Support/Clang/Utils.h index 3fe62b18..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; @@ -103,7 +106,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, @@ -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/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/DiagnosticKinds.td b/include/tsar/Support/DiagnosticKinds.td index a6cda4cc..84768832 100644 --- a/include/tsar/Support/DiagnosticKinds.td +++ b/include/tsar/Support/DiagnosticKinds.td @@ -161,22 +161,25 @@ 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">; 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/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/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/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/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 6cde9a45..611690e0 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,13 +63,16 @@ 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)); 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); @@ -94,14 +98,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 +116,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 +125,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 +149,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,8 +157,32 @@ void for_each_user_insts(llvm::Instruction &I, FunctionT F) { } } while (!WorkList.empty()); } else - F(UI); + F(U); + } +} + +/// 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; } } diff --git a/include/tsar/Support/MetadataUtils.h b/include/tsar/Support/MetadataUtils.h index eceac8a4..388054ce 100644 --- a/include/tsar/Support/MetadataUtils.h +++ b/include/tsar/Support/MetadataUtils.h @@ -27,11 +27,16 @@ #include #include +#include #include #include #include #include +namespace llvm { +class DIBuilder; +} + namespace tsar { /// Returns a language for a specified function. inline llvm::Optional getLanguage(const llvm::Function &F) { @@ -95,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; } @@ -108,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()) { @@ -126,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); } @@ -160,17 +165,21 @@ 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. +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. /// /// 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/include/tsar/Support/OutputFile.h b/include/tsar/Support/OutputFile.h new file mode 100644 index 00000000..4a58f9c5 --- /dev/null +++ b/include/tsar/Support/OutputFile.h @@ -0,0 +1,103 @@ +//===----- 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.empty(); } + 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.front(); + } + + 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)) { + if (Temp) + mTemp.emplace_back(std::move(Temp.getValue())); + } + + bool mBinary{true}; + bool mRemoveFileOnSignal{true}; + bool mCreateMissingDirectories{true}; + llvm::SmallVector 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/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/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/include/tsar/Transform/IR/Passes.h b/include/tsar/Transform/IR/Passes.h index d4b4e8e4..e59e39c6 100644 --- a/include/tsar/Transform/IR/Passes.h +++ b/include/tsar/Transform/IR/Passes.h @@ -100,5 +100,11 @@ 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. +FunctionPass *createPointerScalarizerPass(); } #endif//TSAR_IR_TRANSFORM_PASSES_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/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/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/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!"); 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; diff --git a/lib/APC/LoopInfo.cpp b/lib/APC/LoopInfo.cpp index 48554a11..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)) { @@ -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; } } diff --git a/lib/APC/Parallelization.cpp b/lib/APC/Parallelization.cpp index f149079e..64218c11 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)); @@ -752,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()) { @@ -865,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())) @@ -876,7 +910,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 +1048,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 +1087,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; @@ -1180,7 +1224,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)}; @@ -1275,6 +1320,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 +1360,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{ 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/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/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 b283bea4..53e62d5b 100644 --- a/lib/Analysis/Clang/ExpressionMatcher.cpp +++ b/lib/Analysis/Clang/ExpressionMatcher.cpp @@ -52,12 +52,19 @@ STATISTIC(NumNonMatchASTExpr, "Number of non-matched AST expressions"); namespace { class MatchExprVisitor : - public MatchASTBase, + public ClangMatchASTBase, public RecursiveASTVisitor { + + enum AccessKind : uint8_t { + AK_Store, + AK_AddrOf, + AK_Other + }; + 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. @@ -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) { @@ -108,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; } @@ -128,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; } @@ -138,15 +145,16 @@ 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; } - 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; @@ -158,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)}) { @@ -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) || @@ -185,32 +193,35 @@ 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); } 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; @@ -222,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(), @@ -277,6 +287,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); } @@ -303,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/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 3a4f2835..e21dea32 100644 --- a/lib/Analysis/Clang/IncludeTree.cpp +++ b/lib/Analysis/Clang/IncludeTree.cpp @@ -78,12 +78,11 @@ 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()) { - 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; + while (Loc.first.isValid() && FileInfo.second) { + 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); } } @@ -199,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()); @@ -211,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()); @@ -223,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()); @@ -235,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/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 d2ce058f..9fdafcfd 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; @@ -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. 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/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..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"; } @@ -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 7924f71f..8e68dc84 100644 --- a/lib/Analysis/Memory/BitMemoryTrait.cpp +++ b/lib/Analysis/Memory/BitMemoryTrait.cpp @@ -23,12 +23,15 @@ //===----------------------------------------------------------------------===// #include "BitMemoryTrait.h" +#include 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 +84,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/DIAliasTreePrinter.cpp b/lib/Analysis/Memory/DIAliasTreePrinter.cpp index c79d2cbe..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 { @@ -92,11 +100,11 @@ struct DIEstimateMemoryPassGraphTraits { namespace { struct DIAliasTreePrinter : - public DOTGraphTraitsPrinter { static char ID; DIAliasTreePrinter() : - DOTGraphTraitsPrinter("em-di", ID) { initializeDIAliasTreePrinterPass(*PassRegistry::getPassRegistry()); } @@ -105,11 +113,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/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); diff --git a/lib/Analysis/Memory/DIDependenceAnalysis.cpp b/lib/Analysis/Memory/DIDependenceAnalysis.cpp index d9223ec8..a17ad95a 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" @@ -52,9 +53,10 @@ #include #include #include +#include #include #include -#include +#include #include #include @@ -70,6 +72,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 +82,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()) @@ -309,12 +313,20 @@ 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()) 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() && @@ -560,36 +572,83 @@ 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: + case RecurKind::FMulAdd: 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::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; } +bool handleLoopEmptyBindings( + const Loop *L, const DIMemoryTrait &DITraitItr, + const DIMemoryTraitRegionPool &Pool, + const SpanningTreeRelation &DIAliasSTR) { + auto *DIM = DITraitItr.getMemory(); + if (DIM->emptyBinding()) + return true; + if (!DITraitItr.is()) + return false; + for (auto &Ptr : *DIM) { + if (!Ptr.pointsToAliveValue()) + continue; + 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 *findInAliasTreeMapping(const Function *F, const 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 two operands!"); + 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. @@ -613,7 +672,9 @@ 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 = 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. @@ -642,7 +703,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; @@ -679,7 +740,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())) @@ -1355,35 +1417,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) { @@ -1392,9 +1452,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. @@ -1441,16 +1521,29 @@ 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 = - getRawDIMemoryIfExists(L->getHeader()->getContext(), Candidate); + auto MD{findInAliasTreeMapping(L->getHeader()->getParent(), Candidate)}; + bool IsFromMapping{MD != nullptr}; + 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() || @@ -1461,49 +1554,139 @@ 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() || !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; 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; @@ -1538,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"); @@ -1578,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, @@ -1613,14 +1800,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; @@ -1688,22 +1875,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; @@ -1749,7 +1946,7 @@ void DIDependencyAnalysisPass::analyzeNode(DIAliasMemoryNode &DIN, const SpanningTreeRelation &AliasSTR, const SpanningTreeRelation &DIAliasSTR, ArrayRef LockedTraits, const GlobalOptions &GlobalOpts, - DependenceSet &DepSet, DIDependenceSet &DIDepSet, + const Loop &L, DependenceSet &DepSet, DIDependenceSet &DIDepSet, DIMemoryTraitRegionPool &Pool) { assert(!DIN.empty() && "Alias node must contain memory locations!"); auto *AN = findBoundAliasNode(*mAT, AliasSTR, DIN); @@ -1763,6 +1960,30 @@ void DIDependencyAnalysisPass::analyzeNode(DIAliasMemoryNode &DIN, dbgs() << "\n"; }); auto DIMTraitItr = Pool.find_as(&M); + auto *F{L.getHeader()->getParent()}; + bool NoRedundantMapping{false}; + if (auto MD{F->getMetadata("alias.tree.mapping")}) { + 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; + } + } if (M.isOriginal() || M.emptyBinding() || ATraitItr == DepSet.end()) { if (DIMTraitItr == Pool.end()) continue; @@ -1780,13 +2001,13 @@ void DIDependencyAnalysisPass::analyzeNode(DIAliasMemoryNode &DIN, MustNoAccessValues.insert(Bind); if (IsChanged) { clarify(DIMTrait, *DIMTraitItr); - } else { + } 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 { + } else if (!NoRedundantMapping) { LLVM_DEBUG(dbgs() << "[DA DI]: use existing traits\n"); if ((!M.emptyBinding() && ATraitItr == DepSet.end()) || (M.emptyBinding() && M.isOriginal())) { @@ -1833,7 +2054,7 @@ void DIDependencyAnalysisPass::analyzeNode(DIAliasMemoryNode &DIN, } else { clarify(DIMTrait, *DIMTraitItr); } - } else { + } else if (!NoRedundantMapping) { if (DIMTraitItr == Pool.end()) continue; LLVM_DEBUG(dbgs() << "[DA DI]: use existing traits\n"); @@ -1990,15 +2211,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); @@ -2034,13 +2257,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); + LockedTraits, GlobalOpts, *L, DepSet, DIDepSet, *Pool); for (auto &DIM : cast(*DIN)) if (auto *DIEM = dyn_cast(&DIM)) if (DIEM->getExpression()->getNumElements() == 0) @@ -2332,7 +2555,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( @@ -2341,6 +2564,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(); @@ -2350,6 +2574,7 @@ void DIDependencyAnalysisPass::getAnalysisUsage(AnalysisUsage &AU) const { AU.addRequired(); AU.addRequired(); AU.addRequired(); + AU.addRequired(); AU.setPreservesAll(); } diff --git a/lib/Analysis/Memory/DIEstimateMemory.cpp b/lib/Analysis/Memory/DIEstimateMemory.cpp index e542142b..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); } } @@ -238,6 +266,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 +302,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 +1029,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 +1040,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 +1061,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 +1081,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 +1091,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( @@ -1485,7 +1522,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; @@ -1584,7 +1621,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 +1656,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 +1904,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 || @@ -1899,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); } @@ -2145,7 +2185,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 +2223,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 +2262,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 +2291,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/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/Analysis/Memory/DIMemoryLocation.cpp b/lib/Analysis/Memory/DIMemoryLocation.cpp index c82d30c1..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::unknown() : - 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::unknown(); + 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::unknown(); + 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/DefinedMemory.cpp b/lib/Analysis/Memory/DefinedMemory.cpp index c60b15db..510c352e 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)->getElementType(); - if (ArrayPtr->isAddressOfVariable()) { + auto ArrayType{getPointerElementType(*ArrayPtr->getBase())}; + if (!ArrayType || ArrayPtr->isAddressOfVariable()) { ResLoc.Kind = LocKind::NonCollapsable; return std::make_pair(ResLoc, true); } @@ -436,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::unknown(); - 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); @@ -611,6 +618,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; }; @@ -632,7 +642,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()); @@ -644,7 +657,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 +782,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..93d2715b 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 @@ -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]); } } } @@ -149,7 +151,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); } @@ -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!"); @@ -344,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); @@ -495,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()->getVariableLocation())); + 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"); @@ -558,7 +637,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 +657,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 " 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..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() @@ -83,12 +86,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 +110,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,14 +123,38 @@ 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 *PointeeTy{getPointerElementType(*Loc.Ptr)}; + auto Size = PointeeTy && PointeeTy->isSized() + ? LocationSize::precise( + 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(); @@ -176,26 +203,26 @@ 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 (;;) { - 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 = Loc.Size.mayBeBeforePointer() + ? LocationSize::beforeOrAfterPointer() + : LocationSize::afterPointer(); clarifyUnknownSize(DL, Loc); return true; } @@ -245,9 +272,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 +287,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 +301,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 +708,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 +845,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 +886,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 +967,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; @@ -995,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); } @@ -1087,12 +1115,11 @@ bool EstimateMemoryPass::runOnFunction(Function &F) { return; if (AccessedMemory.count(V)) return; - auto PointeeTy = cast(V->getType())->getElementType(); - 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::unknown())); + : LocationSize::beforeOrAfterPointer())); }; 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 620a5717..7a1c061a 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), 0); + if (BasePtr == Loc.Ptr) + return false; + return LS.getOut().overlap( + MemoryLocation::getAfter(BasePtr, 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)); } @@ -845,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); @@ -1005,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); @@ -1102,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; @@ -1437,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 << " "; } 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..ca32f3a9 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->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 c90401a2..6601fc2a 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); } @@ -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; } } @@ -495,13 +496,15 @@ 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->getElementType())) + 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()) { - 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 3fdfec4e..5b6ffe05 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) || @@ -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/Analysis/Reader/AnalysisReader.cpp b/lib/Analysis/Reader/AnalysisReader.cpp index 399d3214..181ca797 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; @@ -482,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(), '*', '^'); @@ -545,6 +548,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"); } diff --git a/lib/Core/CMakeLists.txt b/lib/Core/CMakeLists.txt index 26611993..f164478a 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} @@ -39,7 +40,7 @@ endif() if(NOT PACKAGE_LLVM) if(Flang_FOUND) add_dependencies(TSARTool TSARTransformFlang TSARFrontendFlang - TSARSupportFlang) + TSARAnalysisFlang TSARSupportFlang) endif() add_dependencies(TSARTool TSARTransformAST @@ -51,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/IRAction.cpp b/lib/Core/IRAction.cpp new file mode 100644 index 00000000..8132c57f --- /dev/null +++ b/lib/Core/IRAction.cpp @@ -0,0 +1,281 @@ +//===--- 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/Action.h" +# include "tsar/Frontend/Flang/Tooling.h" +# include "tsar/Frontend/Flang/TransformationContext.h" +#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(newClangActionFactory( + std::forward_as_tuple(Compilations, + static_cast(SQM))) + .get()); + }); + return mSockets.back()->getContext(); + } + +private: + std::vector> mSockets; +}; + +#ifdef FLANG_FOUND +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) { + 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 +} + +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/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/lib/Core/Query.cpp b/lib/Core/Query.cpp index 819ecd14..905c8bae 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 @@ -137,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()); @@ -180,6 +183,7 @@ void addAfterSROAAnalysis(const GlobalOptions &GO, const DataLayout &DL, Passes.add(createGlobalsAAWrapperPass()); Passes.add(createRPOFunctionAttrsAnalysis()); Passes.add(createPOFunctionAttrsAnalysis()); + Passes.add(createPointerScalarizerPass()); Passes.add(createMemoryMatcherPass()); Passes.add(createGlobalsAccessCollector()); Passes.add(createCallExtractorPass()); @@ -344,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); }, @@ -357,17 +363,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); } @@ -383,13 +401,14 @@ void InstrLLVMQueryManager::run(llvm::Module *M, } Passes.add(createUnreachableBlockEliminationPass()); Passes.add(createNoMetadataDSEPass()); + Passes.add(createFlangDIVariableRetrieverPass()); Passes.add(createDINodeRetrieverPass()); Passes.add(createMemoryMatcherPass()); Passes.add(createDILoopRetrieverPass()); 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 2baee7ce..15112adb 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" @@ -35,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 @@ -374,12 +380,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(); @@ -665,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); + 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 if (InputKind.getLanguage() == clang::Language::Unknown && + InputKind.getFormat() == InputKind::Precompiled) { + CSourcesToMerge.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) { @@ -701,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 " @@ -725,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) @@ -747,44 +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(mCommandLine, 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( - std::forward_as_tuple(mCommandLine, QM), - std::forward_as_tuple(SourcesToMerge, ImportInfoStorage)) + newClangActionFactory( + std::forward_as_tuple(*mCompilations, *QM), + 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()); - // 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(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 || FortranRes != 0 || LLRes != 0) + ? (CRes > 1 || FortranRes > 1 || LLRes > 1) ? 2 : 1 + : 0; } 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/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 { diff --git a/lib/Frontend/Clang/Action.cpp b/lib/Frontend/Clang/Action.cpp index 5bad0fdc..1a6fb108 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) @@ -197,305 +173,21 @@ class AnalysisConsumer : public ASTConsumer { }; } -bool MainAction::BeginSourceFileAction(CompilerInstance &CI) { - TimePassesIsEnabled = CI.getFrontendOpts().ShowTimers; - return mQueryManager->beginSourceFile(CI, getCurrentFile()); +bool ClangMainAction::BeginSourceFileAction(CompilerInstance &CI) { + TimePassesIsEnabled = CI.getCodeGenOpts().TimePasses; + return mQueryManager.beginSourceFile(CI.getDiagnostics(), getCurrentFile(), + CI.getFrontendOpts().OutputFile, + CI.getFileSystemOpts().WorkingDir); } -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); - 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.cooked()); - errs() << IRSource << " could not scan " << Path << '\n'; - return nullptr; - } - Parsing.Parse(outs()); - Parsing.ClearLog(); - Parsing.messages().Emit(errs(), Parsing.cooked()); - 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, - Parsing.cooked(), 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.getBuffer(FID, &Invalid); - if (Invalid) - return; - llvm::SMDiagnostic Err; - LLVMContext Ctx; - std::unique_ptr M = - parseIR(MainFile->getMemBufferRef(), 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(); +bool ClangMainAction::shouldEraseOutputFiles() { + mQueryManager.endSourceFile( + getCompilerInstance().getDiagnostics().hasErrorOccurred()); + return clang::ASTFrontendAction::shouldEraseOutputFiles(); } std::unique_ptr -MainAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { - return std::make_unique(CI, InFile, mTfmInfo.get(), - *mQueryManager); -} - -MainAction::MainAction(ArrayRef CL, QueryManager *QM, - bool LoadSources) - : mQueryManager(QM), - mTfmInfo(LoadSources ? new TransformationInfo(CL) : nullptr) { - assert(QM && "Query manager must not be null!"); +ClangMainAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + return std::make_unique(CI, InFile, mTfmInfo, + mQueryManager); } 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()); } diff --git a/lib/Frontend/Flang/Action.cpp b/lib/Frontend/Flang/Action.cpp new file mode 100644 index 00000000..8ce9f776 --- /dev/null +++ b/lib/Frontend/Flang/Action.cpp @@ -0,0 +1,441 @@ +//===- 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/ADT/DenseMapTraits.h" +#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 +#include +#include +#include +#include +#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(); +} + +namespace { +struct DINodeReplacer { + DINodeReplacer(DINode *From, DINode *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 (Op == From) + MD.replaceOperandWith(I, To); + if (auto *N = dyn_cast(Op)) { + visitMDNode(*N); + continue; + } + } + } + + SmallPtrSet MDNodes; + 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}; +}; +} + +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())}; + 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); + DINodeReplacer 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); + 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{ + 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..fab6a35b 100644 --- a/lib/Frontend/Flang/CMakeLists.txt +++ b/lib/Frontend/Flang/CMakeLists.txt @@ -1,9 +1,9 @@ -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} - ${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}) diff --git a/lib/Frontend/Flang/Tooling.cpp b/lib/Frontend/Flang/Tooling.cpp new file mode 100644 index 00000000..cfde981f --- /dev/null +++ b/lib/Frontend/Flang/Tooling.cpp @@ -0,0 +1,182 @@ +//===- 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) { + Flang->getInvocation() + .getFrontendOpts() + .needProvenanceRangeToCharBlockMappings = true; + 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 a9741667..01856e39 100644 --- a/lib/Frontend/Flang/TransformationContext.cpp +++ b/lib/Frontend/Flang/TransformationContext.cpp @@ -25,63 +25,169 @@ #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; namespace { -using NameHierarchyMapT = std::map, std::string>; -using MangledToSourceMapT = llvm::StringMap; +class ProgramUnitCollector { +public: + template bool Pre(T &N) { return true; } + template void Post(T &N) {} -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()) - 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()); - } + 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 IntrinsicMapT = llvm::StringMap>; +using MangledToSourceMapT = llvm::StringMap; + +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, MangledToSourceMapT &Map) { - if (auto *S{Parent.symbol()}) { - Names.push_back(S->name().ToString()); - 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); - for (auto &Child : Parent.children()) - match(Child, Names, NameHierarchy, Map); - Names.pop_back(); +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)}; + [[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 &&[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); - for (auto &Child : mContext.globalScope().children()) { - NameHierarchyMapT::key_type Names; - match(Child, Names, NameHierarchy, mGlobals); + ProgramUnitCollector V; + parser::Walk(mParsing->parseTree(), V); + 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); + } } - mRewriter = std::make_unique(mParsing.cooked()); - mParsing.cooked().CompileProvenanceRangeToOffsetMappings(); + match(mContext->globalScope(), V, Intrinsics, mGlobals); + mRewriter = std::make_unique(mParsing->cooked(), + mParsing->allCooked()); } std::pair FlangTransformationContext::release( @@ -103,13 +209,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)); @@ -118,6 +224,6 @@ std::pair FlangTransformationContext::release( MainFile = Name; } } - mContext.messages().Emit(errs(), mParsing.cooked()); + 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 26a6e63a..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 @@ -130,7 +132,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, @@ -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/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/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/Support/Utils.cpp b/lib/Support/Utils.cpp index 10b5561e..c5161219 100644 --- a/lib/Support/Utils.cpp +++ b/lib/Support/Utils.cpp @@ -25,7 +25,13 @@ #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 #include using namespace llvm; @@ -95,21 +101,214 @@ 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; } + +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 + /// 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")); +} + +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 (useTemporary()) + consumeError(mTemp.front().discard()); + if (!mFilename.empty()) + llvm::sys::fs::remove(mFilename); + mTemp.clear(); + return Error::success(); + } + if (!useTemporary()) + return Error::success(); + if (getTemporary().TmpName.empty()) { + consumeError(mTemp.front().discard()); + mTemp.clear(); + 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.front().keep(NewOutFile); + if (!E) { + mTemp.clear(); + return Error::success(); + } + llvm::sys::fs::remove(getTemporary().TmpName); + mTemp.clear(); + return std::move(E); +} } 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/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/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/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); diff --git a/lib/Transform/Clang/ExprPropagation.cpp b/lib/Transform/Clang/ExprPropagation.cpp index 4104cc54..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; @@ -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())); @@ -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()); @@ -837,24 +867,13 @@ bool ClangExprPropagation::unparseReplacement( else return false; } else if (auto *GEP = dyn_cast(&Def)) { - auto MD = - buildDIMemory(MemoryLocation(GEP), GEP->getContext(), *mDL, *mDT); - if (MD && MD->isValid() && !MD->Template) { - if (unparseToString(DWLang, *MD, DefStr, false)) { - auto NumberOfDims = 1 + dimensionsNum( - cast(GEP->getType())->getPointerElementType()); - 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; + auto MD = buildDIMemory(MemoryLocation::getAfter(GEP), GEP->getContext(), + *mDL, *mDT); + 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()) diff --git a/lib/Transform/Clang/LoopDistribution.cpp b/lib/Transform/Clang/LoopDistribution.cpp new file mode 100644 index 00000000..04517a6b --- /dev/null +++ b/lib/Transform/Clang/LoopDistribution.cpp @@ -0,0 +1,509 @@ +//=== 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/ExpressionMatcher.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/Analysis/Memory/Utils.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 +#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(ClangExprMatcherPass) +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 { + + typedef std::vector DependencyInstructionVector; + typedef std::set SplitInstructionVector; + +class ASTVisitor : public RecursiveASTVisitor { +public: + ASTVisitor(FunctionPass& Pass, Function& Function, + ClangTransformationContext &TransformationContext) { + 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!"); + mServerDIMemory = new DIMemoryClientServerInfo( + DIEMPass.getAliasTree(), Pass, Function); + mSpanningTreeRelation = new SpanningTreeRelation( + mServerDIMemory->DIAT); + mCanonicalLoop = &Pass.getAnalysis() + .getCanonicalLoopInfo(); + mExpressionMatcher = &Pass.getAnalysis().getMatcher(); + mLoopMatcher = &Pass.getAnalysis() + .getMatcher(); + mGlobalOptions = &Pass.getAnalysis() + .getOptions(); + mRewriter = &TransformationContext.getRewriter(); + mSourceManager = &mRewriter->getSourceMgr(); + mLangOptions = &mRewriter->getLangOpts(); + mASTContext = &TransformationContext.getContext(); + auto& SocketInfo = Pass.getAnalysis().get(); + auto& Socket = SocketInfo.getActive()->second; + auto RF = Socket.getAnalysis(Function); + assert(RF && "Dependence analysis must be available!"); + 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(); + mGetServerLoopIdFunction = [Matcher](ObjectID ID) { + auto ServerID = (*Matcher)->getMappedMD(ID); + return ServerID + ? cast(*ServerID) + : nullptr; + }; + mGetInstructionFunction = [Matcher](Instruction *I) { + auto Instr = (**Matcher)[I]; + return Instr + ? cast(Instr) + : nullptr; + }; + } + + [[maybe_unused]] + bool TraverseForStmt(ForStmt *ForStatement) { + DynTypedNode::create(*ForStatement).dump(dbgs(), *mASTContext); + const auto LoopMatch = mLoopMatcher->find(ForStatement); + if (LoopMatch == mLoopMatcher->end()) { + return false; + } + + auto *const Loop = LoopMatch->get(); + 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 splits:\n"; + for (const auto *Split : Splits) { + Split->dump(); + } + ); + + processSplits(ForStatement, Splits); + + // TODO: Use this information. + const auto PrevIsInsideLoop = mIsInsideLoop; + mIsInsideLoop = true; + const auto Result = 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) { + 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 None; + } + + return DependencyItr->get(); + } + + SplitInstructionVector getLoopSplits( + Loop *Loop, const DIDependenceSet &DIDependenceSet) { + DenseSet Coverage; + accessCoverage(DIDependenceSet, *mDIAliasTree, + Coverage, mGlobalOptions->IgnoreRedundantMemory); + DependencyInstructionVector DependencyReads, DependencyWrites; + for (auto &AliasTrait : DIDependenceSet) { + if (!Coverage.count(AliasTrait.getNode())) { + continue; + } + + 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->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 + } + ); + } + } + // Try to get write instructions after which should be split + return getLoopSplits(Loop, DependencyReads, DependencyWrites); + } + + //TODO: Remove last write if exists + SplitInstructionVector getLoopSplits( + Loop *Loop, const DependencyInstructionVector &Reads, + const DependencyInstructionVector &Writes) const { + const auto LoopDepth = Loop->getLoopDepth(); + SplitInstructionVector Splits(Writes.begin(), Writes.end()); + for (const auto &Write : Writes) { + for (const auto &Read : Reads) { + if (isSplittableDependence(Read, Write, LoopDepth)) { + 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); + } + } + } + } + return Splits; + } + + 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(ServerWrite, ServerRead, false); + if (!Dependence) { + return true; + } + + 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); + 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 SplitLocation = getSplitSourceLocation(Split); + if (SplitLocation.hasValue()) { + mRewriter->InsertTextAfterToken(SplitLocation.getValue(), + LoopHeaderSplitter.getValue()); + } + } + } + + [[nodiscard]] + Optional getLoopHeaderSplitter( + const ForStmt *ForStatement) const { + auto LoopHeader = getCharacterData( + ForStatement->getBeginLoc(), ForStatement->getBody()->getBeginLoc()); + if (!LoopHeader.hasValue()) { + return None; + } + + std::string LoopHeaderSplitter; + raw_string_ostream SplitterStream(LoopHeaderSplitter); + SplitterStream << "}"; + SplitterStream << LoopHeader.getValue(); + SplitterStream << "{"; + return SplitterStream.str(); + } + + [[nodiscard]] + Optional getCharacterData( + const SourceLocation BeginLoc, const SourceLocation EndLoc) const { + bool Invalid; + const auto BeginData = mSourceManager->getCharacterData(BeginLoc, &Invalid); + if (Invalid) { + return None; + } + + const auto EndData = mSourceManager->getCharacterData(EndLoc, &Invalid); + if (Invalid) { + return None; + } + + 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; + 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; + const SourceManager *mSourceManager; + const LangOptions *mLangOptions; + const ASTContext *mASTContext; + DIAliasTree *mDIAliasTree; + DIDependencInfo *mDIDependency; + DependenceInfo *mDependence; + std::function mGetServerLoopIdFunction; + std::function mGetInstructionFunction; + bool mIsInsideLoop = false; +}; +} + +bool LoopDistributionPass::runOnFunction(Function& Function) { + auto *DISub{findMetadata(&Function)}; + if (!DISub) { + return false; + } + + 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; + } + + auto *FunctionDecl = + TransformationContext->getDeclForMangledName(Function.getName()); + if (!FunctionDecl) { + return false; + } + + ASTVisitor LoopVisitor(*this, Function, *TransformationContext); + LoopVisitor.TraverseDecl(FunctionDecl); + 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.addRequired(); + AU.setPreservesAll(); +} + +FunctionPass * llvm::createLoopDistributionPass() { + return new LoopDistributionPass(); +} \ No newline at end of file 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/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()) { 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); } diff --git a/lib/Transform/Clang/StructureReplacement.cpp b/lib/Transform/Clang/StructureReplacement.cpp index b11f94b7..185ecf63 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,34 +76,49 @@ 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 /// 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>>; /// 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 { @@ -165,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; @@ -218,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(); @@ -225,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(); @@ -238,6 +271,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, @@ -261,8 +311,6 @@ bool checkMacroBoundsAndEmitDiag(T *Obj, SourceLocation FuncLoc, return true; } - - using CallList = std::vector; /// This class collects all 'replace' clauses in the code. @@ -390,6 +438,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 +483,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 +530,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 +626,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(), @@ -586,23 +650,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(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; } @@ -648,7 +714,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,15 +849,42 @@ class ReplacementSanitizer : public RecursiveASTVisitor { ReplacementItr->get()); } } else { - Res &= !TraverseStmt(ArgExpr); + Res &= TraverseStmt(ArgExpr); } } 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 (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)); + } + return true; + } + 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 +893,84 @@ 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 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; + 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 || + Op->getOpcode() == UnaryOperatorKind::UO_AddrOf)) + 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) { + bool IsMacroArg{isImmediateMacroArg(Expr->getBeginLoc(), mSrcMgr) && + isImmediateMacroArg(Expr->getEndLoc(), mSrcMgr)}; 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); + if (!IsMacroArg && !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()); + if (auto *Subscript{ + dyn_cast(*Expr->child_begin())}) { + 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 { - auto Itr = addToReplacement(Expr->getMemberDecl(), - ReplacementItr->get()); - Itr->Ranges.emplace_back(Expr->getSourceRange()); - Itr->InAssignment |= CurrInAssignment; + auto Range(Expr->getSourceRange()); + if (IsMacroArg) + Range = getSpellingRange(mSrcMgr, Range).getAsRange(); + Itr->RangesToReplace.emplace_back(std::move(Range)); } + if (CurrInAssignment) + Itr->Flags |= Replacement::InAssignment; } } mIsInnermostMember = false; + mLastDeclRef = nullptr; return Res; } @@ -852,8 +992,10 @@ class ReplacementSanitizer : public RecursiveASTVisitor { FunctionInfo &mReplacements; ReplacementMap &mReplacementInfo; + Stmt *mParent = nullptr; + Stmt *mScope = nullptr; bool mIsInnermostMember = false; - DeclRefExpr *mLastDeclRef; + DeclRefExpr *mLastDeclRef = nullptr; bool mInAssignment = true; }; @@ -917,6 +1059,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 += "="; @@ -925,6 +1069,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 += "="; @@ -948,7 +1094,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(); @@ -962,7 +1108,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 += '('; @@ -972,11 +1125,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 { @@ -986,17 +1139,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; } } @@ -1005,6 +1161,7 @@ void replaceCall(FunctionInfo &FI, const CallExpr &Expr, Rewriter.ReplaceText( getExpansionRange(SrcMgr, Expr.getSourceRange()).getAsRange(), NewCallExpr); + return true; } #ifdef LLVM_DEBUG @@ -1040,7 +1197,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"; } } @@ -1056,8 +1223,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"; @@ -1108,8 +1277,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) { @@ -1130,8 +1300,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; @@ -1197,19 +1368,27 @@ 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. 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); } } @@ -1233,18 +1412,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; @@ -1398,10 +1583,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; } } @@ -1412,14 +1597,19 @@ void ClangStructureReplacementPass::sanitizeCandidates(FunctionInfo &FuncInfo) { Verifier.TraverseDecl(FuncInfo.Definition); for (auto &C : FuncInfo.Candidates) for (auto &R : C.get()) - if (isa(R.Member->getType())) - R.InAssignment = false; - else - R.InAssignment |= FuncInfo.Strict; + 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; } -void ClangStructureReplacementPass::fillImplicitReplacementMembers( +void ClangStructureReplacementPass::fillReplacementMembersFromCallees( scc_iterator &SCC) { + auto &SrcMgr{mTfmCtx->getContext().getSourceManager()}; bool IsChanged = false; do { IsChanged = false; @@ -1427,6 +1617,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 && @@ -1451,12 +1664,24 @@ void ClangStructureReplacementPass::fillImplicitReplacementMembers( return R.Member == CalleeR.Member; }); if (CallerRItr != Itr->get().end()) { - if (!CallerRItr->InAssignment && CalleeR.InAssignment) - CallerRItr->InAssignment = IsChanged = true; + 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().InAssignment = - CalleeR.InAssignment; + Itr->get().back().Flags = CalleeR.Flags; + if (isa(Itr->get()) && + isa(getCanonicalUnqualifiedType( + cast(Itr->get())))) + Itr->get().back().Flags &= + ~Replacement::InAssignment; IsChanged = true; } } @@ -1467,8 +1692,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; @@ -1476,6 +1702,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(); @@ -1486,9 +1743,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->template 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); @@ -1518,11 +1812,102 @@ 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; } +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}; +} -void ClangStructureReplacementPass::buildParameters(FunctionInfo &FuncInfo) { +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. @@ -1566,52 +1951,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 = R.InAssignment - ? R.Member->getType().getAsString() + "*" - : R.Member->getType().getAsString(); - 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(); @@ -1624,14 +1987,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); @@ -1640,10 +2018,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)) @@ -1660,12 +2038,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. @@ -1673,18 +2052,61 @@ 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.Ranges) { - SmallString<64> Tmp; - auto AccessString = R.InAssignment - ? ("(*" + 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); } } } @@ -1698,13 +2120,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; } } } @@ -1801,8 +2228,24 @@ bool ClangStructureReplacementPass::runOnModule(llvm::Module &M) { sanitizeCandidates(*FuncInfo); } } - fillImplicitReplacementMembers(SCC); - buildParameters(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); + }); + } + } + buildDeclarations(SCC); sanitizeCandidatesInCalls(SCC); if (!insertNewFunctions(SCC)) return false; 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/ConstantReplacement.cpp b/lib/Transform/Flang/ConstantReplacement.cpp index ee8d532f..77f33f11 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) { @@ -203,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()}; @@ -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/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..4f12c453 --- /dev/null +++ b/lib/Transform/Flang/VariableRegistration.cpp @@ -0,0 +1,190 @@ +//===- 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/Intrinsics.h" +#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 || ASTSub.isDeclaration()) + 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 " + getName(IntrinsicId::ast_reg_var) + "(" + + S->name().ToString() + ")") + .str(); + 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(); +} 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/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; 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; 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..a3bee968 --- /dev/null +++ b/lib/Transform/IR/PointerScalarizer.cpp @@ -0,0 +1,415 @@ +//=== 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/ADT/SpanningTreeRelation.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 +#include "tsar/Support/IRUtils.h" +#include "tsar/Transform/IR/InterprocAttr.h" +#include +#include +#include +#include + +#undef DEBUG_TYPE +#define DEBUG_TYPE "ptr2reg" + +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, Loop *L, DIBuilder *DIB) + : V(V), L(L), DIB(DIB), PhiLinks{} { + for (auto *BB : L->getBlocks()) + PhiLinks.insert({BB, PhiNodeLink()}); + } + + Value *V{nullptr}; + DILocalVariable *DbgVar{nullptr}; + DILocation *DbgLoc{nullptr}; + Loop *L{nullptr}; + LoadInst *InsertedLoad{nullptr}; + DenseMap PhiLinks; + DenseSet UniqueNodes; + DenseMap LastValues; + DIBuilder *DIB{nullptr}; +}; +} + +char PointerScalarizerPass::ID = 0; + +INITIALIZE_PASS_BEGIN(PointerScalarizerPass, "ptr2reg", + "Promote Pointer To Register", false, false) +INITIALIZE_PASS_DEPENDENCY(DIMemoryTraitPoolWrapper); +INITIALIZE_PASS_DEPENDENCY(LoopAttributesDeductionPass); +INITIALIZE_PASS_DEPENDENCY(EstimateMemoryPass); +INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass); +INITIALIZE_PASS_DEPENDENCY(DIEstimateMemoryPass); +INITIALIZE_PASS_END(PointerScalarizerPass, "ptr2reg", + "Promote Pointer To Register", false, false) + +FunctionPass * llvm::createPointerScalarizerPass() { + return new PointerScalarizerPass(); +} + +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 PreheaderBB{Ctx.L->getLoopPreheader()}; + auto *BeforeInstr{new LoadInst(getPointerElementType(*Ctx.V), + Ctx.V, "load." + Ctx.V->getName(), + &PreheaderBB->back())}; + Ctx.InsertedLoad = BeforeInstr; + 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) { + auto SI{new StoreInst(Ctx.LastValues[BB], Ctx.V, BB->getFirstNonPHI())}; + insertDbgValueCall(Ctx, SI->getValueOperand(), SI, false); + } +} + +static void handleMemoryAccess(BasicBlock *BB, ScalarizerContext &Ctx) { + SmallVector Loads; + DenseMap Stores; + SmallVector ToDelete; + Value *LastValue{Ctx.PhiLinks[BB].getPhi()}; + if (cast(LastValue)->getParent() != BB) + LastValue = Ctx.LastValues[BB->getSinglePredecessor()]; + for (auto &Instr : BB->getInstList()) { + if (auto *SI{dyn_cast(&Instr)}; + SI && SI->getPointerOperand() == Ctx.V) { + ToDelete.push_back(SI); + 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) { + I->dropAllReferences(); + I->eraseFromParent(); + } + Ctx.LastValues[BB] = LastValue; +} + +static void handleLoads(ScalarizerContext &Ctx, BasicBlock *BB, + SmallPtrSetImpl &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) { + if (Ctx.L->contains(BB) && Ctx.PhiLinks[BB].hasValue()) + return; + 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 { + NeedsCreate = true; + } + if (NeedsCreate) { + 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); +} + +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 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 HasAccess = false; + auto *EMNode = EM->getAliasNode(AT); + 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 && "Memory location must not be null!"); + auto *InstNode = InstEM->getAliasNode(AT); + 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 || 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 = [&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.isUnreachable(InstEM, EMNode)) + HasAccess = true; + }; + for_each_memory(Inst, TLI, memLambda, unknownMemLambda); + if (HasAccess) + return true; + } + } + return !HasWrite; +} + +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(); + 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. + // 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 PoolItr{TraitPool.find(L->getLoopID())}; + if (PoolItr == TraitPool.end()) + return; + SmallPtrSet Disabled; + SmallDenseSet Values; + 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) || + !any_of_user_insts(*V, [L](User *U) { + return isa(U) && L->contains(cast(U)); + })) + continue; + if (Disabled.contains(V) || SingleV || !getPointerElementType(*V) || + (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->getVariableLocationOp(0) == 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 &&[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(); + } + 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); + } + Ctx.DbgVar = NewVar; + Ctx.DbgLoc = DILocation::get(F.getContext(), NewVar->getLine(), 0, + Ctx.DbgVar->getScope()); + insertLoadInstructions(Ctx); + insertPhiNodes(Ctx, L->getHeader()); + SmallPtrSet Processed; + handleLoads(Ctx, L->getLoopPreheader(), Processed, true); + fillPhiNodes(Ctx); + deleteRedundantPhiNodes(Ctx); + insertStoreInstructions(Ctx); + } + }); + if (!MDsToAttach.empty()) { + auto *MappingNode = DINode::get(F.getContext(), MDsToAttach); + F.setMetadata("alias.tree.mapping", MappingNode); + } + return false; +} 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/DINodeRetriever.cpp b/lib/Transform/Mixed/DINodeRetriever.cpp index 9664246d..3cb1bfa7 100644 --- a/lib/Transform/Mixed/DINodeRetriever.cpp +++ b/lib/Transform/Mixed/DINodeRetriever.cpp @@ -26,18 +26,19 @@ #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 #include #include +#include #include #include #include #include #include #include -#include using namespace clang; using namespace llvm; @@ -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)) @@ -92,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; @@ -104,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->getAlign().value(), nullptr); DIB.insertDeclare(AI, DIVar, DIExpression::get(Ctx, {}), DILocation::get(AI->getContext(), 0, 0, DISub), AI->getParent()); } @@ -155,8 +142,8 @@ 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(), - nullptr, nullptr, 0); + GlobalVar.hasLocalLinkage(), !GlobalVar.isDeclaration(), + nullptr, nullptr, 0, nullptr); auto *GVE = DIGlobalVariableExpression::get(Ctx, GV, DIExpression::get(Ctx, {})); GlobalVar.setMetadata("sapfor.dbg", GVE); diff --git a/lib/Transform/Mixed/DIVariableRetriever.cpp b/lib/Transform/Mixed/DIVariableRetriever.cpp new file mode 100644 index 00000000..58a4256e --- /dev/null +++ b/lib/Transform/Mixed/DIVariableRetriever.cpp @@ -0,0 +1,336 @@ +//=== 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 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/DummyScopeAAPass.cpp b/lib/Transform/Mixed/DummyScopeAAPass.cpp index 81e7355b..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}); @@ -162,7 +163,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 +173,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/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/Instrumentation.cpp b/lib/Transform/Mixed/Instrumentation.cpp index 1f256096..e09526b0 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()); @@ -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->getElementType())) || + ((PointeeTy && isa(PointeeTy)) || (isa(BasePtr) && cast(BasePtr)->isArrayAllocation())) ? new BitCastInst(BasePtr, Type::getInt8PtrTy(Ctx), @@ -1018,7 +1019,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/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); } diff --git a/lib/Unparse/DIUnparser.cpp b/lib/Unparse/DIUnparser.cpp index 89d514e4..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; @@ -169,7 +171,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 +219,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/lib/Unparse/Utils.cpp b/lib/Unparse/Utils.cpp index 8ad13410..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 << ">"; @@ -161,8 +173,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"; @@ -179,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 << ">"; 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 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..219fc2b9 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, @@ -1629,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 { @@ -1695,10 +1697,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 +1772,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!");