diff options
112 files changed, 1742 insertions, 864 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml index 66fe6430..52bb36cf 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -34,7 +34,7 @@ version: 2 jobs: build_emscripten: docker: - - image: trzeci/emscripten:sdk-tag-1.37.21-64bit + - image: trzeci/emscripten:sdk-tag-1.38.8-64bit environment: TERM: xterm steps: diff --git a/.travis.yml b/.travis.yml index 2036f03e..a8a68aff 100644 --- a/.travis.yml +++ b/.travis.yml @@ -114,7 +114,7 @@ matrix: before_install: - nvm install 8 - nvm use 8 - - docker pull trzeci/emscripten:sdk-tag-1.37.21-64bit + - docker pull trzeci/emscripten:sdk-tag-1.38.8-64bit env: - SOLC_EMSCRIPTEN=On - SOLC_INSTALL_DEPS_TRAVIS=Off diff --git a/Changelog.md b/Changelog.md index bb7b191f..0562426d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,7 @@ ### 0.5.3 (unreleased) Language Features: + * Provide access to creation and runtime code of contracts via ``type(C).creationCode`` / ``type(C).runtimeCode``. Compiler Features: @@ -8,10 +9,15 @@ Compiler Features: Bugfixes: + * Emscripten: Split simplification rule initialization up further to work around issues with soljson.js in some browsers. + * TypeChecker: Return type error if fixed point encoding is attempted instead of throwing ``UnimplementedFeatureError``. * Yul: Check that arguments to ``dataoffset`` and ``datasize`` are literals at parse time and properly take this into account in the optimizer. + * Yul: Parse number literals for detecting duplicate switch cases. + * Yul: Require switch cases to have the same type. Build System: + * Emscripten: Upgrade to emscripten 1.38.8 on travis and circleci. ### 0.5.2 (2018-12-19) diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index c45ecba6..d05ccaff 100644 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -98,7 +98,13 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA # Abort if linking results in any undefined symbols # Note: this is on by default in the CMake Emscripten module which we aren't using set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s ERROR_ON_UNDEFINED_SYMBOLS=1") - add_definitions(-DETH_EMSCRIPTEN=1) + # Disallow deprecated emscripten build options. + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s STRICT=1") + # Export the Emscripten-generated auxiliary methods which are needed by solc-js. + # Which methods of libsolc itself are exported is specified in libsolc/CMakeLists.txt. + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s EXTRA_EXPORTED_RUNTIME_METHODS=['cwrap','addFunction','removeFunction','Pointer_stringify','lengthBytesUTF8','_malloc','stringToUTF8','setValue']") + # Do not build as a WebAssembly target - we need an asm.js output. + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s WASM=0") endif() endif() diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index 68ca8ec4..26293a1f 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -611,15 +611,15 @@ Through ``abi.encodePacked()``, Solidity supports a non-standard packed mode whe This packed mode is mainly used for indexed event parameters. -As an example, the encoding of ``int8(-1), bytes1(0x42), uint16(0x2424), string("Hello, world!")`` results in: +As an example, the encoding of ``int16(-1), bytes1(0x42), uint16(0x03), string("Hello, world!")`` results in: .. code-block:: none - 0xff42242448656c6c6f2c20776f726c6421 - ^^ int8(-1) - ^^ bytes1(0x42) - ^^^^ uint16(0x2424) - ^^^^^^^^^^^^^^^^^^^^^^^^^^ string("Hello, world!") without a length field + 0xffff42000348656c6c6f2c20776f726c6421 + ^^^^ int16(-1) + ^^ bytes1(0x42) + ^^^^ uint16(0x03) + ^^^^^^^^^^^^^^^^^^^^^^^^^^ string("Hello, world!") without a length field More specifically: - Each value type takes as many bytes as its range has. diff --git a/docs/common-patterns.rst b/docs/common-patterns.rst index 3704b730..65bf7f44 100644 --- a/docs/common-patterns.rst +++ b/docs/common-patterns.rst @@ -117,7 +117,7 @@ to read the data, so will everyone else. You can restrict read access to your contract's state by **other contracts**. That is actually the default -unless you declare make your state variables ``public``. +unless you declare your state variables ``public``. Furthermore, you can restrict who can make modifications to your contract's state or call your contract's diff --git a/docs/contracts.rst b/docs/contracts.rst index 646e4cdc..5bab6e78 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -10,7 +10,8 @@ Contracts in Solidity are similar to classes in object-oriented languages. They contain persistent data in state variables and functions that can modify these variables. Calling a function on a different contract (instance) will perform an EVM function call and thus switch the context such that state variables are -inaccessible. +inaccessible. A contract and its functions need to be called for anything to happen. +There is no "cron" concept in Ethereum to call a function at a particular event automatically. .. include:: contracts/creating-contracts.rst diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index 1c24eef1..11db780c 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -54,18 +54,6 @@ Yes, you can use ``abi.encodePacked``:: } } - -Why is the low-level function ``.call()`` less favorable than instantiating a contract with a variable (``ContractB b;``) and executing its functions (``b.doSomething();``)? -============================================================================================================================================================================= - -If you use actual functions, the compiler will tell you if the types -or your arguments do not match, if the function does not exist -or is not visible and it will do the packing of the -arguments for you. - -See `ping.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/45_ping.sol>`_ and -`pong.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/45_pong.sol>`_. - What happens if you send ether along with a function call to a contract? ======================================================================== @@ -121,16 +109,6 @@ does not fit inside this range, it is truncated. These truncations can have `serious consequences <https://en.bitcoin.it/wiki/Value_overflow_incident>`_, so code like the one above is necessary to avoid certain attacks. - -Why can number literals not be converted to fixed-size bytes types? -=================================================================== - -Since version 0.5.0 only hexadecimal number literals can be converted to fixed-size bytes -types and only if the number of hex digits matches the size of the type. See :ref:`types-conversion-literals` -for a full explanation and examples. - - - More Questions? =============== diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 5a6f3875..69124c77 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -385,6 +385,8 @@ Global Variables - ``<address>.balance`` (``uint256``): balance of the :ref:`address` in Wei - ``<address payable>.send(uint256 amount) returns (bool)``: send given amount of Wei to :ref:`address`, returns ``false`` on failure - ``<address payable>.transfer(uint256 amount)``: send given amount of Wei to :ref:`address`, throws on failure +- ``type(C).creationCode`` (``bytes memory``): creation bytecode of the given contract, see :ref:`Type Information<meta-type>`. +- ``type(C).runtimeCode`` (``bytes memory``): runtime bytecode of the given contract, see :ref:`Type Information<meta-type>`. .. note:: Do not rely on ``block.timestamp``, ``now`` and ``blockhash`` as a source of randomness, @@ -445,7 +447,7 @@ These keywords are reserved in Solidity. They might become part of the syntax in ``abstract``, ``after``, ``alias``, ``apply``, ``auto``, ``case``, ``catch``, ``copyof``, ``default``, ``define``, ``final``, ``immutable``, ``implements``, ``in``, ``inline``, ``let``, ``macro``, ``match``, ``mutable``, ``null``, ``of``, ``override``, ``partial``, ``promise``, ``reference``, ``relocatable``, -``sealed``, ``sizeof``, ``static``, ``supports``, ``switch``, ``try``, ``type``, ``typedef``, ``typeof``, +``sealed``, ``sizeof``, ``static``, ``supports``, ``switch``, ``try``, ``typedef``, ``typeof``, ``unchecked``. Language Grammar diff --git a/docs/types/value-types.rst b/docs/types/value-types.rst index 077587dd..09db1423 100644 --- a/docs/types/value-types.rst +++ b/docs/types/value-types.rst @@ -329,6 +329,9 @@ Contracts do not support any operators. The members of contract types are the external functions of the contract including public state variables. +For a contract ``C`` you can use ``type(C)`` to access +:ref:`type information<meta-type>` about the contract. + .. index:: byte array, bytes32 Fixed-size byte arrays diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index 59acfcff..ce7706c1 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -200,6 +200,10 @@ Members of Address Types For more information, see the section on :ref:`address`. .. warning:: + You should avoid using ``.call()`` whenever possible when executing another contract function as it bypasses type checking, + function existence check, and argument packing. + +.. warning:: There are some dangers in using ``send``: The transfer fails if the call stack depth is at 1024 (this can always be forced by the caller) and it also fails if the recipient runs out of gas. So in order to make safe Ether transfers, always check the return value of ``send``, use ``transfer`` or even better: @@ -240,3 +244,33 @@ Furthermore, all functions of the current contract are callable directly includi .. note:: Prior to version 0.5.0, there was a function called ``suicide`` with the same semantics as ``selfdestruct``. + +.. index:: type, creationCode, runtimeCode + +.. _meta-type: + +Type Information +---------------- + +The expression ``type(X)`` can be used to retrieve information about the +type ``X``. Currently, there is limited support for this feature, but +it might be expanded in the future. The following properties are +available for a conract type ``C``: + +``type(C).creationCode``: + Memory byte array that contains the creation bytecode of the contract. + This can be used in inline assembly to build custom creation routines, + especially by using the ``create2`` opcode. + This property can **not** be accessed in the contract itself or any + derived contract. It causes the bytecode to be included in the bytecode + of the call site and thus circular references like that are not possible. + +``type(C).runtimeCode``: + Memory byte array that contains the runtime bytecode of the contract. + This is the code that is usually deployed by the constructor of ``C``. + If ``C`` has a constructor that uses inline assembly, this might be + different from the actually deployed bytecode. Also note that libraries + modify their runtime bytecode at time of deployment to guard against + regular calls. + The same restrictions as with ``.creationCode`` also apply for this + property. diff --git a/libdevcore/CMakeLists.txt b/libdevcore/CMakeLists.txt index e68ac10a..193fa41d 100644 --- a/libdevcore/CMakeLists.txt +++ b/libdevcore/CMakeLists.txt @@ -1,7 +1,6 @@ set(sources Algorithms.h Assertions.h - boost_multiprecision_number_compare_bug_workaround.hpp Common.h CommonData.cpp CommonData.h diff --git a/libdevcore/Common.h b/libdevcore/Common.h index 6208424e..e02cf7fa 100644 --- a/libdevcore/Common.h +++ b/libdevcore/Common.h @@ -39,26 +39,13 @@ #include <libdevcore/vector_ref.h> -#if defined(__GNUC__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-parameter" -#endif // defined(__GNUC__) - -// See https://github.com/ethereum/libweb3core/commit/90680a8c25bfb48b24371b4abcacde56c181517c -// See https://svn.boost.org/trac/boost/ticket/11328 -// Bob comment - perhaps we should just HARD FAIL here with Boost-1.58.00? -// It is quite old now, and requiring end-users to use a newer Boost release is probably not unreasonable. #include <boost/version.hpp> -#if (BOOST_VERSION == 105800) - #include "boost_multiprecision_number_compare_bug_workaround.hpp" -#endif // (BOOST_VERSION == 105800) +#if (BOOST_VERSION < 106500) +#error "Unsupported Boost version. At least 1.65 required." +#endif #include <boost/multiprecision/cpp_int.hpp> -#if defined(__GNUC__) -#pragma GCC diagnostic pop -#endif // defined(__GNUC__) - #include <map> #include <vector> #include <functional> diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h index 1d668f26..98331936 100644 --- a/libdevcore/CommonData.h +++ b/libdevcore/CommonData.h @@ -263,6 +263,59 @@ void iterateReplacing(std::vector<T>& _vector, const F& _f) _vector = std::move(modifiedVector); } + +namespace detail +{ +template <typename T, typename F, std::size_t... I> +void iterateReplacingWindow(std::vector<T>& _vector, F const& _f, std::index_sequence<I...>) +{ + // Concept: _f must be Callable, must accept sizeof...(I) parameters of type T&, must return optional<vector<T>> + bool useModified = false; + std::vector<T> modifiedVector; + size_t i = 0; + for (; i + sizeof...(I) <= _vector.size(); ++i) + { + if (boost::optional<std::vector<T>> r = _f(_vector[i + I]...)) + { + if (!useModified) + { + std::move(_vector.begin(), _vector.begin() + i, back_inserter(modifiedVector)); + useModified = true; + } + modifiedVector += std::move(*r); + i += sizeof...(I) - 1; + } + else if (useModified) + modifiedVector.emplace_back(std::move(_vector[i])); + } + if (useModified) + { + for (; i < _vector.size(); ++i) + modifiedVector.emplace_back(std::move(_vector[i])); + _vector = std::move(modifiedVector); + } +} + +} + +/// Function that iterates over the vector @param _vector, +/// calling the function @param _f on sequences of @tparam N of its +/// elements. If @param _f returns a vector, these elements are replaced by +/// the returned vector and the iteration continues with the next @tparam N elements. +/// If the function does not return a vector, the iteration continues with an overlapping +/// sequence of @tparam N elements that starts with the second element of the previous +/// iteration. +/// During the iteration, the original vector is only valid +/// on the current element and after that. The actual replacement takes +/// place at the end, but already visited elements might be invalidated. +/// If nothing is replaced, no copy is performed. +template <std::size_t N, typename T, typename F> +void iterateReplacingWindow(std::vector<T>& _vector, F const& _f) +{ + // Concept: _f must be Callable, must accept N parameters of type T&, must return optional<vector<T>> + detail::iterateReplacingWindow(_vector, _f, std::make_index_sequence<N>{}); +} + /// @returns true iff @a _str passess the hex address checksum test. /// @param _strict if false, hex strings with only uppercase or only lowercase letters /// are considered valid. diff --git a/libdevcore/CommonIO.cpp b/libdevcore/CommonIO.cpp index cc730575..29b31ebc 100644 --- a/libdevcore/CommonIO.cpp +++ b/libdevcore/CommonIO.cpp @@ -128,26 +128,6 @@ int dev::readStandardInputChar() return cin.get(); } -boost::filesystem::path dev::weaklyCanonicalFilesystemPath(boost::filesystem::path const &_path) -{ - if (boost::filesystem::exists(_path)) - return boost::filesystem::canonical(_path); - else - { - boost::filesystem::path head(_path); - boost::filesystem::path tail; - for (auto it = --_path.end(); !head.empty(); --it) - { - if (boost::filesystem::exists(head)) - break; - tail = (*it) / tail; - head.remove_filename(); - } - head = boost::filesystem::canonical(head); - return head / tail; - } -} - string dev::absolutePath(string const& _path, string const& _reference) { boost::filesystem::path p(_path); diff --git a/libdevcore/CommonIO.h b/libdevcore/CommonIO.h index b9f941ea..0d8aca79 100644 --- a/libdevcore/CommonIO.h +++ b/libdevcore/CommonIO.h @@ -50,10 +50,6 @@ std::string toString(_T const& _t) return o.str(); } -/// Partial implementation of boost::filesystem::weakly_canonical (available in boost>=1.60). -/// Should be replaced by the boost implementation as soon as support for boost<1.60 can be dropped. -boost::filesystem::path weaklyCanonicalFilesystemPath(boost::filesystem::path const &_path); - /// @returns the absolute path corresponding to @a _path relative to @a _reference. std::string absolutePath(std::string const& _path, std::string const& _reference); diff --git a/libdevcore/boost_multiprecision_number_compare_bug_workaround.hpp b/libdevcore/boost_multiprecision_number_compare_bug_workaround.hpp deleted file mode 100644 index 2568e17d..00000000 --- a/libdevcore/boost_multiprecision_number_compare_bug_workaround.hpp +++ /dev/null @@ -1,520 +0,0 @@ - -// This is a copy of boost/multiprecision/detail/number_compare.hpp from boost 1.59 to replace buggy version from 1.58. - -#ifdef BOOST_MP_COMPARE_HPP -#error This bug workaround header must be included before original boost/multiprecision/detail/number_compare.hpp -#endif - -/////////////////////////////////////////////////////////////////////////////// -// Copyright 2012 John Maddock. Distributed under the Boost -// Software License, Version 1.0. (See accompanying file -// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -#ifndef BOOST_MP_COMPARE_HPP -#define BOOST_MP_COMPARE_HPP - -// A copy of boost/multiprecision/traits/is_backend.hpp -#ifndef BOOST_MP_IS_BACKEND_HPP -#define BOOST_MP_IS_BACKEND_HPP - -#include <boost/mpl/has_xxx.hpp> -#include <boost/type_traits/conditional.hpp> -#include <boost/type_traits/is_convertible.hpp> -#include <boost/multiprecision/detail/number_base.hpp> -#include <boost/multiprecision/detail/generic_interconvert.hpp> - -namespace boost{ namespace multiprecision{ namespace detail{ - - BOOST_MPL_HAS_XXX_TRAIT_DEF(signed_types) - BOOST_MPL_HAS_XXX_TRAIT_DEF(unsigned_types) - BOOST_MPL_HAS_XXX_TRAIT_DEF(float_types) - - template <class T> - struct is_backend - { - static const bool value = has_signed_types<T>::value && has_unsigned_types<T>::value && has_float_types<T>::value; - }; - - template <class Backend> - struct other_backend - { - typedef typename boost::conditional< - boost::is_same<number<Backend>, number<Backend, et_on> >::value, - number<Backend, et_off>, number<Backend, et_on> >::type type; - }; - - template <class B, class V> - struct number_from_backend - { - typedef typename boost::conditional < - boost::is_convertible<V, number<B> >::value, - number<B>, - typename other_backend<B>::type > ::type type; - }; - - template <bool b, class T, class U> - struct is_first_backend_imp{ static const bool value = false; }; - template <class T, class U> - struct is_first_backend_imp<true, T, U>{ static const bool value = is_convertible<U, number<T, et_on> >::value || is_convertible<U, number<T, et_off> >::value; }; - - template <class T, class U> - struct is_first_backend : is_first_backend_imp<is_backend<T>::value, T, U> {}; - - template <bool b, class T, class U> - struct is_second_backend_imp{ static const bool value = false; }; - template <class T, class U> - struct is_second_backend_imp<true, T, U>{ static const bool value = is_convertible<T, number<U> >::value || is_convertible<T, number<U, et_off> >::value; }; - - template <class T, class U> - struct is_second_backend : is_second_backend_imp<is_backend<U>::value, T, U> {}; - -} -} -} - -#endif // BOOST_MP_IS_BACKEND_HPP - -// -// Comparison operators for number. -// - -namespace boost{ namespace multiprecision{ - -namespace default_ops{ - -template <class B> -inline bool eval_eq(const B& a, const B& b) -{ - return a.compare(b) == 0; -} -template <class T, class U> -inline typename enable_if_c<boost::multiprecision::detail::is_first_backend<T, U>::value, bool>::type eval_eq(const T& a, const U& b) -{ - typename boost::multiprecision::detail::number_from_backend<T, U>::type t(b); - return eval_eq(a, t.backend()); -} -template <class T, class U> -inline typename enable_if_c<boost::multiprecision::detail::is_second_backend<T, U>::value, bool>::type eval_eq(const T& a, const U& b) -{ - typename boost::multiprecision::detail::number_from_backend<U, T>::type t(a); - return eval_eq(t.backend(), b); -} - -template <class B> -inline bool eval_lt(const B& a, const B& b) -{ - return a.compare(b) < 0; -} -template <class T, class U> -inline typename enable_if_c<boost::multiprecision::detail::is_first_backend<T, U>::value, bool>::type eval_lt(const T& a, const U& b) -{ - typename boost::multiprecision::detail::number_from_backend<T, U>::type t(b); - return eval_lt(a, t.backend()); -} -template <class T, class U> -inline typename enable_if_c<boost::multiprecision::detail::is_second_backend<T, U>::value, bool>::type eval_lt(const T& a, const U& b) -{ - typename boost::multiprecision::detail::number_from_backend<U, T>::type t(a); - return eval_lt(t.backend(), b); -} - -template <class B> -inline bool eval_gt(const B& a, const B& b) -{ - return a.compare(b) > 0; -} -template <class T, class U> -inline typename enable_if_c<boost::multiprecision::detail::is_first_backend<T, U>::value, bool>::type eval_gt(const T& a, const U& b) -{ - typename boost::multiprecision::detail::number_from_backend<T, U>::type t(b); - return eval_gt(a, t.backend()); -} -template <class T, class U> -inline typename enable_if_c<boost::multiprecision::detail::is_second_backend<T, U>::value, bool>::type eval_gt(const T& a, const U& b) -{ - typename boost::multiprecision::detail::number_from_backend<U, T>::type t(a); - return eval_gt(t.backend(), b); -} - -} // namespace default_ops - -namespace detail{ - -template <class Num, class Val> -struct is_valid_mixed_compare : public mpl::false_ {}; - -template <class B, expression_template_option ET, class Val> -struct is_valid_mixed_compare<number<B, ET>, Val> : public is_convertible<Val, number<B, ET> > {}; - -template <class B, expression_template_option ET> -struct is_valid_mixed_compare<number<B, ET>, number<B, ET> > : public mpl::false_ {}; - -template <class B, expression_template_option ET, class tag, class Arg1, class Arg2, class Arg3, class Arg4> -struct is_valid_mixed_compare<number<B, ET>, expression<tag, Arg1, Arg2, Arg3, Arg4> > - : public mpl::bool_<is_convertible<expression<tag, Arg1, Arg2, Arg3, Arg4>, number<B, ET> >::value> {}; - -template <class tag, class Arg1, class Arg2, class Arg3, class Arg4, class B, expression_template_option ET> -struct is_valid_mixed_compare<expression<tag, Arg1, Arg2, Arg3, Arg4>, number<B, ET> > - : public mpl::bool_<is_convertible<expression<tag, Arg1, Arg2, Arg3, Arg4>, number<B, ET> >::value> {}; - -template <class Backend, expression_template_option ExpressionTemplates> -inline BOOST_CONSTEXPR typename boost::enable_if_c<number_category<Backend>::value != number_kind_floating_point, bool>::type is_unordered_value(const number<Backend, ExpressionTemplates>&) -{ - return false; -} -template <class Backend, expression_template_option ExpressionTemplates> -inline BOOST_CONSTEXPR typename boost::enable_if_c<number_category<Backend>::value == number_kind_floating_point, bool>::type is_unordered_value(const number<Backend, ExpressionTemplates>& a) -{ - using default_ops::eval_fpclassify; - return eval_fpclassify(a.backend()) == FP_NAN; -} - -template <class Arithmetic> -inline BOOST_CONSTEXPR typename boost::enable_if_c<number_category<Arithmetic>::value != number_kind_floating_point, bool>::type is_unordered_value(const Arithmetic&) -{ - return false; -} -template <class Arithmetic> -inline BOOST_CONSTEXPR typename boost::enable_if_c<number_category<Arithmetic>::value == number_kind_floating_point, bool>::type is_unordered_value(const Arithmetic& a) -{ - return (boost::math::isnan)(a); -} - -template <class T, class U> -inline BOOST_CONSTEXPR bool is_unordered_comparison(const T& a, const U& b) -{ - return is_unordered_value(a) || is_unordered_value(b); -} - -} - -template <class Backend, expression_template_option ExpressionTemplates, class Backend2, expression_template_option ExpressionTemplates2> -inline bool operator == (const number<Backend, ExpressionTemplates>& a, const number<Backend2, ExpressionTemplates2>& b) -{ - using default_ops::eval_eq; - if(detail::is_unordered_comparison(a, b)) return false; - return eval_eq(a.backend(), b.backend()); -} -template <class Backend, expression_template_option ExpressionTemplates, class Arithmetic> -inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type - operator == (const number<Backend, ExpressionTemplates>& a, const Arithmetic& b) -{ - using default_ops::eval_eq; - if(detail::is_unordered_comparison(a, b)) return false; - return eval_eq(a.backend(), number<Backend, ExpressionTemplates>::canonical_value(b)); -} -template <class Arithmetic, class Backend, expression_template_option ExpressionTemplates> -inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type - operator == (const Arithmetic& a, const number<Backend, ExpressionTemplates>& b) -{ - using default_ops::eval_eq; - if(detail::is_unordered_comparison(a, b)) return false; - return eval_eq(b.backend(), number<Backend, ExpressionTemplates>::canonical_value(a)); -} -template <class Arithmetic, class Tag, class A1, class A2, class A3, class A4> -inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type - operator == (const Arithmetic& a, const detail::expression<Tag, A1, A2, A3, A4>& b) -{ - typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type; - using default_ops::eval_eq; - result_type t(b); - if(detail::is_unordered_comparison(a, t)) return false; - return eval_eq(t.backend(), result_type::canonical_value(a)); -} -template <class Tag, class A1, class A2, class A3, class A4, class Arithmetic> -inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type - operator == (const detail::expression<Tag, A1, A2, A3, A4>& a, const Arithmetic& b) -{ - typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type; - using default_ops::eval_eq; - result_type t(a); - if(detail::is_unordered_comparison(t, b)) return false; - return eval_eq(t.backend(), result_type::canonical_value(b)); -} -template <class Tag, class A1, class A2, class A3, class A4, class Tagb, class A1b, class A2b, class A3b, class A4b> -inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type - operator == (const detail::expression<Tag, A1, A2, A3, A4>& a, const detail::expression<Tagb, A1b, A2b, A3b, A4b>& b) -{ - using default_ops::eval_eq; - typename detail::expression<Tag, A1, A2, A3, A4>::result_type t(a); - typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type t2(b); - if(detail::is_unordered_comparison(t, t2)) return false; - return eval_eq(t.backend(), t2.backend()); -} - -template <class Backend, expression_template_option ExpressionTemplates, class Backend2, expression_template_option ExpressionTemplates2> -inline bool operator != (const number<Backend, ExpressionTemplates>& a, const number<Backend2, ExpressionTemplates2>& b) -{ - using default_ops::eval_eq; - if(detail::is_unordered_comparison(a, b)) return true; - return !eval_eq(a.backend(), b.backend()); -} -template <class Backend, expression_template_option ExpressionTemplates, class Arithmetic> -inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type - operator != (const number<Backend, ExpressionTemplates>& a, const Arithmetic& b) -{ - using default_ops::eval_eq; - if(detail::is_unordered_comparison(a, b)) return true; - return !eval_eq(a.backend(), number<Backend, et_on>::canonical_value(b)); -} -template <class Arithmetic, class Backend, expression_template_option ExpressionTemplates> -inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type - operator != (const Arithmetic& a, const number<Backend, ExpressionTemplates>& b) -{ - using default_ops::eval_eq; - if(detail::is_unordered_comparison(a, b)) return true; - return !eval_eq(b.backend(), number<Backend, et_on>::canonical_value(a)); -} -template <class Arithmetic, class Tag, class A1, class A2, class A3, class A4> -inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type - operator != (const Arithmetic& a, const detail::expression<Tag, A1, A2, A3, A4>& b) -{ - typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type; - using default_ops::eval_eq; - result_type t(b); - if(detail::is_unordered_comparison(a, t)) return true; - return !eval_eq(t.backend(), result_type::canonical_value(a)); -} -template <class Tag, class A1, class A2, class A3, class A4, class Arithmetic> -inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type - operator != (const detail::expression<Tag, A1, A2, A3, A4>& a, const Arithmetic& b) -{ - typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type; - using default_ops::eval_eq; - result_type t(a); - if(detail::is_unordered_comparison(t, b)) return true; - return !eval_eq(t.backend(), result_type::canonical_value(b)); -} -template <class Tag, class A1, class A2, class A3, class A4, class Tagb, class A1b, class A2b, class A3b, class A4b> -inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type - operator != (const detail::expression<Tag, A1, A2, A3, A4>& a, const detail::expression<Tagb, A1b, A2b, A3b, A4b>& b) -{ - using default_ops::eval_eq; - typename detail::expression<Tag, A1, A2, A3, A4>::result_type t(a); - typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type t2(b); - if(detail::is_unordered_comparison(t, t2)) return true; - return !eval_eq(t.backend(), t2.backend()); -} - -template <class Backend, expression_template_option ExpressionTemplates, class Backend2, expression_template_option ExpressionTemplates2> -inline bool operator < (const number<Backend, ExpressionTemplates>& a, const number<Backend2, ExpressionTemplates2>& b) -{ - using default_ops::eval_lt; - if(detail::is_unordered_comparison(a, b)) return false; - return eval_lt(a.backend(), b.backend()); -} -template <class Backend, expression_template_option ExpressionTemplates, class Arithmetic> -inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type - operator < (const number<Backend, ExpressionTemplates>& a, const Arithmetic& b) -{ - using default_ops::eval_lt; - if(detail::is_unordered_comparison(a, b)) return false; - return eval_lt(a.backend(), number<Backend, ExpressionTemplates>::canonical_value(b)); -} -template <class Arithmetic, class Backend, expression_template_option ExpressionTemplates> -inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type - operator < (const Arithmetic& a, const number<Backend, ExpressionTemplates>& b) -{ - using default_ops::eval_gt; - if(detail::is_unordered_comparison(a, b)) return false; - return eval_gt(b.backend(), number<Backend, ExpressionTemplates>::canonical_value(a)); -} -template <class Arithmetic, class Tag, class A1, class A2, class A3, class A4> -inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type - operator < (const Arithmetic& a, const detail::expression<Tag, A1, A2, A3, A4>& b) -{ - typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type; - using default_ops::eval_gt; - result_type t(b); - if(detail::is_unordered_comparison(a, t)) return false; - return eval_gt(t.backend(), result_type::canonical_value(a)); -} -template <class Tag, class A1, class A2, class A3, class A4, class Arithmetic> -inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type - operator < (const detail::expression<Tag, A1, A2, A3, A4>& a, const Arithmetic& b) -{ - typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type; - using default_ops::eval_lt; - result_type t(a); - if(detail::is_unordered_comparison(t, b)) return false; - return eval_lt(t.backend(), result_type::canonical_value(b)); -} -template <class Tag, class A1, class A2, class A3, class A4, class Tagb, class A1b, class A2b, class A3b, class A4b> -inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type - operator < (const detail::expression<Tag, A1, A2, A3, A4>& a, const detail::expression<Tagb, A1b, A2b, A3b, A4b>& b) -{ - using default_ops::eval_lt; - typename detail::expression<Tag, A1, A2, A3, A4>::result_type t(a); - typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type t2(b); - if(detail::is_unordered_comparison(t, t2)) return false; - return eval_lt(t.backend(), t2.backend()); -} - -template <class Backend, expression_template_option ExpressionTemplates, class Backend2, expression_template_option ExpressionTemplates2> -inline bool operator > (const number<Backend, ExpressionTemplates>& a, const number<Backend2, ExpressionTemplates2>& b) -{ - using default_ops::eval_gt; - if(detail::is_unordered_comparison(a, b)) return false; - return eval_gt(a.backend(), b.backend()); -} -template <class Backend, expression_template_option ExpressionTemplates, class Arithmetic> -inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type - operator > (const number<Backend, ExpressionTemplates>& a, const Arithmetic& b) -{ - using default_ops::eval_gt; - if(detail::is_unordered_comparison(a, b)) return false; - return eval_gt(a.backend(), number<Backend, ExpressionTemplates>::canonical_value(b)); -} -template <class Arithmetic, class Backend, expression_template_option ExpressionTemplates> -inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type - operator > (const Arithmetic& a, const number<Backend, ExpressionTemplates>& b) -{ - using default_ops::eval_lt; - if(detail::is_unordered_comparison(a, b)) return false; - return eval_lt(b.backend(), number<Backend, ExpressionTemplates>::canonical_value(a)); -} -template <class Arithmetic, class Tag, class A1, class A2, class A3, class A4> -inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type - operator > (const Arithmetic& a, const detail::expression<Tag, A1, A2, A3, A4>& b) -{ - typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type; - using default_ops::eval_lt; - result_type t(b); - if(detail::is_unordered_comparison(a, t)) return false; - return a > t; -} -template <class Tag, class A1, class A2, class A3, class A4, class Arithmetic> -inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type - operator > (const detail::expression<Tag, A1, A2, A3, A4>& a, const Arithmetic& b) -{ - typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type; - using default_ops::eval_gt; - result_type t(a); - if(detail::is_unordered_comparison(t, b)) return false; - return t > b; -} -template <class Tag, class A1, class A2, class A3, class A4, class Tagb, class A1b, class A2b, class A3b, class A4b> -inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type - operator > (const detail::expression<Tag, A1, A2, A3, A4>& a, const detail::expression<Tagb, A1b, A2b, A3b, A4b>& b) -{ - using default_ops::eval_gt; - typename detail::expression<Tag, A1, A2, A3, A4>::result_type t(a); - typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type t2(b); - if(detail::is_unordered_comparison(t, t2)) return false; - return t > t2; -} - -template <class Backend, expression_template_option ExpressionTemplates, class Backend2, expression_template_option ExpressionTemplates2> -inline bool operator <= (const number<Backend, ExpressionTemplates>& a, const number<Backend2, ExpressionTemplates2>& b) -{ - using default_ops::eval_gt; - if(detail::is_unordered_comparison(a, b)) return false; - return !eval_gt(a.backend(), b.backend()); -} -template <class Backend, expression_template_option ExpressionTemplates, class Arithmetic> -inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type - operator <= (const number<Backend, ExpressionTemplates>& a, const Arithmetic& b) -{ - using default_ops::eval_gt; - if(detail::is_unordered_comparison(a, b)) return false; - return !eval_gt(a.backend(), number<Backend, ExpressionTemplates>::canonical_value(b)); -} -template <class Arithmetic, class Backend, expression_template_option ExpressionTemplates> -inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type - operator <= (const Arithmetic& a, const number<Backend, ExpressionTemplates>& b) -{ - using default_ops::eval_lt; - if(detail::is_unordered_comparison(a, b)) return false; - return !eval_lt(b.backend(), number<Backend, ExpressionTemplates>::canonical_value(a)); -} -template <class Arithmetic, class Tag, class A1, class A2, class A3, class A4> -inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type - operator <= (const Arithmetic& a, const detail::expression<Tag, A1, A2, A3, A4>& b) -{ - typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type; - using default_ops::eval_lt; - if(detail::is_unordered_value(a) || detail::is_unordered_value(b)) - return false; - result_type t(b); - if(detail::is_unordered_comparison(a, t)) return false; - return !eval_lt(t.backend(), result_type::canonical_value(a)); -} -template <class Tag, class A1, class A2, class A3, class A4, class Arithmetic> -inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type - operator <= (const detail::expression<Tag, A1, A2, A3, A4>& a, const Arithmetic& b) -{ - typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type; - using default_ops::eval_gt; - result_type t(a); - if(detail::is_unordered_comparison(t, b)) return false; - return !eval_gt(t.backend(), result_type::canonical_value(b)); -} -template <class Tag, class A1, class A2, class A3, class A4, class Tagb, class A1b, class A2b, class A3b, class A4b> -inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type - operator <= (const detail::expression<Tag, A1, A2, A3, A4>& a, const detail::expression<Tagb, A1b, A2b, A3b, A4b>& b) -{ - using default_ops::eval_gt; - typename detail::expression<Tag, A1, A2, A3, A4>::result_type t(a); - typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type t2(b); - if(detail::is_unordered_comparison(t, t2)) return false; - return !eval_gt(t.backend(), t2.backend()); -} - -template <class Backend, expression_template_option ExpressionTemplates, class Backend2, expression_template_option ExpressionTemplates2> -inline bool operator >= (const number<Backend, ExpressionTemplates>& a, const number<Backend2, ExpressionTemplates2>& b) -{ - using default_ops::eval_lt; - if(detail::is_unordered_comparison(a, b)) return false; - return !eval_lt(a.backend(), b.backend()); -} -template <class Backend, expression_template_option ExpressionTemplates, class Arithmetic> -inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type - operator >= (const number<Backend, ExpressionTemplates>& a, const Arithmetic& b) -{ - using default_ops::eval_lt; - if(detail::is_unordered_comparison(a, b)) return false; - return !eval_lt(a.backend(), number<Backend, ExpressionTemplates>::canonical_value(b)); -} -template <class Arithmetic, class Backend, expression_template_option ExpressionTemplates> -inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type - operator >= (const Arithmetic& a, const number<Backend, ExpressionTemplates>& b) -{ - using default_ops::eval_gt; - if(detail::is_unordered_comparison(a, b)) return false; - return !eval_gt(b.backend(), number<Backend, ExpressionTemplates>::canonical_value(a)); -} -template <class Arithmetic, class Tag, class A1, class A2, class A3, class A4> -inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type - operator >= (const Arithmetic& a, const detail::expression<Tag, A1, A2, A3, A4>& b) -{ - typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type; - using default_ops::eval_gt; - result_type t(b); - if(detail::is_unordered_comparison(a, t)) return false; - return !eval_gt(t.backend(), result_type::canonical_value(a)); -} -template <class Tag, class A1, class A2, class A3, class A4, class Arithmetic> -inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type - operator >= (const detail::expression<Tag, A1, A2, A3, A4>& a, const Arithmetic& b) -{ - typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type; - using default_ops::eval_lt; - result_type t(a); - if(detail::is_unordered_comparison(t, b)) return false; - return !eval_lt(t.backend(), result_type::canonical_value(b)); -} -template <class Tag, class A1, class A2, class A3, class A4, class Tagb, class A1b, class A2b, class A3b, class A4b> -inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type - operator >= (const detail::expression<Tag, A1, A2, A3, A4>& a, const detail::expression<Tagb, A1b, A2b, A3b, A4b>& b) -{ - using default_ops::eval_lt; - typename detail::expression<Tag, A1, A2, A3, A4>::result_type t(a); - typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type t2(b); - if(detail::is_unordered_comparison(t, t2)) return false; - return !eval_lt(t.backend(), t2.backend()); -} - - -}} // namespaces - -#endif // BOOST_MP_COMPARE_HPP diff --git a/libevmasm/RuleList.h b/libevmasm/RuleList.h index 874a8929..01e9b984 100644 --- a/libevmasm/RuleList.h +++ b/libevmasm/RuleList.h @@ -44,7 +44,7 @@ template <class S> S modWorkaround(S const& _a, S const& _b) return (S)(bigint(_a) % bigint(_b)); } -// This part of simplificationRuleList below was split out to prevent +// simplificationRuleList below was split up into parts to prevent // stack overflows in the JavaScript optimizer for emscripten builds // that affected certain browser versions. template <class Pattern> @@ -52,8 +52,8 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart1( Pattern A, Pattern B, Pattern C, - Pattern X, - Pattern Y + Pattern, + Pattern ) { return std::vector<SimplificationRule<Pattern>> { @@ -96,8 +96,20 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart1( if (A.d() > 255) return u256(0); return B.d() >> unsigned(A.d()); - }, false}, + }, false} + }; +} +template <class Pattern> +std::vector<SimplificationRule<Pattern>> simplificationRuleListPart2( + Pattern, + Pattern, + Pattern, + Pattern X, + Pattern +) +{ + return std::vector<SimplificationRule<Pattern>> { // invariants involving known constants {{Instruction::ADD, {X, 0}}, [=]{ return X; }, false}, {{Instruction::ADD, {0, X}}, [=]{ return X; }, false}, @@ -128,7 +140,19 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart1( {{Instruction::MOD, {0, X}}, [=]{ return u256(0); }, true}, {{Instruction::EQ, {X, 0}}, [=]() -> Pattern { return {Instruction::ISZERO, {X}}; }, false }, {{Instruction::EQ, {0, X}}, [=]() -> Pattern { return {Instruction::ISZERO, {X}}; }, false }, + }; +} +template <class Pattern> +std::vector<SimplificationRule<Pattern>> simplificationRuleListPart3( + Pattern, + Pattern, + Pattern, + Pattern X, + Pattern +) +{ + return std::vector<SimplificationRule<Pattern>> { // operations involving an expression and itself {{Instruction::AND, {X, X}}, [=]{ return X; }, true}, {{Instruction::OR, {X, X}}, [=]{ return X; }, true}, @@ -139,8 +163,20 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart1( {{Instruction::SLT, {X, X}}, [=]{ return u256(0); }, true}, {{Instruction::GT, {X, X}}, [=]{ return u256(0); }, true}, {{Instruction::SGT, {X, X}}, [=]{ return u256(0); }, true}, - {{Instruction::MOD, {X, X}}, [=]{ return u256(0); }, true}, + {{Instruction::MOD, {X, X}}, [=]{ return u256(0); }, true} + }; +} +template <class Pattern> +std::vector<SimplificationRule<Pattern>> simplificationRuleListPart4( + Pattern, + Pattern, + Pattern, + Pattern X, + Pattern Y +) +{ + return std::vector<SimplificationRule<Pattern>> { // logical instruction combinations {{Instruction::NOT, {{Instruction::NOT, {X}}}}, [=]{ return X; }, false}, {{Instruction::XOR, {X, {Instruction::XOR, {X, Y}}}}, [=]{ return Y; }, true}, @@ -163,16 +199,13 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart1( } -// This part of simplificationRuleList below was split out to prevent -// stack overflows in the JavaScript optimizer for emscripten builds -// that affected certain browser versions. template <class Pattern> -std::vector<SimplificationRule<Pattern>> simplificationRuleListPart2( - Pattern A, - Pattern B, +std::vector<SimplificationRule<Pattern>> simplificationRuleListPart5( + Pattern, + Pattern, Pattern, Pattern X, - Pattern Y + Pattern ) { std::vector<SimplificationRule<Pattern>> rules; @@ -207,7 +240,19 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart2( false }); } + return rules; +} +template <class Pattern> +std::vector<SimplificationRule<Pattern>> simplificationRuleListPart6( + Pattern, + Pattern, + Pattern, + Pattern X, + Pattern Y +) +{ + std::vector<SimplificationRule<Pattern>> rules; // Double negation of opcodes with boolean result for (auto const& op: std::vector<Instruction>{ Instruction::EQ, @@ -234,6 +279,19 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart2( false }); + return rules; +} + +template <class Pattern> +std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7( + Pattern A, + Pattern B, + Pattern, + Pattern X, + Pattern Y +) +{ + std::vector<SimplificationRule<Pattern>> rules; // Associative operations for (auto const& opFun: std::vector<std::pair<Instruction,std::function<u256(u256 const&,u256 const&)>>>{ {Instruction::ADD, std::plus<u256>()}, @@ -274,6 +332,20 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart2( } } + return rules; +} + +template <class Pattern> +std::vector<SimplificationRule<Pattern>> simplificationRuleListPart8( + Pattern A, + Pattern, + Pattern, + Pattern X, + Pattern Y +) +{ + std::vector<SimplificationRule<Pattern>> rules; + // move constants across subtractions rules += std::vector<SimplificationRule<Pattern>>{ { @@ -322,6 +394,12 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleList( std::vector<SimplificationRule<Pattern>> rules; rules += simplificationRuleListPart1(A, B, C, X, Y); rules += simplificationRuleListPart2(A, B, C, X, Y); + rules += simplificationRuleListPart3(A, B, C, X, Y); + rules += simplificationRuleListPart4(A, B, C, X, Y); + rules += simplificationRuleListPart5(A, B, C, X, Y); + rules += simplificationRuleListPart6(A, B, C, X, Y); + rules += simplificationRuleListPart7(A, B, C, X, Y); + rules += simplificationRuleListPart8(A, B, C, X, Y); return rules; } diff --git a/liblangutil/Token.h b/liblangutil/Token.h index f832fdf7..b3a1acb1 100644 --- a/liblangutil/Token.h +++ b/liblangutil/Token.h @@ -180,6 +180,7 @@ namespace langutil K(CallData, "calldata", 0) \ K(Struct, "struct", 0) \ K(Throw, "throw", 0) \ + K(Type, "type", 0) \ K(Using, "using", 0) \ K(Var, "var", 0) \ K(View, "view", 0) \ @@ -256,7 +257,6 @@ namespace langutil K(Supports, "supports", 0) \ K(Switch, "switch", 0) \ K(Try, "try", 0) \ - K(Type, "type", 0) \ K(Typedef, "typedef", 0) \ K(TypeOf, "typeof", 0) \ K(Unchecked, "unchecked", 0) \ diff --git a/libsolc/CMakeLists.txt b/libsolc/CMakeLists.txt index dfdf8162..7fb4aa05 100644 --- a/libsolc/CMakeLists.txt +++ b/libsolc/CMakeLists.txt @@ -1,4 +1,7 @@ if (EMSCRIPTEN) + # Specify which functions to export in soljson.js. + # Note that additional Emscripten-generated methods needed by solc-js are + # defined to be exported in cmake/EthCompilerSettings.cmake. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s EXPORTED_FUNCTIONS='[\"_solidity_license\",\"_solidity_version\",\"_solidity_compile\"]' -s RESERVED_FUNCTION_POINTERS=20") add_executable(soljson libsolc.cpp libsolc.h) target_link_libraries(soljson PRIVATE solidity) diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp index cd5fe07d..2276d783 100644 --- a/libsolidity/analysis/GlobalContext.cpp +++ b/libsolidity/analysis/GlobalContext.cpp @@ -61,7 +61,14 @@ m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{ make_shared<MagicVariableDeclaration>("sha256", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::SHA256, false, StateMutability::Pure)), make_shared<MagicVariableDeclaration>("sha3", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::KECCAK256, false, StateMutability::Pure)), make_shared<MagicVariableDeclaration>("suicide", make_shared<FunctionType>(strings{"address payable"}, strings{}, FunctionType::Kind::Selfdestruct)), - make_shared<MagicVariableDeclaration>("tx", make_shared<MagicType>(MagicType::Kind::Transaction)) + make_shared<MagicVariableDeclaration>("tx", make_shared<MagicType>(MagicType::Kind::Transaction)), + make_shared<MagicVariableDeclaration>("type", make_shared<FunctionType>( + strings{"address"} /* accepts any contract type, handled by the type checker */, + strings{} /* returns a MagicType, handled by the type checker */, + FunctionType::Kind::MetaType, + false, + StateMutability::Pure + )), }) { } diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index aaaa4f9f..11ed6a4f 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -32,6 +32,56 @@ using namespace dev; using namespace langutil; using namespace dev::solidity; +/** + * Helper class that determines whether a contract's constructor uses inline assembly. + */ +class dev::solidity::ConstructorUsesAssembly +{ +public: + /// @returns true if and only if the contract's or any of its bases' constructors + /// use inline assembly. + bool check(ContractDefinition const& _contract) + { + for (auto const* base: _contract.annotation().linearizedBaseContracts) + if (checkInternal(*base)) + return true; + return false; + } + + +private: + class Checker: public ASTConstVisitor + { + public: + Checker(FunctionDefinition const& _f) { _f.accept(*this); } + bool visit(InlineAssembly const&) override { assemblySeen = true; return false; } + bool assemblySeen = false; + }; + + bool checkInternal(ContractDefinition const& _contract) + { + if (!m_usesAssembly.count(&_contract)) + { + bool usesAssembly = false; + if (_contract.constructor()) + usesAssembly = Checker{*_contract.constructor()}.assemblySeen; + m_usesAssembly[&_contract] = usesAssembly; + } + return m_usesAssembly[&_contract]; + } + + map<ContractDefinition const*, bool> m_usesAssembly; +}; + +StaticAnalyzer::StaticAnalyzer(ErrorReporter& _errorReporter): + m_errorReporter(_errorReporter) +{ +} + +StaticAnalyzer::~StaticAnalyzer() +{ +} + bool StaticAnalyzer::analyze(SourceUnit const& _sourceUnit) { _sourceUnit.accept(*this); @@ -152,6 +202,18 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess) _memberAccess.location(), "\"block.blockhash()\" has been deprecated in favor of \"blockhash()\"" ); + else if (type->kind() == MagicType::Kind::MetaType && _memberAccess.memberName() == "runtimeCode") + { + if (!m_constructorUsesAssembly) + m_constructorUsesAssembly = make_unique<ConstructorUsesAssembly>(); + ContractType const& contract = dynamic_cast<ContractType const&>(*type->typeArgument()); + if (m_constructorUsesAssembly->check(contract.contractDefinition())) + m_errorReporter.warning( + _memberAccess.location(), + "The constructor of the contract (or its base) uses inline assembly. " + "Because of that, it might be that the deployed bytecode is different from type(...).runtimeCode." + ); + } } if (_memberAccess.memberName() == "callcode") diff --git a/libsolidity/analysis/StaticAnalyzer.h b/libsolidity/analysis/StaticAnalyzer.h index ff33fa3a..3daf83b3 100644 --- a/libsolidity/analysis/StaticAnalyzer.h +++ b/libsolidity/analysis/StaticAnalyzer.h @@ -38,6 +38,8 @@ namespace dev namespace solidity { +class ConstructorUsesAssembly; + /** * The module that performs static analysis on the AST. @@ -49,7 +51,8 @@ class StaticAnalyzer: private ASTConstVisitor { public: /// @param _errorReporter provides the error logging functionality. - explicit StaticAnalyzer(langutil::ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {} + explicit StaticAnalyzer(langutil::ErrorReporter& _errorReporter); + ~StaticAnalyzer(); /// Performs static analysis on the given source unit and all of its sub-nodes. /// @returns true iff all checks passed. Note even if all checks passed, errors() can still contain warnings @@ -85,6 +88,10 @@ private: /// when traversing. std::map<std::pair<size_t, VariableDeclaration const*>, int> m_localVarUseCount; + /// Cache that holds information about whether a contract's constructor + /// uses inline assembly. + std::unique_ptr<ConstructorUsesAssembly> m_constructorUsesAssembly; + FunctionDefinition const* m_currentFunction = nullptr; /// Flag that indicates a constructor. diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index 066b5004..7b8aa0f2 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -262,7 +262,7 @@ bool SyntaxChecker::visit(PlaceholderStatement const&) bool SyntaxChecker::visit(ContractDefinition const& _contract) { - m_isInterface = _contract.contractKind() == ContractDefinition::ContractKind::Interface; + m_isInterface = _contract.isInterface(); ASTString const& contractName = _contract.name(); for (FunctionDefinition const* function: _contract.definedFunctions()) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 507a2c94..f1a5f7ce 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -199,12 +199,44 @@ TypePointers TypeChecker::typeCheckABIDecodeAndRetrieveReturnType(FunctionCall c return components; } +TypePointers TypeChecker::typeCheckMetaTypeFunctionAndRetrieveReturnType(FunctionCall const& _functionCall) +{ + vector<ASTPointer<Expression const>> arguments = _functionCall.arguments(); + if (arguments.size() != 1) + { + m_errorReporter.typeError( + _functionCall.location(), + "This function takes one argument, but " + + toString(arguments.size()) + + " were provided." + ); + return {}; + } + TypePointer firstArgType = type(*arguments.front()); + if ( + firstArgType->category() != Type::Category::TypeType || + dynamic_cast<TypeType const&>(*firstArgType).actualType()->category() != TypeType::Category::Contract + ) + { + m_errorReporter.typeError( + arguments.front()->location(), + "Invalid type for argument in function call. " + "Contract type required, but " + + type(*arguments.front())->toString(true) + + " provided." + ); + return {}; + } + + return {MagicType::metaType(dynamic_cast<TypeType const&>(*firstArgType).actualType())}; +} + void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance) { auto base = dynamic_cast<ContractDefinition const*>(&dereference(_inheritance.name())); solAssert(base, "Base contract not available."); - if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface) + if (m_scope->isInterface()) m_errorReporter.typeError(_inheritance.location(), "Interfaces cannot inherit."); if (base->isLibrary()) @@ -212,7 +244,7 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance) auto const& arguments = _inheritance.arguments(); TypePointers parameterTypes; - if (base->contractKind() != ContractDefinition::ContractKind::Interface) + if (!base->isInterface()) // Interfaces do not have constructors, so there are zero parameters. parameterTypes = ContractType(*base).newExpressionType()->parameterTypes(); @@ -299,33 +331,40 @@ bool TypeChecker::visit(FunctionDefinition const& _function) if (!_function.isConstructor() && !_function.isFallback() && !_function.isPartOfExternalInterface()) m_errorReporter.typeError(_function.location(), "Internal functions cannot be payable."); } - for (ASTPointer<VariableDeclaration> const& var: _function.parameters() + _function.returnParameters()) - { - if (type(*var)->category() == Type::Category::Mapping) + auto checkArgumentAndReturnParameter = [&](VariableDeclaration const& var) { + if (type(var)->category() == Type::Category::Mapping) { - if (!type(*var)->dataStoredIn(DataLocation::Storage)) - m_errorReporter.typeError(var->location(), "Mapping types can only have a data location of \"storage\"." ); + if (!type(var)->dataStoredIn(DataLocation::Storage)) + m_errorReporter.typeError(var.location(), "Mapping types can only have a data location of \"storage\"." ); else if (!isLibraryFunction && _function.isPublic()) - m_errorReporter.typeError(var->location(), "Mapping types for parameters or return variables can only be used in internal or library functions."); + m_errorReporter.typeError(var.location(), "Mapping types for parameters or return variables can only be used in internal or library functions."); } else { - if (!type(*var)->canLiveOutsideStorage() && _function.isPublic()) - m_errorReporter.typeError(var->location(), "Type is required to live outside storage."); - if (_function.isPublic() && !(type(*var)->interfaceType(isLibraryFunction))) - m_errorReporter.fatalTypeError(var->location(), "Internal or recursive type is not allowed for public or external functions."); + if (!type(var)->canLiveOutsideStorage() && _function.isPublic()) + m_errorReporter.typeError(var.location(), "Type is required to live outside storage."); + if (_function.isPublic() && !(type(var)->interfaceType(isLibraryFunction))) + m_errorReporter.fatalTypeError(var.location(), "Internal or recursive type is not allowed for public or external functions."); } if ( _function.isPublic() && !_function.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2) && - !typeSupportedByOldABIEncoder(*type(*var)) + !typeSupportedByOldABIEncoder(*type(var)) ) m_errorReporter.typeError( - var->location(), + var.location(), "This type is only supported in the new experimental ABI encoder. " "Use \"pragma experimental ABIEncoderV2;\" to enable the feature." ); - + }; + for (ASTPointer<VariableDeclaration> const& var: _function.parameters()) + { + checkArgumentAndReturnParameter(*var); + var->accept(*this); + } + for (ASTPointer<VariableDeclaration> const& var: _function.returnParameters()) + { + checkArgumentAndReturnParameter(*var); var->accept(*this); } set<Declaration const*> modifiers; @@ -346,7 +385,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function) else modifiers.insert(decl); } - if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface) + if (m_scope->isInterface()) { if (_function.isImplemented()) m_errorReporter.typeError(_function.location(), "Functions in interfaces cannot have an implementation."); @@ -375,7 +414,7 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) // * a function's input/output parameters, // * or inside of a struct definition. if ( - m_scope->contractKind() == ContractDefinition::ContractKind::Interface + m_scope->isInterface() && !_variable.isCallableParameter() && !m_insideStruct ) @@ -1482,7 +1521,16 @@ void TypeChecker::typeCheckABIEncodeFunctions( if (argType->category() == Type::Category::RationalNumber) { - if (!argType->mobileType()) + auto const& rationalType = dynamic_cast<RationalNumberType const&>(*argType); + if (rationalType.isFractional()) + { + m_errorReporter.typeError( + arguments[i]->location(), + "Fractional numbers cannot yet be encoded." + ); + continue; + } + else if (!argType->mobileType()) { m_errorReporter.typeError( arguments[i]->location(), @@ -1822,6 +1870,9 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) returnTypes = functionType->returnParameterTypes(); break; } + case FunctionType::Kind::MetaType: + returnTypes = typeCheckMetaTypeFunctionAndRetrieveReturnType(_functionCall); + break; default: { typeCheckFunctionCall(_functionCall, functionType); @@ -1862,7 +1913,7 @@ void TypeChecker::endVisit(NewExpression const& _newExpression) if (!contract) m_errorReporter.fatalTypeError(_newExpression.location(), "Identifier is not a contract."); - if (contract->contractKind() == ContractDefinition::ContractKind::Interface) + if (contract->isInterface()) m_errorReporter.fatalTypeError(_newExpression.location(), "Cannot instantiate an interface."); if (!contract->annotation().unimplementedFunctions.empty()) { @@ -2062,8 +2113,24 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) if (tt->actualType()->category() == Type::Category::Enum) annotation.isPure = true; if (auto magicType = dynamic_cast<MagicType const*>(exprType.get())) + { if (magicType->kind() == MagicType::Kind::ABI) annotation.isPure = true; + else if (magicType->kind() == MagicType::Kind::MetaType && ( + memberName == "creationCode" || memberName == "runtimeCode" + )) + { + annotation.isPure = true; + m_scope->annotation().contractDependencies.insert( + &dynamic_cast<ContractType const&>(*magicType->typeArgument()).contractDefinition() + ); + if (contractDependenciesAreCyclic(*m_scope)) + m_errorReporter.typeError( + _memberAccess.location(), + "Circular reference for contract code access." + ); + } + } return false; } diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index b60c571a..d5f3645c 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -81,6 +81,8 @@ private: bool _abiEncoderV2 ); + TypePointers typeCheckMetaTypeFunctionAndRetrieveReturnType(FunctionCall const& _functionCall); + /// Performs type checks and determines result types for type conversion FunctionCall nodes. TypePointer typeCheckTypeConversionAndRetrieveReturnType( FunctionCall const& _functionCall diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index eb019481..7df7ac17 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -338,7 +338,9 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess) {MagicType::Kind::ABI, "encodeWithSignature"}, {MagicType::Kind::Block, "blockhash"}, {MagicType::Kind::Message, "data"}, - {MagicType::Kind::Message, "sig"} + {MagicType::Kind::Message, "sig"}, + {MagicType::Kind::MetaType, "creationCode"}, + {MagicType::Kind::MetaType, "runtimeCode"} }; set<MagicMember> static const payableMembers{ {MagicType::Kind::Message, "value"} diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index f379d758..4cf8b1e8 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -138,6 +138,11 @@ bool ContractDefinition::constructorIsPublic() const return !f || f->isPublic(); } +bool ContractDefinition::canBeDeployed() const +{ + return constructorIsPublic() && annotation().unimplementedFunctions.empty(); +} + FunctionDefinition const* ContractDefinition::fallbackFunction() const { for (ContractDefinition const* contract: annotation().linearizedBaseContracts) diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 9ac065ea..f6fdc441 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -394,6 +394,7 @@ public: std::vector<FunctionDefinition const*> definedFunctions() const { return filteredNodes<FunctionDefinition>(m_subNodes); } std::vector<EventDefinition const*> events() const { return filteredNodes<EventDefinition>(m_subNodes); } std::vector<EventDefinition const*> const& interfaceEvents() const; + bool isInterface() const { return m_contractKind == ContractKind::Interface; } bool isLibrary() const { return m_contractKind == ContractKind::Library; } /// @returns a map of canonical function signatures to FunctionDefinitions @@ -408,6 +409,10 @@ public: FunctionDefinition const* constructor() const; /// @returns true iff the constructor of this contract is public (or non-existing). bool constructorIsPublic() const; + /// @returns true iff the contract can be deployed, i.e. is not abstract and has a + /// public constructor. + /// Should only be called after the type checker has run. + bool canBeDeployed() const; /// Returns the fallback function or nullptr if no fallback function was specified. FunctionDefinition const* fallbackFunction() const; diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index cc978b4a..3a8c9878 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -2525,7 +2525,7 @@ FunctionTypePointer FunctionType::newExpressionType(ContractDefinition const& _c strings parameterNames; StateMutability stateMutability = StateMutability::NonPayable; - solAssert(_contract.contractKind() != ContractDefinition::ContractKind::Interface, ""); + solAssert(!_contract.isInterface(), ""); if (constructor) { @@ -2626,6 +2626,7 @@ string FunctionType::richIdentifier() const case Kind::ABIEncodeWithSelector: id += "abiencodewithselector"; break; case Kind::ABIEncodeWithSignature: id += "abiencodewithsignature"; break; case Kind::ABIDecode: id += "abidecode"; break; + case Kind::MetaType: id += "metatype"; break; } id += "_" + stateMutabilityToString(m_stateMutability); id += identifierList(m_parameterTypes) + "returns" + identifierList(m_returnParameterTypes); @@ -3037,7 +3038,8 @@ bool FunctionType::isPure() const m_kind == Kind::ABIEncodePacked || m_kind == Kind::ABIEncodeWithSelector || m_kind == Kind::ABIEncodeWithSignature || - m_kind == Kind::ABIDecode; + m_kind == Kind::ABIDecode || + m_kind == Kind::MetaType; } TypePointers FunctionType::parseElementaryTypeVector(strings const& _types) @@ -3305,6 +3307,14 @@ string ModuleType::toString(bool) const return string("module \"") + m_sourceUnit.annotation().path + string("\""); } +shared_ptr<MagicType> MagicType::metaType(TypePointer _type) +{ + solAssert(_type && _type->category() == Type::Category::Contract, "Only contracts supported for now."); + auto t = make_shared<MagicType>(Kind::MetaType); + t->m_typeArgument = std::move(_type); + return t; +} + string MagicType::richIdentifier() const { switch (m_kind) @@ -3317,6 +3327,9 @@ string MagicType::richIdentifier() const return "t_magic_transaction"; case Kind::ABI: return "t_magic_abi"; + case Kind::MetaType: + solAssert(m_typeArgument, ""); + return "t_magic_meta_type_" + m_typeArgument->richIdentifier(); } return ""; } @@ -3403,12 +3416,27 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const StateMutability::Pure )} }); - default: - solAssert(false, "Unknown kind of magic."); + case Kind::MetaType: + { + solAssert( + m_typeArgument && m_typeArgument->category() == Type::Category::Contract, + "Only contracts supported for now" + ); + ContractDefinition const& contract = dynamic_cast<ContractType const&>(*m_typeArgument).contractDefinition(); + if (contract.canBeDeployed()) + return MemberList::MemberMap({ + {"creationCode", std::make_shared<ArrayType>(DataLocation::Memory)}, + {"runtimeCode", std::make_shared<ArrayType>(DataLocation::Memory)} + }); + else + return {}; + } } + solAssert(false, "Unknown kind of magic."); + return {}; } -string MagicType::toString(bool) const +string MagicType::toString(bool _short) const { switch (m_kind) { @@ -3420,7 +3448,17 @@ string MagicType::toString(bool) const return "tx"; case Kind::ABI: return "abi"; - default: - solAssert(false, "Unknown kind of magic."); + case Kind::MetaType: + solAssert(m_typeArgument, ""); + return "type(" + m_typeArgument->toString(_short) + ")"; } + solAssert(false, "Unknown kind of magic."); + return {}; +} + +TypePointer MagicType::typeArgument() const +{ + solAssert(m_kind == Kind::MetaType, ""); + solAssert(m_typeArgument, ""); + return m_typeArgument; } diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index bee00661..53109de1 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -989,6 +989,7 @@ public: ABIEncodeWithSignature, ABIDecode, GasLeft, ///< gasleft() + MetaType ///< type(...) }; Category category() const override { return Category::Function; } @@ -1299,16 +1300,23 @@ private: }; /** - * Special type for magic variables (block, msg, tx), similar to a struct but without any reference - * (it always references a global singleton by name). + * Special type for magic variables (block, msg, tx, type(...)), similar to a struct but without any reference. */ class MagicType: public Type { public: - enum class Kind { Block, Message, Transaction, ABI }; + enum class Kind { + Block, ///< "block" + Message, ///< "msg" + Transaction, ///< "tx" + ABI, ///< "abi" + MetaType ///< "type(...)" + }; Category category() const override { return Category::Magic; } explicit MagicType(Kind _kind): m_kind(_kind) {} + /// Factory function for meta type + static std::shared_ptr<MagicType> metaType(TypePointer _type); TypeResult binaryOperatorResult(Token, TypePointer const&) const override { @@ -1327,8 +1335,13 @@ public: Kind kind() const { return m_kind; } + TypePointer typeArgument() const; + private: Kind m_kind; + /// Contract type used for contract metadata magic. + TypePointer m_typeArgument; + }; /** diff --git a/libsolidity/codegen/Compiler.cpp b/libsolidity/codegen/Compiler.cpp index a22e6e9d..72efed33 100644 --- a/libsolidity/codegen/Compiler.cpp +++ b/libsolidity/codegen/Compiler.cpp @@ -31,22 +31,28 @@ using namespace dev::solidity; void Compiler::compileContract( ContractDefinition const& _contract, - std::map<const ContractDefinition*, eth::Assembly const*> const& _contracts, + std::map<ContractDefinition const*, shared_ptr<Compiler const>> const& _otherCompilers, bytes const& _metadata ) { ContractCompiler runtimeCompiler(nullptr, m_runtimeContext, m_optimize, m_optimizeRuns); - runtimeCompiler.compileContract(_contract, _contracts); + runtimeCompiler.compileContract(_contract, _otherCompilers); m_runtimeContext.appendAuxiliaryData(_metadata); // This might modify m_runtimeContext because it can access runtime functions at // creation time. ContractCompiler creationCompiler(&runtimeCompiler, m_context, m_optimize, 1); - m_runtimeSub = creationCompiler.compileConstructor(_contract, _contracts); + m_runtimeSub = creationCompiler.compileConstructor(_contract, _otherCompilers); m_context.optimise(m_optimize, m_optimizeRuns); } +std::shared_ptr<eth::Assembly> Compiler::runtimeAssemblyPtr() const +{ + solAssert(m_context.runtimeContext(), ""); + return m_context.runtimeContext()->assemblyPtr(); +} + eth::AssemblyItem Compiler::functionEntryLabel(FunctionDefinition const& _function) const { return m_runtimeContext.functionEntryLabelIfExists(_function); diff --git a/libsolidity/codegen/Compiler.h b/libsolidity/codegen/Compiler.h index 784d7f8c..c21de96d 100644 --- a/libsolidity/codegen/Compiler.h +++ b/libsolidity/codegen/Compiler.h @@ -45,11 +45,15 @@ public: /// @arg _metadata contains the to be injected metadata CBOR void compileContract( ContractDefinition const& _contract, - std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts, + std::map<ContractDefinition const*, std::shared_ptr<Compiler const>> const& _otherCompilers, bytes const& _metadata ); /// @returns Entire assembly. eth::Assembly const& assembly() const { return m_context.assembly(); } + /// @returns Entire assembly as a shared pointer to non-const. + std::shared_ptr<eth::Assembly> assemblyPtr() const { return m_context.assemblyPtr(); } + /// @returns Runtime assembly. + std::shared_ptr<eth::Assembly> runtimeAssemblyPtr() const; /// @returns The entire assembled object (with constructor). eth::LinkerObject assembledObject() const { return m_context.assembledObject(); } /// @returns Only the runtime object (without constructor). diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index be681b2e..861b1c98 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -167,11 +167,18 @@ unsigned CompilerContext::numberOfLocalVariables() const return m_localVariables.size(); } -eth::Assembly const& CompilerContext::compiledContract(const ContractDefinition& _contract) const +shared_ptr<eth::Assembly> CompilerContext::compiledContract(ContractDefinition const& _contract) const { - auto ret = m_compiledContracts.find(&_contract); - solAssert(ret != m_compiledContracts.end(), "Compiled contract not found."); - return *ret->second; + auto ret = m_otherCompilers.find(&_contract); + solAssert(ret != m_otherCompilers.end(), "Compiled contract not found."); + return ret->second->assemblyPtr(); +} + +shared_ptr<eth::Assembly> CompilerContext::compiledContractRuntime(ContractDefinition const& _contract) const +{ + auto ret = m_otherCompilers.find(&_contract); + solAssert(ret != m_otherCompilers.end(), "Compiled contract not found."); + return ret->second->runtimeAssemblyPtr(); } bool CompilerContext::isLocalVariable(Declaration const* _declaration) const diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index dedcd95f..e5ddfbc5 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -41,6 +41,7 @@ namespace dev { namespace solidity { +class Compiler; /** * Context to be shared by all units that compile the same contract. @@ -74,8 +75,9 @@ public: /// Returns the number of currently allocated local variables. unsigned numberOfLocalVariables() const; - void setCompiledContracts(std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts) { m_compiledContracts = _contracts; } - eth::Assembly const& compiledContract(ContractDefinition const& _contract) const; + void setOtherCompilers(std::map<ContractDefinition const*, std::shared_ptr<Compiler const>> const& _otherCompilers) { m_otherCompilers = _otherCompilers; } + std::shared_ptr<eth::Assembly> compiledContract(ContractDefinition const& _contract) const; + std::shared_ptr<eth::Assembly> compiledContractRuntime(ContractDefinition const& _contract) const; void setStackOffset(int _offset) { m_asm->setDeposit(_offset); } void adjustStackOffset(int _adjustment) { m_asm->adjustDeposit(_adjustment); } @@ -222,15 +224,15 @@ public: void optimise(bool _fullOptimsation, unsigned _runs = 200) { m_asm->optimise(_fullOptimsation, m_evmVersion, true, _runs); } /// @returns the runtime context if in creation mode and runtime context is set, nullptr otherwise. - CompilerContext* runtimeContext() { return m_runtimeContext; } + CompilerContext* runtimeContext() const { return m_runtimeContext; } /// @returns the identifier of the runtime subroutine. size_t runtimeSub() const { return m_runtimeSub; } /// @returns a const reference to the underlying assembly. eth::Assembly const& assembly() const { return *m_asm; } - /// @returns non-const reference to the underlying assembly. Should be avoided in favour of - /// wrappers in this class. - eth::Assembly& nonConstAssembly() { return *m_asm; } + /// @returns a shared pointer to the assembly. + /// Should be avoided except when adding sub-assemblies. + std::shared_ptr<eth::Assembly> assemblyPtr() const { return m_asm; } /// @arg _sourceCodes is the map of input files to source code strings std::string assemblyString(StringMap const& _sourceCodes = StringMap()) const @@ -307,7 +309,7 @@ private: /// Activated experimental features. std::set<ExperimentalFeature> m_experimentalFeatures; /// Other already compiled contracts to be used in contract creation calls. - std::map<ContractDefinition const*, eth::Assembly const*> m_compiledContracts; + std::map<ContractDefinition const*, std::shared_ptr<Compiler const>> m_otherCompilers; /// Storage offsets of state variables std::map<Declaration const*, std::pair<u256, unsigned>> m_stateVariables; /// Offsets of local variables on the stack (relative to stack base). diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index bbc703c7..6cfb0777 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -1199,6 +1199,29 @@ void CompilerUtils::computeHashStatic() m_context << u256(32) << u256(0) << Instruction::KECCAK256; } +void CompilerUtils::copyContractCodeToMemory(ContractDefinition const& contract, bool _creation) +{ + string which = _creation ? "Creation" : "Runtime"; + m_context.callLowLevelFunction( + "$copyContract" + which + "CodeToMemory_" + contract.type()->identifier(), + 1, + 1, + [&contract, _creation](CompilerContext& _context) + { + // copy the contract's code into memory + shared_ptr<eth::Assembly> assembly = + _creation ? + _context.compiledContract(contract) : + _context.compiledContractRuntime(contract); + // pushes size + auto subroutine = _context.addSubroutine(assembly); + _context << Instruction::DUP1 << subroutine; + _context << Instruction::DUP4 << Instruction::CODECOPY; + _context << Instruction::ADD; + } + ); +} + void CompilerUtils::storeStringData(bytesConstRef _data) { //@todo provide both alternatives to the optimiser diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h index 7e4f47ba..6bde2e8b 100644 --- a/libsolidity/codegen/CompilerUtils.h +++ b/libsolidity/codegen/CompilerUtils.h @@ -263,6 +263,13 @@ public: /// Appends code that computes the Keccak-256 hash of the topmost stack element of 32 byte type. void computeHashStatic(); + /// Apppends code that copies the code of the given contract to memory. + /// Stack pre: Memory position + /// Stack post: Updated memory position + /// @param creation if true, copies creation code, if false copies runtime code. + /// @note the contract has to be compiled already, so beware of cyclic dependencies! + void copyContractCodeToMemory(ContractDefinition const& contract, bool _creationCode); + /// Bytes we need to the start of call data. /// - The size in bytes of the function (hash) identifier. static const unsigned dataStartOffset; diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index b051d260..f843e07a 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -60,7 +60,7 @@ private: void ContractCompiler::compileContract( ContractDefinition const& _contract, - std::map<const ContractDefinition*, eth::Assembly const*> const& _contracts + map<ContractDefinition const*, shared_ptr<Compiler const>> const& _otherCompilers ) { CompilerContext::LocationSetter locationSetter(m_context, _contract); @@ -70,7 +70,7 @@ void ContractCompiler::compileContract( // This has to be the first code in the contract. appendDelegatecallCheck(); - initializeContext(_contract, _contracts); + initializeContext(_contract, _otherCompilers); // This generates the dispatch function for externally visible functions // and adds the function to the compilation queue. Additionally internal functions, // which are referenced directly or indirectly will be added. @@ -81,7 +81,7 @@ void ContractCompiler::compileContract( size_t ContractCompiler::compileConstructor( ContractDefinition const& _contract, - std::map<const ContractDefinition*, eth::Assembly const*> const& _contracts + std::map<ContractDefinition const*, shared_ptr<Compiler const>> const& _otherCompilers ) { CompilerContext::LocationSetter locationSetter(m_context, _contract); @@ -89,18 +89,18 @@ size_t ContractCompiler::compileConstructor( return deployLibrary(_contract); else { - initializeContext(_contract, _contracts); + initializeContext(_contract, _otherCompilers); return packIntoContractCreator(_contract); } } void ContractCompiler::initializeContext( ContractDefinition const& _contract, - map<ContractDefinition const*, eth::Assembly const*> const& _compiledContracts + map<ContractDefinition const*, shared_ptr<Compiler const>> const& _otherCompilers ) { m_context.setExperimentalFeatures(_contract.sourceUnit().annotation().experimentalFeatures); - m_context.setCompiledContracts(_compiledContracts); + m_context.setOtherCompilers(_otherCompilers); m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts); CompilerUtils(m_context).initialiseFreeMemoryPointer(); registerStateVariables(_contract); @@ -716,7 +716,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) CodeGenerator::assemble( _inlineAssembly.operations(), *_inlineAssembly.annotation().analysisInfo, - m_context.nonConstAssembly(), + *m_context.assemblyPtr(), identifierAccess ); m_context.setStackOffset(startStackHeight); diff --git a/libsolidity/codegen/ContractCompiler.h b/libsolidity/codegen/ContractCompiler.h index 40871f0d..9ab006f6 100644 --- a/libsolidity/codegen/ContractCompiler.h +++ b/libsolidity/codegen/ContractCompiler.h @@ -49,13 +49,13 @@ public: void compileContract( ContractDefinition const& _contract, - std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts + std::map<ContractDefinition const*, std::shared_ptr<Compiler const>> const& _otherCompilers ); /// Compiles the constructor part of the contract. /// @returns the identifier of the runtime sub-assembly. size_t compileConstructor( ContractDefinition const& _contract, - std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts + std::map<ContractDefinition const*, std::shared_ptr<Compiler const>> const& _otherCompilers ); private: @@ -63,7 +63,7 @@ private: /// information about the contract like the AST annotations. void initializeContext( ContractDefinition const& _contract, - std::map<ContractDefinition const*, eth::Assembly const*> const& _compiledContracts + std::map<ContractDefinition const*, std::shared_ptr<Compiler const>> const& _otherCompilers ); /// Adds the code that is run at creation time. Should be run after exchanging the run-time context /// with a new and initialized context. Adds the constructor code. diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index be2709ae..e6bb163d 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -594,22 +594,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) } ContractDefinition const* contract = &dynamic_cast<ContractType const&>(*function.returnParameterTypes().front()).contractDefinition(); - m_context.callLowLevelFunction( - "$copyContractCreationCodeToMemory_" + contract->type()->identifier(), - 0, - 1, - [contract](CompilerContext& _context) - { - // copy the contract's code into memory - eth::Assembly const& assembly = _context.compiledContract(*contract); - CompilerUtils(_context).fetchFreeMemoryPointer(); - // pushes size - auto subroutine = _context.addSubroutine(make_shared<eth::Assembly>(assembly)); - _context << Instruction::DUP1 << subroutine; - _context << Instruction::DUP4 << Instruction::CODECOPY; - _context << Instruction::ADD; - } - ); + utils().fetchFreeMemoryPointer(); + utils().copyContractCodeToMemory(*contract, true); utils().abiEncode(argumentTypes, function.parameterTypes()); // now on stack: memory_end_ptr // need: size, offset, endowment @@ -1107,6 +1093,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) case FunctionType::Kind::GasLeft: m_context << Instruction::GAS; break; + case FunctionType::Kind::MetaType: + // No code to generate. + break; } } return false; @@ -1348,6 +1337,23 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) solAssert(false, "Gas has been removed."); else if (member == "blockhash") solAssert(false, "Blockhash has been removed."); + else if (member == "creationCode" || member == "runtimeCode") + { + TypePointer arg = dynamic_cast<MagicType const&>(*_memberAccess.expression().annotation().type).typeArgument(); + ContractDefinition const& contract = dynamic_cast<ContractType const&>(*arg).contractDefinition(); + utils().fetchFreeMemoryPointer(); + m_context << Instruction::DUP1 << u256(32) << Instruction::ADD; + utils().copyContractCodeToMemory(contract, member == "creationCode"); + // Stack: start end + m_context.appendInlineAssembly( + Whiskers(R"({ + mstore(start, sub(end, add(start, 0x20))) + mstore(<free>, end) + })")("free", to_string(CompilerUtils::freeMemoryPointer)).render(), + {"start", "end"} + ); + m_context << Instruction::POP; + } else solAssert(false, "Unknown magic member."); break; diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index f9d889e7..9e4da62d 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -343,12 +343,12 @@ bool CompilerStack::compile() return false; // Only compile contracts individually which have been requested. - map<ContractDefinition const*, eth::Assembly const*> compiledContracts; + map<ContractDefinition const*, shared_ptr<Compiler const>> otherCompilers; for (Source const* source: m_sourceOrder) for (ASTPointer<ASTNode> const& node: source->ast->nodes()) if (auto contract = dynamic_cast<ContractDefinition const*>(node.get())) if (isRequestedContract(*contract)) - compileContract(*contract, compiledContracts); + compileContract(*contract, otherCompilers); m_stackState = CompilationSuccessful; this->link(); return true; @@ -795,19 +795,15 @@ bool onlySafeExperimentalFeaturesActivated(set<ExperimentalFeature> const& featu void CompilerStack::compileContract( ContractDefinition const& _contract, - map<ContractDefinition const*, eth::Assembly const*>& _compiledContracts + map<ContractDefinition const*, shared_ptr<Compiler const>>& _otherCompilers ) { solAssert(m_stackState >= AnalysisSuccessful, ""); - if ( - _compiledContracts.count(&_contract) || - !_contract.annotation().unimplementedFunctions.empty() || - !_contract.constructorIsPublic() - ) + if (_otherCompilers.count(&_contract) || !_contract.canBeDeployed()) return; for (auto const* dependency: _contract.annotation().contractDependencies) - compileContract(*dependency, _compiledContracts); + compileContract(*dependency, _otherCompilers); Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName()); @@ -825,7 +821,7 @@ void CompilerStack::compileContract( try { // Run optimiser and compile the contract. - compiler->compileContract(_contract, _compiledContracts, cborEncodedMetadata); + compiler->compileContract(_contract, _otherCompilers, cborEncodedMetadata); } catch(eth::OptimizerException const&) { @@ -852,7 +848,7 @@ void CompilerStack::compileContract( solAssert(false, "Assembly exception for deployed bytecode"); } - _compiledContracts[compiledContract.contract] = &compiler->assembly(); + _otherCompilers[compiledContract.contract] = compiler; } CompilerStack::Contract const& CompilerStack::contract(string const& _contractName) const diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 81d5009f..79927850 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -293,10 +293,12 @@ private: /// @returns true if the contract is requested to be compiled. bool isRequestedContract(ContractDefinition const& _contract) const; - /// Compile a single contract and put the result in @a _compiledContracts. + /// Compile a single contract. + /// @param _otherCompilers provides access to compilers of other contracts, to get + /// their bytecode if needed. Only filled after they have been compiled. void compileContract( ContractDefinition const& _contract, - std::map<ContractDefinition const*, eth::Assembly const*>& _compiledContracts + std::map<ContractDefinition const*, std::shared_ptr<Compiler const>>& _otherCompilers ); /// Links all the known library addresses in the available objects. Any unknown diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 8a6bc343..35476a76 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -1551,6 +1551,12 @@ ASTPointer<Expression> Parser::parsePrimaryExpression() nodeFactory.markEndPosition(); expression = nodeFactory.createNode<Identifier>(getLiteralAndAdvance()); break; + case Token::Type: + // Inside expressions "type" is the name of a special, globally-available function. + nodeFactory.markEndPosition(); + m_scanner->next(); + expression = nodeFactory.createNode<Identifier>(make_shared<ASTString>("type")); + break; case Token::LParen: case Token::LBrack: { diff --git a/libyul/AsmData.h b/libyul/AsmData.h index 86c373a4..fd8eab38 100644 --- a/libyul/AsmData.h +++ b/libyul/AsmData.h @@ -59,25 +59,25 @@ struct StackAssignment { langutil::SourceLocation location; Identifier variableN /// Multiple assignment ("x, y := f()"), where the left hand side variables each occupy /// a single stack slot and expects a single expression on the right hand returning /// the same amount of items as the number of variables. -struct Assignment { langutil::SourceLocation location; std::vector<Identifier> variableNames; std::shared_ptr<Expression> value; }; +struct Assignment { langutil::SourceLocation location; std::vector<Identifier> variableNames; std::unique_ptr<Expression> value; }; /// Functional instruction, e.g. "mul(mload(20:u256), add(2:u256, x))" struct FunctionalInstruction { langutil::SourceLocation location; dev::solidity::Instruction instruction; std::vector<Expression> arguments; }; struct FunctionCall { langutil::SourceLocation location; Identifier functionName; std::vector<Expression> arguments; }; /// Statement that contains only a single expression struct ExpressionStatement { langutil::SourceLocation location; Expression expression; }; /// Block-scope variable declaration ("let x:u256 := mload(20:u256)"), non-hoisted -struct VariableDeclaration { langutil::SourceLocation location; TypedNameList variables; std::shared_ptr<Expression> value; }; +struct VariableDeclaration { langutil::SourceLocation location; TypedNameList variables; std::unique_ptr<Expression> value; }; /// Block that creates a scope (frees declared stack variables) struct Block { langutil::SourceLocation location; std::vector<Statement> statements; }; /// Function definition ("function f(a, b) -> (d, e) { ... }") struct FunctionDefinition { langutil::SourceLocation location; YulString name; TypedNameList parameters; TypedNameList returnVariables; Block body; }; /// Conditional execution without "else" part. -struct If { langutil::SourceLocation location; std::shared_ptr<Expression> condition; Block body; }; +struct If { langutil::SourceLocation location; std::unique_ptr<Expression> condition; Block body; }; /// Switch case or default case -struct Case { langutil::SourceLocation location; std::shared_ptr<Literal> value; Block body; }; +struct Case { langutil::SourceLocation location; std::unique_ptr<Literal> value; Block body; }; /// Switch statement -struct Switch { langutil::SourceLocation location; std::shared_ptr<Expression> expression; std::vector<Case> cases; }; -struct ForLoop { langutil::SourceLocation location; Block pre; std::shared_ptr<Expression> condition; Block post; Block body; }; +struct Switch { langutil::SourceLocation location; std::unique_ptr<Expression> expression; std::vector<Case> cases; }; +struct ForLoop { langutil::SourceLocation location; Block pre; std::unique_ptr<Expression> condition; Block post; Block body; }; struct LocationExtractor: boost::static_visitor<langutil::SourceLocation> { diff --git a/libyul/AsmParser.cpp b/libyul/AsmParser.cpp index f3ca6cd0..217c838a 100644 --- a/libyul/AsmParser.cpp +++ b/libyul/AsmParser.cpp @@ -81,15 +81,15 @@ Statement Parser::parseStatement() { If _if = createWithLocation<If>(); m_scanner->next(); - _if.condition = make_shared<Expression>(parseExpression()); + _if.condition = make_unique<Expression>(parseExpression()); _if.body = parseBlock(); - return _if; + return Statement{move(_if)}; } case Token::Switch: { Switch _switch = createWithLocation<Switch>(); m_scanner->next(); - _switch.expression = make_shared<Expression>(parseExpression()); + _switch.expression = make_unique<Expression>(parseExpression()); while (m_scanner->currentToken() == Token::Case) _switch.cases.emplace_back(parseCase()); if (m_scanner->currentToken() == Token::Default) @@ -101,7 +101,7 @@ Statement Parser::parseStatement() if (_switch.cases.empty()) fatalParserError("Switch statement without any cases."); _switch.location.end = _switch.cases.back().body.location.end; - return _switch; + return Statement{move(_switch)}; } case Token::For: return parseForLoop(); @@ -120,7 +120,7 @@ Statement Parser::parseStatement() fatalParserError("Identifier expected, got instruction name."); assignment.location.end = endPosition(); expectToken(Token::Identifier); - return assignment; + return Statement{move(assignment)}; } default: break; @@ -163,7 +163,7 @@ Statement Parser::parseStatement() assignment.value.reset(new Expression(parseExpression())); assignment.location.end = locationOf(*assignment.value).end; - return assignment; + return Statement{std::move(assignment)}; } case Token::Colon: { @@ -184,7 +184,7 @@ Statement Parser::parseStatement() assignment.variableNames.emplace_back(identifier); assignment.value.reset(new Expression(parseExpression())); assignment.location.end = locationOf(*assignment.value).end; - return assignment; + return Statement{std::move(assignment)}; } else { @@ -230,7 +230,7 @@ Case Parser::parseCase() ElementaryOperation literal = parseElementaryOperation(); if (literal.type() != typeid(Literal)) fatalParserError("Literal expected."); - _case.value = make_shared<Literal>(boost::get<Literal>(std::move(literal))); + _case.value = make_unique<Literal>(boost::get<Literal>(std::move(literal))); } else fatalParserError("Case or default case expected."); @@ -245,7 +245,7 @@ ForLoop Parser::parseForLoop() ForLoop forLoop = createWithLocation<ForLoop>(); expectToken(Token::For); forLoop.pre = parseBlock(); - forLoop.condition = make_shared<Expression>(parseExpression()); + forLoop.condition = make_unique<Expression>(parseExpression()); forLoop.post = parseBlock(); forLoop.body = parseBlock(); forLoop.location.end = forLoop.body.location.end; @@ -443,7 +443,7 @@ VariableDeclaration Parser::parseVariableDeclaration() { expectToken(Token::Colon); expectToken(Token::Assign); - varDecl.value.reset(new Expression(parseExpression())); + varDecl.value = make_unique<Expression>(parseExpression()); varDecl.location.end = locationOf(*varDecl.value).end; } else diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index 44af2fc3..259f43f8 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -80,6 +80,8 @@ add_library(yul optimiser/RedundantAssignEliminator.h optimiser/Rematerialiser.cpp optimiser/Rematerialiser.h + optimiser/SSAReverser.cpp + optimiser/SSAReverser.h optimiser/SSATransform.cpp optimiser/SSATransform.h optimiser/SSAValueTracker.cpp diff --git a/libyul/optimiser/ASTCopier.h b/libyul/optimiser/ASTCopier.h index 4d2f18ae..7e5e9c86 100644 --- a/libyul/optimiser/ASTCopier.h +++ b/libyul/optimiser/ASTCopier.h @@ -93,10 +93,11 @@ protected: std::vector<T> translateVector(std::vector<T> const& _values); template <typename T> - std::shared_ptr<T> translate(std::shared_ptr<T> const& _v) + std::unique_ptr<T> translate(std::unique_ptr<T> const& _v) { - return _v ? std::make_shared<T>(translate(*_v)) : nullptr; + return _v ? std::make_unique<T>(translate(*_v)) : nullptr; } + Block translate(Block const& _block); Case translate(Case const& _case); Identifier translate(Identifier const& _identifier); diff --git a/libyul/optimiser/DataFlowAnalyzer.cpp b/libyul/optimiser/DataFlowAnalyzer.cpp index e97bcdc4..b4f2d1f9 100644 --- a/libyul/optimiser/DataFlowAnalyzer.cpp +++ b/libyul/optimiser/DataFlowAnalyzer.cpp @@ -51,8 +51,10 @@ void DataFlowAnalyzer::operator()(VariableDeclaration& _varDecl) for (auto const& var: _varDecl.variables) names.emplace(var.name); m_variableScopes.back().variables += names; + if (_varDecl.value) visit(*_varDecl.value); + handleAssignment(names, _varDecl.value.get()); } diff --git a/libyul/optimiser/ExpressionSplitter.cpp b/libyul/optimiser/ExpressionSplitter.cpp index 334e33f4..2f80fc32 100644 --- a/libyul/optimiser/ExpressionSplitter.cpp +++ b/libyul/optimiser/ExpressionSplitter.cpp @@ -106,7 +106,7 @@ void ExpressionSplitter::outlineExpression(Expression& _expr) m_statementsToPrefix.emplace_back(VariableDeclaration{ location, {{TypedName{location, var, {}}}}, - make_shared<Expression>(std::move(_expr)) + make_unique<Expression>(std::move(_expr)) }); _expr = Identifier{location, var}; } diff --git a/libyul/optimiser/FullInliner.cpp b/libyul/optimiser/FullInliner.cpp index dd969faf..ca6162ec 100644 --- a/libyul/optimiser/FullInliner.cpp +++ b/libyul/optimiser/FullInliner.cpp @@ -181,9 +181,9 @@ vector<Statement> InlineModifier::performInline(Statement& _statement, FunctionC variableReplacements[_existingVariable.name] = newName; VariableDeclaration varDecl{_funCall.location, {{_funCall.location, newName, _existingVariable.type}}, {}}; if (_value) - varDecl.value = make_shared<Expression>(std::move(*_value)); + varDecl.value = make_unique<Expression>(std::move(*_value)); else - varDecl.value = make_shared<Expression>(zero); + varDecl.value = make_unique<Expression>(zero); newStatements.emplace_back(std::move(varDecl)); }; @@ -202,7 +202,7 @@ vector<Statement> InlineModifier::performInline(Statement& _statement, FunctionC newStatements.emplace_back(Assignment{ _assignment.location, {_assignment.variableNames[i]}, - make_shared<Expression>(Identifier{ + make_unique<Expression>(Identifier{ _assignment.location, variableReplacements.at(function->returnVariables[i].name) }) @@ -214,7 +214,7 @@ vector<Statement> InlineModifier::performInline(Statement& _statement, FunctionC newStatements.emplace_back(VariableDeclaration{ _varDecl.location, {std::move(_varDecl.variables[i])}, - make_shared<Expression>(Identifier{ + make_unique<Expression>(Identifier{ _varDecl.location, variableReplacements.at(function->returnVariables[i].name) }) @@ -232,10 +232,10 @@ Statement BodyCopier::operator()(VariableDeclaration const& _varDecl) return ASTCopier::operator()(_varDecl); } -Statement BodyCopier::operator()(FunctionDefinition const& _funDef) +Statement BodyCopier::operator()(FunctionDefinition const&) { assertThrow(false, OptimizerException, "Function hoisting has to be done before function inlining."); - return _funDef; + return {}; } YulString BodyCopier::translateIdentifier(YulString _name) diff --git a/libyul/optimiser/Metrics.cpp b/libyul/optimiser/Metrics.cpp index df919682..e13940aa 100644 --- a/libyul/optimiser/Metrics.cpp +++ b/libyul/optimiser/Metrics.cpp @@ -134,3 +134,15 @@ void CodeCost::visit(Expression const& _expression) ++m_cost; ASTWalker::visit(_expression); } + +void AssignmentCounter::operator()(Assignment const& _assignment) +{ + for (auto const& variable: _assignment.variableNames) + ++m_assignmentCounters[variable.name]; +} + +size_t AssignmentCounter::assignmentCount(YulString _name) const +{ + auto it = m_assignmentCounters.find(_name); + return (it == m_assignmentCounters.end()) ? 0 : it->second; +} diff --git a/libyul/optimiser/Metrics.h b/libyul/optimiser/Metrics.h index 03e1b62a..5364646e 100644 --- a/libyul/optimiser/Metrics.h +++ b/libyul/optimiser/Metrics.h @@ -77,4 +77,18 @@ private: size_t m_cost = 0; }; +/** + * Counts the number of assignments to every variable. + * Only works after running the Disambiguator. + */ +class AssignmentCounter: public ASTWalker +{ +public: + using ASTWalker::operator(); + void operator()(Assignment const& _assignment) override; + std::size_t assignmentCount(YulString _name) const; +private: + std::map<YulString, size_t> m_assignmentCounters; +}; + } diff --git a/libyul/optimiser/SSAReverser.cpp b/libyul/optimiser/SSAReverser.cpp new file mode 100644 index 00000000..6548ebb5 --- /dev/null +++ b/libyul/optimiser/SSAReverser.cpp @@ -0,0 +1,114 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +#include <libyul/optimiser/SSAReverser.h> +#include <libyul/optimiser/Metrics.h> +#include <libyul/AsmData.h> +#include <libdevcore/CommonData.h> + +using namespace std; +using namespace dev; +using namespace yul; + +void SSAReverser::run(Block& _block) +{ + AssignmentCounter assignmentCounter; + assignmentCounter(_block); + SSAReverser{assignmentCounter}(_block); +} + +void SSAReverser::operator()(Block& _block) +{ + walkVector(_block.statements); + iterateReplacingWindow<2>( + _block.statements, + [&](Statement& _stmt1, Statement& _stmt2) -> boost::optional<vector<Statement>> + { + auto* varDecl = boost::get<VariableDeclaration>(&_stmt1); + + if (!varDecl || varDecl->variables.size() != 1 || !varDecl->value) + return {}; + + // Replaces + // let a_1 := E + // a := a_1 + // with + // a := E + // let a_1 := a + if (auto* assignment = boost::get<Assignment>(&_stmt2)) + { + auto* identifier = boost::get<Identifier>(assignment->value.get()); + if ( + assignment->variableNames.size() == 1 && + identifier && + identifier->name == varDecl->variables.front().name + ) + { + vector<Statement> result; + result.emplace_back(Assignment{ + std::move(assignment->location), + assignment->variableNames, + std::move(varDecl->value) + }); + result.emplace_back(VariableDeclaration{ + std::move(varDecl->location), + std::move(varDecl->variables), + std::make_unique<Expression>(std::move(assignment->variableNames.front())) + }); + return { std::move(result) }; + } + } + // Replaces + // let a_1 := E + // let a := a_1 + // with + // let a := E + // let a_1 := a + else if (auto* varDecl2 = boost::get<VariableDeclaration>(&_stmt2)) + { + auto* identifier = boost::get<Identifier>(varDecl2->value.get()); + if ( + varDecl2->variables.size() == 1 && + identifier && + identifier->name == varDecl->variables.front().name && ( + m_assignmentCounter.assignmentCount(varDecl2->variables.front().name) > + m_assignmentCounter.assignmentCount(varDecl->variables.front().name) + ) + ) + { + vector<Statement> result; + auto varIdentifier2 = std::make_unique<Expression>(Identifier{ + varDecl2->variables.front().location, + varDecl2->variables.front().name + }); + result.emplace_back(VariableDeclaration{ + std::move(varDecl2->location), + std::move(varDecl2->variables), + std::move(varDecl->value) + }); + result.emplace_back(VariableDeclaration{ + std::move(varDecl->location), + std::move(varDecl->variables), + std::move(varIdentifier2) + }); + return { std::move(result) }; + } + } + + return {}; + } + ); +} diff --git a/libyul/optimiser/SSAReverser.h b/libyul/optimiser/SSAReverser.h new file mode 100644 index 00000000..67abeb56 --- /dev/null +++ b/libyul/optimiser/SSAReverser.h @@ -0,0 +1,74 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <libyul/optimiser/ASTWalker.h> + +namespace yul +{ + +class AssignmentCounter; + +/** + * Reverses the SSA transformation. + * + * In particular, the SSA transform will rewrite + * + * a := E + * + * to + * + * let a_1 := E + * a := a_1 + * + * To undo this kind of transformation, the SSAReverser changes this back to + * + * a := E + * let a_1 := a + * + * Secondly, the SSA transform will rewrite + * + * let a := E + * to + * + * let a_1 := E + * let a := a_1 + * + * To undo this kind of transformation, the SSAReverser changes this back to + * + * let a := E + * let a_1 := a + * + * After that the CSE can replace references of a_1 by references to a, + * after which the unused pruner can remove the declaration of a_1. + * + * Prerequisites: Disambiguator + * + */ +class SSAReverser: public ASTModifier +{ +public: + using ASTModifier::operator(); + void operator()(Block& _block) override; + + static void run(Block& _block); +private: + SSAReverser(AssignmentCounter const& _assignmentCounter): m_assignmentCounter(_assignmentCounter) {} + AssignmentCounter const& m_assignmentCounter; +}; + +} diff --git a/libyul/optimiser/SSATransform.cpp b/libyul/optimiser/SSATransform.cpp index 928c0859..33c875b5 100644 --- a/libyul/optimiser/SSATransform.cpp +++ b/libyul/optimiser/SSATransform.cpp @@ -66,12 +66,13 @@ void SSATransform::operator()(Block& _block) // Creates a new variable (and returns its declaration) with value _value // and replaces _value by a reference to that new variable. - auto replaceByNew = [&](SourceLocation _loc, YulString _varName, YulString _type, shared_ptr<Expression>& _value) -> VariableDeclaration + + auto replaceByNew = [&](SourceLocation _loc, YulString _varName, YulString _type, unique_ptr<Expression>& _value) -> VariableDeclaration { YulString newName = m_nameDispenser.newName(_varName); m_currentVariableValues[_varName] = newName; variablesToClearAtEnd.emplace(_varName); - shared_ptr<Expression> v = make_shared<Expression>(Identifier{_loc, newName}); + unique_ptr<Expression> v = make_unique<Expression>(Identifier{_loc, newName}); _value.swap(v); return VariableDeclaration{_loc, {TypedName{_loc, std::move(newName), std::move(_type)}}, std::move(v)}; }; @@ -87,14 +88,16 @@ void SSATransform::operator()(Block& _block) visit(*varDecl.value); if (varDecl.variables.size() != 1 || !m_variablesToReplace.count(varDecl.variables.front().name)) return {}; - // Replace "let a := v" by "let a_1 := v let a := v" - VariableDeclaration newVarDecl = replaceByNew( + vector<Statement> v; + // Replace "let a := v" by "let a_1 := v let a := a_1" + v.emplace_back(replaceByNew( varDecl.location, varDecl.variables.front().name, varDecl.variables.front().type, varDecl.value - ); - return vector<Statement>{std::move(newVarDecl), std::move(varDecl)}; + )); + v.emplace_back(move(varDecl)); + return v; } else if (_s.type() == typeid(Assignment)) { @@ -103,14 +106,16 @@ void SSATransform::operator()(Block& _block) if (assignment.variableNames.size() != 1) return {}; assertThrow(m_variablesToReplace.count(assignment.variableNames.front().name), OptimizerException, ""); + vector<Statement> v; // Replace "a := v" by "let a_1 := v a := v" - VariableDeclaration newVarDecl = replaceByNew( + v.emplace_back(replaceByNew( assignment.location, assignment.variableNames.front().name, {}, // TODO determine type assignment.value - ); - return vector<Statement>{std::move(newVarDecl), std::move(assignment)}; + )); + v.emplace_back(move(assignment)); + return v; } else visit(_s); diff --git a/libyul/optimiser/StructuralSimplifier.cpp b/libyul/optimiser/StructuralSimplifier.cpp index 8d7dcd57..727775cb 100644 --- a/libyul/optimiser/StructuralSimplifier.cpp +++ b/libyul/optimiser/StructuralSimplifier.cpp @@ -51,7 +51,11 @@ void StructuralSimplifier::simplify(std::vector<yul::Statement>& _statements) GenericFallbackReturnsVisitor<OptionalStatements, If, Switch, ForLoop> const visitor( [&](If& _ifStmt) -> OptionalStatements { if (_ifStmt.body.statements.empty()) - return {{makePopExpressionStatement(_ifStmt.location, std::move(*_ifStmt.condition))}}; + { + OptionalStatements s = vector<Statement>{}; + s->emplace_back(makePopExpressionStatement(_ifStmt.location, std::move(*_ifStmt.condition))); + return s; + } if (expressionAlwaysTrue(*_ifStmt.condition)) return {std::move(_ifStmt.body.statements)}; else if (expressionAlwaysFalse(*_ifStmt.condition)) @@ -64,18 +68,24 @@ void StructuralSimplifier::simplify(std::vector<yul::Statement>& _statements) auto& switchCase = _switchStmt.cases.front(); auto loc = locationOf(*_switchStmt.expression); if (switchCase.value) - return {{If{ + { + OptionalStatements s = vector<Statement>{}; + s->emplace_back(If{ std::move(_switchStmt.location), - make_shared<Expression>(FunctionalInstruction{ + make_unique<Expression>(FunctionalInstruction{ std::move(loc), solidity::Instruction::EQ, {std::move(*switchCase.value), std::move(*_switchStmt.expression)} - }), std::move(switchCase.body)}}}; + }), std::move(switchCase.body)}); + return s; + } else - return {{ - makePopExpressionStatement(loc, std::move(*_switchStmt.expression)), - std::move(switchCase.body) - }}; + { + OptionalStatements s = vector<Statement>{}; + s->emplace_back(makePopExpressionStatement(loc, std::move(*_switchStmt.expression))); + s->emplace_back(std::move(switchCase.body)); + return s; + } } else return {}; diff --git a/libyul/optimiser/Suite.cpp b/libyul/optimiser/Suite.cpp index 38c0bf49..8cf6e104 100644 --- a/libyul/optimiser/Suite.cpp +++ b/libyul/optimiser/Suite.cpp @@ -35,6 +35,7 @@ #include <libyul/optimiser/UnusedPruner.h> #include <libyul/optimiser/ExpressionSimplifier.h> #include <libyul/optimiser/CommonSubexpressionEliminator.h> +#include <libyul/optimiser/SSAReverser.h> #include <libyul/optimiser/SSATransform.h> #include <libyul/optimiser/StructuralSimplifier.h> #include <libyul/optimiser/RedundantAssignEliminator.h> @@ -88,9 +89,10 @@ void OptimiserSuite::run( UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); CommonSubexpressionEliminator{_dialect}(ast); UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); - SSATransform::run(ast, dispenser); - RedundantAssignEliminator::run(_dialect, ast); - RedundantAssignEliminator::run(_dialect, ast); + + SSAReverser::run(ast); + CommonSubexpressionEliminator{_dialect}(ast); + UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); ExpressionJoiner::run(ast); ExpressionJoiner::run(ast); @@ -127,6 +129,11 @@ void OptimiserSuite::run( UnusedPruner::runUntilStabilised(_dialect, ast); ExpressionJoiner::run(ast); UnusedPruner::runUntilStabilised(_dialect, ast); + + SSAReverser::run(ast); + CommonSubexpressionEliminator{_dialect}(ast); + UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); + ExpressionJoiner::run(ast); Rematerialiser::run(_dialect, ast); UnusedPruner::runUntilStabilised(_dialect, ast); diff --git a/libyul/optimiser/SyntacticalEquality.cpp b/libyul/optimiser/SyntacticalEquality.cpp index ba8cc793..53f0b029 100644 --- a/libyul/optimiser/SyntacticalEquality.cpp +++ b/libyul/optimiser/SyntacticalEquality.cpp @@ -100,7 +100,7 @@ bool SyntacticallyEqual::statementEqual(Assignment const& _lhs, Assignment const bool SyntacticallyEqual::statementEqual(VariableDeclaration const& _lhs, VariableDeclaration const& _rhs) { // first visit expression, then variable declarations - if (!compareSharedPtr<Expression, &SyntacticallyEqual::operator()>(_lhs.value, _rhs.value)) + if (!compareUniquePtr<Expression, &SyntacticallyEqual::operator()>(_lhs.value, _rhs.value)) return false; return containerEqual(_lhs.variables, _rhs.variables, [this](TypedName const& _lhsVarName, TypedName const& _rhsVarName) -> bool { return this->visitDeclaration(_lhsVarName, _rhsVarName); @@ -123,7 +123,7 @@ bool SyntacticallyEqual::statementEqual(FunctionDefinition const& _lhs, Function bool SyntacticallyEqual::statementEqual(If const& _lhs, If const& _rhs) { return - compareSharedPtr<Expression, &SyntacticallyEqual::operator()>(_lhs.condition, _rhs.condition) && + compareUniquePtr<Expression, &SyntacticallyEqual::operator()>(_lhs.condition, _rhs.condition) && statementEqual(_lhs.body, _rhs.body); } @@ -139,7 +139,7 @@ bool SyntacticallyEqual::statementEqual(Switch const& _lhs, Switch const& _rhs) for (auto const& rhsCase: _rhs.cases) rhsCases.insert(&rhsCase); return - compareSharedPtr<Expression, &SyntacticallyEqual::operator()>(_lhs.expression, _rhs.expression) && + compareUniquePtr<Expression, &SyntacticallyEqual::operator()>(_lhs.expression, _rhs.expression) && containerEqual(lhsCases, rhsCases, [this](Case const* _lhsCase, Case const* _rhsCase) -> bool { return this->switchCaseEqual(*_lhsCase, *_rhsCase); }); @@ -149,7 +149,7 @@ bool SyntacticallyEqual::statementEqual(Switch const& _lhs, Switch const& _rhs) bool SyntacticallyEqual::switchCaseEqual(Case const& _lhs, Case const& _rhs) { return - compareSharedPtr<Literal, &SyntacticallyEqual::expressionEqual>(_lhs.value, _rhs.value) && + compareUniquePtr<Literal, &SyntacticallyEqual::expressionEqual>(_lhs.value, _rhs.value) && statementEqual(_lhs.body, _rhs.body); } @@ -157,7 +157,7 @@ bool SyntacticallyEqual::statementEqual(ForLoop const& _lhs, ForLoop const& _rhs { return statementEqual(_lhs.pre, _rhs.pre) && - compareSharedPtr<Expression, &SyntacticallyEqual::operator()>(_lhs.condition, _rhs.condition) && + compareUniquePtr<Expression, &SyntacticallyEqual::operator()>(_lhs.condition, _rhs.condition) && statementEqual(_lhs.body, _rhs.body) && statementEqual(_lhs.post, _rhs.post); } diff --git a/libyul/optimiser/SyntacticalEquality.h b/libyul/optimiser/SyntacticalEquality.h index d4630d95..af240717 100644 --- a/libyul/optimiser/SyntacticalEquality.h +++ b/libyul/optimiser/SyntacticalEquality.h @@ -76,7 +76,7 @@ private: } template<typename T, bool (SyntacticallyEqual::*CompareMember)(T const&, T const&)> - bool compareSharedPtr(std::shared_ptr<T> const& _lhs, std::shared_ptr<T> const& _rhs) + bool compareUniquePtr(std::unique_ptr<T> const& _lhs, std::unique_ptr<T> const& _rhs) { return (_lhs == _rhs) || (_lhs && _rhs && (this->*CompareMember)(*_lhs, *_rhs)); } diff --git a/libyul/optimiser/VarDeclInitializer.cpp b/libyul/optimiser/VarDeclInitializer.cpp index 4a26757f..7009cc9b 100644 --- a/libyul/optimiser/VarDeclInitializer.cpp +++ b/libyul/optimiser/VarDeclInitializer.cpp @@ -39,7 +39,7 @@ void VarDeclInitializer::operator()(Block& _block) return {}; else if (_varDecl.variables.size() == 1) { - _varDecl.value = make_shared<Expression>(zero); + _varDecl.value = make_unique<Expression>(zero); return {}; } else @@ -47,7 +47,7 @@ void VarDeclInitializer::operator()(Block& _block) OptionalStatements ret{vector<Statement>{}}; langutil::SourceLocation loc{std::move(_varDecl.location)}; for (auto& var: _varDecl.variables) - ret->push_back(VariableDeclaration{loc, {std::move(var)}, make_shared<Expression>(zero)}); + ret->push_back(VariableDeclaration{loc, {std::move(var)}, make_unique<Expression>(zero)}); return ret; } } diff --git a/scripts/build_emscripten.sh b/scripts/build_emscripten.sh index 14c497ae..46521cc5 100755 --- a/scripts/build_emscripten.sh +++ b/scripts/build_emscripten.sh @@ -30,5 +30,5 @@ set -e if [[ "$OSTYPE" != "darwin"* ]]; then ./scripts/travis-emscripten/install_deps.sh - docker run -v $(pwd):/root/project -w /root/project trzeci/emscripten:sdk-tag-1.37.21-64bit ./scripts/travis-emscripten/build_emscripten.sh + docker run -v $(pwd):/root/project -w /root/project trzeci/emscripten:sdk-tag-1.38.8-64bit ./scripts/travis-emscripten/build_emscripten.sh fi diff --git a/scripts/travis-emscripten/build_emscripten.sh b/scripts/travis-emscripten/build_emscripten.sh index 32677460..373f32db 100755 --- a/scripts/travis-emscripten/build_emscripten.sh +++ b/scripts/travis-emscripten/build_emscripten.sh @@ -49,11 +49,11 @@ fi WORKSPACE=/root/project # Increase nodejs stack size -if [ -e ~/.emscripten ] +if ! [ -e /emsdk_portable/node/bin/node_orig ] then - sed -i -e 's/NODE_JS="nodejs"/NODE_JS=["nodejs", "--stack_size=8192"]/' ~/.emscripten -else - echo 'NODE_JS=["nodejs", "--stack_size=8192"]' > ~/.emscripten + mv /emsdk_portable/node/bin/node /emsdk_portable/node/bin/node_orig + echo -e '#!/bin/sh\nexec /emsdk_portable/node/bin/node_orig --stack-size=8192 $@' > /emsdk_portable/node/bin/node + chmod 755 /emsdk_portable/node/bin/node fi # Boost diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index bda1b78a..4bf24901 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -731,7 +731,7 @@ bool CommandLineInterface::processInput() try { auto path = boost::filesystem::path(_path); - auto canonicalPath = weaklyCanonicalFilesystemPath(path); + auto canonicalPath = boost::filesystem::weakly_canonical(path); bool isAllowed = false; for (auto const& allowedDir: m_allowedDirectories) { diff --git a/test/libsolidity/ASTJSON/address_payable.json b/test/libsolidity/ASTJSON/address_payable.json index 0f30e8e8..c8b71628 100644 --- a/test/libsolidity/ASTJSON/address_payable.json +++ b/test/libsolidity/ASTJSON/address_payable.json @@ -277,7 +277,7 @@ "name" : "this", "nodeType" : "Identifier", "overloadedDeclarations" : [], - "referencedDeclaration" : 65, + "referencedDeclaration" : 66, "src" : "217:4:1", "typeDescriptions" : { diff --git a/test/libsolidity/ASTJSON/address_payable_legacy.json b/test/libsolidity/ASTJSON/address_payable_legacy.json index dd8a5582..8abebce0 100644 --- a/test/libsolidity/ASTJSON/address_payable_legacy.json +++ b/test/libsolidity/ASTJSON/address_payable_legacy.json @@ -424,7 +424,7 @@ [ null ], - "referencedDeclaration" : 65, + "referencedDeclaration" : 66, "type" : "contract C", "value" : "this" }, diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index baa9bff1..b5a1797b 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -46,6 +46,7 @@ namespace dev { namespace solidity { +class Contract; namespace test { @@ -84,7 +85,7 @@ eth::AssemblyItems compileContract(std::shared_ptr<CharStream> _sourceCode) if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get())) { Compiler compiler(dev::test::Options::get().evmVersion()); - compiler.compileContract(*contract, map<ContractDefinition const*, Assembly const*>{}, bytes()); + compiler.compileContract(*contract, map<ContractDefinition const*, shared_ptr<Compiler const>>{}, bytes()); return compiler.runtimeAssemblyItems(); } diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 8d219d16..38be5ae7 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -14234,6 +14234,113 @@ BOOST_AUTO_TEST_CASE(base_access_to_function_type_variables) ABI_CHECK(callContractFunction("h()"), encodeArgs(2)); } +BOOST_AUTO_TEST_CASE(code_access) +{ + char const* sourceCode = R"( + contract C { + function lengths() public pure returns (bool) { + uint crLen = type(D).creationCode.length; + uint runLen = type(D).runtimeCode.length; + require(runLen < crLen); + require(crLen >= 0x20); + require(runLen >= 0x20); + return true; + } + function creation() public pure returns (bytes memory) { + return type(D).creationCode; + } + function runtime() public pure returns (bytes memory) { + return type(D).runtimeCode; + } + function runtimeAllocCheck() public pure returns (bytes memory) { + uint[] memory a = new uint[](2); + bytes memory c = type(D).runtimeCode; + uint[] memory b = new uint[](2); + a[0] = 0x1111; + a[1] = 0x2222; + b[0] = 0x3333; + b[1] = 0x4444; + return c; + } + } + contract D { + function f() public pure returns (uint) { return 7; } + } + )"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("lengths()"), encodeArgs(true)); + bytes codeCreation = callContractFunction("creation()"); + bytes codeRuntime1 = callContractFunction("runtime()"); + bytes codeRuntime2 = callContractFunction("runtimeAllocCheck()"); + ABI_CHECK(codeRuntime1, codeRuntime2); +} + +BOOST_AUTO_TEST_CASE(code_access_create) +{ + char const* sourceCode = R"( + contract C { + function test() public returns (uint) { + bytes memory c = type(D).creationCode; + D d; + assembly { + d := create(0, add(c, 0x20), mload(c)) + } + return d.f(); + } + } + contract D { + uint x; + constructor() public { x = 7; } + function f() public view returns (uint) { return x; } + } + )"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("test()"), encodeArgs(7)); +} + +BOOST_AUTO_TEST_CASE(code_access_content) +{ + char const* sourceCode = R"( + contract C { + function testRuntime() public returns (bool) { + D d = new D(); + bytes32 runtimeHash = keccak256(type(D).runtimeCode); + bytes32 otherHash; + uint size; + assembly { + size := extcodesize(d) + extcodecopy(d, mload(0x40), 0, size) + otherHash := keccak256(mload(0x40), size) + } + require(size == type(D).runtimeCode.length); + require(runtimeHash == otherHash); + return true; + } + function testCreation() public returns (bool) { + D d = new D(); + bytes32 creationHash = keccak256(type(D).creationCode); + require(creationHash == d.x()); + return true; + } + } + contract D { + bytes32 public x; + constructor() public { + bytes32 codeHash; + assembly { + let size := codesize() + codecopy(mload(0x40), 0, size) + codeHash := keccak256(mload(0x40), size) + } + x = codeHash; + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("testRuntime()"), encodeArgs(true)); + ABI_CHECK(callContractFunction("testCreation()"), encodeArgs(true)); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index a33c6134..413a8c9c 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -539,7 +539,6 @@ BOOST_AUTO_TEST_CASE(keyword_is_reserved) "supports", "switch", "try", - "type", "typedef", "typeof", "unchecked" diff --git a/test/libsolidity/syntaxTests/metaTypes/codeAccess.sol b/test/libsolidity/syntaxTests/metaTypes/codeAccess.sol new file mode 100644 index 00000000..e90443e1 --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/codeAccess.sol @@ -0,0 +1,12 @@ +contract Test { + function creationOther() public pure returns (bytes memory) { + return type(Other).creationCode; + } + function runtimeOther() public pure returns (bytes memory) { + return type(Other).runtimeCode; + } +} +contract Other { + function f(uint) public pure returns (uint) {} +} +// ---- diff --git a/test/libsolidity/syntaxTests/metaTypes/codeAccessAbstractCreation.sol b/test/libsolidity/syntaxTests/metaTypes/codeAccessAbstractCreation.sol new file mode 100644 index 00000000..bae137c5 --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/codeAccessAbstractCreation.sol @@ -0,0 +1,10 @@ +contract Test { + function creationOther() public pure returns (bytes memory) { + return type(Other).creationCode; + } +} +contract Other { + function f(uint) public returns (uint); +} +// ---- +// TypeError: (97-121): Member "creationCode" not found or not visible after argument-dependent lookup in type(contract Other). diff --git a/test/libsolidity/syntaxTests/metaTypes/codeAccessAbstractRuntime.sol b/test/libsolidity/syntaxTests/metaTypes/codeAccessAbstractRuntime.sol new file mode 100644 index 00000000..186d2714 --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/codeAccessAbstractRuntime.sol @@ -0,0 +1,10 @@ +contract Test { + function runtime() public pure returns (bytes memory) { + return type(Other).runtimeCode; + } +} +contract Other { + function f(uint) public returns (uint); +} +// ---- +// TypeError: (91-114): Member "runtimeCode" not found or not visible after argument-dependent lookup in type(contract Other). diff --git a/test/libsolidity/syntaxTests/metaTypes/codeAccessBase.sol b/test/libsolidity/syntaxTests/metaTypes/codeAccessBase.sol new file mode 100644 index 00000000..33dbfd7c --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/codeAccessBase.sol @@ -0,0 +1,22 @@ +contract Base { + function f() public pure returns (uint) {} +} +contract Test is Base { + function creation() public pure returns (bytes memory) { + return type(Test).creationCode; + } + function runtime() public pure returns (bytes memory) { + return type(Test).runtimeCode; + } + function creationBase() public pure returns (bytes memory) { + return type(Base).creationCode; + } + function runtimeBase() public pure returns (bytes memory) { + return type(Base).runtimeCode; + } +} +// ---- +// TypeError: (165-188): Circular reference for contract code access. +// TypeError: (271-293): Circular reference for contract code access. +// TypeError: (381-404): Circular reference for contract code access. +// TypeError: (491-513): Circular reference for contract code access. diff --git a/test/libsolidity/syntaxTests/metaTypes/codeAccessCyclic.sol b/test/libsolidity/syntaxTests/metaTypes/codeAccessCyclic.sol new file mode 100644 index 00000000..d5723df6 --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/codeAccessCyclic.sol @@ -0,0 +1,12 @@ +contract A { + function f() public pure { + type(B).runtimeCode; + } +} +contract B { + function f() public pure { + type(A).runtimeCode; + } +} +// ---- +// TypeError: (133-152): Circular reference for contract code access. diff --git a/test/libsolidity/syntaxTests/metaTypes/codeAccessIsConstant.sol b/test/libsolidity/syntaxTests/metaTypes/codeAccessIsConstant.sol new file mode 100644 index 00000000..cda5d5c3 --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/codeAccessIsConstant.sol @@ -0,0 +1,7 @@ +contract Test { + bytes constant c = type(B).creationCode; + bytes constant r = type(B).runtimeCode; + +} +contract B { function f() public pure {} } +// ---- diff --git a/test/libsolidity/syntaxTests/metaTypes/codeAccessLibrary.sol b/test/libsolidity/syntaxTests/metaTypes/codeAccessLibrary.sol new file mode 100644 index 00000000..f746dc35 --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/codeAccessLibrary.sol @@ -0,0 +1,12 @@ +contract Test { + function creationOther() public pure returns (bytes memory) { + return type(Library).creationCode; + } + function runtime() public pure returns (bytes memory) { + return type(Library).runtimeCode; + } +} +contract Library { + function f(uint) public pure returns (uint) {} +} +// ---- diff --git a/test/libsolidity/syntaxTests/metaTypes/codeIsNoLValue.sol b/test/libsolidity/syntaxTests/metaTypes/codeIsNoLValue.sol new file mode 100644 index 00000000..022e4d6f --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/codeIsNoLValue.sol @@ -0,0 +1,10 @@ +contract Test { + function f() public pure { + type(C).creationCode = new bytes(6); + type(C).runtimeCode = new bytes(6); + } +} +contract C {} +// ---- +// TypeError: (55-75): Expression has to be an lvalue. +// TypeError: (100-119): Expression has to be an lvalue. diff --git a/test/libsolidity/syntaxTests/metaTypes/noArgForType.sol b/test/libsolidity/syntaxTests/metaTypes/noArgForType.sol new file mode 100644 index 00000000..542aaf53 --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/noArgForType.sol @@ -0,0 +1,7 @@ +contract Test { + function creation() public pure returns (bytes memory) { + type(); + } +} +// ---- +// TypeError: (85-91): This function takes one argument, but 0 were provided. diff --git a/test/libsolidity/syntaxTests/metaTypes/runtimeCodeWarningAssembly.sol b/test/libsolidity/syntaxTests/metaTypes/runtimeCodeWarningAssembly.sol new file mode 100644 index 00000000..ec8d9784 --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/runtimeCodeWarningAssembly.sol @@ -0,0 +1,17 @@ +contract Test { + function f() public pure returns (uint) { + return type(C).runtimeCode.length + + type(D).runtimeCode.length + + type(C).creationCode.length + + type(D).creationCode.length; + } +} +contract C { + constructor() public { assembly {} } +} +contract D is C { + constructor() public {} +} +// ---- +// Warning: (77-96): The constructor of the contract (or its base) uses inline assembly. Because of that, it might be that the deployed bytecode is different from type(...).runtimeCode. +// Warning: (118-137): The constructor of the contract (or its base) uses inline assembly. Because of that, it might be that the deployed bytecode is different from type(...).runtimeCode. diff --git a/test/libsolidity/syntaxTests/metaTypes/tooManyArgsForType.sol b/test/libsolidity/syntaxTests/metaTypes/tooManyArgsForType.sol new file mode 100644 index 00000000..61c2b779 --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/tooManyArgsForType.sol @@ -0,0 +1,7 @@ +contract Test { + function creation() public pure returns (bytes memory) { + type(1, 2); + } +} +// ---- +// TypeError: (85-95): This function takes one argument, but 2 were provided. diff --git a/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierContractName.sol b/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierContractName.sol new file mode 100644 index 00000000..144ca1c3 --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierContractName.sol @@ -0,0 +1,3 @@ +contract type { } +// ---- +// ParserError: (9-13): Expected identifier but got 'type' diff --git a/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierFunction.sol b/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierFunction.sol new file mode 100644 index 00000000..b7881f15 --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierFunction.sol @@ -0,0 +1,6 @@ +contract Test { + function type() public pure { + } +} +// ---- +// ParserError: (29-33): Expected identifier but got 'type' diff --git a/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierParameter.sol b/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierParameter.sol new file mode 100644 index 00000000..001ba840 --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierParameter.sol @@ -0,0 +1,6 @@ +contract Test { + function f(uint type) public pure { + } +} +// ---- +// ParserError: (36-40): Expected ',' but got 'type' diff --git a/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierStateVariable.sol b/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierStateVariable.sol new file mode 100644 index 00000000..fa827a33 --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierStateVariable.sol @@ -0,0 +1,5 @@ +contract Test { + uint type; +} +// ---- +// ParserError: (25-29): Expected identifier but got 'type' diff --git a/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierVariable.sol b/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierVariable.sol new file mode 100644 index 00000000..fa57698d --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/typeNotRegularIdentifierVariable.sol @@ -0,0 +1,7 @@ +contract Test { + function f() public pure { + uint type; + } +} +// ---- +// ParserError: (60-64): Expected ';' but got 'type' diff --git a/test/libsolidity/syntaxTests/metaTypes/typeOfContract.sol b/test/libsolidity/syntaxTests/metaTypes/typeOfContract.sol new file mode 100644 index 00000000..036c36e8 --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/typeOfContract.sol @@ -0,0 +1,6 @@ +contract Test { + function f() public pure returns (bytes memory) { + type(Test); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/metaTypes/typeRecursive.sol b/test/libsolidity/syntaxTests/metaTypes/typeRecursive.sol new file mode 100644 index 00000000..0ce06786 --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/typeRecursive.sol @@ -0,0 +1,8 @@ +contract Test { + function f() public pure { + type(type(type(Test))); + } +} +// ---- +// TypeError: (65-75): Invalid type for argument in function call. Contract type required, but type(contract Test) provided. +// TypeError: (60-76): Invalid type for argument in function call. Contract type required, but tuple() provided. diff --git a/test/libsolidity/syntaxTests/metaTypes/unsupportedArgForType.sol b/test/libsolidity/syntaxTests/metaTypes/unsupportedArgForType.sol new file mode 100644 index 00000000..5c27d42f --- /dev/null +++ b/test/libsolidity/syntaxTests/metaTypes/unsupportedArgForType.sol @@ -0,0 +1,9 @@ +contract Test { + struct S { uint x; } + function f() public pure { + // Unsupported for now, but might be supported in the future + type(S); + } +} +// ---- +// TypeError: (154-155): Invalid type for argument in function call. Contract type required, but type(struct Test.S) provided. diff --git a/test/libsolidity/syntaxTests/types/encoding_fractional.sol b/test/libsolidity/syntaxTests/types/encoding_fractional.sol new file mode 100644 index 00000000..16c76d9a --- /dev/null +++ b/test/libsolidity/syntaxTests/types/encoding_fractional.sol @@ -0,0 +1,7 @@ +contract C { + function f1() public pure returns (bytes memory) { + return abi.encode(0.1, 1); + } +} +// ---- +// TypeError: (92-95): Fractional numbers cannot yet be encoded. diff --git a/test/libsolidity/syntaxTests/types/encoding_fractional_abiencoderv2.sol b/test/libsolidity/syntaxTests/types/encoding_fractional_abiencoderv2.sol new file mode 100644 index 00000000..80efa9c5 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/encoding_fractional_abiencoderv2.sol @@ -0,0 +1,9 @@ +pragma experimental ABIEncoderV2; +contract C { + function f1() public pure returns (bytes memory) { + return abi.encode(0.1, 1); + } +} +// ---- +// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments. +// TypeError: (126-129): Fractional numbers cannot yet be encoded. diff --git a/test/libsolidity/syntaxTests/types/encoding_packed_fractional.sol b/test/libsolidity/syntaxTests/types/encoding_packed_fractional.sol new file mode 100644 index 00000000..71080cb0 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/encoding_packed_fractional.sol @@ -0,0 +1,8 @@ +contract C { + function f1() public pure returns (bytes memory) { + return abi.encodePacked(0.1, 1); + } +} +// ---- +// TypeError: (98-101): Fractional numbers cannot yet be encoded. +// TypeError: (103-104): Cannot perform packed encoding for a literal. Please convert it to an explicit type first. diff --git a/test/libsolidity/syntaxTests/types/encoding_packed_fractional_abiencoderv2.sol b/test/libsolidity/syntaxTests/types/encoding_packed_fractional_abiencoderv2.sol new file mode 100644 index 00000000..e82c3c9a --- /dev/null +++ b/test/libsolidity/syntaxTests/types/encoding_packed_fractional_abiencoderv2.sol @@ -0,0 +1,10 @@ +pragma experimental ABIEncoderV2; +contract C { + function f1() public pure returns (bytes memory) { + return abi.encodePacked(0.1, 1); + } +} +// ---- +// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments. +// TypeError: (132-135): Fractional numbers cannot yet be encoded. +// TypeError: (137-138): Cannot perform packed encoding for a literal. Please convert it to an explicit type first. diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp index 59cde4fe..306721a0 100644 --- a/test/libyul/YulOptimizerTest.cpp +++ b/test/libyul/YulOptimizerTest.cpp @@ -38,6 +38,7 @@ #include <libyul/optimiser/ExpressionSimplifier.h> #include <libyul/optimiser/UnusedPruner.h> #include <libyul/optimiser/ExpressionJoiner.h> +#include <libyul/optimiser/SSAReverser.h> #include <libyul/optimiser/SSATransform.h> #include <libyul/optimiser/RedundantAssignEliminator.h> #include <libyul/optimiser/StructuralSimplifier.h> @@ -223,6 +224,23 @@ bool YulOptimizerTest::run(ostream& _stream, string const& _linePrefix, bool con disambiguate(); EquivalentFunctionCombiner::run(*m_ast); } + else if (m_optimizerStep == "ssaReverser") + { + disambiguate(); + SSAReverser::run(*m_ast); + } + else if (m_optimizerStep == "ssaAndBack") + { + disambiguate(); + // apply SSA + NameDispenser nameDispenser{*m_dialect, *m_ast}; + SSATransform::run(*m_ast, nameDispenser); + RedundantAssignEliminator::run(*m_dialect, *m_ast); + // reverse SSA + SSAReverser::run(*m_ast); + CommonSubexpressionEliminator{*m_dialect}(*m_ast); + UnusedPruner::runUntilStabilised(*m_dialect, *m_ast); + } else if (m_optimizerStep == "fullSuite") OptimiserSuite::run(*m_dialect, *m_ast, *m_analysisInfo); else diff --git a/test/libyul/yulOptimizerTests/fullSuite/abi2.yul b/test/libyul/yulOptimizerTests/fullSuite/abi2.yul index 887399b6..41a52c67 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/abi2.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/abi2.yul @@ -1073,12 +1073,12 @@ // fullSuite // { // let _2 := mload(1) -// let _172 := mload(0) -// if slt(sub(_2, _172), 64) +// let _153 := mload(0) +// if slt(sub(_2, _153), 64) // { // revert(0, 0) // } -// sstore(0, and(calldataload(_172), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) +// sstore(0, and(calldataload(_153), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) // let x0, x1, x2, x3, x4 := abi_decode_tuple_t_addresst_uint256t_bytes_calldata_ptrt_enum$_Operation_$1949(mload(7), mload(8)) // sstore(x1, x0) // sstore(x3, x2) @@ -1093,40 +1093,40 @@ // value0_57 := and(calldataload(add(headStart_55, value4)), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) // value1_58 := calldataload(add(headStart_55, 32)) // let offset_62 := calldataload(add(headStart_55, 64)) -// let _220 := 0xffffffffffffffff -// if gt(offset_62, _220) +// let _201 := 0xffffffffffffffff +// if gt(offset_62, _201) // { // revert(value4, value4) // } -// let _222 := add(headStart_55, offset_62) -// if iszero(slt(add(_222, 0x1f), dataEnd_56)) +// let _203 := add(headStart_55, offset_62) +// if iszero(slt(add(_203, 0x1f), dataEnd_56)) // { // revert(value4, value4) // } -// let abi_decode_length_15_116 := calldataload(_222) -// if gt(abi_decode_length_15_116, _220) +// let abi_decode_length_15_246 := calldataload(_203) +// if gt(abi_decode_length_15_246, _201) // { // revert(value4, value4) // } -// if gt(add(add(_222, abi_decode_length_15_116), 32), dataEnd_56) +// if gt(add(add(_203, abi_decode_length_15_246), 32), dataEnd_56) // { // revert(value4, value4) // } -// value2_59 := add(_222, 32) -// value3 := abi_decode_length_15_116 -// let _225 := calldataload(add(headStart_55, 96)) -// if iszero(lt(_225, 3)) +// value2_59 := add(_203, 32) +// value3 := abi_decode_length_15_246 +// let _206 := calldataload(add(headStart_55, 96)) +// if iszero(lt(_206, 3)) // { // revert(value4, value4) // } -// value4 := _225 +// value4 := _206 // } // function abi_encode_tuple_t_bytes32_t_address_t_uint256_t_bytes32_t_enum$_Operation_$1949_t_uint256_t_uint256_t_uint256_t_address_t_address_t_uint256__to_t_bytes32_t_address_t_uint256_t_bytes32_t_uint8_t_uint256_t_uint256_t_uint256_t_address_t_address_t_uint256_(headStart_252, value10_253, value9_254, value8_255, value7_256, value6_257, value5_258, value4_259, value3_260, value2_261, value1_262, value0_263) -> tail_264 // { // tail_264 := add(headStart_252, 352) // mstore(headStart_252, value0_263) -// let _439 := 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -// mstore(add(headStart_252, 32), and(value1_262, _439)) +// let _413 := 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +// mstore(add(headStart_252, 32), and(value1_262, _413)) // mstore(add(headStart_252, 64), value2_261) // mstore(add(headStart_252, 96), value3_260) // if iszero(lt(value4_259, 3)) @@ -1137,8 +1137,8 @@ // mstore(add(headStart_252, 160), value5_258) // mstore(add(headStart_252, 192), value6_257) // mstore(add(headStart_252, 224), value7_256) -// mstore(add(headStart_252, 256), and(value8_255, _439)) -// mstore(add(headStart_252, 288), and(value9_254, _439)) +// mstore(add(headStart_252, 256), and(value8_255, _413)) +// mstore(add(headStart_252, 288), and(value9_254, _413)) // mstore(add(headStart_252, 320), value10_253) // } // } diff --git a/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul b/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul index a8cac6c6..d38025ae 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul @@ -460,12 +460,12 @@ // { // let _1 := 0x20 // let _2 := 0 -// let _268 := mload(_2) +// let _218 := mload(_2) // let abi_encode_pos := _1 -// let abi_encode_length_68 := mload(_268) +// let abi_encode_length_68 := mload(_218) // mstore(_1, abi_encode_length_68) // abi_encode_pos := 64 -// let abi_encode_srcPtr := add(_268, _1) +// let abi_encode_srcPtr := add(_218, _1) // let abi_encode_i_69 := _2 // for { // } @@ -474,66 +474,64 @@ // abi_encode_i_69 := add(abi_encode_i_69, 1) // } // { -// let _668 := mload(abi_encode_srcPtr) -// let abi_encode_pos_71_760 := abi_encode_pos -// let abi_encode_srcPtr_73_762 := _668 -// let abi_encode_i_74_763 := _2 +// let _580 := mload(abi_encode_srcPtr) +// let abi_encode_pos_71_672 := abi_encode_pos +// let abi_encode_srcPtr_73_674 := _580 +// let abi_encode_i_74_675 := _2 // for { // } -// lt(abi_encode_i_74_763, 0x3) +// lt(abi_encode_i_74_675, 0x3) // { -// abi_encode_i_74_763 := add(abi_encode_i_74_763, 1) +// abi_encode_i_74_675 := add(abi_encode_i_74_675, 1) // } // { -// mstore(abi_encode_pos_71_760, and(mload(abi_encode_srcPtr_73_762), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) -// abi_encode_srcPtr_73_762 := add(abi_encode_srcPtr_73_762, _1) -// abi_encode_pos_71_760 := add(abi_encode_pos_71_760, _1) +// mstore(abi_encode_pos_71_672, and(mload(abi_encode_srcPtr_73_674), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) +// abi_encode_srcPtr_73_674 := add(abi_encode_srcPtr_73_674, _1) +// abi_encode_pos_71_672 := add(abi_encode_pos_71_672, _1) // } // abi_encode_srcPtr := add(abi_encode_srcPtr, _1) // abi_encode_pos := add(abi_encode_pos, 0x60) // } -// let _270 := mload(64) -// let _271 := mload(_1) -// if slt(sub(_270, _271), 128) +// let _220 := mload(64) +// let _221 := mload(_1) +// if slt(sub(_220, _221), 128) // { // revert(_2, _2) // } -// let abi_decode_offset_64 := calldataload(add(_271, 64)) +// let abi_decode_offset_64 := calldataload(add(_221, 64)) // let abi_decode__74 := 0xffffffffffffffff // if gt(abi_decode_offset_64, abi_decode__74) // { // revert(_2, _2) // } -// let abi_decode_value2_367 := abi_decode_t_array$_t_uint256_$dyn_memory_ptr(add(_271, abi_decode_offset_64), _270) -// let abi_decode_offset_65 := calldataload(add(_271, 96)) +// let abi_decode_value2_317 := abi_decode_t_array$_t_uint256_$dyn_memory_ptr(add(_221, abi_decode_offset_64), _220) +// let abi_decode_offset_65 := calldataload(add(_221, 96)) // if gt(abi_decode_offset_65, abi_decode__74) // { // revert(_2, _2) // } -// let abi_decode_value3_368 := abi_decode_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(add(_271, abi_decode_offset_65), _270) -// sstore(calldataload(_271), calldataload(add(_271, _1))) -// sstore(abi_decode_value2_367, abi_decode_value3_368) +// let abi_decode_value3_318 := abi_decode_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(add(_221, abi_decode_offset_65), _220) +// sstore(calldataload(_221), calldataload(add(_221, _1))) +// sstore(abi_decode_value2_317, abi_decode_value3_318) // sstore(_2, abi_encode_pos) // function abi_decode_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(offset_3, end_4) -> array_5 // { // if iszero(slt(add(offset_3, 0x1f), end_4)) // { -// revert(0, 0) +// revert(array_5, array_5) // } // let length_6 := calldataload(offset_3) -// let array_5_115 := allocateMemory(array_allocation_size_t_array$_t_address_$dyn_memory(length_6)) -// array_5 := array_5_115 -// let dst_7 := array_5_115 -// mstore(array_5_115, length_6) +// array_5 := allocateMemory(array_allocation_size_t_array$_t_address_$dyn_memory(length_6)) +// let dst_7 := array_5 +// mstore(array_5, length_6) // let _16 := 0x20 -// dst_7 := add(array_5_115, _16) +// dst_7 := add(array_5, _16) // let src_8 := add(offset_3, _16) // if gt(add(add(offset_3, mul(length_6, 0x40)), _16), end_4) // { // revert(0, 0) // } -// let i_9_346 := 0 -// let i_9 := i_9_346 +// let i_9 := 0 // for { // } // lt(i_9, length_6) @@ -543,17 +541,17 @@ // { // if iszero(slt(add(src_8, 0x1f), end_4)) // { -// revert(i_9_346, i_9_346) +// revert(0, 0) // } -// let abi_decode_array_13_124 := allocateMemory(array_allocation_size_t_array$_t_uint256_$2_memory(0x2)) -// let abi_decode_dst_15 := abi_decode_array_13_124 +// let abi_decode_dst_15 := allocateMemory(array_allocation_size_t_array$_t_uint256_$2_memory(0x2)) +// let abi_decode_dst_15_1155 := abi_decode_dst_15 // let abi_decode_src_16 := src_8 -// let abi_decode__289 := add(src_8, 0x40) -// if gt(abi_decode__289, end_4) +// let abi_decode__239 := add(src_8, 0x40) +// if gt(abi_decode__239, end_4) // { -// revert(i_9_346, i_9_346) +// revert(0, 0) // } -// let abi_decode_i_17 := i_9_346 +// let abi_decode_i_17 := 0 // for { // } // lt(abi_decode_i_17, 0x2) @@ -565,24 +563,23 @@ // abi_decode_dst_15 := add(abi_decode_dst_15, _16) // abi_decode_src_16 := add(abi_decode_src_16, _16) // } -// mstore(dst_7, abi_decode_array_13_124) +// mstore(dst_7, abi_decode_dst_15_1155) // dst_7 := add(dst_7, _16) -// src_8 := abi_decode__289 +// src_8 := abi_decode__239 // } // } // function abi_decode_t_array$_t_uint256_$dyn_memory_ptr(offset_27, end_28) -> array_29 // { // if iszero(slt(add(offset_27, 0x1f), end_28)) // { -// revert(0, 0) +// revert(array_29, array_29) // } // let length_30 := calldataload(offset_27) -// let array_29_131 := allocateMemory(array_allocation_size_t_array$_t_address_$dyn_memory(length_30)) -// array_29 := array_29_131 -// let dst_31 := array_29_131 -// mstore(array_29_131, length_30) +// array_29 := allocateMemory(array_allocation_size_t_array$_t_address_$dyn_memory(length_30)) +// let dst_31 := array_29 +// mstore(array_29, length_30) // let _52 := 0x20 -// dst_31 := add(array_29_131, _52) +// dst_31 := add(array_29, _52) // let src_32 := add(offset_27, _52) // if gt(add(add(offset_27, mul(length_30, _52)), _52), end_28) // { @@ -603,10 +600,9 @@ // } // function allocateMemory(size) -> memPtr // { -// let memPtr_157 := mload(64) -// memPtr := memPtr_157 -// let newFreePtr := add(memPtr_157, size) -// if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr_157)) +// memPtr := mload(64) +// let newFreePtr := add(memPtr, size) +// if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) // { // revert(0, 0) // } diff --git a/test/libyul/yulOptimizerTests/fullSuite/aztec.yul b/test/libyul/yulOptimizerTests/fullSuite/aztec.yul index fc09b5d9..868c6b01 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/aztec.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/aztec.yul @@ -245,17 +245,16 @@ // mstore(0x00, 404) // revert(0x00, 0x20) // } -// let validateJo_kn_287 := calldataload(add(calldatasize(), 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff40)) -// let validateJo_kn := validateJo_kn_287 +// let validateJo_kn := calldataload(add(calldatasize(), 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff40)) // let validateJo__24 := 0x2a0 // mstore(validateJo__24, caller()) -// mstore(0x2c0, validateJo_kn_287) +// mstore(0x2c0, validateJo_kn) // mstore(0x2e0, validateJo_m) -// validateJo_kn := mulmod(sub(validateJo_gen_order, validateJo_kn_287), validateJo_challenge, validateJo_gen_order) +// validateJo_kn := mulmod(sub(validateJo_gen_order, validateJo_kn), validateJo_challenge, validateJo_gen_order) // hashCommitments(validateJo_notes, validateJo_n) // let validateJo_b := add(0x300, mul(validateJo_n, validateJo__6)) -// let validateJo_i_290 := 0 -// let validateJo_i := validateJo_i_290 +// let validateJo_i := 0 +// let validateJo_i_1306 := validateJo_i // for { // } // lt(validateJo_i, validateJo_n) @@ -264,11 +263,10 @@ // } // { // let validateJo__34 := 0x20 -// let validateJo__373 := add(validateJo__10, mul(validateJo_i, 0xc0)) -// let validateJo_noteIndex := add(validateJo__373, 0x24) -// let validateJo_k := validateJo_i_290 -// let validateJo_a_292 := calldataload(add(validateJo__373, 0x44)) -// let validateJo_a := validateJo_a_292 +// let validateJo__351 := add(validateJo__10, mul(validateJo_i, 0xc0)) +// let validateJo_noteIndex := add(validateJo__351, 0x24) +// let validateJo_k := validateJo_i_1306 +// let validateJo_a := calldataload(add(validateJo__351, 0x44)) // let validateJo_c := validateJo_challenge // let validateJo__39 := add(validateJo_i, 0x01) // switch eq(validateJo__39, validateJo_n) @@ -282,36 +280,34 @@ // case 0 { // validateJo_k := calldataload(validateJo_noteIndex) // } -// validateCommitment(validateJo_noteIndex, validateJo_k, validateJo_a_292) +// validateCommitment(validateJo_noteIndex, validateJo_k, validateJo_a) // switch gt(validateJo__39, validateJo_m) // case 1 { // validateJo_kn := addmod(validateJo_kn, sub(validateJo_gen_order, validateJo_k), validateJo_gen_order) -// let validateJo_x := mod(mload(validateJo_i_290), validateJo_gen_order) +// let validateJo_x := mod(mload(validateJo_i_1306), validateJo_gen_order) // validateJo_k := mulmod(validateJo_k, validateJo_x, validateJo_gen_order) -// validateJo_a := mulmod(validateJo_a_292, validateJo_x, validateJo_gen_order) +// validateJo_a := mulmod(validateJo_a, validateJo_x, validateJo_gen_order) // validateJo_c := mulmod(validateJo_challenge, validateJo_x, validateJo_gen_order) -// mstore(validateJo_i_290, keccak256(validateJo_i_290, validateJo__34)) +// mstore(validateJo_i_1306, keccak256(validateJo_i_1306, validateJo__34)) // } // case 0 { // validateJo_kn := addmod(validateJo_kn, validateJo_k, validateJo_gen_order) // } // let validateJo__52 := 0x40 -// calldatacopy(0xe0, add(validateJo__373, 164), validateJo__52) -// calldatacopy(validateJo__34, add(validateJo__373, 100), validateJo__52) +// calldatacopy(0xe0, add(validateJo__351, 164), validateJo__52) +// calldatacopy(validateJo__34, add(validateJo__351, 100), validateJo__52) // let validateJo__61 := 0x120 // mstore(validateJo__61, sub(validateJo_gen_order, validateJo_c)) // let validateJo__62 := 0x60 // mstore(validateJo__62, validateJo_k) // mstore(0xc0, validateJo_a) // let validateJo__65 := 0x1a0 -// let validateJo_result_302 := call(gas(), 7, validateJo_i_290, 0xe0, validateJo__62, validateJo__65, validateJo__52) -// let validateJo_result := validateJo_result_302 -// let validateJo_result_303 := and(validateJo_result_302, call(gas(), 7, validateJo_i_290, validateJo__34, validateJo__62, validateJo__61, validateJo__52)) +// let validateJo_result := call(gas(), 7, validateJo_i_1306, 0xe0, validateJo__62, validateJo__65, validateJo__52) +// let validateJo_result_303 := and(validateJo_result, call(gas(), 7, validateJo_i_1306, validateJo__34, validateJo__62, validateJo__61, validateJo__52)) // let validateJo__80 := 0x160 -// let validateJo_result_304 := and(validateJo_result_303, call(gas(), 7, validateJo_i_290, validateJo__6, validateJo__62, validateJo__80, validateJo__52)) -// let validateJo_result_305 := and(validateJo_result_304, call(gas(), 6, validateJo_i_290, validateJo__61, validateJo__6, validateJo__80, validateJo__52)) -// let validateJo_result_306 := and(validateJo_result_305, call(gas(), 6, validateJo_i_290, validateJo__80, validateJo__6, validateJo_b, validateJo__52)) -// validateJo_result := validateJo_result_306 +// let validateJo_result_304 := and(validateJo_result_303, call(gas(), 7, validateJo_i_1306, validateJo__6, validateJo__62, validateJo__80, validateJo__52)) +// let validateJo_result_305 := and(validateJo_result_304, call(gas(), 6, validateJo_i_1306, validateJo__61, validateJo__6, validateJo__80, validateJo__52)) +// validateJo_result := and(validateJo_result_305, call(gas(), 6, validateJo_i_1306, validateJo__80, validateJo__6, validateJo_b, validateJo__52)) // if eq(validateJo_i, validateJo_m) // { // mstore(0x260, mload(validateJo__34)) @@ -323,14 +319,14 @@ // { // mstore(validateJo__62, validateJo_c) // let validateJo__120 := 0x220 -// let validateJo_result_307 := and(validateJo_result_306, call(gas(), 7, validateJo_i_290, validateJo__34, validateJo__62, validateJo__120, validateJo__52)) -// let validateJo_result_308 := and(validateJo_result_307, call(gas(), 6, validateJo_i_290, validateJo__120, validateJo__6, 0x260, validateJo__52)) -// validateJo_result := and(validateJo_result_308, call(gas(), 6, validateJo_i_290, validateJo__65, validateJo__6, 0x1e0, validateJo__52)) +// let validateJo_result_307 := and(validateJo_result, call(gas(), 7, validateJo_i_1306, validateJo__34, validateJo__62, validateJo__120, validateJo__52)) +// let validateJo_result_308 := and(validateJo_result_307, call(gas(), 6, validateJo_i_1306, validateJo__120, validateJo__6, 0x260, validateJo__52)) +// validateJo_result := and(validateJo_result_308, call(gas(), 6, validateJo_i_1306, validateJo__65, validateJo__6, 0x1e0, validateJo__52)) // } // if iszero(validateJo_result) // { -// mstore(validateJo_i_290, 400) -// revert(validateJo_i_290, validateJo__34) +// mstore(validateJo_i_1306, 400) +// revert(validateJo_i_1306, validateJo__34) // } // validateJo_b := add(validateJo_b, validateJo__52) // } @@ -340,13 +336,13 @@ // } // if iszero(eq(mod(keccak256(validateJo__24, add(validateJo_b, 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd60)), validateJo_gen_order), validateJo_challenge)) // { -// mstore(validateJo_i_290, 404) -// revert(validateJo_i_290, 0x20) +// mstore(validateJo_i_1306, 404) +// revert(validateJo_i_1306, 0x20) // } -// mstore(validateJo_i_290, 0x01) -// return(validateJo_i_290, 0x20) -// mstore(validateJo_i_290, 404) -// revert(validateJo_i_290, 0x20) +// mstore(validateJo_i_1306, 0x01) +// return(validateJo_i_1306, 0x20) +// mstore(validateJo_i_1306, 404) +// revert(validateJo_i_1306, 0x20) // function validatePairing(t2) // { // let t2_x_1 := calldataload(t2) diff --git a/test/libyul/yulOptimizerTests/fullSuite/medium.yul b/test/libyul/yulOptimizerTests/fullSuite/medium.yul index b10c6c69..1d07cd03 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/medium.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/medium.yul @@ -21,8 +21,8 @@ // { // let allocate__19 := 0x40 // mstore(allocate__19, add(mload(allocate__19), 0x20)) -// let allocate_p_24_41 := mload(allocate__19) -// mstore(allocate__19, add(allocate_p_24_41, allocate__19)) -// mstore(add(allocate_p_24_41, 96), 2) +// let allocate_p_35_39 := mload(allocate__19) +// mstore(allocate__19, add(allocate_p_35_39, allocate__19)) +// mstore(add(allocate_p_35_39, 96), 2) // mstore(allocate__19, 0x20) // } diff --git a/test/libyul/yulOptimizerTests/fullSuite/ssaReverse.yul b/test/libyul/yulOptimizerTests/fullSuite/ssaReverse.yul new file mode 100644 index 00000000..204b444e --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/ssaReverse.yul @@ -0,0 +1,62 @@ +{ + // This is an abi decode function with the SSA transform applied once. + // This test is supposed to verify that the SSA transform is correctly reversed by the full suite. + function abi_decode_t_bytes_calldata_ptr(offset_12, end_13) -> arrayPos_14, length_15 + { + if iszero(slt(add(offset_12, 0x1f), end_13)) + { + revert(0, 0) + } + let length_15_1 := calldataload(offset_12) + length_15 := length_15_1 + if gt(length_15_1, 0xffffffffffffffff) + { + revert(0, 0) + } + let arrayPos_14_2 := add(offset_12, 0x20) + arrayPos_14 := arrayPos_14_2 + if gt(add(arrayPos_14_2, mul(length_15_1, 0x1)), end_13) + { + revert(0, 0) + } + } + + // prevent inlining + let a,b := abi_decode_t_bytes_calldata_ptr(mload(0),mload(1)) + a,b := abi_decode_t_bytes_calldata_ptr(a,b) + a,b := abi_decode_t_bytes_calldata_ptr(a,b) + a,b := abi_decode_t_bytes_calldata_ptr(a,b) + a,b := abi_decode_t_bytes_calldata_ptr(a,b) + a,b := abi_decode_t_bytes_calldata_ptr(a,b) + a,b := abi_decode_t_bytes_calldata_ptr(a,b) + mstore(a,b) +} +// ---- +// fullSuite +// { +// let a, b := abi_decode_t_bytes_calldata_ptr(mload(0), mload(1)) +// a, b := abi_decode_t_bytes_calldata_ptr(a, b) +// a, b := abi_decode_t_bytes_calldata_ptr(a, b) +// a, b := abi_decode_t_bytes_calldata_ptr(a, b) +// a, b := abi_decode_t_bytes_calldata_ptr(a, b) +// a, b := abi_decode_t_bytes_calldata_ptr(a, b) +// a, b := abi_decode_t_bytes_calldata_ptr(a, b) +// mstore(a, b) +// function abi_decode_t_bytes_calldata_ptr(offset_12, end_13) -> arrayPos_14, length_15 +// { +// if iszero(slt(add(offset_12, 0x1f), end_13)) +// { +// revert(0, 0) +// } +// length_15 := calldataload(offset_12) +// if gt(length_15, 0xffffffffffffffff) +// { +// revert(0, 0) +// } +// arrayPos_14 := add(offset_12, 0x20) +// if gt(add(add(offset_12, length_15), 0x20), end_13) +// { +// revert(0, 0) +// } +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullSuite/ssaReverseComplex.yul b/test/libyul/yulOptimizerTests/fullSuite/ssaReverseComplex.yul new file mode 100644 index 00000000..2e178f31 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/ssaReverseComplex.yul @@ -0,0 +1,23 @@ +{ + let a := mload(0) + let b := mload(1) + if mload(2) { + a := mload(b) + b := mload(a) + a := mload(b) + b := mload(a) + } + mstore(a, b) +} +// ---- +// fullSuite +// { +// let a := mload(0) +// let b := mload(1) +// if mload(2) +// { +// a := mload(mload(mload(b))) +// b := mload(a) +// } +// mstore(a, b) +// } diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/for_loop.yul b/test/libyul/yulOptimizerTests/ssaAndBack/for_loop.yul new file mode 100644 index 00000000..18498e61 --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaAndBack/for_loop.yul @@ -0,0 +1,36 @@ +{ + for { + let a := mload(0) + let b := mload(1) + } + lt(mload(a),mload(b)) + { + a := mload(b) + } + { + b := mload(a) + a := mload(b) + a := mload(b) + a := mload(b) + b := mload(a) + } +} +// ---- +// ssaAndBack +// { +// for { +// let a := mload(0) +// let b := mload(1) +// } +// lt(mload(a), mload(b)) +// { +// a := mload(b) +// } +// { +// let b_3 := mload(a) +// pop(mload(b_3)) +// pop(mload(b_3)) +// let a_6 := mload(b_3) +// b := mload(a_6) +// } +// } diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign.yul b/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign.yul new file mode 100644 index 00000000..23c433d1 --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign.yul @@ -0,0 +1,18 @@ +{ + let a := mload(0) + a := mload(1) + a := mload(2) + a := mload(3) + a := mload(4) + mstore(a, 0) +} +// ---- +// ssaAndBack +// { +// pop(mload(0)) +// pop(mload(1)) +// pop(mload(2)) +// pop(mload(3)) +// let a_5 := mload(4) +// mstore(a_5, 0) +// } diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_if.yul b/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_if.yul new file mode 100644 index 00000000..fd5981ef --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_if.yul @@ -0,0 +1,22 @@ +{ + let a := mload(0) + if mload(1) + { + a := mload(1) + a := mload(2) + a := mload(3) + } + mstore(a, 0) +} +// ---- +// ssaAndBack +// { +// let a := mload(0) +// if mload(1) +// { +// pop(mload(1)) +// pop(mload(2)) +// a := mload(3) +// } +// mstore(a, 0) +// } diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_multi_var_if.yul b/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_multi_var_if.yul new file mode 100644 index 00000000..b0b3efb5 --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_multi_var_if.yul @@ -0,0 +1,25 @@ +{ + let a := mload(0) + let b := mload(1) + if mload(2) { + a := mload(b) + b := mload(a) + a := mload(b) + b := mload(a) + } + mstore(a, b) +} +// ---- +// ssaAndBack +// { +// let a := mload(0) +// let b := mload(1) +// if mload(2) +// { +// let a_3 := mload(b) +// let b_4 := mload(a_3) +// a := mload(b_4) +// b := mload(a) +// } +// mstore(a, b) +// } diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_multi_var_switch.yul b/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_multi_var_switch.yul new file mode 100644 index 00000000..50f56b87 --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_multi_var_switch.yul @@ -0,0 +1,38 @@ +{ + let a := mload(0) + let b := mload(1) + switch mload(2) + case 0 { + a := mload(b) + b := mload(a) + a := mload(b) + b := mload(a) + } + default { + b := mload(a) + a := mload(b) + b := mload(a) + a := mload(b) + } + mstore(a, b) +} +// ---- +// ssaAndBack +// { +// let a := mload(0) +// let b := mload(1) +// switch mload(2) +// case 0 { +// let a_3 := mload(b) +// let b_4 := mload(a_3) +// a := mload(b_4) +// b := mload(a) +// } +// default { +// let b_7 := mload(a) +// let a_8 := mload(b_7) +// b := mload(a_8) +// a := mload(b) +// } +// mstore(a, b) +// } diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_switch.yul b/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_switch.yul new file mode 100644 index 00000000..1efbbde7 --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaAndBack/multi_assign_switch.yul @@ -0,0 +1,32 @@ +{ + let a := mload(0) + switch mload(1) + case 0 { + a := mload(1) + a := mload(2) + a := mload(3) + } + default { + a := mload(4) + a := mload(5) + a := mload(6) + } + mstore(a, 0) +} +// ---- +// ssaAndBack +// { +// let a := mload(0) +// switch mload(1) +// case 0 { +// pop(mload(1)) +// pop(mload(2)) +// a := mload(3) +// } +// default { +// pop(mload(4)) +// pop(mload(5)) +// a := mload(6) +// } +// mstore(a, 0) +// } diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/simple.yul b/test/libyul/yulOptimizerTests/ssaAndBack/simple.yul new file mode 100644 index 00000000..912940c5 --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaAndBack/simple.yul @@ -0,0 +1,12 @@ +{ + let a := mload(0) + a := mload(1) + mstore(a, 0) +} +// ---- +// ssaAndBack +// { +// pop(mload(0)) +// let a_2 := mload(1) +// mstore(a_2, 0) +// } diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/single_assign_if.yul b/test/libyul/yulOptimizerTests/ssaAndBack/single_assign_if.yul new file mode 100644 index 00000000..5185245c --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaAndBack/single_assign_if.yul @@ -0,0 +1,18 @@ +{ + let a := mload(0) + if mload(1) + { + a := mload(1) + } + mstore(a, 0) +} +// ---- +// ssaAndBack +// { +// let a := mload(0) +// if mload(1) +// { +// a := mload(1) +// } +// mstore(a, 0) +// } diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/single_assign_switch.yul b/test/libyul/yulOptimizerTests/ssaAndBack/single_assign_switch.yul new file mode 100644 index 00000000..e0e53b3f --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaAndBack/single_assign_switch.yul @@ -0,0 +1,24 @@ +{ + let a := mload(0) + switch mload(1) + case 0 { + a := mload(1) + } + default { + a := mload(2) + } + mstore(a, 0) +} +// ---- +// ssaAndBack +// { +// let a := mload(0) +// switch mload(1) +// case 0 { +// a := mload(1) +// } +// default { +// a := mload(2) +// } +// mstore(a, 0) +// } diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/ssaReverse.yul b/test/libyul/yulOptimizerTests/ssaAndBack/ssaReverse.yul new file mode 100644 index 00000000..d2ba6471 --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaAndBack/ssaReverse.yul @@ -0,0 +1,45 @@ +{ + function abi_decode_t_bytes_calldata_ptr(offset_12, end_13) -> arrayPos_14, length_15 + { + if iszero(slt(add(offset_12, 0x1f), end_13)) + { + revert(0, 0) + } + length_15 := calldataload(offset_12) + if gt(length_15, 0xffffffffffffffff) + { + revert(0, 0) + } + arrayPos_14 := add(offset_12, 0x20) + if gt(add(add(offset_12, length_15), 0x20), end_13) + { + revert(0, 0) + } + } + // prevent removal of the function + let a,b := abi_decode_t_bytes_calldata_ptr(mload(0),mload(1)) + mstore(a,b) +} +// ---- +// ssaAndBack +// { +// function abi_decode_t_bytes_calldata_ptr(offset_12, end_13) -> arrayPos_14, length_15 +// { +// if iszero(slt(add(offset_12, 0x1f), end_13)) +// { +// revert(arrayPos_14, arrayPos_14) +// } +// length_15 := calldataload(offset_12) +// if gt(length_15, 0xffffffffffffffff) +// { +// revert(arrayPos_14, arrayPos_14) +// } +// arrayPos_14 := add(offset_12, 0x20) +// if gt(add(add(offset_12, length_15), 0x20), end_13) +// { +// revert(0, 0) +// } +// } +// let a, b := abi_decode_t_bytes_calldata_ptr(mload(0), mload(1)) +// mstore(a, b) +// } diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/two_vars.yul b/test/libyul/yulOptimizerTests/ssaAndBack/two_vars.yul new file mode 100644 index 00000000..9f2a046e --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaAndBack/two_vars.yul @@ -0,0 +1,20 @@ +{ + let a := mload(0) + let b := mload(a) + a := mload(b) + b := mload(a) + a := mload(b) + b := mload(a) + mstore(a, b) +} +// ---- +// ssaAndBack +// { +// let a_1 := mload(0) +// let b_2 := mload(a_1) +// let a_3 := mload(b_2) +// let b_4 := mload(a_3) +// let a_5 := mload(b_4) +// let b_6 := mload(a_5) +// mstore(a_5, b_6) +// } diff --git a/test/libyul/yulOptimizerTests/ssaReverser/abi_example.yul b/test/libyul/yulOptimizerTests/ssaReverser/abi_example.yul new file mode 100644 index 00000000..923a42ba --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaReverser/abi_example.yul @@ -0,0 +1,44 @@ +{ + function abi_decode_t_bytes_calldata_ptr(offset_12, end_13) -> arrayPos_14, length_15 + { + if iszero(slt(add(offset_12, 0x1f), end_13)) + { + revert(0, 0) + } + let length_15_1 := calldataload(offset_12) + length_15 := length_15_1 + if gt(length_15_1, 0xffffffffffffffff) + { + revert(0, 0) + } + let arrayPos_14_2 := add(offset_12, 0x20) + arrayPos_14 := arrayPos_14_2 + if gt(add(arrayPos_14_2, mul(length_15_1, 0x1)), end_13) + { + revert(0, 0) + } + } +} +// ---- +// ssaReverser +// { +// function abi_decode_t_bytes_calldata_ptr(offset_12, end_13) -> arrayPos_14, length_15 +// { +// if iszero(slt(add(offset_12, 0x1f), end_13)) +// { +// revert(0, 0) +// } +// length_15 := calldataload(offset_12) +// let length_15_1 := length_15 +// if gt(length_15_1, 0xffffffffffffffff) +// { +// revert(0, 0) +// } +// arrayPos_14 := add(offset_12, 0x20) +// let arrayPos_14_2 := arrayPos_14 +// if gt(add(arrayPos_14_2, mul(length_15_1, 0x1)), end_13) +// { +// revert(0, 0) +// } +// } +// } diff --git a/test/libyul/yulOptimizerTests/ssaReverser/simple.yul b/test/libyul/yulOptimizerTests/ssaReverser/simple.yul new file mode 100644 index 00000000..eba1f5f1 --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaReverser/simple.yul @@ -0,0 +1,14 @@ +{ + let a := mload(1) + let a_1 := mload(0) + a := a_1 + mstore(a_1, 0) +} +// ---- +// ssaReverser +// { +// let a := mload(1) +// a := mload(0) +// let a_1 := a +// mstore(a_1, 0) +// } diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index efd1ba05..7203ef54 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -46,6 +46,7 @@ #include <libyul/optimiser/UnusedPruner.h> #include <libyul/optimiser/ExpressionJoiner.h> #include <libyul/optimiser/RedundantAssignEliminator.h> +#include <libyul/optimiser/SSAReverser.h> #include <libyul/optimiser/SSATransform.h> #include <libyul/optimiser/StructuralSimplifier.h> #include <libyul/optimiser/VarDeclInitializer.h> @@ -129,7 +130,7 @@ public: cout << "(q)quit/(f)flatten/(c)se/initialize var(d)ecls/(x)plit/(j)oin/(g)rouper/(h)oister/" << endl; cout << " (e)xpr inline/(i)nline/(s)implify/(u)nusedprune/ss(a) transform/" << endl; cout << " (r)edundant assign elim./re(m)aterializer/f(o)r-loop-pre-rewriter/" << endl; - cout << " s(t)ructural simplifier/equi(v)alent function combiner? " << endl; + cout << " s(t)ructural simplifier/equi(v)alent function combiner/ssa re(V)erser? " << endl; cout.flush(); int option = readStandardInputChar(); cout << ' ' << char(option) << endl; @@ -188,6 +189,9 @@ public: case 'v': EquivalentFunctionCombiner::run(*m_ast); break; + case 'V': + SSAReverser::run(*m_ast); + break; default: cout << "Unknown option." << endl; } |