aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorchriseth <chris@ethereum.org>2018-12-06 23:47:18 +0800
committerGitHub <noreply@github.com>2018-12-06 23:47:18 +0800
commite1e578d71e22577dc7313214143b81aa24f5ebab (patch)
treeb008fb966c8371a47d6610aa4c40ac8afe87ff19
parent0e8841005ca7bf93545a1bc76bd8fa0ed67cb32d (diff)
parentb7cfa499b0bd674b59284821e33349726cbc4299 (diff)
downloaddexon-solidity-e1e578d71e22577dc7313214143b81aa24f5ebab.tar.gz
dexon-solidity-e1e578d71e22577dc7313214143b81aa24f5ebab.tar.zst
dexon-solidity-e1e578d71e22577dc7313214143b81aa24f5ebab.zip
Merge pull request #5589 from ethereum/yulObjectCodegen
Yul object codegen
-rw-r--r--libsolidity/codegen/AsmCodeGen.cpp224
-rw-r--r--libsolidity/codegen/AsmCodeGen.h37
-rw-r--r--libsolidity/interface/AssemblyStack.cpp38
-rw-r--r--libsolidity/interface/AssemblyStack.h3
-rw-r--r--libyul/CMakeLists.txt1
-rw-r--r--libyul/backends/evm/AbstractAssembly.h10
-rw-r--r--libyul/backends/evm/EVMAssembly.cpp21
-rw-r--r--libyul/backends/evm/EVMAssembly.h4
-rw-r--r--libyul/backends/evm/EVMObjectCompiler.cpp55
-rw-r--r--libyul/backends/evm/EVMObjectCompiler.h43
-rw-r--r--test/boostTest.cpp7
-rw-r--r--test/libyul/ObjectCompilerTest.cpp134
-rw-r--r--test/libyul/ObjectCompilerTest.h69
-rw-r--r--test/libyul/objectCompiler/data.yul11
-rw-r--r--test/libyul/objectCompiler/namedObject.yul6
-rw-r--r--test/libyul/objectCompiler/namedObjectCode.yul13
-rw-r--r--test/libyul/objectCompiler/nested_optimizer.yul49
-rw-r--r--test/libyul/objectCompiler/simple.yul13
-rw-r--r--test/libyul/objectCompiler/simple_optimizer.yul23
-rw-r--r--test/libyul/objectCompiler/smoke.yul5
-rw-r--r--test/libyul/objectCompiler/subObject.yul21
-rw-r--r--test/libyul/objectCompiler/subSubObject.yul39
-rw-r--r--test/tools/CMakeLists.txt18
-rw-r--r--test/tools/isoltest.cpp12
24 files changed, 755 insertions, 101 deletions
diff --git a/libsolidity/codegen/AsmCodeGen.cpp b/libsolidity/codegen/AsmCodeGen.cpp
index 83dd08fb..dfcc900b 100644
--- a/libsolidity/codegen/AsmCodeGen.cpp
+++ b/libsolidity/codegen/AsmCodeGen.cpp
@@ -27,10 +27,13 @@
#include <libyul/backends/evm/EVMCodeTransform.h>
#include <libevmasm/Assembly.h>
+#include <libevmasm/AssemblyItem.h>
#include <libevmasm/Instruction.h>
#include <liblangutil/SourceLocation.h>
+#include <libdevcore/FixedHash.h>
+
#include <memory>
#include <functional>
@@ -40,97 +43,136 @@ using namespace langutil;
using namespace yul;
using namespace dev::solidity;
-class EthAssemblyAdapter: public AbstractAssembly
-{
-public:
- explicit EthAssemblyAdapter(eth::Assembly& _assembly):
- m_assembly(_assembly)
- {
- }
- virtual void setSourceLocation(SourceLocation const& _location) override
- {
- m_assembly.setSourceLocation(_location);
- }
- virtual int stackHeight() const override { return m_assembly.deposit(); }
- virtual void appendInstruction(solidity::Instruction _instruction) override
- {
- m_assembly.append(_instruction);
- }
- virtual void appendConstant(u256 const& _constant) override
- {
- m_assembly.append(_constant);
- }
- /// Append a label.
- virtual void appendLabel(LabelID _labelId) override
- {
- m_assembly.append(eth::AssemblyItem(eth::Tag, _labelId));
- }
- /// Append a label reference.
- virtual void appendLabelReference(LabelID _labelId) override
- {
- m_assembly.append(eth::AssemblyItem(eth::PushTag, _labelId));
- }
- virtual size_t newLabelId() override
- {
- return assemblyTagToIdentifier(m_assembly.newTag());
- }
- virtual size_t namedLabel(std::string const& _name) override
- {
- return assemblyTagToIdentifier(m_assembly.namedTag(_name));
- }
- virtual void appendLinkerSymbol(std::string const& _linkerSymbol) override
- {
- m_assembly.appendLibraryAddress(_linkerSymbol);
- }
- virtual void appendJump(int _stackDiffAfter) override
- {
- appendInstruction(solidity::Instruction::JUMP);
- m_assembly.adjustDeposit(_stackDiffAfter);
- }
- virtual void appendJumpTo(LabelID _labelId, int _stackDiffAfter) override
- {
- appendLabelReference(_labelId);
- appendJump(_stackDiffAfter);
- }
- virtual void appendJumpToIf(LabelID _labelId) override
- {
- appendLabelReference(_labelId);
- appendInstruction(solidity::Instruction::JUMPI);
- }
- virtual void appendBeginsub(LabelID, int) override
- {
- // TODO we could emulate that, though
- solAssert(false, "BEGINSUB not implemented for EVM 1.0");
- }
- /// Call a subroutine.
- virtual void appendJumpsub(LabelID, int, int) override
- {
- // TODO we could emulate that, though
- solAssert(false, "JUMPSUB not implemented for EVM 1.0");
- }
-
- /// Return from a subroutine.
- virtual void appendReturnsub(int, int) override
- {
- // TODO we could emulate that, though
- solAssert(false, "RETURNSUB not implemented for EVM 1.0");
- }
-
- virtual void appendAssemblySize() override
- {
- m_assembly.appendProgramSize();
- }
-
-private:
- static LabelID assemblyTagToIdentifier(eth::AssemblyItem const& _tag)
- {
- u256 id = _tag.data();
- solAssert(id <= std::numeric_limits<LabelID>::max(), "Tag id too large.");
- return LabelID(id);
- }
-
- eth::Assembly& m_assembly;
-};
+EthAssemblyAdapter::EthAssemblyAdapter(eth::Assembly& _assembly):
+ m_assembly(_assembly)
+{
+}
+
+void EthAssemblyAdapter::setSourceLocation(SourceLocation const& _location)
+{
+ m_assembly.setSourceLocation(_location);
+}
+
+int EthAssemblyAdapter::stackHeight() const
+{
+ return m_assembly.deposit();
+}
+
+void EthAssemblyAdapter::appendInstruction(solidity::Instruction _instruction)
+{
+ m_assembly.append(_instruction);
+}
+
+void EthAssemblyAdapter::appendConstant(u256 const& _constant)
+{
+ m_assembly.append(_constant);
+}
+
+void EthAssemblyAdapter::appendLabel(LabelID _labelId)
+{
+ m_assembly.append(eth::AssemblyItem(eth::Tag, _labelId));
+}
+
+void EthAssemblyAdapter::appendLabelReference(LabelID _labelId)
+{
+ m_assembly.append(eth::AssemblyItem(eth::PushTag, _labelId));
+}
+
+size_t EthAssemblyAdapter::newLabelId()
+{
+ return assemblyTagToIdentifier(m_assembly.newTag());
+}
+
+size_t EthAssemblyAdapter::namedLabel(std::string const& _name)
+{
+ return assemblyTagToIdentifier(m_assembly.namedTag(_name));
+}
+
+void EthAssemblyAdapter::appendLinkerSymbol(std::string const& _linkerSymbol)
+{
+ m_assembly.appendLibraryAddress(_linkerSymbol);
+}
+
+void EthAssemblyAdapter::appendJump(int _stackDiffAfter)
+{
+ appendInstruction(solidity::Instruction::JUMP);
+ m_assembly.adjustDeposit(_stackDiffAfter);
+}
+
+void EthAssemblyAdapter::appendJumpTo(LabelID _labelId, int _stackDiffAfter)
+{
+ appendLabelReference(_labelId);
+ appendJump(_stackDiffAfter);
+}
+
+void EthAssemblyAdapter::appendJumpToIf(LabelID _labelId)
+{
+ appendLabelReference(_labelId);
+ appendInstruction(solidity::Instruction::JUMPI);
+}
+
+void EthAssemblyAdapter::appendBeginsub(LabelID, int)
+{
+ // TODO we could emulate that, though
+ solAssert(false, "BEGINSUB not implemented for EVM 1.0");
+}
+
+void EthAssemblyAdapter::appendJumpsub(LabelID, int, int)
+{
+ // TODO we could emulate that, though
+ solAssert(false, "JUMPSUB not implemented for EVM 1.0");
+}
+
+void EthAssemblyAdapter::appendReturnsub(int, int)
+{
+ // TODO we could emulate that, though
+ solAssert(false, "RETURNSUB not implemented for EVM 1.0");
+}
+
+void EthAssemblyAdapter::appendAssemblySize()
+{
+ m_assembly.appendProgramSize();
+}
+
+pair<shared_ptr<AbstractAssembly>, AbstractAssembly::SubID> EthAssemblyAdapter::createSubAssembly()
+{
+ shared_ptr<eth::Assembly> assembly{make_shared<eth::Assembly>()};
+ auto sub = m_assembly.newSub(assembly);
+ return {make_shared<EthAssemblyAdapter>(*assembly), size_t(sub.data())};
+}
+
+void EthAssemblyAdapter::appendDataOffset(AbstractAssembly::SubID _sub)
+{
+ auto it = m_dataHashBySubId.find(_sub);
+ if (it == m_dataHashBySubId.end())
+ m_assembly.pushSubroutineOffset(size_t(_sub));
+ else
+ m_assembly << eth::AssemblyItem(eth::PushData, it->second);
+}
+
+void EthAssemblyAdapter::appendDataSize(AbstractAssembly::SubID _sub)
+{
+ auto it = m_dataHashBySubId.find(_sub);
+ if (it == m_dataHashBySubId.end())
+ m_assembly.pushSubroutineSize(size_t(_sub));
+ else
+ m_assembly << u256(m_assembly.data(h256(it->second)).size());
+}
+
+AbstractAssembly::SubID EthAssemblyAdapter::appendData(bytes const& _data)
+{
+ eth::AssemblyItem pushData = m_assembly.newData(_data);
+ SubID subID = m_nextDataCounter++;
+ m_dataHashBySubId[subID] = pushData.data();
+ return subID;
+}
+
+EthAssemblyAdapter::LabelID EthAssemblyAdapter::assemblyTagToIdentifier(eth::AssemblyItem const& _tag)
+{
+ u256 id = _tag.data();
+ solAssert(id <= std::numeric_limits<LabelID>::max(), "Tag id too large.");
+ return LabelID(id);
+}
void CodeGenerator::assemble(
Block const& _parsedData,
diff --git a/libsolidity/codegen/AsmCodeGen.h b/libsolidity/codegen/AsmCodeGen.h
index dc529e10..4c6d97f4 100644
--- a/libsolidity/codegen/AsmCodeGen.h
+++ b/libsolidity/codegen/AsmCodeGen.h
@@ -21,6 +21,9 @@
#pragma once
#include <libyul/AsmAnalysis.h>
+#include <libyul/backends/evm/AbstractAssembly.h>
+
+#include <liblangutil/SourceLocation.h>
#include <functional>
@@ -34,11 +37,45 @@ namespace dev
namespace eth
{
class Assembly;
+class AssemblyItem;
}
namespace solidity
{
+class EthAssemblyAdapter: public yul::AbstractAssembly
+{
+public:
+ explicit EthAssemblyAdapter(eth::Assembly& _assembly);
+ void setSourceLocation(langutil::SourceLocation const& _location) override;
+ int stackHeight() const override;
+ void appendInstruction(solidity::Instruction _instruction) override;
+ void appendConstant(u256 const& _constant) override;
+ void appendLabel(LabelID _labelId) override;
+ void appendLabelReference(LabelID _labelId) override;
+ size_t newLabelId() override;
+ size_t namedLabel(std::string const& _name) override;
+ void appendLinkerSymbol(std::string const& _linkerSymbol) override;
+ void appendJump(int _stackDiffAfter) override;
+ void appendJumpTo(LabelID _labelId, int _stackDiffAfter) override;
+ void appendJumpToIf(LabelID _labelId) override;
+ void appendBeginsub(LabelID, int) override;
+ void appendJumpsub(LabelID, int, int) override;
+ void appendReturnsub(int, int) override;
+ void appendAssemblySize() override;
+ std::pair<std::shared_ptr<AbstractAssembly>, SubID> createSubAssembly() override;
+ void appendDataOffset(SubID _sub) override;
+ void appendDataSize(SubID _sub) override;
+ SubID appendData(dev::bytes const& _data) override;
+
+private:
+ static LabelID assemblyTagToIdentifier(eth::AssemblyItem const& _tag);
+
+ eth::Assembly& m_assembly;
+ std::map<SubID, dev::u256> m_dataHashBySubId;
+ size_t m_nextDataCounter = std::numeric_limits<size_t>::max() / 2;
+};
+
class CodeGenerator
{
public:
diff --git a/libsolidity/interface/AssemblyStack.cpp b/libsolidity/interface/AssemblyStack.cpp
index c15a192a..0fe57a45 100644
--- a/libsolidity/interface/AssemblyStack.cpp
+++ b/libsolidity/interface/AssemblyStack.cpp
@@ -29,6 +29,7 @@
#include <libyul/AsmParser.h>
#include <libyul/AsmAnalysis.h>
#include <libyul/AsmAnalysisInfo.h>
+#include <libyul/backends/evm/EVMObjectCompiler.h>
#include <libyul/backends/evm/EVMCodeTransform.h>
#include <libyul/backends/evm/EVMAssembly.h>
#include <libyul/ObjectParser.h>
@@ -85,20 +86,42 @@ bool AssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string
void AssemblyStack::optimize()
{
solAssert(m_language != Language::Assembly, "Optimization requested for loose assembly.");
- yul::OptimiserSuite::run(*m_parserResult->code, *m_parserResult->analysisInfo);
+ solAssert(m_analysisSuccessful, "Analysis was not successful.");
+ m_analysisSuccessful = false;
+ optimize(*m_parserResult);
solAssert(analyzeParsed(), "Invalid source code after optimization.");
}
bool AssemblyStack::analyzeParsed()
{
solAssert(m_parserResult, "");
- solAssert(m_parserResult->code, "");
- m_parserResult->analysisInfo = make_shared<yul::AsmAnalysisInfo>();
- yul::AsmAnalyzer analyzer(*m_parserResult->analysisInfo, m_errorReporter, m_evmVersion, boost::none, languageToDialect(m_language));
- m_analysisSuccessful = analyzer.analyze(*m_parserResult->code);
+ m_analysisSuccessful = analyzeParsed(*m_parserResult);
return m_analysisSuccessful;
}
+bool AssemblyStack::analyzeParsed(yul::Object& _object)
+{
+ solAssert(_object.code, "");
+ _object.analysisInfo = make_shared<yul::AsmAnalysisInfo>();
+ yul::AsmAnalyzer analyzer(*_object.analysisInfo, m_errorReporter, m_evmVersion, boost::none, languageToDialect(m_language));
+ bool success = analyzer.analyze(*_object.code);
+ for (auto& subNode: _object.subObjects)
+ if (auto subObject = dynamic_cast<yul::Object*>(subNode.get()))
+ if (!analyzeParsed(*subObject))
+ success = false;
+ return success;
+}
+
+void AssemblyStack::optimize(yul::Object& _object)
+{
+ solAssert(_object.code, "");
+ solAssert(_object.analysisInfo, "");
+ for (auto& subNode: _object.subObjects)
+ if (auto subObject = dynamic_cast<yul::Object*>(subNode.get()))
+ optimize(*subObject);
+ yul::OptimiserSuite::run(*_object.code, *_object.analysisInfo);
+}
+
MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const
{
solAssert(m_analysisSuccessful, "");
@@ -112,7 +135,8 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const
{
MachineAssemblyObject object;
eth::Assembly assembly;
- CodeGenerator::assemble(*m_parserResult->code, *m_parserResult->analysisInfo, assembly);
+ EthAssemblyAdapter adapter(assembly);
+ yul::EVMObjectCompiler::compile(*m_parserResult, adapter, m_language == Language::Yul, false);
object.bytecode = make_shared<eth::LinkerObject>(assembly.assemble());
object.assembly = assembly.assemblyString();
return object;
@@ -121,7 +145,7 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const
{
MachineAssemblyObject object;
yul::EVMAssembly assembly(true);
- yul::CodeTransform(assembly, *m_parserResult->analysisInfo, m_language == Language::Yul, true)(*m_parserResult->code);
+ yul::EVMObjectCompiler::compile(*m_parserResult, assembly, m_language == Language::Yul, true);
object.bytecode = make_shared<eth::LinkerObject>(assembly.finalize());
/// TODO: fill out text representation
return object;
diff --git a/libsolidity/interface/AssemblyStack.h b/libsolidity/interface/AssemblyStack.h
index 0d04ffec..485ec1e7 100644
--- a/libsolidity/interface/AssemblyStack.h
+++ b/libsolidity/interface/AssemblyStack.h
@@ -83,6 +83,9 @@ public:
private:
bool analyzeParsed();
+ bool analyzeParsed(yul::Object& _object);
+
+ void optimize(yul::Object& _object);
Language m_language = Language::Assembly;
EVMVersion m_evmVersion;
diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt
index 2dec0a44..4be8155f 100644
--- a/libyul/CMakeLists.txt
+++ b/libyul/CMakeLists.txt
@@ -9,6 +9,7 @@ add_library(yul
ObjectParser.cpp
backends/evm/EVMAssembly.cpp
backends/evm/EVMCodeTransform.cpp
+ backends/evm/EVMObjectCompiler.cpp
optimiser/ASTCopier.cpp
optimiser/ASTWalker.cpp
optimiser/BlockFlattener.cpp
diff --git a/libyul/backends/evm/AbstractAssembly.h b/libyul/backends/evm/AbstractAssembly.h
index 97b1d305..1f224ded 100644
--- a/libyul/backends/evm/AbstractAssembly.h
+++ b/libyul/backends/evm/AbstractAssembly.h
@@ -26,6 +26,7 @@
#include <libdevcore/CommonData.h>
#include <functional>
+#include <memory>
namespace langutil
{
@@ -52,6 +53,7 @@ class AbstractAssembly
{
public:
using LabelID = size_t;
+ using SubID = size_t;
virtual ~AbstractAssembly() {}
@@ -98,6 +100,14 @@ public:
/// Append the assembled size as a constant.
virtual void appendAssemblySize() = 0;
+ /// Creates a new sub-assembly, which can be referenced using dataSize and dataOffset.
+ virtual std::pair<std::shared_ptr<AbstractAssembly>, SubID> createSubAssembly() = 0;
+ /// Appends the offset of the given sub-assembly or data.
+ virtual void appendDataOffset(SubID _sub) = 0;
+ /// Appends the size of the given sub-assembly or data.
+ virtual void appendDataSize(SubID _sub) = 0;
+ /// Appends the given data to the assembly and returns its ID.
+ virtual SubID appendData(dev::bytes const& _data) = 0;
};
enum class IdentifierContext { LValue, RValue };
diff --git a/libyul/backends/evm/EVMAssembly.cpp b/libyul/backends/evm/EVMAssembly.cpp
index 99506317..2cf9f001 100644
--- a/libyul/backends/evm/EVMAssembly.cpp
+++ b/libyul/backends/evm/EVMAssembly.cpp
@@ -194,6 +194,27 @@ void EVMAssembly::appendAssemblySize()
m_bytecode += bytes(assemblySizeReferenceSize);
}
+pair<shared_ptr<AbstractAssembly>, AbstractAssembly::SubID> EVMAssembly::createSubAssembly()
+{
+ solAssert(false, "Sub assemblies not implemented.");
+ return {};
+}
+
+void EVMAssembly::appendDataOffset(AbstractAssembly::SubID)
+{
+ solAssert(false, "Data not implemented.");
+}
+
+void EVMAssembly::appendDataSize(AbstractAssembly::SubID)
+{
+ solAssert(false, "Data not implemented.");
+}
+
+AbstractAssembly::SubID EVMAssembly::appendData(bytes const&)
+{
+ solAssert(false, "Data not implemented.");
+}
+
void EVMAssembly::updateReference(size_t pos, size_t size, u256 value)
{
solAssert(m_bytecode.size() >= size && pos <= m_bytecode.size() - size, "");
diff --git a/libyul/backends/evm/EVMAssembly.h b/libyul/backends/evm/EVMAssembly.h
index d0a437cc..cef9c19a 100644
--- a/libyul/backends/evm/EVMAssembly.h
+++ b/libyul/backends/evm/EVMAssembly.h
@@ -77,6 +77,10 @@ public:
/// Append the assembled size as a constant.
void appendAssemblySize() override;
+ std::pair<std::shared_ptr<AbstractAssembly>, SubID> createSubAssembly() override;
+ void appendDataOffset(SubID _sub) override;
+ void appendDataSize(SubID _sub) override;
+ SubID appendData(dev::bytes const& _data) override;
/// Resolves references inside the bytecode and returns the linker object.
dev::eth::LinkerObject finalize();
diff --git a/libyul/backends/evm/EVMObjectCompiler.cpp b/libyul/backends/evm/EVMObjectCompiler.cpp
new file mode 100644
index 00000000..e7e8ad99
--- /dev/null
+++ b/libyul/backends/evm/EVMObjectCompiler.cpp
@@ -0,0 +1,55 @@
+/*
+ This file is part of solidity.
+
+ solidity is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ solidity is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with solidity. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * Compiler that transforms Yul Objects to EVM bytecode objects.
+ */
+
+#include <libyul/backends/evm/EVMObjectCompiler.h>
+
+#include <libyul/backends/evm/EVMCodeTransform.h>
+#include <libyul/Object.h>
+#include <libyul/Exceptions.h>
+
+using namespace yul;
+using namespace std;
+
+void EVMObjectCompiler::compile(Object& _object, AbstractAssembly& _assembly, bool _yul, bool _evm15)
+{
+ EVMObjectCompiler compiler(_assembly, _yul, _evm15);
+ compiler.run(_object);
+}
+
+void EVMObjectCompiler::run(Object& _object)
+{
+ map<YulString, AbstractAssembly::SubID> subIDs;
+
+ for (auto& subNode: _object.subObjects)
+ if (Object* subObject = dynamic_cast<Object*>(subNode.get()))
+ {
+ auto subAssemblyAndID = m_assembly.createSubAssembly();
+ subIDs[subObject->name] = subAssemblyAndID.second;
+ compile(*subObject, *subAssemblyAndID.first, m_yul, m_evm15);
+ }
+ else
+ {
+ Data const& data = dynamic_cast<Data const&>(*subNode);
+ subIDs[data.name] = m_assembly.appendData(data.data);
+ }
+
+ yulAssert(_object.analysisInfo, "No analysis info.");
+ CodeTransform{m_assembly, *_object.analysisInfo, m_yul, m_evm15}(*_object.code);
+}
diff --git a/libyul/backends/evm/EVMObjectCompiler.h b/libyul/backends/evm/EVMObjectCompiler.h
new file mode 100644
index 00000000..c7172e47
--- /dev/null
+++ b/libyul/backends/evm/EVMObjectCompiler.h
@@ -0,0 +1,43 @@
+/*
+ This file is part of solidity.
+
+ solidity is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ solidity is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with solidity. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * Compiler that transforms Yul Objects to EVM bytecode objects.
+ */
+
+
+namespace yul
+{
+struct Object;
+class AbstractAssembly;
+
+class EVMObjectCompiler
+{
+public:
+ static void compile(Object& _object, AbstractAssembly& _assembly, bool _yul, bool _evm15);
+private:
+ EVMObjectCompiler(AbstractAssembly& _assembly, bool _yul, bool _evm15):
+ m_assembly(_assembly), m_yul(_yul), m_evm15(_evm15)
+ {}
+
+ void run(Object& _object);
+
+ AbstractAssembly& m_assembly;
+ bool m_yul = false;
+ bool m_evm15 = false;
+};
+
+}
diff --git a/test/boostTest.cpp b/test/boostTest.cpp
index 7cb0c143..ff443d11 100644
--- a/test/boostTest.cpp
+++ b/test/boostTest.cpp
@@ -40,6 +40,7 @@
#include <test/libsolidity/SyntaxTest.h>
#include <test/libsolidity/SMTCheckerJSONTest.h>
#include <test/libyul/YulOptimizerTest.h>
+#include <test/libyul/ObjectCompilerTest.h>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/predicate.hpp>
@@ -146,6 +147,12 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] )
"yulOptimizerTests",
yul::test::YulOptimizerTest::create
) > 0, "no Yul Optimizer tests found");
+ solAssert(registerTests(
+ master,
+ dev::test::Options::get().testPath / "libyul",
+ "objectCompiler",
+ yul::test::ObjectCompilerTest::create
+ ) > 0, "no Yul Object compiler tests found");
if (!dev::test::Options::get().disableSMT)
{
solAssert(registerTests(
diff --git a/test/libyul/ObjectCompilerTest.cpp b/test/libyul/ObjectCompilerTest.cpp
new file mode 100644
index 00000000..e60f718d
--- /dev/null
+++ b/test/libyul/ObjectCompilerTest.cpp
@@ -0,0 +1,134 @@
+/*
+ This file is part of solidity.
+
+ solidity is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ solidity is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with solidity. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <test/libyul/ObjectCompilerTest.h>
+
+#include <test/libsolidity/FormattedScope.h>
+
+#include <libsolidity/interface/AssemblyStack.h>
+
+#include <libevmasm/Instruction.h>
+
+#include <liblangutil/SourceReferenceFormatter.h>
+
+#include <boost/algorithm/string.hpp>
+
+#include <fstream>
+
+using namespace dev;
+using namespace langutil;
+using namespace yul;
+using namespace yul::test;
+using namespace dev::solidity;
+using namespace dev::solidity::test;
+using namespace std;
+
+ObjectCompilerTest::ObjectCompilerTest(string const& _filename)
+{
+ boost::filesystem::path path(_filename);
+
+ ifstream file(_filename);
+ if (!file)
+ BOOST_THROW_EXCEPTION(runtime_error("Cannot open test case: \"" + _filename + "\"."));
+ file.exceptions(ios::badbit);
+
+ string line;
+ while (getline(file, line))
+ {
+ if (boost::algorithm::starts_with(line, "// ----"))
+ break;
+ if (m_source.empty() && boost::algorithm::starts_with(line, "// optimize"))
+ m_optimize = true;
+ m_source += line + "\n";
+ }
+ while (getline(file, line))
+ if (boost::algorithm::starts_with(line, "//"))
+ m_expectation += line.substr((line.size() >= 3 && line[2] == ' ') ? 3 : 2) + "\n";
+ else
+ m_expectation += line + "\n";
+}
+
+bool ObjectCompilerTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted)
+{
+ AssemblyStack stack(EVMVersion(), AssemblyStack::Language::StrictAssembly);
+ if (!stack.parseAndAnalyze("source", m_source))
+ {
+ FormattedScope(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl;
+ printErrors(_stream, stack.errors());
+ return false;
+ }
+ if (m_optimize)
+ stack.optimize();
+
+ MachineAssemblyObject obj = stack.assemble(AssemblyStack::Machine::EVM);
+ solAssert(obj.bytecode, "");
+
+ m_obtainedResult = "Assembly:\n" + obj.assembly;
+ if (obj.bytecode->bytecode.empty())
+ m_obtainedResult += "-- empty bytecode --\n";
+ else
+ m_obtainedResult +=
+ "Bytecode: " +
+ toHex(obj.bytecode->bytecode) +
+ "\nOpcodes: " +
+ boost::trim_copy(solidity::disassemble(obj.bytecode->bytecode)) +
+ "\n";
+
+ if (m_expectation != m_obtainedResult)
+ {
+ string nextIndentLevel = _linePrefix + " ";
+ FormattedScope(_stream, _formatted, {formatting::BOLD, formatting::CYAN}) << _linePrefix << "Expected result:" << endl;
+ printIndented(_stream, m_expectation, nextIndentLevel);
+ FormattedScope(_stream, _formatted, {formatting::BOLD, formatting::CYAN}) << _linePrefix << "Obtained result:" << endl;
+ printIndented(_stream, m_obtainedResult, nextIndentLevel);
+ return false;
+ }
+ return true;
+}
+
+void ObjectCompilerTest::printSource(ostream& _stream, string const& _linePrefix, bool const) const
+{
+ printIndented(_stream, m_source, _linePrefix);
+}
+
+void ObjectCompilerTest::printUpdatedExpectations(ostream& _stream, string const& _linePrefix) const
+{
+ printIndented(_stream, m_obtainedResult, _linePrefix);
+}
+
+void ObjectCompilerTest::printIndented(ostream& _stream, string const& _output, string const& _linePrefix) const
+{
+ stringstream output(_output);
+ string line;
+ while (getline(output, line))
+ if (line.empty())
+ // Avoid trailing spaces.
+ _stream << boost::trim_right_copy(_linePrefix) << endl;
+ else
+ _stream << _linePrefix << line << endl;
+}
+
+void ObjectCompilerTest::printErrors(ostream& _stream, ErrorList const& _errors)
+{
+ SourceReferenceFormatter formatter(_stream);
+
+ for (auto const& error: _errors)
+ formatter.printExceptionInformation(
+ *error,
+ (error->type() == Error::Type::Warning) ? "Warning" : "Error"
+ );
+}
diff --git a/test/libyul/ObjectCompilerTest.h b/test/libyul/ObjectCompilerTest.h
new file mode 100644
index 00000000..a5f8d777
--- /dev/null
+++ b/test/libyul/ObjectCompilerTest.h
@@ -0,0 +1,69 @@
+/*
+ This file is part of solidity.
+
+ solidity is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ solidity is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with solidity. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#include <test/TestCase.h>
+
+namespace langutil
+{
+class Scanner;
+class Error;
+using ErrorList = std::vector<std::shared_ptr<Error const>>;
+}
+
+namespace yul
+{
+struct AsmAnalysisInfo;
+struct Block;
+}
+
+namespace yul
+{
+namespace test
+{
+
+class ObjectCompilerTest: public dev::solidity::test::TestCase
+{
+public:
+ static std::unique_ptr<TestCase> create(std::string const& _filename)
+ {
+ return std::unique_ptr<TestCase>(new ObjectCompilerTest(_filename));
+ }
+
+ explicit ObjectCompilerTest(std::string const& _filename);
+
+ bool run(std::ostream& _stream, std::string const& _linePrefix = "", bool const _formatted = false) override;
+
+ void printSource(std::ostream& _stream, std::string const &_linePrefix = "", bool const _formatted = false) const override;
+ void printUpdatedExpectations(std::ostream& _stream, std::string const& _linePrefix) const override;
+
+private:
+ void printIndented(std::ostream& _stream, std::string const& _output, std::string const& _linePrefix = "") const;
+ bool parse(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted);
+ void disambiguate();
+
+ static void printErrors(std::ostream& _stream, langutil::ErrorList const& _errors);
+
+ std::string m_source;
+ bool m_optimize = false;
+ std::string m_expectation;
+ std::string m_obtainedResult;
+};
+
+}
+}
diff --git a/test/libyul/objectCompiler/data.yul b/test/libyul/objectCompiler/data.yul
new file mode 100644
index 00000000..daa22d21
--- /dev/null
+++ b/test/libyul/objectCompiler/data.yul
@@ -0,0 +1,11 @@
+object "a" {
+ code {}
+ // Unreferenced data is not added to the assembled bytecode.
+ data "str" "Hello, World!"
+}
+// ----
+// Assembly:
+// stop
+// data_acaf3289d7b601cbd114fb36c4d29c85bbfd5e133f14cb355c3fd8d99367964f 48656c6c6f2c20576f726c6421
+// Bytecode: fe
+// Opcodes: INVALID
diff --git a/test/libyul/objectCompiler/namedObject.yul b/test/libyul/objectCompiler/namedObject.yul
new file mode 100644
index 00000000..940160fd
--- /dev/null
+++ b/test/libyul/objectCompiler/namedObject.yul
@@ -0,0 +1,6 @@
+object "a" {
+ code {}
+}
+// ----
+// Assembly:
+// -- empty bytecode --
diff --git a/test/libyul/objectCompiler/namedObjectCode.yul b/test/libyul/objectCompiler/namedObjectCode.yul
new file mode 100644
index 00000000..4fc6891c
--- /dev/null
+++ b/test/libyul/objectCompiler/namedObjectCode.yul
@@ -0,0 +1,13 @@
+object "a" {
+ code { sstore(0, 1) }
+}
+// ----
+// Assembly:
+// /* "source":32:33 */
+// 0x01
+// /* "source":29:30 */
+// 0x00
+// /* "source":22:34 */
+// sstore
+// Bytecode: 6001600055
+// Opcodes: PUSH1 0x1 PUSH1 0x0 SSTORE
diff --git a/test/libyul/objectCompiler/nested_optimizer.yul b/test/libyul/objectCompiler/nested_optimizer.yul
new file mode 100644
index 00000000..7739ce61
--- /dev/null
+++ b/test/libyul/objectCompiler/nested_optimizer.yul
@@ -0,0 +1,49 @@
+// optimize
+object "a" {
+ code {
+ let x := calldataload(0)
+ let y := calldataload(0)
+ let z := sub(y, x)
+ sstore(add(x, 0), z)
+ }
+ object "sub" {
+ code {
+ let x := calldataload(0)
+ let y := calldataload(0)
+ let z := sub(y, x)
+ sstore(add(x, 0), z)
+ }
+ }
+}
+// ----
+// Assembly:
+// /* "source":60:61 */
+// 0x00
+// /* "source":137:138 */
+// dup1
+// /* "source":60:61 */
+// dup2
+// /* "source":47:62 */
+// calldataload
+// /* "source":119:139 */
+// sstore
+// /* "source":32:143 */
+// pop
+// stop
+//
+// sub_0: assembly {
+// /* "source":200:201 */
+// 0x00
+// /* "source":283:284 */
+// dup1
+// /* "source":200:201 */
+// dup2
+// /* "source":187:202 */
+// calldataload
+// /* "source":265:285 */
+// sstore
+// /* "source":170:291 */
+// pop
+// }
+// Bytecode: 60008081355550fe
+// Opcodes: PUSH1 0x0 DUP1 DUP2 CALLDATALOAD SSTORE POP INVALID
diff --git a/test/libyul/objectCompiler/simple.yul b/test/libyul/objectCompiler/simple.yul
new file mode 100644
index 00000000..d41b527c
--- /dev/null
+++ b/test/libyul/objectCompiler/simple.yul
@@ -0,0 +1,13 @@
+{
+ sstore(0, 1)
+}
+// ----
+// Assembly:
+// /* "source":14:15 */
+// 0x01
+// /* "source":11:12 */
+// 0x00
+// /* "source":4:16 */
+// sstore
+// Bytecode: 6001600055
+// Opcodes: PUSH1 0x1 PUSH1 0x0 SSTORE
diff --git a/test/libyul/objectCompiler/simple_optimizer.yul b/test/libyul/objectCompiler/simple_optimizer.yul
new file mode 100644
index 00000000..43b33553
--- /dev/null
+++ b/test/libyul/objectCompiler/simple_optimizer.yul
@@ -0,0 +1,23 @@
+// optimize
+{
+ let x := calldataload(0)
+ let y := calldataload(0)
+ let z := sub(y, x)
+ sstore(add(x, 0), z)
+}
+// ----
+// Assembly:
+// /* "source":38:39 */
+// 0x00
+// /* "source":109:110 */
+// dup1
+// /* "source":38:39 */
+// dup2
+// /* "source":25:40 */
+// calldataload
+// /* "source":91:111 */
+// sstore
+// /* "source":12:113 */
+// pop
+// Bytecode: 60008081355550
+// Opcodes: PUSH1 0x0 DUP1 DUP2 CALLDATALOAD SSTORE POP
diff --git a/test/libyul/objectCompiler/smoke.yul b/test/libyul/objectCompiler/smoke.yul
new file mode 100644
index 00000000..b2e44d4d
--- /dev/null
+++ b/test/libyul/objectCompiler/smoke.yul
@@ -0,0 +1,5 @@
+{
+}
+// ----
+// Assembly:
+// -- empty bytecode --
diff --git a/test/libyul/objectCompiler/subObject.yul b/test/libyul/objectCompiler/subObject.yul
new file mode 100644
index 00000000..98ea4d07
--- /dev/null
+++ b/test/libyul/objectCompiler/subObject.yul
@@ -0,0 +1,21 @@
+object "a" {
+ code {}
+ // Unreferenced data is not added to the assembled bytecode.
+ data "str" "Hello, World!"
+ object "sub" { code { sstore(0, 1) } }
+}
+// ----
+// Assembly:
+// stop
+// data_acaf3289d7b601cbd114fb36c4d29c85bbfd5e133f14cb355c3fd8d99367964f 48656c6c6f2c20576f726c6421
+//
+// sub_0: assembly {
+// /* "source":149:150 */
+// 0x01
+// /* "source":146:147 */
+// 0x00
+// /* "source":139:151 */
+// sstore
+// }
+// Bytecode: fe
+// Opcodes: INVALID
diff --git a/test/libyul/objectCompiler/subSubObject.yul b/test/libyul/objectCompiler/subSubObject.yul
new file mode 100644
index 00000000..5e01f6dd
--- /dev/null
+++ b/test/libyul/objectCompiler/subSubObject.yul
@@ -0,0 +1,39 @@
+object "a" {
+ code {}
+ // Unreferenced data is not added to the assembled bytecode.
+ data "str" "Hello, World!"
+ object "sub" {
+ code { sstore(0, 1) }
+ object "subsub" {
+ code { sstore(2, 3) }
+ data "str" hex"123456"
+ }
+ }
+}
+// ----
+// Assembly:
+// stop
+// data_acaf3289d7b601cbd114fb36c4d29c85bbfd5e133f14cb355c3fd8d99367964f 48656c6c6f2c20576f726c6421
+//
+// sub_0: assembly {
+// /* "source":153:154 */
+// 0x01
+// /* "source":150:151 */
+// 0x00
+// /* "source":143:155 */
+// sstore
+// stop
+//
+// sub_0: assembly {
+// /* "source":203:204 */
+// 0x03
+// /* "source":200:201 */
+// 0x02
+// /* "source":193:205 */
+// sstore
+// stop
+// data_6adf031833174bbe4c85eafe59ddb54e6584648c2c962c6f94791ab49caa0ad4 123456
+// }
+// }
+// Bytecode: fe
+// Opcodes: INVALID
diff --git a/test/tools/CMakeLists.txt b/test/tools/CMakeLists.txt
index 736212fc..da8e0b39 100644
--- a/test/tools/CMakeLists.txt
+++ b/test/tools/CMakeLists.txt
@@ -4,7 +4,19 @@ target_link_libraries(solfuzzer PRIVATE libsolc evmasm ${Boost_PROGRAM_OPTIONS_L
add_executable(yulopti yulopti.cpp)
target_link_libraries(yulopti PRIVATE solidity ${Boost_PROGRAM_OPTIONS_LIBRARIES} ${Boost_SYSTEM_LIBRARIES})
-add_executable(isoltest isoltest.cpp ../Options.cpp ../Common.cpp ../TestCase.cpp ../libsolidity/SyntaxTest.cpp
- ../libsolidity/AnalysisFramework.cpp ../libsolidity/SolidityExecutionFramework.cpp ../ExecutionFramework.cpp
- ../RPCSession.cpp ../libsolidity/ASTJSONTest.cpp ../libsolidity/SMTCheckerJSONTest.cpp ../libyul/YulOptimizerTest.cpp)
+add_executable(isoltest
+ isoltest.cpp
+ ../Options.cpp
+ ../Common.cpp
+ ../TestCase.cpp
+ ../libsolidity/SyntaxTest.cpp
+ ../libsolidity/AnalysisFramework.cpp
+ ../libsolidity/SolidityExecutionFramework.cpp
+ ../ExecutionFramework.cpp
+ ../RPCSession.cpp
+ ../libsolidity/ASTJSONTest.cpp
+ ../libsolidity/SMTCheckerJSONTest.cpp
+ ../libyul/ObjectCompilerTest.cpp
+ ../libyul/YulOptimizerTest.cpp
+)
target_link_libraries(isoltest PRIVATE libsolc solidity evmasm ${Boost_PROGRAM_OPTIONS_LIBRARIES} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES})
diff --git a/test/tools/isoltest.cpp b/test/tools/isoltest.cpp
index f8e2dc58..13585887 100644
--- a/test/tools/isoltest.cpp
+++ b/test/tools/isoltest.cpp
@@ -23,6 +23,7 @@
#include <test/libsolidity/ASTJSONTest.h>
#include <test/libsolidity/SMTCheckerJSONTest.h>
#include <test/libyul/YulOptimizerTest.h>
+#include <test/libyul/ObjectCompilerTest.h>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/replace.hpp>
@@ -401,6 +402,17 @@ Allowed options)",
else
return 1;
+ if (auto stats = runTestSuite(
+ "Yul Object Compiler",
+ testPath / "libyul",
+ "objectCompiler",
+ yul::test::ObjectCompilerTest::create,
+ formatted
+ ))
+ global_stats += *stats;
+ else
+ return 1;
+
if (!disableSMT)
{
if (auto stats = runTestSuite(