diff --git a/CMakeLists.txt b/CMakeLists.txt index 05d41b59eba7..c3611e676ff7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,6 +36,7 @@ endif() option(SOLC_LINK_STATIC "Link solc executable statically on supported platforms" OFF) option(SOLC_STATIC_STDLIBS "Link solc against static versions of libgcc and libstdc++ on supported platforms" OFF) +option(SOLC_STRIP_SYMBOLS "Discard the symbol table from the solc executable at link time on supported platforms" OFF) option(STRICT_Z3_VERSION "Require the exact version of Z3 solver expected by our test suite." ON) option(PEDANTIC "Enable extra warnings and pedantic build flags. Treat all warnings as errors." ON) option(PROFILE_OPTIMIZER_STEPS "Output performance metrics for the optimiser steps." OFF) @@ -50,6 +51,11 @@ option( "Only build library targets that can be statically linked against. Do not build executables or tests." OFF ) +option( + USE_YULC + "Link solc against libyulc, the powdr-labs verified Yul -> EVM compiler, and enable --yul-backend=yulc." + OFF +) mark_as_advanced(PROFILE_OPTIMIZER_STEPS) mark_as_advanced(IGNORE_VENDORED_DEPENDENCIES) mark_as_advanced(ONLY_BUILD_SOLIDITY_LIBRARIES) @@ -72,6 +78,10 @@ endif() find_package(Threads) +if (USE_YULC) + include(yulc) +endif() + if(NOT PEDANTIC) message(WARNING "-- Pedantic build flags turned off. Warnings will not make compilation fail. This is NOT recommended in development builds.") endif() diff --git a/cmake/EthOptions.cmake b/cmake/EthOptions.cmake index 04808c8e268e..6d3fd5f5b82c 100644 --- a/cmake/EthOptions.cmake +++ b/cmake/EthOptions.cmake @@ -52,6 +52,7 @@ if (SUPPORT_TOOLS) endif() message("------------------------------------------------------------------ flags") message("-- OSSFUZZ ${OSSFUZZ}") + message("-- USE_YULC libyulc Yul backend ${USE_YULC}") message("-- PROPERTY_BASED_TESTS (FuzzTest) ${PROPERTY_BASED_TESTS}") message("-- PROPERTY_BASED_TESTS_MODE ${PROPERTY_BASED_TESTS_MODE}") message("------------------------------------------------------------------------") diff --git a/cmake/yulc.cmake b/cmake/yulc.cmake new file mode 100644 index 000000000000..d8230773e417 --- /dev/null +++ b/cmake/yulc.cmake @@ -0,0 +1,74 @@ +# Makes the powdr-labs verified Yul -> EVM compiler (libyulc) available as the +# imported target Yulc::yulc. +# +# libyulc is a prebuilt binary release of https://github.com/powdr-labs/yul-compiler: +# a Lean-compiled compiler behind a small C interface (yulc.h). It is not built +# from source here -- that would require the Lean toolchain and a Mathlib build. +# By default the pinned release below is downloaded and cached in the build +# directory; point YULC_ROOT at an extracted release (or at yul-compiler's own +# .lake/build/c) to use a local build instead. +# +# The static archive is used deliberately. It is a merged archive containing the +# whole closure, Lean runtime included, so solc stays a single self-contained +# binary with no runtime library search path to get right. This only works when +# linking an executable: the Lean runtime is compiled with local-exec TLS, which +# no linker accepts inside a shared object. The release also ships libyulc.so +# for that case, but solc does not need it. + +set(YULC_VERSION "0.0.1" CACHE STRING "Version of the libyulc release to download") +set(YULC_RELEASE_SHA256 + "1b8bf1c09cb6b3feaef4efe3d10c3c3348d0a96dc3cf17ba818aabe357d0f32f" + CACHE STRING "SHA-256 of the libyulc release tarball" +) +set(YULC_ROOT "" CACHE PATH + "Directory containing a prebuilt libyulc (yulc.h and libyulc.a). \ +Downloads the pinned release when empty." +) + +if (NOT UNIX OR APPLE) + message(FATAL_ERROR "USE_YULC is only supported on Linux; libyulc is released for x86_64 Linux only.") +endif() + +if (SOLC_LINK_STATIC) + # libyulc.a already brings its own libc++ and glibc-dependent Lean runtime. + # Folding that into a fully static binary is untested; refuse rather than + # produce something subtly broken. + message(FATAL_ERROR "USE_YULC is not supported together with SOLC_LINK_STATIC.") +endif() + +if (YULC_ROOT) + set(_yulc_dir "${YULC_ROOT}") + message(STATUS "Using libyulc from ${_yulc_dir}") +else() + # The release workflow derives the tarball name from the tag by stripping only + # the "libyulc-" prefix, so the "v" stays in it. + set(_yulc_name "libyulc-v${YULC_VERSION}-x86_64-linux") + include(FetchContent) + FetchContent_Declare( + yulc + URL "https://github.com/powdr-labs/yul-compiler/releases/download/libyulc-v${YULC_VERSION}/${_yulc_name}.tar.gz" + URL_HASH "SHA256=${YULC_RELEASE_SHA256}" + DOWNLOAD_EXTRACT_TIMESTAMP TRUE + ) + # The tarball has no CMakeLists.txt, so this only downloads and extracts it. + FetchContent_MakeAvailable(yulc) + set(_yulc_dir "${yulc_SOURCE_DIR}") +endif() + +find_library(YULC_LIBRARY NAMES libyulc.a PATHS "${_yulc_dir}" NO_DEFAULT_PATH REQUIRED + DOC "Path to the merged libyulc static archive" +) +find_file(YULC_HEADER NAMES yulc.h PATHS "${_yulc_dir}" NO_DEFAULT_PATH REQUIRED + DOC "Path to the libyulc public header" +) +get_filename_component(_yulc_includedir "${YULC_HEADER}" DIRECTORY) + +add_library(Yulc::yulc STATIC IMPORTED GLOBAL) +set_target_properties(Yulc::yulc PROPERTIES + IMPORTED_LOCATION "${YULC_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${_yulc_includedir}" + # What `leanc --print-ldflags` requires of the runtime baked into the archive. + INTERFACE_LINK_LIBRARIES "Threads::Threads;${CMAKE_DL_LIBS};rt;m" +) + +message(STATUS "Found libyulc: ${YULC_LIBRARY}") diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index a3a50a56e63a..a2d0e07266b3 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -536,6 +536,71 @@ but their presence is checked only at runtime, they are not needed for the build The emscripten builds require Z3 and will statically link against it instead. +.. _yulc-build: + +The libyulc Yul Backend +----------------------- + +``-DUSE_YULC=ON`` links ``solc`` against +`libyulc `_ and enables +:ref:`--yul-backend=yulc `. It is off by default. + +libyulc is a Lean-compiled compiler and is not built from source here; a prebuilt +release is downloaded and cached in the build directory. The merged static archive +is linked in, so ``solc`` remains a single self-contained binary -- a considerably +larger one, since the archive carries the Lean runtime. + +.. code-block:: sh + + cmake .. -DUSE_YULC=ON + +To build against a local checkout of yul-compiler instead of the pinned release, +point ``YULC_ROOT`` at a directory holding ``yulc.h`` and ``libyulc.a`` (which is +what ``scripts/build-c-lib.sh`` writes to ``.lake/build/c``): + +.. code-block:: sh + + cmake .. -DUSE_YULC=ON -DYULC_ROOT=/path/to/yul-compiler/.lake/build/c + +Only x86-64 Linux is supported, and the option cannot be combined with +``-DSOLC_LINK_STATIC=ON``. + +Most of the size the archive adds is code, but a large part of it is just the +symbol table; see :ref:`stripping-symbols` if that matters to you. + +.. _stripping-symbols: + +Stripping Symbols +----------------- + +``-DSOLC_STRIP_SYMBOLS=ON`` passes ``-s`` to the linker when ``solc`` is linked, +which leaves the symbol table (``.symtab``/``.strtab``) out of the executable. It +is off by default and currently only takes effect on Linux. + +This is worth its own option mostly because of :ref:`libyulc `, whose +Lean-mangled symbol names are far bigger than the code they name. With +``-DUSE_YULC=ON -DCMAKE_BUILD_TYPE=Release``, the symbol table is a third of the +binary: + +.. code-block:: text + + -DSOLC_STRIP_SYMBOLS=OFF 339 MiB + -DSOLC_STRIP_SYMBOLS=ON 226 MiB + +Without libyulc there is much less to gain: the same build goes from 20 MiB to +17 MiB. + +Note that ``-s`` discards debug info together with the symbol table, so turning it +on in a ``RelWithDebInfo`` build -- which is what a build from a git checkout +defaults to -- leaves you with a binary you cannot debug. That is why it is opt-in +rather than tied to the build type. + +Solidity itself never symbolizes anything at runtime: it installs no crash handler +and does not use ``boost::stacktrace``, so its own error output is unaffected. What +you lose is the ability of external tools -- ``gdb``, ``perf``, ``addr2line`` -- to +name anything but the handful of exported dynamic symbols. Keep an unstripped copy +around if you expect to need that. + The Version String in Detail ============================ diff --git a/docs/yul.rst b/docs/yul.rst index 5c8086b00120..1ee205e5bf19 100644 --- a/docs/yul.rst +++ b/docs/yul.rst @@ -128,6 +128,42 @@ to code as data to deploy contracts. This Yul mode is available for the commandl Yul is in active development and bytecode generation is only fully implemented for the EVM dialect of Yul with EVM 1.0 as target. +.. _yul-backend: + +Choosing the Code Generator +--------------------------- + +In stand-alone mode, ``--yul-backend`` selects which code generator turns Yul into +EVM bytecode: + +``solc`` (the default) + The code generator built into this compiler, described in the rest of this + document. + +``yulc`` + `libyulc `_, a Yul to EVM compiler + written and proved correct in Lean. + +.. code-block:: sh + + solc --strict-assembly --yul-backend yulc input.yul + +``--yul-backend=yulc`` requires a compiler built with ``-DUSE_YULC=ON``; see +:ref:`building the compiler `. It accepts the same input as the default +backend -- a Yul block or a Yul object, with child objects and data segments +resolved during compilation -- but it produces only the ``--bin`` output. There is +no assembly text, no assembly JSON, no AST and no source mapping to report, and +because the compiler only emits code it has proved correct, it rejects +``--optimize`` and the other optimizer options rather than silently ignoring them. +It also cannot resolve ``--libraries`` placeholders. + +Passing a program that the verified compiler does not cover is an error rather than +a fallback to the default backend: + +.. code-block:: none + + Error: input.yul: parsed, but uses unsupported compiler features (libyulc) + Informal Description of Yul =========================== diff --git a/solc/CMakeLists.txt b/solc/CMakeLists.txt index 8d9d2b364cd8..d9292b3c591f 100644 --- a/solc/CMakeLists.txt +++ b/solc/CMakeLists.txt @@ -7,6 +7,14 @@ set(libsolcli_sources add_library(solcli ${libsolcli_sources}) target_link_libraries(solcli PUBLIC solidity Boost::boost Boost::program_options) +if (USE_YULC) + # PRIVATE: yulc.h is an implementation detail of CommandLineInterface.cpp and + # nothing outside solcli refers to it. The archive itself still propagates to + # the solc link, as it must -- it may only be linked into an executable. + target_link_libraries(solcli PRIVATE Yulc::yulc) + target_compile_definitions(solcli PRIVATE SOLC_HAVE_YULC) +endif() + set(sources main.cpp) add_executable(solc ${sources}) @@ -31,3 +39,14 @@ elseif(SOLC_STATIC_STDLIBS AND UNIX AND NOT APPLE) LINK_FLAGS "-static-libgcc -static-libstdc++" ) endif() + +if(SOLC_STRIP_SYMBOLS AND UNIX AND NOT APPLE) + # Tell the linker to omit .symtab/.strtab. Note that this discards the debug + # info of a RelWithDebInfo build along with them, which is why it is opt-in + # even though the default build type is not Release. + # + # target_link_options() rather than the LINK_FLAGS property above, because + # this has to compose with whatever SOLC_LINK_STATIC or SOLC_STATIC_STDLIBS + # already put there. + target_link_options(solc PRIVATE "LINKER:-s") +endif() diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index fe28b287c6db..daf266ce9ad8 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -43,6 +43,10 @@ #include +#ifdef SOLC_HAVE_YULC +#include +#endif + #include #include @@ -859,7 +863,10 @@ void CommandLineInterface::processInput() serveLSP(); break; case InputMode::Assembler: - assembleYul(m_options.assembly.targetMachine); + if (m_options.assembly.yulBackend == YulBackend::Yulc) + assembleYulWithYulc(); + else + assembleYul(m_options.assembly.targetMachine); break; case InputMode::Linker: link(); @@ -1295,6 +1302,46 @@ std::string CommandLineInterface::objectWithLinkRefsHex(evmasm::LinkerObject con return out; } +void CommandLineInterface::assembleYulWithYulc() +{ + solAssert(m_options.input.mode == InputMode::Assembler); + solAssert(m_options.assembly.yulBackend == YulBackend::Yulc); + +#ifndef SOLC_HAVE_YULC + solThrow( + CommandLineExecutionError, + "This binary was built without libyulc support. Rebuild solc with -DUSE_YULC=ON to use " + "--yul-backend=yulc." + ); +#else + // libyulc accepts exactly what its own CLI accepts: one complete Yul program, + // block- or object-rooted, with child objects and data segments resolved + // during compilation. So each source unit is handed over verbatim and comes + // back as finished creation bytecode. + for (auto const& [sourceUnitName, yulSource]: m_fileReader.sourceUnits()) + { + uint8_t* bytecode = nullptr; + size_t bytecodeLength = 0; + int const status = yulc_compile(yulSource.c_str(), &bytecode, &bytecodeLength); + + if (status != YULC_OK) + solThrow( + CommandLineExecutionError, + sourceUnitName + ": " + yulc_status_string(status) + " (libyulc)" + ); + + // yulc_compile() hands over ownership of the buffer. + bytes bytecodeBytes(bytecode, bytecode + bytecodeLength); + yulc_free(bytecode); + + sout() << std::endl << "======= " << sourceUnitName << " (EVM) =======" << std::endl; + solAssert(m_options.compiler.outputs.binary); + sout() << std::endl << "Binary representation:" << std::endl; + sout() << util::toHex(bytecodeBytes) << std::endl; + } +#endif +} + void CommandLineInterface::assembleYul(yul::YulStack::Machine _targetMachine) { solAssert(m_options.input.mode == InputMode::Assembler); diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index e61bf359f48a..ef207dc9fcc7 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -96,6 +96,9 @@ class CommandLineInterface static std::string objectWithLinkRefsHex(evmasm::LinkerObject const& _obj); void assembleYul(yul::YulStack::Machine _targetMachine); + /// Assembles the Yul input with libyulc instead of solc's own code generator. + /// Only produces the binary output; see --yul-backend. + void assembleYulWithYulc(); void outputCompilationResults(); diff --git a/solc/CommandLineParser.cpp b/solc/CommandLineParser.cpp index 8d1ca153b427..088cd072d8d6 100644 --- a/solc/CommandLineParser.cpp +++ b/solc/CommandLineParser.cpp @@ -57,6 +57,7 @@ static std::string const g_strImportAst = "import-ast"; static std::string const g_strImportEvmAssemblerJson = "import-asm-json"; static std::string const g_strInputFile = "input-file"; static std::string const g_strYul = "yul"; +static std::string const g_strYulBackend = "yul-backend"; static std::string const g_strYulDialect = "yul-dialect"; static std::string const g_strDebugInfo = "debug-info"; static std::string const g_strIPFS = "ipfs"; @@ -125,6 +126,16 @@ static std::set const g_yulDialectArgs g_strEVM }; +static std::string const g_strYulBackendSolc = "solc"; +static std::string const g_strYulBackendYulc = "yulc"; + +/// Possible arguments to for --yul-backend +static std::set const g_yulBackendArgs +{ + g_strYulBackendSolc, + g_strYulBackendYulc +}; + /// Possible arguments to for --metadata-hash static std::set const g_metadataHashArgs { @@ -709,6 +720,18 @@ General Information)").c_str(), po::value()->value_name(util::joinHumanReadable(g_yulDialectArgs, ",")), "Input dialect to use in assembly or yul mode." ) + ( + g_strYulBackend.c_str(), + po::value()->value_name(util::joinHumanReadable(g_yulBackendArgs, ",")) + ->default_value(g_strYulBackendSolc), + ("Code generator to use for translating Yul to EVM bytecode in assembly mode. " + "'" + g_strYulBackendSolc + "' is solc's own code generator. " + "'" + g_strYulBackendYulc + "' is libyulc, the formally verified compiler from " + "https://github.com/powdr-labs/yul-compiler. It produces only the --" + + CompilerOutputs::componentName(&CompilerOutputs::binary) + " output, and rejects " + "--" + g_strOptimize + " and the other optimizer options because it optimizes " + "according to its own proofs. Requires a binary built with USE_YULC=ON.").c_str() + ) ; desc.add(assemblyModeOptions); @@ -1076,7 +1099,8 @@ void CommandLineParser::processArgs() {g_strModelCheckerBMCLoopIterations, {InputMode::Compiler, InputMode::CompilerWithASTImport}}, {g_strModelCheckerContracts, {InputMode::Compiler, InputMode::CompilerWithASTImport}}, {g_strModelCheckerTargets, {InputMode::Compiler, InputMode::CompilerWithASTImport}}, - {g_strViaSSACFG, {InputMode::Compiler, InputMode::CompilerWithASTImport, InputMode::Assembler}} + {g_strViaSSACFG, {InputMode::Compiler, InputMode::CompilerWithASTImport, InputMode::Assembler}}, + {g_strYulBackend, {InputMode::Assembler}} }; std::vector invalidOptionsForCurrentInputMode; for (auto const& [optionName, inputModes]: validOptionInputModeCombinations) @@ -1361,8 +1385,67 @@ void CommandLineParser::processArgs() solThrow(CommandLineValidationError, "Invalid option for --" + g_strYulDialect + ": " + dialect); } + { + auto const& backend = m_args[g_strYulBackend].as(); + if (backend == g_strYulBackendSolc) + m_options.assembly.yulBackend = YulBackend::Solc; + else if (backend == g_strYulBackendYulc) + m_options.assembly.yulBackend = YulBackend::Yulc; + else + solThrow(CommandLineValidationError, "Invalid option for --" + g_strYulBackend + ": " + backend); + } + m_options.output.viaSSACFG = m_args.contains(g_strViaSSACFG); + if (m_options.assembly.yulBackend == YulBackend::Yulc) + { + // libyulc takes Yul source and returns bytecode. It exposes neither the + // intermediate assembly nor an AST, it applies its own proof-carrying + // optimizations rather than solc's, and it emits fully resolved objects + // with no link references. Reject anything that would silently do + // something other than what was asked for. + std::string const withBackend = + " with --" + g_strYulBackend + "=" + g_strYulBackendYulc; + + std::vector unsupportedOutputs; + for (auto&& [optionName, outputComponent]: CompilerOutputs::componentMap()) + if (outputComponent != &CompilerOutputs::binary && m_args.count(optionName) > 0) + unsupportedOutputs.push_back(optionName); + if (!unsupportedOutputs.empty()) + solThrow( + CommandLineValidationError, + "The following outputs are not available" + withBackend + " (only --" + + CompilerOutputs::componentName(&CompilerOutputs::binary) + " is): " + + joinOptionNames(unsupportedOutputs) + "." + ); + + std::vector unsupportedOptions; + for (std::string const& optionName: { + g_strOptimize, + g_strOptimizeRuns, + g_strOptimizeYul, + g_strNoOptimizeYul, + g_strYulOptimizations, + g_strLibraries, + g_strViaSSACFG, + }) + // Some of these carry a default value, which makes them present in + // m_args even when the user did not pass them. + if (m_args.count(optionName) > 0 && !m_args[optionName].defaulted()) + unsupportedOptions.push_back(optionName); + if (!unsupportedOptions.empty()) + solThrow( + CommandLineValidationError, + "The following options are not supported" + withBackend + ": " + + joinOptionNames(unsupportedOptions) + "." + ); + + // The assembler-mode defaults above also request the text representation + // and the pretty-printed source, which this backend cannot produce. + m_options.compiler.outputs = CompilerOutputs{}; + m_options.compiler.outputs.binary = true; + } + if (m_options.compiler.outputs.ethdebugProgram || m_options.compiler.outputs.ethdebugProgramRuntime) { if (m_options.output.viaSSACFG) diff --git a/solc/CommandLineParser.h b/solc/CommandLineParser.h index 99dc7c8dd1e5..2d51de985b75 100644 --- a/solc/CommandLineParser.h +++ b/solc/CommandLineParser.h @@ -47,6 +47,16 @@ namespace solidity::frontend { +/// Backend used to turn Yul into EVM bytecode in assembler mode. +enum class YulBackend +{ + /// solc's own Yul code generator. + Solc, + /// libyulc, the powdr-labs verified Yul -> EVM compiler. Only available in + /// binaries built with USE_YULC=ON. + Yulc +}; + enum class InputMode { Help, @@ -217,6 +227,7 @@ struct CommandLineOptions bool operator!=(Assembly const&) const noexcept = default; yul::YulStack::Machine targetMachine = yul::YulStack::Machine::EVM; + YulBackend yulBackend = YulBackend::Solc; } assembly; struct Linker diff --git a/test/cmdlineTests/strict_asm_yul_backend_invalid/args b/test/cmdlineTests/strict_asm_yul_backend_invalid/args new file mode 100644 index 000000000000..f23b560de736 --- /dev/null +++ b/test/cmdlineTests/strict_asm_yul_backend_invalid/args @@ -0,0 +1 @@ +--strict-assembly --yul-backend bogus diff --git a/test/cmdlineTests/strict_asm_yul_backend_invalid/err b/test/cmdlineTests/strict_asm_yul_backend_invalid/err new file mode 100644 index 000000000000..246ff16831c4 --- /dev/null +++ b/test/cmdlineTests/strict_asm_yul_backend_invalid/err @@ -0,0 +1 @@ +Error: Invalid option for --yul-backend: bogus diff --git a/test/cmdlineTests/strict_asm_yul_backend_invalid/exit b/test/cmdlineTests/strict_asm_yul_backend_invalid/exit new file mode 100644 index 000000000000..d00491fd7e5b --- /dev/null +++ b/test/cmdlineTests/strict_asm_yul_backend_invalid/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/strict_asm_yul_backend_invalid/input.yul b/test/cmdlineTests/strict_asm_yul_backend_invalid/input.yul new file mode 100644 index 000000000000..34d4a38f3abf --- /dev/null +++ b/test/cmdlineTests/strict_asm_yul_backend_invalid/input.yul @@ -0,0 +1 @@ +{ let x := 2 let y := 40 sstore(0, add(x, y)) } diff --git a/test/cmdlineTests/strict_asm_yul_backend_yulc_unsupported_option/args b/test/cmdlineTests/strict_asm_yul_backend_yulc_unsupported_option/args new file mode 100644 index 000000000000..5670366fa150 --- /dev/null +++ b/test/cmdlineTests/strict_asm_yul_backend_yulc_unsupported_option/args @@ -0,0 +1 @@ +--strict-assembly --yul-backend yulc --optimize diff --git a/test/cmdlineTests/strict_asm_yul_backend_yulc_unsupported_option/err b/test/cmdlineTests/strict_asm_yul_backend_yulc_unsupported_option/err new file mode 100644 index 000000000000..d963ba5cf0d3 --- /dev/null +++ b/test/cmdlineTests/strict_asm_yul_backend_yulc_unsupported_option/err @@ -0,0 +1 @@ +Error: The following options are not supported with --yul-backend=yulc: --optimize. diff --git a/test/cmdlineTests/strict_asm_yul_backend_yulc_unsupported_option/exit b/test/cmdlineTests/strict_asm_yul_backend_yulc_unsupported_option/exit new file mode 100644 index 000000000000..d00491fd7e5b --- /dev/null +++ b/test/cmdlineTests/strict_asm_yul_backend_yulc_unsupported_option/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/strict_asm_yul_backend_yulc_unsupported_option/input.yul b/test/cmdlineTests/strict_asm_yul_backend_yulc_unsupported_option/input.yul new file mode 100644 index 000000000000..34d4a38f3abf --- /dev/null +++ b/test/cmdlineTests/strict_asm_yul_backend_yulc_unsupported_option/input.yul @@ -0,0 +1 @@ +{ let x := 2 let y := 40 sstore(0, add(x, y)) } diff --git a/test/cmdlineTests/strict_asm_yul_backend_yulc_unsupported_output/args b/test/cmdlineTests/strict_asm_yul_backend_yulc_unsupported_output/args new file mode 100644 index 000000000000..253ab84fedf1 --- /dev/null +++ b/test/cmdlineTests/strict_asm_yul_backend_yulc_unsupported_output/args @@ -0,0 +1 @@ +--strict-assembly --yul-backend yulc --asm diff --git a/test/cmdlineTests/strict_asm_yul_backend_yulc_unsupported_output/err b/test/cmdlineTests/strict_asm_yul_backend_yulc_unsupported_output/err new file mode 100644 index 000000000000..8f496abe90bd --- /dev/null +++ b/test/cmdlineTests/strict_asm_yul_backend_yulc_unsupported_output/err @@ -0,0 +1 @@ +Error: The following outputs are not available with --yul-backend=yulc (only --bin is): --asm. diff --git a/test/cmdlineTests/strict_asm_yul_backend_yulc_unsupported_output/exit b/test/cmdlineTests/strict_asm_yul_backend_yulc_unsupported_output/exit new file mode 100644 index 000000000000..d00491fd7e5b --- /dev/null +++ b/test/cmdlineTests/strict_asm_yul_backend_yulc_unsupported_output/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/strict_asm_yul_backend_yulc_unsupported_output/input.yul b/test/cmdlineTests/strict_asm_yul_backend_yulc_unsupported_output/input.yul new file mode 100644 index 000000000000..34d4a38f3abf --- /dev/null +++ b/test/cmdlineTests/strict_asm_yul_backend_yulc_unsupported_output/input.yul @@ -0,0 +1 @@ +{ let x := 2 let y := 40 sstore(0, add(x, y)) } diff --git a/test/cmdlineTests/yul_backend_in_non_asm_mode/args b/test/cmdlineTests/yul_backend_in_non_asm_mode/args new file mode 100644 index 000000000000..1770d240dcec --- /dev/null +++ b/test/cmdlineTests/yul_backend_in_non_asm_mode/args @@ -0,0 +1 @@ +--yul-backend yulc diff --git a/test/cmdlineTests/yul_backend_in_non_asm_mode/err b/test/cmdlineTests/yul_backend_in_non_asm_mode/err new file mode 100644 index 000000000000..0878bb5ace87 --- /dev/null +++ b/test/cmdlineTests/yul_backend_in_non_asm_mode/err @@ -0,0 +1 @@ +Error: The following options are not supported in the current input mode: --yul-backend diff --git a/test/cmdlineTests/yul_backend_in_non_asm_mode/exit b/test/cmdlineTests/yul_backend_in_non_asm_mode/exit new file mode 100644 index 000000000000..d00491fd7e5b --- /dev/null +++ b/test/cmdlineTests/yul_backend_in_non_asm_mode/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/yul_backend_in_non_asm_mode/input.sol b/test/cmdlineTests/yul_backend_in_non_asm_mode/input.sol new file mode 100644 index 000000000000..6923ca7023b3 --- /dev/null +++ b/test/cmdlineTests/yul_backend_in_non_asm_mode/input.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +contract C +{ + function f() public pure {} +} diff --git a/test/solc/CommandLineParser.cpp b/test/solc/CommandLineParser.cpp index 63b8512f4a11..c6e689aa723a 100644 --- a/test/solc/CommandLineParser.cpp +++ b/test/solc/CommandLineParser.cpp @@ -272,6 +272,57 @@ BOOST_AUTO_TEST_CASE(via_ir_options) BOOST_TEST(parseCommandLine({"solc", viaIrOption, "contract.sol"}).output.viaIR); } +BOOST_AUTO_TEST_CASE(yul_backend_option) +{ + BOOST_TEST( + (parseCommandLine({"solc", "--strict-assembly", "contract.yul"}).assembly.yulBackend == YulBackend::Solc) + ); + BOOST_TEST( + (parseCommandLine({"solc", "--strict-assembly", "--yul-backend=solc", "contract.yul"}).assembly.yulBackend == YulBackend::Solc) + ); + + CommandLineOptions const yulcOptions = + parseCommandLine({"solc", "--strict-assembly", "--yul-backend=yulc", "contract.yul"}); + BOOST_TEST((yulcOptions.assembly.yulBackend == YulBackend::Yulc)); + + // libyulc produces bytecode and nothing else, so the assembler-mode output + // defaults must be narrowed down to just --bin. + CompilerOutputs expectedOutputs; + expectedOutputs.binary = true; + BOOST_TEST((yulcOptions.compiler.outputs == expectedOutputs)); +} + +BOOST_AUTO_TEST_CASE(yul_backend_option_invalid) +{ + std::vector, std::string>> const invalidInputs = { + { + {"solc", "--strict-assembly", "--yul-backend=lean", "contract.yul"}, + "Invalid option for --yul-backend: lean" + }, + { + {"solc", "--yul-backend=yulc", "contract.sol"}, + "The following options are not supported in the current input mode: --yul-backend" + }, + { + {"solc", "--strict-assembly", "--yul-backend=yulc", "--asm", "contract.yul"}, + "The following outputs are not available with --yul-backend=yulc (only --bin is): --asm." + }, + { + {"solc", "--strict-assembly", "--yul-backend=yulc", "--optimize", "contract.yul"}, + "The following options are not supported with --yul-backend=yulc: --optimize." + }, + }; + + for (auto const& [commandLine, expectedErrorMessage]: invalidInputs) + { + auto hasCorrectMessage = [&](CommandLineValidationError const& _exception) + { + return _exception.what() == expectedErrorMessage; + }; + BOOST_CHECK_EXCEPTION(parseCommandLine(commandLine), CommandLineValidationError, hasCorrectMessage); + } +} + BOOST_AUTO_TEST_CASE(assembly_mode_options) { static std::vector, YulStack::Machine>> const allowedCombinations = {