Implement cxx20 polyfill and replace InvertibleMap entirely.

This commit is contained in:
Daniel Kirchner 2020-12-11 17:30:06 +01:00
parent 625d402dbb
commit 7fe03cbab0
7 changed files with 99 additions and 131 deletions

View File

@ -8,6 +8,7 @@ set(sources
CommonData.h CommonData.h
CommonIO.cpp CommonIO.cpp
CommonIO.h CommonIO.h
cxx20.h
Exceptions.cpp Exceptions.cpp
Exceptions.h Exceptions.h
ErrorCodes.h ErrorCodes.h
@ -15,7 +16,6 @@ set(sources
FunctionSelector.h FunctionSelector.h
IndentedWriter.cpp IndentedWriter.cpp
IndentedWriter.h IndentedWriter.h
InvertibleMap.h
IpfsHash.cpp IpfsHash.cpp
IpfsHash.h IpfsHash.h
JSON.cpp JSON.cpp

View File

@ -1,66 +0,0 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
#pragma once
#include <unordered_map>
/**
* Data structure that keeps track of values and keys of a mapping.
*/
template <class K, class V>
struct InvertibleMap
{
std::unordered_map<K, V> values;
void set(K _key, V _value)
{
values[_key] = _value;
}
std::optional<V> fetch(K _key)
{
auto it = values.find(_key);
if (it == values.end())
return std::nullopt;
else
return it->second;
}
void eraseKey(K _key)
{
values.erase(_key);
}
void eraseValue(V _value)
{
auto it = values.begin();
while (it != values.end())
{
if (it->second == _value)
it = values.erase(it);
else
++it;
}
}
void clear()
{
values.clear();
}
};

52
libsolutil/cxx20.h Normal file
View File

@ -0,0 +1,52 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
#include <map>
// Contains polyfills of STL functions and algorithms that will become available in C++20.
namespace solidity::cxx20
{
// Taken from https://en.cppreference.com/w/cpp/container/map/erase_if.
template< class Key, class T, class Compare, class Alloc, class Pred >
typename std::map<Key,T,Compare,Alloc>::size_type erase_if(std::map<Key,T,Compare,Alloc>& c, Pred pred)
{
auto old_size = c.size();
for (auto i = c.begin(), last = c.end(); i != last;)
if (pred(*i))
i = c.erase(i);
else
++i;
return old_size - c.size();
}
// Taken from https://en.cppreference.com/w/cpp/container/unordered_map/erase_if.
template<class Key, class T, class Hash, class KeyEqual, class Alloc, class Pred>
typename std::unordered_map<Key,T,Hash,KeyEqual,Alloc>::size_type
erase_if(std::unordered_map<Key,T,Hash,KeyEqual,Alloc>& c, Pred pred)
{
auto old_size = c.size();
for (auto i = c.begin(), last = c.end(); i != last;)
if (pred(*i))
i = c.erase(i);
else
++i;
return old_size - c.size();
}
}

View File

@ -23,8 +23,6 @@
#include <libyul/optimiser/ASTWalker.h> #include <libyul/optimiser/ASTWalker.h>
#include <libsolutil/InvertibleMap.h>
#include <map> #include <map>
#include <optional> #include <optional>
#include <set> #include <set>

View File

@ -29,6 +29,7 @@
#include <libyul/Exceptions.h> #include <libyul/Exceptions.h>
#include <libsolutil/CommonData.h> #include <libsolutil/CommonData.h>
#include <libsolutil/cxx20.h>
#include <boost/range/adaptor/reversed.hpp> #include <boost/range/adaptor/reversed.hpp>
#include <boost/range/algorithm_ext/erase.hpp> #include <boost/range/algorithm_ext/erase.hpp>
@ -62,27 +63,21 @@ void DataFlowAnalyzer::operator()(ExpressionStatement& _statement)
if (auto vars = isSimpleStore(StoreLoadLocation::Storage, _statement)) if (auto vars = isSimpleStore(StoreLoadLocation::Storage, _statement))
{ {
ASTModifier::operator()(_statement); ASTModifier::operator()(_statement);
auto it = m_storage.values.begin(); cxx20::erase_if(m_storage, [&](auto const& entry) {
while (it != m_storage.values.end()) return !(
if (!( m_knowledgeBase.knownToBeDifferent(vars->first, entry.first) ||
m_knowledgeBase.knownToBeDifferent(vars->first, it->first) || m_knowledgeBase.knownToBeEqual(vars->second, entry.second)
m_knowledgeBase.knownToBeEqual(vars->second, it->second) );
)) });
it = m_storage.values.erase(it); m_storage[vars->first] = vars->second;
else
++it;
m_storage.set(vars->first, vars->second);
} }
else if (auto vars = isSimpleStore(StoreLoadLocation::Memory, _statement)) else if (auto vars = isSimpleStore(StoreLoadLocation::Memory, _statement))
{ {
ASTModifier::operator()(_statement); ASTModifier::operator()(_statement);
auto it = m_memory.values.begin(); cxx20::erase_if(m_memory, [&](auto const& entry) {
while (it != m_memory.values.end()) return !m_knowledgeBase.knownToBeDifferentByAtLeast32(vars->first, entry.first);
if (!m_knowledgeBase.knownToBeDifferentByAtLeast32(vars->first, it->first)) });
it = m_memory.values.erase(it); m_memory[vars->first] = vars->second;
else
++it;
m_memory.set(vars->first, vars->second);
} }
else else
{ {
@ -121,8 +116,8 @@ void DataFlowAnalyzer::operator()(VariableDeclaration& _varDecl)
void DataFlowAnalyzer::operator()(If& _if) void DataFlowAnalyzer::operator()(If& _if)
{ {
clearKnowledgeIfInvalidated(*_if.condition); clearKnowledgeIfInvalidated(*_if.condition);
InvertibleMap<YulString, YulString> storage = m_storage; unordered_map<YulString, YulString> storage = m_storage;
InvertibleMap<YulString, YulString> memory = m_memory; unordered_map<YulString, YulString> memory = m_memory;
ASTModifier::operator()(_if); ASTModifier::operator()(_if);
@ -140,8 +135,8 @@ void DataFlowAnalyzer::operator()(Switch& _switch)
set<YulString> assignedVariables; set<YulString> assignedVariables;
for (auto& _case: _switch.cases) for (auto& _case: _switch.cases)
{ {
InvertibleMap<YulString, YulString> storage = m_storage; unordered_map<YulString, YulString> storage = m_storage;
InvertibleMap<YulString, YulString> memory = m_memory; unordered_map<YulString, YulString> memory = m_memory;
(*this)(_case.body); (*this)(_case.body);
joinKnowledge(storage, memory); joinKnowledge(storage, memory);
@ -164,8 +159,8 @@ void DataFlowAnalyzer::operator()(FunctionDefinition& _fun)
map<YulString, AssignedValue> value; map<YulString, AssignedValue> value;
size_t loopDepth{0}; size_t loopDepth{0};
unordered_map<YulString, set<YulString>> references; unordered_map<YulString, set<YulString>> references;
InvertibleMap<YulString, YulString> storage; unordered_map<YulString, YulString> storage;
InvertibleMap<YulString, YulString> memory; unordered_map<YulString, YulString> memory;
swap(m_value, value); swap(m_value, value);
swap(m_loopDepth, loopDepth); swap(m_loopDepth, loopDepth);
swap(m_references, references); swap(m_references, references);
@ -265,13 +260,13 @@ void DataFlowAnalyzer::handleAssignment(set<YulString> const& _variables, Expres
if (!_isDeclaration) if (!_isDeclaration)
{ {
// assignment to slot denoted by "name" // assignment to slot denoted by "name"
m_storage.eraseKey(name); m_storage.erase(name);
// assignment to slot contents denoted by "name" // assignment to slot contents denoted by "name"
m_storage.eraseValue(name); cxx20::erase_if(m_storage, [&](auto const& entry) { return entry.second == name; });
// assignment to slot denoted by "name" // assignment to slot denoted by "name"
m_memory.eraseKey(name); m_memory.erase(name);
// assignment to slot contents denoted by "name" // assignment to slot contents denoted by "name"
m_memory.eraseValue(name); cxx20::erase_if(m_memory, [&](auto const& entry) { return entry.second == name; });
} }
} }
@ -284,9 +279,9 @@ void DataFlowAnalyzer::handleAssignment(set<YulString> const& _variables, Expres
// On the other hand, if we knew the value in the slot // On the other hand, if we knew the value in the slot
// already, then the sload() / mload() would have been replaced by a variable anyway. // already, then the sload() / mload() would have been replaced by a variable anyway.
if (auto key = isSimpleLoad(StoreLoadLocation::Memory, *_value)) if (auto key = isSimpleLoad(StoreLoadLocation::Memory, *_value))
m_memory.set(*key, variable); m_memory[*key] = variable;
else if (auto key = isSimpleLoad(StoreLoadLocation::Storage, *_value)) else if (auto key = isSimpleLoad(StoreLoadLocation::Storage, *_value))
m_storage.set(*key, variable); m_storage[*key] = variable;
} }
} }
} }
@ -319,16 +314,11 @@ void DataFlowAnalyzer::clearValues(set<YulString> _variables)
// First clear storage knowledge, because we do not have to clear // First clear storage knowledge, because we do not have to clear
// storage knowledge of variables whose expression has changed, // storage knowledge of variables whose expression has changed,
// since the value is still unchanged. // since the value is still unchanged.
auto clear = [&](auto&& values) { auto eraseCondition = [&](auto const& entry) {
auto it = values.begin(); return _variables.count(entry.first) || _variables.count(entry.second);
while (it != values.end())
if (_variables.count(it->first) || _variables.count(it->second))
it = values.erase(it);
else
++it;
}; };
clear(m_storage.values); cxx20::erase_if(m_storage, eraseCondition);
clear(m_memory.values); cxx20::erase_if(m_memory, eraseCondition);
// Also clear variables that reference variables to be cleared. // Also clear variables that reference variables to be cleared.
for (auto const& name: _variables) for (auto const& name: _variables)
@ -338,10 +328,11 @@ void DataFlowAnalyzer::clearValues(set<YulString> _variables)
// Clear the value and update the reference relation. // Clear the value and update the reference relation.
for (auto const& name: _variables) for (auto const& name: _variables)
{
m_value.erase(name); m_value.erase(name);
for (auto const& name: _variables)
m_references.erase(name); m_references.erase(name);
} }
}
void DataFlowAnalyzer::assignValue(YulString _variable, Expression const* _value) void DataFlowAnalyzer::assignValue(YulString _variable, Expression const* _value)
{ {
@ -367,8 +358,8 @@ void DataFlowAnalyzer::clearKnowledgeIfInvalidated(Expression const& _expr)
} }
void DataFlowAnalyzer::joinKnowledge( void DataFlowAnalyzer::joinKnowledge(
InvertibleMap<YulString, YulString> const& _olderStorage, unordered_map<YulString, YulString> const& _olderStorage,
InvertibleMap<YulString, YulString> const& _olderMemory unordered_map<YulString, YulString> const& _olderMemory
) )
{ {
joinKnowledgeHelper(m_storage, _olderStorage); joinKnowledgeHelper(m_storage, _olderStorage);
@ -376,23 +367,18 @@ void DataFlowAnalyzer::joinKnowledge(
} }
void DataFlowAnalyzer::joinKnowledgeHelper( void DataFlowAnalyzer::joinKnowledgeHelper(
InvertibleMap<YulString, YulString>& _this, std::unordered_map<YulString, YulString>& _this,
InvertibleMap<YulString, YulString> const& _older std::unordered_map<YulString, YulString> const& _older
) )
{ {
// We clear if the key does not exist in the older map or if the value is different. // We clear if the key does not exist in the older map or if the value is different.
// This also works for memory because _older is an "older version" // This also works for memory because _older is an "older version"
// of m_memory and thus any overlapping write would have cleared the keys // of m_memory and thus any overlapping write would have cleared the keys
// that are not known to be different inside m_memory already. // that are not known to be different inside m_memory already.
auto it = _this.values.begin(); cxx20::erase_if(_this, [&](auto const& entry){
while (it != _this.values.end()) YulString const* value = valueOrNullptr(_older, entry.first);
{ return !value || *value != entry.second;
auto oldit = _older.values.find(it->first); });
if (oldit != _older.values.end() && it->second == oldit->second)
++it;
else
it = _this.values.erase(it);
}
} }
bool DataFlowAnalyzer::inScope(YulString _variableName) const bool DataFlowAnalyzer::inScope(YulString _variableName) const

View File

@ -29,8 +29,6 @@
#include <libyul/AST.h> // Needed for m_zero below. #include <libyul/AST.h> // Needed for m_zero below.
#include <libyul/SideEffects.h> #include <libyul/SideEffects.h>
#include <libsolutil/InvertibleMap.h>
#include <map> #include <map>
#include <set> #include <set>
@ -124,13 +122,13 @@ protected:
/// This only works if the current state is a direct successor of the older point, /// This only works if the current state is a direct successor of the older point,
/// i.e. `_otherStorage` and `_otherMemory` cannot have additional changes. /// i.e. `_otherStorage` and `_otherMemory` cannot have additional changes.
void joinKnowledge( void joinKnowledge(
InvertibleMap<YulString, YulString> const& _olderStorage, std::unordered_map<YulString, YulString> const& _olderStorage,
InvertibleMap<YulString, YulString> const& _olderMemory std::unordered_map<YulString, YulString> const& _olderMemory
); );
static void joinKnowledgeHelper( static void joinKnowledgeHelper(
InvertibleMap<YulString, YulString>& _thisData, std::unordered_map<YulString, YulString>& _thisData,
InvertibleMap<YulString, YulString> const& _olderData std::unordered_map<YulString, YulString> const& _olderData
); );
/// Returns true iff the variable is in scope. /// Returns true iff the variable is in scope.
@ -166,8 +164,8 @@ protected:
/// m_references[a].contains(b) <=> the current expression assigned to a references b /// m_references[a].contains(b) <=> the current expression assigned to a references b
std::unordered_map<YulString, std::set<YulString>> m_references; std::unordered_map<YulString, std::set<YulString>> m_references;
InvertibleMap<YulString, YulString> m_storage; std::unordered_map<YulString, YulString> m_storage;
InvertibleMap<YulString, YulString> m_memory; std::unordered_map<YulString, YulString> m_memory;
KnowledgeBase m_knowledgeBase; KnowledgeBase m_knowledgeBase;

View File

@ -67,10 +67,10 @@ void LoadResolver::tryResolve(
YulString key = std::get<Identifier>(_arguments.at(0)).name; YulString key = std::get<Identifier>(_arguments.at(0)).name;
if (_location == StoreLoadLocation::Storage) if (_location == StoreLoadLocation::Storage)
{ {
if (auto value = m_storage.fetch(key)) if (auto value = util::valueOrNullptr(m_storage, key))
_e = Identifier{locationOf(_e), *value}; _e = Identifier{locationOf(_e), *value};
} }
else if (m_optimizeMLoad && _location == StoreLoadLocation::Memory) else if (m_optimizeMLoad && _location == StoreLoadLocation::Memory)
if (auto value = m_memory.fetch(key)) if (auto value = util::valueOrNullptr(m_memory, key))
_e = Identifier{locationOf(_e), *value}; _e = Identifier{locationOf(_e), *value};
} }