Skip to content

Commit 606ebe5

Browse files
committed
Add SWAPN/DUPN.
1 parent c315294 commit 606ebe5

File tree

14 files changed

+99
-2
lines changed

14 files changed

+99
-2
lines changed

libevmasm/Assembly.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -772,6 +772,16 @@ AssemblyItem Assembly::newAuxDataLoadN(size_t _offset)
772772
return AssemblyItem{AuxDataLoadN, _offset};
773773
}
774774

775+
AssemblyItem Assembly::newSwapN(uint8_t _depth)
776+
{
777+
return AssemblyItem::swapn(_depth);
778+
}
779+
780+
AssemblyItem Assembly::newDupN(uint8_t _depth)
781+
{
782+
return AssemblyItem::dupn(_depth);
783+
}
784+
775785
Assembly& Assembly::optimise(OptimiserSettings const& _settings)
776786
{
777787
optimiseInternal(_settings, {});
@@ -1636,6 +1646,16 @@ LinkerObject const& Assembly::assembleEOF() const
16361646
case RetF:
16371647
ret.bytecode.push_back(static_cast<uint8_t>(Instruction::RETF));
16381648
break;
1649+
case SwapN:
1650+
ret.bytecode.push_back(static_cast<uint8_t>(Instruction::SWAPN));
1651+
solAssert(item.data() < 256);
1652+
ret.bytecode.push_back(static_cast<uint8_t>(item.data()));
1653+
break;
1654+
case DupN:
1655+
ret.bytecode.push_back(static_cast<uint8_t>(Instruction::DUPN));
1656+
solAssert(item.data() < 256);
1657+
ret.bytecode.push_back(static_cast<uint8_t>(item.data()));
1658+
break;
16391659
default:
16401660
solAssert(false, "Unexpected opcode while assembling.");
16411661
}

libevmasm/Assembly.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ class Assembly
8989
AssemblyItem newPushImmutable(std::string const& _identifier);
9090
AssemblyItem newImmutableAssignment(std::string const& _identifier);
9191
AssemblyItem newAuxDataLoadN(size_t offset);
92+
AssemblyItem newSwapN(uint8_t _depth);
93+
AssemblyItem newDupN(uint8_t _depth);
9294

9395
AssemblyItem const& append(AssemblyItem _i);
9496
AssemblyItem const& append(bytes const& _data) { return append(newData(_data)); }
@@ -102,6 +104,8 @@ class Assembly
102104
void appendImmutable(std::string const& _identifier) { append(newPushImmutable(_identifier)); }
103105
void appendImmutableAssignment(std::string const& _identifier) { append(newImmutableAssignment(_identifier)); }
104106
void appendAuxDataLoadN(uint16_t _offset) { append(newAuxDataLoadN(_offset));}
107+
void appendSwapN(uint8_t _depth) { append(newSwapN(_depth)); }
108+
void appendDupN(uint8_t _depth) { append(newDupN(_depth)); }
105109

106110
void appendVerbatim(bytes _data, size_t _arguments, size_t _returnVariables)
107111
{

libevmasm/AssemblyItem.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,10 @@ std::pair<std::string, std::string> AssemblyItem::nameAndData(langutil::EVMVersi
9090
case JumpF:
9191
case RetF:
9292
return {instructionInfo(instruction(), _evmVersion).name, ""};
93+
case SwapN:
94+
return {instructionInfo(instruction(), _evmVersion).name, util::toString(static_cast<size_t>(data())) };
95+
case DupN:
96+
return {instructionInfo(instruction(), _evmVersion).name, util::toString(static_cast<size_t>(data())) };
9397
case Push:
9498
return {"PUSH", toStringInHex(data())};
9599
case PushTag:
@@ -192,6 +196,10 @@ size_t AssemblyItem::bytesRequired(size_t _addressLength, langutil::EVMVersion _
192196
return 2;
193197
case ReturnContract:
194198
return 2;
199+
case SwapN:
200+
return 2;
201+
case DupN:
202+
return 2;
195203
case UndefinedItem:
196204
solAssert(false);
197205
}
@@ -203,6 +211,10 @@ size_t AssemblyItem::arguments() const
203211
{
204212
if (type() == CallF || type() == JumpF)
205213
return functionSignature().argsNum;
214+
else if (type() == SwapN)
215+
return static_cast<size_t>(data()) + 2;
216+
else if (type() == DupN)
217+
return static_cast<size_t>(data()) + 1;
206218
else if (hasInstruction())
207219
{
208220
solAssert(instruction() != Instruction::CALLF && instruction() != Instruction::JUMPF);
@@ -231,6 +243,9 @@ size_t AssemblyItem::returnValues() const
231243
// The latest EVMVersion is used here, since the InstructionInfo is assumed to be
232244
// the same across all EVM versions except for the instruction name.
233245
return static_cast<size_t>(instructionInfo(instruction(), EVMVersion()).ret);
246+
case SwapN:
247+
case DupN:
248+
return static_cast<size_t>(data()) + 2;
234249
case Push:
235250
case PushTag:
236251
case PushData:
@@ -283,6 +298,8 @@ bool AssemblyItem::canBeFunctional() const
283298
case PushImmutable:
284299
case AuxDataLoadN:
285300
return true;
301+
case SwapN:
302+
case DupN:
286303
case Tag:
287304
return false;
288305
case AssignImmutable:
@@ -410,6 +427,12 @@ std::string AssemblyItem::toAssemblyText(Assembly const& _assembly) const
410427
case RetF:
411428
text = "retf";
412429
break;
430+
case SwapN:
431+
text = "swapn{" + std::to_string(static_cast<size_t>(data())) + "}";
432+
break;
433+
case DupN:
434+
text = "dupn{" + std::to_string(static_cast<size_t>(data())) + "}";
435+
break;
413436
}
414437
if (m_jumpType == JumpType::IntoFunction || m_jumpType == JumpType::OutOfFunction)
415438
{
@@ -435,6 +458,8 @@ std::ostream& solidity::evmasm::operator<<(std::ostream& _out, AssemblyItem cons
435458
case CallF:
436459
case JumpF:
437460
case RetF:
461+
case SwapN:
462+
case DupN:
438463
_out << " " << instructionInfo(_item.instruction(), EVMVersion()).name;
439464
if (_item.instruction() == Instruction::JUMP || _item.instruction() == Instruction::JUMPI)
440465
_out << "\t" << _item.getJumpTypeAsString();

libevmasm/AssemblyItem.h

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,9 @@ enum AssemblyItemType
6262
CallF, ///< Jumps to a returning EOF function, adding a new frame to the return stack.
6363
JumpF, ///< Jumps to a returning or non-returning EOF function without changing the return stack.
6464
RetF, ///< Returns from an EOF function, removing a frame from the return stack.
65-
VerbatimBytecode ///< Contains data that is inserted into the bytecode code section without modification.
65+
VerbatimBytecode, ///< Contains data that is inserted into the bytecode code section without modification.
66+
SwapN, ///< EOF SWAPN with immediate argument.
67+
DupN, ///< EOF DUPN with immediate argument.
6668
};
6769

6870
enum class Precision { Precise , Approximate };
@@ -147,6 +149,14 @@ class AssemblyItem
147149
solAssert(_tag.type() == Tag);
148150
return AssemblyItem(ConditionalRelativeJump, Instruction::RJUMPI, _tag.data(), _debugData);
149151
}
152+
static AssemblyItem swapn(uint8_t _depth, langutil::DebugData::ConstPtr _debugData = langutil::DebugData::create())
153+
{
154+
return AssemblyItem(SwapN, Instruction::SWAPN, _depth, _debugData);
155+
}
156+
static AssemblyItem dupn(uint8_t _depth, langutil::DebugData::ConstPtr _debugData = langutil::DebugData::create())
157+
{
158+
return AssemblyItem(DupN, Instruction::DUPN, _depth, _debugData);
159+
}
150160

151161
AssemblyItem(AssemblyItem const&) = default;
152162
AssemblyItem(AssemblyItem&&) = default;
@@ -191,7 +201,9 @@ class AssemblyItem
191201
m_type == ConditionalRelativeJump ||
192202
m_type == CallF ||
193203
m_type == JumpF ||
194-
m_type == RetF;
204+
m_type == RetF ||
205+
m_type == SwapN ||
206+
m_type == DupN;
195207
}
196208
/// @returns the instruction of this item (only valid if hasInstruction returns true)
197209
Instruction instruction() const

libevmasm/Instruction.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,8 @@ static std::map<Instruction, InstructionInfo> const c_instructionInfo =
339339
{Instruction::RETF, {"RETF", 0, 0, 0, true, Tier::RetF}},
340340
{Instruction::CALLF, {"CALLF", 2, 0, 0, true, Tier::CallF}},
341341
{Instruction::JUMPF, {"JUMPF", 2, 0, 0, true, Tier::JumpF}},
342+
{Instruction::SWAPN, {"SWAPN", 1, 0, 0, false, Tier::VeryLow}},
343+
{Instruction::DUPN, {"DUPN", 1, 0, 0, false, Tier::VeryLow}},
342344
{Instruction::EOFCREATE, {"EOFCREATE", 1, 4, 1, true, Tier::Special}},
343345
{Instruction::RETURNCONTRACT, {"RETURNCONTRACT", 1, 2, 0, true, Tier::Special}},
344346
{Instruction::CREATE, {"CREATE", 0, 3, 1, true, Tier::Special}},

libevmasm/Instruction.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,8 @@ enum class Instruction: uint8_t
190190
CALLF = 0xe3, ///< call function in a EOF code section
191191
RETF = 0xe4, ///< return to caller from the code section of EOF container
192192
JUMPF = 0xe5, ///< jump to a code section of EOF container without adding a new return stack frame.
193+
DUPN = 0xe6, ///< copies a value at the stack depth given as immediate argument to the top of the stack
194+
SWAPN = 0xe7, ///< swaps the highest value with a value at a stack depth given as immediate argument
193195
EOFCREATE = 0xec, ///< create a new account with associated container code.
194196
RETURNCONTRACT = 0xee, ///< return container to be deployed with axiliary data filled in.
195197
CREATE = 0xf0, ///< create a new account with associated code

libevmasm/SemanticInformation.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,13 +312,17 @@ bool SemanticInformation::isCommutativeOperation(AssemblyItem const& _item)
312312

313313
bool SemanticInformation::isDupInstruction(AssemblyItem const& _item)
314314
{
315+
if (_item.type() == evmasm::DupN)
316+
return true;
315317
if (_item.type() != evmasm::Operation)
316318
return false;
317319
return evmasm::isDupInstruction(_item.instruction());
318320
}
319321

320322
bool SemanticInformation::isSwapInstruction(AssemblyItem const& _item)
321323
{
324+
if (_item.type() == evmasm::SwapN)
325+
return true;
322326
if (_item.type() != evmasm::Operation)
323327
return false;
324328
return evmasm::isSwapInstruction(_item.instruction());

liblangutil/EVMVersion.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ bool EVMVersion::hasOpcode(Instruction _opcode, std::optional<uint8_t> _eofVersi
8484
case Instruction::RJUMPI:
8585
case Instruction::CALLF:
8686
case Instruction::JUMPF:
87+
case Instruction::DUPN:
88+
case Instruction::SWAPN:
8789
case Instruction::RETF:
8890
case Instruction::EXTCALL:
8991
case Instruction::EXTSTATICCALL:

libyul/backends/evm/AbstractAssembly.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,9 @@ class AbstractAssembly
149149
/// EOF auxiliary data in data section and the auxiliary data are different things.
150150
virtual void appendToAuxiliaryData(bytes const& _data) = 0;
151151

152+
virtual void appendSwapN(uint8_t _depth) = 0;
153+
virtual void appendDupN(uint8_t _depth) = 0;
154+
152155
/// Mark this assembly as invalid. Any attempt to request bytecode from it should throw.
153156
virtual void markAsInvalid() = 0;
154157

libyul/backends/evm/EthAssemblyAdapter.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,17 @@ void EthAssemblyAdapter::appendAuxDataLoadN(uint16_t _offset)
232232
m_assembly.appendAuxDataLoadN(_offset);
233233
}
234234

235+
void EthAssemblyAdapter::appendSwapN(uint8_t _depth)
236+
{
237+
m_assembly.appendSwapN(_depth);
238+
}
239+
240+
void EthAssemblyAdapter::appendDupN(uint8_t _depth)
241+
{
242+
m_assembly.appendDupN(_depth);
243+
}
244+
245+
235246
void EthAssemblyAdapter::markAsInvalid()
236247
{
237248
m_assembly.markAsInvalid();

0 commit comments

Comments
 (0)