[yul-phaser] Add Program class

This commit is contained in:
cameel 2020-01-17 07:34:18 +01:00
parent b75370d93e
commit 513d41c315
5 changed files with 293 additions and 3 deletions

View File

@ -15,7 +15,9 @@ install(TARGETS solidity-upgrade DESTINATION "${CMAKE_INSTALL_BINDIR}")
add_executable(yul-phaser
yulPhaser/main.cpp
yulPhaser/Program.h
yulPhaser/Program.cpp
)
target_link_libraries(yul-phaser PRIVATE Boost::program_options)
target_link_libraries(yul-phaser PRIVATE solidity Boost::program_options)
install(TARGETS yul-phaser DESTINATION "${CMAKE_INSTALL_BINDIR}")

View File

@ -0,0 +1,27 @@
/*
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 <libsolutil/Exceptions.h>
namespace solidity::phaser
{
struct InvalidProgram: virtual util::Exception {};
}

140
tools/yulPhaser/Program.cpp Normal file
View File

@ -0,0 +1,140 @@
/*
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 <tools/yulPhaser/Program.h>
#include <tools/yulPhaser/Exceptions.h>
#include <liblangutil/CharStream.h>
#include <liblangutil/ErrorReporter.h>
#include <liblangutil/Exceptions.h>
#include <libyul/AsmAnalysis.h>
#include <libyul/AsmAnalysisInfo.h>
#include <libyul/AsmData.h>
#include <libyul/AsmParser.h>
#include <libyul/YulString.h>
#include <libyul/backends/evm/EVMDialect.h>
#include <libyul/optimiser/Disambiguator.h>
#include <libyul/optimiser/ForLoopInitRewriter.h>
#include <libyul/optimiser/FunctionGrouper.h>
#include <libyul/optimiser/FunctionHoister.h>
#include <libyul/optimiser/Metrics.h>
#include <libyul/optimiser/Suite.h>
#include <libsolutil/CommonIO.h>
#include <boost/filesystem.hpp>
#include <cassert>
#include <memory>
using namespace std;
using namespace solidity;
using namespace solidity::langutil;
using namespace solidity::yul;
using namespace solidity::util;
using namespace solidity::phaser;
set<YulString> const Program::s_externallyUsedIdentifiers = {};
Program Program::load(string const& _sourcePath)
{
Dialect const& dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion{});
shared_ptr<Block> ast = parseSource(dialect, loadSource(_sourcePath));
unique_ptr<AsmAnalysisInfo> analysisInfo = analyzeAST(dialect, *ast);
Program program(
dialect,
disambiguateAST(dialect, *ast, *analysisInfo)
);
program.optimise({
FunctionHoister::name,
FunctionGrouper::name,
ForLoopInitRewriter::name,
});
return program;
}
void Program::optimise(vector<string> const& _optimisationSteps)
{
applyOptimisationSteps(m_optimiserStepContext, *m_ast, _optimisationSteps);
}
CharStream Program::loadSource(string const& _sourcePath)
{
assertThrow(boost::filesystem::exists(_sourcePath), InvalidProgram, "Source file does not exist");
string sourceCode = readFileAsString(_sourcePath);
return CharStream(sourceCode, _sourcePath);
}
shared_ptr<Block> Program::parseSource(Dialect const& _dialect, CharStream _source)
{
ErrorList errors;
ErrorReporter errorReporter(errors);
auto scanner = make_shared<Scanner>(move(_source));
Parser parser(errorReporter, _dialect);
shared_ptr<Block> ast = parser.parse(scanner, false);
assertThrow(ast != nullptr, InvalidProgram, "Error parsing source");
assert(errorReporter.errors().empty());
return ast;
}
unique_ptr<AsmAnalysisInfo> Program::analyzeAST(Dialect const& _dialect, Block const& _ast)
{
ErrorList errors;
ErrorReporter errorReporter(errors);
auto analysisInfo = make_unique<AsmAnalysisInfo>();
AsmAnalyzer analyzer(*analysisInfo, errorReporter, _dialect);
bool analysisSuccessful = analyzer.analyze(_ast);
assertThrow(analysisSuccessful, InvalidProgram, "Error analyzing source");
assert(errorReporter.errors().empty());
return analysisInfo;
}
unique_ptr<Block> Program::disambiguateAST(
Dialect const& _dialect,
Block const& _ast,
AsmAnalysisInfo const& _analysisInfo
)
{
Disambiguator disambiguator(_dialect, _analysisInfo, s_externallyUsedIdentifiers);
return make_unique<Block>(get<Block>(disambiguator(_ast)));
}
void Program::applyOptimisationSteps(
OptimiserStepContext& _context,
Block& _ast,
vector<string> const& _optimisationSteps
)
{
for (string const& step: _optimisationSteps)
OptimiserSuite::allSteps().at(step)->run(_context, _ast);
}
size_t Program::computeCodeSize(Block const& _ast)
{
return CodeSize::codeSizeIncludingFunctions(_ast);
}

108
tools/yulPhaser/Program.h Normal file
View File

@ -0,0 +1,108 @@
/*
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 <libyul/optimiser/OptimiserStep.h>
#include <libyul/optimiser/NameDispenser.h>
#include <optional>
#include <set>
#include <string>
#include <vector>
namespace solidity::langutil
{
class CharStream;
}
namespace solidity::yul
{
struct AsmAnalysisInfo;
struct Block;
struct Dialect;
class YulString;
}
namespace solidity::phaser
{
/**
* Class representing parsed and analysed Yul program that we can apply optimisations to.
* The program is already disambiguated and has several prerequisite optimiser steps applied to it
* so that the requirements of any possible step that could be later applied by the user are
* already satisfied.
*
* The class allows the user to apply extra optimisations and obtain metrics and general
* information about the resulting syntax tree.
*/
class Program
{
public:
static Program load(std::string const& _sourcePath);
void optimise(std::vector<std::string> const& _optimisationSteps);
size_t codeSize() const { return computeCodeSize(*m_ast); }
yul::Block const& ast() const { return *m_ast; }
private:
Program(
yul::Dialect const& _dialect,
std::shared_ptr<yul::Block> _ast
):
m_ast(_ast),
m_nameDispenser(_dialect, *_ast, s_externallyUsedIdentifiers),
m_optimiserStepContext{_dialect, m_nameDispenser, s_externallyUsedIdentifiers}
{}
static langutil::CharStream loadSource(std::string const& _sourcePath);
static std::shared_ptr<yul::Block> parseSource(
yul::Dialect const& _dialect,
langutil::CharStream _source
);
static std::unique_ptr<yul::AsmAnalysisInfo> analyzeAST(
yul::Dialect const& _dialect,
yul::Block const& _ast
);
static std::unique_ptr<yul::Block> disambiguateAST(
yul::Dialect const& _dialect,
yul::Block const& _ast,
yul::AsmAnalysisInfo const& _analysisInfo
);
static void applyOptimisationSteps(
yul::OptimiserStepContext& _context,
yul::Block& _ast,
std::vector<std::string> const& _optimisationSteps
);
static size_t computeCodeSize(yul::Block const& _ast);
std::shared_ptr<yul::Block> m_ast;
yul::NameDispenser m_nameDispenser;
yul::OptimiserStepContext m_optimiserStepContext;
// Always empty set of reserved identifiers. It could be a constructor parameter but I don't
// think it would be useful in this tool. Other tools (like yulopti) have it empty too.
// We need it to live as long as m_optimiserStepContext because it stores a reference
// but since it's empty and read-only we can just have all objects share one static instance.
static std::set<yul::YulString> const s_externallyUsedIdentifiers;
};
}

View File

@ -15,6 +15,8 @@
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
#include <tools/yulPhaser/Exceptions.h>
#include <tools/yulPhaser/Program.h>
#include <boost/program_options.hpp>
@ -22,6 +24,8 @@
#include <string>
using namespace std;
using namespace solidity::phaser;
namespace po = boost::program_options;
namespace
@ -35,7 +39,8 @@ struct CommandLineParsingResult
void runAlgorithm(string const& _sourcePath)
{
cout << "Input: " << _sourcePath << endl;
Program::load(_sourcePath);
cout << "Program load successful." << endl;
}
CommandLineParsingResult parseCommandLine(int argc, char** argv)
@ -99,7 +104,15 @@ int main(int argc, char** argv)
if (parsingResult.exitCode != 0)
return parsingResult.exitCode;
runAlgorithm(parsingResult.arguments["input-file"].as<string>());
try
{
runAlgorithm(parsingResult.arguments["input-file"].as<string>());
}
catch (InvalidProgram const& _exception)
{
cerr << "ERROR: " << _exception.what() << endl;
return 1;
}
return 0;
}