Skip to content

Commit 436f00e

Browse files
committed
eof: Support CALLF, JUMPF and RETF
1 parent 56b3d7b commit 436f00e

24 files changed

+445
-29
lines changed

libevmasm/Assembly.cpp

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -407,9 +407,18 @@ void Assembly::assemblyStream(
407407
f.feed(i, _debugInfoSelection);
408408
f.flush();
409409

410-
// Implementing this requires introduction of CALLF, RETF and JUMPF
411410
if (m_codeSections.size() > 1)
412-
solUnimplemented("Add support for more code sections");
411+
{
412+
for (size_t i = 1; i < m_codeSections.size(); ++i)
413+
{
414+
_out << std::endl << _prefix << "code_section_" << i << ": assembly {\n";
415+
Functionalizer codeSectionF(_out, _prefix + " ", _sourceCodes, *this);
416+
for (auto const& item: m_codeSections[i].items)
417+
codeSectionF.feed(item, _debugInfoSelection);
418+
codeSectionF.flush();
419+
_out << _prefix << "}" << std::endl;
420+
}
421+
}
413422

414423
if (!m_data.empty() || !m_subs.empty())
415424
{
@@ -1415,7 +1424,10 @@ LinkerObject const& Assembly::assembleEOF() const
14151424
item.instruction() != Instruction::RETURNCONTRACT &&
14161425
item.instruction() != Instruction::EOFCREATE &&
14171426
item.instruction() != Instruction::RJUMP &&
1418-
item.instruction() != Instruction::RJUMPI
1427+
item.instruction() != Instruction::RJUMPI &&
1428+
item.instruction() != Instruction::CALLF &&
1429+
item.instruction() != Instruction::JUMPF &&
1430+
item.instruction() != Instruction::RETF
14191431
);
14201432
solAssert(!(item.instruction() >= Instruction::PUSH0 && item.instruction() <= Instruction::PUSH32));
14211433
ret.bytecode += assembleOperation(item);
@@ -1470,6 +1482,25 @@ LinkerObject const& Assembly::assembleEOF() const
14701482
appendBigEndianUint16(ret.bytecode, item.data());
14711483
break;
14721484
}
1485+
case CallF:
1486+
{
1487+
ret.bytecode.push_back(static_cast<uint8_t>(Instruction::CALLF));
1488+
solAssert(item.data() <= std::numeric_limits<uint16_t>::max(), "Invalid callf index value.");
1489+
appendBigEndianUint16(ret.bytecode, item.data());
1490+
break;
1491+
}
1492+
case JumpF:
1493+
{
1494+
ret.bytecode.push_back(static_cast<uint8_t>(Instruction::JUMPF));
1495+
solAssert(item.data() <= std::numeric_limits<uint16_t>::max(), "Invalid jumpf index value.");
1496+
appendBigEndianUint16(ret.bytecode, item.data());
1497+
break;
1498+
}
1499+
case RetF:
1500+
{
1501+
ret.bytecode.push_back(static_cast<uint8_t>(Instruction::RETF));
1502+
break;
1503+
}
14731504
default:
14741505
solThrow(InvalidOpcode, "Unexpected opcode while assembling.");
14751506
}

libevmasm/Assembly.h

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,50 @@ class Assembly
6565
}
6666

6767
std::optional<uint8_t> eofVersion() const { return m_eofVersion; }
68+
bool supportsFunctions() const { return m_eofVersion.has_value(); }
6869
bool supportsRelativeJumps() const { return m_eofVersion.has_value(); }
6970
AssemblyItem newTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(Tag, m_usedTags++); }
7071
AssemblyItem newPushTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(PushTag, m_usedTags++); }
72+
AssemblyItem newFunctionCall(uint16_t _functionID)
73+
{
74+
solAssert(_functionID < m_codeSections.size(), "Call to undeclared function.");
75+
solAssert(_functionID > 0, "Cannot call call section 0");
76+
auto const& section = m_codeSections.at(_functionID);
77+
if (section.outputs != 0x80)
78+
return AssemblyItem::functionCall(_functionID, section.inputs, section.outputs);
79+
else
80+
return AssemblyItem::jumpF(_functionID, section.inputs);
81+
}
82+
83+
AssemblyItem newFunctionReturn()
84+
{
85+
return AssemblyItem::functionReturn(m_codeSections.at(m_currentCodeSection).outputs);
86+
}
87+
88+
uint16_t createFunction(uint8_t _args, uint8_t _rets)
89+
{
90+
size_t functionID = m_codeSections.size();
91+
solAssert(functionID < 1024, "Too many functions.");
92+
solAssert(m_currentCodeSection == 0, "Functions need to be declared from the main block.");
93+
solAssert(_rets <= 0x80, "Too many function returns.");
94+
m_codeSections.emplace_back(CodeSection{_args, _rets, {}});
95+
return static_cast<uint16_t>(functionID);
96+
}
97+
98+
void beginFunction(uint16_t _functionID)
99+
{
100+
solAssert(m_currentCodeSection == 0, "Atempted to begin a function before ending the last one.");
101+
solAssert(_functionID < m_codeSections.size(), "Attempt to begin an undeclared function.");
102+
auto& section = m_codeSections.at(_functionID);
103+
solAssert(section.items.empty(), "Function already defined.");
104+
m_currentCodeSection = _functionID;
105+
}
106+
void endFunction()
107+
{
108+
solAssert(m_currentCodeSection != 0, "End function without begin function.");
109+
m_currentCodeSection = 0;
110+
}
111+
71112
/// Returns a tag identified by the given name. Creates it if it does not yet exist.
72113
AssemblyItem namedTag(std::string const& _name, size_t _params, size_t _returns, std::optional<uint64_t> _sourceID);
73114
AssemblyItem newData(bytes const& _data) { util::h256 h(util::keccak256(util::asString(_data))); m_data[h] = _data; return AssemblyItem(PushData, h); }
@@ -111,6 +152,16 @@ class Assembly
111152
return append(AssemblyItem::returnContract(_containerId));
112153
}
113154

155+
AssemblyItem appendFunctionCall(uint16_t _functionID)
156+
{
157+
return append(newFunctionCall(_functionID));
158+
}
159+
160+
AssemblyItem appendFunctionReturn()
161+
{
162+
return append(newFunctionReturn());
163+
}
164+
114165
AssemblyItem appendJump() { auto ret = append(newPushTag()); append(Instruction::JUMP); return ret; }
115166
AssemblyItem appendJumpI() { auto ret = append(newPushTag()); append(Instruction::JUMPI); return ret; }
116167
AssemblyItem appendJump(AssemblyItem const& _tag) { auto ret = append(_tag.pushTag()); append(Instruction::JUMP); return ret; }

libevmasm/AssemblyItem.cpp

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ std::pair<std::string, std::string> AssemblyItem::nameAndData(langutil::EVMVersi
7878
case ReturnContract:
7979
case RelativeJump:
8080
case ConditionalRelativeJump:
81+
case CallF:
82+
case JumpF:
83+
case RetF:
8184
return {instructionInfo(instruction(), _evmVersion).name, m_data != nullptr ? toStringInHex(*m_data) : ""};
8285
case Push:
8386
return {"PUSH", toStringInHex(data())};
@@ -130,6 +133,7 @@ size_t AssemblyItem::bytesRequired(size_t _addressLength, langutil::EVMVersion _
130133
{
131134
case Operation:
132135
case Tag: // 1 byte for the JUMPDEST
136+
case RetF:
133137
return 1;
134138
case Push:
135139
return
@@ -172,6 +176,8 @@ size_t AssemblyItem::bytesRequired(size_t _addressLength, langutil::EVMVersion _
172176
case RelativeJump:
173177
case ConditionalRelativeJump:
174178
case AuxDataLoadN:
179+
case JumpF:
180+
case CallF:
175181
return 1 + 2;
176182
case EOFCreate:
177183
return 2;
@@ -186,14 +192,21 @@ size_t AssemblyItem::bytesRequired(size_t _addressLength, langutil::EVMVersion _
186192

187193
size_t AssemblyItem::arguments() const
188194
{
189-
if (hasInstruction())
195+
if (hasInstruction() && type() != CallF && type() != JumpF && type() != RetF)
190196
// The latest EVMVersion is used here, since the InstructionInfo is assumed to be
191197
// the same across all EVM versions except for the instruction name.
192198
return static_cast<size_t>(instructionInfo(instruction(), EVMVersion()).args);
193199
else if (type() == VerbatimBytecode)
194200
return std::get<0>(*m_verbatimBytecode);
195201
else if (type() == AssignImmutable)
196202
return 2;
203+
else if (type() == CallF || type() == JumpF)
204+
{
205+
solAssert(m_functionSignature.has_value());
206+
return (*m_functionSignature).argsNum;
207+
}
208+
else if (type() == RetF)
209+
return static_cast<size_t>(data());
197210
else
198211
return 0;
199212
}
@@ -226,6 +239,12 @@ size_t AssemblyItem::returnValues() const
226239
return std::get<1>(*m_verbatimBytecode);
227240
case AuxDataLoadN:
228241
return 1;
242+
case CallF:
243+
solAssert(m_functionSignature.has_value());
244+
return (*m_functionSignature).retsNum;
245+
case JumpF:
246+
case RetF:
247+
return 0;
229248
case AssignImmutable:
230249
case UndefinedItem:
231250
break;
@@ -244,6 +263,9 @@ bool AssemblyItem::canBeFunctional() const
244263
case ReturnContract:
245264
case RelativeJump:
246265
case ConditionalRelativeJump:
266+
case CallF:
267+
case JumpF:
268+
case RetF:
247269
return !isDupInstruction(instruction()) && !isSwapInstruction(instruction());
248270
case Push:
249271
case PushTag:
@@ -374,6 +396,15 @@ std::string AssemblyItem::toAssemblyText(Assembly const& _assembly) const
374396
case ConditionalRelativeJump:
375397
text = "rjumpi{" + std::string("tag_") + std::to_string(static_cast<size_t>(data())) + "}";
376398
break;
399+
case CallF:
400+
text = "callf{" + std::to_string(static_cast<size_t>(data())) + "}";
401+
break;
402+
case JumpF:
403+
text = "jumpf{" + std::to_string(static_cast<size_t>(data())) + "}";
404+
break;
405+
case RetF:
406+
text = "retf";
407+
break;
377408
}
378409
if (m_jumpType == JumpType::IntoFunction || m_jumpType == JumpType::OutOfFunction)
379410
{
@@ -396,6 +427,9 @@ std::ostream& solidity::evmasm::operator<<(std::ostream& _out, AssemblyItem cons
396427
case ReturnContract:
397428
case RelativeJump:
398429
case ConditionalRelativeJump:
430+
case CallF:
431+
case JumpF:
432+
case RetF:
399433
_out << " " << instructionInfo(_item.instruction(), EVMVersion()).name;
400434
if (_item.instruction() == Instruction::JUMP || _item.instruction() == Instruction::JUMPI)
401435
_out << "\t" << _item.getJumpTypeAsString();

libevmasm/AssemblyItem.h

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ enum AssemblyItemType
5959
ReturnContract, ///< Returns new container (with auxiliary data filled in) to be deployed
6060
RelativeJump, ///< Jumps to relative position accordingly to its argument
6161
ConditionalRelativeJump, ///< Same as RelativeJump but takes condition from the stack
62+
CallF, ///< Calls a function under function ID. Function is returning.
63+
JumpF, ///< Calls a function under function ID. Function is non-returning.
64+
RetF, ///< Returns from returning function to the caller.
6265
VerbatimBytecode ///< Contains data that is inserted into the bytecode code section without modification.
6366
};
6467

@@ -105,6 +108,25 @@ class AssemblyItem
105108
m_debugData{langutil::DebugData::create()}
106109
{}
107110

111+
static AssemblyItem functionCall(uint16_t _functionID, uint8_t _args, uint8_t _rets, langutil::DebugData::ConstPtr _debugData = langutil::DebugData::create())
112+
{
113+
// TODO: Make this constructor this way that it's impossible to create it without setting functions signature.
114+
// It can be done by template constructor with Instruction as template parameter i.e. Same for JumpF below.
115+
AssemblyItem result(CallF, Instruction::CALLF, _functionID, _debugData);
116+
result.m_functionSignature = {_args, _rets};
117+
return result;
118+
}
119+
static AssemblyItem jumpF(uint16_t _functionID, uint8_t _args, langutil::DebugData::ConstPtr _debugData = langutil::DebugData::create())
120+
{
121+
AssemblyItem result(JumpF, Instruction::JUMPF, _functionID, _debugData);
122+
result.m_functionSignature = {_args, 0};
123+
return result;
124+
}
125+
static AssemblyItem functionReturn(uint8_t _rets, langutil::DebugData::ConstPtr _debugData = langutil::DebugData::create())
126+
{
127+
return AssemblyItem(RetF, Instruction::RETF, _rets, _debugData);
128+
}
129+
108130
static AssemblyItem eofCreate(ContainerID _containerID, langutil::DebugData::ConstPtr _debugData = langutil::DebugData::create())
109131
{
110132
return AssemblyItem(EOFCreate, Instruction::EOFCREATE, _containerID, std::move(_debugData));
@@ -162,7 +184,10 @@ class AssemblyItem
162184
m_type == EOFCreate ||
163185
m_type == ReturnContract ||
164186
m_type == RelativeJump ||
165-
m_type == ConditionalRelativeJump;
187+
m_type == ConditionalRelativeJump ||
188+
m_type == CallF ||
189+
m_type == JumpF ||
190+
m_type == RetF;
166191
}
167192
/// @returns the instruction of this item (only valid if hasInstruction returns true)
168193
Instruction instruction() const
@@ -267,6 +292,13 @@ class AssemblyItem
267292
AssemblyItemType m_type;
268293
Instruction m_instruction; ///< Only valid if m_type == Operation
269294
std::shared_ptr<u256> m_data; ///< Only valid if m_type != Operation
295+
296+
struct FunctionSignature
297+
{
298+
uint8_t argsNum;
299+
uint8_t retsNum;
300+
};
301+
std::optional<FunctionSignature> m_functionSignature; ///< Only valid if m_type == CallF or JumpF
270302
/// If m_type == VerbatimBytecode, this holds number of arguments, number of
271303
/// return variables and verbatim bytecode.
272304
std::optional<std::tuple<size_t, size_t, bytes>> m_verbatimBytecode;

libevmasm/Instruction.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,9 @@ std::map<std::string, Instruction> const solidity::evmasm::c_instructions =
169169
{ "LOG3", Instruction::LOG3 },
170170
{ "LOG4", Instruction::LOG4 },
171171
{ "DATALOADN", Instruction::DATALOADN },
172+
{ "CALLF", Instruction::CALLF },
173+
{ "RETF", Instruction::RETF },
174+
{ "JUMPF", Instruction::JUMPF },
172175
{ "RJUMP", Instruction::RJUMP },
173176
{ "RJUMPI", Instruction::RJUMPI },
174177
{ "EOFCREATE", Instruction::EOFCREATE },
@@ -330,6 +333,9 @@ static std::map<Instruction, InstructionInfo> const c_instructionInfo =
330333
{Instruction::LOG2, {"LOG2", 0, 4, 0, true, Tier::Special}},
331334
{Instruction::LOG3, {"LOG3", 0, 5, 0, true, Tier::Special}},
332335
{Instruction::LOG4, {"LOG4", 0, 6, 0, true, Tier::Special}},
336+
{Instruction::RETF, {"RETF", 0, 0, 0, true, Tier::Special}},
337+
{Instruction::CALLF, {"CALLF", 2, 0, 0, true, Tier::Special}},
338+
{Instruction::JUMPF, {"JUMPF", 2, 0, 0, true, Tier::Special}},
333339
{Instruction::EOFCREATE, {"EOFCREATE", 1, 4, 1, true, Tier::Special}},
334340
{Instruction::RETURNCONTRACT, {"RETURNCONTRACT", 1, 2, 0, true, Tier::Special}},
335341
{Instruction::CREATE, {"CREATE", 0, 3, 1, true, Tier::Special}},

libevmasm/Instruction.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,9 @@ enum class Instruction: uint8_t
186186

187187
RJUMP = 0xe0, ///< relative jump
188188
RJUMPI = 0xe1, ///< conditional relative jump
189+
CALLF = 0xe3, ///< call function in a EOF code section
190+
RETF = 0xe4, ///< return to caller from the code section of EOF continer
191+
JUMPF = 0xe5, ///< jump to a code section of EOF contaner. No stack cleaning.
189192
EOFCREATE = 0xec, ///< create a new account with associated container code.
190193
RETURNCONTRACT = 0xee, ///< return container to be deployed with axiliary data filled in.
191194
CREATE = 0xf0, ///< create a new account with associated code

libevmasm/SemanticInformation.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,9 @@ bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item, bool
231231
case PushDeployTimeAddress:
232232
case AssignImmutable:
233233
case VerbatimBytecode:
234+
case CallF:
235+
case JumpF:
236+
case RetF:
234237
return true;
235238
case Push:
236239
case PushTag:
@@ -331,6 +334,9 @@ bool SemanticInformation::altersControlFlow(AssemblyItem const& _item)
331334
case Instruction::INVALID:
332335
case Instruction::REVERT:
333336
case Instruction::RETURNCONTRACT:
337+
case Instruction::CALLF:
338+
case Instruction::JUMPF:
339+
case Instruction::RETF:
334340
return true;
335341
default:
336342
return false;
@@ -397,6 +403,7 @@ bool SemanticInformation::isDeterministic(AssemblyItem const& _item)
397403
case Instruction::RETURNDATACOPY: // depends on previous calls
398404
case Instruction::RETURNDATASIZE:
399405
case Instruction::EOFCREATE:
406+
case Instruction::CALLF:
400407
return false;
401408
default:
402409
return true;

liblangutil/EVMVersion.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,9 @@ bool EVMVersion::hasOpcode(Instruction _opcode, std::optional<uint8_t> _eofVersi
8282
case Instruction::DATALOADN:
8383
case Instruction::RJUMP:
8484
case Instruction::RJUMPI:
85+
case Instruction::CALLF:
86+
case Instruction::JUMPF:
87+
case Instruction::RETF:
8588
return _eofVersion.has_value();
8689
default:
8790
return true;

libyul/backends/evm/AbstractAssembly.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ class AbstractAssembly
5757
using LabelID = size_t;
5858
using SubID = size_t;
5959
using ContainerID = uint8_t;
60+
using FunctionID = uint16_t;
6061
enum class JumpType { Ordinary, IntoFunction, OutOfFunction };
6162

6263
virtual ~AbstractAssembly() = default;
@@ -101,6 +102,19 @@ class AbstractAssembly
101102
virtual void appendAssemblySize() = 0;
102103
/// Creates a new sub-assembly, which can be referenced using dataSize and dataOffset.
103104
virtual std::pair<std::shared_ptr<AbstractAssembly>, SubID> createSubAssembly(bool _creation, std::string _name = "") = 0;
105+
106+
/// Creates new function with given signature and returns newly created function ID
107+
virtual FunctionID createFunction(uint8_t _args, uint8_t _rets) = 0;
108+
/// Starts filling function body under given function ID
109+
virtual void beginFunction(FunctionID _functionID) = 0;
110+
/// Ends currently being filled function
111+
virtual void endFunction() = 0;
112+
113+
/// Appends function call to a function under given ID
114+
virtual void appendFunctionCall(FunctionID _functionID) = 0;
115+
/// Appends function return from currently being filled function.
116+
virtual void appendFunctionReturn() = 0;
117+
104118
/// Appends the offset of the given sub-assembly or data.
105119
virtual void appendDataOffset(std::vector<SubID> const& _subPath) = 0;
106120
/// Appends the size of the given sub-assembly or data.

0 commit comments

Comments
 (0)