@@ -16,6 +16,7 @@ Author: Daniel Kroening, kroening@kroening.com
1616#include < util/cprover_prefix.h>
1717#include < util/expr_initializer.h>
1818#include < util/expr_util.h>
19+ #include < util/fresh_symbol.h>
1920#include < util/mathematical_expr.h>
2021#include < util/mathematical_types.h>
2122#include < util/pointer_expr.h>
@@ -721,6 +722,97 @@ void goto_convertt::do_havoc_slice(
721722 dest.add (goto_programt::make_other (array_replace, source_location));
722723}
723724
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+
724816// / add function calls to function queue for later processing
725817void goto_convertt::do_function_call_symbol (
726818 const exprt &lhs,
@@ -1398,45 +1490,9 @@ void goto_convertt::do_function_call_symbol(
13981490
13991491 copy (function_call, FUNCTION_CALL, dest);
14001492 }
1401- else if (
1402- (identifier == " alloca" || identifier == " __builtin_alloca" ) &&
1403- function.source_location ().get_function () != " alloca" )
1493+ else if (identifier == " alloca" || identifier == " __builtin_alloca" )
14041494 {
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);
14401496 }
14411497 else
14421498 {
0 commit comments