Skip to content

Commit dd5ab8f

Browse files
Enable sharing of irept's as long as no references are held to sub-parts
A PR by @reuk some time ago prevented sharing when references are held to sub-parts but didn't re-enable sharing in situations where it is actually safe to do so. This PR amends that. This should improve performance, particularly in memory usage.
1 parent 1e3a9cd commit dd5ab8f

File tree

5 files changed

+97
-147
lines changed

5 files changed

+97
-147
lines changed

Diff for: src/util/expr.cpp

+18-42
Original file line numberDiff line numberDiff line change
@@ -21,64 +21,40 @@ Author: Daniel Kroening, [email protected]
2121

2222
void exprt::move_to_operands(exprt &expr)
2323
{
24-
operandst &op=operands();
25-
op.push_back(static_cast<const exprt &>(get_nil_irep()));
26-
op.back().swap(expr);
24+
move_to_sub(expr);
2725
}
2826

2927
void exprt::move_to_operands(exprt &e1, exprt &e2)
3028
{
31-
operandst &op=operands();
32-
#ifndef USE_LIST
33-
op.reserve(op.size()+2);
34-
#endif
35-
op.push_back(static_cast<const exprt &>(get_nil_irep()));
36-
op.back().swap(e1);
37-
op.push_back(static_cast<const exprt &>(get_nil_irep()));
38-
op.back().swap(e2);
29+
reserve_operands(get_operands_size() + 2);
30+
move_to_sub(e1);
31+
move_to_sub(e2);
3932
}
4033

4134
void exprt::move_to_operands(exprt &e1, exprt &e2, exprt &e3)
4235
{
43-
operandst &op=operands();
44-
#ifndef USE_LIST
45-
op.reserve(op.size()+3);
46-
#endif
47-
op.push_back(static_cast<const exprt &>(get_nil_irep()));
48-
op.back().swap(e1);
49-
op.push_back(static_cast<const exprt &>(get_nil_irep()));
50-
op.back().swap(e2);
51-
op.push_back(static_cast<const exprt &>(get_nil_irep()));
52-
op.back().swap(e3);
36+
reserve_operands(get_operands_size() + 3);
37+
move_to_sub(e1);
38+
move_to_sub(e2);
39+
move_to_sub(e3);
5340
}
5441

55-
void exprt::copy_to_operands(const exprt &expr)
42+
void exprt::copy_to_operands(exprt expr)
5643
{
57-
operands().push_back(expr);
44+
// Move the copy created by passing the argument by value to the operands
45+
move_to_operands(expr);
5846
}
5947

60-
void exprt::copy_to_operands(const exprt &e1, const exprt &e2)
48+
void exprt::copy_to_operands(exprt e1, exprt e2)
6149
{
62-
operandst &op=operands();
63-
#ifndef USE_LIST
64-
op.reserve(op.size()+2);
65-
#endif
66-
op.push_back(e1);
67-
op.push_back(e2);
50+
// Move the copies created by passing the arguments by value to the operands
51+
move_to_operands(e1, e2);
6852
}
6953

70-
void exprt::copy_to_operands(
71-
const exprt &e1,
72-
const exprt &e2,
73-
const exprt &e3)
54+
void exprt::copy_to_operands(exprt e1, exprt e2, exprt e3)
7455
{
75-
operandst &op=operands();
76-
#ifndef USE_LIST
77-
op.reserve(op.size()+3);
78-
#endif
79-
op.push_back(e1);
80-
op.push_back(e2);
81-
op.push_back(e3);
56+
// Move the copies created by passing the arguments by value to the operands
57+
move_to_operands(e1, e2, e3);
8258
}
8359

8460
void exprt::make_typecast(const typet &_type)
@@ -105,7 +81,7 @@ void exprt::make_not()
10581

10682
if(id()==ID_not && operands().size()==1)
10783
{
108-
new_expr.swap(operands().front());
84+
new_expr.swap(op0());
10985
}
11086
else
11187
{

Diff for: src/util/expr.h

+10-4
Original file line numberDiff line numberDiff line change
@@ -93,17 +93,23 @@ class exprt:public irept
9393
const exprt &op3() const
9494
{ return operands()[3]; }
9595

96+
operandst::size_type get_operands_size() const
97+
{
98+
return get_sub_size();
99+
}
96100
void reserve_operands(operandst::size_type n)
97-
{ operands().reserve(n) ; }
101+
{
102+
reserve_sub(n);
103+
}
98104

99105
// destroys expr, e1, e2, e3
100106
void move_to_operands(exprt &expr);
101107
void move_to_operands(exprt &e1, exprt &e2);
102108
void move_to_operands(exprt &e1, exprt &e2, exprt &e3);
103109
// does not destroy expr, e1, e2, e3
104-
void copy_to_operands(const exprt &expr);
105-
void copy_to_operands(const exprt &e1, const exprt &e2);
106-
void copy_to_operands(const exprt &e1, const exprt &e2, const exprt &e3);
110+
void copy_to_operands(exprt expr);
111+
void copy_to_operands(exprt e1, exprt e2);
112+
void copy_to_operands(exprt e1, exprt e2, exprt e3);
107113

108114
// the following are deprecated -- use constructors instead
109115
void make_typecast(const typet &_type);

Diff for: src/util/irep.cpp

+14-38
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,14 @@ const irept &get_nil_irep()
5757

5858
void irept::move_to_named_sub(const irep_namet &name, irept &irep)
5959
{
60-
add(name).swap(irep);
60+
add(name, std::move(irep));
6161
irep.clear();
6262
}
6363

6464
void irept::move_to_sub(irept &irep)
6565
{
66-
get_sub().push_back(get_nil_irep());
67-
get_sub().back().swap(irep);
66+
write(true).sub.push_back(std::move(irep));
67+
irep.clear();
6868
}
6969

7070
const irep_idt &irept::get(const irep_namet &name) const
@@ -121,13 +121,13 @@ long long irept::get_long_long(const irep_namet &name) const
121121

122122
void irept::set(const irep_namet &name, const long long value)
123123
{
124-
add(name).id(std::to_string(value));
124+
set(name, std::to_string(value));
125125
}
126126

127127
void irept::remove(const irep_namet &name)
128128
{
129-
named_subt &s=
130-
is_comment(name)?get_comments():get_named_sub();
129+
named_subt &s =
130+
is_comment(name) ? write(true).comments : write(true).named_sub;
131131

132132
#ifdef SUB_IS_LIST
133133
named_subt::iterator it=named_subt_lower_bound(s, name);
@@ -160,47 +160,23 @@ const irept &irept::find(const irep_namet &name) const
160160
return it->second;
161161
}
162162

163-
irept &irept::add(const irep_namet &name)
163+
irept &irept::add(const irep_namet &name, bool mark_sharable)
164164
{
165-
named_subt &s=
166-
is_comment(name)?get_comments():get_named_sub();
165+
named_subt &s =
166+
is_comment(name)
167+
? write(mark_sharable).comments : write(mark_sharable).named_sub;
167168

168169
#ifdef SUB_IS_LIST
169-
named_subt::iterator it=named_subt_lower_bound(s, name);
170-
171-
if(it==s.end() ||
172-
it->first!=name)
173-
it=s.insert(it, std::make_pair(name, irept()));
174170

175-
return it->second;
176-
#else
177-
return s[name];
178-
#endif
179-
}
180-
181-
irept &irept::add(const irep_namet &name, const irept &irep)
182-
{
183-
named_subt &s=
184-
is_comment(name)?get_comments():get_named_sub();
185-
186-
#ifdef SUB_IS_LIST
187171
named_subt::iterator it=named_subt_lower_bound(s, name);
188-
189-
if(it==s.end() ||
190-
it->first!=name)
191-
it=s.insert(it, std::make_pair(name, irep));
192-
else
193-
it->second=irep;
194-
172+
if(it == s.end() || it->first != name)
173+
it = s.emplace(it, name, irept());
195174
return it->second;
175+
196176
#else
197-
std::pair<named_subt::iterator, bool> entry=
198-
s.insert(std::make_pair(name, irep));
199177

200-
if(!entry.second)
201-
entry.first->second=irep;
178+
return s[name];
202179

203-
return entry.first->second;
204180
#endif
205181
}
206182

Diff for: src/util/irep.h

+35-3
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,14 @@ class irept
125125
}
126126

127127
const irept &find(const irep_namet &name) const;
128-
irept &add(const irep_namet &name);
129-
irept &add(const irep_namet &name, const irept &irep);
128+
irept &add(const irep_namet &name)
129+
{
130+
return add(name, false);
131+
}
132+
void add(const irep_namet &name, irept irep)
133+
{
134+
add(name, true) = std::move(irep);
135+
}
130136

131137
const std::string &get_string(const irep_namet &name) const
132138
{
@@ -141,7 +147,9 @@ class irept
141147
long long get_long_long(const irep_namet &name) const;
142148

143149
void set(const irep_namet &name, const irep_idt &value)
144-
{ add(name).id(value); }
150+
{
151+
add(name, true).id(value);
152+
}
145153
void set(const irep_namet &name, const irept &irep)
146154
{ add(name, irep); }
147155
void set(const irep_namet &name, const long long value);
@@ -176,6 +184,16 @@ class irept
176184
return write(false).sub;
177185
}
178186
const subt &get_sub() const { return read().sub; }
187+
subt::size_type get_sub_size() const
188+
{
189+
return read().sub.size();
190+
}
191+
void reserve_sub(subt::size_type n)
192+
{
193+
#ifndef USE_LIST
194+
write(true).sub.reserve(n);
195+
#endif
196+
}
179197
named_subt &get_named_sub()
180198
{
181199
return write(false).named_sub;
@@ -199,6 +217,20 @@ class irept
199217
{ return !name.empty() && name[0]=='#'; }
200218

201219
private:
220+
/// Adds a new irept to the comments or named_sub collection with the given
221+
/// name or retrieves the existing irept with the matching name
222+
/// \remarks
223+
/// If name starts with a hash symbol ('#') then the comments collection is
224+
/// used, otherwise the named_sub collection is used
225+
/// If the reference returned by this call is potentially held long enough
226+
/// that a copy of this irept could be made before it is disposed of then you
227+
/// must pass false for mark_sharable to ensure safe behaviour.
228+
/// \param name: The name of the irept to add/retrieve
229+
/// \param mark_sharable: If false then future copies of this irept will
230+
/// not use sharing
231+
/// \returns The added or retrieved irept
232+
irept &add(const irep_namet &name, bool mark_sharable);
233+
202234
class dt
203235
#ifdef SHARING
204236
: public copy_on_write_pointeet<unsigned>

Diff for: unit/util/irep_sharing.cpp

+20-60
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
#ifdef SHARING
99

10-
SCENARIO("irept_sharing_trade_offs", "[!mayfail][core][utils][irept]")
10+
SCENARIO("irept_sharing", "[core][utils][irept]")
1111
{
1212
GIVEN("An irept created with move_to_sub")
1313
{
@@ -21,35 +21,6 @@ SCENARIO("irept_sharing_trade_offs", "[!mayfail][core][utils][irept]")
2121
irept irep = test_irep;
2222
REQUIRE(&irep.read() == &test_irep.read());
2323
}
24-
THEN("Changing subs in the original using a reference taken before "
25-
"copying should not change them in the copy")
26-
{
27-
irept &sub = test_irep.get_sub()[0];
28-
irept irep = test_irep;
29-
sub.id(ID_0);
30-
REQUIRE(test_irep.get_sub()[0].id() == ID_0);
31-
REQUIRE(irep.get_sub()[0].id() == ID_1);
32-
}
33-
THEN("Holding a reference to a sub should not prevent sharing")
34-
{
35-
irept &sub = test_irep.get_sub()[0];
36-
irept irep = test_irep;
37-
REQUIRE(&irep.read() == &test_irep.read());
38-
sub = irept(ID_0);
39-
REQUIRE(irep.get_sub()[0].id() == test_irep.get_sub()[0].id());
40-
}
41-
}
42-
}
43-
44-
SCENARIO("irept_sharing", "[core][utils][irept]")
45-
{
46-
GIVEN("An irept created with move_to_sub")
47-
{
48-
irept test_irep(ID_1);
49-
irept test_subirep(ID_1);
50-
irept test_subsubirep(ID_1);
51-
test_subirep.move_to_sub(test_subsubirep);
52-
test_irep.move_to_sub(test_subirep);
5324
THEN("Copies of a copy should return object with the same address")
5425
{
5526
irept irep1 = test_irep;
@@ -69,6 +40,15 @@ SCENARIO("irept_sharing", "[core][utils][irept]")
6940
REQUIRE(test_irep.get_sub()[0].id() == ID_1);
7041
REQUIRE(irep.get_sub()[0].id() == ID_0);
7142
}
43+
THEN("Changing subs in the original using a reference taken before "
44+
"copying should not change them in the copy")
45+
{
46+
irept &sub = test_irep.get_sub()[0];
47+
irept irep = test_irep;
48+
sub.id(ID_0);
49+
REQUIRE(test_irep.get_sub()[0].id() == ID_0);
50+
REQUIRE(irep.get_sub()[0].id() == ID_1);
51+
}
7252
THEN("Getting a reference to a sub in a copy should break sharing")
7353
{
7454
irept irep = test_irep;
@@ -104,7 +84,7 @@ SCENARIO("irept_sharing", "[core][utils][irept]")
10484

10585
#include <util/expr.h>
10686

107-
SCENARIO("exprt_sharing_trade_offs", "[!mayfail][core][utils][exprt]")
87+
SCENARIO("exprt_sharing", "[core][utils][exprt]")
10888
{
10989
GIVEN("An expression created with move_to_operands")
11090
{
@@ -118,35 +98,6 @@ SCENARIO("exprt_sharing_trade_offs", "[!mayfail][core][utils][exprt]")
11898
exprt expr = test_expr;
11999
REQUIRE(&expr.read() == &test_expr.read());
120100
}
121-
THEN("Changing operands in the original using a reference taken before "
122-
"copying should not change them in the copy")
123-
{
124-
exprt &operand = test_expr.op0();
125-
exprt expr = test_expr;
126-
operand.id(ID_0);
127-
REQUIRE(test_expr.op0().id() == ID_0);
128-
REQUIRE(expr.op0().id() == ID_1);
129-
}
130-
THEN("Holding a reference to an operand should not prevent sharing")
131-
{
132-
exprt &operand = test_expr.op0();
133-
exprt expr = test_expr;
134-
REQUIRE(&expr.read() == &test_expr.read());
135-
operand = exprt(ID_0);
136-
REQUIRE(expr.op0().id() == test_expr.op0().id());
137-
}
138-
}
139-
}
140-
141-
SCENARIO("exprt_sharing", "[core][utils][exprt]")
142-
{
143-
GIVEN("An expression created with move_to_operands")
144-
{
145-
exprt test_expr(ID_1);
146-
exprt test_subexpr(ID_1);
147-
exprt test_subsubexpr(ID_1);
148-
test_subexpr.move_to_operands(test_subsubexpr);
149-
test_expr.move_to_operands(test_subexpr);
150101
THEN("Copies of a copy should return object with the same address")
151102
{
152103
exprt expr1 = test_expr;
@@ -166,6 +117,15 @@ SCENARIO("exprt_sharing", "[core][utils][exprt]")
166117
REQUIRE(test_expr.op0().id() == ID_1);
167118
REQUIRE(expr.op0().id() == ID_0);
168119
}
120+
THEN("Changing operands in the original using a reference taken before "
121+
"copying should not change them in the copy")
122+
{
123+
exprt &operand = test_expr.op0();
124+
exprt expr = test_expr;
125+
operand.id(ID_0);
126+
REQUIRE(test_expr.op0().id() == ID_0);
127+
REQUIRE(expr.op0().id() == ID_1);
128+
}
169129
THEN("Getting a reference to an operand in a copy should break sharing")
170130
{
171131
exprt expr = test_expr;

0 commit comments

Comments
 (0)