Support compiling Yul within StandardCompiler

This commit is contained in:
Alex Beregszaszi 2017-04-21 15:34:40 +01:00
parent ed1ad2fc14
commit 3bc2c35cc4
22 changed files with 265 additions and 14 deletions

View File

@ -9,6 +9,7 @@ Language Features:
Compiler Features: Compiler Features:
* Function calls with named arguments now work with overloaded functions. * Function calls with named arguments now work with overloaded functions.
* Inline Assembly: Issue error when using ``callvalue()`` inside nonpayable function (in the same way that ``msg.value`` already does). * Inline Assembly: Issue error when using ``callvalue()`` inside nonpayable function (in the same way that ``msg.value`` already does).
* Standard JSON Interface: Support "Yul" as input language.
* SMTChecker: Show callstack together with model if applicable. * SMTChecker: Show callstack together with model if applicable.
* SMTChecker: Support modifiers. * SMTChecker: Support modifiers.
* Yul Optimizer: Enable stack allocation optimization by default if yul optimizer is active (disable in yulDetails). * Yul Optimizer: Enable stack allocation optimization by default if yul optimizer is active (disable in yulDetails).

View File

@ -147,7 +147,7 @@ Input Description
.. code-block:: none .. code-block:: none
{ {
// Required: Source code language, such as "Solidity", "Vyper", "lll", "assembly", etc. // Required: Source code language. Currently supported are "Solidity" and "Yul".
"language": "Solidity", "language": "Solidity",
// Required // Required
"sources": "sources":
@ -263,8 +263,9 @@ Input Description
// devdoc - Developer documentation (natspec) // devdoc - Developer documentation (natspec)
// userdoc - User documentation (natspec) // userdoc - User documentation (natspec)
// metadata - Metadata // metadata - Metadata
// ir - New assembly format before desugaring // ir - Yul intermediate representation of the code before optimization
// evm.assembly - New assembly format after desugaring // irOptimized - Intermediate representation after optimization
// evm.assembly - New assembly format
// evm.legacyAssembly - Old-style assembly format in JSON // evm.legacyAssembly - Old-style assembly format in JSON
// evm.bytecode.object - Bytecode object // evm.bytecode.object - Bytecode object
// evm.bytecode.opcodes - Opcodes list // evm.bytecode.opcodes - Opcodes list
@ -273,8 +274,8 @@ Input Description
// evm.deployedBytecode* - Deployed bytecode (has the same options as evm.bytecode) // evm.deployedBytecode* - Deployed bytecode (has the same options as evm.bytecode)
// evm.methodIdentifiers - The list of function hashes // evm.methodIdentifiers - The list of function hashes
// evm.gasEstimates - Function gas estimates // evm.gasEstimates - Function gas estimates
// ewasm.wast - eWASM S-expressions format (not supported atm) // ewasm.wast - eWASM S-expressions format (not supported at the moment)
// ewasm.wasm - eWASM binary format (not supported atm) // ewasm.wasm - eWASM binary format (not supported at the moment)
// //
// Note that using a using `evm`, `evm.bytecode`, `ewasm`, etc. will select every // Note that using a using `evm`, `evm.bytecode`, `ewasm`, etc. will select every
// target part of that output. Additionally, `*` can be used as a wildcard to request everything. // target part of that output. Additionally, `*` can be used as a wildcard to request everything.

View File

@ -15,6 +15,23 @@ It can already be used for "inline assembly" inside Solidity and
future versions of the Solidity compiler will even use Yul as intermediate future versions of the Solidity compiler will even use Yul as intermediate
language. It should also be easy to build high-level optimizer stages for Yul. language. It should also be easy to build high-level optimizer stages for Yul.
In its flavour of inline-assembly, Yul can be used as a language setting
for the :ref:`standard-json interface <compiler-api>`:
::
{
"language": "Yul",
"sources": { "input.yul": { "content": "{ sstore(0, 1) }" } },
"settings": {
"outputSelection": { "*": { "*": ["*"], "": [ "*" ] } },
"optimizer": { "enabled": true, "details": { "yul": true } }
}
}
Furthermore, the commandline interface can be switched to Yul mode
using ``solc --strict-assembly``.
.. note:: .. note::
Note that the flavour used for "inline assembly" does not have types Note that the flavour used for "inline assembly" does not have types

View File

@ -23,6 +23,7 @@
#include <libsolidity/interface/StandardCompiler.h> #include <libsolidity/interface/StandardCompiler.h>
#include <libsolidity/ast/ASTJsonConverter.h> #include <libsolidity/ast/ASTJsonConverter.h>
#include <libyul/AssemblyStack.h>
#include <liblangutil/SourceReferenceFormatter.h> #include <liblangutil/SourceReferenceFormatter.h>
#include <libevmasm/Instruction.h> #include <libevmasm/Instruction.h>
#include <libdevcore/JSON.h> #include <libdevcore/JSON.h>
@ -37,6 +38,7 @@ using namespace std;
using namespace dev; using namespace dev;
using namespace langutil; using namespace langutil;
using namespace dev::solidity; using namespace dev::solidity;
using namespace yul;
namespace { namespace {
@ -354,7 +356,6 @@ boost::optional<Json::Value> checkOutputSelection(Json::Value const& _outputSele
return boost::none; return boost::none;
} }
/// Validates the optimizer settings and returns them in a parsed object. /// Validates the optimizer settings and returns them in a parsed object.
/// On error returns the json-formatted error message. /// On error returns the json-formatted error message.
boost::variant<OptimiserSettings, Json::Value> parseOptimizerSettings(Json::Value const& _jsonInput) boost::variant<OptimiserSettings, Json::Value> parseOptimizerSettings(Json::Value const& _jsonInput)
@ -427,8 +428,7 @@ boost::variant<StandardCompiler::InputsAndSettings, Json::Value> StandardCompile
if (auto result = checkRootKeys(_input)) if (auto result = checkRootKeys(_input))
return *result; return *result;
if (_input["language"] != "Solidity") ret.language = _input["language"].asString();
return formatFatalError("JSONError", "Only \"Solidity\" is supported as a language.");
Json::Value const& sources = _input["sources"]; Json::Value const& sources = _input["sources"];
@ -850,15 +850,86 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
return output; return output;
} }
Json::Value StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings)
{
if (_inputsAndSettings.sources.size() != 1)
return formatFatalError("JSONError", "Yul mode only supports exactly one input file.");
if (!_inputsAndSettings.smtLib2Responses.empty())
return formatFatalError("JSONError", "Yul mode does not support smtlib2responses.");
if (!_inputsAndSettings.remappings.empty())
return formatFatalError("JSONError", "Field \"settings.remappings\" cannot be used for Yul.");
if (!_inputsAndSettings.libraries.empty())
return formatFatalError("JSONError", "Field \"settings.libraries\" cannot be used for Yul.");
Json::Value output = Json::objectValue;
AssemblyStack stack(_inputsAndSettings.evmVersion, AssemblyStack::Language::StrictAssembly);
string const& sourceName = _inputsAndSettings.sources.begin()->first;
string const& sourceContents = _inputsAndSettings.sources.begin()->second;
if (!stack.parseAndAnalyze(sourceName, sourceContents))
{
Json::Value errors = Json::arrayValue;
for (auto const& error: stack.errors())
{
auto err = dynamic_pointer_cast<Error const>(error);
errors.append(formatErrorWithException(
*error,
err->type() == Error::Type::Warning,
err->typeName(),
"general",
""
));
}
output["errors"] = errors;
return output;
}
string contractName = stack.parserResult()->name.str();
if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "ir"))
output["contracts"][sourceName][contractName]["ir"] = stack.print();
if (_inputsAndSettings.optimiserSettings.runYulOptimiser)
stack.optimize();
MachineAssemblyObject object = stack.assemble(
AssemblyStack::Machine::EVM,
_inputsAndSettings.optimiserSettings.optimizeStackAllocation
);
if (isArtifactRequested(
_inputsAndSettings.outputSelection,
sourceName,
contractName,
{ "evm.bytecode", "evm.bytecode.object", "evm.bytecode.opcodes", "evm.bytecode.sourceMap", "evm.bytecode.linkReferences" }
))
output["contracts"][sourceName][contractName]["evm"]["bytecode"] = collectEVMObject(*object.bytecode, nullptr);
if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "irOptimized"))
output["contracts"][sourceName][contractName]["irOptimized"] = stack.print();
if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "evm.assembly"))
output["contracts"][sourceName][contractName]["evm"]["assembly"] = object.assembly;
return output;
}
Json::Value StandardCompiler::compile(Json::Value const& _input) noexcept Json::Value StandardCompiler::compile(Json::Value const& _input) noexcept
{ {
try try
{ {
auto parsed = parseInput(_input); auto parsed = parseInput(_input);
if (parsed.type() == typeid(InputsAndSettings)) if (parsed.type() == typeid(Json::Value))
return compileSolidity(boost::get<InputsAndSettings>(std::move(parsed)));
else
return boost::get<Json::Value>(std::move(parsed)); return boost::get<Json::Value>(std::move(parsed));
InputsAndSettings settings = boost::get<InputsAndSettings>(std::move(parsed));
if (settings.language == "Solidity")
return compileSolidity(std::move(settings));
else if (settings.language == "Yul")
return compileYul(std::move(settings));
else
return formatFatalError("JSONError", "Only \"Solidity\" or \"Yul\" is supported as a language.");
} }
catch (Json::LogicError const& _exception) catch (Json::LogicError const& _exception)
{ {

View File

@ -58,6 +58,7 @@ public:
private: private:
struct InputsAndSettings struct InputsAndSettings
{ {
std::string language;
Json::Value errors; Json::Value errors;
std::map<std::string, std::string> sources; std::map<std::string, std::string> sources;
std::map<h256, std::string> smtLib2Responses; std::map<h256, std::string> smtLib2Responses;
@ -74,6 +75,7 @@ private:
boost::variant<InputsAndSettings, Json::Value> parseInput(Json::Value const& _input); boost::variant<InputsAndSettings, Json::Value> parseInput(Json::Value const& _input);
Json::Value compileSolidity(InputsAndSettings _inputsAndSettings); Json::Value compileSolidity(InputsAndSettings _inputsAndSettings);
Json::Value compileYul(InputsAndSettings _inputsAndSettings);
ReadCallback::Callback m_readFile; ReadCallback::Callback m_readFile;
}; };

View File

@ -0,0 +1,17 @@
{
"language": "Yul",
"sources":
{
"A":
{
"content": "{ let x := mload(0) sstore(add(x, 0), 0) }"
}
},
"settings":
{
"outputSelection":
{
"*": { "*": ["*"], "": [ "*" ] }
}
}
}

View File

@ -0,0 +1 @@
{"contracts":{"A":{"object":{"evm":{"assembly":" /* \"A\":17:18 */\n 0x00\n /* \"A\":11:19 */\n mload\n /* \"A\":38:39 */\n 0x00\n /* \"A\":34:35 */\n 0x00\n /* \"A\":31:32 */\n dup3\n /* \"A\":27:36 */\n add\n /* \"A\":20:40 */\n sstore\n /* \"A\":0:42 */\n pop\n","bytecode":{"linkReferences":{},"object":"6000516000600082015550","opcodes":"PUSH1 0x0 MLOAD PUSH1 0x0 PUSH1 0x0 DUP3 ADD SSTORE POP ","sourceMap":""}},"ir":"object \"object\" {\n code {\n let x := mload(0)\n sstore(add(x, 0), 0)\n }\n}\n","irOptimized":"object \"object\" {\n code {\n let x := mload(0)\n sstore(add(x, 0), 0)\n }\n}\n"}}}}

View File

@ -0,0 +1,17 @@
{
"language": "Yul",
"sources":
{
"A":
{
"content": "object \"NamedObject\" { code { let x := dataoffset(\"DataName\") sstore(add(x, 0), 0) } data \"DataName\" \"abc\" object \"OtherObject\" { code { revert(0, 0) } } }"
}
},
"settings":
{
"outputSelection":
{
"A": { "OtherObject": ["*"], "": [ "*" ] }
}
}
}

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1,17 @@
{
"language": "Yul",
"sources":
{
"A":
{
"content": "{ let x := mload(0) sstore(add(x, 0), 0) }"
}
},
"settings":
{
"outputSelection":
{
"A": { "OtherObject": ["*"], "": [ "*" ] }
}
}
}

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1,21 @@
{
"language": "Yul",
"sources":
{
"A":
{
"content": "{ let x := mload(0) sstore(add(x, 0), 0) }"
},
"B":
{
"content": "{ let x := mload(0) sstore(add(x, 0), 0) }"
}
},
"settings":
{
"outputSelection":
{
"*": { "*": ["*"], "": [ "*" ] }
}
}
}

View File

@ -0,0 +1 @@
{"errors":[{"component":"general","formattedMessage":"Yul mode only supports exactly one input file.","message":"Yul mode only supports exactly one input file.","severity":"error","type":"JSONError"}]}

View File

@ -0,0 +1,21 @@
{
"language": "Yul",
"sources":
{
"A":
{
"content": "{ let x := mload(0) sstore(add(x, 0), 0) }"
},
"B":
{
"content": "{ let x := mload(0) sstore(add(x, 0), 0) }"
}
},
"settings":
{
"outputSelection":
{
"B": { "*": ["*"], "": [ "*" ] }
}
}
}

View File

@ -0,0 +1 @@
{"errors":[{"component":"general","formattedMessage":"Yul mode only supports exactly one input file.","message":"Yul mode only supports exactly one input file.","severity":"error","type":"JSONError"}]}

View File

@ -0,0 +1,17 @@
{
"language": "Yul",
"sources":
{
"A":
{
"content": "object \"NamedObject\" { code { let x := dataoffset(\"DataName\") sstore(add(x, 0), 0) } data \"DataName\" \"abc\" }"
}
},
"settings":
{
"outputSelection":
{
"*": { "*": ["*"], "": [ "*" ] }
}
}
}

View File

@ -0,0 +1 @@
{"contracts":{"A":{"NamedObject":{"evm":{"assembly":" data_4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45\n /* \"A\":80:81 */\n 0x00\n /* \"A\":76:77 */\n 0x00\n /* \"A\":73:74 */\n dup3\n /* \"A\":69:78 */\n add\n /* \"A\":62:82 */\n sstore\n /* \"A\":28:84 */\n pop\nstop\ndata_4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45 616263\n","bytecode":{"linkReferences":{},"object":"600b6000600082015550fe616263","opcodes":"PUSH1 0xB PUSH1 0x0 PUSH1 0x0 DUP3 ADD SSTORE POP INVALID PUSH2 0x6263 ","sourceMap":""}},"ir":"object \"NamedObject\" {\n code {\n let x := dataoffset(\"DataName\")\n sstore(add(x, 0), 0)\n }\n data \"DataName\" hex\"616263\"\n}\n","irOptimized":"object \"NamedObject\" {\n code {\n let x := dataoffset(\"DataName\")\n sstore(add(x, 0), 0)\n }\n data \"DataName\" hex\"616263\"\n}\n"}}}}

View File

@ -0,0 +1,17 @@
{
"language": "Yul",
"sources":
{
"A":
{
"content": "object \"NamedObject\" { code { let x := dataoffset(\"DataName\") sstore(add(x, 0), 0) } data \"DataName\" \"abc\" object \"OtherObject\" { code { revert(0, 0) } } }"
}
},
"settings":
{
"outputSelection":
{
"A": { "NamedObject": ["*"], "": [ "*" ] }
}
}
}

View File

@ -0,0 +1 @@
{"contracts":{"A":{"NamedObject":{"evm":{"assembly":" data_4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45\n /* \"A\":80:81 */\n 0x00\n /* \"A\":76:77 */\n 0x00\n /* \"A\":73:74 */\n dup3\n /* \"A\":69:78 */\n add\n /* \"A\":62:82 */\n sstore\n /* \"A\":28:84 */\n pop\nstop\ndata_4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45 616263\n\nsub_0: assembly {\n /* \"A\":147:148 */\n 0x00\n /* \"A\":144:145 */\n 0x00\n /* \"A\":137:149 */\n revert\n}\n","bytecode":{"linkReferences":{},"object":"600b6000600082015550fe616263","opcodes":"PUSH1 0xB PUSH1 0x0 PUSH1 0x0 DUP3 ADD SSTORE POP INVALID PUSH2 0x6263 ","sourceMap":""}},"ir":"object \"NamedObject\" {\n code {\n let x := dataoffset(\"DataName\")\n sstore(add(x, 0), 0)\n }\n data \"DataName\" hex\"616263\"\n object \"OtherObject\" {\n code {\n revert(0, 0)\n }\n }\n}\n","irOptimized":"object \"NamedObject\" {\n code {\n let x := dataoffset(\"DataName\")\n sstore(add(x, 0), 0)\n }\n data \"DataName\" hex\"616263\"\n object \"OtherObject\" {\n code {\n revert(0, 0)\n }\n }\n}\n"}}}}

View File

@ -0,0 +1,23 @@
{
"language": "Yul",
"sources":
{
"A":
{
"content": "{ let x := mload(0) sstore(add(x, 0), 0) }"
}
},
"settings":
{
"optimizer": {
"enabled": true,
"details": {
"yul": true
}
},
"outputSelection":
{
"*": { "*": ["*"], "": [ "*" ] }
}
}
}

View File

@ -0,0 +1 @@
{"contracts":{"A":{"object":{"evm":{"assembly":" /* \"A\":17:18 */\n 0x00\n 0x00\n /* \"A\":11:19 */\n mload\n /* \"A\":20:40 */\n sstore\n","bytecode":{"linkReferences":{},"object":"600060005155","opcodes":"PUSH1 0x0 PUSH1 0x0 MLOAD SSTORE ","sourceMap":""}},"ir":"object \"object\" {\n code {\n let x := mload(0)\n sstore(add(x, 0), 0)\n }\n}\n","irOptimized":"object \"object\" {\n code {\n sstore(mload(0), 0)\n }\n}\n"}}}}

View File

@ -124,11 +124,12 @@ BOOST_AUTO_TEST_CASE(invalid_language)
{ {
char const* input = R"( char const* input = R"(
{ {
"language": "INVALID" "language": "INVALID",
"sources": { "name": { "content": "abc" } }
} }
)"; )";
Json::Value result = compile(input); Json::Value result = compile(input);
BOOST_CHECK(containsError(result, "JSONError", "Only \"Solidity\" is supported as a language.")); BOOST_CHECK(containsError(result, "JSONError", "Only \"Solidity\" or \"Yul\" is supported as a language."));
} }
BOOST_AUTO_TEST_CASE(valid_language) BOOST_AUTO_TEST_CASE(valid_language)
@ -139,7 +140,7 @@ BOOST_AUTO_TEST_CASE(valid_language)
} }
)"; )";
Json::Value result = compile(input); Json::Value result = compile(input);
BOOST_CHECK(!containsError(result, "JSONError", "Only \"Solidity\" is supported as a language.")); BOOST_CHECK(!containsError(result, "JSONError", "Only \"Solidity\" or \"Yul\" is supported as a language."));
} }
BOOST_AUTO_TEST_CASE(no_sources) BOOST_AUTO_TEST_CASE(no_sources)