fn analyze(target: &FunctionTarget, targets: &FunctionTargetsHolder) -> Self {
        use BorrowNode::*;
        use Bytecode::*;
        use Operation::*;

        // collect information
        let fid = target.func_env.get_qualified_id();
        let env = target.global_env();
        let inv_analysis = env
            .get_extension::<InvariantAnalysisData>()
            .expect("Verification analysis not performed");

        let check_suspendable_inv_on_return =
            inv_analysis.fun_set_with_inv_check_on_exit.contains(&fid);
        let inv_applicability = inv_analysis
            .fun_to_inv_map
            .get(&fid)
            .expect("Invariant applicability not available");
        let fun_type_params_arity = target.get_type_parameter_count();

        let inv_ro = &inv_applicability.accessed;
        let (inv_rw_return, inv_rw_normal): (BTreeSet<_>, BTreeSet<_>) =
            if check_suspendable_inv_on_return {
                inv_applicability
                    .direct_modified
                    .iter()
                    .cloned()
                    .partition(|inv_id| is_invariant_suspendable(env, *inv_id))
            } else {
                (BTreeSet::new(), inv_applicability.direct_modified.clone())
            };

        // collect invariant applicability and instantiation information per bytecode, i.e.,
        // - which invariants should be instrumented after each instruction and
        // - per each invariant applicable, how to instantiate them.
        let mut entrypoint_assumptions = BTreeMap::new();
        let mut per_bytecode_assertions = BTreeMap::new();
        let mut exitpoint_assertions = BTreeMap::new();
        let mut ghost_type_param_count = 0;

        for (code_offset, bc) in target.data.code.iter().enumerate() {
            let code_offset = code_offset as CodeOffset;

            // collect memory accessed/modified in operations
            let (mem_ro, mem_rw) = match bc {
                Call(_, _, oper, _, _) => match oper {
                    Function(mid, fid, inst) => {
                        let callee_fid = mid.qualified(*fid);
                        get_callee_memory_usage_for_invariant_instrumentation(
                            env, targets, callee_fid, inst,
                        )
                    }
                    OpaqueCallBegin(mid, fid, inst) => {
                        let callee_fid = mid.qualified(*fid);
                        let (mem_ro, _) = get_callee_memory_usage_for_invariant_instrumentation(
                            env, targets, callee_fid, inst,
                        );
                        (mem_ro, BTreeSet::new())
                    }
                    OpaqueCallEnd(mid, fid, inst) => {
                        let callee_fid = mid.qualified(*fid);
                        let (_, mem_rw) = get_callee_memory_usage_for_invariant_instrumentation(
                            env, targets, callee_fid, inst,
                        );
                        (BTreeSet::new(), mem_rw)
                    }

                    MoveTo(mid, sid, inst) | MoveFrom(mid, sid, inst) => {
                        let mem = mid.qualified_inst(*sid, inst.to_owned());
                        (BTreeSet::new(), std::iter::once(mem).collect())
                    }
                    WriteBack(GlobalRoot(mem), _) => {
                        (BTreeSet::new(), std::iter::once(mem.clone()).collect())
                    }

                    Exists(mid, sid, inst) | GetGlobal(mid, sid, inst) => {
                        let mem = mid.qualified_inst(*sid, inst.to_owned());
                        (std::iter::once(mem).collect(), BTreeSet::new())
                    }

                    // shortcut other operations
                    _ => continue,
                },

                Prop(_, PropKind::Assert, exp) | Prop(_, PropKind::Assume, exp) => (
                    exp.used_memory(env)
                        .into_iter()
                        .map(|(usage, _)| usage)
                        .collect(),
                    BTreeSet::new(),
                ),

                // shortcut other bytecodes
                _ => continue,
            };

            // collect instantiation information (step 1)
            // - entrypoint assumptions arised from memories that are read-only from the bytecode
            let relevance_ro = Self::calculate_invariant_relevance(
                env,
                mem_ro.iter(),
                inv_ro,
                fun_type_params_arity,
                &mut ghost_type_param_count,
                /* ignore_uninstantiated_invariant */ true,
            );

            // collect instantiation information (step 2)
            // - invariants that need to be assumed and asserted for read-write operations
            let relevance_rw_normal = Self::calculate_invariant_relevance(
                env,
                mem_rw.iter(),
                &inv_rw_normal,
                fun_type_params_arity,
                &mut ghost_type_param_count,
                /* ignore_uninstantiated_invariant */ false,
            );
            let relevance_rw_return = Self::calculate_invariant_relevance(
                env,
                mem_rw.iter(),
                &inv_rw_return,
                fun_type_params_arity,
                &mut ghost_type_param_count,
                /* ignore_uninstantiated_invariant */ false,
            );

            // entrypoint assumptions are about both the ro invariants and rw invariants, and
            // regardless of whether they are checked in-place or deferred to the exit point.
            for (inv_id, inv_rel) in relevance_ro
                .iter()
                .chain(relevance_rw_normal.iter())
                .chain(relevance_rw_return.iter())
            {
                let inv = env.get_global_invariant(*inv_id).unwrap();
                if matches!(inv.kind, ConditionKind::GlobalInvariantUpdate(..)) {
                    // update invariants should not be assumed at function entrypoint
                    continue;
                }
                entrypoint_assumptions
                    .entry(*inv_id)
                    .or_insert_with(PerBytecodeRelevance::default)
                    .merge(inv_rel.clone());
            }

            // normal rw invariants are asserted in-place, right after the bytecode
            per_bytecode_assertions.insert(code_offset, relevance_rw_normal);

            // exitpoint assertions are only about the rw invariants deferred to the exit point.
            for (inv_id, inv_rel) in relevance_rw_return {
                exitpoint_assertions
                    .entry(inv_id)
                    .or_insert_with(PerBytecodeRelevance::default)
                    .merge(inv_rel);
            }
        }

        // wrap and return the analysis result
        Self {
            entrypoint_assumptions,
            per_bytecode_assertions,
            exitpoint_assertions,
            ghost_type_param_count,
        }
    }
/// Collect and build the relevance analysis information for this function target.
Collect and build the relevance analysis information for this function target.
Collect and build the relevance analysis information for this function target .
fn analyze(target: &FunctionTarget, targets: &FunctionTargetsHolder) -> Self {
        use BorrowNode::*;
        use Bytecode::*;
        use Operation::*;
        // collect information
        let fid = target.func_env.get_qualified_id();
        let env = target.global_env();
        let inv_analysis = env
            .get_extension::<InvariantAnalysisData>()
            .expect("Verification analysis not performed");
        let check_suspendable_inv_on_return =
            inv_analysis.fun_set_with_inv_check_on_exit.contains(&fid);
        let inv_applicability = inv_analysis
            .fun_to_inv_map
            .get(&fid)
            .expect("Invariant applicability not available");
        let fun_type_params_arity = target.get_type_parameter_count();
        let inv_ro = &inv_applicability.accessed;
        let (inv_rw_return, inv_rw_normal): (BTreeSet<_>, BTreeSet<_>) =
            if check_suspendable_inv_on_return {
                inv_applicability
                    .direct_modified
                    .iter()
                    .cloned()
                    .partition(|inv_id| is_invariant_suspendable(env, *inv_id))
            } else {
                (BTreeSet::new(), inv_applicability.direct_modified.clone())
            };
        // collect invariant applicability and instantiation information per bytecode, i.e.,
        // - which invariants should be instrumented after each instruction and
        // - per each invariant applicable, how to instantiate them.
        let mut entrypoint_assumptions = BTreeMap::new();
        let mut per_bytecode_assertions = BTreeMap::new();
        let mut exitpoint_assertions = BTreeMap::new();
        let mut ghost_type_param_count = 0;
        for (code_offset, bc) in target.data.code.iter().enumerate() {
            let code_offset = code_offset as CodeOffset;
            // collect memory accessed/modified in operations
            let (mem_ro, mem_rw) = match bc {
                Call(_, _, oper, _, _) => match oper {
                    Function(mid, fid, inst) => {
                        let callee_fid = mid.qualified(*fid);
                        get_callee_memory_usage_for_invariant_instrumentation(
                            env, targets, callee_fid, inst,
                        )
                    }
                    OpaqueCallBegin(mid, fid, inst) => {
                        let callee_fid = mid.qualified(*fid);
                        let (mem_ro, _) = get_callee_memory_usage_for_invariant_instrumentation(
                            env, targets, callee_fid, inst,
                        );
                        (mem_ro, BTreeSet::new())
                    }
                    OpaqueCallEnd(mid, fid, inst) => {
                        let callee_fid = mid.qualified(*fid);
                        let (_, mem_rw) = get_callee_memory_usage_for_invariant_instrumentation(
                            env, targets, callee_fid, inst,
                        );
                        (BTreeSet::new(), mem_rw)
                    }
                    MoveTo(mid, sid, inst) | MoveFrom(mid, sid, inst) => {
                        let mem = mid.qualified_inst(*sid, inst.to_owned());
                        (BTreeSet::new(), std::iter::once(mem).collect())
                    }
                    WriteBack(GlobalRoot(mem), _) => {
                        (BTreeSet::new(), std::iter::once(mem.clone()).collect())
                    }
                    Exists(mid, sid, inst) | GetGlobal(mid, sid, inst) => {
                        let mem = mid.qualified_inst(*sid, inst.to_owned());
                        (std::iter::once(mem).collect(), BTreeSet::new())
                    }
                    // shortcut other operations
                    _ => continue,
                },
                Prop(_, PropKind::Assert, exp) | Prop(_, PropKind::Assume, exp) => (
                    exp.used_memory(env)
                        .into_iter()
                        .map(|(usage, _)| usage)
                        .collect(),
                    BTreeSet::new(),
                ),
                // shortcut other bytecodes
                _ => continue,
            };
            // collect instantiation information (step 1)
            // - entrypoint assumptions arised from memories that are read-only from the bytecode
            let relevance_ro = Self::calculate_invariant_relevance(
                env,
                mem_ro.iter(),
                inv_ro,
                fun_type_params_arity,
                &mut ghost_type_param_count,
                /* ignore_uninstantiated_invariant */ true,
            );
            // collect instantiation information (step 2)
            // - invariants that need to be assumed and asserted for read-write operations
            let relevance_rw_normal = Self::calculate_invariant_relevance(
                env,
                mem_rw.iter(),
                &inv_rw_normal,
                fun_type_params_arity,
                &mut ghost_type_param_count,
                /* ignore_uninstantiated_invariant */ false,
            );
            let relevance_rw_return = Self::calculate_invariant_relevance(
                env,
                mem_rw.iter(),
                &inv_rw_return,
                fun_type_params_arity,
                &mut ghost_type_param_count,
                /* ignore_uninstantiated_invariant */ false,
            );
            // entrypoint assumptions are about both the ro invariants and rw invariants, and
            // regardless of whether they are checked in-place or deferred to the exit point.
            for (inv_id, inv_rel) in relevance_ro
                .iter()
                .chain(relevance_rw_normal.iter())
                .chain(relevance_rw_return.iter())
            {
                let inv = env.get_global_invariant(*inv_id).unwrap();
                if matches!(inv.kind, ConditionKind::GlobalInvariantUpdate(..)) {
                    // update invariants should not be assumed at function entrypoint
                    continue;
                }
                entrypoint_assumptions
                    .entry(*inv_id)
                    .or_insert_with(PerBytecodeRelevance::default)
                    .merge(inv_rel.clone());
            }
            // normal rw invariants are asserted in-place, right after the bytecode
            per_bytecode_assertions.insert(code_offset, relevance_rw_normal);
            // exitpoint assertions are only about the rw invariants deferred to the exit point.
            for (inv_id, inv_rel) in relevance_rw_return {
                exitpoint_assertions
                    .entry(inv_id)
                    .or_insert_with(PerBytecodeRelevance::default)
                    .merge(inv_rel);
            }
        }
        // wrap and return the analysis result
        Self {
            entrypoint_assumptions,
            per_bytecode_assertions,
            exitpoint_assertions,
            ghost_type_param_count,
        }
    }
fn analyze ( target : & FunctionTarget , targets : & FunctionTargetsHolder ) -> Self { use BorrowNode :: * ; use Bytecode :: * ; use Operation :: * ; // collect information let fid = target . func_env . get_qualified_id ( ) ; let env = target . global_env ( ) ; let inv_analysis = env . get_extension :: < InvariantAnalysisData > ( ) . expect ( " " ) ; let check_suspendable_inv_on_return = inv_analysis . fun_set_with_inv_check_on_exit . contains ( & fid ) ; let inv_applicability = inv_analysis . fun_to_inv_map . get ( & fid ) . expect ( " " ) ; let fun_type_params_arity = target . get_type_parameter_count ( ) ; let inv_ro = & inv_applicability . accessed ; let ( inv_rw_return , inv_rw_normal ) : ( BTreeSet < _ > , BTreeSet < _ > ) = if check_suspendable_inv_on_return { inv_applicability . direct_modified . iter ( ) . cloned ( ) . partition ( | inv_id | is_invariant_suspendable ( env , * inv_id ) ) } else { ( BTreeSet :: new ( ) , inv_applicability . direct_modified . clone ( ) ) } ; // collect invariant applicability and instantiation information per bytecode, i.e., // - which invariants should be instrumented after each instruction and // - per each invariant applicable, how to instantiate them. let mut entrypoint_assumptions = BTreeMap :: new ( ) ; let mut per_bytecode_assertions = BTreeMap :: new ( ) ; let mut exitpoint_assertions = BTreeMap :: new ( ) ; let mut ghost_type_param_count = 0 ; for ( code_offset , bc ) in target . data . code . iter ( ) . enumerate ( ) { let code_offset = code_offset as CodeOffset ; // collect memory accessed/modified in operations let ( mem_ro , mem_rw ) = match bc { Call ( _ , _ , oper , _ , _ ) => match oper { Function ( mid , fid , inst ) => { let callee_fid = mid . qualified ( * fid ) ; get_callee_memory_usage_for_invariant_instrumentation ( env , targets , callee_fid , inst , ) } OpaqueCallBegin ( mid , fid , inst ) => { let callee_fid = mid . qualified ( * fid ) ; let ( mem_ro , _ ) = get_callee_memory_usage_for_invariant_instrumentation ( env , targets , callee_fid , inst , ) ; ( mem_ro , BTreeSet :: new ( ) ) } OpaqueCallEnd ( mid , fid , inst ) => { let callee_fid = mid . qualified ( * fid ) ; let ( _ , mem_rw ) = get_callee_memory_usage_for_invariant_instrumentation ( env , targets , callee_fid , inst , ) ; ( BTreeSet :: new ( ) , mem_rw ) } MoveTo ( mid , sid , inst ) | MoveFrom ( mid , sid , inst ) => { let mem = mid . qualified_inst ( * sid , inst . to_owned ( ) ) ; ( BTreeSet :: new ( ) , std :: iter :: once ( mem ) . collect ( ) ) } WriteBack ( GlobalRoot ( mem ) , _ ) => { ( BTreeSet :: new ( ) , std :: iter :: once ( mem . clone ( ) ) . collect ( ) ) } Exists ( mid , sid , inst ) | GetGlobal ( mid , sid , inst ) => { let mem = mid . qualified_inst ( * sid , inst . to_owned ( ) ) ; ( std :: iter :: once ( mem ) . collect ( ) , BTreeSet :: new ( ) ) } // shortcut other operations _ => continue , } , Prop ( _ , PropKind :: Assert , exp ) | Prop ( _ , PropKind :: Assume , exp ) => ( exp . used_memory ( env ) . into_iter ( ) . map ( | ( usage , _ ) | usage ) . collect ( ) , BTreeSet :: new ( ) , ) , // shortcut other bytecodes _ => continue , } ; // collect instantiation information (step 1) // - entrypoint assumptions arised from memories that are read-only from the bytecode let relevance_ro = Self :: calculate_invariant_relevance ( env , mem_ro . iter ( ) , inv_ro , fun_type_params_arity , & mut ghost_type_param_count , /* ignore_uninstantiated_invariant */ true , ) ; // collect instantiation information (step 2) // - invariants that need to be assumed and asserted for read-write operations let relevance_rw_normal = Self :: calculate_invariant_relevance ( env , mem_rw . iter ( ) , & inv_rw_normal , fun_type_params_arity , & mut ghost_type_param_count , /* ignore_uninstantiated_invariant */ false , ) ; let relevance_rw_return = Self :: calculate_invariant_relevance ( env , mem_rw . iter ( ) , & inv_rw_return , fun_type_params_arity , & mut ghost_type_param_count , /* ignore_uninstantiated_invariant */ false , ) ; // entrypoint assumptions are about both the ro invariants and rw invariants, and // regardless of whether they are checked in-place or deferred to the exit point. for ( inv_id , inv_rel ) in relevance_ro . iter ( ) . chain ( relevance_rw_normal . iter ( ) ) . chain ( relevance_rw_return . iter ( ) ) { let inv = env . get_global_invariant ( * inv_id ) . unwrap ( ) ; if matches ! ( inv kind ConditionKind GlobalInvariantUpdate ( ) ) { // update invariants should not be assumed at function entrypoint continue ; } entrypoint_assumptions . entry ( * inv_id ) . or_insert_with ( PerBytecodeRelevance :: default ) . merge ( inv_rel . clone ( ) ) ; } // normal rw invariants are asserted in-place, right after the bytecode per_bytecode_assertions . insert ( code_offset , relevance_rw_normal ) ; // exitpoint assertions are only about the rw invariants deferred to the exit point. for ( inv_id , inv_rel ) in relevance_rw_return { exitpoint_assertions . entry ( inv_id ) . or_insert_with ( PerBytecodeRelevance :: default ) . merge ( inv_rel ) ; } } // wrap and return the analysis result Self { entrypoint_assumptions , per_bytecode_assertions , exitpoint_assertions , ghost_type_param_count , } }
Collect and build the relevance analysis information for this function target.
Collect and build the relevance analysis information for this function target .
target
&FunctionTarget
targets
&FunctionTargetsHolder