diff --git a/regression/cbmc/graphml_witness1/test.desc b/regression/cbmc/graphml_witness1/test.desc
index cb22eaba5d0..d0428e2738d 100644
--- a/regression/cbmc/graphml_witness1/test.desc
+++ b/regression/cbmc/graphml_witness1/test.desc
@@ -7,34 +7,21 @@ main.c
-
- path
-
-
- false
-
false
false
-
- false
-
false
-
- 0
-
-
@@ -45,7 +32,6 @@ main.c
C
-
true
@@ -57,7 +43,7 @@ main.c
main.c
29
- main
+ remove_one
@@ -68,10 +54,6 @@ main.c
true
-
- main.c
- 31
-
--
diff --git a/regression/cbmc/graphml_witness2/main.c b/regression/cbmc/graphml_witness2/main.c
new file mode 100644
index 00000000000..ebe307a73c0
--- /dev/null
+++ b/regression/cbmc/graphml_witness2/main.c
@@ -0,0 +1,31 @@
+extern void __VERIFIER_error();
+
+static float one(float *foo)
+{
+ return *foo;
+}
+
+static float two(float *foo)
+{
+ return *foo;
+}
+
+float x = 0;
+
+void step()
+{
+ x = one(0);
+}
+
+int main(void)
+{
+ while(1)
+ {
+ step();
+ if(x == 0)
+ {
+ __VERIFIER_error();
+ }
+ }
+ return 0;
+}
diff --git a/regression/cbmc/graphml_witness2/test.desc b/regression/cbmc/graphml_witness2/test.desc
new file mode 100644
index 00000000000..6a97a4749fb
--- /dev/null
+++ b/regression/cbmc/graphml_witness2/test.desc
@@ -0,0 +1,10 @@
+CORE
+main.c
+--graphml-witness - --unwindset main.0:1 --unwinding-assertions
+^EXIT=10$
+^SIGNAL=0$
+^VERIFICATION FAILED$
+foo = \(\(float \*\)0\);
+one
+--
+^warning: ignoring
diff --git a/src/ansi-c/expr2c.cpp b/src/ansi-c/expr2c.cpp
index da3e16c4d11..077c43b2b39 100644
--- a/src/ansi-c/expr2c.cpp
+++ b/src/ansi-c/expr2c.cpp
@@ -42,7 +42,8 @@ expr2c_configurationt expr2c_configurationt::default_configuration
true,
true,
"TRUE",
- "FALSE"
+ "FALSE",
+ true
};
expr2c_configurationt expr2c_configurationt::clean_configuration
@@ -51,7 +52,8 @@ expr2c_configurationt expr2c_configurationt::clean_configuration
false,
false,
"1",
- "0"
+ "0",
+ false
};
// clang-format on
@@ -1857,14 +1859,39 @@ std::string expr2ct::convert_constant(
else if(dest.size()==4 &&
(dest[0]=='+' || dest[0]=='-'))
{
- if(dest=="+inf")
- dest="+INFINITY";
- else if(dest=="-inf")
- dest="-INFINITY";
- else if(dest=="+NaN")
- dest="+NAN";
- else if(dest=="-NaN")
- dest="-NAN";
+ if(configuration.use_library_macros)
+ {
+ if(dest == "+inf")
+ dest = "+INFINITY";
+ else if(dest == "-inf")
+ dest = "-INFINITY";
+ else if(dest == "+NaN")
+ dest = "+NAN";
+ else if(dest == "-NaN")
+ dest = "-NAN";
+ }
+ else
+ {
+ // ANSI-C: double is default; float/long-double require annotation
+ std::string suffix = "";
+ if(src.type() == float_type())
+ suffix = "f";
+ else if(
+ src.type() == long_double_type() &&
+ double_type() != long_double_type())
+ {
+ suffix = "l";
+ }
+
+ if(dest == "+inf")
+ dest = "(1.0" + suffix + "/0.0" + suffix + ")";
+ else if(dest == "-inf")
+ dest = "(-1.0" + suffix + "/0.0" + suffix + ")";
+ else if(dest == "+NaN")
+ dest = "(0.0" + suffix + "/0.0" + suffix + ")";
+ else if(dest == "-NaN")
+ dest = "(-0.0" + suffix + "/0.0" + suffix + ")";
+ }
}
}
else if(type.id()==ID_fixedbv)
@@ -1885,16 +1912,14 @@ std::string expr2ct::convert_constant(
}
else if(type.id()==ID_pointer)
{
- if(value==ID_NULL)
- {
- dest="NULL";
- if(type.subtype().id()!=ID_empty)
- dest="(("+convert(type)+")"+dest+")";
- }
- else if(value==std::string(value.size(), '0') &&
- config.ansi_c.NULL_is_zero)
+ if(
+ value == ID_NULL ||
+ (value == std::string(value.size(), '0') && config.ansi_c.NULL_is_zero))
{
- dest="NULL";
+ if(configuration.use_library_macros)
+ dest = "NULL";
+ else
+ dest = "0";
if(type.subtype().id()!=ID_empty)
dest="(("+convert(type)+")"+dest+")";
}
diff --git a/src/ansi-c/expr2c.h b/src/ansi-c/expr2c.h
index 03460c01909..e025a7f8b34 100644
--- a/src/ansi-c/expr2c.h
+++ b/src/ansi-c/expr2c.h
@@ -36,17 +36,22 @@ struct expr2c_configurationt final
/// This is the string that will be printed for the false boolean expression
std::string false_string;
+ /// This is the string that will be printed for null pointers
+ bool use_library_macros;
+
expr2c_configurationt(
const bool include_struct_padding_components,
const bool print_struct_body_in_type,
const bool include_array_size,
const std::string &true_string,
- const std::string &false_string)
+ const std::string &false_string,
+ const bool use_library_macros)
: include_struct_padding_components(include_struct_padding_components),
print_struct_body_in_type(print_struct_body_in_type),
include_array_size(include_array_size),
true_string(true_string),
- false_string(false_string)
+ false_string(false_string),
+ use_library_macros(use_library_macros)
{
}
diff --git a/src/goto-programs/graphml_witness.cpp b/src/goto-programs/graphml_witness.cpp
index 4e919938376..c81bbc9ef6d 100644
--- a/src/goto-programs/graphml_witness.cpp
+++ b/src/goto-programs/graphml_witness.cpp
@@ -11,16 +11,31 @@ Author: Daniel Kroening
#include "graphml_witness.h"
+#include
#include
#include
-#include
+#include
+#include
#include
#include
+#include
#include
+#include
+
+#include
#include "goto_program.h"
+static std::string
+expr_to_string(const namespacet &ns, const irep_idt &id, const exprt &expr)
+{
+ if(get_mode_from_identifier(ns, id) == ID_C)
+ return expr2c(expr, ns, expr2c_configurationt::clean_configuration);
+ else
+ return from_expr(ns, id, expr);
+}
+
void graphml_witnesst::remove_l0_l1(exprt &expr)
{
if(expr.id()==ID_symbol)
@@ -51,9 +66,32 @@ std::string graphml_witnesst::convert_assign_rec(
const irep_idt &identifier,
const code_assignt &assign)
{
+#ifdef USE_DSTRING
+ const auto cit = cache.find({identifier.get_no(), &assign.read()});
+#else
+ const auto cit =
+ cache.find({get_string_container()[id2string(identifier)], &assign.read()});
+#endif
+ if(cit != cache.end())
+ return cit->second;
+
std::string result;
- if(assign.rhs().id()==ID_array)
+ if(assign.rhs().id() == ID_array_list)
+ {
+ const array_list_exprt &array_list = to_array_list_expr(assign.rhs());
+ const auto &ops = array_list.operands();
+
+ for(std::size_t listidx = 0; listidx != ops.size(); listidx += 2)
+ {
+ const index_exprt index{assign.lhs(), ops[listidx]};
+ if(!result.empty())
+ result += ' ';
+ result +=
+ convert_assign_rec(identifier, code_assignt{index, ops[listidx + 1]});
+ }
+ }
+ else if(assign.rhs().id() == ID_array)
{
const array_typet &type = to_array_type(assign.rhs().type());
@@ -124,18 +162,57 @@ std::string graphml_witnesst::convert_assign_rec(
break;
}
}
+ else if(assign.rhs().id() == ID_with)
+ {
+ const with_exprt &with_expr = to_with_expr(assign.rhs());
+ const auto &ops = with_expr.operands();
+
+ for(std::size_t i = 1; i < ops.size(); i += 2)
+ {
+ if(!result.empty())
+ result += ' ';
+
+ if(ops[i].id() == ID_member_name)
+ {
+ const member_exprt member{
+ assign.lhs(), ops[i].get(ID_component_name), ops[i + 1].type()};
+ result +=
+ convert_assign_rec(identifier, code_assignt(member, ops[i + 1]));
+ }
+ else
+ {
+ const index_exprt index{assign.lhs(), ops[i]};
+ result +=
+ convert_assign_rec(identifier, code_assignt(index, ops[i + 1]));
+ }
+ }
+ }
else
{
exprt clean_rhs=assign.rhs();
remove_l0_l1(clean_rhs);
- std::string lhs=from_expr(ns, identifier, assign.lhs());
- if(lhs.find('$')!=std::string::npos)
+ exprt clean_lhs = assign.lhs();
+ remove_l0_l1(clean_lhs);
+ std::string lhs = expr_to_string(ns, identifier, clean_lhs);
+
+ if(
+ lhs.find("#return_value") != std::string::npos ||
+ (lhs.find('$') != std::string::npos &&
+ has_prefix(lhs, "return_value___VERIFIER_nondet_")))
+ {
lhs="\\result";
+ }
- result=lhs+" = "+from_expr(ns, identifier, clean_rhs)+";";
+ result = lhs + " = " + expr_to_string(ns, identifier, clean_rhs) + ";";
}
+#ifdef USE_DSTRING
+ cache.insert({{identifier.get_no(), &assign.read()}, result});
+#else
+ cache.insert(
+ {{get_string_container()[id2string(identifier)], &assign.read()}, result});
+#endif
return result;
}
@@ -169,22 +246,93 @@ static bool filter_out(
source_location.get_file().empty() ||
source_location.is_built_in() ||
source_location.get_line().empty())
+ {
+ const irep_idt id = source_location.get_function();
+ // Do not filter out assertions in functions the name of which starts with
+ // CPROVER_PREFIX, because we need to maintain those as violation nodes:
+ // these are assertions generated, for examples, for memory leaks.
+ if(!has_prefix(id2string(id), CPROVER_PREFIX) || !it->is_assert())
+ return true;
+ }
+
+ return false;
+}
+
+static bool contains_symbol_prefix(const exprt &expr, const std::string &prefix)
+{
+ if(
+ expr.id() == ID_symbol &&
+ has_prefix(id2string(to_symbol_expr(expr).get_identifier()), prefix))
+ {
return true;
+ }
+ forall_operands(it, expr)
+ {
+ if(contains_symbol_prefix(*it, prefix))
+ return true;
+ }
return false;
}
/// counterexample witness
void graphml_witnesst::operator()(const goto_tracet &goto_trace)
{
+ unsigned int max_thread_idx = 0;
+ bool trace_has_violation = false;
+ for(goto_tracet::stepst::const_iterator it = goto_trace.steps.begin();
+ it != goto_trace.steps.end();
+ ++it)
+ {
+ if(it->thread_nr > max_thread_idx)
+ max_thread_idx = it->thread_nr;
+ if(it->is_assert() && !it->cond_value)
+ trace_has_violation = true;
+ }
+
graphml.key_values["sourcecodelang"]="C";
const graphmlt::node_indext sink=graphml.add_node();
graphml[sink].node_name="sink";
- graphml[sink].thread_nr=0;
graphml[sink].is_violation=false;
graphml[sink].has_invariant=false;
+ if(max_thread_idx > 0 && trace_has_violation)
+ {
+ std::vector nodes;
+
+ for(unsigned int i = 0; i <= max_thread_idx + 1; ++i)
+ {
+ nodes.push_back(graphml.add_node());
+ graphml[nodes.back()].node_name = "N" + std::to_string(i);
+ graphml[nodes.back()].is_violation = i == max_thread_idx + 1;
+ graphml[nodes.back()].has_invariant = false;
+ }
+
+ for(auto it = nodes.cbegin(); std::next(it) != nodes.cend(); ++it)
+ {
+ xmlt edge("edge");
+ edge.set_attribute("source", graphml[*it].node_name);
+ edge.set_attribute("target", graphml[*std::next(it)].node_name);
+ const auto thread_id = std::distance(nodes.cbegin(), it);
+ xmlt &data = edge.new_element("data");
+ data.set_attribute("key", "createThread");
+ data.data = std::to_string(thread_id);
+ if(thread_id == 0)
+ {
+ xmlt &data = edge.new_element("data");
+ data.set_attribute("key", "enterFunction");
+ data.data = "main";
+ }
+ graphml[*std::next(it)].in[*it].xml_node = edge;
+ graphml[*it].out[*std::next(it)].xml_node = edge;
+ }
+
+ // we do not provide any further details as CPAchecker does not seem to
+ // handle more detailed concurrency witnesses
+ return;
+ }
+
// step numbers start at 1
std::vector step_to_node(goto_trace.steps.size()+1, 0);
@@ -223,7 +371,6 @@ void graphml_witnesst::operator()(const goto_tracet &goto_trace)
std::to_string(it->pc->location_number)+"."+std::to_string(it->step_nr);
graphml[node].file=source_location.get_file();
graphml[node].line=source_location.get_line();
- graphml[node].thread_nr=it->thread_nr;
graphml[node].is_violation=
it->type==goto_trace_stept::typet::ASSERT && !it->cond_value;
graphml[node].has_invariant=false;
@@ -231,6 +378,8 @@ void graphml_witnesst::operator()(const goto_tracet &goto_trace)
step_to_node[it->step_nr]=node;
}
+ unsigned thread_id = 0;
+
// build edges
for(goto_tracet::stepst::const_iterator
it=goto_trace.steps.begin();
@@ -239,7 +388,8 @@ void graphml_witnesst::operator()(const goto_tracet &goto_trace)
{
const std::size_t from=step_to_node[it->step_nr];
- if(from==sink)
+ // no outgoing edges from sinks or violation nodes
+ if(from == sink || graphml[from].is_violation)
{
++it;
continue;
@@ -262,73 +412,85 @@ void graphml_witnesst::operator()(const goto_tracet &goto_trace)
case goto_trace_stept::typet::ASSIGNMENT:
case goto_trace_stept::typet::ASSERT:
case goto_trace_stept::typet::GOTO:
+ case goto_trace_stept::typet::SPAWN:
+ {
+ xmlt edge(
+ "edge",
+ {{"source", graphml[from].node_name},
+ {"target", graphml[to].node_name}},
+ {});
+
{
- xmlt edge(
- "edge",
- {{"source", graphml[from].node_name},
- {"target", graphml[to].node_name}},
- {});
+ xmlt &data_f = edge.new_element("data");
+ data_f.set_attribute("key", "originfile");
+ data_f.data = id2string(graphml[from].file);
- {
- xmlt &data_f=edge.new_element("data");
- data_f.set_attribute("key", "originfile");
- data_f.data=id2string(graphml[from].file);
+ xmlt &data_l = edge.new_element("data");
+ data_l.set_attribute("key", "startline");
+ data_l.data = id2string(graphml[from].line);
- xmlt &data_l=edge.new_element("data");
- data_l.set_attribute("key", "startline");
- data_l.data=id2string(graphml[from].line);
- }
+ xmlt &data_t = edge.new_element("data");
+ data_t.set_attribute("key", "threadId");
+ data_t.data = std::to_string(it->thread_nr);
+ }
- if(it->type==goto_trace_stept::typet::ASSIGNMENT &&
- it->full_lhs_value.is_not_nil() &&
- it->full_lhs.is_not_nil())
+ const auto lhs_object = it->get_lhs_object();
+ if(
+ it->type == goto_trace_stept::typet::ASSIGNMENT &&
+ lhs_object.has_value())
+ {
+ const std::string &lhs_id = id2string(lhs_object->get_identifier());
+ if(lhs_id.find("pthread_create::thread") != std::string::npos)
+ {
+ xmlt &data_t = edge.new_element("data");
+ data_t.set_attribute("key", "createThread");
+ data_t.data = std::to_string(++thread_id);
+ }
+ else if(
+ !contains_symbol_prefix(
+ it->full_lhs_value, SYMEX_DYNAMIC_PREFIX "dynamic_object") &&
+ !contains_symbol_prefix(
+ it->full_lhs, SYMEX_DYNAMIC_PREFIX "dynamic_object") &&
+ lhs_id.find("thread") == std::string::npos &&
+ lhs_id.find("mutex") == std::string::npos &&
+ (!it->full_lhs_value.is_constant() ||
+ !it->full_lhs_value.has_operands() ||
+ !has_prefix(
+ id2string(it->full_lhs_value.op0().get(ID_value)), "INVALID-")))
{
- xmlt &val=edge.new_element("data");
+ xmlt &val = edge.new_element("data");
val.set_attribute("key", "assumption");
- val.data = from_expr(ns, it->function_id, it->full_lhs) + " = " +
- from_expr(ns, it->function_id, it->full_lhs_value) + ";";
- xmlt &val_s=edge.new_element("data");
+ code_assignt assign{it->full_lhs, it->full_lhs_value};
+ val.data = convert_assign_rec(lhs_id, assign);
+
+ xmlt &val_s = edge.new_element("data");
val_s.set_attribute("key", "assumption.scope");
- val_s.data = id2string(it->function_id);
- }
- else if(it->type==goto_trace_stept::typet::GOTO &&
- it->pc->is_goto())
- {
- xmlt &val=edge.new_element("data");
- val.set_attribute("key", "sourcecode");
- const std::string cond =
- from_expr(ns, it->function_id, it->cond_expr);
- const std::string neg_cond =
- from_expr(ns, it->function_id, not_exprt(it->cond_expr));
- val.data="["+(it->cond_value ? cond : neg_cond)+"]";
-
- #if 0
- xmlt edge2("edge", {
- {"source", graphml[from].node_name},
- {"target", graphml[sink].node_name}}, {});
-
- xmlt &data_f2=edge2.new_element("data");
- data_f2.set_attribute("key", "originfile");
- data_f2.data=id2string(graphml[from].file);
-
- xmlt &data_l2=edge2.new_element("data");
- data_l2.set_attribute("key", "startline");
- data_l2.data=id2string(graphml[from].line);
-
- xmlt &val2=edge2.new_element("data");
- val2.set_attribute("key", "sourcecode");
- val2.data="["+(it->cond_value ? neg_cond : cond)+"]";
-
- graphml[sink].in[from].xml_node=edge2;
- graphml[from].out[sink].xml_node=edge2;
- #endif
+ irep_idt function_id = it->function_id;
+ const symbolt *symbol_ptr = nullptr;
+ if(!ns.lookup(lhs_id, symbol_ptr) && symbol_ptr->is_parameter)
+ {
+ function_id = lhs_id.substr(0, lhs_id.find("::"));
+ }
+ val_s.data = id2string(function_id);
+
+ if(has_prefix(val.data, "\\result ="))
+ {
+ xmlt &val_f = edge.new_element("data");
+ val_f.set_attribute("key", "assumption.resultfunction");
+ val_f.data = id2string(it->function_id);
+ }
}
-
- graphml[to].in[from].xml_node=edge;
- graphml[from].out[to].xml_node=edge;
}
+ else if(it->type == goto_trace_stept::typet::GOTO && it->pc->is_goto())
+ {
+ }
+
+ graphml[to].in[from].xml_node = edge;
+ graphml[from].out[to].xml_node = edge;
+
break;
+ }
case goto_trace_stept::typet::DECL:
case goto_trace_stept::typet::FUNCTION_CALL:
@@ -339,7 +501,6 @@ void graphml_witnesst::operator()(const goto_tracet &goto_trace)
case goto_trace_stept::typet::OUTPUT:
case goto_trace_stept::typet::SHARED_READ:
case goto_trace_stept::typet::SHARED_WRITE:
- case goto_trace_stept::typet::SPAWN:
case goto_trace_stept::typet::MEMORY_BARRIER:
case goto_trace_stept::typet::ATOMIC_BEGIN:
case goto_trace_stept::typet::ATOMIC_END:
@@ -361,7 +522,6 @@ void graphml_witnesst::operator()(const symex_target_equationt &equation)
const graphmlt::node_indext sink=graphml.add_node();
graphml[sink].node_name="sink";
- graphml[sink].thread_nr=0;
graphml[sink].is_violation=false;
graphml[sink].has_invariant=false;
@@ -407,7 +567,6 @@ void graphml_witnesst::operator()(const symex_target_equationt &equation)
std::to_string(step_nr);
graphml[node].file=source_location.get_file();
graphml[node].line=source_location.get_line();
- graphml[node].thread_nr=it->source.thread_nr;
graphml[node].is_violation=false;
graphml[node].has_invariant=false;
@@ -448,49 +607,41 @@ void graphml_witnesst::operator()(const symex_target_equationt &equation)
case goto_trace_stept::typet::ASSIGNMENT:
case goto_trace_stept::typet::ASSERT:
case goto_trace_stept::typet::GOTO:
+ case goto_trace_stept::typet::SPAWN:
+ {
+ xmlt edge(
+ "edge",
+ {{"source", graphml[from].node_name},
+ {"target", graphml[to].node_name}},
+ {});
+
{
- xmlt edge(
- "edge",
- {{"source", graphml[from].node_name},
- {"target", graphml[to].node_name}},
- {});
+ xmlt &data_f = edge.new_element("data");
+ data_f.set_attribute("key", "originfile");
+ data_f.data = id2string(graphml[from].file);
- {
- xmlt &data_f=edge.new_element("data");
- data_f.set_attribute("key", "originfile");
- data_f.data=id2string(graphml[from].file);
+ xmlt &data_l = edge.new_element("data");
+ data_l.set_attribute("key", "startline");
+ data_l.data = id2string(graphml[from].line);
+ }
- xmlt &data_l=edge.new_element("data");
- data_l.set_attribute("key", "startline");
- data_l.data=id2string(graphml[from].line);
- }
+ if(
+ (it->is_assignment() || it->is_decl()) && it->ssa_rhs.is_not_nil() &&
+ it->ssa_full_lhs.is_not_nil())
+ {
+ irep_idt identifier = it->ssa_lhs.get_object_name();
- if((it->is_assignment() ||
- it->is_decl()) &&
- it->ssa_rhs.is_not_nil() &&
- it->ssa_full_lhs.is_not_nil())
- {
- irep_idt identifier=it->ssa_lhs.get_object_name();
+ graphml[to].has_invariant = true;
+ code_assignt assign(it->ssa_lhs, it->ssa_rhs);
+ graphml[to].invariant = convert_assign_rec(identifier, assign);
+ graphml[to].invariant_scope = id2string(it->source.function_id);
+ }
- graphml[to].has_invariant=true;
- code_assignt assign(it->ssa_full_lhs, it->ssa_rhs);
- graphml[to].invariant=convert_assign_rec(identifier, assign);
- graphml[to].invariant_scope = id2string(it->source.function_id);
- }
- else if(it->is_goto() &&
- it->source.pc->is_goto())
- {
- xmlt &val=edge.new_element("data");
- val.set_attribute("key", "sourcecode");
- const std::string cond =
- from_expr(ns, it->source.function_id, it->cond_expr);
- val.data="["+cond+"]";
- }
+ graphml[to].in[from].xml_node = edge;
+ graphml[from].out[to].xml_node = edge;
- graphml[to].in[from].xml_node=edge;
- graphml[from].out[to].xml_node=edge;
- }
break;
+ }
case goto_trace_stept::typet::DECL:
case goto_trace_stept::typet::FUNCTION_CALL:
@@ -501,7 +652,6 @@ void graphml_witnesst::operator()(const symex_target_equationt &equation)
case goto_trace_stept::typet::OUTPUT:
case goto_trace_stept::typet::SHARED_READ:
case goto_trace_stept::typet::SHARED_WRITE:
- case goto_trace_stept::typet::SPAWN:
case goto_trace_stept::typet::MEMORY_BARRIER:
case goto_trace_stept::typet::ATOMIC_BEGIN:
case goto_trace_stept::typet::ATOMIC_END:
diff --git a/src/goto-programs/graphml_witness.h b/src/goto-programs/graphml_witness.h
index 053eb801146..804dbf84a1a 100644
--- a/src/goto-programs/graphml_witness.h
+++ b/src/goto-programs/graphml_witness.h
@@ -42,6 +42,30 @@ class graphml_witnesst
std::string convert_assign_rec(
const irep_idt &identifier,
const code_assignt &assign);
+
+ template
+ static void hash_combine(std::size_t &seed, const T &v)
+ {
+ std::hash hasher;
+ seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
+ }
+
+ template
+ struct pair_hash // NOLINT(readability/identifiers)
+ {
+ std::size_t operator()(const std::pair &v) const
+ {
+ std::size_t seed = 0;
+ hash_combine(seed, v.first);
+ hash_combine(seed, v.second);
+ return seed;
+ }
+ };
+ std::unordered_map<
+ std::pair,
+ std::string,
+ pair_hash>
+ cache;
};
#endif // CPROVER_GOTO_PROGRAMS_GRAPHML_WITNESS_H
diff --git a/src/goto-programs/module_dependencies.txt b/src/goto-programs/module_dependencies.txt
index 817a766a240..b63faa217a6 100644
--- a/src/goto-programs/module_dependencies.txt
+++ b/src/goto-programs/module_dependencies.txt
@@ -1,4 +1,5 @@
analyses # dubious - concerns call_graph and does_remove_const
+ansi-c # for GraphML witnesses
goto-programs
goto-symex # dubious - spurious inclusion of symex_target_equation in graphml_witness
json
diff --git a/src/xmllang/graphml.cpp b/src/xmllang/graphml.cpp
index 17dc6fd9577..286912aa089 100644
--- a/src/xmllang/graphml.cpp
+++ b/src/xmllang/graphml.cpp
@@ -54,7 +54,6 @@ static bool build_graph_rec(
node.node_name=node_name;
node.is_violation=false;
node.has_invariant=false;
- node.thread_nr=0;
for(xmlt::elementst::const_iterator
e_it=xml.elements.begin();
@@ -66,8 +65,6 @@ static bool build_graph_rec(
if(e_it->get_attribute("key")=="violation" &&
e_it->data=="true")
node.is_violation=e_it->data=="true";
- else if(e_it->get_attribute("key")=="threadNumber")
- node.thread_nr=safe_string2unsigned(e_it->data);
else if(e_it->get_attribute("key")=="entry" &&
e_it->data=="true")
entrynode=node_name;
@@ -255,33 +252,6 @@ bool write_graphml(const graphmlt &src, std::ostream &os)
key.set_attribute("id", "invariant.scope");
}
- //
- // path
- //
- {
- xmlt &key=graphml.new_element("key");
- key.set_attribute("attr.name", "nodeType");
- key.set_attribute("attr.type", "string");
- key.set_attribute("for", "node");
- key.set_attribute("id", "nodetype");
-
- key.new_element("default").data="path";
- }
-
- //
- // false
- //
- {
- xmlt &key=graphml.new_element("key");
- key.set_attribute("attr.name", "isFrontierNode");
- key.set_attribute("attr.type", "boolean");
- key.set_attribute("for", "node");
- key.set_attribute("id", "frontier");
-
- key.new_element("default").data="false";
- }
-
//
// false
@@ -336,14 +306,39 @@ bool write_graphml(const graphmlt &src, std::ostream &os)
key.new_element("default").data="false";
}
+ //
+ // false
+ //
+ {
+ xmlt &key = graphml.new_element("key");
+ key.set_attribute("attr.name", "cyclehead");
+ key.set_attribute("attr.type", "boolean");
+ key.set_attribute("for", "edge");
+ key.set_attribute("id", "cyclehead");
+
+ key.new_element("default").data = "false";
+ }
+
//
- // TODO: format for multi-threaded programs not defined yet
{
xmlt &key=graphml.new_element("key");
- key.set_attribute("attr.name", "threadNumber");
+ key.set_attribute("attr.name", "threadId");
key.set_attribute("attr.type", "int");
- key.set_attribute("for", "node");
- key.set_attribute("id", "thread");
+ key.set_attribute("for", "edge");
+ key.set_attribute("id", "threadId");
+
+ key.new_element("default").data = "0";
+ }
+
+ //
+ {
+ xmlt &key = graphml.new_element("key");
+ key.set_attribute("attr.name", "createThread");
+ key.set_attribute("attr.type", "int");
+ key.set_attribute("for", "edge");
+ key.set_attribute("id", "createThread");
key.new_element("default").data="0";
}
@@ -408,15 +403,6 @@ bool write_graphml(const graphmlt &src, std::ostream &os)
key.set_attribute("id", "producer");
}
- //
- {
- xmlt &key=graphml.new_element("key");
- key.set_attribute("attr.name", "sourcecode");
- key.set_attribute("attr.type", "string");
- key.set_attribute("for", "edge");
- key.set_attribute("id", "sourcecode");
- }
-
//
{
xmlt &key=graphml.new_element("key");
@@ -525,13 +511,6 @@ bool write_graphml(const graphmlt &src, std::ostream &os)
entry_done=true;
}
- if(n.thread_nr!=0)
- {
- xmlt &entry=node.new_element("data");
- entry.set_attribute("key", "threadNumber");
- entry.data=std::to_string(n.thread_nr);
- }
-
//
// true
//
diff --git a/src/xmllang/graphml.h b/src/xmllang/graphml.h
index f86dac5648e..cbdcd122828 100644
--- a/src/xmllang/graphml.h
+++ b/src/xmllang/graphml.h
@@ -32,7 +32,6 @@ struct xml_graph_nodet:public graph_nodet
std::string node_name;
irep_idt file;
irep_idt line;
- unsigned thread_nr;
bool is_violation;
bool has_invariant;
std::string invariant;