diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index 5b6e2a86f..65052f3ed 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -558,6 +558,26 @@ LinkerObject const& Assembly::assemble() const case Operation: ret.bytecode.push_back(static_cast(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(evmasm::Instruction::SWAP1 + (i.data() - 1))); + else + { + ret.bytecode.push_back(static_cast(evmasm::Instruction::SWAP_N)); + ret.bytecode.push_back(static_cast(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(evmasm::Instruction::DUP1 + (i.data() - 1))); + else + { + ret.bytecode.push_back(static_cast(evmasm::Instruction::DUP_N)); + ret.bytecode.push_back(static_cast(i.data() - 1)); + } + break; case Push: { unsigned b = max(1, numberEncodingSize(i.data())); diff --git a/libevmasm/Assembly.h b/libevmasm/Assembly.h index 7b8b4509d..bd42fc785 100644 --- a/libevmasm/Assembly.h +++ b/libevmasm/Assembly.h @@ -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; } diff --git a/libevmasm/AssemblyItem.cpp b/libevmasm/AssemblyItem.cpp index a36e0ddb1..0466c3de4 100644 --- a/libevmasm/AssemblyItem.cpp +++ b/libevmasm/AssemblyItem.cpp @@ -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(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(data()); + else if (type() == Dup) + return static_cast(data()); else return 0; } @@ -183,6 +193,10 @@ size_t AssemblyItem::returnValues() const { case Operation: return static_cast(instructionInfo(instruction()).ret); + case Swap: + return static_cast(data()); + case Dup: + return static_cast(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; diff --git a/libevmasm/AssemblyItem.h b/libevmasm/AssemblyItem.h index 4aef82d8b..52c46063f 100644 --- a/libevmasm/AssemblyItem.h +++ b/libevmasm/AssemblyItem.h @@ -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(static_cast(_i) - static_cast(Instruction::SWAP1) + 1); + m_instruction = {}; + } + else if (_i >= Instruction::DUP1 && _i <= Instruction::DUP16) + { + m_type = Dup; + m_data = std::make_shared(static_cast(_i) - static_cast(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); } diff --git a/libevmasm/CommonSubexpressionEliminator.cpp b/libevmasm/CommonSubexpressionEliminator.cpp index 19b275cb6..38e27f092 100644 --- a/libevmasm/CommonSubexpressionEliminator.cpp +++ b/libevmasm/CommonSubexpressionEliminator.cpp @@ -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(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(instructionNum)), _location)); + appendItem(AssemblyItem(AssemblyItemType::Swap, instructionNum, _location)); if (m_stack[m_stackHeight] != m_stack[_fromPosition]) { diff --git a/libevmasm/ConstantOptimiser.cpp b/libevmasm/ConstantOptimiser.cpp index 37ac4c18b..0072cefbc 100644 --- a/libevmasm/ConstantOptimiser.cpp +++ b/libevmasm/ConstantOptimiser.cpp @@ -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, , 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; diff --git a/libevmasm/Instruction.h b/libevmasm/Instruction.h index 5d4432ca5..ba9b8c431 100644 --- a/libevmasm/Instruction.h +++ b/libevmasm/Instruction.h @@ -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(_inst) - unsigned(Instruction::PUSH1) + 1; -} - -/// @returns the number of DUP Instruction _inst -inline unsigned getDupNumber(Instruction _inst) -{ - return static_cast(_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) { diff --git a/libevmasm/KnownState.cpp b/libevmasm/KnownState.cpp index 1e886f7ab..db77abe19 100644 --- a/libevmasm/KnownState.cpp +++ b/libevmasm/KnownState.cpp @@ -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(_item.data() - 1), + _item.location() + ) + ); + m_stackHeight++; + } + else if (_item.type() == Swap) + { + // TODO: check + swapStackElements( + m_stackHeight, + m_stackHeight - 1 - static_cast(_item.data() - 1), + _item.location() + ); + } else if (_item.type() != Operation) { assertThrow(_item.deposit() == 1, InvalidDeposit, ""); diff --git a/libevmasm/PeepholeOptimiser.cpp b/libevmasm/PeepholeOptimiser.cpp index 334903c92..85d7669bf 100644 --- a/libevmasm/PeepholeOptimiser.cpp +++ b/libevmasm/PeepholeOptimiser.cpp @@ -159,11 +159,13 @@ struct OpReturnRevert: SimplePeepholeOptimizerMethod 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 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 { // 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 }; 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 std::back_insert_iterator _out ) { + auto dupNum = [](AssemblyItem const& _item) -> std::optional { + if (_item.type() == AssemblyItemType::Dup) + return static_cast(_item.data()); + else if ( + _item.type() == AssemblyItemType::Operation && + _item.instruction() >= Instruction::DUP1 && + _item.instruction() <= Instruction::DUP16 + ) + return static_cast(_item.instruction()) - static_cast(Instruction::DUP1) + 1; + return nullopt; + }; + auto swapNum = [](AssemblyItem const& _item) -> std::optional { + if (_item.type() == AssemblyItemType::Swap) + return static_cast(_item.data()); + else if ( + _item.type() == AssemblyItemType::Operation && + _item.instruction() >= Instruction::SWAP1 && + _item.instruction() <= Instruction::SWAP16 + ) + return static_cast(_item.instruction()) - static_cast(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; diff --git a/libevmasm/SemanticInformation.cpp b/libevmasm/SemanticInformation.cpp index acbcaa5c0..2e21dfcf2 100644 --- a/libevmasm/SemanticInformation.cpp +++ b/libevmasm/SemanticInformation.cpp @@ -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) diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index 609ff9993..c94034a49 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -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; } diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index bc69f82c5..6d09fa143 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -419,17 +419,17 @@ void CompilerContext::appendInlineAssembly( size_t stackDiff = static_cast(_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(stackDiff))); + _assembly.appendDup(static_cast(stackDiff)); else { - _assembly.appendInstruction(swapInstruction(static_cast(stackDiff))); + _assembly.appendSwap(static_cast(stackDiff)); _assembly.appendInstruction(Instruction::POP); } }; diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index 4f7fd3abd..3fcb87f47 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -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(_instruction) - static_cast(evmasm::Instruction::SWAP1) + 1)); + else if (_instruction >= evmasm::Instruction::DUP1 && _instruction <= evmasm::Instruction::DUP16) + m_asm->append(evmasm::AssemblyItem(evmasm::Dup, static_cast(_instruction) - static_cast(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; } diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 783a7b1a4..4cab88169 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -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: ... if (_givenTypes[i]->category() == Type::Category::StringLiteral) @@ -583,13 +583,13 @@ void CompilerUtils::encodeToMemory( copyToStackTop(argSize - stackPos + dynPointers + 2, arrayType->sizeOnStack()); // stack: ... // 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: ... storeInMemoryDynamic(*TypeProvider::uint256(), true); // stack: ... // copy the new memory pointer - m_context << swapInstruction(arrayType->sizeOnStack() + 1) << Instruction::POP; + m_context << AssemblyItem(AssemblyItemType::Swap, arrayType->sizeOnStack() + 1) << Instruction::POP; // stack: ... // 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) diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index a6e7ab2fb..306ce787a 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -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> larger{_ids.begin() + static_cast(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(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(stackLayout.size()) - static_cast(stackLayout.back()) - 1u); + m_context << AssemblyItem(AssemblyItemType::Swap, static_cast(stackLayout.size()) - static_cast(stackLayout.back()) - 1u); swap(stackLayout[static_cast(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); } }; diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 461de5648..db2377488 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -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(paramTypes.size())); + m_context << AssemblyItem(AssemblyItemType::Swap, static_cast(paramTypes.size())); m_context << Instruction::POP; - m_context << swapInstruction(static_cast(paramTypes.size())); + m_context << AssemblyItem(AssemblyItemType::Swap, static_cast(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. diff --git a/libsolidity/codegen/LValue.cpp b/libsolidity/codegen/LValue.cpp index 2708d319d..faca484e7 100644 --- a/libsolidity/codegen/LValue.cpp +++ b/libsolidity/codegen/LValue.cpp @@ -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 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); diff --git a/libsolidity/interface/GasEstimator.cpp b/libsolidity/interface/GasEstimator.cpp index 512f91bee..b627a4f53 100644 --- a/libsolidity/interface/GasEstimator.cpp +++ b/libsolidity/interface/GasEstimator.cpp @@ -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); } diff --git a/libyul/backends/evm/AbstractAssembly.h b/libyul/backends/evm/AbstractAssembly.h index 2d7be9001..66bda7b3d 100644 --- a/libyul/backends/evm/AbstractAssembly.h +++ b/libyul/backends/evm/AbstractAssembly.h @@ -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. diff --git a/libyul/backends/evm/EVMCodeTransform.cpp b/libyul/backends/evm/EVMCodeTransform.cpp index 8a268675c..2127c8962 100644 --- a/libyul/backends/evm/EVMCodeTransform.cpp +++ b/libyul/backends/evm/EVMCodeTransform.cpp @@ -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(heightDiff - 1))); + m_assembly.appendSwap(static_cast(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(heightDiff))); + m_assembly.appendDup(static_cast(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(stackLayout.size()) - static_cast(stackLayout.back()) - 1u)); + m_assembly.appendSwap(static_cast(stackLayout.size()) - static_cast(stackLayout.back()) - 1u); swap(stackLayout[static_cast(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(*var); if (size_t heightDiff = variableHeightDiff(_var, _variableName.name, true)) - m_assembly.appendInstruction(evmasm::swapInstruction(static_cast(heightDiff - 1))); + m_assembly.appendSwap(static_cast(heightDiff - 1)); m_assembly.appendInstruction(evmasm::Instruction::POP); decreaseReference(_variableName.name, _var); } diff --git a/libyul/backends/evm/EVMDialect.cpp b/libyul/backends/evm/EVMDialect.cpp index 71b9dba4c..e207d9e89 100644 --- a/libyul/backends/evm/EVMDialect.cpp +++ b/libyul/backends/evm/EVMDialect.cpp @@ -145,9 +145,16 @@ map 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 && diff --git a/libyul/backends/evm/EthAssemblyAdapter.cpp b/libyul/backends/evm/EthAssemblyAdapter.cpp index 685f56cbc..157896c3e 100644 --- a/libyul/backends/evm/EthAssemblyAdapter.cpp +++ b/libyul/backends/evm/EthAssemblyAdapter.cpp @@ -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); diff --git a/libyul/backends/evm/EthAssemblyAdapter.h b/libyul/backends/evm/EthAssemblyAdapter.h index 87047ccbf..1eaf8d8a2 100644 --- a/libyul/backends/evm/EthAssemblyAdapter.h +++ b/libyul/backends/evm/EthAssemblyAdapter.h @@ -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; diff --git a/libyul/backends/evm/NoOutputAssembly.cpp b/libyul/backends/evm/NoOutputAssembly.cpp index e26204201..a8b5c32cd 100644 --- a/libyul/backends/evm/NoOutputAssembly.cpp +++ b/libyul/backends/evm/NoOutputAssembly.cpp @@ -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)); diff --git a/libyul/backends/evm/NoOutputAssembly.h b/libyul/backends/evm/NoOutputAssembly.h index 1103392ef..cdccda0fc 100644 --- a/libyul/backends/evm/NoOutputAssembly.h +++ b/libyul/backends/evm/NoOutputAssembly.h @@ -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; diff --git a/libyul/backends/evm/OptimizedEVMCodeTransform.cpp b/libyul/backends/evm/OptimizedEVMCodeTransform.cpp index fd3a572e0..e49f7987a 100644 --- a/libyul/backends/evm/OptimizedEVMCodeTransform.cpp +++ b/libyul/backends/evm/OptimizedEVMCodeTransform.cpp @@ -259,11 +259,11 @@ void OptimizedEVMCodeTransform::createStackLayout(std::shared_ptr(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(_i) - 16; + int deficit = static_cast(_i) - static_cast(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(*depth + 1))); + m_assembly.appendDup(static_cast(*depth + 1)); return; } else if (!canBeFreelyGenerated(_slot)) { - int deficit = static_cast(*depth - 15); + int deficit = static_cast(*depth) - static_cast(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, diff --git a/libyul/backends/evm/StackLayoutGenerator.cpp b/libyul/backends/evm/StackLayoutGenerator.cpp index 95c840733..f8262d0c5 100644 --- a/libyul/backends/evm/StackLayoutGenerator.cpp +++ b/libyul/backends/evm/StackLayoutGenerator.cpp @@ -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(*depth + 1))); + if (*depth < maxDup()) + // TODO + opGas += evmasm::GasMeter::runGas(evmasm::Instruction::DUP1); else opGas += 1000; } diff --git a/libyul/backends/evm/StackLayoutGenerator.h b/libyul/backends/evm/StackLayoutGenerator.h index 40b554cd6..6307449ad 100644 --- a/libyul/backends/evm/StackLayoutGenerator.h +++ b/libyul/backends/evm/StackLayoutGenerator.h @@ -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; } }; } diff --git a/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp b/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp index f0691a836..5d236ed4f 100644 --- a/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp +++ b/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp @@ -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;