diff --git a/libsolutil/CMakeLists.txt b/libsolutil/CMakeLists.txt
index 7c10cf8b5..535b2b90f 100644
--- a/libsolutil/CMakeLists.txt
+++ b/libsolutil/CMakeLists.txt
@@ -8,6 +8,7 @@ set(sources
 	CommonData.h
 	CommonIO.cpp
 	CommonIO.h
+	cxx20.h
 	Exceptions.cpp
 	Exceptions.h
 	ErrorCodes.h
@@ -15,7 +16,6 @@ set(sources
 	FunctionSelector.h
 	IndentedWriter.cpp
 	IndentedWriter.h
-	InvertibleMap.h
 	IpfsHash.cpp
 	IpfsHash.h
 	JSON.cpp
diff --git a/libsolutil/InvertibleMap.h b/libsolutil/InvertibleMap.h
deleted file mode 100644
index 882890df5..000000000
--- a/libsolutil/InvertibleMap.h
+++ /dev/null
@@ -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();
-	}
-};
diff --git a/libsolutil/cxx20.h b/libsolutil/cxx20.h
new file mode 100644
index 000000000..69e09a005
--- /dev/null
+++ b/libsolutil/cxx20.h
@@ -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();
+}
+
+}
diff --git a/libyul/optimiser/CallGraphGenerator.h b/libyul/optimiser/CallGraphGenerator.h
index 11224eeaa..a581df018 100644
--- a/libyul/optimiser/CallGraphGenerator.h
+++ b/libyul/optimiser/CallGraphGenerator.h
@@ -23,8 +23,6 @@
 
 #include <libyul/optimiser/ASTWalker.h>
 
-#include <libsolutil/InvertibleMap.h>
-
 #include <map>
 #include <optional>
 #include <set>
diff --git a/libyul/optimiser/DataFlowAnalyzer.cpp b/libyul/optimiser/DataFlowAnalyzer.cpp
index 220a9d673..aee1e5e37 100644
--- a/libyul/optimiser/DataFlowAnalyzer.cpp
+++ b/libyul/optimiser/DataFlowAnalyzer.cpp
@@ -29,6 +29,7 @@
 #include <libyul/Exceptions.h>
 
 #include <libsolutil/CommonData.h>
+#include <libsolutil/cxx20.h>
 
 #include <boost/range/adaptor/reversed.hpp>
 #include <boost/range/algorithm_ext/erase.hpp>
@@ -62,27 +63,21 @@ void DataFlowAnalyzer::operator()(ExpressionStatement& _statement)
 	if (auto vars = isSimpleStore(StoreLoadLocation::Storage, _statement))
 	{
 		ASTModifier::operator()(_statement);
-		auto it = m_storage.values.begin();
-		while (it != m_storage.values.end())
-			if (!(
-				m_knowledgeBase.knownToBeDifferent(vars->first, it->first) ||
-				m_knowledgeBase.knownToBeEqual(vars->second, it->second)
-			))
-				it = m_storage.values.erase(it);
-			else
-				++it;
-		m_storage.set(vars->first, vars->second);
+		cxx20::erase_if(m_storage, [&](auto const& entry) {
+			return !(
+				m_knowledgeBase.knownToBeDifferent(vars->first, entry.first) ||
+				m_knowledgeBase.knownToBeEqual(vars->second, entry.second)
+			);
+		});
+		m_storage[vars->first] = vars->second;
 	}
 	else if (auto vars = isSimpleStore(StoreLoadLocation::Memory, _statement))
 	{
 		ASTModifier::operator()(_statement);
-		auto it = m_memory.values.begin();
-		while (it != m_memory.values.end())
-			if (!m_knowledgeBase.knownToBeDifferentByAtLeast32(vars->first, it->first))
-				it = m_memory.values.erase(it);
-			else
-				++it;
-		m_memory.set(vars->first, vars->second);
+		cxx20::erase_if(m_memory, [&](auto const& entry) {
+			return !m_knowledgeBase.knownToBeDifferentByAtLeast32(vars->first, entry.first);
+		});
+		m_memory[vars->first] = vars->second;
 	}
 	else
 	{
@@ -121,8 +116,8 @@ void DataFlowAnalyzer::operator()(VariableDeclaration& _varDecl)
 void DataFlowAnalyzer::operator()(If& _if)
 {
 	clearKnowledgeIfInvalidated(*_if.condition);
-	InvertibleMap<YulString, YulString> storage = m_storage;
-	InvertibleMap<YulString, YulString> memory = m_memory;
+	unordered_map<YulString, YulString> storage = m_storage;
+	unordered_map<YulString, YulString> memory = m_memory;
 
 	ASTModifier::operator()(_if);
 
@@ -140,8 +135,8 @@ void DataFlowAnalyzer::operator()(Switch& _switch)
 	set<YulString> assignedVariables;
 	for (auto& _case: _switch.cases)
 	{
-		InvertibleMap<YulString, YulString> storage = m_storage;
-		InvertibleMap<YulString, YulString> memory = m_memory;
+		unordered_map<YulString, YulString> storage = m_storage;
+		unordered_map<YulString, YulString> memory = m_memory;
 		(*this)(_case.body);
 		joinKnowledge(storage, memory);
 
@@ -164,8 +159,8 @@ void DataFlowAnalyzer::operator()(FunctionDefinition& _fun)
 	map<YulString, AssignedValue> value;
 	size_t loopDepth{0};
 	unordered_map<YulString, set<YulString>> references;
-	InvertibleMap<YulString, YulString> storage;
-	InvertibleMap<YulString, YulString> memory;
+	unordered_map<YulString, YulString> storage;
+	unordered_map<YulString, YulString> memory;
 	swap(m_value, value);
 	swap(m_loopDepth, loopDepth);
 	swap(m_references, references);
@@ -265,13 +260,13 @@ void DataFlowAnalyzer::handleAssignment(set<YulString> const& _variables, Expres
 		if (!_isDeclaration)
 		{
 			// assignment to slot denoted by "name"
-			m_storage.eraseKey(name);
+			m_storage.erase(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"
-			m_memory.eraseKey(name);
+			m_memory.erase(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
 			// already, then the sload() / mload() would have been replaced by a variable anyway.
 			if (auto key = isSimpleLoad(StoreLoadLocation::Memory, *_value))
-				m_memory.set(*key, variable);
+				m_memory[*key] = variable;
 			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
 	// storage knowledge of variables whose expression has changed,
 	// since the value is still unchanged.
-	auto clear = [&](auto&& values) {
-		auto it = values.begin();
-		while (it != values.end())
-			if (_variables.count(it->first) || _variables.count(it->second))
-				it = values.erase(it);
-			else
-				++it;
+	auto eraseCondition = [&](auto const& entry) {
+		return _variables.count(entry.first) || _variables.count(entry.second);
 	};
-	clear(m_storage.values);
-	clear(m_memory.values);
+	cxx20::erase_if(m_storage, eraseCondition);
+	cxx20::erase_if(m_memory, eraseCondition);
 
 	// Also clear variables that reference variables to be cleared.
 	for (auto const& name: _variables)
@@ -338,9 +328,10 @@ void DataFlowAnalyzer::clearValues(set<YulString> _variables)
 
 	// Clear the value and update the reference relation.
 	for (auto const& name: _variables)
+	{
 		m_value.erase(name);
-	for (auto const& name: _variables)
 		m_references.erase(name);
+	}
 }
 
 void DataFlowAnalyzer::assignValue(YulString _variable, Expression const* _value)
@@ -367,8 +358,8 @@ void DataFlowAnalyzer::clearKnowledgeIfInvalidated(Expression const& _expr)
 }
 
 void DataFlowAnalyzer::joinKnowledge(
-	InvertibleMap<YulString, YulString> const& _olderStorage,
-	InvertibleMap<YulString, YulString> const& _olderMemory
+	unordered_map<YulString, YulString> const& _olderStorage,
+	unordered_map<YulString, YulString> const& _olderMemory
 )
 {
 	joinKnowledgeHelper(m_storage, _olderStorage);
@@ -376,23 +367,18 @@ void DataFlowAnalyzer::joinKnowledge(
 }
 
 void DataFlowAnalyzer::joinKnowledgeHelper(
-	InvertibleMap<YulString, YulString>& _this,
-	InvertibleMap<YulString, YulString> const& _older
+	std::unordered_map<YulString, YulString>& _this,
+	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.
 	// This also works for memory because _older is an "older version"
 	// of m_memory and thus any overlapping write would have cleared the keys
 	// that are not known to be different inside m_memory already.
-	auto it = _this.values.begin();
-	while (it != _this.values.end())
-	{
-		auto oldit = _older.values.find(it->first);
-		if (oldit != _older.values.end() && it->second == oldit->second)
-			++it;
-		else
-			it = _this.values.erase(it);
-	}
+	cxx20::erase_if(_this, [&](auto const& entry){
+		YulString const* value = valueOrNullptr(_older, entry.first);
+		return !value || *value != entry.second;
+	});
 }
 
 bool DataFlowAnalyzer::inScope(YulString _variableName) const
diff --git a/libyul/optimiser/DataFlowAnalyzer.h b/libyul/optimiser/DataFlowAnalyzer.h
index 02b2cb261..7cbbe63b7 100644
--- a/libyul/optimiser/DataFlowAnalyzer.h
+++ b/libyul/optimiser/DataFlowAnalyzer.h
@@ -29,8 +29,6 @@
 #include <libyul/AST.h> // Needed for m_zero below.
 #include <libyul/SideEffects.h>
 
-#include <libsolutil/InvertibleMap.h>
-
 #include <map>
 #include <set>
 
@@ -124,13 +122,13 @@ protected:
 	/// This only works if the current state is a direct successor of the older point,
 	/// i.e. `_otherStorage` and `_otherMemory` cannot have additional changes.
 	void joinKnowledge(
-		InvertibleMap<YulString, YulString> const& _olderStorage,
-		InvertibleMap<YulString, YulString> const& _olderMemory
+		std::unordered_map<YulString, YulString> const& _olderStorage,
+		std::unordered_map<YulString, YulString> const& _olderMemory
 	);
 
 	static void joinKnowledgeHelper(
-		InvertibleMap<YulString, YulString>& _thisData,
-		InvertibleMap<YulString, YulString> const& _olderData
+		std::unordered_map<YulString, YulString>& _thisData,
+		std::unordered_map<YulString, YulString> const& _olderData
 	);
 
 	/// 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
 	std::unordered_map<YulString, std::set<YulString>> m_references;
 
-	InvertibleMap<YulString, YulString> m_storage;
-	InvertibleMap<YulString, YulString> m_memory;
+	std::unordered_map<YulString, YulString> m_storage;
+	std::unordered_map<YulString, YulString> m_memory;
 
 	KnowledgeBase m_knowledgeBase;
 
diff --git a/libyul/optimiser/LoadResolver.cpp b/libyul/optimiser/LoadResolver.cpp
index d5e542216..86e406115 100644
--- a/libyul/optimiser/LoadResolver.cpp
+++ b/libyul/optimiser/LoadResolver.cpp
@@ -67,10 +67,10 @@ void LoadResolver::tryResolve(
 	YulString key = std::get<Identifier>(_arguments.at(0)).name;
 	if (_location == StoreLoadLocation::Storage)
 	{
-		if (auto value = m_storage.fetch(key))
+		if (auto value = util::valueOrNullptr(m_storage, key))
 			_e = Identifier{locationOf(_e), *value};
 	}
 	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};
 }