/* 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 . */ /** * Interactive yul optimizer */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; using namespace dev; using namespace langutil; using namespace dev::solidity; using namespace dev::solidity::assembly; using namespace dev::yul; namespace po = boost::program_options; class YulOpti { public: void printErrors(Scanner const& _scanner) { SourceReferenceFormatter formatter(cout, [&](string const&) -> Scanner const& { return _scanner; }); for (auto const& error: m_errors) formatter.printExceptionInformation( *error, (error->type() == Error::Type::Warning) ? "Warning" : "Error" ); } bool parse(string const& _input) { ErrorReporter errorReporter(m_errors); shared_ptr scanner = make_shared(CharStream(_input), ""); m_ast = assembly::Parser(errorReporter, assembly::AsmFlavour::Strict).parse(scanner, false); if (!m_ast || !errorReporter.errors().empty()) { cout << "Error parsing source." << endl; printErrors(*scanner); return false; } m_analysisInfo = make_shared(); AsmAnalyzer analyzer( *m_analysisInfo, errorReporter, EVMVersion::byzantium(), boost::none, AsmFlavour::Strict ); if (!analyzer.analyze(*m_ast) || !errorReporter.errors().empty()) { cout << "Error analyzing source." << endl; printErrors(*scanner); return false; } return true; } void runInteractive(string source) { bool disambiguated = false; while (true) { cout << "----------------------" << endl; cout << source << endl; if (!parse(source)) return; if (!disambiguated) { *m_ast = boost::get(Disambiguator(*m_analysisInfo)(*m_ast)); m_analysisInfo.reset(); m_nameDispenser = make_shared(*m_ast); disambiguated = true; } cout << "(q)quit/(f)flatten/(c)se/propagate var(d)ecls/(x)plit/(j)oin/(g)rouper/(h)oister/" << endl; cout << " (e)xpr inline/(i)nline/(s)implify/(u)nusedprune/ss(a) transform/" << endl; cout << " (r)edundant assign elim./re(m)aterializer/f(o)r-loop-pre-rewriter? "; cout.flush(); int option = readStandardInputChar(); cout << ' ' << char(option) << endl; switch (option) { case 'q': return; case 'f': BlockFlattener{}(*m_ast); break; case 'o': ForLoopInitRewriter{}(*m_ast); break; case 'c': (CommonSubexpressionEliminator{})(*m_ast); break; case 'd': (VarDeclPropagator{})(*m_ast); break; case 'x': ExpressionSplitter{*m_nameDispenser}(*m_ast); break; case 'j': ExpressionJoiner::run(*m_ast); break; case 'g': (FunctionGrouper{})(*m_ast); break; case 'h': (FunctionHoister{})(*m_ast); break; case 'e': ExpressionInliner{*m_ast}.run(); break; case 'i': FullInliner(*m_ast, *m_nameDispenser).run(); break; case 's': ExpressionSimplifier::run(*m_ast); break; case 'u': UnusedPruner::runUntilStabilised(*m_ast); break; case 'a': SSATransform::run(*m_ast, *m_nameDispenser); break; case 'r': RedundantAssignEliminator::run(*m_ast); break; case 'm': Rematerialiser{}(*m_ast); break; default: cout << "Unknown option." << endl; } source = AsmPrinter{}(*m_ast); } } private: ErrorList m_errors; shared_ptr m_ast; shared_ptr m_analysisInfo; shared_ptr m_nameDispenser; }; int main(int argc, char** argv) { po::options_description options( R"(yulopti, yul optimizer exploration tool. Usage: yulopti [Options] Reads as yul code and applies optimizer steps to it, interactively read from stdin. Allowed options)", po::options_description::m_default_line_length, po::options_description::m_default_line_length - 23); options.add_options() ( "input-file", po::value(), "input file" ) ("help", "Show this help screen."); // All positional options should be interpreted as input files po::positional_options_description filesPositions; filesPositions.add("input-file", 1); po::variables_map arguments; try { po::command_line_parser cmdLineParser(argc, argv); cmdLineParser.options(options).positional(filesPositions); po::store(cmdLineParser.run(), arguments); } catch (po::error const& _exception) { cerr << _exception.what() << endl; return 1; } string input; if (arguments.count("input-file")) YulOpti{}.runInteractive(readFileAsString(arguments["input-file"].as())); else cout << options; return 0; }