Merge pull request #5341 from ethereum/optimizeAssemblyCommandline

Apply the optimize commandline parameter to assembly mode.
This commit is contained in:
chriseth 2018-12-03 11:52:48 +01:00 committed by GitHub
commit 4b98946e5a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 73 additions and 10 deletions

View File

@ -7,6 +7,7 @@ Language Features:
Compiler Features: Compiler Features:
* Build System: LLL is not built anymore by default. Must configure it with CMake as `-DLLL=ON`. * Build System: LLL is not built anymore by default. Must configure it with CMake as `-DLLL=ON`.
* Code generator: Do not perform redundant double cleanup on unsigned integers when loading from calldata. * Code generator: Do not perform redundant double cleanup on unsigned integers when loading from calldata.
* Commandline interface: Experimental ``--optimize`` option for assembly mode.
* SMTChecker: SMTLib2 queries and responses passed via standard JSON compiler interface. * SMTChecker: SMTLib2 queries and responses passed via standard JSON compiler interface.
* SMTChecker: Support ``msg``, ``tx`` and ``block`` member variables. * SMTChecker: Support ``msg``, ``tx`` and ``block`` member variables.
* SMTChecker: Support ``gasleft()`` and ``blockhash()`` functions. * SMTChecker: Support ``gasleft()`` and ``blockhash()`` functions.

View File

@ -34,6 +34,8 @@
#include <libevmasm/Assembly.h> #include <libevmasm/Assembly.h>
#include <libyul/optimiser/Suite.h>
using namespace std; using namespace std;
using namespace dev; using namespace dev;
using namespace langutil; using namespace langutil;
@ -79,6 +81,13 @@ bool AssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string
return analyzeParsed(); return analyzeParsed();
} }
void AssemblyStack::optimize()
{
solAssert(m_language != Language::Assembly, "Optimization requested for loose assembly.");
yul::OptimiserSuite::run(*m_parserResult->code, *m_parserResult->analysisInfo);
solAssert(analyzeParsed(), "Invalid source code after optimization.");
}
bool AssemblyStack::analyzeParsed() bool AssemblyStack::analyzeParsed()
{ {
solAssert(m_parserResult, ""); solAssert(m_parserResult, "");

View File

@ -69,6 +69,9 @@ public:
/// Multiple calls overwrite the previous state. /// Multiple calls overwrite the previous state.
bool parseAndAnalyze(std::string const& _sourceName, std::string const& _source); bool parseAndAnalyze(std::string const& _sourceName, std::string const& _source);
/// Run the optimizer suite. Can only be used with Yul or strict assembly.
void optimize();
/// Run the assembly step (should only be called after parseAndAnalyze). /// Run the assembly step (should only be called after parseAndAnalyze).
MachineAssemblyObject assemble(Machine _machine) const; MachineAssemblyObject assemble(Machine _machine) const;

View File

@ -626,15 +626,15 @@ Allowed options)",
) )
( (
g_argAssemble.c_str(), g_argAssemble.c_str(),
"Switch to assembly mode, ignoring all options except --machine and assumes input is assembly." "Switch to assembly mode, ignoring all options except --machine and --optimize and assumes input is assembly."
) )
( (
g_argYul.c_str(), g_argYul.c_str(),
"Switch to Yul mode, ignoring all options except --machine and assumes input is Yul." "Switch to Yul mode, ignoring all options except --machine and --optimize and assumes input is Yul."
) )
( (
g_argStrictAssembly.c_str(), g_argStrictAssembly.c_str(),
"Switch to strict assembly mode, ignoring all options except --machine and assumes input is strict assembly." "Switch to strict assembly mode, ignoring all options except --machine and --optimize and assumes input is strict assembly."
) )
( (
g_argMachine.c_str(), g_argMachine.c_str(),
@ -820,6 +820,7 @@ bool CommandLineInterface::processInput()
using Machine = AssemblyStack::Machine; using Machine = AssemblyStack::Machine;
Input inputLanguage = m_args.count(g_argYul) ? Input::Yul : (m_args.count(g_argStrictAssembly) ? Input::StrictAssembly : Input::Assembly); Input inputLanguage = m_args.count(g_argYul) ? Input::Yul : (m_args.count(g_argStrictAssembly) ? Input::StrictAssembly : Input::Assembly);
Machine targetMachine = Machine::EVM; Machine targetMachine = Machine::EVM;
bool optimize = m_args.count(g_argOptimize);
if (m_args.count(g_argMachine)) if (m_args.count(g_argMachine))
{ {
string machine = m_args[g_argMachine].as<string>(); string machine = m_args[g_argMachine].as<string>();
@ -835,7 +836,18 @@ bool CommandLineInterface::processInput()
return false; return false;
} }
} }
return assemble(inputLanguage, targetMachine); if (optimize && inputLanguage == Input::Assembly)
{
serr() <<
"Optimizer cannot be used for loose assembly. Use --" <<
g_strStrictAssembly <<
" or --" <<
g_strYul <<
"." <<
endl;
return false;
}
return assemble(inputLanguage, targetMachine, optimize);
} }
if (m_args.count(g_argLink)) if (m_args.count(g_argLink))
{ {
@ -1179,7 +1191,8 @@ string CommandLineInterface::objectWithLinkRefsHex(eth::LinkerObject const& _obj
bool CommandLineInterface::assemble( bool CommandLineInterface::assemble(
AssemblyStack::Language _language, AssemblyStack::Language _language,
AssemblyStack::Machine _targetMachine AssemblyStack::Machine _targetMachine,
bool _optimize
) )
{ {
bool successful = true; bool successful = true;
@ -1191,6 +1204,8 @@ bool CommandLineInterface::assemble(
{ {
if (!stack.parseAndAnalyze(src.first, src.second)) if (!stack.parseAndAnalyze(src.first, src.second))
successful = false; successful = false;
else if (_optimize)
stack.optimize();
} }
catch (Exception const& _exception) catch (Exception const& _exception)
{ {

View File

@ -59,7 +59,7 @@ private:
/// @returns the full object with library placeholder hints in hex. /// @returns the full object with library placeholder hints in hex.
static std::string objectWithLinkRefsHex(eth::LinkerObject const& _obj); static std::string objectWithLinkRefsHex(eth::LinkerObject const& _obj);
bool assemble(AssemblyStack::Language _language, AssemblyStack::Machine _targetMachine); bool assemble(AssemblyStack::Language _language, AssemblyStack::Machine _targetMachine, bool _optimize);
void outputCompilationResults(); void outputCompilationResults();

View File

@ -262,11 +262,46 @@ SOLTMPDIR=$(mktemp -d)
) )
rm -rf "$SOLTMPDIR" rm -rf "$SOLTMPDIR"
printTask "Testing assemble, yul, strict-assembly..." test_solc_assembly_output() {
local input="${1}"
local expected="${2}"
local solc_args="${3}"
local expected_object="object \"object\" { code "${expected}" }"
output=$(echo "${input}" | "$SOLC" - ${solc_args} 2>/dev/null)
empty=$(echo $output | sed -ne '/'"${expected_object}"'/p')
if [ -z "$empty" ]
then
printError "Incorrect assembly output. Expected: "
echo -e ${expected}
printError "with arguments ${solc_args}, but got:"
echo "${output}"
exit 1
fi
}
printTask "Testing assemble, yul, strict-assembly and optimize..."
(
echo '{}' | "$SOLC" - --assemble &>/dev/null echo '{}' | "$SOLC" - --assemble &>/dev/null
echo '{}' | "$SOLC" - --yul &>/dev/null echo '{}' | "$SOLC" - --yul &>/dev/null
echo '{}' | "$SOLC" - --strict-assembly &>/dev/null echo '{}' | "$SOLC" - --strict-assembly &>/dev/null
# Test options above in conjunction with --optimize.
# Using both, --assemble and --optimize should fail.
! echo '{}' | "$SOLC" - --assemble --optimize &>/dev/null
# Test yul and strict assembly output
# Non-empty code results in non-empty binary representation with optimizations turned off,
# while it results in empty binary representation with optimizations turned on.
test_solc_assembly_output "{ let x:u256 := 0:u256 }" "{ let x:u256 := 0:u256 }" "--yul"
test_solc_assembly_output "{ let x:u256 := 0:u256 }" "{ }" "--yul --optimize"
test_solc_assembly_output "{ let x := 0 }" "{ let x := 0 }" "--strict-assembly"
test_solc_assembly_output "{ let x := 0 }" "{ }" "--strict-assembly --optimize"
)
printTask "Testing standard input..." printTask "Testing standard input..."
SOLTMPDIR=$(mktemp -d) SOLTMPDIR=$(mktemp -d)
( (