@@ -16,6 +16,7 @@ Author: Daniel Kroening, kroening@kroening.com
16
16
#include < util/cprover_prefix.h>
17
17
#include < util/expr_initializer.h>
18
18
#include < util/expr_util.h>
19
+ #include < util/fresh_symbol.h>
19
20
#include < util/mathematical_expr.h>
20
21
#include < util/mathematical_types.h>
21
22
#include < util/pointer_expr.h>
@@ -721,6 +722,97 @@ void goto_convertt::do_havoc_slice(
721
722
dest.add (goto_programt::make_other (array_replace, source_location));
722
723
}
723
724
725
+ // / alloca allocates memory that is freed when leaving the function (and not the
726
+ // / block, as regular destructors would do).
727
+ void goto_convertt::do_alloca (
728
+ const exprt &lhs,
729
+ const symbol_exprt &function,
730
+ const exprt::operandst &arguments,
731
+ goto_programt &dest,
732
+ const irep_idt &mode)
733
+ {
734
+ const source_locationt &source_location = function.source_location ();
735
+ exprt new_lhs = lhs;
736
+
737
+ // make sure we have a left-hand side to track the allocation even when the
738
+ // original program did not
739
+ if (lhs.is_nil ())
740
+ {
741
+ new_lhs = new_tmp_symbol (
742
+ to_code_type (function.type ()).return_type (),
743
+ " alloca" ,
744
+ dest,
745
+ source_location,
746
+ mode)
747
+ .symbol_expr ();
748
+ }
749
+
750
+ // do the actual function call
751
+ code_function_callt function_call (new_lhs, function, arguments);
752
+ function_call.add_source_location () = source_location;
753
+ copy (function_call, FUNCTION_CALL, dest);
754
+
755
+ // Don't add instrumentation when we're in alloca (which might in turn call
756
+ // __builtin_alloca) -- the instrumentation will be done for the call of
757
+ // alloca. Also, we can only add instrumentation when we're in a function
758
+ // context.
759
+ if (
760
+ function.source_location ().get_function () == " alloca" || !targets.prefix ||
761
+ !targets.suffix )
762
+ {
763
+ return ;
764
+ }
765
+
766
+ // create a symbol to eventually (and non-deterministically) mark the
767
+ // allocation as dead; this symbol has function scope and is initialised to
768
+ // NULL
769
+ symbol_exprt this_alloca_ptr =
770
+ get_fresh_aux_symbol (
771
+ to_code_type (function.type ()).return_type (),
772
+ id2string (function.source_location ().get_function ()),
773
+ " tmp_alloca" ,
774
+ source_location,
775
+ mode,
776
+ symbol_table)
777
+ .symbol_expr ();
778
+ goto_programt decl_prg;
779
+ decl_prg.add (goto_programt::make_decl (this_alloca_ptr, source_location));
780
+ decl_prg.add (goto_programt::make_assignment (
781
+ this_alloca_ptr,
782
+ null_pointer_exprt{to_pointer_type (this_alloca_ptr.type ())},
783
+ source_location));
784
+ targets.prefix ->destructive_insert (
785
+ targets.prefix ->instructions .begin (), decl_prg);
786
+
787
+ // non-deterministically update this_alloca_ptr
788
+ if_exprt rhs{
789
+ side_effect_expr_nondett{bool_typet (), source_location},
790
+ new_lhs,
791
+ this_alloca_ptr};
792
+ dest.add (goto_programt::make_assignment (
793
+ this_alloca_ptr, std::move (rhs), source_location));
794
+
795
+ // mark pointer to alloca result as dead, unless the alloca result (in
796
+ // this_alloca_ptr) is still NULL
797
+ symbol_exprt dead_object_sym =
798
+ ns.lookup (CPROVER_PREFIX " dead_object" ).symbol_expr ();
799
+ exprt alloca_result =
800
+ typecast_exprt::conditional_cast (this_alloca_ptr, dead_object_sym.type ());
801
+ if_exprt not_null{
802
+ equal_exprt{
803
+ this_alloca_ptr,
804
+ null_pointer_exprt{to_pointer_type (this_alloca_ptr.type ())}},
805
+ dead_object_sym,
806
+ std::move (alloca_result)};
807
+ auto assign = goto_programt::make_assignment (
808
+ std::move (dead_object_sym), std::move (not_null), source_location);
809
+ targets.suffix ->insert_before_swap (
810
+ targets.suffix ->instructions .begin (), assign);
811
+ targets.suffix ->insert_after (
812
+ targets.suffix ->instructions .begin (),
813
+ goto_programt::make_dead (this_alloca_ptr, source_location));
814
+ }
815
+
724
816
// / add function calls to function queue for later processing
725
817
void goto_convertt::do_function_call_symbol (
726
818
const exprt &lhs,
@@ -1398,45 +1490,9 @@ void goto_convertt::do_function_call_symbol(
1398
1490
1399
1491
copy (function_call, FUNCTION_CALL, dest);
1400
1492
}
1401
- else if (
1402
- (identifier == " alloca" || identifier == " __builtin_alloca" ) &&
1403
- function.source_location ().get_function () != " alloca" )
1493
+ else if (identifier == " alloca" || identifier == " __builtin_alloca" )
1404
1494
{
1405
- const source_locationt &source_location = function.source_location ();
1406
- exprt new_lhs = lhs;
1407
-
1408
- if (lhs.is_nil ())
1409
- {
1410
- new_lhs = new_tmp_symbol (
1411
- to_code_type (function.type ()).return_type (),
1412
- " alloca" ,
1413
- dest,
1414
- source_location,
1415
- mode)
1416
- .symbol_expr ();
1417
- }
1418
-
1419
- code_function_callt function_call (new_lhs, function, arguments);
1420
- function_call.add_source_location () = source_location;
1421
- copy (function_call, FUNCTION_CALL, dest);
1422
-
1423
- // create a backup copy to ensure that no assignments to the pointer affect
1424
- // the destructor code that will execute eventually
1425
- if (!lhs.is_nil ())
1426
- make_temp_symbol (new_lhs, " alloca" , dest, mode);
1427
-
1428
- // mark pointer to alloca result as dead
1429
- symbol_exprt dead_object_sym =
1430
- ns.lookup (CPROVER_PREFIX " dead_object" ).symbol_expr ();
1431
- exprt alloca_result =
1432
- typecast_exprt::conditional_cast (new_lhs, dead_object_sym.type ());
1433
- if_exprt rhs{
1434
- side_effect_expr_nondett{bool_typet (), source_location},
1435
- std::move (alloca_result),
1436
- dead_object_sym};
1437
- code_assignt assign{
1438
- std::move (dead_object_sym), std::move (rhs), source_location};
1439
- targets.destructor_stack .add (assign);
1495
+ do_alloca (lhs, function, arguments, dest, mode);
1440
1496
}
1441
1497
else
1442
1498
{
0 commit comments