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:
* 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).
* Standard JSON Interface: Support "Yul" as input language.
* SMTChecker: Show callstack together with model if applicable.
* SMTChecker: Support modifiers.
* 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
{
// Required: Source code language, such as "Solidity", "Vyper", "lll", "assembly", etc.
// Required: Source code language. Currently supported are "Solidity" and "Yul".
"language": "Solidity",
// Required
"sources":
@ -263,8 +263,9 @@ Input Description
// devdoc - Developer documentation (natspec)
// userdoc - User documentation (natspec)
// metadata - Metadata
// ir - New assembly format before desugaring
// evm.assembly - New assembly format after desugaring
// ir - Yul intermediate representation of the code before optimization
// irOptimized - Intermediate representation after optimization
// evm.assembly - New assembly format
// evm.legacyAssembly - Old-style assembly format in JSON
// evm.bytecode.object - Bytecode object
// evm.bytecode.opcodes - Opcodes list
@ -273,8 +274,8 @@ Input Description
// evm.deployedBytecode* - Deployed bytecode (has the same options as evm.bytecode)
// evm.methodIdentifiers - The list of function hashes
// evm.gasEstimates - Function gas estimates
// ewasm.wast - eWASM S-expressions format (not supported atm)
// ewasm.wasm - eWASM binary format (not supported atm)
// ewasm.wast - eWASM S-expressions format (not supported at the moment)
// ewasm.wasm - eWASM binary format (not supported at the moment)
//
// 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.

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
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 that the flavour used for "inline assembly" does not have types

View File

@ -23,6 +23,7 @@
#include <libsolidity/interface/StandardCompiler.h>
#include <libsolidity/ast/ASTJsonConverter.h>
#include <libyul/AssemblyStack.h>
#include <liblangutil/SourceReferenceFormatter.h>
#include <libevmasm/Instruction.h>
#include <libdevcore/JSON.h>
@ -37,6 +38,7 @@ using namespace std;
using namespace dev;
using namespace langutil;
using namespace dev::solidity;
using namespace yul;
namespace {
@ -354,7 +356,6 @@ boost::optional<Json::Value> checkOutputSelection(Json::Value const& _outputSele
return boost::none;
}
/// Validates the optimizer settings and returns them in a parsed object.
/// On error returns the json-formatted error message.
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))
return *result;
if (_input["language"] != "Solidity")
return formatFatalError("JSONError", "Only \"Solidity\" is supported as a language.");
ret.language = _input["language"].asString();
Json::Value const& sources = _input["sources"];
@ -850,15 +850,86 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
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
{
try
{
auto parsed = parseInput(_input);
if (parsed.type() == typeid(InputsAndSettings))
return compileSolidity(boost::get<InputsAndSettings>(std::move(parsed)));
else
if (parsed.type() == typeid(Json::Value))
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)
{

View File

@ -58,6 +58,7 @@ public:
private:
struct InputsAndSettings
{
std::string language;
Json::Value errors;
std::map<std::string, std::string> sources;
std::map<h256, std::string> smtLib2Responses;
@ -74,6 +75,7 @@ private:
boost::variant<InputsAndSettings, Json::Value> parseInput(Json::Value const& _input);
Json::Value compileSolidity(InputsAndSettings _inputsAndSettings);
Json::Value compileYul(InputsAndSettings _inputsAndSettings);
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"(
{
"language": "INVALID"
"language": "INVALID",
"sources": { "name": { "content": "abc" } }
}
)";
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)
@ -139,7 +140,7 @@ BOOST_AUTO_TEST_CASE(valid_language)
}
)";
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)