Skip to content

Commit d0d4bca

Browse files
committed
Extend side effects to operations.
1 parent 5369bdc commit d0d4bca

File tree

2 files changed

+151
-6
lines changed

2 files changed

+151
-6
lines changed

libevmasm/SemanticInformation.cpp

Lines changed: 125 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,125 @@ using namespace std;
2929
using namespace solidity;
3030
using namespace solidity::evmasm;
3131

32+
vector<SemanticInformation::Operation> SemanticInformation::readWriteOperations(Instruction _instruction)
33+
{
34+
switch (_instruction)
35+
{
36+
case Instruction::SSTORE:
37+
case Instruction::SLOAD:
38+
{
39+
assertThrow(memory(_instruction) == Effect::None, OptimizerException, "");
40+
assertThrow(storage(_instruction) != Effect::None, OptimizerException, "");
41+
Operation op;
42+
op.effect = storage(_instruction);
43+
op.location = Location::Storage;
44+
op.startParameter = 0;
45+
// We know that exactly one slot is affected.
46+
op.lengthConstant = 1;
47+
return {op};
48+
}
49+
case Instruction::MSTORE:
50+
case Instruction::MSTORE8:
51+
case Instruction::MLOAD:
52+
{
53+
assertThrow(memory(_instruction) != Effect::None, OptimizerException, "");
54+
assertThrow(storage(_instruction) == Effect::None, OptimizerException, "");
55+
Operation op;
56+
op.effect = memory(_instruction);
57+
op.location = Location::Memory;
58+
op.startParameter = 0;
59+
if (_instruction == Instruction::MSTORE || _instruction == Instruction::MLOAD)
60+
op.lengthConstant = 32;
61+
else if (_instruction == Instruction::MSTORE8)
62+
op.lengthConstant = 1;
63+
64+
return {op};
65+
}
66+
case Instruction::REVERT:
67+
case Instruction::RETURN:
68+
case Instruction::KECCAK256:
69+
case Instruction::LOG0:
70+
case Instruction::LOG1:
71+
case Instruction::LOG2:
72+
case Instruction::LOG3:
73+
case Instruction::LOG4:
74+
{
75+
assertThrow(storage(_instruction) == Effect::None, OptimizerException, "");
76+
assertThrow(memory(_instruction) == Effect::Read, OptimizerException, "");
77+
Operation op;
78+
op.effect = memory(_instruction);
79+
op.location = Location::Memory;
80+
op.startParameter = 0;
81+
op.lengthParameter = 1;
82+
return {op};
83+
}
84+
case Instruction::EXTCODECOPY:
85+
{
86+
assertThrow(memory(_instruction) == Effect::Write, OptimizerException, "");
87+
assertThrow(storage(_instruction) == Effect::None, OptimizerException, "");
88+
Operation op;
89+
op.effect = memory(_instruction);
90+
op.location = Location::Memory;
91+
op.startParameter = 1;
92+
op.lengthParameter = 3;
93+
return {op};
94+
}
95+
case Instruction::CODECOPY:
96+
case Instruction::CALLDATACOPY:
97+
case Instruction::RETURNDATACOPY:
98+
{
99+
assertThrow(memory(_instruction) == Effect::Write, OptimizerException, "");
100+
assertThrow(storage(_instruction) == Effect::None, OptimizerException, "");
101+
Operation op;
102+
op.effect = memory(_instruction);
103+
op.location = Location::Memory;
104+
op.startParameter = 0;
105+
op.lengthParameter = 2;
106+
return {op};
107+
}
108+
case Instruction::STATICCALL:
109+
case Instruction::CALL:
110+
case Instruction::CALLCODE:
111+
case Instruction::DELEGATECALL:
112+
{
113+
size_t paramCount = static_cast<size_t>(instructionInfo(_instruction).args);
114+
vector<Operation> operations{
115+
Operation{Location::Memory, Effect::Read, paramCount - 4, paramCount - 3, {}},
116+
Operation{Location::Storage, Effect::Read, {}, {}, {}}
117+
};
118+
if (_instruction != Instruction::STATICCALL)
119+
operations.emplace_back(Operation{Location::Storage, Effect::Write, {}, {}, {}});
120+
operations.emplace_back(Operation{
121+
Location::Memory,
122+
Effect::Write,
123+
paramCount - 2,
124+
paramCount - 1,
125+
{}
126+
});
127+
return operations;
128+
}
129+
case Instruction::CREATE:
130+
case Instruction::CREATE2:
131+
return vector<Operation>{
132+
Operation{
133+
Location::Memory,
134+
Effect::Read,
135+
1,
136+
2,
137+
{}
138+
},
139+
Operation{Location::Storage, Effect::Read, {}, {}, {}},
140+
Operation{Location::Storage, Effect::Write, {}, {}, {}}
141+
};
142+
case Instruction::MSIZE:
143+
// This is just to satisfy the assert below.
144+
return vector<Operation>{};
145+
default:
146+
assertThrow(storage(_instruction) == None && memory(_instruction) == None, AssemblyException, "");
147+
}
148+
return {};
149+
}
150+
32151
bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item, bool _msizeImportant)
33152
{
34153
switch (_item.type())
@@ -49,7 +168,7 @@ bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item, bool
49168
case PushLibraryAddress:
50169
case PushImmutable:
51170
return false;
52-
case Operation:
171+
case evmasm::Operation:
53172
{
54173
if (isSwapInstruction(_item) || isDupInstruction(_item))
55174
return false;
@@ -79,7 +198,7 @@ bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item, bool
79198

80199
bool SemanticInformation::isCommutativeOperation(AssemblyItem const& _item)
81200
{
82-
if (_item.type() != Operation)
201+
if (_item.type() != evmasm::Operation)
83202
return false;
84203
switch (_item.instruction())
85204
{
@@ -97,14 +216,14 @@ bool SemanticInformation::isCommutativeOperation(AssemblyItem const& _item)
97216

98217
bool SemanticInformation::isDupInstruction(AssemblyItem const& _item)
99218
{
100-
if (_item.type() != Operation)
219+
if (_item.type() != evmasm::Operation)
101220
return false;
102221
return evmasm::isDupInstruction(_item.instruction());
103222
}
104223

105224
bool SemanticInformation::isSwapInstruction(AssemblyItem const& _item)
106225
{
107-
if (_item.type() != Operation)
226+
if (_item.type() != evmasm::Operation)
108227
return false;
109228
return evmasm::isSwapInstruction(_item.instruction());
110229
}
@@ -116,7 +235,7 @@ bool SemanticInformation::isJumpInstruction(AssemblyItem const& _item)
116235

117236
bool SemanticInformation::altersControlFlow(AssemblyItem const& _item)
118237
{
119-
if (_item.type() != Operation)
238+
if (_item.type() != evmasm::Operation)
120239
return false;
121240
switch (_item.instruction())
122241
{
@@ -166,7 +285,7 @@ bool SemanticInformation::isDeterministic(AssemblyItem const& _item)
166285
{
167286
assertThrow(_item.type() != VerbatimBytecode, AssemblyException, "");
168287

169-
if (_item.type() != Operation)
288+
if (_item.type() != evmasm::Operation)
170289
return true;
171290

172291
switch (_item.instruction())

libevmasm/SemanticInformation.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,32 @@ struct SemanticInformation
4545
Write
4646
};
4747

48+
enum class Location { Storage, Memory };
49+
50+
/**
51+
* Represents a read or write operation from or to one of the data locations.
52+
*/
53+
struct Operation
54+
{
55+
Location location;
56+
Effect effect;
57+
/// Start of affected area as an index into the parameters.
58+
/// Unknown if not provided.
59+
std::optional<size_t> startParameter;
60+
/// Length of the affected area as an index into the parameters (if this is an opcode).
61+
/// Unknown if neither this nor lengthConstant is provided.
62+
std::optional<size_t> lengthParameter;
63+
/// Length as a constant.
64+
/// Unknown if neither this nor lengthArgument is provided.
65+
std::optional<size_t> lengthConstant;
66+
};
67+
68+
/// @returns the sequence of read write operations performed by the instruction.
69+
/// Order matters.
70+
/// For external calls, there is just one unknown read and one unknown write operation,
71+
/// event though there might be multiple.
72+
static std::vector<Operation> readWriteOperations(Instruction _instruction);
73+
4874
/// @returns true if the given items starts a new block for common subexpression analysis.
4975
/// @param _msizeImportant if false, consider an operation non-breaking if its only side-effect is that it modifies msize.
5076
static bool breaksCSEAnalysisBlock(AssemblyItem const& _item, bool _msizeImportant);

0 commit comments

Comments
 (0)