mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
initial attempt
This commit is contained in:
parent
056c4593e3
commit
a467bdcb3c
@ -558,6 +558,26 @@ LinkerObject const& Assembly::assemble() const
|
||||
case Operation:
|
||||
ret.bytecode.push_back(static_cast<uint8_t>(i.instruction()));
|
||||
break;
|
||||
case Swap:
|
||||
assertThrow(i.data() > 0 && i.data() <= maxSwap(), AssemblyException, "Invalid swap.");
|
||||
if (i.data() <= 16)
|
||||
ret.bytecode.push_back(static_cast<uint8_t>(evmasm::Instruction::SWAP1 + (i.data() - 1)));
|
||||
else
|
||||
{
|
||||
ret.bytecode.push_back(static_cast<uint8_t>(evmasm::Instruction::SWAP_N));
|
||||
ret.bytecode.push_back(static_cast<uint8_t>(i.data() - 1));
|
||||
}
|
||||
break;
|
||||
case Dup:
|
||||
assertThrow(i.data() > 0 && i.data() <= maxDup(), AssemblyException, "Invalid dup.");
|
||||
if (i.data() <= 16)
|
||||
ret.bytecode.push_back(static_cast<uint8_t>(evmasm::Instruction::DUP1 + (i.data() - 1)));
|
||||
else
|
||||
{
|
||||
ret.bytecode.push_back(static_cast<uint8_t>(evmasm::Instruction::DUP_N));
|
||||
ret.bytecode.push_back(static_cast<uint8_t>(i.data() - 1));
|
||||
}
|
||||
break;
|
||||
case Push:
|
||||
{
|
||||
unsigned b = max<unsigned>(1, numberEncodingSize(i.data()));
|
||||
|
@ -83,6 +83,23 @@ public:
|
||||
append(AssemblyItem(std::move(_data), _arguments, _returnVariables));
|
||||
}
|
||||
|
||||
void appendSwap(unsigned _height)
|
||||
{
|
||||
append(AssemblyItem(AssemblyItemType::Swap, _height));
|
||||
}
|
||||
void appendDup(unsigned _height)
|
||||
{
|
||||
append(AssemblyItem(AssemblyItemType::Dup, _height));
|
||||
}
|
||||
unsigned maxDup() const
|
||||
{
|
||||
return 16;
|
||||
}
|
||||
unsigned maxSwap() const
|
||||
{
|
||||
return 16;
|
||||
}
|
||||
|
||||
AssemblyItem appendJump() { auto ret = append(newPushTag()); append(Instruction::JUMP); return ret; }
|
||||
AssemblyItem appendJumpI() { auto ret = append(newPushTag()); append(Instruction::JUMPI); return ret; }
|
||||
AssemblyItem appendJump(AssemblyItem const& _tag) { auto ret = append(_tag.pushTag()); append(Instruction::JUMP); return ret; }
|
||||
|
@ -123,6 +123,12 @@ size_t AssemblyItem::bytesRequired(size_t _addressLength, Precision _precision)
|
||||
case Operation:
|
||||
case Tag: // 1 byte for the JUMPDEST
|
||||
return 1;
|
||||
case Swap:
|
||||
case Dup:
|
||||
if (data() <= 16)
|
||||
return 1;
|
||||
else
|
||||
return 2;
|
||||
case Push:
|
||||
return 1 + max<size_t>(1, numberEncodingSize(data()));
|
||||
case PushSubSize:
|
||||
@ -173,6 +179,10 @@ size_t AssemblyItem::arguments() const
|
||||
return get<0>(*m_verbatimBytecode);
|
||||
else if (type() == AssignImmutable)
|
||||
return 2;
|
||||
else if (type() == Swap)
|
||||
return static_cast<size_t>(data());
|
||||
else if (type() == Dup)
|
||||
return static_cast<size_t>(data());
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
@ -183,6 +193,10 @@ size_t AssemblyItem::returnValues() const
|
||||
{
|
||||
case Operation:
|
||||
return static_cast<size_t>(instructionInfo(instruction()).ret);
|
||||
case Swap:
|
||||
return static_cast<size_t>(data());
|
||||
case Dup:
|
||||
return static_cast<size_t>(data()) + 1;
|
||||
case Push:
|
||||
case PushTag:
|
||||
case PushData:
|
||||
@ -210,7 +224,11 @@ bool AssemblyItem::canBeFunctional() const
|
||||
switch (m_type)
|
||||
{
|
||||
case Operation:
|
||||
return !isDupInstruction(instruction()) && !isSwapInstruction(instruction());
|
||||
return !(m_instruction >= Instruction::SWAP1 && m_instruction <= Instruction::SWAP16) &&
|
||||
!(m_instruction >= Instruction::DUP1 && m_instruction <= Instruction::DUP16) &&
|
||||
m_instruction != Instruction::SWAP_N &&
|
||||
m_instruction != Instruction::DUP_N
|
||||
;
|
||||
case Push:
|
||||
case PushTag:
|
||||
case PushData:
|
||||
@ -254,6 +272,16 @@ string AssemblyItem::toAssemblyText(Assembly const& _assembly) const
|
||||
text = util::toLower(instructionInfo(instruction()).name);
|
||||
break;
|
||||
}
|
||||
case Swap:
|
||||
{
|
||||
text = "swap" + to_string(data());
|
||||
break;
|
||||
}
|
||||
case Dup:
|
||||
{
|
||||
text = "dup" + to_string(data());
|
||||
break;
|
||||
}
|
||||
case Push:
|
||||
text = toHex(toCompactBigEndian(data(), 1), util::HexPrefix::Add);
|
||||
break;
|
||||
@ -332,6 +360,12 @@ ostream& solidity::evmasm::operator<<(ostream& _out, AssemblyItem const& _item)
|
||||
if (_item.instruction() == Instruction::JUMP || _item.instruction() == Instruction::JUMPI)
|
||||
_out << "\t" << _item.getJumpTypeAsString();
|
||||
break;
|
||||
case Swap:
|
||||
_out << "SWAP" << dec << _item.data();
|
||||
break;
|
||||
case Dup:
|
||||
_out << "DUP" << dec << _item.data();
|
||||
break;
|
||||
case Push:
|
||||
_out << " PUSH " << hex << _item.data() << dec;
|
||||
break;
|
||||
|
@ -39,6 +39,8 @@ enum AssemblyItemType
|
||||
{
|
||||
UndefinedItem,
|
||||
Operation,
|
||||
Swap,
|
||||
Dup,
|
||||
Push,
|
||||
PushTag,
|
||||
PushSub,
|
||||
@ -70,7 +72,21 @@ public:
|
||||
m_type(Operation),
|
||||
m_instruction(_i),
|
||||
m_location(std::move(_location))
|
||||
{}
|
||||
{
|
||||
// TODO: avoid this on call sites instead
|
||||
if (_i >= Instruction::SWAP1 && _i <= Instruction::SWAP16)
|
||||
{
|
||||
m_type = Swap;
|
||||
m_data = std::make_shared<u256>(static_cast<unsigned>(_i) - static_cast<unsigned>(Instruction::SWAP1) + 1);
|
||||
m_instruction = {};
|
||||
}
|
||||
else if (_i >= Instruction::DUP1 && _i <= Instruction::DUP16)
|
||||
{
|
||||
m_type = Dup;
|
||||
m_data = std::make_shared<u256>(static_cast<unsigned>(_i) - static_cast<unsigned>(Instruction::DUP1) + 1);
|
||||
m_instruction = {};
|
||||
}
|
||||
}
|
||||
AssemblyItem(AssemblyItemType _type, u256 _data = 0, langutil::SourceLocation _location = langutil::SourceLocation()):
|
||||
m_type(_type),
|
||||
m_location(std::move(_location))
|
||||
@ -144,10 +160,9 @@ public:
|
||||
return data() < _other.data();
|
||||
}
|
||||
|
||||
/// Shortcut that avoids constructing an AssemblyItem just to perform the comparison.
|
||||
bool operator==(Instruction _instr) const
|
||||
{
|
||||
return type() == Operation && instruction() == _instr;
|
||||
return (*this) == AssemblyItem(_instr);
|
||||
}
|
||||
bool operator!=(Instruction _instr) const { return !operator==(_instr); }
|
||||
|
||||
|
@ -95,7 +95,7 @@ void CommonSubexpressionEliminator::optimizeBreakingItem()
|
||||
Id condition = m_state.stackElement(m_state.stackHeight() - 1, itemLocation);
|
||||
if (classes.knownNonZero(condition))
|
||||
{
|
||||
feedItem(AssemblyItem(Instruction::SWAP1, itemLocation), true);
|
||||
feedItem(AssemblyItem(AssemblyItemType::Swap, 1, itemLocation), true);
|
||||
feedItem(AssemblyItem(Instruction::POP, itemLocation), true);
|
||||
|
||||
AssemblyItem item(Instruction::JUMP, itemLocation);
|
||||
@ -397,7 +397,7 @@ void CSECodeGenerator::generateClassElement(Id _c, bool _allowSequenced)
|
||||
|
||||
while (SemanticInformation::isCommutativeOperation(*expr.item) &&
|
||||
!m_generatedItems.empty() &&
|
||||
m_generatedItems.back() == AssemblyItem(Instruction::SWAP1))
|
||||
m_generatedItems.back() == AssemblyItem(AssemblyItemType::Swap, 1))
|
||||
// this will not append a swap but remove the one that is already there
|
||||
appendOrRemoveSwap(m_stackHeight - 1, itemLocation);
|
||||
for (size_t i = 0; i < arguments.size(); ++i)
|
||||
@ -474,7 +474,7 @@ void CSECodeGenerator::appendDup(int _fromPosition, SourceLocation const& _locat
|
||||
int instructionNum = 1 + m_stackHeight - _fromPosition;
|
||||
assertThrow(instructionNum <= 16, StackTooDeepException, util::stackTooDeepString);
|
||||
assertThrow(1 <= instructionNum, OptimizerException, "Invalid stack access.");
|
||||
appendItem(AssemblyItem(dupInstruction(static_cast<unsigned>(instructionNum)), _location));
|
||||
appendItem(AssemblyItem(AssemblyItemType::Dup, instructionNum, _location));
|
||||
m_stack[m_stackHeight] = m_stack[_fromPosition];
|
||||
m_classPositions[m_stack[m_stackHeight]].insert(m_stackHeight);
|
||||
}
|
||||
@ -487,7 +487,7 @@ void CSECodeGenerator::appendOrRemoveSwap(int _fromPosition, SourceLocation cons
|
||||
int instructionNum = m_stackHeight - _fromPosition;
|
||||
assertThrow(instructionNum <= 16, StackTooDeepException, util::stackTooDeepString);
|
||||
assertThrow(1 <= instructionNum, OptimizerException, "Invalid stack access.");
|
||||
appendItem(AssemblyItem(swapInstruction(static_cast<unsigned>(instructionNum)), _location));
|
||||
appendItem(AssemblyItem(AssemblyItemType::Swap, instructionNum, _location));
|
||||
|
||||
if (m_stack[m_stackHeight] != m_stack[_fromPosition])
|
||||
{
|
||||
|
@ -85,6 +85,12 @@ bigint ConstantOptimisationMethod::simpleRunGas(AssemblyItems const& _items)
|
||||
for (AssemblyItem const& item: _items)
|
||||
if (item.type() == Push)
|
||||
gas += GasMeter::runGas(Instruction::PUSH1);
|
||||
else if (item.type() == Swap)
|
||||
// TODO
|
||||
gas += GasMeter::runGas(Instruction::SWAP1);
|
||||
else if (item.type() == Dup)
|
||||
// TODO
|
||||
gas += GasMeter::runGas(Instruction::DUP1);
|
||||
else if (item.type() == Operation)
|
||||
{
|
||||
if (item.instruction() == Instruction::EXP)
|
||||
@ -167,21 +173,21 @@ AssemblyItems const& CodeCopyMethod::copyRoutine()
|
||||
|
||||
// back up memory
|
||||
// mload(0)
|
||||
Instruction::DUP1,
|
||||
AssemblyItem(Dup, 1),
|
||||
Instruction::MLOAD,
|
||||
|
||||
// codecopy(0, <offset>, 32)
|
||||
u256(32),
|
||||
AssemblyItem(PushData, u256(1) << 16), // replaced above in actualCopyRoutine[4]
|
||||
Instruction::DUP4,
|
||||
AssemblyItem(Dup, 4),
|
||||
Instruction::CODECOPY,
|
||||
|
||||
// mload(0)
|
||||
Instruction::DUP2,
|
||||
AssemblyItem(Dup, 2),
|
||||
Instruction::MLOAD,
|
||||
|
||||
// restore original memory
|
||||
Instruction::SWAP2,
|
||||
AssemblyItem(Swap, 2),
|
||||
Instruction::MSTORE
|
||||
};
|
||||
return copyRoutine;
|
||||
|
@ -175,6 +175,9 @@ enum class Instruction: uint8_t
|
||||
LOG3, ///< Makes a log entry; 3 topics.
|
||||
LOG4, ///< Makes a log entry; 4 topics.
|
||||
|
||||
SWAP_N = 0xB0,
|
||||
DUP_N,
|
||||
|
||||
CREATE = 0xf0, ///< create a new account with associated code
|
||||
CALL, ///< message-call into an account
|
||||
CALLCODE, ///< message-call with another account's code only
|
||||
@ -209,36 +212,12 @@ inline bool isPushInstruction(Instruction _inst)
|
||||
return Instruction::PUSH1 <= _inst && _inst <= Instruction::PUSH32;
|
||||
}
|
||||
|
||||
/// @returns true if the instruction is a DUP
|
||||
inline bool isDupInstruction(Instruction _inst)
|
||||
{
|
||||
return Instruction::DUP1 <= _inst && _inst <= Instruction::DUP16;
|
||||
}
|
||||
|
||||
/// @returns true if the instruction is a SWAP
|
||||
inline bool isSwapInstruction(Instruction _inst)
|
||||
{
|
||||
return Instruction::SWAP1 <= _inst && _inst <= Instruction::SWAP16;
|
||||
}
|
||||
|
||||
/// @returns true if the instruction is a LOG
|
||||
inline bool isLogInstruction(Instruction _inst)
|
||||
{
|
||||
return Instruction::LOG0 <= _inst && _inst <= Instruction::LOG4;
|
||||
}
|
||||
|
||||
/// @returns the number of PUSH Instruction _inst
|
||||
inline unsigned getPushNumber(Instruction _inst)
|
||||
{
|
||||
return static_cast<uint8_t>(_inst) - unsigned(Instruction::PUSH1) + 1;
|
||||
}
|
||||
|
||||
/// @returns the number of DUP Instruction _inst
|
||||
inline unsigned getDupNumber(Instruction _inst)
|
||||
{
|
||||
return static_cast<uint8_t>(_inst) - unsigned(Instruction::DUP1) + 1;
|
||||
}
|
||||
|
||||
/// @returns the number of SWAP Instruction _inst
|
||||
inline unsigned getSwapNumber(Instruction _inst)
|
||||
{
|
||||
@ -258,20 +237,6 @@ inline Instruction pushInstruction(unsigned _number)
|
||||
return Instruction(unsigned(Instruction::PUSH1) + _number - 1);
|
||||
}
|
||||
|
||||
/// @returns the DUP<_number> instruction
|
||||
inline Instruction dupInstruction(unsigned _number)
|
||||
{
|
||||
assertThrow(1 <= _number && _number <= 16, InvalidOpcode, std::string("Invalid DUP instruction requested (") + std::to_string(_number) + ").");
|
||||
return Instruction(unsigned(Instruction::DUP1) + _number - 1);
|
||||
}
|
||||
|
||||
/// @returns the SWAP<_number> instruction
|
||||
inline Instruction swapInstruction(unsigned _number)
|
||||
{
|
||||
assertThrow(1 <= _number && _number <= 16, InvalidOpcode, std::string("Invalid SWAP instruction requested (") + std::to_string(_number) + ").");
|
||||
return Instruction(unsigned(Instruction::SWAP1) + _number - 1);
|
||||
}
|
||||
|
||||
/// @returns the LOG<_number> instruction
|
||||
inline Instruction logInstruction(unsigned _number)
|
||||
{
|
||||
|
@ -118,6 +118,27 @@ KnownState::StoreOperation KnownState::feedItem(AssemblyItem const& _item, bool
|
||||
m_expressionClasses->newClass(_item.location())
|
||||
);
|
||||
}
|
||||
else if (_item.type() == Dup)
|
||||
{
|
||||
// TODO: check
|
||||
setStackElement(
|
||||
m_stackHeight + 1,
|
||||
stackElement(
|
||||
m_stackHeight - static_cast<int>(_item.data() - 1),
|
||||
_item.location()
|
||||
)
|
||||
);
|
||||
m_stackHeight++;
|
||||
}
|
||||
else if (_item.type() == Swap)
|
||||
{
|
||||
// TODO: check
|
||||
swapStackElements(
|
||||
m_stackHeight,
|
||||
m_stackHeight - 1 - static_cast<int>(_item.data() - 1),
|
||||
_item.location()
|
||||
);
|
||||
}
|
||||
else if (_item.type() != Operation)
|
||||
{
|
||||
assertThrow(_item.deposit() == 1, InvalidDeposit, "");
|
||||
|
@ -159,11 +159,13 @@ struct OpReturnRevert: SimplePeepholeOptimizerMethod<OpReturnRevert>
|
||||
if (
|
||||
(_returnRevert == Instruction::RETURN || _returnRevert == Instruction::REVERT) &&
|
||||
_push.type() == Push &&
|
||||
(_pushOrDup.type() == Push || _pushOrDup == dupInstruction(1))
|
||||
(_pushOrDup.type() == Push || ((_pushOrDup.type() == Dup && _pushOrDup.data() == 1) || _pushOrDup == Instruction::DUP1))
|
||||
)
|
||||
if (
|
||||
(_op.type() == Operation && !instructionInfo(_op.instruction()).sideEffects) ||
|
||||
_op.type() == Push
|
||||
_op.type() == Push ||
|
||||
_op.type() == Swap ||
|
||||
_op.type() == Dup
|
||||
)
|
||||
{
|
||||
*_out = _push;
|
||||
@ -190,7 +192,7 @@ struct DoublePush: SimplePeepholeOptimizerMethod<DoublePush>
|
||||
if (_push1.type() == Push && _push2.type() == Push && _push1.data() == _push2.data())
|
||||
{
|
||||
*_out = _push1;
|
||||
*_out = {Instruction::DUP1, _push2.location()};
|
||||
*_out = AssemblyItem(Dup, 1, _push2.location());
|
||||
return true;
|
||||
}
|
||||
else
|
||||
@ -204,7 +206,10 @@ struct CommutativeSwap: SimplePeepholeOptimizerMethod<CommutativeSwap>
|
||||
{
|
||||
// Remove SWAP1 if following instruction is commutative
|
||||
if (
|
||||
_swap == Instruction::SWAP1 &&
|
||||
(
|
||||
_swap == Instruction::SWAP1 ||
|
||||
_swap == AssemblyItem(Swap, 1)
|
||||
) &&
|
||||
SemanticInformation::isCommutativeOperation(_op)
|
||||
)
|
||||
{
|
||||
@ -228,7 +233,10 @@ struct SwapComparison: SimplePeepholeOptimizerMethod<SwapComparison>
|
||||
};
|
||||
|
||||
if (
|
||||
_swap == Instruction::SWAP1 &&
|
||||
(
|
||||
_swap == Instruction::SWAP1 ||
|
||||
_swap == AssemblyItem(Swap, 1)
|
||||
) &&
|
||||
_op.type() == Operation &&
|
||||
swappableOps.count(_op.instruction())
|
||||
)
|
||||
@ -250,10 +258,33 @@ struct DupSwap: SimplePeepholeOptimizerMethod<DupSwap>
|
||||
std::back_insert_iterator<AssemblyItems> _out
|
||||
)
|
||||
{
|
||||
auto dupNum = [](AssemblyItem const& _item) -> std::optional<unsigned> {
|
||||
if (_item.type() == AssemblyItemType::Dup)
|
||||
return static_cast<unsigned>(_item.data());
|
||||
else if (
|
||||
_item.type() == AssemblyItemType::Operation &&
|
||||
_item.instruction() >= Instruction::DUP1 &&
|
||||
_item.instruction() <= Instruction::DUP16
|
||||
)
|
||||
return static_cast<unsigned>(_item.instruction()) - static_cast<unsigned>(Instruction::DUP1) + 1;
|
||||
return nullopt;
|
||||
};
|
||||
auto swapNum = [](AssemblyItem const& _item) -> std::optional<unsigned> {
|
||||
if (_item.type() == AssemblyItemType::Swap)
|
||||
return static_cast<unsigned>(_item.data());
|
||||
else if (
|
||||
_item.type() == AssemblyItemType::Operation &&
|
||||
_item.instruction() >= Instruction::SWAP1 &&
|
||||
_item.instruction() <= Instruction::SWAP16
|
||||
)
|
||||
return static_cast<unsigned>(_item.instruction()) - static_cast<unsigned>(Instruction::SWAP1) + 1;
|
||||
return nullopt;
|
||||
};
|
||||
if (
|
||||
SemanticInformation::isDupInstruction(_dupN) &&
|
||||
SemanticInformation::isSwapInstruction(_swapN) &&
|
||||
getDupNumber(_dupN.instruction()) == getSwapNumber(_swapN.instruction())
|
||||
dupNum(_dupN).has_value() && swapNum(_swapN).has_value() && dupNum(_dupN) == swapNum(_swapN)
|
||||
/* _dupN.type() == AssemblyItemType::Dup &&
|
||||
_swapN.type() == AssemblyItemType::Swap &&
|
||||
_dupN.data() == _swapN.data()*/
|
||||
)
|
||||
{
|
||||
*_out = _dupN;
|
||||
|
@ -169,6 +169,8 @@ bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item, bool
|
||||
case PushData:
|
||||
case PushLibraryAddress:
|
||||
case PushImmutable:
|
||||
case Swap:
|
||||
case Dup:
|
||||
return false;
|
||||
case evmasm::Operation:
|
||||
{
|
||||
@ -218,16 +220,27 @@ bool SemanticInformation::isCommutativeOperation(AssemblyItem const& _item)
|
||||
|
||||
bool SemanticInformation::isDupInstruction(AssemblyItem const& _item)
|
||||
{
|
||||
if (_item.type() != evmasm::Operation)
|
||||
return false;
|
||||
return evmasm::isDupInstruction(_item.instruction());
|
||||
return _item.type() == evmasm::Dup ||
|
||||
(_item.type() == evmasm::Operation &&
|
||||
(
|
||||
(_item.instruction() >= Instruction::DUP1 && _item.instruction() <= Instruction::DUP16) ||
|
||||
(_item.instruction() == Instruction::DUP_N)
|
||||
)
|
||||
)
|
||||
;
|
||||
|
||||
}
|
||||
|
||||
bool SemanticInformation::isSwapInstruction(AssemblyItem const& _item)
|
||||
{
|
||||
if (_item.type() != evmasm::Operation)
|
||||
return false;
|
||||
return evmasm::isSwapInstruction(_item.instruction());
|
||||
return _item.type() == evmasm::Swap ||
|
||||
(_item.type() == evmasm::Operation &&
|
||||
(
|
||||
(_item.instruction() >= Instruction::SWAP1 && _item.instruction() <= Instruction::SWAP16) ||
|
||||
(_item.instruction() == Instruction::SWAP_N)
|
||||
)
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
bool SemanticInformation::isJumpInstruction(AssemblyItem const& _item)
|
||||
|
@ -65,7 +65,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
|
||||
// stack: source_ref [source_length] target_ref
|
||||
// store target_ref
|
||||
for (unsigned i = _sourceType.sizeOnStack(); i > 0; --i)
|
||||
m_context << swapInstruction(i);
|
||||
m_context << AssemblyItem(AssemblyItemType::Swap, i);
|
||||
// stack: target_ref source_ref [source_length]
|
||||
|
||||
if (_targetType.isByteArrayOrString())
|
||||
@ -177,7 +177,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
|
||||
_context << copyLoopStart;
|
||||
// check for loop condition
|
||||
_context
|
||||
<< dupInstruction(3 + byteOffsetSize) << dupInstruction(2 + byteOffsetSize)
|
||||
<< AssemblyItem(AssemblyItemType::Dup, 3 + byteOffsetSize) << AssemblyItem(AssemblyItemType::Dup, 2 + byteOffsetSize)
|
||||
<< Instruction::GT << Instruction::ISZERO;
|
||||
evmasm::AssemblyItem copyLoopEnd = _context.appendConditionalJump();
|
||||
// stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset]
|
||||
@ -213,7 +213,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
|
||||
// We might copy too much if there is padding at the last element, but this way end
|
||||
// checking is easier.
|
||||
// stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset]
|
||||
_context << dupInstruction(3 + byteOffsetSize);
|
||||
_context << AssemblyItem(AssemblyItemType::Dup, 3 + byteOffsetSize);
|
||||
if (_sourceType.location() == DataLocation::Storage)
|
||||
{
|
||||
if (haveByteOffsetSource)
|
||||
@ -233,9 +233,9 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
|
||||
util::stackTooDeepString
|
||||
);
|
||||
// fetch target storage reference
|
||||
_context << dupInstruction(2 + byteOffsetSize + sourceBaseType->sizeOnStack());
|
||||
_context << AssemblyItem(AssemblyItemType::Dup, 2 + byteOffsetSize + sourceBaseType->sizeOnStack());
|
||||
if (haveByteOffsetTarget)
|
||||
_context << dupInstruction(1 + byteOffsetSize + sourceBaseType->sizeOnStack());
|
||||
_context << AssemblyItem(AssemblyItemType::Dup, 1 + byteOffsetSize + sourceBaseType->sizeOnStack());
|
||||
else
|
||||
_context << u256(0);
|
||||
StorageItem(_context, *targetBaseType).storeValue(*sourceBaseType, SourceLocation(), true);
|
||||
@ -246,7 +246,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
|
||||
utils.incrementByteOffset(sourceBaseType->storageBytes(), 1, haveByteOffsetTarget ? 5 : 4);
|
||||
else
|
||||
{
|
||||
_context << swapInstruction(2 + byteOffsetSize);
|
||||
_context << AssemblyItem(AssemblyItemType::Swap, 2 + byteOffsetSize);
|
||||
if (sourceIsStorage)
|
||||
_context << sourceBaseType->storageSize();
|
||||
else if (_sourceType.location() == DataLocation::Memory)
|
||||
@ -255,26 +255,26 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
|
||||
_context << sourceBaseType->calldataHeadSize();
|
||||
_context
|
||||
<< Instruction::ADD
|
||||
<< swapInstruction(2 + byteOffsetSize);
|
||||
<< AssemblyItem(AssemblyItemType::Swap, 2 + byteOffsetSize);
|
||||
}
|
||||
// increment target
|
||||
if (haveByteOffsetTarget)
|
||||
utils.incrementByteOffset(targetBaseType->storageBytes(), byteOffsetSize, byteOffsetSize + 2);
|
||||
else
|
||||
_context
|
||||
<< swapInstruction(1 + byteOffsetSize)
|
||||
<< AssemblyItem(AssemblyItemType::Swap, 1 + byteOffsetSize)
|
||||
<< targetBaseType->storageSize()
|
||||
<< Instruction::ADD
|
||||
<< swapInstruction(1 + byteOffsetSize);
|
||||
<< AssemblyItem(AssemblyItemType::Swap, 1 + byteOffsetSize);
|
||||
_context.appendJumpTo(copyLoopStart);
|
||||
_context << copyLoopEnd;
|
||||
if (haveByteOffsetTarget)
|
||||
{
|
||||
// clear elements that might be left over in the current slot in target
|
||||
// stack: target_ref target_data_end source_data_pos target_data_pos source_data_end target_byte_offset [source_byte_offset]
|
||||
_context << dupInstruction(byteOffsetSize) << Instruction::ISZERO;
|
||||
_context << AssemblyItem(AssemblyItemType::Dup, byteOffsetSize) << Instruction::ISZERO;
|
||||
evmasm::AssemblyItem copyCleanupLoopEnd = _context.appendConditionalJump();
|
||||
_context << dupInstruction(2 + byteOffsetSize) << dupInstruction(1 + byteOffsetSize);
|
||||
_context << AssemblyItem(AssemblyItemType::Dup, 2 + byteOffsetSize) << AssemblyItem(AssemblyItemType::Dup, 1 + byteOffsetSize);
|
||||
StorageItem(_context, *targetBaseType).setToZero(SourceLocation(), true);
|
||||
utils.incrementByteOffset(targetBaseType->storageBytes(), byteOffsetSize, byteOffsetSize + 2);
|
||||
_context.appendJumpTo(copyLoopEnd);
|
||||
@ -510,7 +510,7 @@ void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWord
|
||||
}
|
||||
}
|
||||
// check for loop condition
|
||||
m_context << Instruction::DUP1 << dupInstruction(haveByteOffset ? 5 : 4);
|
||||
m_context << Instruction::DUP1 << AssemblyItem(AssemblyItemType::Dup, haveByteOffset ? 5 : 4);
|
||||
m_context << Instruction::GT;
|
||||
m_context.appendConditionalJumpTo(loopStart);
|
||||
// stack here: memory_end_offset storage_data_offset [storage_byte_offset] memory_offset
|
||||
@ -1008,7 +1008,7 @@ void ArrayUtils::retrieveLength(ArrayType const& _arrayType, unsigned _stackDept
|
||||
m_context << _arrayType.length();
|
||||
else
|
||||
{
|
||||
m_context << dupInstruction(1 + _stackDepth);
|
||||
m_context << AssemblyItem(AssemblyItemType::Dup, 1 + _stackDepth);
|
||||
switch (_arrayType.location())
|
||||
{
|
||||
case DataLocation::CallData:
|
||||
@ -1178,19 +1178,19 @@ void ArrayUtils::incrementByteOffset(unsigned _byteSize, unsigned _byteOffsetPos
|
||||
// byteOffset = 0;
|
||||
// }
|
||||
if (_byteOffsetPosition > 1)
|
||||
m_context << swapInstruction(_byteOffsetPosition - 1);
|
||||
m_context << AssemblyItem(AssemblyItemType::Swap, _byteOffsetPosition - 1);
|
||||
m_context << u256(_byteSize) << Instruction::ADD;
|
||||
if (_byteOffsetPosition > 1)
|
||||
m_context << swapInstruction(_byteOffsetPosition - 1);
|
||||
m_context << AssemblyItem(AssemblyItemType::Swap, _byteOffsetPosition - 1);
|
||||
// compute, X := (byteOffset + byteSize - 1) / 32, should be 1 iff byteOffset + bytesize > 32
|
||||
m_context
|
||||
<< u256(32) << dupInstruction(1 + _byteOffsetPosition) << u256(_byteSize - 1)
|
||||
<< u256(32) << AssemblyItem(AssemblyItemType::Dup, 1 + _byteOffsetPosition) << u256(_byteSize - 1)
|
||||
<< Instruction::ADD << Instruction::DIV;
|
||||
// increment storage offset if X == 1 (just add X to it)
|
||||
// stack: X
|
||||
m_context
|
||||
<< swapInstruction(_storageOffsetPosition) << dupInstruction(_storageOffsetPosition + 1)
|
||||
<< Instruction::ADD << swapInstruction(_storageOffsetPosition);
|
||||
<< AssemblyItem(AssemblyItemType::Swap, _storageOffsetPosition) << AssemblyItem(AssemblyItemType::Dup, _storageOffsetPosition + 1)
|
||||
<< Instruction::ADD << AssemblyItem(AssemblyItemType::Swap, _storageOffsetPosition);
|
||||
// stack: X
|
||||
// set source_byte_offset to zero if X == 1 (using source_byte_offset *= 1 - X)
|
||||
m_context << u256(1) << Instruction::SUB;
|
||||
@ -1199,6 +1199,6 @@ void ArrayUtils::incrementByteOffset(unsigned _byteSize, unsigned _byteOffsetPos
|
||||
m_context << Instruction::MUL;
|
||||
else
|
||||
m_context
|
||||
<< dupInstruction(_byteOffsetPosition + 1) << Instruction::MUL
|
||||
<< swapInstruction(_byteOffsetPosition) << Instruction::POP;
|
||||
<< AssemblyItem(AssemblyItemType::Dup, _byteOffsetPosition + 1) << Instruction::MUL
|
||||
<< AssemblyItem(AssemblyItemType::Swap, _byteOffsetPosition) << Instruction::POP;
|
||||
}
|
||||
|
@ -419,17 +419,17 @@ void CompilerContext::appendInlineAssembly(
|
||||
size_t stackDiff = static_cast<size_t>(_assembly.stackHeight()) - startStackHeight + stackDepth;
|
||||
if (_context == yul::IdentifierContext::LValue)
|
||||
stackDiff -= 1;
|
||||
if (stackDiff < 1 || stackDiff > 16)
|
||||
if (stackDiff < 1 || stackDiff > _assembly.maxDup() || stackDiff > _assembly.maxSwap())
|
||||
BOOST_THROW_EXCEPTION(
|
||||
StackTooDeepError() <<
|
||||
errinfo_sourceLocation(nativeLocationOf(_identifier)) <<
|
||||
util::errinfo_comment(util::stackTooDeepString)
|
||||
);
|
||||
if (_context == yul::IdentifierContext::RValue)
|
||||
_assembly.appendInstruction(dupInstruction(static_cast<unsigned>(stackDiff)));
|
||||
_assembly.appendDup(static_cast<unsigned>(stackDiff));
|
||||
else
|
||||
{
|
||||
_assembly.appendInstruction(swapInstruction(static_cast<unsigned>(stackDiff)));
|
||||
_assembly.appendSwap(static_cast<unsigned>(stackDiff));
|
||||
_assembly.appendInstruction(Instruction::POP);
|
||||
}
|
||||
};
|
||||
|
@ -251,7 +251,15 @@ public:
|
||||
|
||||
/// Append elements to the current instruction list and adjust @a m_stackOffset.
|
||||
CompilerContext& operator<<(evmasm::AssemblyItem const& _item) { m_asm->append(_item); return *this; }
|
||||
CompilerContext& operator<<(evmasm::Instruction _instruction) { m_asm->append(_instruction); return *this; }
|
||||
CompilerContext& operator<<(evmasm::Instruction _instruction) {
|
||||
if (_instruction >= evmasm::Instruction::SWAP1 && _instruction <= evmasm::Instruction::SWAP16)
|
||||
m_asm->append(evmasm::AssemblyItem(evmasm::Swap, static_cast<unsigned>(_instruction) - static_cast<unsigned>(evmasm::Instruction::SWAP1) + 1));
|
||||
else if (_instruction >= evmasm::Instruction::DUP1 && _instruction <= evmasm::Instruction::DUP16)
|
||||
m_asm->append(evmasm::AssemblyItem(evmasm::Dup, static_cast<unsigned>(_instruction) - static_cast<unsigned>(evmasm::Instruction::DUP1) + 1));
|
||||
else
|
||||
m_asm->append(_instruction);
|
||||
return *this;
|
||||
}
|
||||
CompilerContext& operator<<(u256 const& _value) { m_asm->append(_value); return *this; }
|
||||
CompilerContext& operator<<(bytes const& _data) { m_asm->append(_data); return *this; }
|
||||
|
||||
|
@ -540,9 +540,9 @@ void CompilerUtils::encodeToMemory(
|
||||
StackTooDeepError,
|
||||
util::stackTooDeepString
|
||||
);
|
||||
m_context << dupInstruction(2 + dynPointers) << Instruction::DUP2;
|
||||
m_context << AssemblyItem(AssemblyItemType::Dup, 2 + dynPointers) << Instruction::DUP2;
|
||||
m_context << Instruction::SUB;
|
||||
m_context << dupInstruction(2 + dynPointers - thisDynPointer);
|
||||
m_context << AssemblyItem(AssemblyItemType::Dup, 2 + dynPointers - thisDynPointer);
|
||||
m_context << Instruction::MSTORE;
|
||||
// stack: ... <end_of_mem>
|
||||
if (_givenTypes[i]->category() == Type::Category::StringLiteral)
|
||||
@ -583,13 +583,13 @@ void CompilerUtils::encodeToMemory(
|
||||
copyToStackTop(argSize - stackPos + dynPointers + 2, arrayType->sizeOnStack());
|
||||
// stack: ... <end_of_mem> <value...>
|
||||
// copy length to memory
|
||||
m_context << dupInstruction(1 + arrayType->sizeOnStack());
|
||||
m_context << AssemblyItem(AssemblyItemType::Dup, 1 + arrayType->sizeOnStack());
|
||||
ArrayUtils(m_context).retrieveLength(*arrayType, 1);
|
||||
// stack: ... <end_of_mem> <value...> <end_of_mem'> <length>
|
||||
storeInMemoryDynamic(*TypeProvider::uint256(), true);
|
||||
// stack: ... <end_of_mem> <value...> <end_of_mem''>
|
||||
// copy the new memory pointer
|
||||
m_context << swapInstruction(arrayType->sizeOnStack() + 1) << Instruction::POP;
|
||||
m_context << AssemblyItem(AssemblyItemType::Swap, arrayType->sizeOnStack() + 1) << Instruction::POP;
|
||||
// stack: ... <end_of_mem''> <value...>
|
||||
// copy data part
|
||||
ArrayUtils(m_context).copyArrayToMemory(*arrayType, _padToWordBoundaries);
|
||||
@ -602,7 +602,7 @@ void CompilerUtils::encodeToMemory(
|
||||
}
|
||||
|
||||
// remove unneeded stack elements (and retain memory pointer)
|
||||
m_context << swapInstruction(argSize + dynPointers + 1);
|
||||
m_context << AssemblyItem(AssemblyItemType::Swap, argSize + dynPointers + 1);
|
||||
popStackSlots(argSize + dynPointers + 1);
|
||||
}
|
||||
|
||||
@ -1270,7 +1270,7 @@ void CompilerUtils::convertType(
|
||||
// Move it back into its place.
|
||||
for (unsigned j = 0; j < min(sourceSize, targetSize); ++j)
|
||||
m_context <<
|
||||
swapInstruction(depth + targetSize - sourceSize) <<
|
||||
AssemblyItem(AssemblyItemType::Swap, depth + targetSize - sourceSize) <<
|
||||
Instruction::POP;
|
||||
// Value shrank
|
||||
for (unsigned j = targetSize; j < sourceSize; ++j)
|
||||
@ -1422,7 +1422,7 @@ void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
|
||||
util::errinfo_comment(util::stackTooDeepString)
|
||||
);
|
||||
for (unsigned i = 0; i < size; ++i)
|
||||
m_context << swapInstruction(stackPosition - size + 1) << Instruction::POP;
|
||||
m_context << AssemblyItem(AssemblyItemType::Swap, stackPosition - size + 1) << Instruction::POP;
|
||||
}
|
||||
|
||||
void CompilerUtils::copyToStackTop(unsigned _stackDepth, unsigned _itemSize)
|
||||
@ -1433,7 +1433,7 @@ void CompilerUtils::copyToStackTop(unsigned _stackDepth, unsigned _itemSize)
|
||||
util::stackTooDeepString
|
||||
);
|
||||
for (unsigned i = 0; i < _itemSize; ++i)
|
||||
m_context << dupInstruction(_stackDepth);
|
||||
m_context << AssemblyItem(AssemblyItemType::Dup, _stackDepth);
|
||||
}
|
||||
|
||||
void CompilerUtils::moveToStackTop(unsigned _stackDepth, unsigned _itemSize)
|
||||
@ -1459,7 +1459,7 @@ void CompilerUtils::rotateStackUp(unsigned _items)
|
||||
util::stackTooDeepString
|
||||
);
|
||||
for (unsigned i = 1; i < _items; ++i)
|
||||
m_context << swapInstruction(_items - i);
|
||||
m_context << AssemblyItem(AssemblyItemType::Swap, _items - i);
|
||||
}
|
||||
|
||||
void CompilerUtils::rotateStackDown(unsigned _items)
|
||||
@ -1470,7 +1470,7 @@ void CompilerUtils::rotateStackDown(unsigned _items)
|
||||
util::stackTooDeepString
|
||||
);
|
||||
for (unsigned i = 1; i < _items; ++i)
|
||||
m_context << swapInstruction(i);
|
||||
m_context << AssemblyItem(AssemblyItemType::Swap, i);
|
||||
}
|
||||
|
||||
void CompilerUtils::popStackElement(Type const& _type)
|
||||
|
@ -366,7 +366,7 @@ void ContractCompiler::appendInternalSelector(
|
||||
{
|
||||
size_t pivotIndex = _ids.size() / 2;
|
||||
FixedHash<4> pivot{_ids.at(pivotIndex)};
|
||||
m_context << dupInstruction(1) << u256(FixedHash<4>::Arith(pivot)) << Instruction::GT;
|
||||
m_context << AssemblyItem(AssemblyItemType::Dup, 1) << u256(FixedHash<4>::Arith(pivot)) << Instruction::GT;
|
||||
evmasm::AssemblyItem lessTag{m_context.appendConditionalJump()};
|
||||
// Here, we have funid >= pivot
|
||||
vector<FixedHash<4>> larger{_ids.begin() + static_cast<ptrdiff_t>(pivotIndex), _ids.end()};
|
||||
@ -380,7 +380,7 @@ void ContractCompiler::appendInternalSelector(
|
||||
{
|
||||
for (auto const& id: _ids)
|
||||
{
|
||||
m_context << dupInstruction(1) << u256(FixedHash<4>::Arith(id)) << Instruction::EQ;
|
||||
m_context << AssemblyItem(AssemblyItemType::Dup, 1) << u256(FixedHash<4>::Arith(id)) << Instruction::EQ;
|
||||
m_context.appendConditionalJumpTo(_entryPoints.at(id));
|
||||
}
|
||||
m_context.appendJumpTo(_notFoundTag);
|
||||
@ -519,7 +519,7 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac
|
||||
{
|
||||
// If the function is not a view function and is called without DELEGATECALL,
|
||||
// we revert.
|
||||
m_context << dupInstruction(2);
|
||||
m_context << AssemblyItem(AssemblyItemType::Dup, 2);
|
||||
m_context.appendConditionalRevert(false, "Non-view function of library called without DELEGATECALL");
|
||||
}
|
||||
m_context.setStackOffset(0);
|
||||
@ -669,7 +669,7 @@ bool ContractCompiler::visit(FunctionDefinition const& _function)
|
||||
for (size_t i = 0; i < c_returnValuesSize; ++i)
|
||||
stackLayout.push_back(static_cast<int>(i));
|
||||
|
||||
if (stackLayout.size() > 17)
|
||||
if (stackLayout.size() > m_context.assembly().maxSwap() + 1)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
StackTooDeepError() <<
|
||||
errinfo_sourceLocation(_function.location()) <<
|
||||
@ -683,7 +683,7 @@ bool ContractCompiler::visit(FunctionDefinition const& _function)
|
||||
}
|
||||
else
|
||||
{
|
||||
m_context << swapInstruction(static_cast<unsigned>(stackLayout.size()) - static_cast<unsigned>(stackLayout.back()) - 1u);
|
||||
m_context << AssemblyItem(AssemblyItemType::Swap, static_cast<unsigned>(stackLayout.size()) - static_cast<unsigned>(stackLayout.back()) - 1u);
|
||||
swap(stackLayout[static_cast<size_t>(stackLayout.back())], stackLayout.back());
|
||||
}
|
||||
for (size_t i = 0; i < stackLayout.size(); ++i)
|
||||
@ -839,13 +839,13 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
|
||||
}
|
||||
else
|
||||
solAssert(variable->type()->sizeOnStack() == 1, "");
|
||||
if (stackDiff < 1 || stackDiff > 16)
|
||||
if (stackDiff < 1 || stackDiff > _assembly.maxDup())
|
||||
BOOST_THROW_EXCEPTION(
|
||||
StackTooDeepError() <<
|
||||
errinfo_sourceLocation(_inlineAssembly.location()) <<
|
||||
util::errinfo_comment(util::stackTooDeepString)
|
||||
);
|
||||
_assembly.appendInstruction(dupInstruction(stackDiff));
|
||||
_assembly.appendDup(stackDiff);
|
||||
}
|
||||
else
|
||||
solAssert(false, "");
|
||||
@ -913,13 +913,13 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
|
||||
else
|
||||
solAssert(suffix.empty(), "");
|
||||
|
||||
if (stackDiff > 16 || stackDiff < 1)
|
||||
if (stackDiff > _assembly.maxSwap() || stackDiff < 1)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
StackTooDeepError() <<
|
||||
errinfo_sourceLocation(_inlineAssembly.location()) <<
|
||||
util::errinfo_comment(util::stackTooDeepString)
|
||||
);
|
||||
_assembly.appendInstruction(swapInstruction(stackDiff));
|
||||
_assembly.appendSwap(stackDiff);
|
||||
_assembly.appendInstruction(Instruction::POP);
|
||||
}
|
||||
};
|
||||
|
@ -118,7 +118,7 @@ void ExpressionCompiler::appendConstStateVariableAccessor(VariableDeclaration co
|
||||
acceptAndConvert(*_varDecl.value(), *_varDecl.annotation().type);
|
||||
|
||||
// append return
|
||||
m_context << dupInstruction(_varDecl.annotation().type->sizeOnStack() + 1);
|
||||
m_context << AssemblyItem(AssemblyItemType::Dup, _varDecl.annotation().type->sizeOnStack() + 1);
|
||||
m_context.appendJump(evmasm::AssemblyItem::JumpType::OutOfFunction);
|
||||
}
|
||||
|
||||
@ -220,9 +220,9 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
|
||||
m_context << Instruction::SWAP2 << Instruction::POP << Instruction::SWAP1;
|
||||
else if (paramTypes.size() >= 2)
|
||||
{
|
||||
m_context << swapInstruction(static_cast<unsigned>(paramTypes.size()));
|
||||
m_context << AssemblyItem(AssemblyItemType::Swap, static_cast<unsigned>(paramTypes.size()));
|
||||
m_context << Instruction::POP;
|
||||
m_context << swapInstruction(static_cast<unsigned>(paramTypes.size()));
|
||||
m_context << AssemblyItem(AssemblyItemType::Swap, static_cast<unsigned>(paramTypes.size()));
|
||||
utils().popStackSlots(paramTypes.size() - 1);
|
||||
}
|
||||
unsigned retSizeOnStack = 0;
|
||||
@ -265,13 +265,13 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
|
||||
retSizeOnStack = returnTypes.front()->sizeOnStack();
|
||||
}
|
||||
solAssert(retSizeOnStack == utils().sizeOnStack(returnTypes), "");
|
||||
if (retSizeOnStack > 15)
|
||||
if (retSizeOnStack + 1 > m_context.assembly().maxDup())
|
||||
BOOST_THROW_EXCEPTION(
|
||||
StackTooDeepError() <<
|
||||
errinfo_sourceLocation(_varDecl.location()) <<
|
||||
util::errinfo_comment(util::stackTooDeepString)
|
||||
);
|
||||
m_context << dupInstruction(retSizeOnStack + 1);
|
||||
m_context << AssemblyItem(AssemblyItemType::Dup, retSizeOnStack + 1);
|
||||
m_context.appendJump(evmasm::AssemblyItem::JumpType::OutOfFunction);
|
||||
}
|
||||
|
||||
@ -347,7 +347,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
|
||||
}
|
||||
if (lvalueSize > 0)
|
||||
{
|
||||
if (itemSize + lvalueSize > 16)
|
||||
if (itemSize + lvalueSize > m_context.assembly().maxSwap())
|
||||
BOOST_THROW_EXCEPTION(
|
||||
StackTooDeepError() <<
|
||||
errinfo_sourceLocation(_assignment.location()) <<
|
||||
@ -355,7 +355,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
|
||||
);
|
||||
// value [lvalue_ref] updated_value
|
||||
for (unsigned i = 0; i < itemSize; ++i)
|
||||
m_context << swapInstruction(itemSize + lvalueSize) << Instruction::POP;
|
||||
m_context << AssemblyItem(AssemblyItemType::Swap, itemSize + lvalueSize) << Instruction::POP;
|
||||
}
|
||||
m_currentLValue->storeValue(*_assignment.annotation().type, _assignment.location());
|
||||
}
|
||||
@ -447,7 +447,7 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
|
||||
m_context << Instruction::DUP1;
|
||||
if (m_currentLValue->sizeOnStack() > 0)
|
||||
for (unsigned i = 1 + m_currentLValue->sizeOnStack(); i > 0; --i)
|
||||
m_context << swapInstruction(i);
|
||||
m_context << AssemblyItem(AssemblyItemType::Swap, i);
|
||||
}
|
||||
if (_unaryOperation.getOperator() == Token::Inc)
|
||||
{
|
||||
@ -472,7 +472,7 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
|
||||
// Stack for prefix: [ref...] (*ref)+-1
|
||||
// Stack for postfix: *ref [ref...] (*ref)+-1
|
||||
for (unsigned i = m_currentLValue->sizeOnStack(); i > 0; --i)
|
||||
m_context << swapInstruction(i);
|
||||
m_context << AssemblyItem(AssemblyItemType::Swap, i);
|
||||
m_currentLValue->storeValue(
|
||||
*_unaryOperation.annotation().type, _unaryOperation.location(),
|
||||
!_unaryOperation.isPrefixOperation());
|
||||
@ -715,7 +715,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
|
||||
if (function.saltSet())
|
||||
{
|
||||
m_context << dupInstruction(2 + (function.valueSet() ? 1 : 0));
|
||||
m_context << AssemblyItem(AssemblyItemType::Dup, 2 + (function.valueSet() ? 1 : 0));
|
||||
m_context << Instruction::SWAP1;
|
||||
}
|
||||
|
||||
@ -724,7 +724,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
|
||||
// now: [salt], [value], [salt], size, offset
|
||||
if (function.valueSet())
|
||||
m_context << dupInstruction(3 + (function.saltSet() ? 1 : 0));
|
||||
m_context << AssemblyItem(AssemblyItemType::Dup, 3 + (function.saltSet() ? 1 : 0));
|
||||
else
|
||||
m_context << u256(0);
|
||||
|
||||
@ -737,9 +737,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
// now: [salt], [value], address
|
||||
|
||||
if (function.valueSet())
|
||||
m_context << swapInstruction(1) << Instruction::POP;
|
||||
m_context << AssemblyItem(AssemblyItemType::Swap, 1) << Instruction::POP;
|
||||
if (function.saltSet())
|
||||
m_context << swapInstruction(1) << Instruction::POP;
|
||||
m_context << AssemblyItem(AssemblyItemType::Swap, 1) << Instruction::POP;
|
||||
|
||||
// Check if zero (reverted)
|
||||
m_context << Instruction::DUP1 << Instruction::ISZERO;
|
||||
@ -765,7 +765,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
// Its values of gasSet and valueSet is equal to the original function's though.
|
||||
unsigned stackDepth = (function.gasSet() ? 1u : 0u) + (function.valueSet() ? 1u : 0u);
|
||||
if (stackDepth > 0)
|
||||
m_context << swapInstruction(stackDepth);
|
||||
m_context << AssemblyItem(AssemblyItemType::Swap, stackDepth);
|
||||
if (function.gasSet())
|
||||
m_context << Instruction::POP;
|
||||
break;
|
||||
@ -1022,7 +1022,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
};
|
||||
m_context << contractAddresses.at(function.kind());
|
||||
for (unsigned i = function.sizeOnStack(); i > 0; --i)
|
||||
m_context << swapInstruction(i);
|
||||
m_context << AssemblyItem(AssemblyItemType::Swap, i);
|
||||
solAssert(!_functionCall.annotation().tryCall, "");
|
||||
appendExternalFunctionCall(function, arguments, false);
|
||||
break;
|
||||
@ -2648,7 +2648,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
|
||||
utils().fetchFreeMemoryPointer();
|
||||
if (!_functionType.isBareCall())
|
||||
{
|
||||
m_context << dupInstruction(2 + gasValueSize + CompilerUtils::sizeOnStack(argumentTypes));
|
||||
m_context << AssemblyItem(AssemblyItemType::Dup, 2 + gasValueSize + CompilerUtils::sizeOnStack(argumentTypes));
|
||||
utils().storeInMemoryDynamic(IntegerType(8 * CompilerUtils::dataStartOffset), false);
|
||||
}
|
||||
|
||||
@ -2703,10 +2703,10 @@ void ExpressionCompiler::appendExternalFunctionCall(
|
||||
else if (useStaticCall)
|
||||
solAssert(!_functionType.valueSet(), "Value set for staticcall");
|
||||
else if (_functionType.valueSet())
|
||||
m_context << dupInstruction(m_context.baseToCurrentStackOffset(valueStackPos));
|
||||
m_context << AssemblyItem(AssemblyItemType::Dup, m_context.baseToCurrentStackOffset(valueStackPos));
|
||||
else
|
||||
m_context << u256(0);
|
||||
m_context << dupInstruction(m_context.baseToCurrentStackOffset(contractStackPos));
|
||||
m_context << AssemblyItem(AssemblyItemType::Dup, m_context.baseToCurrentStackOffset(contractStackPos));
|
||||
|
||||
bool existenceChecked = false;
|
||||
// Check the target contract exists (has code) for non-low-level calls.
|
||||
@ -2730,7 +2730,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
|
||||
}
|
||||
|
||||
if (_functionType.gasSet())
|
||||
m_context << dupInstruction(m_context.baseToCurrentStackOffset(gasStackPos));
|
||||
m_context << AssemblyItem(AssemblyItemType::Dup, m_context.baseToCurrentStackOffset(gasStackPos));
|
||||
else if (m_context.evmVersion().canOverchargeGasForCall())
|
||||
// Send all gas (requires tangerine whistle EVM)
|
||||
m_context << Instruction::GAS;
|
||||
@ -2768,7 +2768,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
|
||||
m_context.appendConditionalRevert(true);
|
||||
}
|
||||
else
|
||||
m_context << swapInstruction(remainsSize);
|
||||
m_context << AssemblyItem(AssemblyItemType::Swap, remainsSize);
|
||||
utils().popStackSlots(remainsSize);
|
||||
|
||||
// Only success flag is remaining on stack.
|
||||
|
@ -56,7 +56,7 @@ void StackVariable::retrieveValue(SourceLocation const& _location, bool) const
|
||||
);
|
||||
solAssert(stackPos + 1 >= m_size, "Size and stack pos mismatch.");
|
||||
for (unsigned i = 0; i < m_size; ++i)
|
||||
m_context << dupInstruction(stackPos + 1);
|
||||
m_context << AssemblyItem(AssemblyItemType::Dup, stackPos + 1);
|
||||
}
|
||||
|
||||
void StackVariable::storeValue(Type const&, SourceLocation const& _location, bool _move) const
|
||||
@ -70,7 +70,7 @@ void StackVariable::storeValue(Type const&, SourceLocation const& _location, boo
|
||||
);
|
||||
else if (stackDiff > 0)
|
||||
for (unsigned i = 0; i < m_size; ++i)
|
||||
m_context << swapInstruction(stackDiff) << Instruction::POP;
|
||||
m_context << AssemblyItem(AssemblyItemType::Swap, stackDiff) << Instruction::POP;
|
||||
if (!_move)
|
||||
retrieveValue(_location);
|
||||
}
|
||||
@ -421,7 +421,7 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
|
||||
}
|
||||
unsigned stackSize = sourceMemberType->sizeOnStack();
|
||||
pair<u256, unsigned> const& offsets = structType.storageOffsetsOfMember(member.name);
|
||||
m_context << dupInstruction(1 + stackSize) << offsets.first << Instruction::ADD;
|
||||
m_context << AssemblyItem(AssemblyItemType::Dup, 1 + stackSize) << offsets.first << Instruction::ADD;
|
||||
m_context << u256(offsets.second);
|
||||
// stack: source_ref target_ref target_off source_value... target_member_ref target_member_byte_off
|
||||
StorageItem(m_context, *memberType).storeValue(*sourceMemberType, _location, true);
|
||||
|
@ -99,7 +99,7 @@ GasEstimator::GasConsumption GasEstimator::functionalEstimation(
|
||||
AssemblyItem invalidTag(PushTag, u256(-0x10));
|
||||
state->feedItem(invalidTag, true);
|
||||
if (parametersSize > 0)
|
||||
state->feedItem(swapInstruction(parametersSize));
|
||||
state->feedItem(AssemblyItem(AssemblyItemType::Swap, parametersSize));
|
||||
|
||||
return PathGasMeter::estimateMax(_items, m_evmVersion, _offset, state);
|
||||
}
|
||||
|
@ -67,6 +67,13 @@ public:
|
||||
virtual void setStackHeight(int height) = 0;
|
||||
/// Append an EVM instruction.
|
||||
virtual void appendInstruction(evmasm::Instruction _instruction) = 0;
|
||||
/// Append a swap.
|
||||
virtual void appendSwap(unsigned _height) = 0;
|
||||
/// Append a dup.
|
||||
virtual void appendDup(unsigned _height) = 0;
|
||||
|
||||
virtual unsigned maxSwap() const = 0;
|
||||
virtual unsigned maxDup() const = 0;
|
||||
/// Append a constant.
|
||||
virtual void appendConstant(u256 const& _constant) = 0;
|
||||
/// Append a label.
|
||||
|
@ -187,7 +187,7 @@ void CodeTransform::operator()(VariableDeclaration const& _varDecl)
|
||||
m_unusedStackSlots.erase(it);
|
||||
m_context->variableStackHeights[&var] = slot;
|
||||
if (size_t heightDiff = variableHeightDiff(var, varName, true))
|
||||
m_assembly.appendInstruction(evmasm::swapInstruction(static_cast<unsigned>(heightDiff - 1)));
|
||||
m_assembly.appendSwap(static_cast<unsigned>(heightDiff - 1));
|
||||
m_assembly.appendInstruction(evmasm::Instruction::POP);
|
||||
break;
|
||||
}
|
||||
@ -274,7 +274,7 @@ void CodeTransform::operator()(Identifier const& _identifier)
|
||||
// TODO: opportunity for optimization: Do not DUP if this is the last reference
|
||||
// to the top most element of the stack
|
||||
if (size_t heightDiff = variableHeightDiff(_var, _identifier.name, false))
|
||||
m_assembly.appendInstruction(evmasm::dupInstruction(static_cast<unsigned>(heightDiff)));
|
||||
m_assembly.appendDup(static_cast<unsigned>(heightDiff));
|
||||
else
|
||||
// Store something to balance the stack
|
||||
m_assembly.appendConstant(u256(0));
|
||||
@ -328,7 +328,7 @@ void CodeTransform::operator()(Switch const& _switch)
|
||||
AbstractAssembly::LabelID bodyLabel = m_assembly.newLabelId();
|
||||
caseBodies[&c] = bodyLabel;
|
||||
yulAssert(m_assembly.stackHeight() == expressionHeight + 1, "");
|
||||
m_assembly.appendInstruction(evmasm::dupInstruction(2));
|
||||
m_assembly.appendDup(2);
|
||||
m_assembly.appendInstruction(evmasm::Instruction::EQ);
|
||||
m_assembly.appendJumpToIf(bodyLabel);
|
||||
}
|
||||
@ -478,7 +478,7 @@ void CodeTransform::operator()(FunctionDefinition const& _function)
|
||||
}
|
||||
else
|
||||
{
|
||||
m_assembly.appendInstruction(evmasm::swapInstruction(static_cast<unsigned>(stackLayout.size()) - static_cast<unsigned>(stackLayout.back()) - 1u));
|
||||
m_assembly.appendSwap(static_cast<unsigned>(stackLayout.size()) - static_cast<unsigned>(stackLayout.back()) - 1u);
|
||||
swap(stackLayout[static_cast<size_t>(stackLayout.back())], stackLayout.back());
|
||||
}
|
||||
for (size_t i = 0; i < stackLayout.size(); ++i)
|
||||
@ -758,7 +758,7 @@ void CodeTransform::generateAssignment(Identifier const& _variableName)
|
||||
{
|
||||
Scope::Variable const& _var = std::get<Scope::Variable>(*var);
|
||||
if (size_t heightDiff = variableHeightDiff(_var, _variableName.name, true))
|
||||
m_assembly.appendInstruction(evmasm::swapInstruction(static_cast<unsigned>(heightDiff - 1)));
|
||||
m_assembly.appendSwap(static_cast<unsigned>(heightDiff - 1));
|
||||
m_assembly.appendInstruction(evmasm::Instruction::POP);
|
||||
decreaseReference(_variableName.name, _var);
|
||||
}
|
||||
|
@ -145,9 +145,16 @@ map<YulString, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion _evmVe
|
||||
string name = toLower(instr.first);
|
||||
auto const opcode = instr.second;
|
||||
|
||||
auto isSwapInstruction = [](auto i) {
|
||||
return (i >= evmasm::Instruction::SWAP1 && i <= evmasm::Instruction::SWAP16) || i == evmasm::Instruction::SWAP_N;
|
||||
};
|
||||
auto isDupInstruction = [](auto i) {
|
||||
return (i >= evmasm::Instruction::DUP1 && i <= evmasm::Instruction::DUP16) || i == evmasm::Instruction::DUP_N;
|
||||
};
|
||||
|
||||
if (
|
||||
!evmasm::isDupInstruction(opcode) &&
|
||||
!evmasm::isSwapInstruction(opcode) &&
|
||||
!isDupInstruction(opcode) &&
|
||||
!isSwapInstruction(opcode) &&
|
||||
!evmasm::isPushInstruction(opcode) &&
|
||||
opcode != evmasm::Instruction::JUMP &&
|
||||
opcode != evmasm::Instruction::JUMPI &&
|
||||
|
@ -64,6 +64,28 @@ void EthAssemblyAdapter::appendInstruction(evmasm::Instruction _instruction)
|
||||
m_assembly.append(_instruction);
|
||||
}
|
||||
|
||||
|
||||
void EthAssemblyAdapter::appendSwap(unsigned _height)
|
||||
{
|
||||
m_assembly.append(evmasm::AssemblyItem(evmasm::AssemblyItemType::Swap, _height));
|
||||
}
|
||||
|
||||
|
||||
void EthAssemblyAdapter::appendDup(unsigned _height)
|
||||
{
|
||||
m_assembly.append(evmasm::AssemblyItem(evmasm::AssemblyItemType::Dup, _height));
|
||||
}
|
||||
|
||||
unsigned EthAssemblyAdapter::maxDup() const
|
||||
{
|
||||
return m_assembly.maxDup();
|
||||
}
|
||||
|
||||
unsigned EthAssemblyAdapter::maxSwap() const
|
||||
{
|
||||
return m_assembly.maxSwap();
|
||||
}
|
||||
|
||||
void EthAssemblyAdapter::appendConstant(u256 const& _constant)
|
||||
{
|
||||
m_assembly.append(_constant);
|
||||
|
@ -44,6 +44,10 @@ public:
|
||||
int stackHeight() const override;
|
||||
void setStackHeight(int height) override;
|
||||
void appendInstruction(evmasm::Instruction _instruction) override;
|
||||
void appendDup(unsigned _height) override;
|
||||
void appendSwap(unsigned _height) override;
|
||||
unsigned maxDup() const override;
|
||||
unsigned maxSwap() const override;
|
||||
void appendConstant(u256 const& _constant) override;
|
||||
void appendLabel(LabelID _labelId) override;
|
||||
void appendLabelReference(LabelID _labelId) override;
|
||||
|
@ -40,6 +40,22 @@ void NoOutputAssembly::appendInstruction(evmasm::Instruction _instr)
|
||||
m_stackHeight += instructionInfo(_instr).ret - instructionInfo(_instr).args;
|
||||
}
|
||||
|
||||
void NoOutputAssembly::appendSwap(unsigned)
|
||||
{
|
||||
}
|
||||
|
||||
void NoOutputAssembly::appendDup(unsigned)
|
||||
{
|
||||
m_stackHeight++;
|
||||
}
|
||||
unsigned NoOutputAssembly::maxDup() const
|
||||
{
|
||||
return 16;
|
||||
}
|
||||
unsigned NoOutputAssembly::maxSwap() const
|
||||
{
|
||||
return 16;
|
||||
}
|
||||
void NoOutputAssembly::appendConstant(u256 const&)
|
||||
{
|
||||
appendInstruction(evmasm::pushInstruction(1));
|
||||
|
@ -52,6 +52,10 @@ public:
|
||||
int stackHeight() const override { return m_stackHeight; }
|
||||
void setStackHeight(int height) override { m_stackHeight = height; }
|
||||
void appendInstruction(evmasm::Instruction _instruction) override;
|
||||
void appendSwap(unsigned _height) override;
|
||||
void appendDup(unsigned _height) override;
|
||||
unsigned maxDup() const override;
|
||||
unsigned maxSwap() const override;
|
||||
void appendConstant(u256 const& _constant) override;
|
||||
void appendLabel(LabelID _labelId) override;
|
||||
void appendLabelReference(LabelID _labelId) override;
|
||||
|
@ -259,11 +259,11 @@ void OptimizedEVMCodeTransform::createStackLayout(std::shared_ptr<DebugData cons
|
||||
{
|
||||
yulAssert(static_cast<int>(m_stack.size()) == m_assembly.stackHeight(), "");
|
||||
yulAssert(_i > 0 && _i < m_stack.size(), "");
|
||||
if (_i <= 16)
|
||||
m_assembly.appendInstruction(evmasm::swapInstruction(_i));
|
||||
if (_i <= m_assembly.maxSwap())
|
||||
m_assembly.appendSwap(_i);
|
||||
else
|
||||
{
|
||||
int deficit = static_cast<int>(_i) - 16;
|
||||
int deficit = static_cast<int>(_i) - static_cast<int>(m_assembly.maxSwap());
|
||||
StackSlot const& deepSlot = m_stack.at(m_stack.size() - _i - 1);
|
||||
YulString varNameDeep = slotVariableName(deepSlot);
|
||||
YulString varNameTop = slotVariableName(m_stack.back());
|
||||
@ -288,18 +288,18 @@ void OptimizedEVMCodeTransform::createStackLayout(std::shared_ptr<DebugData cons
|
||||
// Dup the slot, if already on stack and reachable.
|
||||
if (auto depth = util::findOffset(m_stack | ranges::views::reverse, _slot))
|
||||
{
|
||||
if (*depth < 16)
|
||||
if (*depth < m_assembly.maxDup())
|
||||
{
|
||||
m_assembly.appendInstruction(evmasm::dupInstruction(static_cast<unsigned>(*depth + 1)));
|
||||
m_assembly.appendDup(static_cast<unsigned>(*depth + 1));
|
||||
return;
|
||||
}
|
||||
else if (!canBeFreelyGenerated(_slot))
|
||||
{
|
||||
int deficit = static_cast<int>(*depth - 15);
|
||||
int deficit = static_cast<int>(*depth) - static_cast<int>(m_assembly.maxDup() - 1);
|
||||
YulString varName = slotVariableName(_slot);
|
||||
string msg =
|
||||
(varName.empty() ? "Slot " + stackSlotToString(_slot) : "Variable " + varName.str())
|
||||
+ " is " + to_string(*depth - 15) + " too deep in the stack " + stackToString(m_stack);
|
||||
+ " is " + to_string(*depth - (m_assembly.maxDup() - 1)) + " too deep in the stack " + stackToString(m_stack);
|
||||
m_stackErrors.emplace_back(StackTooDeepError(
|
||||
m_currentFunctionInfo ? m_currentFunctionInfo->function.name : YulString{},
|
||||
varName,
|
||||
|
@ -740,14 +740,15 @@ void StackLayoutGenerator::fillInJunk(CFG::BasicBlock const& _block, CFG::Functi
|
||||
});
|
||||
};
|
||||
/// @returns the number of operations required to transform @a _source to @a _target.
|
||||
auto evaluateTransform = [](Stack _source, Stack const& _target) -> size_t {
|
||||
auto evaluateTransform = [&](Stack _source, Stack const& _target) -> size_t {
|
||||
size_t opGas = 0;
|
||||
auto swap = [&](unsigned _swapDepth)
|
||||
{
|
||||
if (_swapDepth > 16)
|
||||
if (_swapDepth > maxSwap())
|
||||
opGas += 1000;
|
||||
else
|
||||
opGas += evmasm::GasMeter::runGas(evmasm::swapInstruction(_swapDepth));
|
||||
// TODO
|
||||
opGas += evmasm::GasMeter::runGas(evmasm::Instruction::SWAP1);
|
||||
};
|
||||
auto dupOrPush = [&](StackSlot const& _slot)
|
||||
{
|
||||
@ -757,8 +758,9 @@ void StackLayoutGenerator::fillInJunk(CFG::BasicBlock const& _block, CFG::Functi
|
||||
{
|
||||
auto depth = util::findOffset(_source | ranges::views::reverse, _slot);
|
||||
yulAssert(depth);
|
||||
if (*depth < 16)
|
||||
opGas += evmasm::GasMeter::runGas(evmasm::dupInstruction(static_cast<unsigned>(*depth + 1)));
|
||||
if (*depth < maxDup())
|
||||
// TODO
|
||||
opGas += evmasm::GasMeter::runGas(evmasm::Instruction::DUP1);
|
||||
else
|
||||
opGas += 1000;
|
||||
}
|
||||
|
@ -115,6 +115,10 @@ private:
|
||||
void fillInJunk(CFG::BasicBlock const& _block, CFG::FunctionInfo const* _functionInfo = nullptr);
|
||||
|
||||
StackLayout& m_layout;
|
||||
|
||||
// TODO
|
||||
unsigned maxSwap() const { return 16; }
|
||||
unsigned maxDup() const { return 16; }
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -425,6 +425,7 @@ u256 EVMInstructionInterpreter::eval(
|
||||
case Instruction::DUP14:
|
||||
case Instruction::DUP15:
|
||||
case Instruction::DUP16:
|
||||
case Instruction::DUP_N:
|
||||
case Instruction::SWAP1:
|
||||
case Instruction::SWAP2:
|
||||
case Instruction::SWAP3:
|
||||
@ -441,6 +442,7 @@ u256 EVMInstructionInterpreter::eval(
|
||||
case Instruction::SWAP14:
|
||||
case Instruction::SWAP15:
|
||||
case Instruction::SWAP16:
|
||||
case Instruction::SWAP_N:
|
||||
{
|
||||
yulAssert(false, "");
|
||||
return 0;
|
||||
|
Loading…
Reference in New Issue
Block a user