Skip to content

Commit 721bb93

Browse files
authored
Merge pull request #15840 from ethereum/function_call_finder_on_handle
Yul FunctionCallFinder uses FunctionName instead of strings as search key
2 parents 3dc2cf8 + b07843d commit 721bb93

File tree

10 files changed

+201
-31
lines changed

10 files changed

+201
-31
lines changed

libyul/backends/evm/EVMObjectCompiler.cpp

+4-3
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,12 @@ void EVMObjectCompiler::run(Object const& _object, bool _optimize)
9292
);
9393
if (!stackErrors.empty())
9494
{
95-
yulAssert(_object.dialect());
95+
yulAssert(evmDialect->providesObjectAccess());
96+
auto const memoryGuardHandle = evmDialect->findBuiltin("memoryguard");
97+
yulAssert(memoryGuardHandle, "Compiling with object access, memoryguard should be available as builtin.");
9698
std::vector<FunctionCall const*> memoryGuardCalls = findFunctionCalls(
9799
_object.code()->root(),
98-
"memoryguard",
99-
*_object.dialect()
100+
*memoryGuardHandle
100101
);
101102
auto stackError = stackErrors.front();
102103
std::string msg = stackError.comment() ? *stackError.comment() : "";

libyul/optimiser/FullInliner.cpp

+7-4
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,13 @@ FullInliner::FullInliner(Block& _ast, NameDispenser& _dispenser, Dialect const&
8383
}
8484

8585
// Check for memory guard.
86-
std::vector<FunctionCall*> memoryGuardCalls = findFunctionCalls(_ast, "memoryguard", m_dialect);
87-
// We will perform less aggressive inlining, if no ``memoryguard`` call is found.
88-
if (!memoryGuardCalls.empty())
89-
m_hasMemoryGuard = true;
86+
if (auto const memoryGuard = m_dialect.findBuiltin("memoryguard"))
87+
{
88+
std::vector<FunctionCall*> memoryGuardCalls = findFunctionCalls(_ast, *memoryGuard);
89+
// We will perform less aggressive inlining, if no ``memoryguard`` call is found.
90+
if (!memoryGuardCalls.empty())
91+
m_hasMemoryGuard = true;
92+
}
9093
}
9194

9295
void FullInliner::run(Pass _pass)

libyul/optimiser/FunctionCallFinder.cpp

+10-12
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919

2020
#include <libyul/optimiser/ASTWalker.h>
2121
#include <libyul/AST.h>
22-
#include <libyul/Dialect.h>
2322
#include <libyul/Utilities.h>
2423

2524
using namespace solidity;
@@ -32,35 +31,34 @@ class MaybeConstFunctionCallFinder: Base
3231
{
3332
public:
3433
using MaybeConstBlock = std::conditional_t<std::is_const_v<ResultType>, Block const, Block>;
35-
static std::vector<ResultType*> run(MaybeConstBlock& _block, std::string_view const _functionName, Dialect const& _dialect)
34+
static std::vector<ResultType*> run(MaybeConstBlock& _block, FunctionHandle const& _functionHandle)
3635
{
37-
MaybeConstFunctionCallFinder functionCallFinder(_functionName, _dialect);
36+
MaybeConstFunctionCallFinder functionCallFinder(_functionHandle);
3837
functionCallFinder(_block);
3938
return functionCallFinder.m_calls;
4039
}
4140
private:
42-
explicit MaybeConstFunctionCallFinder(std::string_view const _functionName, Dialect const& _dialect):
43-
m_dialect(_dialect), m_functionName(_functionName), m_calls() {}
41+
explicit MaybeConstFunctionCallFinder(FunctionHandle const& _functionHandle):
42+
m_functionHandle(_functionHandle), m_calls() {}
4443

4544
using Base::operator();
4645
void operator()(ResultType& _functionCall) override
4746
{
4847
Base::operator()(_functionCall);
49-
if (resolveFunctionName(_functionCall.functionName, m_dialect) == m_functionName)
48+
if (functionNameToHandle(_functionCall.functionName) == m_functionHandle)
5049
m_calls.emplace_back(&_functionCall);
5150
}
52-
Dialect const& m_dialect;
53-
std::string_view m_functionName;
51+
FunctionHandle const& m_functionHandle;
5452
std::vector<ResultType*> m_calls;
5553
};
5654
}
5755

58-
std::vector<FunctionCall*> solidity::yul::findFunctionCalls(Block& _block, std::string_view const _functionName, Dialect const& _dialect)
56+
std::vector<FunctionCall*> yul::findFunctionCalls(Block& _block, FunctionHandle const& _functionHandle)
5957
{
60-
return MaybeConstFunctionCallFinder<ASTModifier, FunctionCall>::run(_block, _functionName, _dialect);
58+
return MaybeConstFunctionCallFinder<ASTModifier, FunctionCall>::run(_block, _functionHandle);
6159
}
6260

63-
std::vector<FunctionCall const*> solidity::yul::findFunctionCalls(Block const& _block, std::string_view const _functionName, Dialect const& _dialect)
61+
std::vector<FunctionCall const*> yul::findFunctionCalls(Block const& _block, FunctionHandle const& _functionHandle)
6462
{
65-
return MaybeConstFunctionCallFinder<ASTWalker, FunctionCall const>::run(_block, _functionName, _dialect);
63+
return MaybeConstFunctionCallFinder<ASTWalker, FunctionCall const>::run(_block, _functionHandle);
6664
}

libyul/optimiser/FunctionCallFinder.h

+3-6
Original file line numberDiff line numberDiff line change
@@ -20,28 +20,25 @@
2020

2121
#pragma once
2222

23-
#include <libyul/ASTForward.h>
23+
#include <libyul/AST.h>
2424

25-
#include <string_view>
2625
#include <vector>
2726

2827
namespace solidity::yul
2928
{
3029

31-
class Dialect;
32-
3330
/**
3431
* Finds all calls to a function of a given name using an ASTModifier.
3532
*
3633
* Prerequisite: Disambiguator
3734
*/
38-
std::vector<FunctionCall*> findFunctionCalls(Block& _block, std::string_view _functionName, Dialect const& _dialect);
35+
std::vector<FunctionCall*> findFunctionCalls(Block& _block, FunctionHandle const& _functionHandle);
3936

4037
/**
4138
* Finds all calls to a function of a given name using an ASTWalker.
4239
*
4340
* Prerequisite: Disambiguator
4441
*/
45-
std::vector<FunctionCall const*> findFunctionCalls(Block const& _block, std::string_view _functionName, Dialect const& _dialect);
42+
std::vector<FunctionCall const*> findFunctionCalls(Block const& _block, FunctionHandle const& _functionHandle);
4643

4744
}

libyul/optimiser/StackLimitEvader.cpp

+4-2
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,9 @@ void StackLimitEvader::run(
201201
"StackLimitEvader does not support EOF."
202202
);
203203

204-
std::vector<FunctionCall*> memoryGuardCalls = findFunctionCalls(_astRoot, "memoryguard", *evmDialect);
204+
auto const memoryGuardHandle = evmDialect->findBuiltin("memoryguard");
205+
yulAssert(memoryGuardHandle, "Compiling with object access, memoryguard should be available as builtin.");
206+
std::vector<FunctionCall*> const memoryGuardCalls = findFunctionCalls(_astRoot, *memoryGuardHandle);
205207
// Do not optimise, if no ``memoryguard`` call is found.
206208
if (memoryGuardCalls.empty())
207209
return;
@@ -233,7 +235,7 @@ void StackLimitEvader::run(
233235
StackToMemoryMover::run(_context, reservedMemory, memoryOffsetAllocator.slotAllocations, requiredSlots, _astRoot);
234236

235237
reservedMemory += 32 * requiredSlots;
236-
for (FunctionCall* memoryGuardCall: findFunctionCalls(_astRoot, "memoryguard", *evmDialect))
238+
for (FunctionCall* memoryGuardCall: findFunctionCalls(_astRoot, *memoryGuardHandle))
237239
{
238240
Literal* literal = std::get_if<Literal>(&memoryGuardCall->arguments.front());
239241
yulAssert(literal && literal->kind == LiteralKind::Number, "");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
--ir --debug-info none
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// SPDX-License-Identifier: GPL-3.0
2+
pragma solidity >=0.0;
3+
4+
contract C {
5+
function f() public pure {
6+
// NOTE: memoryguard is a builtin but only in pure Yul, not inline assembly.
7+
// NOTE: memoryguard is not a reserved identifier.
8+
// The expectation of this test is to not see the shadowed memoryguard within the generated Yul code but rather
9+
// a mangled version of it
10+
assembly { function memoryguard() {} }
11+
assembly { function f(memoryguard) {} }
12+
assembly { function f() -> memoryguard {} }
13+
assembly { let memoryguard }
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
IR:
2+
3+
/// @use-src 0:"memoryguard_shadowing_by_inline_assembly_identifiers/input.sol"
4+
object "C_10" {
5+
code {
6+
7+
mstore(64, memoryguard(128))
8+
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
9+
10+
constructor_C_10()
11+
12+
let _1 := allocate_unbounded()
13+
codecopy(_1, dataoffset("C_10_deployed"), datasize("C_10_deployed"))
14+
15+
return(_1, datasize("C_10_deployed"))
16+
17+
function allocate_unbounded() -> memPtr {
18+
memPtr := mload(64)
19+
}
20+
21+
function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() {
22+
revert(0, 0)
23+
}
24+
25+
function constructor_C_10() {
26+
27+
}
28+
29+
}
30+
/// @use-src 0:"memoryguard_shadowing_by_inline_assembly_identifiers/input.sol"
31+
object "C_10_deployed" {
32+
code {
33+
34+
mstore(64, memoryguard(128))
35+
36+
if iszero(lt(calldatasize(), 4))
37+
{
38+
let selector := shift_right_224_unsigned(calldataload(0))
39+
switch selector
40+
41+
case 0x26121ff0
42+
{
43+
// f()
44+
45+
external_fun_f_9()
46+
}
47+
48+
default {}
49+
}
50+
51+
revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74()
52+
53+
function shift_right_224_unsigned(value) -> newValue {
54+
newValue :=
55+
56+
shr(224, value)
57+
58+
}
59+
60+
function allocate_unbounded() -> memPtr {
61+
memPtr := mload(64)
62+
}
63+
64+
function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() {
65+
revert(0, 0)
66+
}
67+
68+
function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() {
69+
revert(0, 0)
70+
}
71+
72+
function abi_decode_tuple_(headStart, dataEnd) {
73+
if slt(sub(dataEnd, headStart), 0) { revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() }
74+
75+
}
76+
77+
function abi_encode_tuple__to__fromStack(headStart ) -> tail {
78+
tail := add(headStart, 0)
79+
80+
}
81+
82+
function external_fun_f_9() {
83+
84+
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
85+
abi_decode_tuple_(4, calldatasize())
86+
fun_f_9()
87+
let memPos := allocate_unbounded()
88+
let memEnd := abi_encode_tuple__to__fromStack(memPos )
89+
return(memPos, sub(memEnd, memPos))
90+
91+
}
92+
93+
function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() {
94+
revert(0, 0)
95+
}
96+
97+
function fun_f_9() {
98+
99+
{
100+
function usr$memoryguard()
101+
{ }
102+
}
103+
104+
{
105+
function usr$f(usr$memoryguard)
106+
{ }
107+
}
108+
109+
{
110+
function usr$f() -> usr$memoryguard
111+
{ }
112+
}
113+
114+
{ let usr$memoryguard }
115+
116+
}
117+
118+
}
119+
120+
data ".metadata" hex"<BYTECODE REMOVED>"
121+
}
122+
123+
}

test/libsolidity/MemoryGuardTest.cpp

+8-4
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,15 @@ TestCase::TestResult MemoryGuardTest::run(std::ostream& _stream, std::string con
7676
}
7777

7878
auto handleObject = [&](std::string const& _kind, Object const& _object) {
79-
m_obtainedResult += contractName + "(" + _kind + ") " + (findFunctionCalls(
79+
auto const memoryGuardHandle = yulStack.dialect().findBuiltin("memoryguard");
80+
m_obtainedResult += contractName + "(" + _kind + ") ";
81+
if (memoryGuardHandle && !findFunctionCalls(
8082
_object.code()->root(),
81-
"memoryguard",
82-
yulStack.dialect()
83-
).empty() ? "false" : "true") + "\n";
83+
*memoryGuardHandle
84+
).empty())
85+
m_obtainedResult += "true\n";
86+
else
87+
m_obtainedResult += "false\n";
8488
};
8589
handleObject("creation", *yulStack.parserResult());
8690
size_t deployedIndex = yulStack.parserResult()->subIndexByName.at(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// NOTE: memoryguard is a builtin but only in pure Yul, not inline assembly.
2+
// NOTE: memoryguard is not a reserved identifier.
3+
4+
contract C {
5+
constructor() {
6+
// The expectation of this test is to see a 'true' outcome, indicating the memoryguard builtin being used
7+
// due to explicitly marking the inline assembly as memory-safe
8+
9+
assembly ("memory-safe") { mstore(42, 42) function memoryguard() {} }
10+
assembly ("memory-safe") { mstore(42, 42) function f(memoryguard) {} }
11+
assembly ("memory-safe") { mstore(42, 42) function f() -> memoryguard {} }
12+
assembly ("memory-safe") { mstore(42, 42) let memoryguard }
13+
}
14+
15+
function f() public pure {
16+
// The expectation of this test is to see a 'false' outcome, indicating the memoryguard builtin not being used
17+
18+
assembly { mstore(42, 42) function memoryguard() {} }
19+
assembly { mstore(42, 42) function f(memoryguard) {} }
20+
assembly { mstore(42, 42) function f() -> memoryguard {} }
21+
assembly { mstore(42, 42) let memoryguard }
22+
}
23+
}
24+
// ----
25+
// :C(creation) true
26+
// :C(runtime) false

0 commit comments

Comments
 (0)