ESBMC
Efficient SMT-based Context-Bounded Model Checker
Loading...
Searching...
No Matches
goto_loop_invariant.cpp File Reference
#include <goto-programs/goto_loop_invariant.h>
#include <goto-programs/frame_enforcer.h>
#include <goto-programs/remove_no_op.h>
#include <util/lang/c_types.h>
#include <util/expr/expr_util.h>
#include <util/base/i2string.h>
#include <util/irep/std_expr.h>
#include <util/config/options.h>
#include <irep2/irep2_utils.h>
#include <functional>
#include <map>
Include dependency graph for goto_loop_invariant.cpp:

Namespaces

namespace  loop_invariant

Functions

bool loop_invariant::is_inert_scan_instruction (goto_programt::const_targett t)
 Instructions with no effect a caller needs to account for: no write to a tracked variable, no control-flow or heap/external effect.
static bool is_generated_name (const irep_idt &name)
 True for a name the frontend generated rather than the user: ESBMC spells those with a '$' (e.g.
static bool is_compiler_temp (goto_programt::const_targett t)
 Returns true if the instruction is a compiler-generated DECL, ASSIGN, or FUNCTION_CALL.
static std::vector< expr2tc > extract_invariants_near (goto_programt::targett loop_head, goto_programt::targett begin)
 Walk backwards from loop_head (up to kMaxInvariantSearchBack steps) and return the invariant expression(s) for the nearest LOOP_INVARIANT found.
void goto_loop_invariant (goto_functionst &goto_functions, contextt &context, bool use_frame_rule)
 Entry point: process loop invariants for all functions.
static bool is_trivial_rhs (const expr2tc &expr)
 Return true if expr is a trivial RHS (constant 0/1, constant bool, or NONDET).
static bool is_likely_compiler_temp (const irep_idt &id)
 Heuristic: compiler-generated temporaries (e.g.
static bool symbol_name_matches (const irep_idt &full_or_short, const irep_idt &decl_id, bool allow_suffix)
 True if full_or_short matches decl_id: either exact match, or (when allow_suffix) full_or_short ends with "::" + decl_id.
static void collect_symbols_local (const expr2tc &expr, std::set< irep_idt > &symbols)
 Local helper: collect all symbol names reachable from an expression tree.
static void extract_and_remove_side_effects_impl (goto_functiont &goto_function, goto_programt::targett loop_head, const loopst &loop, const std::vector< expr2tc > &invariants, goto_programt &side_effects_out)
 Shared implementation used by both the legacy loop-invariant pass and the combined loop-invariant + k-induction pass.
void goto_loop_invariant_combined (goto_functionst &goto_functions)
 Combined loop-invariant + k-induction mode.
static expr2tc loop_entry_cond (goto_programt::targett loop_head)
 The condition under which a while or for loop is entered: the negation of its head IF's guard, which ASSUME(entry_cond) stands in for so the head need not be copied.
static goto_programt::targett loop_body_begin (goto_programt::targett loop_head, const expr2tc &entry_cond)
 Where the copied body starts: after the head when ASSUME(entry_cond) stands in for it, at the head otherwise, because a do-while head is a body instruction and skipping it left the verification branch empty (#7494).
static void assume_continue_cond (goto_programt::targett loop_exit, goto_programt &out)
 Constrain the copied iteration to one another would follow.

Variables

const char *const kSynthesisedInvariantProperty = "synthesised-loop-invariant"
 Property tag that goto_synthesise_loop_invariants stamps on the LOOP_INVARIANT markers it emits.

Function Documentation

◆ assume_continue_cond()

void assume_continue_cond ( goto_programt::targett loop_exit,
goto_programt & out )
static

Constrain the copied iteration to one another would follow.

A do-while's exit test lives on its back edge, so the copy may be the last iteration, and a head invariant need not hold where the loop exits: x <= 4 on do x++; while (x < 5) holds at every head and is false once the exiting iteration leaves x == 5. No-op for the unconditional back edge of a while or for loop (issue #7494).

◆ collect_symbols_local()

void collect_symbols_local ( const expr2tc & expr,
std::set< irep_idt > & symbols )
static

Local helper: collect all symbol names reachable from an expression tree.

◆ extract_and_remove_side_effects_impl()

void extract_and_remove_side_effects_impl ( goto_functiont & goto_function,
goto_programt::targett loop_head,
const loopst & loop,
const std::vector< expr2tc > & invariants,
goto_programt & side_effects_out )
static

Shared implementation used by both the legacy loop-invariant pass and the combined loop-invariant + k-induction pass.

See the declaration of goto_loop_invariantt::extract_and_remove_side_effects for a high-level description; this helper just takes an explicit goto_function parameter so it can be reused from both passes.

◆ extract_invariants_near()

std::vector< expr2tc > extract_invariants_near ( goto_programt::targett loop_head,
goto_programt::targett begin )
static

Walk backwards from loop_head (up to kMaxInvariantSearchBack steps) and return the invariant expression(s) for the nearest LOOP_INVARIANT found.

Multiple conjuncts are folded into a single and2tc. Returns an empty vector when no annotated LOOP_INVARIANT is in range.

◆ goto_loop_invariant()

void goto_loop_invariant ( goto_functionst & goto_functions,
contextt & context,
bool use_frame_rule )

Entry point: process loop invariants for all functions.

When use_frame_rule is true, enables the observational frame rule (Snapshot → Havoc → Assume) for enhanced inductive verification.

◆ goto_loop_invariant_combined()

void goto_loop_invariant_combined ( goto_functionst & goto_functions)

Combined loop-invariant + k-induction mode.

Inserts a non-deterministic "Branch 1" verification block immediately before each annotated loop. Branch 1 checks that the user-supplied invariant is actually inductive (base case + inductive step), using specially tagged ASSERT instructions so the verifier can distinguish invariant failures from ordinary program assertion failures.

The original loop is left untouched for subsequent transformation by goto_k_induction (Branch 2), which adds ASSUME(invariant) after its nondet-assign step to tighten the inductive-step search space.

◆ is_compiler_temp()

bool is_compiler_temp ( goto_programt::const_targett t)
static

Returns true if the instruction is a compiler-generated DECL, ASSIGN, or FUNCTION_CALL.

These may legitimately appear between consecutive __ESBMC_loop_invariant() calls. FUNCTION_CALL instructions arise when remove_function_call emits a DECL + FUNCTION_CALL pair for helper functions called inside an invariant.

◆ is_generated_name()

bool is_generated_name ( const irep_idt & name)
static

True for a name the frontend generated rather than the user: ESBMC spells those with a '$' (e.g.

return_value$___ESBMC_forall$N).

◆ is_likely_compiler_temp()

bool is_likely_compiler_temp ( const irep_idt & id)
static

Heuristic: compiler-generated temporaries (e.g.

for short-circuit evaluation) typically have '$' in their name; user variables do not. Used to avoid moving user variable DECLs/ASSIGNs while still collecting compiler temps.

◆ is_trivial_rhs()

bool is_trivial_rhs ( const expr2tc & expr)
static

Return true if expr is a trivial RHS (constant 0/1, constant bool, or NONDET).

We skip collecting such assignments when tracing back from the invariant so that we only pull in the "real" definition (e.g. from a function call) and not the fallback or initialiser.

◆ loop_body_begin()

goto_programt::targett loop_body_begin ( goto_programt::targett loop_head,
const expr2tc & entry_cond )
static

Where the copied body starts: after the head when ASSUME(entry_cond) stands in for it, at the head otherwise, because a do-while head is a body instruction and skipping it left the verification branch empty (#7494).

◆ loop_entry_cond()

expr2tc loop_entry_cond ( goto_programt::targett loop_head)
static

The condition under which a while or for loop is entered: the negation of its head IF's guard, which ASSUME(entry_cond) stands in for so the head need not be copied.

Nil for a do-while, whose head is not a conditional goto but the first instruction of the body – it always enters (issue #7494).

◆ symbol_name_matches()

bool symbol_name_matches ( const irep_idt & full_or_short,
const irep_idt & decl_id,
bool allow_suffix )
static

True if full_or_short matches decl_id: either exact match, or (when allow_suffix) full_or_short ends with "::" + decl_id.

Variable Documentation

◆ kSynthesisedInvariantProperty

const char* const kSynthesisedInvariantProperty = "synthesised-loop-invariant"

Property tag that goto_synthesise_loop_invariants stamps on the LOOP_INVARIANT markers it emits.

The extractor here accepts a tagged marker only when it sits immediately before the loop head it is being extracted for; a user-written __ESBMC_loop_invariant carries no tag and keeps the looser proximity rule. Without the tag the two passes share nothing but a 10-instruction window, and a synthesised invariant silently attaches itself to whichever loop happens to be nearby.