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:
ret.bytecode.push_back(static_cast<uint8_t>(i.instruction()));
break;
case Swap:
assertThrow(i.data() > 0 && i.data() <= maxSwap(), AssemblyException, "Invalid swap.");
if (i.data() <= 16)
ret.bytecode.push_back(static_cast<uint8_t>(evmasm::Instruction::SWAP1 + (i.data() - 1)));
else
{
ret.bytecode.push_back(static_cast<uint8_t>(evmasm::Instruction::SWAP_N));
ret.bytecode.push_back(static_cast<uint8_t>(i.data() - 1));
}
break;
case Dup:
assertThrow(i.data() > 0 && i.data() <= maxDup(), AssemblyException, "Invalid dup.");
if (i.data() <= 16)
ret.bytecode.push_back(static_cast<uint8_t>(evmasm::Instruction::DUP1 + (i.data() - 1)));
else
{
ret.bytecode.push_back(static_cast<uint8_t>(evmasm::Instruction::DUP_N));
ret.bytecode.push_back(static_cast<uint8_t>(i.data() - 1));
}
break;
case Push:
{
unsigned b = max<unsigned>(1, numberEncodingSize(i.data()));

View File

@ -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; }

View File

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

View File

@ -39,6 +39,8 @@ enum AssemblyItemType
{
UndefinedItem,
Operation,
Swap,
Dup,
Push,
PushTag,
PushSub,
@ -70,7 +72,21 @@ public:
m_type(Operation),
m_instruction(_i),
m_location(std::move(_location))
{}
{
// TODO: avoid this on call sites instead
if (_i >= Instruction::SWAP1 && _i <= Instruction::SWAP16)
{
m_type = Swap;
m_data = std::make_shared<u256>(static_cast<unsigned>(_i) - static_cast<unsigned>(Instruction::SWAP1) + 1);
m_instruction = {};
}
else if (_i >= Instruction::DUP1 && _i <= Instruction::DUP16)
{
m_type = Dup;
m_data = std::make_shared<u256>(static_cast<unsigned>(_i) - static_cast<unsigned>(Instruction::DUP1) + 1);
m_instruction = {};
}
}
AssemblyItem(AssemblyItemType _type, u256 _data = 0, langutil::SourceLocation _location = langutil::SourceLocation()):
m_type(_type),
m_location(std::move(_location))
@ -144,10 +160,9 @@ public:
return data() < _other.data();
}
/// Shortcut that avoids constructing an AssemblyItem just to perform the comparison.
bool operator==(Instruction _instr) const
{
return type() == Operation && instruction() == _instr;
return (*this) == AssemblyItem(_instr);
}
bool operator!=(Instruction _instr) const { return !operator==(_instr); }

View File

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

View File

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

View File

@ -175,6 +175,9 @@ enum class Instruction: uint8_t
LOG3, ///< Makes a log entry; 3 topics.
LOG4, ///< Makes a log entry; 4 topics.
SWAP_N = 0xB0,
DUP_N,
CREATE = 0xf0, ///< create a new account with associated code
CALL, ///< message-call into an account
CALLCODE, ///< message-call with another account's code only
@ -209,36 +212,12 @@ inline bool isPushInstruction(Instruction _inst)
return Instruction::PUSH1 <= _inst && _inst <= Instruction::PUSH32;
}
/// @returns true if the instruction is a DUP
inline bool isDupInstruction(Instruction _inst)
{
return Instruction::DUP1 <= _inst && _inst <= Instruction::DUP16;
}
/// @returns true if the instruction is a SWAP
inline bool isSwapInstruction(Instruction _inst)
{
return Instruction::SWAP1 <= _inst && _inst <= Instruction::SWAP16;
}
/// @returns true if the instruction is a LOG
inline bool isLogInstruction(Instruction _inst)
{
return Instruction::LOG0 <= _inst && _inst <= Instruction::LOG4;
}
/// @returns the number of PUSH Instruction _inst
inline unsigned getPushNumber(Instruction _inst)
{
return static_cast<uint8_t>(_inst) - unsigned(Instruction::PUSH1) + 1;
}
/// @returns the number of DUP Instruction _inst
inline unsigned getDupNumber(Instruction _inst)
{
return static_cast<uint8_t>(_inst) - unsigned(Instruction::DUP1) + 1;
}
/// @returns the number of SWAP Instruction _inst
inline unsigned getSwapNumber(Instruction _inst)
{
@ -258,20 +237,6 @@ inline Instruction pushInstruction(unsigned _number)
return Instruction(unsigned(Instruction::PUSH1) + _number - 1);
}
/// @returns the DUP<_number> instruction
inline Instruction dupInstruction(unsigned _number)
{
assertThrow(1 <= _number && _number <= 16, InvalidOpcode, std::string("Invalid DUP instruction requested (") + std::to_string(_number) + ").");
return Instruction(unsigned(Instruction::DUP1) + _number - 1);
}
/// @returns the SWAP<_number> instruction
inline Instruction swapInstruction(unsigned _number)
{
assertThrow(1 <= _number && _number <= 16, InvalidOpcode, std::string("Invalid SWAP instruction requested (") + std::to_string(_number) + ").");
return Instruction(unsigned(Instruction::SWAP1) + _number - 1);
}
/// @returns the LOG<_number> instruction
inline Instruction logInstruction(unsigned _number)
{

View File

@ -118,6 +118,27 @@ KnownState::StoreOperation KnownState::feedItem(AssemblyItem const& _item, bool
m_expressionClasses->newClass(_item.location())
);
}
else if (_item.type() == Dup)
{
// TODO: check
setStackElement(
m_stackHeight + 1,
stackElement(
m_stackHeight - static_cast<int>(_item.data() - 1),
_item.location()
)
);
m_stackHeight++;
}
else if (_item.type() == Swap)
{
// TODO: check
swapStackElements(
m_stackHeight,
m_stackHeight - 1 - static_cast<int>(_item.data() - 1),
_item.location()
);
}
else if (_item.type() != Operation)
{
assertThrow(_item.deposit() == 1, InvalidDeposit, "");

View File

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

View File

@ -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)

View File

@ -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;
}

View File

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

View File

@ -251,7 +251,15 @@ public:
/// Append elements to the current instruction list and adjust @a m_stackOffset.
CompilerContext& operator<<(evmasm::AssemblyItem const& _item) { m_asm->append(_item); return *this; }
CompilerContext& operator<<(evmasm::Instruction _instruction) { m_asm->append(_instruction); return *this; }
CompilerContext& operator<<(evmasm::Instruction _instruction) {
if (_instruction >= evmasm::Instruction::SWAP1 && _instruction <= evmasm::Instruction::SWAP16)
m_asm->append(evmasm::AssemblyItem(evmasm::Swap, static_cast<unsigned>(_instruction) - static_cast<unsigned>(evmasm::Instruction::SWAP1) + 1));
else if (_instruction >= evmasm::Instruction::DUP1 && _instruction <= evmasm::Instruction::DUP16)
m_asm->append(evmasm::AssemblyItem(evmasm::Dup, static_cast<unsigned>(_instruction) - static_cast<unsigned>(evmasm::Instruction::DUP1) + 1));
else
m_asm->append(_instruction);
return *this;
}
CompilerContext& operator<<(u256 const& _value) { m_asm->append(_value); return *this; }
CompilerContext& operator<<(bytes const& _data) { m_asm->append(_data); return *this; }

View File

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

View File

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

View File

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

View File

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

View File

@ -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);
}

View File

@ -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.

View File

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

View File

@ -145,9 +145,16 @@ map<YulString, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion _evmVe
string name = toLower(instr.first);
auto const opcode = instr.second;
auto isSwapInstruction = [](auto i) {
return (i >= evmasm::Instruction::SWAP1 && i <= evmasm::Instruction::SWAP16) || i == evmasm::Instruction::SWAP_N;
};
auto isDupInstruction = [](auto i) {
return (i >= evmasm::Instruction::DUP1 && i <= evmasm::Instruction::DUP16) || i == evmasm::Instruction::DUP_N;
};
if (
!evmasm::isDupInstruction(opcode) &&
!evmasm::isSwapInstruction(opcode) &&
!isDupInstruction(opcode) &&
!isSwapInstruction(opcode) &&
!evmasm::isPushInstruction(opcode) &&
opcode != evmasm::Instruction::JUMP &&
opcode != evmasm::Instruction::JUMPI &&

View File

@ -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);

View File

@ -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;

View File

@ -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));

View File

@ -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;

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

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

View File

@ -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; }
};
}

View File

@ -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;