initial attempt

This commit is contained in:
Daniel Kirchner 2022-12-02 17:16:10 +01:00
parent 056c4593e3
commit a467bdcb3c
29 changed files with 348 additions and 150 deletions

View File

@ -558,6 +558,26 @@ LinkerObject const& Assembly::assemble() const
case Operation: case Operation:
ret.bytecode.push_back(static_cast<uint8_t>(i.instruction())); ret.bytecode.push_back(static_cast<uint8_t>(i.instruction()));
break; 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: case Push:
{ {
unsigned b = max<unsigned>(1, numberEncodingSize(i.data())); unsigned b = max<unsigned>(1, numberEncodingSize(i.data()));

View File

@ -83,6 +83,23 @@ public:
append(AssemblyItem(std::move(_data), _arguments, _returnVariables)); 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 appendJump() { auto ret = append(newPushTag()); append(Instruction::JUMP); return ret; }
AssemblyItem appendJumpI() { auto ret = append(newPushTag()); append(Instruction::JUMPI); 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; } AssemblyItem appendJump(AssemblyItem const& _tag) { auto ret = append(_tag.pushTag()); append(Instruction::JUMP); return ret; }

View File

@ -123,6 +123,12 @@ size_t AssemblyItem::bytesRequired(size_t _addressLength, Precision _precision)
case Operation: case Operation:
case Tag: // 1 byte for the JUMPDEST case Tag: // 1 byte for the JUMPDEST
return 1; return 1;
case Swap:
case Dup:
if (data() <= 16)
return 1;
else
return 2;
case Push: case Push:
return 1 + max<size_t>(1, numberEncodingSize(data())); return 1 + max<size_t>(1, numberEncodingSize(data()));
case PushSubSize: case PushSubSize:
@ -173,6 +179,10 @@ size_t AssemblyItem::arguments() const
return get<0>(*m_verbatimBytecode); return get<0>(*m_verbatimBytecode);
else if (type() == AssignImmutable) else if (type() == AssignImmutable)
return 2; return 2;
else if (type() == Swap)
return static_cast<size_t>(data());
else if (type() == Dup)
return static_cast<size_t>(data());
else else
return 0; return 0;
} }
@ -183,6 +193,10 @@ size_t AssemblyItem::returnValues() const
{ {
case Operation: case Operation:
return static_cast<size_t>(instructionInfo(instruction()).ret); 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 Push:
case PushTag: case PushTag:
case PushData: case PushData:
@ -210,7 +224,11 @@ bool AssemblyItem::canBeFunctional() const
switch (m_type) switch (m_type)
{ {
case Operation: 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 Push:
case PushTag: case PushTag:
case PushData: case PushData:
@ -254,6 +272,16 @@ string AssemblyItem::toAssemblyText(Assembly const& _assembly) const
text = util::toLower(instructionInfo(instruction()).name); text = util::toLower(instructionInfo(instruction()).name);
break; break;
} }
case Swap:
{
text = "swap" + to_string(data());
break;
}
case Dup:
{
text = "dup" + to_string(data());
break;
}
case Push: case Push:
text = toHex(toCompactBigEndian(data(), 1), util::HexPrefix::Add); text = toHex(toCompactBigEndian(data(), 1), util::HexPrefix::Add);
break; break;
@ -332,6 +360,12 @@ ostream& solidity::evmasm::operator<<(ostream& _out, AssemblyItem const& _item)
if (_item.instruction() == Instruction::JUMP || _item.instruction() == Instruction::JUMPI) if (_item.instruction() == Instruction::JUMP || _item.instruction() == Instruction::JUMPI)
_out << "\t" << _item.getJumpTypeAsString(); _out << "\t" << _item.getJumpTypeAsString();
break; break;
case Swap:
_out << "SWAP" << dec << _item.data();
break;
case Dup:
_out << "DUP" << dec << _item.data();
break;
case Push: case Push:
_out << " PUSH " << hex << _item.data() << dec; _out << " PUSH " << hex << _item.data() << dec;
break; break;

View File

@ -39,6 +39,8 @@ enum AssemblyItemType
{ {
UndefinedItem, UndefinedItem,
Operation, Operation,
Swap,
Dup,
Push, Push,
PushTag, PushTag,
PushSub, PushSub,
@ -70,7 +72,21 @@ public:
m_type(Operation), m_type(Operation),
m_instruction(_i), m_instruction(_i),
m_location(std::move(_location)) 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()): AssemblyItem(AssemblyItemType _type, u256 _data = 0, langutil::SourceLocation _location = langutil::SourceLocation()):
m_type(_type), m_type(_type),
m_location(std::move(_location)) m_location(std::move(_location))
@ -144,10 +160,9 @@ public:
return data() < _other.data(); return data() < _other.data();
} }
/// Shortcut that avoids constructing an AssemblyItem just to perform the comparison.
bool operator==(Instruction _instr) const bool operator==(Instruction _instr) const
{ {
return type() == Operation && instruction() == _instr; return (*this) == AssemblyItem(_instr);
} }
bool operator!=(Instruction _instr) const { return !operator==(_instr); } bool operator!=(Instruction _instr) const { return !operator==(_instr); }

View File

@ -95,7 +95,7 @@ void CommonSubexpressionEliminator::optimizeBreakingItem()
Id condition = m_state.stackElement(m_state.stackHeight() - 1, itemLocation); Id condition = m_state.stackElement(m_state.stackHeight() - 1, itemLocation);
if (classes.knownNonZero(condition)) if (classes.knownNonZero(condition))
{ {
feedItem(AssemblyItem(Instruction::SWAP1, itemLocation), true); feedItem(AssemblyItem(AssemblyItemType::Swap, 1, itemLocation), true);
feedItem(AssemblyItem(Instruction::POP, itemLocation), true); feedItem(AssemblyItem(Instruction::POP, itemLocation), true);
AssemblyItem item(Instruction::JUMP, itemLocation); AssemblyItem item(Instruction::JUMP, itemLocation);
@ -397,7 +397,7 @@ void CSECodeGenerator::generateClassElement(Id _c, bool _allowSequenced)
while (SemanticInformation::isCommutativeOperation(*expr.item) && while (SemanticInformation::isCommutativeOperation(*expr.item) &&
!m_generatedItems.empty() && !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 // this will not append a swap but remove the one that is already there
appendOrRemoveSwap(m_stackHeight - 1, itemLocation); appendOrRemoveSwap(m_stackHeight - 1, itemLocation);
for (size_t i = 0; i < arguments.size(); ++i) 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; int instructionNum = 1 + m_stackHeight - _fromPosition;
assertThrow(instructionNum <= 16, StackTooDeepException, util::stackTooDeepString); assertThrow(instructionNum <= 16, StackTooDeepException, util::stackTooDeepString);
assertThrow(1 <= instructionNum, OptimizerException, "Invalid stack access."); 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_stack[m_stackHeight] = m_stack[_fromPosition];
m_classPositions[m_stack[m_stackHeight]].insert(m_stackHeight); 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; int instructionNum = m_stackHeight - _fromPosition;
assertThrow(instructionNum <= 16, StackTooDeepException, util::stackTooDeepString); assertThrow(instructionNum <= 16, StackTooDeepException, util::stackTooDeepString);
assertThrow(1 <= instructionNum, OptimizerException, "Invalid stack access."); 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]) if (m_stack[m_stackHeight] != m_stack[_fromPosition])
{ {

View File

@ -85,6 +85,12 @@ bigint ConstantOptimisationMethod::simpleRunGas(AssemblyItems const& _items)
for (AssemblyItem const& item: _items) for (AssemblyItem const& item: _items)
if (item.type() == Push) if (item.type() == Push)
gas += GasMeter::runGas(Instruction::PUSH1); 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) else if (item.type() == Operation)
{ {
if (item.instruction() == Instruction::EXP) if (item.instruction() == Instruction::EXP)
@ -167,21 +173,21 @@ AssemblyItems const& CodeCopyMethod::copyRoutine()
// back up memory // back up memory
// mload(0) // mload(0)
Instruction::DUP1, AssemblyItem(Dup, 1),
Instruction::MLOAD, Instruction::MLOAD,
// codecopy(0, <offset>, 32) // codecopy(0, <offset>, 32)
u256(32), u256(32),
AssemblyItem(PushData, u256(1) << 16), // replaced above in actualCopyRoutine[4] AssemblyItem(PushData, u256(1) << 16), // replaced above in actualCopyRoutine[4]
Instruction::DUP4, AssemblyItem(Dup, 4),
Instruction::CODECOPY, Instruction::CODECOPY,
// mload(0) // mload(0)
Instruction::DUP2, AssemblyItem(Dup, 2),
Instruction::MLOAD, Instruction::MLOAD,
// restore original memory // restore original memory
Instruction::SWAP2, AssemblyItem(Swap, 2),
Instruction::MSTORE Instruction::MSTORE
}; };
return copyRoutine; return copyRoutine;

View File

@ -175,6 +175,9 @@ enum class Instruction: uint8_t
LOG3, ///< Makes a log entry; 3 topics. LOG3, ///< Makes a log entry; 3 topics.
LOG4, ///< Makes a log entry; 4 topics. LOG4, ///< Makes a log entry; 4 topics.
SWAP_N = 0xB0,
DUP_N,
CREATE = 0xf0, ///< create a new account with associated code CREATE = 0xf0, ///< create a new account with associated code
CALL, ///< message-call into an account CALL, ///< message-call into an account
CALLCODE, ///< message-call with another account's code only 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; 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 /// @returns true if the instruction is a LOG
inline bool isLogInstruction(Instruction _inst) inline bool isLogInstruction(Instruction _inst)
{ {
return Instruction::LOG0 <= _inst && _inst <= Instruction::LOG4; 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 /// @returns the number of SWAP Instruction _inst
inline unsigned getSwapNumber(Instruction _inst) inline unsigned getSwapNumber(Instruction _inst)
{ {
@ -258,20 +237,6 @@ inline Instruction pushInstruction(unsigned _number)
return Instruction(unsigned(Instruction::PUSH1) + _number - 1); 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 /// @returns the LOG<_number> instruction
inline Instruction logInstruction(unsigned _number) inline Instruction logInstruction(unsigned _number)
{ {

View File

@ -118,6 +118,27 @@ KnownState::StoreOperation KnownState::feedItem(AssemblyItem const& _item, bool
m_expressionClasses->newClass(_item.location()) 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) else if (_item.type() != Operation)
{ {
assertThrow(_item.deposit() == 1, InvalidDeposit, ""); assertThrow(_item.deposit() == 1, InvalidDeposit, "");

View File

@ -159,11 +159,13 @@ struct OpReturnRevert: SimplePeepholeOptimizerMethod<OpReturnRevert>
if ( if (
(_returnRevert == Instruction::RETURN || _returnRevert == Instruction::REVERT) && (_returnRevert == Instruction::RETURN || _returnRevert == Instruction::REVERT) &&
_push.type() == Push && _push.type() == Push &&
(_pushOrDup.type() == Push || _pushOrDup == dupInstruction(1)) (_pushOrDup.type() == Push || ((_pushOrDup.type() == Dup && _pushOrDup.data() == 1) || _pushOrDup == Instruction::DUP1))
) )
if ( if (
(_op.type() == Operation && !instructionInfo(_op.instruction()).sideEffects) || (_op.type() == Operation && !instructionInfo(_op.instruction()).sideEffects) ||
_op.type() == Push _op.type() == Push ||
_op.type() == Swap ||
_op.type() == Dup
) )
{ {
*_out = _push; *_out = _push;
@ -190,7 +192,7 @@ struct DoublePush: SimplePeepholeOptimizerMethod<DoublePush>
if (_push1.type() == Push && _push2.type() == Push && _push1.data() == _push2.data()) if (_push1.type() == Push && _push2.type() == Push && _push1.data() == _push2.data())
{ {
*_out = _push1; *_out = _push1;
*_out = {Instruction::DUP1, _push2.location()}; *_out = AssemblyItem(Dup, 1, _push2.location());
return true; return true;
} }
else else
@ -204,7 +206,10 @@ struct CommutativeSwap: SimplePeepholeOptimizerMethod<CommutativeSwap>
{ {
// Remove SWAP1 if following instruction is commutative // Remove SWAP1 if following instruction is commutative
if ( if (
_swap == Instruction::SWAP1 && (
_swap == Instruction::SWAP1 ||
_swap == AssemblyItem(Swap, 1)
) &&
SemanticInformation::isCommutativeOperation(_op) SemanticInformation::isCommutativeOperation(_op)
) )
{ {
@ -228,7 +233,10 @@ struct SwapComparison: SimplePeepholeOptimizerMethod<SwapComparison>
}; };
if ( if (
_swap == Instruction::SWAP1 && (
_swap == Instruction::SWAP1 ||
_swap == AssemblyItem(Swap, 1)
) &&
_op.type() == Operation && _op.type() == Operation &&
swappableOps.count(_op.instruction()) swappableOps.count(_op.instruction())
) )
@ -250,10 +258,33 @@ struct DupSwap: SimplePeepholeOptimizerMethod<DupSwap>
std::back_insert_iterator<AssemblyItems> _out 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 ( if (
SemanticInformation::isDupInstruction(_dupN) && dupNum(_dupN).has_value() && swapNum(_swapN).has_value() && dupNum(_dupN) == swapNum(_swapN)
SemanticInformation::isSwapInstruction(_swapN) && /* _dupN.type() == AssemblyItemType::Dup &&
getDupNumber(_dupN.instruction()) == getSwapNumber(_swapN.instruction()) _swapN.type() == AssemblyItemType::Swap &&
_dupN.data() == _swapN.data()*/
) )
{ {
*_out = _dupN; *_out = _dupN;

View File

@ -169,6 +169,8 @@ bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item, bool
case PushData: case PushData:
case PushLibraryAddress: case PushLibraryAddress:
case PushImmutable: case PushImmutable:
case Swap:
case Dup:
return false; return false;
case evmasm::Operation: case evmasm::Operation:
{ {
@ -218,16 +220,27 @@ bool SemanticInformation::isCommutativeOperation(AssemblyItem const& _item)
bool SemanticInformation::isDupInstruction(AssemblyItem const& _item) bool SemanticInformation::isDupInstruction(AssemblyItem const& _item)
{ {
if (_item.type() != evmasm::Operation) return _item.type() == evmasm::Dup ||
return false; (_item.type() == evmasm::Operation &&
return evmasm::isDupInstruction(_item.instruction()); (
(_item.instruction() >= Instruction::DUP1 && _item.instruction() <= Instruction::DUP16) ||
(_item.instruction() == Instruction::DUP_N)
)
)
;
} }
bool SemanticInformation::isSwapInstruction(AssemblyItem const& _item) bool SemanticInformation::isSwapInstruction(AssemblyItem const& _item)
{ {
if (_item.type() != evmasm::Operation) return _item.type() == evmasm::Swap ||
return false; (_item.type() == evmasm::Operation &&
return evmasm::isSwapInstruction(_item.instruction()); (
(_item.instruction() >= Instruction::SWAP1 && _item.instruction() <= Instruction::SWAP16) ||
(_item.instruction() == Instruction::SWAP_N)
)
)
;
} }
bool SemanticInformation::isJumpInstruction(AssemblyItem const& _item) bool SemanticInformation::isJumpInstruction(AssemblyItem const& _item)

View File

@ -65,7 +65,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
// stack: source_ref [source_length] target_ref // stack: source_ref [source_length] target_ref
// store target_ref // store target_ref
for (unsigned i = _sourceType.sizeOnStack(); i > 0; --i) 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] // stack: target_ref source_ref [source_length]
if (_targetType.isByteArrayOrString()) if (_targetType.isByteArrayOrString())
@ -177,7 +177,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
_context << copyLoopStart; _context << copyLoopStart;
// check for loop condition // check for loop condition
_context _context
<< dupInstruction(3 + byteOffsetSize) << dupInstruction(2 + byteOffsetSize) << AssemblyItem(AssemblyItemType::Dup, 3 + byteOffsetSize) << AssemblyItem(AssemblyItemType::Dup, 2 + byteOffsetSize)
<< Instruction::GT << Instruction::ISZERO; << Instruction::GT << Instruction::ISZERO;
evmasm::AssemblyItem copyLoopEnd = _context.appendConditionalJump(); 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] // 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 // We might copy too much if there is padding at the last element, but this way end
// checking is easier. // checking is easier.
// stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] // 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 (_sourceType.location() == DataLocation::Storage)
{ {
if (haveByteOffsetSource) if (haveByteOffsetSource)
@ -233,9 +233,9 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
util::stackTooDeepString util::stackTooDeepString
); );
// fetch target storage reference // fetch target storage reference
_context << dupInstruction(2 + byteOffsetSize + sourceBaseType->sizeOnStack()); _context << AssemblyItem(AssemblyItemType::Dup, 2 + byteOffsetSize + sourceBaseType->sizeOnStack());
if (haveByteOffsetTarget) if (haveByteOffsetTarget)
_context << dupInstruction(1 + byteOffsetSize + sourceBaseType->sizeOnStack()); _context << AssemblyItem(AssemblyItemType::Dup, 1 + byteOffsetSize + sourceBaseType->sizeOnStack());
else else
_context << u256(0); _context << u256(0);
StorageItem(_context, *targetBaseType).storeValue(*sourceBaseType, SourceLocation(), true); 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); utils.incrementByteOffset(sourceBaseType->storageBytes(), 1, haveByteOffsetTarget ? 5 : 4);
else else
{ {
_context << swapInstruction(2 + byteOffsetSize); _context << AssemblyItem(AssemblyItemType::Swap, 2 + byteOffsetSize);
if (sourceIsStorage) if (sourceIsStorage)
_context << sourceBaseType->storageSize(); _context << sourceBaseType->storageSize();
else if (_sourceType.location() == DataLocation::Memory) else if (_sourceType.location() == DataLocation::Memory)
@ -255,26 +255,26 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
_context << sourceBaseType->calldataHeadSize(); _context << sourceBaseType->calldataHeadSize();
_context _context
<< Instruction::ADD << Instruction::ADD
<< swapInstruction(2 + byteOffsetSize); << AssemblyItem(AssemblyItemType::Swap, 2 + byteOffsetSize);
} }
// increment target // increment target
if (haveByteOffsetTarget) if (haveByteOffsetTarget)
utils.incrementByteOffset(targetBaseType->storageBytes(), byteOffsetSize, byteOffsetSize + 2); utils.incrementByteOffset(targetBaseType->storageBytes(), byteOffsetSize, byteOffsetSize + 2);
else else
_context _context
<< swapInstruction(1 + byteOffsetSize) << AssemblyItem(AssemblyItemType::Swap, 1 + byteOffsetSize)
<< targetBaseType->storageSize() << targetBaseType->storageSize()
<< Instruction::ADD << Instruction::ADD
<< swapInstruction(1 + byteOffsetSize); << AssemblyItem(AssemblyItemType::Swap, 1 + byteOffsetSize);
_context.appendJumpTo(copyLoopStart); _context.appendJumpTo(copyLoopStart);
_context << copyLoopEnd; _context << copyLoopEnd;
if (haveByteOffsetTarget) if (haveByteOffsetTarget)
{ {
// clear elements that might be left over in the current slot in target // 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] // 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(); 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); StorageItem(_context, *targetBaseType).setToZero(SourceLocation(), true);
utils.incrementByteOffset(targetBaseType->storageBytes(), byteOffsetSize, byteOffsetSize + 2); utils.incrementByteOffset(targetBaseType->storageBytes(), byteOffsetSize, byteOffsetSize + 2);
_context.appendJumpTo(copyLoopEnd); _context.appendJumpTo(copyLoopEnd);
@ -510,7 +510,7 @@ void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWord
} }
} }
// check for loop condition // 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 << Instruction::GT;
m_context.appendConditionalJumpTo(loopStart); m_context.appendConditionalJumpTo(loopStart);
// stack here: memory_end_offset storage_data_offset [storage_byte_offset] memory_offset // 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(); m_context << _arrayType.length();
else else
{ {
m_context << dupInstruction(1 + _stackDepth); m_context << AssemblyItem(AssemblyItemType::Dup, 1 + _stackDepth);
switch (_arrayType.location()) switch (_arrayType.location())
{ {
case DataLocation::CallData: case DataLocation::CallData:
@ -1178,19 +1178,19 @@ void ArrayUtils::incrementByteOffset(unsigned _byteSize, unsigned _byteOffsetPos
// byteOffset = 0; // byteOffset = 0;
// } // }
if (_byteOffsetPosition > 1) if (_byteOffsetPosition > 1)
m_context << swapInstruction(_byteOffsetPosition - 1); m_context << AssemblyItem(AssemblyItemType::Swap, _byteOffsetPosition - 1);
m_context << u256(_byteSize) << Instruction::ADD; m_context << u256(_byteSize) << Instruction::ADD;
if (_byteOffsetPosition > 1) 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 // compute, X := (byteOffset + byteSize - 1) / 32, should be 1 iff byteOffset + bytesize > 32
m_context m_context
<< u256(32) << dupInstruction(1 + _byteOffsetPosition) << u256(_byteSize - 1) << u256(32) << AssemblyItem(AssemblyItemType::Dup, 1 + _byteOffsetPosition) << u256(_byteSize - 1)
<< Instruction::ADD << Instruction::DIV; << Instruction::ADD << Instruction::DIV;
// increment storage offset if X == 1 (just add X to it) // increment storage offset if X == 1 (just add X to it)
// stack: X // stack: X
m_context m_context
<< swapInstruction(_storageOffsetPosition) << dupInstruction(_storageOffsetPosition + 1) << AssemblyItem(AssemblyItemType::Swap, _storageOffsetPosition) << AssemblyItem(AssemblyItemType::Dup, _storageOffsetPosition + 1)
<< Instruction::ADD << swapInstruction(_storageOffsetPosition); << Instruction::ADD << AssemblyItem(AssemblyItemType::Swap, _storageOffsetPosition);
// stack: X // stack: X
// set source_byte_offset to zero if X == 1 (using source_byte_offset *= 1 - X) // set source_byte_offset to zero if X == 1 (using source_byte_offset *= 1 - X)
m_context << u256(1) << Instruction::SUB; m_context << u256(1) << Instruction::SUB;
@ -1199,6 +1199,6 @@ void ArrayUtils::incrementByteOffset(unsigned _byteSize, unsigned _byteOffsetPos
m_context << Instruction::MUL; m_context << Instruction::MUL;
else else
m_context m_context
<< dupInstruction(_byteOffsetPosition + 1) << Instruction::MUL << AssemblyItem(AssemblyItemType::Dup, _byteOffsetPosition + 1) << Instruction::MUL
<< swapInstruction(_byteOffsetPosition) << Instruction::POP; << AssemblyItem(AssemblyItemType::Swap, _byteOffsetPosition) << Instruction::POP;
} }

View File

@ -419,17 +419,17 @@ void CompilerContext::appendInlineAssembly(
size_t stackDiff = static_cast<size_t>(_assembly.stackHeight()) - startStackHeight + stackDepth; size_t stackDiff = static_cast<size_t>(_assembly.stackHeight()) - startStackHeight + stackDepth;
if (_context == yul::IdentifierContext::LValue) if (_context == yul::IdentifierContext::LValue)
stackDiff -= 1; stackDiff -= 1;
if (stackDiff < 1 || stackDiff > 16) if (stackDiff < 1 || stackDiff > _assembly.maxDup() || stackDiff > _assembly.maxSwap())
BOOST_THROW_EXCEPTION( BOOST_THROW_EXCEPTION(
StackTooDeepError() << StackTooDeepError() <<
errinfo_sourceLocation(nativeLocationOf(_identifier)) << errinfo_sourceLocation(nativeLocationOf(_identifier)) <<
util::errinfo_comment(util::stackTooDeepString) util::errinfo_comment(util::stackTooDeepString)
); );
if (_context == yul::IdentifierContext::RValue) if (_context == yul::IdentifierContext::RValue)
_assembly.appendInstruction(dupInstruction(static_cast<unsigned>(stackDiff))); _assembly.appendDup(static_cast<unsigned>(stackDiff));
else else
{ {
_assembly.appendInstruction(swapInstruction(static_cast<unsigned>(stackDiff))); _assembly.appendSwap(static_cast<unsigned>(stackDiff));
_assembly.appendInstruction(Instruction::POP); _assembly.appendInstruction(Instruction::POP);
} }
}; };

View File

@ -251,7 +251,15 @@ public:
/// Append elements to the current instruction list and adjust @a m_stackOffset. /// 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::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<<(u256 const& _value) { m_asm->append(_value); return *this; }
CompilerContext& operator<<(bytes const& _data) { m_asm->append(_data); return *this; } CompilerContext& operator<<(bytes const& _data) { m_asm->append(_data); return *this; }

View File

@ -540,9 +540,9 @@ void CompilerUtils::encodeToMemory(
StackTooDeepError, StackTooDeepError,
util::stackTooDeepString util::stackTooDeepString
); );
m_context << dupInstruction(2 + dynPointers) << Instruction::DUP2; m_context << AssemblyItem(AssemblyItemType::Dup, 2 + dynPointers) << Instruction::DUP2;
m_context << Instruction::SUB; m_context << Instruction::SUB;
m_context << dupInstruction(2 + dynPointers - thisDynPointer); m_context << AssemblyItem(AssemblyItemType::Dup, 2 + dynPointers - thisDynPointer);
m_context << Instruction::MSTORE; m_context << Instruction::MSTORE;
// stack: ... <end_of_mem> // stack: ... <end_of_mem>
if (_givenTypes[i]->category() == Type::Category::StringLiteral) if (_givenTypes[i]->category() == Type::Category::StringLiteral)
@ -583,13 +583,13 @@ void CompilerUtils::encodeToMemory(
copyToStackTop(argSize - stackPos + dynPointers + 2, arrayType->sizeOnStack()); copyToStackTop(argSize - stackPos + dynPointers + 2, arrayType->sizeOnStack());
// stack: ... <end_of_mem> <value...> // stack: ... <end_of_mem> <value...>
// copy length to memory // copy length to memory
m_context << dupInstruction(1 + arrayType->sizeOnStack()); m_context << AssemblyItem(AssemblyItemType::Dup, 1 + arrayType->sizeOnStack());
ArrayUtils(m_context).retrieveLength(*arrayType, 1); ArrayUtils(m_context).retrieveLength(*arrayType, 1);
// stack: ... <end_of_mem> <value...> <end_of_mem'> <length> // stack: ... <end_of_mem> <value...> <end_of_mem'> <length>
storeInMemoryDynamic(*TypeProvider::uint256(), true); storeInMemoryDynamic(*TypeProvider::uint256(), true);
// stack: ... <end_of_mem> <value...> <end_of_mem''> // stack: ... <end_of_mem> <value...> <end_of_mem''>
// copy the new memory pointer // 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...> // stack: ... <end_of_mem''> <value...>
// copy data part // copy data part
ArrayUtils(m_context).copyArrayToMemory(*arrayType, _padToWordBoundaries); ArrayUtils(m_context).copyArrayToMemory(*arrayType, _padToWordBoundaries);
@ -602,7 +602,7 @@ void CompilerUtils::encodeToMemory(
} }
// remove unneeded stack elements (and retain memory pointer) // 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); popStackSlots(argSize + dynPointers + 1);
} }
@ -1270,7 +1270,7 @@ void CompilerUtils::convertType(
// Move it back into its place. // Move it back into its place.
for (unsigned j = 0; j < min(sourceSize, targetSize); ++j) for (unsigned j = 0; j < min(sourceSize, targetSize); ++j)
m_context << m_context <<
swapInstruction(depth + targetSize - sourceSize) << AssemblyItem(AssemblyItemType::Swap, depth + targetSize - sourceSize) <<
Instruction::POP; Instruction::POP;
// Value shrank // Value shrank
for (unsigned j = targetSize; j < sourceSize; ++j) for (unsigned j = targetSize; j < sourceSize; ++j)
@ -1422,7 +1422,7 @@ void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
util::errinfo_comment(util::stackTooDeepString) util::errinfo_comment(util::stackTooDeepString)
); );
for (unsigned i = 0; i < size; ++i) 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) void CompilerUtils::copyToStackTop(unsigned _stackDepth, unsigned _itemSize)
@ -1433,7 +1433,7 @@ void CompilerUtils::copyToStackTop(unsigned _stackDepth, unsigned _itemSize)
util::stackTooDeepString util::stackTooDeepString
); );
for (unsigned i = 0; i < _itemSize; ++i) for (unsigned i = 0; i < _itemSize; ++i)
m_context << dupInstruction(_stackDepth); m_context << AssemblyItem(AssemblyItemType::Dup, _stackDepth);
} }
void CompilerUtils::moveToStackTop(unsigned _stackDepth, unsigned _itemSize) void CompilerUtils::moveToStackTop(unsigned _stackDepth, unsigned _itemSize)
@ -1459,7 +1459,7 @@ void CompilerUtils::rotateStackUp(unsigned _items)
util::stackTooDeepString util::stackTooDeepString
); );
for (unsigned i = 1; i < _items; ++i) for (unsigned i = 1; i < _items; ++i)
m_context << swapInstruction(_items - i); m_context << AssemblyItem(AssemblyItemType::Swap, _items - i);
} }
void CompilerUtils::rotateStackDown(unsigned _items) void CompilerUtils::rotateStackDown(unsigned _items)
@ -1470,7 +1470,7 @@ void CompilerUtils::rotateStackDown(unsigned _items)
util::stackTooDeepString util::stackTooDeepString
); );
for (unsigned i = 1; i < _items; ++i) for (unsigned i = 1; i < _items; ++i)
m_context << swapInstruction(i); m_context << AssemblyItem(AssemblyItemType::Swap, i);
} }
void CompilerUtils::popStackElement(Type const& _type) void CompilerUtils::popStackElement(Type const& _type)

View File

@ -366,7 +366,7 @@ void ContractCompiler::appendInternalSelector(
{ {
size_t pivotIndex = _ids.size() / 2; size_t pivotIndex = _ids.size() / 2;
FixedHash<4> pivot{_ids.at(pivotIndex)}; 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()}; evmasm::AssemblyItem lessTag{m_context.appendConditionalJump()};
// Here, we have funid >= pivot // Here, we have funid >= pivot
vector<FixedHash<4>> larger{_ids.begin() + static_cast<ptrdiff_t>(pivotIndex), _ids.end()}; 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) 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.appendConditionalJumpTo(_entryPoints.at(id));
} }
m_context.appendJumpTo(_notFoundTag); 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, // If the function is not a view function and is called without DELEGATECALL,
// we revert. // 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.appendConditionalRevert(false, "Non-view function of library called without DELEGATECALL");
} }
m_context.setStackOffset(0); m_context.setStackOffset(0);
@ -669,7 +669,7 @@ bool ContractCompiler::visit(FunctionDefinition const& _function)
for (size_t i = 0; i < c_returnValuesSize; ++i) for (size_t i = 0; i < c_returnValuesSize; ++i)
stackLayout.push_back(static_cast<int>(i)); stackLayout.push_back(static_cast<int>(i));
if (stackLayout.size() > 17) if (stackLayout.size() > m_context.assembly().maxSwap() + 1)
BOOST_THROW_EXCEPTION( BOOST_THROW_EXCEPTION(
StackTooDeepError() << StackTooDeepError() <<
errinfo_sourceLocation(_function.location()) << errinfo_sourceLocation(_function.location()) <<
@ -683,7 +683,7 @@ bool ContractCompiler::visit(FunctionDefinition const& _function)
} }
else 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()); swap(stackLayout[static_cast<size_t>(stackLayout.back())], stackLayout.back());
} }
for (size_t i = 0; i < stackLayout.size(); ++i) for (size_t i = 0; i < stackLayout.size(); ++i)
@ -839,13 +839,13 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
} }
else else
solAssert(variable->type()->sizeOnStack() == 1, ""); solAssert(variable->type()->sizeOnStack() == 1, "");
if (stackDiff < 1 || stackDiff > 16) if (stackDiff < 1 || stackDiff > _assembly.maxDup())
BOOST_THROW_EXCEPTION( BOOST_THROW_EXCEPTION(
StackTooDeepError() << StackTooDeepError() <<
errinfo_sourceLocation(_inlineAssembly.location()) << errinfo_sourceLocation(_inlineAssembly.location()) <<
util::errinfo_comment(util::stackTooDeepString) util::errinfo_comment(util::stackTooDeepString)
); );
_assembly.appendInstruction(dupInstruction(stackDiff)); _assembly.appendDup(stackDiff);
} }
else else
solAssert(false, ""); solAssert(false, "");
@ -913,13 +913,13 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
else else
solAssert(suffix.empty(), ""); solAssert(suffix.empty(), "");
if (stackDiff > 16 || stackDiff < 1) if (stackDiff > _assembly.maxSwap() || stackDiff < 1)
BOOST_THROW_EXCEPTION( BOOST_THROW_EXCEPTION(
StackTooDeepError() << StackTooDeepError() <<
errinfo_sourceLocation(_inlineAssembly.location()) << errinfo_sourceLocation(_inlineAssembly.location()) <<
util::errinfo_comment(util::stackTooDeepString) util::errinfo_comment(util::stackTooDeepString)
); );
_assembly.appendInstruction(swapInstruction(stackDiff)); _assembly.appendSwap(stackDiff);
_assembly.appendInstruction(Instruction::POP); _assembly.appendInstruction(Instruction::POP);
} }
}; };

View File

@ -118,7 +118,7 @@ void ExpressionCompiler::appendConstStateVariableAccessor(VariableDeclaration co
acceptAndConvert(*_varDecl.value(), *_varDecl.annotation().type); acceptAndConvert(*_varDecl.value(), *_varDecl.annotation().type);
// append return // 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); m_context.appendJump(evmasm::AssemblyItem::JumpType::OutOfFunction);
} }
@ -220,9 +220,9 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
m_context << Instruction::SWAP2 << Instruction::POP << Instruction::SWAP1; m_context << Instruction::SWAP2 << Instruction::POP << Instruction::SWAP1;
else if (paramTypes.size() >= 2) 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 << 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); utils().popStackSlots(paramTypes.size() - 1);
} }
unsigned retSizeOnStack = 0; unsigned retSizeOnStack = 0;
@ -265,13 +265,13 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
retSizeOnStack = returnTypes.front()->sizeOnStack(); retSizeOnStack = returnTypes.front()->sizeOnStack();
} }
solAssert(retSizeOnStack == utils().sizeOnStack(returnTypes), ""); solAssert(retSizeOnStack == utils().sizeOnStack(returnTypes), "");
if (retSizeOnStack > 15) if (retSizeOnStack + 1 > m_context.assembly().maxDup())
BOOST_THROW_EXCEPTION( BOOST_THROW_EXCEPTION(
StackTooDeepError() << StackTooDeepError() <<
errinfo_sourceLocation(_varDecl.location()) << errinfo_sourceLocation(_varDecl.location()) <<
util::errinfo_comment(util::stackTooDeepString) util::errinfo_comment(util::stackTooDeepString)
); );
m_context << dupInstruction(retSizeOnStack + 1); m_context << AssemblyItem(AssemblyItemType::Dup, retSizeOnStack + 1);
m_context.appendJump(evmasm::AssemblyItem::JumpType::OutOfFunction); m_context.appendJump(evmasm::AssemblyItem::JumpType::OutOfFunction);
} }
@ -347,7 +347,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
} }
if (lvalueSize > 0) if (lvalueSize > 0)
{ {
if (itemSize + lvalueSize > 16) if (itemSize + lvalueSize > m_context.assembly().maxSwap())
BOOST_THROW_EXCEPTION( BOOST_THROW_EXCEPTION(
StackTooDeepError() << StackTooDeepError() <<
errinfo_sourceLocation(_assignment.location()) << errinfo_sourceLocation(_assignment.location()) <<
@ -355,7 +355,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
); );
// value [lvalue_ref] updated_value // value [lvalue_ref] updated_value
for (unsigned i = 0; i < itemSize; ++i) 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()); m_currentLValue->storeValue(*_assignment.annotation().type, _assignment.location());
} }
@ -447,7 +447,7 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
m_context << Instruction::DUP1; m_context << Instruction::DUP1;
if (m_currentLValue->sizeOnStack() > 0) if (m_currentLValue->sizeOnStack() > 0)
for (unsigned i = 1 + m_currentLValue->sizeOnStack(); i > 0; --i) 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) if (_unaryOperation.getOperator() == Token::Inc)
{ {
@ -472,7 +472,7 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
// Stack for prefix: [ref...] (*ref)+-1 // Stack for prefix: [ref...] (*ref)+-1
// Stack for postfix: *ref [ref...] (*ref)+-1 // Stack for postfix: *ref [ref...] (*ref)+-1
for (unsigned i = m_currentLValue->sizeOnStack(); i > 0; --i) for (unsigned i = m_currentLValue->sizeOnStack(); i > 0; --i)
m_context << swapInstruction(i); m_context << AssemblyItem(AssemblyItemType::Swap, i);
m_currentLValue->storeValue( m_currentLValue->storeValue(
*_unaryOperation.annotation().type, _unaryOperation.location(), *_unaryOperation.annotation().type, _unaryOperation.location(),
!_unaryOperation.isPrefixOperation()); !_unaryOperation.isPrefixOperation());
@ -715,7 +715,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
if (function.saltSet()) 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; m_context << Instruction::SWAP1;
} }
@ -724,7 +724,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
// now: [salt], [value], [salt], size, offset // now: [salt], [value], [salt], size, offset
if (function.valueSet()) if (function.valueSet())
m_context << dupInstruction(3 + (function.saltSet() ? 1 : 0)); m_context << AssemblyItem(AssemblyItemType::Dup, 3 + (function.saltSet() ? 1 : 0));
else else
m_context << u256(0); m_context << u256(0);
@ -737,9 +737,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
// now: [salt], [value], address // now: [salt], [value], address
if (function.valueSet()) if (function.valueSet())
m_context << swapInstruction(1) << Instruction::POP; m_context << AssemblyItem(AssemblyItemType::Swap, 1) << Instruction::POP;
if (function.saltSet()) if (function.saltSet())
m_context << swapInstruction(1) << Instruction::POP; m_context << AssemblyItem(AssemblyItemType::Swap, 1) << Instruction::POP;
// Check if zero (reverted) // Check if zero (reverted)
m_context << Instruction::DUP1 << Instruction::ISZERO; 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. // Its values of gasSet and valueSet is equal to the original function's though.
unsigned stackDepth = (function.gasSet() ? 1u : 0u) + (function.valueSet() ? 1u : 0u); unsigned stackDepth = (function.gasSet() ? 1u : 0u) + (function.valueSet() ? 1u : 0u);
if (stackDepth > 0) if (stackDepth > 0)
m_context << swapInstruction(stackDepth); m_context << AssemblyItem(AssemblyItemType::Swap, stackDepth);
if (function.gasSet()) if (function.gasSet())
m_context << Instruction::POP; m_context << Instruction::POP;
break; break;
@ -1022,7 +1022,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
}; };
m_context << contractAddresses.at(function.kind()); m_context << contractAddresses.at(function.kind());
for (unsigned i = function.sizeOnStack(); i > 0; --i) for (unsigned i = function.sizeOnStack(); i > 0; --i)
m_context << swapInstruction(i); m_context << AssemblyItem(AssemblyItemType::Swap, i);
solAssert(!_functionCall.annotation().tryCall, ""); solAssert(!_functionCall.annotation().tryCall, "");
appendExternalFunctionCall(function, arguments, false); appendExternalFunctionCall(function, arguments, false);
break; break;
@ -2648,7 +2648,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
utils().fetchFreeMemoryPointer(); utils().fetchFreeMemoryPointer();
if (!_functionType.isBareCall()) 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); utils().storeInMemoryDynamic(IntegerType(8 * CompilerUtils::dataStartOffset), false);
} }
@ -2703,10 +2703,10 @@ void ExpressionCompiler::appendExternalFunctionCall(
else if (useStaticCall) else if (useStaticCall)
solAssert(!_functionType.valueSet(), "Value set for staticcall"); solAssert(!_functionType.valueSet(), "Value set for staticcall");
else if (_functionType.valueSet()) else if (_functionType.valueSet())
m_context << dupInstruction(m_context.baseToCurrentStackOffset(valueStackPos)); m_context << AssemblyItem(AssemblyItemType::Dup, m_context.baseToCurrentStackOffset(valueStackPos));
else else
m_context << u256(0); m_context << u256(0);
m_context << dupInstruction(m_context.baseToCurrentStackOffset(contractStackPos)); m_context << AssemblyItem(AssemblyItemType::Dup, m_context.baseToCurrentStackOffset(contractStackPos));
bool existenceChecked = false; bool existenceChecked = false;
// Check the target contract exists (has code) for non-low-level calls. // Check the target contract exists (has code) for non-low-level calls.
@ -2730,7 +2730,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
} }
if (_functionType.gasSet()) 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()) else if (m_context.evmVersion().canOverchargeGasForCall())
// Send all gas (requires tangerine whistle EVM) // Send all gas (requires tangerine whistle EVM)
m_context << Instruction::GAS; m_context << Instruction::GAS;
@ -2768,7 +2768,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
m_context.appendConditionalRevert(true); m_context.appendConditionalRevert(true);
} }
else else
m_context << swapInstruction(remainsSize); m_context << AssemblyItem(AssemblyItemType::Swap, remainsSize);
utils().popStackSlots(remainsSize); utils().popStackSlots(remainsSize);
// Only success flag is remaining on stack. // Only success flag is remaining on stack.

View File

@ -56,7 +56,7 @@ void StackVariable::retrieveValue(SourceLocation const& _location, bool) const
); );
solAssert(stackPos + 1 >= m_size, "Size and stack pos mismatch."); solAssert(stackPos + 1 >= m_size, "Size and stack pos mismatch.");
for (unsigned i = 0; i < m_size; ++i) 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 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) else if (stackDiff > 0)
for (unsigned i = 0; i < m_size; ++i) for (unsigned i = 0; i < m_size; ++i)
m_context << swapInstruction(stackDiff) << Instruction::POP; m_context << AssemblyItem(AssemblyItemType::Swap, stackDiff) << Instruction::POP;
if (!_move) if (!_move)
retrieveValue(_location); retrieveValue(_location);
} }
@ -421,7 +421,7 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
} }
unsigned stackSize = sourceMemberType->sizeOnStack(); unsigned stackSize = sourceMemberType->sizeOnStack();
pair<u256, unsigned> const& offsets = structType.storageOffsetsOfMember(member.name); 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); m_context << u256(offsets.second);
// stack: source_ref target_ref target_off source_value... target_member_ref target_member_byte_off // stack: source_ref target_ref target_off source_value... target_member_ref target_member_byte_off
StorageItem(m_context, *memberType).storeValue(*sourceMemberType, _location, true); StorageItem(m_context, *memberType).storeValue(*sourceMemberType, _location, true);

View File

@ -99,7 +99,7 @@ GasEstimator::GasConsumption GasEstimator::functionalEstimation(
AssemblyItem invalidTag(PushTag, u256(-0x10)); AssemblyItem invalidTag(PushTag, u256(-0x10));
state->feedItem(invalidTag, true); state->feedItem(invalidTag, true);
if (parametersSize > 0) if (parametersSize > 0)
state->feedItem(swapInstruction(parametersSize)); state->feedItem(AssemblyItem(AssemblyItemType::Swap, parametersSize));
return PathGasMeter::estimateMax(_items, m_evmVersion, _offset, state); return PathGasMeter::estimateMax(_items, m_evmVersion, _offset, state);
} }

View File

@ -67,6 +67,13 @@ public:
virtual void setStackHeight(int height) = 0; virtual void setStackHeight(int height) = 0;
/// Append an EVM instruction. /// Append an EVM instruction.
virtual void appendInstruction(evmasm::Instruction _instruction) = 0; 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. /// Append a constant.
virtual void appendConstant(u256 const& _constant) = 0; virtual void appendConstant(u256 const& _constant) = 0;
/// Append a label. /// Append a label.

View File

@ -187,7 +187,7 @@ void CodeTransform::operator()(VariableDeclaration const& _varDecl)
m_unusedStackSlots.erase(it); m_unusedStackSlots.erase(it);
m_context->variableStackHeights[&var] = slot; m_context->variableStackHeights[&var] = slot;
if (size_t heightDiff = variableHeightDiff(var, varName, true)) 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); m_assembly.appendInstruction(evmasm::Instruction::POP);
break; break;
} }
@ -274,7 +274,7 @@ void CodeTransform::operator()(Identifier const& _identifier)
// TODO: opportunity for optimization: Do not DUP if this is the last reference // TODO: opportunity for optimization: Do not DUP if this is the last reference
// to the top most element of the stack // to the top most element of the stack
if (size_t heightDiff = variableHeightDiff(_var, _identifier.name, false)) 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 else
// Store something to balance the stack // Store something to balance the stack
m_assembly.appendConstant(u256(0)); m_assembly.appendConstant(u256(0));
@ -328,7 +328,7 @@ void CodeTransform::operator()(Switch const& _switch)
AbstractAssembly::LabelID bodyLabel = m_assembly.newLabelId(); AbstractAssembly::LabelID bodyLabel = m_assembly.newLabelId();
caseBodies[&c] = bodyLabel; caseBodies[&c] = bodyLabel;
yulAssert(m_assembly.stackHeight() == expressionHeight + 1, ""); yulAssert(m_assembly.stackHeight() == expressionHeight + 1, "");
m_assembly.appendInstruction(evmasm::dupInstruction(2)); m_assembly.appendDup(2);
m_assembly.appendInstruction(evmasm::Instruction::EQ); m_assembly.appendInstruction(evmasm::Instruction::EQ);
m_assembly.appendJumpToIf(bodyLabel); m_assembly.appendJumpToIf(bodyLabel);
} }
@ -478,7 +478,7 @@ void CodeTransform::operator()(FunctionDefinition const& _function)
} }
else 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()); swap(stackLayout[static_cast<size_t>(stackLayout.back())], stackLayout.back());
} }
for (size_t i = 0; i < stackLayout.size(); ++i) 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); Scope::Variable const& _var = std::get<Scope::Variable>(*var);
if (size_t heightDiff = variableHeightDiff(_var, _variableName.name, true)) 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); m_assembly.appendInstruction(evmasm::Instruction::POP);
decreaseReference(_variableName.name, _var); decreaseReference(_variableName.name, _var);
} }

View File

@ -145,9 +145,16 @@ map<YulString, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion _evmVe
string name = toLower(instr.first); string name = toLower(instr.first);
auto const opcode = instr.second; 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 ( if (
!evmasm::isDupInstruction(opcode) && !isDupInstruction(opcode) &&
!evmasm::isSwapInstruction(opcode) && !isSwapInstruction(opcode) &&
!evmasm::isPushInstruction(opcode) && !evmasm::isPushInstruction(opcode) &&
opcode != evmasm::Instruction::JUMP && opcode != evmasm::Instruction::JUMP &&
opcode != evmasm::Instruction::JUMPI && opcode != evmasm::Instruction::JUMPI &&

View File

@ -64,6 +64,28 @@ void EthAssemblyAdapter::appendInstruction(evmasm::Instruction _instruction)
m_assembly.append(_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) void EthAssemblyAdapter::appendConstant(u256 const& _constant)
{ {
m_assembly.append(_constant); m_assembly.append(_constant);

View File

@ -44,6 +44,10 @@ public:
int stackHeight() const override; int stackHeight() const override;
void setStackHeight(int height) override; void setStackHeight(int height) override;
void appendInstruction(evmasm::Instruction _instruction) 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 appendConstant(u256 const& _constant) override;
void appendLabel(LabelID _labelId) override; void appendLabel(LabelID _labelId) override;
void appendLabelReference(LabelID _labelId) override; void appendLabelReference(LabelID _labelId) override;

View File

@ -40,6 +40,22 @@ void NoOutputAssembly::appendInstruction(evmasm::Instruction _instr)
m_stackHeight += instructionInfo(_instr).ret - instructionInfo(_instr).args; 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&) void NoOutputAssembly::appendConstant(u256 const&)
{ {
appendInstruction(evmasm::pushInstruction(1)); appendInstruction(evmasm::pushInstruction(1));

View File

@ -52,6 +52,10 @@ public:
int stackHeight() const override { return m_stackHeight; } int stackHeight() const override { return m_stackHeight; }
void setStackHeight(int height) override { m_stackHeight = height; } void setStackHeight(int height) override { m_stackHeight = height; }
void appendInstruction(evmasm::Instruction _instruction) override; 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 appendConstant(u256 const& _constant) override;
void appendLabel(LabelID _labelId) override; void appendLabel(LabelID _labelId) override;
void appendLabelReference(LabelID _labelId) override; void appendLabelReference(LabelID _labelId) override;

View File

@ -259,11 +259,11 @@ void OptimizedEVMCodeTransform::createStackLayout(std::shared_ptr<DebugData cons
{ {
yulAssert(static_cast<int>(m_stack.size()) == m_assembly.stackHeight(), ""); yulAssert(static_cast<int>(m_stack.size()) == m_assembly.stackHeight(), "");
yulAssert(_i > 0 && _i < m_stack.size(), ""); yulAssert(_i > 0 && _i < m_stack.size(), "");
if (_i <= 16) if (_i <= m_assembly.maxSwap())
m_assembly.appendInstruction(evmasm::swapInstruction(_i)); m_assembly.appendSwap(_i);
else 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); StackSlot const& deepSlot = m_stack.at(m_stack.size() - _i - 1);
YulString varNameDeep = slotVariableName(deepSlot); YulString varNameDeep = slotVariableName(deepSlot);
YulString varNameTop = slotVariableName(m_stack.back()); 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. // Dup the slot, if already on stack and reachable.
if (auto depth = util::findOffset(m_stack | ranges::views::reverse, _slot)) 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; return;
} }
else if (!canBeFreelyGenerated(_slot)) 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); YulString varName = slotVariableName(_slot);
string msg = string msg =
(varName.empty() ? "Slot " + stackSlotToString(_slot) : "Variable " + varName.str()) (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_stackErrors.emplace_back(StackTooDeepError(
m_currentFunctionInfo ? m_currentFunctionInfo->function.name : YulString{}, m_currentFunctionInfo ? m_currentFunctionInfo->function.name : YulString{},
varName, varName,

View File

@ -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. /// @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; size_t opGas = 0;
auto swap = [&](unsigned _swapDepth) auto swap = [&](unsigned _swapDepth)
{ {
if (_swapDepth > 16) if (_swapDepth > maxSwap())
opGas += 1000; opGas += 1000;
else else
opGas += evmasm::GasMeter::runGas(evmasm::swapInstruction(_swapDepth)); // TODO
opGas += evmasm::GasMeter::runGas(evmasm::Instruction::SWAP1);
}; };
auto dupOrPush = [&](StackSlot const& _slot) 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); auto depth = util::findOffset(_source | ranges::views::reverse, _slot);
yulAssert(depth); yulAssert(depth);
if (*depth < 16) if (*depth < maxDup())
opGas += evmasm::GasMeter::runGas(evmasm::dupInstruction(static_cast<unsigned>(*depth + 1))); // TODO
opGas += evmasm::GasMeter::runGas(evmasm::Instruction::DUP1);
else else
opGas += 1000; opGas += 1000;
} }

View File

@ -115,6 +115,10 @@ private:
void fillInJunk(CFG::BasicBlock const& _block, CFG::FunctionInfo const* _functionInfo = nullptr); void fillInJunk(CFG::BasicBlock const& _block, CFG::FunctionInfo const* _functionInfo = nullptr);
StackLayout& m_layout; StackLayout& m_layout;
// TODO
unsigned maxSwap() const { return 16; }
unsigned maxDup() const { return 16; }
}; };
} }

View File

@ -425,6 +425,7 @@ u256 EVMInstructionInterpreter::eval(
case Instruction::DUP14: case Instruction::DUP14:
case Instruction::DUP15: case Instruction::DUP15:
case Instruction::DUP16: case Instruction::DUP16:
case Instruction::DUP_N:
case Instruction::SWAP1: case Instruction::SWAP1:
case Instruction::SWAP2: case Instruction::SWAP2:
case Instruction::SWAP3: case Instruction::SWAP3:
@ -441,6 +442,7 @@ u256 EVMInstructionInterpreter::eval(
case Instruction::SWAP14: case Instruction::SWAP14:
case Instruction::SWAP15: case Instruction::SWAP15:
case Instruction::SWAP16: case Instruction::SWAP16:
case Instruction::SWAP_N:
{ {
yulAssert(false, ""); yulAssert(false, "");
return 0; return 0;