|  | // RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify=expected,default %s | 
|  | // RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-config inline-functions-with-ambiguous-loops=true -verify=expected,enabled %s | 
|  |  | 
|  | // This file tests some heuristics in the engine that put functions on a | 
|  | // "do not inline" list if their analyisis reaches the `analyzer-max-loop` | 
|  | // limit (by default 4 iterations) in a loop. This was almost surely intended | 
|  | // as memoization optimization for the "retry without inlining" fallback (if we | 
|  | // had to retry once, next time don't even try inlining), but aggressively | 
|  | // oversteps the "natural" scope: reaching 4 iterations on _one particular_ | 
|  | // execution path does not imply that each path would need "retry without | 
|  | // inlining" especially if a different call receives different arguments. | 
|  | // | 
|  | // This heuristic significantly affects the scope/depth of the analysis (and | 
|  | // therefore the execution time) because without this limitation on the | 
|  | // inlining significantly more entry points would be able to exhaust their | 
|  | // `max-nodes` quota. (Trivial thin wrappers around big complex functions are | 
|  | // common in many projects.) | 
|  | // | 
|  | // Unfortunately, this arbitrary heuristic strongly relies on the current loop | 
|  | // handling model and its many limitations, so improvements in loop handling | 
|  | // can cause surprising slowdowns by reducing the "do not inline" blacklist. | 
|  | // In the tests "FIXME-BUT-NEEDED" comments mark "problematic" (aka buggy) | 
|  | // analyzer behavior which cannot be fixed without also improving the | 
|  | // heuristics for (not) inlining large functions. | 
|  |  | 
|  | int getNum(void); // Get an unknown symbolic number. | 
|  |  | 
|  | void clang_analyzer_dump(int arg); | 
|  |  | 
|  | //----------------------------------------------------------------------------- | 
|  | // Simple case: inlined function never reaches `analyzer-max-loop`, so it is | 
|  | // always inlined. | 
|  |  | 
|  | int inner_simple(int callIdx) { | 
|  | clang_analyzer_dump(callIdx); // expected-warning {{1 S32}} | 
|  | // expected-warning@-1 {{2 S32}} | 
|  | return 42; | 
|  | } | 
|  |  | 
|  | int outer_simple(void) { | 
|  | int x = inner_simple(1); | 
|  | int y = inner_simple(2); | 
|  | return 53 / (x - y); // expected-warning {{Division by zero}} | 
|  | } | 
|  |  | 
|  | //----------------------------------------------------------------------------- | 
|  | // Inlined function always reaches `analyzer-max-loop`, which stops the | 
|  | // analysis on that path and puts the function on the "do not inline" list. | 
|  |  | 
|  | int inner_fixed_loop_1(int callIdx) { | 
|  | int i; | 
|  | clang_analyzer_dump(callIdx); // expected-warning {{1 S32}} | 
|  | for (i = 0; i < 10; i++); // FIXME-BUT-NEEDED: This stops the analysis. | 
|  | clang_analyzer_dump(callIdx); // no-warning | 
|  | return 42; | 
|  | } | 
|  |  | 
|  | int outer_fixed_loop_1(void) { | 
|  | int x = inner_fixed_loop_1(1); | 
|  | int y = inner_fixed_loop_1(2); | 
|  |  | 
|  | // FIXME-BUT-NEEDED: The analysis doesn't reach this zero division. | 
|  | return 53 / (x - y); // no-warning | 
|  | } | 
|  |  | 
|  | //----------------------------------------------------------------------------- | 
|  | // Inlined function always reaches `analyzer-max-loop`; inlining is prevented | 
|  | // even for different entry points. | 
|  | // NOTE: the analyzer happens to analyze the entry points in a reversed order, | 
|  | // so `outer_2_fixed_loop_2` is analyzed first and it will be the one which is | 
|  | // able to inline the inner function. | 
|  |  | 
|  | int inner_fixed_loop_2(int callIdx) { | 
|  | // Identical copy of inner_fixed_loop_1. | 
|  | int i; | 
|  | clang_analyzer_dump(callIdx); // expected-warning {{2 S32}} | 
|  | for (i = 0; i < 10; i++); // FIXME-BUT-NEEDED: This stops the analysis. | 
|  | clang_analyzer_dump(callIdx); // no-warning | 
|  | return 42; | 
|  | } | 
|  |  | 
|  | int outer_1_fixed_loop_2(void) { | 
|  | return inner_fixed_loop_2(1); | 
|  | } | 
|  |  | 
|  | int outer_2_fixed_loop_2(void) { | 
|  | return inner_fixed_loop_2(2); | 
|  | } | 
|  |  | 
|  | //----------------------------------------------------------------------------- | 
|  | // Inlined function reaches `analyzer-max-loop` only in its second call. The | 
|  | // function is inlined twice but the second call doesn't finish and ends up | 
|  | // being conservatively evaluated. | 
|  |  | 
|  | int inner_parametrized_loop_1(int count) { | 
|  | int i; | 
|  | clang_analyzer_dump(count); // expected-warning {{2 S32}} | 
|  | // expected-warning@-1 {{10 S32}} | 
|  | for (i = 0; i < count; i++); | 
|  | // FIXME-BUT-NEEDED: This loop stops the analysis when count >=4. | 
|  | clang_analyzer_dump(count); // expected-warning {{2 S32}} | 
|  | return 42; | 
|  | } | 
|  |  | 
|  | int outer_parametrized_loop_1(void) { | 
|  | int x = inner_parametrized_loop_1(2); | 
|  | int y = inner_parametrized_loop_1(10); | 
|  |  | 
|  | // FIXME-BUT-NEEDED: The analysis doesn't reach this zero division. | 
|  | return 53 / (x - y); // no-warning | 
|  | } | 
|  |  | 
|  | //----------------------------------------------------------------------------- | 
|  | // Inlined function reaches `analyzer-max-loop` on its first call, so the | 
|  | // second call isn't inlined (although it could be fully evaluated). | 
|  |  | 
|  | int inner_parametrized_loop_2(int count) { | 
|  | // Identical copy of inner_parametrized_loop_1. | 
|  | int i; | 
|  | clang_analyzer_dump(count); // expected-warning {{10 S32}} | 
|  | for (i = 0; i < count; i++); | 
|  | // FIXME-BUT-NEEDED: This loop stops the analysis when count >=4. | 
|  | clang_analyzer_dump(count); // no-warning | 
|  | return 42; | 
|  | } | 
|  |  | 
|  | int outer_parametrized_loop_2(void) { | 
|  | int y = inner_parametrized_loop_2(10); | 
|  | int x = inner_parametrized_loop_2(2); | 
|  |  | 
|  | // FIXME-BUT-NEEDED: The analysis doesn't reach this zero division. | 
|  | return 53 / (x - y); // no-warning | 
|  | } | 
|  |  | 
|  | //----------------------------------------------------------------------------- | 
|  | // Inlined function may or may not reach `analyzer-max-loop` depending on an | 
|  | // ambiguous check before the loop. This is very similar to the "fixed loop" | 
|  | // cases: the function is placed on the "don't inline" list when any execution | 
|  | // path reaches `analyzer-max-loop` (even if other execution paths reach the | 
|  | // end of the function). | 
|  | // NOTE: This is tested with two separate entry points to ensure that one | 
|  | // inlined call is fully evaluated before we try to inline the other call. | 
|  | // NOTE: the analyzer happens to analyze the entry points in a reversed order, | 
|  | // so `outer_2_conditional_loop` is analyzed first and it will be the one which | 
|  | // is able to inline the inner function. | 
|  |  | 
|  | int inner_conditional_loop(int callIdx) { | 
|  | int i; | 
|  | clang_analyzer_dump(callIdx); // expected-warning {{2 S32}} | 
|  | if (getNum() == 777) { | 
|  | for (i = 0; i < 10; i++); | 
|  | } | 
|  | clang_analyzer_dump(callIdx); // expected-warning {{2 S32}} | 
|  | return 42; | 
|  | } | 
|  |  | 
|  | int outer_1_conditional_loop(void) { | 
|  | return inner_conditional_loop(1); | 
|  | } | 
|  |  | 
|  | int outer_2_conditional_loop(void) { | 
|  | return inner_conditional_loop(2); | 
|  | } | 
|  |  | 
|  | //----------------------------------------------------------------------------- | 
|  | // Inlined function executes an ambiguous loop that may or may not reach | 
|  | // `analyzer-max-loop`. Historically, before the "don't assume third iteration" | 
|  | // commit (bb27d5e5c6b194a1440b8ac4e5ace68d0ee2a849) this worked like the | 
|  | // `conditional_loop` cases: the analyzer was able to find a path reaching | 
|  | // `analyzer-max-loop` so inlining was disabled. After that commit the analyzer | 
|  | // does not _assume_ a third (or later) iteration (i.e. does not enter those | 
|  | // iterations if the loop condition is an unknown value), so e.g. this test | 
|  | // function does not reach `analyzer-max-loop` iterations and the inlining is | 
|  | // not disabled. | 
|  | // Unfortunately this change significantly increased the workload and | 
|  | // runtime of the analyzer (more entry points used up their budget), so the | 
|  | // option `inline-functions-with-ambiguous-loops` was introduced and disabled | 
|  | // by default to suppress the inlining in situations where the "don't assume | 
|  | // third iteration" logic activates. | 
|  | // NOTE: This is tested with two separate entry points to ensure that one | 
|  | // inlined call is fully evaluated before we try to inline the other call. | 
|  | // NOTE: the analyzer happens to analyze the entry points in a reversed order, | 
|  | // so `outer_2_ambiguous_loop` is analyzed first and it will be the one which | 
|  | // is able to inline the inner function. | 
|  |  | 
|  | int inner_ambiguous_loop(int callIdx) { | 
|  | int i; | 
|  | clang_analyzer_dump(callIdx); // default-warning {{2 S32}} | 
|  | // enabled-warning@-1 {{1 S32}} | 
|  | // enabled-warning@-2 {{2 S32}} | 
|  | for (i = 0; i < getNum(); i++); | 
|  | return i; | 
|  | } | 
|  |  | 
|  | int outer_1_ambiguous_loop(void) { | 
|  | return inner_ambiguous_loop(1); | 
|  | } | 
|  | int outer_2_ambiguous_loop(void) { | 
|  | return inner_ambiguous_loop(2); | 
|  | } |