@@ -40,7 +40,7 @@ using namespace yul;
40
40
41
41
void DataFlowAnalyzer::operator ()(ExpressionStatement& _statement)
42
42
{
43
- if (boost::optional<pair<YulString, YulString>> vars = isSimpleSStore ( _statement))
43
+ if (auto vars = isSimpleStore (dev::eth::Instruction::SSTORE, _statement))
44
44
{
45
45
ASTModifier::operator ()(_statement);
46
46
m_storage.set (vars->first , vars->second );
@@ -54,9 +54,22 @@ void DataFlowAnalyzer::operator()(ExpressionStatement& _statement)
54
54
for (YulString const & key: keysToErase)
55
55
m_storage.eraseKey (key);
56
56
}
57
+ else if (auto vars = isSimpleStore (dev::eth::Instruction::MSTORE, _statement))
58
+ {
59
+ ASTModifier::operator ()(_statement);
60
+ set<YulString> keysToErase;
61
+ for (auto const & item: m_memory.values )
62
+ if (!m_knowledgeBase.knownToBeDifferentByAtLeast32 (vars->first , item.first ))
63
+ keysToErase.insert (item.first );
64
+ // TODO is it fine to do that here?
65
+ // can we also move the storage above?
66
+ m_memory.set (vars->first , vars->second );
67
+ for (YulString const & key: keysToErase)
68
+ m_memory.eraseKey (key);
69
+ }
57
70
else
58
71
{
59
- clearStorageKnowledgeIfInvalidated (_statement.expression );
72
+ clearKnowledgeIfInvalidated (_statement.expression );
60
73
ASTModifier::operator ()(_statement);
61
74
}
62
75
}
@@ -67,7 +80,7 @@ void DataFlowAnalyzer::operator()(Assignment& _assignment)
67
80
for (auto const & var: _assignment.variableNames )
68
81
names.emplace (var.name );
69
82
assertThrow (_assignment.value , OptimizerException, " " );
70
- clearStorageKnowledgeIfInvalidated (*_assignment.value );
83
+ clearKnowledgeIfInvalidated (*_assignment.value );
71
84
visit (*_assignment.value );
72
85
handleAssignment (names, _assignment.value .get ());
73
86
}
@@ -81,7 +94,7 @@ void DataFlowAnalyzer::operator()(VariableDeclaration& _varDecl)
81
94
82
95
if (_varDecl.value )
83
96
{
84
- clearStorageKnowledgeIfInvalidated (*_varDecl.value );
97
+ clearKnowledgeIfInvalidated (*_varDecl.value );
85
98
visit (*_varDecl.value );
86
99
}
87
100
@@ -90,12 +103,13 @@ void DataFlowAnalyzer::operator()(VariableDeclaration& _varDecl)
90
103
91
104
void DataFlowAnalyzer::operator ()(If& _if)
92
105
{
93
- clearStorageKnowledgeIfInvalidated (*_if.condition );
106
+ clearKnowledgeIfInvalidated (*_if.condition );
94
107
InvertibleMap<YulString, YulString> storage = m_storage;
108
+ InvertibleMap<YulString, YulString> memory = m_memory;
95
109
96
110
ASTModifier::operator ()(_if);
97
111
98
- joinStorageKnowledge (storage);
112
+ joinKnowledge (storage, memory );
99
113
100
114
Assignments assignments;
101
115
assignments (_if.body );
@@ -104,24 +118,25 @@ void DataFlowAnalyzer::operator()(If& _if)
104
118
105
119
void DataFlowAnalyzer::operator ()(Switch& _switch)
106
120
{
107
- clearStorageKnowledgeIfInvalidated (*_switch.expression );
121
+ clearKnowledgeIfInvalidated (*_switch.expression );
108
122
visit (*_switch.expression );
109
123
set<YulString> assignedVariables;
110
124
for (auto & _case: _switch.cases )
111
125
{
112
126
InvertibleMap<YulString, YulString> storage = m_storage;
127
+ InvertibleMap<YulString, YulString> memory = m_memory;
113
128
(*this )(_case.body );
114
- joinStorageKnowledge (storage);
129
+ joinKnowledge (storage, memory );
115
130
116
131
Assignments assignments;
117
132
assignments (_case.body );
118
133
assignedVariables += assignments.names ();
119
134
// This is a little too destructive, we could retain the old values.
120
135
clearValues (assignments.names ());
121
- clearStorageKnowledgeIfInvalidated (_case.body );
136
+ clearKnowledgeIfInvalidated (_case.body );
122
137
}
123
138
for (auto & _case: _switch.cases )
124
- clearStorageKnowledgeIfInvalidated (_case.body );
139
+ clearKnowledgeIfInvalidated (_case.body );
125
140
clearValues (assignedVariables);
126
141
}
127
142
@@ -132,9 +147,11 @@ void DataFlowAnalyzer::operator()(FunctionDefinition& _fun)
132
147
map<YulString, Expression const *> value;
133
148
InvertibleRelation<YulString> references;
134
149
InvertibleMap<YulString, YulString> storage;
150
+ InvertibleMap<YulString, YulString> memory;
135
151
m_value.swap (value);
136
152
swap (m_references, references);
137
153
swap (m_storage, storage);
154
+ swap (m_memory, memory);
138
155
pushScope (true );
139
156
140
157
for (auto const & parameter: _fun.parameters )
@@ -150,6 +167,7 @@ void DataFlowAnalyzer::operator()(FunctionDefinition& _fun)
150
167
m_value.swap (value);
151
168
swap (m_references, references);
152
169
swap (m_storage, storage);
170
+ swap (m_memory, memory);
153
171
}
154
172
155
173
void DataFlowAnalyzer::operator ()(ForLoop& _for)
@@ -167,19 +185,19 @@ void DataFlowAnalyzer::operator()(ForLoop& _for)
167
185
clearValues (assignments.names ());
168
186
169
187
// break/continue are tricky for storage and thus we almost always clear here.
170
- clearStorageKnowledgeIfInvalidated (*_for.condition );
171
- clearStorageKnowledgeIfInvalidated (_for.post );
172
- clearStorageKnowledgeIfInvalidated (_for.body );
188
+ clearKnowledgeIfInvalidated (*_for.condition );
189
+ clearKnowledgeIfInvalidated (_for.post );
190
+ clearKnowledgeIfInvalidated (_for.body );
173
191
174
192
visit (*_for.condition );
175
193
(*this )(_for.body );
176
194
clearValues (assignmentsSinceCont.names ());
177
- clearStorageKnowledgeIfInvalidated (_for.body );
195
+ clearKnowledgeIfInvalidated (_for.body );
178
196
(*this )(_for.post );
179
197
clearValues (assignments.names ());
180
- clearStorageKnowledgeIfInvalidated (*_for.condition );
181
- clearStorageKnowledgeIfInvalidated (_for.post );
182
- clearStorageKnowledgeIfInvalidated (_for.body );
198
+ clearKnowledgeIfInvalidated (*_for.condition );
199
+ clearKnowledgeIfInvalidated (_for.post );
200
+ clearKnowledgeIfInvalidated (_for.body );
183
201
}
184
202
185
203
void DataFlowAnalyzer::operator ()(Block& _block)
@@ -219,6 +237,10 @@ void DataFlowAnalyzer::handleAssignment(set<YulString> const& _variables, Expres
219
237
m_storage.eraseKey (name);
220
238
// assignment to slot contents denoted by "name"
221
239
m_storage.eraseValue (name);
240
+ // assignment to slot denoted by "name"
241
+ m_memory.eraseKey (name);
242
+ // assignment to slot contents denoted by "name"
243
+ m_memory.eraseValue (name);
222
244
}
223
245
}
224
246
@@ -257,6 +279,10 @@ void DataFlowAnalyzer::clearValues(set<YulString> _variables)
257
279
m_storage.eraseKey (name);
258
280
// clear slot contents denoted by "name"
259
281
m_storage.eraseValue (name);
282
+ // assignment to slot denoted by "name"
283
+ m_memory.eraseKey (name);
284
+ // assignment to slot contents denoted by "name"
285
+ m_memory.eraseValue (name);
260
286
}
261
287
262
288
// Also clear variables that reference variables to be cleared.
@@ -271,29 +297,51 @@ void DataFlowAnalyzer::clearValues(set<YulString> _variables)
271
297
m_references.eraseKey (name);
272
298
}
273
299
274
- void DataFlowAnalyzer::clearStorageKnowledgeIfInvalidated (Block const & _block)
300
+ void DataFlowAnalyzer::clearKnowledgeIfInvalidated (Block const & _block)
275
301
{
276
- if (SideEffectsCollector (m_dialect, _block).invalidatesStorage ())
302
+ SideEffectsCollector sideEffects (m_dialect, _block);
303
+ if (sideEffects.invalidatesStorage ())
277
304
m_storage.clear ();
305
+ if (sideEffects.invalidatesMemory ())
306
+ m_memory.clear ();
278
307
}
279
308
280
- void DataFlowAnalyzer::clearStorageKnowledgeIfInvalidated (Expression const & _expr)
309
+ void DataFlowAnalyzer::clearKnowledgeIfInvalidated (Expression const & _expr)
281
310
{
282
- if (SideEffectsCollector (m_dialect, _expr).invalidatesStorage ())
311
+ SideEffectsCollector sideEffects (m_dialect, _expr);
312
+ if (sideEffects.invalidatesStorage ())
283
313
m_storage.clear ();
314
+ if (sideEffects.invalidatesMemory ())
315
+ m_memory.clear ();
316
+ }
317
+
318
+ void DataFlowAnalyzer::joinKnowledge (
319
+ InvertibleMap<YulString, YulString> const & _olderStorage,
320
+ InvertibleMap<YulString, YulString> const & _olderMemory
321
+ )
322
+ {
323
+ joinKnowledgeHelper (m_storage, _olderStorage);
324
+ joinKnowledgeHelper (m_memory, _olderMemory);
284
325
}
285
326
286
- void DataFlowAnalyzer::joinStorageKnowledge (InvertibleMap<YulString, YulString> const & _other)
327
+ void DataFlowAnalyzer::joinKnowledgeHelper (
328
+ InvertibleMap<YulString, YulString>& _this,
329
+ InvertibleMap<YulString, YulString> const & _older
330
+ )
287
331
{
332
+ // We clear if the key does not exist in the older map or if the value is different.
333
+ // This also works for memory because _older is an "older version"
334
+ // of m_memory and thus any overlapping write would have cleared the keys
335
+ // that are not known to be different inside m_memory already.
288
336
set<YulString> keysToErase;
289
- for (auto const & item: m_storage .values )
337
+ for (auto const & item: _this .values )
290
338
{
291
- auto it = _other .values .find (item.first );
292
- if (it == _other .values .end () || it->second != item.second )
339
+ auto it = _older .values .find (item.first );
340
+ if (it == _older .values .end () || it->second != item.second )
293
341
keysToErase.insert (item.first );
294
342
}
295
343
for (auto const & key: keysToErase)
296
- m_storage .eraseKey (key);
344
+ _this .eraseKey (key);
297
345
}
298
346
299
347
bool DataFlowAnalyzer::inScope (YulString _variableName) const
@@ -308,16 +356,22 @@ bool DataFlowAnalyzer::inScope(YulString _variableName) const
308
356
return false ;
309
357
}
310
358
311
- boost::optional<pair<YulString, YulString>> DataFlowAnalyzer::isSimpleSStore (
359
+ boost::optional<pair<YulString, YulString>> DataFlowAnalyzer::isSimpleStore (
360
+ dev::eth::Instruction _store,
312
361
ExpressionStatement const & _statement
313
362
) const
314
363
{
364
+ yulAssert (
365
+ _store == dev::eth::Instruction::MSTORE ||
366
+ _store == dev::eth::Instruction::SSTORE,
367
+ " "
368
+ );
315
369
if (_statement.expression .type () == typeid (FunctionCall))
316
370
{
317
371
FunctionCall const & funCall = boost::get<FunctionCall>(_statement.expression );
318
372
if (EVMDialect const * dialect = dynamic_cast <EVMDialect const *>(&m_dialect))
319
373
if (auto const * builtin = dialect->builtin (funCall.functionName .name ))
320
- if (builtin->instruction == dev::eth::Instruction::SSTORE )
374
+ if (builtin->instruction == _store )
321
375
if (
322
376
funCall.arguments .at (0 ).type () == typeid (Identifier) &&
323
377
funCall.arguments .at (1 ).type () == typeid (Identifier)
0 commit comments