diff --git a/Changelog.md b/Changelog.md index c14af7738..5c5e07dd4 100644 --- a/Changelog.md +++ b/Changelog.md @@ -81,6 +81,7 @@ Compiler Features: * SMTChecker: Properties that are proved safe are now reported explicitly at the end of analysis. By default, only the number of safe properties is shown. The CLI option ``--model-checker-show-proved-safe`` and the JSON option ``settings.modelChecker.showProvedSafe`` can be enabled to show the full list of safe properties. * Standard JSON Interface: Add experimental support for importing ASTs via Standard JSON. * Yul EVM Code Transform: If available, use ``push0`` instead of ``codesize`` to produce an arbitrary value on stack in order to create equal stack heights between branches. + * Yul IR Code Generation: Cheaper code for reverting with errors of a static small encoding size. Bugfixes: diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index c05fffbff..b19ab6cfc 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -47,6 +47,7 @@ #include #include +#include #include using namespace solidity; @@ -57,6 +58,18 @@ using namespace std::string_literals; namespace { +optional staticEncodingSize(vector const& _parameterTypes) +{ + size_t encodedSize = 0; + for (auto const* type: _parameterTypes) + { + if (type->isDynamicallyEncoded()) + return nullopt; + encodedSize += type->calldataHeadSize(); + } + return encodedSize; +} + struct CopyTranslate: public yul::ASTCopier { using ExternalRefsMap = std::map; @@ -3362,8 +3375,16 @@ void IRGeneratorForStatements::revertWithError( std::vector> const& _errorArguments ) { + bool needsAllocation = true; + if (optional size = staticEncodingSize(_parameterTypes)) + if (ranges::all_of(_parameterTypes, [](auto const* type) { return type && type->isValueType(); })) + needsAllocation = *size + 4 > CompilerUtils::generalPurposeMemoryStart; Whiskers templ(R"({ + let := () + + let := 0 + mstore(, ) let := (add(, 4) ) revert(, sub(, )) @@ -3371,7 +3392,9 @@ void IRGeneratorForStatements::revertWithError( templ("pos", m_context.newYulVariable()); templ("end", m_context.newYulVariable()); templ("hash", util::selectorFromSignatureU256(_signature).str()); - templ("allocateUnbounded", m_utils.allocateUnboundedFunction()); + templ("needsAllocation", needsAllocation); + if (needsAllocation) + templ("allocateUnbounded", m_utils.allocateUnboundedFunction()); std::vector errorArgumentVars; std::vector errorArgumentTypes; diff --git a/test/libsolidity/semanticTests/errors/small_error_optimization.sol b/test/libsolidity/semanticTests/errors/small_error_optimization.sol new file mode 100644 index 000000000..cb05d308f --- /dev/null +++ b/test/libsolidity/semanticTests/errors/small_error_optimization.sol @@ -0,0 +1,19 @@ +error E(); +contract A { + uint8[] x; + function f() public { + for (uint i = 0; i < 100; ++i) + x.push(uint8(i)); + revert E(); + } +} +contract B { + function f() public { + (new A()).f(); + } +} +// ---- +// f() -> FAILURE, hex"92bbf6e8" +// gas irOptimized: 270934 +// gas legacy: 310592 +// gas legacyOptimized: 273662