CBMC
Loading...
Searching...
No Matches
dfcc_cfg_info.cpp
Go to the documentation of this file.
1/*******************************************************************\
2
3Module: Dynamic frame condition checking for function and loop contracts.
4
5Author: Qinheping Hu, qinhh@amazon.com
6Author: Remi Delmas delmasrd@amazon.com
7
8Date: March 2023
9
10\*******************************************************************/
11
12#include "dfcc_cfg_info.h"
13
14#include <util/c_types.h>
15#include <util/format_expr.h>
16#include <util/fresh_symbol.h>
17#include <util/pointer_expr.h>
18
22
26#include "dfcc_library.h"
28#include "dfcc_loop_tags.h"
29#include "dfcc_root_object.h"
30#include "dfcc_utils.h"
31
34static bool has_contract(
36 const bool check_side_effect)
37{
39 get_loop_invariants(latch_target, check_side_effect).is_not_nil() ||
40 get_loop_decreases(latch_target, check_side_effect).is_not_nil();
41}
42
43void dfcc_loop_infot::output(std::ostream &out) const
44{
45 out << "dfcc_loop_id: " << loop_id << "\n";
46 out << "cbmc_loop_id: " << cbmc_loop_id << "\n";
47 out << "local: {";
48 for(auto &id : local)
49 {
50 out << id << ", ";
51 }
52 out << "}\n";
53
54 out << "tracked: {";
55 for(auto &id : tracked)
56 {
57 out << id << ", ";
58 }
59 out << "}\n";
60
61 out << "write_set: " << format(write_set_var) << "\n";
62
63 out << "addr_of_write_set: " << format(addr_of_write_set_var) << "\n";
64
65 out << "assigns: {";
66 for(auto &expr : assigns)
67 {
68 out << format(expr) << ", ";
69 }
70 out << "}\n";
71
72 out << "invariant: " << format(invariant) << "\n";
73
74 out << "decreases: {";
75 for(auto &expr : decreases)
76 {
77 out << format(expr) << ", ";
78 }
79 out << "}\n";
80
81 out << "inner loops: {"
82 << "\n";
83 for(auto &id : inner_loops)
84 {
85 out << id << ", ";
86 }
87 out << "}\n";
88
89 out << "outer loops: {"
90 << "\n";
91 for(auto &id : outer_loops)
92 {
93 out << id << ", ";
94 }
95 out << "}\n";
96}
97
98std::optional<goto_programt::targett>
100{
101 for(auto target = goto_program.instructions.begin();
102 target != goto_program.instructions.end();
103 target++)
104 {
105 if(dfcc_is_loop_head(target) && dfcc_has_loop_id(target, loop_id))
106 {
107 return target;
108 }
109 }
110 return {};
111}
112
113std::optional<goto_programt::targett>
115{
116 std::optional<goto_programt::targett> result = std::nullopt;
117 for(auto target = goto_program.instructions.begin();
118 target != goto_program.instructions.end();
119 target++)
120 {
121 // go until the end because we want to find the very last occurrence
122 if(dfcc_is_loop_latch(target) && dfcc_has_loop_id(target, loop_id))
123 {
124 result = target;
125 }
126 }
127 return result;
128}
129
130static std::optional<goto_programt::targett> check_has_contract_rec(
132 const std::size_t loop_idx,
133 const bool must_have_contract,
134 const bool check_side_effect)
135{
136 const auto &node = loop_nesting_graph[loop_idx];
137 if(must_have_contract && !has_contract(node.latch, check_side_effect))
138 return node.head;
139
140 for(const auto pred_idx : loop_nesting_graph.get_predecessors(loop_idx))
141 {
142 auto result = check_has_contract_rec(
144 pred_idx,
145 has_contract(node.latch, check_side_effect),
146 check_side_effect);
147 if(result.has_value())
148 return result;
149 }
150 return {};
151}
152
156static std::optional<goto_programt::targett> check_inner_loops_have_contracts(
158 const bool check_side_effect)
159{
160 for(std::size_t idx = 0; idx < loop_nesting_graph.size(); idx++)
161 {
162 if(loop_nesting_graph.get_successors(idx).empty())
163 {
164 auto result = check_has_contract_rec(
165 loop_nesting_graph, idx, false, check_side_effect);
166 if(result.has_value())
167 return result;
168 }
169 }
170 return {};
171}
172
177 goto_programt &goto_program,
179{
180 for(const auto &idx : loop_nesting_graph.topsort())
181 {
182 auto &node = loop_nesting_graph[idx];
183 auto &head = node.head;
184 auto &latch = node.latch;
185 auto &instruction_iterators = node.instructions;
186
187 dfcc_set_loop_head(head);
188 dfcc_set_loop_latch(latch);
189
191 {
192 // Skip instructions that are already tagged and belong to inner loops.
194 if(loop_id_opt.has_value())
195 continue;
196
197 dfcc_set_loop_id(t, idx);
198
199 if(t != head && t != latch)
201
202 if(t->is_goto() && !instruction_iterators.contains(t->get_target()))
203 {
205 }
206 }
207 }
208
209 auto top_level_id = loop_nesting_graph.size();
210
211 // tag remaining instructions as top level
212 for(auto target = goto_program.instructions.begin();
213 target != goto_program.instructions.end();
214 target++)
215 {
216 // Skip instructions that are already tagged (belong to loops).
217 auto loop_id_opt = dfcc_get_loop_id(target);
218 if(loop_id_opt.has_value())
219 {
220 continue;
221 }
222 dfcc_set_loop_id(target, top_level_id);
224 }
225}
226
227static bool is_assigned(dirtyt &dirty, const irep_idt &ident, assignst assigns)
228{
229 PRECONDITION(!dirty(ident));
230 // For each assigns clause target
231 for(const auto &expr : assigns)
232 {
233 auto root_objects = dfcc_root_objects(expr);
234 for(const auto &root_object : root_objects)
235 {
236 if(
237 root_object.id() == ID_symbol &&
238 expr_try_dynamic_cast<symbol_exprt>(root_object)->identifier() == ident)
239 {
240 return true;
241 }
242 // If `root_object` is not a symbol, then it contains a combination of
243 // address-of and dereference operators that cannot be statically
244 // resolved to a symbol.
245 // Since we know `ident` is not dirty, we know that dereference
246 // operations cannot land on that `ident`. So the root_object cannot
247 // describe a memory location within the object backing that ident.
248 // We conclude that ident is not assigned by this target and move on to
249 // the next target.
250 }
251 }
252 return false;
253}
254
268static std::unordered_set<irep_idt> gen_tracked_set(
269 const std::vector<std::size_t> &inner_loops_ids,
270 const std::unordered_set<irep_idt> &locals,
271 dirtyt &dirty,
272 const std::map<std::size_t, dfcc_loop_infot> &loop_info_map)
273{
274 std::unordered_set<irep_idt> tracked;
275 for(const auto &ident : locals)
276 {
277 if(dirty(ident))
278 {
279 tracked.insert(ident);
280 }
281 else
282 {
283 // Check if this ident is touched by one of the inner loops
284 for(const auto inner_loop_id : inner_loops_ids)
285 {
286 if(is_assigned(dirty, ident, loop_info_map.at(inner_loop_id).assigns))
287 tracked.insert(ident);
288 }
289 }
290 }
291 return tracked;
292}
293
304
309 const std::size_t loop_id,
310 const irep_idt &function_id,
312 const bool check_side_effect,
313 message_handlert &message_handler,
314 const namespacet &ns)
315{
316 messaget log(message_handler);
317 const auto &loop = loop_nesting_graph[loop_id];
318
319 // Process loop contract clauses
321 get_loop_invariants(loop.latch, check_side_effect).operands();
323
324 // Initialise defaults
325 struct contract_clausest result(
326 get_loop_decreases(loop.latch, check_side_effect).operands());
327
328 // Generate defaults for all clauses if at least one type of clause is defined
329 if(
330 !invariant_clauses.empty() || !result.decreases_clauses.empty() ||
331 !assigns_clauses.empty())
332 {
333 if(invariant_clauses.empty())
334 {
335 // use a default invariant if none given.
336 result.invariant_expr = true_exprt{};
337 // assigns clause is missing; we will try to automatic inference
338 log.warning() << "No invariant provided for loop " << function_id << "."
339 << loop.latch->loop_number << " at "
340 << loop.head->source_location()
341 << ". Using 'true' as a sound default invariant. Please "
342 "provide an invariant the default is too weak."
343 << messaget::eom;
344 }
345 else
346 {
347 // conjoin all invariant clauses
349 }
350
351 // unpack assigns clause targets
352 if(!assigns_clauses.empty())
353 {
354 for(auto &operand : assigns_clauses)
355 {
356 result.assigns.insert(operand);
357 }
358 }
359 else
360 {
361 // infer assigns clause targets if none given
362 log.debug() << "No assigns clause provided for loop " << function_id
363 << "." << loop.latch->loop_number << " at "
364 << loop.head->source_location() << ". The inferred set {";
365 bool first = true;
366 for(const auto &expr : inferred_assigns)
367 {
368 if(!first)
369 {
370 log.debug() << ", ";
371 }
372 first = false;
373 log.debug() << format(expr);
374 }
375 log.debug() << "} might be incomplete or imprecise, please provide an "
376 "assigns clause if the analysis fails."
377 << messaget::eom;
378 result.assigns = inferred_assigns;
379 }
380
381 if(result.decreases_clauses.empty())
382 {
383 log.debug() << "No decrease clause provided for loop " << function_id
384 << "." << loop.latch->loop_number << " at "
385 << loop.head->source_location()
386 << ". Termination will not be checked." << messaget::eom;
387 }
388 }
389 return result;
390}
391
394 const std::size_t loop_id,
395 const irep_idt &function_id,
396 goto_functiont &goto_function,
397 const std::map<std::size_t, dfcc_loop_infot> &loop_info_map,
398 dirtyt &dirty,
400 const bool check_side_effect,
401 message_handlert &message_handler,
402 dfcc_libraryt &library,
403 symbol_table_baset &symbol_table)
404{
405 const namespacet ns(symbol_table);
406 std::unordered_set<irep_idt> loop_locals = gen_loop_locals_set(
407 function_id,
408 goto_function,
409 loop_nesting_graph[loop_id],
410 message_handler,
411 ns);
412
413 // Exclude locals of inner nested loops that are abstracted by a contract.
414 // Locals of skipped (contract-less) inner loops are kept, because such loops
415 // are not abstracted and share the enclosing scope's write set; their locals
416 // must therefore be considered local to this scope (otherwise assignments to
417 // them would be checked against this scope's write set and fail).
418 for(const auto &inner_loop : loop_nesting_graph.get_predecessors(loop_id))
419 {
420 INVARIANT(
421 loop_info_map.find(inner_loop) != loop_info_map.end(),
422 "DFCC should gen_dfcc_loop_info for inner loops first.");
423 if(loop_info_map.at(inner_loop).must_skip())
424 continue;
425 for(const auto &inner_local : loop_info_map.at(inner_loop).local)
426 {
428 }
429 }
430
431 std::unordered_set<irep_idt> loop_tracked = gen_tracked_set(
432 loop_nesting_graph.get_predecessors(loop_id),
434 dirty,
435 loop_info_map);
436
439 loop_id,
440 function_id,
442 check_side_effect,
443 message_handler,
444 ns);
445
446 std::set<std::size_t> inner_loops;
447 for(auto pred_idx : loop_nesting_graph.get_predecessors(loop_id))
448 {
449 inner_loops.insert(pred_idx);
450 }
451
452 std::set<std::size_t> outer_loops;
453 for(auto succ_idx : loop_nesting_graph.get_successors(loop_id))
454 {
455 outer_loops.insert(succ_idx);
456 }
457
458 auto &loop = loop_nesting_graph[loop_id];
459 const auto cbmc_loop_number = loop.latch->loop_number;
460
461 // Generate "write set" variable
462 const auto write_set_var = dfcc_utilst::create_symbol(
463 symbol_table,
465 function_id,
466 "__write_set_loop_" + std::to_string(cbmc_loop_number),
467 loop.head->source_location());
468
469 // Generate "address of write set" variable
470 const auto &addr_of_write_set_var = dfcc_utilst::create_symbol(
471 symbol_table,
473 function_id,
474 "__address_of_write_set_loop_" + std::to_string(cbmc_loop_number),
475 loop.head->source_location());
476
477 return {
478 loop_id,
480 contract_clauses.assigns,
481 contract_clauses.invariant_expr,
482 contract_clauses.decreases_clauses,
485 inner_loops,
486 outer_loops,
487 write_set_var,
488 addr_of_write_set_var};
489}
490
492 goto_modelt &goto_model,
493 const irep_idt &function_id,
494 goto_functiont &goto_function,
495 const exprt &top_level_write_set,
496 const loop_contract_configt &loop_contract_config,
497 symbol_table_baset &symbol_table,
498 message_handlert &message_handler,
499 dfcc_libraryt &library)
500 : function_id(function_id),
501 goto_function(goto_function),
502 top_level_write_set(top_level_write_set),
503 ns(symbol_table)
504{
506 goto_programt &goto_program = goto_function.body;
507
508 // Clean up possible fake loops that are due to do { ... } while(0);
509 simplify_gotos(goto_program, ns);
510
511 // From loop number to the inferred loop assigns.
512 std::map<std::size_t, assignst> inferred_loop_assigns_map;
513
514 if(loop_contract_config.apply_loop_contracts)
515 {
516 messaget log(message_handler);
517 dfcc_check_loop_normal_form(goto_program, log);
519
520 const auto head = check_inner_loops_have_contracts(
521 loop_nesting_graph, loop_contract_config.check_side_effect);
522 if(head.has_value())
523 {
525 "Found loop without contract nested in a loop with a "
526 "contract.\nPlease "
527 "provide a contract or unwind this loop before applying loop "
528 "contracts.",
529 head.value()->source_location());
530 }
531
532 auto topsorted = loop_nesting_graph.topsort();
533
534 bool has_loops_with_contracts = false;
535 for(const auto idx : topsorted)
536 {
537 topsorted_loops.push_back(idx);
539 loop_nesting_graph[idx].latch, loop_contract_config.check_side_effect);
540 }
541
542 // We infer loop assigns for all loops in the function.
544 {
547 goto_model.goto_functions,
549 message_handler,
550 ns);
551 }
552 }
553
554 // At this point, either loop contracts were activated and the loop nesting
555 // graph describes the loop structure of the function,
556 // or loop contracts were not activated and the loop nesting graph is empty
557 // (i.e. there might be some loops in the function but we won't consider them
558 // for the instrumentation).
559 // In both cases, we tag program instructions and generate the dfcc_cfg_infot
560 // instance from that graph's contents. The tags will decide against which
561 // write set the instructions are going to be instrumented (either the
562 // function's write set, or the write set of a loop), and each dfcc_loop_infot
563 // contained in the loop_info_map describes a loop to be abstracted by a
564 // contract.
565
567
568 // generate dfcc_cfg_loop_info for loops and add to loop_info_map
569 dirtyt dirty(goto_function);
570
571 for(const auto &loop_id : topsorted_loops)
572 {
574 inferred_loop_assigns_map[loop_nesting_graph[loop_id].latch->loop_number];
575 loop_info_map.insert(
576 {loop_id,
579 loop_id,
583 dirty,
585 loop_contract_config.check_side_effect,
586 message_handler,
587 library,
588 symbol_table)});
589
590 if(loop_nesting_graph.get_successors(loop_id).empty())
591 top_level_loops.push_back(loop_id);
592 }
593
594 // generate set of top level of locals
595 top_level_local.insert(
598
599 for(auto target = goto_function.body.instructions.begin();
600 target != goto_function.body.instructions.end();
601 target++)
602 {
603 // A DECL belongs to the top level either when it is directly at the top
604 // level, or when it is declared inside a skipped (contract-less) loop whose
605 // enclosing non-skipped scope is the top level. Such loops are not
606 // abstracted and share the top-level write set, so their locals must be
607 // considered top-level locals.
608 if(
609 target->is_decl() &&
611 dfcc_get_loop_id(target).value())))
612 top_level_local.insert(target->decl_symbol().identifier());
613 }
614
617}
618
619void dfcc_cfg_infot::output(std::ostream &out) const
620{
621 out << "// dfcc_cfg_infot for: " << function_id << "\n";
622 out << "// top_level_local: {";
623 for(auto &id : top_level_local)
624 {
625 out << id << ", ";
626 }
627 out << "}\n";
628
629 out << "// top_level_tracked: {";
630 for(auto &id : top_level_tracked)
631 {
632 out << id << ", ";
633 }
634 out << "}\n";
635
636 out << "// loop:\n";
637 for(auto &loop : loop_info_map)
638 {
639 out << "// dfcc-loop_id:" << loop.first << "\n";
640 auto head = loop.second.find_head(goto_function.body);
641 auto latch = loop.second.find_latch(goto_function.body);
642 out << "// head:\n";
643 head.value()->output(out);
644 out << "// latch:\n";
645 latch.value()->output(out);
646 loop.second.output(out);
647 }
648 out << "// program:\n";
650 {
651 out << "// dfcc-loop-id:" << dfcc_get_loop_id(target).value();
652 out << " cbmc-loop-number:" << target->loop_number;
653 out << " top-level:" << dfcc_is_loop_top_level(target);
654 out << " head:" << dfcc_is_loop_head(target);
655 out << " body:" << dfcc_is_loop_body(target);
656 out << " exiting:" << dfcc_is_loop_exiting(target);
657 out << " latch:" << dfcc_is_loop_latch(target);
658 out << "\n";
659 target->output(out);
660 }
661}
662
664 const std::size_t loop_id) const
665{
666 if(is_top_level_id(loop_id) || !get_loop_info(loop_id).must_skip())
667 {
668 return loop_id;
669 }
672}
673
674const exprt &
676{
677 auto loop_id_opt = dfcc_get_loop_id(target);
679 loop_id_opt.has_value() &&
682 if(is_top_level_id(loop_id))
683 {
684 return top_level_write_set;
685 }
686 else
687 {
688 return loop_info_map.at(loop_id).addr_of_write_set_var;
689 }
690}
691
692const std::unordered_set<irep_idt> &
694{
695 auto loop_id_opt = dfcc_get_loop_id(target);
697 loop_id_opt.has_value() &&
700 if(is_top_level_id(loop_id))
701 {
702 return top_level_tracked;
703 }
704 else
705 {
706 return loop_info_map.at(loop_id).tracked;
707 }
708}
709
710const std::unordered_set<irep_idt> &
712{
713 auto loop_id_opt = dfcc_get_loop_id(target);
715 loop_id_opt.has_value() &&
718 if(is_top_level_id(loop_id))
719 {
720 return top_level_local;
721 }
722 else
723 {
724 return loop_info_map.at(loop_id).local;
725 }
726}
727
728const exprt &dfcc_cfg_infot::get_outer_write_set(std::size_t loop_id) const
729{
732 return outer_loop_opt.has_value()
733 ? get_loop_info(outer_loop_opt.value()).addr_of_write_set_var
735}
736
737const dfcc_loop_infot &
738dfcc_cfg_infot::get_loop_info(const std::size_t loop_id) const
739{
740 return loop_info_map.at(loop_id);
741}
742
743// find the identifier or the immediately enclosing loop in topological order
744const std::optional<std::size_t>
745dfcc_cfg_infot::get_outer_loop_identifier(const std::size_t loop_id) const
746{
748 auto outer_loops = get_loop_info(loop_id).outer_loops;
749
750 // find the first loop in the topological order that is connected
751 // to our node.
752 for(const auto &idx : get_loops_toposorted())
753 {
754 if(
755 std::find(outer_loops.begin(), outer_loops.end(), idx) !=
756 outer_loops.end())
757 {
758 return idx;
759 }
760 }
761 // return nullopt for loops that are not nested in other loops
762 return std::nullopt;
763}
764
765bool dfcc_cfg_infot::is_valid_loop_or_top_level_id(const std::size_t id) const
766{
767 return id <= loop_info_map.size();
768}
769
770bool dfcc_cfg_infot::is_valid_loop_id(const std::size_t id) const
771{
772 return id < loop_info_map.size();
773}
774
775bool dfcc_cfg_infot::is_top_level_id(const std::size_t id) const
776{
777 return id == loop_info_map.size();
778}
779
781{
782 return loop_info_map.size();
783}
784
786 goto_programt::const_targett target) const
787{
788 PRECONDITION(target->is_decl() || target->is_dead());
789 auto &ident = target->is_decl() ? target->decl_symbol().identifier()
790 : target->dead_symbol().identifier();
791 auto &tracked = get_tracked_set(target);
792 return tracked.find(ident) != tracked.end();
793}
794
800 const exprt &lhs,
801 const std::unordered_set<irep_idt> &local,
802 const std::unordered_set<irep_idt> &tracked)
803{
805
806 // Check wether all root_objects can be resolved to actual identifiers.
807 std::unordered_set<irep_idt> root_idents;
808 for(const auto &expr : root_objects)
809 {
810 if(expr.id() != ID_symbol)
811 {
812 // This means that lhs contains either an address-of operation or a
813 // dereference operation, and we cannot really know statically which
814 // object it refers to without using the may_alias analysis.
815 // Since the may_alias analysis is also used to infer targets, for
816 // soundness reasons we cannot also use it to skip checks, so we check
817 // the assignment. If happens to assign to a mix of tracked and
818 // non-tracked identifiers the check will fail but this is sound anyway.
819 return true;
820 }
821 const auto &id = to_symbol_expr(expr).identifier();
823 {
824 // Skip the check if we have a single cprover symbol as root object
825 // cprover symbols are used for generic checks instrumentation and are
826 // de-facto ghost code. We implicitly allow assignments to these symbols.
827 // To make this really sound we should use a white list of known
828 // CPROVER symbols, because right now simply naming a symbol with the
829 // CPROVER prefix bypasses the checks.
830 if(root_objects.size() == 1)
831 {
832 return false;
833 }
834 else
835 {
836 // error out if we have a cprover symbol and something else in the set
838 "LHS expression `" + format_to_string(lhs) +
839 "` in assignment refers to a cprover symbol and something else.");
840 }
841 }
842 root_idents.insert(id);
843 }
844
845 // The root idents set is Non-empty.
846 // true iff root_idents contains non-local idents
847 bool some_non_local = false;
848 // true iff root_idents contains some local that is not tracked
849 bool some_local_not_tracked = false;
850 // true iff root_idents contains only local that are not tracked
851 bool all_local_not_tracked = true;
852 // true iff root_idents contains only local that are tracked
853 bool all_local_tracked = true;
854 for(const auto &root_ident : root_idents)
855 {
856 bool loc = local.find(root_ident) != local.end();
857 bool tra = tracked.find(root_ident) != tracked.end();
858 bool local_tracked = loc && tra;
859 bool local_not_tracked = loc && !tra;
860 some_non_local |= !loc;
864 }
865
866 // some root identifier is not local, the lhs must be checked
868 {
869 // if we also have a local that is not tracked, we know the check will
870 // fail with the current algorithm, error out.
872 {
874 "LHS expression `" + format_to_string(lhs) +
875 "` in assignment mentions both explicitly and implicitly tracked "
876 "memory locations. DFCC does not yet handle that case, please "
877 "reformulate the assignment into separate assignments to either "
878 "memory locations.");
879 }
880 return true;
881 }
882 else
883 {
884 // all root identifiers are local
885 // if they are all not tracked, we *have* to skip the check
886 // (and it is sound to do so, because we know that the identifiers that
887 // are not tracked explicitly are not dirty and not assigned to outside of
888 // their scope).
889 // if they are all tracked, we *can* skip the check, because they are all
890 // local to that scope anyway and implicitly allowed.
892 {
893 return false;
894 }
895 else
896 {
897 // we have a combination of tracked and not-tracked locals, we know
898 // the check will fail with the current algorithm, error out.
900 "LHS expression `" + format_to_string(lhs) +
901 "` in assignment mentions both explicitly and implicitly tracked "
902 "memory locations. DFCC does not yet handle that case, please "
903 "reformulate the assignment into separate assignments to either "
904 "memory locations.");
905 }
906 }
907}
908
910{
911 PRECONDITION(target->is_assign() || target->is_function_call());
912 const exprt &lhs =
913 target->is_assign() ? target->assign_lhs() : target->call_lhs();
914 if(lhs.is_nil())
915 return false;
917 lhs, get_local_set(target), get_tracked_set(target));
918}
virtual void output(const namespacet &ns, const irep_idt &function_id, const goto_programt &goto_program, std::ostream &out) const
Output the abstract states for a single function.
Definition ai.cpp:39
ait supplies three of the four components needed: an abstract interpreter (in this case handling func...
Definition ai.h:566
Thrown when an unexpected error occurs during the analysis (e.g., when the SAT solver returns an erro...
const std::optional< std::size_t > get_outer_loop_identifier(const std::size_t loop_id) const
Finds the DFCC id of the loop that contains the given loop, returns nullopt when the loop has no oute...
std::unordered_set< irep_idt > top_level_local
Set of identifiers DECL at top level.
const exprt & top_level_write_set
std::size_t get_first_id_not_skipped_or_top_level_id(const std::size_t loop_id) const
Returns the id of the first outer loop (including this one) that is not skipped, or the top level id.
const namespacet ns
std::vector< std::size_t > topsorted_loops
Loop identifiers sorted from most deeply nested to less deeply nested.
std::unordered_set< irep_idt > top_level_tracked
Set of identifiers DECL at top level.
bool is_top_level_id(const std::size_t id) const
True iff id is in the valid range for a loop id for this function.
const dfcc_loop_infot & get_loop_info(const std::size_t loop_id) const
Returns the loop info for that loop_id.
void output(std::ostream &out) const
const std::vector< std::size_t > & get_loops_toposorted() const
goto_functiont & goto_function
std::vector< std::size_t > top_level_loops
Loop identifiers for top level loops (ie for loops that are not nested in in another loop).
const std::unordered_set< irep_idt > & get_tracked_set(goto_programt::const_targett target) const
Returns the subset of local variable that are explicitly tracked in the write set for the scope where...
dfcc_cfg_infot(goto_modelt &goto_model, const irep_idt &function_id, goto_functiont &goto_function, const exprt &top_level_write_set, const loop_contract_configt &loop_contract_config, symbol_table_baset &symbol_table, message_handlert &message_handler, dfcc_libraryt &library)
bool must_check_lhs(goto_programt::const_targett target) const
True iff the lhs of an assignment must be checked against the ambient write set.
const std::unordered_set< irep_idt > & get_local_set(goto_programt::const_targett target) const
Returns the set of local variable for the scope where that target instruction is found.
const exprt & get_outer_write_set(std::size_t loop_id) const
Returns the write set of the outer loop of that loop or the top level write set if that loop has no o...
const exprt & get_write_set(goto_programt::const_targett target) const
Returns the write set variable to use for the given instruction Returns the write set for the loop,...
bool must_track_decl_or_dead(goto_programt::const_targett target) const
True iff a DECL ident must be tracked in the write set of the loop that contains the DECL.
size_t top_level_id() const
Returns the top level ID.
bool is_valid_loop_id(const std::size_t id) const
True iff id is in the valid range for a loop id for this function.
const irep_idt & function_id
std::map< std::size_t, dfcc_loop_infot > loop_info_map
Map from loop identifier to loop info struct.
bool is_valid_loop_or_top_level_id(const std::size_t id) const
True iff id is in the valid range for a loop id or is equal to the top level id for this function.
Class interface to library types and functions defined in cprover_contracts.c.
std::map< dfcc_typet, typet > dfcc_type
Maps enum values to the actual types (dynamically loaded)
Describes a single loop for the purpose of DFCC loop contract instrumentation.
const symbol_exprt addr_of_write_set_var
Symbol representing pointer to the stack allocated write set object for this loop.
void output(std::ostream &out) const
Prints a textual representation of the struct to out.
const exprt::operandst decreases
Decreases clause expression.
const std::unordered_set< irep_idt > local
Set of local identifiers locally DECL in loop instructions, excluding identifiers declared in nested ...
const std::set< exprt > assigns
Set of targets assigned by the loop, either user-provided or inferred.
const std::size_t cbmc_loop_id
Loop identifier assigned to this loop by traditional CBMC loop numbering.
std::optional< goto_programt::targett > find_latch(goto_programt &goto_program) const
const std::unordered_set< irep_idt > tracked
Subset of locals that must be tracked in the loop's write set.
const std::size_t loop_id
Loop identifier assigned by DFCC to this loop.
const exprt invariant
Loop invariant expression.
const std::set< std::size_t > outer_loops
Integer identifier of the outer loop(s) if they exists.
const std::set< std::size_t > inner_loops
Integer identifiers of inner loops of that loop.
std::optional< goto_programt::targett > find_head(goto_programt &goto_program) const
Finds the first instruction tagged as loop head and having the same loop identifier as this struct in...
const symbol_exprt write_set_var
Symbol representing the stack-allocated write set object for this loop.
Dirty variables are ones which have their address taken so we can't reliably work out where they may ...
Definition dirty.h:28
dstringt has one field, an unsigned integer no which is an index into a static table of strings.
Definition dstring.h:38
Base class for all expressions.
Definition expr.h:57
std::vector< exprt > operandst
Definition expr.h:59
operandst & operands()
Definition expr.h:95
A goto function, consisting of function body (see body) and parameter identifiers (see parameter_iden...
goto_programt body
parameter_identifierst parameter_identifiers
The identifiers of the parameters of this function.
goto_functionst goto_functions
GOTO functions.
Definition goto_model.h:34
A generic container class for the GOTO intermediate representation of one function.
instructionst instructions
The list of instructions in the goto program.
instructionst::iterator targett
instructionst::const_iterator const_targett
Thrown when we can't handle something in an input source file.
bool is_not_nil() const
Definition irep.h:372
bool is_nil() const
Definition irep.h:368
Class that provides messages with a built-in verbosity 'level'.
Definition message.h:154
static eomt eom
Definition message.h:289
A namespacet is essentially one or two symbol tables bound together, to allow for symbol lookups in t...
Definition namespace.h:91
The NIL expression.
Definition std_expr.h:3144
The symbol table base class interface.
The Boolean constant true.
Definition std_expr.h:3126
static bool is_assigned(dirtyt &dirty, const irep_idt &ident, assignst assigns)
static bool has_contract(const goto_programt::const_targett &latch_target, const bool check_side_effect)
Returns true iff some contract clause expression is attached to the latch condition of this loop.
static void tag_loop_instructions(goto_programt &goto_program, dfcc_loop_nesting_grapht &loop_nesting_graph)
Tags instructions of loops found in loop_nesting_graph with the loop identifier of the innermost loop...
static std::optional< goto_programt::targett > check_inner_loops_have_contracts(const dfcc_loop_nesting_grapht &loop_nesting_graph, const bool check_side_effect)
Traverses the loop nesting graph from top level loops and checks if all loops nested in loops that ha...
static bool must_check_lhs_from_local_and_tracked(const exprt &lhs, const std::unordered_set< irep_idt > &local, const std::unordered_set< irep_idt > &tracked)
Returns true if the lhs to an assignment must be checked against its write set.
static struct contract_clausest default_loop_contract_clauses(const dfcc_loop_nesting_grapht &loop_nesting_graph, const std::size_t loop_id, const irep_idt &function_id, const assignst &inferred_assigns, const bool check_side_effect, message_handlert &message_handler, const namespacet &ns)
Generate defaults for all contract clauses of the loop with ID loop_id if at least one type of clause...
static dfcc_loop_infot gen_dfcc_loop_info(const dfcc_loop_nesting_grapht &loop_nesting_graph, const std::size_t loop_id, const irep_idt &function_id, goto_functiont &goto_function, const std::map< std::size_t, dfcc_loop_infot > &loop_info_map, dirtyt &dirty, const assignst &inferred_assigns, const bool check_side_effect, message_handlert &message_handler, dfcc_libraryt &library, symbol_table_baset &symbol_table)
static std::optional< goto_programt::targett > check_has_contract_rec(const dfcc_loop_nesting_grapht &loop_nesting_graph, const std::size_t loop_idx, const bool must_have_contract, const bool check_side_effect)
static std::unordered_set< irep_idt > gen_tracked_set(const std::vector< std::size_t > &inner_loops_ids, const std::unordered_set< irep_idt > &locals, dirtyt &dirty, const std::map< std::size_t, dfcc_loop_infot > &loop_info_map)
Compute subset of locals that must be tracked in the loop's write set.
Class that computes CFG information about the loop structure of a GOTO function for the purpose of dy...
void dfcc_check_loop_normal_form(goto_programt &goto_program, messaget &log)
Checks and enforces normal form properties for natural loops of the given goto_program.
Checks normal form properties of natural loops in a GOTO program.
std::unordered_set< irep_idt > gen_loop_locals_set(const irep_idt &function_id, goto_functiont &goto_function, const dfcc_loop_nesting_graph_nodet &loop_node, message_handlert &message_handler, const namespacet &ns)
Collect identifiers that are local to this loop.
void dfcc_infer_loop_assigns_for_function(std::map< std::size_t, assignst > &inferred_loop_assigns_map, goto_functionst &goto_functions, const goto_functiont &goto_function, message_handlert &message_handler, const namespacet &ns)
Infer assigns clause targets for loops in goto_function from their instructions and an alias analysis...
Infer a set of assigns clause targets for a natural loop.
bool dfcc_is_cprover_static_symbol(const irep_idt &id)
Returns true iff the symbol is one of the known CPROVER static instrumentation variables or ends with...
Dynamic frame condition checking library loading.
@ WRITE_SET
type of descriptors of assignable/freeable sets of locations
@ WRITE_SET_PTR
type of pointers to descriptors of assignable/freeable sets of locations
dfcc_loop_nesting_grapht build_loop_nesting_graph(goto_programt &goto_program)
Builds a graph instance describing the nesting structure of natural loops in the given goto_program.
Builds a graph describing how loops are nested in a GOTO program.
bool dfcc_has_loop_id(const goto_programt::instructiont::const_targett &target, std::size_t loop_id)
void dfcc_set_loop_exiting(goto_programt::instructiont::targett &target)
void dfcc_set_loop_latch(goto_programt::instructiont::targett &target)
bool dfcc_is_loop_head(const goto_programt::instructiont::const_targett &target)
bool dfcc_is_loop_latch(const goto_programt::instructiont::const_targett &target)
bool dfcc_is_loop_top_level(const goto_programt::instructiont::const_targett &target)
void dfcc_set_loop_top_level(goto_programt::instructiont::targett &target)
void dfcc_set_loop_body(goto_programt::instructiont::targett &target)
void dfcc_set_loop_id(goto_programt::instructiont::targett &target, const std::size_t loop_id)
std::optional< std::size_t > dfcc_get_loop_id(const goto_programt::instructiont::const_targett &target)
bool dfcc_is_loop_exiting(const goto_programt::instructiont::const_targett &target)
bool dfcc_is_loop_body(const goto_programt::instructiont::const_targett &target)
void dfcc_set_loop_head(goto_programt::instructiont::targett &target)
Functions that allow to tag GOTO instructions with loop identifiers and loop instruction type: head,...
std::unordered_set< exprt, irep_hash > dfcc_root_objects(const exprt &expr)
Computes a set of root object expressions from an lvalue or assigns clause target expression.
Utility functions that compute root object expressions for assigns clause targets and LHS expressions...
Dynamic frame condition checking utility functions.
static format_containert< T > format(const T &o)
Definition format.h:37
std::string format_to_string(const T &o)
Definition format.h:43
Fresh auxiliary symbol creation.
#define Forall_goto_program_instructions(it, program)
std::set< exprt > assignst
Definition havoc_utils.h:24
bool has_contract(const irep_idt &function_identifier, const namespacet &ns)
Field-insensitive, location-sensitive may-alias analysis.
double log(double x)
Definition math.c:2416
Compute natural loops in a goto_function.
API to expression classes for Pointers.
#define PRECONDITION(CONDITION)
Definition invariant.h:463
#define INVARIANT(CONDITION, REASON)
This macro uses the wrapper function 'invariant_violated_string'.
Definition invariant.h:423
exprt conjunction(exprt a, exprt b)
Conjunction of two expressions.
Definition std_expr.cpp:252
const symbol_exprt & to_symbol_expr(const exprt &expr)
Cast an exprt to a symbol_exprt.
Definition std_expr.h:221
exprt::operandst decreases_clauses
contract_clausest(const exprt::operandst &decreases)
static symbol_exprt create_symbol(symbol_table_baset &, const typet &type, const irep_idt &function_id, const std::string &base_name, const source_locationt &source_location)
Adds a new symbol named function_id::base_name of type type with given attributes in the symbol table...
Loop contract configurations.
exprt get_loop_assigns(const goto_programt::const_targett &loop_end)
Extract loop assigns from annotated loop end.
Definition utils.cpp:683
void simplify_gotos(goto_programt &goto_program, const namespacet &ns)
Turns goto instructions IF cond GOTO label where the condition statically simplifies to false into SK...
Definition utils.cpp:260
exprt get_loop_invariants(const goto_programt::const_targett &loop_end, const bool check_side_effect)
Extract loop invariants from annotated loop end.
Definition utils.cpp:666
exprt get_loop_decreases(const goto_programt::const_targett &loop_end, const bool check_side_effect)
Extract loop decreases from annotated loop end.
Definition utils.cpp:688