martong created this revision. martong added reviewers: steakhal, NoQ. Herald added subscribers: manas, ASDenysPetrov, gamesh411, dkrupp, donat.nagy, Szelethus, mikhail.ramalho, a.sidorin, rnkovacs, szepet, baloghadamsoftware, xazax.hun. Herald added a reviewer: Szelethus. Herald added a project: All. martong requested review of this revision. Herald added a project: clang. Herald added a subscriber: cfe-commits.
This new CTU implementation is the natural extension of the normal single TU analysis. The approach consists of two analysis phases. During the first phase, we do a normal single TU analysis. During this phase, if we find a foreign function (that could be inlined from another TU) then we don’t inline that immediately, we rather mark that to be analysed later. When the first phase is finished then we start the second phase, the CTU phase. In this phase, we continue the analysis from those points (exploded nodes) which had been enqueued during the first phase. We gradually extend the exploded graph of the single TU analysis with new nodes that are created by the inlining of foreign functions. We count the number of analysis steps of the first phase and we limit the second (ctu) phase with this number. Discussion: https://discourse.llvm.org/t/rfc-much-faster-cross-translation-unit-ctu-analysis-implementation/61728 Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D123773 Files: clang/include/clang/CrossTU/CrossTranslationUnit.h clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h clang/lib/CrossTU/CrossTranslationUnit.cpp clang/lib/StaticAnalyzer/Core/CallEvent.cpp clang/lib/StaticAnalyzer/Core/CoreEngine.cpp clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp clang/test/Analysis/Inputs/ctu-onego-existingdef-other.cpp clang/test/Analysis/Inputs/ctu-onego-existingdef-other.cpp.externalDefMap.ast-dump.txt clang/test/Analysis/Inputs/ctu-onego-indirect-other.cpp clang/test/Analysis/Inputs/ctu-onego-indirect-other.cpp.externalDefMap.ast-dump.txt clang/test/Analysis/Inputs/ctu-onego-toplevel-other.cpp clang/test/Analysis/Inputs/ctu-onego-toplevel-other.cpp.externalDefMap.ast-dump.txt clang/test/Analysis/analyzer-config.c clang/test/Analysis/ctu-implicit.c clang/test/Analysis/ctu-main.c clang/test/Analysis/ctu-main.cpp clang/test/Analysis/ctu-on-demand-parsing.c clang/test/Analysis/ctu-on-demand-parsing.cpp clang/test/Analysis/ctu-onego-existingdef.cpp clang/test/Analysis/ctu-onego-indirect.cpp clang/test/Analysis/ctu-onego-toplevel.cpp
Index: clang/test/Analysis/ctu-onego-toplevel.cpp =================================================================== --- /dev/null +++ clang/test/Analysis/ctu-onego-toplevel.cpp @@ -0,0 +1,52 @@ +// RUN: rm -rf %t && mkdir %t +// RUN: mkdir -p %t/ctudir +// RUN: %clang_cc1 -std=c++14 -triple x86_64-pc-linux-gnu \ +// RUN: -emit-pch -o %t/ctudir/ctu-onego-toplevel-other.cpp.ast %S/Inputs/ctu-onego-toplevel-other.cpp +// RUN: cp %S/Inputs/ctu-onego-toplevel-other.cpp.externalDefMap.ast-dump.txt %t/ctudir/externalDefMap.txt + +// RUN: %clang_analyze_cc1 -std=c++14 -triple x86_64-pc-linux-gnu \ +// RUN: -analyzer-checker=core,debug.ExprInspection \ +// RUN: -analyzer-config eagerly-assume=false \ +// RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \ +// RUN: -analyzer-config ctu-dir=%t/ctudir \ +// RUN: -verify=ctu %s + +// RUN: %clang_analyze_cc1 -std=c++14 -triple x86_64-pc-linux-gnu \ +// RUN: -analyzer-checker=core,debug.ExprInspection \ +// RUN: -analyzer-config eagerly-assume=false \ +// RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \ +// RUN: -analyzer-config ctu-dir=%t/ctudir \ +// RUN: -analyzer-config display-ctu-progress=true \ +// RUN: -analyzer-display-progress \ +// RUN: -verify=ctu %s 2>&1 | FileCheck %s +// +// CallGraph: c->b +// topological sort: c, b +// Note that `other` calls into `b` but that is not visible in the CallGraph +// b/c that happens in another TU. + +// During the onego CTU analysis, we start with c() as top level function. +// Then we visit b() as non-toplevel during the processing of the FWList, thus +// that would not be visited as toplevel without special care. + +// `c` is analyzed as toplevel and during that the other TU is loaded: +// CHECK: ANALYZE (Path, Inline_Regular): {{.*}} c(int){{.*}}CTU loaded AST file +// next, `b` is analyzed as toplevel: +// CHECK: ANALYZE (Path, Inline_Regular): {{.*}} b(int) + +void b(int x); +void other(int y); +void c(int y) { + other(y); + return; + // The below call is here to form the proper CallGraph, but will not be + // analyzed. + b(1); +} + +void b(int x) { + if (x == 0) + (void)(1 / x); + // ctu-warning@-1{{Division by zero}} + // We receive the above warning only if `b` is analyzed as top-level. +} Index: clang/test/Analysis/ctu-onego-indirect.cpp =================================================================== --- /dev/null +++ clang/test/Analysis/ctu-onego-indirect.cpp @@ -0,0 +1,56 @@ +// RUN: rm -rf %t && mkdir %t +// RUN: mkdir -p %t/ctudir +// RUN: %clang_cc1 -std=c++14 -triple x86_64-pc-linux-gnu \ +// RUN: -emit-pch -o %t/ctudir/ctu-onego-indirect-other.cpp.ast %S/Inputs/ctu-onego-indirect-other.cpp +// RUN: cp %S/Inputs/ctu-onego-indirect-other.cpp.externalDefMap.ast-dump.txt %t/ctudir/externalDefMap.txt + +int bar(); + +// Here we have a foreign function `bar` that is imported when we analyze +// `adirectbaruser`. During the subsequent toplevel analysis of `baruser` we +// should bifurcate on the call of `bar`. + +//Ensure the order of the toplevel analyzed functions. +// RUN: %clang_analyze_cc1 -std=c++14 -triple x86_64-pc-linux-gnu \ +// RUN: -analyzer-checker=core,debug.ExprInspection \ +// RUN: -analyzer-config eagerly-assume=false \ +// RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \ +// RUN: -analyzer-config ctu-dir=%t/ctudir \ +// RUN: -analyzer-display-progress \ +// RUN: -analyzer-inlining-mode=all \ +// RUN: -analyzer-config ctu-max-nodes-mul=100 \ +// RUN: -analyzer-config ctu-max-nodes-min=1000 2>&1 %s | FileCheck %s +// CHECK: ANALYZE (Path, Inline_Regular):{{.*}}adirectbaruser(int) +// CHECK: ANALYZE (Path, Inline_Regular):{{.*}}baruser(int) + +// RUN: %clang_analyze_cc1 -std=c++14 -triple x86_64-pc-linux-gnu \ +// RUN: -analyzer-checker=core,debug.ExprInspection \ +// RUN: -analyzer-config eagerly-assume=false \ +// RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \ +// RUN: -analyzer-config ctu-dir=%t/ctudir \ +// RUN: -analyzer-display-progress \ +// RUN: -analyzer-inlining-mode=all \ +// RUN: -verify %s \ +// RUN: -analyzer-config ctu-max-nodes-mul=100 \ +// RUN: -analyzer-config ctu-max-nodes-min=1000 + + +void other(); // Defined in the other TU. + +void clang_analyzer_eval(int); + +void baruser(int x) { + if (x == 1) + return; + int y = bar(); + clang_analyzer_eval(y == 0); // expected-warning{{TRUE}} + // expected-warning@-1{{UNKNOWN}} + other(); +} + +void adirectbaruser(int) { + int y = bar(); + (void)y; + baruser(1); +} + Index: clang/test/Analysis/ctu-onego-existingdef.cpp =================================================================== --- /dev/null +++ clang/test/Analysis/ctu-onego-existingdef.cpp @@ -0,0 +1,56 @@ +// RUN: %clang_analyze_cc1 -std=c++14 -triple x86_64-pc-linux-gnu \ +// RUN: -analyzer-checker=core,debug.ExprInspection \ +// RUN: -analyzer-config eagerly-assume=false \ +// RUN: -analyze-function='baruser(int)' -x c++ \ +// RUN: -verify=nonctu %s + +// RUN: rm -rf %t && mkdir %t +// RUN: mkdir -p %t/ctudir +// RUN: %clang_cc1 -std=c++14 -triple x86_64-pc-linux-gnu \ +// RUN: -emit-pch -o %t/ctudir/ctu-onego-existingdef-other.cpp.ast %S/Inputs/ctu-onego-existingdef-other.cpp +// RUN: cp %S/Inputs/ctu-onego-existingdef-other.cpp.externalDefMap.ast-dump.txt %t/ctudir/externalDefMap.txt + +// Existing and equal function definition in both TU. `other` calls `bar` thus +// `bar` will be indirectly imported. During the import we recognize that there +// is an existing definition in the main TU, so we don't create a new Decl. +// Thus, ctu should not bifurcate on the call of `bar` it should directly +// inlinie that as in the case of nonctu. +// Note, we would not get a warning below, if `bar` is conservatively evaluated. +int bar() { + return 0; +} + +//Here we completely supress the CTU work list execution. We should not +//bifurcate on the call of `bar`. (We do not load the foreign AST at all.) +// RUN: %clang_analyze_cc1 -std=c++14 -triple x86_64-pc-linux-gnu \ +// RUN: -analyzer-checker=core,debug.ExprInspection \ +// RUN: -analyzer-config eagerly-assume=false \ +// RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \ +// RUN: -analyzer-config ctu-dir=%t/ctudir \ +// RUN: -verify=stu %s \ +// RUN: -analyze-function='baruser(int)' -x c++ \ +// RUN: -analyzer-config ctu-max-nodes-mul=0 \ +// RUN: -analyzer-config ctu-max-nodes-min=0 + +//Here we enable the CTU work list execution. We should not bifurcate on the +//call of `bar`. +// RUN: %clang_analyze_cc1 -std=c++14 -triple x86_64-pc-linux-gnu \ +// RUN: -analyzer-checker=core,debug.ExprInspection \ +// RUN: -analyzer-config eagerly-assume=false \ +// RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \ +// RUN: -analyzer-config ctu-dir=%t/ctudir \ +// RUN: -verify=ctu %s \ +// RUN: -analyze-function='baruser(int)' -x c++ \ +// RUN: -analyzer-config ctu-max-nodes-mul=100 \ +// RUN: -analyzer-config ctu-max-nodes-min=1000 + +void other(); // Defined in the other TU. + +void baruser(int) { + other(); + int x = bar(); + (void)(1 / x); + // ctu-warning@-1{{Division by zero}} + // stu-warning@-2{{Division by zero}} + // nonctu-warning@-3{{Division by zero}} +} Index: clang/test/Analysis/ctu-on-demand-parsing.cpp =================================================================== --- clang/test/Analysis/ctu-on-demand-parsing.cpp +++ clang/test/Analysis/ctu-on-demand-parsing.cpp @@ -15,6 +15,7 @@ // // RUN: cd "%t" && %clang_analyze_cc1 \ // RUN: -analyzer-checker=core,debug.ExprInspection \ +// RUN: -analyzer-config eagerly-assume=false \ // RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \ // RUN: -analyzer-config ctu-dir=. \ // RUN: -analyzer-config ctu-invocation-list=invocations.yaml \ @@ -82,30 +83,46 @@ void test_virtual_functions(mycls *obj) { // The dynamic type is known. clang_analyzer_eval(mycls().fvcl(1) == 8); // expected-warning{{TRUE}} + // expected-warning@-1{{UNKNOWN}} stu clang_analyzer_eval(derived().fvcl(1) == 9); // expected-warning{{TRUE}} + // expected-warning@-1{{UNKNOWN}} stu // We cannot decide about the dynamic type. - clang_analyzer_eval(obj->fvcl(1) == 8); // expected-warning{{FALSE}} expected-warning{{TRUE}} - clang_analyzer_eval(obj->fvcl(1) == 9); // expected-warning{{FALSE}} expected-warning{{TRUE}} + clang_analyzer_eval(obj->fvcl(1) == 8); // expected-warning{{TRUE}} ctu + // expected-warning@-1{{UNKNOWN}} ctu, stu } int main() { - clang_analyzer_eval(f(3) == 2); // expected-warning{{TRUE}} - clang_analyzer_eval(f(4) == 3); // expected-warning{{TRUE}} - clang_analyzer_eval(f(5) == 3); // expected-warning{{FALSE}} - clang_analyzer_eval(g(4) == 6); // expected-warning{{TRUE}} - clang_analyzer_eval(h(2) == 8); // expected-warning{{TRUE}} - - clang_analyzer_eval(myns::fns(2) == 9); // expected-warning{{TRUE}} - clang_analyzer_eval(myns::embed_ns::fens(2) == -1); // expected-warning{{TRUE}} - clang_analyzer_eval(mycls().fcl(1) == 6); // expected-warning{{TRUE}} - clang_analyzer_eval(mycls::fscl(1) == 7); // expected-warning{{TRUE}} - clang_analyzer_eval(myns::embed_cls().fecl(1) == -6); // expected-warning{{TRUE}} - clang_analyzer_eval(mycls::embed_cls2().fecl2(0) == -11); // expected-warning{{TRUE}} - - clang_analyzer_eval(chns::chf1(4) == 12); // expected-warning{{TRUE}} - clang_analyzer_eval(fun_using_anon_struct(8) == 8); // expected-warning{{TRUE}} + clang_analyzer_eval(f(3) == 2); // expected-warning{{TRUE}} ctu + // expected-warning@-1{{UNKNOWN}} stu + clang_analyzer_eval(f(4) == 3); // expected-warning{{TRUE}} ctu + // expected-warning@-1{{UNKNOWN}} stu + clang_analyzer_eval(f(5) == 3); // expected-warning{{FALSE}} ctu + // expected-warning@-1{{UNKNOWN}} stu + clang_analyzer_eval(g(4) == 6); // expected-warning{{TRUE}} ctu + // expected-warning@-1{{UNKNOWN}} stu + clang_analyzer_eval(h(2) == 8); // expected-warning{{TRUE}} ctu + // expected-warning@-1{{UNKNOWN}} stu + + clang_analyzer_eval(myns::fns(2) == 9); // expected-warning{{TRUE}} ctu + // expected-warning@-1{{UNKNOWN}} stu + clang_analyzer_eval(myns::embed_ns::fens(2) == -1); // expected-warning{{TRUE}} ctu + // expected-warning@-1{{UNKNOWN}} stu + clang_analyzer_eval(mycls().fcl(1) == 6); // expected-warning{{TRUE}} ctu + // expected-warning@-1{{UNKNOWN}} stu + clang_analyzer_eval(mycls::fscl(1) == 7); // expected-warning{{TRUE}} ctu + // expected-warning@-1{{UNKNOWN}} stu + clang_analyzer_eval(myns::embed_cls().fecl(1) == -6); // expected-warning{{TRUE}} ctu + // expected-warning@-1{{UNKNOWN}} stu + clang_analyzer_eval(mycls::embed_cls2().fecl2(0) == -11); // expected-warning{{TRUE}} ctu + // expected-warning@-1{{UNKNOWN}} stu + + clang_analyzer_eval(chns::chf1(4) == 12); // expected-warning{{TRUE}} ctu + // expected-warning@-1{{UNKNOWN}} stu + clang_analyzer_eval(fun_using_anon_struct(8) == 8); // expected-warning{{TRUE}} ctu + // expected-warning@-1{{UNKNOWN}} stu clang_analyzer_eval(other_macro_diag(1) == 1); // expected-warning{{TRUE}} + // expected-warning@-1{{UNKNOWN}} stu // expected-warning@Inputs/ctu-other.cpp:93{{REACHABLE}} MACRODIAG(); // expected-warning{{REACHABLE}} } Index: clang/test/Analysis/ctu-on-demand-parsing.c =================================================================== --- clang/test/Analysis/ctu-on-demand-parsing.c +++ clang/test/Analysis/ctu-on-demand-parsing.c @@ -13,6 +13,7 @@ // // RUN: cd "%t" && %clang_cc1 -fsyntax-only -std=c89 -analyze \ // RUN: -analyzer-checker=core,debug.ExprInspection \ +// RUN: -analyzer-config eagerly-assume=false \ // RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \ // RUN: -analyzer-config ctu-dir=. \ // RUN: -analyzer-config ctu-invocation-list=invocations.yaml \ @@ -32,6 +33,7 @@ int f(int); void testGlobalVariable() { clang_analyzer_eval(f(5) == 1); // expected-warning{{TRUE}} + // expected-warning@-1{{UNKNOWN}} stu } // Test enums. @@ -42,6 +44,7 @@ void testEnum() { clang_analyzer_eval(x == 0); // expected-warning{{TRUE}} clang_analyzer_eval(enumCheck() == 42); // expected-warning{{TRUE}} + // expected-warning@-1{{UNKNOWN}} stu } // Test that asm import does not fail. @@ -61,6 +64,7 @@ void testImplicit() { int res = identImplicit(6); // external implicit functions are not inlined clang_analyzer_eval(res == 6); // expected-warning{{TRUE}} + // expected-warning@-1{{UNKNOWN}} stu // Call something with uninitialized from the same function in which the // implicit was called. This is necessary to reproduce a special bug in // NoStoreFuncVisitor. @@ -79,5 +83,6 @@ struct DataType d; d.a = 1; d.b = 0; - clang_analyzer_eval(structInProto(&d) == 0); // expected-warning{{TRUE}} expected-warning{{FALSE}} + // Not imported, thus remains unknown both in stu and ctu. + clang_analyzer_eval(structInProto(&d) == 0); // expected-warning{{UNKNOWN}} } Index: clang/test/Analysis/ctu-main.cpp =================================================================== --- clang/test/Analysis/ctu-main.cpp +++ clang/test/Analysis/ctu-main.cpp @@ -7,6 +7,7 @@ // RUN: cp %S/Inputs/ctu-other.cpp.externalDefMap.ast-dump.txt %t/ctudir/externalDefMap.txt // RUN: %clang_analyze_cc1 -std=c++14 -triple x86_64-pc-linux-gnu \ // RUN: -analyzer-checker=core,debug.ExprInspection \ +// RUN: -analyzer-config eagerly-assume=false \ // RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \ // RUN: -analyzer-config ctu-dir=%t/ctudir \ // RUN: -verify %s @@ -113,10 +114,13 @@ void test_virtual_functions(mycls* obj) { // The dynamic type is known. - clang_analyzer_eval(mycls().fvcl(1) == 8); // expected-warning{{TRUE}} - clang_analyzer_eval(derived().fvcl(1) == 9); // expected-warning{{TRUE}} + clang_analyzer_eval(mycls().fvcl(1) == 8); // expected-warning{{TRUE}} ctu + // expected-warning@-1{{UNKNOWN}} stu + clang_analyzer_eval(derived().fvcl(1) == 9); // expected-warning{{TRUE}} ctu + // expected-warning@-1{{UNKNOWN}} stu // We cannot decide about the dynamic type. - clang_analyzer_eval(obj->fvcl(1) == 8); // expected-warning{{FALSE}} expected-warning{{TRUE}} + clang_analyzer_eval(obj->fvcl(1) == 8); // expected-warning{{TRUE}} ctu + // expected-warning@-1{{UNKNOWN}} ctu, stu } class TestAnonUnionUSR { @@ -137,32 +141,47 @@ extern int testImportOfDelegateConstructor(int); int main() { - clang_analyzer_eval(f(3) == 2); // expected-warning{{TRUE}} - clang_analyzer_eval(f(4) == 3); // expected-warning{{TRUE}} - clang_analyzer_eval(f(5) == 3); // expected-warning{{FALSE}} - clang_analyzer_eval(g(4) == 6); // expected-warning{{TRUE}} - clang_analyzer_eval(h(2) == 8); // expected-warning{{TRUE}} - - clang_analyzer_eval(myns::fns(2) == 9); // expected-warning{{TRUE}} - clang_analyzer_eval(myns::embed_ns::fens(2) == -1); // expected-warning{{TRUE}} - clang_analyzer_eval(mycls().fcl(1) == 6); // expected-warning{{TRUE}} - clang_analyzer_eval(mycls::fscl(1) == 7); // expected-warning{{TRUE}} - clang_analyzer_eval(myns::embed_cls().fecl(1) == -6); // expected-warning{{TRUE}} - clang_analyzer_eval(mycls::embed_cls2().fecl2(0) == -11); // expected-warning{{TRUE}} - - clang_analyzer_eval(chns::chf1(4) == 12); // expected-warning{{TRUE}} - clang_analyzer_eval(fun_using_anon_struct(8) == 8); // expected-warning{{TRUE}} - - clang_analyzer_eval(other_macro_diag(1) == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(f(3) == 2); // expected-warning{{TRUE}} ctu + // expected-warning@-1{{UNKNOWN}} stu + clang_analyzer_eval(f(4) == 3); // expected-warning{{TRUE}} ctu + // expected-warning@-1{{UNKNOWN}} stu + clang_analyzer_eval(f(5) == 3); // expected-warning{{FALSE}} ctu + // expected-warning@-1{{UNKNOWN}} stu + clang_analyzer_eval(g(4) == 6); // expected-warning{{TRUE}} ctu + // expected-warning@-1{{UNKNOWN}} stu + clang_analyzer_eval(h(2) == 8); // expected-warning{{TRUE}} ctu + // expected-warning@-1{{UNKNOWN}} stu + + clang_analyzer_eval(myns::fns(2) == 9); // expected-warning{{TRUE}} ctu + // expected-warning@-1{{UNKNOWN}} stu + clang_analyzer_eval(myns::embed_ns::fens(2) == -1); // expected-warning{{TRUE}} ctu + // expected-warning@-1{{UNKNOWN}} stu + clang_analyzer_eval(mycls().fcl(1) == 6); // expected-warning{{TRUE}} ctu + // expected-warning@-1{{UNKNOWN}} stu + clang_analyzer_eval(mycls::fscl(1) == 7); // expected-warning{{TRUE}} ctu + // expected-warning@-1{{UNKNOWN}} stu + clang_analyzer_eval(myns::embed_cls().fecl(1) == -6); // expected-warning{{TRUE}} ctu + // expected-warning@-1{{UNKNOWN}} stu + clang_analyzer_eval(mycls::embed_cls2().fecl2(0) == -11); // expected-warning{{TRUE}} ctu + // expected-warning@-1{{UNKNOWN}} stu + + clang_analyzer_eval(chns::chf1(4) == 12); // expected-warning{{TRUE}} ctu + // expected-warning@-1{{UNKNOWN}} stu + clang_analyzer_eval(fun_using_anon_struct(8) == 8); // expected-warning{{TRUE}} ctu + // expected-warning@-1{{UNKNOWN}} stu + + clang_analyzer_eval(other_macro_diag(1) == 1); // expected-warning{{TRUE}} ctu + // expected-warning@-1{{UNKNOWN}} stu // expected-warning@Inputs/ctu-other.cpp:93{{REACHABLE}} MACRODIAG(); // expected-warning{{REACHABLE}} + // FIXME we should report an UNKNOWN as well for all external variables! clang_analyzer_eval(extInt == 2); // expected-warning{{TRUE}} clang_analyzer_eval(intns::extInt == 3); // expected-warning{{TRUE}} clang_analyzer_eval(extS.a == 4); // expected-warning{{TRUE}} - clang_analyzer_eval(extNonConstS.a == 4); // expected-warning{{TRUE}} expected-warning{{FALSE}} + clang_analyzer_eval(extNonConstS.a == 4); // expected-warning{{UNKNOWN}} // Do not import non-trivial classes' initializers. - clang_analyzer_eval(extNTS.a == 4); // expected-warning{{TRUE}} expected-warning{{FALSE}} + clang_analyzer_eval(extNTS.a == 4); // expected-warning{{UNKNOWN}} clang_analyzer_eval(extHere == 6); // expected-warning{{TRUE}} clang_analyzer_eval(A::a == 3); // expected-warning{{TRUE}} clang_analyzer_eval(extSC.a == 8); // expected-warning{{TRUE}} @@ -171,10 +190,13 @@ clang_analyzer_eval(extSubSCN.a == 1); // expected-warning{{TRUE}} // clang_analyzer_eval(extSCC.a == 7); // TODO clang_analyzer_eval(extU.a == 4); // expected-warning{{TRUE}} - clang_analyzer_eval(TestAnonUnionUSR::Test == 5); // expected-warning{{TRUE}} - clang_analyzer_eval(testImportOfIncompleteDefaultParmDuringImport(9) == 9); // expected-warning{{TRUE}} + clang_analyzer_eval(testImportOfIncompleteDefaultParmDuringImport(9) == 9); + // expected-warning@-1{{TRUE}} ctu + // expected-warning@-2{{UNKNOWN}} stu - clang_analyzer_eval(testImportOfDelegateConstructor(10) == 10); // expected-warning{{TRUE}} + clang_analyzer_eval(testImportOfDelegateConstructor(10) == 10); + // expected-warning@-1{{TRUE}} ctu + // expected-warning@-2{{UNKNOWN}} stu } Index: clang/test/Analysis/ctu-main.c =================================================================== --- clang/test/Analysis/ctu-main.c +++ clang/test/Analysis/ctu-main.c @@ -5,12 +5,21 @@ // RUN: cp %S/Inputs/ctu-other.c.externalDefMap.ast-dump.txt %t/ctudir2/externalDefMap.txt // RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fsyntax-only -std=c89 -analyze \ // RUN: -analyzer-checker=core,debug.ExprInspection \ +// RUN: -analyzer-config eagerly-assume=false \ // RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \ // RUN: -analyzer-config ctu-dir=%t/ctudir2 \ // RUN: -verify %s void clang_analyzer_eval(int); +// A function that's definition is unknown both for single-tu (stu) and ctu +// mode. +int unknown(int); +void test_unknown() { + int res = unknown(6); + clang_analyzer_eval(res == 6); // expected-warning{{UNKNOWN}} +} + // Test typedef and global variable in function. typedef struct { int a; @@ -18,8 +27,9 @@ } FooBar; extern FooBar fb; int f(int); -void testGlobalVariable(void) { - clang_analyzer_eval(f(5) == 1); // expected-warning{{TRUE}} +void testGlobalVariable() { + clang_analyzer_eval(f(5) == 1); // expected-warning{{TRUE}} ctu + // expected-warning@-1{{UNKNOWN}} stu } // Test enums. @@ -29,7 +39,8 @@ z }; void testEnum(void) { clang_analyzer_eval(x == 0); // expected-warning{{TRUE}} - clang_analyzer_eval(enumCheck() == 42); // expected-warning{{TRUE}} + clang_analyzer_eval(enumCheck() == 42); // expected-warning{{TRUE}} ctu + // expected-warning@-1{{UNKNOWN}} stu } // Test that asm import does not fail. @@ -49,7 +60,8 @@ // warning:implicit functions are prohibited by c99 void testImplicit(void) { int res = identImplicit(6); // external implicit functions are not inlined - clang_analyzer_eval(res == 6); // expected-warning{{TRUE}} + clang_analyzer_eval(res == 6); // expected-warning{{TRUE}} ctu + // expected-warning@-1{{UNKNOWN}} stu // Call something with uninitialized from the same function in which the implicit was called. // This is necessary to reproduce a special bug in NoStoreFuncVisitor. int uninitialized; @@ -67,7 +79,8 @@ struct DataType d; d.a = 1; d.b = 0; - clang_analyzer_eval(structInProto(&d) == 0); // expected-warning{{TRUE}} expected-warning{{FALSE}} + // Not imported, thus remains unknown both in stu and ctu. + clang_analyzer_eval(structInProto(&d) == 0); // expected-warning{{UNKNOWN}} } int switchWithoutCases(int); Index: clang/test/Analysis/ctu-implicit.c =================================================================== --- clang/test/Analysis/ctu-implicit.c +++ clang/test/Analysis/ctu-implicit.c @@ -5,6 +5,7 @@ // RUN: cp %S/Inputs/ctu-import.c.externalDefMap.ast-dump.txt %t/ctudir2/externalDefMap.txt // RUN: %clang_cc1 -analyze \ // RUN: -analyzer-checker=core,debug.ExprInspection \ +// RUN: -analyzer-config eagerly-assume=false \ // RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \ // RUN: -analyzer-config display-ctu-progress=true \ // RUN: -analyzer-config ctu-dir=%t/ctudir2 \ @@ -15,6 +16,7 @@ int testStaticImplicit(void); int func(void) { int ret = testStaticImplicit(); - clang_analyzer_eval(ret == 4); // expected-warning{{TRUE}} + clang_analyzer_eval(ret == 4); // expected-warning{{TRUE}} ctu + // expected-warning@-1{{UNKNOWN}} stu return testStaticImplicit(); } Index: clang/test/Analysis/analyzer-config.c =================================================================== --- clang/test/Analysis/analyzer-config.c +++ clang/test/Analysis/analyzer-config.c @@ -49,6 +49,8 @@ // CHECK-NEXT: ctu-import-threshold = 24 // CHECK-NEXT: ctu-index-name = externalDefMap.txt // CHECK-NEXT: ctu-invocation-list = invocations.yaml +// CHECK-NEXT: ctu-max-nodes-min = 1000 +// CHECK-NEXT: ctu-max-nodes-mul = 100 // CHECK-NEXT: deadcode.DeadStores:ShowFixIts = false // CHECK-NEXT: deadcode.DeadStores:WarnForDeadNestedAssignments = true // CHECK-NEXT: debug.AnalysisOrder:* = false Index: clang/test/Analysis/Inputs/ctu-onego-toplevel-other.cpp.externalDefMap.ast-dump.txt =================================================================== --- /dev/null +++ clang/test/Analysis/Inputs/ctu-onego-toplevel-other.cpp.externalDefMap.ast-dump.txt @@ -0,0 +1 @@ +13:c:@F@other#I# ctu-onego-toplevel-other.cpp.ast Index: clang/test/Analysis/Inputs/ctu-onego-toplevel-other.cpp =================================================================== --- /dev/null +++ clang/test/Analysis/Inputs/ctu-onego-toplevel-other.cpp @@ -0,0 +1,4 @@ +void b(int x); +void other(int y) { + b(1); +} Index: clang/test/Analysis/Inputs/ctu-onego-indirect-other.cpp.externalDefMap.ast-dump.txt =================================================================== --- /dev/null +++ clang/test/Analysis/Inputs/ctu-onego-indirect-other.cpp.externalDefMap.ast-dump.txt @@ -0,0 +1,2 @@ +11:c:@F@other# ctu-onego-indirect-other.cpp.ast +9:c:@F@bar# ctu-onego-indirect-other.cpp.ast Index: clang/test/Analysis/Inputs/ctu-onego-indirect-other.cpp =================================================================== --- /dev/null +++ clang/test/Analysis/Inputs/ctu-onego-indirect-other.cpp @@ -0,0 +1,7 @@ +int bar() { + return 0; +} + +void other() { + bar(); +} Index: clang/test/Analysis/Inputs/ctu-onego-existingdef-other.cpp.externalDefMap.ast-dump.txt =================================================================== --- /dev/null +++ clang/test/Analysis/Inputs/ctu-onego-existingdef-other.cpp.externalDefMap.ast-dump.txt @@ -0,0 +1 @@ +11:c:@F@other# ctu-onego-other.cpp.ast Index: clang/test/Analysis/Inputs/ctu-onego-existingdef-other.cpp =================================================================== --- /dev/null +++ clang/test/Analysis/Inputs/ctu-onego-existingdef-other.cpp @@ -0,0 +1,7 @@ +int bar() { + return 0; +} + +void other() { + bar(); +} Index: clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp =================================================================== --- clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp +++ clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp @@ -476,6 +476,18 @@ if (shouldSkipFunction(D, Visited, VisitedAsTopLevel)) continue; + // The CallGraph might have declarations as callees. However, during CTU + // the declaration might form a declaration chain with the newly imported + // definition from another TU. In this case we don't want to analyze the + // function definition as toplevel. + if (const auto *FD = dyn_cast<FunctionDecl>(D)) { + // Calling 'hasBody' replaces 'FD' in place with the FunctionDecl + // that has the body. + FD->hasBody(FD); + if (CTU.isImportedAsNew(FD)) + continue; + } + // Analyze the function. SetOfConstDecls VisitedCallees; Index: clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -427,10 +427,33 @@ REGISTER_MAP_WITH_PROGRAMSTATE(DynamicDispatchBifurcationMap, const MemRegion *, unsigned) +REGISTER_SET_WITH_PROGRAMSTATE(CTUDispatchBifurcationSet, const Decl *) + +bool ExprEngine::ctuBifurcate(const CallEvent &Call, const Decl *D, + NodeBuilder &Bldr, ExplodedNode *Pred, + ProgramStateRef State) { + ProgramStateRef ConservativeEvalState = nullptr; + WorkList *CTUWList = Engine.getCTUWorkList(); + if (Call.isForeign() && CTUWList) { + const bool BState = State->contains<CTUDispatchBifurcationSet>(D); + if (!BState) { // This is the first time we see this foreign function. + // Enqueue it to be analyzed in the second (ctu) phase. + inlineCall(CTUWList, Call, D, Bldr, Pred, State); + // Conservatively evaluate in the first phase. + ConservativeEvalState = State->add<CTUDispatchBifurcationSet>(D); + conservativeEvalCall(Call, Bldr, Pred, ConservativeEvalState); + } else { + conservativeEvalCall(Call, Bldr, Pred, State); + } + return true; + } + inlineCall(Engine.getWorkList(), Call, D, Bldr, Pred, State); + return true; +} -bool ExprEngine::inlineCall(const CallEvent &Call, const Decl *D, - NodeBuilder &Bldr, ExplodedNode *Pred, - ProgramStateRef State) { +bool ExprEngine::inlineCall(WorkList *WList, const CallEvent &Call, + const Decl *D, NodeBuilder &Bldr, + ExplodedNode *Pred, ProgramStateRef State) { assert(D); const LocationContext *CurLC = Pred->getLocationContext(); @@ -465,7 +488,7 @@ if (ExplodedNode *N = G.getNode(Loc, State, false, &isNew)) { N->addPredecessor(Pred, G); if (isNew) - Engine.getWorkList()->enqueue(N); + WList->enqueue(N); } // If we decided to inline the call, the successor has been manually @@ -475,9 +498,17 @@ NumInlinedCalls++; Engine.FunctionSummaries->bumpNumTimesInlined(D); - // Mark the decl as visited. - if (VisitedCallees) - VisitedCallees->insert(D); + // Do not mark as visited in the 2nd run (CTUWList), so the function will + // be visited as top-level, this way we won't loose reports in non-ctu + // mode. Considering the case when a function in a foreign TU calls back + // into the main TU. + // Note, during the 1st run, it doesn't matter if we mark the foreign + // functions as visited (or not) because they can never appear as a top level + // function in the main TU. + if (Engine.getCTUWorkList()) + // Mark the decl as visited. + if (VisitedCallees) + VisitedCallees->insert(D); return true; } @@ -1068,6 +1099,7 @@ State = InlinedFailedState; } else { RuntimeDefinition RD = Call->getRuntimeDefinition(); + Call->setForeign(RD.isForeign()); const Decl *D = RD.getDecl(); if (shouldInlineCall(*Call, D, Pred, CallOpts)) { if (RD.mayHaveOtherDefinitions()) { @@ -1087,7 +1119,7 @@ } // We are not bifurcating and we do have a Decl, so just inline. - if (inlineCall(*Call, D, Bldr, Pred, State)) + if (ctuBifurcate(*Call, D, Bldr, Pred, State)) return; } } @@ -1110,7 +1142,7 @@ if (BState) { // If we are on "inline path", keep inlining if possible. if (*BState == DynamicDispatchModeInlined) - if (inlineCall(Call, D, Bldr, Pred, State)) + if (ctuBifurcate(Call, D, Bldr, Pred, State)) return; // If inline failed, or we are on the path where we assume we // don't have enough info about the receiver to inline, conjure the @@ -1124,7 +1156,7 @@ ProgramStateRef IState = State->set<DynamicDispatchBifurcationMap>(BifurReg, DynamicDispatchModeInlined); - inlineCall(Call, D, Bldr, Pred, IState); + ctuBifurcate(Call, D, Bldr, Pred, IState); ProgramStateRef NoIState = State->set<DynamicDispatchBifurcationMap>(BifurReg, Index: clang/lib/StaticAnalyzer/Core/CoreEngine.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/CoreEngine.cpp +++ clang/lib/StaticAnalyzer/Core/CoreEngine.cpp @@ -43,6 +43,8 @@ STATISTIC(NumSteps, "The # of steps executed."); +STATISTIC(NumSTUSteps, "The # of STU steps executed."); +STATISTIC(NumCTUSteps, "The # of CTU steps executed."); STATISTIC(NumReachedMaxSteps, "The # of times we reached the max number of steps."); STATISTIC(NumPathsExplored, @@ -73,11 +75,18 @@ CoreEngine::CoreEngine(ExprEngine &exprengine, FunctionSummariesTy *FS, AnalyzerOptions &Opts) : ExprEng(exprengine), WList(generateWorkList(Opts)), - BCounterFactory(G.getAllocator()), FunctionSummaries(FS) {} + CTUWList(generateWorkList(Opts)), BCounterFactory(G.getAllocator()), + FunctionSummaries(FS) {} + +void CoreEngine::setBlockCounter(BlockCounter C) { + WList->setBlockCounter(C); + if (CTUWList) + CTUWList->setBlockCounter(C); +} /// ExecuteWorkList - Run the worklist algorithm for a maximum number of steps. -bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps, - ProgramStateRef InitState) { +bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned MaxSteps, + ProgramStateRef InitState) { if (G.num_roots() == 0) { // Initialize the analysis by constructing // the root if none exists. @@ -100,7 +109,7 @@ BlockEdge StartLoc(Entry, Succ, L); // Set the current block counter to being empty. - WList->setBlockCounter(BCounterFactory.GetEmptyCounter()); + setBlockCounter(BCounterFactory.GetEmptyCounter()); if (!InitState) InitState = ExprEng.getInitialState(L); @@ -118,34 +127,55 @@ } // Check if we have a steps limit - bool UnlimitedSteps = Steps == 0; + bool UnlimitedSteps = MaxSteps == 0; + // Cap our pre-reservation in the event that the user specifies // a very large number of maximum steps. const unsigned PreReservationCap = 4000000; if(!UnlimitedSteps) - G.reserve(std::min(Steps,PreReservationCap)); - - while (WList->hasWork()) { - if (!UnlimitedSteps) { - if (Steps == 0) { - NumReachedMaxSteps++; - break; + G.reserve(std::min(MaxSteps, PreReservationCap)); + + auto ProcessWList = [this, UnlimitedSteps](unsigned MaxSteps) { + unsigned Steps = MaxSteps; + while (WList->hasWork()) { + if (!UnlimitedSteps) { + if (Steps == 0) { + NumReachedMaxSteps++; + break; + } + --Steps; } - --Steps; - } - NumSteps++; + NumSteps++; - const WorkListUnit& WU = WList->dequeue(); + const WorkListUnit &WU = WList->dequeue(); - // Set the current block counter. - WList->setBlockCounter(WU.getBlockCounter()); + // Set the current block counter. + setBlockCounter(WU.getBlockCounter()); - // Retrieve the node. - ExplodedNode *Node = WU.getNode(); + // Retrieve the node. + ExplodedNode *Node = WU.getNode(); + + dispatchWorkItem(Node, Node->getLocation(), WU); + } + return MaxSteps - Steps; + }; + const unsigned STUSteps = ProcessWList(MaxSteps); + NumSTUSteps += STUSteps; + + // Let CTU run as many steps we had in the single TU run. + // However, we need at least some minimal value to pass those lit tests that + // report a bug only in the CTU mode. + const unsigned MinCTUSteps = + this->ExprEng.getAnalysisManager().options.CTUMaxNodesMin; + const unsigned Mul = + this->ExprEng.getAnalysisManager().options.CTUMaxNodesMultiplier; + unsigned MaxCTUSteps = std::max(STUSteps * Mul / 100, MinCTUSteps); + + WList = std::move(CTUWList); + const unsigned CTUSteps = ProcessWList(MaxCTUSteps); + NumCTUSteps += CTUSteps; - dispatchWorkItem(Node, Node->getLocation(), WU); - } ExprEng.processEndWorklist(); return WList->hasWork(); } @@ -282,7 +312,7 @@ BlockCounter Counter = WList->getBlockCounter(); Counter = BCounterFactory.IncrementCount(Counter, LC->getStackFrame(), BlockId); - WList->setBlockCounter(Counter); + setBlockCounter(Counter); // Process the entrance of the block. if (Optional<CFGElement> E = L.getFirstElement()) { Index: clang/lib/StaticAnalyzer/Core/CallEvent.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/CallEvent.cpp +++ clang/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -515,20 +515,28 @@ llvm::dbgs() << "Using autosynthesized body for " << FD->getName() << "\n"; }); + + ExprEngine &Engine = getState()->getStateManager().getOwningEngine(); + cross_tu::CrossTranslationUnitContext &CTUCtx = + *Engine.getCrossTranslationUnitContext(); + if (Body) { const Decl* Decl = AD->getDecl(); - return RuntimeDefinition(Decl); + if (CTUCtx.isImportedAsNew(Decl)) { + // A newly created definition, but we had error(s) during the import. + if (CTUCtx.hasError(Decl)) + return {}; + return RuntimeDefinition(Decl, /*Foreign=*/true); + } + return RuntimeDefinition(Decl, /*Foreign=*/false); } - ExprEngine &Engine = getState()->getStateManager().getOwningEngine(); AnalyzerOptions &Opts = Engine.getAnalysisManager().options; // Try to get CTU definition only if CTUDir is provided. if (!Opts.IsNaiveCTUEnabled) return {}; - cross_tu::CrossTranslationUnitContext &CTUCtx = - *Engine.getCrossTranslationUnitContext(); llvm::Expected<const FunctionDecl *> CTUDeclOrError = CTUCtx.getCrossTUDefinition(FD, Opts.CTUDir, Opts.CTUIndexName, Opts.DisplayCTUProgress); @@ -541,7 +549,7 @@ return {}; } - return RuntimeDefinition(*CTUDeclOrError); + return RuntimeDefinition(*CTUDeclOrError, /*Foreign=*/true); } void AnyFunctionCall::getInitialStackFrameContents( Index: clang/lib/CrossTU/CrossTranslationUnit.cpp =================================================================== --- clang/lib/CrossTU/CrossTranslationUnit.cpp +++ clang/lib/CrossTU/CrossTranslationUnit.cpp @@ -801,5 +801,18 @@ return llvm::None; } +bool CrossTranslationUnitContext::isImportedAsNew(const Decl *ToDecl) const { + if (!ImporterSharedSt) + return false; + return ImporterSharedSt->isNewDecl(const_cast<Decl *>(ToDecl)); +} + +bool CrossTranslationUnitContext::hasError(const Decl *ToDecl) const { + if (!ImporterSharedSt) + return false; + return static_cast<bool>( + ImporterSharedSt->getImportDeclErrorIfAny(const_cast<Decl *>(ToDecl))); +} + } // namespace cross_tu } // namespace clang Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -805,8 +805,11 @@ const ExplodedNode *Pred, const EvalCallOptions &CallOpts = {}); - bool inlineCall(const CallEvent &Call, const Decl *D, NodeBuilder &Bldr, - ExplodedNode *Pred, ProgramStateRef State); + bool inlineCall(WorkList *WList, const CallEvent &Call, const Decl *D, + NodeBuilder &Bldr, ExplodedNode *Pred, ProgramStateRef State); + + bool ctuBifurcate(const CallEvent &Call, const Decl *D, NodeBuilder &Bldr, + ExplodedNode *Pred, ProgramStateRef State); /// Conservatively evaluate call by invalidating regions and binding /// a conjured return value. Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h +++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h @@ -78,6 +78,7 @@ /// worklist algorithm. It is up to the implementation of WList to decide /// the order that nodes are processed. std::unique_ptr<WorkList> WList; + std::unique_ptr<WorkList> CTUWList; /// BCounterFactory - A factory object for created BlockCounter objects. /// These are used to record for key nodes in the ExplodedGraph the @@ -101,6 +102,8 @@ /// tags. DataTag::Factory DataTags; + void setBlockCounter(BlockCounter C); + void generateNode(const ProgramPoint &Loc, ProgramStateRef State, ExplodedNode *Pred); @@ -170,6 +173,7 @@ } WorkList *getWorkList() const { return WList.get(); } + WorkList *getCTUWorkList() const { return CTUWList.get(); } BlocksExhausted::const_iterator blocks_exhausted_begin() const { return blocksExhausted.begin(); Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h +++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h @@ -113,12 +113,16 @@ /// precise. const MemRegion *R = nullptr; + const bool Foreign = false; // From CTU. + public: RuntimeDefinition() = default; RuntimeDefinition(const Decl *InD): D(InD) {} + RuntimeDefinition(const Decl *InD, bool Foreign) : D(InD), Foreign(Foreign) {} RuntimeDefinition(const Decl *InD, const MemRegion *InR): D(InD), R(InR) {} const Decl *getDecl() { return D; } + bool isForeign() const { return Foreign; } /// Check if the definition we have is precise. /// If not, it is possible that the call dispatches to another definition at @@ -147,6 +151,7 @@ ProgramStateRef State; const LocationContext *LCtx; llvm::PointerUnion<const Expr *, const Decl *> Origin; + mutable Optional<bool> Foreign; // From CTU. protected: // This is user data for subclasses. @@ -208,6 +213,12 @@ return Origin.dyn_cast<const Decl *>(); } + bool isForeign() const { + assert(Foreign.hasValue() && "Foreign must be set before querying"); + return *Foreign; + } + void setForeign(bool B) const { Foreign = B; } + /// The state in which the call is being evaluated. const ProgramStateRef &getState() const { return State; Index: clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def =================================================================== --- clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def +++ clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def @@ -408,6 +408,23 @@ "top level function (for each exploded graph). 0 means no limit.", /* SHALLOW_VAL */ 75000, /* DEEP_VAL */ 225000) +ANALYZER_OPTION( + unsigned, CTUMaxNodesMultiplier, "ctu-max-nodes-mul", + "We count the nodes for a normal single tu analysis. We multiply that " + "number with this config value then we divide the result by 100 to get " + "the maximum number of nodes that the analyzer can generate while " + "exploring a top level function in CTU mode (for each exploded graph)." + "For example, 100 means that CTU will explore maximum as much nodes that " + "we explored during the single tu mode, 200 means it will explore twice as " + "much, 50 means it will explore maximum 50% more.", 100) + +ANALYZER_OPTION( + unsigned, CTUMaxNodesMin, "ctu-max-nodes-min", + "The maximum number of nodes in CTU mode is determinded by " + "'ctu-max-nodes-mul'. However, if the number of nodes in single tu analysis " + "is too low, it is meaningful to provide a minimum value that serves as an " + "upper bound instead.", 1000) + ANALYZER_OPTION( unsigned, RegionStoreSmallStructLimit, "region-store-small-struct-limit", "The largest number of fields a struct can have and still be considered " Index: clang/include/clang/CrossTU/CrossTranslationUnit.h =================================================================== --- clang/include/clang/CrossTU/CrossTranslationUnit.h +++ clang/include/clang/CrossTU/CrossTranslationUnit.h @@ -197,6 +197,14 @@ getMacroExpansionContextForSourceLocation( const clang::SourceLocation &ToLoc) const; + /// Returns true if the given Decl is newly created during the import. + bool isImportedAsNew(const Decl *ToDecl) const; + + /// Returns true if the given Decl is mapped (or created) during an import + /// but there was an unrecoverable error (the AST node cannot be erased, it + /// is marked with an Error object in this case). + bool hasError(const Decl *ToDecl) const; + private: void lazyInitImporterSharedSt(TranslationUnitDecl *ToTU); ASTImporter &getOrCreateASTImporter(ASTUnit *Unit);
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits