diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt
index 3c980fbb8..039e24ea9 100644
--- a/libyul/CMakeLists.txt
+++ b/libyul/CMakeLists.txt
@@ -40,6 +40,8 @@ add_library(yul
Dialect.cpp
Dialect.h
Exceptions.h
+ FunctionReferenceResolver.cpp
+ FunctionReferenceResolver.h
Object.cpp
Object.h
ObjectParser.cpp
diff --git a/libyul/FunctionReferenceResolver.cpp b/libyul/FunctionReferenceResolver.cpp
new file mode 100644
index 000000000..5df94237a
--- /dev/null
+++ b/libyul/FunctionReferenceResolver.cpp
@@ -0,0 +1,60 @@
+/*
+ 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 .
+*/
+// SPDX-License-Identifier: GPL-3.0
+
+#include
+
+#include
+#include
+
+#include
+
+using namespace std;
+using namespace solidity::yul;
+using namespace solidity::util;
+
+FunctionReferenceResolver::FunctionReferenceResolver(Block const& _ast)
+{
+ (*this)(_ast);
+ yulAssert(m_scopes.empty());
+}
+
+void FunctionReferenceResolver::operator()(FunctionCall const& _functionCall)
+{
+ for (auto&& scope: m_scopes | ranges::views::reverse)
+ if (FunctionDefinition const** function = util::valueOrNullptr(scope, _functionCall.functionName.name))
+ {
+ m_functionReferences[&_functionCall] = *function;
+ break;
+ }
+
+ // If we did not find anything, it was a builtin call.
+
+ ASTWalker::operator()(_functionCall);
+}
+
+void FunctionReferenceResolver::operator()(Block const& _block)
+{
+ m_scopes.emplace_back();
+ for (auto const& statement: _block.statements)
+ if (auto const* function = get_if(&statement))
+ m_scopes.back()[function->name] = function;
+
+ ASTWalker::operator()(_block);
+
+ m_scopes.pop_back();
+}
diff --git a/libyul/FunctionReferenceResolver.h b/libyul/FunctionReferenceResolver.h
new file mode 100644
index 000000000..8c1385adb
--- /dev/null
+++ b/libyul/FunctionReferenceResolver.h
@@ -0,0 +1,48 @@
+/*
+ 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 .
+*/
+// SPDX-License-Identifier: GPL-3.0
+
+#pragma once
+
+#include
+
+namespace solidity::yul
+{
+
+/**
+ * Resolves references to user-defined functions in function calls.
+ * Assumes the code is correct, i.e. does not check for references to be valid or unique.
+ *
+ * Be careful not to iterate over the result - it is not deterministic.
+ */
+class FunctionReferenceResolver: private ASTWalker
+{
+public:
+ explicit FunctionReferenceResolver(Block const& _ast);
+ std::map const& references() const { return m_functionReferences; }
+
+private:
+ using ASTWalker::operator();
+ void operator()(FunctionCall const& _functionCall) override;
+ void operator()(Block const& _block) override;
+
+ std::map m_functionReferences;
+ std::vector> m_scopes;
+};
+
+
+}