Skip to content

Commit 1060c57

Browse files
authored
Merge pull request #3562 from smowton/smowton/feature/simplify-truncating-cast
Simplifier: transform ((struct A)x).field1 into x.a.b.field2
2 parents 69f54ca + d47a07c commit 1060c57

12 files changed

+156
-0
lines changed
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
interface Intf {
2+
3+
public int f();
4+
5+
}
Binary file not shown.
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
2+
public class Test {
3+
4+
public static void main() {
5+
6+
// The following line is only needed so that multiple possible callees
7+
// for the interface Intf are found, and so a dispatch table
8+
// (if i->@class_identfier == "Impl1" then ...) is generated.
9+
Impl2 unused = new Impl2();
10+
11+
// This should be easily verified, as long as symex can determine that *i
12+
// certainly refers to an instance of Impl1, and so i.f() is certainly a
13+
// call to Impl1.f. This requires it to simplify the expression
14+
// "((struct Intf)some_impl1_instance)[email protected].@class_identifier".
15+
Intf i = new Impl1();
16+
int got = i.f();
17+
assert got == 1;
18+
19+
}
20+
21+
public static void main_derived_class() {
22+
23+
// This is just like the "main" method, except a class with a superclass
24+
// is used, to check that the cast to struct Intf can be simplified even
25+
// when the object doesn't directly have such a member (instead it has a
26+
// member "@Impl1", which itself has a "@java.lang.Object").
27+
28+
Impl2 unused = new Impl2();
29+
30+
Intf i = new Impl1Sub();
31+
int got = i.f();
32+
assert got == 1;
33+
34+
}
35+
36+
}
37+
38+
class Impl1 implements Intf {
39+
40+
// This field is important-- without it, the structures of Impl1 and Intf
41+
// are the same (both being simply { java.lang.Object @java.lang.Object }),
42+
// which allows the expression simplifier to take an easier route than the one
43+
// we intend to test.
44+
public int x;
45+
46+
public int f() {
47+
return 1;
48+
}
49+
50+
}
51+
52+
class Impl1Sub extends Impl1 {
53+
54+
public int z;
55+
56+
}
57+
58+
class Impl2 implements Intf {
59+
60+
public int f() {
61+
return 2;
62+
}
63+
64+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
CORE
2+
Test.class
3+
--function Test.main
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
^Passing problem to
9+
^warning: ignoring
10+
--
11+
The "Passing problem to..." line is a sign that symex couldn't prove the
12+
assertion is valid by itself, which in turn probably indicates a weakness of the
13+
expression simplifier. The companion test_no_simplify.desc checks that the
14+
"Passing problem to" line is indeed present when we indeed resort to the SAT
15+
solver.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
CORE
2+
Test.class
3+
--function Test.main_derived_class
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
^Passing problem to
9+
^warning: ignoring
10+
--
11+
The "Passing problem to..." line is a sign that symex couldn't prove the
12+
assertion is valid by itself, which in turn probably indicates a weakness of the
13+
expression simplifier. The companion test_no_propagation.desc checks that the
14+
"Passing problem to" line is indeed present when we indeed resort to the SAT
15+
solver.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
CORE
2+
Test.class
3+
--function Test.main --no-simplify --unwind 10
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
^Passing problem to
8+
--
9+
^warning: ignoring
10+
--
11+
This is just to check the "Passing problem to" line is printed when the solver
12+
is invoked -- the real test (test.desc) depends on that to check that its test
13+
code was analysed entirely by goto-symex. If the output format changes this test
14+
should notice that.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CORE
2+
Test.class
3+
--function Test.main --no-simplify --unwind 10 --show-vcc
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
"java::Impl1" = cast\(\{ \{ "java::Impl1"\}, 0\}, struct \{ struct \{ string @class_identifier \} @java.lang.Object \}\)\[email protected]\.@class_identifier
7+
--
8+
^warning: ignoring
9+
--
10+
This checks that the test generates the VCC testing the class-id that we're
11+
intending to simplify away in the main test.

src/util/simplify_expr_struct.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Author: Daniel Kroening, [email protected]
1414
#include "namespace.h"
1515
#include "pointer_offset_size.h"
1616
#include "std_expr.h"
17+
#include "type_eq.h"
1718

1819
bool simplify_exprt::simplify_member(exprt &expr)
1920
{
@@ -240,6 +241,37 @@ bool simplify_exprt::simplify_member(exprt &expr)
240241
simplify_member(expr);
241242
return false;
242243
}
244+
245+
// Try to translate into an equivalent member (perhaps nested) of the type
246+
// being cast (for example, this might turn ((struct A)x).field1 into
247+
// x.substruct.othersubstruct.field2, if field1 and field2 have the same
248+
// type and offset with respect to x.
249+
if(op_type.id() == ID_struct)
250+
{
251+
optionalt<mp_integer> requested_offset =
252+
member_offset(to_struct_type(op_type), component_name, ns);
253+
if(requested_offset.has_value())
254+
{
255+
exprt equivalent_member = get_subexpression_at_offset(
256+
op.op0(), *requested_offset, expr.type(), ns);
257+
258+
// Guess: turning this into a byte-extract operation is not really an
259+
// optimisation.
260+
// The type_eq check is because get_subexpression_at_offset uses
261+
// base_type_eq, whereas in the context of a simplifier we should not
262+
// change the type of the expression.
263+
if(
264+
equivalent_member.is_not_nil() &&
265+
equivalent_member.id() != ID_byte_extract_little_endian &&
266+
equivalent_member.id() != ID_byte_extract_big_endian &&
267+
type_eq(equivalent_member.type(), expr.type(), ns))
268+
{
269+
expr = equivalent_member;
270+
simplify_rec(expr);
271+
return false;
272+
}
273+
}
274+
}
243275
}
244276
else if(op.id()==ID_if)
245277
{

0 commit comments

Comments
 (0)