pax_global_header00006660000000000000000000000064136240261400014510gustar00rootroot0000000000000052 comment=dfb2f27d0123ad62b44914fd0210db1d21cfc91e binaryen-version_91/000077500000000000000000000000001362402614000146575ustar00rootroot00000000000000binaryen-version_91/.appveyor.yml000066400000000000000000000032321362402614000173250ustar00rootroot00000000000000--- skip_branch_with_pr: true image: Visual Studio 2017 init: - set PATH=C:\Python27\Scripts;%PATH% # while python's bin is already in PATH, but pip.exe in Scripts\ dir isn't - set PATH=C:\msys64\mingw64\bin;C:\msys64\usr\bin;%PATH% environment: DEGREE_OF_PARALLELISM: 3 matrix: - GENERATOR: MSYS Makefiles CONFIG: Release PARALLEL_FLAG: -j - GENERATOR: Visual Studio 15 2017 CONFIG: Release PARALLEL_FLAG: "/m:" DEPLOY: 1 ARCH: x86 - GENERATOR: Visual Studio 15 2017 Win64 CONFIG: Debug PARALLEL_FLAG: "/m:" - GENERATOR: Visual Studio 15 2017 Win64 CONFIG: Release PARALLEL_FLAG: "/m:" DEPLOY: 1 ARCH: x86_64 install: - pip install flake8==3.4.1 before_build: # Check the style of a subset of Python code until the other code is updated. - flake8 ./scripts/ build_script: # Request `libcmt.lib` is used so our released artifacts don't dynamically # link to `msvcrt.dll` - cmake . -DCMAKE_BUILD_TYPE=%CONFIG% -G "%GENERATOR%" -DMSVC_USE_LIBCMT=YES - cmake --build . --config %CONFIG% -- %PARALLEL_FLAG%%DEGREE_OF_PARALLELISM% test_script: - ctest --output-on-failure --timeout 10 -j 5 -C Release before_deploy: - ps: | $NAME = "binaryen-${env:APPVEYOR_REPO_TAG_NAME}-${env:ARCH}-windows" Move-Item -Path bin -Destination $NAME 7z a -ttar "${NAME}.tar" "${NAME}" 7z a "${NAME}.tar.gz" "${NAME}.tar" Push-AppveyorArtifact "${NAME}.tar.gz" deploy: artifact: /.*\.tar.gz/ auth_token: secure: zM0Bcjy1JXOBuu2C32lY0vCxREu7ah+bYFUpwmuryw82+HgCjvq7ZMutAk34Lv9d description: '' on: appveyor_repo_tag: true DEPLOY: 1 provider: GitHub binaryen-version_91/.clang-format000066400000000000000000000004261362402614000172340ustar00rootroot00000000000000--- Language: Cpp BasedOnStyle: LLVM PointerAlignment: Left IndentCaseLabels: true ContinuationIndentWidth: 2 ConstructorInitializerIndentWidth: 2 SpaceAfterTemplateKeyword: false BinPackArguments: false BinPackParameters: false --- Language: JavaScript DisableFormat: true --- binaryen-version_91/.clang-tidy000066400000000000000000000014011362402614000167070ustar00rootroot00000000000000Checks: '-*,clang-diagnostic-*,readability-braces-around-statements' CheckOptions: - key: readability-identifier-naming.ClassCase value: CamelCase - key: readability-identifier-naming.EnumCase value: CamelCase - key: readability-identifier-naming.EnumConstantCase value: CamelCase - key: readability-identifier-naming.FunctionCase value: camelBack - key: readability-identifier-naming.MemberCase value: camelBack - key: readability-identifier-naming.ParameterCase value: camelBack - key: readability-identifier-naming.VariableCase value: camelBack HeaderFilterRegex: src/* binaryen-version_91/.flake8000066400000000000000000000003231362402614000160300ustar00rootroot00000000000000[flake8] ignore = E501, # line too long E241, # space after comma (ignored for list in gen-s-parser.py) W504 # line break after binary operator exclude = ./test/emscripten,./test/spec,./test/wasm-install binaryen-version_91/.gitattributes000066400000000000000000000000641362402614000175520ustar00rootroot00000000000000*.sh text eol=lf test/binaryen.js/*.txt text eol=lf binaryen-version_91/.gitignore000066400000000000000000000007741362402614000166570ustar00rootroot00000000000000Makefile *.pyc *~ *.diff # autogenerated during the build /src/passes/WasmIntrinsics.cpp # File generated by build-js.sh /out/ # files related to building in-tree CMakeFiles *.cmake /CMakeCache.txt /Makefile /*.ninja /.ninja_deps /.ninja_log /bin/ /lib/ config.h *.o *.obj # files related to bulding in-tree on windows /.vs/ /*.vcxproj* /*.dir/ /*.sln /*.sdf /*.VC.opendb /Win32/ # files commonly related to building out-of-tree /build/ # macOS .DS_Store # files related to VS Code /.history /.vscode binaryen-version_91/.gitmodules000066400000000000000000000003051362402614000170320ustar00rootroot00000000000000[submodule "test/spec"] path = test/spec url = https://github.com/WebAssembly/testsuite.git [submodule "test/emscripten"] path = test/emscripten url = https://github.com/kripken/emscripten.git binaryen-version_91/.travis.yml000066400000000000000000000153261362402614000167770ustar00rootroot00000000000000sudo: false dist: bionic language: cpp python: - 3.6 stages: - name: test - name: archive # Don't run archive stage for pull requests and other branches than master # to save time and resources. if: type != pull_request AND (branch = master OR tag IS present) DEPLOY_TO_GITHUB: &DEPLOY_TO_GITHUB before_deploy: - PKGNAME="binaryen-$TRAVIS_TAG-$ARCH" - mv bin binaryen-$TRAVIS_TAG - tar -czf $PKGNAME.tar.gz binaryen-$TRAVIS_TAG - shasum -a 256 $PKGNAME.tar.gz > $PKGNAME.tar.gz.sha256 deploy: provider: releases api_key: secure: "cu6CD5BaycXdCylvcs+Fho5+OVTkh9mZwH8RTnNpXo9hAQzLJDFgcNBHeXHEHtcp4IWf/YZSMu48UKnpU9sP5iF0AS4rtuEBJk5gOKkgB8GWnuIOePFkfANAZMN+EncuUwhAdN56iOAESXqnlHYgmJjyRVCHOxiezuWTOYui4lxoIAdxvOMJc3E9yfzUq4Epm2GDszSDN7ObmRIJpVgDXD9Sze1Xv4IkbIwc0biCmduGGLp3ow2KM+RZ4tOF0c8P0ki49vOFHr6n2Vmqg0QCiVNd4JJBRBCGn6Tzip2jsTQewnUUvpYCZafLeRV//v//voNA6ZUz91yXR23GIhkfdlyuqnz3/7l335Sa749M1lpYfSRWvwg9mJEqP66mxqTrWzj1xSItr9T+p0WhSmRN/4UEJPuItYPSma6kfv+H7qhLa3ZYKECH8hHW79grYmUWtiX0vQVIgnctJGgboPNLfG/1mNtmCI241wK0S3zvL2okdZH8/PqxfllYHMBTUp9lUrop8eoLKPgHZPm6+V20dgTUgOuGTZzTWwQ7Uk/Pg8JMUgkre5y0eo6pP3z0vDW1NNFNhouJ5oGkAeK/HAznr8Q0zWWF1vGFhoyC8ok/IJ7yKxK9scJVPBDe4oox6tr1zlsxzNEYE0/mY3JjuWV0z8RgjrIAbRe8IpGTkYz5VOM=" file: binaryen-$TRAVIS_TAG-*.tar.gz* file_glob: true skip_cleanup: true on: tags: true jobs: include: - name: lint-checks stage: test addons: apt: packages: ['cmake', 'python3-pip'] install: pip3 install --user flake8==3.7.8 script: - flake8 - ./clang-format-diff.sh # ensure generated parser is up to date - ./scripts/gen-s-parser.py | diff src/gen-s-parser.inc - # clang-tidy requires compile_commands.json generated by cmake - cmake ${TRAVIS_BUILD_DIR} -DCMAKE_EXPORT_COMPILE_COMMANDS=ON - ./clang-tidy-diff.sh # Build with clang and run tests on the host system (Ubuntu). - &test-ubuntu name: clang stage: test compiler: clang addons: apt: sources: ['ubuntu-toolchain-r-test'] packages: ['cmake', 'g++-5', 'ninja-build'] before_install: - export ASAN_OPTIONS="$ASAN_OPTIONS symbolize=1" install: - nvm install 12 - nvm use 12 # get jsvu in order to get more js engines - npm install jsvu -g - export PATH="${HOME}/.jsvu:${PATH}" - jsvu --os=linux64 --engines=spidermonkey,v8 script: - set -o errexit - BUILD_DIR=${BUILD_DIR:-.} - mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR} - cmake ${TRAVIS_BUILD_DIR} -G Ninja -DCMAKE_C_FLAGS="$COMPILER_FLAGS" -DCMAKE_CXX_FLAGS="$COMPILER_FLAGS" -DCMAKE_EXE_LINKER_FLAGS="$LINKER_FLAGS" -DCMAKE_INSTALL_PREFIX=install -DCMAKE_EXPORT_COMPILE_COMMANDS=ON - ninja install # Run tests from source directory - cd ${TRAVIS_BUILD_DIR} - python3 ./check.py --binaryen-bin=${BUILD_DIR}/install/bin - <<: *test-ubuntu name: ubsan env: | COMPILER_FLAGS="-fsanitize=undefined -fno-sanitize-recover=all -fsanitize-blacklist=$(pwd)/ubsan.blacklist" # FIXME we currently must disable LSAN entirely, see #1351 - <<: *test-ubuntu name: asan env: | COMPILER_FLAGS="-fsanitize=address" ASAN_OPTIONS="detect_leaks=0" - <<: *test-ubuntu name: tsan env: | COMPILER_FLAGS="-fsanitize=thread" LINKER_FLAGS="-fsanitize=thread" # Build with gcc 7 and run tests on the host system (Ubuntu). # Also tests that out-of-tree builds work - <<: *test-ubuntu name: gcc-7 / out-of-tree compiler: gcc env: | CC="gcc-7" CXX="g++-7" BUILD_DIR=out # Build the .js outputs using emcc - name: emscripten stage: test compiler: clang python: 2.7 language: node_js sudo: required services: - docker before_install: # TODO: Emscripten upstream produces a broken build in CI environments only :( - docker run -dit --name emscripten -v $(pwd):/src trzeci/emscripten-fastcomp bash script: # run binaryen.js tests before and after building, so we see if the bundled # version is good too - docker exec -it emscripten bash ./travis-emcc-tests.sh # Build with gcc 6.3 and run tests on Alpine Linux (inside chroot). # Note: Alpine uses musl libc. - &test-alpine name: alpine stage: test sudo: true language: minimal compiler: gcc env: ARCH=x86_64 before_install: - docker run -w /src -dit --name alpine -v $(pwd):/src node:lts-alpine - alpine() { docker exec -it alpine "$@"; } install: - alpine apk update - alpine apk add build-base cmake git python3 clang ninja script: - alpine cmake . -G Ninja - alpine ninja - alpine python3 ./check.py - name: osx env: JOB=dist-osx ARCH=x86_64-apple-darwin os: osx stage: archive script: - cmake . && make <<: *DEPLOY_TO_GITHUB # Build statically linked release binaries with gcc 6.3 on Alpine Linux # (inside chroot). If building a tagged commit, then deploy release tarball # to GitHub Releases. - &archive-alpine <<: *test-alpine name: x86_64-linux stage: archive env: ARCH=x86_64-linux script: - alpine cmake . -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_CXX_FLAGS="-static" -DCMAKE_C_FLAGS="-static" -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ - alpine ninja - alpine find bin/ -type f -perm -u=x -exec strip {} + - alpine ls -lh bin/ # Check if the built executables are really statically linked. - if [ -n "$(find bin/ -type f -perm -u=x -exec file {} + | grep -Fvw 'statically linked')" ]; then file bin/*; false; fi <<: *DEPLOY_TO_GITHUB # Build binaries for other architectures using QEMU user-mode emulation. # Note: We don't run tests for these architectures, because some fail under # QEMU/binfmt and it takes too long time (hits time limit on Travis). # Note: We had to remove ppc64le, because it takes more than 50 minutes # (Travis limit) to build. :( - <<: *archive-alpine name: x86-linux env: ARCH=x86-linux - <<: *archive-alpine name: aarch64-linux env: ARCH=aarch64-linux - <<: *archive-alpine name: armhf-linux env: ARCH=armhf-linux notifications: email: false binaryen-version_91/CHANGELOG.md000066400000000000000000000130461362402614000164740ustar00rootroot00000000000000Changelog ========= This document describes changes between tagged Binaryen versions. To browse or download snapshots of old tagged versions, visit https://github.com/WebAssembly/binaryen/releases. Not all changes are documented here. In particular, new features, user-oriented fixes, options, command-line parameters, usage changes, deprecations, significant internal modifications and optimizations etc. generally deserve a mention. To examine the full set of changes between versions, visit the link to full changeset diff at the end of each section. Current Trunk ------------- v91 --- - `BinaryenExpressionGetSideEffects` (C API) and `getSideEffects` (JS API) now takes an additional `features` parameter. - Reference type support is added. Supported instructions are `ref.null`, `ref.is_null`, `ref.func`, and typed `select`. Table instructions are not supported yet. For typed `select`, C/JS API can take an additional 'type' parameter. v90 --- - `local.tee`'s C/Binaryen.js API now takes an additional type parameter for its local type, like `local.get`. This is required to handle subtypes. - Added load_splat SIMD instructions - Binaryen.js instruction API changes: - `notify` -> `atomic.notify` - `i32.wait` / `i64.wait` -> `i32.atomic.wait` / `i64.atomic.wait` - Binaryen.js: `flags` argument in `setMemory` function is removed. - `atomic.fence` instruction support is added. - wasm-emscripten-finalize: Don't rely on name section being present in the input. Use the exported names for things instead. - Added `mutable` parameter to BinaryenAddGlobalImport. - Replace BinaryenSIMDBitselect* with BinaryenSIMDTernary* in the C API and add qfma/qfms instructions. - Added `offset` parameter to BinaryenSetFunctionTable. - Add the ability to create multivalue Types in the C and JS APIs. - Remove named function types. They are replaced by `params` and `results` types local to each function. - Binaryen.js can now be compiled to Wasm using the `binaryen_wasm` target. Unlike the JS variant, the Wasm variant requires asynchronously awaiting the Wasm blob's instantiation and initialization before being usable, using the `binaryen.ready` promise, e.g. `binaryen.ready.then(() => ...)`. - Binaryen.js now uses `binaryen` (was `Binaryen`) as its global name to align with the npm package. - Binaryen.js: The result of `getMemorySegmentInfoByIndex` now has the same structure as the respective inputs on creation (`byteOffset` -> `offset`). v88 --- - wasm-emscripten-finalize: For -pie binaries that import a mutable stack pointer we internalize this an import it as immutable. - The `tail-call` feature including the `return_call` and `return_call_indirect` instructions is ready to use. v87 --- - Rename Bysyncify => Asyncify v86 --- - The --initial-stack-pointer argument to wasm-emscripten-finalize no longer has any effect. It will be removed completely in future release. v85 --- - Wast file parsing rules now don't allow a few invalid formats for typeuses that were previously allowed. Typeuse entries should follow this format, meaning they should have (type) -> (param) -> (result) order if more than one of them exist. ``` typeuse ::= (type index|name)+ | (type index|name)+ (param ..)* (result ..)* | (param ..)* (result ..)* ``` Also, all (local) nodes in function definition should be after all typeuse elements. - Removed APIs related to deprecated instruction names in Binaryen.js: - `get_local` / `getLocal` - `set_local` / `setLocal` - `tee_local` / `teeLocal` - `get_global` / `getGlobal` - `set_global` / `setGlobal` - `current_memory` / `currentMemory` - `grow_memory` / `growMemory` They are now available as their new instruction names: `local.get`, `local.set`, `local.tee`, `global.get`, `global.set`, `memory.size`, and `memory.grow`. - Add feature handling to the C/JS API with no feature enabled by default. v84 --- - Generate dynCall thunks for any signatures used in "invoke" calls. v81 --- - Fix AsmConstWalker handling of string address in arg0 with -fPIC code v80 --- - Change default feature set in the absence of a target features section from all features to MVP. v79 --- - Improve support for side modules v78 --- - Add `namedGlobals` to metadata output of wasm-emscripten-finalize - Add support for llvm PIC code. - Add --side-module option to wasm-emscripten-finalize. - Add `segmentPassive` argument to `BinaryenSetMemory` for marking segments passive. - Make `-o -` print to stdout instead of a file named "-". v73 --- - Remove wasm-merge tool. v73 --- - Remove jsCall generation from wasm-emscripten-finalize. This is not needed as of https://github.com/emscripten-core/emscripten/pull/8255. v55 --- - `RelooperCreate` in the C API now has a Module parameter, and `RelooperRenderAndDispose` does not. - The JS API now has the `Relooper` constructor receive the `Module`. - Relooper: Condition properties on Branches must not have side effects. older ----- - `BinaryenSetFunctionTable` in the C API no longer accepts an array of functions, instead it accepts an array of function names, `const char** funcNames`. Previously, you could not include imported functions because they are of type `BinaryenImportRef` instead of `BinaryenFunctionRef`. #1650 - `BinaryenSetFunctionTable` in the C API now expects the initial and maximum table size as additional parameters, like `BinaryenSetMemory` does for pages, so tables can be grown dynamically. #1687 - Add `shared` parameters to `BinaryenAddMemoryImport` and `BinaryenSetMemory`, to support a shared memory. #1686 binaryen-version_91/CMakeLists.txt000066400000000000000000000407311362402614000174240ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.1.3) project(binaryen LANGUAGES C CXX VERSION 91) include(GNUInstallDirs) if(NOT CMAKE_BUILD_TYPE) message(STATUS "No build type selected, default to Release") set(CMAKE_BUILD_TYPE "Release") endif() # We default to assertions enabled, even in release builds so that we get # more useful error reports from users. option(BYN_ENABLE_ASSERTIONS "Enable assertions" ON) # For git users, attempt to generate a more useful version string if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git) find_package(Git QUIET REQUIRED) execute_process(COMMAND "${GIT_EXECUTABLE}" --git-dir=${CMAKE_CURRENT_SOURCE_DIR}/.git describe --tags --match version_* RESULT_VARIABLE GIT_VERSION_RESULT OUTPUT_VARIABLE GIT_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE) if(${GIT_VERSION_RESULT}) message(WARNING "Error running git describe to determine version") else() set(PROJECT_VERSION "${PROJECT_VERSION} (${GIT_VERSION})") endif() endif() configure_file(config.h.in config.h) # Support functionality. function(ADD_COMPILE_FLAG value) message(STATUS "Building with ${value}") FOREACH(variable CMAKE_C_FLAGS CMAKE_CXX_FLAGS) set(${variable} "${${variable}} ${value}" PARENT_SCOPE) ENDFOREACH(variable) endfunction() function(ADD_CXX_FLAG value) message(STATUS "Building with ${value}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${value}" PARENT_SCOPE) endfunction() function(ADD_DEBUG_COMPILE_FLAG value) if("${CMAKE_BUILD_TYPE}" MATCHES "Debug") message(STATUS "Building with ${value}") endif() FOREACH(variable CMAKE_C_FLAGS_DEBUG CMAKE_CXX_FLAGS_DEBUG) set(${variable} "${${variable}} ${value}" PARENT_SCOPE) ENDFOREACH(variable) endfunction() function(ADD_NONDEBUG_COMPILE_FLAG value) if(NOT "${CMAKE_BUILD_TYPE}" MATCHES "Debug") message(STATUS "Building with ${value}") endif() FOREACH(variable CMAKE_C_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELEASE CMAKE_C_FLAGS_RELWITHDEBINFO CMAKE_CXX_FLAGS_RELWITHDEBINFO CMAKE_C_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_MINSIZEREL) set(${variable} "${${variable}} ${value}" PARENT_SCOPE) ENDFOREACH(variable) endfunction() function(ADD_LINK_FLAG value) message(STATUS "Linking with ${value}") FOREACH(variable CMAKE_EXE_LINKER_FLAGS) set(${variable} "${${variable}} ${value}" PARENT_SCOPE) ENDFOREACH(variable) endfunction() # Options option(BUILD_STATIC_LIB "Build as a static library" OFF) # For now, don't include full DWARF support in JS builds, for size. if (NOT EMSCRIPTEN) option(BUILD_LLVM_DWARF "Enable full DWARF support" ON) if(BUILD_LLVM_DWARF) if(MSVC) ADD_COMPILE_FLAG("/DBUILD_LLVM_DWARF") else() ADD_COMPILE_FLAG("-DBUILD_LLVM_DWARF") endif() endif() endif() # Compiler setup. include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src) if(BUILD_LLVM_DWARF) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/third_party/llvm-project/include) endif() # Add output directory to include path so config.h can be found include_directories(${CMAKE_CURRENT_BINARY_DIR}) # Force output to bin/ and lib/. This is to suppress CMake multigenerator output paths and avoid bin/Debug, bin/Release/ and so on, which is CMake default. FOREACH(SUFFIX "_DEBUG" "_RELEASE" "_RELWITHDEBINFO" "_MINSIZEREL" "") set(CMAKE_RUNTIME_OUTPUT_DIRECTORY${SUFFIX} "${PROJECT_BINARY_DIR}/bin") set(CMAKE_LIBRARY_OUTPUT_DIRECTORY${SUFFIX} "${PROJECT_BINARY_DIR}/lib") set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY${SUFFIX} "${PROJECT_BINARY_DIR}/lib") ENDFOREACH() if(MSVC) if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "19.0") # VS2013 and older explicitly need /arch:sse2 set, VS2015 no longer has that option, but always enabled. add_compile_flag("/arch:sse2") endif() add_compile_flag("/wd4146") # Ignore warning "warning C4146: unary minus operator applied to unsigned type, result still unsigned", this pattern is used somewhat commonly in the code. # 4267 and 4244 are conversion/truncation warnings. We might want to fix these but they are currently pervasive. add_compile_flag("/wd4267") add_compile_flag("/wd4244") # 4722 warns that destructors never return, even with WASM_NORETURN. add_compile_flag("/wd4722") add_compile_flag("/WX-") add_debug_compile_flag("/Od") add_nondebug_compile_flag("/O2") add_compile_flag("/D_CRT_SECURE_NO_WARNINGS") add_compile_flag("/D_SCL_SECURE_NO_WARNINGS") # Visual Studio 2018 15.8 implemented conformant support for std::aligned_storage, but the conformant support is only enabled when the following flag is passed, to avoid # breaking backwards compatibility with code that relied on the non-conformant behavior (the old nonconformant behavior is not used with Binaryen) add_compile_flag("/D_ENABLE_EXTENDED_ALIGNED_STORAGE") # Don't warn about using "strdup" as a reserved name. add_compile_flag("/D_CRT_NONSTDC_NO_DEPRECATE") if(BYN_ENABLE_ASSERTIONS) # On non-Debug builds cmake automatically defines NDEBUG, so we # explicitly undefine it: add_nondebug_compile_flag("/UNDEBUG") # Keep asserts. endif() # Also remove /D NDEBUG to avoid MSVC warnings about conflicting defines. if( NOT CMAKE_BUILD_TYPE MATCHES "Debug" ) foreach(flags_var_to_scrub CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELWITHDEBINFO CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELEASE CMAKE_C_FLAGS_RELWITHDEBINFO CMAKE_C_FLAGS_MINSIZEREL) string(REGEX REPLACE "(^| )[/-]D *NDEBUG($| )" " " "${flags_var_to_scrub}" "${${flags_var_to_scrub}}") # Compile with `/MT` to link against `libcmt.lib`, removing a dependency # on `msvcrt.dll`. May result in slightly larger binaries but they should # be more portable across systems. string(REPLACE "/MD" "/MT" ${flags_var_to_scrub} "${${flags_var_to_scrub}}") endforeach() endif() add_link_flag("/STACK:8388608") if(RUN_STATIC_ANALYZER) add_definitions(/analyze) endif() else() option(ENABLE_WERROR "Enable -Werror" ON) set(THREADS_PREFER_PTHREAD_FLAG ON) set(CMAKE_THREAD_PREFER_PTHREAD ON) find_package(Threads REQUIRED) add_cxx_flag("-std=c++14") if(NOT EMSCRIPTEN) if(CMAKE_SYSTEM_PROCESSOR MATCHES "^i.86$") # wasm doesn't allow for x87 floating point math add_compile_flag("-msse2") add_compile_flag("-mfpmath=sse") elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^armv[2-6]" AND NOT CMAKE_CXX_FLAGS MATCHES "-mfpu=") add_compile_flag("-mfpu=vfpv3") endif() endif() add_compile_flag("-Wall") if(ENABLE_WERROR) add_compile_flag("-Werror") endif() add_compile_flag("-Wextra") add_compile_flag("-Wno-unused-parameter") add_compile_flag("-fno-omit-frame-pointer") # TODO(https://github.com/WebAssembly/binaryen/pull/2314): Remove these two # flags once we resolve the issue. add_compile_flag("-Wno-implicit-int-float-conversion") add_compile_flag("-Wno-unknown-warning-option") add_compile_flag("-Wswitch") # we explicitly expect this in the code if(WIN32) add_compile_flag("-D_GNU_SOURCE") add_link_flag("-Wl,--stack,8388608") elseif(NOT EMSCRIPTEN) add_compile_flag("-fPIC") endif() add_debug_compile_flag("-O0") add_debug_compile_flag("-g3") if(EMSCRIPTEN) # really focus on minimizing output size when compiling sources add_nondebug_compile_flag("-Oz") else() add_nondebug_compile_flag("-O2") endif() if(BYN_ENABLE_ASSERTIONS) # On non-Debug builds cmake automatically defines NDEBUG, so we # explicitly undefine it: add_nondebug_compile_flag("-UNDEBUG") endif() endif() if(EMSCRIPTEN) # link with -O3 for metadce and other powerful optimizations. note that we # must use add_link_options so that this appears after CMake's default -O2 add_link_options("-O3") add_link_flag("-s SINGLE_FILE") add_link_flag("-s ALLOW_MEMORY_GROWTH=1") add_compile_flag("-s DISABLE_EXCEPTION_CATCHING=0") add_link_flag("-s DISABLE_EXCEPTION_CATCHING=0") # make the tools immediately usable on Node.js add_link_flag("-s NODERAWFS") # this can be moved into the fastcomp section once upstream ignores this flag, # https://github.com/emscripten-core/emscripten/pull/9897 add_compile_flag("-Wno-almost-asm") # check for fastcomp by the clang version, which is stuck in fastcomp way # back in the past if(NOT ${CMAKE_CXX_COMPILER_VERSION} STREQUAL "6.0.1") # in opt builds, LTO helps so much (>20%) it's worth slow compile times add_nondebug_compile_flag("-s WASM_OBJECT_FILES=0") endif() endif() # clang doesn't print colored diagnostics when invoked from Ninja if(UNIX AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_GENERATOR STREQUAL "Ninja") add_compile_flag("-fcolor-diagnostics") endif() # Static libraries # Current (partial) dependency structure is as follows: # passes -> wasm -> asmjs -> support # TODO: It's odd that wasm should depend on asmjs, maybe we should fix that. add_subdirectory(src/ir) add_subdirectory(src/asmjs) add_subdirectory(src/cfg) add_subdirectory(src/emscripten-optimizer) add_subdirectory(src/passes) add_subdirectory(src/support) add_subdirectory(src/wasm) add_subdirectory(third_party) # Object files set(binaryen_objs $ $ $ $ $ $ $) IF(BUILD_LLVM_DWARF) SET(binaryen_objs ${binaryen_objs} $) ENDIF() # Sources. set(binaryen_SOURCES src/binaryen-c.cpp ) if(BUILD_STATIC_LIB) message(STATUS "Building libbinaryen as statically linked library.") add_library(binaryen STATIC ${binaryen_SOURCES} ${binaryen_objs}) add_definitions(-DBUILD_STATIC_LIBRARY) else() message(STATUS "Building libbinaryen as shared library.") add_library(binaryen SHARED ${binaryen_SOURCES} ${binaryen_objs}) endif() install(TARGETS binaryen DESTINATION ${CMAKE_INSTALL_LIBDIR}) install(FILES src/binaryen-c.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) set(wasm-shell_SOURCES src/tools/wasm-shell.cpp ) add_executable(wasm-shell ${wasm-shell_SOURCES} ${binaryen_objs}) target_link_libraries(wasm-shell ${CMAKE_THREAD_LIBS_INIT}) set_property(TARGET wasm-shell PROPERTY CXX_STANDARD 14) set_property(TARGET wasm-shell PROPERTY CXX_STANDARD_REQUIRED ON) install(TARGETS wasm-shell DESTINATION ${CMAKE_INSTALL_BINDIR}) set(wasm-opt_SOURCES src/tools/wasm-opt.cpp ) add_executable(wasm-opt ${wasm-opt_SOURCES} ${binaryen_objs}) target_link_libraries(wasm-opt ${CMAKE_THREAD_LIBS_INIT}) set_property(TARGET wasm-opt PROPERTY CXX_STANDARD 14) set_property(TARGET wasm-opt PROPERTY CXX_STANDARD_REQUIRED ON) install(TARGETS wasm-opt DESTINATION ${CMAKE_INSTALL_BINDIR}) set(wasm-metadce_SOURCES src/tools/wasm-metadce.cpp ) add_executable(wasm-metadce ${wasm-metadce_SOURCES} ${binaryen_objs}) target_link_libraries(wasm-metadce ${CMAKE_THREAD_LIBS_INIT}) set_property(TARGET wasm-metadce PROPERTY CXX_STANDARD 14) set_property(TARGET wasm-metadce PROPERTY CXX_STANDARD_REQUIRED ON) install(TARGETS wasm-metadce DESTINATION bin) set(asm2wasm_SOURCES src/tools/asm2wasm.cpp ) add_executable(asm2wasm ${asm2wasm_SOURCES} ${binaryen_objs}) target_link_libraries(asm2wasm ${CMAKE_THREAD_LIBS_INIT}) set_property(TARGET asm2wasm PROPERTY CXX_STANDARD 14) set_property(TARGET asm2wasm PROPERTY CXX_STANDARD_REQUIRED ON) install(TARGETS asm2wasm DESTINATION ${CMAKE_INSTALL_BINDIR}) set(wasm2js_SOURCES src/tools/wasm2js.cpp ) add_executable(wasm2js ${wasm2js_SOURCES} ${binaryen_objs}) target_link_libraries(wasm2js ${CMAKE_THREAD_LIBS_INIT}) set_property(TARGET wasm2js PROPERTY CXX_STANDARD 14) set_property(TARGET wasm2js PROPERTY CXX_STANDARD_REQUIRED ON) install(TARGETS wasm2js DESTINATION ${CMAKE_INSTALL_BINDIR}) set(wasm-emscripten-finalize_SOURCES src/tools/wasm-emscripten-finalize.cpp ) add_executable(wasm-emscripten-finalize ${wasm-emscripten-finalize_SOURCES} ${binaryen_objs}) target_link_libraries(wasm-emscripten-finalize ${CMAKE_THREAD_LIBS_INIT}) set_property(TARGET wasm-emscripten-finalize PROPERTY CXX_STANDARD 14) set_property(TARGET wasm-emscripten-finalize PROPERTY CXX_STANDARD_REQUIRED ON) install(TARGETS wasm-emscripten-finalize DESTINATION ${CMAKE_INSTALL_BINDIR}) set(wasm_as_SOURCES src/tools/wasm-as.cpp ) add_executable(wasm-as ${wasm_as_SOURCES} ${binaryen_objs}) target_link_libraries(wasm-as ${CMAKE_THREAD_LIBS_INIT}) set_property(TARGET wasm-as PROPERTY CXX_STANDARD 14) set_property(TARGET wasm-as PROPERTY CXX_STANDARD_REQUIRED ON) install(TARGETS wasm-as DESTINATION ${CMAKE_INSTALL_BINDIR}) set(wasm_dis_SOURCES src/tools/wasm-dis.cpp ) add_executable(wasm-dis ${wasm_dis_SOURCES} ${binaryen_objs}) target_link_libraries(wasm-dis ${CMAKE_THREAD_LIBS_INIT}) set_property(TARGET wasm-dis PROPERTY CXX_STANDARD 14) set_property(TARGET wasm-dis PROPERTY CXX_STANDARD_REQUIRED ON) install(TARGETS wasm-dis DESTINATION ${CMAKE_INSTALL_BINDIR}) set(wasm-ctor-eval_SOURCES src/tools/wasm-ctor-eval.cpp ) add_executable(wasm-ctor-eval ${wasm-ctor-eval_SOURCES} ${binaryen_objs}) target_link_libraries(wasm-ctor-eval ${CMAKE_THREAD_LIBS_INIT}) set_property(TARGET wasm-ctor-eval PROPERTY CXX_STANDARD 14) set_property(TARGET wasm-ctor-eval PROPERTY CXX_STANDARD_REQUIRED ON) install(TARGETS wasm-ctor-eval DESTINATION bin) set(wasm-reduce_SOURCES src/tools/wasm-reduce.cpp ) add_executable(wasm-reduce ${wasm-reduce_SOURCES} ${binaryen_objs}) target_link_libraries(wasm-reduce ${CMAKE_THREAD_LIBS_INIT}) set_property(TARGET wasm-reduce PROPERTY CXX_STANDARD 14) set_property(TARGET wasm-reduce PROPERTY CXX_STANDARD_REQUIRED ON) install(TARGETS wasm-reduce DESTINATION ${CMAKE_INSTALL_BINDIR}) # binaryen.js # # Note that we can't emit binaryen.js directly, as there is libbinaryen already # declared earlier, so we create binaryen_wasm/js.js, which must then be copied. # Note that SHELL: is needed as otherwise cmake will coalesce -s link flags # in an incorrect way for emscripten. if(EMSCRIPTEN) set(binaryen_emscripten_SOURCES src/binaryen-c.cpp ) # binaryen.js WebAssembly variant add_executable(binaryen_wasm ${binaryen_emscripten_SOURCES}) target_link_libraries(binaryen_wasm wasm asmjs emscripten-optimizer passes ir cfg support wasm) target_link_libraries(binaryen_wasm "-s MODULARIZE_INSTANCE=1") target_link_libraries(binaryen_wasm "-s NO_FILESYSTEM=0") target_link_libraries(binaryen_wasm "-s NODERAWFS=0") target_link_libraries(binaryen_wasm "-s EXPORT_NAME=binaryen") target_link_libraries(binaryen_wasm "--post-js ${CMAKE_CURRENT_SOURCE_DIR}/src/js/binaryen.js-post.js") target_link_libraries(binaryen_wasm optimized "--closure 1") target_link_libraries(binaryen_wasm optimized "--llvm-lto 1") target_link_libraries(binaryen_wasm debug "--profiling") set_property(TARGET binaryen_wasm PROPERTY CXX_STANDARD 14) set_property(TARGET binaryen_wasm PROPERTY CXX_STANDARD_REQUIRED ON) install(TARGETS binaryen_wasm DESTINATION ${CMAKE_INSTALL_BINDIR}) # binaryen.js JavaScript variant add_executable(binaryen_js ${binaryen_emscripten_SOURCES}) target_link_libraries(binaryen_js wasm asmjs emscripten-optimizer passes ir cfg support wasm) target_link_libraries(binaryen_js "-s WASM=0") target_link_libraries(binaryen_js "-s WASM_ASYNC_COMPILATION=0") if(${CMAKE_CXX_COMPILER_VERSION} STREQUAL "6.0.1") # only valid with fastcomp and WASM=0 target_link_libraries(binaryen_js "-s ELIMINATE_DUPLICATE_FUNCTIONS=1") endif() target_link_libraries(binaryen_js "-s MODULARIZE_INSTANCE=1") target_link_libraries(binaryen_js "-s NO_FILESYSTEM=0") target_link_libraries(binaryen_js "-s NODERAWFS=0") target_link_libraries(binaryen_js "-s EXPORT_NAME=binaryen") target_link_libraries(binaryen_js "--post-js ${CMAKE_CURRENT_SOURCE_DIR}/src/js/binaryen.js-post.js") target_link_libraries(binaryen_js optimized "--closure 1") target_link_libraries(binaryen_js optimized "--llvm-lto 1") target_link_libraries(binaryen_js debug "--profiling") target_link_libraries(binaryen_js debug "-s ASSERTIONS") set_property(TARGET binaryen_js PROPERTY CXX_STANDARD 14) set_property(TARGET binaryen_js PROPERTY CXX_STANDARD_REQUIRED ON) install(TARGETS binaryen_js DESTINATION ${CMAKE_INSTALL_BINDIR}) endif() # Testing # # Currently just some very simple smoke tests. enable_testing() add_test(NAME opt-unit COMMAND bin/wasm-opt test/unit.wat --flatten --ssa --metrics -O4 -Os --metrics) add_test(NAME metrics-emcc COMMAND bin/wasm-opt test/emcc_hello_world.fromasm --metrics) add_test(NAME exec-unit COMMAND bin/wasm-opt test/unit.wat --fuzz-exec) add_test(NAME exec-hello COMMAND bin/wasm-opt test/hello_world.wat --fuzz-exec) binaryen-version_91/Contributing.md000066400000000000000000000032251362402614000176520ustar00rootroot00000000000000# Contributing to WebAssembly Interested in participating? Please follow [the same contributing guidelines as the design repository][]. [the same contributing guidelines as the design repository]: https://github.com/WebAssembly/design/blob/master/Contributing.md Also, please be sure to read [the README.md](README.md) for this repository. ## Adding support for new instructions Use this handy checklist to make sure your new instructions are fully supported: - [ ] Instruction class or opcode added to src/wasm.h - [ ] Instruction class added to src/wasm-builder.h - [ ] Instruction class added to src/wasm-traversal.h - [ ] Validation added to src/wasm/wasm-validator.cpp - [ ] Interpretation added to src/wasm-interpreter.h - [ ] Effects handled in src/ir/effects.h - [ ] Precomputing handled in src/passes/Precompute.cpp - [ ] Hashing and comparing in src/ir/ExpressionAnalyzer.cpp - [ ] Parsing added in scripts/gen-s-parser.py, src/wasm-s-parser.h and src/wasm/wasm-s-parser.cpp - [ ] Printing added in src/passes/Print.cpp - [ ] Decoding added in src/wasm-binary.h and src/wasm/wasm-binary.cpp - [ ] Binary writing added in src/wasm-stack.h and src/wasm/wasm-stack.cpp - [ ] Support added in various classes inheriting OverriddenVisitor (and possibly other non-OverriddenVisitor classes as necessary) - [ ] Support added to src/tools/fuzzing.h - [ ] C API support added in src/binaryen-c.h and src/binaryen-c.cpp - [ ] JS API support added in src/js/binaryen.js-post.js - [ ] C API tested in test/example/c-api-kitchen-sink.c - [ ] JS API tested in test/binaryen.js/kitchen-sink.js - [ ] Tests added in test/spec - [ ] Tests added in top-level test/ binaryen-version_91/LICENSE000066400000000000000000000261351362402614000156730ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. binaryen-version_91/README.md000066400000000000000000000474601362402614000161510ustar00rootroot00000000000000[![Build Status](https://travis-ci.org/WebAssembly/binaryen.svg?branch=master)](https://travis-ci.org/WebAssembly/binaryen) [![Windows CI](https://ci.appveyor.com/api/projects/status/nvm9tuwxnup9h5ar/branch/master?svg=true)](https://ci.appveyor.com/project/WebAssembly/binaryen/branch/master) # Binaryen Binaryen is a compiler and toolchain infrastructure library for WebAssembly, written in C++. It aims to make [compiling to WebAssembly] **easy, fast, and effective**: * **Easy**: Binaryen has a simple [C API] in a single header, and can also be [used from JavaScript][JS_API]. It accepts input in [WebAssembly-like form][compile_to_wasm] but also accepts a general [control flow graph] for compilers that prefer that. * **Fast**: Binaryen's internal IR uses compact data structures and is designed for completely parallel codegen and optimization, using all available CPU cores. Binaryen's IR also compiles down to WebAssembly extremely easily and quickly because it is essentially a subset of WebAssembly. * **Effective**: Binaryen's optimizer has many [passes] that can improve code very significantly (e.g. local coloring to coalesce local variables; dead code elimination; precomputing expressions when possible at compile time; etc.). These optimizations aim to make Binaryen powerful enough to be used as a [compiler backend][backend] by itself. One specific area of focus is on WebAssembly-specific optimizations (that general-purpose compilers might not do), which you can think of as wasm [minification] , similar to minification for JavaScript, CSS, etc., all of which are language-specific (an example of such an optimization is block return value generation in `SimplifyLocals`). Compilers built using Binaryen include * [`asm2wasm`](https://github.com/WebAssembly/binaryen/blob/master/src/asm2wasm.h) which compiles asm.js to WebAssembly * [`AssemblyScript`](https://github.com/AssemblyScript/assemblyscript) which compiles TypeScript to Binaryen IR * [`wasm2js`](https://github.com/WebAssembly/binaryen/blob/master/src/wasm2js.h) which compiles WebAssembly to JS * [`Asterius`](https://github.com/tweag/asterius) which compiles Haskell to WebAssembly Binaryen also provides a set of **toolchain utilities** that can * **Parse** and **emit** WebAssembly. In particular this lets you load WebAssembly, optimize it using Binaryen, and re-emit it, thus implementing a wasm-to-wasm optimizer in a single command. * **Interpret** WebAssembly as well as run the WebAssembly spec tests. * Integrate with **[Emscripten](http://emscripten.org)** in order to provide a complete compiler toolchain from C and C++ to WebAssembly. * **Polyfill** WebAssembly by running it in the interpreter compiled to JavaScript, if the browser does not yet have native support (useful for testing). Consult the [contributing instructions](Contributing.md) if you're interested in participating. ## Binaryen IR Binaryen's internal IR is designed to be * **Flexible and fast** for optimization. * **As close as possible to WebAssembly** so it is simple and fast to convert it to and from WebAssembly. There are a few differences between Binaryen IR and the WebAssembly language: * Tree structure * Binaryen IR [is a tree][binaryen_ir], i.e., it has hierarchical structure, for convenience of optimization. This differs from the WebAssembly binary format which is a stack machine. * Consequently Binaryen's text format allows only s-expressions. WebAssembly's official text format is primarily a linear instruction list (with s-expression extensions). Binaryen can't read the linear style, but it can read a wasm text file if it contains only s-expressions. * Binaryen uses Stack IR to optimize "stacky" code (that can't be represented in structured form). * In rare cases stacky code must be represented in Binaryen IR as well, like popping a value in an exception catch. To support that Binaryen IR has `push` and `pop` instructions. * Types and unreachable code * WebAssembly limits block/if/loop types to none and the concrete value types (i32, i64, f32, f64). Binaryen IR has an unreachable type, and it allows block/if/loop to take it, allowing [local transforms that don't need to know the global context][unreachable]. As a result, Binaryen's default text output is not necessarily valid wasm text. (To get valid wasm text, you can do `--generate-stack-ir --print-stack-ir`, which prints Stack IR, this is guaranteed to be valid for wasm parsers.) * Binaryen ignores unreachable code when reading WebAssembly binaries. That means that if you read a wasm file with unreachable code, that code will be discarded as if it were optimized out (often this is what you want anyhow, and optimized programs have no unreachable code anyway, but if you write an unoptimized file and then read it, it may look different). The reason for this behavior is that unreachable code in WebAssembly has corner cases that are tricky to handle in Binaryen IR (it can be very unstructured, and Binaryen IR is more structured than WebAssembly as noted earlier). Note that Binaryen does support unreachable code in .wat text files, since as we saw Binaryen only supports s-expressions there, which are structured. * Blocks * Binaryen IR has only one node that contains a variable-length list of operands: the block. WebAssembly on the other hand allows lists in loops, if arms, and the top level of a function. Binaryen's IR has a single operand for all non-block nodes; this operand may of course be a block. The motivation for this property is that many passes need special code for iterating on lists, so having a single IR node with a list simplifies them. * As in wasm, blocks and loops may have names. Branch targets in the IR are resolved by name (as opposed to nesting depth). This has 2 consequences: * Blocks without names may not be branch targets. * Names are required to be unique. (Reading .wat files with duplicate names is supported; the names are modified when the IR is constructed). * As an optimization, a block that is the child of a loop (or if arm, or function toplevel) and which has no branches targeting it will not be emitted when generating wasm. Instead its list of operands will be directly used in the containing node. Such a block is sometimes called an "implicit block". * Multivalue * Binaryen will not represent multivalue instructions and values directly. Binaryen's main focus is on optimization of wasm, and therefore the question of whether we should have multivalue in the main IR is whether it justifes the extra complexity there. Experiments show that the shrinking of code size thanks to multivalue is useful but small, just 1-3% or so. Given that, we prefer to keep the main IR simple, and focus on multivalue optimizations in Stack IR, which is more suitable for such things. * Binaryen does still need to implement the "ABI" level of multivalue, that is, we need multivalue calls because those may cross module boundaries, and so they are observable externally. To support that, Binaryen may use `push` and `pop` as mentioned earlier; another option is to add LLVM-like `extractvalue/composevalue` instructions. As a result, you might notice that round-trip conversions (wasm => Binaryen IR => wasm) change code a little in some corner cases. * When optimizing Binaryen uses an additional IR, Stack IR (see `src/wasm-stack.h`). Stack IR allows a bunch of optimizations that are tailored for the stack machine form of WebAssembly's binary format (but Stack IR is less efficient for general optimizations than the main Binaryen IR). If you have a wasm file that has been particularly well-optimized, a simple round-trip conversion (just read and write, without optimization) may cause more noticeable differences, as Binaryen fits it into Binaryen IR's more structured format. If you also optimize during the round-trip conversion then Stack IR opts will be run and the final wasm will be better optimized. Notes when working with Binaryen IR: * As mentioned above, Binaryen IR has a tree structure. As a result, each expression should have exactly one parent - you should not "reuse" a node by having it appear more than once in the tree. The motivation for this limitation is that when we optimize we modify nodes, so if they appear more than once in the tree, a change in one place can appear in another incorrectly. * For similar reasons, nodes should not appear in more than one functions. ## Tools This repository contains code that builds the following tools in `bin/`: * **wasm-opt**: Loads WebAssembly and runs Binaryen IR passes on it. * **wasm-as**: Assembles WebAssembly in text format (currently S-Expression format) into binary format (going through Binaryen IR). * **wasm-dis**: Un-assembles WebAssembly in binary format into text format (going through Binaryen IR). * **wasm2js**: A WebAssembly-to-JS compiler. This is used by Emscripten to generate JavaScript as an alternative to WebAssembly. * **wasm-reduce**: A testcase reducer for WebAssembly files. Given a wasm file that is interesting for some reason (say, it crashes a specific VM), wasm-reduce can find a smaller wasm file that has the same property, which is often easier to debug. See the [docs](https://github.com/WebAssembly/binaryen/wiki/Fuzzing#reducing) for more details. * **wasm-shell**: A shell that can load and interpret WebAssembly code. It can also run the spec test suite. * **wasm-emscripten-finalize**: Takes a wasm binary produced by llvm+lld and performs emscripten-specific passes over it. * **asm2wasm**: An asm.js-to-WebAssembly compiler, using Emscripten's asm optimizer infrastructure. This is used by Emscripten in Binaryen mode when it uses Emscripten's fastcomp asm.js backend. * **wasm-ctor-eval**: A tool that can execute C++ global constructors ahead of time. Used by Emscripten. * **binaryen.js**: A standalone JavaScript library that exposes Binaryen methods for [creating and optimizing WASM modules](https://github.com/WebAssembly/binaryen/blob/master/test/binaryen.js/hello-world.js). For builds, see [binaryen.js on npm](https://www.npmjs.com/package/binaryen) (or download it directly from [github](https://raw.githubusercontent.com/AssemblyScript/binaryen.js/master/index.js), [rawgit](https://cdn.rawgit.com/AssemblyScript/binaryen.js/master/index.js), or [unpkg](https://unpkg.com/binaryen@latest/index.js)). Usage instructions for each are below. ## Building ``` cmake . && make ``` Note that you can also use `ninja` as your generator: `cmake -G Ninja . && ninja` * A C++11 compiler is required. * The JavaScript components can be built using `build-js.sh`, see notes inside. Normally this is not needed as builds are provided in this repo already. If you also want to compile C/C++ to WebAssembly (and not just asm.js to WebAssembly), you'll need Emscripten. You'll need the `incoming` branch there (which you can get via [the SDK](http://kripken.github.io/emscripten-site/docs/getting_started/downloads.html)), for more details see [the wiki](https://github.com/kripken/emscripten/wiki/WebAssembly). ### Visual C++ 1. Using the Microsoft Visual Studio Installer, install the "Visual C++ tools for CMake" component. 1. Generate the projects: ``` mkdir build cd build "%VISUAL_STUDIO_ROOT%\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe" .. ``` Substitute VISUAL_STUDIO_ROOT with the path to your Visual Studio installation. In case you are using the Visual Studio Build Tools, the path will be "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools". 1. From the Developer Command Prompt, build the desired projects: ``` msbuild binaryen.vcxproj ``` CMake generates a project named "ALL_BUILD.vcxproj" for conveniently building all the projects. ## Running ### wasm-opt Run ```` bin/wasm-opt [.wasm or .wat file] [options] [passes, see --help] [--help] ```` The wasm optimizer receives WebAssembly as input, and can run transformation passes on it, as well as print it (before and/or after the transformations). For example, try ```` bin/wasm-opt test/passes/lower-if-else.wat --print ```` That will pretty-print out one of the test cases in the test suite. To run a transformation pass on it, try ```` bin/wasm-opt test/passes/lower-if-else.wat --print --lower-if-else ```` The `lower-if-else` pass lowers if-else into a block and a break. You can see the change the transformation causes by comparing the output of the two print commands. It's easy to add your own transformation passes to the shell, just add `.cpp` files into `src/passes`, and rebuild the shell. For example code, take a look at the [`lower-if-else` pass](https://github.com/WebAssembly/binaryen/blob/master/src/passes/LowerIfElse.cpp). Some more notes: * See `bin/wasm-opt --help` for the full list of options and passes. * Passing `--debug` will emit some debugging info. ### wasm2js Run ``` bin/wasm2js [input.wasm file] ``` This will print out JavaScript to the console. For example, try ``` $ bin/wasm2js test/hello_world.wat ``` That output contains ``` function add(x, y) { x = x | 0; y = y | 0; return x + y | 0 | 0; } ``` as a translation of ``` (func $add (; 0 ;) (type $0) (param $x i32) (param $y i32) (result i32) (i32.add (local.get $x) (local.get $y) ) ) ``` wasm2js's output is in ES6 module format - basically, it converts a wasm module into an ES6 module (to run on older browsers and Node.js versions you can use Babel etc. to convert it to ES5). Let's look at a full example of calling that hello world wat; first, create the main JS file: ```javascript // main.mjs import { add } from "./hello_world.mjs"; console.log('the sum of 1 and 2 is:', add(1, 2)); ``` The run this (note that you need a new enough Node.js with ES6 module support): ```shell $ bin/wasm2js test/hello_world.wat -o hello_world.mjs $ node --experimental-modules main.mjs the sum of 1 and 2 is: 3 ``` Things keep to in mind with wasm2js's output: * You should run wasm2js with optimizations for release builds, using `-O` or another optimization level. That will optimize along the entire pipeline (wasm and JS). It won't do everything a JS minifer would, though, like minify whitespace, so you should still run a normal JS minifer afterwards. * It is not possible to match WebAssembly semantics 100% precisely with fast JavaScript code. For example, every load and store may trap, and to make JavaScript do the same we'd need to add checks everywhere, which would be large and slow. Instead, wasm2js assumes loads and stores do not trap, that int/float conversions do not trap, and so forth. There may also be slight differences in corner cases of conversions, like non-trapping float to int. ### asm2wasm Run ``` bin/asm2wasm [input.asm.js file] ``` This will print out a WebAssembly module in s-expression format to the console. For example, try ``` $ bin/asm2wasm test/hello_world.asm.js ``` That input file contains ```javascript function () { "use asm"; function add(x, y) { x = x | 0; y = y | 0; return x + y | 0; } return { add: add }; } ``` You should see something like this: ![example output](https://raw.github.com/WebAssembly/wasm-emscripten/master/media/example.png) By default you should see pretty colors as in that image. Set `COLORS=0` in the env to disable colors if you prefer that. On Linux and Mac, you can set `COLORS=1` in the env to force colors (useful when piping to `more`, for example). For Windows, pretty colors are only available when `stdout/stderr` are not redirected/piped. Pass `--debug` on the command line to see debug info, about asm.js functions as they are parsed, etc. ### C/C++ Source ⇒ asm2wasm ⇒ WebAssembly When using `emcc` with the `BINARYEN` option, it will use Binaryen to build to WebAssembly. This lets you compile C and C++ to WebAssembly, with emscripten using asm.js internally as a build step. Since emscripten's asm.js generation is very stable, and asm2wasm is a fairly simple process, this method of compiling C and C++ to WebAssembly is usable already. See the [emscripten wiki](https://github.com/kripken/emscripten/wiki/WebAssembly) for more details about how to use it. ## Testing ``` ./check.py ``` (or `python check.py`) will run `wasm-shell`, `wasm-opt`, `asm2wasm`, etc. on the testcases in `test/`, and verify their outputs. The `check.py` script supports some options: ``` ./check.py [--interpreter=/path/to/interpreter] [TEST1] [TEST2].. ``` * If an interpreter is provided, we run the output through it, checking for parse errors. * If tests are provided, we run exactly those. If none are provided, we run them all. To see what tests are available, run `./check.py --list-suites`. * Some tests require `emcc` or `nodejs` in the path. They will not run if the tool cannot be found, and you'll see a warning. * We have tests from upstream in `tests/spec`, in git submodules. Running `./check.py` should update those. ## Design Principles * **Interned strings for names**: It's very convenient to have names on nodes, instead of just numeric indices etc. To avoid most of the performance difference between strings and numeric indices, all strings are interned, which means there is a single copy of each string in memory, string comparisons are just a pointer comparison, etc. * **Allocate in arenas**: Based on experience with other optimizing/transformating toolchains, it's not worth the overhead to carefully track memory of individual nodes. Instead, we allocate all elements of a module in an arena, and the entire arena can be freed when the module is no longer needed. ## FAQ * Why the weird name for the project? "Binaryen" is a combination of **binary** - since WebAssembly is a binary format for the web - and **Emscripten** - with which it can integrate in order to compile C and C++ all the way to WebAssembly, via asm.js. Binaryen began as Emscripten's WebAssembly processing library (`wasm-emscripten`). "Binaryen" is pronounced [in the same manner](http://www.makinggameofthrones.com/production-diary/2011/2/11/official-pronunciation-guide-for-game-of-thrones.html) as "[Targaryen](https://en.wikipedia.org/wiki/List_of_A_Song_of_Ice_and_Fire_characters#House_Targaryen)": *bi-NAIR-ee-in*. Or something like that? Anyhow, however Targaryen is correctly pronounced, they should rhyme. Aside from pronunciation, the Targaryen house words, "Fire and Blood", have also inspired Binaryen's: "Code and Bugs." * Does it compile under Windows and/or Visual Studio? Yes, it does. Here's a step-by-step [tutorial][win32] on how to compile it under **Windows 10 x64** with with **CMake** and **Visual Studio 2015**. Help would be appreciated on Windows and OS X as most of the core devs are on Linux. [compiling to WebAssembly]: https://github.com/WebAssembly/binaryen/wiki/Compiling-to-WebAssembly-with-Binaryen [win32]: https://github.com/brakmic/bazaar/blob/master/webassembly/COMPILING_WIN32.md [C API]: https://github.com/WebAssembly/binaryen/wiki/Compiling-to-WebAssembly-with-Binaryen#c-api-1 [control flow graph]: https://github.com/WebAssembly/binaryen/wiki/Compiling-to-WebAssembly-with-Binaryen#cfg-api [JS_API]: https://github.com/WebAssembly/binaryen/wiki/binaryen.js-API [compile_to_wasm]: https://github.com/WebAssembly/binaryen/wiki/Compiling-to-WebAssembly-with-Binaryen#what-do-i-need-to-have-in-order-to-use-binaryen-to-compile-to-webassembly [passes]: https://github.com/WebAssembly/binaryen/tree/master/src/passes [backend]: https://kripken.github.io/talks/binaryen.html#/9 [minification]: https://kripken.github.io/talks/binaryen.html#/2 [unreachable]: https://github.com/WebAssembly/binaryen/issues/903 [binaryen_ir]: https://github.com/WebAssembly/binaryen/issues/663 binaryen-version_91/auto_update_tests.py000077500000000000000000000347421362402614000210020ustar00rootroot00000000000000#!/usr/bin/env python3 # # Copyright 2015 WebAssembly Community Group participants # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import os import shutil import subprocess import sys from collections import OrderedDict from scripts.test import lld from scripts.test import shared from scripts.test import support from scripts.test import wasm2js def update_asm_js_tests(): print('[ processing and updating testcases... ]\n') for asm in shared.get_tests(shared.options.binaryen_test, ['.asm.js']): basename = os.path.basename(asm) for precise in [0, 1, 2]: for opts in [1, 0]: cmd = shared.ASM2WASM + [asm] if 'threads' in basename: cmd += ['--enable-threads'] wasm = asm.replace('.asm.js', '.fromasm') if not precise: cmd += ['--trap-mode=allow', '--ignore-implicit-traps'] wasm += '.imprecise' elif precise == 2: cmd += ['--trap-mode=clamp'] wasm += '.clamp' if not opts: wasm += '.no-opts' if precise: cmd += ['-O0'] # test that -O0 does nothing else: cmd += ['-O'] if 'debugInfo' in basename: cmd += ['-g'] if 'noffi' in basename: cmd += ['--no-legalize-javascript-ffi'] if precise and opts: # test mem init importing open('a.mem', 'wb').write(bytes(basename, 'utf-8')) cmd += ['--mem-init=a.mem'] if basename[0] == 'e': cmd += ['--mem-base=1024'] if '4GB' in basename: cmd += ['--mem-max=4294967296'] if 'i64' in basename or 'wasm-only' in basename or 'noffi' in basename: cmd += ['--wasm-only'] print(' '.join(cmd)) actual = support.run_command(cmd) with open(os.path.join(shared.options.binaryen_test, wasm), 'w') as o: o.write(actual) if 'debugInfo' in basename: cmd += ['--source-map', os.path.join(shared.options.binaryen_test, wasm + '.map'), '-o', 'a.wasm'] support.run_command(cmd) def update_wasm_opt_tests(): print('\n[ checking wasm-opt -o notation... ]\n') wast = os.path.join(shared.options.binaryen_test, 'hello_world.wat') cmd = shared.WASM_OPT + [wast, '-o', 'a.wast', '-S'] support.run_command(cmd) open(wast, 'w').write(open('a.wast').read()) print('\n[ checking wasm-opt parsing & printing... ]\n') for t in shared.get_tests(shared.get_test_dir('print'), ['.wast']): print('..', os.path.basename(t)) wasm = t.replace('.wast', '') cmd = shared.WASM_OPT + [t, '--print', '-all'] print(' ', ' '.join(cmd)) actual = subprocess.check_output(cmd) print(cmd, actual) with open(wasm + '.txt', 'wb') as o: o.write(actual) cmd = shared.WASM_OPT + [t, '--print-minified', '-all'] print(' ', ' '.join(cmd)) actual = subprocess.check_output(cmd) with open(wasm + '.minified.txt', 'wb') as o: o.write(actual) print('\n[ checking wasm-opt passes... ]\n') for t in shared.get_tests(shared.get_test_dir('passes'), ['.wast', '.wasm']): print('..', os.path.basename(t)) binary = t.endswith('.wasm') base = os.path.basename(t).replace('.wast', '').replace('.wasm', '') passname = base passes_file = os.path.join(shared.get_test_dir('passes'), passname + '.passes') if os.path.exists(passes_file): passname = open(passes_file).read().strip() opts = [('--' + p if not p.startswith('O') and p != 'g' else '-' + p) for p in passname.split('_')] actual = '' for module, asserts in support.split_wast(t): assert len(asserts) == 0 support.write_wast('split.wast', module) cmd = shared.WASM_OPT + opts + ['split.wast'] if 'noprint' not in t: cmd.append('--print') actual += support.run_command(cmd) with open(os.path.join(shared.options.binaryen_test, 'passes', base + ('.bin' if binary else '') + '.txt'), 'w') as o: o.write(actual) if 'emit-js-wrapper' in t: with open('a.js') as i: with open(t + '.js', 'w') as o: o.write(i.read()) if 'emit-spec-wrapper' in t: with open('a.wat') as i: with open(t + '.wat', 'w') as o: o.write(i.read()) print('\n[ checking wasm-opt testcases... ]\n') for t in shared.get_tests(shared.options.binaryen_test, ['.wast']): print('..', os.path.basename(t)) f = t + '.from-wast' cmd = shared.WASM_OPT + [t, '--print', '-all'] actual = support.run_command(cmd) actual = actual.replace('printing before:\n', '') open(f, 'w').write(actual) print('\n[ checking wasm-opt debugInfo read-write... ]\n') for t in shared.get_tests(shared.options.binaryen_test, ['.fromasm']): if 'debugInfo' not in t: continue print('..', os.path.basename(t)) f = t + '.read-written' support.run_command(shared.WASM_AS + [t, '--source-map=a.map', '-o', 'a.wasm', '-g']) support.run_command(shared.WASM_OPT + ['a.wasm', '--input-source-map=a.map', '-o', 'b.wasm', '--output-source-map=b.map', '-g']) actual = support.run_command(shared.WASM_DIS + ['b.wasm', '--source-map=b.map']) open(f, 'w').write(actual) def update_bin_fmt_tests(): print('\n[ checking binary format testcases... ]\n') for wast in shared.get_tests(shared.options.binaryen_test, ['.wast']): for debug_info in [0, 1]: cmd = shared.WASM_AS + [wast, '-o', 'a.wasm', '-all'] if debug_info: cmd += ['-g'] print(' '.join(cmd)) if os.path.exists('a.wasm'): os.unlink('a.wasm') subprocess.check_call(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) assert os.path.exists('a.wasm') cmd = shared.WASM_DIS + ['a.wasm', '-o', 'a.wast'] print(' '.join(cmd)) if os.path.exists('a.wast'): os.unlink('a.wast') subprocess.check_call(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) assert os.path.exists('a.wast') actual = open('a.wast').read() binary_file = wast + '.fromBinary' if not debug_info: binary_file += '.noDebugInfo' with open(binary_file, 'w') as o: o.write(actual) def update_example_tests(): print('\n[ checking example testcases... ]\n') for t in shared.get_tests(shared.get_test_dir('example')): basename = os.path.basename(t) output_file = os.path.join(shared.options.binaryen_bin, 'example') libdir = os.path.join(shared.BINARYEN_INSTALL_DIR, 'lib') cmd = ['-I' + os.path.join(shared.options.binaryen_root, 'src'), '-g', '-pthread', '-o', output_file] if t.endswith('.txt'): # check if there is a trace in the file, if so, we should build it out = subprocess.Popen([os.path.join(shared.options.binaryen_root, 'scripts', 'clean_c_api_trace.py'), t], stdout=subprocess.PIPE).communicate()[0] if len(out) == 0: print(' (no trace in ', basename, ')') continue print(' (will check trace in ', basename, ')') src = 'trace.cpp' with open(src, 'wb') as o: o.write(out) expected = t + '.txt' else: src = t expected = os.path.splitext(t)[0] + '.txt' if not src.endswith(('.c', '.cpp')): continue # build the C file separately extra = [os.environ.get('CC') or 'gcc', src, '-c', '-o', 'example.o', '-I' + os.path.join(shared.options.binaryen_root, 'src'), '-g', '-L' + libdir, '-pthread'] print('build: ', ' '.join(extra)) if src.endswith('.cpp'): extra += ['-std=c++14'] print(os.getcwd()) subprocess.check_call(extra) # Link against the binaryen C library DSO, using rpath cmd = ['example.o', '-L' + libdir, '-lbinaryen', '-Wl,-rpath,' + os.path.abspath(libdir)] + cmd print(' ', basename, src, expected) if os.environ.get('COMPILER_FLAGS'): for f in os.environ.get('COMPILER_FLAGS').split(' '): cmd.append(f) cmd = [os.environ.get('CXX') or 'g++', '-std=c++14'] + cmd try: print('link: ', ' '.join(cmd)) subprocess.check_call(cmd) print('run...', output_file) proc = subprocess.Popen([output_file], stdout=subprocess.PIPE, stderr=subprocess.PIPE) actual, err = proc.communicate() assert proc.returncode == 0, [proc.returncode, actual, err] with open(expected, 'wb') as o: o.write(actual) finally: os.remove(output_file) if sys.platform == 'darwin': # Also removes debug directory produced on Mac OS shutil.rmtree(output_file + '.dSYM') def update_wasm_dis_tests(): print('\n[ checking wasm-dis on provided binaries... ]\n') for t in shared.get_tests(shared.options.binaryen_test, ['.wasm']): print('..', os.path.basename(t)) cmd = shared.WASM_DIS + [t] if os.path.isfile(t + '.map'): cmd += ['--source-map', t + '.map'] actual = support.run_command(cmd) open(t + '.fromBinary', 'w').write(actual) def update_binaryen_js_tests(): if not (shared.MOZJS or shared.NODEJS): print('no vm to run binaryen.js tests') return if not os.path.exists(shared.BINARYEN_JS): print('no binaryen.js build to test') return print('\n[ checking binaryen.js testcases... ]\n') node_has_wasm = shared.NODEJS and support.node_has_webassembly(shared.NODEJS) for s in shared.get_tests(shared.get_test_dir('binaryen.js'), ['.js']): basename = os.path.basename(s) print(basename) f = open('a.js', 'w') f.write(open(shared.BINARYEN_JS).read()) test_src = open(s).read() f.write(support.js_test_wrap().replace('%TEST%', test_src)) f.close() if shared.MOZJS or node_has_wasm or 'WebAssembly.' not in test_src: cmd = [shared.MOZJS or shared.NODEJS, 'a.js'] if 'fatal' not in basename: out = support.run_command(cmd, stderr=subprocess.STDOUT) else: # expect an error - the specific error code will depend on the vm out = support.run_command(cmd, stderr=subprocess.STDOUT, expected_status=None) with open(s + '.txt', 'w') as o: o.write(out) else: print('Skipping ' + basename + ' because WebAssembly might not be supported') def update_ctor_eval_tests(): print('\n[ checking wasm-ctor-eval... ]\n') for t in shared.get_tests(shared.get_test_dir('ctor-eval'), ['.wast', '.wasm']): print('..', os.path.basename(t)) ctors = open(t + '.ctors').read().strip() cmd = shared.WASM_CTOR_EVAL + [t, '-all', '-o', 'a.wast', '-S', '--ctors', ctors] support.run_command(cmd) actual = open('a.wast').read() out = t + '.out' with open(out, 'w') as o: o.write(actual) def update_metadce_tests(): print('\n[ checking wasm-metadce... ]\n') for t in shared.get_tests(shared.get_test_dir('metadce'), ['.wast', '.wasm']): print('..', os.path.basename(t)) graph = t + '.graph.txt' cmd = shared.WASM_METADCE + [t, '--graph-file=' + graph, '-o', 'a.wast', '-S', '-all'] stdout = support.run_command(cmd) actual = open('a.wast').read() out = t + '.dced' with open(out, 'w') as o: o.write(actual) with open(out + '.stdout', 'w') as o: o.write(stdout) def update_reduce_tests(): if not shared.has_shell_timeout(): return print('\n[ checking wasm-reduce ]\n') for t in shared.get_tests(shared.get_test_dir('reduce'), ['.wast']): print('..', os.path.basename(t)) # convert to wasm support.run_command(shared.WASM_AS + [t, '-o', 'a.wasm']) print(support.run_command(shared.WASM_REDUCE + ['a.wasm', '--command=%s b.wasm --fuzz-exec' % shared.WASM_OPT[0], '-t', 'b.wasm', '-w', 'c.wasm'])) expected = t + '.txt' support.run_command(shared.WASM_DIS + ['c.wasm', '-o', expected]) def update_spec_tests(): print('\n[ updating wasm-shell spec testcases... ]\n') for t in shared.options.spec_tests: print('..', os.path.basename(t)) cmd = shared.WASM_SHELL + [t] expected = os.path.join(shared.get_test_dir('spec'), 'expected-output', os.path.basename(t) + '.log') if os.path.isfile(expected): stdout = support.run_command(cmd, stderr=subprocess.PIPE) with open(expected, 'w') as o: o.write(stdout) TEST_SUITES = OrderedDict([ ('wasm-opt', update_wasm_opt_tests), ('asm2wasm', update_asm_js_tests), ('wasm-dis', update_wasm_dis_tests), ('example', update_example_tests), ('ctor-eval', update_ctor_eval_tests), ('wasm-metadce', update_metadce_tests), ('wasm-reduce', update_reduce_tests), ('spec', update_spec_tests), ('binaryenjs', update_binaryen_js_tests), ('lld', lld.update_lld_tests), ('wasm2js', wasm2js.update_wasm2js_tests), ('binfmt', update_bin_fmt_tests), ]) def main(): if shared.options.list_suites: for suite in TEST_SUITES.keys(): print(suite) return 0 for test in shared.requested or TEST_SUITES.keys(): TEST_SUITES[test]() print('\n[ success! ]') if __name__ == '__main__': sys.exit(main()) binaryen-version_91/check.py000077500000000000000000000526131362402614000163200ustar00rootroot00000000000000#!/usr/bin/env python3 # # Copyright 2015 WebAssembly Community Group participants # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import glob import os import shutil import subprocess import sys import unittest from collections import OrderedDict from scripts.test import asm2wasm from scripts.test import binaryenjs from scripts.test import lld from scripts.test import shared from scripts.test import support from scripts.test import wasm2js if shared.options.interpreter: print('[ using wasm interpreter at "%s" ]' % shared.options.interpreter) assert os.path.exists(shared.options.interpreter), 'interpreter not found' def get_changelog_version(): with open(os.path.join(shared.options.binaryen_root, 'CHANGELOG.md')) as f: lines = f.readlines() lines = [l for l in lines if len(l.split()) == 1] lines = [l for l in lines if l.startswith('v')] version = lines[0][1:] print("Parsed CHANGELOG.md version: %s" % version) return int(version) def run_help_tests(): print('[ checking --help is useful... ]\n') not_executable_suffix = ['.txt', '.js', '.ilk', '.pdb', '.dll', '.wasm', '.manifest'] bin_files = [os.path.join(shared.options.binaryen_bin, f) for f in os.listdir(shared.options.binaryen_bin)] executables = [f for f in bin_files if os.path.isfile(f) and not any(f.endswith(s) for s in not_executable_suffix)] executables = sorted(executables) assert len(executables) for e in executables: print('.. %s --help' % e) out, err = subprocess.Popen([e, '--help'], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() out = out.decode('utf-8') err = err.decode('utf-8') assert len(err) == 0, 'Expected no stderr, got:\n%s' % err assert os.path.basename(e).replace('.exe', '') in out, 'Expected help to contain program name, got:\n%s' % out assert len(out.split('\n')) > 8, 'Expected some help, got:\n%s' % out print('[ checking --version ... ]\n') changelog_version = get_changelog_version() for e in executables: print('.. %s --version' % e) out, err = subprocess.Popen([e, '--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() out = out.decode('utf-8') err = err.decode('utf-8') assert len(err) == 0, 'Expected no stderr, got:\n%s' % err assert os.path.basename(e).replace('.exe', '') in out, 'Expected version to contain program name, got:\n%s' % out assert len(out.strip().splitlines()) == 1, 'Expected only version info, got:\n%s' % out parts = out.split() assert parts[1] == 'version' version = int(parts[2]) assert version == changelog_version def run_wasm_opt_tests(): print('\n[ checking wasm-opt -o notation... ]\n') for extra_args in [[], ['--no-validation']]: wast = os.path.join(shared.options.binaryen_test, 'hello_world.wat') shared.delete_from_orbit('a.wat') out = 'a.wat' cmd = shared.WASM_OPT + [wast, '-o', out, '-S'] + extra_args support.run_command(cmd) shared.fail_if_not_identical_to_file(open(out).read(), wast) print('\n[ checking wasm-opt binary reading/writing... ]\n') shutil.copyfile(os.path.join(shared.options.binaryen_test, 'hello_world.wat'), 'a.wat') shared.delete_from_orbit('a.wasm') shared.delete_from_orbit('b.wast') support.run_command(shared.WASM_OPT + ['a.wat', '-o', 'a.wasm']) assert open('a.wasm', 'rb').read()[0] == 0, 'we emit binary by default' support.run_command(shared.WASM_OPT + ['a.wasm', '-o', 'b.wast', '-S']) assert open('b.wast', 'rb').read()[0] != 0, 'we emit text with -S' print('\n[ checking wasm-opt passes... ]\n') for t in shared.get_tests(shared.get_test_dir('passes'), ['.wast', '.wasm']): print('..', os.path.basename(t)) binary = '.wasm' in t base = os.path.basename(t).replace('.wast', '').replace('.wasm', '') passname = base passes_file = os.path.join(shared.get_test_dir('passes'), passname + '.passes') if os.path.exists(passes_file): passname = open(passes_file).read().strip() opts = [('--' + p if not p.startswith('O') and p != 'g' else '-' + p) for p in passname.split('_')] actual = '' for module, asserts in support.split_wast(t): assert len(asserts) == 0 support.write_wast('split.wast', module) cmd = shared.WASM_OPT + opts + ['split.wast'] if 'noprint' not in t: cmd.append('--print') curr = support.run_command(cmd) actual += curr # also check debug mode output is valid debugged = support.run_command(cmd + ['--debug'], stderr=subprocess.PIPE) shared.fail_if_not_contained(actual, debugged) # also check pass-debug mode def check(): pass_debug = support.run_command(cmd) shared.fail_if_not_identical(curr, pass_debug) shared.with_pass_debug(check) expected_file = os.path.join(shared.get_test_dir('passes'), base + ('.bin' if binary else '') + '.txt') shared.fail_if_not_identical_to_file(actual, expected_file) if 'emit-js-wrapper' in t: with open('a.js') as actual: shared.fail_if_not_identical_to_file(actual.read(), t + '.js') if 'emit-spec-wrapper' in t: with open('a.wat') as actual: shared.fail_if_not_identical_to_file(actual.read(), t + '.wat') print('\n[ checking wasm-opt parsing & printing... ]\n') for t in shared.get_tests(shared.get_test_dir('print'), ['.wast']): print('..', os.path.basename(t)) wasm = os.path.basename(t).replace('.wast', '') cmd = shared.WASM_OPT + [t, '--print', '-all'] print(' ', ' '.join(cmd)) actual, err = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True).communicate() expected_file = os.path.join(shared.get_test_dir('print'), wasm + '.txt') shared.fail_if_not_identical_to_file(actual, expected_file) cmd = shared.WASM_OPT + [os.path.join(shared.get_test_dir('print'), t), '--print-minified', '-all'] print(' ', ' '.join(cmd)) actual, err = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True).communicate() shared.fail_if_not_identical(actual.strip(), open(os.path.join(shared.get_test_dir('print'), wasm + '.minified.txt')).read().strip()) print('\n[ checking wasm-opt testcases... ]\n') for t in shared.get_tests(shared.options.binaryen_test, ['.wast']): print('..', os.path.basename(t)) f = t + '.from-wast' cmd = shared.WASM_OPT + [t, '--print', '-all'] actual = support.run_command(cmd) actual = actual.replace('printing before:\n', '') shared.fail_if_not_identical_to_file(actual, f) # FIXME Remove this condition after nullref is implemented in V8 if 'reference-types.wast' not in t: shared.binary_format_check(t, wasm_as_args=['-g']) # test with debuginfo shared.binary_format_check(t, wasm_as_args=[], binary_suffix='.fromBinary.noDebugInfo') # test without debuginfo shared.minify_check(t) print('\n[ checking wasm-opt debugInfo read-write... ]\n') for t in shared.get_tests(shared.options.binaryen_test, ['.fromasm']): if 'debugInfo' not in t: continue print('..', os.path.basename(t)) f = t + '.read-written' support.run_command(shared.WASM_AS + [t, '--source-map=a.map', '-o', 'a.wasm', '-g']) support.run_command(shared.WASM_OPT + ['a.wasm', '--input-source-map=a.map', '-o', 'b.wasm', '--output-source-map=b.map', '-g']) actual = support.run_command(shared.WASM_DIS + ['b.wasm', '--source-map=b.map']) shared.fail_if_not_identical_to_file(actual, f) def run_wasm_dis_tests(): print('\n[ checking wasm-dis on provided binaries... ]\n') for t in shared.get_tests(shared.options.binaryen_test, ['.wasm']): print('..', os.path.basename(t)) cmd = shared.WASM_DIS + [t] if os.path.isfile(t + '.map'): cmd += ['--source-map', t + '.map'] actual = support.run_command(cmd) shared.fail_if_not_identical_to_file(actual, t + '.fromBinary') # also verify there are no validation errors def check(): cmd = shared.WASM_OPT + [t, '-all'] support.run_command(cmd) shared.with_pass_debug(check) shared.validate_binary(t) def run_crash_tests(): print("\n[ checking we don't crash on tricky inputs... ]\n") for t in shared.get_tests(shared.get_test_dir('crash'), ['.wast', '.wasm']): print('..', os.path.basename(t)) cmd = shared.WASM_OPT + [t] # expect a parse error to be reported support.run_command(cmd, expected_err='parse exception:', err_contains=True, expected_status=1) def run_dylink_tests(): print("\n[ we emit dylink sections properly... ]\n") dylink_tests = glob.glob(os.path.join(shared.options.binaryen_test, 'dylib*.wasm')) for t in sorted(dylink_tests): print('..', os.path.basename(t)) cmd = shared.WASM_OPT + [t, '-o', 'a.wasm'] support.run_command(cmd) with open('a.wasm', 'rb') as output: index = output.read().find(b'dylink') print(' ', index) assert index == 11, 'dylink section must be first, right after the magic number etc.' def run_ctor_eval_tests(): print('\n[ checking wasm-ctor-eval... ]\n') for t in shared.get_tests(shared.get_test_dir('ctor-eval'), ['.wast', '.wasm']): print('..', os.path.basename(t)) ctors = open(t + '.ctors').read().strip() cmd = shared.WASM_CTOR_EVAL + [t, '-all', '-o', 'a.wat', '-S', '--ctors', ctors] support.run_command(cmd) actual = open('a.wat').read() out = t + '.out' shared.fail_if_not_identical_to_file(actual, out) def run_wasm_metadce_tests(): print('\n[ checking wasm-metadce ]\n') for t in shared.get_tests(shared.get_test_dir('metadce'), ['.wast', '.wasm']): print('..', os.path.basename(t)) graph = t + '.graph.txt' cmd = shared.WASM_METADCE + [t, '--graph-file=' + graph, '-o', 'a.wat', '-S', '-all'] stdout = support.run_command(cmd) expected = t + '.dced' with open('a.wat') as seen: shared.fail_if_not_identical_to_file(seen.read(), expected) shared.fail_if_not_identical_to_file(stdout, expected + '.stdout') def run_wasm_reduce_tests(): if not shared.has_shell_timeout(): print('\n[ skipping wasm-reduce testcases]\n') return print('\n[ checking wasm-reduce testcases]\n') # fixed testcases for t in shared.get_tests(shared.get_test_dir('reduce'), ['.wast']): print('..', os.path.basename(t)) # convert to wasm support.run_command(shared.WASM_AS + [t, '-o', 'a.wasm']) support.run_command(shared.WASM_REDUCE + ['a.wasm', '--command=%s b.wasm --fuzz-exec -all' % shared.WASM_OPT[0], '-t', 'b.wasm', '-w', 'c.wasm', '--timeout=4']) expected = t + '.txt' support.run_command(shared.WASM_DIS + ['c.wasm', '-o', 'a.wat']) with open('a.wat') as seen: shared.fail_if_not_identical_to_file(seen.read(), expected) # run on a nontrivial fuzz testcase, for general coverage # this is very slow in ThreadSanitizer, so avoid it there if 'fsanitize=thread' not in str(os.environ): print('\n[ checking wasm-reduce fuzz testcase ]\n') support.run_command(shared.WASM_OPT + [os.path.join(shared.options.binaryen_test, 'unreachable-import_wasm-only.asm.js'), '-ttf', '-Os', '-o', 'a.wasm', '-all']) before = os.stat('a.wasm').st_size support.run_command(shared.WASM_REDUCE + ['a.wasm', '--command=%s b.wasm --fuzz-exec -all' % shared.WASM_OPT[0], '-t', 'b.wasm', '-w', 'c.wasm']) after = os.stat('c.wasm').st_size # This number is a custom threshold to check if we have shrunk the # output sufficiently assert after < 0.75 * before, [before, after] def run_spec_tests(): print('\n[ checking wasm-shell spec testcases... ]\n') for wast in shared.options.spec_tests: print('..', os.path.basename(wast)) def run_spec_test(wast): cmd = shared.WASM_SHELL + [wast] return support.run_command(cmd, stderr=subprocess.PIPE) def run_opt_test(wast): # check optimization validation cmd = shared.WASM_OPT + [wast, '-O', '-all'] support.run_command(cmd) def check_expected(actual, expected): if expected and os.path.exists(expected): expected = open(expected).read() print(' (using expected output)') actual = actual.strip() expected = expected.strip() if actual != expected: shared.fail(actual, expected) expected = os.path.join(shared.get_test_dir('spec'), 'expected-output', os.path.basename(wast) + '.log') # some spec tests should fail (actual process failure, not just assert_invalid) try: actual = run_spec_test(wast) except Exception as e: if ('wasm-validator error' in str(e) or 'parse exception' in str(e)) and '.fail.' in os.path.basename(wast): print('<< test failed as expected >>') continue # don't try all the binary format stuff TODO else: shared.fail_with_error(str(e)) check_expected(actual, expected) # skip binary checks for tests that reuse previous modules by name, as that's a wast-only feature if 'exports.wast' in os.path.basename(wast): # FIXME continue # check binary format. here we can verify execution of the final # result, no need for an output verification # some wast files cannot be split: # * comments.wast: contains characters that are not valid utf-8, # so our string splitting code fails there # FIXME Remove reference type tests from this list after nullref is # implemented in V8 if os.path.basename(wast) not in ['comments.wast', 'ref_null.wast', 'ref_is_null.wast', 'ref_func.wast', 'old_select.wast']: split_num = 0 actual = '' for module, asserts in support.split_wast(wast): print(' testing split module', split_num) split_num += 1 support.write_wast('split.wast', module, asserts) run_spec_test('split.wast') # before binary stuff - just check it's still ok split out run_opt_test('split.wast') # also that our optimizer doesn't break on it result_wast = shared.binary_format_check('split.wast', verify_final_result=False, original_wast=wast) # add the asserts, and verify that the test still passes open(result_wast, 'a').write('\n' + '\n'.join(asserts)) actual += run_spec_test(result_wast) # compare all the outputs to the expected output check_expected(actual, os.path.join(shared.get_test_dir('spec'), 'expected-output', os.path.basename(wast) + '.log')) else: # handle unsplittable wast files run_spec_test(wast) def run_validator_tests(): print('\n[ running validation tests... ]\n') # Ensure the tests validate by default cmd = shared.WASM_AS + [os.path.join(shared.get_test_dir('validator'), 'invalid_export.wast')] support.run_command(cmd) cmd = shared.WASM_AS + [os.path.join(shared.get_test_dir('validator'), 'invalid_import.wast')] support.run_command(cmd) cmd = shared.WASM_AS + ['--validate=web', os.path.join(shared.get_test_dir('validator'), 'invalid_export.wast')] support.run_command(cmd, expected_status=1) cmd = shared.WASM_AS + ['--validate=web', os.path.join(shared.get_test_dir('validator'), 'invalid_import.wast')] support.run_command(cmd, expected_status=1) cmd = shared.WASM_AS + ['--validate=none', os.path.join(shared.get_test_dir('validator'), 'invalid_return.wast')] support.run_command(cmd) cmd = shared.WASM_AS + [os.path.join(shared.get_test_dir('validator'), 'invalid_number.wast')] support.run_command(cmd, expected_status=1) def run_gcc_tests(): print('\n[ checking native gcc testcases...]\n') if not shared.NATIVECC or not shared.NATIVEXX: shared.fail_with_error('Native compiler (e.g. gcc/g++) was not found in PATH!') return for t in sorted(os.listdir(shared.get_test_dir('example'))): output_file = 'example' cmd = ['-I' + os.path.join(shared.options.binaryen_root, 'src'), '-g', '-pthread', '-o', output_file] if t.endswith('.txt'): # check if there is a trace in the file, if so, we should build it out = subprocess.check_output([os.path.join(shared.options.binaryen_root, 'scripts', 'clean_c_api_trace.py'), os.path.join(shared.get_test_dir('example'), t)]) if len(out) == 0: print(' (no trace in ', t, ')') continue print(' (will check trace in ', t, ')') src = 'trace.cpp' with open(src, 'wb') as o: o.write(out) expected = os.path.join(shared.get_test_dir('example'), t + '.txt') else: src = os.path.join(shared.get_test_dir('example'), t) expected = os.path.join(shared.get_test_dir('example'), '.'.join(t.split('.')[:-1]) + '.txt') if src.endswith(('.c', '.cpp')): # build the C file separately libpath = os.path.join(os.path.dirname(shared.options.binaryen_bin), 'lib') extra = [shared.NATIVECC, src, '-c', '-o', 'example.o', '-I' + os.path.join(shared.options.binaryen_root, 'src'), '-g', '-L' + libpath, '-pthread'] if src.endswith('.cpp'): extra += ['-std=c++14'] if os.environ.get('COMPILER_FLAGS'): for f in os.environ.get('COMPILER_FLAGS').split(' '): extra.append(f) print('build: ', ' '.join(extra)) subprocess.check_call(extra) # Link against the binaryen C library DSO, using an executable-relative rpath cmd = ['example.o', '-L' + libpath, '-lbinaryen'] + cmd + ['-Wl,-rpath,' + libpath] else: continue print(' ', t, src, expected) if os.environ.get('COMPILER_FLAGS'): for f in os.environ.get('COMPILER_FLAGS').split(' '): cmd.append(f) cmd = [shared.NATIVEXX, '-std=c++14'] + cmd print('link: ', ' '.join(cmd)) subprocess.check_call(cmd) print('run...', output_file) actual = subprocess.check_output([os.path.abspath(output_file)]).decode('utf-8') os.remove(output_file) if sys.platform == 'darwin': # Also removes debug directory produced on Mac OS shutil.rmtree(output_file + '.dSYM') shared.fail_if_not_identical_to_file(actual, expected) def run_unittest(): print('\n[ checking unit tests...]\n') # equivalent to `python -m unittest discover -s ./test -v` suite = unittest.defaultTestLoader.discover(os.path.dirname(shared.options.binaryen_test)) result = unittest.TextTestRunner(verbosity=2, failfast=shared.options.abort_on_first_failure).run(suite) shared.num_failures += len(result.errors) + len(result.failures) if shared.options.abort_on_first_failure and shared.num_failures: raise Exception("unittest failed") TEST_SUITES = OrderedDict([ ('help-messages', run_help_tests), ('wasm-opt', run_wasm_opt_tests), ('asm2wasm', asm2wasm.test_asm2wasm), ('asm2wasm-binary', asm2wasm.test_asm2wasm_binary), ('wasm-dis', run_wasm_dis_tests), ('crash', run_crash_tests), ('dylink', run_dylink_tests), ('ctor-eval', run_ctor_eval_tests), ('wasm-metadce', run_wasm_metadce_tests), ('wasm-reduce', run_wasm_reduce_tests), ('spec', run_spec_tests), ('binaryenjs', binaryenjs.test_binaryen_js), ('lld', lld.test_wasm_emscripten_finalize), ('wasm2js', wasm2js.test_wasm2js), ('validator', run_validator_tests), ('gcc', run_gcc_tests), ('unit', run_unittest), ]) # Run all the tests def main(): if shared.options.list_suites: for suite in TEST_SUITES.keys(): print(suite) return 0 for test in shared.requested or TEST_SUITES.keys(): TEST_SUITES[test]() # Check/display the results if shared.num_failures == 0: print('\n[ success! ]') if shared.warnings: print('\n' + '\n'.join(shared.warnings)) if shared.num_failures > 0: print('\n[ ' + str(shared.num_failures) + ' failures! ]') return 1 return 0 if __name__ == '__main__': sys.exit(main()) binaryen-version_91/clang-format-diff.sh000077500000000000000000000014511362402614000204770ustar00rootroot00000000000000#!/bin/bash set -o errexit # When we are running on travis and *not* part of a pull request we don't # have any upstream branch to compare against. if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then echo "Skipping since not running on travis PR" exit 0 fi if [ -n "$TRAVIS_BRANCH" ]; then BRANCH=$TRAVIS_BRANCH else BRANCH=origin/master fi MERGE_BASE=$(git merge-base $BRANCH HEAD) FORMAT_MSG=$(git clang-format $MERGE_BASE -q --diff -- src/) if [ -n "$FORMAT_MSG" -a "$FORMAT_MSG" != "no modified files to format" ] then echo "Please run git clang-format before committing, or apply this diff:" echo # Run git clang-format again, this time without capruting stdout. This way # clang-format format the message nicely and add color. git clang-format $MERGE_BASE -q --diff -- src/ exit 1 fi binaryen-version_91/clang-tidy-diff.sh000077500000000000000000000014421362402614000201600ustar00rootroot00000000000000#!/bin/bash set -o errexit # When we are running on travis and *not* part of a pull request we don't # have any upstream branch to compare against. if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then echo "Skipping since not running on travis PR" exit 0 fi if [ -n "$TRAVIS_BRANCH" ]; then BRANCH=$TRAVIS_BRANCH else BRANCH=origin/master fi CLANG_DIR=$(dirname $(dirname $(which clang-tidy))) CLANG_TIDY_DIFF=$CLANG_DIR/share/clang/clang-tidy-diff.py TIDY_MSG=$(git diff -U0 $BRANCH... | $CLANG_TIDY_DIFF -quiet -p1 2> /dev/null) if [ -n "$TIDY_MSG" -a "$TIDY_MSG" != "No relevant changes found." ] then echo "Please fix clang-tidy errors before committing" echo # Run clang-tidy once again to show the error git diff -U0 $BRANCH... | $CLANG_TIDY_DIFF -quiet -p1 2> /dev/null exit 1 fi binaryen-version_91/config.h.in000066400000000000000000000000621362402614000167000ustar00rootroot00000000000000#cmakedefine PROJECT_VERSION "${PROJECT_VERSION}" binaryen-version_91/media/000077500000000000000000000000001362402614000157365ustar00rootroot00000000000000binaryen-version_91/media/example.png000066400000000000000000000655461362402614000201170ustar00rootroot00000000000000‰PNG  IHDRø,9P pHYs  šœtIMEß 4Hì IDATxÚìÝw\ÇûðÏî^§÷ŽŠ  bW¬¨X°÷.F“Xc,Ñø5‰1¦G£1–4ÆÄÅÞ{‰cW""½×ãúí€ ú¼_þws»3;³³ÏÎÎî2Mež „By±´ !„B!„B:„B!èB!„P C!„B!„B:„B¡@‡B!„B!„ t!„B(Ð!„B¡@‡B!„B!„P C!„B!„B:„B!èB!„P C!„B!„B(Ð1ónkäá7ä›:›¿†Âˆ[|~T~ãîÇõ$ÉxðÈñˆ5þ†ä‡³õ_~ð?ùí+l¸ªÏ!Fl½í˜Eÿ•´“˜ì PÉ’±jª­ªì ©ÉÎë$ñÑÓˆÛyð‰²à3}“Îum¥•SÖhøøzbH<Þí#«ú¼‘Wàµ*²ÒJ·^ò*½ US…ý!!èTŒÔ«÷  æèöÇêŠ-!÷NðúpT‘ÿ¹—[ÍòfÌ5 ïá³¾;züLVø yøä Á{¾ÞÆúÍøç áýÝ·¶mÕ¯û‚½8ýn³öˆ·¾…BÈ›Å#:¢ZCWNòbñtã‚o—¸™¦bMl½št5¦¯ý…XÅ+)‡Ô«÷ G ¶”‘—ÀoØ¡¡« Ú̧!Çw,^±íLB~JónkâoY,ý¥œ‚Îd½xÞ]»Ïg“Z7ônXÇÉFº̧¡‡·ÿõå_ç£ÕåΛ¤ÞØ?Ò¦¶““¹ JztëèÞ-KÖŸPðES2ÒÚ'0k@Ûf®¦ô¹IQᡇ7¯üæ`lÞÅu|ýñÀÍëÕµ•qPÆ„þ»~ý‰ÿ÷fõò±Ì Û¾|áìíò¼EjcÖO¾¾pÑŠ˜­ß¹´üoŠK½¶î’u1âö?E® Ýü¼ÎðƒIüó!­Ã uô•›ê1í¿œ×UëV¿ŸãÃ˨_C˽7x`{7Ãç&Þ¸pø×•kƒïçèËS„r®ÔàÖë;íÈ©/}Ü,D€<þÎáà5_¬9õüÀAE0öi’‰w±óÞí׳©³9«ËzzxÏæÅkO?È-Ç:MËÓÞ _)kâÞ¾‹ÏNí:5ñpw°µ‘q49‰—ÿž5`åýrwJBûNASçh_Û\UÂýËÇ»V0¦­Æ»LÉaIM×}7*Ð×já àÖŸ ƒ¯$æõ6ÙIwÎïš~׫*†Ø»_7 úÐÑ%Žü»ö|¿ _X¸ûíß»ã§Ãg¬z ,W? öøñТ½gáÞläœU½›/ô›ºï©®|yÓó2߯µœŠ¤´÷l=vNëQ=~ï­-ïoüþ`lþ/ëöý°GÓ¾ԵqàüŸóÿg×0è»)w‡Ì¿SÚEMüƒdÀE«Òò€6#&™Ù[pHzþ¢#géd 5*][½ŠP™£µy»«öNk\8Ý’‘94ï>~m÷®m&ŽŸs&MgpªªõÊ<›dÎÄ©ÑЙ+ºù|ÒrÆÑx]9‹`\œÃàïÖnâRøs÷f#f41xoШov'ºÎrµ7ÃWjÚvÁ‘ŸŸ?` MLS3Ë}$–zM_½nQÛ­+vôî8Ö»‚›íMØeªp_ ¤JÎÉŒ° mZt*€&3VMîäcñ:æ´Š=Fôrvx\òÈ¿æÁ†ïf¶íÐÁº‘¿Ïð/~ UAÖê‡U|%@¦%Þ]Ú±cgû-­[è»øl`Þå£O[˜”7oêðÕM ÖkR¯•Cû‘£V]L¾“õ´-¬Yƒ ymÄP\ÿnÒP&­,};7øáô?Oœ¼ÿ|vgQ‹æ~6~ï|qS@}eIÛvÃf_R.#{IKmmzy™ûƒïËM^mZ:[—pK`ãf %*CS­ŠP’¼jͯÐK9æÓ«™É³ÓSÖ¹ï·{¦5–!óÜÚ…Ý:Û6hã8õû£·‰K>êlΔ«†­´œ­7òï¡ýú¸7nmÛr`¿ÅgSËsæ4,¼ ÝÐ"÷ ©î˜Åq¢ƒ¿Ú¬u[ëÆÝZNøyg à:`ÝŠau >ƒ*O{+ÿJ£·N7ªQ[[ïæyµÐns|9Ã>qã)?.j+ƒòÎoóÆù´ð³nØvÒÒ ÷4ÛpÕy—y®3,­?,GÞycEØ_s÷§¶}f/¿zõRÄžË'¶r1¯ª¯^ƒ¸cÁ¥œ­]ÿcÞº³¡‰r•*ûéýÿ{oæúT ÎˆZ˜Vd}Ù±2å*óé鿾œ~FXv p—T,oyxmNÒƒ}+çŽN¸Æþµ Ï9+kOÏl;‘˜«Õ(3£Â.üµxîä#©Ï_!'Æe«•ia›¶ÜyTdrÄ®àì<¬K™Ã9~òk“´ƒß~~1‹tY1Q¹€¥›€ÀsÂyø'ß65‘½›£«FE¨iÃ9ó:š@yj~PÿEûÎGe*4ªÔÇÿý:wÒ´ËZXvŸæ—&”£Fo½I¡îÇ¥*4ŠÌ¨S}5û¼° p—³Æ$óýèƒ&È~ôþ„õÿ=LWª©÷þÝðîÈù‡s!h>ñ£¦†Þ\ŽöV•Æ]Úr?2%[¡ã+XR³æ³ÇÖÒÖOžö¿]¡O³Ô*yRèéÍóVÞ2Âf¬‰»LUî „Tß@ºä]s‡uY|&FpŽ ý'Î]tæüÙk¿¼×ÅNPõeùôëî D>rß° >óæš}‰€iÇ.µ¤•\¹>óÚéHöVB#äMvþ 3YaÍ(ÂÏ\Õõg_Ú·ôëmë¿|ÌL¯ÈVI… xU®'`Kƒîùùñ•¦×VõÿìdþÀ¿:ùNÀÙÖµ€³ëÒ».»€ž ¤€ÀÊÓŽ’ï$©«M*dÔéÚLjßþž˜bEÒ%Ùu7ië&©`ª¦õê3¯ýû€S=[a9‹`ÌíæÑ¥» |héÉä¢!¯.ñÔ’½)€u÷îµ ]©Áí͘+5<®Ó±ƒph͵,¾ªz°š´ËTå¾@Hutè2.oý¡O—öµúN›òÓ?»o%ë!®8ýÀ¾oÚUñ#LÄu‡õrví0øŽuthBÞO¥1}vŠ€XVÒVÙyc$Ç._½áVȹ´ðòð9¡'/,h€aŸ-L»;hÊæ›jH½ºÎùö·ëWÏßüë[Û‹+™qFÚ0hiȪ–ç~ xÿï¹ݸ6ån‚°õ±rv~£캎ñ–@dÛÐÐÆ„&iªE*MhïéÀiìùÛÅ’~-bQ#Öî–‚*-B¹[¯.#!€ÄLÌ–³ÆÜn¶¢o¾p“˜êñÕ8ž¶†Ž%ÜÞŒ¹RÃKj]Ë@\X´±&ÙÖð]¦šçª tòiR„lüãÇ ¡=zÍ_Øö\ü~ý*}éij×' þxð£rtEy} oŒs4½ž¯HÞXË®ó·Ü^3{b€¯§©8?ö°v·{ñôH—pfi§v#Ç-ß>F H¼:ŽZ¾ùpÈ×]]*|c-:Ì\}n¡úöyí&oºUôÞ ^ñäN`áédâÔa`kÉBa;xDCs3׺f@ìü#Íë-Â+!’˜ª,BZ¯N«À0(‚ÑñXÆ2 noF\i9J˜×I°ÆÙ†oÈ.SSwgBŽqésùú×ÛëÙVåÓYDÞýº»1‡ß/ÇD8qÎ¥h^[ô8¡ Šó&ñ÷˸:@Ê¡Ÿt tmÜÚÂÇÏ¡e÷vßÜ+ù,*óÁŽ_¿ìÒεׇ³7^Më=êÇ#*Ô±0&­güqhZƒû«'vúüXÔó›@£œ|ôÙ„Aòö_þØ›ÀÞ ]UdXÞ-¤¯³å8RéõX®ÄáuMòãD—5öiVtfi3ˆ -B™+­të­Tò²Ç’½Ò“iR'pkR[òâžåRòžUz loF]©á%LàÖÌçk5c—1p\§ç*t¤M—nÿkíŒþÞNVRŽaX‰™½OÛÁ_NõðøZlNÅ{ ïå$î.Çu+œ»:ÐH;q<ê¹_éér¶ª͛ÈÁÛÀ­•þvôâã¤t…F«Uçd¦DÆÉË>—Jtaõ×S:°Íà1f~?O+‘ÐÔÁgà´yYÉro/ûõ¦&ý–ÿ¹úÖž–"¡Äª~Ç ?ÿù¡· 4×Ö,»YŽgòØÞŒ»RƒKúÓo¡ZHº/Þ¸mz×fŽ&B–J-ÝœL*Ðq½Þ]ÆÀNõå×·;R!ÆgTlq©¼ÌdÚÇ[>y¯á_‡Ôµ`õ¨E~³ï½YÛ—ëýv†µ7w¥—4bó¼÷|þÜ0ĥ猥=gTªãz½»Œaª§p¯ow&äµè@taûÊñ£{4i^ð¨´OZô›2ù׋räÞÙºî¡ Êˆu›Jq®ØcXo' q÷¾ˆÒNåTVº|+:­ #ÚôÈkÿü<§Y߯w•øðV]â¶Yã§m¹™×åkÒî]صô@t¹¯¾½4oº¤õŸ÷÷Ž+‘ñYªü×3(²“ãŸÜ¸ôï†Ïž¤®|´mÎÏ»ŽÝÏ,èÇ•©OÎï_ÔwÚ¯‘å X»Û¾üЙ|õv&Ä^O Úèíëoê$ÞºUx¹ë5¡|t±}0éÓ=¡Oó›‘üñ¥=?î)ò]ÚñG}·òØíÈÌü&¡J‹úïØ?ßí~–¬|ExéJ k½—Ñ "I·îƒ÷çî É[±*åι%4ò²“éw}6¼ÕGkw‡&ä€>+&tǯóZöýjWB9Ïê loÆ]©á%Õ&ìülx³i¿ýs¹ð ÍP¤ÇÞ 9ºrw¤ª\×kÝe êT‹+ñ¦×¹;RLS™§1——ÿª”Ò_ëcDŸ™7öwOÜÒ±Û’ëÕì]ˆÕ9o„Z!†œyNÚvk®GöÁÉž³.çÐö 5¶%×ܬ‹êõ t⎼§¤¼j½„Tç8xæp§°s§B#"Sy—VCšâh¯Ÿ~L¤:Ïd˜hâõJr.®3¬—´{o„¢ºmÕêœ7B-ä•(ù=ØÏy%C¿Ä@B÷žŸM}×Ågï=ü{þ±z·©ÉØššq‰GàW ñä¶påPë%¤’ôi—–¬Úê~\J®rãBv,î:ætºFj6cÏÑ!„B©6XÚ„B¡@‡B!„B!„ t!„B(Ð!„B¡@‡B!„B!„P C!„B!„B:„B!èB!„T–àÍ) ËþìÆ5þóhí}~X¨œK „BHõŒhB!„rÕî1çðŽy–FB!ä52ú¥+βÓÌ•»¦úJ Ô¬?»+A[í·^ÿQ”(ñ!„Bj0㎹°æ?^{hª¯Oþž9lxMˆr!„òæ2æˆçÒïëí<À?Z4aÁåÌ¢“y¦‡%ÛUÂx s¼ž?•Í«ÅL c<ÌÑ-JÓGòÏ’Ë„ìH ¶»”qdÁóü}¿+SwR ¾Ø"?svœëÍA¯ãeó&ÏÇ]%MO®ðœåBŽégÁõ‘1À#Z¥?’¥ß¡àÕÔš!„76Ðam:/ù²“ä‡ÿ7}añ(Ãô2g ¯ 1,ÓÕ‚)ü²ž)÷ž7WìdÜJ;Ö±H@ã#cæË˜Ö)ºå|Á ÓÝVð©¬`åÓÓ’yÛËLÌ-v`} WÅÀMÂN”°m2´ó2y5(B!¤:1Ú¥+‘wÐŒ¦P]\2k®X9ÉXgö ÛgQNÑX©»-7D˜ÿs);CöÊ7ÇþϾH”S„¯%7VD͉By3‰çøaî@Úæ•Çbte¦äHÔÇæý©×’¨OÌû›ƒ  ,¸Fy‘„N¿*AÛç©fh¼îX~äÄŒ2g¤€æælÞ…*J?7VÓ3Zû]6_Õ¾ñ4g;° ×èˆ×ö~ªé§ýK™Ÿ·~挌!„ò:B'¿n@Fȶ°—^¾ÉÕò©ù±ÃhùÄ‚¿E¦ƒ4?Ù™TÝnŸË#U­_™ªÏXHY7`˜¦ùC;ü¾4ÝU-TzþD†>´J7ÃøËòB0~c²î˜šWðÈÖð[RuS1ëÂP‹"„Bª#ÍÑ;7ts3òåSry è…Wø7À`P;?ôâCŠL=ÎÕðO†XÆš‡‚-ì•ÝØÅ .—ÿ×8gḶ%cÇ œ§FE!„TFÑa%fRÊÌ\]%—¤/5Ìx'ü_Ä´4±1ÆZ¸—%0¡B!ä tôŠl™•)g@býGQš.Qš.Q/ÜÝÍ#*?RbZ Ÿ}l*bjåý¥ãÓx€G|A²v"”]ð¹ã+ÞœAåЕ²ðˆ}Ù$ - çB!o` £Š‹àÖ¼®¤RËáùóùs{Ñ͆ë)‚˜˜k“?õ8]¡æž¿\p‰Ì߆ f¤Ç¼0âÂ#² êgÅ6@ÀÀ”…¨ôµ§\Tnθ²²LS3îkF€çC òv=S73NÛï©& J3(NûC¦þF޶o”ö4½”B©NŒ4GGéD"ê;´ jbr&D^‰%ÝËÔÝ6á|@ÀÎsbçD6fñyÁÆõ,}ª”µÀ±3Ù™¥.—k ÷³ËËFuxþ’¨k.Øh^X:Þ|.p3SaÂÕš[pÍ-žÛ¼%ÃËiD‡B©NŒu{¹òÑúO‹³ûyT*xÒjõ_§èãJ CަèöL=ÎUê¾Ìä5„M'Ë3kè¿ }ô‹ŸrŒ=~~Š>¾Ä_rŒ µ&B!¤šá…ÖFY.õQŠï¨Þn~Í2ßÌxîf(†éi‘ÿÀSYúh¾¬Ïs5ü‘\è9Æ‘cL€çï)øÕɺMªbS•“”újH9Æ–c¤ Èu|œWsõg”|Þíè<χÈùl–qà˜Âw©ghù‡*þßþ¼ŠnðI«ãO+a.`œŒ(•~_¦þœyA•\Ãó™ L9Æ’€G´Z4[A%5(B!¤:ašÊ<4¹ZvõG3$oùΔC Ú¼„ByŒ6¢€ÏzrIêÔܵQ¯Þ¾—ßN¡]B!äÍt^õô¿cç¹C[ÕnÐiPÇŒCÿÜÊ¢‘!„òF:xeÔŃÁ¤Ý—,=®¤û!„òºsŽ!„BHµÂÒ& „B:„B!èB!„P C!„B)'ónkäá7ä›:›u±²Æ¥^ÓW¯[ÔVVÂ".}ät&«\ÉŒk,>‰·°{ñà,ÊüÙ!3º0Öá…“*™!IþÄ^¡,W2£ª)•%­7îðÞY­9{¦ {,Fu!5P5à+òš1Àª‹Kfí/ñŠcÒ°ÿ£7ÿ‘„ùǾŒ«ÎÇñl†´wKêôni аÃûïÉ_Ús‹juÝÉ)J&kÕzHߣ–¾}FûY¿|Ž65&³ŠNŒµiÑ©šÌX5¹“E©YQ‡¯nâÕÌ$ï_½VíGŽZu1 øNZÔÓ¶ 9ˆOùqQ[”w~›7Χ…ŸuÓÀ¶“–n¸§)¾0“™N˜ùûÓYùÿ¢ç¹$üe¡”¬Úb`.W°0.™¶• õY›Ä¯\£?v]b—~IôÜ[f Lö–V–âá– ‹Ãx˜œ3¬>ML&„ÔHÕóÒ•Äsü0w móÊc1%ÆBÇV=ó:^>åò¾Ã§ï'+Xó:­ºì\K X7oå~þ@„ ´)×vŸo8­£ ¬[t‚»)äÞÝyäQNI³¨Ý¿ÿ}7—3wiÕs`O/ «~µÏíy¨„ȵu×¼£KîÝ£{ÝŒÏÙùv0¨±9À8·kí~íHdñ9‰g¶ýs#13W­ç$fV6vfê˜üª{–|^|ø§œC8Ï¿°Ð IDAT“Âþš»¿ÛÆ~¶}f/ï3[—vaÿ¡C›÷ž¾š¨.õ Íks’ì[97Ýaß‘áÖýkËv'ç0k>{lm mýäiÿ Éâ )ôôæy\çw~oYd\°dU‹WqêP‹”\Ó‡9œ³B(2Ñ©èÅ]s„€î?»¤Ý=ºXiönHý’‹<ÇÀdÆVs*KóhÇÊmÓVô4®ÞºOË$„ЈŽüº9!ÛÂ%&àl|êå ¬¤žÛ(4![­Ó*ÓÃ/=ž˜(¹zY†pÚ„#2ÀÄÝÕroì=õ ·ä#Š^£ÑêõªŒè ûŽÜ×äg§¾Øz×1(ïÞ}9&C­Sç$\?räfÞÅ Ymo›çãÆÜ¤Ä”•FÏë4ŠŒ¤˜ðˆ$E• è’wÍÖeAð™ À96ôŸ8wÑ™óg¯ýò^»²ãYyØù'ÌldyÍAZ§c3 áКkYed×Àd¯„>^¢ Ñ3y#:B­´®àr/ˆõeäÍÀdoueå„m<Ÿ8toç(¤“B#:F!vnè æfd)÷´pæ¦yÙø¿û¥ÿ‹ LÍ9\óâÕqgÞn:Æ×$ÿlúÈሗǼ"în¼]ȬeÀ›Ùæ-I•Ï’ÃSÐÔ€‰½ékŽu—·þÐgëRÛú­zuéØ­Û€&võ§há5¦ÿü=Éz`$†ÍÕ½‹o7;S1À+Ò¢s,0lþe¡u-;qaÑeÞSd`²*ÀØ*L»ä˜ÔS ¬t¬à]:Ë(œhÃé„fªt¦¬˜ìí®,EÄ•ôõvõu#JC}&!„FtŒ)‰™€23·”ù,La7_Ú‘P$I!´ts2)<·­]ß]fÀq×<÷–çyÏ—œ—g³_ùê±ý4)B6þñcÐОN½æ¯‹l{.~¿¾kÙuþ–ÛkfO ðõ´3Í{>!#µv·+6M„Ï/ÉK6²ÉŒÞ:¼Ó?O¶j¯Ù騼@á9kÝ‹Ù+ù”Q ƒ’½Å•¥—§ç™KèñÉ„ tŒC¯ÈVY™–2MS'OÍ}Y”òlž±À¾åPÛgß™4Ö×Çü¥%ZºçÏ;V¦+ô€>'Ež÷»º¶…Ï0f$Þù7å$åTä¶¾ >bÆô9Ž|ýëmŽõl…€Ä{Ü/ãê)‡V|Ò-0еqk ?‡–ÝÛ}s¯ø±72 €[3IÙ‡hƒ’—Pm><[èîX¤¬pŠýÌ-z¶[ô<ר¥VÅF*tœ&€Vl[fði`²WÓê«ke±2 )E¶’îº"„P cªø°nÍë–Ò)kSÂcò.©"/¯ÿkñ’Ÿ¿øö§¯–­]»÷âí'·×ý¸dMXÁÅ)ÖªUŸöø„“Ûÿ}¢q½îý”|ÉÔÑÁZ*äD^ºµ2Ë;&=N×Ú”ûó"©o`Ÿ&¦Ndîê×·W£¼¨Gy¯"ÏyãµÊü_97óqr #Y¹4jÑÀ®œ—¥M—nÿkíŒþÞNVRŽaX‰™½OÛÁ_NõðøZ¬ 9x»¸µòÃߎ^|œ”®ÐhµêœÌ”È8yÑ%)Ÿ>‘Øõý´—c™00™qq:‘%QF°En¤P—Ëðz†W±ºA±£°F˜{Ÿt&¹‚2ڸɌ¯&U–¤N 1¡ 4™RUË9:šøK'Qß¡mP“3!ò’ºí'Ï'zws€¸NÛáuÚÿ2%Ö„}”ÿ"ƬA@wWàcOéGùàFd.¨c/ŸŠöíçÆÍú¿Ó¬±óñ¨s—c*2A›—oÂ:]?ü¸kÁçI"î'—ãÙûRÏ›6sjÚläô¾{²uöæ'j€Mz‡6nM&|=2yÅɰÇ)ÙJ+”È쬊¿e+7ô§ßBÇ|Þ¸ûâÛ\}»ý¿;I ˆÍ‹\ÿ+O2£ÒqêLH¬Ô}st§¥êN¯8ž5ÕTõ Uët±OªÃmúaSe§ÏZi+”ÌèqN ª,™÷ÈŽæ@ⱚ C¡@ÇH”Öïx:mšûˆÙý¾¿¼õq ã$º”sÛöÛ¼Ó¯Y Oµ1u0å¦Àˆ]º{ˆ¤þ·çjšȸuìïûjqÔïÛñZđؗÌÍT†8™LŸqeç~çqZX=Ÿ*ãÖm×2*ô„}zØõ'º×æ^Z2ãPŽ@Gñ`ݸϔuöoXÛ!’*îþÍSGv,_w*ï‘AÊû›ç轡¯{ÐW¿}UVô±yÞ{>nâÒsÆÒž3*™Ì¨A°({ŸÌd\® uš}ë2GÊRLS6ªÆÉ 2mdV2™‘ÕœÊâÜzOm<Þ½þ! èBj"ÎQh] ³¥K}”â;ª‡·›_³ÌãÁ73JuxUÊýëaÑ F,–˜È¤B€.75þAèµ+24<ÀÚ´0ÄÇȽ¶ëÀÕ4]ÞÏ’â™-ÝM‰³mÎí;1JØ5ñkl 9ãSs©€ÑæÄÝ¿²gû©°"oŸàÕ©n>Lâe–ææŽ:#öÑ¥“vœZdlèÙÒ2Â.ÝH}IüÃ+ï?QšÚX[™I  WË3RžÜ¿}ãALªºSGtòè»—öìܶâÕ¿Ükùq_g\š[oØ/;¯2Ú„Ò[@¤ì7óéw?Ä7’RÕ×ð@GT»ÇœÃ;æX–˜×vÖ\»’¾Ðkø8zH5«,R…rï¯WA¹ñŸ{¹%íêfuý§}¶øØÑS)oÈÃoÈï½·gÕŠ÷Û×–<FÀYxŸõÝÑãg²ÂoÈÃo$_Þóåð6Ö/œã±–Ý¿Üxh^—Z"jHÔ¨!ÂdÅžæý›ãþÒôœÎ£¾ÚÂBÏRÕ—©š?G‡³ì4s宩¾(OÎ4xWÂso÷d˜®‚Â2´ßgóI<Ì8¦¾„q×ñ»¼¶º·kFR§Ó°Ycz¶öq“ŸãòÙ­ëÖ¬¾˜ZùÜø%ã¼née•õ¶¨â:­`ïñî…£3”ô•6lM÷ ß/çäŸ]mÛóE Å éâvôëÿé"OIºN-t-ÿ_Ž>¸D9¬Y»7„®™=¡s~G€1uj0bf{aÍê” ¨¬·Bu­Sõ“}ßì ¿¼oõ¤qÃëµð³ðiëÚiÔ¯¶_WAÐpâŠa.…yÓ$\Ù{)ü|ðÏAÃû»7nmÛª_÷{púÝ ç¢EÐ< 0té‘ H›O<¼éÃöæ,5$jHÔÞT5¦êkÔˆç2à§kK;™ñ–MXp¹ØëvXög7®iY¿æ?Ö^@a2þóhíýs¿-ò!Ãt6gýÅŒ‡q@ Péø›¹ú™ú°^hXÏ„`Â63ŽyÕÂ#F­‘ë·eóiåë¼rl†žn\ðí²w"ÓT¬‰­W“£Æôµß:kâ¿ùc+’zcÿüaH›ÚNNæy£{ª¤G·ŽîݲdýÙEñ«sBûNASçh_Û\UÂýËÇ{ííøüPÉŒTYšÖýÒZz©\4R€V.мcvúˆÅíDöY„ê€ñI<ÔÖú¼W“h²D‘af§XÞIfŠ•”ÑÛyÉ}æúÔW:YëÌMõ,›iùÇ*ëü¢²4¦ÍÀÔv .v:1 uºävˆÅMUn÷n9î2.êŠõ¦mæñêrôP§œ]÷õ‡d‘¾mê°‰'R [g×õ¯CK‡Zfíž1dÜá_£™?WT9ê‘uÚroa}õ™Ý'^È.½X¾svý7ÅEyjZíÉ!Ï%¹ô^³ã»¡¶È:þY‹é‡+÷úOjHåhHÒFsnírÉ:ÜÇÿ³3òbçùí¾?t|˜eâ–w-¼K ér ok3¤êóe®lÓ=½}3…›µžáÙŒ§Ò+§­]ë|½CÓŒ¾Ý³½Ý5R¼šËHEÞ¶Ø{ÀôÙØ[‰£/e É”=Z“ÿmÉã<åày³ª¾DÕõ]W¬Mç%_v2ƒüðÿ¦/¼\å/d˜–ìs‘“˜cüÌ8?3?^R°å…v†=×÷¹3*®bv8ËÈáÓ Ÿ$®ÕÂÀ­?_IÌ+avÒó»æŸßU4™ž—ù6®åTäwöž­ÇÎi=ªÇïǬ¹Q¸»I½¦¯^·¨má2±£wDZÞ/î†%3je Õm»gÝ'&j/¿T/¿¬ÿsÙzSÿ ¥}‘ñN¡¹º^ÛÔz~9t9ò´È‘L¢=+ñùœÓ[йÜÂ}Ä¥ Õ~9…»›•²Ee‹‚ÑØZm“¦äˆ¿Ý-.Ç;Ö ©S]òÉíòûkðˆŸžìõÑæ¼ÝZà4zÑ7C-·íÓGS^Õ˦VRÙÉò2ר‰ ¸hUÚ›·:öДqvµöÎjÕý³%]/=–¦§†ôJ’"âä±Ô wmÚl ;s¥H<#vïÕÖP^9ý$—R)‡CªÕM›:=­ŽèY@cYKÞ}¼¼©‡ó²`YvAƒ³óÿd„¢ð°ÀˆtVN +™èäAÓÔ7} §zU}Í tDÞA3˜Buqɬý¯yÈ‘cg™ë¯¥ó*€a™YŽ\ïRî»KTð‰åšþ¬M‹N›ÌX59ká–ó÷2Kn'êðÕM¼VìAS»º#gþ:½­µï¤E=w÷Ú¬qã)?.j+ƒòÎo —¬:q?QgY¿u÷©Í|ǧhTf`²ª©,ñŽïÿKàÔÐ[¸ævè—è£i÷~bÌWÎgSЊ}éy¨p˵N>YƤù˜¨z •‡ül–ùüæþ»Áîâ#qj&«Ô0ÏYŽ¥‰ƒ¿q Iá]âgP²ZéŽÖM˜;BaÓ:Ëù€]”ÆÈuªO;¿,hM‹S;þ²lÄåw¶„«EõÆþ°Ê_ŠÈõA‹.–k//úòóÎÌJŽë9™™¥S­Æ-çþþ/ëï–uDd-Úôò2÷ß——ô½âá– ‹»ßœßpàœaõϬ¾§¦†ôJ’âá?§3ÞjÙ{ˆé•k9…¥qôëé üà=95¤—>ʪzÖ2ûÓêˆn¹k¿Åí(Z¬©Û*}ä°l;ÿÄA·jm¼ÇòDª.½B0:n;'M’3bKumŸì¶u$iUwðR˜¬øÀÓ8stÞ ª/uõÕ2ΑxŽæ¤m^y,¦¤\¯ÿ(JÓ%JÓ%ZwóÙµ*M—¨ÂפÊÿ!VÓ=JÓ;F»ª ÝØÉXgê˜?‹rngë>‹×xª ˆÒôŒÖNLÔ-ÍæËW9а¿æîOlûÌ^~õꥈ=+–O lå *ë®x^›“ô`ßʹ£ƒÓ®±íü‘³æ³ÇÖÒÖOžö¿]¡O³Ô*yRèéÍóVÞ*ös“µ²Š4¶´TN©…^˦?1Ýÿ›ëÖ 8E¯ª'ÜëT‚˜›ÖkÿÏ<`]rJH$xpK“Â)^<8•oilF«ÕpQ-¢€IJá⯛Ç0Ó˜ ª¢Nyù¥•s¿ Ó‹ZÌÝ4ÙÇ®Ñû›?k"Ôßûrúï—rªöfAónkä¯&_;ºkåòq-¬².-=å§e4]Î9ð“_{˜¤üöó‹Y¥dNóhÇÊm€Ç qõÄÔ^UCʽ¹óT2`Óc˜ŸYá7¬½_·Ÿý/MO ée§ú¥W=ïÞ#µ‘ê{ö?®²ýï‘P®a49¢û§íWl–ê kÖE!ËÛè¬ÎÖ€0ô²,>‹Õé˜ÜTñÝó¶k7šf¿‘wþVߪ¯YŽÐɯ›²-LñÊW.×C (tüþ }\~-Á†¦“I~gr/M;;MQÍgñà•ž¤Ô_-wð®KÞ5wX—Ágb4çØÐâÜEgΟ½öË{]ìÊîåaçŸ0³‘åU ´NÇf@¡5×²ÊØµ Löj*K'¼|À,0k˜ãPúh’2N–@¢£©–½4^Ãjx^Ëj°˜Q+pR ûñ­Ot`Ì}G4'OÓdçâÚ‹ÖZ_z$Ô"ûÜN#c¿ù4¹©e±…äu¿Ü›0àS-«¾†èèÙ ÀDfeÊ!½2•Ïåó»?o§*Ýì#uð`úʘãÙUòœ}Σ#_ÿ:rü¾Žõl…¼Çý2®rhÅÒe‡nÜOÏÖ0óºV†,ð)ÒF&¡½‹[3É¡dE]¦AÉ^Ue ¬UV²…9:@¨ê:&Ãȼm½ó˜éãA¶‚a…¼Ì>kʧ)nåÍ•q—fÜ:-8ÏfL}'ßÞé7›4í´0øÃð€a•š¢£×ë––c·VÇìÛqUû¦. DxŸ7ƤõŒ?Móº³zbŸeWÓ šéÁÊ,¤ÙJ=5¤WØ´qÇ™¹«·óû´[>ój“ÁmÌ€ìóû®æPCªÔáC'ˆÏB+Ñî…n'“ 8 Ñs1׬7^³Þl¢ön™Ù½w¦—SæûD_.³HÍ+Ï(µ€Pg%ã!7謆çl™G}½iÞ–ª¯a#:ªø°nÍëJ*šämô~VlS LYTðYÓ<ÿoÁ­Ü ¬ß[±Í…Œ)°p1­D(ç{ФM—nÿkíŒþÞNVRŽaX‰™½OÛÁ_NõðøZ¬ 9x»¸µòÃߎ^|œ”®ÐhµêœÌ”ȸbSוOŸHìú~Ú˱Œj`²WSYŒÞÃ?Û P„›$iÖÍ€dßfëë¢ 9«Ó3›™"¬È|Å––ÿüõÊ4Âл îÒU)+Ï`«^.ºû¯Ýªmc¦–ܱðx£ÄfÐøzè s˜\5½ié'?z6/‰‘.3Ô誯aŽ&þÒ‰DÀ¬mP“J:ü¥‚ŽF"á~vwîwáTty3õç ª¬•9÷“³`¿»ðT-áa7á'Á"+Ö´|]™gM›œþÕþý‡bB¯æ<¼–zýèÕ óß©<Ù:{ó5 Nz É„¯G¶ià`&á°B‰©Uñy鹡?ýª…¤ûâÛ¦wmæh"dY¡ÔÒÍɤ"ɪ¨²TÝúfÕwÒ‰8^b¥l58nJ; ³çÚ—´@VÀØ3È4¼Q?ØqÌ"æYHÊij½Ú*¡¥YnßiOû¾|õ‚›§¥ýF+ÌZ&~Ö21ÿÃóó³tu‘4ÿJŒTšµHú¤ERaÞ®l+ÈÛÛUõ5kDPßÛ´j¿l“ÙÿT»z~Q¼ö×>ªÈµÚ -ªÐ§ëÃËßäªõ bµ§éϨøÔ"?OÑðgrùr^W']ؾrüèÁMš;M½ —>iÑoÊä_O<ëÈtI;?ê?ïïW"ã³TykÔ*²“ãŸÜ¸ôÏZ³6açgÛMûíŸËQéû¶"=öVÈÑ•»#UåMV•%¸vQŸÍϦ<2ÛûK­UÇÅêüÍÈd\søn±ý±kÒ„LV“÷¡žQd ŸJo‡˜¤”¯¿2îÒŒZ§—¾+ç5"cÛ¼Eû oÂâ³Ï-]¸!‚f3W vªà ˆ.qÛ¬ñÓ¶\Ì»°©I»wa×ÒÑ*@õhËûŸü¾öäíðTeþ×Éã#®ï^·dH¾CׇÎX»Û–÷tɹϊ­DÈÙóÓöêªßë©!½@¶ñ«¼MŸ¼mËœJ®ý-iH/—}×öÇù.[OšF¦°ù›]Ë%=6=sÐls!Lœ¦(¸$¥á™ZãöëiQÑ¢Ï2Ýøƒó¾«’Œ‚ÂéU\R”ìê ³’Æ0˜Ô ŽË·›G¦ç-–ÉŠ•]8úü½î©çƒª]| IDATVì4‹LË?”+“¥!GÌ“´oiÕ—ºWßw]¹ZvõG3$oùΔC ¼ÉŒóþðêZY¯ò»T§¯kÙú»VÏðDÖñO[L?RÉWQCªXCbd¾ßîß0˸¿¢éàuájHoÑáƒú:¢@³wÁ°?#»‘+6oëmÊP{ Ê"Õ #qüuÇêž@øúaŸ«tE ©\GsÓZÎȦn‡ÿýþÛ,wñ¿}µý‘†5¤·¶êK^KµÑÉÛ“-ýg®Ü=ÕWÝß ì¹!†óšXYoç‰øOà2hïÑ/:‹¡¾ý÷à ¿œ6Ö ¨!FX{üÇgy!¯êüâw¯}(ç©!Ñáãí®úš‚£Ðºo^uñ`ðic·ð%K‡+yj5±²„š696^;fž¨¥ õ¦ÐËã“ìZ¹ÝXÚÎÖF|«5$ÈœZ÷ïÚ¤– ^uóäŠ/æ|´û‰‚§†D‡·¾êŸWÍGt!„B*Ž¥M@!„ t!„B(Ð!„B¡@‡B!¤J hn ëF†wmfÇ'Ü<¹åPXš®¦HìóÎŒ1µ 2xåÚªÊ%#„Bj Ñ) rïÐÌNˆ›víê*¢-B!„ÔxoÝˆŽ¨vé¿O,™°äÔó¯Ue˜’þ,–BbçÙªUãÆõ\ÌD, ÍM‹ ¿úß•[q }ñ„NZ´kZ¿ž‹‰¼:+.òÁå —nÄ*ôÔæ!„ tªgÙiæÊ]S}%PjÖŸÝ•Pô™cê§gNÞ´ïÚÔ‰·Nžˆ~ñµbœ­ßði=\…E·žÌºVC¿Z 78°i댼‹]¬I£v(ò^FFdîR¿Õ ú|lú§ !„B(Ð1Ö¼ãÇkMðžü=súLJž²ª6íÎŽ?îì(uºÌ§ñ9pµ*á+©O`WÂä<ÀY7îØ¡ä·OK½{õð}´ýf6=¡“B¡@Lj8—~_oŸàþÑò  .g>wéÙ„ÜB/ÎÌÕ$\=q¯V Åýka‘ñrЦnë¡C[;³€Ðµ‘½0,R èR®:Öp|ûÔ[gÎ_¸•¬–85òÑÛÇçÒÈ^x3»\o¡ç¬µïÖ¤–³½••©˜Ëˉ<562üÚWoŽ&°òjÕÿ‰·³™Ï ½..¹Î KF!„P S3°6—|ÙÉ òÃÿ›¾ð…(Ç`|έëo=û¿6éÁ• q­‡¹`¸Âi=Ú” ۷Űñ‘éZÔO¯Ÿ>ÑÄgˆ ½¶Ü+gÄŽ>=Ì‹~$4±©ÝȦv£Æ>û6m»•©Ö²q¿xJó$siâïRÒ¦0,!„òf„oC!EÞA3˜Buqɬý F{ #9øøuÌ ”×⟽W—ý8?ÊÉ L,œÌ9÷/ǩ˹mFLz)Ñ‘¬AÏNu%ÀH=zõ,_ÊȳaÉ!„7ÄÛ0¢#ñ?ÌHÛ¼òXL)U÷6,ù(ñVIŠ'SÅœ>ñ°´× ,›õÔÎ@ZÈî#ËÿœÕÓ³›¶p™©™9 •–çD2Kçz݆tk$Dn>¶Â‡1YfÞyW tq§·8ÿTmåÓaØ€¦Åo30!„BNM!tòëæd„l STÑÌ\<\,c¥¿0ZÄÊÜ:à.‚>9dÛö#O”˜‡¬Wfåš¶ðoïUÇÉÚ\Äà5JUþÝ_R+) ­kÙåMßɺvòìãL pûì‰fM‹m&#„B(Щ1ÄÎ ]ÄÜŒTWÑX‹z}`{`ÕÎ{YÏ.21çÃFu©/Ÿr{gðÉ[©šŠÜm%°m7rLï:Å`È%’‚?9#µÌÿ =&³ô‹s&#„B(Щ1X‰™€23×x°QÝÛ°d#›Y¹zµèÓË׎ÔÛ¿­}øÑü9@¬Y½®ïhj >éúÁ-Çî¥h*˜y«¦½ž‹rJŒ`6¶+`+ŒB¡@§ÆÐ+²€‰ÌÊ”CúKc½NŸ7E[ 0P•1ÃóZeVò£ëçÎ6ñâÀÌÉŒC‚`¤µ:¿?¢©-÷o ¾š\‰‘$—]Þ_¹÷Oÿû06=W¥Õ3Sß )C]Ÿå:7]ÈØ{9JnE”rÎÀd„BÈ›â-8¯WŇÅpk^Wb@T¤*xÈK«Æî&ÔÚ«MàVÀšzõî×¹m7 ‰Ãp3»z~]»çߟ›–«ÀHk÷ÚÂÈøo—!QgÕpФß.˜9½#î…pJÇ?ûS§Óétzž×iµšb7biR#ò‡’Ä>Zº˜‹X†Ÿ¯]“B!oŠ·`DGéD"ê;´ jbr&D^vbm꓾‰+°Î&Îî÷©òÎÝý×ÒY§F­šz ›¶*ù§éaW5kÙ¨csX¶óE›¡È½s×bMºv }ü¤hh¤IOâë;3€Ì»ëûÞ]KË·âÉ¥9­L˜4ê5ºQ¯J%#„BÞoÃɼòÑúO‹³ûy¼,°ãs†\Ë~þS‰•…˜ØÖu–ö»ÜÇ‚Câµ¶umË—?¾„¿ ?ɸsüDÌË'ñªØ£;.ÆêŒ“ŒByCpŽBë7¾ºÔG)¾£zx»ù5Ë<|3£Ìû´™î%ÁÊÎÞJ&dÀ«2£Þ:q&,:[­HKJËUëÀ ÅiÞõ­"->*ôò¿;÷„<ÌÊ „ŽÍý|-Ëz.MFØ¥©…±†.;1Ç¢v-']â­Sû®Æ?ÿ8½üé»1*L&ûÿöî<.ªrÿøwfcA@5AÔ54÷]”Ô´{£ÒºbeþºÖµ¬[÷Zš¥uoš·ÒP·4 q7E QÙ÷a`†Ù˜™ß,‚ $Iãçý—gΜó<œ×ùð}ž9¹€ËnÛ±A«RÈjªJ‹óÒ³ŠÊ›õDÔÒXr3½RÍ1YÍ9L"C‹ZÙX_[Z™šYTÙ¾ß߸€)`Œ¸?N2oëµB,¨&zÍò•q•Zt=À#…Šs®$òC–H|§M÷“%H«Õ ÷tL%ê¨K~>•À \8jðcãç“Å}w³Q_SÉ:ªâ«?ÈæsÊݼåD® ÓQLÛ£2GAxV è è èô-6šàÂõY¹Ô¥ËK]Þ2ÕcMÚUñÅHJ|uв ø=x„ ¢:` gðSkOzãIqß·#K`ëöì³áî\4üùXÄ“7FŽ1Ñ…ƒ¶ø³ÀÐUxüšíGVùñH¥}rÏÅ#•-}ùi\υ϶Žì²z½uæ·›ÿAdtœèaë“ccð\LJ½²tÚ” "¢àOÊS+R“.FïÞµój]OûËL2u}¸o0kkJÀ®y/î¸$õþ¤¢ z×n¢q¯·ÊGE߬ [ÔÇ)zÕGc_ÿöÖ®×"&ø8 Ú“pPÀ“‹×LhÖóýiKÌY¸%^Fü€çOìýÛã"\<ý*:½ÁrœõÞÁ72ä}º,bCRƒ‘?íYB‰ÿ˜À/gG+‹H§Q6Õ×HKKÒ¯&¥Ët·s =.ÀÓu 9›tʺò¬›‰ç ëÛ‚“‘ò†ë¢È÷;ý·ïf3¸6¾cÆŒñsss¬¼èVòÕ‹éÕ¿X:ƒ)äø˜«ƒ˜Ç""ji¾S™Ÿ“}íê|¹žˆ5À÷ñIÃ]ZY ¹­)­¢®¬07åçk7+”}Sá¸,Üþ‚“J¢6¼¿õLzá5ÓÜÆcøO/9ðr™²µõm'ï9ñÑ<Ëú˜UaÏŸ©ëè–mè×q[Š¿\°âDmûëú¦ô}OÏ­ßu胅^9´)7põ‰r.«XOؼq¼)NüßêwŒ¥ßù‰åKF;uª°8|±³ØÎÙ¼àzGÐað$¡KÂ&8vt‹oíäÿ¤“ŸÇÅ/÷'–?ÔåÕ™"¯¹+fˆ;Ή#vô qô6$ö?dÞµa‰‡M·0À¦kuƒ- ñͯ¸~#_ND ®½Ï07Qç-ÌÌ­ûZöæóÃÞ˜› }¸.îDtóïH®j=Þ¦êô„#o%éØFWs6òÕ#Á_Ï_üñ;g§½º¯5¶°-ÙôÏ…b*Yy²öžCӔŭ\aërì•Q“ßÜšøÌ©;ÁèÏ7m4A+ÞË"çI}uó+ÇX1n³wI9ÝL̇Î^Ð)åtŠ N!áãìÌ毅UЂ™wSN'b¿K‚°ÚÇ´§Ý›rÚ5äµ¥À™´¾›8 xlêø!¼¾8‡–;¥uD4<ò³ÇûXv7±I'aë²]Å$÷ùÖÅ"âx>óïÏBøT¸gÙ¦«FSŒ2gćÎ]æ…‰Éý*:=Ås6Ì™èÎ¾í§¤Æê ¡G_Û»>%öÄ…Û• -|¿gV…9uÙÎÌ~ÔÔÖ»¤¡6é‡ç³j”L‘ë¨Éá\¸DF9'Äæ«LÑýž|ÑM”´¦—æÛ'ÅݨhæØú…Ι7LDÄpäœ_¨!öÀ‘³ü…­oQ•¦ž¾œ–#½Ó j!6ÏÒz ½ ¾ª=ªK.îÝÏj¨“5È•ê‹#;xNZ0ÉW@Äqò±1Ë‘jôI(3¾^w|RÔ,›¯}:ã5]eÆåãqqûŽ¿V¥é2öfP$n_·qlôÆÀu{_Ly~¾7‡›é37®þ"QÞÝúöÚ¼CÛc^Þî6o…çî¿§«qY 蘳AÁ“ìˆdWb2”Æ7°qµe´V4ccS+µDDZͽ•–µgkŤîÒñ¸[µ-DDõ¹—Ožöya¦OâaÅÎ8sœÙ6Þ®æ­ñåö‰ï“¤J"ÒT^wõ\4‚G$ìmÍ.¬0XûxhÍ eçvD¥ÜäÑ*ë+‹ë;×MTÍÂÀÇ=\ qDdЪÔm5+¾¿O ‹ºš#ë¤I«ÞyqÞ‰™ýÐ燆<¿N}ò˵ï~{¾¦SÓªr·­ùðÉׇDîJxÎ\Bê‹ï½±íþaRž• Ÿi7y¬ý†ôb-. ÓÀu*!"éÂn¦Ð0¸Â¶ÑŒziC÷7@–È®­bò—!¿Ü@h/bÑà :L ›ÖœCÕ9UªŽÊ‡º&·–FHˆÈ| ID–ƒ,ZTžš]§»_p¾tºk—A†¯}ÀŠÁê³T,)úß3¢·Øxš6ñ‰)“&Ínë5eul ÇÒÙo­¹›>5ŇŸ[?&eë‰95žy÷¹èÒ_›!¥ÌO–ÒLo‰Ÿ=—tú-ÌÑéiƒñ,øD¤jhîæÎnhQ·¥ó¶¯ ÏC &ã¾ÄàpÙŒ‡t’ƒñCfÜ»Aû †ûµ˜Õˆi÷¤œ?š¶6ûJÔŽ–-œ:hÚ[»‹‰l¦~øœïžö晵^ L3.ç×[^¯¨o&"ˆ‡kAÇtè•MJ"X » 1-²²¶Õ”l2ØÉ‚Ãdó,mí÷ÖuÍ¿’6ôú®á£-M0ÙÌ߀ ú¶½±Ä)½¼VÑv Cl:ÃÌàÙyÛ¶þS^-×雪å­ÿäï9 ÛLǶõh{[sÖ¹Ý;w|°iëÛïoygÓŽCÒ^Ûïì=y^ü{ÿI#"{O›N³½B¿üëq!Õß(ÐÇ¿sàoC…¿òÑL%Ÿˆ”M*|ë  ÃÐU©+2¤êå0„w°Taì^ZŸ“Q2ˆxnÓŸ]9½›ýòù¡¤»þñ‹Óú$vÛ’—·mËXüÂéû Èñ\‰Hz«S‘tL‡¶"ñLyÙY6ÜüÂcIGW›zìêÐçÇXßGª¢« UÞ“ìˆë:f‘똮?¬-3gæ©õ] EåämMDf®¡{=´ýõêØü¬š/FÐR“_©>˜Eİ zæå ŽÏ­5ghôš²¤s¥~³œ˜D–þ³—ûÏîDŠ/%µ~GJ[‘Ÿí»È‹KDænÁóÝ‚»VfJ¢·Æ¤7kkr« ^ "wèsÞ¡¿óØ~{ÎqjîÿA#üÃWÿâgEѯí+j MlÇå[þõ´ É/¼·ô¹ò–¼×_=¾õ_W¦¿üUi7!Rà>NDTuêJ%&èôcºê)UÞžC%D–‹_›åÖMLÔŸÝ¿ãÇÔ¼ÚÖQ ½¢º´¤í+ZÃ2DºÚK1ÇSeF÷ ´»wdL_Ÿq½ÈH}AhoÑ‹µ¯ ͹ —ë~ùº…ƒˆMD¤—%>žRoä²›±1)íO<4ÈÓŽFÿ˜#7þ! +‰ˆMd¥Ÿ>#Õ=°cû­”Ù»W¼ùÍÁäªæŽ6W—g%îýtÝȹ›Ï·…C3÷%ïò8N½ðflI ?{!º–xA[?^ìnüiF,§é+—X|¿'€þŒeo6­Ð#ºº¼Z¿§Ÿòv öo8}à†ÌèŸü-7®%¿xåÜÅ« ×KXô² "­459£#'¨k³®g”*\.Ï\À7c‘®¹®"ûVJrN¥LÛõq/ªª¬"•Ðz€•Í Òk²Ú¢¬´ÔliÆÐó³f”(VÖb >›AÔ"¯-»}írBVµ\GDdÐÔeßÈ©6,Å"Å ¬,/ñlì¡„EçOÓ)¤7RKz6‡Ïç 8,Fë™5Ý‘æggæ–T« ¤W”¤ß–ªÙÀ\Ð1ïÆ U)d5U¥ÅyéYEåÍúß~l¿õ¥·ŽÙ¶cçç™#_Ÿé@‰ë<Ã>?œ\PÛÞ´lÇ9ßî\ìÆ’Å¼²zKº²ýÄ4¥×ó%ógø5´"6&C~O‰í0ëë/—z™Éþcî¥×@ÿÅ!pG+ô8Jæm½öQˆÕD¯Y¾2îžÁ žë‚ðŠ[©©¹åµr-“oå2|¢ICDDÕ±ŸEýŒe¯ÿh¢I»*¾I‰¯Zv¡ñ÷íŠ)ú໑îÔxz}àêx¬uпaŽNoè¤Ç6„y~1$|Û> ë—ÿº7ëîCtÙ–’ÁNîVNîOüâ}†²[Ù<åYøÓ¨?à‘ʦþgÏù©O¾Þ´Ü…(wOØúSH9ýæèô޾ñÒ–ˆi_¤©hÀŒ·÷{Frw.ׯQlô=Ziüñ´zTs‚Æ3Ï›{ø›ÿ¾rÛqîá¸MË]H“öÍÌeÛЕýæèôšAU|õÇÙüaN¹›·œÈUµ—tôʺ¥ŽaÆø&T Õùi‰Çœ»Yß%¶íðàaâ_ßN–‘˜Z‡Dïí¢¢Úv”Sê–Ùk£S›03àOstÀdaè tttúž£ó(1òÄ~óp>=¶øÀ@øSAEtÀÎà§Öž8ôÆ“â¾oG–ÀÖ30$ìÙgÃݹhø‡s±ˆ'oŒŠ{c¢ mðg¡«Þ'ñø5Û¬òã‘Jûäž‹G*[úòÓ¸ž ŸmÙ)dõz'êÌo7ÿƒè·¯ñê“ccð\LJ½²tÚ” "¢àOÊS+R“.FïÞµój]OûËL2u}¸o0kkJÀ®y/%Ëþ ¤¢ z×n¢q¯·ÊGE߬ [ÔÇ)zÕGc_ÿöÖ®×"&ø8 Ú“pPÀ“‹×LhÖóýiKÌY¸%^Fü€çOìýÛã"\<ý*:½ÁrœõÞÁ72ä}º,bC’±EXB‰ÿ˜À/gG+‹H§Q6Õ×HKKÒ¯&¥Ët·s =.ÀÓu 9›tʺò¬›‰ç ëÛ‚“‘ò†ë¢È÷;ý·ïf3¸6¾cÆŒñsss¬¼èVòÕ‹éÕª{W?` y>æãê`'汈ˆZšïTæçd_»z#_®'b ð}|Òp‡VVBnkAJ«¨++ÌMùùÚÍ eßTF8. ·¿àÁ¤’¨ ïo=“^xGÍ4·ñþÄÓKg¼\¦$""¾ïÚ›ß/sl<1#äÍ ŠÎïæýWÜé0qÕþå¾ï¤5·½¨oJß÷ôÜú]‡>Xèõ—C›rWŸÀºž:&W)°ž°yãx Rœø¿ÕïK9 ¾óË—ŒvêT5`qøb;g±³yÁõŽ ÃàIB—„MpìèßÚÉÿI'?‹_îO,×<Ô“yÍ]1; c -Gìèâè9lHìÿ~ȼ;jÛ:oa€M×ê[0@â7š_qýF¾œˆ\{Ÿan¢Î[˜™[öµì;Ì燽17ú .p]݉èæÿÞ9\Õz¼MÕé GÞJ8Ò±2ÿ쩺e±þ˜àBrs§7;O#&R%Ÿ/jîº[MYÜʶ.Ç^5ùÍÍ¡‰Ïœºƒ,€þ|ÓFô¸Rà½,rŽÔW7¿rÜØˆCà6kq—”ÓMÁÄ|èìRN§˜à>ÎÎìaþZX-˜`l¡P±ßŒ%ÁXí‰ÎcÚÓ‹îM9í òÚR`‹LÚݪí‚ǦŽÂë‹sh¹SZGDÃ#?{q¼e7›”9ß—YN_à#ìÃ샧Jˆ(÷ÇL…±7íø0Ã@¹kü01 _CE§§xîφ9ÝÙ·ý”ÔX‚!ôòk»s×§Äž¸p»²¡…ï÷̪0§.Û™ÙšÚz—4Ô&ýpâ|V’)r59|‚ —h@À(ç„Ø|µ‘)ºÀ“o8’ ‰’ÖôÒ|û䱸Í[¿Ð9󆉈cƒœSâ 5Ä8r–[>P•¦ž¾œ–#½Ó j!6ÏÒz ½ ¾ª=ªK.îÝÏj¨“5È•ê‹#;xNZ0ÉW@Äqò±1Ë‘jôI(3¾^w|RÔ,›¯}:ã5]eÆåãqqûŽ¿V¥é4öÖ|ã𹚅ómŸ þàúÙ¶5É™ƒ'=FD6^¯ÑæÚóòÎp·y+I*Óè4òÊëññ7T­U˜ÁÞÖl"–µ÷""2”Ûu&1·ª^©Õ z­²¾²8³ ±#êUÍB·Ù‹#×¾öÏ ¯o|ã¥5áO¸·Íæ[ñûä×PWsd]ØÄ .HµD,û¡!ϯÛt!ábÊçh{·ié߬"²˜ðòãí `XO÷!"éùË%Ýå/yFT‚œÈnòX{3\¨è˜ ®ÃP Iov3…†Á¶fÔKº/S°Dvmµë¿l ùåB{‹Η¹˜6­9‡ªsªTí¯Ô5¹µ4BBDæ…L"²dÑú£òÔì:Ýý‚ÓØð¥Ó]» ò0ÌxíV VŸÅm,)úß3¢·Øxš6ñ‰)“&Ínë5eul ÇÒÙo­Ñ©²¿Ž)~)ÒeÊ SœOGéˆ!ò[À&’=S¤êv×Êüd)Íô–øÙs©X‹  ŸBE§§ Ƴà‘ª¡¹›;»¡EÝ–NÌÛ¾‚d<1˜Œû~ƒÃe3ÒI Æ™qïí/î×bV#¦Ý“rþhÚÚì+Q;>Z¶pê ioí.&²™úás^íAK›{do¢žÈï™/Y Ÿ5žO$O:œ¥ì~ŸzE}3 D<\C:¦C¯lR‘ÀJØMˆi‘•µ­¦dûø“ÁN&›gik/¸·Ö ¨kþ•´¡×w mi‚Éfþþdзím$NéåµmSpm‡Øt<†™Á³ó¶mý§¼Z®'Ò7UË[ÿ?Èßs@·™ŽmëÑö¶æ¬s»wîø`ÓÖ·ßßòΦ‡¤½8¶ßÙ{ò¼ø÷þ“FDöž6CN-å§?Œ—9<÷ÒX–pÔüÑDM ?\“ßïÊXò‰H٤·®ú1 ]õº"CJ¡^NCxKÆî¥õ9Õ¡!‰ˆç6ýÙ•Ó»ÉCµ¹RÕ(o‘ºð걋™…Õ µž%K†<4\˜r >¯ËWC‹ª…ÈŒˆü}ì nV©|±››eÕÍÛ5=àÒ«ÕD<"’Œé)ý9¿DŸqclÒŽœ)дÔf(F7'âûM™QxìdF­F0(`Ê4ßÖÔ£(̬k!¢ÚÌÜÆ±""¦$ô…páÉ+™yõr•ŽaÆå E¶˜Õ…• =tƒN§ÓétzQK‹Vß‹cëÁIòGlù6ÒêÒÑ}§’S‹«e*Whãê¹Êˆ RÊZªÚÊ áIDAT¡á§‡Š§?ë2%reÀ}' ‰šÏ¾ußå?y®ŽD$½U‰©È:¦C[‘x¦Š¼ìÆ,n~኱¤£«M=vuèóc¬ï¿#UÑÕ„*ïIvÄu³ÈuL×Ö–™3óÔú.…¢òFò¶&"3×п½Úþzul~VM#h©É¯Ô Ì"bØ=órPÇçÖš3 4zMYÒ¹R¿YNL"KÿÙËýgw "Å—’Z¿#¥­HŠÏö]äÅ%"s·àùnÁ]+3%Ñ[cÒ›µ5¹Õ/‘À;ô9ïÐßyl¿=ç¸?5w„ÿ þá«ñ³¢è×öuMªì˜M‰K¿výûßaˆW÷¤Êï7 'ð'"ª:u¥tú1 ]õ”*oÏ¡"ËůÍrë&&jŠÏîßñcj^m먆^Q]ZÒ6Û£cX†HW{)æxªÌè„v÷ŽŒéë3®™$´·èÅÚW†æÜ„Ëu¿|ÝÂAÄ&"ÒË’O©7òNÙÍØ˜”ö'äiG£Ìéf„‡a%±‰ ²ôÓg¤ºvl¿•2{÷Š7¿9˜\XÕÜÑæêò¬Ä½Ÿ®9wóù{¡®òðG1¥D ±ˆîœŒ¾ÜpŸœÃrš¾r‰5QÁ÷{rPÐèÏXöfÐ =¢«Ë«õ{ú)o§`ÿ†ÓnÈŒŽµ4Vܸ–tþâ•s¯&\/a=6ÒË‚ˆ´ÒÔ䌎œ ®ÍºžQªdp¹ ± šè5ËWÆÝ3xÁs]>Bq+55·¼V®eò­\†OX4iˆ€ˆ¨:ö³¨Ÿ±ìõM4iWÅ#)ñÕAË.t7ó†!ð{ÿø·¯8em1wn7#RLqÐßíŒt§ÆÓëWÇc­+€þ stzC'=¶!Ìóëøˆ!áÛöYX¿ü×½Yw't°-%ƒÜ­œÜŸøÅû e·²xÊ1²ð§QÀ#•ÿ|‘UèbǪªVš»E¼ýïWœ‰¨â¿ïÌ3žr<ç§>ùzÓr¢Ü=aëO!å è˜(}ã¥-Ó´Û¿_å7ãí½Çs§~Û¾ˆׯQlô=Ziüñ´zTs‚Æ3Ï›{yÝÌiá‘Ók¼ï¾ Nøð•) ££Qlǹ‡ãÞžÀ%MÚ7ó#>Oh@W è˜0ìâÖˆÀôÕ;^ä~|¼ün @UüÓÁsC}=]\옳‰È j¨.ÎI¿råf~#Jý ‹Ï©©ÖxäAQ|ãâîÿ~¾ý§rU7“nZ*Î}rh»eϪ-g T˜™ð§€9:`²ðõr@Ð@Ð@Ð@Ð@ÐxÔ‚SÜw|ÄÖ”¨—ƉQÂxt<«—ën¹Ðä;u¬kàÒñœŸb“KÕx¬-‚މ0hªÓŽÆ•ºÏ êì?Ç­ø»ø¼&D“¡kÊ=yI5qñh7`çÌcGó•ˆ:¦îQš²¢ÌÙña†„s׆yab2‚ŽiÑæÚ##r›·Â“‹Î@Ð1-òŒ¨9‘Ýä±öfè}Ó¢ÌO–‘ÄÏ%£WÔ7‘@ÄÃutLí„–|"R6©ôè}ÓÂs t$"é­J5zAǤ¼ÃljˆªN]©Ô¢÷tLËiúÊ%ÖDßïÉAAAÇ„°flÛ0ŠCò£ÌÖ ïtLæDÅAÿüêÝ)j<ýÁº³w0AÇ40xÎSþshg¤;Qîž°õ§Êuèx€Gû8Eǹ‡ãÞžÀ%MÚ7ó#>Oh@5àñ¬^®WTTÛŽrJÝ2{mtjÖ,xt0FÜÑ `’° è è è è èôÿ ÃOÞ÷ÆDzL+è˜I¦®÷±5%ê¥qb wúå“‘õ ·\hò:ÖÇ5péxÎO±É¥j<ÑL"è4ÕiGãJÝg†uöŸãVü]|oÓ:DDº¦Ü“—TvóvÎÜw|ÄÖ”¨—ƉQ&€þà=YßpûÈ…&ß©c}\—Žçü›\ªÆÀ$‚‘ASv4®Ô}fèPgÿ9nÅßÅçañ0 CD¤kÊ=yI5qñh7`çÌcGó•ˆ:ðð<èé4ÊœýfH8wm˜&&€)"mÞ¡í12"·y+<¹h`0¥ C$ψJÙMko†“ :¤ÌO–‘ÄÏ%0± £WÔ7‘@ÄÃuÀÄ‚S`É'"e“JÓ :<×@G"’ÞªT£…À¤‚ŽÀ;|œˆ¨êÔ•J-ZL(è°œ¦¯\bMTðýžtÀ„‚Ûaƶ £8$?úñÁl ÚL%è0ÅAÿüêÝ)j<ýÁº³w0L#è0xÎSþshg¤;Qîž°õ§Êuh\x¸Øh7Žsǽ=Kš´oæG|žÐ€j= 0: trace = trace[start:end] while 1: start = trace.find('\n(') if start < 0: break end = trace.find('\n)', start + 1) assert end > 0 trace = trace[:start] + trace[end + 2:] print(trace) binaryen-version_91/scripts/embedwat.py000077500000000000000000000020771362402614000205210ustar00rootroot00000000000000#!/usr/bin/env python3 # # Copyright 2018 WebAssembly Community Group participants # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import sys input_file = sys.argv[1] output_file = sys.argv[2] with open(input_file) as f: wat = f.read() output = """\ // Automatically generated by embedwat.py #include "passes/intrinsics-module.h" static const char theModule[%d] = { """ % (len(wat) + 1) for c in wat: output += str(ord(c)) + ', ' output += '''0 }; namespace wasm { const char* IntrinsicsModuleWast = theModule; } ''' with open(output_file, 'w') as f: f.write(output) binaryen-version_91/scripts/fuzz_opt.py000066400000000000000000000553161362402614000206120ustar00rootroot00000000000000''' Runs random passes and options on random inputs, using wasm-opt. Can be configured to run just wasm-opt itself (using --fuzz-exec) or also run VMs on it. For afl-fuzz integration, you probably don't want this, and can use something like BINARYEN_CORES=1 BINARYEN_PASS_DEBUG=1 afl-fuzz -i afl-testcases/ -o afl-findings/ -m 100 -d -- bin/wasm-opt -ttf --fuzz-exec --Os @@ (that is on a fixed set of arguments to wasm-opt, though - this script covers different options being passed) ''' import os import difflib import subprocess import random import re import shutil import sys import time from test import shared # parameters NANS = True # feature options that are always passed to the tools. # exceptions: https://github.com/WebAssembly/binaryen/issues/2195 # simd: known issues with d8 # atomics, bulk memory: doesn't work in wasm2js # truncsat: https://github.com/WebAssembly/binaryen/issues/2198 CONSTANT_FEATURE_OPTS = ['--all-features'] FUZZ_OPTS = [] INPUT_SIZE_LIMIT = 150 * 1024 # utilities def in_binaryen(*args): return os.path.join(shared.options.binaryen_root, *args) def in_bin(tool): return os.path.join(shared.options.binaryen_root, 'bin', tool) def random_size(): return random.randint(1, INPUT_SIZE_LIMIT) def run(cmd): print(' '.join(cmd)) return subprocess.check_output(cmd) def run_unchecked(cmd): print(' '.join(cmd)) return subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate()[0] def randomize_pass_debug(): if random.random() < 0.125: print('[pass-debug]') os.environ['BINARYEN_PASS_DEBUG'] = '1' else: os.environ['BINARYEN_PASS_DEBUG'] = '0' del os.environ['BINARYEN_PASS_DEBUG'] def randomize_feature_opts(): global FEATURE_OPTS FEATURE_OPTS = CONSTANT_FEATURE_OPTS[:] # half the time apply all the possible opts. this lets all test runners work at max # capacity at least half the time, as otherwise if they need almost all the opts, the # chance of getting them is exponentially small. if random.random() < 0.5: FEATURE_OPTS += POSSIBLE_FEATURE_OPTS else: for possible in POSSIBLE_FEATURE_OPTS: if random.random() < 0.5: FEATURE_OPTS.append(possible) print('feature opts:', ' '.join(FEATURE_OPTS)) # Test outputs we want to ignore are marked this way. IGNORE = '[binaryen-fuzzer-ignore]' def compare(x, y, context): if x != y and x != IGNORE and y != IGNORE: message = ''.join([a + '\n' for a in difflib.unified_diff(x.splitlines(), y.splitlines(), fromfile='expected', tofile='actual')]) raise Exception(context + " comparison error, expected to have '%s' == '%s', diff:\n\n%s" % ( x, y, message )) def fix_output(out): # large doubles may print slightly different on different VMs def fix_double(x): x = x.group(1) if 'nan' in x or 'NaN' in x: x = 'nan' else: x = x.replace('Infinity', 'inf') x = str(float(x)) return 'f64.const ' + x out = re.sub(r'f64\.const (-?[nanN:abcdefxIity\d+-.]+)', fix_double, out) # mark traps from wasm-opt as exceptions, even though they didn't run in a vm out = out.replace('[trap ', 'exception: [trap ') # exceptions may differ when optimizing, but an exception should occur. so ignore their types # also js engines print them out slightly differently return '\n'.join(map(lambda x: ' *exception*' if 'exception' in x else x, out.splitlines())) def fix_spec_output(out): out = fix_output(out) # spec shows a pointer when it traps, remove that out = '\n'.join(map(lambda x: x if 'runtime trap' not in x else x[x.find('runtime trap'):], out.splitlines())) # https://github.com/WebAssembly/spec/issues/543 , float consts are messed up out = '\n'.join(map(lambda x: x if 'f32' not in x and 'f64' not in x else '', out.splitlines())) return out def run_vm(cmd): # ignore some vm assertions, if bugs have already been filed known_issues = [ 'local count too large', # ignore this; can be caused by flatten, ssa, etc. passes 'liftoff-assembler.cc, line 239\n', # https://bugs.chromium.org/p/v8/issues/detail?id=8631 'liftoff-assembler.cc, line 245\n', # https://bugs.chromium.org/p/v8/issues/detail?id=8631 'liftoff-register.h, line 86\n', # https://bugs.chromium.org/p/v8/issues/detail?id=8632 ] try: return run(cmd) except subprocess.CalledProcessError: output = run_unchecked(cmd) for issue in known_issues: if issue in output: return IGNORE raise MAX_INTERPRETER_ENV_VAR = 'BINARYEN_MAX_INTERPRETER_DEPTH' MAX_INTERPRETER_DEPTH = 1000 def run_bynterp(wasm, args): # increase the interpreter stack depth, to test more things os.environ[MAX_INTERPRETER_ENV_VAR] = str(MAX_INTERPRETER_DEPTH) try: return run_vm([in_bin('wasm-opt'), wasm] + FEATURE_OPTS + args) finally: del os.environ['BINARYEN_MAX_INTERPRETER_DEPTH'] def run_d8(wasm): return run_vm([shared.V8] + shared.V8_OPTS + [in_binaryen('scripts', 'fuzz_shell.js'), '--', wasm]) # There are two types of test case handlers: # * get_commands() users: these return a list of commands to run (for example, "run this wasm-opt # command, then that one"). The calling code gets and runs those commands on the test wasm # file, and has enough information and control to be able to perform auto-reduction of any # bugs found. # * Totally generic: These receive the input pattern, a wasm generated from it, and a wasm # optimized from that, and can then do anything it wants with those. class TestCaseHandler: # If the core handle_pair() method is not overridden, it calls handle_single() # on each of the pair. That is useful if you just want the two wasms, and don't # care about their relationship def handle_pair(self, input, before_wasm, after_wasm, opts): self.handle(before_wasm) self.handle(after_wasm) def can_run_on_feature_opts(self, feature_opts): return True # Run VMs and compare results class CompareVMs(TestCaseHandler): def handle_pair(self, input, before_wasm, after_wasm, opts): run([in_bin('wasm-opt'), before_wasm, '--emit-js-wrapper=a.js', '--emit-spec-wrapper=a.wat'] + FEATURE_OPTS) run([in_bin('wasm-opt'), after_wasm, '--emit-js-wrapper=b.js', '--emit-spec-wrapper=b.wat'] + FEATURE_OPTS) before = self.run_vms('a.js', before_wasm) after = self.run_vms('b.js', after_wasm) self.compare_vs(before, after) def run_vms(self, js, wasm): results = [] results.append(fix_output(run_bynterp(wasm, ['--fuzz-exec-before']))) results.append(fix_output(run_vm([shared.V8, js] + shared.V8_OPTS + ['--', wasm]))) # append to add results from VMs # results += [fix_output(run_vm([shared.V8, js] + shared.V8_OPTS + ['--', wasm]))] # results += [fix_output(run_vm([os.path.expanduser('~/.jsvu/jsc'), js, '--', wasm]))] # spec has no mechanism to not halt on a trap. so we just check until the first trap, basically # run(['../spec/interpreter/wasm', wasm]) # results += [fix_spec_output(run_unchecked(['../spec/interpreter/wasm', wasm, '-e', open(prefix + 'wat').read()]))] if len(results) == 0: results = [0] # NaNs are a source of nondeterminism between VMs; don't compare them if not NANS: first = results[0] for i in range(len(results)): compare(first, results[i], 'CompareVMs at ' + str(i)) return results def compare_vs(self, before, after): for i in range(len(before)): compare(before[i], after[i], 'CompareVMs at ' + str(i)) # with nans, we can only compare the binaryen interpreter to itself if NANS: break def can_run_on_feature_opts(self, feature_opts): return all([x in feature_opts for x in ['--disable-simd', '--disable-reference-types', '--disable-exception-handling']]) # Fuzz the interpreter with --fuzz-exec. This tests everything in a single command (no # two separate binaries) so it's easy to reproduce. class FuzzExec(TestCaseHandler): def get_commands(self, wasm, opts, random_seed): return [ '%(MAX_INTERPRETER_ENV_VAR)s=%(MAX_INTERPRETER_DEPTH)d %(wasm_opt)s --fuzz-exec --fuzz-binary %(opts)s %(wasm)s' % { 'MAX_INTERPRETER_ENV_VAR': MAX_INTERPRETER_ENV_VAR, 'MAX_INTERPRETER_DEPTH': MAX_INTERPRETER_DEPTH, 'wasm_opt': in_bin('wasm-opt'), 'opts': ' '.join(opts), 'wasm': wasm } ] # As FuzzExec, but without a separate invocation. This can find internal bugs with generating # the IR (which might be worked around by writing it and then reading it). class FuzzExecImmediately(TestCaseHandler): def handle_pair(self, input, before_wasm, after_wasm, opts): # fuzz binaryen interpreter itself. separate invocation so result is easily reduceable run_bynterp(before_wasm, ['--fuzz-exec', '--fuzz-binary'] + opts) # Check for determinism - the same command must have the same output. # Note that this doesn't use get_commands() intentionally, since we are testing # for something that autoreduction won't help with anyhow (nondeterminism is very # hard to reduce). class CheckDeterminism(TestCaseHandler): def handle_pair(self, input, before_wasm, after_wasm, opts): # check for determinism run([in_bin('wasm-opt'), before_wasm, '-o', 'b1.wasm'] + opts) run([in_bin('wasm-opt'), before_wasm, '-o', 'b2.wasm'] + opts) assert open('b1.wasm').read() == open('b2.wasm').read(), 'output must be deterministic' class Wasm2JS(TestCaseHandler): def handle_pair(self, input, before_wasm, after_wasm, opts): compare(self.run(before_wasm), self.run(after_wasm), 'Wasm2JS') def run(self, wasm): # TODO: wasm2js does not handle nans precisely, and does not # handle oob loads etc. with traps, should we use # FUZZ_OPTS += ['--no-fuzz-nans'] # FUZZ_OPTS += ['--no-fuzz-oob'] # ? wrapper = run([in_bin('wasm-opt'), wasm, '--emit-js-wrapper=/dev/stdout'] + FEATURE_OPTS) cmd = [in_bin('wasm2js'), wasm, '--emscripten'] if random.random() < 0.5: cmd += ['-O'] main = run(cmd + FEATURE_OPTS) with open(os.path.join(shared.options.binaryen_root, 'scripts', 'wasm2js.js')) as f: glue = f.read() with open('js.js', 'w') as f: f.write(glue) f.write(main) f.write(wrapper) out = fix_output(run_vm([shared.NODEJS, 'js.js', 'a.wasm'])) if 'exception' in out: # exception, so ignoring - wasm2js does not have normal wasm trapping, so opts can eliminate a trap out = IGNORE return out def can_run_on_feature_opts(self, feature_opts): return all([x in feature_opts for x in ['--disable-exception-handling', '--disable-simd', '--disable-threads', '--disable-bulk-memory', '--disable-nontrapping-float-to-int', '--disable-tail-call', '--disable-sign-ext', '--disable-reference-types']]) class Asyncify(TestCaseHandler): def handle_pair(self, input, before_wasm, after_wasm, opts): # we must legalize in order to run in JS run([in_bin('wasm-opt'), before_wasm, '--legalize-js-interface', '-o', before_wasm] + FEATURE_OPTS) run([in_bin('wasm-opt'), after_wasm, '--legalize-js-interface', '-o', after_wasm] + FEATURE_OPTS) before = fix_output(run_d8(before_wasm)) after = fix_output(run_d8(after_wasm)) # TODO: also something that actually does async sleeps in the code, say # on the logging commands? # --remove-unused-module-elements removes the asyncify intrinsics, which are not valid to call def do_asyncify(wasm): cmd = [in_bin('wasm-opt'), wasm, '--asyncify', '-o', 't.wasm'] if random.random() < 0.5: cmd += ['--optimize-level=%d' % random.randint(1, 3)] if random.random() < 0.5: cmd += ['--shrink-level=%d' % random.randint(1, 2)] cmd += FEATURE_OPTS run(cmd) out = run_d8('t.wasm') # emit some status logging from asyncify print(out.splitlines()[-1]) # ignore the output from the new asyncify API calls - the ones with asserts will trap, too for ignore in ['[fuzz-exec] calling $asyncify_start_unwind\nexception!\n', '[fuzz-exec] calling $asyncify_start_unwind\n', '[fuzz-exec] calling $asyncify_start_rewind\nexception!\n', '[fuzz-exec] calling $asyncify_start_rewind\n', '[fuzz-exec] calling $asyncify_stop_rewind\n', '[fuzz-exec] calling $asyncify_stop_unwind\n']: out = out.replace(ignore, '') out = '\n'.join([l for l in out.splitlines() if 'asyncify: ' not in l]) return fix_output(out) before_asyncify = do_asyncify(before_wasm) after_asyncify = do_asyncify(after_wasm) compare(before, after, 'Asyncify (before/after)') compare(before, before_asyncify, 'Asyncify (before/before_asyncify)') compare(before, after_asyncify, 'Asyncify (before/after_asyncify)') def can_run_on_feature_opts(self, feature_opts): return all([x in feature_opts for x in ['--disable-exception-handling', '--disable-simd', '--disable-tail-call', '--disable-reference-types']]) # The global list of all test case handlers testcase_handlers = [ FuzzExec(), CompareVMs(), CheckDeterminism(), Wasm2JS(), Asyncify(), FuzzExecImmediately(), ] # Do one test, given an input file for -ttf and some optimizations to run def test_one(random_input, opts): randomize_pass_debug() randomize_feature_opts() run([in_bin('wasm-opt'), random_input, '-ttf', '-o', 'a.wasm'] + FUZZ_OPTS + FEATURE_OPTS) wasm_size = os.stat('a.wasm').st_size bytes = wasm_size print('pre wasm size:', wasm_size) # first, run all handlers that use get_commands(). those don't need the second wasm in the # pair, since they all they do is return their commands, and expect us to run them, and # those commands do the actual testing, by operating on the original input wasm file. by # fuzzing the get_commands() ones first we can find bugs in creating the second wasm (that # has the opts run on it) before we try to create it later down for the passes that # expect to get it as one of their inputs. for testcase_handler in testcase_handlers: if testcase_handler.can_run_on_feature_opts(FEATURE_OPTS): if hasattr(testcase_handler, 'get_commands'): print('running testcase handler:', testcase_handler.__class__.__name__) # if the testcase handler supports giving us a list of commands, then we can get those commands # and use them to do useful things like automatic reduction. in this case we give it the input # wasm plus opts and a random seed (if it needs any internal randomness; we want to have the same # value there if we reduce). random_seed = random.random() # gets commands from the handler, for a given set of optimizations. this is all the commands # needed to run the testing that that handler wants to do. def get_commands(opts): return testcase_handler.get_commands(wasm='a.wasm', opts=opts + FUZZ_OPTS + FEATURE_OPTS, random_seed=random_seed) def write_commands_and_test(opts): commands = get_commands(opts) write_commands(commands, 't.sh') subprocess.check_call(['bash', 't.sh']) try: write_commands_and_test(opts) except subprocess.CalledProcessError: print('') print('====================') print('Found a problem! See "t.sh" for the commands, and "input.wasm" for the input. Auto-reducing to "reduced.wasm" and "tt.sh"...') print('====================') print('') # first, reduce the fuzz opts: keep removing until we can't while 1: reduced = False for i in range(len(opts)): # some opts can't be removed, like --flatten --dfo requires flatten if opts[i] == '--flatten': if i != len(opts) - 1 and opts[i + 1] in ('--dfo', '--local-cse', '--rereloop'): continue shorter = opts[:i] + opts[i + 1:] try: write_commands_and_test(shorter) except subprocess.CalledProcessError: # great, the shorter one is good as well opts = shorter print('reduced opts to ' + ' '.join(opts)) reduced = True break if not reduced: break # second, reduce the wasm # copy a.wasm to a safe place as the reducer will use the commands on new inputs, and the commands work on a.wasm shutil.copyfile('a.wasm', 'input.wasm') # add a command to verify the input. this lets the reducer see that it is indeed working on the input correctly commands = [in_bin('wasm-opt') + ' -all a.wasm'] + get_commands(opts) write_commands(commands, 'tt.sh') # reduce the input to something smaller with the same behavior on the script subprocess.check_call([in_bin('wasm-reduce'), 'input.wasm', '--command=bash tt.sh', '-t', 'a.wasm', '-w', 'reduced.wasm']) print('Finished reduction. See "tt.sh" and "reduced.wasm".') sys.exit(1) print('') # created a second wasm for handlers that want to look at pairs. run([in_bin('wasm-opt'), 'a.wasm', '-o', 'b.wasm'] + opts + FUZZ_OPTS + FEATURE_OPTS) wasm_size = os.stat('b.wasm').st_size bytes += wasm_size print('post wasm size:', wasm_size) for testcase_handler in testcase_handlers: if testcase_handler.can_run_on_feature_opts(FEATURE_OPTS): if not hasattr(testcase_handler, 'get_commands'): print('running testcase handler:', testcase_handler.__class__.__name__) # let the testcase handler handle this testcase however it wants. in this case we give it # the input and both wasms. testcase_handler.handle_pair(input=random_input, before_wasm='a.wasm', after_wasm='b.wasm', opts=opts + FUZZ_OPTS + FEATURE_OPTS) print('') return bytes def write_commands(commands, filename): with open(filename, 'w') as f: f.write('set -e\n') for command in commands: f.write('echo "%s"\n' % command) pre = 'BINARYEN_PASS_DEBUG=%s ' % (os.environ.get('BINARYEN_PASS_DEBUG') or '0') f.write(pre + command + ' &> /dev/null\n') f.write('echo "ok"\n') # main opt_choices = [ [], ['-O1'], ['-O2'], ['-O3'], ['-O4'], ['-Os'], ['-Oz'], ["--coalesce-locals"], # XXX slow, non-default ["--coalesce-locals-learning"], ["--code-pushing"], ["--code-folding"], ["--const-hoisting"], ["--dae"], ["--dae-optimizing"], ["--dce"], ["--directize"], ["--flatten", "--dfo"], ["--duplicate-function-elimination"], ["--flatten"], # ["--fpcast-emu"], # removes indirect call failures as it makes them go through regardless of type ["--inlining"], ["--inlining-optimizing"], ["--flatten", "--local-cse"], ["--generate-stack-ir"], ["--licm"], ["--memory-packing"], ["--merge-blocks"], ['--merge-locals'], ["--optimize-instructions"], ["--optimize-stack-ir"], ["--generate-stack-ir", "--optimize-stack-ir"], ["--pick-load-signs"], ["--precompute"], ["--precompute-propagate"], ["--print"], ["--remove-unused-brs"], ["--remove-unused-nonfunction-module-elements"], ["--remove-unused-module-elements"], ["--remove-unused-names"], ["--reorder-functions"], ["--reorder-locals"], ["--flatten", "--rereloop"], ["--roundtrip"], ["--rse"], ["--simplify-locals"], ["--simplify-locals-nonesting"], ["--simplify-locals-nostructure"], ["--simplify-locals-notee"], ["--simplify-locals-notee-nostructure"], ["--ssa"], ["--vacuum"], ] def get_multiple_opt_choices(): ret = [] # core opts while 1: choice = random.choice(opt_choices) if '--flatten' in ret and '--flatten' in choice: print('avoiding multiple --flatten in a single command, due to exponential overhead') else: ret += choice if len(ret) > 20 or random.random() < 0.3: break # modifiers (if not already implied by a -O? option) if '-O' not in str(ret): if random.random() < 0.5: ret += ['--optimize-level=' + str(random.randint(0, 3))] if random.random() < 0.5: ret += ['--shrink-level=' + str(random.randint(0, 3))] assert ret.count('--flatten') <= 1 return ret # main if not NANS: FUZZ_OPTS += ['--no-fuzz-nans'] # possible feature options that are sometimes passed to the tools. this # contains the list of all possible feature flags we can disable (after # we enable all before that in the constant options) POSSIBLE_FEATURE_OPTS = run([in_bin('wasm-opt'), '--print-features', '-all', in_binaryen('test', 'hello_world.wat'), '-all']).replace('--enable', '--disable').strip().split('\n') print('POSSIBLE_FEATURE_OPTS:', POSSIBLE_FEATURE_OPTS) if __name__ == '__main__': print('checking infinite random inputs') random.seed(time.time() * os.getpid()) temp = 'input.dat' counter = 0 bytes = 0 # wasm bytes tested start_time = time.time() while True: counter += 1 f = open(temp, 'w') size = random_size() print('') print('ITERATION:', counter, 'size:', size, 'speed:', counter / (time.time() - start_time), 'iters/sec, ', bytes / (time.time() - start_time), 'bytes/sec\n') for x in range(size): f.write(chr(random.randint(0, 255))) f.close() opts = get_multiple_opt_choices() print('opts:', ' '.join(opts)) bytes += test_one('input.dat', opts) binaryen-version_91/scripts/fuzz_passes.py000077500000000000000000000101651362402614000213020ustar00rootroot00000000000000#!/usr/bin/env python3 # # Copyright 2016 WebAssembly Community Group participants # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ''' This fuzzes passes, by starting with a working program, then running random passes on the wast, and seeing if they break something Usage: Provide a base filename for a runnable program, e.g. a.out.js. Then we will modify a.out.wast. Note that the program must be built to run using that wast (BINARYEN_METHOD=interpret-s-expr) Other parameters after the first are used when calling the program. ''' from __future__ import print_function import os import random import shutil import subprocess import sys PASSES = [ "duplicate-function-elimination", "dce", "remove-unused-brs", "remove-unused-names", "optimize-instructions", "precompute", "simplify-locals", "vacuum", "coalesce-locals", "reorder-locals", "merge-blocks", "remove-unused-functions", ] # main base = sys.argv[1] wast = base[:-3] + '.wast' print('>>> base program:', base, ', wast:', wast) args = sys.argv[2:] def run(): if os.path.exists(wast): print('>>> running using a wast of size', os.stat(wast).st_size) cmd = ['mozjs', base] + args try: return subprocess.check_output(cmd, stderr=subprocess.STDOUT) except Exception as e: print(">>> !!! ", e, " !!!") original_wast = None try: # get normal output normal = run() print('>>> normal output:\n', normal) assert normal, 'must be output' # ensure we actually use the wast original_wast = wast + '.original.wast' shutil.move(wast, original_wast) assert run() != normal, 'running without the wast must fail' # ensure a bad pass makes it fail def apply_passes(passes): wasm_opt = os.path.join('bin', 'wasm-opt') subprocess.check_call([wasm_opt, original_wast] + passes + ['-o', wast]) apply_passes(['--remove-imports']) assert run() != normal, 'running after a breaking pass must fail' # loop, looking for failures def simplify(passes): # passes is known to fail, try to simplify down by removing more = True while more: more = False print('>>> trying to reduce:', ' '.join(passes), ' [' + str(len(passes)) + ']') for i in range(len(passes)): smaller = passes[:i] + passes[i + 1:] print('>>>>>> try to reduce to:', ' '.join(smaller), ' [' + str(len(smaller)) + ']') try: apply_passes(smaller) assert run() == normal except Exception: # this failed too, so it's a good reduction passes = smaller print('>>> reduction successful') more = True break print('>>> reduced to:', ' '.join(passes)) tested = set() def pick_passes(): ret = [] while 1: str_ret = str(ret) if random.random() < 0.1 and str_ret not in tested: tested.add(str_ret) return ret ret.append('--' + random.choice(PASSES)) counter = 0 while 1: passes = pick_passes() print('>>> [' + str(counter) + '] testing:', ' '.join(passes)) counter += 1 try: apply_passes(passes) except Exception as e: print(e) simplify(passes) break seen = run() if seen != normal: print('>>> bad output:\n', seen) simplify(passes) break finally: if original_wast: shutil.move(original_wast, wast) binaryen-version_91/scripts/fuzz_passes_wast.py000077500000000000000000000072301362402614000223370ustar00rootroot00000000000000#!/usr/bin/env python3 # # Copyright 2016 WebAssembly Community Group participants # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ''' This fuzzes passes, by starting with a wast, then running random passes on the wast, and seeing if they break optimization or validation Usage: Provide the filename of the wast. ''' from __future__ import print_function import os import random import shutil import subprocess import sys PASSES = [ "duplicate-function-elimination", "dce", "remove-unused-brs", "remove-unused-names", "optimize-instructions", "precompute", "simplify-locals", "vacuum", "coalesce-locals", "reorder-locals", "merge-blocks", "remove-unused-functions", ] # main wast = sys.argv[1] print('>>> wast:', wast) args = sys.argv[2:] def run(): try: cmd = ['bin/wasm-opt', wast] print('run', cmd) subprocess.check_call(cmd, stderr=open('/dev/null')) except Exception as e: return ">>> !!! ", e, " !!!" return 'ok' original_wast = None try: # get normal output normal = run() print('>>> normal output:\n', normal) assert normal, 'must be output' # ensure we actually use the wast original_wast = wast + '.original.wast' shutil.move(wast, original_wast) def apply_passes(passes): wasm_opt = os.path.join('bin', 'wasm-opt') subprocess.check_call([wasm_opt, original_wast] + passes + ['-o', wast], stderr=open('/dev/null')) # loop, looking for failures def simplify(passes): # passes is known to fail, try to simplify down by removing more = True while more: more = False print('>>> trying to reduce:', ' '.join(passes), ' [' + str(len(passes)) + ']') for i in range(len(passes)): smaller = passes[:i] + passes[i + 1:] print('>>>>>> try to reduce to:', ' '.join(smaller), ' [' + str(len(smaller)) + ']') try: apply_passes(smaller) assert run() == normal except Exception: # this failed too, so it's a good reduction passes = smaller print('>>> reduction successful') more = True break print('>>> reduced to:', ' '.join(passes)) tested = set() def pick_passes(): # return '--waka'.split(' ') ret = [] while 1: str_ret = str(ret) if random.random() < 0.5 and str_ret not in tested: tested.add(str_ret) return ret ret.append('--' + random.choice(PASSES)) counter = 0 while 1: passes = pick_passes() print('>>> [' + str(counter) + '] testing:', ' '.join(passes)) counter += 1 try: apply_passes(passes) except Exception as e: print(e) simplify(passes) break seen = run() if seen != normal: print('>>> bad output:\n', seen) simplify(passes) break finally: if original_wast: shutil.move(original_wast, wast) binaryen-version_91/scripts/fuzz_relooper.py000077500000000000000000000303701362402614000216330ustar00rootroot00000000000000#!/usr/bin/env python3 # # Copyright 2016 WebAssembly Community Group participants # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ''' This fuzzes the relooper using the C API. ''' from __future__ import print_function import difflib import os import random import subprocess import time seed_init = int(time.time()) seed_init *= seed_init seed_init %= (2**32) if os.environ.get('LD_LIBRARY_PATH'): os.environ['LD_LIBRARY_PATH'] += os.pathsep + 'lib' else: os.environ['LD_LIBRARY_PATH'] = 'lib' counter = 0 while True: # Random decisions seed = seed_init random.seed(seed) seed_init += 1 num = random.randint(2, 250) density = random.random() * random.random() code_likelihood = random.random() code_max = random.randint(0, num if random.random() < 0.5 else 3) max_printed = random.randint(1, num if random.random() < 0.5 else 3) max_decision = num * 20 decisions = [random.randint(1, max_decision) for x in range(num * 3)] branches = [0] * num defaults = [0] * num branch_codes = [0] * num # code on the branch, which may alter the global state # with some probability print the same id for different blocks, # as the printing is the block contents - allow merging etc. opts def printed_id(i): if random.random() < 0.5: return i return i % max_printed printed_ids = [printed_id(i) for i in range(num)] def random_code(): if code_max == 0 or random.random() > code_likelihood: return 0 # no code # A random number to perturb/increment the global state return random.randint(1, code_max) for i in range(num): b = set([]) bs = random.randint(1, max(1, round(density * random.random() * (num - 1)))) for j in range(bs): b.add(random.randint(1, num - 1)) b = list(b) defaults[i] = random.choice(b) b.remove(defaults[i]) branches[i] = b branch_codes[i] = [random_code() for item in range(len(b) + 1)] # one for each branch, plus the default optimize = random.random() < 0.5 print(counter, ':', num, density, optimize, code_likelihood, code_max, max_printed, ', seed =', seed) counter += 1 for temp in ['fuzz.wasm', 'fuzz.wast', 'fast.txt', 'fuzz.slow.js', 'fuzz.c']: try: os.unlink(temp) except OSError: pass # parts entry = ''' var label = 0; var state; var decisions = %s; var index = 0; function check() { if (index >= decisions.length) throw 'HALT'; console.log('(i32.const ' + (-decisions[index]) + ')'); return decisions[index++]; } ''' % str(decisions) slow = entry + '\n' slow += 'label = 0;\n' slow += ''' while(1) switch(label) { ''' fast = ''' #include #include #include "binaryen-c.h" // globals: address 4 is index // decisions are at address 8+ int main() { BinaryenModuleRef module = BinaryenModuleCreate(); // check() // if the end, halt BinaryenExpressionRef halter = BinaryenIf(module, BinaryenBinary(module, BinaryenGeUInt32(), BinaryenLoad(module, 4, 0, 0, 0, BinaryenTypeInt32(), BinaryenConst(module, BinaryenLiteralInt32(4))), BinaryenConst(module, BinaryenLiteralInt32(4 * %d)) // jumps of 4 bytes ), BinaryenUnreachable(module), NULL ); // increment index BinaryenExpressionRef incer = BinaryenStore(module, 4, 0, 0, BinaryenConst(module, BinaryenLiteralInt32(4)), BinaryenBinary(module, BinaryenAddInt32(), BinaryenLoad(module, 4, 0, 0, 0, BinaryenTypeInt32(), BinaryenConst(module, BinaryenLiteralInt32(4))), BinaryenConst(module, BinaryenLiteralInt32(4)) ), BinaryenTypeInt32() ); // optionally, print the return value BinaryenExpressionRef args[] = { BinaryenBinary(module, BinaryenSubInt32(), BinaryenConst(module, BinaryenLiteralInt32(0)), BinaryenLoad(module, 4, 0, 4, 0, BinaryenTypeInt32(), BinaryenLoad(module, 4, 0, 0, 0, BinaryenTypeInt32(), BinaryenConst(module, BinaryenLiteralInt32(4))) ) ) }; BinaryenExpressionRef debugger; if (1) debugger = BinaryenCall(module, "print", args, 1, BinaryenTypeNone()); else debugger = BinaryenNop(module); // return the decision. need to subtract 4 that we just added, // and add 8 since that's where we start, so overall offset 4 BinaryenExpressionRef returner = BinaryenLoad(module, 4, 0, 4, 0, BinaryenTypeInt32(), BinaryenLoad(module, 4, 0, 0, 0, BinaryenTypeInt32(), BinaryenConst(module, BinaryenLiteralInt32(4))) ); BinaryenExpressionRef checkBodyList[] = { halter, incer, debugger, returner }; BinaryenExpressionRef checkBody = BinaryenBlock(module, NULL, checkBodyList, sizeof(checkBodyList) / sizeof(BinaryenExpressionRef), BinaryenTypeInt32() ); BinaryenFunctionTypeRef i = BinaryenAddFunctionType(module, "i", BinaryenTypeInt32(), NULL, 0); BinaryenAddFunction(module, "check", i, NULL, 0, checkBody); // contents of main() begin here RelooperRef relooper = RelooperCreate(module); ''' % len(decisions) for i in range(num): slow += ' case %d: console.log("(i32.const %d)"); state = check(); \n' % ( i, printed_ids[i]) b = branches[i] bc = branch_codes[i] def get_phi(j): phi = '' if bc[j]: phi = 'index += %d; ' % bc[j] return phi for j in range(len(b)): slow += ' if (state %% %d == %d) { %s label = %d; break }\n' % ( len(b) + 1, j, get_phi(j), b[j]) # TODO: split range 1-n into these options slow += ' %slabel = %d; break\n' % (get_phi(-1), defaults[i]) use_switch = [random.random() < 0.5 for i in range(num)] for i in range(num): fast += ''' RelooperBlockRef b%d; { BinaryenExpressionRef args[] = { BinaryenConst(module, BinaryenLiteralInt32(%d)) }; BinaryenExpressionRef list[] = { BinaryenCall(module, "print", args, 1, BinaryenTypeNone()), BinaryenLocalSet(module, 0, BinaryenCall(module, "check", NULL, 0, BinaryenTypeInt32())) }; ''' % (i, printed_ids[i]) if use_switch[i]: fast += ''' b%d = RelooperAddBlockWithSwitch(relooper, BinaryenBlock(module, NULL, list, 2, BinaryenTypeNone()), BinaryenBinary(module, BinaryenRemUInt32(), BinaryenLocalGet(module, 0, BinaryenTypeInt32()), BinaryenConst(module, BinaryenLiteralInt32(%d)) ) ); ''' % (i, len(branches[i]) + 1) else: # non-switch fast += ''' b%d = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2, BinaryenTypeNone())); ''' % i fast += ''' } ''' for i in range(num): b = branches[i] bc = branch_codes[i] def get_phi(j): phi = 'NULL' if bc[j]: # increment the index of global state phi = ''' BinaryenStore(module, 4, 0, 0, BinaryenConst(module, BinaryenLiteralInt32(4)), BinaryenBinary(module, BinaryenAddInt32(), BinaryenLoad(module, 4, 0, 0, 0, BinaryenTypeInt32(), BinaryenConst(module, BinaryenLiteralInt32(4))), BinaryenConst(module, BinaryenLiteralInt32(4 * %d)) ), BinaryenTypeInt32() )''' % bc[j] return phi for j in range(len(b)): if use_switch[i]: total = len(b) + 1 values = ','.join([str(x) for x in range(random.randint(len(b) + 1, max_decision + 2)) if x % total == j]) fast += ''' { BinaryenIndex values[] = { %s }; RelooperAddBranchForSwitch(b%d, b%d, values, sizeof(values) / sizeof(BinaryenIndex), %s); } ''' % (values, i, b[j], get_phi(j)) else: # non-switch fast += ''' RelooperAddBranch(b%d, b%d, BinaryenBinary(module, BinaryenEqInt32(), BinaryenBinary(module, BinaryenRemUInt32(), BinaryenLocalGet(module, 0, BinaryenTypeInt32()), BinaryenConst(module, BinaryenLiteralInt32(%d)) ), BinaryenConst(module, BinaryenLiteralInt32(%d)) ), %s); ''' % (i, b[j], len(b) + 1, j, get_phi(j)) # default branch if use_switch[i]: fast += ''' RelooperAddBranchForSwitch(b%d, b%d, NULL, 0, %s); ''' % (i, defaults[i], get_phi(-1)) else: fast += ''' RelooperAddBranch(b%d, b%d, NULL, %s); ''' % (i, defaults[i], get_phi(-1)) fast += ''' BinaryenExpressionRef body = RelooperRenderAndDispose(relooper, b0, 1); int decisions[] = { %s }; int numDecisions = sizeof(decisions)/sizeof(int); // write out all the decisions, then the body of the function BinaryenExpressionRef full[numDecisions + 1]; { int i; for (i = 0; i < numDecisions; i++) { full[i] = BinaryenStore(module, 4, 0, 0, BinaryenConst(module, BinaryenLiteralInt32(8 + 4 * i)), BinaryenConst(module, BinaryenLiteralInt32(decisions[i])), BinaryenTypeInt32() ); } } full[numDecisions] = body; BinaryenExpressionRef all = BinaryenBlock(module, NULL, full, numDecisions + 1, BinaryenTypeNone()); BinaryenFunctionTypeRef v = BinaryenAddFunctionType(module, "v", BinaryenTypeNone(), NULL, 0); // locals: state, free-for-label BinaryenType localTypes[] = { BinaryenTypeInt32(), BinaryenTypeInt32() }; BinaryenFunctionRef theMain = BinaryenAddFunction(module, "main", v, localTypes, 2, all); BinaryenSetStart(module, theMain); // import BinaryenType iparams[] = { BinaryenTypeInt32() }; BinaryenFunctionTypeRef vi = BinaryenAddFunctionType(module, "vi", BinaryenTypeNone(), iparams, 1); BinaryenAddFunctionImport(module, "print", "spectest", "print", vi); // memory BinaryenSetMemory(module, 1, 1, "mem", NULL, NULL, NULL, 0, 0); // optionally, optimize if (%d) BinaryenModuleOptimize(module); assert(BinaryenModuleValidate(module)); // write it out BinaryenModulePrint(module); BinaryenModuleDispose(module); return 0; } ''' % (', '.join(map(str, decisions)), optimize) slow += '}' open('fuzz.slow.js', 'w').write(slow) open('fuzz.c', 'w').write(fast) print('.') cmd = [os.environ.get('CC') or 'gcc', 'fuzz.c', '-Isrc', '-lbinaryen', '-lasmjs', '-lsupport', '-Llib/.', '-pthread', '-o', 'fuzz'] subprocess.check_call(cmd) print('^') subprocess.check_call(['./fuzz'], stdout=open('fuzz.wast', 'w')) print('*') fast_out = subprocess.Popen(['bin/wasm-shell', 'fuzz.wast'], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0] print('-') node = os.getenv('NODE', 'nodejs') slow_out = subprocess.Popen([node, 'fuzz.slow.js'], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0] print('_') if slow_out != fast_out: print(''.join([a.rstrip() + '\n' for a in difflib.unified_diff( slow_out.split('\n'), fast_out.split('\n'), fromfile='slow', tofile='fast')])) assert False binaryen-version_91/scripts/fuzz_shell.js000066400000000000000000000156271362402614000211040ustar00rootroot00000000000000// Shell integration. if (typeof console === 'undefined') { console = { log: print }; } var tempRet0; var binary; if (typeof process === 'object' && typeof require === 'function' /* node.js detection */) { var args = process.argv.slice(2); binary = require('fs').readFileSync(args[0]); if (!binary.buffer) binary = new Uint8Array(binary); } else { var args; if (typeof scriptArgs != 'undefined') { args = scriptArgs; } else if (typeof arguments != 'undefined') { args = arguments; } if (typeof readbuffer === 'function') { binary = new Uint8Array(readbuffer(args[0])); } else { binary = read(args[0], 'binary'); } } // Utilities. function assert(x, y) { if (!x) throw (y || 'assertion failed');// + new Error().stack; } // Deterministic randomness. var detrand = (function() { var hash = 5381; // TODO DET_RAND_SEED; var x = 0; return function() { hash = (((hash << 5) + hash) ^ (x & 0xff)) >>> 0; x = (x + 1) % 256; return (hash % 256) / 256; }; })(); // Asyncify integration. var Asyncify = { sleeping: false, sleepingFunction: null, sleeps: 0, maxDepth: 0, DATA_ADDR: 4, DATA_MAX: 65536, savedMemory: null, instrumentImports: function(imports) { var ret = {}; for (var module in imports) { ret[module] = {}; for (var i in imports[module]) { if (typeof imports[module][i] === 'function') { (function(module, i) { ret[module][i] = function() { if (!Asyncify.sleeping) { // Sleep if asyncify support is present, and at a certain // probability. if (exports.asyncify_start_unwind && detrand() < 0.5) { // We are called in order to start a sleep/unwind. console.log('asyncify: sleep in ' + i + '...'); Asyncify.sleepingFunction = i; Asyncify.sleeps++; var depth = new Error().stack.split('\n').length - 6; Asyncify.maxDepth = Math.max(Asyncify.maxDepth, depth); // Save the memory we use for data, so after we restore it later, the // sleep/resume appears to have had no change to memory. Asyncify.savedMemory = new Int32Array(view.subarray(Asyncify.DATA_ADDR >> 2, Asyncify.DATA_MAX >> 2)); // Unwinding. // Fill in the data structure. The first value has the stack location, // which for simplicity we can start right after the data structure itself. view[Asyncify.DATA_ADDR >> 2] = Asyncify.DATA_ADDR + 8; // The end of the stack will not be reached here anyhow. view[Asyncify.DATA_ADDR + 4 >> 2] = Asyncify.DATA_MAX; exports.asyncify_start_unwind(Asyncify.DATA_ADDR); Asyncify.sleeping = true; } else { // Don't sleep, normal execution. return imports[module][i].apply(null, arguments); } } else { // We are called as part of a resume/rewind. Stop sleeping. console.log('asyncify: resume in ' + i + '...'); assert(Asyncify.sleepingFunction === i); exports.asyncify_stop_rewind(); // The stack should have been all used up, and so returned to the original state. assert(view[Asyncify.DATA_ADDR >> 2] == Asyncify.DATA_ADDR + 8); assert(view[Asyncify.DATA_ADDR + 4 >> 2] == Asyncify.DATA_MAX); Asyncify.sleeping = false; // Restore the memory to the state from before we slept. view.set(Asyncify.savedMemory, Asyncify.DATA_ADDR >> 2); return imports[module][i].apply(null, arguments); } }; })(module, i); } else { ret[module][i] = imports[module][i]; } } } // Add ignored.print, which is ignored by asyncify, and allows debugging of asyncified code. ret['ignored'] = { 'print': function(x, y) { console.log(x, y) } }; return ret; }, instrumentExports: function(exports) { var ret = {}; for (var e in exports) { if (typeof exports[e] === 'function' && !e.startsWith('asyncify_')) { (function(e) { ret[e] = function() { while (1) { var ret = exports[e].apply(null, arguments); // If we are sleeping, then the stack was unwound; rewind it. if (Asyncify.sleeping) { console.log('asyncify: stop unwind; rewind'); assert(!ret, 'results during sleep are meaningless, just 0'); //console.log('asyncify: after unwind', view[Asyncify.DATA_ADDR >> 2], view[Asyncify.DATA_ADDR + 4 >> 2]); try { exports.asyncify_stop_unwind(); exports.asyncify_start_rewind(Asyncify.DATA_ADDR); } catch (e) { console.log('error in unwind/rewind switch', e); } continue; } return ret; } }; })(e); } else { ret[e] = exports[e]; } } return ret; }, check: function() { assert(!Asyncify.sleeping); }, finish: function() { if (Asyncify.sleeps > 0) { print('asyncify:', 'sleeps:', Asyncify.sleeps, 'max depth:', Asyncify.maxDepth); } }, }; // Fuzz integration. function logValue(x, y) { if (typeof y !== 'undefined') { console.log('[LoggingExternalInterface logging ' + x + ' ' + y + ']'); } else { console.log('[LoggingExternalInterface logging ' + x + ']'); } } // Set up the imports. var imports = { 'fuzzing-support': { 'log-i32': logValue, 'log-i64': logValue, 'log-f32': logValue, 'log-f64': logValue, }, 'env': { 'setTempRet0': function(x) { tempRet0 = x }, 'getTempRet0': function() { return tempRet0 }, }, }; imports = Asyncify.instrumentImports(imports); // Create the wasm. var instance = new WebAssembly.Instance(new WebAssembly.Module(binary), imports); // Handle the exports. var exports = instance.exports; exports = Asyncify.instrumentExports(exports); if (exports.memory) { var view = new Int32Array(exports.memory.buffer); } // Run the wasm. var sortedExports = []; for (var e in exports) { sortedExports.push(e); } sortedExports.sort(); sortedExports = sortedExports.filter(function(e) { // Filter special intrinsic functions. return !e.startsWith('asyncify_'); }); sortedExports.forEach(function(e) { Asyncify.check(); if (typeof exports[e] !== 'function') return; try { console.log('[fuzz-exec] calling $' + e); var result = exports[e](); if (typeof result !== 'undefined') { console.log('[fuzz-exec] note result: $' + e + ' => ' + result); } } catch (e) { console.log('exception!');// + [e, e.stack]); } }); // Finish up Asyncify.finish(); binaryen-version_91/scripts/gen-s-parser.py000077500000000000000000001101341362402614000212260ustar00rootroot00000000000000#!/usr/bin/env python3 # # Copyright 2018 WebAssembly Community Group participants # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import sys instructions = [ ("unreachable", "makeUnreachable()"), ("nop", "makeNop()"), ("block", "makeBlock(s)"), ("loop", "makeLoop(s)"), ("if", "makeIf(s)"), ("then", "makeThenOrElse(s)"), ("else", "makeThenOrElse(s)"), ("br", "makeBreak(s)"), ("br_if", "makeBreak(s)"), ("br_table", "makeBreakTable(s)"), ("return", "makeReturn(s)"), ("call", "makeCall(s, /*isReturn=*/false)"), ("call_indirect", "makeCallIndirect(s, /*isReturn=*/false)"), ("return_call", "makeCall(s, /*isReturn=*/true)"), ("return_call_indirect", "makeCallIndirect(s, /*isReturn=*/true)"), ("drop", "makeDrop(s)"), ("select", "makeSelect(s)"), ("local.get", "makeLocalGet(s)"), ("local.set", "makeLocalSet(s)"), ("local.tee", "makeLocalTee(s)"), ("global.get", "makeGlobalGet(s)"), ("global.set", "makeGlobalSet(s)"), ("memory.init", "makeMemoryInit(s)"), ("data.drop", "makeDataDrop(s)"), ("memory.copy", "makeMemoryCopy(s)"), ("memory.fill", "makeMemoryFill(s)"), ("push", "makePush(s)"), ("i32.pop", "makePop(Type::i32)"), ("i64.pop", "makePop(Type::i64)"), ("f32.pop", "makePop(Type::f32)"), ("f64.pop", "makePop(Type::f64)"), ("v128.pop", "makePop(Type::v128)"), ("funcref.pop", "makePop(Type::funcref)"), ("anyref.pop", "makePop(Type::anyref)"), ("nullref.pop", "makePop(Type::nullref)"), ("exnref.pop", "makePop(Type::exnref)"), ("i32.load", "makeLoad(s, Type::i32, /*isAtomic=*/false)"), ("i64.load", "makeLoad(s, Type::i64, /*isAtomic=*/false)"), ("f32.load", "makeLoad(s, Type::f32, /*isAtomic=*/false)"), ("f64.load", "makeLoad(s, Type::f64, /*isAtomic=*/false)"), ("i32.load8_s", "makeLoad(s, Type::i32, /*isAtomic=*/false)"), ("i32.load8_u", "makeLoad(s, Type::i32, /*isAtomic=*/false)"), ("i32.load16_s", "makeLoad(s, Type::i32, /*isAtomic=*/false)"), ("i32.load16_u", "makeLoad(s, Type::i32, /*isAtomic=*/false)"), ("i64.load8_s", "makeLoad(s, Type::i64, /*isAtomic=*/false)"), ("i64.load8_u", "makeLoad(s, Type::i64, /*isAtomic=*/false)"), ("i64.load16_s", "makeLoad(s, Type::i64, /*isAtomic=*/false)"), ("i64.load16_u", "makeLoad(s, Type::i64, /*isAtomic=*/false)"), ("i64.load32_s", "makeLoad(s, Type::i64, /*isAtomic=*/false)"), ("i64.load32_u", "makeLoad(s, Type::i64, /*isAtomic=*/false)"), ("i32.store", "makeStore(s, Type::i32, /*isAtomic=*/false)"), ("i64.store", "makeStore(s, Type::i64, /*isAtomic=*/false)"), ("f32.store", "makeStore(s, Type::f32, /*isAtomic=*/false)"), ("f64.store", "makeStore(s, Type::f64, /*isAtomic=*/false)"), ("i32.store8", "makeStore(s, Type::i32, /*isAtomic=*/false)"), ("i32.store16", "makeStore(s, Type::i32, /*isAtomic=*/false)"), ("i64.store8", "makeStore(s, Type::i64, /*isAtomic=*/false)"), ("i64.store16", "makeStore(s, Type::i64, /*isAtomic=*/false)"), ("i64.store32", "makeStore(s, Type::i64, /*isAtomic=*/false)"), ("memory.size", "makeHost(s, HostOp::MemorySize)"), ("memory.grow", "makeHost(s, HostOp::MemoryGrow)"), ("i32.const", "makeConst(s, Type::i32)"), ("i64.const", "makeConst(s, Type::i64)"), ("f32.const", "makeConst(s, Type::f32)"), ("f64.const", "makeConst(s, Type::f64)"), ("i32.eqz", "makeUnary(s, UnaryOp::EqZInt32)"), ("i32.eq", "makeBinary(s, BinaryOp::EqInt32)"), ("i32.ne", "makeBinary(s, BinaryOp::NeInt32)"), ("i32.lt_s", "makeBinary(s, BinaryOp::LtSInt32)"), ("i32.lt_u", "makeBinary(s, BinaryOp::LtUInt32)"), ("i32.gt_s", "makeBinary(s, BinaryOp::GtSInt32)"), ("i32.gt_u", "makeBinary(s, BinaryOp::GtUInt32)"), ("i32.le_s", "makeBinary(s, BinaryOp::LeSInt32)"), ("i32.le_u", "makeBinary(s, BinaryOp::LeUInt32)"), ("i32.ge_s", "makeBinary(s, BinaryOp::GeSInt32)"), ("i32.ge_u", "makeBinary(s, BinaryOp::GeUInt32)"), ("i64.eqz", "makeUnary(s, UnaryOp::EqZInt64)"), ("i64.eq", "makeBinary(s, BinaryOp::EqInt64)"), ("i64.ne", "makeBinary(s, BinaryOp::NeInt64)"), ("i64.lt_s", "makeBinary(s, BinaryOp::LtSInt64)"), ("i64.lt_u", "makeBinary(s, BinaryOp::LtUInt64)"), ("i64.gt_s", "makeBinary(s, BinaryOp::GtSInt64)"), ("i64.gt_u", "makeBinary(s, BinaryOp::GtUInt64)"), ("i64.le_s", "makeBinary(s, BinaryOp::LeSInt64)"), ("i64.le_u", "makeBinary(s, BinaryOp::LeUInt64)"), ("i64.ge_s", "makeBinary(s, BinaryOp::GeSInt64)"), ("i64.ge_u", "makeBinary(s, BinaryOp::GeUInt64)"), ("f32.eq", "makeBinary(s, BinaryOp::EqFloat32)"), ("f32.ne", "makeBinary(s, BinaryOp::NeFloat32)"), ("f32.lt", "makeBinary(s, BinaryOp::LtFloat32)"), ("f32.gt", "makeBinary(s, BinaryOp::GtFloat32)"), ("f32.le", "makeBinary(s, BinaryOp::LeFloat32)"), ("f32.ge", "makeBinary(s, BinaryOp::GeFloat32)"), ("f64.eq", "makeBinary(s, BinaryOp::EqFloat64)"), ("f64.ne", "makeBinary(s, BinaryOp::NeFloat64)"), ("f64.lt", "makeBinary(s, BinaryOp::LtFloat64)"), ("f64.gt", "makeBinary(s, BinaryOp::GtFloat64)"), ("f64.le", "makeBinary(s, BinaryOp::LeFloat64)"), ("f64.ge", "makeBinary(s, BinaryOp::GeFloat64)"), ("i32.clz", "makeUnary(s, UnaryOp::ClzInt32)"), ("i32.ctz", "makeUnary(s, UnaryOp::CtzInt32)"), ("i32.popcnt", "makeUnary(s, UnaryOp::PopcntInt32)"), ("i32.add", "makeBinary(s, BinaryOp::AddInt32)"), ("i32.sub", "makeBinary(s, BinaryOp::SubInt32)"), ("i32.mul", "makeBinary(s, BinaryOp::MulInt32)"), ("i32.div_s", "makeBinary(s, BinaryOp::DivSInt32)"), ("i32.div_u", "makeBinary(s, BinaryOp::DivUInt32)"), ("i32.rem_s", "makeBinary(s, BinaryOp::RemSInt32)"), ("i32.rem_u", "makeBinary(s, BinaryOp::RemUInt32)"), ("i32.and", "makeBinary(s, BinaryOp::AndInt32)"), ("i32.or", "makeBinary(s, BinaryOp::OrInt32)"), ("i32.xor", "makeBinary(s, BinaryOp::XorInt32)"), ("i32.shl", "makeBinary(s, BinaryOp::ShlInt32)"), ("i32.shr_s", "makeBinary(s, BinaryOp::ShrSInt32)"), ("i32.shr_u", "makeBinary(s, BinaryOp::ShrUInt32)"), ("i32.rotl", "makeBinary(s, BinaryOp::RotLInt32)"), ("i32.rotr", "makeBinary(s, BinaryOp::RotRInt32)"), ("i64.clz", "makeUnary(s, UnaryOp::ClzInt64)"), ("i64.ctz", "makeUnary(s, UnaryOp::CtzInt64)"), ("i64.popcnt", "makeUnary(s, UnaryOp::PopcntInt64)"), ("i64.add", "makeBinary(s, BinaryOp::AddInt64)"), ("i64.sub", "makeBinary(s, BinaryOp::SubInt64)"), ("i64.mul", "makeBinary(s, BinaryOp::MulInt64)"), ("i64.div_s", "makeBinary(s, BinaryOp::DivSInt64)"), ("i64.div_u", "makeBinary(s, BinaryOp::DivUInt64)"), ("i64.rem_s", "makeBinary(s, BinaryOp::RemSInt64)"), ("i64.rem_u", "makeBinary(s, BinaryOp::RemUInt64)"), ("i64.and", "makeBinary(s, BinaryOp::AndInt64)"), ("i64.or", "makeBinary(s, BinaryOp::OrInt64)"), ("i64.xor", "makeBinary(s, BinaryOp::XorInt64)"), ("i64.shl", "makeBinary(s, BinaryOp::ShlInt64)"), ("i64.shr_s", "makeBinary(s, BinaryOp::ShrSInt64)"), ("i64.shr_u", "makeBinary(s, BinaryOp::ShrUInt64)"), ("i64.rotl", "makeBinary(s, BinaryOp::RotLInt64)"), ("i64.rotr", "makeBinary(s, BinaryOp::RotRInt64)"), ("f32.abs", "makeUnary(s, UnaryOp::AbsFloat32)"), ("f32.neg", "makeUnary(s, UnaryOp::NegFloat32)"), ("f32.ceil", "makeUnary(s, UnaryOp::CeilFloat32)"), ("f32.floor", "makeUnary(s, UnaryOp::FloorFloat32)"), ("f32.trunc", "makeUnary(s, UnaryOp::TruncFloat32)"), ("f32.nearest", "makeUnary(s, UnaryOp::NearestFloat32)"), ("f32.sqrt", "makeUnary(s, UnaryOp::SqrtFloat32)"), ("f32.add", "makeBinary(s, BinaryOp::AddFloat32)"), ("f32.sub", "makeBinary(s, BinaryOp::SubFloat32)"), ("f32.mul", "makeBinary(s, BinaryOp::MulFloat32)"), ("f32.div", "makeBinary(s, BinaryOp::DivFloat32)"), ("f32.min", "makeBinary(s, BinaryOp::MinFloat32)"), ("f32.max", "makeBinary(s, BinaryOp::MaxFloat32)"), ("f32.copysign", "makeBinary(s, BinaryOp::CopySignFloat32)"), ("f64.abs", "makeUnary(s, UnaryOp::AbsFloat64)"), ("f64.neg", "makeUnary(s, UnaryOp::NegFloat64)"), ("f64.ceil", "makeUnary(s, UnaryOp::CeilFloat64)"), ("f64.floor", "makeUnary(s, UnaryOp::FloorFloat64)"), ("f64.trunc", "makeUnary(s, UnaryOp::TruncFloat64)"), ("f64.nearest", "makeUnary(s, UnaryOp::NearestFloat64)"), ("f64.sqrt", "makeUnary(s, UnaryOp::SqrtFloat64)"), ("f64.add", "makeBinary(s, BinaryOp::AddFloat64)"), ("f64.sub", "makeBinary(s, BinaryOp::SubFloat64)"), ("f64.mul", "makeBinary(s, BinaryOp::MulFloat64)"), ("f64.div", "makeBinary(s, BinaryOp::DivFloat64)"), ("f64.min", "makeBinary(s, BinaryOp::MinFloat64)"), ("f64.max", "makeBinary(s, BinaryOp::MaxFloat64)"), ("f64.copysign", "makeBinary(s, BinaryOp::CopySignFloat64)"), ("i32.wrap_i64", "makeUnary(s, UnaryOp::WrapInt64)"), ("i32.trunc_f32_s", "makeUnary(s, UnaryOp::TruncSFloat32ToInt32)"), ("i32.trunc_f32_u", "makeUnary(s, UnaryOp::TruncUFloat32ToInt32)"), ("i32.trunc_f64_s", "makeUnary(s, UnaryOp::TruncSFloat64ToInt32)"), ("i32.trunc_f64_u", "makeUnary(s, UnaryOp::TruncUFloat64ToInt32)"), ("i64.extend_i32_s", "makeUnary(s, UnaryOp::ExtendSInt32)"), ("i64.extend_i32_u", "makeUnary(s, UnaryOp::ExtendUInt32)"), ("i64.trunc_f32_s", "makeUnary(s, UnaryOp::TruncSFloat32ToInt64)"), ("i64.trunc_f32_u", "makeUnary(s, UnaryOp::TruncUFloat32ToInt64)"), ("i64.trunc_f64_s", "makeUnary(s, UnaryOp::TruncSFloat64ToInt64)"), ("i64.trunc_f64_u", "makeUnary(s, UnaryOp::TruncUFloat64ToInt64)"), ("f32.convert_i32_s", "makeUnary(s, UnaryOp::ConvertSInt32ToFloat32)"), ("f32.convert_i32_u", "makeUnary(s, UnaryOp::ConvertUInt32ToFloat32)"), ("f32.convert_i64_s", "makeUnary(s, UnaryOp::ConvertSInt64ToFloat32)"), ("f32.convert_i64_u", "makeUnary(s, UnaryOp::ConvertUInt64ToFloat32)"), ("f32.demote_f64", "makeUnary(s, UnaryOp::DemoteFloat64)"), ("f64.convert_i32_s", "makeUnary(s, UnaryOp::ConvertSInt32ToFloat64)"), ("f64.convert_i32_u", "makeUnary(s, UnaryOp::ConvertUInt32ToFloat64)"), ("f64.convert_i64_s", "makeUnary(s, UnaryOp::ConvertSInt64ToFloat64)"), ("f64.convert_i64_u", "makeUnary(s, UnaryOp::ConvertUInt64ToFloat64)"), ("f64.promote_f32", "makeUnary(s, UnaryOp::PromoteFloat32)"), ("i32.reinterpret_f32", "makeUnary(s, UnaryOp::ReinterpretFloat32)"), ("i64.reinterpret_f64", "makeUnary(s, UnaryOp::ReinterpretFloat64)"), ("f32.reinterpret_i32", "makeUnary(s, UnaryOp::ReinterpretInt32)"), ("f64.reinterpret_i64", "makeUnary(s, UnaryOp::ReinterpretInt64)"), ("i32.extend8_s", "makeUnary(s, UnaryOp::ExtendS8Int32)"), ("i32.extend16_s", "makeUnary(s, UnaryOp::ExtendS16Int32)"), ("i64.extend8_s", "makeUnary(s, UnaryOp::ExtendS8Int64)"), ("i64.extend16_s", "makeUnary(s, UnaryOp::ExtendS16Int64)"), ("i64.extend32_s", "makeUnary(s, UnaryOp::ExtendS32Int64)"), # atomic instructions ("atomic.notify", "makeAtomicNotify(s)"), ("i32.atomic.wait", "makeAtomicWait(s, Type::i32)"), ("i64.atomic.wait", "makeAtomicWait(s, Type::i64)"), ("atomic.fence", "makeAtomicFence(s)"), ("i32.atomic.load8_u", "makeLoad(s, Type::i32, /*isAtomic=*/true)"), ("i32.atomic.load16_u", "makeLoad(s, Type::i32, /*isAtomic=*/true)"), ("i32.atomic.load", "makeLoad(s, Type::i32, /*isAtomic=*/true)"), ("i64.atomic.load8_u", "makeLoad(s, Type::i64, /*isAtomic=*/true)"), ("i64.atomic.load16_u", "makeLoad(s, Type::i64, /*isAtomic=*/true)"), ("i64.atomic.load32_u", "makeLoad(s, Type::i64, /*isAtomic=*/true)"), ("i64.atomic.load", "makeLoad(s, Type::i64, /*isAtomic=*/true)"), ("i32.atomic.store8", "makeStore(s, Type::i32, /*isAtomic=*/true)"), ("i32.atomic.store16", "makeStore(s, Type::i32, /*isAtomic=*/true)"), ("i32.atomic.store", "makeStore(s, Type::i32, /*isAtomic=*/true)"), ("i64.atomic.store8", "makeStore(s, Type::i64, /*isAtomic=*/true)"), ("i64.atomic.store16", "makeStore(s, Type::i64, /*isAtomic=*/true)"), ("i64.atomic.store32", "makeStore(s, Type::i64, /*isAtomic=*/true)"), ("i64.atomic.store", "makeStore(s, Type::i64, /*isAtomic=*/true)"), ("i32.atomic.rmw8.add_u", "makeAtomicRMWOrCmpxchg(s, Type::i32)"), ("i32.atomic.rmw16.add_u", "makeAtomicRMWOrCmpxchg(s, Type::i32)"), ("i32.atomic.rmw.add", "makeAtomicRMWOrCmpxchg(s, Type::i32)"), ("i64.atomic.rmw8.add_u", "makeAtomicRMWOrCmpxchg(s, Type::i64)"), ("i64.atomic.rmw16.add_u", "makeAtomicRMWOrCmpxchg(s, Type::i64)"), ("i64.atomic.rmw32.add_u", "makeAtomicRMWOrCmpxchg(s, Type::i64)"), ("i64.atomic.rmw.add", "makeAtomicRMWOrCmpxchg(s, Type::i64)"), ("i32.atomic.rmw8.sub_u", "makeAtomicRMWOrCmpxchg(s, Type::i32)"), ("i32.atomic.rmw16.sub_u", "makeAtomicRMWOrCmpxchg(s, Type::i32)"), ("i32.atomic.rmw.sub", "makeAtomicRMWOrCmpxchg(s, Type::i32)"), ("i64.atomic.rmw8.sub_u", "makeAtomicRMWOrCmpxchg(s, Type::i64)"), ("i64.atomic.rmw16.sub_u", "makeAtomicRMWOrCmpxchg(s, Type::i64)"), ("i64.atomic.rmw32.sub_u", "makeAtomicRMWOrCmpxchg(s, Type::i64)"), ("i64.atomic.rmw.sub", "makeAtomicRMWOrCmpxchg(s, Type::i64)"), ("i32.atomic.rmw8.and_u", "makeAtomicRMWOrCmpxchg(s, Type::i32)"), ("i32.atomic.rmw16.and_u", "makeAtomicRMWOrCmpxchg(s, Type::i32)"), ("i32.atomic.rmw.and", "makeAtomicRMWOrCmpxchg(s, Type::i32)"), ("i64.atomic.rmw8.and_u", "makeAtomicRMWOrCmpxchg(s, Type::i64)"), ("i64.atomic.rmw16.and_u", "makeAtomicRMWOrCmpxchg(s, Type::i64)"), ("i64.atomic.rmw32.and_u", "makeAtomicRMWOrCmpxchg(s, Type::i64)"), ("i64.atomic.rmw.and", "makeAtomicRMWOrCmpxchg(s, Type::i64)"), ("i32.atomic.rmw8.or_u", "makeAtomicRMWOrCmpxchg(s, Type::i32)"), ("i32.atomic.rmw16.or_u", "makeAtomicRMWOrCmpxchg(s, Type::i32)"), ("i32.atomic.rmw.or", "makeAtomicRMWOrCmpxchg(s, Type::i32)"), ("i64.atomic.rmw8.or_u", "makeAtomicRMWOrCmpxchg(s, Type::i64)"), ("i64.atomic.rmw16.or_u", "makeAtomicRMWOrCmpxchg(s, Type::i64)"), ("i64.atomic.rmw32.or_u", "makeAtomicRMWOrCmpxchg(s, Type::i64)"), ("i64.atomic.rmw.or", "makeAtomicRMWOrCmpxchg(s, Type::i64)"), ("i32.atomic.rmw8.xor_u", "makeAtomicRMWOrCmpxchg(s, Type::i32)"), ("i32.atomic.rmw16.xor_u", "makeAtomicRMWOrCmpxchg(s, Type::i32)"), ("i32.atomic.rmw.xor", "makeAtomicRMWOrCmpxchg(s, Type::i32)"), ("i64.atomic.rmw8.xor_u", "makeAtomicRMWOrCmpxchg(s, Type::i64)"), ("i64.atomic.rmw16.xor_u", "makeAtomicRMWOrCmpxchg(s, Type::i64)"), ("i64.atomic.rmw32.xor_u", "makeAtomicRMWOrCmpxchg(s, Type::i64)"), ("i64.atomic.rmw.xor", "makeAtomicRMWOrCmpxchg(s, Type::i64)"), ("i32.atomic.rmw8.xchg_u", "makeAtomicRMWOrCmpxchg(s, Type::i32)"), ("i32.atomic.rmw16.xchg_u", "makeAtomicRMWOrCmpxchg(s, Type::i32)"), ("i32.atomic.rmw.xchg", "makeAtomicRMWOrCmpxchg(s, Type::i32)"), ("i64.atomic.rmw8.xchg_u", "makeAtomicRMWOrCmpxchg(s, Type::i64)"), ("i64.atomic.rmw16.xchg_u", "makeAtomicRMWOrCmpxchg(s, Type::i64)"), ("i64.atomic.rmw32.xchg_u", "makeAtomicRMWOrCmpxchg(s, Type::i64)"), ("i64.atomic.rmw.xchg", "makeAtomicRMWOrCmpxchg(s, Type::i64)"), ("i32.atomic.rmw8.cmpxchg_u", "makeAtomicRMWOrCmpxchg(s, Type::i32)"), ("i32.atomic.rmw16.cmpxchg_u", "makeAtomicRMWOrCmpxchg(s, Type::i32)"), ("i32.atomic.rmw.cmpxchg", "makeAtomicRMWOrCmpxchg(s, Type::i32)"), ("i64.atomic.rmw8.cmpxchg_u", "makeAtomicRMWOrCmpxchg(s, Type::i64)"), ("i64.atomic.rmw16.cmpxchg_u", "makeAtomicRMWOrCmpxchg(s, Type::i64)"), ("i64.atomic.rmw32.cmpxchg_u", "makeAtomicRMWOrCmpxchg(s, Type::i64)"), ("i64.atomic.rmw.cmpxchg", "makeAtomicRMWOrCmpxchg(s, Type::i64)"), # nontrapping float-to-int instructions ("i32.trunc_sat_f32_s", "makeUnary(s, UnaryOp::TruncSatSFloat32ToInt32)"), ("i32.trunc_sat_f32_u", "makeUnary(s, UnaryOp::TruncSatUFloat32ToInt32)"), ("i32.trunc_sat_f64_s", "makeUnary(s, UnaryOp::TruncSatSFloat64ToInt32)"), ("i32.trunc_sat_f64_u", "makeUnary(s, UnaryOp::TruncSatUFloat64ToInt32)"), ("i64.trunc_sat_f32_s", "makeUnary(s, UnaryOp::TruncSatSFloat32ToInt64)"), ("i64.trunc_sat_f32_u", "makeUnary(s, UnaryOp::TruncSatUFloat32ToInt64)"), ("i64.trunc_sat_f64_s", "makeUnary(s, UnaryOp::TruncSatSFloat64ToInt64)"), ("i64.trunc_sat_f64_u", "makeUnary(s, UnaryOp::TruncSatUFloat64ToInt64)"), # SIMD ops ("v128.load", "makeLoad(s, Type::v128, /*isAtomic=*/false)"), ("v128.store", "makeStore(s, Type::v128, /*isAtomic=*/false)"), ("v128.const", "makeConst(s, Type::v128)"), ("v8x16.shuffle", "makeSIMDShuffle(s)"), ("i8x16.splat", "makeUnary(s, UnaryOp::SplatVecI8x16)"), ("i8x16.extract_lane_s", "makeSIMDExtract(s, SIMDExtractOp::ExtractLaneSVecI8x16, 16)"), ("i8x16.extract_lane_u", "makeSIMDExtract(s, SIMDExtractOp::ExtractLaneUVecI8x16, 16)"), ("i8x16.replace_lane", "makeSIMDReplace(s, SIMDReplaceOp::ReplaceLaneVecI8x16, 16)"), ("i16x8.splat", "makeUnary(s, UnaryOp::SplatVecI16x8)"), ("i16x8.extract_lane_s", "makeSIMDExtract(s, SIMDExtractOp::ExtractLaneSVecI16x8, 8)"), ("i16x8.extract_lane_u", "makeSIMDExtract(s, SIMDExtractOp::ExtractLaneUVecI16x8, 8)"), ("i16x8.replace_lane", "makeSIMDReplace(s, SIMDReplaceOp::ReplaceLaneVecI16x8, 8)"), ("i32x4.splat", "makeUnary(s, UnaryOp::SplatVecI32x4)"), ("i32x4.extract_lane", "makeSIMDExtract(s, SIMDExtractOp::ExtractLaneVecI32x4, 4)"), ("i32x4.replace_lane", "makeSIMDReplace(s, SIMDReplaceOp::ReplaceLaneVecI32x4, 4)"), ("i64x2.splat", "makeUnary(s, UnaryOp::SplatVecI64x2)"), ("i64x2.extract_lane", "makeSIMDExtract(s, SIMDExtractOp::ExtractLaneVecI64x2, 2)"), ("i64x2.replace_lane", "makeSIMDReplace(s, SIMDReplaceOp::ReplaceLaneVecI64x2, 2)"), ("f32x4.splat", "makeUnary(s, UnaryOp::SplatVecF32x4)"), ("f32x4.extract_lane", "makeSIMDExtract(s, SIMDExtractOp::ExtractLaneVecF32x4, 4)"), ("f32x4.replace_lane", "makeSIMDReplace(s, SIMDReplaceOp::ReplaceLaneVecF32x4, 4)"), ("f64x2.splat", "makeUnary(s, UnaryOp::SplatVecF64x2)"), ("f64x2.extract_lane", "makeSIMDExtract(s, SIMDExtractOp::ExtractLaneVecF64x2, 2)"), ("f64x2.replace_lane", "makeSIMDReplace(s, SIMDReplaceOp::ReplaceLaneVecF64x2, 2)"), ("i8x16.eq", "makeBinary(s, BinaryOp::EqVecI8x16)"), ("i8x16.ne", "makeBinary(s, BinaryOp::NeVecI8x16)"), ("i8x16.lt_s", "makeBinary(s, BinaryOp::LtSVecI8x16)"), ("i8x16.lt_u", "makeBinary(s, BinaryOp::LtUVecI8x16)"), ("i8x16.gt_s", "makeBinary(s, BinaryOp::GtSVecI8x16)"), ("i8x16.gt_u", "makeBinary(s, BinaryOp::GtUVecI8x16)"), ("i8x16.le_s", "makeBinary(s, BinaryOp::LeSVecI8x16)"), ("i8x16.le_u", "makeBinary(s, BinaryOp::LeUVecI8x16)"), ("i8x16.ge_s", "makeBinary(s, BinaryOp::GeSVecI8x16)"), ("i8x16.ge_u", "makeBinary(s, BinaryOp::GeUVecI8x16)"), ("i16x8.eq", "makeBinary(s, BinaryOp::EqVecI16x8)"), ("i16x8.ne", "makeBinary(s, BinaryOp::NeVecI16x8)"), ("i16x8.lt_s", "makeBinary(s, BinaryOp::LtSVecI16x8)"), ("i16x8.lt_u", "makeBinary(s, BinaryOp::LtUVecI16x8)"), ("i16x8.gt_s", "makeBinary(s, BinaryOp::GtSVecI16x8)"), ("i16x8.gt_u", "makeBinary(s, BinaryOp::GtUVecI16x8)"), ("i16x8.le_s", "makeBinary(s, BinaryOp::LeSVecI16x8)"), ("i16x8.le_u", "makeBinary(s, BinaryOp::LeUVecI16x8)"), ("i16x8.ge_s", "makeBinary(s, BinaryOp::GeSVecI16x8)"), ("i16x8.ge_u", "makeBinary(s, BinaryOp::GeUVecI16x8)"), ("i32x4.eq", "makeBinary(s, BinaryOp::EqVecI32x4)"), ("i32x4.ne", "makeBinary(s, BinaryOp::NeVecI32x4)"), ("i32x4.lt_s", "makeBinary(s, BinaryOp::LtSVecI32x4)"), ("i32x4.lt_u", "makeBinary(s, BinaryOp::LtUVecI32x4)"), ("i32x4.gt_s", "makeBinary(s, BinaryOp::GtSVecI32x4)"), ("i32x4.gt_u", "makeBinary(s, BinaryOp::GtUVecI32x4)"), ("i32x4.le_s", "makeBinary(s, BinaryOp::LeSVecI32x4)"), ("i32x4.le_u", "makeBinary(s, BinaryOp::LeUVecI32x4)"), ("i32x4.ge_s", "makeBinary(s, BinaryOp::GeSVecI32x4)"), ("i32x4.ge_u", "makeBinary(s, BinaryOp::GeUVecI32x4)"), ("f32x4.eq", "makeBinary(s, BinaryOp::EqVecF32x4)"), ("f32x4.ne", "makeBinary(s, BinaryOp::NeVecF32x4)"), ("f32x4.lt", "makeBinary(s, BinaryOp::LtVecF32x4)"), ("f32x4.gt", "makeBinary(s, BinaryOp::GtVecF32x4)"), ("f32x4.le", "makeBinary(s, BinaryOp::LeVecF32x4)"), ("f32x4.ge", "makeBinary(s, BinaryOp::GeVecF32x4)"), ("f64x2.eq", "makeBinary(s, BinaryOp::EqVecF64x2)"), ("f64x2.ne", "makeBinary(s, BinaryOp::NeVecF64x2)"), ("f64x2.lt", "makeBinary(s, BinaryOp::LtVecF64x2)"), ("f64x2.gt", "makeBinary(s, BinaryOp::GtVecF64x2)"), ("f64x2.le", "makeBinary(s, BinaryOp::LeVecF64x2)"), ("f64x2.ge", "makeBinary(s, BinaryOp::GeVecF64x2)"), ("v128.not", "makeUnary(s, UnaryOp::NotVec128)"), ("v128.and", "makeBinary(s, BinaryOp::AndVec128)"), ("v128.or", "makeBinary(s, BinaryOp::OrVec128)"), ("v128.xor", "makeBinary(s, BinaryOp::XorVec128)"), ("v128.andnot", "makeBinary(s, BinaryOp::AndNotVec128)"), ("v128.bitselect", "makeSIMDTernary(s, SIMDTernaryOp::Bitselect)"), ("i8x16.neg", "makeUnary(s, UnaryOp::NegVecI8x16)"), ("i8x16.any_true", "makeUnary(s, UnaryOp::AnyTrueVecI8x16)"), ("i8x16.all_true", "makeUnary(s, UnaryOp::AllTrueVecI8x16)"), ("i8x16.shl", "makeSIMDShift(s, SIMDShiftOp::ShlVecI8x16)"), ("i8x16.shr_s", "makeSIMDShift(s, SIMDShiftOp::ShrSVecI8x16)"), ("i8x16.shr_u", "makeSIMDShift(s, SIMDShiftOp::ShrUVecI8x16)"), ("i8x16.add", "makeBinary(s, BinaryOp::AddVecI8x16)"), ("i8x16.add_saturate_s", "makeBinary(s, BinaryOp::AddSatSVecI8x16)"), ("i8x16.add_saturate_u", "makeBinary(s, BinaryOp::AddSatUVecI8x16)"), ("i8x16.sub", "makeBinary(s, BinaryOp::SubVecI8x16)"), ("i8x16.sub_saturate_s", "makeBinary(s, BinaryOp::SubSatSVecI8x16)"), ("i8x16.sub_saturate_u", "makeBinary(s, BinaryOp::SubSatUVecI8x16)"), ("i8x16.mul", "makeBinary(s, BinaryOp::MulVecI8x16)"), ("i8x16.min_s", "makeBinary(s, BinaryOp::MinSVecI8x16)"), ("i8x16.min_u", "makeBinary(s, BinaryOp::MinUVecI8x16)"), ("i8x16.max_s", "makeBinary(s, BinaryOp::MaxSVecI8x16)"), ("i8x16.max_u", "makeBinary(s, BinaryOp::MaxUVecI8x16)"), ("i8x16.avgr_u", "makeBinary(s, BinaryOp::AvgrUVecI8x16)"), ("i16x8.neg", "makeUnary(s, UnaryOp::NegVecI16x8)"), ("i16x8.any_true", "makeUnary(s, UnaryOp::AnyTrueVecI16x8)"), ("i16x8.all_true", "makeUnary(s, UnaryOp::AllTrueVecI16x8)"), ("i16x8.shl", "makeSIMDShift(s, SIMDShiftOp::ShlVecI16x8)"), ("i16x8.shr_s", "makeSIMDShift(s, SIMDShiftOp::ShrSVecI16x8)"), ("i16x8.shr_u", "makeSIMDShift(s, SIMDShiftOp::ShrUVecI16x8)"), ("i16x8.add", "makeBinary(s, BinaryOp::AddVecI16x8)"), ("i16x8.add_saturate_s", "makeBinary(s, BinaryOp::AddSatSVecI16x8)"), ("i16x8.add_saturate_u", "makeBinary(s, BinaryOp::AddSatUVecI16x8)"), ("i16x8.sub", "makeBinary(s, BinaryOp::SubVecI16x8)"), ("i16x8.sub_saturate_s", "makeBinary(s, BinaryOp::SubSatSVecI16x8)"), ("i16x8.sub_saturate_u", "makeBinary(s, BinaryOp::SubSatUVecI16x8)"), ("i16x8.mul", "makeBinary(s, BinaryOp::MulVecI16x8)"), ("i16x8.min_s", "makeBinary(s, BinaryOp::MinSVecI16x8)"), ("i16x8.min_u", "makeBinary(s, BinaryOp::MinUVecI16x8)"), ("i16x8.max_s", "makeBinary(s, BinaryOp::MaxSVecI16x8)"), ("i16x8.max_u", "makeBinary(s, BinaryOp::MaxUVecI16x8)"), ("i16x8.avgr_u", "makeBinary(s, BinaryOp::AvgrUVecI16x8)"), ("i32x4.neg", "makeUnary(s, UnaryOp::NegVecI32x4)"), ("i32x4.any_true", "makeUnary(s, UnaryOp::AnyTrueVecI32x4)"), ("i32x4.all_true", "makeUnary(s, UnaryOp::AllTrueVecI32x4)"), ("i32x4.shl", "makeSIMDShift(s, SIMDShiftOp::ShlVecI32x4)"), ("i32x4.shr_s", "makeSIMDShift(s, SIMDShiftOp::ShrSVecI32x4)"), ("i32x4.shr_u", "makeSIMDShift(s, SIMDShiftOp::ShrUVecI32x4)"), ("i32x4.add", "makeBinary(s, BinaryOp::AddVecI32x4)"), ("i32x4.sub", "makeBinary(s, BinaryOp::SubVecI32x4)"), ("i32x4.mul", "makeBinary(s, BinaryOp::MulVecI32x4)"), ("i32x4.min_s", "makeBinary(s, BinaryOp::MinSVecI32x4)"), ("i32x4.min_u", "makeBinary(s, BinaryOp::MinUVecI32x4)"), ("i32x4.max_s", "makeBinary(s, BinaryOp::MaxSVecI32x4)"), ("i32x4.max_u", "makeBinary(s, BinaryOp::MaxUVecI32x4)"), ("i32x4.dot_i16x8_s", "makeBinary(s, BinaryOp::DotSVecI16x8ToVecI32x4)"), ("i64x2.neg", "makeUnary(s, UnaryOp::NegVecI64x2)"), ("i64x2.any_true", "makeUnary(s, UnaryOp::AnyTrueVecI64x2)"), ("i64x2.all_true", "makeUnary(s, UnaryOp::AllTrueVecI64x2)"), ("i64x2.shl", "makeSIMDShift(s, SIMDShiftOp::ShlVecI64x2)"), ("i64x2.shr_s", "makeSIMDShift(s, SIMDShiftOp::ShrSVecI64x2)"), ("i64x2.shr_u", "makeSIMDShift(s, SIMDShiftOp::ShrUVecI64x2)"), ("i64x2.add", "makeBinary(s, BinaryOp::AddVecI64x2)"), ("i64x2.sub", "makeBinary(s, BinaryOp::SubVecI64x2)"), ("f32x4.abs", "makeUnary(s, UnaryOp::AbsVecF32x4)"), ("f32x4.neg", "makeUnary(s, UnaryOp::NegVecF32x4)"), ("f32x4.sqrt", "makeUnary(s, UnaryOp::SqrtVecF32x4)"), ("f32x4.qfma", "makeSIMDTernary(s, SIMDTernaryOp::QFMAF32x4)"), ("f32x4.qfms", "makeSIMDTernary(s, SIMDTernaryOp::QFMSF32x4)"), ("f32x4.add", "makeBinary(s, BinaryOp::AddVecF32x4)"), ("f32x4.sub", "makeBinary(s, BinaryOp::SubVecF32x4)"), ("f32x4.mul", "makeBinary(s, BinaryOp::MulVecF32x4)"), ("f32x4.div", "makeBinary(s, BinaryOp::DivVecF32x4)"), ("f32x4.min", "makeBinary(s, BinaryOp::MinVecF32x4)"), ("f32x4.max", "makeBinary(s, BinaryOp::MaxVecF32x4)"), ("f64x2.abs", "makeUnary(s, UnaryOp::AbsVecF64x2)"), ("f64x2.neg", "makeUnary(s, UnaryOp::NegVecF64x2)"), ("f64x2.sqrt", "makeUnary(s, UnaryOp::SqrtVecF64x2)"), ("f64x2.qfma", "makeSIMDTernary(s, SIMDTernaryOp::QFMAF64x2)"), ("f64x2.qfms", "makeSIMDTernary(s, SIMDTernaryOp::QFMSF64x2)"), ("f64x2.add", "makeBinary(s, BinaryOp::AddVecF64x2)"), ("f64x2.sub", "makeBinary(s, BinaryOp::SubVecF64x2)"), ("f64x2.mul", "makeBinary(s, BinaryOp::MulVecF64x2)"), ("f64x2.div", "makeBinary(s, BinaryOp::DivVecF64x2)"), ("f64x2.min", "makeBinary(s, BinaryOp::MinVecF64x2)"), ("f64x2.max", "makeBinary(s, BinaryOp::MaxVecF64x2)"), ("i32x4.trunc_sat_f32x4_s", "makeUnary(s, UnaryOp::TruncSatSVecF32x4ToVecI32x4)"), ("i32x4.trunc_sat_f32x4_u", "makeUnary(s, UnaryOp::TruncSatUVecF32x4ToVecI32x4)"), ("i64x2.trunc_sat_f64x2_s", "makeUnary(s, UnaryOp::TruncSatSVecF64x2ToVecI64x2)"), ("i64x2.trunc_sat_f64x2_u", "makeUnary(s, UnaryOp::TruncSatUVecF64x2ToVecI64x2)"), ("f32x4.convert_i32x4_s", "makeUnary(s, UnaryOp::ConvertSVecI32x4ToVecF32x4)"), ("f32x4.convert_i32x4_u", "makeUnary(s, UnaryOp::ConvertUVecI32x4ToVecF32x4)"), ("f64x2.convert_i64x2_s", "makeUnary(s, UnaryOp::ConvertSVecI64x2ToVecF64x2)"), ("f64x2.convert_i64x2_u", "makeUnary(s, UnaryOp::ConvertUVecI64x2ToVecF64x2)"), ("v8x16.load_splat", "makeSIMDLoad(s, SIMDLoadOp::LoadSplatVec8x16)"), ("v16x8.load_splat", "makeSIMDLoad(s, SIMDLoadOp::LoadSplatVec16x8)"), ("v32x4.load_splat", "makeSIMDLoad(s, SIMDLoadOp::LoadSplatVec32x4)"), ("v64x2.load_splat", "makeSIMDLoad(s, SIMDLoadOp::LoadSplatVec64x2)"), ("i16x8.load8x8_s", "makeSIMDLoad(s, SIMDLoadOp::LoadExtSVec8x8ToVecI16x8)"), ("i16x8.load8x8_u", "makeSIMDLoad(s, SIMDLoadOp::LoadExtUVec8x8ToVecI16x8)"), ("i32x4.load16x4_s", "makeSIMDLoad(s, SIMDLoadOp::LoadExtSVec16x4ToVecI32x4)"), ("i32x4.load16x4_u", "makeSIMDLoad(s, SIMDLoadOp::LoadExtUVec16x4ToVecI32x4)"), ("i64x2.load32x2_s", "makeSIMDLoad(s, SIMDLoadOp::LoadExtSVec32x2ToVecI64x2)"), ("i64x2.load32x2_u", "makeSIMDLoad(s, SIMDLoadOp::LoadExtUVec32x2ToVecI64x2)"), ("i8x16.narrow_i16x8_s", "makeBinary(s, BinaryOp::NarrowSVecI16x8ToVecI8x16)"), ("i8x16.narrow_i16x8_u", "makeBinary(s, BinaryOp::NarrowUVecI16x8ToVecI8x16)"), ("i16x8.narrow_i32x4_s", "makeBinary(s, BinaryOp::NarrowSVecI32x4ToVecI16x8)"), ("i16x8.narrow_i32x4_u", "makeBinary(s, BinaryOp::NarrowUVecI32x4ToVecI16x8)"), ("i16x8.widen_low_i8x16_s", "makeUnary(s, UnaryOp::WidenLowSVecI8x16ToVecI16x8)"), ("i16x8.widen_high_i8x16_s", "makeUnary(s, UnaryOp::WidenHighSVecI8x16ToVecI16x8)"), ("i16x8.widen_low_i8x16_u", "makeUnary(s, UnaryOp::WidenLowUVecI8x16ToVecI16x8)"), ("i16x8.widen_high_i8x16_u", "makeUnary(s, UnaryOp::WidenHighUVecI8x16ToVecI16x8)"), ("i32x4.widen_low_i16x8_s", "makeUnary(s, UnaryOp::WidenLowSVecI16x8ToVecI32x4)"), ("i32x4.widen_high_i16x8_s", "makeUnary(s, UnaryOp::WidenHighSVecI16x8ToVecI32x4)"), ("i32x4.widen_low_i16x8_u", "makeUnary(s, UnaryOp::WidenLowUVecI16x8ToVecI32x4)"), ("i32x4.widen_high_i16x8_u", "makeUnary(s, UnaryOp::WidenHighUVecI16x8ToVecI32x4)"), ("v8x16.swizzle", "makeBinary(s, BinaryOp::SwizzleVec8x16)"), # reference types instructions # TODO Add table instructions ("ref.null", "makeRefNull(s)"), ("ref.is_null", "makeRefIsNull(s)"), ("ref.func", "makeRefFunc(s)"), # exception handling instructions ("try", "makeTry(s)"), ("throw", "makeThrow(s)"), ("rethrow", "makeRethrow(s)"), ("br_on_exn", "makeBrOnExn(s)") ] class CodePrinter: indents = 0 def __enter__(self): CodePrinter.indents += 1 def __exit__(self, *args): CodePrinter.indents -= 1 def indent(self): # call in a 'with' statement return self def print_line(self, line): print(" " * CodePrinter.indents + line) class Node: def __init__(self, expr=None, children=None, inst=None): # the expression to return if this is the string has ended self.expr = expr # map unique strings to children nodes self.children = children if children else {} # full instruction leading to this node self.inst = inst def _common_prefix(a, b): """Return the common prefix of two strings.""" prefix = [] while a and b and a[0] == b[0]: prefix.append(a[0]) a = a[1:] b = b[1:] return "".join(prefix) def do_insert(self, full_inst, inst, expr): if not inst: assert self.expr is None, "Repeated instruction " + full_inst self.expr = expr self.inst = full_inst return # find key with shared prefix prefix, key = "", None for k in self.children: prefix = Node._common_prefix(inst, k) if prefix: key = k break if key is None: # unique prefix, insert and stop self.children[inst] = Node(expr, inst=full_inst) return key_remainder = key[len(prefix):] if key_remainder: # split key and move everything after the prefix to a new node child = self.children.pop(key) self.children[prefix] = Node(children={key_remainder: child}) # update key for recursive insert key = prefix # chop off prefix and recurse self.children[key].do_insert(full_inst, inst[len(key):], expr) def insert(self, inst, expr): self.do_insert(inst, inst, expr) def instruction_parser(): """Build a trie out of all the instructions, then emit it as C++ code.""" trie = Node() inst_length = 0 for inst, expr in instructions: inst_length = max(inst_length, len(inst)) trie.insert(inst, expr) printer = CodePrinter() printer.print_line("char op[{}] = {{'\\0'}};".format(inst_length + 1)) printer.print_line("strncpy(op, s[0]->c_str(), {});".format(inst_length)) def print_leaf(expr, inst): printer.print_line("if (strcmp(op, \"{inst}\") == 0) {{ return {expr}; }}" .format(inst=inst, expr=expr)) printer.print_line("goto parse_error;") def emit(node, idx=0): assert node.children printer.print_line("switch (op[{}]) {{".format(idx)) with printer.indent(): if node.expr: printer.print_line("case '\\0':") with printer.indent(): print_leaf(node.expr, node.inst) children = sorted(node.children.items(), key=lambda pair: pair[0]) for prefix, child in children: if child.children: printer.print_line("case '{}': {{".format(prefix[0])) with printer.indent(): emit(child, idx + len(prefix)) printer.print_line("}") else: assert child.expr printer.print_line("case '{}':".format(prefix[0])) with printer.indent(): print_leaf(child.expr, child.inst) printer.print_line("default: goto parse_error;") printer.print_line("}") emit(trie) printer.print_line("parse_error:") with printer.indent(): printer.print_line("throw ParseException(std::string(op), s.line, s.col);") def print_header(): print("// DO NOT EDIT! This file generated by scripts/gen-s-parser.py\n") print("// clang-format off\n") def print_footer(): print("\n// clang-format on") def generate_with_guard(generator, guard): print("#ifdef {}".format(guard)) print("#undef {}".format(guard)) generator() print("#endif // {}".format(guard)) def main(): if sys.version_info.major != 3: import datetime print("It's " + str(datetime.datetime.now().year) + "! Use Python 3!") sys.exit(1) print_header() generate_with_guard(instruction_parser, "INSTRUCTION_PARSER") print_footer() if __name__ == "__main__": main() binaryen-version_91/scripts/process_optimize_instructions.py000077500000000000000000000006121362402614000251440ustar00rootroot00000000000000#!/usr/bin/python import os root = os.path.dirname(os.path.dirname(__file__)) infile = os.path.join(root, 'src', 'passes', 'OptimizeInstructions.wast') outfile = os.path.join(root, 'src', 'passes', 'OptimizeInstructions.wast.processed') out = open(outfile, 'w') for line in open(infile): out.write('"' + line.strip().replace('"', '\\"') + '\\n"\n') out.close() binaryen-version_91/scripts/spidermonkify.py000066400000000000000000000027421362402614000216100ustar00rootroot00000000000000#!/usr/bin/env python3 # # 2016 WebAssembly Community Group participants # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """A bunch of hackish fixups for testing of SpiderMonkey support. We should get rid of these ASAP. This is meant to be run using BINARYEN_SCRIPTS in emcc, and not standalone. """ import subprocess import sys import emscripten js_target = sys.argv[1] wast_target = sys.argv[2] wasm_target = wast_target[:-5] + '.wasm' # convert to binary using spidermonkey ''' using something like mozjs -e 'os.file.writeTypedArrayToFile("moz.wasm", new Uint8Array(wasmTextToBinary(os.file.readFile("a.out.wast"))))' investigate with >>> map(chr, map(ord, open('moz.wasm').read())) or python -c "print str(map(chr,map(ord, open('a.out.wasm').read()))).replace(',', '\n')" ''' subprocess.check_call( emscripten.shared.SPIDERMONKEY_ENGINE + ['-e', 'os.file.writeTypedArrayToFile("' + wasm_target + '", new Uint8Array(wasmTextToBinary(os.file.readFile("' + wast_target + '"))))']) binaryen-version_91/scripts/storage.py000077500000000000000000000036531362402614000203760ustar00rootroot00000000000000#!/usr/bin/env python3 # # Copyright 2016 WebAssembly Community Group participants # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from __future__ import print_function import glob import json import os import urllib2 STORAGE_BASE = 'https://storage.googleapis.com/wasm-llvm/builds/git/' def download_revision(force_latest): name = 'latest' if force_latest else 'lkgr' downloaded = urllib2.urlopen(STORAGE_BASE + name).read().strip() # TODO: for now try opening as JSON, if that doesn't work then the content is # just a hash. The waterfall is in the process of migrating to JSON. info = None try: info = json.loads(downloaded) except ValueError: pass return info['build'] if type(info) == dict else downloaded def download_tar(tar_pattern, directory, revision): tar_path = os.path.join(directory, tar_pattern) revision_tar_path = tar_path % revision if os.path.isfile(revision_tar_path): print('Already have `%s`' % revision_tar_path) else: print('Downloading `%s`' % revision_tar_path) with open(revision_tar_path, 'w+') as f: f.write(urllib2.urlopen(STORAGE_BASE + tar_pattern % revision).read()) # Remove any previous tarfiles. for older_tar in glob.glob(tar_path % '*'): if older_tar != revision_tar_path: print('Removing older tar file `%s`' % older_tar) os.remove(older_tar) return revision_tar_path binaryen-version_91/scripts/strip_local_names.py000066400000000000000000000005221362402614000224150ustar00rootroot00000000000000 """Removes local names. When you don't care about local names but do want to diff for structural changes, this can help. """ import sys for line in open(sys.argv[1]).readlines(): if '(local.tee ' in line or '(local.set ' in line or '(local.get ' in line: print(line[:line.find('$')]) else: print(line.rstrip()) binaryen-version_91/scripts/test/000077500000000000000000000000001362402614000173255ustar00rootroot00000000000000binaryen-version_91/scripts/test/__init__.py000066400000000000000000000012541362402614000214400ustar00rootroot00000000000000# Copyright 2015 WebAssembly Community Group participants # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Empty __init__.py file: Python treats the directory as containing a package. binaryen-version_91/scripts/test/asm2wasm.py000077500000000000000000000163151362402614000214420ustar00rootroot00000000000000#!/usr/bin/env python3 # # Copyright 2017 WebAssembly Community Group participants # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import os import subprocess from scripts.test import shared from scripts.test import support def test_asm2wasm(): print('[ checking asm2wasm testcases... ]\n') for asm in shared.get_tests(shared.options.binaryen_test, ['.asm.js']): basename = os.path.basename(asm) for precise in [0, 1, 2]: for opts in [1, 0]: cmd = shared.ASM2WASM + [asm] if 'threads' in asm: cmd += ['--enable-threads'] wasm = asm.replace('.asm.js', '.fromasm') if not precise: cmd += ['--trap-mode=allow', '--ignore-implicit-traps'] wasm += '.imprecise' elif precise == 2: cmd += ['--trap-mode=clamp'] wasm += '.clamp' if not opts: wasm += '.no-opts' if precise: cmd += ['-O0'] # test that -O0 does nothing else: cmd += ['-O'] if 'debugInfo' in basename: cmd += ['-g'] if 'noffi' in basename: cmd += ['--no-legalize-javascript-ffi'] if precise and opts: # test mem init importing open('a.mem', 'w').write(basename) cmd += ['--mem-init=a.mem'] if basename[0] == 'e': cmd += ['--mem-base=1024'] if '4GB' in basename: cmd += ['--mem-max=4294967296'] if 'i64' in basename or 'wasm-only' in basename or 'noffi' in basename: cmd += ['--wasm-only'] print('..', basename, os.path.basename(wasm)) def do_asm2wasm_test(): actual = support.run_command(cmd) # verify output if not os.path.exists(wasm): shared.fail_with_error('output .wast file %s does not exist' % wasm) shared.fail_if_not_identical_to_file(actual, wasm) shared.binary_format_check(wasm, verify_final_result=False) # test both normally and with pass debug (so each inter-pass state # is validated) old_pass_debug = os.environ.get('BINARYEN_PASS_DEBUG') try: os.environ['BINARYEN_PASS_DEBUG'] = '1' print("With BINARYEN_PASS_DEBUG=1:") do_asm2wasm_test() del os.environ['BINARYEN_PASS_DEBUG'] print("With BINARYEN_PASS_DEBUG disabled:") do_asm2wasm_test() finally: if old_pass_debug is not None: os.environ['BINARYEN_PASS_DEBUG'] = old_pass_debug else: if 'BINARYEN_PASS_DEBUG' in os.environ: del os.environ['BINARYEN_PASS_DEBUG'] # verify in wasm if shared.options.interpreter: # remove imports, spec interpreter doesn't know what to do with them subprocess.check_call(shared.WASM_OPT + ['--remove-imports', wasm], stdout=open('ztemp.wast', 'w'), stderr=subprocess.PIPE) proc = subprocess.Popen([shared.options.interpreter, 'ztemp.wast'], stderr=subprocess.PIPE) out, err = proc.communicate() if proc.returncode != 0: try: # to parse the error reported = err.split(':')[1] start, end = reported.split('-') start_line, start_col = map(int, start.split('.')) lines = open('ztemp.wast').read().split('\n') print() print('=' * 80) print(lines[start_line - 1]) print((' ' * (start_col - 1)) + '^') print((' ' * (start_col - 2)) + '/_\\') print('=' * 80) print(err) except Exception: # failed to pretty-print shared.fail_with_error('wasm interpreter error: ' + err) shared.fail_with_error('wasm interpreter error') # verify debug info if 'debugInfo' in asm: jsmap = 'a.wasm.map' cmd += ['--source-map', jsmap, '--source-map-url', 'http://example.org/' + jsmap, '-o', 'a.wasm'] support.run_command(cmd) if not os.path.isfile(jsmap): shared.fail_with_error('Debug info map not created: %s' % jsmap) with open(jsmap, 'rb') as actual: shared.fail_if_not_identical_to_file(actual.read(), wasm + '.map') with open('a.wasm', 'rb') as binary: url_section_name = bytes([16]) + bytes('sourceMappingURL', 'utf-8') url = 'http://example.org/' + jsmap assert len(url) < 256, 'name too long' url_section_contents = bytes([len(url)]) + bytes(url, 'utf-8') print(url_section_name) binary_contents = binary.read() if url_section_name not in binary_contents: shared.fail_with_error('source map url section not found in binary') url_section_index = binary_contents.index(url_section_name) if url_section_contents not in binary_contents[url_section_index:]: shared.fail_with_error('source map url not found in url section') def test_asm2wasm_binary(): print('\n[ checking asm2wasm binary reading/writing... ]\n') asmjs = os.path.join(shared.options.binaryen_test, 'hello_world.asm.js') shared.delete_from_orbit('a.wasm') shared.delete_from_orbit('b.wast') support.run_command(shared.ASM2WASM + [asmjs, '-o', 'a.wasm']) assert open('a.wasm', 'rb').read()[0] == 0, 'we emit binary by default' support.run_command(shared.ASM2WASM + [asmjs, '-o', 'b.wast', '-S']) assert open('b.wast', 'rb').read()[0] != 0, 'we emit text with -S' if __name__ == '__main__': test_asm2wasm() test_asm2wasm_binary() binaryen-version_91/scripts/test/binaryenjs.py000077500000000000000000000057721362402614000220610ustar00rootroot00000000000000#!/usr/bin/env python3 # # Copyright 2016 WebAssembly Community Group participants # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import os import subprocess import sys from . import shared from . import support def do_test_binaryen_js_with(which): if not (shared.MOZJS or shared.NODEJS): print('no vm to run binaryen.js tests') return node_has_wasm = shared.NODEJS and support.node_has_webassembly(shared.NODEJS) if not os.path.exists(which): print('no ' + which + ' build to test') return print('\n[ checking binaryen.js testcases (' + which + ')... ]\n') for s in sorted(os.listdir(os.path.join(shared.options.binaryen_test, 'binaryen.js'))): if not s.endswith('.js'): continue print(s) f = open('a.js', 'w') # avoid stdout/stderr ordering issues in some js shells - use just stdout f.write(''' console.warn = console.error = console.log; ''') binaryen_js = open(which).read() f.write(binaryen_js) test_path = os.path.join(shared.options.binaryen_test, 'binaryen.js', s) test_src = open(test_path).read() f.write(support.js_test_wrap().replace('%TEST%', test_src)) f.close() def test(engine): cmd = [engine, 'a.js'] if 'fatal' not in s: out = support.run_command(cmd, stderr=subprocess.STDOUT) else: # expect an error - the specific error code will depend on the vm out = support.run_command(cmd, stderr=subprocess.STDOUT, expected_status=None) expected = open(os.path.join(shared.options.binaryen_test, 'binaryen.js', s + '.txt')).read() if expected not in out: shared.fail(out, expected) # run in all possible shells if shared.MOZJS: test(shared.MOZJS) if shared.NODEJS: if node_has_wasm or 'WebAssembly.' not in test_src: test(shared.NODEJS) else: print('Skipping ' + test_path + ' because WebAssembly might not be supported') def test_binaryen_js(): do_test_binaryen_js_with(shared.BINARYEN_JS) def test_binaryen_wasm(): do_test_binaryen_js_with(shared.BINARYEN_WASM) def test_binaryen_js_and_wasm(): test_binaryen_js() test_binaryen_wasm() if __name__ == "__main__": if sys.argv[1] == "js": test_binaryen_js() elif sys.argv[1] == "wasm": test_binaryen_wasm() else: test_binaryen_js_and_wasm() binaryen-version_91/scripts/test/env.js000066400000000000000000000003321362402614000204510ustar00rootroot00000000000000 export const FUNCTION_TABLE = []; var tempRet0 = 0; export function setTempRet0(x) { tempRet0 = x; } export function getTempRet0() { return tempRet0; } export const memoryBase = 0; export const tableBase = 0; binaryen-version_91/scripts/test/generate_lld_tests.py000077500000000000000000000064441362402614000235610ustar00rootroot00000000000000#!/usr/bin/env python3 # # Copyright 2017 WebAssembly Community Group participants # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from __future__ import print_function import os import sys import shared import support def files_with_extensions(path, extensions): for file in sorted(os.listdir(path)): ext = os.path.splitext(file)[1] if ext in extensions: yield file, ext def generate_wat_files(llvm_bin, emscripten_root): print('\n[ building wat files from C sources... ]\n') lld_path = os.path.join(shared.options.binaryen_test, 'lld') for src_file, ext in files_with_extensions(lld_path, ['.c', '.cpp']): print('..', src_file) obj_file = src_file.replace(ext, '.o') src_path = os.path.join(lld_path, src_file) obj_path = os.path.join(lld_path, obj_file) wasm_file = src_file.replace(ext, '.wasm') wat_file = src_file.replace(ext, '.wat') obj_path = os.path.join(lld_path, obj_file) wasm_path = os.path.join(lld_path, wasm_file) wat_path = os.path.join(lld_path, wat_file) is_shared = 'shared' in src_file compile_cmd = [ os.path.join(llvm_bin, 'clang'), src_path, '-o', obj_path, '--target=wasm32-emscripten', '-mllvm', '-enable-emscripten-sjlj', '-c', '-nostdinc', '-Xclang', '-nobuiltininc', '-Xclang', '-nostdsysteminc', '-Xclang', '-I%s/system/include' % emscripten_root, '-O1', ] link_cmd = [ os.path.join(llvm_bin, 'wasm-ld'), '-flavor', 'wasm', '-z', '-stack-size=1048576', obj_path, '-o', wasm_path, '--allow-undefined', '--export', '__wasm_call_ctors', '--export', '__data_end', '--global-base=568', ] # We had a regression where this test only worked if debug names # were included. if 'longjmp' in src_file: link_cmd.append('--strip-debug') if is_shared: compile_cmd.append('-fPIC') compile_cmd.append('-fvisibility=default') link_cmd.append('-shared') else: link_cmd.append('--entry=main') try: support.run_command(compile_cmd) support.run_command(link_cmd) support.run_command(shared.WASM_DIS + [wasm_path, '-o', wat_path]) finally: # Don't need the .o or .wasm files, don't leave them around shared.delete_from_orbit(obj_path) shared.delete_from_orbit(wasm_path) if __name__ == '__main__': if len(shared.options.positional_args) != 2: print('Usage: generate_lld_tests.py [llvm/bin/dir] [path/to/emscripten]') sys.exit(1) generate_wat_files(*shared.options.positional_args) binaryen-version_91/scripts/test/lld.py000077500000000000000000000066431362402614000204660ustar00rootroot00000000000000#!/usr/bin/env python3 # Copyright 2017 WebAssembly Community Group participants # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import os from . import shared from . import support def args_for_finalize(filename): ret = ['--global-base=568'] if 'safe_stack' in filename: ret += ['--check-stack-overflow'] if 'shared' in filename: ret += ['--side-module'] if 'standalone-wasm' in filename: ret += ['--standalone-wasm'] return ret def test_wasm_emscripten_finalize(): print('\n[ checking wasm-emscripten-finalize testcases... ]\n') for wat_path in shared.get_tests(shared.get_test_dir('lld'), ['.wat']): print('..', wat_path) is_passive = '.passive.' in wat_path mem_file = wat_path + '.mem' extension_arg_map = { '.out': [], } if not is_passive: extension_arg_map.update({ '.mem.out': ['--separate-data-segments', mem_file], }) for ext, ext_args in extension_arg_map.items(): expected_file = wat_path + ext if ext != '.out' and not os.path.exists(expected_file): continue cmd = shared.WASM_EMSCRIPTEN_FINALIZE + [wat_path, '-S'] + \ ext_args cmd += args_for_finalize(os.path.basename(wat_path)) actual = support.run_command(cmd) if not os.path.exists(expected_file): print(actual) shared.fail_with_error('output ' + expected_file + ' does not exist') shared.fail_if_not_identical_to_file(actual, expected_file) if ext == '.mem.out': with open(mem_file) as mf: mem = mf.read() shared.fail_if_not_identical_to_file(mem, wat_path + '.mem.mem') os.remove(mem_file) def update_lld_tests(): print('\n[ updating wasm-emscripten-finalize testcases... ]\n') for wat_path in shared.get_tests(shared.get_test_dir('lld'), ['.wat']): print('..', wat_path) is_passive = '.passive.' in wat_path mem_file = wat_path + '.mem' extension_arg_map = { '.out': [], } if not is_passive: extension_arg_map.update({ '.mem.out': ['--separate-data-segments', mem_file + '.mem'], }) for ext, ext_args in extension_arg_map.items(): out_path = wat_path + ext if ext != '.out' and not os.path.exists(out_path): continue cmd = shared.WASM_EMSCRIPTEN_FINALIZE + [wat_path, '-S'] + \ ext_args cmd += args_for_finalize(os.path.basename(wat_path)) actual = support.run_command(cmd) with open(out_path, 'w') as o: o.write(actual) if __name__ == '__main__': test_wasm_emscripten_finalize() binaryen-version_91/scripts/test/mod.ule.js000066400000000000000000000001011362402614000212160ustar00rootroot00000000000000 export function ba_se() { console.log('"mod.ule"."ba.se"'); } binaryen-version_91/scripts/test/node-esm-loader.mjs000066400000000000000000000022771362402614000230230ustar00rootroot00000000000000// originally lifted from https://nodejs.org/api/esm.html import path from 'path'; import process from 'process'; const baseURL = new URL('file://'); const binaryen_root = path.dirname(path.dirname(process.cwd())); baseURL.pathname = `${binaryen_root}/`; const specialTestSuiteModules = { 'spectest': { url: new URL('scripts/test/spectest.js', baseURL).href, format: 'module' }, 'env': { url: new URL('scripts/test/env.js', baseURL).href, format: 'module' }, 'mod.ule': { url: new URL('scripts/test/mod.ule.js', baseURL).href, format: 'module' } }; export async function resolve(specifier, context, defaultResolve) { const specialModule = specialTestSuiteModules[specifier]; if (specialModule) { return specialModule; } return defaultResolve(specifier, context, defaultResolve); } export async function getFormat(url, context, defaultGetFormat) { const specifiers = Object.keys(specialTestSuiteModules); for (let i = 0, k = specifiers.length; i < k; ++i) { const specialModule = specialTestSuiteModules[specifiers[i]]; if (specialModule.url == url) { return specialModule; } } return defaultGetFormat(url, context, defaultGetFormat); } binaryen-version_91/scripts/test/shared.py000066400000000000000000000461771362402614000211640ustar00rootroot00000000000000# Copyright 2015 WebAssembly Community Group participants # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from __future__ import print_function import argparse import difflib import glob import os import shutil import subprocess import sys def parse_args(args): usage_str = ("usage: 'python check.py [options]'\n\n" "Runs the Binaryen test suite.") parser = argparse.ArgumentParser(description=usage_str) parser.add_argument( '--torture', dest='torture', action='store_true', default=True, help='Chooses whether to run the torture testcases. Default: true.') parser.add_argument( '--no-torture', dest='torture', action='store_false', help='Disables running the torture testcases.') parser.add_argument( '--abort-on-first-failure', dest='abort_on_first_failure', action='store_true', default=True, help=('Specifies whether to halt test suite execution on first test error.' ' Default: true.')) parser.add_argument( '--no-abort-on-first-failure', dest='abort_on_first_failure', action='store_false', help=('If set, the whole test suite will run to completion independent of' ' earlier errors.')) parser.add_argument( '--interpreter', dest='interpreter', default='', help='Specifies the wasm interpreter executable to run tests on.') parser.add_argument( '--binaryen-bin', dest='binaryen_bin', default='', help=('Specifies a path to where the built Binaryen executables reside at.' ' Default: bin/ of current directory (i.e. assume an in-tree build).' ' If not specified, the environment variable BINARYEN_ROOT= can also' ' be used to adjust this.')) parser.add_argument( '--binaryen-root', dest='binaryen_root', default='', help=('Specifies a path to the root of the Binaryen repository tree.' ' Default: the directory where this file check.py resides.')) parser.add_argument( '--out-dir', dest='out_dir', default='', help=('Specifies a path to the output directory for temp files, which ' 'is also where the test runner changes directory into.' ' Default:. out/test under the binaryen root.')) parser.add_argument( '--valgrind', dest='valgrind', default='', help=('Specifies a path to Valgrind tool, which will be used to validate' ' execution if specified. (Pass --valgrind=valgrind to search in' ' PATH)')) parser.add_argument( '--valgrind-full-leak-check', dest='valgrind_full_leak_check', action='store_true', default=False, help=('If specified, all unfreed (but still referenced) pointers at the' ' end of execution are considered memory leaks. Default: disabled.')) parser.add_argument( '--spec-test', action='append', nargs='*', default=[], dest='spec_tests', help='Names specific spec tests to run.') parser.add_argument( 'positional_args', metavar='TEST_SUITE', nargs='*', help=('Names specific test suites to run. Use --list-suites to see a ' 'list of all test suites')) parser.add_argument( '--list-suites', action='store_true', help='List the test suites that can be run.') return parser.parse_args(args) options = parse_args(sys.argv[1:]) requested = options.positional_args script_dir = os.path.dirname(os.path.abspath(__file__)) num_failures = 0 warnings = [] def warn(text): global warnings warnings.append(text) print('warning:', text, file=sys.stderr) # setup # Locate Binaryen build artifacts directory (bin/ by default) if not options.binaryen_bin: if os.environ.get('BINARYEN_ROOT'): if os.path.isdir(os.path.join(os.environ.get('BINARYEN_ROOT'), 'bin')): options.binaryen_bin = os.path.join( os.environ.get('BINARYEN_ROOT'), 'bin') else: options.binaryen_bin = os.environ.get('BINARYEN_ROOT') else: options.binaryen_bin = 'bin' options.binaryen_bin = os.path.normpath(os.path.abspath(options.binaryen_bin)) # ensure BINARYEN_ROOT is set up os.environ['BINARYEN_ROOT'] = os.path.dirname(options.binaryen_bin) wasm_dis_filenames = ['wasm-dis', 'wasm-dis.exe'] if not any(os.path.isfile(os.path.join(options.binaryen_bin, f)) for f in wasm_dis_filenames): warn('Binaryen not found (or has not been successfully built to bin/ ?') # Locate Binaryen source directory if not specified. if not options.binaryen_root: options.binaryen_root = os.path.dirname(os.path.dirname(script_dir)) options.binaryen_test = os.path.join(options.binaryen_root, 'test') if not options.out_dir: options.out_dir = os.path.join(options.binaryen_root, 'out', 'test') if not os.path.exists(options.out_dir): os.makedirs(options.out_dir) os.chdir(options.out_dir) # Finds the given executable 'program' in PATH. # Operates like the Unix tool 'which'. def which(program): def is_exe(fpath): return os.path.isfile(fpath) and os.access(fpath, os.X_OK) fpath, fname = os.path.split(program) if fpath: if is_exe(program): return program else: for path in os.environ["PATH"].split(os.pathsep): path = path.strip('"') exe_file = os.path.join(path, program) if is_exe(exe_file): return exe_file if '.' not in fname: if is_exe(exe_file + '.exe'): return exe_file + '.exe' if is_exe(exe_file + '.cmd'): return exe_file + '.cmd' if is_exe(exe_file + '.bat'): return exe_file + '.bat' WATERFALL_BUILD_DIR = os.path.join(options.binaryen_test, 'wasm-install') BIN_DIR = os.path.abspath(os.path.join(WATERFALL_BUILD_DIR, 'wasm-install', 'bin')) NATIVECC = (os.environ.get('CC') or which('mingw32-gcc') or which('gcc') or which('clang')) NATIVEXX = (os.environ.get('CXX') or which('mingw32-g++') or which('g++') or which('clang++')) NODEJS = os.getenv('NODE', which('nodejs') or which('node')) MOZJS = which('mozjs') or which('spidermonkey') V8 = which('v8') or which('d8') EMCC = which('emcc') BINARYEN_INSTALL_DIR = os.path.dirname(options.binaryen_bin) WASM_OPT = [os.path.join(options.binaryen_bin, 'wasm-opt')] WASM_AS = [os.path.join(options.binaryen_bin, 'wasm-as')] WASM_DIS = [os.path.join(options.binaryen_bin, 'wasm-dis')] ASM2WASM = [os.path.join(options.binaryen_bin, 'asm2wasm')] WASM2JS = [os.path.join(options.binaryen_bin, 'wasm2js')] WASM_CTOR_EVAL = [os.path.join(options.binaryen_bin, 'wasm-ctor-eval')] WASM_SHELL = [os.path.join(options.binaryen_bin, 'wasm-shell')] WASM_REDUCE = [os.path.join(options.binaryen_bin, 'wasm-reduce')] WASM_METADCE = [os.path.join(options.binaryen_bin, 'wasm-metadce')] WASM_EMSCRIPTEN_FINALIZE = [os.path.join(options.binaryen_bin, 'wasm-emscripten-finalize')] BINARYEN_JS = os.path.join(options.binaryen_bin, 'binaryen_js.js') BINARYEN_WASM = os.path.join(options.binaryen_bin, 'binaryen_wasm.js') def wrap_with_valgrind(cmd): # Exit code 97 is arbitrary, used to easily detect when an error occurs that # is detected by Valgrind. valgrind = [options.valgrind, '--quiet', '--error-exitcode=97'] if options.valgrind_full_leak_check: valgrind += ['--leak-check=full', '--show-leak-kinds=all'] return valgrind + cmd if options.valgrind: WASM_OPT = wrap_with_valgrind(WASM_OPT) WASM_AS = wrap_with_valgrind(WASM_AS) WASM_DIS = wrap_with_valgrind(WASM_DIS) ASM2WASM = wrap_with_valgrind(ASM2WASM) WASM_SHELL = wrap_with_valgrind(WASM_SHELL) def in_binaryen(*args): return os.path.join(options.binaryen_root, *args) os.environ['BINARYEN'] = in_binaryen() def get_platform(): return {'linux': 'linux', 'linux2': 'linux', 'darwin': 'mac', 'win32': 'windows', 'cygwin': 'windows'}[sys.platform] def has_shell_timeout(): return get_platform() != 'windows' and os.system('timeout 1s pwd') == 0 # Default options to pass to v8. These enable all features. V8_OPTS = [ '--experimental-wasm-eh', '--experimental-wasm-mv', '--experimental-wasm-sat-f2i-conversions', '--experimental-wasm-se', '--experimental-wasm-threads', '--experimental-wasm-simd', '--experimental-wasm-anyref', '--experimental-wasm-bulk-memory', '--experimental-wasm-return-call' ] has_vanilla_llvm = False # external tools try: if NODEJS is not None: subprocess.check_call([NODEJS, '--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) except (OSError, subprocess.CalledProcessError): NODEJS = None if NODEJS is None: warn('no node found (did not check proper js form)') try: if MOZJS is not None: subprocess.check_call([MOZJS, '--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) except (OSError, subprocess.CalledProcessError): MOZJS = None if MOZJS is None: warn('no mozjs found (did not check native wasm support nor asm.js' ' validation)') try: if EMCC is not None: subprocess.check_call([EMCC, '--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) except (OSError, subprocess.CalledProcessError): EMCC = None if EMCC is None: warn('no emcc found (did not check non-vanilla emscripten/binaryen' ' integration)') has_vanilla_emcc = False try: subprocess.check_call( [os.path.join(options.binaryen_test, 'emscripten', 'emcc'), '--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) has_vanilla_emcc = True except (OSError, subprocess.CalledProcessError): pass # utilities # removes a file if it exists, using any and all ways of doing so def delete_from_orbit(filename): try: os.unlink(filename) except OSError: pass if not os.path.exists(filename): return try: shutil.rmtree(filename, ignore_errors=True) except OSError: pass if not os.path.exists(filename): return try: import stat os.chmod(filename, os.stat(filename).st_mode | stat.S_IWRITE) def remove_readonly_and_try_again(func, path, exc_info): if not (os.stat(path).st_mode & stat.S_IWRITE): os.chmod(path, os.stat(path).st_mode | stat.S_IWRITE) func(path) else: raise shutil.rmtree(filename, onerror=remove_readonly_and_try_again) except OSError: pass # This is a workaround for https://bugs.python.org/issue9400 class Py2CalledProcessError(subprocess.CalledProcessError): def __init__(self, returncode, cmd, output=None, stderr=None): super(Exception, self).__init__(returncode, cmd, output, stderr) self.returncode = returncode self.cmd = cmd self.output = output self.stderr = stderr def run_process(cmd, check=True, input=None, capture_output=False, decode_output=True, *args, **kw): if input and type(input) == str: input = bytes(input, 'utf-8') if capture_output: kw['stdout'] = subprocess.PIPE kw['stderr'] = subprocess.PIPE ret = subprocess.run(cmd, check=check, input=input, *args, **kw) if decode_output and ret.stdout is not None: ret.stdout = ret.stdout.decode('utf-8') if ret.stderr is not None: ret.stderr = ret.stderr.decode('utf-8') return ret def fail_with_error(msg): global num_failures try: num_failures += 1 raise Exception(msg) except Exception as e: print(str(e)) if options.abort_on_first_failure: raise def fail(actual, expected, fromfile='expected'): diff_lines = difflib.unified_diff( expected.split('\n'), actual.split('\n'), fromfile=fromfile, tofile='actual') diff_str = ''.join([a.rstrip() + '\n' for a in diff_lines])[:] fail_with_error("incorrect output, diff:\n\n%s" % diff_str) def fail_if_not_identical(actual, expected, fromfile='expected'): if expected != actual: fail(actual, expected, fromfile=fromfile) def fail_if_not_contained(actual, expected): if expected not in actual: fail(actual, expected) def fail_if_not_identical_to_file(actual, expected_file): binary = expected_file.endswith(".wasm") or type(actual) == bytes with open(expected_file, 'rb' if binary else 'r') as f: fail_if_not_identical(actual, f.read(), fromfile=expected_file) def get_test_dir(name): """Returns the test directory located at BINARYEN_ROOT/test/[name].""" return os.path.join(options.binaryen_test, name) def get_tests(test_dir, extensions=[]): """Returns the list of test files in a given directory. 'extensions' is a list of file extensions. If 'extensions' is empty, returns all files. """ tests = [] if not extensions: tests += glob.glob(os.path.join(test_dir, '*')) for ext in extensions: tests += glob.glob(os.path.join(test_dir, '*' + ext)) return sorted(tests) if not options.interpreter: warn('no interpreter provided (did not test spec interpreter validation)') if not has_vanilla_emcc: warn('no functional emcc submodule found') if not options.spec_tests: options.spec_tests = get_tests(get_test_dir('spec'), ['.wast']) else: options.spec_tests = options.spec_tests[:] # 11/27/2019: We updated the spec test suite to upstream spec repo. For some # files that started failing after this update, we added the new files to this # blacklist and preserved old ones by renaming them to 'old_[FILENAME].wast' # not to lose coverage. When the cause of the error is fixed or the unsupported # construct gets support so the new test passes, we can delete the # corresponding 'old_[FILENAME].wast' file. When you fix the new file and # delete the old file, make sure you rename the corresponding .wast.log file in # expected-output/ if any. SPEC_TEST_BLACKLIST = [ # Stacky code / notation 'block.wast', 'call.wast', 'float_exprs.wast', 'globals.wast', 'loop.wast', 'nop.wast', 'select.wast', 'stack.wast', 'unwind.wast', # Binary module 'binary.wast', 'binary-leb128.wast', 'custom.wast', # Empty 'then' or 'else' in 'if' 'if.wast', 'local_set.wast', 'store.wast', # No module in a file 'token.wast', 'utf8-custom-section-id.wast', 'utf8-import-field.wast', 'utf8-import-module.wast', 'utf8-invalid-encoding.wast', # 'register' command 'imports.wast', 'linking.wast', # Misc. unsupported constructs 'call_indirect.wast', # Empty (param) and (result) 'const.wast', # Unparenthesized expression 'data.wast', # Various unsupported (data) notations 'elem.wast', # Unsupported 'offset' syntax in (elem) 'exports.wast', # Multiple inlined exports for a function 'func.wast', # Forward named type reference 'skip-stack-guard-page.wast', # Hexadecimal style (0x..) in memory offset # Untriaged: We don't know the cause of the error yet 'address.wast', # wasm2js 'assert_return' failure 'br_if.wast', # Validation error 'float_literals.wast', # 'assert_return' failure 'int_literals.wast', # 'assert_return' failure 'local_tee.wast', # Validation failure 'memory_grow.wast', # 'assert_return' failure 'start.wast', # Assertion failure 'type.wast', # 'assertion_invalid' failure 'unreachable.wast', # Validation failure 'unreached-invalid.wast' # 'assert_invalid' failure ] options.spec_tests = [t for t in options.spec_tests if os.path.basename(t) not in SPEC_TEST_BLACKLIST] # check utilities def validate_binary(wasm): if V8: cmd = [V8] + V8_OPTS + [in_binaryen('scripts', 'validation_shell.js'), '--', wasm] print(' ', ' '.join(cmd)) subprocess.check_call(cmd, stdout=subprocess.PIPE) else: print('(skipping v8 binary validation)') def binary_format_check(wast, verify_final_result=True, wasm_as_args=['-g'], binary_suffix='.fromBinary', original_wast=None): # checks we can convert the wast to binary and back print(' (binary format check)') cmd = WASM_AS + [wast, '-o', 'a.wasm', '-all'] + wasm_as_args print(' ', ' '.join(cmd)) if os.path.exists('a.wasm'): os.unlink('a.wasm') subprocess.check_call(cmd, stdout=subprocess.PIPE) assert os.path.exists('a.wasm') # make sure it is a valid wasm, using a real wasm VM if os.path.basename(original_wast or wast) not in [ 'atomics.wast', # https://bugs.chromium.org/p/v8/issues/detail?id=9425 'simd.wast', # https://bugs.chromium.org/p/v8/issues/detail?id=8460 ]: validate_binary('a.wasm') cmd = WASM_DIS + ['a.wasm', '-o', 'ab.wast'] print(' ', ' '.join(cmd)) if os.path.exists('ab.wast'): os.unlink('ab.wast') subprocess.check_call(cmd, stdout=subprocess.PIPE) assert os.path.exists('ab.wast') # make sure it is a valid wast cmd = WASM_OPT + ['ab.wast', '-all'] print(' ', ' '.join(cmd)) subprocess.check_call(cmd, stdout=subprocess.PIPE) if verify_final_result: actual = open('ab.wast').read() fail_if_not_identical_to_file(actual, wast + binary_suffix) return 'ab.wast' def minify_check(wast, verify_final_result=True): # checks we can parse minified output print(' (minify check)') cmd = WASM_OPT + [wast, '--print-minified', '-all'] print(' ', ' '.join(cmd)) subprocess.check_call(cmd, stdout=open('a.wast', 'w'), stderr=subprocess.PIPE) assert os.path.exists('a.wast') subprocess.check_call(WASM_OPT + ['a.wast', '--print-minified', '-all'], stdout=open('b.wast', 'w'), stderr=subprocess.PIPE) assert os.path.exists('b.wast') if verify_final_result: expected = open('a.wast').read() actual = open('b.wast').read() if actual != expected: fail(actual, expected) if os.path.exists('a.wast'): os.unlink('a.wast') if os.path.exists('b.wast'): os.unlink('b.wast') # run a check with BINARYEN_PASS_DEBUG set, to do full validation def with_pass_debug(check): old_pass_debug = os.environ.get('BINARYEN_PASS_DEBUG') try: os.environ['BINARYEN_PASS_DEBUG'] = '1' check() finally: if old_pass_debug is not None: os.environ['BINARYEN_PASS_DEBUG'] = old_pass_debug else: if 'BINARYEN_PASS_DEBUG' in os.environ: del os.environ['BINARYEN_PASS_DEBUG'] binaryen-version_91/scripts/test/spectest.js000066400000000000000000000007101362402614000215130ustar00rootroot00000000000000export function print() { console.log(); } export function print_i32(arg) { console.log(arg, ' : i32'); } export function print_f32(arg) { console.log(arg, ' : f32'); } export function print_f64(arg) { console.log(arg, ' : f64'); } export function print_i32_f32(arg0, arg1) { console.log(arg0, ' : i32'); console.log(arg1, ' : f32'); } export function print_f64_f64(arg0, arg1) { console.log(arg0, ' : f64'); console.log(arg1, ' : f64'); } binaryen-version_91/scripts/test/support.py000066400000000000000000000151641362402614000214220ustar00rootroot00000000000000# Copyright 2016 WebAssembly Community Group participants # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import filecmp import os import shutil import subprocess import sys import tempfile def _open_archive(tarfile, tmp_dir): with tempfile.TemporaryFile(mode='w+') as f: try: subprocess.check_call(['tar', '-xvf', tarfile], cwd=tmp_dir, stdout=f) except Exception: f.seek(0) sys.stderr.write(f.read()) raise return os.listdir(tmp_dir) def _files_same(dir1, dir2, basenames): diff = filecmp.cmpfiles(dir1, dir2, basenames) return 0 == len(diff[1] + diff[2]) def _dirs_same(dir1, dir2, basenames): for d in basenames: left = os.path.join(dir1, d) right = os.path.join(dir2, d) if not (os.path.isdir(left) and os.path.isdir(right)): return False diff = filecmp.dircmp(right, right) if 0 != len(diff.left_only + diff.right_only + diff.diff_files + diff.common_funny + diff.funny_files): return False return True def _move_files(dirfrom, dirto, basenames): for f in basenames: from_file = os.path.join(dirfrom, f) to_file = os.path.join(dirto, f) if os.path.isfile(to_file): os.path.remove(to_file) shutil.move(from_file, to_file) def _move_dirs(dirfrom, dirto, basenames): for d in basenames: from_dir = os.path.join(dirfrom, d) to_dir = os.path.join(dirto, d) if os.path.isdir(to_dir): shutil.rmtree(to_dir) shutil.move(from_dir, to_dir) def untar(tarfile, outdir): """Returns True if untar content differs from pre-existing outdir content.""" tmpdir = tempfile.mkdtemp() try: untared = _open_archive(tarfile, tmpdir) files = [f for f in untared if os.path.isfile(os.path.join(tmpdir, f))] dirs = [d for d in untared if os.path.isdir(os.path.join(tmpdir, d))] assert len(files) + len(dirs) == len(untared), 'Only files and directories' if _files_same(tmpdir, outdir, files) and _dirs_same(tmpdir, outdir, dirs): # Nothing new or different in the tarfile. return False # Some or all of the files / directories are new. _move_files(tmpdir, outdir, files) _move_dirs(tmpdir, outdir, dirs) return True finally: if os.path.isdir(tmpdir): shutil.rmtree(tmpdir) def split_wast(wastFile): # if it's a binary, leave it as is, we can't split it wast = None if not wastFile.endswith('.wasm'): try: wast = open(wastFile, 'r').read() except Exception: pass if not wast: return ((open(wastFile, 'rb').read(), []),) # .wast files can contain multiple modules, and assertions for each one. # this splits out a wast into [(module, assertions), ..] # we ignore module invalidity tests here. ret = [] def to_end(j): depth = 1 while depth > 0 and j < len(wast): if wast[j] == '"': while 1: j = wast.find('"', j + 1) if wast[j - 1] == '\\': continue break assert j > 0 elif wast[j] == '(': depth += 1 elif wast[j] == ')': depth -= 1 elif wast[j] == ';' and wast[j + 1] == ';': j = wast.find('\n', j) j += 1 return j i = 0 while i >= 0: start = wast.find('(', i) if start >= 0 and wast[start + 1] == ';': # block comment i = wast.find(';)', start + 2) assert i > 0, wast[start:] i += 2 continue skip = wast.find(';', i) if skip >= 0 and skip < start and skip + 1 < len(wast): if wast[skip + 1] == ';': i = wast.find('\n', i) + 1 continue if start < 0: break i = to_end(start + 1) chunk = wast[start:i] if chunk.startswith('(module'): ret += [(chunk, [])] elif chunk.startswith('(assert_invalid'): continue elif chunk.startswith(('(assert', '(invoke')): ret[-1][1].append(chunk) return ret # write a split wast from split_wast. the wast may be binary if the original # file was binary def write_wast(filename, wast, asserts=[]): if type(wast) == bytes: assert not asserts with open(filename, 'wb') as o: o.write(wast) else: with open(filename, 'w') as o: o.write(wast + '\n'.join(asserts)) def run_command(cmd, expected_status=0, stderr=None, expected_err=None, err_contains=False, err_ignore=None): if expected_err is not None: assert stderr == subprocess.PIPE or stderr is None,\ "Can't redirect stderr if using expected_err" stderr = subprocess.PIPE print('executing: ', ' '.join(cmd)) proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=stderr, universal_newlines=True) out, err = proc.communicate() code = proc.returncode if expected_status is not None and code != expected_status: raise Exception(('run_command failed (%s)' % code, out + str(err or ''))) if expected_err is not None: if err_ignore is not None: err = "\n".join([line for line in err.split('\n') if err_ignore not in line]) err_correct = expected_err in err if err_contains else expected_err == err if not err_correct: raise Exception(('run_command unexpected stderr', "expected '%s', actual '%s'" % (expected_err, err))) return out def node_has_webassembly(cmd): cmd = [cmd, '-e', 'process.stdout.write(typeof WebAssembly)'] return run_command(cmd) == 'object' def js_test_wrap(): # common wrapper code for JS tests, waiting for binaryen.js to become ready # and providing common utility used by all tests: return ''' binaryen.ready.then(function() { function assert(x) { if (!x) throw Error('Test assertion failed'); } %TEST% }); ''' binaryen-version_91/scripts/test/wasm2js.py000077500000000000000000000161301362402614000212710ustar00rootroot00000000000000#!/usr/bin/env python3 # # Copyright 2016 WebAssembly Community Group participants # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import os from scripts.test import shared from scripts.test import support tests = shared.get_tests(shared.options.binaryen_test) spec_tests = shared.options.spec_tests spec_tests = [t for t in spec_tests if '.fail' not in t] wasm2js_tests = shared.get_tests(shared.get_test_dir('wasm2js'), ['.wast']) assert_tests = ['wasm2js.wast.asserts'] # These tests exercise functionality not supported by wasm2js wasm2js_blacklist = ['empty_imported_table.wast'] def test_wasm2js_output(): for opt in (0, 1): for t in tests + spec_tests + wasm2js_tests: basename = os.path.basename(t) if basename in wasm2js_blacklist: continue asm = basename.replace('.wast', '.2asm.js') expected_file = os.path.join(shared.get_test_dir('wasm2js'), asm) if opt: expected_file += '.opt' if not os.path.exists(expected_file): continue print('..', os.path.basename(t)) all_out = [] for module, asserts in support.split_wast(t): support.write_wast('split.wast', module, asserts) # wasm2js does not yet support EH, and enabling it can reduce # optimization opportunities cmd = shared.WASM2JS + ['split.wast', '-all', '--disable-exception-handling'] if opt: cmd += ['-O'] if 'emscripten' in t: cmd += ['--emscripten'] out = support.run_command(cmd) all_out.append(out) if not shared.NODEJS and not shared.MOZJS: print('No JS interpreters. Skipping spec tests.') continue open('a.2asm.mjs', 'w').write(out) cmd += ['--allow-asserts'] out = support.run_command(cmd) # also verify it passes pass-debug verifications shared.with_pass_debug(lambda: support.run_command(cmd)) open('a.2asm.asserts.mjs', 'w').write(out) # verify asm.js is valid js, note that we're using --experimental-modules # to enable ESM syntax and we're also passing a custom loader to handle the # `spectest` and `env` modules in our tests. if shared.NODEJS: loader = os.path.join(shared.options.binaryen_root, 'scripts', 'test', 'node-esm-loader.mjs') node = [shared.NODEJS, '--experimental-modules', '--loader', loader] cmd = node[:] cmd.append('a.2asm.mjs') out = support.run_command(cmd) shared.fail_if_not_identical(out, '') cmd = node[:] cmd.append('a.2asm.asserts.mjs') out = support.run_command(cmd, expected_err='', err_ignore='ExperimentalWarning') shared.fail_if_not_identical(out, '') shared.fail_if_not_identical_to_file(''.join(all_out), expected_file) def test_asserts_output(): for wasm in assert_tests: print('..', wasm) asserts = os.path.basename(wasm).replace('.wast.asserts', '.asserts.js') traps = os.path.basename(wasm).replace('.wast.asserts', '.traps.js') asserts_expected_file = os.path.join(shared.options.binaryen_test, asserts) traps_expected_file = os.path.join(shared.options.binaryen_test, traps) wasm = os.path.join(shared.get_test_dir('wasm2js'), wasm) cmd = shared.WASM2JS + [wasm, '--allow-asserts', '-all', '--disable-exception-handling'] out = support.run_command(cmd) shared.fail_if_not_identical_to_file(out, asserts_expected_file) cmd += ['--pedantic'] out = support.run_command(cmd) shared.fail_if_not_identical_to_file(out, traps_expected_file) def test_wasm2js(): print('\n[ checking wasm2js testcases... ]\n') test_wasm2js_output() test_asserts_output() def update_wasm2js_tests(): print('\n[ checking wasm2js ]\n') for opt in (0, 1): for wasm in tests + spec_tests + wasm2js_tests: if not wasm.endswith('.wast'): continue if os.path.basename(wasm) in wasm2js_blacklist: continue asm = os.path.basename(wasm).replace('.wast', '.2asm.js') expected_file = os.path.join(shared.get_test_dir('wasm2js'), asm) if opt: expected_file += '.opt' # we run wasm2js on tests and spec tests only if the output # exists - only some work so far. the tests in extra are in # the test/wasm2js dir and so are specific to wasm2js, and # we run all of those. if wasm not in wasm2js_tests and not os.path.exists(expected_file): continue print('..', wasm) t = os.path.join(shared.options.binaryen_test, wasm) all_out = [] for module, asserts in support.split_wast(t): support.write_wast('split.wast', module, asserts) # wasm2js does not yet support EH, and enable it can reduce # optimization opportunities cmd = shared.WASM2JS + ['split.wast', '-all', '--disable-exception-handling'] if opt: cmd += ['-O'] if 'emscripten' in wasm: cmd += ['--emscripten'] out = support.run_command(cmd) all_out.append(out) with open(expected_file, 'w') as o: o.write(''.join(all_out)) for wasm in assert_tests: print('..', wasm) asserts = os.path.basename(wasm).replace('.wast.asserts', '.asserts.js') traps = os.path.basename(wasm).replace('.wast.asserts', '.traps.js') asserts_expected_file = os.path.join(shared.options.binaryen_test, asserts) traps_expected_file = os.path.join(shared.options.binaryen_test, traps) cmd = shared.WASM2JS + [os.path.join(shared.get_test_dir('wasm2js'), wasm), '--allow-asserts', '-all', '--disable-exception-handling'] out = support.run_command(cmd) with open(asserts_expected_file, 'w') as o: o.write(out) cmd += ['--pedantic'] out = support.run_command(cmd) with open(traps_expected_file, 'w') as o: o.write(out) if __name__ == "__main__": test_wasm2js() binaryen-version_91/scripts/validation_shell.js000066400000000000000000000013771362402614000222350ustar00rootroot00000000000000// Test a file is valid, by just loading it. // Shell integration. if (typeof console === 'undefined') { console = { log: print }; } var binary; if (typeof process === 'object' && typeof require === 'function' /* node.js detection */) { var args = process.argv.slice(2); binary = require('fs').readFileSync(args[0]); if (!binary.buffer) binary = new Uint8Array(binary); } else { var args; if (typeof scriptArgs != 'undefined') { args = scriptArgs; } else if (typeof arguments != 'undefined') { args = arguments; } if (typeof readbuffer === 'function') { binary = new Uint8Array(readbuffer(args[0])); } else { binary = read(args[0], 'binary'); } } // Test the wasm for validity by compiling it. new WebAssembly.Module(binary); binaryen-version_91/scripts/wasm2js.js000066400000000000000000000141211362402614000202710ustar00rootroot00000000000000// wasm2js.js - enough of a polyfill for the WebAssembly object so that we can load // wasm2js code that way. Similar to the same file in emscripten, but tailored for // fuzzing purposes here. var WebAssembly = { Memory: function(opts) { return { buffer: new ArrayBuffer(opts['initial'] * 64 * 1024), grow: function(amount) { var oldBuffer = this.buffer; var ret = __growWasmMemory(amount); assert(this.buffer !== oldBuffer); // the call should have updated us return ret; } }; }, Table: function(opts) { var ret = new Array(opts['initial']); ret.grow = function(by) { ret.push(null); }; ret.set = function(i, func) { ret[i] = func; }; ret.get = function(i) { return ret[i]; }; return ret; }, Module: function(binary) { // TODO: use the binary and info somehow - right now the wasm2js output is embedded in // the main JS return {}; }, Instance: function(module, info) { // TODO: use the module and info somehow - right now the wasm2js output is embedded in // the main JS var decodeBase64 = typeof atob === 'function' ? atob : function (input) { var keyStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; var output = ''; var chr1, chr2, chr3; var enc1, enc2, enc3, enc4; var i = 0; // remove all characters that are not A-Z, a-z, 0-9, +, /, or = input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ''); do { enc1 = keyStr.indexOf(input.charAt(i++)); enc2 = keyStr.indexOf(input.charAt(i++)); enc3 = keyStr.indexOf(input.charAt(i++)); enc4 = keyStr.indexOf(input.charAt(i++)); chr1 = (enc1 << 2) | (enc2 >> 4); chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); chr3 = ((enc3 & 3) << 6) | enc4; output = output + String.fromCharCode(chr1); if (enc3 !== 64) { output = output + String.fromCharCode(chr2); } if (enc4 !== 64) { output = output + String.fromCharCode(chr3); } } while (i < input.length); return output; }; var atob = decodeBase64; // Additional imports asmLibraryArg['__tempMemory__'] = 0; // risky! // This will be replaced by the actual wasm2js code. var exports = instantiate(asmLibraryArg, wasmMemory, wasmTable); return { 'exports': exports }; }, instantiate: function(binary, info) { return { then: function(ok, err) { ok({ 'instance': new WebAssembly.Instance(new WebAssembly.Module(binary, info)) }); } }; } }; var tempRet0 = 0; var asmLibraryArg = { log_i32: function(x) { console.log('[LoggingExternalInterface logging ' + literal(x, 'i32') + ']'); }, log_i64: function(x, h) { console.log('[LoggingExternalInterface logging ' + literal(x, 'i32') + ' ' + literal(h, 'i32') + ']'); }, log_f32: function(x) { console.log('[LoggingExternalInterface logging ' + literal(x, 'f64') + ']'); }, log_f64: function(x) { console.log('[LoggingExternalInterface logging ' + literal(x, 'f64') + ']'); }, log_execution: function(loc) { console.log('log_execution ' + loc); }, setTempRet0: function(x) { tempRet0 = x; }, getTempRet0: function() { return x; }, get_i32: function(loc, index, value) { console.log('get_i32 ' + [loc, index, value]); return value; }, get_i64: function(loc, index, low, high) { console.log('get_i64 ' + [loc, index, low, high]); asmLibraryArg['setTempRet0'](high); return low; }, get_f32: function(loc, index, value) { console.log('get_f32 ' + [loc, index, value]); return value; }, get_f64: function(loc, index, value) { console.log('get_f64 ' + [loc, index, value]); return value; }, get_anyref: function(loc, index, value) { console.log('get_anyref ' + [loc, index, value]); return value; }, get_exnref: function(loc, index, value) { console.log('get_exnref ' + [loc, index, value]); return value; }, set_i32: function(loc, index, value) { console.log('set_i32 ' + [loc, index, value]); return value; }, set_i64: function(loc, index, low, high) { console.log('set_i64 ' + [loc, index, low, high]); asmLibraryArg['setTempRet0'](high); return low; }, set_f32: function(loc, index, value) { console.log('set_f32 ' + [loc, index, value]); return value; }, set_f64: function(loc, index, value) { console.log('set_f64 ' + [loc, index, value]); return value; }, set_anyref: function(loc, index, value) { console.log('set_anyref ' + [loc, index, value]); return value; }, set_exnref: function(loc, index, value) { console.log('set_exnref ' + [loc, index, value]); return value; }, load_ptr: function(loc, bytes, offset, ptr) { console.log('load_ptr ' + [loc, bytes, offset, ptr]); return ptr; }, load_val_i32: function(loc, value) { console.log('load_val_i32 ' + [loc, value]); return value; }, load_val_i64: function(loc, low, high) { console.log('load_val_i64 ' + [loc, low, high]); asmLibraryArg['setTempRet0'](high); return low; }, load_val_f32: function(loc, value) { console.log('loaload_val_i32d_ptr ' + [loc, value]); return value; }, load_val_f64: function(loc, value) { console.log('load_val_f64 ' + [loc, value]); return value; }, store_ptr: function(loc, bytes, offset, ptr) { console.log('store_ptr ' + [loc, bytes, offset, ptr]); return ptr; }, store_val_i32: function(loc, value) { console.log('store_val_i32 ' + [loc, value]); return value; }, store_val_i64: function(loc, low, high) { console.log('store_val_i64 ' + [loc, low, high]); asmLibraryArg['setTempRet0'](high); return low; }, store_val_f32: function(loc, value) { console.log('loastore_val_i32d_ptr ' + [loc, value]); return value; }, store_val_f64: function(loc, value) { console.log('store_val_f64 ' + [loc, value]); return value; }, }; var wasmMemory = new WebAssembly.Memory({ initial: 1 }); var wasmTable = new WebAssembly.Table({ initial: 1 }); binaryen-version_91/setup.cfg000066400000000000000000000000661362402614000165020ustar00rootroot00000000000000[pep8] ignore = E111,E114 [flake8] ignore = E111,E114 binaryen-version_91/src/000077500000000000000000000000001362402614000154465ustar00rootroot00000000000000binaryen-version_91/src/abi/000077500000000000000000000000001362402614000162015ustar00rootroot00000000000000binaryen-version_91/src/abi/abi.h000066400000000000000000000015751362402614000171150ustar00rootroot00000000000000/* * Copyright 2017 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_abi_abi_h #define wasm_abi_abi_h #include "wasm.h" namespace wasm { namespace ABI { // The pointer type. Will need to update this for wasm64 const static Type PointerType = Type::i32; } // namespace ABI } // namespace wasm #endif // wasm_abi_abi_h binaryen-version_91/src/abi/js.h000066400000000000000000000057171362402614000170000ustar00rootroot00000000000000/* * Copyright 2018 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_abi_abi_h #define wasm_abi_abi_h #include "asmjs/shared-constants.h" #include "wasm.h" namespace wasm { namespace ABI { enum class LegalizationLevel { Full = 0, Minimal = 1 }; inline std::string getLegalizationPass(LegalizationLevel level) { if (level == LegalizationLevel::Full) { return "legalize-js-interface"; } else { return "legalize-js-interface-minimally"; } } namespace wasm2js { extern cashew::IString SCRATCH_LOAD_I32; extern cashew::IString SCRATCH_STORE_I32; extern cashew::IString SCRATCH_LOAD_I64; extern cashew::IString SCRATCH_STORE_I64; extern cashew::IString SCRATCH_LOAD_F32; extern cashew::IString SCRATCH_STORE_F32; extern cashew::IString SCRATCH_LOAD_F64; extern cashew::IString SCRATCH_STORE_F64; // The wasm2js scratch memory helpers let us read and write to scratch memory // for purposes of implementing things like reinterpret, etc. // The optional "specific" parameter is a specific function we want. If not // provided, we create them all. inline void ensureScratchMemoryHelpers(Module* wasm, cashew::IString specific = cashew::IString()) { auto ensureImport = [&](Name name, Type params, Type results) { if (wasm->getFunctionOrNull(name)) { return; } if (specific.is() && name != specific) { return; } auto func = make_unique(); func->name = name; func->sig = Signature(params, results); func->module = ENV; func->base = name; wasm->addFunction(std::move(func)); }; ensureImport(SCRATCH_LOAD_I32, {Type::i32}, Type::i32); ensureImport(SCRATCH_STORE_I32, {Type::i32, Type::i32}, Type::none); ensureImport(SCRATCH_LOAD_I64, {}, Type::i64); ensureImport(SCRATCH_STORE_I64, {Type::i64}, Type::none); ensureImport(SCRATCH_LOAD_F32, {}, Type::f32); ensureImport(SCRATCH_STORE_F32, {Type::f32}, Type::none); ensureImport(SCRATCH_LOAD_F64, {}, Type::f64); ensureImport(SCRATCH_STORE_F64, {Type::f64}, Type::none); } inline bool isScratchMemoryHelper(cashew::IString name) { return name == SCRATCH_LOAD_I32 || name == SCRATCH_STORE_I32 || name == SCRATCH_LOAD_I64 || name == SCRATCH_STORE_I64 || name == SCRATCH_LOAD_F32 || name == SCRATCH_STORE_F32 || name == SCRATCH_LOAD_F64 || name == SCRATCH_STORE_F64; } } // namespace wasm2js } // namespace ABI } // namespace wasm #endif // wasm_abi_abi_h binaryen-version_91/src/abi/stack.h000066400000000000000000000120251362402614000174570ustar00rootroot00000000000000/* * Copyright 2017 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_abi_stack_h #define wasm_abi_stack_h #include "abi.h" #include "asmjs/shared-constants.h" #include "ir/find_all.h" #include "ir/global-utils.h" #include "shared-constants.h" #include "wasm-builder.h" #include "wasm.h" namespace wasm { namespace ABI { enum { StackAlign = 16 }; inline Index stackAlign(Index size) { return (size + StackAlign - 1) & -StackAlign; } // Allocate some space on the stack, and assign it to a local. // The local will have the same constant value in all the function, so you can // just local.get it anywhere there. // // FIXME: This function assumes that the stack grows upward, per the convention // used by fastcomp. The stack grows downward when using the WASM backend. inline void getStackSpace(Index local, Function* func, Index size, Module& wasm) { // Attempt to locate the stack pointer by recognizing code idioms // used by Emscripten. First, look for a global initialized to an // imported variable named "STACKTOP" in environment "env". auto* stackPointer = GlobalUtils::getGlobalInitializedToImport(wasm, ENV, "STACKTOP"); // Starting with Emscripten 1.38.24, the stack pointer variable is // initialized with a literal constant, eliminating the import that // we used to locate the stack pointer by name. We must match a more // complicated idiom, expecting to see the module structured as follows: // //(module // ... // (export "stackSave" (func $stackSave)) // ... // (func $stackSave (; 410 ;) (; has Stack IR ;) (result i32) // (global.get $STACKTOP) // ) // ... //) if (!stackPointer) { auto* stackSaveFunctionExport = wasm.getExportOrNull("stackSave"); if (stackSaveFunctionExport && stackSaveFunctionExport->kind == ExternalKind::Function) { auto* stackSaveFunction = wasm.getFunction(stackSaveFunctionExport->value); assert(!stackSaveFunction->imported()); auto* globalGet = stackSaveFunction->body->dynCast(); if (globalGet) { stackPointer = wasm.getGlobal(globalGet->name); } } } if (!stackPointer) { Fatal() << "getStackSpace: failed to find the stack pointer"; } // align the size size = stackAlign(size); // TODO: find existing stack usage, and add on top of that - carefully Builder builder(wasm); auto* block = builder.makeBlock(); block->list.push_back(builder.makeLocalSet( local, builder.makeGlobalGet(stackPointer->name, PointerType))); // TODO: add stack max check Expression* added; if (PointerType == Type::i32) { added = builder.makeBinary(AddInt32, builder.makeLocalGet(local, PointerType), builder.makeConst(Literal(int32_t(size)))); } else { WASM_UNREACHABLE("unhandled PointerType"); } block->list.push_back(builder.makeGlobalSet(stackPointer->name, added)); auto makeStackRestore = [&]() { return builder.makeGlobalSet(stackPointer->name, builder.makeLocalGet(local, PointerType)); }; // add stack restores to the returns FindAllPointers finder(func->body); for (auto** ptr : finder.list) { auto* ret = (*ptr)->cast(); if (ret->value && ret->value->type != Type::unreachable) { // handle the returned value auto* block = builder.makeBlock(); auto temp = builder.addVar(func, ret->value->type); block->list.push_back(builder.makeLocalSet(temp, ret->value)); block->list.push_back(makeStackRestore()); block->list.push_back( builder.makeReturn(builder.makeLocalGet(temp, ret->value->type))); block->finalize(); *ptr = block; } else { // restore, then return *ptr = builder.makeSequence(makeStackRestore(), ret); } } // add stack restores to the body if (func->body->type == Type::none) { block->list.push_back(func->body); block->list.push_back(makeStackRestore()); } else if (func->body->type == Type::unreachable) { block->list.push_back(func->body); // no need to restore the old stack value, we're gone anyhow } else { // save the return value auto temp = builder.addVar(func, func->sig.results); block->list.push_back(builder.makeLocalSet(temp, func->body)); block->list.push_back(makeStackRestore()); block->list.push_back(builder.makeLocalGet(temp, func->sig.results)); } block->finalize(); func->body = block; } } // namespace ABI } // namespace wasm #endif // wasm_abi_stack_h binaryen-version_91/src/abi/wasm-object.h000066400000000000000000000021431362402614000205650ustar00rootroot00000000000000/* * Copyright 2018 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // // Contains definitions used for wasm object files. // See: https://github.com/WebAssembly/tool-conventions/blob/master/Linking.md // #ifndef wasm_abi_wasm_object_h #define wasm_abi_wasm_object_h namespace wasm { namespace ABI { enum LinkType : unsigned { WASM_STACK_POINTER = 0x1, WASM_SYMBOL_INFO = 0x2, WASM_DATA_SIZE = 0x3, WASM_DATA_ALIGNMENT = 0x4, WASM_SEGMENT_INFO = 0x5, WASM_INIT_FUNCS = 0x6, }; } // namespace ABI } // namespace wasm #endif // wasm_abi_wasm_object_h binaryen-version_91/src/asm2wasm.h000066400000000000000000003527171362402614000173700ustar00rootroot00000000000000/* * Copyright 2015 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // // asm.js-to-WebAssembly translator. Uses the Emscripten optimizer // infrastructure. // #ifndef wasm_asm2wasm_h #define wasm_asm2wasm_h #include "abi/js.h" #include "asm_v_wasm.h" #include "asmjs/shared-constants.h" #include "emscripten-optimizer/optimizer.h" #include "ir/bits.h" #include "ir/branch-utils.h" #include "ir/literal-utils.h" #include "ir/module-utils.h" #include "ir/trapping.h" #include "ir/utils.h" #include "mixed_arena.h" #include "parsing.h" #include "pass.h" #include "passes/passes.h" #include "shared-constants.h" #include "support/debug.h" #include "wasm-builder.h" #include "wasm-emscripten.h" #include "wasm-module-building.h" #include "wasm.h" #define DEBUG_TYPE "asm2wasm" namespace wasm { using namespace cashew; // Names Name I32_CTTZ("i32_cttz"); Name I32_CTPOP("i32_ctpop"); Name I32_BC2F("i32_bc2f"); Name I32_BC2I("i32_bc2i"); Name I64("i64"); Name I64_CONST("i64_const"); Name I64_ADD("i64_add"); Name I64_SUB("i64_sub"); Name I64_MUL("i64_mul"); Name I64_UDIV("i64_udiv"); Name I64_SDIV("i64_sdiv"); Name I64_UREM("i64_urem"); Name I64_SREM("i64_srem"); Name I64_AND("i64_and"); Name I64_OR("i64_or"); Name I64_XOR("i64_xor"); Name I64_SHL("i64_shl"); Name I64_ASHR("i64_ashr"); Name I64_LSHR("i64_lshr"); Name I64_EQ("i64_eq"); Name I64_NE("i64_ne"); Name I64_ULE("i64_ule"); Name I64_SLE("i64_sle"); Name I64_UGE("i64_uge"); Name I64_SGE("i64_sge"); Name I64_ULT("i64_ult"); Name I64_SLT("i64_slt"); Name I64_UGT("i64_ugt"); Name I64_SGT("i64_sgt"); Name I64_TRUNC("i64_trunc"); Name I64_SEXT("i64_sext"); Name I64_ZEXT("i64_zext"); Name I64_S2F("i64_s2f"); Name I64_S2D("i64_s2d"); Name I64_U2F("i64_u2f"); Name I64_U2D("i64_u2d"); Name I64_F2S("i64_f2s"); Name I64_D2S("i64_d2s"); Name I64_F2U("i64_f2u"); Name I64_D2U("i64_d2u"); Name I64_BC2D("i64_bc2d"); Name I64_BC2I("i64_bc2i"); Name I64_CTTZ("i64_cttz"); Name I64_CTLZ("i64_ctlz"); Name I64_CTPOP("i64_ctpop"); Name F32_COPYSIGN("f32_copysign"); Name F64_COPYSIGN("f64_copysign"); Name LOAD1("load1"); Name LOAD2("load2"); Name LOAD4("load4"); Name LOAD8("load8"); Name LOADF("loadf"); Name LOADD("loadd"); Name STORE1("store1"); Name STORE2("store2"); Name STORE4("store4"); Name STORE8("store8"); Name STOREF("storef"); Name STORED("stored"); Name FTCALL("ftCall_"); Name MFTCALL("mftCall_"); Name MAX_("max"); Name MIN_("min"); Name ATOMICS("Atomics"); Name ATOMICS_LOAD("load"); Name ATOMICS_STORE("store"); Name ATOMICS_EXCHANGE("exchange"); Name ATOMICS_COMPARE_EXCHANGE("compareExchange"); Name ATOMICS_ADD("add"); Name ATOMICS_SUB("sub"); Name ATOMICS_AND("and"); Name ATOMICS_OR("or"); Name ATOMICS_XOR("xor"); Name I64_ATOMICS_LOAD("i64_atomics_load"); Name I64_ATOMICS_STORE("i64_atomics_store"); Name I64_ATOMICS_AND("i64_atomics_and"); Name I64_ATOMICS_OR("i64_atomics_or"); Name I64_ATOMICS_XOR("i64_atomics_xor"); Name I64_ATOMICS_ADD("i64_atomics_add"); Name I64_ATOMICS_SUB("i64_atomics_sub"); Name I64_ATOMICS_EXCHANGE("i64_atomics_exchange"); Name I64_ATOMICS_COMPAREEXCHANGE("i64_atomics_compareExchange"); Name TEMP_DOUBLE_PTR("tempDoublePtr"); Name EMSCRIPTEN_DEBUGINFO("emscripten_debuginfo"); // Utilities static WASM_NORETURN void abort_on(std::string why, Ref element) { std::cerr << why << ' '; element->stringify(std::cerr); std::cerr << '\n'; abort(); } static WASM_NORETURN void abort_on(std::string why, IString element) { std::cerr << why << ' ' << element.str << '\n'; abort(); } Index indexOr(Index x, Index y) { return x ? x : y; } // useful when we need to see our parent, in an asm.js expression stack struct AstStackHelper { static std::vector astStack; AstStackHelper(Ref curr) { astStack.push_back(curr); } ~AstStackHelper() { astStack.pop_back(); } Ref getParent() { if (astStack.size() >= 2) { return astStack[astStack.size() - 2]; } else { return Ref(); } } }; std::vector AstStackHelper::astStack; static bool startsWith(const char* string, const char* prefix) { while (1) { if (*prefix == 0) { return true; } if (*string == 0) { return false; } if (*string++ != *prefix++) { return false; } } }; // // Asm2WasmPreProcessor - does some initial parsing/processing // of asm.js code. // struct Asm2WasmPreProcessor { bool memoryGrowth = false; bool debugInfo = false; std::vector debugInfoFileNames; std::unordered_map debugInfoFileIndices; char* allocatedCopy = nullptr; ~Asm2WasmPreProcessor() { if (allocatedCopy) { free(allocatedCopy); } } char* process(char* input) { // emcc --separate-asm modules can look like // // Module["asm"] = (function(global, env, buffer) { // .. // }); // // we need to clean that up. if (*input == 'M') { size_t num = strlen(input); while (*input != 'f') { input++; num--; } char* end = input + num - 1; while (*end != '}') { *end = 0; end--; } } // asm.js memory growth uses a quite elaborate pattern. Instead of parsing // and matching it, we do a simpler detection on emscripten's asm.js output // format const char* START_FUNCS = "// EMSCRIPTEN_START_FUNCS"; char* marker = strstr(input, START_FUNCS); if (marker) { // look for memory growth code just up to here, as an optimization *marker = 0; } // this can only show up in growth code, as normal asm.js lacks "true" char* growthSign = strstr(input, "return true;"); if (growthSign) { memoryGrowth = true; // clean out this function, we don't need it. first where it starts char* growthFuncStart = growthSign; while (*growthFuncStart != '{') { growthFuncStart--; // skip body } while (*growthFuncStart != '(') { growthFuncStart--; // skip params } while (*growthFuncStart != ' ') { growthFuncStart--; // skip function name } while (*growthFuncStart != 'f') { growthFuncStart--; // skip 'function' } assert(strstr(growthFuncStart, "function ") == growthFuncStart); char* growthFuncEnd = strchr(growthSign, '}'); assert(growthFuncEnd > growthFuncStart + 5); growthFuncStart[0] = '/'; growthFuncStart[1] = '*'; growthFuncEnd--; growthFuncEnd[0] = '*'; growthFuncEnd[1] = '/'; } if (marker) { *marker = START_FUNCS[0]; } // handle debug info, if this build wants that. if (debugInfo) { // asm.js debug info comments look like // ..command..; //@line 4 "tests/hello_world.c" // we convert those into emscripten_debuginfo(file, line) // calls, where the params are indices into a mapping. then // the compiler and optimizer can operate on them. after // that, we can apply the debug info to the wasm node right // before it - this is guaranteed to be correct without opts, // and is usually decently accurate with them. // an upper bound on how much more space we need as a multiple of the // original const auto SCALE_FACTOR = 1.25; // an upper bound on how much we write for each debug info element itself const auto ADD_FACTOR = 100; auto size = strlen(input); auto upperBound = Index(size * SCALE_FACTOR) + ADD_FACTOR; char* copy = allocatedCopy = (char*)malloc(upperBound); char* end = copy + upperBound; char* out = copy; std::string DEBUGINFO_INTRINSIC = EMSCRIPTEN_DEBUGINFO.str; auto DEBUGINFO_INTRINSIC_SIZE = DEBUGINFO_INTRINSIC.size(); const char* UNKNOWN_FILE = "(unknown)"; bool seenUseAsm = false; while (input[0]) { if (out + ADD_FACTOR >= end) { Fatal() << "error in handling debug info"; } if (startsWith(input, "//@line")) { char* linePos = input + 8; char* lineEnd = strpbrk(input + 8, " \n"); if (!lineEnd) { // comment goes to end of input break; } input = lineEnd + 1; std::string file; if (*lineEnd == ' ') { // we have a file char* filePos = strpbrk(input, "\"\n"); if (!filePos) { // goes to end of input break; } if (*filePos == '"') { char* fileEnd = strpbrk(filePos + 1, "\"\n"); input = fileEnd + 1; *fileEnd = 0; file = filePos + 1; } else { file = UNKNOWN_FILE; input = filePos + 1; } } else { // no file, we found \n file = UNKNOWN_FILE; } *lineEnd = 0; std::string line = linePos; auto iter = debugInfoFileIndices.find(file); if (iter == debugInfoFileIndices.end()) { Index index = debugInfoFileNames.size(); debugInfoFileNames.push_back(file); debugInfoFileIndices[file] = index; } std::string fileIndex = std::to_string(debugInfoFileIndices[file]); // write out the intrinsic strcpy(out, DEBUGINFO_INTRINSIC.c_str()); out += DEBUGINFO_INTRINSIC_SIZE; *out++ = '('; strcpy(out, fileIndex.c_str()); out += fileIndex.size(); *out++ = ','; strcpy(out, line.c_str()); out += line.size(); *out++ = ')'; *out++ = ';'; } else if (!seenUseAsm && (startsWith(input, "asm'") || startsWith(input, "asm\""))) { // end of "use asm" or "almost asm" // skip the end of "use asm"; (5 chars, a,s,m," or ',;) const auto SKIP = 5; seenUseAsm = true; memcpy(out, input, SKIP); out += SKIP; input += SKIP; // add a fake import for the intrinsic, so the module validates std::string import = "\n var emscripten_debuginfo = env.emscripten_debuginfo;"; strcpy(out, import.c_str()); out += import.size(); } else { *out++ = *input++; } } if (out >= end) { Fatal() << "error in handling debug info"; } *out = 0; input = copy; } return input; } }; static Call* checkDebugInfo(Expression* curr) { if (auto* call = curr->dynCast()) { if (call->target == EMSCRIPTEN_DEBUGINFO) { return call; } } return nullptr; } // Debug info appears in the ast as calls to the debug intrinsic. These are // usually after the relevant node. We adjust them to a position that is not // dce-able, so that they are not trivially removed when optimizing. struct AdjustDebugInfo : public WalkerPass>> { bool isFunctionParallel() override { return true; } Pass* create() override { return new AdjustDebugInfo(); } AdjustDebugInfo() { name = "adjust-debug-info"; } void visitBlock(Block* curr) { // look for a debug info call that is unreachable if (curr->list.size() == 0) { return; } auto* back = curr->list.back(); for (Index i = 1; i < curr->list.size(); i++) { if (checkDebugInfo(curr->list[i]) && !checkDebugInfo(curr->list[i - 1])) { // swap them std::swap(curr->list[i - 1], curr->list[i]); } } if (curr->list.back() != back) { // we changed the last element, update the type curr->finalize(); } } }; // // Asm2WasmBuilder - converts an asm.js module into WebAssembly // class Asm2WasmBuilder { public: Module& wasm; MixedArena& allocator; Builder builder; std::unique_ptr optimizingBuilder; // globals struct MappedGlobal { Type type; // if true, this is an import - we should read the value, not just set a // zero bool import; IString module, base; MappedGlobal() : type(Type::none), import(false) {} MappedGlobal(Type type) : type(type), import(false) {} MappedGlobal(Type type, bool import, IString module, IString base) : type(type), import(import), module(module), base(base) {} }; // function table // each asm function table gets a range in the one wasm table, starting at a // location std::map functionTableStarts; Asm2WasmPreProcessor& preprocessor; bool debug; TrapMode trapMode; TrappingFunctionContainer trappingFunctions; PassOptions passOptions; bool legalizeJavaScriptFFI; bool runOptimizationPasses; bool wasmOnly; public: std::map mappedGlobals; private: void allocateGlobal(IString name, Type type, Literal value = Literal()) { assert(mappedGlobals.find(name) == mappedGlobals.end()); if (value.type == Type::none) { value = Literal::makeZero(type); } mappedGlobals.emplace(name, MappedGlobal(type)); wasm.addGlobal(builder.makeGlobal( name, type, builder.makeConst(value), Builder::Mutable)); } struct View { unsigned bytes; bool integer, signed_; AsmType type; View() : bytes(0) {} View(unsigned bytes, bool integer, bool signed_, AsmType type) : bytes(bytes), integer(integer), signed_(signed_), type(type) {} }; std::map views; // name (e.g. HEAP8) => view info // Imported names of Math.* IString Math_imul; IString Math_clz32; IString Math_fround; IString Math_abs; IString Math_floor; IString Math_ceil; IString Math_sqrt; IString Math_max; IString Math_min; // Imported names of Atomics.* IString Atomics_load; IString Atomics_store; IString Atomics_exchange; IString Atomics_compareExchange; IString Atomics_add; IString Atomics_sub; IString Atomics_and; IString Atomics_or; IString Atomics_xor; IString llvm_cttz_i32; IString tempDoublePtr; // imported name of tempDoublePtr // possibly-minified names, detected via their exports IString udivmoddi4; IString getTempRet0; // function types. we fill in this information as we see // uses, in the first pass std::map importedSignatures; void noteImportedFunctionCall(Ref ast, Type resultType, Call* call) { assert(ast[0] == CALL && ast[1]->isString()); IString importName = ast[1]->getIString(); std::vector params; for (auto* operand : call->operands) { params.push_back(operand->type); } Signature sig = Signature(Type(params), resultType); // if we already saw this signature, verify it's the same (or else handle // that) if (importedSignatures.find(importName) != importedSignatures.end()) { Signature& previous = importedSignatures[importName]; if (sig != previous) { std::vector mergedParams = previous.params.expand(); // merge it in. we'll add on extra 0 parameters for ones not actually // used, and upgrade types to double where there is a conflict (which is // ok since in JS, double can contain everything i32 and f32 can). for (size_t i = 0; i < params.size(); i++) { if (mergedParams.size() > i) { if (mergedParams[i] != params[i]) { mergedParams[i] = Type::f64; // overloaded type, make it a double } } else { mergedParams.push_back(params[i]); // add a new param } } previous.params = Type(mergedParams); // we accept none and a concrete type, but two concrete types mean we // need to use an f64 to contain anything if (previous.results == Type::none) { previous.results = sig.results; // use a more concrete type } else if (previous.results != sig.results && sig.results != Type::none) { // overloaded return type, make it a double previous.results = Type::f64; } } } else { importedSignatures[importName] = sig; } } Type getResultTypeOfCallUsingParent(Ref parent, AsmData* data) { Type result = Type::none; if (!!parent) { // if the parent is a seq, we cannot be the last element in it (we would // have a coercion, which would be the parent), so we must be (us, // somethingElse), and so our return is void if (parent[0] != SEQ) { result = detectWasmType(parent, data); } } return result; } Signature getSignature(Ref parent, ExpressionList& operands, AsmData* data) { Type results = getResultTypeOfCallUsingParent(parent, data); std::vector paramTypes; for (auto& op : operands) { assert(op->type != Type::unreachable); paramTypes.push_back(op->type); } return Signature(Type(paramTypes), results); } public: Asm2WasmBuilder(Module& wasm, Asm2WasmPreProcessor& preprocessor, bool debug, TrapMode trapMode, PassOptions passOptions, bool legalizeJavaScriptFFI, bool runOptimizationPasses, bool wasmOnly) : wasm(wasm), allocator(wasm.allocator), builder(wasm), preprocessor(preprocessor), debug(debug), trapMode(trapMode), trappingFunctions(trapMode, wasm, /* immediate = */ true), passOptions(passOptions), legalizeJavaScriptFFI(legalizeJavaScriptFFI), runOptimizationPasses(runOptimizationPasses), wasmOnly(wasmOnly) {} void processAsm(Ref ast); private: AsmType detectAsmType(Ref ast, AsmData* data) { if (ast->isString()) { IString name = ast->getIString(); if (!data->isLocal(name)) { // must be global assert(mappedGlobals.find(name) != mappedGlobals.end()); return wasmToAsmType(mappedGlobals[name].type); } } else if (ast->isArray(SUB) && ast[1]->isString()) { // could be a heap access, use view info auto view = views.find(ast[1]->getIString()); if (view != views.end()) { return view->second.type; } } return detectType(ast, data, false, Math_fround, wasmOnly); } Type detectWasmType(Ref ast, AsmData* data) { return asmToWasmType(detectAsmType(ast, data)); } bool isUnsignedCoercion(Ref ast) { return detectSign(ast, Math_fround) == ASM_UNSIGNED; } bool isParentUnsignedCoercion(Ref parent) { // parent may not exist, or may be a non-relevant node if (!!parent && parent->isArray() && parent[0] == BINARY && isUnsignedCoercion(parent)) { return true; } return false; } BinaryOp parseAsmBinaryOp(IString op, Ref left, Ref right, Expression* leftWasm, Expression* rightWasm) { Type leftType = leftWasm->type; bool isInteger = leftType == Type::i32; if (op == PLUS) { return isInteger ? BinaryOp::AddInt32 : (leftType == Type::f32 ? BinaryOp::AddFloat32 : BinaryOp::AddFloat64); } if (op == MINUS) { return isInteger ? BinaryOp::SubInt32 : (leftType == Type::f32 ? BinaryOp::SubFloat32 : BinaryOp::SubFloat64); } if (op == MUL) { return isInteger ? BinaryOp::MulInt32 : (leftType == Type::f32 ? BinaryOp::MulFloat32 : BinaryOp::MulFloat64); } if (op == AND) { return BinaryOp::AndInt32; } if (op == OR) { return BinaryOp::OrInt32; } if (op == XOR) { return BinaryOp::XorInt32; } if (op == LSHIFT) { return BinaryOp::ShlInt32; } if (op == RSHIFT) { return BinaryOp::ShrSInt32; } if (op == TRSHIFT) { return BinaryOp::ShrUInt32; } if (op == EQ) { return isInteger ? BinaryOp::EqInt32 : (leftType == Type::f32 ? BinaryOp::EqFloat32 : BinaryOp::EqFloat64); } if (op == NE) { return isInteger ? BinaryOp::NeInt32 : (leftType == Type::f32 ? BinaryOp::NeFloat32 : BinaryOp::NeFloat64); } bool isUnsigned = isUnsignedCoercion(left) || isUnsignedCoercion(right); if (op == DIV) { if (isInteger) { return isUnsigned ? BinaryOp::DivUInt32 : BinaryOp::DivSInt32; } return leftType == Type::f32 ? BinaryOp::DivFloat32 : BinaryOp::DivFloat64; } if (op == MOD) { if (isInteger) { return isUnsigned ? BinaryOp::RemUInt32 : BinaryOp::RemSInt32; } return BinaryOp::RemSInt32; // XXX no floating-point remainder op, this // must be handled by the caller } if (op == GE) { if (isInteger) { return isUnsigned ? BinaryOp::GeUInt32 : BinaryOp::GeSInt32; } return leftType == Type::f32 ? BinaryOp::GeFloat32 : BinaryOp::GeFloat64; } if (op == GT) { if (isInteger) { return isUnsigned ? BinaryOp::GtUInt32 : BinaryOp::GtSInt32; } return leftType == Type::f32 ? BinaryOp::GtFloat32 : BinaryOp::GtFloat64; } if (op == LE) { if (isInteger) { return isUnsigned ? BinaryOp::LeUInt32 : BinaryOp::LeSInt32; } return leftType == Type::f32 ? BinaryOp::LeFloat32 : BinaryOp::LeFloat64; } if (op == LT) { if (isInteger) { return isUnsigned ? BinaryOp::LtUInt32 : BinaryOp::LtSInt32; } return leftType == Type::f32 ? BinaryOp::LtFloat32 : BinaryOp::LtFloat64; } abort_on("bad wasm binary op", op); abort(); // avoid warning } int32_t bytesToShift(unsigned bytes) { switch (bytes) { case 1: return 0; case 2: return 1; case 4: return 2; case 8: return 3; default: {} } abort(); return -1; // avoid warning } std::map tempNums; Literal checkLiteral(Ref ast, bool rawIsInteger = true) { if (ast->isNumber()) { if (rawIsInteger) { return Literal((int32_t)ast->getInteger()); } else { return Literal(ast->getNumber()); } } else if (ast->isArray(UNARY_PREFIX)) { if (ast[1] == PLUS && ast[2]->isNumber()) { return Literal((double)ast[2]->getNumber()); } if (ast[1] == MINUS && ast[2]->isNumber()) { double num = -ast[2]->getNumber(); if (isSInteger32(num)) { return Literal((int32_t)num); } if (isUInteger32(num)) { return Literal((uint32_t)num); } assert(false && "expected signed or unsigned int32"); } if (ast[1] == PLUS && ast[2]->isArray(UNARY_PREFIX) && ast[2][1] == MINUS && ast[2][2]->isNumber()) { return Literal((double)-ast[2][2]->getNumber()); } if (ast[1] == MINUS && ast[2]->isArray(UNARY_PREFIX) && ast[2][1] == PLUS && ast[2][2]->isNumber()) { return Literal((double)-ast[2][2]->getNumber()); } } else if (wasmOnly && ast->isArray(CALL) && ast[1]->isString() && ast[1] == I64_CONST) { uint64_t low = ast[2][0]->getNumber(); uint64_t high = ast[2][1]->getNumber(); return Literal(uint64_t(low + (high << 32))); } return Literal(); } Literal getLiteral(Ref ast) { Literal ret = checkLiteral(ast); assert(ret.type != Type::none); return ret; } void fixCallType(Expression* call, Type type) { if (call->is()) { call->cast()->type = type; } else if (call->is()) { call->cast()->type = type; } } bool getBuiltinSignature(Signature& sig, Name module, Name base, ExpressionList* operands = nullptr) { if (module == GLOBAL_MATH) { if (base == ABS) { assert(operands && operands->size() == 1); Type type = (*operands)[0]->type; if (type == Type::i32) { sig = Signature(Type::i32, Type::i32); return true; } if (type == Type::f32) { sig = Signature(Type::f32, Type::f32); return true; } if (type == Type::f64) { sig = Signature(Type::f64, Type::f64); return true; } } } return false; } // ensure a nameless block Block* blockify(Expression* expression) { if (expression->is() && !expression->cast()->name.is()) { return expression->dynCast(); } auto ret = allocator.alloc(); ret->list.push_back(expression); ret->finalize(); return ret; } Expression* ensureDouble(Expression* expr) { return wasm::ensureDouble(expr, allocator); } Expression* truncateToInt32(Expression* value) { if (value->type == Type::i64) { return builder.makeUnary(UnaryOp::WrapInt64, value); } // either i32, or a call_import whose type we don't know yet (but would be // legalized to i32 anyhow) return value; } Function* processFunction(Ref ast); }; void Asm2WasmBuilder::processAsm(Ref ast) { assert(ast[0] == TOPLEVEL); if (ast[1]->size() == 0) { Fatal() << "empty input"; } Ref asmFunction = ast[1][0]; assert(asmFunction[0] == DEFUN); Ref body = asmFunction[3]; assert(body[0][0] == STRING && (body[0][1]->getIString() == IString("use asm") || body[0][1]->getIString() == IString("almost asm"))); // extra functions that we add, that are not from the compiled code. we need // to make sure to optimize them normally (OptimizingIncrementalModuleBuilder // does that on the fly for compiled code) std::vector extraSupportFunctions; // first, add the memory elements. we do this before the main compile+optimize // since the optimizer should see the memory // apply memory growth, if relevant if (preprocessor.memoryGrowth) { EmscriptenGlueGenerator generator(wasm); auto* func = generator.generateMemoryGrowthFunction(); extraSupportFunctions.push_back(func); wasm.memory.max = Memory::kUnlimitedSize; } // import memory wasm.memory.name = MEMORY; wasm.memory.module = ENV; wasm.memory.base = MEMORY; wasm.memory.exists = true; // import table wasm.table.name = TABLE; wasm.table.module = ENV; wasm.table.base = TABLE; wasm.table.exists = true; // Import memory offset, if not already there { auto* import = new Global; import->name = MEMORY_BASE; import->module = "env"; import->base = MEMORY_BASE; import->type = Type::i32; wasm.addGlobal(import); } // Import table offset, if not already there { auto* import = new Global; import->name = TABLE_BASE; import->module = "env"; import->base = TABLE_BASE; import->type = Type::i32; wasm.addGlobal(import); } auto addImport = [&](IString name, Ref imported, Type type) { assert(imported[0] == DOT); Ref module = imported[1]; IString moduleName; if (module->isArray(DOT)) { // we can have (global.Math).floor; skip the 'Math' assert(module[1]->isString()); if (module[2] == MATH) { if (imported[2] == IMUL) { assert(Math_imul.isNull()); Math_imul = name; return; } else if (imported[2] == CLZ32) { assert(Math_clz32.isNull()); Math_clz32 = name; return; } else if (imported[2] == FROUND) { assert(Math_fround.isNull()); Math_fround = name; return; } else if (imported[2] == ABS) { assert(Math_abs.isNull()); Math_abs = name; return; } else if (imported[2] == FLOOR) { assert(Math_floor.isNull()); Math_floor = name; return; } else if (imported[2] == CEIL) { assert(Math_ceil.isNull()); Math_ceil = name; return; } else if (imported[2] == SQRT) { assert(Math_sqrt.isNull()); Math_sqrt = name; return; } else if (imported[2] == MAX_) { assert(Math_max.isNull()); Math_max = name; return; } else if (imported[2] == MIN_) { assert(Math_min.isNull()); Math_min = name; return; } } else if (module[2] == ATOMICS) { if (imported[2] == ATOMICS_LOAD) { assert(Atomics_load.isNull()); Atomics_load = name; return; } else if (imported[2] == ATOMICS_STORE) { assert(Atomics_store.isNull()); Atomics_store = name; return; } else if (imported[2] == ATOMICS_EXCHANGE) { assert(Atomics_exchange.isNull()); Atomics_exchange = name; return; } else if (imported[2] == ATOMICS_COMPARE_EXCHANGE) { assert(Atomics_compareExchange.isNull()); Atomics_compareExchange = name; return; } else if (imported[2] == ATOMICS_ADD) { assert(Atomics_add.isNull()); Atomics_add = name; return; } else if (imported[2] == ATOMICS_SUB) { assert(Atomics_sub.isNull()); Atomics_sub = name; return; } else if (imported[2] == ATOMICS_AND) { assert(Atomics_and.isNull()); Atomics_and = name; return; } else if (imported[2] == ATOMICS_OR) { assert(Atomics_or.isNull()); Atomics_or = name; return; } else if (imported[2] == ATOMICS_XOR) { assert(Atomics_xor.isNull()); Atomics_xor = name; return; } } std::string fullName = module[1]->getCString(); fullName += '.'; fullName += +module[2]->getCString(); moduleName = IString(fullName.c_str(), false); } else { assert(module->isString()); moduleName = module->getIString(); if (moduleName == ENV) { auto base = imported[2]->getIString(); if (base == TEMP_DOUBLE_PTR) { assert(tempDoublePtr.isNull()); tempDoublePtr = name; // we don't return here, as we can only optimize out some uses of tDP. // So it remains imported } else if (base == LLVM_CTTZ_I32) { assert(llvm_cttz_i32.isNull()); llvm_cttz_i32 = name; return; } } } auto base = imported[2]->getIString(); // special-case some asm builtins if (module == GLOBAL && (base == NAN_ || base == INFINITY_)) { type = Type::f64; } if (type != Type::none) { // this is a global auto* import = new Global; import->name = name; import->module = moduleName; import->base = base; import->type = type; mappedGlobals.emplace(name, type); // __table_base and __memory_base are used as segment/element offsets, and // must be constant; otherwise, an asm.js import of a constant is mutable, // e.g. STACKTOP if (name != TABLE_BASE && name != MEMORY_BASE) { // we need imported globals to be mutable, but wasm doesn't support that // yet, so we must import an immutable and create a mutable global // initialized to its value import->name = Name(std::string(import->name.str) + "$asm2wasm$import"); { wasm.addGlobal( builder.makeGlobal(name, type, builder.makeGlobalGet(import->name, type), Builder::Mutable)); } } if ((name == TABLE_BASE || name == MEMORY_BASE) && wasm.getGlobalOrNull(import->base)) { return; } wasm.addGlobal(import); } else { // this is a function auto* import = new Function; import->name = name; import->module = moduleName; import->base = base; import->sig = Signature(Type::none, Type::none); wasm.addFunction(import); } }; IString Int8Array, Int16Array, Int32Array, UInt8Array, UInt16Array, UInt32Array, Float32Array, Float64Array; // set up optimization if (runOptimizationPasses) { Index numFunctions = 0; for (unsigned i = 1; i < body->size(); i++) { if (body[i][0] == DEFUN) { numFunctions++; } } optimizingBuilder = make_unique( &wasm, numFunctions, passOptions, [&](PassRunner& passRunner) { // addPrePasses passRunner.options.lowMemoryUnused = true; if (debug) { passRunner.setDebug(true); passRunner.setValidateGlobally(false); } // run autodrop first, before optimizations passRunner.add(make_unique()); if (preprocessor.debugInfo) { // fix up debug info to better survive optimization passRunner.add(make_unique()); } // optimize relooper label variable usage at the wasm level, where it is // easy passRunner.add("relooper-jump-threading"); }, debug, false /* do not validate globally yet */); } // if we see no function tables in the processing below, then the table still // exists and has size 0 wasm.table.initial = wasm.table.max = 0; // first pass - do all global things, aside from function bodies (second pass) // and function imports and indirect calls (last pass) for (unsigned i = 1; i < body->size(); i++) { Ref curr = body[i]; if (curr[0] == VAR) { // import, global, or table for (unsigned j = 0; j < curr[1]->size(); j++) { Ref pair = curr[1][j]; IString name = pair[0]->getIString(); Ref value = pair[1]; if (value->isNumber()) { // global int allocateGlobal( name, Type::i32, Literal(int32_t(value->getInteger()))); } else if (value[0] == BINARY) { // int import assert(value[1] == OR && value[3]->isNumber() && value[3]->getNumber() == 0); Ref import = value[2]; // env.what addImport(name, import, Type::i32); } else if (value[0] == UNARY_PREFIX) { // double import or global assert(value[1] == PLUS); Ref import = value[2]; if (import->isNumber()) { // global assert(import->getNumber() == 0); allocateGlobal(name, Type::f64); } else { // import addImport(name, import, Type::f64); } } else if (value[0] == CALL) { assert(value[1]->isString() && value[1] == Math_fround && value[2][0]->isNumber() && value[2][0]->getNumber() == 0); allocateGlobal(name, Type::f32); } else if (value[0] == DOT) { // simple module.base import. can be a view, or a function. if (value[1]->isString()) { IString module = value[1]->getIString(); IString base = value[2]->getIString(); if (module == GLOBAL) { if (base == INT8ARRAY) { Int8Array = name; } else if (base == INT16ARRAY) { Int16Array = name; } else if (base == INT32ARRAY) { Int32Array = name; } else if (base == UINT8ARRAY) { UInt8Array = name; } else if (base == UINT16ARRAY) { UInt16Array = name; } else if (base == UINT32ARRAY) { UInt32Array = name; } else if (base == FLOAT32ARRAY) { Float32Array = name; } else if (base == FLOAT64ARRAY) { Float64Array = name; } } } // function import addImport(name, value, Type::none); } else if (value[0] == NEW) { // ignore imports of typed arrays, but note the names of the arrays value = value[1]; assert(value[0] == CALL); unsigned bytes; bool integer, signed_; AsmType asmType; Ref constructor = value[1]; if (constructor->isArray(DOT)) { // global.*Array IString heap = constructor[2]->getIString(); if (heap == INT8ARRAY) { bytes = 1; integer = true; signed_ = true; asmType = ASM_INT; } else if (heap == INT16ARRAY) { bytes = 2; integer = true; signed_ = true; asmType = ASM_INT; } else if (heap == INT32ARRAY) { bytes = 4; integer = true; signed_ = true; asmType = ASM_INT; } else if (heap == UINT8ARRAY) { bytes = 1; integer = true; signed_ = false; asmType = ASM_INT; } else if (heap == UINT16ARRAY) { bytes = 2; integer = true; signed_ = false; asmType = ASM_INT; } else if (heap == UINT32ARRAY) { bytes = 4; integer = true; signed_ = false; asmType = ASM_INT; } else if (heap == FLOAT32ARRAY) { bytes = 4; integer = false; signed_ = true; asmType = ASM_FLOAT; } else if (heap == FLOAT64ARRAY) { bytes = 8; integer = false; signed_ = true; asmType = ASM_DOUBLE; } else { abort_on("invalid view import", heap); } } else { // *ArrayView that was previously imported assert(constructor->isString()); IString viewName = constructor->getIString(); if (viewName == Int8Array) { bytes = 1; integer = true; signed_ = true; asmType = ASM_INT; } else if (viewName == Int16Array) { bytes = 2; integer = true; signed_ = true; asmType = ASM_INT; } else if (viewName == Int32Array) { bytes = 4; integer = true; signed_ = true; asmType = ASM_INT; } else if (viewName == UInt8Array) { bytes = 1; integer = true; signed_ = false; asmType = ASM_INT; } else if (viewName == UInt16Array) { bytes = 2; integer = true; signed_ = false; asmType = ASM_INT; } else if (viewName == UInt32Array) { bytes = 4; integer = true; signed_ = false; asmType = ASM_INT; } else if (viewName == Float32Array) { bytes = 4; integer = false; signed_ = true; asmType = ASM_FLOAT; } else if (viewName == Float64Array) { bytes = 8; integer = false; signed_ = true; asmType = ASM_DOUBLE; } else { abort_on("invalid short view import", viewName); } } assert(views.find(name) == views.end()); views.emplace(name, View(bytes, integer, signed_, asmType)); } else if (value[0] == ARRAY) { // function table. we merge them into one big table, so e.g. [foo, // b1] , [b2, bar] => [foo, b1, b2, bar] // TODO: when not using aliasing function pointers, we could merge // them by noticing that // index 0 in each table is the null func, and each other index // should only have one non-null func. However, that breaks down // when function pointer casts are emulated. if (wasm.table.segments.size() == 0) { wasm.table.segments.emplace_back( builder.makeGlobalGet(Name(TABLE_BASE), Type::i32)); } auto& segment = wasm.table.segments[0]; functionTableStarts[name] = segment.data.size(); // this table starts here Ref contents = value[1]; for (unsigned k = 0; k < contents->size(); k++) { IString curr = contents[k]->getIString(); segment.data.push_back(curr); } wasm.table.initial = wasm.table.max = segment.data.size(); } else { abort_on("invalid var element", pair); } } } else if (curr[0] == RETURN) { // exports Ref object = curr[1]; Ref contents = object[1]; std::map exported; for (unsigned k = 0; k < contents->size(); k++) { Ref pair = contents[k]; IString key = pair[0]->getIString(); if (pair[1]->isString()) { // exporting a function IString value = pair[1]->getIString(); if (key == Name("_emscripten_replace_memory")) { // asm.js memory growth provides this special non-asm function, // which we don't need (we use memory.grow) assert(!wasm.getFunctionOrNull(value)); continue; } else if (key == UDIVMODDI4) { udivmoddi4 = value; } else if (key == GET_TEMP_RET0) { getTempRet0 = value; } if (exported.count(key) > 0) { // asm.js allows duplicate exports, but not wasm. use the last, like // asm.js exported[key]->value = value; } else { auto* export_ = new Export; export_->name = key; export_->value = value; export_->kind = ExternalKind::Function; wasm.addExport(export_); exported[key] = export_; } } else { // export a number. create a global and export it assert(pair[1]->isNumber()); assert(exported.count(key) == 0); auto value = pair[1]->getInteger(); auto* global = builder.makeGlobal(key, Type::i32, builder.makeConst(Literal(int32_t(value))), Builder::Immutable); wasm.addGlobal(global); auto* export_ = new Export; export_->name = key; export_->value = global->name; export_->kind = ExternalKind::Global; wasm.addExport(export_); exported[key] = export_; } } } } // second pass: function bodies for (unsigned i = 1; i < body->size(); i++) { Ref curr = body[i]; if (curr[0] == DEFUN) { // function auto* func = processFunction(curr); if (wasm.getFunctionOrNull(func->name)) { Fatal() << "duplicate function: " << func->name; } if (runOptimizationPasses) { optimizingBuilder->addFunction(func); } else { wasm.addFunction(func); } } } if (runOptimizationPasses) { optimizingBuilder->finish(); // Now that we have a full module, do memory packing optimizations { PassRunner passRunner(&wasm, passOptions); passRunner.options.lowMemoryUnused = true; passRunner.add("memory-packing"); passRunner.run(); } // if we added any helper functions (like non-trapping i32-div, etc.), then // those have not been optimized (the optimizing builder has just been fed // the asm.js functions). Optimize those now. Typically there are very few, // just do it sequentially. PassRunner passRunner(&wasm, passOptions); passRunner.options.lowMemoryUnused = true; passRunner.addDefaultFunctionOptimizationPasses(); for (auto& pair : trappingFunctions.getFunctions()) { auto* func = pair.second; passRunner.runOnFunction(func); } for (auto* func : extraSupportFunctions) { passRunner.runOnFunction(func); } } wasm.debugInfoFileNames = std::move(preprocessor.debugInfoFileNames); // third pass. first, function imports std::vector toErase; ModuleUtils::iterImportedFunctions(wasm, [&](Function* import) { IString name = import->name; if (importedSignatures.find(name) != importedSignatures.end()) { // special math builtins Signature builtin; if (getBuiltinSignature(builtin, import->module, import->base)) { import->sig = builtin; } else { import->sig = importedSignatures[name]; } } else if (import->module != ASM2WASM) { // special-case the special module // never actually used, which means we don't know the function type since // the usage tells us, so illegal for it to remain toErase.push_back(name); } }); for (auto curr : toErase) { wasm.removeFunction(curr); } // Finalize calls now that everything is known and generated struct FinalizeCalls : public WalkerPass> { bool isFunctionParallel() override { return true; } Pass* create() override { return new FinalizeCalls(parent); } Asm2WasmBuilder* parent; FinalizeCalls(Asm2WasmBuilder* parent) : parent(parent) { name = "finalize-calls"; } void notifyAboutWrongOperands(std::string why, Function* calledFunc) { // use a mutex as this may be shown from multiple threads static std::mutex mutex; std::unique_lock lock(mutex); static const int MAX_SHOWN = 20; static std::unique_ptr> numShown; if (!numShown) { numShown = make_unique>(); numShown->store(0); } if (numShown->load() >= MAX_SHOWN) { return; } std::cerr << why << " in call from " << getFunction()->name << " to " << calledFunc->name << " (this is likely due to undefined behavior in C, like " "defining a function one way and calling it in another, " "which is important to fix)\n"; (*numShown)++; if (numShown->load() >= MAX_SHOWN) { std::cerr << "(" << numShown->load() << " such warnings shown; not showing any more)\n"; } } void visitCall(Call* curr) { // The call target may not exist if it is one of our special fake imports // for callIndirect fixups auto* calledFunc = getModule()->getFunctionOrNull(curr->target); if (calledFunc && !calledFunc->imported()) { // The result type of the function being called is now known, and can be // applied. auto results = calledFunc->sig.results; if (curr->type != results) { curr->type = results; } // Handle mismatched numbers of arguments. In clang, if a function is // declared one way but called in another, it inserts bitcasts to make // things work. Those end up working since it is "ok" to drop or add // parameters in native platforms, even though it's undefined behavior. // We warn about it here, but tolerate it, if there is a simple // solution. const std::vector& params = calledFunc->sig.params.expand(); if (curr->operands.size() < params.size()) { notifyAboutWrongOperands("warning: asm2wasm adding operands", calledFunc); while (curr->operands.size() < params.size()) { // Add params as necessary, with zeros. curr->operands.push_back(LiteralUtils::makeZero( params[curr->operands.size()], *getModule())); } } if (curr->operands.size() > params.size()) { notifyAboutWrongOperands("warning: asm2wasm dropping operands", calledFunc); curr->operands.resize(params.size()); } // If the types are wrong, validation will fail later anyhow, but add a // warning here, it may help people. for (Index i = 0; i < curr->operands.size(); i++) { auto sent = curr->operands[i]->type; if (sent != Type::unreachable && sent != params[i]) { notifyAboutWrongOperands( "error: asm2wasm seeing an invalid argument type at index " + std::to_string(i) + " (this will not validate)", calledFunc); } } } else { // A call to an import // fill things out: add extra params as needed, etc. asm tolerates ffi // overloading, wasm does not auto iter = parent->importedSignatures.find(curr->target); if (iter == parent->importedSignatures.end()) { return; // one of our fake imports for callIndirect fixups } const std::vector& params = iter->second.params.expand(); for (size_t i = 0; i < params.size(); i++) { if (i >= curr->operands.size()) { // add a new param auto val = parent->allocator.alloc(); val->type = val->value.type = params[i]; curr->operands.push_back(val); } else if (curr->operands[i]->type != params[i]) { // if the param is used, then we have overloading here and the // combined type must be f64; if this is an unreachable param, then // it doesn't matter. assert(params[i] == Type::f64 || curr->operands[i]->type == Type::unreachable); // overloaded, upgrade to f64 switch (curr->operands[i]->type.getSingle()) { case Type::i32: curr->operands[i] = parent->builder.makeUnary( ConvertSInt32ToFloat64, curr->operands[i]); break; case Type::f32: curr->operands[i] = parent->builder.makeUnary(PromoteFloat32, curr->operands[i]); break; default: {} // f64, unreachable, etc., are all good } } } Module* wasm = getModule(); Type importResults = wasm->getFunction(curr->target)->sig.results; if (curr->type != importResults) { auto old = curr->type; curr->type = importResults; if (importResults == Type::f64) { // we use a JS f64 value which is the most general, and convert to // it switch (old.getSingle()) { case Type::i32: { Unary* trunc = parent->builder.makeUnary(TruncSFloat64ToInt32, curr); replaceCurrent( makeTrappingUnary(trunc, parent->trappingFunctions)); break; } case Type::f32: { replaceCurrent(parent->builder.makeUnary(DemoteFloat64, curr)); break; } case Type::none: { // this function returns a value, but we are not using it, so it // must be dropped. autodrop will do that for us. break; } default: WASM_UNREACHABLE("unexpected type"); } } else { assert(old == Type::none); // we don't want a return value here, but the import does provide // one autodrop will do that for us. } } } } void visitCallIndirect(CallIndirect* curr) { // we already call into target = something + offset, where offset is a // callImport with the name of the table. replace that with the table // offset note that for an ftCall or mftCall, we have no asm.js mask, so // have nothing to do here auto* target = curr->target; // might be a block with a fallthrough if (auto* block = target->dynCast()) { target = block->list.back(); } // the something might have been optimized out, leaving only the call if (auto* call = target->dynCast()) { auto tableName = call->target; if (parent->functionTableStarts.find(tableName) == parent->functionTableStarts.end()) { return; } curr->target = parent->builder.makeConst( Literal((int32_t)parent->functionTableStarts[tableName])); return; } auto* add = target->dynCast(); if (!add) { return; } if (add->right->is()) { auto* offset = add->right->cast(); auto tableName = offset->target; if (parent->functionTableStarts.find(tableName) == parent->functionTableStarts.end()) { return; } add->right = parent->builder.makeConst( Literal((int32_t)parent->functionTableStarts[tableName])); } else { auto* offset = add->left->dynCast(); if (!offset) { return; } auto tableName = offset->target; if (parent->functionTableStarts.find(tableName) == parent->functionTableStarts.end()) { return; } add->left = parent->builder.makeConst( Literal((int32_t)parent->functionTableStarts[tableName])); } } void visitFunction(Function* curr) { // changing call types requires we percolate types, and drop stuff. // we do this in this pass so that we don't look broken between passes AutoDrop().walkFunctionInModule(curr, getModule()); } }; // apply debug info, reducing intrinsic calls into annotations on the ast // nodes struct ApplyDebugInfo : public WalkerPass< ExpressionStackWalker>> { bool isFunctionParallel() override { return true; } Pass* create() override { return new ApplyDebugInfo(); } ApplyDebugInfo() { name = "apply-debug-info"; } Call* lastDebugInfo = nullptr; void visitExpression(Expression* curr) { if (auto* call = checkDebugInfo(curr)) { lastDebugInfo = call; replaceCurrent(getModule()->allocator.alloc()); } else { if (lastDebugInfo) { auto& debugLocations = getFunction()->debugLocations; uint32_t fileIndex = lastDebugInfo->operands[0]->cast()->value.geti32(); assert(getModule()->debugInfoFileNames.size() > fileIndex); uint32_t lineNumber = lastDebugInfo->operands[1]->cast()->value.geti32(); // look up the stack, apply to the root expression Index i = expressionStack.size() - 1; while (1) { auto* exp = expressionStack[i]; bool parentIsStructure = i > 0 && (expressionStack[i - 1]->is() || expressionStack[i - 1]->is() || expressionStack[i - 1]->is()); if (i == 0 || parentIsStructure || exp->type == Type::none || exp->type == Type::unreachable) { if (debugLocations.count(exp) > 0) { // already present, so look back up i++; while (i < expressionStack.size()) { exp = expressionStack[i]; if (debugLocations.count(exp) == 0) { debugLocations[exp] = {fileIndex, lineNumber, 0}; break; } i++; } } else { debugLocations[exp] = {fileIndex, lineNumber, 0}; } break; } i--; } lastDebugInfo = nullptr; } } } }; PassRunner passRunner(&wasm, passOptions); passRunner.options.lowMemoryUnused = true; if (debug) { passRunner.setDebug(true); passRunner.setValidateGlobally(false); } // finalizeCalls also does autoDrop, which is crucial for the non-optimizing // case, so that the output of the first pass is valid passRunner.add(make_unique(this)); passRunner.add(ABI::getLegalizationPass(legalizeJavaScriptFFI ? ABI::LegalizationLevel::Full : ABI::LegalizationLevel::Minimal)); if (runOptimizationPasses) { // autodrop can add some garbage passRunner.add("vacuum"); passRunner.add("remove-unused-brs"); passRunner.add("vacuum"); passRunner.add("remove-unused-names"); passRunner.add("merge-blocks"); passRunner.add("optimize-instructions"); passRunner.add("post-emscripten"); } else { if (preprocessor.debugInfo) { // we would have run this before if optimizing, do it now otherwise. must // precede ApplyDebugInfo passRunner.add(make_unique()); } } if (preprocessor.debugInfo) { passRunner.add(make_unique()); // FIXME maybe just remove the nops that were debuginfo nodes, if not // optimizing? passRunner.add("vacuum"); } if (runOptimizationPasses) { // do final global optimizations after all function work is done // (e.g. duplicate funcs may appear thanks to that work) passRunner.addDefaultGlobalOptimizationPostPasses(); } passRunner.run(); // remove the debug info intrinsic if (preprocessor.debugInfo) { wasm.removeFunction(EMSCRIPTEN_DEBUGINFO); } if (udivmoddi4.is() && getTempRet0.is()) { // generate a wasm-optimized __udivmoddi4 method, which we can do much more // efficiently in wasm we can only do this if we know getTempRet0 as well // since we use it to figure out which minified global is tempRet0 // (getTempRet0 might be an import, if this is a shared module, so we can't // optimize that case) Name tempRet0; { Expression* curr = wasm.getFunction(getTempRet0)->body; if (curr->is()) { curr = curr->cast()->list.back(); } if (curr->is()) { curr = curr->cast()->value; } auto* get = curr->cast(); tempRet0 = get->name; } // udivmoddi4 receives xl, xh, yl, yl, r, and // if r then *r = x % y // returns x / y auto* func = wasm.getFunction(udivmoddi4); Builder::clearLocals(func); Index xl = Builder::addParam(func, "xl", Type::i32), xh = Builder::addParam(func, "xh", Type::i32), yl = Builder::addParam(func, "yl", Type::i32), yh = Builder::addParam(func, "yh", Type::i32), r = Builder::addParam(func, "r", Type::i32), x64 = Builder::addVar(func, "x64", Type::i64), y64 = Builder::addVar(func, "y64", Type::i64); auto* body = allocator.alloc(); body->list.push_back( builder.makeLocalSet(x64, I64Utilities::recreateI64(builder, xl, xh))); body->list.push_back( builder.makeLocalSet(y64, I64Utilities::recreateI64(builder, yl, yh))); body->list.push_back( builder.makeIf(builder.makeLocalGet(r, Type::i32), builder.makeStore( 8, 0, 8, builder.makeLocalGet(r, Type::i32), builder.makeBinary(RemUInt64, builder.makeLocalGet(x64, Type::i64), builder.makeLocalGet(y64, Type::i64)), Type::i64))); body->list.push_back(builder.makeLocalSet( x64, builder.makeBinary(DivUInt64, builder.makeLocalGet(x64, Type::i64), builder.makeLocalGet(y64, Type::i64)))); body->list.push_back( builder.makeGlobalSet(tempRet0, I64Utilities::getI64High(builder, x64))); body->list.push_back(I64Utilities::getI64Low(builder, x64)); body->finalize(); func->body = body; } } Function* Asm2WasmBuilder::processFunction(Ref ast) { auto name = ast[1]->getIString(); BYN_TRACE("asm2wasming func: " << ast[1]->getIString().str << '\n'); auto function = new Function; function->sig = Signature(Type::none, Type::none); function->name = name; Ref params = ast[2]; Ref body = ast[3]; UniqueNameMapper nameMapper; // given an asm.js label, returns the wasm label for breaks or continues auto getBreakLabelName = [](IString label) { return Name(std::string("label$break$") + label.str); }; auto getContinueLabelName = [](IString label) { return Name(std::string("label$continue$") + label.str); }; IStringSet functionVariables; // params or vars IString parentLabel; // set in LABEL, then read in WHILE/DO/SWITCH std::vector breakStack; // where a break will go std::vector continueStack; // where a continue will go AsmData asmData; // need to know var and param types, for asm type detection for (unsigned i = 0; i < params->size(); i++) { Ref curr = body[i]; auto* assign = curr->asAssignName(); IString name = assign->target(); AsmType asmType = detectType(assign->value(), nullptr, false, Math_fround, wasmOnly); Builder::addParam(function, name, asmToWasmType(asmType)); functionVariables.insert(name); asmData.addParam(name, asmType); } unsigned start = params->size(); while (start < body->size() && body[start]->isArray(VAR)) { Ref curr = body[start]; for (unsigned j = 0; j < curr[1]->size(); j++) { Ref pair = curr[1][j]; IString name = pair[0]->getIString(); AsmType asmType = detectType(pair[1], nullptr, true, Math_fround, wasmOnly); Builder::addVar(function, name, asmToWasmType(asmType)); functionVariables.insert(name); asmData.addVar(name, asmType); } start++; } bool addedI32Temp = false; auto ensureI32Temp = [&]() { if (addedI32Temp) { return; } addedI32Temp = true; Builder::addVar(function, I32_TEMP, Type::i32); functionVariables.insert(I32_TEMP); asmData.addVar(I32_TEMP, ASM_INT); }; bool seenReturn = false; // function->result is updated if we see a return // processors std::function processStatements; std::function processUnshifted; std::function processIgnoringShift; std::function process = [&](Ref ast) -> Expression* { // TODO: only create one when we need it? AstStackHelper astStackHelper(ast); if (ast->isString()) { IString name = ast->getIString(); if (functionVariables.has(name)) { // var in scope auto ret = allocator.alloc(); ret->index = function->getLocalIndex(name); ret->type = asmToWasmType(asmData.getType(name)); return ret; } if (name == DEBUGGER) { Call* call = allocator.alloc(); call->target = DEBUGGER; call->type = Type::none; static bool addedImport = false; if (!addedImport) { addedImport = true; auto import = new Function; // debugger = asm2wasm.debugger; import->name = DEBUGGER; import->module = ASM2WASM; import->base = DEBUGGER; import->sig = Signature(Type::none, Type::none); wasm.addFunction(import); } return call; } // global var assert(mappedGlobals.find(name) != mappedGlobals.end() ? true : (std::cerr << name.str << '\n', false)); MappedGlobal& global = mappedGlobals[name]; return builder.makeGlobalGet(name, global.type); } if (ast->isNumber()) { auto ret = allocator.alloc(); double num = ast->getNumber(); if (isSInteger32(num)) { ret->value = Literal(int32_t(toSInteger32(num))); } else if (isUInteger32(num)) { ret->value = Literal(uint32_t(toUInteger32(num))); } else { ret->value = Literal(num); } ret->type = ret->value.type; return ret; } if (ast->isAssignName()) { auto* assign = ast->asAssignName(); IString name = assign->target(); if (functionVariables.has(name)) { auto ret = allocator.alloc(); ret->index = function->getLocalIndex(assign->target()); ret->value = process(assign->value()); ret->makeSet(); ret->finalize(); return ret; } // global var if (mappedGlobals.find(name) == mappedGlobals.end()) { Fatal() << "error: access of a non-existent global var " << name.str; } auto* ret = builder.makeGlobalSet(name, process(assign->value())); // global.set does not return; if our value is trivially not used, don't // emit a load (if nontrivially not used, opts get it later) auto parent = astStackHelper.getParent(); if (!parent || parent->isArray(BLOCK) || parent->isArray(IF)) { return ret; } return builder.makeSequence( ret, builder.makeGlobalGet(name, ret->value->type)); } if (ast->isAssign()) { auto* assign = ast->asAssign(); assert(assign->target()->isArray(SUB)); Ref target = assign->target(); assert(target[1]->isString()); IString heap = target[1]->getIString(); assert(views.find(heap) != views.end()); View& view = views[heap]; auto ret = allocator.alloc(); ret->isAtomic = false; ret->bytes = view.bytes; ret->offset = 0; ret->align = view.bytes; ret->ptr = processUnshifted(target[2], view.bytes); ret->value = process(assign->value()); ret->valueType = asmToWasmType(view.type); ret->finalize(); if (ret->valueType != ret->value->type) { // in asm.js we have some implicit coercions that we must do explicitly // here if (ret->valueType == Type::f32 && ret->value->type == Type::f64) { auto conv = allocator.alloc(); conv->op = DemoteFloat64; conv->value = ret->value; conv->type = Type::f32; ret->value = conv; } else if (ret->valueType == Type::f64 && ret->value->type == Type::f32) { ret->value = ensureDouble(ret->value); } else { abort_on("bad sub[] types", ast); } } return ret; } IString what = ast[0]->getIString(); if (what == BINARY) { if ((ast[1] == OR || ast[1] == TRSHIFT) && ast[3]->isNumber() && ast[3]->getNumber() == 0) { // just look through the ()|0 or ()>>>0 coercion auto ret = process(ast[2]); fixCallType(ret, Type::i32); return ret; } auto ret = allocator.alloc(); ret->left = process(ast[2]); ret->right = process(ast[3]); ret->op = parseAsmBinaryOp( ast[1]->getIString(), ast[2], ast[3], ret->left, ret->right); ret->finalize(); if (ret->op == BinaryOp::RemSInt32 && ret->type.isFloat()) { // WebAssembly does not have floating-point remainder, we have to emit a // call to a special import of ours Call* call = allocator.alloc(); call->target = F64_REM; call->operands.push_back(ensureDouble(ret->left)); call->operands.push_back(ensureDouble(ret->right)); call->type = Type::f64; static bool addedImport = false; if (!addedImport) { addedImport = true; auto import = new Function; // f64-rem = asm2wasm.f64-rem; import->name = F64_REM; import->module = ASM2WASM; import->base = F64_REM; import->sig = Signature({Type::f64, Type::f64}, Type::f64); wasm.addFunction(import); } return call; } return makeTrappingBinary(ret, trappingFunctions); } else if (what == SUB) { Ref target = ast[1]; assert(target->isString()); IString heap = target->getIString(); assert(views.find(heap) != views.end()); View& view = views[heap]; auto ret = allocator.alloc(); ret->isAtomic = false; ret->bytes = view.bytes; ret->signed_ = view.signed_; ret->offset = 0; ret->align = view.bytes; ret->ptr = processUnshifted(ast[2], view.bytes); ret->type = Type::get(view.bytes, !view.integer); return ret; } else if (what == UNARY_PREFIX) { if (ast[1] == PLUS) { Literal literal = checkLiteral(ast); if (literal.type != Type::none) { return builder.makeConst(literal); } auto ret = process(ast[2]); // we are a +() coercion if (ret->type == Type::i32) { auto conv = allocator.alloc(); conv->op = isUnsignedCoercion(ast[2]) ? ConvertUInt32ToFloat64 : ConvertSInt32ToFloat64; conv->value = ret; conv->type = Type::Type::f64; return conv; } if (ret->type == Type::f32) { return ensureDouble(ret); } fixCallType(ret, Type::f64); return ret; } else if (ast[1] == MINUS) { if (ast[2]->isNumber() || (ast[2]->isArray(UNARY_PREFIX) && ast[2][1] == PLUS && ast[2][2]->isNumber())) { auto ret = allocator.alloc(); ret->value = getLiteral(ast); ret->type = ret->value.type; return ret; } AsmType asmType = detectAsmType(ast[2], &asmData); if (asmType == ASM_INT) { // wasm has no unary negation for int, so do 0- auto ret = allocator.alloc(); ret->op = SubInt32; ret->left = builder.makeConst(Literal((int32_t)0)); ret->right = process(ast[2]); ret->type = Type::i32; return ret; } auto ret = allocator.alloc(); ret->value = process(ast[2]); if (asmType == ASM_DOUBLE) { ret->op = NegFloat64; ret->type = Type::f64; } else if (asmType == ASM_FLOAT) { ret->op = NegFloat32; ret->type = Type::f32; } else { WASM_UNREACHABLE("unexpected asm type"); } return ret; } else if (ast[1] == B_NOT) { // ~, might be ~~ as a coercion or just a not if (ast[2]->isArray(UNARY_PREFIX) && ast[2][1] == B_NOT) { // if we have an unsigned coercion on us, it is an unsigned op Expression* expr = process(ast[2][2]); bool isSigned = !isParentUnsignedCoercion(astStackHelper.getParent()); bool isF64 = expr->type == Type::f64; UnaryOp op; if (isSigned && isF64) { op = UnaryOp::TruncSFloat64ToInt32; } else if (isSigned && !isF64) { op = UnaryOp::TruncSFloat32ToInt32; } else if (!isSigned && isF64) { op = UnaryOp::TruncUFloat64ToInt32; } else { // !isSigned && !isF64 op = UnaryOp::TruncUFloat32ToInt32; } return makeTrappingUnary(builder.makeUnary(op, expr), trappingFunctions); } // no bitwise unary not, so do xor with -1 auto ret = allocator.alloc(); ret->op = XorInt32; ret->left = process(ast[2]); ret->right = builder.makeConst(Literal(int32_t(-1))); ret->type = Type::i32; return ret; } else if (ast[1] == L_NOT) { auto ret = allocator.alloc(); ret->op = EqZInt32; ret->value = process(ast[2]); ret->type = Type::i32; return ret; } abort_on("bad unary", ast); } else if (what == IF) { auto* condition = process(ast[1]); auto* ifTrue = process(ast[2]); return builder.makeIf(truncateToInt32(condition), ifTrue, !!ast[3] ? process(ast[3]) : nullptr); } else if (what == CALL) { if (ast[1]->isString()) { IString name = ast[1]->getIString(); if (name == Math_imul) { assert(ast[2]->size() == 2); auto ret = allocator.alloc(); ret->op = MulInt32; ret->left = process(ast[2][0]); ret->right = process(ast[2][1]); ret->type = Type::i32; return ret; } if (name == Math_clz32 || name == llvm_cttz_i32) { assert(ast[2]->size() == 1); auto ret = allocator.alloc(); ret->op = name == Math_clz32 ? ClzInt32 : CtzInt32; ret->value = process(ast[2][0]); ret->type = Type::i32; return ret; } if (name == Math_fround) { assert(ast[2]->size() == 1); Literal lit = checkLiteral(ast[2][0], false /* raw is float */); if (lit.type == Type::f64) { return builder.makeConst(Literal((float)lit.getf64())); } auto ret = allocator.alloc(); ret->value = process(ast[2][0]); if (ret->value->type == Type::f64) { ret->op = DemoteFloat64; } else if (ret->value->type == Type::i32) { if (isUnsignedCoercion(ast[2][0])) { ret->op = ConvertUInt32ToFloat32; } else { ret->op = ConvertSInt32ToFloat32; } } else if (ret->value->type == Type::f32) { return ret->value; } else if (ret->value->type == Type::none) { // call, etc. ret->value->type = Type::f32; return ret->value; } else { abort_on("confusing fround target", ast[2][0]); } ret->type = Type::f32; return ret; } if (name == Math_abs) { // overloaded on type: i32, f32 or f64 Expression* value = process(ast[2][0]); if (value->type == Type::i32) { // No wasm support, so use a temp local ensureI32Temp(); auto set = allocator.alloc(); set->index = function->getLocalIndex(I32_TEMP); set->value = value; set->makeSet(); set->finalize(); auto get = [&]() { auto ret = allocator.alloc(); ret->index = function->getLocalIndex(I32_TEMP); ret->type = Type::i32; return ret; }; auto isNegative = allocator.alloc(); isNegative->op = LtSInt32; isNegative->left = get(); isNegative->right = builder.makeConst(Literal(0)); isNegative->finalize(); auto block = allocator.alloc(); block->list.push_back(set); auto flip = allocator.alloc(); flip->op = SubInt32; flip->left = builder.makeConst(Literal(0)); flip->right = get(); flip->type = Type::i32; auto select = allocator.alloc(); if (tracing) { traceExpression(ret, "BinaryenSelect", condition, ifTrue, ifFalse, type); } ret->condition = (Expression*)condition; ret->ifTrue = (Expression*)ifTrue; ret->ifFalse = (Expression*)ifFalse; if (type != BinaryenTypeAuto()) { ret->finalize(Type(type)); } else { ret->finalize(); } return static_cast(ret); } BinaryenExpressionRef BinaryenDrop(BinaryenModuleRef module, BinaryenExpressionRef value) { auto* ret = ((Module*)module)->allocator.alloc(); if (tracing) { traceExpression(ret, "BinaryenDrop", value); } ret->value = (Expression*)value; ret->finalize(); return static_cast(ret); } BinaryenExpressionRef BinaryenReturn(BinaryenModuleRef module, BinaryenExpressionRef value) { auto* ret = Builder(*(Module*)module).makeReturn((Expression*)value); if (tracing) { traceExpression(ret, "BinaryenReturn", value); } return static_cast(ret); } BinaryenExpressionRef BinaryenHost(BinaryenModuleRef module, BinaryenOp op, const char* name, BinaryenExpressionRef* operands, BinaryenIndex numOperands) { auto* ret = ((Module*)module)->allocator.alloc(); if (tracing) { std::cout << " {\n"; std::cout << " BinaryenExpressionRef operands[] = { "; for (BinaryenIndex i = 0; i < numOperands; i++) { if (i > 0) { std::cout << ", "; } std::cout << "expressions[" << expressions[operands[i]] << "]"; } if (numOperands == 0) { // ensure the array is not empty, otherwise a compiler error on VS std::cout << "0"; } std::cout << " };\n "; traceExpression( ret, "BinaryenHost", StringLit(name), "operands", numOperands); std::cout << " }\n"; } ret->op = HostOp(op); if (name) { ret->nameOperand = name; } for (BinaryenIndex i = 0; i < numOperands; i++) { ret->operands.push_back((Expression*)operands[i]); } ret->finalize(); return static_cast(ret); } BinaryenExpressionRef BinaryenNop(BinaryenModuleRef module) { auto* ret = ((Module*)module)->allocator.alloc(); if (tracing) { traceExpression(ret, "BinaryenNop"); } return static_cast(ret); } BinaryenExpressionRef BinaryenUnreachable(BinaryenModuleRef module) { auto* ret = ((Module*)module)->allocator.alloc(); if (tracing) { traceExpression(ret, "BinaryenUnreachable"); } return static_cast(ret); } BinaryenExpressionRef BinaryenAtomicLoad(BinaryenModuleRef module, uint32_t bytes, uint32_t offset, BinaryenType type, BinaryenExpressionRef ptr) { auto* ret = Builder(*(Module*)module) .makeAtomicLoad(bytes, offset, (Expression*)ptr, Type(type)); if (tracing) { traceExpression(ret, "BinaryenAtomicLoad", bytes, offset, type, ptr); } return static_cast(ret); } BinaryenExpressionRef BinaryenAtomicStore(BinaryenModuleRef module, uint32_t bytes, uint32_t offset, BinaryenExpressionRef ptr, BinaryenExpressionRef value, BinaryenType type) { auto* ret = Builder(*(Module*)module) .makeAtomicStore( bytes, offset, (Expression*)ptr, (Expression*)value, Type(type)); if (tracing) { traceExpression( ret, "BinaryenAtomicStore", bytes, offset, ptr, value, type); } return static_cast(ret); } BinaryenExpressionRef BinaryenAtomicRMW(BinaryenModuleRef module, BinaryenOp op, BinaryenIndex bytes, BinaryenIndex offset, BinaryenExpressionRef ptr, BinaryenExpressionRef value, BinaryenType type) { auto* ret = Builder(*(Module*)module) .makeAtomicRMW(AtomicRMWOp(op), bytes, offset, (Expression*)ptr, (Expression*)value, Type(type)); if (tracing) { traceExpression( ret, "BinaryenAtomicRMW", op, bytes, offset, ptr, value, type); } return static_cast(ret); } BinaryenExpressionRef BinaryenAtomicCmpxchg(BinaryenModuleRef module, BinaryenIndex bytes, BinaryenIndex offset, BinaryenExpressionRef ptr, BinaryenExpressionRef expected, BinaryenExpressionRef replacement, BinaryenType type) { auto* ret = Builder(*(Module*)module) .makeAtomicCmpxchg(bytes, offset, (Expression*)ptr, (Expression*)expected, (Expression*)replacement, Type(type)); if (tracing) { traceExpression(ret, "BinaryenAtomicCmpxchg", bytes, offset, ptr, expected, replacement, type); } return static_cast(ret); } BinaryenExpressionRef BinaryenAtomicWait(BinaryenModuleRef module, BinaryenExpressionRef ptr, BinaryenExpressionRef expected, BinaryenExpressionRef timeout, BinaryenType expectedType) { auto* ret = Builder(*(Module*)module) .makeAtomicWait((Expression*)ptr, (Expression*)expected, (Expression*)timeout, Type(expectedType), 0); if (tracing) { traceExpression( ret, "BinaryenAtomicWait", ptr, expected, timeout, expectedType); } return static_cast(ret); } BinaryenExpressionRef BinaryenAtomicNotify(BinaryenModuleRef module, BinaryenExpressionRef ptr, BinaryenExpressionRef notifyCount) { auto* ret = Builder(*(Module*)module) .makeAtomicNotify((Expression*)ptr, (Expression*)notifyCount, 0); if (tracing) { traceExpression(ret, "BinaryenAtomicNotify", ptr, notifyCount); } return static_cast(ret); } BinaryenExpressionRef BinaryenAtomicFence(BinaryenModuleRef module) { auto* ret = Builder(*(Module*)module).makeAtomicFence(); if (tracing) { traceExpression(ret, "BinaryenAtomicFence"); } return static_cast(ret); } BinaryenExpressionRef BinaryenSIMDExtract(BinaryenModuleRef module, BinaryenOp op, BinaryenExpressionRef vec, uint8_t index) { auto* ret = Builder(*(Module*)module) .makeSIMDExtract(SIMDExtractOp(op), (Expression*)vec, index); if (tracing) { traceExpression(ret, "BinaryenSIMDExtract", op, vec, int(index)); } return static_cast(ret); } BinaryenExpressionRef BinaryenSIMDReplace(BinaryenModuleRef module, BinaryenOp op, BinaryenExpressionRef vec, uint8_t index, BinaryenExpressionRef value) { auto* ret = Builder(*(Module*)module) .makeSIMDReplace( SIMDReplaceOp(op), (Expression*)vec, index, (Expression*)value); if (tracing) { traceExpression(ret, "BinaryenSIMDReplace", op, vec, int(index), value); } return static_cast(ret); } BinaryenExpressionRef BinaryenSIMDShuffle(BinaryenModuleRef module, BinaryenExpressionRef left, BinaryenExpressionRef right, const uint8_t mask_[16]) { std::array mask; memcpy(mask.data(), mask_, 16); auto* ret = Builder(*(Module*)module) .makeSIMDShuffle((Expression*)left, (Expression*)right, mask); if (tracing) { std::cout << " {\n"; std::cout << " uint8_t mask[] = {"; for (size_t i = 0; i < mask.size(); ++i) { std::cout << int(mask[i]); if (i < mask.size() - 1) { std::cout << ", "; } } std::cout << "};\n "; traceExpression(ret, "BinaryenSIMDShuffle", left, right, "mask"); std::cout << " }\n"; } return static_cast(ret); } BinaryenExpressionRef BinaryenSIMDTernary(BinaryenModuleRef module, BinaryenOp op, BinaryenExpressionRef a, BinaryenExpressionRef b, BinaryenExpressionRef c) { auto* ret = Builder(*(Module*)module) .makeSIMDTernary( SIMDTernaryOp(op), (Expression*)a, (Expression*)b, (Expression*)c); if (tracing) { traceExpression(ret, "BinaryenSIMDTernary", op, a, b, c); } return static_cast(ret); } BinaryenExpressionRef BinaryenSIMDShift(BinaryenModuleRef module, BinaryenOp op, BinaryenExpressionRef vec, BinaryenExpressionRef shift) { auto* ret = Builder(*(Module*)module) .makeSIMDShift(SIMDShiftOp(op), (Expression*)vec, (Expression*)shift); if (tracing) { traceExpression(ret, "BinaryenSIMDShift", op, vec, shift); } return static_cast(ret); } BinaryenExpressionRef BinaryenSIMDLoad(BinaryenModuleRef module, BinaryenOp op, uint32_t offset, uint32_t align, BinaryenExpressionRef ptr) { auto* ret = Builder(*(Module*)module) .makeSIMDLoad( SIMDLoadOp(op), Address(offset), Address(align), (Expression*)ptr); if (tracing) { traceExpression(ret, "BinaryenSIMDLoad", op, offset, align, ptr); } return static_cast(ret); } BinaryenExpressionRef BinaryenMemoryInit(BinaryenModuleRef module, uint32_t segment, BinaryenExpressionRef dest, BinaryenExpressionRef offset, BinaryenExpressionRef size) { auto* ret = Builder(*(Module*)module) .makeMemoryInit( segment, (Expression*)dest, (Expression*)offset, (Expression*)size); if (tracing) { traceExpression(ret, "BinaryenMemoryInit", segment, dest, offset, size); } return static_cast(ret); } BinaryenExpressionRef BinaryenDataDrop(BinaryenModuleRef module, uint32_t segment) { auto* ret = Builder(*(Module*)module).makeDataDrop(segment); if (tracing) { traceExpression(ret, "BinaryenDataDrop", segment); } return static_cast(ret); } BinaryenExpressionRef BinaryenMemoryCopy(BinaryenModuleRef module, BinaryenExpressionRef dest, BinaryenExpressionRef source, BinaryenExpressionRef size) { auto* ret = Builder(*(Module*)module) .makeMemoryCopy( (Expression*)dest, (Expression*)source, (Expression*)size); if (tracing) { traceExpression(ret, "BinaryenMemoryCopy", dest, source, size); } return static_cast(ret); } BinaryenExpressionRef BinaryenMemoryFill(BinaryenModuleRef module, BinaryenExpressionRef dest, BinaryenExpressionRef value, BinaryenExpressionRef size) { auto* ret = Builder(*(Module*)module) .makeMemoryFill((Expression*)dest, (Expression*)value, (Expression*)size); if (tracing) { traceExpression(ret, "BinaryenMemoryFill", dest, value, size); } return static_cast(ret); } BinaryenExpressionRef BinaryenPush(BinaryenModuleRef module, BinaryenExpressionRef value) { auto* ret = Builder(*(Module*)module).makePush((Expression*)value); if (tracing) { traceExpression(ret, "BinaryenPush", value); } return static_cast(ret); } BinaryenExpressionRef BinaryenPop(BinaryenModuleRef module, BinaryenType type) { auto* ret = Builder(*(Module*)module).makePop(Type(type)); if (tracing) { traceExpression(ret, "BinaryenPop", type); } return static_cast(ret); } BinaryenExpressionRef BinaryenRefNull(BinaryenModuleRef module) { auto* ret = Builder(*(Module*)module).makeRefNull(); if (tracing) { traceExpression(ret, "BinaryenRefNull"); } return static_cast(ret); } BinaryenExpressionRef BinaryenRefIsNull(BinaryenModuleRef module, BinaryenExpressionRef value) { auto* ret = Builder(*(Module*)module).makeRefIsNull((Expression*)value); if (tracing) { traceExpression(ret, "BinaryenRefIsNull", value); } return static_cast(ret); } BinaryenExpressionRef BinaryenRefFunc(BinaryenModuleRef module, const char* func) { auto* ret = Builder(*(Module*)module).makeRefFunc(func); if (tracing) { traceExpression(ret, "BinaryenRefFunc", StringLit(func)); } return static_cast(ret); } BinaryenExpressionRef BinaryenTry(BinaryenModuleRef module, BinaryenExpressionRef body, BinaryenExpressionRef catchBody) { auto* ret = Builder(*(Module*)module) .makeTry((Expression*)body, (Expression*)catchBody); if (tracing) { traceExpression(ret, "BinaryenTry", body, catchBody); } return static_cast(ret); } BinaryenExpressionRef BinaryenThrow(BinaryenModuleRef module, const char* event, BinaryenExpressionRef* operands, BinaryenIndex numOperands) { std::vector args; for (BinaryenIndex i = 0; i < numOperands; i++) { args.push_back((Expression*)operands[i]); } auto* ret = Builder(*(Module*)module).makeThrow(event, args); if (tracing) { std::cout << " {\n"; std::cout << " BinaryenExpressionRef operands[] = { "; for (BinaryenIndex i = 0; i < numOperands; i++) { if (i > 0) { std::cout << ", "; } std::cout << "expressions[" << expressions[operands[i]] << "]"; } if (numOperands == 0) { // ensure the array is not empty, otherwise a compiler error on VS std::cout << "0"; } std::cout << " };\n "; traceExpression( ret, "BinaryenThrow", StringLit(event), "operands", numOperands); std::cout << " }\n"; } return static_cast(ret); } BinaryenExpressionRef BinaryenRethrow(BinaryenModuleRef module, BinaryenExpressionRef exnref) { auto* ret = Builder(*(Module*)module).makeRethrow((Expression*)exnref); if (tracing) { traceExpression(ret, "BinaryenRethrow", exnref); } return static_cast(ret); } BinaryenExpressionRef BinaryenBrOnExn(BinaryenModuleRef module, const char* name, const char* eventName, BinaryenExpressionRef exnref) { Module* wasm = (Module*)module; Event* event = wasm->getEventOrNull(eventName); assert(event && "br_on_exn's event must exist"); auto* ret = Builder(*wasm).makeBrOnExn(name, event, (Expression*)exnref); if (tracing) { traceExpression( ret, "BinaryenBrOnExn", StringLit(name), StringLit(eventName), exnref); } return static_cast(ret); } // Expression utility BinaryenExpressionId BinaryenExpressionGetId(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenExpressionGetId(expressions[" << expressions[expr] << "]);\n"; } return ((Expression*)expr)->_id; } BinaryenType BinaryenExpressionGetType(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenExpressionGetType(expressions[" << expressions[expr] << "]);\n"; } return ((Expression*)expr)->type.getID(); } void BinaryenExpressionPrint(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenExpressionPrint(expressions[" << expressions[expr] << "]);\n"; } WasmPrinter::printExpression((Expression*)expr, std::cout); std::cout << '\n'; } // Specific expression utility // Block const char* BinaryenBlockGetName(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenBlockGetName(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->name.c_str(); } BinaryenIndex BinaryenBlockGetNumChildren(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenBlockGetNumChildren(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->list.size(); } BinaryenExpressionRef BinaryenBlockGetChild(BinaryenExpressionRef expr, BinaryenIndex index) { if (tracing) { std::cout << " BinaryenBlockGetChild(expressions[" << expressions[expr] << "], " << index << ");\n"; } auto* expression = (Expression*)expr; assert(expression->is()); assert(index < static_cast(expression)->list.size()); return static_cast(expression)->list[index]; } // If BinaryenExpressionRef BinaryenIfGetCondition(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenIfGetCondition(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->condition; } BinaryenExpressionRef BinaryenIfGetIfTrue(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenIfGetIfTrue(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->ifTrue; } BinaryenExpressionRef BinaryenIfGetIfFalse(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenIfGetIfFalse(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->ifFalse; } // Loop const char* BinaryenLoopGetName(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenLoopGetName(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->name.c_str(); } BinaryenExpressionRef BinaryenLoopGetBody(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenLoopGetBody(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->body; } // Break const char* BinaryenBreakGetName(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenBreakGetName(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->name.c_str(); } BinaryenExpressionRef BinaryenBreakGetCondition(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenBreakGetCondition(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->condition; } BinaryenExpressionRef BinaryenBreakGetValue(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenBreakGetValue(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->value; } // Switch BinaryenIndex BinaryenSwitchGetNumNames(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenSwitchGetNumNames(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->targets.size(); } const char* BinaryenSwitchGetName(BinaryenExpressionRef expr, BinaryenIndex index) { if (tracing) { std::cout << " BinaryenSwitchGetName(expressions[" << expressions[expr] << "], " << index << ");\n"; } auto* expression = (Expression*)expr; assert(expression->is()); assert(index < static_cast(expression)->targets.size()); return static_cast(expression)->targets[index].c_str(); } const char* BinaryenSwitchGetDefaultName(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenSwitchGetDefaultName(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->default_.c_str(); } BinaryenExpressionRef BinaryenSwitchGetCondition(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenSwitchGetCondition(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->condition; } BinaryenExpressionRef BinaryenSwitchGetValue(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenSwitchGetValue(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->value; } // Call const char* BinaryenCallGetTarget(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenCallGetTarget(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->target.c_str(); } BinaryenIndex BinaryenCallGetNumOperands(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenCallGetNumOperands(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->operands.size(); } BinaryenExpressionRef BinaryenCallGetOperand(BinaryenExpressionRef expr, BinaryenIndex index) { if (tracing) { std::cout << " BinaryenCallGetOperand(expressions[" << expressions[expr] << "], " << index << ");\n"; } auto* expression = (Expression*)expr; assert(expression->is()); assert(index < static_cast(expression)->operands.size()); return static_cast(expression)->operands[index]; } // CallIndirect BinaryenExpressionRef BinaryenCallIndirectGetTarget(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenCallIndirectGetTarget(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->target; } BinaryenIndex BinaryenCallIndirectGetNumOperands(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenCallIndirectGetNumOperands(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->operands.size(); } BinaryenExpressionRef BinaryenCallIndirectGetOperand(BinaryenExpressionRef expr, BinaryenIndex index) { if (tracing) { std::cout << " BinaryenCallIndirectGetOperand(expressions[" << expressions[expr] << "], " << index << ");\n"; } auto* expression = (Expression*)expr; assert(expression->is()); assert(index < static_cast(expression)->operands.size()); return static_cast(expression)->operands[index]; } // LocalGet BinaryenIndex BinaryenLocalGetGetIndex(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenLocalGetGetIndex(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->index; } // LocalSet int BinaryenLocalSetIsTee(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenLocalSetIsTee(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->isTee(); } BinaryenIndex BinaryenLocalSetGetIndex(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenLocalSetGetIndex(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->index; } BinaryenExpressionRef BinaryenLocalSetGetValue(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenLocalSetGetValue(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->value; } // GlobalGet const char* BinaryenGlobalGetGetName(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenGlobalGetGetName(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->name.c_str(); } // GlobalSet const char* BinaryenGlobalSetGetName(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenGlobalSetGetName(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->name.c_str(); } BinaryenExpressionRef BinaryenGlobalSetGetValue(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenGlobalSetGetValue(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->value; } // Host BinaryenOp BinaryenHostGetOp(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenHostGetOp(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->op; } const char* BinaryenHostGetNameOperand(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenHostGetNameOperand(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->nameOperand.c_str(); } BinaryenIndex BinaryenHostGetNumOperands(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenHostGetNumOperands(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->operands.size(); } BinaryenExpressionRef BinaryenHostGetOperand(BinaryenExpressionRef expr, BinaryenIndex index) { if (tracing) { std::cout << " BinaryenHostGetOperand(expressions[" << expressions[expr] << "], " << index << ");\n"; } auto* expression = (Expression*)expr; assert(expression->is()); assert(index < static_cast(expression)->operands.size()); return static_cast(expression)->operands[index]; } // Load int BinaryenLoadIsAtomic(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenLoadIsAtomic(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->isAtomic; } int BinaryenLoadIsSigned(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenLoadIsSigned(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->signed_; } uint32_t BinaryenLoadGetBytes(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenLoadGetBytes(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->bytes; } uint32_t BinaryenLoadGetOffset(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenLoadGetOffset(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->offset; } uint32_t BinaryenLoadGetAlign(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenLoadGetAlign(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->align; } BinaryenExpressionRef BinaryenLoadGetPtr(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenLoadGetPtr(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->ptr; } // Store int BinaryenStoreIsAtomic(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenStoreIsAtomic(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->isAtomic; } uint32_t BinaryenStoreGetBytes(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenStoreGetBytes(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->bytes; } uint32_t BinaryenStoreGetOffset(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenStoreGetOffset(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->offset; } uint32_t BinaryenStoreGetAlign(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenStoreGetAlign(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->align; } BinaryenExpressionRef BinaryenStoreGetPtr(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenStoreGetPtr(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->ptr; } BinaryenExpressionRef BinaryenStoreGetValue(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenStoreGetValue(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->value; } // Const int32_t BinaryenConstGetValueI32(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenConstGetValueI32(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->value.geti32(); } int64_t BinaryenConstGetValueI64(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenConstGetValueI64(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->value.geti64(); } int32_t BinaryenConstGetValueI64Low(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenConstGetValueI64Low(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return (int32_t)(static_cast(expression)->value.geti64() & 0xffffffff); } int32_t BinaryenConstGetValueI64High(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenConstGetValueI64High(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return (int32_t)(static_cast(expression)->value.geti64() >> 32); } float BinaryenConstGetValueF32(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenConstGetValueF32(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->value.getf32(); } double BinaryenConstGetValueF64(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenConstGetValueF64(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->value.getf64(); } void BinaryenConstGetValueV128(BinaryenExpressionRef expr, uint8_t* out) { if (tracing) { std::cout << " BinaryenConstGetValueV128(expressions[" << expressions[expr] << "], " << out << ");\n"; } auto* expression = (Expression*)expr; assert(expression->is()); memcpy(out, static_cast(expression)->value.getv128().data(), 16); } // Unary BinaryenOp BinaryenUnaryGetOp(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenUnaryGetOp(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->op; } BinaryenExpressionRef BinaryenUnaryGetValue(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenUnaryGetValue(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->value; } // Binary BinaryenOp BinaryenBinaryGetOp(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenBinaryGetOp(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->op; } BinaryenExpressionRef BinaryenBinaryGetLeft(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenBinaryGetLeft(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->left; } BinaryenExpressionRef BinaryenBinaryGetRight(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenBinaryGetRight(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->right; } // Select BinaryenExpressionRef BinaryenSelectGetIfTrue(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenSelectGetIfTrue(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()); return static_cast(expression)->ifFalse; } BinaryenExpressionRef BinaryenSelectGetCondition(BinaryenExpressionRef expr) { if (tracing) { std::cout << " BinaryenSelectGetCondition(expressions[" << expressions[expr] << "]);\n"; } auto* expression = (Expression*)expr; assert(expression->is()) { return doVisitSelect(select); } else if (auto* unreachable = curr->dynCast()) { return doVisitUnreachable(unreachable); } else if (auto* drop = curr->dynCast()) { return doVisitDrop(drop); } else { return doVisitGeneric(curr); } } Node* doVisitBlock(Block* curr) { // TODO: handle super-deep nesting auto* oldParent = parent; expressionParentMap[curr] = oldParent; parent = curr; for (auto* child : curr->list) { visit(child); } // Merge the outputs // TODO handle conditions on these breaks if (curr->name.is()) { auto iter = breakStates.find(curr->name); if (iter != breakStates.end()) { auto& states = iter->second; // Add the state flowing out if (!isInUnreachable()) { states.push_back(locals); } mergeBlock(states, locals); } } parent = oldParent; return &bad; } Node* doVisitIf(If* curr) { auto* oldParent = parent; expressionParentMap[curr] = oldParent; parent = curr; // Set up the condition. Node* condition = visit(curr->condition); assert(condition); // Handle the contents. auto initialState = locals; visit(curr->ifTrue); auto afterIfTrueState = locals; if (curr->ifFalse) { locals = initialState; visit(curr->ifFalse); auto afterIfFalseState = locals; // TODO: optimize mergeIf(afterIfTrueState, afterIfFalseState, condition, curr, locals); } else { mergeIf(initialState, afterIfTrueState, condition, curr, locals); } parent = oldParent; return &bad; } Node* doVisitLoop(Loop* curr) { auto* oldParent = parent; expressionParentMap[curr] = oldParent; parent = curr; // As in Souper's LLVM extractor, we avoid loop phis, as we don't want // our traces to represent a value that differs across loop iterations. // For example, // %b = block // %x = phi %b, 1, %y // %y = phi %b, 2, %x // %z = eq %x %y // infer %z // Here %y refers to the previous iteration's %x. // To do this, we set all locals to a Var at the loop entry, then process // the inside of the loop. When that is done, we can see if a phi was // actually needed for each local. If it was, we leave the Var (it // represents an unknown value; analysis stops there), and if not, we // can replace the Var with the fixed value. // TODO: perhaps some more general uses of DataFlow will want loop phis? // TODO: optimize stuff here if (isInUnreachable()) { return &bad; // none of this matters } if (!curr->name.is()) { visit(curr->body); return &bad; // no phis are possible } auto previous = locals; auto numLocals = func->getNumLocals(); for (Index i = 0; i < numLocals; i++) { locals[i] = makeVar(func->getLocalType(i)); } auto vars = locals; // all the Vars we just created // We may need to replace values later - only new nodes added from // here are relevant. auto firstNodeFromLoop = nodes.size(); // Process the loop body. visit(curr->body); // Find all incoming paths. auto& breaks = breakStates[curr->name]; // Phis are possible, check for them. for (Index i = 0; i < numLocals; i++) { if (!isRelevantType(func->getLocalType(i))) { continue; } bool needPhi = false; // We replaced the proper value with a Var. If it's still that // Var - or it's the original proper value, which can happen with // constants - on all incoming paths, then a phi is not needed. auto* var = vars[i]; auto* proper = previous[i]; for (auto& other : breaks) { assert(!isInUnreachable(other)); auto& curr = *(other[i]); if (curr != *var && curr != *proper) { // A phi would be necessary here. needPhi = true; break; } } if (needPhi) { // Nothing to do - leave the Vars, the loop phis are // unknown values to us. } else { // Undo the Var for this local: In every new node added for // the loop body, replace references to the Var with the // previous value (the value that is all we need instead of a phi). for (auto j = firstNodeFromLoop; j < nodes.size(); j++) { for (auto*& value : nodes[j].get()->values) { if (value == var) { value = proper; } } } // Also undo in the current local state, which is flowing out // of the loop. for (auto*& node : locals) { if (node == var) { node = proper; } } } } return &bad; } Node* doVisitBreak(Break* curr) { if (!isInUnreachable()) { breakStates[curr->name].push_back(locals); } if (!curr->condition) { setInUnreachable(); } else { visit(curr->condition); } return &bad; } Node* doVisitSwitch(Switch* curr) { visit(curr->condition); if (!isInUnreachable()) { std::unordered_set targets; for (auto target : curr->targets) { targets.insert(target); } targets.insert(curr->default_); for (auto target : targets) { breakStates[target].push_back(locals); } } setInUnreachable(); return &bad; } Node* doVisitLocalGet(LocalGet* curr) { if (!isRelevantLocal(curr->index) || isInUnreachable()) { return &bad; } // We now know which IR node this get refers to auto* node = locals[curr->index]; return node; } Node* doVisitLocalSet(LocalSet* curr) { if (!isRelevantLocal(curr->index) || isInUnreachable()) { return &bad; } assert(curr->value->type.isConcrete()); sets.push_back(curr); expressionParentMap[curr] = parent; expressionParentMap[curr->value] = curr; // Set the current node in the local state. auto* node = visit(curr->value); locals[curr->index] = setNodeMap[curr] = node; // If we created a new node (and not just did a get of a set, which // passes around an existing node), mark its parent. if (nodeParentMap.find(node) == nodeParentMap.end()) { nodeParentMap[node] = curr; } return &bad; } Node* doVisitConst(Const* curr) { return makeConst(curr->value); } Node* doVisitUnary(Unary* curr) { // First, check if we support this op. switch (curr->op) { case ClzInt32: case ClzInt64: case CtzInt32: case CtzInt64: case PopcntInt32: case PopcntInt64: { // These are ok as-is. // Check if our child is supported. auto* value = expandFromI1(visit(curr->value), curr); if (value->isBad()) { return value; } // Great, we are supported! auto* ret = addNode(Node::makeExpr(curr, curr)); ret->addValue(value); return ret; } case EqZInt32: case EqZInt64: { // These can be implemented using a binary. // Check if our child is supported. auto* value = expandFromI1(visit(curr->value), curr); if (value->isBad()) { return value; } // Great, we are supported! return makeZeroComp(value, true, curr); } default: { // Anything else is an unknown value. return makeVar(curr->type); } } } Node* doVisitBinary(Binary* curr) { // First, check if we support this op. switch (curr->op) { case AddInt32: case AddInt64: case SubInt32: case SubInt64: case MulInt32: case MulInt64: case DivSInt32: case DivSInt64: case DivUInt32: case DivUInt64: case RemSInt32: case RemSInt64: case RemUInt32: case RemUInt64: case AndInt32: case AndInt64: case OrInt32: case OrInt64: case XorInt32: case XorInt64: case ShlInt32: case ShlInt64: case ShrUInt32: case ShrUInt64: case ShrSInt32: case ShrSInt64: case RotLInt32: case RotLInt64: case RotRInt32: case RotRInt64: case EqInt32: case EqInt64: case NeInt32: case NeInt64: case LtSInt32: case LtSInt64: case LtUInt32: case LtUInt64: case LeSInt32: case LeSInt64: case LeUInt32: case LeUInt64: { // These are ok as-is. // Check if our children are supported. auto* left = expandFromI1(visit(curr->left), curr); if (left->isBad()) { return left; } auto* right = expandFromI1(visit(curr->right), curr); if (right->isBad()) { return right; } // Great, we are supported! auto* ret = addNode(Node::makeExpr(curr, curr)); ret->addValue(left); ret->addValue(right); return ret; } case GtSInt32: case GtSInt64: case GeSInt32: case GeSInt64: case GtUInt32: case GtUInt64: case GeUInt32: case GeUInt64: { // These need to be flipped as Souper does not support redundant ops. Builder builder(*module); BinaryOp opposite; switch (curr->op) { case GtSInt32: opposite = LtSInt32; break; case GtSInt64: opposite = LtSInt64; break; case GeSInt32: opposite = LeSInt32; break; case GeSInt64: opposite = LeSInt64; break; case GtUInt32: opposite = LtUInt32; break; case GtUInt64: opposite = LtUInt64; break; case GeUInt32: opposite = LeUInt32; break; case GeUInt64: opposite = LeUInt64; break; default: WASM_UNREACHABLE("unexpected op"); } auto* ret = visitBinary(builder.makeBinary(opposite, curr->right, curr->left)); // We just created a new binary node, but we need to set the origin // properly to the original. ret->origin = curr; return ret; } default: { // Anything else is an unknown value. return makeVar(curr->type); } } } Node* doVisitSelect(Select* curr) { auto* ifTrue = expandFromI1(visit(curr->ifTrue), curr); if (ifTrue->isBad()) { return ifTrue; } auto* ifFalse = expandFromI1(visit(curr->ifFalse), curr); if (ifFalse->isBad()) { return ifFalse; } auto* condition = ensureI1(visit(curr->condition), curr); if (condition->isBad()) { return condition; } // Great, we are supported! auto* ret = addNode(Node::makeExpr(curr, curr)); ret->addValue(condition); ret->addValue(ifTrue); ret->addValue(ifFalse); return ret; } Node* doVisitUnreachable(Unreachable* curr) { setInUnreachable(); return &bad; } Node* doVisitDrop(Drop* curr) { visit(curr->value); // We need to know that the value's parent is a drop, indicating // the value is not actually used here. expressionParentMap[curr->value] = curr; return &bad; } Node* doVisitGeneric(Expression* curr) { // Just need to visit the nodes so we note all the gets for (auto* child : ChildIterator(curr)) { visit(child); } return makeVar(curr->type); } // Helpers. bool isRelevantType(wasm::Type type) { return type.isInteger(); } bool isRelevantLocal(Index index) { return isRelevantType(func->getLocalType(index)); } // Merge local state for an if, also creating a block and conditions. void mergeIf(Locals& aState, Locals& bState, Node* condition, Expression* expr, Locals& out) { // Create the conditions (if we can). Node* ifTrue; Node* ifFalse; if (!condition->isBad()) { // Generate boolean (i1 returning) conditions for the two branches. auto& conditions = expressionConditionMap[expr]; ifTrue = ensureI1(condition, nullptr); conditions.push_back(ifTrue); ifFalse = makeZeroComp(condition, true, nullptr); conditions.push_back(ifFalse); } else { ifTrue = ifFalse = &bad; } // Finally, merge the state with that block. TODO optimize std::vector states; if (!isInUnreachable(aState)) { states.emplace_back(aState, ifTrue); } if (!isInUnreachable(bState)) { states.emplace_back(bState, ifFalse); } merge(states, out); } // Merge local state for a block void mergeBlock(std::vector& localses, Locals& out) { // TODO: conditions std::vector states; for (auto& locals : localses) { states.emplace_back(locals, &bad); } merge(states, out); } // Merge local state for multiple control flow paths, creating phis as needed. void merge(std::vector& states, Locals& out) { // We should only receive reachable states. #ifndef NDEBUG for (auto& state : states) { assert(!isInUnreachable(state.locals)); } #endif Index numStates = states.size(); if (numStates == 0) { // We were unreachable, and still are. assert(isInUnreachable()); return; } // We may have just become reachable, if we were not before. setInReachable(); // Just one thing to merge is trivial. if (numStates == 1) { out = states[0].locals; return; } // We create a block if we need one. Index numLocals = func->getNumLocals(); Node* block = nullptr; for (Index i = 0; i < numLocals; i++) { if (!isRelevantType(func->getLocalType(i))) { continue; } // Process the inputs. If any is bad, the phi is bad. bool bad = false; for (auto& state : states) { auto* node = state.locals[i]; if (node->isBad()) { bad = true; out[i] = node; break; } } if (bad) { continue; } // Nothing is bad, proceed. Node* first = nullptr; for (auto& state : states) { if (!first) { first = out[i] = state.locals[i]; } else if (state.locals[i] != first) { // We need to actually merge some stuff. if (!block) { block = addNode(Node::makeBlock()); for (Index index = 0; index < numStates; index++) { auto* condition = states[index].condition; if (!condition->isBad()) { condition = addNode(Node::makeCond(block, index, condition)); } block->addValue(condition); } } auto* phi = addNode(Node::makePhi(block, i)); for (auto& state : states) { auto* value = expandFromI1(state.locals[i], nullptr); phi->addValue(value); } out[i] = phi; break; } } } } // If the node returns an i1, then we are called from a context that needs // to use it normally as in wasm - extend it Node* expandFromI1(Node* node, Expression* origin) { if (!node->isBad() && node->returnsI1()) { node = addNode(Node::makeZext(node, origin)); } return node; } Node* ensureI1(Node* node, Expression* origin) { if (!node->isBad() && !node->returnsI1()) { node = makeZeroComp(node, false, origin); } return node; } // Given a node representing something that is local.set'd, return // the set. LocalSet* getSet(Node* node) { auto iter = nodeParentMap.find(node); if (iter == nodeParentMap.end()) { return nullptr; } return iter->second->dynCast(); } // Given an expression, return the parent if such exists. Expression* getParent(Expression* curr) { auto iter = expressionParentMap.find(curr); if (iter == expressionParentMap.end()) { return nullptr; } return iter->second; } // Given an expression, return the set for it if such exists. LocalSet* getSet(Expression* curr) { auto* parent = getParent(curr); return parent ? parent->dynCast() : nullptr; } // Creates an expression that uses a node. Generally, a node represents // a value in a local, so we create a local.get for it. Expression* makeUse(Node* node) { Builder builder(*module); if (node->isPhi()) { // The index is the wasm local that we assign to when implementing // the phi; get from there. auto index = node->index; return builder.makeLocalGet(index, func->getLocalType(index)); } else if (node->isConst()) { return builder.makeConst(node->expr->cast()->value); } else if (node->isExpr()) { // Find the set we are a value of. auto index = getSet(node)->index; return builder.makeLocalGet(index, func->getLocalType(index)); } else if (node->isZext()) { // i1 zexts are a no-op for wasm return makeUse(node->values[0]); } else if (node->isVar()) { // Nothing valid for us to read here. Emit a call, representing an unknown // variable value. return Builder(*module).makeCall(FAKE_CALL, {}, node->wasmType); } else { WASM_UNREACHABLE("unexpected node type"); // TODO } } const Name FAKE_CALL = "fake$dfo$call"; }; } // namespace DataFlow } // namespace wasm #endif // wasm_dataflow_graph_h binaryen-version_91/src/dataflow/node.h000066400000000000000000000150311362402614000203450ustar00rootroot00000000000000/* * Copyright 2018 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // // DataFlow IR is an SSA representation. It can be built from the main // Binaryen IR. // // THe main initial use case was an IR that could easily be converted to // Souper IR, and the design favors that. // #ifndef wasm_dataflow_node_h #define wasm_dataflow_node_h #include "ir/utils.h" #include "wasm.h" namespace wasm { namespace DataFlow { // // The core IR representation in DataFlow: a Node. // // We reuse the Binaryen IR as much as possible: when things are identical // between the two IRs, we just create an Expr node, which stores the opcode and // other details, and we can emit them to Souper by reading the Binaryen // Expression. Other node types here are special things from Souper IR that we // can't represent that way. // // * Souper comparisons return an i1. We extend them immediately if they are // going to be used as i32s or i64s. // * When we use an Expression node, we just use its immediate fields, like the // op in a binary, alignment etc. in a load, etc. We don't look into the // pointers to child nodes. Instead, the DataFlow IR has its own pointers // directly to DataFlow children. In particular, this means that it's easy // to create an Expression with the info you need and not care about linking // it up to other Expressions. // struct Node { enum Type { Var, // an unknown variable number (not to be confused with var/param/local // in wasm) Expr, // a value represented by a Binaryen Expression Phi, // a phi from converging control flow Cond, // a blockpc, representing one of the branchs for a Block Block, // a source of phis Zext, // zero-extend an i1 (from an op where Souper returns i1 but wasm does // not, and so we need a special way to get back to an i32/i64 if we // operate on that value instead of just passing it straight to // Souper). Bad // something we can't handle and should ignore } type; Node(Type type) : type(type) {} // TODO: the others, if we need them bool isVar() { return type == Var; } bool isExpr() { return type == Expr; } bool isPhi() { return type == Phi; } bool isCond() { return type == Cond; } bool isBlock() { return type == Block; } bool isZext() { return type == Zext; } bool isBad() { return type == Bad; } bool isConst() { return type == Expr && expr->is(); } union { // For Var wasm::Type wasmType; // For Expr Expression* expr; // For Phi and Cond (the local index for phi, the block // index for cond) Index index; }; // The wasm expression that we originate from (if such exists). A single // wasm instruction may be turned into multiple dataflow IR nodes, and some // nodes have no wasm origin (like phis). Expression* origin = nullptr; // Extra list of related nodes. // For Expr, these are the Nodes for the inputs to the expression (e.g. // a binary would have 2 in this vector here). // For Phi, this is the block and then the list of values to pick from. // For Cond, this is the block and node. // For Block, this is the list of Conds. Note that that block does not // depend on them - the Phis do, but we store them in the block so that // we can avoid duplication. // For Zext, this is the value we extend. std::vector values; // Constructors static Node* makeVar(wasm::Type wasmType) { Node* ret = new Node(Var); ret->wasmType = wasmType; return ret; } static Node* makeExpr(Expression* expr, Expression* origin) { Node* ret = new Node(Expr); ret->expr = expr; ret->origin = origin; return ret; } static Node* makePhi(Node* block, Index index) { Node* ret = new Node(Phi); ret->addValue(block); ret->index = index; return ret; } static Node* makeCond(Node* block, Index index, Node* node) { Node* ret = new Node(Cond); ret->addValue(block); ret->index = index; ret->addValue(node); return ret; } static Node* makeBlock() { Node* ret = new Node(Block); return ret; } static Node* makeZext(Node* child, Expression* origin) { Node* ret = new Node(Zext); ret->addValue(child); ret->origin = origin; return ret; } static Node* makeBad() { Node* ret = new Node(Bad); return ret; } // Helpers void addValue(Node* value) { values.push_back(value); } Node* getValue(Index i) { return values.at(i); } // Gets the wasm type of the node. If there isn't a valid one, // return unreachable. wasm::Type getWasmType() { switch (type) { case Var: return wasmType; case Expr: return expr->type; case Phi: return getValue(1)->getWasmType(); case Zext: return getValue(0)->getWasmType(); case Bad: return wasm::Type::unreachable; default: WASM_UNREACHABLE("invalid node type"); } } bool operator==(const Node& other) { if (type != other.type) { return false; } switch (type) { case Var: case Block: return this == &other; case Expr: { if (!ExpressionAnalyzer::equal(expr, other.expr)) { return false; } break; } case Cond: if (index != other.index) { return false; } default: {} } if (values.size() != other.values.size()) { return false; } for (Index i = 0; i < values.size(); i++) { if (*(values[i]) != *(other.values[i])) { return false; } } return true; } bool operator!=(const Node& other) { return !(*this == other); } // As mentioned above, comparisons return i1. This checks // if an operation is of that sort. bool returnsI1() { if (isExpr()) { if (auto* binary = expr->dynCast()) { return binary->isRelational(); } else if (auto* unary = expr->dynCast()) { return unary->isRelational(); } } return false; } }; } // namespace DataFlow } // namespace wasm #endif // wasm_dataflow_node binaryen-version_91/src/dataflow/users.h000066400000000000000000000051171362402614000205650ustar00rootroot00000000000000/* * Copyright 2018 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // // DataFlow IR is an SSA representation. It can be built from the main // Binaryen IR. // // THe main initial use case was an IR that could easily be converted to // Souper IR, and the design favors that. // #ifndef wasm_dataflow_users_h #define wasm_dataflow_users_h #include "dataflow/graph.h" namespace wasm { namespace DataFlow { // Calculates the users of each node. // users[x] = { y, z, .. } // where y, z etc. are nodes that use x, that is, x is in their // values vector. class Users { typedef std::unordered_set UserSet; std::unordered_map users; public: void build(Graph& graph) { for (auto& node : graph.nodes) { for (auto* value : node->values) { users[value].insert(node.get()); } } } UserSet& getUsers(Node* node) { auto iter = users.find(node); if (iter == users.end()) { static UserSet empty; // FIXME thread_local? return empty; } return iter->second; } Index getNumUses(Node* node) { auto& users = getUsers(node); // A user may have more than one use Index numUses = 0; for (auto* user : users) { #ifndef NDEBUG bool found = false; #endif for (auto* value : user->values) { if (value == node) { numUses++; #ifndef NDEBUG found = true; #endif } } assert(found); } return numUses; } // Stops using all the values of this node. Called when a node is being // removed. void stopUsingValues(Node* node) { for (auto* value : node->values) { auto& users = getUsers(value); users.erase(node); } } // Adds a new user to a node. Called when we add or change a value of a node. void addUser(Node* node, Node* newUser) { users[node].insert(newUser); } // Remove all uses of a node. Called when a node is being removed. void removeAllUsesOf(Node* node) { users.erase(node); } }; } // namespace DataFlow } // namespace wasm #endif // wasm_dataflow_users binaryen-version_91/src/dataflow/utils.h000066400000000000000000000101151362402614000205560ustar00rootroot00000000000000/* * Copyright 2018 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // // DataFlow IR is an SSA representation. It can be built from the main // Binaryen IR. // // THe main initial use case was an IR that could easily be converted to // Souper IR, and the design favors that. // #ifndef wasm_dataflow_utils_h #define wasm_dataflow_utils_h #include "dataflow/node.h" #include "wasm-printing.h" #include "wasm.h" namespace wasm { namespace DataFlow { inline std::ostream& dump(Node* node, std::ostream& o, size_t indent = 0) { auto doIndent = [&]() { for (size_t i = 0; i < indent; i++) { o << ' '; } }; doIndent(); o << '[' << node << ' '; switch (node->type) { case Node::Type::Var: o << "var " << node->wasmType << ' ' << node; break; case Node::Type::Expr: { o << "expr "; WasmPrinter::printExpression(node->expr, o, true); break; } case Node::Type::Phi: o << "phi " << node->index; break; case Node::Type::Cond: o << "cond " << node->index; break; case Node::Type::Block: { // don't print the conds - they would recurse o << "block (" << node->values.size() << " conds)]\n"; return o; } case Node::Type::Zext: o << "zext"; break; case Node::Type::Bad: o << "bad"; break; } if (!node->values.empty()) { o << '\n'; for (auto* value : node->values) { dump(value, o, indent + 1); } doIndent(); } o << "] (origin: " << (void*)(node->origin) << ")\n"; return o; } inline std::ostream& dump(Graph& graph, std::ostream& o) { for (auto& node : graph.nodes) { o << "NODE " << node.get() << ": "; dump(node.get(), o); if (auto* set = graph.getSet(node.get())) { o << " and that is set to local " << set->index << '\n'; } } return o; } // Checks if the inputs are all identical - something we could // probably optimize. Returns false if irrelevant. inline bool allInputsIdentical(Node* node) { switch (node->type) { case Node::Type::Expr: { if (node->expr->is()) { return *(node->getValue(0)) == *(node->getValue(1)); } else if (node->expr->is()) { return node->getValue(0)->isConst() && node->getValue(1)->isConst() && node->getValue(2)->isConst(); } break; } case Node::Type::Phi: { // Check if any of the others are not equal for (Index i = 1; i < node->values.size(); i++) { if (!node->getValue(i)->isConst()) { return false; } } return true; } default: {} } return false; } } // namespace DataFlow } // namespace wasm #endif // wasm_dataflow_utils binaryen-version_91/src/emscripten-optimizer/000077500000000000000000000000001362402614000216375ustar00rootroot00000000000000binaryen-version_91/src/emscripten-optimizer/CMakeLists.txt000066400000000000000000000002411362402614000243740ustar00rootroot00000000000000set(emscripten-optimizer_SOURCES optimizer-shared.cpp parser.cpp simple_ast.cpp ) add_library(emscripten-optimizer OBJECT ${emscripten-optimizer_SOURCES}) binaryen-version_91/src/emscripten-optimizer/istring.h000066400000000000000000000130761362402614000234760ustar00rootroot00000000000000/* * Copyright 2015 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // Interned String type, 100% interned on creation. Comparisons are always just // a pointer comparison #ifndef wasm_istring_h #define wasm_istring_h #include #include #include #include #include #include #include #include #include "support/threads.h" #include "support/utilities.h" namespace cashew { struct IString { const char* str = nullptr; static size_t hash_c(const char* str) { // see http://www.cse.yorku.ca/~oz/hash.html unsigned int hash = 5381; int c; while ((c = *str++)) { hash = ((hash << 5) + hash) ^ c; } return (size_t)hash; } class CStringHash : public std::hash { public: size_t operator()(const char* str) const { return IString::hash_c(str); } }; class CStringEqual : public std::equal_to { public: bool operator()(const char* x, const char* y) const { return strcmp(x, y) == 0; } }; IString() = default; // if reuse=true, then input is assumed to remain alive; not copied IString(const char* s, bool reuse = true) { assert(s); set(s, reuse); } void set(const char* s, bool reuse = true) { typedef std::unordered_set StringSet; // one global store of strings per thread, we must not access this // in parallel thread_local static StringSet strings; auto existing = strings.find(s); if (existing == strings.end()) { // if the string isn't already known, we must use a single global // storage location, guarded by a mutex, so each string is allocated // exactly once static std::mutex mutex; std::unique_lock lock(mutex); // a single global set contains the actual strings, so we allocate each // one exactly once. static StringSet globalStrings; auto globalExisting = globalStrings.find(s); if (globalExisting == globalStrings.end()) { if (!reuse) { static std::vector> allocated; allocated.emplace_back(wasm::make_unique(s)); s = allocated.back()->c_str(); // we'll never modify it, so this is ok } // insert into global set globalStrings.insert(s); } else { s = *globalExisting; } // add the string to our thread-local set strings.insert(s); } else { s = *existing; } str = s; } void set(const IString& s) { str = s.str; } void clear() { str = nullptr; } bool operator==(const IString& other) const { // assert((str == other.str) == !strcmp(str, other.str)); return str == other.str; // fast! } bool operator!=(const IString& other) const { // assert((str == other.str) == !strcmp(str, other.str)); return str != other.str; // fast! } bool operator<(const IString& other) const { return strcmp(str ? str : "", other.str ? other.str : "") < 0; } char operator[](int x) const { return str[x]; } bool operator!() const { // no string, or empty string return !str || str[0] == 0; } const char* c_str() const { return str; } bool equals(const char* other) const { return !strcmp(str, other); } bool is() const { return str != nullptr; } bool isNull() const { return str == nullptr; } const char* stripPrefix(const char* prefix) const { const char* ptr = str; while (true) { if (*prefix == 0) { return ptr; } if (*ptr == 0) { return nullptr; } if (*ptr++ != *prefix++) { return nullptr; } } } bool startsWith(const char* prefix) const { return stripPrefix(prefix) != nullptr; } bool startsWith(const IString& prefix) const { return startsWith(prefix.str); } size_t size() const { return str ? strlen(str) : 0; } }; } // namespace cashew // Utilities for creating hashmaps/sets over IStrings namespace std { template<> struct hash { size_t operator()(const cashew::IString& str) const { return std::hash{}(size_t(str.str)); } }; template<> struct equal_to { bool operator()(const cashew::IString& x, const cashew::IString& y) const { return x == y; } }; } // namespace std namespace cashew { // IStringSet class IStringSet : public std::unordered_set { std::vector data; public: IStringSet() = default; IStringSet(const char* init) { // comma-delimited list int size = strlen(init) + 1; data.resize(size); char* curr = &data[0]; strncpy(curr, init, size); while (1) { char* end = strchr(curr, ' '); if (end) { *end = 0; } insert(curr); if (!end) { break; } curr = end + 1; } } bool has(const IString& str) { return count(str) > 0; } }; class IOrderedStringSet : public std::set { public: bool has(const IString& str) { return count(str) > 0; } }; } // namespace cashew #endif // wasm_istring_h binaryen-version_91/src/emscripten-optimizer/optimizer-shared.cpp000066400000000000000000000254311362402614000256360ustar00rootroot00000000000000/* * Copyright 2015 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "optimizer.h" #include "support/safe_integer.h" using namespace cashew; IString ASM_FLOAT_ZERO; IString SIMD_INT8X16_CHECK("SIMD_Int8x16_check"); IString SIMD_INT16X8_CHECK("SIMD_Int16x8_check"); IString SIMD_INT32X4_CHECK("SIMD_Int32x4_check"); IString SIMD_FLOAT32X4_CHECK("SIMD_Float32x4_check"); IString SIMD_FLOAT64X2_CHECK("SIMD_Float64x2_check"); IString TEMP_RET0("tempRet0"); int parseInt(const char* str) { int ret = *str - '0'; while (*(++str)) { ret *= 10; ret += *str - '0'; } return ret; } HeapInfo parseHeap(const char* name) { HeapInfo ret; if (name[0] != 'H' || name[1] != 'E' || name[2] != 'A' || name[3] != 'P') { ret.valid = false; return ret; } ret.valid = true; ret.unsign = name[4] == 'U'; ret.floaty = name[4] == 'F'; ret.bits = parseInt(name + (ret.unsign || ret.floaty ? 5 : 4)); ret.type = !ret.floaty ? ASM_INT : (ret.bits == 64 ? ASM_DOUBLE : ASM_FLOAT); return ret; } AsmType detectType(Ref node, AsmData* asmData, bool inVarDef, IString minifiedFround, bool allowI64) { if (node->isString()) { if (asmData) { AsmType ret = asmData->getType(node->getCString()); if (ret != ASM_NONE) { return ret; } } if (!inVarDef) { if (node == INF || node == NaN) { return ASM_DOUBLE; } if (node == TEMP_RET0) { return ASM_INT; } return ASM_NONE; } // We are in a variable definition, where Math_fround(0) optimized into a // global constant becomes f0 = Math_fround(0) if (ASM_FLOAT_ZERO.isNull()) { ASM_FLOAT_ZERO = node->getIString(); } else { assert(node == ASM_FLOAT_ZERO); } return ASM_FLOAT; } if (node->isNumber()) { if (!wasm::isInteger(node->getNumber())) { return ASM_DOUBLE; } return ASM_INT; } switch (node[0]->getCString()[0]) { case 'u': { if (node[0] == UNARY_PREFIX) { switch (node[1]->getCString()[0]) { case '+': return ASM_DOUBLE; case '-': return detectType( node[2], asmData, inVarDef, minifiedFround, allowI64); case '!': case '~': return ASM_INT; } break; } break; } case 'c': { if (node[0] == CALL) { if (node[1]->isString()) { IString name = node[1]->getIString(); if (name == MATH_FROUND || name == minifiedFround) { return ASM_FLOAT; } else if (allowI64 && (name == INT64 || name == INT64_CONST)) { return ASM_INT64; } else if (name == SIMD_FLOAT32X4 || name == SIMD_FLOAT32X4_CHECK) { return ASM_FLOAT32X4; } else if (name == SIMD_FLOAT64X2 || name == SIMD_FLOAT64X2_CHECK) { return ASM_FLOAT64X2; } else if (name == SIMD_INT8X16 || name == SIMD_INT8X16_CHECK) { return ASM_INT8X16; } else if (name == SIMD_INT16X8 || name == SIMD_INT16X8_CHECK) { return ASM_INT16X8; } else if (name == SIMD_INT32X4 || name == SIMD_INT32X4_CHECK) { return ASM_INT32X4; } } return ASM_NONE; } else if (node[0] == CONDITIONAL) { return detectType(node[2], asmData, inVarDef, minifiedFround, allowI64); } break; } case 'b': { if (node[0] == BINARY) { switch (node[1]->getCString()[0]) { case '+': case '-': case '*': case '/': case '%': return detectType( node[2], asmData, inVarDef, minifiedFround, allowI64); case '|': case '&': case '^': case '<': case '>': // handles <<, >>, >>=, <=, >= case '=': case '!': { // handles ==, != return ASM_INT; } } } break; } case 's': { if (node[0] == SEQ) { return detectType(node[2], asmData, inVarDef, minifiedFround, allowI64); } else if (node[0] == SUB) { assert(node[1]->isString()); HeapInfo info = parseHeap(node[1][1]->getCString()); if (info.valid) { return ASM_NONE; } return info.floaty ? ASM_DOUBLE : ASM_INT; // XXX ASM_FLOAT? } break; } } // dump("horrible", node); // assert(0); return ASM_NONE; } static void abort_on(Ref node) { node->stringify(std::cerr); std::cerr << '\n'; abort(); } AsmSign detectSign(Ref node, IString minifiedFround) { if (node->isString()) { return ASM_FLEXIBLE; } if (node->isNumber()) { double value = node->getNumber(); if (value < 0) { return ASM_SIGNED; } if (value > uint32_t(-1) || fmod(value, 1) != 0) { return ASM_NONSIGNED; } if (wasm::isSInteger32(value)) { return ASM_FLEXIBLE; } return ASM_UNSIGNED; } IString type = node[0]->getIString(); if (type == BINARY) { IString op = node[1]->getIString(); switch (op.str[0]) { case '>': { if (op == TRSHIFT) { return ASM_UNSIGNED; } } // fallthrough case '|': case '&': case '^': case '<': case '=': case '!': return ASM_SIGNED; case '+': case '-': return ASM_FLEXIBLE; case '*': case '/': case '%': return ASM_NONSIGNED; // without a coercion, these are double default: abort_on(node); } } else if (type == UNARY_PREFIX) { IString op = node[1]->getIString(); switch (op.str[0]) { case '-': return ASM_FLEXIBLE; case '+': return ASM_NONSIGNED; // XXX double case '~': return ASM_SIGNED; default: abort_on(node); } } else if (type == CONDITIONAL) { return detectSign(node[2], minifiedFround); } else if (type == CALL) { if (node[1]->isString() && (node[1] == MATH_FROUND || node[1] == minifiedFround)) { return ASM_NONSIGNED; } } else if (type == SEQ) { return detectSign(node[2], minifiedFround); } abort_on(node); abort(); // avoid warning } Ref makeAsmCoercedZero(AsmType type) { switch (type) { case ASM_INT: return ValueBuilder::makeNum(0); break; case ASM_DOUBLE: return ValueBuilder::makeUnary(PLUS, ValueBuilder::makeNum(0)); break; case ASM_FLOAT: { if (!ASM_FLOAT_ZERO.isNull()) { return ValueBuilder::makeName(ASM_FLOAT_ZERO); } else { return ValueBuilder::makeCall(MATH_FROUND, ValueBuilder::makeNum(0)); } break; } case ASM_FLOAT32X4: { return ValueBuilder::makeCall(SIMD_FLOAT32X4, ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0)); break; } case ASM_FLOAT64X2: { return ValueBuilder::makeCall( SIMD_FLOAT64X2, ValueBuilder::makeNum(0), ValueBuilder::makeNum(0)); break; } case ASM_INT8X16: { return ValueBuilder::makeCall(SIMD_INT8X16, ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0)); break; } case ASM_INT16X8: { return ValueBuilder::makeCall(SIMD_INT16X8, ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0)); break; } case ASM_INT32X4: { return ValueBuilder::makeCall(SIMD_INT32X4, ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0)); break; } default: assert(0); } abort(); } Ref makeAsmCoercion(Ref node, AsmType type) { switch (type) { case ASM_INT: return ValueBuilder::makeBinary(node, OR, ValueBuilder::makeNum(0)); case ASM_DOUBLE: return ValueBuilder::makeUnary(PLUS, node); case ASM_FLOAT: return ValueBuilder::makeCall(MATH_FROUND, node); case ASM_FLOAT32X4: return ValueBuilder::makeCall(SIMD_FLOAT32X4_CHECK, node); case ASM_FLOAT64X2: return ValueBuilder::makeCall(SIMD_FLOAT64X2_CHECK, node); case ASM_INT8X16: return ValueBuilder::makeCall(SIMD_INT8X16_CHECK, node); case ASM_INT16X8: return ValueBuilder::makeCall(SIMD_INT16X8_CHECK, node); case ASM_INT32X4: return ValueBuilder::makeCall(SIMD_INT32X4_CHECK, node); case ASM_NONE: default: // non-validating code, emit nothing XXX this is dangerous, we should only // allow this when we know we are not validating return node; } } Ref makeSigning(Ref node, AsmSign sign) { assert(sign == ASM_SIGNED || sign == ASM_UNSIGNED); return ValueBuilder::makeBinary( node, sign == ASM_SIGNED ? OR : TRSHIFT, ValueBuilder::makeNum(0)); } binaryen-version_91/src/emscripten-optimizer/optimizer.h000066400000000000000000000072331362402614000240370ustar00rootroot00000000000000/* * Copyright 2015 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_optimizer_h #define wasm_optimizer_h #include "simple_ast.h" extern bool preciseF32, receiveJSON, emitJSON, minifyWhitespace, last; extern cashew::Ref extraInfo; // enum AsmType { ASM_INT = 0, ASM_DOUBLE, ASM_FLOAT, ASM_FLOAT32X4, ASM_FLOAT64X2, ASM_INT8X16, ASM_INT16X8, ASM_INT32X4, ASM_INT64, // non-asm.js ASM_NONE // number of types }; struct AsmData; AsmType detectType(cashew::Ref node, AsmData* asmData = nullptr, bool inVarDef = false, cashew::IString minifiedFround = cashew::IString(), bool allowI64 = false); struct AsmData { struct Local { Local() = default; Local(AsmType type, bool param) : type(type), param(param) {} AsmType type; bool param; // false if a var }; typedef std::unordered_map Locals; Locals locals; std::vector params; // in order std::vector vars; // in order AsmType ret; cashew::Ref func; AsmType getType(const cashew::IString& name) { auto ret = locals.find(name); if (ret != locals.end()) { return ret->second.type; } return ASM_NONE; } void setType(const cashew::IString& name, AsmType type) { locals[name].type = type; } bool isLocal(const cashew::IString& name) { return locals.count(name) > 0; } bool isParam(const cashew::IString& name) { return isLocal(name) && locals[name].param; } bool isVar(const cashew::IString& name) { return isLocal(name) && !locals[name].param; } // if you want to fill in the data yourself AsmData() = default; // if you want to read data from f, and modify it as you go (parallel to // denormalize) AsmData(cashew::Ref f); void denormalize(); void addParam(cashew::IString name, AsmType type) { locals[name] = Local(type, true); params.push_back(name); } void addVar(cashew::IString name, AsmType type) { locals[name] = Local(type, false); vars.push_back(name); } void deleteVar(cashew::IString name) { locals.erase(name); for (size_t i = 0; i < vars.size(); i++) { if (vars[i] == name) { vars.erase(vars.begin() + i); break; } } } }; extern cashew::IString ASM_FLOAT_ZERO; extern cashew::IString SIMD_INT8X16_CHECK; extern cashew::IString SIMD_INT16X8_CHECK; extern cashew::IString SIMD_INT32X4_CHECK; extern cashew::IString SIMD_FLOAT32X4_CHECK; extern cashew::IString SIMD_FLOAT64X2_CHECK; int parseInt(const char* str); struct HeapInfo { bool valid, unsign, floaty; int bits; AsmType type; }; HeapInfo parseHeap(const char* name); enum AsmSign { // small constants can be signed or unsigned, variables are also flexible ASM_FLEXIBLE = 0, ASM_SIGNED = 1, ASM_UNSIGNED = 2, ASM_NONSIGNED = 3, }; extern AsmSign detectSign(cashew::Ref node, cashew::IString minifiedFround); cashew::Ref makeAsmCoercedZero(AsmType type); cashew::Ref makeAsmCoercion(cashew::Ref node, AsmType type); cashew::Ref makeSigning(cashew::Ref node, AsmSign sign); #endif // wasm_optimizer_h binaryen-version_91/src/emscripten-optimizer/parser.cpp000066400000000000000000000112601362402614000236370ustar00rootroot00000000000000/* * Copyright 2015 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "parser.h" namespace cashew { // common strings IString TOPLEVEL("toplevel"); IString DEFUN("defun"); IString BLOCK("block"); IString VAR("var"); IString CONST("const"); IString CONDITIONAL("conditional"); IString BINARY("binary"); IString RETURN("return"); IString IF("if"); IString ELSE("else"); IString WHILE("while"); IString DO("do"); IString FOR("for"); IString SEQ("seq"); IString SUB("sub"); IString CALL("call"); IString LABEL("label"); IString BREAK("break"); IString CONTINUE("continue"); IString SWITCH("switch"); IString STRING("string"); IString TRY("try"); IString INF("inf"); IString NaN("nan"); IString LLVM_CTTZ_I32("_llvm_cttz_i32"); IString UDIVMODDI4("___udivmoddi4"); IString UNARY_PREFIX("unary-prefix"); IString UNARY_POSTFIX("unary-postfix"); IString MATH_FROUND("Math_fround"); IString MATH_CLZ32("Math_clz32"); IString INT64("i64"); IString INT64_CONST("i64_const"); IString SIMD_FLOAT32X4("SIMD_Float32x4"); IString SIMD_FLOAT64X2("SIMD_Float64x2"); IString SIMD_INT8X16("SIMD_Int8x16"); IString SIMD_INT16X8("SIMD_Int16x8"); IString SIMD_INT32X4("SIMD_Int32x4"); IString PLUS("+"); IString MINUS("-"); IString OR("|"); IString AND("&"); IString XOR("^"); IString L_NOT("!"); IString B_NOT("~"); IString LT("<"); IString GE(">="); IString LE("<="); IString GT(">"); IString EQ("=="); IString NE("!="); IString DIV("/"); IString MOD("%"); IString MUL("*"); IString RSHIFT(">>"); IString LSHIFT("<<"); IString TRSHIFT(">>>"); IString HEAP8("HEAP8"); IString HEAP16("HEAP16"); IString HEAP32("HEAP32"); IString HEAPF32("HEAPF32"); IString HEAPU8("HEAPU8"); IString HEAPU16("HEAPU16"); IString HEAPU32("HEAPU32"); IString HEAPF64("HEAPF64"); IString F0("f0"); IString EMPTY(""); IString FUNCTION("function"); IString OPEN_PAREN("("); IString OPEN_BRACE("["); IString OPEN_CURLY("{"); IString CLOSE_CURLY("}"); IString COMMA(","); IString QUESTION("?"); IString COLON(":"); IString CASE("case"); IString DEFAULT("default"); IString DOT("dot"); IString PERIOD("."); IString NEW("new"); IString ARRAY("array"); IString OBJECT("object"); IString THROW("throw"); IString SET("="); IStringSet keywords("var const function if else do while for break continue return " "switch case default throw try catch finally true false null new"); const char *OPERATOR_INITS = "+-*/%<>&^|~=!,?:.", *SEPARATORS = "([;{}"; int MAX_OPERATOR_SIZE = 3; std::vector operatorClasses; static std::vector> precedences; // op, type => prec struct Init { Init() { // operators, rtl, type operatorClasses.emplace_back(".", false, OperatorClass::Binary); operatorClasses.emplace_back("! ~ + -", true, OperatorClass::Prefix); operatorClasses.emplace_back("* / %", false, OperatorClass::Binary); operatorClasses.emplace_back("+ -", false, OperatorClass::Binary); operatorClasses.emplace_back("<< >> >>>", false, OperatorClass::Binary); operatorClasses.emplace_back("< <= > >=", false, OperatorClass::Binary); operatorClasses.emplace_back("== !=", false, OperatorClass::Binary); operatorClasses.emplace_back("&", false, OperatorClass::Binary); operatorClasses.emplace_back("^", false, OperatorClass::Binary); operatorClasses.emplace_back("|", false, OperatorClass::Binary); operatorClasses.emplace_back("? :", true, OperatorClass::Tertiary); operatorClasses.emplace_back("=", true, OperatorClass::Binary); operatorClasses.emplace_back(",", true, OperatorClass::Binary); precedences.resize(OperatorClass::Tertiary + 1); for (size_t prec = 0; prec < operatorClasses.size(); prec++) { for (auto curr : operatorClasses[prec].ops) { precedences[operatorClasses[prec].type][curr] = prec; } } } }; Init init; int OperatorClass::getPrecedence(Type type, IString op) { return precedences[type][op]; } bool OperatorClass::getRtl(int prec) { return operatorClasses[prec].rtl; } bool isIdentInit(char x) { return (x >= 'a' && x <= 'z') || (x >= 'A' && x <= 'Z') || x == '_' || x == '$'; } bool isIdentPart(char x) { return isIdentInit(x) || (x >= '0' && x <= '9'); } } // namespace cashew binaryen-version_91/src/emscripten-optimizer/parser.h000066400000000000000000000744261362402614000233210ustar00rootroot00000000000000/* * Copyright 2015 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // Pure parsing. Calls methods on a Builder (template argument) to actually // construct the AST // // XXX All parsing methods assume they take ownership of the input string. This // lets them reuse parts of it. You will segfault if the input string cannot // be reused and written to. #ifndef wasm_parser_h #define wasm_parser_h #include #include #include #include #include #include "istring.h" #include "support/safe_integer.h" namespace cashew { // common strings extern IString TOPLEVEL; extern IString DEFUN; extern IString BLOCK; extern IString VAR; extern IString CONST; extern IString CONDITIONAL; extern IString BINARY; extern IString RETURN; extern IString IF; extern IString ELSE; extern IString WHILE; extern IString DO; extern IString FOR; extern IString SEQ; extern IString SUB; extern IString CALL; extern IString LABEL; extern IString BREAK; extern IString CONTINUE; extern IString SWITCH; extern IString STRING; extern IString TRY; extern IString INF; extern IString NaN; extern IString LLVM_CTTZ_I32; extern IString UDIVMODDI4; extern IString UNARY_PREFIX; extern IString UNARY_POSTFIX; extern IString MATH_FROUND; extern IString MATH_CLZ32; extern IString INT64; extern IString INT64_CONST; extern IString SIMD_FLOAT32X4; extern IString SIMD_FLOAT64X2; extern IString SIMD_INT8X16; extern IString SIMD_INT16X8; extern IString SIMD_INT32X4; extern IString PLUS; extern IString MINUS; extern IString OR; extern IString AND; extern IString XOR; extern IString L_NOT; extern IString B_NOT; extern IString LT; extern IString GE; extern IString LE; extern IString GT; extern IString EQ; extern IString NE; extern IString DIV; extern IString MOD; extern IString MUL; extern IString RSHIFT; extern IString LSHIFT; extern IString TRSHIFT; extern IString HEAP8; extern IString HEAP16; extern IString HEAP32; extern IString HEAPF32; extern IString HEAPU8; extern IString HEAPU16; extern IString HEAPU32; extern IString HEAPF64; extern IString F0; extern IString EMPTY; extern IString FUNCTION; extern IString OPEN_PAREN; extern IString OPEN_BRACE; extern IString OPEN_CURLY; extern IString CLOSE_CURLY; extern IString COMMA; extern IString QUESTION; extern IString COLON; extern IString CASE; extern IString DEFAULT; extern IString DOT; extern IString PERIOD; extern IString NEW; extern IString ARRAY; extern IString OBJECT; extern IString THROW; extern IString SET; extern IStringSet keywords; extern const char *OPERATOR_INITS, *SEPARATORS; extern int MAX_OPERATOR_SIZE, LOWEST_PREC; struct OperatorClass { enum Type { Binary = 0, Prefix = 1, Postfix = 2, Tertiary = 3 }; IStringSet ops; bool rtl; Type type; OperatorClass(const char* o, bool r, Type t) : ops(o), rtl(r), type(t) {} static int getPrecedence(Type type, IString op); static bool getRtl(int prec); }; extern std::vector operatorClasses; extern bool isIdentInit(char x); extern bool isIdentPart(char x); // parser template class Parser { static bool isSpace(char x) { return x == 32 || x == 9 || x == 10 || x == 13; } /* space, tab, linefeed/newline, or return */ static void skipSpace(char*& curr) { while (*curr) { if (isSpace(*curr)) { curr++; continue; } if (curr[0] == '/' && curr[1] == '/') { curr += 2; while (*curr && *curr != '\n') { curr++; } if (*curr) { curr++; } continue; } if (curr[0] == '/' && curr[1] == '*') { curr += 2; while (*curr && (curr[0] != '*' || curr[1] != '/')) { curr++; } curr += 2; continue; } return; } } static bool isDigit(char x) { return x >= '0' && x <= '9'; } static bool hasChar(const char* list, char x) { while (*list) { if (*list++ == x) { return true; } } return false; } // An atomic fragment of something. Stops at a natural boundary. enum FragType { KEYWORD = 0, OPERATOR = 1, IDENT = 2, STRING = 3, // without quotes INT = 4, DOUBLE = 5, SEPARATOR = 6 }; struct Frag { // MSVC does not allow unrestricted unions: // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2544.pdf #ifndef _MSC_VER union { #endif IString str; double num; #ifndef _MSC_VER }; #endif int size; FragType type; bool isNumber() const { return type == INT || type == DOUBLE; } explicit Frag(char* src) { char* start = src; if (isIdentInit(*src)) { // read an identifier or a keyword src++; while (isIdentPart(*src)) { src++; } if (*src == 0) { str.set(start); } else { char temp = *src; *src = 0; str.set(start, false); *src = temp; } type = keywords.has(str) ? KEYWORD : IDENT; } else if (isDigit(*src) || (src[0] == '.' && isDigit(src[1]))) { if (src[0] == '0' && (src[1] == 'x' || src[1] == 'X')) { // Explicitly parse hex numbers of form "0x...", because strtod // supports hex number strings only in C++11, and Visual Studio 2013 // does not yet support that functionality. src += 2; num = 0; while (1) { if (*src >= '0' && *src <= '9') { num *= 16; num += *src - '0'; } else if (*src >= 'a' && *src <= 'f') { num *= 16; num += *src - 'a' + 10; } else if (*src >= 'A' && *src <= 'F') { num *= 16; num += *src - 'A' + 10; } else { break; } src++; } } else { num = strtod(start, &src); } // asm.js must have a '.' for double values. however, we also tolerate // uglify's tendency to emit without a '.' (and fix it later with a +). // for valid asm.js input, the '.' should be enough, and for uglify // in the emscripten optimizer pipeline, we use simple_ast where // INT/DOUBLE is quite the same at this point anyhow type = (std::find(start, src, '.') == src && (wasm::isSInteger32(num) || wasm::isUInteger32(num))) ? INT : DOUBLE; assert(src > start); } else if (hasChar(OPERATOR_INITS, *src)) { switch (*src) { case '!': str = src[1] == '=' ? NE : L_NOT; break; case '%': str = MOD; break; case '&': str = AND; break; case '*': str = MUL; break; case '+': str = PLUS; break; case ',': str = COMMA; break; case '-': str = MINUS; break; case '.': str = PERIOD; break; case '/': str = DIV; break; case ':': str = COLON; break; case '<': str = src[1] == '<' ? LSHIFT : (src[1] == '=' ? LE : LT); break; case '=': str = src[1] == '=' ? EQ : SET; break; case '>': str = src[1] == '>' ? (src[2] == '>' ? TRSHIFT : RSHIFT) : (src[1] == '=' ? GE : GT); break; case '?': str = QUESTION; break; case '^': str = XOR; break; case '|': str = OR; break; case '~': str = B_NOT; break; default: abort(); } size = strlen(str.str); #ifndef NDEBUG char temp = start[size]; start[size] = 0; assert(strcmp(str.str, start) == 0); start[size] = temp; #endif type = OPERATOR; return; } else if (hasChar(SEPARATORS, *src)) { type = SEPARATOR; char temp = src[1]; src[1] = 0; str.set(src, false); src[1] = temp; src++; } else if (*src == '"' || *src == '\'') { char* end = strchr(src + 1, *src); *end = 0; str.set(src + 1); src = end + 1; type = STRING; } else { dump("frag parsing", src); abort(); } size = src - start; } }; struct ExpressionElement { bool isNode; // MSVC does not allow unrestricted unions: // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2544.pdf #ifndef _MSC_VER union { #endif NodeRef node; IString op; #ifndef _MSC_VER }; #endif ExpressionElement(NodeRef n) : isNode(true), node(n) {} ExpressionElement(IString o) : isNode(false), op(o) {} NodeRef getNode() { assert(isNode); return node; } IString getOp() { assert(!isNode); return op; } }; // This is a list of the current stack of node-operator-node-operator-etc. // this works by each parseExpression call appending to the vector; then // recursing out, and the toplevel sorts it all typedef std::vector ExpressionParts; std::vector expressionPartsStack; // Parses an element in a list of such elements, e.g. list of statements in a // block, or list of parameters in a call NodeRef parseElement(char*& src, const char* seps = ";") { // dump("parseElement", src); skipSpace(src); Frag frag(src); src += frag.size; switch (frag.type) { case KEYWORD: { return parseAfterKeyword(frag, src, seps); } case IDENT: { return parseAfterIdent(frag, src, seps); } case STRING: case INT: case DOUBLE: { return parseExpression(parseFrag(frag), src, seps); } case SEPARATOR: { if (frag.str == OPEN_PAREN) { return parseExpression(parseAfterParen(src), src, seps); } if (frag.str == OPEN_BRACE) { return parseExpression(parseAfterBrace(src), src, seps); } if (frag.str == OPEN_CURLY) { return parseExpression(parseAfterCurly(src), src, seps); } abort(); } case OPERATOR: { return parseExpression(frag.str, src, seps); } default: /* dump("parseElement", src); printf("bad frag type: %d\n",frag.type); */ abort(); } return nullptr; } NodeRef parseFrag(Frag& frag) { switch (frag.type) { case IDENT: return Builder::makeName(frag.str); case STRING: return Builder::makeString(frag.str); case INT: return Builder::makeInt(uint32_t(frag.num)); case DOUBLE: return Builder::makeDouble(frag.num); default: abort(); } return nullptr; } NodeRef parseAfterKeyword(Frag& frag, char*& src, const char* seps) { skipSpace(src); if (frag.str == FUNCTION) { return parseFunction(src, seps); } else if (frag.str == VAR) { return parseVar(src, seps, false); } else if (frag.str == CONST) { return parseVar(src, seps, true); } else if (frag.str == RETURN) { return parseReturn(src, seps); } else if (frag.str == IF) { return parseIf(src, seps); } else if (frag.str == DO) { return parseDo(src, seps); } else if (frag.str == WHILE) { return parseWhile(src, seps); } else if (frag.str == BREAK) { return parseBreak(src, seps); } else if (frag.str == CONTINUE) { return parseContinue(src, seps); } else if (frag.str == SWITCH) { return parseSwitch(src, seps); } else if (frag.str == NEW) { return parseNew(src, seps); } else if (frag.str == FOR) { return parseFor(src, seps); } dump(frag.str.str, src); abort(); return nullptr; } NodeRef parseFunction(char*& src, const char* seps) { Frag name(src); if (name.type == IDENT) { src += name.size; } else { assert(name.type == SEPARATOR && name.str[0] == '('); name.str = IString(); } NodeRef ret = Builder::makeFunction(name.str); skipSpace(src); assert(*src == '('); src++; while (1) { skipSpace(src); if (*src == ')') { break; } Frag arg(src); assert(arg.type == IDENT); src += arg.size; Builder::appendArgumentToFunction(ret, arg.str); skipSpace(src); if (*src == ')') { break; } if (*src == ',') { src++; continue; } abort(); } src++; Builder::setBlockContent(ret, parseBracketedBlock(src)); // TODO: parse expression? return ret; } NodeRef parseVar(char*& src, const char* seps, bool is_const) { NodeRef ret = Builder::makeVar(is_const); while (1) { skipSpace(src); if (*src == ';') { break; } Frag name(src); assert(name.type == IDENT); NodeRef value; src += name.size; skipSpace(src); if (*src == '=') { src++; skipSpace(src); value = parseElement(src, ";,"); } Builder::appendToVar(ret, name.str, value); skipSpace(src); if (*src == ';') { break; } if (*src == ',') { src++; continue; } abort(); } src++; return ret; } NodeRef parseReturn(char*& src, const char* seps) { skipSpace(src); NodeRef value = !hasChar(seps, *src) ? parseElement(src, seps) : nullptr; skipSpace(src); assert(hasChar(seps, *src)); if (*src == ';') { src++; } return Builder::makeReturn(value); } NodeRef parseIf(char*& src, const char* seps) { NodeRef condition = parseParenned(src); NodeRef ifTrue = parseMaybeBracketed(src, seps); skipSpace(src); NodeRef ifFalse; if (!hasChar(seps, *src)) { Frag next(src); if (next.type == KEYWORD && next.str == ELSE) { src += next.size; ifFalse = parseMaybeBracketed(src, seps); } } return Builder::makeIf(condition, ifTrue, ifFalse); } NodeRef parseDo(char*& src, const char* seps) { NodeRef body = parseMaybeBracketed(src, seps); skipSpace(src); Frag next(src); assert(next.type == KEYWORD && next.str == WHILE); src += next.size; NodeRef condition = parseParenned(src); return Builder::makeDo(body, condition); } NodeRef parseWhile(char*& src, const char* seps) { NodeRef condition = parseParenned(src); NodeRef body = parseMaybeBracketed(src, seps); return Builder::makeWhile(condition, body); } NodeRef parseFor(char*& src, const char* seps) { skipSpace(src); assert(*src == '('); src++; NodeRef init = parseElement(src, ";"); skipSpace(src); assert(*src == ';'); src++; NodeRef condition = parseElement(src, ";"); skipSpace(src); assert(*src == ';'); src++; NodeRef inc = parseElement(src, ")"); skipSpace(src); assert(*src == ')'); src++; NodeRef body = parseMaybeBracketed(src, seps); return Builder::makeFor(init, condition, inc, body); } NodeRef parseBreak(char*& src, const char* seps) { skipSpace(src); Frag next(src); if (next.type == IDENT) { src += next.size; } return Builder::makeBreak(next.type == IDENT ? next.str : IString()); } NodeRef parseContinue(char*& src, const char* seps) { skipSpace(src); Frag next(src); if (next.type == IDENT) { src += next.size; } return Builder::makeContinue(next.type == IDENT ? next.str : IString()); } NodeRef parseSwitch(char*& src, const char* seps) { NodeRef ret = Builder::makeSwitch(parseParenned(src)); skipSpace(src); assert(*src == '{'); src++; while (1) { // find all cases and possibly a default skipSpace(src); if (*src == '}') { break; } Frag next(src); if (next.type == KEYWORD) { if (next.str == CASE) { src += next.size; skipSpace(src); NodeRef arg; Frag value(src); if (value.isNumber()) { arg = parseFrag(value); src += value.size; } else if (value.type == OPERATOR) { // negative number assert(value.str == MINUS); src += value.size; skipSpace(src); Frag value2(src); assert(value2.isNumber()); arg = Builder::makePrefix(MINUS, parseFrag(value2)); src += value2.size; } else { // identifier and function call assert(value.type == IDENT); src += value.size; skipSpace(src); arg = parseCall(parseFrag(value), src); } Builder::appendCaseToSwitch(ret, arg); skipSpace(src); assert(*src == ':'); src++; continue; } else if (next.str == DEFAULT) { src += next.size; Builder::appendDefaultToSwitch(ret); skipSpace(src); assert(*src == ':'); src++; continue; } // otherwise, may be some keyword that happens to start a block (e.g. // case 1: _return_ 5) } // not case X: or default: or }, so must be some code skipSpace(src); bool explicitBlock = *src == '{'; NodeRef subBlock = explicitBlock ? parseBracketedBlock(src) : parseBlock(src, ";}", CASE, DEFAULT); Builder::appendCodeToSwitch(ret, subBlock, explicitBlock); } skipSpace(src); assert(*src == '}'); src++; return ret; } NodeRef parseNew(char*& src, const char* seps) { return Builder::makeNew(parseElement(src, seps)); } NodeRef parseAfterIdent(Frag& frag, char*& src, const char* seps) { skipSpace(src); if (*src == '(') { return parseExpression(parseCall(parseFrag(frag), src), src, seps); } if (*src == '[') { return parseExpression(parseIndexing(parseFrag(frag), src), src, seps); } if (*src == ':' && expressionPartsStack.back().size() == 0) { src++; skipSpace(src); NodeRef inner; if (*src == '{') { // context lets us know this is not an object, but a block inner = parseBracketedBlock(src); } else { inner = parseElement(src, seps); } return Builder::makeLabel(frag.str, inner); } if (*src == '.') { return parseExpression(parseDotting(parseFrag(frag), src), src, seps); } return parseExpression(parseFrag(frag), src, seps); } NodeRef parseCall(NodeRef target, char*& src) { expressionPartsStack.resize(expressionPartsStack.size() + 1); assert(*src == '('); src++; NodeRef ret = Builder::makeCall(target); while (1) { skipSpace(src); if (*src == ')') { break; } Builder::appendToCall(ret, parseElement(src, ",)")); skipSpace(src); if (*src == ')') { break; } if (*src == ',') { src++; continue; } abort(); } src++; assert(expressionPartsStack.back().size() == 0); expressionPartsStack.pop_back(); return ret; } NodeRef parseIndexing(NodeRef target, char*& src) { expressionPartsStack.resize(expressionPartsStack.size() + 1); assert(*src == '['); src++; NodeRef ret = Builder::makeIndexing(target, parseElement(src, "]")); skipSpace(src); assert(*src == ']'); src++; assert(expressionPartsStack.back().size() == 0); expressionPartsStack.pop_back(); return ret; } NodeRef parseDotting(NodeRef target, char*& src) { assert(*src == '.'); src++; Frag key(src); assert(key.type == IDENT); src += key.size; return Builder::makeDot(target, key.str); } NodeRef parseAfterParen(char*& src) { expressionPartsStack.resize(expressionPartsStack.size() + 1); skipSpace(src); NodeRef ret = parseElement(src, ")"); skipSpace(src); assert(*src == ')'); src++; assert(expressionPartsStack.back().size() == 0); expressionPartsStack.pop_back(); return ret; } NodeRef parseAfterBrace(char*& src) { expressionPartsStack.resize(expressionPartsStack.size() + 1); NodeRef ret = Builder::makeArray(); while (1) { skipSpace(src); assert(*src); if (*src == ']') { break; } NodeRef element = parseElement(src, ",]"); Builder::appendToArray(ret, element); skipSpace(src); if (*src == ']') { break; } if (*src == ',') { src++; continue; } abort(); } src++; return ret; } NodeRef parseAfterCurly(char*& src) { expressionPartsStack.resize(expressionPartsStack.size() + 1); NodeRef ret = Builder::makeObject(); while (1) { skipSpace(src); assert(*src); if (*src == '}') { break; } Frag key(src); assert(key.type == IDENT || key.type == STRING); src += key.size; skipSpace(src); assert(*src == ':'); src++; NodeRef value = parseElement(src, ",}"); Builder::appendToObject(ret, key.str, value); skipSpace(src); if (*src == '}') { break; } if (*src == ',') { src++; continue; } abort(); } src++; return ret; } void dumpParts(ExpressionParts& parts, int i) { printf("expressionparts: %d (at %d)\n", parts.size(), i); printf("| "); for (int i = 0; i < parts.size(); i++) { if (parts[i].isNode) { parts[i].getNode()->stringify(std::cout); printf(" "); } else { printf(" _%s_ ", parts[i].getOp().str); } } printf("|\n"); } NodeRef makeBinary(NodeRef left, IString op, NodeRef right) { if (op == PERIOD) { return Builder::makeDot(left, right); } else { return Builder::makeBinary(left, op, right); } } NodeRef parseExpression(ExpressionElement initial, char*& src, const char* seps) { // dump("parseExpression", src); ExpressionParts& parts = expressionPartsStack.back(); skipSpace(src); if (*src == 0 || hasChar(seps, *src)) { if (parts.size() > 0) { parts.push_back(initial); // cherry on top of the cake } return initial.getNode(); } bool top = parts.size() == 0; if (initial.isNode) { Frag next(src); if (next.type == OPERATOR) { parts.push_back(initial); src += next.size; parts.push_back(next.str); } else { if (*src == '(') { initial = parseCall(initial.getNode(), src); } else if (*src == '[') { initial = parseIndexing(initial.getNode(), src); } else { dump("bad parseExpression state", src); abort(); } return parseExpression(initial, src, seps); } } else { parts.push_back(initial); } NodeRef last = parseElement(src, seps); if (!top) { return last; } { // |parts| may have been invalidated by that call ExpressionParts& parts = expressionPartsStack.back(); // we are the toplevel. sort it all out // collapse right to left, highest priority first // dumpParts(parts, 0); for (auto& ops : operatorClasses) { if (ops.rtl) { // right to left for (int i = parts.size() - 1; i >= 0; i--) { if (parts[i].isNode) { continue; } IString op = parts[i].getOp(); if (!ops.ops.has(op)) { continue; } if (ops.type == OperatorClass::Binary && i > 0 && i < (int)parts.size() - 1) { parts[i] = makeBinary(parts[i - 1].getNode(), op, parts[i + 1].getNode()); parts.erase(parts.begin() + i + 1); parts.erase(parts.begin() + i - 1); } else if (ops.type == OperatorClass::Prefix && i < (int)parts.size() - 1) { if (i > 0 && parts[i - 1].isNode) { // cannot apply prefix operator if it would join two nodes continue; } parts[i] = Builder::makePrefix(op, parts[i + 1].getNode()); parts.erase(parts.begin() + i + 1); } else if (ops.type == OperatorClass::Tertiary) { // we must be at X ? Y : Z // ^ // dumpParts(parts, i); if (op != COLON) { continue; } assert(i < (int)parts.size() - 1 && i >= 3); if (parts[i - 2].getOp() != QUESTION) { continue; // e.g. x ? y ? 1 : 0 : 2 } parts[i - 3] = Builder::makeConditional(parts[i - 3].getNode(), parts[i - 1].getNode(), parts[i + 1].getNode()); parts.erase(parts.begin() + i - 2, parts.begin() + i + 2); // basically a reset, due to things like x ? y ? 1 : 0 : 2 i = parts.size(); } // TODO: postfix } } else { // left to right for (int i = 0; i < (int)parts.size(); i++) { if (parts[i].isNode) { continue; } IString op = parts[i].getOp(); if (!ops.ops.has(op)) { continue; } if (ops.type == OperatorClass::Binary && i > 0 && i < (int)parts.size() - 1) { parts[i] = makeBinary(parts[i - 1].getNode(), op, parts[i + 1].getNode()); parts.erase(parts.begin() + i + 1); parts.erase(parts.begin() + i - 1); i--; } else if (ops.type == OperatorClass::Prefix && i < (int)parts.size() - 1) { if (i > 0 && parts[i - 1].isNode) { // cannot apply prefix operator if it would join two nodes continue; } parts[i] = Builder::makePrefix(op, parts[i + 1].getNode()); parts.erase(parts.begin() + i + 1); // allow a previous prefix operator to cascade i = std::max(i - 2, 0); } // TODO: tertiary, postfix } } } assert(parts.size() == 1); NodeRef ret = parts[0].getNode(); parts.clear(); return ret; } } // Parses a block of code (e.g. a bunch of statements inside {,}, or the top // level of o file) NodeRef parseBlock(char*& src, const char* seps = ";", IString keywordSep1 = IString(), IString keywordSep2 = IString()) { NodeRef block = Builder::makeBlock(); // dump("parseBlock", src); while (1) { skipSpace(src); if (*src == 0) { break; } if (*src == ';') { src++; // skip a statement in this block continue; } if (hasChar(seps, *src)) { break; } if (!!keywordSep1) { Frag next(src); if (next.type == KEYWORD && next.str == keywordSep1) { break; } } if (!!keywordSep2) { Frag next(src); if (next.type == KEYWORD && next.str == keywordSep2) { break; } } NodeRef element = parseElementOrStatement(src, seps); Builder::appendToBlock(block, element); } return block; } NodeRef parseBracketedBlock(char*& src) { skipSpace(src); assert(*src == '{'); src++; // the two are not symmetrical, ; is just internally separating, } is the // final one - parseBlock knows all this NodeRef block = parseBlock(src, ";}"); assert(*src == '}'); src++; return block; } NodeRef parseElementOrStatement(char*& src, const char* seps) { skipSpace(src); if (*src == ';') { src++; // we don't need the brackets here, but oh well return Builder::makeBlock(); } if (*src == '{') { // detect a trivial {} in a statement context char* before = src; src++; skipSpace(src); if (*src == '}') { src++; // we don't need the brackets here, but oh well return Builder::makeBlock(); } src = before; } NodeRef ret = parseElement(src, seps); skipSpace(src); if (*src == ';') { ret = Builder::makeStatement(ret); src++; } return ret; } NodeRef parseMaybeBracketed(char*& src, const char* seps) { skipSpace(src); return *src == '{' ? parseBracketedBlock(src) : parseElementOrStatement(src, seps); } NodeRef parseParenned(char*& src) { skipSpace(src); assert(*src == '('); src++; NodeRef ret = parseElement(src, ")"); skipSpace(src); assert(*src == ')'); src++; return ret; } // Debugging char* allSource = nullptr; int allSize = 0; static void dump(const char* where, char* curr) { /* printf("%s:\n=============\n", where); for (int i = 0; i < allSize; i++) printf("%c", allSource[i] ? allSource[i] : '?'); printf("\n"); for (int i = 0; i < (curr - allSource); i++) printf(" "); printf("^\n=============\n"); */ fprintf(stderr, "%s:\n==========\n", where); int newlinesLeft = 2; int charsLeft = 200; while (*curr) { if (*curr == '\n') { newlinesLeft--; if (newlinesLeft == 0) { break; } } charsLeft--; if (charsLeft == 0) { break; } fprintf(stderr, "%c", *curr++); } fprintf(stderr, "\n\n"); } public: Parser() { expressionPartsStack.resize(1); } // Highest-level parsing, as of a JavaScript script file. NodeRef parseToplevel(char* src) { allSource = src; allSize = strlen(src); NodeRef toplevel = Builder::makeToplevel(); Builder::setBlockContent(toplevel, parseBlock(src)); return toplevel; } }; } // namespace cashew #endif // wasm_parser_h binaryen-version_91/src/emscripten-optimizer/simple_ast.cpp000066400000000000000000000105151362402614000245050ustar00rootroot00000000000000/* * Copyright 2015 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "simple_ast.h" namespace cashew { // Ref methods Ref& Ref::operator[](unsigned x) { return (*get())[x]; } Ref& Ref::operator[](IString x) { return (*get())[x]; } bool Ref::operator==(const char* str) { return get()->isString() && !strcmp(get()->str.str, str); } bool Ref::operator!=(const char* str) { return get()->isString() ? !!strcmp(get()->str.str, str) : true; } bool Ref::operator==(const IString& str) { return get()->isString() && get()->str == str; } bool Ref::operator!=(const IString& str) { return get()->isString() && get()->str != str; } bool Ref::operator==(Ref other) { return **this == *other; } bool Ref::operator!() { return !get() || get()->isNull(); } // Arena GlobalMixedArena arena; // Value Value& Value::setAssign(Ref target, Ref value) { asAssign()->target() = target; asAssign()->value() = value; return *this; } Value& Value::setAssignName(IString target, Ref value) { asAssignName()->target() = target; asAssignName()->value() = value; return *this; } Assign* Value::asAssign() { assert(isAssign()); return static_cast(this); } AssignName* Value::asAssignName() { assert(isAssignName()); return static_cast(this); } void Value::stringify(std::ostream& os, bool pretty) { static int indent = 0; #define indentify() \ { \ for (int i_ = 0; i_ < indent; i_++) \ os << " "; \ } switch (type) { case String: { if (str.str) { os << '"' << str.str << '"'; } else { os << "\"(null)\""; } break; } case Number: { // doubles can have 17 digits of precision os << std::setprecision(17) << num; break; } case Array: { if (arr->size() == 0) { os << "[]"; break; } os << '['; if (pretty) { os << std::endl; indent++; } for (size_t i = 0; i < arr->size(); i++) { if (i > 0) { if (pretty) { os << "," << std::endl; } else { os << ", "; } } indentify(); (*arr)[i]->stringify(os, pretty); } if (pretty) { os << std::endl; indent--; } indentify(); os << ']'; break; } case Null: { os << "null"; break; } case Bool: { os << (boo ? "true" : "false"); break; } case Object: { os << '{'; if (pretty) { os << std::endl; indent++; } bool first = true; for (auto i : *obj) { if (first) { first = false; } else { os << ", "; if (pretty) { os << std::endl; } } indentify(); os << '"' << i.first.c_str() << "\": "; i.second->stringify(os, pretty); } if (pretty) { os << std::endl; indent--; } indentify(); os << '}'; break; } case Assign_: { os << "["; ref->stringify(os, pretty); os << ", "; asAssign()->value()->stringify(os, pretty); os << "]"; break; } case AssignName_: { os << "[\"" << asAssignName()->target().str << "\""; os << ", "; asAssignName()->value()->stringify(os, pretty); os << "]"; break; } } } // dump void dump(const char* str, Ref node, bool pretty) { std::cerr << str << ": "; if (!!node) { node->stringify(std::cerr, pretty); } else { std::cerr << "(nullptr)"; } std::cerr << std::endl; } } // namespace cashew binaryen-version_91/src/emscripten-optimizer/simple_ast.h000066400000000000000000001306421362402614000241560ustar00rootroot00000000000000/* * Copyright 2015 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_simple_ast_h #define wasm_simple_ast_h #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mixed_arena.h" #include "parser.h" #include "snprintf.h" #include "support/safe_integer.h" #define err(str) fprintf(stderr, str "\n"); #define errv(str, ...) fprintf(stderr, str "\n", __VA_ARGS__); #define printErr err namespace cashew { struct Value; struct Ref; void dump(const char* str, Ref node, bool pretty = false); // Reference to a value, plus some operators for convenience struct Ref { Value* inst; Ref(Value* v = nullptr) : inst(v) {} Value* get() { return inst; } Value& operator*() { return *inst; } Value* operator->() { return inst; } Ref& operator[](unsigned x); Ref& operator[](IString x); // special conveniences bool operator==(const char* str); // comparison to string, which is by value bool operator!=(const char* str); bool operator==(const IString& str); bool operator!=(const IString& str); // prevent Ref == number, which is potentially ambiguous; use ->getNumber() == // number bool operator==(double d) { abort(); return false; } bool operator==(Ref other); bool operator!(); // check if null, in effect }; // Arena allocation, free it all on process exit // A mixed arena for global allocation only, so members do not // receive an allocator, they all use the global one anyhow class GlobalMixedArena : public MixedArena { public: template T* alloc() { auto* ret = static_cast(allocSpace(sizeof(T), alignof(T))); new (ret) T(); return ret; } }; extern GlobalMixedArena arena; class ArrayStorage : public ArenaVectorBase { public: void allocate(size_t size) { allocatedElements = size; data = static_cast( arena.allocSpace(sizeof(Ref) * allocatedElements, alignof(Ref))); } }; struct Assign; struct AssignName; // Main value type struct Value { enum Type { String = 0, Number = 1, Array = 2, Null = 3, Bool = 4, Object = 5, Assign_ = 6, // ref = target AssignName_ = 7 }; Type type = Null; typedef std::unordered_map ObjectStorage; // MSVC does not allow unrestricted unions: // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2544.pdf #ifdef _MSC_VER IString str; #endif union { // TODO: optimize #ifndef _MSC_VER IString str; #endif double num; ArrayStorage* arr; bool boo; ObjectStorage* obj; Ref ref; }; // constructors all copy their input Value() {} explicit Value(const char* s) { setString(s); } explicit Value(double n) { setNumber(n); } explicit Value(ArrayStorage& a) { setArray(); *arr = a; } // no bool constructor - would endanger the double one (int might convert the // wrong way) ~Value() { free(); } void free() { if (type == Array) { arr->clear(); } else if (type == Object) { delete obj; } type = Null; num = 0; } Value& setString(const char* s) { free(); type = String; str.set(s); return *this; } Value& setString(const IString& s) { free(); type = String; str.set(s); return *this; } Value& setNumber(double n) { free(); type = Number; num = n; return *this; } Value& setArray(ArrayStorage& a) { free(); type = Array; arr = arena.alloc(); *arr = a; return *this; } Value& setArray(size_t size_hint = 0) { free(); type = Array; arr = arena.alloc(); arr->reserve(size_hint); return *this; } Value& setNull() { free(); type = Null; return *this; } // Bool in the name, as otherwise might overload over int Value& setBool(bool b) { free(); type = Bool; boo = b; return *this; } Value& setObject() { free(); type = Object; obj = new ObjectStorage(); return *this; } Value& setAssign(Ref target, Ref value); Value& setAssignName(IString target, Ref value); bool isString() { return type == String; } bool isNumber() { return type == Number; } bool isArray() { return type == Array; } bool isNull() { return type == Null; } bool isBool() { return type == Bool; } bool isObject() { return type == Object; } bool isAssign() { return type == Assign_; } bool isAssignName() { return type == AssignName_; } // avoid overloading == as it might overload over int bool isBool(bool b) { return type == Bool && b == boo; } // convenience function to check if something is an array and // also has a certain string as the first element. This is a // very common operation as the first element defines the node // type for most ast nodes bool isArray(IString name) { return isArray() && (*this)[0] == name; } const char* getCString() { assert(isString()); return str.str; } IString& getIString() { assert(isString()); return str; } double& getNumber() { assert(isNumber()); return num; } ArrayStorage& getArray() { assert(isArray()); return *arr; } bool& getBool() { assert(isBool()); return boo; } Assign* asAssign(); AssignName* asAssignName(); int32_t getInteger() { // convenience function to get a known integer assert(fmod(getNumber(), 1) == 0); int32_t ret = getNumber(); assert(double(ret) == getNumber()); // no loss in conversion return ret; } Value& operator=(const Value& other) { free(); switch (other.type) { case String: setString(other.str); break; case Number: setNumber(other.num); break; case Array: setArray(*other.arr); break; case Null: setNull(); break; case Bool: setBool(other.boo); break; default: abort(); // TODO } return *this; } bool operator==(const Value& other) { if (type != other.type) { return false; } switch (other.type) { case String: return str == other.str; case Number: return num == other.num; case Array: return this == &other; // if you want a deep compare, use deepCompare case Null: break; case Bool: return boo == other.boo; case Object: return this == &other; // if you want a deep compare, use deepCompare default: abort(); } return true; } char* parse(char* curr) { /* space, tab, linefeed/newline, or return */ #define is_json_space(x) (x == 32 || x == 9 || x == 10 || x == 13) #define skip() \ { \ while (*curr && is_json_space(*curr)) \ curr++; \ } skip(); if (*curr == '"') { // String curr++; char* close = strchr(curr, '"'); assert(close); *close = 0; // end this string, and reuse it straight from the input setString(curr); curr = close + 1; } else if (*curr == '[') { // Array curr++; skip(); setArray(); while (*curr != ']') { Ref temp = arena.alloc(); arr->push_back(temp); curr = temp->parse(curr); skip(); if (*curr == ']') { break; } assert(*curr == ','); curr++; skip(); } curr++; } else if (*curr == 'n') { // Null assert(strncmp(curr, "null", 4) == 0); setNull(); curr += 4; } else if (*curr == 't') { // Bool true assert(strncmp(curr, "true", 4) == 0); setBool(true); curr += 4; } else if (*curr == 'f') { // Bool false assert(strncmp(curr, "false", 5) == 0); setBool(false); curr += 5; } else if (*curr == '{') { // Object curr++; skip(); setObject(); while (*curr != '}') { assert(*curr == '"'); curr++; char* close = strchr(curr, '"'); assert(close); *close = 0; // end this string, and reuse it straight from the input IString key(curr); curr = close + 1; skip(); assert(*curr == ':'); curr++; skip(); Ref value = arena.alloc(); curr = value->parse(curr); (*obj)[key] = value; skip(); if (*curr == '}') { break; } assert(*curr == ','); curr++; skip(); } curr++; } else { // Number char* after; setNumber(strtod(curr, &after)); curr = after; } return curr; } void stringify(std::ostream& os, bool pretty = false); // String operations // Number operations // Array operations size_t size() { assert(isArray()); return arr->size(); } bool empty() { return size() == 0; } void setSize(size_t size) { assert(isArray()); auto old = arr->size(); if (old != size) { arr->resize(size); } if (old < size) { for (auto i = old; i < size; i++) { (*arr)[i] = arena.alloc(); } } } Ref& operator[](unsigned x) { assert(isArray()); return (*arr)[x]; } Value& push_back(Ref r) { assert(isArray()); arr->push_back(r); return *this; } Ref pop_back() { assert(isArray()); Ref ret = arr->back(); arr->pop_back(); return ret; } Ref back() { assert(isArray()); if (arr->size() == 0) { return nullptr; } return arr->back(); } void splice(int x, int num) { assert(isArray()); arr->erase(arr->begin() + x, arr->begin() + x + num); } int indexOf(Ref other) { assert(isArray()); for (size_t i = 0; i < arr->size(); i++) { if (other == (*arr)[i]) { return i; } } return -1; } Ref map(std::function func) { assert(isArray()); Ref ret = arena.alloc(); ret->setArray(); for (size_t i = 0; i < arr->size(); i++) { ret->push_back(func((*arr)[i])); } return ret; } Ref filter(std::function func) { assert(isArray()); Ref ret = arena.alloc(); ret->setArray(); for (size_t i = 0; i < arr->size(); i++) { Ref curr = (*arr)[i]; if (func(curr)) { ret->push_back(curr); } } return ret; } /* void forEach(std::function func) { for (size_t i = 0; i < arr->size(); i++) { func((*arr)[i]); } } */ // Null operations // Bool operations // Object operations Ref& operator[](IString x) { assert(isObject()); return (*obj)[x]; } bool has(IString x) { assert(isObject()); return obj->count(x) > 0; } }; struct Assign : public Value { Ref value_; Assign(Ref targetInit, Ref valueInit) { type = Assign_; target() = targetInit; value() = valueInit; } Assign() : Assign(nullptr, nullptr) {} Ref& target() { return ref; } Ref& value() { return value_; } }; struct AssignName : public Value { IString target_; AssignName(IString targetInit, Ref valueInit) { type = AssignName_; target() = targetInit; value() = valueInit; } AssignName() : AssignName(IString(), nullptr) {} IString& target() { return target_; } Ref& value() { return ref; } }; // JS printing support struct JSPrinter { bool pretty, finalize; char* buffer = nullptr; size_t size = 0; size_t used = 0; int indent = 0; bool possibleSpace = false; // add a space to separate identifiers Ref ast; JSPrinter(bool pretty_, bool finalize_, Ref ast_) : pretty(pretty_), finalize(finalize_), ast(ast_) {} ~JSPrinter() { free(buffer); } void printAst() { print(ast); ensure(1); buffer[used] = 0; } // Utils void ensure(int safety = 100) { if (size >= used + safety) { return; } size = std::max((size_t)1024, size * 2) + safety; if (!buffer) { buffer = (char*)malloc(size); if (!buffer) { errv("Out of memory allocating %zd bytes for output buffer!", size); abort(); } } else { char* buf = (char*)realloc(buffer, size); if (!buf) { free(buffer); errv("Out of memory allocating %zd bytes for output buffer!", size); abort(); } buffer = buf; } } void emit(char c) { maybeSpace(c); if (!pretty && c == '}' && buffer[used - 1] == ';') { used--; // optimize ;} into }, the ; is not separating anything } ensure(1); buffer[used++] = c; } void emit(const char* s) { maybeSpace(*s); int len = strlen(s); ensure(len + 1); strncpy(buffer + used, s, len + 1); used += len; } void newline() { if (!pretty) { return; } emit('\n'); for (int i = 0; i < indent; i++) { emit(' '); } } void space() { if (pretty) { emit(' '); } } void safeSpace() { if (pretty) { emit(' '); } else { possibleSpace = true; } } void maybeSpace(char s) { if (possibleSpace) { possibleSpace = false; if (isIdentPart(s)) { emit(' '); } } } bool isNothing(Ref node) { return node->isArray() && node[0] == TOPLEVEL && node[1]->size() == 0; } bool isDefun(Ref node) { return node->isArray() && node[0] == DEFUN; } bool endsInBlock(Ref node) { if (node->isArray() && node[0] == BLOCK) { return true; } // Check for a label on a block if (node->isArray() && node[0] == LABEL && endsInBlock(node[2])) { return true; } // Check for an if if (node->isArray() && node[0] == IF && endsInBlock(ifHasElse(node) ? node[3] : node[2])) { return true; } return false; } bool isIf(Ref node) { return node->isArray() && node[0] == IF; } void print(Ref node) { ensure(); if (node->isString()) { printName(node); return; } if (node->isNumber()) { printNum(node); return; } if (node->isAssignName()) { printAssignName(node); return; } if (node->isAssign()) { printAssign(node); return; } IString type = node[0]->getIString(); switch (type.str[0]) { case 'a': { if (type == ARRAY) { printArray(node); } else { abort(); } break; } case 'b': { if (type == BINARY) { printBinary(node); } else if (type == BLOCK) { printBlock(node); } else if (type == BREAK) { printBreak(node); } else { abort(); } break; } case 'c': { if (type == CALL) { printCall(node); } else if (type == CONDITIONAL) { printConditional(node); } else if (type == CONTINUE) { printContinue(node); } else { abort(); } break; } case 'd': { if (type == DEFUN) { printDefun(node); } else if (type == DO) { printDo(node); } else if (type == DOT) { printDot(node); } else { abort(); } break; } case 'i': { if (type == IF) { printIf(node); } else { abort(); } break; } case 'l': { if (type == LABEL) { printLabel(node); } else { abort(); } break; } case 'n': { if (type == NEW) { printNew(node); } else { abort(); } break; } case 'o': { if (type == OBJECT) { printObject(node); } break; } case 'r': { if (type == RETURN) { printReturn(node); } else { abort(); } break; } case 's': { if (type == SUB) { printSub(node); } else if (type == SEQ) { printSeq(node); } else if (type == SWITCH) { printSwitch(node); } else if (type == STRING) { printString(node); } else { abort(); } break; } case 't': { if (type == TOPLEVEL) { printToplevel(node); } else if (type == TRY) { printTry(node); } else { abort(); } break; } case 'u': { if (type == UNARY_PREFIX) { printUnaryPrefix(node); } else { abort(); } break; } case 'v': { if (type == VAR) { printVar(node); } else { abort(); } break; } case 'w': { if (type == WHILE) { printWhile(node); } else { abort(); } break; } default: { errv("cannot yet print %s\n", type.str); abort(); } } } // print a node, and if nothing is emitted, emit something instead void print(Ref node, const char* otherwise) { auto last = used; print(node); if (used == last) { emit(otherwise); } } void printStats(Ref stats) { bool first = true; for (size_t i = 0; i < stats->size(); i++) { Ref curr = stats[i]; if (!isNothing(curr)) { if (first) { first = false; } else { newline(); } print(curr); if (!isDefun(curr) && !endsInBlock(curr) && !isIf(curr)) { emit(';'); } } } } void printToplevel(Ref node) { if (node[1]->size() > 0) { printStats(node[1]); } } void printBlock(Ref node) { if (node->size() == 1 || node[1]->size() == 0) { emit("{}"); return; } emit('{'); indent++; newline(); printStats(node[1]); indent--; newline(); emit('}'); } void printDefun(Ref node) { emit("function "); emit(node[1]->getCString()); emit('('); Ref args = node[2]; for (size_t i = 0; i < args->size(); i++) { if (i > 0) { (pretty ? emit(", ") : emit(',')); } emit(args[i]->getCString()); } emit(')'); space(); if (node->size() == 3 || node[3]->size() == 0) { emit("{}"); return; } emit('{'); indent++; newline(); printStats(node[3]); indent--; newline(); emit('}'); newline(); } void printAssign(Ref node) { auto* assign = node->asAssign(); printChild(assign->target(), node, -1); space(); emit('='); space(); printChild(assign->value(), node, 1); } void printAssignName(Ref node) { auto* assign = node->asAssignName(); emit(assign->target().c_str()); space(); emit('='); space(); printChild(assign->value(), node, 1); } void printName(Ref node) { emit(node->getCString()); } static char* numToString(double d, bool finalize = true) { // If this number is NaN or infinite then things are a bit tricky. In JS we // want to eventually use `NaN` and/or `Infinity`, but neither of those // identifiers are valid in asm.js. Instead we have to explicitly import // `NaN` and `Infinity` from the global environment, and those names are // bound locally in an asm function as `nan` and `infinity`. // // TODO: the JS names of `NaN` and `Infinity` should be used once literal // asm.js code isn't generated any more if (std::isnan(d)) { if (std::signbit(d)) { return (char*)"-nan"; } else { return (char*)"nan"; } } else if (!std::isfinite(d)) { if (std::signbit(d)) { return (char*)"-infinity"; } else { return (char*)"infinity"; } } bool neg = d < 0; if (neg) { d = -d; } // try to emit the fewest necessary characters bool integer = fmod(d, 1) == 0; #define BUFFERSIZE 1000 // f is normal, e is scientific for float, x for integer static char full_storage_f[BUFFERSIZE], full_storage_e[BUFFERSIZE]; // full has one more char, for a possible '-' static char *storage_f = full_storage_f + 1, *storage_e = full_storage_e + 1; auto err_f = std::numeric_limits::quiet_NaN(); auto err_e = std::numeric_limits::quiet_NaN(); for (int e = 0; e <= 1; e++) { char* buffer = e ? storage_e : storage_f; double temp; if (!integer) { static char format[6]; for (int i = 0; i <= 18; i++) { format[0] = '%'; format[1] = '.'; if (i < 10) { format[2] = '0' + i; format[3] = e ? 'e' : 'f'; format[4] = 0; } else { format[2] = '1'; format[3] = '0' + (i - 10); format[4] = e ? 'e' : 'f'; format[5] = 0; } snprintf(buffer, BUFFERSIZE - 1, format, d); sscanf(buffer, "%lf", &temp); // errv("%.18f, %.18e => %s => %.18f, %.18e (%d), ", d, d, // buffer, temp, temp, temp == d); if (temp == d) { break; } } } else { // integer assert(d >= 0); if (wasm::isUInteger64(d)) { unsigned long long uu = wasm::toUInteger64(d); bool asHex = e && !finalize; snprintf(buffer, BUFFERSIZE - 1, asHex ? "0x%llx" : "%llu", uu); if (asHex) { unsigned long long tempULL; sscanf(buffer, "%llx", &tempULL); temp = (double)tempULL; } else { sscanf(buffer, "%lf", &temp); } } else { // too large for a machine integer, just use floats // even on integers, e with a dot is useful, e.g. 1.2e+200 snprintf(buffer, BUFFERSIZE - 1, e ? "%e" : "%.0f", d); sscanf(buffer, "%lf", &temp); } // errv("%.18f, %.18e => %s => %.18f, %.18e, %llu (%d)\n", d, // d, buffer, temp, temp, uu, temp == d); } (e ? err_e : err_f) = fabs(temp - d); // errv("current attempt: %.18f => %s", d, buffer); // assert(temp == d); char* dot = strchr(buffer, '.'); if (dot) { // remove trailing zeros char* end = dot + 1; while (*end >= '0' && *end <= '9') { end++; } end--; while (*end == '0') { char* copy = end; do { copy[0] = copy[1]; } while (*copy++ != 0); end--; } // errv("%.18f => %s", d, buffer); // remove preceding zeros while (*buffer == '0') { char* copy = buffer; do { copy[0] = copy[1]; } while (*copy++ != 0); } // errv("%.18f ===> %s", d, buffer); } else if (!integer || !e) { // no dot. try to change 12345000 => 12345e3 char* end = strchr(buffer, 0); end--; char* test = end; // remove zeros, and also doubles can use at most 24 digits, we can // truncate any extras even if not zero while ((*test == '0' || test - buffer > 24) && test > buffer) { test--; } int num = end - test; if (num >= 3) { test++; test[0] = 'e'; if (num < 10) { test[1] = '0' + num; test[2] = 0; } else if (num < 100) { test[1] = '0' + (num / 10); test[2] = '0' + (num % 10); test[3] = 0; } else { assert(num < 1000); test[1] = '0' + (num / 100); test[2] = '0' + (num % 100) / 10; test[3] = '0' + (num % 10); test[4] = 0; } } } // errv("..current attempt: %.18f => %s", d, buffer); } // fprintf(stderr, "options:\n%s\n%s\n (first? %d)\n", storage_e, storage_f, // strlen(storage_e) < strlen(storage_f)); char* ret; if (err_e == err_f) { ret = strlen(storage_e) < strlen(storage_f) ? storage_e : storage_f; } else { ret = err_e < err_f ? storage_e : storage_f; } if (neg) { ret--; // safe to go back one, there is one more char in full_* *ret = '-'; } return ret; } void printNum(Ref node) { if (node->getNumber() < 0 && buffer[used - 1] == '-') { emit(' '); // cannot join - and - to --, looks like the -- operator } emit(numToString(node->getNumber(), finalize)); } void printString(Ref node) { emit('"'); emit(node[1]->getCString()); emit('"'); } // Parens optimizing bool capturesOperators(Ref node) { Ref type = node[0]; return type == CALL || type == ARRAY || type == OBJECT || type == SEQ; } int getPrecedence(Ref node, bool parent) { if (node->isAssign() || node->isAssignName()) { return OperatorClass::getPrecedence(OperatorClass::Binary, SET); } if (!node->isArray()) { // node is a value return -1; } Ref type = node[0]; if (type == BINARY || type == UNARY_PREFIX) { return OperatorClass::getPrecedence( type == BINARY ? OperatorClass::Binary : OperatorClass::Prefix, node[1]->getIString()); } else if (type == SEQ) { return OperatorClass::getPrecedence(OperatorClass::Binary, COMMA); } else if (type == CALL) { // call arguments are split by commas, but call itself is safe return parent ? OperatorClass::getPrecedence(OperatorClass::Binary, COMMA) : -1; } else if (type == CONDITIONAL) { return OperatorClass::getPrecedence(OperatorClass::Tertiary, QUESTION); } // otherwise, this is something that fixes precedence explicitly, and we can // ignore return -1; // XXX } // check whether we need parens for the child, when rendered in the parent // @param childPosition -1 means it is printed to the left of parent, 0 means // "anywhere", 1 means right bool needParens(Ref parent, Ref child, int childPosition) { int parentPrecedence = getPrecedence(parent, true); int childPrecedence = getPrecedence(child, false); if (childPrecedence > parentPrecedence) { return true; // child is definitely a danger } if (childPrecedence < parentPrecedence) { return false; // definitely cool } // equal precedence, so associativity (rtl/ltr) is what matters // (except for some exceptions, where multiple operators can combine into // confusion) if (parent->isArray() && parent[0] == UNARY_PREFIX) { assert(child[0] == UNARY_PREFIX); if ((parent[1] == PLUS || parent[1] == MINUS) && child[1] == parent[1]) { // cannot emit ++x when we mean +(+x) return true; } } if (childPosition == 0) { return true; // child could be anywhere, so always paren } if (childPrecedence < 0) { return false; // both precedences are safe } // check if child is on the dangerous side if (OperatorClass::getRtl(parentPrecedence)) { return childPosition < 0; } else { return childPosition > 0; } } void printChild(Ref child, Ref parent, int childPosition = 0) { bool parens = needParens(parent, child, childPosition); if (parens) { emit('('); } print(child); if (parens) { emit(')'); } } void printBinary(Ref node) { printChild(node[2], node, -1); space(); emit(node[1]->getCString()); space(); printChild(node[3], node, 1); } void printUnaryPrefix(Ref node) { if (finalize && node[1] == PLUS && (node[2]->isNumber() || (node[2]->isArray() && node[2][0] == UNARY_PREFIX && node[2][1] == MINUS && node[2][2]->isNumber()))) { // emit a finalized number int last = used; print(node[2]); ensure(1); // we temporarily append a 0 char* curr = buffer + last; // ensure might invalidate buffer[used] = 0; if (strstr(curr, "infinity")) { return; } if (strstr(curr, "nan")) { return; } if (strchr(curr, '.')) { return; // already a decimal point, all good } char* e = strchr(curr, 'e'); if (!e) { emit(".0"); return; } ensure(3); curr = buffer + last; // ensure might invalidate char* end = strchr(curr, 0); while (end >= e) { end[2] = end[0]; end--; } e[0] = '.'; e[1] = '0'; used += 2; return; } if ((buffer[used - 1] == '-' && node[1] == MINUS) || (buffer[used - 1] == '+' && node[1] == PLUS)) { emit(' '); // cannot join - and - to --, looks like the -- operator } emit(node[1]->getCString()); printChild(node[2], node, 1); } void printConditional(Ref node) { printChild(node[1], node, -1); space(); emit('?'); space(); printChild(node[2], node, 0); space(); emit(':'); space(); printChild(node[3], node, 1); } void printCall(Ref node) { printChild(node[1], node, 0); emit('('); Ref args = node[2]; for (size_t i = 0; i < args->size(); i++) { if (i > 0) { (pretty ? emit(", ") : emit(',')); } printChild(args[i], node, 0); } emit(')'); } void printSeq(Ref node) { printChild(node[1], node, -1); emit(','); space(); printChild(node[2], node, 1); } void printDot(Ref node) { print(node[1]); emit('.'); emit(node[2]->getCString()); } void printSwitch(Ref node) { emit("switch"); space(); emit('('); print(node[1]); emit(')'); space(); emit('{'); newline(); Ref cases = node[2]; for (size_t i = 0; i < cases->size(); i++) { Ref c = cases[i]; if (!c[0]) { emit("default:"); } else { emit("case "); print(c[0]); emit(':'); } if (c[1]->size() > 0) { indent++; newline(); auto curr = used; printStats(c[1]); indent--; if (curr != used) { newline(); } else { used--; // avoid the extra indentation we added tentatively } } else { newline(); } } emit('}'); } void printTry(Ref node) { emit("try "); printBlock(node[1]); emit(" catch ("); printName(node[2]); emit(") "); printBlock(node[3]); } void printSub(Ref node) { printChild(node[1], node, -1); emit('['); print(node[2]); emit(']'); } void printVar(Ref node) { emit("var "); Ref args = node[1]; for (size_t i = 0; i < args->size(); i++) { if (i > 0) { (pretty ? emit(", ") : emit(',')); } emit(args[i][0]->getCString()); if (args[i]->size() > 1) { space(); emit('='); space(); print(args[i][1]); } } } static bool isBlock(Ref node) { return node->isArray() && !node->empty() && node[0] == BLOCK; } static bool ifHasElse(Ref node) { assert(node->isArray() && node[0] == IF); return node->size() >= 4 && !!node[3]; } void printIf(Ref node) { emit("if"); safeSpace(); emit('('); print(node[1]); emit(')'); space(); bool emitsBracesAnyhow = isBlock(node[2]); if (!emitsBracesAnyhow) { emit('{'); indent++; newline(); } print(node[2]); if (!emitsBracesAnyhow) { indent--; newline(); emit('}'); } if (ifHasElse(node)) { space(); emit("else"); safeSpace(); bool emitsBracesAnyhow = isBlock(node[3]); if (!emitsBracesAnyhow) { emit('{'); indent++; newline(); } print(node[3]); if (!emitsBracesAnyhow) { indent--; newline(); emit('}'); } } } void printDo(Ref node) { emit("do"); safeSpace(); print(node[2], "{}"); space(); emit("while"); space(); emit('('); print(node[1]); emit(')'); } void printWhile(Ref node) { emit("while"); space(); emit('('); print(node[1]); emit(')'); space(); print(node[2], "{}"); } void printLabel(Ref node) { emit(node[1]->getCString()); space(); emit(':'); space(); print(node[2]); } void printReturn(Ref node) { emit("return"); if (!!node[1]) { emit(' '); print(node[1]); } } void printBreak(Ref node) { emit("break"); if (!!node[1]) { emit(' '); emit(node[1]->getCString()); } } void printContinue(Ref node) { emit("continue"); if (!!node[1]) { emit(' '); emit(node[1]->getCString()); } } void printNew(Ref node) { emit("new "); print(node[1]); } void printArray(Ref node) { emit('['); Ref args = node[1]; for (size_t i = 0; i < args->size(); i++) { if (i > 0) { (pretty ? emit(", ") : emit(',')); } print(args[i]); } emit(']'); } void printObject(Ref node) { emit('{'); indent++; newline(); Ref args = node[1]; for (size_t i = 0; i < args->size(); i++) { if (i > 0) { pretty ? emit(", ") : emit(','); newline(); } bool needQuote = false; const char* str; if (args[i][0]->isArray()) { assert(args[i][0][0] == STRING); // A quoted string. needQuote = true; str = args[i][0][1]->getCString(); } else { // Just a raw string, no quotes. str = args[i][0]->getCString(); } const char* check = str; while (*check) { if (!isalnum(*check) && *check != '_' && *check != '$') { needQuote = true; break; } check++; } if (needQuote) { emit('"'); } emit(str); if (needQuote) { emit('"'); } emit(":"); space(); print(args[i][1]); } indent--; newline(); emit('}'); } }; // cashew builder class ValueBuilder { static Ref makeRawString(const IString& s) { return &arena.alloc()->setString(s); } static Ref makeNull() { return &arena.alloc()->setNull(); } public: static Ref makeRawArray(int size_hint = 0) { return &arena.alloc()->setArray(size_hint); } static Ref makeToplevel() { return &makeRawArray(2) ->push_back(makeRawString(TOPLEVEL)) .push_back(makeRawArray()); } static Ref makeString(IString str) { return &makeRawArray(2) ->push_back(makeRawString(STRING)) .push_back(makeRawString(str)); } static Ref makeBlock() { return &makeRawArray(2) ->push_back(makeRawString(BLOCK)) .push_back(makeRawArray()); } static Ref makeName(IString name) { return makeRawString(name); } static void setBlockContent(Ref target, Ref block) { if (target[0] == TOPLEVEL) { target[1]->setArray(block[1]->getArray()); } else if (target[0] == DEFUN) { target[3]->setArray(block[1]->getArray()); } else { abort(); } } static void appendToBlock(Ref block, Ref element) { assert(block[0] == BLOCK); block[1]->push_back(element); } static Ref makeCall(Ref target) { return &makeRawArray(3) ->push_back(makeRawString(CALL)) .push_back(target) .push_back(makeRawArray()); } static Ref makeCall(Ref target, Ref arg) { Ref ret = &makeRawArray(3) ->push_back(makeRawString(CALL)) .push_back(target) .push_back(makeRawArray()); ret[2]->push_back(arg); return ret; } static Ref makeCall(IString target) { Ref ret = &makeRawArray(3) ->push_back(makeRawString(CALL)) .push_back(makeName(target)) .push_back(makeRawArray()); return ret; } template static Ref makeCall(IString target, Ts... args) { size_t nArgs = sizeof...(Ts); Ref callArgs = makeRawArray(nArgs); Ref argArray[] = {args...}; for (size_t i = 0; i < nArgs; ++i) { callArgs->push_back(argArray[i]); } return &makeRawArray(3) ->push_back(makeRawString(CALL)) .push_back(makeName(target)) .push_back(callArgs); } static void appendToCall(Ref call, Ref element) { assert(call[0] == CALL); call[2]->push_back(element); } static Ref makeStatement(Ref contents) { return contents; } static Ref makeDouble(double num) { return &arena.alloc()->setNumber(num); } static Ref makeInt(uint32_t num) { return makeDouble(double(num)); } static Ref makeInt(int32_t num) { return makeDouble(double(num)); } static Ref makeNum(double num) { return makeDouble(num); } static Ref makeUnary(IString op, Ref value) { return &makeRawArray(3) ->push_back(makeRawString(UNARY_PREFIX)) .push_back(makeRawString(op)) .push_back(value); } static Ref makeBinary(Ref left, IString op, Ref right) { if (op == SET) { if (left->isString()) { return &arena.alloc()->setAssignName(left->getIString(), right); } else { return &arena.alloc()->setAssign(left, right); } } else if (op == COMMA) { return &makeRawArray(3) ->push_back(makeRawString(SEQ)) .push_back(left) .push_back(right); } else { return &makeRawArray(4) ->push_back(makeRawString(BINARY)) .push_back(makeRawString(op)) .push_back(left) .push_back(right); } } static Ref makePrefix(IString op, Ref right) { return &makeRawArray(3) ->push_back(makeRawString(UNARY_PREFIX)) .push_back(makeRawString(op)) .push_back(right); } static Ref makeFunction(IString name) { return &makeRawArray(4) ->push_back(makeRawString(DEFUN)) .push_back(makeRawString(name)) .push_back(makeRawArray()) .push_back(makeRawArray()); } static void appendArgumentToFunction(Ref func, IString arg) { assert(func[0] == DEFUN); func[2]->push_back(makeRawString(arg)); } static Ref makeVar(bool is_const = false) { return &makeRawArray(2) ->push_back(makeRawString(VAR)) .push_back(makeRawArray()); } static void appendToVar(Ref var, IString name, Ref value) { assert(var[0] == VAR); Ref array = &makeRawArray(1)->push_back(makeRawString(name)); if (!!value) { array->push_back(value); } var[1]->push_back(array); } static Ref makeReturn(Ref value) { return &makeRawArray(2) ->push_back(makeRawString(RETURN)) .push_back(!!value ? value : makeNull()); } static Ref makeIndexing(Ref target, Ref index) { return &makeRawArray(3) ->push_back(makeRawString(SUB)) .push_back(target) .push_back(index); } static Ref makeIf(Ref condition, Ref ifTrue, Ref ifFalse) { return &makeRawArray(4) ->push_back(makeRawString(IF)) .push_back(condition) .push_back(ifTrue) .push_back(!!ifFalse ? ifFalse : makeNull()); } static Ref makeConditional(Ref condition, Ref ifTrue, Ref ifFalse) { return &makeRawArray(4) ->push_back(makeRawString(CONDITIONAL)) .push_back(condition) .push_back(ifTrue) .push_back(ifFalse); } static Ref makeSeq(Ref left, Ref right) { return &makeRawArray(3) ->push_back(makeRawString(SEQ)) .push_back(left) .push_back(right); } static Ref makeDo(Ref body, Ref condition) { return &makeRawArray(3) ->push_back(makeRawString(DO)) .push_back(condition) .push_back(body); } static Ref makeWhile(Ref condition, Ref body) { return &makeRawArray(3) ->push_back(makeRawString(WHILE)) .push_back(condition) .push_back(body); } static Ref makeFor(Ref init, Ref condition, Ref inc, Ref body) { return &makeRawArray(5) ->push_back(makeRawString(FOR)) .push_back(init) .push_back(condition) .push_back(inc) .push_back(body); } static Ref makeBreak(IString label) { return &makeRawArray(2) ->push_back(makeRawString(BREAK)) .push_back(!!label ? makeRawString(label) : makeNull()); } static Ref makeContinue(IString label) { return &makeRawArray(2) ->push_back(makeRawString(CONTINUE)) .push_back(!!label ? makeRawString(label) : makeNull()); } static Ref makeLabel(IString name, Ref body) { return &makeRawArray(3) ->push_back(makeRawString(LABEL)) .push_back(makeRawString(name)) .push_back(body); } static Ref makeSwitch(Ref input) { return &makeRawArray(3) ->push_back(makeRawString(SWITCH)) .push_back(input) .push_back(makeRawArray()); } static void appendCaseToSwitch(Ref switch_, Ref arg) { assert(switch_[0] == SWITCH); switch_[2]->push_back( &makeRawArray(2)->push_back(arg).push_back(makeRawArray())); } static void appendDefaultToSwitch(Ref switch_) { assert(switch_[0] == SWITCH); switch_[2]->push_back( &makeRawArray(2)->push_back(makeNull()).push_back(makeRawArray())); } static void appendCodeToSwitch(Ref switch_, Ref code, bool explicitBlock) { assert(switch_[0] == SWITCH); assert(code[0] == BLOCK); if (!explicitBlock) { for (size_t i = 0; i < code[1]->size(); i++) { switch_[2]->back()->back()->push_back(code[1][i]); } } else { switch_[2]->back()->back()->push_back(code); } } static Ref makeTry(Ref try_, Ref arg, Ref catch_) { assert(try_[0] == BLOCK); assert(catch_[0] == BLOCK); return &makeRawArray(3) ->push_back(makeRawString(TRY)) .push_back(try_) .push_back(arg) .push_back(catch_); } static Ref makeDot(Ref obj, IString key) { return &makeRawArray(3) ->push_back(makeRawString(DOT)) .push_back(obj) .push_back(makeRawString(key)); } template static Ref makeDot(Ref obj, Ref key, Ts... args) { return makeDot(makeDot(obj, key), args...); } static Ref makeDot(Ref obj, Ref key) { assert(key->isString()); return makeDot(obj, key->getIString()); } static Ref makeNew(Ref call) { return &makeRawArray(2)->push_back(makeRawString(NEW)).push_back(call); } static Ref makeArray() { return &makeRawArray(2) ->push_back(makeRawString(ARRAY)) .push_back(makeRawArray()); } static void appendToArray(Ref array, Ref element) { assert(array[0] == ARRAY); array[1]->push_back(element); } static Ref makeObject() { return &makeRawArray(2) ->push_back(makeRawString(OBJECT)) .push_back(makeRawArray()); } static void appendToObject(Ref array, IString key, Ref value) { assert(array[0] == OBJECT); array[1]->push_back( &makeRawArray(2)->push_back(makeRawString(key)).push_back(value)); } static void appendToObjectWithQuotes(Ref array, IString key, Ref value) { assert(array[0] == OBJECT); array[1]->push_back( &makeRawArray(2)->push_back(makeString(key)).push_back(value)); } static Ref makeSub(Ref obj, Ref index) { return &makeRawArray(2) ->push_back(makeRawString(SUB)) .push_back(obj) .push_back(index); } static Ref makePtrShift(Ref ptr, int shifts) { return makeBinary(ptr, RSHIFT, makeInt(shifts)); } }; // Tolerates 0.0 in the input; does not trust a +() to be there. class DotZeroValueBuilder : public ValueBuilder { public: static Ref makeDouble(double num) { return makePrefix(PLUS, ValueBuilder::makeDouble(num)); } }; } // namespace cashew #endif // wasm_simple_ast_h binaryen-version_91/src/emscripten-optimizer/snprintf.h000066400000000000000000000024571362402614000236630ustar00rootroot00000000000000/* * Copyright 2015 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_snprintf_h #define wasm_snprintf_h #include // Visual Studio does not support C99, so emulate snprintf support for it // manually. #ifdef _MSC_VER #define snprintf c99_snprintf inline int c99_vsnprintf(char* str, size_t size, const char* format, va_list ap) { int count = -1; if (size != 0) count = _vsnprintf_s(str, size, _TRUNCATE, format, ap); if (count == -1) count = _vscprintf(format, ap); return count; } inline int c99_snprintf(char* str, size_t size, const char* format, ...) { int count; va_list ap; va_start(ap, format); count = c99_vsnprintf(str, size, format, ap); va_end(ap); return count; } #endif #endif // wasm_snprintf_h binaryen-version_91/src/gen-s-parser.inc000066400000000000000000003510651362402614000204560ustar00rootroot00000000000000// DO NOT EDIT! This file generated by scripts/gen-s-parser.py // clang-format off #ifdef INSTRUCTION_PARSER #undef INSTRUCTION_PARSER char op[27] = {'\0'}; strncpy(op, s[0]->c_str(), 26); switch (op[0]) { case 'a': { switch (op[1]) { case 'n': if (strcmp(op, "anyref.pop") == 0) { return makePop(Type::anyref); } goto parse_error; case 't': { switch (op[7]) { case 'f': if (strcmp(op, "atomic.fence") == 0) { return makeAtomicFence(s); } goto parse_error; case 'n': if (strcmp(op, "atomic.notify") == 0) { return makeAtomicNotify(s); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } case 'b': { switch (op[1]) { case 'l': if (strcmp(op, "block") == 0) { return makeBlock(s); } goto parse_error; case 'r': { switch (op[2]) { case '\0': if (strcmp(op, "br") == 0) { return makeBreak(s); } goto parse_error; case '_': { switch (op[3]) { case 'i': if (strcmp(op, "br_if") == 0) { return makeBreak(s); } goto parse_error; case 'o': if (strcmp(op, "br_on_exn") == 0) { return makeBrOnExn(s); } goto parse_error; case 't': if (strcmp(op, "br_table") == 0) { return makeBreakTable(s); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } default: goto parse_error; } } case 'c': { switch (op[4]) { case '\0': if (strcmp(op, "call") == 0) { return makeCall(s, /*isReturn=*/false); } goto parse_error; case '_': if (strcmp(op, "call_indirect") == 0) { return makeCallIndirect(s, /*isReturn=*/false); } goto parse_error; default: goto parse_error; } } case 'd': { switch (op[1]) { case 'a': if (strcmp(op, "data.drop") == 0) { return makeDataDrop(s); } goto parse_error; case 'r': if (strcmp(op, "drop") == 0) { return makeDrop(s); } goto parse_error; default: goto parse_error; } } case 'e': { switch (op[1]) { case 'l': if (strcmp(op, "else") == 0) { return makeThenOrElse(s); } goto parse_error; case 'x': if (strcmp(op, "exnref.pop") == 0) { return makePop(Type::exnref); } goto parse_error; default: goto parse_error; } } case 'f': { switch (op[1]) { case '3': { switch (op[3]) { case '.': { switch (op[4]) { case 'a': { switch (op[5]) { case 'b': if (strcmp(op, "f32.abs") == 0) { return makeUnary(s, UnaryOp::AbsFloat32); } goto parse_error; case 'd': if (strcmp(op, "f32.add") == 0) { return makeBinary(s, BinaryOp::AddFloat32); } goto parse_error; default: goto parse_error; } } case 'c': { switch (op[5]) { case 'e': if (strcmp(op, "f32.ceil") == 0) { return makeUnary(s, UnaryOp::CeilFloat32); } goto parse_error; case 'o': { switch (op[6]) { case 'n': { switch (op[7]) { case 's': if (strcmp(op, "f32.const") == 0) { return makeConst(s, Type::f32); } goto parse_error; case 'v': { switch (op[13]) { case '3': { switch (op[16]) { case 's': if (strcmp(op, "f32.convert_i32_s") == 0) { return makeUnary(s, UnaryOp::ConvertSInt32ToFloat32); } goto parse_error; case 'u': if (strcmp(op, "f32.convert_i32_u") == 0) { return makeUnary(s, UnaryOp::ConvertUInt32ToFloat32); } goto parse_error; default: goto parse_error; } } case '6': { switch (op[16]) { case 's': if (strcmp(op, "f32.convert_i64_s") == 0) { return makeUnary(s, UnaryOp::ConvertSInt64ToFloat32); } goto parse_error; case 'u': if (strcmp(op, "f32.convert_i64_u") == 0) { return makeUnary(s, UnaryOp::ConvertUInt64ToFloat32); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } default: goto parse_error; } } case 'p': if (strcmp(op, "f32.copysign") == 0) { return makeBinary(s, BinaryOp::CopySignFloat32); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } case 'd': { switch (op[5]) { case 'e': if (strcmp(op, "f32.demote_f64") == 0) { return makeUnary(s, UnaryOp::DemoteFloat64); } goto parse_error; case 'i': if (strcmp(op, "f32.div") == 0) { return makeBinary(s, BinaryOp::DivFloat32); } goto parse_error; default: goto parse_error; } } case 'e': if (strcmp(op, "f32.eq") == 0) { return makeBinary(s, BinaryOp::EqFloat32); } goto parse_error; case 'f': if (strcmp(op, "f32.floor") == 0) { return makeUnary(s, UnaryOp::FloorFloat32); } goto parse_error; case 'g': { switch (op[5]) { case 'e': if (strcmp(op, "f32.ge") == 0) { return makeBinary(s, BinaryOp::GeFloat32); } goto parse_error; case 't': if (strcmp(op, "f32.gt") == 0) { return makeBinary(s, BinaryOp::GtFloat32); } goto parse_error; default: goto parse_error; } } case 'l': { switch (op[5]) { case 'e': if (strcmp(op, "f32.le") == 0) { return makeBinary(s, BinaryOp::LeFloat32); } goto parse_error; case 'o': if (strcmp(op, "f32.load") == 0) { return makeLoad(s, Type::f32, /*isAtomic=*/false); } goto parse_error; case 't': if (strcmp(op, "f32.lt") == 0) { return makeBinary(s, BinaryOp::LtFloat32); } goto parse_error; default: goto parse_error; } } case 'm': { switch (op[5]) { case 'a': if (strcmp(op, "f32.max") == 0) { return makeBinary(s, BinaryOp::MaxFloat32); } goto parse_error; case 'i': if (strcmp(op, "f32.min") == 0) { return makeBinary(s, BinaryOp::MinFloat32); } goto parse_error; case 'u': if (strcmp(op, "f32.mul") == 0) { return makeBinary(s, BinaryOp::MulFloat32); } goto parse_error; default: goto parse_error; } } case 'n': { switch (op[6]) { case '\0': if (strcmp(op, "f32.ne") == 0) { return makeBinary(s, BinaryOp::NeFloat32); } goto parse_error; case 'a': if (strcmp(op, "f32.nearest") == 0) { return makeUnary(s, UnaryOp::NearestFloat32); } goto parse_error; case 'g': if (strcmp(op, "f32.neg") == 0) { return makeUnary(s, UnaryOp::NegFloat32); } goto parse_error; default: goto parse_error; } } case 'p': if (strcmp(op, "f32.pop") == 0) { return makePop(Type::f32); } goto parse_error; case 'r': if (strcmp(op, "f32.reinterpret_i32") == 0) { return makeUnary(s, UnaryOp::ReinterpretInt32); } goto parse_error; case 's': { switch (op[5]) { case 'q': if (strcmp(op, "f32.sqrt") == 0) { return makeUnary(s, UnaryOp::SqrtFloat32); } goto parse_error; case 't': if (strcmp(op, "f32.store") == 0) { return makeStore(s, Type::f32, /*isAtomic=*/false); } goto parse_error; case 'u': if (strcmp(op, "f32.sub") == 0) { return makeBinary(s, BinaryOp::SubFloat32); } goto parse_error; default: goto parse_error; } } case 't': if (strcmp(op, "f32.trunc") == 0) { return makeUnary(s, UnaryOp::TruncFloat32); } goto parse_error; default: goto parse_error; } } case 'x': { switch (op[6]) { case 'a': { switch (op[7]) { case 'b': if (strcmp(op, "f32x4.abs") == 0) { return makeUnary(s, UnaryOp::AbsVecF32x4); } goto parse_error; case 'd': if (strcmp(op, "f32x4.add") == 0) { return makeBinary(s, BinaryOp::AddVecF32x4); } goto parse_error; default: goto parse_error; } } case 'c': { switch (op[20]) { case 's': if (strcmp(op, "f32x4.convert_i32x4_s") == 0) { return makeUnary(s, UnaryOp::ConvertSVecI32x4ToVecF32x4); } goto parse_error; case 'u': if (strcmp(op, "f32x4.convert_i32x4_u") == 0) { return makeUnary(s, UnaryOp::ConvertUVecI32x4ToVecF32x4); } goto parse_error; default: goto parse_error; } } case 'd': if (strcmp(op, "f32x4.div") == 0) { return makeBinary(s, BinaryOp::DivVecF32x4); } goto parse_error; case 'e': { switch (op[7]) { case 'q': if (strcmp(op, "f32x4.eq") == 0) { return makeBinary(s, BinaryOp::EqVecF32x4); } goto parse_error; case 'x': if (strcmp(op, "f32x4.extract_lane") == 0) { return makeSIMDExtract(s, SIMDExtractOp::ExtractLaneVecF32x4, 4); } goto parse_error; default: goto parse_error; } } case 'g': { switch (op[7]) { case 'e': if (strcmp(op, "f32x4.ge") == 0) { return makeBinary(s, BinaryOp::GeVecF32x4); } goto parse_error; case 't': if (strcmp(op, "f32x4.gt") == 0) { return makeBinary(s, BinaryOp::GtVecF32x4); } goto parse_error; default: goto parse_error; } } case 'l': { switch (op[7]) { case 'e': if (strcmp(op, "f32x4.le") == 0) { return makeBinary(s, BinaryOp::LeVecF32x4); } goto parse_error; case 't': if (strcmp(op, "f32x4.lt") == 0) { return makeBinary(s, BinaryOp::LtVecF32x4); } goto parse_error; default: goto parse_error; } } case 'm': { switch (op[7]) { case 'a': if (strcmp(op, "f32x4.max") == 0) { return makeBinary(s, BinaryOp::MaxVecF32x4); } goto parse_error; case 'i': if (strcmp(op, "f32x4.min") == 0) { return makeBinary(s, BinaryOp::MinVecF32x4); } goto parse_error; case 'u': if (strcmp(op, "f32x4.mul") == 0) { return makeBinary(s, BinaryOp::MulVecF32x4); } goto parse_error; default: goto parse_error; } } case 'n': { switch (op[8]) { case '\0': if (strcmp(op, "f32x4.ne") == 0) { return makeBinary(s, BinaryOp::NeVecF32x4); } goto parse_error; case 'g': if (strcmp(op, "f32x4.neg") == 0) { return makeUnary(s, UnaryOp::NegVecF32x4); } goto parse_error; default: goto parse_error; } } case 'q': { switch (op[9]) { case 'a': if (strcmp(op, "f32x4.qfma") == 0) { return makeSIMDTernary(s, SIMDTernaryOp::QFMAF32x4); } goto parse_error; case 's': if (strcmp(op, "f32x4.qfms") == 0) { return makeSIMDTernary(s, SIMDTernaryOp::QFMSF32x4); } goto parse_error; default: goto parse_error; } } case 'r': if (strcmp(op, "f32x4.replace_lane") == 0) { return makeSIMDReplace(s, SIMDReplaceOp::ReplaceLaneVecF32x4, 4); } goto parse_error; case 's': { switch (op[7]) { case 'p': if (strcmp(op, "f32x4.splat") == 0) { return makeUnary(s, UnaryOp::SplatVecF32x4); } goto parse_error; case 'q': if (strcmp(op, "f32x4.sqrt") == 0) { return makeUnary(s, UnaryOp::SqrtVecF32x4); } goto parse_error; case 'u': if (strcmp(op, "f32x4.sub") == 0) { return makeBinary(s, BinaryOp::SubVecF32x4); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } default: goto parse_error; } } case '6': { switch (op[3]) { case '.': { switch (op[4]) { case 'a': { switch (op[5]) { case 'b': if (strcmp(op, "f64.abs") == 0) { return makeUnary(s, UnaryOp::AbsFloat64); } goto parse_error; case 'd': if (strcmp(op, "f64.add") == 0) { return makeBinary(s, BinaryOp::AddFloat64); } goto parse_error; default: goto parse_error; } } case 'c': { switch (op[5]) { case 'e': if (strcmp(op, "f64.ceil") == 0) { return makeUnary(s, UnaryOp::CeilFloat64); } goto parse_error; case 'o': { switch (op[6]) { case 'n': { switch (op[7]) { case 's': if (strcmp(op, "f64.const") == 0) { return makeConst(s, Type::f64); } goto parse_error; case 'v': { switch (op[13]) { case '3': { switch (op[16]) { case 's': if (strcmp(op, "f64.convert_i32_s") == 0) { return makeUnary(s, UnaryOp::ConvertSInt32ToFloat64); } goto parse_error; case 'u': if (strcmp(op, "f64.convert_i32_u") == 0) { return makeUnary(s, UnaryOp::ConvertUInt32ToFloat64); } goto parse_error; default: goto parse_error; } } case '6': { switch (op[16]) { case 's': if (strcmp(op, "f64.convert_i64_s") == 0) { return makeUnary(s, UnaryOp::ConvertSInt64ToFloat64); } goto parse_error; case 'u': if (strcmp(op, "f64.convert_i64_u") == 0) { return makeUnary(s, UnaryOp::ConvertUInt64ToFloat64); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } default: goto parse_error; } } case 'p': if (strcmp(op, "f64.copysign") == 0) { return makeBinary(s, BinaryOp::CopySignFloat64); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } case 'd': if (strcmp(op, "f64.div") == 0) { return makeBinary(s, BinaryOp::DivFloat64); } goto parse_error; case 'e': if (strcmp(op, "f64.eq") == 0) { return makeBinary(s, BinaryOp::EqFloat64); } goto parse_error; case 'f': if (strcmp(op, "f64.floor") == 0) { return makeUnary(s, UnaryOp::FloorFloat64); } goto parse_error; case 'g': { switch (op[5]) { case 'e': if (strcmp(op, "f64.ge") == 0) { return makeBinary(s, BinaryOp::GeFloat64); } goto parse_error; case 't': if (strcmp(op, "f64.gt") == 0) { return makeBinary(s, BinaryOp::GtFloat64); } goto parse_error; default: goto parse_error; } } case 'l': { switch (op[5]) { case 'e': if (strcmp(op, "f64.le") == 0) { return makeBinary(s, BinaryOp::LeFloat64); } goto parse_error; case 'o': if (strcmp(op, "f64.load") == 0) { return makeLoad(s, Type::f64, /*isAtomic=*/false); } goto parse_error; case 't': if (strcmp(op, "f64.lt") == 0) { return makeBinary(s, BinaryOp::LtFloat64); } goto parse_error; default: goto parse_error; } } case 'm': { switch (op[5]) { case 'a': if (strcmp(op, "f64.max") == 0) { return makeBinary(s, BinaryOp::MaxFloat64); } goto parse_error; case 'i': if (strcmp(op, "f64.min") == 0) { return makeBinary(s, BinaryOp::MinFloat64); } goto parse_error; case 'u': if (strcmp(op, "f64.mul") == 0) { return makeBinary(s, BinaryOp::MulFloat64); } goto parse_error; default: goto parse_error; } } case 'n': { switch (op[6]) { case '\0': if (strcmp(op, "f64.ne") == 0) { return makeBinary(s, BinaryOp::NeFloat64); } goto parse_error; case 'a': if (strcmp(op, "f64.nearest") == 0) { return makeUnary(s, UnaryOp::NearestFloat64); } goto parse_error; case 'g': if (strcmp(op, "f64.neg") == 0) { return makeUnary(s, UnaryOp::NegFloat64); } goto parse_error; default: goto parse_error; } } case 'p': { switch (op[5]) { case 'o': if (strcmp(op, "f64.pop") == 0) { return makePop(Type::f64); } goto parse_error; case 'r': if (strcmp(op, "f64.promote_f32") == 0) { return makeUnary(s, UnaryOp::PromoteFloat32); } goto parse_error; default: goto parse_error; } } case 'r': if (strcmp(op, "f64.reinterpret_i64") == 0) { return makeUnary(s, UnaryOp::ReinterpretInt64); } goto parse_error; case 's': { switch (op[5]) { case 'q': if (strcmp(op, "f64.sqrt") == 0) { return makeUnary(s, UnaryOp::SqrtFloat64); } goto parse_error; case 't': if (strcmp(op, "f64.store") == 0) { return makeStore(s, Type::f64, /*isAtomic=*/false); } goto parse_error; case 'u': if (strcmp(op, "f64.sub") == 0) { return makeBinary(s, BinaryOp::SubFloat64); } goto parse_error; default: goto parse_error; } } case 't': if (strcmp(op, "f64.trunc") == 0) { return makeUnary(s, UnaryOp::TruncFloat64); } goto parse_error; default: goto parse_error; } } case 'x': { switch (op[6]) { case 'a': { switch (op[7]) { case 'b': if (strcmp(op, "f64x2.abs") == 0) { return makeUnary(s, UnaryOp::AbsVecF64x2); } goto parse_error; case 'd': if (strcmp(op, "f64x2.add") == 0) { return makeBinary(s, BinaryOp::AddVecF64x2); } goto parse_error; default: goto parse_error; } } case 'c': { switch (op[20]) { case 's': if (strcmp(op, "f64x2.convert_i64x2_s") == 0) { return makeUnary(s, UnaryOp::ConvertSVecI64x2ToVecF64x2); } goto parse_error; case 'u': if (strcmp(op, "f64x2.convert_i64x2_u") == 0) { return makeUnary(s, UnaryOp::ConvertUVecI64x2ToVecF64x2); } goto parse_error; default: goto parse_error; } } case 'd': if (strcmp(op, "f64x2.div") == 0) { return makeBinary(s, BinaryOp::DivVecF64x2); } goto parse_error; case 'e': { switch (op[7]) { case 'q': if (strcmp(op, "f64x2.eq") == 0) { return makeBinary(s, BinaryOp::EqVecF64x2); } goto parse_error; case 'x': if (strcmp(op, "f64x2.extract_lane") == 0) { return makeSIMDExtract(s, SIMDExtractOp::ExtractLaneVecF64x2, 2); } goto parse_error; default: goto parse_error; } } case 'g': { switch (op[7]) { case 'e': if (strcmp(op, "f64x2.ge") == 0) { return makeBinary(s, BinaryOp::GeVecF64x2); } goto parse_error; case 't': if (strcmp(op, "f64x2.gt") == 0) { return makeBinary(s, BinaryOp::GtVecF64x2); } goto parse_error; default: goto parse_error; } } case 'l': { switch (op[7]) { case 'e': if (strcmp(op, "f64x2.le") == 0) { return makeBinary(s, BinaryOp::LeVecF64x2); } goto parse_error; case 't': if (strcmp(op, "f64x2.lt") == 0) { return makeBinary(s, BinaryOp::LtVecF64x2); } goto parse_error; default: goto parse_error; } } case 'm': { switch (op[7]) { case 'a': if (strcmp(op, "f64x2.max") == 0) { return makeBinary(s, BinaryOp::MaxVecF64x2); } goto parse_error; case 'i': if (strcmp(op, "f64x2.min") == 0) { return makeBinary(s, BinaryOp::MinVecF64x2); } goto parse_error; case 'u': if (strcmp(op, "f64x2.mul") == 0) { return makeBinary(s, BinaryOp::MulVecF64x2); } goto parse_error; default: goto parse_error; } } case 'n': { switch (op[8]) { case '\0': if (strcmp(op, "f64x2.ne") == 0) { return makeBinary(s, BinaryOp::NeVecF64x2); } goto parse_error; case 'g': if (strcmp(op, "f64x2.neg") == 0) { return makeUnary(s, UnaryOp::NegVecF64x2); } goto parse_error; default: goto parse_error; } } case 'q': { switch (op[9]) { case 'a': if (strcmp(op, "f64x2.qfma") == 0) { return makeSIMDTernary(s, SIMDTernaryOp::QFMAF64x2); } goto parse_error; case 's': if (strcmp(op, "f64x2.qfms") == 0) { return makeSIMDTernary(s, SIMDTernaryOp::QFMSF64x2); } goto parse_error; default: goto parse_error; } } case 'r': if (strcmp(op, "f64x2.replace_lane") == 0) { return makeSIMDReplace(s, SIMDReplaceOp::ReplaceLaneVecF64x2, 2); } goto parse_error; case 's': { switch (op[7]) { case 'p': if (strcmp(op, "f64x2.splat") == 0) { return makeUnary(s, UnaryOp::SplatVecF64x2); } goto parse_error; case 'q': if (strcmp(op, "f64x2.sqrt") == 0) { return makeUnary(s, UnaryOp::SqrtVecF64x2); } goto parse_error; case 'u': if (strcmp(op, "f64x2.sub") == 0) { return makeBinary(s, BinaryOp::SubVecF64x2); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } default: goto parse_error; } } case 'u': if (strcmp(op, "funcref.pop") == 0) { return makePop(Type::funcref); } goto parse_error; default: goto parse_error; } } case 'g': { switch (op[7]) { case 'g': if (strcmp(op, "global.get") == 0) { return makeGlobalGet(s); } goto parse_error; case 's': if (strcmp(op, "global.set") == 0) { return makeGlobalSet(s); } goto parse_error; default: goto parse_error; } } case 'i': { switch (op[1]) { case '1': { switch (op[6]) { case 'a': { switch (op[7]) { case 'd': { switch (op[9]) { case '\0': if (strcmp(op, "i16x8.add") == 0) { return makeBinary(s, BinaryOp::AddVecI16x8); } goto parse_error; case '_': { switch (op[19]) { case 's': if (strcmp(op, "i16x8.add_saturate_s") == 0) { return makeBinary(s, BinaryOp::AddSatSVecI16x8); } goto parse_error; case 'u': if (strcmp(op, "i16x8.add_saturate_u") == 0) { return makeBinary(s, BinaryOp::AddSatUVecI16x8); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } case 'l': if (strcmp(op, "i16x8.all_true") == 0) { return makeUnary(s, UnaryOp::AllTrueVecI16x8); } goto parse_error; case 'n': if (strcmp(op, "i16x8.any_true") == 0) { return makeUnary(s, UnaryOp::AnyTrueVecI16x8); } goto parse_error; case 'v': if (strcmp(op, "i16x8.avgr_u") == 0) { return makeBinary(s, BinaryOp::AvgrUVecI16x8); } goto parse_error; default: goto parse_error; } } case 'e': { switch (op[7]) { case 'q': if (strcmp(op, "i16x8.eq") == 0) { return makeBinary(s, BinaryOp::EqVecI16x8); } goto parse_error; case 'x': { switch (op[19]) { case 's': if (strcmp(op, "i16x8.extract_lane_s") == 0) { return makeSIMDExtract(s, SIMDExtractOp::ExtractLaneSVecI16x8, 8); } goto parse_error; case 'u': if (strcmp(op, "i16x8.extract_lane_u") == 0) { return makeSIMDExtract(s, SIMDExtractOp::ExtractLaneUVecI16x8, 8); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } case 'g': { switch (op[7]) { case 'e': { switch (op[9]) { case 's': if (strcmp(op, "i16x8.ge_s") == 0) { return makeBinary(s, BinaryOp::GeSVecI16x8); } goto parse_error; case 'u': if (strcmp(op, "i16x8.ge_u") == 0) { return makeBinary(s, BinaryOp::GeUVecI16x8); } goto parse_error; default: goto parse_error; } } case 't': { switch (op[9]) { case 's': if (strcmp(op, "i16x8.gt_s") == 0) { return makeBinary(s, BinaryOp::GtSVecI16x8); } goto parse_error; case 'u': if (strcmp(op, "i16x8.gt_u") == 0) { return makeBinary(s, BinaryOp::GtUVecI16x8); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } case 'l': { switch (op[7]) { case 'e': { switch (op[9]) { case 's': if (strcmp(op, "i16x8.le_s") == 0) { return makeBinary(s, BinaryOp::LeSVecI16x8); } goto parse_error; case 'u': if (strcmp(op, "i16x8.le_u") == 0) { return makeBinary(s, BinaryOp::LeUVecI16x8); } goto parse_error; default: goto parse_error; } } case 'o': { switch (op[14]) { case 's': if (strcmp(op, "i16x8.load8x8_s") == 0) { return makeSIMDLoad(s, SIMDLoadOp::LoadExtSVec8x8ToVecI16x8); } goto parse_error; case 'u': if (strcmp(op, "i16x8.load8x8_u") == 0) { return makeSIMDLoad(s, SIMDLoadOp::LoadExtUVec8x8ToVecI16x8); } goto parse_error; default: goto parse_error; } } case 't': { switch (op[9]) { case 's': if (strcmp(op, "i16x8.lt_s") == 0) { return makeBinary(s, BinaryOp::LtSVecI16x8); } goto parse_error; case 'u': if (strcmp(op, "i16x8.lt_u") == 0) { return makeBinary(s, BinaryOp::LtUVecI16x8); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } case 'm': { switch (op[7]) { case 'a': { switch (op[10]) { case 's': if (strcmp(op, "i16x8.max_s") == 0) { return makeBinary(s, BinaryOp::MaxSVecI16x8); } goto parse_error; case 'u': if (strcmp(op, "i16x8.max_u") == 0) { return makeBinary(s, BinaryOp::MaxUVecI16x8); } goto parse_error; default: goto parse_error; } } case 'i': { switch (op[10]) { case 's': if (strcmp(op, "i16x8.min_s") == 0) { return makeBinary(s, BinaryOp::MinSVecI16x8); } goto parse_error; case 'u': if (strcmp(op, "i16x8.min_u") == 0) { return makeBinary(s, BinaryOp::MinUVecI16x8); } goto parse_error; default: goto parse_error; } } case 'u': if (strcmp(op, "i16x8.mul") == 0) { return makeBinary(s, BinaryOp::MulVecI16x8); } goto parse_error; default: goto parse_error; } } case 'n': { switch (op[7]) { case 'a': { switch (op[19]) { case 's': if (strcmp(op, "i16x8.narrow_i32x4_s") == 0) { return makeBinary(s, BinaryOp::NarrowSVecI32x4ToVecI16x8); } goto parse_error; case 'u': if (strcmp(op, "i16x8.narrow_i32x4_u") == 0) { return makeBinary(s, BinaryOp::NarrowUVecI32x4ToVecI16x8); } goto parse_error; default: goto parse_error; } } case 'e': { switch (op[8]) { case '\0': if (strcmp(op, "i16x8.ne") == 0) { return makeBinary(s, BinaryOp::NeVecI16x8); } goto parse_error; case 'g': if (strcmp(op, "i16x8.neg") == 0) { return makeUnary(s, UnaryOp::NegVecI16x8); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } case 'r': if (strcmp(op, "i16x8.replace_lane") == 0) { return makeSIMDReplace(s, SIMDReplaceOp::ReplaceLaneVecI16x8, 8); } goto parse_error; case 's': { switch (op[7]) { case 'h': { switch (op[8]) { case 'l': if (strcmp(op, "i16x8.shl") == 0) { return makeSIMDShift(s, SIMDShiftOp::ShlVecI16x8); } goto parse_error; case 'r': { switch (op[10]) { case 's': if (strcmp(op, "i16x8.shr_s") == 0) { return makeSIMDShift(s, SIMDShiftOp::ShrSVecI16x8); } goto parse_error; case 'u': if (strcmp(op, "i16x8.shr_u") == 0) { return makeSIMDShift(s, SIMDShiftOp::ShrUVecI16x8); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } case 'p': if (strcmp(op, "i16x8.splat") == 0) { return makeUnary(s, UnaryOp::SplatVecI16x8); } goto parse_error; case 'u': { switch (op[9]) { case '\0': if (strcmp(op, "i16x8.sub") == 0) { return makeBinary(s, BinaryOp::SubVecI16x8); } goto parse_error; case '_': { switch (op[19]) { case 's': if (strcmp(op, "i16x8.sub_saturate_s") == 0) { return makeBinary(s, BinaryOp::SubSatSVecI16x8); } goto parse_error; case 'u': if (strcmp(op, "i16x8.sub_saturate_u") == 0) { return makeBinary(s, BinaryOp::SubSatUVecI16x8); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } default: goto parse_error; } } case 'w': { switch (op[12]) { case 'h': { switch (op[23]) { case 's': if (strcmp(op, "i16x8.widen_high_i8x16_s") == 0) { return makeUnary(s, UnaryOp::WidenHighSVecI8x16ToVecI16x8); } goto parse_error; case 'u': if (strcmp(op, "i16x8.widen_high_i8x16_u") == 0) { return makeUnary(s, UnaryOp::WidenHighUVecI8x16ToVecI16x8); } goto parse_error; default: goto parse_error; } } case 'l': { switch (op[22]) { case 's': if (strcmp(op, "i16x8.widen_low_i8x16_s") == 0) { return makeUnary(s, UnaryOp::WidenLowSVecI8x16ToVecI16x8); } goto parse_error; case 'u': if (strcmp(op, "i16x8.widen_low_i8x16_u") == 0) { return makeUnary(s, UnaryOp::WidenLowUVecI8x16ToVecI16x8); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } default: goto parse_error; } } case '3': { switch (op[3]) { case '.': { switch (op[4]) { case 'a': { switch (op[5]) { case 'd': if (strcmp(op, "i32.add") == 0) { return makeBinary(s, BinaryOp::AddInt32); } goto parse_error; case 'n': if (strcmp(op, "i32.and") == 0) { return makeBinary(s, BinaryOp::AndInt32); } goto parse_error; case 't': { switch (op[11]) { case 'l': { switch (op[15]) { case '\0': if (strcmp(op, "i32.atomic.load") == 0) { return makeLoad(s, Type::i32, /*isAtomic=*/true); } goto parse_error; case '1': if (strcmp(op, "i32.atomic.load16_u") == 0) { return makeLoad(s, Type::i32, /*isAtomic=*/true); } goto parse_error; case '8': if (strcmp(op, "i32.atomic.load8_u") == 0) { return makeLoad(s, Type::i32, /*isAtomic=*/true); } goto parse_error; default: goto parse_error; } } case 'r': { switch (op[14]) { case '.': { switch (op[15]) { case 'a': { switch (op[16]) { case 'd': if (strcmp(op, "i32.atomic.rmw.add") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } goto parse_error; case 'n': if (strcmp(op, "i32.atomic.rmw.and") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } goto parse_error; default: goto parse_error; } } case 'c': if (strcmp(op, "i32.atomic.rmw.cmpxchg") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } goto parse_error; case 'o': if (strcmp(op, "i32.atomic.rmw.or") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } goto parse_error; case 's': if (strcmp(op, "i32.atomic.rmw.sub") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } goto parse_error; case 'x': { switch (op[16]) { case 'c': if (strcmp(op, "i32.atomic.rmw.xchg") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } goto parse_error; case 'o': if (strcmp(op, "i32.atomic.rmw.xor") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } case '1': { switch (op[17]) { case 'a': { switch (op[18]) { case 'd': if (strcmp(op, "i32.atomic.rmw16.add_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } goto parse_error; case 'n': if (strcmp(op, "i32.atomic.rmw16.and_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } goto parse_error; default: goto parse_error; } } case 'c': if (strcmp(op, "i32.atomic.rmw16.cmpxchg_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } goto parse_error; case 'o': if (strcmp(op, "i32.atomic.rmw16.or_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } goto parse_error; case 's': if (strcmp(op, "i32.atomic.rmw16.sub_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } goto parse_error; case 'x': { switch (op[18]) { case 'c': if (strcmp(op, "i32.atomic.rmw16.xchg_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } goto parse_error; case 'o': if (strcmp(op, "i32.atomic.rmw16.xor_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } case '8': { switch (op[16]) { case 'a': { switch (op[17]) { case 'd': if (strcmp(op, "i32.atomic.rmw8.add_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } goto parse_error; case 'n': if (strcmp(op, "i32.atomic.rmw8.and_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } goto parse_error; default: goto parse_error; } } case 'c': if (strcmp(op, "i32.atomic.rmw8.cmpxchg_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } goto parse_error; case 'o': if (strcmp(op, "i32.atomic.rmw8.or_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } goto parse_error; case 's': if (strcmp(op, "i32.atomic.rmw8.sub_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } goto parse_error; case 'x': { switch (op[17]) { case 'c': if (strcmp(op, "i32.atomic.rmw8.xchg_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } goto parse_error; case 'o': if (strcmp(op, "i32.atomic.rmw8.xor_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i32); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } default: goto parse_error; } } case 's': { switch (op[16]) { case '\0': if (strcmp(op, "i32.atomic.store") == 0) { return makeStore(s, Type::i32, /*isAtomic=*/true); } goto parse_error; case '1': if (strcmp(op, "i32.atomic.store16") == 0) { return makeStore(s, Type::i32, /*isAtomic=*/true); } goto parse_error; case '8': if (strcmp(op, "i32.atomic.store8") == 0) { return makeStore(s, Type::i32, /*isAtomic=*/true); } goto parse_error; default: goto parse_error; } } case 'w': if (strcmp(op, "i32.atomic.wait") == 0) { return makeAtomicWait(s, Type::i32); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } case 'c': { switch (op[5]) { case 'l': if (strcmp(op, "i32.clz") == 0) { return makeUnary(s, UnaryOp::ClzInt32); } goto parse_error; case 'o': if (strcmp(op, "i32.const") == 0) { return makeConst(s, Type::i32); } goto parse_error; case 't': if (strcmp(op, "i32.ctz") == 0) { return makeUnary(s, UnaryOp::CtzInt32); } goto parse_error; default: goto parse_error; } } case 'd': { switch (op[8]) { case 's': if (strcmp(op, "i32.div_s") == 0) { return makeBinary(s, BinaryOp::DivSInt32); } goto parse_error; case 'u': if (strcmp(op, "i32.div_u") == 0) { return makeBinary(s, BinaryOp::DivUInt32); } goto parse_error; default: goto parse_error; } } case 'e': { switch (op[5]) { case 'q': { switch (op[6]) { case '\0': if (strcmp(op, "i32.eq") == 0) { return makeBinary(s, BinaryOp::EqInt32); } goto parse_error; case 'z': if (strcmp(op, "i32.eqz") == 0) { return makeUnary(s, UnaryOp::EqZInt32); } goto parse_error; default: goto parse_error; } } case 'x': { switch (op[10]) { case '1': if (strcmp(op, "i32.extend16_s") == 0) { return makeUnary(s, UnaryOp::ExtendS16Int32); } goto parse_error; case '8': if (strcmp(op, "i32.extend8_s") == 0) { return makeUnary(s, UnaryOp::ExtendS8Int32); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } case 'g': { switch (op[5]) { case 'e': { switch (op[7]) { case 's': if (strcmp(op, "i32.ge_s") == 0) { return makeBinary(s, BinaryOp::GeSInt32); } goto parse_error; case 'u': if (strcmp(op, "i32.ge_u") == 0) { return makeBinary(s, BinaryOp::GeUInt32); } goto parse_error; default: goto parse_error; } } case 't': { switch (op[7]) { case 's': if (strcmp(op, "i32.gt_s") == 0) { return makeBinary(s, BinaryOp::GtSInt32); } goto parse_error; case 'u': if (strcmp(op, "i32.gt_u") == 0) { return makeBinary(s, BinaryOp::GtUInt32); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } case 'l': { switch (op[5]) { case 'e': { switch (op[7]) { case 's': if (strcmp(op, "i32.le_s") == 0) { return makeBinary(s, BinaryOp::LeSInt32); } goto parse_error; case 'u': if (strcmp(op, "i32.le_u") == 0) { return makeBinary(s, BinaryOp::LeUInt32); } goto parse_error; default: goto parse_error; } } case 'o': { switch (op[8]) { case '\0': if (strcmp(op, "i32.load") == 0) { return makeLoad(s, Type::i32, /*isAtomic=*/false); } goto parse_error; case '1': { switch (op[11]) { case 's': if (strcmp(op, "i32.load16_s") == 0) { return makeLoad(s, Type::i32, /*isAtomic=*/false); } goto parse_error; case 'u': if (strcmp(op, "i32.load16_u") == 0) { return makeLoad(s, Type::i32, /*isAtomic=*/false); } goto parse_error; default: goto parse_error; } } case '8': { switch (op[10]) { case 's': if (strcmp(op, "i32.load8_s") == 0) { return makeLoad(s, Type::i32, /*isAtomic=*/false); } goto parse_error; case 'u': if (strcmp(op, "i32.load8_u") == 0) { return makeLoad(s, Type::i32, /*isAtomic=*/false); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } case 't': { switch (op[7]) { case 's': if (strcmp(op, "i32.lt_s") == 0) { return makeBinary(s, BinaryOp::LtSInt32); } goto parse_error; case 'u': if (strcmp(op, "i32.lt_u") == 0) { return makeBinary(s, BinaryOp::LtUInt32); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } case 'm': if (strcmp(op, "i32.mul") == 0) { return makeBinary(s, BinaryOp::MulInt32); } goto parse_error; case 'n': if (strcmp(op, "i32.ne") == 0) { return makeBinary(s, BinaryOp::NeInt32); } goto parse_error; case 'o': if (strcmp(op, "i32.or") == 0) { return makeBinary(s, BinaryOp::OrInt32); } goto parse_error; case 'p': { switch (op[7]) { case '\0': if (strcmp(op, "i32.pop") == 0) { return makePop(Type::i32); } goto parse_error; case 'c': if (strcmp(op, "i32.popcnt") == 0) { return makeUnary(s, UnaryOp::PopcntInt32); } goto parse_error; default: goto parse_error; } } case 'r': { switch (op[5]) { case 'e': { switch (op[6]) { case 'i': if (strcmp(op, "i32.reinterpret_f32") == 0) { return makeUnary(s, UnaryOp::ReinterpretFloat32); } goto parse_error; case 'm': { switch (op[8]) { case 's': if (strcmp(op, "i32.rem_s") == 0) { return makeBinary(s, BinaryOp::RemSInt32); } goto parse_error; case 'u': if (strcmp(op, "i32.rem_u") == 0) { return makeBinary(s, BinaryOp::RemUInt32); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } case 'o': { switch (op[7]) { case 'l': if (strcmp(op, "i32.rotl") == 0) { return makeBinary(s, BinaryOp::RotLInt32); } goto parse_error; case 'r': if (strcmp(op, "i32.rotr") == 0) { return makeBinary(s, BinaryOp::RotRInt32); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } case 's': { switch (op[5]) { case 'h': { switch (op[6]) { case 'l': if (strcmp(op, "i32.shl") == 0) { return makeBinary(s, BinaryOp::ShlInt32); } goto parse_error; case 'r': { switch (op[8]) { case 's': if (strcmp(op, "i32.shr_s") == 0) { return makeBinary(s, BinaryOp::ShrSInt32); } goto parse_error; case 'u': if (strcmp(op, "i32.shr_u") == 0) { return makeBinary(s, BinaryOp::ShrUInt32); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } case 't': { switch (op[9]) { case '\0': if (strcmp(op, "i32.store") == 0) { return makeStore(s, Type::i32, /*isAtomic=*/false); } goto parse_error; case '1': if (strcmp(op, "i32.store16") == 0) { return makeStore(s, Type::i32, /*isAtomic=*/false); } goto parse_error; case '8': if (strcmp(op, "i32.store8") == 0) { return makeStore(s, Type::i32, /*isAtomic=*/false); } goto parse_error; default: goto parse_error; } } case 'u': if (strcmp(op, "i32.sub") == 0) { return makeBinary(s, BinaryOp::SubInt32); } goto parse_error; default: goto parse_error; } } case 't': { switch (op[10]) { case 'f': { switch (op[11]) { case '3': { switch (op[14]) { case 's': if (strcmp(op, "i32.trunc_f32_s") == 0) { return makeUnary(s, UnaryOp::TruncSFloat32ToInt32); } goto parse_error; case 'u': if (strcmp(op, "i32.trunc_f32_u") == 0) { return makeUnary(s, UnaryOp::TruncUFloat32ToInt32); } goto parse_error; default: goto parse_error; } } case '6': { switch (op[14]) { case 's': if (strcmp(op, "i32.trunc_f64_s") == 0) { return makeUnary(s, UnaryOp::TruncSFloat64ToInt32); } goto parse_error; case 'u': if (strcmp(op, "i32.trunc_f64_u") == 0) { return makeUnary(s, UnaryOp::TruncUFloat64ToInt32); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } case 's': { switch (op[15]) { case '3': { switch (op[18]) { case 's': if (strcmp(op, "i32.trunc_sat_f32_s") == 0) { return makeUnary(s, UnaryOp::TruncSatSFloat32ToInt32); } goto parse_error; case 'u': if (strcmp(op, "i32.trunc_sat_f32_u") == 0) { return makeUnary(s, UnaryOp::TruncSatUFloat32ToInt32); } goto parse_error; default: goto parse_error; } } case '6': { switch (op[18]) { case 's': if (strcmp(op, "i32.trunc_sat_f64_s") == 0) { return makeUnary(s, UnaryOp::TruncSatSFloat64ToInt32); } goto parse_error; case 'u': if (strcmp(op, "i32.trunc_sat_f64_u") == 0) { return makeUnary(s, UnaryOp::TruncSatUFloat64ToInt32); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } default: goto parse_error; } } case 'w': if (strcmp(op, "i32.wrap_i64") == 0) { return makeUnary(s, UnaryOp::WrapInt64); } goto parse_error; case 'x': if (strcmp(op, "i32.xor") == 0) { return makeBinary(s, BinaryOp::XorInt32); } goto parse_error; default: goto parse_error; } } case 'x': { switch (op[6]) { case 'a': { switch (op[7]) { case 'd': if (strcmp(op, "i32x4.add") == 0) { return makeBinary(s, BinaryOp::AddVecI32x4); } goto parse_error; case 'l': if (strcmp(op, "i32x4.all_true") == 0) { return makeUnary(s, UnaryOp::AllTrueVecI32x4); } goto parse_error; case 'n': if (strcmp(op, "i32x4.any_true") == 0) { return makeUnary(s, UnaryOp::AnyTrueVecI32x4); } goto parse_error; default: goto parse_error; } } case 'd': if (strcmp(op, "i32x4.dot_i16x8_s") == 0) { return makeBinary(s, BinaryOp::DotSVecI16x8ToVecI32x4); } goto parse_error; case 'e': { switch (op[7]) { case 'q': if (strcmp(op, "i32x4.eq") == 0) { return makeBinary(s, BinaryOp::EqVecI32x4); } goto parse_error; case 'x': if (strcmp(op, "i32x4.extract_lane") == 0) { return makeSIMDExtract(s, SIMDExtractOp::ExtractLaneVecI32x4, 4); } goto parse_error; default: goto parse_error; } } case 'g': { switch (op[7]) { case 'e': { switch (op[9]) { case 's': if (strcmp(op, "i32x4.ge_s") == 0) { return makeBinary(s, BinaryOp::GeSVecI32x4); } goto parse_error; case 'u': if (strcmp(op, "i32x4.ge_u") == 0) { return makeBinary(s, BinaryOp::GeUVecI32x4); } goto parse_error; default: goto parse_error; } } case 't': { switch (op[9]) { case 's': if (strcmp(op, "i32x4.gt_s") == 0) { return makeBinary(s, BinaryOp::GtSVecI32x4); } goto parse_error; case 'u': if (strcmp(op, "i32x4.gt_u") == 0) { return makeBinary(s, BinaryOp::GtUVecI32x4); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } case 'l': { switch (op[7]) { case 'e': { switch (op[9]) { case 's': if (strcmp(op, "i32x4.le_s") == 0) { return makeBinary(s, BinaryOp::LeSVecI32x4); } goto parse_error; case 'u': if (strcmp(op, "i32x4.le_u") == 0) { return makeBinary(s, BinaryOp::LeUVecI32x4); } goto parse_error; default: goto parse_error; } } case 'o': { switch (op[15]) { case 's': if (strcmp(op, "i32x4.load16x4_s") == 0) { return makeSIMDLoad(s, SIMDLoadOp::LoadExtSVec16x4ToVecI32x4); } goto parse_error; case 'u': if (strcmp(op, "i32x4.load16x4_u") == 0) { return makeSIMDLoad(s, SIMDLoadOp::LoadExtUVec16x4ToVecI32x4); } goto parse_error; default: goto parse_error; } } case 't': { switch (op[9]) { case 's': if (strcmp(op, "i32x4.lt_s") == 0) { return makeBinary(s, BinaryOp::LtSVecI32x4); } goto parse_error; case 'u': if (strcmp(op, "i32x4.lt_u") == 0) { return makeBinary(s, BinaryOp::LtUVecI32x4); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } case 'm': { switch (op[7]) { case 'a': { switch (op[10]) { case 's': if (strcmp(op, "i32x4.max_s") == 0) { return makeBinary(s, BinaryOp::MaxSVecI32x4); } goto parse_error; case 'u': if (strcmp(op, "i32x4.max_u") == 0) { return makeBinary(s, BinaryOp::MaxUVecI32x4); } goto parse_error; default: goto parse_error; } } case 'i': { switch (op[10]) { case 's': if (strcmp(op, "i32x4.min_s") == 0) { return makeBinary(s, BinaryOp::MinSVecI32x4); } goto parse_error; case 'u': if (strcmp(op, "i32x4.min_u") == 0) { return makeBinary(s, BinaryOp::MinUVecI32x4); } goto parse_error; default: goto parse_error; } } case 'u': if (strcmp(op, "i32x4.mul") == 0) { return makeBinary(s, BinaryOp::MulVecI32x4); } goto parse_error; default: goto parse_error; } } case 'n': { switch (op[8]) { case '\0': if (strcmp(op, "i32x4.ne") == 0) { return makeBinary(s, BinaryOp::NeVecI32x4); } goto parse_error; case 'g': if (strcmp(op, "i32x4.neg") == 0) { return makeUnary(s, UnaryOp::NegVecI32x4); } goto parse_error; default: goto parse_error; } } case 'r': if (strcmp(op, "i32x4.replace_lane") == 0) { return makeSIMDReplace(s, SIMDReplaceOp::ReplaceLaneVecI32x4, 4); } goto parse_error; case 's': { switch (op[7]) { case 'h': { switch (op[8]) { case 'l': if (strcmp(op, "i32x4.shl") == 0) { return makeSIMDShift(s, SIMDShiftOp::ShlVecI32x4); } goto parse_error; case 'r': { switch (op[10]) { case 's': if (strcmp(op, "i32x4.shr_s") == 0) { return makeSIMDShift(s, SIMDShiftOp::ShrSVecI32x4); } goto parse_error; case 'u': if (strcmp(op, "i32x4.shr_u") == 0) { return makeSIMDShift(s, SIMDShiftOp::ShrUVecI32x4); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } case 'p': if (strcmp(op, "i32x4.splat") == 0) { return makeUnary(s, UnaryOp::SplatVecI32x4); } goto parse_error; case 'u': if (strcmp(op, "i32x4.sub") == 0) { return makeBinary(s, BinaryOp::SubVecI32x4); } goto parse_error; default: goto parse_error; } } case 't': { switch (op[22]) { case 's': if (strcmp(op, "i32x4.trunc_sat_f32x4_s") == 0) { return makeUnary(s, UnaryOp::TruncSatSVecF32x4ToVecI32x4); } goto parse_error; case 'u': if (strcmp(op, "i32x4.trunc_sat_f32x4_u") == 0) { return makeUnary(s, UnaryOp::TruncSatUVecF32x4ToVecI32x4); } goto parse_error; default: goto parse_error; } } case 'w': { switch (op[12]) { case 'h': { switch (op[23]) { case 's': if (strcmp(op, "i32x4.widen_high_i16x8_s") == 0) { return makeUnary(s, UnaryOp::WidenHighSVecI16x8ToVecI32x4); } goto parse_error; case 'u': if (strcmp(op, "i32x4.widen_high_i16x8_u") == 0) { return makeUnary(s, UnaryOp::WidenHighUVecI16x8ToVecI32x4); } goto parse_error; default: goto parse_error; } } case 'l': { switch (op[22]) { case 's': if (strcmp(op, "i32x4.widen_low_i16x8_s") == 0) { return makeUnary(s, UnaryOp::WidenLowSVecI16x8ToVecI32x4); } goto parse_error; case 'u': if (strcmp(op, "i32x4.widen_low_i16x8_u") == 0) { return makeUnary(s, UnaryOp::WidenLowUVecI16x8ToVecI32x4); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } default: goto parse_error; } } default: goto parse_error; } } case '6': { switch (op[3]) { case '.': { switch (op[4]) { case 'a': { switch (op[5]) { case 'd': if (strcmp(op, "i64.add") == 0) { return makeBinary(s, BinaryOp::AddInt64); } goto parse_error; case 'n': if (strcmp(op, "i64.and") == 0) { return makeBinary(s, BinaryOp::AndInt64); } goto parse_error; case 't': { switch (op[11]) { case 'l': { switch (op[15]) { case '\0': if (strcmp(op, "i64.atomic.load") == 0) { return makeLoad(s, Type::i64, /*isAtomic=*/true); } goto parse_error; case '1': if (strcmp(op, "i64.atomic.load16_u") == 0) { return makeLoad(s, Type::i64, /*isAtomic=*/true); } goto parse_error; case '3': if (strcmp(op, "i64.atomic.load32_u") == 0) { return makeLoad(s, Type::i64, /*isAtomic=*/true); } goto parse_error; case '8': if (strcmp(op, "i64.atomic.load8_u") == 0) { return makeLoad(s, Type::i64, /*isAtomic=*/true); } goto parse_error; default: goto parse_error; } } case 'r': { switch (op[14]) { case '.': { switch (op[15]) { case 'a': { switch (op[16]) { case 'd': if (strcmp(op, "i64.atomic.rmw.add") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i64); } goto parse_error; case 'n': if (strcmp(op, "i64.atomic.rmw.and") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i64); } goto parse_error; default: goto parse_error; } } case 'c': if (strcmp(op, "i64.atomic.rmw.cmpxchg") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i64); } goto parse_error; case 'o': if (strcmp(op, "i64.atomic.rmw.or") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i64); } goto parse_error; case 's': if (strcmp(op, "i64.atomic.rmw.sub") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i64); } goto parse_error; case 'x': { switch (op[16]) { case 'c': if (strcmp(op, "i64.atomic.rmw.xchg") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i64); } goto parse_error; case 'o': if (strcmp(op, "i64.atomic.rmw.xor") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i64); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } case '1': { switch (op[17]) { case 'a': { switch (op[18]) { case 'd': if (strcmp(op, "i64.atomic.rmw16.add_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i64); } goto parse_error; case 'n': if (strcmp(op, "i64.atomic.rmw16.and_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i64); } goto parse_error; default: goto parse_error; } } case 'c': if (strcmp(op, "i64.atomic.rmw16.cmpxchg_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i64); } goto parse_error; case 'o': if (strcmp(op, "i64.atomic.rmw16.or_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i64); } goto parse_error; case 's': if (strcmp(op, "i64.atomic.rmw16.sub_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i64); } goto parse_error; case 'x': { switch (op[18]) { case 'c': if (strcmp(op, "i64.atomic.rmw16.xchg_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i64); } goto parse_error; case 'o': if (strcmp(op, "i64.atomic.rmw16.xor_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i64); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } case '3': { switch (op[17]) { case 'a': { switch (op[18]) { case 'd': if (strcmp(op, "i64.atomic.rmw32.add_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i64); } goto parse_error; case 'n': if (strcmp(op, "i64.atomic.rmw32.and_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i64); } goto parse_error; default: goto parse_error; } } case 'c': if (strcmp(op, "i64.atomic.rmw32.cmpxchg_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i64); } goto parse_error; case 'o': if (strcmp(op, "i64.atomic.rmw32.or_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i64); } goto parse_error; case 's': if (strcmp(op, "i64.atomic.rmw32.sub_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i64); } goto parse_error; case 'x': { switch (op[18]) { case 'c': if (strcmp(op, "i64.atomic.rmw32.xchg_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i64); } goto parse_error; case 'o': if (strcmp(op, "i64.atomic.rmw32.xor_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i64); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } case '8': { switch (op[16]) { case 'a': { switch (op[17]) { case 'd': if (strcmp(op, "i64.atomic.rmw8.add_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i64); } goto parse_error; case 'n': if (strcmp(op, "i64.atomic.rmw8.and_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i64); } goto parse_error; default: goto parse_error; } } case 'c': if (strcmp(op, "i64.atomic.rmw8.cmpxchg_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i64); } goto parse_error; case 'o': if (strcmp(op, "i64.atomic.rmw8.or_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i64); } goto parse_error; case 's': if (strcmp(op, "i64.atomic.rmw8.sub_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i64); } goto parse_error; case 'x': { switch (op[17]) { case 'c': if (strcmp(op, "i64.atomic.rmw8.xchg_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i64); } goto parse_error; case 'o': if (strcmp(op, "i64.atomic.rmw8.xor_u") == 0) { return makeAtomicRMWOrCmpxchg(s, Type::i64); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } default: goto parse_error; } } case 's': { switch (op[16]) { case '\0': if (strcmp(op, "i64.atomic.store") == 0) { return makeStore(s, Type::i64, /*isAtomic=*/true); } goto parse_error; case '1': if (strcmp(op, "i64.atomic.store16") == 0) { return makeStore(s, Type::i64, /*isAtomic=*/true); } goto parse_error; case '3': if (strcmp(op, "i64.atomic.store32") == 0) { return makeStore(s, Type::i64, /*isAtomic=*/true); } goto parse_error; case '8': if (strcmp(op, "i64.atomic.store8") == 0) { return makeStore(s, Type::i64, /*isAtomic=*/true); } goto parse_error; default: goto parse_error; } } case 'w': if (strcmp(op, "i64.atomic.wait") == 0) { return makeAtomicWait(s, Type::i64); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } case 'c': { switch (op[5]) { case 'l': if (strcmp(op, "i64.clz") == 0) { return makeUnary(s, UnaryOp::ClzInt64); } goto parse_error; case 'o': if (strcmp(op, "i64.const") == 0) { return makeConst(s, Type::i64); } goto parse_error; case 't': if (strcmp(op, "i64.ctz") == 0) { return makeUnary(s, UnaryOp::CtzInt64); } goto parse_error; default: goto parse_error; } } case 'd': { switch (op[8]) { case 's': if (strcmp(op, "i64.div_s") == 0) { return makeBinary(s, BinaryOp::DivSInt64); } goto parse_error; case 'u': if (strcmp(op, "i64.div_u") == 0) { return makeBinary(s, BinaryOp::DivUInt64); } goto parse_error; default: goto parse_error; } } case 'e': { switch (op[5]) { case 'q': { switch (op[6]) { case '\0': if (strcmp(op, "i64.eq") == 0) { return makeBinary(s, BinaryOp::EqInt64); } goto parse_error; case 'z': if (strcmp(op, "i64.eqz") == 0) { return makeUnary(s, UnaryOp::EqZInt64); } goto parse_error; default: goto parse_error; } } case 'x': { switch (op[10]) { case '1': if (strcmp(op, "i64.extend16_s") == 0) { return makeUnary(s, UnaryOp::ExtendS16Int64); } goto parse_error; case '3': if (strcmp(op, "i64.extend32_s") == 0) { return makeUnary(s, UnaryOp::ExtendS32Int64); } goto parse_error; case '8': if (strcmp(op, "i64.extend8_s") == 0) { return makeUnary(s, UnaryOp::ExtendS8Int64); } goto parse_error; case '_': { switch (op[15]) { case 's': if (strcmp(op, "i64.extend_i32_s") == 0) { return makeUnary(s, UnaryOp::ExtendSInt32); } goto parse_error; case 'u': if (strcmp(op, "i64.extend_i32_u") == 0) { return makeUnary(s, UnaryOp::ExtendUInt32); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } default: goto parse_error; } } case 'g': { switch (op[5]) { case 'e': { switch (op[7]) { case 's': if (strcmp(op, "i64.ge_s") == 0) { return makeBinary(s, BinaryOp::GeSInt64); } goto parse_error; case 'u': if (strcmp(op, "i64.ge_u") == 0) { return makeBinary(s, BinaryOp::GeUInt64); } goto parse_error; default: goto parse_error; } } case 't': { switch (op[7]) { case 's': if (strcmp(op, "i64.gt_s") == 0) { return makeBinary(s, BinaryOp::GtSInt64); } goto parse_error; case 'u': if (strcmp(op, "i64.gt_u") == 0) { return makeBinary(s, BinaryOp::GtUInt64); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } case 'l': { switch (op[5]) { case 'e': { switch (op[7]) { case 's': if (strcmp(op, "i64.le_s") == 0) { return makeBinary(s, BinaryOp::LeSInt64); } goto parse_error; case 'u': if (strcmp(op, "i64.le_u") == 0) { return makeBinary(s, BinaryOp::LeUInt64); } goto parse_error; default: goto parse_error; } } case 'o': { switch (op[8]) { case '\0': if (strcmp(op, "i64.load") == 0) { return makeLoad(s, Type::i64, /*isAtomic=*/false); } goto parse_error; case '1': { switch (op[11]) { case 's': if (strcmp(op, "i64.load16_s") == 0) { return makeLoad(s, Type::i64, /*isAtomic=*/false); } goto parse_error; case 'u': if (strcmp(op, "i64.load16_u") == 0) { return makeLoad(s, Type::i64, /*isAtomic=*/false); } goto parse_error; default: goto parse_error; } } case '3': { switch (op[11]) { case 's': if (strcmp(op, "i64.load32_s") == 0) { return makeLoad(s, Type::i64, /*isAtomic=*/false); } goto parse_error; case 'u': if (strcmp(op, "i64.load32_u") == 0) { return makeLoad(s, Type::i64, /*isAtomic=*/false); } goto parse_error; default: goto parse_error; } } case '8': { switch (op[10]) { case 's': if (strcmp(op, "i64.load8_s") == 0) { return makeLoad(s, Type::i64, /*isAtomic=*/false); } goto parse_error; case 'u': if (strcmp(op, "i64.load8_u") == 0) { return makeLoad(s, Type::i64, /*isAtomic=*/false); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } case 't': { switch (op[7]) { case 's': if (strcmp(op, "i64.lt_s") == 0) { return makeBinary(s, BinaryOp::LtSInt64); } goto parse_error; case 'u': if (strcmp(op, "i64.lt_u") == 0) { return makeBinary(s, BinaryOp::LtUInt64); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } case 'm': if (strcmp(op, "i64.mul") == 0) { return makeBinary(s, BinaryOp::MulInt64); } goto parse_error; case 'n': if (strcmp(op, "i64.ne") == 0) { return makeBinary(s, BinaryOp::NeInt64); } goto parse_error; case 'o': if (strcmp(op, "i64.or") == 0) { return makeBinary(s, BinaryOp::OrInt64); } goto parse_error; case 'p': { switch (op[7]) { case '\0': if (strcmp(op, "i64.pop") == 0) { return makePop(Type::i64); } goto parse_error; case 'c': if (strcmp(op, "i64.popcnt") == 0) { return makeUnary(s, UnaryOp::PopcntInt64); } goto parse_error; default: goto parse_error; } } case 'r': { switch (op[5]) { case 'e': { switch (op[6]) { case 'i': if (strcmp(op, "i64.reinterpret_f64") == 0) { return makeUnary(s, UnaryOp::ReinterpretFloat64); } goto parse_error; case 'm': { switch (op[8]) { case 's': if (strcmp(op, "i64.rem_s") == 0) { return makeBinary(s, BinaryOp::RemSInt64); } goto parse_error; case 'u': if (strcmp(op, "i64.rem_u") == 0) { return makeBinary(s, BinaryOp::RemUInt64); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } case 'o': { switch (op[7]) { case 'l': if (strcmp(op, "i64.rotl") == 0) { return makeBinary(s, BinaryOp::RotLInt64); } goto parse_error; case 'r': if (strcmp(op, "i64.rotr") == 0) { return makeBinary(s, BinaryOp::RotRInt64); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } case 's': { switch (op[5]) { case 'h': { switch (op[6]) { case 'l': if (strcmp(op, "i64.shl") == 0) { return makeBinary(s, BinaryOp::ShlInt64); } goto parse_error; case 'r': { switch (op[8]) { case 's': if (strcmp(op, "i64.shr_s") == 0) { return makeBinary(s, BinaryOp::ShrSInt64); } goto parse_error; case 'u': if (strcmp(op, "i64.shr_u") == 0) { return makeBinary(s, BinaryOp::ShrUInt64); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } case 't': { switch (op[9]) { case '\0': if (strcmp(op, "i64.store") == 0) { return makeStore(s, Type::i64, /*isAtomic=*/false); } goto parse_error; case '1': if (strcmp(op, "i64.store16") == 0) { return makeStore(s, Type::i64, /*isAtomic=*/false); } goto parse_error; case '3': if (strcmp(op, "i64.store32") == 0) { return makeStore(s, Type::i64, /*isAtomic=*/false); } goto parse_error; case '8': if (strcmp(op, "i64.store8") == 0) { return makeStore(s, Type::i64, /*isAtomic=*/false); } goto parse_error; default: goto parse_error; } } case 'u': if (strcmp(op, "i64.sub") == 0) { return makeBinary(s, BinaryOp::SubInt64); } goto parse_error; default: goto parse_error; } } case 't': { switch (op[10]) { case 'f': { switch (op[11]) { case '3': { switch (op[14]) { case 's': if (strcmp(op, "i64.trunc_f32_s") == 0) { return makeUnary(s, UnaryOp::TruncSFloat32ToInt64); } goto parse_error; case 'u': if (strcmp(op, "i64.trunc_f32_u") == 0) { return makeUnary(s, UnaryOp::TruncUFloat32ToInt64); } goto parse_error; default: goto parse_error; } } case '6': { switch (op[14]) { case 's': if (strcmp(op, "i64.trunc_f64_s") == 0) { return makeUnary(s, UnaryOp::TruncSFloat64ToInt64); } goto parse_error; case 'u': if (strcmp(op, "i64.trunc_f64_u") == 0) { return makeUnary(s, UnaryOp::TruncUFloat64ToInt64); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } case 's': { switch (op[15]) { case '3': { switch (op[18]) { case 's': if (strcmp(op, "i64.trunc_sat_f32_s") == 0) { return makeUnary(s, UnaryOp::TruncSatSFloat32ToInt64); } goto parse_error; case 'u': if (strcmp(op, "i64.trunc_sat_f32_u") == 0) { return makeUnary(s, UnaryOp::TruncSatUFloat32ToInt64); } goto parse_error; default: goto parse_error; } } case '6': { switch (op[18]) { case 's': if (strcmp(op, "i64.trunc_sat_f64_s") == 0) { return makeUnary(s, UnaryOp::TruncSatSFloat64ToInt64); } goto parse_error; case 'u': if (strcmp(op, "i64.trunc_sat_f64_u") == 0) { return makeUnary(s, UnaryOp::TruncSatUFloat64ToInt64); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } default: goto parse_error; } } case 'x': if (strcmp(op, "i64.xor") == 0) { return makeBinary(s, BinaryOp::XorInt64); } goto parse_error; default: goto parse_error; } } case 'x': { switch (op[6]) { case 'a': { switch (op[7]) { case 'd': if (strcmp(op, "i64x2.add") == 0) { return makeBinary(s, BinaryOp::AddVecI64x2); } goto parse_error; case 'l': if (strcmp(op, "i64x2.all_true") == 0) { return makeUnary(s, UnaryOp::AllTrueVecI64x2); } goto parse_error; case 'n': if (strcmp(op, "i64x2.any_true") == 0) { return makeUnary(s, UnaryOp::AnyTrueVecI64x2); } goto parse_error; default: goto parse_error; } } case 'e': if (strcmp(op, "i64x2.extract_lane") == 0) { return makeSIMDExtract(s, SIMDExtractOp::ExtractLaneVecI64x2, 2); } goto parse_error; case 'l': { switch (op[15]) { case 's': if (strcmp(op, "i64x2.load32x2_s") == 0) { return makeSIMDLoad(s, SIMDLoadOp::LoadExtSVec32x2ToVecI64x2); } goto parse_error; case 'u': if (strcmp(op, "i64x2.load32x2_u") == 0) { return makeSIMDLoad(s, SIMDLoadOp::LoadExtUVec32x2ToVecI64x2); } goto parse_error; default: goto parse_error; } } case 'n': if (strcmp(op, "i64x2.neg") == 0) { return makeUnary(s, UnaryOp::NegVecI64x2); } goto parse_error; case 'r': if (strcmp(op, "i64x2.replace_lane") == 0) { return makeSIMDReplace(s, SIMDReplaceOp::ReplaceLaneVecI64x2, 2); } goto parse_error; case 's': { switch (op[7]) { case 'h': { switch (op[8]) { case 'l': if (strcmp(op, "i64x2.shl") == 0) { return makeSIMDShift(s, SIMDShiftOp::ShlVecI64x2); } goto parse_error; case 'r': { switch (op[10]) { case 's': if (strcmp(op, "i64x2.shr_s") == 0) { return makeSIMDShift(s, SIMDShiftOp::ShrSVecI64x2); } goto parse_error; case 'u': if (strcmp(op, "i64x2.shr_u") == 0) { return makeSIMDShift(s, SIMDShiftOp::ShrUVecI64x2); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } case 'p': if (strcmp(op, "i64x2.splat") == 0) { return makeUnary(s, UnaryOp::SplatVecI64x2); } goto parse_error; case 'u': if (strcmp(op, "i64x2.sub") == 0) { return makeBinary(s, BinaryOp::SubVecI64x2); } goto parse_error; default: goto parse_error; } } case 't': { switch (op[22]) { case 's': if (strcmp(op, "i64x2.trunc_sat_f64x2_s") == 0) { return makeUnary(s, UnaryOp::TruncSatSVecF64x2ToVecI64x2); } goto parse_error; case 'u': if (strcmp(op, "i64x2.trunc_sat_f64x2_u") == 0) { return makeUnary(s, UnaryOp::TruncSatUVecF64x2ToVecI64x2); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } default: goto parse_error; } } case '8': { switch (op[6]) { case 'a': { switch (op[7]) { case 'd': { switch (op[9]) { case '\0': if (strcmp(op, "i8x16.add") == 0) { return makeBinary(s, BinaryOp::AddVecI8x16); } goto parse_error; case '_': { switch (op[19]) { case 's': if (strcmp(op, "i8x16.add_saturate_s") == 0) { return makeBinary(s, BinaryOp::AddSatSVecI8x16); } goto parse_error; case 'u': if (strcmp(op, "i8x16.add_saturate_u") == 0) { return makeBinary(s, BinaryOp::AddSatUVecI8x16); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } case 'l': if (strcmp(op, "i8x16.all_true") == 0) { return makeUnary(s, UnaryOp::AllTrueVecI8x16); } goto parse_error; case 'n': if (strcmp(op, "i8x16.any_true") == 0) { return makeUnary(s, UnaryOp::AnyTrueVecI8x16); } goto parse_error; case 'v': if (strcmp(op, "i8x16.avgr_u") == 0) { return makeBinary(s, BinaryOp::AvgrUVecI8x16); } goto parse_error; default: goto parse_error; } } case 'e': { switch (op[7]) { case 'q': if (strcmp(op, "i8x16.eq") == 0) { return makeBinary(s, BinaryOp::EqVecI8x16); } goto parse_error; case 'x': { switch (op[19]) { case 's': if (strcmp(op, "i8x16.extract_lane_s") == 0) { return makeSIMDExtract(s, SIMDExtractOp::ExtractLaneSVecI8x16, 16); } goto parse_error; case 'u': if (strcmp(op, "i8x16.extract_lane_u") == 0) { return makeSIMDExtract(s, SIMDExtractOp::ExtractLaneUVecI8x16, 16); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } case 'g': { switch (op[7]) { case 'e': { switch (op[9]) { case 's': if (strcmp(op, "i8x16.ge_s") == 0) { return makeBinary(s, BinaryOp::GeSVecI8x16); } goto parse_error; case 'u': if (strcmp(op, "i8x16.ge_u") == 0) { return makeBinary(s, BinaryOp::GeUVecI8x16); } goto parse_error; default: goto parse_error; } } case 't': { switch (op[9]) { case 's': if (strcmp(op, "i8x16.gt_s") == 0) { return makeBinary(s, BinaryOp::GtSVecI8x16); } goto parse_error; case 'u': if (strcmp(op, "i8x16.gt_u") == 0) { return makeBinary(s, BinaryOp::GtUVecI8x16); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } case 'l': { switch (op[7]) { case 'e': { switch (op[9]) { case 's': if (strcmp(op, "i8x16.le_s") == 0) { return makeBinary(s, BinaryOp::LeSVecI8x16); } goto parse_error; case 'u': if (strcmp(op, "i8x16.le_u") == 0) { return makeBinary(s, BinaryOp::LeUVecI8x16); } goto parse_error; default: goto parse_error; } } case 't': { switch (op[9]) { case 's': if (strcmp(op, "i8x16.lt_s") == 0) { return makeBinary(s, BinaryOp::LtSVecI8x16); } goto parse_error; case 'u': if (strcmp(op, "i8x16.lt_u") == 0) { return makeBinary(s, BinaryOp::LtUVecI8x16); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } case 'm': { switch (op[7]) { case 'a': { switch (op[10]) { case 's': if (strcmp(op, "i8x16.max_s") == 0) { return makeBinary(s, BinaryOp::MaxSVecI8x16); } goto parse_error; case 'u': if (strcmp(op, "i8x16.max_u") == 0) { return makeBinary(s, BinaryOp::MaxUVecI8x16); } goto parse_error; default: goto parse_error; } } case 'i': { switch (op[10]) { case 's': if (strcmp(op, "i8x16.min_s") == 0) { return makeBinary(s, BinaryOp::MinSVecI8x16); } goto parse_error; case 'u': if (strcmp(op, "i8x16.min_u") == 0) { return makeBinary(s, BinaryOp::MinUVecI8x16); } goto parse_error; default: goto parse_error; } } case 'u': if (strcmp(op, "i8x16.mul") == 0) { return makeBinary(s, BinaryOp::MulVecI8x16); } goto parse_error; default: goto parse_error; } } case 'n': { switch (op[7]) { case 'a': { switch (op[19]) { case 's': if (strcmp(op, "i8x16.narrow_i16x8_s") == 0) { return makeBinary(s, BinaryOp::NarrowSVecI16x8ToVecI8x16); } goto parse_error; case 'u': if (strcmp(op, "i8x16.narrow_i16x8_u") == 0) { return makeBinary(s, BinaryOp::NarrowUVecI16x8ToVecI8x16); } goto parse_error; default: goto parse_error; } } case 'e': { switch (op[8]) { case '\0': if (strcmp(op, "i8x16.ne") == 0) { return makeBinary(s, BinaryOp::NeVecI8x16); } goto parse_error; case 'g': if (strcmp(op, "i8x16.neg") == 0) { return makeUnary(s, UnaryOp::NegVecI8x16); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } case 'r': if (strcmp(op, "i8x16.replace_lane") == 0) { return makeSIMDReplace(s, SIMDReplaceOp::ReplaceLaneVecI8x16, 16); } goto parse_error; case 's': { switch (op[7]) { case 'h': { switch (op[8]) { case 'l': if (strcmp(op, "i8x16.shl") == 0) { return makeSIMDShift(s, SIMDShiftOp::ShlVecI8x16); } goto parse_error; case 'r': { switch (op[10]) { case 's': if (strcmp(op, "i8x16.shr_s") == 0) { return makeSIMDShift(s, SIMDShiftOp::ShrSVecI8x16); } goto parse_error; case 'u': if (strcmp(op, "i8x16.shr_u") == 0) { return makeSIMDShift(s, SIMDShiftOp::ShrUVecI8x16); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } case 'p': if (strcmp(op, "i8x16.splat") == 0) { return makeUnary(s, UnaryOp::SplatVecI8x16); } goto parse_error; case 'u': { switch (op[9]) { case '\0': if (strcmp(op, "i8x16.sub") == 0) { return makeBinary(s, BinaryOp::SubVecI8x16); } goto parse_error; case '_': { switch (op[19]) { case 's': if (strcmp(op, "i8x16.sub_saturate_s") == 0) { return makeBinary(s, BinaryOp::SubSatSVecI8x16); } goto parse_error; case 'u': if (strcmp(op, "i8x16.sub_saturate_u") == 0) { return makeBinary(s, BinaryOp::SubSatUVecI8x16); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } default: goto parse_error; } } default: goto parse_error; } } case 'f': if (strcmp(op, "if") == 0) { return makeIf(s); } goto parse_error; default: goto parse_error; } } case 'l': { switch (op[2]) { case 'c': { switch (op[6]) { case 'g': if (strcmp(op, "local.get") == 0) { return makeLocalGet(s); } goto parse_error; case 's': if (strcmp(op, "local.set") == 0) { return makeLocalSet(s); } goto parse_error; case 't': if (strcmp(op, "local.tee") == 0) { return makeLocalTee(s); } goto parse_error; default: goto parse_error; } } case 'o': if (strcmp(op, "loop") == 0) { return makeLoop(s); } goto parse_error; default: goto parse_error; } } case 'm': { switch (op[7]) { case 'c': if (strcmp(op, "memory.copy") == 0) { return makeMemoryCopy(s); } goto parse_error; case 'f': if (strcmp(op, "memory.fill") == 0) { return makeMemoryFill(s); } goto parse_error; case 'g': if (strcmp(op, "memory.grow") == 0) { return makeHost(s, HostOp::MemoryGrow); } goto parse_error; case 'i': if (strcmp(op, "memory.init") == 0) { return makeMemoryInit(s); } goto parse_error; case 's': if (strcmp(op, "memory.size") == 0) { return makeHost(s, HostOp::MemorySize); } goto parse_error; default: goto parse_error; } } case 'n': { switch (op[1]) { case 'o': if (strcmp(op, "nop") == 0) { return makeNop(); } goto parse_error; case 'u': if (strcmp(op, "nullref.pop") == 0) { return makePop(Type::nullref); } goto parse_error; default: goto parse_error; } } case 'p': if (strcmp(op, "push") == 0) { return makePush(s); } goto parse_error; case 'r': { switch (op[2]) { case 'f': { switch (op[4]) { case 'f': if (strcmp(op, "ref.func") == 0) { return makeRefFunc(s); } goto parse_error; case 'i': if (strcmp(op, "ref.is_null") == 0) { return makeRefIsNull(s); } goto parse_error; case 'n': if (strcmp(op, "ref.null") == 0) { return makeRefNull(s); } goto parse_error; default: goto parse_error; } } case 't': { switch (op[3]) { case 'h': if (strcmp(op, "rethrow") == 0) { return makeRethrow(s); } goto parse_error; case 'u': { switch (op[6]) { case '\0': if (strcmp(op, "return") == 0) { return makeReturn(s); } goto parse_error; case '_': { switch (op[11]) { case '\0': if (strcmp(op, "return_call") == 0) { return makeCall(s, /*isReturn=*/true); } goto parse_error; case '_': if (strcmp(op, "return_call_indirect") == 0) { return makeCallIndirect(s, /*isReturn=*/true); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } default: goto parse_error; } } default: goto parse_error; } } case 's': if (strcmp(op, "select") == 0) { return makeSelect(s); } goto parse_error; case 't': { switch (op[1]) { case 'h': { switch (op[2]) { case 'e': if (strcmp(op, "then") == 0) { return makeThenOrElse(s); } goto parse_error; case 'r': if (strcmp(op, "throw") == 0) { return makeThrow(s); } goto parse_error; default: goto parse_error; } } case 'r': if (strcmp(op, "try") == 0) { return makeTry(s); } goto parse_error; default: goto parse_error; } } case 'u': if (strcmp(op, "unreachable") == 0) { return makeUnreachable(); } goto parse_error; case 'v': { switch (op[1]) { case '1': { switch (op[2]) { case '2': { switch (op[5]) { case 'a': { switch (op[8]) { case '\0': if (strcmp(op, "v128.and") == 0) { return makeBinary(s, BinaryOp::AndVec128); } goto parse_error; case 'n': if (strcmp(op, "v128.andnot") == 0) { return makeBinary(s, BinaryOp::AndNotVec128); } goto parse_error; default: goto parse_error; } } case 'b': if (strcmp(op, "v128.bitselect") == 0) { return makeSIMDTernary(s, SIMDTernaryOp::Bitselect); } goto parse_error; case 'c': if (strcmp(op, "v128.const") == 0) { return makeConst(s, Type::v128); } goto parse_error; case 'l': if (strcmp(op, "v128.load") == 0) { return makeLoad(s, Type::v128, /*isAtomic=*/false); } goto parse_error; case 'n': if (strcmp(op, "v128.not") == 0) { return makeUnary(s, UnaryOp::NotVec128); } goto parse_error; case 'o': if (strcmp(op, "v128.or") == 0) { return makeBinary(s, BinaryOp::OrVec128); } goto parse_error; case 'p': if (strcmp(op, "v128.pop") == 0) { return makePop(Type::v128); } goto parse_error; case 's': if (strcmp(op, "v128.store") == 0) { return makeStore(s, Type::v128, /*isAtomic=*/false); } goto parse_error; case 'x': if (strcmp(op, "v128.xor") == 0) { return makeBinary(s, BinaryOp::XorVec128); } goto parse_error; default: goto parse_error; } } case '6': if (strcmp(op, "v16x8.load_splat") == 0) { return makeSIMDLoad(s, SIMDLoadOp::LoadSplatVec16x8); } goto parse_error; default: goto parse_error; } } case '3': if (strcmp(op, "v32x4.load_splat") == 0) { return makeSIMDLoad(s, SIMDLoadOp::LoadSplatVec32x4); } goto parse_error; case '6': if (strcmp(op, "v64x2.load_splat") == 0) { return makeSIMDLoad(s, SIMDLoadOp::LoadSplatVec64x2); } goto parse_error; case '8': { switch (op[6]) { case 'l': if (strcmp(op, "v8x16.load_splat") == 0) { return makeSIMDLoad(s, SIMDLoadOp::LoadSplatVec8x16); } goto parse_error; case 's': { switch (op[7]) { case 'h': if (strcmp(op, "v8x16.shuffle") == 0) { return makeSIMDShuffle(s); } goto parse_error; case 'w': if (strcmp(op, "v8x16.swizzle") == 0) { return makeBinary(s, BinaryOp::SwizzleVec8x16); } goto parse_error; default: goto parse_error; } } default: goto parse_error; } } default: goto parse_error; } } default: goto parse_error; } parse_error: throw ParseException(std::string(op), s.line, s.col); #endif // INSTRUCTION_PARSER // clang-format on binaryen-version_91/src/ir/000077500000000000000000000000001362402614000160605ustar00rootroot00000000000000binaryen-version_91/src/ir/CMakeLists.txt000066400000000000000000000002151362402614000206160ustar00rootroot00000000000000set(ir_SOURCES ExpressionAnalyzer.cpp ExpressionManipulator.cpp LocalGraph.cpp ReFinalize.cpp ) add_library(ir OBJECT ${ir_SOURCES}) binaryen-version_91/src/ir/ExpressionAnalyzer.cpp000066400000000000000000000403631362402614000224370ustar00rootroot00000000000000/* * Copyright 2016 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ir/iteration.h" #include "ir/load-utils.h" #include "ir/utils.h" #include "support/hash.h" #include "support/small_vector.h" #include "wasm-traversal.h" #include "wasm.h" namespace wasm { // Given a stack of expressions, checks if the topmost is used as a result. // For example, if the parent is a block and the node is before the last // position, it is not used. bool ExpressionAnalyzer::isResultUsed(ExpressionStack& stack, Function* func) { for (int i = int(stack.size()) - 2; i >= 0; i--) { auto* curr = stack[i]; auto* above = stack[i + 1]; // only if and block can drop values (pre-drop expression was added) FIXME if (curr->is()) { auto* block = curr->cast(); for (size_t j = 0; j < block->list.size() - 1; j++) { if (block->list[j] == above) { return false; } } assert(block->list.back() == above); // continue down } else if (curr->is()) { auto* iff = curr->cast(); if (above == iff->condition) { return true; } if (!iff->ifFalse) { return false; } assert(above == iff->ifTrue || above == iff->ifFalse); // continue down } else { if (curr->is()) { return false; } return true; // all other node types use the result } } // The value might be used, so it depends on if the function returns return func->sig.results != Type::none; } // Checks if a value is dropped. bool ExpressionAnalyzer::isResultDropped(ExpressionStack& stack) { for (int i = int(stack.size()) - 2; i >= 0; i--) { auto* curr = stack[i]; auto* above = stack[i + 1]; if (curr->is()) { auto* block = curr->cast(); for (size_t j = 0; j < block->list.size() - 1; j++) { if (block->list[j] == above) { return false; } } assert(block->list.back() == above); // continue down } else if (curr->is()) { auto* iff = curr->cast(); if (above == iff->condition) { return false; } if (!iff->ifFalse) { return false; } assert(above == iff->ifTrue || above == iff->ifFalse); // continue down } else { if (curr->is()) { return true; // dropped } return false; // all other node types use the result } } return false; } // // Allows visiting the immediate fields of the expression. This is // useful for comparisons and hashing. // // The passed-in visitor object must implement: // * visitScopeName - a Name that represents a block or loop scope // * visitNonScopeName - a non-scope name // * visitInt - anything that has a short enumeration, including // opcodes, # of bytes in a load, bools, etc. - must be // guaranteed to fit in an int32 or less. // * visitLiteral - a Literal // * visitType - a Type // * visitIndex - an Index // * visitAddress - an Address // namespace { template void visitImmediates(Expression* curr, T& visitor) { struct ImmediateVisitor : public OverriddenVisitor { T& visitor; ImmediateVisitor(Expression* curr, T& visitor) : visitor(visitor) { this->visit(curr); } void visitBlock(Block* curr) { visitor.visitScopeName(curr->name); } void visitIf(If* curr) {} void visitLoop(Loop* curr) { visitor.visitScopeName(curr->name); } void visitBreak(Break* curr) { visitor.visitScopeName(curr->name); } void visitSwitch(Switch* curr) { for (auto target : curr->targets) { visitor.visitScopeName(target); } visitor.visitScopeName(curr->default_); } void visitCall(Call* curr) { visitor.visitNonScopeName(curr->target); visitor.visitInt(curr->isReturn); } void visitCallIndirect(CallIndirect* curr) { visitor.visitInt(curr->sig.params.getID()); visitor.visitInt(curr->sig.results.getID()); visitor.visitInt(curr->isReturn); } void visitLocalGet(LocalGet* curr) { visitor.visitIndex(curr->index); } void visitLocalSet(LocalSet* curr) { visitor.visitIndex(curr->index); } void visitGlobalGet(GlobalGet* curr) { visitor.visitNonScopeName(curr->name); } void visitGlobalSet(GlobalSet* curr) { visitor.visitNonScopeName(curr->name); } void visitLoad(Load* curr) { visitor.visitInt(curr->bytes); if (curr->type != Type::unreachable && curr->bytes < curr->type.getByteSize()) { visitor.visitInt(curr->signed_); } visitor.visitAddress(curr->offset); visitor.visitAddress(curr->align); visitor.visitInt(curr->isAtomic); } void visitStore(Store* curr) { visitor.visitInt(curr->bytes); visitor.visitAddress(curr->offset); visitor.visitAddress(curr->align); visitor.visitInt(curr->isAtomic); visitor.visitInt(curr->valueType.getID()); } void visitAtomicRMW(AtomicRMW* curr) { visitor.visitInt(curr->op); visitor.visitInt(curr->bytes); visitor.visitAddress(curr->offset); } void visitAtomicCmpxchg(AtomicCmpxchg* curr) { visitor.visitInt(curr->bytes); visitor.visitAddress(curr->offset); } void visitAtomicWait(AtomicWait* curr) { visitor.visitAddress(curr->offset); visitor.visitType(curr->expectedType); } void visitAtomicNotify(AtomicNotify* curr) { visitor.visitAddress(curr->offset); } void visitAtomicFence(AtomicFence* curr) { visitor.visitInt(curr->order); } void visitSIMDExtract(SIMDExtract* curr) { visitor.visitInt(curr->op); visitor.visitInt(curr->index); } void visitSIMDReplace(SIMDReplace* curr) { visitor.visitInt(curr->op); visitor.visitInt(curr->index); } void visitSIMDShuffle(SIMDShuffle* curr) { for (auto x : curr->mask) { visitor.visitInt(x); } } void visitSIMDTernary(SIMDTernary* curr) { visitor.visitInt(curr->op); } void visitSIMDShift(SIMDShift* curr) { visitor.visitInt(curr->op); } void visitSIMDLoad(SIMDLoad* curr) { visitor.visitInt(curr->op); visitor.visitAddress(curr->offset); visitor.visitAddress(curr->align); } void visitMemoryInit(MemoryInit* curr) { visitor.visitIndex(curr->segment); } void visitDataDrop(DataDrop* curr) { visitor.visitIndex(curr->segment); } void visitMemoryCopy(MemoryCopy* curr) {} void visitMemoryFill(MemoryFill* curr) {} void visitConst(Const* curr) { visitor.visitLiteral(curr->value); } void visitUnary(Unary* curr) { visitor.visitInt(curr->op); } void visitBinary(Binary* curr) { visitor.visitInt(curr->op); } void visitSelect(Select* curr) {} void visitDrop(Drop* curr) {} void visitReturn(Return* curr) {} void visitHost(Host* curr) { visitor.visitInt(curr->op); visitor.visitNonScopeName(curr->nameOperand); } void visitRefNull(RefNull* curr) {} void visitRefIsNull(RefIsNull* curr) {} void visitRefFunc(RefFunc* curr) { visitor.visitNonScopeName(curr->func); } void visitTry(Try* curr) {} void visitThrow(Throw* curr) { visitor.visitNonScopeName(curr->event); } void visitRethrow(Rethrow* curr) {} void visitBrOnExn(BrOnExn* curr) { visitor.visitScopeName(curr->name); visitor.visitNonScopeName(curr->event); } void visitNop(Nop* curr) {} void visitUnreachable(Unreachable* curr) {} void visitPush(Push* curr) {} void visitPop(Pop* curr) {} } singleton(curr, visitor); } } // namespace bool ExpressionAnalyzer::flexibleEqual(Expression* left, Expression* right, ExprComparer comparer) { struct Comparer { // for each name on the left, the corresponding name on the right std::map rightNames; std::vector leftStack; std::vector rightStack; struct Immediates { Comparer& parent; Immediates(Comparer& parent) : parent(parent) {} SmallVector scopeNames; SmallVector nonScopeNames; SmallVector ints; SmallVector literals; SmallVector types; SmallVector indexes; SmallVector addresses; void visitScopeName(Name curr) { scopeNames.push_back(curr); } void visitNonScopeName(Name curr) { nonScopeNames.push_back(curr); } void visitInt(int32_t curr) { ints.push_back(curr); } void visitLiteral(Literal curr) { literals.push_back(curr); } void visitType(Type curr) { types.push_back(curr); } void visitIndex(Index curr) { indexes.push_back(curr); } void visitAddress(Address curr) { addresses.push_back(curr); } // Comparison is by value, except for names, which must match. bool operator==(const Immediates& other) { if (scopeNames.size() != other.scopeNames.size()) { return false; } for (Index i = 0; i < scopeNames.size(); i++) { auto leftName = scopeNames[i]; auto rightName = other.scopeNames[i]; auto iter = parent.rightNames.find(leftName); // If it's not found, that means it was defined out of the expression // being compared, in which case we can just treat it literally - it // must be exactly identical. if (iter != parent.rightNames.end()) { leftName = iter->second; } if (leftName != rightName) { return false; } } if (nonScopeNames != other.nonScopeNames) { return false; } if (ints != other.ints) { return false; } if (literals != other.literals) { return false; } if (types != other.types) { return false; } if (indexes != other.indexes) { return false; } if (addresses != other.addresses) { return false; } return true; } bool operator!=(const Immediates& other) { return !(*this == other); } void clear() { scopeNames.clear(); nonScopeNames.clear(); ints.clear(); literals.clear(); types.clear(); indexes.clear(); addresses.clear(); } }; bool noteNames(Name left, Name right) { if (left.is() != right.is()) { return false; } if (left.is()) { assert(rightNames.find(left) == rightNames.end()); rightNames[left] = right; } return true; } bool compare(Expression* left, Expression* right, ExprComparer comparer) { Immediates leftImmediates(*this), rightImmediates(*this); // The empty name is the same on both sides. rightNames[Name()] = Name(); leftStack.push_back(left); rightStack.push_back(right); while (leftStack.size() > 0 && rightStack.size() > 0) { left = leftStack.back(); leftStack.pop_back(); right = rightStack.back(); rightStack.pop_back(); if (!left != !right) { return false; } if (!left) { continue; } if (comparer(left, right)) { continue; // comparison hook, before all the rest } // continue with normal structural comparison if (left->_id != right->_id) { return false; } // Blocks and loops introduce scoping. if (auto* block = left->dynCast()) { if (!noteNames(block->name, right->cast()->name)) { return false; } } else if (auto* loop = left->dynCast()) { if (!noteNames(loop->name, right->cast()->name)) { return false; } } else { // For all other nodes, compare their immediate values visitImmediates(left, leftImmediates); visitImmediates(right, rightImmediates); if (leftImmediates != rightImmediates) { return false; } leftImmediates.clear(); rightImmediates.clear(); } // Add child nodes. Index counter = 0; for (auto* child : ChildIterator(left)) { leftStack.push_back(child); counter++; } for (auto* child : ChildIterator(right)) { rightStack.push_back(child); counter--; } // The number of child nodes must match (e.g. return has an optional // one). if (counter != 0) { return false; } } if (leftStack.size() > 0 || rightStack.size() > 0) { return false; } return true; } }; return Comparer().compare(left, right, comparer); } // hash an expression, ignoring superficial details like specific internal names HashType ExpressionAnalyzer::hash(Expression* curr) { struct Hasher { HashType digest = 0; Index internalCounter = 0; // for each internal name, its unique id std::map internalNames; ExpressionStack stack; void noteScopeName(Name curr) { if (curr.is()) { internalNames[curr] = internalCounter++; } } Hasher(Expression* curr) { stack.push_back(curr); while (stack.size() > 0) { curr = stack.back(); stack.pop_back(); if (!curr) { continue; } hash(curr->_id); // we often don't need to hash the type, as it is tied to other values // we are hashing anyhow, but there are exceptions: for example, a // local.get's type is determined by the function, so if we are // hashing only expression fragments, then two from different // functions may turn out the same even if the type differs. Likewise, // if we hash between modules, then we need to take int account // call_imports type, etc. The simplest thing is just to hash the // type for all of them. hash(curr->type.getID()); // Blocks and loops introduce scoping. if (auto* block = curr->dynCast()) { noteScopeName(block->name); } else if (auto* loop = curr->dynCast()) { noteScopeName(loop->name); } else { // For all other nodes, compare their immediate values visitImmediates(curr, *this); } // Hash children Index counter = 0; for (auto* child : ChildIterator(curr)) { stack.push_back(child); counter++; } // Sometimes children are optional, e.g. return, so we must hash // their number as well. hash(counter); } } void hash(HashType hash) { digest = rehash(digest, hash); } void hash64(uint64_t hash) { digest = rehash(rehash(digest, HashType(hash >> 32)), HashType(hash)); } void visitScopeName(Name curr) { // Names are relative, we give the same hash for // (block $x (br $x)) // (block $y (br $y)) static_assert(sizeof(Index) == sizeof(int32_t), "wasm64 will need changes here"); assert(internalNames.find(curr) != internalNames.end()); return hash(internalNames[curr]); } void visitNonScopeName(Name curr) { return hash64(uint64_t(curr.str)); } void visitInt(int32_t curr) { hash(curr); } void visitLiteral(Literal curr) { hash(std::hash()(curr)); } void visitType(Type curr) { hash(int32_t(curr.getSingle())); } void visitIndex(Index curr) { static_assert(sizeof(Index) == sizeof(int32_t), "wasm64 will need changes here"); hash(int32_t(curr)); } void visitAddress(Address curr) { static_assert(sizeof(Address) == sizeof(int32_t), "wasm64 will need changes here"); hash(int32_t(curr)); } }; return Hasher(curr).digest; } } // namespace wasm binaryen-version_91/src/ir/ExpressionManipulator.cpp000066400000000000000000000244431362402614000231460ustar00rootroot00000000000000/* * Copyright 2017 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ir/load-utils.h" #include "ir/utils.h" #include "support/hash.h" namespace wasm { namespace ExpressionManipulator { Expression* flexibleCopy(Expression* original, Module& wasm, CustomCopier custom) { struct Copier : public OverriddenVisitor { Module& wasm; CustomCopier custom; Builder builder; Copier(Module& wasm, CustomCopier custom) : wasm(wasm), custom(custom), builder(wasm) {} Expression* copy(Expression* curr) { if (!curr) { return nullptr; } auto* ret = custom(curr); if (ret) { return ret; } return OverriddenVisitor::visit(curr); } Expression* visitBlock(Block* curr) { ExpressionList list(wasm.allocator); for (Index i = 0; i < curr->list.size(); i++) { list.push_back(copy(curr->list[i])); } return builder.makeBlock(curr->name, list, curr->type); } Expression* visitIf(If* curr) { return builder.makeIf(copy(curr->condition), copy(curr->ifTrue), copy(curr->ifFalse), curr->type); } Expression* visitLoop(Loop* curr) { return builder.makeLoop(curr->name, copy(curr->body), curr->type); } Expression* visitBreak(Break* curr) { return builder.makeBreak( curr->name, copy(curr->value), copy(curr->condition)); } Expression* visitSwitch(Switch* curr) { return builder.makeSwitch(curr->targets, curr->default_, copy(curr->condition), copy(curr->value)); } Expression* visitCall(Call* curr) { auto* ret = builder.makeCall(curr->target, {}, curr->type, curr->isReturn); for (Index i = 0; i < curr->operands.size(); i++) { ret->operands.push_back(copy(curr->operands[i])); } return ret; } Expression* visitCallIndirect(CallIndirect* curr) { std::vector copiedOps; for (auto op : curr->operands) { copiedOps.push_back(copy(op)); } return builder.makeCallIndirect( copy(curr->target), copiedOps, curr->sig, curr->isReturn); } Expression* visitLocalGet(LocalGet* curr) { return builder.makeLocalGet(curr->index, curr->type); } Expression* visitLocalSet(LocalSet* curr) { if (curr->isTee()) { return builder.makeLocalTee(curr->index, copy(curr->value), curr->type); } else { return builder.makeLocalSet(curr->index, copy(curr->value)); } } Expression* visitGlobalGet(GlobalGet* curr) { return builder.makeGlobalGet(curr->name, curr->type); } Expression* visitGlobalSet(GlobalSet* curr) { return builder.makeGlobalSet(curr->name, copy(curr->value)); } Expression* visitLoad(Load* curr) { if (curr->isAtomic) { return builder.makeAtomicLoad( curr->bytes, curr->offset, copy(curr->ptr), curr->type); } return builder.makeLoad(curr->bytes, LoadUtils::isSignRelevant(curr) ? curr->signed_ : false, curr->offset, curr->align, copy(curr->ptr), curr->type); } Expression* visitStore(Store* curr) { if (curr->isAtomic) { return builder.makeAtomicStore(curr->bytes, curr->offset, copy(curr->ptr), copy(curr->value), curr->valueType); } return builder.makeStore(curr->bytes, curr->offset, curr->align, copy(curr->ptr), copy(curr->value), curr->valueType); } Expression* visitAtomicRMW(AtomicRMW* curr) { return builder.makeAtomicRMW(curr->op, curr->bytes, curr->offset, copy(curr->ptr), copy(curr->value), curr->type); } Expression* visitAtomicCmpxchg(AtomicCmpxchg* curr) { return builder.makeAtomicCmpxchg(curr->bytes, curr->offset, copy(curr->ptr), copy(curr->expected), copy(curr->replacement), curr->type); } Expression* visitAtomicWait(AtomicWait* curr) { return builder.makeAtomicWait(copy(curr->ptr), copy(curr->expected), copy(curr->timeout), curr->expectedType, curr->offset); } Expression* visitAtomicNotify(AtomicNotify* curr) { return builder.makeAtomicNotify( copy(curr->ptr), copy(curr->notifyCount), curr->offset); } Expression* visitAtomicFence(AtomicFence* curr) { return builder.makeAtomicFence(); } Expression* visitSIMDExtract(SIMDExtract* curr) { return builder.makeSIMDExtract(curr->op, copy(curr->vec), curr->index); } Expression* visitSIMDReplace(SIMDReplace* curr) { return builder.makeSIMDReplace( curr->op, copy(curr->vec), curr->index, copy(curr->value)); } Expression* visitSIMDShuffle(SIMDShuffle* curr) { return builder.makeSIMDShuffle( copy(curr->left), copy(curr->right), curr->mask); } Expression* visitSIMDTernary(SIMDTernary* curr) { return builder.makeSIMDTernary( curr->op, copy(curr->a), copy(curr->b), copy(curr->c)); } Expression* visitSIMDShift(SIMDShift* curr) { return builder.makeSIMDShift( curr->op, copy(curr->vec), copy(curr->shift)); } Expression* visitSIMDLoad(SIMDLoad* curr) { return builder.makeSIMDLoad( curr->op, curr->offset, curr->align, copy(curr->ptr)); } Expression* visitConst(Const* curr) { return builder.makeConst(curr->value); } Expression* visitMemoryInit(MemoryInit* curr) { return builder.makeMemoryInit( curr->segment, copy(curr->dest), copy(curr->offset), copy(curr->size)); } Expression* visitDataDrop(DataDrop* curr) { return builder.makeDataDrop(curr->segment); } Expression* visitMemoryCopy(MemoryCopy* curr) { return builder.makeMemoryCopy( copy(curr->dest), copy(curr->source), copy(curr->size)); } Expression* visitMemoryFill(MemoryFill* curr) { return builder.makeMemoryFill( copy(curr->dest), copy(curr->value), copy(curr->size)); } Expression* visitUnary(Unary* curr) { return builder.makeUnary(curr->op, copy(curr->value)); } Expression* visitBinary(Binary* curr) { return builder.makeBinary(curr->op, copy(curr->left), copy(curr->right)); } Expression* visitSelect(Select* curr) { return builder.makeSelect(copy(curr->condition), copy(curr->ifTrue), copy(curr->ifFalse), curr->type); } Expression* visitDrop(Drop* curr) { return builder.makeDrop(copy(curr->value)); } Expression* visitReturn(Return* curr) { return builder.makeReturn(copy(curr->value)); } Expression* visitHost(Host* curr) { std::vector operands; for (Index i = 0; i < curr->operands.size(); i++) { operands.push_back(copy(curr->operands[i])); } auto* ret = builder.makeHost(curr->op, curr->nameOperand, std::move(operands)); return ret; } Expression* visitRefNull(RefNull* curr) { return builder.makeRefNull(); } Expression* visitRefIsNull(RefIsNull* curr) { return builder.makeRefIsNull(copy(curr->value)); } Expression* visitRefFunc(RefFunc* curr) { return builder.makeRefFunc(curr->func); } Expression* visitTry(Try* curr) { return builder.makeTry( copy(curr->body), copy(curr->catchBody), curr->type); } Expression* visitThrow(Throw* curr) { std::vector operands; for (Index i = 0; i < curr->operands.size(); i++) { operands.push_back(copy(curr->operands[i])); } return builder.makeThrow(curr->event, std::move(operands)); } Expression* visitRethrow(Rethrow* curr) { return builder.makeRethrow(copy(curr->exnref)); } Expression* visitBrOnExn(BrOnExn* curr) { return builder.makeBrOnExn( curr->name, curr->event, copy(curr->exnref), curr->sent); } Expression* visitNop(Nop* curr) { return builder.makeNop(); } Expression* visitUnreachable(Unreachable* curr) { return builder.makeUnreachable(); } Expression* visitPush(Push* curr) { return builder.makePush(copy(curr->value)); } Expression* visitPop(Pop* curr) { return builder.makePop(curr->type); } }; Copier copier(wasm, custom); return copier.copy(original); } // Splice an item into the middle of a block's list void spliceIntoBlock(Block* block, Index index, Expression* add) { auto& list = block->list; if (index == list.size()) { list.push_back(add); // simple append } else { // we need to make room list.push_back(nullptr); for (Index i = list.size() - 1; i > index; i--) { list[i] = list[i - 1]; } list[index] = add; } block->finalize(block->type); } } // namespace ExpressionManipulator } // namespace wasm binaryen-version_91/src/ir/LocalGraph.cpp000066400000000000000000000236341362402614000206100ustar00rootroot00000000000000/* * Copyright 2017 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include namespace wasm { namespace LocalGraphInternal { // Information about a basic block. struct Info { // actions occurring in this block: local.gets and local.sets std::vector actions; // for each index, the last local.set for it std::unordered_map lastSets; }; // flow helper class. flows the gets to their sets struct Flower : public CFGWalker, Info> { LocalGraph::GetSetses& getSetses; LocalGraph::Locations& locations; Flower(LocalGraph::GetSetses& getSetses, LocalGraph::Locations& locations, Function* func) : getSetses(getSetses), locations(locations) { setFunction(func); // create the CFG by walking the IR CFGWalker, Info>::doWalkFunction(func); // flow gets across blocks flow(func); } BasicBlock* makeBasicBlock() { return new BasicBlock(); } // cfg traversal work static void doVisitLocalGet(Flower* self, Expression** currp) { auto* curr = (*currp)->cast(); // if in unreachable code, skip if (!self->currBasicBlock) { return; } self->currBasicBlock->contents.actions.emplace_back(curr); self->locations[curr] = currp; } static void doVisitLocalSet(Flower* self, Expression** currp) { auto* curr = (*currp)->cast(); // if in unreachable code, skip if (!self->currBasicBlock) { return; } self->currBasicBlock->contents.actions.emplace_back(curr); self->currBasicBlock->contents.lastSets[curr->index] = curr; self->locations[curr] = currp; } void flow(Function* func) { // This block struct is optimized for this flow process (Minimal // information, iteration index). struct FlowBlock { // Last Traversed Iteration: This value helps us to find if this block has // been seen while traversing blocks. We compare this value to the current // iteration index in order to determine if we already process this block // in the current iteration. This speeds up the processing compared to // unordered_set or other struct usage. (No need to reset internal values, // lookup into container, ...) size_t lastTraversedIteration; std::vector actions; std::vector in; // Sor each index, the last local.set for it // The unordered_map from BasicBlock.Info is converted into a vector // This speeds up search as there are usually few sets in a block, so just // scanning them linearly is efficient, avoiding hash computations (while // in Info, it's convenient to have a map so we can assign them easily, // where the last one seen overwrites the previous; and, we do that O(1)). std::vector> lastSets; }; auto numLocals = func->getNumLocals(); std::vector> allGets; allGets.resize(numLocals); std::vector work; // Convert input blocks (basicBlocks) into more efficient flow blocks to // improve memory access. std::vector flowBlocks; flowBlocks.resize(basicBlocks.size()); // Init mapping between basicblocks and flowBlocks std::unordered_map basicToFlowMap; for (Index i = 0; i < basicBlocks.size(); ++i) { basicToFlowMap[basicBlocks[i].get()] = &flowBlocks[i]; } const size_t NULL_ITERATION = -1; FlowBlock* entryFlowBlock = nullptr; for (Index i = 0; i < flowBlocks.size(); ++i) { auto& block = basicBlocks[i]; auto& flowBlock = flowBlocks[i]; // Get the equivalent block to entry in the flow list if (block.get() == entry) { entryFlowBlock = &flowBlock; } flowBlock.lastTraversedIteration = NULL_ITERATION; flowBlock.actions.swap(block->contents.actions); // Map in block to flow blocks auto& in = block->in; flowBlock.in.resize(in.size()); std::transform(in.begin(), in.end(), flowBlock.in.begin(), [&](BasicBlock* block) { return basicToFlowMap[block]; }); // Convert unordered_map to vector. flowBlock.lastSets.reserve(block->contents.lastSets.size()); for (auto set : block->contents.lastSets) { flowBlock.lastSets.emplace_back(std::make_pair(set.first, set.second)); } } assert(entryFlowBlock != nullptr); size_t currentIteration = 0; for (auto& block : flowBlocks) { #ifdef LOCAL_GRAPH_DEBUG std::cout << "basic block " << block.get() << " :\n"; for (auto& action : block->contents.actions) { std::cout << " action: " << *action << '\n'; } for (auto* lastSet : block->contents.lastSets) { std::cout << " last set " << lastSet << '\n'; } #endif // go through the block, finding each get and adding it to its index, // and seeing how sets affect that auto& actions = block.actions; // move towards the front, handling things as we go for (int i = int(actions.size()) - 1; i >= 0; i--) { auto* action = actions[i]; if (auto* get = action->dynCast()) { allGets[get->index].push_back(get); } else { // This set is the only set for all those gets. auto* set = action->cast(); auto& gets = allGets[set->index]; for (auto* get : gets) { getSetses[get].insert(set); } gets.clear(); } } // If anything is left, we must flow it back through other blocks. we // can do that for all gets as a whole, they will get the same results. for (Index index = 0; index < numLocals; index++) { auto& gets = allGets[index]; if (gets.empty()) { continue; } work.push_back(&block); // Note that we may need to revisit the later parts of this initial // block, if we are in a loop, so don't mark it as seen. while (!work.empty()) { auto* curr = work.back(); work.pop_back(); // We have gone through this block; now we must handle flowing to // the inputs. if (curr->in.empty()) { if (curr == entryFlowBlock) { // These receive a param or zero init value. for (auto* get : gets) { getSetses[get].insert(nullptr); } } } else { for (auto* pred : curr->in) { if (pred->lastTraversedIteration == currentIteration) { // We've already seen pred in this iteration. continue; } pred->lastTraversedIteration = currentIteration; auto lastSet = std::find_if(pred->lastSets.begin(), pred->lastSets.end(), [&](std::pair& value) { return value.first == index; }); if (lastSet != pred->lastSets.end()) { // There is a set here, apply it, and stop the flow. for (auto* get : gets) { getSetses[get].insert(lastSet->second); } } else { // Keep on flowing. work.push_back(pred); } } } } gets.clear(); currentIteration++; } } } }; } // namespace LocalGraphInternal // LocalGraph implementation LocalGraph::LocalGraph(Function* func) { LocalGraphInternal::Flower flower(getSetses, locations, func); #ifdef LOCAL_GRAPH_DEBUG std::cout << "LocalGraph::dump\n"; for (auto& pair : getSetses) { auto* get = pair.first; auto& sets = pair.second; std::cout << "GET\n" << get << " is influenced by\n"; for (auto* set : sets) { std::cout << set << '\n'; } } std::cout << "total locations: " << locations.size() << '\n'; #endif } void LocalGraph::computeInfluences() { for (auto& pair : locations) { auto* curr = pair.first; if (auto* set = curr->dynCast()) { FindAll findAll(set->value); for (auto* get : findAll.list) { getInfluences[get].insert(set); } } else { auto* get = curr->cast(); for (auto* set : getSetses[get]) { setInfluences[set].insert(get); } } } } void LocalGraph::computeSSAIndexes() { std::unordered_map> indexSets; for (auto& pair : getSetses) { auto* get = pair.first; auto& sets = pair.second; for (auto* set : sets) { indexSets[get->index].insert(set); } } for (auto& pair : locations) { auto* curr = pair.first; if (auto* set = curr->dynCast()) { auto& sets = indexSets[set->index]; if (sets.size() == 1 && *sets.begin() != curr) { // While it has just one set, it is not the right one (us), // so mark it invalid. sets.clear(); } } } for (auto& pair : indexSets) { auto index = pair.first; auto& sets = pair.second; if (sets.size() == 1) { SSAIndexes.insert(index); } } } bool LocalGraph::isSSA(Index x) { return SSAIndexes.count(x); } } // namespace wasm binaryen-version_91/src/ir/ReFinalize.cpp000066400000000000000000000167731362402614000206320ustar00rootroot00000000000000/* * Copyright 2018 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ir/branch-utils.h" #include "ir/find_all.h" #include "ir/utils.h" namespace wasm { static Type getValueType(Expression* value) { return value ? value->type : Type::none; } namespace { // Handles a branch fixup for visitBlock: if the branch goes to the // target name, give it a value which is unreachable. template void handleBranchForVisitBlock(T* curr, Name name, Module* module) { if (BranchUtils::getUniqueTargets(curr).count(name)) { assert(!curr->value); Builder builder(*module); curr->value = builder.makeUnreachable(); } } } // anonymous namespace void ReFinalize::visitBlock(Block* curr) { if (curr->list.size() == 0) { curr->type = Type::none; return; } // Get the least upper bound type of the last element and all branch return // values curr->type = curr->list.back()->type; if (curr->name.is()) { auto iter = breakValues.find(curr->name); if (iter != breakValues.end()) { curr->type = Type::getLeastUpperBound(curr->type, iter->second); return; } } if (curr->type == Type::unreachable) { return; } // type is none, but we might be unreachable if (curr->type == Type::none) { for (auto* child : curr->list) { if (child->type == Type::unreachable) { curr->type = Type::unreachable; break; } } } } void ReFinalize::visitIf(If* curr) { curr->finalize(); } void ReFinalize::visitLoop(Loop* curr) { curr->finalize(); } void ReFinalize::visitBreak(Break* curr) { curr->finalize(); auto valueType = getValueType(curr->value); if (valueType == Type::unreachable) { replaceUntaken(curr->value, curr->condition); } else { updateBreakValueType(curr->name, valueType); } } void ReFinalize::visitSwitch(Switch* curr) { curr->finalize(); auto valueType = getValueType(curr->value); if (valueType == Type::unreachable) { replaceUntaken(curr->value, curr->condition); } else { for (auto target : curr->targets) { updateBreakValueType(target, valueType); } updateBreakValueType(curr->default_, valueType); } } void ReFinalize::visitCall(Call* curr) { curr->finalize(); } void ReFinalize::visitCallIndirect(CallIndirect* curr) { curr->finalize(); } void ReFinalize::visitLocalGet(LocalGet* curr) { curr->finalize(); } void ReFinalize::visitLocalSet(LocalSet* curr) { curr->finalize(); } void ReFinalize::visitGlobalGet(GlobalGet* curr) { curr->finalize(); } void ReFinalize::visitGlobalSet(GlobalSet* curr) { curr->finalize(); } void ReFinalize::visitLoad(Load* curr) { curr->finalize(); } void ReFinalize::visitStore(Store* curr) { curr->finalize(); } void ReFinalize::visitAtomicRMW(AtomicRMW* curr) { curr->finalize(); } void ReFinalize::visitAtomicCmpxchg(AtomicCmpxchg* curr) { curr->finalize(); } void ReFinalize::visitAtomicWait(AtomicWait* curr) { curr->finalize(); } void ReFinalize::visitAtomicNotify(AtomicNotify* curr) { curr->finalize(); } void ReFinalize::visitAtomicFence(AtomicFence* curr) { curr->finalize(); } void ReFinalize::visitSIMDExtract(SIMDExtract* curr) { curr->finalize(); } void ReFinalize::visitSIMDReplace(SIMDReplace* curr) { curr->finalize(); } void ReFinalize::visitSIMDShuffle(SIMDShuffle* curr) { curr->finalize(); } void ReFinalize::visitSIMDTernary(SIMDTernary* curr) { curr->finalize(); } void ReFinalize::visitSIMDShift(SIMDShift* curr) { curr->finalize(); } void ReFinalize::visitSIMDLoad(SIMDLoad* curr) { curr->finalize(); } void ReFinalize::visitMemoryInit(MemoryInit* curr) { curr->finalize(); } void ReFinalize::visitDataDrop(DataDrop* curr) { curr->finalize(); } void ReFinalize::visitMemoryCopy(MemoryCopy* curr) { curr->finalize(); } void ReFinalize::visitMemoryFill(MemoryFill* curr) { curr->finalize(); } void ReFinalize::visitConst(Const* curr) { curr->finalize(); } void ReFinalize::visitUnary(Unary* curr) { curr->finalize(); } void ReFinalize::visitBinary(Binary* curr) { curr->finalize(); } void ReFinalize::visitSelect(Select* curr) { curr->finalize(); } void ReFinalize::visitDrop(Drop* curr) { curr->finalize(); } void ReFinalize::visitReturn(Return* curr) { curr->finalize(); } void ReFinalize::visitHost(Host* curr) { curr->finalize(); } void ReFinalize::visitRefNull(RefNull* curr) { curr->finalize(); } void ReFinalize::visitRefIsNull(RefIsNull* curr) { curr->finalize(); } void ReFinalize::visitRefFunc(RefFunc* curr) { curr->finalize(); } void ReFinalize::visitTry(Try* curr) { curr->finalize(); } void ReFinalize::visitThrow(Throw* curr) { curr->finalize(); } void ReFinalize::visitRethrow(Rethrow* curr) { curr->finalize(); } void ReFinalize::visitBrOnExn(BrOnExn* curr) { curr->finalize(); if (curr->exnref->type == Type::unreachable) { replaceUntaken(curr->exnref, nullptr); } updateBreakValueType(curr->name, curr->sent); } void ReFinalize::visitNop(Nop* curr) { curr->finalize(); } void ReFinalize::visitUnreachable(Unreachable* curr) { curr->finalize(); } void ReFinalize::visitPush(Push* curr) { curr->finalize(); } void ReFinalize::visitPop(Pop* curr) { curr->finalize(); } void ReFinalize::visitFunction(Function* curr) { // we may have changed the body from unreachable to none, which might be bad // if the function has a return value if (curr->sig.results != Type::none && curr->body->type == Type::none) { Builder builder(*getModule()); curr->body = builder.blockify(curr->body, builder.makeUnreachable()); } } void ReFinalize::visitExport(Export* curr) { WASM_UNREACHABLE("unimp"); } void ReFinalize::visitGlobal(Global* curr) { WASM_UNREACHABLE("unimp"); } void ReFinalize::visitTable(Table* curr) { WASM_UNREACHABLE("unimp"); } void ReFinalize::visitMemory(Memory* curr) { WASM_UNREACHABLE("unimp"); } void ReFinalize::visitEvent(Event* curr) { WASM_UNREACHABLE("unimp"); } void ReFinalize::visitModule(Module* curr) { WASM_UNREACHABLE("unimp"); } void ReFinalize::updateBreakValueType(Name name, Type type) { if (type != Type::unreachable) { if (breakValues.count(name) == 0) { breakValues[name] = type; } else { breakValues[name] = Type::getLeastUpperBound(breakValues[name], type); } } } // Replace an untaken branch/switch with an unreachable value. // A condition may also exist and may or may not be unreachable. void ReFinalize::replaceUntaken(Expression* value, Expression* condition) { assert(value->type == Type::unreachable); auto* replacement = value; if (condition) { Builder builder(*getModule()); // Even if we have // (block // (unreachable) // (i32.const 1) // ) // we want the block type to be unreachable. That is valid as // the value is unreachable, and necessary since the type of // the condition did not have an impact before (the break/switch // type was unreachable), and might not fit in. if (condition->type.isConcrete()) { condition = builder.makeDrop(condition); } replacement = builder.makeSequence(value, condition); assert(replacement->type.getSingle()); } replaceCurrent(replacement); } } // namespace wasm binaryen-version_91/src/ir/abstract.h000066400000000000000000000115241362402614000200370ustar00rootroot00000000000000/* * Copyright 2018 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // Abstracts out operations from specific opcodes. #ifndef wasm_ir_abstract_h #define wasm_ir_abstract_h #include namespace wasm { namespace Abstract { enum Op { // Unary Neg, // Binary Add, Sub, Mul, DivU, DivS, Rem, RemU, RemS, Shl, ShrU, ShrS, And, Or, Xor, // Relational Eq, Ne, }; // Provide a wasm type and an abstract op and get the concrete one. For example, // you can provide i32 and Add and receive the specific opcode for a 32-bit // addition, AddInt32. If the op does not exist, it returns Invalid. inline UnaryOp getUnary(Type type, Op op) { switch (type.getSingle()) { case Type::i32: { return InvalidUnary; } case Type::i64: { return InvalidUnary; } case Type::f32: { switch (op) { case Neg: return NegFloat32; default: return InvalidUnary; } break; } case Type::f64: { switch (op) { case Neg: return NegFloat64; default: return InvalidUnary; } break; } case Type::v128: { WASM_UNREACHABLE("v128 not implemented yet"); } case Type::funcref: case Type::anyref: case Type::nullref: case Type::exnref: case Type::none: case Type::unreachable: { return InvalidUnary; } } WASM_UNREACHABLE("invalid type"); } inline BinaryOp getBinary(Type type, Op op) { switch (type.getSingle()) { case Type::i32: { switch (op) { case Add: return AddInt32; case Sub: return SubInt32; case Mul: return MulInt32; case DivU: return DivUInt32; case DivS: return DivSInt32; case RemU: return RemUInt32; case RemS: return RemSInt32; case Shl: return ShlInt32; case ShrU: return ShrUInt32; case ShrS: return ShrSInt32; case And: return AndInt32; case Or: return OrInt32; case Xor: return XorInt32; case Eq: return EqInt32; case Ne: return NeInt32; default: return InvalidBinary; } break; } case Type::i64: { switch (op) { case Add: return AddInt64; case Sub: return SubInt64; case Mul: return MulInt64; case DivU: return DivUInt64; case DivS: return DivSInt64; case RemU: return RemUInt64; case RemS: return RemSInt64; case Shl: return ShlInt64; case ShrU: return ShrUInt64; case ShrS: return ShrSInt64; case And: return AndInt64; case Or: return OrInt64; case Xor: return XorInt64; case Eq: return EqInt64; case Ne: return NeInt64; default: return InvalidBinary; } break; } case Type::f32: { switch (op) { case Add: return AddFloat32; case Sub: return SubFloat32; case Mul: return MulFloat32; case DivU: return DivFloat32; case DivS: return DivFloat32; case Eq: return EqFloat32; case Ne: return NeFloat32; default: return InvalidBinary; } break; } case Type::f64: { switch (op) { case Add: return AddFloat64; case Sub: return SubFloat64; case Mul: return MulFloat64; case DivU: return DivFloat64; case DivS: return DivFloat64; case Eq: return EqFloat64; case Ne: return NeFloat64; default: return InvalidBinary; } break; } case Type::v128: { WASM_UNREACHABLE("v128 not implemented yet"); } case Type::funcref: case Type::anyref: case Type::nullref: case Type::exnref: case Type::none: case Type::unreachable: { return InvalidBinary; } } WASM_UNREACHABLE("invalid type"); } } // namespace Abstract } // namespace wasm #endif // wasm_ir_abstract_h binaryen-version_91/src/ir/bits.h000066400000000000000000000066071362402614000172030ustar00rootroot00000000000000/* * Copyright 2017 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_ir_bits_h #define wasm_ir_bits_h #include "ir/literal-utils.h" #include "support/bits.h" #include "wasm-builder.h" namespace wasm { struct Bits { // get a mask to keep only the low # of bits static int32_t lowBitMask(int32_t bits) { uint32_t ret = -1; if (bits >= 32) { return ret; } return ret >> (32 - bits); } // checks if the input is a mask of lower bits, i.e., all 1s up to some high // bit, and all zeros from there. returns the number of masked bits, or 0 if // this is not such a mask static uint32_t getMaskedBits(uint32_t mask) { if (mask == uint32_t(-1)) { return 32; // all the bits } if (mask == 0) { return 0; // trivially not a mask } // otherwise, see if adding one turns this into a 1-bit thing, 00011111 + 1 // => 00100000 if (PopCount(mask + 1) != 1) { return 0; } // this is indeed a mask return 32 - CountLeadingZeroes(mask); } // gets the number of effective shifts a shift operation does. In // wasm, only 5 bits matter for 32-bit shifts, and 6 for 64. static Index getEffectiveShifts(Index amount, Type type) { if (type == Type::i32) { return amount & 31; } else if (type == Type::i64) { return amount & 63; } WASM_UNREACHABLE("unexpected type"); } static Index getEffectiveShifts(Expression* expr) { auto* amount = expr->cast(); if (amount->type == Type::i32) { return getEffectiveShifts(amount->value.geti32(), Type::i32); } else if (amount->type == Type::i64) { return getEffectiveShifts(amount->value.geti64(), Type::i64); } WASM_UNREACHABLE("unexpected type"); } static Expression* makeSignExt(Expression* value, Index bytes, Module& wasm) { if (value->type == Type::i32) { if (bytes == 1 || bytes == 2) { auto shifts = bytes == 1 ? 24 : 16; Builder builder(wasm); return builder.makeBinary( ShrSInt32, builder.makeBinary( ShlInt32, value, LiteralUtils::makeFromInt32(shifts, Type::i32, wasm)), LiteralUtils::makeFromInt32(shifts, Type::i32, wasm)); } assert(bytes == 4); return value; // nothing to do } else { assert(value->type == Type::i64); if (bytes == 1 || bytes == 2 || bytes == 4) { auto shifts = bytes == 1 ? 56 : (bytes == 2 ? 48 : 32); Builder builder(wasm); return builder.makeBinary( ShrSInt64, builder.makeBinary( ShlInt64, value, LiteralUtils::makeFromInt32(shifts, Type::i64, wasm)), LiteralUtils::makeFromInt32(shifts, Type::i64, wasm)); } assert(bytes == 8); return value; // nothing to do } } }; } // namespace wasm #endif // wasm_ir_bits_h binaryen-version_91/src/ir/block-utils.h000066400000000000000000000053421362402614000204650ustar00rootroot00000000000000/* * Copyright 2017 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_ir_block_h #define wasm_ir_block_h #include "ir/branch-utils.h" #include "ir/effects.h" #include "ir/manipulation.h" #include "literal.h" #include "wasm-builder.h" #include "wasm.h" namespace wasm { namespace BlockUtils { // if a block has just one element, it can often be replaced // with that content template inline Expression* simplifyToContents(Block* block, T* parent, bool allowTypeChange = false) { auto& list = block->list; if (list.size() == 1 && !BranchUtils::BranchSeeker::has(list[0], block->name)) { // just one element. try to replace the block auto* singleton = list[0]; auto sideEffects = EffectAnalyzer(parent->getPassOptions(), parent->getModule()->features, singleton) .hasSideEffects(); if (!sideEffects && !singleton->type.isConcrete()) { // no side effects, and singleton is not returning a value, so we can // throw away the block and its contents, basically return Builder(*parent->getModule()).replaceWithIdenticalType(block); } else if (Type::isSubType(singleton->type, block->type) || allowTypeChange) { return singleton; } else { // (side effects +) type change, must be block with declared value but // inside is unreachable (if both concrete, must match, and since no name // on block, we can't be branched to, so if singleton is unreachable, so // is the block) assert(block->type.isConcrete() && singleton->type == Type::unreachable); // we could replace with unreachable, but would need to update all // the parent's types } } else if (list.size() == 0) { ExpressionManipulator::nop(block); } return block; } // similar, but when we allow the type to change while doing so template inline Expression* simplifyToContentsWithPossibleTypeChange(Block* block, T* parent) { return simplifyToContents(block, parent, true); } } // namespace BlockUtils } // namespace wasm #endif // wasm_ir_block_h binaryen-version_91/src/ir/branch-utils.h000066400000000000000000000130221362402614000206220ustar00rootroot00000000000000/* * Copyright 2017 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_ir_branch_h #define wasm_ir_branch_h #include "wasm-traversal.h" #include "wasm.h" namespace wasm { namespace BranchUtils { // Some branches are obviously not actually reachable (e.g. (br $out // (unreachable))) inline bool isBranchReachable(Break* br) { return !(br->value && br->value->type == Type::unreachable) && !(br->condition && br->condition->type == Type::unreachable); } inline bool isBranchReachable(Switch* sw) { return !(sw->value && sw->value->type == Type::unreachable) && sw->condition->type != Type::unreachable; } inline bool isBranchReachable(BrOnExn* br) { return br->exnref->type != Type::unreachable; } inline bool isBranchReachable(Expression* expr) { if (auto* br = expr->dynCast()) { return isBranchReachable(br); } else if (auto* sw = expr->dynCast()) { return isBranchReachable(sw); } else if (auto* br = expr->dynCast()) { return isBranchReachable(br); } WASM_UNREACHABLE("unexpected expression type"); } inline std::set getUniqueTargets(Break* br) { return {br->name}; } inline std::set getUniqueTargets(Switch* sw) { std::set ret; for (auto target : sw->targets) { ret.insert(target); } ret.insert(sw->default_); return ret; } inline std::set getUniqueTargets(BrOnExn* br) { return {br->name}; } // If we branch to 'from', change that to 'to' instead. inline bool replacePossibleTarget(Expression* branch, Name from, Name to) { bool worked = false; if (auto* br = branch->dynCast()) { if (br->name == from) { br->name = to; worked = true; } } else if (auto* sw = branch->dynCast()) { for (auto& target : sw->targets) { if (target == from) { target = to; worked = true; } } if (sw->default_ == from) { sw->default_ = to; worked = true; } } else if (auto* br = branch->dynCast()) { if (br->name == from) { br->name = to; worked = true; } } else { WASM_UNREACHABLE("unexpected expression type"); } return worked; } // returns the set of targets to which we branch that are // outside of a node inline std::set getExitingBranches(Expression* ast) { struct Scanner : public PostWalker { std::set targets; void visitBreak(Break* curr) { targets.insert(curr->name); } void visitSwitch(Switch* curr) { for (auto target : curr->targets) { targets.insert(target); } targets.insert(curr->default_); } void visitBrOnExn(BrOnExn* curr) { targets.insert(curr->name); } void visitBlock(Block* curr) { if (curr->name.is()) { targets.erase(curr->name); } } void visitLoop(Loop* curr) { if (curr->name.is()) { targets.erase(curr->name); } } }; Scanner scanner; scanner.walk(ast); // anything not erased is a branch out return scanner.targets; } // returns the list of all branch targets in a node inline std::set getBranchTargets(Expression* ast) { struct Scanner : public PostWalker { std::set targets; void visitBlock(Block* curr) { if (curr->name.is()) { targets.insert(curr->name); } } void visitLoop(Loop* curr) { if (curr->name.is()) { targets.insert(curr->name); } } }; Scanner scanner; scanner.walk(ast); return scanner.targets; } // Finds if there are branches targeting a name. Note that since names are // unique in our IR, we just need to look for the name, and do not need // to analyze scoping. struct BranchSeeker : public PostWalker { Name target; Index found = 0; Type valueType; BranchSeeker(Name target) : target(target) {} void noteFound(Expression* value) { noteFound(value ? value->type : Type::none); } void noteFound(Type type) { found++; if (found == 1) { valueType = Type::unreachable; } if (type != Type::unreachable) { valueType = type; } } void visitBreak(Break* curr) { // check the break if (curr->name == target) { noteFound(curr->value); } } void visitSwitch(Switch* curr) { // check the switch for (auto name : curr->targets) { if (name == target) { noteFound(curr->value); } } if (curr->default_ == target) { noteFound(curr->value); } } void visitBrOnExn(BrOnExn* curr) { // check the br_on_exn if (curr->name == target) { noteFound(curr->sent); } } static bool has(Expression* tree, Name target) { if (!target.is()) { return false; } BranchSeeker seeker(target); seeker.walk(tree); return seeker.found > 0; } static Index count(Expression* tree, Name target) { if (!target.is()) { return 0; } BranchSeeker seeker(target); seeker.walk(tree); return seeker.found; } }; } // namespace BranchUtils } // namespace wasm #endif // wasm_ir_branch_h binaryen-version_91/src/ir/cost.h000066400000000000000000000404051362402614000172040ustar00rootroot00000000000000/* * Copyright 2016 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_ir_cost_h #define wasm_ir_cost_h #include #include namespace wasm { // Measure the execution cost of an AST. Very handwave-ey struct CostAnalyzer : public Visitor { CostAnalyzer(Expression* ast) { cost = visit(ast); } Index cost; Index maybeVisit(Expression* curr) { return curr ? visit(curr) : 0; } Index visitBlock(Block* curr) { Index ret = 0; for (auto* child : curr->list) { ret += visit(child); } return ret; } Index visitIf(If* curr) { return 1 + visit(curr->condition) + std::max(visit(curr->ifTrue), maybeVisit(curr->ifFalse)); } Index visitLoop(Loop* curr) { return 5 * visit(curr->body); } Index visitBreak(Break* curr) { return 1 + maybeVisit(curr->value) + maybeVisit(curr->condition); } Index visitSwitch(Switch* curr) { return 2 + visit(curr->condition) + maybeVisit(curr->value); } Index visitCall(Call* curr) { // XXX this does not take into account if the call is to an import, which // may be costlier in general Index ret = 4; for (auto* child : curr->operands) { ret += visit(child); } return ret; } Index visitCallIndirect(CallIndirect* curr) { Index ret = 6 + visit(curr->target); for (auto* child : curr->operands) { ret += visit(child); } return ret; } Index visitLocalGet(LocalGet* curr) { return 0; } Index visitLocalSet(LocalSet* curr) { return 1; } Index visitGlobalGet(GlobalGet* curr) { return 1; } Index visitGlobalSet(GlobalSet* curr) { return 2; } Index visitLoad(Load* curr) { return 1 + visit(curr->ptr) + 10 * curr->isAtomic; } Index visitStore(Store* curr) { return 2 + visit(curr->ptr) + visit(curr->value) + 10 * curr->isAtomic; } Index visitAtomicRMW(AtomicRMW* curr) { return 100; } Index visitAtomicCmpxchg(AtomicCmpxchg* curr) { return 100; } Index visitConst(Const* curr) { return 1; } Index visitUnary(Unary* curr) { Index ret = 0; switch (curr->op) { case ClzInt32: case CtzInt32: case PopcntInt32: case NegFloat32: case AbsFloat32: case CeilFloat32: case FloorFloat32: case TruncFloat32: case NearestFloat32: case ClzInt64: case CtzInt64: case PopcntInt64: case NegFloat64: case AbsFloat64: case CeilFloat64: case FloorFloat64: case TruncFloat64: case NearestFloat64: case EqZInt32: case EqZInt64: case ExtendSInt32: case ExtendUInt32: case WrapInt64: case PromoteFloat32: case DemoteFloat64: case TruncSFloat32ToInt32: case TruncUFloat32ToInt32: case TruncSFloat64ToInt32: case TruncUFloat64ToInt32: case ReinterpretFloat32: case TruncSFloat32ToInt64: case TruncUFloat32ToInt64: case TruncSFloat64ToInt64: case TruncUFloat64ToInt64: case ReinterpretFloat64: case ReinterpretInt32: case ConvertSInt32ToFloat32: case ConvertUInt32ToFloat32: case ConvertSInt64ToFloat32: case ConvertUInt64ToFloat32: case ReinterpretInt64: case ConvertSInt32ToFloat64: case ConvertUInt32ToFloat64: case ConvertSInt64ToFloat64: case ConvertUInt64ToFloat64: case ExtendS8Int32: case ExtendS16Int32: case ExtendS8Int64: case ExtendS16Int64: case ExtendS32Int64: case TruncSatSFloat32ToInt32: case TruncSatUFloat32ToInt32: case TruncSatSFloat64ToInt32: case TruncSatUFloat64ToInt32: case TruncSatSFloat32ToInt64: case TruncSatUFloat32ToInt64: case TruncSatSFloat64ToInt64: case TruncSatUFloat64ToInt64: ret = 1; break; case SqrtFloat32: case SqrtFloat64: ret = 2; break; case SplatVecI8x16: case SplatVecI16x8: case SplatVecI32x4: case SplatVecI64x2: case SplatVecF32x4: case SplatVecF64x2: case NotVec128: case NegVecI8x16: case AnyTrueVecI8x16: case AllTrueVecI8x16: case NegVecI16x8: case AnyTrueVecI16x8: case AllTrueVecI16x8: case NegVecI32x4: case AnyTrueVecI32x4: case AllTrueVecI32x4: case NegVecI64x2: case AnyTrueVecI64x2: case AllTrueVecI64x2: case AbsVecF32x4: case NegVecF32x4: case SqrtVecF32x4: case AbsVecF64x2: case NegVecF64x2: case SqrtVecF64x2: case TruncSatSVecF32x4ToVecI32x4: case TruncSatUVecF32x4ToVecI32x4: case TruncSatSVecF64x2ToVecI64x2: case TruncSatUVecF64x2ToVecI64x2: case ConvertSVecI32x4ToVecF32x4: case ConvertUVecI32x4ToVecF32x4: case ConvertSVecI64x2ToVecF64x2: case ConvertUVecI64x2ToVecF64x2: case WidenLowSVecI8x16ToVecI16x8: case WidenHighSVecI8x16ToVecI16x8: case WidenLowUVecI8x16ToVecI16x8: case WidenHighUVecI8x16ToVecI16x8: case WidenLowSVecI16x8ToVecI32x4: case WidenHighSVecI16x8ToVecI32x4: case WidenLowUVecI16x8ToVecI32x4: case WidenHighUVecI16x8ToVecI32x4: return 1; case InvalidUnary: WASM_UNREACHABLE("invalid unary op"); } return ret + visit(curr->value); } Index visitBinary(Binary* curr) { Index ret = 0; switch (curr->op) { case AddInt32: ret = 1; break; case SubInt32: ret = 1; break; case MulInt32: ret = 2; break; case DivSInt32: ret = 3; break; case DivUInt32: ret = 3; break; case RemSInt32: ret = 3; break; case RemUInt32: ret = 3; break; case AndInt32: ret = 1; break; case OrInt32: ret = 1; break; case XorInt32: ret = 1; break; case ShlInt32: ret = 1; break; case ShrUInt32: ret = 1; break; case ShrSInt32: ret = 1; break; case RotLInt32: ret = 1; break; case RotRInt32: ret = 1; break; case AddInt64: ret = 1; break; case SubInt64: ret = 1; break; case MulInt64: ret = 2; break; case DivSInt64: ret = 3; break; case DivUInt64: ret = 3; break; case RemSInt64: ret = 3; break; case RemUInt64: ret = 3; break; case AndInt64: ret = 1; break; case OrInt64: ret = 1; break; case XorInt64: ret = 1; break; case ShlInt64: ret = 1; break; case ShrUInt64: ret = 1; break; case ShrSInt64: ret = 1; break; case RotLInt64: ret = 1; break; case RotRInt64: ret = 1; break; case AddFloat32: ret = 1; break; case SubFloat32: ret = 1; break; case MulFloat32: ret = 2; break; case DivFloat32: ret = 3; break; case CopySignFloat32: ret = 1; break; case MinFloat32: ret = 1; break; case MaxFloat32: ret = 1; break; case AddFloat64: ret = 1; break; case SubFloat64: ret = 1; break; case MulFloat64: ret = 2; break; case DivFloat64: ret = 3; break; case CopySignFloat64: ret = 1; break; case MinFloat64: ret = 1; break; case MaxFloat64: ret = 1; break; case LtUInt32: ret = 1; break; case LtSInt32: ret = 1; break; case LeUInt32: ret = 1; break; case LeSInt32: ret = 1; break; case GtUInt32: ret = 1; break; case GtSInt32: ret = 1; break; case GeUInt32: ret = 1; break; case GeSInt32: ret = 1; break; case LtUInt64: ret = 1; break; case LtSInt64: ret = 1; break; case LeUInt64: ret = 1; break; case LeSInt64: ret = 1; break; case GtUInt64: ret = 1; break; case GtSInt64: ret = 1; break; case GeUInt64: ret = 1; break; case GeSInt64: ret = 1; break; case LtFloat32: ret = 1; break; case GtFloat32: ret = 1; break; case LeFloat32: ret = 1; break; case GeFloat32: ret = 1; break; case LtFloat64: ret = 1; break; case GtFloat64: ret = 1; break; case LeFloat64: ret = 1; break; case GeFloat64: ret = 1; break; case EqInt32: ret = 1; break; case NeInt32: ret = 1; break; case EqInt64: ret = 1; break; case NeInt64: ret = 1; break; case EqFloat32: ret = 1; break; case NeFloat32: ret = 1; break; case EqFloat64: ret = 1; break; case NeFloat64: ret = 1; break; case EqVecI8x16: ret = 1; break; case NeVecI8x16: ret = 1; break; case LtSVecI8x16: ret = 1; break; case LtUVecI8x16: ret = 1; break; case LeSVecI8x16: ret = 1; break; case LeUVecI8x16: ret = 1; break; case GtSVecI8x16: ret = 1; break; case GtUVecI8x16: ret = 1; break; case GeSVecI8x16: ret = 1; break; case GeUVecI8x16: ret = 1; break; case EqVecI16x8: ret = 1; break; case NeVecI16x8: ret = 1; break; case LtSVecI16x8: ret = 1; break; case LtUVecI16x8: ret = 1; break; case LeSVecI16x8: ret = 1; break; case LeUVecI16x8: ret = 1; break; case GtSVecI16x8: ret = 1; break; case GtUVecI16x8: ret = 1; break; case GeSVecI16x8: ret = 1; break; case GeUVecI16x8: ret = 1; break; case EqVecI32x4: ret = 1; break; case NeVecI32x4: ret = 1; break; case LtSVecI32x4: ret = 1; break; case LtUVecI32x4: ret = 1; break; case LeSVecI32x4: ret = 1; break; case LeUVecI32x4: ret = 1; break; case GtSVecI32x4: ret = 1; break; case GtUVecI32x4: ret = 1; break; case GeSVecI32x4: ret = 1; break; case GeUVecI32x4: ret = 1; break; case EqVecF32x4: ret = 1; break; case NeVecF32x4: ret = 1; break; case LtVecF32x4: ret = 1; break; case LeVecF32x4: ret = 1; break; case GtVecF32x4: ret = 1; break; case GeVecF32x4: ret = 1; break; case EqVecF64x2: ret = 1; break; case NeVecF64x2: ret = 1; break; case LtVecF64x2: ret = 1; break; case LeVecF64x2: ret = 1; break; case GtVecF64x2: ret = 1; break; case GeVecF64x2: ret = 1; break; case AndVec128: ret = 1; break; case OrVec128: ret = 1; break; case XorVec128: ret = 1; break; case AndNotVec128: ret = 1; break; case AddVecI8x16: ret = 1; break; case AddSatSVecI8x16: ret = 1; break; case AddSatUVecI8x16: ret = 1; break; case SubVecI8x16: ret = 1; break; case SubSatSVecI8x16: ret = 1; break; case SubSatUVecI8x16: ret = 1; break; case MulVecI8x16: ret = 2; break; case MinSVecI8x16: ret = 1; break; case MinUVecI8x16: ret = 1; break; case MaxSVecI8x16: ret = 1; break; case MaxUVecI8x16: ret = 1; break; case AvgrUVecI8x16: ret = 1; break; case AddVecI16x8: ret = 1; break; case AddSatSVecI16x8: ret = 1; break; case AddSatUVecI16x8: ret = 1; break; case SubVecI16x8: ret = 1; break; case SubSatSVecI16x8: ret = 1; break; case SubSatUVecI16x8: ret = 1; break; case MulVecI16x8: ret = 2; break; case MinSVecI16x8: ret = 1; break; case MinUVecI16x8: ret = 1; break; case MaxSVecI16x8: ret = 1; break; case MaxUVecI16x8: ret = 1; break; case AvgrUVecI16x8: ret = 1; break; case AddVecI32x4: ret = 1; break; case SubVecI32x4: ret = 1; break; case MulVecI32x4: ret = 2; break; case MinSVecI32x4: ret = 1; break; case MinUVecI32x4: ret = 1; break; case MaxSVecI32x4: ret = 1; break; case MaxUVecI32x4: ret = 1; break; case DotSVecI16x8ToVecI32x4: ret = 1; break; case AddVecI64x2: ret = 1; break; case SubVecI64x2: ret = 1; break; case AddVecF32x4: ret = 1; break; case SubVecF32x4: ret = 1; break; case MulVecF32x4: ret = 2; break; case DivVecF32x4: ret = 3; break; case MinVecF32x4: ret = 1; break; case MaxVecF32x4: ret = 1; break; case AddVecF64x2: ret = 1; break; case SubVecF64x2: ret = 1; break; case MulVecF64x2: ret = 2; break; case DivVecF64x2: ret = 3; break; case MinVecF64x2: ret = 1; break; case MaxVecF64x2: ret = 1; break; case NarrowSVecI16x8ToVecI8x16: ret = 1; break; case NarrowUVecI16x8ToVecI8x16: ret = 1; break; case NarrowSVecI32x4ToVecI16x8: ret = 1; break; case NarrowUVecI32x4ToVecI16x8: ret = 1; break; case SwizzleVec8x16: ret = 1; break; case InvalidBinary: WASM_UNREACHABLE("invalid binary op"); } return ret + visit(curr->left) + visit(curr->right); } Index visitSelect(Select* curr) { return 2 + visit(curr->condition) + visit(curr->ifTrue) + visit(curr->ifFalse); } Index visitDrop(Drop* curr) { return visit(curr->value); } Index visitReturn(Return* curr) { return maybeVisit(curr->value); } Index visitHost(Host* curr) { return 100; } Index visitRefNull(RefNull* curr) { return 1; } Index visitRefIsNull(RefIsNull* curr) { return 1; } Index visitRefFunc(RefFunc* curr) { return 1; } Index visitTry(Try* curr) { // We assume no exception will be thrown in most cases return visit(curr->body); } Index visitThrow(Throw* curr) { return 100; } Index visitRethrow(Rethrow* curr) { return 100; } Index visitBrOnExn(BrOnExn* curr) { return 1 + visit(curr->exnref) + curr->sent.size(); } Index visitNop(Nop* curr) { return 0; } Index visitUnreachable(Unreachable* curr) { return 0; } }; } // namespace wasm #endif // wasm_ir_cost_h binaryen-version_91/src/ir/debug.h000066400000000000000000000033671362402614000173300ustar00rootroot00000000000000/* * Copyright 2019 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_ir_debug_h #define wasm_ir_debug_h #include namespace wasm { namespace debug { // Given an expression and a copy of it in another function, copy the debug // info into the second function. inline void copyDebugInfo(Expression* origin, Expression* copy, Function* originFunc, Function* copyFunc) { struct Lister : public PostWalker> { std::vector list; void visitExpression(Expression* curr) { list.push_back(curr); } }; Lister originList; originList.walk(origin); Lister copyList; copyList.walk(copy); auto& originDebug = originFunc->debugLocations; auto& copyDebug = copyFunc->debugLocations; assert(originList.list.size() == copyList.list.size()); for (Index i = 0; i < originList.list.size(); i++) { auto iter = originDebug.find(originList.list[i]); if (iter != originDebug.end()) { auto location = iter->second; copyDebug[copyList.list[i]] = location; } } }; } // namespace debug } // namespace wasm #endif // wasm_ir_debug_h binaryen-version_91/src/ir/effects.h000066400000000000000000000366351362402614000176650ustar00rootroot00000000000000/* * Copyright 2017 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_ir_effects_h #define wasm_ir_effects_h #include "pass.h" #include "wasm-traversal.h" namespace wasm { // Look for side effects, including control flow // TODO: optimize struct EffectAnalyzer : public PostWalker> { EffectAnalyzer(const PassOptions& passOptions, FeatureSet features, Expression* ast = nullptr) : ignoreImplicitTraps(passOptions.ignoreImplicitTraps), debugInfo(passOptions.debugInfo), features(features) { if (ast) { analyze(ast); } } bool ignoreImplicitTraps; bool debugInfo; FeatureSet features; void analyze(Expression* ast) { breakNames.clear(); walk(ast); // if we are left with breaks, they are external if (breakNames.size() > 0) { branches = true; } assert(tryDepth == 0); } // Core effect tracking // branches out of this expression, returns, infinite loops, etc bool branches = false; bool calls = false; std::set localsRead; std::set localsWritten; std::set globalsRead; std::set globalsWritten; bool readsMemory = false; bool writesMemory = false; // a load or div/rem, which may trap. we ignore trap differences, so it is ok // to reorder these, but we can't remove them, as they count as side effects, // and we can't move them in a way that would cause other noticeable (global) // side effects bool implicitTrap = false; // An atomic load/store/RMW/Cmpxchg or an operator that has a defined ordering // wrt atomics (e.g. memory.grow) bool isAtomic = false; bool throws = false; // The nested depth of try. If an instruction that may throw is inside an // inner try, we don't mark it as 'throws', because it will be caught by an // inner catch. size_t tryDepth = 0; static void scan(EffectAnalyzer* self, Expression** currp) { Expression* curr = *currp; // We need to decrement try depth before catch starts, so handle it // separately if (curr->is()) { self->pushTask(doVisitTry, currp); self->pushTask(scan, &curr->cast()->catchBody); self->pushTask(doStartCatch, currp); self->pushTask(scan, &curr->cast()->body); self->pushTask(doStartTry, currp); return; } PostWalker>::scan(self, currp); } static void doStartTry(EffectAnalyzer* self, Expression** currp) { self->tryDepth++; } static void doStartCatch(EffectAnalyzer* self, Expression** currp) { assert(self->tryDepth > 0 && "try depth cannot be negative"); self->tryDepth--; } // Helper functions to check for various effect types bool accessesLocal() const { return localsRead.size() + localsWritten.size() > 0; } bool accessesGlobal() const { return globalsRead.size() + globalsWritten.size() > 0; } bool accessesMemory() const { return calls || readsMemory || writesMemory; } bool transfersControlFlow() const { return branches || throws; } bool hasGlobalSideEffects() const { return calls || globalsWritten.size() > 0 || writesMemory || isAtomic || throws; } bool hasSideEffects() const { return hasGlobalSideEffects() || localsWritten.size() > 0 || branches || implicitTrap; } bool hasAnything() const { return calls || accessesLocal() || readsMemory || writesMemory || accessesGlobal() || implicitTrap || isAtomic || transfersControlFlow(); } bool noticesGlobalSideEffects() { return calls || readsMemory || isAtomic || globalsRead.size(); } // check if we break to anything external from ourselves bool hasExternalBreakTargets() { return !breakNames.empty(); } // checks if these effects would invalidate another set (e.g., if we write, we // invalidate someone that reads, they can't be moved past us) bool invalidates(const EffectAnalyzer& other) { if ((transfersControlFlow() && other.hasSideEffects()) || (other.transfersControlFlow() && hasSideEffects()) || ((writesMemory || calls) && other.accessesMemory()) || (accessesMemory() && (other.writesMemory || other.calls))) { return true; } // All atomics are sequentially consistent for now, and ordered wrt other // memory references. if ((isAtomic && other.accessesMemory()) || (other.isAtomic && accessesMemory())) { return true; } for (auto local : localsWritten) { if (other.localsWritten.count(local) || other.localsRead.count(local)) { return true; } } for (auto local : localsRead) { if (other.localsWritten.count(local)) { return true; } } if ((accessesGlobal() && other.calls) || (other.accessesGlobal() && calls)) { return true; } for (auto global : globalsWritten) { if (other.globalsWritten.count(global) || other.globalsRead.count(global)) { return true; } } for (auto global : globalsRead) { if (other.globalsWritten.count(global)) { return true; } } // we are ok to reorder implicit traps, but not conditionalize them if ((implicitTrap && other.transfersControlFlow()) || (other.implicitTrap && transfersControlFlow())) { return true; } // we can't reorder an implicit trap in a way that alters global state if ((implicitTrap && other.hasGlobalSideEffects()) || (other.implicitTrap && hasGlobalSideEffects())) { return true; } return false; } void mergeIn(EffectAnalyzer& other) { branches = branches || other.branches; calls = calls || other.calls; readsMemory = readsMemory || other.readsMemory; writesMemory = writesMemory || other.writesMemory; implicitTrap = implicitTrap || other.implicitTrap; isAtomic = isAtomic || other.isAtomic; throws = throws || other.throws; for (auto i : other.localsRead) { localsRead.insert(i); } for (auto i : other.localsWritten) { localsWritten.insert(i); } for (auto i : other.globalsRead) { globalsRead.insert(i); } for (auto i : other.globalsWritten) { globalsWritten.insert(i); } } // the checks above happen after the node's children were processed, in the // order of execution we must also check for control flow that happens before // the children, i.e., loops bool checkPre(Expression* curr) { if (curr->is()) { branches = true; return true; } return false; } bool checkPost(Expression* curr) { visit(curr); if (curr->is()) { branches = true; } return hasAnything(); } std::set breakNames; void visitBlock(Block* curr) { if (curr->name.is()) { breakNames.erase(curr->name); // these were internal breaks } } void visitIf(If* curr) {} void visitLoop(Loop* curr) { if (curr->name.is()) { breakNames.erase(curr->name); // these were internal breaks } // if the loop is unreachable, then there is branching control flow: // (1) if the body is unreachable because of a (return), uncaught (br) // etc., then we already noted branching, so it is ok to mark it again // (if we have *caught* (br)s, then they did not lead to the loop body // being unreachable). (same logic applies to blocks) // (2) if the loop is unreachable because it only has branches up to the // loop top, but no way to get out, then it is an infinite loop, and we // consider that a branching side effect (note how the same logic does // not apply to blocks). if (curr->type == Type::unreachable) { branches = true; } } void visitBreak(Break* curr) { breakNames.insert(curr->name); } void visitSwitch(Switch* curr) { for (auto name : curr->targets) { breakNames.insert(name); } breakNames.insert(curr->default_); } void visitCall(Call* curr) { calls = true; // When EH is enabled, any call can throw. if (features.hasExceptionHandling() && tryDepth == 0) { throws = true; } if (curr->isReturn) { branches = true; } if (debugInfo) { // debugInfo call imports must be preserved very strongly, do not // move code around them // FIXME: we could check if the call is to an import branches = true; } } void visitCallIndirect(CallIndirect* curr) { calls = true; if (features.hasExceptionHandling() && tryDepth == 0) { throws = true; } if (curr->isReturn) { branches = true; } } void visitLocalGet(LocalGet* curr) { localsRead.insert(curr->index); } void visitLocalSet(LocalSet* curr) { localsWritten.insert(curr->index); } void visitGlobalGet(GlobalGet* curr) { globalsRead.insert(curr->name); } void visitGlobalSet(GlobalSet* curr) { globalsWritten.insert(curr->name); } void visitLoad(Load* curr) { readsMemory = true; isAtomic |= curr->isAtomic; if (!ignoreImplicitTraps) { implicitTrap = true; } } void visitStore(Store* curr) { writesMemory = true; isAtomic |= curr->isAtomic; if (!ignoreImplicitTraps) { implicitTrap = true; } } void visitAtomicRMW(AtomicRMW* curr) { readsMemory = true; writesMemory = true; isAtomic = true; if (!ignoreImplicitTraps) { implicitTrap = true; } } void visitAtomicCmpxchg(AtomicCmpxchg* curr) { readsMemory = true; writesMemory = true; isAtomic = true; if (!ignoreImplicitTraps) { implicitTrap = true; } } void visitAtomicWait(AtomicWait* curr) { readsMemory = true; // AtomicWait doesn't strictly write memory, but it does modify the waiters // list associated with the specified address, which we can think of as a // write. writesMemory = true; isAtomic = true; if (!ignoreImplicitTraps) { implicitTrap = true; } } void visitAtomicNotify(AtomicNotify* curr) { // AtomicNotify doesn't strictly write memory, but it does modify the // waiters list associated with the specified address, which we can think of // as a write. readsMemory = true; writesMemory = true; isAtomic = true; if (!ignoreImplicitTraps) { implicitTrap = true; } } void visitAtomicFence(AtomicFence* curr) { // AtomicFence should not be reordered with any memory operations, so we set // these to true. readsMemory = true; writesMemory = true; isAtomic = true; } void visitSIMDExtract(SIMDExtract* curr) {} void visitSIMDReplace(SIMDReplace* curr) {} void visitSIMDShuffle(SIMDShuffle* curr) {} void visitSIMDTernary(SIMDTernary* curr) {} void visitSIMDShift(SIMDShift* curr) {} void visitSIMDLoad(SIMDLoad* curr) { readsMemory = true; if (!ignoreImplicitTraps) { implicitTrap = true; } } void visitMemoryInit(MemoryInit* curr) { writesMemory = true; if (!ignoreImplicitTraps) { implicitTrap = true; } } void visitDataDrop(DataDrop* curr) { // prevent reordering with memory.init readsMemory = true; if (!ignoreImplicitTraps) { implicitTrap = true; } } void visitMemoryCopy(MemoryCopy* curr) { readsMemory = true; writesMemory = true; if (!ignoreImplicitTraps) { implicitTrap = true; } } void visitMemoryFill(MemoryFill* curr) { writesMemory = true; if (!ignoreImplicitTraps) { implicitTrap = true; } } void visitConst(Const* curr) {} void visitUnary(Unary* curr) { if (!ignoreImplicitTraps) { switch (curr->op) { case TruncSFloat32ToInt32: case TruncSFloat32ToInt64: case TruncUFloat32ToInt32: case TruncUFloat32ToInt64: case TruncSFloat64ToInt32: case TruncSFloat64ToInt64: case TruncUFloat64ToInt32: case TruncUFloat64ToInt64: { implicitTrap = true; break; } default: {} } } } void visitBinary(Binary* curr) { if (!ignoreImplicitTraps) { switch (curr->op) { case DivSInt32: case DivUInt32: case RemSInt32: case RemUInt32: case DivSInt64: case DivUInt64: case RemSInt64: case RemUInt64: { implicitTrap = true; break; } default: {} } } } void visitSelect(Select* curr) {} void visitDrop(Drop* curr) {} void visitReturn(Return* curr) { branches = true; } void visitHost(Host* curr) { calls = true; // memory.grow modifies the set of valid addresses, and thus can be modeled // as modifying memory writesMemory = true; // Atomics are also sequentially consistent with memory.grow. isAtomic = true; } void visitRefNull(RefNull* curr) {} void visitRefIsNull(RefIsNull* curr) {} void visitRefFunc(RefFunc* curr) {} void visitTry(Try* curr) {} void visitThrow(Throw* curr) { if (tryDepth == 0) { throws = true; } } void visitRethrow(Rethrow* curr) { if (tryDepth == 0) { throws = true; } } void visitBrOnExn(BrOnExn* curr) { breakNames.insert(curr->name); } void visitNop(Nop* curr) {} void visitUnreachable(Unreachable* curr) { branches = true; } void visitPush(Push* curr) { calls = true; } void visitPop(Pop* curr) { calls = true; } // Helpers static bool canReorder(const PassOptions& passOptions, FeatureSet features, Expression* a, Expression* b) { EffectAnalyzer aEffects(passOptions, features, a); EffectAnalyzer bEffects(passOptions, features, b); return !aEffects.invalidates(bEffects); } // C-API enum SideEffects : uint32_t { None = 0, Branches = 1 << 0, Calls = 1 << 1, ReadsLocal = 1 << 2, WritesLocal = 1 << 3, ReadsGlobal = 1 << 4, WritesGlobal = 1 << 5, ReadsMemory = 1 << 6, WritesMemory = 1 << 7, ImplicitTrap = 1 << 8, IsAtomic = 1 << 9, Throws = 1 << 10, Any = (1 << 11) - 1 }; uint32_t getSideEffects() const { uint32_t effects = 0; if (branches) { effects |= SideEffects::Branches; } if (calls) { effects |= SideEffects::Calls; } if (localsRead.size() > 0) { effects |= SideEffects::ReadsLocal; } if (localsWritten.size() > 0) { effects |= SideEffects::WritesLocal; } if (globalsRead.size() > 0) { effects |= SideEffects::ReadsGlobal; } if (globalsWritten.size() > 0) { effects |= SideEffects::WritesGlobal; } if (readsMemory) { effects |= SideEffects::ReadsMemory; } if (writesMemory) { effects |= SideEffects::WritesMemory; } if (implicitTrap) { effects |= SideEffects::ImplicitTrap; } if (isAtomic) { effects |= SideEffects::IsAtomic; } if (throws) { effects |= SideEffects::Throws; } return effects; } }; } // namespace wasm #endif // wasm_ir_effects_h binaryen-version_91/src/ir/equivalent_sets.h000066400000000000000000000051331362402614000214460ustar00rootroot00000000000000/* * Copyright 2018 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_ir_equivalent_sets_h #define wasm_ir_equivalent_sets_h #include namespace wasm { // // A map of each index to all those it is equivalent to, and some helpers. // struct EquivalentSets { // A set of indexes. This is ordered for deterministic iteration. typedef std::set Set; std::unordered_map> indexSets; // Clears the state completely, removing all equivalences. void clear() { indexSets.clear(); } // Resets an index, removing any equivalences between it and others. void reset(Index index) { auto iter = indexSets.find(index); if (iter != indexSets.end()) { auto& set = iter->second; assert(!set->empty()); // can't be empty - we are equal to ourselves! if (set->size() > 1) { // We are not the last item, fix things up set->erase(index); } indexSets.erase(iter); } } // Adds a new equivalence between two indexes. // `justReset` is an index that was just reset, and has no // equivalences. `other` may have existing equivalences. void add(Index justReset, Index other) { auto iter = indexSets.find(other); if (iter != indexSets.end()) { auto& set = iter->second; set->insert(justReset); indexSets[justReset] = set; } else { auto set = std::make_shared(); set->insert(justReset); set->insert(other); indexSets[justReset] = set; indexSets[other] = set; } } // Checks whether two indexes contain the same data. bool check(Index a, Index b) { if (a == b) { return true; } if (auto* set = getEquivalents(a)) { if (set->find(b) != set->end()) { return true; } } return false; } // Returns the equivalent set, or nullptr Set* getEquivalents(Index index) { auto iter = indexSets.find(index); if (iter != indexSets.end()) { return iter->second.get(); } return nullptr; } }; } // namespace wasm #endif // wasm_ir_equivalent_sets_h binaryen-version_91/src/ir/features.h000066400000000000000000000102331362402614000200460ustar00rootroot00000000000000/* * Copyright 2018 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_ir_features_h #define wasm_ir_features_h #include #include #include #include namespace wasm { namespace Features { inline FeatureSet get(UnaryOp op) { FeatureSet ret; switch (op) { case TruncSatSFloat32ToInt32: case TruncSatUFloat32ToInt32: case TruncSatSFloat64ToInt32: case TruncSatUFloat64ToInt32: case TruncSatSFloat32ToInt64: case TruncSatUFloat32ToInt64: case TruncSatSFloat64ToInt64: case TruncSatUFloat64ToInt64: { ret.setTruncSat(); break; } case SplatVecI8x16: case SplatVecI16x8: case SplatVecI32x4: case SplatVecI64x2: case SplatVecF32x4: case SplatVecF64x2: case NotVec128: case NegVecI8x16: case AnyTrueVecI8x16: case AllTrueVecI8x16: case NegVecI16x8: case AnyTrueVecI16x8: case AllTrueVecI16x8: case NegVecI32x4: case AnyTrueVecI32x4: case AllTrueVecI32x4: case NegVecI64x2: case AnyTrueVecI64x2: case AllTrueVecI64x2: case AbsVecF32x4: case NegVecF32x4: case SqrtVecF32x4: case AbsVecF64x2: case NegVecF64x2: case SqrtVecF64x2: case TruncSatSVecF32x4ToVecI32x4: case TruncSatUVecF32x4ToVecI32x4: case TruncSatSVecF64x2ToVecI64x2: case TruncSatUVecF64x2ToVecI64x2: case ConvertSVecI32x4ToVecF32x4: case ConvertUVecI32x4ToVecF32x4: case ConvertSVecI64x2ToVecF64x2: case ConvertUVecI64x2ToVecF64x2: { ret.setSIMD(); break; } case ExtendS8Int32: case ExtendS16Int32: case ExtendS8Int64: case ExtendS16Int64: case ExtendS32Int64: { ret.setSignExt(); break; } default: {} } return ret; } inline FeatureSet get(BinaryOp op) { FeatureSet ret; switch (op) { case EqVecI8x16: case NeVecI8x16: case LtSVecI8x16: case LtUVecI8x16: case GtSVecI8x16: case GtUVecI8x16: case LeSVecI8x16: case LeUVecI8x16: case GeSVecI8x16: case GeUVecI8x16: case EqVecI16x8: case NeVecI16x8: case LtSVecI16x8: case LtUVecI16x8: case GtSVecI16x8: case GtUVecI16x8: case LeSVecI16x8: case LeUVecI16x8: case GeSVecI16x8: case GeUVecI16x8: case EqVecI32x4: case NeVecI32x4: case LtSVecI32x4: case LtUVecI32x4: case GtSVecI32x4: case GtUVecI32x4: case LeSVecI32x4: case LeUVecI32x4: case GeSVecI32x4: case GeUVecI32x4: case EqVecF32x4: case NeVecF32x4: case LtVecF32x4: case GtVecF32x4: case LeVecF32x4: case GeVecF32x4: case EqVecF64x2: case NeVecF64x2: case LtVecF64x2: case GtVecF64x2: case LeVecF64x2: case GeVecF64x2: case AndVec128: case OrVec128: case XorVec128: case AddVecI8x16: case AddSatSVecI8x16: case AddSatUVecI8x16: case SubVecI8x16: case SubSatSVecI8x16: case SubSatUVecI8x16: case MulVecI8x16: case AddVecI16x8: case AddSatSVecI16x8: case AddSatUVecI16x8: case SubVecI16x8: case SubSatSVecI16x8: case SubSatUVecI16x8: case MulVecI16x8: case AddVecI32x4: case SubVecI32x4: case MulVecI32x4: case AddVecI64x2: case SubVecI64x2: case AddVecF32x4: case SubVecF32x4: case MulVecF32x4: case DivVecF32x4: case MinVecF32x4: case MaxVecF32x4: case AddVecF64x2: case SubVecF64x2: case MulVecF64x2: case DivVecF64x2: case MinVecF64x2: case MaxVecF64x2: { ret.setSIMD(); break; } default: {} } return ret; } } // namespace Features } // namespace wasm #endif // wasm_ir_features_h binaryen-version_91/src/ir/find_all.h000066400000000000000000000036331362402614000200060ustar00rootroot00000000000000/* * Copyright 2017 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_ir_find_all_h #define wasm_ir_find_all_h #include namespace wasm { // Find all instances of a certain node type template struct FindAll { std::vector list; FindAll(Expression* ast) { struct Finder : public PostWalker> { std::vector* list; void visitExpression(Expression* curr) { if (curr->is()) { (*list).push_back(curr->cast()); } } }; Finder finder; finder.list = &list; finder.walk(ast); } }; // Find all pointers to instances of a certain node type struct PointerFinder : public PostWalker> { Expression::Id id; std::vector* list; void visitExpression(Expression* curr) { if (curr->_id == id) { (*list).push_back(getCurrentPointer()); } } }; template struct FindAllPointers { std::vector list; // Note that a pointer may be to the function->body itself, so we must // take \ast by reference. FindAllPointers(Expression*& ast) { PointerFinder finder; finder.id = (Expression::Id)T::SpecificId; finder.list = &list; finder.walk(ast); } }; } // namespace wasm #endif // wasm_ir_find_all_h binaryen-version_91/src/ir/flat.h000066400000000000000000000072331362402614000171640ustar00rootroot00000000000000/* * Copyright 2019 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // // Flattens code, removing nesting.e.g. an if return value would be // converted to a local // // (i32.add // (if (..condition..) // (..if true..) // (..if false..) // ) // (i32.const 1) // ) // => // (if (..condition..) // (local.set $temp // (..if true..) // ) // (local.set $temp // (..if false..) // ) // ) // (i32.add // (local.get $temp) // (i32.const 1) // ) // // Formally, this pass flattens in the precise sense of // making the AST have these properties: // // 1. Aside from a local.set, the operands of an instruction must be a // local.get, a const, or an unreachable. Anything else is written // to a local earlier. // 2. Disallow block, loop, and if return values, and do not allow the // function body to have a concrete type, i.e., do not use // control flow to pass around values. // 3. Disallow local.tee, setting a local is always done in a local.set // on a non-nested-expression location. // #ifndef wasm_ir_flat_h #define wasm_ir_flat_h #include "ir/iteration.h" #include "ir/properties.h" #include "pass.h" #include "wasm-traversal.h" namespace wasm { namespace Flat { inline bool isControlFlowStructure(Expression* curr) { return curr->is() || curr->is() || curr->is() || curr->is(); } inline void verifyFlatness(Function* func) { struct VerifyFlatness : public PostWalker> { void visitExpression(Expression* curr) { if (isControlFlowStructure(curr)) { verify(!curr->type.isConcrete(), "control flow structures must not flow values"); } else if (curr->is()) { verify(!curr->type.isConcrete(), "tees are not allowed, only sets"); } else { for (auto* child : ChildIterator(curr)) { verify(Properties::isConstantExpression(child) || child->is() || child->is(), "instructions must only have constant expressions, local.get, " "or unreachable as children"); } } } void verify(bool condition, const char* message) { if (!condition) { Fatal() << "IR must be flat: run --flatten beforehand (" << message << ", in " << getFunction()->name << ')'; } } }; VerifyFlatness verifier; verifier.walkFunction(func); verifier.setFunction(func); verifier.verify(!func->body->type.isConcrete(), "function bodies must not flow values"); } inline void verifyFlatness(Module* module) { struct VerifyFlatness : public WalkerPass< PostWalker>> { bool isFunctionParallel() override { return true; } VerifyFlatness* create() override { return new VerifyFlatness(); } void doVisitFunction(Function* func) { verifyFlatness(func); } }; PassRunner runner(module); VerifyFlatness().run(&runner, module); } } // namespace Flat } // namespace wasm #endif // wasm_ir_flat_h binaryen-version_91/src/ir/function-utils.h000066400000000000000000000030051362402614000212120ustar00rootroot00000000000000/* * Copyright 2018 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_ir_function_h #define wasm_ir_function_h #include "ir/utils.h" #include "wasm.h" namespace wasm { namespace FunctionUtils { // Checks if two functions are equal in all functional aspects, // everything but their name (which can't be the same, in the same // module!) - same params, vars, body, result, etc. inline bool equal(Function* left, Function* right) { if (left->sig != right->sig) { return false; } if (left->getNumVars() != right->getNumVars()) { return false; } for (Index i = left->sig.params.size(); i < left->getNumLocals(); i++) { if (left->getLocalType(i) != right->getLocalType(i)) { return false; } } if (!left->imported() && !right->imported()) { return ExpressionAnalyzer::equal(left->body, right->body); } return left->imported() && right->imported(); } } // namespace FunctionUtils } // namespace wasm #endif // wasm_ir_function_h binaryen-version_91/src/ir/global-utils.h000066400000000000000000000033541362402614000206340ustar00rootroot00000000000000/* * Copyright 2017 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_ir_global_h #define wasm_ir_global_h #include #include #include "ir/module-utils.h" #include "literal.h" #include "wasm.h" namespace wasm { namespace GlobalUtils { // find a global initialized to the value of an import, or null if no such // global inline Global* getGlobalInitializedToImport(Module& wasm, Name module, Name base) { // find the import Name imported; ModuleUtils::iterImportedGlobals(wasm, [&](Global* import) { if (import->module == module && import->base == base) { imported = import->name; } }); if (imported.isNull()) { return nullptr; } // find a global inited to it Global* ret = nullptr; ModuleUtils::iterDefinedGlobals(wasm, [&](Global* defined) { if (auto* init = defined->init->dynCast()) { if (init->name == imported) { ret = defined; } } }); return ret; } inline bool canInitializeGlobal(const Expression* curr) { return curr->is() || curr->is() || curr->is() || curr->is(); } } // namespace GlobalUtils } // namespace wasm #endif // wasm_ir_global_h binaryen-version_91/src/ir/hashed.h000066400000000000000000000043121362402614000174650ustar00rootroot00000000000000/* * Copyright 2017 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef _wasm_ir_hashed_h #define _wasm_ir_hashed_h #include "ir/utils.h" #include "support/hash.h" #include "wasm.h" namespace wasm { // An expression with a cached hash value struct HashedExpression { Expression* expr; HashType hash; HashedExpression(Expression* expr) : expr(expr) { if (expr) { hash = ExpressionAnalyzer::hash(expr); } } HashedExpression(const HashedExpression& other) : expr(other.expr), hash(other.hash) {} }; // A pass that hashes all functions struct FunctionHasher : public WalkerPass> { bool isFunctionParallel() override { return true; } struct Map : public std::map {}; FunctionHasher(Map* output) : output(output) {} FunctionHasher* create() override { return new FunctionHasher(output); } static Map createMap(Module* module) { Map hashes; for (auto& func : module->functions) { // ensure an entry for each function - we must not modify the map shape in // parallel, just the values hashes[func.get()] = 0; } return hashes; } void doWalkFunction(Function* func) { output->at(func) = hashFunction(func); } static HashType hashFunction(Function* func) { HashType ret = 0; ret = rehash(ret, (HashType)func->sig.params.getID()); ret = rehash(ret, (HashType)func->sig.results.getID()); for (auto type : func->vars) { ret = rehash(ret, (HashType)type.getSingle()); } ret = rehash(ret, (HashType)ExpressionAnalyzer::hash(func->body)); return ret; } private: Map* output; }; } // namespace wasm #endif // _wasm_ir_hashed_h binaryen-version_91/src/ir/import-utils.h000066400000000000000000000054531362402614000207100ustar00rootroot00000000000000/* * Copyright 2017 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_ir_import_h #define wasm_ir_import_h #include "literal.h" #include "wasm.h" namespace wasm { // Collects info on imports, into a form convenient for summarizing // and searching. struct ImportInfo { Module& wasm; std::vector importedGlobals; std::vector importedFunctions; std::vector importedEvents; ImportInfo(Module& wasm) : wasm(wasm) { for (auto& import : wasm.globals) { if (import->imported()) { importedGlobals.push_back(import.get()); } } for (auto& import : wasm.functions) { if (import->imported()) { importedFunctions.push_back(import.get()); } } for (auto& import : wasm.events) { if (import->imported()) { importedEvents.push_back(import.get()); } } } Global* getImportedGlobal(Name module, Name base) { for (auto* import : importedGlobals) { if (import->module == module && import->base == base) { return import; } } return nullptr; } Function* getImportedFunction(Name module, Name base) { for (auto* import : importedFunctions) { if (import->module == module && import->base == base) { return import; } } return nullptr; } Event* getImportedEvent(Name module, Name base) { for (auto* import : importedEvents) { if (import->module == module && import->base == base) { return import; } } return nullptr; } Index getNumImportedGlobals() { return importedGlobals.size(); } Index getNumImportedFunctions() { return importedFunctions.size(); } Index getNumImportedEvents() { return importedEvents.size(); } Index getNumImports() { return getNumImportedGlobals() + getNumImportedFunctions() + getNumImportedEvents() + (wasm.memory.imported() ? 1 : 0) + (wasm.table.imported() ? 1 : 0); } Index getNumDefinedGlobals() { return wasm.globals.size() - getNumImportedGlobals(); } Index getNumDefinedFunctions() { return wasm.functions.size() - getNumImportedFunctions(); } Index getNumDefinedEvents() { return wasm.events.size() - getNumImportedEvents(); } }; } // namespace wasm #endif // wasm_ir_import_h binaryen-version_91/src/ir/iteration.h000066400000000000000000000050741362402614000202350ustar00rootroot00000000000000/* * Copyright 2018 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_ir_iteration_h #define wasm_ir_iteration_h #include "wasm-traversal.h" #include "wasm.h" namespace wasm { // // Allows iteration over the children of the expression, in order of execution // where relevant. // // * This skips missing children, e.g. if an if has no else, it is represented // as having 2 children (and not 3 with the last a nullptr). // // In general, it is preferable not to use this class and to directly access // the children (using e.g. iff->ifTrue etc.), as that is faster. However, in // cases where speed does not matter, this can be convenient. // class ChildIterator { struct Iterator { const ChildIterator& parent; Index index; Iterator(const ChildIterator& parent, Index index) : parent(parent), index(index) {} bool operator!=(const Iterator& other) const { return index != other.index || &parent != &(other.parent); } void operator++() { index++; } Expression* operator*() { return parent.children[index]; } }; public: std::vector children; ChildIterator(Expression* parent) { struct Traverser : public PostWalker { Expression* parent; std::vector* children; // We need to scan subchildren exactly once - just the parent. bool scanned = false; static void scan(Traverser* self, Expression** currp) { if (!self->scanned) { self->scanned = true; PostWalker>::scan( self, currp); } else { // This is one of the children. Do not scan further, just note it. self->children->push_back(*currp); } } } traverser; traverser.parent = parent; traverser.children = &children; traverser.walk(parent); } Iterator begin() const { return Iterator(*this, 0); } Iterator end() const { return Iterator(*this, children.size()); } }; } // namespace wasm #endif // wasm_ir_iteration_h binaryen-version_91/src/ir/label-utils.h000066400000000000000000000026731362402614000204560ustar00rootroot00000000000000/* * Copyright 2017 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_ir_label_h #define wasm_ir_label_h #include "wasm-traversal.h" #include "wasm.h" namespace wasm { namespace LabelUtils { // Handles branch/loop labels in a function; makes it easy to add new // ones without duplicates class LabelManager : public PostWalker { public: LabelManager(Function* func) { walkFunction(func); } Name getUnique(std::string prefix) { while (1) { auto curr = Name(prefix + std::to_string(counter++)); if (labels.find(curr) == labels.end()) { labels.insert(curr); return curr; } } } void visitBlock(Block* curr) { labels.insert(curr->name); } void visitLoop(Loop* curr) { labels.insert(curr->name); } private: std::set labels; size_t counter = 0; }; } // namespace LabelUtils } // namespace wasm #endif // wasm_ir_label_h binaryen-version_91/src/ir/literal-utils.h000066400000000000000000000030031362402614000210170ustar00rootroot00000000000000/* * Copyright 2017 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_ir_literal_utils_h #define wasm_ir_literal_utils_h #include "wasm-builder.h" #include "wasm.h" namespace wasm { namespace LiteralUtils { inline Expression* makeFromInt32(int32_t x, Type type, Module& wasm) { auto* ret = wasm.allocator.alloc(); ret->value = Literal::makeFromInt32(x, type); ret->type = type; return ret; } inline Expression* makeZero(Type type, Module& wasm) { // TODO: Switch to using v128.const once V8 supports it // (https://bugs.chromium.org/p/v8/issues/detail?id=8460) if (type == Type::v128) { Builder builder(wasm); return builder.makeUnary(SplatVecI32x4, builder.makeConst(Literal(int32_t(0)))); } if (type.isRef()) { Builder builder(wasm); return builder.makeRefNull(); } return makeFromInt32(0, type, wasm); } } // namespace LiteralUtils } // namespace wasm #endif // wasm_ir_literal_utils_h binaryen-version_91/src/ir/load-utils.h000066400000000000000000000024301362402614000203050ustar00rootroot00000000000000/* * Copyright 2017 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_ir_load_h #define wasm_ir_load_h #include "wasm.h" namespace wasm { namespace LoadUtils { // checks if the sign of a load matters, which is when an integer // load is of fewer bytes than the size of the type (so we must // fill in bits either signed or unsigned wise) inline bool isSignRelevant(Load* load) { auto type = load->type; if (load->type == Type::unreachable) { return false; } return !type.isFloat() && load->bytes < type.getByteSize(); } // check if a load can be signed (which some opts want to do) inline bool canBeSigned(Load* load) { return !load->isAtomic; } } // namespace LoadUtils } // namespace wasm #endif // wasm_ir_load_h binaryen-version_91/src/ir/local-graph.h000066400000000000000000000062061362402614000204260ustar00rootroot00000000000000/* * Copyright 2017 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_ir_local_graph_h #define wasm_ir_local_graph_h #include "wasm.h" namespace wasm { // // Finds the connections between local.gets and local.sets, creating // a graph of those ties. This is useful for "ssa-style" optimization, // in which you want to know exactly which sets are relevant for a // a get, so it is as if each get has just one set, logically speaking // (see the SSA pass for actually creating new local indexes based // on this). // struct LocalGraph { // main API // the constructor computes getSetses, the sets affecting each get LocalGraph(Function* func); // the local.sets relevant for an index or a get. typedef std::set Sets; typedef std::map GetSetses; typedef std::map Locations; // externally useful information GetSetses getSetses; // the sets affecting each get. a nullptr set means the // initial value (0 for a var, the received value for a // param) Locations locations; // where each get and set is (for easy replacing) // Optional: compute the influence graphs between sets and gets // (useful for algorithms that propagate changes). void computeInfluences(); // for each get, the sets whose values are influenced by that get std::unordered_map> getInfluences; // for each set, the gets whose values are influenced by that set std::unordered_map> setInfluences; // Optional: Compute the local indexes that are SSA, in the sense of // * a single set for all the gets for that local index // * the set dominates all the gets (logically implied by the former // property) // * no other set (aside from the zero-init) // The third property is not exactly standard SSA, but is useful since we are // not in SSA form in our IR. To see why it matters, consider these: // // x = 0 // zero init // [..] // x = 10 // y = x + 20 // x = 30 // !!! // f(y) // // The !!! line violates that property - it is another set for x, and it may // interfere say with replacing f(y) with f(x + 20). Instead, if we know the // only other possible set for x is the zero init, then things like the !!! // line cannot exist, and it is valid to replace f(y) with f(x + 20). (This // could be simpler, but in wasm the zero init always exists.) void computeSSAIndexes(); bool isSSA(Index x); private: std::set SSAIndexes; }; } // namespace wasm #endif // wasm_ir_local_graph_h binaryen-version_91/src/ir/local-utils.h000066400000000000000000000064361362402614000204720ustar00rootroot00000000000000/* * Copyright 2019 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_ir_local_utils_h #define wasm_ir_local_utils_h #include #include namespace wasm { struct LocalGetCounter : public PostWalker { std::vector num; LocalGetCounter() = default; LocalGetCounter(Function* func) { analyze(func, func->body); } LocalGetCounter(Function* func, Expression* ast) { analyze(func, ast); } void analyze(Function* func) { analyze(func, func->body); } void analyze(Function* func, Expression* ast) { num.resize(func->getNumLocals()); std::fill(num.begin(), num.end(), 0); walk(ast); } void visitLocalGet(LocalGet* curr) { num[curr->index]++; } }; // Removes trivially unneeded sets: sets for whom there is no possible get, and // sets of the same value immediately. struct UnneededSetRemover : public PostWalker { PassOptions& passOptions; LocalGetCounter* localGetCounter = nullptr; FeatureSet features; UnneededSetRemover(Function* func, PassOptions& passOptions, FeatureSet features) : passOptions(passOptions), features(features) { LocalGetCounter counter(func); UnneededSetRemover inner(counter, func, passOptions, features); removed = inner.removed; } UnneededSetRemover(LocalGetCounter& localGetCounter, Function* func, PassOptions& passOptions, FeatureSet features) : passOptions(passOptions), localGetCounter(&localGetCounter), features(features) { walk(func->body); } bool removed = false; void visitLocalSet(LocalSet* curr) { // If no possible uses, remove. if (localGetCounter->num[curr->index] == 0) { remove(curr); } // If setting the same value as we already have, remove. auto* value = curr->value; while (true) { if (auto* set = value->dynCast()) { if (set->index == curr->index) { remove(curr); } else { // Handle tee chains. value = set->value; continue; } } else if (auto* get = value->dynCast()) { if (get->index == curr->index) { remove(curr); } } break; } } void remove(LocalSet* set) { auto* value = set->value; if (set->isTee()) { replaceCurrent(value); } else if (EffectAnalyzer(passOptions, features, set->value) .hasSideEffects()) { Drop* drop = ExpressionManipulator::convert(set); drop->value = value; drop->finalize(); } else { ExpressionManipulator::nop(set); } removed = true; } }; } // namespace wasm #endif // wasm_ir_local_utils_h binaryen-version_91/src/ir/localize.h000066400000000000000000000024601362402614000200350ustar00rootroot00000000000000/* * Copyright 2016 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_ir_localizer_h #define wasm_ir_localizer_h #include namespace wasm { // Make an expression available in a local. If already in one, just // use that local, otherwise use a new local struct Localizer { Index index; Expression* expr; Localizer(Expression* input, Function* func, Module* wasm) { expr = input; if (auto* get = expr->dynCast()) { index = get->index; } else if (auto* set = expr->dynCast()) { index = set->index; } else { index = Builder::addVar(func, expr->type); expr = Builder(*wasm).makeLocalTee(index, expr, expr->type); } } }; } // namespace wasm #endif // wasm_ir_localizer_h binaryen-version_91/src/ir/manipulation.h000066400000000000000000000051761362402614000207420ustar00rootroot00000000000000/* * Copyright 2017 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_ir_manipulation_h #define wasm_ir_manipulation_h #include "wasm.h" namespace wasm { namespace ExpressionManipulator { // Re-use a node's memory. This helps avoid allocation when optimizing. template inline OutputType* convert(InputType* input) { static_assert(sizeof(OutputType) <= sizeof(InputType), "Can only convert to a smaller size Expression node"); input->~InputType(); // arena-allocaed, so no destructor, but avoid UB. OutputType* output = (OutputType*)(input); new (output) OutputType; return output; } // Convenience methods for certain instructions, which are common conversions template inline Nop* nop(InputType* target) { auto* ret = convert(target); ret->finalize(); return ret; } template inline RefNull* refNull(InputType* target) { auto* ret = convert(target); ret->finalize(); return ret; } template inline Unreachable* unreachable(InputType* target) { auto* ret = convert(target); ret->finalize(); return ret; } // Convert a node that allocates template inline OutputType* convert(InputType* input, MixedArena& allocator) { assert(sizeof(OutputType) <= sizeof(InputType)); input->~InputType(); // arena-allocaed, so no destructor, but avoid UB. OutputType* output = (OutputType*)(input); new (output) OutputType(allocator); return output; } using CustomCopier = std::function; Expression* flexibleCopy(Expression* original, Module& wasm, CustomCopier custom); inline Expression* copy(Expression* original, Module& wasm) { auto copy = [](Expression* curr) { return nullptr; }; return flexibleCopy(original, wasm, copy); } // Splice an item into the middle of a block's list void spliceIntoBlock(Block* block, Index index, Expression* add); } // namespace ExpressionManipulator } // namespace wasm #endif // wams_ir_manipulation_h binaryen-version_91/src/ir/memory-utils.h000066400000000000000000000142231362402614000207010ustar00rootroot00000000000000/* * Copyright 2017 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_ir_memory_h #define wasm_ir_memory_h #include #include #include "literal.h" #include "wasm-binary.h" #include "wasm-builder.h" #include "wasm.h" namespace wasm { namespace MemoryUtils { // Flattens memory into a single data segment, or no segment. If there is // a segment, it starts at 0. // If ensuredSegmentSize is provided, then a segment is always emitted, // and of at least that size. // Returns true if successful (e.g. relocatable segments cannot be flattened). inline bool flatten(Memory& memory, Index ensuredSegmentSize = 0, Module* module = nullptr) { if (memory.segments.size() == 0) { if (ensuredSegmentSize > 0) { assert(module); // must provide a module if ensuring a size. Builder builder(*module); memory.segments.emplace_back(builder.makeConst(Literal(int32_t(0)))); memory.segments[0].data.resize(ensuredSegmentSize); } return true; } std::vector data; data.resize(ensuredSegmentSize); for (auto& segment : memory.segments) { if (segment.isPassive) { return false; } auto* offset = segment.offset->dynCast(); if (!offset) { return false; } } for (auto& segment : memory.segments) { auto* offset = segment.offset->dynCast(); Index start = offset->value.getInteger(); Index end = start + segment.data.size(); if (end > data.size()) { data.resize(end); } std::copy(segment.data.begin(), segment.data.end(), data.begin() + start); } memory.segments.resize(1); memory.segments[0].offset->cast()->value = Literal(int32_t(0)); memory.segments[0].data.swap(data); return true; } // Ensures that the memory exists (of minimal size). inline void ensureExists(Memory& memory) { if (!memory.exists) { memory.exists = true; memory.initial = memory.max = 1; } } // Try to merge segments until they fit into web limitations. // Return true if successful. inline bool ensureLimitedSegments(Module& module) { Memory& memory = module.memory; if (memory.segments.size() <= WebLimitations::MaxDataSegments) { return true; } // Conservatively refuse to change segments if there might be memory.init // and data.drop instructions. if (module.features.hasBulkMemory()) { return false; } auto isEmpty = [](Memory::Segment& segment) { return segment.data.size() == 0; }; auto isConstantOffset = [](Memory::Segment& segment) { return segment.offset && segment.offset->is(); }; Index numConstant = 0, numDynamic = 0; bool hasPassiveSegments = false; for (auto& segment : memory.segments) { if (!isEmpty(segment)) { if (isConstantOffset(segment)) { numConstant++; } else { numDynamic++; } } hasPassiveSegments |= segment.isPassive; } if (hasPassiveSegments) { return false; } // check if we have too many dynamic data segments, which we can do nothing // about if (numDynamic + 1 >= WebLimitations::MaxDataSegments) { return false; } // we'll merge constant segments if we must if (numConstant + numDynamic >= WebLimitations::MaxDataSegments) { numConstant = WebLimitations::MaxDataSegments - numDynamic - 1; auto num = numConstant + numDynamic; WASM_UNUSED(num); assert(num == WebLimitations::MaxDataSegments - 1); } std::vector mergedSegments; mergedSegments.reserve(WebLimitations::MaxDataSegments); // drop empty segments and pass through dynamic-offset segments for (auto& segment : memory.segments) { if (isEmpty(segment)) { continue; } if (isConstantOffset(segment)) { continue; } mergedSegments.push_back(segment); } // from here on, we concern ourselves with non-empty constant-offset // segments, the ones which we may need to merge auto isRelevant = [&](Memory::Segment& segment) { return !isEmpty(segment) && isConstantOffset(segment); }; for (Index i = 0; i < memory.segments.size(); i++) { auto& segment = memory.segments[i]; if (!isRelevant(segment)) { continue; } if (mergedSegments.size() + 2 < WebLimitations::MaxDataSegments) { mergedSegments.push_back(segment); continue; } // we can emit only one more segment! merge everything into one // start the combined segment at the bottom of them all auto start = segment.offset->cast()->value.getInteger(); for (Index j = i + 1; j < memory.segments.size(); j++) { auto& segment = memory.segments[j]; if (!isRelevant(segment)) { continue; } auto offset = segment.offset->cast()->value.getInteger(); start = std::min(start, offset); } // create the segment and add in all the data auto* c = module.allocator.alloc(); c->value = Literal(int32_t(start)); c->type = Type::i32; Memory::Segment combined(c); for (Index j = i; j < memory.segments.size(); j++) { auto& segment = memory.segments[j]; if (!isRelevant(segment)) { continue; } auto offset = segment.offset->cast()->value.getInteger(); auto needed = offset + segment.data.size() - start; if (combined.data.size() < needed) { combined.data.resize(needed); } std::copy(segment.data.begin(), segment.data.end(), combined.data.begin() + (offset - start)); } mergedSegments.push_back(combined); break; } memory.segments.swap(mergedSegments); return true; } } // namespace MemoryUtils } // namespace wasm #endif // wasm_ir_memory_h binaryen-version_91/src/ir/module-utils.h000066400000000000000000000336301362402614000206610ustar00rootroot00000000000000/* * Copyright 2017 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_ir_module_h #define wasm_ir_module_h #include "ir/find_all.h" #include "ir/manipulation.h" #include "pass.h" #include "support/unique_deferring_queue.h" #include "wasm.h" namespace wasm { namespace ModuleUtils { // Computes the indexes in a wasm binary, i.e., with function imports // and function implementations sharing a single index space, etc., // and with the imports first (the Module's functions and globals // arrays are not assumed to be in a particular order, so we can't // just use them directly). struct BinaryIndexes { std::unordered_map functionIndexes; std::unordered_map globalIndexes; std::unordered_map eventIndexes; BinaryIndexes(Module& wasm) { auto addIndexes = [&](auto& source, auto& indexes) { auto addIndex = [&](auto* curr) { auto index = indexes.size(); indexes[curr->name] = index; }; for (auto& curr : source) { if (curr->imported()) { addIndex(curr.get()); } } for (auto& curr : source) { if (!curr->imported()) { addIndex(curr.get()); } } }; addIndexes(wasm.functions, functionIndexes); addIndexes(wasm.globals, globalIndexes); addIndexes(wasm.events, eventIndexes); } }; inline Function* copyFunction(Function* func, Module& out) { auto* ret = new Function(); ret->name = func->name; ret->sig = func->sig; ret->vars = func->vars; ret->localNames = func->localNames; ret->localIndices = func->localIndices; ret->debugLocations = func->debugLocations; ret->body = ExpressionManipulator::copy(func->body, out); ret->module = func->module; ret->base = func->base; // TODO: copy Stack IR assert(!func->stackIR); out.addFunction(ret); return ret; } inline Global* copyGlobal(Global* global, Module& out) { auto* ret = new Global(); ret->name = global->name; ret->type = global->type; ret->mutable_ = global->mutable_; ret->module = global->module; ret->base = global->base; if (global->imported()) { ret->init = nullptr; } else { ret->init = ExpressionManipulator::copy(global->init, out); } out.addGlobal(ret); return ret; } inline Event* copyEvent(Event* event, Module& out) { auto* ret = new Event(); ret->name = event->name; ret->attribute = event->attribute; ret->sig = event->sig; out.addEvent(ret); return ret; } inline void copyModule(const Module& in, Module& out) { // we use names throughout, not raw pointers, so simple copying is fine // for everything *but* expressions for (auto& curr : in.exports) { out.addExport(new Export(*curr)); } for (auto& curr : in.functions) { copyFunction(curr.get(), out); } for (auto& curr : in.globals) { copyGlobal(curr.get(), out); } for (auto& curr : in.events) { copyEvent(curr.get(), out); } out.table = in.table; for (auto& segment : out.table.segments) { segment.offset = ExpressionManipulator::copy(segment.offset, out); } out.memory = in.memory; for (auto& segment : out.memory.segments) { segment.offset = ExpressionManipulator::copy(segment.offset, out); } out.start = in.start; out.userSections = in.userSections; out.debugInfoFileNames = in.debugInfoFileNames; } inline void clearModule(Module& wasm) { wasm.exports.clear(); wasm.functions.clear(); wasm.globals.clear(); wasm.events.clear(); wasm.table.clear(); wasm.memory.clear(); wasm.start = Name(); wasm.userSections.clear(); wasm.debugInfoFileNames.clear(); wasm.updateMaps(); wasm.allocator.clear(); } // Renaming // Rename functions along with all their uses. // Note that for this to work the functions themselves don't necessarily need // to exist. For example, it is possible to remove a given function and then // call this redirect all of its uses. template inline void renameFunctions(Module& wasm, T& map) { // Update the function itself. for (auto& pair : map) { if (Function* F = wasm.getFunctionOrNull(pair.first)) { assert(!wasm.getFunctionOrNull(pair.second)); F->name = pair.second; } } wasm.updateMaps(); // Update other global things. auto maybeUpdate = [&](Name& name) { auto iter = map.find(name); if (iter != map.end()) { name = iter->second; } }; maybeUpdate(wasm.start); for (auto& segment : wasm.table.segments) { for (auto& name : segment.data) { maybeUpdate(name); } } for (auto& exp : wasm.exports) { if (exp->kind == ExternalKind::Function) { maybeUpdate(exp->value); } } // Update call instructions. for (auto& func : wasm.functions) { // TODO: parallelize if (!func->imported()) { FindAll calls(func->body); for (auto* call : calls.list) { maybeUpdate(call->target); } } } } inline void renameFunction(Module& wasm, Name oldName, Name newName) { std::map map; map[oldName] = newName; renameFunctions(wasm, map); } // Convenient iteration over imported/non-imported module elements template inline void iterImportedMemories(Module& wasm, T visitor) { if (wasm.memory.exists && wasm.memory.imported()) { visitor(&wasm.memory); } } template inline void iterDefinedMemories(Module& wasm, T visitor) { if (wasm.memory.exists && !wasm.memory.imported()) { visitor(&wasm.memory); } } template inline void iterImportedTables(Module& wasm, T visitor) { if (wasm.table.exists && wasm.table.imported()) { visitor(&wasm.table); } } template inline void iterDefinedTables(Module& wasm, T visitor) { if (wasm.table.exists && !wasm.table.imported()) { visitor(&wasm.table); } } template inline void iterImportedGlobals(Module& wasm, T visitor) { for (auto& import : wasm.globals) { if (import->imported()) { visitor(import.get()); } } } template inline void iterDefinedGlobals(Module& wasm, T visitor) { for (auto& import : wasm.globals) { if (!import->imported()) { visitor(import.get()); } } } template inline void iterImportedFunctions(Module& wasm, T visitor) { for (auto& import : wasm.functions) { if (import->imported()) { visitor(import.get()); } } } template inline void iterDefinedFunctions(Module& wasm, T visitor) { for (auto& import : wasm.functions) { if (!import->imported()) { visitor(import.get()); } } } template inline void iterImportedEvents(Module& wasm, T visitor) { for (auto& import : wasm.events) { if (import->imported()) { visitor(import.get()); } } } template inline void iterDefinedEvents(Module& wasm, T visitor) { for (auto& import : wasm.events) { if (!import->imported()) { visitor(import.get()); } } } // Helper class for performing an operation on all the functions in the module, // in parallel, with an Info object for each one that can contain results of // some computation that the operation performs. // The operation performend should not modify the wasm module in any way. // TODO: enforce this template struct ParallelFunctionAnalysis { Module& wasm; typedef std::map Map; Map map; typedef std::function Func; ParallelFunctionAnalysis(Module& wasm, Func work) : wasm(wasm) { // Fill in map, as we operate on it in parallel (each function to its own // entry). for (auto& func : wasm.functions) { map[func.get()]; } // Run on the imports first. TODO: parallelize this too for (auto& func : wasm.functions) { if (func->imported()) { work(func.get(), map[func.get()]); } } struct Mapper : public WalkerPass> { bool isFunctionParallel() override { return true; } bool modifiesBinaryenIR() override { return false; } Mapper(Module& module, Map& map, Func work) : module(module), map(map), work(work) {} Mapper* create() override { return new Mapper(module, map, work); } void doWalkFunction(Function* curr) { assert(map.count(curr)); work(curr, map[curr]); } private: Module& module; Map& map; Func work; }; PassRunner runner(&wasm); Mapper(wasm, map, work).run(&runner, &wasm); } }; // Helper class for analyzing the call graph. // // Provides hooks for running some initial calculation on each function (which // is done in parallel), writing to a FunctionInfo structure for each function. // Then you can call propagateBack() to propagate a property of interest to the // calling functions, transitively. // // For example, if some functions are known to call an import "foo", then you // can use this to find which functions call something that might eventually // reach foo, by initially marking the direct callers as "calling foo" and // propagating that backwards. template struct CallGraphPropertyAnalysis { Module& wasm; // The basic information for each function about whom it calls and who is // called by it. struct FunctionInfo { std::set callsTo; std::set calledBy; bool hasIndirectCall = false; }; typedef std::map Map; Map map; typedef std::function Func; CallGraphPropertyAnalysis(Module& wasm, Func work) : wasm(wasm) { ParallelFunctionAnalysis analysis(wasm, [&](Function* func, T& info) { work(func, info); if (func->imported()) { return; } struct Mapper : public PostWalker { Mapper(Module* module, T& info, Func work) : module(module), info(info), work(work) {} void visitCall(Call* curr) { info.callsTo.insert(module->getFunction(curr->target)); } void visitCallIndirect(CallIndirect* curr) { info.hasIndirectCall = true; } private: Module* module; T& info; Func work; } mapper(&wasm, info, work); mapper.walk(func->body); }); map.swap(analysis.map); // Find what is called by what. for (auto& pair : map) { auto* func = pair.first; auto& info = pair.second; for (auto* target : info.callsTo) { map[target].calledBy.insert(func); } } } enum IndirectCalls { IgnoreIndirectCalls, IndirectCallsHaveProperty }; // Propagate a property from a function to those that call it. void propagateBack(std::function hasProperty, std::function canHaveProperty, std::function addProperty, IndirectCalls indirectCalls) { // The work queue contains items we just learned can change the state. UniqueDeferredQueue work; for (auto& func : wasm.functions) { if (hasProperty(map[func.get()]) || (indirectCalls == IndirectCallsHaveProperty && map[func.get()].hasIndirectCall)) { addProperty(map[func.get()]); work.push(func.get()); } } while (!work.empty()) { auto* func = work.pop(); for (auto* caller : map[func].calledBy) { // If we don't already have the property, and we are not forbidden // from getting it, then it propagates back to us now. if (!hasProperty(map[caller]) && canHaveProperty(map[caller])) { addProperty(map[caller]); work.push(caller); } } } } }; // Helper function for collecting the type signature used in a module // // Used when emitting or printing a module to give signatures canonical // indices. Signatures are sorted in order of decreasing frequency to minize the // size of their collective encoding. Both a vector mapping indices to // signatures and a map mapping signatures to indices are produced. inline void collectSignatures(Module& wasm, std::vector& signatures, std::unordered_map& sigIndices) { using Counts = std::unordered_map; // Collect the signature use counts for a single function auto updateCounts = [&](Function* func, Counts& counts) { if (func->imported()) { return; } struct TypeCounter : PostWalker { Counts& counts; TypeCounter(Counts& counts) : counts(counts) {} void visitCallIndirect(CallIndirect* curr) { counts[curr->sig]++; } }; TypeCounter(counts).walk(func->body); }; ModuleUtils::ParallelFunctionAnalysis analysis(wasm, updateCounts); // Collect all the counts. Counts counts; for (auto& curr : wasm.functions) { counts[curr->sig]++; } for (auto& curr : wasm.events) { counts[curr->sig]++; } for (auto& pair : analysis.map) { Counts& functionCounts = pair.second; for (auto& innerPair : functionCounts) { counts[innerPair.first] += innerPair.second; } } std::vector> sorted(counts.begin(), counts.end()); std::sort(sorted.begin(), sorted.end(), [&](auto a, auto b) { // order by frequency then simplicity if (a.second != b.second) { return a.second > b.second; } return a.first < b.first; }); for (Index i = 0; i < sorted.size(); ++i) { sigIndices[sorted[i].first] = i; signatures.push_back(sorted[i].first); } } } // namespace ModuleUtils } // namespace wasm #endif // wasm_ir_module_h binaryen-version_91/src/ir/names.h000066400000000000000000000026131362402614000173360ustar00rootroot00000000000000/* * Copyright 2017 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_ir_names_h #define wasm_ir_names_h #include "wasm.h" namespace wasm { namespace Names { // Add explicit names for function locals not yet named, and do not // modify existing names inline void ensureNames(Function* func) { std::unordered_set seen; for (auto& pair : func->localNames) { seen.insert(pair.second); } Index nameIndex = seen.size(); for (Index i = 0; i < func->getNumLocals(); i++) { if (!func->hasLocalName(i)) { while (1) { auto name = Name::fromInt(nameIndex++); if (seen.count(name) == 0) { func->localNames[i] = name; func->localIndices[name] = i; seen.insert(name); break; } } } } } } // namespace Names } // namespace wasm #endif // wasm_ir_names_h binaryen-version_91/src/ir/parents.h000066400000000000000000000021571362402614000177120ustar00rootroot00000000000000/* * Copyright 2019 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_ir_parents_h #define wasm_ir_parents_h namespace wasm { struct Parents { Parents(Expression* expr) { inner.walk(expr); } Expression* getParent(Expression* curr) { return inner.parentMap[curr]; } private: struct Inner : public ExpressionStackWalker> { void visitExpression(Expression* curr) { parentMap[curr] = getParent(); } std::map parentMap; } inner; }; } // namespace wasm #endif // wasm_ir_parents_h binaryen-version_91/src/ir/properties.h000066400000000000000000000146101362402614000204270ustar00rootroot00000000000000/* * Copyright 2016 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_ir_properties_h #define wasm_ir_properties_h #include "ir/bits.h" #include "ir/effects.h" #include "ir/iteration.h" #include "wasm.h" namespace wasm { namespace Properties { inline bool emitsBoolean(Expression* curr) { if (auto* unary = curr->dynCast()) { return unary->isRelational(); } else if (auto* binary = curr->dynCast()) { return binary->isRelational(); } return false; } inline bool isSymmetric(Binary* binary) { switch (binary->op) { case AddInt32: case MulInt32: case AndInt32: case OrInt32: case XorInt32: case EqInt32: case NeInt32: case AddInt64: case MulInt64: case AndInt64: case OrInt64: case XorInt64: case EqInt64: case NeInt64: return true; default: return false; } } // Check if an expression is a control flow construct with a name, // which implies it may have breaks to it. inline bool isNamedControlFlow(Expression* curr) { if (auto* block = curr->dynCast()) { return block->name.is(); } else if (auto* loop = curr->dynCast()) { return loop->name.is(); } return false; } inline bool isConstantExpression(const Expression* curr) { return curr->is() || curr->is() || curr->is(); } // Check if an expression is a sign-extend, and if so, returns the value // that is extended, otherwise nullptr inline Expression* getSignExtValue(Expression* curr) { if (auto* outer = curr->dynCast()) { if (outer->op == ShrSInt32) { if (auto* outerConst = outer->right->dynCast()) { if (outerConst->value.geti32() != 0) { if (auto* inner = outer->left->dynCast()) { if (inner->op == ShlInt32) { if (auto* innerConst = inner->right->dynCast()) { if (outerConst->value == innerConst->value) { return inner->left; } } } } } } } } return nullptr; } // gets the size of the sign-extended value inline Index getSignExtBits(Expression* curr) { return 32 - Bits::getEffectiveShifts(curr->cast()->right); } // Check if an expression is almost a sign-extend: perhaps the inner shift // is too large. We can split the shifts in that case, which is sometimes // useful (e.g. if we can remove the signext) inline Expression* getAlmostSignExt(Expression* curr) { if (auto* outer = curr->dynCast()) { if (outer->op == ShrSInt32) { if (auto* outerConst = outer->right->dynCast()) { if (outerConst->value.geti32() != 0) { if (auto* inner = outer->left->dynCast()) { if (inner->op == ShlInt32) { if (auto* innerConst = inner->right->dynCast()) { if (Bits::getEffectiveShifts(outerConst) <= Bits::getEffectiveShifts(innerConst)) { return inner->left; } } } } } } } } return nullptr; } // gets the size of the almost sign-extended value, as well as the // extra shifts, if any inline Index getAlmostSignExtBits(Expression* curr, Index& extraShifts) { extraShifts = Bits::getEffectiveShifts( curr->cast()->left->cast()->right) - Bits::getEffectiveShifts(curr->cast()->right); return getSignExtBits(curr); } // Check if an expression is a zero-extend, and if so, returns the value // that is extended, otherwise nullptr inline Expression* getZeroExtValue(Expression* curr) { if (auto* binary = curr->dynCast()) { if (binary->op == AndInt32) { if (auto* c = binary->right->dynCast()) { if (Bits::getMaskedBits(c->value.geti32())) { return binary->right; } } } } return nullptr; } // gets the size of the sign-extended value inline Index getZeroExtBits(Expression* curr) { return Bits::getMaskedBits( curr->cast()->right->cast()->value.geti32()); } // Returns a falling-through value, that is, it looks through a local.tee // and other operations that receive a value and let it flow through them. inline Expression* getFallthrough(Expression* curr, const PassOptions& passOptions, FeatureSet features) { // If the current node is unreachable, there is no value // falling through. if (curr->type == Type::unreachable) { return curr; } if (auto* set = curr->dynCast()) { if (set->isTee()) { return getFallthrough(set->value, passOptions, features); } } else if (auto* block = curr->dynCast()) { // if no name, we can't be broken to, and then can look at the fallthrough if (!block->name.is() && block->list.size() > 0) { return getFallthrough(block->list.back(), passOptions, features); } } else if (auto* loop = curr->dynCast()) { return getFallthrough(loop->body, passOptions, features); } else if (auto* iff = curr->dynCast()) { if (iff->ifFalse) { // Perhaps just one of the two actually returns. if (iff->ifTrue->type == Type::unreachable) { return getFallthrough(iff->ifFalse, passOptions, features); } else if (iff->ifFalse->type == Type::unreachable) { return getFallthrough(iff->ifTrue, passOptions, features); } } } else if (auto* br = curr->dynCast()) { if (br->condition && br->value) { return getFallthrough(br->value, passOptions, features); } } else if (auto* tryy = curr->dynCast()) { if (!EffectAnalyzer(passOptions, features, tryy->body).throws) { return getFallthrough(tryy->body, passOptions, features); } } return curr; } } // namespace Properties } // namespace wasm #endif // wasm_ir_properties_h binaryen-version_91/src/ir/table-utils.h000066400000000000000000000025671362402614000204700ustar00rootroot00000000000000/* * Copyright 2019 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_ir_table_h #define wasm_ir_table_h #include "wasm-traversal.h" #include "wasm.h" namespace wasm { struct FlatTable { std::vector names; bool valid; FlatTable(Table& table) { valid = true; for (auto& segment : table.segments) { auto offset = segment.offset; if (!offset->is()) { // TODO: handle some non-constant segments valid = false; return; } Index start = offset->cast()->value.geti32(); Index end = start + segment.data.size(); if (end > names.size()) { names.resize(end); } for (Index i = 0; i < segment.data.size(); i++) { names[start + i] = segment.data[i]; } } } }; } // namespace wasm #endif // wasm_ir_table_h binaryen-version_91/src/ir/trapping.h000066400000000000000000000054631362402614000200650ustar00rootroot00000000000000/* * Copyright 2017 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_ir_trapping_h #define wasm_ir_trapping_h #include #include "pass.h" namespace wasm { enum class TrapMode { Allow, Clamp, JS }; inline void addTrapModePass(PassRunner& runner, TrapMode trapMode) { if (trapMode == TrapMode::Clamp) { runner.add("trap-mode-clamp"); } else if (trapMode == TrapMode::JS) { runner.add("trap-mode-js"); } } class TrappingFunctionContainer { public: TrappingFunctionContainer(TrapMode mode, Module& wasm, bool immediate = false) : mode(mode), wasm(wasm), immediate(immediate) {} bool hasFunction(Name name) { return functions.find(name) != functions.end(); } bool hasImport(Name name) { return imports.find(name) != imports.end(); } void addFunction(Function* function) { functions[function->name] = function; if (immediate) { wasm.addFunction(function); } } void addImport(Function* import) { imports[import->name] = import; if (immediate) { wasm.addFunction(import); } } void addToModule() { if (!immediate) { for (auto& pair : functions) { wasm.addFunction(pair.second); } for (auto& pair : imports) { wasm.addFunction(pair.second); } } functions.clear(); imports.clear(); } TrapMode getMode() { return mode; } Module& getModule() { return wasm; } std::map& getFunctions() { return functions; } private: std::map functions; std::map imports; TrapMode mode; Module& wasm; bool immediate; }; Expression* makeTrappingBinary(Binary* curr, TrappingFunctionContainer& trappingFunctions); Expression* makeTrappingUnary(Unary* curr, TrappingFunctionContainer& trappingFunctions); inline TrapMode trapModeFromString(std::string const& str) { if (str == "allow") { return TrapMode::Allow; } else if (str == "clamp") { return TrapMode::Clamp; } else if (str == "js") { return TrapMode::JS; } else { throw std::invalid_argument( "Unsupported trap mode \"" + str + "\". " "Valid modes are \"allow\", \"js\", and \"clamp\""); } } } // namespace wasm #endif // wasm_ir_trapping_h binaryen-version_91/src/ir/type-updating.h000066400000000000000000000250211362402614000210230ustar00rootroot00000000000000/* * Copyright 2017 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_ir_type_updating_h #define wasm_ir_type_updating_h #include "wasm-traversal.h" namespace wasm { // a class that tracks type dependencies between nodes, letting you // update types efficiently when removing and altering code. // altering code can alter types in the following way: // * removing a break can make a block unreachable, if nothing else // reaches it // * altering the type of a child to unreachable can make the parent // unreachable struct TypeUpdater : public ExpressionStackWalker> { // Part 1: Scanning // track names to their blocks, so that when we remove a break to // a block, we know how to find it if we need to update it struct BlockInfo { Block* block = nullptr; int numBreaks = 0; }; std::map blockInfos; // track the parent of each node, as child type changes may lead to // unreachability std::map parents; void visitExpression(Expression* curr) { if (expressionStack.size() > 1) { parents[curr] = expressionStack[expressionStack.size() - 2]; } else { parents[curr] = nullptr; // this is the top level } // discover block/break relationships if (auto* block = curr->dynCast()) { if (block->name.is()) { blockInfos[block->name].block = block; } } else if (auto* br = curr->dynCast()) { // ensure info exists, discoverBreaks can then fill it blockInfos[br->name]; } else if (auto* sw = curr->dynCast()) { // ensure info exists, discoverBreaks can then fill it for (auto target : sw->targets) { blockInfos[target]; } blockInfos[sw->default_]; } else if (auto* br = curr->dynCast()) { blockInfos[br->name]; } // add a break to the info, for break and switch discoverBreaks(curr, +1); } // Part 2: Updating // Node replacements, additions, removals and type changes should be noted. An // exception is nodes you know will never be looked at again. // note the replacement of one node with another. this should be called // after performing the replacement. // this does *not* look into the node by default. see // noteReplacementWithRecursiveRemoval (we don't support recursive addition // because in practice we do not create new trees in the passes that use this, // they just move around children) void noteReplacement(Expression* from, Expression* to, bool recursivelyRemove = false) { auto parent = parents[from]; if (recursivelyRemove) { noteRecursiveRemoval(from); } else { noteRemoval(from); } // if we are replacing with a child, i.e. a node that was already present // in the ast, then we just have a type and parent to update if (parents.find(to) != parents.end()) { parents[to] = parent; if (from->type != to->type) { propagateTypesUp(to); } } else { noteAddition(to, parent, from); } } void noteReplacementWithRecursiveRemoval(Expression* from, Expression* to) { noteReplacement(from, to, true); } // note the removal of a node void noteRemoval(Expression* curr) { noteRemovalOrAddition(curr, nullptr); parents.erase(curr); } // note the removal of a node and all its children void noteRecursiveRemoval(Expression* curr) { struct Recurser : public PostWalker> { TypeUpdater& parent; Recurser(TypeUpdater& parent, Expression* root) : parent(parent) { walk(root); } void visitExpression(Expression* curr) { parent.noteRemoval(curr); } }; Recurser(*this, curr); } void noteAddition(Expression* curr, Expression* parent, Expression* previous = nullptr) { assert(parents.find(curr) == parents.end()); // must not already exist noteRemovalOrAddition(curr, parent); // if we didn't replace with the exact same type, propagate types up if (!(previous && previous->type == curr->type)) { propagateTypesUp(curr); } } // if parent is nullptr, this is a removal void noteRemovalOrAddition(Expression* curr, Expression* parent) { parents[curr] = parent; discoverBreaks(curr, parent ? +1 : -1); } // adds (or removes) breaks depending on break/switch contents void discoverBreaks(Expression* curr, int change) { if (auto* br = curr->dynCast()) { noteBreakChange(br->name, change, br->value); } else if (auto* sw = curr->dynCast()) { applySwitchChanges(sw, change); } else if (auto* br = curr->dynCast()) { noteBreakChange(br->name, change, br->sent); } } void applySwitchChanges(Switch* sw, int change) { std::set seen; for (auto target : sw->targets) { if (seen.insert(target).second) { noteBreakChange(target, change, sw->value); } } if (seen.insert(sw->default_).second) { noteBreakChange(sw->default_, change, sw->value); } } // note the addition of a node void noteBreakChange(Name name, int change, Expression* value) { noteBreakChange(name, change, value ? value->type : Type::none); } void noteBreakChange(Name name, int change, Type type) { auto iter = blockInfos.find(name); if (iter == blockInfos.end()) { return; // we can ignore breaks to loops } auto& info = iter->second; info.numBreaks += change; assert(info.numBreaks >= 0); auto* block = info.block; if (block) { // if to a loop, can ignore if (info.numBreaks == 0) { // dropped to 0! the block may now be unreachable. that // requires that it doesn't have a fallthrough makeBlockUnreachableIfNoFallThrough(block); } else if (change == 1 && info.numBreaks == 1) { // bumped to 1! the block may now be reachable if (block->type != Type::unreachable) { return; // was already reachable, had a fallthrough } changeTypeTo(block, type); } } } // alters the type of a node to a new type. // this propagates the type change through all the parents. void changeTypeTo(Expression* curr, Type newType) { if (curr->type == newType) { return; // nothing to do } curr->type = newType; propagateTypesUp(curr); } // given a node that has a new type, or is a new node, update // all the parents accordingly. the existence of the node and // any changes to it already occurred, this just updates the // parents following that. i.e., nothing is done to the // node we start on, it's done. // the one thing we need to do here is propagate unreachability, // no other change is possible void propagateTypesUp(Expression* curr) { if (curr->type != Type::unreachable) { return; } while (1) { auto* child = curr; curr = parents[child]; if (!curr) { return; } // get ready to apply unreachability to this node if (curr->type == Type::unreachable) { return; // already unreachable, stop here } // most nodes become unreachable if a child is unreachable, // but exceptions exist if (auto* block = curr->dynCast()) { // if the block has a fallthrough, it can keep its type if (block->list.back()->type.isConcrete()) { return; // did not turn } // if the block has breaks, it can keep its type if (!block->name.is() || blockInfos[block->name].numBreaks == 0) { curr->type = Type::unreachable; } else { return; // did not turn } } else if (auto* iff = curr->dynCast()) { // may not be unreachable if just one side is iff->finalize(); if (curr->type != Type::unreachable) { return; // did not turn } } else if (auto* tryy = curr->dynCast()) { tryy->finalize(); if (curr->type != Type::unreachable) { return; // did not turn } } else { curr->type = Type::unreachable; } } } // efficiently update the type of a block, given the data we know. this // can remove a concrete type and turn the block unreachable when it is // unreachable, and it does this efficiently, without scanning the full // contents void maybeUpdateTypeToUnreachable(Block* curr) { if (!curr->type.isConcrete()) { return; // nothing concrete to change to unreachable } if (curr->name.is() && blockInfos[curr->name].numBreaks > 0) { return; // has a break, not unreachable } // look for a fallthrough makeBlockUnreachableIfNoFallThrough(curr); } void makeBlockUnreachableIfNoFallThrough(Block* curr) { if (curr->type == Type::unreachable) { return; // no change possible } if (!curr->list.empty() && curr->list.back()->type.isConcrete()) { // should keep type due to fallthrough, even if has an unreachable child return; } for (auto* child : curr->list) { if (child->type == Type::unreachable) { // no fallthrough, and an unreachable, => this block is now unreachable changeTypeTo(curr, Type::unreachable); return; } } } // efficiently update the type of an if, given the data we know. this // can remove a concrete type and turn the if unreachable when it is // unreachable void maybeUpdateTypeToUnreachable(If* curr) { if (!curr->type.isConcrete()) { return; // nothing concrete to change to unreachable } curr->finalize(); if (curr->type == Type::unreachable) { propagateTypesUp(curr); } } void maybeUpdateTypeToUnreachable(Try* curr) { if (!curr->type.isConcrete()) { return; // nothing concrete to change to unreachable } curr->finalize(); if (curr->type == Type::unreachable) { propagateTypesUp(curr); } } }; } // namespace wasm #endif // wasm_ir_type_updating_h binaryen-version_91/src/ir/utils.h000066400000000000000000000315611362402614000173770ustar00rootroot00000000000000/* * Copyright 2016 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_ir_utils_h #define wasm_ir_utils_h #include "ir/branch-utils.h" #include "pass.h" #include "wasm-builder.h" #include "wasm-traversal.h" #include "wasm.h" namespace wasm { // Measure the size of an AST struct Measurer : public PostWalker> { Index size = 0; void visitExpression(Expression* curr) { size++; } static Index measure(Expression* tree) { Measurer measurer; measurer.walk(tree); return measurer.size; } }; struct ExpressionAnalyzer { // Given a stack of expressions, checks if the topmost is used as a result. // For example, if the parent is a block and the node is before the last // position, it is not used. static bool isResultUsed(ExpressionStack& stack, Function* func); // Checks if a value is dropped. static bool isResultDropped(ExpressionStack& stack); // Checks if a break is a simple - no condition, no value, just a plain // branching static bool isSimple(Break* curr) { return !curr->condition && !curr->value; } using ExprComparer = std::function; static bool flexibleEqual(Expression* left, Expression* right, ExprComparer comparer); // Compares two expressions for equivalence. static bool equal(Expression* left, Expression* right) { auto comparer = [](Expression* left, Expression* right) { return false; }; return flexibleEqual(left, right, comparer); } // A shallow comparison, ignoring child nodes. static bool shallowEqual(Expression* left, Expression* right) { auto comparer = [left, right](Expression* currLeft, Expression* currRight) { if (currLeft == left && currRight == right) { // these are the ones we want to compare return false; } // otherwise, don't do the comparison, we don't care return true; }; return flexibleEqual(left, right, comparer); } // hash an expression, ignoring superficial details like specific internal // names static HashType hash(Expression* curr); }; // Re-Finalizes all node types. This can be run after code was modified in // various ways that require propagating types around, and it does such an // "incremental" update. This is done under the assumption that there is // a valid assignment of types to apply. // This removes "unnecessary' block/if/loop types, i.e., that are added // specifically, as in // (block (result i32) (unreachable)) // vs // (block (unreachable)) // This converts to the latter form. // This also removes un-taken branches that would be a problem for // refinalization: if a block has been marked unreachable, and has // branches to it with values of type unreachable, then we don't // know the type for the block: it can't be none since the breaks // exist, but the breaks don't declare the type, rather everything // depends on the block. To avoid looking at the parent or something // else, just remove such un-taken branches. struct ReFinalize : public WalkerPass>> { bool isFunctionParallel() override { return true; } Pass* create() override { return new ReFinalize; } ReFinalize() { name = "refinalize"; } // block finalization is O(bad) if we do each block by itself, so do it in // bulk, tracking break value types so we just do a linear pass std::map breakValues; void visitBlock(Block* curr); void visitIf(If* curr); void visitLoop(Loop* curr); void visitBreak(Break* curr); void visitSwitch(Switch* curr); void visitCall(Call* curr); void visitCallIndirect(CallIndirect* curr); void visitLocalGet(LocalGet* curr); void visitLocalSet(LocalSet* curr); void visitGlobalGet(GlobalGet* curr); void visitGlobalSet(GlobalSet* curr); void visitLoad(Load* curr); void visitStore(Store* curr); void visitAtomicRMW(AtomicRMW* curr); void visitAtomicCmpxchg(AtomicCmpxchg* curr); void visitAtomicWait(AtomicWait* curr); void visitAtomicNotify(AtomicNotify* curr); void visitAtomicFence(AtomicFence* curr); void visitSIMDExtract(SIMDExtract* curr); void visitSIMDReplace(SIMDReplace* curr); void visitSIMDShuffle(SIMDShuffle* curr); void visitSIMDTernary(SIMDTernary* curr); void visitSIMDShift(SIMDShift* curr); void visitSIMDLoad(SIMDLoad* curr); void visitMemoryInit(MemoryInit* curr); void visitDataDrop(DataDrop* curr); void visitMemoryCopy(MemoryCopy* curr); void visitMemoryFill(MemoryFill* curr); void visitConst(Const* curr); void visitUnary(Unary* curr); void visitBinary(Binary* curr); void visitSelect(Select* curr); void visitDrop(Drop* curr); void visitReturn(Return* curr); void visitHost(Host* curr); void visitRefNull(RefNull* curr); void visitRefIsNull(RefIsNull* curr); void visitRefFunc(RefFunc* curr); void visitTry(Try* curr); void visitThrow(Throw* curr); void visitRethrow(Rethrow* curr); void visitBrOnExn(BrOnExn* curr); void visitNop(Nop* curr); void visitUnreachable(Unreachable* curr); void visitPush(Push* curr); void visitPop(Pop* curr); void visitFunction(Function* curr); void visitExport(Export* curr); void visitGlobal(Global* curr); void visitTable(Table* curr); void visitMemory(Memory* curr); void visitEvent(Event* curr); void visitModule(Module* curr); private: void updateBreakValueType(Name name, Type type); // Replace an untaken branch/switch with an unreachable value. // A condition may also exist and may or may not be unreachable. void replaceUntaken(Expression* value, Expression* condition); }; // Re-finalize a single node. This is slow, if you want to refinalize // an entire ast, use ReFinalize struct ReFinalizeNode : public OverriddenVisitor { void visitBlock(Block* curr) { curr->finalize(); } void visitIf(If* curr) { curr->finalize(); } void visitLoop(Loop* curr) { curr->finalize(); } void visitBreak(Break* curr) { curr->finalize(); } void visitSwitch(Switch* curr) { curr->finalize(); } void visitCall(Call* curr) { curr->finalize(); } void visitCallIndirect(CallIndirect* curr) { curr->finalize(); } void visitLocalGet(LocalGet* curr) { curr->finalize(); } void visitLocalSet(LocalSet* curr) { curr->finalize(); } void visitGlobalGet(GlobalGet* curr) { curr->finalize(); } void visitGlobalSet(GlobalSet* curr) { curr->finalize(); } void visitLoad(Load* curr) { curr->finalize(); } void visitStore(Store* curr) { curr->finalize(); } void visitAtomicRMW(AtomicRMW* curr) { curr->finalize(); } void visitAtomicCmpxchg(AtomicCmpxchg* curr) { curr->finalize(); } void visitAtomicWait(AtomicWait* curr) { curr->finalize(); } void visitAtomicNotify(AtomicNotify* curr) { curr->finalize(); } void visitAtomicFence(AtomicFence* curr) { curr->finalize(); } void visitSIMDExtract(SIMDExtract* curr) { curr->finalize(); } void visitSIMDReplace(SIMDReplace* curr) { curr->finalize(); } void visitSIMDShuffle(SIMDShuffle* curr) { curr->finalize(); } void visitSIMDTernary(SIMDTernary* curr) { curr->finalize(); } void visitSIMDShift(SIMDShift* curr) { curr->finalize(); } void visitSIMDLoad(SIMDLoad* curr) { curr->finalize(); } void visitMemoryInit(MemoryInit* curr) { curr->finalize(); } void visitDataDrop(DataDrop* curr) { curr->finalize(); } void visitMemoryCopy(MemoryCopy* curr) { curr->finalize(); } void visitMemoryFill(MemoryFill* curr) { curr->finalize(); } void visitConst(Const* curr) { curr->finalize(); } void visitUnary(Unary* curr) { curr->finalize(); } void visitBinary(Binary* curr) { curr->finalize(); } void visitSelect(Select* curr) { curr->finalize(); } void visitDrop(Drop* curr) { curr->finalize(); } void visitReturn(Return* curr) { curr->finalize(); } void visitHost(Host* curr) { curr->finalize(); } void visitRefNull(RefNull* curr) { curr->finalize(); } void visitRefIsNull(RefIsNull* curr) { curr->finalize(); } void visitRefFunc(RefFunc* curr) { curr->finalize(); } void visitTry(Try* curr) { curr->finalize(); } void visitThrow(Throw* curr) { curr->finalize(); } void visitRethrow(Rethrow* curr) { curr->finalize(); } void visitBrOnExn(BrOnExn* curr) { curr->finalize(); } void visitNop(Nop* curr) { curr->finalize(); } void visitUnreachable(Unreachable* curr) { curr->finalize(); } void visitPush(Push* curr) { curr->finalize(); } void visitPop(Pop* curr) { curr->finalize(); } void visitExport(Export* curr) { WASM_UNREACHABLE("unimp"); } void visitGlobal(Global* curr) { WASM_UNREACHABLE("unimp"); } void visitTable(Table* curr) { WASM_UNREACHABLE("unimp"); } void visitMemory(Memory* curr) { WASM_UNREACHABLE("unimp"); } void visitEvent(Event* curr) { WASM_UNREACHABLE("unimp"); } void visitModule(Module* curr) { WASM_UNREACHABLE("unimp"); } // given a stack of nested expressions, update them all from child to parent static void updateStack(ExpressionStack& expressionStack) { for (int i = int(expressionStack.size()) - 1; i >= 0; i--) { auto* curr = expressionStack[i]; ReFinalizeNode().visit(curr); } } }; // Adds drop() operations where necessary. This lets you not worry about adding // drop when generating code. This also refinalizes before and after, as // dropping can change types, and depends on types being cleaned up - no // unnecessary block/if/loop types (see refinalize) // TODO: optimize that, interleave them struct AutoDrop : public WalkerPass> { bool isFunctionParallel() override { return true; } Pass* create() override { return new AutoDrop; } AutoDrop() { name = "autodrop"; } bool maybeDrop(Expression*& child) { bool acted = false; if (child->type.isConcrete()) { expressionStack.push_back(child); if (!ExpressionAnalyzer::isResultUsed(expressionStack, getFunction()) && !ExpressionAnalyzer::isResultDropped(expressionStack)) { child = Builder(*getModule()).makeDrop(child); acted = true; } expressionStack.pop_back(); } return acted; } void reFinalize() { ReFinalizeNode::updateStack(expressionStack); } void visitBlock(Block* curr) { if (curr->list.size() == 0) { return; } for (Index i = 0; i < curr->list.size() - 1; i++) { auto* child = curr->list[i]; if (child->type.isConcrete()) { curr->list[i] = Builder(*getModule()).makeDrop(child); } } if (maybeDrop(curr->list.back())) { reFinalize(); assert(curr->type == Type::none || curr->type == Type::unreachable); } } void visitIf(If* curr) { bool acted = false; if (maybeDrop(curr->ifTrue)) { acted = true; } if (curr->ifFalse) { if (maybeDrop(curr->ifFalse)) { acted = true; } } if (acted) { reFinalize(); assert(curr->type == Type::none); } } void visitTry(Try* curr) { bool acted = false; if (maybeDrop(curr->body)) { acted = true; } if (maybeDrop(curr->catchBody)) { acted = true; } if (acted) { reFinalize(); assert(curr->type == Type::none); } } void doWalkFunction(Function* curr) { ReFinalize().walkFunctionInModule(curr, getModule()); walk(curr->body); if (curr->sig.results == Type::none && curr->body->type.isConcrete()) { curr->body = Builder(*getModule()).makeDrop(curr->body); } ReFinalize().walkFunctionInModule(curr, getModule()); } }; struct I64Utilities { static Expression* recreateI64(Builder& builder, Expression* low, Expression* high) { return builder.makeBinary( OrInt64, builder.makeUnary(ExtendUInt32, low), builder.makeBinary(ShlInt64, builder.makeUnary(ExtendUInt32, high), builder.makeConst(Literal(int64_t(32))))); }; static Expression* recreateI64(Builder& builder, Index low, Index high) { return recreateI64(builder, builder.makeLocalGet(low, Type::i32), builder.makeLocalGet(high, Type::i32)); }; static Expression* getI64High(Builder& builder, Index index) { return builder.makeUnary( WrapInt64, builder.makeBinary(ShrUInt64, builder.makeLocalGet(index, Type::i64), builder.makeConst(Literal(int64_t(32))))); } static Expression* getI64Low(Builder& builder, Index index) { return builder.makeUnary(WrapInt64, builder.makeLocalGet(index, Type::i64)); } }; } // namespace wasm #endif // wasm_ir_utils_h binaryen-version_91/src/js/000077500000000000000000000000001362402614000160625ustar00rootroot00000000000000binaryen-version_91/src/js/binaryen.js-post.js000066400000000000000000003270011362402614000216300ustar00rootroot00000000000000// export friendly API methods function preserveStack(func) { try { var stack = stackSave(); return func(); } finally { stackRestore(stack); } } function strToStack(str) { if (!str) return 0; return allocate(intArrayFromString(str), 'i8', ALLOC_STACK); } function i32sToStack(i32s) { var ret = stackAlloc(i32s.length << 2); for (var i = 0; i < i32s.length; i++) { HEAP32[ret + (i << 2) >> 2] = i32s[i]; } return ret; } function i8sToStack(i8s) { var ret = stackAlloc(i8s.length); for (var i = 0; i < i8s.length; i++) { HEAP8[ret + i] = i8s[i]; } return ret; } function initializeConstants() { // Types [ ['none', 'None'], ['i32', 'Int32'], ['i64', 'Int64'], ['f32', 'Float32'], ['f64', 'Float64'], ['v128', 'Vec128'], ['funcref', 'Funcref'], ['anyref', 'Anyref'], ['nullref', 'Nullref'], ['exnref', 'Exnref'], ['unreachable', 'Unreachable'], ['auto', 'Auto'] ].forEach(function(entry) { Module[entry[0]] = Module['_BinaryenType' + entry[1]](); }); // Expression ids Module['ExpressionIds'] = {}; [ 'Invalid', 'Block', 'If', 'Loop', 'Break', 'Switch', 'Call', 'CallIndirect', 'LocalGet', 'LocalSet', 'GlobalGet', 'GlobalSet', 'Load', 'Store', 'Const', 'Unary', 'Binary', 'Select', 'Drop', 'Return', 'Host', 'Nop', 'Unreachable', 'AtomicCmpxchg', 'AtomicRMW', 'AtomicWait', 'AtomicNotify', 'AtomicFence', 'SIMDExtract', 'SIMDReplace', 'SIMDShuffle', 'SIMDTernary', 'SIMDShift', 'SIMDLoad', 'MemoryInit', 'DataDrop', 'MemoryCopy', 'MemoryFill', 'RefNull', 'RefIsNull', 'RefFunc', 'Try', 'Throw', 'Rethrow', 'BrOnExn', 'Push', 'Pop' ].forEach(function(name) { Module['ExpressionIds'][name] = Module[name + 'Id'] = Module['_Binaryen' + name + 'Id'](); }); // External kinds Module['ExternalKinds'] = {}; [ 'Function', 'Table', 'Memory', 'Global', 'Event' ].forEach(function(name) { Module['ExternalKinds'][name] = Module['External' + name] = Module['_BinaryenExternal' + name](); }); // Features Module['Features'] = {}; [ 'MVP', 'Atomics', 'BulkMemory', 'MutableGlobals', 'NontrappingFPToInt', 'SignExt', 'SIMD128', 'ExceptionHandling', 'TailCall', 'ReferenceTypes', 'All' ].forEach(function(name) { Module['Features'][name] = Module['_BinaryenFeature' + name](); }); // Operations Module['Operations'] = {}; [ 'ClzInt32', 'CtzInt32', 'PopcntInt32', 'NegFloat32', 'AbsFloat32', 'CeilFloat32', 'FloorFloat32', 'TruncFloat32', 'NearestFloat32', 'SqrtFloat32', 'EqZInt32', 'ClzInt64', 'CtzInt64', 'PopcntInt64', 'NegFloat64', 'AbsFloat64', 'CeilFloat64', 'FloorFloat64', 'TruncFloat64', 'NearestFloat64', 'SqrtFloat64', 'EqZInt64', 'ExtendSInt32', 'ExtendUInt32', 'WrapInt64', 'TruncSFloat32ToInt32', 'TruncSFloat32ToInt64', 'TruncUFloat32ToInt32', 'TruncUFloat32ToInt64', 'TruncSFloat64ToInt32', 'TruncSFloat64ToInt64', 'TruncUFloat64ToInt32', 'TruncUFloat64ToInt64', 'TruncSatSFloat32ToInt32', 'TruncSatSFloat32ToInt64', 'TruncSatUFloat32ToInt32', 'TruncSatUFloat32ToInt64', 'TruncSatSFloat64ToInt32', 'TruncSatSFloat64ToInt64', 'TruncSatUFloat64ToInt32', 'TruncSatUFloat64ToInt64', 'ReinterpretFloat32', 'ReinterpretFloat64', 'ConvertSInt32ToFloat32', 'ConvertSInt32ToFloat64', 'ConvertUInt32ToFloat32', 'ConvertUInt32ToFloat64', 'ConvertSInt64ToFloat32', 'ConvertSInt64ToFloat64', 'ConvertUInt64ToFloat32', 'ConvertUInt64ToFloat64', 'PromoteFloat32', 'DemoteFloat64', 'ReinterpretInt32', 'ReinterpretInt64', 'ExtendS8Int32', 'ExtendS16Int32', 'ExtendS8Int64', 'ExtendS16Int64', 'ExtendS32Int64', 'AddInt32', 'SubInt32', 'MulInt32', 'DivSInt32', 'DivUInt32', 'RemSInt32', 'RemUInt32', 'AndInt32', 'OrInt32', 'XorInt32', 'ShlInt32', 'ShrUInt32', 'ShrSInt32', 'RotLInt32', 'RotRInt32', 'EqInt32', 'NeInt32', 'LtSInt32', 'LtUInt32', 'LeSInt32', 'LeUInt32', 'GtSInt32', 'GtUInt32', 'GeSInt32', 'GeUInt32', 'AddInt64', 'SubInt64', 'MulInt64', 'DivSInt64', 'DivUInt64', 'RemSInt64', 'RemUInt64', 'AndInt64', 'OrInt64', 'XorInt64', 'ShlInt64', 'ShrUInt64', 'ShrSInt64', 'RotLInt64', 'RotRInt64', 'EqInt64', 'NeInt64', 'LtSInt64', 'LtUInt64', 'LeSInt64', 'LeUInt64', 'GtSInt64', 'GtUInt64', 'GeSInt64', 'GeUInt64', 'AddFloat32', 'SubFloat32', 'MulFloat32', 'DivFloat32', 'CopySignFloat32', 'MinFloat32', 'MaxFloat32', 'EqFloat32', 'NeFloat32', 'LtFloat32', 'LeFloat32', 'GtFloat32', 'GeFloat32', 'AddFloat64', 'SubFloat64', 'MulFloat64', 'DivFloat64', 'CopySignFloat64', 'MinFloat64', 'MaxFloat64', 'EqFloat64', 'NeFloat64', 'LtFloat64', 'LeFloat64', 'GtFloat64', 'GeFloat64', 'MemorySize', 'MemoryGrow', 'AtomicRMWAdd', 'AtomicRMWSub', 'AtomicRMWAnd', 'AtomicRMWOr', 'AtomicRMWXor', 'AtomicRMWXchg', 'SplatVecI8x16', 'ExtractLaneSVecI8x16', 'ExtractLaneUVecI8x16', 'ReplaceLaneVecI8x16', 'SplatVecI16x8', 'ExtractLaneSVecI16x8', 'ExtractLaneUVecI16x8', 'ReplaceLaneVecI16x8', 'SplatVecI32x4', 'ExtractLaneVecI32x4', 'ReplaceLaneVecI32x4', 'SplatVecI64x2', 'ExtractLaneVecI64x2', 'ReplaceLaneVecI64x2', 'SplatVecF32x4', 'ExtractLaneVecF32x4', 'ReplaceLaneVecF32x4', 'SplatVecF64x2', 'ExtractLaneVecF64x2', 'ReplaceLaneVecF64x2', 'EqVecI8x16', 'NeVecI8x16', 'LtSVecI8x16', 'LtUVecI8x16', 'GtSVecI8x16', 'GtUVecI8x16', 'LeSVecI8x16', 'LeUVecI8x16', 'GeSVecI8x16', 'GeUVecI8x16', 'EqVecI16x8', 'NeVecI16x8', 'LtSVecI16x8', 'LtUVecI16x8', 'GtSVecI16x8', 'GtUVecI16x8', 'LeSVecI16x8', 'LeUVecI16x8', 'GeSVecI16x8', 'GeUVecI16x8', 'EqVecI32x4', 'NeVecI32x4', 'LtSVecI32x4', 'LtUVecI32x4', 'GtSVecI32x4', 'GtUVecI32x4', 'LeSVecI32x4', 'LeUVecI32x4', 'GeSVecI32x4', 'GeUVecI32x4', 'EqVecF32x4', 'NeVecF32x4', 'LtVecF32x4', 'GtVecF32x4', 'LeVecF32x4', 'GeVecF32x4', 'EqVecF64x2', 'NeVecF64x2', 'LtVecF64x2', 'GtVecF64x2', 'LeVecF64x2', 'GeVecF64x2', 'NotVec128', 'AndVec128', 'OrVec128', 'XorVec128', 'AndNotVec128', 'BitselectVec128', 'NegVecI8x16', 'AnyTrueVecI8x16', 'AllTrueVecI8x16', 'ShlVecI8x16', 'ShrSVecI8x16', 'ShrUVecI8x16', 'AddVecI8x16', 'AddSatSVecI8x16', 'AddSatUVecI8x16', 'SubVecI8x16', 'SubSatSVecI8x16', 'SubSatUVecI8x16', 'MulVecI8x16', 'MinSVecI8x16', 'MinUVecI8x16', 'MaxSVecI8x16', 'MaxUVecI8x16', 'AvgrUVecI8x16', 'NegVecI16x8', 'AnyTrueVecI16x8', 'AllTrueVecI16x8', 'ShlVecI16x8', 'ShrSVecI16x8', 'ShrUVecI16x8', 'AddVecI16x8', 'AddSatSVecI16x8', 'AddSatUVecI16x8', 'SubVecI16x8', 'SubSatSVecI16x8', 'SubSatUVecI16x8', 'MulVecI16x8', 'MinSVecI16x8', 'MinUVecI16x8', 'MaxSVecI16x8', 'MaxUVecI16x8', 'AvgrUVecI16x8', 'DotSVecI16x8ToVecI32x4', 'NegVecI32x4', 'AnyTrueVecI32x4', 'AllTrueVecI32x4', 'ShlVecI32x4', 'ShrSVecI32x4', 'ShrUVecI32x4', 'AddVecI32x4', 'SubVecI32x4', 'MulVecI32x4', 'MinSVecI32x4', 'MinUVecI32x4', 'MaxSVecI32x4', 'MaxUVecI32x4', 'NegVecI64x2', 'AnyTrueVecI64x2', 'AllTrueVecI64x2', 'ShlVecI64x2', 'ShrSVecI64x2', 'ShrUVecI64x2', 'AddVecI64x2', 'SubVecI64x2', 'AbsVecF32x4', 'NegVecF32x4', 'SqrtVecF32x4', 'QFMAVecF32x4', 'QFMSVecF32x4', 'AddVecF32x4', 'SubVecF32x4', 'MulVecF32x4', 'DivVecF32x4', 'MinVecF32x4', 'MaxVecF32x4', 'AbsVecF64x2', 'NegVecF64x2', 'SqrtVecF64x2', 'QFMAVecF64x2', 'QFMSVecF64x2', 'AddVecF64x2', 'SubVecF64x2', 'MulVecF64x2', 'DivVecF64x2', 'MinVecF64x2', 'MaxVecF64x2', 'TruncSatSVecF32x4ToVecI32x4', 'TruncSatUVecF32x4ToVecI32x4', 'TruncSatSVecF64x2ToVecI64x2', 'TruncSatUVecF64x2ToVecI64x2', 'ConvertSVecI32x4ToVecF32x4', 'ConvertUVecI32x4ToVecF32x4', 'ConvertSVecI64x2ToVecF64x2', 'ConvertUVecI64x2ToVecF64x2', 'LoadSplatVec8x16', 'LoadSplatVec16x8', 'LoadSplatVec32x4', 'LoadSplatVec64x2', 'LoadExtSVec8x8ToVecI16x8', 'LoadExtUVec8x8ToVecI16x8', 'LoadExtSVec16x4ToVecI32x4', 'LoadExtUVec16x4ToVecI32x4', 'LoadExtSVec32x2ToVecI64x2', 'LoadExtUVec32x2ToVecI64x2', 'NarrowSVecI16x8ToVecI8x16', 'NarrowUVecI16x8ToVecI8x16', 'NarrowSVecI32x4ToVecI16x8', 'NarrowUVecI32x4ToVecI16x8', 'WidenLowSVecI8x16ToVecI16x8', 'WidenHighSVecI8x16ToVecI16x8', 'WidenLowUVecI8x16ToVecI16x8', 'WidenHighUVecI8x16ToVecI16x8', 'WidenLowSVecI16x8ToVecI32x4', 'WidenHighSVecI16x8ToVecI32x4', 'WidenLowUVecI16x8ToVecI32x4', 'WidenHighUVecI16x8ToVecI32x4', 'SwizzleVec8x16', ].forEach(function(name) { Module['Operations'][name] = Module[name] = Module['_Binaryen' + name](); }); // Expression side effects Module['SideEffects'] = {}; [ 'None', 'Branches', 'Calls', 'ReadsLocal', 'WritesLocal', 'ReadsGlobal', 'WritesGlobal', 'ReadsMemory', 'WritesMemory', 'ImplicitTrap', 'IsAtomic', 'Throws', 'Any' ].forEach(function(name) { Module['SideEffects'][name] = Module['_BinaryenSideEffect' + name](); }); } // 'Module' interface Module['Module'] = function(module) { assert(!module); // guard against incorrect old API usage wrapModule(Module['_BinaryenModuleCreate'](), this); }; // Receives a C pointer to a C Module and a JS object, and creates // the JS wrappings on the object to access the C data. // This is meant for internal use only, and is necessary as we // want to access Module from JS that were perhaps not created // from JS. function wrapModule(module, self) { assert(module); // guard against incorrect old API usage if (!self) self = {}; self['ptr'] = module; // The size of a single literal in memory as used in Const creation, // which is a little different: we don't want users to need to make // their own Literals, as the C API handles them by value, which means // we would leak them. Instead, Const creation is fused together with // an intermediate stack allocation of this size to pass the value. var sizeOfLiteral = _BinaryenSizeofLiteral(); // 'Expression' creation self['block'] = function(name, children, type) { return preserveStack(function() { return Module['_BinaryenBlock'](module, name ? strToStack(name) : 0, i32sToStack(children), children.length, typeof type !== 'undefined' ? type : Module['none']); }); }; self['if'] = function(condition, ifTrue, ifFalse) { return Module['_BinaryenIf'](module, condition, ifTrue, ifFalse); }; self['loop'] = function(label, body) { return preserveStack(function() { return Module['_BinaryenLoop'](module, strToStack(label), body); }); }; self['break'] = self['br'] = function(label, condition, value) { return preserveStack(function() { return Module['_BinaryenBreak'](module, strToStack(label), condition, value); }); }; self['br_if'] = function(label, condition, value) { return self['br'](label, condition, value); }; self['switch'] = function(names, defaultName, condition, value) { return preserveStack(function() { var namei32s = []; names.forEach(function(name) { namei32s.push(strToStack(name)); }); return Module['_BinaryenSwitch'](module, i32sToStack(namei32s), namei32s.length, strToStack(defaultName), condition, value); }); }; self['call'] = function(name, operands, type) { return preserveStack(function() { return Module['_BinaryenCall'](module, strToStack(name), i32sToStack(operands), operands.length, type); }); }; self['callIndirect'] = self['call_indirect'] = function(target, operands, params, results) { return preserveStack(function() { return Module['_BinaryenCallIndirect'](module, target, i32sToStack(operands), operands.length, params, results); }); }; self['returnCall'] = function(name, operands, type) { return preserveStack(function() { return Module['_BinaryenReturnCall'](module, strToStack(name), i32sToStack(operands), operands.length, type); }); }; self['returnCallIndirect'] = function(target, operands, params, results) { return preserveStack(function() { return Module['_BinaryenReturnCallIndirect'](module, target, i32sToStack(operands), operands.length, params, results); }); }; self['local'] = { 'get': function(index, type) { return Module['_BinaryenLocalGet'](module, index, type); }, 'set': function(index, value) { return Module['_BinaryenLocalSet'](module, index, value); }, 'tee': function(index, value, type) { if (typeof type === 'undefined') { throw new Error("local.tee's type should be defined"); } return Module['_BinaryenLocalTee'](module, index, value, type); } } self['global'] = { 'get': function(name, type) { return Module['_BinaryenGlobalGet'](module, strToStack(name), type); }, 'set': function(name, value) { return Module['_BinaryenGlobalSet'](module, strToStack(name), value); } } self['memory'] = { 'size': function() { return Module['_BinaryenHost'](module, Module['MemorySize']); }, 'grow': function(value) { return Module['_BinaryenHost'](module, Module['MemoryGrow'], null, i32sToStack([value]), 1); }, 'init': function(segment, dest, offset, size) { return Module['_BinaryenMemoryInit'](module, segment, dest, offset, size); }, 'copy': function(dest, source, size) { return Module['_BinaryenMemoryCopy'](module, dest, source, size); }, 'fill': function(dest, value, size) { return Module['_BinaryenMemoryFill'](module, dest, value, size); } } self['data'] = { 'drop': function(segment) { return Module['_BinaryenDataDrop'](module, segment); } } self['i32'] = { 'load': function(offset, align, ptr) { return Module['_BinaryenLoad'](module, 4, true, offset, align, Module['i32'], ptr); }, 'load8_s': function(offset, align, ptr) { return Module['_BinaryenLoad'](module, 1, true, offset, align, Module['i32'], ptr); }, 'load8_u': function(offset, align, ptr) { return Module['_BinaryenLoad'](module, 1, false, offset, align, Module['i32'], ptr); }, 'load16_s': function(offset, align, ptr) { return Module['_BinaryenLoad'](module, 2, true, offset, align, Module['i32'], ptr); }, 'load16_u': function(offset, align, ptr) { return Module['_BinaryenLoad'](module, 2, false, offset, align, Module['i32'], ptr); }, 'store': function(offset, align, ptr, value) { return Module['_BinaryenStore'](module, 4, offset, align, ptr, value, Module['i32']); }, 'store8': function(offset, align, ptr, value) { return Module['_BinaryenStore'](module, 1, offset, align, ptr, value, Module['i32']); }, 'store16': function(offset, align, ptr, value) { return Module['_BinaryenStore'](module, 2, offset, align, ptr, value, Module['i32']); }, 'const': function(x) { return preserveStack(function() { var tempLiteral = stackAlloc(sizeOfLiteral); Module['_BinaryenLiteralInt32'](tempLiteral, x); return Module['_BinaryenConst'](module, tempLiteral); }); }, 'clz': function(value) { return Module['_BinaryenUnary'](module, Module['ClzInt32'], value); }, 'ctz': function(value) { return Module['_BinaryenUnary'](module, Module['CtzInt32'], value); }, 'popcnt': function(value) { return Module['_BinaryenUnary'](module, Module['PopcntInt32'], value); }, 'eqz': function(value) { return Module['_BinaryenUnary'](module, Module['EqZInt32'], value); }, 'trunc_s': { 'f32': function(value) { return Module['_BinaryenUnary'](module, Module['TruncSFloat32ToInt32'], value); }, 'f64': function(value) { return Module['_BinaryenUnary'](module, Module['TruncSFloat64ToInt32'], value); }, }, 'trunc_u': { 'f32': function(value) { return Module['_BinaryenUnary'](module, Module['TruncUFloat32ToInt32'], value); }, 'f64': function(value) { return Module['_BinaryenUnary'](module, Module['TruncUFloat64ToInt32'], value); }, }, 'trunc_s_sat': { 'f32': function(value) { return Module['_BinaryenUnary'](module, Module['TruncSatSFloat32ToInt32'], value); }, 'f64': function(value) { return Module['_BinaryenUnary'](module, Module['TruncSatSFloat64ToInt32'], value); }, }, 'trunc_u_sat': { 'f32': function(value) { return Module['_BinaryenUnary'](module, Module['TruncSatUFloat32ToInt32'], value); }, 'f64': function(value) { return Module['_BinaryenUnary'](module, Module['TruncSatUFloat64ToInt32'], value); }, }, 'reinterpret': function(value) { return Module['_BinaryenUnary'](module, Module['ReinterpretFloat32'], value); }, 'extend8_s': function(value) { return Module['_BinaryenUnary'](module, Module['ExtendS8Int32'], value); }, 'extend16_s': function(value) { return Module['_BinaryenUnary'](module, Module['ExtendS16Int32'], value); }, 'wrap': function(value) { return Module['_BinaryenUnary'](module, Module['WrapInt64'], value); }, 'add': function(left, right) { return Module['_BinaryenBinary'](module, Module['AddInt32'], left, right); }, 'sub': function(left, right) { return Module['_BinaryenBinary'](module, Module['SubInt32'], left, right); }, 'mul': function(left, right) { return Module['_BinaryenBinary'](module, Module['MulInt32'], left, right); }, 'div_s': function(left, right) { return Module['_BinaryenBinary'](module, Module['DivSInt32'], left, right); }, 'div_u': function(left, right) { return Module['_BinaryenBinary'](module, Module['DivUInt32'], left, right); }, 'rem_s': function(left, right) { return Module['_BinaryenBinary'](module, Module['RemSInt32'], left, right); }, 'rem_u': function(left, right) { return Module['_BinaryenBinary'](module, Module['RemUInt32'], left, right); }, 'and': function(left, right) { return Module['_BinaryenBinary'](module, Module['AndInt32'], left, right); }, 'or': function(left, right) { return Module['_BinaryenBinary'](module, Module['OrInt32'], left, right); }, 'xor': function(left, right) { return Module['_BinaryenBinary'](module, Module['XorInt32'], left, right); }, 'shl': function(left, right) { return Module['_BinaryenBinary'](module, Module['ShlInt32'], left, right); }, 'shr_u': function(left, right) { return Module['_BinaryenBinary'](module, Module['ShrUInt32'], left, right); }, 'shr_s': function(left, right) { return Module['_BinaryenBinary'](module, Module['ShrSInt32'], left, right); }, 'rotl': function(left, right) { return Module['_BinaryenBinary'](module, Module['RotLInt32'], left, right); }, 'rotr': function(left, right) { return Module['_BinaryenBinary'](module, Module['RotRInt32'], left, right); }, 'eq': function(left, right) { return Module['_BinaryenBinary'](module, Module['EqInt32'], left, right); }, 'ne': function(left, right) { return Module['_BinaryenBinary'](module, Module['NeInt32'], left, right); }, 'lt_s': function(left, right) { return Module['_BinaryenBinary'](module, Module['LtSInt32'], left, right); }, 'lt_u': function(left, right) { return Module['_BinaryenBinary'](module, Module['LtUInt32'], left, right); }, 'le_s': function(left, right) { return Module['_BinaryenBinary'](module, Module['LeSInt32'], left, right); }, 'le_u': function(left, right) { return Module['_BinaryenBinary'](module, Module['LeUInt32'], left, right); }, 'gt_s': function(left, right) { return Module['_BinaryenBinary'](module, Module['GtSInt32'], left, right); }, 'gt_u': function(left, right) { return Module['_BinaryenBinary'](module, Module['GtUInt32'], left, right); }, 'ge_s': function(left, right) { return Module['_BinaryenBinary'](module, Module['GeSInt32'], left, right); }, 'ge_u': function(left, right) { return Module['_BinaryenBinary'](module, Module['GeUInt32'], left, right); }, 'atomic': { 'load': function(offset, ptr) { return Module['_BinaryenAtomicLoad'](module, 4, offset, Module['i32'], ptr); }, 'load8_u': function(offset, ptr) { return Module['_BinaryenAtomicLoad'](module, 1, offset, Module['i32'], ptr); }, 'load16_u': function(offset, ptr) { return Module['_BinaryenAtomicLoad'](module, 2, offset, Module['i32'], ptr); }, 'store': function(offset, ptr, value) { return Module['_BinaryenAtomicStore'](module, 4, offset, ptr, value, Module['i32']); }, 'store8': function(offset, ptr, value) { return Module['_BinaryenAtomicStore'](module, 1, offset, ptr, value, Module['i32']); }, 'store16': function(offset, ptr, value) { return Module['_BinaryenAtomicStore'](module, 2, offset, ptr, value, Module['i32']); }, 'rmw': { 'add': function(offset, ptr, value) { return Module['_BinaryenAtomicRMW'](module, Module['AtomicRMWAdd'], 4, offset, ptr, value, Module['i32']); }, 'sub': function(offset, ptr, value) { return Module['_BinaryenAtomicRMW'](module, Module['AtomicRMWSub'], 4, offset, ptr, value, Module['i32']); }, 'and': function(offset, ptr, value) { return Module['_BinaryenAtomicRMW'](module, Module['AtomicRMWAnd'], 4, offset, ptr, value, Module['i32']); }, 'or': function(offset, ptr, value) { return Module['_BinaryenAtomicRMW'](module, Module['AtomicRMWOr'], 4, offset, ptr, value, Module['i32']); }, 'xor': function(offset, ptr, value) { return Module['_BinaryenAtomicRMW'](module, Module['AtomicRMWXor'], 4, offset, ptr, value, Module['i32']); }, 'xchg': function(offset, ptr, value) { return Module['_BinaryenAtomicRMW'](module, Module['AtomicRMWXchg'], 4, offset, ptr, value, Module['i32']); }, 'cmpxchg': function(offset, ptr, expected, replacement) { return Module['_BinaryenAtomicCmpxchg'](module, 4, offset, ptr, expected, replacement, Module['i32']) }, }, 'rmw8_u': { 'add': function(offset, ptr, value) { return Module['_BinaryenAtomicRMW'](module, Module['AtomicRMWAdd'], 1, offset, ptr, value, Module['i32']); }, 'sub': function(offset, ptr, value) { return Module['_BinaryenAtomicRMW'](module, Module['AtomicRMWSub'], 1, offset, ptr, value, Module['i32']); }, 'and': function(offset, ptr, value) { return Module['_BinaryenAtomicRMW'](module, Module['AtomicRMWAnd'], 1, offset, ptr, value, Module['i32']); }, 'or': function(offset, ptr, value) { return Module['_BinaryenAtomicRMW'](module, Module['AtomicRMWOr'], 1, offset, ptr, value, Module['i32']); }, 'xor': function(offset, ptr, value) { return Module['_BinaryenAtomicRMW'](module, Module['AtomicRMWXor'], 1, offset, ptr, value, Module['i32']); }, 'xchg': function(offset, ptr, value) { return Module['_BinaryenAtomicRMW'](module, Module['AtomicRMWXchg'], 1, offset, ptr, value, Module['i32']); }, 'cmpxchg': function(offset, ptr, expected, replacement) { return Module['_BinaryenAtomicCmpxchg'](module, 1, offset, ptr, expected, replacement, Module['i32']) }, }, 'rmw16_u': { 'add': function(offset, ptr, value) { return Module['_BinaryenAtomicRMW'](module, Module['AtomicRMWAdd'], 2, offset, ptr, value, Module['i32']); }, 'sub': function(offset, ptr, value) { return Module['_BinaryenAtomicRMW'](module, Module['AtomicRMWSub'], 2, offset, ptr, value, Module['i32']); }, 'and': function(offset, ptr, value) { return Module['_BinaryenAtomicRMW'](module, Module['AtomicRMWAnd'], 2, offset, ptr, value, Module['i32']); }, 'or': function(offset, ptr, value) { return Module['_BinaryenAtomicRMW'](module, Module['AtomicRMWOr'], 2, offset, ptr, value, Module['i32']); }, 'xor': function(offset, ptr, value) { return Module['_BinaryenAtomicRMW'](module, Module['AtomicRMWXor'], 2, offset, ptr, value, Module['i32']); }, 'xchg': function(offset, ptr, value) { return Module['_BinaryenAtomicRMW'](module, Module['AtomicRMWXchg'], 2, offset, ptr, value, Module['i32']); }, 'cmpxchg': function(offset, ptr, expected, replacement) { return Module['_BinaryenAtomicCmpxchg'](module, 2, offset, ptr, expected, replacement, Module['i32']) }, }, 'wait': function(ptr, expected, timeout) { return Module['_BinaryenAtomicWait'](module, ptr, expected, timeout, Module['i32']); } }, 'pop': function() { return Module['_BinaryenPop'](module, Module['i32']); } }; self['i64'] = { 'load': function(offset, align, ptr) { return Module['_BinaryenLoad'](module, 8, true, offset, align, Module['i64'], ptr); }, 'load8_s': function(offset, align, ptr) { return Module['_BinaryenLoad'](module, 1, true, offset, align, Module['i64'], ptr); }, 'load8_u': function(offset, align, ptr) { return Module['_BinaryenLoad'](module, 1, false, offset, align, Module['i64'], ptr); }, 'load16_s': function(offset, align, ptr) { return Module['_BinaryenLoad'](module, 2, true, offset, align, Module['i64'], ptr); }, 'load16_u': function(offset, align, ptr) { return Module['_BinaryenLoad'](module, 2, false, offset, align, Module['i64'], ptr); }, 'load32_s': function(offset, align, ptr) { return Module['_BinaryenLoad'](module, 4, true, offset, align, Module['i64'], ptr); }, 'load32_u': function(offset, align, ptr) { return Module['_BinaryenLoad'](module, 4, false, offset, align, Module['i64'], ptr); }, 'store': function(offset, align, ptr, value) { return Module['_BinaryenStore'](module, 8, offset, align, ptr, value, Module['i64']); }, 'store8': function(offset, align, ptr, value) { return Module['_BinaryenStore'](module, 1, offset, align, ptr, value, Module['i64']); }, 'store16': function(offset, align, ptr, value) { return Module['_BinaryenStore'](module, 2, offset, align, ptr, value, Module['i64']); }, 'store32': function(offset, align, ptr, value) { return Module['_BinaryenStore'](module, 4, offset, align, ptr, value, Module['i64']); }, 'const': function(x, y) { return preserveStack(function() { var tempLiteral = stackAlloc(sizeOfLiteral); Module['_BinaryenLiteralInt64'](tempLiteral, x, y); return Module['_BinaryenConst'](module, tempLiteral); }); }, 'clz': function(value) { return Module['_BinaryenUnary'](module, Module['ClzInt64'], value); }, 'ctz': function(value) { return Module['_BinaryenUnary'](module, Module['CtzInt64'], value); }, 'popcnt': function(value) { return Module['_BinaryenUnary'](module, Module['PopcntInt64'], value); }, 'eqz': function(value) { return Module['_BinaryenUnary'](module, Module['EqZInt64'], value); }, 'trunc_s': { 'f32': function(value) { return Module['_BinaryenUnary'](module, Module['TruncSFloat32ToInt64'], value); }, 'f64': function(value) { return Module['_BinaryenUnary'](module, Module['TruncSFloat64ToInt64'], value); }, }, 'trunc_u': { 'f32': function(value) { return Module['_BinaryenUnary'](module, Module['TruncUFloat32ToInt64'], value); }, 'f64': function(value) { return Module['_BinaryenUnary'](module, Module['TruncUFloat64ToInt64'], value); }, }, 'trunc_s_sat': { 'f32': function(value) { return Module['_BinaryenUnary'](module, Module['TruncSatSFloat32ToInt64'], value); }, 'f64': function(value) { return Module['_BinaryenUnary'](module, Module['TruncSatSFloat64ToInt64'], value); }, }, 'trunc_u_sat': { 'f32': function(value) { return Module['_BinaryenUnary'](module, Module['TruncSatUFloat32ToInt64'], value); }, 'f64': function(value) { return Module['_BinaryenUnary'](module, Module['TruncSatUFloat64ToInt64'], value); }, }, 'reinterpret': function(value) { return Module['_BinaryenUnary'](module, Module['ReinterpretFloat64'], value); }, 'extend8_s': function(value) { return Module['_BinaryenUnary'](module, Module['ExtendS8Int64'], value); }, 'extend16_s': function(value) { return Module['_BinaryenUnary'](module, Module['ExtendS16Int64'], value); }, 'extend32_s': function(value) { return Module['_BinaryenUnary'](module, Module['ExtendS32Int64'], value); }, 'extend_s': function(value) { return Module['_BinaryenUnary'](module, Module['ExtendSInt32'], value); }, 'extend_u': function(value) { return Module['_BinaryenUnary'](module, Module['ExtendUInt32'], value); }, 'add': function(left, right) { return Module['_BinaryenBinary'](module, Module['AddInt64'], left, right); }, 'sub': function(left, right) { return Module['_BinaryenBinary'](module, Module['SubInt64'], left, right); }, 'mul': function(left, right) { return Module['_BinaryenBinary'](module, Module['MulInt64'], left, right); }, 'div_s': function(left, right) { return Module['_BinaryenBinary'](module, Module['DivSInt64'], left, right); }, 'div_u': function(left, right) { return Module['_BinaryenBinary'](module, Module['DivUInt64'], left, right); }, 'rem_s': function(left, right) { return Module['_BinaryenBinary'](module, Module['RemSInt64'], left, right); }, 'rem_u': function(left, right) { return Module['_BinaryenBinary'](module, Module['RemUInt64'], left, right); }, 'and': function(left, right) { return Module['_BinaryenBinary'](module, Module['AndInt64'], left, right); }, 'or': function(left, right) { return Module['_BinaryenBinary'](module, Module['OrInt64'], left, right); }, 'xor': function(left, right) { return Module['_BinaryenBinary'](module, Module['XorInt64'], left, right); }, 'shl': function(left, right) { return Module['_BinaryenBinary'](module, Module['ShlInt64'], left, right); }, 'shr_u': function(left, right) { return Module['_BinaryenBinary'](module, Module['ShrUInt64'], left, right); }, 'shr_s': function(left, right) { return Module['_BinaryenBinary'](module, Module['ShrSInt64'], left, right); }, 'rotl': function(left, right) { return Module['_BinaryenBinary'](module, Module['RotLInt64'], left, right); }, 'rotr': function(left, right) { return Module['_BinaryenBinary'](module, Module['RotRInt64'], left, right); }, 'eq': function(left, right) { return Module['_BinaryenBinary'](module, Module['EqInt64'], left, right); }, 'ne': function(left, right) { return Module['_BinaryenBinary'](module, Module['NeInt64'], left, right); }, 'lt_s': function(left, right) { return Module['_BinaryenBinary'](module, Module['LtSInt64'], left, right); }, 'lt_u': function(left, right) { return Module['_BinaryenBinary'](module, Module['LtUInt64'], left, right); }, 'le_s': function(left, right) { return Module['_BinaryenBinary'](module, Module['LeSInt64'], left, right); }, 'le_u': function(left, right) { return Module['_BinaryenBinary'](module, Module['LeUInt64'], left, right); }, 'gt_s': function(left, right) { return Module['_BinaryenBinary'](module, Module['GtSInt64'], left, right); }, 'gt_u': function(left, right) { return Module['_BinaryenBinary'](module, Module['GtUInt64'], left, right); }, 'ge_s': function(left, right) { return Module['_BinaryenBinary'](module, Module['GeSInt64'], left, right); }, 'ge_u': function(left, right) { return Module['_BinaryenBinary'](module, Module['GeUInt64'], left, right); }, 'atomic': { 'load': function(offset, ptr) { return Module['_BinaryenAtomicLoad'](module, 8, offset, Module['i64'], ptr); }, 'load8_u': function(offset, ptr) { return Module['_BinaryenAtomicLoad'](module, 1, offset, Module['i64'], ptr); }, 'load16_u': function(offset, ptr) { return Module['_BinaryenAtomicLoad'](module, 2, offset, Module['i64'], ptr); }, 'load32_u': function(offset, ptr) { return Module['_BinaryenAtomicLoad'](module, 4, offset, Module['i64'], ptr); }, 'store': function(offset, ptr, value) { return Module['_BinaryenAtomicStore'](module, 8, offset, ptr, value, Module['i64']); }, 'store8': function(offset, ptr, value) { return Module['_BinaryenAtomicStore'](module, 1, offset, ptr, value, Module['i64']); }, 'store16': function(offset, ptr, value) { return Module['_BinaryenAtomicStore'](module, 2, offset, ptr, value, Module['i64']); }, 'store32': function(offset, ptr, value) { return Module['_BinaryenAtomicStore'](module, 4, offset, ptr, value, Module['i64']); }, 'rmw': { 'add': function(offset, ptr, value) { return Module['_BinaryenAtomicRMW'](module, Module['AtomicRMWAdd'], 8, offset, ptr, value, Module['i64']); }, 'sub': function(offset, ptr, value) { return Module['_BinaryenAtomicRMW'](module, Module['AtomicRMWSub'], 8, offset, ptr, value, Module['i64']); }, 'and': function(offset, ptr, value) { return Module['_BinaryenAtomicRMW'](module, Module['AtomicRMWAnd'], 8, offset, ptr, value, Module['i64']); }, 'or': function(offset, ptr, value) { return Module['_BinaryenAtomicRMW'](module, Module['AtomicRMWOr'], 8, offset, ptr, value, Module['i64']); }, 'xor': function(offset, ptr, value) { return Module['_BinaryenAtomicRMW'](module, Module['AtomicRMWXor'], 8, offset, ptr, value, Module['i64']); }, 'xchg': function(offset, ptr, value) { return Module['_BinaryenAtomicRMW'](module, Module['AtomicRMWXchg'], 8, offset, ptr, value, Module['i64']); }, 'cmpxchg': function(offset, ptr, expected, replacement) { return Module['_BinaryenAtomicCmpxchg'](module, 8, offset, ptr, expected, replacement, Module['i64']) }, }, 'rmw8_u': { 'add': function(offset, ptr, value) { return Module['_BinaryenAtomicRMW'](module, Module['AtomicRMWAdd'], 1, offset, ptr, value, Module['i64']); }, 'sub': function(offset, ptr, value) { return Module['_BinaryenAtomicRMW'](module, Module['AtomicRMWSub'], 1, offset, ptr, value, Module['i64']); }, 'and': function(offset, ptr, value) { return Module['_BinaryenAtomicRMW'](module, Module['AtomicRMWAnd'], 1, offset, ptr, value, Module['i64']); }, 'or': function(offset, ptr, value) { return Module['_BinaryenAtomicRMW'](module, Module['AtomicRMWOr'], 1, offset, ptr, value, Module['i64']); }, 'xor': function(offset, ptr, value) { return Module['_BinaryenAtomicRMW'](module, Module['AtomicRMWXor'], 1, offset, ptr, value, Module['i64']); }, 'xchg': function(offset, ptr, value) { return Module['_BinaryenAtomicRMW'](module, Module['AtomicRMWXchg'], 1, offset, ptr, value, Module['i64']); }, 'cmpxchg': function(offset, ptr, expected, replacement) { return Module['_BinaryenAtomicCmpxchg'](module, 1, offset, ptr, expected, replacement, Module['i64']) }, }, 'rmw16_u': { 'add': function(offset, ptr, value) { return Module['_BinaryenAtomicRMW'](module, Module['AtomicRMWAdd'], 2, offset, ptr, value, Module['i64']); }, 'sub': function(offset, ptr, value) { return Module['_BinaryenAtomicRMW'](module, Module['AtomicRMWSub'], 2, offset, ptr, value, Module['i64']); }, 'and': function(offset, ptr, value) { return Module['_BinaryenAtomicRMW'](module, Module['AtomicRMWAnd'], 2, offset, ptr, value, Module['i64']); }, 'or': function(offset, ptr, value) { return Module['_BinaryenAtomicRMW'](module, Module['AtomicRMWOr'], 2, offset, ptr, value, Module['i64']); }, 'xor': function(offset, ptr, value) { return Module['_BinaryenAtomicRMW'](module, Module['AtomicRMWXor'], 2, offset, ptr, value, Module['i64']); }, 'xchg': function(offset, ptr, value) { return Module['_BinaryenAtomicRMW'](module, Module['AtomicRMWXchg'], 2, offset, ptr, value, Module['i64']); }, 'cmpxchg': function(offset, ptr, expected, replacement) { return Module['_BinaryenAtomicCmpxchg'](module, 2, offset, ptr, expected, replacement, Module['i64']) }, }, 'rmw32_u': { 'add': function(offset, ptr, value) { return Module['_BinaryenAtomicRMW'](module, Module['AtomicRMWAdd'], 4, offset, ptr, value, Module['i64']); }, 'sub': function(offset, ptr, value) { return Module['_BinaryenAtomicRMW'](module, Module['AtomicRMWSub'], 4, offset, ptr, value, Module['i64']); }, 'and': function(offset, ptr, value) { return Module['_BinaryenAtomicRMW'](module, Module['AtomicRMWAnd'], 4, offset, ptr, value, Module['i64']); }, 'or': function(offset, ptr, value) { return Module['_BinaryenAtomicRMW'](module, Module['AtomicRMWOr'], 4, offset, ptr, value, Module['i64']); }, 'xor': function(offset, ptr, value) { return Module['_BinaryenAtomicRMW'](module, Module['AtomicRMWXor'], 4, offset, ptr, value, Module['i64']); }, 'xchg': function(offset, ptr, value) { return Module['_BinaryenAtomicRMW'](module, Module['AtomicRMWXchg'], 4, offset, ptr, value, Module['i64']); }, 'cmpxchg': function(offset, ptr, expected, replacement) { return Module['_BinaryenAtomicCmpxchg'](module, 4, offset, ptr, expected, replacement, Module['i64']) }, }, 'wait': function(ptr, expected, timeout) { return Module['_BinaryenAtomicWait'](module, ptr, expected, timeout, Module['i64']); } }, 'pop': function() { return Module['_BinaryenPop'](module, Module['i64']); } }; self['f32'] = { 'load': function(offset, align, ptr) { return Module['_BinaryenLoad'](module, 4, true, offset, align, Module['f32'], ptr); }, 'store': function(offset, align, ptr, value) { return Module['_BinaryenStore'](module, 4, offset, align, ptr, value, Module['f32']); }, 'const': function(x) { return preserveStack(function() { var tempLiteral = stackAlloc(sizeOfLiteral); Module['_BinaryenLiteralFloat32'](tempLiteral, x); return Module['_BinaryenConst'](module, tempLiteral); }); }, 'const_bits': function(x) { return preserveStack(function() { var tempLiteral = stackAlloc(sizeOfLiteral); Module['_BinaryenLiteralFloat32Bits'](tempLiteral, x); return Module['_BinaryenConst'](module, tempLiteral); }); }, 'neg': function(value) { return Module['_BinaryenUnary'](module, Module['NegFloat32'], value); }, 'abs': function(value) { return Module['_BinaryenUnary'](module, Module['AbsFloat32'], value); }, 'ceil': function(value) { return Module['_BinaryenUnary'](module, Module['CeilFloat32'], value); }, 'floor': function(value) { return Module['_BinaryenUnary'](module, Module['FloorFloat32'], value); }, 'trunc': function(value) { return Module['_BinaryenUnary'](module, Module['TruncFloat32'], value); }, 'nearest': function(value) { return Module['_BinaryenUnary'](module, Module['NearestFloat32'], value); }, 'sqrt': function(value) { return Module['_BinaryenUnary'](module, Module['SqrtFloat32'], value); }, 'reinterpret': function(value) { return Module['_BinaryenUnary'](module, Module['ReinterpretInt32'], value); }, 'convert_s': { 'i32': function(value) { return Module['_BinaryenUnary'](module, Module['ConvertSInt32ToFloat32'], value); }, 'i64': function(value) { return Module['_BinaryenUnary'](module, Module['ConvertSInt64ToFloat32'], value); }, }, 'convert_u': { 'i32': function(value) { return Module['_BinaryenUnary'](module, Module['ConvertUInt32ToFloat32'], value); }, 'i64': function(value) { return Module['_BinaryenUnary'](module, Module['ConvertUInt64ToFloat32'], value); }, }, 'demote': function(value) { return Module['_BinaryenUnary'](module, Module['DemoteFloat64'], value); }, 'add': function(left, right) { return Module['_BinaryenBinary'](module, Module['AddFloat32'], left, right); }, 'sub': function(left, right) { return Module['_BinaryenBinary'](module, Module['SubFloat32'], left, right); }, 'mul': function(left, right) { return Module['_BinaryenBinary'](module, Module['MulFloat32'], left, right); }, 'div': function(left, right) { return Module['_BinaryenBinary'](module, Module['DivFloat32'], left, right); }, 'copysign': function(left, right) { return Module['_BinaryenBinary'](module, Module['CopySignFloat32'], left, right); }, 'min': function(left, right) { return Module['_BinaryenBinary'](module, Module['MinFloat32'], left, right); }, 'max': function(left, right) { return Module['_BinaryenBinary'](module, Module['MaxFloat32'], left, right); }, 'eq': function(left, right) { return Module['_BinaryenBinary'](module, Module['EqFloat32'], left, right); }, 'ne': function(left, right) { return Module['_BinaryenBinary'](module, Module['NeFloat32'], left, right); }, 'lt': function(left, right) { return Module['_BinaryenBinary'](module, Module['LtFloat32'], left, right); }, 'le': function(left, right) { return Module['_BinaryenBinary'](module, Module['LeFloat32'], left, right); }, 'gt': function(left, right) { return Module['_BinaryenBinary'](module, Module['GtFloat32'], left, right); }, 'ge': function(left, right) { return Module['_BinaryenBinary'](module, Module['GeFloat32'], left, right); }, 'pop': function() { return Module['_BinaryenPop'](module, Module['f32']); } }; self['f64'] = { 'load': function(offset, align, ptr) { return Module['_BinaryenLoad'](module, 8, true, offset, align, Module['f64'], ptr); }, 'store': function(offset, align, ptr, value) { return Module['_BinaryenStore'](module, 8, offset, align, ptr, value, Module['f64']); }, 'const': function(x) { return preserveStack(function() { var tempLiteral = stackAlloc(sizeOfLiteral); Module['_BinaryenLiteralFloat64'](tempLiteral, x); return Module['_BinaryenConst'](module, tempLiteral); }); }, 'const_bits': function(x, y) { return preserveStack(function() { var tempLiteral = stackAlloc(sizeOfLiteral); Module['_BinaryenLiteralFloat64Bits'](tempLiteral, x, y); return Module['_BinaryenConst'](module, tempLiteral); }); }, 'neg': function(value) { return Module['_BinaryenUnary'](module, Module['NegFloat64'], value); }, 'abs': function(value) { return Module['_BinaryenUnary'](module, Module['AbsFloat64'], value); }, 'ceil': function(value) { return Module['_BinaryenUnary'](module, Module['CeilFloat64'], value); }, 'floor': function(value) { return Module['_BinaryenUnary'](module, Module['FloorFloat64'], value); }, 'trunc': function(value) { return Module['_BinaryenUnary'](module, Module['TruncFloat64'], value); }, 'nearest': function(value) { return Module['_BinaryenUnary'](module, Module['NearestFloat64'], value); }, 'sqrt': function(value) { return Module['_BinaryenUnary'](module, Module['SqrtFloat64'], value); }, 'reinterpret': function(value) { return Module['_BinaryenUnary'](module, Module['ReinterpretInt64'], value); }, 'convert_s': { 'i32': function(value) { return Module['_BinaryenUnary'](module, Module['ConvertSInt32ToFloat64'], value); }, 'i64': function(value) { return Module['_BinaryenUnary'](module, Module['ConvertSInt64ToFloat64'], value); }, }, 'convert_u': { 'i32': function(value) { return Module['_BinaryenUnary'](module, Module['ConvertUInt32ToFloat64'], value); }, 'i64': function(value) { return Module['_BinaryenUnary'](module, Module['ConvertUInt64ToFloat64'], value); }, }, 'promote': function(value) { return Module['_BinaryenUnary'](module, Module['PromoteFloat32'], value); }, 'add': function(left, right) { return Module['_BinaryenBinary'](module, Module['AddFloat64'], left, right); }, 'sub': function(left, right) { return Module['_BinaryenBinary'](module, Module['SubFloat64'], left, right); }, 'mul': function(left, right) { return Module['_BinaryenBinary'](module, Module['MulFloat64'], left, right); }, 'div': function(left, right) { return Module['_BinaryenBinary'](module, Module['DivFloat64'], left, right); }, 'copysign': function(left, right) { return Module['_BinaryenBinary'](module, Module['CopySignFloat64'], left, right); }, 'min': function(left, right) { return Module['_BinaryenBinary'](module, Module['MinFloat64'], left, right); }, 'max': function(left, right) { return Module['_BinaryenBinary'](module, Module['MaxFloat64'], left, right); }, 'eq': function(left, right) { return Module['_BinaryenBinary'](module, Module['EqFloat64'], left, right); }, 'ne': function(left, right) { return Module['_BinaryenBinary'](module, Module['NeFloat64'], left, right); }, 'lt': function(left, right) { return Module['_BinaryenBinary'](module, Module['LtFloat64'], left, right); }, 'le': function(left, right) { return Module['_BinaryenBinary'](module, Module['LeFloat64'], left, right); }, 'gt': function(left, right) { return Module['_BinaryenBinary'](module, Module['GtFloat64'], left, right); }, 'ge': function(left, right) { return Module['_BinaryenBinary'](module, Module['GeFloat64'], left, right); }, 'pop': function() { return Module['_BinaryenPop'](module, Module['f64']); } }; self['v128'] = { 'load': function(offset, align, ptr) { return Module['_BinaryenLoad'](module, 16, false, offset, align, Module['v128'], ptr); }, 'store': function(offset, align, ptr, value) { return Module['_BinaryenStore'](module, 16, offset, align, ptr, value, Module['v128']); }, 'const': function(i8s) { return preserveStack(function() { var tempLiteral = stackAlloc(sizeOfLiteral); Module['_BinaryenLiteralVec128'](tempLiteral, i8sToStack(i8s)); return Module['_BinaryenConst'](module, tempLiteral); }); }, 'not': function(value) { return Module['_BinaryenUnary'](module, Module['NotVec128'], value); }, 'and': function(left, right) { return Module['_BinaryenBinary'](module, Module['AndVec128'], left, right); }, 'or': function(left, right) { return Module['_BinaryenBinary'](module, Module['OrVec128'], left, right); }, 'xor': function(left, right) { return Module['_BinaryenBinary'](module, Module['XorVec128'], left, right); }, 'andnot': function(left, right) { return Module['_BinaryenBinary'](module, Module['AndNotVec128'], left, right); }, 'bitselect': function(left, right, cond) { return Module['_BinaryenSIMDTernary'](module, Module['BitselectVec128'], left, right, cond); }, 'pop': function() { return Module['_BinaryenPop'](module, Module['v128']); } }; self['i8x16'] = { 'splat': function(value) { return Module['_BinaryenUnary'](module, Module['SplatVecI8x16'], value); }, 'extract_lane_s': function(vec, index) { return Module['_BinaryenSIMDExtract'](module, Module['ExtractLaneSVecI8x16'], vec, index); }, 'extract_lane_u': function(vec, index) { return Module['_BinaryenSIMDExtract'](module, Module['ExtractLaneUVecI8x16'], vec, index); }, 'replace_lane': function(vec, index, value) { return Module['_BinaryenSIMDReplace'](module, Module['ReplaceLaneVecI8x16'], vec, index, value); }, 'eq': function(left, right) { return Module['_BinaryenBinary'](module, Module['EqVecI8x16'], left, right); }, 'ne': function(left, right) { return Module['_BinaryenBinary'](module, Module['NeVecI8x16'], left, right); }, 'lt_s': function(left, right) { return Module['_BinaryenBinary'](module, Module['LtSVecI8x16'], left, right); }, 'lt_u': function(left, right) { return Module['_BinaryenBinary'](module, Module['LtUVecI8x16'], left, right); }, 'gt_s': function(left, right) { return Module['_BinaryenBinary'](module, Module['GtSVecI8x16'], left, right); }, 'gt_u': function(left, right) { return Module['_BinaryenBinary'](module, Module['GtUVecI8x16'], left, right); }, 'le_s': function(left, right) { return Module['_BinaryenBinary'](module, Module['LeSVecI8x16'], left, right); }, 'le_u': function(left, right) { return Module['_BinaryenBinary'](module, Module['LeUVecI8x16'], left, right); }, 'ge_s': function(left, right) { return Module['_BinaryenBinary'](module, Module['GeSVecI8x16'], left, right); }, 'ge_u': function(left, right) { return Module['_BinaryenBinary'](module, Module['GeUVecI8x16'], left, right); }, 'neg': function(value) { return Module['_BinaryenUnary'](module, Module['NegVecI8x16'], value); }, 'any_true': function(value) { return Module['_BinaryenUnary'](module, Module['AnyTrueVecI8x16'], value); }, 'all_true': function(value) { return Module['_BinaryenUnary'](module, Module['AllTrueVecI8x16'], value); }, 'shl': function(vec, shift) { return Module['_BinaryenSIMDShift'](module, Module['ShlVecI8x16'], vec, shift); }, 'shr_s': function(vec, shift) { return Module['_BinaryenSIMDShift'](module, Module['ShrSVecI8x16'], vec, shift); }, 'shr_u': function(vec, shift) { return Module['_BinaryenSIMDShift'](module, Module['ShrUVecI8x16'], vec, shift); }, 'add': function(left, right) { return Module['_BinaryenBinary'](module, Module['AddVecI8x16'], left, right); }, 'add_saturate_s': function(left, right) { return Module['_BinaryenBinary'](module, Module['AddSatSVecI8x16'], left, right); }, 'add_saturate_u': function(left, right) { return Module['_BinaryenBinary'](module, Module['AddSatUVecI8x16'], left, right); }, 'sub': function(left, right) { return Module['_BinaryenBinary'](module, Module['SubVecI8x16'], left, right); }, 'sub_saturate_s': function(left, right) { return Module['_BinaryenBinary'](module, Module['SubSatSVecI8x16'], left, right); }, 'sub_saturate_u': function(left, right) { return Module['_BinaryenBinary'](module, Module['SubSatUVecI8x16'], left, right); }, 'mul': function(left, right) { return Module['_BinaryenBinary'](module, Module['MulVecI8x16'], left, right); }, 'min_s': function(left, right) { return Module['_BinaryenBinary'](module, Module['MinSVecI8x16'], left, right); }, 'min_u': function(left, right) { return Module['_BinaryenBinary'](module, Module['MinUVecI8x16'], left, right); }, 'max_s': function(left, right) { return Module['_BinaryenBinary'](module, Module['MaxSVecI8x16'], left, right); }, 'max_u': function(left, right) { return Module['_BinaryenBinary'](module, Module['MaxUVecI8x16'], left, right); }, 'avgr_u': function(left, right) { return Module['_BinaryenBinary'](module, Module['AvgrUVecI8x16'], left, right); }, 'narrow_i16x8_s': function(left, right) { return Module['_BinaryenBinary'](module, Module['NarrowSVecI16x8ToVecI8x16'], left, right); }, 'narrow_i16x8_u': function(left, right) { return Module['_BinaryenBinary'](module, Module['NarrowUVecI16x8ToVecI8x16'], left, right); }, }; self['i16x8'] = { 'splat': function(value) { return Module['_BinaryenUnary'](module, Module['SplatVecI16x8'], value); }, 'extract_lane_s': function(vec, index) { return Module['_BinaryenSIMDExtract'](module, Module['ExtractLaneSVecI16x8'], vec, index); }, 'extract_lane_u': function(vec, index) { return Module['_BinaryenSIMDExtract'](module, Module['ExtractLaneUVecI16x8'], vec, index); }, 'replace_lane': function(vec, index, value) { return Module['_BinaryenSIMDReplace'](module, Module['ReplaceLaneVecI16x8'], vec, index, value); }, 'eq': function(left, right) { return Module['_BinaryenBinary'](module, Module['EqVecI16x8'], left, right); }, 'ne': function(left, right) { return Module['_BinaryenBinary'](module, Module['NeVecI16x8'], left, right); }, 'lt_s': function(left, right) { return Module['_BinaryenBinary'](module, Module['LtSVecI16x8'], left, right); }, 'lt_u': function(left, right) { return Module['_BinaryenBinary'](module, Module['LtUVecI16x8'], left, right); }, 'gt_s': function(left, right) { return Module['_BinaryenBinary'](module, Module['GtSVecI16x8'], left, right); }, 'gt_u': function(left, right) { return Module['_BinaryenBinary'](module, Module['GtUVecI16x8'], left, right); }, 'le_s': function(left, right) { return Module['_BinaryenBinary'](module, Module['LeSVecI16x8'], left, right); }, 'le_u': function(left, right) { return Module['_BinaryenBinary'](module, Module['LeUVecI16x8'], left, right); }, 'ge_s': function(left, right) { return Module['_BinaryenBinary'](module, Module['GeSVecI16x8'], left, right); }, 'ge_u': function(left, right) { return Module['_BinaryenBinary'](module, Module['GeUVecI16x8'], left, right); }, 'neg': function(value) { return Module['_BinaryenUnary'](module, Module['NegVecI16x8'], value); }, 'any_true': function(value) { return Module['_BinaryenUnary'](module, Module['AnyTrueVecI16x8'], value); }, 'all_true': function(value) { return Module['_BinaryenUnary'](module, Module['AllTrueVecI16x8'], value); }, 'shl': function(vec, shift) { return Module['_BinaryenSIMDShift'](module, Module['ShlVecI16x8'], vec, shift); }, 'shr_s': function(vec, shift) { return Module['_BinaryenSIMDShift'](module, Module['ShrSVecI16x8'], vec, shift); }, 'shr_u': function(vec, shift) { return Module['_BinaryenSIMDShift'](module, Module['ShrUVecI16x8'], vec, shift); }, 'add': function(left, right) { return Module['_BinaryenBinary'](module, Module['AddVecI16x8'], left, right); }, 'add_saturate_s': function(left, right) { return Module['_BinaryenBinary'](module, Module['AddSatSVecI16x8'], left, right); }, 'add_saturate_u': function(left, right) { return Module['_BinaryenBinary'](module, Module['AddSatUVecI16x8'], left, right); }, 'sub': function(left, right) { return Module['_BinaryenBinary'](module, Module['SubVecI16x8'], left, right); }, 'sub_saturate_s': function(left, right) { return Module['_BinaryenBinary'](module, Module['SubSatSVecI16x8'], left, right); }, 'sub_saturate_u': function(left, right) { return Module['_BinaryenBinary'](module, Module['SubSatUVecI16x8'], left, right); }, 'mul': function(left, right) { return Module['_BinaryenBinary'](module, Module['MulVecI16x8'], left, right); }, 'min_s': function(left, right) { return Module['_BinaryenBinary'](module, Module['MinSVecI16x8'], left, right); }, 'min_u': function(left, right) { return Module['_BinaryenBinary'](module, Module['MinUVecI16x8'], left, right); }, 'max_s': function(left, right) { return Module['_BinaryenBinary'](module, Module['MaxSVecI16x8'], left, right); }, 'max_u': function(left, right) { return Module['_BinaryenBinary'](module, Module['MaxUVecI16x8'], left, right); }, 'avgr_u': function(left, right) { return Module['_BinaryenBinary'](module, Module['AvgrUVecI16x8'], left, right); }, 'narrow_i32x4_s': function(left, right) { return Module['_BinaryenBinary'](module, Module['NarrowSVecI32x4ToVecI16x8'], left, right); }, 'narrow_i32x4_u': function(left, right) { return Module['_BinaryenBinary'](module, Module['NarrowUVecI32x4ToVecI16x8'], left, right); }, 'widen_low_i8x16_s': function(value) { return Module['_BinaryenUnary'](module, Module['WidenLowSVecI8x16ToVecI16x8'], value); }, 'widen_high_i8x16_s': function(value) { return Module['_BinaryenUnary'](module, Module['WidenHighSVecI8x16ToVecI16x8'], value); }, 'widen_low_i8x16_u': function(value) { return Module['_BinaryenUnary'](module, Module['WidenLowUVecI8x16ToVecI16x8'], value); }, 'widen_high_i8x16_u': function(value) { return Module['_BinaryenUnary'](module, Module['WidenHighUVecI8x16ToVecI16x8'], value); }, 'load8x8_s': function(offset, align, ptr) { return Module['_BinaryenSIMDLoad'](module, Module['LoadExtSVec8x8ToVecI16x8'], offset, align, ptr); }, 'load8x8_u': function(offset, align, ptr) { return Module['_BinaryenSIMDLoad'](module, Module['LoadExtUVec8x8ToVecI16x8'], offset, align, ptr); }, }; self['i32x4'] = { 'splat': function(value) { return Module['_BinaryenUnary'](module, Module['SplatVecI32x4'], value); }, 'extract_lane': function(vec, index) { return Module['_BinaryenSIMDExtract'](module, Module['ExtractLaneVecI32x4'], vec, index); }, 'replace_lane': function(vec, index, value) { return Module['_BinaryenSIMDReplace'](module, Module['ReplaceLaneVecI32x4'], vec, index, value); }, 'eq': function(left, right) { return Module['_BinaryenBinary'](module, Module['EqVecI32x4'], left, right); }, 'ne': function(left, right) { return Module['_BinaryenBinary'](module, Module['NeVecI32x4'], left, right); }, 'lt_s': function(left, right) { return Module['_BinaryenBinary'](module, Module['LtSVecI32x4'], left, right); }, 'lt_u': function(left, right) { return Module['_BinaryenBinary'](module, Module['LtUVecI32x4'], left, right); }, 'gt_s': function(left, right) { return Module['_BinaryenBinary'](module, Module['GtSVecI32x4'], left, right); }, 'gt_u': function(left, right) { return Module['_BinaryenBinary'](module, Module['GtUVecI32x4'], left, right); }, 'le_s': function(left, right) { return Module['_BinaryenBinary'](module, Module['LeSVecI32x4'], left, right); }, 'le_u': function(left, right) { return Module['_BinaryenBinary'](module, Module['LeUVecI32x4'], left, right); }, 'ge_s': function(left, right) { return Module['_BinaryenBinary'](module, Module['GeSVecI32x4'], left, right); }, 'ge_u': function(left, right) { return Module['_BinaryenBinary'](module, Module['GeUVecI32x4'], left, right); }, 'neg': function(value) { return Module['_BinaryenUnary'](module, Module['NegVecI32x4'], value); }, 'any_true': function(value) { return Module['_BinaryenUnary'](module, Module['AnyTrueVecI32x4'], value); }, 'all_true': function(value) { return Module['_BinaryenUnary'](module, Module['AllTrueVecI32x4'], value); }, 'shl': function(vec, shift) { return Module['_BinaryenSIMDShift'](module, Module['ShlVecI32x4'], vec, shift); }, 'shr_s': function(vec, shift) { return Module['_BinaryenSIMDShift'](module, Module['ShrSVecI32x4'], vec, shift); }, 'shr_u': function(vec, shift) { return Module['_BinaryenSIMDShift'](module, Module['ShrUVecI32x4'], vec, shift); }, 'add': function(left, right) { return Module['_BinaryenBinary'](module, Module['AddVecI32x4'], left, right); }, 'sub': function(left, right) { return Module['_BinaryenBinary'](module, Module['SubVecI32x4'], left, right); }, 'mul': function(left, right) { return Module['_BinaryenBinary'](module, Module['MulVecI32x4'], left, right); }, 'min_s': function(left, right) { return Module['_BinaryenBinary'](module, Module['MinSVecI32x4'], left, right); }, 'min_u': function(left, right) { return Module['_BinaryenBinary'](module, Module['MinUVecI32x4'], left, right); }, 'max_s': function(left, right) { return Module['_BinaryenBinary'](module, Module['MaxSVecI32x4'], left, right); }, 'max_u': function(left, right) { return Module['_BinaryenBinary'](module, Module['MaxUVecI32x4'], left, right); }, 'dot_i16x8_s': function(left, right) { return Module['_BinaryenBinary'](module, Module['DotSVecI16x8ToVecI32x4'], left, right); }, 'trunc_sat_f32x4_s': function(value) { return Module['_BinaryenUnary'](module, Module['TruncSatSVecF32x4ToVecI32x4'], value); }, 'trunc_sat_f32x4_u': function(value) { return Module['_BinaryenUnary'](module, Module['TruncSatUVecF32x4ToVecI32x4'], value); }, 'widen_low_i16x8_s': function(value) { return Module['_BinaryenUnary'](module, Module['WidenLowSVecI16x8ToVecI32x4'], value); }, 'widen_high_i16x8_s': function(value) { return Module['_BinaryenUnary'](module, Module['WidenHighSVecI16x8ToVecI32x4'], value); }, 'widen_low_i16x8_u': function(value) { return Module['_BinaryenUnary'](module, Module['WidenLowUVecI16x8ToVecI32x4'], value); }, 'widen_high_i16x8_u': function(value) { return Module['_BinaryenUnary'](module, Module['WidenHighUVecI16x8ToVecI32x4'], value); }, 'load16x4_s': function(offset, align, ptr) { return Module['_BinaryenSIMDLoad'](module, Module['LoadExtSVec16x4ToVecI32x4'], offset, align, ptr); }, 'load16x4_u': function(offset, align, ptr) { return Module['_BinaryenSIMDLoad'](module, Module['LoadExtUVec16x4ToVecI32x4'], offset, align, ptr); }, }; self['i64x2'] = { 'splat': function(value) { return Module['_BinaryenUnary'](module, Module['SplatVecI64x2'], value); }, 'extract_lane': function(vec, index) { return Module['_BinaryenSIMDExtract'](module, Module['ExtractLaneVecI64x2'], vec, index); }, 'replace_lane': function(vec, index, value) { return Module['_BinaryenSIMDReplace'](module, Module['ReplaceLaneVecI64x2'], vec, index, value); }, 'neg': function(value) { return Module['_BinaryenUnary'](module, Module['NegVecI64x2'], value); }, 'any_true': function(value) { return Module['_BinaryenUnary'](module, Module['AnyTrueVecI64x2'], value); }, 'all_true': function(value) { return Module['_BinaryenUnary'](module, Module['AllTrueVecI64x2'], value); }, 'shl': function(vec, shift) { return Module['_BinaryenSIMDShift'](module, Module['ShlVecI64x2'], vec, shift); }, 'shr_s': function(vec, shift) { return Module['_BinaryenSIMDShift'](module, Module['ShrSVecI64x2'], vec, shift); }, 'shr_u': function(vec, shift) { return Module['_BinaryenSIMDShift'](module, Module['ShrUVecI64x2'], vec, shift); }, 'add': function(left, right) { return Module['_BinaryenBinary'](module, Module['AddVecI64x2'], left, right); }, 'sub': function(left, right) { return Module['_BinaryenBinary'](module, Module['SubVecI64x2'], left, right); }, 'trunc_sat_f64x2_s': function(value) { return Module['_BinaryenUnary'](module, Module['TruncSatSVecF64x2ToVecI64x2'], value); }, 'trunc_sat_f64x2_u': function(value) { return Module['_BinaryenUnary'](module, Module['TruncSatUVecF64x2ToVecI64x2'], value); }, 'load32x2_s': function(offset, align, ptr) { return Module['_BinaryenSIMDLoad'](module, Module['LoadExtSVec32x2ToVecI64x2'], offset, align, ptr); }, 'load32x2_u': function(offset, align, ptr) { return Module['_BinaryenSIMDLoad'](module, Module['LoadExtUVec32x2ToVecI64x2'], offset, align, ptr); }, }; self['f32x4'] = { 'splat': function(value) { return Module['_BinaryenUnary'](module, Module['SplatVecF32x4'], value); }, 'extract_lane': function(vec, index) { return Module['_BinaryenSIMDExtract'](module, Module['ExtractLaneVecF32x4'], vec, index); }, 'replace_lane': function(vec, index, value) { return Module['_BinaryenSIMDReplace'](module, Module['ReplaceLaneVecF32x4'], vec, index, value); }, 'eq': function(left, right) { return Module['_BinaryenBinary'](module, Module['EqVecF32x4'], left, right); }, 'ne': function(left, right) { return Module['_BinaryenBinary'](module, Module['NeVecF32x4'], left, right); }, 'lt': function(left, right) { return Module['_BinaryenBinary'](module, Module['LtVecF32x4'], left, right); }, 'gt': function(left, right) { return Module['_BinaryenBinary'](module, Module['GtVecF32x4'], left, right); }, 'le': function(left, right) { return Module['_BinaryenBinary'](module, Module['LeVecF32x4'], left, right); }, 'ge': function(left, right) { return Module['_BinaryenBinary'](module, Module['GeVecF32x4'], left, right); }, 'abs': function(value) { return Module['_BinaryenUnary'](module, Module['AbsVecF32x4'], value); }, 'neg': function(value) { return Module['_BinaryenUnary'](module, Module['NegVecF32x4'], value); }, 'sqrt': function(value) { return Module['_BinaryenUnary'](module, Module['SqrtVecF32x4'], value); }, 'qfma': function(a, b, c) { return Module['_BinaryenSIMDTernary'](module, Module['QFMAVecF32x4'], a, b, c); }, 'qfms': function(a, b, c) { return Module['_BinaryenSIMDTernary'](module, Module['QFMSVecF32x4'], a, b, c); }, 'add': function(left, right) { return Module['_BinaryenBinary'](module, Module['AddVecF32x4'], left, right); }, 'sub': function(left, right) { return Module['_BinaryenBinary'](module, Module['SubVecF32x4'], left, right); }, 'mul': function(left, right) { return Module['_BinaryenBinary'](module, Module['MulVecF32x4'], left, right); }, 'div': function(left, right) { return Module['_BinaryenBinary'](module, Module['DivVecF32x4'], left, right); }, 'min': function(left, right) { return Module['_BinaryenBinary'](module, Module['MinVecF32x4'], left, right); }, 'max': function(left, right) { return Module['_BinaryenBinary'](module, Module['MaxVecF32x4'], left, right); }, 'convert_i32x4_s': function(value) { return Module['_BinaryenUnary'](module, Module['ConvertSVecI32x4ToVecF32x4'], value); }, 'convert_i32x4_u': function(value) { return Module['_BinaryenUnary'](module, Module['ConvertUVecI32x4ToVecF32x4'], value); }, }; self['f64x2'] = { 'splat': function(value) { return Module['_BinaryenUnary'](module, Module['SplatVecF64x2'], value); }, 'extract_lane': function(vec, index) { return Module['_BinaryenSIMDExtract'](module, Module['ExtractLaneVecF64x2'], vec, index); }, 'replace_lane': function(vec, index, value) { return Module['_BinaryenSIMDReplace'](module, Module['ReplaceLaneVecF64x2'], vec, index, value); }, 'eq': function(left, right) { return Module['_BinaryenBinary'](module, Module['EqVecF64x2'], left, right); }, 'ne': function(left, right) { return Module['_BinaryenBinary'](module, Module['NeVecF64x2'], left, right); }, 'lt': function(left, right) { return Module['_BinaryenBinary'](module, Module['LtVecF64x2'], left, right); }, 'gt': function(left, right) { return Module['_BinaryenBinary'](module, Module['GtVecF64x2'], left, right); }, 'le': function(left, right) { return Module['_BinaryenBinary'](module, Module['LeVecF64x2'], left, right); }, 'ge': function(left, right) { return Module['_BinaryenBinary'](module, Module['GeVecF64x2'], left, right); }, 'abs': function(value) { return Module['_BinaryenUnary'](module, Module['AbsVecF64x2'], value); }, 'neg': function(value) { return Module['_BinaryenUnary'](module, Module['NegVecF64x2'], value); }, 'sqrt': function(value) { return Module['_BinaryenUnary'](module, Module['SqrtVecF64x2'], value); }, 'qfma': function(a, b, c) { return Module['_BinaryenSIMDTernary'](module, Module['QFMAVecF64x2'], a, b, c); }, 'qfms': function(a, b, c) { return Module['_BinaryenSIMDTernary'](module, Module['QFMSVecF64x2'], a, b, c); }, 'add': function(left, right) { return Module['_BinaryenBinary'](module, Module['AddVecF64x2'], left, right); }, 'sub': function(left, right) { return Module['_BinaryenBinary'](module, Module['SubVecF64x2'], left, right); }, 'mul': function(left, right) { return Module['_BinaryenBinary'](module, Module['MulVecF64x2'], left, right); }, 'div': function(left, right) { return Module['_BinaryenBinary'](module, Module['DivVecF64x2'], left, right); }, 'min': function(left, right) { return Module['_BinaryenBinary'](module, Module['MinVecF64x2'], left, right); }, 'max': function(left, right) { return Module['_BinaryenBinary'](module, Module['MaxVecF64x2'], left, right); }, 'convert_i64x2_s': function(value) { return Module['_BinaryenUnary'](module, Module['ConvertSVecI64x2ToVecF64x2'], value); }, 'convert_i64x2_u': function(value) { return Module['_BinaryenUnary'](module, Module['ConvertUVecI64x2ToVecF64x2'], value); }, }; self['v8x16'] = { 'shuffle': function(left, right, mask) { return preserveStack(function() { return Module['_BinaryenSIMDShuffle'](module, left, right, i8sToStack(mask)); }); }, 'swizzle': function(left, right) { return Module['_BinaryenBinary'](module, Module['SwizzleVec8x16'], left, right); }, 'load_splat': function(offset, align, ptr) { return Module['_BinaryenSIMDLoad'](module, Module['LoadSplatVec8x16'], offset, align, ptr); }, }; self['v16x8'] = { 'load_splat': function(offset, align, ptr) { return Module['_BinaryenSIMDLoad'](module, Module['LoadSplatVec16x8'], offset, align, ptr); }, }; self['v32x4'] = { 'load_splat': function(offset, align, ptr) { return Module['_BinaryenSIMDLoad'](module, Module['LoadSplatVec32x4'], offset, align, ptr); }, }; self['v64x2'] = { 'load_splat': function(offset, align, ptr) { return Module['_BinaryenSIMDLoad'](module, Module['LoadSplatVec64x2'], offset, align, ptr); }, }; self['funcref'] = { 'pop': function() { return Module['_BinaryenPop'](module, Module['funcref']); } }; self['anyref'] = { 'pop': function() { return Module['_BinaryenPop'](module, Module['anyref']); } }; self['nullref'] = { 'pop': function() { return Module['_BinaryenPop'](module, Module['nullref']); } }; self['exnref'] = { 'pop': function() { return Module['_BinaryenPop'](module, Module['exnref']); } }; self['ref'] = { 'null': function() { return Module['_BinaryenRefNull'](module); }, 'is_null': function(value) { return Module['_BinaryenRefIsNull'](module, value); }, 'func': function(func) { return preserveStack(function() { return Module['_BinaryenRefFunc'](module, strToStack(func)); }); } }; self['select'] = function(condition, ifTrue, ifFalse, type) { return Module['_BinaryenSelect']( module, condition, ifTrue, ifFalse, typeof type !== 'undefined' ? type : Module['auto']); }; self['drop'] = function(value) { return Module['_BinaryenDrop'](module, value); }; self['return'] = function(value) { return Module['_BinaryenReturn'](module, value); }; self['host'] = function(op, name, operands) { if (!operands) operands = []; return preserveStack(function() { return Module['_BinaryenHost'](module, op, strToStack(name), i32sToStack(operands), operands.length); }); }; self['nop'] = function() { return Module['_BinaryenNop'](module); }; self['unreachable'] = function() { return Module['_BinaryenUnreachable'](module); }; self['atomic'] = { 'notify': function(ptr, notifyCount) { return Module['_BinaryenAtomicNotify'](module, ptr, notifyCount); }, 'fence': function() { return Module['_BinaryenAtomicFence'](module); } }; self['try'] = function(body, catchBody) { return Module['_BinaryenTry'](module, body, catchBody); }; self['throw'] = function(event_, operands) { return preserveStack(function() { return Module['_BinaryenThrow'](module, strToStack(event_), i32sToStack(operands), operands.length); }); }; self['rethrow'] = function(exnref) { return Module['_BinaryenRethrow'](module, exnref); }; self['br_on_exn'] = function(label, event_, exnref) { return preserveStack(function() { return Module['_BinaryenBrOnExn'](module, strToStack(label), strToStack(event_), exnref); }); }; self['push'] = function(value) { return Module['_BinaryenPush'](module, value); }; // 'Module' operations self['addFunction'] = function(name, params, results, varTypes, body) { return preserveStack(function() { return Module['_BinaryenAddFunction'](module, strToStack(name), params, results, i32sToStack(varTypes), varTypes.length, body); }); }; self['getFunction'] = function(name) { return preserveStack(function() { return Module['_BinaryenGetFunction'](module, strToStack(name)); }); }; self['removeFunction'] = function(name) { return preserveStack(function() { return Module['_BinaryenRemoveFunction'](module, strToStack(name)); }); }; self['addGlobal'] = function(name, type, mutable, init) { return preserveStack(function() { return Module['_BinaryenAddGlobal'](module, strToStack(name), type, mutable, init); }); } self['getGlobal'] = function(name) { return preserveStack(function() { return Module['_BinaryenGetGlobal'](module, strToStack(name)); }); }; self['removeGlobal'] = function(name) { return preserveStack(function() { return Module['_BinaryenRemoveGlobal'](module, strToStack(name)); }); } self['addEvent'] = function(name, attribute, params, results) { return preserveStack(function() { return Module['_BinaryenAddEvent'](module, strToStack(name), attribute, params, results); }); }; self['getEvent'] = function(name) { return preserveStack(function() { return Module['_BinaryenGetEvent'](module, strToStack(name)); }); }; self['removeEvent'] = function(name) { return preserveStack(function() { return Module['_BinaryenRemoveEvent'](module, strToStack(name)); }); }; self['addFunctionImport'] = function(internalName, externalModuleName, externalBaseName, params, results) { return preserveStack(function() { return Module['_BinaryenAddFunctionImport'](module, strToStack(internalName), strToStack(externalModuleName), strToStack(externalBaseName), params, results); }); }; self['addTableImport'] = function(internalName, externalModuleName, externalBaseName) { return preserveStack(function() { return Module['_BinaryenAddTableImport'](module, strToStack(internalName), strToStack(externalModuleName), strToStack(externalBaseName)); }); }; self['addMemoryImport'] = function(internalName, externalModuleName, externalBaseName, shared) { return preserveStack(function() { return Module['_BinaryenAddMemoryImport'](module, strToStack(internalName), strToStack(externalModuleName), strToStack(externalBaseName), shared); }); }; self['addGlobalImport'] = function(internalName, externalModuleName, externalBaseName, globalType, mutable) { return preserveStack(function() { return Module['_BinaryenAddGlobalImport'](module, strToStack(internalName), strToStack(externalModuleName), strToStack(externalBaseName), globalType, mutable); }); }; self['addEventImport'] = function(internalName, externalModuleName, externalBaseName, attribute, params, results) { return preserveStack(function() { return Module['_BinaryenAddEventImport'](module, strToStack(internalName), strToStack(externalModuleName), strToStack(externalBaseName), attribute, params, results); }); }; self['addExport'] = // deprecated self['addFunctionExport'] = function(internalName, externalName) { return preserveStack(function() { return Module['_BinaryenAddFunctionExport'](module, strToStack(internalName), strToStack(externalName)); }); }; self['addTableExport'] = function(internalName, externalName) { return preserveStack(function() { return Module['_BinaryenAddTableExport'](module, strToStack(internalName), strToStack(externalName)); }); }; self['addMemoryExport'] = function(internalName, externalName) { return preserveStack(function() { return Module['_BinaryenAddMemoryExport'](module, strToStack(internalName), strToStack(externalName)); }); }; self['addGlobalExport'] = function(internalName, externalName) { return preserveStack(function() { return Module['_BinaryenAddGlobalExport'](module, strToStack(internalName), strToStack(externalName)); }); }; self['addEventExport'] = function(internalName, externalName) { return preserveStack(function() { return Module['_BinaryenAddEventExport'](module, strToStack(internalName), strToStack(externalName)); }); }; self['removeExport'] = function(externalName) { return preserveStack(function() { return Module['_BinaryenRemoveExport'](module, strToStack(externalName)); }); }; self['setFunctionTable'] = function(initial, maximum, funcNames, offset) { return preserveStack(function() { return Module['_BinaryenSetFunctionTable'](module, initial, maximum, i32sToStack(funcNames.map(strToStack)), funcNames.length, offset || self['i32']['const'](0) ); }); }; self['getFunctionTable'] = function() { return { 'imported': Boolean(Module['_BinaryenIsFunctionTableImported'](module)), 'segments': (function() { var arr = []; for (var i = 0, numSegments = Module['_BinaryenGetNumFunctionTableSegments'](module); i !== numSegments; ++i) { var seg = {'offset': Module['_BinaryenGetFunctionTableSegmentOffset'](module, i), 'names': []}; for (var j = 0, segmentLength = Module['_BinaryenGetFunctionTableSegmentLength'](module, i); j !== segmentLength; ++j) { var ptr = Module['_BinaryenGetFunctionTableSegmentData'](module, i, j); seg['names'].push(UTF8ToString(ptr)); } arr.push(seg); } return arr; })() }; }; self['setMemory'] = function(initial, maximum, exportName, segments, shared) { // segments are assumed to be { passive: bool, offset: expression ref, data: array of 8-bit data } if (!segments) segments = []; return preserveStack(function() { return Module['_BinaryenSetMemory']( module, initial, maximum, strToStack(exportName), i32sToStack( segments.map(function(segment) { return allocate(segment.data, 'i8', ALLOC_STACK); }) ), i8sToStack( segments.map(function(segment) { return segment.passive; }) ), i32sToStack( segments.map(function(segment) { return segment.offset; }) ), i32sToStack( segments.map(function(segment) { return segment.data.length; }) ), segments.length, shared ); }); }; self['getNumMemorySegments'] = function() { return Module['_BinaryenGetNumMemorySegments'](module); } self['getMemorySegmentInfoByIndex'] = function(id) { return { 'offset': Module['_BinaryenGetMemorySegmentByteOffset'](module, id), 'data': (function(){ var size = Module['_BinaryenGetMemorySegmentByteLength'](module, id); var ptr = _malloc(size); Module['_BinaryenCopyMemorySegmentData'](module, id, ptr); var res = new Uint8Array(size); res.set(new Uint8Array(buffer, ptr, size)); _free(ptr); return res.buffer; })(), 'passive': Boolean(Module['_BinaryenGetMemorySegmentPassive'](module, id)) }; } self['setStart'] = function(start) { return Module['_BinaryenSetStart'](module, start); }; self['getFeatures'] = function() { return Module['_BinaryenModuleGetFeatures'](module); }; self['setFeatures'] = function(features) { Module['_BinaryenModuleSetFeatures'](module, features); }; self['addCustomSection'] = function(name, contents) { return preserveStack(function() { return Module['_BinaryenAddCustomSection'](module, strToStack(name), i8sToStack(contents), contents.length); }); }; self['getNumExports'] = function() { return Module['_BinaryenGetNumExports'](module); } self['getExportByIndex'] = function(id) { return Module['_BinaryenGetExportByIndex'](module, id); } self['getNumFunctions'] = function() { return Module['_BinaryenGetNumFunctions'](module); } self['getFunctionByIndex'] = function(id) { return Module['_BinaryenGetFunctionByIndex'](module, id); } self['emitText'] = function() { var old = out; var ret = ''; out = function(x) { ret += x + '\n' }; Module['_BinaryenModulePrint'](module); out = old; return ret; }; self['emitStackIR'] = function(optimize) { self['runPasses'](['generate-stack-ir']); if (optimize) self['runPasses'](['optimize-stack-ir']); var old = out; var ret = ''; out = function(x) { ret += x + '\n' }; self['runPasses'](['print-stack-ir']); out = old; return ret; }; self['emitAsmjs'] = function() { var old = out; var ret = ''; out = function(x) { ret += x + '\n' }; Module['_BinaryenModulePrintAsmjs'](module); out = old; return ret; }; self['validate'] = function() { return Module['_BinaryenModuleValidate'](module); }; self['optimize'] = function() { return Module['_BinaryenModuleOptimize'](module); }; self['optimizeFunction'] = function(func) { if (typeof func === 'string') func = self['getFunction'](func); return Module['_BinaryenFunctionOptimize'](func, module); }; self['runPasses'] = function(passes) { return preserveStack(function() { return Module['_BinaryenModuleRunPasses'](module, i32sToStack( passes.map(strToStack) ), passes.length); }); }; self['runPassesOnFunction'] = function(func, passes) { if (typeof func === 'string') func = self['getFunction'](func); return preserveStack(function() { return Module['_BinaryenFunctionRunPasses'](func, module, i32sToStack( passes.map(strToStack) ), passes.length); }); }; self['autoDrop'] = function() { return Module['_BinaryenModuleAutoDrop'](module); }; self['dispose'] = function() { Module['_BinaryenModuleDispose'](module); }; self['emitBinary'] = function(sourceMapUrl) { return preserveStack(function() { var tempBuffer = stackAlloc(_BinaryenSizeofAllocateAndWriteResult()); Module['_BinaryenModuleAllocateAndWrite'](tempBuffer, module, strToStack(sourceMapUrl)); var binaryPtr = HEAPU32[ tempBuffer >>> 2 ]; var binaryBytes = HEAPU32[(tempBuffer >>> 2) + 1]; var sourceMapPtr = HEAPU32[(tempBuffer >>> 2) + 2]; try { var buffer = new Uint8Array(binaryBytes); buffer.set(HEAPU8.subarray(binaryPtr, binaryPtr + binaryBytes)); return typeof sourceMapUrl === 'undefined' ? buffer : { 'binary': buffer, 'sourceMap': UTF8ToString(sourceMapPtr) }; } finally { _free(binaryPtr); if (sourceMapPtr) _free(sourceMapPtr); } }); }; self['interpret'] = function() { return Module['_BinaryenModuleInterpret'](module); }; self['addDebugInfoFileName'] = function(filename) { return preserveStack(function() { return Module['_BinaryenModuleAddDebugInfoFileName'](module, strToStack(filename)); }); }; self['getDebugInfoFileName'] = function(index) { return UTF8ToString(Module['_BinaryenModuleGetDebugInfoFileName'](module, index)); }; self['setDebugLocation'] = function(func, expr, fileIndex, lineNumber, columnNumber) { return Module['_BinaryenFunctionSetDebugLocation'](func, expr, fileIndex, lineNumber, columnNumber); }; return self; } Module['wrapModule'] = wrapModule; // 'Relooper' interface Module['Relooper'] = function(module) { assert(module && typeof module === 'object' && module['ptr'] && module['block'] && module['if']); // guard against incorrect old API usage var relooper = Module['_RelooperCreate'](module['ptr']); this['ptr'] = relooper; this['addBlock'] = function(code) { return Module['_RelooperAddBlock'](relooper, code); }; this['addBranch'] = function(from, to, condition, code) { return Module['_RelooperAddBranch'](from, to, condition, code); }; this['addBlockWithSwitch'] = function(code, condition) { return Module['_RelooperAddBlockWithSwitch'](relooper, code, condition); }; this['addBranchForSwitch'] = function(from, to, indexes, code) { return preserveStack(function() { return Module['_RelooperAddBranchForSwitch'](from, to, i32sToStack(indexes), indexes.length, code); }); }; this['renderAndDispose'] = function(entry, labelHelper) { return Module['_RelooperRenderAndDispose'](relooper, entry, labelHelper); }; }; function getAllNested(ref, numFn, getFn) { var num = numFn(ref); var ret = new Array(num); for (var i = 0; i < num; ++i) ret[i] = getFn(ref, i); return ret; } // Gets the specific id of an 'Expression' Module['getExpressionId'] = function(expr) { return Module['_BinaryenExpressionGetId'](expr); }; // Gets the result type of an 'Expression' Module['getExpressionType'] = function(expr) { return Module['_BinaryenExpressionGetType'](expr); }; // Obtains information about an 'Expression' Module['getExpressionInfo'] = function(expr) { var id = Module['_BinaryenExpressionGetId'](expr); var type = Module['_BinaryenExpressionGetType'](expr); switch (id) { case Module['BlockId']: return { 'id': id, 'type': type, 'name': UTF8ToString(Module['_BinaryenBlockGetName'](expr)), 'children': getAllNested(expr, Module['_BinaryenBlockGetNumChildren'], Module['_BinaryenBlockGetChild']) }; case Module['IfId']: return { 'id': id, 'type': type, 'condition': Module['_BinaryenIfGetCondition'](expr), 'ifTrue': Module['_BinaryenIfGetIfTrue'](expr), 'ifFalse': Module['_BinaryenIfGetIfFalse'](expr) }; case Module['LoopId']: return { 'id': id, 'type': type, 'name': UTF8ToString(Module['_BinaryenLoopGetName'](expr)), 'body': Module['_BinaryenLoopGetBody'](expr) }; case Module['BreakId']: return { 'id': id, 'type': type, 'name': UTF8ToString(Module['_BinaryenBreakGetName'](expr)), 'condition': Module['_BinaryenBreakGetCondition'](expr), 'value': Module['_BinaryenBreakGetValue'](expr) }; case Module['SwitchId']: return { 'id': id, 'type': type, 'names': getAllNested(expr, Module['_BinaryenSwitchGetNumNames'], Module['_BinaryenSwitchGetName']).map(function (p) { // Do not pass the index as the second parameter to UTF8ToString as that will cut off the string. return UTF8ToString(p); }), 'defaultName': UTF8ToString(Module['_BinaryenSwitchGetDefaultName'](expr)), 'condition': Module['_BinaryenSwitchGetCondition'](expr), 'value': Module['_BinaryenSwitchGetValue'](expr) }; case Module['CallId']: return { 'id': id, 'type': type, 'target': UTF8ToString(Module['_BinaryenCallGetTarget'](expr)), 'operands': getAllNested(expr, Module[ '_BinaryenCallGetNumOperands'], Module['_BinaryenCallGetOperand']) }; case Module['CallIndirectId']: return { 'id': id, 'type': type, 'target': Module['_BinaryenCallIndirectGetTarget'](expr), 'operands': getAllNested(expr, Module['_BinaryenCallIndirectGetNumOperands'], Module['_BinaryenCallIndirectGetOperand']) }; case Module['LocalGetId']: return { 'id': id, 'type': type, 'index': Module['_BinaryenLocalGetGetIndex'](expr) }; case Module['LocalSetId']: return { 'id': id, 'type': type, 'isTee': Boolean(Module['_BinaryenLocalSetIsTee'](expr)), 'index': Module['_BinaryenLocalSetGetIndex'](expr), 'value': Module['_BinaryenLocalSetGetValue'](expr) }; case Module['GlobalGetId']: return { 'id': id, 'type': type, 'name': UTF8ToString(Module['_BinaryenGlobalGetGetName'](expr)) }; case Module['GlobalSetId']: return { 'id': id, 'type': type, 'name': UTF8ToString(Module['_BinaryenGlobalSetGetName'](expr)), 'value': Module['_BinaryenGlobalSetGetValue'](expr) }; case Module['LoadId']: return { 'id': id, 'type': type, 'isAtomic': Boolean(Module['_BinaryenLoadIsAtomic'](expr)), 'isSigned': Boolean(Module['_BinaryenLoadIsSigned'](expr)), 'offset': Module['_BinaryenLoadGetOffset'](expr), 'bytes': Module['_BinaryenLoadGetBytes'](expr), 'align': Module['_BinaryenLoadGetAlign'](expr), 'ptr': Module['_BinaryenLoadGetPtr'](expr) }; case Module['StoreId']: return { 'id': id, 'type': type, 'isAtomic': Boolean(Module['_BinaryenStoreIsAtomic'](expr)), 'offset': Module['_BinaryenStoreGetOffset'](expr), 'bytes': Module['_BinaryenStoreGetBytes'](expr), 'align': Module['_BinaryenStoreGetAlign'](expr), 'ptr': Module['_BinaryenStoreGetPtr'](expr), 'value': Module['_BinaryenStoreGetValue'](expr) }; case Module['ConstId']: { var value; switch (type) { case Module['i32']: value = Module['_BinaryenConstGetValueI32'](expr); break; case Module['i64']: value = { 'low': Module['_BinaryenConstGetValueI64Low'](expr), 'high': Module['_BinaryenConstGetValueI64High'](expr) }; break; case Module['f32']: value = Module['_BinaryenConstGetValueF32'](expr); break; case Module['f64']: value = Module['_BinaryenConstGetValueF64'](expr); break; case Module['v128']: { preserveStack(function() { var tempBuffer = stackAlloc(16); Module['_BinaryenConstGetValueV128'](expr, tempBuffer); value = new Array(16); for (var i = 0; i < 16; i++) { value[i] = HEAPU8[tempBuffer + i]; } }); break; } default: throw Error('unexpected type: ' + type); } return { 'id': id, 'type': type, 'value': value }; } case Module['UnaryId']: return { 'id': id, 'type': type, 'op': Module['_BinaryenUnaryGetOp'](expr), 'value': Module['_BinaryenUnaryGetValue'](expr) }; case Module['BinaryId']: return { 'id': id, 'type': type, 'op': Module['_BinaryenBinaryGetOp'](expr), 'left': Module['_BinaryenBinaryGetLeft'](expr), 'right': Module['_BinaryenBinaryGetRight'](expr) }; case Module['SelectId']: return { 'id': id, 'type': type, 'ifTrue': Module['_BinaryenSelectGetIfTrue'](expr), 'ifFalse': Module['_BinaryenSelectGetIfFalse'](expr), 'condition': Module['_BinaryenSelectGetCondition'](expr) }; case Module['DropId']: return { 'id': id, 'type': type, 'value': Module['_BinaryenDropGetValue'](expr) }; case Module['ReturnId']: return { 'id': id, 'type': type, 'value': Module['_BinaryenReturnGetValue'](expr) }; case Module['NopId']: case Module['UnreachableId']: case Module['PopId']: return { 'id': id, 'type': type }; case Module['HostId']: return { 'id': id, 'type': type, 'op': Module['_BinaryenHostGetOp'](expr), 'nameOperand': UTF8ToString(Module['_BinaryenHostGetNameOperand'](expr)), 'operands': getAllNested(expr, Module['_BinaryenHostGetNumOperands'], Module['_BinaryenHostGetOperand']) }; case Module['AtomicRMWId']: return { 'id': id, 'type': type, 'op': Module['_BinaryenAtomicRMWGetOp'](expr), 'bytes': Module['_BinaryenAtomicRMWGetBytes'](expr), 'offset': Module['_BinaryenAtomicRMWGetOffset'](expr), 'ptr': Module['_BinaryenAtomicRMWGetPtr'](expr), 'value': Module['_BinaryenAtomicRMWGetValue'](expr) }; case Module['AtomicCmpxchgId']: return { 'id': id, 'type': type, 'bytes': Module['_BinaryenAtomicCmpxchgGetBytes'](expr), 'offset': Module['_BinaryenAtomicCmpxchgGetOffset'](expr), 'ptr': Module['_BinaryenAtomicCmpxchgGetPtr'](expr), 'expected': Module['_BinaryenAtomicCmpxchgGetExpected'](expr), 'replacement': Module['_BinaryenAtomicCmpxchgGetReplacement'](expr) }; case Module['AtomicWaitId']: return { 'id': id, 'type': type, 'ptr': Module['_BinaryenAtomicWaitGetPtr'](expr), 'expected': Module['_BinaryenAtomicWaitGetExpected'](expr), 'timeout': Module['_BinaryenAtomicWaitGetTimeout'](expr), 'expectedType': Module['_BinaryenAtomicWaitGetExpectedType'](expr) }; case Module['AtomicNotifyId']: return { 'id': id, 'type': type, 'ptr': Module['_BinaryenAtomicNotifyGetPtr'](expr), 'notifyCount': Module['_BinaryenAtomicNotifyGetNotifyCount'](expr) }; case Module['AtomicFenceId']: return { 'id': id, 'type': type, 'order': Module['_BinaryenAtomicFenceGetOrder'](expr) }; case Module['SIMDExtractId']: return { 'id': id, 'type': type, 'op': Module['_BinaryenSIMDExtractGetOp'](expr), 'vec': Module['_BinaryenSIMDExtractGetVec'](expr), 'index': Module['_BinaryenSIMDExtractGetIndex'](expr) }; case Module['SIMDReplaceId']: return { 'id': id, 'type': type, 'op': Module['_BinaryenSIMDReplaceGetOp'](expr), 'vec': Module['_BinaryenSIMDReplaceGetVec'](expr), 'index': Module['_BinaryenSIMDReplaceGetIndex'](expr), 'value': Module['_BinaryenSIMDReplaceGetValue'](expr) }; case Module['SIMDShuffleId']: return preserveStack(function() { var tempBuffer = stackAlloc(16); Module['_BinaryenSIMDShuffleGetMask'](expr, tempBuffer); var mask = new Array(16); for (var i = 0; i < 16; i++) { mask[i] = HEAPU8[tempBuffer + i]; } return { 'id': id, 'type': type, 'left': Module['_BinaryenSIMDShuffleGetLeft'](expr), 'right': Module['_BinaryenSIMDShuffleGetRight'](expr), 'mask': mask }; }); case Module['SIMDTernaryId']: return { 'id': id, 'type': type, 'op': Module['_BinaryenSIMDTernaryGetOp'](expr), 'a': Module['_BinaryenSIMDTernaryGetA'](expr), 'b': Module['_BinaryenSIMDTernaryGetB'](expr), 'c': Module['_BinaryenSIMDTernaryGetC'](expr) }; case Module['SIMDShiftId']: return { 'id': id, 'type': type, 'op': Module['_BinaryenSIMDShiftGetOp'](expr), 'vec': Module['_BinaryenSIMDShiftGetVec'](expr), 'shift': Module['_BinaryenSIMDShiftGetShift'](expr) }; case Module['SIMDLoadId']: return { 'id': id, 'type': type, 'op': Module['_BinaryenSIMDLoadGetOp'](expr), 'offset': Module['_BinaryenSIMDLoadGetOffset'](expr), 'align': Module['_BinaryenSIMDLoadGetAlign'](expr), 'ptr': Module['_BinaryenSIMDLoadGetPtr'](expr) }; case Module['MemoryInitId']: return { 'id': id, 'segment': Module['_BinaryenMemoryInitGetSegment'](expr), 'dest': Module['_BinaryenMemoryInitGetDest'](expr), 'offset': Module['_BinaryenMemoryInitGetOffset'](expr), 'size': Module['_BinaryenMemoryInitGetSize'](expr) }; case Module['DataDropId']: return { 'id': id, 'segment': Module['_BinaryenDataDropGetSegment'](expr), }; case Module['MemoryCopyId']: return { 'id': id, 'dest': Module['_BinaryenMemoryCopyGetDest'](expr), 'source': Module['_BinaryenMemoryCopyGetSource'](expr), 'size': Module['_BinaryenMemoryCopyGetSize'](expr) }; case Module['MemoryFillId']: return { 'id': id, 'dest': Module['_BinaryenMemoryFillGetDest'](expr), 'value': Module['_BinaryenMemoryFillGetValue'](expr), 'size': Module['_BinaryenMemoryFillGetSize'](expr) }; case Module['RefNullId']: return { 'id': id, 'type': type }; case Module['RefIsNullId']: return { 'id': id, 'type': type, 'value': Module['_BinaryenRefIsNullGetValue'](expr) }; case Module['RefFuncId']: return { 'id': id, 'type': type, 'func': UTF8ToString(Module['_BinaryenRefFuncGetFunc'](expr)), }; case Module['TryId']: return { 'id': id, 'type': type, 'body': Module['_BinaryenTryGetBody'](expr), 'catchBody': Module['_BinaryenTryGetCatchBody'](expr) }; case Module['ThrowId']: return { 'id': id, 'type': type, 'event': UTF8ToString(Module['_BinaryenThrowGetEvent'](expr)), 'operands': getAllNested(expr, Module['_BinaryenThrowGetNumOperands'], Module['_BinaryenThrowGetOperand']) }; case Module['RethrowId']: return { 'id': id, 'type': type, 'exnref': Module['_BinaryenRethrowGetExnref'](expr) }; case Module['BrOnExnId']: return { 'id': id, 'type': type, 'name': UTF8ToString(Module['_BinaryenBrOnExnGetName'](expr)), 'event': UTF8ToString(Module['_BinaryenBrOnExnGetEvent'](expr)), 'exnref': Module['_BinaryenBrOnExnGetExnref'](expr) }; case Module['PushId']: return { 'id': id, 'value': Module['_BinaryenPushGetValue'](expr) }; default: throw Error('unexpected id: ' + id); } }; // Gets the side effects of the specified expression Module['getSideEffects'] = function(expr, features) { return Module['_BinaryenExpressionGetSideEffects'](expr, features); }; Module['createType'] = function(types) { return preserveStack(function() { var array = i32sToStack(types); return Module['_BinaryenTypeCreate'](array, types.length); }); }; Module['expandType'] = function(ty) { return preserveStack(function() { var numTypes = Module['_BinaryenTypeArity'](ty); var array = stackAlloc(numTypes << 2); Module['_BinaryenTypeExpand'](ty, array); var types = []; for (var i = 0; i < numTypes; i++) { types.push(HEAPU32[(array >>> 2) + i]); } return types; }); }; // Obtains information about a 'Function' Module['getFunctionInfo'] = function(func) { return { 'name': UTF8ToString(Module['_BinaryenFunctionGetName'](func)), 'module': UTF8ToString(Module['_BinaryenFunctionImportGetModule'](func)), 'base': UTF8ToString(Module['_BinaryenFunctionImportGetBase'](func)), 'params': Module['_BinaryenFunctionGetParams'](func), 'results': Module['_BinaryenFunctionGetResults'](func), 'vars': getAllNested(func, Module['_BinaryenFunctionGetNumVars'], Module['_BinaryenFunctionGetVar']), 'body': Module['_BinaryenFunctionGetBody'](func) }; }; // Obtains information about a 'Global' Module['getGlobalInfo'] = function(global) { return { 'name': UTF8ToString(Module['_BinaryenGlobalGetName'](global)), 'module': UTF8ToString(Module['_BinaryenGlobalImportGetModule'](global)), 'base': UTF8ToString(Module['_BinaryenGlobalImportGetBase'](global)), 'type': Module['_BinaryenGlobalGetType'](global), 'mutable': Boolean(Module['_BinaryenGlobalIsMutable'](global)), 'init': Module['_BinaryenGlobalGetInitExpr'](global) }; }; // Obtains information about a 'Event' Module['getEventInfo'] = function(event_) { return { 'name': UTF8ToString(Module['_BinaryenEventGetName'](event_)), 'module': UTF8ToString(Module['_BinaryenEventImportGetModule'](event_)), 'base': UTF8ToString(Module['_BinaryenEventImportGetBase'](event_)), 'attribute': Module['_BinaryenEventGetAttribute'](event_), 'params': Module['_BinaryenEventGetParams'](event_), 'results': Module['_BinaryenEventGetResults'](event_) }; }; // Obtains information about an 'Export' Module['getExportInfo'] = function(export_) { return { 'kind': Module['_BinaryenExportGetKind'](export_), 'name': UTF8ToString(Module['_BinaryenExportGetName'](export_)), 'value': UTF8ToString(Module['_BinaryenExportGetValue'](export_)) }; }; // Emits text format of an expression or a module Module['emitText'] = function(expr) { if (typeof expr === 'object') { return expr.emitText(); } var old = out; var ret = ''; out = function(x) { ret += x + '\n' }; Module['_BinaryenExpressionPrint'](expr); out = old; return ret; }; // Parses a binary to a module // If building with Emscripten ASSERTIONS, there is a property added to // Module to guard against users mistakening using the removed readBinary() // API. We must defuse that carefully. Object.defineProperty(Module, 'readBinary', { writable: true }); Module['readBinary'] = function(data) { var buffer = allocate(data, 'i8', ALLOC_NORMAL); var ptr = Module['_BinaryenModuleRead'](buffer, data.length); _free(buffer); return wrapModule(ptr); }; // Parses text format to a module Module['parseText'] = function(text) { var buffer = _malloc(text.length + 1); writeAsciiToMemory(text, buffer); var ptr = Module['_BinaryenModuleParse'](buffer); _free(buffer); return wrapModule(ptr); }; // Gets the currently set optimize level. 0, 1, 2 correspond to -O0, -O1, -O2, etc. Module['getOptimizeLevel'] = function() { return Module['_BinaryenGetOptimizeLevel'](); }; // Sets the optimization level to use. 0, 1, 2 correspond to -O0, -O1, -O2, etc. Module['setOptimizeLevel'] = function(level) { Module['_BinaryenSetOptimizeLevel'](level); }; // Gets the currently set shrink level. 0, 1, 2 correspond to -O0, -Os, -Oz. Module['getShrinkLevel'] = function() { return Module['_BinaryenGetShrinkLevel'](); }; // Sets the shrink level to use. 0, 1, 2 correspond to -O0, -Os, -Oz. Module['setShrinkLevel'] = function(level) { Module['_BinaryenSetShrinkLevel'](level); }; // Gets whether generating debug information is currently enabled or not. Module['getDebugInfo'] = function() { return Boolean(Module['_BinaryenGetDebugInfo']()); }; // Enables or disables debug information in emitted binaries. Module['setDebugInfo'] = function(on) { Module['_BinaryenSetDebugInfo'](on); }; // Gets whether the low 1K of memory can be considered unused when optimizing. Module['getLowMemoryUnused'] = function() { return Boolean(Module['_BinaryenGetLowMemoryUnused']()); }; // Enables or disables whether the low 1K of memory can be considered unused // when optimizing. Module['setLowMemoryUnused'] = function(on) { Module['_BinaryenSetLowMemoryUnused'](on); }; // Gets the value of the specified arbitrary pass argument. Module['getPassArgument'] = function(key) { return preserveStack(function() { var ret = Module['_BinaryenGetPassArgument'](strToStack(key)); return ret !== 0 ? UTF8ToString(ret) : null; }); }; // Sets the value of the specified arbitrary pass argument. Removes the // respective argument if `value` is NULL. Module['setPassArgument'] = function (key, value) { preserveStack(function () { Module['_BinaryenSetPassArgument'](strToStack(key), strToStack(value)); }); }; // Clears all arbitrary pass arguments. Module['clearPassArguments'] = function() { Module['_BinaryenClearPassArguments'](); }; // Gets the function size at which we always inline. Module['getAlwaysInlineMaxSize'] = function() { return Module['_BinaryenGetAlwaysInlineMaxSize'](); }; // Sets the function size at which we always inline. Module['setAlwaysInlineMaxSize'] = function(size) { Module['_BinaryenSetAlwaysInlineMaxSize'](size); }; // Gets the function size which we inline when functions are lightweight. Module['getFlexibleInlineMaxSize'] = function() { return Module['_BinaryenGetFlexibleInlineMaxSize'](); }; // Sets the function size which we inline when functions are lightweight. Module['setFlexibleInlineMaxSize'] = function(size) { Module['_BinaryenSetFlexibleInlineMaxSize'](size); }; // Gets the function size which we inline when there is only one caller. Module['getOneCallerInlineMaxSize'] = function() { return Module['_BinaryenGetOneCallerInlineMaxSize'](); }; // Sets the function size which we inline when there is only one caller. Module['setOneCallerInlineMaxSize'] = function(size) { Module['_BinaryenSetOneCallerInlineMaxSize'](size); }; // Enables or disables C-API tracing Module['setAPITracing'] = function(on) { return Module['_BinaryenSetAPITracing'](on); }; // Additional customizations Module['exit'] = function(status) { // Instead of exiting silently on errors, always show an error with // a stack trace, for debuggability. if (status != 0) throw new Error('exiting due to error: ' + status); }; // Indicates if Binaryen has been loaded and is ready Module['isReady'] = runtimeInitialized; // Provide a mechanism to tell when the module is ready // // if (!binaryen.isReady) await binaryen.ready; // ... // var pendingPromises = []; var initializeError = null; Object.defineProperty(Module, 'ready', { get: function() { return new Promise(function(resolve, reject) { if (initializeError) { reject(initializeError); } else if (runtimeInitialized) { resolve(Module); } else { pendingPromises.push({ resolve: resolve, reject: reject }); } }); } }); // Intercept the onRuntimeInitialized hook if necessary if (runtimeInitialized) { initializeConstants(); } else { Module['onRuntimeInitialized'] = (function(super_) { return function() { try { initializeConstants(); if (super_) super_(); Module['isReady'] = true; pendingPromises.forEach(function(p) { p.resolve(Module); }); } catch (e) { initializeError = e; pendingPromises.forEach(function(p) { p.reject(e); }); } finally { pendingPromises = []; } }; })(Module['onRuntimeInitialized']); } binaryen-version_91/src/literal.h000066400000000000000000000427371362402614000172700ustar00rootroot00000000000000/* * Copyright 2017 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_literal_h #define wasm_literal_h #include #include #include "compiler-support.h" #include "support/hash.h" #include "support/name.h" #include "support/utilities.h" #include "wasm-type.h" namespace wasm { class Literal { // store only integers, whose bits are deterministic. floats // can have their signalling bit set, for example. union { int32_t i32; int64_t i64; uint8_t v128[16]; Name func; // function name for funcref }; public: Type type; public: Literal() : v128(), type(Type::none) {} explicit Literal(Type type) : v128(), type(type) {} explicit Literal(int32_t init) : i32(init), type(Type::i32) {} explicit Literal(uint32_t init) : i32(init), type(Type::i32) {} explicit Literal(int64_t init) : i64(init), type(Type::i64) {} explicit Literal(uint64_t init) : i64(init), type(Type::i64) {} explicit Literal(float init) : i32(bit_cast(init)), type(Type::f32) {} explicit Literal(double init) : i64(bit_cast(init)), type(Type::f64) {} // v128 literal from bytes explicit Literal(const uint8_t init[16]); // v128 literal from lane value literals explicit Literal(const std::array&); explicit Literal(const std::array&); explicit Literal(const std::array&); explicit Literal(const std::array&); explicit Literal(Name func) : func(func), type(Type::funcref) {} bool isConcrete() { return type != Type::none; } bool isNone() { return type == Type::none; } static Literal makeFromInt32(int32_t x, Type type) { switch (type.getSingle()) { case Type::i32: return Literal(int32_t(x)); break; case Type::i64: return Literal(int64_t(x)); break; case Type::f32: return Literal(float(x)); break; case Type::f64: return Literal(double(x)); break; case Type::v128: return Literal(std::array{{Literal(x), Literal(int32_t(0)), Literal(int32_t(0)), Literal(int32_t(0))}}); case Type::funcref: case Type::anyref: case Type::nullref: case Type::exnref: case Type::none: case Type::unreachable: WASM_UNREACHABLE("unexpected type"); } WASM_UNREACHABLE("unexpected type"); } static Literal makeZero(Type type) { if (type.isRef()) { return makeNullref(); } return makeFromInt32(0, type); } static Literal makeNullref() { return Literal(Type(Type::nullref)); } static Literal makeFuncref(Name func) { return Literal(func.c_str()); } Literal castToF32(); Literal castToF64(); Literal castToI32(); Literal castToI64(); int32_t geti32() const { assert(type == Type::i32); return i32; } int64_t geti64() const { assert(type == Type::i64); return i64; } float getf32() const { assert(type == Type::f32); return bit_cast(i32); } double getf64() const { assert(type == Type::f64); return bit_cast(i64); } std::array getv128() const; Name getFunc() const { return func; } // careful! int32_t* geti32Ptr() { assert(type == Type::i32); return &i32; } uint8_t* getv128Ptr() { assert(type == Type::v128); return v128; } const uint8_t* getv128Ptr() const { assert(type == Type::v128); return v128; } int32_t reinterpreti32() const { assert(type == Type::f32); return i32; } int64_t reinterpreti64() const { assert(type == Type::f64); return i64; } float reinterpretf32() const { assert(type == Type::i32); return bit_cast(i32); } double reinterpretf64() const { assert(type == Type::i64); return bit_cast(i64); } int64_t getInteger() const; double getFloat() const; void getBits(uint8_t (&buf)[16]) const; // Equality checks for the type and the bits, so a nan float would // be compared bitwise (which means that a Literal containing a nan // would be equal to itself, if the bits are equal). bool operator==(const Literal& other) const; bool operator!=(const Literal& other) const; bool isNaN(); static uint32_t NaNPayload(float f); static uint64_t NaNPayload(double f); static float setQuietNaN(float f); static double setQuietNaN(double f); static void printFloat(std::ostream& o, float f); static void printDouble(std::ostream& o, double d); static void printVec128(std::ostream& o, const std::array& v); friend std::ostream& operator<<(std::ostream& o, Literal literal); Literal countLeadingZeroes() const; Literal countTrailingZeroes() const; Literal popCount() const; Literal extendToSI64() const; Literal extendToUI64() const; Literal extendToF64() const; Literal extendS8() const; Literal extendS16() const; Literal extendS32() const; Literal wrapToI32() const; Literal convertSIToF32() const; Literal convertUIToF32() const; Literal convertSIToF64() const; Literal convertUIToF64() const; Literal truncSatToSI32() const; Literal truncSatToSI64() const; Literal truncSatToUI32() const; Literal truncSatToUI64() const; Literal eqz() const; Literal neg() const; Literal abs() const; Literal ceil() const; Literal floor() const; Literal trunc() const; Literal nearbyint() const; Literal sqrt() const; Literal demote() const; Literal add(const Literal& other) const; Literal sub(const Literal& other) const; Literal mul(const Literal& other) const; Literal div(const Literal& other) const; Literal divS(const Literal& other) const; Literal divU(const Literal& other) const; Literal remS(const Literal& other) const; Literal remU(const Literal& other) const; Literal and_(const Literal& other) const; Literal or_(const Literal& other) const; Literal xor_(const Literal& other) const; Literal shl(const Literal& other) const; Literal shrS(const Literal& other) const; Literal shrU(const Literal& other) const; Literal rotL(const Literal& other) const; Literal rotR(const Literal& other) const; // Note that these functions perform equality checks based // on the type of the literal, so that (unlike the == operator) // a float nan would not be identical to itself. Literal eq(const Literal& other) const; Literal ne(const Literal& other) const; Literal ltS(const Literal& other) const; Literal ltU(const Literal& other) const; Literal lt(const Literal& other) const; Literal leS(const Literal& other) const; Literal leU(const Literal& other) const; Literal le(const Literal& other) const; Literal gtS(const Literal& other) const; Literal gtU(const Literal& other) const; Literal gt(const Literal& other) const; Literal geS(const Literal& other) const; Literal geU(const Literal& other) const; Literal ge(const Literal& other) const; Literal min(const Literal& other) const; Literal max(const Literal& other) const; Literal copysign(const Literal& other) const; std::array getLanesSI8x16() const; std::array getLanesUI8x16() const; std::array getLanesSI16x8() const; std::array getLanesUI16x8() const; std::array getLanesI32x4() const; std::array getLanesI64x2() const; std::array getLanesF32x4() const; std::array getLanesF64x2() const; Literal shuffleV8x16(const Literal& other, const std::array& mask) const; Literal splatI8x16() const; Literal extractLaneSI8x16(uint8_t index) const; Literal extractLaneUI8x16(uint8_t index) const; Literal replaceLaneI8x16(const Literal& other, uint8_t index) const; Literal splatI16x8() const; Literal extractLaneSI16x8(uint8_t index) const; Literal extractLaneUI16x8(uint8_t index) const; Literal replaceLaneI16x8(const Literal& other, uint8_t index) const; Literal splatI32x4() const; Literal extractLaneI32x4(uint8_t index) const; Literal replaceLaneI32x4(const Literal& other, uint8_t index) const; Literal splatI64x2() const; Literal extractLaneI64x2(uint8_t index) const; Literal replaceLaneI64x2(const Literal& other, uint8_t index) const; Literal splatF32x4() const; Literal extractLaneF32x4(uint8_t index) const; Literal replaceLaneF32x4(const Literal& other, uint8_t index) const; Literal splatF64x2() const; Literal extractLaneF64x2(uint8_t index) const; Literal replaceLaneF64x2(const Literal& other, uint8_t index) const; Literal eqI8x16(const Literal& other) const; Literal neI8x16(const Literal& other) const; Literal ltSI8x16(const Literal& other) const; Literal ltUI8x16(const Literal& other) const; Literal gtSI8x16(const Literal& other) const; Literal gtUI8x16(const Literal& other) const; Literal leSI8x16(const Literal& other) const; Literal leUI8x16(const Literal& other) const; Literal geSI8x16(const Literal& other) const; Literal geUI8x16(const Literal& other) const; Literal eqI16x8(const Literal& other) const; Literal neI16x8(const Literal& other) const; Literal ltSI16x8(const Literal& other) const; Literal ltUI16x8(const Literal& other) const; Literal gtSI16x8(const Literal& other) const; Literal gtUI16x8(const Literal& other) const; Literal leSI16x8(const Literal& other) const; Literal leUI16x8(const Literal& other) const; Literal geSI16x8(const Literal& other) const; Literal geUI16x8(const Literal& other) const; Literal eqI32x4(const Literal& other) const; Literal neI32x4(const Literal& other) const; Literal ltSI32x4(const Literal& other) const; Literal ltUI32x4(const Literal& other) const; Literal gtSI32x4(const Literal& other) const; Literal gtUI32x4(const Literal& other) const; Literal leSI32x4(const Literal& other) const; Literal leUI32x4(const Literal& other) const; Literal geSI32x4(const Literal& other) const; Literal geUI32x4(const Literal& other) const; Literal eqF32x4(const Literal& other) const; Literal neF32x4(const Literal& other) const; Literal ltF32x4(const Literal& other) const; Literal gtF32x4(const Literal& other) const; Literal leF32x4(const Literal& other) const; Literal geF32x4(const Literal& other) const; Literal eqF64x2(const Literal& other) const; Literal neF64x2(const Literal& other) const; Literal ltF64x2(const Literal& other) const; Literal gtF64x2(const Literal& other) const; Literal leF64x2(const Literal& other) const; Literal geF64x2(const Literal& other) const; Literal notV128() const; Literal andV128(const Literal& other) const; Literal orV128(const Literal& other) const; Literal xorV128(const Literal& other) const; Literal bitselectV128(const Literal& left, const Literal& right) const; Literal negI8x16() const; Literal anyTrueI8x16() const; Literal allTrueI8x16() const; Literal shlI8x16(const Literal& other) const; Literal shrSI8x16(const Literal& other) const; Literal shrUI8x16(const Literal& other) const; Literal addI8x16(const Literal& other) const; Literal addSaturateSI8x16(const Literal& other) const; Literal addSaturateUI8x16(const Literal& other) const; Literal subI8x16(const Literal& other) const; Literal subSaturateSI8x16(const Literal& other) const; Literal subSaturateUI8x16(const Literal& other) const; Literal mulI8x16(const Literal& other) const; Literal minSI8x16(const Literal& other) const; Literal minUI8x16(const Literal& other) const; Literal maxSI8x16(const Literal& other) const; Literal maxUI8x16(const Literal& other) const; Literal avgrUI8x16(const Literal& other) const; Literal negI16x8() const; Literal anyTrueI16x8() const; Literal allTrueI16x8() const; Literal shlI16x8(const Literal& other) const; Literal shrSI16x8(const Literal& other) const; Literal shrUI16x8(const Literal& other) const; Literal addI16x8(const Literal& other) const; Literal addSaturateSI16x8(const Literal& other) const; Literal addSaturateUI16x8(const Literal& other) const; Literal subI16x8(const Literal& other) const; Literal subSaturateSI16x8(const Literal& other) const; Literal subSaturateUI16x8(const Literal& other) const; Literal mulI16x8(const Literal& other) const; Literal minSI16x8(const Literal& other) const; Literal minUI16x8(const Literal& other) const; Literal maxSI16x8(const Literal& other) const; Literal maxUI16x8(const Literal& other) const; Literal avgrUI16x8(const Literal& other) const; Literal negI32x4() const; Literal anyTrueI32x4() const; Literal allTrueI32x4() const; Literal shlI32x4(const Literal& other) const; Literal shrSI32x4(const Literal& other) const; Literal shrUI32x4(const Literal& other) const; Literal addI32x4(const Literal& other) const; Literal subI32x4(const Literal& other) const; Literal mulI32x4(const Literal& other) const; Literal minSI32x4(const Literal& other) const; Literal minUI32x4(const Literal& other) const; Literal maxSI32x4(const Literal& other) const; Literal maxUI32x4(const Literal& other) const; Literal dotSI16x8toI32x4(const Literal& other) const; Literal negI64x2() const; Literal anyTrueI64x2() const; Literal allTrueI64x2() const; Literal shlI64x2(const Literal& other) const; Literal shrSI64x2(const Literal& other) const; Literal shrUI64x2(const Literal& other) const; Literal addI64x2(const Literal& other) const; Literal subI64x2(const Literal& other) const; Literal absF32x4() const; Literal negF32x4() const; Literal sqrtF32x4() const; Literal addF32x4(const Literal& other) const; Literal subF32x4(const Literal& other) const; Literal mulF32x4(const Literal& other) const; Literal divF32x4(const Literal& other) const; Literal minF32x4(const Literal& other) const; Literal maxF32x4(const Literal& other) const; Literal absF64x2() const; Literal negF64x2() const; Literal sqrtF64x2() const; Literal addF64x2(const Literal& other) const; Literal subF64x2(const Literal& other) const; Literal mulF64x2(const Literal& other) const; Literal divF64x2(const Literal& other) const; Literal minF64x2(const Literal& other) const; Literal maxF64x2(const Literal& other) const; Literal truncSatToSI32x4() const; Literal truncSatToUI32x4() const; Literal truncSatToSI64x2() const; Literal truncSatToUI64x2() const; Literal convertSToF32x4() const; Literal convertUToF32x4() const; Literal convertSToF64x2() const; Literal convertUToF64x2() const; Literal narrowSToVecI8x16(const Literal& other) const; Literal narrowUToVecI8x16(const Literal& other) const; Literal narrowSToVecI16x8(const Literal& other) const; Literal narrowUToVecI16x8(const Literal& other) const; Literal widenLowSToVecI16x8() const; Literal widenHighSToVecI16x8() const; Literal widenLowUToVecI16x8() const; Literal widenHighUToVecI16x8() const; Literal widenLowSToVecI32x4() const; Literal widenHighSToVecI32x4() const; Literal widenLowUToVecI32x4() const; Literal widenHighUToVecI32x4() const; Literal swizzleVec8x16(const Literal& other) const; private: Literal addSatSI8(const Literal& other) const; Literal addSatUI8(const Literal& other) const; Literal addSatSI16(const Literal& other) const; Literal addSatUI16(const Literal& other) const; Literal subSatSI8(const Literal& other) const; Literal subSatUI8(const Literal& other) const; Literal subSatSI16(const Literal& other) const; Literal subSatUI16(const Literal& other) const; Literal minInt(const Literal& other) const; Literal maxInt(const Literal& other) const; Literal minUInt(const Literal& other) const; Literal maxUInt(const Literal& other) const; Literal avgrUInt(const Literal& other) const; }; } // namespace wasm namespace std { template<> struct hash { size_t operator()(const wasm::Literal& a) const { uint8_t bytes[16]; a.getBits(bytes); int64_t chunks[2]; memcpy(chunks, bytes, sizeof(chunks)); return wasm::rehash(wasm::rehash(uint64_t(hash()(a.type.getID())), uint64_t(hash()(chunks[0]))), uint64_t(hash()(chunks[1]))); } }; template<> struct less { bool operator()(const wasm::Literal& a, const wasm::Literal& b) const { if (a.type < b.type) { return true; } if (b.type < a.type) { return false; } switch (a.type.getSingle()) { case wasm::Type::i32: return a.geti32() < b.geti32(); case wasm::Type::f32: return a.reinterpreti32() < b.reinterpreti32(); case wasm::Type::i64: return a.geti64() < b.geti64(); case wasm::Type::f64: return a.reinterpreti64() < b.reinterpreti64(); case wasm::Type::v128: return memcmp(a.getv128Ptr(), b.getv128Ptr(), 16) < 0; case wasm::Type::funcref: case wasm::Type::anyref: case wasm::Type::nullref: case wasm::Type::exnref: case wasm::Type::none: case wasm::Type::unreachable: return false; } WASM_UNREACHABLE("unexpected type"); } }; } // namespace std #endif // wasm_literal_h binaryen-version_91/src/mixed_arena.h000066400000000000000000000263451362402614000201050ustar00rootroot00000000000000/* * Copyright 2015 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_mixed_arena_h #define wasm_mixed_arena_h #include #include #include #include #include #include #include #include // // Arena allocation for mixed-type data. // // Arena-style bump allocation is important for two reasons: First, so that // allocation is quick, and second, so that allocated items are close together, // which is cache-friendy. Arena allocation is also useful for a minor third // reason which is to make freeing all the items in an arena very quick. // // Each WebAssembly Module has an arena allocator, which should be used // for all of its AST nodes and so forth. When the Module is destroyed, the // entire arena is cleaned up. // // When allocating an object in an arena, the object's proper constructor // is called. Note that destructors are not called, because to make the // arena simple and fast we do not track internal allocations inside it // (and we can also avoid the need for virtual destructors). // // In general, optimization passes avoid allocation as much as possible. // Many passes only remove or modify nodes anyhow, others can often // reuse nodes that are being optimized out. This keeps things // cache-friendly, and also makes the operations trivially thread-safe. // In the rare case that a pass does need to allocate, and it is a // parallel pass (so multiple threads might access the allocator), // the MixedArena instance will notice if it is on a different thread // than that arena's original thread, and will perform the allocation // in a side arena for that other thread. This is done in a transparent // way to the outside; as a result, it is always safe to allocate using // a MixedArena, no matter which thread you are on. Allocations will // of course be fastest on the original thread for the arena. // struct MixedArena { // fast bump allocation static const size_t CHUNK_SIZE = 32768; static const size_t MAX_ALIGN = 16; // allow 128bit SIMD // Each pointer in chunks is to a multiple of CHUNK_SIZE - typically 1, // but possibly more. std::vector chunks; size_t index = 0; // in last chunk std::thread::id threadId; // multithreaded allocation - each arena is valid on a specific thread. // if we are on the wrong thread, we atomically look in the linked // list of next, adding an allocator if necessary std::atomic next; MixedArena() { threadId = std::this_thread::get_id(); next.store(nullptr); } // Allocate an amount of space with a guaranteed alignment void* allocSpace(size_t size, size_t align) { // the bump allocator data should not be modified by multiple threads at // once. auto myId = std::this_thread::get_id(); if (myId != threadId) { MixedArena* curr = this; MixedArena* allocated = nullptr; while (myId != curr->threadId) { auto seen = curr->next.load(); if (seen) { curr = seen; continue; } // there is a nullptr for next, so we may be able to place a new // allocator for us there. but carefully, as others may do so as // well. we may waste a few allocations here, but it doesn't matter // as this can only happen as the chain is built up, i.e., // O(# of cores) per allocator, and our allocatrs are long-lived. if (!allocated) { allocated = new MixedArena(); // has our thread id } if (curr->next.compare_exchange_weak(seen, allocated)) { // we replaced it, so we are the next in the chain // we can forget about allocated, it is owned by the chain now allocated = nullptr; break; } // otherwise, the cmpxchg updated seen, and we continue to loop curr = seen; } if (allocated) { delete allocated; } return curr->allocSpace(size, align); } // First, move the current index in the last chunk to an aligned position. index = (index + align - 1) & (-align); if (index + size > CHUNK_SIZE || chunks.size() == 0) { // Allocate a new chunk. auto numChunks = (size + CHUNK_SIZE - 1) / CHUNK_SIZE; assert(size <= numChunks * CHUNK_SIZE); auto* allocation = wasm::aligned_malloc(MAX_ALIGN, numChunks * CHUNK_SIZE); if (!allocation) { abort(); } chunks.push_back(allocation); index = 0; } uint8_t* ret = static_cast(chunks.back()); ret += index; index += size; // TODO: if we allocated more than 1 chunk, reuse the // remainder, right now we allocate another next time return static_cast(ret); } template T* alloc() { static_assert(alignof(T) <= MAX_ALIGN, "maximum alignment not large enough"); auto* ret = static_cast(allocSpace(sizeof(T), alignof(T))); new (ret) T(*this); // allocated objects receive the allocator, so they can // allocate more later if necessary return ret; } void clear() { for (auto* chunk : chunks) { wasm::aligned_free(chunk); } chunks.clear(); } ~MixedArena() { clear(); if (next.load()) { delete next.load(); } } }; // // A vector that allocates in an arena. // // TODO: specialize on the initial size of the array template class ArenaVectorBase { protected: T* data = nullptr; size_t usedElements = 0, allocatedElements = 0; void reallocate(size_t size) { T* old = data; static_cast(this)->allocate(size); for (size_t i = 0; i < usedElements; i++) { data[i] = old[i]; } } public: struct Iterator; T& operator[](size_t index) const { assert(index < usedElements); return data[index]; } size_t size() const { return usedElements; } bool empty() const { return size() == 0; } void resize(size_t size) { if (size > allocatedElements) { reallocate(size); } // construct new elements for (size_t i = usedElements; i < size; i++) { new (data + i) T(); } usedElements = size; } T& back() const { assert(usedElements > 0); return data[usedElements - 1]; } T& pop_back() { assert(usedElements > 0); usedElements--; return data[usedElements]; } void push_back(T item) { if (usedElements == allocatedElements) { reallocate((allocatedElements + 1) * 2); // TODO: optimize } data[usedElements] = item; usedElements++; } T& front() const { assert(usedElements > 0); return data[0]; } void erase(Iterator start_it, Iterator end_it) { assert(start_it.parent == end_it.parent && start_it.parent == this); assert(start_it.index <= end_it.index && end_it.index <= usedElements); size_t size = end_it.index - start_it.index; for (size_t cur = start_it.index; cur + size < usedElements; ++cur) { data[cur] = data[cur + size]; } usedElements -= size; } void erase(Iterator it) { erase(it, it + 1); } void clear() { usedElements = 0; } void reserve(size_t size) { if (size > allocatedElements) { reallocate(size); } } template void set(const ListType& list) { size_t size = list.size(); if (allocatedElements < size) { static_cast(this)->allocate(size); } for (size_t i = 0; i < size; i++) { data[i] = list[i]; } usedElements = size; } void operator=(SubType& other) { set(other); } void swap(SubType& other) { data = other.data; usedElements = other.usedElements; allocatedElements = other.allocatedElements; other.data = nullptr; other.usedElements = other.allocatedElements = 0; } // iteration struct Iterator { using iterator_category = std::random_access_iterator_tag; using value_type = T; using difference_type = std::ptrdiff_t; using pointer = T*; using reference = T&; const SubType* parent; size_t index; Iterator() : parent(nullptr), index(0) {} Iterator(const SubType* parent, size_t index) : parent(parent), index(index) {} bool operator==(const Iterator& other) const { return index == other.index && parent == other.parent; } bool operator!=(const Iterator& other) const { return !(*this == other); } bool operator<(const Iterator& other) const { assert(parent == other.parent); return index < other.index; } bool operator>(const Iterator& other) const { return other < *this; } bool operator<=(const Iterator& other) const { return !(other < *this); } bool operator>=(const Iterator& other) const { return !(*this < other); } Iterator& operator++() { index++; return *this; } Iterator& operator--() { index--; return *this; } Iterator operator++(int) { Iterator it = *this; ++*this; return it; } Iterator operator--(int) { Iterator it = *this; --*this; return it; } Iterator& operator+=(std::ptrdiff_t off) { index += off; return *this; } Iterator& operator-=(std::ptrdiff_t off) { return *this += -off; } Iterator operator+(std::ptrdiff_t off) const { return Iterator(*this) += off; } Iterator operator-(std::ptrdiff_t off) const { return *this + -off; } std::ptrdiff_t operator-(const Iterator& other) const { assert(parent == other.parent); return index - other.index; } friend Iterator operator+(std::ptrdiff_t off, const Iterator& it) { return it + off; } T& operator*() const { return (*parent)[index]; } T& operator[](std::ptrdiff_t off) const { return (*parent)[index + off]; } T* operator->() const { return &(*parent)[index]; } }; Iterator begin() const { return Iterator(static_cast(this), 0); } Iterator end() const { return Iterator(static_cast(this), usedElements); } void allocate(size_t size) { abort(); // must be implemented in children } }; // A vector that has an allocator for arena allocation // // TODO: consider not saving the allocator, but requiring it be // passed in when needed, would make this (and thus Blocks etc. // smaller) template class ArenaVector : public ArenaVectorBase, T> { private: MixedArena& allocator; public: ArenaVector(MixedArena& allocator) : allocator(allocator) {} ArenaVector(ArenaVector&& other) : allocator(other.allocator) { *this = other; } void allocate(size_t size) { this->allocatedElements = size; this->data = static_cast( allocator.allocSpace(sizeof(T) * this->allocatedElements, alignof(T))); } }; #endif // wasm_mixed_arena_h binaryen-version_91/src/parsing.h000066400000000000000000000264071362402614000172730ustar00rootroot00000000000000/* * Copyright 2015 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_parsing_h #define wasm_parsing_h #include #include #include #include #include "asmjs/shared-constants.h" #include "mixed_arena.h" #include "shared-constants.h" #include "support/colors.h" #include "support/utilities.h" #include "wasm-printing.h" #include "wasm.h" namespace wasm { struct ParseException { std::string text; size_t line, col; ParseException() : text("unknown parse error"), line(-1), col(-1) {} ParseException(std::string text) : text(text), line(-1), col(-1) {} ParseException(std::string text, size_t line, size_t col) : text(text), line(line), col(col) {} void dump(std::ostream& o) const { Colors::magenta(o); o << "["; Colors::red(o); o << "parse exception: "; Colors::green(o); o << text; if (line != size_t(-1)) { Colors::normal(o); o << " (at " << line << ":" << col << ")"; } Colors::magenta(o); o << "]"; Colors::normal(o); } }; struct MapParseException { std::string text; MapParseException() : text("unknown parse error") {} MapParseException(std::string text) : text(text) {} void dump(std::ostream& o) const { Colors::magenta(o); o << "["; Colors::red(o); o << "map parse exception: "; Colors::green(o); o << text; Colors::magenta(o); o << "]"; Colors::normal(o); } }; inline Expression* parseConst(cashew::IString s, Type type, MixedArena& allocator) { const char* str = s.str; auto ret = allocator.alloc(); ret->type = type; if (type.isFloat()) { if (s == _INFINITY) { switch (type.getSingle()) { case Type::f32: ret->value = Literal(std::numeric_limits::infinity()); break; case Type::f64: ret->value = Literal(std::numeric_limits::infinity()); break; default: return nullptr; } // std::cerr << "make constant " << str << " ==> " << ret->value << '\n'; return ret; } if (s == NEG_INFINITY) { switch (type.getSingle()) { case Type::f32: ret->value = Literal(-std::numeric_limits::infinity()); break; case Type::f64: ret->value = Literal(-std::numeric_limits::infinity()); break; default: return nullptr; } // std::cerr << "make constant " << str << " ==> " << ret->value << '\n'; return ret; } if (s == _NAN) { switch (type.getSingle()) { case Type::f32: ret->value = Literal(float(std::nan(""))); break; case Type::f64: ret->value = Literal(double(std::nan(""))); break; default: return nullptr; } // std::cerr << "make constant " << str << " ==> " << ret->value << '\n'; return ret; } bool negative = str[0] == '-'; const char* positive = negative ? str + 1 : str; if (!negative) { if (positive[0] == '+') { positive++; } } if (positive[0] == 'n' && positive[1] == 'a' && positive[2] == 'n') { const char* modifier = positive[3] == ':' ? positive + 4 : nullptr; if (!(modifier ? positive[4] == '0' && positive[5] == 'x' : 1)) { throw ParseException("bad nan input"); } switch (type.getSingle()) { case Type::f32: { uint32_t pattern; if (modifier) { std::istringstream istr(modifier); istr >> std::hex >> pattern; if (istr.fail()) { throw ParseException("invalid f32 format"); } pattern |= 0x7f800000U; } else { pattern = 0x7fc00000U; } if (negative) { pattern |= 0x80000000U; } if (!std::isnan(bit_cast(pattern))) { pattern |= 1U; } ret->value = Literal(pattern).castToF32(); break; } case Type::f64: { uint64_t pattern; if (modifier) { std::istringstream istr(modifier); istr >> std::hex >> pattern; if (istr.fail()) { throw ParseException("invalid f64 format"); } pattern |= 0x7ff0000000000000ULL; } else { pattern = 0x7ff8000000000000UL; } if (negative) { pattern |= 0x8000000000000000ULL; } if (!std::isnan(bit_cast(pattern))) { pattern |= 1ULL; } ret->value = Literal(pattern).castToF64(); break; } default: return nullptr; } // std::cerr << "make constant " << str << " ==> " << ret->value << '\n'; return ret; } if (s == NEG_NAN) { switch (type.getSingle()) { case Type::f32: ret->value = Literal(float(-std::nan(""))); break; case Type::f64: ret->value = Literal(double(-std::nan(""))); break; default: return nullptr; } // std::cerr << "make constant " << str << " ==> " << ret->value << '\n'; return ret; } } switch (type.getSingle()) { case Type::i32: { if ((str[0] == '0' && str[1] == 'x') || (str[0] == '-' && str[1] == '0' && str[2] == 'x')) { bool negative = str[0] == '-'; if (negative) { str++; } std::istringstream istr(str); uint32_t temp; istr >> std::hex >> temp; if (istr.fail()) { throw ParseException("invalid i32 format"); } ret->value = Literal(negative ? -temp : temp); } else { std::istringstream istr(str[0] == '-' ? str + 1 : str); uint32_t temp; istr >> temp; if (istr.fail()) { throw ParseException("invalid i32 format"); } ret->value = Literal(str[0] == '-' ? -temp : temp); } break; } case Type::i64: { if ((str[0] == '0' && str[1] == 'x') || (str[0] == '-' && str[1] == '0' && str[2] == 'x')) { bool negative = str[0] == '-'; if (negative) { str++; } std::istringstream istr(str); uint64_t temp; istr >> std::hex >> temp; if (istr.fail()) { throw ParseException("invalid i64 format"); } ret->value = Literal(negative ? -temp : temp); } else { std::istringstream istr(str[0] == '-' ? str + 1 : str); uint64_t temp; istr >> temp; if (istr.fail()) { throw ParseException("invalid i64 format"); } ret->value = Literal(str[0] == '-' ? -temp : temp); } break; } case Type::f32: { char* end; ret->value = Literal(strtof(str, &end)); break; } case Type::f64: { char* end; ret->value = Literal(strtod(str, &end)); break; } case Type::v128: case Type::funcref: case Type::anyref: case Type::nullref: case Type::exnref: WASM_UNREACHABLE("unexpected const type"); case Type::none: case Type::unreachable: { return nullptr; } } if (ret->value.type != type) { throw ParseException("parsed type does not match expected type"); } // std::cerr << "make constant " << str << " ==> " << ret->value << '\n'; return ret; } // Helper for parsers that may not have unique label names. This transforms // the names into unique ones, as required by Binaryen IR. struct UniqueNameMapper { std::vector labelStack; // name in source => stack of uniquified names std::map> labelMappings; std::map reverseLabelMapping; // uniquified name => name in source Index otherIndex = 0; Name getPrefixedName(Name prefix) { if (reverseLabelMapping.find(prefix) == reverseLabelMapping.end()) { return prefix; } // make sure to return a unique name not already on the stack while (1) { Name ret = Name(prefix.str + std::to_string(otherIndex++)); if (reverseLabelMapping.find(ret) == reverseLabelMapping.end()) { return ret; } } } // receives a source name. generates a unique name, pushes it, and returns it Name pushLabelName(Name sName) { Name name = getPrefixedName(sName); labelStack.push_back(name); labelMappings[sName].push_back(name); reverseLabelMapping[name] = sName; return name; } void popLabelName(Name name) { assert(labelStack.back() == name); labelStack.pop_back(); labelMappings[reverseLabelMapping[name]].pop_back(); } Name sourceToUnique(Name sName) { if (labelMappings.find(sName) == labelMappings.end()) { throw ParseException("bad label in sourceToUnique"); } if (labelMappings[sName].empty()) { throw ParseException("use of popped label in sourceToUnique"); } return labelMappings[sName].back(); } Name uniqueToSource(Name name) { if (reverseLabelMapping.find(name) == reverseLabelMapping.end()) { throw ParseException("label mismatch in uniqueToSource"); } return reverseLabelMapping[name]; } void clear() { labelStack.clear(); labelMappings.clear(); reverseLabelMapping.clear(); } // Given an expression, ensures all names are unique static void uniquify(Expression* curr) { struct Walker : public ControlFlowWalker> { UniqueNameMapper mapper; static void doPreVisitControlFlow(Walker* self, Expression** currp) { auto* curr = *currp; if (auto* block = curr->dynCast()) { if (block->name.is()) { block->name = self->mapper.pushLabelName(block->name); } } else if (auto* loop = curr->dynCast()) { if (loop->name.is()) { loop->name = self->mapper.pushLabelName(loop->name); } } } static void doPostVisitControlFlow(Walker* self, Expression** currp) { auto* curr = *currp; if (auto* block = curr->dynCast()) { if (block->name.is()) { self->mapper.popLabelName(block->name); } } else if (auto* loop = curr->dynCast()) { if (loop->name.is()) { self->mapper.popLabelName(loop->name); } } } void visitBreak(Break* curr) { curr->name = mapper.sourceToUnique(curr->name); } void visitBrOnExn(BrOnExn* curr) { curr->name = mapper.sourceToUnique(curr->name); } void visitSwitch(Switch* curr) { for (auto& target : curr->targets) { target = mapper.sourceToUnique(target); } curr->default_ = mapper.sourceToUnique(curr->default_); } }; Walker walker; walker.walk(curr); } }; } // namespace wasm #endif // wasm_parsing_h binaryen-version_91/src/pass.h000066400000000000000000000274031362402614000165730ustar00rootroot00000000000000/* * Copyright 2015 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_pass_h #define wasm_pass_h #include #include "mixed_arena.h" #include "support/utilities.h" #include "wasm-traversal.h" #include "wasm.h" namespace wasm { class Pass; // // Global registry of all passes in /passes/ // struct PassRegistry { PassRegistry(); static PassRegistry* get(); typedef std::function Creator; void registerPass(const char* name, const char* description, Creator create); std::unique_ptr createPass(std::string name); std::vector getRegisteredNames(); std::string getPassDescription(std::string name); private: void registerPasses(); struct PassInfo { std::string description; Creator create; PassInfo() = default; PassInfo(std::string description, Creator create) : description(description), create(create) {} }; std::map passInfos; }; struct InliningOptions { // Function size at which we always inline. // Typically a size so small that after optimizations, the inlined code will // be smaller than the call instruction itself. 2 is a safe number because // there is no risk of things like // (func $reverse (param $x i32) (param $y i32) // (call $something (local.get $y) (local.get $x)) // ) // in which case the reversing of the params means we'll possibly need // a block and a temp local. But that takes at least 3 nodes, and 2 < 3. // More generally, with 2 items we may have a local.get, but no way to // require it to be saved instead of directly consumed. Index alwaysInlineMaxSize = 2; // Function size which we inline when functions are lightweight (no loops // and calls) and we are doing aggressive optimisation for speed (-O3). // In particular it's nice that with this limit we can inline the clamp // functions (i32s-div, f64-to-int, etc.), that can affect perf. Index flexibleInlineMaxSize = 20; // Function size which we inline when there is only one caller. // FIXME: this should logically be higher than flexibleInlineMaxSize. Index oneCallerInlineMaxSize = 15; }; struct PassOptions { // Run passes in debug mode, doing extra validation and timing checks. bool debug = false; // Whether to run the validator to check for errors. bool validate = true; // When validating validate globally and not just locally bool validateGlobally = false; // 0, 1, 2 correspond to -O0, -O1, -O2, etc. int optimizeLevel = 0; // 0, 1, 2 correspond to -O0, -Os, -Oz int shrinkLevel = 0; // Tweak thresholds for the Inlining pass. InliningOptions inlining; // Optimize assuming things like div by 0, bad load/store, will not trap. bool ignoreImplicitTraps = false; // Optimize assuming that the low 1K of memory is not valid memory for the // application to use. In that case, we can optimize load/store offsets in // many cases. bool lowMemoryUnused = false; enum { LowMemoryBound = 1024 }; // Whether to try to preserve debug info through, which are special calls. bool debugInfo = false; // Arbitrary string arguments from the commandline, which we forward to // passes. std::map arguments; void setDefaultOptimizationOptions() { // -Os is our default optimizeLevel = 2; shrinkLevel = 1; } static PassOptions getWithDefaultOptimizationOptions() { PassOptions ret; ret.setDefaultOptimizationOptions(); return ret; } static PassOptions getWithoutOptimization() { return PassOptions(); // defaults are to not optimize } std::string getArgument(std::string key, std::string errorTextIfMissing) { if (arguments.count(key) == 0) { Fatal() << errorTextIfMissing; } return arguments[key]; } std::string getArgumentOrDefault(std::string key, std::string default_) { if (arguments.count(key) == 0) { return default_; } return arguments[key]; } }; // // Runs a set of passes, in order // struct PassRunner { Module* wasm; MixedArena* allocator; std::vector> passes; PassOptions options; PassRunner(Module* wasm) : wasm(wasm), allocator(&wasm->allocator) {} PassRunner(Module* wasm, PassOptions options) : wasm(wasm), allocator(&wasm->allocator), options(options) {} // no copying, we control |passes| PassRunner(const PassRunner&) = delete; PassRunner& operator=(const PassRunner&) = delete; void setOptions(PassOptions newOptions) { options = newOptions; } void setDebug(bool debug) { options.debug = debug; // validate everything by default if debugging options.validateGlobally = debug; } void setDebugInfo(bool debugInfo) { options.debugInfo = debugInfo; } void setValidateGlobally(bool validate) { options.validateGlobally = validate; } // Add a pass using its name. void add(std::string passName) { auto pass = PassRegistry::get()->createPass(passName); if (!pass) { Fatal() << "Could not find pass: " << passName << "\n"; } doAdd(std::move(pass)); } // Add a pass given an instance. template void add(std::unique_ptr

pass) { doAdd(std::move(pass)); } // Adds the default set of optimization passes; this is // what -O does. void addDefaultOptimizationPasses(); // Adds the default optimization passes that work on // individual functions. void addDefaultFunctionOptimizationPasses(); // Adds the default optimization passes that work on // entire modules as a whole, and make sense to // run before function passes. void addDefaultGlobalOptimizationPrePasses(); // Adds the default optimization passes that work on // entire modules as a whole, and make sense to // run after function passes. // This is run at the very end of the optimization // process - you can assume no other opts will be run // afterwards. void addDefaultGlobalOptimizationPostPasses(); // Run the passes on the module void run(); // Run the passes on a specific function void runOnFunction(Function* func); // Get the last pass that was already executed of a certain type. template P* getLast(); // When running a pass runner within another pass runner, this // flag should be set. This influences how pass debugging works, // and may influence other things in the future too. void setIsNested(bool nested) { isNested = nested; } // BINARYEN_PASS_DEBUG is a convenient commandline way to log out the toplevel // passes, their times, and validate between each pass. // (we don't recurse pass debug into sub-passes, as it // doesn't help anyhow and also is bad for e.g. printing // which is a pass) // this method returns whether we are in passDebug mode, and which value: // 1: run pass by pass, validating in between // 2: also save the last pass, so it breakage happens we can print the last // one 3: also dump out byn-* files for each pass static int getPassDebug(); protected: bool isNested = false; private: void doAdd(std::unique_ptr pass); void runPass(Pass* pass); void runPassOnFunction(Pass* pass, Function* func); // After running a pass, handle any changes due to // how the pass is defined, such as clearing away any // temporary data structures that the pass declares it // invalidates. // If a function is passed, we operate just on that function; // otherwise, the whole module. void handleAfterEffects(Pass* pass, Function* func = nullptr); }; // // Core pass class // class Pass { public: virtual ~Pass() = default; // Override this to perform preparation work before the pass runs. // This will be called before the pass is run on a module. virtual void prepareToRun(PassRunner* runner, Module* module) {} // Implement this with code to run the pass on the whole module virtual void run(PassRunner* runner, Module* module) { WASM_UNREACHABLE("unimplemented"); } // Implement this with code to run the pass on a single function, for // a function-parallel pass virtual void runOnFunction(PassRunner* runner, Module* module, Function* function) { WASM_UNREACHABLE("unimplemented"); } // Function parallelism. By default, passes are not run in parallel, but you // can override this method to say that functions are parallelizable. This // should always be safe *unless* you do something in the pass that makes it // not thread-safe; in other words, the Module and Function objects and // so forth are set up so that Functions can be processed in parallel, so // if you do not add global state that could be raced on, your pass could be // function-parallel. // // Function-parallel passes create an instance of the Walker class per // function. That means that you can't rely on Walker object properties to // persist across your functions, and you can't expect a new object to be // created for each function either (which could be very inefficient). // // It is valid for function-parallel passes to read (but not modify) global // module state, like globals or imports. However, reading other functions' // contents is invalid, as function-parallel tests can be run while still // adding functions to the module. virtual bool isFunctionParallel() { return false; } // This method is used to create instances per function for a // function-parallel pass. You may need to override this if you subclass a // Walker, as otherwise this will create the parent class. virtual Pass* create() { WASM_UNREACHABLE("unimplenented"); } // Whether this pass modifies the Binaryen IR in the module. This is true for // most passes, except for passes that have no side effects, or passes that // only modify other things than Binaryen IR (for example, the Stack IR // passes only modify that IR). // This property is important as if Binaryen IR is modified, we need to throw // out any Stack IR - it would need to be regenerated and optimized. virtual bool modifiesBinaryenIR() { return true; } std::string name; protected: Pass() = default; Pass(Pass&) = default; Pass& operator=(const Pass&) = delete; }; // // Core pass class that uses AST walking. This class can be parameterized by // different types of AST walkers. // template class WalkerPass : public Pass, public WalkerType { PassRunner* runner; protected: typedef WalkerPass super; public: void run(PassRunner* runner, Module* module) override { // Parallel pass running is implemented in the PassRunner. if (isFunctionParallel()) { PassRunner runner(module); runner.setIsNested(true); std::unique_ptr copy; copy.reset(create()); runner.add(std::move(copy)); runner.run(); return; } // Single-thread running just calls the walkModule traversal. setPassRunner(runner); WalkerType::setModule(module); WalkerType::walkModule(module); } void runOnFunction(PassRunner* runner, Module* module, Function* func) override { setPassRunner(runner); WalkerType::setModule(module); WalkerType::walkFunction(func); } PassRunner* getPassRunner() { return runner; } PassOptions& getPassOptions() { return runner->options; } void setPassRunner(PassRunner* runner_) { runner = runner_; } }; } // namespace wasm #endif // wasm_pass_h binaryen-version_91/src/passes/000077500000000000000000000000001362402614000167445ustar00rootroot00000000000000binaryen-version_91/src/passes/AlignmentLowering.cpp000066400000000000000000000203161362402614000230770ustar00rootroot00000000000000/* * Copyright 2017 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // // Lowers unaligned loads and stores into aligned loads and stores // that are smaller. This leaves only aligned operations. // #include "ir/bits.h" #include "pass.h" #include "wasm-builder.h" #include "wasm.h" namespace wasm { struct AlignmentLowering : public WalkerPass> { void visitLoad(Load* curr) { if (curr->align == 0 || curr->align == curr->bytes) { return; } Builder builder(*getModule()); if (curr->type == Type::unreachable) { replaceCurrent(curr->ptr); return; } assert(curr->type == Type::i32); // TODO: i64, f32, f64 auto temp = builder.addVar(getFunction(), Type::i32); Expression* ret; if (curr->bytes == 2) { ret = builder.makeBinary( OrInt32, builder.makeLoad(1, false, curr->offset, 1, builder.makeLocalGet(temp, Type::i32), Type::i32), builder.makeBinary( ShlInt32, builder.makeLoad(1, false, curr->offset + 1, 1, builder.makeLocalGet(temp, Type::i32), Type::i32), builder.makeConst(Literal(int32_t(8))))); if (curr->signed_) { ret = Bits::makeSignExt(ret, 2, *getModule()); } } else if (curr->bytes == 4) { if (curr->align == 1) { ret = builder.makeBinary( OrInt32, builder.makeBinary( OrInt32, builder.makeLoad(1, false, curr->offset, 1, builder.makeLocalGet(temp, Type::i32), Type::i32), builder.makeBinary( ShlInt32, builder.makeLoad(1, false, curr->offset + 1, 1, builder.makeLocalGet(temp, Type::i32), Type::i32), builder.makeConst(Literal(int32_t(8))))), builder.makeBinary( OrInt32, builder.makeBinary( ShlInt32, builder.makeLoad(1, false, curr->offset + 2, 1, builder.makeLocalGet(temp, Type::i32), Type::i32), builder.makeConst(Literal(int32_t(16)))), builder.makeBinary( ShlInt32, builder.makeLoad(1, false, curr->offset + 3, 1, builder.makeLocalGet(temp, Type::i32), Type::i32), builder.makeConst(Literal(int32_t(24)))))); } else if (curr->align == 2) { ret = builder.makeBinary( OrInt32, builder.makeLoad(2, false, curr->offset, 2, builder.makeLocalGet(temp, Type::i32), Type::i32), builder.makeBinary( ShlInt32, builder.makeLoad(2, false, curr->offset + 2, 2, builder.makeLocalGet(temp, Type::i32), Type::i32), builder.makeConst(Literal(int32_t(16))))); } else { WASM_UNREACHABLE("invalid alignment"); } } else { WASM_UNREACHABLE("invalid size"); } replaceCurrent( builder.makeBlock({builder.makeLocalSet(temp, curr->ptr), ret})); } void visitStore(Store* curr) { if (curr->align == 0 || curr->align == curr->bytes) { return; } Builder builder(*getModule()); if (curr->type == Type::unreachable) { replaceCurrent(builder.makeBlock( {builder.makeDrop(curr->ptr), builder.makeDrop(curr->value)})); return; } assert(curr->value->type == Type::i32); // TODO: i64, f32, f64 auto tempPtr = builder.addVar(getFunction(), Type::i32); auto tempValue = builder.addVar(getFunction(), Type::i32); auto* block = builder.makeBlock({builder.makeLocalSet(tempPtr, curr->ptr), builder.makeLocalSet(tempValue, curr->value)}); if (curr->bytes == 2) { block->list.push_back( builder.makeStore(1, curr->offset, 1, builder.makeLocalGet(tempPtr, Type::i32), builder.makeLocalGet(tempValue, Type::i32), Type::i32)); block->list.push_back(builder.makeStore( 1, curr->offset + 1, 1, builder.makeLocalGet(tempPtr, Type::i32), builder.makeBinary(ShrUInt32, builder.makeLocalGet(tempValue, Type::i32), builder.makeConst(Literal(int32_t(8)))), Type::i32)); } else if (curr->bytes == 4) { if (curr->align == 1) { block->list.push_back( builder.makeStore(1, curr->offset, 1, builder.makeLocalGet(tempPtr, Type::i32), builder.makeLocalGet(tempValue, Type::i32), Type::i32)); block->list.push_back(builder.makeStore( 1, curr->offset + 1, 1, builder.makeLocalGet(tempPtr, Type::i32), builder.makeBinary(ShrUInt32, builder.makeLocalGet(tempValue, Type::i32), builder.makeConst(Literal(int32_t(8)))), Type::i32)); block->list.push_back(builder.makeStore( 1, curr->offset + 2, 1, builder.makeLocalGet(tempPtr, Type::i32), builder.makeBinary(ShrUInt32, builder.makeLocalGet(tempValue, Type::i32), builder.makeConst(Literal(int32_t(16)))), Type::i32)); block->list.push_back(builder.makeStore( 1, curr->offset + 3, 1, builder.makeLocalGet(tempPtr, Type::i32), builder.makeBinary(ShrUInt32, builder.makeLocalGet(tempValue, Type::i32), builder.makeConst(Literal(int32_t(24)))), Type::i32)); } else if (curr->align == 2) { block->list.push_back( builder.makeStore(2, curr->offset, 2, builder.makeLocalGet(tempPtr, Type::i32), builder.makeLocalGet(tempValue, Type::i32), Type::i32)); block->list.push_back(builder.makeStore( 2, curr->offset + 2, 2, builder.makeLocalGet(tempPtr, Type::i32), builder.makeBinary(ShrUInt32, builder.makeLocalGet(tempValue, Type::i32), builder.makeConst(Literal(int32_t(16)))), Type::i32)); } else { WASM_UNREACHABLE("invalid alignment"); } } else { WASM_UNREACHABLE("invalid size"); } block->finalize(); replaceCurrent(block); } }; Pass* createAlignmentLoweringPass() { return new AlignmentLowering(); } } // namespace wasm binaryen-version_91/src/passes/Asyncify.cpp000066400000000000000000001573251362402614000212520ustar00rootroot00000000000000/* * Copyright 2019 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // // Asyncify: async/await style code transformation, that allows pausing // and resuming. This lets a language support synchronous operations in // an async manner, for example, you can do a blocking wait, and that will // be turned into code that unwinds the stack at the "blocking" operation, // then is able to rewind it back up when the actual async operation // comples, the so the code appears to have been running synchronously // all the while. Use cases for this include coroutines, python generators, // etc. // // The approach here is a third-generation design after Emscripten's original // Asyncify and then Emterpreter-Async approaches: // // * Old Asyncify rewrote control flow in LLVM IR. A problem is that this // needs to save all SSA registers as part of the local state, which can be // very costly. A further increase can happen because of phis that are // added because of control flow transformations. As a result we saw // pathological cases where the code size increase was unacceptable. // * Emterpreter-Async avoids any risk of code size increase by running it // in a little bytecode VM, which also makes pausing/resuming trivial. // Speed is the main downside here; however, the approach did prove that by // *selectively* emterpretifying only certain code, many projects can run // at full speed - often just the "main loop" must be emterpreted, while // high-speed code that it calls, and in which cannot be an async operation, // remain at full speed. // // New Asyncify's design learns from both of those: // // * The code rewrite is done in Binaryen, that is, at the wasm level. At // this level we will only need to save wasm locals, which is a much smaller // number than LLVM's SSA registers. // * We aim for low but non-zero overhead. Low overhead is very important // for obvious reasons, while Emterpreter-Async proved it is tolerable to // have *some* overhead, if the transform can be applied selectively. // // The specific transform implemented here is nicknamed "Bysyncify" (as it is // in BinarYen, and "B" comes after "A"). It is simpler than old Asyncify but // has low overhead when properly optimized. Old Asyncify worked at the CFG // level and added branches there; new Asyncify on the other hand works on the // structured control flow of wasm and simply "skips over" code when rewinding // the stack, and jumps out when unwinding. The transformed code looks // conceptually like this: // // void foo(int x) { // // new prelude // if (rewinding) { // loadLocals(); // } // // main body starts here // if (!rewinding) { // // some code we must skip while rewinding // x = x + 1; // x = x / 2; // } // // If rewinding, do this call only if it is the right one. // if (!rewinding or nextCall == 0) { // bar(x); // if (unwinding) { // noteUnWound(0); // saveLocals(); // return; // } // } // if (!rewinding) { // // more code we must skip while rewinding // while (x & 7) { // x = x + 1; // } // } // [..] // // The general idea is that while rewinding we just "keep going forward", // skipping code we should not run. That is, instead of jumping directly // to the right place, we have ifs that skip along instead. The ifs for // rewinding and unwinding should be well-predicted, so the overhead should // not be very high. However, we do need to reach the right location via // such skipping, which means that in a very large function the rewind // takes some extra time. On the other hand, though, code that cannot // unwind or rewind (like that loop near the end) can run at full speed. // Overall, this should allow good performance with small overhead that is // mostly noticed at rewind time. // // After this pass is run a new i32 global "__asyncify_state" is added, which // has the following values: // // 0: normal execution // 1: unwinding the stack // 2: rewinding the stack // // There is also "__asyncify_data" which when rewinding and unwinding // contains a pointer to a data structure with the info needed to rewind // and unwind: // // { // offsets // i32 - current asyncify stack location // 0 // i32 - asyncify stack end // 4 // } // // The asyncify stack is a representation of the call frame, as a list of // indexes of calls. In the example above, we saw index "0" for calling "bar" // from "foo". When unwinding, the indexes are added to the stack; when // rewinding, they are popped off; the current asyncify stack location is // undated while doing both operations. The asyncify stack is also used to // save locals. Note that the stack end location is provided, which is for // error detection. // // Note: all pointers are assumed to be 4-byte aligned. // // When you start an unwinding operation, you must set the initial fields // of the data structure, that is, set the current stack location to the // proper place, and the end to the proper end based on how much memory // you reserved. Note that asyncify will grow the stack up. // // The pass will also create four functions that let you control unwinding // and rewinding: // // * asyncify_start_unwind(data : i32): call this to start unwinding the // stack from the current location. "data" must point to a data // structure as described above (with fields containing valid data). // // * asyncify_stop_unwind(): call this to note that unwinding has // concluded. If no other code will run before you start to rewind, // this is not strictly necessary, however, if you swap between // coroutines, or even just want to run some normal code during a // "sleep", then you must call this at the proper time. Otherwise, // the code will think it is still unwinding when it should not be, // which means it will keep unwinding in a meaningless way. // // * asyncify_start_rewind(data : i32): call this to start rewinding the // stack vack up to the location stored in the provided data. This prepares // for the rewind; to start it, you must call the first function in the // call stack to be unwound. // // * asyncify_stop_rewind(): call this to note that rewinding has // concluded, and normal execution can resume. // // These four functions are exported so that you can call them from the // outside. If you want to manage things from inside the wasm, then you // couldn't have called them before they were created by this pass. To work // around that, you can create imports to asyncify.start_unwind, // asyncify.stop_unwind, asyncify.start_rewind, and asyncify.stop_rewind; // if those exist when this pass runs then it will turn those into direct // calls to the functions that it creates. Note that when doing everything // in wasm like this, Asyncify must not instrument your "runtime" support // code, that is, the code that initiates unwinds and rewinds and stops them. // If it did, the unwind would not stop until you left the wasm module // entirely, etc. Therefore we do not instrument a function if it has // a call to the four asyncify_* methods. Note that you may need to disable // inlining if that would cause code that does need to be instrumented // show up in that runtime code. // // To use this API, call asyncify_start_unwind when you want to. The call // stack will then be unwound, and so execution will resume in the JS or // other host environment on the outside that called into wasm. When you // return there after unwinding, call asyncify_stop_unwind. Then when // you want to rewind, call asyncify_start_rewind, and then call the same // initial function you called before, so that unwinding can begin. The // unwinding will reach the same function from which you started, since // we are recreating the call stack. At that point you should call // asyncify_stop_rewind and then execution can resume normally. // // Note that the asyncify_* API calls will verify that the data structure // is valid, checking if the current location is past the end. If so, they // will throw a wasm unreachable. No check is done during unwinding or // rewinding, to keep things fast - in principle, from when unwinding begins // and until it ends there should be no memory accesses aside from the // asyncify code itself (and likewise when rewinding), so there should be // no chance of memory corruption causing odd errors. However, the low // overhead of this approach does cause an error only to be shown when // unwinding/rewinding finishes, and not at the specific spot where the // stack end was exceeded. // // By default this pass is very carefuly: it assumes that any import and // any indirect call may start an unwind/rewind operation. If you know more // specific information you can inform asyncify about that, which can reduce // a great deal of overhead, as it can instrument less code. The relevant // options to wasm-opt etc. are: // // --pass-arg=asyncify-imports@module1.base1,module2.base2,module3.base3 // // Each module.base in that comma-separated list will be considered to // be an import that can unwind/rewind, and all others are assumed not to // (aside from the asyncify.* imports, which are always assumed to). // Each entry can end in a '*' in which case it is matched as a prefix. // // The list of imports can be a response file (which is convenient if it // is long, or you don't want to bother escaping it on the commandline // etc.), e.g. --pass-arg=asyncify-imports@@file.txt will load the // contents of file.txt (note the double "@@" - the first is the // separator for --pass-arg, and the second is the usual convention for // indicating a response file). // // --pass-arg=asyncify-ignore-imports // // Ignore all imports (except for bynsyncify.*), that is, assume none of // them can start an unwind/rewind. (This is effectively the same as // providing asyncify-imports with a list of non-existent imports.) // // --pass-arg=asyncify-ignore-indirect // // Ignore all indirect calls. This implies that you know an call stack // will never need to be unwound with an indirect call somewhere in it. // If that is true for your codebase, then this can be extremely useful // as otherwise it looks like any indirect call can go to a lot of places. // // --pass-arg=asyncify-blacklist@name1,name2,name3 // // If the blacklist is provided, then the functions in it will not be // instrumented even if it looks like they need to. This can be useful // if you know things the whole-program analysis doesn't, like if you // know certain indirect calls are safe and won't unwind. But if you // get the list wrong things will break (and in a production build user // input might reach code paths you missed during testing, so it's hard // to know you got this right), so this is not recommended unless you // really know what are doing, and need to optimize every bit of speed // and size. '*' wildcard matching supported. // // As with --asyncify-imports, you can use a response file here. // // --pass-arg=asyncify-whitelist@name1,name2,name3 // // If the whitelist is provided, then only the functions in the list // will be instrumented. Like the blacklist, getting this wrong will // break your application. '*' wildcard matching supported. // // As with --asyncify-imports, you can use a response file here. // // --pass-arg=asyncify-asserts // // This enables extra asserts in the output, like checking if we in // an unwind/rewind in an invalid place (this can be helpful for manual // tweaking of the whitelist/blacklist). // // TODO When wasm has GC, extending the live ranges of locals can keep things // alive unnecessarily. We may want to set locals to null at the end // of their original range. // #include "ir/effects.h" #include "ir/find_all.h" #include "ir/literal-utils.h" #include "ir/memory-utils.h" #include "ir/module-utils.h" #include "ir/utils.h" #include "pass.h" #include "support/file.h" #include "support/string.h" #include "wasm-builder.h" #include "wasm.h" namespace wasm { namespace { static const Name ASYNCIFY_STATE = "__asyncify_state"; static const Name ASYNCIFY_DATA = "__asyncify_data"; static const Name ASYNCIFY_START_UNWIND = "asyncify_start_unwind"; static const Name ASYNCIFY_STOP_UNWIND = "asyncify_stop_unwind"; static const Name ASYNCIFY_START_REWIND = "asyncify_start_rewind"; static const Name ASYNCIFY_STOP_REWIND = "asyncify_stop_rewind"; static const Name ASYNCIFY_UNWIND = "__asyncify_unwind"; static const Name ASYNCIFY = "asyncify"; static const Name START_UNWIND = "start_unwind"; static const Name STOP_UNWIND = "stop_unwind"; static const Name START_REWIND = "start_rewind"; static const Name STOP_REWIND = "stop_rewind"; static const Name ASYNCIFY_GET_CALL_INDEX = "__asyncify_get_call_index"; static const Name ASYNCIFY_CHECK_CALL_INDEX = "__asyncify_check_call_index"; // TODO: having just normal/unwind_or_rewind would decrease code // size, but make debugging harder enum class State { Normal = 0, Unwinding = 1, Rewinding = 2 }; enum class DataOffset { BStackPos = 0, BStackEnd = 4 }; const auto STACK_ALIGN = 4; // A helper class for managing fake global names. Creates the globals and // provides mappings for using them. class GlobalHelper { Module& module; public: GlobalHelper(Module& module) : module(module) { map[Type::i32] = "asyncify_fake_call_global_i32"; map[Type::i64] = "asyncify_fake_call_global_i64"; map[Type::f32] = "asyncify_fake_call_global_f32"; map[Type::f64] = "asyncify_fake_call_global_f64"; Builder builder(module); for (auto& pair : map) { auto type = pair.first; auto name = pair.second; rev[name] = type; module.addGlobal(builder.makeGlobal( name, type, LiteralUtils::makeZero(type, module), Builder::Mutable)); } } ~GlobalHelper() { for (auto& pair : map) { auto name = pair.second; module.removeGlobal(name); } } Name getName(Type type) { return map.at(type); } Type getTypeOrNone(Name name) { auto iter = rev.find(name); if (iter != rev.end()) { return iter->second; } return Type::none; } private: std::map map; std::map rev; }; class PatternMatcher { public: std::string designation; std::set names; std::set patterns; std::set patternsMatched; std::map unescaped; PatternMatcher(std::string designation, Module& module, const String::Split& list) : designation(designation) { // The lists contain human-readable strings. Turn them into the // internal escaped names for later comparisons for (auto& name : list) { auto escaped = WasmBinaryBuilder::escape(name); unescaped[escaped.str] = name; if (name.find('*') != std::string::npos) { patterns.insert(escaped.str); } else { auto* func = module.getFunctionOrNull(escaped); if (!func) { std::cerr << "warning: Asyncify " << designation << "list contained a non-existing function name: " << name << " (" << escaped << ")\n"; } else if (func->imported()) { Fatal() << "Asyncify " << designation << "list contained an imported function name (use the import " "list for imports): " << name << '\n'; } names.insert(escaped.str); } } } bool match(Name funcName) { if (names.count(funcName) > 0) { return true; } else { for (auto& pattern : patterns) { if (String::wildcardMatch(pattern, funcName.str)) { patternsMatched.insert(pattern); return true; } } } return false; } void checkPatternsMatches() { for (auto& pattern : patterns) { if (patternsMatched.count(pattern) == 0) { std::cerr << "warning: Asyncify " << designation << "list contained a non-matching pattern: " << unescaped[pattern] << " (" << pattern << ")\n"; } } } }; // Analyze the entire module to see which calls may change the state, that // is, start an unwind or rewind), either in itself or in something called // by it. // Handles global module management, needed from the various parts of this // transformation. class ModuleAnalyzer { Module& module; bool canIndirectChangeState; struct Info : public ModuleUtils::CallGraphPropertyAnalysis::FunctionInfo { // If this function can start an unwind/rewind. bool canChangeState = false; // If this function is part of the runtime that receives an unwinding // and starts a rewinding. If so, we do not instrument it, see above. // This is only relevant when handling things entirely inside wasm, // as opposed to imports. bool isBottomMostRuntime = false; // If this function is part of the runtime that starts an unwinding // and stops a rewinding. If so, we do not instrument it, see above. // The difference between the top-most and bottom-most runtime is that // the top-most part is still marked as changing the state; so things // that call it are instrumented. This is not done for the bottom. bool isTopMostRuntime = false; bool inBlacklist = false; }; typedef std::map Map; Map map; public: ModuleAnalyzer(Module& module, std::function canImportChangeState, bool canIndirectChangeState, const String::Split& blacklistInput, const String::Split& whitelistInput, bool asserts) : module(module), canIndirectChangeState(canIndirectChangeState), globals(module), asserts(asserts) { PatternMatcher blacklist("black", module, blacklistInput); PatternMatcher whitelist("white", module, whitelistInput); // Scan to see which functions can directly change the state. // Also handle the asyncify imports, removing them (as we will implement // them later), and replace calls to them with calls to the later proper // name. ModuleUtils::CallGraphPropertyAnalysis scanner( module, [&](Function* func, Info& info) { if (func->imported()) { // The relevant asyncify imports can definitely change the state. if (func->module == ASYNCIFY && (func->base == START_UNWIND || func->base == STOP_REWIND)) { info.canChangeState = true; } else { info.canChangeState = canImportChangeState(func->module, func->base); } return; } struct Walker : PostWalker { void visitCall(Call* curr) { if (curr->isReturn) { Fatal() << "tail calls not yet supported in asyncify"; } auto* target = module->getFunction(curr->target); if (target->imported() && target->module == ASYNCIFY) { // Redirect the imports to the functions we'll add later. if (target->base == START_UNWIND) { curr->target = ASYNCIFY_START_UNWIND; info->canChangeState = true; info->isTopMostRuntime = true; } else if (target->base == STOP_UNWIND) { curr->target = ASYNCIFY_STOP_UNWIND; info->isBottomMostRuntime = true; } else if (target->base == START_REWIND) { curr->target = ASYNCIFY_START_REWIND; info->isBottomMostRuntime = true; } else if (target->base == STOP_REWIND) { curr->target = ASYNCIFY_STOP_REWIND; info->canChangeState = true; info->isTopMostRuntime = true; } else { Fatal() << "call to unidenfied asyncify import: " << target->base; } } } void visitCallIndirect(CallIndirect* curr) { if (curr->isReturn) { Fatal() << "tail calls not yet supported in asyncify"; } if (canIndirectChangeState) { info->canChangeState = true; } // TODO optimize the other case, at least by type } Info* info; Module* module; ModuleAnalyzer* analyzer; bool canIndirectChangeState; }; Walker walker; walker.info = &info; walker.module = &module; walker.analyzer = this; walker.canIndirectChangeState = canIndirectChangeState; walker.walk(func->body); if (info.isBottomMostRuntime) { info.canChangeState = false; // TODO: issue warnings on suspicious things, like a function in // the bottom-most runtime also doing top-most runtime stuff // like starting and unwinding. } }); // Functions in the blacklist are assumed to not change the state. for (auto& pair : scanner.map) { auto* func = pair.first; auto& info = pair.second; if (blacklist.match(func->name)) { info.inBlacklist = true; info.canChangeState = false; } } // Remove the asyncify imports, if any, and any calls to them. std::vector funcsToDelete; for (auto& pair : scanner.map) { auto* func = pair.first; auto& callsTo = pair.second.callsTo; if (func->imported() && func->module == ASYNCIFY) { funcsToDelete.push_back(func->name); } std::vector callersToDelete; for (auto* target : callsTo) { if (target->imported() && target->module == ASYNCIFY) { callersToDelete.push_back(target); } } for (auto* target : callersToDelete) { callsTo.erase(target); } } for (auto name : funcsToDelete) { module.removeFunction(name); } scanner.propagateBack([](const Info& info) { return info.canChangeState; }, [](const Info& info) { return !info.isBottomMostRuntime && !info.inBlacklist; }, [](Info& info) { info.canChangeState = true; }, scanner.IgnoreIndirectCalls); map.swap(scanner.map); if (!whitelistInput.empty()) { // Only the functions in the whitelist can change the state. for (auto& func : module.functions) { if (!func->imported()) { map[func.get()].canChangeState = whitelist.match(func->name); } } } blacklist.checkPatternsMatches(); whitelist.checkPatternsMatches(); } bool needsInstrumentation(Function* func) { auto& info = map[func]; return info.canChangeState && !info.isTopMostRuntime; } bool canChangeState(Expression* curr) { // Look inside to see if we call any of the things we know can change the // state. // TODO: caching, this is O(N^2) struct Walker : PostWalker { void visitCall(Call* curr) { // We only implement these at the very end, but we know that they // definitely change the state. if (curr->target == ASYNCIFY_START_UNWIND || curr->target == ASYNCIFY_STOP_REWIND || curr->target == ASYNCIFY_GET_CALL_INDEX || curr->target == ASYNCIFY_CHECK_CALL_INDEX) { canChangeState = true; return; } if (curr->target == ASYNCIFY_STOP_UNWIND || curr->target == ASYNCIFY_START_REWIND) { isBottomMostRuntime = true; return; } // The target may not exist if it is one of our temporary intrinsics. auto* target = module->getFunctionOrNull(curr->target); if (target && (*map)[target].canChangeState) { canChangeState = true; } } void visitCallIndirect(CallIndirect* curr) { if (canIndirectChangeState) { canChangeState = true; } // TODO optimize the other case, at least by type } Module* module; ModuleAnalyzer* analyzer; Map* map; bool canIndirectChangeState; bool canChangeState = false; bool isBottomMostRuntime = false; }; Walker walker; walker.module = &module; walker.analyzer = this; walker.map = ↦ walker.canIndirectChangeState = canIndirectChangeState; walker.walk(curr); if (walker.isBottomMostRuntime) { walker.canChangeState = false; } return walker.canChangeState; } GlobalHelper globals; bool asserts; }; // Checks if something performs a call: either a direct or indirect call, // and perhaps it is dropped or assigned to a local. This captures all the // cases of a call in flat IR. static bool doesCall(Expression* curr) { if (auto* set = curr->dynCast()) { curr = set->value; } else if (auto* drop = curr->dynCast()) { curr = drop->value; } return curr->is() || curr->is(); } class AsyncifyBuilder : public Builder { public: AsyncifyBuilder(Module& wasm) : Builder(wasm) {} Expression* makeGetStackPos() { return makeLoad(4, false, int32_t(DataOffset::BStackPos), 4, makeGlobalGet(ASYNCIFY_DATA, Type::i32), Type::i32); } Expression* makeIncStackPos(int32_t by) { if (by == 0) { return makeNop(); } return makeStore( 4, int32_t(DataOffset::BStackPos), 4, makeGlobalGet(ASYNCIFY_DATA, Type::i32), makeBinary(AddInt32, makeGetStackPos(), makeConst(Literal(by))), Type::i32); } Expression* makeStateCheck(State value) { return makeBinary(EqInt32, makeGlobalGet(ASYNCIFY_STATE, Type::i32), makeConst(Literal(int32_t(value)))); } Expression* makeNegatedStateCheck(State value) { return makeUnary(EqZInt32, makeStateCheck(value)); } }; // Instrument control flow, around calls and adding skips for rewinding. struct AsyncifyFlow : public Pass { bool isFunctionParallel() override { return true; } ModuleAnalyzer* analyzer; AsyncifyFlow* create() override { return new AsyncifyFlow(analyzer); } AsyncifyFlow(ModuleAnalyzer* analyzer) : analyzer(analyzer) {} void runOnFunction(PassRunner* runner, Module* module_, Function* func_) override { module = module_; func = func_; builder = make_unique(*module); // If the function cannot change our state, we have nothing to do - // we will never unwind or rewind the stack here. if (!analyzer->needsInstrumentation(func)) { if (analyzer->asserts) { addAssertsInNonInstrumented(func); } return; } // Rewrite the function body. // Each function we enter will pop one from the stack, which is the index // of the next call to make. auto* block = builder->makeBlock( {builder->makeIf(builder->makeStateCheck( State::Rewinding), // TODO: such checks can be !normal makeCallIndexPop()), process(func->body)}); if (func->sig.results != Type::none) { // Rewriting control flow may alter things; make sure the function ends in // something valid (which the optimizer can remove later). block->list.push_back(builder->makeUnreachable()); } block->finalize(); func->body = block; // Making things like returns conditional may alter types. ReFinalize().walkFunctionInModule(func, module); } private: std::unique_ptr builder; Module* module; Function* func; // Each call in the function has an index, noted during unwind and checked // during rewind. Index callIndex = 0; Expression* process(Expression* curr) { if (!analyzer->canChangeState(curr)) { return makeMaybeSkip(curr); } // The IR is in flat form, which makes this much simpler: there are no // unnecessarily nested side effects or control flow, so we can add // skips for rewinding in an easy manner, putting a single if around // whole chunks of code. Also calls are separated out so that it is easy // to add the necessary checks for them for unwinding/rewinding support. // // Aside from that, we must "linearize" all control flow so that we can // reach the right part when rewinding, which is done by always skipping // forward. For example, for an if we do this: // // if (condition()) { // side1(); // } else { // side2(); // } // => // if (!rewinding) { // temp = condition(); // } // if (rewinding || temp) { // side1(); // } // if (rewinding || !temp) { // side2(); // } // // This way we will linearly get through all the code in the function, // if we are rewinding. In a similar way we skip over breaks, etc.; just // "keep on truckin'". // // Note that temp locals added in this way are just normal locals, and in // particular they are saved and loaded. That way if we resume from the // first if arm we will avoid the second. if (auto* block = curr->dynCast()) { // At least one of our children may change the state. Clump them as // necessary. Index i = 0; auto& list = block->list; while (i < list.size()) { if (analyzer->canChangeState(list[i])) { list[i] = process(list[i]); i++; } else { Index end = i + 1; while (end < list.size() && !analyzer->canChangeState(list[end])) { end++; } // We have a range of [i, end) in which the state cannot change, // so all we need to do is skip it if rewinding. if (end == i + 1) { list[i] = makeMaybeSkip(list[i]); } else { auto* block = builder->makeBlock(); for (auto j = i; j < end; j++) { block->list.push_back(list[j]); } block->finalize(); list[i] = makeMaybeSkip(block); for (auto j = i + 1; j < end; j++) { list[j] = builder->makeNop(); } } i = end; } } return block; } else if (auto* iff = curr->dynCast()) { // The state change cannot be in the condition due to flat form, so it // must be in one of the children. assert(!analyzer->canChangeState(iff->condition)); // We must linearize this, which means we pass through both arms if we // are rewinding. if (!iff->ifFalse) { iff->condition = builder->makeBinary( OrInt32, iff->condition, builder->makeStateCheck(State::Rewinding)); iff->ifTrue = process(iff->ifTrue); iff->finalize(); return iff; } auto conditionTemp = builder->addVar(func, Type::i32); // TODO: can avoid pre if the condition is a get or a const auto* pre = makeMaybeSkip(builder->makeLocalSet(conditionTemp, iff->condition)); iff->condition = builder->makeLocalGet(conditionTemp, Type::i32); iff->condition = builder->makeBinary( OrInt32, iff->condition, builder->makeStateCheck(State::Rewinding)); iff->ifTrue = process(iff->ifTrue); auto* otherArm = iff->ifFalse; iff->ifFalse = nullptr; iff->finalize(); // Add support for the second arm as well. auto* otherIf = builder->makeIf( builder->makeBinary( OrInt32, builder->makeUnary(EqZInt32, builder->makeLocalGet(conditionTemp, Type::i32)), builder->makeStateCheck(State::Rewinding)), process(otherArm)); otherIf->finalize(); return builder->makeBlock({pre, iff, otherIf}); } else if (auto* loop = curr->dynCast()) { loop->body = process(loop->body); return loop; } else if (doesCall(curr)) { return makeCallSupport(curr); } // We must handle all control flow above, and all things that can change // the state, so there should be nothing that can reach here - add it // earlier as necessary. // std::cout << *curr << '\n'; WASM_UNREACHABLE("unexpected expression type"); } // Possibly skip some code, if rewinding. Expression* makeMaybeSkip(Expression* curr) { return builder->makeIf(builder->makeStateCheck(State::Normal), curr); } Expression* makeCallSupport(Expression* curr) { // TODO: stop doing this after code can no longer reach a call that may // change the state assert(doesCall(curr)); assert(curr->type == Type::none); // The case of a set is tricky: we must *not* execute the set when // unwinding, since at that point we have a fake value for the return, // and if we applied it to the local, it would end up saved and then // potentially used later - and with coalescing, that may interfere // with other things. Note also that at this point in the process we // have not yet written the load saving/loading code, so the optimizer // doesn't see that locals will have another use at the beginning and // end of the function. We don't do that yet because we don't want it // to force the final number of locals to be too high, but we also // must be careful to never touch a local unnecessarily to avoid bugs. // To implement this, write to a fake global; we'll fix it up after // AsyncifyLocals locals adds local saving/restoring. auto* set = curr->dynCast(); if (set) { auto name = analyzer->globals.getName(set->value->type); curr = builder->makeGlobalSet(name, set->value); set->value = builder->makeGlobalGet(name, set->value->type); } // Instrument the call itself (or, if a drop, the drop+call). auto index = callIndex++; // Execute the call, if either normal execution, or if rewinding and this // is the right call to go into. // TODO: we can read the next call index once in each function (but should // avoid saving/restoring that local later) curr = builder->makeIf( builder->makeIf(builder->makeStateCheck(State::Normal), builder->makeConst(Literal(int32_t(1))), makeCallIndexPeek(index)), builder->makeSequence(curr, makePossibleUnwind(index, set))); return curr; } Expression* makePossibleUnwind(Index index, Expression* ifNotUnwinding) { // In this pass we emit an "unwind" as a call to a fake intrinsic. We // will implement it in the later pass. (We can't create the unwind block // target here, as the optimizer would remove it later; we can only add // it when we add its contents, later.) return builder->makeIf( builder->makeStateCheck(State::Unwinding), builder->makeCall(ASYNCIFY_UNWIND, {builder->makeConst(Literal(int32_t(index)))}, Type::none), ifNotUnwinding); } Expression* makeCallIndexPeek(Index index) { // Emit an intrinsic for this, as we store the index into a local, and // don't want it to be seen by asyncify itself. return builder->makeCall(ASYNCIFY_CHECK_CALL_INDEX, {builder->makeConst(Literal(int32_t(index)))}, Type::i32); } Expression* makeCallIndexPop() { // Emit an intrinsic for this, as we store the index into a local, and // don't want it to be seen by asyncify itself. return builder->makeCall(ASYNCIFY_GET_CALL_INDEX, {}, Type::none); } // Given a function that is not instrumented - because we proved it doesn't // need it, or depending on the whitelist/blacklist - add assertions that // verify that property at runtime. // Note that it is ok to run code while sleeping (if you are careful not // to break assumptions in the program!) - so what is actually // checked here is if the state *changes* in an uninstrumented function. // That is, if in an uninstrumented function, a sleep should not begin // from any call. void addAssertsInNonInstrumented(Function* func) { auto oldState = builder->addVar(func, Type::i32); // Add a check at the function entry. func->body = builder->makeSequence( builder->makeLocalSet(oldState, builder->makeGlobalGet(ASYNCIFY_STATE, Type::i32)), func->body); // Add a check around every call. struct Walker : PostWalker { void visitCall(Call* curr) { // Tail calls will need another type of check, as they wouldn't reach // this assertion. assert(!curr->isReturn); handleCall(curr); } void visitCallIndirect(CallIndirect* curr) { // Tail calls will need another type of check, as they wouldn't reach // this assertion. assert(!curr->isReturn); handleCall(curr); } void handleCall(Expression* call) { auto* check = builder->makeIf( builder->makeBinary(NeInt32, builder->makeGlobalGet(ASYNCIFY_STATE, Type::i32), builder->makeLocalGet(oldState, Type::i32)), builder->makeUnreachable()); Expression* rep; if (call->type.isConcrete()) { auto temp = builder->addVar(func, call->type); rep = builder->makeBlock({ builder->makeLocalSet(temp, call), check, builder->makeLocalGet(temp, call->type), }); } else { rep = builder->makeSequence(call, check); } replaceCurrent(rep); } Function* func; AsyncifyBuilder* builder; Index oldState; }; Walker walker; walker.func = func; walker.builder = builder.get(); walker.oldState = oldState; walker.walk(func->body); } }; // Instrument local saving/restoring. struct AsyncifyLocals : public WalkerPass> { bool isFunctionParallel() override { return true; } ModuleAnalyzer* analyzer; AsyncifyLocals* create() override { return new AsyncifyLocals(analyzer); } AsyncifyLocals(ModuleAnalyzer* analyzer) : analyzer(analyzer) {} void visitCall(Call* curr) { // Replace calls to the fake intrinsics. if (curr->target == ASYNCIFY_UNWIND) { replaceCurrent(builder->makeBreak(ASYNCIFY_UNWIND, curr->operands[0])); } else if (curr->target == ASYNCIFY_GET_CALL_INDEX) { replaceCurrent(builder->makeSequence( builder->makeIncStackPos(-4), builder->makeLocalSet( rewindIndex, builder->makeLoad( 4, false, 0, 4, builder->makeGetStackPos(), Type::i32)))); } else if (curr->target == ASYNCIFY_CHECK_CALL_INDEX) { replaceCurrent(builder->makeBinary( EqInt32, builder->makeLocalGet(rewindIndex, Type::i32), builder->makeConst( Literal(int32_t(curr->operands[0]->cast()->value.geti32()))))); } } void visitGlobalSet(GlobalSet* curr) { auto type = analyzer->globals.getTypeOrNone(curr->name); if (type != Type::none) { replaceCurrent( builder->makeLocalSet(getFakeCallLocal(type), curr->value)); } } void visitGlobalGet(GlobalGet* curr) { auto type = analyzer->globals.getTypeOrNone(curr->name); if (type != Type::none) { replaceCurrent(builder->makeLocalGet(getFakeCallLocal(type), type)); } } Index getFakeCallLocal(Type type) { auto iter = fakeCallLocals.find(type); if (iter != fakeCallLocals.end()) { return iter->second; } return fakeCallLocals[type] = builder->addVar(getFunction(), type); } void doWalkFunction(Function* func) { // If the function cannot change our state, we have nothing to do - // we will never unwind or rewind the stack here. if (!analyzer->needsInstrumentation(func)) { return; } // Note the locals we want to preserve, before we add any more helpers. numPreservableLocals = func->getNumLocals(); // The new function body has a prelude to load locals if rewinding, // then the actual main body, which does all its unwindings by breaking // to the unwind block, which then handles pushing the call index, as // well as saving the locals. // An index is needed for getting the unwinding and rewinding call indexes // around TODO: can this be the same index? auto unwindIndex = builder->addVar(func, Type::i32); rewindIndex = builder->addVar(func, Type::i32); // Rewrite the function body. builder = make_unique(*getModule()); walk(func->body); // After the normal function body, emit a barrier before the postamble. Expression* barrier; if (func->sig.results == Type::none) { // The function may have ended without a return; ensure one. barrier = builder->makeReturn(); } else { // The function must have returned or hit an unreachable, but emit one // to make possible bugs easier to figure out (as this should never be // reached). The optimizer can remove this anyhow. barrier = builder->makeUnreachable(); } auto* newBody = builder->makeBlock( {builder->makeIf(builder->makeStateCheck(State::Rewinding), makeLocalLoading()), builder->makeLocalSet( unwindIndex, builder->makeBlock(ASYNCIFY_UNWIND, builder->makeSequence(func->body, barrier))), makeCallIndexPush(unwindIndex), makeLocalSaving()}); if (func->sig.results != Type::none) { // If we unwind, we must still "return" a value, even if it will be // ignored on the outside. newBody->list.push_back( LiteralUtils::makeZero(func->sig.results, *getModule())); newBody->finalize(func->sig.results); } func->body = newBody; // Making things like returns conditional may alter types. ReFinalize().walkFunctionInModule(func, getModule()); } private: std::unique_ptr builder; Index rewindIndex; Index numPreservableLocals; std::map fakeCallLocals; Expression* makeLocalLoading() { if (numPreservableLocals == 0) { return builder->makeNop(); } auto* func = getFunction(); Index total = 0; for (Index i = 0; i < numPreservableLocals; i++) { auto type = func->getLocalType(i); auto size = type.getByteSize(); total += size; } auto* block = builder->makeBlock(); block->list.push_back(builder->makeIncStackPos(-total)); auto tempIndex = builder->addVar(func, Type::i32); block->list.push_back( builder->makeLocalSet(tempIndex, builder->makeGetStackPos())); Index offset = 0; for (Index i = 0; i < numPreservableLocals; i++) { auto type = func->getLocalType(i); auto size = type.getByteSize(); assert(size % STACK_ALIGN == 0); // TODO: higher alignment? block->list.push_back(builder->makeLocalSet( i, builder->makeLoad(size, true, offset, STACK_ALIGN, builder->makeLocalGet(tempIndex, Type::i32), type))); offset += size; } block->finalize(); return block; } Expression* makeLocalSaving() { if (numPreservableLocals == 0) { return builder->makeNop(); } auto* func = getFunction(); auto* block = builder->makeBlock(); auto tempIndex = builder->addVar(func, Type::i32); block->list.push_back( builder->makeLocalSet(tempIndex, builder->makeGetStackPos())); Index offset = 0; for (Index i = 0; i < numPreservableLocals; i++) { auto type = func->getLocalType(i); auto size = type.getByteSize(); assert(size % STACK_ALIGN == 0); // TODO: higher alignment? block->list.push_back( builder->makeStore(size, offset, STACK_ALIGN, builder->makeLocalGet(tempIndex, Type::i32), builder->makeLocalGet(i, type), type)); offset += size; } block->list.push_back(builder->makeIncStackPos(offset)); block->finalize(); return block; } Expression* makeCallIndexPush(Index tempIndex) { // TODO: add a check against the stack end here return builder->makeSequence( builder->makeStore(4, 0, 4, builder->makeGetStackPos(), builder->makeLocalGet(tempIndex, Type::i32), Type::i32), builder->makeIncStackPos(4)); } }; } // anonymous namespace static std::string getFullImportName(Name module, Name base) { return std::string(module.str) + '.' + base.str; } struct Asyncify : public Pass { void run(PassRunner* runner, Module* module) override { bool optimize = runner->options.optimizeLevel > 0; // Ensure there is a memory, as we need it. MemoryUtils::ensureExists(module->memory); // Find which things can change the state. auto stateChangingImports = String::trim(read_possible_response_file( runner->options.getArgumentOrDefault("asyncify-imports", ""))); auto ignoreImports = runner->options.getArgumentOrDefault("asyncify-ignore-imports", ""); bool allImportsCanChangeState = stateChangingImports == "" && ignoreImports == ""; String::Split listedImports(stateChangingImports, ","); auto ignoreIndirect = runner->options.getArgumentOrDefault( "asyncify-ignore-indirect", "") == ""; String::Split blacklist( String::trim(read_possible_response_file( runner->options.getArgumentOrDefault("asyncify-blacklist", ""))), ","); String::Split whitelist( String::trim(read_possible_response_file( runner->options.getArgumentOrDefault("asyncify-whitelist", ""))), ","); auto asserts = runner->options.getArgumentOrDefault("asyncify-asserts", "") != ""; blacklist = handleBracketingOperators(blacklist); whitelist = handleBracketingOperators(whitelist); if (!blacklist.empty() && !whitelist.empty()) { Fatal() << "It makes no sense to use both a blacklist and a whitelist " "with asyncify."; } auto canImportChangeState = [&](Name module, Name base) { if (allImportsCanChangeState) { return true; } auto full = getFullImportName(module, base); for (auto& listedImport : listedImports) { if (String::wildcardMatch(listedImport, full)) { return true; } } return false; }; // Scan the module. ModuleAnalyzer analyzer(*module, canImportChangeState, ignoreIndirect, blacklist, whitelist, asserts); // Add necessary globals before we emit code to use them. addGlobals(module); // Instrument the flow of code, adding code instrumentation and // skips for when rewinding. We do this on flat IR so that it is // practical to add code around each call, without affecting // anything else. { PassRunner runner(module); runner.add("flatten"); // Dce is useful here, since AsyncifyFlow makes control flow conditional, // which may make unreachable code look reachable. It also lets us ignore // unreachable code here. runner.add("dce"); if (optimize) { // Optimizing before BsyncifyFlow is crucial, especially coalescing, // because the flow changes add many branches, break up if-elses, etc., // all of which extend the live ranges of locals. In other words, it is // not possible to coalesce well afterwards. runner.add("simplify-locals-nonesting"); runner.add("reorder-locals"); runner.add("coalesce-locals"); runner.add("simplify-locals-nonesting"); runner.add("reorder-locals"); runner.add("merge-blocks"); } runner.add(make_unique(&analyzer)); runner.setIsNested(true); runner.setValidateGlobally(false); runner.run(); } // Next, add local saving/restoring logic. We optimize before doing this, // to undo the extra code generated by flattening, and to arrive at the // minimal amount of locals (which is important as we must save and // restore those locals). We also and optimize after as well to simplify // the code as much as possible. { PassRunner runner(module); if (optimize) { runner.addDefaultFunctionOptimizationPasses(); } runner.add(make_unique(&analyzer)); if (optimize) { runner.addDefaultFunctionOptimizationPasses(); } runner.setIsNested(true); runner.setValidateGlobally(false); runner.run(); } // Finally, add function support (that should not have been seen by // the previous passes). addFunctions(module); } private: void addGlobals(Module* module) { Builder builder(*module); module->addGlobal(builder.makeGlobal(ASYNCIFY_STATE, Type::i32, builder.makeConst(Literal(int32_t(0))), Builder::Mutable)); module->addGlobal(builder.makeGlobal(ASYNCIFY_DATA, Type::i32, builder.makeConst(Literal(int32_t(0))), Builder::Mutable)); } void addFunctions(Module* module) { Builder builder(*module); auto makeFunction = [&](Name name, bool setData, State state) { std::vector params; if (setData) { params.push_back(Type::i32); } auto* body = builder.makeBlock(); body->list.push_back(builder.makeGlobalSet( ASYNCIFY_STATE, builder.makeConst(Literal(int32_t(state))))); if (setData) { body->list.push_back(builder.makeGlobalSet( ASYNCIFY_DATA, builder.makeLocalGet(0, Type::i32))); } // Verify the data is valid. auto* stackPos = builder.makeLoad(4, false, int32_t(DataOffset::BStackPos), 4, builder.makeGlobalGet(ASYNCIFY_DATA, Type::i32), Type::i32); auto* stackEnd = builder.makeLoad(4, false, int32_t(DataOffset::BStackEnd), 4, builder.makeGlobalGet(ASYNCIFY_DATA, Type::i32), Type::i32); body->list.push_back( builder.makeIf(builder.makeBinary(GtUInt32, stackPos, stackEnd), builder.makeUnreachable())); body->finalize(); auto* func = builder.makeFunction( name, Signature(Type(params), Type::none), {}, body); module->addFunction(func); module->addExport(builder.makeExport(name, name, ExternalKind::Function)); }; makeFunction(ASYNCIFY_START_UNWIND, true, State::Unwinding); makeFunction(ASYNCIFY_STOP_UNWIND, false, State::Normal); makeFunction(ASYNCIFY_START_REWIND, true, State::Rewinding); makeFunction(ASYNCIFY_STOP_REWIND, false, State::Normal); } }; Pass* createAsyncifyPass() { return new Asyncify(); } // Helper passes that can be run after Asyncify. template struct ModAsyncify : public WalkerPass>> { bool isFunctionParallel() override { return true; } ModAsyncify* create() override { return new ModAsyncify(); } void doWalkFunction(Function* func) { // Find the asyncify state name. auto* unwind = this->getModule()->getExport(ASYNCIFY_STOP_UNWIND); auto* unwindFunc = this->getModule()->getFunction(unwind->value); FindAll sets(unwindFunc->body); assert(sets.list.size() == 1); asyncifyStateName = sets.list[0]->name; // Walk and optimize. this->walk(func->body); } // Note that we don't just implement GetGlobal as we may know the value is // *not* 0, 1, or 2, but not know the actual value. So what we can say depends // on the comparison being done on it, and so we implement Binary and // Select. void visitBinary(Binary* curr) { // Check if this is a comparison of the asyncify state to a specific // constant, which we may know is impossible. bool flip = false; if (curr->op == NeInt32) { flip = true; } else if (curr->op != EqInt32) { return; } auto* c = curr->right->dynCast(); if (!c) { return; } auto* get = curr->left->dynCast(); if (!get || get->name != asyncifyStateName) { return; } // This is a comparison of the state to a constant, check if we know the // value. int32_t value; auto checkedValue = c->value.geti32(); if ((checkedValue == int(State::Unwinding) && neverUnwind) || (checkedValue == int(State::Rewinding) && neverRewind)) { // We know the state is checked against an impossible value. value = 0; } else if (checkedValue == int(State::Unwinding) && this->unwinding) { // We know we are in fact unwinding right now. value = 1; unsetUnwinding(); } else { return; } if (flip) { value = 1 - value; } Builder builder(*this->getModule()); this->replaceCurrent(builder.makeConst(Literal(int32_t(value)))); } void visitSelect(Select* curr) { auto* get = curr->condition->dynCast(); if (!get || get->name != asyncifyStateName) { return; } // This is a comparison of the state to zero, which means we are checking // "if running normally, run this code, but if rewinding, ignore it". If // we know we'll never rewind, we can optimize this. if (neverRewind) { Builder builder(*this->getModule()); curr->condition = builder.makeConst(Literal(int32_t(0))); } } void visitCall(Call* curr) { unsetUnwinding(); if (!importsAlwaysUnwind) { return; } auto* target = this->getModule()->getFunction(curr->target); if (!target->imported()) { return; } // This is an import that definitely unwinds. Await the next check of // the state in this linear execution trace, which we can turn into a // constant. this->unwinding = true; } void visitCallIndirect(CallIndirect* curr) { unsetUnwinding(); } static void doNoteNonLinear( ModAsyncify* self, Expression**) { // When control flow branches, stop tracking an unwinding. self->unsetUnwinding(); } void visitGlobalSet(GlobalSet* set) { // TODO: this could be more precise unsetUnwinding(); } private: Name asyncifyStateName; // Whether we just did a call to an import that indicates we are unwinding. bool unwinding = false; void unsetUnwinding() { this->unwinding = false; } }; // // Assume imports that may unwind will always unwind, and that rewinding never // happens. // Pass* createModAsyncifyAlwaysOnlyUnwindPass() { return new ModAsyncify(); } // // Assume that we never unwind, but may still rewind. // struct ModAsyncifyNeverUnwind : public Pass { void run(PassRunner* runner, Module* module) override {} }; Pass* createModAsyncifyNeverUnwindPass() { return new ModAsyncify(); } } // namespace wasm binaryen-version_91/src/passes/AvoidReinterprets.cpp000066400000000000000000000160131362402614000231220ustar00rootroot00000000000000/* * Copyright 2017 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // Avoids reinterprets by using more loads: if we load a value and // reinterpret it, we could have loaded it with the other type // anyhow. This uses more locals and loads, so it is not generally // beneficial, unless reinterprets are very costly. #include #include #include #include #include namespace wasm { static bool canReplaceWithReinterpret(Load* load) { // We can replace a full-size load with a valid pointer with // a reinterpret of the same address. A partial load would see // more bytes and possibly invalid data, and an unreachable // pointer is just not interesting to handle. return load->type != Type::unreachable && load->bytes == load->type.getByteSize(); } static Load* getSingleLoad(LocalGraph* localGraph, LocalGet* get, const PassOptions& passOptions, FeatureSet features) { std::set seen; seen.insert(get); while (1) { auto& sets = localGraph->getSetses[get]; if (sets.size() != 1) { return nullptr; } auto* set = *sets.begin(); if (!set) { return nullptr; } auto* value = Properties::getFallthrough(set->value, passOptions, features); if (auto* parentGet = value->dynCast()) { if (seen.count(parentGet)) { // We are in a cycle of gets, in unreachable code. return nullptr; } get = parentGet; seen.insert(get); continue; } if (auto* load = value->dynCast()) { return load; } return nullptr; } } static bool isReinterpret(Unary* curr) { return curr->op == ReinterpretInt32 || curr->op == ReinterpretInt64 || curr->op == ReinterpretFloat32 || curr->op == ReinterpretFloat64; } struct AvoidReinterprets : public WalkerPass> { bool isFunctionParallel() override { return true; } Pass* create() override { return new AvoidReinterprets; } struct Info { // Info used when analyzing. bool reinterpreted; // Info used when optimizing. Index ptrLocal; Index reinterpretedLocal; }; std::map infos; LocalGraph* localGraph; void doWalkFunction(Function* func) { // prepare LocalGraph localGraph_(func); localGraph = &localGraph_; // walk PostWalker::doWalkFunction(func); // optimize optimize(func); } void visitUnary(Unary* curr) { if (isReinterpret(curr)) { FeatureSet features = getModule()->features; if (auto* get = Properties::getFallthrough(curr->value, getPassOptions(), features) ->dynCast()) { if (auto* load = getSingleLoad(localGraph, get, getPassOptions(), features)) { auto& info = infos[load]; info.reinterpreted = true; } } } } void optimize(Function* func) { std::set unoptimizables; for (auto& pair : infos) { auto* load = pair.first; auto& info = pair.second; if (info.reinterpreted && canReplaceWithReinterpret(load)) { // We should use another load here, to avoid reinterprets. info.ptrLocal = Builder::addVar(func, Type::i32); info.reinterpretedLocal = Builder::addVar(func, load->type.reinterpret()); } else { unoptimizables.insert(load); } } for (auto* load : unoptimizables) { infos.erase(load); } // We now know which we can optimize, and how. struct FinalOptimizer : public PostWalker { std::map& infos; LocalGraph* localGraph; Module* module; const PassOptions& passOptions; FinalOptimizer(std::map& infos, LocalGraph* localGraph, Module* module, const PassOptions& passOptions) : infos(infos), localGraph(localGraph), module(module), passOptions(passOptions) {} void visitUnary(Unary* curr) { if (isReinterpret(curr)) { auto* value = Properties::getFallthrough( curr->value, passOptions, module->features); if (auto* load = value->dynCast()) { // A reinterpret of a load - flip it right here if we can. if (canReplaceWithReinterpret(load)) { replaceCurrent(makeReinterpretedLoad(load, load->ptr)); } } else if (auto* get = value->dynCast()) { if (auto* load = getSingleLoad( localGraph, get, passOptions, module->features)) { auto iter = infos.find(load); if (iter != infos.end()) { auto& info = iter->second; // A reinterpret of a get of a load - use the new local. Builder builder(*module); replaceCurrent(builder.makeLocalGet(info.reinterpretedLocal, load->type.reinterpret())); } } } } } void visitLoad(Load* curr) { auto iter = infos.find(curr); if (iter != infos.end()) { auto& info = iter->second; Builder builder(*module); auto* ptr = curr->ptr; curr->ptr = builder.makeLocalGet(info.ptrLocal, Type::i32); // Note that the other load can have its sign set to false - if the // original were an integer, the other is a float anyhow; and if // original were a float, we don't know what sign to use. replaceCurrent(builder.makeBlock( {builder.makeLocalSet(info.ptrLocal, ptr), builder.makeLocalSet( info.reinterpretedLocal, makeReinterpretedLoad( curr, builder.makeLocalGet(info.ptrLocal, Type::i32))), curr})); } } Load* makeReinterpretedLoad(Load* load, Expression* ptr) { Builder builder(*module); return builder.makeLoad(load->bytes, false, load->offset, load->align, ptr, load->type.reinterpret()); } } finalOptimizer(infos, localGraph, getModule(), getPassOptions()); finalOptimizer.walk(func->body); } }; Pass* createAvoidReinterpretsPass() { return new AvoidReinterprets(); } } // namespace wasm binaryen-version_91/src/passes/CMakeLists.txt000066400000000000000000000035541362402614000215130ustar00rootroot00000000000000# Python 3.5 is the version shipped in Ubuntu Xenial find_package(PythonInterp 3.5 REQUIRED) add_custom_command( OUTPUT WasmIntrinsics.cpp COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/scripts/embedwat.py ${PROJECT_SOURCE_DIR}/src/passes/wasm-intrinsics.wat ${CMAKE_CURRENT_BINARY_DIR}/WasmIntrinsics.cpp DEPENDS ${PROJECT_SOURCE_DIR}/scripts/embedwat.py wasm-intrinsics.wat) set(passes_SOURCES pass.cpp AlignmentLowering.cpp Asyncify.cpp AvoidReinterprets.cpp CoalesceLocals.cpp CodePushing.cpp CodeFolding.cpp ConstHoisting.cpp DataFlowOpts.cpp DeadArgumentElimination.cpp DeadCodeElimination.cpp Directize.cpp DuplicateImportElimination.cpp DuplicateFunctionElimination.cpp DWARF.cpp ExtractFunction.cpp Flatten.cpp FuncCastEmulation.cpp I64ToI32Lowering.cpp Inlining.cpp InstrumentLocals.cpp InstrumentMemory.cpp LegalizeJSInterface.cpp LimitSegments.cpp LocalCSE.cpp LogExecution.cpp LoopInvariantCodeMotion.cpp MemoryPacking.cpp MergeBlocks.cpp MergeLocals.cpp Metrics.cpp MinifyImportsAndExports.cpp NameList.cpp NoExitRuntime.cpp OptimizeAddedConstants.cpp OptimizeInstructions.cpp PickLoadSigns.cpp PostAssemblyScript.cpp PostEmscripten.cpp Precompute.cpp Print.cpp PrintCallGraph.cpp PrintFeatures.cpp PrintFunctionMap.cpp RoundTrip.cpp StackIR.cpp Strip.cpp StripTargetFeatures.cpp RedundantSetElimination.cpp RelooperJumpThreading.cpp RemoveImports.cpp RemoveMemory.cpp RemoveNonJSOps.cpp RemoveUnusedBrs.cpp RemoveUnusedNames.cpp RemoveUnusedModuleElements.cpp ReorderLocals.cpp ReorderFunctions.cpp ReReloop.cpp TrapMode.cpp SafeHeap.cpp SimplifyGlobals.cpp SimplifyLocals.cpp Souperify.cpp SpillPointers.cpp SSAify.cpp Untee.cpp Vacuum.cpp ${CMAKE_CURRENT_BINARY_DIR}/WasmIntrinsics.cpp ) add_library(passes OBJECT ${passes_SOURCES}) binaryen-version_91/src/passes/CoalesceLocals.cpp000066400000000000000000000460121362402614000223270ustar00rootroot00000000000000/* * Copyright 2016 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // // Coalesce locals, in order to reduce the total number of locals. This // is similar to register allocation, however, there is never any // spilling, and there isn't a fixed number of locals. // #include #include #include #include "cfg/liveness-traversal.h" #include "ir/utils.h" #include "pass.h" #include "support/learning.h" #include "support/permutations.h" #include "wasm-builder.h" #include "wasm.h" #ifdef CFG_PROFILE #include "support/timing.h" #endif namespace wasm { struct CoalesceLocals : public WalkerPass>> { bool isFunctionParallel() override { return true; } Pass* create() override { return new CoalesceLocals; } // main entry point void doWalkFunction(Function* func); void increaseBackEdgePriorities(); void calculateInterferences(); void calculateInterferences(const SetOfLocals& locals); void pickIndicesFromOrder(std::vector& order, std::vector& indices); void pickIndicesFromOrder(std::vector& order, std::vector& indices, Index& removedCopies); // returns a vector of oldIndex => newIndex virtual void pickIndices(std::vector& indices); void applyIndices(std::vector& indices, Expression* root); // interference state // canonicalized - accesses should check (low, high) std::vector interferences; void interfere(Index i, Index j) { if (i == j) { return; } interferences[std::min(i, j) * numLocals + std::max(i, j)] = 1; } // optimized version where you know that low < high void interfereLowHigh(Index low, Index high) { assert(low < high); interferences[low * numLocals + high] = 1; } void unInterfere(Index i, Index j) { interferences[std::min(i, j) * numLocals + std::max(i, j)] = 0; } bool interferes(Index i, Index j) { return interferences[std::min(i, j) * numLocals + std::max(i, j)]; } }; void CoalesceLocals::doWalkFunction(Function* func) { if (!canRun(func)) { return; } super::doWalkFunction(func); // prioritize back edges increaseBackEdgePriorities(); // use liveness to find interference calculateInterferences(); // pick new indices std::vector indices; pickIndices(indices); // apply indices applyIndices(indices, func->body); } // A copy on a backedge can be especially costly, forcing us to branch just to // do that copy. Add weight to such copies, so we prioritize getting rid of // them. void CoalesceLocals::increaseBackEdgePriorities() { for (auto* loopTop : loopTops) { // ignore the first edge, it is the initial entry, we just want backedges auto& in = loopTop->in; for (Index i = 1; i < in.size(); i++) { auto* arrivingBlock = in[i]; if (arrivingBlock->out.size() > 1) { // we just want unconditional branches to the loop top, true phi // fragments continue; } for (auto& action : arrivingBlock->contents.actions) { if (action.isSet()) { auto* set = (*action.origin)->cast(); if (auto* get = getCopy(set)) { // this is indeed a copy, add to the cost (default cost is 2, so // this adds 50%, and can mostly break ties) addCopy(set->index, get->index); } } } } } } void CoalesceLocals::calculateInterferences() { interferences.resize(numLocals * numLocals); std::fill(interferences.begin(), interferences.end(), false); for (auto& curr : basicBlocks) { if (liveBlocks.count(curr.get()) == 0) { continue; // ignore dead blocks } // everything coming in might interfere, as it might come from a different // block auto live = curr->contents.end; calculateInterferences(live); // scan through the block itself auto& actions = curr->contents.actions; for (int i = int(actions.size()) - 1; i >= 0; i--) { auto& action = actions[i]; auto index = action.index; if (action.isGet()) { // new live local, interferes with all the rest live.insert(index); for (auto i : live) { interfere(i, index); } } else { if (live.erase(index)) { action.effective = true; } } } } // Params have a value on entry, so mark them as live, as variables // live at the entry expect their zero-init value. SetOfLocals start = entry->contents.start; auto numParams = getFunction()->getNumParams(); for (Index i = 0; i < numParams; i++) { start.insert(i); } calculateInterferences(start); } void CoalesceLocals::calculateInterferences(const SetOfLocals& locals) { Index size = locals.size(); for (Index i = 0; i < size; i++) { for (Index j = i + 1; j < size; j++) { interfereLowHigh(locals[i], locals[j]); } } } // Indices decision making void CoalesceLocals::pickIndicesFromOrder(std::vector& order, std::vector& indices) { Index removedCopies; pickIndicesFromOrder(order, indices, removedCopies); } void CoalesceLocals::pickIndicesFromOrder(std::vector& order, std::vector& indices, Index& removedCopies) { // mostly-simple greedy coloring #if CFG_DEBUG std::cerr << "\npickIndicesFromOrder on " << getFunction()->name << '\n'; std::cerr << getFunction()->body << '\n'; std::cerr << "order:\n"; for (auto i : order) std::cerr << i << ' '; std::cerr << '\n'; std::cerr << "interferences:\n"; for (Index i = 0; i < numLocals; i++) { for (Index j = 0; j < i + 1; j++) { std::cerr << " "; } for (Index j = i + 1; j < numLocals; j++) { std::cerr << int(interferes(i, j)) << ' '; } std::cerr << " : $" << i << '\n'; } std::cerr << "copies:\n"; for (Index i = 0; i < numLocals; i++) { for (Index j = 0; j < i + 1; j++) { std::cerr << " "; } for (Index j = i + 1; j < numLocals; j++) { std::cerr << int(getCopies(i, j)) << ' '; } std::cerr << " : $" << i << '\n'; } std::cerr << "total copies:\n"; for (Index i = 0; i < numLocals; i++) { std::cerr << " $" << i << ": " << totalCopies[i] << '\n'; } #endif // TODO: take into account distribution (99-1 is better than 50-50 with two // registers, for gzip) std::vector types; // new index * numLocals => list of all interferences of locals merged to it std::vector newInterferences; // new index * numLocals => list of all copies of locals merged to it std::vector newCopies; indices.resize(numLocals); types.resize(numLocals); newInterferences.resize(numLocals * numLocals); std::fill(newInterferences.begin(), newInterferences.end(), false); auto numParams = getFunction()->getNumParams(); // start with enough room for the params newCopies.resize(numParams * numLocals); std::fill(newCopies.begin(), newCopies.end(), 0); Index nextFree = 0; removedCopies = 0; // we can't reorder parameters, they are fixed in order, and cannot coalesce Index i = 0; for (; i < numParams; i++) { assert(order[i] == i); // order must leave the params in place indices[i] = i; types[i] = getFunction()->getLocalType(i); for (Index j = numParams; j < numLocals; j++) { newInterferences[numLocals * i + j] = interferes(i, j); newCopies[numLocals * i + j] = getCopies(i, j); } nextFree++; } for (; i < numLocals; i++) { Index actual = order[i]; Index found = -1; uint8_t foundCopies = -1; for (Index j = 0; j < nextFree; j++) { if (!newInterferences[j * numLocals + actual] && getFunction()->getLocalType(actual) == types[j]) { // this does not interfere, so it might be what we want. but pick the // one eliminating the most copies (we could stop looking forward when // there are no more items that have copies anyhow, but it doesn't seem // to help) auto currCopies = newCopies[j * numLocals + actual]; if (found == Index(-1) || currCopies > foundCopies) { indices[actual] = found = j; foundCopies = currCopies; } } } if (found == Index(-1)) { indices[actual] = found = nextFree; types[found] = getFunction()->getLocalType(actual); nextFree++; removedCopies += getCopies(found, actual); newCopies.resize(nextFree * numLocals); } else { removedCopies += foundCopies; } #if CFG_DEBUG std::cerr << "set local $" << actual << " to $" << found << '\n'; #endif // merge new interferences and copies for the new index for (Index k = i + 1; k < numLocals; k++) { // go in the order, we only need to update for those we will see later auto j = order[k]; newInterferences[found * numLocals + j] = newInterferences[found * numLocals + j] | interferes(actual, j); newCopies[found * numLocals + j] += getCopies(actual, j); } } } // given a baseline order, adjust it based on an important order of priorities // (higher values are higher priority). The priorities take precedence, unless // they are equal and then the original order should be kept. std::vector adjustOrderByPriorities(std::vector& baseline, std::vector& priorities) { std::vector ret = baseline; std::vector reversed = makeReversed(baseline); std::sort(ret.begin(), ret.end(), [&priorities, &reversed](Index x, Index y) { return priorities[x] > priorities[y] || (priorities[x] == priorities[y] && reversed[x] < reversed[y]); }); return ret; } void CoalesceLocals::pickIndices(std::vector& indices) { if (numLocals == 0) { return; } if (numLocals == 1) { indices.push_back(0); return; } // take into account total copies. but we must keep params in place, so give // them max priority auto adjustedTotalCopies = totalCopies; auto numParams = getFunction()->getNumParams(); for (Index i = 0; i < numParams; i++) { adjustedTotalCopies[i] = std::numeric_limits::max(); } // first try the natural order. this is less arbitrary than it seems, as the // program may have a natural order of locals inherent in it. auto order = makeIdentity(numLocals); order = adjustOrderByPriorities(order, adjustedTotalCopies); Index removedCopies; pickIndicesFromOrder(order, indices, removedCopies); auto maxIndex = *std::max_element(indices.begin(), indices.end()); // next try the reverse order. this both gives us another chance at something // good, and also the very naturalness of the simple order may be quite // suboptimal setIdentity(order); for (Index i = numParams; i < numLocals; i++) { order[i] = numParams + numLocals - 1 - i; } order = adjustOrderByPriorities(order, adjustedTotalCopies); std::vector reverseIndices; Index reverseRemovedCopies; pickIndicesFromOrder(order, reverseIndices, reverseRemovedCopies); auto reverseMaxIndex = *std::max_element(reverseIndices.begin(), reverseIndices.end()); // prefer to remove copies foremost, as it matters more for code size (minus // gzip), and improves throughput. if (reverseRemovedCopies > removedCopies || (reverseRemovedCopies == removedCopies && reverseMaxIndex < maxIndex)) { indices.swap(reverseIndices); } } void CoalesceLocals::applyIndices(std::vector& indices, Expression* root) { assert(indices.size() == numLocals); for (auto& curr : basicBlocks) { auto& actions = curr->contents.actions; for (auto& action : actions) { if (action.isGet()) { auto* get = (*action.origin)->cast(); get->index = indices[get->index]; } else if (action.isSet()) { auto* set = (*action.origin)->cast(); set->index = indices[set->index]; // in addition, we can optimize out redundant copies and ineffective // sets LocalGet* get; if ((get = set->value->dynCast()) && get->index == set->index) { action.removeCopy(); continue; } // remove ineffective actions if (!action.effective) { // value may have no side effects, further optimizations can eliminate // it *action.origin = set->value; if (!set->isTee()) { // we need to drop it Drop* drop = ExpressionManipulator::convert(set); drop->value = *action.origin; *action.origin = drop; } continue; } } } } // update type list auto numParams = getFunction()->getNumParams(); Index newNumLocals = 0; for (auto index : indices) { newNumLocals = std::max(newNumLocals, index + 1); } auto oldVars = getFunction()->vars; getFunction()->vars.resize(newNumLocals - numParams); for (Index index = numParams; index < numLocals; index++) { Index newIndex = indices[index]; if (newIndex >= numParams) { getFunction()->vars[newIndex - numParams] = oldVars[index - numParams]; } } // names are gone getFunction()->localNames.clear(); getFunction()->localIndices.clear(); } struct CoalesceLocalsWithLearning : public CoalesceLocals { virtual Pass* create() override { return new CoalesceLocalsWithLearning; } virtual void pickIndices(std::vector& indices) override; }; void CoalesceLocalsWithLearning::pickIndices(std::vector& indices) { if (getFunction()->getNumVars() <= 1) { // nothing to think about here CoalesceLocals::pickIndices(indices); return; } struct Order : public std::vector { void setFitness(double f) { fitness = f; } double getFitness() { return fitness; } void dump(std::string text) { std::cout << text + ": ( "; for (Index i = 0; i < size(); i++) { std::cout << (*this)[i] << " "; } std::cout << ")\n"; std::cout << "of quality: " << getFitness() << "\n"; } private: double fitness; }; struct Generator { Generator(CoalesceLocalsWithLearning* parent) : parent(parent), noise(42) {} void calculateFitness(Order* order) { // apply the order std::vector indices; // the phenotype Index removedCopies; parent->pickIndicesFromOrder(*order, indices, removedCopies); auto maxIndex = *std::max_element(indices.begin(), indices.end()); assert(maxIndex <= parent->numLocals); // main part of fitness is the number of locals double fitness = parent->numLocals - maxIndex; // higher fitness is better // secondarily, it is nice to not reorder locals unnecessarily double fragment = 1.0 / (2.0 * parent->numLocals); for (Index i = 0; i < parent->numLocals; i++) { if ((*order)[i] == i) { fitness += fragment; // boost for each that wasn't moved } } // removing copies is a secondary concern fitness = (100 * fitness) + removedCopies; order->setFitness(fitness); } Order* makeRandom() { auto* ret = new Order; ret->resize(parent->numLocals); for (Index i = 0; i < parent->numLocals; i++) { (*ret)[i] = i; } if (first) { // as the first guess, use the natural order. this is not arbitrary for // two reasons. first, there may be an inherent order in the input // (frequent indices are lower, etc.). second, by ensuring we start with // the natural order, we ensure we are at least as good as the // non-learning variant. // TODO: use ::pickIndices from the parent, so we literally get the // simpler approach as our first option first = false; } else { // leave params alone, shuffle the rest std::shuffle(ret->begin() + parent->getFunction()->getNumParams(), ret->end(), noise); } calculateFitness(ret); #ifdef CFG_LEARN_DEBUG order->dump("new rando"); #endif return ret; } Order* makeMixture(Order* left, Order* right) { // perturb left using right. this is useful since // we don't care about absolute locations, relative ones matter more, // and a true merge of two vectors could obscure that (e.g. // a.......... and ..........a would merge a into the middle, for no // reason), and cause a lot of unnecessary noise Index size = left->size(); Order reverseRight; // reverseRight[x] is the index of x in right reverseRight.resize(size); for (Index i = 0; i < size; i++) { reverseRight[(*right)[i]] = i; } auto* ret = new Order; *ret = *left; assert(size >= 1); for (Index i = parent->getFunction()->getNumParams(); i < size - 1; i++) { // if (i, i + 1) is in reverse order in right, flip them if (reverseRight[(*ret)[i]] > reverseRight[(*ret)[i + 1]]) { std::swap((*ret)[i], (*ret)[i + 1]); // if we don't skip, we might end up pushing an element all the way to // the end, which is not very perturbation-y i++; } } calculateFitness(ret); #ifdef CFG_LEARN_DEBUG ret->dump("new mixture"); #endif return ret; } private: CoalesceLocalsWithLearning* parent; std::mt19937 noise; bool first = true; }; #ifdef CFG_LEARN_DEBUG std::cout << "[learning for " << getFunction()->name << "]\n"; #endif auto numVars = this->getFunction()->getNumVars(); const int GENERATION_SIZE = std::min(Index(numVars * (numVars - 1)), Index(20)); Generator generator(this); GeneticLearner learner(generator, GENERATION_SIZE); #ifdef CFG_LEARN_DEBUG learner.getBest()->dump("first best"); #endif // keep working while we see improvement auto oldBest = learner.getBest()->getFitness(); while (1) { learner.runGeneration(); auto newBest = learner.getBest()->getFitness(); if (newBest == oldBest) { break; // unlikely we can improve } oldBest = newBest; #ifdef CFG_LEARN_DEBUG learner.getBest()->dump("current best"); #endif } #ifdef CFG_LEARN_DEBUG learner.getBest()->dump("the best"); #endif // TODO: cache indices in Orders, at the cost of more memory? this->pickIndicesFromOrder(*learner.getBest(), indices); } // declare passes Pass* createCoalesceLocalsPass() { return new CoalesceLocals(); } Pass* createCoalesceLocalsWithLearningPass() { return new CoalesceLocalsWithLearning(); } } // namespace wasm binaryen-version_91/src/passes/CodeFolding.cpp000066400000000000000000000651231362402614000216340ustar00rootroot00000000000000/* * Copyright 2017 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // // Folds duplicate code together, saving space (and possibly phis in // the wasm VM, which can save time). // // We fold tails of code where they merge and moving the code // to the merge point is helpful. There are two cases here: (1) expressions, // in which we merge to right after the expression itself, in these cases: // * blocks, we merge the fallthrough + the breaks // * if-else, we merge the arms // and (2) the function body as a whole, in which we can merge returns or // unreachables, putting the merged code at the end of the function body. // // For example, with an if-else, we might merge this: // (if (condition) // (block // A // C // ) // (block // B // C // ) // ) // to // (if (condition) // (block // A // ) // (block // B // ) // ) // C // // Note that the merged code, C in the example above, can be anything, // including code with control flow. If C is identical in all the locations, // then it must be safe to merge (if it contains a branch to something // higher up, then since our branch target names are unique, it must be // to the same thing, and after merging it can still reach it). // #include #include "ir/branch-utils.h" #include "ir/effects.h" #include "ir/label-utils.h" #include "ir/utils.h" #include "pass.h" #include "wasm-builder.h" #include "wasm.h" namespace wasm { static const Index WORTH_ADDING_BLOCK_TO_REMOVE_THIS_MUCH = 3; struct ExpressionMarker : public PostWalker> { std::set& marked; ExpressionMarker(std::set& marked, Expression* expr) : marked(marked) { walk(expr); } void visitExpression(Expression* expr) { marked.insert(expr); } }; struct CodeFolding : public WalkerPass> { bool isFunctionParallel() override { return true; } Pass* create() override { return new CodeFolding; } // information about a "tail" - code that reaches a point that we can // merge (e.g., a branch and some code leading up to it) struct Tail { Expression* expr; // nullptr if this is a fallthrough Block* block; // the enclosing block of code we hope to merge at its tail Expression** pointer; // for an expr with no parent block, the location it // is at, so we can replace it // For a fallthrough Tail(Block* block) : expr(nullptr), block(block), pointer(nullptr) {} // For a break Tail(Expression* expr, Block* block) : expr(expr), block(block), pointer(nullptr) { validate(); } Tail(Expression* expr, Expression** pointer) : expr(expr), block(nullptr), pointer(pointer) {} bool isFallthrough() const { return expr == nullptr; } void validate() const { if (expr && block) { assert(block->list.back() == expr); } } }; // state bool anotherPass; // pass state std::map> breakTails; // break target name => tails // that reach it std::vector unreachableTails; // tails leading to (unreachable) std::vector returnTails; // tails leading to (return) std::set unoptimizables; // break target names that we can't handle std::set modifieds; // modified code should not be processed // again, wait for next pass // walking void visitBreak(Break* curr) { if (curr->condition || curr->value) { unoptimizables.insert(curr->name); } else { // we can only optimize if we are at the end of the parent block, // and if the parent block does not return a value (we can't move // elements out of it if there is a value being returned) Block* parent = controlFlowStack.back()->dynCast(); if (parent && curr == parent->list.back() && !parent->list.back()->type.isConcrete()) { breakTails[curr->name].push_back(Tail(curr, parent)); } else { unoptimizables.insert(curr->name); } } } void visitSwitch(Switch* curr) { for (auto target : curr->targets) { unoptimizables.insert(target); } unoptimizables.insert(curr->default_); } void visitUnreachable(Unreachable* curr) { // we can only optimize if we are at the end of the parent block if (!controlFlowStack.empty()) { Block* parent = controlFlowStack.back()->dynCast(); if (parent && curr == parent->list.back()) { unreachableTails.push_back(Tail(curr, parent)); } } } void visitReturn(Return* curr) { if (!controlFlowStack.empty()) { // we can easily optimize if we are at the end of the parent block Block* parent = controlFlowStack.back()->dynCast(); if (parent && curr == parent->list.back()) { returnTails.push_back(Tail(curr, parent)); return; } } // otherwise, if we have a large value, it might be worth optimizing us as // well returnTails.push_back(Tail(curr, getCurrentPointer())); } void visitBlock(Block* curr) { if (curr->list.empty()) { return; } if (!curr->name.is()) { return; } if (unoptimizables.count(curr->name) > 0) { return; } // we can't optimize a fallthrough value if (curr->list.back()->type.isConcrete()) { return; } auto iter = breakTails.find(curr->name); if (iter == breakTails.end()) { return; } // looks promising auto& tails = iter->second; // see if there is a fallthrough bool hasFallthrough = true; for (auto* child : curr->list) { if (child->type == Type::unreachable) { hasFallthrough = false; } } if (hasFallthrough) { tails.push_back({Tail(curr)}); } optimizeExpressionTails(tails, curr); } void visitIf(If* curr) { if (!curr->ifFalse) { return; } // if both sides are identical, this is easy to fold if (ExpressionAnalyzer::equal(curr->ifTrue, curr->ifFalse)) { Builder builder(*getModule()); // remove if (4 bytes), remove one arm, add drop (1), add block (3), // so this must be a net savings markAsModified(curr); auto* ret = builder.makeSequence(builder.makeDrop(curr->condition), curr->ifTrue); // we must ensure we present the same type as the if had ret->finalize(curr->type); replaceCurrent(ret); } else { // if both are blocks, look for a tail we can merge auto* left = curr->ifTrue->dynCast(); auto* right = curr->ifFalse->dynCast(); // If one is a block and the other isn't, and the non-block is a tail // of the other, we can fold that - for our convenience, we just add // a block and run the rest of the optimization mormally. auto maybeAddBlock = [this](Block* block, Expression*& other) -> Block* { // if other is a suffix of the block, wrap it in a block if (block->list.empty() || !ExpressionAnalyzer::equal(other, block->list.back())) { return nullptr; } // do it, assign to the out param `other`, and return the block Builder builder(*getModule()); auto* ret = builder.makeBlock(other); other = ret; return ret; }; if (left && !right) { right = maybeAddBlock(left, curr->ifFalse); } else if (!left && right) { left = maybeAddBlock(right, curr->ifTrue); } // we need nameless blocks, as if there is a name, someone might branch // to the end, skipping the code we want to merge if (left && right && !left->name.is() && !right->name.is()) { std::vector tails = {Tail(left), Tail(right)}; optimizeExpressionTails(tails, curr); } } } void doWalkFunction(Function* func) { anotherPass = true; while (anotherPass) { anotherPass = false; super::doWalkFunction(func); optimizeTerminatingTails(unreachableTails); // optimize returns at the end, so we can benefit from a fallthrough if // there is a value TODO: separate passes for them? optimizeTerminatingTails(returnTails); // TODO add fallthrough for returns // TODO optimize returns not in blocks, a big return value can be worth it // clean up breakTails.clear(); unreachableTails.clear(); returnTails.clear(); unoptimizables.clear(); modifieds.clear(); // if we did any work, types may need to be propagated if (anotherPass) { ReFinalize().walkFunctionInModule(func, getModule()); } } } private: // check if we can move a list of items out of another item. we can't do so // if one of the items has a branch to something inside outOf that is not // inside that item bool canMove(const std::vector& items, Expression* outOf) { auto allTargets = BranchUtils::getBranchTargets(outOf); for (auto* item : items) { auto exiting = BranchUtils::getExitingBranches(item); std::vector intersection; std::set_intersection(allTargets.begin(), allTargets.end(), exiting.begin(), exiting.end(), std::back_inserter(intersection)); if (intersection.size() > 0) { // anything exiting that is in all targets is something bad return false; } } return true; } // optimize tails that reach the outside of an expression. code that is // identical in all paths leading to the block exit can be merged. template void optimizeExpressionTails(std::vector& tails, T* curr) { if (tails.size() < 2) { return; } // see if anything is untoward, and we should not do this for (auto& tail : tails) { if (tail.expr && modifieds.count(tail.expr) > 0) { return; } if (modifieds.count(tail.block) > 0) { return; } // if we were not modified, then we should be valid for processing tail.validate(); } // we can ignore the final br in a tail auto effectiveSize = [&](const Tail& tail) { auto ret = tail.block->list.size(); if (!tail.isFallthrough()) { ret--; } return ret; }; // the mergeable items do not include the final br in a tail auto getMergeable = [&](const Tail& tail, Index num) { return tail.block->list[effectiveSize(tail) - num - 1]; }; // we are going to remove duplicate elements and add a block. // so for this to make sense, we need the size of the duplicate // elements to be worth that extra block (although, there is // some chance the block would get merged higher up, see later) std::vector mergeable; // the elements we can merge Index num = 0; // how many elements back from the tail to look at Index saved = 0; // how much we can save while (1) { // check if this num is still relevant bool stop = false; for (auto& tail : tails) { assert(tail.block); if (num >= effectiveSize(tail)) { // one of the lists is too short stop = true; break; } } if (stop) { break; } auto* item = getMergeable(tails[0], num); for (auto& tail : tails) { if (!ExpressionAnalyzer::equal(item, getMergeable(tail, num))) { // one of the lists has a different item stop = true; break; } } if (stop) { break; } // we may have found another one we can merge - can we move it? if (!canMove({item}, curr)) { break; } // we found another one we can merge mergeable.push_back(item); num++; saved += Measurer::measure(item); } if (saved == 0) { return; } // we may be able to save enough. if (saved < WORTH_ADDING_BLOCK_TO_REMOVE_THIS_MUCH) { // it's not obvious we can save enough. see if we get rid // of a block, that would justify this bool willEmptyBlock = false; for (auto& tail : tails) { // it is enough to zero out the block, or leave just one // element, as then the block can be replaced with that if (num >= tail.block->list.size() - 1) { willEmptyBlock = true; break; } } if (!willEmptyBlock) { // last chance, if our parent is a block, then it should be // fine to create a new block here, it will be merged up // we are an if or a block, at the top assert(curr == controlFlowStack.back()); if (controlFlowStack.size() <= 1) { return; // no parent at all // TODO: if we are the toplevel in the function, then in the binary // format we might avoid emitting a block, so the same logic // applies here? } auto* parent = controlFlowStack[controlFlowStack.size() - 2]->dynCast(); if (!parent) { return; // parent is not a block } bool isChild = false; for (auto* child : parent->list) { if (child == curr) { isChild = true; break; } } if (!isChild) { return; // not a child, something in between } } } // this is worth doing, do it! for (auto& tail : tails) { // remove the items we are merging / moving // first, mark them as modified, so we don't try to handle them // again in this pass, which might be buggy markAsModified(tail.block); // we must preserve the br if there is one Expression* last = nullptr; if (!tail.isFallthrough()) { last = tail.block->list.back(); tail.block->list.pop_back(); } for (Index i = 0; i < mergeable.size(); i++) { tail.block->list.pop_back(); } if (!tail.isFallthrough()) { tail.block->list.push_back(last); } // the block type may change if we removed unreachable stuff, // but in general it should remain the same, as if it had a // forced type it should remain, *and*, we don't have a // fallthrough value (we would never get here), so a concrete // type was not from that. I.e., any type on the block is // either forced and/or from breaks with a value, so the // type cannot be changed by moving code out. tail.block->finalize(tail.block->type); } // since we managed a merge, then it might open up more opportunities later anotherPass = true; // make a block with curr + the merged code Builder builder(*getModule()); auto* block = builder.makeBlock(); block->list.push_back(curr); while (!mergeable.empty()) { block->list.push_back(mergeable.back()); mergeable.pop_back(); } auto oldType = curr->type; // NB: we template-specialize so that this calls the proper finalizer for // the type curr->finalize(); // ensure the replacement has the same type, so the outside is not surprised block->finalize(oldType); replaceCurrent(block); } // optimize tails that terminate control flow in this function, so we // are (1) merge just a few of them, we don't need all like with the // branches to a block, and (2) we do it on the function body. // num is the depth, i.e., how many tail items we can merge. 0 means // we are just starting; num > 0 means that tails is guaranteed to be // equal in the last num items, so we can merge there, but we look for // deeper merges first. // returns whether we optimized something. bool optimizeTerminatingTails(std::vector& tails, Index num = 0) { if (tails.size() < 2) { return false; } // remove things that are untoward and cannot be optimized tails.erase( std::remove_if(tails.begin(), tails.end(), [&](Tail& tail) { if (tail.expr && modifieds.count(tail.expr) > 0) { return true; } if (tail.block && modifieds.count(tail.block) > 0) { return true; } // if we were not modified, then we should be valid for // processing tail.validate(); return false; }), tails.end()); // now let's try to find subsets that are mergeable. we don't look hard // for the most optimal; further passes may find more // effectiveSize: TODO: special-case fallthrough, matters for returns auto effectiveSize = [&](Tail& tail) -> Index { if (tail.block) { return tail.block->list.size(); } else { return 1; } }; // getItem: returns the relevant item from the tail. this includes the // final item // TODO: special-case fallthrough, matters for returns auto getItem = [&](Tail& tail, Index num) { if (tail.block) { return tail.block->list[effectiveSize(tail) - num - 1]; } else { return tail.expr; } }; // gets the tail elements of a certain depth auto getTailItems = [&](Index num, std::vector& tails) { std::vector items; for (Index i = 0; i < num; i++) { auto item = getItem(tails[0], i); items.push_back(item); } return items; }; // estimate if a merging is worth the cost auto worthIt = [&](Index num, std::vector& tails) { auto items = getTailItems(num, tails); // the elements we can merge Index saved = 0; // how much we can save for (auto* item : items) { saved += Measurer::measure(item) * (tails.size() - 1); } // compure the cost: in non-fallthroughs, we are replacing the final // element with a br; for a fallthrough, if there is one, we must // add a return element (for the function body, so it doesn't reach us) // TODO: handle fallthroughts for return Index cost = tails.size(); // we also need to add two blocks: for us to break to, and to contain // that block and the merged code. very possibly one of the blocks // can be removed, though cost += WORTH_ADDING_BLOCK_TO_REMOVE_THIS_MUCH; // if we cannot merge to the end, then we definitely need 2 blocks, // and a branch // TODO: efficiency, entire body if (!canMove(items, getFunction()->body)) { cost += 1 + WORTH_ADDING_BLOCK_TO_REMOVE_THIS_MUCH; // TODO: to do this, we need to maintain a map of element=>parent, // so that we can insert the new blocks in the right place // for now, just don't do this optimization return false; } // is it worth it? return saved > cost; }; // let's see if we can merge deeper than num, to num + 1 auto next = tails; // remove tails that are too short, or that we hit an item we can't handle next.erase(std::remove_if(next.begin(), next.end(), [&](Tail& tail) { if (effectiveSize(tail) < num + 1) { return true; } auto* newItem = getItem(tail, num); // ignore tails that break to outside blocks. we // want to move code to the very outermost // position, so such code cannot be moved // TODO: this should not be a problem in // *non*-terminating tails, but // double-verify that if (EffectAnalyzer(getPassOptions(), getModule()->features, newItem) .hasExternalBreakTargets()) { return true; } return false; }), next.end()); // if we have enough to investigate, do so if (next.size() >= 2) { // now we want to find a mergeable item - any item that is equal among a // subset std::map hashes; // expression => hash value // hash value => expressions with that hash std::map> hashed; for (auto& tail : next) { auto* item = getItem(tail, num); auto hash = hashes[item] = ExpressionAnalyzer::hash(item); hashed[hash].push_back(item); } // look at each hash value exactly once. we do this in a deterministic // order. std::set seen; for (auto& tail : next) { auto* item = getItem(tail, num); auto hash = hashes[item]; if (seen.count(hash)) { continue; } seen.insert(hash); auto& items = hashed[hash]; if (items.size() == 1) { continue; } assert(items.size() > 0); // look for an item that has another match. while (items.size() >= 2) { auto first = items[0]; std::vector others; items.erase( std::remove_if(items.begin(), items.end(), [&](Expression* item) { if (item == first || // don't bother comparing the first ExpressionAnalyzer::equal(item, first)) { // equal, keep it return false; } else { // unequal, look at it later others.push_back(item); return true; } }), items.end()); if (items.size() >= 2) { // possible merge here, investigate it auto* correct = items[0]; auto explore = next; explore.erase(std::remove_if(explore.begin(), explore.end(), [&](Tail& tail) { auto* item = getItem(tail, num); return !ExpressionAnalyzer::equal( item, correct); }), explore.end()); // try to optimize this deeper tail. if we succeed, then stop here, // as the changes may influence us. we leave further opts to further // passes (as this is rare in practice, it's generally not a perf // issue, but TODO optimize) if (optimizeTerminatingTails(explore, num + 1)) { return true; } } items.swap(others); } } } // we explored deeper (higher num) options, but perhaps there // was nothing there while there is something we can do at this level // but if we are at num == 0, then we found nothing at all if (num == 0) { return false; } // if not worth it, stop if (!worthIt(num, tails)) { return false; } // this is worth doing, do it! auto mergeable = getTailItems(num, tails); // the elements we can merge // since we managed a merge, then it might open up more opportunities later anotherPass = true; Builder builder(*getModule()); // TODO: don't create one per merge, linear in function size LabelUtils::LabelManager labels(getFunction()); Name innerName = labels.getUnique("folding-inner"); for (auto& tail : tails) { // remove the items we are merging / moving, and add a break // also mark as modified, so we don't try to handle them // again in this pass, which might be buggy if (tail.block) { markAsModified(tail.block); for (Index i = 0; i < mergeable.size(); i++) { tail.block->list.pop_back(); } tail.block->list.push_back(builder.makeBreak(innerName)); tail.block->finalize(tail.block->type); } else { markAsModified(tail.expr); *tail.pointer = builder.makeBreak(innerName); } } // make a block with the old body + the merged code auto* old = getFunction()->body; auto* inner = builder.makeBlock(); inner->name = innerName; if (old->type == Type::unreachable) { // the old body is not flowed out of anyhow, so just put it there inner->list.push_back(old); } else { // otherwise, we must not flow out to the merged code if (old->type == Type::none) { inner->list.push_back(old); inner->list.push_back(builder.makeReturn()); } else { // looks like we must return this. but if it's a toplevel block // then it might be marked as having a type, but not actually // returning it (we marked it as such for wasm type-checking // rules, and now it won't be toplevel in the function, it can // change) auto* toplevel = old->dynCast(); if (toplevel) { toplevel->finalize(); } if (old->type != Type::unreachable) { inner->list.push_back(builder.makeReturn(old)); } else { inner->list.push_back(old); } } } inner->finalize(); auto* outer = builder.makeBlock(); outer->list.push_back(inner); while (!mergeable.empty()) { outer->list.push_back(mergeable.back()); mergeable.pop_back(); } // ensure the replacement has the same type, so the outside is not surprised outer->finalize(getFunction()->sig.results); getFunction()->body = outer; return true; } void markAsModified(Expression* curr) { ExpressionMarker marker(modifieds, curr); } }; Pass* createCodeFoldingPass() { return new CodeFolding(); } } // namespace wasm binaryen-version_91/src/passes/CodePushing.cpp000066400000000000000000000220031362402614000216550ustar00rootroot00000000000000/* * Copyright 2016 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // // Pushes code "forward" as much as possible, potentially into // a location behind a condition, where it might not always execute. // #include #include #include #include namespace wasm { // // Analyzers some useful local properties: # of sets and gets, and SFA. // // Single First Assignment (SFA) form: the local has a single local.set, is // not a parameter, and has no local.gets before the local.set in postorder. // This is a much weaker property than SSA, obviously, but together with // our implicit dominance properties in the structured AST is quite useful. // struct LocalAnalyzer : public PostWalker { std::vector sfa; std::vector numSets; std::vector numGets; void analyze(Function* func) { auto num = func->getNumLocals(); numSets.resize(num); std::fill(numSets.begin(), numSets.end(), 0); numGets.resize(num); std::fill(numGets.begin(), numGets.end(), 0); sfa.resize(num); std::fill(sfa.begin(), sfa.begin() + func->getNumParams(), false); std::fill(sfa.begin() + func->getNumParams(), sfa.end(), true); walk(func->body); for (Index i = 0; i < num; i++) { if (numSets[i] == 0) { sfa[i] = false; } } } bool isSFA(Index i) { return sfa[i]; } Index getNumGets(Index i) { return numGets[i]; } void visitLocalGet(LocalGet* curr) { if (numSets[curr->index] == 0) { sfa[curr->index] = false; } numGets[curr->index]++; } void visitLocalSet(LocalSet* curr) { numSets[curr->index]++; if (numSets[curr->index] > 1) { sfa[curr->index] = false; } } }; // Implements core optimization logic. Used and then discarded entirely // for each block. class Pusher { ExpressionList& list; LocalAnalyzer& analyzer; std::vector& numGetsSoFar; PassOptions& passOptions; FeatureSet features; public: Pusher(Block* block, LocalAnalyzer& analyzer, std::vector& numGetsSoFar, PassOptions& passOptions, FeatureSet features) : list(block->list), analyzer(analyzer), numGetsSoFar(numGetsSoFar), passOptions(passOptions), features(features) { // Find an optimization segment: from the first pushable thing, to the first // point past which we want to push. We then push in that range before // continuing forward. // we never need to push past a final element, as we couldn't be used after // it. Index relevant = list.size() - 1; const Index nothing = -1; Index i = 0; Index firstPushable = nothing; while (i < relevant) { if (firstPushable == nothing && isPushable(list[i])) { firstPushable = i; i++; continue; } if (firstPushable != nothing && isPushPoint(list[i])) { // optimize this segment, and proceed from where it tells us i = optimizeSegment(firstPushable, i); firstPushable = nothing; continue; } i++; } } private: LocalSet* isPushable(Expression* curr) { auto* set = curr->dynCast(); if (!set) { return nullptr; } auto index = set->index; // to be pushable, this must be SFA and the right # of gets, // but also have no side effects, as it may not execute if pushed. if (analyzer.isSFA(index) && numGetsSoFar[index] == analyzer.getNumGets(index) && !EffectAnalyzer(passOptions, features, set->value).hasSideEffects()) { return set; } return nullptr; } // Push past conditional control flow. // TODO: push into ifs as well bool isPushPoint(Expression* curr) { // look through drops if (auto* drop = curr->dynCast()) { curr = drop->value; } if (curr->is() || curr->is()) { return true; } if (auto* br = curr->dynCast()) { return !!br->condition; } return false; } Index optimizeSegment(Index firstPushable, Index pushPoint) { // The interesting part. Starting at firstPushable, try to push // code past pushPoint. We start at the end since we are pushing // forward, that way we can push later things out of the way // of earlier ones. Once we know all we can push, we push it all // in one pass, keeping the order of the pushables intact. assert(firstPushable != Index(-1) && pushPoint != Index(-1) && firstPushable < pushPoint); // everything that matters if you want to be pushed past the pushPoint EffectAnalyzer cumulativeEffects(passOptions, features); cumulativeEffects.analyze(list[pushPoint]); // it is ok to ignore the branching here, that is the crucial point of this // opt cumulativeEffects.branches = false; std::vector toPush; Index i = pushPoint - 1; while (1) { auto* pushable = isPushable(list[i]); if (pushable) { auto iter = pushableEffects.find(pushable); if (iter == pushableEffects.end()) { iter = pushableEffects .emplace(std::piecewise_construct, std::forward_as_tuple(pushable), std::forward_as_tuple(passOptions, features, pushable)) .first; } auto& effects = iter->second; if (cumulativeEffects.invalidates(effects)) { // we can't push this, so further pushables must pass it cumulativeEffects.mergeIn(effects); } else { // we can push this, great! toPush.push_back(pushable); } if (i == firstPushable) { // no point in looking further break; } } else { // something that can't be pushed, so it might block further pushing cumulativeEffects.analyze(list[i]); } assert(i > 0); i--; } if (toPush.size() == 0) { // nothing to do, can only continue after the push point return pushPoint + 1; } // we have work to do! Index total = toPush.size(); Index last = total - 1; Index skip = 0; for (Index i = firstPushable; i <= pushPoint; i++) { // we see the first elements at the end of toPush if (skip < total && list[i] == toPush[last - skip]) { // this is one of our elements to push, skip it skip++; } else { if (skip) { list[i - skip] = list[i]; } } } assert(skip == total); // write out the skipped elements for (Index i = 0; i < total; i++) { list[pushPoint - i] = toPush[i]; } // proceed right after the push point, we may push the pushed elements again return pushPoint - total + 1; } // Pushables may need to be scanned more than once, so cache their effects. std::unordered_map pushableEffects; }; struct CodePushing : public WalkerPass> { bool isFunctionParallel() override { return true; } Pass* create() override { return new CodePushing; } LocalAnalyzer analyzer; // gets seen so far in the main traversal std::vector numGetsSoFar; void doWalkFunction(Function* func) { // pre-scan to find which vars are sfa, and also count their gets&sets analyzer.analyze(func); // prepare to walk numGetsSoFar.resize(func->getNumLocals()); std::fill(numGetsSoFar.begin(), numGetsSoFar.end(), 0); // walk and optimize walk(func->body); } void visitLocalGet(LocalGet* curr) { numGetsSoFar[curr->index]++; } void visitBlock(Block* curr) { // Pushing code only makes sense if we are size 3 or above: we need // one element to push, an element to push it past, and an element to use // what we pushed. if (curr->list.size() < 3) { return; } // At this point in the postorder traversal we have gone through all our // children. Therefore any variable whose gets seen so far is equal to the // total gets must have no further users after this block. And therefore // when we see an SFA variable defined here, we know it isn't used before it // either, and has just this one assign. So we can push it forward while we // don't hit a non-control-flow ordering invalidation issue, since if this // isn't a loop, it's fine (we're not used outside), and if it is, we hit // the assign before any use (as we can't push it past a use). Pusher pusher( curr, analyzer, numGetsSoFar, getPassOptions(), getModule()->features); } }; Pass* createCodePushingPass() { return new CodePushing(); } } // namespace wasm binaryen-version_91/src/passes/ConstHoisting.cpp000066400000000000000000000103351362402614000222450ustar00rootroot00000000000000/* * Copyright 2017 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // // Hoists repeated constants to a local. A local.get takes 2 bytes // in most cases, and if a const is larger than that, it may be // better to store it to a local, then get it from that local. // // WARNING: this often shrinks code size, but can *increase* gzip // size. apparently having the constants in their proper // places lets them be compressed better, across // functions, etc. TODO investigate // TODO: hoisting a zero does not even require an initial set! // TODO: hoisting a float or double zero is especially beneficial as there // is no LEB compression for them, and no need for the set, so // each f32.const is 5 bytes and f64.const is 9 bytes, while it is // <= 1 byte to declare the local and 2-3 to use it! // #include #include #include #include #include namespace wasm { // with fewer uses than this, it is never beneficial to hoist static const Index MIN_USES = 2; struct ConstHoisting : public WalkerPass> { bool isFunctionParallel() override { return true; } Pass* create() override { return new ConstHoisting; } std::map> uses; void visitConst(Const* curr) { uses[curr->value].push_back(getCurrentPointer()); } void visitFunction(Function* curr) { std::vector prelude; for (auto& pair : uses) { auto value = pair.first; auto& vec = pair.second; auto num = vec.size(); if (worthHoisting(value, num)) { prelude.push_back(hoist(vec)); } } if (!prelude.empty()) { Builder builder(*getModule()); // merge-blocks can optimize this into a single block later in most cases curr->body = builder.makeSequence(builder.makeBlock(prelude), curr->body); } } private: bool worthHoisting(Literal value, Index num) { if (num < MIN_USES) { return false; } // measure the size of the constant Index size = 0; switch (value.type.getSingle()) { case Type::i32: { size = getWrittenSize(S32LEB(value.geti32())); break; } case Type::i64: { size = getWrittenSize(S64LEB(value.geti64())); break; } case Type::f32: case Type::f64: { size = value.type.getByteSize(); break; } // not implemented yet case Type::v128: case Type::funcref: case Type::anyref: case Type::nullref: case Type::exnref: { return false; } case Type::none: case Type::unreachable: WASM_UNREACHABLE("unexpected type"); } // compute the benefit, of replacing the uses with // one use + a set and then a get for each use // doing the algebra, the criterion here is when // size > 2(1+num)/(num-1) // or // num > (size+2)/(size-2) auto before = num * size; auto after = size + 2 /* local.set */ + (2 /* local.get */ * num); return after < before; } template Index getWrittenSize(const T& thing) { BufferWithRandomAccess buffer; buffer << thing; return buffer.size(); } // replace all the uses with gets, for a local set at the top. returns // the set. Expression* hoist(std::vector& vec) { auto type = (*(vec[0]))->type; Builder builder(*getModule()); auto temp = builder.addVar(getFunction(), type); auto* ret = builder.makeLocalSet(temp, *(vec[0])); for (auto item : vec) { *item = builder.makeLocalGet(temp, type); } return ret; } }; Pass* createConstHoistingPass() { return new ConstHoisting(); } } // namespace wasm binaryen-version_91/src/passes/DWARF.cpp000066400000000000000000000023451362402614000203170ustar00rootroot00000000000000/* * Copyright 2019 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // // Dump DWARF sections. This results in something similar to llvm-dwarfdump, // as it uses the same code. // // Note that this dumps the DWARF data read from the binary when we loaded it. // It does not contain changes made since then, which will only be updated // when we write the binary. To see those changes, you must round-trip. // #include "pass.h" #include "wasm-debug.h" #include "wasm.h" namespace wasm { struct DWARFDump : public Pass { void run(PassRunner* runner, Module* module) override { Debug::dumpDWARF(*module); } }; Pass* createDWARFDumpPass() { return new DWARFDump(); } } // namespace wasm binaryen-version_91/src/passes/DataFlowOpts.cpp000066400000000000000000000222241362402614000220210ustar00rootroot00000000000000/* * Copyright 2018 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // // Optimize using the DataFlow SSA IR. // // This needs 'flatten' to be run before it, and you should run full // regular opts afterwards to clean up the flattening. For example, // you might use it like this: // // --flatten --dfo -Os // #include "dataflow/graph.h" #include "dataflow/node.h" #include "dataflow/users.h" #include "dataflow/utils.h" #include "ir/flat.h" #include "ir/utils.h" #include "pass.h" #include "wasm-builder.h" #include "wasm.h" namespace wasm { struct DataFlowOpts : public WalkerPass> { bool isFunctionParallel() override { return true; } Pass* create() override { return new DataFlowOpts; } DataFlow::Users nodeUsers; // The optimization work left to do: nodes that we need to look at. std::unordered_set workLeft; DataFlow::Graph graph; void doWalkFunction(Function* func) { Flat::verifyFlatness(func); // Build the data-flow IR. graph.build(func, getModule()); nodeUsers.build(graph); // Propagate optimizations through the graph. std::unordered_set optimized; // which nodes we optimized for (auto& node : graph.nodes) { workLeft.insert(node.get()); // we should try to optimize each node } while (!workLeft.empty()) { // std::cout << "\n\ndump before work iter\n"; // dump(graph, std::cout); auto iter = workLeft.begin(); auto* node = *iter; workLeft.erase(iter); workOn(node); } // After updating the DataFlow IR, we can update the sets in // the wasm. // TODO: we also need phis, as a phi can flow directly into say // a return or a call parameter. for (auto* set : graph.sets) { auto* node = graph.setNodeMap[set]; auto iter = optimized.find(node); if (iter != optimized.end()) { assert(node->isExpr()); // this is a set, where the node is defined set->value = node->expr; } } } void workOn(DataFlow::Node* node) { if (node->isConst()) { return; } // If there are no uses, there is no point to work. if (nodeUsers.getNumUses(node) == 0) { return; } // Optimize: Look for nodes that we can easily convert into // something simpler. // TODO: we can expressionify and run full normal opts on that, // then copy the result if it's smaller. if (node->isPhi() && DataFlow::allInputsIdentical(node)) { // Note we don't need to check for effects when replacing, as in // flattened IR expression children are local.gets or consts. auto* value = node->getValue(1); if (value->isConst()) { replaceAllUsesWith(node, value); } } else if (node->isExpr() && DataFlow::allInputsConstant(node)) { assert(!node->isConst()); // If this is a concrete value (not e.g. an eqz of unreachable), // it can definitely be precomputed into a constant. if (node->expr->type.isConcrete()) { // This can be precomputed. // TODO not just all-constant inputs? E.g. i32.mul of 0 and X. optimizeExprToConstant(node); } } } void optimizeExprToConstant(DataFlow::Node* node) { assert(node->isExpr()); assert(!node->isConst()); // std::cout << "will optimize an Expr of all constant inputs. before" << // '\n'; // dump(node, std::cout); auto* expr = node->expr; // First, note that some of the expression's children may be // local.gets that we inferred during SSA analysis as constant. // We can apply those now. for (Index i = 0; i < node->values.size(); i++) { if (node->values[i]->isConst()) { auto* currp = getIndexPointer(expr, i); // Directly represent it as a constant. (Note that it may already be // a constant, but for now to avoid corner cases just replace them // all here.) auto* c = node->values[i]->expr->dynCast(); *currp = Builder(*getModule()).makeConst(c->value); } } // Now we know that all our DataFlow inputs are constant, and all // our Binaryen IR representations of them are constant too. RUn // precompute, which will transform the expression into a constanat. Module temp; // XXX we should copy expr here, in principle, and definitely will need to // when we do arbitrarily regenerated expressions auto* func = Builder(temp).makeFunction( "temp", Signature(Type::none, Type::none), {}, expr); PassRunner runner(&temp); runner.setIsNested(true); runner.add("precompute"); runner.runOnFunction(func); // Get the optimized thing auto* result = func->body; // It may not be a constant, e.g. 0 / 0 does not optimize to 0 if (!result->is()) { return; } // All good, copy it. node->expr = Builder(*getModule()).makeConst(result->cast()->value); assert(node->isConst()); // We no longer have values, and so do not use anything. nodeUsers.stopUsingValues(node); node->values.clear(); // Our contents changed, update our users. replaceAllUsesWith(node, node); } // Replaces all uses of a node with another value. This both modifies // the DataFlow IR to make the other users point to this one, and // updates the underlying Binaryen IR as well. // This can be used to "replace" a node with itself, which makes sense // when the node contents have changed and so the users must be updated. void replaceAllUsesWith(DataFlow::Node* node, DataFlow::Node* with) { // Const nodes are trivial to replace, but other stuff is trickier - // in particular phis. assert(with->isConst()); // TODO // All the users should be worked on later, as we will update them. auto& users = nodeUsers.getUsers(node); for (auto* user : users) { // Add the user to the work left to do, as we are modifying it. workLeft.insert(user); // `with` is getting another user. nodeUsers.addUser(with, user); // Replacing in the DataFlow IR is simple - just replace it, // in all the indexes it appears. std::vector indexes; for (Index i = 0; i < user->values.size(); i++) { if (user->values[i] == node) { user->values[i] = with; indexes.push_back(i); } } assert(!indexes.empty()); // Replacing in the Binaryen IR requires more care switch (user->type) { case DataFlow::Node::Type::Expr: { auto* expr = user->expr; for (auto index : indexes) { *(getIndexPointer(expr, index)) = graph.makeUse(with); } break; } case DataFlow::Node::Type::Phi: { // Nothing to do: a phi is not in the Binaryen IR. // If the entire phi can become a constant, that will be // propagated when we process that phi later. break; } case DataFlow::Node::Type::Cond: { // Nothing to do: a cond is not in the Binaryen IR. // If the cond input is a constant, that might indicate // useful optimizations are possible, which perhaps we // should look into TODO break; } case DataFlow::Node::Type::Zext: { // Nothing to do: a zext is not in the Binaryen IR. // If the cond input is a constant, that might indicate // useful optimizations are possible, which perhaps we // should look into TODO break; } default: WASM_UNREACHABLE("unexpected dataflow node type"); } } // No one is a user of this node after we replaced all the uses. nodeUsers.removeAllUsesOf(node); } // Gets a pointer to the expression pointer in an expression. // That is, given an index in the values() vector, get an // Expression** that we can assign to so as to modify it. Expression** getIndexPointer(Expression* expr, Index index) { if (auto* unary = expr->dynCast()) { assert(index == 0); return &unary->value; } else if (auto* binary = expr->dynCast()) { if (index == 0) { return &binary->left; } else if (index == 1) { return &binary->right; } WASM_UNREACHABLE("unexpected index"); } else if (auto* select = expr->dynCast()) { select->condition = optimizeBoolean(select->condition); auto* condition = select->condition->dynCast(); if (condition && condition->op == EqZInt32) { // flip select to remove eqz, if we can reorder EffectAnalyzer ifTrue(getPassOptions(), features, select->ifTrue); EffectAnalyzer ifFalse(getPassOptions(), features, select->ifFalse); if (!ifTrue.invalidates(ifFalse)) { select->condition = condition->value; std::swap(select->ifTrue, select->ifFalse); } } if (auto* c = select->condition->dynCast()) { // constant condition, we can just pick the right side (barring side // effects) if (c->value.getInteger()) { if (!EffectAnalyzer(getPassOptions(), features, select->ifFalse) .hasSideEffects()) { return select->ifTrue; } else { // don't bother - we would need to reverse the order using a temp // local, which is bad } } else { if (!EffectAnalyzer(getPassOptions(), features, select->ifTrue) .hasSideEffects()) { return select->ifFalse; } else { Builder builder(*getModule()); return builder.makeSequence(builder.makeDrop(select->ifTrue), select->ifFalse); } } } if (ExpressionAnalyzer::equal(select->ifTrue, select->ifFalse)) { // sides are identical, fold EffectAnalyzer value(getPassOptions(), features, select->ifTrue); if (value.hasSideEffects()) { // at best we don't need the condition, but need to execute the value // twice. a block is larger than a select by 2 bytes, and // we must drop one value, so 3, while we save the condition, // so it's not clear this is worth it, TODO } else { // value has no side effects EffectAnalyzer condition( getPassOptions(), features, select->condition); if (!condition.hasSideEffects()) { return select->ifTrue; } else { // the condition is last, so we need a new local, and it may be // a bad idea to use a block like we do for an if. Do it only if we // can reorder if (!condition.invalidates(value)) { Builder builder(*getModule()); return builder.makeSequence(builder.makeDrop(select->condition), select->ifTrue); } } } } } else if (auto* br = curr->dynCast()) { if (br->condition) { br->condition = optimizeBoolean(br->condition); } } else if (auto* load = curr->dynCast()) { optimizeMemoryAccess(load->ptr, load->offset); } else if (auto* store = curr->dynCast()) { optimizeMemoryAccess(store->ptr, store->offset); // stores of fewer bits truncates anyhow if (auto* binary = store->value->dynCast()) { if (binary->op == AndInt32) { if (auto* right = binary->right->dynCast()) { if (right->type == Type::i32) { auto mask = right->value.geti32(); if ((store->bytes == 1 && mask == 0xff) || (store->bytes == 2 && mask == 0xffff)) { store->value = binary->left; } } } } else if (auto* ext = Properties::getSignExtValue(binary)) { // if sign extending the exact bit size we store, we can skip the // extension if extending something bigger, then we just alter bits we // don't save anyhow if (Properties::getSignExtBits(binary) >= Index(store->bytes) * 8) { store->value = ext; } } } else if (auto* unary = store->value->dynCast()) { if (unary->op == WrapInt64) { // instead of wrapping to 32, just store some of the bits in the i64 store->valueType = Type::i64; store->value = unary->value; } } } return nullptr; } Index getMaxBitsForLocal(LocalGet* get) { // check what we know about the local return localInfo[get->index].maxBits; } private: // Information about our locals std::vector localInfo; // Canonicalizing the order of a symmetric binary helps us // write more concise pattern matching code elsewhere. void canonicalize(Binary* binary) { assert(Properties::isSymmetric(binary)); FeatureSet features = getModule()->features; auto swap = [&]() { assert(EffectAnalyzer::canReorder( getPassOptions(), features, binary->left, binary->right)); std::swap(binary->left, binary->right); }; auto maybeSwap = [&]() { if (EffectAnalyzer::canReorder( getPassOptions(), features, binary->left, binary->right)) { swap(); } }; // Prefer a const on the right. if (binary->left->is() && !binary->right->is()) { return swap(); } if (binary->right->is()) { return; } // Prefer a get on the right. if (binary->left->is() && !binary->right->is()) { return maybeSwap(); } // Sort by the node id type, if different. if (binary->left->_id != binary->right->_id) { if (binary->left->_id > binary->right->_id) { return maybeSwap(); } return; } // If the children have the same node id, we have to go deeper. if (auto* left = binary->left->dynCast()) { auto* right = binary->right->cast(); if (left->op > right->op) { return maybeSwap(); } } if (auto* left = binary->left->dynCast()) { auto* right = binary->right->cast(); if (left->op > right->op) { return maybeSwap(); } } if (auto* left = binary->left->dynCast()) { auto* right = binary->right->cast(); if (left->index > right->index) { return maybeSwap(); } } } // Optimize given that the expression is flowing into a boolean context Expression* optimizeBoolean(Expression* boolean) { // TODO use a general getFallthroughs if (auto* unary = boolean->dynCast()) { if (unary && unary->op == EqZInt32) { auto* unary2 = unary->value->dynCast(); if (unary2 && unary2->op == EqZInt32) { // double eqz return unary2->value; } } } else if (auto* binary = boolean->dynCast()) { if (binary->op == OrInt32) { // an or flowing into a boolean context can consider each input as // boolean binary->left = optimizeBoolean(binary->left); binary->right = optimizeBoolean(binary->right); } else if (binary->op == NeInt32) { // x != 0 is just x if it's used as a bool if (auto* num = binary->right->dynCast()) { if (num->value.geti32() == 0) { return binary->left; } } } if (auto* ext = Properties::getSignExtValue(binary)) { // use a cheaper zero-extent, we just care about the boolean value // anyhow return makeZeroExt(ext, Properties::getSignExtBits(binary)); } } else if (auto* block = boolean->dynCast()) { if (block->type == Type::i32 && block->list.size() > 0) { block->list.back() = optimizeBoolean(block->list.back()); } } else if (auto* iff = boolean->dynCast()) { if (iff->type == Type::i32) { iff->ifTrue = optimizeBoolean(iff->ifTrue); iff->ifFalse = optimizeBoolean(iff->ifFalse); } } else if (auto* select = boolean->dynCast(); } return false; } void findExternalUses() { // Find all the wasm code represented in this trace. std::unordered_set origins; for (auto& node : nodes) { if (auto* origin = node->origin) { if (debug() >= 2) { std::cout << "note origin " << origin << '\n'; } origins.insert(origin); } } for (auto& node : nodes) { if (node == toInfer) { continue; } if (auto* origin = node->origin) { auto uses = UseFinder().getUses(origin, graph, localGraph); for (auto* use : uses) { // A non-set use (a drop or return etc.) is definitely external. // Otherwise, check if internal or external. if (use == nullptr || origins.count(use) == 0) { if (debug() >= 2) { std::cout << "found external use for\n"; dump(node, std::cout); std::cout << " due to " << use << '\n'; } hasExternalUses.insert(node); break; } } } } } }; // Emits a trace, which is basically a Souper LHS. struct Printer { Graph& graph; Trace& trace; // Each Node in a trace has an index, from 0. std::unordered_map indexing; bool printedHasExternalUses = false; Printer(Graph& graph, Trace& trace) : graph(graph), trace(trace) { std::cout << "\n; start LHS (in " << graph.func->name << ")\n"; // Index the nodes. for (auto* node : trace.nodes) { // pcs and blockpcs are not instructions and do not need to be indexed if (!node->isCond()) { auto index = indexing.size(); indexing[node] = index; } } // Print them out. for (auto* node : trace.nodes) { print(node); } // Print out pcs. for (auto* condition : trace.pathConditions) { printPathCondition(condition); } // Finish up std::cout << "infer %" << indexing[trace.toInfer] << "\n\n"; } Node* getMaybeReplaced(Node* node) { auto iter = trace.replacements.find(node); if (iter != trace.replacements.end()) { return iter->second.get(); } return node; } void print(Node* node) { // The node may have been replaced during trace building, if so then // print the proper replacement. node = getMaybeReplaced(node); assert(node); switch (node->type) { case Node::Type::Var: { std::cout << "%" << indexing[node] << ":" << node->wasmType << " = var"; break; // nothing more to add } case Node::Type::Expr: { if (debug()) { std::cout << "; "; WasmPrinter::printExpression(node->expr, std::cout, true); std::cout << '\n'; } std::cout << "%" << indexing[node] << " = "; printExpression(node); break; } case Node::Type::Phi: { auto* block = node->getValue(0); auto size = block->values.size(); std::cout << "%" << indexing[node] << " = phi %" << indexing[block]; for (Index i = 1; i < size + 1; i++) { std::cout << ", "; printInternal(node->getValue(i)); } break; } case Node::Type::Cond: { std::cout << "blockpc %" << indexing[node->getValue(0)] << ' ' << node->index << ' '; printInternal(node->getValue(1)); std::cout << " 1:i1"; break; } case Node::Type::Block: { std::cout << "%" << indexing[node] << " = block " << node->values.size(); break; } case Node::Type::Zext: { auto* child = node->getValue(0); std::cout << "%" << indexing[node] << ':' << child->getWasmType(); std::cout << " = zext "; printInternal(child); break; } case Node::Type::Bad: { WASM_UNREACHABLE("!!!BAD!!!"); } default: WASM_UNREACHABLE("unexpted type"); } if (node->isExpr() || node->isPhi()) { if (node->origin != trace.toInfer->origin && trace.hasExternalUses.count(node) > 0) { std::cout << " (hasExternalUses)"; printedHasExternalUses = true; } } std::cout << '\n'; if (debug() && (node->isExpr() || node->isPhi())) { warnOnSuspiciousValues(node); } } void print(Literal value) { std::cout << value.getInteger() << ':' << value.type; } void printInternal(Node* node) { node = getMaybeReplaced(node); assert(node); if (node->isConst()) { print(node->expr->cast()->value); } else { std::cout << "%" << indexing[node]; } } // Emit an expression void printExpression(Node* node) { assert(node->isExpr()); // TODO use a Visitor here? auto* curr = node->expr; if (auto* c = curr->dynCast()) { print(c->value); } else if (auto* unary = curr->dynCast()) { switch (unary->op) { case ClzInt32: case ClzInt64: std::cout << "ctlz"; break; case CtzInt32: case CtzInt64: std::cout << "cttz"; break; case PopcntInt32: case PopcntInt64: std::cout << "ctpop"; break; default: WASM_UNREACHABLE("invalid op"); } std::cout << ' '; auto* value = node->getValue(0); printInternal(value); } else if (auto* binary = curr->dynCast()) { switch (binary->op) { case AddInt32: case AddInt64: std::cout << "add"; break; case SubInt32: case SubInt64: std::cout << "sub"; break; case MulInt32: case MulInt64: std::cout << "mul"; break; case DivSInt32: case DivSInt64: std::cout << "sdiv"; break; case DivUInt32: case DivUInt64: std::cout << "udiv"; break; case RemSInt32: case RemSInt64: std::cout << "srem"; break; case RemUInt32: case RemUInt64: std::cout << "urem"; break; case AndInt32: case AndInt64: std::cout << "and"; break; case OrInt32: case OrInt64: std::cout << "or"; break; case XorInt32: case XorInt64: std::cout << "xor"; break; case ShlInt32: case ShlInt64: std::cout << "shl"; break; case ShrUInt32: case ShrUInt64: std::cout << "lshr"; break; case ShrSInt32: case ShrSInt64: std::cout << "ashr"; break; case RotLInt32: case RotLInt64: std::cout << "rotl"; break; case RotRInt32: case RotRInt64: std::cout << "rotr"; break; case EqInt32: case EqInt64: std::cout << "eq"; break; case NeInt32: case NeInt64: std::cout << "ne"; break; case LtSInt32: case LtSInt64: std::cout << "slt"; break; case LtUInt32: case LtUInt64: std::cout << "ult"; break; case LeSInt32: case LeSInt64: std::cout << "sle"; break; case LeUInt32: case LeUInt64: std::cout << "ule"; break; default: WASM_UNREACHABLE("invalid op"); } std::cout << ' '; auto* left = node->getValue(0); printInternal(left); std::cout << ", "; auto* right = node->getValue(1); printInternal(right); } else if (curr->is(); if (EffectAnalyzer(getPassOptions(), features, select->ifTrue) .hasSideEffects()) { if (EffectAnalyzer(getPassOptions(), features, select->ifFalse) .hasSideEffects()) { return curr; // leave them } else { if (EffectAnalyzer( getPassOptions(), features, select->condition) .hasSideEffects()) { return curr; // leave them } else { curr = select->ifTrue; continue; } } } else { if (EffectAnalyzer(getPassOptions(), features, select->ifFalse) .hasSideEffects()) { if (EffectAnalyzer( getPassOptions(), features, select->condition) .hasSideEffects()) { return curr; // leave them } else { curr = select->ifFalse; continue; } } else { if (EffectAnalyzer( getPassOptions(), features, select->condition) .hasSideEffects()) { curr = select->condition; continue; } else { return nullptr; } } } } } default: return curr; // assume needed } } } void visitBlock(Block* curr) { // compress out nops and other dead code int skip = 0; auto& list = curr->list; size_t size = list.size(); for (size_t z = 0; z < size; z++) { auto* child = list[z]; // The last element may be used. bool used = z == size - 1 && curr->type.isConcrete() && ExpressionAnalyzer::isResultUsed(expressionStack, getFunction()); auto* optimized = optimize(child, used, true); if (!optimized) { if (child->type.isConcrete()) { // We can't just skip a final concrete element, even if it isn't used. // Instead, replace it with something that's easy to optimize out (for // example, code-folding can merge out identical zeros at the end of // if arms). optimized = LiteralUtils::makeZero(child->type, *getModule()); } else if (child->type == Type::unreachable) { // Don't try to optimize out an unreachable child (dce can do that // properly). optimized = child; } } if (!optimized) { typeUpdater.noteRecursiveRemoval(child); skip++; } else { if (optimized != child) { typeUpdater.noteReplacement(child, optimized); list[z] = optimized; } if (skip > 0) { list[z - skip] = list[z]; list[z] = nullptr; } // if this is unreachable, the rest is dead code if (list[z - skip]->type == Type::unreachable && z < size - 1) { for (Index i = z - skip + 1; i < list.size(); i++) { auto* remove = list[i]; if (remove) { typeUpdater.noteRecursiveRemoval(remove); } } list.resize(z - skip + 1); typeUpdater.maybeUpdateTypeToUnreachable(curr); skip = 0; // nothing more to do on the list break; } } } if (skip > 0) { list.resize(size - skip); typeUpdater.maybeUpdateTypeToUnreachable(curr); } // the block may now be a trivial one that we can get rid of and just leave // its contents replaceCurrent(BlockUtils::simplifyToContents(curr, this)); } void visitIf(If* curr) { // if the condition is a constant, just apply it // we can just return the ifTrue or ifFalse. if (auto* value = curr->condition->dynCast()) { Expression* child; if (value->value.getInteger()) { child = curr->ifTrue; if (curr->ifFalse) { typeUpdater.noteRecursiveRemoval(curr->ifFalse); } } else { if (curr->ifFalse) { child = curr->ifFalse; typeUpdater.noteRecursiveRemoval(curr->ifTrue); } else { typeUpdater.noteRecursiveRemoval(curr); ExpressionManipulator::nop(curr); return; } } replaceCurrent(child); return; } // if the condition is unreachable, just return it if (curr->condition->type == Type::unreachable) { typeUpdater.noteRecursiveRemoval(curr->ifTrue); if (curr->ifFalse) { typeUpdater.noteRecursiveRemoval(curr->ifFalse); } replaceCurrent(curr->condition); return; } // from here on, we can assume the condition executed if (curr->ifFalse) { if (curr->ifFalse->is()) { curr->ifFalse = nullptr; } else if (curr->ifTrue->is()) { curr->ifTrue = curr->ifFalse; curr->ifFalse = nullptr; curr->condition = Builder(*getModule()).makeUnary(EqZInt32, curr->condition); } else if (curr->ifTrue->is() && curr->ifFalse->is()) { // instead of dropping both sides, drop the if, if they are the same // type auto* left = curr->ifTrue->cast()->value; auto* right = curr->ifFalse->cast()->value; if (left->type == right->type) { curr->ifTrue = left; curr->ifFalse = right; curr->finalize(); replaceCurrent(Builder(*getModule()).makeDrop(curr)); } } } else { // no else if (curr->ifTrue->is()) { // no nothing replaceCurrent(Builder(*getModule()).makeDrop(curr->condition)); } } } void visitLoop(Loop* curr) { if (curr->body->is()) { ExpressionManipulator::nop(curr); } } void visitDrop(Drop* curr) { // optimize the dropped value, maybe leaving nothing curr->value = optimize(curr->value, false, false); if (curr->value == nullptr) { ExpressionManipulator::nop(curr); return; } // a drop of a tee is a set if (auto* set = curr->value->dynCast()) { assert(set->isTee()); set->makeSet(); replaceCurrent(set); return; } // if we are dropping a block's return value, we might be able to remove it // entirely if (auto* block = curr->value->dynCast()) { auto* last = block->list.back(); // note that the last element may be concrete but not the block, if the // block has an unreachable element in the middle, making the block // unreachable despite later elements and in particular the last if (last->type.isConcrete() && block->type == last->type) { last = optimize(last, false, false); if (!last) { // we may be able to remove this, if there are no brs bool canPop = true; if (block->name.is()) { BranchUtils::BranchSeeker seeker(block->name); Expression* temp = block; seeker.walk(temp); if (seeker.found && seeker.valueType != Type::none) { canPop = false; } } if (canPop) { block->list.back() = last; block->list.pop_back(); block->type = Type::none; // we don't need the drop anymore, let's see what we have left in // the block if (block->list.size() > 1) { replaceCurrent(block); } else if (block->list.size() == 1) { replaceCurrent(block->list[0]); } else { ExpressionManipulator::nop(curr); } return; } } } } // sink a drop into an arm of an if-else if the other arm ends in an // unreachable, as it if is a branch, this can make that branch optimizable // and more vaccuming possible auto* iff = curr->value->dynCast(); if (iff && iff->ifFalse && iff->type.isConcrete()) { // reuse the drop in both cases if (iff->ifTrue->type == Type::unreachable && iff->ifFalse->type.isConcrete()) { curr->value = iff->ifFalse; iff->ifFalse = curr; iff->type = Type::none; replaceCurrent(iff); } else if (iff->ifFalse->type == Type::unreachable && iff->ifTrue->type.isConcrete()) { curr->value = iff->ifTrue; iff->ifTrue = curr; iff->type = Type::none; replaceCurrent(iff); } } } void visitTry(Try* curr) { // If try's body does not throw, the whole try-catch can be replaced with // the try's body. if (!EffectAnalyzer(getPassOptions(), getModule()->features, curr->body) .throws) { replaceCurrent(curr->body); } } void visitFunction(Function* curr) { auto* optimized = optimize(curr->body, curr->sig.results != Type::none, true); if (optimized) { curr->body = optimized; } else { ExpressionManipulator::nop(curr->body); } if (curr->sig.results == Type::none && !EffectAnalyzer(getPassOptions(), getModule()->features, curr->body) .hasSideEffects()) { ExpressionManipulator::nop(curr->body); } } }; Pass* createVacuumPass() { return new Vacuum(); } } // namespace wasm binaryen-version_91/src/passes/intrinsics-module.h000066400000000000000000000014561362402614000225730ustar00rootroot00000000000000/* * Copyright 2018 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef passes_intrinsics_module_h #define passes_intrinsics_module_h namespace wasm { extern const char* IntrinsicsModuleWast; } // namespace wasm #endif // passes_intrinsics_module_h binaryen-version_91/src/passes/opt-utils.h000066400000000000000000000060551362402614000210630ustar00rootroot00000000000000/* * Copyright 2018 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_passes_opt_utils_h #define wasm_passes_opt_utils_h #include #include #include #include namespace wasm { namespace OptUtils { // Run useful optimizations after inlining new code into a set // of functions. inline void optimizeAfterInlining(std::unordered_set& funcs, Module* module, PassRunner* parentRunner) { // save the full list of functions on the side std::vector> all; all.swap(module->functions); module->updateMaps(); for (auto& func : funcs) { module->addFunction(func); } PassRunner runner(module, parentRunner->options); runner.setIsNested(true); runner.setValidateGlobally(false); // not a full valid module // this is especially useful after inlining runner.add("precompute-propagate"); runner.addDefaultFunctionOptimizationPasses(); // do all the usual stuff runner.run(); // restore all the funcs for (auto& func : module->functions) { func.release(); } all.swap(module->functions); module->updateMaps(); } struct FunctionRefReplacer : public WalkerPass> { bool isFunctionParallel() override { return true; } using MaybeReplace = std::function; FunctionRefReplacer(MaybeReplace maybeReplace) : maybeReplace(maybeReplace) {} FunctionRefReplacer* create() override { return new FunctionRefReplacer(maybeReplace); } void visitCall(Call* curr) { maybeReplace(curr->target); } void visitRefFunc(RefFunc* curr) { maybeReplace(curr->func); } private: MaybeReplace maybeReplace; }; inline void replaceFunctions(PassRunner* runner, Module& module, const std::map& replacements) { auto maybeReplace = [&](Name& name) { auto iter = replacements.find(name); if (iter != replacements.end()) { name = iter->second; } }; // replace direct calls FunctionRefReplacer(maybeReplace).run(runner, &module); // replace in table for (auto& segment : module.table.segments) { for (auto& name : segment.data) { maybeReplace(name); } } // replace in start if (module.start.is()) { maybeReplace(module.start); } // replace in exports for (auto& exp : module.exports) { maybeReplace(exp->value); } } } // namespace OptUtils } // namespace wasm #endif // wasm_passes_opt_utils_h binaryen-version_91/src/passes/pass.cpp000066400000000000000000000740641362402614000204310ustar00rootroot00000000000000/* * Copyright 2015 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #ifdef __linux__ #include #endif #include "ir/hashed.h" #include "ir/module-utils.h" #include "pass.h" #include "passes/passes.h" #include "support/colors.h" #include "wasm-debug.h" #include "wasm-io.h" #include "wasm-validator.h" namespace wasm { // PassRegistry PassRegistry::PassRegistry() { registerPasses(); } static PassRegistry singleton; PassRegistry* PassRegistry::get() { return &singleton; } void PassRegistry::registerPass(const char* name, const char* description, Creator create) { assert(passInfos.find(name) == passInfos.end()); passInfos[name] = PassInfo(description, create); } std::unique_ptr PassRegistry::createPass(std::string name) { if (passInfos.find(name) == passInfos.end()) { return nullptr; } std::unique_ptr ret; ret.reset(passInfos[name].create()); ret->name = name; return ret; } std::vector PassRegistry::getRegisteredNames() { std::vector ret; for (auto pair : passInfos) { ret.push_back(pair.first); } return ret; } std::string PassRegistry::getPassDescription(std::string name) { assert(passInfos.find(name) != passInfos.end()); return passInfos[name].description; } // PassRunner void PassRegistry::registerPasses() { registerPass("alignment-lowering", "lower unaligned loads and stores to smaller aligned ones", createAlignmentLoweringPass); registerPass("asyncify", "async/await style transform, allowing pausing and resuming", createAsyncifyPass); registerPass("avoid-reinterprets", "Tries to avoid reinterpret operations via more loads", createAvoidReinterpretsPass); registerPass( "dae", "removes arguments to calls in an lto-like manner", createDAEPass); registerPass("dae-optimizing", "removes arguments to calls in an lto-like manner, and " "optimizes where we removed", createDAEOptimizingPass); registerPass("coalesce-locals", "reduce # of locals by coalescing", createCoalesceLocalsPass); registerPass("coalesce-locals-learning", "reduce # of locals by coalescing and learning", createCoalesceLocalsWithLearningPass); registerPass("code-pushing", "push code forward, potentially making it not always execute", createCodePushingPass); registerPass( "code-folding", "fold code, merging duplicates", createCodeFoldingPass); registerPass("const-hoisting", "hoist repeated constants to a local", createConstHoistingPass); registerPass( "dce", "removes unreachable code", createDeadCodeEliminationPass); registerPass( "directize", "turns indirect calls into direct ones", createDirectizePass); registerPass( "dfo", "optimizes using the DataFlow SSA IR", createDataFlowOptsPass); registerPass("dwarfdump", "dump DWARF debug info sections from the read binary", createDWARFDumpPass); registerPass("duplicate-import-elimination", "removes duplicate imports", createDuplicateImportEliminationPass); registerPass("duplicate-function-elimination", "removes duplicate functions", createDuplicateFunctionEliminationPass); registerPass("emit-target-features", "emit the target features section in the output", createEmitTargetFeaturesPass); registerPass("extract-function", "leaves just one function (useful for debugging)", createExtractFunctionPass); registerPass( "flatten", "flattens out code, removing nesting", createFlattenPass); registerPass("fpcast-emu", "emulates function pointer casts, allowing incorrect indirect " "calls to (sometimes) work", createFuncCastEmulationPass); registerPass( "func-metrics", "reports function metrics", createFunctionMetricsPass); registerPass( "generate-stack-ir", "generate Stack IR", createGenerateStackIRPass); registerPass( "inline-main", "inline __original_main into main", createInlineMainPass); registerPass("inlining", "inline functions (you probably want inlining-optimizing)", createInliningPass); registerPass("inlining-optimizing", "inline functions and optimizes where we inlined", createInliningOptimizingPass); registerPass("legalize-js-interface", "legalizes i64 types on the import/export boundary", createLegalizeJSInterfacePass); registerPass("legalize-js-interface-minimally", "legalizes i64 types on the import/export boundary in a minimal " "manner, only on things only JS will call", createLegalizeJSInterfaceMinimallyPass); registerPass("local-cse", "common subexpression elimination inside basic blocks", createLocalCSEPass); registerPass("log-execution", "instrument the build with logging of where execution goes", createLogExecutionPass); registerPass("i64-to-i32-lowering", "lower all uses of i64s to use i32s instead", createI64ToI32LoweringPass); registerPass( "instrument-locals", "instrument the build with code to intercept all loads and stores", createInstrumentLocalsPass); registerPass( "instrument-memory", "instrument the build with code to intercept all loads and stores", createInstrumentMemoryPass); registerPass( "licm", "loop invariant code motion", createLoopInvariantCodeMotionPass); registerPass("limit-segments", "attempt to merge segments to fit within web limits", createLimitSegmentsPass); registerPass("memory-packing", "packs memory into separate segments, skipping zeros", createMemoryPackingPass); registerPass( "merge-blocks", "merges blocks to their parents", createMergeBlocksPass); registerPass( "merge-locals", "merges locals when beneficial", createMergeLocalsPass); registerPass("metrics", "reports metrics", createMetricsPass); registerPass("minify-imports", "minifies import names (only those, and not export names), and " "emits a mapping to the minified ones", createMinifyImportsPass); registerPass("minify-imports-and-exports", "minifies both import and export names, and emits a mapping to " "the minified ones", createMinifyImportsAndExportsPass); registerPass("minify-imports-and-exports-and-modules", "minifies both import and export names, and emits a mapping to " "the minified ones, and minifies the modules as well", createMinifyImportsAndExportsAndModulesPass); registerPass("mod-asyncify-always-and-only-unwind", "apply the assumption that asyncify imports always unwind, " "and we never rewind", createModAsyncifyAlwaysOnlyUnwindPass); registerPass("mod-asyncify-never-unwind", "apply the assumption that asyncify never unwinds", createModAsyncifyNeverUnwindPass); registerPass("nm", "name list", createNameListPass); registerPass("no-exit-runtime", "removes calls to atexit(), which is valid if the C runtime " "will never be exited", createNoExitRuntimePass); registerPass("optimize-added-constants", "optimizes added constants into load/store offsets", createOptimizeAddedConstantsPass); registerPass("optimize-added-constants-propagate", "optimizes added constants into load/store offsets, propagating " "them across locals too", createOptimizeAddedConstantsPropagatePass); registerPass("optimize-instructions", "optimizes instruction combinations", createOptimizeInstructionsPass); registerPass( "optimize-stack-ir", "optimize Stack IR", createOptimizeStackIRPass); registerPass("pick-load-signs", "pick load signs based on their uses", createPickLoadSignsPass); registerPass("post-assemblyscript", "eliminates redundant ARC patterns in AssemblyScript output", createPostAssemblyScriptPass); registerPass("post-assemblyscript-finalize", "eliminates collapsed ARC patterns after other optimizations", createPostAssemblyScriptFinalizePass); registerPass("post-emscripten", "miscellaneous optimizations for Emscripten-generated code", createPostEmscriptenPass); registerPass("precompute", "computes compile-time evaluatable expressions", createPrecomputePass); registerPass("precompute-propagate", "computes compile-time evaluatable expressions and propagates " "them through locals", createPrecomputePropagatePass); registerPass("print", "print in s-expression format", createPrinterPass); registerPass("print-minified", "print in minified s-expression format", createMinifiedPrinterPass); registerPass("print-features", "print options for enabled features", createPrintFeaturesPass); registerPass( "print-full", "print in full s-expression format", createFullPrinterPass); registerPass( "print-call-graph", "print call graph", createPrintCallGraphPass); registerPass("print-function-map", "print a map of function indexes to names", createPrintFunctionMapPass); registerPass("print-stack-ir", "print out Stack IR (useful for internal debugging)", createPrintStackIRPass); registerPass("relooper-jump-threading", "thread relooper jumps (fastcomp output only)", createRelooperJumpThreadingPass); registerPass("remove-non-js-ops", "removes operations incompatible with js", createRemoveNonJSOpsPass); registerPass("remove-imports", "removes imports and replaces them with nops", createRemoveImportsPass); registerPass( "remove-memory", "removes memory segments", createRemoveMemoryPass); registerPass("remove-unused-brs", "removes breaks from locations that are not needed", createRemoveUnusedBrsPass); registerPass("remove-unused-module-elements", "removes unused module elements", createRemoveUnusedModuleElementsPass); registerPass("remove-unused-nonfunction-module-elements", "removes unused module elements that are not functions", createRemoveUnusedNonFunctionModuleElementsPass); registerPass("remove-unused-names", "removes names from locations that are never branched to", createRemoveUnusedNamesPass); registerPass("reorder-functions", "sorts functions by access frequency", createReorderFunctionsPass); registerPass("reorder-locals", "sorts locals by access frequency", createReorderLocalsPass); registerPass("rereloop", "re-optimize control flow using the relooper algorithm", createReReloopPass); registerPass( "rse", "remove redundant local.sets", createRedundantSetEliminationPass); registerPass("roundtrip", "write the module to binary, then read it", createRoundTripPass); registerPass("safe-heap", "instrument loads and stores to check for invalid behavior", createSafeHeapPass); registerPass("simplify-globals", "miscellaneous globals-related optimizations", createSimplifyGlobalsPass); registerPass("simplify-globals-optimizing", "miscellaneous globals-related optimizations, and optimizes " "where we replaced global.gets with constants", createSimplifyGlobalsOptimizingPass); registerPass("simplify-locals", "miscellaneous locals-related optimizations", createSimplifyLocalsPass); registerPass("simplify-locals-nonesting", "miscellaneous locals-related optimizations (no nesting at all; " "preserves flatness)", createSimplifyLocalsNoNestingPass); registerPass("simplify-locals-notee", "miscellaneous locals-related optimizations (no tees)", createSimplifyLocalsNoTeePass); registerPass("simplify-locals-nostructure", "miscellaneous locals-related optimizations (no structure)", createSimplifyLocalsNoStructurePass); registerPass( "simplify-locals-notee-nostructure", "miscellaneous locals-related optimizations (no tees or structure)", createSimplifyLocalsNoTeeNoStructurePass); registerPass("souperify", "emit Souper IR in text form", createSouperifyPass); registerPass("souperify-single-use", "emit Souper IR in text form (single-use nodes only)", createSouperifySingleUsePass); registerPass("spill-pointers", "spill pointers to the C stack (useful for Boehm-style GC)", createSpillPointersPass); registerPass("ssa", "ssa-ify variables so that they have a single assignment", createSSAifyPass); registerPass( "ssa-nomerge", "ssa-ify variables so that they have a single assignment, ignoring merges", createSSAifyNoMergePass); registerPass( "strip", "deprecated; same as strip-debug", createStripDebugPass); registerPass("strip-debug", "strip debug info (including the names section)", createStripDebugPass); registerPass("strip-dwarf", "strip dwarf debug info", createStripDWARFPass); registerPass("strip-producers", "strip the wasm producers section", createStripProducersPass); registerPass("strip-target-features", "strip the wasm target features section", createStripTargetFeaturesPass); registerPass("trap-mode-clamp", "replace trapping operations with clamping semantics", createTrapModeClamp); registerPass("trap-mode-js", "replace trapping operations with js semantics", createTrapModeJS); registerPass("untee", "removes local.tees, replacing them with sets and gets", createUnteePass); registerPass("vacuum", "removes obviously unneeded code", createVacuumPass); // registerPass( // "lower-i64", "lowers i64 into pairs of i32s", createLowerInt64Pass); } void PassRunner::addDefaultOptimizationPasses() { addDefaultGlobalOptimizationPrePasses(); addDefaultFunctionOptimizationPasses(); addDefaultGlobalOptimizationPostPasses(); } // Check whether we should preserve valid DWARF while optimizing. If so, we // disable optimizations that currently cause issues with debug info. static bool shouldPreserveDWARF(PassOptions& options, Module& wasm) { return options.debugInfo && Debug::hasDWARFSections(wasm); } void PassRunner::addDefaultFunctionOptimizationPasses() { auto preserveDWARF = shouldPreserveDWARF(options, *wasm); // Untangling to semi-ssa form is helpful (but best to ignore merges // so as to not introduce new copies). // FIXME DWARF updating does not handle local changes yet. if (!preserveDWARF && (options.optimizeLevel >= 3 || options.shrinkLevel >= 1)) { add("ssa-nomerge"); } // if we are willing to work very very hard, flatten the IR and do opts // that depend on flat IR // FIXME DWARF updating does not handle local changes yet. if (!preserveDWARF && options.optimizeLevel >= 4) { add("flatten"); add("local-cse"); } add("dce"); add("remove-unused-brs"); add("remove-unused-names"); add("optimize-instructions"); if (options.optimizeLevel >= 2 || options.shrinkLevel >= 2) { add("pick-load-signs"); } // early propagation if (options.optimizeLevel >= 3 || options.shrinkLevel >= 2) { add("precompute-propagate"); } else { add("precompute"); } if (options.lowMemoryUnused) { if (options.optimizeLevel >= 3 || options.shrinkLevel >= 1) { add("optimize-added-constants-propagate"); } else { add("optimize-added-constants"); } } if (options.optimizeLevel >= 2 || options.shrinkLevel >= 2) { add("code-pushing"); } // don't create if/block return values yet, as coalesce can remove copies that // that could inhibit add("simplify-locals-nostructure"); add("vacuum"); // previous pass creates garbage add("reorder-locals"); // simplify-locals opens opportunities for optimizations add("remove-unused-brs"); // if we are willing to work hard, also optimize copies before coalescing // FIXME DWARF updating does not handle local changes yet. if (!preserveDWARF && (options.optimizeLevel >= 3 || options.shrinkLevel >= 2)) { add("merge-locals"); // very slow on e.g. sqlite } // FIXME DWARF updating does not handle local changes yet. if (!preserveDWARF) { add("coalesce-locals"); } add("simplify-locals"); add("vacuum"); add("reorder-locals"); // FIXME DWARF updating does not handle local changes yet. if (!preserveDWARF) { add("coalesce-locals"); add("reorder-locals"); } add("vacuum"); if (options.optimizeLevel >= 3 || options.shrinkLevel >= 1) { add("code-folding"); } add("merge-blocks"); // makes remove-unused-brs more effective add("remove-unused-brs"); // coalesce-locals opens opportunities add("remove-unused-names"); // remove-unused-brs opens opportunities add("merge-blocks"); // clean up remove-unused-brs new blocks // late propagation if (options.optimizeLevel >= 3 || options.shrinkLevel >= 2) { add("precompute-propagate"); } else { add("precompute"); } add("optimize-instructions"); if (options.optimizeLevel >= 2 || options.shrinkLevel >= 1) { add("rse"); // after all coalesce-locals, and before a final vacuum } add("vacuum"); // just to be safe } void PassRunner::addDefaultGlobalOptimizationPrePasses() { // FIXME DWARF updating does not handle merging debug info with merged code. if (!shouldPreserveDWARF(options, *wasm)) { add("duplicate-function-elimination"); } add("memory-packing"); } void PassRunner::addDefaultGlobalOptimizationPostPasses() { auto preserveDWARF = shouldPreserveDWARF(options, *wasm); // FIXME DWARF may be badly affected currently as DAE changes function // signatures and hence params and locals. if (!preserveDWARF && (options.optimizeLevel >= 2 || options.shrinkLevel >= 1)) { add("dae-optimizing"); } // FIXME DWARF updating does not handle inlining yet. if (!preserveDWARF && (options.optimizeLevel >= 2 || options.shrinkLevel >= 2)) { add("inlining-optimizing"); } // Optimizations show more functions as duplicate, so run this here in Post. // FIXME DWARF updating does not handle merging debug info with merged code. if (!preserveDWARF) { add("duplicate-function-elimination"); } add("duplicate-import-elimination"); if (options.optimizeLevel >= 2 || options.shrinkLevel >= 2) { add("simplify-globals-optimizing"); } else { add("simplify-globals"); } add("remove-unused-module-elements"); // may allow more inlining/dae/etc., need --converge for that add("directize"); // perform Stack IR optimizations here, at the very end of the // optimization pipeline if (options.optimizeLevel >= 2 || options.shrinkLevel >= 1) { add("generate-stack-ir"); add("optimize-stack-ir"); } } static void dumpWast(Name name, Module* wasm) { // write out the wat static int counter = 0; std::string numstr = std::to_string(counter++); while (numstr.size() < 3) { numstr = '0' + numstr; } auto fullName = std::string("byn-"); #ifdef __linux__ // TODO: use _getpid() on windows, elsewhere? fullName += std::to_string(getpid()) + '-'; #endif fullName += numstr + "-" + name.str; Colors::setEnabled(false); ModuleWriter writer; writer.writeText(*wasm, fullName + ".wast"); writer.writeBinary(*wasm, fullName + ".wasm"); } void PassRunner::run() { static const int passDebug = getPassDebug(); if (!isNested && (options.debug || passDebug)) { // for debug logging purposes, run each pass in full before running the // other auto totalTime = std::chrono::duration(0); size_t padding = 0; WasmValidator::Flags validationFlags = WasmValidator::Minimal; if (options.validateGlobally) { validationFlags = validationFlags | WasmValidator::Globally; } std::cerr << "[PassRunner] running passes..." << std::endl; for (auto& pass : passes) { padding = std::max(padding, pass->name.size()); } if (passDebug >= 3) { dumpWast("before", wasm); } for (auto& pass : passes) { // ignoring the time, save a printout of the module before, in case this // pass breaks it, so we can print the before and after std::stringstream moduleBefore; if (passDebug == 2) { WasmPrinter::printModule(wasm, moduleBefore); } // prepare to run std::cerr << "[PassRunner] running pass: " << pass->name << "... "; for (size_t i = 0; i < padding - pass->name.size(); i++) { std::cerr << ' '; } auto before = std::chrono::steady_clock::now(); if (pass->isFunctionParallel()) { // function-parallel passes should get a new instance per function ModuleUtils::iterDefinedFunctions( *wasm, [&](Function* func) { runPassOnFunction(pass.get(), func); }); } else { runPass(pass.get()); } auto after = std::chrono::steady_clock::now(); std::chrono::duration diff = after - before; std::cerr << diff.count() << " seconds." << std::endl; totalTime += diff; if (options.validate) { // validate, ignoring the time std::cerr << "[PassRunner] (validating)\n"; if (!WasmValidator().validate(*wasm, validationFlags)) { WasmPrinter::printModule(wasm); if (passDebug >= 2) { std::cerr << "Last pass (" << pass->name << ") broke validation. Here is the module before: \n" << moduleBefore.str() << "\n"; } else { std::cerr << "Last pass (" << pass->name << ") broke validation. Run with BINARYEN_PASS_DEBUG=2 " "in the env to see the earlier state, or 3 to dump " "byn-* files for each pass\n"; } abort(); } } if (passDebug >= 3) { dumpWast(pass->name, wasm); } } std::cerr << "[PassRunner] passes took " << totalTime.count() << " seconds." << std::endl; if (options.validate) { std::cerr << "[PassRunner] (final validation)\n"; if (!WasmValidator().validate(*wasm, validationFlags)) { WasmPrinter::printModule(wasm); std::cerr << "final module does not validate\n"; abort(); } } } else { // non-debug normal mode, run them in an optimal manner - for locality it is // better to run as many passes as possible on a single function before // moving to the next std::vector stack; auto flush = [&]() { if (stack.size() > 0) { // run the stack of passes on all the functions, in parallel size_t num = ThreadPool::get()->size(); std::vector> doWorkers; std::atomic nextFunction; nextFunction.store(0); size_t numFunctions = wasm->functions.size(); for (size_t i = 0; i < num; i++) { doWorkers.push_back([&]() { auto index = nextFunction.fetch_add(1); // get the next task, if there is one if (index >= numFunctions) { return ThreadWorkState::Finished; // nothing left } Function* func = this->wasm->functions[index].get(); if (!func->imported()) { // do the current task: run all passes on this function for (auto* pass : stack) { runPassOnFunction(pass, func); } } if (index + 1 == numFunctions) { return ThreadWorkState::Finished; // we did the last one } return ThreadWorkState::More; }); } ThreadPool::get()->work(doWorkers); } stack.clear(); }; for (auto& pass : passes) { if (pass->isFunctionParallel()) { stack.push_back(pass.get()); } else { flush(); runPass(pass.get()); } } flush(); } } void PassRunner::runOnFunction(Function* func) { if (options.debug) { std::cerr << "[PassRunner] running passes on function " << func->name << std::endl; } for (auto& pass : passes) { runPassOnFunction(pass.get(), func); } } void PassRunner::doAdd(std::unique_ptr pass) { pass->prepareToRun(this, wasm); passes.emplace_back(std::move(pass)); } // Checks that the state is valid before and after a // pass runs on a function. We run these extra checks when // pass-debug mode is enabled. struct AfterEffectFunctionChecker { Function* func; Name name; // Check Stack IR state: if the main IR changes, there should be no // stack IR, as the stack IR would be wrong. bool beganWithStackIR; HashType originalFunctionHash; // In the creator we can scan the state of the module and function before the // pass runs. AfterEffectFunctionChecker(Function* func) : func(func), name(func->name) { beganWithStackIR = func->stackIR != nullptr; if (beganWithStackIR) { originalFunctionHash = FunctionHasher::hashFunction(func); } } // This is called after the pass is run, at which time we can check things. void check() { assert(func->name == name); // no global module changes should have occurred if (beganWithStackIR && func->stackIR) { auto after = FunctionHasher::hashFunction(func); if (after != originalFunctionHash) { Fatal() << "[PassRunner] PASS_DEBUG check failed: had Stack IR before " "and after the pass ran, and the pass modified the main IR, " "which invalidates Stack IR - pass should have been marked " "'modifiesBinaryenIR'"; } } } }; // Runs checks on the entire module, in a non-function-parallel pass. // In particular, in such a pass functions may be removed or renamed, track // that. struct AfterEffectModuleChecker { Module* module; std::vector checkers; bool beganWithAnyStackIR; AfterEffectModuleChecker(Module* module) : module(module) { for (auto& func : module->functions) { checkers.emplace_back(func.get()); } beganWithAnyStackIR = hasAnyStackIR(); } void check() { if (beganWithAnyStackIR && hasAnyStackIR()) { // If anything changed to the functions, that's not good. if (checkers.size() != module->functions.size()) { error(); } for (Index i = 0; i < checkers.size(); i++) { // Did a pointer change? (a deallocated function could cause that) if (module->functions[i].get() != checkers[i].func || module->functions[i]->body != checkers[i].func->body) { error(); } // Did a name change? if (module->functions[i]->name != checkers[i].name) { error(); } } // Global function state appears to not have been changed: the same // functions are there. Look into their contents. for (auto& checker : checkers) { checker.check(); } } } void error() { Fatal() << "[PassRunner] PASS_DEBUG check failed: had Stack IR before and " "after the pass ran, and the pass modified global function " "state - pass should have been marked 'modifiesBinaryenIR'"; } bool hasAnyStackIR() { for (auto& func : module->functions) { if (func->stackIR) { return true; } } return false; } }; void PassRunner::runPass(Pass* pass) { std::unique_ptr checker; if (getPassDebug()) { checker = std::unique_ptr( new AfterEffectModuleChecker(wasm)); } pass->run(this, wasm); handleAfterEffects(pass); if (getPassDebug()) { checker->check(); } } void PassRunner::runPassOnFunction(Pass* pass, Function* func) { assert(pass->isFunctionParallel()); // function-parallel passes get a new instance per function auto instance = std::unique_ptr(pass->create()); std::unique_ptr checker; if (getPassDebug()) { checker = std::unique_ptr( new AfterEffectFunctionChecker(func)); } instance->runOnFunction(this, wasm, func); handleAfterEffects(pass, func); if (getPassDebug()) { checker->check(); } } void PassRunner::handleAfterEffects(Pass* pass, Function* func) { if (pass->modifiesBinaryenIR()) { // If Binaryen IR is modified, Stack IR must be cleared - it would // be out of sync in a potentially dangerous way. if (func) { func->stackIR.reset(nullptr); } else { for (auto& func : wasm->functions) { func->stackIR.reset(nullptr); } } } } int PassRunner::getPassDebug() { static const int passDebug = getenv("BINARYEN_PASS_DEBUG") ? atoi(getenv("BINARYEN_PASS_DEBUG")) : 0; return passDebug; } } // namespace wasm binaryen-version_91/src/passes/passes.h000066400000000000000000000100711362402614000204120ustar00rootroot00000000000000/* * Copyright 2015 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_passes_h #define wasm_passes_h namespace wasm { class Pass; // All passes: Pass* createAlignmentLoweringPass(); Pass* createAsyncifyPass(); Pass* createAvoidReinterpretsPass(); Pass* createCoalesceLocalsPass(); Pass* createCoalesceLocalsWithLearningPass(); Pass* createCodeFoldingPass(); Pass* createCodePushingPass(); Pass* createConstHoistingPass(); Pass* createDAEPass(); Pass* createDAEOptimizingPass(); Pass* createDataFlowOptsPass(); Pass* createDeadCodeEliminationPass(); Pass* createDirectizePass(); Pass* createDWARFDumpPass(); Pass* createDuplicateImportEliminationPass(); Pass* createDuplicateFunctionEliminationPass(); Pass* createEmitTargetFeaturesPass(); Pass* createExtractFunctionPass(); Pass* createFlattenPass(); Pass* createFuncCastEmulationPass(); Pass* createFullPrinterPass(); Pass* createFunctionMetricsPass(); Pass* createGenerateStackIRPass(); Pass* createI64ToI32LoweringPass(); Pass* createInlineMainPass(); Pass* createInliningPass(); Pass* createInliningOptimizingPass(); Pass* createLegalizeJSInterfacePass(); Pass* createLegalizeJSInterfaceMinimallyPass(); Pass* createLimitSegmentsPass(); Pass* createLocalCSEPass(); Pass* createLogExecutionPass(); Pass* createInstrumentLocalsPass(); Pass* createInstrumentMemoryPass(); Pass* createLoopInvariantCodeMotionPass(); Pass* createMemoryPackingPass(); Pass* createMergeBlocksPass(); Pass* createMergeLocalsPass(); Pass* createMinifiedPrinterPass(); Pass* createMinifyImportsPass(); Pass* createMinifyImportsAndExportsPass(); Pass* createMinifyImportsAndExportsAndModulesPass(); Pass* createMetricsPass(); Pass* createNameListPass(); Pass* createNoExitRuntimePass(); Pass* createOptimizeAddedConstantsPass(); Pass* createOptimizeAddedConstantsPropagatePass(); Pass* createOptimizeInstructionsPass(); Pass* createOptimizeStackIRPass(); Pass* createPickLoadSignsPass(); Pass* createModAsyncifyAlwaysOnlyUnwindPass(); Pass* createModAsyncifyNeverUnwindPass(); Pass* createPostAssemblyScriptPass(); Pass* createPostAssemblyScriptFinalizePass(); Pass* createPostEmscriptenPass(); Pass* createPrecomputePass(); Pass* createPrecomputePropagatePass(); Pass* createPrinterPass(); Pass* createPrintCallGraphPass(); Pass* createPrintFeaturesPass(); Pass* createPrintFunctionMapPass(); Pass* createPrintStackIRPass(); Pass* createRelooperJumpThreadingPass(); Pass* createRemoveNonJSOpsPass(); Pass* createRemoveImportsPass(); Pass* createRemoveMemoryPass(); Pass* createRemoveUnusedBrsPass(); Pass* createRemoveUnusedModuleElementsPass(); Pass* createRemoveUnusedNonFunctionModuleElementsPass(); Pass* createRemoveUnusedNamesPass(); Pass* createReorderFunctionsPass(); Pass* createReorderLocalsPass(); Pass* createReReloopPass(); Pass* createRedundantSetEliminationPass(); Pass* createRoundTripPass(); Pass* createSafeHeapPass(); Pass* createSimplifyLocalsPass(); Pass* createSimplifyGlobalsPass(); Pass* createSimplifyGlobalsOptimizingPass(); Pass* createSimplifyLocalsNoNestingPass(); Pass* createSimplifyLocalsNoTeePass(); Pass* createSimplifyLocalsNoStructurePass(); Pass* createSimplifyLocalsNoTeeNoStructurePass(); Pass* createStripDebugPass(); Pass* createStripDWARFPass(); Pass* createStripProducersPass(); Pass* createStripTargetFeaturesPass(); Pass* createSouperifyPass(); Pass* createSouperifySingleUsePass(); Pass* createSpillPointersPass(); Pass* createSSAifyPass(); Pass* createSSAifyNoMergePass(); Pass* createTrapModeClamp(); Pass* createTrapModeJS(); Pass* createUnteePass(); Pass* createVacuumPass(); } // namespace wasm #endif binaryen-version_91/src/passes/wasm-intrinsics.wat000066400000000000000000000575331362402614000226300ustar00rootroot00000000000000;; A large WAST blob which contains the implementations of all the intrinsics ;; that we inject as part of this module. This blob was generated from a Rust ;; program [1] which uses the Rust compiler-builtins project. It's not ;; necessarily perfect but gets the job done! The idea here is that we inject ;; these pretty early so they can continue to be optimized by further passes ;; (aka inlining and whatnot) ;; ;; LOCAL MODS done by hand afterwards: ;; * Remove hardcoded address 1024 (apparently a free memory location rustc ;; thinks is ok to use?); add intrinsic functions, which load/store to ;; special scratch space, wasm2js_scratch_load_i32 etc. ;; * Fix function type of __wasm_ctz_i64, which was wrong somehow, ;; i32, i32 => i32 instead of i64 => i64 ;; ;; [1]: https://gist.github.com/alexcrichton/e7ea67bcdd17ce4b6254e66f77165690 (module (type $0 (func (param i64 i64) (result i64))) (type $1 (func (param f32) (result f32))) (type $2 (func (param f64) (result f64))) (type $3 (func (param i32) (result i32))) (type $4 (func (param i32 i32) (result i32))) (type $5 (func (param i64) (result i64))) (import "env" "memory" (memory $0 17)) (import "env" "wasm2js_scratch_load_i64" (func $wasm2js_scratch_load_i64 (result i64))) (import "env" "wasm2js_scratch_store_i64" (func $wasm2js_scratch_store_i64 (param i64))) (export "__wasm_i64_sdiv" (func $__wasm_i64_sdiv)) (export "__wasm_i64_udiv" (func $__wasm_i64_udiv)) (export "__wasm_i64_srem" (func $__wasm_i64_srem)) (export "__wasm_i64_urem" (func $__wasm_i64_urem)) (export "__wasm_i64_mul" (func $__wasm_i64_mul)) (export "__wasm_trunc_f32" (func $__wasm_trunc_f32)) (export "__wasm_trunc_f64" (func $__wasm_trunc_f64)) (export "__wasm_ctz_i32" (func $__wasm_ctz_i32)) (export "__wasm_ctz_i64" (func $__wasm_ctz_i64)) (export "__wasm_rotl_i32" (func $__wasm_rotl_i32)) (export "__wasm_rotr_i32" (func $__wasm_rotr_i32)) (export "__wasm_rotl_i64" (func $__wasm_rotl_i64)) (export "__wasm_rotr_i64" (func $__wasm_rotr_i64)) (export "__wasm_nearest_f32" (func $__wasm_nearest_f32)) (export "__wasm_nearest_f64" (func $__wasm_nearest_f64)) (export "__wasm_popcnt_i32" (func $__wasm_popcnt_i32)) (export "__wasm_popcnt_i64" (func $__wasm_popcnt_i64)) ;; lowering of the i32.popcnt instruction, counts the number of bits set in the ;; input and returns the result (func $__wasm_popcnt_i32 (param $var$0 i32) (result i32) (local $var$1 i32) (block $label$1 (result i32) (loop $label$2 (drop (br_if $label$1 (local.get $var$1) (i32.eqz (local.get $var$0) ) ) ) (local.set $var$0 (i32.and (local.get $var$0) (i32.sub (local.get $var$0) (i32.const 1) ) ) ) (local.set $var$1 (i32.add (local.get $var$1) (i32.const 1) ) ) (br $label$2) ) ) ) ;; lowering of the i64.popcnt instruction, counts the number of bits set in the ;; input and returns the result (func $__wasm_popcnt_i64 (param $var$0 i64) (result i64) (local $var$1 i64) (block $label$1 (result i64) (loop $label$2 (drop (br_if $label$1 (local.get $var$1) (i64.eqz (local.get $var$0) ) ) ) (local.set $var$0 (i64.and (local.get $var$0) (i64.sub (local.get $var$0) (i64.const 1) ) ) ) (local.set $var$1 (i64.add (local.get $var$1) (i64.const 1) ) ) (br $label$2) ) ) ) ;; lowering of the i64.div_s instruction, return $var0 / $var$1 (func $__wasm_i64_sdiv (; 0 ;) (type $0) (param $var$0 i64) (param $var$1 i64) (result i64) (call $_ZN17compiler_builtins3int4sdiv3Div3div17he78fc483e41d7ec7E (local.get $var$0) (local.get $var$1) ) ) ;; lowering of the i64.div_u instruction, return $var0 / $var$1 (func $__wasm_i64_udiv (; 1 ;) (type $0) (param $var$0 i64) (param $var$1 i64) (result i64) (call $_ZN17compiler_builtins3int4udiv10divmod_u6417h6026910b5ed08e40E (local.get $var$0) (local.get $var$1) ) ) ;; lowering of the i64.rem_s instruction, return $var0 % $var$1 (func $__wasm_i64_srem (; 2 ;) (type $0) (param $var$0 i64) (param $var$1 i64) (result i64) (call $_ZN17compiler_builtins3int4sdiv3Mod4mod_17h2cbb7bbf36e41d68E (local.get $var$0) (local.get $var$1) ) ) ;; lowering of the i64.rem_u instruction, return $var0 % $var$1 (func $__wasm_i64_urem (; 3 ;) (type $0) (param $var$0 i64) (param $var$1 i64) (result i64) (drop (call $_ZN17compiler_builtins3int4udiv10divmod_u6417h6026910b5ed08e40E (local.get $var$0) (local.get $var$1) ) ) (call $wasm2js_scratch_load_i64) ) ;; lowering of the i64.mul instruction, return $var0 * $var$1 (func $__wasm_i64_mul (; 4 ;) (type $0) (param $var$0 i64) (param $var$1 i64) (result i64) (call $_ZN17compiler_builtins3int3mul3Mul3mul17h070e9a1c69faec5bE (local.get $var$0) (local.get $var$1) ) ) ;; lowering of the f32.trunc instruction, rounds to the nearest integer, ;; towards zero (func $__wasm_trunc_f32 (; 5 ;) (type $1) (param $var$0 f32) (result f32) (select (f32.ceil (local.get $var$0) ) (f32.floor (local.get $var$0) ) (f32.lt (local.get $var$0) (f32.const 0) ) ) ) ;; lowering of the f64.trunc instruction, rounds to the nearest integer, ;; towards zero (func $__wasm_trunc_f64 (; 6 ;) (type $2) (param $var$0 f64) (result f64) (select (f64.ceil (local.get $var$0) ) (f64.floor (local.get $var$0) ) (f64.lt (local.get $var$0) (f64.const 0) ) ) ) ;; lowering of the i32.ctz instruction, counting the number of zeros in $var$0 (func $__wasm_ctz_i32 (; 7 ;) (type $3) (param $var$0 i32) (result i32) (if (local.get $var$0) (return (i32.sub (i32.const 31) (i32.clz (i32.xor (i32.add (local.get $var$0) (i32.const -1) ) (local.get $var$0) ) ) ) ) ) (i32.const 32) ) ;; lowering of the i64.ctz instruction, counting the number of zeros in $var$0 (func $__wasm_ctz_i64 (; 8 ;) (type $5) (param $var$0 i64) (result i64) (if (i32.eqz (i64.eqz (local.get $var$0) ) ) (return (i64.sub (i64.const 63) (i64.clz (i64.xor (i64.add (local.get $var$0) (i64.const -1) ) (local.get $var$0) ) ) ) ) ) (i64.const 64) ) ;; lowering of the i32.rotl instruction, rotating the first argument, with ;; wraparound, by the second argument (func $__wasm_rotl_i32 (; 8 ;) (type $4) (param $var$0 i32) (param $var$1 i32) (result i32) (local $var$2 i32) (i32.or (i32.shl (i32.and (i32.shr_u (i32.const -1) (local.tee $var$2 (i32.and (local.get $var$1) (i32.const 31) ) ) ) (local.get $var$0) ) (local.get $var$2) ) (i32.shr_u (i32.and (i32.shl (i32.const -1) (local.tee $var$1 (i32.and (i32.sub (i32.const 0) (local.get $var$1) ) (i32.const 31) ) ) ) (local.get $var$0) ) (local.get $var$1) ) ) ) ;; lowering of the i32.rotr instruction, rotating the first argument, with ;; wraparound, by the second argument (func $__wasm_rotr_i32 (; 9 ;) (type $4) (param $var$0 i32) (param $var$1 i32) (result i32) (local $var$2 i32) (i32.or (i32.shr_u (i32.and (i32.shl (i32.const -1) (local.tee $var$2 (i32.and (local.get $var$1) (i32.const 31) ) ) ) (local.get $var$0) ) (local.get $var$2) ) (i32.shl (i32.and (i32.shr_u (i32.const -1) (local.tee $var$1 (i32.and (i32.sub (i32.const 0) (local.get $var$1) ) (i32.const 31) ) ) ) (local.get $var$0) ) (local.get $var$1) ) ) ) ;; lowering of the i64.rotl instruction, rotating the first argument, with ;; wraparound, by the second argument (func $__wasm_rotl_i64 (; 10 ;) (type $0) (param $var$0 i64) (param $var$1 i64) (result i64) (local $var$2 i64) (i64.or (i64.shl (i64.and (i64.shr_u (i64.const -1) (local.tee $var$2 (i64.and (local.get $var$1) (i64.const 63) ) ) ) (local.get $var$0) ) (local.get $var$2) ) (i64.shr_u (i64.and (i64.shl (i64.const -1) (local.tee $var$1 (i64.and (i64.sub (i64.const 0) (local.get $var$1) ) (i64.const 63) ) ) ) (local.get $var$0) ) (local.get $var$1) ) ) ) ;; lowering of the i64.rotr instruction, rotating the first argument, with ;; wraparound, by the second argument (func $__wasm_rotr_i64 (; 11 ;) (type $0) (param $var$0 i64) (param $var$1 i64) (result i64) (local $var$2 i64) (i64.or (i64.shr_u (i64.and (i64.shl (i64.const -1) (local.tee $var$2 (i64.and (local.get $var$1) (i64.const 63) ) ) ) (local.get $var$0) ) (local.get $var$2) ) (i64.shl (i64.and (i64.shr_u (i64.const -1) (local.tee $var$1 (i64.and (i64.sub (i64.const 0) (local.get $var$1) ) (i64.const 63) ) ) ) (local.get $var$0) ) (local.get $var$1) ) ) ) ;; lowering of the f32.nearest instruction, rounding the input to the nearest ;; integer while breaking ties by rounding to even (func $__wasm_nearest_f32 (; 12 ;) (type $1) (param $var$0 f32) (result f32) (local $var$1 f32) (local $var$2 f32) (if (i32.eqz (f32.lt (local.tee $var$2 (f32.sub (local.get $var$0) (local.tee $var$1 (f32.floor (local.get $var$0) ) ) ) ) (f32.const 0.5) ) ) (block (local.set $var$0 (f32.ceil (local.get $var$0) ) ) (if (f32.gt (local.get $var$2) (f32.const 0.5) ) (return (local.get $var$0) ) ) (local.set $var$1 (select (local.get $var$1) (local.get $var$0) (f32.eq (f32.sub (local.tee $var$2 (f32.mul (local.get $var$1) (f32.const 0.5) ) ) (f32.floor (local.get $var$2) ) ) (f32.const 0) ) ) ) ) ) (local.get $var$1) ) ;; lowering of the f64.nearest instruction, rounding the input to the nearest ;; integer while breaking ties by rounding to even (func $__wasm_nearest_f64 (; 13 ;) (type $2) (param $var$0 f64) (result f64) (local $var$1 f64) (local $var$2 f64) (if (i32.eqz (f64.lt (local.tee $var$2 (f64.sub (local.get $var$0) (local.tee $var$1 (f64.floor (local.get $var$0) ) ) ) ) (f64.const 0.5) ) ) (block (local.set $var$0 (f64.ceil (local.get $var$0) ) ) (if (f64.gt (local.get $var$2) (f64.const 0.5) ) (return (local.get $var$0) ) ) (local.set $var$1 (select (local.get $var$1) (local.get $var$0) (f64.eq (f64.sub (local.tee $var$2 (f64.mul (local.get $var$1) (f64.const 0.5) ) ) (f64.floor (local.get $var$2) ) ) (f64.const 0) ) ) ) ) ) (local.get $var$1) ) (func $_ZN17compiler_builtins3int4udiv10divmod_u6417h6026910b5ed08e40E (; 14 ;) (type $0) (param $var$0 i64) (param $var$1 i64) (result i64) (local $var$2 i32) (local $var$3 i32) (local $var$4 i32) (local $var$5 i64) (local $var$6 i64) (local $var$7 i64) (local $var$8 i64) (block $label$1 (block $label$2 (block $label$3 (block $label$4 (block $label$5 (block $label$6 (block $label$7 (block $label$8 (block $label$9 (block $label$10 (block $label$11 (if (local.tee $var$2 (i32.wrap_i64 (i64.shr_u (local.get $var$0) (i64.const 32) ) ) ) (block (br_if $label$11 (i32.eqz (local.tee $var$3 (i32.wrap_i64 (local.get $var$1) ) ) ) ) (br_if $label$9 (i32.eqz (local.tee $var$4 (i32.wrap_i64 (i64.shr_u (local.get $var$1) (i64.const 32) ) ) ) ) ) (br_if $label$8 (i32.le_u (local.tee $var$2 (i32.sub (i32.clz (local.get $var$4) ) (i32.clz (local.get $var$2) ) ) ) (i32.const 31) ) ) (br $label$2) ) ) (br_if $label$2 (i64.ge_u (local.get $var$1) (i64.const 4294967296) ) ) (call $wasm2js_scratch_store_i64 (i64.extend_i32_u (i32.sub (local.tee $var$2 (i32.wrap_i64 (local.get $var$0) ) ) (i32.mul (local.tee $var$2 (i32.div_u (local.get $var$2) (local.tee $var$3 (i32.wrap_i64 (local.get $var$1) ) ) ) ) (local.get $var$3) ) ) ) ) (return (i64.extend_i32_u (local.get $var$2) ) ) ) (local.set $var$3 (i32.wrap_i64 (i64.shr_u (local.get $var$1) (i64.const 32) ) ) ) (br_if $label$7 (i32.eqz (i32.wrap_i64 (local.get $var$0) ) ) ) (br_if $label$6 (i32.eqz (local.get $var$3) ) ) (br_if $label$6 (i32.and (local.tee $var$4 (i32.add (local.get $var$3) (i32.const -1) ) ) (local.get $var$3) ) ) (call $wasm2js_scratch_store_i64 (i64.or (i64.shl (i64.extend_i32_u (i32.and (local.get $var$4) (local.get $var$2) ) ) (i64.const 32) ) (i64.and (local.get $var$0) (i64.const 4294967295) ) ) ) (return (i64.extend_i32_u (i32.shr_u (local.get $var$2) (i32.and (i32.ctz (local.get $var$3) ) (i32.const 31) ) ) ) ) ) (unreachable) ) (br_if $label$5 (i32.eqz (i32.and (local.tee $var$4 (i32.add (local.get $var$3) (i32.const -1) ) ) (local.get $var$3) ) ) ) (local.set $var$3 (i32.sub (i32.const 0) (local.tee $var$2 (i32.sub (i32.add (i32.clz (local.get $var$3) ) (i32.const 33) ) (i32.clz (local.get $var$2) ) ) ) ) ) (br $label$3) ) (local.set $var$3 (i32.sub (i32.const 63) (local.get $var$2) ) ) (local.set $var$2 (i32.add (local.get $var$2) (i32.const 1) ) ) (br $label$3) ) (call $wasm2js_scratch_store_i64 (i64.shl (i64.extend_i32_u (i32.sub (local.get $var$2) (i32.mul (local.tee $var$4 (i32.div_u (local.get $var$2) (local.get $var$3) ) ) (local.get $var$3) ) ) ) (i64.const 32) ) ) (return (i64.extend_i32_u (local.get $var$4) ) ) ) (br_if $label$4 (i32.lt_u (local.tee $var$2 (i32.sub (i32.clz (local.get $var$3) ) (i32.clz (local.get $var$2) ) ) ) (i32.const 31) ) ) (br $label$2) ) (call $wasm2js_scratch_store_i64 (i64.extend_i32_u (i32.and (local.get $var$4) (i32.wrap_i64 (local.get $var$0) ) ) ) ) (br_if $label$1 (i32.eq (local.get $var$3) (i32.const 1) ) ) (return (i64.shr_u (local.get $var$0) (i64.extend_i32_u (i32.ctz (local.get $var$3) ) ) ) ) ) (local.set $var$3 (i32.sub (i32.const 63) (local.get $var$2) ) ) (local.set $var$2 (i32.add (local.get $var$2) (i32.const 1) ) ) ) (local.set $var$5 (i64.shr_u (local.get $var$0) (i64.extend_i32_u (i32.and (local.get $var$2) (i32.const 63) ) ) ) ) (local.set $var$0 (i64.shl (local.get $var$0) (i64.extend_i32_u (i32.and (local.get $var$3) (i32.const 63) ) ) ) ) (block $label$13 (if (local.get $var$2) (block (local.set $var$8 (i64.add (local.get $var$1) (i64.const -1) ) ) (loop $label$15 (local.set $var$5 (i64.sub (local.tee $var$5 (i64.or (i64.shl (local.get $var$5) (i64.const 1) ) (i64.shr_u (local.get $var$0) (i64.const 63) ) ) ) (i64.and (local.tee $var$6 (i64.shr_s (i64.sub (local.get $var$8) (local.get $var$5) ) (i64.const 63) ) ) (local.get $var$1) ) ) ) (local.set $var$0 (i64.or (i64.shl (local.get $var$0) (i64.const 1) ) (local.get $var$7) ) ) (local.set $var$7 (local.tee $var$6 (i64.and (local.get $var$6) (i64.const 1) ) ) ) (br_if $label$15 (local.tee $var$2 (i32.add (local.get $var$2) (i32.const -1) ) ) ) ) (br $label$13) ) ) ) (call $wasm2js_scratch_store_i64 (local.get $var$5) ) (return (i64.or (i64.shl (local.get $var$0) (i64.const 1) ) (local.get $var$6) ) ) ) (call $wasm2js_scratch_store_i64 (local.get $var$0) ) (local.set $var$0 (i64.const 0) ) ) (local.get $var$0) ) (func $_ZN17compiler_builtins3int3mul3Mul3mul17h070e9a1c69faec5bE (; 15 ;) (type $0) (param $var$0 i64) (param $var$1 i64) (result i64) (local $var$2 i32) (local $var$3 i32) (local $var$4 i32) (local $var$5 i32) (local $var$6 i32) (i64.or (i64.shl (i64.extend_i32_u (i32.add (i32.add (i32.add (i32.add (i32.mul (local.tee $var$4 (i32.shr_u (local.tee $var$2 (i32.wrap_i64 (local.get $var$1) ) ) (i32.const 16) ) ) (local.tee $var$5 (i32.shr_u (local.tee $var$3 (i32.wrap_i64 (local.get $var$0) ) ) (i32.const 16) ) ) ) (i32.mul (local.get $var$2) (i32.wrap_i64 (i64.shr_u (local.get $var$0) (i64.const 32) ) ) ) ) (i32.mul (i32.wrap_i64 (i64.shr_u (local.get $var$1) (i64.const 32) ) ) (local.get $var$3) ) ) (i32.shr_u (local.tee $var$2 (i32.add (i32.shr_u (local.tee $var$6 (i32.mul (local.tee $var$2 (i32.and (local.get $var$2) (i32.const 65535) ) ) (local.tee $var$3 (i32.and (local.get $var$3) (i32.const 65535) ) ) ) ) (i32.const 16) ) (i32.mul (local.get $var$2) (local.get $var$5) ) ) ) (i32.const 16) ) ) (i32.shr_u (local.tee $var$2 (i32.add (i32.and (local.get $var$2) (i32.const 65535) ) (i32.mul (local.get $var$4) (local.get $var$3) ) ) ) (i32.const 16) ) ) ) (i64.const 32) ) (i64.extend_i32_u (i32.or (i32.shl (local.get $var$2) (i32.const 16) ) (i32.and (local.get $var$6) (i32.const 65535) ) ) ) ) ) (func $_ZN17compiler_builtins3int4sdiv3Div3div17he78fc483e41d7ec7E (; 16 ;) (type $0) (param $var$0 i64) (param $var$1 i64) (result i64) (local $var$2 i64) (i64.sub (i64.xor (i64.div_u (i64.sub (i64.xor (local.tee $var$2 (i64.shr_s (local.get $var$0) (i64.const 63) ) ) (local.get $var$0) ) (local.get $var$2) ) (i64.sub (i64.xor (local.tee $var$2 (i64.shr_s (local.get $var$1) (i64.const 63) ) ) (local.get $var$1) ) (local.get $var$2) ) ) (local.tee $var$0 (i64.shr_s (i64.xor (local.get $var$1) (local.get $var$0) ) (i64.const 63) ) ) ) (local.get $var$0) ) ) (func $_ZN17compiler_builtins3int4sdiv3Mod4mod_17h2cbb7bbf36e41d68E (; 17 ;) (type $0) (param $var$0 i64) (param $var$1 i64) (result i64) (local $var$2 i64) (i64.sub (i64.xor (i64.rem_u (i64.sub (i64.xor (local.tee $var$2 (i64.shr_s (local.get $var$0) (i64.const 63) ) ) (local.get $var$0) ) (local.get $var$2) ) (i64.sub (i64.xor (local.tee $var$0 (i64.shr_s (local.get $var$1) (i64.const 63) ) ) (local.get $var$1) ) (local.get $var$0) ) ) (local.get $var$2) ) (local.get $var$2) ) ) ;; custom section "linking", size 3 ) binaryen-version_91/src/pretty_printing.h000066400000000000000000000036671362402614000210740ustar00rootroot00000000000000/* * Copyright 2015 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // // Pretty printing helpers // #ifndef wasm_pretty_printing_h #define wasm_pretty_printing_h #include #include "support/colors.h" inline std::ostream& doIndent(std::ostream& o, unsigned indent) { for (unsigned i = 0; i < indent; i++) { o << " "; } return o; } inline std::ostream& prepareMajorColor(std::ostream& o) { Colors::red(o); Colors::bold(o); return o; } inline std::ostream& prepareColor(std::ostream& o) { Colors::magenta(o); Colors::bold(o); return o; } inline std::ostream& prepareMinorColor(std::ostream& o) { Colors::orange(o); return o; } inline std::ostream& restoreNormalColor(std::ostream& o) { Colors::normal(o); return o; } inline std::ostream& printText(std::ostream& o, const char* str) { o << '"'; Colors::green(o); o << str; Colors::normal(o); return o << '"'; } inline std::ostream& printMajor(std::ostream& o, const char* str, bool major = false) { prepareMajorColor(o); o << str; restoreNormalColor(o); return o; } inline std::ostream& printMedium(std::ostream& o, const char* str, bool major = false) { prepareColor(o); o << str; restoreNormalColor(o); return o; } inline std::ostream& printMinor(std::ostream& o, const char* str) { prepareMinorColor(o); o << str; restoreNormalColor(o); return o; } #endif // wasm_pretty_printing_h binaryen-version_91/src/shared-constants.h000066400000000000000000000032541362402614000211030ustar00rootroot00000000000000/* * Copyright 2016 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_shared_constants_h #define wasm_shared_constants_h #include "wasm.h" namespace wasm { extern Name GROW_WASM_MEMORY; extern Name WASM_CALL_CTORS; extern Name MEMORY_BASE; extern Name TABLE_BASE; extern Name STACK_POINTER; extern Name GET_TEMP_RET0; extern Name SET_TEMP_RET0; extern Name NEW_SIZE; extern Name MODULE; extern Name START; extern Name FUNC; extern Name PARAM; extern Name RESULT; extern Name MEMORY; extern Name DATA; extern Name PASSIVE; extern Name EXPORT; extern Name IMPORT; extern Name TABLE; extern Name ELEM; extern Name LOCAL; extern Name TYPE; extern Name CALL; extern Name CALL_IMPORT; extern Name CALL_INDIRECT; extern Name BLOCK; extern Name BR_IF; extern Name THEN; extern Name ELSE; extern Name _NAN; extern Name _INFINITY; extern Name NEG_INFINITY; extern Name NEG_NAN; extern Name CASE; extern Name BR; extern Name FUNCREF; extern Name FAKE_RETURN; extern Name MUT; extern Name SPECTEST; extern Name PRINT; extern Name EXIT; extern Name SHARED; extern Name EVENT; extern Name ATTR; } // namespace wasm #endif // wasm_shared_constants_h binaryen-version_91/src/shell-interface.h000066400000000000000000000175201362402614000206710ustar00rootroot00000000000000/* * Copyright 2016 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // // Implementation of the shell interpreter execution environment // #ifndef wasm_shell_interface_h #define wasm_shell_interface_h #include "asmjs/shared-constants.h" #include "ir/module-utils.h" #include "shared-constants.h" #include "support/name.h" #include "wasm-interpreter.h" #include "wasm.h" namespace wasm { struct ExitException {}; struct TrapException {}; struct ShellExternalInterface : ModuleInstance::ExternalInterface { // The underlying memory can be accessed through unaligned pointers which // isn't well-behaved in C++. WebAssembly nonetheless expects it to behave // properly. Avoid emitting unaligned load/store by checking for alignment // explicitly, and performing memcpy if unaligned. // // The allocated memory tries to have the same alignment as the memory being // simulated. class Memory { // Use char because it doesn't run afoul of aliasing rules. std::vector memory; template static bool aligned(const char* address) { static_assert(!(sizeof(T) & (sizeof(T) - 1)), "must be a power of 2"); return 0 == (reinterpret_cast(address) & (sizeof(T) - 1)); } Memory(Memory&) = delete; Memory& operator=(const Memory&) = delete; public: Memory() = default; void resize(size_t newSize) { // Ensure the smallest allocation is large enough that most allocators // will provide page-aligned storage. This hopefully allows the // interpreter's memory to be as aligned as the memory being simulated, // ensuring that the performance doesn't needlessly degrade. // // The code is optimistic this will work until WG21's p0035r0 happens. const size_t minSize = 1 << 12; size_t oldSize = memory.size(); memory.resize(std::max(minSize, newSize)); if (newSize < oldSize && newSize < minSize) { std::memset(&memory[newSize], 0, minSize - newSize); } } template void set(size_t address, T value) { if (aligned(&memory[address])) { *reinterpret_cast(&memory[address]) = value; } else { std::memcpy(&memory[address], &value, sizeof(T)); } } template T get(size_t address) { if (aligned(&memory[address])) { return *reinterpret_cast(&memory[address]); } else { T loaded; std::memcpy(&loaded, &memory[address], sizeof(T)); return loaded; } } } memory; std::vector table; ShellExternalInterface() : memory() {} virtual ~ShellExternalInterface() = default; void init(Module& wasm, ModuleInstance& instance) override { memory.resize(wasm.memory.initial * wasm::Memory::kPageSize); table.resize(wasm.table.initial); } void importGlobals(std::map& globals, Module& wasm) override { // add spectest globals ModuleUtils::iterImportedGlobals(wasm, [&](Global* import) { if (import->module == SPECTEST && import->base.startsWith(GLOBAL)) { switch (import->type.getSingle()) { case Type::i32: globals[import->name] = Literal(int32_t(666)); break; case Type::i64: globals[import->name] = Literal(int64_t(666)); break; case Type::f32: globals[import->name] = Literal(float(666.6)); break; case Type::f64: globals[import->name] = Literal(double(666.6)); break; case Type::v128: assert(false && "v128 not implemented yet"); case Type::funcref: case Type::anyref: case Type::nullref: case Type::exnref: globals[import->name] = Literal::makeNullref(); break; case Type::none: case Type::unreachable: WASM_UNREACHABLE("unexpected type"); } } }); if (wasm.memory.imported() && wasm.memory.module == SPECTEST && wasm.memory.base == MEMORY) { // imported memory has initial 1 and max 2 wasm.memory.initial = 1; wasm.memory.max = 2; } } Literal callImport(Function* import, LiteralList& arguments) override { if (import->module == SPECTEST && import->base.startsWith(PRINT)) { for (auto argument : arguments) { std::cout << argument << " : " << argument.type << '\n'; } return Literal(); } else if (import->module == ENV && import->base == EXIT) { // XXX hack for torture tests std::cout << "exit()\n"; throw ExitException(); } Fatal() << "callImport: unknown import: " << import->module.str << "." << import->name.str; } Literal callTable(Index index, Signature sig, LiteralList& arguments, Type results, ModuleInstance& instance) override { if (index >= table.size()) { trap("callTable overflow"); } auto* func = instance.wasm.getFunctionOrNull(table[index]); if (!func) { trap("uninitialized table element"); } if (sig != func->sig) { trap("callIndirect: function signatures don't match"); } const std::vector& params = func->sig.params.expand(); if (params.size() != arguments.size()) { trap("callIndirect: bad # of arguments"); } for (size_t i = 0; i < params.size(); i++) { if (!Type::isSubType(arguments[i].type, params[i])) { trap("callIndirect: bad argument type"); } } if (func->sig.results != results) { trap("callIndirect: bad result type"); } if (func->imported()) { return callImport(func, arguments); } else { return instance.callFunctionInternal(func->name, arguments); } } int8_t load8s(Address addr) override { return memory.get(addr); } uint8_t load8u(Address addr) override { return memory.get(addr); } int16_t load16s(Address addr) override { return memory.get(addr); } uint16_t load16u(Address addr) override { return memory.get(addr); } int32_t load32s(Address addr) override { return memory.get(addr); } uint32_t load32u(Address addr) override { return memory.get(addr); } int64_t load64s(Address addr) override { return memory.get(addr); } uint64_t load64u(Address addr) override { return memory.get(addr); } std::array load128(Address addr) override { return memory.get>(addr); } void store8(Address addr, int8_t value) override { memory.set(addr, value); } void store16(Address addr, int16_t value) override { memory.set(addr, value); } void store32(Address addr, int32_t value) override { memory.set(addr, value); } void store64(Address addr, int64_t value) override { memory.set(addr, value); } void store128(Address addr, const std::array& value) override { memory.set>(addr, value); } void tableStore(Address addr, Name entry) override { table[addr] = entry; } void growMemory(Address /*oldSize*/, Address newSize) override { memory.resize(newSize); } void trap(const char* why) override { std::cerr << "[trap " << why << "]\n"; throw TrapException(); } }; } // namespace wasm #endif // wasm_shell_interface_h binaryen-version_91/src/support/000077500000000000000000000000001362402614000171625ustar00rootroot00000000000000binaryen-version_91/src/support/CMakeLists.txt000066400000000000000000000003211362402614000217160ustar00rootroot00000000000000set(support_SOURCES archive.cpp bits.cpp colors.cpp command-line.cpp debug.cpp file.cpp path.cpp safe_integer.cpp threads.cpp utilities.cpp ) add_library(support OBJECT ${support_SOURCES}) binaryen-version_91/src/support/alloc.h000066400000000000000000000030051362402614000204230ustar00rootroot00000000000000/* * Copyright 2019 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // // Allocation helpers // #ifndef wasm_support_alloc_h #define wasm_support_alloc_h #include #if defined(WIN32) || defined(_WIN32) #include #endif namespace wasm { // An allocation of a specific size and a minimum alignment. Must be freed // with aligned_free. Returns nullptr on failure. inline void* aligned_malloc(size_t align, size_t size) { #if defined(WIN32) || defined(_WIN32) _set_errno(0); void* ret = _aligned_malloc(size, align); if (errno == ENOMEM) ret = nullptr; return ret; #elif defined(__APPLE__) || !defined(_ISOC11_SOURCE) void* ptr; int result = posix_memalign(&ptr, align, size); return result == 0 ? ptr : nullptr; #else return aligned_alloc(align, size); #endif } inline void aligned_free(void* ptr) { #if defined(WIN32) || defined(_WIN32) _aligned_free(ptr); #else free(ptr); #endif } } // namespace wasm #endif // wasm_support_alloc_h binaryen-version_91/src/support/archive.cpp000066400000000000000000000163271362402614000213200ustar00rootroot00000000000000/* * Copyright 2016 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "support/archive.h" #include "support/utilities.h" #include static const char* const magic = "!\n"; class ArchiveMemberHeader { public: uint8_t fileName[16]; uint8_t timestamp[12]; uint8_t UID[6]; uint8_t GID[6]; uint8_t accessMode[8]; uint8_t size[10]; // Size of data only, not including padding or header uint8_t magic[2]; std::string getName() const; // Members are not larger than 4GB uint32_t getSize() const; }; std::string ArchiveMemberHeader::getName() const { char endChar; if (fileName[0] == '/') { // Special name (string table or reference, or symbol table) endChar = ' '; } else { endChar = '/'; // regular name } auto* end = static_cast(memchr(fileName, endChar, sizeof(fileName))); if (!end) { end = fileName + sizeof(fileName); } return std::string((char*)(fileName), end - fileName); } uint32_t ArchiveMemberHeader::getSize() const { auto* end = static_cast(memchr(size, ' ', sizeof(size))); std::string sizeString((const char*)size, end); auto sizeInt = std::stoll(sizeString, nullptr, 10); if (sizeInt < 0 || sizeInt >= std::numeric_limits::max()) { wasm::Fatal() << "Malformed archive: size parsing failed\n"; } return static_cast(sizeInt); } Archive::Archive(Buffer& b, bool& error) : data(b), symbolTable({nullptr, 0}), stringTable({nullptr, 0}), firstRegularData(nullptr) { error = false; if (data.size() < strlen(magic) || memcmp(data.data(), magic, strlen(magic))) { error = true; return; } // We require GNU format archives. So the first member may be named "/" and it // points to the symbol table. The next member may optionally be "//" and // point to a string table if a filename is too large to fit in the 16-char // name field of the header. child_iterator it = child_begin(false); if (it.hasError()) { error = true; return; } child_iterator end = child_end(); if (it == end) { return; // Empty archive. } const Child* c = &*it; auto increment = [&]() { ++it; error = it.hasError(); if (error) { return true; } c = &*it; return false; }; std::string name = c->getRawName(); if (name == "/") { symbolTable = c->getBuffer(); if (increment() || it == end) { return; } name = c->getRawName(); } if (name == "//") { stringTable = c->getBuffer(); if (increment() || it == end) { return; } setFirstRegular(*c); return; } if (name[0] != '/') { setFirstRegular(*c); return; } // Not a GNU archive. error = true; } Archive::Child::Child(const Archive* parent, const uint8_t* data, bool* error) : parent(parent), data(data) { if (!data) { return; } len = sizeof(ArchiveMemberHeader) + getHeader()->getSize(); startOfFile = sizeof(ArchiveMemberHeader); } uint32_t Archive::Child::getSize() const { return len - startOfFile; } Archive::SubBuffer Archive::Child::getBuffer() const { return {data + startOfFile, getSize()}; } std::string Archive::Child::getRawName() const { return getHeader()->getName(); } Archive::Child Archive::Child::getNext(bool& error) const { // Members are aligned to even byte boundaries. uint32_t nextOffset = len + (len & 1); if ((size_t)(data - (const uint8_t*)parent->data.data() + nextOffset) >= parent->data.size()) { // End of the archive. return Child(); } return Child(parent, data + nextOffset, &error); } std::string Archive::Child::getName() const { std::string name = getRawName(); // Check if it's a special name. if (name[0] == '/') { if (name.size() == 1) { // Linker member. return name; } if (name.size() == 2 && name[1] == '/') { // String table. return name; } // It's a long name. // Get the offset. int offset = std::stoi(name.substr(1), nullptr, 10); // Verify it. if (offset < 0 || (unsigned)offset >= parent->stringTable.len) { wasm::Fatal() << "Malformed archive: name parsing failed\n"; } std::string addr(parent->stringTable.data + offset, parent->stringTable.data + parent->stringTable.len); // GNU long file names end with a "/\n". size_t end = addr.find('\n'); return addr.substr(0, end - 1); } // It's a simple name. if (name[name.size() - 1] == '/') { return name.substr(0, name.size() - 1); } return name; } Archive::child_iterator Archive::child_begin(bool SkipInternal) const { if (data.size() == 0) { return child_end(); } if (SkipInternal) { child_iterator it; it.child = Child(this, firstRegularData, &it.error); return it; } auto* loc = (const uint8_t*)data.data() + strlen(magic); child_iterator it; it.child = Child(this, loc, &it.error); return it; } Archive::child_iterator Archive::child_end() const { return Child(); } namespace { struct Symbol { uint32_t symbolIndex; uint32_t stringIndex; void next(Archive::SubBuffer& symbolTable) { // Symbol table entries are NUL-terminated. Skip past the next NUL. stringIndex = strchr((char*)symbolTable.data + stringIndex, '\0') - (char*)symbolTable.data + 1; ++symbolIndex; } }; } // namespace static uint32_t read32be(const uint8_t* buf) { return static_cast(buf[0]) << 24 | static_cast(buf[1]) << 16 | static_cast(buf[2]) << 8 | static_cast(buf[3]); } void Archive::dump() const { printf("Archive data %p len %zu, firstRegularData %p\n", data.data(), data.size(), firstRegularData); printf("Symbol table %p, len %u\n", symbolTable.data, symbolTable.len); printf("string table %p, len %u\n", stringTable.data, stringTable.len); const uint8_t* buf = symbolTable.data; if (!buf) { for (auto c = child_begin(), e = child_end(); c != e; ++c) { printf("Child %p, len %u, name %s, size %u\n", c->data, c->len, c->getName().c_str(), c->getSize()); } return; } uint32_t symbolCount = read32be(buf); printf("Symbol count %u\n", symbolCount); buf += sizeof(uint32_t) + (symbolCount * sizeof(uint32_t)); uint32_t string_start_offset = buf - symbolTable.data; Symbol sym = {0, string_start_offset}; while (sym.symbolIndex != symbolCount) { printf("Symbol %u, offset %u\n", sym.symbolIndex, sym.stringIndex); // get the member uint32_t offset = read32be(symbolTable.data + sym.symbolIndex * 4); auto* loc = (const uint8_t*)&data[offset]; child_iterator it; it.child = Child(this, loc, &it.error); printf("Child %p, len %u\n", it.child.data, it.child.len); } } binaryen-version_91/src/support/archive.h000066400000000000000000000066041362402614000207620ustar00rootroot00000000000000/* * Copyright 2016 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* Minimal class for interacting with archives. The API is inspired by LLVM's * Archive class, (in case we want to switch to using that at some point); * however we are missing useful error-handling capabilities and other utilities * that LLVM has (e.g. ErrorOr, MemoryBuffer, StringRef). * We only support the GNU format (not the BSD or COFF variants) */ #ifndef wasm_support_archive_h #define wasm_support_archive_h #include #include #include "wasm.h" class ArchiveMemberHeader; class Archive { // Vector is char instead of uint8_t because read_file only works with char. // Everything else is uint8_t to help distinguish between uses as // uninterpreted bytes (most uses) and C strings (a few uses e.g. strchr) // because most things in these buffers are not nul-terminated using Buffer = std::vector; public: struct SubBuffer { const uint8_t* data; uint32_t len; }; class Child { friend class Archive; const Archive* parent = nullptr; // Includes header but not padding byte. const uint8_t* data = nullptr; uint32_t len = 0; // Offset from data to the start of the file uint16_t startOfFile = 0; const ArchiveMemberHeader* getHeader() const { return reinterpret_cast(data); } Child getNext(bool& error) const; public: Child(){}; Child(const Archive* parent, const uint8_t* data, bool* error); // Size of actual member data (no header/padding) uint32_t getSize() const; SubBuffer getBuffer() const; std::string getRawName() const; std::string getName() const; bool operator==(const Child& other) const { return data == other.data; } }; class child_iterator { friend class Archive; Child child; bool error = false; // TODO: use std::error_code instead? public: child_iterator() = default; explicit child_iterator(bool error) : error(error) {} child_iterator(const Child& c) : child(c) {} const Child* operator->() const { return &child; } const Child& operator*() const { return child; } bool operator==(const child_iterator& other) const { return child == other.child; } bool operator!=(const child_iterator& other) const { return !(*this == other); } child_iterator& operator++() { assert(!error); child = child.getNext(error); return *this; } bool hasError() const { return error; } }; Archive(Buffer& buffer, bool& error); child_iterator child_begin(bool SkipInternal = true) const; child_iterator child_end() const; void dump() const; private: void setFirstRegular(const Child& c) { firstRegularData = c.data; } Buffer& data; SubBuffer symbolTable; SubBuffer stringTable; const uint8_t* firstRegularData; }; #endif // wasm_support_archive_h binaryen-version_91/src/support/base64.h000066400000000000000000000036741362402614000204310ustar00rootroot00000000000000/* * Copyright 2019 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_support_base64_h #define wasm_support_base64_h #include #include #include inline std::string base64Encode(std::vector& data) { std::string ret; size_t i = 0; const char* alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789+/"; while (i + 3 <= data.size()) { uint32_t bits = (((uint32_t)(uint8_t)data[i + 0]) << 16) | (((uint32_t)(uint8_t)data[i + 1]) << 8) | (((uint32_t)(uint8_t)data[i + 2]) << 0); ret += alphabet[(bits >> 18) & 0x3f]; ret += alphabet[(bits >> 12) & 0x3f]; ret += alphabet[(bits >> 6) & 0x3f]; ret += alphabet[(bits >> 0) & 0x3f]; i += 3; } if (i + 2 == data.size()) { uint32_t bits = (((uint32_t)(uint8_t)data[i + 0]) << 8) | (((uint32_t)(uint8_t)data[i + 1]) << 0); ret += alphabet[(bits >> 10) & 0x3f]; ret += alphabet[(bits >> 4) & 0x3f]; ret += alphabet[(bits << 2) & 0x3f]; ret += '='; } else if (i + 1 == data.size()) { uint32_t bits = (uint32_t)(uint8_t)data[i + 0]; ret += alphabet[(bits >> 2) & 0x3f]; ret += alphabet[(bits << 4) & 0x3f]; ret += '='; ret += '='; } else { assert(i == data.size()); } return ret; } #endif // wasm_support_base64_h binaryen-version_91/src/support/bits.cpp000066400000000000000000000101001362402614000206170ustar00rootroot00000000000000/* * Copyright 2015 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define wasm_support_bits_definitions #include "support/bits.h" #include "../compiler-support.h" #include "support/utilities.h" namespace wasm { template<> int PopCount(uint8_t v) { // Small table lookup. static const uint8_t tbl[32] = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5}; return tbl[v & 0xf] + tbl[v >> 4]; } template<> int PopCount(uint16_t v) { return PopCount((uint8_t)(v & 0xff)) + PopCount((uint8_t)(v >> 8)); } template<> int PopCount(uint32_t v) { // See Stanford bithacks, counting bits set in parallel, "best method": // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel v = v - ((v >> 1) & 0x55555555); v = (v & 0x33333333) + ((v >> 2) & 0x33333333); return (((v + (v >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24; } template<> int PopCount(uint64_t v) { return PopCount((uint32_t)v) + PopCount((uint32_t)(v >> 32)); } template<> uint32_t BitReverse(uint32_t v) { // See Hacker's Delight, first edition, figure 7-1. v = ((v & 0x55555555) << 1) | ((v >> 1) & 0x55555555); v = ((v & 0x33333333) << 2) | ((v >> 2) & 0x33333333); v = ((v & 0x0F0F0F0F) << 4) | ((v >> 4) & 0x0F0F0F0F); v = (v << 24) | ((v & 0xFF00) << 8) | ((v >> 8) & 0xFF00) | (v >> 24); return v; } template<> int CountTrailingZeroes(uint32_t v) { // See Stanford bithacks, count the consecutive zero bits (trailing) on the // right with multiply and lookup: // http://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightMultLookup static const uint8_t tbl[32] = {0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9}; return v ? (int)tbl[((uint32_t)((v & -v) * 0x077CB531U)) >> 27] : 32; } template<> int CountTrailingZeroes(uint64_t v) { return (uint32_t)v ? CountTrailingZeroes((uint32_t)v) : 32 + CountTrailingZeroes((uint32_t)(v >> 32)); } template<> int CountLeadingZeroes(uint32_t v) { // See Stanford bithacks, find the log base 2 of an N-bit integer in // O(lg(N)) operations with multiply and lookup: // http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogDeBruijn static const uint8_t tbl[32] = {31, 22, 30, 21, 18, 10, 29, 2, 20, 17, 15, 13, 9, 6, 28, 1, 23, 19, 11, 3, 16, 14, 7, 24, 12, 4, 8, 25, 5, 26, 27, 0}; v = v | (v >> 1); v = v | (v >> 2); v = v | (v >> 4); v = v | (v >> 8); v = v | (v >> 16); return v ? (int)tbl[((uint32_t)(v * 0x07C4ACDDU)) >> 27] : 32; } template<> int CountLeadingZeroes(uint64_t v) { return v >> 32 ? CountLeadingZeroes((uint32_t)(v >> 32)) : 32 + CountLeadingZeroes((uint32_t)v); } uint32_t Log2(uint32_t v) { switch (v) { default: WASM_UNREACHABLE("invalid value"); case 1: return 0; case 2: return 1; case 4: return 2; case 8: return 3; case 16: return 4; case 32: return 5; } } uint32_t Pow2(uint32_t v) { switch (v) { case 0: return 1; case 1: return 2; case 2: return 4; case 3: return 8; case 4: return 16; case 5: return 32; default: return 1 << v; } } } // namespace wasm binaryen-version_91/src/support/bits.h000066400000000000000000000055731362402614000203060ustar00rootroot00000000000000/* * Copyright 2015 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_support_bits_h #define wasm_support_bits_h #include #include #include /* * Portable bit functions. * * Not all platforms offer fast intrinsics for these functions, and some * compilers require checking CPUID at runtime before using the intrinsic. * * We instead use portable and reasonably-fast implementations, while * avoiding implementations with large lookup tables. * * TODO: The convention here should be changed PopCount => popCount, * initial lowercase, to match the rest of the codebase. */ namespace wasm { template int PopCount(T); template uint32_t BitReverse(T); template int CountTrailingZeroes(T); template int CountLeadingZeroes(T); #ifndef wasm_support_bits_definitions // The template specializations are provided elsewhere. extern template int PopCount(uint8_t); extern template int PopCount(uint16_t); extern template int PopCount(uint32_t); extern template int PopCount(uint64_t); extern template uint32_t BitReverse(uint32_t); extern template int CountTrailingZeroes(uint32_t); extern template int CountTrailingZeroes(uint64_t); extern template int CountLeadingZeroes(uint32_t); extern template int CountLeadingZeroes(uint64_t); #endif // Convenience signed -> unsigned. It usually doesn't make much sense to use bit // functions on signed types. template int PopCount(T v) { return PopCount(typename std::make_unsigned::type(v)); } template int CountTrailingZeroes(T v) { return CountTrailingZeroes(typename std::make_unsigned::type(v)); } template int CountLeadingZeroes(T v) { return CountLeadingZeroes(typename std::make_unsigned::type(v)); } template bool IsPowerOf2(T v) { return v != 0 && PopCount(v) == 1; } template inline static T RotateLeft(T val, U count) { T mask = sizeof(T) * CHAR_BIT - 1; count &= mask; return (val << count) | (val >> (-count & mask)); } template inline static T RotateRight(T val, U count) { T mask = sizeof(T) * CHAR_BIT - 1; count &= mask; return (val >> count) | (val << (-count & mask)); } extern uint32_t Log2(uint32_t v); extern uint32_t Pow2(uint32_t v); } // namespace wasm #endif // wasm_support_bits_h binaryen-version_91/src/support/colors.cpp000066400000000000000000000035651362402614000212000ustar00rootroot00000000000000/* * Copyright 2015 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "support/colors.h" #include #include namespace { bool colors_enabled = true; } // anonymous namespace void Colors::setEnabled(bool enabled) { colors_enabled = enabled; } bool Colors::isEnabled() { return colors_enabled; } #if defined(__linux__) || defined(__APPLE__) #include void Colors::outputColorCode(std::ostream& stream, const char* colorCode) { const static bool has_color = []() { return (getenv("COLORS") && getenv("COLORS")[0] == '1') || // forced (isatty(STDOUT_FILENO) && (!getenv("COLORS") || getenv("COLORS")[0] != '0')); // implicit }(); if (has_color && colors_enabled) { stream << colorCode; } } #elif defined(_WIN32) #include #include #include void Colors::outputColorCode(std::ostream& stream, const WORD& colorCode) { const static bool has_color = []() { return _isatty(_fileno(stdout)) && (!getenv("COLORS") || getenv("COLORS")[0] != '0'); // implicit }(); static HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE); static HANDLE hStderr = GetStdHandle(STD_ERROR_HANDLE); if (has_color && colors_enabled) SetConsoleTextAttribute(&stream == &std::cout ? hStdout : hStderr, colorCode); } #endif binaryen-version_91/src/support/colors.h000066400000000000000000000051031362402614000206330ustar00rootroot00000000000000/* * Copyright 2015 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef wasm_support_color_h #define wasm_support_color_h #include namespace Colors { void setEnabled(bool enabled); bool isEnabled(); #if defined(__linux__) || defined(__APPLE__) void outputColorCode(std::ostream& stream, const char* colorCode); inline void normal(std::ostream& stream) { outputColorCode(stream, "\033[0m"); } inline void red(std::ostream& stream) { outputColorCode(stream, "\033[31m"); } inline void magenta(std::ostream& stream) { outputColorCode(stream, "\033[35m"); } inline void orange(std::ostream& stream) { outputColorCode(stream, "\033[33m"); } inline void grey(std::ostream& stream) { outputColorCode(stream, "\033[37m"); } inline void green(std::ostream& stream) { outputColorCode(stream, "\033[32m"); } inline void blue(std::ostream& stream) { outputColorCode(stream, "\033[34m"); } inline void bold(std::ostream& stream) { outputColorCode(stream, "\033[1m"); } #elif defined(_WIN32) void outputColorCode(std::ostream& stream, const unsigned short& colorCode); inline void normal(std::ostream& stream) { outputColorCode(stream, 0x07); } inline void red(std::ostream& stream) { outputColorCode(stream, 0x0c); } inline void magenta(std::ostream& stream) { outputColorCode(stream, 0x05); } inline void orange(std::ostream& stream) { outputColorCode(stream, 0x06); } inline void grey(std::ostream& stream) { outputColorCode(stream, 0x08); } inline void green(std::ostream& stream) { outputColorCode(stream, 0x02); } inline void blue(std::ostream& stream) { outputColorCode(stream, 0x09); } inline void bold(std::ostream& stream) { /* Do nothing */ } #else inline void normal(std::ostream& stream) {} inline void red(std::ostream& stream) {} inline void magenta(std::ostream& stream) {} inline void orange(std::ostream& stream) {} inline void grey(std::ostream& stream) {} inline void green(std::ostream& stream) {} inline void blue(std::ostream& stream) {} inline void bold(std::ostream& stream) {} #endif } // namespace Colors #endif // wasm_support_color_h binaryen-version_91/src/support/command-line.cpp000066400000000000000000000145351362402614000222410ustar00rootroot00000000000000/* * Copyright 2015 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "support/command-line.h" #include "config.h" #include "support/debug.h" using namespace wasm; #ifndef SCREEN_WIDTH #define SCREEN_WIDTH 80 #endif void printWrap(std::ostream& os, int leftPad, const std::string& content) { int len = content.size(); int space = SCREEN_WIDTH - leftPad; std::string nextWord; std::string pad(leftPad, ' '); for (int i = 0; i <= len; ++i) { if (i != len && content[i] != ' ' && content[i] != '\n') { nextWord += content[i]; } else { if (static_cast(nextWord.size()) > space) { os << '\n' << pad; space = SCREEN_WIDTH - leftPad; } os << nextWord; space -= nextWord.size() + 1; if (space > 0) { os << ' '; } nextWord.clear(); if (content[i] == '\n') { os << '\n'; space = SCREEN_WIDTH - leftPad; } } } } Options::Options(const std::string& command, const std::string& description) : debug(false), positional(Arguments::Zero) { add("--version", "", "Output version information and exit", Arguments::Zero, [command](Options*, const std::string&) { std::cout << command << " version " << PROJECT_VERSION << "\n"; exit(0); }); add("--help", "-h", "Show this help message and exit", Arguments::Zero, [this, command, description](Options* o, const std::string&) { std::cout << command; if (positional != Arguments::Zero) { std::cout << ' ' << positionalName; } std::cout << "\n\n"; printWrap(std::cout, 0, description); std::cout << "\n\nOptions:\n"; size_t optionWidth = 0; for (const auto& o : options) { optionWidth = std::max(optionWidth, o.longName.size() + o.shortName.size()); } for (const auto& o : options) { bool long_n_short = o.longName.size() != 0 && o.shortName.size() != 0; size_t pad = 1 + optionWidth - o.longName.size() - o.shortName.size(); std::cout << " " << o.longName << (long_n_short ? ',' : ' ') << o.shortName << std::string(pad, ' '); printWrap(std::cout, optionWidth + 4, o.description); std::cout << '\n'; } std::cout << '\n'; exit(EXIT_SUCCESS); }); add("--debug", "-d", "Print debug information to stderr", Arguments::Optional, [&](Options* o, const std::string& arguments) { debug = true; setDebugEnabled(arguments.c_str()); }); } Options::~Options() {} Options& Options::add(const std::string& longName, const std::string& shortName, const std::string& description, Arguments arguments, const Action& action) { options.push_back({longName, shortName, description, arguments, action, 0}); return *this; } Options& Options::add_positional(const std::string& name, Arguments arguments, const Action& action) { positional = arguments; positionalName = name; positionalAction = action; return *this; } void Options::parse(int argc, const char* argv[]) { assert(argc > 0 && "expect at least program name as an argument"); size_t positionalsSeen = 0; auto dashes = [](const std::string& s) { for (size_t i = 0;; ++i) { if (s[i] != '-') { return i; } } }; for (size_t i = 1, e = argc; i != e; ++i) { std::string currentOption = argv[i]; if (dashes(currentOption) == 0) { // Positional. switch (positional) { case Arguments::Zero: std::cerr << "Unexpected positional argument '" << currentOption << "'\n"; exit(EXIT_FAILURE); case Arguments::One: case Arguments::Optional: if (positionalsSeen) { std::cerr << "Unexpected second positional argument '" << currentOption << "' for " << positionalName << '\n'; exit(EXIT_FAILURE); } // Fallthrough. case Arguments::N: positionalAction(this, currentOption); ++positionalsSeen; break; } continue; } // Non-positional. std::string argument; auto equal = currentOption.find_first_of('='); if (equal != std::string::npos) { argument = currentOption.substr(equal + 1); currentOption = currentOption.substr(0, equal); } Option* option = nullptr; for (auto& o : options) { if (o.longName == currentOption || o.shortName == currentOption) { option = &o; } } if (!option) { std::cerr << "Unknown option '" << currentOption << "'\n"; exit(EXIT_FAILURE); } switch (option->arguments) { case Arguments::Zero: if (argument.size()) { std::cerr << "Unexpected argument '" << argument << "' for option '" << currentOption << "'\n"; exit(EXIT_FAILURE); } break; case Arguments::One: if (option->seen) { std::cerr << "Unexpected second argument '" << argument << "' for '" << currentOption << "'\n"; exit(EXIT_FAILURE); } // Fallthrough. case Arguments::N: if (!argument.size()) { if (i + 1 == e) { std::cerr << "Couldn't find expected argument for '" << currentOption << "'\n"; exit(EXIT_FAILURE); } argument = argv[++i]; } break; case Arguments::Optional: if (!argument.size()) { if (i + 1 != e) { argument = argv[++i]; } } break; } option->action(this, argument); ++option->seen; } } binaryen-version_91/src/support/command-line.h000066400000000000000000000035541362402614000217050ustar00rootroot00000000000000/* * Copyright 2015 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // // Command line helpers. // #ifndef wasm_support_command_line_h #define wasm_support_command_line_h #include #include #include #include #include #include "wasm.h" namespace wasm { class Options { public: using Action = std::function; enum class Arguments { Zero, One, N, Optional }; bool debug; std::map extra; Options(const std::string& command, const std::string& description); ~Options(); Options& add(const std::string& longName, const std::string& shortName, const std::string& description, Arguments arguments, const Action& action); Options& add_positional(const std::string& name, Arguments arguments, const Action& action); void parse(int argc, const char* argv[]); private: struct Option { std::string longName; std::string shortName; std::string description; Arguments arguments; Action action; size_t seen; }; std::vector

(Data.getULEB128(OffsetPtr)); if (A && F) { bool IsImplicitConst = (F == DW_FORM_implicit_const); if (IsImplicitConst) { int64_t V = Data.getSLEB128(OffsetPtr); AttributeSpecs.push_back(AttributeSpec(A, F, V)); continue; } Optional ByteSize; // If this abbrevation still has a fixed byte size, then update the // FixedAttributeSize as needed. switch (F) { case DW_FORM_addr: if (FixedAttributeSize) ++FixedAttributeSize->NumAddrs; break; case DW_FORM_ref_addr: if (FixedAttributeSize) ++FixedAttributeSize->NumRefAddrs; break; case DW_FORM_strp: case DW_FORM_GNU_ref_alt: case DW_FORM_GNU_strp_alt: case DW_FORM_line_strp: case DW_FORM_sec_offset: case DW_FORM_strp_sup: if (FixedAttributeSize) ++FixedAttributeSize->NumDwarfOffsets; break; default: // The form has a byte size that doesn't depend on Params. // If it's a fixed size, keep track of it. if ((ByteSize = dwarf::getFixedFormByteSize(F, dwarf::FormParams()))) { if (FixedAttributeSize) FixedAttributeSize->NumBytes += *ByteSize; break; } // Indicate we no longer have a fixed byte size for this // abbreviation by clearing the FixedAttributeSize optional value // so it doesn't have a value. FixedAttributeSize.reset(); break; } // Record this attribute and its fixed size if it has one. AttributeSpecs.push_back(AttributeSpec(A, F, ByteSize)); } else if (A == 0 && F == 0) { // We successfully reached the end of this abbreviation declaration // since both attribute and form are zero. break; } else { // Attribute and form pairs must either both be non-zero, in which case // they are added to the abbreviation declaration, or both be zero to // terminate the abbrevation declaration. In this case only one was // zero which is an error. clear(); return false; } } return true; } void DWARFAbbreviationDeclaration::dump(raw_ostream &OS) const { OS << '[' << getCode() << "] "; OS << formatv("{0}", getTag()); OS << "\tDW_CHILDREN_" << (hasChildren() ? "yes" : "no") << '\n'; for (const AttributeSpec &Spec : AttributeSpecs) { OS << formatv("\t{0}\t{1}", Spec.Attr, Spec.Form); if (Spec.isImplicitConst()) OS << '\t' << Spec.getImplicitConstValue(); OS << '\n'; } OS << '\n'; } Optional DWARFAbbreviationDeclaration::findAttributeIndex(dwarf::Attribute Attr) const { for (uint32_t i = 0, e = AttributeSpecs.size(); i != e; ++i) { if (AttributeSpecs[i].Attr == Attr) return i; } return None; } Optional DWARFAbbreviationDeclaration::getAttributeValue( const uint64_t DIEOffset, const dwarf::Attribute Attr, const DWARFUnit &U) const { Optional MatchAttrIndex = findAttributeIndex(Attr); if (!MatchAttrIndex) return None; auto DebugInfoData = U.getDebugInfoExtractor(); // Add the byte size of ULEB that for the abbrev Code so we can start // skipping the attribute data. uint64_t Offset = DIEOffset + CodeByteSize; uint32_t AttrIndex = 0; for (const auto &Spec : AttributeSpecs) { if (*MatchAttrIndex == AttrIndex) { // We have arrived at the attribute to extract, extract if from Offset. if (Spec.isImplicitConst()) return DWARFFormValue::createFromSValue(Spec.Form, Spec.getImplicitConstValue()); DWARFFormValue FormValue(Spec.Form); if (FormValue.extractValue(DebugInfoData, &Offset, U.getFormParams(), &U)) return FormValue; } // March Offset along until we get to the attribute we want. if (auto FixedSize = Spec.getByteSize(U)) Offset += *FixedSize; else DWARFFormValue::skipValue(Spec.Form, DebugInfoData, &Offset, U.getFormParams()); ++AttrIndex; } return None; } size_t DWARFAbbreviationDeclaration::FixedSizeInfo::getByteSize( const DWARFUnit &U) const { size_t ByteSize = NumBytes; if (NumAddrs) ByteSize += NumAddrs * U.getAddressByteSize(); if (NumRefAddrs) ByteSize += NumRefAddrs * U.getRefAddrByteSize(); if (NumDwarfOffsets) ByteSize += NumDwarfOffsets * U.getDwarfOffsetByteSize(); return ByteSize; } Optional DWARFAbbreviationDeclaration::AttributeSpec::getByteSize( const DWARFUnit &U) const { if (isImplicitConst()) return 0; if (ByteSize.HasByteSize) return ByteSize.ByteSize; Optional S; auto FixedByteSize = dwarf::getFixedFormByteSize(Form, U.getFormParams()); if (FixedByteSize) S = *FixedByteSize; return S; } Optional DWARFAbbreviationDeclaration::getFixedAttributesByteSize( const DWARFUnit &U) const { if (FixedAttributeSize) return FixedAttributeSize->getByteSize(U); return None; } binaryen-version_91/third_party/llvm-project/DWARFAcceleratorTable.cpp000066400000000000000000000724551362402614000263270ustar00rootroot00000000000000//===- DWARFAcceleratorTable.cpp ------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h" #include "llvm/ADT/SmallVector.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/DebugInfo/DWARF/DWARFRelocMap.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/DJB.h" #include "llvm/Support/Errc.h" #include "llvm/Support/Format.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/ScopedPrinter.h" #include "llvm/Support/raw_ostream.h" #include #include #include using namespace llvm; namespace { struct Atom { unsigned Value; }; static raw_ostream &operator<<(raw_ostream &OS, const Atom &A) { StringRef Str = dwarf::AtomTypeString(A.Value); if (!Str.empty()) return OS << Str; return OS << "DW_ATOM_unknown_" << format("%x", A.Value); } } // namespace static Atom formatAtom(unsigned Atom) { return {Atom}; } DWARFAcceleratorTable::~DWARFAcceleratorTable() = default; Error AppleAcceleratorTable::extract() { uint64_t Offset = 0; // Check that we can at least read the header. if (!AccelSection.isValidOffset(offsetof(Header, HeaderDataLength) + 4)) return createStringError(errc::illegal_byte_sequence, "Section too small: cannot read header."); Hdr.Magic = AccelSection.getU32(&Offset); Hdr.Version = AccelSection.getU16(&Offset); Hdr.HashFunction = AccelSection.getU16(&Offset); Hdr.BucketCount = AccelSection.getU32(&Offset); Hdr.HashCount = AccelSection.getU32(&Offset); Hdr.HeaderDataLength = AccelSection.getU32(&Offset); // Check that we can read all the hashes and offsets from the // section (see SourceLevelDebugging.rst for the structure of the index). // We need to substract one because we're checking for an *offset* which is // equal to the size for an empty table and hence pointer after the section. if (!AccelSection.isValidOffset(sizeof(Hdr) + Hdr.HeaderDataLength + Hdr.BucketCount * 4 + Hdr.HashCount * 8 - 1)) return createStringError( errc::illegal_byte_sequence, "Section too small: cannot read buckets and hashes."); HdrData.DIEOffsetBase = AccelSection.getU32(&Offset); uint32_t NumAtoms = AccelSection.getU32(&Offset); for (unsigned i = 0; i < NumAtoms; ++i) { uint16_t AtomType = AccelSection.getU16(&Offset); auto AtomForm = static_cast(AccelSection.getU16(&Offset)); HdrData.Atoms.push_back(std::make_pair(AtomType, AtomForm)); } IsValid = true; return Error::success(); } uint32_t AppleAcceleratorTable::getNumBuckets() { return Hdr.BucketCount; } uint32_t AppleAcceleratorTable::getNumHashes() { return Hdr.HashCount; } uint32_t AppleAcceleratorTable::getSizeHdr() { return sizeof(Hdr); } uint32_t AppleAcceleratorTable::getHeaderDataLength() { return Hdr.HeaderDataLength; } ArrayRef> AppleAcceleratorTable::getAtomsDesc() { return HdrData.Atoms; } bool AppleAcceleratorTable::validateForms() { for (auto Atom : getAtomsDesc()) { DWARFFormValue FormValue(Atom.second); switch (Atom.first) { case dwarf::DW_ATOM_die_offset: case dwarf::DW_ATOM_die_tag: case dwarf::DW_ATOM_type_flags: if ((!FormValue.isFormClass(DWARFFormValue::FC_Constant) && !FormValue.isFormClass(DWARFFormValue::FC_Flag)) || FormValue.getForm() == dwarf::DW_FORM_sdata) return false; break; default: break; } } return true; } std::pair AppleAcceleratorTable::readAtoms(uint64_t *HashDataOffset) { uint64_t DieOffset = dwarf::DW_INVALID_OFFSET; dwarf::Tag DieTag = dwarf::DW_TAG_null; dwarf::FormParams FormParams = {Hdr.Version, 0, dwarf::DwarfFormat::DWARF32}; for (auto Atom : getAtomsDesc()) { DWARFFormValue FormValue(Atom.second); FormValue.extractValue(AccelSection, HashDataOffset, FormParams); switch (Atom.first) { case dwarf::DW_ATOM_die_offset: DieOffset = *FormValue.getAsUnsignedConstant(); break; case dwarf::DW_ATOM_die_tag: DieTag = (dwarf::Tag)*FormValue.getAsUnsignedConstant(); break; default: break; } } return {DieOffset, DieTag}; } void AppleAcceleratorTable::Header::dump(ScopedPrinter &W) const { DictScope HeaderScope(W, "Header"); W.printHex("Magic", Magic); W.printHex("Version", Version); W.printHex("Hash function", HashFunction); W.printNumber("Bucket count", BucketCount); W.printNumber("Hashes count", HashCount); W.printNumber("HeaderData length", HeaderDataLength); } Optional AppleAcceleratorTable::HeaderData::extractOffset( Optional Value) const { if (!Value) return None; switch (Value->getForm()) { case dwarf::DW_FORM_ref1: case dwarf::DW_FORM_ref2: case dwarf::DW_FORM_ref4: case dwarf::DW_FORM_ref8: case dwarf::DW_FORM_ref_udata: return Value->getRawUValue() + DIEOffsetBase; default: return Value->getAsSectionOffset(); } } bool AppleAcceleratorTable::dumpName(ScopedPrinter &W, SmallVectorImpl &AtomForms, uint64_t *DataOffset) const { dwarf::FormParams FormParams = {Hdr.Version, 0, dwarf::DwarfFormat::DWARF32}; uint64_t NameOffset = *DataOffset; if (!AccelSection.isValidOffsetForDataOfSize(*DataOffset, 4)) { W.printString("Incorrectly terminated list."); return false; } uint64_t StringOffset = AccelSection.getRelocatedValue(4, DataOffset); if (!StringOffset) return false; // End of list DictScope NameScope(W, ("Name@0x" + Twine::utohexstr(NameOffset)).str()); W.startLine() << format("String: 0x%08" PRIx64, StringOffset); W.getOStream() << " \"" << StringSection.getCStr(&StringOffset) << "\"\n"; unsigned NumData = AccelSection.getU32(DataOffset); for (unsigned Data = 0; Data < NumData; ++Data) { ListScope DataScope(W, ("Data " + Twine(Data)).str()); unsigned i = 0; for (auto &Atom : AtomForms) { W.startLine() << format("Atom[%d]: ", i); if (Atom.extractValue(AccelSection, DataOffset, FormParams)) { Atom.dump(W.getOStream()); if (Optional Val = Atom.getAsUnsignedConstant()) { StringRef Str = dwarf::AtomValueString(HdrData.Atoms[i].first, *Val); if (!Str.empty()) W.getOStream() << " (" << Str << ")"; } } else W.getOStream() << "Error extracting the value"; W.getOStream() << "\n"; i++; } } return true; // more entries follow } LLVM_DUMP_METHOD void AppleAcceleratorTable::dump(raw_ostream &OS) const { if (!IsValid) return; ScopedPrinter W(OS); Hdr.dump(W); W.printNumber("DIE offset base", HdrData.DIEOffsetBase); W.printNumber("Number of atoms", uint64_t(HdrData.Atoms.size())); SmallVector AtomForms; { ListScope AtomsScope(W, "Atoms"); unsigned i = 0; for (const auto &Atom : HdrData.Atoms) { DictScope AtomScope(W, ("Atom " + Twine(i++)).str()); W.startLine() << "Type: " << formatAtom(Atom.first) << '\n'; W.startLine() << "Form: " << formatv("{0}", Atom.second) << '\n'; AtomForms.push_back(DWARFFormValue(Atom.second)); } } // Now go through the actual tables and dump them. uint64_t Offset = sizeof(Hdr) + Hdr.HeaderDataLength; uint64_t HashesBase = Offset + Hdr.BucketCount * 4; uint64_t OffsetsBase = HashesBase + Hdr.HashCount * 4; for (unsigned Bucket = 0; Bucket < Hdr.BucketCount; ++Bucket) { unsigned Index = AccelSection.getU32(&Offset); ListScope BucketScope(W, ("Bucket " + Twine(Bucket)).str()); if (Index == UINT32_MAX) { W.printString("EMPTY"); continue; } for (unsigned HashIdx = Index; HashIdx < Hdr.HashCount; ++HashIdx) { uint64_t HashOffset = HashesBase + HashIdx*4; uint64_t OffsetsOffset = OffsetsBase + HashIdx*4; uint32_t Hash = AccelSection.getU32(&HashOffset); if (Hash % Hdr.BucketCount != Bucket) break; uint64_t DataOffset = AccelSection.getU32(&OffsetsOffset); ListScope HashScope(W, ("Hash 0x" + Twine::utohexstr(Hash)).str()); if (!AccelSection.isValidOffset(DataOffset)) { W.printString("Invalid section offset"); continue; } while (dumpName(W, AtomForms, &DataOffset)) /*empty*/; } } } AppleAcceleratorTable::Entry::Entry( const AppleAcceleratorTable::HeaderData &HdrData) : HdrData(&HdrData) { Values.reserve(HdrData.Atoms.size()); for (const auto &Atom : HdrData.Atoms) Values.push_back(DWARFFormValue(Atom.second)); } void AppleAcceleratorTable::Entry::extract( const AppleAcceleratorTable &AccelTable, uint64_t *Offset) { dwarf::FormParams FormParams = {AccelTable.Hdr.Version, 0, dwarf::DwarfFormat::DWARF32}; for (auto &Atom : Values) Atom.extractValue(AccelTable.AccelSection, Offset, FormParams); } Optional AppleAcceleratorTable::Entry::lookup(HeaderData::AtomType Atom) const { assert(HdrData && "Dereferencing end iterator?"); assert(HdrData->Atoms.size() == Values.size()); for (const auto &Tuple : zip_first(HdrData->Atoms, Values)) { if (std::get<0>(Tuple).first == Atom) return std::get<1>(Tuple); } return None; } Optional AppleAcceleratorTable::Entry::getDIESectionOffset() const { return HdrData->extractOffset(lookup(dwarf::DW_ATOM_die_offset)); } Optional AppleAcceleratorTable::Entry::getCUOffset() const { return HdrData->extractOffset(lookup(dwarf::DW_ATOM_cu_offset)); } Optional AppleAcceleratorTable::Entry::getTag() const { Optional Tag = lookup(dwarf::DW_ATOM_die_tag); if (!Tag) return None; if (Optional Value = Tag->getAsUnsignedConstant()) return dwarf::Tag(*Value); return None; } AppleAcceleratorTable::ValueIterator::ValueIterator( const AppleAcceleratorTable &AccelTable, uint64_t Offset) : AccelTable(&AccelTable), Current(AccelTable.HdrData), DataOffset(Offset) { if (!AccelTable.AccelSection.isValidOffsetForDataOfSize(DataOffset, 4)) return; // Read the first entry. NumData = AccelTable.AccelSection.getU32(&DataOffset); Next(); } void AppleAcceleratorTable::ValueIterator::Next() { assert(NumData > 0 && "attempted to increment iterator past the end"); auto &AccelSection = AccelTable->AccelSection; if (Data >= NumData || !AccelSection.isValidOffsetForDataOfSize(DataOffset, 4)) { NumData = 0; DataOffset = 0; return; } Current.extract(*AccelTable, &DataOffset); ++Data; } iterator_range AppleAcceleratorTable::equal_range(StringRef Key) const { if (!IsValid) return make_range(ValueIterator(), ValueIterator()); // Find the bucket. unsigned HashValue = djbHash(Key); unsigned Bucket = HashValue % Hdr.BucketCount; uint64_t BucketBase = sizeof(Hdr) + Hdr.HeaderDataLength; uint64_t HashesBase = BucketBase + Hdr.BucketCount * 4; uint64_t OffsetsBase = HashesBase + Hdr.HashCount * 4; uint64_t BucketOffset = BucketBase + Bucket * 4; unsigned Index = AccelSection.getU32(&BucketOffset); // Search through all hashes in the bucket. for (unsigned HashIdx = Index; HashIdx < Hdr.HashCount; ++HashIdx) { uint64_t HashOffset = HashesBase + HashIdx * 4; uint64_t OffsetsOffset = OffsetsBase + HashIdx * 4; uint32_t Hash = AccelSection.getU32(&HashOffset); if (Hash % Hdr.BucketCount != Bucket) // We are already in the next bucket. break; uint64_t DataOffset = AccelSection.getU32(&OffsetsOffset); uint64_t StringOffset = AccelSection.getRelocatedValue(4, &DataOffset); if (!StringOffset) break; // Finally, compare the key. if (Key == StringSection.getCStr(&StringOffset)) return make_range({*this, DataOffset}, ValueIterator()); } return make_range(ValueIterator(), ValueIterator()); } void DWARFDebugNames::Header::dump(ScopedPrinter &W) const { DictScope HeaderScope(W, "Header"); W.printHex("Length", UnitLength); W.printNumber("Version", Version); W.printHex("Padding", Padding); W.printNumber("CU count", CompUnitCount); W.printNumber("Local TU count", LocalTypeUnitCount); W.printNumber("Foreign TU count", ForeignTypeUnitCount); W.printNumber("Bucket count", BucketCount); W.printNumber("Name count", NameCount); W.printHex("Abbreviations table size", AbbrevTableSize); W.startLine() << "Augmentation: '" << AugmentationString << "'\n"; } Error DWARFDebugNames::Header::extract(const DWARFDataExtractor &AS, uint64_t *Offset) { // Check that we can read the fixed-size part. if (!AS.isValidOffset(*Offset + sizeof(HeaderPOD) - 1)) return createStringError(errc::illegal_byte_sequence, "Section too small: cannot read header."); UnitLength = AS.getU32(Offset); Version = AS.getU16(Offset); Padding = AS.getU16(Offset); CompUnitCount = AS.getU32(Offset); LocalTypeUnitCount = AS.getU32(Offset); ForeignTypeUnitCount = AS.getU32(Offset); BucketCount = AS.getU32(Offset); NameCount = AS.getU32(Offset); AbbrevTableSize = AS.getU32(Offset); AugmentationStringSize = alignTo(AS.getU32(Offset), 4); if (!AS.isValidOffsetForDataOfSize(*Offset, AugmentationStringSize)) return createStringError( errc::illegal_byte_sequence, "Section too small: cannot read header augmentation."); AugmentationString.resize(AugmentationStringSize); AS.getU8(Offset, reinterpret_cast(AugmentationString.data()), AugmentationStringSize); return Error::success(); } void DWARFDebugNames::Abbrev::dump(ScopedPrinter &W) const { DictScope AbbrevScope(W, ("Abbreviation 0x" + Twine::utohexstr(Code)).str()); W.startLine() << formatv("Tag: {0}\n", Tag); for (const auto &Attr : Attributes) W.startLine() << formatv("{0}: {1}\n", Attr.Index, Attr.Form); } static constexpr DWARFDebugNames::AttributeEncoding sentinelAttrEnc() { return {dwarf::Index(0), dwarf::Form(0)}; } static bool isSentinel(const DWARFDebugNames::AttributeEncoding &AE) { return AE == sentinelAttrEnc(); } static DWARFDebugNames::Abbrev sentinelAbbrev() { return DWARFDebugNames::Abbrev(0, dwarf::Tag(0), {}); } static bool isSentinel(const DWARFDebugNames::Abbrev &Abbr) { return Abbr.Code == 0; } DWARFDebugNames::Abbrev DWARFDebugNames::AbbrevMapInfo::getEmptyKey() { return sentinelAbbrev(); } DWARFDebugNames::Abbrev DWARFDebugNames::AbbrevMapInfo::getTombstoneKey() { return DWARFDebugNames::Abbrev(~0, dwarf::Tag(0), {}); } Expected DWARFDebugNames::NameIndex::extractAttributeEncoding(uint64_t *Offset) { if (*Offset >= EntriesBase) { return createStringError(errc::illegal_byte_sequence, "Incorrectly terminated abbreviation table."); } uint32_t Index = Section.AccelSection.getULEB128(Offset); uint32_t Form = Section.AccelSection.getULEB128(Offset); return AttributeEncoding(dwarf::Index(Index), dwarf::Form(Form)); } Expected> DWARFDebugNames::NameIndex::extractAttributeEncodings(uint64_t *Offset) { std::vector Result; for (;;) { auto AttrEncOr = extractAttributeEncoding(Offset); if (!AttrEncOr) return AttrEncOr.takeError(); if (isSentinel(*AttrEncOr)) return std::move(Result); Result.emplace_back(*AttrEncOr); } } Expected DWARFDebugNames::NameIndex::extractAbbrev(uint64_t *Offset) { if (*Offset >= EntriesBase) { return createStringError(errc::illegal_byte_sequence, "Incorrectly terminated abbreviation table."); } uint32_t Code = Section.AccelSection.getULEB128(Offset); if (Code == 0) return sentinelAbbrev(); uint32_t Tag = Section.AccelSection.getULEB128(Offset); auto AttrEncOr = extractAttributeEncodings(Offset); if (!AttrEncOr) return AttrEncOr.takeError(); return Abbrev(Code, dwarf::Tag(Tag), std::move(*AttrEncOr)); } Error DWARFDebugNames::NameIndex::extract() { const DWARFDataExtractor &AS = Section.AccelSection; uint64_t Offset = Base; if (Error E = Hdr.extract(AS, &Offset)) return E; CUsBase = Offset; Offset += Hdr.CompUnitCount * 4; Offset += Hdr.LocalTypeUnitCount * 4; Offset += Hdr.ForeignTypeUnitCount * 8; BucketsBase = Offset; Offset += Hdr.BucketCount * 4; HashesBase = Offset; if (Hdr.BucketCount > 0) Offset += Hdr.NameCount * 4; StringOffsetsBase = Offset; Offset += Hdr.NameCount * 4; EntryOffsetsBase = Offset; Offset += Hdr.NameCount * 4; if (!AS.isValidOffsetForDataOfSize(Offset, Hdr.AbbrevTableSize)) return createStringError(errc::illegal_byte_sequence, "Section too small: cannot read abbreviations."); EntriesBase = Offset + Hdr.AbbrevTableSize; for (;;) { auto AbbrevOr = extractAbbrev(&Offset); if (!AbbrevOr) return AbbrevOr.takeError(); if (isSentinel(*AbbrevOr)) return Error::success(); if (!Abbrevs.insert(std::move(*AbbrevOr)).second) return createStringError(errc::invalid_argument, "Duplicate abbreviation code."); } } DWARFDebugNames::Entry::Entry(const NameIndex &NameIdx, const Abbrev &Abbr) : NameIdx(&NameIdx), Abbr(&Abbr) { // This merely creates form values. It is up to the caller // (NameIndex::getEntry) to populate them. Values.reserve(Abbr.Attributes.size()); for (const auto &Attr : Abbr.Attributes) Values.emplace_back(Attr.Form); } Optional DWARFDebugNames::Entry::lookup(dwarf::Index Index) const { assert(Abbr->Attributes.size() == Values.size()); for (const auto &Tuple : zip_first(Abbr->Attributes, Values)) { if (std::get<0>(Tuple).Index == Index) return std::get<1>(Tuple); } return None; } Optional DWARFDebugNames::Entry::getDIEUnitOffset() const { if (Optional Off = lookup(dwarf::DW_IDX_die_offset)) return Off->getAsReferenceUVal(); return None; } Optional DWARFDebugNames::Entry::getCUIndex() const { if (Optional Off = lookup(dwarf::DW_IDX_compile_unit)) return Off->getAsUnsignedConstant(); // In a per-CU index, the entries without a DW_IDX_compile_unit attribute // implicitly refer to the single CU. if (NameIdx->getCUCount() == 1) return 0; return None; } Optional DWARFDebugNames::Entry::getCUOffset() const { Optional Index = getCUIndex(); if (!Index || *Index >= NameIdx->getCUCount()) return None; return NameIdx->getCUOffset(*Index); } void DWARFDebugNames::Entry::dump(ScopedPrinter &W) const { W.printHex("Abbrev", Abbr->Code); W.startLine() << formatv("Tag: {0}\n", Abbr->Tag); assert(Abbr->Attributes.size() == Values.size()); for (const auto &Tuple : zip_first(Abbr->Attributes, Values)) { W.startLine() << formatv("{0}: ", std::get<0>(Tuple).Index); std::get<1>(Tuple).dump(W.getOStream()); W.getOStream() << '\n'; } } char DWARFDebugNames::SentinelError::ID; std::error_code DWARFDebugNames::SentinelError::convertToErrorCode() const { return inconvertibleErrorCode(); } uint64_t DWARFDebugNames::NameIndex::getCUOffset(uint32_t CU) const { assert(CU < Hdr.CompUnitCount); uint64_t Offset = CUsBase + 4 * CU; return Section.AccelSection.getRelocatedValue(4, &Offset); } uint64_t DWARFDebugNames::NameIndex::getLocalTUOffset(uint32_t TU) const { assert(TU < Hdr.LocalTypeUnitCount); uint64_t Offset = CUsBase + 4 * (Hdr.CompUnitCount + TU); return Section.AccelSection.getRelocatedValue(4, &Offset); } uint64_t DWARFDebugNames::NameIndex::getForeignTUSignature(uint32_t TU) const { assert(TU < Hdr.ForeignTypeUnitCount); uint64_t Offset = CUsBase + 4 * (Hdr.CompUnitCount + Hdr.LocalTypeUnitCount) + 8 * TU; return Section.AccelSection.getU64(&Offset); } Expected DWARFDebugNames::NameIndex::getEntry(uint64_t *Offset) const { const DWARFDataExtractor &AS = Section.AccelSection; if (!AS.isValidOffset(*Offset)) return createStringError(errc::illegal_byte_sequence, "Incorrectly terminated entry list."); uint32_t AbbrevCode = AS.getULEB128(Offset); if (AbbrevCode == 0) return make_error(); const auto AbbrevIt = Abbrevs.find_as(AbbrevCode); if (AbbrevIt == Abbrevs.end()) return createStringError(errc::invalid_argument, "Invalid abbreviation."); Entry E(*this, *AbbrevIt); dwarf::FormParams FormParams = {Hdr.Version, 0, dwarf::DwarfFormat::DWARF32}; for (auto &Value : E.Values) { if (!Value.extractValue(AS, Offset, FormParams)) return createStringError(errc::io_error, "Error extracting index attribute values."); } return std::move(E); } DWARFDebugNames::NameTableEntry DWARFDebugNames::NameIndex::getNameTableEntry(uint32_t Index) const { assert(0 < Index && Index <= Hdr.NameCount); uint64_t StringOffsetOffset = StringOffsetsBase + 4 * (Index - 1); uint64_t EntryOffsetOffset = EntryOffsetsBase + 4 * (Index - 1); const DWARFDataExtractor &AS = Section.AccelSection; uint64_t StringOffset = AS.getRelocatedValue(4, &StringOffsetOffset); uint64_t EntryOffset = AS.getU32(&EntryOffsetOffset); EntryOffset += EntriesBase; return {Section.StringSection, Index, StringOffset, EntryOffset}; } uint32_t DWARFDebugNames::NameIndex::getBucketArrayEntry(uint32_t Bucket) const { assert(Bucket < Hdr.BucketCount); uint64_t BucketOffset = BucketsBase + 4 * Bucket; return Section.AccelSection.getU32(&BucketOffset); } uint32_t DWARFDebugNames::NameIndex::getHashArrayEntry(uint32_t Index) const { assert(0 < Index && Index <= Hdr.NameCount); uint64_t HashOffset = HashesBase + 4 * (Index - 1); return Section.AccelSection.getU32(&HashOffset); } // Returns true if we should continue scanning for entries, false if this is the // last (sentinel) entry). In case of a parsing error we also return false, as // it's not possible to recover this entry list (but the other lists may still // parse OK). bool DWARFDebugNames::NameIndex::dumpEntry(ScopedPrinter &W, uint64_t *Offset) const { uint64_t EntryId = *Offset; auto EntryOr = getEntry(Offset); if (!EntryOr) { handleAllErrors(EntryOr.takeError(), [](const SentinelError &) {}, [&W](const ErrorInfoBase &EI) { EI.log(W.startLine()); }); return false; } DictScope EntryScope(W, ("Entry @ 0x" + Twine::utohexstr(EntryId)).str()); EntryOr->dump(W); return true; } void DWARFDebugNames::NameIndex::dumpName(ScopedPrinter &W, const NameTableEntry &NTE, Optional Hash) const { DictScope NameScope(W, ("Name " + Twine(NTE.getIndex())).str()); if (Hash) W.printHex("Hash", *Hash); W.startLine() << format("String: 0x%08" PRIx64, NTE.getStringOffset()); W.getOStream() << " \"" << NTE.getString() << "\"\n"; uint64_t EntryOffset = NTE.getEntryOffset(); while (dumpEntry(W, &EntryOffset)) /*empty*/; } void DWARFDebugNames::NameIndex::dumpCUs(ScopedPrinter &W) const { ListScope CUScope(W, "Compilation Unit offsets"); for (uint32_t CU = 0; CU < Hdr.CompUnitCount; ++CU) W.startLine() << format("CU[%u]: 0x%08" PRIx64 "\n", CU, getCUOffset(CU)); } void DWARFDebugNames::NameIndex::dumpLocalTUs(ScopedPrinter &W) const { if (Hdr.LocalTypeUnitCount == 0) return; ListScope TUScope(W, "Local Type Unit offsets"); for (uint32_t TU = 0; TU < Hdr.LocalTypeUnitCount; ++TU) W.startLine() << format("LocalTU[%u]: 0x%08" PRIx64 "\n", TU, getLocalTUOffset(TU)); } void DWARFDebugNames::NameIndex::dumpForeignTUs(ScopedPrinter &W) const { if (Hdr.ForeignTypeUnitCount == 0) return; ListScope TUScope(W, "Foreign Type Unit signatures"); for (uint32_t TU = 0; TU < Hdr.ForeignTypeUnitCount; ++TU) { W.startLine() << format("ForeignTU[%u]: 0x%016" PRIx64 "\n", TU, getForeignTUSignature(TU)); } } void DWARFDebugNames::NameIndex::dumpAbbreviations(ScopedPrinter &W) const { ListScope AbbrevsScope(W, "Abbreviations"); for (const auto &Abbr : Abbrevs) Abbr.dump(W); } void DWARFDebugNames::NameIndex::dumpBucket(ScopedPrinter &W, uint32_t Bucket) const { ListScope BucketScope(W, ("Bucket " + Twine(Bucket)).str()); uint32_t Index = getBucketArrayEntry(Bucket); if (Index == 0) { W.printString("EMPTY"); return; } if (Index > Hdr.NameCount) { W.printString("Name index is invalid"); return; } for (; Index <= Hdr.NameCount; ++Index) { uint32_t Hash = getHashArrayEntry(Index); if (Hash % Hdr.BucketCount != Bucket) break; dumpName(W, getNameTableEntry(Index), Hash); } } LLVM_DUMP_METHOD void DWARFDebugNames::NameIndex::dump(ScopedPrinter &W) const { DictScope UnitScope(W, ("Name Index @ 0x" + Twine::utohexstr(Base)).str()); Hdr.dump(W); dumpCUs(W); dumpLocalTUs(W); dumpForeignTUs(W); dumpAbbreviations(W); if (Hdr.BucketCount > 0) { for (uint32_t Bucket = 0; Bucket < Hdr.BucketCount; ++Bucket) dumpBucket(W, Bucket); return; } W.startLine() << "Hash table not present\n"; for (NameTableEntry NTE : *this) dumpName(W, NTE, None); } Error DWARFDebugNames::extract() { uint64_t Offset = 0; while (AccelSection.isValidOffset(Offset)) { NameIndex Next(*this, Offset); if (Error E = Next.extract()) return E; Offset = Next.getNextUnitOffset(); NameIndices.push_back(std::move(Next)); } return Error::success(); } iterator_range DWARFDebugNames::NameIndex::equal_range(StringRef Key) const { return make_range(ValueIterator(*this, Key), ValueIterator()); } LLVM_DUMP_METHOD void DWARFDebugNames::dump(raw_ostream &OS) const { ScopedPrinter W(OS); for (const NameIndex &NI : NameIndices) NI.dump(W); } Optional DWARFDebugNames::ValueIterator::findEntryOffsetInCurrentIndex() { const Header &Hdr = CurrentIndex->Hdr; if (Hdr.BucketCount == 0) { // No Hash Table, We need to search through all names in the Name Index. for (NameTableEntry NTE : *CurrentIndex) { if (NTE.getString() == Key) return NTE.getEntryOffset(); } return None; } // The Name Index has a Hash Table, so use that to speed up the search. // Compute the Key Hash, if it has not been done already. if (!Hash) Hash = caseFoldingDjbHash(Key); uint32_t Bucket = *Hash % Hdr.BucketCount; uint32_t Index = CurrentIndex->getBucketArrayEntry(Bucket); if (Index == 0) return None; // Empty bucket for (; Index <= Hdr.NameCount; ++Index) { uint32_t Hash = CurrentIndex->getHashArrayEntry(Index); if (Hash % Hdr.BucketCount != Bucket) return None; // End of bucket NameTableEntry NTE = CurrentIndex->getNameTableEntry(Index); if (NTE.getString() == Key) return NTE.getEntryOffset(); } return None; } bool DWARFDebugNames::ValueIterator::getEntryAtCurrentOffset() { auto EntryOr = CurrentIndex->getEntry(&DataOffset); if (!EntryOr) { consumeError(EntryOr.takeError()); return false; } CurrentEntry = std::move(*EntryOr); return true; } bool DWARFDebugNames::ValueIterator::findInCurrentIndex() { Optional Offset = findEntryOffsetInCurrentIndex(); if (!Offset) return false; DataOffset = *Offset; return getEntryAtCurrentOffset(); } void DWARFDebugNames::ValueIterator::searchFromStartOfCurrentIndex() { for (const NameIndex *End = CurrentIndex->Section.NameIndices.end(); CurrentIndex != End; ++CurrentIndex) { if (findInCurrentIndex()) return; } setEnd(); } void DWARFDebugNames::ValueIterator::next() { assert(CurrentIndex && "Incrementing an end() iterator?"); // First try the next entry in the current Index. if (getEntryAtCurrentOffset()) return; // If we're a local iterator or we have reached the last Index, we're done. if (IsLocal || CurrentIndex == &CurrentIndex->Section.NameIndices.back()) { setEnd(); return; } // Otherwise, try the next index. ++CurrentIndex; searchFromStartOfCurrentIndex(); } DWARFDebugNames::ValueIterator::ValueIterator(const DWARFDebugNames &AccelTable, StringRef Key) : CurrentIndex(AccelTable.NameIndices.begin()), IsLocal(false), Key(Key) { searchFromStartOfCurrentIndex(); } DWARFDebugNames::ValueIterator::ValueIterator( const DWARFDebugNames::NameIndex &NI, StringRef Key) : CurrentIndex(&NI), IsLocal(true), Key(Key) { if (!findInCurrentIndex()) setEnd(); } iterator_range DWARFDebugNames::equal_range(StringRef Key) const { if (NameIndices.empty()) return make_range(ValueIterator(), ValueIterator()); return make_range(ValueIterator(*this, Key), ValueIterator()); } const DWARFDebugNames::NameIndex * DWARFDebugNames::getCUNameIndex(uint64_t CUOffset) { if (CUToNameIndex.size() == 0 && NameIndices.size() > 0) { for (const auto &NI : *this) { for (uint32_t CU = 0; CU < NI.getCUCount(); ++CU) CUToNameIndex.try_emplace(NI.getCUOffset(CU), &NI); } } return CUToNameIndex.lookup(CUOffset); } binaryen-version_91/third_party/llvm-project/DWARFAddressRange.cpp000066400000000000000000000020101362402614000254510ustar00rootroot00000000000000//===- DWARFDebugAranges.cpp ------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFAddressRange.h" #include "llvm/Support/Format.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; void DWARFAddressRange::dump(raw_ostream &OS, uint32_t AddressSize, DIDumpOptions DumpOpts) const { OS << (DumpOpts.DisplayRawContents ? " " : "["); OS << format("0x%*.*" PRIx64 ", ", AddressSize * 2, AddressSize * 2, LowPC) << format("0x%*.*" PRIx64, AddressSize * 2, AddressSize * 2, HighPC); OS << (DumpOpts.DisplayRawContents ? "" : ")"); } raw_ostream &llvm::operator<<(raw_ostream &OS, const DWARFAddressRange &R) { R.dump(OS, /* AddressSize */ 8); return OS; } binaryen-version_91/third_party/llvm-project/DWARFCompileUnit.cpp000066400000000000000000000031021362402614000253420ustar00rootroot00000000000000//===-- DWARFCompileUnit.cpp ----------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h" #include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h" #include "llvm/DebugInfo/DWARF/DWARFDie.h" #include "llvm/Support/Format.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; void DWARFCompileUnit::dump(raw_ostream &OS, DIDumpOptions DumpOpts) { OS << format("0x%08" PRIx64, getOffset()) << ": Compile Unit:" << " length = " << format("0x%08" PRIx64, getLength()) << " version = " << format("0x%04x", getVersion()); if (getVersion() >= 5) OS << " unit_type = " << dwarf::UnitTypeString(getUnitType()); if (auto* Abbreviations = getAbbreviations()) { // XXX BINARYEN OS << " abbr_offset = " << format("0x%04" PRIx64, Abbreviations->getOffset()); } OS << " addr_size = " << format("0x%02x", getAddressByteSize()); if (getVersion() >= 5 && getUnitType() != dwarf::DW_UT_compile) OS << " DWO_id = " << format("0x%016" PRIx64, *getDWOId()); OS << " (next unit at " << format("0x%08" PRIx64, getNextUnitOffset()) << ")\n"; if (DWARFDie CUDie = getUnitDIE(false)) CUDie.dump(OS, 0, DumpOpts); else OS << "\n\n"; } // VTable anchor. DWARFCompileUnit::~DWARFCompileUnit() = default; binaryen-version_91/third_party/llvm-project/DWARFContext.cpp000066400000000000000000002111741362402614000245500ustar00rootroot00000000000000//===- DWARFContext.cpp ---------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h" #include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h" #include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h" #include "llvm/DebugInfo/DWARF/DWARFDebugAddr.h" #include "llvm/DebugInfo/DWARF/DWARFDebugArangeSet.h" #include "llvm/DebugInfo/DWARF/DWARFDebugAranges.h" #include "llvm/DebugInfo/DWARF/DWARFDebugFrame.h" #include "llvm/DebugInfo/DWARF/DWARFDebugLine.h" #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h" #include "llvm/DebugInfo/DWARF/DWARFDebugMacro.h" #include "llvm/DebugInfo/DWARF/DWARFDebugPubTable.h" #include "llvm/DebugInfo/DWARF/DWARFDebugRangeList.h" #include "llvm/DebugInfo/DWARF/DWARFDebugRnglists.h" #include "llvm/DebugInfo/DWARF/DWARFDie.h" #include "llvm/DebugInfo/DWARF/DWARFFormValue.h" #include "llvm/DebugInfo/DWARF/DWARFGdbIndex.h" #include "llvm/DebugInfo/DWARF/DWARFSection.h" #include "llvm/DebugInfo/DWARF/DWARFUnitIndex.h" #include "llvm/DebugInfo/DWARF/DWARFVerifier.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/Object/Decompressor.h" #include "llvm/Object/MachO.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Object/RelocationResolver.h" #include "llvm/Support/Casting.h" #include "llvm/Support/DataExtractor.h" #include "llvm/Support/Error.h" #include "llvm/Support/Format.h" #include "llvm/Support/LEB128.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include #include #include #include #include #include #include using namespace llvm; using namespace dwarf; using namespace object; #define DEBUG_TYPE "dwarf" using DWARFLineTable = DWARFDebugLine::LineTable; using FileLineInfoKind = DILineInfoSpecifier::FileLineInfoKind; using FunctionNameKind = DILineInfoSpecifier::FunctionNameKind; DWARFContext::DWARFContext(std::unique_ptr DObj, std::string DWPName) : DIContext(CK_DWARF), DWPName(std::move(DWPName)), DObj(std::move(DObj)) {} DWARFContext::~DWARFContext() = default; /// Dump the UUID load command. static void dumpUUID(raw_ostream &OS, const ObjectFile &Obj) { #if 0 // XXX BINARYEN auto *MachO = dyn_cast(&Obj); if (!MachO) return; for (auto LC : MachO->load_commands()) { raw_ostream::uuid_t UUID; if (LC.C.cmd == MachO::LC_UUID) { if (LC.C.cmdsize < sizeof(UUID) + sizeof(LC.C)) { OS << "error: UUID load command is too short.\n"; return; } OS << "UUID: "; memcpy(&UUID, LC.Ptr+sizeof(LC.C), sizeof(UUID)); OS.write_uuid(UUID); Triple T = MachO->getArchTriple(); OS << " (" << T.getArchName() << ')'; OS << ' ' << MachO->getFileName() << '\n'; } } #endif } using ContributionCollection = std::vector>; // Collect all the contributions to the string offsets table from all units, // sort them by their starting offsets and remove duplicates. static ContributionCollection collectContributionData(DWARFContext::unit_iterator_range Units) { ContributionCollection Contributions; for (const auto &U : Units) if (const auto &C = U->getStringOffsetsTableContribution()) Contributions.push_back(C); // Sort the contributions so that any invalid ones are placed at // the start of the contributions vector. This way they are reported // first. llvm::sort(Contributions, [](const Optional &L, const Optional &R) { if (L && R) return L->Base < R->Base; return R.hasValue(); }); // Uniquify contributions, as it is possible that units (specifically // type units in dwo or dwp files) share contributions. We don't want // to report them more than once. Contributions.erase( std::unique(Contributions.begin(), Contributions.end(), [](const Optional &L, const Optional &R) { if (L && R) return L->Base == R->Base && L->Size == R->Size; return false; }), Contributions.end()); return Contributions; } static void dumpDWARFv5StringOffsetsSection( raw_ostream &OS, StringRef SectionName, const DWARFObject &Obj, const DWARFSection &StringOffsetsSection, StringRef StringSection, DWARFContext::unit_iterator_range Units, bool LittleEndian) { auto Contributions = collectContributionData(Units); DWARFDataExtractor StrOffsetExt(Obj, StringOffsetsSection, LittleEndian, 0); DataExtractor StrData(StringSection, LittleEndian, 0); uint64_t SectionSize = StringOffsetsSection.Data.size(); uint64_t Offset = 0; for (auto &Contribution : Contributions) { // Report an ill-formed contribution. if (!Contribution) { OS << "error: invalid contribution to string offsets table in section ." << SectionName << ".\n"; return; } dwarf::DwarfFormat Format = Contribution->getFormat(); uint16_t Version = Contribution->getVersion(); uint64_t ContributionHeader = Contribution->Base; // In DWARF v5 there is a contribution header that immediately precedes // the string offsets base (the location we have previously retrieved from // the CU DIE's DW_AT_str_offsets attribute). The header is located either // 8 or 16 bytes before the base, depending on the contribution's format. if (Version >= 5) ContributionHeader -= Format == DWARF32 ? 8 : 16; // Detect overlapping contributions. if (Offset > ContributionHeader) { WithColor::error() << "overlapping contributions to string offsets table in section ." << SectionName << ".\n"; return; } // Report a gap in the table. if (Offset < ContributionHeader) { OS << format("0x%8.8" PRIx64 ": Gap, length = ", Offset); OS << (ContributionHeader - Offset) << "\n"; } OS << format("0x%8.8" PRIx64 ": ", ContributionHeader); // In DWARF v5 the contribution size in the descriptor does not equal // the originally encoded length (it does not contain the length of the // version field and the padding, a total of 4 bytes). Add them back in // for reporting. OS << "Contribution size = " << (Contribution->Size + (Version < 5 ? 0 : 4)) << ", Format = " << (Format == DWARF32 ? "DWARF32" : "DWARF64") << ", Version = " << Version << "\n"; Offset = Contribution->Base; unsigned EntrySize = Contribution->getDwarfOffsetByteSize(); while (Offset - Contribution->Base < Contribution->Size) { OS << format("0x%8.8" PRIx64 ": ", Offset); uint64_t StringOffset = StrOffsetExt.getRelocatedValue(EntrySize, &Offset); OS << format("%8.8" PRIx64 " ", StringOffset); const char *S = StrData.getCStr(&StringOffset); if (S) OS << format("\"%s\"", S); OS << "\n"; } } // Report a gap at the end of the table. if (Offset < SectionSize) { OS << format("0x%8.8" PRIx64 ": Gap, length = ", Offset); OS << (SectionSize - Offset) << "\n"; } } // Dump a DWARF string offsets section. This may be a DWARF v5 formatted // string offsets section, where each compile or type unit contributes a // number of entries (string offsets), with each contribution preceded by // a header containing size and version number. Alternatively, it may be a // monolithic series of string offsets, as generated by the pre-DWARF v5 // implementation of split DWARF. static void dumpStringOffsetsSection(raw_ostream &OS, StringRef SectionName, const DWARFObject &Obj, const DWARFSection &StringOffsetsSection, StringRef StringSection, DWARFContext::unit_iterator_range Units, bool LittleEndian, unsigned MaxVersion) { // If we have at least one (compile or type) unit with DWARF v5 or greater, // we assume that the section is formatted like a DWARF v5 string offsets // section. if (MaxVersion >= 5) dumpDWARFv5StringOffsetsSection(OS, SectionName, Obj, StringOffsetsSection, StringSection, Units, LittleEndian); else { DataExtractor strOffsetExt(StringOffsetsSection.Data, LittleEndian, 0); uint64_t offset = 0; uint64_t size = StringOffsetsSection.Data.size(); // Ensure that size is a multiple of the size of an entry. if (size & ((uint64_t)(sizeof(uint32_t) - 1))) { OS << "error: size of ." << SectionName << " is not a multiple of " << sizeof(uint32_t) << ".\n"; size &= -(uint64_t)sizeof(uint32_t); } DataExtractor StrData(StringSection, LittleEndian, 0); while (offset < size) { OS << format("0x%8.8" PRIx64 ": ", offset); uint64_t StringOffset = strOffsetExt.getU32(&offset); OS << format("%8.8" PRIx64 " ", StringOffset); const char *S = StrData.getCStr(&StringOffset); if (S) OS << format("\"%s\"", S); OS << "\n"; } } } // Dump the .debug_addr section. static void dumpAddrSection(raw_ostream &OS, DWARFDataExtractor &AddrData, DIDumpOptions DumpOpts, uint16_t Version, uint8_t AddrSize) { uint64_t Offset = 0; while (AddrData.isValidOffset(Offset)) { DWARFDebugAddrTable AddrTable; uint64_t TableOffset = Offset; if (Error Err = AddrTable.extract(AddrData, &Offset, Version, AddrSize, DWARFContext::dumpWarning)) { WithColor::error() << toString(std::move(Err)) << '\n'; // Keep going after an error, if we can, assuming that the length field // could be read. If it couldn't, stop reading the section. if (!AddrTable.hasValidLength()) break; Offset = TableOffset + AddrTable.getLength(); } else { AddrTable.dump(OS, DumpOpts); } } } // Dump the .debug_rnglists or .debug_rnglists.dwo section (DWARF v5). static void dumpRnglistsSection( raw_ostream &OS, DWARFDataExtractor &rnglistData, llvm::function_ref(uint32_t)> LookupPooledAddress, DIDumpOptions DumpOpts) { uint64_t Offset = 0; while (rnglistData.isValidOffset(Offset)) { llvm::DWARFDebugRnglistTable Rnglists; uint64_t TableOffset = Offset; if (Error Err = Rnglists.extract(rnglistData, &Offset)) { WithColor::error() << toString(std::move(Err)) << '\n'; uint64_t Length = Rnglists.length(); // Keep going after an error, if we can, assuming that the length field // could be read. If it couldn't, stop reading the section. if (Length == 0) break; Offset = TableOffset + Length; } else { Rnglists.dump(OS, LookupPooledAddress, DumpOpts); } } } static void dumpLoclistsSection(raw_ostream &OS, DIDumpOptions DumpOpts, DWARFDataExtractor Data, const MCRegisterInfo *MRI, Optional DumpOffset) { uint64_t Offset = 0; while (Data.isValidOffset(Offset)) { DWARFListTableHeader Header(".debug_loclists", "locations"); if (Error E = Header.extract(Data, &Offset)) { WithColor::error() << toString(std::move(E)) << '\n'; return; } Header.dump(OS, DumpOpts); uint64_t EndOffset = Header.length() + Header.getHeaderOffset(); Data.setAddressSize(Header.getAddrSize()); if (DumpOffset) { if (DumpOffset >= Offset && DumpOffset < EndOffset) { Offset = *DumpOffset; DWARFDebugLoclists::dumpLocationList(Data, &Offset, Header.getVersion(), OS, /*BaseAddr=*/0, MRI, nullptr, DumpOpts, /*Indent=*/0); OS << "\n"; return; } } else { DWARFDebugLoclists::dumpRange(Data, Offset, EndOffset - Offset, Header.getVersion(), OS, 0, MRI, DumpOpts); } Offset = EndOffset; } } void DWARFContext::dump( raw_ostream &OS, DIDumpOptions DumpOpts, std::array, DIDT_ID_Count> DumpOffsets) { uint64_t DumpType = DumpOpts.DumpType; StringRef Extension = sys::path::extension(DObj->getFileName()); bool IsDWO = (Extension == ".dwo") || (Extension == ".dwp"); #if 0 // XXX BINARYEN // Print UUID header. const auto *ObjFile = DObj->getFile(); if (DumpType & DIDT_UUID) dumpUUID(OS, *ObjFile); #endif // Print a header for each explicitly-requested section. // Otherwise just print one for non-empty sections. // Only print empty .dwo section headers when dumping a .dwo file. bool Explicit = DumpType != DIDT_All && !IsDWO; bool ExplicitDWO = Explicit && IsDWO; auto shouldDump = [&](bool Explicit, const char *Name, unsigned ID, StringRef Section) -> Optional * { unsigned Mask = 1U << ID; bool Should = (DumpType & Mask) && (Explicit || !Section.empty()); if (!Should) return nullptr; OS << "\n" << Name << " contents:\n"; return &DumpOffsets[ID]; }; // Dump individual sections. if (shouldDump(Explicit, ".debug_abbrev", DIDT_ID_DebugAbbrev, DObj->getAbbrevSection())) getDebugAbbrev()->dump(OS); if (shouldDump(ExplicitDWO, ".debug_abbrev.dwo", DIDT_ID_DebugAbbrev, DObj->getAbbrevDWOSection())) getDebugAbbrevDWO()->dump(OS); auto dumpDebugInfo = [&](const char *Name, unit_iterator_range Units) { OS << '\n' << Name << " contents:\n"; if (auto DumpOffset = DumpOffsets[DIDT_ID_DebugInfo]) for (const auto &U : Units) U->getDIEForOffset(DumpOffset.getValue()) .dump(OS, 0, DumpOpts.noImplicitRecursion()); else for (const auto &U : Units) U->dump(OS, DumpOpts); }; if ((DumpType & DIDT_DebugInfo)) { if (Explicit || getNumCompileUnits()) dumpDebugInfo(".debug_info", info_section_units()); if (ExplicitDWO || getNumDWOCompileUnits()) dumpDebugInfo(".debug_info.dwo", dwo_info_section_units()); } auto dumpDebugType = [&](const char *Name, unit_iterator_range Units) { OS << '\n' << Name << " contents:\n"; for (const auto &U : Units) if (auto DumpOffset = DumpOffsets[DIDT_ID_DebugTypes]) U->getDIEForOffset(*DumpOffset) .dump(OS, 0, DumpOpts.noImplicitRecursion()); else U->dump(OS, DumpOpts); }; if ((DumpType & DIDT_DebugTypes)) { if (Explicit || getNumTypeUnits()) dumpDebugType(".debug_types", types_section_units()); if (ExplicitDWO || getNumDWOTypeUnits()) dumpDebugType(".debug_types.dwo", dwo_types_section_units()); } if (const auto *Off = shouldDump(Explicit, ".debug_loc", DIDT_ID_DebugLoc, DObj->getLocSection().Data)) { getDebugLoc()->dump(OS, getRegisterInfo(), DumpOpts, *Off); } if (const auto *Off = shouldDump(Explicit, ".debug_loclists", DIDT_ID_DebugLoclists, DObj->getLoclistsSection().Data)) { DWARFDataExtractor Data(*DObj, DObj->getLoclistsSection(), isLittleEndian(), 0); dumpLoclistsSection(OS, DumpOpts, Data, getRegisterInfo(), *Off); } if (const auto *Off = shouldDump(ExplicitDWO, ".debug_loc.dwo", DIDT_ID_DebugLoc, DObj->getLocDWOSection().Data)) { DWARFDataExtractor Data(*DObj, DObj->getLocDWOSection(), isLittleEndian(), 4); if (*Off) { uint64_t Offset = **Off; DWARFDebugLoclists::dumpLocationList(Data, &Offset, /*Version=*/4, OS, /*BaseAddr=*/0, getRegisterInfo(), nullptr, DumpOpts, /*Indent=*/0); OS << "\n"; } else { DWARFDebugLoclists::dumpRange(Data, 0, Data.getData().size(), /*Version=*/4, OS, /*BaseAddr=*/0, getRegisterInfo(), DumpOpts); } } if (const auto *Off = shouldDump(Explicit, ".debug_frame", DIDT_ID_DebugFrame, DObj->getFrameSection().Data)) getDebugFrame()->dump(OS, getRegisterInfo(), *Off); if (const auto *Off = shouldDump(Explicit, ".eh_frame", DIDT_ID_DebugFrame, DObj->getEHFrameSection().Data)) getEHFrame()->dump(OS, getRegisterInfo(), *Off); if (DumpType & DIDT_DebugMacro) { if (Explicit || !getDebugMacro()->empty()) { OS << "\n.debug_macinfo contents:\n"; getDebugMacro()->dump(OS); } } if (shouldDump(Explicit, ".debug_aranges", DIDT_ID_DebugAranges, DObj->getArangesSection())) { uint64_t offset = 0; DataExtractor arangesData(DObj->getArangesSection(), isLittleEndian(), 0); DWARFDebugArangeSet set; while (set.extract(arangesData, &offset)) set.dump(OS); } auto DumpLineSection = [&](DWARFDebugLine::SectionParser Parser, DIDumpOptions DumpOpts, Optional DumpOffset) { while (!Parser.done()) { if (DumpOffset && Parser.getOffset() != *DumpOffset) { Parser.skip(dumpWarning); continue; } OS << "debug_line[" << format("0x%8.8" PRIx64, Parser.getOffset()) << "]\n"; if (DumpOpts.Verbose) { Parser.parseNext(dumpWarning, dumpWarning, &OS); } else { DWARFDebugLine::LineTable LineTable = Parser.parseNext(dumpWarning, dumpWarning); LineTable.dump(OS, DumpOpts); } } }; if (const auto *Off = shouldDump(Explicit, ".debug_line", DIDT_ID_DebugLine, DObj->getLineSection().Data)) { DWARFDataExtractor LineData(*DObj, DObj->getLineSection(), isLittleEndian(), 0); DWARFDebugLine::SectionParser Parser(LineData, *this, compile_units(), type_units()); DumpLineSection(Parser, DumpOpts, *Off); } if (const auto *Off = shouldDump(ExplicitDWO, ".debug_line.dwo", DIDT_ID_DebugLine, DObj->getLineDWOSection().Data)) { DWARFDataExtractor LineData(*DObj, DObj->getLineDWOSection(), isLittleEndian(), 0); DWARFDebugLine::SectionParser Parser(LineData, *this, dwo_compile_units(), dwo_type_units()); DumpLineSection(Parser, DumpOpts, *Off); } if (shouldDump(Explicit, ".debug_cu_index", DIDT_ID_DebugCUIndex, DObj->getCUIndexSection())) { getCUIndex().dump(OS); } if (shouldDump(Explicit, ".debug_tu_index", DIDT_ID_DebugTUIndex, DObj->getTUIndexSection())) { getTUIndex().dump(OS); } if (shouldDump(Explicit, ".debug_str", DIDT_ID_DebugStr, DObj->getStrSection())) { DataExtractor strData(DObj->getStrSection(), isLittleEndian(), 0); uint64_t offset = 0; uint64_t strOffset = 0; while (const char *s = strData.getCStr(&offset)) { OS << format("0x%8.8" PRIx64 ": \"%s\"\n", strOffset, s); strOffset = offset; } } if (shouldDump(ExplicitDWO, ".debug_str.dwo", DIDT_ID_DebugStr, DObj->getStrDWOSection())) { DataExtractor strDWOData(DObj->getStrDWOSection(), isLittleEndian(), 0); uint64_t offset = 0; uint64_t strDWOOffset = 0; while (const char *s = strDWOData.getCStr(&offset)) { OS << format("0x%8.8" PRIx64 ": \"%s\"\n", strDWOOffset, s); strDWOOffset = offset; } } if (shouldDump(Explicit, ".debug_line_str", DIDT_ID_DebugLineStr, DObj->getLineStrSection())) { DataExtractor strData(DObj->getLineStrSection(), isLittleEndian(), 0); uint64_t offset = 0; uint64_t strOffset = 0; while (const char *s = strData.getCStr(&offset)) { OS << format("0x%8.8" PRIx64 ": \"", strOffset); OS.write_escaped(s); OS << "\"\n"; strOffset = offset; } } if (shouldDump(Explicit, ".debug_addr", DIDT_ID_DebugAddr, DObj->getAddrSection().Data)) { DWARFDataExtractor AddrData(*DObj, DObj->getAddrSection(), isLittleEndian(), 0); dumpAddrSection(OS, AddrData, DumpOpts, getMaxVersion(), getCUAddrSize()); } if (shouldDump(Explicit, ".debug_ranges", DIDT_ID_DebugRanges, DObj->getRangesSection().Data)) { uint8_t savedAddressByteSize = getCUAddrSize(); DWARFDataExtractor rangesData(*DObj, DObj->getRangesSection(), isLittleEndian(), savedAddressByteSize); uint64_t offset = 0; DWARFDebugRangeList rangeList; while (rangesData.isValidOffset(offset)) { if (Error E = rangeList.extract(rangesData, &offset)) { WithColor::error() << toString(std::move(E)) << '\n'; break; } rangeList.dump(OS); } } auto LookupPooledAddress = [&](uint32_t Index) -> Optional { const auto &CUs = compile_units(); auto I = CUs.begin(); if (I == CUs.end()) return None; return (*I)->getAddrOffsetSectionItem(Index); }; if (shouldDump(Explicit, ".debug_rnglists", DIDT_ID_DebugRnglists, DObj->getRnglistsSection().Data)) { DWARFDataExtractor RnglistData(*DObj, DObj->getRnglistsSection(), isLittleEndian(), 0); dumpRnglistsSection(OS, RnglistData, LookupPooledAddress, DumpOpts); } if (shouldDump(ExplicitDWO, ".debug_rnglists.dwo", DIDT_ID_DebugRnglists, DObj->getRnglistsDWOSection().Data)) { DWARFDataExtractor RnglistData(*DObj, DObj->getRnglistsDWOSection(), isLittleEndian(), 0); dumpRnglistsSection(OS, RnglistData, LookupPooledAddress, DumpOpts); } if (shouldDump(Explicit, ".debug_pubnames", DIDT_ID_DebugPubnames, DObj->getPubnamesSection().Data)) DWARFDebugPubTable(*DObj, DObj->getPubnamesSection(), isLittleEndian(), false) .dump(OS); if (shouldDump(Explicit, ".debug_pubtypes", DIDT_ID_DebugPubtypes, DObj->getPubtypesSection().Data)) DWARFDebugPubTable(*DObj, DObj->getPubtypesSection(), isLittleEndian(), false) .dump(OS); if (shouldDump(Explicit, ".debug_gnu_pubnames", DIDT_ID_DebugGnuPubnames, DObj->getGnuPubnamesSection().Data)) DWARFDebugPubTable(*DObj, DObj->getGnuPubnamesSection(), isLittleEndian(), true /* GnuStyle */) .dump(OS); if (shouldDump(Explicit, ".debug_gnu_pubtypes", DIDT_ID_DebugGnuPubtypes, DObj->getGnuPubtypesSection().Data)) DWARFDebugPubTable(*DObj, DObj->getGnuPubtypesSection(), isLittleEndian(), true /* GnuStyle */) .dump(OS); if (shouldDump(Explicit, ".debug_str_offsets", DIDT_ID_DebugStrOffsets, DObj->getStrOffsetsSection().Data)) dumpStringOffsetsSection(OS, "debug_str_offsets", *DObj, DObj->getStrOffsetsSection(), DObj->getStrSection(), normal_units(), isLittleEndian(), getMaxVersion()); if (shouldDump(ExplicitDWO, ".debug_str_offsets.dwo", DIDT_ID_DebugStrOffsets, DObj->getStrOffsetsDWOSection().Data)) dumpStringOffsetsSection(OS, "debug_str_offsets.dwo", *DObj, DObj->getStrOffsetsDWOSection(), DObj->getStrDWOSection(), dwo_units(), isLittleEndian(), getMaxDWOVersion()); if (shouldDump(Explicit, ".gdb_index", DIDT_ID_GdbIndex, DObj->getGdbIndexSection())) { getGdbIndex().dump(OS); } if (shouldDump(Explicit, ".apple_names", DIDT_ID_AppleNames, DObj->getAppleNamesSection().Data)) getAppleNames().dump(OS); if (shouldDump(Explicit, ".apple_types", DIDT_ID_AppleTypes, DObj->getAppleTypesSection().Data)) getAppleTypes().dump(OS); if (shouldDump(Explicit, ".apple_namespaces", DIDT_ID_AppleNamespaces, DObj->getAppleNamespacesSection().Data)) getAppleNamespaces().dump(OS); if (shouldDump(Explicit, ".apple_objc", DIDT_ID_AppleObjC, DObj->getAppleObjCSection().Data)) getAppleObjC().dump(OS); if (shouldDump(Explicit, ".debug_names", DIDT_ID_DebugNames, DObj->getNamesSection().Data)) getDebugNames().dump(OS); } DWARFCompileUnit *DWARFContext::getDWOCompileUnitForHash(uint64_t Hash) { parseDWOUnits(LazyParse); if (const auto &CUI = getCUIndex()) { if (const auto *R = CUI.getFromHash(Hash)) return dyn_cast_or_null( DWOUnits.getUnitForIndexEntry(*R)); return nullptr; } // If there's no index, just search through the CUs in the DWO - there's // probably only one unless this is something like LTO - though an in-process // built/cached lookup table could be used in that case to improve repeated // lookups of different CUs in the DWO. for (const auto &DWOCU : dwo_compile_units()) { // Might not have parsed DWO ID yet. if (!DWOCU->getDWOId()) { if (Optional DWOId = toUnsigned(DWOCU->getUnitDIE().find(DW_AT_GNU_dwo_id))) DWOCU->setDWOId(*DWOId); else // No DWO ID? continue; } if (DWOCU->getDWOId() == Hash) return dyn_cast(DWOCU.get()); } return nullptr; } DWARFDie DWARFContext::getDIEForOffset(uint64_t Offset) { parseNormalUnits(); if (auto *CU = NormalUnits.getUnitForOffset(Offset)) return CU->getDIEForOffset(Offset); return DWARFDie(); } bool DWARFContext::verify(raw_ostream &OS, DIDumpOptions DumpOpts) { bool Success = true; DWARFVerifier verifier(OS, *this, DumpOpts); Success &= verifier.handleDebugAbbrev(); if (DumpOpts.DumpType & DIDT_DebugInfo) Success &= verifier.handleDebugInfo(); if (DumpOpts.DumpType & DIDT_DebugLine) Success &= verifier.handleDebugLine(); Success &= verifier.handleAccelTables(); return Success; } const DWARFUnitIndex &DWARFContext::getCUIndex() { if (CUIndex) return *CUIndex; DataExtractor CUIndexData(DObj->getCUIndexSection(), isLittleEndian(), 0); CUIndex = std::make_unique(DW_SECT_INFO); CUIndex->parse(CUIndexData); return *CUIndex; } const DWARFUnitIndex &DWARFContext::getTUIndex() { if (TUIndex) return *TUIndex; DataExtractor TUIndexData(DObj->getTUIndexSection(), isLittleEndian(), 0); TUIndex = std::make_unique(DW_SECT_TYPES); TUIndex->parse(TUIndexData); return *TUIndex; } DWARFGdbIndex &DWARFContext::getGdbIndex() { if (GdbIndex) return *GdbIndex; DataExtractor GdbIndexData(DObj->getGdbIndexSection(), true /*LE*/, 0); GdbIndex = std::make_unique(); GdbIndex->parse(GdbIndexData); return *GdbIndex; } const DWARFDebugAbbrev *DWARFContext::getDebugAbbrev() { if (Abbrev) return Abbrev.get(); DataExtractor abbrData(DObj->getAbbrevSection(), isLittleEndian(), 0); Abbrev.reset(new DWARFDebugAbbrev()); Abbrev->extract(abbrData); return Abbrev.get(); } const DWARFDebugAbbrev *DWARFContext::getDebugAbbrevDWO() { if (AbbrevDWO) return AbbrevDWO.get(); DataExtractor abbrData(DObj->getAbbrevDWOSection(), isLittleEndian(), 0); AbbrevDWO.reset(new DWARFDebugAbbrev()); AbbrevDWO->extract(abbrData); return AbbrevDWO.get(); } const DWARFDebugLoc *DWARFContext::getDebugLoc() { if (Loc) return Loc.get(); Loc.reset(new DWARFDebugLoc); // Assume all units have the same address byte size. if (getNumCompileUnits()) { DWARFDataExtractor LocData(*DObj, DObj->getLocSection(), isLittleEndian(), getUnitAtIndex(0)->getAddressByteSize()); Loc->parse(LocData); } return Loc.get(); } const DWARFDebugAranges *DWARFContext::getDebugAranges() { if (Aranges) return Aranges.get(); Aranges.reset(new DWARFDebugAranges()); Aranges->generate(this); return Aranges.get(); } const DWARFDebugFrame *DWARFContext::getDebugFrame() { if (DebugFrame) return DebugFrame.get(); // There's a "bug" in the DWARFv3 standard with respect to the target address // size within debug frame sections. While DWARF is supposed to be independent // of its container, FDEs have fields with size being "target address size", // which isn't specified in DWARF in general. It's only specified for CUs, but // .eh_frame can appear without a .debug_info section. Follow the example of // other tools (libdwarf) and extract this from the container (ObjectFile // provides this information). This problem is fixed in DWARFv4 // See this dwarf-discuss discussion for more details: // http://lists.dwarfstd.org/htdig.cgi/dwarf-discuss-dwarfstd.org/2011-December/001173.html DWARFDataExtractor debugFrameData(*DObj, DObj->getFrameSection(), isLittleEndian(), DObj->getAddressSize()); DebugFrame.reset(new DWARFDebugFrame(getArch(), false /* IsEH */)); DebugFrame->parse(debugFrameData); return DebugFrame.get(); } const DWARFDebugFrame *DWARFContext::getEHFrame() { if (EHFrame) return EHFrame.get(); DWARFDataExtractor debugFrameData(*DObj, DObj->getEHFrameSection(), isLittleEndian(), DObj->getAddressSize()); DebugFrame.reset(new DWARFDebugFrame(getArch(), true /* IsEH */)); DebugFrame->parse(debugFrameData); return DebugFrame.get(); } const DWARFDebugMacro *DWARFContext::getDebugMacro() { if (Macro) return Macro.get(); DataExtractor MacinfoData(DObj->getMacinfoSection(), isLittleEndian(), 0); Macro.reset(new DWARFDebugMacro()); Macro->parse(MacinfoData); return Macro.get(); } template static T &getAccelTable(std::unique_ptr &Cache, const DWARFObject &Obj, const DWARFSection &Section, StringRef StringSection, bool IsLittleEndian) { if (Cache) return *Cache; DWARFDataExtractor AccelSection(Obj, Section, IsLittleEndian, 0); DataExtractor StrData(StringSection, IsLittleEndian, 0); Cache.reset(new T(AccelSection, StrData)); if (Error E = Cache->extract()) llvm::consumeError(std::move(E)); return *Cache; } const DWARFDebugNames &DWARFContext::getDebugNames() { return getAccelTable(Names, *DObj, DObj->getNamesSection(), DObj->getStrSection(), isLittleEndian()); } const AppleAcceleratorTable &DWARFContext::getAppleNames() { return getAccelTable(AppleNames, *DObj, DObj->getAppleNamesSection(), DObj->getStrSection(), isLittleEndian()); } const AppleAcceleratorTable &DWARFContext::getAppleTypes() { return getAccelTable(AppleTypes, *DObj, DObj->getAppleTypesSection(), DObj->getStrSection(), isLittleEndian()); } const AppleAcceleratorTable &DWARFContext::getAppleNamespaces() { return getAccelTable(AppleNamespaces, *DObj, DObj->getAppleNamespacesSection(), DObj->getStrSection(), isLittleEndian()); } const AppleAcceleratorTable &DWARFContext::getAppleObjC() { return getAccelTable(AppleObjC, *DObj, DObj->getAppleObjCSection(), DObj->getStrSection(), isLittleEndian()); } const DWARFDebugLine::LineTable * DWARFContext::getLineTableForUnit(DWARFUnit *U) { Expected ExpectedLineTable = getLineTableForUnit(U, dumpWarning); if (!ExpectedLineTable) { dumpWarning(ExpectedLineTable.takeError()); return nullptr; } return *ExpectedLineTable; } Expected DWARFContext::getLineTableForUnit( DWARFUnit *U, std::function RecoverableErrorCallback) { if (!Line) Line.reset(new DWARFDebugLine); auto UnitDIE = U->getUnitDIE(); if (!UnitDIE) return nullptr; auto Offset = toSectionOffset(UnitDIE.find(DW_AT_stmt_list)); if (!Offset) return nullptr; // No line table for this compile unit. uint64_t stmtOffset = *Offset + U->getLineTableOffset(); // See if the line table is cached. if (const DWARFLineTable *lt = Line->getLineTable(stmtOffset)) return lt; // Make sure the offset is good before we try to parse. if (stmtOffset >= U->getLineSection().Data.size()) return nullptr; // We have to parse it first. DWARFDataExtractor lineData(*DObj, U->getLineSection(), isLittleEndian(), U->getAddressByteSize()); return Line->getOrParseLineTable(lineData, stmtOffset, *this, U, RecoverableErrorCallback); } void DWARFContext::parseNormalUnits() { if (!NormalUnits.empty()) return; DObj->forEachInfoSections([&](const DWARFSection &S) { NormalUnits.addUnitsForSection(*this, S, DW_SECT_INFO); }); NormalUnits.finishedInfoUnits(); DObj->forEachTypesSections([&](const DWARFSection &S) { NormalUnits.addUnitsForSection(*this, S, DW_SECT_TYPES); }); } void DWARFContext::parseDWOUnits(bool Lazy) { if (!DWOUnits.empty()) return; DObj->forEachInfoDWOSections([&](const DWARFSection &S) { DWOUnits.addUnitsForDWOSection(*this, S, DW_SECT_INFO, Lazy); }); DWOUnits.finishedInfoUnits(); DObj->forEachTypesDWOSections([&](const DWARFSection &S) { DWOUnits.addUnitsForDWOSection(*this, S, DW_SECT_TYPES, Lazy); }); } DWARFCompileUnit *DWARFContext::getCompileUnitForOffset(uint64_t Offset) { parseNormalUnits(); return dyn_cast_or_null( NormalUnits.getUnitForOffset(Offset)); } DWARFCompileUnit *DWARFContext::getCompileUnitForAddress(uint64_t Address) { // First, get the offset of the compile unit. uint64_t CUOffset = getDebugAranges()->findAddress(Address); // Retrieve the compile unit. return getCompileUnitForOffset(CUOffset); } DWARFContext::DIEsForAddress DWARFContext::getDIEsForAddress(uint64_t Address) { DIEsForAddress Result; DWARFCompileUnit *CU = getCompileUnitForAddress(Address); if (!CU) return Result; Result.CompileUnit = CU; Result.FunctionDIE = CU->getSubroutineForAddress(Address); std::vector Worklist; Worklist.push_back(Result.FunctionDIE); while (!Worklist.empty()) { DWARFDie DIE = Worklist.back(); Worklist.pop_back(); if (!DIE.isValid()) continue; if (DIE.getTag() == DW_TAG_lexical_block && DIE.addressRangeContainsAddress(Address)) { Result.BlockDIE = DIE; break; } for (auto Child : DIE) Worklist.push_back(Child); } return Result; } /// TODO: change input parameter from "uint64_t Address" /// into "SectionedAddress Address" static bool getFunctionNameAndStartLineForAddress(DWARFCompileUnit *CU, uint64_t Address, FunctionNameKind Kind, std::string &FunctionName, uint32_t &StartLine) { // The address may correspond to instruction in some inlined function, // so we have to build the chain of inlined functions and take the // name of the topmost function in it. SmallVector InlinedChain; CU->getInlinedChainForAddress(Address, InlinedChain); if (InlinedChain.empty()) return false; const DWARFDie &DIE = InlinedChain[0]; bool FoundResult = false; const char *Name = nullptr; if (Kind != FunctionNameKind::None && (Name = DIE.getSubroutineName(Kind))) { FunctionName = Name; FoundResult = true; } if (auto DeclLineResult = DIE.getDeclLine()) { StartLine = DeclLineResult; FoundResult = true; } return FoundResult; } static Optional getTypeSize(DWARFDie Type, uint64_t PointerSize) { if (auto SizeAttr = Type.find(DW_AT_byte_size)) if (Optional Size = SizeAttr->getAsUnsignedConstant()) return Size; switch (Type.getTag()) { case DW_TAG_pointer_type: case DW_TAG_reference_type: case DW_TAG_rvalue_reference_type: return PointerSize; case DW_TAG_ptr_to_member_type: { if (DWARFDie BaseType = Type.getAttributeValueAsReferencedDie(DW_AT_type)) if (BaseType.getTag() == DW_TAG_subroutine_type) return 2 * PointerSize; return PointerSize; } case DW_TAG_const_type: case DW_TAG_volatile_type: case DW_TAG_restrict_type: case DW_TAG_typedef: { if (DWARFDie BaseType = Type.getAttributeValueAsReferencedDie(DW_AT_type)) return getTypeSize(BaseType, PointerSize); break; } case DW_TAG_array_type: { DWARFDie BaseType = Type.getAttributeValueAsReferencedDie(DW_AT_type); if (!BaseType) return Optional(); Optional BaseSize = getTypeSize(BaseType, PointerSize); if (!BaseSize) return Optional(); uint64_t Size = *BaseSize; for (DWARFDie Child : Type) { if (Child.getTag() != DW_TAG_subrange_type) continue; if (auto ElemCountAttr = Child.find(DW_AT_count)) if (Optional ElemCount = ElemCountAttr->getAsUnsignedConstant()) Size *= *ElemCount; if (auto UpperBoundAttr = Child.find(DW_AT_upper_bound)) if (Optional UpperBound = UpperBoundAttr->getAsSignedConstant()) { int64_t LowerBound = 0; if (auto LowerBoundAttr = Child.find(DW_AT_lower_bound)) LowerBound = LowerBoundAttr->getAsSignedConstant().getValueOr(0); Size *= *UpperBound - LowerBound + 1; } } return Size; } default: break; } return Optional(); } void DWARFContext::addLocalsForDie(DWARFCompileUnit *CU, DWARFDie Subprogram, DWARFDie Die, std::vector &Result) { if (Die.getTag() == DW_TAG_variable || Die.getTag() == DW_TAG_formal_parameter) { DILocal Local; if (auto NameAttr = Subprogram.find(DW_AT_name)) if (Optional Name = NameAttr->getAsCString()) Local.FunctionName = *Name; if (auto LocationAttr = Die.find(DW_AT_location)) if (Optional> Location = LocationAttr->getAsBlock()) if (!Location->empty() && (*Location)[0] == DW_OP_fbreg) Local.FrameOffset = decodeSLEB128(Location->data() + 1, nullptr, Location->end()); if (auto TagOffsetAttr = Die.find(DW_AT_LLVM_tag_offset)) Local.TagOffset = TagOffsetAttr->getAsUnsignedConstant(); if (auto Origin = Die.getAttributeValueAsReferencedDie(DW_AT_abstract_origin)) Die = Origin; if (auto NameAttr = Die.find(DW_AT_name)) if (Optional Name = NameAttr->getAsCString()) Local.Name = *Name; if (auto Type = Die.getAttributeValueAsReferencedDie(DW_AT_type)) Local.Size = getTypeSize(Type, getCUAddrSize()); if (auto DeclFileAttr = Die.find(DW_AT_decl_file)) { if (const auto *LT = CU->getContext().getLineTableForUnit(CU)) LT->getFileNameByIndex( DeclFileAttr->getAsUnsignedConstant().getValue(), CU->getCompilationDir(), DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, Local.DeclFile); } if (auto DeclLineAttr = Die.find(DW_AT_decl_line)) Local.DeclLine = DeclLineAttr->getAsUnsignedConstant().getValue(); Result.push_back(Local); return; } if (Die.getTag() == DW_TAG_inlined_subroutine) if (auto Origin = Die.getAttributeValueAsReferencedDie(DW_AT_abstract_origin)) Subprogram = Origin; for (auto Child : Die) addLocalsForDie(CU, Subprogram, Child, Result); } std::vector DWARFContext::getLocalsForAddress(object::SectionedAddress Address) { std::vector Result; DWARFCompileUnit *CU = getCompileUnitForAddress(Address.Address); if (!CU) return Result; DWARFDie Subprogram = CU->getSubroutineForAddress(Address.Address); if (Subprogram.isValid()) addLocalsForDie(CU, Subprogram, Subprogram, Result); return Result; } DILineInfo DWARFContext::getLineInfoForAddress(object::SectionedAddress Address, DILineInfoSpecifier Spec) { DILineInfo Result; DWARFCompileUnit *CU = getCompileUnitForAddress(Address.Address); if (!CU) return Result; getFunctionNameAndStartLineForAddress(CU, Address.Address, Spec.FNKind, Result.FunctionName, Result.StartLine); if (Spec.FLIKind != FileLineInfoKind::None) { if (const DWARFLineTable *LineTable = getLineTableForUnit(CU)) { LineTable->getFileLineInfoForAddress( {Address.Address, Address.SectionIndex}, CU->getCompilationDir(), Spec.FLIKind, Result); } } return Result; } DILineInfoTable DWARFContext::getLineInfoForAddressRange( object::SectionedAddress Address, uint64_t Size, DILineInfoSpecifier Spec) { DILineInfoTable Lines; DWARFCompileUnit *CU = getCompileUnitForAddress(Address.Address); if (!CU) return Lines; uint32_t StartLine = 0; std::string FunctionName(DILineInfo::BadString); getFunctionNameAndStartLineForAddress(CU, Address.Address, Spec.FNKind, FunctionName, StartLine); // If the Specifier says we don't need FileLineInfo, just // return the top-most function at the starting address. if (Spec.FLIKind == FileLineInfoKind::None) { DILineInfo Result; Result.FunctionName = FunctionName; Result.StartLine = StartLine; Lines.push_back(std::make_pair(Address.Address, Result)); return Lines; } const DWARFLineTable *LineTable = getLineTableForUnit(CU); // Get the index of row we're looking for in the line table. std::vector RowVector; if (!LineTable->lookupAddressRange({Address.Address, Address.SectionIndex}, Size, RowVector)) { return Lines; } for (uint32_t RowIndex : RowVector) { // Take file number and line/column from the row. const DWARFDebugLine::Row &Row = LineTable->Rows[RowIndex]; DILineInfo Result; LineTable->getFileNameByIndex(Row.File, CU->getCompilationDir(), Spec.FLIKind, Result.FileName); Result.FunctionName = FunctionName; Result.Line = Row.Line; Result.Column = Row.Column; Result.StartLine = StartLine; Lines.push_back(std::make_pair(Row.Address.Address, Result)); } return Lines; } DIInliningInfo DWARFContext::getInliningInfoForAddress(object::SectionedAddress Address, DILineInfoSpecifier Spec) { DIInliningInfo InliningInfo; DWARFCompileUnit *CU = getCompileUnitForAddress(Address.Address); if (!CU) return InliningInfo; const DWARFLineTable *LineTable = nullptr; SmallVector InlinedChain; CU->getInlinedChainForAddress(Address.Address, InlinedChain); if (InlinedChain.size() == 0) { // If there is no DIE for address (e.g. it is in unavailable .dwo file), // try to at least get file/line info from symbol table. if (Spec.FLIKind != FileLineInfoKind::None) { DILineInfo Frame; LineTable = getLineTableForUnit(CU); if (LineTable && LineTable->getFileLineInfoForAddress( {Address.Address, Address.SectionIndex}, CU->getCompilationDir(), Spec.FLIKind, Frame)) InliningInfo.addFrame(Frame); } return InliningInfo; } uint32_t CallFile = 0, CallLine = 0, CallColumn = 0, CallDiscriminator = 0; for (uint32_t i = 0, n = InlinedChain.size(); i != n; i++) { DWARFDie &FunctionDIE = InlinedChain[i]; DILineInfo Frame; // Get function name if necessary. if (const char *Name = FunctionDIE.getSubroutineName(Spec.FNKind)) Frame.FunctionName = Name; if (auto DeclLineResult = FunctionDIE.getDeclLine()) Frame.StartLine = DeclLineResult; if (Spec.FLIKind != FileLineInfoKind::None) { if (i == 0) { // For the topmost frame, initialize the line table of this // compile unit and fetch file/line info from it. LineTable = getLineTableForUnit(CU); // For the topmost routine, get file/line info from line table. if (LineTable) LineTable->getFileLineInfoForAddress( {Address.Address, Address.SectionIndex}, CU->getCompilationDir(), Spec.FLIKind, Frame); } else { // Otherwise, use call file, call line and call column from // previous DIE in inlined chain. if (LineTable) LineTable->getFileNameByIndex(CallFile, CU->getCompilationDir(), Spec.FLIKind, Frame.FileName); Frame.Line = CallLine; Frame.Column = CallColumn; Frame.Discriminator = CallDiscriminator; } // Get call file/line/column of a current DIE. if (i + 1 < n) { FunctionDIE.getCallerFrame(CallFile, CallLine, CallColumn, CallDiscriminator); } } InliningInfo.addFrame(Frame); } return InliningInfo; } std::shared_ptr DWARFContext::getDWOContext(StringRef AbsolutePath) { #if 0 if (auto S = DWP.lock()) { DWARFContext *Ctxt = S->Context.get(); return std::shared_ptr(std::move(S), Ctxt); } std::weak_ptr *Entry = &DWOFiles[AbsolutePath]; if (auto S = Entry->lock()) { DWARFContext *Ctxt = S->Context.get(); return std::shared_ptr(std::move(S), Ctxt); } Expected> Obj = [&] { if (!CheckedForDWP) { SmallString<128> DWPName; auto Obj = object::ObjectFile::createObjectFile( this->DWPName.empty() ? (DObj->getFileName() + ".dwp").toStringRef(DWPName) : StringRef(this->DWPName)); if (Obj) { Entry = &DWP; return Obj; } else { CheckedForDWP = true; // TODO: Should this error be handled (maybe in a high verbosity mode) // before falling back to .dwo files? consumeError(Obj.takeError()); } } return object::ObjectFile::createObjectFile(AbsolutePath); }(); if (!Obj) { // TODO: Actually report errors helpfully. consumeError(Obj.takeError()); return nullptr; } auto S = std::make_shared(); S->File = std::move(Obj.get()); S->Context = DWARFContext::create(*S->File.getBinary()); *Entry = S; auto *Ctxt = S->Context.get(); return std::shared_ptr(std::move(S), Ctxt); #else llvm_unreachable("XXX BINARYEN DWO"); #endif } static Error createError(const Twine &Reason, llvm::Error E) { return make_error(Reason + toString(std::move(E)), inconvertibleErrorCode()); } /// SymInfo contains information about symbol: it's address /// and section index which is -1LL for absolute symbols. struct SymInfo { uint64_t Address; uint64_t SectionIndex; }; /// Returns the address of symbol relocation used against and a section index. /// Used for futher relocations computation. Symbol's section load address is static Expected getSymbolInfo(const object::ObjectFile &Obj, const RelocationRef &Reloc, const LoadedObjectInfo *L, std::map &Cache) { SymInfo Ret = {0, (uint64_t)-1LL}; object::section_iterator RSec = Obj.section_end(); object::symbol_iterator Sym = Reloc.getSymbol(); std::map::iterator CacheIt = Cache.end(); // First calculate the address of the symbol or section as it appears // in the object file if (Sym != Obj.symbol_end()) { bool New; std::tie(CacheIt, New) = Cache.insert({*Sym, {0, 0}}); if (!New) return CacheIt->second; Expected SymAddrOrErr = Sym->getAddress(); if (!SymAddrOrErr) return createError("failed to compute symbol address: ", SymAddrOrErr.takeError()); // Also remember what section this symbol is in for later auto SectOrErr = Sym->getSection(); if (!SectOrErr) return createError("failed to get symbol section: ", SectOrErr.takeError()); RSec = *SectOrErr; Ret.Address = *SymAddrOrErr; #if 0 // XXX BINARYEN } else if (auto *MObj = dyn_cast(&Obj)) { RSec = MObj->getRelocationSection(Reloc.getRawDataRefImpl()); Ret.Address = RSec->getAddress(); #endif } if (RSec != Obj.section_end()) Ret.SectionIndex = RSec->getIndex(); // If we are given load addresses for the sections, we need to adjust: // SymAddr = (Address of Symbol Or Section in File) - // (Address of Section in File) + // (Load Address of Section) // RSec is now either the section being targeted or the section // containing the symbol being targeted. In either case, // we need to perform the same computation. if (L && RSec != Obj.section_end()) if (uint64_t SectionLoadAddress = L->getSectionLoadAddress(*RSec)) Ret.Address += SectionLoadAddress - RSec->getAddress(); if (CacheIt != Cache.end()) CacheIt->second = Ret; return Ret; } #if 0 // XXX BINARYEN static bool isRelocScattered(const object::ObjectFile &Obj, const RelocationRef &Reloc) { const MachOObjectFile *MachObj = dyn_cast(&Obj); if (!MachObj) return false; // MachO also has relocations that point to sections and // scattered relocations. auto RelocInfo = MachObj->getRelocation(Reloc.getRawDataRefImpl()); return MachObj->isRelocationScattered(RelocInfo); } #endif ErrorPolicy DWARFContext::defaultErrorHandler(Error E) { WithColor::error() << toString(std::move(E)) << '\n'; return ErrorPolicy::Continue; } namespace { struct DWARFSectionMap final : public DWARFSection { RelocAddrMap Relocs; }; class DWARFObjInMemory final : public DWARFObject { bool IsLittleEndian; uint8_t AddressSize; StringRef FileName; const object::ObjectFile *Obj = nullptr; std::vector SectionNames; using InfoSectionMap = MapVector>; InfoSectionMap InfoSections; InfoSectionMap TypesSections; InfoSectionMap InfoDWOSections; InfoSectionMap TypesDWOSections; DWARFSectionMap LocSection; DWARFSectionMap LoclistsSection; DWARFSectionMap LineSection; DWARFSectionMap RangesSection; DWARFSectionMap RnglistsSection; DWARFSectionMap StrOffsetsSection; DWARFSectionMap LineDWOSection; DWARFSectionMap FrameSection; DWARFSectionMap EHFrameSection; DWARFSectionMap LocDWOSection; DWARFSectionMap StrOffsetsDWOSection; DWARFSectionMap RangesDWOSection; DWARFSectionMap RnglistsDWOSection; DWARFSectionMap AddrSection; DWARFSectionMap AppleNamesSection; DWARFSectionMap AppleTypesSection; DWARFSectionMap AppleNamespacesSection; DWARFSectionMap AppleObjCSection; DWARFSectionMap NamesSection; DWARFSectionMap PubnamesSection; DWARFSectionMap PubtypesSection; DWARFSectionMap GnuPubnamesSection; DWARFSectionMap GnuPubtypesSection; DWARFSectionMap *mapNameToDWARFSection(StringRef Name) { return StringSwitch(Name) .Case("debug_loc", &LocSection) .Case("debug_loclists", &LoclistsSection) .Case("debug_line", &LineSection) .Case("debug_frame", &FrameSection) .Case("eh_frame", &EHFrameSection) .Case("debug_str_offsets", &StrOffsetsSection) .Case("debug_ranges", &RangesSection) .Case("debug_rnglists", &RnglistsSection) .Case("debug_loc.dwo", &LocDWOSection) .Case("debug_line.dwo", &LineDWOSection) .Case("debug_names", &NamesSection) .Case("debug_rnglists.dwo", &RnglistsDWOSection) .Case("debug_str_offsets.dwo", &StrOffsetsDWOSection) .Case("debug_addr", &AddrSection) .Case("apple_names", &AppleNamesSection) .Case("debug_pubnames", &PubnamesSection) .Case("debug_pubtypes", &PubtypesSection) .Case("debug_gnu_pubnames", &GnuPubnamesSection) .Case("debug_gnu_pubtypes", &GnuPubtypesSection) .Case("apple_types", &AppleTypesSection) .Case("apple_namespaces", &AppleNamespacesSection) .Case("apple_namespac", &AppleNamespacesSection) .Case("apple_objc", &AppleObjCSection) .Default(nullptr); } StringRef AbbrevSection; StringRef ArangesSection; StringRef StrSection; StringRef MacinfoSection; StringRef AbbrevDWOSection; StringRef StrDWOSection; StringRef CUIndexSection; StringRef GdbIndexSection; StringRef TUIndexSection; StringRef LineStrSection; // A deque holding section data whose iterators are not invalidated when // new decompressed sections are inserted at the end. std::deque> UncompressedSections; StringRef *mapSectionToMember(StringRef Name) { if (DWARFSection *Sec = mapNameToDWARFSection(Name)) return &Sec->Data; return StringSwitch(Name) .Case("debug_abbrev", &AbbrevSection) .Case("debug_aranges", &ArangesSection) .Case("debug_str", &StrSection) .Case("debug_macinfo", &MacinfoSection) .Case("debug_abbrev.dwo", &AbbrevDWOSection) .Case("debug_str.dwo", &StrDWOSection) .Case("debug_cu_index", &CUIndexSection) .Case("debug_tu_index", &TUIndexSection) .Case("gdb_index", &GdbIndexSection) .Case("debug_line_str", &LineStrSection) // Any more debug info sections go here. .Default(nullptr); } /// If Sec is compressed section, decompresses and updates its contents /// provided by Data. Otherwise leaves it unchanged. Error maybeDecompress(const object::SectionRef &Sec, StringRef Name, StringRef &Data) { #if 1 // XXX BINARYEN errs() << "maybeDecompress?\n"; return Error::success(); #else if (!Decompressor::isCompressed(Sec)) return Error::success(); Expected Decompressor = Decompressor::create(Name, Data, IsLittleEndian, AddressSize == 8); if (!Decompressor) return Decompressor.takeError(); SmallString<0> Out; if (auto Err = Decompressor->resizeAndDecompress(Out)) return Err; UncompressedSections.push_back(std::move(Out)); Data = UncompressedSections.back(); return Error::success(); #endif } public: DWARFObjInMemory(const StringMap> &Sections, uint8_t AddrSize, bool IsLittleEndian) : IsLittleEndian(IsLittleEndian), AddressSize(4) // XXX BINARYEN { for (const auto &SecIt : Sections) { if (StringRef *SectionData = mapSectionToMember(SecIt.first())) *SectionData = SecIt.second->getBuffer(); else if (SecIt.first() == "debug_info") // Find debug_info and debug_types data by section rather than name as // there are multiple, comdat grouped, of these sections. InfoSections[SectionRef()].Data = SecIt.second->getBuffer(); else if (SecIt.first() == "debug_info.dwo") InfoDWOSections[SectionRef()].Data = SecIt.second->getBuffer(); else if (SecIt.first() == "debug_types") TypesSections[SectionRef()].Data = SecIt.second->getBuffer(); else if (SecIt.first() == "debug_types.dwo") TypesDWOSections[SectionRef()].Data = SecIt.second->getBuffer(); } } DWARFObjInMemory(const object::ObjectFile &Obj, const LoadedObjectInfo *L, function_ref HandleError) : IsLittleEndian(Obj.isLittleEndian()), AddressSize(Obj.getBytesInAddress()), FileName(Obj.getFileName()), Obj(&Obj) { StringMap SectionAmountMap; for (const SectionRef &Section : Obj.sections()) { StringRef Name; if (auto NameOrErr = Section.getName()) Name = *NameOrErr; else consumeError(NameOrErr.takeError()); ++SectionAmountMap[Name]; SectionNames.push_back({ Name, true }); // Skip BSS and Virtual sections, they aren't interesting. if (Section.isBSS() || Section.isVirtual()) continue; // Skip sections stripped by dsymutil. if (Section.isStripped()) continue; StringRef Data; Expected SecOrErr = Section.getRelocatedSection(); if (!SecOrErr) { ErrorPolicy EP = HandleError(createError( "failed to get relocated section: ", SecOrErr.takeError())); if (EP == ErrorPolicy::Halt) return; continue; } // Try to obtain an already relocated version of this section. // Else use the unrelocated section from the object file. We'll have to // apply relocations ourselves later. section_iterator RelocatedSection = *SecOrErr; if (!L || !L->getLoadedSectionContents(*RelocatedSection, Data)) { Expected E = Section.getContents(); if (E) Data = *E; else // maybeDecompress below will error. consumeError(E.takeError()); } if (auto Err = maybeDecompress(Section, Name, Data)) { ErrorPolicy EP = HandleError(createError( "failed to decompress '" + Name + "', ", std::move(Err))); if (EP == ErrorPolicy::Halt) return; continue; } // Compressed sections names in GNU style starts from ".z", // at this point section is decompressed and we drop compression prefix. Name = Name.substr( Name.find_first_not_of("._z")); // Skip ".", "z" and "_" prefixes. // Map platform specific debug section names to DWARF standard section // names. Name = Obj.mapDebugSectionName(Name); if (StringRef *SectionData = mapSectionToMember(Name)) { *SectionData = Data; if (Name == "debug_ranges") { // FIXME: Use the other dwo range section when we emit it. RangesDWOSection.Data = Data; } } else if (Name == "debug_info") { // Find debug_info and debug_types data by section rather than name as // there are multiple, comdat grouped, of these sections. InfoSections[Section].Data = Data; } else if (Name == "debug_info.dwo") { InfoDWOSections[Section].Data = Data; } else if (Name == "debug_types") { TypesSections[Section].Data = Data; } else if (Name == "debug_types.dwo") { TypesDWOSections[Section].Data = Data; } if (RelocatedSection == Obj.section_end()) continue; StringRef RelSecName; if (auto NameOrErr = RelocatedSection->getName()) RelSecName = *NameOrErr; else consumeError(NameOrErr.takeError()); // If the section we're relocating was relocated already by the JIT, // then we used the relocated version above, so we do not need to process // relocations for it now. StringRef RelSecData; if (L && L->getLoadedSectionContents(*RelocatedSection, RelSecData)) continue; // In Mach-o files, the relocations do not need to be applied if // there is no load offset to apply. The value read at the // relocation point already factors in the section address // (actually applying the relocations will produce wrong results // as the section address will be added twice). if (!L && isa(&Obj)) continue; RelSecName = RelSecName.substr( RelSecName.find_first_not_of("._z")); // Skip . and _ prefixes. // TODO: Add support for relocations in other sections as needed. // Record relocations for the debug_info and debug_line sections. DWARFSectionMap *Sec = mapNameToDWARFSection(RelSecName); RelocAddrMap *Map = Sec ? &Sec->Relocs : nullptr; if (!Map) { // Find debug_info and debug_types relocs by section rather than name // as there are multiple, comdat grouped, of these sections. if (RelSecName == "debug_info") Map = &static_cast(InfoSections[*RelocatedSection]) .Relocs; else if (RelSecName == "debug_info.dwo") Map = &static_cast( InfoDWOSections[*RelocatedSection]) .Relocs; else if (RelSecName == "debug_types") Map = &static_cast(TypesSections[*RelocatedSection]) .Relocs; else if (RelSecName == "debug_types.dwo") Map = &static_cast( TypesDWOSections[*RelocatedSection]) .Relocs; else continue; } if (Section.relocation_begin() == Section.relocation_end()) continue; #if 1 // XXX BINARYEN errs() << "relocation?\n"; #else // Symbol to [address, section index] cache mapping. std::map AddrCache; bool (*Supports)(uint64_t); RelocationResolver Resolver; std::tie(Supports, Resolver) = getRelocationResolver(Obj); for (const RelocationRef &Reloc : Section.relocations()) { // FIXME: it's not clear how to correctly handle scattered // relocations. if (isRelocScattered(Obj, Reloc)) continue; Expected SymInfoOrErr = getSymbolInfo(Obj, Reloc, L, AddrCache); if (!SymInfoOrErr) { if (HandleError(SymInfoOrErr.takeError()) == ErrorPolicy::Halt) return; continue; } // Check if Resolver can handle this relocation type early so as not to // handle invalid cases in DWARFDataExtractor. // // TODO Don't store Resolver in every RelocAddrEntry. if (Supports && Supports(Reloc.getType())) { auto I = Map->try_emplace( Reloc.getOffset(), RelocAddrEntry{SymInfoOrErr->SectionIndex, Reloc, SymInfoOrErr->Address, Optional(), 0, Resolver}); // If we didn't successfully insert that's because we already had a // relocation for that offset. Store it as a second relocation in the // same RelocAddrEntry instead. if (!I.second) { RelocAddrEntry &entry = I.first->getSecond(); if (entry.Reloc2) { ErrorPolicy EP = HandleError(createError( "At most two relocations per offset are supported")); if (EP == ErrorPolicy::Halt) return; } entry.Reloc2 = Reloc; entry.SymbolValue2 = SymInfoOrErr->Address; } } else { SmallString<32> Type; Reloc.getTypeName(Type); ErrorPolicy EP = HandleError( createError("failed to compute relocation: " + Type + ", ", errorCodeToError(object_error::parse_failed))); if (EP == ErrorPolicy::Halt) return; } } #endif } for (SectionName &S : SectionNames) if (SectionAmountMap[S.Name] > 1) S.IsNameUnique = false; } Optional find(const DWARFSection &S, uint64_t Pos) const override { auto &Sec = static_cast(S); RelocAddrMap::const_iterator AI = Sec.Relocs.find(Pos); if (AI == Sec.Relocs.end()) return None; return AI->second; } const object::ObjectFile *getFile() const override { return Obj; } ArrayRef getSectionNames() const override { return SectionNames; } bool isLittleEndian() const override { return IsLittleEndian; } StringRef getAbbrevDWOSection() const override { return AbbrevDWOSection; } const DWARFSection &getLineDWOSection() const override { return LineDWOSection; } const DWARFSection &getLocDWOSection() const override { return LocDWOSection; } StringRef getStrDWOSection() const override { return StrDWOSection; } const DWARFSection &getStrOffsetsDWOSection() const override { return StrOffsetsDWOSection; } const DWARFSection &getRangesDWOSection() const override { return RangesDWOSection; } const DWARFSection &getRnglistsDWOSection() const override { return RnglistsDWOSection; } const DWARFSection &getAddrSection() const override { return AddrSection; } StringRef getCUIndexSection() const override { return CUIndexSection; } StringRef getGdbIndexSection() const override { return GdbIndexSection; } StringRef getTUIndexSection() const override { return TUIndexSection; } // DWARF v5 const DWARFSection &getStrOffsetsSection() const override { return StrOffsetsSection; } StringRef getLineStrSection() const override { return LineStrSection; } // Sections for DWARF5 split dwarf proposal. void forEachInfoDWOSections( function_ref F) const override { for (auto &P : InfoDWOSections) F(P.second); } void forEachTypesDWOSections( function_ref F) const override { for (auto &P : TypesDWOSections) F(P.second); } StringRef getAbbrevSection() const override { return AbbrevSection; } const DWARFSection &getLocSection() const override { return LocSection; } const DWARFSection &getLoclistsSection() const override { return LoclistsSection; } StringRef getArangesSection() const override { return ArangesSection; } const DWARFSection &getFrameSection() const override { return FrameSection; } const DWARFSection &getEHFrameSection() const override { return EHFrameSection; } const DWARFSection &getLineSection() const override { return LineSection; } StringRef getStrSection() const override { return StrSection; } const DWARFSection &getRangesSection() const override { return RangesSection; } const DWARFSection &getRnglistsSection() const override { return RnglistsSection; } StringRef getMacinfoSection() const override { return MacinfoSection; } const DWARFSection &getPubnamesSection() const override { return PubnamesSection; } const DWARFSection &getPubtypesSection() const override { return PubtypesSection; } const DWARFSection &getGnuPubnamesSection() const override { return GnuPubnamesSection; } const DWARFSection &getGnuPubtypesSection() const override { return GnuPubtypesSection; } const DWARFSection &getAppleNamesSection() const override { return AppleNamesSection; } const DWARFSection &getAppleTypesSection() const override { return AppleTypesSection; } const DWARFSection &getAppleNamespacesSection() const override { return AppleNamespacesSection; } const DWARFSection &getAppleObjCSection() const override { return AppleObjCSection; } const DWARFSection &getNamesSection() const override { return NamesSection; } StringRef getFileName() const override { return FileName; } uint8_t getAddressSize() const override { return AddressSize; } void forEachInfoSections( function_ref F) const override { for (auto &P : InfoSections) F(P.second); } void forEachTypesSections( function_ref F) const override { for (auto &P : TypesSections) F(P.second); } }; } // namespace #if 0 // XXX BINARYEN std::unique_ptr DWARFContext::create(const object::ObjectFile &Obj, const LoadedObjectInfo *L, function_ref HandleError, std::string DWPName) { auto DObj = std::make_unique(Obj, L, HandleError); return std::make_unique(std::move(DObj), std::move(DWPName)); } #endif std::unique_ptr DWARFContext::create(const StringMap> &Sections, uint8_t AddrSize, bool isLittleEndian) { auto DObj = std::make_unique(Sections, AddrSize, isLittleEndian); return std::make_unique(std::move(DObj), ""); } Error DWARFContext::loadRegisterInfo(const object::ObjectFile &Obj) { llvm_unreachable("loadRegisterInfo"); // XXX BINARYEN } uint8_t DWARFContext::getCUAddrSize() { // In theory, different compile units may have different address byte // sizes, but for simplicity we just use the address byte size of the // last compile unit. In practice the address size field is repeated across // various DWARF headers (at least in version 5) to make it easier to dump // them independently, not to enable varying the address size. uint8_t Addr = 0; for (const auto &CU : compile_units()) { Addr = CU->getAddressByteSize(); break; } return Addr; } void DWARFContext::dumpWarning(Error Warning) { handleAllErrors(std::move(Warning), [](ErrorInfoBase &Info) { WithColor::warning() << Info.message() << '\n'; }); } binaryen-version_91/third_party/llvm-project/DWARFDataExtractor.cpp000066400000000000000000000053251362402614000256700ustar00rootroot00000000000000//===- DWARFDataExtractor.cpp ---------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" using namespace llvm; uint64_t DWARFDataExtractor::getRelocatedValue(uint32_t Size, uint64_t *Off, uint64_t *SecNdx, Error *Err) const { if (SecNdx) *SecNdx = object::SectionedAddress::UndefSection; if (!Section) return getUnsigned(Off, Size, Err); Optional E = Obj->find(*Section, *Off); uint64_t A = getUnsigned(Off, Size, Err); if (!E) return A; if (SecNdx) *SecNdx = E->SectionIndex; uint64_t R = E->Resolver(E->Reloc, E->SymbolValue, A); if (E->Reloc2) R = E->Resolver(*E->Reloc2, E->SymbolValue2, R); return R; } Optional DWARFDataExtractor::getEncodedPointer(uint64_t *Offset, uint8_t Encoding, uint64_t PCRelOffset) const { if (Encoding == dwarf::DW_EH_PE_omit) return None; uint64_t Result = 0; uint64_t OldOffset = *Offset; // First get value switch (Encoding & 0x0F) { case dwarf::DW_EH_PE_absptr: switch (getAddressSize()) { case 2: case 4: case 8: Result = getUnsigned(Offset, getAddressSize()); break; default: return None; } break; case dwarf::DW_EH_PE_uleb128: Result = getULEB128(Offset); break; case dwarf::DW_EH_PE_sleb128: Result = getSLEB128(Offset); break; case dwarf::DW_EH_PE_udata2: Result = getUnsigned(Offset, 2); break; case dwarf::DW_EH_PE_udata4: Result = getUnsigned(Offset, 4); break; case dwarf::DW_EH_PE_udata8: Result = getUnsigned(Offset, 8); break; case dwarf::DW_EH_PE_sdata2: Result = getSigned(Offset, 2); break; case dwarf::DW_EH_PE_sdata4: Result = getSigned(Offset, 4); break; case dwarf::DW_EH_PE_sdata8: Result = getSigned(Offset, 8); break; default: return None; } // Then add relative offset, if required switch (Encoding & 0x70) { case dwarf::DW_EH_PE_absptr: // do nothing break; case dwarf::DW_EH_PE_pcrel: Result += PCRelOffset; break; case dwarf::DW_EH_PE_datarel: case dwarf::DW_EH_PE_textrel: case dwarf::DW_EH_PE_funcrel: case dwarf::DW_EH_PE_aligned: default: *Offset = OldOffset; return None; } return Result; } binaryen-version_91/third_party/llvm-project/DWARFDebugAbbrev.cpp000066400000000000000000000073531362402614000252760ustar00rootroot00000000000000//===- DWARFDebugAbbrev.cpp -----------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h" #include "llvm/Support/Format.h" #include "llvm/Support/raw_ostream.h" #include #include #include using namespace llvm; DWARFAbbreviationDeclarationSet::DWARFAbbreviationDeclarationSet() { clear(); } void DWARFAbbreviationDeclarationSet::clear() { Offset = 0; FirstAbbrCode = 0; Decls.clear(); } bool DWARFAbbreviationDeclarationSet::extract(DataExtractor Data, uint64_t *OffsetPtr) { clear(); const uint64_t BeginOffset = *OffsetPtr; Offset = BeginOffset; DWARFAbbreviationDeclaration AbbrDecl; uint32_t PrevAbbrCode = 0; while (AbbrDecl.extract(Data, OffsetPtr)) { if (FirstAbbrCode == 0) { FirstAbbrCode = AbbrDecl.getCode(); } else { if (PrevAbbrCode + 1 != AbbrDecl.getCode()) { // Codes are not consecutive, can't do O(1) lookups. FirstAbbrCode = UINT32_MAX; } } PrevAbbrCode = AbbrDecl.getCode(); Decls.push_back(std::move(AbbrDecl)); } return BeginOffset != *OffsetPtr; } void DWARFAbbreviationDeclarationSet::dump(raw_ostream &OS) const { for (const auto &Decl : Decls) Decl.dump(OS); } const DWARFAbbreviationDeclaration * DWARFAbbreviationDeclarationSet::getAbbreviationDeclaration( uint32_t AbbrCode) const { if (FirstAbbrCode == UINT32_MAX) { for (const auto &Decl : Decls) { if (Decl.getCode() == AbbrCode) return &Decl; } return nullptr; } if (AbbrCode < FirstAbbrCode || AbbrCode >= FirstAbbrCode + Decls.size()) return nullptr; return &Decls[AbbrCode - FirstAbbrCode]; } DWARFDebugAbbrev::DWARFDebugAbbrev() { clear(); } void DWARFDebugAbbrev::clear() { AbbrDeclSets.clear(); PrevAbbrOffsetPos = AbbrDeclSets.end(); } void DWARFDebugAbbrev::extract(DataExtractor Data) { clear(); this->Data = Data; } void DWARFDebugAbbrev::parse() const { if (!Data) return; uint64_t Offset = 0; auto I = AbbrDeclSets.begin(); while (Data->isValidOffset(Offset)) { while (I != AbbrDeclSets.end() && I->first < Offset) ++I; uint64_t CUAbbrOffset = Offset; DWARFAbbreviationDeclarationSet AbbrDecls; if (!AbbrDecls.extract(*Data, &Offset)) break; AbbrDeclSets.insert(I, std::make_pair(CUAbbrOffset, std::move(AbbrDecls))); } Data = None; } void DWARFDebugAbbrev::dump(raw_ostream &OS) const { parse(); if (AbbrDeclSets.empty()) { OS << "< EMPTY >\n"; return; } for (const auto &I : AbbrDeclSets) { OS << format("Abbrev table for offset: 0x%8.8" PRIx64 "\n", I.first); I.second.dump(OS); } } const DWARFAbbreviationDeclarationSet* DWARFDebugAbbrev::getAbbreviationDeclarationSet(uint64_t CUAbbrOffset) const { const auto End = AbbrDeclSets.end(); if (PrevAbbrOffsetPos != End && PrevAbbrOffsetPos->first == CUAbbrOffset) { return &(PrevAbbrOffsetPos->second); } const auto Pos = AbbrDeclSets.find(CUAbbrOffset); if (Pos != End) { PrevAbbrOffsetPos = Pos; return &(Pos->second); } if (Data && CUAbbrOffset < Data->getData().size()) { uint64_t Offset = CUAbbrOffset; DWARFAbbreviationDeclarationSet AbbrDecls; if (!AbbrDecls.extract(*Data, &Offset)) return nullptr; PrevAbbrOffsetPos = AbbrDeclSets.insert(std::make_pair(CUAbbrOffset, std::move(AbbrDecls))) .first; return &PrevAbbrOffsetPos->second; } return nullptr; } binaryen-version_91/third_party/llvm-project/DWARFDebugAddr.cpp000066400000000000000000000164251362402614000247470ustar00rootroot00000000000000//===- DWARFDebugAddr.cpp -------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFDebugAddr.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/DebugInfo/DWARF/DWARFUnit.h" using namespace llvm; void DWARFDebugAddrTable::clear() { HeaderData = {}; Addrs.clear(); invalidateLength(); } Error DWARFDebugAddrTable::extract(DWARFDataExtractor Data, uint64_t *OffsetPtr, uint16_t Version, uint8_t AddrSize, std::function WarnCallback) { clear(); HeaderOffset = *OffsetPtr; // Read and verify the length field. if (!Data.isValidOffsetForDataOfSize(*OffsetPtr, sizeof(uint32_t))) return createStringError(errc::invalid_argument, "section is not large enough to contain a " ".debug_addr table length at offset 0x%" PRIx64, *OffsetPtr); uint16_t UnitVersion; if (Version == 0) { WarnCallback(createStringError(errc::invalid_argument, "DWARF version is not defined in CU," " assuming version 5")); UnitVersion = 5; } else { UnitVersion = Version; } // TODO: Add support for DWARF64. Format = dwarf::DwarfFormat::DWARF32; if (UnitVersion >= 5) { HeaderData.Length = Data.getU32(OffsetPtr); if (HeaderData.Length == dwarf::DW_LENGTH_DWARF64) { invalidateLength(); return createStringError(errc::not_supported, "DWARF64 is not supported in .debug_addr at offset 0x%" PRIx64, HeaderOffset); } if (HeaderData.Length + sizeof(uint32_t) < sizeof(Header)) { uint32_t TmpLength = getLength(); invalidateLength(); return createStringError(errc::invalid_argument, ".debug_addr table at offset 0x%" PRIx64 " has too small length (0x%" PRIx32 ") to contain a complete header", HeaderOffset, TmpLength); } uint64_t End = HeaderOffset + getLength(); if (!Data.isValidOffsetForDataOfSize(HeaderOffset, End - HeaderOffset)) { uint32_t TmpLength = getLength(); invalidateLength(); return createStringError(errc::invalid_argument, "section is not large enough to contain a .debug_addr table " "of length 0x%" PRIx32 " at offset 0x%" PRIx64, TmpLength, HeaderOffset); } HeaderData.Version = Data.getU16(OffsetPtr); HeaderData.AddrSize = Data.getU8(OffsetPtr); HeaderData.SegSize = Data.getU8(OffsetPtr); DataSize = getDataSize(); } else { HeaderData.Version = UnitVersion; HeaderData.AddrSize = AddrSize; // TODO: Support for non-zero SegSize. HeaderData.SegSize = 0; DataSize = Data.size(); } // Perform basic validation of the remaining header fields. // We support DWARF version 5 for now as well as pre-DWARF5 // implementations of .debug_addr table, which doesn't contain a header // and consists only of a series of addresses. if (HeaderData.Version > 5) { return createStringError(errc::not_supported, "version %" PRIu16 " of .debug_addr section at offset 0x%" PRIx64 " is not supported", HeaderData.Version, HeaderOffset); } // FIXME: For now we just treat version mismatch as an error, // however the correct way to associate a .debug_addr table // with a .debug_info table is to look at the DW_AT_addr_base // attribute in the info table. if (HeaderData.Version != UnitVersion) return createStringError(errc::invalid_argument, ".debug_addr table at offset 0x%" PRIx64 " has version %" PRIu16 " which is different from the version suggested" " by the DWARF unit header: %" PRIu16, HeaderOffset, HeaderData.Version, UnitVersion); if (HeaderData.AddrSize != 4 && HeaderData.AddrSize != 8) return createStringError(errc::not_supported, ".debug_addr table at offset 0x%" PRIx64 " has unsupported address size %" PRIu8, HeaderOffset, HeaderData.AddrSize); if (HeaderData.AddrSize != AddrSize && AddrSize != 0) return createStringError(errc::invalid_argument, ".debug_addr table at offset 0x%" PRIx64 " has address size %" PRIu8 " which is different from CU address size %" PRIu8, HeaderOffset, HeaderData.AddrSize, AddrSize); // TODO: add support for non-zero segment selector size. if (HeaderData.SegSize != 0) return createStringError(errc::not_supported, ".debug_addr table at offset 0x%" PRIx64 " has unsupported segment selector size %" PRIu8, HeaderOffset, HeaderData.SegSize); if (DataSize % HeaderData.AddrSize != 0) { invalidateLength(); return createStringError(errc::invalid_argument, ".debug_addr table at offset 0x%" PRIx64 " contains data of size %" PRIu32 " which is not a multiple of addr size %" PRIu8, HeaderOffset, DataSize, HeaderData.AddrSize); } Data.setAddressSize(HeaderData.AddrSize); uint32_t AddrCount = DataSize / HeaderData.AddrSize; for (uint32_t I = 0; I < AddrCount; ++I) if (HeaderData.AddrSize == 4) Addrs.push_back(Data.getU32(OffsetPtr)); else Addrs.push_back(Data.getU64(OffsetPtr)); return Error::success(); } void DWARFDebugAddrTable::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { if (DumpOpts.Verbose) OS << format("0x%8.8" PRIx32 ": ", HeaderOffset); OS << format("Addr Section: length = 0x%8.8" PRIx32 ", version = 0x%4.4" PRIx16 ", " "addr_size = 0x%2.2" PRIx8 ", seg_size = 0x%2.2" PRIx8 "\n", HeaderData.Length, HeaderData.Version, HeaderData.AddrSize, HeaderData.SegSize); if (Addrs.size() > 0) { const char *AddrFmt = (HeaderData.AddrSize == 4) ? "0x%8.8" PRIx64 "\n" : "0x%16.16" PRIx64 "\n"; OS << "Addrs: [\n"; for (uint64_t Addr : Addrs) OS << format(AddrFmt, Addr); OS << "]\n"; } } Expected DWARFDebugAddrTable::getAddrEntry(uint32_t Index) const { if (Index < Addrs.size()) return Addrs[Index]; return createStringError(errc::invalid_argument, "Index %" PRIu32 " is out of range of the " ".debug_addr table at offset 0x%" PRIx64, Index, HeaderOffset); } uint32_t DWARFDebugAddrTable::getLength() const { if (HeaderData.Length == 0) return 0; // TODO: DWARF64 support. return HeaderData.Length + sizeof(uint32_t); } uint32_t DWARFDebugAddrTable::getDataSize() const { if (DataSize != 0) return DataSize; if (getLength() == 0) return 0; return getLength() - getHeaderSize(); } binaryen-version_91/third_party/llvm-project/DWARFDebugArangeSet.cpp000066400000000000000000000105731362402614000257440ustar00rootroot00000000000000//===- DWARFDebugArangeSet.cpp --------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFDebugArangeSet.h" #include "llvm/Support/Format.h" #include "llvm/Support/raw_ostream.h" #include #include #include #include using namespace llvm; void DWARFDebugArangeSet::Descriptor::dump(raw_ostream &OS, uint32_t AddressSize) const { OS << format("[0x%*.*" PRIx64 ", ", AddressSize * 2, AddressSize * 2, Address) << format(" 0x%*.*" PRIx64 ")", AddressSize * 2, AddressSize * 2, getEndAddress()); } void DWARFDebugArangeSet::clear() { Offset = -1ULL; std::memset(&HeaderData, 0, sizeof(Header)); ArangeDescriptors.clear(); } bool DWARFDebugArangeSet::extract(DataExtractor data, uint64_t *offset_ptr) { if (data.isValidOffset(*offset_ptr)) { ArangeDescriptors.clear(); Offset = *offset_ptr; // 7.20 Address Range Table // // Each set of entries in the table of address ranges contained in // the .debug_aranges section begins with a header consisting of: a // 4-byte length containing the length of the set of entries for this // compilation unit, not including the length field itself; a 2-byte // version identifier containing the value 2 for DWARF Version 2; a // 4-byte offset into the.debug_infosection; a 1-byte unsigned integer // containing the size in bytes of an address (or the offset portion of // an address for segmented addressing) on the target system; and a // 1-byte unsigned integer containing the size in bytes of a segment // descriptor on the target system. This header is followed by a series // of tuples. Each tuple consists of an address and a length, each in // the size appropriate for an address on the target architecture. HeaderData.Length = data.getU32(offset_ptr); HeaderData.Version = data.getU16(offset_ptr); HeaderData.CuOffset = data.getU32(offset_ptr); HeaderData.AddrSize = data.getU8(offset_ptr); HeaderData.SegSize = data.getU8(offset_ptr); // Perform basic validation of the header fields. if (!data.isValidOffsetForDataOfSize(Offset, HeaderData.Length) || (HeaderData.AddrSize != 4 && HeaderData.AddrSize != 8)) { clear(); return false; } // The first tuple following the header in each set begins at an offset // that is a multiple of the size of a single tuple (that is, twice the // size of an address). The header is padded, if necessary, to the // appropriate boundary. const uint32_t header_size = *offset_ptr - Offset; const uint32_t tuple_size = HeaderData.AddrSize * 2; uint32_t first_tuple_offset = 0; while (first_tuple_offset < header_size) first_tuple_offset += tuple_size; *offset_ptr = Offset + first_tuple_offset; Descriptor arangeDescriptor; static_assert(sizeof(arangeDescriptor.Address) == sizeof(arangeDescriptor.Length), "Different datatypes for addresses and sizes!"); assert(sizeof(arangeDescriptor.Address) >= HeaderData.AddrSize); while (data.isValidOffset(*offset_ptr)) { arangeDescriptor.Address = data.getUnsigned(offset_ptr, HeaderData.AddrSize); arangeDescriptor.Length = data.getUnsigned(offset_ptr, HeaderData.AddrSize); // Each set of tuples is terminated by a 0 for the address and 0 // for the length. if (arangeDescriptor.Address || arangeDescriptor.Length) ArangeDescriptors.push_back(arangeDescriptor); else break; // We are done if we get a zero address and length } return !ArangeDescriptors.empty(); } return false; } void DWARFDebugArangeSet::dump(raw_ostream &OS) const { OS << format("Address Range Header: length = 0x%8.8x, version = 0x%4.4x, ", HeaderData.Length, HeaderData.Version) << format("cu_offset = 0x%8.8x, addr_size = 0x%2.2x, seg_size = 0x%2.2x\n", HeaderData.CuOffset, HeaderData.AddrSize, HeaderData.SegSize); for (const auto &Desc : ArangeDescriptors) { Desc.dump(OS, HeaderData.AddrSize); OS << '\n'; } } binaryen-version_91/third_party/llvm-project/DWARFDebugAranges.cpp000066400000000000000000000076531362402614000254600ustar00rootroot00000000000000//===- DWARFDebugAranges.cpp ----------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFDebugAranges.h" #include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/DWARF/DWARFDebugArangeSet.h" #include "llvm/Support/DataExtractor.h" #include "llvm/Support/WithColor.h" #include #include #include #include #include using namespace llvm; void DWARFDebugAranges::extract(DataExtractor DebugArangesData) { if (!DebugArangesData.isValidOffset(0)) return; uint64_t Offset = 0; DWARFDebugArangeSet Set; while (Set.extract(DebugArangesData, &Offset)) { uint64_t CUOffset = Set.getCompileUnitDIEOffset(); for (const auto &Desc : Set.descriptors()) { uint64_t LowPC = Desc.Address; uint64_t HighPC = Desc.getEndAddress(); appendRange(CUOffset, LowPC, HighPC); } ParsedCUOffsets.insert(CUOffset); } } void DWARFDebugAranges::generate(DWARFContext *CTX) { clear(); if (!CTX) return; // Extract aranges from .debug_aranges section. DataExtractor ArangesData(CTX->getDWARFObj().getArangesSection(), CTX->isLittleEndian(), 0); extract(ArangesData); // Generate aranges from DIEs: even if .debug_aranges section is present, // it may describe only a small subset of compilation units, so we need to // manually build aranges for the rest of them. for (const auto &CU : CTX->compile_units()) { uint64_t CUOffset = CU->getOffset(); if (ParsedCUOffsets.insert(CUOffset).second) { Expected CURanges = CU->collectAddressRanges(); if (!CURanges) WithColor::error() << toString(CURanges.takeError()) << '\n'; else for (const auto &R : *CURanges) appendRange(CUOffset, R.LowPC, R.HighPC); } } construct(); } void DWARFDebugAranges::clear() { Endpoints.clear(); Aranges.clear(); ParsedCUOffsets.clear(); } void DWARFDebugAranges::appendRange(uint64_t CUOffset, uint64_t LowPC, uint64_t HighPC) { if (LowPC >= HighPC) return; Endpoints.emplace_back(LowPC, CUOffset, true); Endpoints.emplace_back(HighPC, CUOffset, false); } void DWARFDebugAranges::construct() { std::multiset ValidCUs; // Maintain the set of CUs describing // a current address range. llvm::sort(Endpoints); uint64_t PrevAddress = -1ULL; for (const auto &E : Endpoints) { if (PrevAddress < E.Address && !ValidCUs.empty()) { // If the address range between two endpoints is described by some // CU, first try to extend the last range in Aranges. If we can't // do it, start a new range. if (!Aranges.empty() && Aranges.back().HighPC() == PrevAddress && ValidCUs.find(Aranges.back().CUOffset) != ValidCUs.end()) { Aranges.back().setHighPC(E.Address); } else { Aranges.emplace_back(PrevAddress, E.Address, *ValidCUs.begin()); } } // Update the set of valid CUs. if (E.IsRangeStart) { ValidCUs.insert(E.CUOffset); } else { auto CUPos = ValidCUs.find(E.CUOffset); assert(CUPos != ValidCUs.end()); ValidCUs.erase(CUPos); } PrevAddress = E.Address; } assert(ValidCUs.empty()); // Endpoints are not needed now. Endpoints.clear(); Endpoints.shrink_to_fit(); } uint32_t DWARFDebugAranges::findAddress(uint64_t Address) const { RangeCollIterator It = partition_point(Aranges, [=](Range R) { return R.HighPC() <= Address; }); if (It != Aranges.end() && It->LowPC <= Address) return It->CUOffset; return -1U; } binaryen-version_91/third_party/llvm-project/DWARFDebugFrame.cpp000066400000000000000000000506261362402614000251300ustar00rootroot00000000000000//===- DWARFDebugFrame.h - Parsing of .debug_frame ------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFDebugFrame.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/DataExtractor.h" #include "llvm/Support/Errc.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Format.h" #include "llvm/Support/raw_ostream.h" #include #include #include #include #include #include using namespace llvm; using namespace dwarf; // See DWARF standard v3, section 7.23 const uint8_t DWARF_CFI_PRIMARY_OPCODE_MASK = 0xc0; const uint8_t DWARF_CFI_PRIMARY_OPERAND_MASK = 0x3f; Error CFIProgram::parse(DWARFDataExtractor Data, uint64_t *Offset, uint64_t EndOffset) { while (*Offset < EndOffset) { uint8_t Opcode = Data.getRelocatedValue(1, Offset); // Some instructions have a primary opcode encoded in the top bits. uint8_t Primary = Opcode & DWARF_CFI_PRIMARY_OPCODE_MASK; if (Primary) { // If it's a primary opcode, the first operand is encoded in the bottom // bits of the opcode itself. uint64_t Op1 = Opcode & DWARF_CFI_PRIMARY_OPERAND_MASK; switch (Primary) { default: return createStringError(errc::illegal_byte_sequence, "Invalid primary CFI opcode 0x%" PRIx8, Primary); case DW_CFA_advance_loc: case DW_CFA_restore: addInstruction(Primary, Op1); break; case DW_CFA_offset: addInstruction(Primary, Op1, Data.getULEB128(Offset)); break; } } else { // Extended opcode - its value is Opcode itself. switch (Opcode) { default: return createStringError(errc::illegal_byte_sequence, "Invalid extended CFI opcode 0x%" PRIx8, Opcode); case DW_CFA_nop: case DW_CFA_remember_state: case DW_CFA_restore_state: case DW_CFA_GNU_window_save: // No operands addInstruction(Opcode); break; case DW_CFA_set_loc: // Operands: Address addInstruction(Opcode, Data.getRelocatedAddress(Offset)); break; case DW_CFA_advance_loc1: // Operands: 1-byte delta addInstruction(Opcode, Data.getRelocatedValue(1, Offset)); break; case DW_CFA_advance_loc2: // Operands: 2-byte delta addInstruction(Opcode, Data.getRelocatedValue(2, Offset)); break; case DW_CFA_advance_loc4: // Operands: 4-byte delta addInstruction(Opcode, Data.getRelocatedValue(4, Offset)); break; case DW_CFA_restore_extended: case DW_CFA_undefined: case DW_CFA_same_value: case DW_CFA_def_cfa_register: case DW_CFA_def_cfa_offset: case DW_CFA_GNU_args_size: // Operands: ULEB128 addInstruction(Opcode, Data.getULEB128(Offset)); break; case DW_CFA_def_cfa_offset_sf: // Operands: SLEB128 addInstruction(Opcode, Data.getSLEB128(Offset)); break; case DW_CFA_offset_extended: case DW_CFA_register: case DW_CFA_def_cfa: case DW_CFA_val_offset: { // Operands: ULEB128, ULEB128 // Note: We can not embed getULEB128 directly into function // argument list. getULEB128 changes Offset and order of evaluation // for arguments is unspecified. auto op1 = Data.getULEB128(Offset); auto op2 = Data.getULEB128(Offset); addInstruction(Opcode, op1, op2); break; } case DW_CFA_offset_extended_sf: case DW_CFA_def_cfa_sf: case DW_CFA_val_offset_sf: { // Operands: ULEB128, SLEB128 // Note: see comment for the previous case auto op1 = Data.getULEB128(Offset); auto op2 = (uint64_t)Data.getSLEB128(Offset); addInstruction(Opcode, op1, op2); break; } case DW_CFA_def_cfa_expression: { uint32_t ExprLength = Data.getULEB128(Offset); addInstruction(Opcode, 0); DataExtractor Extractor( Data.getData().slice(*Offset, *Offset + ExprLength), Data.isLittleEndian(), Data.getAddressSize()); Instructions.back().Expression = DWARFExpression( Extractor, Data.getAddressSize(), dwarf::DWARF_VERSION); *Offset += ExprLength; break; } case DW_CFA_expression: case DW_CFA_val_expression: { auto RegNum = Data.getULEB128(Offset); auto BlockLength = Data.getULEB128(Offset); addInstruction(Opcode, RegNum, 0); DataExtractor Extractor( Data.getData().slice(*Offset, *Offset + BlockLength), Data.isLittleEndian(), Data.getAddressSize()); Instructions.back().Expression = DWARFExpression( Extractor, Data.getAddressSize(), dwarf::DWARF_VERSION); *Offset += BlockLength; break; } } } } return Error::success(); } namespace { } // end anonymous namespace ArrayRef CFIProgram::getOperandTypes() { static OperandType OpTypes[DW_CFA_restore+1][2]; static bool Initialized = false; if (Initialized) { return ArrayRef(&OpTypes[0], DW_CFA_restore+1); } Initialized = true; #define DECLARE_OP2(OP, OPTYPE0, OPTYPE1) \ do { \ OpTypes[OP][0] = OPTYPE0; \ OpTypes[OP][1] = OPTYPE1; \ } while (false) #define DECLARE_OP1(OP, OPTYPE0) DECLARE_OP2(OP, OPTYPE0, OT_None) #define DECLARE_OP0(OP) DECLARE_OP1(OP, OT_None) DECLARE_OP1(DW_CFA_set_loc, OT_Address); DECLARE_OP1(DW_CFA_advance_loc, OT_FactoredCodeOffset); DECLARE_OP1(DW_CFA_advance_loc1, OT_FactoredCodeOffset); DECLARE_OP1(DW_CFA_advance_loc2, OT_FactoredCodeOffset); DECLARE_OP1(DW_CFA_advance_loc4, OT_FactoredCodeOffset); DECLARE_OP1(DW_CFA_MIPS_advance_loc8, OT_FactoredCodeOffset); DECLARE_OP2(DW_CFA_def_cfa, OT_Register, OT_Offset); DECLARE_OP2(DW_CFA_def_cfa_sf, OT_Register, OT_SignedFactDataOffset); DECLARE_OP1(DW_CFA_def_cfa_register, OT_Register); DECLARE_OP1(DW_CFA_def_cfa_offset, OT_Offset); DECLARE_OP1(DW_CFA_def_cfa_offset_sf, OT_SignedFactDataOffset); DECLARE_OP1(DW_CFA_def_cfa_expression, OT_Expression); DECLARE_OP1(DW_CFA_undefined, OT_Register); DECLARE_OP1(DW_CFA_same_value, OT_Register); DECLARE_OP2(DW_CFA_offset, OT_Register, OT_UnsignedFactDataOffset); DECLARE_OP2(DW_CFA_offset_extended, OT_Register, OT_UnsignedFactDataOffset); DECLARE_OP2(DW_CFA_offset_extended_sf, OT_Register, OT_SignedFactDataOffset); DECLARE_OP2(DW_CFA_val_offset, OT_Register, OT_UnsignedFactDataOffset); DECLARE_OP2(DW_CFA_val_offset_sf, OT_Register, OT_SignedFactDataOffset); DECLARE_OP2(DW_CFA_register, OT_Register, OT_Register); DECLARE_OP2(DW_CFA_expression, OT_Register, OT_Expression); DECLARE_OP2(DW_CFA_val_expression, OT_Register, OT_Expression); DECLARE_OP1(DW_CFA_restore, OT_Register); DECLARE_OP1(DW_CFA_restore_extended, OT_Register); DECLARE_OP0(DW_CFA_remember_state); DECLARE_OP0(DW_CFA_restore_state); DECLARE_OP0(DW_CFA_GNU_window_save); DECLARE_OP1(DW_CFA_GNU_args_size, OT_Offset); DECLARE_OP0(DW_CFA_nop); #undef DECLARE_OP0 #undef DECLARE_OP1 #undef DECLARE_OP2 return ArrayRef(&OpTypes[0], DW_CFA_restore+1); } /// Print \p Opcode's operand number \p OperandIdx which has value \p Operand. void CFIProgram::printOperand(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH, const Instruction &Instr, unsigned OperandIdx, uint64_t Operand) const { assert(OperandIdx < 2); uint8_t Opcode = Instr.Opcode; OperandType Type = getOperandTypes()[Opcode][OperandIdx]; switch (Type) { case OT_Unset: { OS << " Unsupported " << (OperandIdx ? "second" : "first") << " operand to"; auto OpcodeName = CallFrameString(Opcode, Arch); if (!OpcodeName.empty()) OS << " " << OpcodeName; else OS << format(" Opcode %x", Opcode); break; } case OT_None: break; case OT_Address: OS << format(" %" PRIx64, Operand); break; case OT_Offset: // The offsets are all encoded in a unsigned form, but in practice // consumers use them signed. It's most certainly legacy due to // the lack of signed variants in the first Dwarf standards. OS << format(" %+" PRId64, int64_t(Operand)); break; case OT_FactoredCodeOffset: // Always Unsigned if (CodeAlignmentFactor) OS << format(" %" PRId64, Operand * CodeAlignmentFactor); else OS << format(" %" PRId64 "*code_alignment_factor" , Operand); break; case OT_SignedFactDataOffset: if (DataAlignmentFactor) OS << format(" %" PRId64, int64_t(Operand) * DataAlignmentFactor); else OS << format(" %" PRId64 "*data_alignment_factor" , int64_t(Operand)); break; case OT_UnsignedFactDataOffset: if (DataAlignmentFactor) OS << format(" %" PRId64, Operand * DataAlignmentFactor); else OS << format(" %" PRId64 "*data_alignment_factor" , Operand); break; case OT_Register: OS << format(" reg%" PRId64, Operand); break; case OT_Expression: assert(Instr.Expression && "missing DWARFExpression object"); OS << " "; Instr.Expression->print(OS, MRI, nullptr, IsEH); break; } } void CFIProgram::dump(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH, unsigned IndentLevel) const { for (const auto &Instr : Instructions) { uint8_t Opcode = Instr.Opcode; if (Opcode & DWARF_CFI_PRIMARY_OPCODE_MASK) Opcode &= DWARF_CFI_PRIMARY_OPCODE_MASK; OS.indent(2 * IndentLevel); OS << CallFrameString(Opcode, Arch) << ":"; for (unsigned i = 0; i < Instr.Ops.size(); ++i) printOperand(OS, MRI, IsEH, Instr, i, Instr.Ops[i]); OS << '\n'; } } void CIE::dump(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH) const { OS << format("%08x %08x %08x CIE", (uint32_t)Offset, (uint32_t)Length, DW_CIE_ID) << "\n"; OS << format(" Version: %d\n", Version); OS << " Augmentation: \"" << Augmentation << "\"\n"; if (Version >= 4) { OS << format(" Address size: %u\n", (uint32_t)AddressSize); OS << format(" Segment desc size: %u\n", (uint32_t)SegmentDescriptorSize); } OS << format(" Code alignment factor: %u\n", (uint32_t)CodeAlignmentFactor); OS << format(" Data alignment factor: %d\n", (int32_t)DataAlignmentFactor); OS << format(" Return address column: %d\n", (int32_t)ReturnAddressRegister); if (Personality) OS << format(" Personality Address: %016" PRIx64 "\n", *Personality); if (!AugmentationData.empty()) { OS << " Augmentation data: "; for (uint8_t Byte : AugmentationData) OS << ' ' << hexdigit(Byte >> 4) << hexdigit(Byte & 0xf); OS << "\n"; } OS << "\n"; CFIs.dump(OS, MRI, IsEH); OS << "\n"; } void FDE::dump(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH) const { OS << format("%08x %08x %08x FDE ", (uint32_t)Offset, (uint32_t)Length, (int32_t)LinkedCIEOffset); OS << format("cie=%08x pc=%08x...%08x\n", (int32_t)LinkedCIEOffset, (uint32_t)InitialLocation, (uint32_t)InitialLocation + (uint32_t)AddressRange); if (LSDAAddress) OS << format(" LSDA Address: %016" PRIx64 "\n", *LSDAAddress); CFIs.dump(OS, MRI, IsEH); OS << "\n"; } DWARFDebugFrame::DWARFDebugFrame(Triple::ArchType Arch, bool IsEH, uint64_t EHFrameAddress) : Arch(Arch), IsEH(IsEH), EHFrameAddress(EHFrameAddress) {} DWARFDebugFrame::~DWARFDebugFrame() = default; static void LLVM_ATTRIBUTE_UNUSED dumpDataAux(DataExtractor Data, uint64_t Offset, int Length) { errs() << "DUMP: "; for (int i = 0; i < Length; ++i) { uint8_t c = Data.getU8(&Offset); errs().write_hex(c); errs() << " "; } errs() << "\n"; } // This is a workaround for old compilers which do not allow // noreturn attribute usage in lambdas. Once the support for those // compilers are phased out, we can remove this and return back to // a ReportError lambda: [StartOffset](const char *ErrorMsg). static void LLVM_ATTRIBUTE_NORETURN ReportError(uint64_t StartOffset, const char *ErrorMsg) { std::string Str; raw_string_ostream OS(Str); OS << format(ErrorMsg, StartOffset); OS.flush(); report_fatal_error(Str); } void DWARFDebugFrame::parse(DWARFDataExtractor Data) { uint64_t Offset = 0; DenseMap CIEs; while (Data.isValidOffset(Offset)) { uint64_t StartOffset = Offset; bool IsDWARF64 = false; uint64_t Length = Data.getRelocatedValue(4, &Offset); uint64_t Id; if (Length == dwarf::DW_LENGTH_DWARF64) { // DWARF-64 is distinguished by the first 32 bits of the initial length // field being 0xffffffff. Then, the next 64 bits are the actual entry // length. IsDWARF64 = true; Length = Data.getRelocatedValue(8, &Offset); } // At this point, Offset points to the next field after Length. // Length is the structure size excluding itself. Compute an offset one // past the end of the structure (needed to know how many instructions to // read). uint64_t StartStructureOffset = Offset; uint64_t EndStructureOffset = Offset + Length; // The Id field's size depends on the DWARF format Id = Data.getUnsigned(&Offset, (IsDWARF64 && !IsEH) ? 8 : 4); bool IsCIE = ((IsDWARF64 && Id == DW64_CIE_ID) || Id == DW_CIE_ID || (IsEH && !Id)); if (IsCIE) { uint8_t Version = Data.getU8(&Offset); const char *Augmentation = Data.getCStr(&Offset); StringRef AugmentationString(Augmentation ? Augmentation : ""); uint8_t AddressSize = Version < 4 ? Data.getAddressSize() : Data.getU8(&Offset); Data.setAddressSize(AddressSize); uint8_t SegmentDescriptorSize = Version < 4 ? 0 : Data.getU8(&Offset); uint64_t CodeAlignmentFactor = Data.getULEB128(&Offset); int64_t DataAlignmentFactor = Data.getSLEB128(&Offset); uint64_t ReturnAddressRegister = Version == 1 ? Data.getU8(&Offset) : Data.getULEB128(&Offset); // Parse the augmentation data for EH CIEs StringRef AugmentationData(""); uint32_t FDEPointerEncoding = DW_EH_PE_absptr; uint32_t LSDAPointerEncoding = DW_EH_PE_omit; Optional Personality; Optional PersonalityEncoding; if (IsEH) { Optional AugmentationLength; uint64_t StartAugmentationOffset; uint64_t EndAugmentationOffset; // Walk the augmentation string to get all the augmentation data. for (unsigned i = 0, e = AugmentationString.size(); i != e; ++i) { switch (AugmentationString[i]) { default: ReportError( StartOffset, "Unknown augmentation character in entry at %" PRIx64); case 'L': LSDAPointerEncoding = Data.getU8(&Offset); break; case 'P': { if (Personality) ReportError(StartOffset, "Duplicate personality in entry at %" PRIx64); PersonalityEncoding = Data.getU8(&Offset); Personality = Data.getEncodedPointer( &Offset, *PersonalityEncoding, EHFrameAddress ? EHFrameAddress + Offset : 0); break; } case 'R': FDEPointerEncoding = Data.getU8(&Offset); break; case 'S': // Current frame is a signal trampoline. break; case 'z': if (i) ReportError(StartOffset, "'z' must be the first character at %" PRIx64); // Parse the augmentation length first. We only parse it if // the string contains a 'z'. AugmentationLength = Data.getULEB128(&Offset); StartAugmentationOffset = Offset; EndAugmentationOffset = Offset + *AugmentationLength; break; case 'B': // B-Key is used for signing functions associated with this // augmentation string break; } } if (AugmentationLength.hasValue()) { if (Offset != EndAugmentationOffset) ReportError(StartOffset, "Parsing augmentation data at %" PRIx64 " failed"); AugmentationData = Data.getData().slice(StartAugmentationOffset, EndAugmentationOffset); } } auto Cie = std::make_unique( StartOffset, Length, Version, AugmentationString, AddressSize, SegmentDescriptorSize, CodeAlignmentFactor, DataAlignmentFactor, ReturnAddressRegister, AugmentationData, FDEPointerEncoding, LSDAPointerEncoding, Personality, PersonalityEncoding, Arch); CIEs[StartOffset] = Cie.get(); Entries.emplace_back(std::move(Cie)); } else { // FDE uint64_t CIEPointer = Id; uint64_t InitialLocation = 0; uint64_t AddressRange = 0; Optional LSDAAddress; CIE *Cie = CIEs[IsEH ? (StartStructureOffset - CIEPointer) : CIEPointer]; if (IsEH) { // The address size is encoded in the CIE we reference. if (!Cie) ReportError(StartOffset, "Parsing FDE data at %" PRIx64 " failed due to missing CIE"); if (auto Val = Data.getEncodedPointer( &Offset, Cie->getFDEPointerEncoding(), EHFrameAddress ? EHFrameAddress + Offset : 0)) { InitialLocation = *Val; } if (auto Val = Data.getEncodedPointer( &Offset, Cie->getFDEPointerEncoding(), 0)) { AddressRange = *Val; } StringRef AugmentationString = Cie->getAugmentationString(); if (!AugmentationString.empty()) { // Parse the augmentation length and data for this FDE. uint64_t AugmentationLength = Data.getULEB128(&Offset); uint64_t EndAugmentationOffset = Offset + AugmentationLength; // Decode the LSDA if the CIE augmentation string said we should. if (Cie->getLSDAPointerEncoding() != DW_EH_PE_omit) { LSDAAddress = Data.getEncodedPointer( &Offset, Cie->getLSDAPointerEncoding(), EHFrameAddress ? Offset + EHFrameAddress : 0); } if (Offset != EndAugmentationOffset) ReportError(StartOffset, "Parsing augmentation data at %" PRIx64 " failed"); } } else { InitialLocation = Data.getRelocatedAddress(&Offset); AddressRange = Data.getRelocatedAddress(&Offset); } Entries.emplace_back(new FDE(StartOffset, Length, CIEPointer, InitialLocation, AddressRange, Cie, LSDAAddress, Arch)); } if (Error E = Entries.back()->cfis().parse(Data, &Offset, EndStructureOffset)) { report_fatal_error(toString(std::move(E))); } if (Offset != EndStructureOffset) ReportError(StartOffset, "Parsing entry instructions at %" PRIx64 " failed"); } } FrameEntry *DWARFDebugFrame::getEntryAtOffset(uint64_t Offset) const { auto It = partition_point(Entries, [=](const std::unique_ptr &E) { return E->getOffset() < Offset; }); if (It != Entries.end() && (*It)->getOffset() == Offset) return It->get(); return nullptr; } void DWARFDebugFrame::dump(raw_ostream &OS, const MCRegisterInfo *MRI, Optional Offset) const { if (Offset) { if (auto *Entry = getEntryAtOffset(*Offset)) Entry->dump(OS, MRI, IsEH); return; } OS << "\n"; for (const auto &Entry : Entries) Entry->dump(OS, MRI, IsEH); } binaryen-version_91/third_party/llvm-project/DWARFDebugInfoEntry.cpp000066400000000000000000000053501362402614000260050ustar00rootroot00000000000000//===- DWARFDebugInfoEntry.cpp --------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFDebugInfoEntry.h" #include "llvm/ADT/Optional.h" #include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h" #include "llvm/DebugInfo/DWARF/DWARFFormValue.h" #include "llvm/DebugInfo/DWARF/DWARFUnit.h" #include "llvm/Support/DataExtractor.h" #include #include using namespace llvm; using namespace dwarf; bool DWARFDebugInfoEntry::extractFast(const DWARFUnit &U, uint64_t *OffsetPtr) { DWARFDataExtractor DebugInfoData = U.getDebugInfoExtractor(); const uint64_t UEndOffset = U.getNextUnitOffset(); return extractFast(U, OffsetPtr, DebugInfoData, UEndOffset, 0); } bool DWARFDebugInfoEntry::extractFast(const DWARFUnit &U, uint64_t *OffsetPtr, const DWARFDataExtractor &DebugInfoData, uint64_t UEndOffset, uint32_t D) { Offset = *OffsetPtr; Depth = D; if (Offset >= UEndOffset || !DebugInfoData.isValidOffset(Offset)) return false; uint64_t AbbrCode = DebugInfoData.getULEB128(OffsetPtr); if (0 == AbbrCode) { // NULL debug tag entry. AbbrevDecl = nullptr; return true; } if (auto* Abbreviations = U.getAbbreviations()) { // XXX BINARYEN AbbrevDecl = Abbreviations->getAbbreviationDeclaration(AbbrCode); } else { AbbrevDecl = nullptr; // XXX BINARYEN } if (nullptr == AbbrevDecl) { // Restore the original offset. *OffsetPtr = Offset; return false; } // See if all attributes in this DIE have fixed byte sizes. If so, we can // just add this size to the offset to skip to the next DIE. if (Optional FixedSize = AbbrevDecl->getFixedAttributesByteSize(U)) { *OffsetPtr += *FixedSize; return true; } // Skip all data in the .debug_info for the attributes for (const auto &AttrSpec : AbbrevDecl->attributes()) { // Check if this attribute has a fixed byte size. if (auto FixedSize = AttrSpec.getByteSize(U)) { // Attribute byte size if fixed, just add the size to the offset. *OffsetPtr += *FixedSize; } else if (!DWARFFormValue::skipValue(AttrSpec.Form, DebugInfoData, OffsetPtr, U.getFormParams())) { // We failed to skip this attribute's value, restore the original offset // and return the failure status. *OffsetPtr = Offset; return false; } } return true; } binaryen-version_91/third_party/llvm-project/DWARFDebugLine.cpp000066400000000000000000001311551362402614000247620ustar00rootroot00000000000000//===- DWARFDebugLine.cpp -------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFDebugLine.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/DebugInfo/DWARF/DWARFFormValue.h" #include "llvm/DebugInfo/DWARF/DWARFRelocMap.h" #include "llvm/Support/Errc.h" #include "llvm/Support/Format.h" #include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include #include #include #include #include #include using namespace llvm; using namespace dwarf; using FileLineInfoKind = DILineInfoSpecifier::FileLineInfoKind; namespace { struct ContentDescriptor { dwarf::LineNumberEntryFormat Type; dwarf::Form Form; }; using ContentDescriptors = SmallVector; } // end anonmyous namespace void DWARFDebugLine::ContentTypeTracker::trackContentType( dwarf::LineNumberEntryFormat ContentType) { switch (ContentType) { case dwarf::DW_LNCT_timestamp: HasModTime = true; break; case dwarf::DW_LNCT_size: HasLength = true; break; case dwarf::DW_LNCT_MD5: HasMD5 = true; break; case dwarf::DW_LNCT_LLVM_source: HasSource = true; break; default: // We only care about values we consider optional, and new values may be // added in the vendor extension range, so we do not match exhaustively. break; } } DWARFDebugLine::Prologue::Prologue() { clear(); } bool DWARFDebugLine::Prologue::hasFileAtIndex(uint64_t FileIndex) const { uint16_t DwarfVersion = getVersion(); assert(DwarfVersion != 0 && "line table prologue has no dwarf version information"); if (DwarfVersion >= 5) return FileIndex < FileNames.size(); return FileIndex != 0 && FileIndex <= FileNames.size(); } const llvm::DWARFDebugLine::FileNameEntry & DWARFDebugLine::Prologue::getFileNameEntry(uint64_t Index) const { uint16_t DwarfVersion = getVersion(); assert(DwarfVersion != 0 && "line table prologue has no dwarf version information"); // In DWARF v5 the file names are 0-indexed. if (DwarfVersion >= 5) return FileNames[Index]; return FileNames[Index - 1]; } void DWARFDebugLine::Prologue::clear() { TotalLength = PrologueLength = 0; SegSelectorSize = 0; MinInstLength = MaxOpsPerInst = DefaultIsStmt = LineBase = LineRange = 0; OpcodeBase = 0; FormParams = dwarf::FormParams({0, 0, DWARF32}); ContentTypes = ContentTypeTracker(); StandardOpcodeLengths.clear(); IncludeDirectories.clear(); FileNames.clear(); } void DWARFDebugLine::Prologue::dump(raw_ostream &OS, DIDumpOptions DumpOptions) const { OS << "Line table prologue:\n" << format(" total_length: 0x%8.8" PRIx64 "\n", TotalLength) << format(" version: %u\n", getVersion()); if (getVersion() >= 5) OS << format(" address_size: %u\n", getAddressSize()) << format(" seg_select_size: %u\n", SegSelectorSize); OS << format(" prologue_length: 0x%8.8" PRIx64 "\n", PrologueLength) << format(" min_inst_length: %u\n", MinInstLength) << format(getVersion() >= 4 ? "max_ops_per_inst: %u\n" : "", MaxOpsPerInst) << format(" default_is_stmt: %u\n", DefaultIsStmt) << format(" line_base: %i\n", LineBase) << format(" line_range: %u\n", LineRange) << format(" opcode_base: %u\n", OpcodeBase); for (uint32_t I = 0; I != StandardOpcodeLengths.size(); ++I) OS << format("standard_opcode_lengths[%s] = %u\n", LNStandardString(I + 1).data(), StandardOpcodeLengths[I]); if (!IncludeDirectories.empty()) { // DWARF v5 starts directory indexes at 0. uint32_t DirBase = getVersion() >= 5 ? 0 : 1; for (uint32_t I = 0; I != IncludeDirectories.size(); ++I) { OS << format("include_directories[%3u] = ", I + DirBase); IncludeDirectories[I].dump(OS, DumpOptions); OS << '\n'; } } if (!FileNames.empty()) { // DWARF v5 starts file indexes at 0. uint32_t FileBase = getVersion() >= 5 ? 0 : 1; for (uint32_t I = 0; I != FileNames.size(); ++I) { const FileNameEntry &FileEntry = FileNames[I]; OS << format("file_names[%3u]:\n", I + FileBase); OS << " name: "; FileEntry.Name.dump(OS, DumpOptions); OS << '\n' << format(" dir_index: %" PRIu64 "\n", FileEntry.DirIdx); if (ContentTypes.HasMD5) OS << " md5_checksum: " << FileEntry.Checksum.digest() << '\n'; if (ContentTypes.HasModTime) OS << format(" mod_time: 0x%8.8" PRIx64 "\n", FileEntry.ModTime); if (ContentTypes.HasLength) OS << format(" length: 0x%8.8" PRIx64 "\n", FileEntry.Length); if (ContentTypes.HasSource) { OS << " source: "; FileEntry.Source.dump(OS, DumpOptions); OS << '\n'; } } } } // Parse v2-v4 directory and file tables. static void parseV2DirFileTables(const DWARFDataExtractor &DebugLineData, uint64_t *OffsetPtr, uint64_t EndPrologueOffset, DWARFDebugLine::ContentTypeTracker &ContentTypes, std::vector &IncludeDirectories, std::vector &FileNames) { while (*OffsetPtr < EndPrologueOffset) { StringRef S = DebugLineData.getCStrRef(OffsetPtr); if (S.empty()) break; DWARFFormValue Dir = DWARFFormValue::createFromPValue(dwarf::DW_FORM_string, S.data()); IncludeDirectories.push_back(Dir); } while (*OffsetPtr < EndPrologueOffset) { StringRef Name = DebugLineData.getCStrRef(OffsetPtr); if (Name.empty()) break; DWARFDebugLine::FileNameEntry FileEntry; FileEntry.Name = DWARFFormValue::createFromPValue(dwarf::DW_FORM_string, Name.data()); FileEntry.DirIdx = DebugLineData.getULEB128(OffsetPtr); FileEntry.ModTime = DebugLineData.getULEB128(OffsetPtr); FileEntry.Length = DebugLineData.getULEB128(OffsetPtr); FileNames.push_back(FileEntry); } ContentTypes.HasModTime = true; ContentTypes.HasLength = true; } // Parse v5 directory/file entry content descriptions. // Returns the descriptors, or an error if we did not find a path or ran off // the end of the prologue. static llvm::Expected parseV5EntryFormat(const DWARFDataExtractor &DebugLineData, uint64_t *OffsetPtr, uint64_t EndPrologueOffset, DWARFDebugLine::ContentTypeTracker *ContentTypes) { ContentDescriptors Descriptors; int FormatCount = DebugLineData.getU8(OffsetPtr); bool HasPath = false; for (int I = 0; I != FormatCount; ++I) { if (*OffsetPtr >= EndPrologueOffset) return createStringError( errc::invalid_argument, "failed to parse entry content descriptions at offset " "0x%8.8" PRIx64 " because offset extends beyond the prologue end at offset " "0x%8.8" PRIx64, *OffsetPtr, EndPrologueOffset); ContentDescriptor Descriptor; Descriptor.Type = dwarf::LineNumberEntryFormat(DebugLineData.getULEB128(OffsetPtr)); Descriptor.Form = dwarf::Form(DebugLineData.getULEB128(OffsetPtr)); if (Descriptor.Type == dwarf::DW_LNCT_path) HasPath = true; if (ContentTypes) ContentTypes->trackContentType(Descriptor.Type); Descriptors.push_back(Descriptor); } if (!HasPath) return createStringError(errc::invalid_argument, "failed to parse entry content descriptions" " because no path was found"); return Descriptors; } static Error parseV5DirFileTables(const DWARFDataExtractor &DebugLineData, uint64_t *OffsetPtr, uint64_t EndPrologueOffset, const dwarf::FormParams &FormParams, const DWARFContext &Ctx, const DWARFUnit *U, DWARFDebugLine::ContentTypeTracker &ContentTypes, std::vector &IncludeDirectories, std::vector &FileNames) { // Get the directory entry description. llvm::Expected DirDescriptors = parseV5EntryFormat(DebugLineData, OffsetPtr, EndPrologueOffset, nullptr); if (!DirDescriptors) return DirDescriptors.takeError(); // Get the directory entries, according to the format described above. int DirEntryCount = DebugLineData.getU8(OffsetPtr); for (int I = 0; I != DirEntryCount; ++I) { if (*OffsetPtr >= EndPrologueOffset) return createStringError( errc::invalid_argument, "failed to parse directory entry at offset " "0x%8.8" PRIx64 " because offset extends beyond the prologue end at offset " "0x%8.8" PRIx64, *OffsetPtr, EndPrologueOffset); for (auto Descriptor : *DirDescriptors) { DWARFFormValue Value(Descriptor.Form); switch (Descriptor.Type) { case DW_LNCT_path: if (!Value.extractValue(DebugLineData, OffsetPtr, FormParams, &Ctx, U)) return createStringError(errc::invalid_argument, "failed to parse directory entry because " "extracting the form value failed."); IncludeDirectories.push_back(Value); break; default: if (!Value.skipValue(DebugLineData, OffsetPtr, FormParams)) return createStringError(errc::invalid_argument, "failed to parse directory entry because " "skipping the form value failed."); } } } // Get the file entry description. llvm::Expected FileDescriptors = parseV5EntryFormat( DebugLineData, OffsetPtr, EndPrologueOffset, &ContentTypes); if (!FileDescriptors) return FileDescriptors.takeError(); // Get the file entries, according to the format described above. int FileEntryCount = DebugLineData.getU8(OffsetPtr); for (int I = 0; I != FileEntryCount; ++I) { if (*OffsetPtr >= EndPrologueOffset) return createStringError( errc::invalid_argument, "failed to parse file entry at offset " "0x%8.8" PRIx64 " because offset extends beyond the prologue end at offset " "0x%8.8" PRIx64, *OffsetPtr, EndPrologueOffset); DWARFDebugLine::FileNameEntry FileEntry; for (auto Descriptor : *FileDescriptors) { DWARFFormValue Value(Descriptor.Form); if (!Value.extractValue(DebugLineData, OffsetPtr, FormParams, &Ctx, U)) return createStringError(errc::invalid_argument, "failed to parse file entry because " "extracting the form value failed."); switch (Descriptor.Type) { case DW_LNCT_path: FileEntry.Name = Value; break; case DW_LNCT_LLVM_source: FileEntry.Source = Value; break; case DW_LNCT_directory_index: FileEntry.DirIdx = Value.getAsUnsignedConstant().getValue(); break; case DW_LNCT_timestamp: FileEntry.ModTime = Value.getAsUnsignedConstant().getValue(); break; case DW_LNCT_size: FileEntry.Length = Value.getAsUnsignedConstant().getValue(); break; case DW_LNCT_MD5: if (!Value.getAsBlock() || Value.getAsBlock().getValue().size() != 16) return createStringError( errc::invalid_argument, "failed to parse file entry because the MD5 hash is invalid"); std::uninitialized_copy_n(Value.getAsBlock().getValue().begin(), 16, FileEntry.Checksum.Bytes.begin()); break; default: break; } } FileNames.push_back(FileEntry); } return Error::success(); } Error DWARFDebugLine::Prologue::parse(const DWARFDataExtractor &DebugLineData, uint64_t *OffsetPtr, const DWARFContext &Ctx, const DWARFUnit *U) { const uint64_t PrologueOffset = *OffsetPtr; clear(); TotalLength = DebugLineData.getRelocatedValue(4, OffsetPtr); if (TotalLength == dwarf::DW_LENGTH_DWARF64) { FormParams.Format = dwarf::DWARF64; TotalLength = DebugLineData.getU64(OffsetPtr); } else if (TotalLength >= dwarf::DW_LENGTH_lo_reserved) { return createStringError(errc::invalid_argument, "parsing line table prologue at offset 0x%8.8" PRIx64 " unsupported reserved unit length found of value 0x%8.8" PRIx64, PrologueOffset, TotalLength); } FormParams.Version = DebugLineData.getU16(OffsetPtr); if (getVersion() < 2) return createStringError(errc::not_supported, "parsing line table prologue at offset 0x%8.8" PRIx64 " found unsupported version 0x%2.2" PRIx16, PrologueOffset, getVersion()); if (getVersion() >= 5) { FormParams.AddrSize = DebugLineData.getU8(OffsetPtr); assert((DebugLineData.getAddressSize() == 0 || DebugLineData.getAddressSize() == getAddressSize()) && "Line table header and data extractor disagree"); SegSelectorSize = DebugLineData.getU8(OffsetPtr); } PrologueLength = DebugLineData.getRelocatedValue(sizeofPrologueLength(), OffsetPtr); const uint64_t EndPrologueOffset = PrologueLength + *OffsetPtr; MinInstLength = DebugLineData.getU8(OffsetPtr); if (getVersion() >= 4) MaxOpsPerInst = DebugLineData.getU8(OffsetPtr); DefaultIsStmt = DebugLineData.getU8(OffsetPtr); LineBase = DebugLineData.getU8(OffsetPtr); LineRange = DebugLineData.getU8(OffsetPtr); OpcodeBase = DebugLineData.getU8(OffsetPtr); StandardOpcodeLengths.reserve(OpcodeBase - 1); for (uint32_t I = 1; I < OpcodeBase; ++I) { uint8_t OpLen = DebugLineData.getU8(OffsetPtr); StandardOpcodeLengths.push_back(OpLen); } if (getVersion() >= 5) { if (Error e = parseV5DirFileTables( DebugLineData, OffsetPtr, EndPrologueOffset, FormParams, Ctx, U, ContentTypes, IncludeDirectories, FileNames)) { return joinErrors( createStringError( errc::invalid_argument, "parsing line table prologue at 0x%8.8" PRIx64 " found an invalid directory or file table description at" " 0x%8.8" PRIx64, PrologueOffset, *OffsetPtr), std::move(e)); } } else parseV2DirFileTables(DebugLineData, OffsetPtr, EndPrologueOffset, ContentTypes, IncludeDirectories, FileNames); if (*OffsetPtr != EndPrologueOffset) return createStringError(errc::invalid_argument, "parsing line table prologue at 0x%8.8" PRIx64 " should have ended at 0x%8.8" PRIx64 " but it ended at 0x%8.8" PRIx64, PrologueOffset, EndPrologueOffset, *OffsetPtr); return Error::success(); } DWARFDebugLine::Row::Row(bool DefaultIsStmt) { reset(DefaultIsStmt); } void DWARFDebugLine::Row::postAppend() { Discriminator = 0; BasicBlock = false; PrologueEnd = false; EpilogueBegin = false; } void DWARFDebugLine::Row::reset(bool DefaultIsStmt) { Address.Address = 0; Address.SectionIndex = object::SectionedAddress::UndefSection; Line = 1; Column = 0; File = 1; Isa = 0; Discriminator = 0; IsStmt = DefaultIsStmt; BasicBlock = false; EndSequence = false; PrologueEnd = false; EpilogueBegin = false; } void DWARFDebugLine::Row::dumpTableHeader(raw_ostream &OS) { OS << "Address Line Column File ISA Discriminator Flags\n" << "------------------ ------ ------ ------ --- ------------- " "-------------\n"; } void DWARFDebugLine::Row::dump(raw_ostream &OS) const { OS << format("0x%16.16" PRIx64 " %6u %6u", Address.Address, Line, Column) << format(" %6u %3u %13u ", File, Isa, Discriminator) << (IsStmt ? " is_stmt" : "") << (BasicBlock ? " basic_block" : "") << (PrologueEnd ? " prologue_end" : "") << (EpilogueBegin ? " epilogue_begin" : "") << (EndSequence ? " end_sequence" : "") << '\n'; } DWARFDebugLine::Sequence::Sequence() { reset(); } void DWARFDebugLine::Sequence::reset() { LowPC = 0; HighPC = 0; SectionIndex = object::SectionedAddress::UndefSection; FirstRowIndex = 0; LastRowIndex = 0; Empty = true; } DWARFDebugLine::LineTable::LineTable() { clear(); } void DWARFDebugLine::LineTable::dump(raw_ostream &OS, DIDumpOptions DumpOptions) const { Prologue.dump(OS, DumpOptions); OS << '\n'; if (!Rows.empty()) { Row::dumpTableHeader(OS); for (const Row &R : Rows) { R.dump(OS); } } } void DWARFDebugLine::LineTable::clear() { Prologue.clear(); Rows.clear(); Sequences.clear(); } DWARFDebugLine::ParsingState::ParsingState(struct LineTable *LT) : LineTable(LT) { resetRowAndSequence(); } void DWARFDebugLine::ParsingState::resetRowAndSequence() { Row.reset(LineTable->Prologue.DefaultIsStmt); Sequence.reset(); } void DWARFDebugLine::ParsingState::appendRowToMatrix() { unsigned RowNumber = LineTable->Rows.size(); if (Sequence.Empty) { // Record the beginning of instruction sequence. Sequence.Empty = false; Sequence.LowPC = Row.Address.Address; Sequence.FirstRowIndex = RowNumber; } LineTable->appendRow(Row); if (Row.EndSequence) { // Record the end of instruction sequence. Sequence.HighPC = Row.Address.Address; Sequence.LastRowIndex = RowNumber + 1; Sequence.SectionIndex = Row.Address.SectionIndex; if (Sequence.isValid()) LineTable->appendSequence(Sequence); Sequence.reset(); } Row.postAppend(); } const DWARFDebugLine::LineTable * DWARFDebugLine::getLineTable(uint64_t Offset) const { LineTableConstIter Pos = LineTableMap.find(Offset); if (Pos != LineTableMap.end()) return &Pos->second; return nullptr; } Expected DWARFDebugLine::getOrParseLineTable( DWARFDataExtractor &DebugLineData, uint64_t Offset, const DWARFContext &Ctx, const DWARFUnit *U, std::function RecoverableErrorCallback) { if (!DebugLineData.isValidOffset(Offset)) return createStringError(errc::invalid_argument, "offset 0x%8.8" PRIx64 " is not a valid debug line section offset", Offset); std::pair Pos = LineTableMap.insert(LineTableMapTy::value_type(Offset, LineTable())); LineTable *LT = &Pos.first->second; if (Pos.second) { if (Error Err = LT->parse(DebugLineData, &Offset, Ctx, U, RecoverableErrorCallback)) return std::move(Err); return LT; } return LT; } Error DWARFDebugLine::LineTable::parse( DWARFDataExtractor &DebugLineData, uint64_t *OffsetPtr, const DWARFContext &Ctx, const DWARFUnit *U, std::function RecoverableErrorCallback, raw_ostream *OS) { const uint64_t DebugLineOffset = *OffsetPtr; clear(); Error PrologueErr = Prologue.parse(DebugLineData, OffsetPtr, Ctx, U); if (OS) { // The presence of OS signals verbose dumping. DIDumpOptions DumpOptions; DumpOptions.Verbose = true; Prologue.dump(*OS, DumpOptions); } if (PrologueErr) return PrologueErr; const uint64_t EndOffset = DebugLineOffset + Prologue.TotalLength + Prologue.sizeofTotalLength(); // See if we should tell the data extractor the address size. if (DebugLineData.getAddressSize() == 0) DebugLineData.setAddressSize(Prologue.getAddressSize()); else assert(Prologue.getAddressSize() == 0 || Prologue.getAddressSize() == DebugLineData.getAddressSize()); ParsingState State(this); while (*OffsetPtr < EndOffset) { if (OS) *OS << format("0x%08.08" PRIx64 ": ", *OffsetPtr); uint8_t Opcode = DebugLineData.getU8(OffsetPtr); if (OS) *OS << format("%02.02" PRIx8 " ", Opcode); if (Opcode == 0) { // Extended Opcodes always start with a zero opcode followed by // a uleb128 length so you can skip ones you don't know about uint64_t Len = DebugLineData.getULEB128(OffsetPtr); uint64_t ExtOffset = *OffsetPtr; // Tolerate zero-length; assume length is correct and soldier on. if (Len == 0) { if (OS) *OS << "Badly formed extended line op (length 0)\n"; continue; } uint8_t SubOpcode = DebugLineData.getU8(OffsetPtr); if (OS) *OS << LNExtendedString(SubOpcode); switch (SubOpcode) { case DW_LNE_end_sequence: // Set the end_sequence register of the state machine to true and // append a row to the matrix using the current values of the // state-machine registers. Then reset the registers to the initial // values specified above. Every statement program sequence must end // with a DW_LNE_end_sequence instruction which creates a row whose // address is that of the byte after the last target machine instruction // of the sequence. State.Row.EndSequence = true; State.appendRowToMatrix(); if (OS) { *OS << "\n"; OS->indent(12); State.Row.dump(*OS); } State.resetRowAndSequence(); break; case DW_LNE_set_address: // Takes a single relocatable address as an operand. The size of the // operand is the size appropriate to hold an address on the target // machine. Set the address register to the value given by the // relocatable address. All of the other statement program opcodes // that affect the address register add a delta to it. This instruction // stores a relocatable value into it instead. // // Make sure the extractor knows the address size. If not, infer it // from the size of the operand. if (DebugLineData.getAddressSize() == 0) DebugLineData.setAddressSize(Len - 1); else if (DebugLineData.getAddressSize() != Len - 1) { return createStringError(errc::invalid_argument, "mismatching address size at offset 0x%8.8" PRIx64 " expected 0x%2.2" PRIx8 " found 0x%2.2" PRIx64, ExtOffset, DebugLineData.getAddressSize(), Len - 1); } State.Row.Address.Address = DebugLineData.getRelocatedAddress( OffsetPtr, &State.Row.Address.SectionIndex); if (OS) *OS << format(" (0x%16.16" PRIx64 ")", State.Row.Address.Address); break; case DW_LNE_define_file: // Takes 4 arguments. The first is a null terminated string containing // a source file name. The second is an unsigned LEB128 number // representing the directory index of the directory in which the file // was found. The third is an unsigned LEB128 number representing the // time of last modification of the file. The fourth is an unsigned // LEB128 number representing the length in bytes of the file. The time // and length fields may contain LEB128(0) if the information is not // available. // // The directory index represents an entry in the include_directories // section of the statement program prologue. The index is LEB128(0) // if the file was found in the current directory of the compilation, // LEB128(1) if it was found in the first directory in the // include_directories section, and so on. The directory index is // ignored for file names that represent full path names. // // The files are numbered, starting at 1, in the order in which they // appear; the names in the prologue come before names defined by // the DW_LNE_define_file instruction. These numbers are used in the // the file register of the state machine. { FileNameEntry FileEntry; const char *Name = DebugLineData.getCStr(OffsetPtr); FileEntry.Name = DWARFFormValue::createFromPValue(dwarf::DW_FORM_string, Name); FileEntry.DirIdx = DebugLineData.getULEB128(OffsetPtr); FileEntry.ModTime = DebugLineData.getULEB128(OffsetPtr); FileEntry.Length = DebugLineData.getULEB128(OffsetPtr); Prologue.FileNames.push_back(FileEntry); if (OS) *OS << " (" << Name << ", dir=" << FileEntry.DirIdx << ", mod_time=" << format("(0x%16.16" PRIx64 ")", FileEntry.ModTime) << ", length=" << FileEntry.Length << ")"; } break; case DW_LNE_set_discriminator: State.Row.Discriminator = DebugLineData.getULEB128(OffsetPtr); if (OS) *OS << " (" << State.Row.Discriminator << ")"; break; default: if (OS) *OS << format("Unrecognized extended op 0x%02.02" PRIx8, SubOpcode) << format(" length %" PRIx64, Len); // Len doesn't include the zero opcode byte or the length itself, but // it does include the sub_opcode, so we have to adjust for that. (*OffsetPtr) += Len - 1; break; } // Make sure the stated and parsed lengths are the same. // Otherwise we have an unparseable line-number program. if (*OffsetPtr - ExtOffset != Len) return createStringError(errc::illegal_byte_sequence, "unexpected line op length at offset 0x%8.8" PRIx64 " expected 0x%2.2" PRIx64 " found 0x%2.2" PRIx64, ExtOffset, Len, *OffsetPtr - ExtOffset); } else if (Opcode < Prologue.OpcodeBase) { if (OS) *OS << LNStandardString(Opcode); switch (Opcode) { // Standard Opcodes case DW_LNS_copy: // Takes no arguments. Append a row to the matrix using the // current values of the state-machine registers. if (OS) { *OS << "\n"; OS->indent(12); State.Row.dump(*OS); *OS << "\n"; } State.appendRowToMatrix(); break; case DW_LNS_advance_pc: // Takes a single unsigned LEB128 operand, multiplies it by the // min_inst_length field of the prologue, and adds the // result to the address register of the state machine. { uint64_t AddrOffset = DebugLineData.getULEB128(OffsetPtr) * Prologue.MinInstLength; State.Row.Address.Address += AddrOffset; if (OS) *OS << " (" << AddrOffset << ")"; } break; case DW_LNS_advance_line: // Takes a single signed LEB128 operand and adds that value to // the line register of the state machine. State.Row.Line += DebugLineData.getSLEB128(OffsetPtr); if (OS) *OS << " (" << State.Row.Line << ")"; break; case DW_LNS_set_file: // Takes a single unsigned LEB128 operand and stores it in the file // register of the state machine. State.Row.File = DebugLineData.getULEB128(OffsetPtr); if (OS) *OS << " (" << State.Row.File << ")"; break; case DW_LNS_set_column: // Takes a single unsigned LEB128 operand and stores it in the // column register of the state machine. State.Row.Column = DebugLineData.getULEB128(OffsetPtr); if (OS) *OS << " (" << State.Row.Column << ")"; break; case DW_LNS_negate_stmt: // Takes no arguments. Set the is_stmt register of the state // machine to the logical negation of its current value. State.Row.IsStmt = !State.Row.IsStmt; break; case DW_LNS_set_basic_block: // Takes no arguments. Set the basic_block register of the // state machine to true State.Row.BasicBlock = true; break; case DW_LNS_const_add_pc: // Takes no arguments. Add to the address register of the state // machine the address increment value corresponding to special // opcode 255. The motivation for DW_LNS_const_add_pc is this: // when the statement program needs to advance the address by a // small amount, it can use a single special opcode, which occupies // a single byte. When it needs to advance the address by up to // twice the range of the last special opcode, it can use // DW_LNS_const_add_pc followed by a special opcode, for a total // of two bytes. Only if it needs to advance the address by more // than twice that range will it need to use both DW_LNS_advance_pc // and a special opcode, requiring three or more bytes. { uint8_t AdjustOpcode = 255 - Prologue.OpcodeBase; uint64_t AddrOffset = (AdjustOpcode / Prologue.LineRange) * Prologue.MinInstLength; State.Row.Address.Address += AddrOffset; if (OS) *OS << format(" (0x%16.16" PRIx64 ")", AddrOffset); } break; case DW_LNS_fixed_advance_pc: // Takes a single uhalf operand. Add to the address register of // the state machine the value of the (unencoded) operand. This // is the only extended opcode that takes an argument that is not // a variable length number. The motivation for DW_LNS_fixed_advance_pc // is this: existing assemblers cannot emit DW_LNS_advance_pc or // special opcodes because they cannot encode LEB128 numbers or // judge when the computation of a special opcode overflows and // requires the use of DW_LNS_advance_pc. Such assemblers, however, // can use DW_LNS_fixed_advance_pc instead, sacrificing compression. { uint16_t PCOffset = DebugLineData.getRelocatedValue(2, OffsetPtr); State.Row.Address.Address += PCOffset; if (OS) *OS << format(" (0x%4.4" PRIx16 ")", PCOffset); } break; case DW_LNS_set_prologue_end: // Takes no arguments. Set the prologue_end register of the // state machine to true State.Row.PrologueEnd = true; break; case DW_LNS_set_epilogue_begin: // Takes no arguments. Set the basic_block register of the // state machine to true State.Row.EpilogueBegin = true; break; case DW_LNS_set_isa: // Takes a single unsigned LEB128 operand and stores it in the // column register of the state machine. State.Row.Isa = DebugLineData.getULEB128(OffsetPtr); if (OS) *OS << " (" << State.Row.Isa << ")"; break; default: // Handle any unknown standard opcodes here. We know the lengths // of such opcodes because they are specified in the prologue // as a multiple of LEB128 operands for each opcode. { assert(Opcode - 1U < Prologue.StandardOpcodeLengths.size()); uint8_t OpcodeLength = Prologue.StandardOpcodeLengths[Opcode - 1]; for (uint8_t I = 0; I < OpcodeLength; ++I) { uint64_t Value = DebugLineData.getULEB128(OffsetPtr); if (OS) *OS << format("Skipping ULEB128 value: 0x%16.16" PRIx64 ")\n", Value); } } break; } } else { // Special Opcodes // A special opcode value is chosen based on the amount that needs // to be added to the line and address registers. The maximum line // increment for a special opcode is the value of the line_base // field in the header, plus the value of the line_range field, // minus 1 (line base + line range - 1). If the desired line // increment is greater than the maximum line increment, a standard // opcode must be used instead of a special opcode. The "address // advance" is calculated by dividing the desired address increment // by the minimum_instruction_length field from the header. The // special opcode is then calculated using the following formula: // // opcode = (desired line increment - line_base) + // (line_range * address advance) + opcode_base // // If the resulting opcode is greater than 255, a standard opcode // must be used instead. // // To decode a special opcode, subtract the opcode_base from the // opcode itself to give the adjusted opcode. The amount to // increment the address register is the result of the adjusted // opcode divided by the line_range multiplied by the // minimum_instruction_length field from the header. That is: // // address increment = (adjusted opcode / line_range) * // minimum_instruction_length // // The amount to increment the line register is the line_base plus // the result of the adjusted opcode modulo the line_range. That is: // // line increment = line_base + (adjusted opcode % line_range) uint8_t AdjustOpcode = Opcode - Prologue.OpcodeBase; uint64_t AddrOffset = (AdjustOpcode / Prologue.LineRange) * Prologue.MinInstLength; int32_t LineOffset = Prologue.LineBase + (AdjustOpcode % Prologue.LineRange); State.Row.Line += LineOffset; State.Row.Address.Address += AddrOffset; if (OS) { *OS << "address += " << AddrOffset << ", line += " << LineOffset << "\n"; OS->indent(12); State.Row.dump(*OS); } State.appendRowToMatrix(); } if(OS) *OS << "\n"; } if (!State.Sequence.Empty) RecoverableErrorCallback( createStringError(errc::illegal_byte_sequence, "last sequence in debug line table is not terminated!")); // Sort all sequences so that address lookup will work faster. if (!Sequences.empty()) { llvm::sort(Sequences, Sequence::orderByHighPC); // Note: actually, instruction address ranges of sequences should not // overlap (in shared objects and executables). If they do, the address // lookup would still work, though, but result would be ambiguous. // We don't report warning in this case. For example, // sometimes .so compiled from multiple object files contains a few // rudimentary sequences for address ranges [0x0, 0xsomething). } return Error::success(); } uint32_t DWARFDebugLine::LineTable::findRowInSeq( const DWARFDebugLine::Sequence &Seq, object::SectionedAddress Address) const { if (!Seq.containsPC(Address)) return UnknownRowIndex; assert(Seq.SectionIndex == Address.SectionIndex); // In some cases, e.g. first instruction in a function, the compiler generates // two entries, both with the same address. We want the last one. // // In general we want a non-empty range: the last row whose address is less // than or equal to Address. This can be computed as upper_bound - 1. DWARFDebugLine::Row Row; Row.Address = Address; RowIter FirstRow = Rows.begin() + Seq.FirstRowIndex; RowIter LastRow = Rows.begin() + Seq.LastRowIndex; assert(FirstRow->Address.Address <= Row.Address.Address && Row.Address.Address < LastRow[-1].Address.Address); RowIter RowPos = std::upper_bound(FirstRow + 1, LastRow - 1, Row, DWARFDebugLine::Row::orderByAddress) - 1; assert(Seq.SectionIndex == RowPos->Address.SectionIndex); return RowPos - Rows.begin(); } uint32_t DWARFDebugLine::LineTable::lookupAddress( object::SectionedAddress Address) const { // Search for relocatable addresses uint32_t Result = lookupAddressImpl(Address); if (Result != UnknownRowIndex || Address.SectionIndex == object::SectionedAddress::UndefSection) return Result; // Search for absolute addresses Address.SectionIndex = object::SectionedAddress::UndefSection; return lookupAddressImpl(Address); } uint32_t DWARFDebugLine::LineTable::lookupAddressImpl( object::SectionedAddress Address) const { // First, find an instruction sequence containing the given address. DWARFDebugLine::Sequence Sequence; Sequence.SectionIndex = Address.SectionIndex; Sequence.HighPC = Address.Address; SequenceIter It = llvm::upper_bound(Sequences, Sequence, DWARFDebugLine::Sequence::orderByHighPC); if (It == Sequences.end() || It->SectionIndex != Address.SectionIndex) return UnknownRowIndex; return findRowInSeq(*It, Address); } bool DWARFDebugLine::LineTable::lookupAddressRange( object::SectionedAddress Address, uint64_t Size, std::vector &Result) const { // Search for relocatable addresses if (lookupAddressRangeImpl(Address, Size, Result)) return true; if (Address.SectionIndex == object::SectionedAddress::UndefSection) return false; // Search for absolute addresses Address.SectionIndex = object::SectionedAddress::UndefSection; return lookupAddressRangeImpl(Address, Size, Result); } bool DWARFDebugLine::LineTable::lookupAddressRangeImpl( object::SectionedAddress Address, uint64_t Size, std::vector &Result) const { if (Sequences.empty()) return false; uint64_t EndAddr = Address.Address + Size; // First, find an instruction sequence containing the given address. DWARFDebugLine::Sequence Sequence; Sequence.SectionIndex = Address.SectionIndex; Sequence.HighPC = Address.Address; SequenceIter LastSeq = Sequences.end(); SequenceIter SeqPos = llvm::upper_bound( Sequences, Sequence, DWARFDebugLine::Sequence::orderByHighPC); if (SeqPos == LastSeq || !SeqPos->containsPC(Address)) return false; SequenceIter StartPos = SeqPos; // Add the rows from the first sequence to the vector, starting with the // index we just calculated while (SeqPos != LastSeq && SeqPos->LowPC < EndAddr) { const DWARFDebugLine::Sequence &CurSeq = *SeqPos; // For the first sequence, we need to find which row in the sequence is the // first in our range. uint32_t FirstRowIndex = CurSeq.FirstRowIndex; if (SeqPos == StartPos) FirstRowIndex = findRowInSeq(CurSeq, Address); // Figure out the last row in the range. uint32_t LastRowIndex = findRowInSeq(CurSeq, {EndAddr - 1, Address.SectionIndex}); if (LastRowIndex == UnknownRowIndex) LastRowIndex = CurSeq.LastRowIndex - 1; assert(FirstRowIndex != UnknownRowIndex); assert(LastRowIndex != UnknownRowIndex); for (uint32_t I = FirstRowIndex; I <= LastRowIndex; ++I) { Result.push_back(I); } ++SeqPos; } return true; } Optional DWARFDebugLine::LineTable::getSourceByIndex(uint64_t FileIndex, FileLineInfoKind Kind) const { if (Kind == FileLineInfoKind::None || !Prologue.hasFileAtIndex(FileIndex)) return None; const FileNameEntry &Entry = Prologue.getFileNameEntry(FileIndex); if (Optional source = Entry.Source.getAsCString()) return StringRef(*source); return None; } static bool isPathAbsoluteOnWindowsOrPosix(const Twine &Path) { // Debug info can contain paths from any OS, not necessarily // an OS we're currently running on. Moreover different compilation units can // be compiled on different operating systems and linked together later. return sys::path::is_absolute(Path, sys::path::Style::posix) || sys::path::is_absolute(Path, sys::path::Style::windows); } bool DWARFDebugLine::Prologue::getFileNameByIndex( uint64_t FileIndex, StringRef CompDir, FileLineInfoKind Kind, std::string &Result, sys::path::Style Style) const { if (Kind == FileLineInfoKind::None || !hasFileAtIndex(FileIndex)) return false; const FileNameEntry &Entry = getFileNameEntry(FileIndex); StringRef FileName = Entry.Name.getAsCString().getValue(); if (Kind != FileLineInfoKind::AbsoluteFilePath || isPathAbsoluteOnWindowsOrPosix(FileName)) { Result = FileName; return true; } SmallString<16> FilePath; StringRef IncludeDir; // Be defensive about the contents of Entry. if (getVersion() >= 5) { if (Entry.DirIdx < IncludeDirectories.size()) IncludeDir = IncludeDirectories[Entry.DirIdx].getAsCString().getValue(); } else { if (0 < Entry.DirIdx && Entry.DirIdx <= IncludeDirectories.size()) IncludeDir = IncludeDirectories[Entry.DirIdx - 1].getAsCString().getValue(); // We may still need to append compilation directory of compile unit. // We know that FileName is not absolute, the only way to have an // absolute path at this point would be if IncludeDir is absolute. if (!CompDir.empty() && !isPathAbsoluteOnWindowsOrPosix(IncludeDir)) sys::path::append(FilePath, Style, CompDir); } // sys::path::append skips empty strings. sys::path::append(FilePath, Style, IncludeDir, FileName); Result = FilePath.str(); return true; } bool DWARFDebugLine::LineTable::getFileLineInfoForAddress( object::SectionedAddress Address, const char *CompDir, FileLineInfoKind Kind, DILineInfo &Result) const { // Get the index of row we're looking for in the line table. uint32_t RowIndex = lookupAddress(Address); if (RowIndex == -1U) return false; // Take file number and line/column from the row. const auto &Row = Rows[RowIndex]; if (!getFileNameByIndex(Row.File, CompDir, Kind, Result.FileName)) return false; Result.Line = Row.Line; Result.Column = Row.Column; Result.Discriminator = Row.Discriminator; Result.Source = getSourceByIndex(Row.File, Kind); return true; } // We want to supply the Unit associated with a .debug_line[.dwo] table when // we dump it, if possible, but still dump the table even if there isn't a Unit. // Therefore, collect up handles on all the Units that point into the // line-table section. static DWARFDebugLine::SectionParser::LineToUnitMap buildLineToUnitMap(DWARFDebugLine::SectionParser::cu_range CUs, DWARFDebugLine::SectionParser::tu_range TUs) { DWARFDebugLine::SectionParser::LineToUnitMap LineToUnit; for (const auto &CU : CUs) if (auto CUDIE = CU->getUnitDIE()) if (auto StmtOffset = toSectionOffset(CUDIE.find(DW_AT_stmt_list))) LineToUnit.insert(std::make_pair(*StmtOffset, &*CU)); for (const auto &TU : TUs) if (auto TUDIE = TU->getUnitDIE()) if (auto StmtOffset = toSectionOffset(TUDIE.find(DW_AT_stmt_list))) LineToUnit.insert(std::make_pair(*StmtOffset, &*TU)); return LineToUnit; } DWARFDebugLine::SectionParser::SectionParser(DWARFDataExtractor &Data, const DWARFContext &C, cu_range CUs, tu_range TUs) : DebugLineData(Data), Context(C) { LineToUnit = buildLineToUnitMap(CUs, TUs); if (!DebugLineData.isValidOffset(Offset)) Done = true; } bool DWARFDebugLine::Prologue::totalLengthIsValid() const { return TotalLength == dwarf::DW_LENGTH_DWARF64 || TotalLength < dwarf::DW_LENGTH_lo_reserved; } DWARFDebugLine::LineTable DWARFDebugLine::SectionParser::parseNext( function_ref RecoverableErrorCallback, function_ref UnrecoverableErrorCallback, raw_ostream *OS) { assert(DebugLineData.isValidOffset(Offset) && "parsing should have terminated"); DWARFUnit *U = prepareToParse(Offset); uint64_t OldOffset = Offset; LineTable LT; if (Error Err = LT.parse(DebugLineData, &Offset, Context, U, RecoverableErrorCallback, OS)) UnrecoverableErrorCallback(std::move(Err)); moveToNextTable(OldOffset, LT.Prologue); return LT; } void DWARFDebugLine::SectionParser::skip( function_ref ErrorCallback) { assert(DebugLineData.isValidOffset(Offset) && "parsing should have terminated"); DWARFUnit *U = prepareToParse(Offset); uint64_t OldOffset = Offset; LineTable LT; if (Error Err = LT.Prologue.parse(DebugLineData, &Offset, Context, U)) ErrorCallback(std::move(Err)); moveToNextTable(OldOffset, LT.Prologue); } DWARFUnit *DWARFDebugLine::SectionParser::prepareToParse(uint64_t Offset) { DWARFUnit *U = nullptr; auto It = LineToUnit.find(Offset); if (It != LineToUnit.end()) U = It->second; DebugLineData.setAddressSize(U ? U->getAddressByteSize() : 0); return U; } void DWARFDebugLine::SectionParser::moveToNextTable(uint64_t OldOffset, const Prologue &P) { // If the length field is not valid, we don't know where the next table is, so // cannot continue to parse. Mark the parser as done, and leave the Offset // value as it currently is. This will be the end of the bad length field. if (!P.totalLengthIsValid()) { Done = true; return; } Offset = OldOffset + P.TotalLength + P.sizeofTotalLength(); if (!DebugLineData.isValidOffset(Offset)) { Done = true; } } binaryen-version_91/third_party/llvm-project/DWARFDebugLoc.cpp000066400000000000000000000260511362402614000246060ustar00rootroot00000000000000//===- DWARFDebugLoc.cpp --------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h" #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/DWARF/DWARFExpression.h" #include "llvm/DebugInfo/DWARF/DWARFRelocMap.h" #include "llvm/DebugInfo/DWARF/DWARFUnit.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/Format.h" #include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include #include #include using namespace llvm; // When directly dumping the .debug_loc without a compile unit, we have to guess // at the DWARF version. This only affects DW_OP_call_ref, which is a rare // expression that LLVM doesn't produce. Guessing the wrong version means we // won't be able to pretty print expressions in DWARF2 binaries produced by // non-LLVM tools. static void dumpExpression(raw_ostream &OS, ArrayRef Data, bool IsLittleEndian, unsigned AddressSize, const MCRegisterInfo *MRI, DWARFUnit *U) { DWARFDataExtractor Extractor(toStringRef(Data), IsLittleEndian, AddressSize); DWARFExpression(Extractor, dwarf::DWARF_VERSION, AddressSize).print(OS, MRI, U); } void DWARFDebugLoc::LocationList::dump(raw_ostream &OS, uint64_t BaseAddress, bool IsLittleEndian, unsigned AddressSize, const MCRegisterInfo *MRI, DWARFUnit *U, DIDumpOptions DumpOpts, unsigned Indent) const { for (const Entry &E : Entries) { OS << '\n'; OS.indent(Indent); OS << format("[0x%*.*" PRIx64 ", ", AddressSize * 2, AddressSize * 2, BaseAddress + E.Begin); OS << format(" 0x%*.*" PRIx64 ")", AddressSize * 2, AddressSize * 2, BaseAddress + E.End); OS << ": "; dumpExpression(OS, E.Loc, IsLittleEndian, AddressSize, MRI, U); } } DWARFDebugLoc::LocationList const * DWARFDebugLoc::getLocationListAtOffset(uint64_t Offset) const { auto It = partition_point( Locations, [=](const LocationList &L) { return L.Offset < Offset; }); if (It != Locations.end() && It->Offset == Offset) return &(*It); return nullptr; } void DWARFDebugLoc::dump(raw_ostream &OS, const MCRegisterInfo *MRI, DIDumpOptions DumpOpts, Optional Offset) const { auto DumpLocationList = [&](const LocationList &L) { OS << format("0x%8.8" PRIx64 ": ", L.Offset); L.dump(OS, 0, IsLittleEndian, AddressSize, MRI, nullptr, DumpOpts, 12); OS << "\n"; }; if (Offset) { if (auto *L = getLocationListAtOffset(*Offset)) DumpLocationList(*L); return; } for (const LocationList &L : Locations) { DumpLocationList(L); if (&L != &Locations.back()) OS << '\n'; } } Expected DWARFDebugLoc::parseOneLocationList(const DWARFDataExtractor &Data, uint64_t *Offset) { LocationList LL; LL.Offset = *Offset; AddressSize = Data.getAddressSize(); DataExtractor::Cursor C(*Offset); // 2.6.2 Location Lists // A location list entry consists of: while (true) { Entry E; // 1. A beginning address offset. ... E.Begin = Data.getRelocatedAddress(C); // 2. An ending address offset. ... E.End = Data.getRelocatedAddress(C); if (Error Err = C.takeError()) return std::move(Err); // The end of any given location list is marked by an end of list entry, // which consists of a 0 for the beginning address offset and a 0 for the // ending address offset. if (E.Begin == 0 && E.End == 0) { *Offset = C.tell(); return LL; } if (E.Begin != (AddressSize == 4 ? -1U : -1ULL)) { unsigned Bytes = Data.getU16(C); // A single location description describing the location of the object... Data.getU8(C, E.Loc, Bytes); } LL.Entries.push_back(std::move(E)); } } void DWARFDebugLoc::parse(const DWARFDataExtractor &data) { IsLittleEndian = data.isLittleEndian(); AddressSize = data.getAddressSize(); uint64_t Offset = 0; while (Offset < data.getData().size()) { if (auto LL = parseOneLocationList(data, &Offset)) Locations.push_back(std::move(*LL)); else { logAllUnhandledErrors(LL.takeError(), WithColor::error()); break; } } } Error DWARFDebugLoclists::visitLocationList( const DWARFDataExtractor &Data, uint64_t *Offset, uint16_t Version, llvm::function_ref F) { DataExtractor::Cursor C(*Offset); bool Continue = true; while (Continue) { Entry E; E.Offset = C.tell(); E.Kind = Data.getU8(C); switch (E.Kind) { case dwarf::DW_LLE_end_of_list: break; case dwarf::DW_LLE_base_addressx: E.Value0 = Data.getULEB128(C); break; case dwarf::DW_LLE_startx_length: E.Value0 = Data.getULEB128(C); // Pre-DWARF 5 has different interpretation of the length field. We have // to support both pre- and standartized styles for the compatibility. if (Version < 5) E.Value1 = Data.getU32(C); else E.Value1 = Data.getULEB128(C); break; case dwarf::DW_LLE_offset_pair: E.Value0 = Data.getULEB128(C); E.Value1 = Data.getULEB128(C); break; case dwarf::DW_LLE_base_address: E.Value0 = Data.getRelocatedAddress(C); break; case dwarf::DW_LLE_start_length: E.Value0 = Data.getRelocatedAddress(C); E.Value1 = Data.getULEB128(C); break; case dwarf::DW_LLE_startx_endx: case dwarf::DW_LLE_default_location: case dwarf::DW_LLE_start_end: default: cantFail(C.takeError()); return createStringError(errc::illegal_byte_sequence, "LLE of kind %x not supported", (int)E.Kind); } if (E.Kind != dwarf::DW_LLE_base_address && E.Kind != dwarf::DW_LLE_base_addressx && E.Kind != dwarf::DW_LLE_end_of_list) { unsigned Bytes = Version >= 5 ? Data.getULEB128(C) : Data.getU16(C); // A single location description describing the location of the object... Data.getU8(C, E.Loc, Bytes); } if (!C) return C.takeError(); Continue = F(E) && E.Kind != dwarf::DW_LLE_end_of_list; } *Offset = C.tell(); return Error::success(); } bool DWARFDebugLoclists::dumpLocationList(const DWARFDataExtractor &Data, uint64_t *Offset, uint16_t Version, raw_ostream &OS, uint64_t BaseAddr, const MCRegisterInfo *MRI, DWARFUnit *U, DIDumpOptions DumpOpts, unsigned Indent) { size_t MaxEncodingStringLength = 0; if (DumpOpts.Verbose) { #define HANDLE_DW_LLE(ID, NAME) \ MaxEncodingStringLength = std::max(MaxEncodingStringLength, \ dwarf::LocListEncodingString(ID).size()); #include "llvm/BinaryFormat/Dwarf.def" } OS << format("0x%8.8" PRIx64 ": ", *Offset); Error E = visitLocationList(Data, Offset, Version, [&](const Entry &E) { E.dump(OS, BaseAddr, Data.isLittleEndian(), Data.getAddressSize(), MRI, U, DumpOpts, Indent, MaxEncodingStringLength); return true; }); if (E) { OS << "\n"; OS.indent(Indent); OS << "error: " << toString(std::move(E)); return false; } return true; } void DWARFDebugLoclists::Entry::dump(raw_ostream &OS, uint64_t &BaseAddr, bool IsLittleEndian, unsigned AddressSize, const MCRegisterInfo *MRI, DWARFUnit *U, DIDumpOptions DumpOpts, unsigned Indent, size_t MaxEncodingStringLength) const { if (DumpOpts.Verbose) { OS << "\n"; OS.indent(Indent); auto EncodingString = dwarf::LocListEncodingString(Kind); // Unsupported encodings should have been reported during parsing. assert(!EncodingString.empty() && "Unknown loclist entry encoding"); OS << format("%s%*c", EncodingString.data(), MaxEncodingStringLength - EncodingString.size() + 1, '('); switch (Kind) { case dwarf::DW_LLE_startx_length: case dwarf::DW_LLE_start_length: case dwarf::DW_LLE_offset_pair: OS << format("0x%*.*" PRIx64 ", 0x%*.*" PRIx64, AddressSize * 2, AddressSize * 2, Value0, AddressSize * 2, AddressSize * 2, Value1); break; case dwarf::DW_LLE_base_addressx: case dwarf::DW_LLE_base_address: OS << format("0x%*.*" PRIx64, AddressSize * 2, AddressSize * 2, Value0); break; case dwarf::DW_LLE_end_of_list: break; } OS << ')'; } auto PrintPrefix = [&] { OS << "\n"; OS.indent(Indent); if (DumpOpts.Verbose) OS << format("%*s", MaxEncodingStringLength, (const char *)"=> "); }; switch (Kind) { case dwarf::DW_LLE_startx_length: PrintPrefix(); OS << "Addr idx " << Value0 << " (w/ length " << Value1 << "): "; break; case dwarf::DW_LLE_start_length: PrintPrefix(); DWARFAddressRange(Value0, Value0 + Value1) .dump(OS, AddressSize, DumpOpts); OS << ": "; break; case dwarf::DW_LLE_offset_pair: PrintPrefix(); DWARFAddressRange(BaseAddr + Value0, BaseAddr + Value1) .dump(OS, AddressSize, DumpOpts); OS << ": "; break; case dwarf::DW_LLE_base_addressx: if (!DumpOpts.Verbose) return; break; case dwarf::DW_LLE_end_of_list: if (!DumpOpts.Verbose) return; break; case dwarf::DW_LLE_base_address: BaseAddr = Value0; if (!DumpOpts.Verbose) return; break; default: llvm_unreachable("unreachable locations list kind"); } dumpExpression(OS, Loc, IsLittleEndian, AddressSize, MRI, U); } void DWARFDebugLoclists::dumpRange(const DWARFDataExtractor &Data, uint64_t StartOffset, uint64_t Size, uint16_t Version, raw_ostream &OS, uint64_t BaseAddr, const MCRegisterInfo *MRI, DIDumpOptions DumpOpts) { if (!Data.isValidOffsetForDataOfSize(StartOffset, Size)) { OS << "Invalid dump range\n"; return; } uint64_t Offset = StartOffset; StringRef Separator; bool CanContinue = true; while (CanContinue && Offset < StartOffset + Size) { OS << Separator; Separator = "\n"; CanContinue = dumpLocationList(Data, &Offset, Version, OS, BaseAddr, MRI, nullptr, DumpOpts, /*Indent=*/12); OS << '\n'; } } binaryen-version_91/third_party/llvm-project/DWARFDebugMacro.cpp000066400000000000000000000062011362402614000251250ustar00rootroot00000000000000//===- DWARFDebugMacro.cpp ------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFDebugMacro.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include using namespace llvm; using namespace dwarf; void DWARFDebugMacro::dump(raw_ostream &OS) const { unsigned IndLevel = 0; for (const auto &Macros : MacroLists) { for (const Entry &E : Macros) { // There should not be DW_MACINFO_end_file when IndLevel is Zero. However, // this check handles the case of corrupted ".debug_macinfo" section. if (IndLevel > 0) IndLevel -= (E.Type == DW_MACINFO_end_file); // Print indentation. for (unsigned I = 0; I < IndLevel; I++) OS << " "; IndLevel += (E.Type == DW_MACINFO_start_file); WithColor(OS, HighlightColor::Macro).get() << MacinfoString(E.Type); switch (E.Type) { default: // Got a corrupted ".debug_macinfo" section (invalid macinfo type). break; case DW_MACINFO_define: case DW_MACINFO_undef: OS << " - lineno: " << E.Line; OS << " macro: " << E.MacroStr; break; case DW_MACINFO_start_file: OS << " - lineno: " << E.Line; OS << " filenum: " << E.File; break; case DW_MACINFO_end_file: break; case DW_MACINFO_vendor_ext: OS << " - constant: " << E.ExtConstant; OS << " string: " << E.ExtStr; break; } OS << "\n"; } OS << "\n"; } } void DWARFDebugMacro::parse(DataExtractor data) { uint64_t Offset = 0; MacroList *M = nullptr; while (data.isValidOffset(Offset)) { if (!M) { MacroLists.emplace_back(); M = &MacroLists.back(); } // A macro list entry consists of: M->emplace_back(); Entry &E = M->back(); // 1. Macinfo type E.Type = data.getULEB128(&Offset); if (E.Type == 0) { // Reached end of a ".debug_macinfo" section contribution. continue; } switch (E.Type) { default: // Got a corrupted ".debug_macinfo" section (invalid macinfo type). // Push the corrupted entry to the list and halt parsing. E.Type = DW_MACINFO_invalid; return; case DW_MACINFO_define: case DW_MACINFO_undef: // 2. Source line E.Line = data.getULEB128(&Offset); // 3. Macro string E.MacroStr = data.getCStr(&Offset); break; case DW_MACINFO_start_file: // 2. Source line E.Line = data.getULEB128(&Offset); // 3. Source file id E.File = data.getULEB128(&Offset); break; case DW_MACINFO_end_file: break; case DW_MACINFO_vendor_ext: // 2. Vendor extension constant E.ExtConstant = data.getULEB128(&Offset); // 3. Vendor extension string E.ExtStr = data.getCStr(&Offset); break; } } } binaryen-version_91/third_party/llvm-project/DWARFDebugPubTable.cpp000066400000000000000000000050331362402614000255640ustar00rootroot00000000000000//===- DWARFDebugPubTable.cpp ---------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFDebugPubTable.h" #include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h" #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/Support/DataExtractor.h" #include "llvm/Support/Format.h" #include "llvm/Support/raw_ostream.h" #include using namespace llvm; using namespace dwarf; DWARFDebugPubTable::DWARFDebugPubTable(const DWARFObject &Obj, const DWARFSection &Sec, bool LittleEndian, bool GnuStyle) : GnuStyle(GnuStyle) { DWARFDataExtractor PubNames(Obj, Sec, LittleEndian, 0); uint64_t Offset = 0; while (PubNames.isValidOffset(Offset)) { Sets.push_back({}); Set &SetData = Sets.back(); SetData.Length = PubNames.getU32(&Offset); SetData.Version = PubNames.getU16(&Offset); SetData.Offset = PubNames.getRelocatedValue(4, &Offset); SetData.Size = PubNames.getU32(&Offset); while (Offset < Sec.Data.size()) { uint32_t DieRef = PubNames.getU32(&Offset); if (DieRef == 0) break; uint8_t IndexEntryValue = GnuStyle ? PubNames.getU8(&Offset) : 0; StringRef Name = PubNames.getCStrRef(&Offset); SetData.Entries.push_back( {DieRef, PubIndexEntryDescriptor(IndexEntryValue), Name}); } } } void DWARFDebugPubTable::dump(raw_ostream &OS) const { for (const Set &S : Sets) { OS << "length = " << format("0x%08x", S.Length); OS << " version = " << format("0x%04x", S.Version); OS << " unit_offset = " << format("0x%08" PRIx64, S.Offset); OS << " unit_size = " << format("0x%08x", S.Size) << '\n'; OS << (GnuStyle ? "Offset Linkage Kind Name\n" : "Offset Name\n"); for (const Entry &E : S.Entries) { OS << format("0x%8.8" PRIx64 " ", E.SecOffset); if (GnuStyle) { StringRef EntryLinkage = GDBIndexEntryLinkageString(E.Descriptor.Linkage); StringRef EntryKind = dwarf::GDBIndexEntryKindString(E.Descriptor.Kind); OS << format("%-8s", EntryLinkage.data()) << ' ' << format("%-8s", EntryKind.data()) << ' '; } OS << '\"' << E.Name << "\"\n"; } } } binaryen-version_91/third_party/llvm-project/DWARFDebugRangeList.cpp000066400000000000000000000063341362402614000257630ustar00rootroot00000000000000//===- DWARFDebugRangesList.cpp -------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFDebugRangeList.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/Support/Errc.h" #include "llvm/Support/Format.h" #include "llvm/Support/raw_ostream.h" #include #include using namespace llvm; void DWARFDebugRangeList::clear() { Offset = -1ULL; AddressSize = 0; Entries.clear(); } Error DWARFDebugRangeList::extract(const DWARFDataExtractor &data, uint64_t *offset_ptr) { clear(); if (!data.isValidOffset(*offset_ptr)) return createStringError(errc::invalid_argument, "invalid range list offset 0x%" PRIx64, *offset_ptr); AddressSize = data.getAddressSize(); if (AddressSize != 4 && AddressSize != 8) return createStringError(errc::invalid_argument, "invalid address size: %" PRIu8, AddressSize); Offset = *offset_ptr; while (true) { RangeListEntry Entry; Entry.SectionIndex = -1ULL; uint64_t prev_offset = *offset_ptr; Entry.StartAddress = data.getRelocatedAddress(offset_ptr); Entry.EndAddress = data.getRelocatedAddress(offset_ptr, &Entry.SectionIndex); // Check that both values were extracted correctly. if (*offset_ptr != prev_offset + 2 * AddressSize) { clear(); return createStringError(errc::invalid_argument, "invalid range list entry at offset 0x%" PRIx64, prev_offset); } if (Entry.isEndOfListEntry()) break; Entries.push_back(Entry); } return Error::success(); } void DWARFDebugRangeList::dump(raw_ostream &OS) const { for (const RangeListEntry &RLE : Entries) { const char *format_str = (AddressSize == 4 ? "%08" PRIx64 " %08" PRIx64 " %08" PRIx64 "\n" : "%08" PRIx64 " %016" PRIx64 " %016" PRIx64 "\n"); OS << format(format_str, Offset, RLE.StartAddress, RLE.EndAddress); } OS << format("%08" PRIx64 " \n", Offset); } DWARFAddressRangesVector DWARFDebugRangeList::getAbsoluteRanges( llvm::Optional BaseAddr) const { DWARFAddressRangesVector Res; for (const RangeListEntry &RLE : Entries) { if (RLE.isBaseAddressSelectionEntry(AddressSize)) { BaseAddr = {RLE.EndAddress, RLE.SectionIndex}; continue; } DWARFAddressRange E; E.LowPC = RLE.StartAddress; E.HighPC = RLE.EndAddress; E.SectionIndex = RLE.SectionIndex; // Base address of a range list entry is determined by the closest preceding // base address selection entry in the same range list. It defaults to the // base address of the compilation unit if there is no such entry. if (BaseAddr) { E.LowPC += BaseAddr->Address; E.HighPC += BaseAddr->Address; if (E.SectionIndex == -1ULL) E.SectionIndex = BaseAddr->SectionIndex; } Res.push_back(E); } return Res; } binaryen-version_91/third_party/llvm-project/DWARFDebugRnglists.cpp000066400000000000000000000210411362402614000256700ustar00rootroot00000000000000//===- DWARFDebugRnglists.cpp ---------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFDebugRnglists.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/DebugInfo/DWARF/DWARFUnit.h" #include "llvm/Support/Errc.h" #include "llvm/Support/Error.h" #include "llvm/Support/Format.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; Error RangeListEntry::extract(DWARFDataExtractor Data, uint64_t End, uint64_t *OffsetPtr) { Offset = *OffsetPtr; SectionIndex = -1ULL; // The caller should guarantee that we have at least 1 byte available, so // we just assert instead of revalidate. assert(*OffsetPtr < End && "not enough space to extract a rangelist encoding"); uint8_t Encoding = Data.getU8(OffsetPtr); switch (Encoding) { case dwarf::DW_RLE_end_of_list: Value0 = Value1 = 0; break; // TODO: Support other encodings. case dwarf::DW_RLE_base_addressx: { uint64_t PreviousOffset = *OffsetPtr - 1; Value0 = Data.getULEB128(OffsetPtr); if (End < *OffsetPtr) return createStringError( errc::invalid_argument, "read past end of table when reading " "DW_RLE_base_addressx encoding at offset 0x%" PRIx64, PreviousOffset); break; } case dwarf::DW_RLE_startx_endx: return createStringError(errc::not_supported, "unsupported rnglists encoding DW_RLE_startx_endx at " "offset 0x%" PRIx64, *OffsetPtr - 1); case dwarf::DW_RLE_startx_length: { uint64_t PreviousOffset = *OffsetPtr - 1; Value0 = Data.getULEB128(OffsetPtr); Value1 = Data.getULEB128(OffsetPtr); if (End < *OffsetPtr) return createStringError( errc::invalid_argument, "read past end of table when reading " "DW_RLE_startx_length encoding at offset 0x%" PRIx64, PreviousOffset); break; } case dwarf::DW_RLE_offset_pair: { uint64_t PreviousOffset = *OffsetPtr - 1; Value0 = Data.getULEB128(OffsetPtr); Value1 = Data.getULEB128(OffsetPtr); if (End < *OffsetPtr) return createStringError(errc::invalid_argument, "read past end of table when reading " "DW_RLE_offset_pair encoding at offset 0x%" PRIx64, PreviousOffset); break; } case dwarf::DW_RLE_base_address: { if ((End - *OffsetPtr) < Data.getAddressSize()) return createStringError(errc::invalid_argument, "insufficient space remaining in table for " "DW_RLE_base_address encoding at offset 0x%" PRIx64, *OffsetPtr - 1); Value0 = Data.getRelocatedAddress(OffsetPtr, &SectionIndex); break; } case dwarf::DW_RLE_start_end: { if ((End - *OffsetPtr) < unsigned(Data.getAddressSize() * 2)) return createStringError(errc::invalid_argument, "insufficient space remaining in table for " "DW_RLE_start_end encoding " "at offset 0x%" PRIx64, *OffsetPtr - 1); Value0 = Data.getRelocatedAddress(OffsetPtr, &SectionIndex); Value1 = Data.getRelocatedAddress(OffsetPtr); break; } case dwarf::DW_RLE_start_length: { uint64_t PreviousOffset = *OffsetPtr - 1; Value0 = Data.getRelocatedAddress(OffsetPtr, &SectionIndex); Value1 = Data.getULEB128(OffsetPtr); if (End < *OffsetPtr) return createStringError(errc::invalid_argument, "read past end of table when reading " "DW_RLE_start_length encoding at offset 0x%" PRIx64, PreviousOffset); break; } default: return createStringError(errc::not_supported, "unknown rnglists encoding 0x%" PRIx32 " at offset 0x%" PRIx64, uint32_t(Encoding), *OffsetPtr - 1); } EntryKind = Encoding; return Error::success(); } DWARFAddressRangesVector DWARFDebugRnglist::getAbsoluteRanges( llvm::Optional BaseAddr, DWARFUnit &U) const { DWARFAddressRangesVector Res; for (const RangeListEntry &RLE : Entries) { if (RLE.EntryKind == dwarf::DW_RLE_end_of_list) break; if (RLE.EntryKind == dwarf::DW_RLE_base_addressx) { BaseAddr = U.getAddrOffsetSectionItem(RLE.Value0); if (!BaseAddr) BaseAddr = {RLE.Value0, -1ULL}; continue; } if (RLE.EntryKind == dwarf::DW_RLE_base_address) { BaseAddr = {RLE.Value0, RLE.SectionIndex}; continue; } DWARFAddressRange E; E.SectionIndex = RLE.SectionIndex; if (BaseAddr && E.SectionIndex == -1ULL) E.SectionIndex = BaseAddr->SectionIndex; switch (RLE.EntryKind) { case dwarf::DW_RLE_offset_pair: E.LowPC = RLE.Value0; E.HighPC = RLE.Value1; if (BaseAddr) { E.LowPC += BaseAddr->Address; E.HighPC += BaseAddr->Address; } break; case dwarf::DW_RLE_start_end: E.LowPC = RLE.Value0; E.HighPC = RLE.Value1; break; case dwarf::DW_RLE_start_length: E.LowPC = RLE.Value0; E.HighPC = E.LowPC + RLE.Value1; break; case dwarf::DW_RLE_startx_length: { auto Start = U.getAddrOffsetSectionItem(RLE.Value0); if (!Start) Start = {0, -1ULL}; E.SectionIndex = Start->SectionIndex; E.LowPC = Start->Address; E.HighPC = E.LowPC + RLE.Value1; break; } default: // Unsupported encodings should have been reported during extraction, // so we should not run into any here. llvm_unreachable("Unsupported range list encoding"); } Res.push_back(E); } return Res; } void RangeListEntry::dump( raw_ostream &OS, uint8_t AddrSize, uint8_t MaxEncodingStringLength, uint64_t &CurrentBase, DIDumpOptions DumpOpts, llvm::function_ref(uint32_t)> LookupPooledAddress) const { auto PrintRawEntry = [](raw_ostream &OS, const RangeListEntry &Entry, uint8_t AddrSize, DIDumpOptions DumpOpts) { if (DumpOpts.Verbose) { DumpOpts.DisplayRawContents = true; DWARFAddressRange(Entry.Value0, Entry.Value1) .dump(OS, AddrSize, DumpOpts); OS << " => "; } }; if (DumpOpts.Verbose) { // Print the section offset in verbose mode. OS << format("0x%8.8" PRIx64 ":", Offset); auto EncodingString = dwarf::RangeListEncodingString(EntryKind); // Unsupported encodings should have been reported during parsing. assert(!EncodingString.empty() && "Unknown range entry encoding"); OS << format(" [%s%*c", EncodingString.data(), MaxEncodingStringLength - EncodingString.size() + 1, ']'); if (EntryKind != dwarf::DW_RLE_end_of_list) OS << ": "; } switch (EntryKind) { case dwarf::DW_RLE_end_of_list: OS << (DumpOpts.Verbose ? "" : ""); break; case dwarf::DW_RLE_base_addressx: { if (auto SA = LookupPooledAddress(Value0)) CurrentBase = SA->Address; else CurrentBase = Value0; if (!DumpOpts.Verbose) return; OS << format(" 0x%*.*" PRIx64, AddrSize * 2, AddrSize * 2, Value0); break; } case dwarf::DW_RLE_base_address: // In non-verbose mode we do not print anything for this entry. CurrentBase = Value0; if (!DumpOpts.Verbose) return; OS << format(" 0x%*.*" PRIx64, AddrSize * 2, AddrSize * 2, Value0); break; case dwarf::DW_RLE_start_length: PrintRawEntry(OS, *this, AddrSize, DumpOpts); DWARFAddressRange(Value0, Value0 + Value1).dump(OS, AddrSize, DumpOpts); break; case dwarf::DW_RLE_offset_pair: PrintRawEntry(OS, *this, AddrSize, DumpOpts); DWARFAddressRange(Value0 + CurrentBase, Value1 + CurrentBase) .dump(OS, AddrSize, DumpOpts); break; case dwarf::DW_RLE_start_end: DWARFAddressRange(Value0, Value1).dump(OS, AddrSize, DumpOpts); break; case dwarf::DW_RLE_startx_length: { PrintRawEntry(OS, *this, AddrSize, DumpOpts); uint64_t Start = 0; if (auto SA = LookupPooledAddress(Value0)) Start = SA->Address; DWARFAddressRange(Start, Start + Value1).dump(OS, AddrSize, DumpOpts); break; } default: llvm_unreachable("Unsupported range list encoding"); } OS << "\n"; } binaryen-version_91/third_party/llvm-project/DWARFDie.cpp000066400000000000000000000574201362402614000236270ustar00rootroot00000000000000//===- DWARFDie.cpp -------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFDie.h" #include "llvm/ADT/None.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/DebugInfo/DWARF/DWARFAbbreviationDeclaration.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/DWARF/DWARFDebugRangeList.h" #include "llvm/DebugInfo/DWARF/DWARFExpression.h" #include "llvm/DebugInfo/DWARF/DWARFFormValue.h" #include "llvm/DebugInfo/DWARF/DWARFUnit.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Support/DataExtractor.h" #include "llvm/Support/Format.h" #include "llvm/Support/FormatAdapters.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include #include #include #include #include #include using namespace llvm; using namespace dwarf; using namespace object; static void dumpApplePropertyAttribute(raw_ostream &OS, uint64_t Val) { OS << " ("; do { uint64_t Shift = countTrailingZeros(Val); assert(Shift < 64 && "undefined behavior"); uint64_t Bit = 1ULL << Shift; auto PropName = ApplePropertyString(Bit); if (!PropName.empty()) OS << PropName; else OS << format("DW_APPLE_PROPERTY_0x%" PRIx64, Bit); if (!(Val ^= Bit)) break; OS << ", "; } while (true); OS << ")"; } static void dumpRanges(const DWARFObject &Obj, raw_ostream &OS, const DWARFAddressRangesVector &Ranges, unsigned AddressSize, unsigned Indent, const DIDumpOptions &DumpOpts) { if (!DumpOpts.ShowAddresses) return; ArrayRef SectionNames; if (DumpOpts.Verbose) SectionNames = Obj.getSectionNames(); for (const DWARFAddressRange &R : Ranges) { OS << '\n'; OS.indent(Indent); R.dump(OS, AddressSize); DWARFFormValue::dumpAddressSection(Obj, OS, DumpOpts, R.SectionIndex); } } static void dumpLocation(raw_ostream &OS, DWARFFormValue &FormValue, DWARFUnit *U, unsigned Indent, DIDumpOptions DumpOpts) { DWARFContext &Ctx = U->getContext(); const DWARFObject &Obj = Ctx.getDWARFObj(); const MCRegisterInfo *MRI = Ctx.getRegisterInfo(); if (FormValue.isFormClass(DWARFFormValue::FC_Block) || FormValue.isFormClass(DWARFFormValue::FC_Exprloc)) { ArrayRef Expr = *FormValue.getAsBlock(); DataExtractor Data(StringRef((const char *)Expr.data(), Expr.size()), Ctx.isLittleEndian(), 0); DWARFExpression(Data, U->getVersion(), U->getAddressByteSize()) .print(OS, MRI, U); return; } if (FormValue.isFormClass(DWARFFormValue::FC_SectionOffset)) { uint64_t Offset = *FormValue.getAsSectionOffset(); uint64_t BaseAddr = 0; if (Optional BA = U->getBaseAddress()) BaseAddr = BA->Address; auto LLDumpOpts = DumpOpts; LLDumpOpts.Verbose = false; if (!U->isDWOUnit() && !U->getLocSection()->Data.empty()) { DWARFDebugLoc DebugLoc; DWARFDataExtractor Data(Obj, *U->getLocSection(), Ctx.isLittleEndian(), Obj.getAddressSize()); FormValue.dump(OS, DumpOpts); OS << ": "; if (Expected LL = DebugLoc.parseOneLocationList(Data, &Offset)) { LL->dump(OS, BaseAddr, Ctx.isLittleEndian(), Obj.getAddressSize(), MRI, U, LLDumpOpts, Indent); } else { OS << '\n'; OS.indent(Indent); OS << formatv("error extracting location list: {0}", fmt_consume(LL.takeError())); } return; } bool UseLocLists = !U->isDWOUnit(); auto Data = UseLocLists ? DWARFDataExtractor(Obj, Obj.getLoclistsSection(), Ctx.isLittleEndian(), Obj.getAddressSize()) : DWARFDataExtractor(U->getLocSectionData(), Ctx.isLittleEndian(), Obj.getAddressSize()); if (!Data.getData().empty()) { // Old-style location list were used in DWARF v4 (.debug_loc.dwo section). // Modern locations list (.debug_loclists) are used starting from v5. // Ideally we should take the version from the .debug_loclists section // header, but using CU's version for simplicity. DWARFDebugLoclists::dumpLocationList( Data, &Offset, UseLocLists ? U->getVersion() : 4, OS, BaseAddr, MRI, U, LLDumpOpts, Indent); } return; } FormValue.dump(OS, DumpOpts); } /// Dump the name encoded in the type tag. static void dumpTypeTagName(raw_ostream &OS, dwarf::Tag T) { StringRef TagStr = TagString(T); if (!TagStr.startswith("DW_TAG_") || !TagStr.endswith("_type")) return; OS << TagStr.substr(7, TagStr.size() - 12) << " "; } static void dumpArrayType(raw_ostream &OS, const DWARFDie &D) { Optional Bound; for (const DWARFDie &C : D.children()) if (C.getTag() == DW_TAG_subrange_type) { Optional LB; Optional Count; Optional UB; Optional DefaultLB; if (Optional L = C.find(DW_AT_lower_bound)) LB = L->getAsUnsignedConstant(); if (Optional CountV = C.find(DW_AT_count)) Count = CountV->getAsUnsignedConstant(); if (Optional UpperV = C.find(DW_AT_upper_bound)) UB = UpperV->getAsUnsignedConstant(); if (Optional LV = D.getDwarfUnit()->getUnitDIE().find(DW_AT_language)) if (Optional LC = LV->getAsUnsignedConstant()) if ((DefaultLB = LanguageLowerBound(static_cast(*LC)))) if (LB && *LB == *DefaultLB) LB = None; if (!LB && !Count && !UB) OS << "[]"; else if (!LB && (Count || UB) && DefaultLB) OS << '[' << (Count ? *Count : *UB - *DefaultLB + 1) << ']'; else { OS << "[["; if (LB) OS << *LB; else OS << '?'; OS << ", "; if (Count) if (LB) OS << *LB + *Count; else OS << "? + " << *Count; else if (UB) OS << *UB + 1; else OS << '?'; OS << ")]"; } } } /// Recursively dump the DIE type name when applicable. static void dumpTypeName(raw_ostream &OS, const DWARFDie &D) { if (!D.isValid()) return; if (const char *Name = D.getName(DINameKind::LinkageName)) { OS << Name; return; } // FIXME: We should have pretty printers per language. Currently we print // everything as if it was C++ and fall back to the TAG type name. const dwarf::Tag T = D.getTag(); switch (T) { case DW_TAG_array_type: case DW_TAG_pointer_type: case DW_TAG_ptr_to_member_type: case DW_TAG_reference_type: case DW_TAG_rvalue_reference_type: case DW_TAG_subroutine_type: break; default: dumpTypeTagName(OS, T); } // Follow the DW_AT_type if possible. DWARFDie TypeDie = D.getAttributeValueAsReferencedDie(DW_AT_type); dumpTypeName(OS, TypeDie); switch (T) { case DW_TAG_subroutine_type: { if (!TypeDie) OS << "void"; OS << '('; bool First = true; for (const DWARFDie &C : D.children()) { if (C.getTag() == DW_TAG_formal_parameter) { if (!First) OS << ", "; First = false; dumpTypeName(OS, C.getAttributeValueAsReferencedDie(DW_AT_type)); } } OS << ')'; break; } case DW_TAG_array_type: { dumpArrayType(OS, D); break; } case DW_TAG_pointer_type: OS << '*'; break; case DW_TAG_ptr_to_member_type: if (DWARFDie Cont = D.getAttributeValueAsReferencedDie(DW_AT_containing_type)) { dumpTypeName(OS << ' ', Cont); OS << "::"; } OS << '*'; break; case DW_TAG_reference_type: OS << '&'; break; case DW_TAG_rvalue_reference_type: OS << "&&"; break; default: break; } } static void dumpAttribute(raw_ostream &OS, const DWARFDie &Die, uint64_t *OffsetPtr, dwarf::Attribute Attr, dwarf::Form Form, unsigned Indent, DIDumpOptions DumpOpts) { if (!Die.isValid()) return; const char BaseIndent[] = " "; OS << BaseIndent; OS.indent(Indent + 2); WithColor(OS, HighlightColor::Attribute) << formatv("{0}", Attr); if (DumpOpts.Verbose || DumpOpts.ShowForm) OS << formatv(" [{0}]", Form); DWARFUnit *U = Die.getDwarfUnit(); DWARFFormValue FormValue = DWARFFormValue::createFromUnit(Form, U, OffsetPtr); OS << "\t("; StringRef Name; std::string File; auto Color = HighlightColor::Enumerator; if (Attr == DW_AT_decl_file || Attr == DW_AT_call_file) { Color = HighlightColor::String; if (const auto *LT = U->getContext().getLineTableForUnit(U)) if (LT->getFileNameByIndex( FormValue.getAsUnsignedConstant().getValue(), U->getCompilationDir(), DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, File)) { File = '"' + File + '"'; Name = File; } } else if (Optional Val = FormValue.getAsUnsignedConstant()) Name = AttributeValueString(Attr, *Val); if (!Name.empty()) WithColor(OS, Color) << Name; else if (Attr == DW_AT_decl_line || Attr == DW_AT_call_line) OS << *FormValue.getAsUnsignedConstant(); else if (Attr == DW_AT_high_pc && !DumpOpts.ShowForm && !DumpOpts.Verbose && FormValue.getAsUnsignedConstant()) { if (DumpOpts.ShowAddresses) { // Print the actual address rather than the offset. uint64_t LowPC, HighPC, Index; if (Die.getLowAndHighPC(LowPC, HighPC, Index)) OS << format("0x%016" PRIx64, HighPC); else FormValue.dump(OS, DumpOpts); } } else if (DWARFAttribute::mayHaveLocationDescription(Attr)) dumpLocation(OS, FormValue, U, sizeof(BaseIndent) + Indent + 4, DumpOpts); else FormValue.dump(OS, DumpOpts); std::string Space = DumpOpts.ShowAddresses ? " " : ""; // We have dumped the attribute raw value. For some attributes // having both the raw value and the pretty-printed value is // interesting. These attributes are handled below. if (Attr == DW_AT_specification || Attr == DW_AT_abstract_origin) { if (const char *Name = Die.getAttributeValueAsReferencedDie(FormValue).getName( DINameKind::LinkageName)) OS << Space << "\"" << Name << '\"'; } else if (Attr == DW_AT_type) { OS << Space << "\""; dumpTypeName(OS, Die.getAttributeValueAsReferencedDie(FormValue)); OS << '"'; } else if (Attr == DW_AT_APPLE_property_attribute) { if (Optional OptVal = FormValue.getAsUnsignedConstant()) dumpApplePropertyAttribute(OS, *OptVal); } else if (Attr == DW_AT_ranges) { const DWARFObject &Obj = Die.getDwarfUnit()->getContext().getDWARFObj(); // For DW_FORM_rnglistx we need to dump the offset separately, since // we have only dumped the index so far. if (FormValue.getForm() == DW_FORM_rnglistx) if (auto RangeListOffset = U->getRnglistOffset(*FormValue.getAsSectionOffset())) { DWARFFormValue FV = DWARFFormValue::createFromUValue( dwarf::DW_FORM_sec_offset, *RangeListOffset); FV.dump(OS, DumpOpts); } if (auto RangesOrError = Die.getAddressRanges()) dumpRanges(Obj, OS, RangesOrError.get(), U->getAddressByteSize(), sizeof(BaseIndent) + Indent + 4, DumpOpts); else WithColor::error() << "decoding address ranges: " << toString(RangesOrError.takeError()) << '\n'; } OS << ")\n"; } bool DWARFDie::isSubprogramDIE() const { return getTag() == DW_TAG_subprogram; } bool DWARFDie::isSubroutineDIE() const { auto Tag = getTag(); return Tag == DW_TAG_subprogram || Tag == DW_TAG_inlined_subroutine; } Optional DWARFDie::find(dwarf::Attribute Attr) const { if (!isValid()) return None; auto AbbrevDecl = getAbbreviationDeclarationPtr(); if (AbbrevDecl) return AbbrevDecl->getAttributeValue(getOffset(), Attr, *U); return None; } Optional DWARFDie::find(ArrayRef Attrs) const { if (!isValid()) return None; auto AbbrevDecl = getAbbreviationDeclarationPtr(); if (AbbrevDecl) { for (auto Attr : Attrs) { if (auto Value = AbbrevDecl->getAttributeValue(getOffset(), Attr, *U)) return Value; } } return None; } Optional DWARFDie::findRecursively(ArrayRef Attrs) const { std::vector Worklist; Worklist.push_back(*this); // Keep track if DIEs already seen to prevent infinite recursion. // Empirically we rarely see a depth of more than 3 when dealing with valid // DWARF. This corresponds to following the DW_AT_abstract_origin and // DW_AT_specification just once. SmallSet Seen; Seen.insert(*this); while (!Worklist.empty()) { DWARFDie Die = Worklist.back(); Worklist.pop_back(); if (!Die.isValid()) continue; if (auto Value = Die.find(Attrs)) return Value; if (auto D = Die.getAttributeValueAsReferencedDie(DW_AT_abstract_origin)) if (Seen.insert(D).second) Worklist.push_back(D); if (auto D = Die.getAttributeValueAsReferencedDie(DW_AT_specification)) if (Seen.insert(D).second) Worklist.push_back(D); } return None; } DWARFDie DWARFDie::getAttributeValueAsReferencedDie(dwarf::Attribute Attr) const { if (Optional F = find(Attr)) return getAttributeValueAsReferencedDie(*F); return DWARFDie(); } DWARFDie DWARFDie::getAttributeValueAsReferencedDie(const DWARFFormValue &V) const { if (auto SpecRef = V.getAsRelativeReference()) { if (SpecRef->Unit) return SpecRef->Unit->getDIEForOffset(SpecRef->Unit->getOffset() + SpecRef->Offset); if (auto SpecUnit = U->getUnitVector().getUnitForOffset(SpecRef->Offset)) return SpecUnit->getDIEForOffset(SpecRef->Offset); } return DWARFDie(); } Optional DWARFDie::getRangesBaseAttribute() const { return toSectionOffset(find({DW_AT_rnglists_base, DW_AT_GNU_ranges_base})); } Optional DWARFDie::getHighPC(uint64_t LowPC) const { if (auto FormValue = find(DW_AT_high_pc)) { if (auto Address = FormValue->getAsAddress()) { // High PC is an address. return Address; } if (auto Offset = FormValue->getAsUnsignedConstant()) { // High PC is an offset from LowPC. return LowPC + *Offset; } } return None; } bool DWARFDie::getLowAndHighPC(uint64_t &LowPC, uint64_t &HighPC, uint64_t &SectionIndex) const { auto F = find(DW_AT_low_pc); auto LowPcAddr = toSectionedAddress(F); if (!LowPcAddr) return false; if (auto HighPcAddr = getHighPC(LowPcAddr->Address)) { LowPC = LowPcAddr->Address; HighPC = *HighPcAddr; SectionIndex = LowPcAddr->SectionIndex; return true; } return false; } Expected DWARFDie::getAddressRanges() const { if (isNULL()) return DWARFAddressRangesVector(); // Single range specified by low/high PC. uint64_t LowPC, HighPC, Index; if (getLowAndHighPC(LowPC, HighPC, Index)) return DWARFAddressRangesVector{{LowPC, HighPC, Index}}; Optional Value = find(DW_AT_ranges); if (Value) { if (Value->getForm() == DW_FORM_rnglistx) return U->findRnglistFromIndex(*Value->getAsSectionOffset()); return U->findRnglistFromOffset(*Value->getAsSectionOffset()); } return DWARFAddressRangesVector(); } void DWARFDie::collectChildrenAddressRanges( DWARFAddressRangesVector &Ranges) const { if (isNULL()) return; if (isSubprogramDIE()) { if (auto DIERangesOrError = getAddressRanges()) Ranges.insert(Ranges.end(), DIERangesOrError.get().begin(), DIERangesOrError.get().end()); else llvm::consumeError(DIERangesOrError.takeError()); } for (auto Child : children()) Child.collectChildrenAddressRanges(Ranges); } bool DWARFDie::addressRangeContainsAddress(const uint64_t Address) const { auto RangesOrError = getAddressRanges(); if (!RangesOrError) { llvm::consumeError(RangesOrError.takeError()); return false; } for (const auto &R : RangesOrError.get()) if (R.LowPC <= Address && Address < R.HighPC) return true; return false; } const char *DWARFDie::getSubroutineName(DINameKind Kind) const { if (!isSubroutineDIE()) return nullptr; return getName(Kind); } const char *DWARFDie::getName(DINameKind Kind) const { if (!isValid() || Kind == DINameKind::None) return nullptr; // Try to get mangled name only if it was asked for. if (Kind == DINameKind::LinkageName) { if (auto Name = dwarf::toString( findRecursively({DW_AT_MIPS_linkage_name, DW_AT_linkage_name}), nullptr)) return Name; } if (auto Name = dwarf::toString(findRecursively(DW_AT_name), nullptr)) return Name; return nullptr; } uint64_t DWARFDie::getDeclLine() const { return toUnsigned(findRecursively(DW_AT_decl_line), 0); } void DWARFDie::getCallerFrame(uint32_t &CallFile, uint32_t &CallLine, uint32_t &CallColumn, uint32_t &CallDiscriminator) const { CallFile = toUnsigned(find(DW_AT_call_file), 0); CallLine = toUnsigned(find(DW_AT_call_line), 0); CallColumn = toUnsigned(find(DW_AT_call_column), 0); CallDiscriminator = toUnsigned(find(DW_AT_GNU_discriminator), 0); } /// Helper to dump a DIE with all of its parents, but no siblings. static unsigned dumpParentChain(DWARFDie Die, raw_ostream &OS, unsigned Indent, DIDumpOptions DumpOpts, unsigned Depth = 0) { if (!Die) return Indent; if (DumpOpts.ParentRecurseDepth > 0 && Depth >= DumpOpts.ParentRecurseDepth) return Indent; Indent = dumpParentChain(Die.getParent(), OS, Indent, DumpOpts, Depth + 1); Die.dump(OS, Indent, DumpOpts); return Indent + 2; } void DWARFDie::dump(raw_ostream &OS, unsigned Indent, DIDumpOptions DumpOpts) const { if (!isValid()) return; DWARFDataExtractor debug_info_data = U->getDebugInfoExtractor(); const uint64_t Offset = getOffset(); uint64_t offset = Offset; if (DumpOpts.ShowParents) { DIDumpOptions ParentDumpOpts = DumpOpts; ParentDumpOpts.ShowParents = false; ParentDumpOpts.ShowChildren = false; Indent = dumpParentChain(getParent(), OS, Indent, ParentDumpOpts); } if (debug_info_data.isValidOffset(offset)) { uint32_t abbrCode = debug_info_data.getULEB128(&offset); if (DumpOpts.ShowAddresses) WithColor(OS, HighlightColor::Address).get() << format("\n0x%8.8" PRIx64 ": ", Offset); if (abbrCode) { auto AbbrevDecl = getAbbreviationDeclarationPtr(); if (AbbrevDecl) { WithColor(OS, HighlightColor::Tag).get().indent(Indent) << formatv("{0}", getTag()); if (DumpOpts.Verbose) OS << format(" [%u] %c", abbrCode, AbbrevDecl->hasChildren() ? '*' : ' '); OS << '\n'; // Dump all data in the DIE for the attributes. for (const auto &AttrSpec : AbbrevDecl->attributes()) { if (AttrSpec.Form == DW_FORM_implicit_const) { // We are dumping .debug_info section , // implicit_const attribute values are not really stored here, // but in .debug_abbrev section. So we just skip such attrs. continue; } dumpAttribute(OS, *this, &offset, AttrSpec.Attr, AttrSpec.Form, Indent, DumpOpts); } DWARFDie child = getFirstChild(); if (DumpOpts.ShowChildren && DumpOpts.ChildRecurseDepth > 0 && child) { DumpOpts.ChildRecurseDepth--; DIDumpOptions ChildDumpOpts = DumpOpts; ChildDumpOpts.ShowParents = false; while (child) { child.dump(OS, Indent + 2, ChildDumpOpts); child = child.getSibling(); } } } else { OS << "Abbreviation code not found in 'debug_abbrev' class for code: " << abbrCode << '\n'; } } else { OS.indent(Indent) << "NULL\n"; } } } LLVM_DUMP_METHOD void DWARFDie::dump() const { dump(llvm::errs(), 0); } DWARFDie DWARFDie::getParent() const { if (isValid()) return U->getParent(Die); return DWARFDie(); } DWARFDie DWARFDie::getSibling() const { if (isValid()) return U->getSibling(Die); return DWARFDie(); } DWARFDie DWARFDie::getPreviousSibling() const { if (isValid()) return U->getPreviousSibling(Die); return DWARFDie(); } DWARFDie DWARFDie::getFirstChild() const { if (isValid()) return U->getFirstChild(Die); return DWARFDie(); } DWARFDie DWARFDie::getLastChild() const { if (isValid()) return U->getLastChild(Die); return DWARFDie(); } iterator_range DWARFDie::attributes() const { return make_range(attribute_iterator(*this, false), attribute_iterator(*this, true)); } DWARFDie::attribute_iterator::attribute_iterator(DWARFDie D, bool End) : Die(D), Index(0) { auto AbbrDecl = Die.getAbbreviationDeclarationPtr(); assert(AbbrDecl && "Must have abbreviation declaration"); if (End) { // This is the end iterator so we set the index to the attribute count. Index = AbbrDecl->getNumAttributes(); } else { // This is the begin iterator so we extract the value for this->Index. AttrValue.Offset = D.getOffset() + AbbrDecl->getCodeByteSize(); updateForIndex(*AbbrDecl, 0); } } void DWARFDie::attribute_iterator::updateForIndex( const DWARFAbbreviationDeclaration &AbbrDecl, uint32_t I) { Index = I; // AbbrDecl must be valid before calling this function. auto NumAttrs = AbbrDecl.getNumAttributes(); if (Index < NumAttrs) { AttrValue.Attr = AbbrDecl.getAttrByIndex(Index); // Add the previous byte size of any previous attribute value. AttrValue.Offset += AttrValue.ByteSize; uint64_t ParseOffset = AttrValue.Offset; auto U = Die.getDwarfUnit(); assert(U && "Die must have valid DWARF unit"); AttrValue.Value = DWARFFormValue::createFromUnit( AbbrDecl.getFormByIndex(Index), U, &ParseOffset); AttrValue.ByteSize = ParseOffset - AttrValue.Offset; } else { assert(Index == NumAttrs && "Indexes should be [0, NumAttrs) only"); AttrValue = {}; } } DWARFDie::attribute_iterator &DWARFDie::attribute_iterator::operator++() { if (auto AbbrDecl = Die.getAbbreviationDeclarationPtr()) updateForIndex(*AbbrDecl, Index + 1); return *this; } bool DWARFAttribute::mayHaveLocationDescription(dwarf::Attribute Attr) { switch (Attr) { // From the DWARF v5 specification. case DW_AT_location: case DW_AT_byte_size: case DW_AT_bit_size: case DW_AT_string_length: case DW_AT_lower_bound: case DW_AT_return_addr: case DW_AT_bit_stride: case DW_AT_upper_bound: case DW_AT_count: case DW_AT_data_member_location: case DW_AT_frame_base: case DW_AT_segment: case DW_AT_static_link: case DW_AT_use_location: case DW_AT_vtable_elem_location: case DW_AT_allocated: case DW_AT_associated: case DW_AT_byte_stride: case DW_AT_rank: case DW_AT_call_value: case DW_AT_call_origin: case DW_AT_call_target: case DW_AT_call_target_clobbered: case DW_AT_call_data_location: case DW_AT_call_data_value: // Extensions. case DW_AT_GNU_call_site_value: case DW_AT_GNU_call_site_target: return true; default: return false; } } binaryen-version_91/third_party/llvm-project/DWARFEmitter.cpp000066400000000000000000000412031362402614000245270ustar00rootroot00000000000000//===- DWARFEmitter - Convert YAML to DWARF binary data -------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// /// \file /// The DWARF component of yaml2obj. Provided as library code for tests. /// //===----------------------------------------------------------------------===// #include "llvm/ObjectYAML/DWARFEmitter.h" #include "DWARFVisitor.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/ObjectYAML/DWARFYAML.h" #include "llvm/Support/Error.h" #include "llvm/Support/Host.h" #include "llvm/Support/LEB128.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/SwapByteOrder.h" #include "llvm/Support/YAMLTraits.h" #include "llvm/Support/raw_ostream.h" #include #include #include #include #include #include #include using namespace llvm; template static void writeInteger(T Integer, raw_ostream &OS, bool IsLittleEndian) { if (IsLittleEndian != sys::IsLittleEndianHost) sys::swapByteOrder(Integer); OS.write(reinterpret_cast(&Integer), sizeof(T)); } static void writeVariableSizedInteger(uint64_t Integer, size_t Size, raw_ostream &OS, bool IsLittleEndian) { if (8 == Size) writeInteger((uint64_t)Integer, OS, IsLittleEndian); else if (4 == Size) writeInteger((uint32_t)Integer, OS, IsLittleEndian); else if (2 == Size) writeInteger((uint16_t)Integer, OS, IsLittleEndian); else if (1 == Size) writeInteger((uint8_t)Integer, OS, IsLittleEndian); else assert(false && "Invalid integer write size."); } static void ZeroFillBytes(raw_ostream &OS, size_t Size) { std::vector FillData; FillData.insert(FillData.begin(), Size, 0); OS.write(reinterpret_cast(FillData.data()), Size); } static void writeInitialLength(const DWARFYAML::InitialLength &Length, raw_ostream &OS, bool IsLittleEndian) { writeInteger((uint32_t)Length.TotalLength, OS, IsLittleEndian); if (Length.isDWARF64()) writeInteger((uint64_t)Length.TotalLength64, OS, IsLittleEndian); } void DWARFYAML::EmitDebugStr(raw_ostream &OS, const DWARFYAML::Data &DI) { for (auto Str : DI.DebugStrings) { OS.write(Str.data(), Str.size()); OS.write('\0'); } } void DWARFYAML::EmitDebugAbbrev(raw_ostream &OS, const DWARFYAML::Data &DI) { for (auto AbbrevDecl : DI.AbbrevDecls) { encodeULEB128(AbbrevDecl.Code, OS); // XXX BINARYEN This is a terminator. if (!AbbrevDecl.Code) { continue; } encodeULEB128(AbbrevDecl.Tag, OS); OS.write(AbbrevDecl.Children); for (auto Attr : AbbrevDecl.Attributes) { encodeULEB128(Attr.Attribute, OS); encodeULEB128(Attr.Form, OS); if (Attr.Form == dwarf::DW_FORM_implicit_const) encodeSLEB128(Attr.Value, OS); } encodeULEB128(0, OS); encodeULEB128(0, OS); } } void DWARFYAML::EmitDebugAranges(raw_ostream &OS, const DWARFYAML::Data &DI) { for (auto Range : DI.ARanges) { auto HeaderStart = OS.tell(); writeInitialLength(Range.Length, OS, DI.IsLittleEndian); writeInteger((uint16_t)Range.Version, OS, DI.IsLittleEndian); writeInteger((uint32_t)Range.CuOffset, OS, DI.IsLittleEndian); writeInteger((uint8_t)Range.AddrSize, OS, DI.IsLittleEndian); writeInteger((uint8_t)Range.SegSize, OS, DI.IsLittleEndian); auto HeaderSize = OS.tell() - HeaderStart; auto FirstDescriptor = alignTo(HeaderSize, Range.AddrSize * 2); ZeroFillBytes(OS, FirstDescriptor - HeaderSize); for (auto Descriptor : Range.Descriptors) { writeVariableSizedInteger(Descriptor.Address, Range.AddrSize, OS, DI.IsLittleEndian); writeVariableSizedInteger(Descriptor.Length, Range.AddrSize, OS, DI.IsLittleEndian); } ZeroFillBytes(OS, Range.AddrSize * 2); } } // XXX BINARYEN void DWARFYAML::EmitDebugRanges(raw_ostream &OS, const DWARFYAML::Data &DI) { // As DwarfStreamer.cpp says, "The debug_range section // format is totally trivial, consisting just of pairs of address // sized addresses describing the ranges." and apparently it ends // with a null termination of a pair of zeros for (auto Range : DI.Ranges) { writeInteger((uint32_t)Range.Start, OS, DI.IsLittleEndian); writeInteger((uint32_t)Range.End, OS, DI.IsLittleEndian); } } // XXX BINARYEN void DWARFYAML::EmitDebugLoc(raw_ostream &OS, const DWARFYAML::Data &DI) { for (auto Loc : DI.Locs) { writeInteger((uint32_t)Loc.Start, OS, DI.IsLittleEndian); writeInteger((uint32_t)Loc.End, OS, DI.IsLittleEndian); if (Loc.Start == 0 && Loc.End == 0) { // End of a list. continue; } if (Loc.Start != -1) { writeInteger((uint16_t)Loc.Location.size(), OS, DI.IsLittleEndian); for (auto x : Loc.Location) { writeInteger((uint8_t)x, OS, DI.IsLittleEndian); } } } } void DWARFYAML::EmitPubSection(raw_ostream &OS, const DWARFYAML::PubSection &Sect, bool IsLittleEndian) { writeInitialLength(Sect.Length, OS, IsLittleEndian); writeInteger((uint16_t)Sect.Version, OS, IsLittleEndian); writeInteger((uint32_t)Sect.UnitOffset, OS, IsLittleEndian); writeInteger((uint32_t)Sect.UnitSize, OS, IsLittleEndian); for (auto Entry : Sect.Entries) { writeInteger((uint32_t)Entry.DieOffset, OS, IsLittleEndian); if (Sect.IsGNUStyle) writeInteger((uint32_t)Entry.Descriptor, OS, IsLittleEndian); OS.write(Entry.Name.data(), Entry.Name.size()); OS.write('\0'); } } namespace { /// An extension of the DWARFYAML::ConstVisitor which writes compile /// units and DIEs to a stream. class DumpVisitor : public DWARFYAML::ConstVisitor { raw_ostream &OS; size_t StartPos; // XXX BINARYEN protected: void onStartCompileUnit(const DWARFYAML::Unit &CU) override { writeInitialLength(CU.Length, OS, DebugInfo.IsLittleEndian); StartPos = OS.tell(); // XXX BINARYEN writeInteger((uint16_t)CU.Version, OS, DebugInfo.IsLittleEndian); if(CU.Version >= 5) { writeInteger((uint8_t)CU.Type, OS, DebugInfo.IsLittleEndian); writeInteger((uint8_t)CU.AddrSize, OS, DebugInfo.IsLittleEndian); writeInteger((uint32_t)CU.AbbrOffset, OS, DebugInfo.IsLittleEndian); }else { writeInteger((uint32_t)CU.AbbrOffset, OS, DebugInfo.IsLittleEndian); writeInteger((uint8_t)CU.AddrSize, OS, DebugInfo.IsLittleEndian); } } // XXX BINARYEN Make sure we emit the right size. We should not change the // size as we only modify relocatable fields like addresses, and such fields // have a fixed size, so any change is a bug. void onEndCompileUnit(const DWARFYAML::Unit &CU) { size_t EndPos = OS.tell(); if (EndPos - StartPos != CU.Length.getLength()) { llvm_unreachable("compile unit size was incorrect"); } } void onStartDIE(const DWARFYAML::Unit &CU, const DWARFYAML::Entry &DIE) override { encodeULEB128(DIE.AbbrCode, OS); } void onValue(const uint8_t U) override { writeInteger(U, OS, DebugInfo.IsLittleEndian); } void onValue(const uint16_t U) override { writeInteger(U, OS, DebugInfo.IsLittleEndian); } void onValue(const uint32_t U) override { writeInteger(U, OS, DebugInfo.IsLittleEndian); } void onValue(const uint64_t U, const bool LEB = false) override { if (LEB) encodeULEB128(U, OS); else writeInteger(U, OS, DebugInfo.IsLittleEndian); } void onValue(const int64_t S, const bool LEB = false) override { if (LEB) encodeSLEB128(S, OS); else writeInteger(S, OS, DebugInfo.IsLittleEndian); } void onValue(const StringRef String) override { OS.write(String.data(), String.size()); OS.write('\0'); } void onValue(const MemoryBufferRef MBR) override { OS.write(MBR.getBufferStart(), MBR.getBufferSize()); } public: DumpVisitor(const DWARFYAML::Data &DI, raw_ostream &Out) : DWARFYAML::ConstVisitor(DI), OS(Out) {} }; } // namespace void DWARFYAML::EmitDebugInfo(raw_ostream &OS, const DWARFYAML::Data &DI) { DumpVisitor Visitor(DI, OS); Visitor.traverseDebugInfo(); } static void EmitFileEntry(raw_ostream &OS, const DWARFYAML::File &File) { OS.write(File.Name.data(), File.Name.size()); OS.write('\0'); encodeULEB128(File.DirIdx, OS); encodeULEB128(File.ModTime, OS); encodeULEB128(File.Length, OS); } // XXX BINARYEN: Refactor to an *Internal method that allows us to optionally // compute the new lengths. static void EmitDebugLineInternal(raw_ostream &RealOS, const DWARFYAML::Data &DI, std::vector* computedLengths) { for (auto &LineTable : DI.DebugLines) { // XXX BINARYEN We need to update each line table's length. Write to a // temp stream first, then get the size from that. std::string Buffer; raw_string_ostream OS(Buffer); // XXX BINARYEN writeInitialLength(LineTable.Length, OS, DI.IsLittleEndian); uint64_t SizeOfPrologueLength = LineTable.Length.isDWARF64() ? 8 : 4; writeInteger((uint16_t)LineTable.Version, OS, DI.IsLittleEndian); writeVariableSizedInteger(LineTable.PrologueLength, SizeOfPrologueLength, OS, DI.IsLittleEndian); writeInteger((uint8_t)LineTable.MinInstLength, OS, DI.IsLittleEndian); if (LineTable.Version >= 4) writeInteger((uint8_t)LineTable.MaxOpsPerInst, OS, DI.IsLittleEndian); writeInteger((uint8_t)LineTable.DefaultIsStmt, OS, DI.IsLittleEndian); writeInteger((uint8_t)LineTable.LineBase, OS, DI.IsLittleEndian); writeInteger((uint8_t)LineTable.LineRange, OS, DI.IsLittleEndian); writeInteger((uint8_t)LineTable.OpcodeBase, OS, DI.IsLittleEndian); for (auto OpcodeLength : LineTable.StandardOpcodeLengths) writeInteger((uint8_t)OpcodeLength, OS, DI.IsLittleEndian); for (auto IncludeDir : LineTable.IncludeDirs) { OS.write(IncludeDir.data(), IncludeDir.size()); OS.write('\0'); } OS.write('\0'); for (auto File : LineTable.Files) EmitFileEntry(OS, File); OS.write('\0'); for (auto Op : LineTable.Opcodes) { writeInteger((uint8_t)Op.Opcode, OS, DI.IsLittleEndian); if (Op.Opcode == 0) { encodeULEB128(Op.ExtLen, OS); writeInteger((uint8_t)Op.SubOpcode, OS, DI.IsLittleEndian); switch (Op.SubOpcode) { case dwarf::DW_LNE_set_address: case dwarf::DW_LNE_set_discriminator: writeVariableSizedInteger(Op.Data, DI.CompileUnits[0].AddrSize, OS, DI.IsLittleEndian); break; case dwarf::DW_LNE_define_file: EmitFileEntry(OS, Op.FileEntry); break; case dwarf::DW_LNE_end_sequence: break; default: for (auto OpByte : Op.UnknownOpcodeData) writeInteger((uint8_t)OpByte, OS, DI.IsLittleEndian); } } else if (Op.Opcode < LineTable.OpcodeBase) { switch (Op.Opcode) { case dwarf::DW_LNS_copy: case dwarf::DW_LNS_negate_stmt: case dwarf::DW_LNS_set_basic_block: case dwarf::DW_LNS_const_add_pc: case dwarf::DW_LNS_set_prologue_end: case dwarf::DW_LNS_set_epilogue_begin: break; case dwarf::DW_LNS_advance_pc: case dwarf::DW_LNS_set_file: case dwarf::DW_LNS_set_column: case dwarf::DW_LNS_set_isa: encodeULEB128(Op.Data, OS); break; case dwarf::DW_LNS_advance_line: encodeSLEB128(Op.SData, OS); break; case dwarf::DW_LNS_fixed_advance_pc: writeInteger((uint16_t)Op.Data, OS, DI.IsLittleEndian); break; default: for (auto OpData : Op.StandardOpcodeData) { encodeULEB128(OpData, OS); } } } } // XXX BINARYEN Write to the actual stream, with the proper size. // We assume for now that the length fits in 32 bits. size_t Size = OS.str().size(); if (Size >= UINT32_MAX) { llvm_unreachable("Table is too big"); } if (computedLengths) { computedLengths->push_back(Size); } writeInteger((uint32_t)Size, RealOS, DI.IsLittleEndian); RealOS << OS.str(); } } void DWARFYAML::EmitDebugLine(raw_ostream &RealOS, const DWARFYAML::Data &DI) { EmitDebugLineInternal(RealOS, DI, nullptr); } void DWARFYAML::ComputeDebugLine(Data &DI, std::vector& computedLengths) { // TODO: Avoid writing out the data, or at least cache it so we don't need to // do it again later. std::string buffer; llvm::raw_string_ostream tempStream(buffer); EmitDebugLineInternal(tempStream, DI, &computedLengths); } using EmitFuncType = void (*)(raw_ostream &, const DWARFYAML::Data &); static void EmitDebugSectionImpl(const DWARFYAML::Data &DI, EmitFuncType EmitFunc, StringRef Sec, StringMap> &OutputBuffers) { std::string Data; raw_string_ostream DebugInfoStream(Data); EmitFunc(DebugInfoStream, DI); DebugInfoStream.flush(); if (!Data.empty()) OutputBuffers[Sec] = MemoryBuffer::getMemBufferCopy(Data); } namespace { class DIEFixupVisitor : public DWARFYAML::Visitor { uint64_t Length; public: DIEFixupVisitor(DWARFYAML::Data &DI) : DWARFYAML::Visitor(DI){}; private: virtual void onStartCompileUnit(DWARFYAML::Unit &CU) { Length = 7; } virtual void onEndCompileUnit(DWARFYAML::Unit &CU) { CU.Length.setLength(Length); } virtual void onStartDIE(DWARFYAML::Unit &CU, DWARFYAML::Entry &DIE) { Length += getULEB128Size(DIE.AbbrCode); } virtual void onValue(const uint8_t U) { Length += 1; } virtual void onValue(const uint16_t U) { Length += 2; } virtual void onValue(const uint32_t U) { Length += 4; } virtual void onValue(const uint64_t U, const bool LEB = false) { if (LEB) Length += getULEB128Size(U); else Length += 8; } virtual void onValue(const int64_t S, const bool LEB = false) { if (LEB) Length += getSLEB128Size(S); else Length += 8; } virtual void onValue(const StringRef String) { Length += String.size() + 1; } virtual void onValue(const MemoryBufferRef MBR) { Length += MBR.getBufferSize(); } }; } // namespace Expected>> DWARFYAML::EmitDebugSections(StringRef YAMLString, bool ApplyFixups, bool IsLittleEndian) { yaml::Input YIn(YAMLString); DWARFYAML::Data DI; DI.IsLittleEndian = IsLittleEndian; YIn >> DI; if (YIn.error()) return errorCodeToError(YIn.error()); if (ApplyFixups) { DIEFixupVisitor DIFixer(DI); DIFixer.traverseDebugInfo(); } StringMap> DebugSections; EmitDebugSectionImpl(DI, &DWARFYAML::EmitDebugInfo, "debug_info", DebugSections); EmitDebugSectionImpl(DI, &DWARFYAML::EmitDebugLine, "debug_line", DebugSections); EmitDebugSectionImpl(DI, &DWARFYAML::EmitDebugStr, "debug_str", DebugSections); EmitDebugSectionImpl(DI, &DWARFYAML::EmitDebugAbbrev, "debug_abbrev", DebugSections); EmitDebugSectionImpl(DI, &DWARFYAML::EmitDebugAranges, "debug_aranges", DebugSections); return std::move(DebugSections); } // XXX BINARYEN <-- namespace llvm { namespace DWARFYAML { StringMap> EmitDebugSections(llvm::DWARFYAML::Data &DI, bool ApplyFixups) { if (ApplyFixups) { DIEFixupVisitor DIFixer(DI); DIFixer.traverseDebugInfo(); } StringMap> DebugSections; EmitDebugSectionImpl(DI, &DWARFYAML::EmitDebugInfo, "debug_info", DebugSections); EmitDebugSectionImpl(DI, &DWARFYAML::EmitDebugLine, "debug_line", DebugSections); EmitDebugSectionImpl(DI, &DWARFYAML::EmitDebugStr, "debug_str", DebugSections); EmitDebugSectionImpl(DI, &DWARFYAML::EmitDebugAbbrev, "debug_abbrev", DebugSections); EmitDebugSectionImpl(DI, &DWARFYAML::EmitDebugAranges, "debug_aranges", DebugSections); EmitDebugSectionImpl(DI, &DWARFYAML::EmitDebugRanges, "debug_ranges", DebugSections); // XXX BINARYEN EmitDebugSectionImpl(DI, &DWARFYAML::EmitDebugLoc, "debug_loc", DebugSections); // XXX BINARYEN return std::move(DebugSections); } } // namespace DWARFYAML } // namespace llvm // XXX BINARYEN --> binaryen-version_91/third_party/llvm-project/DWARFExpression.cpp000066400000000000000000000300031362402614000252510ustar00rootroot00000000000000//===-- DWARFExpression.cpp -----------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFExpression.h" #include "llvm/DebugInfo/DWARF/DWARFUnit.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/Support/Format.h" #include #include #include using namespace llvm; using namespace dwarf; namespace llvm { typedef std::vector DescVector; static DescVector getDescriptions() { DescVector Descriptions; typedef DWARFExpression::Operation Op; typedef Op::Description Desc; Descriptions.resize(0xff); Descriptions[DW_OP_addr] = Desc(Op::Dwarf2, Op::SizeAddr); Descriptions[DW_OP_deref] = Desc(Op::Dwarf2); Descriptions[DW_OP_const1u] = Desc(Op::Dwarf2, Op::Size1); Descriptions[DW_OP_const1s] = Desc(Op::Dwarf2, Op::SignedSize1); Descriptions[DW_OP_const2u] = Desc(Op::Dwarf2, Op::Size2); Descriptions[DW_OP_const2s] = Desc(Op::Dwarf2, Op::SignedSize2); Descriptions[DW_OP_const4u] = Desc(Op::Dwarf2, Op::Size4); Descriptions[DW_OP_const4s] = Desc(Op::Dwarf2, Op::SignedSize4); Descriptions[DW_OP_const8u] = Desc(Op::Dwarf2, Op::Size8); Descriptions[DW_OP_const8s] = Desc(Op::Dwarf2, Op::SignedSize8); Descriptions[DW_OP_constu] = Desc(Op::Dwarf2, Op::SizeLEB); Descriptions[DW_OP_consts] = Desc(Op::Dwarf2, Op::SignedSizeLEB); Descriptions[DW_OP_dup] = Desc(Op::Dwarf2); Descriptions[DW_OP_drop] = Desc(Op::Dwarf2); Descriptions[DW_OP_over] = Desc(Op::Dwarf2); Descriptions[DW_OP_pick] = Desc(Op::Dwarf2, Op::Size1); Descriptions[DW_OP_swap] = Desc(Op::Dwarf2); Descriptions[DW_OP_rot] = Desc(Op::Dwarf2); Descriptions[DW_OP_xderef] = Desc(Op::Dwarf2); Descriptions[DW_OP_abs] = Desc(Op::Dwarf2); Descriptions[DW_OP_and] = Desc(Op::Dwarf2); Descriptions[DW_OP_div] = Desc(Op::Dwarf2); Descriptions[DW_OP_minus] = Desc(Op::Dwarf2); Descriptions[DW_OP_mod] = Desc(Op::Dwarf2); Descriptions[DW_OP_mul] = Desc(Op::Dwarf2); Descriptions[DW_OP_neg] = Desc(Op::Dwarf2); Descriptions[DW_OP_not] = Desc(Op::Dwarf2); Descriptions[DW_OP_or] = Desc(Op::Dwarf2); Descriptions[DW_OP_plus] = Desc(Op::Dwarf2); Descriptions[DW_OP_plus_uconst] = Desc(Op::Dwarf2, Op::SizeLEB); Descriptions[DW_OP_shl] = Desc(Op::Dwarf2); Descriptions[DW_OP_shr] = Desc(Op::Dwarf2); Descriptions[DW_OP_shra] = Desc(Op::Dwarf2); Descriptions[DW_OP_xor] = Desc(Op::Dwarf2); Descriptions[DW_OP_skip] = Desc(Op::Dwarf2, Op::SignedSize2); Descriptions[DW_OP_bra] = Desc(Op::Dwarf2, Op::SignedSize2); Descriptions[DW_OP_eq] = Desc(Op::Dwarf2); Descriptions[DW_OP_ge] = Desc(Op::Dwarf2); Descriptions[DW_OP_gt] = Desc(Op::Dwarf2); Descriptions[DW_OP_le] = Desc(Op::Dwarf2); Descriptions[DW_OP_lt] = Desc(Op::Dwarf2); Descriptions[DW_OP_ne] = Desc(Op::Dwarf2); for (uint16_t LA = DW_OP_lit0; LA <= DW_OP_lit31; ++LA) Descriptions[LA] = Desc(Op::Dwarf2); for (uint16_t LA = DW_OP_reg0; LA <= DW_OP_reg31; ++LA) Descriptions[LA] = Desc(Op::Dwarf2); for (uint16_t LA = DW_OP_breg0; LA <= DW_OP_breg31; ++LA) Descriptions[LA] = Desc(Op::Dwarf2, Op::SignedSizeLEB); Descriptions[DW_OP_regx] = Desc(Op::Dwarf2, Op::SizeLEB); Descriptions[DW_OP_fbreg] = Desc(Op::Dwarf2, Op::SignedSizeLEB); Descriptions[DW_OP_bregx] = Desc(Op::Dwarf2, Op::SizeLEB, Op::SignedSizeLEB); Descriptions[DW_OP_piece] = Desc(Op::Dwarf2, Op::SizeLEB); Descriptions[DW_OP_deref_size] = Desc(Op::Dwarf2, Op::Size1); Descriptions[DW_OP_xderef_size] = Desc(Op::Dwarf2, Op::Size1); Descriptions[DW_OP_nop] = Desc(Op::Dwarf2); Descriptions[DW_OP_push_object_address] = Desc(Op::Dwarf3); Descriptions[DW_OP_call2] = Desc(Op::Dwarf3, Op::Size2); Descriptions[DW_OP_call4] = Desc(Op::Dwarf3, Op::Size4); Descriptions[DW_OP_call_ref] = Desc(Op::Dwarf3, Op::SizeRefAddr); Descriptions[DW_OP_form_tls_address] = Desc(Op::Dwarf3); Descriptions[DW_OP_call_frame_cfa] = Desc(Op::Dwarf3); Descriptions[DW_OP_bit_piece] = Desc(Op::Dwarf3, Op::SizeLEB, Op::SizeLEB); Descriptions[DW_OP_implicit_value] = Desc(Op::Dwarf3, Op::SizeLEB, Op::SizeBlock); Descriptions[DW_OP_stack_value] = Desc(Op::Dwarf3); Descriptions[DW_OP_WASM_location] = Desc(Op::Dwarf4, Op::SizeLEB, Op::SignedSizeLEB); Descriptions[DW_OP_GNU_push_tls_address] = Desc(Op::Dwarf3); Descriptions[DW_OP_addrx] = Desc(Op::Dwarf4, Op::SizeLEB); Descriptions[DW_OP_GNU_addr_index] = Desc(Op::Dwarf4, Op::SizeLEB); Descriptions[DW_OP_GNU_const_index] = Desc(Op::Dwarf4, Op::SizeLEB); Descriptions[DW_OP_GNU_entry_value] = Desc(Op::Dwarf4, Op::SizeLEB); Descriptions[DW_OP_convert] = Desc(Op::Dwarf5, Op::BaseTypeRef); Descriptions[DW_OP_entry_value] = Desc(Op::Dwarf5, Op::SizeLEB); return Descriptions; } static DWARFExpression::Operation::Description getOpDesc(unsigned OpCode) { // FIXME: Make this constexpr once all compilers are smart enough to do it. static DescVector Descriptions = getDescriptions(); // Handle possible corrupted or unsupported operation. if (OpCode >= Descriptions.size()) return {}; return Descriptions[OpCode]; } static uint8_t getRefAddrSize(uint8_t AddrSize, uint16_t Version) { return (Version == 2) ? AddrSize : 4; } bool DWARFExpression::Operation::extract(DataExtractor Data, uint16_t Version, uint8_t AddressSize, uint64_t Offset) { Opcode = Data.getU8(&Offset); Desc = getOpDesc(Opcode); if (Desc.Version == Operation::DwarfNA) { EndOffset = Offset; return false; } for (unsigned Operand = 0; Operand < 2; ++Operand) { unsigned Size = Desc.Op[Operand]; unsigned Signed = Size & Operation::SignBit; if (Size == Operation::SizeNA) break; switch (Size & ~Operation::SignBit) { case Operation::Size1: Operands[Operand] = Data.getU8(&Offset); if (Signed) Operands[Operand] = (int8_t)Operands[Operand]; break; case Operation::Size2: Operands[Operand] = Data.getU16(&Offset); if (Signed) Operands[Operand] = (int16_t)Operands[Operand]; break; case Operation::Size4: Operands[Operand] = Data.getU32(&Offset); if (Signed) Operands[Operand] = (int32_t)Operands[Operand]; break; case Operation::Size8: Operands[Operand] = Data.getU64(&Offset); break; case Operation::SizeAddr: if (AddressSize == 8) { Operands[Operand] = Data.getU64(&Offset); } else if (AddressSize == 4) { Operands[Operand] = Data.getU32(&Offset); } else { assert(AddressSize == 2); Operands[Operand] = Data.getU16(&Offset); } break; case Operation::SizeRefAddr: if (getRefAddrSize(AddressSize, Version) == 8) { Operands[Operand] = Data.getU64(&Offset); } else if (getRefAddrSize(AddressSize, Version) == 4) { Operands[Operand] = Data.getU32(&Offset); } else { assert(getRefAddrSize(AddressSize, Version) == 2); Operands[Operand] = Data.getU16(&Offset); } break; case Operation::SizeLEB: if (Signed) Operands[Operand] = Data.getSLEB128(&Offset); else Operands[Operand] = Data.getULEB128(&Offset); break; case Operation::BaseTypeRef: Operands[Operand] = Data.getULEB128(&Offset); break; case Operation::SizeBlock: // We need a size, so this cannot be the first operand if (Operand == 0) return false; // Store the offset of the block as the value. Operands[Operand] = Offset; Offset += Operands[Operand - 1]; break; default: llvm_unreachable("Unknown DWARFExpression Op size"); } OperandEndOffsets[Operand] = Offset; } EndOffset = Offset; return true; } static bool prettyPrintRegisterOp(raw_ostream &OS, uint8_t Opcode, uint64_t Operands[2], const MCRegisterInfo *MRI, bool isEH) { if (!MRI) return false; uint64_t DwarfRegNum; unsigned OpNum = 0; if (Opcode == DW_OP_bregx || Opcode == DW_OP_regx) DwarfRegNum = Operands[OpNum++]; else if (Opcode >= DW_OP_breg0 && Opcode < DW_OP_bregx) DwarfRegNum = Opcode - DW_OP_breg0; else DwarfRegNum = Opcode - DW_OP_reg0; if (Optional LLVMRegNum = MRI->getLLVMRegNum(DwarfRegNum, isEH)) { if (const char *RegName = MRI->getName(*LLVMRegNum)) { if ((Opcode >= DW_OP_breg0 && Opcode <= DW_OP_breg31) || Opcode == DW_OP_bregx) OS << format(" %s%+" PRId64, RegName, Operands[OpNum]); else OS << ' ' << RegName; return true; } } return false; } bool DWARFExpression::Operation::print(raw_ostream &OS, const DWARFExpression *Expr, const MCRegisterInfo *RegInfo, DWARFUnit *U, bool isEH) { if (Error) { OS << ""; return false; } StringRef Name = OperationEncodingString(Opcode); assert(!Name.empty() && "DW_OP has no name!"); OS << Name; if ((Opcode >= DW_OP_breg0 && Opcode <= DW_OP_breg31) || (Opcode >= DW_OP_reg0 && Opcode <= DW_OP_reg31) || Opcode == DW_OP_bregx || Opcode == DW_OP_regx) if (prettyPrintRegisterOp(OS, Opcode, Operands, RegInfo, isEH)) return true; for (unsigned Operand = 0; Operand < 2; ++Operand) { unsigned Size = Desc.Op[Operand]; unsigned Signed = Size & Operation::SignBit; if (Size == Operation::SizeNA) break; if (Size == Operation::BaseTypeRef && U) { auto Die = U->getDIEForOffset(U->getOffset() + Operands[Operand]); if (Die && Die.getTag() == dwarf::DW_TAG_base_type) { OS << format(" (0x%08" PRIx64 ")", U->getOffset() + Operands[Operand]); if (auto Name = Die.find(dwarf::DW_AT_name)) OS << " \"" << Name->getAsCString() << "\""; } else { OS << format(" ", Operands[Operand]); } } else if (Size == Operation::SizeBlock) { uint64_t Offset = Operands[Operand]; for (unsigned i = 0; i < Operands[Operand - 1]; ++i) OS << format(" 0x%02x", Expr->Data.getU8(&Offset)); } else { if (Signed) OS << format(" %+" PRId64, (int64_t)Operands[Operand]); else if (Opcode != DW_OP_entry_value && Opcode != DW_OP_GNU_entry_value) OS << format(" 0x%" PRIx64, Operands[Operand]); } } return true; } void DWARFExpression::print(raw_ostream &OS, const MCRegisterInfo *RegInfo, DWARFUnit *U, bool IsEH) const { uint32_t EntryValExprSize = 0; for (auto &Op : *this) { if (!Op.print(OS, this, RegInfo, U, IsEH)) { uint64_t FailOffset = Op.getEndOffset(); while (FailOffset < Data.getData().size()) OS << format(" %02x", Data.getU8(&FailOffset)); return; } if (Op.getCode() == DW_OP_entry_value || Op.getCode() == DW_OP_GNU_entry_value) { OS << "("; EntryValExprSize = Op.getRawOperand(0); continue; } if (EntryValExprSize) { EntryValExprSize--; if (EntryValExprSize == 0) OS << ")"; } if (Op.getEndOffset() < Data.getData().size()) OS << ", "; } } bool DWARFExpression::Operation::verify(DWARFUnit *U) { for (unsigned Operand = 0; Operand < 2; ++Operand) { unsigned Size = Desc.Op[Operand]; if (Size == Operation::SizeNA) break; if (Size == Operation::BaseTypeRef) { auto Die = U->getDIEForOffset(U->getOffset() + Operands[Operand]); if (!Die || Die.getTag() != dwarf::DW_TAG_base_type) { Error = true; return false; } } } return true; } bool DWARFExpression::verify(DWARFUnit *U) { for (auto &Op : *this) if (!Op.verify(U)) return false; return true; } } // namespace llvm binaryen-version_91/third_party/llvm-project/DWARFFormValue.cpp000066400000000000000000000527041362402614000250260ustar00rootroot00000000000000//===- DWARFFormValue.cpp -------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFFormValue.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/None.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/DWARF/DWARFRelocMap.h" #include "llvm/DebugInfo/DWARF/DWARFUnit.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Format.h" #include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include #include #include using namespace llvm; using namespace dwarf; static const DWARFFormValue::FormClass DWARF5FormClasses[] = { DWARFFormValue::FC_Unknown, // 0x0 DWARFFormValue::FC_Address, // 0x01 DW_FORM_addr DWARFFormValue::FC_Unknown, // 0x02 unused DWARFFormValue::FC_Block, // 0x03 DW_FORM_block2 DWARFFormValue::FC_Block, // 0x04 DW_FORM_block4 DWARFFormValue::FC_Constant, // 0x05 DW_FORM_data2 // --- These can be FC_SectionOffset in DWARF3 and below: DWARFFormValue::FC_Constant, // 0x06 DW_FORM_data4 DWARFFormValue::FC_Constant, // 0x07 DW_FORM_data8 // --- DWARFFormValue::FC_String, // 0x08 DW_FORM_string DWARFFormValue::FC_Block, // 0x09 DW_FORM_block DWARFFormValue::FC_Block, // 0x0a DW_FORM_block1 DWARFFormValue::FC_Constant, // 0x0b DW_FORM_data1 DWARFFormValue::FC_Flag, // 0x0c DW_FORM_flag DWARFFormValue::FC_Constant, // 0x0d DW_FORM_sdata DWARFFormValue::FC_String, // 0x0e DW_FORM_strp DWARFFormValue::FC_Constant, // 0x0f DW_FORM_udata DWARFFormValue::FC_Reference, // 0x10 DW_FORM_ref_addr DWARFFormValue::FC_Reference, // 0x11 DW_FORM_ref1 DWARFFormValue::FC_Reference, // 0x12 DW_FORM_ref2 DWARFFormValue::FC_Reference, // 0x13 DW_FORM_ref4 DWARFFormValue::FC_Reference, // 0x14 DW_FORM_ref8 DWARFFormValue::FC_Reference, // 0x15 DW_FORM_ref_udata DWARFFormValue::FC_Indirect, // 0x16 DW_FORM_indirect DWARFFormValue::FC_SectionOffset, // 0x17 DW_FORM_sec_offset DWARFFormValue::FC_Exprloc, // 0x18 DW_FORM_exprloc DWARFFormValue::FC_Flag, // 0x19 DW_FORM_flag_present DWARFFormValue::FC_String, // 0x1a DW_FORM_strx DWARFFormValue::FC_Address, // 0x1b DW_FORM_addrx DWARFFormValue::FC_Reference, // 0x1c DW_FORM_ref_sup4 DWARFFormValue::FC_String, // 0x1d DW_FORM_strp_sup DWARFFormValue::FC_Constant, // 0x1e DW_FORM_data16 DWARFFormValue::FC_String, // 0x1f DW_FORM_line_strp DWARFFormValue::FC_Reference, // 0x20 DW_FORM_ref_sig8 DWARFFormValue::FC_Constant, // 0x21 DW_FORM_implicit_const DWARFFormValue::FC_SectionOffset, // 0x22 DW_FORM_loclistx DWARFFormValue::FC_SectionOffset, // 0x23 DW_FORM_rnglistx DWARFFormValue::FC_Reference, // 0x24 DW_FORM_ref_sup8 DWARFFormValue::FC_String, // 0x25 DW_FORM_strx1 DWARFFormValue::FC_String, // 0x26 DW_FORM_strx2 DWARFFormValue::FC_String, // 0x27 DW_FORM_strx3 DWARFFormValue::FC_String, // 0x28 DW_FORM_strx4 DWARFFormValue::FC_Address, // 0x29 DW_FORM_addrx1 DWARFFormValue::FC_Address, // 0x2a DW_FORM_addrx2 DWARFFormValue::FC_Address, // 0x2b DW_FORM_addrx3 DWARFFormValue::FC_Address, // 0x2c DW_FORM_addrx4 }; DWARFFormValue DWARFFormValue::createFromSValue(dwarf::Form F, int64_t V) { return DWARFFormValue(F, ValueType(V)); } DWARFFormValue DWARFFormValue::createFromUValue(dwarf::Form F, uint64_t V) { return DWARFFormValue(F, ValueType(V)); } DWARFFormValue DWARFFormValue::createFromPValue(dwarf::Form F, const char *V) { return DWARFFormValue(F, ValueType(V)); } DWARFFormValue DWARFFormValue::createFromBlockValue(dwarf::Form F, ArrayRef D) { ValueType V; V.uval = D.size(); V.data = D.data(); return DWARFFormValue(F, V); } DWARFFormValue DWARFFormValue::createFromUnit(dwarf::Form F, const DWARFUnit *U, uint64_t *OffsetPtr) { DWARFFormValue FormValue(F); FormValue.extractValue(U->getDebugInfoExtractor(), OffsetPtr, U->getFormParams(), U); return FormValue; } bool DWARFFormValue::skipValue(dwarf::Form Form, DataExtractor DebugInfoData, uint64_t *OffsetPtr, const dwarf::FormParams Params) { bool Indirect = false; do { switch (Form) { // Blocks of inlined data that have a length field and the data bytes // inlined in the .debug_info. case DW_FORM_exprloc: case DW_FORM_block: { uint64_t size = DebugInfoData.getULEB128(OffsetPtr); *OffsetPtr += size; return true; } case DW_FORM_block1: { uint8_t size = DebugInfoData.getU8(OffsetPtr); *OffsetPtr += size; return true; } case DW_FORM_block2: { uint16_t size = DebugInfoData.getU16(OffsetPtr); *OffsetPtr += size; return true; } case DW_FORM_block4: { uint32_t size = DebugInfoData.getU32(OffsetPtr); *OffsetPtr += size; return true; } // Inlined NULL terminated C-strings. case DW_FORM_string: DebugInfoData.getCStr(OffsetPtr); return true; case DW_FORM_addr: case DW_FORM_ref_addr: case DW_FORM_flag_present: case DW_FORM_data1: case DW_FORM_data2: case DW_FORM_data4: case DW_FORM_data8: case DW_FORM_data16: case DW_FORM_flag: case DW_FORM_ref1: case DW_FORM_ref2: case DW_FORM_ref4: case DW_FORM_ref8: case DW_FORM_ref_sig8: case DW_FORM_ref_sup4: case DW_FORM_ref_sup8: case DW_FORM_strx1: case DW_FORM_strx2: case DW_FORM_strx4: case DW_FORM_addrx1: case DW_FORM_addrx2: case DW_FORM_addrx4: case DW_FORM_sec_offset: case DW_FORM_strp: case DW_FORM_strp_sup: case DW_FORM_line_strp: case DW_FORM_GNU_ref_alt: case DW_FORM_GNU_strp_alt: if (Optional FixedSize = dwarf::getFixedFormByteSize(Form, Params)) { *OffsetPtr += *FixedSize; return true; } return false; // signed or unsigned LEB 128 values. case DW_FORM_sdata: DebugInfoData.getSLEB128(OffsetPtr); return true; case DW_FORM_udata: case DW_FORM_ref_udata: case DW_FORM_strx: case DW_FORM_addrx: case DW_FORM_loclistx: case DW_FORM_rnglistx: case DW_FORM_GNU_addr_index: case DW_FORM_GNU_str_index: DebugInfoData.getULEB128(OffsetPtr); return true; case DW_FORM_indirect: Indirect = true; Form = static_cast(DebugInfoData.getULEB128(OffsetPtr)); break; default: return false; } } while (Indirect); return true; } bool DWARFFormValue::isFormClass(DWARFFormValue::FormClass FC) const { // First, check DWARF5 form classes. if (Form < makeArrayRef(DWARF5FormClasses).size() && DWARF5FormClasses[Form] == FC) return true; // Check more forms from extensions and proposals. switch (Form) { case DW_FORM_GNU_ref_alt: return (FC == FC_Reference); case DW_FORM_GNU_addr_index: return (FC == FC_Address); case DW_FORM_GNU_str_index: case DW_FORM_GNU_strp_alt: return (FC == FC_String); default: break; } if (FC == FC_SectionOffset) { if (Form == DW_FORM_strp || Form == DW_FORM_line_strp) return true; // In DWARF3 DW_FORM_data4 and DW_FORM_data8 served also as a section // offset. If we don't have a DWARFUnit, default to the old behavior. if (Form == DW_FORM_data4 || Form == DW_FORM_data8) return !U || U->getVersion() <= 3; } return false; } bool DWARFFormValue::extractValue(const DWARFDataExtractor &Data, uint64_t *OffsetPtr, dwarf::FormParams FP, const DWARFContext *Ctx, const DWARFUnit *CU) { if (!Ctx && CU) Ctx = &CU->getContext(); C = Ctx; U = CU; bool Indirect = false; bool IsBlock = false; Value.data = nullptr; // Read the value for the form into value and follow and DW_FORM_indirect // instances we run into do { Indirect = false; switch (Form) { case DW_FORM_addr: case DW_FORM_ref_addr: { uint16_t Size = (Form == DW_FORM_addr) ? FP.AddrSize : FP.getRefAddrByteSize(); Value.uval = Data.getRelocatedValue(Size, OffsetPtr, &Value.SectionIndex); break; } case DW_FORM_exprloc: case DW_FORM_block: Value.uval = Data.getULEB128(OffsetPtr); IsBlock = true; break; case DW_FORM_block1: Value.uval = Data.getU8(OffsetPtr); IsBlock = true; break; case DW_FORM_block2: Value.uval = Data.getU16(OffsetPtr); IsBlock = true; break; case DW_FORM_block4: Value.uval = Data.getU32(OffsetPtr); IsBlock = true; break; case DW_FORM_data1: case DW_FORM_ref1: case DW_FORM_flag: case DW_FORM_strx1: case DW_FORM_addrx1: Value.uval = Data.getU8(OffsetPtr); break; case DW_FORM_data2: case DW_FORM_ref2: case DW_FORM_strx2: case DW_FORM_addrx2: Value.uval = Data.getU16(OffsetPtr); break; case DW_FORM_strx3: Value.uval = Data.getU24(OffsetPtr); break; case DW_FORM_data4: case DW_FORM_ref4: case DW_FORM_ref_sup4: case DW_FORM_strx4: case DW_FORM_addrx4: Value.uval = Data.getRelocatedValue(4, OffsetPtr); break; case DW_FORM_data8: case DW_FORM_ref8: case DW_FORM_ref_sup8: Value.uval = Data.getRelocatedValue(8, OffsetPtr); break; case DW_FORM_data16: // Treat this like a 16-byte block. Value.uval = 16; IsBlock = true; break; case DW_FORM_sdata: Value.sval = Data.getSLEB128(OffsetPtr); break; case DW_FORM_udata: case DW_FORM_ref_udata: case DW_FORM_rnglistx: Value.uval = Data.getULEB128(OffsetPtr); break; case DW_FORM_string: Value.cstr = Data.getCStr(OffsetPtr); break; case DW_FORM_indirect: Form = static_cast(Data.getULEB128(OffsetPtr)); Indirect = true; break; case DW_FORM_strp: case DW_FORM_sec_offset: case DW_FORM_GNU_ref_alt: case DW_FORM_GNU_strp_alt: case DW_FORM_line_strp: case DW_FORM_strp_sup: { Value.uval = Data.getRelocatedValue(FP.getDwarfOffsetByteSize(), OffsetPtr); break; } case DW_FORM_flag_present: Value.uval = 1; break; case DW_FORM_ref_sig8: Value.uval = Data.getU64(OffsetPtr); break; case DW_FORM_GNU_addr_index: case DW_FORM_GNU_str_index: case DW_FORM_addrx: case DW_FORM_strx: Value.uval = Data.getULEB128(OffsetPtr); break; default: // DWARFFormValue::skipValue() will have caught this and caused all // DWARF DIEs to fail to be parsed, so this code is not be reachable. llvm_unreachable("unsupported form"); } } while (Indirect); if (IsBlock) { StringRef Str = Data.getData().substr(*OffsetPtr, Value.uval); Value.data = nullptr; if (!Str.empty()) { Value.data = Str.bytes_begin(); *OffsetPtr += Value.uval; } } return true; } void DWARFFormValue::dumpSectionedAddress(raw_ostream &OS, DIDumpOptions DumpOpts, object::SectionedAddress SA) const { OS << format("0x%016" PRIx64, SA.Address); dumpAddressSection(U->getContext().getDWARFObj(), OS, DumpOpts, SA.SectionIndex); } void DWARFFormValue::dumpAddressSection(const DWARFObject &Obj, raw_ostream &OS, DIDumpOptions DumpOpts, uint64_t SectionIndex) { if (!DumpOpts.Verbose || SectionIndex == -1ULL) return; ArrayRef SectionNames = Obj.getSectionNames(); const auto &SecRef = SectionNames[SectionIndex]; OS << " \"" << SecRef.Name << '\"'; // Print section index if name is not unique. if (!SecRef.IsNameUnique) OS << format(" [%" PRIu64 "]", SectionIndex); } void DWARFFormValue::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { uint64_t UValue = Value.uval; bool CURelativeOffset = false; raw_ostream &AddrOS = DumpOpts.ShowAddresses ? WithColor(OS, HighlightColor::Address).get() : nulls(); switch (Form) { case DW_FORM_addr: dumpSectionedAddress(AddrOS, DumpOpts, {Value.uval, Value.SectionIndex}); break; case DW_FORM_addrx: case DW_FORM_addrx1: case DW_FORM_addrx2: case DW_FORM_addrx3: case DW_FORM_addrx4: case DW_FORM_GNU_addr_index: { if (U == nullptr) { OS << ""; break; } Optional A = U->getAddrOffsetSectionItem(UValue); if (!A || DumpOpts.Verbose) AddrOS << format("indexed (%8.8x) address = ", (uint32_t)UValue); if (A) dumpSectionedAddress(AddrOS, DumpOpts, *A); else OS << ""; break; } case DW_FORM_flag_present: OS << "true"; break; case DW_FORM_flag: case DW_FORM_data1: OS << format("0x%02x", (uint8_t)UValue); break; case DW_FORM_data2: OS << format("0x%04x", (uint16_t)UValue); break; case DW_FORM_data4: OS << format("0x%08x", (uint32_t)UValue); break; case DW_FORM_ref_sig8: AddrOS << format("0x%016" PRIx64, UValue); break; case DW_FORM_data8: OS << format("0x%016" PRIx64, UValue); break; case DW_FORM_data16: OS << format_bytes(ArrayRef(Value.data, 16), None, 16, 16); break; case DW_FORM_string: OS << '"'; OS.write_escaped(Value.cstr); OS << '"'; break; case DW_FORM_exprloc: case DW_FORM_block: case DW_FORM_block1: case DW_FORM_block2: case DW_FORM_block4: if (UValue > 0) { switch (Form) { case DW_FORM_exprloc: case DW_FORM_block: AddrOS << format("<0x%" PRIx64 "> ", UValue); break; case DW_FORM_block1: AddrOS << format("<0x%2.2x> ", (uint8_t)UValue); break; case DW_FORM_block2: AddrOS << format("<0x%4.4x> ", (uint16_t)UValue); break; case DW_FORM_block4: AddrOS << format("<0x%8.8x> ", (uint32_t)UValue); break; default: break; } const uint8_t *DataPtr = Value.data; if (DataPtr) { // UValue contains size of block const uint8_t *EndDataPtr = DataPtr + UValue; while (DataPtr < EndDataPtr) { AddrOS << format("%2.2x ", *DataPtr); ++DataPtr; } } else OS << "NULL"; } break; case DW_FORM_sdata: OS << Value.sval; break; case DW_FORM_udata: OS << Value.uval; break; case DW_FORM_strp: if (DumpOpts.Verbose) OS << format(" .debug_str[0x%8.8x] = ", (uint32_t)UValue); dumpString(OS); break; case DW_FORM_line_strp: if (DumpOpts.Verbose) OS << format(" .debug_line_str[0x%8.8x] = ", (uint32_t)UValue); dumpString(OS); break; case DW_FORM_strx: case DW_FORM_strx1: case DW_FORM_strx2: case DW_FORM_strx3: case DW_FORM_strx4: case DW_FORM_GNU_str_index: if (DumpOpts.Verbose) OS << format("indexed (%8.8x) string = ", (uint32_t)UValue); dumpString(OS); break; case DW_FORM_GNU_strp_alt: if (DumpOpts.Verbose) OS << format("alt indirect string, offset: 0x%" PRIx64 "", UValue); dumpString(OS); break; case DW_FORM_ref_addr: AddrOS << format("0x%016" PRIx64, UValue); break; case DW_FORM_ref1: CURelativeOffset = true; if (DumpOpts.Verbose) AddrOS << format("cu + 0x%2.2x", (uint8_t)UValue); break; case DW_FORM_ref2: CURelativeOffset = true; if (DumpOpts.Verbose) AddrOS << format("cu + 0x%4.4x", (uint16_t)UValue); break; case DW_FORM_ref4: CURelativeOffset = true; if (DumpOpts.Verbose) AddrOS << format("cu + 0x%4.4x", (uint32_t)UValue); break; case DW_FORM_ref8: CURelativeOffset = true; if (DumpOpts.Verbose) AddrOS << format("cu + 0x%8.8" PRIx64, UValue); break; case DW_FORM_ref_udata: CURelativeOffset = true; if (DumpOpts.Verbose) AddrOS << format("cu + 0x%" PRIx64, UValue); break; case DW_FORM_GNU_ref_alt: AddrOS << format("", UValue); break; // All DW_FORM_indirect attributes should be resolved prior to calling // this function case DW_FORM_indirect: OS << "DW_FORM_indirect"; break; case DW_FORM_rnglistx: OS << format("indexed (0x%x) rangelist = ", (uint32_t)UValue); break; // Should be formatted to 64-bit for DWARF64. case DW_FORM_sec_offset: AddrOS << format("0x%08x", (uint32_t)UValue); break; default: OS << format("DW_FORM(0x%4.4x)", Form); break; } if (CURelativeOffset) { if (DumpOpts.Verbose) OS << " => {"; if (DumpOpts.ShowAddresses) WithColor(OS, HighlightColor::Address).get() << format("0x%8.8" PRIx64, UValue + (U ? U->getOffset() : 0)); if (DumpOpts.Verbose) OS << "}"; } } void DWARFFormValue::dumpString(raw_ostream &OS) const { Optional DbgStr = getAsCString(); if (DbgStr.hasValue()) { auto COS = WithColor(OS, HighlightColor::String); COS.get() << '"'; COS.get().write_escaped(DbgStr.getValue()); COS.get() << '"'; } } Optional DWARFFormValue::getAsCString() const { if (!isFormClass(FC_String)) return None; if (Form == DW_FORM_string) return Value.cstr; // FIXME: Add support for DW_FORM_GNU_strp_alt if (Form == DW_FORM_GNU_strp_alt || C == nullptr) return None; uint64_t Offset = Value.uval; if (Form == DW_FORM_line_strp) { // .debug_line_str is tracked in the Context. if (const char *Str = C->getLineStringExtractor().getCStr(&Offset)) return Str; return None; } if (Form == DW_FORM_GNU_str_index || Form == DW_FORM_strx || Form == DW_FORM_strx1 || Form == DW_FORM_strx2 || Form == DW_FORM_strx3 || Form == DW_FORM_strx4) { if (!U) return None; Optional StrOffset = U->getStringOffsetSectionItem(Offset); if (!StrOffset) return None; Offset = *StrOffset; } // Prefer the Unit's string extractor, because for .dwo it will point to // .debug_str.dwo, while the Context's extractor always uses .debug_str. if (U) { if (const char *Str = U->getStringExtractor().getCStr(&Offset)) return Str; return None; } if (const char *Str = C->getStringExtractor().getCStr(&Offset)) return Str; return None; } Optional DWARFFormValue::getAsAddress() const { if (auto SA = getAsSectionedAddress()) return SA->Address; return None; } Optional DWARFFormValue::getAsSectionedAddress() const { if (!isFormClass(FC_Address)) return None; if (Form == DW_FORM_GNU_addr_index || Form == DW_FORM_addrx) { uint32_t Index = Value.uval; if (!U) return None; Optional SA = U->getAddrOffsetSectionItem(Index); if (!SA) return None; return SA; } return {{Value.uval, Value.SectionIndex}}; } Optional DWARFFormValue::getAsReference() const { if (auto R = getAsRelativeReference()) return R->Unit ? R->Unit->getOffset() + R->Offset : R->Offset; return None; } Optional DWARFFormValue::getAsRelativeReference() const { if (!isFormClass(FC_Reference)) return None; switch (Form) { case DW_FORM_ref1: case DW_FORM_ref2: case DW_FORM_ref4: case DW_FORM_ref8: case DW_FORM_ref_udata: if (!U) return None; return UnitOffset{const_cast(U), Value.uval}; case DW_FORM_ref_addr: case DW_FORM_ref_sig8: case DW_FORM_GNU_ref_alt: return UnitOffset{nullptr, Value.uval}; default: return None; } } Optional DWARFFormValue::getAsSectionOffset() const { if (!isFormClass(FC_SectionOffset)) return None; return Value.uval; } Optional DWARFFormValue::getAsUnsignedConstant() const { if ((!isFormClass(FC_Constant) && !isFormClass(FC_Flag)) || Form == DW_FORM_sdata) return None; return Value.uval; } Optional DWARFFormValue::getAsSignedConstant() const { if ((!isFormClass(FC_Constant) && !isFormClass(FC_Flag)) || (Form == DW_FORM_udata && uint64_t(std::numeric_limits::max()) < Value.uval)) return None; switch (Form) { case DW_FORM_data4: return int32_t(Value.uval); case DW_FORM_data2: return int16_t(Value.uval); case DW_FORM_data1: return int8_t(Value.uval); case DW_FORM_sdata: case DW_FORM_data8: default: return Value.sval; } } Optional> DWARFFormValue::getAsBlock() const { if (!isFormClass(FC_Block) && !isFormClass(FC_Exprloc) && Form != DW_FORM_data16) return None; return makeArrayRef(Value.data, Value.uval); } Optional DWARFFormValue::getAsCStringOffset() const { if (!isFormClass(FC_String) && Form == DW_FORM_string) return None; return Value.uval; } Optional DWARFFormValue::getAsReferenceUVal() const { if (!isFormClass(FC_Reference)) return None; return Value.uval; } binaryen-version_91/third_party/llvm-project/DWARFGdbIndex.cpp000066400000000000000000000160311362402614000246030ustar00rootroot00000000000000//===- DWARFGdbIndex.cpp --------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFGdbIndex.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Format.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/raw_ostream.h" #include #include #include #include #include using namespace llvm; // .gdb_index section format reference: // https://sourceware.org/gdb/onlinedocs/gdb/Index-Section-Format.html void DWARFGdbIndex::dumpCUList(raw_ostream &OS) const { OS << format("\n CU list offset = 0x%x, has %" PRId64 " entries:", CuListOffset, (uint64_t)CuList.size()) << '\n'; uint32_t I = 0; for (const CompUnitEntry &CU : CuList) OS << format(" %d: Offset = 0x%llx, Length = 0x%llx\n", I++, CU.Offset, CU.Length); } void DWARFGdbIndex::dumpTUList(raw_ostream &OS) const { OS << formatv("\n Types CU list offset = {0:x}, has {1} entries:\n", TuListOffset, TuList.size()); uint32_t I = 0; for (const TypeUnitEntry &TU : TuList) OS << formatv(" {0}: offset = {1:x8}, type_offset = {2:x8}, " "type_signature = {3:x16}\n", I++, TU.Offset, TU.TypeOffset, TU.TypeSignature); } void DWARFGdbIndex::dumpAddressArea(raw_ostream &OS) const { OS << format("\n Address area offset = 0x%x, has %" PRId64 " entries:", AddressAreaOffset, (uint64_t)AddressArea.size()) << '\n'; for (const AddressEntry &Addr : AddressArea) OS << format( " Low/High address = [0x%llx, 0x%llx) (Size: 0x%llx), CU id = %d\n", Addr.LowAddress, Addr.HighAddress, Addr.HighAddress - Addr.LowAddress, Addr.CuIndex); } void DWARFGdbIndex::dumpSymbolTable(raw_ostream &OS) const { OS << format("\n Symbol table offset = 0x%x, size = %" PRId64 ", filled slots:", SymbolTableOffset, (uint64_t)SymbolTable.size()) << '\n'; uint32_t I = -1; for (const SymTableEntry &E : SymbolTable) { ++I; if (!E.NameOffset && !E.VecOffset) continue; OS << format(" %d: Name offset = 0x%x, CU vector offset = 0x%x\n", I, E.NameOffset, E.VecOffset); StringRef Name = ConstantPoolStrings.substr( ConstantPoolOffset - StringPoolOffset + E.NameOffset); auto CuVector = std::find_if( ConstantPoolVectors.begin(), ConstantPoolVectors.end(), [&](const std::pair> &V) { return V.first == E.VecOffset; }); assert(CuVector != ConstantPoolVectors.end() && "Invalid symbol table"); uint32_t CuVectorId = CuVector - ConstantPoolVectors.begin(); OS << format(" String name: %s, CU vector index: %d\n", Name.data(), CuVectorId); } } void DWARFGdbIndex::dumpConstantPool(raw_ostream &OS) const { OS << format("\n Constant pool offset = 0x%x, has %" PRId64 " CU vectors:", ConstantPoolOffset, (uint64_t)ConstantPoolVectors.size()); uint32_t I = 0; for (const auto &V : ConstantPoolVectors) { OS << format("\n %d(0x%x): ", I++, V.first); for (uint32_t Val : V.second) OS << format("0x%x ", Val); } OS << '\n'; } void DWARFGdbIndex::dump(raw_ostream &OS) { if (HasError) { OS << "\n\n"; return; } if (HasContent) { OS << " Version = " << Version << '\n'; dumpCUList(OS); dumpTUList(OS); dumpAddressArea(OS); dumpSymbolTable(OS); dumpConstantPool(OS); } } bool DWARFGdbIndex::parseImpl(DataExtractor Data) { uint64_t Offset = 0; // Only version 7 is supported at this moment. Version = Data.getU32(&Offset); if (Version != 7) return false; CuListOffset = Data.getU32(&Offset); TuListOffset = Data.getU32(&Offset); AddressAreaOffset = Data.getU32(&Offset); SymbolTableOffset = Data.getU32(&Offset); ConstantPoolOffset = Data.getU32(&Offset); if (Offset != CuListOffset) return false; uint32_t CuListSize = (TuListOffset - CuListOffset) / 16; CuList.reserve(CuListSize); for (uint32_t i = 0; i < CuListSize; ++i) { uint64_t CuOffset = Data.getU64(&Offset); uint64_t CuLength = Data.getU64(&Offset); CuList.push_back({CuOffset, CuLength}); } // CU Types are no longer needed as DWARF skeleton type units never made it // into the standard. uint32_t TuListSize = (AddressAreaOffset - TuListOffset) / 24; TuList.resize(TuListSize); for (uint32_t I = 0; I < TuListSize; ++I) { uint64_t CuOffset = Data.getU64(&Offset); uint64_t TypeOffset = Data.getU64(&Offset); uint64_t Signature = Data.getU64(&Offset); TuList[I] = {CuOffset, TypeOffset, Signature}; } uint32_t AddressAreaSize = (SymbolTableOffset - AddressAreaOffset) / 20; AddressArea.reserve(AddressAreaSize); for (uint32_t i = 0; i < AddressAreaSize; ++i) { uint64_t LowAddress = Data.getU64(&Offset); uint64_t HighAddress = Data.getU64(&Offset); uint32_t CuIndex = Data.getU32(&Offset); AddressArea.push_back({LowAddress, HighAddress, CuIndex}); } // The symbol table. This is an open addressed hash table. The size of the // hash table is always a power of 2. // Each slot in the hash table consists of a pair of offset_type values. The // first value is the offset of the symbol's name in the constant pool. The // second value is the offset of the CU vector in the constant pool. // If both values are 0, then this slot in the hash table is empty. This is ok // because while 0 is a valid constant pool index, it cannot be a valid index // for both a string and a CU vector. uint32_t SymTableSize = (ConstantPoolOffset - SymbolTableOffset) / 8; SymbolTable.reserve(SymTableSize); uint32_t CuVectorsTotal = 0; for (uint32_t i = 0; i < SymTableSize; ++i) { uint32_t NameOffset = Data.getU32(&Offset); uint32_t CuVecOffset = Data.getU32(&Offset); SymbolTable.push_back({NameOffset, CuVecOffset}); if (NameOffset || CuVecOffset) ++CuVectorsTotal; } // The constant pool. CU vectors are stored first, followed by strings. // The first value is the number of CU indices in the vector. Each subsequent // value is the index and symbol attributes of a CU in the CU list. for (uint32_t i = 0; i < CuVectorsTotal; ++i) { ConstantPoolVectors.emplace_back(0, SmallVector()); auto &Vec = ConstantPoolVectors.back(); Vec.first = Offset - ConstantPoolOffset; uint32_t Num = Data.getU32(&Offset); for (uint32_t j = 0; j < Num; ++j) Vec.second.push_back(Data.getU32(&Offset)); } ConstantPoolStrings = Data.getData().drop_front(Offset); StringPoolOffset = Offset; return true; } void DWARFGdbIndex::parse(DataExtractor Data) { HasContent = !Data.getData().empty(); HasError = HasContent && !parseImpl(Data); } binaryen-version_91/third_party/llvm-project/DWARFListTable.cpp000066400000000000000000000122001362402614000247740ustar00rootroot00000000000000//===- DWARFListTable.cpp ---------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFListTable.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/Support/Errc.h" #include "llvm/Support/Error.h" #include "llvm/Support/Format.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; Error DWARFListTableHeader::extract(DWARFDataExtractor Data, uint64_t *OffsetPtr) { HeaderOffset = *OffsetPtr; // Read and verify the length field. if (!Data.isValidOffsetForDataOfSize(*OffsetPtr, sizeof(uint32_t))) return createStringError(errc::invalid_argument, "section is not large enough to contain a " "%s table length at offset 0x%" PRIx64, SectionName.data(), *OffsetPtr); Format = dwarf::DwarfFormat::DWARF32; uint8_t OffsetByteSize = 4; HeaderData.Length = Data.getRelocatedValue(4, OffsetPtr); if (HeaderData.Length == dwarf::DW_LENGTH_DWARF64) { Format = dwarf::DwarfFormat::DWARF64; OffsetByteSize = 8; HeaderData.Length = Data.getU64(OffsetPtr); } else if (HeaderData.Length >= dwarf::DW_LENGTH_lo_reserved) { return createStringError(errc::invalid_argument, "%s table at offset 0x%" PRIx64 " has unsupported reserved unit length of value 0x%8.8" PRIx64, SectionName.data(), HeaderOffset, HeaderData.Length); } uint64_t FullLength = HeaderData.Length + dwarf::getUnitLengthFieldByteSize(Format); assert(FullLength == length()); if (FullLength < getHeaderSize(Format)) return createStringError(errc::invalid_argument, "%s table at offset 0x%" PRIx64 " has too small length (0x%" PRIx64 ") to contain a complete header", SectionName.data(), HeaderOffset, FullLength); uint64_t End = HeaderOffset + FullLength; if (!Data.isValidOffsetForDataOfSize(HeaderOffset, FullLength)) return createStringError(errc::invalid_argument, "section is not large enough to contain a %s table " "of length 0x%" PRIx64 " at offset 0x%" PRIx64, SectionName.data(), FullLength, HeaderOffset); HeaderData.Version = Data.getU16(OffsetPtr); HeaderData.AddrSize = Data.getU8(OffsetPtr); HeaderData.SegSize = Data.getU8(OffsetPtr); HeaderData.OffsetEntryCount = Data.getU32(OffsetPtr); // Perform basic validation of the remaining header fields. if (HeaderData.Version != 5) return createStringError(errc::invalid_argument, "unrecognised %s table version %" PRIu16 " in table at offset 0x%" PRIx64, SectionName.data(), HeaderData.Version, HeaderOffset); if (HeaderData.AddrSize != 4 && HeaderData.AddrSize != 8) return createStringError(errc::not_supported, "%s table at offset 0x%" PRIx64 " has unsupported address size %" PRIu8, SectionName.data(), HeaderOffset, HeaderData.AddrSize); if (HeaderData.SegSize != 0) return createStringError(errc::not_supported, "%s table at offset 0x%" PRIx64 " has unsupported segment selector size %" PRIu8, SectionName.data(), HeaderOffset, HeaderData.SegSize); if (End < HeaderOffset + getHeaderSize(Format) + HeaderData.OffsetEntryCount * OffsetByteSize) return createStringError(errc::invalid_argument, "%s table at offset 0x%" PRIx64 " has more offset entries (%" PRIu32 ") than there is space for", SectionName.data(), HeaderOffset, HeaderData.OffsetEntryCount); Data.setAddressSize(HeaderData.AddrSize); for (uint32_t I = 0; I < HeaderData.OffsetEntryCount; ++I) Offsets.push_back(Data.getRelocatedValue(OffsetByteSize, OffsetPtr)); return Error::success(); } void DWARFListTableHeader::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const { if (DumpOpts.Verbose) OS << format("0x%8.8" PRIx64 ": ", HeaderOffset); OS << format( "%s list header: length = 0x%8.8" PRIx64 ", version = 0x%4.4" PRIx16 ", " "addr_size = 0x%2.2" PRIx8 ", seg_size = 0x%2.2" PRIx8 ", offset_entry_count = " "0x%8.8" PRIx32 "\n", ListTypeString.data(), HeaderData.Length, HeaderData.Version, HeaderData.AddrSize, HeaderData.SegSize, HeaderData.OffsetEntryCount); if (HeaderData.OffsetEntryCount > 0) { OS << "offsets: ["; for (const auto &Off : Offsets) { OS << format("\n0x%8.8" PRIx64, Off); if (DumpOpts.Verbose) OS << format(" => 0x%8.8" PRIx64, Off + HeaderOffset + getHeaderSize(Format)); } OS << "\n]\n"; } } uint64_t DWARFListTableHeader::length() const { if (HeaderData.Length == 0) return 0; return HeaderData.Length + dwarf::getUnitLengthFieldByteSize(Format); } binaryen-version_91/third_party/llvm-project/DWARFTypeUnit.cpp000066400000000000000000000036301362402614000247010ustar00rootroot00000000000000//===- DWARFTypeUnit.cpp --------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFTypeUnit.h" #include "llvm/DebugInfo/DIContext.h" #include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h" #include "llvm/DebugInfo/DWARF/DWARFDie.h" #include "llvm/DebugInfo/DWARF/DWARFUnit.h" #include "llvm/Support/Format.h" #include "llvm/Support/raw_ostream.h" #include using namespace llvm; void DWARFTypeUnit::dump(raw_ostream &OS, DIDumpOptions DumpOpts) { DWARFDie TD = getDIEForOffset(getTypeOffset() + getOffset()); const char *Name = TD.getName(DINameKind::ShortName); if (DumpOpts.SummarizeTypes) { OS << "name = '" << Name << "'" << " type_signature = " << format("0x%016" PRIx64, getTypeHash()) << " length = " << format("0x%08" PRIx64, getLength()) << '\n'; return; } OS << format("0x%08" PRIx64, getOffset()) << ": Type Unit:" << " length = " << format("0x%08" PRIx64, getLength()) << " version = " << format("0x%04x", getVersion()); if (getVersion() >= 5) OS << " unit_type = " << dwarf::UnitTypeString(getUnitType()); OS << " abbr_offset = " << format("0x%04" PRIx64, getAbbreviations()->getOffset()) << " addr_size = " << format("0x%02x", getAddressByteSize()) << " name = '" << Name << "'" << " type_signature = " << format("0x%016" PRIx64, getTypeHash()) << " type_offset = " << format("0x%04" PRIx64, getTypeOffset()) << " (next unit at " << format("0x%08" PRIx64, getNextUnitOffset()) << ")\n"; if (DWARFDie TU = getUnitDIE(false)) TU.dump(OS, 0, DumpOpts); else OS << "\n\n"; } binaryen-version_91/third_party/llvm-project/DWARFUnit.cpp000066400000000000000000001035451362402614000240450ustar00rootroot00000000000000//===- DWARFUnit.cpp ------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFUnit.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/DebugInfo/DWARF/DWARFAbbreviationDeclaration.h" #include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h" #include "llvm/DebugInfo/DWARF/DWARFDebugInfoEntry.h" #include "llvm/DebugInfo/DWARF/DWARFDebugRnglists.h" #include "llvm/DebugInfo/DWARF/DWARFDie.h" #include "llvm/DebugInfo/DWARF/DWARFFormValue.h" #include "llvm/DebugInfo/DWARF/DWARFTypeUnit.h" #include "llvm/Support/DataExtractor.h" #include "llvm/Support/Errc.h" #include "llvm/Support/Path.h" #include "llvm/Support/WithColor.h" #include #include #include #include #include #include #include using namespace llvm; using namespace dwarf; void DWARFUnitVector::addUnitsForSection(DWARFContext &C, const DWARFSection &Section, DWARFSectionKind SectionKind) { const DWARFObject &D = C.getDWARFObj(); addUnitsImpl(C, D, Section, C.getDebugAbbrev(), &D.getRangesSection(), &D.getLocSection(), D.getStrSection(), D.getStrOffsetsSection(), &D.getAddrSection(), D.getLineSection(), D.isLittleEndian(), false, false, SectionKind); } void DWARFUnitVector::addUnitsForDWOSection(DWARFContext &C, const DWARFSection &DWOSection, DWARFSectionKind SectionKind, bool Lazy) { const DWARFObject &D = C.getDWARFObj(); addUnitsImpl(C, D, DWOSection, C.getDebugAbbrevDWO(), &D.getRangesDWOSection(), &D.getLocDWOSection(), D.getStrDWOSection(), D.getStrOffsetsDWOSection(), &D.getAddrSection(), D.getLineDWOSection(), C.isLittleEndian(), true, Lazy, SectionKind); } void DWARFUnitVector::addUnitsImpl( DWARFContext &Context, const DWARFObject &Obj, const DWARFSection &Section, const DWARFDebugAbbrev *DA, const DWARFSection *RS, const DWARFSection *LocSection, StringRef SS, const DWARFSection &SOS, const DWARFSection *AOS, const DWARFSection &LS, bool LE, bool IsDWO, bool Lazy, DWARFSectionKind SectionKind) { DWARFDataExtractor Data(Obj, Section, LE, 0); // Lazy initialization of Parser, now that we have all section info. if (!Parser) { Parser = [=, &Context, &Obj, &Section, &SOS, &LS](uint64_t Offset, DWARFSectionKind SectionKind, const DWARFSection *CurSection, const DWARFUnitIndex::Entry *IndexEntry) -> std::unique_ptr { const DWARFSection &InfoSection = CurSection ? *CurSection : Section; DWARFDataExtractor Data(Obj, InfoSection, LE, 0); if (!Data.isValidOffset(Offset)) return nullptr; const DWARFUnitIndex *Index = nullptr; if (IsDWO) Index = &getDWARFUnitIndex(Context, SectionKind); DWARFUnitHeader Header; if (!Header.extract(Context, Data, &Offset, SectionKind, Index, IndexEntry)) return nullptr; std::unique_ptr U; if (Header.isTypeUnit()) U = std::make_unique(Context, InfoSection, Header, DA, RS, LocSection, SS, SOS, AOS, LS, LE, IsDWO, *this); else U = std::make_unique(Context, InfoSection, Header, DA, RS, LocSection, SS, SOS, AOS, LS, LE, IsDWO, *this); return U; }; } if (Lazy) return; // Find a reasonable insertion point within the vector. We skip over // (a) units from a different section, (b) units from the same section // but with lower offset-within-section. This keeps units in order // within a section, although not necessarily within the object file, // even if we do lazy parsing. auto I = this->begin(); uint64_t Offset = 0; while (Data.isValidOffset(Offset)) { if (I != this->end() && (&(*I)->getInfoSection() != &Section || (*I)->getOffset() == Offset)) { ++I; continue; } auto U = Parser(Offset, SectionKind, &Section, nullptr); // If parsing failed, we're done with this section. if (!U) break; Offset = U->getNextUnitOffset(); I = std::next(this->insert(I, std::move(U))); } } DWARFUnit *DWARFUnitVector::addUnit(std::unique_ptr Unit) { auto I = std::upper_bound(begin(), end(), Unit, [](const std::unique_ptr &LHS, const std::unique_ptr &RHS) { return LHS->getOffset() < RHS->getOffset(); }); return this->insert(I, std::move(Unit))->get(); } DWARFUnit *DWARFUnitVector::getUnitForOffset(uint64_t Offset) const { auto end = begin() + getNumInfoUnits(); auto *CU = std::upper_bound(begin(), end, Offset, [](uint64_t LHS, const std::unique_ptr &RHS) { return LHS < RHS->getNextUnitOffset(); }); if (CU != end && (*CU)->getOffset() <= Offset) return CU->get(); return nullptr; } DWARFUnit * DWARFUnitVector::getUnitForIndexEntry(const DWARFUnitIndex::Entry &E) { const auto *CUOff = E.getOffset(DW_SECT_INFO); if (!CUOff) return nullptr; auto Offset = CUOff->Offset; auto end = begin() + getNumInfoUnits(); auto *CU = std::upper_bound(begin(), end, CUOff->Offset, [](uint64_t LHS, const std::unique_ptr &RHS) { return LHS < RHS->getNextUnitOffset(); }); if (CU != end && (*CU)->getOffset() <= Offset) return CU->get(); if (!Parser) return nullptr; auto U = Parser(Offset, DW_SECT_INFO, nullptr, &E); if (!U) U = nullptr; auto *NewCU = U.get(); this->insert(CU, std::move(U)); ++NumInfoUnits; return NewCU; } DWARFUnit::DWARFUnit(DWARFContext &DC, const DWARFSection &Section, const DWARFUnitHeader &Header, const DWARFDebugAbbrev *DA, const DWARFSection *RS, const DWARFSection *LocSection, StringRef SS, const DWARFSection &SOS, const DWARFSection *AOS, const DWARFSection &LS, bool LE, bool IsDWO, const DWARFUnitVector &UnitVector) : Context(DC), InfoSection(Section), Header(Header), Abbrev(DA), RangeSection(RS), LocSection(LocSection), LineSection(LS), StringSection(SS), StringOffsetSection(SOS), AddrOffsetSection(AOS), isLittleEndian(LE), IsDWO(IsDWO), UnitVector(UnitVector) { clear(); // For split DWARF we only need to keep track of the location list section's // data (no relocations), and if we are reading a package file, we need to // adjust the location list data based on the index entries. if (IsDWO) { LocSectionData = LocSection->Data; if (auto *IndexEntry = Header.getIndexEntry()) if (const auto *C = IndexEntry->getOffset(DW_SECT_LOC)) LocSectionData = LocSectionData.substr(C->Offset, C->Length); } } DWARFUnit::~DWARFUnit() = default; DWARFDataExtractor DWARFUnit::getDebugInfoExtractor() const { return DWARFDataExtractor(Context.getDWARFObj(), InfoSection, isLittleEndian, getAddressByteSize()); } Optional DWARFUnit::getAddrOffsetSectionItem(uint32_t Index) const { if (IsDWO) { auto R = Context.info_section_units(); auto I = R.begin(); // Surprising if a DWO file has more than one skeleton unit in it - this // probably shouldn't be valid, but if a use case is found, here's where to // support it (probably have to linearly search for the matching skeleton CU // here) if (I != R.end() && std::next(I) == R.end()) return (*I)->getAddrOffsetSectionItem(Index); } uint64_t Offset = AddrOffsetSectionBase + Index * getAddressByteSize(); if (AddrOffsetSection->Data.size() < Offset + getAddressByteSize()) return None; DWARFDataExtractor DA(Context.getDWARFObj(), *AddrOffsetSection, isLittleEndian, getAddressByteSize()); uint64_t Section; uint64_t Address = DA.getRelocatedAddress(&Offset, &Section); return {{Address, Section}}; } Optional DWARFUnit::getStringOffsetSectionItem(uint32_t Index) const { if (!StringOffsetsTableContribution) return None; unsigned ItemSize = getDwarfStringOffsetsByteSize(); uint64_t Offset = getStringOffsetsBase() + Index * ItemSize; if (StringOffsetSection.Data.size() < Offset + ItemSize) return None; DWARFDataExtractor DA(Context.getDWARFObj(), StringOffsetSection, isLittleEndian, 0); return DA.getRelocatedValue(ItemSize, &Offset); } bool DWARFUnitHeader::extract(DWARFContext &Context, const DWARFDataExtractor &debug_info, uint64_t *offset_ptr, DWARFSectionKind SectionKind, const DWARFUnitIndex *Index, const DWARFUnitIndex::Entry *Entry) { Offset = *offset_ptr; IndexEntry = Entry; if (!IndexEntry && Index) IndexEntry = Index->getFromOffset(*offset_ptr); Length = debug_info.getRelocatedValue(4, offset_ptr); FormParams.Format = DWARF32; if (Length == dwarf::DW_LENGTH_DWARF64) { Length = debug_info.getU64(offset_ptr); FormParams.Format = DWARF64; } FormParams.Version = debug_info.getU16(offset_ptr); if (FormParams.Version >= 5) { UnitType = debug_info.getU8(offset_ptr); FormParams.AddrSize = debug_info.getU8(offset_ptr); AbbrOffset = debug_info.getRelocatedValue(FormParams.getDwarfOffsetByteSize(), offset_ptr); } else { AbbrOffset = debug_info.getRelocatedValue(FormParams.getDwarfOffsetByteSize(), offset_ptr); FormParams.AddrSize = debug_info.getU8(offset_ptr); // Fake a unit type based on the section type. This isn't perfect, // but distinguishing compile and type units is generally enough. if (SectionKind == DW_SECT_TYPES) UnitType = DW_UT_type; else UnitType = DW_UT_compile; } if (IndexEntry) { if (AbbrOffset) return false; auto *UnitContrib = IndexEntry->getOffset(); if (!UnitContrib || UnitContrib->Length != (Length + 4)) return false; auto *AbbrEntry = IndexEntry->getOffset(DW_SECT_ABBREV); if (!AbbrEntry) return false; AbbrOffset = AbbrEntry->Offset; } if (isTypeUnit()) { TypeHash = debug_info.getU64(offset_ptr); TypeOffset = debug_info.getUnsigned(offset_ptr, FormParams.getDwarfOffsetByteSize()); } else if (UnitType == DW_UT_split_compile || UnitType == DW_UT_skeleton) DWOId = debug_info.getU64(offset_ptr); // Header fields all parsed, capture the size of this unit header. assert(*offset_ptr - Offset <= 255 && "unexpected header size"); Size = uint8_t(*offset_ptr - Offset); // Type offset is unit-relative; should be after the header and before // the end of the current unit. bool TypeOffsetOK = !isTypeUnit() ? true : TypeOffset >= Size && TypeOffset < getLength() + getUnitLengthFieldByteSize(); bool LengthOK = debug_info.isValidOffset(getNextUnitOffset() - 1); bool VersionOK = DWARFContext::isSupportedVersion(getVersion()); bool AddrSizeOK = getAddressByteSize() == 4 || getAddressByteSize() == 8; if (!LengthOK || !VersionOK || !AddrSizeOK || !TypeOffsetOK) return false; // Keep track of the highest DWARF version we encounter across all units. Context.setMaxVersionIfGreater(getVersion()); return true; } // Parse the rangelist table header, including the optional array of offsets // following it (DWARF v5 and later). static Expected parseRngListTableHeader(DWARFDataExtractor &DA, uint64_t Offset, DwarfFormat Format) { // We are expected to be called with Offset 0 or pointing just past the table // header. Correct Offset in the latter case so that it points to the start // of the header. if (Offset > 0) { uint64_t HeaderSize = DWARFListTableHeader::getHeaderSize(Format); if (Offset < HeaderSize) return createStringError(errc::invalid_argument, "Did not detect a valid" " range list table with base = 0x%" PRIx64 "\n", Offset); Offset -= HeaderSize; } llvm::DWARFDebugRnglistTable Table; if (Error E = Table.extractHeaderAndOffsets(DA, &Offset)) return std::move(E); return Table; } Error DWARFUnit::extractRangeList(uint64_t RangeListOffset, DWARFDebugRangeList &RangeList) const { // Require that compile unit is extracted. assert(!DieArray.empty()); DWARFDataExtractor RangesData(Context.getDWARFObj(), *RangeSection, isLittleEndian, getAddressByteSize()); uint64_t ActualRangeListOffset = RangeSectionBase + RangeListOffset; return RangeList.extract(RangesData, &ActualRangeListOffset); } void DWARFUnit::clear() { Abbrevs = nullptr; BaseAddr.reset(); RangeSectionBase = 0; AddrOffsetSectionBase = 0; clearDIEs(false); DWO.reset(); } const char *DWARFUnit::getCompilationDir() { return dwarf::toString(getUnitDIE().find(DW_AT_comp_dir), nullptr); } void DWARFUnit::extractDIEsToVector( bool AppendCUDie, bool AppendNonCUDies, std::vector &Dies) const { if (!AppendCUDie && !AppendNonCUDies) return; // Set the offset to that of the first DIE and calculate the start of the // next compilation unit header. uint64_t DIEOffset = getOffset() + getHeaderSize(); uint64_t NextCUOffset = getNextUnitOffset(); DWARFDebugInfoEntry DIE; DWARFDataExtractor DebugInfoData = getDebugInfoExtractor(); uint32_t Depth = 0; bool IsCUDie = true; while (DIE.extractFast(*this, &DIEOffset, DebugInfoData, NextCUOffset, Depth)) { if (IsCUDie) { if (AppendCUDie) Dies.push_back(DIE); if (!AppendNonCUDies) break; // The average bytes per DIE entry has been seen to be // around 14-20 so let's pre-reserve the needed memory for // our DIE entries accordingly. Dies.reserve(Dies.size() + getDebugInfoSize() / 14); IsCUDie = false; } else { Dies.push_back(DIE); } if (const DWARFAbbreviationDeclaration *AbbrDecl = DIE.getAbbreviationDeclarationPtr()) { // Normal DIE if (AbbrDecl->hasChildren()) ++Depth; } else { // NULL DIE. if (Depth > 0) --Depth; if (Depth == 0) break; // We are done with this compile unit! } } // Give a little bit of info if we encounter corrupt DWARF (our offset // should always terminate at or before the start of the next compilation // unit header). if (DIEOffset > NextCUOffset) WithColor::warning() << format("DWARF compile unit extends beyond its " "bounds cu 0x%8.8" PRIx64 " " "at 0x%8.8" PRIx64 "\n", getOffset(), DIEOffset); } void DWARFUnit::extractDIEsIfNeeded(bool CUDieOnly) { if (Error e = tryExtractDIEsIfNeeded(CUDieOnly)) WithColor::error() << toString(std::move(e)); } Error DWARFUnit::tryExtractDIEsIfNeeded(bool CUDieOnly) { if ((CUDieOnly && !DieArray.empty()) || DieArray.size() > 1) return Error::success(); // Already parsed. bool HasCUDie = !DieArray.empty(); extractDIEsToVector(!HasCUDie, !CUDieOnly, DieArray); if (DieArray.empty()) return Error::success(); // If CU DIE was just parsed, copy several attribute values from it. if (HasCUDie) return Error::success(); DWARFDie UnitDie(this, &DieArray[0]); if (Optional DWOId = toUnsigned(UnitDie.find(DW_AT_GNU_dwo_id))) Header.setDWOId(*DWOId); if (!IsDWO) { assert(AddrOffsetSectionBase == 0); assert(RangeSectionBase == 0); AddrOffsetSectionBase = toSectionOffset(UnitDie.find(DW_AT_addr_base), 0); if (!AddrOffsetSectionBase) AddrOffsetSectionBase = toSectionOffset(UnitDie.find(DW_AT_GNU_addr_base), 0); RangeSectionBase = toSectionOffset(UnitDie.find(DW_AT_rnglists_base), 0); } // In general, in DWARF v5 and beyond we derive the start of the unit's // contribution to the string offsets table from the unit DIE's // DW_AT_str_offsets_base attribute. Split DWARF units do not use this // attribute, so we assume that there is a contribution to the string // offsets table starting at offset 0 of the debug_str_offsets.dwo section. // In both cases we need to determine the format of the contribution, // which may differ from the unit's format. DWARFDataExtractor DA(Context.getDWARFObj(), StringOffsetSection, isLittleEndian, 0); if (IsDWO || getVersion() >= 5) { auto StringOffsetOrError = IsDWO ? determineStringOffsetsTableContributionDWO(DA) : determineStringOffsetsTableContribution(DA); if (!StringOffsetOrError) return createStringError(errc::invalid_argument, "invalid reference to or invalid content in " ".debug_str_offsets[.dwo]: " + toString(StringOffsetOrError.takeError())); StringOffsetsTableContribution = *StringOffsetOrError; } // DWARF v5 uses the .debug_rnglists and .debug_rnglists.dwo sections to // describe address ranges. if (getVersion() >= 5) { if (IsDWO) setRangesSection(&Context.getDWARFObj().getRnglistsDWOSection(), 0); else setRangesSection(&Context.getDWARFObj().getRnglistsSection(), toSectionOffset(UnitDie.find(DW_AT_rnglists_base), 0)); if (RangeSection->Data.size()) { // Parse the range list table header. Individual range lists are // extracted lazily. DWARFDataExtractor RangesDA(Context.getDWARFObj(), *RangeSection, isLittleEndian, 0); auto TableOrError = parseRngListTableHeader(RangesDA, RangeSectionBase, Header.getFormat()); if (!TableOrError) return createStringError(errc::invalid_argument, "parsing a range list table: " + toString(TableOrError.takeError())); RngListTable = TableOrError.get(); // In a split dwarf unit, there is no DW_AT_rnglists_base attribute. // Adjust RangeSectionBase to point past the table header. if (IsDWO && RngListTable) RangeSectionBase = RngListTable->getHeaderSize(); } } // Don't fall back to DW_AT_GNU_ranges_base: it should be ignored for // skeleton CU DIE, so that DWARF users not aware of it are not broken. return Error::success(); } bool DWARFUnit::parseDWO() { if (IsDWO) return false; if (DWO.get()) return false; DWARFDie UnitDie = getUnitDIE(); if (!UnitDie) return false; auto DWOFileName = dwarf::toString(UnitDie.find(DW_AT_GNU_dwo_name)); if (!DWOFileName) return false; auto CompilationDir = dwarf::toString(UnitDie.find(DW_AT_comp_dir)); SmallString<16> AbsolutePath; if (sys::path::is_relative(*DWOFileName) && CompilationDir && *CompilationDir) { sys::path::append(AbsolutePath, *CompilationDir); } sys::path::append(AbsolutePath, *DWOFileName); auto DWOId = getDWOId(); if (!DWOId) return false; auto DWOContext = Context.getDWOContext(AbsolutePath); if (!DWOContext) return false; DWARFCompileUnit *DWOCU = DWOContext->getDWOCompileUnitForHash(*DWOId); if (!DWOCU) return false; DWO = std::shared_ptr(std::move(DWOContext), DWOCU); // Share .debug_addr and .debug_ranges section with compile unit in .dwo DWO->setAddrOffsetSection(AddrOffsetSection, AddrOffsetSectionBase); if (getVersion() >= 5) { DWO->setRangesSection(&Context.getDWARFObj().getRnglistsDWOSection(), 0); DWARFDataExtractor RangesDA(Context.getDWARFObj(), *RangeSection, isLittleEndian, 0); if (auto TableOrError = parseRngListTableHeader(RangesDA, RangeSectionBase, Header.getFormat())) DWO->RngListTable = TableOrError.get(); else WithColor::error() << "parsing a range list table: " << toString(TableOrError.takeError()) << '\n'; if (DWO->RngListTable) DWO->RangeSectionBase = DWO->RngListTable->getHeaderSize(); } else { auto DWORangesBase = UnitDie.getRangesBaseAttribute(); DWO->setRangesSection(RangeSection, DWORangesBase ? *DWORangesBase : 0); } return true; } void DWARFUnit::clearDIEs(bool KeepCUDie) { if (DieArray.size() > (unsigned)KeepCUDie) { DieArray.resize((unsigned)KeepCUDie); DieArray.shrink_to_fit(); } } Expected DWARFUnit::findRnglistFromOffset(uint64_t Offset) { if (getVersion() <= 4) { DWARFDebugRangeList RangeList; if (Error E = extractRangeList(Offset, RangeList)) return std::move(E); return RangeList.getAbsoluteRanges(getBaseAddress()); } if (RngListTable) { DWARFDataExtractor RangesData(Context.getDWARFObj(), *RangeSection, isLittleEndian, RngListTable->getAddrSize()); auto RangeListOrError = RngListTable->findList(RangesData, Offset); if (RangeListOrError) return RangeListOrError.get().getAbsoluteRanges(getBaseAddress(), *this); return RangeListOrError.takeError(); } return createStringError(errc::invalid_argument, "missing or invalid range list table"); } Expected DWARFUnit::findRnglistFromIndex(uint32_t Index) { if (auto Offset = getRnglistOffset(Index)) return findRnglistFromOffset(*Offset + RangeSectionBase); if (RngListTable) return createStringError(errc::invalid_argument, "invalid range list table index %d", Index); return createStringError(errc::invalid_argument, "missing or invalid range list table"); } Expected DWARFUnit::collectAddressRanges() { DWARFDie UnitDie = getUnitDIE(); if (!UnitDie) return createStringError(errc::invalid_argument, "No unit DIE"); // First, check if unit DIE describes address ranges for the whole unit. auto CUDIERangesOrError = UnitDie.getAddressRanges(); if (!CUDIERangesOrError) return createStringError(errc::invalid_argument, "decoding address ranges: %s", toString(CUDIERangesOrError.takeError()).c_str()); return *CUDIERangesOrError; } void DWARFUnit::updateAddressDieMap(DWARFDie Die) { if (Die.isSubroutineDIE()) { auto DIERangesOrError = Die.getAddressRanges(); if (DIERangesOrError) { for (const auto &R : DIERangesOrError.get()) { // Ignore 0-sized ranges. if (R.LowPC == R.HighPC) continue; auto B = AddrDieMap.upper_bound(R.LowPC); if (B != AddrDieMap.begin() && R.LowPC < (--B)->second.first) { // The range is a sub-range of existing ranges, we need to split the // existing range. if (R.HighPC < B->second.first) AddrDieMap[R.HighPC] = B->second; if (R.LowPC > B->first) AddrDieMap[B->first].first = R.LowPC; } AddrDieMap[R.LowPC] = std::make_pair(R.HighPC, Die); } } else llvm::consumeError(DIERangesOrError.takeError()); } // Parent DIEs are added to the AddrDieMap prior to the Children DIEs to // simplify the logic to update AddrDieMap. The child's range will always // be equal or smaller than the parent's range. With this assumption, when // adding one range into the map, it will at most split a range into 3 // sub-ranges. for (DWARFDie Child = Die.getFirstChild(); Child; Child = Child.getSibling()) updateAddressDieMap(Child); } DWARFDie DWARFUnit::getSubroutineForAddress(uint64_t Address) { extractDIEsIfNeeded(false); if (AddrDieMap.empty()) updateAddressDieMap(getUnitDIE()); auto R = AddrDieMap.upper_bound(Address); if (R == AddrDieMap.begin()) return DWARFDie(); // upper_bound's previous item contains Address. --R; if (Address >= R->second.first) return DWARFDie(); return R->second.second; } void DWARFUnit::getInlinedChainForAddress(uint64_t Address, SmallVectorImpl &InlinedChain) { assert(InlinedChain.empty()); // Try to look for subprogram DIEs in the DWO file. parseDWO(); // First, find the subroutine that contains the given address (the leaf // of inlined chain). DWARFDie SubroutineDIE = (DWO ? *DWO : *this).getSubroutineForAddress(Address); if (!SubroutineDIE) return; while (!SubroutineDIE.isSubprogramDIE()) { if (SubroutineDIE.getTag() == DW_TAG_inlined_subroutine) InlinedChain.push_back(SubroutineDIE); SubroutineDIE = SubroutineDIE.getParent(); } InlinedChain.push_back(SubroutineDIE); } const DWARFUnitIndex &llvm::getDWARFUnitIndex(DWARFContext &Context, DWARFSectionKind Kind) { if (Kind == DW_SECT_INFO) return Context.getCUIndex(); assert(Kind == DW_SECT_TYPES); return Context.getTUIndex(); } DWARFDie DWARFUnit::getParent(const DWARFDebugInfoEntry *Die) { if (!Die) return DWARFDie(); const uint32_t Depth = Die->getDepth(); // Unit DIEs always have a depth of zero and never have parents. if (Depth == 0) return DWARFDie(); // Depth of 1 always means parent is the compile/type unit. if (Depth == 1) return getUnitDIE(); // Look for previous DIE with a depth that is one less than the Die's depth. const uint32_t ParentDepth = Depth - 1; for (uint32_t I = getDIEIndex(Die) - 1; I > 0; --I) { if (DieArray[I].getDepth() == ParentDepth) return DWARFDie(this, &DieArray[I]); } return DWARFDie(); } DWARFDie DWARFUnit::getSibling(const DWARFDebugInfoEntry *Die) { if (!Die) return DWARFDie(); uint32_t Depth = Die->getDepth(); // Unit DIEs always have a depth of zero and never have siblings. if (Depth == 0) return DWARFDie(); // NULL DIEs don't have siblings. if (Die->getAbbreviationDeclarationPtr() == nullptr) return DWARFDie(); // Find the next DIE whose depth is the same as the Die's depth. for (size_t I = getDIEIndex(Die) + 1, EndIdx = DieArray.size(); I < EndIdx; ++I) { if (DieArray[I].getDepth() == Depth) return DWARFDie(this, &DieArray[I]); } return DWARFDie(); } DWARFDie DWARFUnit::getPreviousSibling(const DWARFDebugInfoEntry *Die) { if (!Die) return DWARFDie(); uint32_t Depth = Die->getDepth(); // Unit DIEs always have a depth of zero and never have siblings. if (Depth == 0) return DWARFDie(); // Find the previous DIE whose depth is the same as the Die's depth. for (size_t I = getDIEIndex(Die); I > 0;) { --I; if (DieArray[I].getDepth() == Depth - 1) return DWARFDie(); if (DieArray[I].getDepth() == Depth) return DWARFDie(this, &DieArray[I]); } return DWARFDie(); } DWARFDie DWARFUnit::getFirstChild(const DWARFDebugInfoEntry *Die) { if (!Die->hasChildren()) return DWARFDie(); // We do not want access out of bounds when parsing corrupted debug data. size_t I = getDIEIndex(Die) + 1; if (I >= DieArray.size()) return DWARFDie(); return DWARFDie(this, &DieArray[I]); } DWARFDie DWARFUnit::getLastChild(const DWARFDebugInfoEntry *Die) { if (!Die->hasChildren()) return DWARFDie(); uint32_t Depth = Die->getDepth(); for (size_t I = getDIEIndex(Die) + 1, EndIdx = DieArray.size(); I < EndIdx; ++I) { if (DieArray[I].getDepth() == Depth + 1 && DieArray[I].getTag() == dwarf::DW_TAG_null) return DWARFDie(this, &DieArray[I]); assert(DieArray[I].getDepth() > Depth && "Not processing children?"); } return DWARFDie(); } const DWARFAbbreviationDeclarationSet *DWARFUnit::getAbbreviations() const { if (!Abbrevs) Abbrevs = Abbrev->getAbbreviationDeclarationSet(Header.getAbbrOffset()); return Abbrevs; } llvm::Optional DWARFUnit::getBaseAddress() { if (BaseAddr) return BaseAddr; DWARFDie UnitDie = getUnitDIE(); Optional PC = UnitDie.find({DW_AT_low_pc, DW_AT_entry_pc}); BaseAddr = toSectionedAddress(PC); return BaseAddr; } Expected StrOffsetsContributionDescriptor::validateContributionSize( DWARFDataExtractor &DA) { uint8_t EntrySize = getDwarfOffsetByteSize(); // In order to ensure that we don't read a partial record at the end of // the section we validate for a multiple of the entry size. uint64_t ValidationSize = alignTo(Size, EntrySize); // Guard against overflow. if (ValidationSize >= Size) if (DA.isValidOffsetForDataOfSize((uint32_t)Base, ValidationSize)) return *this; return createStringError(errc::invalid_argument, "length exceeds section size"); } // Look for a DWARF64-formatted contribution to the string offsets table // starting at a given offset and record it in a descriptor. static Expected parseDWARF64StringOffsetsTableHeader(DWARFDataExtractor &DA, uint64_t Offset) { if (!DA.isValidOffsetForDataOfSize(Offset, 16)) return createStringError(errc::invalid_argument, "section offset exceeds section size"); if (DA.getU32(&Offset) != dwarf::DW_LENGTH_DWARF64) return createStringError(errc::invalid_argument, "32 bit contribution referenced from a 64 bit unit"); uint64_t Size = DA.getU64(&Offset); uint8_t Version = DA.getU16(&Offset); (void)DA.getU16(&Offset); // padding // The encoded length includes the 2-byte version field and the 2-byte // padding, so we need to subtract them out when we populate the descriptor. return StrOffsetsContributionDescriptor(Offset, Size - 4, Version, DWARF64); } // Look for a DWARF32-formatted contribution to the string offsets table // starting at a given offset and record it in a descriptor. static Expected parseDWARF32StringOffsetsTableHeader(DWARFDataExtractor &DA, uint64_t Offset) { if (!DA.isValidOffsetForDataOfSize(Offset, 8)) return createStringError(errc::invalid_argument, "section offset exceeds section size"); uint32_t ContributionSize = DA.getU32(&Offset); if (ContributionSize >= dwarf::DW_LENGTH_lo_reserved) return createStringError(errc::invalid_argument, "invalid length"); uint8_t Version = DA.getU16(&Offset); (void)DA.getU16(&Offset); // padding // The encoded length includes the 2-byte version field and the 2-byte // padding, so we need to subtract them out when we populate the descriptor. return StrOffsetsContributionDescriptor(Offset, ContributionSize - 4, Version, DWARF32); } static Expected parseDWARFStringOffsetsTableHeader(DWARFDataExtractor &DA, llvm::dwarf::DwarfFormat Format, uint64_t Offset) { StrOffsetsContributionDescriptor Desc; switch (Format) { case dwarf::DwarfFormat::DWARF64: { if (Offset < 16) return createStringError(errc::invalid_argument, "insufficient space for 64 bit header prefix"); auto DescOrError = parseDWARF64StringOffsetsTableHeader(DA, Offset - 16); if (!DescOrError) return DescOrError.takeError(); Desc = *DescOrError; break; } case dwarf::DwarfFormat::DWARF32: { if (Offset < 8) return createStringError(errc::invalid_argument, "insufficient space for 32 bit header prefix"); auto DescOrError = parseDWARF32StringOffsetsTableHeader(DA, Offset - 8); if (!DescOrError) return DescOrError.takeError(); Desc = *DescOrError; break; } } return Desc.validateContributionSize(DA); } Expected> DWARFUnit::determineStringOffsetsTableContribution(DWARFDataExtractor &DA) { uint64_t Offset; if (IsDWO) { Offset = 0; if (DA.getData().data() == nullptr) return None; } else { auto OptOffset = toSectionOffset(getUnitDIE().find(DW_AT_str_offsets_base)); if (!OptOffset) return None; Offset = *OptOffset; } auto DescOrError = parseDWARFStringOffsetsTableHeader(DA, Header.getFormat(), Offset); if (!DescOrError) return DescOrError.takeError(); return *DescOrError; } Expected> DWARFUnit::determineStringOffsetsTableContributionDWO(DWARFDataExtractor & DA) { uint64_t Offset = 0; auto IndexEntry = Header.getIndexEntry(); const auto *C = IndexEntry ? IndexEntry->getOffset(DW_SECT_STR_OFFSETS) : nullptr; if (C) Offset = C->Offset; if (getVersion() >= 5) { if (DA.getData().data() == nullptr) return None; Offset += Header.getFormat() == dwarf::DwarfFormat::DWARF32 ? 8 : 16; // Look for a valid contribution at the given offset. auto DescOrError = parseDWARFStringOffsetsTableHeader(DA, Header.getFormat(), Offset); if (!DescOrError) return DescOrError.takeError(); return *DescOrError; } // Prior to DWARF v5, we derive the contribution size from the // index table (in a package file). In a .dwo file it is simply // the length of the string offsets section. if (!IndexEntry) return { Optional( {0, StringOffsetSection.Data.size(), 4, DWARF32})}; if (C) return {Optional( {C->Offset, C->Length, 4, DWARF32})}; return None; } binaryen-version_91/third_party/llvm-project/DWARFUnitIndex.cpp000066400000000000000000000137771362402614000250440ustar00rootroot00000000000000//===- DWARFUnitIndex.cpp -------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFUnitIndex.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Format.h" #include "llvm/Support/raw_ostream.h" #include #include using namespace llvm; bool DWARFUnitIndex::Header::parse(DataExtractor IndexData, uint64_t *OffsetPtr) { if (!IndexData.isValidOffsetForDataOfSize(*OffsetPtr, 16)) return false; Version = IndexData.getU32(OffsetPtr); NumColumns = IndexData.getU32(OffsetPtr); NumUnits = IndexData.getU32(OffsetPtr); NumBuckets = IndexData.getU32(OffsetPtr); return Version <= 2; } void DWARFUnitIndex::Header::dump(raw_ostream &OS) const { OS << format("version = %u slots = %u\n\n", Version, NumBuckets); } bool DWARFUnitIndex::parse(DataExtractor IndexData) { bool b = parseImpl(IndexData); if (!b) { // Make sure we don't try to dump anything Header.NumBuckets = 0; // Release any partially initialized data. ColumnKinds.reset(); Rows.reset(); } return b; } bool DWARFUnitIndex::parseImpl(DataExtractor IndexData) { uint64_t Offset = 0; if (!Header.parse(IndexData, &Offset)) return false; if (!IndexData.isValidOffsetForDataOfSize( Offset, Header.NumBuckets * (8 + 4) + (2 * Header.NumUnits + 1) * 4 * Header.NumColumns)) return false; Rows = std::make_unique(Header.NumBuckets); auto Contribs = std::make_unique(Header.NumUnits); ColumnKinds = std::make_unique(Header.NumColumns); // Read Hash Table of Signatures for (unsigned i = 0; i != Header.NumBuckets; ++i) Rows[i].Signature = IndexData.getU64(&Offset); // Read Parallel Table of Indexes for (unsigned i = 0; i != Header.NumBuckets; ++i) { auto Index = IndexData.getU32(&Offset); if (!Index) continue; Rows[i].Index = this; Rows[i].Contributions = std::make_unique(Header.NumColumns); Contribs[Index - 1] = Rows[i].Contributions.get(); } // Read the Column Headers for (unsigned i = 0; i != Header.NumColumns; ++i) { ColumnKinds[i] = static_cast(IndexData.getU32(&Offset)); if (ColumnKinds[i] == InfoColumnKind) { if (InfoColumn != -1) return false; InfoColumn = i; } } if (InfoColumn == -1) return false; // Read Table of Section Offsets for (unsigned i = 0; i != Header.NumUnits; ++i) { auto *Contrib = Contribs[i]; for (unsigned i = 0; i != Header.NumColumns; ++i) Contrib[i].Offset = IndexData.getU32(&Offset); } // Read Table of Section Sizes for (unsigned i = 0; i != Header.NumUnits; ++i) { auto *Contrib = Contribs[i]; for (unsigned i = 0; i != Header.NumColumns; ++i) Contrib[i].Length = IndexData.getU32(&Offset); } return true; } StringRef DWARFUnitIndex::getColumnHeader(DWARFSectionKind DS) { #define CASE(DS) \ case DW_SECT_##DS: \ return #DS; switch (DS) { CASE(INFO); CASE(TYPES); CASE(ABBREV); CASE(LINE); CASE(LOC); CASE(STR_OFFSETS); CASE(MACINFO); CASE(MACRO); } llvm_unreachable("unknown DWARFSectionKind"); } void DWARFUnitIndex::dump(raw_ostream &OS) const { if (!*this) return; Header.dump(OS); OS << "Index Signature "; for (unsigned i = 0; i != Header.NumColumns; ++i) OS << ' ' << left_justify(getColumnHeader(ColumnKinds[i]), 24); OS << "\n----- ------------------"; for (unsigned i = 0; i != Header.NumColumns; ++i) OS << " ------------------------"; OS << '\n'; for (unsigned i = 0; i != Header.NumBuckets; ++i) { auto &Row = Rows[i]; if (auto *Contribs = Row.Contributions.get()) { OS << format("%5u 0x%016" PRIx64 " ", i + 1, Row.Signature); for (unsigned i = 0; i != Header.NumColumns; ++i) { auto &Contrib = Contribs[i]; OS << format("[0x%08x, 0x%08x) ", Contrib.Offset, Contrib.Offset + Contrib.Length); } OS << '\n'; } } } const DWARFUnitIndex::Entry::SectionContribution * DWARFUnitIndex::Entry::getOffset(DWARFSectionKind Sec) const { uint32_t i = 0; for (; i != Index->Header.NumColumns; ++i) if (Index->ColumnKinds[i] == Sec) return &Contributions[i]; return nullptr; } const DWARFUnitIndex::Entry::SectionContribution * DWARFUnitIndex::Entry::getOffset() const { return &Contributions[Index->InfoColumn]; } const DWARFUnitIndex::Entry * DWARFUnitIndex::getFromOffset(uint32_t Offset) const { if (OffsetLookup.empty()) { for (uint32_t i = 0; i != Header.NumBuckets; ++i) if (Rows[i].Contributions) OffsetLookup.push_back(&Rows[i]); llvm::sort(OffsetLookup, [&](Entry *E1, Entry *E2) { return E1->Contributions[InfoColumn].Offset < E2->Contributions[InfoColumn].Offset; }); } auto I = partition_point(OffsetLookup, [&](Entry *E2) { return E2->Contributions[InfoColumn].Offset <= Offset; }); if (I == OffsetLookup.begin()) return nullptr; --I; const auto *E = *I; const auto &InfoContrib = E->Contributions[InfoColumn]; if ((InfoContrib.Offset + InfoContrib.Length) <= Offset) return nullptr; return E; } const DWARFUnitIndex::Entry *DWARFUnitIndex::getFromHash(uint64_t S) const { uint64_t Mask = Header.NumBuckets - 1; auto H = S & Mask; auto HP = ((S >> 32) & Mask) | 1; while (Rows[H].getSignature() != S && Rows[H].getSignature() != 0) H = (H + HP) & Mask; if (Rows[H].getSignature() != S) return nullptr; return &Rows[H]; } binaryen-version_91/third_party/llvm-project/DWARFVerifier.cpp000066400000000000000000001527301362402614000247010ustar00rootroot00000000000000//===- DWARFVerifier.cpp --------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/DebugInfo/DWARF/DWARFVerifier.h" #include "llvm/ADT/SmallSet.h" #include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/DWARF/DWARFDebugLine.h" #include "llvm/DebugInfo/DWARF/DWARFDie.h" #include "llvm/DebugInfo/DWARF/DWARFExpression.h" #include "llvm/DebugInfo/DWARF/DWARFFormValue.h" #include "llvm/DebugInfo/DWARF/DWARFSection.h" #include "llvm/Support/DJB.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include #include #include using namespace llvm; using namespace dwarf; using namespace object; DWARFVerifier::DieRangeInfo::address_range_iterator DWARFVerifier::DieRangeInfo::insert(const DWARFAddressRange &R) { auto Begin = Ranges.begin(); auto End = Ranges.end(); auto Pos = std::lower_bound(Begin, End, R); if (Pos != End) { if (Pos->intersects(R)) return std::move(Pos); if (Pos != Begin) { auto Iter = Pos - 1; if (Iter->intersects(R)) return std::move(Iter); } } Ranges.insert(Pos, R); return Ranges.end(); } DWARFVerifier::DieRangeInfo::die_range_info_iterator DWARFVerifier::DieRangeInfo::insert(const DieRangeInfo &RI) { auto End = Children.end(); auto Iter = Children.begin(); while (Iter != End) { if (Iter->intersects(RI)) return Iter; ++Iter; } Children.insert(RI); return Children.end(); } bool DWARFVerifier::DieRangeInfo::contains(const DieRangeInfo &RHS) const { auto I1 = Ranges.begin(), E1 = Ranges.end(); auto I2 = RHS.Ranges.begin(), E2 = RHS.Ranges.end(); if (I2 == E2) return true; DWARFAddressRange R = *I2; while (I1 != E1) { bool Covered = I1->LowPC <= R.LowPC; if (R.LowPC == R.HighPC || (Covered && R.HighPC <= I1->HighPC)) { if (++I2 == E2) return true; R = *I2; continue; } if (!Covered) return false; if (R.LowPC < I1->HighPC) R.LowPC = I1->HighPC; ++I1; } return false; } bool DWARFVerifier::DieRangeInfo::intersects(const DieRangeInfo &RHS) const { auto I1 = Ranges.begin(), E1 = Ranges.end(); auto I2 = RHS.Ranges.begin(), E2 = RHS.Ranges.end(); while (I1 != E1 && I2 != E2) { if (I1->intersects(*I2)) return true; if (I1->LowPC < I2->LowPC) ++I1; else ++I2; } return false; } bool DWARFVerifier::verifyUnitHeader(const DWARFDataExtractor DebugInfoData, uint64_t *Offset, unsigned UnitIndex, uint8_t &UnitType, bool &isUnitDWARF64) { uint64_t AbbrOffset, Length; uint8_t AddrSize = 0; uint16_t Version; bool Success = true; bool ValidLength = false; bool ValidVersion = false; bool ValidAddrSize = false; bool ValidType = true; bool ValidAbbrevOffset = true; uint64_t OffsetStart = *Offset; Length = DebugInfoData.getU32(Offset); if (Length == dwarf::DW_LENGTH_DWARF64) { Length = DebugInfoData.getU64(Offset); isUnitDWARF64 = true; } Version = DebugInfoData.getU16(Offset); if (Version >= 5) { UnitType = DebugInfoData.getU8(Offset); AddrSize = DebugInfoData.getU8(Offset); AbbrOffset = isUnitDWARF64 ? DebugInfoData.getU64(Offset) : DebugInfoData.getU32(Offset); ValidType = dwarf::isUnitType(UnitType); } else { UnitType = 0; AbbrOffset = isUnitDWARF64 ? DebugInfoData.getU64(Offset) : DebugInfoData.getU32(Offset); AddrSize = DebugInfoData.getU8(Offset); } if (!DCtx.getDebugAbbrev()->getAbbreviationDeclarationSet(AbbrOffset)) ValidAbbrevOffset = false; ValidLength = DebugInfoData.isValidOffset(OffsetStart + Length + 3); ValidVersion = DWARFContext::isSupportedVersion(Version); ValidAddrSize = AddrSize == 4 || AddrSize == 8; if (!ValidLength || !ValidVersion || !ValidAddrSize || !ValidAbbrevOffset || !ValidType) { Success = false; error() << format("Units[%d] - start offset: 0x%08" PRIx64 " \n", UnitIndex, OffsetStart); if (!ValidLength) note() << "The length for this unit is too " "large for the .debug_info provided.\n"; if (!ValidVersion) note() << "The 16 bit unit header version is not valid.\n"; if (!ValidType) note() << "The unit type encoding is not valid.\n"; if (!ValidAbbrevOffset) note() << "The offset into the .debug_abbrev section is " "not valid.\n"; if (!ValidAddrSize) note() << "The address size is unsupported.\n"; } *Offset = OffsetStart + Length + (isUnitDWARF64 ? 12 : 4); return Success; } unsigned DWARFVerifier::verifyUnitContents(DWARFUnit &Unit) { unsigned NumUnitErrors = 0; unsigned NumDies = Unit.getNumDIEs(); for (unsigned I = 0; I < NumDies; ++I) { auto Die = Unit.getDIEAtIndex(I); if (Die.getTag() == DW_TAG_null) continue; for (auto AttrValue : Die.attributes()) { NumUnitErrors += verifyDebugInfoAttribute(Die, AttrValue); NumUnitErrors += verifyDebugInfoForm(Die, AttrValue); } NumUnitErrors += verifyDebugInfoCallSite(Die); } DWARFDie Die = Unit.getUnitDIE(/* ExtractUnitDIEOnly = */ false); if (!Die) { error() << "Compilation unit without DIE.\n"; NumUnitErrors++; return NumUnitErrors; } if (!dwarf::isUnitType(Die.getTag())) { error() << "Compilation unit root DIE is not a unit DIE: " << dwarf::TagString(Die.getTag()) << ".\n"; NumUnitErrors++; } uint8_t UnitType = Unit.getUnitType(); if (!DWARFUnit::isMatchingUnitTypeAndTag(UnitType, Die.getTag())) { error() << "Compilation unit type (" << dwarf::UnitTypeString(UnitType) << ") and root DIE (" << dwarf::TagString(Die.getTag()) << ") do not match.\n"; NumUnitErrors++; } DieRangeInfo RI; NumUnitErrors += verifyDieRanges(Die, RI); return NumUnitErrors; } unsigned DWARFVerifier::verifyDebugInfoCallSite(const DWARFDie &Die) { if (Die.getTag() != DW_TAG_call_site && Die.getTag() != DW_TAG_GNU_call_site) return 0; DWARFDie Curr = Die.getParent(); for (; Curr.isValid() && !Curr.isSubprogramDIE(); Curr = Die.getParent()) { if (Curr.getTag() == DW_TAG_inlined_subroutine) { error() << "Call site entry nested within inlined subroutine:"; Curr.dump(OS); return 1; } } if (!Curr.isValid()) { error() << "Call site entry not nested within a valid subprogram:"; Die.dump(OS); return 1; } Optional CallAttr = Curr.find({DW_AT_call_all_calls, DW_AT_call_all_source_calls, DW_AT_call_all_tail_calls, DW_AT_GNU_all_call_sites, DW_AT_GNU_all_source_call_sites, DW_AT_GNU_all_tail_call_sites}); if (!CallAttr) { error() << "Subprogram with call site entry has no DW_AT_call attribute:"; Curr.dump(OS); Die.dump(OS, /*indent*/ 1); return 1; } return 0; } unsigned DWARFVerifier::verifyAbbrevSection(const DWARFDebugAbbrev *Abbrev) { unsigned NumErrors = 0; if (Abbrev) { const DWARFAbbreviationDeclarationSet *AbbrDecls = Abbrev->getAbbreviationDeclarationSet(0); for (auto AbbrDecl : *AbbrDecls) { SmallDenseSet AttributeSet; for (auto Attribute : AbbrDecl.attributes()) { auto Result = AttributeSet.insert(Attribute.Attr); if (!Result.second) { error() << "Abbreviation declaration contains multiple " << AttributeString(Attribute.Attr) << " attributes.\n"; AbbrDecl.dump(OS); ++NumErrors; } } } } return NumErrors; } bool DWARFVerifier::handleDebugAbbrev() { OS << "Verifying .debug_abbrev...\n"; const DWARFObject &DObj = DCtx.getDWARFObj(); unsigned NumErrors = 0; if (!DObj.getAbbrevSection().empty()) NumErrors += verifyAbbrevSection(DCtx.getDebugAbbrev()); if (!DObj.getAbbrevDWOSection().empty()) NumErrors += verifyAbbrevSection(DCtx.getDebugAbbrevDWO()); return NumErrors == 0; } unsigned DWARFVerifier::verifyUnitSection(const DWARFSection &S, DWARFSectionKind SectionKind) { const DWARFObject &DObj = DCtx.getDWARFObj(); DWARFDataExtractor DebugInfoData(DObj, S, DCtx.isLittleEndian(), 0); unsigned NumDebugInfoErrors = 0; uint64_t OffsetStart = 0, Offset = 0, UnitIdx = 0; uint8_t UnitType = 0; bool isUnitDWARF64 = false; bool isHeaderChainValid = true; bool hasDIE = DebugInfoData.isValidOffset(Offset); DWARFUnitVector TypeUnitVector; DWARFUnitVector CompileUnitVector; while (hasDIE) { OffsetStart = Offset; if (!verifyUnitHeader(DebugInfoData, &Offset, UnitIdx, UnitType, isUnitDWARF64)) { isHeaderChainValid = false; if (isUnitDWARF64) break; } else { DWARFUnitHeader Header; Header.extract(DCtx, DebugInfoData, &OffsetStart, SectionKind); DWARFUnit *Unit; switch (UnitType) { case dwarf::DW_UT_type: case dwarf::DW_UT_split_type: { Unit = TypeUnitVector.addUnit(std::make_unique( DCtx, S, Header, DCtx.getDebugAbbrev(), &DObj.getRangesSection(), &DObj.getLocSection(), DObj.getStrSection(), DObj.getStrOffsetsSection(), &DObj.getAppleObjCSection(), DObj.getLineSection(), DCtx.isLittleEndian(), false, TypeUnitVector)); break; } case dwarf::DW_UT_skeleton: case dwarf::DW_UT_split_compile: case dwarf::DW_UT_compile: case dwarf::DW_UT_partial: // UnitType = 0 means that we are verifying a compile unit in DWARF v4. case 0: { Unit = CompileUnitVector.addUnit(std::make_unique( DCtx, S, Header, DCtx.getDebugAbbrev(), &DObj.getRangesSection(), &DObj.getLocSection(), DObj.getStrSection(), DObj.getStrOffsetsSection(), &DObj.getAppleObjCSection(), DObj.getLineSection(), DCtx.isLittleEndian(), false, CompileUnitVector)); break; } default: { llvm_unreachable("Invalid UnitType."); } } NumDebugInfoErrors += verifyUnitContents(*Unit); } hasDIE = DebugInfoData.isValidOffset(Offset); ++UnitIdx; } if (UnitIdx == 0 && !hasDIE) { warn() << "Section is empty.\n"; isHeaderChainValid = true; } if (!isHeaderChainValid) ++NumDebugInfoErrors; NumDebugInfoErrors += verifyDebugInfoReferences(); return NumDebugInfoErrors; } bool DWARFVerifier::handleDebugInfo() { const DWARFObject &DObj = DCtx.getDWARFObj(); unsigned NumErrors = 0; OS << "Verifying .debug_info Unit Header Chain...\n"; DObj.forEachInfoSections([&](const DWARFSection &S) { NumErrors += verifyUnitSection(S, DW_SECT_INFO); }); OS << "Verifying .debug_types Unit Header Chain...\n"; DObj.forEachTypesSections([&](const DWARFSection &S) { NumErrors += verifyUnitSection(S, DW_SECT_TYPES); }); return NumErrors == 0; } unsigned DWARFVerifier::verifyDieRanges(const DWARFDie &Die, DieRangeInfo &ParentRI) { unsigned NumErrors = 0; if (!Die.isValid()) return NumErrors; auto RangesOrError = Die.getAddressRanges(); if (!RangesOrError) { // FIXME: Report the error. ++NumErrors; llvm::consumeError(RangesOrError.takeError()); return NumErrors; } DWARFAddressRangesVector Ranges = RangesOrError.get(); // Build RI for this DIE and check that ranges within this DIE do not // overlap. DieRangeInfo RI(Die); // TODO support object files better // // Some object file formats (i.e. non-MachO) support COMDAT. ELF in // particular does so by placing each function into a section. The DWARF data // for the function at that point uses a section relative DW_FORM_addrp for // the DW_AT_low_pc and a DW_FORM_data4 for the offset as the DW_AT_high_pc. // In such a case, when the Die is the CU, the ranges will overlap, and we // will flag valid conflicting ranges as invalid. // // For such targets, we should read the ranges from the CU and partition them // by the section id. The ranges within a particular section should be // disjoint, although the ranges across sections may overlap. We would map // the child die to the entity that it references and the section with which // it is associated. The child would then be checked against the range // information for the associated section. // // For now, simply elide the range verification for the CU DIEs if we are // processing an object file. if (!IsObjectFile || IsMachOObject || Die.getTag() != DW_TAG_compile_unit) { for (auto Range : Ranges) { if (!Range.valid()) { ++NumErrors; error() << "Invalid address range " << Range << "\n"; continue; } // Verify that ranges don't intersect. const auto IntersectingRange = RI.insert(Range); if (IntersectingRange != RI.Ranges.end()) { ++NumErrors; error() << "DIE has overlapping address ranges: " << Range << " and " << *IntersectingRange << "\n"; break; } } } // Verify that children don't intersect. const auto IntersectingChild = ParentRI.insert(RI); if (IntersectingChild != ParentRI.Children.end()) { ++NumErrors; error() << "DIEs have overlapping address ranges:"; dump(Die); dump(IntersectingChild->Die) << '\n'; } // Verify that ranges are contained within their parent. bool ShouldBeContained = !Ranges.empty() && !ParentRI.Ranges.empty() && !(Die.getTag() == DW_TAG_subprogram && ParentRI.Die.getTag() == DW_TAG_subprogram); if (ShouldBeContained && !ParentRI.contains(RI)) { ++NumErrors; error() << "DIE address ranges are not contained in its parent's ranges:"; dump(ParentRI.Die); dump(Die, 2) << '\n'; } // Recursively check children. for (DWARFDie Child : Die) NumErrors += verifyDieRanges(Child, RI); return NumErrors; } unsigned DWARFVerifier::verifyDebugInfoAttribute(const DWARFDie &Die, DWARFAttribute &AttrValue) { unsigned NumErrors = 0; auto ReportError = [&](const Twine &TitleMsg) { ++NumErrors; error() << TitleMsg << '\n'; dump(Die) << '\n'; }; const DWARFObject &DObj = DCtx.getDWARFObj(); const auto Attr = AttrValue.Attr; switch (Attr) { case DW_AT_ranges: // Make sure the offset in the DW_AT_ranges attribute is valid. if (auto SectionOffset = AttrValue.Value.getAsSectionOffset()) { if (*SectionOffset >= DObj.getRangesSection().Data.size()) ReportError("DW_AT_ranges offset is beyond .debug_ranges bounds:"); break; } ReportError("DIE has invalid DW_AT_ranges encoding:"); break; case DW_AT_stmt_list: // Make sure the offset in the DW_AT_stmt_list attribute is valid. if (auto SectionOffset = AttrValue.Value.getAsSectionOffset()) { if (*SectionOffset >= DObj.getLineSection().Data.size()) ReportError("DW_AT_stmt_list offset is beyond .debug_line bounds: " + llvm::formatv("{0:x8}", *SectionOffset)); break; } ReportError("DIE has invalid DW_AT_stmt_list encoding:"); break; case DW_AT_location: { auto VerifyLocationExpr = [&](ArrayRef D) { DWARFUnit *U = Die.getDwarfUnit(); DataExtractor Data(toStringRef(D), DCtx.isLittleEndian(), 0); DWARFExpression Expression(Data, U->getVersion(), U->getAddressByteSize()); bool Error = llvm::any_of(Expression, [](DWARFExpression::Operation &Op) { return Op.isError(); }); if (Error || !Expression.verify(U)) ReportError("DIE contains invalid DWARF expression:"); }; if (Optional> Expr = AttrValue.Value.getAsBlock()) { // Verify inlined location. VerifyLocationExpr(*Expr); } else if (auto LocOffset = AttrValue.Value.getAsSectionOffset()) { // Verify location list. if (auto DebugLoc = DCtx.getDebugLoc()) if (auto LocList = DebugLoc->getLocationListAtOffset(*LocOffset)) for (const auto &Entry : LocList->Entries) VerifyLocationExpr(Entry.Loc); } break; } case DW_AT_specification: case DW_AT_abstract_origin: { if (auto ReferencedDie = Die.getAttributeValueAsReferencedDie(Attr)) { auto DieTag = Die.getTag(); auto RefTag = ReferencedDie.getTag(); if (DieTag == RefTag) break; if (DieTag == DW_TAG_inlined_subroutine && RefTag == DW_TAG_subprogram) break; if (DieTag == DW_TAG_variable && RefTag == DW_TAG_member) break; // This might be reference to a function declaration. if (DieTag == DW_TAG_GNU_call_site && RefTag == DW_TAG_subprogram) break; ReportError("DIE with tag " + TagString(DieTag) + " has " + AttributeString(Attr) + " that points to DIE with " "incompatible tag " + TagString(RefTag)); } break; } case DW_AT_type: { DWARFDie TypeDie = Die.getAttributeValueAsReferencedDie(DW_AT_type); if (TypeDie && !isType(TypeDie.getTag())) { ReportError("DIE has " + AttributeString(Attr) + " with incompatible tag " + TagString(TypeDie.getTag())); } break; } default: break; } return NumErrors; } unsigned DWARFVerifier::verifyDebugInfoForm(const DWARFDie &Die, DWARFAttribute &AttrValue) { const DWARFObject &DObj = DCtx.getDWARFObj(); auto DieCU = Die.getDwarfUnit(); unsigned NumErrors = 0; const auto Form = AttrValue.Value.getForm(); switch (Form) { case DW_FORM_ref1: case DW_FORM_ref2: case DW_FORM_ref4: case DW_FORM_ref8: case DW_FORM_ref_udata: { // Verify all CU relative references are valid CU offsets. Optional RefVal = AttrValue.Value.getAsReference(); assert(RefVal); if (RefVal) { auto CUSize = DieCU->getNextUnitOffset() - DieCU->getOffset(); auto CUOffset = AttrValue.Value.getRawUValue(); if (CUOffset >= CUSize) { ++NumErrors; error() << FormEncodingString(Form) << " CU offset " << format("0x%08" PRIx64, CUOffset) << " is invalid (must be less than CU size of " << format("0x%08" PRIx64, CUSize) << "):\n"; Die.dump(OS, 0, DumpOpts); dump(Die) << '\n'; } else { // Valid reference, but we will verify it points to an actual // DIE later. ReferenceToDIEOffsets[*RefVal].insert(Die.getOffset()); } } break; } case DW_FORM_ref_addr: { // Verify all absolute DIE references have valid offsets in the // .debug_info section. Optional RefVal = AttrValue.Value.getAsReference(); assert(RefVal); if (RefVal) { if (*RefVal >= DieCU->getInfoSection().Data.size()) { ++NumErrors; error() << "DW_FORM_ref_addr offset beyond .debug_info " "bounds:\n"; dump(Die) << '\n'; } else { // Valid reference, but we will verify it points to an actual // DIE later. ReferenceToDIEOffsets[*RefVal].insert(Die.getOffset()); } } break; } case DW_FORM_strp: { auto SecOffset = AttrValue.Value.getAsSectionOffset(); assert(SecOffset); // DW_FORM_strp is a section offset. if (SecOffset && *SecOffset >= DObj.getStrSection().size()) { ++NumErrors; error() << "DW_FORM_strp offset beyond .debug_str bounds:\n"; dump(Die) << '\n'; } break; } case DW_FORM_strx: case DW_FORM_strx1: case DW_FORM_strx2: case DW_FORM_strx3: case DW_FORM_strx4: { auto Index = AttrValue.Value.getRawUValue(); auto DieCU = Die.getDwarfUnit(); // Check that we have a valid DWARF v5 string offsets table. if (!DieCU->getStringOffsetsTableContribution()) { ++NumErrors; error() << FormEncodingString(Form) << " used without a valid string offsets table:\n"; dump(Die) << '\n'; break; } // Check that the index is within the bounds of the section. unsigned ItemSize = DieCU->getDwarfStringOffsetsByteSize(); // Use a 64-bit type to calculate the offset to guard against overflow. uint64_t Offset = (uint64_t)DieCU->getStringOffsetsBase() + Index * ItemSize; if (DObj.getStrOffsetsSection().Data.size() < Offset + ItemSize) { ++NumErrors; error() << FormEncodingString(Form) << " uses index " << format("%" PRIu64, Index) << ", which is too large:\n"; dump(Die) << '\n'; break; } // Check that the string offset is valid. uint64_t StringOffset = *DieCU->getStringOffsetSectionItem(Index); if (StringOffset >= DObj.getStrSection().size()) { ++NumErrors; error() << FormEncodingString(Form) << " uses index " << format("%" PRIu64, Index) << ", but the referenced string" " offset is beyond .debug_str bounds:\n"; dump(Die) << '\n'; } break; } default: break; } return NumErrors; } unsigned DWARFVerifier::verifyDebugInfoReferences() { // Take all references and make sure they point to an actual DIE by // getting the DIE by offset and emitting an error OS << "Verifying .debug_info references...\n"; unsigned NumErrors = 0; for (const std::pair> &Pair : ReferenceToDIEOffsets) { if (DCtx.getDIEForOffset(Pair.first)) continue; ++NumErrors; error() << "invalid DIE reference " << format("0x%08" PRIx64, Pair.first) << ". Offset is in between DIEs:\n"; for (auto Offset : Pair.second) dump(DCtx.getDIEForOffset(Offset)) << '\n'; OS << "\n"; } return NumErrors; } void DWARFVerifier::verifyDebugLineStmtOffsets() { std::map StmtListToDie; for (const auto &CU : DCtx.compile_units()) { auto Die = CU->getUnitDIE(); // Get the attribute value as a section offset. No need to produce an // error here if the encoding isn't correct because we validate this in // the .debug_info verifier. auto StmtSectionOffset = toSectionOffset(Die.find(DW_AT_stmt_list)); if (!StmtSectionOffset) continue; const uint64_t LineTableOffset = *StmtSectionOffset; auto LineTable = DCtx.getLineTableForUnit(CU.get()); if (LineTableOffset < DCtx.getDWARFObj().getLineSection().Data.size()) { if (!LineTable) { ++NumDebugLineErrors; error() << ".debug_line[" << format("0x%08" PRIx64, LineTableOffset) << "] was not able to be parsed for CU:\n"; dump(Die) << '\n'; continue; } } else { // Make sure we don't get a valid line table back if the offset is wrong. assert(LineTable == nullptr); // Skip this line table as it isn't valid. No need to create an error // here because we validate this in the .debug_info verifier. continue; } auto Iter = StmtListToDie.find(LineTableOffset); if (Iter != StmtListToDie.end()) { ++NumDebugLineErrors; error() << "two compile unit DIEs, " << format("0x%08" PRIx64, Iter->second.getOffset()) << " and " << format("0x%08" PRIx64, Die.getOffset()) << ", have the same DW_AT_stmt_list section offset:\n"; dump(Iter->second); dump(Die) << '\n'; // Already verified this line table before, no need to do it again. continue; } StmtListToDie[LineTableOffset] = Die; } } void DWARFVerifier::verifyDebugLineRows() { for (const auto &CU : DCtx.compile_units()) { auto Die = CU->getUnitDIE(); auto LineTable = DCtx.getLineTableForUnit(CU.get()); // If there is no line table we will have created an error in the // .debug_info verifier or in verifyDebugLineStmtOffsets(). if (!LineTable) continue; // Verify prologue. uint32_t MaxDirIndex = LineTable->Prologue.IncludeDirectories.size(); uint32_t FileIndex = 1; StringMap FullPathMap; for (const auto &FileName : LineTable->Prologue.FileNames) { // Verify directory index. if (FileName.DirIdx > MaxDirIndex) { ++NumDebugLineErrors; error() << ".debug_line[" << format("0x%08" PRIx64, *toSectionOffset(Die.find(DW_AT_stmt_list))) << "].prologue.file_names[" << FileIndex << "].dir_idx contains an invalid index: " << FileName.DirIdx << "\n"; } // Check file paths for duplicates. std::string FullPath; const bool HasFullPath = LineTable->getFileNameByIndex( FileIndex, CU->getCompilationDir(), DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, FullPath); assert(HasFullPath && "Invalid index?"); (void)HasFullPath; auto It = FullPathMap.find(FullPath); if (It == FullPathMap.end()) FullPathMap[FullPath] = FileIndex; else if (It->second != FileIndex) { warn() << ".debug_line[" << format("0x%08" PRIx64, *toSectionOffset(Die.find(DW_AT_stmt_list))) << "].prologue.file_names[" << FileIndex << "] is a duplicate of file_names[" << It->second << "]\n"; } FileIndex++; } // Verify rows. uint64_t PrevAddress = 0; uint32_t RowIndex = 0; for (const auto &Row : LineTable->Rows) { // Verify row address. if (Row.Address.Address < PrevAddress) { ++NumDebugLineErrors; error() << ".debug_line[" << format("0x%08" PRIx64, *toSectionOffset(Die.find(DW_AT_stmt_list))) << "] row[" << RowIndex << "] decreases in address from previous row:\n"; DWARFDebugLine::Row::dumpTableHeader(OS); if (RowIndex > 0) LineTable->Rows[RowIndex - 1].dump(OS); Row.dump(OS); OS << '\n'; } // Verify file index. if (!LineTable->hasFileAtIndex(Row.File)) { ++NumDebugLineErrors; bool isDWARF5 = LineTable->Prologue.getVersion() >= 5; error() << ".debug_line[" << format("0x%08" PRIx64, *toSectionOffset(Die.find(DW_AT_stmt_list))) << "][" << RowIndex << "] has invalid file index " << Row.File << " (valid values are [" << (isDWARF5 ? "0," : "1,") << LineTable->Prologue.FileNames.size() << (isDWARF5 ? ")" : "]") << "):\n"; DWARFDebugLine::Row::dumpTableHeader(OS); Row.dump(OS); OS << '\n'; } if (Row.EndSequence) PrevAddress = 0; else PrevAddress = Row.Address.Address; ++RowIndex; } } } DWARFVerifier::DWARFVerifier(raw_ostream &S, DWARFContext &D, DIDumpOptions DumpOpts) : OS(S), DCtx(D), DumpOpts(std::move(DumpOpts)), IsObjectFile(false), IsMachOObject(false) { if (const auto *F = DCtx.getDWARFObj().getFile()) { IsObjectFile = F->isRelocatableObject(); IsMachOObject = F->isMachO(); } } bool DWARFVerifier::handleDebugLine() { NumDebugLineErrors = 0; OS << "Verifying .debug_line...\n"; verifyDebugLineStmtOffsets(); verifyDebugLineRows(); return NumDebugLineErrors == 0; } unsigned DWARFVerifier::verifyAppleAccelTable(const DWARFSection *AccelSection, DataExtractor *StrData, const char *SectionName) { unsigned NumErrors = 0; DWARFDataExtractor AccelSectionData(DCtx.getDWARFObj(), *AccelSection, DCtx.isLittleEndian(), 0); AppleAcceleratorTable AccelTable(AccelSectionData, *StrData); OS << "Verifying " << SectionName << "...\n"; // Verify that the fixed part of the header is not too short. if (!AccelSectionData.isValidOffset(AccelTable.getSizeHdr())) { error() << "Section is too small to fit a section header.\n"; return 1; } // Verify that the section is not too short. if (Error E = AccelTable.extract()) { error() << toString(std::move(E)) << '\n'; return 1; } // Verify that all buckets have a valid hash index or are empty. uint32_t NumBuckets = AccelTable.getNumBuckets(); uint32_t NumHashes = AccelTable.getNumHashes(); uint64_t BucketsOffset = AccelTable.getSizeHdr() + AccelTable.getHeaderDataLength(); uint64_t HashesBase = BucketsOffset + NumBuckets * 4; uint64_t OffsetsBase = HashesBase + NumHashes * 4; for (uint32_t BucketIdx = 0; BucketIdx < NumBuckets; ++BucketIdx) { uint32_t HashIdx = AccelSectionData.getU32(&BucketsOffset); if (HashIdx >= NumHashes && HashIdx != UINT32_MAX) { error() << format("Bucket[%d] has invalid hash index: %u.\n", BucketIdx, HashIdx); ++NumErrors; } } uint32_t NumAtoms = AccelTable.getAtomsDesc().size(); if (NumAtoms == 0) { error() << "No atoms: failed to read HashData.\n"; return 1; } if (!AccelTable.validateForms()) { error() << "Unsupported form: failed to read HashData.\n"; return 1; } for (uint32_t HashIdx = 0; HashIdx < NumHashes; ++HashIdx) { uint64_t HashOffset = HashesBase + 4 * HashIdx; uint64_t DataOffset = OffsetsBase + 4 * HashIdx; uint32_t Hash = AccelSectionData.getU32(&HashOffset); uint64_t HashDataOffset = AccelSectionData.getU32(&DataOffset); if (!AccelSectionData.isValidOffsetForDataOfSize(HashDataOffset, sizeof(uint64_t))) { error() << format("Hash[%d] has invalid HashData offset: " "0x%08" PRIx64 ".\n", HashIdx, HashDataOffset); ++NumErrors; } uint64_t StrpOffset; uint64_t StringOffset; uint32_t StringCount = 0; uint64_t Offset; unsigned Tag; while ((StrpOffset = AccelSectionData.getU32(&HashDataOffset)) != 0) { const uint32_t NumHashDataObjects = AccelSectionData.getU32(&HashDataOffset); for (uint32_t HashDataIdx = 0; HashDataIdx < NumHashDataObjects; ++HashDataIdx) { std::tie(Offset, Tag) = AccelTable.readAtoms(&HashDataOffset); auto Die = DCtx.getDIEForOffset(Offset); if (!Die) { const uint32_t BucketIdx = NumBuckets ? (Hash % NumBuckets) : UINT32_MAX; StringOffset = StrpOffset; const char *Name = StrData->getCStr(&StringOffset); if (!Name) Name = ""; error() << format( "%s Bucket[%d] Hash[%d] = 0x%08x " "Str[%u] = 0x%08" PRIx64 " DIE[%d] = 0x%08" PRIx64 " " "is not a valid DIE offset for \"%s\".\n", SectionName, BucketIdx, HashIdx, Hash, StringCount, StrpOffset, HashDataIdx, Offset, Name); ++NumErrors; continue; } if ((Tag != dwarf::DW_TAG_null) && (Die.getTag() != Tag)) { error() << "Tag " << dwarf::TagString(Tag) << " in accelerator table does not match Tag " << dwarf::TagString(Die.getTag()) << " of DIE[" << HashDataIdx << "].\n"; ++NumErrors; } } ++StringCount; } } return NumErrors; } unsigned DWARFVerifier::verifyDebugNamesCULists(const DWARFDebugNames &AccelTable) { // A map from CU offset to the (first) Name Index offset which claims to index // this CU. DenseMap CUMap; const uint64_t NotIndexed = std::numeric_limits::max(); CUMap.reserve(DCtx.getNumCompileUnits()); for (const auto &CU : DCtx.compile_units()) CUMap[CU->getOffset()] = NotIndexed; unsigned NumErrors = 0; for (const DWARFDebugNames::NameIndex &NI : AccelTable) { if (NI.getCUCount() == 0) { error() << formatv("Name Index @ {0:x} does not index any CU\n", NI.getUnitOffset()); ++NumErrors; continue; } for (uint32_t CU = 0, End = NI.getCUCount(); CU < End; ++CU) { uint64_t Offset = NI.getCUOffset(CU); auto Iter = CUMap.find(Offset); if (Iter == CUMap.end()) { error() << formatv( "Name Index @ {0:x} references a non-existing CU @ {1:x}\n", NI.getUnitOffset(), Offset); ++NumErrors; continue; } if (Iter->second != NotIndexed) { error() << formatv("Name Index @ {0:x} references a CU @ {1:x}, but " "this CU is already indexed by Name Index @ {2:x}\n", NI.getUnitOffset(), Offset, Iter->second); continue; } Iter->second = NI.getUnitOffset(); } } for (const auto &KV : CUMap) { if (KV.second == NotIndexed) warn() << formatv("CU @ {0:x} not covered by any Name Index\n", KV.first); } return NumErrors; } unsigned DWARFVerifier::verifyNameIndexBuckets(const DWARFDebugNames::NameIndex &NI, const DataExtractor &StrData) { struct BucketInfo { uint32_t Bucket; uint32_t Index; constexpr BucketInfo(uint32_t Bucket, uint32_t Index) : Bucket(Bucket), Index(Index) {} bool operator<(const BucketInfo &RHS) const { return Index < RHS.Index; }; }; uint32_t NumErrors = 0; if (NI.getBucketCount() == 0) { warn() << formatv("Name Index @ {0:x} does not contain a hash table.\n", NI.getUnitOffset()); return NumErrors; } // Build up a list of (Bucket, Index) pairs. We use this later to verify that // each Name is reachable from the appropriate bucket. std::vector BucketStarts; BucketStarts.reserve(NI.getBucketCount() + 1); for (uint32_t Bucket = 0, End = NI.getBucketCount(); Bucket < End; ++Bucket) { uint32_t Index = NI.getBucketArrayEntry(Bucket); if (Index > NI.getNameCount()) { error() << formatv("Bucket {0} of Name Index @ {1:x} contains invalid " "value {2}. Valid range is [0, {3}].\n", Bucket, NI.getUnitOffset(), Index, NI.getNameCount()); ++NumErrors; continue; } if (Index > 0) BucketStarts.emplace_back(Bucket, Index); } // If there were any buckets with invalid values, skip further checks as they // will likely produce many errors which will only confuse the actual root // problem. if (NumErrors > 0) return NumErrors; // Sort the list in the order of increasing "Index" entries. array_pod_sort(BucketStarts.begin(), BucketStarts.end()); // Insert a sentinel entry at the end, so we can check that the end of the // table is covered in the loop below. BucketStarts.emplace_back(NI.getBucketCount(), NI.getNameCount() + 1); // Loop invariant: NextUncovered is the (1-based) index of the first Name // which is not reachable by any of the buckets we processed so far (and // hasn't been reported as uncovered). uint32_t NextUncovered = 1; for (const BucketInfo &B : BucketStarts) { // Under normal circumstances B.Index be equal to NextUncovered, but it can // be less if a bucket points to names which are already known to be in some // bucket we processed earlier. In that case, we won't trigger this error, // but report the mismatched hash value error instead. (We know the hash // will not match because we have already verified that the name's hash // puts it into the previous bucket.) if (B.Index > NextUncovered) { error() << formatv("Name Index @ {0:x}: Name table entries [{1}, {2}] " "are not covered by the hash table.\n", NI.getUnitOffset(), NextUncovered, B.Index - 1); ++NumErrors; } uint32_t Idx = B.Index; // The rest of the checks apply only to non-sentinel entries. if (B.Bucket == NI.getBucketCount()) break; // This triggers if a non-empty bucket points to a name with a mismatched // hash. Clients are likely to interpret this as an empty bucket, because a // mismatched hash signals the end of a bucket, but if this is indeed an // empty bucket, the producer should have signalled this by marking the // bucket as empty. uint32_t FirstHash = NI.getHashArrayEntry(Idx); if (FirstHash % NI.getBucketCount() != B.Bucket) { error() << formatv( "Name Index @ {0:x}: Bucket {1} is not empty but points to a " "mismatched hash value {2:x} (belonging to bucket {3}).\n", NI.getUnitOffset(), B.Bucket, FirstHash, FirstHash % NI.getBucketCount()); ++NumErrors; } // This find the end of this bucket and also verifies that all the hashes in // this bucket are correct by comparing the stored hashes to the ones we // compute ourselves. while (Idx <= NI.getNameCount()) { uint32_t Hash = NI.getHashArrayEntry(Idx); if (Hash % NI.getBucketCount() != B.Bucket) break; const char *Str = NI.getNameTableEntry(Idx).getString(); if (caseFoldingDjbHash(Str) != Hash) { error() << formatv("Name Index @ {0:x}: String ({1}) at index {2} " "hashes to {3:x}, but " "the Name Index hash is {4:x}\n", NI.getUnitOffset(), Str, Idx, caseFoldingDjbHash(Str), Hash); ++NumErrors; } ++Idx; } NextUncovered = std::max(NextUncovered, Idx); } return NumErrors; } unsigned DWARFVerifier::verifyNameIndexAttribute( const DWARFDebugNames::NameIndex &NI, const DWARFDebugNames::Abbrev &Abbr, DWARFDebugNames::AttributeEncoding AttrEnc) { StringRef FormName = dwarf::FormEncodingString(AttrEnc.Form); if (FormName.empty()) { error() << formatv("NameIndex @ {0:x}: Abbreviation {1:x}: {2} uses an " "unknown form: {3}.\n", NI.getUnitOffset(), Abbr.Code, AttrEnc.Index, AttrEnc.Form); return 1; } if (AttrEnc.Index == DW_IDX_type_hash) { if (AttrEnc.Form != dwarf::DW_FORM_data8) { error() << formatv( "NameIndex @ {0:x}: Abbreviation {1:x}: DW_IDX_type_hash " "uses an unexpected form {2} (should be {3}).\n", NI.getUnitOffset(), Abbr.Code, AttrEnc.Form, dwarf::DW_FORM_data8); return 1; } } // A list of known index attributes and their expected form classes. // DW_IDX_type_hash is handled specially in the check above, as it has a // specific form (not just a form class) we should expect. struct FormClassTable { dwarf::Index Index; DWARFFormValue::FormClass Class; StringLiteral ClassName; }; static constexpr FormClassTable Table[] = { {dwarf::DW_IDX_compile_unit, DWARFFormValue::FC_Constant, {"constant"}}, {dwarf::DW_IDX_type_unit, DWARFFormValue::FC_Constant, {"constant"}}, {dwarf::DW_IDX_die_offset, DWARFFormValue::FC_Reference, {"reference"}}, {dwarf::DW_IDX_parent, DWARFFormValue::FC_Constant, {"constant"}}, }; ArrayRef TableRef(Table); auto Iter = find_if(TableRef, [AttrEnc](const FormClassTable &T) { return T.Index == AttrEnc.Index; }); if (Iter == TableRef.end()) { warn() << formatv("NameIndex @ {0:x}: Abbreviation {1:x} contains an " "unknown index attribute: {2}.\n", NI.getUnitOffset(), Abbr.Code, AttrEnc.Index); return 0; } if (!DWARFFormValue(AttrEnc.Form).isFormClass(Iter->Class)) { error() << formatv("NameIndex @ {0:x}: Abbreviation {1:x}: {2} uses an " "unexpected form {3} (expected form class {4}).\n", NI.getUnitOffset(), Abbr.Code, AttrEnc.Index, AttrEnc.Form, Iter->ClassName); return 1; } return 0; } unsigned DWARFVerifier::verifyNameIndexAbbrevs(const DWARFDebugNames::NameIndex &NI) { if (NI.getLocalTUCount() + NI.getForeignTUCount() > 0) { warn() << formatv("Name Index @ {0:x}: Verifying indexes of type units is " "not currently supported.\n", NI.getUnitOffset()); return 0; } unsigned NumErrors = 0; for (const auto &Abbrev : NI.getAbbrevs()) { StringRef TagName = dwarf::TagString(Abbrev.Tag); if (TagName.empty()) { warn() << formatv("NameIndex @ {0:x}: Abbreviation {1:x} references an " "unknown tag: {2}.\n", NI.getUnitOffset(), Abbrev.Code, Abbrev.Tag); } SmallSet Attributes; for (const auto &AttrEnc : Abbrev.Attributes) { if (!Attributes.insert(AttrEnc.Index).second) { error() << formatv("NameIndex @ {0:x}: Abbreviation {1:x} contains " "multiple {2} attributes.\n", NI.getUnitOffset(), Abbrev.Code, AttrEnc.Index); ++NumErrors; continue; } NumErrors += verifyNameIndexAttribute(NI, Abbrev, AttrEnc); } if (NI.getCUCount() > 1 && !Attributes.count(dwarf::DW_IDX_compile_unit)) { error() << formatv("NameIndex @ {0:x}: Indexing multiple compile units " "and abbreviation {1:x} has no {2} attribute.\n", NI.getUnitOffset(), Abbrev.Code, dwarf::DW_IDX_compile_unit); ++NumErrors; } if (!Attributes.count(dwarf::DW_IDX_die_offset)) { error() << formatv( "NameIndex @ {0:x}: Abbreviation {1:x} has no {2} attribute.\n", NI.getUnitOffset(), Abbrev.Code, dwarf::DW_IDX_die_offset); ++NumErrors; } } return NumErrors; } static SmallVector getNames(const DWARFDie &DIE, bool IncludeLinkageName = true) { SmallVector Result; if (const char *Str = DIE.getName(DINameKind::ShortName)) Result.emplace_back(Str); else if (DIE.getTag() == dwarf::DW_TAG_namespace) Result.emplace_back("(anonymous namespace)"); if (IncludeLinkageName) { if (const char *Str = DIE.getName(DINameKind::LinkageName)) { if (Result.empty() || Result[0] != Str) Result.emplace_back(Str); } } return Result; } unsigned DWARFVerifier::verifyNameIndexEntries( const DWARFDebugNames::NameIndex &NI, const DWARFDebugNames::NameTableEntry &NTE) { // Verifying type unit indexes not supported. if (NI.getLocalTUCount() + NI.getForeignTUCount() > 0) return 0; const char *CStr = NTE.getString(); if (!CStr) { error() << formatv( "Name Index @ {0:x}: Unable to get string associated with name {1}.\n", NI.getUnitOffset(), NTE.getIndex()); return 1; } StringRef Str(CStr); unsigned NumErrors = 0; unsigned NumEntries = 0; uint64_t EntryID = NTE.getEntryOffset(); uint64_t NextEntryID = EntryID; Expected EntryOr = NI.getEntry(&NextEntryID); for (; EntryOr; ++NumEntries, EntryID = NextEntryID, EntryOr = NI.getEntry(&NextEntryID)) { uint32_t CUIndex = *EntryOr->getCUIndex(); if (CUIndex > NI.getCUCount()) { error() << formatv("Name Index @ {0:x}: Entry @ {1:x} contains an " "invalid CU index ({2}).\n", NI.getUnitOffset(), EntryID, CUIndex); ++NumErrors; continue; } uint64_t CUOffset = NI.getCUOffset(CUIndex); uint64_t DIEOffset = CUOffset + *EntryOr->getDIEUnitOffset(); DWARFDie DIE = DCtx.getDIEForOffset(DIEOffset); if (!DIE) { error() << formatv("Name Index @ {0:x}: Entry @ {1:x} references a " "non-existing DIE @ {2:x}.\n", NI.getUnitOffset(), EntryID, DIEOffset); ++NumErrors; continue; } if (DIE.getDwarfUnit()->getOffset() != CUOffset) { error() << formatv("Name Index @ {0:x}: Entry @ {1:x}: mismatched CU of " "DIE @ {2:x}: index - {3:x}; debug_info - {4:x}.\n", NI.getUnitOffset(), EntryID, DIEOffset, CUOffset, DIE.getDwarfUnit()->getOffset()); ++NumErrors; } if (DIE.getTag() != EntryOr->tag()) { error() << formatv("Name Index @ {0:x}: Entry @ {1:x}: mismatched Tag of " "DIE @ {2:x}: index - {3}; debug_info - {4}.\n", NI.getUnitOffset(), EntryID, DIEOffset, EntryOr->tag(), DIE.getTag()); ++NumErrors; } auto EntryNames = getNames(DIE); if (!is_contained(EntryNames, Str)) { error() << formatv("Name Index @ {0:x}: Entry @ {1:x}: mismatched Name " "of DIE @ {2:x}: index - {3}; debug_info - {4}.\n", NI.getUnitOffset(), EntryID, DIEOffset, Str, make_range(EntryNames.begin(), EntryNames.end())); ++NumErrors; } } handleAllErrors(EntryOr.takeError(), [&](const DWARFDebugNames::SentinelError &) { if (NumEntries > 0) return; error() << formatv("Name Index @ {0:x}: Name {1} ({2}) is " "not associated with any entries.\n", NI.getUnitOffset(), NTE.getIndex(), Str); ++NumErrors; }, [&](const ErrorInfoBase &Info) { error() << formatv("Name Index @ {0:x}: Name {1} ({2}): {3}\n", NI.getUnitOffset(), NTE.getIndex(), Str, Info.message()); ++NumErrors; }); return NumErrors; } static bool isVariableIndexable(const DWARFDie &Die, DWARFContext &DCtx) { Optional Location = Die.findRecursively(DW_AT_location); if (!Location) return false; auto ContainsInterestingOperators = [&](ArrayRef D) { DWARFUnit *U = Die.getDwarfUnit(); DataExtractor Data(toStringRef(D), DCtx.isLittleEndian(), U->getAddressByteSize()); DWARFExpression Expression(Data, U->getVersion(), U->getAddressByteSize()); return any_of(Expression, [](DWARFExpression::Operation &Op) { return !Op.isError() && (Op.getCode() == DW_OP_addr || Op.getCode() == DW_OP_form_tls_address || Op.getCode() == DW_OP_GNU_push_tls_address); }); }; if (Optional> Expr = Location->getAsBlock()) { // Inlined location. if (ContainsInterestingOperators(*Expr)) return true; } else if (Optional Offset = Location->getAsSectionOffset()) { // Location list. if (const DWARFDebugLoc *DebugLoc = DCtx.getDebugLoc()) { if (const DWARFDebugLoc::LocationList *LocList = DebugLoc->getLocationListAtOffset(*Offset)) { if (any_of(LocList->Entries, [&](const DWARFDebugLoc::Entry &E) { return ContainsInterestingOperators(E.Loc); })) return true; } } } return false; } unsigned DWARFVerifier::verifyNameIndexCompleteness( const DWARFDie &Die, const DWARFDebugNames::NameIndex &NI) { // First check, if the Die should be indexed. The code follows the DWARF v5 // wording as closely as possible. // "All non-defining declarations (that is, debugging information entries // with a DW_AT_declaration attribute) are excluded." if (Die.find(DW_AT_declaration)) return 0; // "DW_TAG_namespace debugging information entries without a DW_AT_name // attribute are included with the name “(anonymous namespace)â€. // All other debugging information entries without a DW_AT_name attribute // are excluded." // "If a subprogram or inlined subroutine is included, and has a // DW_AT_linkage_name attribute, there will be an additional index entry for // the linkage name." auto IncludeLinkageName = Die.getTag() == DW_TAG_subprogram || Die.getTag() == DW_TAG_inlined_subroutine; auto EntryNames = getNames(Die, IncludeLinkageName); if (EntryNames.empty()) return 0; // We deviate from the specification here, which says: // "The name index must contain an entry for each debugging information entry // that defines a named subprogram, label, variable, type, or namespace, // subject to ..." // Instead whitelisting all TAGs representing a "type" or a "subprogram", to // make sure we catch any missing items, we instead blacklist all TAGs that we // know shouldn't be indexed. switch (Die.getTag()) { // Compile units and modules have names but shouldn't be indexed. case DW_TAG_compile_unit: case DW_TAG_module: return 0; // Function and template parameters are not globally visible, so we shouldn't // index them. case DW_TAG_formal_parameter: case DW_TAG_template_value_parameter: case DW_TAG_template_type_parameter: case DW_TAG_GNU_template_parameter_pack: case DW_TAG_GNU_template_template_param: return 0; // Object members aren't globally visible. case DW_TAG_member: return 0; // According to a strict reading of the specification, enumerators should not // be indexed (and LLVM currently does not do that). However, this causes // problems for the debuggers, so we may need to reconsider this. case DW_TAG_enumerator: return 0; // Imported declarations should not be indexed according to the specification // and LLVM currently does not do that. case DW_TAG_imported_declaration: return 0; // "DW_TAG_subprogram, DW_TAG_inlined_subroutine, and DW_TAG_label debugging // information entries without an address attribute (DW_AT_low_pc, // DW_AT_high_pc, DW_AT_ranges, or DW_AT_entry_pc) are excluded." case DW_TAG_subprogram: case DW_TAG_inlined_subroutine: case DW_TAG_label: if (Die.findRecursively( {DW_AT_low_pc, DW_AT_high_pc, DW_AT_ranges, DW_AT_entry_pc})) break; return 0; // "DW_TAG_variable debugging information entries with a DW_AT_location // attribute that includes a DW_OP_addr or DW_OP_form_tls_address operator are // included; otherwise, they are excluded." // // LLVM extension: We also add DW_OP_GNU_push_tls_address to this list. case DW_TAG_variable: if (isVariableIndexable(Die, DCtx)) break; return 0; default: break; } // Now we know that our Die should be present in the Index. Let's check if // that's the case. unsigned NumErrors = 0; uint64_t DieUnitOffset = Die.getOffset() - Die.getDwarfUnit()->getOffset(); for (StringRef Name : EntryNames) { if (none_of(NI.equal_range(Name), [&](const DWARFDebugNames::Entry &E) { return E.getDIEUnitOffset() == DieUnitOffset; })) { error() << formatv("Name Index @ {0:x}: Entry for DIE @ {1:x} ({2}) with " "name {3} missing.\n", NI.getUnitOffset(), Die.getOffset(), Die.getTag(), Name); ++NumErrors; } } return NumErrors; } unsigned DWARFVerifier::verifyDebugNames(const DWARFSection &AccelSection, const DataExtractor &StrData) { unsigned NumErrors = 0; DWARFDataExtractor AccelSectionData(DCtx.getDWARFObj(), AccelSection, DCtx.isLittleEndian(), 0); DWARFDebugNames AccelTable(AccelSectionData, StrData); OS << "Verifying .debug_names...\n"; // This verifies that we can read individual name indices and their // abbreviation tables. if (Error E = AccelTable.extract()) { error() << toString(std::move(E)) << '\n'; return 1; } NumErrors += verifyDebugNamesCULists(AccelTable); for (const auto &NI : AccelTable) NumErrors += verifyNameIndexBuckets(NI, StrData); for (const auto &NI : AccelTable) NumErrors += verifyNameIndexAbbrevs(NI); // Don't attempt Entry validation if any of the previous checks found errors if (NumErrors > 0) return NumErrors; for (const auto &NI : AccelTable) for (DWARFDebugNames::NameTableEntry NTE : NI) NumErrors += verifyNameIndexEntries(NI, NTE); if (NumErrors > 0) return NumErrors; for (const std::unique_ptr &U : DCtx.compile_units()) { if (const DWARFDebugNames::NameIndex *NI = AccelTable.getCUNameIndex(U->getOffset())) { auto *CU = cast(U.get()); for (const DWARFDebugInfoEntry &Die : CU->dies()) NumErrors += verifyNameIndexCompleteness(DWARFDie(CU, &Die), *NI); } } return NumErrors; } bool DWARFVerifier::handleAccelTables() { const DWARFObject &D = DCtx.getDWARFObj(); DataExtractor StrData(D.getStrSection(), DCtx.isLittleEndian(), 0); unsigned NumErrors = 0; if (!D.getAppleNamesSection().Data.empty()) NumErrors += verifyAppleAccelTable(&D.getAppleNamesSection(), &StrData, ".apple_names"); if (!D.getAppleTypesSection().Data.empty()) NumErrors += verifyAppleAccelTable(&D.getAppleTypesSection(), &StrData, ".apple_types"); if (!D.getAppleNamespacesSection().Data.empty()) NumErrors += verifyAppleAccelTable(&D.getAppleNamespacesSection(), &StrData, ".apple_namespaces"); if (!D.getAppleObjCSection().Data.empty()) NumErrors += verifyAppleAccelTable(&D.getAppleObjCSection(), &StrData, ".apple_objc"); if (!D.getNamesSection().Data.empty()) NumErrors += verifyDebugNames(D.getNamesSection(), StrData); return NumErrors == 0; } raw_ostream &DWARFVerifier::error() const { return WithColor::error(OS); } raw_ostream &DWARFVerifier::warn() const { return WithColor::warning(OS); } raw_ostream &DWARFVerifier::note() const { return WithColor::note(OS); } raw_ostream &DWARFVerifier::dump(const DWARFDie &Die, unsigned indent) const { Die.dump(OS, indent, DumpOpts); return OS; } binaryen-version_91/third_party/llvm-project/DWARFVisitor.cpp000066400000000000000000000160671362402614000245670ustar00rootroot00000000000000//===--- DWARFVisitor.cpp ---------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // //===----------------------------------------------------------------------===// #include "DWARFVisitor.h" #include "llvm/ObjectYAML/DWARFYAML.h" using namespace llvm; template void DWARFYAML::VisitorImpl::onVariableSizeValue(uint64_t U, unsigned Size) { switch (Size) { case 8: onValue((uint64_t)U); break; case 4: onValue((uint32_t)U); break; case 2: onValue((uint16_t)U); break; case 1: onValue((uint8_t)U); break; default: llvm_unreachable("Invalid integer write size."); } } static unsigned getOffsetSize(const DWARFYAML::Unit &Unit) { return Unit.Length.isDWARF64() ? 8 : 4; } static unsigned getRefSize(const DWARFYAML::Unit &Unit) { if (Unit.Version == 2) return Unit.AddrSize; return getOffsetSize(Unit); } template void DWARFYAML::VisitorImpl::traverseDebugInfo() { // XXX BINARYEN: Handle multiple linked compile units. Each one has its own // range of values, terminated by a zero. AbbrevStart refers to the start // index for the current unit, and AbbrevEnd to one past the last one // (which is the index of the 0 terminator). // TODO: This code appears to assume that abbreviation codes increment by 1 // so that lookups are linear. In LLVM output that is true, but it might not // be in general. size_t AbbrevStart = 0, AbbrevEnd = -1; for (auto &Unit : DebugInfo.CompileUnits) { // Skip the 0 terminator. AbbrevEnd = AbbrevStart = AbbrevEnd + 1; while (AbbrevEnd < DebugInfo.AbbrevDecls.size() && DebugInfo.AbbrevDecls[AbbrevEnd].Code) { AbbrevEnd++; } onStartCompileUnit(Unit); if (Unit.Entries.empty()) { // XXX BINARYEN continue; } auto FirstAbbrevCode = Unit.Entries[0].AbbrCode; for (auto &Entry : Unit.Entries) { onStartDIE(Unit, Entry); if (Entry.AbbrCode == 0u) continue; // XXX BINARYEN if (Entry.AbbrCode - FirstAbbrevCode + AbbrevStart >= AbbrevEnd) { errs() << "warning: invalid abbreviation code " << Entry.AbbrCode << " (range: " << FirstAbbrevCode << " : " << AbbrevStart << ".." << AbbrevEnd << ")\n"; continue; } auto &Abbrev = DebugInfo.AbbrevDecls[Entry.AbbrCode - FirstAbbrevCode + AbbrevStart]; auto FormVal = Entry.Values.begin(); auto AbbrForm = Abbrev.Attributes.begin(); for (; FormVal != Entry.Values.end() && AbbrForm != Abbrev.Attributes.end(); ++FormVal, ++AbbrForm) { onForm(*AbbrForm, *FormVal); dwarf::Form Form = AbbrForm->Form; bool Indirect; do { Indirect = false; switch (Form) { case dwarf::DW_FORM_addr: onVariableSizeValue(FormVal->Value, Unit.AddrSize); break; case dwarf::DW_FORM_ref_addr: onVariableSizeValue(FormVal->Value, getRefSize(Unit)); break; case dwarf::DW_FORM_exprloc: case dwarf::DW_FORM_block: onValue((uint64_t)FormVal->BlockData.size(), true); onValue( MemoryBufferRef(StringRef((const char *)&FormVal->BlockData[0], FormVal->BlockData.size()), "")); break; case dwarf::DW_FORM_block1: { auto writeSize = FormVal->BlockData.size(); onValue((uint8_t)writeSize); onValue( MemoryBufferRef(StringRef((const char *)&FormVal->BlockData[0], FormVal->BlockData.size()), "")); break; } case dwarf::DW_FORM_block2: { auto writeSize = FormVal->BlockData.size(); onValue((uint16_t)writeSize); onValue( MemoryBufferRef(StringRef((const char *)&FormVal->BlockData[0], FormVal->BlockData.size()), "")); break; } case dwarf::DW_FORM_block4: { auto writeSize = FormVal->BlockData.size(); onValue((uint32_t)writeSize); onValue( MemoryBufferRef(StringRef((const char *)&FormVal->BlockData[0], FormVal->BlockData.size()), "")); break; } case dwarf::DW_FORM_data1: case dwarf::DW_FORM_ref1: case dwarf::DW_FORM_flag: case dwarf::DW_FORM_strx1: case dwarf::DW_FORM_addrx1: onValue((uint8_t)FormVal->Value); break; case dwarf::DW_FORM_data2: case dwarf::DW_FORM_ref2: case dwarf::DW_FORM_strx2: case dwarf::DW_FORM_addrx2: onValue((uint16_t)FormVal->Value); break; case dwarf::DW_FORM_data4: case dwarf::DW_FORM_ref4: case dwarf::DW_FORM_ref_sup4: case dwarf::DW_FORM_strx4: case dwarf::DW_FORM_addrx4: onValue((uint32_t)FormVal->Value); break; case dwarf::DW_FORM_data8: case dwarf::DW_FORM_ref8: case dwarf::DW_FORM_ref_sup8: onValue((uint64_t)FormVal->Value); break; case dwarf::DW_FORM_sdata: onValue((int64_t)FormVal->Value, true); break; case dwarf::DW_FORM_udata: case dwarf::DW_FORM_ref_udata: onValue((uint64_t)FormVal->Value, true); break; case dwarf::DW_FORM_string: onValue(FormVal->CStr); break; case dwarf::DW_FORM_indirect: onValue((uint64_t)FormVal->Value, true); Indirect = true; Form = static_cast((uint64_t)FormVal->Value); ++FormVal; break; case dwarf::DW_FORM_strp: case dwarf::DW_FORM_sec_offset: case dwarf::DW_FORM_GNU_ref_alt: case dwarf::DW_FORM_GNU_strp_alt: case dwarf::DW_FORM_line_strp: case dwarf::DW_FORM_strp_sup: onVariableSizeValue(FormVal->Value, getOffsetSize(Unit)); break; case dwarf::DW_FORM_ref_sig8: onValue((uint64_t)FormVal->Value); break; case dwarf::DW_FORM_GNU_addr_index: case dwarf::DW_FORM_GNU_str_index: onValue((uint64_t)FormVal->Value, true); break; default: break; } } while (Indirect); } onEndDIE(Unit, Entry); } onEndCompileUnit(Unit); } } // Explicitly instantiate the two template expansions. template class DWARFYAML::VisitorImpl; template class DWARFYAML::VisitorImpl; binaryen-version_91/third_party/llvm-project/DWARFVisitor.h000066400000000000000000000054521362402614000242300ustar00rootroot00000000000000//===--- DWARFVisitor.h -----------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // //===----------------------------------------------------------------------===// #ifndef LLVM_OBJECTYAML_DWARFVISITOR_H #define LLVM_OBJECTYAML_DWARFVISITOR_H #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/Support/MemoryBuffer.h" namespace llvm { namespace DWARFYAML { struct Data; struct Unit; struct Entry; struct FormValue; struct AttributeAbbrev; /// A class to visits DWARFYAML Compile Units and DIEs in preorder. /// /// Extensions of this class can either maintain const or non-const references /// to the DWARFYAML::Data object. template class VisitorImpl { protected: T &DebugInfo; /// Visitor Functions /// @{ virtual void onStartCompileUnit(Unit &CU) {} virtual void onEndCompileUnit(Unit &CU) {} virtual void onStartDIE(Unit &CU, Entry &DIE) {} virtual void onEndDIE(Unit &CU, Entry &DIE) {} virtual void onForm(AttributeAbbrev &AttAbbrev, FormValue &Value) {} /// @} /// Const Visitor Functions /// @{ virtual void onStartCompileUnit(const Unit &CU) {} virtual void onEndCompileUnit(const Unit &CU) {} virtual void onStartDIE(const Unit &CU, const Entry &DIE) {} virtual void onEndDIE(const Unit &CU, const Entry &DIE) {} virtual void onForm(const AttributeAbbrev &AttAbbrev, const FormValue &Value) {} /// @} /// Value visitors /// @{ virtual void onValue(const uint8_t U) {} virtual void onValue(const uint16_t U) {} virtual void onValue(const uint32_t U) {} virtual void onValue(const uint64_t U, const bool LEB = false) {} virtual void onValue(const int64_t S, const bool LEB = false) {} virtual void onValue(const StringRef String) {} virtual void onValue(const MemoryBufferRef MBR) {} /// @} public: VisitorImpl(T &DI) : DebugInfo(DI) {} virtual ~VisitorImpl() {} void traverseDebugInfo(); private: void onVariableSizeValue(uint64_t U, unsigned Size); }; // Making the visior instantiations extern and explicit in the cpp file. This // prevents them from being instantiated in every compile unit that uses the // visitors. extern template class VisitorImpl; extern template class VisitorImpl; class Visitor : public VisitorImpl { public: Visitor(Data &DI) : VisitorImpl(DI) {} }; class ConstVisitor : public VisitorImpl { public: ConstVisitor(const Data &DI) : VisitorImpl(DI) {} }; } // namespace DWARFYAML } // namespace llvm #endif binaryen-version_91/third_party/llvm-project/DWARFYAML.cpp000066400000000000000000000154511362402614000236660ustar00rootroot00000000000000//===- DWARFYAML.cpp - DWARF YAMLIO implementation ------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file defines classes for handling the YAML representation of DWARF Debug // Info. // //===----------------------------------------------------------------------===// #include "llvm/ObjectYAML/DWARFYAML.h" namespace llvm { bool DWARFYAML::Data::isEmpty() const { return 0 == DebugStrings.size() + AbbrevDecls.size(); } namespace yaml { void MappingTraits::mapping(IO &IO, DWARFYAML::Data &DWARF) { auto oldContext = IO.getContext(); IO.setContext(&DWARF); IO.mapOptional("debug_str", DWARF.DebugStrings); IO.mapOptional("debug_abbrev", DWARF.AbbrevDecls); if (!DWARF.ARanges.empty() || !IO.outputting()) IO.mapOptional("debug_aranges", DWARF.ARanges); if (!DWARF.PubNames.Entries.empty() || !IO.outputting()) IO.mapOptional("debug_pubnames", DWARF.PubNames); if (!DWARF.PubTypes.Entries.empty() || !IO.outputting()) IO.mapOptional("debug_pubtypes", DWARF.PubTypes); if (!DWARF.GNUPubNames.Entries.empty() || !IO.outputting()) IO.mapOptional("debug_gnu_pubnames", DWARF.GNUPubNames); if (!DWARF.GNUPubTypes.Entries.empty() || !IO.outputting()) IO.mapOptional("debug_gnu_pubtypes", DWARF.GNUPubTypes); IO.mapOptional("debug_info", DWARF.CompileUnits); IO.mapOptional("debug_line", DWARF.DebugLines); IO.setContext(&oldContext); } void MappingTraits::mapping(IO &IO, DWARFYAML::Abbrev &Abbrev) { IO.mapRequired("Code", Abbrev.Code); IO.mapRequired("Tag", Abbrev.Tag); IO.mapRequired("Children", Abbrev.Children); IO.mapRequired("Attributes", Abbrev.Attributes); } void MappingTraits::mapping( IO &IO, DWARFYAML::AttributeAbbrev &AttAbbrev) { IO.mapRequired("Attribute", AttAbbrev.Attribute); IO.mapRequired("Form", AttAbbrev.Form); if(AttAbbrev.Form == dwarf::DW_FORM_implicit_const) IO.mapRequired("Value", AttAbbrev.Value); } void MappingTraits::mapping( IO &IO, DWARFYAML::ARangeDescriptor &Descriptor) { IO.mapRequired("Address", Descriptor.Address); IO.mapRequired("Length", Descriptor.Length); } void MappingTraits::mapping(IO &IO, DWARFYAML::ARange &Range) { IO.mapRequired("Length", Range.Length); IO.mapRequired("Version", Range.Version); IO.mapRequired("CuOffset", Range.CuOffset); IO.mapRequired("AddrSize", Range.AddrSize); IO.mapRequired("SegSize", Range.SegSize); IO.mapRequired("Descriptors", Range.Descriptors); } void MappingTraits::mapping(IO &IO, DWARFYAML::PubEntry &Entry) { IO.mapRequired("DieOffset", Entry.DieOffset); if (reinterpret_cast(IO.getContext())->IsGNUStyle) IO.mapRequired("Descriptor", Entry.Descriptor); IO.mapRequired("Name", Entry.Name); } void MappingTraits::mapping( IO &IO, DWARFYAML::PubSection &Section) { auto OldContext = IO.getContext(); IO.setContext(&Section); IO.mapRequired("Length", Section.Length); IO.mapRequired("Version", Section.Version); IO.mapRequired("UnitOffset", Section.UnitOffset); IO.mapRequired("UnitSize", Section.UnitSize); IO.mapRequired("Entries", Section.Entries); IO.setContext(OldContext); } void MappingTraits::mapping(IO &IO, DWARFYAML::Unit &Unit) { IO.mapRequired("Length", Unit.Length); IO.mapRequired("Version", Unit.Version); if (Unit.Version >= 5) IO.mapRequired("UnitType", Unit.Type); IO.mapRequired("AbbrOffset", Unit.AbbrOffset); IO.mapRequired("AddrSize", Unit.AddrSize); IO.mapOptional("Entries", Unit.Entries); } void MappingTraits::mapping(IO &IO, DWARFYAML::Entry &Entry) { IO.mapRequired("AbbrCode", Entry.AbbrCode); IO.mapRequired("Values", Entry.Values); } void MappingTraits::mapping( IO &IO, DWARFYAML::FormValue &FormValue) { IO.mapOptional("Value", FormValue.Value); if (!FormValue.CStr.empty() || !IO.outputting()) IO.mapOptional("CStr", FormValue.CStr); if (!FormValue.BlockData.empty() || !IO.outputting()) IO.mapOptional("BlockData", FormValue.BlockData); } void MappingTraits::mapping(IO &IO, DWARFYAML::File &File) { IO.mapRequired("Name", File.Name); IO.mapRequired("DirIdx", File.DirIdx); IO.mapRequired("ModTime", File.ModTime); IO.mapRequired("Length", File.Length); } void MappingTraits::mapping( IO &IO, DWARFYAML::LineTableOpcode &LineTableOpcode) { IO.mapRequired("Opcode", LineTableOpcode.Opcode); if (LineTableOpcode.Opcode == dwarf::DW_LNS_extended_op) { IO.mapRequired("ExtLen", LineTableOpcode.ExtLen); IO.mapRequired("SubOpcode", LineTableOpcode.SubOpcode); } if (!LineTableOpcode.UnknownOpcodeData.empty() || !IO.outputting()) IO.mapOptional("UnknownOpcodeData", LineTableOpcode.UnknownOpcodeData); if (!LineTableOpcode.UnknownOpcodeData.empty() || !IO.outputting()) IO.mapOptional("StandardOpcodeData", LineTableOpcode.StandardOpcodeData); if (!LineTableOpcode.FileEntry.Name.empty() || !IO.outputting()) IO.mapOptional("FileEntry", LineTableOpcode.FileEntry); if (LineTableOpcode.Opcode == dwarf::DW_LNS_advance_line || !IO.outputting()) IO.mapOptional("SData", LineTableOpcode.SData); IO.mapOptional("Data", LineTableOpcode.Data); } void MappingTraits::mapping( IO &IO, DWARFYAML::LineTable &LineTable) { IO.mapRequired("Length", LineTable.Length); IO.mapRequired("Version", LineTable.Version); IO.mapRequired("PrologueLength", LineTable.PrologueLength); IO.mapRequired("MinInstLength", LineTable.MinInstLength); if(LineTable.Version >= 4) IO.mapRequired("MaxOpsPerInst", LineTable.MaxOpsPerInst); IO.mapRequired("DefaultIsStmt", LineTable.DefaultIsStmt); IO.mapRequired("LineBase", LineTable.LineBase); IO.mapRequired("LineRange", LineTable.LineRange); IO.mapRequired("OpcodeBase", LineTable.OpcodeBase); IO.mapRequired("StandardOpcodeLengths", LineTable.StandardOpcodeLengths); IO.mapRequired("IncludeDirs", LineTable.IncludeDirs); IO.mapRequired("Files", LineTable.Files); IO.mapRequired("Opcodes", LineTable.Opcodes); } void MappingTraits::mapping( IO &IO, DWARFYAML::InitialLength &InitialLength) { IO.mapRequired("TotalLength", InitialLength.TotalLength); if (InitialLength.isDWARF64()) IO.mapRequired("TotalLength64", InitialLength.TotalLength64); } } // end namespace yaml } // end namespace llvm binaryen-version_91/third_party/llvm-project/DataExtractor.cpp000066400000000000000000000153351362402614000251060ustar00rootroot00000000000000//===-- DataExtractor.cpp -------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/Support/DataExtractor.h" #include "llvm/Support/Errc.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Host.h" #include "llvm/Support/LEB128.h" #include "llvm/Support/SwapByteOrder.h" using namespace llvm; static void unexpectedEndReached(Error *E) { if (E) *E = createStringError(errc::illegal_byte_sequence, "unexpected end of data"); } static bool isError(Error *E) { return E && *E; } template static T getU(uint64_t *offset_ptr, const DataExtractor *de, bool isLittleEndian, const char *Data, llvm::Error *Err) { ErrorAsOutParameter ErrAsOut(Err); T val = 0; if (isError(Err)) return val; uint64_t offset = *offset_ptr; if (!de->isValidOffsetForDataOfSize(offset, sizeof(T))) { unexpectedEndReached(Err); return val; } std::memcpy(&val, &Data[offset], sizeof(val)); if (sys::IsLittleEndianHost != isLittleEndian) sys::swapByteOrder(val); // Advance the offset *offset_ptr += sizeof(val); return val; } template static T *getUs(uint64_t *offset_ptr, T *dst, uint32_t count, const DataExtractor *de, bool isLittleEndian, const char *Data, llvm::Error *Err) { ErrorAsOutParameter ErrAsOut(Err); if (isError(Err)) return nullptr; uint64_t offset = *offset_ptr; if (!de->isValidOffsetForDataOfSize(offset, sizeof(*dst) * count)) { unexpectedEndReached(Err); return nullptr; } for (T *value_ptr = dst, *end = dst + count; value_ptr != end; ++value_ptr, offset += sizeof(*dst)) *value_ptr = getU(offset_ptr, de, isLittleEndian, Data, Err); // Advance the offset *offset_ptr = offset; // Return a non-NULL pointer to the converted data as an indicator of // success return dst; } uint8_t DataExtractor::getU8(uint64_t *offset_ptr, llvm::Error *Err) const { return getU(offset_ptr, this, IsLittleEndian, Data.data(), Err); } uint8_t * DataExtractor::getU8(uint64_t *offset_ptr, uint8_t *dst, uint32_t count) const { return getUs(offset_ptr, dst, count, this, IsLittleEndian, Data.data(), nullptr); } uint8_t *DataExtractor::getU8(Cursor &C, uint8_t *Dst, uint32_t Count) const { return getUs(&C.Offset, Dst, Count, this, IsLittleEndian, Data.data(), &C.Err); } uint16_t DataExtractor::getU16(uint64_t *offset_ptr, llvm::Error *Err) const { return getU(offset_ptr, this, IsLittleEndian, Data.data(), Err); } uint16_t *DataExtractor::getU16(uint64_t *offset_ptr, uint16_t *dst, uint32_t count) const { return getUs(offset_ptr, dst, count, this, IsLittleEndian, Data.data(), nullptr); } uint32_t DataExtractor::getU24(uint64_t *offset_ptr) const { uint24_t ExtractedVal = getU(offset_ptr, this, IsLittleEndian, Data.data(), nullptr); // The 3 bytes are in the correct byte order for the host. return ExtractedVal.getAsUint32(sys::IsLittleEndianHost); } uint32_t DataExtractor::getU32(uint64_t *offset_ptr, llvm::Error *Err) const { return getU(offset_ptr, this, IsLittleEndian, Data.data(), Err); } uint32_t *DataExtractor::getU32(uint64_t *offset_ptr, uint32_t *dst, uint32_t count) const { return getUs(offset_ptr, dst, count, this, IsLittleEndian, Data.data(), nullptr); } uint64_t DataExtractor::getU64(uint64_t *offset_ptr, llvm::Error *Err) const { return getU(offset_ptr, this, IsLittleEndian, Data.data(), Err); } uint64_t *DataExtractor::getU64(uint64_t *offset_ptr, uint64_t *dst, uint32_t count) const { return getUs(offset_ptr, dst, count, this, IsLittleEndian, Data.data(), nullptr); } uint64_t DataExtractor::getUnsigned(uint64_t *offset_ptr, uint32_t byte_size, llvm::Error *Err) const { switch (byte_size) { case 1: return getU8(offset_ptr, Err); case 2: return getU16(offset_ptr, Err); case 4: return getU32(offset_ptr, Err); case 8: return getU64(offset_ptr, Err); } llvm_unreachable("getUnsigned unhandled case!"); } int64_t DataExtractor::getSigned(uint64_t *offset_ptr, uint32_t byte_size) const { switch (byte_size) { case 1: return (int8_t)getU8(offset_ptr); case 2: return (int16_t)getU16(offset_ptr); case 4: return (int32_t)getU32(offset_ptr); case 8: return (int64_t)getU64(offset_ptr); } llvm_unreachable("getSigned unhandled case!"); } const char *DataExtractor::getCStr(uint64_t *offset_ptr) const { uint64_t offset = *offset_ptr; StringRef::size_type pos = Data.find('\0', offset); if (pos != StringRef::npos) { *offset_ptr = pos + 1; return Data.data() + offset; } return nullptr; } StringRef DataExtractor::getCStrRef(uint64_t *offset_ptr) const { uint64_t Start = *offset_ptr; StringRef::size_type Pos = Data.find('\0', Start); if (Pos != StringRef::npos) { *offset_ptr = Pos + 1; return StringRef(Data.data() + Start, Pos - Start); } return StringRef(); } uint64_t DataExtractor::getULEB128(uint64_t *offset_ptr, llvm::Error *Err) const { assert(*offset_ptr <= Data.size()); ErrorAsOutParameter ErrAsOut(Err); if (isError(Err)) return 0; const char *error; unsigned bytes_read; uint64_t result = decodeULEB128( reinterpret_cast(Data.data() + *offset_ptr), &bytes_read, reinterpret_cast(Data.data() + Data.size()), &error); if (error) { if (Err) *Err = createStringError(errc::illegal_byte_sequence, error); return 0; } *offset_ptr += bytes_read; return result; } int64_t DataExtractor::getSLEB128(uint64_t *offset_ptr) const { assert(*offset_ptr <= Data.size()); const char *error; unsigned bytes_read; int64_t result = decodeSLEB128( reinterpret_cast(Data.data() + *offset_ptr), &bytes_read, reinterpret_cast(Data.data() + Data.size()), &error); if (error) return 0; *offset_ptr += bytes_read; return result; } void DataExtractor::skip(Cursor &C, uint64_t Length) const { ErrorAsOutParameter ErrAsOut(&C.Err); if (isError(&C.Err)) return; if (isValidOffsetForDataOfSize(C.Offset, Length)) C.Offset += Length; else unexpectedEndReached(&C.Err); } binaryen-version_91/third_party/llvm-project/Debug.cpp000066400000000000000000000125711362402614000233660ustar00rootroot00000000000000//===-- Debug.cpp - An easy way to add debug output to your code ----------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file implements a handy way of adding debugging information to your // code, without it being enabled all of the time, and without having to add // command line options to enable it. // // In particular, just wrap your code with the LLVM_DEBUG() macro, and it will // be enabled automatically if you specify '-debug' on the command-line. // Alternatively, you can also use the SET_DEBUG_TYPE("foo") macro to specify // that your debug code belongs to class "foo". Then, on the command line, you // can specify '-debug-only=foo' to enable JUST the debug information for the // foo class. // // When compiling without assertions, the -debug-* options and all code in // LLVM_DEBUG() statements disappears, so it does not affect the runtime of the // code. // //===----------------------------------------------------------------------===// #include "llvm/Support/Debug.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/Signals.h" #include "llvm/Support/circular_raw_ostream.h" #include "llvm/Support/raw_ostream.h" #undef isCurrentDebugType #undef setCurrentDebugType #undef setCurrentDebugTypes using namespace llvm; // Even though LLVM might be built with NDEBUG, define symbols that the code // built without NDEBUG can depend on via the llvm/Support/Debug.h header. namespace llvm { /// Exported boolean set by the -debug option. bool DebugFlag = false; #if 0 // XXX BINARYEN static ManagedStatic> CurrentDebugType; #endif /// Return true if the specified string is the debug type /// specified on the command line, or if none was specified on the command line /// with the -debug-only=X option. bool isCurrentDebugType(const char *DebugType) { llvm_unreachable("debug type"); } /// Set the current debug type, as if the -debug-only=X /// option were specified. Note that DebugFlag also needs to be set to true for /// debug output to be produced. /// void setCurrentDebugTypes(const char **Types, unsigned Count); void setCurrentDebugType(const char *Type) { setCurrentDebugTypes(&Type, 1); } void setCurrentDebugTypes(const char **Types, unsigned Count) { llvm_unreachable("set debug type"); } } // namespace llvm // All Debug.h functionality is a no-op in NDEBUG mode. #if 0 // XXX BINARYEN ndef NDEBUG // -debug - Command line option to enable the DEBUG statements in the passes. // This flag may only be enabled in debug builds. static cl::opt Debug("debug", cl::desc("Enable debug output"), cl::Hidden, cl::location(DebugFlag)); // -debug-buffer-size - Buffer the last N characters of debug output //until program termination. static cl::opt DebugBufferSize("debug-buffer-size", cl::desc("Buffer the last N characters of debug output " "until program termination. " "[default 0 -- immediate print-out]"), cl::Hidden, cl::init(0)); namespace { struct DebugOnlyOpt { void operator=(const std::string &Val) const { if (Val.empty()) return; DebugFlag = true; SmallVector dbgTypes; StringRef(Val).split(dbgTypes, ',', -1, false); for (auto dbgType : dbgTypes) CurrentDebugType->push_back(dbgType); } }; } static DebugOnlyOpt DebugOnlyOptLoc; static cl::opt > DebugOnly("debug-only", cl::desc("Enable a specific type of debug output (comma separated list of types)"), cl::Hidden, cl::ZeroOrMore, cl::value_desc("debug string"), cl::location(DebugOnlyOptLoc), cl::ValueRequired); // Signal handlers - dump debug output on termination. static void debug_user_sig_handler(void *Cookie) { // This is a bit sneaky. Since this is under #ifndef NDEBUG, we // know that debug mode is enabled and dbgs() really is a // circular_raw_ostream. If NDEBUG is defined, then dbgs() == // errs() but this will never be invoked. llvm::circular_raw_ostream &dbgout = static_cast(llvm::dbgs()); dbgout.flushBufferWithBanner(); } /// dbgs - Return a circular-buffered debug stream. raw_ostream &llvm::dbgs() { // Do one-time initialization in a thread-safe way. static struct dbgstream { circular_raw_ostream strm; dbgstream() : strm(errs(), "*** Debug Log Output ***\n", (!EnableDebugBuffering || !DebugFlag) ? 0 : DebugBufferSize) { if (EnableDebugBuffering && DebugFlag && DebugBufferSize != 0) // TODO: Add a handler for SIGUSER1-type signals so the user can // force a debug dump. sys::AddSignalHandler(&debug_user_sig_handler, nullptr); // Otherwise we've already set the debug stream buffer size to // zero, disabling buffering so it will output directly to errs(). } } thestrm; return thestrm.strm; } #else // Avoid "has no symbols" warning. namespace llvm { /// dbgs - Return errs(). raw_ostream &dbgs() { return errs(); } } #endif /// EnableDebugBuffering - Turn on signal handler installation. /// bool llvm::EnableDebugBuffering = false; binaryen-version_91/third_party/llvm-project/Dwarf.cpp000066400000000000000000000541511362402614000234030ustar00rootroot00000000000000//===-- llvm/BinaryFormat/Dwarf.cpp - Dwarf Framework ------------*- C++-*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file contains support for generic dwarf information. // //===----------------------------------------------------------------------===// #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/Triple.h" #include "llvm/Support/ErrorHandling.h" using namespace llvm; using namespace dwarf; StringRef llvm::dwarf::TagString(unsigned Tag) { switch (Tag) { default: return StringRef(); #define HANDLE_DW_TAG(ID, NAME, VERSION, VENDOR, KIND) \ case DW_TAG_##NAME: \ return "DW_TAG_" #NAME; #include "llvm/BinaryFormat/Dwarf.def" } } unsigned llvm::dwarf::getTag(StringRef TagString) { return StringSwitch(TagString) #define HANDLE_DW_TAG(ID, NAME, VERSION, VENDOR, KIND) \ .Case("DW_TAG_" #NAME, DW_TAG_##NAME) #include "llvm/BinaryFormat/Dwarf.def" .Default(DW_TAG_invalid); } unsigned llvm::dwarf::TagVersion(dwarf::Tag Tag) { switch (Tag) { default: return 0; #define HANDLE_DW_TAG(ID, NAME, VERSION, VENDOR, KIND) \ case DW_TAG_##NAME: \ return VERSION; #include "llvm/BinaryFormat/Dwarf.def" } } unsigned llvm::dwarf::TagVendor(dwarf::Tag Tag) { switch (Tag) { default: return 0; #define HANDLE_DW_TAG(ID, NAME, VERSION, VENDOR, KIND) \ case DW_TAG_##NAME: \ return DWARF_VENDOR_##VENDOR; #include "llvm/BinaryFormat/Dwarf.def" } } StringRef llvm::dwarf::ChildrenString(unsigned Children) { switch (Children) { case DW_CHILDREN_no: return "DW_CHILDREN_no"; case DW_CHILDREN_yes: return "DW_CHILDREN_yes"; } return StringRef(); } StringRef llvm::dwarf::AttributeString(unsigned Attribute) { switch (Attribute) { default: return StringRef(); #define HANDLE_DW_AT(ID, NAME, VERSION, VENDOR) \ case DW_AT_##NAME: \ return "DW_AT_" #NAME; #include "llvm/BinaryFormat/Dwarf.def" } } unsigned llvm::dwarf::AttributeVersion(dwarf::Attribute Attribute) { switch (Attribute) { default: return 0; #define HANDLE_DW_AT(ID, NAME, VERSION, VENDOR) \ case DW_AT_##NAME: \ return VERSION; #include "llvm/BinaryFormat/Dwarf.def" } } unsigned llvm::dwarf::AttributeVendor(dwarf::Attribute Attribute) { switch (Attribute) { default: return 0; #define HANDLE_DW_AT(ID, NAME, VERSION, VENDOR) \ case DW_AT_##NAME: \ return DWARF_VENDOR_##VENDOR; #include "llvm/BinaryFormat/Dwarf.def" } } StringRef llvm::dwarf::FormEncodingString(unsigned Encoding) { switch (Encoding) { default: return StringRef(); #define HANDLE_DW_FORM(ID, NAME, VERSION, VENDOR) \ case DW_FORM_##NAME: \ return "DW_FORM_" #NAME; #include "llvm/BinaryFormat/Dwarf.def" } } unsigned llvm::dwarf::FormVersion(dwarf::Form Form) { switch (Form) { default: return 0; #define HANDLE_DW_FORM(ID, NAME, VERSION, VENDOR) \ case DW_FORM_##NAME: \ return VERSION; #include "llvm/BinaryFormat/Dwarf.def" } } unsigned llvm::dwarf::FormVendor(dwarf::Form Form) { switch (Form) { default: return 0; #define HANDLE_DW_FORM(ID, NAME, VERSION, VENDOR) \ case DW_FORM_##NAME: \ return DWARF_VENDOR_##VENDOR; #include "llvm/BinaryFormat/Dwarf.def" } } StringRef llvm::dwarf::OperationEncodingString(unsigned Encoding) { switch (Encoding) { default: return StringRef(); #define HANDLE_DW_OP(ID, NAME, VERSION, VENDOR) \ case DW_OP_##NAME: \ return "DW_OP_" #NAME; #include "llvm/BinaryFormat/Dwarf.def" case DW_OP_LLVM_convert: return "DW_OP_LLVM_convert"; case DW_OP_LLVM_fragment: return "DW_OP_LLVM_fragment"; case DW_OP_LLVM_tag_offset: return "DW_OP_LLVM_tag_offset"; case DW_OP_LLVM_entry_value: return "DW_OP_LLVM_entry_value"; } } unsigned llvm::dwarf::getOperationEncoding(StringRef OperationEncodingString) { return StringSwitch(OperationEncodingString) #define HANDLE_DW_OP(ID, NAME, VERSION, VENDOR) \ .Case("DW_OP_" #NAME, DW_OP_##NAME) #include "llvm/BinaryFormat/Dwarf.def" .Case("DW_OP_LLVM_convert", DW_OP_LLVM_convert) .Case("DW_OP_LLVM_fragment", DW_OP_LLVM_fragment) .Case("DW_OP_LLVM_tag_offset", DW_OP_LLVM_tag_offset) .Case("DW_OP_LLVM_entry_value", DW_OP_LLVM_entry_value) .Default(0); } unsigned llvm::dwarf::OperationVersion(dwarf::LocationAtom Op) { switch (Op) { default: return 0; #define HANDLE_DW_OP(ID, NAME, VERSION, VENDOR) \ case DW_OP_##NAME: \ return VERSION; #include "llvm/BinaryFormat/Dwarf.def" } } unsigned llvm::dwarf::OperationVendor(dwarf::LocationAtom Op) { switch (Op) { default: return 0; #define HANDLE_DW_OP(ID, NAME, VERSION, VENDOR) \ case DW_OP_##NAME: \ return DWARF_VENDOR_##VENDOR; #include "llvm/BinaryFormat/Dwarf.def" } } StringRef llvm::dwarf::AttributeEncodingString(unsigned Encoding) { switch (Encoding) { default: return StringRef(); #define HANDLE_DW_ATE(ID, NAME, VERSION, VENDOR) \ case DW_ATE_##NAME: \ return "DW_ATE_" #NAME; #include "llvm/BinaryFormat/Dwarf.def" } } unsigned llvm::dwarf::getAttributeEncoding(StringRef EncodingString) { return StringSwitch(EncodingString) #define HANDLE_DW_ATE(ID, NAME, VERSION, VENDOR) \ .Case("DW_ATE_" #NAME, DW_ATE_##NAME) #include "llvm/BinaryFormat/Dwarf.def" .Default(0); } unsigned llvm::dwarf::AttributeEncodingVersion(dwarf::TypeKind ATE) { switch (ATE) { default: return 0; #define HANDLE_DW_ATE(ID, NAME, VERSION, VENDOR) \ case DW_ATE_##NAME: \ return VERSION; #include "llvm/BinaryFormat/Dwarf.def" } } unsigned llvm::dwarf::AttributeEncodingVendor(dwarf::TypeKind ATE) { switch (ATE) { default: return 0; #define HANDLE_DW_ATE(ID, NAME, VERSION, VENDOR) \ case DW_ATE_##NAME: \ return DWARF_VENDOR_##VENDOR; #include "llvm/BinaryFormat/Dwarf.def" } } StringRef llvm::dwarf::DecimalSignString(unsigned Sign) { switch (Sign) { case DW_DS_unsigned: return "DW_DS_unsigned"; case DW_DS_leading_overpunch: return "DW_DS_leading_overpunch"; case DW_DS_trailing_overpunch: return "DW_DS_trailing_overpunch"; case DW_DS_leading_separate: return "DW_DS_leading_separate"; case DW_DS_trailing_separate: return "DW_DS_trailing_separate"; } return StringRef(); } StringRef llvm::dwarf::EndianityString(unsigned Endian) { switch (Endian) { case DW_END_default: return "DW_END_default"; case DW_END_big: return "DW_END_big"; case DW_END_little: return "DW_END_little"; case DW_END_lo_user: return "DW_END_lo_user"; case DW_END_hi_user: return "DW_END_hi_user"; } return StringRef(); } StringRef llvm::dwarf::AccessibilityString(unsigned Access) { switch (Access) { // Accessibility codes case DW_ACCESS_public: return "DW_ACCESS_public"; case DW_ACCESS_protected: return "DW_ACCESS_protected"; case DW_ACCESS_private: return "DW_ACCESS_private"; } return StringRef(); } StringRef llvm::dwarf::DefaultedMemberString(unsigned DefaultedEncodings) { switch (DefaultedEncodings) { // Defaulted Member Encodings codes case DW_DEFAULTED_no: return "DW_DEFAULTED_no"; case DW_DEFAULTED_in_class: return "DW_DEFAULTED_in_class"; case DW_DEFAULTED_out_of_class: return "DW_DEFAULTED_out_of_class"; } return StringRef(); } StringRef llvm::dwarf::VisibilityString(unsigned Visibility) { switch (Visibility) { case DW_VIS_local: return "DW_VIS_local"; case DW_VIS_exported: return "DW_VIS_exported"; case DW_VIS_qualified: return "DW_VIS_qualified"; } return StringRef(); } StringRef llvm::dwarf::VirtualityString(unsigned Virtuality) { switch (Virtuality) { default: return StringRef(); #define HANDLE_DW_VIRTUALITY(ID, NAME) \ case DW_VIRTUALITY_##NAME: \ return "DW_VIRTUALITY_" #NAME; #include "llvm/BinaryFormat/Dwarf.def" } } unsigned llvm::dwarf::getVirtuality(StringRef VirtualityString) { return StringSwitch(VirtualityString) #define HANDLE_DW_VIRTUALITY(ID, NAME) \ .Case("DW_VIRTUALITY_" #NAME, DW_VIRTUALITY_##NAME) #include "llvm/BinaryFormat/Dwarf.def" .Default(DW_VIRTUALITY_invalid); } StringRef llvm::dwarf::LanguageString(unsigned Language) { switch (Language) { default: return StringRef(); #define HANDLE_DW_LANG(ID, NAME, LOWER_BOUND, VERSION, VENDOR) \ case DW_LANG_##NAME: \ return "DW_LANG_" #NAME; #include "llvm/BinaryFormat/Dwarf.def" } } unsigned llvm::dwarf::getLanguage(StringRef LanguageString) { return StringSwitch(LanguageString) #define HANDLE_DW_LANG(ID, NAME, LOWER_BOUND, VERSION, VENDOR) \ .Case("DW_LANG_" #NAME, DW_LANG_##NAME) #include "llvm/BinaryFormat/Dwarf.def" .Default(0); } unsigned llvm::dwarf::LanguageVersion(dwarf::SourceLanguage Lang) { switch (Lang) { default: return 0; #define HANDLE_DW_LANG(ID, NAME, LOWER_BOUND, VERSION, VENDOR) \ case DW_LANG_##NAME: \ return VERSION; #include "llvm/BinaryFormat/Dwarf.def" } } unsigned llvm::dwarf::LanguageVendor(dwarf::SourceLanguage Lang) { switch (Lang) { default: return 0; #define HANDLE_DW_LANG(ID, NAME, LOWER_BOUND, VERSION, VENDOR) \ case DW_LANG_##NAME: \ return DWARF_VENDOR_##VENDOR; #include "llvm/BinaryFormat/Dwarf.def" } } Optional llvm::dwarf::LanguageLowerBound(dwarf::SourceLanguage Lang) { switch (Lang) { default: return None; #define HANDLE_DW_LANG(ID, NAME, LOWER_BOUND, VERSION, VENDOR) \ case DW_LANG_##NAME: \ return LOWER_BOUND; #include "llvm/BinaryFormat/Dwarf.def" } } StringRef llvm::dwarf::CaseString(unsigned Case) { switch (Case) { case DW_ID_case_sensitive: return "DW_ID_case_sensitive"; case DW_ID_up_case: return "DW_ID_up_case"; case DW_ID_down_case: return "DW_ID_down_case"; case DW_ID_case_insensitive: return "DW_ID_case_insensitive"; } return StringRef(); } StringRef llvm::dwarf::ConventionString(unsigned CC) { switch (CC) { default: return StringRef(); #define HANDLE_DW_CC(ID, NAME) \ case DW_CC_##NAME: \ return "DW_CC_" #NAME; #include "llvm/BinaryFormat/Dwarf.def" } } unsigned llvm::dwarf::getCallingConvention(StringRef CCString) { return StringSwitch(CCString) #define HANDLE_DW_CC(ID, NAME) .Case("DW_CC_" #NAME, DW_CC_##NAME) #include "llvm/BinaryFormat/Dwarf.def" .Default(0); } StringRef llvm::dwarf::InlineCodeString(unsigned Code) { switch (Code) { case DW_INL_not_inlined: return "DW_INL_not_inlined"; case DW_INL_inlined: return "DW_INL_inlined"; case DW_INL_declared_not_inlined: return "DW_INL_declared_not_inlined"; case DW_INL_declared_inlined: return "DW_INL_declared_inlined"; } return StringRef(); } StringRef llvm::dwarf::ArrayOrderString(unsigned Order) { switch (Order) { case DW_ORD_row_major: return "DW_ORD_row_major"; case DW_ORD_col_major: return "DW_ORD_col_major"; } return StringRef(); } StringRef llvm::dwarf::LNStandardString(unsigned Standard) { switch (Standard) { default: return StringRef(); #define HANDLE_DW_LNS(ID, NAME) \ case DW_LNS_##NAME: \ return "DW_LNS_" #NAME; #include "llvm/BinaryFormat/Dwarf.def" } } StringRef llvm::dwarf::LNExtendedString(unsigned Encoding) { switch (Encoding) { default: return StringRef(); #define HANDLE_DW_LNE(ID, NAME) \ case DW_LNE_##NAME: \ return "DW_LNE_" #NAME; #include "llvm/BinaryFormat/Dwarf.def" } } StringRef llvm::dwarf::MacinfoString(unsigned Encoding) { switch (Encoding) { // Macinfo Type Encodings case DW_MACINFO_define: return "DW_MACINFO_define"; case DW_MACINFO_undef: return "DW_MACINFO_undef"; case DW_MACINFO_start_file: return "DW_MACINFO_start_file"; case DW_MACINFO_end_file: return "DW_MACINFO_end_file"; case DW_MACINFO_vendor_ext: return "DW_MACINFO_vendor_ext"; case DW_MACINFO_invalid: return "DW_MACINFO_invalid"; } return StringRef(); } unsigned llvm::dwarf::getMacinfo(StringRef MacinfoString) { return StringSwitch(MacinfoString) .Case("DW_MACINFO_define", DW_MACINFO_define) .Case("DW_MACINFO_undef", DW_MACINFO_undef) .Case("DW_MACINFO_start_file", DW_MACINFO_start_file) .Case("DW_MACINFO_end_file", DW_MACINFO_end_file) .Case("DW_MACINFO_vendor_ext", DW_MACINFO_vendor_ext) .Default(DW_MACINFO_invalid); } StringRef llvm::dwarf::RangeListEncodingString(unsigned Encoding) { switch (Encoding) { default: return StringRef(); #define HANDLE_DW_RLE(ID, NAME) \ case DW_RLE_##NAME: \ return "DW_RLE_" #NAME; #include "llvm/BinaryFormat/Dwarf.def" } } StringRef llvm::dwarf::LocListEncodingString(unsigned Encoding) { switch (Encoding) { default: return StringRef(); #define HANDLE_DW_LLE(ID, NAME) \ case DW_LLE_##NAME: \ return "DW_LLE_" #NAME; #include "llvm/BinaryFormat/Dwarf.def" } } StringRef llvm::dwarf::CallFrameString(unsigned Encoding, Triple::ArchType Arch) { assert(Arch != llvm::Triple::ArchType::UnknownArch); #define SELECT_AARCH64 (Arch == llvm::Triple::aarch64_be || Arch == llvm::Triple::aarch64) #define SELECT_MIPS64 Arch == llvm::Triple::mips64 #define SELECT_SPARC (Arch == llvm::Triple::sparc || Arch == llvm::Triple::sparcv9) #define SELECT_X86 (Arch == llvm::Triple::x86 || Arch == llvm::Triple::x86_64) #define HANDLE_DW_CFA(ID, NAME) #define HANDLE_DW_CFA_PRED(ID, NAME, PRED) \ if (ID == Encoding && PRED) \ return "DW_CFA_" #NAME; #include "llvm/BinaryFormat/Dwarf.def" switch (Encoding) { default: return StringRef(); #define HANDLE_DW_CFA_PRED(ID, NAME, PRED) #define HANDLE_DW_CFA(ID, NAME) \ case DW_CFA_##NAME: \ return "DW_CFA_" #NAME; #include "llvm/BinaryFormat/Dwarf.def" #undef SELECT_X86 #undef SELECT_SPARC #undef SELECT_MIPS64 #undef SELECT_AARCH64 } } StringRef llvm::dwarf::ApplePropertyString(unsigned Prop) { switch (Prop) { default: return StringRef(); #define HANDLE_DW_APPLE_PROPERTY(ID, NAME) \ case DW_APPLE_PROPERTY_##NAME: \ return "DW_APPLE_PROPERTY_" #NAME; #include "llvm/BinaryFormat/Dwarf.def" } } StringRef llvm::dwarf::UnitTypeString(unsigned UT) { switch (UT) { default: return StringRef(); #define HANDLE_DW_UT(ID, NAME) \ case DW_UT_##NAME: \ return "DW_UT_" #NAME; #include "llvm/BinaryFormat/Dwarf.def" } } StringRef llvm::dwarf::AtomTypeString(unsigned AT) { switch (AT) { case dwarf::DW_ATOM_null: return "DW_ATOM_null"; case dwarf::DW_ATOM_die_offset: return "DW_ATOM_die_offset"; case DW_ATOM_cu_offset: return "DW_ATOM_cu_offset"; case DW_ATOM_die_tag: return "DW_ATOM_die_tag"; case DW_ATOM_type_flags: case DW_ATOM_type_type_flags: return "DW_ATOM_type_flags"; case DW_ATOM_qual_name_hash: return "DW_ATOM_qual_name_hash"; } return StringRef(); } StringRef llvm::dwarf::GDBIndexEntryKindString(GDBIndexEntryKind Kind) { switch (Kind) { case GIEK_NONE: return "NONE"; case GIEK_TYPE: return "TYPE"; case GIEK_VARIABLE: return "VARIABLE"; case GIEK_FUNCTION: return "FUNCTION"; case GIEK_OTHER: return "OTHER"; case GIEK_UNUSED5: return "UNUSED5"; case GIEK_UNUSED6: return "UNUSED6"; case GIEK_UNUSED7: return "UNUSED7"; } llvm_unreachable("Unknown GDBIndexEntryKind value"); } StringRef llvm::dwarf::GDBIndexEntryLinkageString(GDBIndexEntryLinkage Linkage) { switch (Linkage) { case GIEL_EXTERNAL: return "EXTERNAL"; case GIEL_STATIC: return "STATIC"; } llvm_unreachable("Unknown GDBIndexEntryLinkage value"); } StringRef llvm::dwarf::AttributeValueString(uint16_t Attr, unsigned Val) { switch (Attr) { case DW_AT_accessibility: return AccessibilityString(Val); case DW_AT_virtuality: return VirtualityString(Val); case DW_AT_language: return LanguageString(Val); case DW_AT_encoding: return AttributeEncodingString(Val); case DW_AT_decimal_sign: return DecimalSignString(Val); case DW_AT_endianity: return EndianityString(Val); case DW_AT_visibility: return VisibilityString(Val); case DW_AT_identifier_case: return CaseString(Val); case DW_AT_calling_convention: return ConventionString(Val); case DW_AT_inline: return InlineCodeString(Val); case DW_AT_ordering: return ArrayOrderString(Val); case DW_AT_APPLE_runtime_class: return LanguageString(Val); case DW_AT_defaulted: return DefaultedMemberString(Val); } return StringRef(); } StringRef llvm::dwarf::AtomValueString(uint16_t Atom, unsigned Val) { switch (Atom) { case DW_ATOM_null: return "NULL"; case DW_ATOM_die_tag: return TagString(Val); } return StringRef(); } StringRef llvm::dwarf::IndexString(unsigned Idx) { switch (Idx) { default: return StringRef(); #define HANDLE_DW_IDX(ID, NAME) \ case DW_IDX_##NAME: \ return "DW_IDX_" #NAME; #include "llvm/BinaryFormat/Dwarf.def" } } Optional llvm::dwarf::getFixedFormByteSize(dwarf::Form Form, FormParams Params) { switch (Form) { case DW_FORM_addr: if (Params) return Params.AddrSize; return None; case DW_FORM_block: // ULEB128 length L followed by L bytes. case DW_FORM_block1: // 1 byte length L followed by L bytes. case DW_FORM_block2: // 2 byte length L followed by L bytes. case DW_FORM_block4: // 4 byte length L followed by L bytes. case DW_FORM_string: // C-string with null terminator. case DW_FORM_sdata: // SLEB128. case DW_FORM_udata: // ULEB128. case DW_FORM_ref_udata: // ULEB128. case DW_FORM_indirect: // ULEB128. case DW_FORM_exprloc: // ULEB128 length L followed by L bytes. case DW_FORM_strx: // ULEB128. case DW_FORM_addrx: // ULEB128. case DW_FORM_loclistx: // ULEB128. case DW_FORM_rnglistx: // ULEB128. case DW_FORM_GNU_addr_index: // ULEB128. case DW_FORM_GNU_str_index: // ULEB128. return None; case DW_FORM_ref_addr: if (Params) return Params.getRefAddrByteSize(); return None; case DW_FORM_flag: case DW_FORM_data1: case DW_FORM_ref1: case DW_FORM_strx1: case DW_FORM_addrx1: return 1; case DW_FORM_data2: case DW_FORM_ref2: case DW_FORM_strx2: case DW_FORM_addrx2: return 2; case DW_FORM_strx3: return 3; case DW_FORM_data4: case DW_FORM_ref4: case DW_FORM_ref_sup4: case DW_FORM_strx4: case DW_FORM_addrx4: return 4; case DW_FORM_strp: case DW_FORM_GNU_ref_alt: case DW_FORM_GNU_strp_alt: case DW_FORM_line_strp: case DW_FORM_sec_offset: case DW_FORM_strp_sup: if (Params) return Params.getDwarfOffsetByteSize(); return None; case DW_FORM_data8: case DW_FORM_ref8: case DW_FORM_ref_sig8: case DW_FORM_ref_sup8: return 8; case DW_FORM_flag_present: return 0; case DW_FORM_data16: return 16; case DW_FORM_implicit_const: // The implicit value is stored in the abbreviation as a SLEB128, and // there no data in debug info. return 0; default: break; } return None; } bool llvm::dwarf::isValidFormForVersion(Form F, unsigned Version, bool ExtensionsOk) { if (FormVendor(F) == DWARF_VENDOR_DWARF) { unsigned FV = FormVersion(F); return FV > 0 && FV <= Version; } return ExtensionsOk; } constexpr char llvm::dwarf::EnumTraits::Type[]; constexpr char llvm::dwarf::EnumTraits::Type[]; constexpr char llvm::dwarf::EnumTraits::Type[]; constexpr char llvm::dwarf::EnumTraits::Type[]; binaryen-version_91/third_party/llvm-project/Error.cpp000066400000000000000000000110251362402614000234220ustar00rootroot00000000000000//===----- lib/Support/Error.cpp - Error and associated utilities ---------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/Support/Error.h" #include "llvm/ADT/Twine.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ManagedStatic.h" #include using namespace llvm; namespace { enum class ErrorErrorCode : int { MultipleErrors = 1, FileError, InconvertibleError }; // FIXME: This class is only here to support the transition to llvm::Error. It // will be removed once this transition is complete. Clients should prefer to // deal with the Error value directly, rather than converting to error_code. class ErrorErrorCategory : public std::error_category { public: const char *name() const noexcept override { return "Error"; } std::string message(int condition) const override { switch (static_cast(condition)) { case ErrorErrorCode::MultipleErrors: return "Multiple errors"; case ErrorErrorCode::InconvertibleError: return "Inconvertible error value. An error has occurred that could " "not be converted to a known std::error_code. Please file a " "bug."; case ErrorErrorCode::FileError: return "A file error occurred."; } llvm_unreachable("Unhandled error code"); } }; } #if 0 // XXX BINARYEN static ManagedStatic ErrorErrorCat; #endif namespace llvm { void ErrorInfoBase::anchor() {} char ErrorInfoBase::ID = 0; char ErrorList::ID = 0; void ECError::anchor() {} char ECError::ID = 0; char StringError::ID = 0; char FileError::ID = 0; void logAllUnhandledErrors(Error E, raw_ostream &OS, Twine ErrorBanner) { if (!E) return; OS << ErrorBanner; handleAllErrors(std::move(E), [&](const ErrorInfoBase &EI) { EI.log(OS); OS << "\n"; }); } std::error_code ErrorList::convertToErrorCode() const { llvm_unreachable("convert error code"); } std::error_code inconvertibleErrorCode() { llvm_unreachable("inconvertible error code"); } std::error_code FileError::convertToErrorCode() const { llvm_unreachable("(file) convert error code"); } Error errorCodeToError(std::error_code EC) { if (!EC) return Error::success(); return Error(std::make_unique(ECError(EC))); } std::error_code errorToErrorCode(Error Err) { std::error_code EC; handleAllErrors(std::move(Err), [&](const ErrorInfoBase &EI) { EC = EI.convertToErrorCode(); }); if (EC == inconvertibleErrorCode()) report_fatal_error(EC.message()); return EC; } #if LLVM_ENABLE_ABI_BREAKING_CHECKS void Error::fatalUncheckedError() const { dbgs() << "Program aborted due to an unhandled Error:\n"; if (getPtr()) getPtr()->log(dbgs()); else dbgs() << "Error value was Success. (Note: Success values must still be " "checked prior to being destroyed).\n"; abort(); } #endif StringError::StringError(std::error_code EC, const Twine &S) : Msg(S.str()), EC(EC) {} StringError::StringError(const Twine &S, std::error_code EC) : Msg(S.str()), EC(EC), PrintMsgOnly(true) {} void StringError::log(raw_ostream &OS) const { if (PrintMsgOnly) { OS << Msg; } else { OS << EC.message(); if (!Msg.empty()) OS << (" " + Msg); } } std::error_code StringError::convertToErrorCode() const { return EC; } Error createStringError(std::error_code EC, char const *Msg) { return make_error(Msg, EC); } void report_fatal_error(Error Err, bool GenCrashDiag) { assert(Err && "report_fatal_error called with success value"); std::string ErrMsg; { raw_string_ostream ErrStream(ErrMsg); logAllUnhandledErrors(std::move(Err), ErrStream); } report_fatal_error(ErrMsg); } } // end namespace llvm LLVMErrorTypeId LLVMGetErrorTypeId(LLVMErrorRef Err) { return reinterpret_cast(Err)->dynamicClassID(); } void LLVMConsumeError(LLVMErrorRef Err) { consumeError(unwrap(Err)); } char *LLVMGetErrorMessage(LLVMErrorRef Err) { std::string Tmp = toString(unwrap(Err)); char *ErrMsg = new char[Tmp.size() + 1]; memcpy(ErrMsg, Tmp.data(), Tmp.size()); ErrMsg[Tmp.size()] = '\0'; return ErrMsg; } void LLVMDisposeErrorMessage(char *ErrMsg) { delete[] ErrMsg; } LLVMErrorTypeId LLVMGetStringErrorTypeId() { return reinterpret_cast(&StringError::ID); } binaryen-version_91/third_party/llvm-project/Error.h000066400000000000000000000027471362402614000231020ustar00rootroot00000000000000//===- Error.h - system_error extensions for obj2yaml -----------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #ifndef LLVM_TOOLS_OBJ2YAML_ERROR_H #define LLVM_TOOLS_OBJ2YAML_ERROR_H #include "llvm/Support/Error.h" #include namespace llvm { const std::error_category &obj2yaml_category(); enum class obj2yaml_error { success = 0, file_not_found, unrecognized_file_format, unsupported_obj_file_format, not_implemented }; inline std::error_code make_error_code(obj2yaml_error e) { return std::error_code(static_cast(e), obj2yaml_category()); } class Obj2YamlError : public ErrorInfo { public: static char ID; Obj2YamlError(obj2yaml_error C) : Code(C) {} Obj2YamlError(std::string ErrMsg) : ErrMsg(std::move(ErrMsg)) {} Obj2YamlError(obj2yaml_error C, std::string ErrMsg) : ErrMsg(std::move(ErrMsg)), Code(C) {} void log(raw_ostream &OS) const override; const std::string &getErrorMessage() const { return ErrMsg; } std::error_code convertToErrorCode() const override; private: std::string ErrMsg; obj2yaml_error Code = obj2yaml_error::success; }; } // namespace llvm namespace std { template <> struct is_error_code_enum : std::true_type {}; } #endif binaryen-version_91/third_party/llvm-project/ErrorHandling.cpp000066400000000000000000000263041362402614000250750ustar00rootroot00000000000000//===- lib/Support/ErrorHandling.cpp - Callbacks for errors ---------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file defines an API used to indicate fatal error conditions. Non-fatal // errors (most of them) should be handled through LLVMContext. // //===----------------------------------------------------------------------===// #include "llvm/Support/ErrorHandling.h" #include "llvm-c/ErrorHandling.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Twine.h" #include "llvm/Config/config.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Errc.h" #include "llvm/Support/Error.h" #include "llvm/Support/Signals.h" #include "llvm/Support/Threading.h" #include "llvm/Support/WindowsError.h" #include "llvm/Support/raw_ostream.h" #include #include #include #include #include // XXX BINARYEN #if defined(HAVE_UNISTD_H) # include #endif #if defined(_MSC_VER) # include # include #endif using namespace llvm; static fatal_error_handler_t ErrorHandler = nullptr; static void *ErrorHandlerUserData = nullptr; static fatal_error_handler_t BadAllocErrorHandler = nullptr; static void *BadAllocErrorHandlerUserData = nullptr; #if LLVM_ENABLE_THREADS == 1 // Mutexes to synchronize installing error handlers and calling error handlers. // Do not use ManagedStatic, or that may allocate memory while attempting to // report an OOM. // // This usage of std::mutex has to be conditionalized behind ifdefs because // of this script: // compiler-rt/lib/sanitizer_common/symbolizer/scripts/build_symbolizer.sh // That script attempts to statically link the LLVM symbolizer library with the // STL and hide all of its symbols with 'opt -internalize'. To reduce size, it // cuts out the threading portions of the hermetic copy of libc++ that it // builds. We can remove these ifdefs if that script goes away. static std::mutex ErrorHandlerMutex; static std::mutex BadAllocErrorHandlerMutex; #endif void llvm::install_fatal_error_handler(fatal_error_handler_t handler, void *user_data) { #if LLVM_ENABLE_THREADS == 1 std::lock_guard Lock(ErrorHandlerMutex); #endif assert(!ErrorHandler && "Error handler already registered!\n"); ErrorHandler = handler; ErrorHandlerUserData = user_data; } void llvm::remove_fatal_error_handler() { #if LLVM_ENABLE_THREADS == 1 std::lock_guard Lock(ErrorHandlerMutex); #endif ErrorHandler = nullptr; ErrorHandlerUserData = nullptr; } void llvm::report_fatal_error(const char *Reason, bool GenCrashDiag) { report_fatal_error(Twine(Reason), GenCrashDiag); } void llvm::report_fatal_error(const std::string &Reason, bool GenCrashDiag) { report_fatal_error(Twine(Reason), GenCrashDiag); } void llvm::report_fatal_error(StringRef Reason, bool GenCrashDiag) { report_fatal_error(Twine(Reason), GenCrashDiag); } void llvm::report_fatal_error(const Twine &Reason, bool GenCrashDiag) { Reason.dump(); #if 0 // XXX BINARYEN llvm::fatal_error_handler_t handler = nullptr; void* handlerData = nullptr; { // Only acquire the mutex while reading the handler, so as not to invoke a // user-supplied callback under a lock. #if LLVM_ENABLE_THREADS == 1 std::lock_guard Lock(ErrorHandlerMutex); #endif handler = ErrorHandler; handlerData = ErrorHandlerUserData; } if (handler) { handler(handlerData, Reason.str(), GenCrashDiag); } else { // Blast the result out to stderr. We don't try hard to make sure this // succeeds (e.g. handling EINTR) and we can't use errs() here because // raw ostreams can call report_fatal_error. SmallVector Buffer; raw_svector_ostream OS(Buffer); OS << "LLVM ERROR: " << Reason << "\n"; ssize_t written = ::write(2, MessageStr.data(), MessageStr.size()); StringRef MessageStr = OS.str(); (void)written; // If something went wrong, we deliberately just give up. } // If we reached here, we are failing ungracefully. Run the interrupt handlers // to make sure any special cleanups get done, in particular that we remove // files registered with RemoveFileOnSignal. sys::RunInterruptHandlers(); #else Reason.dump(); #endif exit(1); } void llvm::install_bad_alloc_error_handler(fatal_error_handler_t handler, void *user_data) { #if LLVM_ENABLE_THREADS == 1 std::lock_guard Lock(BadAllocErrorHandlerMutex); #endif assert(!ErrorHandler && "Bad alloc error handler already registered!\n"); BadAllocErrorHandler = handler; BadAllocErrorHandlerUserData = user_data; } void llvm::remove_bad_alloc_error_handler() { #if LLVM_ENABLE_THREADS == 1 std::lock_guard Lock(BadAllocErrorHandlerMutex); #endif BadAllocErrorHandler = nullptr; BadAllocErrorHandlerUserData = nullptr; } void llvm::report_bad_alloc_error(const char *Reason, bool GenCrashDiag) { fatal_error_handler_t Handler = nullptr; void *HandlerData = nullptr; { // Only acquire the mutex while reading the handler, so as not to invoke a // user-supplied callback under a lock. #if LLVM_ENABLE_THREADS == 1 std::lock_guard Lock(BadAllocErrorHandlerMutex); #endif Handler = BadAllocErrorHandler; HandlerData = BadAllocErrorHandlerUserData; } if (Handler) { Handler(HandlerData, Reason, GenCrashDiag); llvm_unreachable("bad alloc handler should not return"); } #ifdef LLVM_ENABLE_EXCEPTIONS // If exceptions are enabled, make OOM in malloc look like OOM in new. throw std::bad_alloc(); #else // Don't call the normal error handler. It may allocate memory. Directly write // an OOM to stderr and abort. char OOMMessage[] = "LLVM ERROR: out of memory\n"; ssize_t written = ::write(2, OOMMessage, strlen(OOMMessage)); (void)written; abort(); #endif } #ifdef LLVM_ENABLE_EXCEPTIONS // Do not set custom new handler if exceptions are enabled. In this case OOM // errors are handled by throwing 'std::bad_alloc'. void llvm::install_out_of_memory_new_handler() { } #else // Causes crash on allocation failure. It is called prior to the handler set by // 'install_bad_alloc_error_handler'. static void out_of_memory_new_handler() { llvm::report_bad_alloc_error("Allocation failed"); } // Installs new handler that causes crash on allocation failure. It is called by // InitLLVM. void llvm::install_out_of_memory_new_handler() { std::new_handler old = std::set_new_handler(out_of_memory_new_handler); (void)old; assert(old == nullptr && "new-handler already installed"); } #endif void llvm::llvm_unreachable_internal(const char *msg, const char *file, unsigned line) { // This code intentionally doesn't call the ErrorHandler callback, because // llvm_unreachable is intended to be used to indicate "impossible" // situations, and not legitimate runtime errors. // XXX BINARYEN: use cout if (msg) std::cout << msg << "\n"; std::cout << "UNREACHABLE executed"; if (file) std::cout << " at " << file << ":" << line; std::cout << "!\n"; abort(); #ifdef LLVM_BUILTIN_UNREACHABLE // Windows systems and possibly others don't declare abort() to be noreturn, // so use the unreachable builtin to avoid a Clang self-host warning. LLVM_BUILTIN_UNREACHABLE; #endif } static void bindingsErrorHandler(void *user_data, const std::string& reason, bool gen_crash_diag) { LLVMFatalErrorHandler handler = LLVM_EXTENSION reinterpret_cast(user_data); handler(reason.c_str()); } void LLVMInstallFatalErrorHandler(LLVMFatalErrorHandler Handler) { install_fatal_error_handler(bindingsErrorHandler, LLVM_EXTENSION reinterpret_cast(Handler)); } void LLVMResetFatalErrorHandler() { remove_fatal_error_handler(); } #ifdef _WIN32 #include // I'd rather not double the line count of the following. #define MAP_ERR_TO_COND(x, y) \ case x: \ return make_error_code(errc::y) std::error_code llvm::mapWindowsError(unsigned EV) { switch (EV) { MAP_ERR_TO_COND(ERROR_ACCESS_DENIED, permission_denied); MAP_ERR_TO_COND(ERROR_ALREADY_EXISTS, file_exists); MAP_ERR_TO_COND(ERROR_BAD_UNIT, no_such_device); MAP_ERR_TO_COND(ERROR_BUFFER_OVERFLOW, filename_too_long); MAP_ERR_TO_COND(ERROR_BUSY, device_or_resource_busy); MAP_ERR_TO_COND(ERROR_BUSY_DRIVE, device_or_resource_busy); MAP_ERR_TO_COND(ERROR_CANNOT_MAKE, permission_denied); MAP_ERR_TO_COND(ERROR_CANTOPEN, io_error); MAP_ERR_TO_COND(ERROR_CANTREAD, io_error); MAP_ERR_TO_COND(ERROR_CANTWRITE, io_error); MAP_ERR_TO_COND(ERROR_CURRENT_DIRECTORY, permission_denied); MAP_ERR_TO_COND(ERROR_DEV_NOT_EXIST, no_such_device); MAP_ERR_TO_COND(ERROR_DEVICE_IN_USE, device_or_resource_busy); MAP_ERR_TO_COND(ERROR_DIR_NOT_EMPTY, directory_not_empty); MAP_ERR_TO_COND(ERROR_DIRECTORY, invalid_argument); MAP_ERR_TO_COND(ERROR_DISK_FULL, no_space_on_device); MAP_ERR_TO_COND(ERROR_FILE_EXISTS, file_exists); MAP_ERR_TO_COND(ERROR_FILE_NOT_FOUND, no_such_file_or_directory); MAP_ERR_TO_COND(ERROR_HANDLE_DISK_FULL, no_space_on_device); MAP_ERR_TO_COND(ERROR_INVALID_ACCESS, permission_denied); MAP_ERR_TO_COND(ERROR_INVALID_DRIVE, no_such_device); MAP_ERR_TO_COND(ERROR_INVALID_FUNCTION, function_not_supported); MAP_ERR_TO_COND(ERROR_INVALID_HANDLE, invalid_argument); MAP_ERR_TO_COND(ERROR_INVALID_NAME, invalid_argument); MAP_ERR_TO_COND(ERROR_LOCK_VIOLATION, no_lock_available); MAP_ERR_TO_COND(ERROR_LOCKED, no_lock_available); MAP_ERR_TO_COND(ERROR_NEGATIVE_SEEK, invalid_argument); MAP_ERR_TO_COND(ERROR_NOACCESS, permission_denied); MAP_ERR_TO_COND(ERROR_NOT_ENOUGH_MEMORY, not_enough_memory); MAP_ERR_TO_COND(ERROR_NOT_READY, resource_unavailable_try_again); MAP_ERR_TO_COND(ERROR_OPEN_FAILED, io_error); MAP_ERR_TO_COND(ERROR_OPEN_FILES, device_or_resource_busy); MAP_ERR_TO_COND(ERROR_OUTOFMEMORY, not_enough_memory); MAP_ERR_TO_COND(ERROR_PATH_NOT_FOUND, no_such_file_or_directory); MAP_ERR_TO_COND(ERROR_BAD_NETPATH, no_such_file_or_directory); MAP_ERR_TO_COND(ERROR_READ_FAULT, io_error); MAP_ERR_TO_COND(ERROR_RETRY, resource_unavailable_try_again); MAP_ERR_TO_COND(ERROR_SEEK, io_error); MAP_ERR_TO_COND(ERROR_SHARING_VIOLATION, permission_denied); MAP_ERR_TO_COND(ERROR_TOO_MANY_OPEN_FILES, too_many_files_open); MAP_ERR_TO_COND(ERROR_WRITE_FAULT, io_error); MAP_ERR_TO_COND(ERROR_WRITE_PROTECT, permission_denied); MAP_ERR_TO_COND(WSAEACCES, permission_denied); MAP_ERR_TO_COND(WSAEBADF, bad_file_descriptor); MAP_ERR_TO_COND(WSAEFAULT, bad_address); MAP_ERR_TO_COND(WSAEINTR, interrupted); MAP_ERR_TO_COND(WSAEINVAL, invalid_argument); MAP_ERR_TO_COND(WSAEMFILE, too_many_files_open); MAP_ERR_TO_COND(WSAENAMETOOLONG, filename_too_long); default: return std::error_code(EV, std::system_category()); } } #endif binaryen-version_91/third_party/llvm-project/FormatVariadic.cpp000066400000000000000000000122201362402614000252220ustar00rootroot00000000000000//===- FormatVariadic.cpp - Format string parsing and analysis ----*-C++-*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception //===----------------------------------------------------------------------===// #include "llvm/Support/FormatVariadic.h" using namespace llvm; static Optional translateLocChar(char C) { switch (C) { case '-': return AlignStyle::Left; case '=': return AlignStyle::Center; case '+': return AlignStyle::Right; default: return None; } LLVM_BUILTIN_UNREACHABLE; } bool formatv_object_base::consumeFieldLayout(StringRef &Spec, AlignStyle &Where, size_t &Align, char &Pad) { Where = AlignStyle::Right; Align = 0; Pad = ' '; if (Spec.empty()) return true; if (Spec.size() > 1) { // A maximum of 2 characters at the beginning can be used for something // other // than the width. // If Spec[1] is a loc char, then Spec[0] is a pad char and Spec[2:...] // contains the width. // Otherwise, if Spec[0] is a loc char, then Spec[1:...] contains the width. // Otherwise, Spec[0:...] contains the width. if (auto Loc = translateLocChar(Spec[1])) { Pad = Spec[0]; Where = *Loc; Spec = Spec.drop_front(2); } else if (auto Loc = translateLocChar(Spec[0])) { Where = *Loc; Spec = Spec.drop_front(1); } } bool Failed = Spec.consumeInteger(0, Align); return !Failed; } Optional formatv_object_base::parseReplacementItem(StringRef Spec) { StringRef RepString = Spec.trim("{}"); // If the replacement sequence does not start with a non-negative integer, // this is an error. char Pad = ' '; std::size_t Align = 0; AlignStyle Where = AlignStyle::Right; StringRef Options; size_t Index = 0; RepString = RepString.trim(); if (RepString.consumeInteger(0, Index)) { assert(false && "Invalid replacement sequence index!"); return ReplacementItem{}; } RepString = RepString.trim(); if (!RepString.empty() && RepString.front() == ',') { RepString = RepString.drop_front(); if (!consumeFieldLayout(RepString, Where, Align, Pad)) assert(false && "Invalid replacement field layout specification!"); } RepString = RepString.trim(); if (!RepString.empty() && RepString.front() == ':') { Options = RepString.drop_front().trim(); RepString = StringRef(); } RepString = RepString.trim(); if (!RepString.empty()) { assert(false && "Unexpected characters found in replacement string!"); } return ReplacementItem{Spec, Index, Align, Where, Pad, Options}; } std::pair formatv_object_base::splitLiteralAndReplacement(StringRef Fmt) { std::size_t From = 0; while (From < Fmt.size() && From != StringRef::npos) { std::size_t BO = Fmt.find_first_of('{', From); // Everything up until the first brace is a literal. if (BO != 0) return std::make_pair(ReplacementItem{Fmt.substr(0, BO)}, Fmt.substr(BO)); StringRef Braces = Fmt.drop_front(BO).take_while([](char C) { return C == '{'; }); // If there is more than one brace, then some of them are escaped. Treat // these as replacements. if (Braces.size() > 1) { size_t NumEscapedBraces = Braces.size() / 2; StringRef Middle = Fmt.substr(BO, NumEscapedBraces); StringRef Right = Fmt.drop_front(BO + NumEscapedBraces * 2); return std::make_pair(ReplacementItem{Middle}, Right); } // An unterminated open brace is undefined. We treat the rest of the string // as a literal replacement, but we assert to indicate that this is // undefined and that we consider it an error. std::size_t BC = Fmt.find_first_of('}', BO); if (BC == StringRef::npos) { assert( false && "Unterminated brace sequence. Escape with {{ for a literal brace."); return std::make_pair(ReplacementItem{Fmt}, StringRef()); } // Even if there is a closing brace, if there is another open brace before // this closing brace, treat this portion as literal, and try again with the // next one. std::size_t BO2 = Fmt.find_first_of('{', BO + 1); if (BO2 < BC) return std::make_pair(ReplacementItem{Fmt.substr(0, BO2)}, Fmt.substr(BO2)); StringRef Spec = Fmt.slice(BO + 1, BC); StringRef Right = Fmt.substr(BC + 1); auto RI = parseReplacementItem(Spec); if (RI.hasValue()) return std::make_pair(*RI, Right); // If there was an error parsing the replacement item, treat it as an // invalid replacement spec, and just continue. From = BC + 1; } return std::make_pair(ReplacementItem{Fmt}, StringRef()); } std::vector formatv_object_base::parseFormatString(StringRef Fmt) { std::vector Replacements; ReplacementItem I; while (!Fmt.empty()) { std::tie(I, Fmt) = splitLiteralAndReplacement(Fmt); if (I.Type != ReplacementType::Empty) Replacements.push_back(I); } return Replacements; } void detail::format_adapter::anchor() { } binaryen-version_91/third_party/llvm-project/Hashing.cpp000066400000000000000000000022421362402614000237130ustar00rootroot00000000000000//===-------------- lib/Support/Hashing.cpp -------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file provides implementation bits for the LLVM common hashing // infrastructure. Documentation and most of the other information is in the // header file. // //===----------------------------------------------------------------------===// #include "llvm/ADT/Hashing.h" using namespace llvm; // Provide a definition and static initializer for the fixed seed. This // initializer should always be zero to ensure its value can never appear to be // non-zero, even during dynamic initialization. uint64_t llvm::hashing::detail::fixed_seed_override = 0; // Implement the function for forced setting of the fixed seed. // FIXME: Use atomic operations here so that there is no data race. void llvm::set_fixed_execution_hash_seed(uint64_t fixed_value) { hashing::detail::fixed_seed_override = fixed_value; } binaryen-version_91/third_party/llvm-project/LEB128.cpp000066400000000000000000000022771362402614000231770ustar00rootroot00000000000000//===- LEB128.cpp - LEB128 utility functions implementation -----*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file implements some utility functions for encoding SLEB128 and // ULEB128 values. // //===----------------------------------------------------------------------===// #include "llvm/Support/LEB128.h" namespace llvm { /// Utility function to get the size of the ULEB128-encoded value. unsigned getULEB128Size(uint64_t Value) { unsigned Size = 0; do { Value >>= 7; Size += sizeof(int8_t); } while (Value); return Size; } /// Utility function to get the size of the SLEB128-encoded value. unsigned getSLEB128Size(int64_t Value) { unsigned Size = 0; int Sign = Value >> (8 * sizeof(Value) - 1); bool IsMore; do { unsigned Byte = Value & 0x7f; Value >>= 7; IsMore = Value != Sign || ((Byte ^ Sign) & 0x40) != 0; Size += sizeof(int8_t); } while (IsMore); return Size; } } // namespace llvm binaryen-version_91/third_party/llvm-project/LineIterator.cpp000066400000000000000000000051651362402614000247420ustar00rootroot00000000000000//===- LineIterator.cpp - Implementation of line iteration ----------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/Support/LineIterator.h" #include "llvm/Support/MemoryBuffer.h" using namespace llvm; static bool isAtLineEnd(const char *P) { if (*P == '\n') return true; if (*P == '\r' && *(P + 1) == '\n') return true; return false; } static bool skipIfAtLineEnd(const char *&P) { if (*P == '\n') { ++P; return true; } if (*P == '\r' && *(P + 1) == '\n') { P += 2; return true; } return false; } line_iterator::line_iterator(const MemoryBuffer &Buffer, bool SkipBlanks, char CommentMarker) : Buffer(Buffer.getBufferSize() ? &Buffer : nullptr), CommentMarker(CommentMarker), SkipBlanks(SkipBlanks), LineNumber(1), CurrentLine(Buffer.getBufferSize() ? Buffer.getBufferStart() : nullptr, 0) { // Ensure that if we are constructed on a non-empty memory buffer that it is // a null terminated buffer. if (Buffer.getBufferSize()) { assert(Buffer.getBufferEnd()[0] == '\0'); // Make sure we don't skip a leading newline if we're keeping blanks if (SkipBlanks || !isAtLineEnd(Buffer.getBufferStart())) advance(); } } void line_iterator::advance() { assert(Buffer && "Cannot advance past the end!"); const char *Pos = CurrentLine.end(); assert(Pos == Buffer->getBufferStart() || isAtLineEnd(Pos) || *Pos == '\0'); if (skipIfAtLineEnd(Pos)) ++LineNumber; if (!SkipBlanks && isAtLineEnd(Pos)) { // Nothing to do for a blank line. } else if (CommentMarker == '\0') { // If we're not stripping comments, this is simpler. while (skipIfAtLineEnd(Pos)) ++LineNumber; } else { // Skip comments and count line numbers, which is a bit more complex. for (;;) { if (isAtLineEnd(Pos) && !SkipBlanks) break; if (*Pos == CommentMarker) do { ++Pos; } while (*Pos != '\0' && !isAtLineEnd(Pos)); if (!skipIfAtLineEnd(Pos)) break; ++LineNumber; } } if (*Pos == '\0') { // We've hit the end of the buffer, reset ourselves to the end state. Buffer = nullptr; CurrentLine = StringRef(); return; } // Measure the line. size_t Length = 0; while (Pos[Length] != '\0' && !isAtLineEnd(&Pos[Length])) { ++Length; } CurrentLine = StringRef(Pos, Length); } binaryen-version_91/third_party/llvm-project/MCRegisterInfo.cpp000066400000000000000000000114061362402614000251540ustar00rootroot00000000000000//===- MC/MCRegisterInfo.cpp - Target Register Description ----------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file implements MCRegisterInfo functions. // //===----------------------------------------------------------------------===// #include "llvm/MC/MCRegisterInfo.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Twine.h" #include "llvm/Support/ErrorHandling.h" #include #include #include using namespace llvm; MCRegister MCRegisterInfo::getMatchingSuperReg(MCRegister Reg, unsigned SubIdx, const MCRegisterClass *RC) const { for (MCSuperRegIterator Supers(Reg, this); Supers.isValid(); ++Supers) if (RC->contains(*Supers) && Reg == getSubReg(*Supers, SubIdx)) return *Supers; return 0; } MCRegister MCRegisterInfo::getSubReg(MCRegister Reg, unsigned Idx) const { assert(Idx && Idx < getNumSubRegIndices() && "This is not a subregister index"); // Get a pointer to the corresponding SubRegIndices list. This list has the // name of each sub-register in the same order as MCSubRegIterator. const uint16_t *SRI = SubRegIndices + get(Reg).SubRegIndices; for (MCSubRegIterator Subs(Reg, this); Subs.isValid(); ++Subs, ++SRI) if (*SRI == Idx) return *Subs; return 0; } unsigned MCRegisterInfo::getSubRegIndex(MCRegister Reg, MCRegister SubReg) const { assert(SubReg && SubReg < getNumRegs() && "This is not a register"); // Get a pointer to the corresponding SubRegIndices list. This list has the // name of each sub-register in the same order as MCSubRegIterator. const uint16_t *SRI = SubRegIndices + get(Reg).SubRegIndices; for (MCSubRegIterator Subs(Reg, this); Subs.isValid(); ++Subs, ++SRI) if (*Subs == SubReg) return *SRI; return 0; } unsigned MCRegisterInfo::getSubRegIdxSize(unsigned Idx) const { assert(Idx && Idx < getNumSubRegIndices() && "This is not a subregister index"); return SubRegIdxRanges[Idx].Size; } unsigned MCRegisterInfo::getSubRegIdxOffset(unsigned Idx) const { assert(Idx && Idx < getNumSubRegIndices() && "This is not a subregister index"); return SubRegIdxRanges[Idx].Offset; } int MCRegisterInfo::getDwarfRegNum(MCRegister RegNum, bool isEH) const { const DwarfLLVMRegPair *M = isEH ? EHL2DwarfRegs : L2DwarfRegs; unsigned Size = isEH ? EHL2DwarfRegsSize : L2DwarfRegsSize; if (!M) return -1; DwarfLLVMRegPair Key = { RegNum, 0 }; const DwarfLLVMRegPair *I = std::lower_bound(M, M+Size, Key); if (I == M+Size || I->FromReg != RegNum) return -1; return I->ToReg; } Optional MCRegisterInfo::getLLVMRegNum(unsigned RegNum, bool isEH) const { const DwarfLLVMRegPair *M = isEH ? EHDwarf2LRegs : Dwarf2LRegs; unsigned Size = isEH ? EHDwarf2LRegsSize : Dwarf2LRegsSize; if (!M) return None; DwarfLLVMRegPair Key = { RegNum, 0 }; const DwarfLLVMRegPair *I = std::lower_bound(M, M+Size, Key); if (I != M + Size && I->FromReg == RegNum) return I->ToReg; return None; } int MCRegisterInfo::getDwarfRegNumFromDwarfEHRegNum(unsigned RegNum) const { // On ELF platforms, DWARF EH register numbers are the same as DWARF // other register numbers. On Darwin x86, they differ and so need to be // mapped. The .cfi_* directives accept integer literals as well as // register names and should generate exactly what the assembly code // asked for, so there might be DWARF/EH register numbers that don't have // a corresponding LLVM register number at all. So if we can't map the // EH register number to an LLVM register number, assume it's just a // valid DWARF register number as is. if (Optional LRegNum = getLLVMRegNum(RegNum, true)) return getDwarfRegNum(*LRegNum, false); return RegNum; } int MCRegisterInfo::getSEHRegNum(MCRegister RegNum) const { const DenseMap::const_iterator I = L2SEHRegs.find(RegNum); if (I == L2SEHRegs.end()) return (int)RegNum; return I->second; } int MCRegisterInfo::getCodeViewRegNum(MCRegister RegNum) const { if (L2CVRegs.empty()) report_fatal_error("target does not implement codeview register mapping"); const DenseMap::const_iterator I = L2CVRegs.find(RegNum); if (I == L2CVRegs.end()) report_fatal_error("unknown codeview register " + (RegNum < getNumRegs() ? getName(RegNum) : Twine(RegNum))); return I->second; } binaryen-version_91/third_party/llvm-project/MD5.cpp000066400000000000000000000217251362402614000227260ustar00rootroot00000000000000/* * This code is derived from (original license follows): * * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. * MD5 Message-Digest Algorithm (RFC 1321). * * Homepage: * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 * * Author: * Alexander Peslyak, better known as Solar Designer * * This software was written by Alexander Peslyak in 2001. No copyright is * claimed, and the software is hereby placed in the public domain. * In case this attempt to disclaim copyright and place the software in the * public domain is deemed null and void, then the software is * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the * general public under the following terms: * * Redistribution and use in source and binary forms, with or without * modification, are permitted. * * There's ABSOLUTELY NO WARRANTY, express or implied. * * (This is a heavily cut-down "BSD license".) * * This differs from Colin Plumb's older public domain implementation in that * no exactly 32-bit integer data type is required (any 32-bit or wider * unsigned integer data type will do), there's no compile-time endianness * configuration, and the function prototypes match OpenSSL's. No code from * Colin Plumb's implementation has been reused; this comment merely compares * the properties of the two independent implementations. * * The primary goals of this implementation are portability and ease of use. * It is meant to be fast, but not as fast as possible. Some known * optimizations are not included to reduce source code size and avoid * compile-time configuration. */ #include "llvm/Support/MD5.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Format.h" #include "llvm/Support/raw_ostream.h" #include #include #include // The basic MD5 functions. // F and G are optimized compared to their RFC 1321 definitions for // architectures that lack an AND-NOT instruction, just like in Colin Plumb's // implementation. #define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) #define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y)))) #define H(x, y, z) ((x) ^ (y) ^ (z)) #define I(x, y, z) ((y) ^ ((x) | ~(z))) // The MD5 transformation for all four rounds. #define STEP(f, a, b, c, d, x, t, s) \ (a) += f((b), (c), (d)) + (x) + (t); \ (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \ (a) += (b); // SET reads 4 input bytes in little-endian byte order and stores them // in a properly aligned word in host byte order. #define SET(n) \ (block[(n)] = \ (MD5_u32plus) ptr[(n) * 4] | ((MD5_u32plus) ptr[(n) * 4 + 1] << 8) | \ ((MD5_u32plus) ptr[(n) * 4 + 2] << 16) | \ ((MD5_u32plus) ptr[(n) * 4 + 3] << 24)) #define GET(n) (block[(n)]) using namespace llvm; /// This processes one or more 64-byte data blocks, but does NOT update ///the bit counters. There are no alignment requirements. const uint8_t *MD5::body(ArrayRef Data) { const uint8_t *ptr; MD5_u32plus a, b, c, d; MD5_u32plus saved_a, saved_b, saved_c, saved_d; unsigned long Size = Data.size(); ptr = Data.data(); a = this->a; b = this->b; c = this->c; d = this->d; do { saved_a = a; saved_b = b; saved_c = c; saved_d = d; // Round 1 STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7) STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12) STEP(F, c, d, a, b, SET(2), 0x242070db, 17) STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22) STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7) STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12) STEP(F, c, d, a, b, SET(6), 0xa8304613, 17) STEP(F, b, c, d, a, SET(7), 0xfd469501, 22) STEP(F, a, b, c, d, SET(8), 0x698098d8, 7) STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12) STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17) STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22) STEP(F, a, b, c, d, SET(12), 0x6b901122, 7) STEP(F, d, a, b, c, SET(13), 0xfd987193, 12) STEP(F, c, d, a, b, SET(14), 0xa679438e, 17) STEP(F, b, c, d, a, SET(15), 0x49b40821, 22) // Round 2 STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5) STEP(G, d, a, b, c, GET(6), 0xc040b340, 9) STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14) STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20) STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5) STEP(G, d, a, b, c, GET(10), 0x02441453, 9) STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14) STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20) STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5) STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9) STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14) STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20) STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5) STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9) STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14) STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20) // Round 3 STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4) STEP(H, d, a, b, c, GET(8), 0x8771f681, 11) STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16) STEP(H, b, c, d, a, GET(14), 0xfde5380c, 23) STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4) STEP(H, d, a, b, c, GET(4), 0x4bdecfa9, 11) STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16) STEP(H, b, c, d, a, GET(10), 0xbebfbc70, 23) STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4) STEP(H, d, a, b, c, GET(0), 0xeaa127fa, 11) STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16) STEP(H, b, c, d, a, GET(6), 0x04881d05, 23) STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4) STEP(H, d, a, b, c, GET(12), 0xe6db99e5, 11) STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16) STEP(H, b, c, d, a, GET(2), 0xc4ac5665, 23) // Round 4 STEP(I, a, b, c, d, GET(0), 0xf4292244, 6) STEP(I, d, a, b, c, GET(7), 0x432aff97, 10) STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15) STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21) STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6) STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10) STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15) STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21) STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6) STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10) STEP(I, c, d, a, b, GET(6), 0xa3014314, 15) STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21) STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6) STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10) STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15) STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21) a += saved_a; b += saved_b; c += saved_c; d += saved_d; ptr += 64; } while (Size -= 64); this->a = a; this->b = b; this->c = c; this->d = d; return ptr; } MD5::MD5() = default; /// Incrementally add the bytes in \p Data to the hash. void MD5::update(ArrayRef Data) { MD5_u32plus saved_lo; unsigned long used, free; const uint8_t *Ptr = Data.data(); unsigned long Size = Data.size(); saved_lo = lo; if ((lo = (saved_lo + Size) & 0x1fffffff) < saved_lo) hi++; hi += Size >> 29; used = saved_lo & 0x3f; if (used) { free = 64 - used; if (Size < free) { memcpy(&buffer[used], Ptr, Size); return; } memcpy(&buffer[used], Ptr, free); Ptr = Ptr + free; Size -= free; body(makeArrayRef(buffer, 64)); } if (Size >= 64) { Ptr = body(makeArrayRef(Ptr, Size & ~(unsigned long) 0x3f)); Size &= 0x3f; } memcpy(buffer, Ptr, Size); } /// Add the bytes in the StringRef \p Str to the hash. // Note that this isn't a string and so this won't include any trailing NULL // bytes. void MD5::update(StringRef Str) { ArrayRef SVal((const uint8_t *)Str.data(), Str.size()); update(SVal); } /// Finish the hash and place the resulting hash into \p result. /// \param Result is assumed to be a minimum of 16-bytes in size. void MD5::final(MD5Result &Result) { unsigned long used, free; used = lo & 0x3f; buffer[used++] = 0x80; free = 64 - used; if (free < 8) { memset(&buffer[used], 0, free); body(makeArrayRef(buffer, 64)); used = 0; free = 64; } memset(&buffer[used], 0, free - 8); lo <<= 3; support::endian::write32le(&buffer[56], lo); support::endian::write32le(&buffer[60], hi); body(makeArrayRef(buffer, 64)); support::endian::write32le(&Result[0], a); support::endian::write32le(&Result[4], b); support::endian::write32le(&Result[8], c); support::endian::write32le(&Result[12], d); } SmallString<32> MD5::MD5Result::digest() const { SmallString<32> Str; raw_svector_ostream Res(Str); for (int i = 0; i < 16; ++i) Res << format("%.2x", Bytes[i]); return Str; } void MD5::stringifyResult(MD5Result &Result, SmallString<32> &Str) { Str = Result.digest(); } std::array MD5::hash(ArrayRef Data) { MD5 Hash; Hash.update(Data); MD5::MD5Result Res; Hash.final(Res); return Res; } binaryen-version_91/third_party/llvm-project/MemoryBuffer.cpp000066400000000000000000000310551362402614000247400ustar00rootroot00000000000000//===--- MemoryBuffer.cpp - Memory Buffer implementation ------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file implements the MemoryBuffer interface. // //===----------------------------------------------------------------------===// #include "llvm/Support/MemoryBuffer.h" #include "llvm/ADT/SmallString.h" #include "llvm/Config/config.h" #include "llvm/Support/Errc.h" #include "llvm/Support/Errno.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/Path.h" #include "llvm/Support/Process.h" #include "llvm/Support/Program.h" #include "llvm/Support/SmallVectorMemoryBuffer.h" #include #include #include #include #include #include #if !defined(_MSC_VER) && !defined(__MINGW32__) #include #else #include #endif using namespace llvm; //===----------------------------------------------------------------------===// // MemoryBuffer implementation itself. //===----------------------------------------------------------------------===// MemoryBuffer::~MemoryBuffer() { } /// init - Initialize this MemoryBuffer as a reference to externally allocated /// memory, memory that we know is already null terminated. void MemoryBuffer::init(const char *BufStart, const char *BufEnd, bool RequiresNullTerminator) { assert((!RequiresNullTerminator || BufEnd[0] == 0) && "Buffer is not null terminated!"); BufferStart = BufStart; BufferEnd = BufEnd; } //===----------------------------------------------------------------------===// // MemoryBufferMem implementation. //===----------------------------------------------------------------------===// /// CopyStringRef - Copies contents of a StringRef into a block of memory and /// null-terminates it. static void CopyStringRef(char *Memory, StringRef Data) { if (!Data.empty()) memcpy(Memory, Data.data(), Data.size()); Memory[Data.size()] = 0; // Null terminate string. } namespace { struct NamedBufferAlloc { const Twine &Name; NamedBufferAlloc(const Twine &Name) : Name(Name) {} }; } void *operator new(size_t N, const NamedBufferAlloc &Alloc) { SmallString<256> NameBuf; StringRef NameRef = Alloc.Name.toStringRef(NameBuf); char *Mem = static_cast(operator new(N + NameRef.size() + 1)); CopyStringRef(Mem + N, NameRef); return Mem; } namespace { /// MemoryBufferMem - Named MemoryBuffer pointing to a block of memory. template class MemoryBufferMem : public MB { public: MemoryBufferMem(StringRef InputData, bool RequiresNullTerminator) { MemoryBuffer::init(InputData.begin(), InputData.end(), RequiresNullTerminator); } /// Disable sized deallocation for MemoryBufferMem, because it has /// tail-allocated data. void operator delete(void *p) { ::operator delete(p); } StringRef getBufferIdentifier() const override { // The name is stored after the class itself. return StringRef(reinterpret_cast(this + 1)); } MemoryBuffer::BufferKind getBufferKind() const override { return MemoryBuffer::MemoryBuffer_Malloc; } }; } template static ErrorOr> getFileAux(const Twine &Filename, int64_t FileSize, uint64_t MapSize, uint64_t Offset, bool RequiresNullTerminator, bool IsVolatile); std::unique_ptr MemoryBuffer::getMemBuffer(StringRef InputData, StringRef BufferName, bool RequiresNullTerminator) { auto *Ret = new (NamedBufferAlloc(BufferName)) MemoryBufferMem(InputData, RequiresNullTerminator); return std::unique_ptr(Ret); } std::unique_ptr MemoryBuffer::getMemBuffer(MemoryBufferRef Ref, bool RequiresNullTerminator) { return std::unique_ptr(getMemBuffer( Ref.getBuffer(), Ref.getBufferIdentifier(), RequiresNullTerminator)); } static ErrorOr> getMemBufferCopyImpl(StringRef InputData, const Twine &BufferName) { auto Buf = WritableMemoryBuffer::getNewUninitMemBuffer(InputData.size(), BufferName); if (!Buf) return make_error_code(errc::not_enough_memory); memcpy(Buf->getBufferStart(), InputData.data(), InputData.size()); return std::move(Buf); } std::unique_ptr MemoryBuffer::getMemBufferCopy(StringRef InputData, const Twine &BufferName) { auto Buf = getMemBufferCopyImpl(InputData, BufferName); if (Buf) return std::move(*Buf); return nullptr; } ErrorOr> MemoryBuffer::getFileOrSTDIN(const Twine &Filename, int64_t FileSize, bool RequiresNullTerminator) { SmallString<256> NameBuf; StringRef NameRef = Filename.toStringRef(NameBuf); if (NameRef == "-") return getSTDIN(); return getFile(Filename, FileSize, RequiresNullTerminator); } ErrorOr> MemoryBuffer::getFileSlice(const Twine &FilePath, uint64_t MapSize, uint64_t Offset, bool IsVolatile) { return getFileAux(FilePath, -1, MapSize, Offset, false, IsVolatile); } //===----------------------------------------------------------------------===// // MemoryBuffer::getFile implementation. //===----------------------------------------------------------------------===// #if 0 // XXX BINARYEN namespace { /// Memory maps a file descriptor using sys::fs::mapped_file_region. /// /// This handles converting the offset into a legal offset on the platform. template class MemoryBufferMMapFile : public MB { sys::fs::mapped_file_region MFR; static uint64_t getLegalMapOffset(uint64_t Offset) { return Offset & ~(sys::fs::mapped_file_region::alignment() - 1); } static uint64_t getLegalMapSize(uint64_t Len, uint64_t Offset) { return Len + (Offset - getLegalMapOffset(Offset)); } const char *getStart(uint64_t Len, uint64_t Offset) { return MFR.const_data() + (Offset - getLegalMapOffset(Offset)); } public: MemoryBufferMMapFile(bool RequiresNullTerminator, sys::fs::file_t FD, uint64_t Len, uint64_t Offset, std::error_code &EC) : MFR(FD, MB::Mapmode, getLegalMapSize(Len, Offset), getLegalMapOffset(Offset), EC) { if (!EC) { const char *Start = getStart(Len, Offset); MemoryBuffer::init(Start, Start + Len, RequiresNullTerminator); } } /// Disable sized deallocation for MemoryBufferMMapFile, because it has /// tail-allocated data. void operator delete(void *p) { ::operator delete(p); } StringRef getBufferIdentifier() const override { // The name is stored after the class itself. return StringRef(reinterpret_cast(this + 1)); } MemoryBuffer::BufferKind getBufferKind() const override { return MemoryBuffer::MemoryBuffer_MMap; } }; } #endif static ErrorOr> getMemoryBufferForStream(sys::fs::file_t FD, const Twine &BufferName) { llvm_unreachable("getMemoryBufferForStream"); } ErrorOr> MemoryBuffer::getFile(const Twine &Filename, int64_t FileSize, bool RequiresNullTerminator, bool IsVolatile) { return getFileAux(Filename, FileSize, FileSize, 0, RequiresNullTerminator, IsVolatile); } template static ErrorOr> getOpenFileImpl(sys::fs::file_t FD, const Twine &Filename, uint64_t FileSize, uint64_t MapSize, int64_t Offset, bool RequiresNullTerminator, bool IsVolatile); template static ErrorOr> getFileAux(const Twine &Filename, int64_t FileSize, uint64_t MapSize, uint64_t Offset, bool RequiresNullTerminator, bool IsVolatile) { llvm_unreachable("getFileAux"); } ErrorOr> WritableMemoryBuffer::getFile(const Twine &Filename, int64_t FileSize, bool IsVolatile) { return getFileAux(Filename, FileSize, FileSize, 0, /*RequiresNullTerminator*/ false, IsVolatile); } ErrorOr> WritableMemoryBuffer::getFileSlice(const Twine &Filename, uint64_t MapSize, uint64_t Offset, bool IsVolatile) { return getFileAux(Filename, -1, MapSize, Offset, false, IsVolatile); } std::unique_ptr WritableMemoryBuffer::getNewUninitMemBuffer(size_t Size, const Twine &BufferName) { using MemBuffer = MemoryBufferMem; // Allocate space for the MemoryBuffer, the data and the name. It is important // that MemoryBuffer and data are aligned so PointerIntPair works with them. // TODO: Is 16-byte alignment enough? We copy small object files with large // alignment expectations into this buffer. SmallString<256> NameBuf; StringRef NameRef = BufferName.toStringRef(NameBuf); size_t AlignedStringLen = alignTo(sizeof(MemBuffer) + NameRef.size() + 1, 16); size_t RealLen = AlignedStringLen + Size + 1; char *Mem = static_cast(operator new(RealLen, std::nothrow)); if (!Mem) return nullptr; // The name is stored after the class itself. CopyStringRef(Mem + sizeof(MemBuffer), NameRef); // The buffer begins after the name and must be aligned. char *Buf = Mem + AlignedStringLen; Buf[Size] = 0; // Null terminate buffer. auto *Ret = new (Mem) MemBuffer(StringRef(Buf, Size), true); return std::unique_ptr(Ret); } std::unique_ptr WritableMemoryBuffer::getNewMemBuffer(size_t Size, const Twine &BufferName) { auto SB = WritableMemoryBuffer::getNewUninitMemBuffer(Size, BufferName); if (!SB) return nullptr; memset(SB->getBufferStart(), 0, Size); return SB; } static bool shouldUseMmap(sys::fs::file_t FD, size_t FileSize, size_t MapSize, off_t Offset, bool RequiresNullTerminator, int PageSize, bool IsVolatile) { // XXX BINARYEn return false; } static ErrorOr> getReadWriteFile(const Twine &Filename, uint64_t FileSize, uint64_t MapSize, uint64_t Offset) { llvm_unreachable("getReadWriteFile"); } ErrorOr> WriteThroughMemoryBuffer::getFile(const Twine &Filename, int64_t FileSize) { return getReadWriteFile(Filename, FileSize, FileSize, 0); } /// Map a subrange of the specified file as a WritableMemoryBuffer. ErrorOr> WriteThroughMemoryBuffer::getFileSlice(const Twine &Filename, uint64_t MapSize, uint64_t Offset) { return getReadWriteFile(Filename, -1, MapSize, Offset); } template static ErrorOr> getOpenFileImpl(sys::fs::file_t FD, const Twine &Filename, uint64_t FileSize, uint64_t MapSize, int64_t Offset, bool RequiresNullTerminator, bool IsVolatile) { llvm_unreachable("getOpenFileImpl"); } ErrorOr> MemoryBuffer::getOpenFile(sys::fs::file_t FD, const Twine &Filename, uint64_t FileSize, bool RequiresNullTerminator, bool IsVolatile) { return getOpenFileImpl(FD, Filename, FileSize, FileSize, 0, RequiresNullTerminator, IsVolatile); } ErrorOr> MemoryBuffer::getOpenFileSlice(sys::fs::file_t FD, const Twine &Filename, uint64_t MapSize, int64_t Offset, bool IsVolatile) { assert(MapSize != uint64_t(-1)); return getOpenFileImpl(FD, Filename, -1, MapSize, Offset, false, IsVolatile); } ErrorOr> MemoryBuffer::getSTDIN() { llvm_unreachable("getSTDIN"); } ErrorOr> MemoryBuffer::getFileAsStream(const Twine &Filename) { llvm_unreachable("getFileAsStream"); } MemoryBufferRef MemoryBuffer::getMemBufferRef() const { StringRef Data = getBuffer(); StringRef Identifier = getBufferIdentifier(); return MemoryBufferRef(Data, Identifier); } SmallVectorMemoryBuffer::~SmallVectorMemoryBuffer() {} binaryen-version_91/third_party/llvm-project/NativeFormatting.cpp000066400000000000000000000170171362402614000256210ustar00rootroot00000000000000//===- NativeFormatting.cpp - Low level formatting helpers -------*- C++-*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/Support/NativeFormatting.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/Format.h" #include using namespace llvm; template static int format_to_buffer(T Value, char (&Buffer)[N]) { char *EndPtr = std::end(Buffer); char *CurPtr = EndPtr; do { *--CurPtr = '0' + char(Value % 10); Value /= 10; } while (Value); return EndPtr - CurPtr; } static void writeWithCommas(raw_ostream &S, ArrayRef Buffer) { assert(!Buffer.empty()); ArrayRef ThisGroup; int InitialDigits = ((Buffer.size() - 1) % 3) + 1; ThisGroup = Buffer.take_front(InitialDigits); S.write(ThisGroup.data(), ThisGroup.size()); Buffer = Buffer.drop_front(InitialDigits); assert(Buffer.size() % 3 == 0); while (!Buffer.empty()) { S << ','; ThisGroup = Buffer.take_front(3); S.write(ThisGroup.data(), 3); Buffer = Buffer.drop_front(3); } } template static void write_unsigned_impl(raw_ostream &S, T N, size_t MinDigits, IntegerStyle Style, bool IsNegative) { static_assert(std::is_unsigned::value, "Value is not unsigned!"); char NumberBuffer[128]; std::memset(NumberBuffer, '0', sizeof(NumberBuffer)); size_t Len = 0; Len = format_to_buffer(N, NumberBuffer); if (IsNegative) S << '-'; if (Len < MinDigits && Style != IntegerStyle::Number) { for (size_t I = Len; I < MinDigits; ++I) S << '0'; } if (Style == IntegerStyle::Number) { writeWithCommas(S, ArrayRef(std::end(NumberBuffer) - Len, Len)); } else { S.write(std::end(NumberBuffer) - Len, Len); } } template static void write_unsigned(raw_ostream &S, T N, size_t MinDigits, IntegerStyle Style, bool IsNegative = false) { // Output using 32-bit div/mod if possible. if (N == static_cast(N)) write_unsigned_impl(S, static_cast(N), MinDigits, Style, IsNegative); else write_unsigned_impl(S, N, MinDigits, Style, IsNegative); } template static void write_signed(raw_ostream &S, T N, size_t MinDigits, IntegerStyle Style) { static_assert(std::is_signed::value, "Value is not signed!"); using UnsignedT = typename std::make_unsigned::type; if (N >= 0) { write_unsigned(S, static_cast(N), MinDigits, Style); return; } UnsignedT UN = -(UnsignedT)N; write_unsigned(S, UN, MinDigits, Style, true); } void llvm::write_integer(raw_ostream &S, unsigned int N, size_t MinDigits, IntegerStyle Style) { write_unsigned(S, N, MinDigits, Style); } void llvm::write_integer(raw_ostream &S, int N, size_t MinDigits, IntegerStyle Style) { write_signed(S, N, MinDigits, Style); } void llvm::write_integer(raw_ostream &S, unsigned long N, size_t MinDigits, IntegerStyle Style) { write_unsigned(S, N, MinDigits, Style); } void llvm::write_integer(raw_ostream &S, long N, size_t MinDigits, IntegerStyle Style) { write_signed(S, N, MinDigits, Style); } void llvm::write_integer(raw_ostream &S, unsigned long long N, size_t MinDigits, IntegerStyle Style) { write_unsigned(S, N, MinDigits, Style); } void llvm::write_integer(raw_ostream &S, long long N, size_t MinDigits, IntegerStyle Style) { write_signed(S, N, MinDigits, Style); } void llvm::write_hex(raw_ostream &S, uint64_t N, HexPrintStyle Style, Optional Width) { const size_t kMaxWidth = 128u; size_t W = std::min(kMaxWidth, Width.getValueOr(0u)); unsigned Nibbles = (64 - countLeadingZeros(N) + 3) / 4; bool Prefix = (Style == HexPrintStyle::PrefixLower || Style == HexPrintStyle::PrefixUpper); bool Upper = (Style == HexPrintStyle::Upper || Style == HexPrintStyle::PrefixUpper); unsigned PrefixChars = Prefix ? 2 : 0; unsigned NumChars = std::max(static_cast(W), std::max(1u, Nibbles) + PrefixChars); char NumberBuffer[kMaxWidth]; ::memset(NumberBuffer, '0', llvm::array_lengthof(NumberBuffer)); if (Prefix) NumberBuffer[1] = 'x'; char *EndPtr = NumberBuffer + NumChars; char *CurPtr = EndPtr; while (N) { unsigned char x = static_cast(N) % 16; *--CurPtr = hexdigit(x, !Upper); N /= 16; } S.write(NumberBuffer, NumChars); } void llvm::write_double(raw_ostream &S, double N, FloatStyle Style, Optional Precision) { size_t Prec = Precision.getValueOr(getDefaultPrecision(Style)); if (std::isnan(N)) { S << "nan"; return; } else if (std::isinf(N)) { S << "INF"; return; } char Letter; if (Style == FloatStyle::Exponent) Letter = 'e'; else if (Style == FloatStyle::ExponentUpper) Letter = 'E'; else Letter = 'f'; SmallString<8> Spec; llvm::raw_svector_ostream Out(Spec); Out << "%." << Prec << Letter; if (Style == FloatStyle::Exponent || Style == FloatStyle::ExponentUpper) { #ifdef _WIN32 // On MSVCRT and compatible, output of %e is incompatible to Posix // by default. Number of exponent digits should be at least 2. "%+03d" // FIXME: Implement our formatter to here or Support/Format.h! #if defined(__MINGW32__) // FIXME: It should be generic to C++11. if (N == 0.0 && std::signbit(N)) { char NegativeZero[] = "-0.000000e+00"; if (Style == FloatStyle::ExponentUpper) NegativeZero[strlen(NegativeZero) - 4] = 'E'; S << NegativeZero; return; } #else int fpcl = _fpclass(N); // negative zero if (fpcl == _FPCLASS_NZ) { char NegativeZero[] = "-0.000000e+00"; if (Style == FloatStyle::ExponentUpper) NegativeZero[strlen(NegativeZero) - 4] = 'E'; S << NegativeZero; return; } #endif char buf[32]; unsigned len; len = format(Spec.c_str(), N).snprint(buf, sizeof(buf)); if (len <= sizeof(buf) - 2) { if (len >= 5 && (buf[len - 5] == 'e' || buf[len - 5] == 'E') && buf[len - 3] == '0') { int cs = buf[len - 4]; if (cs == '+' || cs == '-') { int c1 = buf[len - 2]; int c0 = buf[len - 1]; if (isdigit(static_cast(c1)) && isdigit(static_cast(c0))) { // Trim leading '0': "...e+012" -> "...e+12\0" buf[len - 3] = c1; buf[len - 2] = c0; buf[--len] = 0; } } } S << buf; return; } #endif } if (Style == FloatStyle::Percent) N *= 100.0; char Buf[32]; format(Spec.c_str(), N).snprint(Buf, sizeof(Buf)); S << Buf; if (Style == FloatStyle::Percent) S << '%'; } bool llvm::isPrefixedHexStyle(HexPrintStyle S) { return (S == HexPrintStyle::PrefixLower || S == HexPrintStyle::PrefixUpper); } size_t llvm::getDefaultPrecision(FloatStyle Style) { switch (Style) { case FloatStyle::Exponent: case FloatStyle::ExponentUpper: return 6; // Number of decimal places. case FloatStyle::Fixed: case FloatStyle::Percent: return 2; // Number of decimal places. } LLVM_BUILTIN_UNREACHABLE; } binaryen-version_91/third_party/llvm-project/ObjectFile.cpp000066400000000000000000000066251362402614000243510ustar00rootroot00000000000000//===- ObjectFile.cpp - File format independent object file ---------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file defines a file format independent ObjectFile class. // //===----------------------------------------------------------------------===// #include "llvm/Object/ObjectFile.h" #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/Magic.h" #include "llvm/Object/Binary.h" #include "llvm/Object/COFF.h" #include "llvm/Object/Error.h" #include "llvm/Object/MachO.h" #include "llvm/Object/Wasm.h" #include "llvm/Support/Error.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_ostream.h" #include #include #include #include using namespace llvm; using namespace object; void ObjectFile::anchor() {} ObjectFile::ObjectFile(unsigned int Type, MemoryBufferRef Source) : SymbolicFile(Type, Source) {} bool SectionRef::containsSymbol(SymbolRef S) const { llvm_unreachable("containsSymbol"); // XXX BINARYEN } uint64_t ObjectFile::getSymbolValue(DataRefImpl Ref) const { uint32_t Flags = getSymbolFlags(Ref); if (Flags & SymbolRef::SF_Undefined) return 0; if (Flags & SymbolRef::SF_Common) return getCommonSymbolSize(Ref); return getSymbolValueImpl(Ref); } Error ObjectFile::printSymbolName(raw_ostream &OS, DataRefImpl Symb) const { Expected Name = getSymbolName(Symb); if (!Name) return Name.takeError(); OS << *Name; return Error::success(); } uint32_t ObjectFile::getSymbolAlignment(DataRefImpl DRI) const { return 0; } bool ObjectFile::isSectionBitcode(DataRefImpl Sec) const { Expected NameOrErr = getSectionName(Sec); if (NameOrErr) return *NameOrErr == ".llvmbc"; consumeError(NameOrErr.takeError()); return false; } bool ObjectFile::isSectionStripped(DataRefImpl Sec) const { return false; } bool ObjectFile::isBerkeleyText(DataRefImpl Sec) const { return isSectionText(Sec); } bool ObjectFile::isBerkeleyData(DataRefImpl Sec) const { return isSectionData(Sec); } Expected ObjectFile::getRelocatedSection(DataRefImpl Sec) const { return section_iterator(SectionRef(Sec, this)); } Triple ObjectFile::makeTriple() const { llvm_unreachable("makeTriple"); // XXX BINARYEN } Expected> ObjectFile::createObjectFile(MemoryBufferRef Object, file_magic Type) { llvm_unreachable("createObjectFile"); // XXX BINARYEN } #if 0 // XXX BINARYEN Expected> ObjectFile::createObjectFile(StringRef ObjectPath) { ErrorOr> FileOrErr = MemoryBuffer::getFile(ObjectPath); if (std::error_code EC = FileOrErr.getError()) return errorCodeToError(EC); std::unique_ptr Buffer = std::move(FileOrErr.get()); Expected> ObjOrErr = createObjectFile(Buffer->getMemBufferRef()); if (Error Err = ObjOrErr.takeError()) return std::move(Err); std::unique_ptr Obj = std::move(ObjOrErr.get()); return OwningBinary(std::move(Obj), std::move(Buffer)); } #endif binaryen-version_91/third_party/llvm-project/Optional.cpp000066400000000000000000000010221362402614000241120ustar00rootroot00000000000000//===- Optional.cpp - Optional values ---------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/ADT/Optional.h" #include "llvm/Support/raw_ostream.h" llvm::raw_ostream &llvm::operator<<(raw_ostream &OS, NoneType) { return OS << "None"; } binaryen-version_91/third_party/llvm-project/Path.cpp000066400000000000000000001033451362402614000232340ustar00rootroot00000000000000//===-- Path.cpp - Implement OS Path Concept ------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file implements the operating system Path API. // //===----------------------------------------------------------------------===// #include "llvm/Support/Path.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/Config/llvm-config.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Errc.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Process.h" #include "llvm/Support/Signals.h" #include #include #if !defined(_MSC_VER) && !defined(__MINGW32__) #include #else #include #endif using namespace llvm; using namespace llvm::support::endian; namespace { using llvm::StringRef; using llvm::sys::path::is_separator; using llvm::sys::path::Style; inline Style real_style(Style style) { #ifdef _WIN32 return (style == Style::posix) ? Style::posix : Style::windows; #else return (style == Style::windows) ? Style::windows : Style::posix; #endif } inline const char *separators(Style style) { if (real_style(style) == Style::windows) return "\\/"; return "/"; } inline char preferred_separator(Style style) { if (real_style(style) == Style::windows) return '\\'; return '/'; } StringRef find_first_component(StringRef path, Style style) { // Look for this first component in the following order. // * empty (in this case we return an empty string) // * either C: or {//,\\}net. // * {/,\} // * {file,directory}name if (path.empty()) return path; if (real_style(style) == Style::windows) { // C: if (path.size() >= 2 && std::isalpha(static_cast(path[0])) && path[1] == ':') return path.substr(0, 2); } // //net if ((path.size() > 2) && is_separator(path[0], style) && path[0] == path[1] && !is_separator(path[2], style)) { // Find the next directory separator. size_t end = path.find_first_of(separators(style), 2); return path.substr(0, end); } // {/,\} if (is_separator(path[0], style)) return path.substr(0, 1); // * {file,directory}name size_t end = path.find_first_of(separators(style)); return path.substr(0, end); } // Returns the first character of the filename in str. For paths ending in // '/', it returns the position of the '/'. size_t filename_pos(StringRef str, Style style) { if (str.size() > 0 && is_separator(str[str.size() - 1], style)) return str.size() - 1; size_t pos = str.find_last_of(separators(style), str.size() - 1); if (real_style(style) == Style::windows) { if (pos == StringRef::npos) pos = str.find_last_of(':', str.size() - 2); } if (pos == StringRef::npos || (pos == 1 && is_separator(str[0], style))) return 0; return pos + 1; } // Returns the position of the root directory in str. If there is no root // directory in str, it returns StringRef::npos. size_t root_dir_start(StringRef str, Style style) { // case "c:/" if (real_style(style) == Style::windows) { if (str.size() > 2 && str[1] == ':' && is_separator(str[2], style)) return 2; } // case "//net" if (str.size() > 3 && is_separator(str[0], style) && str[0] == str[1] && !is_separator(str[2], style)) { return str.find_first_of(separators(style), 2); } // case "/" if (str.size() > 0 && is_separator(str[0], style)) return 0; return StringRef::npos; } // Returns the position past the end of the "parent path" of path. The parent // path will not end in '/', unless the parent is the root directory. If the // path has no parent, 0 is returned. size_t parent_path_end(StringRef path, Style style) { size_t end_pos = filename_pos(path, style); bool filename_was_sep = path.size() > 0 && is_separator(path[end_pos], style); // Skip separators until we reach root dir (or the start of the string). size_t root_dir_pos = root_dir_start(path, style); while (end_pos > 0 && (root_dir_pos == StringRef::npos || end_pos > root_dir_pos) && is_separator(path[end_pos - 1], style)) --end_pos; if (end_pos == root_dir_pos && !filename_was_sep) { // We've reached the root dir and the input path was *not* ending in a // sequence of slashes. Include the root dir in the parent path. return root_dir_pos + 1; } // Otherwise, just include before the last slash. return end_pos; } } // end unnamed namespace enum FSEntity { FS_Dir, FS_File, FS_Name }; static std::error_code createUniqueEntity(const Twine &Model, int &ResultFD, SmallVectorImpl &ResultPath, bool MakeAbsolute, unsigned Mode, FSEntity Type, sys::fs::OpenFlags Flags = sys::fs::OF_None) { llvm_unreachable("createUniqueEntity"); // XXX BINARYEN } namespace llvm { namespace sys { namespace path { const_iterator begin(StringRef path, Style style) { const_iterator i; i.Path = path; i.Component = find_first_component(path, style); i.Position = 0; i.S = style; return i; } const_iterator end(StringRef path) { const_iterator i; i.Path = path; i.Position = path.size(); return i; } const_iterator &const_iterator::operator++() { assert(Position < Path.size() && "Tried to increment past end!"); // Increment Position to past the current component Position += Component.size(); // Check for end. if (Position == Path.size()) { Component = StringRef(); return *this; } // Both POSIX and Windows treat paths that begin with exactly two separators // specially. bool was_net = Component.size() > 2 && is_separator(Component[0], S) && Component[1] == Component[0] && !is_separator(Component[2], S); // Handle separators. if (is_separator(Path[Position], S)) { // Root dir. if (was_net || // c:/ (real_style(S) == Style::windows && Component.endswith(":"))) { Component = Path.substr(Position, 1); return *this; } // Skip extra separators. while (Position != Path.size() && is_separator(Path[Position], S)) { ++Position; } // Treat trailing '/' as a '.', unless it is the root dir. if (Position == Path.size() && Component != "/") { --Position; Component = "."; return *this; } } // Find next component. size_t end_pos = Path.find_first_of(separators(S), Position); Component = Path.slice(Position, end_pos); return *this; } bool const_iterator::operator==(const const_iterator &RHS) const { return Path.begin() == RHS.Path.begin() && Position == RHS.Position; } ptrdiff_t const_iterator::operator-(const const_iterator &RHS) const { return Position - RHS.Position; } reverse_iterator rbegin(StringRef Path, Style style) { reverse_iterator I; I.Path = Path; I.Position = Path.size(); I.S = style; ++I; return I; } reverse_iterator rend(StringRef Path) { reverse_iterator I; I.Path = Path; I.Component = Path.substr(0, 0); I.Position = 0; return I; } reverse_iterator &reverse_iterator::operator++() { size_t root_dir_pos = root_dir_start(Path, S); // Skip separators unless it's the root directory. size_t end_pos = Position; while (end_pos > 0 && (end_pos - 1) != root_dir_pos && is_separator(Path[end_pos - 1], S)) --end_pos; // Treat trailing '/' as a '.', unless it is the root dir. if (Position == Path.size() && !Path.empty() && is_separator(Path.back(), S) && (root_dir_pos == StringRef::npos || end_pos - 1 > root_dir_pos)) { --Position; Component = "."; return *this; } // Find next separator. size_t start_pos = filename_pos(Path.substr(0, end_pos), S); Component = Path.slice(start_pos, end_pos); Position = start_pos; return *this; } bool reverse_iterator::operator==(const reverse_iterator &RHS) const { return Path.begin() == RHS.Path.begin() && Component == RHS.Component && Position == RHS.Position; } ptrdiff_t reverse_iterator::operator-(const reverse_iterator &RHS) const { return Position - RHS.Position; } StringRef root_path(StringRef path, Style style) { const_iterator b = begin(path, style), pos = b, e = end(path); if (b != e) { bool has_net = b->size() > 2 && is_separator((*b)[0], style) && (*b)[1] == (*b)[0]; bool has_drive = (real_style(style) == Style::windows) && b->endswith(":"); if (has_net || has_drive) { if ((++pos != e) && is_separator((*pos)[0], style)) { // {C:/,//net/}, so get the first two components. return path.substr(0, b->size() + pos->size()); } else { // just {C:,//net}, return the first component. return *b; } } // POSIX style root directory. if (is_separator((*b)[0], style)) { return *b; } } return StringRef(); } StringRef root_name(StringRef path, Style style) { const_iterator b = begin(path, style), e = end(path); if (b != e) { bool has_net = b->size() > 2 && is_separator((*b)[0], style) && (*b)[1] == (*b)[0]; bool has_drive = (real_style(style) == Style::windows) && b->endswith(":"); if (has_net || has_drive) { // just {C:,//net}, return the first component. return *b; } } // No path or no name. return StringRef(); } StringRef root_directory(StringRef path, Style style) { const_iterator b = begin(path, style), pos = b, e = end(path); if (b != e) { bool has_net = b->size() > 2 && is_separator((*b)[0], style) && (*b)[1] == (*b)[0]; bool has_drive = (real_style(style) == Style::windows) && b->endswith(":"); if ((has_net || has_drive) && // {C:,//net}, skip to the next component. (++pos != e) && is_separator((*pos)[0], style)) { return *pos; } // POSIX style root directory. if (!has_net && is_separator((*b)[0], style)) { return *b; } } // No path or no root. return StringRef(); } StringRef relative_path(StringRef path, Style style) { StringRef root = root_path(path, style); return path.substr(root.size()); } void append(SmallVectorImpl &path, Style style, const Twine &a, const Twine &b, const Twine &c, const Twine &d) { SmallString<32> a_storage; SmallString<32> b_storage; SmallString<32> c_storage; SmallString<32> d_storage; SmallVector components; if (!a.isTriviallyEmpty()) components.push_back(a.toStringRef(a_storage)); if (!b.isTriviallyEmpty()) components.push_back(b.toStringRef(b_storage)); if (!c.isTriviallyEmpty()) components.push_back(c.toStringRef(c_storage)); if (!d.isTriviallyEmpty()) components.push_back(d.toStringRef(d_storage)); for (auto &component : components) { bool path_has_sep = !path.empty() && is_separator(path[path.size() - 1], style); if (path_has_sep) { // Strip separators from beginning of component. size_t loc = component.find_first_not_of(separators(style)); StringRef c = component.substr(loc); // Append it. path.append(c.begin(), c.end()); continue; } bool component_has_sep = !component.empty() && is_separator(component[0], style); if (!component_has_sep && !(path.empty() || has_root_name(component, style))) { // Add a separator. path.push_back(preferred_separator(style)); } path.append(component.begin(), component.end()); } } void append(SmallVectorImpl &path, const Twine &a, const Twine &b, const Twine &c, const Twine &d) { append(path, Style::native, a, b, c, d); } void append(SmallVectorImpl &path, const_iterator begin, const_iterator end, Style style) { for (; begin != end; ++begin) path::append(path, style, *begin); } StringRef parent_path(StringRef path, Style style) { size_t end_pos = parent_path_end(path, style); if (end_pos == StringRef::npos) return StringRef(); else return path.substr(0, end_pos); } void remove_filename(SmallVectorImpl &path, Style style) { size_t end_pos = parent_path_end(StringRef(path.begin(), path.size()), style); if (end_pos != StringRef::npos) path.set_size(end_pos); } void replace_extension(SmallVectorImpl &path, const Twine &extension, Style style) { StringRef p(path.begin(), path.size()); SmallString<32> ext_storage; StringRef ext = extension.toStringRef(ext_storage); // Erase existing extension. size_t pos = p.find_last_of('.'); if (pos != StringRef::npos && pos >= filename_pos(p, style)) path.set_size(pos); // Append '.' if needed. if (ext.size() > 0 && ext[0] != '.') path.push_back('.'); // Append extension. path.append(ext.begin(), ext.end()); } void replace_path_prefix(SmallVectorImpl &Path, const StringRef &OldPrefix, const StringRef &NewPrefix, Style style) { if (OldPrefix.empty() && NewPrefix.empty()) return; StringRef OrigPath(Path.begin(), Path.size()); if (!OrigPath.startswith(OldPrefix)) return; // If prefixes have the same size we can simply copy the new one over. if (OldPrefix.size() == NewPrefix.size()) { llvm::copy(NewPrefix, Path.begin()); return; } StringRef RelPath = OrigPath.substr(OldPrefix.size()); SmallString<256> NewPath; path::append(NewPath, style, NewPrefix); path::append(NewPath, style, RelPath); Path.swap(NewPath); } void native(const Twine &path, SmallVectorImpl &result, Style style) { assert((!path.isSingleStringRef() || path.getSingleStringRef().data() != result.data()) && "path and result are not allowed to overlap!"); // Clear result. result.clear(); path.toVector(result); native(result, style); } void native(SmallVectorImpl &Path, Style style) { if (Path.empty()) return; if (real_style(style) == Style::windows) { std::replace(Path.begin(), Path.end(), '/', '\\'); if (Path[0] == '~' && (Path.size() == 1 || is_separator(Path[1], style))) { llvm_unreachable("BINARYEN native"); #if 0 SmallString<128> PathHome; home_directory(PathHome); PathHome.append(Path.begin() + 1, Path.end()); Path = PathHome; #endif } } else { for (auto PI = Path.begin(), PE = Path.end(); PI < PE; ++PI) { if (*PI == '\\') { auto PN = PI + 1; if (PN < PE && *PN == '\\') ++PI; // increment once, the for loop will move over the escaped slash else *PI = '/'; } } } } std::string convert_to_slash(StringRef path, Style style) { if (real_style(style) != Style::windows) return path; std::string s = path.str(); std::replace(s.begin(), s.end(), '\\', '/'); return s; } StringRef filename(StringRef path, Style style) { return *rbegin(path, style); } StringRef stem(StringRef path, Style style) { StringRef fname = filename(path, style); size_t pos = fname.find_last_of('.'); if (pos == StringRef::npos) return fname; else if ((fname.size() == 1 && fname == ".") || (fname.size() == 2 && fname == "..")) return fname; else return fname.substr(0, pos); } StringRef extension(StringRef path, Style style) { StringRef fname = filename(path, style); size_t pos = fname.find_last_of('.'); if (pos == StringRef::npos) return StringRef(); else if ((fname.size() == 1 && fname == ".") || (fname.size() == 2 && fname == "..")) return StringRef(); else return fname.substr(pos); } bool is_separator(char value, Style style) { if (value == '/') return true; if (real_style(style) == Style::windows) return value == '\\'; return false; } StringRef get_separator(Style style) { if (real_style(style) == Style::windows) return "\\"; return "/"; } bool has_root_name(const Twine &path, Style style) { SmallString<128> path_storage; StringRef p = path.toStringRef(path_storage); return !root_name(p, style).empty(); } bool has_root_directory(const Twine &path, Style style) { SmallString<128> path_storage; StringRef p = path.toStringRef(path_storage); return !root_directory(p, style).empty(); } bool has_root_path(const Twine &path, Style style) { SmallString<128> path_storage; StringRef p = path.toStringRef(path_storage); return !root_path(p, style).empty(); } bool has_relative_path(const Twine &path, Style style) { SmallString<128> path_storage; StringRef p = path.toStringRef(path_storage); return !relative_path(p, style).empty(); } bool has_filename(const Twine &path, Style style) { SmallString<128> path_storage; StringRef p = path.toStringRef(path_storage); return !filename(p, style).empty(); } bool has_parent_path(const Twine &path, Style style) { SmallString<128> path_storage; StringRef p = path.toStringRef(path_storage); return !parent_path(p, style).empty(); } bool has_stem(const Twine &path, Style style) { SmallString<128> path_storage; StringRef p = path.toStringRef(path_storage); return !stem(p, style).empty(); } bool has_extension(const Twine &path, Style style) { SmallString<128> path_storage; StringRef p = path.toStringRef(path_storage); return !extension(p, style).empty(); } bool is_absolute(const Twine &path, Style style) { SmallString<128> path_storage; StringRef p = path.toStringRef(path_storage); bool rootDir = has_root_directory(p, style); bool rootName = (real_style(style) != Style::windows) || has_root_name(p, style); return rootDir && rootName; } bool is_relative(const Twine &path, Style style) { return !is_absolute(path, style); } StringRef remove_leading_dotslash(StringRef Path, Style style) { // Remove leading "./" (or ".//" or "././" etc.) while (Path.size() > 2 && Path[0] == '.' && is_separator(Path[1], style)) { Path = Path.substr(2); while (Path.size() > 0 && is_separator(Path[0], style)) Path = Path.substr(1); } return Path; } static SmallString<256> remove_dots(StringRef path, bool remove_dot_dot, Style style) { SmallVector components; // Skip the root path, then look for traversal in the components. StringRef rel = path::relative_path(path, style); for (StringRef C : llvm::make_range(path::begin(rel, style), path::end(rel))) { if (C == ".") continue; // Leading ".." will remain in the path unless it's at the root. if (remove_dot_dot && C == "..") { if (!components.empty() && components.back() != "..") { components.pop_back(); continue; } if (path::is_absolute(path, style)) continue; } components.push_back(C); } SmallString<256> buffer = path::root_path(path, style); for (StringRef C : components) path::append(buffer, style, C); return buffer; } bool remove_dots(SmallVectorImpl &path, bool remove_dot_dot, Style style) { StringRef p(path.data(), path.size()); SmallString<256> result = remove_dots(p, remove_dot_dot, style); if (result == path) return false; path.swap(result); return true; } } // end namespace path #if 0 // XXX BINARYEN namespace fs { std::error_code getUniqueID(const Twine Path, UniqueID &Result) { file_status Status; std::error_code EC = status(Path, Status); if (EC) return EC; Result = Status.getUniqueID(); return std::error_code(); } void createUniquePath(const Twine &Model, SmallVectorImpl &ResultPath, bool MakeAbsolute) { SmallString<128> ModelStorage; Model.toVector(ModelStorage); if (MakeAbsolute) { // Make model absolute by prepending a temp directory if it's not already. if (!sys::path::is_absolute(Twine(ModelStorage))) { SmallString<128> TDir; sys::path::system_temp_directory(true, TDir); sys::path::append(TDir, Twine(ModelStorage)); ModelStorage.swap(TDir); } } ResultPath = ModelStorage; ResultPath.push_back(0); ResultPath.pop_back(); // Replace '%' with random chars. for (unsigned i = 0, e = ModelStorage.size(); i != e; ++i) { if (ModelStorage[i] == '%') ResultPath[i] = "0123456789abcdef"[sys::Process::GetRandomNumber() & 15]; } } std::error_code createUniqueFile(const Twine &Model, int &ResultFd, SmallVectorImpl &ResultPath, unsigned Mode) { return createUniqueEntity(Model, ResultFd, ResultPath, false, Mode, FS_File); } static std::error_code createUniqueFile(const Twine &Model, int &ResultFd, SmallVectorImpl &ResultPath, unsigned Mode, OpenFlags Flags) { return createUniqueEntity(Model, ResultFd, ResultPath, false, Mode, FS_File, Flags); } std::error_code createUniqueFile(const Twine &Model, SmallVectorImpl &ResultPath, unsigned Mode) { int FD; auto EC = createUniqueFile(Model, FD, ResultPath, Mode); if (EC) return EC; // FD is only needed to avoid race conditions. Close it right away. close(FD); return EC; } static std::error_code createTemporaryFile(const Twine &Model, int &ResultFD, llvm::SmallVectorImpl &ResultPath, FSEntity Type) { SmallString<128> Storage; StringRef P = Model.toNullTerminatedStringRef(Storage); assert(P.find_first_of(separators(Style::native)) == StringRef::npos && "Model must be a simple filename."); // Use P.begin() so that createUniqueEntity doesn't need to recreate Storage. return createUniqueEntity(P.begin(), ResultFD, ResultPath, true, owner_read | owner_write, Type); } static std::error_code createTemporaryFile(const Twine &Prefix, StringRef Suffix, int &ResultFD, llvm::SmallVectorImpl &ResultPath, FSEntity Type) { const char *Middle = Suffix.empty() ? "-%%%%%%" : "-%%%%%%."; return createTemporaryFile(Prefix + Middle + Suffix, ResultFD, ResultPath, Type); } std::error_code createTemporaryFile(const Twine &Prefix, StringRef Suffix, int &ResultFD, SmallVectorImpl &ResultPath) { return createTemporaryFile(Prefix, Suffix, ResultFD, ResultPath, FS_File); } std::error_code createTemporaryFile(const Twine &Prefix, StringRef Suffix, SmallVectorImpl &ResultPath) { int FD; auto EC = createTemporaryFile(Prefix, Suffix, FD, ResultPath); if (EC) return EC; // FD is only needed to avoid race conditions. Close it right away. close(FD); return EC; } // This is a mkdtemp with a different pattern. We use createUniqueEntity mostly // for consistency. We should try using mkdtemp. std::error_code createUniqueDirectory(const Twine &Prefix, SmallVectorImpl &ResultPath) { int Dummy; return createUniqueEntity(Prefix + "-%%%%%%", Dummy, ResultPath, true, 0, FS_Dir); } std::error_code getPotentiallyUniqueFileName(const Twine &Model, SmallVectorImpl &ResultPath) { int Dummy; return createUniqueEntity(Model, Dummy, ResultPath, false, 0, FS_Name); } std::error_code getPotentiallyUniqueTempFileName(const Twine &Prefix, StringRef Suffix, SmallVectorImpl &ResultPath) { int Dummy; return createTemporaryFile(Prefix, Suffix, Dummy, ResultPath, FS_Name); } void make_absolute(const Twine ¤t_directory, SmallVectorImpl &path) { StringRef p(path.data(), path.size()); bool rootDirectory = path::has_root_directory(p); bool rootName = path::has_root_name(p); // Already absolute. if ((rootName || real_style(Style::native) != Style::windows) && rootDirectory) return; // All of the following conditions will need the current directory. SmallString<128> current_dir; current_directory.toVector(current_dir); // Relative path. Prepend the current directory. if (!rootName && !rootDirectory) { // Append path to the current directory. path::append(current_dir, p); // Set path to the result. path.swap(current_dir); return; } if (!rootName && rootDirectory) { StringRef cdrn = path::root_name(current_dir); SmallString<128> curDirRootName(cdrn.begin(), cdrn.end()); path::append(curDirRootName, p); // Set path to the result. path.swap(curDirRootName); return; } if (rootName && !rootDirectory) { StringRef pRootName = path::root_name(p); StringRef bRootDirectory = path::root_directory(current_dir); StringRef bRelativePath = path::relative_path(current_dir); StringRef pRelativePath = path::relative_path(p); SmallString<128> res; path::append(res, pRootName, bRootDirectory, bRelativePath, pRelativePath); path.swap(res); return; } llvm_unreachable("All rootName and rootDirectory combinations should have " "occurred above!"); } std::error_code make_absolute(SmallVectorImpl &path) { if (path::is_absolute(path)) return {}; SmallString<128> current_dir; if (std::error_code ec = current_path(current_dir)) return ec; make_absolute(current_dir, path); return {}; } std::error_code create_directories(const Twine &Path, bool IgnoreExisting, perms Perms) { SmallString<128> PathStorage; StringRef P = Path.toStringRef(PathStorage); // Be optimistic and try to create the directory std::error_code EC = create_directory(P, IgnoreExisting, Perms); // If we succeeded, or had any error other than the parent not existing, just // return it. if (EC != errc::no_such_file_or_directory) return EC; // We failed because of a no_such_file_or_directory, try to create the // parent. StringRef Parent = path::parent_path(P); if (Parent.empty()) return EC; if ((EC = create_directories(Parent, IgnoreExisting, Perms))) return EC; return create_directory(P, IgnoreExisting, Perms); } static std::error_code copy_file_internal(int ReadFD, int WriteFD) { const size_t BufSize = 4096; char *Buf = new char[BufSize]; int BytesRead = 0, BytesWritten = 0; for (;;) { BytesRead = read(ReadFD, Buf, BufSize); if (BytesRead <= 0) break; while (BytesRead) { BytesWritten = write(WriteFD, Buf, BytesRead); if (BytesWritten < 0) break; BytesRead -= BytesWritten; } if (BytesWritten < 0) break; } delete[] Buf; if (BytesRead < 0 || BytesWritten < 0) return std::error_code(errno, std::generic_category()); return std::error_code(); } #ifndef __APPLE__ std::error_code copy_file(const Twine &From, const Twine &To) { int ReadFD, WriteFD; if (std::error_code EC = openFileForRead(From, ReadFD, OF_None)) return EC; if (std::error_code EC = openFileForWrite(To, WriteFD, CD_CreateAlways, OF_None)) { close(ReadFD); return EC; } std::error_code EC = copy_file_internal(ReadFD, WriteFD); close(ReadFD); close(WriteFD); return EC; } #endif std::error_code copy_file(const Twine &From, int ToFD) { int ReadFD; if (std::error_code EC = openFileForRead(From, ReadFD, OF_None)) return EC; std::error_code EC = copy_file_internal(ReadFD, ToFD); close(ReadFD); return EC; } ErrorOr md5_contents(int FD) { MD5 Hash; constexpr size_t BufSize = 4096; std::vector Buf(BufSize); int BytesRead = 0; for (;;) { BytesRead = read(FD, Buf.data(), BufSize); if (BytesRead <= 0) break; Hash.update(makeArrayRef(Buf.data(), BytesRead)); } if (BytesRead < 0) return std::error_code(errno, std::generic_category()); MD5::MD5Result Result; Hash.final(Result); return Result; } ErrorOr md5_contents(const Twine &Path) { int FD; if (auto EC = openFileForRead(Path, FD, OF_None)) return EC; auto Result = md5_contents(FD); close(FD); return Result; } bool exists(const basic_file_status &status) { return status_known(status) && status.type() != file_type::file_not_found; } bool status_known(const basic_file_status &s) { return s.type() != file_type::status_error; } file_type get_file_type(const Twine &Path, bool Follow) { file_status st; if (status(Path, st, Follow)) return file_type::status_error; return st.type(); } bool is_directory(const basic_file_status &status) { return status.type() == file_type::directory_file; } std::error_code is_directory(const Twine &path, bool &result) { file_status st; if (std::error_code ec = status(path, st)) return ec; result = is_directory(st); return std::error_code(); } bool is_regular_file(const basic_file_status &status) { return status.type() == file_type::regular_file; } std::error_code is_regular_file(const Twine &path, bool &result) { file_status st; if (std::error_code ec = status(path, st)) return ec; result = is_regular_file(st); return std::error_code(); } bool is_symlink_file(const basic_file_status &status) { return status.type() == file_type::symlink_file; } std::error_code is_symlink_file(const Twine &path, bool &result) { file_status st; if (std::error_code ec = status(path, st, false)) return ec; result = is_symlink_file(st); return std::error_code(); } bool is_other(const basic_file_status &status) { return exists(status) && !is_regular_file(status) && !is_directory(status); } std::error_code is_other(const Twine &Path, bool &Result) { file_status FileStatus; if (std::error_code EC = status(Path, FileStatus)) return EC; Result = is_other(FileStatus); return std::error_code(); } void directory_entry::replace_filename(const Twine &Filename, file_type Type, basic_file_status Status) { SmallString<128> PathStr = path::parent_path(Path); path::append(PathStr, Filename); this->Path = PathStr.str(); this->Type = Type; this->Status = Status; } ErrorOr getPermissions(const Twine &Path) { file_status Status; if (std::error_code EC = status(Path, Status)) return EC; return Status.permissions(); } } // end namespace fs #endif // XXX BINARYEN } // end namespace sys } // end namespace llvm // Include the truly platform-specific parts. #if 0 // XXX BINARYEN - we don't need platform-specific parts. #if defined(LLVM_ON_UNIX) #include "Unix/Path.inc" #endif #if defined(_WIN32) #include "Windows/Path.inc" #endif #endif #if 0 // XXX BINARYEN namespace llvm { namespace sys { namespace fs { TempFile::TempFile(StringRef Name, int FD) : TmpName(Name), FD(FD) {} TempFile::TempFile(TempFile &&Other) { *this = std::move(Other); } TempFile &TempFile::operator=(TempFile &&Other) { TmpName = std::move(Other.TmpName); FD = Other.FD; Other.Done = true; Other.FD = -1; return *this; } TempFile::~TempFile() { assert(Done); } Error TempFile::discard() { Done = true; if (FD != -1 && close(FD) == -1) { std::error_code EC = std::error_code(errno, std::generic_category()); return errorCodeToError(EC); } FD = -1; #ifdef _WIN32 // On windows closing will remove the file. TmpName = ""; return Error::success(); #else // Always try to close and remove. std::error_code RemoveEC; if (!TmpName.empty()) { RemoveEC = fs::remove(TmpName); sys::DontRemoveFileOnSignal(TmpName); if (!RemoveEC) TmpName = ""; } return errorCodeToError(RemoveEC); #endif } Error TempFile::keep(const Twine &Name) { assert(!Done); Done = true; // Always try to close and rename. #ifdef _WIN32 // If we can't cancel the delete don't rename. auto H = reinterpret_cast(_get_osfhandle(FD)); std::error_code RenameEC = setDeleteDisposition(H, false); if (!RenameEC) { RenameEC = rename_fd(FD, Name); // If rename failed because it's cross-device, copy instead if (RenameEC == std::error_code(ERROR_NOT_SAME_DEVICE, std::system_category())) { RenameEC = copy_file(TmpName, Name); setDeleteDisposition(H, true); } } // If we can't rename, discard the temporary file. if (RenameEC) setDeleteDisposition(H, true); #else std::error_code RenameEC = fs::rename(TmpName, Name); if (RenameEC) { // If we can't rename, try to copy to work around cross-device link issues. RenameEC = sys::fs::copy_file(TmpName, Name); // If we can't rename or copy, discard the temporary file. if (RenameEC) remove(TmpName); } sys::DontRemoveFileOnSignal(TmpName); #endif if (!RenameEC) TmpName = ""; if (close(FD) == -1) { std::error_code EC(errno, std::generic_category()); return errorCodeToError(EC); } FD = -1; return errorCodeToError(RenameEC); } Error TempFile::keep() { assert(!Done); Done = true; #ifdef _WIN32 auto H = reinterpret_cast(_get_osfhandle(FD)); if (std::error_code EC = setDeleteDisposition(H, false)) return errorCodeToError(EC); #else sys::DontRemoveFileOnSignal(TmpName); #endif TmpName = ""; if (close(FD) == -1) { std::error_code EC(errno, std::generic_category()); return errorCodeToError(EC); } FD = -1; return Error::success(); } Expected TempFile::create(const Twine &Model, unsigned Mode) { int FD; SmallString<128> ResultPath; if (std::error_code EC = createUniqueFile(Model, FD, ResultPath, Mode, OF_Delete)) return errorCodeToError(EC); TempFile Ret(ResultPath, FD); #ifndef _WIN32 if (sys::RemoveFileOnSignal(ResultPath)) { // Make sure we delete the file when RemoveFileOnSignal fails. consumeError(Ret.discard()); std::error_code EC(errc::operation_not_permitted); return errorCodeToError(EC); } #endif return std::move(Ret); } } } // end namsspace sys } // end namespace llvm #endif binaryen-version_91/third_party/llvm-project/ScopedPrinter.cpp000066400000000000000000000023021362402614000251100ustar00rootroot00000000000000#include "llvm/Support/ScopedPrinter.h" #include "llvm/Support/Format.h" #include using namespace llvm::support; namespace llvm { raw_ostream &operator<<(raw_ostream &OS, const HexNumber &Value) { OS << "0x" << to_hexString(Value.Value); return OS; } const std::string to_hexString(uint64_t Value, bool UpperCase) { std::string number; llvm::raw_string_ostream stream(number); stream << format_hex_no_prefix(Value, 1, UpperCase); return stream.str(); } void ScopedPrinter::printBinaryImpl(StringRef Label, StringRef Str, ArrayRef Data, bool Block, uint32_t StartOffset) { if (Data.size() > 16) Block = true; if (Block) { startLine() << Label; if (!Str.empty()) OS << ": " << Str; OS << " (\n"; if (!Data.empty()) OS << format_bytes_with_ascii(Data, StartOffset, 16, 4, (IndentLevel + 1) * 2, true) << "\n"; startLine() << ")\n"; } else { startLine() << Label << ":"; if (!Str.empty()) OS << " " << Str; OS << " (" << format_bytes(Data, None, Data.size(), 1, 0, true) << ")\n"; } } } // namespace llvm binaryen-version_91/third_party/llvm-project/SmallVector.cpp000066400000000000000000000050001362402614000245600ustar00rootroot00000000000000//===- llvm/ADT/SmallVector.cpp - 'Normally small' vectors ----------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file implements the SmallVector class. // //===----------------------------------------------------------------------===// #include "llvm/ADT/SmallVector.h" using namespace llvm; // Check that no bytes are wasted and everything is well-aligned. namespace { struct Struct16B { alignas(16) void *X; }; struct Struct32B { alignas(32) void *X; }; } static_assert(sizeof(SmallVector) == sizeof(unsigned) * 2 + sizeof(void *), "wasted space in SmallVector size 0"); static_assert(alignof(SmallVector) >= alignof(Struct16B), "wrong alignment for 16-byte aligned T"); static_assert(alignof(SmallVector) >= alignof(Struct32B), "wrong alignment for 32-byte aligned T"); static_assert(sizeof(SmallVector) >= alignof(Struct16B), "missing padding for 16-byte aligned T"); static_assert(sizeof(SmallVector) >= alignof(Struct32B), "missing padding for 32-byte aligned T"); static_assert(sizeof(SmallVector) == sizeof(unsigned) * 2 + sizeof(void *) * 2, "wasted space in SmallVector size 1"); /// grow_pod - This is an implementation of the grow() method which only works /// on POD-like datatypes and is out of line to reduce code duplication. void SmallVectorBase::grow_pod(void *FirstEl, size_t MinCapacity, size_t TSize) { // Ensure we can fit the new capacity in 32 bits. if (MinCapacity > UINT32_MAX) report_bad_alloc_error("SmallVector capacity overflow during allocation"); size_t NewCapacity = 2 * capacity() + 1; // Always grow. NewCapacity = std::min(std::max(NewCapacity, MinCapacity), size_t(UINT32_MAX)); void *NewElts; if (BeginX == FirstEl) { NewElts = safe_malloc(NewCapacity * TSize); // Copy the elements over. No need to run dtors on PODs. memcpy(NewElts, this->BeginX, size() * TSize); } else { // If this wasn't grown from the inline copy, grow the allocated space. NewElts = safe_realloc(this->BeginX, NewCapacity * TSize); } this->BeginX = NewElts; this->Capacity = NewCapacity; } binaryen-version_91/third_party/llvm-project/SourceMgr.cpp000066400000000000000000000421401362402614000242410ustar00rootroot00000000000000//===- SourceMgr.cpp - Manager for Simple Source Buffers & Diagnostics ----===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file implements the SourceMgr class. This class is used as a simple // substrate for diagnostics, #include handling, and other low level things for // simple parsers. // //===----------------------------------------------------------------------===// #include "llvm/Support/SourceMgr.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/Locale.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/SMLoc.h" #include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include #include #include #include #include #include #include using namespace llvm; static const size_t TabStop = 8; unsigned SourceMgr::AddIncludeFile(const std::string &Filename, SMLoc IncludeLoc, std::string &IncludedFile) { IncludedFile = Filename; ErrorOr> NewBufOrErr = MemoryBuffer::getFile(IncludedFile); // If the file didn't exist directly, see if it's in an include path. for (unsigned i = 0, e = IncludeDirectories.size(); i != e && !NewBufOrErr; ++i) { IncludedFile = IncludeDirectories[i] + sys::path::get_separator().data() + Filename; NewBufOrErr = MemoryBuffer::getFile(IncludedFile); } if (!NewBufOrErr) return 0; return AddNewSourceBuffer(std::move(*NewBufOrErr), IncludeLoc); } unsigned SourceMgr::FindBufferContainingLoc(SMLoc Loc) const { for (unsigned i = 0, e = Buffers.size(); i != e; ++i) if (Loc.getPointer() >= Buffers[i].Buffer->getBufferStart() && // Use <= here so that a pointer to the null at the end of the buffer // is included as part of the buffer. Loc.getPointer() <= Buffers[i].Buffer->getBufferEnd()) return i + 1; return 0; } template unsigned SourceMgr::SrcBuffer::getLineNumber(const char *Ptr) const { // Ensure OffsetCache is allocated and populated with offsets of all the // '\n' bytes. std::vector *Offsets = nullptr; if (OffsetCache.isNull()) { Offsets = new std::vector(); OffsetCache = Offsets; size_t Sz = Buffer->getBufferSize(); assert(Sz <= std::numeric_limits::max()); StringRef S = Buffer->getBuffer(); for (size_t N = 0; N < Sz; ++N) { if (S[N] == '\n') { Offsets->push_back(static_cast(N)); } } } else { Offsets = OffsetCache.get *>(); } const char *BufStart = Buffer->getBufferStart(); assert(Ptr >= BufStart && Ptr <= Buffer->getBufferEnd()); ptrdiff_t PtrDiff = Ptr - BufStart; assert(PtrDiff >= 0 && static_cast(PtrDiff) <= std::numeric_limits::max()); T PtrOffset = static_cast(PtrDiff); // llvm::lower_bound gives the number of EOL before PtrOffset. Add 1 to get // the line number. return llvm::lower_bound(*Offsets, PtrOffset) - Offsets->begin() + 1; } SourceMgr::SrcBuffer::SrcBuffer(SourceMgr::SrcBuffer &&Other) : Buffer(std::move(Other.Buffer)), OffsetCache(Other.OffsetCache), IncludeLoc(Other.IncludeLoc) { Other.OffsetCache = nullptr; } SourceMgr::SrcBuffer::~SrcBuffer() { if (!OffsetCache.isNull()) { if (OffsetCache.is*>()) delete OffsetCache.get*>(); else if (OffsetCache.is*>()) delete OffsetCache.get*>(); else if (OffsetCache.is*>()) delete OffsetCache.get*>(); else delete OffsetCache.get*>(); OffsetCache = nullptr; } } std::pair SourceMgr::getLineAndColumn(SMLoc Loc, unsigned BufferID) const { if (!BufferID) BufferID = FindBufferContainingLoc(Loc); assert(BufferID && "Invalid Location!"); auto &SB = getBufferInfo(BufferID); const char *Ptr = Loc.getPointer(); size_t Sz = SB.Buffer->getBufferSize(); unsigned LineNo; if (Sz <= std::numeric_limits::max()) LineNo = SB.getLineNumber(Ptr); else if (Sz <= std::numeric_limits::max()) LineNo = SB.getLineNumber(Ptr); else if (Sz <= std::numeric_limits::max()) LineNo = SB.getLineNumber(Ptr); else LineNo = SB.getLineNumber(Ptr); const char *BufStart = SB.Buffer->getBufferStart(); size_t NewlineOffs = StringRef(BufStart, Ptr-BufStart).find_last_of("\n\r"); if (NewlineOffs == StringRef::npos) NewlineOffs = ~(size_t)0; return std::make_pair(LineNo, Ptr-BufStart-NewlineOffs); } void SourceMgr::PrintIncludeStack(SMLoc IncludeLoc, raw_ostream &OS) const { if (IncludeLoc == SMLoc()) return; // Top of stack. unsigned CurBuf = FindBufferContainingLoc(IncludeLoc); assert(CurBuf && "Invalid or unspecified location!"); PrintIncludeStack(getBufferInfo(CurBuf).IncludeLoc, OS); OS << "Included from " << getBufferInfo(CurBuf).Buffer->getBufferIdentifier() << ":" << FindLineNumber(IncludeLoc, CurBuf) << ":\n"; } SMDiagnostic SourceMgr::GetMessage(SMLoc Loc, SourceMgr::DiagKind Kind, const Twine &Msg, ArrayRef Ranges, ArrayRef FixIts) const { // First thing to do: find the current buffer containing the specified // location to pull out the source line. SmallVector, 4> ColRanges; std::pair LineAndCol; StringRef BufferID = ""; std::string LineStr; if (Loc.isValid()) { unsigned CurBuf = FindBufferContainingLoc(Loc); assert(CurBuf && "Invalid or unspecified location!"); const MemoryBuffer *CurMB = getMemoryBuffer(CurBuf); BufferID = CurMB->getBufferIdentifier(); // Scan backward to find the start of the line. const char *LineStart = Loc.getPointer(); const char *BufStart = CurMB->getBufferStart(); while (LineStart != BufStart && LineStart[-1] != '\n' && LineStart[-1] != '\r') --LineStart; // Get the end of the line. const char *LineEnd = Loc.getPointer(); const char *BufEnd = CurMB->getBufferEnd(); while (LineEnd != BufEnd && LineEnd[0] != '\n' && LineEnd[0] != '\r') ++LineEnd; LineStr = std::string(LineStart, LineEnd); // Convert any ranges to column ranges that only intersect the line of the // location. for (unsigned i = 0, e = Ranges.size(); i != e; ++i) { SMRange R = Ranges[i]; if (!R.isValid()) continue; // If the line doesn't contain any part of the range, then ignore it. if (R.Start.getPointer() > LineEnd || R.End.getPointer() < LineStart) continue; // Ignore pieces of the range that go onto other lines. if (R.Start.getPointer() < LineStart) R.Start = SMLoc::getFromPointer(LineStart); if (R.End.getPointer() > LineEnd) R.End = SMLoc::getFromPointer(LineEnd); // Translate from SMLoc ranges to column ranges. // FIXME: Handle multibyte characters. ColRanges.push_back(std::make_pair(R.Start.getPointer()-LineStart, R.End.getPointer()-LineStart)); } LineAndCol = getLineAndColumn(Loc, CurBuf); } return SMDiagnostic(*this, Loc, BufferID, LineAndCol.first, LineAndCol.second-1, Kind, Msg.str(), LineStr, ColRanges, FixIts); } void SourceMgr::PrintMessage(raw_ostream &OS, const SMDiagnostic &Diagnostic, bool ShowColors) const { // Report the message with the diagnostic handler if present. if (DiagHandler) { DiagHandler(Diagnostic, DiagContext); return; } if (Diagnostic.getLoc().isValid()) { unsigned CurBuf = FindBufferContainingLoc(Diagnostic.getLoc()); assert(CurBuf && "Invalid or unspecified location!"); PrintIncludeStack(getBufferInfo(CurBuf).IncludeLoc, OS); } Diagnostic.print(nullptr, OS, ShowColors); } void SourceMgr::PrintMessage(raw_ostream &OS, SMLoc Loc, SourceMgr::DiagKind Kind, const Twine &Msg, ArrayRef Ranges, ArrayRef FixIts, bool ShowColors) const { PrintMessage(OS, GetMessage(Loc, Kind, Msg, Ranges, FixIts), ShowColors); } void SourceMgr::PrintMessage(SMLoc Loc, SourceMgr::DiagKind Kind, const Twine &Msg, ArrayRef Ranges, ArrayRef FixIts, bool ShowColors) const { PrintMessage(errs(), Loc, Kind, Msg, Ranges, FixIts, ShowColors); } //===----------------------------------------------------------------------===// // SMDiagnostic Implementation //===----------------------------------------------------------------------===// SMDiagnostic::SMDiagnostic(const SourceMgr &sm, SMLoc L, StringRef FN, int Line, int Col, SourceMgr::DiagKind Kind, StringRef Msg, StringRef LineStr, ArrayRef> Ranges, ArrayRef Hints) : SM(&sm), Loc(L), Filename(FN), LineNo(Line), ColumnNo(Col), Kind(Kind), Message(Msg), LineContents(LineStr), Ranges(Ranges.vec()), FixIts(Hints.begin(), Hints.end()) { llvm::sort(FixIts); } static void buildFixItLine(std::string &CaretLine, std::string &FixItLine, ArrayRef FixIts, ArrayRef SourceLine){ if (FixIts.empty()) return; const char *LineStart = SourceLine.begin(); const char *LineEnd = SourceLine.end(); size_t PrevHintEndCol = 0; for (ArrayRef::iterator I = FixIts.begin(), E = FixIts.end(); I != E; ++I) { // If the fixit contains a newline or tab, ignore it. if (I->getText().find_first_of("\n\r\t") != StringRef::npos) continue; SMRange R = I->getRange(); // If the line doesn't contain any part of the range, then ignore it. if (R.Start.getPointer() > LineEnd || R.End.getPointer() < LineStart) continue; // Translate from SMLoc to column. // Ignore pieces of the range that go onto other lines. // FIXME: Handle multibyte characters in the source line. unsigned FirstCol; if (R.Start.getPointer() < LineStart) FirstCol = 0; else FirstCol = R.Start.getPointer() - LineStart; // If we inserted a long previous hint, push this one forwards, and add // an extra space to show that this is not part of the previous // completion. This is sort of the best we can do when two hints appear // to overlap. // // Note that if this hint is located immediately after the previous // hint, no space will be added, since the location is more important. unsigned HintCol = FirstCol; if (HintCol < PrevHintEndCol) HintCol = PrevHintEndCol + 1; #if 0 // XXX BINARYEN // FIXME: This assertion is intended to catch unintended use of multibyte // characters in fixits. If we decide to do this, we'll have to track // separate byte widths for the source and fixit lines. assert((size_t)sys::locale::columnWidth(I->getText()) == I->getText().size()); #endif // This relies on one byte per column in our fixit hints. unsigned LastColumnModified = HintCol + I->getText().size(); if (LastColumnModified > FixItLine.size()) FixItLine.resize(LastColumnModified, ' '); std::copy(I->getText().begin(), I->getText().end(), FixItLine.begin() + HintCol); PrevHintEndCol = LastColumnModified; // For replacements, mark the removal range with '~'. // FIXME: Handle multibyte characters in the source line. unsigned LastCol; if (R.End.getPointer() >= LineEnd) LastCol = LineEnd - LineStart; else LastCol = R.End.getPointer() - LineStart; std::fill(&CaretLine[FirstCol], &CaretLine[LastCol], '~'); } } static void printSourceLine(raw_ostream &S, StringRef LineContents) { // Print out the source line one character at a time, so we can expand tabs. for (unsigned i = 0, e = LineContents.size(), OutCol = 0; i != e; ++i) { size_t NextTab = LineContents.find('\t', i); // If there were no tabs left, print the rest, we are done. if (NextTab == StringRef::npos) { S << LineContents.drop_front(i); break; } // Otherwise, print from i to NextTab. S << LineContents.slice(i, NextTab); OutCol += NextTab - i; i = NextTab; // If we have a tab, emit at least one space, then round up to 8 columns. do { S << ' '; ++OutCol; } while ((OutCol % TabStop) != 0); } S << '\n'; } static bool isNonASCII(char c) { return c & 0x80; } void SMDiagnostic::print(const char *ProgName, raw_ostream &OS, bool ShowColors, bool ShowKindLabel) const { { WithColor S(OS, raw_ostream::SAVEDCOLOR, true, false, !ShowColors); if (ProgName && ProgName[0]) S << ProgName << ": "; if (!Filename.empty()) { if (Filename == "-") S << ""; else S << Filename; if (LineNo != -1) { S << ':' << LineNo; if (ColumnNo != -1) S << ':' << (ColumnNo + 1); } S << ": "; } } if (ShowKindLabel) { switch (Kind) { case SourceMgr::DK_Error: WithColor::error(OS, "", !ShowColors); break; case SourceMgr::DK_Warning: WithColor::warning(OS, "", !ShowColors); break; case SourceMgr::DK_Note: WithColor::note(OS, "", !ShowColors); break; case SourceMgr::DK_Remark: WithColor::remark(OS, "", !ShowColors); break; } } WithColor(OS, raw_ostream::SAVEDCOLOR, true, false, !ShowColors) << Message << '\n'; if (LineNo == -1 || ColumnNo == -1) return; // FIXME: If there are multibyte or multi-column characters in the source, all // our ranges will be wrong. To do this properly, we'll need a byte-to-column // map like Clang's TextDiagnostic. For now, we'll just handle tabs by // expanding them later, and bail out rather than show incorrect ranges and // misaligned fixits for any other odd characters. if (find_if(LineContents, isNonASCII) != LineContents.end()) { printSourceLine(OS, LineContents); return; } size_t NumColumns = LineContents.size(); // Build the line with the caret and ranges. std::string CaretLine(NumColumns+1, ' '); // Expand any ranges. for (unsigned r = 0, e = Ranges.size(); r != e; ++r) { std::pair R = Ranges[r]; std::fill(&CaretLine[R.first], &CaretLine[std::min((size_t)R.second, CaretLine.size())], '~'); } // Add any fix-its. // FIXME: Find the beginning of the line properly for multibyte characters. std::string FixItInsertionLine; buildFixItLine(CaretLine, FixItInsertionLine, FixIts, makeArrayRef(Loc.getPointer() - ColumnNo, LineContents.size())); // Finally, plop on the caret. if (unsigned(ColumnNo) <= NumColumns) CaretLine[ColumnNo] = '^'; else CaretLine[NumColumns] = '^'; // ... and remove trailing whitespace so the output doesn't wrap for it. We // know that the line isn't completely empty because it has the caret in it at // least. CaretLine.erase(CaretLine.find_last_not_of(' ')+1); printSourceLine(OS, LineContents); { WithColor S(OS, raw_ostream::GREEN, true, false, !ShowColors); // Print out the caret line, matching tabs in the source line. for (unsigned i = 0, e = CaretLine.size(), OutCol = 0; i != e; ++i) { if (i >= LineContents.size() || LineContents[i] != '\t') { S << CaretLine[i]; ++OutCol; continue; } // Okay, we have a tab. Insert the appropriate number of characters. do { S << CaretLine[i]; ++OutCol; } while ((OutCol % TabStop) != 0); } S << '\n'; } // Print out the replacement line, matching tabs in the source line. if (FixItInsertionLine.empty()) return; for (size_t i = 0, e = FixItInsertionLine.size(), OutCol = 0; i < e; ++i) { if (i >= LineContents.size() || LineContents[i] != '\t') { OS << FixItInsertionLine[i]; ++OutCol; continue; } // Okay, we have a tab. Insert the appropriate number of characters. do { OS << FixItInsertionLine[i]; // FIXME: This is trying not to break up replacements, but then to re-sync // with the tabs between replacements. This will fail, though, if two // fix-it replacements are exactly adjacent, or if a fix-it contains a // space. Really we should be precomputing column widths, which we'll // need anyway for multibyte chars. if (FixItInsertionLine[i] != ' ') ++i; ++OutCol; } while (((OutCol % TabStop) != 0) && i != e); } OS << '\n'; } binaryen-version_91/third_party/llvm-project/StringMap.cpp000066400000000000000000000225241362402614000242430ustar00rootroot00000000000000//===--- StringMap.cpp - String Hash table map implementation -------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file implements the StringMap class. // //===----------------------------------------------------------------------===// #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/DJB.h" #include "llvm/Support/MathExtras.h" #include using namespace llvm; /// Returns the number of buckets to allocate to ensure that the DenseMap can /// accommodate \p NumEntries without need to grow(). static unsigned getMinBucketToReserveForEntries(unsigned NumEntries) { // Ensure that "NumEntries * 4 < NumBuckets * 3" if (NumEntries == 0) return 0; // +1 is required because of the strict equality. // For example if NumEntries is 48, we need to return 401. return NextPowerOf2(NumEntries * 4 / 3 + 1); } StringMapImpl::StringMapImpl(unsigned InitSize, unsigned itemSize) { ItemSize = itemSize; // If a size is specified, initialize the table with that many buckets. if (InitSize) { // The table will grow when the number of entries reach 3/4 of the number of // buckets. To guarantee that "InitSize" number of entries can be inserted // in the table without growing, we allocate just what is needed here. init(getMinBucketToReserveForEntries(InitSize)); return; } // Otherwise, initialize it with zero buckets to avoid the allocation. TheTable = nullptr; NumBuckets = 0; NumItems = 0; NumTombstones = 0; } void StringMapImpl::init(unsigned InitSize) { assert((InitSize & (InitSize-1)) == 0 && "Init Size must be a power of 2 or zero!"); unsigned NewNumBuckets = InitSize ? InitSize : 16; NumItems = 0; NumTombstones = 0; TheTable = static_cast( safe_calloc(NewNumBuckets+1, sizeof(StringMapEntryBase **) + sizeof(unsigned))); // Set the member only if TheTable was successfully allocated NumBuckets = NewNumBuckets; // Allocate one extra bucket, set it to look filled so the iterators stop at // end. TheTable[NumBuckets] = (StringMapEntryBase*)2; } /// LookupBucketFor - Look up the bucket that the specified string should end /// up in. If it already exists as a key in the map, the Item pointer for the /// specified bucket will be non-null. Otherwise, it will be null. In either /// case, the FullHashValue field of the bucket will be set to the hash value /// of the string. unsigned StringMapImpl::LookupBucketFor(StringRef Name) { unsigned HTSize = NumBuckets; if (HTSize == 0) { // Hash table unallocated so far? init(16); HTSize = NumBuckets; } unsigned FullHashValue = djbHash(Name, 0); unsigned BucketNo = FullHashValue & (HTSize-1); unsigned *HashTable = (unsigned *)(TheTable + NumBuckets + 1); unsigned ProbeAmt = 1; int FirstTombstone = -1; while (true) { StringMapEntryBase *BucketItem = TheTable[BucketNo]; // If we found an empty bucket, this key isn't in the table yet, return it. if (LLVM_LIKELY(!BucketItem)) { // If we found a tombstone, we want to reuse the tombstone instead of an // empty bucket. This reduces probing. if (FirstTombstone != -1) { HashTable[FirstTombstone] = FullHashValue; return FirstTombstone; } HashTable[BucketNo] = FullHashValue; return BucketNo; } if (BucketItem == getTombstoneVal()) { // Skip over tombstones. However, remember the first one we see. if (FirstTombstone == -1) FirstTombstone = BucketNo; } else if (LLVM_LIKELY(HashTable[BucketNo] == FullHashValue)) { // If the full hash value matches, check deeply for a match. The common // case here is that we are only looking at the buckets (for item info // being non-null and for the full hash value) not at the items. This // is important for cache locality. // Do the comparison like this because Name isn't necessarily // null-terminated! char *ItemStr = (char*)BucketItem+ItemSize; if (Name == StringRef(ItemStr, BucketItem->getKeyLength())) { // We found a match! return BucketNo; } } // Okay, we didn't find the item. Probe to the next bucket. BucketNo = (BucketNo+ProbeAmt) & (HTSize-1); // Use quadratic probing, it has fewer clumping artifacts than linear // probing and has good cache behavior in the common case. ++ProbeAmt; } } /// FindKey - Look up the bucket that contains the specified key. If it exists /// in the map, return the bucket number of the key. Otherwise return -1. /// This does not modify the map. int StringMapImpl::FindKey(StringRef Key) const { unsigned HTSize = NumBuckets; if (HTSize == 0) return -1; // Really empty table? unsigned FullHashValue = djbHash(Key, 0); unsigned BucketNo = FullHashValue & (HTSize-1); unsigned *HashTable = (unsigned *)(TheTable + NumBuckets + 1); unsigned ProbeAmt = 1; while (true) { StringMapEntryBase *BucketItem = TheTable[BucketNo]; // If we found an empty bucket, this key isn't in the table yet, return. if (LLVM_LIKELY(!BucketItem)) return -1; if (BucketItem == getTombstoneVal()) { // Ignore tombstones. } else if (LLVM_LIKELY(HashTable[BucketNo] == FullHashValue)) { // If the full hash value matches, check deeply for a match. The common // case here is that we are only looking at the buckets (for item info // being non-null and for the full hash value) not at the items. This // is important for cache locality. // Do the comparison like this because NameStart isn't necessarily // null-terminated! char *ItemStr = (char*)BucketItem+ItemSize; if (Key == StringRef(ItemStr, BucketItem->getKeyLength())) { // We found a match! return BucketNo; } } // Okay, we didn't find the item. Probe to the next bucket. BucketNo = (BucketNo+ProbeAmt) & (HTSize-1); // Use quadratic probing, it has fewer clumping artifacts than linear // probing and has good cache behavior in the common case. ++ProbeAmt; } } /// RemoveKey - Remove the specified StringMapEntry from the table, but do not /// delete it. This aborts if the value isn't in the table. void StringMapImpl::RemoveKey(StringMapEntryBase *V) { const char *VStr = (char*)V + ItemSize; StringMapEntryBase *V2 = RemoveKey(StringRef(VStr, V->getKeyLength())); (void)V2; assert(V == V2 && "Didn't find key?"); } /// RemoveKey - Remove the StringMapEntry for the specified key from the /// table, returning it. If the key is not in the table, this returns null. StringMapEntryBase *StringMapImpl::RemoveKey(StringRef Key) { int Bucket = FindKey(Key); if (Bucket == -1) return nullptr; StringMapEntryBase *Result = TheTable[Bucket]; TheTable[Bucket] = getTombstoneVal(); --NumItems; ++NumTombstones; assert(NumItems + NumTombstones <= NumBuckets); return Result; } /// RehashTable - Grow the table, redistributing values into the buckets with /// the appropriate mod-of-hashtable-size. unsigned StringMapImpl::RehashTable(unsigned BucketNo) { unsigned NewSize; unsigned *HashTable = (unsigned *)(TheTable + NumBuckets + 1); // If the hash table is now more than 3/4 full, or if fewer than 1/8 of // the buckets are empty (meaning that many are filled with tombstones), // grow/rehash the table. if (LLVM_UNLIKELY(NumItems * 4 > NumBuckets * 3)) { NewSize = NumBuckets*2; } else if (LLVM_UNLIKELY(NumBuckets - (NumItems + NumTombstones) <= NumBuckets / 8)) { NewSize = NumBuckets; } else { return BucketNo; } unsigned NewBucketNo = BucketNo; // Allocate one extra bucket which will always be non-empty. This allows the // iterators to stop at end. auto NewTableArray = static_cast( safe_calloc(NewSize+1, sizeof(StringMapEntryBase *) + sizeof(unsigned))); unsigned *NewHashArray = (unsigned *)(NewTableArray + NewSize + 1); NewTableArray[NewSize] = (StringMapEntryBase*)2; // Rehash all the items into their new buckets. Luckily :) we already have // the hash values available, so we don't have to rehash any strings. for (unsigned I = 0, E = NumBuckets; I != E; ++I) { StringMapEntryBase *Bucket = TheTable[I]; if (Bucket && Bucket != getTombstoneVal()) { // Fast case, bucket available. unsigned FullHash = HashTable[I]; unsigned NewBucket = FullHash & (NewSize-1); if (!NewTableArray[NewBucket]) { NewTableArray[FullHash & (NewSize-1)] = Bucket; NewHashArray[FullHash & (NewSize-1)] = FullHash; if (I == BucketNo) NewBucketNo = NewBucket; continue; } // Otherwise probe for a spot. unsigned ProbeSize = 1; do { NewBucket = (NewBucket + ProbeSize++) & (NewSize-1); } while (NewTableArray[NewBucket]); // Finally found a slot. Fill it in. NewTableArray[NewBucket] = Bucket; NewHashArray[NewBucket] = FullHash; if (I == BucketNo) NewBucketNo = NewBucket; } } free(TheTable); TheTable = NewTableArray; NumBuckets = NewSize; NumTombstones = 0; return NewBucketNo; } binaryen-version_91/third_party/llvm-project/StringRef.cpp000066400000000000000000000363711362402614000242470ustar00rootroot00000000000000//===-- StringRef.cpp - Lightweight String References ---------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/ADT/StringRef.h" #include "llvm/ADT/APFloat.h" #include "llvm/ADT/APInt.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/edit_distance.h" #include using namespace llvm; // MSVC emits references to this into the translation units which reference it. #ifndef _MSC_VER const size_t StringRef::npos; #endif // strncasecmp() is not available on non-POSIX systems, so define an // alternative function here. static int ascii_strncasecmp(const char *LHS, const char *RHS, size_t Length) { for (size_t I = 0; I < Length; ++I) { unsigned char LHC = toLower(LHS[I]); unsigned char RHC = toLower(RHS[I]); if (LHC != RHC) return LHC < RHC ? -1 : 1; } return 0; } /// compare_lower - Compare strings, ignoring case. int StringRef::compare_lower(StringRef RHS) const { if (int Res = ascii_strncasecmp(Data, RHS.Data, std::min(Length, RHS.Length))) return Res; if (Length == RHS.Length) return 0; return Length < RHS.Length ? -1 : 1; } /// Check if this string starts with the given \p Prefix, ignoring case. bool StringRef::startswith_lower(StringRef Prefix) const { return Length >= Prefix.Length && ascii_strncasecmp(Data, Prefix.Data, Prefix.Length) == 0; } /// Check if this string ends with the given \p Suffix, ignoring case. bool StringRef::endswith_lower(StringRef Suffix) const { return Length >= Suffix.Length && ascii_strncasecmp(end() - Suffix.Length, Suffix.Data, Suffix.Length) == 0; } size_t StringRef::find_lower(char C, size_t From) const { char L = toLower(C); return find_if([L](char D) { return toLower(D) == L; }, From); } /// compare_numeric - Compare strings, handle embedded numbers. int StringRef::compare_numeric(StringRef RHS) const { for (size_t I = 0, E = std::min(Length, RHS.Length); I != E; ++I) { // Check for sequences of digits. if (isDigit(Data[I]) && isDigit(RHS.Data[I])) { // The longer sequence of numbers is considered larger. // This doesn't really handle prefixed zeros well. size_t J; for (J = I + 1; J != E + 1; ++J) { bool ld = J < Length && isDigit(Data[J]); bool rd = J < RHS.Length && isDigit(RHS.Data[J]); if (ld != rd) return rd ? -1 : 1; if (!rd) break; } // The two number sequences have the same length (J-I), just memcmp them. if (int Res = compareMemory(Data + I, RHS.Data + I, J - I)) return Res < 0 ? -1 : 1; // Identical number sequences, continue search after the numbers. I = J - 1; continue; } if (Data[I] != RHS.Data[I]) return (unsigned char)Data[I] < (unsigned char)RHS.Data[I] ? -1 : 1; } if (Length == RHS.Length) return 0; return Length < RHS.Length ? -1 : 1; } // Compute the edit distance between the two given strings. unsigned StringRef::edit_distance(llvm::StringRef Other, bool AllowReplacements, unsigned MaxEditDistance) const { return llvm::ComputeEditDistance( makeArrayRef(data(), size()), makeArrayRef(Other.data(), Other.size()), AllowReplacements, MaxEditDistance); } //===----------------------------------------------------------------------===// // String Operations //===----------------------------------------------------------------------===// std::string StringRef::lower() const { std::string Result(size(), char()); for (size_type i = 0, e = size(); i != e; ++i) { Result[i] = toLower(Data[i]); } return Result; } std::string StringRef::upper() const { std::string Result(size(), char()); for (size_type i = 0, e = size(); i != e; ++i) { Result[i] = toUpper(Data[i]); } return Result; } //===----------------------------------------------------------------------===// // String Searching //===----------------------------------------------------------------------===// /// find - Search for the first string \arg Str in the string. /// /// \return - The index of the first occurrence of \arg Str, or npos if not /// found. size_t StringRef::find(StringRef Str, size_t From) const { if (From > Length) return npos; const char *Start = Data + From; size_t Size = Length - From; const char *Needle = Str.data(); size_t N = Str.size(); if (N == 0) return From; if (Size < N) return npos; if (N == 1) { const char *Ptr = (const char *)::memchr(Start, Needle[0], Size); return Ptr == nullptr ? npos : Ptr - Data; } const char *Stop = Start + (Size - N + 1); // For short haystacks or unsupported needles fall back to the naive algorithm if (Size < 16 || N > 255) { do { if (std::memcmp(Start, Needle, N) == 0) return Start - Data; ++Start; } while (Start < Stop); return npos; } // Build the bad char heuristic table, with uint8_t to reduce cache thrashing. uint8_t BadCharSkip[256]; std::memset(BadCharSkip, N, 256); for (unsigned i = 0; i != N-1; ++i) BadCharSkip[(uint8_t)Str[i]] = N-1-i; do { uint8_t Last = Start[N - 1]; if (LLVM_UNLIKELY(Last == (uint8_t)Needle[N - 1])) if (std::memcmp(Start, Needle, N - 1) == 0) return Start - Data; // Otherwise skip the appropriate number of bytes. Start += BadCharSkip[Last]; } while (Start < Stop); return npos; } size_t StringRef::find_lower(StringRef Str, size_t From) const { StringRef This = substr(From); while (This.size() >= Str.size()) { if (This.startswith_lower(Str)) return From; This = This.drop_front(); ++From; } return npos; } size_t StringRef::rfind_lower(char C, size_t From) const { From = std::min(From, Length); size_t i = From; while (i != 0) { --i; if (toLower(Data[i]) == toLower(C)) return i; } return npos; } /// rfind - Search for the last string \arg Str in the string. /// /// \return - The index of the last occurrence of \arg Str, or npos if not /// found. size_t StringRef::rfind(StringRef Str) const { size_t N = Str.size(); if (N > Length) return npos; for (size_t i = Length - N + 1, e = 0; i != e;) { --i; if (substr(i, N).equals(Str)) return i; } return npos; } size_t StringRef::rfind_lower(StringRef Str) const { size_t N = Str.size(); if (N > Length) return npos; for (size_t i = Length - N + 1, e = 0; i != e;) { --i; if (substr(i, N).equals_lower(Str)) return i; } return npos; } /// find_first_of - Find the first character in the string that is in \arg /// Chars, or npos if not found. /// /// Note: O(size() + Chars.size()) StringRef::size_type StringRef::find_first_of(StringRef Chars, size_t From) const { std::bitset<1 << CHAR_BIT> CharBits; for (size_type i = 0; i != Chars.size(); ++i) CharBits.set((unsigned char)Chars[i]); for (size_type i = std::min(From, Length), e = Length; i != e; ++i) if (CharBits.test((unsigned char)Data[i])) return i; return npos; } /// find_first_not_of - Find the first character in the string that is not /// \arg C or npos if not found. StringRef::size_type StringRef::find_first_not_of(char C, size_t From) const { for (size_type i = std::min(From, Length), e = Length; i != e; ++i) if (Data[i] != C) return i; return npos; } /// find_first_not_of - Find the first character in the string that is not /// in the string \arg Chars, or npos if not found. /// /// Note: O(size() + Chars.size()) StringRef::size_type StringRef::find_first_not_of(StringRef Chars, size_t From) const { std::bitset<1 << CHAR_BIT> CharBits; for (size_type i = 0; i != Chars.size(); ++i) CharBits.set((unsigned char)Chars[i]); for (size_type i = std::min(From, Length), e = Length; i != e; ++i) if (!CharBits.test((unsigned char)Data[i])) return i; return npos; } /// find_last_of - Find the last character in the string that is in \arg C, /// or npos if not found. /// /// Note: O(size() + Chars.size()) StringRef::size_type StringRef::find_last_of(StringRef Chars, size_t From) const { std::bitset<1 << CHAR_BIT> CharBits; for (size_type i = 0; i != Chars.size(); ++i) CharBits.set((unsigned char)Chars[i]); for (size_type i = std::min(From, Length) - 1, e = -1; i != e; --i) if (CharBits.test((unsigned char)Data[i])) return i; return npos; } /// find_last_not_of - Find the last character in the string that is not /// \arg C, or npos if not found. StringRef::size_type StringRef::find_last_not_of(char C, size_t From) const { for (size_type i = std::min(From, Length) - 1, e = -1; i != e; --i) if (Data[i] != C) return i; return npos; } /// find_last_not_of - Find the last character in the string that is not in /// \arg Chars, or npos if not found. /// /// Note: O(size() + Chars.size()) StringRef::size_type StringRef::find_last_not_of(StringRef Chars, size_t From) const { std::bitset<1 << CHAR_BIT> CharBits; for (size_type i = 0, e = Chars.size(); i != e; ++i) CharBits.set((unsigned char)Chars[i]); for (size_type i = std::min(From, Length) - 1, e = -1; i != e; --i) if (!CharBits.test((unsigned char)Data[i])) return i; return npos; } void StringRef::split(SmallVectorImpl &A, StringRef Separator, int MaxSplit, bool KeepEmpty) const { StringRef S = *this; // Count down from MaxSplit. When MaxSplit is -1, this will just split // "forever". This doesn't support splitting more than 2^31 times // intentionally; if we ever want that we can make MaxSplit a 64-bit integer // but that seems unlikely to be useful. while (MaxSplit-- != 0) { size_t Idx = S.find(Separator); if (Idx == npos) break; // Push this split. if (KeepEmpty || Idx > 0) A.push_back(S.slice(0, Idx)); // Jump forward. S = S.slice(Idx + Separator.size(), npos); } // Push the tail. if (KeepEmpty || !S.empty()) A.push_back(S); } void StringRef::split(SmallVectorImpl &A, char Separator, int MaxSplit, bool KeepEmpty) const { StringRef S = *this; // Count down from MaxSplit. When MaxSplit is -1, this will just split // "forever". This doesn't support splitting more than 2^31 times // intentionally; if we ever want that we can make MaxSplit a 64-bit integer // but that seems unlikely to be useful. while (MaxSplit-- != 0) { size_t Idx = S.find(Separator); if (Idx == npos) break; // Push this split. if (KeepEmpty || Idx > 0) A.push_back(S.slice(0, Idx)); // Jump forward. S = S.slice(Idx + 1, npos); } // Push the tail. if (KeepEmpty || !S.empty()) A.push_back(S); } //===----------------------------------------------------------------------===// // Helpful Algorithms //===----------------------------------------------------------------------===// /// count - Return the number of non-overlapped occurrences of \arg Str in /// the string. size_t StringRef::count(StringRef Str) const { size_t Count = 0; size_t N = Str.size(); if (N > Length) return 0; for (size_t i = 0, e = Length - N + 1; i != e; ++i) if (substr(i, N).equals(Str)) ++Count; return Count; } static unsigned GetAutoSenseRadix(StringRef &Str) { if (Str.empty()) return 10; if (Str.startswith("0x") || Str.startswith("0X")) { Str = Str.substr(2); return 16; } if (Str.startswith("0b") || Str.startswith("0B")) { Str = Str.substr(2); return 2; } if (Str.startswith("0o")) { Str = Str.substr(2); return 8; } if (Str[0] == '0' && Str.size() > 1 && isDigit(Str[1])) { Str = Str.substr(1); return 8; } return 10; } bool llvm::consumeUnsignedInteger(StringRef &Str, unsigned Radix, unsigned long long &Result) { // Autosense radix if not specified. if (Radix == 0) Radix = GetAutoSenseRadix(Str); // Empty strings (after the radix autosense) are invalid. if (Str.empty()) return true; // Parse all the bytes of the string given this radix. Watch for overflow. StringRef Str2 = Str; Result = 0; while (!Str2.empty()) { unsigned CharVal; if (Str2[0] >= '0' && Str2[0] <= '9') CharVal = Str2[0] - '0'; else if (Str2[0] >= 'a' && Str2[0] <= 'z') CharVal = Str2[0] - 'a' + 10; else if (Str2[0] >= 'A' && Str2[0] <= 'Z') CharVal = Str2[0] - 'A' + 10; else break; // If the parsed value is larger than the integer radix, we cannot // consume any more characters. if (CharVal >= Radix) break; // Add in this character. unsigned long long PrevResult = Result; Result = Result * Radix + CharVal; // Check for overflow by shifting back and seeing if bits were lost. if (Result / Radix < PrevResult) return true; Str2 = Str2.substr(1); } // We consider the operation a failure if no characters were consumed // successfully. if (Str.size() == Str2.size()) return true; Str = Str2; return false; } bool llvm::consumeSignedInteger(StringRef &Str, unsigned Radix, long long &Result) { unsigned long long ULLVal; // Handle positive strings first. if (Str.empty() || Str.front() != '-') { if (consumeUnsignedInteger(Str, Radix, ULLVal) || // Check for value so large it overflows a signed value. (long long)ULLVal < 0) return true; Result = ULLVal; return false; } // Get the positive part of the value. StringRef Str2 = Str.drop_front(1); if (consumeUnsignedInteger(Str2, Radix, ULLVal) || // Reject values so large they'd overflow as negative signed, but allow // "-0". This negates the unsigned so that the negative isn't undefined // on signed overflow. (long long)-ULLVal > 0) return true; Str = Str2; Result = -ULLVal; return false; } /// GetAsUnsignedInteger - Workhorse method that converts a integer character /// sequence of radix up to 36 to an unsigned long long value. bool llvm::getAsUnsignedInteger(StringRef Str, unsigned Radix, unsigned long long &Result) { if (consumeUnsignedInteger(Str, Radix, Result)) return true; // For getAsUnsignedInteger, we require the whole string to be consumed or // else we consider it a failure. return !Str.empty(); } bool llvm::getAsSignedInteger(StringRef Str, unsigned Radix, long long &Result) { if (consumeSignedInteger(Str, Radix, Result)) return true; // For getAsSignedInteger, we require the whole string to be consumed or else // we consider it a failure. return !Str.empty(); } bool StringRef::getAsInteger(unsigned Radix, APInt &Result) const { llvm_unreachable("getAsinteger"); } bool StringRef::getAsDouble(double &Result, bool AllowInexact) const { llvm_unreachable("getAsDouble"); } // Implementation of StringRef hashing. hash_code llvm::hash_value(StringRef S) { return hash_combine_range(S.begin(), S.end()); } binaryen-version_91/third_party/llvm-project/SymbolicFile.cpp000066400000000000000000000027301362402614000247150ustar00rootroot00000000000000//===- SymbolicFile.cpp - Interface that only provides symbols ------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file defines a file format independent SymbolicFile class. // //===----------------------------------------------------------------------===// #include "llvm/Object/SymbolicFile.h" #include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/Magic.h" #if 0 // XXX BINARYEN #include "llvm/Object/COFFImportFile.h" #endif #include "llvm/Object/Error.h" #if 0 // XXX BINARYEN #include "llvm/Object/IRObjectFile.h" #endif #include "llvm/Object/ObjectFile.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/Error.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" #include #include using namespace llvm; using namespace object; SymbolicFile::SymbolicFile(unsigned int Type, MemoryBufferRef Source) : Binary(Type, Source) {} SymbolicFile::~SymbolicFile() = default; Expected> SymbolicFile::createSymbolicFile(MemoryBufferRef Object, file_magic Type, LLVMContext *Context) { llvm_unreachable("createSymbolicFile"); } binaryen-version_91/third_party/llvm-project/Twine.cpp000066400000000000000000000111451362402614000234220ustar00rootroot00000000000000//===-- Twine.cpp - Fast Temporary String Concatenation -------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/ADT/Twine.h" #include "llvm/ADT/SmallString.h" #include "llvm/Config/llvm-config.h" #include "llvm/Support/Debug.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; std::string Twine::str() const { // If we're storing only a std::string, just return it. if (LHSKind == StdStringKind && RHSKind == EmptyKind) return *LHS.stdString; // If we're storing a formatv_object, we can avoid an extra copy by formatting // it immediately and returning the result. if (LHSKind == FormatvObjectKind && RHSKind == EmptyKind) return LHS.formatvObject->str(); // Otherwise, flatten and copy the contents first. SmallString<256> Vec; return toStringRef(Vec).str(); } void Twine::toVector(SmallVectorImpl &Out) const { raw_svector_ostream OS(Out); print(OS); } StringRef Twine::toNullTerminatedStringRef(SmallVectorImpl &Out) const { if (isUnary()) { switch (getLHSKind()) { case CStringKind: // Already null terminated, yay! return StringRef(LHS.cString); case StdStringKind: { const std::string *str = LHS.stdString; return StringRef(str->c_str(), str->size()); } default: break; } } toVector(Out); Out.push_back(0); Out.pop_back(); return StringRef(Out.data(), Out.size()); } void Twine::printOneChild(raw_ostream &OS, Child Ptr, NodeKind Kind) const { switch (Kind) { case Twine::NullKind: break; case Twine::EmptyKind: break; case Twine::TwineKind: Ptr.twine->print(OS); break; case Twine::CStringKind: OS << Ptr.cString; break; case Twine::StdStringKind: OS << *Ptr.stdString; break; case Twine::StringRefKind: OS << *Ptr.stringRef; break; case Twine::SmallStringKind: OS << *Ptr.smallString; break; case Twine::FormatvObjectKind: OS << *Ptr.formatvObject; break; case Twine::CharKind: OS << Ptr.character; break; case Twine::DecUIKind: OS << Ptr.decUI; break; case Twine::DecIKind: OS << Ptr.decI; break; case Twine::DecULKind: OS << *Ptr.decUL; break; case Twine::DecLKind: OS << *Ptr.decL; break; case Twine::DecULLKind: OS << *Ptr.decULL; break; case Twine::DecLLKind: OS << *Ptr.decLL; break; case Twine::UHexKind: OS.write_hex(*Ptr.uHex); break; } } void Twine::printOneChildRepr(raw_ostream &OS, Child Ptr, NodeKind Kind) const { switch (Kind) { case Twine::NullKind: OS << "null"; break; case Twine::EmptyKind: OS << "empty"; break; case Twine::TwineKind: OS << "rope:"; Ptr.twine->printRepr(OS); break; case Twine::CStringKind: OS << "cstring:\"" << Ptr.cString << "\""; break; case Twine::StdStringKind: OS << "std::string:\"" << Ptr.stdString << "\""; break; case Twine::StringRefKind: OS << "stringref:\"" << Ptr.stringRef << "\""; break; case Twine::SmallStringKind: OS << "smallstring:\"" << *Ptr.smallString << "\""; break; case Twine::FormatvObjectKind: OS << "formatv:\"" << *Ptr.formatvObject << "\""; break; case Twine::CharKind: OS << "char:\"" << Ptr.character << "\""; break; case Twine::DecUIKind: OS << "decUI:\"" << Ptr.decUI << "\""; break; case Twine::DecIKind: OS << "decI:\"" << Ptr.decI << "\""; break; case Twine::DecULKind: OS << "decUL:\"" << *Ptr.decUL << "\""; break; case Twine::DecLKind: OS << "decL:\"" << *Ptr.decL << "\""; break; case Twine::DecULLKind: OS << "decULL:\"" << *Ptr.decULL << "\""; break; case Twine::DecLLKind: OS << "decLL:\"" << *Ptr.decLL << "\""; break; case Twine::UHexKind: OS << "uhex:\"" << Ptr.uHex << "\""; break; } } void Twine::print(raw_ostream &OS) const { printOneChild(OS, LHS, getLHSKind()); printOneChild(OS, RHS, getRHSKind()); } void Twine::printRepr(raw_ostream &OS) const { OS << "(Twine "; printOneChildRepr(OS, LHS, getLHSKind()); OS << " "; printOneChildRepr(OS, RHS, getRHSKind()); OS << ")"; } #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) LLVM_DUMP_METHOD void Twine::dump() const { print(dbgs()); } LLVM_DUMP_METHOD void Twine::dumpRepr() const { printRepr(dbgs()); } #endif binaryen-version_91/third_party/llvm-project/UnicodeCaseFold.cpp000066400000000000000000000365001362402614000253250ustar00rootroot00000000000000//===---------- Support/UnicodeCaseFold.cpp -------------------------------===// // // This file was generated by utils/unicode-case-fold.py from the Unicode // case folding database at // http://www.unicode.org/Public/9.0.0/ucd/CaseFolding.txt // // To regenerate this file, run: // utils/unicode-case-fold.py // "http://www.unicode.org/Public/9.0.0/ucd/CaseFolding.txt" // > lib/Support/UnicodeCaseFold.cpp // //===----------------------------------------------------------------------===// #include "llvm/Support/Unicode.h" int llvm::sys::unicode::foldCharSimple(int C) { if (C < 0x0041) return C; // 26 characters if (C <= 0x005a) return C + 32; // MICRO SIGN if (C == 0x00b5) return 0x03bc; if (C < 0x00c0) return C; // 23 characters if (C <= 0x00d6) return C + 32; if (C < 0x00d8) return C; // 7 characters if (C <= 0x00de) return C + 32; if (C < 0x0100) return C; // 24 characters if (C <= 0x012e) return C | 1; if (C < 0x0132) return C; // 3 characters if (C <= 0x0136) return C | 1; if (C < 0x0139) return C; // 8 characters if (C <= 0x0147 && C % 2 == 1) return C + 1; if (C < 0x014a) return C; // 23 characters if (C <= 0x0176) return C | 1; // LATIN CAPITAL LETTER Y WITH DIAERESIS if (C == 0x0178) return 0x00ff; if (C < 0x0179) return C; // 3 characters if (C <= 0x017d && C % 2 == 1) return C + 1; // LATIN SMALL LETTER LONG S if (C == 0x017f) return 0x0073; // LATIN CAPITAL LETTER B WITH HOOK if (C == 0x0181) return 0x0253; if (C < 0x0182) return C; // 2 characters if (C <= 0x0184) return C | 1; // LATIN CAPITAL LETTER OPEN O if (C == 0x0186) return 0x0254; // LATIN CAPITAL LETTER C WITH HOOK if (C == 0x0187) return 0x0188; if (C < 0x0189) return C; // 2 characters if (C <= 0x018a) return C + 205; // LATIN CAPITAL LETTER D WITH TOPBAR if (C == 0x018b) return 0x018c; // LATIN CAPITAL LETTER REVERSED E if (C == 0x018e) return 0x01dd; // LATIN CAPITAL LETTER SCHWA if (C == 0x018f) return 0x0259; // LATIN CAPITAL LETTER OPEN E if (C == 0x0190) return 0x025b; // LATIN CAPITAL LETTER F WITH HOOK if (C == 0x0191) return 0x0192; // LATIN CAPITAL LETTER G WITH HOOK if (C == 0x0193) return 0x0260; // LATIN CAPITAL LETTER GAMMA if (C == 0x0194) return 0x0263; // LATIN CAPITAL LETTER IOTA if (C == 0x0196) return 0x0269; // LATIN CAPITAL LETTER I WITH STROKE if (C == 0x0197) return 0x0268; // LATIN CAPITAL LETTER K WITH HOOK if (C == 0x0198) return 0x0199; // LATIN CAPITAL LETTER TURNED M if (C == 0x019c) return 0x026f; // LATIN CAPITAL LETTER N WITH LEFT HOOK if (C == 0x019d) return 0x0272; // LATIN CAPITAL LETTER O WITH MIDDLE TILDE if (C == 0x019f) return 0x0275; if (C < 0x01a0) return C; // 3 characters if (C <= 0x01a4) return C | 1; // LATIN LETTER YR if (C == 0x01a6) return 0x0280; // LATIN CAPITAL LETTER TONE TWO if (C == 0x01a7) return 0x01a8; // LATIN CAPITAL LETTER ESH if (C == 0x01a9) return 0x0283; // LATIN CAPITAL LETTER T WITH HOOK if (C == 0x01ac) return 0x01ad; // LATIN CAPITAL LETTER T WITH RETROFLEX HOOK if (C == 0x01ae) return 0x0288; // LATIN CAPITAL LETTER U WITH HORN if (C == 0x01af) return 0x01b0; if (C < 0x01b1) return C; // 2 characters if (C <= 0x01b2) return C + 217; if (C < 0x01b3) return C; // 2 characters if (C <= 0x01b5 && C % 2 == 1) return C + 1; // LATIN CAPITAL LETTER EZH if (C == 0x01b7) return 0x0292; if (C < 0x01b8) return C; // 2 characters if (C <= 0x01bc && C % 4 == 0) return C + 1; // LATIN CAPITAL LETTER DZ WITH CARON if (C == 0x01c4) return 0x01c6; // LATIN CAPITAL LETTER D WITH SMALL LETTER Z WITH CARON if (C == 0x01c5) return 0x01c6; // LATIN CAPITAL LETTER LJ if (C == 0x01c7) return 0x01c9; // LATIN CAPITAL LETTER L WITH SMALL LETTER J if (C == 0x01c8) return 0x01c9; // LATIN CAPITAL LETTER NJ if (C == 0x01ca) return 0x01cc; if (C < 0x01cb) return C; // 9 characters if (C <= 0x01db && C % 2 == 1) return C + 1; if (C < 0x01de) return C; // 9 characters if (C <= 0x01ee) return C | 1; // LATIN CAPITAL LETTER DZ if (C == 0x01f1) return 0x01f3; if (C < 0x01f2) return C; // 2 characters if (C <= 0x01f4) return C | 1; // LATIN CAPITAL LETTER HWAIR if (C == 0x01f6) return 0x0195; // LATIN CAPITAL LETTER WYNN if (C == 0x01f7) return 0x01bf; if (C < 0x01f8) return C; // 20 characters if (C <= 0x021e) return C | 1; // LATIN CAPITAL LETTER N WITH LONG RIGHT LEG if (C == 0x0220) return 0x019e; if (C < 0x0222) return C; // 9 characters if (C <= 0x0232) return C | 1; // LATIN CAPITAL LETTER A WITH STROKE if (C == 0x023a) return 0x2c65; // LATIN CAPITAL LETTER C WITH STROKE if (C == 0x023b) return 0x023c; // LATIN CAPITAL LETTER L WITH BAR if (C == 0x023d) return 0x019a; // LATIN CAPITAL LETTER T WITH DIAGONAL STROKE if (C == 0x023e) return 0x2c66; // LATIN CAPITAL LETTER GLOTTAL STOP if (C == 0x0241) return 0x0242; // LATIN CAPITAL LETTER B WITH STROKE if (C == 0x0243) return 0x0180; // LATIN CAPITAL LETTER U BAR if (C == 0x0244) return 0x0289; // LATIN CAPITAL LETTER TURNED V if (C == 0x0245) return 0x028c; if (C < 0x0246) return C; // 5 characters if (C <= 0x024e) return C | 1; // COMBINING GREEK YPOGEGRAMMENI if (C == 0x0345) return 0x03b9; if (C < 0x0370) return C; // 2 characters if (C <= 0x0372) return C | 1; // GREEK CAPITAL LETTER PAMPHYLIAN DIGAMMA if (C == 0x0376) return 0x0377; // GREEK CAPITAL LETTER YOT if (C == 0x037f) return 0x03f3; // GREEK CAPITAL LETTER ALPHA WITH TONOS if (C == 0x0386) return 0x03ac; if (C < 0x0388) return C; // 3 characters if (C <= 0x038a) return C + 37; // GREEK CAPITAL LETTER OMICRON WITH TONOS if (C == 0x038c) return 0x03cc; if (C < 0x038e) return C; // 2 characters if (C <= 0x038f) return C + 63; if (C < 0x0391) return C; // 17 characters if (C <= 0x03a1) return C + 32; if (C < 0x03a3) return C; // 9 characters if (C <= 0x03ab) return C + 32; // GREEK SMALL LETTER FINAL SIGMA if (C == 0x03c2) return 0x03c3; // GREEK CAPITAL KAI SYMBOL if (C == 0x03cf) return 0x03d7; // GREEK BETA SYMBOL if (C == 0x03d0) return 0x03b2; // GREEK THETA SYMBOL if (C == 0x03d1) return 0x03b8; // GREEK PHI SYMBOL if (C == 0x03d5) return 0x03c6; // GREEK PI SYMBOL if (C == 0x03d6) return 0x03c0; if (C < 0x03d8) return C; // 12 characters if (C <= 0x03ee) return C | 1; // GREEK KAPPA SYMBOL if (C == 0x03f0) return 0x03ba; // GREEK RHO SYMBOL if (C == 0x03f1) return 0x03c1; // GREEK CAPITAL THETA SYMBOL if (C == 0x03f4) return 0x03b8; // GREEK LUNATE EPSILON SYMBOL if (C == 0x03f5) return 0x03b5; // GREEK CAPITAL LETTER SHO if (C == 0x03f7) return 0x03f8; // GREEK CAPITAL LUNATE SIGMA SYMBOL if (C == 0x03f9) return 0x03f2; // GREEK CAPITAL LETTER SAN if (C == 0x03fa) return 0x03fb; if (C < 0x03fd) return C; // 3 characters if (C <= 0x03ff) return C + -130; if (C < 0x0400) return C; // 16 characters if (C <= 0x040f) return C + 80; if (C < 0x0410) return C; // 32 characters if (C <= 0x042f) return C + 32; if (C < 0x0460) return C; // 17 characters if (C <= 0x0480) return C | 1; if (C < 0x048a) return C; // 27 characters if (C <= 0x04be) return C | 1; // CYRILLIC LETTER PALOCHKA if (C == 0x04c0) return 0x04cf; if (C < 0x04c1) return C; // 7 characters if (C <= 0x04cd && C % 2 == 1) return C + 1; if (C < 0x04d0) return C; // 48 characters if (C <= 0x052e) return C | 1; if (C < 0x0531) return C; // 38 characters if (C <= 0x0556) return C + 48; if (C < 0x10a0) return C; // 38 characters if (C <= 0x10c5) return C + 7264; if (C < 0x10c7) return C; // 2 characters if (C <= 0x10cd && C % 6 == 5) return C + 7264; if (C < 0x13f8) return C; // 6 characters if (C <= 0x13fd) return C + -8; // CYRILLIC SMALL LETTER ROUNDED VE if (C == 0x1c80) return 0x0432; // CYRILLIC SMALL LETTER LONG-LEGGED DE if (C == 0x1c81) return 0x0434; // CYRILLIC SMALL LETTER NARROW O if (C == 0x1c82) return 0x043e; if (C < 0x1c83) return C; // 2 characters if (C <= 0x1c84) return C + -6210; // CYRILLIC SMALL LETTER THREE-LEGGED TE if (C == 0x1c85) return 0x0442; // CYRILLIC SMALL LETTER TALL HARD SIGN if (C == 0x1c86) return 0x044a; // CYRILLIC SMALL LETTER TALL YAT if (C == 0x1c87) return 0x0463; // CYRILLIC SMALL LETTER UNBLENDED UK if (C == 0x1c88) return 0xa64b; if (C < 0x1e00) return C; // 75 characters if (C <= 0x1e94) return C | 1; // LATIN SMALL LETTER LONG S WITH DOT ABOVE if (C == 0x1e9b) return 0x1e61; // LATIN CAPITAL LETTER SHARP S if (C == 0x1e9e) return 0x00df; if (C < 0x1ea0) return C; // 48 characters if (C <= 0x1efe) return C | 1; if (C < 0x1f08) return C; // 8 characters if (C <= 0x1f0f) return C + -8; if (C < 0x1f18) return C; // 6 characters if (C <= 0x1f1d) return C + -8; if (C < 0x1f28) return C; // 8 characters if (C <= 0x1f2f) return C + -8; if (C < 0x1f38) return C; // 8 characters if (C <= 0x1f3f) return C + -8; if (C < 0x1f48) return C; // 6 characters if (C <= 0x1f4d) return C + -8; if (C < 0x1f59) return C; // 4 characters if (C <= 0x1f5f && C % 2 == 1) return C + -8; if (C < 0x1f68) return C; // 8 characters if (C <= 0x1f6f) return C + -8; if (C < 0x1f88) return C; // 8 characters if (C <= 0x1f8f) return C + -8; if (C < 0x1f98) return C; // 8 characters if (C <= 0x1f9f) return C + -8; if (C < 0x1fa8) return C; // 8 characters if (C <= 0x1faf) return C + -8; if (C < 0x1fb8) return C; // 2 characters if (C <= 0x1fb9) return C + -8; if (C < 0x1fba) return C; // 2 characters if (C <= 0x1fbb) return C + -74; // GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI if (C == 0x1fbc) return 0x1fb3; // GREEK PROSGEGRAMMENI if (C == 0x1fbe) return 0x03b9; if (C < 0x1fc8) return C; // 4 characters if (C <= 0x1fcb) return C + -86; // GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI if (C == 0x1fcc) return 0x1fc3; if (C < 0x1fd8) return C; // 2 characters if (C <= 0x1fd9) return C + -8; if (C < 0x1fda) return C; // 2 characters if (C <= 0x1fdb) return C + -100; if (C < 0x1fe8) return C; // 2 characters if (C <= 0x1fe9) return C + -8; if (C < 0x1fea) return C; // 2 characters if (C <= 0x1feb) return C + -112; // GREEK CAPITAL LETTER RHO WITH DASIA if (C == 0x1fec) return 0x1fe5; if (C < 0x1ff8) return C; // 2 characters if (C <= 0x1ff9) return C + -128; if (C < 0x1ffa) return C; // 2 characters if (C <= 0x1ffb) return C + -126; // GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI if (C == 0x1ffc) return 0x1ff3; // OHM SIGN if (C == 0x2126) return 0x03c9; // KELVIN SIGN if (C == 0x212a) return 0x006b; // ANGSTROM SIGN if (C == 0x212b) return 0x00e5; // TURNED CAPITAL F if (C == 0x2132) return 0x214e; if (C < 0x2160) return C; // 16 characters if (C <= 0x216f) return C + 16; // ROMAN NUMERAL REVERSED ONE HUNDRED if (C == 0x2183) return 0x2184; if (C < 0x24b6) return C; // 26 characters if (C <= 0x24cf) return C + 26; if (C < 0x2c00) return C; // 47 characters if (C <= 0x2c2e) return C + 48; // LATIN CAPITAL LETTER L WITH DOUBLE BAR if (C == 0x2c60) return 0x2c61; // LATIN CAPITAL LETTER L WITH MIDDLE TILDE if (C == 0x2c62) return 0x026b; // LATIN CAPITAL LETTER P WITH STROKE if (C == 0x2c63) return 0x1d7d; // LATIN CAPITAL LETTER R WITH TAIL if (C == 0x2c64) return 0x027d; if (C < 0x2c67) return C; // 3 characters if (C <= 0x2c6b && C % 2 == 1) return C + 1; // LATIN CAPITAL LETTER ALPHA if (C == 0x2c6d) return 0x0251; // LATIN CAPITAL LETTER M WITH HOOK if (C == 0x2c6e) return 0x0271; // LATIN CAPITAL LETTER TURNED A if (C == 0x2c6f) return 0x0250; // LATIN CAPITAL LETTER TURNED ALPHA if (C == 0x2c70) return 0x0252; if (C < 0x2c72) return C; // 2 characters if (C <= 0x2c75 && C % 3 == 2) return C + 1; if (C < 0x2c7e) return C; // 2 characters if (C <= 0x2c7f) return C + -10815; if (C < 0x2c80) return C; // 50 characters if (C <= 0x2ce2) return C | 1; if (C < 0x2ceb) return C; // 2 characters if (C <= 0x2ced && C % 2 == 1) return C + 1; if (C < 0x2cf2) return C; // 2 characters if (C <= 0xa640 && C % 31054 == 11506) return C + 1; if (C < 0xa642) return C; // 22 characters if (C <= 0xa66c) return C | 1; if (C < 0xa680) return C; // 14 characters if (C <= 0xa69a) return C | 1; if (C < 0xa722) return C; // 7 characters if (C <= 0xa72e) return C | 1; if (C < 0xa732) return C; // 31 characters if (C <= 0xa76e) return C | 1; if (C < 0xa779) return C; // 2 characters if (C <= 0xa77b && C % 2 == 1) return C + 1; // LATIN CAPITAL LETTER INSULAR G if (C == 0xa77d) return 0x1d79; if (C < 0xa77e) return C; // 5 characters if (C <= 0xa786) return C | 1; // LATIN CAPITAL LETTER SALTILLO if (C == 0xa78b) return 0xa78c; // LATIN CAPITAL LETTER TURNED H if (C == 0xa78d) return 0x0265; if (C < 0xa790) return C; // 2 characters if (C <= 0xa792) return C | 1; if (C < 0xa796) return C; // 10 characters if (C <= 0xa7a8) return C | 1; // LATIN CAPITAL LETTER H WITH HOOK if (C == 0xa7aa) return 0x0266; // LATIN CAPITAL LETTER REVERSED OPEN E if (C == 0xa7ab) return 0x025c; // LATIN CAPITAL LETTER SCRIPT G if (C == 0xa7ac) return 0x0261; // LATIN CAPITAL LETTER L WITH BELT if (C == 0xa7ad) return 0x026c; // LATIN CAPITAL LETTER SMALL CAPITAL I if (C == 0xa7ae) return 0x026a; // LATIN CAPITAL LETTER TURNED K if (C == 0xa7b0) return 0x029e; // LATIN CAPITAL LETTER TURNED T if (C == 0xa7b1) return 0x0287; // LATIN CAPITAL LETTER J WITH CROSSED-TAIL if (C == 0xa7b2) return 0x029d; // LATIN CAPITAL LETTER CHI if (C == 0xa7b3) return 0xab53; if (C < 0xa7b4) return C; // 2 characters if (C <= 0xa7b6) return C | 1; if (C < 0xab70) return C; // 80 characters if (C <= 0xabbf) return C + -38864; if (C < 0xff21) return C; // 26 characters if (C <= 0xff3a) return C + 32; if (C < 0x10400) return C; // 40 characters if (C <= 0x10427) return C + 40; if (C < 0x104b0) return C; // 36 characters if (C <= 0x104d3) return C + 40; if (C < 0x10c80) return C; // 51 characters if (C <= 0x10cb2) return C + 64; if (C < 0x118a0) return C; // 32 characters if (C <= 0x118bf) return C + 32; if (C < 0x1e900) return C; // 34 characters if (C <= 0x1e921) return C + 34; return C; } binaryen-version_91/third_party/llvm-project/Unix/000077500000000000000000000000001362402614000225515ustar00rootroot00000000000000binaryen-version_91/third_party/llvm-project/Unix/Path.inc000066400000000000000000001111531362402614000241420ustar00rootroot00000000000000//===- llvm/Support/Unix/Path.inc - Unix Path Implementation ----*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file implements the Unix specific implementation of the Path API. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// //=== WARNING: Implementation here must contain only generic UNIX code that //=== is guaranteed to work on *all* UNIX variants. //===----------------------------------------------------------------------===// #include "Unix.h" #include #include #if HAVE_SYS_STAT_H #include #endif #if HAVE_FCNTL_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_SYS_MMAN_H #include #endif #include #include #ifdef __APPLE__ #include #include #include #elif defined(__DragonFly__) #include #endif // Both stdio.h and cstdio are included via different paths and // stdcxx's cstdio doesn't include stdio.h, so it doesn't #undef the macros // either. #undef ferror #undef feof // For GNU Hurd #if defined(__GNU__) && !defined(PATH_MAX) # define PATH_MAX 4096 # define MAXPATHLEN 4096 #endif #include #if !defined(__APPLE__) && !defined(__OpenBSD__) && !defined(__FreeBSD__) && \ !defined(__linux__) && !defined(__FreeBSD_kernel__) && !defined(_AIX) #include #define STATVFS statvfs #define FSTATVFS fstatvfs #define STATVFS_F_FRSIZE(vfs) vfs.f_frsize #else #if defined(__OpenBSD__) || defined(__FreeBSD__) #include #include #elif defined(__linux__) #if defined(HAVE_LINUX_MAGIC_H) #include #else #if defined(HAVE_LINUX_NFS_FS_H) #include #endif #if defined(HAVE_LINUX_SMB_H) #include #endif #endif #include #elif defined(_AIX) #include // depends on `uint` to be a typedef from to // `uint_t`; however, does not always declare `uint`. We provide // the typedef prior to including to work around this issue. typedef uint_t uint; #include #else #include #endif #define STATVFS statfs #define FSTATVFS fstatfs #define STATVFS_F_FRSIZE(vfs) static_cast(vfs.f_bsize) #endif #if defined(__NetBSD__) || defined(__DragonFly__) || defined(__GNU__) #define STATVFS_F_FLAG(vfs) (vfs).f_flag #else #define STATVFS_F_FLAG(vfs) (vfs).f_flags #endif using namespace llvm; namespace llvm { namespace sys { namespace fs { const file_t kInvalidFile = -1; #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || \ defined(__minix) || defined(__FreeBSD_kernel__) || defined(__linux__) || \ defined(__CYGWIN__) || defined(__DragonFly__) || defined(_AIX) || defined(__GNU__) static int test_dir(char ret[PATH_MAX], const char *dir, const char *bin) { struct stat sb; char fullpath[PATH_MAX]; int chars = snprintf(fullpath, PATH_MAX, "%s/%s", dir, bin); // We cannot write PATH_MAX characters because the string will be terminated // with a null character. Fail if truncation happened. if (chars >= PATH_MAX) return 1; if (!realpath(fullpath, ret)) return 1; if (stat(fullpath, &sb) != 0) return 1; return 0; } static char * getprogpath(char ret[PATH_MAX], const char *bin) { /* First approach: absolute path. */ if (bin[0] == '/') { if (test_dir(ret, "/", bin) == 0) return ret; return nullptr; } /* Second approach: relative path. */ if (strchr(bin, '/')) { char cwd[PATH_MAX]; if (!getcwd(cwd, PATH_MAX)) return nullptr; if (test_dir(ret, cwd, bin) == 0) return ret; return nullptr; } /* Third approach: $PATH */ char *pv; if ((pv = getenv("PATH")) == nullptr) return nullptr; char *s = strdup(pv); if (!s) return nullptr; char *state; for (char *t = strtok_r(s, ":", &state); t != nullptr; t = strtok_r(nullptr, ":", &state)) { if (test_dir(ret, t, bin) == 0) { free(s); return ret; } } free(s); return nullptr; } #endif // __FreeBSD__ || __NetBSD__ || __FreeBSD_kernel__ /// GetMainExecutable - Return the path to the main executable, given the /// value of argv[0] from program startup. std::string getMainExecutable(const char *argv0, void *MainAddr) { #if defined(__APPLE__) // On OS X the executable path is saved to the stack by dyld. Reading it // from there is much faster than calling dladdr, especially for large // binaries with symbols. char exe_path[MAXPATHLEN]; uint32_t size = sizeof(exe_path); if (_NSGetExecutablePath(exe_path, &size) == 0) { char link_path[MAXPATHLEN]; if (realpath(exe_path, link_path)) return link_path; } #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || \ defined(__minix) || defined(__DragonFly__) || \ defined(__FreeBSD_kernel__) || defined(_AIX) const char *curproc = "/proc/curproc/file"; char exe_path[PATH_MAX]; // /proc is not mounted by default under FreeBSD, but gives more accurate // information than argv[0] when it is. if (sys::fs::exists(curproc)) { ssize_t len = readlink(curproc, exe_path, sizeof(exe_path)); if (len > 0) { // Null terminate the string for realpath. readlink never null // terminates its output. len = std::min(len, ssize_t(sizeof(exe_path) - 1)); exe_path[len] = '\0'; return exe_path; } } // If we don't have procfs mounted, fall back to argv[0] if (getprogpath(exe_path, argv0) != NULL) return exe_path; #elif defined(__linux__) || defined(__CYGWIN__) || defined(__gnu_hurd__) char exe_path[MAXPATHLEN]; const char *aPath = "/proc/self/exe"; if (sys::fs::exists(aPath)) { // /proc is not always mounted under Linux (chroot for example). ssize_t len = readlink(aPath, exe_path, sizeof(exe_path)); if (len < 0) return ""; // Null terminate the string for realpath. readlink never null // terminates its output. len = std::min(len, ssize_t(sizeof(exe_path) - 1)); exe_path[len] = '\0'; // On Linux, /proc/self/exe always looks through symlinks. However, on // GNU/Hurd, /proc/self/exe is a symlink to the path that was used to start // the program, and not the eventual binary file. Therefore, call realpath // so this behaves the same on all platforms. #if _POSIX_VERSION >= 200112 || defined(__GLIBC__) if (char *real_path = realpath(exe_path, NULL)) { std::string ret = std::string(real_path); free(real_path); return ret; } #else char real_path[MAXPATHLEN]; if (realpath(exe_path, real_path)) return std::string(real_path); #endif } // Fall back to the classical detection. if (getprogpath(exe_path, argv0)) return exe_path; #elif defined(HAVE_DLFCN_H) && defined(HAVE_DLADDR) // Use dladdr to get executable path if available. Dl_info DLInfo; int err = dladdr(MainAddr, &DLInfo); if (err == 0) return ""; // If the filename is a symlink, we need to resolve and return the location of // the actual executable. char link_path[MAXPATHLEN]; if (realpath(DLInfo.dli_fname, link_path)) return link_path; #else #error GetMainExecutable is not implemented on this host yet. #endif return ""; } TimePoint<> basic_file_status::getLastAccessedTime() const { return toTimePoint(fs_st_atime, fs_st_atime_nsec); } TimePoint<> basic_file_status::getLastModificationTime() const { return toTimePoint(fs_st_mtime, fs_st_mtime_nsec); } UniqueID file_status::getUniqueID() const { return UniqueID(fs_st_dev, fs_st_ino); } uint32_t file_status::getLinkCount() const { return fs_st_nlinks; } ErrorOr disk_space(const Twine &Path) { struct STATVFS Vfs; if (::STATVFS(const_cast(Path.str().c_str()), &Vfs)) return std::error_code(errno, std::generic_category()); auto FrSize = STATVFS_F_FRSIZE(Vfs); space_info SpaceInfo; SpaceInfo.capacity = static_cast(Vfs.f_blocks) * FrSize; SpaceInfo.free = static_cast(Vfs.f_bfree) * FrSize; SpaceInfo.available = static_cast(Vfs.f_bavail) * FrSize; return SpaceInfo; } std::error_code current_path(SmallVectorImpl &result) { result.clear(); const char *pwd = ::getenv("PWD"); llvm::sys::fs::file_status PWDStatus, DotStatus; if (pwd && llvm::sys::path::is_absolute(pwd) && !llvm::sys::fs::status(pwd, PWDStatus) && !llvm::sys::fs::status(".", DotStatus) && PWDStatus.getUniqueID() == DotStatus.getUniqueID()) { result.append(pwd, pwd + strlen(pwd)); return std::error_code(); } #ifdef MAXPATHLEN result.reserve(MAXPATHLEN); #else // For GNU Hurd result.reserve(1024); #endif while (true) { if (::getcwd(result.data(), result.capacity()) == nullptr) { // See if there was a real error. if (errno != ENOMEM) return std::error_code(errno, std::generic_category()); // Otherwise there just wasn't enough space. result.reserve(result.capacity() * 2); } else break; } result.set_size(strlen(result.data())); return std::error_code(); } std::error_code set_current_path(const Twine &path) { SmallString<128> path_storage; StringRef p = path.toNullTerminatedStringRef(path_storage); if (::chdir(p.begin()) == -1) return std::error_code(errno, std::generic_category()); return std::error_code(); } std::error_code create_directory(const Twine &path, bool IgnoreExisting, perms Perms) { SmallString<128> path_storage; StringRef p = path.toNullTerminatedStringRef(path_storage); if (::mkdir(p.begin(), Perms) == -1) { if (errno != EEXIST || !IgnoreExisting) return std::error_code(errno, std::generic_category()); } return std::error_code(); } // Note that we are using symbolic link because hard links are not supported by // all filesystems (SMB doesn't). std::error_code create_link(const Twine &to, const Twine &from) { // Get arguments. SmallString<128> from_storage; SmallString<128> to_storage; StringRef f = from.toNullTerminatedStringRef(from_storage); StringRef t = to.toNullTerminatedStringRef(to_storage); if (::symlink(t.begin(), f.begin()) == -1) return std::error_code(errno, std::generic_category()); return std::error_code(); } std::error_code create_hard_link(const Twine &to, const Twine &from) { // Get arguments. SmallString<128> from_storage; SmallString<128> to_storage; StringRef f = from.toNullTerminatedStringRef(from_storage); StringRef t = to.toNullTerminatedStringRef(to_storage); if (::link(t.begin(), f.begin()) == -1) return std::error_code(errno, std::generic_category()); return std::error_code(); } std::error_code remove(const Twine &path, bool IgnoreNonExisting) { SmallString<128> path_storage; StringRef p = path.toNullTerminatedStringRef(path_storage); struct stat buf; if (lstat(p.begin(), &buf) != 0) { if (errno != ENOENT || !IgnoreNonExisting) return std::error_code(errno, std::generic_category()); return std::error_code(); } // Note: this check catches strange situations. In all cases, LLVM should // only be involved in the creation and deletion of regular files. This // check ensures that what we're trying to erase is a regular file. It // effectively prevents LLVM from erasing things like /dev/null, any block // special file, or other things that aren't "regular" files. if (!S_ISREG(buf.st_mode) && !S_ISDIR(buf.st_mode) && !S_ISLNK(buf.st_mode)) return make_error_code(errc::operation_not_permitted); if (::remove(p.begin()) == -1) { if (errno != ENOENT || !IgnoreNonExisting) return std::error_code(errno, std::generic_category()); } return std::error_code(); } static bool is_local_impl(struct STATVFS &Vfs) { #if defined(__linux__) || defined(__GNU__) #ifndef NFS_SUPER_MAGIC #define NFS_SUPER_MAGIC 0x6969 #endif #ifndef SMB_SUPER_MAGIC #define SMB_SUPER_MAGIC 0x517B #endif #ifndef CIFS_MAGIC_NUMBER #define CIFS_MAGIC_NUMBER 0xFF534D42 #endif #ifdef __GNU__ switch ((uint32_t)Vfs.__f_type) { #else switch ((uint32_t)Vfs.f_type) { #endif case NFS_SUPER_MAGIC: case SMB_SUPER_MAGIC: case CIFS_MAGIC_NUMBER: return false; default: return true; } #elif defined(__CYGWIN__) // Cygwin doesn't expose this information; would need to use Win32 API. return false; #elif defined(__Fuchsia__) // Fuchsia doesn't yet support remote filesystem mounts. return true; #elif defined(__EMSCRIPTEN__) // Emscripten doesn't currently support remote filesystem mounts. return true; #elif defined(__HAIKU__) // Haiku doesn't expose this information. return false; #elif defined(__sun) // statvfs::f_basetype contains a null-terminated FSType name of the mounted target StringRef fstype(Vfs.f_basetype); // NFS is the only non-local fstype?? return !fstype.equals("nfs"); #elif defined(_AIX) // Call mntctl; try more than twice in case of timing issues with a concurrent // mount. int Ret; size_t BufSize = 2048u; std::unique_ptr Buf; int Tries = 3; while (Tries--) { Buf = std::make_unique(BufSize); Ret = mntctl(MCTL_QUERY, BufSize, Buf.get()); if (Ret != 0) break; BufSize = *reinterpret_cast(Buf.get()); Buf.reset(); } if (Ret == -1) // There was an error; "remote" is the conservative answer. return false; // Look for the correct vmount entry. char *CurObjPtr = Buf.get(); while (Ret--) { struct vmount *Vp = reinterpret_cast(CurObjPtr); static_assert(sizeof(Vfs.f_fsid) == sizeof(Vp->vmt_fsid), "fsid length mismatch"); if (memcmp(&Vfs.f_fsid, &Vp->vmt_fsid, sizeof Vfs.f_fsid) == 0) return (Vp->vmt_flags & MNT_REMOTE) == 0; CurObjPtr += Vp->vmt_length; } // vmount entry not found; "remote" is the conservative answer. return false; #else return !!(STATVFS_F_FLAG(Vfs) & MNT_LOCAL); #endif } std::error_code is_local(const Twine &Path, bool &Result) { struct STATVFS Vfs; if (::STATVFS(const_cast(Path.str().c_str()), &Vfs)) return std::error_code(errno, std::generic_category()); Result = is_local_impl(Vfs); return std::error_code(); } std::error_code is_local(int FD, bool &Result) { struct STATVFS Vfs; if (::FSTATVFS(FD, &Vfs)) return std::error_code(errno, std::generic_category()); Result = is_local_impl(Vfs); return std::error_code(); } std::error_code rename(const Twine &from, const Twine &to) { // Get arguments. SmallString<128> from_storage; SmallString<128> to_storage; StringRef f = from.toNullTerminatedStringRef(from_storage); StringRef t = to.toNullTerminatedStringRef(to_storage); if (::rename(f.begin(), t.begin()) == -1) return std::error_code(errno, std::generic_category()); return std::error_code(); } std::error_code resize_file(int FD, uint64_t Size) { #if defined(HAVE_POSIX_FALLOCATE) // If we have posix_fallocate use it. Unlike ftruncate it always allocates // space, so we get an error if the disk is full. if (int Err = ::posix_fallocate(FD, 0, Size)) { #ifdef _AIX constexpr int NotSupportedError = ENOTSUP; #else constexpr int NotSupportedError = EOPNOTSUPP; #endif if (Err != EINVAL && Err != NotSupportedError) return std::error_code(Err, std::generic_category()); } #endif // Use ftruncate as a fallback. It may or may not allocate space. At least on // OS X with HFS+ it does. if (::ftruncate(FD, Size) == -1) return std::error_code(errno, std::generic_category()); return std::error_code(); } static int convertAccessMode(AccessMode Mode) { switch (Mode) { case AccessMode::Exist: return F_OK; case AccessMode::Write: return W_OK; case AccessMode::Execute: return R_OK | X_OK; // scripts also need R_OK. } llvm_unreachable("invalid enum"); } std::error_code access(const Twine &Path, AccessMode Mode) { SmallString<128> PathStorage; StringRef P = Path.toNullTerminatedStringRef(PathStorage); if (::access(P.begin(), convertAccessMode(Mode)) == -1) return std::error_code(errno, std::generic_category()); if (Mode == AccessMode::Execute) { // Don't say that directories are executable. struct stat buf; if (0 != stat(P.begin(), &buf)) return errc::permission_denied; if (!S_ISREG(buf.st_mode)) return errc::permission_denied; } return std::error_code(); } bool can_execute(const Twine &Path) { return !access(Path, AccessMode::Execute); } bool equivalent(file_status A, file_status B) { assert(status_known(A) && status_known(B)); return A.fs_st_dev == B.fs_st_dev && A.fs_st_ino == B.fs_st_ino; } std::error_code equivalent(const Twine &A, const Twine &B, bool &result) { file_status fsA, fsB; if (std::error_code ec = status(A, fsA)) return ec; if (std::error_code ec = status(B, fsB)) return ec; result = equivalent(fsA, fsB); return std::error_code(); } static void expandTildeExpr(SmallVectorImpl &Path) { StringRef PathStr(Path.begin(), Path.size()); if (PathStr.empty() || !PathStr.startswith("~")) return; PathStr = PathStr.drop_front(); StringRef Expr = PathStr.take_until([](char c) { return path::is_separator(c); }); StringRef Remainder = PathStr.substr(Expr.size() + 1); SmallString<128> Storage; if (Expr.empty()) { // This is just ~/..., resolve it to the current user's home dir. if (!path::home_directory(Storage)) { // For some reason we couldn't get the home directory. Just exit. return; } // Overwrite the first character and insert the rest. Path[0] = Storage[0]; Path.insert(Path.begin() + 1, Storage.begin() + 1, Storage.end()); return; } // This is a string of the form ~username/, look up this user's entry in the // password database. struct passwd *Entry = nullptr; std::string User = Expr.str(); Entry = ::getpwnam(User.c_str()); if (!Entry) { // Unable to look up the entry, just return back the original path. return; } Storage = Remainder; Path.clear(); Path.append(Entry->pw_dir, Entry->pw_dir + strlen(Entry->pw_dir)); llvm::sys::path::append(Path, Storage); } void expand_tilde(const Twine &path, SmallVectorImpl &dest) { dest.clear(); if (path.isTriviallyEmpty()) return; path.toVector(dest); expandTildeExpr(dest); return; } static file_type typeForMode(mode_t Mode) { if (S_ISDIR(Mode)) return file_type::directory_file; else if (S_ISREG(Mode)) return file_type::regular_file; else if (S_ISBLK(Mode)) return file_type::block_file; else if (S_ISCHR(Mode)) return file_type::character_file; else if (S_ISFIFO(Mode)) return file_type::fifo_file; else if (S_ISSOCK(Mode)) return file_type::socket_file; else if (S_ISLNK(Mode)) return file_type::symlink_file; return file_type::type_unknown; } static std::error_code fillStatus(int StatRet, const struct stat &Status, file_status &Result) { if (StatRet != 0) { std::error_code EC(errno, std::generic_category()); if (EC == errc::no_such_file_or_directory) Result = file_status(file_type::file_not_found); else Result = file_status(file_type::status_error); return EC; } uint32_t atime_nsec, mtime_nsec; #if defined(HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC) atime_nsec = Status.st_atimespec.tv_nsec; mtime_nsec = Status.st_mtimespec.tv_nsec; #elif defined(HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC) atime_nsec = Status.st_atim.tv_nsec; mtime_nsec = Status.st_mtim.tv_nsec; #else atime_nsec = mtime_nsec = 0; #endif perms Perms = static_cast(Status.st_mode) & all_perms; Result = file_status(typeForMode(Status.st_mode), Perms, Status.st_dev, Status.st_nlink, Status.st_ino, Status.st_atime, atime_nsec, Status.st_mtime, mtime_nsec, Status.st_uid, Status.st_gid, Status.st_size); return std::error_code(); } std::error_code status(const Twine &Path, file_status &Result, bool Follow) { SmallString<128> PathStorage; StringRef P = Path.toNullTerminatedStringRef(PathStorage); struct stat Status; int StatRet = (Follow ? ::stat : ::lstat)(P.begin(), &Status); return fillStatus(StatRet, Status, Result); } std::error_code status(int FD, file_status &Result) { struct stat Status; int StatRet = ::fstat(FD, &Status); return fillStatus(StatRet, Status, Result); } unsigned getUmask() { // Chose arbitary new mask and reset the umask to the old mask. // umask(2) never fails so ignore the return of the second call. unsigned Mask = ::umask(0); (void) ::umask(Mask); return Mask; } std::error_code setPermissions(const Twine &Path, perms Permissions) { SmallString<128> PathStorage; StringRef P = Path.toNullTerminatedStringRef(PathStorage); if (::chmod(P.begin(), Permissions)) return std::error_code(errno, std::generic_category()); return std::error_code(); } std::error_code setPermissions(int FD, perms Permissions) { if (::fchmod(FD, Permissions)) return std::error_code(errno, std::generic_category()); return std::error_code(); } std::error_code setLastAccessAndModificationTime(int FD, TimePoint<> AccessTime, TimePoint<> ModificationTime) { #if defined(HAVE_FUTIMENS) timespec Times[2]; Times[0] = sys::toTimeSpec(AccessTime); Times[1] = sys::toTimeSpec(ModificationTime); if (::futimens(FD, Times)) return std::error_code(errno, std::generic_category()); return std::error_code(); #elif defined(HAVE_FUTIMES) timeval Times[2]; Times[0] = sys::toTimeVal( std::chrono::time_point_cast(AccessTime)); Times[1] = sys::toTimeVal(std::chrono::time_point_cast( ModificationTime)); if (::futimes(FD, Times)) return std::error_code(errno, std::generic_category()); return std::error_code(); #else #warning Missing futimes() and futimens() return make_error_code(errc::function_not_supported); #endif } std::error_code mapped_file_region::init(int FD, uint64_t Offset, mapmode Mode) { assert(Size != 0); int flags = (Mode == readwrite) ? MAP_SHARED : MAP_PRIVATE; int prot = (Mode == readonly) ? PROT_READ : (PROT_READ | PROT_WRITE); #if defined(__APPLE__) //---------------------------------------------------------------------- // Newer versions of MacOSX have a flag that will allow us to read from // binaries whose code signature is invalid without crashing by using // the MAP_RESILIENT_CODESIGN flag. Also if a file from removable media // is mapped we can avoid crashing and return zeroes to any pages we try // to read if the media becomes unavailable by using the // MAP_RESILIENT_MEDIA flag. These flags are only usable when mapping // with PROT_READ, so take care not to specify them otherwise. //---------------------------------------------------------------------- if (Mode == readonly) { #if defined(MAP_RESILIENT_CODESIGN) flags |= MAP_RESILIENT_CODESIGN; #endif #if defined(MAP_RESILIENT_MEDIA) flags |= MAP_RESILIENT_MEDIA; #endif } #endif // #if defined (__APPLE__) Mapping = ::mmap(nullptr, Size, prot, flags, FD, Offset); if (Mapping == MAP_FAILED) return std::error_code(errno, std::generic_category()); return std::error_code(); } mapped_file_region::mapped_file_region(int fd, mapmode mode, size_t length, uint64_t offset, std::error_code &ec) : Size(length), Mapping(), Mode(mode) { (void)Mode; ec = init(fd, offset, mode); if (ec) Mapping = nullptr; } mapped_file_region::~mapped_file_region() { if (Mapping) ::munmap(Mapping, Size); } size_t mapped_file_region::size() const { assert(Mapping && "Mapping failed but used anyway!"); return Size; } char *mapped_file_region::data() const { assert(Mapping && "Mapping failed but used anyway!"); return reinterpret_cast(Mapping); } const char *mapped_file_region::const_data() const { assert(Mapping && "Mapping failed but used anyway!"); return reinterpret_cast(Mapping); } int mapped_file_region::alignment() { return Process::getPageSizeEstimate(); } std::error_code detail::directory_iterator_construct(detail::DirIterState &it, StringRef path, bool follow_symlinks) { SmallString<128> path_null(path); DIR *directory = ::opendir(path_null.c_str()); if (!directory) return std::error_code(errno, std::generic_category()); it.IterationHandle = reinterpret_cast(directory); // Add something for replace_filename to replace. path::append(path_null, "."); it.CurrentEntry = directory_entry(path_null.str(), follow_symlinks); return directory_iterator_increment(it); } std::error_code detail::directory_iterator_destruct(detail::DirIterState &it) { if (it.IterationHandle) ::closedir(reinterpret_cast(it.IterationHandle)); it.IterationHandle = 0; it.CurrentEntry = directory_entry(); return std::error_code(); } static file_type direntType(dirent* Entry) { // Most platforms provide the file type in the dirent: Linux/BSD/Mac. // The DTTOIF macro lets us reuse our status -> type conversion. // Note that while glibc provides a macro to see if this is supported, // _DIRENT_HAVE_D_TYPE, it's not defined on BSD/Mac, so we test for the // d_type-to-mode_t conversion macro instead. #if defined(DTTOIF) return typeForMode(DTTOIF(Entry->d_type)); #else // Other platforms such as Solaris require a stat() to get the type. return file_type::type_unknown; #endif } std::error_code detail::directory_iterator_increment(detail::DirIterState &It) { errno = 0; dirent *CurDir = ::readdir(reinterpret_cast(It.IterationHandle)); if (CurDir == nullptr && errno != 0) { return std::error_code(errno, std::generic_category()); } else if (CurDir != nullptr) { StringRef Name(CurDir->d_name); if ((Name.size() == 1 && Name[0] == '.') || (Name.size() == 2 && Name[0] == '.' && Name[1] == '.')) return directory_iterator_increment(It); It.CurrentEntry.replace_filename(Name, direntType(CurDir)); } else return directory_iterator_destruct(It); return std::error_code(); } ErrorOr directory_entry::status() const { file_status s; if (auto EC = fs::status(Path, s, FollowSymlinks)) return EC; return s; } #if !defined(F_GETPATH) static bool hasProcSelfFD() { // If we have a /proc filesystem mounted, we can quickly establish the // real name of the file with readlink static const bool Result = (::access("/proc/self/fd", R_OK) == 0); return Result; } #endif static int nativeOpenFlags(CreationDisposition Disp, OpenFlags Flags, FileAccess Access) { int Result = 0; if (Access == FA_Read) Result |= O_RDONLY; else if (Access == FA_Write) Result |= O_WRONLY; else if (Access == (FA_Read | FA_Write)) Result |= O_RDWR; // This is for compatibility with old code that assumed OF_Append implied // would open an existing file. See Windows/Path.inc for a longer comment. if (Flags & OF_Append) Disp = CD_OpenAlways; if (Disp == CD_CreateNew) { Result |= O_CREAT; // Create if it doesn't exist. Result |= O_EXCL; // Fail if it does. } else if (Disp == CD_CreateAlways) { Result |= O_CREAT; // Create if it doesn't exist. Result |= O_TRUNC; // Truncate if it does. } else if (Disp == CD_OpenAlways) { Result |= O_CREAT; // Create if it doesn't exist. } else if (Disp == CD_OpenExisting) { // Nothing special, just don't add O_CREAT and we get these semantics. } if (Flags & OF_Append) Result |= O_APPEND; #ifdef O_CLOEXEC if (!(Flags & OF_ChildInherit)) Result |= O_CLOEXEC; #endif return Result; } std::error_code openFile(const Twine &Name, int &ResultFD, CreationDisposition Disp, FileAccess Access, OpenFlags Flags, unsigned Mode) { int OpenFlags = nativeOpenFlags(Disp, Flags, Access); SmallString<128> Storage; StringRef P = Name.toNullTerminatedStringRef(Storage); // Call ::open in a lambda to avoid overload resolution in RetryAfterSignal // when open is overloaded, such as in Bionic. auto Open = [&]() { return ::open(P.begin(), OpenFlags, Mode); }; if ((ResultFD = sys::RetryAfterSignal(-1, Open)) < 0) return std::error_code(errno, std::generic_category()); #ifndef O_CLOEXEC if (!(Flags & OF_ChildInherit)) { int r = fcntl(ResultFD, F_SETFD, FD_CLOEXEC); (void)r; assert(r == 0 && "fcntl(F_SETFD, FD_CLOEXEC) failed"); } #endif return std::error_code(); } Expected openNativeFile(const Twine &Name, CreationDisposition Disp, FileAccess Access, OpenFlags Flags, unsigned Mode) { int FD; std::error_code EC = openFile(Name, FD, Disp, Access, Flags, Mode); if (EC) return errorCodeToError(EC); return FD; } std::error_code openFileForRead(const Twine &Name, int &ResultFD, OpenFlags Flags, SmallVectorImpl *RealPath) { std::error_code EC = openFile(Name, ResultFD, CD_OpenExisting, FA_Read, Flags, 0666); if (EC) return EC; // Attempt to get the real name of the file, if the user asked if(!RealPath) return std::error_code(); RealPath->clear(); #if defined(F_GETPATH) // When F_GETPATH is availble, it is the quickest way to get // the real path name. char Buffer[MAXPATHLEN]; if (::fcntl(ResultFD, F_GETPATH, Buffer) != -1) RealPath->append(Buffer, Buffer + strlen(Buffer)); #else char Buffer[PATH_MAX]; if (hasProcSelfFD()) { char ProcPath[64]; snprintf(ProcPath, sizeof(ProcPath), "/proc/self/fd/%d", ResultFD); ssize_t CharCount = ::readlink(ProcPath, Buffer, sizeof(Buffer)); if (CharCount > 0) RealPath->append(Buffer, Buffer + CharCount); } else { SmallString<128> Storage; StringRef P = Name.toNullTerminatedStringRef(Storage); // Use ::realpath to get the real path name if (::realpath(P.begin(), Buffer) != nullptr) RealPath->append(Buffer, Buffer + strlen(Buffer)); } #endif return std::error_code(); } Expected openNativeFileForRead(const Twine &Name, OpenFlags Flags, SmallVectorImpl *RealPath) { file_t ResultFD; std::error_code EC = openFileForRead(Name, ResultFD, Flags, RealPath); if (EC) return errorCodeToError(EC); return ResultFD; } file_t getStdinHandle() { return 0; } file_t getStdoutHandle() { return 1; } file_t getStderrHandle() { return 2; } Expected readNativeFile(file_t FD, MutableArrayRef Buf) { ssize_t NumRead = sys::RetryAfterSignal(-1, ::read, FD, Buf.data(), Buf.size()); if (ssize_t(NumRead) == -1) return errorCodeToError(std::error_code(errno, std::generic_category())); return NumRead; } Expected readNativeFileSlice(file_t FD, MutableArrayRef Buf, uint64_t Offset) { #ifdef HAVE_PREAD ssize_t NumRead = sys::RetryAfterSignal(-1, ::pread, FD, Buf.data(), Buf.size(), Offset); #else if (lseek(FD, Offset, SEEK_SET) == -1) return errorCodeToError(std::error_code(errno, std::generic_category())); ssize_t NumRead = sys::RetryAfterSignal(-1, ::read, FD, Buf.data(), Buf.size()); #endif if (NumRead == -1) return errorCodeToError(std::error_code(errno, std::generic_category())); return NumRead; } std::error_code closeFile(file_t &F) { file_t TmpF = F; F = kInvalidFile; return Process::SafelyCloseFileDescriptor(TmpF); } template static std::error_code remove_directories_impl(const T &Entry, bool IgnoreErrors) { std::error_code EC; directory_iterator Begin(Entry, EC, false); directory_iterator End; while (Begin != End) { auto &Item = *Begin; ErrorOr st = Item.status(); if (!st && !IgnoreErrors) return st.getError(); if (is_directory(*st)) { EC = remove_directories_impl(Item, IgnoreErrors); if (EC && !IgnoreErrors) return EC; } EC = fs::remove(Item.path(), true); if (EC && !IgnoreErrors) return EC; Begin.increment(EC); if (EC && !IgnoreErrors) return EC; } return std::error_code(); } std::error_code remove_directories(const Twine &path, bool IgnoreErrors) { auto EC = remove_directories_impl(path, IgnoreErrors); if (EC && !IgnoreErrors) return EC; EC = fs::remove(path, true); if (EC && !IgnoreErrors) return EC; return std::error_code(); } std::error_code real_path(const Twine &path, SmallVectorImpl &dest, bool expand_tilde) { dest.clear(); if (path.isTriviallyEmpty()) return std::error_code(); if (expand_tilde) { SmallString<128> Storage; path.toVector(Storage); expandTildeExpr(Storage); return real_path(Storage, dest, false); } SmallString<128> Storage; StringRef P = path.toNullTerminatedStringRef(Storage); char Buffer[PATH_MAX]; if (::realpath(P.begin(), Buffer) == nullptr) return std::error_code(errno, std::generic_category()); dest.append(Buffer, Buffer + strlen(Buffer)); return std::error_code(); } } // end namespace fs namespace path { bool home_directory(SmallVectorImpl &result) { char *RequestedDir = getenv("HOME"); if (!RequestedDir) { struct passwd *pw = getpwuid(getuid()); if (pw && pw->pw_dir) RequestedDir = pw->pw_dir; } if (!RequestedDir) return false; result.clear(); result.append(RequestedDir, RequestedDir + strlen(RequestedDir)); return true; } static bool getDarwinConfDir(bool TempDir, SmallVectorImpl &Result) { #if defined(_CS_DARWIN_USER_TEMP_DIR) && defined(_CS_DARWIN_USER_CACHE_DIR) // On Darwin, use DARWIN_USER_TEMP_DIR or DARWIN_USER_CACHE_DIR. // macros defined in on darwin >= 9 int ConfName = TempDir ? _CS_DARWIN_USER_TEMP_DIR : _CS_DARWIN_USER_CACHE_DIR; size_t ConfLen = confstr(ConfName, nullptr, 0); if (ConfLen > 0) { do { Result.resize(ConfLen); ConfLen = confstr(ConfName, Result.data(), Result.size()); } while (ConfLen > 0 && ConfLen != Result.size()); if (ConfLen > 0) { assert(Result.back() == 0); Result.pop_back(); return true; } Result.clear(); } #endif return false; } static const char *getEnvTempDir() { // Check whether the temporary directory is specified by an environment // variable. const char *EnvironmentVariables[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR"}; for (const char *Env : EnvironmentVariables) { if (const char *Dir = std::getenv(Env)) return Dir; } return nullptr; } static const char *getDefaultTempDir(bool ErasedOnReboot) { #ifdef P_tmpdir if ((bool)P_tmpdir) return P_tmpdir; #endif if (ErasedOnReboot) return "/tmp"; return "/var/tmp"; } void system_temp_directory(bool ErasedOnReboot, SmallVectorImpl &Result) { Result.clear(); if (ErasedOnReboot) { // There is no env variable for the cache directory. if (const char *RequestedDir = getEnvTempDir()) { Result.append(RequestedDir, RequestedDir + strlen(RequestedDir)); return; } } if (getDarwinConfDir(ErasedOnReboot, Result)) return; const char *RequestedDir = getDefaultTempDir(ErasedOnReboot); Result.append(RequestedDir, RequestedDir + strlen(RequestedDir)); } } // end namespace path namespace fs { #ifdef __APPLE__ /// This implementation tries to perform an APFS CoW clone of the file, /// which can be much faster and uses less space. /// Unfortunately fcopyfile(3) does not support COPYFILE_CLONE, so the /// file descriptor variant of this function still uses the default /// implementation. std::error_code copy_file(const Twine &From, const Twine &To) { uint32_t Flag = COPYFILE_DATA; #if __has_builtin(__builtin_available) && defined(COPYFILE_CLONE) if (__builtin_available(macos 10.12, *)) { bool IsSymlink; if (std::error_code Error = is_symlink_file(From, IsSymlink)) return Error; // COPYFILE_CLONE clones the symlink instead of following it // and returns EEXISTS if the target file already exists. if (!IsSymlink && !exists(To)) Flag = COPYFILE_CLONE; } #endif int Status = copyfile(From.str().c_str(), To.str().c_str(), /* State */ NULL, Flag); if (Status == 0) return std::error_code(); return std::error_code(errno, std::generic_category()); } #endif // __APPLE__ } // end namespace fs } // end namespace sys } // end namespace llvm binaryen-version_91/third_party/llvm-project/Unix/Unix.h000066400000000000000000000064031362402614000236500ustar00rootroot00000000000000//===- llvm/Support/Unix/Unix.h - Common Unix Include File -------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file defines things specific to Unix implementations. // //===----------------------------------------------------------------------===// #ifndef LLVM_LIB_SUPPORT_UNIX_UNIX_H #define LLVM_LIB_SUPPORT_UNIX_UNIX_H //===----------------------------------------------------------------------===// //=== WARNING: Implementation here must contain only generic UNIX code that //=== is guaranteed to work on all UNIX variants. //===----------------------------------------------------------------------===// #include "llvm/Config/config.h" #include "llvm/Support/Chrono.h" #include "llvm/Support/Errno.h" #include "llvm/Support/ErrorHandling.h" #include #include #include #include #include #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_SYS_PARAM_H #include #endif #ifdef HAVE_SYS_TIME_H # include #endif #include #ifdef HAVE_DLFCN_H # include #endif #ifdef HAVE_FCNTL_H # include #endif /// This function builds an error message into \p ErrMsg using the \p prefix /// string and the Unix error number given by \p errnum. If errnum is -1, the /// default then the value of errno is used. /// Make an error message /// /// If the error number can be converted to a string, it will be /// separated from prefix by ": ". static inline bool MakeErrMsg( std::string* ErrMsg, const std::string& prefix, int errnum = -1) { if (!ErrMsg) return true; if (errnum == -1) errnum = errno; *ErrMsg = prefix + ": " + llvm::sys::StrError(errnum); return true; } // Include StrError(errnum) in a fatal error message. LLVM_ATTRIBUTE_NORETURN static inline void ReportErrnumFatal(const char *Msg, int errnum) { std::string ErrMsg; MakeErrMsg(&ErrMsg, Msg, errnum); llvm::report_fatal_error(ErrMsg); } namespace llvm { namespace sys { /// Convert a struct timeval to a duration. Note that timeval can be used both /// as a time point and a duration. Be sure to check what the input represents. inline std::chrono::microseconds toDuration(const struct timeval &TV) { return std::chrono::seconds(TV.tv_sec) + std::chrono::microseconds(TV.tv_usec); } /// Convert a time point to struct timespec. inline struct timespec toTimeSpec(TimePoint<> TP) { using namespace std::chrono; struct timespec RetVal; RetVal.tv_sec = toTimeT(TP); RetVal.tv_nsec = (TP.time_since_epoch() % seconds(1)).count(); return RetVal; } /// Convert a time point to struct timeval. inline struct timeval toTimeVal(TimePoint TP) { using namespace std::chrono; struct timeval RetVal; RetVal.tv_sec = toTimeT(TP); RetVal.tv_usec = (TP.time_since_epoch() % seconds(1)).count(); return RetVal; } } // namespace sys } // namespace llvm #endif binaryen-version_91/third_party/llvm-project/WithColor.cpp000066400000000000000000000050521362402614000242460ustar00rootroot00000000000000//===- WithColor.cpp ------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; #if 0 // XXX BINARYEN cl::OptionCategory llvm::ColorCategory("Color Options"); static cl::opt UseColor("color", cl::cat(ColorCategory), cl::desc("Use colors in output (default=autodetect)"), cl::init(cl::BOU_UNSET)); #endif WithColor::WithColor(raw_ostream &OS, HighlightColor Color, bool DisableColors) : OS(OS), DisableColors(DisableColors) { // Detect color from terminal type unless the user passed the --color option. // XXX BINARYEN - no color support } raw_ostream &WithColor::error() { return error(errs()); } raw_ostream &WithColor::warning() { return warning(errs()); } raw_ostream &WithColor::note() { return note(errs()); } raw_ostream &WithColor::remark() { return remark(errs()); } raw_ostream &WithColor::error(raw_ostream &OS, StringRef Prefix, bool DisableColors) { if (!Prefix.empty()) OS << Prefix << ": "; return WithColor(OS, HighlightColor::Error, DisableColors).get() << "error: "; } raw_ostream &WithColor::warning(raw_ostream &OS, StringRef Prefix, bool DisableColors) { if (!Prefix.empty()) OS << Prefix << ": "; return WithColor(OS, HighlightColor::Warning, DisableColors).get() << "warning: "; } raw_ostream &WithColor::note(raw_ostream &OS, StringRef Prefix, bool DisableColors) { if (!Prefix.empty()) OS << Prefix << ": "; return WithColor(OS, HighlightColor::Note, DisableColors).get() << "note: "; } raw_ostream &WithColor::remark(raw_ostream &OS, StringRef Prefix, bool DisableColors) { if (!Prefix.empty()) OS << Prefix << ": "; return WithColor(OS, HighlightColor::Remark, DisableColors).get() << "remark: "; } bool WithColor::colorsEnabled() { return false; // XXX BINARYEN } WithColor &WithColor::changeColor(raw_ostream::Colors Color, bool Bold, bool BG) { // XXX BINARYEN return *this; } WithColor &WithColor::resetColor() { // XXX BINARYEN return *this; } WithColor::~WithColor() { resetColor(); } binaryen-version_91/third_party/llvm-project/YAMLParser.cpp000066400000000000000000002044021362402614000242530ustar00rootroot00000000000000//===- YAMLParser.cpp - Simple YAML parser --------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file implements a YAML parser. // //===----------------------------------------------------------------------===// #include "llvm/Support/YAMLParser.h" #include "llvm/ADT/AllocatorList.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/None.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/SMLoc.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/Unicode.h" #include "llvm/Support/raw_ostream.h" #include #include #include #include #include #include #include #include #include using namespace llvm; using namespace yaml; enum UnicodeEncodingForm { UEF_UTF32_LE, ///< UTF-32 Little Endian UEF_UTF32_BE, ///< UTF-32 Big Endian UEF_UTF16_LE, ///< UTF-16 Little Endian UEF_UTF16_BE, ///< UTF-16 Big Endian UEF_UTF8, ///< UTF-8 or ascii. UEF_Unknown ///< Not a valid Unicode encoding. }; /// EncodingInfo - Holds the encoding type and length of the byte order mark if /// it exists. Length is in {0, 2, 3, 4}. using EncodingInfo = std::pair; /// getUnicodeEncoding - Reads up to the first 4 bytes to determine the Unicode /// encoding form of \a Input. /// /// @param Input A string of length 0 or more. /// @returns An EncodingInfo indicating the Unicode encoding form of the input /// and how long the byte order mark is if one exists. static EncodingInfo getUnicodeEncoding(StringRef Input) { if (Input.empty()) return std::make_pair(UEF_Unknown, 0); switch (uint8_t(Input[0])) { case 0x00: if (Input.size() >= 4) { if ( Input[1] == 0 && uint8_t(Input[2]) == 0xFE && uint8_t(Input[3]) == 0xFF) return std::make_pair(UEF_UTF32_BE, 4); if (Input[1] == 0 && Input[2] == 0 && Input[3] != 0) return std::make_pair(UEF_UTF32_BE, 0); } if (Input.size() >= 2 && Input[1] != 0) return std::make_pair(UEF_UTF16_BE, 0); return std::make_pair(UEF_Unknown, 0); case 0xFF: if ( Input.size() >= 4 && uint8_t(Input[1]) == 0xFE && Input[2] == 0 && Input[3] == 0) return std::make_pair(UEF_UTF32_LE, 4); if (Input.size() >= 2 && uint8_t(Input[1]) == 0xFE) return std::make_pair(UEF_UTF16_LE, 2); return std::make_pair(UEF_Unknown, 0); case 0xFE: if (Input.size() >= 2 && uint8_t(Input[1]) == 0xFF) return std::make_pair(UEF_UTF16_BE, 2); return std::make_pair(UEF_Unknown, 0); case 0xEF: if ( Input.size() >= 3 && uint8_t(Input[1]) == 0xBB && uint8_t(Input[2]) == 0xBF) return std::make_pair(UEF_UTF8, 3); return std::make_pair(UEF_Unknown, 0); } // It could still be utf-32 or utf-16. if (Input.size() >= 4 && Input[1] == 0 && Input[2] == 0 && Input[3] == 0) return std::make_pair(UEF_UTF32_LE, 0); if (Input.size() >= 2 && Input[1] == 0) return std::make_pair(UEF_UTF16_LE, 0); return std::make_pair(UEF_UTF8, 0); } /// Pin the vtables to this file. void Node::anchor() {} void NullNode::anchor() {} void ScalarNode::anchor() {} void BlockScalarNode::anchor() {} void KeyValueNode::anchor() {} void MappingNode::anchor() {} void SequenceNode::anchor() {} void AliasNode::anchor() {} namespace llvm { namespace yaml { /// Token - A single YAML token. struct Token { enum TokenKind { TK_Error, // Uninitialized token. TK_StreamStart, TK_StreamEnd, TK_VersionDirective, TK_TagDirective, TK_DocumentStart, TK_DocumentEnd, TK_BlockEntry, TK_BlockEnd, TK_BlockSequenceStart, TK_BlockMappingStart, TK_FlowEntry, TK_FlowSequenceStart, TK_FlowSequenceEnd, TK_FlowMappingStart, TK_FlowMappingEnd, TK_Key, TK_Value, TK_Scalar, TK_BlockScalar, TK_Alias, TK_Anchor, TK_Tag } Kind = TK_Error; /// A string of length 0 or more whose begin() points to the logical location /// of the token in the input. StringRef Range; /// The value of a block scalar node. std::string Value; Token() = default; }; } // end namespace yaml } // end namespace llvm using TokenQueueT = BumpPtrList; namespace { /// This struct is used to track simple keys. /// /// Simple keys are handled by creating an entry in SimpleKeys for each Token /// which could legally be the start of a simple key. When peekNext is called, /// if the Token To be returned is referenced by a SimpleKey, we continue /// tokenizing until that potential simple key has either been found to not be /// a simple key (we moved on to the next line or went further than 1024 chars). /// Or when we run into a Value, and then insert a Key token (and possibly /// others) before the SimpleKey's Tok. struct SimpleKey { TokenQueueT::iterator Tok; unsigned Column = 0; unsigned Line = 0; unsigned FlowLevel = 0; bool IsRequired = false; bool operator ==(const SimpleKey &Other) { return Tok == Other.Tok; } }; } // end anonymous namespace /// The Unicode scalar value of a UTF-8 minimal well-formed code unit /// subsequence and the subsequence's length in code units (uint8_t). /// A length of 0 represents an error. using UTF8Decoded = std::pair; static UTF8Decoded decodeUTF8(StringRef Range) { StringRef::iterator Position= Range.begin(); StringRef::iterator End = Range.end(); // 1 byte: [0x00, 0x7f] // Bit pattern: 0xxxxxxx if ((*Position & 0x80) == 0) { return std::make_pair(*Position, 1); } // 2 bytes: [0x80, 0x7ff] // Bit pattern: 110xxxxx 10xxxxxx if (Position + 1 != End && ((*Position & 0xE0) == 0xC0) && ((*(Position + 1) & 0xC0) == 0x80)) { uint32_t codepoint = ((*Position & 0x1F) << 6) | (*(Position + 1) & 0x3F); if (codepoint >= 0x80) return std::make_pair(codepoint, 2); } // 3 bytes: [0x8000, 0xffff] // Bit pattern: 1110xxxx 10xxxxxx 10xxxxxx if (Position + 2 != End && ((*Position & 0xF0) == 0xE0) && ((*(Position + 1) & 0xC0) == 0x80) && ((*(Position + 2) & 0xC0) == 0x80)) { uint32_t codepoint = ((*Position & 0x0F) << 12) | ((*(Position + 1) & 0x3F) << 6) | (*(Position + 2) & 0x3F); // Codepoints between 0xD800 and 0xDFFF are invalid, as // they are high / low surrogate halves used by UTF-16. if (codepoint >= 0x800 && (codepoint < 0xD800 || codepoint > 0xDFFF)) return std::make_pair(codepoint, 3); } // 4 bytes: [0x10000, 0x10FFFF] // Bit pattern: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx if (Position + 3 != End && ((*Position & 0xF8) == 0xF0) && ((*(Position + 1) & 0xC0) == 0x80) && ((*(Position + 2) & 0xC0) == 0x80) && ((*(Position + 3) & 0xC0) == 0x80)) { uint32_t codepoint = ((*Position & 0x07) << 18) | ((*(Position + 1) & 0x3F) << 12) | ((*(Position + 2) & 0x3F) << 6) | (*(Position + 3) & 0x3F); if (codepoint >= 0x10000 && codepoint <= 0x10FFFF) return std::make_pair(codepoint, 4); } return std::make_pair(0, 0); } namespace llvm { namespace yaml { /// Scans YAML tokens from a MemoryBuffer. class Scanner { public: Scanner(StringRef Input, SourceMgr &SM, bool ShowColors = true, std::error_code *EC = nullptr); Scanner(MemoryBufferRef Buffer, SourceMgr &SM_, bool ShowColors = true, std::error_code *EC = nullptr); /// Parse the next token and return it without popping it. Token &peekNext(); /// Parse the next token and pop it from the queue. Token getNext(); void printError(SMLoc Loc, SourceMgr::DiagKind Kind, const Twine &Message, ArrayRef Ranges = None) { SM.PrintMessage(Loc, Kind, Message, Ranges, /* FixIts= */ None, ShowColors); } void setError(const Twine &Message, StringRef::iterator Position) { if (Current >= End) Current = End - 1; // propagate the error if possible if (EC) *EC = make_error_code(std::errc::invalid_argument); // Don't print out more errors after the first one we encounter. The rest // are just the result of the first, and have no meaning. if (!Failed) printError(SMLoc::getFromPointer(Current), SourceMgr::DK_Error, Message); Failed = true; } void setError(const Twine &Message) { setError(Message, Current); } /// Returns true if an error occurred while parsing. bool failed() { return Failed; } private: void init(MemoryBufferRef Buffer); StringRef currentInput() { return StringRef(Current, End - Current); } /// Decode a UTF-8 minimal well-formed code unit subsequence starting /// at \a Position. /// /// If the UTF-8 code units starting at Position do not form a well-formed /// code unit subsequence, then the Unicode scalar value is 0, and the length /// is 0. UTF8Decoded decodeUTF8(StringRef::iterator Position) { return ::decodeUTF8(StringRef(Position, End - Position)); } // The following functions are based on the gramar rules in the YAML spec. The // style of the function names it meant to closely match how they are written // in the spec. The number within the [] is the number of the grammar rule in // the spec. // // See 4.2 [Production Naming Conventions] for the meaning of the prefixes. // // c- // A production starting and ending with a special character. // b- // A production matching a single line break. // nb- // A production starting and ending with a non-break character. // s- // A production starting and ending with a white space character. // ns- // A production starting and ending with a non-space character. // l- // A production matching complete line(s). /// Skip a single nb-char[27] starting at Position. /// /// A nb-char is 0x9 | [0x20-0x7E] | 0x85 | [0xA0-0xD7FF] | [0xE000-0xFEFE] /// | [0xFF00-0xFFFD] | [0x10000-0x10FFFF] /// /// @returns The code unit after the nb-char, or Position if it's not an /// nb-char. StringRef::iterator skip_nb_char(StringRef::iterator Position); /// Skip a single b-break[28] starting at Position. /// /// A b-break is 0xD 0xA | 0xD | 0xA /// /// @returns The code unit after the b-break, or Position if it's not a /// b-break. StringRef::iterator skip_b_break(StringRef::iterator Position); /// Skip a single s-space[31] starting at Position. /// /// An s-space is 0x20 /// /// @returns The code unit after the s-space, or Position if it's not a /// s-space. StringRef::iterator skip_s_space(StringRef::iterator Position); /// Skip a single s-white[33] starting at Position. /// /// A s-white is 0x20 | 0x9 /// /// @returns The code unit after the s-white, or Position if it's not a /// s-white. StringRef::iterator skip_s_white(StringRef::iterator Position); /// Skip a single ns-char[34] starting at Position. /// /// A ns-char is nb-char - s-white /// /// @returns The code unit after the ns-char, or Position if it's not a /// ns-char. StringRef::iterator skip_ns_char(StringRef::iterator Position); using SkipWhileFunc = StringRef::iterator (Scanner::*)(StringRef::iterator); /// Skip minimal well-formed code unit subsequences until Func /// returns its input. /// /// @returns The code unit after the last minimal well-formed code unit /// subsequence that Func accepted. StringRef::iterator skip_while( SkipWhileFunc Func , StringRef::iterator Position); /// Skip minimal well-formed code unit subsequences until Func returns its /// input. void advanceWhile(SkipWhileFunc Func); /// Scan ns-uri-char[39]s starting at Cur. /// /// This updates Cur and Column while scanning. void scan_ns_uri_char(); /// Consume a minimal well-formed code unit subsequence starting at /// \a Cur. Return false if it is not the same Unicode scalar value as /// \a Expected. This updates \a Column. bool consume(uint32_t Expected); /// Skip \a Distance UTF-8 code units. Updates \a Cur and \a Column. void skip(uint32_t Distance); /// Return true if the minimal well-formed code unit subsequence at /// Pos is whitespace or a new line bool isBlankOrBreak(StringRef::iterator Position); /// Consume a single b-break[28] if it's present at the current position. /// /// Return false if the code unit at the current position isn't a line break. bool consumeLineBreakIfPresent(); /// If IsSimpleKeyAllowed, create and push_back a new SimpleKey. void saveSimpleKeyCandidate( TokenQueueT::iterator Tok , unsigned AtColumn , bool IsRequired); /// Remove simple keys that can no longer be valid simple keys. /// /// Invalid simple keys are not on the current line or are further than 1024 /// columns back. void removeStaleSimpleKeyCandidates(); /// Remove all simple keys on FlowLevel \a Level. void removeSimpleKeyCandidatesOnFlowLevel(unsigned Level); /// Unroll indentation in \a Indents back to \a Col. Creates BlockEnd /// tokens if needed. bool unrollIndent(int ToColumn); /// Increase indent to \a Col. Creates \a Kind token at \a InsertPoint /// if needed. bool rollIndent( int ToColumn , Token::TokenKind Kind , TokenQueueT::iterator InsertPoint); /// Skip a single-line comment when the comment starts at the current /// position of the scanner. void skipComment(); /// Skip whitespace and comments until the start of the next token. void scanToNextToken(); /// Must be the first token generated. bool scanStreamStart(); /// Generate tokens needed to close out the stream. bool scanStreamEnd(); /// Scan a %BLAH directive. bool scanDirective(); /// Scan a ... or ---. bool scanDocumentIndicator(bool IsStart); /// Scan a [ or { and generate the proper flow collection start token. bool scanFlowCollectionStart(bool IsSequence); /// Scan a ] or } and generate the proper flow collection end token. bool scanFlowCollectionEnd(bool IsSequence); /// Scan the , that separates entries in a flow collection. bool scanFlowEntry(); /// Scan the - that starts block sequence entries. bool scanBlockEntry(); /// Scan an explicit ? indicating a key. bool scanKey(); /// Scan an explicit : indicating a value. bool scanValue(); /// Scan a quoted scalar. bool scanFlowScalar(bool IsDoubleQuoted); /// Scan an unquoted scalar. bool scanPlainScalar(); /// Scan an Alias or Anchor starting with * or &. bool scanAliasOrAnchor(bool IsAlias); /// Scan a block scalar starting with | or >. bool scanBlockScalar(bool IsLiteral); /// Scan a chomping indicator in a block scalar header. char scanBlockChompingIndicator(); /// Scan an indentation indicator in a block scalar header. unsigned scanBlockIndentationIndicator(); /// Scan a block scalar header. /// /// Return false if an error occurred. bool scanBlockScalarHeader(char &ChompingIndicator, unsigned &IndentIndicator, bool &IsDone); /// Look for the indentation level of a block scalar. /// /// Return false if an error occurred. bool findBlockScalarIndent(unsigned &BlockIndent, unsigned BlockExitIndent, unsigned &LineBreaks, bool &IsDone); /// Scan the indentation of a text line in a block scalar. /// /// Return false if an error occurred. bool scanBlockScalarIndent(unsigned BlockIndent, unsigned BlockExitIndent, bool &IsDone); /// Scan a tag of the form !stuff. bool scanTag(); /// Dispatch to the next scanning function based on \a *Cur. bool fetchMoreTokens(); /// The SourceMgr used for diagnostics and buffer management. SourceMgr &SM; /// The original input. MemoryBufferRef InputBuffer; /// The current position of the scanner. StringRef::iterator Current; /// The end of the input (one past the last character). StringRef::iterator End; /// Current YAML indentation level in spaces. int Indent; /// Current column number in Unicode code points. unsigned Column; /// Current line number. unsigned Line; /// How deep we are in flow style containers. 0 Means at block level. unsigned FlowLevel; /// Are we at the start of the stream? bool IsStartOfStream; /// Can the next token be the start of a simple key? bool IsSimpleKeyAllowed; /// True if an error has occurred. bool Failed; /// Should colors be used when printing out the diagnostic messages? bool ShowColors; /// Queue of tokens. This is required to queue up tokens while looking /// for the end of a simple key. And for cases where a single character /// can produce multiple tokens (e.g. BlockEnd). TokenQueueT TokenQueue; /// Indentation levels. SmallVector Indents; /// Potential simple keys. SmallVector SimpleKeys; std::error_code *EC; }; } // end namespace yaml } // end namespace llvm /// encodeUTF8 - Encode \a UnicodeScalarValue in UTF-8 and append it to result. static void encodeUTF8( uint32_t UnicodeScalarValue , SmallVectorImpl &Result) { if (UnicodeScalarValue <= 0x7F) { Result.push_back(UnicodeScalarValue & 0x7F); } else if (UnicodeScalarValue <= 0x7FF) { uint8_t FirstByte = 0xC0 | ((UnicodeScalarValue & 0x7C0) >> 6); uint8_t SecondByte = 0x80 | (UnicodeScalarValue & 0x3F); Result.push_back(FirstByte); Result.push_back(SecondByte); } else if (UnicodeScalarValue <= 0xFFFF) { uint8_t FirstByte = 0xE0 | ((UnicodeScalarValue & 0xF000) >> 12); uint8_t SecondByte = 0x80 | ((UnicodeScalarValue & 0xFC0) >> 6); uint8_t ThirdByte = 0x80 | (UnicodeScalarValue & 0x3F); Result.push_back(FirstByte); Result.push_back(SecondByte); Result.push_back(ThirdByte); } else if (UnicodeScalarValue <= 0x10FFFF) { uint8_t FirstByte = 0xF0 | ((UnicodeScalarValue & 0x1F0000) >> 18); uint8_t SecondByte = 0x80 | ((UnicodeScalarValue & 0x3F000) >> 12); uint8_t ThirdByte = 0x80 | ((UnicodeScalarValue & 0xFC0) >> 6); uint8_t FourthByte = 0x80 | (UnicodeScalarValue & 0x3F); Result.push_back(FirstByte); Result.push_back(SecondByte); Result.push_back(ThirdByte); Result.push_back(FourthByte); } } bool yaml::dumpTokens(StringRef Input, raw_ostream &OS) { SourceMgr SM; Scanner scanner(Input, SM); while (true) { Token T = scanner.getNext(); switch (T.Kind) { case Token::TK_StreamStart: OS << "Stream-Start: "; break; case Token::TK_StreamEnd: OS << "Stream-End: "; break; case Token::TK_VersionDirective: OS << "Version-Directive: "; break; case Token::TK_TagDirective: OS << "Tag-Directive: "; break; case Token::TK_DocumentStart: OS << "Document-Start: "; break; case Token::TK_DocumentEnd: OS << "Document-End: "; break; case Token::TK_BlockEntry: OS << "Block-Entry: "; break; case Token::TK_BlockEnd: OS << "Block-End: "; break; case Token::TK_BlockSequenceStart: OS << "Block-Sequence-Start: "; break; case Token::TK_BlockMappingStart: OS << "Block-Mapping-Start: "; break; case Token::TK_FlowEntry: OS << "Flow-Entry: "; break; case Token::TK_FlowSequenceStart: OS << "Flow-Sequence-Start: "; break; case Token::TK_FlowSequenceEnd: OS << "Flow-Sequence-End: "; break; case Token::TK_FlowMappingStart: OS << "Flow-Mapping-Start: "; break; case Token::TK_FlowMappingEnd: OS << "Flow-Mapping-End: "; break; case Token::TK_Key: OS << "Key: "; break; case Token::TK_Value: OS << "Value: "; break; case Token::TK_Scalar: OS << "Scalar: "; break; case Token::TK_BlockScalar: OS << "Block Scalar: "; break; case Token::TK_Alias: OS << "Alias: "; break; case Token::TK_Anchor: OS << "Anchor: "; break; case Token::TK_Tag: OS << "Tag: "; break; case Token::TK_Error: break; } OS << T.Range << "\n"; if (T.Kind == Token::TK_StreamEnd) break; else if (T.Kind == Token::TK_Error) return false; } return true; } bool yaml::scanTokens(StringRef Input) { SourceMgr SM; Scanner scanner(Input, SM); while (true) { Token T = scanner.getNext(); if (T.Kind == Token::TK_StreamEnd) break; else if (T.Kind == Token::TK_Error) return false; } return true; } std::string yaml::escape(StringRef Input, bool EscapePrintable) { llvm_unreachable("BYN yaml::escape"); } Scanner::Scanner(StringRef Input, SourceMgr &sm, bool ShowColors, std::error_code *EC) : SM(sm), ShowColors(ShowColors), EC(EC) { init(MemoryBufferRef(Input, "YAML")); } Scanner::Scanner(MemoryBufferRef Buffer, SourceMgr &SM_, bool ShowColors, std::error_code *EC) : SM(SM_), ShowColors(ShowColors), EC(EC) { init(Buffer); } void Scanner::init(MemoryBufferRef Buffer) { InputBuffer = Buffer; Current = InputBuffer.getBufferStart(); End = InputBuffer.getBufferEnd(); Indent = -1; Column = 0; Line = 0; FlowLevel = 0; IsStartOfStream = true; IsSimpleKeyAllowed = true; Failed = false; std::unique_ptr InputBufferOwner = MemoryBuffer::getMemBuffer(Buffer); SM.AddNewSourceBuffer(std::move(InputBufferOwner), SMLoc()); } Token &Scanner::peekNext() { // If the current token is a possible simple key, keep parsing until we // can confirm. bool NeedMore = false; while (true) { if (TokenQueue.empty() || NeedMore) { if (!fetchMoreTokens()) { TokenQueue.clear(); SimpleKeys.clear(); TokenQueue.push_back(Token()); return TokenQueue.front(); } } assert(!TokenQueue.empty() && "fetchMoreTokens lied about getting tokens!"); removeStaleSimpleKeyCandidates(); SimpleKey SK; SK.Tok = TokenQueue.begin(); if (!is_contained(SimpleKeys, SK)) break; else NeedMore = true; } return TokenQueue.front(); } Token Scanner::getNext() { Token Ret = peekNext(); // TokenQueue can be empty if there was an error getting the next token. if (!TokenQueue.empty()) TokenQueue.pop_front(); // There cannot be any referenced Token's if the TokenQueue is empty. So do a // quick deallocation of them all. if (TokenQueue.empty()) TokenQueue.resetAlloc(); return Ret; } StringRef::iterator Scanner::skip_nb_char(StringRef::iterator Position) { if (Position == End) return Position; // Check 7 bit c-printable - b-char. if ( *Position == 0x09 || (*Position >= 0x20 && *Position <= 0x7E)) return Position + 1; // Check for valid UTF-8. if (uint8_t(*Position) & 0x80) { UTF8Decoded u8d = decodeUTF8(Position); if ( u8d.second != 0 && u8d.first != 0xFEFF && ( u8d.first == 0x85 || ( u8d.first >= 0xA0 && u8d.first <= 0xD7FF) || ( u8d.first >= 0xE000 && u8d.first <= 0xFFFD) || ( u8d.first >= 0x10000 && u8d.first <= 0x10FFFF))) return Position + u8d.second; } return Position; } StringRef::iterator Scanner::skip_b_break(StringRef::iterator Position) { if (Position == End) return Position; if (*Position == 0x0D) { if (Position + 1 != End && *(Position + 1) == 0x0A) return Position + 2; return Position + 1; } if (*Position == 0x0A) return Position + 1; return Position; } StringRef::iterator Scanner::skip_s_space(StringRef::iterator Position) { if (Position == End) return Position; if (*Position == ' ') return Position + 1; return Position; } StringRef::iterator Scanner::skip_s_white(StringRef::iterator Position) { if (Position == End) return Position; if (*Position == ' ' || *Position == '\t') return Position + 1; return Position; } StringRef::iterator Scanner::skip_ns_char(StringRef::iterator Position) { if (Position == End) return Position; if (*Position == ' ' || *Position == '\t') return Position; return skip_nb_char(Position); } StringRef::iterator Scanner::skip_while( SkipWhileFunc Func , StringRef::iterator Position) { while (true) { StringRef::iterator i = (this->*Func)(Position); if (i == Position) break; Position = i; } return Position; } void Scanner::advanceWhile(SkipWhileFunc Func) { auto Final = skip_while(Func, Current); Column += Final - Current; Current = Final; } static bool is_ns_hex_digit(const char C) { return (C >= '0' && C <= '9') || (C >= 'a' && C <= 'z') || (C >= 'A' && C <= 'Z'); } static bool is_ns_word_char(const char C) { return C == '-' || (C >= 'a' && C <= 'z') || (C >= 'A' && C <= 'Z'); } void Scanner::scan_ns_uri_char() { while (true) { if (Current == End) break; if (( *Current == '%' && Current + 2 < End && is_ns_hex_digit(*(Current + 1)) && is_ns_hex_digit(*(Current + 2))) || is_ns_word_char(*Current) || StringRef(Current, 1).find_first_of("#;/?:@&=+$,_.!~*'()[]") != StringRef::npos) { ++Current; ++Column; } else break; } } bool Scanner::consume(uint32_t Expected) { if (Expected >= 0x80) { setError("Cannot consume non-ascii characters"); return false; } if (Current == End) return false; if (uint8_t(*Current) >= 0x80) { setError("Cannot consume non-ascii characters"); return false; } if (uint8_t(*Current) == Expected) { ++Current; ++Column; return true; } return false; } void Scanner::skip(uint32_t Distance) { Current += Distance; Column += Distance; assert(Current <= End && "Skipped past the end"); } bool Scanner::isBlankOrBreak(StringRef::iterator Position) { if (Position == End) return false; return *Position == ' ' || *Position == '\t' || *Position == '\r' || *Position == '\n'; } bool Scanner::consumeLineBreakIfPresent() { auto Next = skip_b_break(Current); if (Next == Current) return false; Column = 0; ++Line; Current = Next; return true; } void Scanner::saveSimpleKeyCandidate( TokenQueueT::iterator Tok , unsigned AtColumn , bool IsRequired) { if (IsSimpleKeyAllowed) { SimpleKey SK; SK.Tok = Tok; SK.Line = Line; SK.Column = AtColumn; SK.IsRequired = IsRequired; SK.FlowLevel = FlowLevel; SimpleKeys.push_back(SK); } } void Scanner::removeStaleSimpleKeyCandidates() { for (SmallVectorImpl::iterator i = SimpleKeys.begin(); i != SimpleKeys.end();) { if (i->Line != Line || i->Column + 1024 < Column) { if (i->IsRequired) setError( "Could not find expected : for simple key" , i->Tok->Range.begin()); i = SimpleKeys.erase(i); } else ++i; } } void Scanner::removeSimpleKeyCandidatesOnFlowLevel(unsigned Level) { if (!SimpleKeys.empty() && (SimpleKeys.end() - 1)->FlowLevel == Level) SimpleKeys.pop_back(); } bool Scanner::unrollIndent(int ToColumn) { Token T; // Indentation is ignored in flow. if (FlowLevel != 0) return true; while (Indent > ToColumn) { T.Kind = Token::TK_BlockEnd; T.Range = StringRef(Current, 1); TokenQueue.push_back(T); Indent = Indents.pop_back_val(); } return true; } bool Scanner::rollIndent( int ToColumn , Token::TokenKind Kind , TokenQueueT::iterator InsertPoint) { if (FlowLevel) return true; if (Indent < ToColumn) { Indents.push_back(Indent); Indent = ToColumn; Token T; T.Kind = Kind; T.Range = StringRef(Current, 0); TokenQueue.insert(InsertPoint, T); } return true; } void Scanner::skipComment() { if (*Current != '#') return; while (true) { // This may skip more than one byte, thus Column is only incremented // for code points. StringRef::iterator I = skip_nb_char(Current); if (I == Current) break; Current = I; ++Column; } } void Scanner::scanToNextToken() { while (true) { while (*Current == ' ' || *Current == '\t') { skip(1); } skipComment(); // Skip EOL. StringRef::iterator i = skip_b_break(Current); if (i == Current) break; Current = i; ++Line; Column = 0; // New lines may start a simple key. if (!FlowLevel) IsSimpleKeyAllowed = true; } } bool Scanner::scanStreamStart() { IsStartOfStream = false; EncodingInfo EI = getUnicodeEncoding(currentInput()); Token T; T.Kind = Token::TK_StreamStart; T.Range = StringRef(Current, EI.second); TokenQueue.push_back(T); Current += EI.second; return true; } bool Scanner::scanStreamEnd() { // Force an ending new line if one isn't present. if (Column != 0) { Column = 0; ++Line; } unrollIndent(-1); SimpleKeys.clear(); IsSimpleKeyAllowed = false; Token T; T.Kind = Token::TK_StreamEnd; T.Range = StringRef(Current, 0); TokenQueue.push_back(T); return true; } bool Scanner::scanDirective() { // Reset the indentation level. unrollIndent(-1); SimpleKeys.clear(); IsSimpleKeyAllowed = false; StringRef::iterator Start = Current; consume('%'); StringRef::iterator NameStart = Current; Current = skip_while(&Scanner::skip_ns_char, Current); StringRef Name(NameStart, Current - NameStart); Current = skip_while(&Scanner::skip_s_white, Current); Token T; if (Name == "YAML") { Current = skip_while(&Scanner::skip_ns_char, Current); T.Kind = Token::TK_VersionDirective; T.Range = StringRef(Start, Current - Start); TokenQueue.push_back(T); return true; } else if(Name == "TAG") { Current = skip_while(&Scanner::skip_ns_char, Current); Current = skip_while(&Scanner::skip_s_white, Current); Current = skip_while(&Scanner::skip_ns_char, Current); T.Kind = Token::TK_TagDirective; T.Range = StringRef(Start, Current - Start); TokenQueue.push_back(T); return true; } return false; } bool Scanner::scanDocumentIndicator(bool IsStart) { unrollIndent(-1); SimpleKeys.clear(); IsSimpleKeyAllowed = false; Token T; T.Kind = IsStart ? Token::TK_DocumentStart : Token::TK_DocumentEnd; T.Range = StringRef(Current, 3); skip(3); TokenQueue.push_back(T); return true; } bool Scanner::scanFlowCollectionStart(bool IsSequence) { Token T; T.Kind = IsSequence ? Token::TK_FlowSequenceStart : Token::TK_FlowMappingStart; T.Range = StringRef(Current, 1); skip(1); TokenQueue.push_back(T); // [ and { may begin a simple key. saveSimpleKeyCandidate(--TokenQueue.end(), Column - 1, false); // And may also be followed by a simple key. IsSimpleKeyAllowed = true; ++FlowLevel; return true; } bool Scanner::scanFlowCollectionEnd(bool IsSequence) { removeSimpleKeyCandidatesOnFlowLevel(FlowLevel); IsSimpleKeyAllowed = false; Token T; T.Kind = IsSequence ? Token::TK_FlowSequenceEnd : Token::TK_FlowMappingEnd; T.Range = StringRef(Current, 1); skip(1); TokenQueue.push_back(T); if (FlowLevel) --FlowLevel; return true; } bool Scanner::scanFlowEntry() { removeSimpleKeyCandidatesOnFlowLevel(FlowLevel); IsSimpleKeyAllowed = true; Token T; T.Kind = Token::TK_FlowEntry; T.Range = StringRef(Current, 1); skip(1); TokenQueue.push_back(T); return true; } bool Scanner::scanBlockEntry() { rollIndent(Column, Token::TK_BlockSequenceStart, TokenQueue.end()); removeSimpleKeyCandidatesOnFlowLevel(FlowLevel); IsSimpleKeyAllowed = true; Token T; T.Kind = Token::TK_BlockEntry; T.Range = StringRef(Current, 1); skip(1); TokenQueue.push_back(T); return true; } bool Scanner::scanKey() { if (!FlowLevel) rollIndent(Column, Token::TK_BlockMappingStart, TokenQueue.end()); removeSimpleKeyCandidatesOnFlowLevel(FlowLevel); IsSimpleKeyAllowed = !FlowLevel; Token T; T.Kind = Token::TK_Key; T.Range = StringRef(Current, 1); skip(1); TokenQueue.push_back(T); return true; } bool Scanner::scanValue() { // If the previous token could have been a simple key, insert the key token // into the token queue. if (!SimpleKeys.empty()) { SimpleKey SK = SimpleKeys.pop_back_val(); Token T; T.Kind = Token::TK_Key; T.Range = SK.Tok->Range; TokenQueueT::iterator i, e; for (i = TokenQueue.begin(), e = TokenQueue.end(); i != e; ++i) { if (i == SK.Tok) break; } if (i == e) { Failed = true; return false; } i = TokenQueue.insert(i, T); // We may also need to add a Block-Mapping-Start token. rollIndent(SK.Column, Token::TK_BlockMappingStart, i); IsSimpleKeyAllowed = false; } else { if (!FlowLevel) rollIndent(Column, Token::TK_BlockMappingStart, TokenQueue.end()); IsSimpleKeyAllowed = !FlowLevel; } Token T; T.Kind = Token::TK_Value; T.Range = StringRef(Current, 1); skip(1); TokenQueue.push_back(T); return true; } // Forbidding inlining improves performance by roughly 20%. // FIXME: Remove once llvm optimizes this to the faster version without hints. LLVM_ATTRIBUTE_NOINLINE static bool wasEscaped(StringRef::iterator First, StringRef::iterator Position); // Returns whether a character at 'Position' was escaped with a leading '\'. // 'First' specifies the position of the first character in the string. static bool wasEscaped(StringRef::iterator First, StringRef::iterator Position) { assert(Position - 1 >= First); StringRef::iterator I = Position - 1; // We calculate the number of consecutive '\'s before the current position // by iterating backwards through our string. while (I >= First && *I == '\\') --I; // (Position - 1 - I) now contains the number of '\'s before the current // position. If it is odd, the character at 'Position' was escaped. return (Position - 1 - I) % 2 == 1; } bool Scanner::scanFlowScalar(bool IsDoubleQuoted) { StringRef::iterator Start = Current; unsigned ColStart = Column; if (IsDoubleQuoted) { do { ++Current; while (Current != End && *Current != '"') ++Current; // Repeat until the previous character was not a '\' or was an escaped // backslash. } while ( Current != End && *(Current - 1) == '\\' && wasEscaped(Start + 1, Current)); } else { skip(1); while (true) { // Skip a ' followed by another '. if (Current + 1 < End && *Current == '\'' && *(Current + 1) == '\'') { skip(2); continue; } else if (*Current == '\'') break; StringRef::iterator i = skip_nb_char(Current); if (i == Current) { i = skip_b_break(Current); if (i == Current) break; Current = i; Column = 0; ++Line; } else { if (i == End) break; Current = i; ++Column; } } } if (Current == End) { setError("Expected quote at end of scalar", Current); return false; } skip(1); // Skip ending quote. Token T; T.Kind = Token::TK_Scalar; T.Range = StringRef(Start, Current - Start); TokenQueue.push_back(T); saveSimpleKeyCandidate(--TokenQueue.end(), ColStart, false); IsSimpleKeyAllowed = false; return true; } bool Scanner::scanPlainScalar() { StringRef::iterator Start = Current; unsigned ColStart = Column; unsigned LeadingBlanks = 0; assert(Indent >= -1 && "Indent must be >= -1 !"); unsigned indent = static_cast(Indent + 1); while (true) { if (*Current == '#') break; while (!isBlankOrBreak(Current)) { if ( FlowLevel && *Current == ':' && !(isBlankOrBreak(Current + 1) || *(Current + 1) == ',')) { setError("Found unexpected ':' while scanning a plain scalar", Current); return false; } // Check for the end of the plain scalar. if ( (*Current == ':' && isBlankOrBreak(Current + 1)) || ( FlowLevel && (StringRef(Current, 1).find_first_of(",:?[]{}") != StringRef::npos))) break; StringRef::iterator i = skip_nb_char(Current); if (i == Current) break; Current = i; ++Column; } // Are we at the end? if (!isBlankOrBreak(Current)) break; // Eat blanks. StringRef::iterator Tmp = Current; while (isBlankOrBreak(Tmp)) { StringRef::iterator i = skip_s_white(Tmp); if (i != Tmp) { if (LeadingBlanks && (Column < indent) && *Tmp == '\t') { setError("Found invalid tab character in indentation", Tmp); return false; } Tmp = i; ++Column; } else { i = skip_b_break(Tmp); if (!LeadingBlanks) LeadingBlanks = 1; Tmp = i; Column = 0; ++Line; } } if (!FlowLevel && Column < indent) break; Current = Tmp; } if (Start == Current) { setError("Got empty plain scalar", Start); return false; } Token T; T.Kind = Token::TK_Scalar; T.Range = StringRef(Start, Current - Start); TokenQueue.push_back(T); // Plain scalars can be simple keys. saveSimpleKeyCandidate(--TokenQueue.end(), ColStart, false); IsSimpleKeyAllowed = false; return true; } bool Scanner::scanAliasOrAnchor(bool IsAlias) { StringRef::iterator Start = Current; unsigned ColStart = Column; skip(1); while(true) { if ( *Current == '[' || *Current == ']' || *Current == '{' || *Current == '}' || *Current == ',' || *Current == ':') break; StringRef::iterator i = skip_ns_char(Current); if (i == Current) break; Current = i; ++Column; } if (Start == Current) { setError("Got empty alias or anchor", Start); return false; } Token T; T.Kind = IsAlias ? Token::TK_Alias : Token::TK_Anchor; T.Range = StringRef(Start, Current - Start); TokenQueue.push_back(T); // Alias and anchors can be simple keys. saveSimpleKeyCandidate(--TokenQueue.end(), ColStart, false); IsSimpleKeyAllowed = false; return true; } char Scanner::scanBlockChompingIndicator() { char Indicator = ' '; if (Current != End && (*Current == '+' || *Current == '-')) { Indicator = *Current; skip(1); } return Indicator; } /// Get the number of line breaks after chomping. /// /// Return the number of trailing line breaks to emit, depending on /// \p ChompingIndicator. static unsigned getChompedLineBreaks(char ChompingIndicator, unsigned LineBreaks, StringRef Str) { if (ChompingIndicator == '-') // Strip all line breaks. return 0; if (ChompingIndicator == '+') // Keep all line breaks. return LineBreaks; // Clip trailing lines. return Str.empty() ? 0 : 1; } unsigned Scanner::scanBlockIndentationIndicator() { unsigned Indent = 0; if (Current != End && (*Current >= '1' && *Current <= '9')) { Indent = unsigned(*Current - '0'); skip(1); } return Indent; } bool Scanner::scanBlockScalarHeader(char &ChompingIndicator, unsigned &IndentIndicator, bool &IsDone) { auto Start = Current; ChompingIndicator = scanBlockChompingIndicator(); IndentIndicator = scanBlockIndentationIndicator(); // Check for the chomping indicator once again. if (ChompingIndicator == ' ') ChompingIndicator = scanBlockChompingIndicator(); Current = skip_while(&Scanner::skip_s_white, Current); skipComment(); if (Current == End) { // EOF, we have an empty scalar. Token T; T.Kind = Token::TK_BlockScalar; T.Range = StringRef(Start, Current - Start); TokenQueue.push_back(T); IsDone = true; return true; } if (!consumeLineBreakIfPresent()) { setError("Expected a line break after block scalar header", Current); return false; } return true; } bool Scanner::findBlockScalarIndent(unsigned &BlockIndent, unsigned BlockExitIndent, unsigned &LineBreaks, bool &IsDone) { unsigned MaxAllSpaceLineCharacters = 0; StringRef::iterator LongestAllSpaceLine; while (true) { advanceWhile(&Scanner::skip_s_space); if (skip_nb_char(Current) != Current) { // This line isn't empty, so try and find the indentation. if (Column <= BlockExitIndent) { // End of the block literal. IsDone = true; return true; } // We found the block's indentation. BlockIndent = Column; if (MaxAllSpaceLineCharacters > BlockIndent) { setError( "Leading all-spaces line must be smaller than the block indent", LongestAllSpaceLine); return false; } return true; } if (skip_b_break(Current) != Current && Column > MaxAllSpaceLineCharacters) { // Record the longest all-space line in case it's longer than the // discovered block indent. MaxAllSpaceLineCharacters = Column; LongestAllSpaceLine = Current; } // Check for EOF. if (Current == End) { IsDone = true; return true; } if (!consumeLineBreakIfPresent()) { IsDone = true; return true; } ++LineBreaks; } return true; } bool Scanner::scanBlockScalarIndent(unsigned BlockIndent, unsigned BlockExitIndent, bool &IsDone) { // Skip the indentation. while (Column < BlockIndent) { auto I = skip_s_space(Current); if (I == Current) break; Current = I; ++Column; } if (skip_nb_char(Current) == Current) return true; if (Column <= BlockExitIndent) { // End of the block literal. IsDone = true; return true; } if (Column < BlockIndent) { if (Current != End && *Current == '#') { // Trailing comment. IsDone = true; return true; } setError("A text line is less indented than the block scalar", Current); return false; } return true; // A normal text line. } bool Scanner::scanBlockScalar(bool IsLiteral) { // Eat '|' or '>' assert(*Current == '|' || *Current == '>'); skip(1); char ChompingIndicator; unsigned BlockIndent; bool IsDone = false; if (!scanBlockScalarHeader(ChompingIndicator, BlockIndent, IsDone)) return false; if (IsDone) return true; auto Start = Current; unsigned BlockExitIndent = Indent < 0 ? 0 : (unsigned)Indent; unsigned LineBreaks = 0; if (BlockIndent == 0) { if (!findBlockScalarIndent(BlockIndent, BlockExitIndent, LineBreaks, IsDone)) return false; } // Scan the block's scalars body. SmallString<256> Str; while (!IsDone) { if (!scanBlockScalarIndent(BlockIndent, BlockExitIndent, IsDone)) return false; if (IsDone) break; // Parse the current line. auto LineStart = Current; advanceWhile(&Scanner::skip_nb_char); if (LineStart != Current) { Str.append(LineBreaks, '\n'); Str.append(StringRef(LineStart, Current - LineStart)); LineBreaks = 0; } // Check for EOF. if (Current == End) break; if (!consumeLineBreakIfPresent()) break; ++LineBreaks; } if (Current == End && !LineBreaks) // Ensure that there is at least one line break before the end of file. LineBreaks = 1; Str.append(getChompedLineBreaks(ChompingIndicator, LineBreaks, Str), '\n'); // New lines may start a simple key. if (!FlowLevel) IsSimpleKeyAllowed = true; Token T; T.Kind = Token::TK_BlockScalar; T.Range = StringRef(Start, Current - Start); T.Value = Str.str().str(); TokenQueue.push_back(T); return true; } bool Scanner::scanTag() { StringRef::iterator Start = Current; unsigned ColStart = Column; skip(1); // Eat !. if (Current == End || isBlankOrBreak(Current)); // An empty tag. else if (*Current == '<') { skip(1); scan_ns_uri_char(); if (!consume('>')) return false; } else { // FIXME: Actually parse the c-ns-shorthand-tag rule. Current = skip_while(&Scanner::skip_ns_char, Current); } Token T; T.Kind = Token::TK_Tag; T.Range = StringRef(Start, Current - Start); TokenQueue.push_back(T); // Tags can be simple keys. saveSimpleKeyCandidate(--TokenQueue.end(), ColStart, false); IsSimpleKeyAllowed = false; return true; } bool Scanner::fetchMoreTokens() { if (IsStartOfStream) return scanStreamStart(); scanToNextToken(); if (Current == End) return scanStreamEnd(); removeStaleSimpleKeyCandidates(); unrollIndent(Column); if (Column == 0 && *Current == '%') return scanDirective(); if (Column == 0 && Current + 4 <= End && *Current == '-' && *(Current + 1) == '-' && *(Current + 2) == '-' && (Current + 3 == End || isBlankOrBreak(Current + 3))) return scanDocumentIndicator(true); if (Column == 0 && Current + 4 <= End && *Current == '.' && *(Current + 1) == '.' && *(Current + 2) == '.' && (Current + 3 == End || isBlankOrBreak(Current + 3))) return scanDocumentIndicator(false); if (*Current == '[') return scanFlowCollectionStart(true); if (*Current == '{') return scanFlowCollectionStart(false); if (*Current == ']') return scanFlowCollectionEnd(true); if (*Current == '}') return scanFlowCollectionEnd(false); if (*Current == ',') return scanFlowEntry(); if (*Current == '-' && isBlankOrBreak(Current + 1)) return scanBlockEntry(); if (*Current == '?' && (FlowLevel || isBlankOrBreak(Current + 1))) return scanKey(); if (*Current == ':' && (FlowLevel || isBlankOrBreak(Current + 1))) return scanValue(); if (*Current == '*') return scanAliasOrAnchor(true); if (*Current == '&') return scanAliasOrAnchor(false); if (*Current == '!') return scanTag(); if (*Current == '|' && !FlowLevel) return scanBlockScalar(true); if (*Current == '>' && !FlowLevel) return scanBlockScalar(false); if (*Current == '\'') return scanFlowScalar(false); if (*Current == '"') return scanFlowScalar(true); // Get a plain scalar. StringRef FirstChar(Current, 1); if (!(isBlankOrBreak(Current) || FirstChar.find_first_of("-?:,[]{}#&*!|>'\"%@`") != StringRef::npos) || (*Current == '-' && !isBlankOrBreak(Current + 1)) || (!FlowLevel && (*Current == '?' || *Current == ':') && isBlankOrBreak(Current + 1)) || (!FlowLevel && *Current == ':' && Current + 2 < End && *(Current + 1) == ':' && !isBlankOrBreak(Current + 2))) return scanPlainScalar(); setError("Unrecognized character while tokenizing."); return false; } Stream::Stream(StringRef Input, SourceMgr &SM, bool ShowColors, std::error_code *EC) : scanner(new Scanner(Input, SM, ShowColors, EC)), CurrentDoc() {} Stream::Stream(MemoryBufferRef InputBuffer, SourceMgr &SM, bool ShowColors, std::error_code *EC) : scanner(new Scanner(InputBuffer, SM, ShowColors, EC)), CurrentDoc() {} Stream::~Stream() = default; bool Stream::failed() { return scanner->failed(); } void Stream::printError(Node *N, const Twine &Msg) { SMRange Range = N ? N->getSourceRange() : SMRange(); scanner->printError( Range.Start , SourceMgr::DK_Error , Msg , Range); } document_iterator Stream::begin() { if (CurrentDoc) report_fatal_error("Can only iterate over the stream once"); // Skip Stream-Start. scanner->getNext(); CurrentDoc.reset(new Document(*this)); return document_iterator(CurrentDoc); } document_iterator Stream::end() { return document_iterator(); } void Stream::skip() { for (document_iterator i = begin(), e = end(); i != e; ++i) i->skip(); } Node::Node(unsigned int Type, std::unique_ptr &D, StringRef A, StringRef T) : Doc(D), TypeID(Type), Anchor(A), Tag(T) { SMLoc Start = SMLoc::getFromPointer(peekNext().Range.begin()); SourceRange = SMRange(Start, Start); } std::string Node::getVerbatimTag() const { StringRef Raw = getRawTag(); if (!Raw.empty() && Raw != "!") { std::string Ret; if (Raw.find_last_of('!') == 0) { Ret = Doc->getTagMap().find("!")->second; Ret += Raw.substr(1); return Ret; } else if (Raw.startswith("!!")) { Ret = Doc->getTagMap().find("!!")->second; Ret += Raw.substr(2); return Ret; } else { StringRef TagHandle = Raw.substr(0, Raw.find_last_of('!') + 1); std::map::const_iterator It = Doc->getTagMap().find(TagHandle); if (It != Doc->getTagMap().end()) Ret = It->second; else { Token T; T.Kind = Token::TK_Tag; T.Range = TagHandle; setError(Twine("Unknown tag handle ") + TagHandle, T); } Ret += Raw.substr(Raw.find_last_of('!') + 1); return Ret; } } switch (getType()) { case NK_Null: return "tag:yaml.org,2002:null"; case NK_Scalar: case NK_BlockScalar: // TODO: Tag resolution. return "tag:yaml.org,2002:str"; case NK_Mapping: return "tag:yaml.org,2002:map"; case NK_Sequence: return "tag:yaml.org,2002:seq"; } return ""; } Token &Node::peekNext() { return Doc->peekNext(); } Token Node::getNext() { return Doc->getNext(); } Node *Node::parseBlockNode() { return Doc->parseBlockNode(); } BumpPtrAllocator &Node::getAllocator() { return Doc->NodeAllocator; } void Node::setError(const Twine &Msg, Token &Tok) const { Doc->setError(Msg, Tok); } bool Node::failed() const { return Doc->failed(); } StringRef ScalarNode::getValue(SmallVectorImpl &Storage) const { // TODO: Handle newlines properly. We need to remove leading whitespace. if (Value[0] == '"') { // Double quoted. // Pull off the leading and trailing "s. StringRef UnquotedValue = Value.substr(1, Value.size() - 2); // Search for characters that would require unescaping the value. StringRef::size_type i = UnquotedValue.find_first_of("\\\r\n"); if (i != StringRef::npos) return unescapeDoubleQuoted(UnquotedValue, i, Storage); return UnquotedValue; } else if (Value[0] == '\'') { // Single quoted. // Pull off the leading and trailing 's. StringRef UnquotedValue = Value.substr(1, Value.size() - 2); StringRef::size_type i = UnquotedValue.find('\''); if (i != StringRef::npos) { // We're going to need Storage. Storage.clear(); Storage.reserve(UnquotedValue.size()); for (; i != StringRef::npos; i = UnquotedValue.find('\'')) { StringRef Valid(UnquotedValue.begin(), i); Storage.insert(Storage.end(), Valid.begin(), Valid.end()); Storage.push_back('\''); UnquotedValue = UnquotedValue.substr(i + 2); } Storage.insert(Storage.end(), UnquotedValue.begin(), UnquotedValue.end()); return StringRef(Storage.begin(), Storage.size()); } return UnquotedValue; } // Plain or block. return Value.rtrim(' '); } StringRef ScalarNode::unescapeDoubleQuoted( StringRef UnquotedValue , StringRef::size_type i , SmallVectorImpl &Storage) const { // Use Storage to build proper value. Storage.clear(); Storage.reserve(UnquotedValue.size()); for (; i != StringRef::npos; i = UnquotedValue.find_first_of("\\\r\n")) { // Insert all previous chars into Storage. StringRef Valid(UnquotedValue.begin(), i); Storage.insert(Storage.end(), Valid.begin(), Valid.end()); // Chop off inserted chars. UnquotedValue = UnquotedValue.substr(i); assert(!UnquotedValue.empty() && "Can't be empty!"); // Parse escape or line break. switch (UnquotedValue[0]) { case '\r': case '\n': Storage.push_back('\n'); if ( UnquotedValue.size() > 1 && (UnquotedValue[1] == '\r' || UnquotedValue[1] == '\n')) UnquotedValue = UnquotedValue.substr(1); UnquotedValue = UnquotedValue.substr(1); break; default: if (UnquotedValue.size() == 1) { Token T; T.Range = StringRef(UnquotedValue.begin(), 1); setError("Unrecognized escape code", T); return ""; } UnquotedValue = UnquotedValue.substr(1); switch (UnquotedValue[0]) { default: { Token T; T.Range = StringRef(UnquotedValue.begin(), 1); setError("Unrecognized escape code", T); return ""; } case '\r': case '\n': // Remove the new line. if ( UnquotedValue.size() > 1 && (UnquotedValue[1] == '\r' || UnquotedValue[1] == '\n')) UnquotedValue = UnquotedValue.substr(1); // If this was just a single byte newline, it will get skipped // below. break; case '0': Storage.push_back(0x00); break; case 'a': Storage.push_back(0x07); break; case 'b': Storage.push_back(0x08); break; case 't': case 0x09: Storage.push_back(0x09); break; case 'n': Storage.push_back(0x0A); break; case 'v': Storage.push_back(0x0B); break; case 'f': Storage.push_back(0x0C); break; case 'r': Storage.push_back(0x0D); break; case 'e': Storage.push_back(0x1B); break; case ' ': Storage.push_back(0x20); break; case '"': Storage.push_back(0x22); break; case '/': Storage.push_back(0x2F); break; case '\\': Storage.push_back(0x5C); break; case 'N': encodeUTF8(0x85, Storage); break; case '_': encodeUTF8(0xA0, Storage); break; case 'L': encodeUTF8(0x2028, Storage); break; case 'P': encodeUTF8(0x2029, Storage); break; case 'x': { if (UnquotedValue.size() < 3) // TODO: Report error. break; unsigned int UnicodeScalarValue; if (UnquotedValue.substr(1, 2).getAsInteger(16, UnicodeScalarValue)) // TODO: Report error. UnicodeScalarValue = 0xFFFD; encodeUTF8(UnicodeScalarValue, Storage); UnquotedValue = UnquotedValue.substr(2); break; } case 'u': { if (UnquotedValue.size() < 5) // TODO: Report error. break; unsigned int UnicodeScalarValue; if (UnquotedValue.substr(1, 4).getAsInteger(16, UnicodeScalarValue)) // TODO: Report error. UnicodeScalarValue = 0xFFFD; encodeUTF8(UnicodeScalarValue, Storage); UnquotedValue = UnquotedValue.substr(4); break; } case 'U': { if (UnquotedValue.size() < 9) // TODO: Report error. break; unsigned int UnicodeScalarValue; if (UnquotedValue.substr(1, 8).getAsInteger(16, UnicodeScalarValue)) // TODO: Report error. UnicodeScalarValue = 0xFFFD; encodeUTF8(UnicodeScalarValue, Storage); UnquotedValue = UnquotedValue.substr(8); break; } } UnquotedValue = UnquotedValue.substr(1); } } Storage.insert(Storage.end(), UnquotedValue.begin(), UnquotedValue.end()); return StringRef(Storage.begin(), Storage.size()); } Node *KeyValueNode::getKey() { if (Key) return Key; // Handle implicit null keys. { Token &t = peekNext(); if ( t.Kind == Token::TK_BlockEnd || t.Kind == Token::TK_Value || t.Kind == Token::TK_Error) { return Key = new (getAllocator()) NullNode(Doc); } if (t.Kind == Token::TK_Key) getNext(); // skip TK_Key. } // Handle explicit null keys. Token &t = peekNext(); if (t.Kind == Token::TK_BlockEnd || t.Kind == Token::TK_Value) { return Key = new (getAllocator()) NullNode(Doc); } // We've got a normal key. return Key = parseBlockNode(); } Node *KeyValueNode::getValue() { if (Value) return Value; if (Node* Key = getKey()) Key->skip(); else { setError("Null key in Key Value.", peekNext()); return Value = new (getAllocator()) NullNode(Doc); } if (failed()) return Value = new (getAllocator()) NullNode(Doc); // Handle implicit null values. { Token &t = peekNext(); if ( t.Kind == Token::TK_BlockEnd || t.Kind == Token::TK_FlowMappingEnd || t.Kind == Token::TK_Key || t.Kind == Token::TK_FlowEntry || t.Kind == Token::TK_Error) { return Value = new (getAllocator()) NullNode(Doc); } if (t.Kind != Token::TK_Value) { setError("Unexpected token in Key Value.", t); return Value = new (getAllocator()) NullNode(Doc); } getNext(); // skip TK_Value. } // Handle explicit null values. Token &t = peekNext(); if (t.Kind == Token::TK_BlockEnd || t.Kind == Token::TK_Key) { return Value = new (getAllocator()) NullNode(Doc); } // We got a normal value. return Value = parseBlockNode(); } void MappingNode::increment() { if (failed()) { IsAtEnd = true; CurrentEntry = nullptr; return; } if (CurrentEntry) { CurrentEntry->skip(); if (Type == MT_Inline) { IsAtEnd = true; CurrentEntry = nullptr; return; } } Token T = peekNext(); if (T.Kind == Token::TK_Key || T.Kind == Token::TK_Scalar) { // KeyValueNode eats the TK_Key. That way it can detect null keys. CurrentEntry = new (getAllocator()) KeyValueNode(Doc); } else if (Type == MT_Block) { switch (T.Kind) { case Token::TK_BlockEnd: getNext(); IsAtEnd = true; CurrentEntry = nullptr; break; default: setError("Unexpected token. Expected Key or Block End", T); LLVM_FALLTHROUGH; case Token::TK_Error: IsAtEnd = true; CurrentEntry = nullptr; } } else { switch (T.Kind) { case Token::TK_FlowEntry: // Eat the flow entry and recurse. getNext(); return increment(); case Token::TK_FlowMappingEnd: getNext(); LLVM_FALLTHROUGH; case Token::TK_Error: // Set this to end iterator. IsAtEnd = true; CurrentEntry = nullptr; break; default: setError( "Unexpected token. Expected Key, Flow Entry, or Flow " "Mapping End." , T); IsAtEnd = true; CurrentEntry = nullptr; } } } void SequenceNode::increment() { if (failed()) { IsAtEnd = true; CurrentEntry = nullptr; return; } if (CurrentEntry) CurrentEntry->skip(); Token T = peekNext(); if (SeqType == ST_Block) { switch (T.Kind) { case Token::TK_BlockEntry: getNext(); CurrentEntry = parseBlockNode(); if (!CurrentEntry) { // An error occurred. IsAtEnd = true; CurrentEntry = nullptr; } break; case Token::TK_BlockEnd: getNext(); IsAtEnd = true; CurrentEntry = nullptr; break; default: setError( "Unexpected token. Expected Block Entry or Block End." , T); LLVM_FALLTHROUGH; case Token::TK_Error: IsAtEnd = true; CurrentEntry = nullptr; } } else if (SeqType == ST_Indentless) { switch (T.Kind) { case Token::TK_BlockEntry: getNext(); CurrentEntry = parseBlockNode(); if (!CurrentEntry) { // An error occurred. IsAtEnd = true; CurrentEntry = nullptr; } break; default: case Token::TK_Error: IsAtEnd = true; CurrentEntry = nullptr; } } else if (SeqType == ST_Flow) { switch (T.Kind) { case Token::TK_FlowEntry: // Eat the flow entry and recurse. getNext(); WasPreviousTokenFlowEntry = true; return increment(); case Token::TK_FlowSequenceEnd: getNext(); LLVM_FALLTHROUGH; case Token::TK_Error: // Set this to end iterator. IsAtEnd = true; CurrentEntry = nullptr; break; case Token::TK_StreamEnd: case Token::TK_DocumentEnd: case Token::TK_DocumentStart: setError("Could not find closing ]!", T); // Set this to end iterator. IsAtEnd = true; CurrentEntry = nullptr; break; default: if (!WasPreviousTokenFlowEntry) { setError("Expected , between entries!", T); IsAtEnd = true; CurrentEntry = nullptr; break; } // Otherwise it must be a flow entry. CurrentEntry = parseBlockNode(); if (!CurrentEntry) { IsAtEnd = true; } WasPreviousTokenFlowEntry = false; break; } } } Document::Document(Stream &S) : stream(S), Root(nullptr) { // Tag maps starts with two default mappings. TagMap["!"] = "!"; TagMap["!!"] = "tag:yaml.org,2002:"; if (parseDirectives()) expectToken(Token::TK_DocumentStart); Token &T = peekNext(); if (T.Kind == Token::TK_DocumentStart) getNext(); } bool Document::skip() { if (stream.scanner->failed()) return false; if (!Root) getRoot(); Root->skip(); Token &T = peekNext(); if (T.Kind == Token::TK_StreamEnd) return false; if (T.Kind == Token::TK_DocumentEnd) { getNext(); return skip(); } return true; } Token &Document::peekNext() { return stream.scanner->peekNext(); } Token Document::getNext() { return stream.scanner->getNext(); } void Document::setError(const Twine &Message, Token &Location) const { stream.scanner->setError(Message, Location.Range.begin()); } bool Document::failed() const { return stream.scanner->failed(); } Node *Document::parseBlockNode() { Token T = peekNext(); // Handle properties. Token AnchorInfo; Token TagInfo; parse_property: switch (T.Kind) { case Token::TK_Alias: getNext(); return new (NodeAllocator) AliasNode(stream.CurrentDoc, T.Range.substr(1)); case Token::TK_Anchor: if (AnchorInfo.Kind == Token::TK_Anchor) { setError("Already encountered an anchor for this node!", T); return nullptr; } AnchorInfo = getNext(); // Consume TK_Anchor. T = peekNext(); goto parse_property; case Token::TK_Tag: if (TagInfo.Kind == Token::TK_Tag) { setError("Already encountered a tag for this node!", T); return nullptr; } TagInfo = getNext(); // Consume TK_Tag. T = peekNext(); goto parse_property; default: break; } switch (T.Kind) { case Token::TK_BlockEntry: // We got an unindented BlockEntry sequence. This is not terminated with // a BlockEnd. // Don't eat the TK_BlockEntry, SequenceNode needs it. return new (NodeAllocator) SequenceNode( stream.CurrentDoc , AnchorInfo.Range.substr(1) , TagInfo.Range , SequenceNode::ST_Indentless); case Token::TK_BlockSequenceStart: getNext(); return new (NodeAllocator) SequenceNode( stream.CurrentDoc , AnchorInfo.Range.substr(1) , TagInfo.Range , SequenceNode::ST_Block); case Token::TK_BlockMappingStart: getNext(); return new (NodeAllocator) MappingNode( stream.CurrentDoc , AnchorInfo.Range.substr(1) , TagInfo.Range , MappingNode::MT_Block); case Token::TK_FlowSequenceStart: getNext(); return new (NodeAllocator) SequenceNode( stream.CurrentDoc , AnchorInfo.Range.substr(1) , TagInfo.Range , SequenceNode::ST_Flow); case Token::TK_FlowMappingStart: getNext(); return new (NodeAllocator) MappingNode( stream.CurrentDoc , AnchorInfo.Range.substr(1) , TagInfo.Range , MappingNode::MT_Flow); case Token::TK_Scalar: getNext(); return new (NodeAllocator) ScalarNode( stream.CurrentDoc , AnchorInfo.Range.substr(1) , TagInfo.Range , T.Range); case Token::TK_BlockScalar: { getNext(); StringRef NullTerminatedStr(T.Value.c_str(), T.Value.length() + 1); StringRef StrCopy = NullTerminatedStr.copy(NodeAllocator).drop_back(); return new (NodeAllocator) BlockScalarNode(stream.CurrentDoc, AnchorInfo.Range.substr(1), TagInfo.Range, StrCopy, T.Range); } case Token::TK_Key: // Don't eat the TK_Key, KeyValueNode expects it. return new (NodeAllocator) MappingNode( stream.CurrentDoc , AnchorInfo.Range.substr(1) , TagInfo.Range , MappingNode::MT_Inline); case Token::TK_DocumentStart: case Token::TK_DocumentEnd: case Token::TK_StreamEnd: default: // TODO: Properly handle tags. "[!!str ]" should resolve to !!str "", not // !!null null. return new (NodeAllocator) NullNode(stream.CurrentDoc); case Token::TK_FlowMappingEnd: case Token::TK_FlowSequenceEnd: case Token::TK_FlowEntry: { if (Root && (isa(Root) || isa(Root))) return new (NodeAllocator) NullNode(stream.CurrentDoc); setError("Unexpected token", T); return nullptr; } case Token::TK_Error: return nullptr; } llvm_unreachable("Control flow shouldn't reach here."); return nullptr; } bool Document::parseDirectives() { bool isDirective = false; while (true) { Token T = peekNext(); if (T.Kind == Token::TK_TagDirective) { parseTAGDirective(); isDirective = true; } else if (T.Kind == Token::TK_VersionDirective) { parseYAMLDirective(); isDirective = true; } else break; } return isDirective; } void Document::parseYAMLDirective() { getNext(); // Eat %YAML } void Document::parseTAGDirective() { Token Tag = getNext(); // %TAG StringRef T = Tag.Range; // Strip %TAG T = T.substr(T.find_first_of(" \t")).ltrim(" \t"); std::size_t HandleEnd = T.find_first_of(" \t"); StringRef TagHandle = T.substr(0, HandleEnd); StringRef TagPrefix = T.substr(HandleEnd).ltrim(" \t"); TagMap[TagHandle] = TagPrefix; } bool Document::expectToken(int TK) { Token T = getNext(); if (T.Kind != TK) { setError("Unexpected token", T); return false; } return true; } binaryen-version_91/third_party/llvm-project/YAMLTraits.cpp000066400000000000000000000705251362402614000242740ustar00rootroot00000000000000//===- lib/Support/YAMLTraits.cpp -----------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/Support/YAMLTraits.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Errc.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Format.h" #include "llvm/Support/LineIterator.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Unicode.h" #include "llvm/Support/YAMLParser.h" #include "llvm/Support/raw_ostream.h" #include #include #include #include #include #include #include using namespace llvm; using namespace yaml; //===----------------------------------------------------------------------===// // IO //===----------------------------------------------------------------------===// IO::IO(void *Context) : Ctxt(Context) {} IO::~IO() = default; void *IO::getContext() const { return Ctxt; } void IO::setContext(void *Context) { Ctxt = Context; } //===----------------------------------------------------------------------===// // Input //===----------------------------------------------------------------------===// Input::Input(StringRef InputContent, void *Ctxt, SourceMgr::DiagHandlerTy DiagHandler, void *DiagHandlerCtxt) : IO(Ctxt), Strm(new Stream(InputContent, SrcMgr, false, &EC)) { if (DiagHandler) SrcMgr.setDiagHandler(DiagHandler, DiagHandlerCtxt); DocIterator = Strm->begin(); } Input::Input(MemoryBufferRef Input, void *Ctxt, SourceMgr::DiagHandlerTy DiagHandler, void *DiagHandlerCtxt) : IO(Ctxt), Strm(new Stream(Input, SrcMgr, false, &EC)) { if (DiagHandler) SrcMgr.setDiagHandler(DiagHandler, DiagHandlerCtxt); DocIterator = Strm->begin(); } Input::~Input() = default; std::error_code Input::error() { return EC; } // Pin the vtables to this file. void Input::HNode::anchor() {} void Input::EmptyHNode::anchor() {} void Input::ScalarHNode::anchor() {} void Input::MapHNode::anchor() {} void Input::SequenceHNode::anchor() {} bool Input::outputting() const { return false; } bool Input::setCurrentDocument() { if (DocIterator != Strm->end()) { Node *N = DocIterator->getRoot(); if (!N) { EC = make_error_code(errc::invalid_argument); return false; } if (isa(N)) { // Empty files are allowed and ignored ++DocIterator; return setCurrentDocument(); } TopNode = createHNodes(N); CurrentNode = TopNode.get(); return true; } return false; } bool Input::nextDocument() { return ++DocIterator != Strm->end(); } const Node *Input::getCurrentNode() const { return CurrentNode ? CurrentNode->_node : nullptr; } bool Input::mapTag(StringRef Tag, bool Default) { // CurrentNode can be null if setCurrentDocument() was unable to // parse the document because it was invalid or empty. if (!CurrentNode) return false; std::string foundTag = CurrentNode->_node->getVerbatimTag(); if (foundTag.empty()) { // If no tag found and 'Tag' is the default, say it was found. return Default; } // Return true iff found tag matches supplied tag. return Tag.equals(foundTag); } void Input::beginMapping() { if (EC) return; // CurrentNode can be null if the document is empty. MapHNode *MN = dyn_cast_or_null(CurrentNode); if (MN) { MN->ValidKeys.clear(); } } std::vector Input::keys() { MapHNode *MN = dyn_cast(CurrentNode); std::vector Ret; if (!MN) { setError(CurrentNode, "not a mapping"); return Ret; } for (auto &P : MN->Mapping) Ret.push_back(P.first()); return Ret; } bool Input::preflightKey(const char *Key, bool Required, bool, bool &UseDefault, void *&SaveInfo) { UseDefault = false; if (EC) return false; // CurrentNode is null for empty documents, which is an error in case required // nodes are present. if (!CurrentNode) { if (Required) EC = make_error_code(errc::invalid_argument); return false; } MapHNode *MN = dyn_cast(CurrentNode); if (!MN) { if (Required || !isa(CurrentNode)) setError(CurrentNode, "not a mapping"); return false; } MN->ValidKeys.push_back(Key); HNode *Value = MN->Mapping[Key].get(); if (!Value) { if (Required) setError(CurrentNode, Twine("missing required key '") + Key + "'"); else UseDefault = true; return false; } SaveInfo = CurrentNode; CurrentNode = Value; return true; } void Input::postflightKey(void *saveInfo) { CurrentNode = reinterpret_cast(saveInfo); } void Input::endMapping() { if (EC) return; // CurrentNode can be null if the document is empty. MapHNode *MN = dyn_cast_or_null(CurrentNode); if (!MN) return; for (const auto &NN : MN->Mapping) { if (!is_contained(MN->ValidKeys, NN.first())) { setError(NN.second.get(), Twine("unknown key '") + NN.first() + "'"); break; } } } void Input::beginFlowMapping() { beginMapping(); } void Input::endFlowMapping() { endMapping(); } unsigned Input::beginSequence() { if (SequenceHNode *SQ = dyn_cast(CurrentNode)) return SQ->Entries.size(); if (isa(CurrentNode)) return 0; // Treat case where there's a scalar "null" value as an empty sequence. if (ScalarHNode *SN = dyn_cast(CurrentNode)) { if (isNull(SN->value())) return 0; } // Any other type of HNode is an error. setError(CurrentNode, "not a sequence"); return 0; } void Input::endSequence() { } bool Input::preflightElement(unsigned Index, void *&SaveInfo) { if (EC) return false; if (SequenceHNode *SQ = dyn_cast(CurrentNode)) { SaveInfo = CurrentNode; CurrentNode = SQ->Entries[Index].get(); return true; } return false; } void Input::postflightElement(void *SaveInfo) { CurrentNode = reinterpret_cast(SaveInfo); } unsigned Input::beginFlowSequence() { return beginSequence(); } bool Input::preflightFlowElement(unsigned index, void *&SaveInfo) { if (EC) return false; if (SequenceHNode *SQ = dyn_cast(CurrentNode)) { SaveInfo = CurrentNode; CurrentNode = SQ->Entries[index].get(); return true; } return false; } void Input::postflightFlowElement(void *SaveInfo) { CurrentNode = reinterpret_cast(SaveInfo); } void Input::endFlowSequence() { } void Input::beginEnumScalar() { ScalarMatchFound = false; } bool Input::matchEnumScalar(const char *Str, bool) { if (ScalarMatchFound) return false; if (ScalarHNode *SN = dyn_cast(CurrentNode)) { if (SN->value().equals(Str)) { ScalarMatchFound = true; return true; } } return false; } bool Input::matchEnumFallback() { if (ScalarMatchFound) return false; ScalarMatchFound = true; return true; } void Input::endEnumScalar() { if (!ScalarMatchFound) { setError(CurrentNode, "unknown enumerated scalar"); } } bool Input::beginBitSetScalar(bool &DoClear) { BitValuesUsed.clear(); if (SequenceHNode *SQ = dyn_cast(CurrentNode)) { BitValuesUsed.insert(BitValuesUsed.begin(), SQ->Entries.size(), false); } else { setError(CurrentNode, "expected sequence of bit values"); } DoClear = true; return true; } bool Input::bitSetMatch(const char *Str, bool) { if (EC) return false; if (SequenceHNode *SQ = dyn_cast(CurrentNode)) { unsigned Index = 0; for (auto &N : SQ->Entries) { if (ScalarHNode *SN = dyn_cast(N.get())) { if (SN->value().equals(Str)) { BitValuesUsed[Index] = true; return true; } } else { setError(CurrentNode, "unexpected scalar in sequence of bit values"); } ++Index; } } else { setError(CurrentNode, "expected sequence of bit values"); } return false; } void Input::endBitSetScalar() { if (EC) return; if (SequenceHNode *SQ = dyn_cast(CurrentNode)) { assert(BitValuesUsed.size() == SQ->Entries.size()); for (unsigned i = 0; i < SQ->Entries.size(); ++i) { if (!BitValuesUsed[i]) { setError(SQ->Entries[i].get(), "unknown bit value"); return; } } } } void Input::scalarString(StringRef &S, QuotingType) { if (ScalarHNode *SN = dyn_cast(CurrentNode)) { S = SN->value(); } else { setError(CurrentNode, "unexpected scalar"); } } void Input::blockScalarString(StringRef &S) { scalarString(S, QuotingType::None); } void Input::scalarTag(std::string &Tag) { Tag = CurrentNode->_node->getVerbatimTag(); } void Input::setError(HNode *hnode, const Twine &message) { assert(hnode && "HNode must not be NULL"); setError(hnode->_node, message); } NodeKind Input::getNodeKind() { if (isa(CurrentNode)) return NodeKind::Scalar; else if (isa(CurrentNode)) return NodeKind::Map; else if (isa(CurrentNode)) return NodeKind::Sequence; llvm_unreachable("Unsupported node kind"); } void Input::setError(Node *node, const Twine &message) { Strm->printError(node, message); EC = make_error_code(errc::invalid_argument); } std::unique_ptr Input::createHNodes(Node *N) { SmallString<128> StringStorage; if (ScalarNode *SN = dyn_cast(N)) { StringRef KeyStr = SN->getValue(StringStorage); if (!StringStorage.empty()) { // Copy string to permanent storage KeyStr = StringStorage.str().copy(StringAllocator); } return std::make_unique(N, KeyStr); } else if (BlockScalarNode *BSN = dyn_cast(N)) { StringRef ValueCopy = BSN->getValue().copy(StringAllocator); return std::make_unique(N, ValueCopy); } else if (SequenceNode *SQ = dyn_cast(N)) { auto SQHNode = std::make_unique(N); for (Node &SN : *SQ) { auto Entry = createHNodes(&SN); if (EC) break; SQHNode->Entries.push_back(std::move(Entry)); } return std::move(SQHNode); } else if (MappingNode *Map = dyn_cast(N)) { auto mapHNode = std::make_unique(N); for (KeyValueNode &KVN : *Map) { Node *KeyNode = KVN.getKey(); ScalarNode *Key = dyn_cast_or_null(KeyNode); Node *Value = KVN.getValue(); if (!Key || !Value) { if (!Key) setError(KeyNode, "Map key must be a scalar"); if (!Value) setError(KeyNode, "Map value must not be empty"); break; } StringStorage.clear(); StringRef KeyStr = Key->getValue(StringStorage); if (!StringStorage.empty()) { // Copy string to permanent storage KeyStr = StringStorage.str().copy(StringAllocator); } auto ValueHNode = createHNodes(Value); if (EC) break; mapHNode->Mapping[KeyStr] = std::move(ValueHNode); } return std::move(mapHNode); } else if (isa(N)) { return std::make_unique(N); } else { setError(N, "unknown node kind"); return nullptr; } } void Input::setError(const Twine &Message) { setError(CurrentNode, Message); } bool Input::canElideEmptySequence() { return false; } //===----------------------------------------------------------------------===// // Output //===----------------------------------------------------------------------===// Output::Output(raw_ostream &yout, void *context, int WrapColumn) : IO(context), Out(yout), WrapColumn(WrapColumn) {} Output::~Output() = default; bool Output::outputting() const { return true; } void Output::beginMapping() { StateStack.push_back(inMapFirstKey); PaddingBeforeContainer = Padding; Padding = "\n"; } bool Output::mapTag(StringRef Tag, bool Use) { if (Use) { // If this tag is being written inside a sequence we should write the start // of the sequence before writing the tag, otherwise the tag won't be // attached to the element in the sequence, but rather the sequence itself. bool SequenceElement = false; if (StateStack.size() > 1) { auto &E = StateStack[StateStack.size() - 2]; SequenceElement = inSeqAnyElement(E) || inFlowSeqAnyElement(E); } if (SequenceElement && StateStack.back() == inMapFirstKey) { newLineCheck(); } else { output(" "); } output(Tag); if (SequenceElement) { // If we're writing the tag during the first element of a map, the tag // takes the place of the first element in the sequence. if (StateStack.back() == inMapFirstKey) { StateStack.pop_back(); StateStack.push_back(inMapOtherKey); } // Tags inside maps in sequences should act as keys in the map from a // formatting perspective, so we always want a newline in a sequence. Padding = "\n"; } } return Use; } void Output::endMapping() { // If we did not map anything, we should explicitly emit an empty map if (StateStack.back() == inMapFirstKey) { Padding = PaddingBeforeContainer; newLineCheck(); output("{}"); Padding = "\n"; } StateStack.pop_back(); } std::vector Output::keys() { report_fatal_error("invalid call"); } bool Output::preflightKey(const char *Key, bool Required, bool SameAsDefault, bool &UseDefault, void *&) { UseDefault = false; if (Required || !SameAsDefault || WriteDefaultValues) { auto State = StateStack.back(); if (State == inFlowMapFirstKey || State == inFlowMapOtherKey) { flowKey(Key); } else { newLineCheck(); paddedKey(Key); } return true; } return false; } void Output::postflightKey(void *) { if (StateStack.back() == inMapFirstKey) { StateStack.pop_back(); StateStack.push_back(inMapOtherKey); } else if (StateStack.back() == inFlowMapFirstKey) { StateStack.pop_back(); StateStack.push_back(inFlowMapOtherKey); } } void Output::beginFlowMapping() { StateStack.push_back(inFlowMapFirstKey); newLineCheck(); ColumnAtMapFlowStart = Column; output("{ "); } void Output::endFlowMapping() { StateStack.pop_back(); outputUpToEndOfLine(" }"); } void Output::beginDocuments() { outputUpToEndOfLine("---"); } bool Output::preflightDocument(unsigned index) { if (index > 0) outputUpToEndOfLine("\n---"); return true; } void Output::postflightDocument() { } void Output::endDocuments() { output("\n...\n"); } unsigned Output::beginSequence() { StateStack.push_back(inSeqFirstElement); PaddingBeforeContainer = Padding; Padding = "\n"; return 0; } void Output::endSequence() { // If we did not emit anything, we should explicitly emit an empty sequence if (StateStack.back() == inSeqFirstElement) { Padding = PaddingBeforeContainer; newLineCheck(); output("[]"); Padding = "\n"; } StateStack.pop_back(); } bool Output::preflightElement(unsigned, void *&) { return true; } void Output::postflightElement(void *) { if (StateStack.back() == inSeqFirstElement) { StateStack.pop_back(); StateStack.push_back(inSeqOtherElement); } else if (StateStack.back() == inFlowSeqFirstElement) { StateStack.pop_back(); StateStack.push_back(inFlowSeqOtherElement); } } unsigned Output::beginFlowSequence() { StateStack.push_back(inFlowSeqFirstElement); newLineCheck(); ColumnAtFlowStart = Column; output("[ "); NeedFlowSequenceComma = false; return 0; } void Output::endFlowSequence() { StateStack.pop_back(); outputUpToEndOfLine(" ]"); } bool Output::preflightFlowElement(unsigned, void *&) { if (NeedFlowSequenceComma) output(", "); if (WrapColumn && Column > WrapColumn) { output("\n"); for (int i = 0; i < ColumnAtFlowStart; ++i) output(" "); Column = ColumnAtFlowStart; output(" "); } return true; } void Output::postflightFlowElement(void *) { NeedFlowSequenceComma = true; } void Output::beginEnumScalar() { EnumerationMatchFound = false; } bool Output::matchEnumScalar(const char *Str, bool Match) { if (Match && !EnumerationMatchFound) { newLineCheck(); outputUpToEndOfLine(Str); EnumerationMatchFound = true; } return false; } bool Output::matchEnumFallback() { if (EnumerationMatchFound) return false; EnumerationMatchFound = true; return true; } void Output::endEnumScalar() { if (!EnumerationMatchFound) llvm_unreachable("bad runtime enum value"); } bool Output::beginBitSetScalar(bool &DoClear) { newLineCheck(); output("[ "); NeedBitValueComma = false; DoClear = false; return true; } bool Output::bitSetMatch(const char *Str, bool Matches) { if (Matches) { if (NeedBitValueComma) output(", "); output(Str); NeedBitValueComma = true; } return false; } void Output::endBitSetScalar() { outputUpToEndOfLine(" ]"); } void Output::scalarString(StringRef &S, QuotingType MustQuote) { newLineCheck(); if (S.empty()) { // Print '' for the empty string because leaving the field empty is not // allowed. outputUpToEndOfLine("''"); return; } if (MustQuote == QuotingType::None) { // Only quote if we must. outputUpToEndOfLine(S); return; } const char *const Quote = MustQuote == QuotingType::Single ? "'" : "\""; output(Quote); // Starting quote. // When using double-quoted strings (and only in that case), non-printable characters may be // present, and will be escaped using a variety of unicode-scalar and special short-form // escapes. This is handled in yaml::escape. if (MustQuote == QuotingType::Double) { output(yaml::escape(S, /* EscapePrintable= */ false)); outputUpToEndOfLine(Quote); return; } unsigned i = 0; unsigned j = 0; unsigned End = S.size(); const char *Base = S.data(); // When using single-quoted strings, any single quote ' must be doubled to be escaped. while (j < End) { if (S[j] == '\'') { // Escape quotes. output(StringRef(&Base[i], j - i)); // "flush". output(StringLiteral("''")); // Print it as '' i = j + 1; } ++j; } output(StringRef(&Base[i], j - i)); outputUpToEndOfLine(Quote); // Ending quote. } void Output::blockScalarString(StringRef &S) { if (!StateStack.empty()) newLineCheck(); output(" |"); outputNewLine(); unsigned Indent = StateStack.empty() ? 1 : StateStack.size(); auto Buffer = MemoryBuffer::getMemBuffer(S, "", false); for (line_iterator Lines(*Buffer, false); !Lines.is_at_end(); ++Lines) { for (unsigned I = 0; I < Indent; ++I) { output(" "); } output(*Lines); outputNewLine(); } } void Output::scalarTag(std::string &Tag) { if (Tag.empty()) return; newLineCheck(); output(Tag); output(" "); } void Output::setError(const Twine &message) { } bool Output::canElideEmptySequence() { // Normally, with an optional key/value where the value is an empty sequence, // the whole key/value can be not written. But, that produces wrong yaml // if the key/value is the only thing in the map and the map is used in // a sequence. This detects if the this sequence is the first key/value // in map that itself is embedded in a sequnce. if (StateStack.size() < 2) return true; if (StateStack.back() != inMapFirstKey) return true; return !inSeqAnyElement(StateStack[StateStack.size() - 2]); } void Output::output(StringRef s) { Column += s.size(); Out << s; } void Output::outputUpToEndOfLine(StringRef s) { output(s); if (StateStack.empty() || (!inFlowSeqAnyElement(StateStack.back()) && !inFlowMapAnyKey(StateStack.back()))) Padding = "\n"; } void Output::outputNewLine() { Out << "\n"; Column = 0; } // if seq at top, indent as if map, then add "- " // if seq in middle, use "- " if firstKey, else use " " // void Output::newLineCheck() { if (Padding != "\n") { output(Padding); Padding = {}; return; } outputNewLine(); Padding = {}; if (StateStack.size() == 0) return; unsigned Indent = StateStack.size() - 1; bool OutputDash = false; if (StateStack.back() == inSeqFirstElement || StateStack.back() == inSeqOtherElement) { OutputDash = true; } else if ((StateStack.size() > 1) && ((StateStack.back() == inMapFirstKey) || inFlowSeqAnyElement(StateStack.back()) || (StateStack.back() == inFlowMapFirstKey)) && inSeqAnyElement(StateStack[StateStack.size() - 2])) { --Indent; OutputDash = true; } for (unsigned i = 0; i < Indent; ++i) { output(" "); } if (OutputDash) { output("- "); } } void Output::paddedKey(StringRef key) { output(key); output(":"); const char *spaces = " "; if (key.size() < strlen(spaces)) Padding = &spaces[key.size()]; else Padding = " "; } void Output::flowKey(StringRef Key) { if (StateStack.back() == inFlowMapOtherKey) output(", "); if (WrapColumn && Column > WrapColumn) { output("\n"); for (int I = 0; I < ColumnAtMapFlowStart; ++I) output(" "); Column = ColumnAtMapFlowStart; output(" "); } output(Key); output(": "); } NodeKind Output::getNodeKind() { report_fatal_error("invalid call"); } bool Output::inSeqAnyElement(InState State) { return State == inSeqFirstElement || State == inSeqOtherElement; } bool Output::inFlowSeqAnyElement(InState State) { return State == inFlowSeqFirstElement || State == inFlowSeqOtherElement; } bool Output::inMapAnyKey(InState State) { return State == inMapFirstKey || State == inMapOtherKey; } bool Output::inFlowMapAnyKey(InState State) { return State == inFlowMapFirstKey || State == inFlowMapOtherKey; } //===----------------------------------------------------------------------===// // traits for built-in types //===----------------------------------------------------------------------===// void ScalarTraits::output(const bool &Val, void *, raw_ostream &Out) { Out << (Val ? "true" : "false"); } StringRef ScalarTraits::input(StringRef Scalar, void *, bool &Val) { if (Scalar.equals("true")) { Val = true; return StringRef(); } else if (Scalar.equals("false")) { Val = false; return StringRef(); } return "invalid boolean"; } void ScalarTraits::output(const StringRef &Val, void *, raw_ostream &Out) { Out << Val; } StringRef ScalarTraits::input(StringRef Scalar, void *, StringRef &Val) { Val = Scalar; return StringRef(); } void ScalarTraits::output(const std::string &Val, void *, raw_ostream &Out) { Out << Val; } StringRef ScalarTraits::input(StringRef Scalar, void *, std::string &Val) { Val = Scalar.str(); return StringRef(); } void ScalarTraits::output(const uint8_t &Val, void *, raw_ostream &Out) { // use temp uin32_t because ostream thinks uint8_t is a character uint32_t Num = Val; Out << Num; } StringRef ScalarTraits::input(StringRef Scalar, void *, uint8_t &Val) { unsigned long long n; if (getAsUnsignedInteger(Scalar, 0, n)) return "invalid number"; if (n > 0xFF) return "out of range number"; Val = n; return StringRef(); } void ScalarTraits::output(const uint16_t &Val, void *, raw_ostream &Out) { Out << Val; } StringRef ScalarTraits::input(StringRef Scalar, void *, uint16_t &Val) { unsigned long long n; if (getAsUnsignedInteger(Scalar, 0, n)) return "invalid number"; if (n > 0xFFFF) return "out of range number"; Val = n; return StringRef(); } void ScalarTraits::output(const uint32_t &Val, void *, raw_ostream &Out) { Out << Val; } StringRef ScalarTraits::input(StringRef Scalar, void *, uint32_t &Val) { unsigned long long n; if (getAsUnsignedInteger(Scalar, 0, n)) return "invalid number"; if (n > 0xFFFFFFFFUL) return "out of range number"; Val = n; return StringRef(); } void ScalarTraits::output(const uint64_t &Val, void *, raw_ostream &Out) { Out << Val; } StringRef ScalarTraits::input(StringRef Scalar, void *, uint64_t &Val) { unsigned long long N; if (getAsUnsignedInteger(Scalar, 0, N)) return "invalid number"; Val = N; return StringRef(); } void ScalarTraits::output(const int8_t &Val, void *, raw_ostream &Out) { // use temp in32_t because ostream thinks int8_t is a character int32_t Num = Val; Out << Num; } StringRef ScalarTraits::input(StringRef Scalar, void *, int8_t &Val) { long long N; if (getAsSignedInteger(Scalar, 0, N)) return "invalid number"; if ((N > 127) || (N < -128)) return "out of range number"; Val = N; return StringRef(); } void ScalarTraits::output(const int16_t &Val, void *, raw_ostream &Out) { Out << Val; } StringRef ScalarTraits::input(StringRef Scalar, void *, int16_t &Val) { long long N; if (getAsSignedInteger(Scalar, 0, N)) return "invalid number"; if ((N > INT16_MAX) || (N < INT16_MIN)) return "out of range number"; Val = N; return StringRef(); } void ScalarTraits::output(const int32_t &Val, void *, raw_ostream &Out) { Out << Val; } StringRef ScalarTraits::input(StringRef Scalar, void *, int32_t &Val) { long long N; if (getAsSignedInteger(Scalar, 0, N)) return "invalid number"; if ((N > INT32_MAX) || (N < INT32_MIN)) return "out of range number"; Val = N; return StringRef(); } void ScalarTraits::output(const int64_t &Val, void *, raw_ostream &Out) { Out << Val; } StringRef ScalarTraits::input(StringRef Scalar, void *, int64_t &Val) { long long N; if (getAsSignedInteger(Scalar, 0, N)) return "invalid number"; Val = N; return StringRef(); } void ScalarTraits::output(const double &Val, void *, raw_ostream &Out) { Out << format("%g", Val); } StringRef ScalarTraits::input(StringRef Scalar, void *, double &Val) { if (to_float(Scalar, Val)) return StringRef(); return "invalid floating point number"; } void ScalarTraits::output(const float &Val, void *, raw_ostream &Out) { Out << format("%g", Val); } StringRef ScalarTraits::input(StringRef Scalar, void *, float &Val) { if (to_float(Scalar, Val)) return StringRef(); return "invalid floating point number"; } void ScalarTraits::output(const Hex8 &Val, void *, raw_ostream &Out) { uint8_t Num = Val; Out << format("0x%02X", Num); } StringRef ScalarTraits::input(StringRef Scalar, void *, Hex8 &Val) { unsigned long long n; if (getAsUnsignedInteger(Scalar, 0, n)) return "invalid hex8 number"; if (n > 0xFF) return "out of range hex8 number"; Val = n; return StringRef(); } void ScalarTraits::output(const Hex16 &Val, void *, raw_ostream &Out) { uint16_t Num = Val; Out << format("0x%04X", Num); } StringRef ScalarTraits::input(StringRef Scalar, void *, Hex16 &Val) { unsigned long long n; if (getAsUnsignedInteger(Scalar, 0, n)) return "invalid hex16 number"; if (n > 0xFFFF) return "out of range hex16 number"; Val = n; return StringRef(); } void ScalarTraits::output(const Hex32 &Val, void *, raw_ostream &Out) { uint32_t Num = Val; Out << format("0x%08X", Num); } StringRef ScalarTraits::input(StringRef Scalar, void *, Hex32 &Val) { unsigned long long n; if (getAsUnsignedInteger(Scalar, 0, n)) return "invalid hex32 number"; if (n > 0xFFFFFFFFUL) return "out of range hex32 number"; Val = n; return StringRef(); } void ScalarTraits::output(const Hex64 &Val, void *, raw_ostream &Out) { uint64_t Num = Val; Out << format("0x%016llX", Num); } StringRef ScalarTraits::input(StringRef Scalar, void *, Hex64 &Val) { unsigned long long Num; if (getAsUnsignedInteger(Scalar, 0, Num)) return "invalid hex64 number"; Val = Num; return StringRef(); } binaryen-version_91/third_party/llvm-project/dwarf2yaml.cpp000066400000000000000000000370671362402614000244170ustar00rootroot00000000000000//===------ dwarf2yaml.cpp - obj2yaml conversion tool -----------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "Error.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/DWARF/DWARFDebugArangeSet.h" #include "llvm/DebugInfo/DWARF/DWARFFormValue.h" #include "llvm/ObjectYAML/DWARFYAML.h" #include using namespace llvm; void dumpInitialLength(DataExtractor &Data, uint64_t &Offset, DWARFYAML::InitialLength &InitialLength) { InitialLength.TotalLength = Data.getU32(&Offset); if (InitialLength.isDWARF64()) InitialLength.TotalLength64 = Data.getU64(&Offset); } void dumpDebugAbbrev(DWARFContext &DCtx, DWARFYAML::Data &Y) { auto AbbrevSetPtr = DCtx.getDebugAbbrev(); if (AbbrevSetPtr) { for (auto AbbrvDeclSet : *AbbrevSetPtr) { for (auto AbbrvDecl : AbbrvDeclSet.second) { DWARFYAML::Abbrev Abbrv; Abbrv.Code = AbbrvDecl.getCode(); Abbrv.Tag = AbbrvDecl.getTag(); Abbrv.Children = AbbrvDecl.hasChildren() ? dwarf::DW_CHILDREN_yes : dwarf::DW_CHILDREN_no; for (auto Attribute : AbbrvDecl.attributes()) { DWARFYAML::AttributeAbbrev AttAbrv; AttAbrv.Attribute = Attribute.Attr; AttAbrv.Form = Attribute.Form; if (AttAbrv.Form == dwarf::DW_FORM_implicit_const) AttAbrv.Value = Attribute.getImplicitConstValue(); Abbrv.Attributes.push_back(AttAbrv); } Y.AbbrevDecls.push_back(Abbrv); } // XXX BINARYEN: null-terminate the DeclSet. This is needed to separate // DeclSets from each other, and to null-terminate the entire list // (LLVM works with or without this, but other decoders may error, see // https://bugs.llvm.org/show_bug.cgi?id=44511). DWARFYAML::Abbrev Abbrv; Abbrv.Code = 0; Abbrv.Tag = dwarf::Tag(0); Y.AbbrevDecls.push_back(Abbrv); } } } void dumpDebugStrings(DWARFContext &DCtx, DWARFYAML::Data &Y) { StringRef RemainingTable = DCtx.getDWARFObj().getStrSection(); while (RemainingTable.size() > 0) { auto SymbolPair = RemainingTable.split('\0'); RemainingTable = SymbolPair.second; Y.DebugStrings.push_back(SymbolPair.first); } } void dumpDebugARanges(DWARFContext &DCtx, DWARFYAML::Data &Y) { DataExtractor ArangesData(DCtx.getDWARFObj().getArangesSection(), DCtx.isLittleEndian(), 0); uint64_t Offset = 0; DWARFDebugArangeSet Set; while (Set.extract(ArangesData, &Offset)) { DWARFYAML::ARange Range; Range.Length.setLength(Set.getHeader().Length); Range.Version = Set.getHeader().Version; Range.CuOffset = Set.getHeader().CuOffset; Range.AddrSize = Set.getHeader().AddrSize; Range.SegSize = Set.getHeader().SegSize; for (auto Descriptor : Set.descriptors()) { DWARFYAML::ARangeDescriptor Desc; Desc.Address = Descriptor.Address; Desc.Length = Descriptor.Length; Range.Descriptors.push_back(Desc); } Y.ARanges.push_back(Range); } } void dumpDebugRanges(DWARFContext &DCtx, DWARFYAML::Data &Y) { // XXX BINARYEN uint8_t savedAddressByteSize = 4; DWARFDataExtractor rangesData(DCtx.getDWARFObj(), DCtx.getDWARFObj().getRangesSection(), DCtx.isLittleEndian(), savedAddressByteSize); uint64_t offset = 0; DWARFDebugRangeList rangeList; while (rangesData.isValidOffset(offset)) { if (Error E = rangeList.extract(rangesData, &offset)) { errs() << toString(std::move(E)) << '\n'; break; } for (auto& entry : rangeList.getEntries()) { DWARFYAML::Range range; range.Start = entry.StartAddress; range.End = entry.EndAddress; range.SectionIndex = entry.SectionIndex; Y.Ranges.push_back(range); } DWARFYAML::Range range; range.Start = 0; range.End = 0; range.SectionIndex = -1; Y.Ranges.push_back(range); } } void dumpDebugLoc(DWARFContext &DCtx, DWARFYAML::Data &Y) { // XXX BINARYEN uint8_t savedAddressByteSize = 4; DWARFDataExtractor locsData(DCtx.getDWARFObj(), DCtx.getDWARFObj().getLocSection(), DCtx.isLittleEndian(), savedAddressByteSize); uint64_t offset = 0; DWARFDebugLoc locList; while (locsData.isValidOffset(offset)) { auto list = locList.parseOneLocationList(locsData, &offset); if (!list) { errs() << "debug_loc error\n"; break; } for (auto& entry : list.get().Entries) { DWARFYAML::Loc loc; loc.Start = entry.Begin; loc.End = entry.End; for (auto x : entry.Loc) { loc.Location.push_back(x); } Y.Locs.push_back(loc); } DWARFYAML::Loc loc; loc.Start = 0; loc.End = 0; Y.Locs.push_back(loc); } } void dumpPubSection(DWARFContext &DCtx, DWARFYAML::PubSection &Y, DWARFSection Section) { DWARFDataExtractor PubSectionData(DCtx.getDWARFObj(), Section, DCtx.isLittleEndian(), 0); uint64_t Offset = 0; dumpInitialLength(PubSectionData, Offset, Y.Length); Y.Version = PubSectionData.getU16(&Offset); Y.UnitOffset = PubSectionData.getU32(&Offset); Y.UnitSize = PubSectionData.getU32(&Offset); while (Offset < Y.Length.getLength()) { DWARFYAML::PubEntry NewEntry; NewEntry.DieOffset = PubSectionData.getU32(&Offset); if (Y.IsGNUStyle) NewEntry.Descriptor = PubSectionData.getU8(&Offset); NewEntry.Name = PubSectionData.getCStr(&Offset); Y.Entries.push_back(NewEntry); } } void dumpDebugPubSections(DWARFContext &DCtx, DWARFYAML::Data &Y) { const DWARFObject &D = DCtx.getDWARFObj(); Y.PubNames.IsGNUStyle = false; dumpPubSection(DCtx, Y.PubNames, D.getPubnamesSection()); Y.PubTypes.IsGNUStyle = false; dumpPubSection(DCtx, Y.PubTypes, D.getPubtypesSection()); Y.GNUPubNames.IsGNUStyle = true; dumpPubSection(DCtx, Y.GNUPubNames, D.getGnuPubnamesSection()); Y.GNUPubTypes.IsGNUStyle = true; dumpPubSection(DCtx, Y.GNUPubTypes, D.getGnuPubtypesSection()); } void dumpDebugInfo(DWARFContext &DCtx, DWARFYAML::Data &Y) { for (const auto &CU : DCtx.compile_units()) { DWARFYAML::Unit NewUnit; NewUnit.Length.setLength(CU->getLength()); NewUnit.Version = CU->getVersion(); if(NewUnit.Version >= 5) NewUnit.Type = (dwarf::UnitType)CU->getUnitType(); if (auto* Abbreviations = CU->getAbbreviations()) { // XXX BINARYEN NewUnit.AbbrOffset = Abbreviations->getOffset(); } NewUnit.AddrSize = CU->getAddressByteSize(); for (auto DIE : CU->dies()) { DWARFYAML::Entry NewEntry; DataExtractor EntryData = CU->getDebugInfoExtractor(); uint64_t offset = DIE.getOffset(); assert(EntryData.isValidOffset(offset) && "Invalid DIE Offset"); if (!EntryData.isValidOffset(offset)) continue; NewEntry.AbbrCode = EntryData.getULEB128(&offset); auto AbbrevDecl = DIE.getAbbreviationDeclarationPtr(); if (AbbrevDecl) { for (const auto &AttrSpec : AbbrevDecl->attributes()) { DWARFYAML::FormValue NewValue; NewValue.Value = 0xDEADBEEFDEADBEEF; DWARFDie DIEWrapper(CU.get(), &DIE); auto FormValue = DIEWrapper.find(AttrSpec.Attr); if (!FormValue) return; auto Form = FormValue.getValue().getForm(); bool indirect = false; do { indirect = false; switch (Form) { case dwarf::DW_FORM_addr: case dwarf::DW_FORM_GNU_addr_index: if (auto Val = FormValue.getValue().getAsAddress()) NewValue.Value = Val.getValue(); break; case dwarf::DW_FORM_ref_addr: case dwarf::DW_FORM_ref1: case dwarf::DW_FORM_ref2: case dwarf::DW_FORM_ref4: case dwarf::DW_FORM_ref8: case dwarf::DW_FORM_ref_udata: case dwarf::DW_FORM_ref_sig8: if (auto Val = FormValue.getValue().getAsReferenceUVal()) NewValue.Value = Val.getValue(); break; case dwarf::DW_FORM_exprloc: case dwarf::DW_FORM_block: case dwarf::DW_FORM_block1: case dwarf::DW_FORM_block2: case dwarf::DW_FORM_block4: if (auto Val = FormValue.getValue().getAsBlock()) { auto BlockData = Val.getValue(); std::copy(BlockData.begin(), BlockData.end(), std::back_inserter(NewValue.BlockData)); } NewValue.Value = NewValue.BlockData.size(); break; case dwarf::DW_FORM_data1: case dwarf::DW_FORM_flag: case dwarf::DW_FORM_data2: case dwarf::DW_FORM_data4: case dwarf::DW_FORM_data8: case dwarf::DW_FORM_udata: case dwarf::DW_FORM_ref_sup4: case dwarf::DW_FORM_ref_sup8: if (auto Val = FormValue.getValue().getAsUnsignedConstant()) NewValue.Value = Val.getValue(); break; // XXX BINARYEN: sdata is signed, and FormValue won't return it as // unsigned (it returns an empty value). case dwarf::DW_FORM_sdata: if (auto Val = FormValue.getValue().getAsSignedConstant()) NewValue.Value = Val.getValue(); break; case dwarf::DW_FORM_string: if (auto Val = FormValue.getValue().getAsCString()) NewValue.CStr = Val.getValue(); break; case dwarf::DW_FORM_indirect: indirect = true; if (auto Val = FormValue.getValue().getAsUnsignedConstant()) { NewValue.Value = Val.getValue(); NewEntry.Values.push_back(NewValue); Form = static_cast(Val.getValue()); } break; case dwarf::DW_FORM_strp: case dwarf::DW_FORM_sec_offset: case dwarf::DW_FORM_GNU_ref_alt: case dwarf::DW_FORM_GNU_strp_alt: case dwarf::DW_FORM_line_strp: case dwarf::DW_FORM_strp_sup: case dwarf::DW_FORM_GNU_str_index: case dwarf::DW_FORM_strx: if (auto Val = FormValue.getValue().getAsCStringOffset()) NewValue.Value = Val.getValue(); break; case dwarf::DW_FORM_flag_present: NewValue.Value = 1; break; default: break; } } while (indirect); NewEntry.Values.push_back(NewValue); } } NewUnit.Entries.push_back(NewEntry); } Y.CompileUnits.push_back(NewUnit); } } bool dumpFileEntry(DataExtractor &Data, uint64_t &Offset, DWARFYAML::File &File) { File.Name = Data.getCStr(&Offset); if (File.Name.empty()) return false; File.DirIdx = Data.getULEB128(&Offset); File.ModTime = Data.getULEB128(&Offset); File.Length = Data.getULEB128(&Offset); return true; } void dumpDebugLines(DWARFContext &DCtx, DWARFYAML::Data &Y) { for (const auto &CU : DCtx.compile_units()) { auto CUDIE = CU->getUnitDIE(); if (!CUDIE) continue; if (auto StmtOffset = dwarf::toSectionOffset(CUDIE.find(dwarf::DW_AT_stmt_list))) { DWARFYAML::LineTable DebugLines; DataExtractor LineData(DCtx.getDWARFObj().getLineSection().Data, DCtx.isLittleEndian(), CU->getAddressByteSize()); uint64_t Offset = *StmtOffset; dumpInitialLength(LineData, Offset, DebugLines.Length); uint64_t LineTableLength = DebugLines.Length.getLength(); uint64_t SizeOfPrologueLength = DebugLines.Length.isDWARF64() ? 8 : 4; DebugLines.Version = LineData.getU16(&Offset); DebugLines.PrologueLength = LineData.getUnsigned(&Offset, SizeOfPrologueLength); const uint64_t EndPrologue = DebugLines.PrologueLength + Offset; DebugLines.MinInstLength = LineData.getU8(&Offset); if (DebugLines.Version >= 4) DebugLines.MaxOpsPerInst = LineData.getU8(&Offset); DebugLines.DefaultIsStmt = LineData.getU8(&Offset); DebugLines.LineBase = LineData.getU8(&Offset); DebugLines.LineRange = LineData.getU8(&Offset); DebugLines.OpcodeBase = LineData.getU8(&Offset); DebugLines.StandardOpcodeLengths.reserve(DebugLines.OpcodeBase - 1); for (uint8_t i = 1; i < DebugLines.OpcodeBase; ++i) DebugLines.StandardOpcodeLengths.push_back(LineData.getU8(&Offset)); while (Offset < EndPrologue) { StringRef Dir = LineData.getCStr(&Offset); if (!Dir.empty()) DebugLines.IncludeDirs.push_back(Dir); else break; } while (Offset < EndPrologue) { DWARFYAML::File TmpFile; if (dumpFileEntry(LineData, Offset, TmpFile)) DebugLines.Files.push_back(TmpFile); else break; } const uint64_t LineEnd = LineTableLength + *StmtOffset + SizeOfPrologueLength; while (Offset < LineEnd) { DWARFYAML::LineTableOpcode NewOp = {}; NewOp.Opcode = (dwarf::LineNumberOps)LineData.getU8(&Offset); if (NewOp.Opcode == 0) { auto StartExt = Offset; NewOp.ExtLen = LineData.getULEB128(&Offset); NewOp.SubOpcode = (dwarf::LineNumberExtendedOps)LineData.getU8(&Offset); switch (NewOp.SubOpcode) { case dwarf::DW_LNE_set_address: case dwarf::DW_LNE_set_discriminator: NewOp.Data = LineData.getAddress(&Offset); break; case dwarf::DW_LNE_define_file: dumpFileEntry(LineData, Offset, NewOp.FileEntry); break; case dwarf::DW_LNE_end_sequence: break; default: while (Offset < StartExt + NewOp.ExtLen) NewOp.UnknownOpcodeData.push_back(LineData.getU8(&Offset)); } } else if (NewOp.Opcode < DebugLines.OpcodeBase) { switch (NewOp.Opcode) { case dwarf::DW_LNS_copy: case dwarf::DW_LNS_negate_stmt: case dwarf::DW_LNS_set_basic_block: case dwarf::DW_LNS_const_add_pc: case dwarf::DW_LNS_set_prologue_end: case dwarf::DW_LNS_set_epilogue_begin: break; case dwarf::DW_LNS_advance_pc: case dwarf::DW_LNS_set_file: case dwarf::DW_LNS_set_column: case dwarf::DW_LNS_set_isa: NewOp.Data = LineData.getULEB128(&Offset); break; case dwarf::DW_LNS_advance_line: NewOp.SData = LineData.getSLEB128(&Offset); break; case dwarf::DW_LNS_fixed_advance_pc: NewOp.Data = LineData.getU16(&Offset); break; default: for (uint8_t i = 0; i < DebugLines.StandardOpcodeLengths[NewOp.Opcode - 1]; ++i) NewOp.StandardOpcodeData.push_back(LineData.getULEB128(&Offset)); } } DebugLines.Opcodes.push_back(NewOp); } Y.DebugLines.push_back(DebugLines); } } } std::error_code dwarf2yaml(DWARFContext &DCtx, DWARFYAML::Data &Y) { Y.IsLittleEndian = true; // XXX BINARYEN dumpDebugAbbrev(DCtx, Y); dumpDebugStrings(DCtx, Y); dumpDebugARanges(DCtx, Y); dumpDebugRanges(DCtx, Y); // XXX BINARYEN dumpDebugLoc(DCtx, Y); // XXX BINARYEN dumpDebugPubSections(DCtx, Y); dumpDebugInfo(DCtx, Y); dumpDebugLines(DCtx, Y); return obj2yaml_error::success; } binaryen-version_91/third_party/llvm-project/include/000077500000000000000000000000001362402614000232515ustar00rootroot00000000000000binaryen-version_91/third_party/llvm-project/include/llvm-c/000077500000000000000000000000001362402614000244435ustar00rootroot00000000000000binaryen-version_91/third_party/llvm-project/include/llvm-c/DataTypes.h000066400000000000000000000060141362402614000265130ustar00rootroot00000000000000/*===-- include/llvm-c/DataTypes.h - Define fixed size types ------*- C -*-===*\ |* *| |* Part of the LLVM Project, under the Apache License v2.0 with LLVM *| |* Exceptions. *| |* See https://llvm.org/LICENSE.txt for license information. *| |* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception *| |* *| |*===----------------------------------------------------------------------===*| |* *| |* This file contains definitions to figure out the size of _HOST_ data types.*| |* This file is important because different host OS's define different macros,*| |* which makes portability tough. This file exports the following *| |* definitions: *| |* *| |* [u]int(32|64)_t : typedefs for signed and unsigned 32/64 bit system types*| |* [U]INT(8|16|32|64)_(MIN|MAX) : Constants for the min and max values. *| |* *| |* No library is required when using these functions. *| |* *| |*===----------------------------------------------------------------------===*/ /* Please leave this file C-compatible. */ #ifndef LLVM_C_DATATYPES_H #define LLVM_C_DATATYPES_H #ifdef __cplusplus #include #else #include #endif #include #include #ifndef _MSC_VER #if !defined(UINT32_MAX) # error "The standard header is not C++11 compliant. Must #define "\ "__STDC_LIMIT_MACROS before #including llvm-c/DataTypes.h" #endif #if !defined(UINT32_C) # error "The standard header is not C++11 compliant. Must #define "\ "__STDC_CONSTANT_MACROS before #including llvm-c/DataTypes.h" #endif /* Note that includes , if this is a C99 system. */ #include #ifdef _AIX // GCC is strict about defining large constants: they must have LL modifier. #undef INT64_MAX #undef INT64_MIN #endif #else /* _MSC_VER */ #ifdef __cplusplus #include #include #else #include #include #endif #include #if defined(_WIN64) typedef signed __int64 ssize_t; #else typedef signed int ssize_t; #endif /* _WIN64 */ #endif /* _MSC_VER */ /* Set defaults for constants which we cannot find. */ #if !defined(INT64_MAX) # define INT64_MAX 9223372036854775807LL #endif #if !defined(INT64_MIN) # define INT64_MIN ((-INT64_MAX)-1) #endif #if !defined(UINT64_MAX) # define UINT64_MAX 0xffffffffffffffffULL #endif #ifndef HUGE_VALF #define HUGE_VALF (float)HUGE_VAL #endif #endif /* LLVM_C_DATATYPES_H */ binaryen-version_91/third_party/llvm-project/include/llvm-c/DisassemblerTypes.h000066400000000000000000000171271362402614000302660ustar00rootroot00000000000000/*===-- llvm-c/DisassemblerTypedefs.h -----------------------------*- C -*-===*\ |* *| |* Part of the LLVM Project, under the Apache License v2.0 with LLVM *| |* Exceptions. *| |* See https://llvm.org/LICENSE.txt for license information. *| |* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception *| |* *| |*===----------------------------------------------------------------------===*/ #ifndef LLVM_DISASSEMBLER_TYPES_H #define LLVM_DISASSEMBLER_TYPES_H #include "llvm-c/DataTypes.h" #ifdef __cplusplus #include #else #include #endif /** * An opaque reference to a disassembler context. */ typedef void *LLVMDisasmContextRef; /** * The type for the operand information call back function. This is called to * get the symbolic information for an operand of an instruction. Typically * this is from the relocation information, symbol table, etc. That block of * information is saved when the disassembler context is created and passed to * the call back in the DisInfo parameter. The instruction containing operand * is at the PC parameter. For some instruction sets, there can be more than * one operand with symbolic information. To determine the symbolic operand * information for each operand, the bytes for the specific operand in the * instruction are specified by the Offset parameter and its byte widith is the * size parameter. For instructions sets with fixed widths and one symbolic * operand per instruction, the Offset parameter will be zero and Size parameter * will be the instruction width. The information is returned in TagBuf and is * Triple specific with its specific information defined by the value of * TagType for that Triple. If symbolic information is returned the function * returns 1, otherwise it returns 0. */ typedef int (*LLVMOpInfoCallback)(void *DisInfo, uint64_t PC, uint64_t Offset, uint64_t Size, int TagType, void *TagBuf); /** * The initial support in LLVM MC for the most general form of a relocatable * expression is "AddSymbol - SubtractSymbol + Offset". For some Darwin targets * this full form is encoded in the relocation information so that AddSymbol and * SubtractSymbol can be link edited independent of each other. Many other * platforms only allow a relocatable expression of the form AddSymbol + Offset * to be encoded. * * The LLVMOpInfoCallback() for the TagType value of 1 uses the struct * LLVMOpInfo1. The value of the relocatable expression for the operand, * including any PC adjustment, is passed in to the call back in the Value * field. The symbolic information about the operand is returned using all * the fields of the structure with the Offset of the relocatable expression * returned in the Value field. It is possible that some symbols in the * relocatable expression were assembly temporary symbols, for example * "Ldata - LpicBase + constant", and only the Values of the symbols without * symbol names are present in the relocation information. The VariantKind * type is one of the Target specific #defines below and is used to print * operands like "_foo@GOT", ":lower16:_foo", etc. */ struct LLVMOpInfoSymbol1 { uint64_t Present; /* 1 if this symbol is present */ const char *Name; /* symbol name if not NULL */ uint64_t Value; /* symbol value if name is NULL */ }; struct LLVMOpInfo1 { struct LLVMOpInfoSymbol1 AddSymbol; struct LLVMOpInfoSymbol1 SubtractSymbol; uint64_t Value; uint64_t VariantKind; }; /** * The operand VariantKinds for symbolic disassembly. */ #define LLVMDisassembler_VariantKind_None 0 /* all targets */ /** * The ARM target VariantKinds. */ #define LLVMDisassembler_VariantKind_ARM_HI16 1 /* :upper16: */ #define LLVMDisassembler_VariantKind_ARM_LO16 2 /* :lower16: */ /** * The ARM64 target VariantKinds. */ #define LLVMDisassembler_VariantKind_ARM64_PAGE 1 /* @page */ #define LLVMDisassembler_VariantKind_ARM64_PAGEOFF 2 /* @pageoff */ #define LLVMDisassembler_VariantKind_ARM64_GOTPAGE 3 /* @gotpage */ #define LLVMDisassembler_VariantKind_ARM64_GOTPAGEOFF 4 /* @gotpageoff */ #define LLVMDisassembler_VariantKind_ARM64_TLVP 5 /* @tvlppage */ #define LLVMDisassembler_VariantKind_ARM64_TLVOFF 6 /* @tvlppageoff */ /** * The type for the symbol lookup function. This may be called by the * disassembler for things like adding a comment for a PC plus a constant * offset load instruction to use a symbol name instead of a load address value. * It is passed the block information is saved when the disassembler context is * created and the ReferenceValue to look up as a symbol. If no symbol is found * for the ReferenceValue NULL is returned. The ReferenceType of the * instruction is passed indirectly as is the PC of the instruction in * ReferencePC. If the output reference can be determined its type is returned * indirectly in ReferenceType along with ReferenceName if any, or that is set * to NULL. */ typedef const char *(*LLVMSymbolLookupCallback)(void *DisInfo, uint64_t ReferenceValue, uint64_t *ReferenceType, uint64_t ReferencePC, const char **ReferenceName); /** * The reference types on input and output. */ /* No input reference type or no output reference type. */ #define LLVMDisassembler_ReferenceType_InOut_None 0 /* The input reference is from a branch instruction. */ #define LLVMDisassembler_ReferenceType_In_Branch 1 /* The input reference is from a PC relative load instruction. */ #define LLVMDisassembler_ReferenceType_In_PCrel_Load 2 /* The input reference is from an ARM64::ADRP instruction. */ #define LLVMDisassembler_ReferenceType_In_ARM64_ADRP 0x100000001 /* The input reference is from an ARM64::ADDXri instruction. */ #define LLVMDisassembler_ReferenceType_In_ARM64_ADDXri 0x100000002 /* The input reference is from an ARM64::LDRXui instruction. */ #define LLVMDisassembler_ReferenceType_In_ARM64_LDRXui 0x100000003 /* The input reference is from an ARM64::LDRXl instruction. */ #define LLVMDisassembler_ReferenceType_In_ARM64_LDRXl 0x100000004 /* The input reference is from an ARM64::ADR instruction. */ #define LLVMDisassembler_ReferenceType_In_ARM64_ADR 0x100000005 /* The output reference is to as symbol stub. */ #define LLVMDisassembler_ReferenceType_Out_SymbolStub 1 /* The output reference is to a symbol address in a literal pool. */ #define LLVMDisassembler_ReferenceType_Out_LitPool_SymAddr 2 /* The output reference is to a cstring address in a literal pool. */ #define LLVMDisassembler_ReferenceType_Out_LitPool_CstrAddr 3 /* The output reference is to a Objective-C CoreFoundation string. */ #define LLVMDisassembler_ReferenceType_Out_Objc_CFString_Ref 4 /* The output reference is to a Objective-C message. */ #define LLVMDisassembler_ReferenceType_Out_Objc_Message 5 /* The output reference is to a Objective-C message ref. */ #define LLVMDisassembler_ReferenceType_Out_Objc_Message_Ref 6 /* The output reference is to a Objective-C selector ref. */ #define LLVMDisassembler_ReferenceType_Out_Objc_Selector_Ref 7 /* The output reference is to a Objective-C class ref. */ #define LLVMDisassembler_ReferenceType_Out_Objc_Class_Ref 8 /* The output reference is to a C++ symbol name. */ #define LLVMDisassembler_ReferenceType_DeMangled_Name 9 #endif binaryen-version_91/third_party/llvm-project/include/llvm-c/Error.h000066400000000000000000000044531362402614000257130ustar00rootroot00000000000000/*===------- llvm-c/Error.h - llvm::Error class C Interface -------*- C -*-===*\ |* *| |* Part of the LLVM Project, under the Apache License v2.0 with LLVM *| |* Exceptions. *| |* See https://llvm.org/LICENSE.txt for license information. *| |* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception *| |* *| |*===----------------------------------------------------------------------===*| |* *| |* This file defines the C interface to LLVM's Error class. *| |* *| \*===----------------------------------------------------------------------===*/ #ifndef LLVM_C_ERROR_H #define LLVM_C_ERROR_H #ifdef __cplusplus extern "C" { #endif #define LLVMErrorSuccess 0 /** * Opaque reference to an error instance. Null serves as the 'success' value. */ typedef struct LLVMOpaqueError *LLVMErrorRef; /** * Error type identifier. */ typedef const void *LLVMErrorTypeId; /** * Returns the type id for the given error instance, which must be a failure * value (i.e. non-null). */ LLVMErrorTypeId LLVMGetErrorTypeId(LLVMErrorRef Err); /** * Dispose of the given error without handling it. This operation consumes the * error, and the given LLVMErrorRef value is not usable once this call returns. * Note: This method *only* needs to be called if the error is not being passed * to some other consuming operation, e.g. LLVMGetErrorMessage. */ void LLVMConsumeError(LLVMErrorRef Err); /** * Returns the given string's error message. This operation consumes the error, * and the given LLVMErrorRef value is not usable once this call returns. * The caller is responsible for disposing of the string by calling * LLVMDisposeErrorMessage. */ char *LLVMGetErrorMessage(LLVMErrorRef Err); /** * Dispose of the given error message. */ void LLVMDisposeErrorMessage(char *ErrMsg); /** * Returns the type id for llvm StringError. */ LLVMErrorTypeId LLVMGetStringErrorTypeId(void); #ifdef __cplusplus } #endif #endif binaryen-version_91/third_party/llvm-project/include/llvm-c/ErrorHandling.h000066400000000000000000000036321362402614000273560ustar00rootroot00000000000000/*===-- llvm-c/ErrorHandling.h - Error Handling C Interface -------*- C -*-===*\ |* *| |* Part of the LLVM Project, under the Apache License v2.0 with LLVM *| |* Exceptions. *| |* See https://llvm.org/LICENSE.txt for license information. *| |* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception *| |* *| |*===----------------------------------------------------------------------===*| |* *| |* This file defines the C interface to LLVM's error handling mechanism. *| |* *| \*===----------------------------------------------------------------------===*/ #ifndef LLVM_C_ERROR_HANDLING_H #define LLVM_C_ERROR_HANDLING_H #ifdef __cplusplus extern "C" { #endif typedef void (*LLVMFatalErrorHandler)(const char *Reason); /** * Install a fatal error handler. By default, if LLVM detects a fatal error, it * will call exit(1). This may not be appropriate in many contexts. For example, * doing exit(1) will bypass many crash reporting/tracing system tools. This * function allows you to install a callback that will be invoked prior to the * call to exit(1). */ void LLVMInstallFatalErrorHandler(LLVMFatalErrorHandler Handler); /** * Reset the fatal error handler. This resets LLVM's fatal error handling * behavior to the default. */ void LLVMResetFatalErrorHandler(void); /** * Enable LLVM's built-in stack trace code. This intercepts the OS's crash * signals and prints which component of LLVM you were in at the time if the * crash. */ void LLVMEnablePrettyStackTrace(void); #ifdef __cplusplus } #endif #endif binaryen-version_91/third_party/llvm-project/include/llvm-c/Types.h000066400000000000000000000110421362402614000257160ustar00rootroot00000000000000/*===-- llvm-c/Support.h - C Interface Types declarations ---------*- C -*-===*\ |* *| |* Part of the LLVM Project, under the Apache License v2.0 with LLVM *| |* Exceptions. *| |* See https://llvm.org/LICENSE.txt for license information. *| |* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception *| |* *| |*===----------------------------------------------------------------------===*| |* *| |* This file defines types used by the C interface to LLVM. *| |* *| \*===----------------------------------------------------------------------===*/ #ifndef LLVM_C_TYPES_H #define LLVM_C_TYPES_H #include "llvm-c/DataTypes.h" #ifdef __cplusplus extern "C" { #endif /** * @defgroup LLVMCSupportTypes Types and Enumerations * * @{ */ typedef int LLVMBool; /* Opaque types. */ /** * LLVM uses a polymorphic type hierarchy which C cannot represent, therefore * parameters must be passed as base types. Despite the declared types, most * of the functions provided operate only on branches of the type hierarchy. * The declared parameter names are descriptive and specify which type is * required. Additionally, each type hierarchy is documented along with the * functions that operate upon it. For more detail, refer to LLVM's C++ code. * If in doubt, refer to Core.cpp, which performs parameter downcasts in the * form unwrap(Param). */ /** * Used to pass regions of memory through LLVM interfaces. * * @see llvm::MemoryBuffer */ typedef struct LLVMOpaqueMemoryBuffer *LLVMMemoryBufferRef; /** * The top-level container for all LLVM global data. See the LLVMContext class. */ typedef struct LLVMOpaqueContext *LLVMContextRef; /** * The top-level container for all other LLVM Intermediate Representation (IR) * objects. * * @see llvm::Module */ typedef struct LLVMOpaqueModule *LLVMModuleRef; /** * Each value in the LLVM IR has a type, an LLVMTypeRef. * * @see llvm::Type */ typedef struct LLVMOpaqueType *LLVMTypeRef; /** * Represents an individual value in LLVM IR. * * This models llvm::Value. */ typedef struct LLVMOpaqueValue *LLVMValueRef; /** * Represents a basic block of instructions in LLVM IR. * * This models llvm::BasicBlock. */ typedef struct LLVMOpaqueBasicBlock *LLVMBasicBlockRef; /** * Represents an LLVM Metadata. * * This models llvm::Metadata. */ typedef struct LLVMOpaqueMetadata *LLVMMetadataRef; /** * Represents an LLVM Named Metadata Node. * * This models llvm::NamedMDNode. */ typedef struct LLVMOpaqueNamedMDNode *LLVMNamedMDNodeRef; /** * Represents an entry in a Global Object's metadata attachments. * * This models std::pair */ typedef struct LLVMOpaqueValueMetadataEntry LLVMValueMetadataEntry; /** * Represents an LLVM basic block builder. * * This models llvm::IRBuilder. */ typedef struct LLVMOpaqueBuilder *LLVMBuilderRef; /** * Represents an LLVM debug info builder. * * This models llvm::DIBuilder. */ typedef struct LLVMOpaqueDIBuilder *LLVMDIBuilderRef; /** * Interface used to provide a module to JIT or interpreter. * This is now just a synonym for llvm::Module, but we have to keep using the * different type to keep binary compatibility. */ typedef struct LLVMOpaqueModuleProvider *LLVMModuleProviderRef; /** @see llvm::PassManagerBase */ typedef struct LLVMOpaquePassManager *LLVMPassManagerRef; /** @see llvm::PassRegistry */ typedef struct LLVMOpaquePassRegistry *LLVMPassRegistryRef; /** * Used to get the users and usees of a Value. * * @see llvm::Use */ typedef struct LLVMOpaqueUse *LLVMUseRef; /** * Used to represent an attributes. * * @see llvm::Attribute */ typedef struct LLVMOpaqueAttributeRef *LLVMAttributeRef; /** * @see llvm::DiagnosticInfo */ typedef struct LLVMOpaqueDiagnosticInfo *LLVMDiagnosticInfoRef; /** * @see llvm::Comdat */ typedef struct LLVMComdat *LLVMComdatRef; /** * @see llvm::Module::ModuleFlagEntry */ typedef struct LLVMOpaqueModuleFlagEntry LLVMModuleFlagEntry; /** * @see llvm::JITEventListener */ typedef struct LLVMOpaqueJITEventListener *LLVMJITEventListenerRef; /** * @see llvm::object::Binary */ typedef struct LLVMOpaqueBinary *LLVMBinaryRef; /** * @} */ #ifdef __cplusplus } #endif #endif binaryen-version_91/third_party/llvm-project/include/llvm/000077500000000000000000000000001362402614000242235ustar00rootroot00000000000000binaryen-version_91/third_party/llvm-project/include/llvm/ADT/000077500000000000000000000000001362402614000246335ustar00rootroot00000000000000binaryen-version_91/third_party/llvm-project/include/llvm/ADT/APFloat.h000066400000000000000000001365171362402614000263070ustar00rootroot00000000000000//===- llvm/ADT/APFloat.h - Arbitrary Precision Floating Point ---*- C++ -*-==// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// /// \file /// \brief /// This file declares a class to represent arbitrary precision floating point /// values and provide a variety of arithmetic operations on them. /// //===----------------------------------------------------------------------===// #ifndef LLVM_ADT_APFLOAT_H #define LLVM_ADT_APFLOAT_H #include "llvm/ADT/APInt.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/Support/ErrorHandling.h" #include #define APFLOAT_DISPATCH_ON_SEMANTICS(METHOD_CALL) \ do { \ if (usesLayout(getSemantics())) \ return U.IEEE.METHOD_CALL; \ if (usesLayout(getSemantics())) \ return U.Double.METHOD_CALL; \ llvm_unreachable("Unexpected semantics"); \ } while (false) namespace llvm { struct fltSemantics; class APSInt; class StringRef; class APFloat; class raw_ostream; template class SmallVectorImpl; /// Enum that represents what fraction of the LSB truncated bits of an fp number /// represent. /// /// This essentially combines the roles of guard and sticky bits. enum lostFraction { // Example of truncated bits: lfExactlyZero, // 000000 lfLessThanHalf, // 0xxxxx x's not all zero lfExactlyHalf, // 100000 lfMoreThanHalf // 1xxxxx x's not all zero }; /// A self-contained host- and target-independent arbitrary-precision /// floating-point software implementation. /// /// APFloat uses bignum integer arithmetic as provided by static functions in /// the APInt class. The library will work with bignum integers whose parts are /// any unsigned type at least 16 bits wide, but 64 bits is recommended. /// /// Written for clarity rather than speed, in particular with a view to use in /// the front-end of a cross compiler so that target arithmetic can be correctly /// performed on the host. Performance should nonetheless be reasonable, /// particularly for its intended use. It may be useful as a base /// implementation for a run-time library during development of a faster /// target-specific one. /// /// All 5 rounding modes in the IEEE-754R draft are handled correctly for all /// implemented operations. Currently implemented operations are add, subtract, /// multiply, divide, fused-multiply-add, conversion-to-float, /// conversion-to-integer and conversion-from-integer. New rounding modes /// (e.g. away from zero) can be added with three or four lines of code. /// /// Four formats are built-in: IEEE single precision, double precision, /// quadruple precision, and x87 80-bit extended double (when operating with /// full extended precision). Adding a new format that obeys IEEE semantics /// only requires adding two lines of code: a declaration and definition of the /// format. /// /// All operations return the status of that operation as an exception bit-mask, /// so multiple operations can be done consecutively with their results or-ed /// together. The returned status can be useful for compiler diagnostics; e.g., /// inexact, underflow and overflow can be easily diagnosed on constant folding, /// and compiler optimizers can determine what exceptions would be raised by /// folding operations and optimize, or perhaps not optimize, accordingly. /// /// At present, underflow tininess is detected after rounding; it should be /// straight forward to add support for the before-rounding case too. /// /// The library reads hexadecimal floating point numbers as per C99, and /// correctly rounds if necessary according to the specified rounding mode. /// Syntax is required to have been validated by the caller. It also converts /// floating point numbers to hexadecimal text as per the C99 %a and %A /// conversions. The output precision (or alternatively the natural minimal /// precision) can be specified; if the requested precision is less than the /// natural precision the output is correctly rounded for the specified rounding /// mode. /// /// It also reads decimal floating point numbers and correctly rounds according /// to the specified rounding mode. /// /// Conversion to decimal text is not currently implemented. /// /// Non-zero finite numbers are represented internally as a sign bit, a 16-bit /// signed exponent, and the significand as an array of integer parts. After /// normalization of a number of precision P the exponent is within the range of /// the format, and if the number is not denormal the P-th bit of the /// significand is set as an explicit integer bit. For denormals the most /// significant bit is shifted right so that the exponent is maintained at the /// format's minimum, so that the smallest denormal has just the least /// significant bit of the significand set. The sign of zeroes and infinities /// is significant; the exponent and significand of such numbers is not stored, /// but has a known implicit (deterministic) value: 0 for the significands, 0 /// for zero exponent, all 1 bits for infinity exponent. For NaNs the sign and /// significand are deterministic, although not really meaningful, and preserved /// in non-conversion operations. The exponent is implicitly all 1 bits. /// /// APFloat does not provide any exception handling beyond default exception /// handling. We represent Signaling NaNs via IEEE-754R 2008 6.2.1 should clause /// by encoding Signaling NaNs with the first bit of its trailing significand as /// 0. /// /// TODO /// ==== /// /// Some features that may or may not be worth adding: /// /// Binary to decimal conversion (hard). /// /// Optional ability to detect underflow tininess before rounding. /// /// New formats: x87 in single and double precision mode (IEEE apart from /// extended exponent range) (hard). /// /// New operations: sqrt, IEEE remainder, C90 fmod, nexttoward. /// // This is the common type definitions shared by APFloat and its internal // implementation classes. This struct should not define any non-static data // members. struct APFloatBase { typedef APInt::WordType integerPart; static const unsigned integerPartWidth = APInt::APINT_BITS_PER_WORD; /// A signed type to represent a floating point numbers unbiased exponent. typedef signed short ExponentType; /// \name Floating Point Semantics. /// @{ enum Semantics { S_IEEEhalf, S_IEEEsingle, S_IEEEdouble, S_x87DoubleExtended, S_IEEEquad, S_PPCDoubleDouble }; static const llvm::fltSemantics &EnumToSemantics(Semantics S); static Semantics SemanticsToEnum(const llvm::fltSemantics &Sem); static const fltSemantics &IEEEhalf() LLVM_READNONE; static const fltSemantics &IEEEsingle() LLVM_READNONE; static const fltSemantics &IEEEdouble() LLVM_READNONE; static const fltSemantics &IEEEquad() LLVM_READNONE; static const fltSemantics &PPCDoubleDouble() LLVM_READNONE; static const fltSemantics &x87DoubleExtended() LLVM_READNONE; /// A Pseudo fltsemantic used to construct APFloats that cannot conflict with /// anything real. static const fltSemantics &Bogus() LLVM_READNONE; /// @} /// IEEE-754R 5.11: Floating Point Comparison Relations. enum cmpResult { cmpLessThan, cmpEqual, cmpGreaterThan, cmpUnordered }; /// IEEE-754R 4.3: Rounding-direction attributes. enum roundingMode { rmNearestTiesToEven, rmTowardPositive, rmTowardNegative, rmTowardZero, rmNearestTiesToAway }; /// IEEE-754R 7: Default exception handling. /// /// opUnderflow or opOverflow are always returned or-ed with opInexact. /// /// APFloat models this behavior specified by IEEE-754: /// "For operations producing results in floating-point format, the default /// result of an operation that signals the invalid operation exception /// shall be a quiet NaN." enum opStatus { opOK = 0x00, opInvalidOp = 0x01, opDivByZero = 0x02, opOverflow = 0x04, opUnderflow = 0x08, opInexact = 0x10 }; /// Category of internally-represented number. enum fltCategory { fcInfinity, fcNaN, fcNormal, fcZero }; /// Convenience enum used to construct an uninitialized APFloat. enum uninitializedTag { uninitialized }; /// Enumeration of \c ilogb error results. enum IlogbErrorKinds { IEK_Zero = INT_MIN + 1, IEK_NaN = INT_MIN, IEK_Inf = INT_MAX }; static unsigned int semanticsPrecision(const fltSemantics &); static ExponentType semanticsMinExponent(const fltSemantics &); static ExponentType semanticsMaxExponent(const fltSemantics &); static unsigned int semanticsSizeInBits(const fltSemantics &); /// Returns the size of the floating point number (in bits) in the given /// semantics. static unsigned getSizeInBits(const fltSemantics &Sem); }; namespace detail { class IEEEFloat final : public APFloatBase { public: /// \name Constructors /// @{ IEEEFloat(const fltSemantics &); // Default construct to 0.0 IEEEFloat(const fltSemantics &, integerPart); IEEEFloat(const fltSemantics &, uninitializedTag); IEEEFloat(const fltSemantics &, const APInt &); explicit IEEEFloat(double d); explicit IEEEFloat(float f); IEEEFloat(const IEEEFloat &); IEEEFloat(IEEEFloat &&); ~IEEEFloat(); /// @} /// Returns whether this instance allocated memory. bool needsCleanup() const { return partCount() > 1; } /// \name Convenience "constructors" /// @{ /// @} /// \name Arithmetic /// @{ opStatus add(const IEEEFloat &, roundingMode); opStatus subtract(const IEEEFloat &, roundingMode); opStatus multiply(const IEEEFloat &, roundingMode); opStatus divide(const IEEEFloat &, roundingMode); /// IEEE remainder. opStatus remainder(const IEEEFloat &); /// C fmod, or llvm frem. opStatus mod(const IEEEFloat &); opStatus fusedMultiplyAdd(const IEEEFloat &, const IEEEFloat &, roundingMode); opStatus roundToIntegral(roundingMode); /// IEEE-754R 5.3.1: nextUp/nextDown. opStatus next(bool nextDown); /// @} /// \name Sign operations. /// @{ void changeSign(); /// @} /// \name Conversions /// @{ opStatus convert(const fltSemantics &, roundingMode, bool *); opStatus convertToInteger(MutableArrayRef, unsigned int, bool, roundingMode, bool *) const; opStatus convertFromAPInt(const APInt &, bool, roundingMode); opStatus convertFromSignExtendedInteger(const integerPart *, unsigned int, bool, roundingMode); opStatus convertFromZeroExtendedInteger(const integerPart *, unsigned int, bool, roundingMode); opStatus convertFromString(StringRef, roundingMode); APInt bitcastToAPInt() const; double convertToDouble() const; float convertToFloat() const; /// @} /// The definition of equality is not straightforward for floating point, so /// we won't use operator==. Use one of the following, or write whatever it /// is you really mean. bool operator==(const IEEEFloat &) const = delete; /// IEEE comparison with another floating point number (NaNs compare /// unordered, 0==-0). cmpResult compare(const IEEEFloat &) const; /// Bitwise comparison for equality (QNaNs compare equal, 0!=-0). bool bitwiseIsEqual(const IEEEFloat &) const; /// Write out a hexadecimal representation of the floating point value to DST, /// which must be of sufficient size, in the C99 form [-]0xh.hhhhp[+-]d. /// Return the number of characters written, excluding the terminating NUL. unsigned int convertToHexString(char *dst, unsigned int hexDigits, bool upperCase, roundingMode) const; /// \name IEEE-754R 5.7.2 General operations. /// @{ /// IEEE-754R isSignMinus: Returns true if and only if the current value is /// negative. /// /// This applies to zeros and NaNs as well. bool isNegative() const { return sign; } /// IEEE-754R isNormal: Returns true if and only if the current value is normal. /// /// This implies that the current value of the float is not zero, subnormal, /// infinite, or NaN following the definition of normality from IEEE-754R. bool isNormal() const { return !isDenormal() && isFiniteNonZero(); } /// Returns true if and only if the current value is zero, subnormal, or /// normal. /// /// This means that the value is not infinite or NaN. bool isFinite() const { return !isNaN() && !isInfinity(); } /// Returns true if and only if the float is plus or minus zero. bool isZero() const { return category == fcZero; } /// IEEE-754R isSubnormal(): Returns true if and only if the float is a /// denormal. bool isDenormal() const; /// IEEE-754R isInfinite(): Returns true if and only if the float is infinity. bool isInfinity() const { return category == fcInfinity; } /// Returns true if and only if the float is a quiet or signaling NaN. bool isNaN() const { return category == fcNaN; } /// Returns true if and only if the float is a signaling NaN. bool isSignaling() const; /// @} /// \name Simple Queries /// @{ fltCategory getCategory() const { return category; } const fltSemantics &getSemantics() const { return *semantics; } bool isNonZero() const { return category != fcZero; } bool isFiniteNonZero() const { return isFinite() && !isZero(); } bool isPosZero() const { return isZero() && !isNegative(); } bool isNegZero() const { return isZero() && isNegative(); } /// Returns true if and only if the number has the smallest possible non-zero /// magnitude in the current semantics. bool isSmallest() const; /// Returns true if and only if the number has the largest possible finite /// magnitude in the current semantics. bool isLargest() const; /// Returns true if and only if the number is an exact integer. bool isInteger() const; /// @} IEEEFloat &operator=(const IEEEFloat &); IEEEFloat &operator=(IEEEFloat &&); /// Overload to compute a hash code for an APFloat value. /// /// Note that the use of hash codes for floating point values is in general /// frought with peril. Equality is hard to define for these values. For /// example, should negative and positive zero hash to different codes? Are /// they equal or not? This hash value implementation specifically /// emphasizes producing different codes for different inputs in order to /// be used in canonicalization and memoization. As such, equality is /// bitwiseIsEqual, and 0 != -0. friend hash_code hash_value(const IEEEFloat &Arg); /// Converts this value into a decimal string. /// /// \param FormatPrecision The maximum number of digits of /// precision to output. If there are fewer digits available, /// zero padding will not be used unless the value is /// integral and small enough to be expressed in /// FormatPrecision digits. 0 means to use the natural /// precision of the number. /// \param FormatMaxPadding The maximum number of zeros to /// consider inserting before falling back to scientific /// notation. 0 means to always use scientific notation. /// /// \param TruncateZero Indicate whether to remove the trailing zero in /// fraction part or not. Also setting this parameter to false forcing /// producing of output more similar to default printf behavior. /// Specifically the lower e is used as exponent delimiter and exponent /// always contains no less than two digits. /// /// Number Precision MaxPadding Result /// ------ --------- ---------- ------ /// 1.01E+4 5 2 10100 /// 1.01E+4 4 2 1.01E+4 /// 1.01E+4 5 1 1.01E+4 /// 1.01E-2 5 2 0.0101 /// 1.01E-2 4 2 0.0101 /// 1.01E-2 4 1 1.01E-2 void toString(SmallVectorImpl &Str, unsigned FormatPrecision = 0, unsigned FormatMaxPadding = 3, bool TruncateZero = true) const; /// If this value has an exact multiplicative inverse, store it in inv and /// return true. bool getExactInverse(APFloat *inv) const; /// Returns the exponent of the internal representation of the APFloat. /// /// Because the radix of APFloat is 2, this is equivalent to floor(log2(x)). /// For special APFloat values, this returns special error codes: /// /// NaN -> \c IEK_NaN /// 0 -> \c IEK_Zero /// Inf -> \c IEK_Inf /// friend int ilogb(const IEEEFloat &Arg); /// Returns: X * 2^Exp for integral exponents. friend IEEEFloat scalbn(IEEEFloat X, int Exp, roundingMode); friend IEEEFloat frexp(const IEEEFloat &X, int &Exp, roundingMode); /// \name Special value setters. /// @{ void makeLargest(bool Neg = false); void makeSmallest(bool Neg = false); void makeNaN(bool SNaN = false, bool Neg = false, const APInt *fill = nullptr); void makeInf(bool Neg = false); void makeZero(bool Neg = false); void makeQuiet(); /// Returns the smallest (by magnitude) normalized finite number in the given /// semantics. /// /// \param Negative - True iff the number should be negative void makeSmallestNormalized(bool Negative = false); /// @} cmpResult compareAbsoluteValue(const IEEEFloat &) const; private: /// \name Simple Queries /// @{ integerPart *significandParts(); const integerPart *significandParts() const; unsigned int partCount() const; /// @} /// \name Significand operations. /// @{ integerPart addSignificand(const IEEEFloat &); integerPart subtractSignificand(const IEEEFloat &, integerPart); lostFraction addOrSubtractSignificand(const IEEEFloat &, bool subtract); lostFraction multiplySignificand(const IEEEFloat &, const IEEEFloat *); lostFraction divideSignificand(const IEEEFloat &); void incrementSignificand(); void initialize(const fltSemantics *); void shiftSignificandLeft(unsigned int); lostFraction shiftSignificandRight(unsigned int); unsigned int significandLSB() const; unsigned int significandMSB() const; void zeroSignificand(); /// Return true if the significand excluding the integral bit is all ones. bool isSignificandAllOnes() const; /// Return true if the significand excluding the integral bit is all zeros. bool isSignificandAllZeros() const; /// @} /// \name Arithmetic on special values. /// @{ opStatus addOrSubtractSpecials(const IEEEFloat &, bool subtract); opStatus divideSpecials(const IEEEFloat &); opStatus multiplySpecials(const IEEEFloat &); opStatus modSpecials(const IEEEFloat &); /// @} /// \name Miscellany /// @{ bool convertFromStringSpecials(StringRef str); opStatus normalize(roundingMode, lostFraction); opStatus addOrSubtract(const IEEEFloat &, roundingMode, bool subtract); opStatus handleOverflow(roundingMode); bool roundAwayFromZero(roundingMode, lostFraction, unsigned int) const; opStatus convertToSignExtendedInteger(MutableArrayRef, unsigned int, bool, roundingMode, bool *) const; opStatus convertFromUnsignedParts(const integerPart *, unsigned int, roundingMode); opStatus convertFromHexadecimalString(StringRef, roundingMode); opStatus convertFromDecimalString(StringRef, roundingMode); char *convertNormalToHexString(char *, unsigned int, bool, roundingMode) const; opStatus roundSignificandWithExponent(const integerPart *, unsigned int, int, roundingMode); /// @} APInt convertHalfAPFloatToAPInt() const; APInt convertFloatAPFloatToAPInt() const; APInt convertDoubleAPFloatToAPInt() const; APInt convertQuadrupleAPFloatToAPInt() const; APInt convertF80LongDoubleAPFloatToAPInt() const; APInt convertPPCDoubleDoubleAPFloatToAPInt() const; void initFromAPInt(const fltSemantics *Sem, const APInt &api); void initFromHalfAPInt(const APInt &api); void initFromFloatAPInt(const APInt &api); void initFromDoubleAPInt(const APInt &api); void initFromQuadrupleAPInt(const APInt &api); void initFromF80LongDoubleAPInt(const APInt &api); void initFromPPCDoubleDoubleAPInt(const APInt &api); void assign(const IEEEFloat &); void copySignificand(const IEEEFloat &); void freeSignificand(); /// Note: this must be the first data member. /// The semantics that this value obeys. const fltSemantics *semantics; /// A binary fraction with an explicit integer bit. /// /// The significand must be at least one bit wider than the target precision. union Significand { integerPart part; integerPart *parts; } significand; /// The signed unbiased exponent of the value. ExponentType exponent; /// What kind of floating point number this is. /// /// Only 2 bits are required, but VisualStudio incorrectly sign extends it. /// Using the extra bit keeps it from failing under VisualStudio. fltCategory category : 3; /// Sign bit of the number. unsigned int sign : 1; }; hash_code hash_value(const IEEEFloat &Arg); int ilogb(const IEEEFloat &Arg); IEEEFloat scalbn(IEEEFloat X, int Exp, IEEEFloat::roundingMode); IEEEFloat frexp(const IEEEFloat &Val, int &Exp, IEEEFloat::roundingMode RM); // This mode implements more precise float in terms of two APFloats. // The interface and layout is designed for arbitray underlying semantics, // though currently only PPCDoubleDouble semantics are supported, whose // corresponding underlying semantics are IEEEdouble. class DoubleAPFloat final : public APFloatBase { // Note: this must be the first data member. const fltSemantics *Semantics; std::unique_ptr Floats; opStatus addImpl(const APFloat &a, const APFloat &aa, const APFloat &c, const APFloat &cc, roundingMode RM); opStatus addWithSpecial(const DoubleAPFloat &LHS, const DoubleAPFloat &RHS, DoubleAPFloat &Out, roundingMode RM); public: DoubleAPFloat(const fltSemantics &S); DoubleAPFloat(const fltSemantics &S, uninitializedTag); DoubleAPFloat(const fltSemantics &S, integerPart); DoubleAPFloat(const fltSemantics &S, const APInt &I); DoubleAPFloat(const fltSemantics &S, APFloat &&First, APFloat &&Second); DoubleAPFloat(const DoubleAPFloat &RHS); DoubleAPFloat(DoubleAPFloat &&RHS); DoubleAPFloat &operator=(const DoubleAPFloat &RHS); DoubleAPFloat &operator=(DoubleAPFloat &&RHS) { if (this != &RHS) { this->~DoubleAPFloat(); new (this) DoubleAPFloat(std::move(RHS)); } return *this; } bool needsCleanup() const { return Floats != nullptr; } APFloat &getFirst() { return Floats[0]; } const APFloat &getFirst() const { return Floats[0]; } APFloat &getSecond() { return Floats[1]; } const APFloat &getSecond() const { return Floats[1]; } opStatus add(const DoubleAPFloat &RHS, roundingMode RM); opStatus subtract(const DoubleAPFloat &RHS, roundingMode RM); opStatus multiply(const DoubleAPFloat &RHS, roundingMode RM); opStatus divide(const DoubleAPFloat &RHS, roundingMode RM); opStatus remainder(const DoubleAPFloat &RHS); opStatus mod(const DoubleAPFloat &RHS); opStatus fusedMultiplyAdd(const DoubleAPFloat &Multiplicand, const DoubleAPFloat &Addend, roundingMode RM); opStatus roundToIntegral(roundingMode RM); void changeSign(); cmpResult compareAbsoluteValue(const DoubleAPFloat &RHS) const; fltCategory getCategory() const; bool isNegative() const; void makeInf(bool Neg); void makeZero(bool Neg); void makeLargest(bool Neg); void makeSmallest(bool Neg); void makeSmallestNormalized(bool Neg); void makeNaN(bool SNaN, bool Neg, const APInt *fill); cmpResult compare(const DoubleAPFloat &RHS) const; bool bitwiseIsEqual(const DoubleAPFloat &RHS) const; APInt bitcastToAPInt() const; opStatus convertFromString(StringRef, roundingMode); opStatus next(bool nextDown); opStatus convertToInteger(MutableArrayRef Input, unsigned int Width, bool IsSigned, roundingMode RM, bool *IsExact) const; opStatus convertFromAPInt(const APInt &Input, bool IsSigned, roundingMode RM); opStatus convertFromSignExtendedInteger(const integerPart *Input, unsigned int InputSize, bool IsSigned, roundingMode RM); opStatus convertFromZeroExtendedInteger(const integerPart *Input, unsigned int InputSize, bool IsSigned, roundingMode RM); unsigned int convertToHexString(char *DST, unsigned int HexDigits, bool UpperCase, roundingMode RM) const; bool isDenormal() const; bool isSmallest() const; bool isLargest() const; bool isInteger() const; void toString(SmallVectorImpl &Str, unsigned FormatPrecision, unsigned FormatMaxPadding, bool TruncateZero = true) const; bool getExactInverse(APFloat *inv) const; friend int ilogb(const DoubleAPFloat &Arg); friend DoubleAPFloat scalbn(DoubleAPFloat X, int Exp, roundingMode); friend DoubleAPFloat frexp(const DoubleAPFloat &X, int &Exp, roundingMode); friend hash_code hash_value(const DoubleAPFloat &Arg); }; hash_code hash_value(const DoubleAPFloat &Arg); } // End detail namespace // This is a interface class that is currently forwarding functionalities from // detail::IEEEFloat. class APFloat : public APFloatBase { typedef detail::IEEEFloat IEEEFloat; typedef detail::DoubleAPFloat DoubleAPFloat; static_assert(std::is_standard_layout::value, ""); union Storage { const fltSemantics *semantics; IEEEFloat IEEE; DoubleAPFloat Double; explicit Storage(IEEEFloat F, const fltSemantics &S); explicit Storage(DoubleAPFloat F, const fltSemantics &S) : Double(std::move(F)) { assert(&S == &PPCDoubleDouble()); } template Storage(const fltSemantics &Semantics, ArgTypes &&... Args) { if (usesLayout(Semantics)) { new (&IEEE) IEEEFloat(Semantics, std::forward(Args)...); return; } if (usesLayout(Semantics)) { new (&Double) DoubleAPFloat(Semantics, std::forward(Args)...); return; } llvm_unreachable("Unexpected semantics"); } ~Storage() { if (usesLayout(*semantics)) { IEEE.~IEEEFloat(); return; } if (usesLayout(*semantics)) { Double.~DoubleAPFloat(); return; } llvm_unreachable("Unexpected semantics"); } Storage(const Storage &RHS) { if (usesLayout(*RHS.semantics)) { new (this) IEEEFloat(RHS.IEEE); return; } if (usesLayout(*RHS.semantics)) { new (this) DoubleAPFloat(RHS.Double); return; } llvm_unreachable("Unexpected semantics"); } Storage(Storage &&RHS) { if (usesLayout(*RHS.semantics)) { new (this) IEEEFloat(std::move(RHS.IEEE)); return; } if (usesLayout(*RHS.semantics)) { new (this) DoubleAPFloat(std::move(RHS.Double)); return; } llvm_unreachable("Unexpected semantics"); } Storage &operator=(const Storage &RHS) { if (usesLayout(*semantics) && usesLayout(*RHS.semantics)) { IEEE = RHS.IEEE; } else if (usesLayout(*semantics) && usesLayout(*RHS.semantics)) { Double = RHS.Double; } else if (this != &RHS) { this->~Storage(); new (this) Storage(RHS); } return *this; } Storage &operator=(Storage &&RHS) { if (usesLayout(*semantics) && usesLayout(*RHS.semantics)) { IEEE = std::move(RHS.IEEE); } else if (usesLayout(*semantics) && usesLayout(*RHS.semantics)) { Double = std::move(RHS.Double); } else if (this != &RHS) { this->~Storage(); new (this) Storage(std::move(RHS)); } return *this; } } U; template static bool usesLayout(const fltSemantics &Semantics) { static_assert(std::is_same::value || std::is_same::value, ""); if (std::is_same::value) { return &Semantics == &PPCDoubleDouble(); } return &Semantics != &PPCDoubleDouble(); } IEEEFloat &getIEEE() { if (usesLayout(*U.semantics)) return U.IEEE; if (usesLayout(*U.semantics)) return U.Double.getFirst().U.IEEE; llvm_unreachable("Unexpected semantics"); } const IEEEFloat &getIEEE() const { if (usesLayout(*U.semantics)) return U.IEEE; if (usesLayout(*U.semantics)) return U.Double.getFirst().U.IEEE; llvm_unreachable("Unexpected semantics"); } void makeZero(bool Neg) { APFLOAT_DISPATCH_ON_SEMANTICS(makeZero(Neg)); } void makeInf(bool Neg) { APFLOAT_DISPATCH_ON_SEMANTICS(makeInf(Neg)); } void makeNaN(bool SNaN, bool Neg, const APInt *fill) { APFLOAT_DISPATCH_ON_SEMANTICS(makeNaN(SNaN, Neg, fill)); } void makeLargest(bool Neg) { APFLOAT_DISPATCH_ON_SEMANTICS(makeLargest(Neg)); } void makeSmallest(bool Neg) { APFLOAT_DISPATCH_ON_SEMANTICS(makeSmallest(Neg)); } void makeSmallestNormalized(bool Neg) { APFLOAT_DISPATCH_ON_SEMANTICS(makeSmallestNormalized(Neg)); } // FIXME: This is due to clang 3.3 (or older version) always checks for the // default constructor in an array aggregate initialization, even if no // elements in the array is default initialized. APFloat() : U(IEEEdouble()) { llvm_unreachable("This is a workaround for old clang."); } explicit APFloat(IEEEFloat F, const fltSemantics &S) : U(std::move(F), S) {} explicit APFloat(DoubleAPFloat F, const fltSemantics &S) : U(std::move(F), S) {} cmpResult compareAbsoluteValue(const APFloat &RHS) const { assert(&getSemantics() == &RHS.getSemantics() && "Should only compare APFloats with the same semantics"); if (usesLayout(getSemantics())) return U.IEEE.compareAbsoluteValue(RHS.U.IEEE); if (usesLayout(getSemantics())) return U.Double.compareAbsoluteValue(RHS.U.Double); llvm_unreachable("Unexpected semantics"); } public: APFloat(const fltSemantics &Semantics) : U(Semantics) {} APFloat(const fltSemantics &Semantics, StringRef S); APFloat(const fltSemantics &Semantics, integerPart I) : U(Semantics, I) {} // TODO: Remove this constructor. This isn't faster than the first one. APFloat(const fltSemantics &Semantics, uninitializedTag) : U(Semantics, uninitialized) {} APFloat(const fltSemantics &Semantics, const APInt &I) : U(Semantics, I) {} explicit APFloat(double d) : U(IEEEFloat(d), IEEEdouble()) {} explicit APFloat(float f) : U(IEEEFloat(f), IEEEsingle()) {} APFloat(const APFloat &RHS) = default; APFloat(APFloat &&RHS) = default; ~APFloat() = default; bool needsCleanup() const { APFLOAT_DISPATCH_ON_SEMANTICS(needsCleanup()); } /// Factory for Positive and Negative Zero. /// /// \param Negative True iff the number should be negative. static APFloat getZero(const fltSemantics &Sem, bool Negative = false) { APFloat Val(Sem, uninitialized); Val.makeZero(Negative); return Val; } /// Factory for Positive and Negative Infinity. /// /// \param Negative True iff the number should be negative. static APFloat getInf(const fltSemantics &Sem, bool Negative = false) { APFloat Val(Sem, uninitialized); Val.makeInf(Negative); return Val; } /// Factory for NaN values. /// /// \param Negative - True iff the NaN generated should be negative. /// \param payload - The unspecified fill bits for creating the NaN, 0 by /// default. The value is truncated as necessary. static APFloat getNaN(const fltSemantics &Sem, bool Negative = false, uint64_t payload = 0) { if (payload) { APInt intPayload(64, payload); return getQNaN(Sem, Negative, &intPayload); } else { return getQNaN(Sem, Negative, nullptr); } } /// Factory for QNaN values. static APFloat getQNaN(const fltSemantics &Sem, bool Negative = false, const APInt *payload = nullptr) { APFloat Val(Sem, uninitialized); Val.makeNaN(false, Negative, payload); return Val; } /// Factory for SNaN values. static APFloat getSNaN(const fltSemantics &Sem, bool Negative = false, const APInt *payload = nullptr) { APFloat Val(Sem, uninitialized); Val.makeNaN(true, Negative, payload); return Val; } /// Returns the largest finite number in the given semantics. /// /// \param Negative - True iff the number should be negative static APFloat getLargest(const fltSemantics &Sem, bool Negative = false) { APFloat Val(Sem, uninitialized); Val.makeLargest(Negative); return Val; } /// Returns the smallest (by magnitude) finite number in the given semantics. /// Might be denormalized, which implies a relative loss of precision. /// /// \param Negative - True iff the number should be negative static APFloat getSmallest(const fltSemantics &Sem, bool Negative = false) { APFloat Val(Sem, uninitialized); Val.makeSmallest(Negative); return Val; } /// Returns the smallest (by magnitude) normalized finite number in the given /// semantics. /// /// \param Negative - True iff the number should be negative static APFloat getSmallestNormalized(const fltSemantics &Sem, bool Negative = false) { APFloat Val(Sem, uninitialized); Val.makeSmallestNormalized(Negative); return Val; } /// Returns a float which is bitcasted from an all one value int. /// /// \param BitWidth - Select float type /// \param isIEEE - If 128 bit number, select between PPC and IEEE static APFloat getAllOnesValue(unsigned BitWidth, bool isIEEE = false); /// Used to insert APFloat objects, or objects that contain APFloat objects, /// into FoldingSets. void Profile(FoldingSetNodeID &NID) const; opStatus add(const APFloat &RHS, roundingMode RM) { assert(&getSemantics() == &RHS.getSemantics() && "Should only call on two APFloats with the same semantics"); if (usesLayout(getSemantics())) return U.IEEE.add(RHS.U.IEEE, RM); if (usesLayout(getSemantics())) return U.Double.add(RHS.U.Double, RM); llvm_unreachable("Unexpected semantics"); } opStatus subtract(const APFloat &RHS, roundingMode RM) { assert(&getSemantics() == &RHS.getSemantics() && "Should only call on two APFloats with the same semantics"); if (usesLayout(getSemantics())) return U.IEEE.subtract(RHS.U.IEEE, RM); if (usesLayout(getSemantics())) return U.Double.subtract(RHS.U.Double, RM); llvm_unreachable("Unexpected semantics"); } opStatus multiply(const APFloat &RHS, roundingMode RM) { assert(&getSemantics() == &RHS.getSemantics() && "Should only call on two APFloats with the same semantics"); if (usesLayout(getSemantics())) return U.IEEE.multiply(RHS.U.IEEE, RM); if (usesLayout(getSemantics())) return U.Double.multiply(RHS.U.Double, RM); llvm_unreachable("Unexpected semantics"); } opStatus divide(const APFloat &RHS, roundingMode RM) { assert(&getSemantics() == &RHS.getSemantics() && "Should only call on two APFloats with the same semantics"); if (usesLayout(getSemantics())) return U.IEEE.divide(RHS.U.IEEE, RM); if (usesLayout(getSemantics())) return U.Double.divide(RHS.U.Double, RM); llvm_unreachable("Unexpected semantics"); } opStatus remainder(const APFloat &RHS) { assert(&getSemantics() == &RHS.getSemantics() && "Should only call on two APFloats with the same semantics"); if (usesLayout(getSemantics())) return U.IEEE.remainder(RHS.U.IEEE); if (usesLayout(getSemantics())) return U.Double.remainder(RHS.U.Double); llvm_unreachable("Unexpected semantics"); } opStatus mod(const APFloat &RHS) { assert(&getSemantics() == &RHS.getSemantics() && "Should only call on two APFloats with the same semantics"); if (usesLayout(getSemantics())) return U.IEEE.mod(RHS.U.IEEE); if (usesLayout(getSemantics())) return U.Double.mod(RHS.U.Double); llvm_unreachable("Unexpected semantics"); } opStatus fusedMultiplyAdd(const APFloat &Multiplicand, const APFloat &Addend, roundingMode RM) { assert(&getSemantics() == &Multiplicand.getSemantics() && "Should only call on APFloats with the same semantics"); assert(&getSemantics() == &Addend.getSemantics() && "Should only call on APFloats with the same semantics"); if (usesLayout(getSemantics())) return U.IEEE.fusedMultiplyAdd(Multiplicand.U.IEEE, Addend.U.IEEE, RM); if (usesLayout(getSemantics())) return U.Double.fusedMultiplyAdd(Multiplicand.U.Double, Addend.U.Double, RM); llvm_unreachable("Unexpected semantics"); } opStatus roundToIntegral(roundingMode RM) { APFLOAT_DISPATCH_ON_SEMANTICS(roundToIntegral(RM)); } // TODO: bool parameters are not readable and a source of bugs. // Do something. opStatus next(bool nextDown) { APFLOAT_DISPATCH_ON_SEMANTICS(next(nextDown)); } /// Add two APFloats, rounding ties to the nearest even. /// No error checking. APFloat operator+(const APFloat &RHS) const { APFloat Result(*this); (void)Result.add(RHS, rmNearestTiesToEven); return Result; } /// Subtract two APFloats, rounding ties to the nearest even. /// No error checking. APFloat operator-(const APFloat &RHS) const { APFloat Result(*this); (void)Result.subtract(RHS, rmNearestTiesToEven); return Result; } /// Multiply two APFloats, rounding ties to the nearest even. /// No error checking. APFloat operator*(const APFloat &RHS) const { APFloat Result(*this); (void)Result.multiply(RHS, rmNearestTiesToEven); return Result; } /// Divide the first APFloat by the second, rounding ties to the nearest even. /// No error checking. APFloat operator/(const APFloat &RHS) const { APFloat Result(*this); (void)Result.divide(RHS, rmNearestTiesToEven); return Result; } void changeSign() { APFLOAT_DISPATCH_ON_SEMANTICS(changeSign()); } void clearSign() { if (isNegative()) changeSign(); } void copySign(const APFloat &RHS) { if (isNegative() != RHS.isNegative()) changeSign(); } /// A static helper to produce a copy of an APFloat value with its sign /// copied from some other APFloat. static APFloat copySign(APFloat Value, const APFloat &Sign) { Value.copySign(Sign); return Value; } opStatus convert(const fltSemantics &ToSemantics, roundingMode RM, bool *losesInfo); opStatus convertToInteger(MutableArrayRef Input, unsigned int Width, bool IsSigned, roundingMode RM, bool *IsExact) const { APFLOAT_DISPATCH_ON_SEMANTICS( convertToInteger(Input, Width, IsSigned, RM, IsExact)); } opStatus convertToInteger(APSInt &Result, roundingMode RM, bool *IsExact) const; opStatus convertFromAPInt(const APInt &Input, bool IsSigned, roundingMode RM) { APFLOAT_DISPATCH_ON_SEMANTICS(convertFromAPInt(Input, IsSigned, RM)); } opStatus convertFromSignExtendedInteger(const integerPart *Input, unsigned int InputSize, bool IsSigned, roundingMode RM) { APFLOAT_DISPATCH_ON_SEMANTICS( convertFromSignExtendedInteger(Input, InputSize, IsSigned, RM)); } opStatus convertFromZeroExtendedInteger(const integerPart *Input, unsigned int InputSize, bool IsSigned, roundingMode RM) { APFLOAT_DISPATCH_ON_SEMANTICS( convertFromZeroExtendedInteger(Input, InputSize, IsSigned, RM)); } opStatus convertFromString(StringRef, roundingMode); APInt bitcastToAPInt() const { APFLOAT_DISPATCH_ON_SEMANTICS(bitcastToAPInt()); } double convertToDouble() const { return getIEEE().convertToDouble(); } float convertToFloat() const { return getIEEE().convertToFloat(); } bool operator==(const APFloat &) const = delete; cmpResult compare(const APFloat &RHS) const { assert(&getSemantics() == &RHS.getSemantics() && "Should only compare APFloats with the same semantics"); if (usesLayout(getSemantics())) return U.IEEE.compare(RHS.U.IEEE); if (usesLayout(getSemantics())) return U.Double.compare(RHS.U.Double); llvm_unreachable("Unexpected semantics"); } bool bitwiseIsEqual(const APFloat &RHS) const { if (&getSemantics() != &RHS.getSemantics()) return false; if (usesLayout(getSemantics())) return U.IEEE.bitwiseIsEqual(RHS.U.IEEE); if (usesLayout(getSemantics())) return U.Double.bitwiseIsEqual(RHS.U.Double); llvm_unreachable("Unexpected semantics"); } /// We don't rely on operator== working on double values, as /// it returns true for things that are clearly not equal, like -0.0 and 0.0. /// As such, this method can be used to do an exact bit-for-bit comparison of /// two floating point values. /// /// We leave the version with the double argument here because it's just so /// convenient to write "2.0" and the like. Without this function we'd /// have to duplicate its logic everywhere it's called. bool isExactlyValue(double V) const { bool ignored; APFloat Tmp(V); Tmp.convert(getSemantics(), APFloat::rmNearestTiesToEven, &ignored); return bitwiseIsEqual(Tmp); } unsigned int convertToHexString(char *DST, unsigned int HexDigits, bool UpperCase, roundingMode RM) const { APFLOAT_DISPATCH_ON_SEMANTICS( convertToHexString(DST, HexDigits, UpperCase, RM)); } bool isZero() const { return getCategory() == fcZero; } bool isInfinity() const { return getCategory() == fcInfinity; } bool isNaN() const { return getCategory() == fcNaN; } bool isNegative() const { return getIEEE().isNegative(); } bool isDenormal() const { APFLOAT_DISPATCH_ON_SEMANTICS(isDenormal()); } bool isSignaling() const { return getIEEE().isSignaling(); } bool isNormal() const { return !isDenormal() && isFiniteNonZero(); } bool isFinite() const { return !isNaN() && !isInfinity(); } fltCategory getCategory() const { return getIEEE().getCategory(); } const fltSemantics &getSemantics() const { return *U.semantics; } bool isNonZero() const { return !isZero(); } bool isFiniteNonZero() const { return isFinite() && !isZero(); } bool isPosZero() const { return isZero() && !isNegative(); } bool isNegZero() const { return isZero() && isNegative(); } bool isSmallest() const { APFLOAT_DISPATCH_ON_SEMANTICS(isSmallest()); } bool isLargest() const { APFLOAT_DISPATCH_ON_SEMANTICS(isLargest()); } bool isInteger() const { APFLOAT_DISPATCH_ON_SEMANTICS(isInteger()); } APFloat &operator=(const APFloat &RHS) = default; APFloat &operator=(APFloat &&RHS) = default; void toString(SmallVectorImpl &Str, unsigned FormatPrecision = 0, unsigned FormatMaxPadding = 3, bool TruncateZero = true) const { APFLOAT_DISPATCH_ON_SEMANTICS( toString(Str, FormatPrecision, FormatMaxPadding, TruncateZero)); } void print(raw_ostream &) const; void dump() const; bool getExactInverse(APFloat *inv) const { APFLOAT_DISPATCH_ON_SEMANTICS(getExactInverse(inv)); } friend hash_code hash_value(const APFloat &Arg); friend int ilogb(const APFloat &Arg) { return ilogb(Arg.getIEEE()); } friend APFloat scalbn(APFloat X, int Exp, roundingMode RM); friend APFloat frexp(const APFloat &X, int &Exp, roundingMode RM); friend IEEEFloat; friend DoubleAPFloat; }; /// See friend declarations above. /// /// These additional declarations are required in order to compile LLVM with IBM /// xlC compiler. hash_code hash_value(const APFloat &Arg); inline APFloat scalbn(APFloat X, int Exp, APFloat::roundingMode RM) { if (APFloat::usesLayout(X.getSemantics())) return APFloat(scalbn(X.U.IEEE, Exp, RM), X.getSemantics()); if (APFloat::usesLayout(X.getSemantics())) return APFloat(scalbn(X.U.Double, Exp, RM), X.getSemantics()); llvm_unreachable("Unexpected semantics"); } /// Equivalent of C standard library function. /// /// While the C standard says Exp is an unspecified value for infinity and nan, /// this returns INT_MAX for infinities, and INT_MIN for NaNs. inline APFloat frexp(const APFloat &X, int &Exp, APFloat::roundingMode RM) { if (APFloat::usesLayout(X.getSemantics())) return APFloat(frexp(X.U.IEEE, Exp, RM), X.getSemantics()); if (APFloat::usesLayout(X.getSemantics())) return APFloat(frexp(X.U.Double, Exp, RM), X.getSemantics()); llvm_unreachable("Unexpected semantics"); } /// Returns the absolute value of the argument. inline APFloat abs(APFloat X) { X.clearSign(); return X; } /// Returns the negated value of the argument. inline APFloat neg(APFloat X) { X.changeSign(); return X; } /// Implements IEEE minNum semantics. Returns the smaller of the 2 arguments if /// both are not NaN. If either argument is a NaN, returns the other argument. LLVM_READONLY inline APFloat minnum(const APFloat &A, const APFloat &B) { if (A.isNaN()) return B; if (B.isNaN()) return A; return (B.compare(A) == APFloat::cmpLessThan) ? B : A; } /// Implements IEEE maxNum semantics. Returns the larger of the 2 arguments if /// both are not NaN. If either argument is a NaN, returns the other argument. LLVM_READONLY inline APFloat maxnum(const APFloat &A, const APFloat &B) { if (A.isNaN()) return B; if (B.isNaN()) return A; return (A.compare(B) == APFloat::cmpLessThan) ? B : A; } /// Implements IEEE 754-2018 minimum semantics. Returns the smaller of 2 /// arguments, propagating NaNs and treating -0 as less than +0. LLVM_READONLY inline APFloat minimum(const APFloat &A, const APFloat &B) { if (A.isNaN()) return A; if (B.isNaN()) return B; if (A.isZero() && B.isZero() && (A.isNegative() != B.isNegative())) return A.isNegative() ? A : B; return (B.compare(A) == APFloat::cmpLessThan) ? B : A; } /// Implements IEEE 754-2018 maximum semantics. Returns the larger of 2 /// arguments, propagating NaNs and treating -0 as less than +0. LLVM_READONLY inline APFloat maximum(const APFloat &A, const APFloat &B) { if (A.isNaN()) return A; if (B.isNaN()) return B; if (A.isZero() && B.isZero() && (A.isNegative() != B.isNegative())) return A.isNegative() ? B : A; return (A.compare(B) == APFloat::cmpLessThan) ? B : A; } } // namespace llvm #undef APFLOAT_DISPATCH_ON_SEMANTICS #endif // LLVM_ADT_APFLOAT_H binaryen-version_91/third_party/llvm-project/include/llvm/ADT/APInt.h000066400000000000000000002222441362402614000257650ustar00rootroot00000000000000//===-- llvm/ADT/APInt.h - For Arbitrary Precision Integer -----*- C++ -*--===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// /// \file /// This file implements a class to represent arbitrary precision /// integral constant values and operations on them. /// //===----------------------------------------------------------------------===// #ifndef LLVM_ADT_APINT_H #define LLVM_ADT_APINT_H #include "llvm/Support/Compiler.h" #include "llvm/Support/MathExtras.h" #include #include #include #include namespace llvm { class FoldingSetNodeID; class StringRef; class hash_code; class raw_ostream; template class SmallVectorImpl; template class ArrayRef; template class Optional; class APInt; inline APInt operator-(APInt); //===----------------------------------------------------------------------===// // APInt Class //===----------------------------------------------------------------------===// /// Class for arbitrary precision integers. /// /// APInt is a functional replacement for common case unsigned integer type like /// "unsigned", "unsigned long" or "uint64_t", but also allows non-byte-width /// integer sizes and large integer value types such as 3-bits, 15-bits, or more /// than 64-bits of precision. APInt provides a variety of arithmetic operators /// and methods to manipulate integer values of any bit-width. It supports both /// the typical integer arithmetic and comparison operations as well as bitwise /// manipulation. /// /// The class has several invariants worth noting: /// * All bit, byte, and word positions are zero-based. /// * Once the bit width is set, it doesn't change except by the Truncate, /// SignExtend, or ZeroExtend operations. /// * All binary operators must be on APInt instances of the same bit width. /// Attempting to use these operators on instances with different bit /// widths will yield an assertion. /// * The value is stored canonically as an unsigned value. For operations /// where it makes a difference, there are both signed and unsigned variants /// of the operation. For example, sdiv and udiv. However, because the bit /// widths must be the same, operations such as Mul and Add produce the same /// results regardless of whether the values are interpreted as signed or /// not. /// * In general, the class tries to follow the style of computation that LLVM /// uses in its IR. This simplifies its use for LLVM. /// class LLVM_NODISCARD APInt { public: typedef uint64_t WordType; /// This enum is used to hold the constants we needed for APInt. enum : unsigned { /// Byte size of a word. APINT_WORD_SIZE = sizeof(WordType), /// Bits in a word. APINT_BITS_PER_WORD = APINT_WORD_SIZE * CHAR_BIT }; enum class Rounding { DOWN, TOWARD_ZERO, UP, }; static const WordType WORDTYPE_MAX = ~WordType(0); private: /// This union is used to store the integer value. When the /// integer bit-width <= 64, it uses VAL, otherwise it uses pVal. union { uint64_t VAL; ///< Used to store the <= 64 bits integer value. uint64_t *pVal; ///< Used to store the >64 bits integer value. } U; unsigned BitWidth; ///< The number of bits in this APInt. friend struct DenseMapAPIntKeyInfo; friend class APSInt; /// Fast internal constructor /// /// This constructor is used only internally for speed of construction of /// temporaries. It is unsafe for general use so it is not public. APInt(uint64_t *val, unsigned bits) : BitWidth(bits) { U.pVal = val; } /// Determine if this APInt just has one word to store value. /// /// \returns true if the number of bits <= 64, false otherwise. bool isSingleWord() const { return BitWidth <= APINT_BITS_PER_WORD; } /// Determine which word a bit is in. /// /// \returns the word position for the specified bit position. static unsigned whichWord(unsigned bitPosition) { return bitPosition / APINT_BITS_PER_WORD; } /// Determine which bit in a word a bit is in. /// /// \returns the bit position in a word for the specified bit position /// in the APInt. static unsigned whichBit(unsigned bitPosition) { return bitPosition % APINT_BITS_PER_WORD; } /// Get a single bit mask. /// /// \returns a uint64_t with only bit at "whichBit(bitPosition)" set /// This method generates and returns a uint64_t (word) mask for a single /// bit at a specific bit position. This is used to mask the bit in the /// corresponding word. static uint64_t maskBit(unsigned bitPosition) { return 1ULL << whichBit(bitPosition); } /// Clear unused high order bits /// /// This method is used internally to clear the top "N" bits in the high order /// word that are not used by the APInt. This is needed after the most /// significant word is assigned a value to ensure that those bits are /// zero'd out. APInt &clearUnusedBits() { // Compute how many bits are used in the final word unsigned WordBits = ((BitWidth-1) % APINT_BITS_PER_WORD) + 1; // Mask out the high bits. uint64_t mask = WORDTYPE_MAX >> (APINT_BITS_PER_WORD - WordBits); if (isSingleWord()) U.VAL &= mask; else U.pVal[getNumWords() - 1] &= mask; return *this; } /// Get the word corresponding to a bit position /// \returns the corresponding word for the specified bit position. uint64_t getWord(unsigned bitPosition) const { return isSingleWord() ? U.VAL : U.pVal[whichWord(bitPosition)]; } /// Utility method to change the bit width of this APInt to new bit width, /// allocating and/or deallocating as necessary. There is no guarantee on the /// value of any bits upon return. Caller should populate the bits after. void reallocate(unsigned NewBitWidth); /// Convert a char array into an APInt /// /// \param radix 2, 8, 10, 16, or 36 /// Converts a string into a number. The string must be non-empty /// and well-formed as a number of the given base. The bit-width /// must be sufficient to hold the result. /// /// This is used by the constructors that take string arguments. /// /// StringRef::getAsInteger is superficially similar but (1) does /// not assume that the string is well-formed and (2) grows the /// result to hold the input. void fromString(unsigned numBits, StringRef str, uint8_t radix); /// An internal division function for dividing APInts. /// /// This is used by the toString method to divide by the radix. It simply /// provides a more convenient form of divide for internal use since KnuthDiv /// has specific constraints on its inputs. If those constraints are not met /// then it provides a simpler form of divide. static void divide(const WordType *LHS, unsigned lhsWords, const WordType *RHS, unsigned rhsWords, WordType *Quotient, WordType *Remainder); /// out-of-line slow case for inline constructor void initSlowCase(uint64_t val, bool isSigned); /// shared code between two array constructors void initFromArray(ArrayRef array); /// out-of-line slow case for inline copy constructor void initSlowCase(const APInt &that); /// out-of-line slow case for shl void shlSlowCase(unsigned ShiftAmt); /// out-of-line slow case for lshr. void lshrSlowCase(unsigned ShiftAmt); /// out-of-line slow case for ashr. void ashrSlowCase(unsigned ShiftAmt); /// out-of-line slow case for operator= void AssignSlowCase(const APInt &RHS); /// out-of-line slow case for operator== bool EqualSlowCase(const APInt &RHS) const LLVM_READONLY; /// out-of-line slow case for countLeadingZeros unsigned countLeadingZerosSlowCase() const LLVM_READONLY; /// out-of-line slow case for countLeadingOnes. unsigned countLeadingOnesSlowCase() const LLVM_READONLY; /// out-of-line slow case for countTrailingZeros. unsigned countTrailingZerosSlowCase() const LLVM_READONLY; /// out-of-line slow case for countTrailingOnes unsigned countTrailingOnesSlowCase() const LLVM_READONLY; /// out-of-line slow case for countPopulation unsigned countPopulationSlowCase() const LLVM_READONLY; /// out-of-line slow case for intersects. bool intersectsSlowCase(const APInt &RHS) const LLVM_READONLY; /// out-of-line slow case for isSubsetOf. bool isSubsetOfSlowCase(const APInt &RHS) const LLVM_READONLY; /// out-of-line slow case for setBits. void setBitsSlowCase(unsigned loBit, unsigned hiBit); /// out-of-line slow case for flipAllBits. void flipAllBitsSlowCase(); /// out-of-line slow case for operator&=. void AndAssignSlowCase(const APInt& RHS); /// out-of-line slow case for operator|=. void OrAssignSlowCase(const APInt& RHS); /// out-of-line slow case for operator^=. void XorAssignSlowCase(const APInt& RHS); /// Unsigned comparison. Returns -1, 0, or 1 if this APInt is less than, equal /// to, or greater than RHS. int compare(const APInt &RHS) const LLVM_READONLY; /// Signed comparison. Returns -1, 0, or 1 if this APInt is less than, equal /// to, or greater than RHS. int compareSigned(const APInt &RHS) const LLVM_READONLY; public: /// \name Constructors /// @{ /// Create a new APInt of numBits width, initialized as val. /// /// If isSigned is true then val is treated as if it were a signed value /// (i.e. as an int64_t) and the appropriate sign extension to the bit width /// will be done. Otherwise, no sign extension occurs (high order bits beyond /// the range of val are zero filled). /// /// \param numBits the bit width of the constructed APInt /// \param val the initial value of the APInt /// \param isSigned how to treat signedness of val APInt(unsigned numBits, uint64_t val, bool isSigned = false) : BitWidth(numBits) { assert(BitWidth && "bitwidth too small"); if (isSingleWord()) { U.VAL = val; clearUnusedBits(); } else { initSlowCase(val, isSigned); } } /// Construct an APInt of numBits width, initialized as bigVal[]. /// /// Note that bigVal.size() can be smaller or larger than the corresponding /// bit width but any extraneous bits will be dropped. /// /// \param numBits the bit width of the constructed APInt /// \param bigVal a sequence of words to form the initial value of the APInt APInt(unsigned numBits, ArrayRef bigVal); /// Equivalent to APInt(numBits, ArrayRef(bigVal, numWords)), but /// deprecated because this constructor is prone to ambiguity with the /// APInt(unsigned, uint64_t, bool) constructor. /// /// If this overload is ever deleted, care should be taken to prevent calls /// from being incorrectly captured by the APInt(unsigned, uint64_t, bool) /// constructor. APInt(unsigned numBits, unsigned numWords, const uint64_t bigVal[]); /// Construct an APInt from a string representation. /// /// This constructor interprets the string \p str in the given radix. The /// interpretation stops when the first character that is not suitable for the /// radix is encountered, or the end of the string. Acceptable radix values /// are 2, 8, 10, 16, and 36. It is an error for the value implied by the /// string to require more bits than numBits. /// /// \param numBits the bit width of the constructed APInt /// \param str the string to be interpreted /// \param radix the radix to use for the conversion APInt(unsigned numBits, StringRef str, uint8_t radix); /// Simply makes *this a copy of that. /// Copy Constructor. APInt(const APInt &that) : BitWidth(that.BitWidth) { if (isSingleWord()) U.VAL = that.U.VAL; else initSlowCase(that); } /// Move Constructor. APInt(APInt &&that) : BitWidth(that.BitWidth) { memcpy(&U, &that.U, sizeof(U)); that.BitWidth = 0; } /// Destructor. ~APInt() { if (needsCleanup()) delete[] U.pVal; } /// Default constructor that creates an uninteresting APInt /// representing a 1-bit zero value. /// /// This is useful for object deserialization (pair this with the static /// method Read). explicit APInt() : BitWidth(1) { U.VAL = 0; } /// Returns whether this instance allocated memory. bool needsCleanup() const { return !isSingleWord(); } /// Used to insert APInt objects, or objects that contain APInt objects, into /// FoldingSets. void Profile(FoldingSetNodeID &id) const; /// @} /// \name Value Tests /// @{ /// Determine sign of this APInt. /// /// This tests the high bit of this APInt to determine if it is set. /// /// \returns true if this APInt is negative, false otherwise bool isNegative() const { return (*this)[BitWidth - 1]; } /// Determine if this APInt Value is non-negative (>= 0) /// /// This tests the high bit of the APInt to determine if it is unset. bool isNonNegative() const { return !isNegative(); } /// Determine if sign bit of this APInt is set. /// /// This tests the high bit of this APInt to determine if it is set. /// /// \returns true if this APInt has its sign bit set, false otherwise. bool isSignBitSet() const { return (*this)[BitWidth-1]; } /// Determine if sign bit of this APInt is clear. /// /// This tests the high bit of this APInt to determine if it is clear. /// /// \returns true if this APInt has its sign bit clear, false otherwise. bool isSignBitClear() const { return !isSignBitSet(); } /// Determine if this APInt Value is positive. /// /// This tests if the value of this APInt is positive (> 0). Note /// that 0 is not a positive value. /// /// \returns true if this APInt is positive. bool isStrictlyPositive() const { return isNonNegative() && !isNullValue(); } /// Determine if all bits are set /// /// This checks to see if the value has all bits of the APInt are set or not. bool isAllOnesValue() const { if (isSingleWord()) return U.VAL == WORDTYPE_MAX >> (APINT_BITS_PER_WORD - BitWidth); return countTrailingOnesSlowCase() == BitWidth; } /// Determine if all bits are clear /// /// This checks to see if the value has all bits of the APInt are clear or /// not. bool isNullValue() const { return !*this; } /// Determine if this is a value of 1. /// /// This checks to see if the value of this APInt is one. bool isOneValue() const { if (isSingleWord()) return U.VAL == 1; return countLeadingZerosSlowCase() == BitWidth - 1; } /// Determine if this is the largest unsigned value. /// /// This checks to see if the value of this APInt is the maximum unsigned /// value for the APInt's bit width. bool isMaxValue() const { return isAllOnesValue(); } /// Determine if this is the largest signed value. /// /// This checks to see if the value of this APInt is the maximum signed /// value for the APInt's bit width. bool isMaxSignedValue() const { if (isSingleWord()) return U.VAL == ((WordType(1) << (BitWidth - 1)) - 1); return !isNegative() && countTrailingOnesSlowCase() == BitWidth - 1; } /// Determine if this is the smallest unsigned value. /// /// This checks to see if the value of this APInt is the minimum unsigned /// value for the APInt's bit width. bool isMinValue() const { return isNullValue(); } /// Determine if this is the smallest signed value. /// /// This checks to see if the value of this APInt is the minimum signed /// value for the APInt's bit width. bool isMinSignedValue() const { if (isSingleWord()) return U.VAL == (WordType(1) << (BitWidth - 1)); return isNegative() && countTrailingZerosSlowCase() == BitWidth - 1; } /// Check if this APInt has an N-bits unsigned integer value. bool isIntN(unsigned N) const { assert(N && "N == 0 ???"); return getActiveBits() <= N; } /// Check if this APInt has an N-bits signed integer value. bool isSignedIntN(unsigned N) const { assert(N && "N == 0 ???"); return getMinSignedBits() <= N; } /// Check if this APInt's value is a power of two greater than zero. /// /// \returns true if the argument APInt value is a power of two > 0. bool isPowerOf2() const { if (isSingleWord()) return isPowerOf2_64(U.VAL); return countPopulationSlowCase() == 1; } /// Check if the APInt's value is returned by getSignMask. /// /// \returns true if this is the value returned by getSignMask. bool isSignMask() const { return isMinSignedValue(); } /// Convert APInt to a boolean value. /// /// This converts the APInt to a boolean value as a test against zero. bool getBoolValue() const { return !!*this; } /// If this value is smaller than the specified limit, return it, otherwise /// return the limit value. This causes the value to saturate to the limit. uint64_t getLimitedValue(uint64_t Limit = UINT64_MAX) const { return ugt(Limit) ? Limit : getZExtValue(); } /// Check if the APInt consists of a repeated bit pattern. /// /// e.g. 0x01010101 satisfies isSplat(8). /// \param SplatSizeInBits The size of the pattern in bits. Must divide bit /// width without remainder. bool isSplat(unsigned SplatSizeInBits) const; /// \returns true if this APInt value is a sequence of \param numBits ones /// starting at the least significant bit with the remainder zero. bool isMask(unsigned numBits) const { assert(numBits != 0 && "numBits must be non-zero"); assert(numBits <= BitWidth && "numBits out of range"); if (isSingleWord()) return U.VAL == (WORDTYPE_MAX >> (APINT_BITS_PER_WORD - numBits)); unsigned Ones = countTrailingOnesSlowCase(); return (numBits == Ones) && ((Ones + countLeadingZerosSlowCase()) == BitWidth); } /// \returns true if this APInt is a non-empty sequence of ones starting at /// the least significant bit with the remainder zero. /// Ex. isMask(0x0000FFFFU) == true. bool isMask() const { if (isSingleWord()) return isMask_64(U.VAL); unsigned Ones = countTrailingOnesSlowCase(); return (Ones > 0) && ((Ones + countLeadingZerosSlowCase()) == BitWidth); } /// Return true if this APInt value contains a sequence of ones with /// the remainder zero. bool isShiftedMask() const { if (isSingleWord()) return isShiftedMask_64(U.VAL); unsigned Ones = countPopulationSlowCase(); unsigned LeadZ = countLeadingZerosSlowCase(); return (Ones + LeadZ + countTrailingZeros()) == BitWidth; } /// @} /// \name Value Generators /// @{ /// Gets maximum unsigned value of APInt for specific bit width. static APInt getMaxValue(unsigned numBits) { return getAllOnesValue(numBits); } /// Gets maximum signed value of APInt for a specific bit width. static APInt getSignedMaxValue(unsigned numBits) { APInt API = getAllOnesValue(numBits); API.clearBit(numBits - 1); return API; } /// Gets minimum unsigned value of APInt for a specific bit width. static APInt getMinValue(unsigned numBits) { return APInt(numBits, 0); } /// Gets minimum signed value of APInt for a specific bit width. static APInt getSignedMinValue(unsigned numBits) { APInt API(numBits, 0); API.setBit(numBits - 1); return API; } /// Get the SignMask for a specific bit width. /// /// This is just a wrapper function of getSignedMinValue(), and it helps code /// readability when we want to get a SignMask. static APInt getSignMask(unsigned BitWidth) { return getSignedMinValue(BitWidth); } /// Get the all-ones value. /// /// \returns the all-ones value for an APInt of the specified bit-width. static APInt getAllOnesValue(unsigned numBits) { return APInt(numBits, WORDTYPE_MAX, true); } /// Get the '0' value. /// /// \returns the '0' value for an APInt of the specified bit-width. static APInt getNullValue(unsigned numBits) { return APInt(numBits, 0); } /// Compute an APInt containing numBits highbits from this APInt. /// /// Get an APInt with the same BitWidth as this APInt, just zero mask /// the low bits and right shift to the least significant bit. /// /// \returns the high "numBits" bits of this APInt. APInt getHiBits(unsigned numBits) const; /// Compute an APInt containing numBits lowbits from this APInt. /// /// Get an APInt with the same BitWidth as this APInt, just zero mask /// the high bits. /// /// \returns the low "numBits" bits of this APInt. APInt getLoBits(unsigned numBits) const; /// Return an APInt with exactly one bit set in the result. static APInt getOneBitSet(unsigned numBits, unsigned BitNo) { APInt Res(numBits, 0); Res.setBit(BitNo); return Res; } /// Get a value with a block of bits set. /// /// Constructs an APInt value that has a contiguous range of bits set. The /// bits from loBit (inclusive) to hiBit (exclusive) will be set. All other /// bits will be zero. For example, with parameters(32, 0, 16) you would get /// 0x0000FFFF. If hiBit is less than loBit then the set bits "wrap". For /// example, with parameters (32, 28, 4), you would get 0xF000000F. /// /// \param numBits the intended bit width of the result /// \param loBit the index of the lowest bit set. /// \param hiBit the index of the highest bit set. /// /// \returns An APInt value with the requested bits set. static APInt getBitsSet(unsigned numBits, unsigned loBit, unsigned hiBit) { APInt Res(numBits, 0); Res.setBits(loBit, hiBit); return Res; } /// Get a value with upper bits starting at loBit set. /// /// Constructs an APInt value that has a contiguous range of bits set. The /// bits from loBit (inclusive) to numBits (exclusive) will be set. All other /// bits will be zero. For example, with parameters(32, 12) you would get /// 0xFFFFF000. /// /// \param numBits the intended bit width of the result /// \param loBit the index of the lowest bit to set. /// /// \returns An APInt value with the requested bits set. static APInt getBitsSetFrom(unsigned numBits, unsigned loBit) { APInt Res(numBits, 0); Res.setBitsFrom(loBit); return Res; } /// Get a value with high bits set /// /// Constructs an APInt value that has the top hiBitsSet bits set. /// /// \param numBits the bitwidth of the result /// \param hiBitsSet the number of high-order bits set in the result. static APInt getHighBitsSet(unsigned numBits, unsigned hiBitsSet) { APInt Res(numBits, 0); Res.setHighBits(hiBitsSet); return Res; } /// Get a value with low bits set /// /// Constructs an APInt value that has the bottom loBitsSet bits set. /// /// \param numBits the bitwidth of the result /// \param loBitsSet the number of low-order bits set in the result. static APInt getLowBitsSet(unsigned numBits, unsigned loBitsSet) { APInt Res(numBits, 0); Res.setLowBits(loBitsSet); return Res; } /// Return a value containing V broadcasted over NewLen bits. static APInt getSplat(unsigned NewLen, const APInt &V); /// Determine if two APInts have the same value, after zero-extending /// one of them (if needed!) to ensure that the bit-widths match. static bool isSameValue(const APInt &I1, const APInt &I2) { if (I1.getBitWidth() == I2.getBitWidth()) return I1 == I2; if (I1.getBitWidth() > I2.getBitWidth()) return I1 == I2.zext(I1.getBitWidth()); return I1.zext(I2.getBitWidth()) == I2; } /// Overload to compute a hash_code for an APInt value. friend hash_code hash_value(const APInt &Arg); /// This function returns a pointer to the internal storage of the APInt. /// This is useful for writing out the APInt in binary form without any /// conversions. const uint64_t *getRawData() const { if (isSingleWord()) return &U.VAL; return &U.pVal[0]; } /// @} /// \name Unary Operators /// @{ /// Postfix increment operator. /// /// Increments *this by 1. /// /// \returns a new APInt value representing the original value of *this. const APInt operator++(int) { APInt API(*this); ++(*this); return API; } /// Prefix increment operator. /// /// \returns *this incremented by one APInt &operator++(); /// Postfix decrement operator. /// /// Decrements *this by 1. /// /// \returns a new APInt value representing the original value of *this. const APInt operator--(int) { APInt API(*this); --(*this); return API; } /// Prefix decrement operator. /// /// \returns *this decremented by one. APInt &operator--(); /// Logical negation operator. /// /// Performs logical negation operation on this APInt. /// /// \returns true if *this is zero, false otherwise. bool operator!() const { if (isSingleWord()) return U.VAL == 0; return countLeadingZerosSlowCase() == BitWidth; } /// @} /// \name Assignment Operators /// @{ /// Copy assignment operator. /// /// \returns *this after assignment of RHS. APInt &operator=(const APInt &RHS) { // If the bitwidths are the same, we can avoid mucking with memory if (isSingleWord() && RHS.isSingleWord()) { U.VAL = RHS.U.VAL; BitWidth = RHS.BitWidth; return clearUnusedBits(); } AssignSlowCase(RHS); return *this; } /// Move assignment operator. APInt &operator=(APInt &&that) { #ifdef _MSC_VER // The MSVC std::shuffle implementation still does self-assignment. if (this == &that) return *this; #endif assert(this != &that && "Self-move not supported"); if (!isSingleWord()) delete[] U.pVal; // Use memcpy so that type based alias analysis sees both VAL and pVal // as modified. memcpy(&U, &that.U, sizeof(U)); BitWidth = that.BitWidth; that.BitWidth = 0; return *this; } /// Assignment operator. /// /// The RHS value is assigned to *this. If the significant bits in RHS exceed /// the bit width, the excess bits are truncated. If the bit width is larger /// than 64, the value is zero filled in the unspecified high order bits. /// /// \returns *this after assignment of RHS value. APInt &operator=(uint64_t RHS) { if (isSingleWord()) { U.VAL = RHS; clearUnusedBits(); } else { U.pVal[0] = RHS; memset(U.pVal+1, 0, (getNumWords() - 1) * APINT_WORD_SIZE); } return *this; } /// Bitwise AND assignment operator. /// /// Performs a bitwise AND operation on this APInt and RHS. The result is /// assigned to *this. /// /// \returns *this after ANDing with RHS. APInt &operator&=(const APInt &RHS) { assert(BitWidth == RHS.BitWidth && "Bit widths must be the same"); if (isSingleWord()) U.VAL &= RHS.U.VAL; else AndAssignSlowCase(RHS); return *this; } /// Bitwise AND assignment operator. /// /// Performs a bitwise AND operation on this APInt and RHS. RHS is /// logically zero-extended or truncated to match the bit-width of /// the LHS. APInt &operator&=(uint64_t RHS) { if (isSingleWord()) { U.VAL &= RHS; return *this; } U.pVal[0] &= RHS; memset(U.pVal+1, 0, (getNumWords() - 1) * APINT_WORD_SIZE); return *this; } /// Bitwise OR assignment operator. /// /// Performs a bitwise OR operation on this APInt and RHS. The result is /// assigned *this; /// /// \returns *this after ORing with RHS. APInt &operator|=(const APInt &RHS) { assert(BitWidth == RHS.BitWidth && "Bit widths must be the same"); if (isSingleWord()) U.VAL |= RHS.U.VAL; else OrAssignSlowCase(RHS); return *this; } /// Bitwise OR assignment operator. /// /// Performs a bitwise OR operation on this APInt and RHS. RHS is /// logically zero-extended or truncated to match the bit-width of /// the LHS. APInt &operator|=(uint64_t RHS) { if (isSingleWord()) { U.VAL |= RHS; clearUnusedBits(); } else { U.pVal[0] |= RHS; } return *this; } /// Bitwise XOR assignment operator. /// /// Performs a bitwise XOR operation on this APInt and RHS. The result is /// assigned to *this. /// /// \returns *this after XORing with RHS. APInt &operator^=(const APInt &RHS) { assert(BitWidth == RHS.BitWidth && "Bit widths must be the same"); if (isSingleWord()) U.VAL ^= RHS.U.VAL; else XorAssignSlowCase(RHS); return *this; } /// Bitwise XOR assignment operator. /// /// Performs a bitwise XOR operation on this APInt and RHS. RHS is /// logically zero-extended or truncated to match the bit-width of /// the LHS. APInt &operator^=(uint64_t RHS) { if (isSingleWord()) { U.VAL ^= RHS; clearUnusedBits(); } else { U.pVal[0] ^= RHS; } return *this; } /// Multiplication assignment operator. /// /// Multiplies this APInt by RHS and assigns the result to *this. /// /// \returns *this APInt &operator*=(const APInt &RHS); APInt &operator*=(uint64_t RHS); /// Addition assignment operator. /// /// Adds RHS to *this and assigns the result to *this. /// /// \returns *this APInt &operator+=(const APInt &RHS); APInt &operator+=(uint64_t RHS); /// Subtraction assignment operator. /// /// Subtracts RHS from *this and assigns the result to *this. /// /// \returns *this APInt &operator-=(const APInt &RHS); APInt &operator-=(uint64_t RHS); /// Left-shift assignment function. /// /// Shifts *this left by shiftAmt and assigns the result to *this. /// /// \returns *this after shifting left by ShiftAmt APInt &operator<<=(unsigned ShiftAmt) { assert(ShiftAmt <= BitWidth && "Invalid shift amount"); if (isSingleWord()) { if (ShiftAmt == BitWidth) U.VAL = 0; else U.VAL <<= ShiftAmt; return clearUnusedBits(); } shlSlowCase(ShiftAmt); return *this; } /// Left-shift assignment function. /// /// Shifts *this left by shiftAmt and assigns the result to *this. /// /// \returns *this after shifting left by ShiftAmt APInt &operator<<=(const APInt &ShiftAmt); /// @} /// \name Binary Operators /// @{ /// Multiplication operator. /// /// Multiplies this APInt by RHS and returns the result. APInt operator*(const APInt &RHS) const; /// Left logical shift operator. /// /// Shifts this APInt left by \p Bits and returns the result. APInt operator<<(unsigned Bits) const { return shl(Bits); } /// Left logical shift operator. /// /// Shifts this APInt left by \p Bits and returns the result. APInt operator<<(const APInt &Bits) const { return shl(Bits); } /// Arithmetic right-shift function. /// /// Arithmetic right-shift this APInt by shiftAmt. APInt ashr(unsigned ShiftAmt) const { APInt R(*this); R.ashrInPlace(ShiftAmt); return R; } /// Arithmetic right-shift this APInt by ShiftAmt in place. void ashrInPlace(unsigned ShiftAmt) { assert(ShiftAmt <= BitWidth && "Invalid shift amount"); if (isSingleWord()) { int64_t SExtVAL = SignExtend64(U.VAL, BitWidth); if (ShiftAmt == BitWidth) U.VAL = SExtVAL >> (APINT_BITS_PER_WORD - 1); // Fill with sign bit. else U.VAL = SExtVAL >> ShiftAmt; clearUnusedBits(); return; } ashrSlowCase(ShiftAmt); } /// Logical right-shift function. /// /// Logical right-shift this APInt by shiftAmt. APInt lshr(unsigned shiftAmt) const { APInt R(*this); R.lshrInPlace(shiftAmt); return R; } /// Logical right-shift this APInt by ShiftAmt in place. void lshrInPlace(unsigned ShiftAmt) { assert(ShiftAmt <= BitWidth && "Invalid shift amount"); if (isSingleWord()) { if (ShiftAmt == BitWidth) U.VAL = 0; else U.VAL >>= ShiftAmt; return; } lshrSlowCase(ShiftAmt); } /// Left-shift function. /// /// Left-shift this APInt by shiftAmt. APInt shl(unsigned shiftAmt) const { APInt R(*this); R <<= shiftAmt; return R; } /// Rotate left by rotateAmt. APInt rotl(unsigned rotateAmt) const; /// Rotate right by rotateAmt. APInt rotr(unsigned rotateAmt) const; /// Arithmetic right-shift function. /// /// Arithmetic right-shift this APInt by shiftAmt. APInt ashr(const APInt &ShiftAmt) const { APInt R(*this); R.ashrInPlace(ShiftAmt); return R; } /// Arithmetic right-shift this APInt by shiftAmt in place. void ashrInPlace(const APInt &shiftAmt); /// Logical right-shift function. /// /// Logical right-shift this APInt by shiftAmt. APInt lshr(const APInt &ShiftAmt) const { APInt R(*this); R.lshrInPlace(ShiftAmt); return R; } /// Logical right-shift this APInt by ShiftAmt in place. void lshrInPlace(const APInt &ShiftAmt); /// Left-shift function. /// /// Left-shift this APInt by shiftAmt. APInt shl(const APInt &ShiftAmt) const { APInt R(*this); R <<= ShiftAmt; return R; } /// Rotate left by rotateAmt. APInt rotl(const APInt &rotateAmt) const; /// Rotate right by rotateAmt. APInt rotr(const APInt &rotateAmt) const; /// Unsigned division operation. /// /// Perform an unsigned divide operation on this APInt by RHS. Both this and /// RHS are treated as unsigned quantities for purposes of this division. /// /// \returns a new APInt value containing the division result, rounded towards /// zero. APInt udiv(const APInt &RHS) const; APInt udiv(uint64_t RHS) const; /// Signed division function for APInt. /// /// Signed divide this APInt by APInt RHS. /// /// The result is rounded towards zero. APInt sdiv(const APInt &RHS) const; APInt sdiv(int64_t RHS) const; /// Unsigned remainder operation. /// /// Perform an unsigned remainder operation on this APInt with RHS being the /// divisor. Both this and RHS are treated as unsigned quantities for purposes /// of this operation. Note that this is a true remainder operation and not a /// modulo operation because the sign follows the sign of the dividend which /// is *this. /// /// \returns a new APInt value containing the remainder result APInt urem(const APInt &RHS) const; uint64_t urem(uint64_t RHS) const; /// Function for signed remainder operation. /// /// Signed remainder operation on APInt. APInt srem(const APInt &RHS) const; int64_t srem(int64_t RHS) const; /// Dual division/remainder interface. /// /// Sometimes it is convenient to divide two APInt values and obtain both the /// quotient and remainder. This function does both operations in the same /// computation making it a little more efficient. The pair of input arguments /// may overlap with the pair of output arguments. It is safe to call /// udivrem(X, Y, X, Y), for example. static void udivrem(const APInt &LHS, const APInt &RHS, APInt &Quotient, APInt &Remainder); static void udivrem(const APInt &LHS, uint64_t RHS, APInt &Quotient, uint64_t &Remainder); static void sdivrem(const APInt &LHS, const APInt &RHS, APInt &Quotient, APInt &Remainder); static void sdivrem(const APInt &LHS, int64_t RHS, APInt &Quotient, int64_t &Remainder); // Operations that return overflow indicators. APInt sadd_ov(const APInt &RHS, bool &Overflow) const; APInt uadd_ov(const APInt &RHS, bool &Overflow) const; APInt ssub_ov(const APInt &RHS, bool &Overflow) const; APInt usub_ov(const APInt &RHS, bool &Overflow) const; APInt sdiv_ov(const APInt &RHS, bool &Overflow) const; APInt smul_ov(const APInt &RHS, bool &Overflow) const; APInt umul_ov(const APInt &RHS, bool &Overflow) const; APInt sshl_ov(const APInt &Amt, bool &Overflow) const; APInt ushl_ov(const APInt &Amt, bool &Overflow) const; // Operations that saturate APInt sadd_sat(const APInt &RHS) const; APInt uadd_sat(const APInt &RHS) const; APInt ssub_sat(const APInt &RHS) const; APInt usub_sat(const APInt &RHS) const; APInt smul_sat(const APInt &RHS) const; APInt umul_sat(const APInt &RHS) const; APInt sshl_sat(const APInt &RHS) const; APInt ushl_sat(const APInt &RHS) const; /// Array-indexing support. /// /// \returns the bit value at bitPosition bool operator[](unsigned bitPosition) const { assert(bitPosition < getBitWidth() && "Bit position out of bounds!"); return (maskBit(bitPosition) & getWord(bitPosition)) != 0; } /// @} /// \name Comparison Operators /// @{ /// Equality operator. /// /// Compares this APInt with RHS for the validity of the equality /// relationship. bool operator==(const APInt &RHS) const { assert(BitWidth == RHS.BitWidth && "Comparison requires equal bit widths"); if (isSingleWord()) return U.VAL == RHS.U.VAL; return EqualSlowCase(RHS); } /// Equality operator. /// /// Compares this APInt with a uint64_t for the validity of the equality /// relationship. /// /// \returns true if *this == Val bool operator==(uint64_t Val) const { return (isSingleWord() || getActiveBits() <= 64) && getZExtValue() == Val; } /// Equality comparison. /// /// Compares this APInt with RHS for the validity of the equality /// relationship. /// /// \returns true if *this == Val bool eq(const APInt &RHS) const { return (*this) == RHS; } /// Inequality operator. /// /// Compares this APInt with RHS for the validity of the inequality /// relationship. /// /// \returns true if *this != Val bool operator!=(const APInt &RHS) const { return !((*this) == RHS); } /// Inequality operator. /// /// Compares this APInt with a uint64_t for the validity of the inequality /// relationship. /// /// \returns true if *this != Val bool operator!=(uint64_t Val) const { return !((*this) == Val); } /// Inequality comparison /// /// Compares this APInt with RHS for the validity of the inequality /// relationship. /// /// \returns true if *this != Val bool ne(const APInt &RHS) const { return !((*this) == RHS); } /// Unsigned less than comparison /// /// Regards both *this and RHS as unsigned quantities and compares them for /// the validity of the less-than relationship. /// /// \returns true if *this < RHS when both are considered unsigned. bool ult(const APInt &RHS) const { return compare(RHS) < 0; } /// Unsigned less than comparison /// /// Regards both *this as an unsigned quantity and compares it with RHS for /// the validity of the less-than relationship. /// /// \returns true if *this < RHS when considered unsigned. bool ult(uint64_t RHS) const { // Only need to check active bits if not a single word. return (isSingleWord() || getActiveBits() <= 64) && getZExtValue() < RHS; } /// Signed less than comparison /// /// Regards both *this and RHS as signed quantities and compares them for /// validity of the less-than relationship. /// /// \returns true if *this < RHS when both are considered signed. bool slt(const APInt &RHS) const { return compareSigned(RHS) < 0; } /// Signed less than comparison /// /// Regards both *this as a signed quantity and compares it with RHS for /// the validity of the less-than relationship. /// /// \returns true if *this < RHS when considered signed. bool slt(int64_t RHS) const { return (!isSingleWord() && getMinSignedBits() > 64) ? isNegative() : getSExtValue() < RHS; } /// Unsigned less or equal comparison /// /// Regards both *this and RHS as unsigned quantities and compares them for /// validity of the less-or-equal relationship. /// /// \returns true if *this <= RHS when both are considered unsigned. bool ule(const APInt &RHS) const { return compare(RHS) <= 0; } /// Unsigned less or equal comparison /// /// Regards both *this as an unsigned quantity and compares it with RHS for /// the validity of the less-or-equal relationship. /// /// \returns true if *this <= RHS when considered unsigned. bool ule(uint64_t RHS) const { return !ugt(RHS); } /// Signed less or equal comparison /// /// Regards both *this and RHS as signed quantities and compares them for /// validity of the less-or-equal relationship. /// /// \returns true if *this <= RHS when both are considered signed. bool sle(const APInt &RHS) const { return compareSigned(RHS) <= 0; } /// Signed less or equal comparison /// /// Regards both *this as a signed quantity and compares it with RHS for the /// validity of the less-or-equal relationship. /// /// \returns true if *this <= RHS when considered signed. bool sle(uint64_t RHS) const { return !sgt(RHS); } /// Unsigned greater than comparison /// /// Regards both *this and RHS as unsigned quantities and compares them for /// the validity of the greater-than relationship. /// /// \returns true if *this > RHS when both are considered unsigned. bool ugt(const APInt &RHS) const { return !ule(RHS); } /// Unsigned greater than comparison /// /// Regards both *this as an unsigned quantity and compares it with RHS for /// the validity of the greater-than relationship. /// /// \returns true if *this > RHS when considered unsigned. bool ugt(uint64_t RHS) const { // Only need to check active bits if not a single word. return (!isSingleWord() && getActiveBits() > 64) || getZExtValue() > RHS; } /// Signed greater than comparison /// /// Regards both *this and RHS as signed quantities and compares them for the /// validity of the greater-than relationship. /// /// \returns true if *this > RHS when both are considered signed. bool sgt(const APInt &RHS) const { return !sle(RHS); } /// Signed greater than comparison /// /// Regards both *this as a signed quantity and compares it with RHS for /// the validity of the greater-than relationship. /// /// \returns true if *this > RHS when considered signed. bool sgt(int64_t RHS) const { return (!isSingleWord() && getMinSignedBits() > 64) ? !isNegative() : getSExtValue() > RHS; } /// Unsigned greater or equal comparison /// /// Regards both *this and RHS as unsigned quantities and compares them for /// validity of the greater-or-equal relationship. /// /// \returns true if *this >= RHS when both are considered unsigned. bool uge(const APInt &RHS) const { return !ult(RHS); } /// Unsigned greater or equal comparison /// /// Regards both *this as an unsigned quantity and compares it with RHS for /// the validity of the greater-or-equal relationship. /// /// \returns true if *this >= RHS when considered unsigned. bool uge(uint64_t RHS) const { return !ult(RHS); } /// Signed greater or equal comparison /// /// Regards both *this and RHS as signed quantities and compares them for /// validity of the greater-or-equal relationship. /// /// \returns true if *this >= RHS when both are considered signed. bool sge(const APInt &RHS) const { return !slt(RHS); } /// Signed greater or equal comparison /// /// Regards both *this as a signed quantity and compares it with RHS for /// the validity of the greater-or-equal relationship. /// /// \returns true if *this >= RHS when considered signed. bool sge(int64_t RHS) const { return !slt(RHS); } /// This operation tests if there are any pairs of corresponding bits /// between this APInt and RHS that are both set. bool intersects(const APInt &RHS) const { assert(BitWidth == RHS.BitWidth && "Bit widths must be the same"); if (isSingleWord()) return (U.VAL & RHS.U.VAL) != 0; return intersectsSlowCase(RHS); } /// This operation checks that all bits set in this APInt are also set in RHS. bool isSubsetOf(const APInt &RHS) const { assert(BitWidth == RHS.BitWidth && "Bit widths must be the same"); if (isSingleWord()) return (U.VAL & ~RHS.U.VAL) == 0; return isSubsetOfSlowCase(RHS); } /// @} /// \name Resizing Operators /// @{ /// Truncate to new width. /// /// Truncate the APInt to a specified width. It is an error to specify a width /// that is greater than or equal to the current width. APInt trunc(unsigned width) const; /// Truncate to new width with unsigned saturation. /// /// If the APInt, treated as unsigned integer, can be losslessly truncated to /// the new bitwidth, then return truncated APInt. Else, return max value. APInt truncUSat(unsigned width) const; /// Truncate to new width with signed saturation. /// /// If this APInt, treated as signed integer, can be losslessly truncated to /// the new bitwidth, then return truncated APInt. Else, return either /// signed min value if the APInt was negative, or signed max value. APInt truncSSat(unsigned width) const; /// Sign extend to a new width. /// /// This operation sign extends the APInt to a new width. If the high order /// bit is set, the fill on the left will be done with 1 bits, otherwise zero. /// It is an error to specify a width that is less than or equal to the /// current width. APInt sext(unsigned width) const; /// Zero extend to a new width. /// /// This operation zero extends the APInt to a new width. The high order bits /// are filled with 0 bits. It is an error to specify a width that is less /// than or equal to the current width. APInt zext(unsigned width) const; /// Sign extend or truncate to width /// /// Make this APInt have the bit width given by \p width. The value is sign /// extended, truncated, or left alone to make it that width. APInt sextOrTrunc(unsigned width) const; /// Zero extend or truncate to width /// /// Make this APInt have the bit width given by \p width. The value is zero /// extended, truncated, or left alone to make it that width. APInt zextOrTrunc(unsigned width) const; /// Sign extend or truncate to width /// /// Make this APInt have the bit width given by \p width. The value is sign /// extended, or left alone to make it that width. APInt sextOrSelf(unsigned width) const; /// Zero extend or truncate to width /// /// Make this APInt have the bit width given by \p width. The value is zero /// extended, or left alone to make it that width. APInt zextOrSelf(unsigned width) const; /// @} /// \name Bit Manipulation Operators /// @{ /// Set every bit to 1. void setAllBits() { if (isSingleWord()) U.VAL = WORDTYPE_MAX; else // Set all the bits in all the words. memset(U.pVal, -1, getNumWords() * APINT_WORD_SIZE); // Clear the unused ones clearUnusedBits(); } /// Set a given bit to 1. /// /// Set the given bit to 1 whose position is given as "bitPosition". void setBit(unsigned BitPosition) { assert(BitPosition < BitWidth && "BitPosition out of range"); WordType Mask = maskBit(BitPosition); if (isSingleWord()) U.VAL |= Mask; else U.pVal[whichWord(BitPosition)] |= Mask; } /// Set the sign bit to 1. void setSignBit() { setBit(BitWidth - 1); } /// Set the bits from loBit (inclusive) to hiBit (exclusive) to 1. void setBits(unsigned loBit, unsigned hiBit) { assert(hiBit <= BitWidth && "hiBit out of range"); assert(loBit <= BitWidth && "loBit out of range"); assert(loBit <= hiBit && "loBit greater than hiBit"); if (loBit == hiBit) return; if (loBit < APINT_BITS_PER_WORD && hiBit <= APINT_BITS_PER_WORD) { uint64_t mask = WORDTYPE_MAX >> (APINT_BITS_PER_WORD - (hiBit - loBit)); mask <<= loBit; if (isSingleWord()) U.VAL |= mask; else U.pVal[0] |= mask; } else { setBitsSlowCase(loBit, hiBit); } } /// Set the top bits starting from loBit. void setBitsFrom(unsigned loBit) { return setBits(loBit, BitWidth); } /// Set the bottom loBits bits. void setLowBits(unsigned loBits) { return setBits(0, loBits); } /// Set the top hiBits bits. void setHighBits(unsigned hiBits) { return setBits(BitWidth - hiBits, BitWidth); } /// Set every bit to 0. void clearAllBits() { if (isSingleWord()) U.VAL = 0; else memset(U.pVal, 0, getNumWords() * APINT_WORD_SIZE); } /// Set a given bit to 0. /// /// Set the given bit to 0 whose position is given as "bitPosition". void clearBit(unsigned BitPosition) { assert(BitPosition < BitWidth && "BitPosition out of range"); WordType Mask = ~maskBit(BitPosition); if (isSingleWord()) U.VAL &= Mask; else U.pVal[whichWord(BitPosition)] &= Mask; } /// Set bottom loBits bits to 0. void clearLowBits(unsigned loBits) { assert(loBits <= BitWidth && "More bits than bitwidth"); APInt Keep = getHighBitsSet(BitWidth, BitWidth - loBits); *this &= Keep; } /// Set the sign bit to 0. void clearSignBit() { clearBit(BitWidth - 1); } /// Toggle every bit to its opposite value. void flipAllBits() { if (isSingleWord()) { U.VAL ^= WORDTYPE_MAX; clearUnusedBits(); } else { flipAllBitsSlowCase(); } } /// Toggles a given bit to its opposite value. /// /// Toggle a given bit to its opposite value whose position is given /// as "bitPosition". void flipBit(unsigned bitPosition); /// Negate this APInt in place. void negate() { flipAllBits(); ++(*this); } /// Insert the bits from a smaller APInt starting at bitPosition. void insertBits(const APInt &SubBits, unsigned bitPosition); void insertBits(uint64_t SubBits, unsigned bitPosition, unsigned numBits); /// Return an APInt with the extracted bits [bitPosition,bitPosition+numBits). APInt extractBits(unsigned numBits, unsigned bitPosition) const; uint64_t extractBitsAsZExtValue(unsigned numBits, unsigned bitPosition) const; /// @} /// \name Value Characterization Functions /// @{ /// Return the number of bits in the APInt. unsigned getBitWidth() const { return BitWidth; } /// Get the number of words. /// /// Here one word's bitwidth equals to that of uint64_t. /// /// \returns the number of words to hold the integer value of this APInt. unsigned getNumWords() const { return getNumWords(BitWidth); } /// Get the number of words. /// /// *NOTE* Here one word's bitwidth equals to that of uint64_t. /// /// \returns the number of words to hold the integer value with a given bit /// width. static unsigned getNumWords(unsigned BitWidth) { return ((uint64_t)BitWidth + APINT_BITS_PER_WORD - 1) / APINT_BITS_PER_WORD; } /// Compute the number of active bits in the value /// /// This function returns the number of active bits which is defined as the /// bit width minus the number of leading zeros. This is used in several /// computations to see how "wide" the value is. unsigned getActiveBits() const { return BitWidth - countLeadingZeros(); } /// Compute the number of active words in the value of this APInt. /// /// This is used in conjunction with getActiveData to extract the raw value of /// the APInt. unsigned getActiveWords() const { unsigned numActiveBits = getActiveBits(); return numActiveBits ? whichWord(numActiveBits - 1) + 1 : 1; } /// Get the minimum bit size for this signed APInt /// /// Computes the minimum bit width for this APInt while considering it to be a /// signed (and probably negative) value. If the value is not negative, this /// function returns the same value as getActiveBits()+1. Otherwise, it /// returns the smallest bit width that will retain the negative value. For /// example, -1 can be written as 0b1 or 0xFFFFFFFFFF. 0b1 is shorter and so /// for -1, this function will always return 1. unsigned getMinSignedBits() const { if (isNegative()) return BitWidth - countLeadingOnes() + 1; return getActiveBits() + 1; } /// Get zero extended value /// /// This method attempts to return the value of this APInt as a zero extended /// uint64_t. The bitwidth must be <= 64 or the value must fit within a /// uint64_t. Otherwise an assertion will result. uint64_t getZExtValue() const { if (isSingleWord()) return U.VAL; assert(getActiveBits() <= 64 && "Too many bits for uint64_t"); return U.pVal[0]; } /// Get sign extended value /// /// This method attempts to return the value of this APInt as a sign extended /// int64_t. The bit width must be <= 64 or the value must fit within an /// int64_t. Otherwise an assertion will result. int64_t getSExtValue() const { if (isSingleWord()) return SignExtend64(U.VAL, BitWidth); assert(getMinSignedBits() <= 64 && "Too many bits for int64_t"); return int64_t(U.pVal[0]); } /// Get bits required for string value. /// /// This method determines how many bits are required to hold the APInt /// equivalent of the string given by \p str. static unsigned getBitsNeeded(StringRef str, uint8_t radix); /// The APInt version of the countLeadingZeros functions in /// MathExtras.h. /// /// It counts the number of zeros from the most significant bit to the first /// one bit. /// /// \returns BitWidth if the value is zero, otherwise returns the number of /// zeros from the most significant bit to the first one bits. unsigned countLeadingZeros() const { if (isSingleWord()) { unsigned unusedBits = APINT_BITS_PER_WORD - BitWidth; return llvm::countLeadingZeros(U.VAL) - unusedBits; } return countLeadingZerosSlowCase(); } /// Count the number of leading one bits. /// /// This function is an APInt version of the countLeadingOnes /// functions in MathExtras.h. It counts the number of ones from the most /// significant bit to the first zero bit. /// /// \returns 0 if the high order bit is not set, otherwise returns the number /// of 1 bits from the most significant to the least unsigned countLeadingOnes() const { if (isSingleWord()) return llvm::countLeadingOnes(U.VAL << (APINT_BITS_PER_WORD - BitWidth)); return countLeadingOnesSlowCase(); } /// Computes the number of leading bits of this APInt that are equal to its /// sign bit. unsigned getNumSignBits() const { return isNegative() ? countLeadingOnes() : countLeadingZeros(); } /// Count the number of trailing zero bits. /// /// This function is an APInt version of the countTrailingZeros /// functions in MathExtras.h. It counts the number of zeros from the least /// significant bit to the first set bit. /// /// \returns BitWidth if the value is zero, otherwise returns the number of /// zeros from the least significant bit to the first one bit. unsigned countTrailingZeros() const { if (isSingleWord()) return std::min(unsigned(llvm::countTrailingZeros(U.VAL)), BitWidth); return countTrailingZerosSlowCase(); } /// Count the number of trailing one bits. /// /// This function is an APInt version of the countTrailingOnes /// functions in MathExtras.h. It counts the number of ones from the least /// significant bit to the first zero bit. /// /// \returns BitWidth if the value is all ones, otherwise returns the number /// of ones from the least significant bit to the first zero bit. unsigned countTrailingOnes() const { if (isSingleWord()) return llvm::countTrailingOnes(U.VAL); return countTrailingOnesSlowCase(); } /// Count the number of bits set. /// /// This function is an APInt version of the countPopulation functions /// in MathExtras.h. It counts the number of 1 bits in the APInt value. /// /// \returns 0 if the value is zero, otherwise returns the number of set bits. unsigned countPopulation() const { if (isSingleWord()) return llvm::countPopulation(U.VAL); return countPopulationSlowCase(); } /// @} /// \name Conversion Functions /// @{ void print(raw_ostream &OS, bool isSigned) const; /// Converts an APInt to a string and append it to Str. Str is commonly a /// SmallString. void toString(SmallVectorImpl &Str, unsigned Radix, bool Signed, bool formatAsCLiteral = false) const; /// Considers the APInt to be unsigned and converts it into a string in the /// radix given. The radix can be 2, 8, 10 16, or 36. void toStringUnsigned(SmallVectorImpl &Str, unsigned Radix = 10) const { toString(Str, Radix, false, false); } /// Considers the APInt to be signed and converts it into a string in the /// radix given. The radix can be 2, 8, 10, 16, or 36. void toStringSigned(SmallVectorImpl &Str, unsigned Radix = 10) const { toString(Str, Radix, true, false); } /// Return the APInt as a std::string. /// /// Note that this is an inefficient method. It is better to pass in a /// SmallVector/SmallString to the methods above to avoid thrashing the heap /// for the string. std::string toString(unsigned Radix, bool Signed) const; /// \returns a byte-swapped representation of this APInt Value. APInt byteSwap() const; /// \returns the value with the bit representation reversed of this APInt /// Value. APInt reverseBits() const; /// Converts this APInt to a double value. double roundToDouble(bool isSigned) const; /// Converts this unsigned APInt to a double value. double roundToDouble() const { return roundToDouble(false); } /// Converts this signed APInt to a double value. double signedRoundToDouble() const { return roundToDouble(true); } /// Converts APInt bits to a double /// /// The conversion does not do a translation from integer to double, it just /// re-interprets the bits as a double. Note that it is valid to do this on /// any bit width. Exactly 64 bits will be translated. double bitsToDouble() const { return BitsToDouble(getWord(0)); } /// Converts APInt bits to a float /// /// The conversion does not do a translation from integer to float, it just /// re-interprets the bits as a float. Note that it is valid to do this on /// any bit width. Exactly 32 bits will be translated. float bitsToFloat() const { return BitsToFloat(static_cast(getWord(0))); } /// Converts a double to APInt bits. /// /// The conversion does not do a translation from double to integer, it just /// re-interprets the bits of the double. static APInt doubleToBits(double V) { return APInt(sizeof(double) * CHAR_BIT, DoubleToBits(V)); } /// Converts a float to APInt bits. /// /// The conversion does not do a translation from float to integer, it just /// re-interprets the bits of the float. static APInt floatToBits(float V) { return APInt(sizeof(float) * CHAR_BIT, FloatToBits(V)); } /// @} /// \name Mathematics Operations /// @{ /// \returns the floor log base 2 of this APInt. unsigned logBase2() const { return getActiveBits() - 1; } /// \returns the ceil log base 2 of this APInt. unsigned ceilLogBase2() const { APInt temp(*this); --temp; return temp.getActiveBits(); } /// \returns the nearest log base 2 of this APInt. Ties round up. /// /// NOTE: When we have a BitWidth of 1, we define: /// /// log2(0) = UINT32_MAX /// log2(1) = 0 /// /// to get around any mathematical concerns resulting from /// referencing 2 in a space where 2 does no exist. unsigned nearestLogBase2() const { // Special case when we have a bitwidth of 1. If VAL is 1, then we // get 0. If VAL is 0, we get WORDTYPE_MAX which gets truncated to // UINT32_MAX. if (BitWidth == 1) return U.VAL - 1; // Handle the zero case. if (isNullValue()) return UINT32_MAX; // The non-zero case is handled by computing: // // nearestLogBase2(x) = logBase2(x) + x[logBase2(x)-1]. // // where x[i] is referring to the value of the ith bit of x. unsigned lg = logBase2(); return lg + unsigned((*this)[lg - 1]); } /// \returns the log base 2 of this APInt if its an exact power of two, -1 /// otherwise int32_t exactLogBase2() const { if (!isPowerOf2()) return -1; return logBase2(); } /// Compute the square root APInt sqrt() const; /// Get the absolute value; /// /// If *this is < 0 then return -(*this), otherwise *this; APInt abs() const { if (isNegative()) return -(*this); return *this; } /// \returns the multiplicative inverse for a given modulo. APInt multiplicativeInverse(const APInt &modulo) const; /// @} /// \name Support for division by constant /// @{ /// Calculate the magic number for signed division by a constant. struct ms; ms magic() const; /// Calculate the magic number for unsigned division by a constant. struct mu; mu magicu(unsigned LeadingZeros = 0) const; /// @} /// \name Building-block Operations for APInt and APFloat /// @{ // These building block operations operate on a representation of arbitrary // precision, two's-complement, bignum integer values. They should be // sufficient to implement APInt and APFloat bignum requirements. Inputs are // generally a pointer to the base of an array of integer parts, representing // an unsigned bignum, and a count of how many parts there are. /// Sets the least significant part of a bignum to the input value, and zeroes /// out higher parts. static void tcSet(WordType *, WordType, unsigned); /// Assign one bignum to another. static void tcAssign(WordType *, const WordType *, unsigned); /// Returns true if a bignum is zero, false otherwise. static bool tcIsZero(const WordType *, unsigned); /// Extract the given bit of a bignum; returns 0 or 1. Zero-based. static int tcExtractBit(const WordType *, unsigned bit); /// Copy the bit vector of width srcBITS from SRC, starting at bit srcLSB, to /// DST, of dstCOUNT parts, such that the bit srcLSB becomes the least /// significant bit of DST. All high bits above srcBITS in DST are /// zero-filled. static void tcExtract(WordType *, unsigned dstCount, const WordType *, unsigned srcBits, unsigned srcLSB); /// Set the given bit of a bignum. Zero-based. static void tcSetBit(WordType *, unsigned bit); /// Clear the given bit of a bignum. Zero-based. static void tcClearBit(WordType *, unsigned bit); /// Returns the bit number of the least or most significant set bit of a /// number. If the input number has no bits set -1U is returned. static unsigned tcLSB(const WordType *, unsigned n); static unsigned tcMSB(const WordType *parts, unsigned n); /// Negate a bignum in-place. static void tcNegate(WordType *, unsigned); /// DST += RHS + CARRY where CARRY is zero or one. Returns the carry flag. static WordType tcAdd(WordType *, const WordType *, WordType carry, unsigned); /// DST += RHS. Returns the carry flag. static WordType tcAddPart(WordType *, WordType, unsigned); /// DST -= RHS + CARRY where CARRY is zero or one. Returns the carry flag. static WordType tcSubtract(WordType *, const WordType *, WordType carry, unsigned); /// DST -= RHS. Returns the carry flag. static WordType tcSubtractPart(WordType *, WordType, unsigned); /// DST += SRC * MULTIPLIER + PART if add is true /// DST = SRC * MULTIPLIER + PART if add is false /// /// Requires 0 <= DSTPARTS <= SRCPARTS + 1. If DST overlaps SRC they must /// start at the same point, i.e. DST == SRC. /// /// If DSTPARTS == SRC_PARTS + 1 no overflow occurs and zero is returned. /// Otherwise DST is filled with the least significant DSTPARTS parts of the /// result, and if all of the omitted higher parts were zero return zero, /// otherwise overflow occurred and return one. static int tcMultiplyPart(WordType *dst, const WordType *src, WordType multiplier, WordType carry, unsigned srcParts, unsigned dstParts, bool add); /// DST = LHS * RHS, where DST has the same width as the operands and is /// filled with the least significant parts of the result. Returns one if /// overflow occurred, otherwise zero. DST must be disjoint from both /// operands. static int tcMultiply(WordType *, const WordType *, const WordType *, unsigned); /// DST = LHS * RHS, where DST has width the sum of the widths of the /// operands. No overflow occurs. DST must be disjoint from both operands. static void tcFullMultiply(WordType *, const WordType *, const WordType *, unsigned, unsigned); /// If RHS is zero LHS and REMAINDER are left unchanged, return one. /// Otherwise set LHS to LHS / RHS with the fractional part discarded, set /// REMAINDER to the remainder, return zero. i.e. /// /// OLD_LHS = RHS * LHS + REMAINDER /// /// SCRATCH is a bignum of the same size as the operands and result for use by /// the routine; its contents need not be initialized and are destroyed. LHS, /// REMAINDER and SCRATCH must be distinct. static int tcDivide(WordType *lhs, const WordType *rhs, WordType *remainder, WordType *scratch, unsigned parts); /// Shift a bignum left Count bits. Shifted in bits are zero. There are no /// restrictions on Count. static void tcShiftLeft(WordType *, unsigned Words, unsigned Count); /// Shift a bignum right Count bits. Shifted in bits are zero. There are no /// restrictions on Count. static void tcShiftRight(WordType *, unsigned Words, unsigned Count); /// The obvious AND, OR and XOR and complement operations. static void tcAnd(WordType *, const WordType *, unsigned); static void tcOr(WordType *, const WordType *, unsigned); static void tcXor(WordType *, const WordType *, unsigned); static void tcComplement(WordType *, unsigned); /// Comparison (unsigned) of two bignums. static int tcCompare(const WordType *, const WordType *, unsigned); /// Increment a bignum in-place. Return the carry flag. static WordType tcIncrement(WordType *dst, unsigned parts) { return tcAddPart(dst, 1, parts); } /// Decrement a bignum in-place. Return the borrow flag. static WordType tcDecrement(WordType *dst, unsigned parts) { return tcSubtractPart(dst, 1, parts); } /// Set the least significant BITS and clear the rest. static void tcSetLeastSignificantBits(WordType *, unsigned, unsigned bits); /// debug method void dump() const; /// @} }; /// Magic data for optimising signed division by a constant. struct APInt::ms { APInt m; ///< magic number unsigned s; ///< shift amount }; /// Magic data for optimising unsigned division by a constant. struct APInt::mu { APInt m; ///< magic number bool a; ///< add indicator unsigned s; ///< shift amount }; inline bool operator==(uint64_t V1, const APInt &V2) { return V2 == V1; } inline bool operator!=(uint64_t V1, const APInt &V2) { return V2 != V1; } /// Unary bitwise complement operator. /// /// \returns an APInt that is the bitwise complement of \p v. inline APInt operator~(APInt v) { v.flipAllBits(); return v; } inline APInt operator&(APInt a, const APInt &b) { a &= b; return a; } inline APInt operator&(const APInt &a, APInt &&b) { b &= a; return std::move(b); } inline APInt operator&(APInt a, uint64_t RHS) { a &= RHS; return a; } inline APInt operator&(uint64_t LHS, APInt b) { b &= LHS; return b; } inline APInt operator|(APInt a, const APInt &b) { a |= b; return a; } inline APInt operator|(const APInt &a, APInt &&b) { b |= a; return std::move(b); } inline APInt operator|(APInt a, uint64_t RHS) { a |= RHS; return a; } inline APInt operator|(uint64_t LHS, APInt b) { b |= LHS; return b; } inline APInt operator^(APInt a, const APInt &b) { a ^= b; return a; } inline APInt operator^(const APInt &a, APInt &&b) { b ^= a; return std::move(b); } inline APInt operator^(APInt a, uint64_t RHS) { a ^= RHS; return a; } inline APInt operator^(uint64_t LHS, APInt b) { b ^= LHS; return b; } inline raw_ostream &operator<<(raw_ostream &OS, const APInt &I) { I.print(OS, true); return OS; } inline APInt operator-(APInt v) { v.negate(); return v; } inline APInt operator+(APInt a, const APInt &b) { a += b; return a; } inline APInt operator+(const APInt &a, APInt &&b) { b += a; return std::move(b); } inline APInt operator+(APInt a, uint64_t RHS) { a += RHS; return a; } inline APInt operator+(uint64_t LHS, APInt b) { b += LHS; return b; } inline APInt operator-(APInt a, const APInt &b) { a -= b; return a; } inline APInt operator-(const APInt &a, APInt &&b) { b.negate(); b += a; return std::move(b); } inline APInt operator-(APInt a, uint64_t RHS) { a -= RHS; return a; } inline APInt operator-(uint64_t LHS, APInt b) { b.negate(); b += LHS; return b; } inline APInt operator*(APInt a, uint64_t RHS) { a *= RHS; return a; } inline APInt operator*(uint64_t LHS, APInt b) { b *= LHS; return b; } namespace APIntOps { /// Determine the smaller of two APInts considered to be signed. inline const APInt &smin(const APInt &A, const APInt &B) { return A.slt(B) ? A : B; } /// Determine the larger of two APInts considered to be signed. inline const APInt &smax(const APInt &A, const APInt &B) { return A.sgt(B) ? A : B; } /// Determine the smaller of two APInts considered to be signed. inline const APInt &umin(const APInt &A, const APInt &B) { return A.ult(B) ? A : B; } /// Determine the larger of two APInts considered to be unsigned. inline const APInt &umax(const APInt &A, const APInt &B) { return A.ugt(B) ? A : B; } /// Compute GCD of two unsigned APInt values. /// /// This function returns the greatest common divisor of the two APInt values /// using Stein's algorithm. /// /// \returns the greatest common divisor of A and B. APInt GreatestCommonDivisor(APInt A, APInt B); /// Converts the given APInt to a double value. /// /// Treats the APInt as an unsigned value for conversion purposes. inline double RoundAPIntToDouble(const APInt &APIVal) { return APIVal.roundToDouble(); } /// Converts the given APInt to a double value. /// /// Treats the APInt as a signed value for conversion purposes. inline double RoundSignedAPIntToDouble(const APInt &APIVal) { return APIVal.signedRoundToDouble(); } /// Converts the given APInt to a float vlalue. inline float RoundAPIntToFloat(const APInt &APIVal) { return float(RoundAPIntToDouble(APIVal)); } /// Converts the given APInt to a float value. /// /// Treats the APInt as a signed value for conversion purposes. inline float RoundSignedAPIntToFloat(const APInt &APIVal) { return float(APIVal.signedRoundToDouble()); } /// Converts the given double value into a APInt. /// /// This function convert a double value to an APInt value. APInt RoundDoubleToAPInt(double Double, unsigned width); /// Converts a float value into a APInt. /// /// Converts a float value into an APInt value. inline APInt RoundFloatToAPInt(float Float, unsigned width) { return RoundDoubleToAPInt(double(Float), width); } /// Return A unsign-divided by B, rounded by the given rounding mode. APInt RoundingUDiv(const APInt &A, const APInt &B, APInt::Rounding RM); /// Return A sign-divided by B, rounded by the given rounding mode. APInt RoundingSDiv(const APInt &A, const APInt &B, APInt::Rounding RM); /// Let q(n) = An^2 + Bn + C, and BW = bit width of the value range /// (e.g. 32 for i32). /// This function finds the smallest number n, such that /// (a) n >= 0 and q(n) = 0, or /// (b) n >= 1 and q(n-1) and q(n), when evaluated in the set of all /// integers, belong to two different intervals [Rk, Rk+R), /// where R = 2^BW, and k is an integer. /// The idea here is to find when q(n) "overflows" 2^BW, while at the /// same time "allowing" subtraction. In unsigned modulo arithmetic a /// subtraction (treated as addition of negated numbers) would always /// count as an overflow, but here we want to allow values to decrease /// and increase as long as they are within the same interval. /// Specifically, adding of two negative numbers should not cause an /// overflow (as long as the magnitude does not exceed the bit width). /// On the other hand, given a positive number, adding a negative /// number to it can give a negative result, which would cause the /// value to go from [-2^BW, 0) to [0, 2^BW). In that sense, zero is /// treated as a special case of an overflow. /// /// This function returns None if after finding k that minimizes the /// positive solution to q(n) = kR, both solutions are contained between /// two consecutive integers. /// /// There are cases where q(n) > T, and q(n+1) < T (assuming evaluation /// in arithmetic modulo 2^BW, and treating the values as signed) by the /// virtue of *signed* overflow. This function will *not* find such an n, /// however it may find a value of n satisfying the inequalities due to /// an *unsigned* overflow (if the values are treated as unsigned). /// To find a solution for a signed overflow, treat it as a problem of /// finding an unsigned overflow with a range with of BW-1. /// /// The returned value may have a different bit width from the input /// coefficients. Optional SolveQuadraticEquationWrap(APInt A, APInt B, APInt C, unsigned RangeWidth); /// Compare two values, and if they are different, return the position of the /// most significant bit that is different in the values. Optional GetMostSignificantDifferentBit(const APInt &A, const APInt &B); } // End of APIntOps namespace // See friend declaration above. This additional declaration is required in // order to compile LLVM with IBM xlC compiler. hash_code hash_value(const APInt &Arg); /// StoreIntToMemory - Fills the StoreBytes bytes of memory starting from Dst /// with the integer held in IntVal. void StoreIntToMemory(const APInt &IntVal, uint8_t *Dst, unsigned StoreBytes); /// LoadIntFromMemory - Loads the integer stored in the LoadBytes bytes starting /// from Src into IntVal, which is assumed to be wide enough and to hold zero. void LoadIntFromMemory(APInt &IntVal, uint8_t *Src, unsigned LoadBytes); } // namespace llvm #endif binaryen-version_91/third_party/llvm-project/include/llvm/ADT/APSInt.h000066400000000000000000000274001362402614000261050ustar00rootroot00000000000000//===-- llvm/ADT/APSInt.h - Arbitrary Precision Signed Int -----*- C++ -*--===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file implements the APSInt class, which is a simple class that // represents an arbitrary sized integer that knows its signedness. // //===----------------------------------------------------------------------===// #ifndef LLVM_ADT_APSINT_H #define LLVM_ADT_APSINT_H #include "llvm/ADT/APInt.h" namespace llvm { class LLVM_NODISCARD APSInt : public APInt { bool IsUnsigned; public: /// Default constructor that creates an uninitialized APInt. explicit APSInt() : IsUnsigned(false) {} /// APSInt ctor - Create an APSInt with the specified width, default to /// unsigned. explicit APSInt(uint32_t BitWidth, bool isUnsigned = true) : APInt(BitWidth, 0), IsUnsigned(isUnsigned) {} explicit APSInt(APInt I, bool isUnsigned = true) : APInt(std::move(I)), IsUnsigned(isUnsigned) {} /// Construct an APSInt from a string representation. /// /// This constructor interprets the string \p Str using the radix of 10. /// The interpretation stops at the end of the string. The bit width of the /// constructed APSInt is determined automatically. /// /// \param Str the string to be interpreted. explicit APSInt(StringRef Str); /// Determine sign of this APSInt. /// /// \returns true if this APSInt is negative, false otherwise bool isNegative() const { return isSigned() && APInt::isNegative(); } /// Determine if this APSInt Value is non-negative (>= 0) /// /// \returns true if this APSInt is non-negative, false otherwise bool isNonNegative() const { return !isNegative(); } /// Determine if this APSInt Value is positive. /// /// This tests if the value of this APSInt is positive (> 0). Note /// that 0 is not a positive value. /// /// \returns true if this APSInt is positive. bool isStrictlyPositive() const { return isNonNegative() && !isNullValue(); } APSInt &operator=(APInt RHS) { // Retain our current sign. APInt::operator=(std::move(RHS)); return *this; } APSInt &operator=(uint64_t RHS) { // Retain our current sign. APInt::operator=(RHS); return *this; } // Query sign information. bool isSigned() const { return !IsUnsigned; } bool isUnsigned() const { return IsUnsigned; } void setIsUnsigned(bool Val) { IsUnsigned = Val; } void setIsSigned(bool Val) { IsUnsigned = !Val; } /// toString - Append this APSInt to the specified SmallString. void toString(SmallVectorImpl &Str, unsigned Radix = 10) const { APInt::toString(Str, Radix, isSigned()); } /// toString - Converts an APInt to a std::string. This is an inefficient /// method; you should prefer passing in a SmallString instead. std::string toString(unsigned Radix) const { return APInt::toString(Radix, isSigned()); } using APInt::toString; /// Get the correctly-extended \c int64_t value. int64_t getExtValue() const { assert(getMinSignedBits() <= 64 && "Too many bits for int64_t"); return isSigned() ? getSExtValue() : getZExtValue(); } APSInt trunc(uint32_t width) const { return APSInt(APInt::trunc(width), IsUnsigned); } APSInt extend(uint32_t width) const { if (IsUnsigned) return APSInt(zext(width), IsUnsigned); else return APSInt(sext(width), IsUnsigned); } APSInt extOrTrunc(uint32_t width) const { if (IsUnsigned) return APSInt(zextOrTrunc(width), IsUnsigned); else return APSInt(sextOrTrunc(width), IsUnsigned); } const APSInt &operator%=(const APSInt &RHS) { assert(IsUnsigned == RHS.IsUnsigned && "Signedness mismatch!"); if (IsUnsigned) *this = urem(RHS); else *this = srem(RHS); return *this; } const APSInt &operator/=(const APSInt &RHS) { assert(IsUnsigned == RHS.IsUnsigned && "Signedness mismatch!"); if (IsUnsigned) *this = udiv(RHS); else *this = sdiv(RHS); return *this; } APSInt operator%(const APSInt &RHS) const { assert(IsUnsigned == RHS.IsUnsigned && "Signedness mismatch!"); return IsUnsigned ? APSInt(urem(RHS), true) : APSInt(srem(RHS), false); } APSInt operator/(const APSInt &RHS) const { assert(IsUnsigned == RHS.IsUnsigned && "Signedness mismatch!"); return IsUnsigned ? APSInt(udiv(RHS), true) : APSInt(sdiv(RHS), false); } APSInt operator>>(unsigned Amt) const { return IsUnsigned ? APSInt(lshr(Amt), true) : APSInt(ashr(Amt), false); } APSInt& operator>>=(unsigned Amt) { if (IsUnsigned) lshrInPlace(Amt); else ashrInPlace(Amt); return *this; } inline bool operator<(const APSInt& RHS) const { assert(IsUnsigned == RHS.IsUnsigned && "Signedness mismatch!"); return IsUnsigned ? ult(RHS) : slt(RHS); } inline bool operator>(const APSInt& RHS) const { assert(IsUnsigned == RHS.IsUnsigned && "Signedness mismatch!"); return IsUnsigned ? ugt(RHS) : sgt(RHS); } inline bool operator<=(const APSInt& RHS) const { assert(IsUnsigned == RHS.IsUnsigned && "Signedness mismatch!"); return IsUnsigned ? ule(RHS) : sle(RHS); } inline bool operator>=(const APSInt& RHS) const { assert(IsUnsigned == RHS.IsUnsigned && "Signedness mismatch!"); return IsUnsigned ? uge(RHS) : sge(RHS); } inline bool operator==(const APSInt& RHS) const { assert(IsUnsigned == RHS.IsUnsigned && "Signedness mismatch!"); return eq(RHS); } inline bool operator!=(const APSInt& RHS) const { return !((*this) == RHS); } bool operator==(int64_t RHS) const { return compareValues(*this, get(RHS)) == 0; } bool operator!=(int64_t RHS) const { return compareValues(*this, get(RHS)) != 0; } bool operator<=(int64_t RHS) const { return compareValues(*this, get(RHS)) <= 0; } bool operator>=(int64_t RHS) const { return compareValues(*this, get(RHS)) >= 0; } bool operator<(int64_t RHS) const { return compareValues(*this, get(RHS)) < 0; } bool operator>(int64_t RHS) const { return compareValues(*this, get(RHS)) > 0; } // The remaining operators just wrap the logic of APInt, but retain the // signedness information. APSInt operator<<(unsigned Bits) const { return APSInt(static_cast(*this) << Bits, IsUnsigned); } APSInt& operator<<=(unsigned Amt) { static_cast(*this) <<= Amt; return *this; } APSInt& operator++() { ++(static_cast(*this)); return *this; } APSInt& operator--() { --(static_cast(*this)); return *this; } APSInt operator++(int) { return APSInt(++static_cast(*this), IsUnsigned); } APSInt operator--(int) { return APSInt(--static_cast(*this), IsUnsigned); } APSInt operator-() const { return APSInt(-static_cast(*this), IsUnsigned); } APSInt& operator+=(const APSInt& RHS) { assert(IsUnsigned == RHS.IsUnsigned && "Signedness mismatch!"); static_cast(*this) += RHS; return *this; } APSInt& operator-=(const APSInt& RHS) { assert(IsUnsigned == RHS.IsUnsigned && "Signedness mismatch!"); static_cast(*this) -= RHS; return *this; } APSInt& operator*=(const APSInt& RHS) { assert(IsUnsigned == RHS.IsUnsigned && "Signedness mismatch!"); static_cast(*this) *= RHS; return *this; } APSInt& operator&=(const APSInt& RHS) { assert(IsUnsigned == RHS.IsUnsigned && "Signedness mismatch!"); static_cast(*this) &= RHS; return *this; } APSInt& operator|=(const APSInt& RHS) { assert(IsUnsigned == RHS.IsUnsigned && "Signedness mismatch!"); static_cast(*this) |= RHS; return *this; } APSInt& operator^=(const APSInt& RHS) { assert(IsUnsigned == RHS.IsUnsigned && "Signedness mismatch!"); static_cast(*this) ^= RHS; return *this; } APSInt operator&(const APSInt& RHS) const { assert(IsUnsigned == RHS.IsUnsigned && "Signedness mismatch!"); return APSInt(static_cast(*this) & RHS, IsUnsigned); } APSInt operator|(const APSInt& RHS) const { assert(IsUnsigned == RHS.IsUnsigned && "Signedness mismatch!"); return APSInt(static_cast(*this) | RHS, IsUnsigned); } APSInt operator^(const APSInt &RHS) const { assert(IsUnsigned == RHS.IsUnsigned && "Signedness mismatch!"); return APSInt(static_cast(*this) ^ RHS, IsUnsigned); } APSInt operator*(const APSInt& RHS) const { assert(IsUnsigned == RHS.IsUnsigned && "Signedness mismatch!"); return APSInt(static_cast(*this) * RHS, IsUnsigned); } APSInt operator+(const APSInt& RHS) const { assert(IsUnsigned == RHS.IsUnsigned && "Signedness mismatch!"); return APSInt(static_cast(*this) + RHS, IsUnsigned); } APSInt operator-(const APSInt& RHS) const { assert(IsUnsigned == RHS.IsUnsigned && "Signedness mismatch!"); return APSInt(static_cast(*this) - RHS, IsUnsigned); } APSInt operator~() const { return APSInt(~static_cast(*this), IsUnsigned); } /// getMaxValue - Return the APSInt representing the maximum integer value /// with the given bit width and signedness. static APSInt getMaxValue(uint32_t numBits, bool Unsigned) { return APSInt(Unsigned ? APInt::getMaxValue(numBits) : APInt::getSignedMaxValue(numBits), Unsigned); } /// getMinValue - Return the APSInt representing the minimum integer value /// with the given bit width and signedness. static APSInt getMinValue(uint32_t numBits, bool Unsigned) { return APSInt(Unsigned ? APInt::getMinValue(numBits) : APInt::getSignedMinValue(numBits), Unsigned); } /// Determine if two APSInts have the same value, zero- or /// sign-extending as needed. static bool isSameValue(const APSInt &I1, const APSInt &I2) { return !compareValues(I1, I2); } /// Compare underlying values of two numbers. static int compareValues(const APSInt &I1, const APSInt &I2) { if (I1.getBitWidth() == I2.getBitWidth() && I1.isSigned() == I2.isSigned()) return I1.IsUnsigned ? I1.compare(I2) : I1.compareSigned(I2); // Check for a bit-width mismatch. if (I1.getBitWidth() > I2.getBitWidth()) return compareValues(I1, I2.extend(I1.getBitWidth())); if (I2.getBitWidth() > I1.getBitWidth()) return compareValues(I1.extend(I2.getBitWidth()), I2); // We have a signedness mismatch. Check for negative values and do an // unsigned compare if both are positive. if (I1.isSigned()) { assert(!I2.isSigned() && "Expected signed mismatch"); if (I1.isNegative()) return -1; } else { assert(I2.isSigned() && "Expected signed mismatch"); if (I2.isNegative()) return 1; } return I1.compare(I2); } static APSInt get(int64_t X) { return APSInt(APInt(64, X), false); } static APSInt getUnsigned(uint64_t X) { return APSInt(APInt(64, X), true); } /// Profile - Used to insert APSInt objects, or objects that contain APSInt /// objects, into FoldingSets. void Profile(FoldingSetNodeID& ID) const; }; inline bool operator==(int64_t V1, const APSInt &V2) { return V2 == V1; } inline bool operator!=(int64_t V1, const APSInt &V2) { return V2 != V1; } inline bool operator<=(int64_t V1, const APSInt &V2) { return V2 >= V1; } inline bool operator>=(int64_t V1, const APSInt &V2) { return V2 <= V1; } inline bool operator<(int64_t V1, const APSInt &V2) { return V2 > V1; } inline bool operator>(int64_t V1, const APSInt &V2) { return V2 < V1; } inline raw_ostream &operator<<(raw_ostream &OS, const APSInt &I) { I.print(OS, I.isSigned()); return OS; } } // end namespace llvm #endif binaryen-version_91/third_party/llvm-project/include/llvm/ADT/AllocatorList.h000066400000000000000000000170601362402614000275640ustar00rootroot00000000000000//===- llvm/ADT/AllocatorList.h - Custom allocator list ---------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #ifndef LLVM_ADT_ALLOCATORLIST_H #define LLVM_ADT_ALLOCATORLIST_H #include "llvm/ADT/ilist_node.h" #include "llvm/ADT/iterator.h" #include "llvm/ADT/simple_ilist.h" #include "llvm/Support/Allocator.h" #include #include #include #include #include #include namespace llvm { /// A linked-list with a custom, local allocator. /// /// Expose a std::list-like interface that owns and uses a custom LLVM-style /// allocator (e.g., BumpPtrAllocator), leveraging \a simple_ilist for the /// implementation details. /// /// Because this list owns the allocator, calling \a splice() with a different /// list isn't generally safe. As such, \a splice has been left out of the /// interface entirely. template class AllocatorList : AllocatorT { struct Node : ilist_node { Node(Node &&) = delete; Node(const Node &) = delete; Node &operator=(Node &&) = delete; Node &operator=(const Node &) = delete; Node(T &&V) : V(std::move(V)) {} Node(const T &V) : V(V) {} template Node(Ts &&... Vs) : V(std::forward(Vs)...) {} T V; }; using list_type = simple_ilist; list_type List; AllocatorT &getAlloc() { return *this; } const AllocatorT &getAlloc() const { return *this; } template Node *create(ArgTs &&... Args) { return new (getAlloc()) Node(std::forward(Args)...); } struct Cloner { AllocatorList &AL; Cloner(AllocatorList &AL) : AL(AL) {} Node *operator()(const Node &N) const { return AL.create(N.V); } }; struct Disposer { AllocatorList &AL; Disposer(AllocatorList &AL) : AL(AL) {} void operator()(Node *N) const { N->~Node(); AL.getAlloc().Deallocate(N); } }; public: using value_type = T; using pointer = T *; using reference = T &; using const_pointer = const T *; using const_reference = const T &; using size_type = typename list_type::size_type; using difference_type = typename list_type::difference_type; private: template class IteratorImpl : public iterator_adaptor_base, IteratorBase, std::bidirectional_iterator_tag, ValueT> { template friend class IteratorImpl; friend AllocatorList; using base_type = iterator_adaptor_base, IteratorBase, std::bidirectional_iterator_tag, ValueT>; public: using value_type = ValueT; using pointer = ValueT *; using reference = ValueT &; IteratorImpl() = default; IteratorImpl(const IteratorImpl &) = default; IteratorImpl &operator=(const IteratorImpl &) = default; explicit IteratorImpl(const IteratorBase &I) : base_type(I) {} template IteratorImpl(const IteratorImpl &X, typename std::enable_if::value>::type * = nullptr) : base_type(X.wrapped()) {} ~IteratorImpl() = default; reference operator*() const { return base_type::wrapped()->V; } pointer operator->() const { return &operator*(); } friend bool operator==(const IteratorImpl &L, const IteratorImpl &R) { return L.wrapped() == R.wrapped(); } friend bool operator!=(const IteratorImpl &L, const IteratorImpl &R) { return !(L == R); } }; public: using iterator = IteratorImpl; using reverse_iterator = IteratorImpl; using const_iterator = IteratorImpl; using const_reverse_iterator = IteratorImpl; AllocatorList() = default; AllocatorList(AllocatorList &&X) : AllocatorT(std::move(X.getAlloc())), List(std::move(X.List)) {} AllocatorList(const AllocatorList &X) { List.cloneFrom(X.List, Cloner(*this), Disposer(*this)); } AllocatorList &operator=(AllocatorList &&X) { clear(); // Dispose of current nodes explicitly. List = std::move(X.List); getAlloc() = std::move(X.getAlloc()); return *this; } AllocatorList &operator=(const AllocatorList &X) { List.cloneFrom(X.List, Cloner(*this), Disposer(*this)); return *this; } ~AllocatorList() { clear(); } void swap(AllocatorList &RHS) { List.swap(RHS.List); std::swap(getAlloc(), RHS.getAlloc()); } bool empty() { return List.empty(); } size_t size() { return List.size(); } iterator begin() { return iterator(List.begin()); } iterator end() { return iterator(List.end()); } const_iterator begin() const { return const_iterator(List.begin()); } const_iterator end() const { return const_iterator(List.end()); } reverse_iterator rbegin() { return reverse_iterator(List.rbegin()); } reverse_iterator rend() { return reverse_iterator(List.rend()); } const_reverse_iterator rbegin() const { return const_reverse_iterator(List.rbegin()); } const_reverse_iterator rend() const { return const_reverse_iterator(List.rend()); } T &back() { return List.back().V; } T &front() { return List.front().V; } const T &back() const { return List.back().V; } const T &front() const { return List.front().V; } template iterator emplace(iterator I, Ts &&... Vs) { return iterator(List.insert(I.wrapped(), *create(std::forward(Vs)...))); } iterator insert(iterator I, T &&V) { return iterator(List.insert(I.wrapped(), *create(std::move(V)))); } iterator insert(iterator I, const T &V) { return iterator(List.insert(I.wrapped(), *create(V))); } template void insert(iterator I, Iterator First, Iterator Last) { for (; First != Last; ++First) List.insert(I.wrapped(), *create(*First)); } iterator erase(iterator I) { return iterator(List.eraseAndDispose(I.wrapped(), Disposer(*this))); } iterator erase(iterator First, iterator Last) { return iterator( List.eraseAndDispose(First.wrapped(), Last.wrapped(), Disposer(*this))); } void clear() { List.clearAndDispose(Disposer(*this)); } void pop_back() { List.eraseAndDispose(--List.end(), Disposer(*this)); } void pop_front() { List.eraseAndDispose(List.begin(), Disposer(*this)); } void push_back(T &&V) { insert(end(), std::move(V)); } void push_front(T &&V) { insert(begin(), std::move(V)); } void push_back(const T &V) { insert(end(), V); } void push_front(const T &V) { insert(begin(), V); } template void emplace_back(Ts &&... Vs) { emplace(end(), std::forward(Vs)...); } template void emplace_front(Ts &&... Vs) { emplace(begin(), std::forward(Vs)...); } /// Reset the underlying allocator. /// /// \pre \c empty() void resetAlloc() { assert(empty() && "Cannot reset allocator if not empty"); getAlloc().Reset(); } }; template using BumpPtrList = AllocatorList; } // end namespace llvm #endif // LLVM_ADT_ALLOCATORLIST_H binaryen-version_91/third_party/llvm-project/include/llvm/ADT/ArrayRef.h000066400000000000000000000425141362402614000265250ustar00rootroot00000000000000//===- ArrayRef.h - Array Reference Wrapper ---------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #ifndef LLVM_ADT_ARRAYREF_H #define LLVM_ADT_ARRAYREF_H #include "llvm/ADT/Hashing.h" #include "llvm/ADT/None.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Compiler.h" #include #include #include #include #include #include #include #include #include namespace llvm { /// ArrayRef - Represent a constant reference to an array (0 or more elements /// consecutively in memory), i.e. a start pointer and a length. It allows /// various APIs to take consecutive elements easily and conveniently. /// /// This class does not own the underlying data, it is expected to be used in /// situations where the data resides in some other buffer, whose lifetime /// extends past that of the ArrayRef. For this reason, it is not in general /// safe to store an ArrayRef. /// /// This is intended to be trivially copyable, so it should be passed by /// value. template class LLVM_NODISCARD ArrayRef { public: using iterator = const T *; using const_iterator = const T *; using size_type = size_t; using reverse_iterator = std::reverse_iterator; private: /// The start of the array, in an external buffer. const T *Data = nullptr; /// The number of elements. size_type Length = 0; public: /// @name Constructors /// @{ /// Construct an empty ArrayRef. /*implicit*/ ArrayRef() = default; /// Construct an empty ArrayRef from None. /*implicit*/ ArrayRef(NoneType) {} /// Construct an ArrayRef from a single element. /*implicit*/ ArrayRef(const T &OneElt) : Data(&OneElt), Length(1) {} /// Construct an ArrayRef from a pointer and length. /*implicit*/ ArrayRef(const T *data, size_t length) : Data(data), Length(length) {} /// Construct an ArrayRef from a range. ArrayRef(const T *begin, const T *end) : Data(begin), Length(end - begin) {} /// Construct an ArrayRef from a SmallVector. This is templated in order to /// avoid instantiating SmallVectorTemplateCommon whenever we /// copy-construct an ArrayRef. template /*implicit*/ ArrayRef(const SmallVectorTemplateCommon &Vec) : Data(Vec.data()), Length(Vec.size()) { } /// Construct an ArrayRef from a std::vector. template /*implicit*/ ArrayRef(const std::vector &Vec) : Data(Vec.data()), Length(Vec.size()) {} /// Construct an ArrayRef from a std::array template /*implicit*/ constexpr ArrayRef(const std::array &Arr) : Data(Arr.data()), Length(N) {} /// Construct an ArrayRef from a C array. template /*implicit*/ constexpr ArrayRef(const T (&Arr)[N]) : Data(Arr), Length(N) {} /// Construct an ArrayRef from a std::initializer_list. /*implicit*/ ArrayRef(const std::initializer_list &Vec) : Data(Vec.begin() == Vec.end() ? (T*)nullptr : Vec.begin()), Length(Vec.size()) {} /// Construct an ArrayRef from ArrayRef. This uses SFINAE to /// ensure that only ArrayRefs of pointers can be converted. template ArrayRef( const ArrayRef &A, typename std::enable_if< std::is_convertible::value>::type * = nullptr) : Data(A.data()), Length(A.size()) {} /// Construct an ArrayRef from a SmallVector. This is /// templated in order to avoid instantiating SmallVectorTemplateCommon /// whenever we copy-construct an ArrayRef. template /*implicit*/ ArrayRef( const SmallVectorTemplateCommon &Vec, typename std::enable_if< std::is_convertible::value>::type * = nullptr) : Data(Vec.data()), Length(Vec.size()) { } /// Construct an ArrayRef from std::vector. This uses SFINAE /// to ensure that only vectors of pointers can be converted. template ArrayRef(const std::vector &Vec, typename std::enable_if< std::is_convertible::value>::type* = 0) : Data(Vec.data()), Length(Vec.size()) {} /// @} /// @name Simple Operations /// @{ iterator begin() const { return Data; } iterator end() const { return Data + Length; } reverse_iterator rbegin() const { return reverse_iterator(end()); } reverse_iterator rend() const { return reverse_iterator(begin()); } /// empty - Check if the array is empty. bool empty() const { return Length == 0; } const T *data() const { return Data; } /// size - Get the array size. size_t size() const { return Length; } /// front - Get the first element. const T &front() const { assert(!empty()); return Data[0]; } /// back - Get the last element. const T &back() const { assert(!empty()); return Data[Length-1]; } // copy - Allocate copy in Allocator and return ArrayRef to it. template ArrayRef copy(Allocator &A) { T *Buff = A.template Allocate(Length); std::uninitialized_copy(begin(), end(), Buff); return ArrayRef(Buff, Length); } /// equals - Check for element-wise equality. bool equals(ArrayRef RHS) const { if (Length != RHS.Length) return false; return std::equal(begin(), end(), RHS.begin()); } /// slice(n, m) - Chop off the first N elements of the array, and keep M /// elements in the array. ArrayRef slice(size_t N, size_t M) const { assert(N+M <= size() && "Invalid specifier"); return ArrayRef(data()+N, M); } /// slice(n) - Chop off the first N elements of the array. ArrayRef slice(size_t N) const { return slice(N, size() - N); } /// Drop the first \p N elements of the array. ArrayRef drop_front(size_t N = 1) const { assert(size() >= N && "Dropping more elements than exist"); return slice(N, size() - N); } /// Drop the last \p N elements of the array. ArrayRef drop_back(size_t N = 1) const { assert(size() >= N && "Dropping more elements than exist"); return slice(0, size() - N); } /// Return a copy of *this with the first N elements satisfying the /// given predicate removed. template ArrayRef drop_while(PredicateT Pred) const { return ArrayRef(find_if_not(*this, Pred), end()); } /// Return a copy of *this with the first N elements not satisfying /// the given predicate removed. template ArrayRef drop_until(PredicateT Pred) const { return ArrayRef(find_if(*this, Pred), end()); } /// Return a copy of *this with only the first \p N elements. ArrayRef take_front(size_t N = 1) const { if (N >= size()) return *this; return drop_back(size() - N); } /// Return a copy of *this with only the last \p N elements. ArrayRef take_back(size_t N = 1) const { if (N >= size()) return *this; return drop_front(size() - N); } /// Return the first N elements of this Array that satisfy the given /// predicate. template ArrayRef take_while(PredicateT Pred) const { return ArrayRef(begin(), find_if_not(*this, Pred)); } /// Return the first N elements of this Array that don't satisfy the /// given predicate. template ArrayRef take_until(PredicateT Pred) const { return ArrayRef(begin(), find_if(*this, Pred)); } /// @} /// @name Operator Overloads /// @{ const T &operator[](size_t Index) const { assert(Index < Length && "Invalid index!"); return Data[Index]; } /// Disallow accidental assignment from a temporary. /// /// The declaration here is extra complicated so that "arrayRef = {}" /// continues to select the move assignment operator. template typename std::enable_if::value, ArrayRef>::type & operator=(U &&Temporary) = delete; /// Disallow accidental assignment from a temporary. /// /// The declaration here is extra complicated so that "arrayRef = {}" /// continues to select the move assignment operator. template typename std::enable_if::value, ArrayRef>::type & operator=(std::initializer_list) = delete; /// @} /// @name Expensive Operations /// @{ std::vector vec() const { return std::vector(Data, Data+Length); } /// @} /// @name Conversion operators /// @{ operator std::vector() const { return std::vector(Data, Data+Length); } /// @} }; /// MutableArrayRef - Represent a mutable reference to an array (0 or more /// elements consecutively in memory), i.e. a start pointer and a length. It /// allows various APIs to take and modify consecutive elements easily and /// conveniently. /// /// This class does not own the underlying data, it is expected to be used in /// situations where the data resides in some other buffer, whose lifetime /// extends past that of the MutableArrayRef. For this reason, it is not in /// general safe to store a MutableArrayRef. /// /// This is intended to be trivially copyable, so it should be passed by /// value. template class LLVM_NODISCARD MutableArrayRef : public ArrayRef { public: using iterator = T *; using reverse_iterator = std::reverse_iterator; /// Construct an empty MutableArrayRef. /*implicit*/ MutableArrayRef() = default; /// Construct an empty MutableArrayRef from None. /*implicit*/ MutableArrayRef(NoneType) : ArrayRef() {} /// Construct an MutableArrayRef from a single element. /*implicit*/ MutableArrayRef(T &OneElt) : ArrayRef(OneElt) {} /// Construct an MutableArrayRef from a pointer and length. /*implicit*/ MutableArrayRef(T *data, size_t length) : ArrayRef(data, length) {} /// Construct an MutableArrayRef from a range. MutableArrayRef(T *begin, T *end) : ArrayRef(begin, end) {} /// Construct an MutableArrayRef from a SmallVector. /*implicit*/ MutableArrayRef(SmallVectorImpl &Vec) : ArrayRef(Vec) {} /// Construct a MutableArrayRef from a std::vector. /*implicit*/ MutableArrayRef(std::vector &Vec) : ArrayRef(Vec) {} /// Construct an ArrayRef from a std::array template /*implicit*/ constexpr MutableArrayRef(std::array &Arr) : ArrayRef(Arr) {} /// Construct an MutableArrayRef from a C array. template /*implicit*/ constexpr MutableArrayRef(T (&Arr)[N]) : ArrayRef(Arr) {} T *data() const { return const_cast(ArrayRef::data()); } iterator begin() const { return data(); } iterator end() const { return data() + this->size(); } reverse_iterator rbegin() const { return reverse_iterator(end()); } reverse_iterator rend() const { return reverse_iterator(begin()); } /// front - Get the first element. T &front() const { assert(!this->empty()); return data()[0]; } /// back - Get the last element. T &back() const { assert(!this->empty()); return data()[this->size()-1]; } /// slice(n, m) - Chop off the first N elements of the array, and keep M /// elements in the array. MutableArrayRef slice(size_t N, size_t M) const { assert(N + M <= this->size() && "Invalid specifier"); return MutableArrayRef(this->data() + N, M); } /// slice(n) - Chop off the first N elements of the array. MutableArrayRef slice(size_t N) const { return slice(N, this->size() - N); } /// Drop the first \p N elements of the array. MutableArrayRef drop_front(size_t N = 1) const { assert(this->size() >= N && "Dropping more elements than exist"); return slice(N, this->size() - N); } MutableArrayRef drop_back(size_t N = 1) const { assert(this->size() >= N && "Dropping more elements than exist"); return slice(0, this->size() - N); } /// Return a copy of *this with the first N elements satisfying the /// given predicate removed. template MutableArrayRef drop_while(PredicateT Pred) const { return MutableArrayRef(find_if_not(*this, Pred), end()); } /// Return a copy of *this with the first N elements not satisfying /// the given predicate removed. template MutableArrayRef drop_until(PredicateT Pred) const { return MutableArrayRef(find_if(*this, Pred), end()); } /// Return a copy of *this with only the first \p N elements. MutableArrayRef take_front(size_t N = 1) const { if (N >= this->size()) return *this; return drop_back(this->size() - N); } /// Return a copy of *this with only the last \p N elements. MutableArrayRef take_back(size_t N = 1) const { if (N >= this->size()) return *this; return drop_front(this->size() - N); } /// Return the first N elements of this Array that satisfy the given /// predicate. template MutableArrayRef take_while(PredicateT Pred) const { return MutableArrayRef(begin(), find_if_not(*this, Pred)); } /// Return the first N elements of this Array that don't satisfy the /// given predicate. template MutableArrayRef take_until(PredicateT Pred) const { return MutableArrayRef(begin(), find_if(*this, Pred)); } /// @} /// @name Operator Overloads /// @{ T &operator[](size_t Index) const { assert(Index < this->size() && "Invalid index!"); return data()[Index]; } }; /// This is a MutableArrayRef that owns its array. template class OwningArrayRef : public MutableArrayRef { public: OwningArrayRef() = default; OwningArrayRef(size_t Size) : MutableArrayRef(new T[Size], Size) {} OwningArrayRef(ArrayRef Data) : MutableArrayRef(new T[Data.size()], Data.size()) { std::copy(Data.begin(), Data.end(), this->begin()); } OwningArrayRef(OwningArrayRef &&Other) { *this = std::move(Other); } OwningArrayRef &operator=(OwningArrayRef &&Other) { delete[] this->data(); this->MutableArrayRef::operator=(Other); Other.MutableArrayRef::operator=(MutableArrayRef()); return *this; } ~OwningArrayRef() { delete[] this->data(); } }; /// @name ArrayRef Convenience constructors /// @{ /// Construct an ArrayRef from a single element. template ArrayRef makeArrayRef(const T &OneElt) { return OneElt; } /// Construct an ArrayRef from a pointer and length. template ArrayRef makeArrayRef(const T *data, size_t length) { return ArrayRef(data, length); } /// Construct an ArrayRef from a range. template ArrayRef makeArrayRef(const T *begin, const T *end) { return ArrayRef(begin, end); } /// Construct an ArrayRef from a SmallVector. template ArrayRef makeArrayRef(const SmallVectorImpl &Vec) { return Vec; } /// Construct an ArrayRef from a SmallVector. template ArrayRef makeArrayRef(const SmallVector &Vec) { return Vec; } /// Construct an ArrayRef from a std::vector. template ArrayRef makeArrayRef(const std::vector &Vec) { return Vec; } /// Construct an ArrayRef from a std::array. template ArrayRef makeArrayRef(const std::array &Arr) { return Arr; } /// Construct an ArrayRef from an ArrayRef (no-op) (const) template ArrayRef makeArrayRef(const ArrayRef &Vec) { return Vec; } /// Construct an ArrayRef from an ArrayRef (no-op) template ArrayRef &makeArrayRef(ArrayRef &Vec) { return Vec; } /// Construct an ArrayRef from a C array. template ArrayRef makeArrayRef(const T (&Arr)[N]) { return ArrayRef(Arr); } /// Construct a MutableArrayRef from a single element. template MutableArrayRef makeMutableArrayRef(T &OneElt) { return OneElt; } /// Construct a MutableArrayRef from a pointer and length. template MutableArrayRef makeMutableArrayRef(T *data, size_t length) { return MutableArrayRef(data, length); } /// @} /// @name ArrayRef Comparison Operators /// @{ template inline bool operator==(ArrayRef LHS, ArrayRef RHS) { return LHS.equals(RHS); } template inline bool operator!=(ArrayRef LHS, ArrayRef RHS) { return !(LHS == RHS); } /// @} template hash_code hash_value(ArrayRef S) { return hash_combine_range(S.begin(), S.end()); } } // end namespace llvm #endif // LLVM_ADT_ARRAYREF_H binaryen-version_91/third_party/llvm-project/include/llvm/ADT/BitmaskEnum.h000066400000000000000000000127171362402614000272330ustar00rootroot00000000000000//===-- llvm/ADT/BitmaskEnum.h ----------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #ifndef LLVM_ADT_BITMASKENUM_H #define LLVM_ADT_BITMASKENUM_H #include #include #include #include "llvm/Support/MathExtras.h" /// LLVM_MARK_AS_BITMASK_ENUM lets you opt in an individual enum type so you can /// perform bitwise operations on it without putting static_cast everywhere. /// /// \code /// enum MyEnum { /// E1 = 1, E2 = 2, E3 = 4, E4 = 8, /// LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ E4) /// }; /// /// void Foo() { /// MyEnum A = (E1 | E2) & E3 ^ ~E4; // Look, ma: No static_cast! /// } /// \endcode /// /// Normally when you do a bitwise operation on an enum value, you get back an /// instance of the underlying type (e.g. int). But using this macro, bitwise /// ops on your enum will return you back instances of the enum. This is /// particularly useful for enums which represent a combination of flags. /// /// The parameter to LLVM_MARK_AS_BITMASK_ENUM should be the largest individual /// value in your enum. /// /// All of the enum's values must be non-negative. #define LLVM_MARK_AS_BITMASK_ENUM(LargestValue) \ LLVM_BITMASK_LARGEST_ENUMERATOR = LargestValue /// LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE() pulls the operator overloads used /// by LLVM_MARK_AS_BITMASK_ENUM into the current namespace. /// /// Suppose you have an enum foo::bar::MyEnum. Before using /// LLVM_MARK_AS_BITMASK_ENUM on MyEnum, you must put /// LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE() somewhere inside namespace foo or /// namespace foo::bar. This allows the relevant operator overloads to be found /// by ADL. /// /// You don't need to use this macro in namespace llvm; it's done at the bottom /// of this file. #define LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE() \ using ::llvm::BitmaskEnumDetail::operator~; \ using ::llvm::BitmaskEnumDetail::operator|; \ using ::llvm::BitmaskEnumDetail::operator&; \ using ::llvm::BitmaskEnumDetail::operator^; \ using ::llvm::BitmaskEnumDetail::operator|=; \ using ::llvm::BitmaskEnumDetail::operator&=; \ /* Force a semicolon at the end of this macro. */ \ using ::llvm::BitmaskEnumDetail::operator^= namespace llvm { /// Traits class to determine whether an enum has a /// LLVM_BITMASK_LARGEST_ENUMERATOR enumerator. template struct is_bitmask_enum : std::false_type {}; template struct is_bitmask_enum< E, typename std::enable_if= 0>::type> : std::true_type {}; namespace BitmaskEnumDetail { /// Get a bitmask with 1s in all places up to the high-order bit of E's largest /// value. template typename std::underlying_type::type Mask() { // On overflow, NextPowerOf2 returns zero with the type uint64_t, so // subtracting 1 gives us the mask with all bits set, like we want. return NextPowerOf2(static_cast::type>( E::LLVM_BITMASK_LARGEST_ENUMERATOR)) - 1; } /// Check that Val is in range for E, and return Val cast to E's underlying /// type. template typename std::underlying_type::type Underlying(E Val) { auto U = static_cast::type>(Val); assert(U >= 0 && "Negative enum values are not allowed."); assert(U <= Mask() && "Enum value too large (or largest val too small?)"); return U; } template ::value>::type> E operator~(E Val) { return static_cast(~Underlying(Val) & Mask()); } template ::value>::type> E operator|(E LHS, E RHS) { return static_cast(Underlying(LHS) | Underlying(RHS)); } template ::value>::type> E operator&(E LHS, E RHS) { return static_cast(Underlying(LHS) & Underlying(RHS)); } template ::value>::type> E operator^(E LHS, E RHS) { return static_cast(Underlying(LHS) ^ Underlying(RHS)); } // |=, &=, and ^= return a reference to LHS, to match the behavior of the // operators on builtin types. template ::value>::type> E &operator|=(E &LHS, E RHS) { LHS = LHS | RHS; return LHS; } template ::value>::type> E &operator&=(E &LHS, E RHS) { LHS = LHS & RHS; return LHS; } template ::value>::type> E &operator^=(E &LHS, E RHS) { LHS = LHS ^ RHS; return LHS; } } // namespace BitmaskEnumDetail // Enable bitmask enums in namespace ::llvm and all nested namespaces. LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); } // namespace llvm #endif binaryen-version_91/third_party/llvm-project/include/llvm/ADT/DenseMap.h000066400000000000000000001220651362402614000265060ustar00rootroot00000000000000//===- llvm/ADT/DenseMap.h - Dense probed hash table ------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file defines the DenseMap class. // //===----------------------------------------------------------------------===// #ifndef LLVM_ADT_DENSEMAP_H #define LLVM_ADT_DENSEMAP_H #include "llvm/ADT/DenseMapInfo.h" #include "llvm/ADT/EpochTracker.h" #include "llvm/Support/AlignOf.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/ReverseIteration.h" #include "llvm/Support/type_traits.h" #include #include #include #include #include #include #include #include #include namespace llvm { namespace detail { // We extend a pair to allow users to override the bucket type with their own // implementation without requiring two members. template struct DenseMapPair : public std::pair { using std::pair::pair; KeyT &getFirst() { return std::pair::first; } const KeyT &getFirst() const { return std::pair::first; } ValueT &getSecond() { return std::pair::second; } const ValueT &getSecond() const { return std::pair::second; } }; } // end namespace detail template , typename Bucket = llvm::detail::DenseMapPair, bool IsConst = false> class DenseMapIterator; template class DenseMapBase : public DebugEpochBase { template using const_arg_type_t = typename const_pointer_or_const_ref::type; public: using size_type = unsigned; using key_type = KeyT; using mapped_type = ValueT; using value_type = BucketT; using iterator = DenseMapIterator; using const_iterator = DenseMapIterator; inline iterator begin() { // When the map is empty, avoid the overhead of advancing/retreating past // empty buckets. if (empty()) return end(); if (shouldReverseIterate()) return makeIterator(getBucketsEnd() - 1, getBuckets(), *this); return makeIterator(getBuckets(), getBucketsEnd(), *this); } inline iterator end() { return makeIterator(getBucketsEnd(), getBucketsEnd(), *this, true); } inline const_iterator begin() const { if (empty()) return end(); if (shouldReverseIterate()) return makeConstIterator(getBucketsEnd() - 1, getBuckets(), *this); return makeConstIterator(getBuckets(), getBucketsEnd(), *this); } inline const_iterator end() const { return makeConstIterator(getBucketsEnd(), getBucketsEnd(), *this, true); } LLVM_NODISCARD bool empty() const { return getNumEntries() == 0; } unsigned size() const { return getNumEntries(); } /// Grow the densemap so that it can contain at least \p NumEntries items /// before resizing again. void reserve(size_type NumEntries) { auto NumBuckets = getMinBucketToReserveForEntries(NumEntries); incrementEpoch(); if (NumBuckets > getNumBuckets()) grow(NumBuckets); } void clear() { incrementEpoch(); if (getNumEntries() == 0 && getNumTombstones() == 0) return; // If the capacity of the array is huge, and the # elements used is small, // shrink the array. if (getNumEntries() * 4 < getNumBuckets() && getNumBuckets() > 64) { shrink_and_clear(); return; } const KeyT EmptyKey = getEmptyKey(), TombstoneKey = getTombstoneKey(); if (is_trivially_copyable::value && is_trivially_copyable::value) { // Use a simpler loop when these are trivial types. for (BucketT *P = getBuckets(), *E = getBucketsEnd(); P != E; ++P) P->getFirst() = EmptyKey; } else { unsigned NumEntries = getNumEntries(); for (BucketT *P = getBuckets(), *E = getBucketsEnd(); P != E; ++P) { if (!KeyInfoT::isEqual(P->getFirst(), EmptyKey)) { if (!KeyInfoT::isEqual(P->getFirst(), TombstoneKey)) { P->getSecond().~ValueT(); --NumEntries; } P->getFirst() = EmptyKey; } } assert(NumEntries == 0 && "Node count imbalance!"); } setNumEntries(0); setNumTombstones(0); } /// Return 1 if the specified key is in the map, 0 otherwise. size_type count(const_arg_type_t Val) const { const BucketT *TheBucket; return LookupBucketFor(Val, TheBucket) ? 1 : 0; } iterator find(const_arg_type_t Val) { BucketT *TheBucket; if (LookupBucketFor(Val, TheBucket)) return makeIterator(TheBucket, getBucketsEnd(), *this, true); return end(); } const_iterator find(const_arg_type_t Val) const { const BucketT *TheBucket; if (LookupBucketFor(Val, TheBucket)) return makeConstIterator(TheBucket, getBucketsEnd(), *this, true); return end(); } /// Alternate version of find() which allows a different, and possibly /// less expensive, key type. /// The DenseMapInfo is responsible for supplying methods /// getHashValue(LookupKeyT) and isEqual(LookupKeyT, KeyT) for each key /// type used. template iterator find_as(const LookupKeyT &Val) { BucketT *TheBucket; if (LookupBucketFor(Val, TheBucket)) return makeIterator(TheBucket, getBucketsEnd(), *this, true); return end(); } template const_iterator find_as(const LookupKeyT &Val) const { const BucketT *TheBucket; if (LookupBucketFor(Val, TheBucket)) return makeConstIterator(TheBucket, getBucketsEnd(), *this, true); return end(); } /// lookup - Return the entry for the specified key, or a default /// constructed value if no such entry exists. ValueT lookup(const_arg_type_t Val) const { const BucketT *TheBucket; if (LookupBucketFor(Val, TheBucket)) return TheBucket->getSecond(); return ValueT(); } // Inserts key,value pair into the map if the key isn't already in the map. // If the key is already in the map, it returns false and doesn't update the // value. std::pair insert(const std::pair &KV) { return try_emplace(KV.first, KV.second); } // Inserts key,value pair into the map if the key isn't already in the map. // If the key is already in the map, it returns false and doesn't update the // value. std::pair insert(std::pair &&KV) { return try_emplace(std::move(KV.first), std::move(KV.second)); } // Inserts key,value pair into the map if the key isn't already in the map. // The value is constructed in-place if the key is not in the map, otherwise // it is not moved. template std::pair try_emplace(KeyT &&Key, Ts &&... Args) { BucketT *TheBucket; if (LookupBucketFor(Key, TheBucket)) return std::make_pair( makeIterator(TheBucket, getBucketsEnd(), *this, true), false); // Already in map. // Otherwise, insert the new element. TheBucket = InsertIntoBucket(TheBucket, std::move(Key), std::forward(Args)...); return std::make_pair( makeIterator(TheBucket, getBucketsEnd(), *this, true), true); } // Inserts key,value pair into the map if the key isn't already in the map. // The value is constructed in-place if the key is not in the map, otherwise // it is not moved. template std::pair try_emplace(const KeyT &Key, Ts &&... Args) { BucketT *TheBucket; if (LookupBucketFor(Key, TheBucket)) return std::make_pair( makeIterator(TheBucket, getBucketsEnd(), *this, true), false); // Already in map. // Otherwise, insert the new element. TheBucket = InsertIntoBucket(TheBucket, Key, std::forward(Args)...); return std::make_pair( makeIterator(TheBucket, getBucketsEnd(), *this, true), true); } /// Alternate version of insert() which allows a different, and possibly /// less expensive, key type. /// The DenseMapInfo is responsible for supplying methods /// getHashValue(LookupKeyT) and isEqual(LookupKeyT, KeyT) for each key /// type used. template std::pair insert_as(std::pair &&KV, const LookupKeyT &Val) { BucketT *TheBucket; if (LookupBucketFor(Val, TheBucket)) return std::make_pair( makeIterator(TheBucket, getBucketsEnd(), *this, true), false); // Already in map. // Otherwise, insert the new element. TheBucket = InsertIntoBucketWithLookup(TheBucket, std::move(KV.first), std::move(KV.second), Val); return std::make_pair( makeIterator(TheBucket, getBucketsEnd(), *this, true), true); } /// insert - Range insertion of pairs. template void insert(InputIt I, InputIt E) { for (; I != E; ++I) insert(*I); } bool erase(const KeyT &Val) { BucketT *TheBucket; if (!LookupBucketFor(Val, TheBucket)) return false; // not in map. TheBucket->getSecond().~ValueT(); TheBucket->getFirst() = getTombstoneKey(); decrementNumEntries(); incrementNumTombstones(); return true; } void erase(iterator I) { BucketT *TheBucket = &*I; TheBucket->getSecond().~ValueT(); TheBucket->getFirst() = getTombstoneKey(); decrementNumEntries(); incrementNumTombstones(); } value_type& FindAndConstruct(const KeyT &Key) { BucketT *TheBucket; if (LookupBucketFor(Key, TheBucket)) return *TheBucket; return *InsertIntoBucket(TheBucket, Key); } ValueT &operator[](const KeyT &Key) { return FindAndConstruct(Key).second; } value_type& FindAndConstruct(KeyT &&Key) { BucketT *TheBucket; if (LookupBucketFor(Key, TheBucket)) return *TheBucket; return *InsertIntoBucket(TheBucket, std::move(Key)); } ValueT &operator[](KeyT &&Key) { return FindAndConstruct(std::move(Key)).second; } /// isPointerIntoBucketsArray - Return true if the specified pointer points /// somewhere into the DenseMap's array of buckets (i.e. either to a key or /// value in the DenseMap). bool isPointerIntoBucketsArray(const void *Ptr) const { return Ptr >= getBuckets() && Ptr < getBucketsEnd(); } /// getPointerIntoBucketsArray() - Return an opaque pointer into the buckets /// array. In conjunction with the previous method, this can be used to /// determine whether an insertion caused the DenseMap to reallocate. const void *getPointerIntoBucketsArray() const { return getBuckets(); } protected: DenseMapBase() = default; void destroyAll() { if (getNumBuckets() == 0) // Nothing to do. return; const KeyT EmptyKey = getEmptyKey(), TombstoneKey = getTombstoneKey(); for (BucketT *P = getBuckets(), *E = getBucketsEnd(); P != E; ++P) { if (!KeyInfoT::isEqual(P->getFirst(), EmptyKey) && !KeyInfoT::isEqual(P->getFirst(), TombstoneKey)) P->getSecond().~ValueT(); P->getFirst().~KeyT(); } } void initEmpty() { setNumEntries(0); setNumTombstones(0); assert((getNumBuckets() & (getNumBuckets()-1)) == 0 && "# initial buckets must be a power of two!"); const KeyT EmptyKey = getEmptyKey(); for (BucketT *B = getBuckets(), *E = getBucketsEnd(); B != E; ++B) ::new (&B->getFirst()) KeyT(EmptyKey); } /// Returns the number of buckets to allocate to ensure that the DenseMap can /// accommodate \p NumEntries without need to grow(). unsigned getMinBucketToReserveForEntries(unsigned NumEntries) { // Ensure that "NumEntries * 4 < NumBuckets * 3" if (NumEntries == 0) return 0; // +1 is required because of the strict equality. // For example if NumEntries is 48, we need to return 401. return NextPowerOf2(NumEntries * 4 / 3 + 1); } void moveFromOldBuckets(BucketT *OldBucketsBegin, BucketT *OldBucketsEnd) { initEmpty(); // Insert all the old elements. const KeyT EmptyKey = getEmptyKey(); const KeyT TombstoneKey = getTombstoneKey(); for (BucketT *B = OldBucketsBegin, *E = OldBucketsEnd; B != E; ++B) { if (!KeyInfoT::isEqual(B->getFirst(), EmptyKey) && !KeyInfoT::isEqual(B->getFirst(), TombstoneKey)) { // Insert the key/value into the new table. BucketT *DestBucket; bool FoundVal = LookupBucketFor(B->getFirst(), DestBucket); (void)FoundVal; // silence warning. assert(!FoundVal && "Key already in new map?"); DestBucket->getFirst() = std::move(B->getFirst()); ::new (&DestBucket->getSecond()) ValueT(std::move(B->getSecond())); incrementNumEntries(); // Free the value. B->getSecond().~ValueT(); } B->getFirst().~KeyT(); } } template void copyFrom( const DenseMapBase &other) { assert(&other != this); assert(getNumBuckets() == other.getNumBuckets()); setNumEntries(other.getNumEntries()); setNumTombstones(other.getNumTombstones()); if (is_trivially_copyable::value && is_trivially_copyable::value) memcpy(reinterpret_cast(getBuckets()), other.getBuckets(), getNumBuckets() * sizeof(BucketT)); else for (size_t i = 0; i < getNumBuckets(); ++i) { ::new (&getBuckets()[i].getFirst()) KeyT(other.getBuckets()[i].getFirst()); if (!KeyInfoT::isEqual(getBuckets()[i].getFirst(), getEmptyKey()) && !KeyInfoT::isEqual(getBuckets()[i].getFirst(), getTombstoneKey())) ::new (&getBuckets()[i].getSecond()) ValueT(other.getBuckets()[i].getSecond()); } } static unsigned getHashValue(const KeyT &Val) { return KeyInfoT::getHashValue(Val); } template static unsigned getHashValue(const LookupKeyT &Val) { return KeyInfoT::getHashValue(Val); } static const KeyT getEmptyKey() { static_assert(std::is_base_of::value, "Must pass the derived type to this template!"); return KeyInfoT::getEmptyKey(); } static const KeyT getTombstoneKey() { return KeyInfoT::getTombstoneKey(); } private: iterator makeIterator(BucketT *P, BucketT *E, DebugEpochBase &Epoch, bool NoAdvance=false) { if (shouldReverseIterate()) { BucketT *B = P == getBucketsEnd() ? getBuckets() : P + 1; return iterator(B, E, Epoch, NoAdvance); } return iterator(P, E, Epoch, NoAdvance); } const_iterator makeConstIterator(const BucketT *P, const BucketT *E, const DebugEpochBase &Epoch, const bool NoAdvance=false) const { if (shouldReverseIterate()) { const BucketT *B = P == getBucketsEnd() ? getBuckets() : P + 1; return const_iterator(B, E, Epoch, NoAdvance); } return const_iterator(P, E, Epoch, NoAdvance); } unsigned getNumEntries() const { return static_cast(this)->getNumEntries(); } void setNumEntries(unsigned Num) { static_cast(this)->setNumEntries(Num); } void incrementNumEntries() { setNumEntries(getNumEntries() + 1); } void decrementNumEntries() { setNumEntries(getNumEntries() - 1); } unsigned getNumTombstones() const { return static_cast(this)->getNumTombstones(); } void setNumTombstones(unsigned Num) { static_cast(this)->setNumTombstones(Num); } void incrementNumTombstones() { setNumTombstones(getNumTombstones() + 1); } void decrementNumTombstones() { setNumTombstones(getNumTombstones() - 1); } const BucketT *getBuckets() const { return static_cast(this)->getBuckets(); } BucketT *getBuckets() { return static_cast(this)->getBuckets(); } unsigned getNumBuckets() const { return static_cast(this)->getNumBuckets(); } BucketT *getBucketsEnd() { return getBuckets() + getNumBuckets(); } const BucketT *getBucketsEnd() const { return getBuckets() + getNumBuckets(); } void grow(unsigned AtLeast) { static_cast(this)->grow(AtLeast); } void shrink_and_clear() { static_cast(this)->shrink_and_clear(); } template BucketT *InsertIntoBucket(BucketT *TheBucket, KeyArg &&Key, ValueArgs &&... Values) { TheBucket = InsertIntoBucketImpl(Key, Key, TheBucket); TheBucket->getFirst() = std::forward(Key); ::new (&TheBucket->getSecond()) ValueT(std::forward(Values)...); return TheBucket; } template BucketT *InsertIntoBucketWithLookup(BucketT *TheBucket, KeyT &&Key, ValueT &&Value, LookupKeyT &Lookup) { TheBucket = InsertIntoBucketImpl(Key, Lookup, TheBucket); TheBucket->getFirst() = std::move(Key); ::new (&TheBucket->getSecond()) ValueT(std::move(Value)); return TheBucket; } template BucketT *InsertIntoBucketImpl(const KeyT &Key, const LookupKeyT &Lookup, BucketT *TheBucket) { incrementEpoch(); // If the load of the hash table is more than 3/4, or if fewer than 1/8 of // the buckets are empty (meaning that many are filled with tombstones), // grow the table. // // The later case is tricky. For example, if we had one empty bucket with // tons of tombstones, failing lookups (e.g. for insertion) would have to // probe almost the entire table until it found the empty bucket. If the // table completely filled with tombstones, no lookup would ever succeed, // causing infinite loops in lookup. unsigned NewNumEntries = getNumEntries() + 1; unsigned NumBuckets = getNumBuckets(); if (LLVM_UNLIKELY(NewNumEntries * 4 >= NumBuckets * 3)) { this->grow(NumBuckets * 2); LookupBucketFor(Lookup, TheBucket); NumBuckets = getNumBuckets(); } else if (LLVM_UNLIKELY(NumBuckets-(NewNumEntries+getNumTombstones()) <= NumBuckets/8)) { this->grow(NumBuckets); LookupBucketFor(Lookup, TheBucket); } assert(TheBucket); // Only update the state after we've grown our bucket space appropriately // so that when growing buckets we have self-consistent entry count. incrementNumEntries(); // If we are writing over a tombstone, remember this. const KeyT EmptyKey = getEmptyKey(); if (!KeyInfoT::isEqual(TheBucket->getFirst(), EmptyKey)) decrementNumTombstones(); return TheBucket; } /// LookupBucketFor - Lookup the appropriate bucket for Val, returning it in /// FoundBucket. If the bucket contains the key and a value, this returns /// true, otherwise it returns a bucket with an empty marker or tombstone and /// returns false. template bool LookupBucketFor(const LookupKeyT &Val, const BucketT *&FoundBucket) const { const BucketT *BucketsPtr = getBuckets(); const unsigned NumBuckets = getNumBuckets(); if (NumBuckets == 0) { FoundBucket = nullptr; return false; } // FoundTombstone - Keep track of whether we find a tombstone while probing. const BucketT *FoundTombstone = nullptr; const KeyT EmptyKey = getEmptyKey(); const KeyT TombstoneKey = getTombstoneKey(); assert(!KeyInfoT::isEqual(Val, EmptyKey) && !KeyInfoT::isEqual(Val, TombstoneKey) && "Empty/Tombstone value shouldn't be inserted into map!"); unsigned BucketNo = getHashValue(Val) & (NumBuckets-1); unsigned ProbeAmt = 1; while (true) { const BucketT *ThisBucket = BucketsPtr + BucketNo; // Found Val's bucket? If so, return it. if (LLVM_LIKELY(KeyInfoT::isEqual(Val, ThisBucket->getFirst()))) { FoundBucket = ThisBucket; return true; } // If we found an empty bucket, the key doesn't exist in the set. // Insert it and return the default value. if (LLVM_LIKELY(KeyInfoT::isEqual(ThisBucket->getFirst(), EmptyKey))) { // If we've already seen a tombstone while probing, fill it in instead // of the empty bucket we eventually probed to. FoundBucket = FoundTombstone ? FoundTombstone : ThisBucket; return false; } // If this is a tombstone, remember it. If Val ends up not in the map, we // prefer to return it than something that would require more probing. if (KeyInfoT::isEqual(ThisBucket->getFirst(), TombstoneKey) && !FoundTombstone) FoundTombstone = ThisBucket; // Remember the first tombstone found. // Otherwise, it's a hash collision or a tombstone, continue quadratic // probing. BucketNo += ProbeAmt++; BucketNo &= (NumBuckets-1); } } template bool LookupBucketFor(const LookupKeyT &Val, BucketT *&FoundBucket) { const BucketT *ConstFoundBucket; bool Result = const_cast(this) ->LookupBucketFor(Val, ConstFoundBucket); FoundBucket = const_cast(ConstFoundBucket); return Result; } public: /// Return the approximate size (in bytes) of the actual map. /// This is just the raw memory used by DenseMap. /// If entries are pointers to objects, the size of the referenced objects /// are not included. size_t getMemorySize() const { return getNumBuckets() * sizeof(BucketT); } }; /// Equality comparison for DenseMap. /// /// Iterates over elements of LHS confirming that each (key, value) pair in LHS /// is also in RHS, and that no additional pairs are in RHS. /// Equivalent to N calls to RHS.find and N value comparisons. Amortized /// complexity is linear, worst case is O(N^2) (if every hash collides). template bool operator==( const DenseMapBase &LHS, const DenseMapBase &RHS) { if (LHS.size() != RHS.size()) return false; for (auto &KV : LHS) { auto I = RHS.find(KV.first); if (I == RHS.end() || I->second != KV.second) return false; } return true; } /// Inequality comparison for DenseMap. /// /// Equivalent to !(LHS == RHS). See operator== for performance notes. template bool operator!=( const DenseMapBase &LHS, const DenseMapBase &RHS) { return !(LHS == RHS); } template , typename BucketT = llvm::detail::DenseMapPair> class DenseMap : public DenseMapBase, KeyT, ValueT, KeyInfoT, BucketT> { friend class DenseMapBase; // Lift some types from the dependent base class into this class for // simplicity of referring to them. using BaseT = DenseMapBase; BucketT *Buckets; unsigned NumEntries; unsigned NumTombstones; unsigned NumBuckets; public: /// Create a DenseMap wth an optional \p InitialReserve that guarantee that /// this number of elements can be inserted in the map without grow() explicit DenseMap(unsigned InitialReserve = 0) { init(InitialReserve); } DenseMap(const DenseMap &other) : BaseT() { init(0); copyFrom(other); } DenseMap(DenseMap &&other) : BaseT() { init(0); swap(other); } template DenseMap(const InputIt &I, const InputIt &E) { init(std::distance(I, E)); this->insert(I, E); } DenseMap(std::initializer_list Vals) { init(Vals.size()); this->insert(Vals.begin(), Vals.end()); } ~DenseMap() { this->destroyAll(); deallocate_buffer(Buckets, sizeof(BucketT) * NumBuckets, alignof(BucketT)); } void swap(DenseMap& RHS) { this->incrementEpoch(); RHS.incrementEpoch(); std::swap(Buckets, RHS.Buckets); std::swap(NumEntries, RHS.NumEntries); std::swap(NumTombstones, RHS.NumTombstones); std::swap(NumBuckets, RHS.NumBuckets); } DenseMap& operator=(const DenseMap& other) { if (&other != this) copyFrom(other); return *this; } DenseMap& operator=(DenseMap &&other) { this->destroyAll(); deallocate_buffer(Buckets, sizeof(BucketT) * NumBuckets, alignof(BucketT)); init(0); swap(other); return *this; } void copyFrom(const DenseMap& other) { this->destroyAll(); deallocate_buffer(Buckets, sizeof(BucketT) * NumBuckets, alignof(BucketT)); if (allocateBuckets(other.NumBuckets)) { this->BaseT::copyFrom(other); } else { NumEntries = 0; NumTombstones = 0; } } void init(unsigned InitNumEntries) { auto InitBuckets = BaseT::getMinBucketToReserveForEntries(InitNumEntries); if (allocateBuckets(InitBuckets)) { this->BaseT::initEmpty(); } else { NumEntries = 0; NumTombstones = 0; } } void grow(unsigned AtLeast) { unsigned OldNumBuckets = NumBuckets; BucketT *OldBuckets = Buckets; allocateBuckets(std::max(64, static_cast(NextPowerOf2(AtLeast-1)))); assert(Buckets); if (!OldBuckets) { this->BaseT::initEmpty(); return; } this->moveFromOldBuckets(OldBuckets, OldBuckets+OldNumBuckets); // Free the old table. deallocate_buffer(OldBuckets, sizeof(BucketT) * OldNumBuckets, alignof(BucketT)); } void shrink_and_clear() { unsigned OldNumBuckets = NumBuckets; unsigned OldNumEntries = NumEntries; this->destroyAll(); // Reduce the number of buckets. unsigned NewNumBuckets = 0; if (OldNumEntries) NewNumBuckets = std::max(64, 1 << (Log2_32_Ceil(OldNumEntries) + 1)); if (NewNumBuckets == NumBuckets) { this->BaseT::initEmpty(); return; } deallocate_buffer(Buckets, sizeof(BucketT) * OldNumBuckets, alignof(BucketT)); init(NewNumBuckets); } private: unsigned getNumEntries() const { return NumEntries; } void setNumEntries(unsigned Num) { NumEntries = Num; } unsigned getNumTombstones() const { return NumTombstones; } void setNumTombstones(unsigned Num) { NumTombstones = Num; } BucketT *getBuckets() const { return Buckets; } unsigned getNumBuckets() const { return NumBuckets; } bool allocateBuckets(unsigned Num) { NumBuckets = Num; if (NumBuckets == 0) { Buckets = nullptr; return false; } Buckets = static_cast( allocate_buffer(sizeof(BucketT) * NumBuckets, alignof(BucketT))); return true; } }; template , typename BucketT = llvm::detail::DenseMapPair> class SmallDenseMap : public DenseMapBase< SmallDenseMap, KeyT, ValueT, KeyInfoT, BucketT> { friend class DenseMapBase; // Lift some types from the dependent base class into this class for // simplicity of referring to them. using BaseT = DenseMapBase; static_assert(isPowerOf2_64(InlineBuckets), "InlineBuckets must be a power of 2."); unsigned Small : 1; unsigned NumEntries : 31; unsigned NumTombstones; struct LargeRep { BucketT *Buckets; unsigned NumBuckets; }; /// A "union" of an inline bucket array and the struct representing /// a large bucket. This union will be discriminated by the 'Small' bit. AlignedCharArrayUnion storage; public: explicit SmallDenseMap(unsigned NumInitBuckets = 0) { init(NumInitBuckets); } SmallDenseMap(const SmallDenseMap &other) : BaseT() { init(0); copyFrom(other); } SmallDenseMap(SmallDenseMap &&other) : BaseT() { init(0); swap(other); } template SmallDenseMap(const InputIt &I, const InputIt &E) { init(NextPowerOf2(std::distance(I, E))); this->insert(I, E); } ~SmallDenseMap() { this->destroyAll(); deallocateBuckets(); } void swap(SmallDenseMap& RHS) { unsigned TmpNumEntries = RHS.NumEntries; RHS.NumEntries = NumEntries; NumEntries = TmpNumEntries; std::swap(NumTombstones, RHS.NumTombstones); const KeyT EmptyKey = this->getEmptyKey(); const KeyT TombstoneKey = this->getTombstoneKey(); if (Small && RHS.Small) { // If we're swapping inline bucket arrays, we have to cope with some of // the tricky bits of DenseMap's storage system: the buckets are not // fully initialized. Thus we swap every key, but we may have // a one-directional move of the value. for (unsigned i = 0, e = InlineBuckets; i != e; ++i) { BucketT *LHSB = &getInlineBuckets()[i], *RHSB = &RHS.getInlineBuckets()[i]; bool hasLHSValue = (!KeyInfoT::isEqual(LHSB->getFirst(), EmptyKey) && !KeyInfoT::isEqual(LHSB->getFirst(), TombstoneKey)); bool hasRHSValue = (!KeyInfoT::isEqual(RHSB->getFirst(), EmptyKey) && !KeyInfoT::isEqual(RHSB->getFirst(), TombstoneKey)); if (hasLHSValue && hasRHSValue) { // Swap together if we can... std::swap(*LHSB, *RHSB); continue; } // Swap separately and handle any assymetry. std::swap(LHSB->getFirst(), RHSB->getFirst()); if (hasLHSValue) { ::new (&RHSB->getSecond()) ValueT(std::move(LHSB->getSecond())); LHSB->getSecond().~ValueT(); } else if (hasRHSValue) { ::new (&LHSB->getSecond()) ValueT(std::move(RHSB->getSecond())); RHSB->getSecond().~ValueT(); } } return; } if (!Small && !RHS.Small) { std::swap(getLargeRep()->Buckets, RHS.getLargeRep()->Buckets); std::swap(getLargeRep()->NumBuckets, RHS.getLargeRep()->NumBuckets); return; } SmallDenseMap &SmallSide = Small ? *this : RHS; SmallDenseMap &LargeSide = Small ? RHS : *this; // First stash the large side's rep and move the small side across. LargeRep TmpRep = std::move(*LargeSide.getLargeRep()); LargeSide.getLargeRep()->~LargeRep(); LargeSide.Small = true; // This is similar to the standard move-from-old-buckets, but the bucket // count hasn't actually rotated in this case. So we have to carefully // move construct the keys and values into their new locations, but there // is no need to re-hash things. for (unsigned i = 0, e = InlineBuckets; i != e; ++i) { BucketT *NewB = &LargeSide.getInlineBuckets()[i], *OldB = &SmallSide.getInlineBuckets()[i]; ::new (&NewB->getFirst()) KeyT(std::move(OldB->getFirst())); OldB->getFirst().~KeyT(); if (!KeyInfoT::isEqual(NewB->getFirst(), EmptyKey) && !KeyInfoT::isEqual(NewB->getFirst(), TombstoneKey)) { ::new (&NewB->getSecond()) ValueT(std::move(OldB->getSecond())); OldB->getSecond().~ValueT(); } } // The hard part of moving the small buckets across is done, just move // the TmpRep into its new home. SmallSide.Small = false; new (SmallSide.getLargeRep()) LargeRep(std::move(TmpRep)); } SmallDenseMap& operator=(const SmallDenseMap& other) { if (&other != this) copyFrom(other); return *this; } SmallDenseMap& operator=(SmallDenseMap &&other) { this->destroyAll(); deallocateBuckets(); init(0); swap(other); return *this; } void copyFrom(const SmallDenseMap& other) { this->destroyAll(); deallocateBuckets(); Small = true; if (other.getNumBuckets() > InlineBuckets) { Small = false; new (getLargeRep()) LargeRep(allocateBuckets(other.getNumBuckets())); } this->BaseT::copyFrom(other); } void init(unsigned InitBuckets) { Small = true; if (InitBuckets > InlineBuckets) { Small = false; new (getLargeRep()) LargeRep(allocateBuckets(InitBuckets)); } this->BaseT::initEmpty(); } void grow(unsigned AtLeast) { if (AtLeast >= InlineBuckets) AtLeast = std::max(64, NextPowerOf2(AtLeast-1)); if (Small) { if (AtLeast < InlineBuckets) return; // Nothing to do. // First move the inline buckets into a temporary storage. AlignedCharArrayUnion TmpStorage; BucketT *TmpBegin = reinterpret_cast(TmpStorage.buffer); BucketT *TmpEnd = TmpBegin; // Loop over the buckets, moving non-empty, non-tombstones into the // temporary storage. Have the loop move the TmpEnd forward as it goes. const KeyT EmptyKey = this->getEmptyKey(); const KeyT TombstoneKey = this->getTombstoneKey(); for (BucketT *P = getBuckets(), *E = P + InlineBuckets; P != E; ++P) { if (!KeyInfoT::isEqual(P->getFirst(), EmptyKey) && !KeyInfoT::isEqual(P->getFirst(), TombstoneKey)) { assert(size_t(TmpEnd - TmpBegin) < InlineBuckets && "Too many inline buckets!"); ::new (&TmpEnd->getFirst()) KeyT(std::move(P->getFirst())); ::new (&TmpEnd->getSecond()) ValueT(std::move(P->getSecond())); ++TmpEnd; P->getSecond().~ValueT(); } P->getFirst().~KeyT(); } // Now make this map use the large rep, and move all the entries back // into it. Small = false; new (getLargeRep()) LargeRep(allocateBuckets(AtLeast)); this->moveFromOldBuckets(TmpBegin, TmpEnd); return; } LargeRep OldRep = std::move(*getLargeRep()); getLargeRep()->~LargeRep(); if (AtLeast <= InlineBuckets) { Small = true; } else { new (getLargeRep()) LargeRep(allocateBuckets(AtLeast)); } this->moveFromOldBuckets(OldRep.Buckets, OldRep.Buckets+OldRep.NumBuckets); // Free the old table. deallocate_buffer(OldRep.Buckets, sizeof(BucketT) * OldRep.NumBuckets, alignof(BucketT)); } void shrink_and_clear() { unsigned OldSize = this->size(); this->destroyAll(); // Reduce the number of buckets. unsigned NewNumBuckets = 0; if (OldSize) { NewNumBuckets = 1 << (Log2_32_Ceil(OldSize) + 1); if (NewNumBuckets > InlineBuckets && NewNumBuckets < 64u) NewNumBuckets = 64; } if ((Small && NewNumBuckets <= InlineBuckets) || (!Small && NewNumBuckets == getLargeRep()->NumBuckets)) { this->BaseT::initEmpty(); return; } deallocateBuckets(); init(NewNumBuckets); } private: unsigned getNumEntries() const { return NumEntries; } void setNumEntries(unsigned Num) { // NumEntries is hardcoded to be 31 bits wide. assert(Num < (1U << 31) && "Cannot support more than 1<<31 entries"); NumEntries = Num; } unsigned getNumTombstones() const { return NumTombstones; } void setNumTombstones(unsigned Num) { NumTombstones = Num; } const BucketT *getInlineBuckets() const { assert(Small); // Note that this cast does not violate aliasing rules as we assert that // the memory's dynamic type is the small, inline bucket buffer, and the // 'storage.buffer' static type is 'char *'. return reinterpret_cast(storage.buffer); } BucketT *getInlineBuckets() { return const_cast( const_cast(this)->getInlineBuckets()); } const LargeRep *getLargeRep() const { assert(!Small); // Note, same rule about aliasing as with getInlineBuckets. return reinterpret_cast(storage.buffer); } LargeRep *getLargeRep() { return const_cast( const_cast(this)->getLargeRep()); } const BucketT *getBuckets() const { return Small ? getInlineBuckets() : getLargeRep()->Buckets; } BucketT *getBuckets() { return const_cast( const_cast(this)->getBuckets()); } unsigned getNumBuckets() const { return Small ? InlineBuckets : getLargeRep()->NumBuckets; } void deallocateBuckets() { if (Small) return; deallocate_buffer(getLargeRep()->Buckets, sizeof(BucketT) * getLargeRep()->NumBuckets, alignof(BucketT)); getLargeRep()->~LargeRep(); } LargeRep allocateBuckets(unsigned Num) { assert(Num > InlineBuckets && "Must allocate more buckets than are inline"); LargeRep Rep = {static_cast(allocate_buffer( sizeof(BucketT) * Num, alignof(BucketT))), Num}; return Rep; } }; template class DenseMapIterator : DebugEpochBase::HandleBase { friend class DenseMapIterator; friend class DenseMapIterator; using ConstIterator = DenseMapIterator; public: using difference_type = ptrdiff_t; using value_type = typename std::conditional::type; using pointer = value_type *; using reference = value_type &; using iterator_category = std::forward_iterator_tag; private: pointer Ptr = nullptr; pointer End = nullptr; public: DenseMapIterator() = default; DenseMapIterator(pointer Pos, pointer E, const DebugEpochBase &Epoch, bool NoAdvance = false) : DebugEpochBase::HandleBase(&Epoch), Ptr(Pos), End(E) { assert(isHandleInSync() && "invalid construction!"); if (NoAdvance) return; if (shouldReverseIterate()) { RetreatPastEmptyBuckets(); return; } AdvancePastEmptyBuckets(); } // Converting ctor from non-const iterators to const iterators. SFINAE'd out // for const iterator destinations so it doesn't end up as a user defined copy // constructor. template ::type> DenseMapIterator( const DenseMapIterator &I) : DebugEpochBase::HandleBase(I), Ptr(I.Ptr), End(I.End) {} reference operator*() const { assert(isHandleInSync() && "invalid iterator access!"); if (shouldReverseIterate()) return Ptr[-1]; return *Ptr; } pointer operator->() const { assert(isHandleInSync() && "invalid iterator access!"); if (shouldReverseIterate()) return &(Ptr[-1]); return Ptr; } bool operator==(const ConstIterator &RHS) const { assert((!Ptr || isHandleInSync()) && "handle not in sync!"); assert((!RHS.Ptr || RHS.isHandleInSync()) && "handle not in sync!"); assert(getEpochAddress() == RHS.getEpochAddress() && "comparing incomparable iterators!"); return Ptr == RHS.Ptr; } bool operator!=(const ConstIterator &RHS) const { assert((!Ptr || isHandleInSync()) && "handle not in sync!"); assert((!RHS.Ptr || RHS.isHandleInSync()) && "handle not in sync!"); assert(getEpochAddress() == RHS.getEpochAddress() && "comparing incomparable iterators!"); return Ptr != RHS.Ptr; } inline DenseMapIterator& operator++() { // Preincrement assert(isHandleInSync() && "invalid iterator access!"); if (shouldReverseIterate()) { --Ptr; RetreatPastEmptyBuckets(); return *this; } ++Ptr; AdvancePastEmptyBuckets(); return *this; } DenseMapIterator operator++(int) { // Postincrement assert(isHandleInSync() && "invalid iterator access!"); DenseMapIterator tmp = *this; ++*this; return tmp; } private: void AdvancePastEmptyBuckets() { assert(Ptr <= End); const KeyT Empty = KeyInfoT::getEmptyKey(); const KeyT Tombstone = KeyInfoT::getTombstoneKey(); while (Ptr != End && (KeyInfoT::isEqual(Ptr->getFirst(), Empty) || KeyInfoT::isEqual(Ptr->getFirst(), Tombstone))) ++Ptr; } void RetreatPastEmptyBuckets() { assert(Ptr >= End); const KeyT Empty = KeyInfoT::getEmptyKey(); const KeyT Tombstone = KeyInfoT::getTombstoneKey(); while (Ptr != End && (KeyInfoT::isEqual(Ptr[-1].getFirst(), Empty) || KeyInfoT::isEqual(Ptr[-1].getFirst(), Tombstone))) --Ptr; } }; template inline size_t capacity_in_bytes(const DenseMap &X) { return X.getMemorySize(); } } // end namespace llvm #endif // LLVM_ADT_DENSEMAP_H binaryen-version_91/third_party/llvm-project/include/llvm/ADT/DenseMapInfo.h000066400000000000000000000227771362402614000273330ustar00rootroot00000000000000//===- llvm/ADT/DenseMapInfo.h - Type traits for DenseMap -------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file defines DenseMapInfo traits for DenseMap. // //===----------------------------------------------------------------------===// #ifndef LLVM_ADT_DENSEMAPINFO_H #define LLVM_ADT_DENSEMAPINFO_H #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/PointerLikeTypeTraits.h" #include "llvm/Support/TypeSize.h" #include #include #include #include namespace llvm { template struct DenseMapInfo { //static inline T getEmptyKey(); //static inline T getTombstoneKey(); //static unsigned getHashValue(const T &Val); //static bool isEqual(const T &LHS, const T &RHS); }; // Provide DenseMapInfo for all pointers. template struct DenseMapInfo { static inline T* getEmptyKey() { uintptr_t Val = static_cast(-1); Val <<= PointerLikeTypeTraits::NumLowBitsAvailable; return reinterpret_cast(Val); } static inline T* getTombstoneKey() { uintptr_t Val = static_cast(-2); Val <<= PointerLikeTypeTraits::NumLowBitsAvailable; return reinterpret_cast(Val); } static unsigned getHashValue(const T *PtrVal) { return (unsigned((uintptr_t)PtrVal) >> 4) ^ (unsigned((uintptr_t)PtrVal) >> 9); } static bool isEqual(const T *LHS, const T *RHS) { return LHS == RHS; } }; // Provide DenseMapInfo for chars. template<> struct DenseMapInfo { static inline char getEmptyKey() { return ~0; } static inline char getTombstoneKey() { return ~0 - 1; } static unsigned getHashValue(const char& Val) { return Val * 37U; } static bool isEqual(const char &LHS, const char &RHS) { return LHS == RHS; } }; // Provide DenseMapInfo for unsigned chars. template <> struct DenseMapInfo { static inline unsigned char getEmptyKey() { return ~0; } static inline unsigned char getTombstoneKey() { return ~0 - 1; } static unsigned getHashValue(const unsigned char &Val) { return Val * 37U; } static bool isEqual(const unsigned char &LHS, const unsigned char &RHS) { return LHS == RHS; } }; // Provide DenseMapInfo for unsigned shorts. template <> struct DenseMapInfo { static inline unsigned short getEmptyKey() { return 0xFFFF; } static inline unsigned short getTombstoneKey() { return 0xFFFF - 1; } static unsigned getHashValue(const unsigned short &Val) { return Val * 37U; } static bool isEqual(const unsigned short &LHS, const unsigned short &RHS) { return LHS == RHS; } }; // Provide DenseMapInfo for unsigned ints. template<> struct DenseMapInfo { static inline unsigned getEmptyKey() { return ~0U; } static inline unsigned getTombstoneKey() { return ~0U - 1; } static unsigned getHashValue(const unsigned& Val) { return Val * 37U; } static bool isEqual(const unsigned& LHS, const unsigned& RHS) { return LHS == RHS; } }; // Provide DenseMapInfo for unsigned longs. template<> struct DenseMapInfo { static inline unsigned long getEmptyKey() { return ~0UL; } static inline unsigned long getTombstoneKey() { return ~0UL - 1L; } static unsigned getHashValue(const unsigned long& Val) { return (unsigned)(Val * 37UL); } static bool isEqual(const unsigned long& LHS, const unsigned long& RHS) { return LHS == RHS; } }; // Provide DenseMapInfo for unsigned long longs. template<> struct DenseMapInfo { static inline unsigned long long getEmptyKey() { return ~0ULL; } static inline unsigned long long getTombstoneKey() { return ~0ULL - 1ULL; } static unsigned getHashValue(const unsigned long long& Val) { return (unsigned)(Val * 37ULL); } static bool isEqual(const unsigned long long& LHS, const unsigned long long& RHS) { return LHS == RHS; } }; // Provide DenseMapInfo for shorts. template <> struct DenseMapInfo { static inline short getEmptyKey() { return 0x7FFF; } static inline short getTombstoneKey() { return -0x7FFF - 1; } static unsigned getHashValue(const short &Val) { return Val * 37U; } static bool isEqual(const short &LHS, const short &RHS) { return LHS == RHS; } }; // Provide DenseMapInfo for ints. template<> struct DenseMapInfo { static inline int getEmptyKey() { return 0x7fffffff; } static inline int getTombstoneKey() { return -0x7fffffff - 1; } static unsigned getHashValue(const int& Val) { return (unsigned)(Val * 37U); } static bool isEqual(const int& LHS, const int& RHS) { return LHS == RHS; } }; // Provide DenseMapInfo for longs. template<> struct DenseMapInfo { static inline long getEmptyKey() { return (1UL << (sizeof(long) * 8 - 1)) - 1UL; } static inline long getTombstoneKey() { return getEmptyKey() - 1L; } static unsigned getHashValue(const long& Val) { return (unsigned)(Val * 37UL); } static bool isEqual(const long& LHS, const long& RHS) { return LHS == RHS; } }; // Provide DenseMapInfo for long longs. template<> struct DenseMapInfo { static inline long long getEmptyKey() { return 0x7fffffffffffffffLL; } static inline long long getTombstoneKey() { return -0x7fffffffffffffffLL-1; } static unsigned getHashValue(const long long& Val) { return (unsigned)(Val * 37ULL); } static bool isEqual(const long long& LHS, const long long& RHS) { return LHS == RHS; } }; // Provide DenseMapInfo for all pairs whose members have info. template struct DenseMapInfo> { using Pair = std::pair; using FirstInfo = DenseMapInfo; using SecondInfo = DenseMapInfo; static inline Pair getEmptyKey() { return std::make_pair(FirstInfo::getEmptyKey(), SecondInfo::getEmptyKey()); } static inline Pair getTombstoneKey() { return std::make_pair(FirstInfo::getTombstoneKey(), SecondInfo::getTombstoneKey()); } static unsigned getHashValue(const Pair& PairVal) { uint64_t key = (uint64_t)FirstInfo::getHashValue(PairVal.first) << 32 | (uint64_t)SecondInfo::getHashValue(PairVal.second); key += ~(key << 32); key ^= (key >> 22); key += ~(key << 13); key ^= (key >> 8); key += (key << 3); key ^= (key >> 15); key += ~(key << 27); key ^= (key >> 31); return (unsigned)key; } static bool isEqual(const Pair &LHS, const Pair &RHS) { return FirstInfo::isEqual(LHS.first, RHS.first) && SecondInfo::isEqual(LHS.second, RHS.second); } }; // Provide DenseMapInfo for StringRefs. template <> struct DenseMapInfo { static inline StringRef getEmptyKey() { return StringRef(reinterpret_cast(~static_cast(0)), 0); } static inline StringRef getTombstoneKey() { return StringRef(reinterpret_cast(~static_cast(1)), 0); } static unsigned getHashValue(StringRef Val) { assert(Val.data() != getEmptyKey().data() && "Cannot hash the empty key!"); assert(Val.data() != getTombstoneKey().data() && "Cannot hash the tombstone key!"); return (unsigned)(hash_value(Val)); } static bool isEqual(StringRef LHS, StringRef RHS) { if (RHS.data() == getEmptyKey().data()) return LHS.data() == getEmptyKey().data(); if (RHS.data() == getTombstoneKey().data()) return LHS.data() == getTombstoneKey().data(); return LHS == RHS; } }; // Provide DenseMapInfo for ArrayRefs. template struct DenseMapInfo> { static inline ArrayRef getEmptyKey() { return ArrayRef(reinterpret_cast(~static_cast(0)), size_t(0)); } static inline ArrayRef getTombstoneKey() { return ArrayRef(reinterpret_cast(~static_cast(1)), size_t(0)); } static unsigned getHashValue(ArrayRef Val) { assert(Val.data() != getEmptyKey().data() && "Cannot hash the empty key!"); assert(Val.data() != getTombstoneKey().data() && "Cannot hash the tombstone key!"); return (unsigned)(hash_value(Val)); } static bool isEqual(ArrayRef LHS, ArrayRef RHS) { if (RHS.data() == getEmptyKey().data()) return LHS.data() == getEmptyKey().data(); if (RHS.data() == getTombstoneKey().data()) return LHS.data() == getTombstoneKey().data(); return LHS == RHS; } }; template <> struct DenseMapInfo { static inline hash_code getEmptyKey() { return hash_code(-1); } static inline hash_code getTombstoneKey() { return hash_code(-2); } static unsigned getHashValue(hash_code val) { return val; } static bool isEqual(hash_code LHS, hash_code RHS) { return LHS == RHS; } }; template <> struct DenseMapInfo { static inline ElementCount getEmptyKey() { return {~0U, true}; } static inline ElementCount getTombstoneKey() { return {~0U - 1, false}; } static unsigned getHashValue(const ElementCount& EltCnt) { if (EltCnt.Scalable) return (EltCnt.Min * 37U) - 1U; return EltCnt.Min * 37U; } static bool isEqual(const ElementCount& LHS, const ElementCount& RHS) { return LHS == RHS; } }; } // end namespace llvm #endif // LLVM_ADT_DENSEMAPINFO_H binaryen-version_91/third_party/llvm-project/include/llvm/ADT/DenseSet.h000066400000000000000000000222561362402614000265250ustar00rootroot00000000000000//===- llvm/ADT/DenseSet.h - Dense probed hash table ------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file defines the DenseSet and SmallDenseSet classes. // //===----------------------------------------------------------------------===// #ifndef LLVM_ADT_DENSESET_H #define LLVM_ADT_DENSESET_H #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseMapInfo.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/type_traits.h" #include #include #include #include #include namespace llvm { namespace detail { struct DenseSetEmpty {}; // Use the empty base class trick so we can create a DenseMap where the buckets // contain only a single item. template class DenseSetPair : public DenseSetEmpty { KeyT key; public: KeyT &getFirst() { return key; } const KeyT &getFirst() const { return key; } DenseSetEmpty &getSecond() { return *this; } const DenseSetEmpty &getSecond() const { return *this; } }; /// Base class for DenseSet and DenseSmallSet. /// /// MapTy should be either /// /// DenseMap> /// /// or the equivalent SmallDenseMap type. ValueInfoT must implement the /// DenseMapInfo "concept". template class DenseSetImpl { static_assert(sizeof(typename MapTy::value_type) == sizeof(ValueT), "DenseMap buckets unexpectedly large!"); MapTy TheMap; template using const_arg_type_t = typename const_pointer_or_const_ref::type; public: using key_type = ValueT; using value_type = ValueT; using size_type = unsigned; explicit DenseSetImpl(unsigned InitialReserve = 0) : TheMap(InitialReserve) {} DenseSetImpl(std::initializer_list Elems) : DenseSetImpl(PowerOf2Ceil(Elems.size())) { insert(Elems.begin(), Elems.end()); } bool empty() const { return TheMap.empty(); } size_type size() const { return TheMap.size(); } size_t getMemorySize() const { return TheMap.getMemorySize(); } /// Grow the DenseSet so that it has at least Size buckets. Will not shrink /// the Size of the set. void resize(size_t Size) { TheMap.resize(Size); } /// Grow the DenseSet so that it can contain at least \p NumEntries items /// before resizing again. void reserve(size_t Size) { TheMap.reserve(Size); } void clear() { TheMap.clear(); } /// Return 1 if the specified key is in the set, 0 otherwise. size_type count(const_arg_type_t V) const { return TheMap.count(V); } bool erase(const ValueT &V) { return TheMap.erase(V); } void swap(DenseSetImpl &RHS) { TheMap.swap(RHS.TheMap); } // Iterators. class ConstIterator; class Iterator { typename MapTy::iterator I; friend class DenseSetImpl; friend class ConstIterator; public: using difference_type = typename MapTy::iterator::difference_type; using value_type = ValueT; using pointer = value_type *; using reference = value_type &; using iterator_category = std::forward_iterator_tag; Iterator() = default; Iterator(const typename MapTy::iterator &i) : I(i) {} ValueT &operator*() { return I->getFirst(); } const ValueT &operator*() const { return I->getFirst(); } ValueT *operator->() { return &I->getFirst(); } const ValueT *operator->() const { return &I->getFirst(); } Iterator& operator++() { ++I; return *this; } Iterator operator++(int) { auto T = *this; ++I; return T; } bool operator==(const ConstIterator& X) const { return I == X.I; } bool operator!=(const ConstIterator& X) const { return I != X.I; } }; class ConstIterator { typename MapTy::const_iterator I; friend class DenseSetImpl; friend class Iterator; public: using difference_type = typename MapTy::const_iterator::difference_type; using value_type = ValueT; using pointer = const value_type *; using reference = const value_type &; using iterator_category = std::forward_iterator_tag; ConstIterator() = default; ConstIterator(const Iterator &B) : I(B.I) {} ConstIterator(const typename MapTy::const_iterator &i) : I(i) {} const ValueT &operator*() const { return I->getFirst(); } const ValueT *operator->() const { return &I->getFirst(); } ConstIterator& operator++() { ++I; return *this; } ConstIterator operator++(int) { auto T = *this; ++I; return T; } bool operator==(const ConstIterator& X) const { return I == X.I; } bool operator!=(const ConstIterator& X) const { return I != X.I; } }; using iterator = Iterator; using const_iterator = ConstIterator; iterator begin() { return Iterator(TheMap.begin()); } iterator end() { return Iterator(TheMap.end()); } const_iterator begin() const { return ConstIterator(TheMap.begin()); } const_iterator end() const { return ConstIterator(TheMap.end()); } iterator find(const_arg_type_t V) { return Iterator(TheMap.find(V)); } const_iterator find(const_arg_type_t V) const { return ConstIterator(TheMap.find(V)); } /// Alternative version of find() which allows a different, and possibly less /// expensive, key type. /// The DenseMapInfo is responsible for supplying methods /// getHashValue(LookupKeyT) and isEqual(LookupKeyT, KeyT) for each key type /// used. template iterator find_as(const LookupKeyT &Val) { return Iterator(TheMap.find_as(Val)); } template const_iterator find_as(const LookupKeyT &Val) const { return ConstIterator(TheMap.find_as(Val)); } void erase(Iterator I) { return TheMap.erase(I.I); } void erase(ConstIterator CI) { return TheMap.erase(CI.I); } std::pair insert(const ValueT &V) { detail::DenseSetEmpty Empty; return TheMap.try_emplace(V, Empty); } std::pair insert(ValueT &&V) { detail::DenseSetEmpty Empty; return TheMap.try_emplace(std::move(V), Empty); } /// Alternative version of insert that uses a different (and possibly less /// expensive) key type. template std::pair insert_as(const ValueT &V, const LookupKeyT &LookupKey) { return TheMap.insert_as({V, detail::DenseSetEmpty()}, LookupKey); } template std::pair insert_as(ValueT &&V, const LookupKeyT &LookupKey) { return TheMap.insert_as({std::move(V), detail::DenseSetEmpty()}, LookupKey); } // Range insertion of values. template void insert(InputIt I, InputIt E) { for (; I != E; ++I) insert(*I); } }; /// Equality comparison for DenseSet. /// /// Iterates over elements of LHS confirming that each element is also a member /// of RHS, and that RHS contains no additional values. /// Equivalent to N calls to RHS.count. Amortized complexity is linear, worst /// case is O(N^2) (if every hash collides). template bool operator==(const DenseSetImpl &LHS, const DenseSetImpl &RHS) { if (LHS.size() != RHS.size()) return false; for (auto &E : LHS) if (!RHS.count(E)) return false; return true; } /// Inequality comparison for DenseSet. /// /// Equivalent to !(LHS == RHS). See operator== for performance notes. template bool operator!=(const DenseSetImpl &LHS, const DenseSetImpl &RHS) { return !(LHS == RHS); } } // end namespace detail /// Implements a dense probed hash-table based set. template > class DenseSet : public detail::DenseSetImpl< ValueT, DenseMap>, ValueInfoT> { using BaseT = detail::DenseSetImpl>, ValueInfoT>; public: using BaseT::BaseT; }; /// Implements a dense probed hash-table based set with some number of buckets /// stored inline. template > class SmallDenseSet : public detail::DenseSetImpl< ValueT, SmallDenseMap>, ValueInfoT> { using BaseT = detail::DenseSetImpl< ValueT, SmallDenseMap>, ValueInfoT>; public: using BaseT::BaseT; }; } // end namespace llvm #endif // LLVM_ADT_DENSESET_H binaryen-version_91/third_party/llvm-project/include/llvm/ADT/EpochTracker.h000066400000000000000000000063171362402614000273650ustar00rootroot00000000000000//===- llvm/ADT/EpochTracker.h - ADT epoch tracking --------------*- C++ -*-==// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file defines the DebugEpochBase and DebugEpochBase::HandleBase classes. // These can be used to write iterators that are fail-fast when LLVM is built // with asserts enabled. // //===----------------------------------------------------------------------===// #ifndef LLVM_ADT_EPOCH_TRACKER_H #define LLVM_ADT_EPOCH_TRACKER_H #include "llvm/Config/abi-breaking.h" #include namespace llvm { #if LLVM_ENABLE_ABI_BREAKING_CHECKS /// A base class for data structure classes wishing to make iterators /// ("handles") pointing into themselves fail-fast. When building without /// asserts, this class is empty and does nothing. /// /// DebugEpochBase does not by itself track handles pointing into itself. The /// expectation is that routines touching the handles will poll on /// isHandleInSync at appropriate points to assert that the handle they're using /// is still valid. /// class DebugEpochBase { uint64_t Epoch; public: DebugEpochBase() : Epoch(0) {} /// Calling incrementEpoch invalidates all handles pointing into the /// calling instance. void incrementEpoch() { ++Epoch; } /// The destructor calls incrementEpoch to make use-after-free bugs /// more likely to crash deterministically. ~DebugEpochBase() { incrementEpoch(); } /// A base class for iterator classes ("handles") that wish to poll for /// iterator invalidating modifications in the underlying data structure. /// When LLVM is built without asserts, this class is empty and does nothing. /// /// HandleBase does not track the parent data structure by itself. It expects /// the routines modifying the data structure to call incrementEpoch when they /// make an iterator-invalidating modification. /// class HandleBase { const uint64_t *EpochAddress; uint64_t EpochAtCreation; public: HandleBase() : EpochAddress(nullptr), EpochAtCreation(UINT64_MAX) {} explicit HandleBase(const DebugEpochBase *Parent) : EpochAddress(&Parent->Epoch), EpochAtCreation(Parent->Epoch) {} /// Returns true if the DebugEpochBase this Handle is linked to has /// not called incrementEpoch on itself since the creation of this /// HandleBase instance. bool isHandleInSync() const { return *EpochAddress == EpochAtCreation; } /// Returns a pointer to the epoch word stored in the data structure /// this handle points into. Can be used to check if two iterators point /// into the same data structure. const void *getEpochAddress() const { return EpochAddress; } }; }; #else class DebugEpochBase { public: void incrementEpoch() {} class HandleBase { public: HandleBase() = default; explicit HandleBase(const DebugEpochBase *) {} bool isHandleInSync() const { return true; } const void *getEpochAddress() const { return nullptr; } }; }; #endif // LLVM_ENABLE_ABI_BREAKING_CHECKS } // namespace llvm #endif binaryen-version_91/third_party/llvm-project/include/llvm/ADT/FoldingSet.h000066400000000000000000000702731362402614000270530ustar00rootroot00000000000000//===- llvm/ADT/FoldingSet.h - Uniquing Hash Set ----------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file defines a hash set that can be used to remove duplication of nodes // in a graph. This code was originally created by Chris Lattner for use with // SelectionDAGCSEMap, but was isolated to provide use across the llvm code set. // //===----------------------------------------------------------------------===// #ifndef LLVM_ADT_FOLDINGSET_H #define LLVM_ADT_FOLDINGSET_H #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/iterator.h" #include "llvm/Support/Allocator.h" #include #include #include #include namespace llvm { /// This folding set used for two purposes: /// 1. Given information about a node we want to create, look up the unique /// instance of the node in the set. If the node already exists, return /// it, otherwise return the bucket it should be inserted into. /// 2. Given a node that has already been created, remove it from the set. /// /// This class is implemented as a single-link chained hash table, where the /// "buckets" are actually the nodes themselves (the next pointer is in the /// node). The last node points back to the bucket to simplify node removal. /// /// Any node that is to be included in the folding set must be a subclass of /// FoldingSetNode. The node class must also define a Profile method used to /// establish the unique bits of data for the node. The Profile method is /// passed a FoldingSetNodeID object which is used to gather the bits. Just /// call one of the Add* functions defined in the FoldingSetBase::NodeID class. /// NOTE: That the folding set does not own the nodes and it is the /// responsibility of the user to dispose of the nodes. /// /// Eg. /// class MyNode : public FoldingSetNode { /// private: /// std::string Name; /// unsigned Value; /// public: /// MyNode(const char *N, unsigned V) : Name(N), Value(V) {} /// ... /// void Profile(FoldingSetNodeID &ID) const { /// ID.AddString(Name); /// ID.AddInteger(Value); /// } /// ... /// }; /// /// To define the folding set itself use the FoldingSet template; /// /// Eg. /// FoldingSet MyFoldingSet; /// /// Four public methods are available to manipulate the folding set; /// /// 1) If you have an existing node that you want add to the set but unsure /// that the node might already exist then call; /// /// MyNode *M = MyFoldingSet.GetOrInsertNode(N); /// /// If The result is equal to the input then the node has been inserted. /// Otherwise, the result is the node existing in the folding set, and the /// input can be discarded (use the result instead.) /// /// 2) If you are ready to construct a node but want to check if it already /// exists, then call FindNodeOrInsertPos with a FoldingSetNodeID of the bits to /// check; /// /// FoldingSetNodeID ID; /// ID.AddString(Name); /// ID.AddInteger(Value); /// void *InsertPoint; /// /// MyNode *M = MyFoldingSet.FindNodeOrInsertPos(ID, InsertPoint); /// /// If found then M with be non-NULL, else InsertPoint will point to where it /// should be inserted using InsertNode. /// /// 3) If you get a NULL result from FindNodeOrInsertPos then you can as a new /// node with FindNodeOrInsertPos; /// /// InsertNode(N, InsertPoint); /// /// 4) Finally, if you want to remove a node from the folding set call; /// /// bool WasRemoved = RemoveNode(N); /// /// The result indicates whether the node existed in the folding set. class FoldingSetNodeID; class StringRef; //===----------------------------------------------------------------------===// /// FoldingSetBase - Implements the folding set functionality. The main /// structure is an array of buckets. Each bucket is indexed by the hash of /// the nodes it contains. The bucket itself points to the nodes contained /// in the bucket via a singly linked list. The last node in the list points /// back to the bucket to facilitate node removal. /// class FoldingSetBase { virtual void anchor(); // Out of line virtual method. protected: /// Buckets - Array of bucket chains. void **Buckets; /// NumBuckets - Length of the Buckets array. Always a power of 2. unsigned NumBuckets; /// NumNodes - Number of nodes in the folding set. Growth occurs when NumNodes /// is greater than twice the number of buckets. unsigned NumNodes; explicit FoldingSetBase(unsigned Log2InitSize = 6); FoldingSetBase(FoldingSetBase &&Arg); FoldingSetBase &operator=(FoldingSetBase &&RHS); ~FoldingSetBase(); public: //===--------------------------------------------------------------------===// /// Node - This class is used to maintain the singly linked bucket list in /// a folding set. class Node { private: // NextInFoldingSetBucket - next link in the bucket list. void *NextInFoldingSetBucket = nullptr; public: Node() = default; // Accessors void *getNextInBucket() const { return NextInFoldingSetBucket; } void SetNextInBucket(void *N) { NextInFoldingSetBucket = N; } }; /// clear - Remove all nodes from the folding set. void clear(); /// size - Returns the number of nodes in the folding set. unsigned size() const { return NumNodes; } /// empty - Returns true if there are no nodes in the folding set. bool empty() const { return NumNodes == 0; } /// reserve - Increase the number of buckets such that adding the /// EltCount-th node won't cause a rebucket operation. reserve is permitted /// to allocate more space than requested by EltCount. void reserve(unsigned EltCount); /// capacity - Returns the number of nodes permitted in the folding set /// before a rebucket operation is performed. unsigned capacity() { // We allow a load factor of up to 2.0, // so that means our capacity is NumBuckets * 2 return NumBuckets * 2; } private: /// GrowHashTable - Double the size of the hash table and rehash everything. void GrowHashTable(); /// GrowBucketCount - resize the hash table and rehash everything. /// NewBucketCount must be a power of two, and must be greater than the old /// bucket count. void GrowBucketCount(unsigned NewBucketCount); protected: /// GetNodeProfile - Instantiations of the FoldingSet template implement /// this function to gather data bits for the given node. virtual void GetNodeProfile(Node *N, FoldingSetNodeID &ID) const = 0; /// NodeEquals - Instantiations of the FoldingSet template implement /// this function to compare the given node with the given ID. virtual bool NodeEquals(Node *N, const FoldingSetNodeID &ID, unsigned IDHash, FoldingSetNodeID &TempID) const=0; /// ComputeNodeHash - Instantiations of the FoldingSet template implement /// this function to compute a hash value for the given node. virtual unsigned ComputeNodeHash(Node *N, FoldingSetNodeID &TempID) const = 0; // The below methods are protected to encourage subclasses to provide a more // type-safe API. /// RemoveNode - Remove a node from the folding set, returning true if one /// was removed or false if the node was not in the folding set. bool RemoveNode(Node *N); /// GetOrInsertNode - If there is an existing simple Node exactly /// equal to the specified node, return it. Otherwise, insert 'N' and return /// it instead. Node *GetOrInsertNode(Node *N); /// FindNodeOrInsertPos - Look up the node specified by ID. If it exists, /// return it. If not, return the insertion token that will make insertion /// faster. Node *FindNodeOrInsertPos(const FoldingSetNodeID &ID, void *&InsertPos); /// InsertNode - Insert the specified node into the folding set, knowing that /// it is not already in the folding set. InsertPos must be obtained from /// FindNodeOrInsertPos. void InsertNode(Node *N, void *InsertPos); }; //===----------------------------------------------------------------------===// /// DefaultFoldingSetTrait - This class provides default implementations /// for FoldingSetTrait implementations. template struct DefaultFoldingSetTrait { static void Profile(const T &X, FoldingSetNodeID &ID) { X.Profile(ID); } static void Profile(T &X, FoldingSetNodeID &ID) { X.Profile(ID); } // Equals - Test if the profile for X would match ID, using TempID // to compute a temporary ID if necessary. The default implementation // just calls Profile and does a regular comparison. Implementations // can override this to provide more efficient implementations. static inline bool Equals(T &X, const FoldingSetNodeID &ID, unsigned IDHash, FoldingSetNodeID &TempID); // ComputeHash - Compute a hash value for X, using TempID to // compute a temporary ID if necessary. The default implementation // just calls Profile and does a regular hash computation. // Implementations can override this to provide more efficient // implementations. static inline unsigned ComputeHash(T &X, FoldingSetNodeID &TempID); }; /// FoldingSetTrait - This trait class is used to define behavior of how /// to "profile" (in the FoldingSet parlance) an object of a given type. /// The default behavior is to invoke a 'Profile' method on an object, but /// through template specialization the behavior can be tailored for specific /// types. Combined with the FoldingSetNodeWrapper class, one can add objects /// to FoldingSets that were not originally designed to have that behavior. template struct FoldingSetTrait : public DefaultFoldingSetTrait {}; /// DefaultContextualFoldingSetTrait - Like DefaultFoldingSetTrait, but /// for ContextualFoldingSets. template struct DefaultContextualFoldingSetTrait { static void Profile(T &X, FoldingSetNodeID &ID, Ctx Context) { X.Profile(ID, Context); } static inline bool Equals(T &X, const FoldingSetNodeID &ID, unsigned IDHash, FoldingSetNodeID &TempID, Ctx Context); static inline unsigned ComputeHash(T &X, FoldingSetNodeID &TempID, Ctx Context); }; /// ContextualFoldingSetTrait - Like FoldingSetTrait, but for /// ContextualFoldingSets. template struct ContextualFoldingSetTrait : public DefaultContextualFoldingSetTrait {}; //===--------------------------------------------------------------------===// /// FoldingSetNodeIDRef - This class describes a reference to an interned /// FoldingSetNodeID, which can be a useful to store node id data rather /// than using plain FoldingSetNodeIDs, since the 32-element SmallVector /// is often much larger than necessary, and the possibility of heap /// allocation means it requires a non-trivial destructor call. class FoldingSetNodeIDRef { const unsigned *Data = nullptr; size_t Size = 0; public: FoldingSetNodeIDRef() = default; FoldingSetNodeIDRef(const unsigned *D, size_t S) : Data(D), Size(S) {} /// ComputeHash - Compute a strong hash value for this FoldingSetNodeIDRef, /// used to lookup the node in the FoldingSetBase. unsigned ComputeHash() const; bool operator==(FoldingSetNodeIDRef) const; bool operator!=(FoldingSetNodeIDRef RHS) const { return !(*this == RHS); } /// Used to compare the "ordering" of two nodes as defined by the /// profiled bits and their ordering defined by memcmp(). bool operator<(FoldingSetNodeIDRef) const; const unsigned *getData() const { return Data; } size_t getSize() const { return Size; } }; //===--------------------------------------------------------------------===// /// FoldingSetNodeID - This class is used to gather all the unique data bits of /// a node. When all the bits are gathered this class is used to produce a /// hash value for the node. class FoldingSetNodeID { /// Bits - Vector of all the data bits that make the node unique. /// Use a SmallVector to avoid a heap allocation in the common case. SmallVector Bits; public: FoldingSetNodeID() = default; FoldingSetNodeID(FoldingSetNodeIDRef Ref) : Bits(Ref.getData(), Ref.getData() + Ref.getSize()) {} /// Add* - Add various data types to Bit data. void AddPointer(const void *Ptr); void AddInteger(signed I); void AddInteger(unsigned I); void AddInteger(long I); void AddInteger(unsigned long I); void AddInteger(long long I); void AddInteger(unsigned long long I); void AddBoolean(bool B) { AddInteger(B ? 1U : 0U); } void AddString(StringRef String); void AddNodeID(const FoldingSetNodeID &ID); template inline void Add(const T &x) { FoldingSetTrait::Profile(x, *this); } /// clear - Clear the accumulated profile, allowing this FoldingSetNodeID /// object to be used to compute a new profile. inline void clear() { Bits.clear(); } /// ComputeHash - Compute a strong hash value for this FoldingSetNodeID, used /// to lookup the node in the FoldingSetBase. unsigned ComputeHash() const; /// operator== - Used to compare two nodes to each other. bool operator==(const FoldingSetNodeID &RHS) const; bool operator==(const FoldingSetNodeIDRef RHS) const; bool operator!=(const FoldingSetNodeID &RHS) const { return !(*this == RHS); } bool operator!=(const FoldingSetNodeIDRef RHS) const { return !(*this ==RHS);} /// Used to compare the "ordering" of two nodes as defined by the /// profiled bits and their ordering defined by memcmp(). bool operator<(const FoldingSetNodeID &RHS) const; bool operator<(const FoldingSetNodeIDRef RHS) const; /// Intern - Copy this node's data to a memory region allocated from the /// given allocator and return a FoldingSetNodeIDRef describing the /// interned data. FoldingSetNodeIDRef Intern(BumpPtrAllocator &Allocator) const; }; // Convenience type to hide the implementation of the folding set. using FoldingSetNode = FoldingSetBase::Node; template class FoldingSetIterator; template class FoldingSetBucketIterator; // Definitions of FoldingSetTrait and ContextualFoldingSetTrait functions, which // require the definition of FoldingSetNodeID. template inline bool DefaultFoldingSetTrait::Equals(T &X, const FoldingSetNodeID &ID, unsigned /*IDHash*/, FoldingSetNodeID &TempID) { FoldingSetTrait::Profile(X, TempID); return TempID == ID; } template inline unsigned DefaultFoldingSetTrait::ComputeHash(T &X, FoldingSetNodeID &TempID) { FoldingSetTrait::Profile(X, TempID); return TempID.ComputeHash(); } template inline bool DefaultContextualFoldingSetTrait::Equals(T &X, const FoldingSetNodeID &ID, unsigned /*IDHash*/, FoldingSetNodeID &TempID, Ctx Context) { ContextualFoldingSetTrait::Profile(X, TempID, Context); return TempID == ID; } template inline unsigned DefaultContextualFoldingSetTrait::ComputeHash(T &X, FoldingSetNodeID &TempID, Ctx Context) { ContextualFoldingSetTrait::Profile(X, TempID, Context); return TempID.ComputeHash(); } //===----------------------------------------------------------------------===// /// FoldingSetImpl - An implementation detail that lets us share code between /// FoldingSet and ContextualFoldingSet. template class FoldingSetImpl : public FoldingSetBase { protected: explicit FoldingSetImpl(unsigned Log2InitSize) : FoldingSetBase(Log2InitSize) {} FoldingSetImpl(FoldingSetImpl &&Arg) = default; FoldingSetImpl &operator=(FoldingSetImpl &&RHS) = default; ~FoldingSetImpl() = default; public: using iterator = FoldingSetIterator; iterator begin() { return iterator(Buckets); } iterator end() { return iterator(Buckets+NumBuckets); } using const_iterator = FoldingSetIterator; const_iterator begin() const { return const_iterator(Buckets); } const_iterator end() const { return const_iterator(Buckets+NumBuckets); } using bucket_iterator = FoldingSetBucketIterator; bucket_iterator bucket_begin(unsigned hash) { return bucket_iterator(Buckets + (hash & (NumBuckets-1))); } bucket_iterator bucket_end(unsigned hash) { return bucket_iterator(Buckets + (hash & (NumBuckets-1)), true); } /// RemoveNode - Remove a node from the folding set, returning true if one /// was removed or false if the node was not in the folding set. bool RemoveNode(T *N) { return FoldingSetBase::RemoveNode(N); } /// GetOrInsertNode - If there is an existing simple Node exactly /// equal to the specified node, return it. Otherwise, insert 'N' and /// return it instead. T *GetOrInsertNode(T *N) { return static_cast(FoldingSetBase::GetOrInsertNode(N)); } /// FindNodeOrInsertPos - Look up the node specified by ID. If it exists, /// return it. If not, return the insertion token that will make insertion /// faster. T *FindNodeOrInsertPos(const FoldingSetNodeID &ID, void *&InsertPos) { return static_cast(FoldingSetBase::FindNodeOrInsertPos(ID, InsertPos)); } /// InsertNode - Insert the specified node into the folding set, knowing that /// it is not already in the folding set. InsertPos must be obtained from /// FindNodeOrInsertPos. void InsertNode(T *N, void *InsertPos) { FoldingSetBase::InsertNode(N, InsertPos); } /// InsertNode - Insert the specified node into the folding set, knowing that /// it is not already in the folding set. void InsertNode(T *N) { T *Inserted = GetOrInsertNode(N); (void)Inserted; assert(Inserted == N && "Node already inserted!"); } }; //===----------------------------------------------------------------------===// /// FoldingSet - This template class is used to instantiate a specialized /// implementation of the folding set to the node class T. T must be a /// subclass of FoldingSetNode and implement a Profile function. /// /// Note that this set type is movable and move-assignable. However, its /// moved-from state is not a valid state for anything other than /// move-assigning and destroying. This is primarily to enable movable APIs /// that incorporate these objects. template class FoldingSet final : public FoldingSetImpl { using Super = FoldingSetImpl; using Node = typename Super::Node; /// GetNodeProfile - Each instantiatation of the FoldingSet needs to provide a /// way to convert nodes into a unique specifier. void GetNodeProfile(Node *N, FoldingSetNodeID &ID) const override { T *TN = static_cast(N); FoldingSetTrait::Profile(*TN, ID); } /// NodeEquals - Instantiations may optionally provide a way to compare a /// node with a specified ID. bool NodeEquals(Node *N, const FoldingSetNodeID &ID, unsigned IDHash, FoldingSetNodeID &TempID) const override { T *TN = static_cast(N); return FoldingSetTrait::Equals(*TN, ID, IDHash, TempID); } /// ComputeNodeHash - Instantiations may optionally provide a way to compute a /// hash value directly from a node. unsigned ComputeNodeHash(Node *N, FoldingSetNodeID &TempID) const override { T *TN = static_cast(N); return FoldingSetTrait::ComputeHash(*TN, TempID); } public: explicit FoldingSet(unsigned Log2InitSize = 6) : Super(Log2InitSize) {} FoldingSet(FoldingSet &&Arg) = default; FoldingSet &operator=(FoldingSet &&RHS) = default; }; //===----------------------------------------------------------------------===// /// ContextualFoldingSet - This template class is a further refinement /// of FoldingSet which provides a context argument when calling /// Profile on its nodes. Currently, that argument is fixed at /// initialization time. /// /// T must be a subclass of FoldingSetNode and implement a Profile /// function with signature /// void Profile(FoldingSetNodeID &, Ctx); template class ContextualFoldingSet final : public FoldingSetImpl { // Unfortunately, this can't derive from FoldingSet because the // construction of the vtable for FoldingSet requires // FoldingSet::GetNodeProfile to be instantiated, which in turn // requires a single-argument T::Profile(). using Super = FoldingSetImpl; using Node = typename Super::Node; Ctx Context; /// GetNodeProfile - Each instantiatation of the FoldingSet needs to provide a /// way to convert nodes into a unique specifier. void GetNodeProfile(Node *N, FoldingSetNodeID &ID) const override { T *TN = static_cast(N); ContextualFoldingSetTrait::Profile(*TN, ID, Context); } bool NodeEquals(Node *N, const FoldingSetNodeID &ID, unsigned IDHash, FoldingSetNodeID &TempID) const override { T *TN = static_cast(N); return ContextualFoldingSetTrait::Equals(*TN, ID, IDHash, TempID, Context); } unsigned ComputeNodeHash(Node *N, FoldingSetNodeID &TempID) const override { T *TN = static_cast(N); return ContextualFoldingSetTrait::ComputeHash(*TN, TempID, Context); } public: explicit ContextualFoldingSet(Ctx Context, unsigned Log2InitSize = 6) : Super(Log2InitSize), Context(Context) {} Ctx getContext() const { return Context; } }; //===----------------------------------------------------------------------===// /// FoldingSetVector - This template class combines a FoldingSet and a vector /// to provide the interface of FoldingSet but with deterministic iteration /// order based on the insertion order. T must be a subclass of FoldingSetNode /// and implement a Profile function. template > class FoldingSetVector { FoldingSet Set; VectorT Vector; public: explicit FoldingSetVector(unsigned Log2InitSize = 6) : Set(Log2InitSize) {} using iterator = pointee_iterator; iterator begin() { return Vector.begin(); } iterator end() { return Vector.end(); } using const_iterator = pointee_iterator; const_iterator begin() const { return Vector.begin(); } const_iterator end() const { return Vector.end(); } /// clear - Remove all nodes from the folding set. void clear() { Set.clear(); Vector.clear(); } /// FindNodeOrInsertPos - Look up the node specified by ID. If it exists, /// return it. If not, return the insertion token that will make insertion /// faster. T *FindNodeOrInsertPos(const FoldingSetNodeID &ID, void *&InsertPos) { return Set.FindNodeOrInsertPos(ID, InsertPos); } /// GetOrInsertNode - If there is an existing simple Node exactly /// equal to the specified node, return it. Otherwise, insert 'N' and /// return it instead. T *GetOrInsertNode(T *N) { T *Result = Set.GetOrInsertNode(N); if (Result == N) Vector.push_back(N); return Result; } /// InsertNode - Insert the specified node into the folding set, knowing that /// it is not already in the folding set. InsertPos must be obtained from /// FindNodeOrInsertPos. void InsertNode(T *N, void *InsertPos) { Set.InsertNode(N, InsertPos); Vector.push_back(N); } /// InsertNode - Insert the specified node into the folding set, knowing that /// it is not already in the folding set. void InsertNode(T *N) { Set.InsertNode(N); Vector.push_back(N); } /// size - Returns the number of nodes in the folding set. unsigned size() const { return Set.size(); } /// empty - Returns true if there are no nodes in the folding set. bool empty() const { return Set.empty(); } }; //===----------------------------------------------------------------------===// /// FoldingSetIteratorImpl - This is the common iterator support shared by all /// folding sets, which knows how to walk the folding set hash table. class FoldingSetIteratorImpl { protected: FoldingSetNode *NodePtr; FoldingSetIteratorImpl(void **Bucket); void advance(); public: bool operator==(const FoldingSetIteratorImpl &RHS) const { return NodePtr == RHS.NodePtr; } bool operator!=(const FoldingSetIteratorImpl &RHS) const { return NodePtr != RHS.NodePtr; } }; template class FoldingSetIterator : public FoldingSetIteratorImpl { public: explicit FoldingSetIterator(void **Bucket) : FoldingSetIteratorImpl(Bucket) {} T &operator*() const { return *static_cast(NodePtr); } T *operator->() const { return static_cast(NodePtr); } inline FoldingSetIterator &operator++() { // Preincrement advance(); return *this; } FoldingSetIterator operator++(int) { // Postincrement FoldingSetIterator tmp = *this; ++*this; return tmp; } }; //===----------------------------------------------------------------------===// /// FoldingSetBucketIteratorImpl - This is the common bucket iterator support /// shared by all folding sets, which knows how to walk a particular bucket /// of a folding set hash table. class FoldingSetBucketIteratorImpl { protected: void *Ptr; explicit FoldingSetBucketIteratorImpl(void **Bucket); FoldingSetBucketIteratorImpl(void **Bucket, bool) : Ptr(Bucket) {} void advance() { void *Probe = static_cast(Ptr)->getNextInBucket(); uintptr_t x = reinterpret_cast(Probe) & ~0x1; Ptr = reinterpret_cast(x); } public: bool operator==(const FoldingSetBucketIteratorImpl &RHS) const { return Ptr == RHS.Ptr; } bool operator!=(const FoldingSetBucketIteratorImpl &RHS) const { return Ptr != RHS.Ptr; } }; template class FoldingSetBucketIterator : public FoldingSetBucketIteratorImpl { public: explicit FoldingSetBucketIterator(void **Bucket) : FoldingSetBucketIteratorImpl(Bucket) {} FoldingSetBucketIterator(void **Bucket, bool) : FoldingSetBucketIteratorImpl(Bucket, true) {} T &operator*() const { return *static_cast(Ptr); } T *operator->() const { return static_cast(Ptr); } inline FoldingSetBucketIterator &operator++() { // Preincrement advance(); return *this; } FoldingSetBucketIterator operator++(int) { // Postincrement FoldingSetBucketIterator tmp = *this; ++*this; return tmp; } }; //===----------------------------------------------------------------------===// /// FoldingSetNodeWrapper - This template class is used to "wrap" arbitrary /// types in an enclosing object so that they can be inserted into FoldingSets. template class FoldingSetNodeWrapper : public FoldingSetNode { T data; public: template explicit FoldingSetNodeWrapper(Ts &&... Args) : data(std::forward(Args)...) {} void Profile(FoldingSetNodeID &ID) { FoldingSetTrait::Profile(data, ID); } T &getValue() { return data; } const T &getValue() const { return data; } operator T&() { return data; } operator const T&() const { return data; } }; //===----------------------------------------------------------------------===// /// FastFoldingSetNode - This is a subclass of FoldingSetNode which stores /// a FoldingSetNodeID value rather than requiring the node to recompute it /// each time it is needed. This trades space for speed (which can be /// significant if the ID is long), and it also permits nodes to drop /// information that would otherwise only be required for recomputing an ID. class FastFoldingSetNode : public FoldingSetNode { FoldingSetNodeID FastID; protected: explicit FastFoldingSetNode(const FoldingSetNodeID &ID) : FastID(ID) {} public: void Profile(FoldingSetNodeID &ID) const { ID.AddNodeID(FastID); } }; //===----------------------------------------------------------------------===// // Partial specializations of FoldingSetTrait. template struct FoldingSetTrait { static inline void Profile(T *X, FoldingSetNodeID &ID) { ID.AddPointer(X); } }; template struct FoldingSetTrait> { static inline void Profile(const std::pair &P, FoldingSetNodeID &ID) { ID.Add(P.first); ID.Add(P.second); } }; } // end namespace llvm #endif // LLVM_ADT_FOLDINGSET_H binaryen-version_91/third_party/llvm-project/include/llvm/ADT/FunctionExtras.h000066400000000000000000000257371362402614000277760ustar00rootroot00000000000000//===- FunctionExtras.h - Function type erasure utilities -------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// \file /// This file provides a collection of function (or more generally, callable) /// type erasure utilities supplementing those provided by the standard library /// in ``. /// /// It provides `unique_function`, which works like `std::function` but supports /// move-only callable objects. /// /// Future plans: /// - Add a `function` that provides const, volatile, and ref-qualified support, /// which doesn't work with `std::function`. /// - Provide support for specifying multiple signatures to type erase callable /// objects with an overload set, such as those produced by generic lambdas. /// - Expand to include a copyable utility that directly replaces std::function /// but brings the above improvements. /// /// Note that LLVM's utilities are greatly simplified by not supporting /// allocators. /// /// If the standard library ever begins to provide comparable facilities we can /// consider switching to those. /// //===----------------------------------------------------------------------===// #ifndef LLVM_ADT_FUNCTION_EXTRAS_H #define LLVM_ADT_FUNCTION_EXTRAS_H #include "llvm/ADT/PointerIntPair.h" #include "llvm/ADT/PointerUnion.h" #include "llvm/Support/type_traits.h" #include namespace llvm { template class unique_function; template class unique_function { static constexpr size_t InlineStorageSize = sizeof(void *) * 3; // MSVC has a bug and ICEs if we give it a particular dependent value // expression as part of the `std::conditional` below. To work around this, // we build that into a template struct's constexpr bool. template struct IsSizeLessThanThresholdT { static constexpr bool value = sizeof(T) <= (2 * sizeof(void *)); }; // Provide a type function to map parameters that won't observe extra copies // or moves and which are small enough to likely pass in register to values // and all other types to l-value reference types. We use this to compute the // types used in our erased call utility to minimize copies and moves unless // doing so would force things unnecessarily into memory. // // The heuristic used is related to common ABI register passing conventions. // It doesn't have to be exact though, and in one way it is more strict // because we want to still be able to observe either moves *or* copies. template using AdjustedParamT = typename std::conditional< !std::is_reference::value && llvm::is_trivially_copy_constructible::value && llvm::is_trivially_move_constructible::value && IsSizeLessThanThresholdT::value, T, T &>::type; // The type of the erased function pointer we use as a callback to dispatch to // the stored callable when it is trivial to move and destroy. using CallPtrT = ReturnT (*)(void *CallableAddr, AdjustedParamT... Params); using MovePtrT = void (*)(void *LHSCallableAddr, void *RHSCallableAddr); using DestroyPtrT = void (*)(void *CallableAddr); /// A struct to hold a single trivial callback with sufficient alignment for /// our bitpacking. struct alignas(8) TrivialCallback { CallPtrT CallPtr; }; /// A struct we use to aggregate three callbacks when we need full set of /// operations. struct alignas(8) NonTrivialCallbacks { CallPtrT CallPtr; MovePtrT MovePtr; DestroyPtrT DestroyPtr; }; // Create a pointer union between either a pointer to a static trivial call // pointer in a struct or a pointer to a static struct of the call, move, and // destroy pointers. using CallbackPointerUnionT = PointerUnion; // The main storage buffer. This will either have a pointer to out-of-line // storage or an inline buffer storing the callable. union StorageUnionT { // For out-of-line storage we keep a pointer to the underlying storage and // the size. This is enough to deallocate the memory. struct OutOfLineStorageT { void *StoragePtr; size_t Size; size_t Alignment; } OutOfLineStorage; static_assert( sizeof(OutOfLineStorageT) <= InlineStorageSize, "Should always use all of the out-of-line storage for inline storage!"); // For in-line storage, we just provide an aligned character buffer. We // provide three pointers worth of storage here. typename std::aligned_storage::type InlineStorage; } StorageUnion; // A compressed pointer to either our dispatching callback or our table of // dispatching callbacks and the flag for whether the callable itself is // stored inline or not. PointerIntPair CallbackAndInlineFlag; bool isInlineStorage() const { return CallbackAndInlineFlag.getInt(); } bool isTrivialCallback() const { return CallbackAndInlineFlag.getPointer().template is(); } CallPtrT getTrivialCallback() const { return CallbackAndInlineFlag.getPointer().template get()->CallPtr; } NonTrivialCallbacks *getNonTrivialCallbacks() const { return CallbackAndInlineFlag.getPointer() .template get(); } void *getInlineStorage() { return &StorageUnion.InlineStorage; } void *getOutOfLineStorage() { return StorageUnion.OutOfLineStorage.StoragePtr; } size_t getOutOfLineStorageSize() const { return StorageUnion.OutOfLineStorage.Size; } size_t getOutOfLineStorageAlignment() const { return StorageUnion.OutOfLineStorage.Alignment; } void setOutOfLineStorage(void *Ptr, size_t Size, size_t Alignment) { StorageUnion.OutOfLineStorage = {Ptr, Size, Alignment}; } template static ReturnT CallImpl(void *CallableAddr, AdjustedParamT... Params) { return (*reinterpret_cast(CallableAddr))( std::forward(Params)...); } template static void MoveImpl(void *LHSCallableAddr, void *RHSCallableAddr) noexcept { new (LHSCallableAddr) CallableT(std::move(*reinterpret_cast(RHSCallableAddr))); } template static void DestroyImpl(void *CallableAddr) noexcept { reinterpret_cast(CallableAddr)->~CallableT(); } public: unique_function() = default; unique_function(std::nullptr_t /*null_callable*/) {} ~unique_function() { if (!CallbackAndInlineFlag.getPointer()) return; // Cache this value so we don't re-check it after type-erased operations. bool IsInlineStorage = isInlineStorage(); if (!isTrivialCallback()) getNonTrivialCallbacks()->DestroyPtr( IsInlineStorage ? getInlineStorage() : getOutOfLineStorage()); if (!IsInlineStorage) deallocate_buffer(getOutOfLineStorage(), getOutOfLineStorageSize(), getOutOfLineStorageAlignment()); } unique_function(unique_function &&RHS) noexcept { // Copy the callback and inline flag. CallbackAndInlineFlag = RHS.CallbackAndInlineFlag; // If the RHS is empty, just copying the above is sufficient. if (!RHS) return; if (!isInlineStorage()) { // The out-of-line case is easiest to move. StorageUnion.OutOfLineStorage = RHS.StorageUnion.OutOfLineStorage; } else if (isTrivialCallback()) { // Move is trivial, just memcpy the bytes across. memcpy(getInlineStorage(), RHS.getInlineStorage(), InlineStorageSize); } else { // Non-trivial move, so dispatch to a type-erased implementation. getNonTrivialCallbacks()->MovePtr(getInlineStorage(), RHS.getInlineStorage()); } // Clear the old callback and inline flag to get back to as-if-null. RHS.CallbackAndInlineFlag = {}; #ifndef NDEBUG // In debug builds, we also scribble across the rest of the storage. memset(RHS.getInlineStorage(), 0xAD, InlineStorageSize); #endif } unique_function &operator=(unique_function &&RHS) noexcept { if (this == &RHS) return *this; // Because we don't try to provide any exception safety guarantees we can // implement move assignment very simply by first destroying the current // object and then move-constructing over top of it. this->~unique_function(); new (this) unique_function(std::move(RHS)); return *this; } template unique_function(CallableT Callable) { bool IsInlineStorage = true; void *CallableAddr = getInlineStorage(); if (sizeof(CallableT) > InlineStorageSize || alignof(CallableT) > alignof(decltype(StorageUnion.InlineStorage))) { IsInlineStorage = false; // Allocate out-of-line storage. FIXME: Use an explicit alignment // parameter in C++17 mode. auto Size = sizeof(CallableT); auto Alignment = alignof(CallableT); CallableAddr = allocate_buffer(Size, Alignment); setOutOfLineStorage(CallableAddr, Size, Alignment); } // Now move into the storage. new (CallableAddr) CallableT(std::move(Callable)); // See if we can create a trivial callback. We need the callable to be // trivially moved and trivially destroyed so that we don't have to store // type erased callbacks for those operations. // // FIXME: We should use constexpr if here and below to avoid instantiating // the non-trivial static objects when unnecessary. While the linker should // remove them, it is still wasteful. if (llvm::is_trivially_move_constructible::value && std::is_trivially_destructible::value) { // We need to create a nicely aligned object. We use a static variable // for this because it is a trivial struct. static TrivialCallback Callback = { &CallImpl }; CallbackAndInlineFlag = {&Callback, IsInlineStorage}; return; } // Otherwise, we need to point at an object that contains all the different // type erased behaviors needed. Create a static instance of the struct type // here and then use a pointer to that. static NonTrivialCallbacks Callbacks = { &CallImpl, &MoveImpl, &DestroyImpl}; CallbackAndInlineFlag = {&Callbacks, IsInlineStorage}; } ReturnT operator()(ParamTs... Params) { void *CallableAddr = isInlineStorage() ? getInlineStorage() : getOutOfLineStorage(); return (isTrivialCallback() ? getTrivialCallback() : getNonTrivialCallbacks()->CallPtr)(CallableAddr, Params...); } explicit operator bool() const { return (bool)CallbackAndInlineFlag.getPointer(); } }; } // end namespace llvm #endif // LLVM_ADT_FUNCTION_H binaryen-version_91/third_party/llvm-project/include/llvm/ADT/Hashing.h000066400000000000000000000622521362402614000263740ustar00rootroot00000000000000//===-- llvm/ADT/Hashing.h - Utilities for hashing --------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file implements the newly proposed standard C++ interfaces for hashing // arbitrary data and building hash functions for user-defined types. This // interface was originally proposed in N3333[1] and is currently under review // for inclusion in a future TR and/or standard. // // The primary interfaces provide are comprised of one type and three functions: // // -- 'hash_code' class is an opaque type representing the hash code for some // data. It is the intended product of hashing, and can be used to implement // hash tables, checksumming, and other common uses of hashes. It is not an // integer type (although it can be converted to one) because it is risky // to assume much about the internals of a hash_code. In particular, each // execution of the program has a high probability of producing a different // hash_code for a given input. Thus their values are not stable to save or // persist, and should only be used during the execution for the // construction of hashing datastructures. // // -- 'hash_value' is a function designed to be overloaded for each // user-defined type which wishes to be used within a hashing context. It // should be overloaded within the user-defined type's namespace and found // via ADL. Overloads for primitive types are provided by this library. // // -- 'hash_combine' and 'hash_combine_range' are functions designed to aid // programmers in easily and intuitively combining a set of data into // a single hash_code for their object. They should only logically be used // within the implementation of a 'hash_value' routine or similar context. // // Note that 'hash_combine_range' contains very special logic for hashing // a contiguous array of integers or pointers. This logic is *extremely* fast, // on a modern Intel "Gainestown" Xeon (Nehalem uarch) @2.2 GHz, these were // benchmarked at over 6.5 GiB/s for large keys, and <20 cycles/hash for keys // under 32-bytes. // //===----------------------------------------------------------------------===// #ifndef LLVM_ADT_HASHING_H #define LLVM_ADT_HASHING_H #include "llvm/Support/DataTypes.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/SwapByteOrder.h" #include "llvm/Support/type_traits.h" #include #include #include #include #include namespace llvm { /// An opaque object representing a hash code. /// /// This object represents the result of hashing some entity. It is intended to /// be used to implement hashtables or other hashing-based data structures. /// While it wraps and exposes a numeric value, this value should not be /// trusted to be stable or predictable across processes or executions. /// /// In order to obtain the hash_code for an object 'x': /// \code /// using llvm::hash_value; /// llvm::hash_code code = hash_value(x); /// \endcode class hash_code { size_t value; public: /// Default construct a hash_code. /// Note that this leaves the value uninitialized. hash_code() = default; /// Form a hash code directly from a numerical value. hash_code(size_t value) : value(value) {} /// Convert the hash code to its numerical value for use. /*explicit*/ operator size_t() const { return value; } friend bool operator==(const hash_code &lhs, const hash_code &rhs) { return lhs.value == rhs.value; } friend bool operator!=(const hash_code &lhs, const hash_code &rhs) { return lhs.value != rhs.value; } /// Allow a hash_code to be directly run through hash_value. friend size_t hash_value(const hash_code &code) { return code.value; } }; /// Compute a hash_code for any integer value. /// /// Note that this function is intended to compute the same hash_code for /// a particular value without regard to the pre-promotion type. This is in /// contrast to hash_combine which may produce different hash_codes for /// differing argument types even if they would implicit promote to a common /// type without changing the value. template typename std::enable_if::value, hash_code>::type hash_value(T value); /// Compute a hash_code for a pointer's address. /// /// N.B.: This hashes the *address*. Not the value and not the type. template hash_code hash_value(const T *ptr); /// Compute a hash_code for a pair of objects. template hash_code hash_value(const std::pair &arg); /// Compute a hash_code for a standard string. template hash_code hash_value(const std::basic_string &arg); /// Override the execution seed with a fixed value. /// /// This hashing library uses a per-execution seed designed to change on each /// run with high probability in order to ensure that the hash codes are not /// attackable and to ensure that output which is intended to be stable does /// not rely on the particulars of the hash codes produced. /// /// That said, there are use cases where it is important to be able to /// reproduce *exactly* a specific behavior. To that end, we provide a function /// which will forcibly set the seed to a fixed value. This must be done at the /// start of the program, before any hashes are computed. Also, it cannot be /// undone. This makes it thread-hostile and very hard to use outside of /// immediately on start of a simple program designed for reproducible /// behavior. void set_fixed_execution_hash_seed(uint64_t fixed_value); // All of the implementation details of actually computing the various hash // code values are held within this namespace. These routines are included in // the header file mainly to allow inlining and constant propagation. namespace hashing { namespace detail { inline uint64_t fetch64(const char *p) { uint64_t result; memcpy(&result, p, sizeof(result)); if (sys::IsBigEndianHost) sys::swapByteOrder(result); return result; } inline uint32_t fetch32(const char *p) { uint32_t result; memcpy(&result, p, sizeof(result)); if (sys::IsBigEndianHost) sys::swapByteOrder(result); return result; } /// Some primes between 2^63 and 2^64 for various uses. static const uint64_t k0 = 0xc3a5c85c97cb3127ULL; static const uint64_t k1 = 0xb492b66fbe98f273ULL; static const uint64_t k2 = 0x9ae16a3b2f90404fULL; static const uint64_t k3 = 0xc949d7c7509e6557ULL; /// Bitwise right rotate. /// Normally this will compile to a single instruction, especially if the /// shift is a manifest constant. inline uint64_t rotate(uint64_t val, size_t shift) { // Avoid shifting by 64: doing so yields an undefined result. return shift == 0 ? val : ((val >> shift) | (val << (64 - shift))); } inline uint64_t shift_mix(uint64_t val) { return val ^ (val >> 47); } inline uint64_t hash_16_bytes(uint64_t low, uint64_t high) { // Murmur-inspired hashing. const uint64_t kMul = 0x9ddfea08eb382d69ULL; uint64_t a = (low ^ high) * kMul; a ^= (a >> 47); uint64_t b = (high ^ a) * kMul; b ^= (b >> 47); b *= kMul; return b; } inline uint64_t hash_1to3_bytes(const char *s, size_t len, uint64_t seed) { uint8_t a = s[0]; uint8_t b = s[len >> 1]; uint8_t c = s[len - 1]; uint32_t y = static_cast(a) + (static_cast(b) << 8); uint32_t z = static_cast(len) + (static_cast(c) << 2); return shift_mix(y * k2 ^ z * k3 ^ seed) * k2; } inline uint64_t hash_4to8_bytes(const char *s, size_t len, uint64_t seed) { uint64_t a = fetch32(s); return hash_16_bytes(len + (a << 3), seed ^ fetch32(s + len - 4)); } inline uint64_t hash_9to16_bytes(const char *s, size_t len, uint64_t seed) { uint64_t a = fetch64(s); uint64_t b = fetch64(s + len - 8); return hash_16_bytes(seed ^ a, rotate(b + len, len)) ^ b; } inline uint64_t hash_17to32_bytes(const char *s, size_t len, uint64_t seed) { uint64_t a = fetch64(s) * k1; uint64_t b = fetch64(s + 8); uint64_t c = fetch64(s + len - 8) * k2; uint64_t d = fetch64(s + len - 16) * k0; return hash_16_bytes(rotate(a - b, 43) + rotate(c ^ seed, 30) + d, a + rotate(b ^ k3, 20) - c + len + seed); } inline uint64_t hash_33to64_bytes(const char *s, size_t len, uint64_t seed) { uint64_t z = fetch64(s + 24); uint64_t a = fetch64(s) + (len + fetch64(s + len - 16)) * k0; uint64_t b = rotate(a + z, 52); uint64_t c = rotate(a, 37); a += fetch64(s + 8); c += rotate(a, 7); a += fetch64(s + 16); uint64_t vf = a + z; uint64_t vs = b + rotate(a, 31) + c; a = fetch64(s + 16) + fetch64(s + len - 32); z = fetch64(s + len - 8); b = rotate(a + z, 52); c = rotate(a, 37); a += fetch64(s + len - 24); c += rotate(a, 7); a += fetch64(s + len - 16); uint64_t wf = a + z; uint64_t ws = b + rotate(a, 31) + c; uint64_t r = shift_mix((vf + ws) * k2 + (wf + vs) * k0); return shift_mix((seed ^ (r * k0)) + vs) * k2; } inline uint64_t hash_short(const char *s, size_t length, uint64_t seed) { if (length >= 4 && length <= 8) return hash_4to8_bytes(s, length, seed); if (length > 8 && length <= 16) return hash_9to16_bytes(s, length, seed); if (length > 16 && length <= 32) return hash_17to32_bytes(s, length, seed); if (length > 32) return hash_33to64_bytes(s, length, seed); if (length != 0) return hash_1to3_bytes(s, length, seed); return k2 ^ seed; } /// The intermediate state used during hashing. /// Currently, the algorithm for computing hash codes is based on CityHash and /// keeps 56 bytes of arbitrary state. struct hash_state { uint64_t h0 = 0, h1 = 0, h2 = 0, h3 = 0, h4 = 0, h5 = 0, h6 = 0; /// Create a new hash_state structure and initialize it based on the /// seed and the first 64-byte chunk. /// This effectively performs the initial mix. static hash_state create(const char *s, uint64_t seed) { hash_state state = { 0, seed, hash_16_bytes(seed, k1), rotate(seed ^ k1, 49), seed * k1, shift_mix(seed), 0 }; state.h6 = hash_16_bytes(state.h4, state.h5); state.mix(s); return state; } /// Mix 32-bytes from the input sequence into the 16-bytes of 'a' /// and 'b', including whatever is already in 'a' and 'b'. static void mix_32_bytes(const char *s, uint64_t &a, uint64_t &b) { a += fetch64(s); uint64_t c = fetch64(s + 24); b = rotate(b + a + c, 21); uint64_t d = a; a += fetch64(s + 8) + fetch64(s + 16); b += rotate(a, 44) + d; a += c; } /// Mix in a 64-byte buffer of data. /// We mix all 64 bytes even when the chunk length is smaller, but we /// record the actual length. void mix(const char *s) { h0 = rotate(h0 + h1 + h3 + fetch64(s + 8), 37) * k1; h1 = rotate(h1 + h4 + fetch64(s + 48), 42) * k1; h0 ^= h6; h1 += h3 + fetch64(s + 40); h2 = rotate(h2 + h5, 33) * k1; h3 = h4 * k1; h4 = h0 + h5; mix_32_bytes(s, h3, h4); h5 = h2 + h6; h6 = h1 + fetch64(s + 16); mix_32_bytes(s + 32, h5, h6); std::swap(h2, h0); } /// Compute the final 64-bit hash code value based on the current /// state and the length of bytes hashed. uint64_t finalize(size_t length) { return hash_16_bytes(hash_16_bytes(h3, h5) + shift_mix(h1) * k1 + h2, hash_16_bytes(h4, h6) + shift_mix(length) * k1 + h0); } }; /// A global, fixed seed-override variable. /// /// This variable can be set using the \see llvm::set_fixed_execution_seed /// function. See that function for details. Do not, under any circumstances, /// set or read this variable. extern uint64_t fixed_seed_override; inline uint64_t get_execution_seed() { // FIXME: This needs to be a per-execution seed. This is just a placeholder // implementation. Switching to a per-execution seed is likely to flush out // instability bugs and so will happen as its own commit. // // However, if there is a fixed seed override set the first time this is // called, return that instead of the per-execution seed. const uint64_t seed_prime = 0xff51afd7ed558ccdULL; static uint64_t seed = fixed_seed_override ? fixed_seed_override : seed_prime; return seed; } /// Trait to indicate whether a type's bits can be hashed directly. /// /// A type trait which is true if we want to combine values for hashing by /// reading the underlying data. It is false if values of this type must /// first be passed to hash_value, and the resulting hash_codes combined. // // FIXME: We want to replace is_integral_or_enum and is_pointer here with // a predicate which asserts that comparing the underlying storage of two // values of the type for equality is equivalent to comparing the two values // for equality. For all the platforms we care about, this holds for integers // and pointers, but there are platforms where it doesn't and we would like to // support user-defined types which happen to satisfy this property. template struct is_hashable_data : std::integral_constant::value || std::is_pointer::value) && 64 % sizeof(T) == 0)> {}; // Special case std::pair to detect when both types are viable and when there // is no alignment-derived padding in the pair. This is a bit of a lie because // std::pair isn't truly POD, but it's close enough in all reasonable // implementations for our use case of hashing the underlying data. template struct is_hashable_data > : std::integral_constant::value && is_hashable_data::value && (sizeof(T) + sizeof(U)) == sizeof(std::pair))> {}; /// Helper to get the hashable data representation for a type. /// This variant is enabled when the type itself can be used. template typename std::enable_if::value, T>::type get_hashable_data(const T &value) { return value; } /// Helper to get the hashable data representation for a type. /// This variant is enabled when we must first call hash_value and use the /// result as our data. template typename std::enable_if::value, size_t>::type get_hashable_data(const T &value) { using ::llvm::hash_value; return hash_value(value); } /// Helper to store data from a value into a buffer and advance the /// pointer into that buffer. /// /// This routine first checks whether there is enough space in the provided /// buffer, and if not immediately returns false. If there is space, it /// copies the underlying bytes of value into the buffer, advances the /// buffer_ptr past the copied bytes, and returns true. template bool store_and_advance(char *&buffer_ptr, char *buffer_end, const T& value, size_t offset = 0) { size_t store_size = sizeof(value) - offset; if (buffer_ptr + store_size > buffer_end) return false; const char *value_data = reinterpret_cast(&value); memcpy(buffer_ptr, value_data + offset, store_size); buffer_ptr += store_size; return true; } /// Implement the combining of integral values into a hash_code. /// /// This overload is selected when the value type of the iterator is /// integral. Rather than computing a hash_code for each object and then /// combining them, this (as an optimization) directly combines the integers. template hash_code hash_combine_range_impl(InputIteratorT first, InputIteratorT last) { const uint64_t seed = get_execution_seed(); char buffer[64], *buffer_ptr = buffer; char *const buffer_end = std::end(buffer); while (first != last && store_and_advance(buffer_ptr, buffer_end, get_hashable_data(*first))) ++first; if (first == last) return hash_short(buffer, buffer_ptr - buffer, seed); assert(buffer_ptr == buffer_end); hash_state state = state.create(buffer, seed); size_t length = 64; while (first != last) { // Fill up the buffer. We don't clear it, which re-mixes the last round // when only a partial 64-byte chunk is left. buffer_ptr = buffer; while (first != last && store_and_advance(buffer_ptr, buffer_end, get_hashable_data(*first))) ++first; // Rotate the buffer if we did a partial fill in order to simulate doing // a mix of the last 64-bytes. That is how the algorithm works when we // have a contiguous byte sequence, and we want to emulate that here. std::rotate(buffer, buffer_ptr, buffer_end); // Mix this chunk into the current state. state.mix(buffer); length += buffer_ptr - buffer; }; return state.finalize(length); } /// Implement the combining of integral values into a hash_code. /// /// This overload is selected when the value type of the iterator is integral /// and when the input iterator is actually a pointer. Rather than computing /// a hash_code for each object and then combining them, this (as an /// optimization) directly combines the integers. Also, because the integers /// are stored in contiguous memory, this routine avoids copying each value /// and directly reads from the underlying memory. template typename std::enable_if::value, hash_code>::type hash_combine_range_impl(ValueT *first, ValueT *last) { const uint64_t seed = get_execution_seed(); const char *s_begin = reinterpret_cast(first); const char *s_end = reinterpret_cast(last); const size_t length = std::distance(s_begin, s_end); if (length <= 64) return hash_short(s_begin, length, seed); const char *s_aligned_end = s_begin + (length & ~63); hash_state state = state.create(s_begin, seed); s_begin += 64; while (s_begin != s_aligned_end) { state.mix(s_begin); s_begin += 64; } if (length & 63) state.mix(s_end - 64); return state.finalize(length); } } // namespace detail } // namespace hashing /// Compute a hash_code for a sequence of values. /// /// This hashes a sequence of values. It produces the same hash_code as /// 'hash_combine(a, b, c, ...)', but can run over arbitrary sized sequences /// and is significantly faster given pointers and types which can be hashed as /// a sequence of bytes. template hash_code hash_combine_range(InputIteratorT first, InputIteratorT last) { return ::llvm::hashing::detail::hash_combine_range_impl(first, last); } // Implementation details for hash_combine. namespace hashing { namespace detail { /// Helper class to manage the recursive combining of hash_combine /// arguments. /// /// This class exists to manage the state and various calls involved in the /// recursive combining of arguments used in hash_combine. It is particularly /// useful at minimizing the code in the recursive calls to ease the pain /// caused by a lack of variadic functions. struct hash_combine_recursive_helper { char buffer[64] = {}; hash_state state; const uint64_t seed; public: /// Construct a recursive hash combining helper. /// /// This sets up the state for a recursive hash combine, including getting /// the seed and buffer setup. hash_combine_recursive_helper() : seed(get_execution_seed()) {} /// Combine one chunk of data into the current in-flight hash. /// /// This merges one chunk of data into the hash. First it tries to buffer /// the data. If the buffer is full, it hashes the buffer into its /// hash_state, empties it, and then merges the new chunk in. This also /// handles cases where the data straddles the end of the buffer. template char *combine_data(size_t &length, char *buffer_ptr, char *buffer_end, T data) { if (!store_and_advance(buffer_ptr, buffer_end, data)) { // Check for skew which prevents the buffer from being packed, and do // a partial store into the buffer to fill it. This is only a concern // with the variadic combine because that formation can have varying // argument types. size_t partial_store_size = buffer_end - buffer_ptr; memcpy(buffer_ptr, &data, partial_store_size); // If the store fails, our buffer is full and ready to hash. We have to // either initialize the hash state (on the first full buffer) or mix // this buffer into the existing hash state. Length tracks the *hashed* // length, not the buffered length. if (length == 0) { state = state.create(buffer, seed); length = 64; } else { // Mix this chunk into the current state and bump length up by 64. state.mix(buffer); length += 64; } // Reset the buffer_ptr to the head of the buffer for the next chunk of // data. buffer_ptr = buffer; // Try again to store into the buffer -- this cannot fail as we only // store types smaller than the buffer. if (!store_and_advance(buffer_ptr, buffer_end, data, partial_store_size)) llvm_unreachable("buffer smaller than stored type"); } return buffer_ptr; } /// Recursive, variadic combining method. /// /// This function recurses through each argument, combining that argument /// into a single hash. template hash_code combine(size_t length, char *buffer_ptr, char *buffer_end, const T &arg, const Ts &...args) { buffer_ptr = combine_data(length, buffer_ptr, buffer_end, get_hashable_data(arg)); // Recurse to the next argument. return combine(length, buffer_ptr, buffer_end, args...); } /// Base case for recursive, variadic combining. /// /// The base case when combining arguments recursively is reached when all /// arguments have been handled. It flushes the remaining buffer and /// constructs a hash_code. hash_code combine(size_t length, char *buffer_ptr, char *buffer_end) { // Check whether the entire set of values fit in the buffer. If so, we'll // use the optimized short hashing routine and skip state entirely. if (length == 0) return hash_short(buffer, buffer_ptr - buffer, seed); // Mix the final buffer, rotating it if we did a partial fill in order to // simulate doing a mix of the last 64-bytes. That is how the algorithm // works when we have a contiguous byte sequence, and we want to emulate // that here. std::rotate(buffer, buffer_ptr, buffer_end); // Mix this chunk into the current state. state.mix(buffer); length += buffer_ptr - buffer; return state.finalize(length); } }; } // namespace detail } // namespace hashing /// Combine values into a single hash_code. /// /// This routine accepts a varying number of arguments of any type. It will /// attempt to combine them into a single hash_code. For user-defined types it /// attempts to call a \see hash_value overload (via ADL) for the type. For /// integer and pointer types it directly combines their data into the /// resulting hash_code. /// /// The result is suitable for returning from a user's hash_value /// *implementation* for their user-defined type. Consumers of a type should /// *not* call this routine, they should instead call 'hash_value'. template hash_code hash_combine(const Ts &...args) { // Recursively hash each argument using a helper class. ::llvm::hashing::detail::hash_combine_recursive_helper helper; return helper.combine(0, helper.buffer, helper.buffer + 64, args...); } // Implementation details for implementations of hash_value overloads provided // here. namespace hashing { namespace detail { /// Helper to hash the value of a single integer. /// /// Overloads for smaller integer types are not provided to ensure consistent /// behavior in the presence of integral promotions. Essentially, /// "hash_value('4')" and "hash_value('0' + 4)" should be the same. inline hash_code hash_integer_value(uint64_t value) { // Similar to hash_4to8_bytes but using a seed instead of length. const uint64_t seed = get_execution_seed(); const char *s = reinterpret_cast(&value); const uint64_t a = fetch32(s); return hash_16_bytes(seed + (a << 3), fetch32(s + 4)); } } // namespace detail } // namespace hashing // Declared and documented above, but defined here so that any of the hashing // infrastructure is available. template typename std::enable_if::value, hash_code>::type hash_value(T value) { return ::llvm::hashing::detail::hash_integer_value( static_cast(value)); } // Declared and documented above, but defined here so that any of the hashing // infrastructure is available. template hash_code hash_value(const T *ptr) { return ::llvm::hashing::detail::hash_integer_value( reinterpret_cast(ptr)); } // Declared and documented above, but defined here so that any of the hashing // infrastructure is available. template hash_code hash_value(const std::pair &arg) { return hash_combine(arg.first, arg.second); } // Declared and documented above, but defined here so that any of the hashing // infrastructure is available. template hash_code hash_value(const std::basic_string &arg) { return hash_combine_range(arg.begin(), arg.end()); } } // namespace llvm #endif binaryen-version_91/third_party/llvm-project/include/llvm/ADT/MapVector.h000066400000000000000000000174531362402614000267160ustar00rootroot00000000000000//===- llvm/ADT/MapVector.h - Map w/ deterministic value order --*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file implements a map that provides insertion order iteration. The // interface is purposefully minimal. The key is assumed to be cheap to copy // and 2 copies are kept, one for indexing in a DenseMap, one for iteration in // a std::vector. // //===----------------------------------------------------------------------===// #ifndef LLVM_ADT_MAPVECTOR_H #define LLVM_ADT_MAPVECTOR_H #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" #include #include #include #include #include #include #include namespace llvm { /// This class implements a map that also provides access to all stored values /// in a deterministic order. The values are kept in a std::vector and the /// mapping is done with DenseMap from Keys to indexes in that vector. template, typename VectorType = std::vector>> class MapVector { MapType Map; VectorType Vector; static_assert( std::is_integral::value, "The mapped_type of the specified Map must be an integral type"); public: using value_type = typename VectorType::value_type; using size_type = typename VectorType::size_type; using iterator = typename VectorType::iterator; using const_iterator = typename VectorType::const_iterator; using reverse_iterator = typename VectorType::reverse_iterator; using const_reverse_iterator = typename VectorType::const_reverse_iterator; /// Clear the MapVector and return the underlying vector. VectorType takeVector() { Map.clear(); return std::move(Vector); } size_type size() const { return Vector.size(); } /// Grow the MapVector so that it can contain at least \p NumEntries items /// before resizing again. void reserve(size_type NumEntries) { Map.reserve(NumEntries); Vector.reserve(NumEntries); } iterator begin() { return Vector.begin(); } const_iterator begin() const { return Vector.begin(); } iterator end() { return Vector.end(); } const_iterator end() const { return Vector.end(); } reverse_iterator rbegin() { return Vector.rbegin(); } const_reverse_iterator rbegin() const { return Vector.rbegin(); } reverse_iterator rend() { return Vector.rend(); } const_reverse_iterator rend() const { return Vector.rend(); } bool empty() const { return Vector.empty(); } std::pair &front() { return Vector.front(); } const std::pair &front() const { return Vector.front(); } std::pair &back() { return Vector.back(); } const std::pair &back() const { return Vector.back(); } void clear() { Map.clear(); Vector.clear(); } void swap(MapVector &RHS) { std::swap(Map, RHS.Map); std::swap(Vector, RHS.Vector); } ValueT &operator[](const KeyT &Key) { std::pair Pair = std::make_pair(Key, 0); std::pair Result = Map.insert(Pair); auto &I = Result.first->second; if (Result.second) { Vector.push_back(std::make_pair(Key, ValueT())); I = Vector.size() - 1; } return Vector[I].second; } // Returns a copy of the value. Only allowed if ValueT is copyable. ValueT lookup(const KeyT &Key) const { static_assert(std::is_copy_constructible::value, "Cannot call lookup() if ValueT is not copyable."); typename MapType::const_iterator Pos = Map.find(Key); return Pos == Map.end()? ValueT() : Vector[Pos->second].second; } std::pair insert(const std::pair &KV) { std::pair Pair = std::make_pair(KV.first, 0); std::pair Result = Map.insert(Pair); auto &I = Result.first->second; if (Result.second) { Vector.push_back(std::make_pair(KV.first, KV.second)); I = Vector.size() - 1; return std::make_pair(std::prev(end()), true); } return std::make_pair(begin() + I, false); } std::pair insert(std::pair &&KV) { // Copy KV.first into the map, then move it into the vector. std::pair Pair = std::make_pair(KV.first, 0); std::pair Result = Map.insert(Pair); auto &I = Result.first->second; if (Result.second) { Vector.push_back(std::move(KV)); I = Vector.size() - 1; return std::make_pair(std::prev(end()), true); } return std::make_pair(begin() + I, false); } size_type count(const KeyT &Key) const { typename MapType::const_iterator Pos = Map.find(Key); return Pos == Map.end()? 0 : 1; } iterator find(const KeyT &Key) { typename MapType::const_iterator Pos = Map.find(Key); return Pos == Map.end()? Vector.end() : (Vector.begin() + Pos->second); } const_iterator find(const KeyT &Key) const { typename MapType::const_iterator Pos = Map.find(Key); return Pos == Map.end()? Vector.end() : (Vector.begin() + Pos->second); } /// Remove the last element from the vector. void pop_back() { typename MapType::iterator Pos = Map.find(Vector.back().first); Map.erase(Pos); Vector.pop_back(); } /// Remove the element given by Iterator. /// /// Returns an iterator to the element following the one which was removed, /// which may be end(). /// /// \note This is a deceivingly expensive operation (linear time). It's /// usually better to use \a remove_if() if possible. typename VectorType::iterator erase(typename VectorType::iterator Iterator) { Map.erase(Iterator->first); auto Next = Vector.erase(Iterator); if (Next == Vector.end()) return Next; // Update indices in the map. size_t Index = Next - Vector.begin(); for (auto &I : Map) { assert(I.second != Index && "Index was already erased!"); if (I.second > Index) --I.second; } return Next; } /// Remove all elements with the key value Key. /// /// Returns the number of elements removed. size_type erase(const KeyT &Key) { auto Iterator = find(Key); if (Iterator == end()) return 0; erase(Iterator); return 1; } /// Remove the elements that match the predicate. /// /// Erase all elements that match \c Pred in a single pass. Takes linear /// time. template void remove_if(Predicate Pred); }; template template void MapVector::remove_if(Function Pred) { auto O = Vector.begin(); for (auto I = O, E = Vector.end(); I != E; ++I) { if (Pred(*I)) { // Erase from the map. Map.erase(I->first); continue; } if (I != O) { // Move the value and update the index in the map. *O = std::move(*I); Map[O->first] = O - Vector.begin(); } ++O; } // Erase trailing entries in the vector. Vector.erase(O, Vector.end()); } /// A MapVector that performs no allocations if smaller than a certain /// size. template struct SmallMapVector : MapVector, SmallVector, N>> { }; } // end namespace llvm #endif // LLVM_ADT_MAPVECTOR_H binaryen-version_91/third_party/llvm-project/include/llvm/ADT/None.h000066400000000000000000000017271362402614000257120ustar00rootroot00000000000000//===-- None.h - Simple null value for implicit construction ------*- C++ -*-=// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file provides None, an enumerator for use in implicit constructors // of various (usually templated) types to make such construction more // terse. // //===----------------------------------------------------------------------===// #ifndef LLVM_ADT_NONE_H #define LLVM_ADT_NONE_H namespace llvm { /// A simple null object to allow implicit construction of Optional /// and similar types without having to spell out the specialization's name. // (constant value 1 in an attempt to workaround MSVC build issue... ) enum class NoneType { None = 1 }; const NoneType None = NoneType::None; } #endif binaryen-version_91/third_party/llvm-project/include/llvm/ADT/Optional.h000066400000000000000000000244651362402614000266040ustar00rootroot00000000000000//===- Optional.h - Simple variant for passing optional values --*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file provides Optional, a template class modeled in the spirit of // OCaml's 'opt' variant. The idea is to strongly type whether or not // a value can be optional. // //===----------------------------------------------------------------------===// #ifndef LLVM_ADT_OPTIONAL_H #define LLVM_ADT_OPTIONAL_H #include "llvm/ADT/None.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/type_traits.h" #include #include #include #include namespace llvm { class raw_ostream; namespace optional_detail { struct in_place_t {}; /// Storage for any type. template ::value> class OptionalStorage { union { char empty; T value; }; bool hasVal; public: ~OptionalStorage() { reset(); } OptionalStorage() noexcept : empty(), hasVal(false) {} OptionalStorage(OptionalStorage const &other) : OptionalStorage() { if (other.hasValue()) { emplace(other.value); } } OptionalStorage(OptionalStorage &&other) : OptionalStorage() { if (other.hasValue()) { emplace(std::move(other.value)); } } template explicit OptionalStorage(in_place_t, Args &&... args) : value(std::forward(args)...), hasVal(true) {} void reset() noexcept { if (hasVal) { value.~T(); hasVal = false; } } bool hasValue() const noexcept { return hasVal; } T &getValue() LLVM_LVALUE_FUNCTION noexcept { assert(hasVal); return value; } T const &getValue() const LLVM_LVALUE_FUNCTION noexcept { assert(hasVal); return value; } #if LLVM_HAS_RVALUE_REFERENCE_THIS T &&getValue() && noexcept { assert(hasVal); return std::move(value); } #endif template void emplace(Args &&... args) { reset(); ::new ((void *)std::addressof(value)) T(std::forward(args)...); hasVal = true; } OptionalStorage &operator=(T const &y) { if (hasValue()) { value = y; } else { ::new ((void *)std::addressof(value)) T(y); hasVal = true; } return *this; } OptionalStorage &operator=(T &&y) { if (hasValue()) { value = std::move(y); } else { ::new ((void *)std::addressof(value)) T(std::move(y)); hasVal = true; } return *this; } OptionalStorage &operator=(OptionalStorage const &other) { if (other.hasValue()) { if (hasValue()) { value = other.value; } else { ::new ((void *)std::addressof(value)) T(other.value); hasVal = true; } } else { reset(); } return *this; } OptionalStorage &operator=(OptionalStorage &&other) { if (other.hasValue()) { if (hasValue()) { value = std::move(other.value); } else { ::new ((void *)std::addressof(value)) T(std::move(other.value)); hasVal = true; } } else { reset(); } return *this; } }; template class OptionalStorage { union { char empty; T value; }; bool hasVal = false; public: ~OptionalStorage() = default; OptionalStorage() noexcept : empty{} {} OptionalStorage(OptionalStorage const &other) = default; OptionalStorage(OptionalStorage &&other) = default; OptionalStorage &operator=(OptionalStorage const &other) = default; OptionalStorage &operator=(OptionalStorage &&other) = default; template explicit OptionalStorage(in_place_t, Args &&... args) : value(std::forward(args)...), hasVal(true) {} void reset() noexcept { if (hasVal) { value.~T(); hasVal = false; } } bool hasValue() const noexcept { return hasVal; } T &getValue() LLVM_LVALUE_FUNCTION noexcept { assert(hasVal); return value; } T const &getValue() const LLVM_LVALUE_FUNCTION noexcept { assert(hasVal); return value; } #if LLVM_HAS_RVALUE_REFERENCE_THIS T &&getValue() && noexcept { assert(hasVal); return std::move(value); } #endif template void emplace(Args &&... args) { reset(); ::new ((void *)std::addressof(value)) T(std::forward(args)...); hasVal = true; } OptionalStorage &operator=(T const &y) { if (hasValue()) { value = y; } else { ::new ((void *)std::addressof(value)) T(y); hasVal = true; } return *this; } OptionalStorage &operator=(T &&y) { if (hasValue()) { value = std::move(y); } else { ::new ((void *)std::addressof(value)) T(std::move(y)); hasVal = true; } return *this; } }; } // namespace optional_detail template class Optional { optional_detail::OptionalStorage Storage; public: using value_type = T; constexpr Optional() {} constexpr Optional(NoneType) {} Optional(const T &y) : Storage(optional_detail::in_place_t{}, y) {} Optional(const Optional &O) = default; Optional(T &&y) : Storage(optional_detail::in_place_t{}, std::move(y)) {} Optional(Optional &&O) = default; Optional &operator=(T &&y) { Storage = std::move(y); return *this; } Optional &operator=(Optional &&O) = default; /// Create a new object by constructing it in place with the given arguments. template void emplace(ArgTypes &&... Args) { Storage.emplace(std::forward(Args)...); } static inline Optional create(const T *y) { return y ? Optional(*y) : Optional(); } Optional &operator=(const T &y) { Storage = y; return *this; } Optional &operator=(const Optional &O) = default; void reset() { Storage.reset(); } const T *getPointer() const { return &Storage.getValue(); } T *getPointer() { return &Storage.getValue(); } const T &getValue() const LLVM_LVALUE_FUNCTION { return Storage.getValue(); } T &getValue() LLVM_LVALUE_FUNCTION { return Storage.getValue(); } explicit operator bool() const { return hasValue(); } bool hasValue() const { return Storage.hasValue(); } const T *operator->() const { return getPointer(); } T *operator->() { return getPointer(); } const T &operator*() const LLVM_LVALUE_FUNCTION { return getValue(); } T &operator*() LLVM_LVALUE_FUNCTION { return getValue(); } template constexpr T getValueOr(U &&value) const LLVM_LVALUE_FUNCTION { return hasValue() ? getValue() : std::forward(value); } #if LLVM_HAS_RVALUE_REFERENCE_THIS T &&getValue() && { return std::move(Storage.getValue()); } T &&operator*() && { return std::move(Storage.getValue()); } template T getValueOr(U &&value) && { return hasValue() ? std::move(getValue()) : std::forward(value); } #endif }; template bool operator==(const Optional &X, const Optional &Y) { if (X && Y) return *X == *Y; return X.hasValue() == Y.hasValue(); } template bool operator!=(const Optional &X, const Optional &Y) { return !(X == Y); } template bool operator<(const Optional &X, const Optional &Y) { if (X && Y) return *X < *Y; return X.hasValue() < Y.hasValue(); } template bool operator<=(const Optional &X, const Optional &Y) { return !(Y < X); } template bool operator>(const Optional &X, const Optional &Y) { return Y < X; } template bool operator>=(const Optional &X, const Optional &Y) { return !(X < Y); } template bool operator==(const Optional &X, NoneType) { return !X; } template bool operator==(NoneType, const Optional &X) { return X == None; } template bool operator!=(const Optional &X, NoneType) { return !(X == None); } template bool operator!=(NoneType, const Optional &X) { return X != None; } template bool operator<(const Optional &X, NoneType) { return false; } template bool operator<(NoneType, const Optional &X) { return X.hasValue(); } template bool operator<=(const Optional &X, NoneType) { return !(None < X); } template bool operator<=(NoneType, const Optional &X) { return !(X < None); } template bool operator>(const Optional &X, NoneType) { return None < X; } template bool operator>(NoneType, const Optional &X) { return X < None; } template bool operator>=(const Optional &X, NoneType) { return None <= X; } template bool operator>=(NoneType, const Optional &X) { return X <= None; } template bool operator==(const Optional &X, const T &Y) { return X && *X == Y; } template bool operator==(const T &X, const Optional &Y) { return Y && X == *Y; } template bool operator!=(const Optional &X, const T &Y) { return !(X == Y); } template bool operator!=(const T &X, const Optional &Y) { return !(X == Y); } template bool operator<(const Optional &X, const T &Y) { return !X || *X < Y; } template bool operator<(const T &X, const Optional &Y) { return Y && X < *Y; } template bool operator<=(const Optional &X, const T &Y) { return !(Y < X); } template bool operator<=(const T &X, const Optional &Y) { return !(Y < X); } template bool operator>(const Optional &X, const T &Y) { return Y < X; } template bool operator>(const T &X, const Optional &Y) { return Y < X; } template bool operator>=(const Optional &X, const T &Y) { return !(X < Y); } template bool operator>=(const T &X, const Optional &Y) { return !(X < Y); } raw_ostream &operator<<(raw_ostream &OS, NoneType); template () << std::declval())> raw_ostream &operator<<(raw_ostream &OS, const Optional &O) { if (O) OS << *O; else OS << None; return OS; } } // end namespace llvm #endif // LLVM_ADT_OPTIONAL_H binaryen-version_91/third_party/llvm-project/include/llvm/ADT/PointerIntPair.h000066400000000000000000000212661362402614000277220ustar00rootroot00000000000000//===- llvm/ADT/PointerIntPair.h - Pair for pointer and int -----*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file defines the PointerIntPair class. // //===----------------------------------------------------------------------===// #ifndef LLVM_ADT_POINTERINTPAIR_H #define LLVM_ADT_POINTERINTPAIR_H #include "llvm/Support/Compiler.h" #include "llvm/Support/PointerLikeTypeTraits.h" #include "llvm/Support/type_traits.h" #include #include #include namespace llvm { template struct DenseMapInfo; template struct PointerIntPairInfo; /// PointerIntPair - This class implements a pair of a pointer and small /// integer. It is designed to represent this in the space required by one /// pointer by bitmangling the integer into the low part of the pointer. This /// can only be done for small integers: typically up to 3 bits, but it depends /// on the number of bits available according to PointerLikeTypeTraits for the /// type. /// /// Note that PointerIntPair always puts the IntVal part in the highest bits /// possible. For example, PointerIntPair will put the bit for /// the bool into bit #2, not bit #0, which allows the low two bits to be used /// for something else. For example, this allows: /// PointerIntPair, 1, bool> /// ... and the two bools will land in different bits. template , typename Info = PointerIntPairInfo> class PointerIntPair { // Used by MSVC visualizer and generally helpful for debugging/visualizing. using InfoTy = Info; intptr_t Value = 0; public: constexpr PointerIntPair() = default; PointerIntPair(PointerTy PtrVal, IntType IntVal) { setPointerAndInt(PtrVal, IntVal); } explicit PointerIntPair(PointerTy PtrVal) { initWithPointer(PtrVal); } PointerTy getPointer() const { return Info::getPointer(Value); } IntType getInt() const { return (IntType)Info::getInt(Value); } void setPointer(PointerTy PtrVal) LLVM_LVALUE_FUNCTION { Value = Info::updatePointer(Value, PtrVal); } void setInt(IntType IntVal) LLVM_LVALUE_FUNCTION { Value = Info::updateInt(Value, static_cast(IntVal)); } void initWithPointer(PointerTy PtrVal) LLVM_LVALUE_FUNCTION { Value = Info::updatePointer(0, PtrVal); } void setPointerAndInt(PointerTy PtrVal, IntType IntVal) LLVM_LVALUE_FUNCTION { Value = Info::updateInt(Info::updatePointer(0, PtrVal), static_cast(IntVal)); } PointerTy const *getAddrOfPointer() const { return const_cast(this)->getAddrOfPointer(); } PointerTy *getAddrOfPointer() { assert(Value == reinterpret_cast(getPointer()) && "Can only return the address if IntBits is cleared and " "PtrTraits doesn't change the pointer"); return reinterpret_cast(&Value); } void *getOpaqueValue() const { return reinterpret_cast(Value); } void setFromOpaqueValue(void *Val) LLVM_LVALUE_FUNCTION { Value = reinterpret_cast(Val); } static PointerIntPair getFromOpaqueValue(void *V) { PointerIntPair P; P.setFromOpaqueValue(V); return P; } // Allow PointerIntPairs to be created from const void * if and only if the // pointer type could be created from a const void *. static PointerIntPair getFromOpaqueValue(const void *V) { (void)PtrTraits::getFromVoidPointer(V); return getFromOpaqueValue(const_cast(V)); } bool operator==(const PointerIntPair &RHS) const { return Value == RHS.Value; } bool operator!=(const PointerIntPair &RHS) const { return Value != RHS.Value; } bool operator<(const PointerIntPair &RHS) const { return Value < RHS.Value; } bool operator>(const PointerIntPair &RHS) const { return Value > RHS.Value; } bool operator<=(const PointerIntPair &RHS) const { return Value <= RHS.Value; } bool operator>=(const PointerIntPair &RHS) const { return Value >= RHS.Value; } }; // Specialize is_trivially_copyable to avoid limitation of llvm::is_trivially_copyable // when compiled with gcc 4.9. template struct is_trivially_copyable> : std::true_type { #ifdef HAVE_STD_IS_TRIVIALLY_COPYABLE static_assert(std::is_trivially_copyable>::value, "inconsistent behavior between llvm:: and std:: implementation of is_trivially_copyable"); #endif }; template struct PointerIntPairInfo { static_assert(PtrTraits::NumLowBitsAvailable < std::numeric_limits::digits, "cannot use a pointer type that has all bits free"); static_assert(IntBits <= PtrTraits::NumLowBitsAvailable, "PointerIntPair with integer size too large for pointer"); enum : uintptr_t { /// PointerBitMask - The bits that come from the pointer. PointerBitMask = ~(uintptr_t)(((intptr_t)1 << PtrTraits::NumLowBitsAvailable) - 1), /// IntShift - The number of low bits that we reserve for other uses, and /// keep zero. IntShift = (uintptr_t)PtrTraits::NumLowBitsAvailable - IntBits, /// IntMask - This is the unshifted mask for valid bits of the int type. IntMask = (uintptr_t)(((intptr_t)1 << IntBits) - 1), // ShiftedIntMask - This is the bits for the integer shifted in place. ShiftedIntMask = (uintptr_t)(IntMask << IntShift) }; static PointerT getPointer(intptr_t Value) { return PtrTraits::getFromVoidPointer( reinterpret_cast(Value & PointerBitMask)); } static intptr_t getInt(intptr_t Value) { return (Value >> IntShift) & IntMask; } static intptr_t updatePointer(intptr_t OrigValue, PointerT Ptr) { intptr_t PtrWord = reinterpret_cast(PtrTraits::getAsVoidPointer(Ptr)); assert((PtrWord & ~PointerBitMask) == 0 && "Pointer is not sufficiently aligned"); // Preserve all low bits, just update the pointer. return PtrWord | (OrigValue & ~PointerBitMask); } static intptr_t updateInt(intptr_t OrigValue, intptr_t Int) { intptr_t IntWord = static_cast(Int); assert((IntWord & ~IntMask) == 0 && "Integer too large for field"); // Preserve all bits other than the ones we are updating. return (OrigValue & ~ShiftedIntMask) | IntWord << IntShift; } }; // Provide specialization of DenseMapInfo for PointerIntPair. template struct DenseMapInfo> { using Ty = PointerIntPair; static Ty getEmptyKey() { uintptr_t Val = static_cast(-1); Val <<= PointerLikeTypeTraits::NumLowBitsAvailable; return Ty::getFromOpaqueValue(reinterpret_cast(Val)); } static Ty getTombstoneKey() { uintptr_t Val = static_cast(-2); Val <<= PointerLikeTypeTraits::NumLowBitsAvailable; return Ty::getFromOpaqueValue(reinterpret_cast(Val)); } static unsigned getHashValue(Ty V) { uintptr_t IV = reinterpret_cast(V.getOpaqueValue()); return unsigned(IV) ^ unsigned(IV >> 9); } static bool isEqual(const Ty &LHS, const Ty &RHS) { return LHS == RHS; } }; // Teach SmallPtrSet that PointerIntPair is "basically a pointer". template struct PointerLikeTypeTraits< PointerIntPair> { static inline void * getAsVoidPointer(const PointerIntPair &P) { return P.getOpaqueValue(); } static inline PointerIntPair getFromVoidPointer(void *P) { return PointerIntPair::getFromOpaqueValue(P); } static inline PointerIntPair getFromVoidPointer(const void *P) { return PointerIntPair::getFromOpaqueValue(P); } enum { NumLowBitsAvailable = PtrTraits::NumLowBitsAvailable - IntBits }; }; } // end namespace llvm #endif // LLVM_ADT_POINTERINTPAIR_H binaryen-version_91/third_party/llvm-project/include/llvm/ADT/PointerUnion.h000066400000000000000000000251121362402614000274360ustar00rootroot00000000000000//===- llvm/ADT/PointerUnion.h - Discriminated Union of 2 Ptrs --*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file defines the PointerUnion class, which is a discriminated union of // pointer types. // //===----------------------------------------------------------------------===// #ifndef LLVM_ADT_POINTERUNION_H #define LLVM_ADT_POINTERUNION_H #include "llvm/ADT/DenseMapInfo.h" #include "llvm/ADT/PointerIntPair.h" #include "llvm/Support/PointerLikeTypeTraits.h" #include #include #include namespace llvm { template struct PointerUnionTypeSelectorReturn { using Return = T; }; /// Get a type based on whether two types are the same or not. /// /// For: /// /// \code /// using Ret = typename PointerUnionTypeSelector::Return; /// \endcode /// /// Ret will be EQ type if T1 is same as T2 or NE type otherwise. template struct PointerUnionTypeSelector { using Return = typename PointerUnionTypeSelectorReturn::Return; }; template struct PointerUnionTypeSelector { using Return = typename PointerUnionTypeSelectorReturn::Return; }; template struct PointerUnionTypeSelectorReturn< PointerUnionTypeSelector> { using Return = typename PointerUnionTypeSelector::Return; }; namespace pointer_union_detail { /// Determine the number of bits required to store integers with values < n. /// This is ceil(log2(n)). constexpr int bitsRequired(unsigned n) { return n > 1 ? 1 + bitsRequired((n + 1) / 2) : 0; } template constexpr int lowBitsAvailable() { return std::min({PointerLikeTypeTraits::NumLowBitsAvailable...}); } /// Find the index of a type in a list of types. TypeIndex::Index /// is the index of T in Us, or sizeof...(Us) if T does not appear in the /// list. template struct TypeIndex; template struct TypeIndex { static constexpr int Index = 0; }; template struct TypeIndex { static constexpr int Index = 1 + TypeIndex::Index; }; template struct TypeIndex { static constexpr int Index = 0; }; /// Find the first type in a list of types. template struct GetFirstType { using type = T; }; /// Provide PointerLikeTypeTraits for void* that is used by PointerUnion /// for the template arguments. template class PointerUnionUIntTraits { public: static inline void *getAsVoidPointer(void *P) { return P; } static inline void *getFromVoidPointer(void *P) { return P; } static constexpr int NumLowBitsAvailable = lowBitsAvailable(); }; /// Implement assigment in terms of construction. template struct AssignableFrom { Derived &operator=(T t) { return static_cast(*this) = Derived(t); } }; template class PointerUnionMembers; template class PointerUnionMembers { protected: ValTy Val; PointerUnionMembers() = default; PointerUnionMembers(ValTy Val) : Val(Val) {} friend struct PointerLikeTypeTraits; }; template class PointerUnionMembers : public PointerUnionMembers { using Base = PointerUnionMembers; public: using Base::Base; PointerUnionMembers() = default; PointerUnionMembers(Type V) : Base(ValTy(const_cast( PointerLikeTypeTraits::getAsVoidPointer(V)), I)) {} using Base::operator=; Derived &operator=(Type V) { this->Val = ValTy( const_cast(PointerLikeTypeTraits::getAsVoidPointer(V)), I); return static_cast(*this); }; }; } /// A discriminated union of two or more pointer types, with the discriminator /// in the low bit of the pointer. /// /// This implementation is extremely efficient in space due to leveraging the /// low bits of the pointer, while exposing a natural and type-safe API. /// /// Common use patterns would be something like this: /// PointerUnion P; /// P = (int*)0; /// printf("%d %d", P.is(), P.is()); // prints "1 0" /// X = P.get(); // ok. /// Y = P.get(); // runtime assertion failure. /// Z = P.get(); // compile time failure. /// P = (float*)0; /// Y = P.get(); // ok. /// X = P.get(); // runtime assertion failure. template class PointerUnion : public pointer_union_detail::PointerUnionMembers< PointerUnion, PointerIntPair< void *, pointer_union_detail::bitsRequired(sizeof...(PTs)), int, pointer_union_detail::PointerUnionUIntTraits>, 0, PTs...> { // The first type is special because we want to directly cast a pointer to a // default-initialized union to a pointer to the first type. But we don't // want PointerUnion to be a 'template ' // because it's much more convenient to have a name for the whole pack. So // split off the first type here. using First = typename pointer_union_detail::GetFirstType::type; using Base = typename PointerUnion::PointerUnionMembers; public: PointerUnion() = default; PointerUnion(std::nullptr_t) : PointerUnion() {} using Base::Base; /// Test if the pointer held in the union is null, regardless of /// which type it is. bool isNull() const { return !this->Val.getPointer(); } explicit operator bool() const { return !isNull(); } /// Test if the Union currently holds the type matching T. template int is() const { constexpr int Index = pointer_union_detail::TypeIndex::Index; static_assert(Index < sizeof...(PTs), "PointerUnion::is given type not in the union"); return this->Val.getInt() == Index; } /// Returns the value of the specified pointer type. /// /// If the specified pointer type is incorrect, assert. template T get() const { assert(is() && "Invalid accessor called"); return PointerLikeTypeTraits::getFromVoidPointer(this->Val.getPointer()); } /// Returns the current pointer if it is of the specified pointer type, /// otherwises returns null. template T dyn_cast() const { if (is()) return get(); return T(); } /// If the union is set to the first pointer type get an address pointing to /// it. First const *getAddrOfPtr1() const { return const_cast(this)->getAddrOfPtr1(); } /// If the union is set to the first pointer type get an address pointing to /// it. First *getAddrOfPtr1() { assert(is() && "Val is not the first pointer"); assert( PointerLikeTypeTraits::getAsVoidPointer(get()) == this->Val.getPointer() && "Can't get the address because PointerLikeTypeTraits changes the ptr"); return const_cast( reinterpret_cast(this->Val.getAddrOfPointer())); } /// Assignment from nullptr which just clears the union. const PointerUnion &operator=(std::nullptr_t) { this->Val.initWithPointer(nullptr); return *this; } /// Assignment from elements of the union. using Base::operator=; void *getOpaqueValue() const { return this->Val.getOpaqueValue(); } static inline PointerUnion getFromOpaqueValue(void *VP) { PointerUnion V; V.Val = decltype(V.Val)::getFromOpaqueValue(VP); return V; } }; template bool operator==(PointerUnion lhs, PointerUnion rhs) { return lhs.getOpaqueValue() == rhs.getOpaqueValue(); } template bool operator!=(PointerUnion lhs, PointerUnion rhs) { return lhs.getOpaqueValue() != rhs.getOpaqueValue(); } template bool operator<(PointerUnion lhs, PointerUnion rhs) { return lhs.getOpaqueValue() < rhs.getOpaqueValue(); } // Teach SmallPtrSet that PointerUnion is "basically a pointer", that has // # low bits available = min(PT1bits,PT2bits)-1. template struct PointerLikeTypeTraits> { static inline void *getAsVoidPointer(const PointerUnion &P) { return P.getOpaqueValue(); } static inline PointerUnion getFromVoidPointer(void *P) { return PointerUnion::getFromOpaqueValue(P); } // The number of bits available are the min of the pointer types minus the // bits needed for the discriminator. static constexpr int NumLowBitsAvailable = PointerLikeTypeTraits::Val)>::NumLowBitsAvailable; }; /// A pointer union of three pointer types. See documentation for PointerUnion /// for usage. template using PointerUnion3 = PointerUnion; /// A pointer union of four pointer types. See documentation for PointerUnion /// for usage. template using PointerUnion4 = PointerUnion; // Teach DenseMap how to use PointerUnions as keys. template struct DenseMapInfo> { using Union = PointerUnion; using FirstInfo = DenseMapInfo::type>; static inline Union getEmptyKey() { return Union(FirstInfo::getEmptyKey()); } static inline Union getTombstoneKey() { return Union(FirstInfo::getTombstoneKey()); } static unsigned getHashValue(const Union &UnionVal) { intptr_t key = (intptr_t)UnionVal.getOpaqueValue(); return DenseMapInfo::getHashValue(key); } static bool isEqual(const Union &LHS, const Union &RHS) { return LHS == RHS; } }; } // end namespace llvm #endif // LLVM_ADT_POINTERUNION_H binaryen-version_91/third_party/llvm-project/include/llvm/ADT/STLExtras.h000066400000000000000000001552331362402614000266460ustar00rootroot00000000000000//===- llvm/ADT/STLExtras.h - Useful STL related functions ------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file contains some templates that are useful if you are working with the // STL at all. // // No library is required when using these functions. // //===----------------------------------------------------------------------===// #ifndef LLVM_ADT_STLEXTRAS_H #define LLVM_ADT_STLEXTRAS_H #include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/iterator.h" #include "llvm/ADT/iterator_range.h" #include "llvm/Config/abi-breaking.h" #include "llvm/Support/ErrorHandling.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef EXPENSIVE_CHECKS #include // for std::mt19937 #endif namespace llvm { // Only used by compiler if both template types are the same. Useful when // using SFINAE to test for the existence of member functions. template struct SameType; namespace detail { template using IterOfRange = decltype(std::begin(std::declval())); template using ValueOfRange = typename std::remove_reference()))>::type; } // end namespace detail //===----------------------------------------------------------------------===// // Extra additions to //===----------------------------------------------------------------------===// template struct negation : std::integral_constant {}; template struct conjunction : std::true_type {}; template struct conjunction : B1 {}; template struct conjunction : std::conditional, B1>::type {}; template struct make_const_ptr { using type = typename std::add_pointer::type>::type; }; template struct make_const_ref { using type = typename std::add_lvalue_reference< typename std::add_const::type>::type; }; //===----------------------------------------------------------------------===// // Extra additions to //===----------------------------------------------------------------------===// template struct identity { using argument_type = Ty; Ty &operator()(Ty &self) const { return self; } const Ty &operator()(const Ty &self) const { return self; } }; /// An efficient, type-erasing, non-owning reference to a callable. This is /// intended for use as the type of a function parameter that is not used /// after the function in question returns. /// /// This class does not own the callable, so it is not in general safe to store /// a function_ref. template class function_ref; template class function_ref { Ret (*callback)(intptr_t callable, Params ...params) = nullptr; intptr_t callable; template static Ret callback_fn(intptr_t callable, Params ...params) { return (*reinterpret_cast(callable))( std::forward(params)...); } public: function_ref() = default; function_ref(std::nullptr_t) {} template function_ref(Callable &&callable, typename std::enable_if< !std::is_same::type, function_ref>::value>::type * = nullptr) : callback(callback_fn::type>), callable(reinterpret_cast(&callable)) {} Ret operator()(Params ...params) const { return callback(callable, std::forward(params)...); } operator bool() const { return callback; } }; // deleter - Very very very simple method that is used to invoke operator // delete on something. It is used like this: // // for_each(V.begin(), B.end(), deleter); template inline void deleter(T *Ptr) { delete Ptr; } //===----------------------------------------------------------------------===// // Extra additions to //===----------------------------------------------------------------------===// namespace adl_detail { using std::begin; template auto adl_begin(ContainerTy &&container) -> decltype(begin(std::forward(container))) { return begin(std::forward(container)); } using std::end; template auto adl_end(ContainerTy &&container) -> decltype(end(std::forward(container))) { return end(std::forward(container)); } using std::swap; template void adl_swap(T &&lhs, T &&rhs) noexcept(noexcept(swap(std::declval(), std::declval()))) { swap(std::forward(lhs), std::forward(rhs)); } } // end namespace adl_detail template auto adl_begin(ContainerTy &&container) -> decltype(adl_detail::adl_begin(std::forward(container))) { return adl_detail::adl_begin(std::forward(container)); } template auto adl_end(ContainerTy &&container) -> decltype(adl_detail::adl_end(std::forward(container))) { return adl_detail::adl_end(std::forward(container)); } template void adl_swap(T &&lhs, T &&rhs) noexcept( noexcept(adl_detail::adl_swap(std::declval(), std::declval()))) { adl_detail::adl_swap(std::forward(lhs), std::forward(rhs)); } /// Test whether \p RangeOrContainer is empty. Similar to C++17 std::empty. template constexpr bool empty(const T &RangeOrContainer) { return adl_begin(RangeOrContainer) == adl_end(RangeOrContainer); } // mapped_iterator - This is a simple iterator adapter that causes a function to // be applied whenever operator* is invoked on the iterator. template ()(*std::declval()))> class mapped_iterator : public iterator_adaptor_base< mapped_iterator, ItTy, typename std::iterator_traits::iterator_category, typename std::remove_reference::type> { public: mapped_iterator(ItTy U, FuncTy F) : mapped_iterator::iterator_adaptor_base(std::move(U)), F(std::move(F)) {} ItTy getCurrent() { return this->I; } FuncReturnTy operator*() { return F(*this->I); } private: FuncTy F; }; // map_iterator - Provide a convenient way to create mapped_iterators, just like // make_pair is useful for creating pairs... template inline mapped_iterator map_iterator(ItTy I, FuncTy F) { return mapped_iterator(std::move(I), std::move(F)); } template auto map_range(ContainerTy &&C, FuncTy F) -> decltype(make_range(map_iterator(C.begin(), F), map_iterator(C.end(), F))) { return make_range(map_iterator(C.begin(), F), map_iterator(C.end(), F)); } /// Helper to determine if type T has a member called rbegin(). template class has_rbegin_impl { using yes = char[1]; using no = char[2]; template static yes& test(Inner *I, decltype(I->rbegin()) * = nullptr); template static no& test(...); public: static const bool value = sizeof(test(nullptr)) == sizeof(yes); }; /// Metafunction to determine if T& or T has a member called rbegin(). template struct has_rbegin : has_rbegin_impl::type> { }; // Returns an iterator_range over the given container which iterates in reverse. // Note that the container must have rbegin()/rend() methods for this to work. template auto reverse(ContainerTy &&C, typename std::enable_if::value>::type * = nullptr) -> decltype(make_range(C.rbegin(), C.rend())) { return make_range(C.rbegin(), C.rend()); } // Returns a std::reverse_iterator wrapped around the given iterator. template std::reverse_iterator make_reverse_iterator(IteratorTy It) { return std::reverse_iterator(It); } // Returns an iterator_range over the given container which iterates in reverse. // Note that the container must have begin()/end() methods which return // bidirectional iterators for this to work. template auto reverse( ContainerTy &&C, typename std::enable_if::value>::type * = nullptr) -> decltype(make_range(llvm::make_reverse_iterator(std::end(C)), llvm::make_reverse_iterator(std::begin(C)))) { return make_range(llvm::make_reverse_iterator(std::end(C)), llvm::make_reverse_iterator(std::begin(C))); } /// An iterator adaptor that filters the elements of given inner iterators. /// /// The predicate parameter should be a callable object that accepts the wrapped /// iterator's reference type and returns a bool. When incrementing or /// decrementing the iterator, it will call the predicate on each element and /// skip any where it returns false. /// /// \code /// int A[] = { 1, 2, 3, 4 }; /// auto R = make_filter_range(A, [](int N) { return N % 2 == 1; }); /// // R contains { 1, 3 }. /// \endcode /// /// Note: filter_iterator_base implements support for forward iteration. /// filter_iterator_impl exists to provide support for bidirectional iteration, /// conditional on whether the wrapped iterator supports it. template class filter_iterator_base : public iterator_adaptor_base< filter_iterator_base, WrappedIteratorT, typename std::common_type< IterTag, typename std::iterator_traits< WrappedIteratorT>::iterator_category>::type> { using BaseT = iterator_adaptor_base< filter_iterator_base, WrappedIteratorT, typename std::common_type< IterTag, typename std::iterator_traits< WrappedIteratorT>::iterator_category>::type>; protected: WrappedIteratorT End; PredicateT Pred; void findNextValid() { while (this->I != End && !Pred(*this->I)) BaseT::operator++(); } // Construct the iterator. The begin iterator needs to know where the end // is, so that it can properly stop when it gets there. The end iterator only // needs the predicate to support bidirectional iteration. filter_iterator_base(WrappedIteratorT Begin, WrappedIteratorT End, PredicateT Pred) : BaseT(Begin), End(End), Pred(Pred) { findNextValid(); } public: using BaseT::operator++; filter_iterator_base &operator++() { BaseT::operator++(); findNextValid(); return *this; } }; /// Specialization of filter_iterator_base for forward iteration only. template class filter_iterator_impl : public filter_iterator_base { using BaseT = filter_iterator_base; public: filter_iterator_impl(WrappedIteratorT Begin, WrappedIteratorT End, PredicateT Pred) : BaseT(Begin, End, Pred) {} }; /// Specialization of filter_iterator_base for bidirectional iteration. template class filter_iterator_impl : public filter_iterator_base { using BaseT = filter_iterator_base; void findPrevValid() { while (!this->Pred(*this->I)) BaseT::operator--(); } public: using BaseT::operator--; filter_iterator_impl(WrappedIteratorT Begin, WrappedIteratorT End, PredicateT Pred) : BaseT(Begin, End, Pred) {} filter_iterator_impl &operator--() { BaseT::operator--(); findPrevValid(); return *this; } }; namespace detail { template struct fwd_or_bidi_tag_impl { using type = std::forward_iterator_tag; }; template <> struct fwd_or_bidi_tag_impl { using type = std::bidirectional_iterator_tag; }; /// Helper which sets its type member to forward_iterator_tag if the category /// of \p IterT does not derive from bidirectional_iterator_tag, and to /// bidirectional_iterator_tag otherwise. template struct fwd_or_bidi_tag { using type = typename fwd_or_bidi_tag_impl::iterator_category>::value>::type; }; } // namespace detail /// Defines filter_iterator to a suitable specialization of /// filter_iterator_impl, based on the underlying iterator's category. template using filter_iterator = filter_iterator_impl< WrappedIteratorT, PredicateT, typename detail::fwd_or_bidi_tag::type>; /// Convenience function that takes a range of elements and a predicate, /// and return a new filter_iterator range. /// /// FIXME: Currently if RangeT && is a rvalue reference to a temporary, the /// lifetime of that temporary is not kept by the returned range object, and the /// temporary is going to be dropped on the floor after the make_iterator_range /// full expression that contains this function call. template iterator_range, PredicateT>> make_filter_range(RangeT &&Range, PredicateT Pred) { using FilterIteratorT = filter_iterator, PredicateT>; return make_range( FilterIteratorT(std::begin(std::forward(Range)), std::end(std::forward(Range)), Pred), FilterIteratorT(std::end(std::forward(Range)), std::end(std::forward(Range)), Pred)); } /// A pseudo-iterator adaptor that is designed to implement "early increment" /// style loops. /// /// This is *not a normal iterator* and should almost never be used directly. It /// is intended primarily to be used with range based for loops and some range /// algorithms. /// /// The iterator isn't quite an `OutputIterator` or an `InputIterator` but /// somewhere between them. The constraints of these iterators are: /// /// - On construction or after being incremented, it is comparable and /// dereferencable. It is *not* incrementable. /// - After being dereferenced, it is neither comparable nor dereferencable, it /// is only incrementable. /// /// This means you can only dereference the iterator once, and you can only /// increment it once between dereferences. template class early_inc_iterator_impl : public iterator_adaptor_base, WrappedIteratorT, std::input_iterator_tag> { using BaseT = iterator_adaptor_base, WrappedIteratorT, std::input_iterator_tag>; using PointerT = typename std::iterator_traits::pointer; protected: #if LLVM_ENABLE_ABI_BREAKING_CHECKS bool IsEarlyIncremented = false; #endif public: early_inc_iterator_impl(WrappedIteratorT I) : BaseT(I) {} using BaseT::operator*; typename BaseT::reference operator*() { #if LLVM_ENABLE_ABI_BREAKING_CHECKS assert(!IsEarlyIncremented && "Cannot dereference twice!"); IsEarlyIncremented = true; #endif return *(this->I)++; } using BaseT::operator++; early_inc_iterator_impl &operator++() { #if LLVM_ENABLE_ABI_BREAKING_CHECKS assert(IsEarlyIncremented && "Cannot increment before dereferencing!"); IsEarlyIncremented = false; #endif return *this; } using BaseT::operator==; bool operator==(const early_inc_iterator_impl &RHS) const { #if LLVM_ENABLE_ABI_BREAKING_CHECKS assert(!IsEarlyIncremented && "Cannot compare after dereferencing!"); #endif return BaseT::operator==(RHS); } }; /// Make a range that does early increment to allow mutation of the underlying /// range without disrupting iteration. /// /// The underlying iterator will be incremented immediately after it is /// dereferenced, allowing deletion of the current node or insertion of nodes to /// not disrupt iteration provided they do not invalidate the *next* iterator -- /// the current iterator can be invalidated. /// /// This requires a very exact pattern of use that is only really suitable to /// range based for loops and other range algorithms that explicitly guarantee /// to dereference exactly once each element, and to increment exactly once each /// element. template iterator_range>> make_early_inc_range(RangeT &&Range) { using EarlyIncIteratorT = early_inc_iterator_impl>; return make_range(EarlyIncIteratorT(std::begin(std::forward(Range))), EarlyIncIteratorT(std::end(std::forward(Range)))); } // forward declarations required by zip_shortest/zip_first/zip_longest template bool all_of(R &&range, UnaryPredicate P); template bool any_of(R &&range, UnaryPredicate P); namespace detail { using std::declval; // We have to alias this since inlining the actual type at the usage site // in the parameter list of iterator_facade_base<> below ICEs MSVC 2017. template struct ZipTupleType { using type = std::tuple())...>; }; template using zip_traits = iterator_facade_base< ZipType, typename std::common_type::iterator_category...>::type, // ^ TODO: Implement random access methods. typename ZipTupleType::type, typename std::iterator_traits>::type>::difference_type, // ^ FIXME: This follows boost::make_zip_iterator's assumption that all // inner iterators have the same difference_type. It would fail if, for // instance, the second field's difference_type were non-numeric while the // first is. typename ZipTupleType::type *, typename ZipTupleType::type>; template struct zip_common : public zip_traits { using Base = zip_traits; using value_type = typename Base::value_type; std::tuple iterators; protected: template value_type deref(std::index_sequence) const { return value_type(*std::get(iterators)...); } template decltype(iterators) tup_inc(std::index_sequence) const { return std::tuple(std::next(std::get(iterators))...); } template decltype(iterators) tup_dec(std::index_sequence) const { return std::tuple(std::prev(std::get(iterators))...); } public: zip_common(Iters &&... ts) : iterators(std::forward(ts)...) {} value_type operator*() { return deref(std::index_sequence_for{}); } const value_type operator*() const { return deref(std::index_sequence_for{}); } ZipType &operator++() { iterators = tup_inc(std::index_sequence_for{}); return *reinterpret_cast(this); } ZipType &operator--() { static_assert(Base::IsBidirectional, "All inner iterators must be at least bidirectional."); iterators = tup_dec(std::index_sequence_for{}); return *reinterpret_cast(this); } }; template struct zip_first : public zip_common, Iters...> { using Base = zip_common, Iters...>; bool operator==(const zip_first &other) const { return std::get<0>(this->iterators) == std::get<0>(other.iterators); } zip_first(Iters &&... ts) : Base(std::forward(ts)...) {} }; template class zip_shortest : public zip_common, Iters...> { template bool test(const zip_shortest &other, std::index_sequence) const { return all_of(std::initializer_list{std::get(this->iterators) != std::get(other.iterators)...}, identity{}); } public: using Base = zip_common, Iters...>; zip_shortest(Iters &&... ts) : Base(std::forward(ts)...) {} bool operator==(const zip_shortest &other) const { return !test(other, std::index_sequence_for{}); } }; template