From 525bd35370ce07695fc6cb24cb4afe11560f2418 Mon Sep 17 00:00:00 2001 From: Qianyi Shu Date: Fri, 12 Jun 2020 02:43:08 -0700 Subject: [PATCH] [cost] import c performance test to objc Summary: Basically copying test file from c/performance to objc/performance. Reviewed By: skcho Differential Revision: D21972945 fbshipit-source-id: 6e4412598 --- .../codetoanalyze/objc/performance/break.m | 36 ++++ .../objc/performance/compound_loop_guard.m | 95 ++++++++++ .../objc/performance/cost-issues.exp | 66 +++++++ .../objc/performance/cost_test.m | 93 +++++++++ .../objc/performance/cost_test_deps.m | 176 ++++++++++++++++++ .../codetoanalyze/objc/performance/exit.m | 35 ++++ .../objc/performance/instantiate.m | 31 +++ .../objc/performance/invariant.m | 49 +++++ .../codetoanalyze/objc/performance/issues.exp | 30 +++ .../objc/performance/jump_inside_loop.m | 40 ++++ .../codetoanalyze/objc/performance/loops.m | 81 ++++++++ .../codetoanalyze/objc/performance/purity.m | 16 ++ .../objc/performance/switch_continue.m | 47 +++++ .../objc/performance/two_loops_symbolic.m | 19 ++ 14 files changed, 814 insertions(+) create mode 100644 infer/tests/codetoanalyze/objc/performance/break.m create mode 100644 infer/tests/codetoanalyze/objc/performance/compound_loop_guard.m create mode 100644 infer/tests/codetoanalyze/objc/performance/cost_test.m create mode 100644 infer/tests/codetoanalyze/objc/performance/cost_test_deps.m create mode 100644 infer/tests/codetoanalyze/objc/performance/exit.m create mode 100644 infer/tests/codetoanalyze/objc/performance/instantiate.m create mode 100644 infer/tests/codetoanalyze/objc/performance/invariant.m create mode 100644 infer/tests/codetoanalyze/objc/performance/jump_inside_loop.m create mode 100644 infer/tests/codetoanalyze/objc/performance/loops.m create mode 100644 infer/tests/codetoanalyze/objc/performance/purity.m create mode 100644 infer/tests/codetoanalyze/objc/performance/switch_continue.m create mode 100644 infer/tests/codetoanalyze/objc/performance/two_loops_symbolic.m diff --git a/infer/tests/codetoanalyze/objc/performance/break.m b/infer/tests/codetoanalyze/objc/performance/break.m new file mode 100644 index 00000000000..8fdff124e15 --- /dev/null +++ b/infer/tests/codetoanalyze/objc/performance/break.m @@ -0,0 +1,36 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +/* t is also in control variables but once we have invariant analysis, it + * shouldn't be */ +int break_loop(int p, int t) { + for (int i = 0; i < p; i++) { + // do something + if (t < 0) + break; + // do something + } + return 0; +} + +/* t will be in control variables but once we have invariant analysis, + * it shouldn't be. */ +int break_loop_with_t(int p, int t) { + for (int i = 0; i < p; i++) { + // do something + if (t < 0) { + t++; + break; + } + // do something + } + return 0; +} + +/* calling break_loop with a negative t should give constant + cost. Currently, this doesn't work since we can't do case analysis + on the domain. */ +int break_constant_FP(int p) { return break_loop(p, -1); } diff --git a/infer/tests/codetoanalyze/objc/performance/compound_loop_guard.m b/infer/tests/codetoanalyze/objc/performance/compound_loop_guard.m new file mode 100644 index 00000000000..9bc8b818080 --- /dev/null +++ b/infer/tests/codetoanalyze/objc/performance/compound_loop_guard.m @@ -0,0 +1,95 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +/* while loop that contains && in the guard. It gives the correct bound. + * Expected: O(m) */ +int compound_while(int m) { + int i = 0; + int j = 3 * i; + while (j == 0 && i < m) { + i++; + } + return j; +} + +int simplified_simulated_while_with_and_constant(int p) { + int k = 0; + int j = 0; +B: + j++; + if (k == 0 && j < 100) { + goto B; // continue; + } + return k; +} + +/* simulated goto that contains && */ +int simulated_while_with_and_linear(int p) { + int i = 0; + int k = 0; +LOOP_COND: + if (k == 0 && i < p) { // k == 0 always true + goto INCR; + } else { + goto RETURN; + } +INCR: + i++; + goto LOOP_COND; +RETURN: + return i; +} + +/* shortcut in the conditional, hence we won't loop, and get constant cost */ +int simulated_while_shortcut_constant(int p) { + int k = 0; + int j = 0; +B: + j++; + if (k == 1 && j < 100) { + goto B; // continue; + } + return k; +} + +/* p should be in control vars. If p is 1, can run forever */ +void while_and_or(int p) { + int i = 0; + while (p == 1 || (i < 30 && i >= 0)) { + i++; + } +} + +// should be constant cost +int nested_while_and_or_constant(int p) { + int i = 0; + int j = 3 * i; + while (p == 1 || (i < 30 && i >= 0)) { + while (p == 1 || (j < 5 && j >= 0)) { + + return j; + } + i++; + } + return j; +} + +/* j and i will be control variables for B */ +int simulated_nested_loop_with_and_constant(int p) { + int k = 0; + int t = 5; + int j = 0; + for (int i = 0; i < 5; i++) { + B: + t = 3; + j++; + if (k == 0 && j < 100) { // k == 0 always true + goto B; // continue; + } + } + return k; +} diff --git a/infer/tests/codetoanalyze/objc/performance/cost-issues.exp b/infer/tests/codetoanalyze/objc/performance/cost-issues.exp index 3b8e72e5301..c8169d287fc 100644 --- a/infer/tests/codetoanalyze/objc/performance/cost-issues.exp +++ b/infer/tests/codetoanalyze/objc/performance/cost-issues.exp @@ -1,9 +1,75 @@ codetoanalyze/objc/performance/araii.m, Araii.dealloc, 4, OnUIThread:false, [] codetoanalyze/objc/performance/araii.m, Araii.initWithBuffer, 15, OnUIThread:false, [] codetoanalyze/objc/performance/araii.m, memory_leak_raii_main, 18, OnUIThread:false, [] +codetoanalyze/objc/performance/break.m, break_constant_FP, 8 + 5 ⋅ p + 2 ⋅ (1+max(0, p)), OnUIThread:false, [{1+max(0, p)},call to break_loop,Loop at line 10, column 3,{p},call to break_loop,Loop at line 10, column 3] +codetoanalyze/objc/performance/break.m, break_loop, 5 + 5 ⋅ p + 2 ⋅ (1+max(0, p)), OnUIThread:false, [{1+max(0, p)},Loop at line 10, column 3,{p},Loop at line 10, column 3] +codetoanalyze/objc/performance/break.m, break_loop_with_t, 7 + 5 ⋅ p + 2 ⋅ (1+max(0, p)), OnUIThread:false, [{1+max(0, p)},Loop at line 22, column 3,{p},Loop at line 22, column 3] codetoanalyze/objc/performance/cf.m, array_count_linear, 6 + 3 ⋅ arr.length + 2 ⋅ (arr.length + 1), OnUIThread:false, [{arr.length + 1},Loop at line 18, column 3,{arr.length},Loop at line 18, column 3] codetoanalyze/objc/performance/cf.m, cf_array_create_copy_linear, 1010, OnUIThread:false, [] codetoanalyze/objc/performance/cf.m, cf_array_create_linear, 11 + 3 ⋅ x + 2 ⋅ (1+max(0, x)), OnUIThread:false, [{1+max(0, x)},Loop at line 41, column 3,{x},Loop at line 41, column 3] codetoanalyze/objc/performance/cf.m, dict_count_linear, 6 + 3 ⋅ dict.length + 2 ⋅ (dict.length + 1), OnUIThread:false, [{dict.length + 1},Loop at line 24, column 3,{dict.length},Loop at line 24, column 3] +codetoanalyze/objc/performance/compound_loop_guard.m, compound_while, 7 + 3 ⋅ m + 4 ⋅ (1+max(0, m)), OnUIThread:false, [{1+max(0, m)},Loop at line 13, column 3,{m},Loop at line 13, column 3] +codetoanalyze/objc/performance/compound_loop_guard.m, nested_while_and_or_constant, 20, OnUIThread:false, [] +codetoanalyze/objc/performance/compound_loop_guard.m, simplified_simulated_while_with_and_constant, 605, OnUIThread:false, [] +codetoanalyze/objc/performance/compound_loop_guard.m, simulated_nested_loop_with_and_constant, 3529, OnUIThread:false, [] +codetoanalyze/objc/performance/compound_loop_guard.m, simulated_while_shortcut_constant, 9, OnUIThread:false, [] +codetoanalyze/objc/performance/compound_loop_guard.m, simulated_while_with_and_linear, 6 + 3 ⋅ p + 4 ⋅ (1+max(0, p)), OnUIThread:false, [{1+max(0, p)},Loop at line 42, column 3,{p},Loop at line 42, column 3] +codetoanalyze/objc/performance/compound_loop_guard.m, while_and_or, ⊤, OnUIThread:false, [Unbounded loop,Loop at line 62, column 3] codetoanalyze/objc/performance/control.m, __infer_globals_initializer_gvar, 2, OnUIThread:false, [] codetoanalyze/objc/performance/control.m, wrong_cvar_FP, ⊤, OnUIThread:false, [Unbounded loop,Loop at line 20, column 3] +codetoanalyze/objc/performance/cost_test.m, always, 9, OnUIThread:false, [] +codetoanalyze/objc/performance/cost_test.m, call_infinite, ⊤, OnUIThread:false, [Call to infinite,Unbounded loop,Loop at line 72, column 3] +codetoanalyze/objc/performance/cost_test.m, call_while_upto20_10_constant, 56, OnUIThread:false, [] +codetoanalyze/objc/performance/cost_test.m, call_while_upto20_minus100_constant, 606, OnUIThread:false, [] +codetoanalyze/objc/performance/cost_test.m, call_while_upto20_unsigned, 6 + 4 ⋅ (-x + 20) + (21-min(20, x)), OnUIThread:false, [{21-min(20, x)},call to while_upto20,Loop at line 45, column 3,{-x + 20},call to while_upto20,Loop at line 45, column 3] +codetoanalyze/objc/performance/cost_test.m, cond_constant, 14, OnUIThread:false, [] +codetoanalyze/objc/performance/cost_test.m, div_const, 3, OnUIThread:false, [] +codetoanalyze/objc/performance/cost_test.m, foo_constant, 6, OnUIThread:false, [] +codetoanalyze/objc/performance/cost_test.m, infinite, ⊤, OnUIThread:false, [Unbounded loop,Loop at line 72, column 3] +codetoanalyze/objc/performance/cost_test.m, infinite_FN, 19, OnUIThread:false, [] +codetoanalyze/objc/performance/cost_test.m, iter_div_const_constant, 109, OnUIThread:false, [] +codetoanalyze/objc/performance/cost_test.m, loop0_constant, 1005, OnUIThread:false, [] +codetoanalyze/objc/performance/cost_test.m, loop3_constant, 97, OnUIThread:false, [] +codetoanalyze/objc/performance/cost_test.m, loop_character_symbols_linear, 5 + 4 ⋅ (122-min(97, c)), OnUIThread:false, [{122-min(97, c)},Loop at line 80, column 3] +codetoanalyze/objc/performance/cost_test.m, unit_cost_function, 1, OnUIThread:false, [] +codetoanalyze/objc/performance/cost_test.m, while_upto20, 4 + 4 ⋅ (-m + 20) + (21-min(20, m)), OnUIThread:false, [{21-min(20, m)},Loop at line 45, column 3,{-m + 20},Loop at line 45, column 3] +codetoanalyze/objc/performance/cost_test_deps.m, if_bad_constant, 75, OnUIThread:false, [] +codetoanalyze/objc/performance/cost_test_deps.m, if_bad_loop_constant, 203, OnUIThread:false, [] +codetoanalyze/objc/performance/cost_test_deps.m, loop_despite_inferbo_constant, 1208, OnUIThread:false, [] +codetoanalyze/objc/performance/cost_test_deps.m, nested_loop_constant, 2547, OnUIThread:false, [] +codetoanalyze/objc/performance/cost_test_deps.m, real_while_constant, 218, OnUIThread:false, [] +codetoanalyze/objc/performance/cost_test_deps.m, simulated_nested_loop_cond_in_goto_constant, 3534, OnUIThread:false, [] +codetoanalyze/objc/performance/cost_test_deps.m, simulated_nested_loop_constant, 2529, OnUIThread:false, [] +codetoanalyze/objc/performance/cost_test_deps.m, simulated_nested_loop_more_expensive_constant, 2534, OnUIThread:false, [] +codetoanalyze/objc/performance/cost_test_deps.m, simulated_while_constant, 218, OnUIThread:false, [] +codetoanalyze/objc/performance/cost_test_deps.m, two_loops, 549, OnUIThread:false, [] +codetoanalyze/objc/performance/exit.m, call_exit_unreachable, ⊥, OnUIThread:false, [Unreachable node] +codetoanalyze/objc/performance/exit.m, call_unreachable_constant, 2, OnUIThread:false, [] +codetoanalyze/objc/performance/exit.m, compute_exit_unreachable, ⊥, OnUIThread:false, [Unreachable node] +codetoanalyze/objc/performance/exit.m, exit_unreachable, ⊥, OnUIThread:false, [Unreachable node] +codetoanalyze/objc/performance/exit.m, inline_exit_unreachable_FP, 3 ⋅ p + 2 ⋅ (1+max(0, p)), OnUIThread:false, [{1+max(0, p)},Loop at line 30, column 3,{p},Loop at line 30, column 3] +codetoanalyze/objc/performance/exit.m, linear, 3 + 3 ⋅ p + 2 ⋅ (1+max(0, p)), OnUIThread:false, [{1+max(0, p)},Loop at line 18, column 3,{p},Loop at line 18, column 3] +codetoanalyze/objc/performance/instantiate.m, do_2_times_constant, 20, OnUIThread:false, [] +codetoanalyze/objc/performance/instantiate.m, do_half_m2_times_quadratic, 3 + 5 ⋅ (m - 1) × m + 7 ⋅ m + 2 ⋅ m × (max(1, m)) + 2 ⋅ (1+max(0, m)), OnUIThread:false, [{1+max(0, m)},Loop at line 28, column 3,{max(1, m)},call to do_n_times,Loop at line 12, column 3,{m},Loop at line 28, column 3,{m},Loop at line 28, column 3,{m - 1},call to do_n_times,Loop at line 12, column 3] +codetoanalyze/objc/performance/instantiate.m, do_m2_times_quadratic, 3 + 7 ⋅ m + 5 ⋅ m × m + 2 ⋅ m × (1+max(0, m)) + 2 ⋅ (1+max(0, m)), OnUIThread:false, [{1+max(0, m)},Loop at line 21, column 3,{1+max(0, m)},call to do_n_times,Loop at line 12, column 3,{m},call to do_n_times,Loop at line 12, column 3,{m},Loop at line 21, column 3] +codetoanalyze/objc/performance/instantiate.m, do_n_times, 3 + 5 ⋅ n + 2 ⋅ (1+max(0, n)), OnUIThread:false, [{1+max(0, n)},Loop at line 12, column 3,{n},Loop at line 12, column 3] +codetoanalyze/objc/performance/instantiate.m, no_op, 2, OnUIThread:false, [] +codetoanalyze/objc/performance/invariant.m, do_n_m_times_nested, 7 + 5 ⋅ n + 3 ⋅ n × m + 2 ⋅ n × (1+max(0, m)) + 2 ⋅ (1+max(0, n)), OnUIThread:false, [{1+max(0, n)},Loop at line 24, column 3,{1+max(0, m)},Loop at line 25, column 5,{m},Loop at line 25, column 5,{n},Loop at line 24, column 3] +codetoanalyze/objc/performance/invariant.m, two_loops_nested_invariant, 6 + 23 ⋅ p + 2 ⋅ (1+max(0, p)), OnUIThread:false, [{1+max(0, p)},Loop at line 34, column 3,{p},Loop at line 34, column 3] +codetoanalyze/objc/performance/invariant.m, while_infinite_FN, 2, OnUIThread:false, [] +codetoanalyze/objc/performance/invariant.m, while_unique_def_FN, 15, OnUIThread:false, [] +codetoanalyze/objc/performance/jump_inside_loop.m, jump_inside_loop_constant_linear, 9 + (k - 1) + 4 ⋅ (max(1, k)), OnUIThread:false, [{max(1, k)},Loop at line 36, column 3,{k - 1},Loop at line 36, column 3] +codetoanalyze/objc/performance/jump_inside_loop.m, loop_always_linear, 7 + k + 2 ⋅ (max(1, k)) + 2 ⋅ (1+max(1, k)), OnUIThread:false, [{1+max(1, k)},Loop at line 21, column 3,{max(1, k)},Loop at line 21, column 3,{k},Loop at line 21, column 3] +codetoanalyze/objc/performance/loops.m, __infer_globals_initializer_array1, 4, OnUIThread:false, [] +codetoanalyze/objc/performance/loops.m, __infer_globals_initializer_array2, 2, OnUIThread:false, [] +codetoanalyze/objc/performance/loops.m, do_while_independent_of_p, 228, OnUIThread:false, [] +codetoanalyze/objc/performance/loops.m, if_in_loop, 324, OnUIThread:false, [] +codetoanalyze/objc/performance/loops.m, if_out_loop, 515, OnUIThread:false, [] +codetoanalyze/objc/performance/loops.m, larger_state_FN, 1005, OnUIThread:false, [] +codetoanalyze/objc/performance/loops.m, loop_use_global_vars, 4 + 4 ⋅ x + 2 ⋅ (1+max(0, x)), OnUIThread:false, [{1+max(0, x)},Loop at line 69, column 3,{x},Loop at line 69, column 3] +codetoanalyze/objc/performance/loops.m, ptr_cmp, 5 + 5 ⋅ size + 2 ⋅ (2+max(-1, size)), OnUIThread:false, [{2+max(-1, size)},Loop at line 76, column 3,{size},Loop at line 76, column 3] +codetoanalyze/objc/performance/purity.m, loop, 7007, OnUIThread:false, [] +codetoanalyze/objc/performance/switch_continue.m, test_switch_FN, 601, OnUIThread:false, [] +codetoanalyze/objc/performance/switch_continue.m, unroll_loop, 16 + (n - 1) + 11 ⋅ (max(1, n)), OnUIThread:false, [{max(1, n)},Loop at line 43, column 11,{n - 1},Loop at line 43, column 11] +codetoanalyze/objc/performance/two_loops_symbolic.m, nop, 2, OnUIThread:false, [] +codetoanalyze/objc/performance/two_loops_symbolic.m, two_loops_symb_diff, 8 + 5 ⋅ k + 5 ⋅ m + 2 ⋅ (1+max(0, k)) + 2 ⋅ (1+max(0, m)), OnUIThread:false, [{1+max(0, m)},Loop at line 12, column 3,{1+max(0, k)},Loop at line 15, column 3,{m},Loop at line 12, column 3,{k},Loop at line 15, column 3] diff --git a/infer/tests/codetoanalyze/objc/performance/cost_test.m b/infer/tests/codetoanalyze/objc/performance/cost_test.m new file mode 100644 index 00000000000..021f8e3efd2 --- /dev/null +++ b/infer/tests/codetoanalyze/objc/performance/cost_test.m @@ -0,0 +1,93 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +#include + +int foo_constant() { + int i, j; + i = 17; + j = 31; + return i + j + 3 + 7; +} + +int cond_constant(int i) { + int x; + + if (i < 0) { + x = foo_constant(); + } else { + x = 1; + } + return x; +} + +int loop0_constant() { + + for (int i = 0; i < 100; i++) { + foo_constant(); + } + return 0; +} + +int loop3_constant(int k) { + + for (int i = k; i < k + 15; i++) { + alias2(); + } + return 0; +} + +// Expected: O(20-m) +int while_upto20(int m) { + while (m < 20) { + int l = 0; + m++; + } + return m; +} + +void call_while_upto20_minus100_constant() { while_upto20(-100); } + +void call_while_upto20_10_constant() { while_upto20(10); } + +void call_while_upto20_unsigned(unsigned x) { while_upto20(x); } + +// Cost: 1 +void unit_cost_function() {} + +int always(int i) { return i % 2 == (i + 2) % 2; } + +void infinite_FN() { + int z; + for (int i = 0; always(i); i++) { + z += i; + } +} + +void infinite() { + int z; + for (int i = 0; i % 2 == (i + 2) % 2; i++) { + z += i; + } +} + +void call_infinite() { infinite(); } + +void loop_character_symbols_linear(char c) { + for (; c < 'z';) { + if (rand()) { + c = 'a'; + } + } +} + +unsigned int div_const(unsigned int n) { return n / 2; } + +void iter_div_const_constant() { + unsigned int n = div_const(20); + for (int i = 0; i < n; i++) { + } +} diff --git a/infer/tests/codetoanalyze/objc/performance/cost_test_deps.m b/infer/tests/codetoanalyze/objc/performance/cost_test_deps.m new file mode 100644 index 00000000000..665a1293d24 --- /dev/null +++ b/infer/tests/codetoanalyze/objc/performance/cost_test_deps.m @@ -0,0 +1,176 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// Tests that exercise precision of the analysis on control variables + +// -- Below examples didn't work before, but enhancing CF analysis +// makes the analysis much more precise and we can get proper bounds +// +// This example works now because even though j in [-oo.+oo], +// since control vars={k} (notice that we will remove {p,j} in the else branch), +// we ignore j and find the right bound for the inner loop +int if_bad_constant(int j) { + int p = 10; + if (p < 10 + j) { + p++; + } else { + p = j + 3; + for (int k = 0; k < 10; k++) { + j += 3; + } + } + return p; +} + +// Notice that removing {j,p} above doesn't create any problems if we are in a +// loop that depends on them. E.g.: below we still depend on {j} but in the +// conditional prune statement, we will remove the temp. var that map to inner +// {j}, not the outer {j} +int if_bad_loop_constant() { + int p = 10; + for (int j = 0; j < 5; j++) { + if (j < 2) { + p++; + } else { + p = 3; + for (int k = 0; k < 10; k++) { + int m = 0; + } + } + } + return p; +} + +// The fake dependency btw first and second loop disappeared and we can get a +// proper bound +// +int two_loops() { + int p = 10; + int k = 3; + int t = 2 + k; + for (int j = 0; j < 6; j++) { + k++; + } + for (int i = 0; i < 100; i++) { + p = 3; + } + return p; +} + +// We don't get a false dependency to m (hence p) since +// for if statements, we don't add prune variables as dependency +int loop_despite_inferbo_constant(int p) { + + int k = 100; + for (int i = 0; i < k; i++) { + int m = p + 3; + if (m < 14) { + p += 9; + } + } + return p; +} + +int nested_loop_constant() { + int k = 0; + for (int i = 0; i < 5; i++) { + A: + k = 0; + for (int j = 0; j < 100; j++) { + k = 3; + } + } + return k; +} + +// Unlike the above program, B will be inside the inner loop, hence executed +// around 105 times +int simulated_nested_loop_constant(int p) { + int k = 0; + int t = 5; + int j = 0; + for (int i = 0; i < 5; i++) { + B: + t = 3; + j++; + if (j < 100) + goto B; // continue; + } + return k; +} + +// B will be inside the inner loop and executed ~500 times +int simulated_nested_loop_more_expensive_constant(int p) { + int k = 0; + int t = 5; + int j = 0; + for (int i = 0; i < 5; i++) { + B: + t = 3; + j++; + if (j < 100) + goto B; // continue; + else { + j = 0; + } + } + return k; +} + +int real_while_constant() { + int i = 0; + int j = 3 * i; + while (i < 30) { + j = j + i; + i++; + } + return j; +} + +// Examples with gotos + +/* The following program is the version of real_while() with gotos */ + +int simulated_while_constant() { + int i = 0; + int j = 3 * i; +LOOP_COND: + if (i < 30) { + goto INCR; + } else { + goto RETURN; + } +INCR: + j = j + i; + i++; + goto LOOP_COND; +RETURN: + return j; +} + +/* Conditional inside goto loop */ +/* Expected: 5 * 100 */ +int simulated_nested_loop_cond_in_goto_constant(int p) { + int k = 0; + int t = 5; + int j = 0; + for (int i = 0; i < 5; i++) { + B: + if (i > 2) { + t = 3; + } else { + t = 4; + } + j++; + if (j >= 100) + j = 0; + else { + goto B; // continue; + } + } + return k; +} diff --git a/infer/tests/codetoanalyze/objc/performance/exit.m b/infer/tests/codetoanalyze/objc/performance/exit.m new file mode 100644 index 00000000000..a7df75c44c6 --- /dev/null +++ b/infer/tests/codetoanalyze/objc/performance/exit.m @@ -0,0 +1,35 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +void exit_unreachable() { + exit(0); // modeled as unreachable +} + +// constraint solver resolves all nodes to unreachable cost +void compute_exit_unreachable() { + int k = 0; + exit(0); +} + +void linear(int p) { + for (int i = 0; i < p; i++) { + } +} + +void call_exit_unreachable(int p) { + linear(p); + exit(0); +} + +// constraint solver doesn't behave consistently and gets confused +// when resolving constraints and gets linear cost +void inline_exit_unreachable_FP(int p) { + for (int i = 0; i < p; i++) { + } + exit(0); +} + +void call_unreachable_constant() { exit_unreachable_unreachable(); } diff --git a/infer/tests/codetoanalyze/objc/performance/instantiate.m b/infer/tests/codetoanalyze/objc/performance/instantiate.m new file mode 100644 index 00000000000..a847037cc7d --- /dev/null +++ b/infer/tests/codetoanalyze/objc/performance/instantiate.m @@ -0,0 +1,31 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +void no_op() { int x = 0; } + +// Expected: O(n) +void do_n_times(int n) { + for (int i = 0; i < n; i++) { + no_op(); + } +} + +void do_2_times_constant() { do_n_times(2); } + +// Expected: O(m^2) +void do_m2_times_quadratic(int m) { + for (int i = 0; i < m; i++) { + do_n_times(m); + } +} + +// Expected: O(m^2) +void do_half_m2_times_quadratic(int m) { + for (int i = 0; i < m; i++) { + do_n_times(i); + } +} diff --git a/infer/tests/codetoanalyze/objc/performance/invariant.m b/infer/tests/codetoanalyze/objc/performance/invariant.m new file mode 100644 index 00000000000..7e520df2c1d --- /dev/null +++ b/infer/tests/codetoanalyze/objc/performance/invariant.m @@ -0,0 +1,49 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +/* Depending on whether p is positive, the program might loop forever */ +int while_unique_def_FN(int p) { + int j = 0; + while (j < 10) { + if (p > 0) { + j = 500; + } else { + j = -1; + } + } + return 0; +} + +/* Expected O(n * m) */ +void do_n_m_times_nested(int n, int m) { + int k = n; + int p = m; + for (int i = 0; i < k; i++) { + for (int j = 0; j < p; j++) { + } + } +} + +/* Expected:O(p). Also inner loop will have t as invariant */ +void two_loops_nested_invariant(int p) { + int t = 0; + int m = p; + for (int i = 0; i < m; i++) { + t = 3; + for (int j = 0; j < t; j++) { + } + } +} + +/* since the guard i is invariant, we will remove it from control + variables, and hence the total cost will be constant, but the + program diverges */ +int while_infinite_FN() { + int i = 0; + while (i < 10) { + } + return 0; +} diff --git a/infer/tests/codetoanalyze/objc/performance/issues.exp b/infer/tests/codetoanalyze/objc/performance/issues.exp index 83fff3708f6..200eda79912 100644 --- a/infer/tests/codetoanalyze/objc/performance/issues.exp +++ b/infer/tests/codetoanalyze/objc/performance/issues.exp @@ -1,2 +1,32 @@ +codetoanalyze/objc/performance/compound_loop_guard.m, compound_while, 3, CONDITION_ALWAYS_TRUE, no_bucket, WARNING, [Here] +codetoanalyze/objc/performance/compound_loop_guard.m, nested_while_and_or_constant, 3, CONDITION_ALWAYS_TRUE, no_bucket, WARNING, [Here] +codetoanalyze/objc/performance/compound_loop_guard.m, nested_while_and_or_constant, 3, CONDITION_ALWAYS_TRUE, no_bucket, WARNING, [Here] +codetoanalyze/objc/performance/compound_loop_guard.m, nested_while_and_or_constant, 4, CONDITION_ALWAYS_TRUE, no_bucket, WARNING, [Here] +codetoanalyze/objc/performance/compound_loop_guard.m, nested_while_and_or_constant, 4, CONDITION_ALWAYS_TRUE, no_bucket, WARNING, [Here] +codetoanalyze/objc/performance/compound_loop_guard.m, simplified_simulated_while_with_and_constant, 5, CONDITION_ALWAYS_TRUE, no_bucket, WARNING, [Here] +codetoanalyze/objc/performance/compound_loop_guard.m, simulated_nested_loop_with_and_constant, 7, INTEGER_OVERFLOW_L5, no_bucket, ERROR, [,Assignment,Binary operation: ([0, +oo] + 1):signed32] +codetoanalyze/objc/performance/compound_loop_guard.m, simulated_nested_loop_with_and_constant, 8, CONDITION_ALWAYS_TRUE, no_bucket, WARNING, [Here] +codetoanalyze/objc/performance/compound_loop_guard.m, simulated_while_shortcut_constant, 5, CONDITION_ALWAYS_FALSE, no_bucket, WARNING, [Here] +codetoanalyze/objc/performance/compound_loop_guard.m, simulated_while_with_and_linear, 4, CONDITION_ALWAYS_TRUE, no_bucket, WARNING, [Here] +codetoanalyze/objc/performance/compound_loop_guard.m, while_and_or, 0, INFINITE_EXECUTION_TIME, no_bucket, ERROR, [Unbounded loop,Loop at line 62, column 3] +codetoanalyze/objc/performance/compound_loop_guard.m, while_and_or, 2, CONDITION_ALWAYS_TRUE, no_bucket, WARNING, [Here] +codetoanalyze/objc/performance/compound_loop_guard.m, while_and_or, 3, INTEGER_OVERFLOW_L5, no_bucket, ERROR, [,Assignment,Binary operation: ([0, +oo] + 1):signed32] codetoanalyze/objc/performance/control.m, wrong_cvar_FP, 0, INFINITE_EXECUTION_TIME, no_bucket, ERROR, [Unbounded loop,Loop at line 20, column 3] codetoanalyze/objc/performance/control.m, wrong_cvar_FP, 2, CONDITION_ALWAYS_TRUE, no_bucket, WARNING, [Here] +codetoanalyze/objc/performance/cost_test.m, call_infinite, 0, INFINITE_EXECUTION_TIME, no_bucket, ERROR, [Call to infinite,Unbounded loop,Loop at line 72, column 3] +codetoanalyze/objc/performance/cost_test.m, infinite, 0, INFINITE_EXECUTION_TIME, no_bucket, ERROR, [Unbounded loop,Loop at line 72, column 3] +codetoanalyze/objc/performance/cost_test.m, infinite, 3, INTEGER_OVERFLOW_L5, no_bucket, ERROR, [,Assignment,,Assignment,Binary operation: ([-oo, +oo] + [0, +oo]):signed32] +codetoanalyze/objc/performance/cost_test.m, infinite_FN, 3, INTEGER_OVERFLOW_L5, no_bucket, ERROR, [,Assignment,,Assignment,Binary operation: ([-oo, +oo] + [0, +oo]):signed32] +codetoanalyze/objc/performance/cost_test_deps.m, if_bad_loop_constant, 4, INTEGER_OVERFLOW_L5, no_bucket, ERROR, [,Assignment,Binary operation: ([0, +oo] + 1):signed32] +codetoanalyze/objc/performance/cost_test_deps.m, real_while_constant, 4, INTEGER_OVERFLOW_L5, no_bucket, ERROR, [,Assignment,Assignment,,Assignment,Binary operation: ([0, +oo] + [0, 29]):signed32] +codetoanalyze/objc/performance/cost_test_deps.m, simulated_nested_loop_constant, 7, INTEGER_OVERFLOW_L5, no_bucket, ERROR, [,Assignment,Binary operation: ([0, +oo] + 1):signed32] +codetoanalyze/objc/performance/cost_test_deps.m, simulated_while_constant, 10, INTEGER_OVERFLOW_L5, no_bucket, ERROR, [,Assignment,Assignment,,Assignment,Binary operation: ([0, +oo] + [0, 29]):signed32] +codetoanalyze/objc/performance/cost_test_deps.m, two_loops, 5, INTEGER_OVERFLOW_L5, no_bucket, ERROR, [,Assignment,Binary operation: ([3, +oo] + 1):signed32] +codetoanalyze/objc/performance/exit.m, call_exit_unreachable, 0, EXECUTION_TIME_UNREACHABLE_AT_EXIT, no_bucket, ERROR, [Unreachable node] +codetoanalyze/objc/performance/exit.m, compute_exit_unreachable, 0, EXECUTION_TIME_UNREACHABLE_AT_EXIT, no_bucket, ERROR, [Unreachable node] +codetoanalyze/objc/performance/exit.m, exit_unreachable, 0, EXECUTION_TIME_UNREACHABLE_AT_EXIT, no_bucket, ERROR, [Unreachable node] +codetoanalyze/objc/performance/invariant.m, while_infinite_FN, 2, CONDITION_ALWAYS_TRUE, no_bucket, WARNING, [Here] +codetoanalyze/objc/performance/loops.m, if_in_loop, 5, INTEGER_OVERFLOW_L5, no_bucket, ERROR, [,Assignment,Binary operation: ([0, +oo] + 1):signed32] +codetoanalyze/objc/performance/switch_continue.m, test_switch_FN, 3, CONDITION_ALWAYS_TRUE, no_bucket, WARNING, [Here] +codetoanalyze/objc/performance/switch_continue.m, unroll_loop, 6, INTEGER_OVERFLOW_L5, no_bucket, ERROR, [,Assignment,Binary operation: ([0, +oo] + 1):signed32] +codetoanalyze/objc/performance/switch_continue.m, unroll_loop, 9, CONDITION_ALWAYS_TRUE, no_bucket, WARNING, [Here] diff --git a/infer/tests/codetoanalyze/objc/performance/jump_inside_loop.m b/infer/tests/codetoanalyze/objc/performance/jump_inside_loop.m new file mode 100644 index 00000000000..8004e96025c --- /dev/null +++ b/infer/tests/codetoanalyze/objc/performance/jump_inside_loop.m @@ -0,0 +1,40 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +/* With the dominator approach, we can't find any back-edges here + since there are two entry points to the loop and there is no single + back edge to a single loop entry point, but only to the beginning + of the Loop label. With Tarjan's DFS approach, we can identify the + back-edge to the Loop label, and we are able to detect two + exit-edges correctly. + */ +int loop_always_linear(int p, int k) { + int i = 0; + if (p > 0) { + goto Loop; + } + + while (i < k) { + Loop: + i++; + } + return 1; +} + +int jump_inside_loop_constant_linear(int p, int k) { + int i = 0; + if (p > 0) { + goto Loop; + } else { + return p; + } + while (i < k) { + Loop: + i++; + } + return 1; +} diff --git a/infer/tests/codetoanalyze/objc/performance/loops.m b/infer/tests/codetoanalyze/objc/performance/loops.m new file mode 100644 index 00000000000..27be258b913 --- /dev/null +++ b/infer/tests/codetoanalyze/objc/performance/loops.m @@ -0,0 +1,81 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +// j is not a control var, so shouldn't affect the bound +int if_in_loop(int t) { + int p = 0; + int j = t + 1; + for (int i = 0; i < 5; i++) { + if (j < 2) { + p++; + } else { + p = 3; + for (int k = 0; k < 10; k++) { + int m = 0; + } + } + } + return p; +} + +// j is not a control var, so shouldn't affect the bound +int if_out_loop(int t) { + int p = 10; + int j = t + 10; + if (j < 2) { + p++; + } else { + p = 3; + for (int k = 0; k < 100; k++) { + int m = 0; + } + } + return p; +} + +int do_while_independent_of_p(int p) { + int a = 0; + do { + if (p == 15) { + p = p + 1; + } + a++; + } while (a < 25); + + return 0; +} + +void larger_state_FN() { + + int i = 0, k = 0; + while (k < 100) { + i++; + if (i >= 10000) { + k++; + i = 0; + } + } +} + +static int array1[] = {1, 2, 3}; +static int array2[] = {}; + +// Cvars will initially contain array1 and array2 but will be removed +// since they are invariant +void loop_use_global_vars(int x) { + for (int i = 0; i < x && array1 != array2; i++) { + // do something + } +} + +void ptr_cmp(char* end, int size) { + char buf[2] = "hi"; + for (int i = 0; i < size; i += 2) { + if (buf < end) { // pvar &buf occurs directly in prune node + return; + } + } +} diff --git a/infer/tests/codetoanalyze/objc/performance/purity.m b/infer/tests/codetoanalyze/objc/performance/purity.m new file mode 100644 index 00000000000..0d3da5f545f --- /dev/null +++ b/infer/tests/codetoanalyze/objc/performance/purity.m @@ -0,0 +1,16 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +void (*fun_ptr)(int); +// just to sanity check that we don't fail on cost with purity analysis enabled +int loop(int k) { + int p = 0; + for (int i = 0; i < 1000; i++) { + p = 3; + (*fun_ptr)(10); + } + return p; +} diff --git a/infer/tests/codetoanalyze/objc/performance/switch_continue.m b/infer/tests/codetoanalyze/objc/performance/switch_continue.m new file mode 100644 index 00000000000..5a3f832cd55 --- /dev/null +++ b/infer/tests/codetoanalyze/objc/performance/switch_continue.m @@ -0,0 +1,47 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +int test_switch_FN() { + int value = 0; + // infinite loop + while (value < 100) { + switch (value) { + // code before the first case statement gets skipped but can be used to + // declare variables + int x = 1; + x = value + 1; + case 0: + break; + case 1: + continue; + case 2: + default: + continue; + } + value++; + } + return 0; +} + +int unroll_loop(int n) { + int ret = 0; + int loop = n + 3 / 4; + switch (n % 8) { + case 0: + do { + ret++; + case 3: + ret++; + if (1) { + case 2: + ret++; + } + case 1: + ret++; + } while (--loop > 0); + } + return ret; +} diff --git a/infer/tests/codetoanalyze/objc/performance/two_loops_symbolic.m b/infer/tests/codetoanalyze/objc/performance/two_loops_symbolic.m new file mode 100644 index 00000000000..38460e6e38c --- /dev/null +++ b/infer/tests/codetoanalyze/objc/performance/two_loops_symbolic.m @@ -0,0 +1,19 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +void nop() { int k = 0; } + +// Expected: O(m + k) +int two_loops_symb_diff(int m, int k) { + int p = 10; + for (int i = 0; i < m; i++) { + nop(); + } + for (int j = 0; j < k; j++) { + nop(); + } + return p; +}