From 0c365136b1f2f01751efd9aef60fa5e7759ac53f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ezgi=20=C3=87i=C3=A7ek?= Date: Thu, 18 Jun 2020 03:24:43 -0700 Subject: [PATCH] [doc] Add documentation for hoisting and invariant calls Reviewed By: skcho Differential Revision: D22090818 fbshipit-source-id: 86c4bdaf9 --- infer/documentation/checkers/LoopHoisting.md | 3 +++ .../issues/EXPENSIVE_LOOP_INVARIANT_CALL.md | 22 +++++++++++++++++++ infer/documentation/issues/INVARIANT_CALL.md | 19 ++++++++++++++++ infer/src/base/Checker.ml | 5 ++++- infer/src/base/IssueType.ml | 6 ++++- 5 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 infer/documentation/checkers/LoopHoisting.md create mode 100644 infer/documentation/issues/EXPENSIVE_LOOP_INVARIANT_CALL.md create mode 100644 infer/documentation/issues/INVARIANT_CALL.md diff --git a/infer/documentation/checkers/LoopHoisting.md b/infer/documentation/checkers/LoopHoisting.md new file mode 100644 index 000000000..99c45f50a --- /dev/null +++ b/infer/documentation/checkers/LoopHoisting.md @@ -0,0 +1,3 @@ +This checker detects opportunities to hoist function calls that are invariant to outside of loop bodies. The hoisting analysis relies on [purity](/docs/next/checker-purity) analysis to determine whather a function is pure or not. + +It has an additional mode that reports [loop-invariant functions that are expensive](/docs/next/all-issue-types#expensive_loop_invariant_call) (i.e. at least linear). This is enabled by the flag `--hoisting-report-only-expensive`. diff --git a/infer/documentation/issues/EXPENSIVE_LOOP_INVARIANT_CALL.md b/infer/documentation/issues/EXPENSIVE_LOOP_INVARIANT_CALL.md new file mode 100644 index 000000000..b0eab6c0d --- /dev/null +++ b/infer/documentation/issues/EXPENSIVE_LOOP_INVARIANT_CALL.md @@ -0,0 +1,22 @@ +We report this issue type when a function is [loop-invariant](/docs/next/all-issue-types#invariant_call) and also expensive (i.e. at least has linear complexity as determined by the [cost](/docs/next/checker-cost) analysis). + +```java +int incr(int x) { + return x + 1; +} + +// incr will not be hoisted since it is cheap(constant time) +void foo_linear(int size) { + int x = 10; + for (int i = 0; i < size; i++) { + incr(x); // constant call, don't hoist + } +} + +// call to foo_linear will be hoisted since it is expensive(linear in size). +void symbolic_expensive_hoist(int size) { + for (int i = 0; i < size; i++) { + foo_linear(size); // hoist + } +} +``` diff --git a/infer/documentation/issues/INVARIANT_CALL.md b/infer/documentation/issues/INVARIANT_CALL.md new file mode 100644 index 000000000..45561cf26 --- /dev/null +++ b/infer/documentation/issues/INVARIANT_CALL.md @@ -0,0 +1,19 @@ +We report this issue type when a function call is loop-invariant and hoistable, i.e. +- the function has no side side effects (pure) +- has invariant arguments and result (i.e. have the same value in all loop iterations) +- it is guaranteed to execute, i.e. it dominates all loop sources + +```java +int foo(int x, int y) { + return x + y; +} + + +void invariant_hoist(int size) { + int x = 10; + int y = 5; + for (int i = 0; i < size; i++) { + foo(x, y); // hoistable + } + } +``` diff --git a/infer/src/base/Checker.ml b/infer/src/base/Checker.ml index a802d6b45..e15c0670e 100644 --- a/infer/src/base/Checker.ml +++ b/infer/src/base/Checker.ml @@ -238,7 +238,10 @@ let config_unsafe checker = ; activates= [] } | LoopHoisting -> { id= "loop-hoisting" - ; kind= UserFacing {title= "Loop Hoisting"; markdown_body= ""} + ; kind= + UserFacing + { title= "Loop Hoisting" + ; markdown_body= [%blob "../../documentation/checkers/LoopHoisting.md"] } ; support= supports_clang_and_java ; short_documentation= "Detect opportunities to hoist function calls that are invariant outside of loop bodies \ diff --git a/infer/src/base/IssueType.ml b/infer/src/base/IssueType.ml index f92b6dc69..4ecd1775c 100644 --- a/infer/src/base/IssueType.ml +++ b/infer/src/base/IssueType.ml @@ -700,7 +700,10 @@ let internal_error = register_from_string ~visibility:Developer ~id:"Internal_error" Error Biabduction -let invariant_call = register_from_string ~enabled:false ~id:"INVARIANT_CALL" Error LoopHoisting +let invariant_call = + register_from_string ~enabled:false ~id:"INVARIANT_CALL" Error LoopHoisting + ~user_documentation:[%blob "../../documentation/issues/INVARIANT_CALL.md"] + let javascript_injection = register_from_string ~id:"JAVASCRIPT_INJECTION" Error Quandary @@ -738,6 +741,7 @@ let logging_private_data = let expensive_loop_invariant_call = register_from_string ~id:"EXPENSIVE_LOOP_INVARIANT_CALL" Error LoopHoisting + ~user_documentation:[%blob "../../documentation/issues/EXPENSIVE_LOOP_INVARIANT_CALL.md"] let memory_leak =