mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Extend side effects to operations.
This commit is contained in:
parent
5369bdc8fb
commit
d0d4bca35b
@ -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())
|
||||||
|
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user