Extend side effects to operations.

This commit is contained in:
chriseth 2022-02-15 16:19:55 +01:00
parent 5369bdc8fb
commit d0d4bca35b
2 changed files with 151 additions and 6 deletions

View File

@ -29,6 +29,125 @@ using namespace std;
using namespace solidity; using namespace solidity;
using namespace solidity::evmasm; using namespace solidity::evmasm;
vector<SemanticInformation::Operation> SemanticInformation::readWriteOperations(Instruction _instruction)
{
switch (_instruction)
{
case Instruction::SSTORE:
case Instruction::SLOAD:
{
assertThrow(memory(_instruction) == Effect::None, OptimizerException, "");
assertThrow(storage(_instruction) != Effect::None, OptimizerException, "");
Operation op;
op.effect = storage(_instruction);
op.location = Location::Storage;
op.startParameter = 0;
// We know that exactly one slot is affected.
op.lengthConstant = 1;
return {op};
}
case Instruction::MSTORE:
case Instruction::MSTORE8:
case Instruction::MLOAD:
{
assertThrow(memory(_instruction) != Effect::None, OptimizerException, "");
assertThrow(storage(_instruction) == Effect::None, OptimizerException, "");
Operation op;
op.effect = memory(_instruction);
op.location = Location::Memory;
op.startParameter = 0;
if (_instruction == Instruction::MSTORE || _instruction == Instruction::MLOAD)
op.lengthConstant = 32;
else if (_instruction == Instruction::MSTORE8)
op.lengthConstant = 1;
return {op};
}
case Instruction::REVERT:
case Instruction::RETURN:
case Instruction::KECCAK256:
case Instruction::LOG0:
case Instruction::LOG1:
case Instruction::LOG2:
case Instruction::LOG3:
case Instruction::LOG4:
{
assertThrow(storage(_instruction) == Effect::None, OptimizerException, "");
assertThrow(memory(_instruction) == Effect::Read, OptimizerException, "");
Operation op;
op.effect = memory(_instruction);
op.location = Location::Memory;
op.startParameter = 0;
op.lengthParameter = 1;
return {op};
}
case Instruction::EXTCODECOPY:
{
assertThrow(memory(_instruction) == Effect::Write, OptimizerException, "");
assertThrow(storage(_instruction) == Effect::None, OptimizerException, "");
Operation op;
op.effect = memory(_instruction);
op.location = Location::Memory;
op.startParameter = 1;
op.lengthParameter = 3;
return {op};
}
case Instruction::CODECOPY:
case Instruction::CALLDATACOPY:
case Instruction::RETURNDATACOPY:
{
assertThrow(memory(_instruction) == Effect::Write, OptimizerException, "");
assertThrow(storage(_instruction) == Effect::None, OptimizerException, "");
Operation op;
op.effect = memory(_instruction);
op.location = Location::Memory;
op.startParameter = 0;
op.lengthParameter = 2;
return {op};
}
case Instruction::STATICCALL:
case Instruction::CALL:
case Instruction::CALLCODE:
case Instruction::DELEGATECALL:
{
size_t paramCount = static_cast<size_t>(instructionInfo(_instruction).args);
vector<Operation> operations{
Operation{Location::Memory, Effect::Read, paramCount - 4, paramCount - 3, {}},
Operation{Location::Storage, Effect::Read, {}, {}, {}}
};
if (_instruction != Instruction::STATICCALL)
operations.emplace_back(Operation{Location::Storage, Effect::Write, {}, {}, {}});
operations.emplace_back(Operation{
Location::Memory,
Effect::Write,
paramCount - 2,
paramCount - 1,
{}
});
return operations;
}
case Instruction::CREATE:
case Instruction::CREATE2:
return vector<Operation>{
Operation{
Location::Memory,
Effect::Read,
1,
2,
{}
},
Operation{Location::Storage, Effect::Read, {}, {}, {}},
Operation{Location::Storage, Effect::Write, {}, {}, {}}
};
case Instruction::MSIZE:
// This is just to satisfy the assert below.
return vector<Operation>{};
default:
assertThrow(storage(_instruction) == None && memory(_instruction) == None, AssemblyException, "");
}
return {};
}
bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item, bool _msizeImportant) bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item, bool _msizeImportant)
{ {
switch (_item.type()) switch (_item.type())
@ -49,7 +168,7 @@ bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item, bool
case PushLibraryAddress: case PushLibraryAddress:
case PushImmutable: case PushImmutable:
return false; return false;
case Operation: case evmasm::Operation:
{ {
if (isSwapInstruction(_item) || isDupInstruction(_item)) if (isSwapInstruction(_item) || isDupInstruction(_item))
return false; return false;
@ -79,7 +198,7 @@ bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item, bool
bool SemanticInformation::isCommutativeOperation(AssemblyItem const& _item) bool SemanticInformation::isCommutativeOperation(AssemblyItem const& _item)
{ {
if (_item.type() != Operation) if (_item.type() != evmasm::Operation)
return false; return false;
switch (_item.instruction()) switch (_item.instruction())
{ {
@ -97,14 +216,14 @@ bool SemanticInformation::isCommutativeOperation(AssemblyItem const& _item)
bool SemanticInformation::isDupInstruction(AssemblyItem const& _item) bool SemanticInformation::isDupInstruction(AssemblyItem const& _item)
{ {
if (_item.type() != Operation) if (_item.type() != evmasm::Operation)
return false; return false;
return evmasm::isDupInstruction(_item.instruction()); return evmasm::isDupInstruction(_item.instruction());
} }
bool SemanticInformation::isSwapInstruction(AssemblyItem const& _item) bool SemanticInformation::isSwapInstruction(AssemblyItem const& _item)
{ {
if (_item.type() != Operation) if (_item.type() != evmasm::Operation)
return false; return false;
return evmasm::isSwapInstruction(_item.instruction()); return evmasm::isSwapInstruction(_item.instruction());
} }
@ -116,7 +235,7 @@ bool SemanticInformation::isJumpInstruction(AssemblyItem const& _item)
bool SemanticInformation::altersControlFlow(AssemblyItem const& _item) bool SemanticInformation::altersControlFlow(AssemblyItem const& _item)
{ {
if (_item.type() != Operation) if (_item.type() != evmasm::Operation)
return false; return false;
switch (_item.instruction()) switch (_item.instruction())
{ {
@ -166,7 +285,7 @@ bool SemanticInformation::isDeterministic(AssemblyItem const& _item)
{ {
assertThrow(_item.type() != VerbatimBytecode, AssemblyException, ""); assertThrow(_item.type() != VerbatimBytecode, AssemblyException, "");
if (_item.type() != Operation) if (_item.type() != evmasm::Operation)
return true; return true;
switch (_item.instruction()) switch (_item.instruction())

View File

@ -45,6 +45,32 @@ struct SemanticInformation
Write Write
}; };
enum class Location { Storage, Memory };
/**
* Represents a read or write operation from or to one of the data locations.
*/
struct Operation
{
Location location;
Effect effect;
/// Start of affected area as an index into the parameters.
/// Unknown if not provided.
std::optional<size_t> startParameter;
/// Length of the affected area as an index into the parameters (if this is an opcode).
/// Unknown if neither this nor lengthConstant is provided.
std::optional<size_t> lengthParameter;
/// Length as a constant.
/// Unknown if neither this nor lengthArgument is provided.
std::optional<size_t> lengthConstant;
};
/// @returns the sequence of read write operations performed by the instruction.
/// Order matters.
/// For external calls, there is just one unknown read and one unknown write operation,
/// event though there might be multiple.
static std::vector<Operation> readWriteOperations(Instruction _instruction);
/// @returns true if the given items starts a new block for common subexpression analysis. /// @returns true if the given items starts a new block for common subexpression analysis.
/// @param _msizeImportant if false, consider an operation non-breaking if its only side-effect is that it modifies msize. /// @param _msizeImportant if false, consider an operation non-breaking if its only side-effect is that it modifies msize.
static bool breaksCSEAnalysisBlock(AssemblyItem const& _item, bool _msizeImportant); static bool breaksCSEAnalysisBlock(AssemblyItem const& _item, bool _msizeImportant);