diff --git a/clang/include/clang/DPCT/DPCTOptions.inc b/clang/include/clang/DPCT/DPCTOptions.inc index dec4cb7dad84..dce9e683083a 100644 --- a/clang/include/clang/DPCT/DPCTOptions.inc +++ b/clang/include/clang/DPCT/DPCTOptions.inc @@ -460,4 +460,9 @@ DPCT_ENUM_OPTION( llvm::cl::CommaSeparated, llvm::cl::value_desc("value"), llvm::cl::cat(DPCTCat), llvm::cl::ZeroOrMore) +DPCT_NON_ENUM_OPTION( + DPCT_OPT_TYPE(static llvm::cl::opt), AnalysisMode, "analysis-mode", + llvm::cl::desc("Only generate report for porting effort. Default: off."), + llvm::cl::cat(DPCTCat), llvm::cl::init(false)) + #endif // !DPCT_OPTIONS_IN_CLANG_DPCT diff --git a/clang/lib/DPCT/ASTTraversal.cpp b/clang/lib/DPCT/ASTTraversal.cpp index c0b3bef09503..a55b34636e12 100644 --- a/clang/lib/DPCT/ASTTraversal.cpp +++ b/clang/lib/DPCT/ASTTraversal.cpp @@ -12352,6 +12352,7 @@ void RecognizeAPINameRule::processFuncCall(const CallExpr *CE) { return; } + recordRecognizedAPI(CE); auto *NSD = dyn_cast(ND->getDeclContext()); Namespace = getNameSpace(NSD); APIName = CE->getCalleeDecl()->getAsFunction()->getNameAsString(); @@ -12395,6 +12396,7 @@ void RecognizeTypeRule::registerMatcher(ast_matchers::MatchFinder &MF) { auto TypeTable = MigrationStatistics::GetTypeTable(); std::vector UnsupportedType; std::vector UnsupportedPointerType; + std::vector AllTypes; for (auto &Type : TypeTable) { if (!Type.second) { if (Type.first.find("*") != std::string::npos) { @@ -12404,6 +12406,9 @@ void RecognizeTypeRule::registerMatcher(ast_matchers::MatchFinder &MF) { UnsupportedType.push_back(Type.first); } } + if (DpctGlobalInfo::isAnalysisModeEnabled()) { + AllTypes.push_back(Type.first); + } } MF.addMatcher( typeLoc( @@ -12413,31 +12418,42 @@ void RecognizeTypeRule::registerMatcher(ast_matchers::MatchFinder &MF) { loc(pointerType(pointee(qualType(hasDeclaration(namedDecl( internal::Matcher(new internal::HasNameMatcher( UnsupportedPointerType)))))))))) - .bind("typeloc"), + .bind("unsupportedtypeloc"), this); + + if (DpctGlobalInfo::isAnalysisModeEnabled()) { + MF.addMatcher(typeLoc(loc(qualType(hasDeclaration( + namedDecl(internal::Matcher( + new internal::HasNameMatcher(AllTypes))))))) + .bind("alltypeloc"), + this); + } } void RecognizeTypeRule::runRule( const ast_matchers::MatchFinder::MatchResult &Result) { - const TypeLoc *TL = getNodeAsType(Result, "typeloc"); - if (!TL) - return; - auto &Context = DpctGlobalInfo::getContext(); - QualType QTy = TL->getType(); - if (QTy.isCanonical()) - return; - std::string TypeName = + if (const TypeLoc* TL = getNodeAsType(Result, "unsupportedtypeloc")) { + auto& Context = DpctGlobalInfo::getContext(); + QualType QTy = TL->getType(); + if (QTy.isCanonical()) + return; + std::string TypeName = DpctGlobalInfo::getTypeName(QTy.getUnqualifiedType(), Context); - // process pointer type - if (!QTy->isTypedefNameType() && QTy->isPointerType()) { - std::string PointeeTy = DpctGlobalInfo::getTypeName( + // process pointer type + if (!QTy->isTypedefNameType() && QTy->isPointerType()) { + std::string PointeeTy = DpctGlobalInfo::getTypeName( QTy->getPointeeType().getUnqualifiedType(), Context); + report(TL->getBeginLoc(), Diagnostics::KNOWN_UNSUPPORTED_TYPE, false, + PointeeTy + " *"); + return; + } report(TL->getBeginLoc(), Diagnostics::KNOWN_UNSUPPORTED_TYPE, false, - PointeeTy + " *"); + TypeName); return; } - report(TL->getBeginLoc(), Diagnostics::KNOWN_UNSUPPORTED_TYPE, false, - TypeName); + if (const TypeLoc *TL = getNodeAsType(Result, "alltypeloc")) { + recordRecognizedType(*TL); + } } REGISTER_RULE(RecognizeTypeRule, PassKind::PK_Migration) diff --git a/clang/lib/DPCT/AnalysisInfo.cpp b/clang/lib/DPCT/AnalysisInfo.cpp index 0a5ed8262b3c..83adab13a445 100644 --- a/clang/lib/DPCT/AnalysisInfo.cpp +++ b/clang/lib/DPCT/AnalysisInfo.cpp @@ -151,6 +151,7 @@ unsigned DpctGlobalInfo::ExtensionDEFlag = static_cast(-1); unsigned DpctGlobalInfo::ExtensionDDFlag = 0; unsigned DpctGlobalInfo::ExperimentalFlag = 0; unsigned DpctGlobalInfo::HelperFuncPreferenceFlag = 0; +bool DpctGlobalInfo::AnalysisModeFlag = false; unsigned int DpctGlobalInfo::ColorOption = 1; std::unordered_map> DpctGlobalInfo::CubPlaceholderIndexMap; diff --git a/clang/lib/DPCT/AnalysisInfo.h b/clang/lib/DPCT/AnalysisInfo.h index 5302c9fc5d57..34fa30788c9c 100644 --- a/clang/lib/DPCT/AnalysisInfo.h +++ b/clang/lib/DPCT/AnalysisInfo.h @@ -986,6 +986,8 @@ class DpctGlobalInfo { static unsigned getHelperFuncPreferenceFlag() { return HelperFuncPreferenceFlag; } + static bool isAnalysisModeEnabled() { return AnalysisModeFlag; } + static void enableAnalysisMode() { AnalysisModeFlag = true; } inline static format::FormatRange getFormatRange() { return FmtRng; } inline static void setFormatRange(format::FormatRange FR) { FmtRng = FR; } @@ -2135,6 +2137,7 @@ class DpctGlobalInfo { static unsigned ExtensionDDFlag; static unsigned ExperimentalFlag; static unsigned HelperFuncPreferenceFlag; + static bool AnalysisModeFlag; static unsigned int ColorOption; static std::unordered_map> CubPlaceholderIndexMap; diff --git a/clang/lib/DPCT/DPCT.cpp b/clang/lib/DPCT/DPCT.cpp index e6c3e9e67a44..ae2055ab266f 100644 --- a/clang/lib/DPCT/DPCT.cpp +++ b/clang/lib/DPCT/DPCT.cpp @@ -606,6 +606,10 @@ int runDPCT(int argc, const char **argv) { clang::tooling::SetDiagnosticOutput(DpctTerm()); } + if (AnalysisMode) { + DpctGlobalInfo::enableAnalysisMode(); + SuppressWarningsAllFlag = true; + } initWarningIDs(); DpctInstallPath = getInstallPath(argv[0]); @@ -1278,6 +1282,11 @@ int runDPCT(int argc, const char **argv) { } } + if (DpctGlobalInfo::isAnalysisModeEnabled()) { + dumpAnalysisModeStatics(llvm::outs()); + return MigrationSucceeded; + } + // if run was successful int Status = saveNewFiles(Tool, InRoot, OutRoot); ShowStatus(Status); diff --git a/clang/lib/DPCT/Diagnostics.cpp b/clang/lib/DPCT/Diagnostics.cpp index 860a5b933b4f..e82043f3b8f4 100644 --- a/clang/lib/DPCT/Diagnostics.cpp +++ b/clang/lib/DPCT/Diagnostics.cpp @@ -34,14 +34,17 @@ bool checkDuplicated(const std::string &FileAndLine, std::unordered_map DiagnosticIDTable; std::unordered_map CommentIDTable; +#define HIGH_LEVEL EffortLevel::EL_High +#define MEDIUM_LEVEL EffortLevel::EL_Medium +#define LOW_LEVEL EffortLevel::EL_Low -#define DEF_WARNING(NAME, ID, LEVEL, MSG) \ +#define DEF_WARNING(NAME, ID, LEVEL, MSG) \ DiagnosticsMessage wg_##NAME(DiagnosticIDTable, ID, \ - clang::DiagnosticIDs::Warning, MSG); + clang::DiagnosticIDs::Warning, LEVEL, MSG); -#define DEF_COMMENT(NAME, ID, LEVEL, MSG) \ +#define DEF_COMMENT(NAME, ID, LEVEL, MSG) \ DiagnosticsMessage cg_##NAME(CommentIDTable, ID, clang::DiagnosticIDs::Note, \ - MSG); + LEVEL, MSG); #include "Diagnostics.inc" @@ -52,7 +55,8 @@ std::unordered_set APIQueryNeedReportWarningIDSet = {1086}; std::unordered_map MsgIDTable; #define DEF_COMMENT(NAME, ID, MSG) \ - DiagnosticsMessage cg_##NAME(MsgIDTable, ID, clang::DiagnosticIDs::Note, MSG); + DiagnosticsMessage cg_##NAME(MsgIDTable, ID, clang::DiagnosticIDs::Note, \ + EffortLevel::EL_Low, MSG); #include "DiagnosticsBuildScript.inc" #undef DEF_COMMENT diff --git a/clang/lib/DPCT/Diagnostics.h b/clang/lib/DPCT/Diagnostics.h index 588978165c0e..8e546133a3e9 100644 --- a/clang/lib/DPCT/Diagnostics.h +++ b/clang/lib/DPCT/Diagnostics.h @@ -43,6 +43,7 @@ extern std::set WarningIDs; struct DiagnosticsMessage { int ID; int Category; + EffortLevel EL; const char *Msg; #define DEF_WARNING(NAME, ID, LEVEL, MSG) ID, @@ -58,8 +59,8 @@ struct DiagnosticsMessage { #undef DEF_COMMENT DiagnosticsMessage() = default; DiagnosticsMessage(std::unordered_map &Table, int ID, - int Category, const char *Msg) - : ID(ID), Category(Category), Msg(Msg) { + int Category, EffortLevel EL, const char *Msg) + : ID(ID), Category(Category), EL(EL), Msg(Msg) { assert(Table.find(ID) == Table.end() && "[DPCT Internal error] Two " "messages with the same ID " "are being registered"); @@ -317,12 +318,17 @@ inline bool report(SourceLocation SL, IDTy MsgID, if (checkDuplicated(FileAndLine, WarningIDAndMsg)) return false; + auto Diag = DiagnosticIDTable.find((int)MsgID); + if (Diag == DiagnosticIDTable.end()) + return true; + if (DpctGlobalInfo::isAnalysisModeEnabled()) { + recordAnalysisModeEffort(SL, Diag->second.EL); + return true; + } if (!SuppressWarningsAllFlag) { // Only report warnings that are not suppressed - if (WarningIDs.find((int)MsgID) == WarningIDs.end() && - DiagnosticIDTable.find((int)MsgID) != DiagnosticIDTable.end()) { - reportWarning(SL, DiagnosticIDTable[(int)MsgID], SM.getDiagnostics(), - Vals...); + if (WarningIDs.find((int)MsgID) == WarningIDs.end()) { + reportWarning(SL, Diag->second, SM.getDiagnostics(), Vals...); } } if (TS && CommentIDTable.find((int)MsgID) != CommentIDTable.end()) { @@ -402,12 +408,17 @@ bool report(const clang::tooling::UnifiedPath &FileAbsPath, unsigned int Offset, unsigned int ColNum = Offset - Fileinfo->getLineInfo(LineNum).Offset + 1; SourceLocation SL = SM.translateLineCol(FID, LineNum, ColNum); + auto Diag = DiagnosticIDTable.find((int)MsgID); + if (Diag == DiagnosticIDTable.end()) + return true; + if (DpctGlobalInfo::isAnalysisModeEnabled()) { + recordAnalysisModeEffort(FileAbsPath, Offset, Diag->second.EL); + return true; + } if (!SuppressWarningsAllFlag) { // Only report warnings that are not suppressed - if (WarningIDs.find((int)MsgID) == WarningIDs.end() && - DiagnosticIDTable.find((int)MsgID) != DiagnosticIDTable.end()) { - reportWarning(SL, DiagnosticIDTable[(int)MsgID], SM.getDiagnostics(), - Vals...); + if (WarningIDs.find((int)MsgID) == WarningIDs.end()) { + reportWarning(SL, Diag->second, SM.getDiagnostics(), Vals...); } } diff --git a/clang/lib/DPCT/MigrationAction.cpp b/clang/lib/DPCT/MigrationAction.cpp index 571abd508b3d..3ae1735336ac 100644 --- a/clang/lib/DPCT/MigrationAction.cpp +++ b/clang/lib/DPCT/MigrationAction.cpp @@ -174,6 +174,7 @@ std::shared_ptr DpctToolAction::createTranslationUnitInfo( std::shared_ptr DpctToolAction::createTranslationUnitInfoImpl( std::shared_ptr Invocation, bool &Success) { + Invocation->getDiagnosticOpts().IgnoreWarnings = true; auto DiagConsumer = new TextDiagnosticPrinter( DiagnosticStream, &Invocation->getDiagnosticOpts()); auto Info = std::make_shared(); diff --git a/clang/lib/DPCT/Statics.cpp b/clang/lib/DPCT/Statics.cpp index 76c01a3cfb60..d8bcb3491ae6 100644 --- a/clang/lib/DPCT/Statics.cpp +++ b/clang/lib/DPCT/Statics.cpp @@ -182,5 +182,190 @@ void PrintMsg(const std::string &Msg, bool IsPrintOnNormal) { assert(0); } +enum { + LowEffort = static_cast(EffortLevel::EL_Low), + MediumEffort = static_cast(EffortLevel::EL_Medium), + HighEffort = static_cast(EffortLevel::EL_High), + EffortNum = static_cast(EffortLevel::EL_NUM), + NoEffort = EffortNum, + CounterNum, +}; + +class LineStream { + const static StringRef NL; + llvm::raw_ostream& OS; + +public: + LineStream(llvm::raw_ostream &OS, unsigned Indent) + : OS(OS) { + OS.indent(Indent); + } + ~LineStream() { OS << NL; } + + template LineStream &operator<<(T &&Input) { + OS << std::forward(Input); + return *this; + } +}; +const StringRef LineStream::NL = getNL(); + +struct AnalysisModeSummary { + static const unsigned IndentIncremental = 2; + static const unsigned NumberWidth = 3; + + StringRef Name; + unsigned Total = 0; + unsigned Counter[CounterNum] = { 0 }; + + AnalysisModeSummary(StringRef Name) : Name(Name) {} + + AnalysisModeSummary &operator+=(const AnalysisModeSummary &Other) { + Total += Other.Total; + Counter[HighEffort] += Other.Counter[HighEffort]; + Counter[MediumEffort] += Other.Counter[MediumEffort]; + Counter[LowEffort] += Other.Counter[LowEffort]; + Counter[NoEffort] += Other.Counter[NoEffort]; + return *this; + } + + void dump(llvm::raw_ostream &OS, unsigned Indent) const { + LineStream(OS, Indent) << llvm::raw_ostream::Colors::BLUE << Name << ':' + << llvm::raw_ostream::Colors::RESET; + Indent += IndentIncremental; + printClassify(OS, Indent, "will be automatically migrated", NoEffort, + LowEffort, MediumEffort); + printClassify(OS, Indent, "will not be automatically migrated", HighEffort); + } + +private: + void printClassifyMsg(llvm::raw_ostream &OS, unsigned Indent, + StringRef ClassifyMsg, unsigned ClassifyNum) const { + LineStream(OS, Indent) << '+' + << llvm::format_decimal(ClassifyNum, NumberWidth) + << " lines of code (" + << llvm::format_decimal((ClassifyNum * 100) / Total, + 3) + << "%) " << ClassifyMsg << '.'; + } + template + unsigned sum(unsigned FirstLevel, LevelTys... RestLevels) const { + return Counter[FirstLevel] + sum(RestLevels...); + } + unsigned sum(unsigned Level) const { return Counter[Level]; } + + template + void printClassify(llvm::raw_ostream &OS, unsigned Indent, + StringRef ClassifyMsg, LevelTys... Levels) const { + printClassifyMsg(OS, Indent, ClassifyMsg, sum(Levels...)); + printLevel(OS, Indent + IndentIncremental, Levels...); + } + template + void printLevel(llvm::raw_ostream &OS, unsigned Indent, unsigned FirstLevel, + LevelTys... RestLevels) const { + printLevel(OS, Indent, FirstLevel); + printLevel(OS, Indent, RestLevels...); + } + void printLevel(llvm::raw_ostream &OS, unsigned Indent, + unsigned Level) const { + const static std::string PostMsgs[] = { + "High manual effort for code fixing", + "Medium manual effort for code fixing", + "Low manual effort for checking and code fixing", "No manual effort"}; + LineStream(OS, Indent) << '-' + << llvm::format_decimal(Counter[Level], NumberWidth) + << " APIs/Types - " << PostMsgs[Level] << '.'; + } +}; + +class AnalysisModeStats { + static const std::string LastMsg; + static llvm::StringMap AnalysisModeStaticsMap; + + struct EffortLevelWrap { + unsigned EL; + EffortLevelWrap() : EL(NoEffort) {} + EffortLevelWrap &operator=(EffortLevel Other) { + if (auto O = static_cast(Other); EL > O) + EL = O; + return *this; + } + + operator unsigned() const { return EL; } + }; + + std::map FileEffortsMap; + + AnalysisModeSummary getSummary(StringRef Name) const { + AnalysisModeSummary Summary(Name); + for (auto Entry : FileEffortsMap) { + ++Summary.Counter[Entry.second]; + ++Summary.Total; + } + return Summary; + } + + void recordEffort(unsigned Offset, EffortLevel Level) { + FileEffortsMap[Offset] = Level; + } + void recordApisOrTypes(unsigned Offset) { (void)FileEffortsMap[Offset]; } + +public: + static void dump(llvm::raw_ostream &OS) { + static const unsigned Indent = 0; + AnalysisModeSummary Total("Total Project"); + for (const auto &Entry : AnalysisModeStaticsMap) { + auto Summary = Entry.second.getSummary(Entry.first()); + Summary.dump(OS, Indent); + Total += Summary; + } + Total.dump(OS, Indent); + LineStream(OS, Indent) << LastMsg; + } + + static void recordApisOrTypes(SourceLocation SL) { + auto LocInfo = DpctGlobalInfo::getLocInfo(SL); + AnalysisModeStaticsMap[LocInfo.first.getPath()].recordApisOrTypes( + LocInfo.second); + } + static void recordEffort(SourceLocation SL, EffortLevel EL) { + auto LocInfo = DpctGlobalInfo::getLocInfo(SL); + recordEffort(LocInfo.first, LocInfo.second, EL); + } + static void recordEffort(const tooling::UnifiedPath &Filename, + unsigned Offset, EffortLevel EL) { + AnalysisModeStaticsMap[Filename.getPath()].recordEffort(Offset, EL); + } +}; + +const std::string AnalysisModeStats::LastMsg = + "See " + "https://www.intel.com/content/www/us/en/docs/dpcpp-compatibility-tool/" + "developer-guide-reference/current/overview.html for more details."; +llvm::StringMap AnalysisModeStats::AnalysisModeStaticsMap; + +void dumpAnalysisModeStatics(llvm::raw_ostream &OS) { + if (!DpctGlobalInfo::isAnalysisModeEnabled()) + return; + + AnalysisModeStats::dump(OS); +} + +void recordAnalysisModeEffort(SourceLocation SL, EffortLevel EL) { + AnalysisModeStats::recordEffort(SL, EL); +} +void recordAnalysisModeEffort(const clang::tooling::UnifiedPath &Filename, + unsigned Offset, EffortLevel EL) { + AnalysisModeStats::recordEffort(Filename, Offset, EL); +} + +void recordRecognizedAPI(const CallExpr *CE) { + if (DpctGlobalInfo::isAnalysisModeEnabled()) + AnalysisModeStats::recordApisOrTypes(CE->getBeginLoc()); +} +void recordRecognizedType(TypeLoc TL) { + if (DpctGlobalInfo::isAnalysisModeEnabled()) + AnalysisModeStats::recordApisOrTypes(TL.getBeginLoc()); +} + } // namespace dpct } // namespace clang diff --git a/clang/lib/DPCT/Statics.h b/clang/lib/DPCT/Statics.h index 07958e843a74..6127566e684d 100644 --- a/clang/lib/DPCT/Statics.h +++ b/clang/lib/DPCT/Statics.h @@ -60,7 +60,19 @@ enum VerboseLevel { VL_VerboseHigh = 2, }; +enum class EffortLevel : unsigned { EL_High = 0, EL_Medium, EL_Low, EL_NUM }; + void PrintMsg(const std::string &Msg, bool IsPrintOnNormal = true); + +void dumpAnalysisModeStatics(llvm::raw_ostream& OS); + +void recordAnalysisModeEffort(SourceLocation SL, EffortLevel EL); +void recordAnalysisModeEffort(const clang::tooling::UnifiedPath &Filename, unsigned Offset, + EffortLevel EL); + +void recordRecognizedAPI(const CallExpr *CE); +void recordRecognizedType(TypeLoc TL); + } // namespace dpct } // namespace clang #endif // DPCT_DEBUG_H diff --git a/clang/test/dpct/autocomplete.c b/clang/test/dpct/autocomplete.c index 87bcf3cd0d2e..4f9a1a02ce41 100644 --- a/clang/test/dpct/autocomplete.c +++ b/clang/test/dpct/autocomplete.c @@ -8,6 +8,7 @@ // Notice: When modify DASH prefix check, need modify behavior_tests/src/bt-autocomplete/do_test.py // in SYCLomatic-test repo too. // DASH: --always-use-async-handler +// DASH-NEXT: --analysis-mode // DASH-NEXT: --analysis-scope-path // DASH-NEXT: --assume-nd-range-dim= // DASH-NEXT: --build-script-file