Skip to content

Commit dc193c4

Browse files
authored
Merge pull request #8670 from tautschnig/convert-with
SMT2 back-end: flatten with_exprt operands
2 parents 062962c + 769f7a2 commit dc193c4

File tree

7 files changed

+160
-46
lines changed

7 files changed

+160
-46
lines changed

.github/workflows/pull-request-checks.yaml

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,10 @@ jobs:
7272
make TAGS="[!shouldfail]" -C jbmc/unit test IPASIR=$PWD/riss.git/riss
7373
- name: Run regression tests
7474
run: |
75+
export PATH=$PATH:`pwd`/src/solvers
7576
make -C regression test-parallel JOBS=${{env.linux-vcpus}} LIBS="$PWD/riss.git/release/lib/libriss-coprocessor.a -lpthread" IPASIR=$PWD/riss.git/riss
7677
make -C regression/cbmc test-paths-lifo
77-
env PATH=$PATH:`pwd`/src/solvers make -C regression/cbmc test-cprover-smt2
78+
make -C regression/cbmc test-cprover-smt2
7879
make -C jbmc/regression test-parallel JOBS=${{env.linux-vcpus}}
7980
- name: Check cleanup
8081
run: |
@@ -153,9 +154,10 @@ jobs:
153154
make TAGS="[!shouldfail]" -C jbmc/unit test
154155
- name: Run regression tests
155156
run: |
157+
export PATH=$PATH:`pwd`/src/solvers
156158
make -C regression test-parallel JOBS=${{env.linux-vcpus}}
157159
make -C regression/cbmc test-paths-lifo
158-
env PATH=$PATH:`pwd`/src/solvers make -C regression/cbmc test-cprover-smt2
160+
make -C regression/cbmc test-cprover-smt2
159161
make -C jbmc/regression test-parallel JOBS=${{env.linux-vcpus}}
160162
161163
# This job has been copied from the one above it, and modified to only build CBMC
@@ -339,9 +341,10 @@ jobs:
339341
make TAGS="[!shouldfail]" -C jbmc/unit test
340342
- name: Run regression tests
341343
run: |
344+
export PATH=$PATH:`pwd`/src/solvers
342345
make -C regression test-parallel JOBS=${{env.linux-vcpus}}
343346
make -C regression/cbmc test-paths-lifo
344-
env PATH=$PATH:`pwd`/src/solvers make -C regression/cbmc test-cprover-smt2
347+
make -C regression/cbmc test-cprover-smt2
345348
make -C jbmc/regression test-parallel JOBS=${{env.linux-vcpus}}
346349
347350
# This job takes approximately 17 to 41 minutes
@@ -689,7 +692,9 @@ jobs:
689692
- name: Run JBMC unit tests
690693
run: cd jbmc/unit; ./unit_tests
691694
- name: Run regression tests
692-
run: make -C regression test-parallel JOBS=4
695+
run: |
696+
export PATH=$PATH:`pwd`/src/solvers
697+
make -C regression test-parallel JOBS=4
693698
- name: Run JBMC regression tests
694699
run: make -C jbmc/regression test-parallel JOBS=4
695700

@@ -855,7 +860,9 @@ jobs:
855860
- name: Download minisat with make
856861
run: make -C src minisat2-download
857862
- name: Build CBMC with make
858-
run: make CXX=clcache BUILD_ENV=MSVC -j${{env.windows-vcpus}} -C src
863+
run: |
864+
make CXX=clcache BUILD_ENV=MSVC -j${{env.windows-vcpus}} -C src
865+
echo "$pwd\src\solvers;" >> $env:GITHUB_PATH
859866
- name: Build unit tests with make
860867
run: make CXX=clcache BUILD_ENV=MSVC -j${{env.windows-vcpus}} -C unit all
861868
- name: Build jbmc with make

regression/contracts-dfcc/CMakeLists.txt

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,15 @@ add_test_pl_profile(
4141
#)
4242

4343
# solver appears on the PATH in Windows already
44-
#if(NOT "${CMAKE_SYSTEM_NAME}" STREQUAL "Windows")
45-
# set_property(
46-
# TEST "cbmc-cprover-smt2-CORE"
47-
# PROPERTY ENVIRONMENT
48-
# "PATH=$ENV{PATH}:$<TARGET_FILE_DIR:smt2_solver>"
49-
# )
50-
#endif()
44+
if(NOT "${CMAKE_SYSTEM_NAME}" STREQUAL "Windows")
45+
set_property(
46+
TEST "contracts-dfcc-CORE"
47+
PROPERTY ENVIRONMENT
48+
"PATH=$ENV{PATH}:$<TARGET_FILE_DIR:smt2_solver>"
49+
)
50+
set_property(
51+
TEST "contracts-non-dfcc-CORE"
52+
PROPERTY ENVIRONMENT
53+
"PATH=$ENV{PATH}:$<TARGET_FILE_DIR:smt2_solver>"
54+
)
55+
endif()
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
typedef struct
2+
{
3+
int coeffs[1];
4+
} mld_poly;
5+
6+
typedef struct
7+
{
8+
mld_poly vec[1];
9+
} mld_polyveck;
10+
11+
int main()
12+
{
13+
mld_polyveck h;
14+
15+
// clang-format off
16+
for(unsigned int i = 0; i < 1; ++i)
17+
__CPROVER_loop_invariant(i <= 1)
18+
{
19+
h.vec[i].coeffs[0] = 1;
20+
}
21+
// clang-format on
22+
23+
return 0;
24+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
main.c
3+
--dfcc main --apply-loop-contracts _ --cprover-smt2
4+
^VERIFICATION SUCCESSFUL$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
8+
--
9+
Make sure the SMT back-end produces valid SMT2 when structs and arrays are
10+
nested in each other.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
typedef struct
2+
{
3+
int coeffs[2];
4+
} mlk_poly;
5+
6+
// clang-format off
7+
void mlk_poly_add(mlk_poly *r, const mlk_poly *b)
8+
__CPROVER_ensures(r->coeffs[0] == __CPROVER_old(*r).coeffs[0] + b->coeffs[0])
9+
__CPROVER_assigns(__CPROVER_object_upto(r, sizeof(mlk_poly)));
10+
// clang-format on
11+
12+
int main()
13+
{
14+
mlk_poly r[1];
15+
mlk_poly b[1];
16+
mlk_poly_add(&r[0], &b[0]);
17+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CORE
2+
main.c
3+
--dfcc main --replace-call-with-contract mlk_poly_add _ --no-array-field-sensitivity --cprover-smt2
4+
^\[mlk_poly_add.overflow.1\] line 8 arithmetic overflow on signed \+
5+
^\*\* 1 of \d+ failed
6+
^VERIFICATION FAILED$
7+
^EXIT=10$
8+
^SIGNAL=0$
9+
--
10+
--
11+
Make sure the SMT back-end produces valid SMT2 when structs and arrays are
12+
nested in each other.

src/solvers/smt2/smt2_conv.cpp

Lines changed: 73 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1407,9 +1407,35 @@ void smt2_convt::convert_expr(const exprt &expr)
14071407
out << "(ite ";
14081408
convert_expr(if_expr.cond());
14091409
out << " ";
1410-
convert_expr(if_expr.true_case());
1410+
if(
1411+
expr.type().id() == ID_array && !use_array_theory(if_expr.true_case()) &&
1412+
use_array_theory(if_expr.false_case()))
1413+
{
1414+
unflatten(wheret::BEGIN, expr.type());
1415+
1416+
convert_expr(if_expr.true_case());
1417+
1418+
unflatten(wheret::END, expr.type());
1419+
}
1420+
else
1421+
{
1422+
convert_expr(if_expr.true_case());
1423+
}
14111424
out << " ";
1412-
convert_expr(if_expr.false_case());
1425+
if(
1426+
expr.type().id() == ID_array && use_array_theory(if_expr.true_case()) &&
1427+
!use_array_theory(if_expr.false_case()))
1428+
{
1429+
unflatten(wheret::BEGIN, expr.type());
1430+
1431+
convert_expr(if_expr.false_case());
1432+
1433+
unflatten(wheret::END, expr.type());
1434+
}
1435+
else
1436+
{
1437+
convert_expr(if_expr.false_case());
1438+
}
14131439
out << ")";
14141440
}
14151441
else if(expr.id()==ID_and ||
@@ -4400,50 +4426,63 @@ void smt2_convt::convert_with(const with_exprt &expr)
44004426
}
44014427
else
44024428
{
4429+
auto convert_operand = [this](const exprt &op)
4430+
{
4431+
// may need to flatten array-theory arrays in there
4432+
if(op.type().id() == ID_array && use_array_theory(op))
4433+
flatten_array(op);
4434+
else if(op.type().id() == ID_bool)
4435+
flatten2bv(op);
4436+
else
4437+
convert_expr(op);
4438+
};
4439+
44034440
std::size_t struct_width=boolbv_width(struct_type);
44044441

44054442
// figure out the offset and width of the member
44064443
const boolbv_widtht::membert &m =
44074444
boolbv_width.get_member(struct_type, component_name);
44084445

4409-
out << "(let ((?withop ";
4410-
convert_expr(expr.old());
4411-
out << ")) ";
4412-
44134446
if(m.width==struct_width)
44144447
{
4415-
// the struct is the same as the member, no concat needed,
4416-
// ?withop won't be used
4417-
convert_expr(value);
4418-
}
4419-
else if(m.offset==0)
4420-
{
4421-
// the member is at the beginning
4422-
out << "(concat "
4423-
<< "((_ extract " << (struct_width-1) << " "
4424-
<< m.width << ") ?withop) ";
4425-
convert_expr(value);
4426-
out << ")"; // concat
4427-
}
4428-
else if(m.offset+m.width==struct_width)
4429-
{
4430-
// the member is at the end
4431-
out << "(concat ";
4432-
convert_expr(value);
4433-
out << " ((_ extract " << (m.offset - 1) << " 0) ?withop))";
4448+
// the struct is the same as the member, no concat needed
4449+
convert_operand(value);
44344450
}
44354451
else
44364452
{
4437-
// most general case, need two concat-s
4438-
out << "(concat (concat "
4439-
<< "((_ extract " << (struct_width-1) << " "
4440-
<< (m.offset+m.width) << ") ?withop) ";
4441-
convert_expr(value);
4442-
out << ") ((_ extract " << (m.offset-1) << " 0) ?withop)";
4443-
out << ")"; // concat
4444-
}
4453+
out << "(let ((?withop ";
4454+
convert_operand(expr.old());
4455+
out << ")) ";
44454456

4446-
out << ")"; // let ?withop
4457+
if(m.offset == 0)
4458+
{
4459+
// the member is at the beginning
4460+
out << "(concat "
4461+
<< "((_ extract " << (struct_width - 1) << " " << m.width
4462+
<< ") ?withop) ";
4463+
convert_operand(value);
4464+
out << ")"; // concat
4465+
}
4466+
else if(m.offset + m.width == struct_width)
4467+
{
4468+
// the member is at the end
4469+
out << "(concat ";
4470+
convert_operand(value);
4471+
out << " ((_ extract " << (m.offset - 1) << " 0) ?withop))";
4472+
}
4473+
else
4474+
{
4475+
// most general case, need two concat-s
4476+
out << "(concat (concat "
4477+
<< "((_ extract " << (struct_width - 1) << " "
4478+
<< (m.offset + m.width) << ") ?withop) ";
4479+
convert_operand(value);
4480+
out << ") ((_ extract " << (m.offset - 1) << " 0) ?withop)";
4481+
out << ")"; // concat
4482+
}
4483+
4484+
out << ")"; // let ?withop
4485+
}
44474486
}
44484487
}
44494488
else if(expr_type.id() == ID_union || expr_type.id() == ID_union_tag)

0 commit comments

Comments
 (0)