mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Support compiling Yul within StandardCompiler
This commit is contained in:
parent
ed1ad2fc14
commit
3bc2c35cc4
@ -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).
|
||||
|
@ -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.
|
||||
|
17
docs/yul.rst
17
docs/yul.rst
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
};
|
||||
|
17
test/cmdlineTests/standard_yul/input.json
Normal file
17
test/cmdlineTests/standard_yul/input.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"language": "Yul",
|
||||
"sources":
|
||||
{
|
||||
"A":
|
||||
{
|
||||
"content": "{ let x := mload(0) sstore(add(x, 0), 0) }"
|
||||
}
|
||||
},
|
||||
"settings":
|
||||
{
|
||||
"outputSelection":
|
||||
{
|
||||
"*": { "*": ["*"], "": [ "*" ] }
|
||||
}
|
||||
}
|
||||
}
|
1
test/cmdlineTests/standard_yul/output.json
Normal file
1
test/cmdlineTests/standard_yul/output.json
Normal 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"}}}}
|
@ -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": ["*"], "": [ "*" ] }
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
{}
|
@ -0,0 +1,17 @@
|
||||
{
|
||||
"language": "Yul",
|
||||
"sources":
|
||||
{
|
||||
"A":
|
||||
{
|
||||
"content": "{ let x := mload(0) sstore(add(x, 0), 0) }"
|
||||
}
|
||||
},
|
||||
"settings":
|
||||
{
|
||||
"outputSelection":
|
||||
{
|
||||
"A": { "OtherObject": ["*"], "": [ "*" ] }
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
{}
|
21
test/cmdlineTests/standard_yul_multiple_files/input.json
Normal file
21
test/cmdlineTests/standard_yul_multiple_files/input.json
Normal 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":
|
||||
{
|
||||
"*": { "*": ["*"], "": [ "*" ] }
|
||||
}
|
||||
}
|
||||
}
|
@ -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"}]}
|
@ -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": { "*": ["*"], "": [ "*" ] }
|
||||
}
|
||||
}
|
||||
}
|
@ -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"}]}
|
17
test/cmdlineTests/standard_yul_object/input.json
Normal file
17
test/cmdlineTests/standard_yul_object/input.json
Normal 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":
|
||||
{
|
||||
"*": { "*": ["*"], "": [ "*" ] }
|
||||
}
|
||||
}
|
||||
}
|
1
test/cmdlineTests/standard_yul_object/output.json
Normal file
1
test/cmdlineTests/standard_yul_object/output.json
Normal 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"}}}}
|
17
test/cmdlineTests/standard_yul_object_name/input.json
Normal file
17
test/cmdlineTests/standard_yul_object_name/input.json
Normal 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": ["*"], "": [ "*" ] }
|
||||
}
|
||||
}
|
||||
}
|
1
test/cmdlineTests/standard_yul_object_name/output.json
Normal file
1
test/cmdlineTests/standard_yul_object_name/output.json
Normal 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"}}}}
|
23
test/cmdlineTests/standard_yul_optimized/input.json
Normal file
23
test/cmdlineTests/standard_yul_optimized/input.json
Normal 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":
|
||||
{
|
||||
"*": { "*": ["*"], "": [ "*" ] }
|
||||
}
|
||||
}
|
||||
}
|
1
test/cmdlineTests/standard_yul_optimized/output.json
Normal file
1
test/cmdlineTests/standard_yul_optimized/output.json
Normal 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"}}}}
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user