Skip to content

Commit 68c4aa3

Browse files
committed
Add SWAPN/DUPN.
1 parent 65e0fd7 commit 68c4aa3

22 files changed

+155
-38
lines changed

libevmasm/Assembly.cpp

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -767,11 +767,21 @@ AssemblyItem Assembly::newImmutableAssignment(std::string const& _identifier)
767767
return AssemblyItem{AssignImmutable, h};
768768
}
769769

770-
AssemblyItem Assembly::newAuxDataLoadN(size_t _offset)
770+
AssemblyItem Assembly::newAuxDataLoadN(size_t _offset) const
771771
{
772772
return AssemblyItem{AuxDataLoadN, _offset};
773773
}
774774

775+
AssemblyItem Assembly::newSwapN(size_t _depth) const
776+
{
777+
return AssemblyItem::swapN(_depth);
778+
}
779+
780+
AssemblyItem Assembly::newDupN(size_t _depth) const
781+
{
782+
return AssemblyItem::dupN(_depth);
783+
}
784+
775785
Assembly& Assembly::optimise(OptimiserSettings const& _settings)
776786
{
777787
optimiseInternal(_settings, {});
@@ -1558,7 +1568,9 @@ LinkerObject const& Assembly::assembleEOF() const
15581568
item.instruction() != Instruction::RJUMPI &&
15591569
item.instruction() != Instruction::CALLF &&
15601570
item.instruction() != Instruction::JUMPF &&
1561-
item.instruction() != Instruction::RETF
1571+
item.instruction() != Instruction::RETF &&
1572+
item.instruction() != Instruction::DUPN &&
1573+
item.instruction() != Instruction::SWAPN
15621574
);
15631575
solAssert(!(item.instruction() >= Instruction::PUSH0 && item.instruction() <= Instruction::PUSH32));
15641576
ret.bytecode += assembleOperation(item);
@@ -1636,6 +1648,12 @@ LinkerObject const& Assembly::assembleEOF() const
16361648
case RetF:
16371649
ret.bytecode.push_back(static_cast<uint8_t>(Instruction::RETF));
16381650
break;
1651+
case SwapN:
1652+
case DupN:
1653+
ret.bytecode.push_back(static_cast<uint8_t>(item.instruction()));
1654+
solAssert(item.data() >= 1 && item.data() <= 256);
1655+
ret.bytecode.push_back(static_cast<uint8_t>(item.data() - 1));
1656+
break;
16391657
default:
16401658
solAssert(false, "Unexpected opcode while assembling.");
16411659
}

libevmasm/Assembly.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,9 @@ class Assembly
8888
AssemblyItem newPushLibraryAddress(std::string const& _identifier);
8989
AssemblyItem newPushImmutable(std::string const& _identifier);
9090
AssemblyItem newImmutableAssignment(std::string const& _identifier);
91-
AssemblyItem newAuxDataLoadN(size_t offset);
91+
AssemblyItem newAuxDataLoadN(size_t offset) const;
92+
AssemblyItem newSwapN(size_t _depth) const;
93+
AssemblyItem newDupN(size_t _depth) const;
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(size_t _depth) { append(newSwapN(_depth)); }
108+
void appendDupN(size_t _depth) { append(newDupN(_depth)); }
105109

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

libevmasm/AssemblyItem.cpp

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <libevmasm/AssemblyItem.h>
2020

2121
#include <libevmasm/Assembly.h>
22+
#include <libevmasm/SemanticInformation.h>
2223

2324
#include <libsolutil/CommonData.h>
2425
#include <libsolutil/CommonIO.h>
@@ -90,6 +91,9 @@ std::pair<std::string, std::string> AssemblyItem::nameAndData(langutil::EVMVersi
9091
case JumpF:
9192
case RetF:
9293
return {instructionInfo(instruction(), _evmVersion).name, ""};
94+
case SwapN:
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()) + 1;
216+
else if (type() == DupN)
217+
return static_cast<size_t>(data());
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()) + 1;
234249
case Push:
235250
case PushTag:
236251
case PushData:
@@ -270,8 +285,10 @@ bool AssemblyItem::canBeFunctional() const
270285
case ConditionalRelativeJump:
271286
case CallF:
272287
case JumpF:
288+
case SwapN:
289+
case DupN:
273290
case RetF:
274-
return !isDupInstruction(instruction()) && !isSwapInstruction(instruction());
291+
return !SemanticInformation::isDupInstruction(*this) && !SemanticInformation::isSwapInstruction(*this);
275292
case Push:
276293
case PushTag:
277294
case PushData:
@@ -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: 16 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,16 @@ class AssemblyItem
147149
solAssert(_tag.type() == Tag);
148150
return AssemblyItem(ConditionalRelativeJump, Instruction::RJUMPI, _tag.data(), _debugData);
149151
}
152+
static AssemblyItem swapN(size_t _depth, langutil::DebugData::ConstPtr _debugData = langutil::DebugData::create())
153+
{
154+
solAssert(_depth >= 1 && _depth <= 256);
155+
return AssemblyItem(SwapN, Instruction::SWAPN, _depth, _debugData);
156+
}
157+
static AssemblyItem dupN(size_t _depth, langutil::DebugData::ConstPtr _debugData = langutil::DebugData::create())
158+
{
159+
solAssert(_depth >= 1 && _depth <= 256);
160+
return AssemblyItem(DupN, Instruction::DUPN, _depth, _debugData);
161+
}
150162

151163
AssemblyItem(AssemblyItem const&) = default;
152164
AssemblyItem(AssemblyItem&&) = default;
@@ -191,7 +203,9 @@ class AssemblyItem
191203
m_type == ConditionalRelativeJump ||
192204
m_type == CallF ||
193205
m_type == JumpF ||
194-
m_type == RetF;
206+
m_type == RetF ||
207+
m_type == SwapN ||
208+
m_type == DupN;
195209
}
196210
/// @returns the instruction of this item (only valid if hasInstruction returns true)
197211
Instruction instruction() const

libevmasm/Instruction.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,8 @@ std::map<std::string, Instruction> const solidity::evmasm::c_instructions =
172172
{ "CALLF", Instruction::CALLF },
173173
{ "RETF", Instruction::RETF },
174174
{ "JUMPF", Instruction::JUMPF },
175+
{ "DUPN", Instruction::DUPN },
176+
{ "SWAPN", Instruction::SWAPN },
175177
{ "RJUMP", Instruction::RJUMP },
176178
{ "RJUMPI", Instruction::RJUMPI },
177179
{ "EOFCREATE", Instruction::EOFCREATE },
@@ -339,6 +341,8 @@ static std::map<Instruction, InstructionInfo> const c_instructionInfo =
339341
{Instruction::RETF, {"RETF", 0, 0, 0, true, Tier::RetF}},
340342
{Instruction::CALLF, {"CALLF", 2, 0, 0, true, Tier::CallF}},
341343
{Instruction::JUMPF, {"JUMPF", 2, 0, 0, true, Tier::JumpF}},
344+
{Instruction::SWAPN, {"SWAPN", 1, 0, 0, false, Tier::VeryLow}},
345+
{Instruction::DUPN, {"DUPN", 1, 0, 0, false, Tier::VeryLow}},
342346
{Instruction::EOFCREATE, {"EOFCREATE", 1, 4, 1, true, Tier::Special}},
343347
{Instruction::RETURNCONTRACT, {"RETURNCONTRACT", 1, 2, 0, true, Tier::Special}},
344348
{Instruction::CREATE, {"CREATE", 0, 3, 1, true, Tier::Special}},

libevmasm/Instruction.h

Lines changed: 2 additions & 24 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
@@ -232,18 +234,6 @@ inline bool isPushInstruction(Instruction _inst)
232234
return Instruction::PUSH0 <= _inst && _inst <= Instruction::PUSH32;
233235
}
234236

235-
/// @returns true if the instruction is a DUP
236-
inline bool isDupInstruction(Instruction _inst)
237-
{
238-
return Instruction::DUP1 <= _inst && _inst <= Instruction::DUP16;
239-
}
240-
241-
/// @returns true if the instruction is a SWAP
242-
inline bool isSwapInstruction(Instruction _inst)
243-
{
244-
return Instruction::SWAP1 <= _inst && _inst <= Instruction::SWAP16;
245-
}
246-
247237
/// @returns true if the instruction is a LOG
248238
inline bool isLogInstruction(Instruction _inst)
249239
{
@@ -256,18 +246,6 @@ inline unsigned getPushNumber(Instruction _inst)
256246
return static_cast<uint8_t>(_inst) - unsigned(Instruction::PUSH0);
257247
}
258248

259-
/// @returns the number of DUP Instruction _inst
260-
inline unsigned getDupNumber(Instruction _inst)
261-
{
262-
return static_cast<uint8_t>(_inst) - unsigned(Instruction::DUP1) + 1;
263-
}
264-
265-
/// @returns the number of SWAP Instruction _inst
266-
inline unsigned getSwapNumber(Instruction _inst)
267-
{
268-
return static_cast<uint8_t>(_inst) - unsigned(Instruction::SWAP1) + 1;
269-
}
270-
271249
/// @returns the number of LOG Instruction _inst
272250
inline unsigned getLogNumber(Instruction _inst)
273251
{

libevmasm/KnownState.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,14 +137,14 @@ KnownState::StoreOperation KnownState::feedItem(AssemblyItem const& _item, bool
137137
setStackElement(
138138
m_stackHeight + 1,
139139
stackElement(
140-
m_stackHeight - static_cast<int>(instruction) + static_cast<int>(Instruction::DUP1),
140+
m_stackHeight - (static_cast<int>(SemanticInformation::getDupNumber(_item)) - 1),
141141
_item.debugData()
142142
)
143143
);
144144
else if (SemanticInformation::isSwapInstruction(_item))
145145
swapStackElements(
146146
m_stackHeight,
147-
m_stackHeight - 1 - static_cast<int>(instruction) + static_cast<int>(Instruction::SWAP1),
147+
m_stackHeight - 1 - (static_cast<int>(SemanticInformation::getSwapNumber(_item)) - 1),
148148
_item.debugData()
149149
);
150150
else if (instruction != Instruction::POP)

libevmasm/PeepholeOptimiser.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ struct DupSwap: SimplePeepholeOptimizerMethod<DupSwap>
290290
if (
291291
SemanticInformation::isDupInstruction(_dupN) &&
292292
SemanticInformation::isSwapInstruction(_swapN) &&
293-
getDupNumber(_dupN.instruction()) == getSwapNumber(_swapN.instruction())
293+
SemanticInformation::getDupNumber(_dupN) == SemanticInformation::getSwapNumber(_swapN)
294294
)
295295
{
296296
*_out = _dupN;

libevmasm/SemanticInformation.cpp

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -312,16 +312,22 @@ 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;
317-
return evmasm::isDupInstruction(_item.instruction());
319+
auto inst = _item.instruction();
320+
return Instruction::DUP1 <= inst && inst <= Instruction::DUP16;
318321
}
319322

320323
bool SemanticInformation::isSwapInstruction(AssemblyItem const& _item)
321324
{
325+
if (_item.type() == evmasm::SwapN)
326+
return true;
322327
if (_item.type() != evmasm::Operation)
323328
return false;
324-
return evmasm::isSwapInstruction(_item.instruction());
329+
auto inst = _item.instruction();
330+
return Instruction::SWAP1 <= inst && inst <= Instruction::SWAP16;
325331
}
326332

327333
bool SemanticInformation::altersControlFlow(AssemblyItem const& _item)
@@ -387,6 +393,24 @@ bool SemanticInformation::reverts(Instruction _instruction)
387393
}
388394
}
389395

396+
size_t SemanticInformation::getDupNumber(AssemblyItem const& _item)
397+
{
398+
assertThrow(isDupInstruction(_item), OptimizerException, "Not a DUP instruction.");
399+
if (_item.type() == evmasm::DupN)
400+
return static_cast<size_t>(_item.data());
401+
auto inst = _item.instruction();
402+
return static_cast<uint8_t>(inst) - static_cast<size_t>(Instruction::DUP1) + 1;
403+
}
404+
405+
size_t SemanticInformation::getSwapNumber(AssemblyItem const& _item)
406+
{
407+
assertThrow(isSwapInstruction(_item), OptimizerException, "Not a swap instruction.");
408+
if (_item.type() == evmasm::SwapN)
409+
return static_cast<size_t>(_item.data());
410+
auto inst = _item.instruction();
411+
return static_cast<uint8_t>(inst) - static_cast<size_t>(Instruction::SWAP1) + 1;
412+
}
413+
390414
bool SemanticInformation::isDeterministic(AssemblyItem const& _item)
391415
{
392416
assertThrow(_item.type() != VerbatimBytecode, AssemblyException, "");

libevmasm/SemanticInformation.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@ struct SemanticInformation
8686
static bool terminatesControlFlow(AssemblyItem const& _item);
8787
static bool terminatesControlFlow(Instruction _instruction);
8888
static bool reverts(Instruction _instruction);
89+
/// @returns the 1-based DUP depth a DUP AssembleItem @a _item
90+
static size_t getDupNumber(AssemblyItem const& _item);
91+
/// @returns the 1-based SWAP depth a DUP AssembleItem @a _item
92+
static size_t getSwapNumber(AssemblyItem const& _item);
8993
/// @returns false if the value put on the stack by _item depends on anything else than
9094
/// the information in the current block header, memory, storage, transient storage or stack.
9195
static bool isDeterministic(AssemblyItem const& _item);

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/AsmAnalysis.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -741,7 +741,9 @@ bool AsmAnalyzer::validateInstructions(evmasm::Instruction _instr, SourceLocatio
741741
_instr != evmasm::Instruction::RJUMPI &&
742742
_instr != evmasm::Instruction::CALLF &&
743743
_instr != evmasm::Instruction::JUMPF &&
744-
_instr != evmasm::Instruction::RETF
744+
_instr != evmasm::Instruction::RETF &&
745+
_instr != evmasm::Instruction::DUPN &&
746+
_instr != evmasm::Instruction::SWAPN
745747
);
746748

747749
auto errorForVM = [&](ErrorId _errorId, std::string const& vmKindMessage) {

libyul/backends/evm/AbstractAssembly.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,13 @@ 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+
/// Appends a SWAPN with 1-based indexing for @arg _depth. I.e. a depth of 1 would be equivalent to emitting SWAP1.
153+
/// @arg _depth ranges from 1 to 256.
154+
virtual void appendSwapN(size_t _depth) = 0;
155+
/// Appends a DUPN with 1-based indexing for @arg _depth. I.e. a depth of 1 would be equivalent to emitting DUP1.
156+
/// @arg _depth ranges from 1 to 256.
157+
virtual void appendDupN(size_t _depth) = 0;
158+
152159
/// Mark this assembly as invalid. Any attempt to request bytecode from it should throw.
153160
virtual void markAsInvalid() = 0;
154161

libyul/backends/evm/EVMDialect.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,8 +211,8 @@ std::vector<std::optional<BuiltinFunctionForEVM>> createBuiltins(langutil::EVMVe
211211
auto const opcode = instr.second;
212212

213213
if (
214-
!evmasm::isDupInstruction(opcode) &&
215-
!evmasm::isSwapInstruction(opcode) &&
214+
!(opcode >= evmasm::Instruction::DUP1 && opcode <= evmasm::Instruction::DUP16) &&
215+
!(opcode >= evmasm::Instruction::SWAP1 && opcode <= evmasm::Instruction::SWAP16) &&
216216
!evmasm::isPushInstruction(opcode) &&
217217
opcode != evmasm::Instruction::JUMP &&
218218
opcode != evmasm::Instruction::JUMPI &&
@@ -224,6 +224,8 @@ std::vector<std::optional<BuiltinFunctionForEVM>> createBuiltins(langutil::EVMVe
224224
opcode != evmasm::Instruction::RJUMPI &&
225225
opcode != evmasm::Instruction::CALLF &&
226226
opcode != evmasm::Instruction::JUMPF &&
227+
opcode != evmasm::Instruction::DUPN &&
228+
opcode != evmasm::Instruction::SWAPN &&
227229
opcode != evmasm::Instruction::RETF &&
228230
_evmVersion.hasOpcode(opcode, _eofVersion) &&
229231
!prevRandaoException(name)

0 commit comments

Comments
 (0)