Skip to content

Annotate loop unwinding bounds via pragma #8431

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions regression/cbmc/pragma_cprover_loop1/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
int main()
{
#pragma CPROVER unwind 2
for(int i = 0; i < 5; ++i)
{
if(i < 2)
continue;

for(int j = 0; j < 10; ++j)
++j;
}

#pragma CPROVER unwind 1
do
{
int x = 42;
} while(0);

#pragma CPROVER unwind 10
while(1)
{
int x = 42;
}

__CPROVER_assert(0, "false");
}
8 changes: 8 additions & 0 deletions regression/cbmc/pragma_cprover_loop1/test.desc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
CORE
main.c

^VERIFICATION FAILED$
^EXIT=10$
^SIGNAL=0$
--
^warning: ignoring
39 changes: 32 additions & 7 deletions src/ansi-c/ansi_c_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -188,21 +188,37 @@ void ansi_c_parsert::pragma_cprover_add_check(
if(pragma_cprover_stack.empty())
pragma_cprover_push();

pragma_cprover_stack.back()[name] = enabled;
pragma_cprover_stack.back()[name] = enabled ? 1 : 0;
}

void ansi_c_parsert::pragma_cprover_add_unwind(size_t bound)
{
if(pragma_cprover_stack.empty())
pragma_cprover_push();

pragma_cprover_stack.back()["unwind"] = bound;
}

void ansi_c_parsert::pragma_cprover_consume_unwind()
{
for(auto &map : pragma_cprover_stack)
map.erase("unwind");

set_pragma_cprover();
}

bool ansi_c_parsert::pragma_cprover_clash(const irep_idt &name, bool enabled)
{
auto top = pragma_cprover_stack.back();
auto found = top.find(name);
return found != top.end() && found->second != enabled;
return found != top.end() && found->second != (enabled ? 1 : 0);
}

void ansi_c_parsert::set_pragma_cprover()
{
// handle enable/disable shadowing
// by bottom-to-top flattening
std::map<irep_idt, bool> flattened;
std::map<irep_idt, size_t> flattened;

for(const auto &pragma_set : pragma_cprover_stack)
for(const auto &pragma : pragma_set)
Expand All @@ -212,9 +228,18 @@ void ansi_c_parsert::set_pragma_cprover()

for(const auto &pragma : flattened)
{
std::string check_name = id2string(pragma.first);
std::string full_name =
(pragma.second ? "enable:" : "disable:") + check_name;
source_location.add_pragma(full_name);
if(pragma.first == "unwind")
{
std::string unwind_info =
id2string(pragma.first) + ":" + std::to_string(pragma.second);
source_location.add_pragma(unwind_info);
}
else
{
std::string check_name = id2string(pragma.first);
std::string full_name =
(pragma.second == 1 ? "enable:" : "disable:") + check_name;
source_location.add_pragma(full_name);
}
}
}
8 changes: 7 additions & 1 deletion src/ansi-c/ansi_c_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,12 +153,18 @@ class ansi_c_parsert:public parsert
/// is already present at top of the stack
bool pragma_cprover_clash(const irep_idt &name, bool enabled);

/// \brief Adds an unwinding bound to the CPROVER pragma stack
void pragma_cprover_add_unwind(size_t bound);

/// \brief Remove unwinding bounds from the CPROVER pragma stack
void pragma_cprover_consume_unwind();

/// \brief Tags \ref source_location with
/// the current CPROVER pragma stack
void set_pragma_cprover();

private:
std::list<std::map<const irep_idt, bool>> pragma_cprover_stack;
std::list<std::map<const irep_idt, size_t>> pragma_cprover_stack;
};

void ansi_c_scanner_init(ansi_c_parsert &);
Expand Down
3 changes: 3 additions & 0 deletions src/ansi-c/c_typecheck_code.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,7 @@ void c_typecheck_baset::typecheck_expression(codet &code)

void c_typecheck_baset::typecheck_for(codet &code)
{
warning() << code.pretty() << eom;
if(code.operands().size()!=4)
{
error().source_location = code.source_location();
Expand Down Expand Up @@ -771,6 +772,7 @@ void c_typecheck_baset::typecheck_switch(codet &code)

void c_typecheck_baset::typecheck_while(code_whilet &code)
{
warning() << code.pretty() << eom;
if(code.operands().size()!=2)
{
error().source_location = code.source_location();
Expand Down Expand Up @@ -817,6 +819,7 @@ void c_typecheck_baset::typecheck_while(code_whilet &code)

void c_typecheck_baset::typecheck_dowhile(code_dowhilet &code)
{
warning() << code.pretty() << eom;
if(code.operands().size()!=2)
{
error().source_location = code.source_location();
Expand Down
31 changes: 19 additions & 12 deletions src/ansi-c/parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -2495,25 +2495,31 @@ declaration_or_expression_statement:

iteration_statement:
TOK_WHILE '(' comma_expression_opt ')'
{
PARSER.pragma_cprover_consume_unwind();
}
cprover_contract_assigns_opt
cprover_contract_loop_invariant_list_opt
cprover_contract_decreases_opt
statement
{
$$=$1;
statement($$, ID_while);
parser_stack($$).add_to_operands(std::move(parser_stack($3)), std::move(parser_stack($8)));

if(!parser_stack($5).operands().empty())
static_cast<exprt &>(parser_stack($$).add(ID_C_spec_assigns)).operands().swap(parser_stack($5).operands());
parser_stack($$).add_to_operands(std::move(parser_stack($3)), std::move(parser_stack($9)));

if(!parser_stack($6).operands().empty())
static_cast<exprt &>(parser_stack($$).add(ID_C_spec_loop_invariant)).operands().swap(parser_stack($6).operands());
static_cast<exprt &>(parser_stack($$).add(ID_C_spec_assigns)).operands().swap(parser_stack($6).operands());

if(!parser_stack($7).operands().empty())
static_cast<exprt &>(parser_stack($$).add(ID_C_spec_decreases)).operands().swap(parser_stack($7).operands());
static_cast<exprt &>(parser_stack($$).add(ID_C_spec_loop_invariant)).operands().swap(parser_stack($7).operands());

if(!parser_stack($8).operands().empty())
static_cast<exprt &>(parser_stack($$).add(ID_C_spec_decreases)).operands().swap(parser_stack($8).operands());
}
| TOK_DO
{
PARSER.pragma_cprover_consume_unwind();
}
cprover_contract_assigns_opt
cprover_contract_loop_invariant_list_opt
cprover_contract_decreases_opt
Expand All @@ -2522,16 +2528,16 @@ iteration_statement:
{
$$=$1;
statement($$, ID_dowhile);
parser_stack($$).add_to_operands(std::move(parser_stack($8)), std::move(parser_stack($5)));

if(!parser_stack($2).operands().empty())
static_cast<exprt &>(parser_stack($$).add(ID_C_spec_assigns)).operands().swap(parser_stack($2).operands());
parser_stack($$).add_to_operands(std::move(parser_stack($9)), std::move(parser_stack($6)));

if(!parser_stack($3).operands().empty())
static_cast<exprt &>(parser_stack($$).add(ID_C_spec_loop_invariant)).operands().swap(parser_stack($3).operands());
static_cast<exprt &>(parser_stack($$).add(ID_C_spec_assigns)).operands().swap(parser_stack($3).operands());

if(!parser_stack($4).operands().empty())
static_cast<exprt &>(parser_stack($$).add(ID_C_spec_decreases)).operands().swap(parser_stack($4).operands());
static_cast<exprt &>(parser_stack($$).add(ID_C_spec_loop_invariant)).operands().swap(parser_stack($4).operands());

if(!parser_stack($5).operands().empty())
static_cast<exprt &>(parser_stack($$).add(ID_C_spec_decreases)).operands().swap(parser_stack($5).operands());
}
| TOK_FOR
{
Expand All @@ -2541,6 +2547,7 @@ iteration_statement:
unsigned prefix=++PARSER.current_scope().compound_counter;
PARSER.new_scope(std::to_string(prefix)+"::");
}
PARSER.pragma_cprover_consume_unwind();
}
'(' declaration_or_expression_statement
comma_expression_opt ';'
Expand Down
15 changes: 15 additions & 0 deletions src/ansi-c/scanner.l
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ static int isatty(int) { return 0; }
#endif

#include <util/string_constant.h>
#include <util/string2int.h>
#include <util/unicode.h>

#include "preprocessor_line.h"
Expand Down Expand Up @@ -443,6 +444,20 @@ enable_or_disable ("enable"|"disable")
PARSER.set_pragma_cprover();
}

<CPROVER_PRAGMA>{ws}"unwind"{ws}{integer} {
std::string tmp(yytext);
std::string::size_type p = tmp.rfind('d') + 1;
auto bound = string2optional_size_t(tmp.substr(p));
if(!bound.has_value())
{
ansi_c_parser->parse_error(
"Found invalid loop unwinding annotation " + tmp, "");
return TOK_SCANNER_ERROR;
}
PARSER.pragma_cprover_add_unwind(*bound);
PARSER.set_pragma_cprover();
}

<CPROVER_PRAGMA>. {
yyansi_cerror("Unsupported #pragma CPROVER");
return TOK_SCANNER_ERROR;
Expand Down
Loading