Skip to content

Commit afe887a

Browse files
committed
Split MovableChecker and introduce SideEffectsUpToMSize.
1 parent b95eebe commit afe887a

File tree

7 files changed

+103
-39
lines changed

7 files changed

+103
-39
lines changed

libevmasm/SemanticInformation.cpp

+8-13
Original file line numberDiff line numberDiff line change
@@ -213,20 +213,15 @@ bool SemanticInformation::sideEffectFree(Instruction _instruction)
213213
// These are not really functional.
214214
assertThrow(!isDupInstruction(_instruction) && !isSwapInstruction(_instruction), AssemblyException, "");
215215

216-
InstructionInfo info = instructionInfo(_instruction);
217-
switch (_instruction)
218-
{
219-
// All the instructions that merely read memory are fine
220-
// even though they are marked "sideEffects" in Instructions.cpp
221-
case Instruction::KECCAK256:
222-
case Instruction::MLOAD:
216+
return !instructionInfo(_instruction).sideEffects;
217+
}
218+
219+
bool SemanticInformation::sideEffectFreeIfNoMSize(Instruction _instruction)
220+
{
221+
if (_instruction == Instruction::KECCAK256 || _instruction == Instruction::MLOAD)
223222
return true;
224-
default:
225-
break;
226-
}
227-
if (info.sideEffects)
228-
return false;
229-
return true;
223+
else
224+
return sideEffectFree(_instruction);
230225
}
231226

232227
bool SemanticInformation::invalidatesMemory(Instruction _instruction)

libevmasm/SemanticInformation.h

+6
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ struct SemanticInformation
6060
/// This does not mean that it has to be deterministic or retrieve information from
6161
/// somewhere else than purely the values of its arguments.
6262
static bool sideEffectFree(Instruction _instruction);
63+
/// @returns true if the instruction can be removed without changing the semantics.
64+
/// This does not mean that it has to be deterministic or retrieve information from
65+
/// somewhere else than purely the values of its arguments.
66+
/// If true, the instruction is still allowed to influence the value returned by the
67+
/// msize instruction.
68+
static bool sideEffectFreeIfNoMSize(Instruction _instruction);
6369
/// @returns true if the given instruction modifies memory.
6470
static bool invalidatesMemory(Instruction _instruction);
6571
/// @returns true if the given instruction modifies storage (even indirectly).

libyul/Dialect.h

+5
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ struct BuiltinFunction
5151
bool movable = false;
5252
/// If true, a call to this function can be omitted without changing semantics.
5353
bool sideEffectFree = false;
54+
/// If true, a call to this function can be omitted without changing semantics if the
55+
/// program does not contain the msize instruction.
56+
bool sideEffectFreeIfNoMSize = false;
57+
/// If true, this is the msize instruction.
58+
bool isMSize = false;
5459
/// If true, can only accept literals as arguments and they cannot be moved to variables.
5560
bool literalArguments = false;
5661
};

libyul/backends/evm/EVMDialect.cpp

+8-3
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ pair<YulString, BuiltinFunctionForEVM> createEVMFunction(
5252
f.returns.resize(info.ret);
5353
f.movable = eth::SemanticInformation::movable(_instruction);
5454
f.sideEffectFree = eth::SemanticInformation::sideEffectFree(_instruction);
55+
f.sideEffectFreeIfNoMSize = eth::SemanticInformation::sideEffectFreeIfNoMSize(_instruction);
56+
f.isMSize = _instruction == dev::eth::Instruction::MSIZE;
5557
f.literalArguments = false;
5658
f.instruction = _instruction;
5759
f.generateCode = [_instruction](
@@ -73,6 +75,7 @@ pair<YulString, BuiltinFunctionForEVM> createFunction(
7375
size_t _returns,
7476
bool _movable,
7577
bool _sideEffectFree,
78+
bool _sideEffectFreeIfNoMSize,
7679
bool _literalArguments,
7780
std::function<void(FunctionCall const&, AbstractAssembly&, BuiltinContext&, std::function<void()>)> _generateCode
7881
)
@@ -85,6 +88,8 @@ pair<YulString, BuiltinFunctionForEVM> createFunction(
8588
f.movable = _movable;
8689
f.literalArguments = _literalArguments;
8790
f.sideEffectFree = _sideEffectFree;
91+
f.sideEffectFreeIfNoMSize = _sideEffectFreeIfNoMSize;
92+
f.isMSize = false;
8893
f.instruction = {};
8994
f.generateCode = std::move(_generateCode);
9095
return {name, f};
@@ -105,7 +110,7 @@ map<YulString, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion _evmVe
105110

106111
if (_objectAccess)
107112
{
108-
builtins.emplace(createFunction("datasize", 1, 1, true, true, true, [](
113+
builtins.emplace(createFunction("datasize", 1, 1, true, true, true, true, [](
109114
FunctionCall const& _call,
110115
AbstractAssembly& _assembly,
111116
BuiltinContext& _context,
@@ -126,7 +131,7 @@ map<YulString, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion _evmVe
126131
_assembly.appendDataSize(_context.subIDs.at(dataName));
127132
}
128133
}));
129-
builtins.emplace(createFunction("dataoffset", 1, 1, true, true, true, [](
134+
builtins.emplace(createFunction("dataoffset", 1, 1, true, true, true, true, [](
130135
FunctionCall const& _call,
131136
AbstractAssembly& _assembly,
132137
BuiltinContext& _context,
@@ -147,7 +152,7 @@ map<YulString, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion _evmVe
147152
_assembly.appendDataOffset(_context.subIDs.at(dataName));
148153
}
149154
}));
150-
builtins.emplace(createFunction("datacopy", 3, 0, false, false, false, [](
155+
builtins.emplace(createFunction("datacopy", 3, 0, false, false, false, false, [](
151156
FunctionCall const&,
152157
AbstractAssembly& _assembly,
153158
BuiltinContext&,

libyul/backends/wasm/WasmDialect.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -72,5 +72,7 @@ void WasmDialect::addFunction(string _name, size_t _params, size_t _returns)
7272
f.returns.resize(_returns);
7373
f.movable = false;
7474
f.sideEffectFree = false;
75+
f.sideEffectFreeIfNoMSize = false;
76+
f.isMSize = false;
7577
f.literalArguments = false;
7678
}

libyul/optimiser/Semantics.cpp

+32-10
Original file line numberDiff line numberDiff line change
@@ -33,34 +33,39 @@ using namespace std;
3333
using namespace dev;
3434
using namespace yul;
3535

36-
MovableChecker::MovableChecker(Dialect const& _dialect):
37-
m_dialect(_dialect)
36+
SideEffectsCollector::SideEffectsCollector(Dialect const& _dialect, Expression const& _expression):
37+
SideEffectsCollector(_dialect)
3838
{
39+
visit(_expression);
3940
}
4041

41-
MovableChecker::MovableChecker(Dialect const& _dialect, Expression const& _expression):
42-
MovableChecker(_dialect)
42+
SideEffectsCollector::SideEffectsCollector(Dialect const& _dialect, Statement const& _statement):
43+
SideEffectsCollector(_dialect)
4344
{
44-
visit(_expression);
45+
visit(_statement);
4546
}
4647

47-
void MovableChecker::operator()(Identifier const& _identifier)
48+
SideEffectsCollector::SideEffectsCollector(Dialect const& _dialect, Block const& _ast):
49+
SideEffectsCollector(_dialect)
4850
{
49-
ASTWalker::operator()(_identifier);
50-
m_variableReferences.emplace(_identifier.name);
51+
operator()(_ast);
5152
}
5253

53-
void MovableChecker::operator()(FunctionalInstruction const& _instr)
54+
void SideEffectsCollector::operator()(FunctionalInstruction const& _instr)
5455
{
5556
ASTWalker::operator()(_instr);
5657

5758
if (!eth::SemanticInformation::movable(_instr.instruction))
5859
m_movable = false;
5960
if (!eth::SemanticInformation::sideEffectFree(_instr.instruction))
6061
m_sideEffectFree = false;
62+
if (!eth::SemanticInformation::sideEffectFreeIfNoMSize(_instr.instruction))
63+
m_sideEffectFreeIfNoMSize = false;
64+
if (_instr.instruction == eth::Instruction::MSIZE)
65+
m_containsMSize = true;
6166
}
6267

63-
void MovableChecker::operator()(FunctionCall const& _functionCall)
68+
void SideEffectsCollector::operator()(FunctionCall const& _functionCall)
6469
{
6570
ASTWalker::operator()(_functionCall);
6671

@@ -70,14 +75,31 @@ void MovableChecker::operator()(FunctionCall const& _functionCall)
7075
m_movable = false;
7176
if (!f->sideEffectFree)
7277
m_sideEffectFree = false;
78+
if (!f->sideEffectFreeIfNoMSize)
79+
m_sideEffectFreeIfNoMSize = false;
80+
if (f->isMSize)
81+
m_containsMSize = true;
7382
}
7483
else
7584
{
7685
m_movable = false;
7786
m_sideEffectFree = false;
87+
m_sideEffectFreeIfNoMSize = false;
7888
}
7989
}
8090

91+
MovableChecker::MovableChecker(Dialect const& _dialect, Expression const& _expression):
92+
MovableChecker(_dialect)
93+
{
94+
visit(_expression);
95+
}
96+
97+
void MovableChecker::operator()(Identifier const& _identifier)
98+
{
99+
SideEffectsCollector::operator()(_identifier);
100+
m_variableReferences.emplace(_identifier.name);
101+
}
102+
81103
void MovableChecker::visit(Statement const&)
82104
{
83105
assertThrow(false, OptimizerException, "Movability for statement requested.");

libyul/optimiser/Semantics.h

+42-13
Original file line numberDiff line numberDiff line change
@@ -29,38 +29,67 @@ namespace yul
2929
struct Dialect;
3030

3131
/**
32-
* Specific AST walker that determines whether an expression is movable.
32+
* Specific AST walker that determines side-effect free-ness and movability of code.
33+
* Enters into function definitions.
3334
*/
34-
class MovableChecker: public ASTWalker
35+
class SideEffectsCollector: public ASTWalker
3536
{
3637
public:
37-
explicit MovableChecker(Dialect const& _dialect);
38-
MovableChecker(Dialect const& _dialect, Expression const& _expression);
38+
explicit SideEffectsCollector(Dialect const& _dialect): m_dialect(_dialect) {}
39+
SideEffectsCollector(Dialect const& _dialect, Expression const& _expression);
40+
SideEffectsCollector(Dialect const& _dialect, Statement const& _statement);
41+
SideEffectsCollector(Dialect const& _dialect, Block const& _ast);
3942

40-
void operator()(Identifier const& _identifier) override;
43+
using ASTWalker::operator();
4144
void operator()(FunctionalInstruction const& _functionalInstruction) override;
4245
void operator()(FunctionCall const& _functionCall) override;
4346

44-
/// Disallow visiting anything apart from Expressions (this throws).
45-
void visit(Statement const&) override;
46-
using ASTWalker::visit;
47-
4847
bool movable() const { return m_movable; }
4948
bool sideEffectFree() const { return m_sideEffectFree; }
50-
51-
std::set<YulString> const& referencedVariables() const { return m_variableReferences; }
49+
bool sideEffectFreeIfNoMSize() const { return m_sideEffectFreeIfNoMSize; }
50+
bool containsMSize() const { return m_containsMSize; }
5251

5352
private:
5453
Dialect const& m_dialect;
55-
/// Which variables the current expression references.
56-
std::set<YulString> m_variableReferences;
5754
/// Is the current expression movable or not.
5855
bool m_movable = true;
5956
/// Is the current expression side-effect free, i.e. can be removed
6057
/// without changing the semantics.
6158
bool m_sideEffectFree = true;
59+
/// Is the current expression side-effect free up to msize, i.e. can be removed
60+
/// without changing the semantics except for the value returned by the msize instruction.
61+
bool m_sideEffectFreeIfNoMSize = true;
62+
/// Does the current code contain the MSize operation?
63+
/// Note that this is a purely syntactic property meaning that even if this is false,
64+
/// the code can still contain calls to functions that contain the msize instruction.
65+
bool m_containsMSize = false;
66+
};
67+
68+
/**
69+
* Specific AST walker that determines whether an expression is movable
70+
* and collects the referenced variables.
71+
* Can only be used on expressions.
72+
*/
73+
class MovableChecker: public SideEffectsCollector
74+
{
75+
public:
76+
explicit MovableChecker(Dialect const& _dialect): SideEffectsCollector(_dialect) {}
77+
MovableChecker(Dialect const& _dialect, Expression const& _expression);
78+
79+
void operator()(Identifier const& _identifier) override;
80+
81+
/// Disallow visiting anything apart from Expressions (this throws).
82+
void visit(Statement const&) override;
83+
using ASTWalker::visit;
84+
85+
std::set<YulString> const& referencedVariables() const { return m_variableReferences; }
86+
87+
private:
88+
/// Which variables the current expression references.
89+
std::set<YulString> m_variableReferences;
6290
};
6391

92+
6493
/**
6594
* Helper class to find "irregular" control flow.
6695
* This includes termination, break and continue.

0 commit comments

Comments
 (0)